mirror of
https://github.com/tmux/tmux.git
synced 2026-09-17 18:44:45 +00:00
Compare commits
55 Commits
local_wind
...
osc133_awa
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2ec4328380 | ||
|
|
8746405e34 | ||
|
|
8c0ac1fe05 | ||
|
|
dc65677ff9 | ||
|
|
2142818e25 | ||
|
|
cda0fe9f7b | ||
|
|
4cc9fc91fa | ||
|
|
081b79fd6e | ||
|
|
6294be4986 | ||
|
|
578e07fcbc | ||
|
|
93169e2e62 | ||
|
|
db24c029be | ||
|
|
a6f9a206b7 | ||
|
|
ce253864f8 | ||
|
|
79f1f43830 | ||
|
|
7b666e62be | ||
|
|
5d5f66239a | ||
|
|
4b1586fa81 | ||
|
|
9fa390aee6 | ||
|
|
ad632726bd | ||
|
|
1dd1717462 | ||
|
|
73db721dab | ||
|
|
6bb6c286a9 | ||
|
|
ca34d190b5 | ||
|
|
57a13664cc | ||
|
|
59dc0e75c4 | ||
|
|
2d6aed1d92 | ||
|
|
3dd575ce55 | ||
|
|
60e871658f | ||
|
|
c49dfdf496 | ||
|
|
c5a5b99788 | ||
|
|
e23219ec04 | ||
|
|
0f29c47278 | ||
|
|
7c9fdcb770 | ||
|
|
0330d8ac57 | ||
|
|
0a7788b66f | ||
|
|
458d045bd2 | ||
|
|
a81154d6d9 | ||
|
|
cffc86dae5 | ||
|
|
4cfd155ffc | ||
|
|
670ea5d083 | ||
|
|
eaeb234dd4 | ||
|
|
5948722d5a | ||
|
|
d8ad7cd9c5 | ||
|
|
b49cc2d936 | ||
|
|
abb7108e25 | ||
|
|
d080378464 | ||
|
|
473eca949a | ||
|
|
f0eaa5e9d6 | ||
|
|
f44246c764 | ||
|
|
5d4d41da57 | ||
|
|
bacbb4d9d8 | ||
|
|
07a3a16ae7 | ||
|
|
f41288ee27 | ||
|
|
457b6cbefd |
49
CHANGES
49
CHANGES
@@ -9,7 +9,12 @@ CHANGES FROM 3.7c TO 3.8
|
|||||||
|
|
||||||
- modal panes may be created with new-pane -O; a window can have one modal
|
- modal panes may be created with new-pane -O; a window can have one modal
|
||||||
pane and it prevents interaction with the other panes while it is active.
|
pane and it prevents interaction with the other panes while it is active.
|
||||||
A modal pane is now used for the editor in buffer mode;
|
-C closes the pane when the mouse is clicked outside it and -K sends all
|
||||||
|
keys, including the prefix key, to the pane. A modal pane is now used for
|
||||||
|
the editor in buffer mode;
|
||||||
|
|
||||||
|
- new-pane -A creates a floating pane which remains above a zoomed pane;
|
||||||
|
modal panes do this by default;
|
||||||
|
|
||||||
- move-pane and resize-pane can now move and resize floating panes, including
|
- move-pane and resize-pane can now move and resize floating panes, including
|
||||||
mouse dragging;
|
mouse dragging;
|
||||||
@@ -24,6 +29,9 @@ CHANGES FROM 3.7c TO 3.8
|
|||||||
-k for choose commands kills the pane when the mode exits, which is useful
|
-k for choose commands kills the pane when the mode exits, which is useful
|
||||||
for modes in floating panes;
|
for modes in floating panes;
|
||||||
|
|
||||||
|
- command-prompt -P opens a prompt inside a pane rather than on the status
|
||||||
|
line;
|
||||||
|
|
||||||
- pane-border-status has top-floating and bottom-floating, and there are new
|
- pane-border-status has top-floating and bottom-floating, and there are new
|
||||||
default bindings under C-b g for common move and resize operations;
|
default bindings under C-b g for common move and resize operations;
|
||||||
|
|
||||||
@@ -74,7 +82,7 @@ CHANGES FROM 3.7c TO 3.8
|
|||||||
|
|
||||||
- new hooks to cover areas including client create and destroy, pane
|
- new hooks to cover areas including client create and destroy, pane
|
||||||
activity, pane mode and prompt changes, pane movement and resizing, session
|
activity, pane mode and prompt changes, pane movement and resizing, session
|
||||||
group changes and window creation, close and zoom;
|
group changes and window creation, close, swap and zoom;
|
||||||
|
|
||||||
- OSC 133 escape sequences now trigger events: pane-command-started,
|
- OSC 133 escape sequences now trigger events: pane-command-started,
|
||||||
pane-command-finished and pane-shell-prompt.
|
pane-command-finished and pane-shell-prompt.
|
||||||
@@ -87,10 +95,23 @@ CHANGES FROM 3.7c TO 3.8
|
|||||||
while scrolling or while hovered and disappears after pane-scrollbars-timeout
|
while scrolling or while hovered and disappears after pane-scrollbars-timeout
|
||||||
(Michael Grant).
|
(Michael Grant).
|
||||||
|
|
||||||
* Extend the fill-character option so both inside and outside the window can be
|
* Add a clear-on-attach server option. When disabled, tmux does not enter the
|
||||||
changed.
|
alternate screen on attach and instead scrolls the existing terminal content
|
||||||
|
into the scrollback buffer so it remains accessible (issue 5508).
|
||||||
|
|
||||||
* Change set-option and set-hooks to use formats and add a -F flag to each.
|
* Copy mode can now automatically refresh as pane content changes with the
|
||||||
|
refresh-on, refresh-off and refresh-toggle commands. Automatic refresh is off
|
||||||
|
by default; refresh-now refreshes once and is bound to r. These replace
|
||||||
|
refresh-from-pane (issue 5165).
|
||||||
|
|
||||||
|
* Extend the fill-character option so both inside and outside the window can be
|
||||||
|
changed, with a new default for unused areas inside the window.
|
||||||
|
|
||||||
|
* Change show-options and show-hooks to use formats and add a -F flag to each.
|
||||||
|
|
||||||
|
* Allow individual terminal features to be disabled by adding @ to their names
|
||||||
|
in terminal-features, and add a utf8 feature for terminals which support
|
||||||
|
UTF-8 output.
|
||||||
|
|
||||||
* 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).
|
||||||
@@ -105,6 +126,8 @@ CHANGES FROM 3.7c TO 3.8
|
|||||||
* Add new-window -E, respawn-pane -E and respawn-window -E as more convenient
|
* Add new-window -E, respawn-pane -E and respawn-window -E as more convenient
|
||||||
methods to create an empty pane (rather than using '' for the command).
|
methods to create an empty pane (rather than using '' for the command).
|
||||||
|
|
||||||
|
* Add a default C-b T binding to change the current pane title.
|
||||||
|
|
||||||
* Menus now belong to the window, so appear on all clients.
|
* Menus now belong to the window, so appear on all clients.
|
||||||
|
|
||||||
* Dragging over an existing copy mode selection now adjusts it (Michael Grant).
|
* Dragging over an existing copy mode selection now adjusts it (Michael Grant).
|
||||||
@@ -118,6 +141,13 @@ CHANGES FROM 3.7c TO 3.8
|
|||||||
* Copy mode no longer exits at the bottom while a selection is in progress
|
* Copy mode no longer exits at the bottom while a selection is in progress
|
||||||
(issue 5349).
|
(issue 5349).
|
||||||
|
|
||||||
|
* Add copy-mode-current-line-style to set the style of the line containing the
|
||||||
|
cursor in copy mode (issue 5391).
|
||||||
|
|
||||||
|
* Copy mode word commands now recognise all Unicode whitespace characters; a
|
||||||
|
space in word-separators matches any Unicode whitespace character (issue
|
||||||
|
5562).
|
||||||
|
|
||||||
* Add style attributes for dimming colours (dim=) and for hyperlinks (link= and
|
* Add style attributes for dimming colours (dim=) and for hyperlinks (link= and
|
||||||
nolink) (issues 4842 and 4280 from Moritz Angermann).
|
nolink) (issues 4842 and 4280 from Moritz Angermann).
|
||||||
|
|
||||||
@@ -128,7 +158,9 @@ CHANGES FROM 3.7c TO 3.8
|
|||||||
|
|
||||||
* Add or improve format variables including client_colours,
|
* Add or improve format variables including client_colours,
|
||||||
pane_start_command_list, window_manual_width, window_manual_height,
|
pane_start_command_list, window_manual_width, window_manual_height,
|
||||||
pane_last_output_time, pane_modal_flag and window_modal_pane.
|
pane_last_output_time, pane_modal_flag, window_modal_pane, pane_private_modes,
|
||||||
|
pane_output_generation, history_added, history_collected, history_generation,
|
||||||
|
hook_fire_count and hook_fire_time.
|
||||||
|
|
||||||
* Add additional pane sort orders, and a z sort order for choose-tree so
|
* Add additional pane sort orders, and a z sort order for choose-tree so
|
||||||
floating panes can be sorted by z-index.
|
floating panes can be sorted by z-index.
|
||||||
@@ -145,7 +177,10 @@ CHANGES FROM 3.7c TO 3.8
|
|||||||
|
|
||||||
* Fix control mode clients hanging on exit if pty data was still queued (Ben
|
* Fix control mode clients hanging on exit if pty data was still queued (Ben
|
||||||
Maurer, issue 5356), and avoid sending notifications to clients which are
|
Maurer, issue 5356), and avoid sending notifications to clients which are
|
||||||
already exiting (Ben Maurer, issue 5357).
|
already exiting (Ben Maurer, issue 5357). Queue notifications so they are not
|
||||||
|
sent inside %begin/%end (issue 5458), do not let a stuck client prevent the
|
||||||
|
server from exiting (issue 5444), and reset control mode offsets when a pane
|
||||||
|
is respawned (issue 5498).
|
||||||
|
|
||||||
* Fix grouped sessions sometimes being left as unusable command targets while
|
* Fix grouped sessions sometimes being left as unusable command targets while
|
||||||
they are being killed (Bryce Miller, issue 5180).
|
they are being killed (Bryce Miller, issue 5180).
|
||||||
|
|||||||
@@ -85,7 +85,6 @@ endif
|
|||||||
|
|
||||||
# List of sources.
|
# List of sources.
|
||||||
dist_tmux_SOURCES = \
|
dist_tmux_SOURCES = \
|
||||||
active.c \
|
|
||||||
alerts.c \
|
alerts.c \
|
||||||
arguments.c \
|
arguments.c \
|
||||||
attributes.c \
|
attributes.c \
|
||||||
|
|||||||
449
active.c
449
active.c
@@ -1,449 +0,0 @@
|
|||||||
/* $OpenBSD$ */
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Copyright (c) 2026 Nicholas Marriott <nicholas.marriott@gmail.com>
|
|
||||||
*
|
|
||||||
* Permission to use, copy, modify, and distribute this software for any
|
|
||||||
* purpose with or without fee is hereby granted, provided that the above
|
|
||||||
* copyright notice and this permission notice appear in all copies.
|
|
||||||
*
|
|
||||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
|
||||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
|
||||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
|
||||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
|
||||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
|
||||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
|
||||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <sys/types.h>
|
|
||||||
|
|
||||||
#include <stdlib.h>
|
|
||||||
|
|
||||||
#include "tmux.h"
|
|
||||||
|
|
||||||
/* Local last-window stack entry. */
|
|
||||||
struct active_window_entry {
|
|
||||||
u_int window;
|
|
||||||
TAILQ_ENTRY(active_window_entry) entry;
|
|
||||||
};
|
|
||||||
|
|
||||||
/* Local last-window stack. */
|
|
||||||
TAILQ_HEAD(active_window_stack, active_window_entry);
|
|
||||||
|
|
||||||
/* Per-client and per-session local active window state. */
|
|
||||||
struct active_window {
|
|
||||||
struct client *client;
|
|
||||||
struct session *session;
|
|
||||||
|
|
||||||
u_int window;
|
|
||||||
struct active_window_stack lastw;
|
|
||||||
|
|
||||||
RB_ENTRY(active_window) entry;
|
|
||||||
};
|
|
||||||
RB_HEAD(active_windows, active_window);
|
|
||||||
|
|
||||||
static int
|
|
||||||
active_window_cmp(struct active_window *aw1, struct active_window *aw2)
|
|
||||||
{
|
|
||||||
uintptr_t c1 = (uintptr_t)aw1->client;
|
|
||||||
uintptr_t c2 = (uintptr_t)aw2->client;
|
|
||||||
uintptr_t s1 = (uintptr_t)aw1->session;
|
|
||||||
uintptr_t s2 = (uintptr_t)aw2->session;
|
|
||||||
|
|
||||||
if (c1 < c2)
|
|
||||||
return (-1);
|
|
||||||
if (c1 > c2)
|
|
||||||
return (1);
|
|
||||||
if (s1 < s2)
|
|
||||||
return (-1);
|
|
||||||
if (s1 > s2)
|
|
||||||
return (1);
|
|
||||||
return (0);
|
|
||||||
}
|
|
||||||
RB_GENERATE_STATIC(active_windows, active_window, entry, active_window_cmp);
|
|
||||||
|
|
||||||
static struct active_window *active_get(struct client *, struct session *);
|
|
||||||
static struct active_window *active_add(struct client *, struct session *,
|
|
||||||
struct winlink *);
|
|
||||||
static void active_free(struct active_window *);
|
|
||||||
static struct winlink *active_resolve(struct active_window *);
|
|
||||||
static void active_stack_remove(struct active_window_stack *, u_int);
|
|
||||||
static void active_stack_push(struct active_window_stack *, u_int);
|
|
||||||
static void active_changed(struct client *, struct session *,
|
|
||||||
struct winlink *, struct winlink *);
|
|
||||||
|
|
||||||
/* All local active window states. */
|
|
||||||
static struct active_windows active_windows = RB_INITIALIZER(&active_windows);
|
|
||||||
|
|
||||||
/* Find local active window state for a client and session. */
|
|
||||||
static struct active_window *
|
|
||||||
active_get(struct client *c, struct session *s)
|
|
||||||
{
|
|
||||||
struct active_window aw;
|
|
||||||
|
|
||||||
if (c == NULL || s == NULL)
|
|
||||||
return (NULL);
|
|
||||||
aw.client = c;
|
|
||||||
aw.session = s;
|
|
||||||
return (RB_FIND(active_windows, &active_windows, &aw));
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Add local active window state for a client and session. */
|
|
||||||
static struct active_window *
|
|
||||||
active_add(struct client *c, struct session *s, struct winlink *wl)
|
|
||||||
{
|
|
||||||
struct active_window *aw;
|
|
||||||
|
|
||||||
aw = active_get(c, s);
|
|
||||||
if (aw == NULL) {
|
|
||||||
aw = xcalloc(1, sizeof *aw);
|
|
||||||
aw->client = c;
|
|
||||||
aw->session = s;
|
|
||||||
TAILQ_INIT(&aw->lastw);
|
|
||||||
RB_INSERT(active_windows, &active_windows, aw);
|
|
||||||
}
|
|
||||||
if (wl != NULL)
|
|
||||||
aw->window = wl->window->id;
|
|
||||||
return (aw);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Free local active window state. */
|
|
||||||
static void
|
|
||||||
active_free(struct active_window *aw)
|
|
||||||
{
|
|
||||||
struct active_window_entry *awe;
|
|
||||||
|
|
||||||
while (!TAILQ_EMPTY(&aw->lastw)) {
|
|
||||||
awe = TAILQ_FIRST(&aw->lastw);
|
|
||||||
TAILQ_REMOVE(&aw->lastw, awe, entry);
|
|
||||||
free(awe);
|
|
||||||
}
|
|
||||||
RB_REMOVE(active_windows, &active_windows, aw);
|
|
||||||
free(aw);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Resolve local active window state to a valid winlink. */
|
|
||||||
static struct winlink *
|
|
||||||
active_resolve(struct active_window *aw)
|
|
||||||
{
|
|
||||||
struct winlink *wl, *next;
|
|
||||||
u_int window;
|
|
||||||
|
|
||||||
if (aw == NULL || aw->session->curw == NULL)
|
|
||||||
return (NULL);
|
|
||||||
|
|
||||||
wl = winlink_find_by_window_id(&aw->session->windows, aw->window);
|
|
||||||
if (wl != NULL)
|
|
||||||
return (wl);
|
|
||||||
|
|
||||||
while (!TAILQ_EMPTY(&aw->lastw)) {
|
|
||||||
window = TAILQ_FIRST(&aw->lastw)->window;
|
|
||||||
|
|
||||||
next = winlink_find_by_window_id(&aw->session->windows, window);
|
|
||||||
active_stack_remove(&aw->lastw, window);
|
|
||||||
if (next != NULL) {
|
|
||||||
aw->window = next->window->id;
|
|
||||||
return (next);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
aw->window = aw->session->curw->window->id;
|
|
||||||
return (aw->session->curw);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Remove a window from a local last-window stack. */
|
|
||||||
static void
|
|
||||||
active_stack_remove(struct active_window_stack *stack, u_int window)
|
|
||||||
{
|
|
||||||
struct active_window_entry *awe, *awe1;
|
|
||||||
|
|
||||||
TAILQ_FOREACH_SAFE(awe, stack, entry, awe1) {
|
|
||||||
if (awe->window == window) {
|
|
||||||
TAILQ_REMOVE(stack, awe, entry);
|
|
||||||
free(awe);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Push a window onto a local last-window stack. */
|
|
||||||
static void
|
|
||||||
active_stack_push(struct active_window_stack *stack, u_int window)
|
|
||||||
{
|
|
||||||
struct active_window_entry *awe;
|
|
||||||
|
|
||||||
active_stack_remove(stack, window);
|
|
||||||
awe = xcalloc(1, sizeof *awe);
|
|
||||||
awe->window = window;
|
|
||||||
TAILQ_INSERT_HEAD(stack, awe, entry);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Notify the server that a client's effective window changed. */
|
|
||||||
static void
|
|
||||||
active_changed(struct client *c, struct session *s, struct winlink *wl,
|
|
||||||
struct winlink *old)
|
|
||||||
{
|
|
||||||
struct event_payload *ep;
|
|
||||||
struct cmd_find_state fs;
|
|
||||||
|
|
||||||
if (wl == NULL || wl == old)
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (options_get_number(global_options, "focus-events")) {
|
|
||||||
if (old != NULL)
|
|
||||||
window_update_focus(old->window);
|
|
||||||
window_update_focus(wl->window);
|
|
||||||
}
|
|
||||||
winlink_clear_flags(wl);
|
|
||||||
window_update_activity(wl->window);
|
|
||||||
tty_update_window_offset(wl->window);
|
|
||||||
|
|
||||||
if (c != NULL && c->session == s)
|
|
||||||
wl->window->latest = c;
|
|
||||||
|
|
||||||
ep = event_payload_create();
|
|
||||||
cmd_find_from_winlink(&fs, wl, 0);
|
|
||||||
event_payload_set_target(ep, &fs);
|
|
||||||
event_payload_set_client(ep, "client", c);
|
|
||||||
event_payload_set_session(ep, "session", s);
|
|
||||||
event_payload_set_window(ep, "window", wl->window);
|
|
||||||
event_payload_set_window(ep, "new_window", wl->window);
|
|
||||||
event_payload_set_int(ep, "window_index", wl->idx);
|
|
||||||
event_payload_set_int(ep, "new_window_index", wl->idx);
|
|
||||||
if (old != NULL) {
|
|
||||||
event_payload_set_window(ep, "old_window", old->window);
|
|
||||||
event_payload_set_int(ep, "old_window_index", old->idx);
|
|
||||||
}
|
|
||||||
events_fire("client-window-changed", ep);
|
|
||||||
|
|
||||||
if (c != NULL) {
|
|
||||||
c->flags |= CLIENT_REDRAWWINDOW|CLIENT_REDRAWSTATUS|
|
|
||||||
CLIENT_REDRAWBORDERS;
|
|
||||||
tty_update_client_offset(c);
|
|
||||||
status_update_cache(s);
|
|
||||||
}
|
|
||||||
recalculate_sizes();
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Return if a client has local window selection for a session. */
|
|
||||||
int
|
|
||||||
active_is_local_window(struct client *c, struct session *s)
|
|
||||||
{
|
|
||||||
return (active_get(c, s) != NULL);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Make a client use local or shared window selection for a session. */
|
|
||||||
void
|
|
||||||
active_set_local_window(struct client *c, struct session *s,
|
|
||||||
enum active_window_mode mode)
|
|
||||||
{
|
|
||||||
struct active_window *aw;
|
|
||||||
struct winlink *wl, *old;
|
|
||||||
|
|
||||||
if (c == NULL || s == NULL || s->curw == NULL)
|
|
||||||
return;
|
|
||||||
|
|
||||||
old = active_get_effective_winlink(c, s);
|
|
||||||
if (mode == ACTIVE_LOCAL) {
|
|
||||||
active_add(c, s, old);
|
|
||||||
if (c != NULL && c->session == s)
|
|
||||||
c->flags |= CLIENT_REDRAWSTATUS;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
aw = active_get(c, s);
|
|
||||||
if (aw != NULL) {
|
|
||||||
active_free(aw);
|
|
||||||
wl = active_get_effective_winlink(c, s);
|
|
||||||
active_changed(c, s, wl, old);
|
|
||||||
if (c != NULL && c->session == s)
|
|
||||||
c->flags |= CLIENT_REDRAWSTATUS;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Return the effective winlink for a client and session. */
|
|
||||||
struct winlink *
|
|
||||||
active_get_effective_winlink(struct client *c, struct session *s)
|
|
||||||
{
|
|
||||||
struct active_window *aw;
|
|
||||||
|
|
||||||
if (s == NULL)
|
|
||||||
return (NULL);
|
|
||||||
aw = active_get(c, s);
|
|
||||||
if (aw != NULL)
|
|
||||||
return (active_resolve(aw));
|
|
||||||
return (s->curw);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Return the effective window for a client and session. */
|
|
||||||
struct window *
|
|
||||||
active_get_effective_window(struct client *c, struct session *s)
|
|
||||||
{
|
|
||||||
struct winlink *wl;
|
|
||||||
|
|
||||||
wl = active_get_effective_winlink(c, s);
|
|
||||||
if (wl == NULL)
|
|
||||||
return (NULL);
|
|
||||||
return (wl->window);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Select a winlink for a client and session. */
|
|
||||||
int
|
|
||||||
active_select_window(struct client *c, struct session *s, struct winlink *wl)
|
|
||||||
{
|
|
||||||
struct active_window *aw;
|
|
||||||
struct winlink *old;
|
|
||||||
int changed;
|
|
||||||
|
|
||||||
if (wl == NULL)
|
|
||||||
return (-1);
|
|
||||||
aw = active_get(c, s);
|
|
||||||
if (aw == NULL)
|
|
||||||
return (session_set_current(s, wl));
|
|
||||||
|
|
||||||
old = active_resolve(aw);
|
|
||||||
if (old == wl)
|
|
||||||
return (1);
|
|
||||||
if (old != NULL)
|
|
||||||
active_stack_push(&aw->lastw, old->window->id);
|
|
||||||
active_stack_remove(&aw->lastw, wl->window->id);
|
|
||||||
aw->window = wl->window->id;
|
|
||||||
changed = (old != wl);
|
|
||||||
active_changed(c, s, wl, old);
|
|
||||||
return (changed ? 0 : 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Select a window by index. */
|
|
||||||
int
|
|
||||||
active_select_window_index(struct client *c, struct session *s, int idx)
|
|
||||||
{
|
|
||||||
struct winlink *wl;
|
|
||||||
|
|
||||||
wl = winlink_find_by_index(&s->windows, idx);
|
|
||||||
return (active_select_window(c, s, wl));
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Select the next window. */
|
|
||||||
int
|
|
||||||
active_next_window(struct client *c, struct session *s, int alert)
|
|
||||||
{
|
|
||||||
struct winlink *wl;
|
|
||||||
|
|
||||||
wl = active_get_effective_winlink(c, s);
|
|
||||||
if (wl == NULL)
|
|
||||||
return (-1);
|
|
||||||
do {
|
|
||||||
wl = winlink_next(wl);
|
|
||||||
if (wl == NULL)
|
|
||||||
wl = RB_MIN(winlinks, &s->windows);
|
|
||||||
if (!alert || (wl->flags & WINLINK_ALERTFLAGS))
|
|
||||||
return (active_select_window(c, s, wl));
|
|
||||||
} while (wl != active_get_effective_winlink(c, s));
|
|
||||||
return (-1);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Select the previous window. */
|
|
||||||
int
|
|
||||||
active_previous_window(struct client *c, struct session *s, int alert)
|
|
||||||
{
|
|
||||||
struct winlink *wl;
|
|
||||||
|
|
||||||
wl = active_get_effective_winlink(c, s);
|
|
||||||
if (wl == NULL)
|
|
||||||
return (-1);
|
|
||||||
do {
|
|
||||||
wl = winlink_previous(wl);
|
|
||||||
if (wl == NULL)
|
|
||||||
wl = RB_MAX(winlinks, &s->windows);
|
|
||||||
if (!alert || (wl->flags & WINLINK_ALERTFLAGS))
|
|
||||||
return (active_select_window(c, s, wl));
|
|
||||||
} while (wl != active_get_effective_winlink(c, s));
|
|
||||||
return (-1);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Select the last window. */
|
|
||||||
int
|
|
||||||
active_last_window(struct client *c, struct session *s)
|
|
||||||
{
|
|
||||||
struct active_window *aw;
|
|
||||||
struct winlink *wl;
|
|
||||||
u_int window;
|
|
||||||
|
|
||||||
aw = active_get(c, s);
|
|
||||||
if (aw == NULL)
|
|
||||||
return (session_last(s));
|
|
||||||
while (!TAILQ_EMPTY(&aw->lastw)) {
|
|
||||||
window = TAILQ_FIRST(&aw->lastw)->window;
|
|
||||||
|
|
||||||
wl = winlink_find_by_window_id(&s->windows, window);
|
|
||||||
if (wl != NULL)
|
|
||||||
return (active_select_window(c, s, wl));
|
|
||||||
active_stack_remove(&aw->lastw, window);
|
|
||||||
}
|
|
||||||
return (-1);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Remove all state for a client. */
|
|
||||||
void
|
|
||||||
active_remove_client(struct client *c)
|
|
||||||
{
|
|
||||||
struct active_window *aw, *aw1;
|
|
||||||
|
|
||||||
RB_FOREACH_SAFE(aw, active_windows, &active_windows, aw1) {
|
|
||||||
if (aw->client == c)
|
|
||||||
active_free(aw);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Remove all state for a session. */
|
|
||||||
void
|
|
||||||
active_remove_session(struct session *s)
|
|
||||||
{
|
|
||||||
struct active_window *aw, *aw1;
|
|
||||||
|
|
||||||
RB_FOREACH_SAFE(aw, active_windows, &active_windows, aw1) {
|
|
||||||
if (aw->session == s)
|
|
||||||
active_free(aw);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Remove a window from all local state. */
|
|
||||||
void
|
|
||||||
active_remove_window(struct window *w)
|
|
||||||
{
|
|
||||||
struct active_window *aw;
|
|
||||||
|
|
||||||
RB_FOREACH(aw, active_windows, &active_windows) {
|
|
||||||
active_stack_remove(&aw->lastw, w->id);
|
|
||||||
if (aw->window == w->id && aw->session->curw != NULL)
|
|
||||||
aw->window = aw->session->curw->window->id;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Return if this winlink is the effective winlink. */
|
|
||||||
int
|
|
||||||
active_is_effective_window(struct client *c, struct session *s,
|
|
||||||
struct winlink *wl)
|
|
||||||
{
|
|
||||||
return (wl != NULL && wl == active_get_effective_winlink(c, s));
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Return if this winlink is the last effective winlink. */
|
|
||||||
int
|
|
||||||
active_is_last_window(struct client *c, struct session *s, struct winlink *wl)
|
|
||||||
{
|
|
||||||
struct active_window *aw;
|
|
||||||
struct active_window_entry *awe;
|
|
||||||
|
|
||||||
if (wl == NULL)
|
|
||||||
return (0);
|
|
||||||
aw = active_get(c, s);
|
|
||||||
if (aw == NULL)
|
|
||||||
return (wl == TAILQ_FIRST(&s->lastw));
|
|
||||||
TAILQ_FOREACH(awe, &aw->lastw, entry) {
|
|
||||||
if (winlink_find_by_window_id(&s->windows, awe->window) == NULL)
|
|
||||||
continue;
|
|
||||||
return (wl->window->id == awe->window);
|
|
||||||
}
|
|
||||||
return (0);
|
|
||||||
}
|
|
||||||
2
alerts.c
2
alerts.c
@@ -314,7 +314,7 @@ alerts_set_message(struct winlink *wl, const char *type, const char *option)
|
|||||||
tty_putcode(&c->tty, TTYC_BEL);
|
tty_putcode(&c->tty, TTYC_BEL);
|
||||||
if (visual == VISUAL_OFF)
|
if (visual == VISUAL_OFF)
|
||||||
continue;
|
continue;
|
||||||
if (active_get_effective_winlink(c, c->session) == wl) {
|
if (c->session->curw == wl) {
|
||||||
status_message_set(c, -1, 1, 0, 0,
|
status_message_set(c, -1, 1, 0, 0,
|
||||||
"%s in current window", type);
|
"%s in current window", type);
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -93,7 +93,7 @@ cmd_attach_session(struct cmdq_item *item, const char *tflag, int dflag,
|
|||||||
if (wl != NULL) {
|
if (wl != NULL) {
|
||||||
if (wp != NULL)
|
if (wp != NULL)
|
||||||
window_set_active_pane(wp->window, wp, 1);
|
window_set_active_pane(wp->window, wp, 1);
|
||||||
active_select_window(c, 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);
|
||||||
else
|
else
|
||||||
|
|||||||
@@ -93,18 +93,14 @@ cmd_break_pane_exec(struct cmd *self, struct cmdq_item *item)
|
|||||||
struct cmd_find_state *current = cmdq_get_current(item);
|
struct cmd_find_state *current = cmdq_get_current(item);
|
||||||
struct cmd_find_state *target = cmdq_get_target(item);
|
struct cmd_find_state *target = cmdq_get_target(item);
|
||||||
struct cmd_find_state *source = cmdq_get_source(item);
|
struct cmd_find_state *source = cmdq_get_source(item);
|
||||||
struct client *c = cmdq_get_client(item);
|
|
||||||
struct client *tc = cmdq_get_target_client(item);
|
struct client *tc = cmdq_get_target_client(item);
|
||||||
struct client *ac;
|
|
||||||
struct winlink *wl = source->wl;
|
struct winlink *wl = source->wl;
|
||||||
struct winlink *current_wl;
|
|
||||||
struct session *src_s = source->s;
|
struct session *src_s = source->s;
|
||||||
struct session *dst_s = target->s;
|
struct session *dst_s = target->s;
|
||||||
struct window_pane *wp = source->wp;
|
struct window_pane *wp = source->wp;
|
||||||
struct window *w = wl->window, *old_w = w;
|
struct window *w = wl->window, *old_w = w;
|
||||||
char *cause, *cp;
|
char *cause, *cp;
|
||||||
int idx = target->idx, before, old_idx = wl->idx;
|
int idx = target->idx, before, old_idx = wl->idx;
|
||||||
int detached, local;
|
|
||||||
const char *template, *name = args_get(args, 'n');
|
const char *template, *name = args_get(args, 'n');
|
||||||
|
|
||||||
if (wp == w->modal) {
|
if (wp == w->modal) {
|
||||||
@@ -121,18 +117,11 @@ cmd_break_pane_exec(struct cmd *self, struct cmdq_item *item)
|
|||||||
}
|
}
|
||||||
|
|
||||||
before = args_has(args, 'b');
|
before = args_has(args, 'b');
|
||||||
ac = tc;
|
|
||||||
if (ac == NULL || ac->session != dst_s)
|
|
||||||
ac = c;
|
|
||||||
detached = args_has(args, 'd');
|
|
||||||
local = (!detached && active_is_local_window(ac, dst_s));
|
|
||||||
if (args_has(args, 'a') || before) {
|
if (args_has(args, 'a') || before) {
|
||||||
if (target->wl != NULL)
|
if (target->wl != NULL)
|
||||||
idx = winlink_shuffle_up(dst_s, target->wl, before);
|
idx = winlink_shuffle_up(dst_s, target->wl, before);
|
||||||
else {
|
else
|
||||||
current_wl = active_get_effective_winlink(ac, dst_s);
|
idx = winlink_shuffle_up(dst_s, dst_s->curw, before);
|
||||||
idx = winlink_shuffle_up(dst_s, current_wl, before);
|
|
||||||
}
|
|
||||||
if (idx == -1)
|
if (idx == -1)
|
||||||
return (CMD_RETURN_ERROR);
|
return (CMD_RETURN_ERROR);
|
||||||
}
|
}
|
||||||
@@ -140,7 +129,7 @@ cmd_break_pane_exec(struct cmd *self, struct cmdq_item *item)
|
|||||||
|
|
||||||
if (window_count_panes(w, 1) == 1) {
|
if (window_count_panes(w, 1) == 1) {
|
||||||
if (server_link_window(src_s, wl, dst_s, idx, 0,
|
if (server_link_window(src_s, wl, dst_s, idx, 0,
|
||||||
!detached && !local, &cause) != 0) {
|
!args_has(args, 'd'), &cause) != 0) {
|
||||||
cmdq_error(item, "%s", cause);
|
cmdq_error(item, "%s", cause);
|
||||||
free(cause);
|
free(cause);
|
||||||
return (CMD_RETURN_ERROR);
|
return (CMD_RETURN_ERROR);
|
||||||
@@ -153,13 +142,6 @@ cmd_break_pane_exec(struct cmd *self, struct cmdq_item *item)
|
|||||||
wl = winlink_find_by_window(&dst_s->windows, w);
|
wl = winlink_find_by_window(&dst_s->windows, w);
|
||||||
if (wl == NULL)
|
if (wl == NULL)
|
||||||
return (CMD_RETURN_ERROR);
|
return (CMD_RETURN_ERROR);
|
||||||
if (local) {
|
|
||||||
active_select_window(ac, dst_s, wl);
|
|
||||||
if (ac != NULL && ac->session == dst_s)
|
|
||||||
cmd_find_from_client(current, ac, 0);
|
|
||||||
if (ac != NULL && ac->session == dst_s)
|
|
||||||
server_redraw_client(ac);
|
|
||||||
}
|
|
||||||
window_fire_pane_moved(wp, old_w, old_idx, w, wl->idx);
|
window_fire_pane_moved(wp, old_w, old_idx, w, wl->idx);
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
@@ -203,17 +185,9 @@ cmd_break_pane_exec(struct cmd *self, struct cmdq_item *item)
|
|||||||
window_remove_ref(w, __func__);
|
window_remove_ref(w, __func__);
|
||||||
events_fire_window("window-created", w);
|
events_fire_window("window-created", w);
|
||||||
window_fire_pane_moved(wp, old_w, old_idx, w, wl->idx);
|
window_fire_pane_moved(wp, old_w, old_idx, w, wl->idx);
|
||||||
if (!detached) {
|
if (!args_has(args, 'd')) {
|
||||||
if (local)
|
session_select(dst_s, wl->idx);
|
||||||
active_select_window(ac, dst_s, wl);
|
cmd_find_from_session(current, dst_s, 0);
|
||||||
else
|
|
||||||
session_select(dst_s, wl->idx);
|
|
||||||
if (local && ac != NULL && ac->session == dst_s)
|
|
||||||
cmd_find_from_client(current, ac, 0);
|
|
||||||
else
|
|
||||||
cmd_find_from_session(current, dst_s, 0);
|
|
||||||
if (local && ac != NULL && ac->session == dst_s)
|
|
||||||
server_redraw_client(ac);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
server_redraw_session(src_s);
|
server_redraw_session(src_s);
|
||||||
|
|||||||
@@ -30,8 +30,8 @@ const struct cmd_entry cmd_copy_mode_entry = {
|
|||||||
.name = "copy-mode",
|
.name = "copy-mode",
|
||||||
.alias = NULL,
|
.alias = NULL,
|
||||||
|
|
||||||
.args = { "dekHMqSs:t:u", 0, 0, NULL },
|
.args = { "UcdekHMqSs:t:u", 0, 0, NULL },
|
||||||
.usage = "[-dekHMqSu] [-s src-pane] " CMD_TARGET_PANE_USAGE,
|
.usage = "[-UcdekHMqSu] [-s src-pane] " CMD_TARGET_PANE_USAGE,
|
||||||
|
|
||||||
.source = { 's', CMD_FIND_PANE, 0 },
|
.source = { 's', CMD_FIND_PANE, 0 },
|
||||||
.target = { 't', CMD_FIND_PANE, 0 },
|
.target = { 't', CMD_FIND_PANE, 0 },
|
||||||
|
|||||||
@@ -483,7 +483,7 @@ cmd_display_menu_exec(struct cmd *self, struct cmdq_item *item)
|
|||||||
char *title, *cause = NULL;
|
char *title, *cause = NULL;
|
||||||
int flags = 0, starting_choice = 0;
|
int flags = 0, starting_choice = 0;
|
||||||
u_int px, py, i, count = args_count(args);
|
u_int px, py, i, count = args_count(args);
|
||||||
struct options *o = target->w->options;
|
struct options *o = target->s->curw->window->options;
|
||||||
struct options_entry *oe;
|
struct options_entry *oe;
|
||||||
|
|
||||||
if (args_has(args, 'C')) {
|
if (args_has(args, 'C')) {
|
||||||
@@ -584,7 +584,7 @@ cmd_display_popup_exec(struct cmd *self, struct cmdq_item *item)
|
|||||||
u_int px, py, w, h, count = args_count(args);
|
u_int px, py, w, h, count = args_count(args);
|
||||||
struct args_value *av;
|
struct args_value *av;
|
||||||
struct environ *env = NULL;
|
struct environ *env = NULL;
|
||||||
struct options *o = target->w->options;
|
struct options *o = s->curw->window->options;
|
||||||
struct options_entry *oe;
|
struct options_entry *oe;
|
||||||
|
|
||||||
if (args_has(args, 'C')) {
|
if (args_has(args, 'C')) {
|
||||||
|
|||||||
84
cmd-find.c
84
cmd-find.c
@@ -884,13 +884,13 @@ cmd_find_from_client(struct cmd_find_state *fs, struct client *c, int flags)
|
|||||||
if (c->session != NULL) {
|
if (c->session != NULL) {
|
||||||
cmd_find_clear_state(fs, flags);
|
cmd_find_clear_state(fs, flags);
|
||||||
|
|
||||||
fs->wl = active_get_effective_winlink(c, c->session);
|
fs->wp = c->session->curw->window->active;
|
||||||
fs->wp = fs->wl->window->active;
|
|
||||||
if (fs->wp == NULL) {
|
if (fs->wp == NULL) {
|
||||||
cmd_find_from_session(fs, c->session, flags);
|
cmd_find_from_session(fs, c->session, flags);
|
||||||
return (0);
|
return (0);
|
||||||
}
|
}
|
||||||
fs->s = c->session;
|
fs->s = c->session;
|
||||||
|
fs->wl = fs->s->curw;
|
||||||
fs->w = fs->wl->window;
|
fs->w = fs->wl->window;
|
||||||
|
|
||||||
cmd_find_log_state(__func__, fs);
|
cmd_find_log_state(__func__, fs);
|
||||||
@@ -931,57 +931,6 @@ unknown_pane:
|
|||||||
return (cmd_find_from_nothing(fs, flags));
|
return (cmd_find_from_nothing(fs, flags));
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Find state from mouse target. */
|
|
||||||
static int
|
|
||||||
cmd_find_target_from_mouse(struct cmd_find_state *fs, struct cmdq_item *item,
|
|
||||||
enum cmd_find_type type)
|
|
||||||
{
|
|
||||||
struct mouse_event *m = &cmdq_get_event(item)->m;
|
|
||||||
struct client *c = cmdq_get_client(item);
|
|
||||||
struct client *tc = cmdq_get_target_client(item);
|
|
||||||
|
|
||||||
switch (type) {
|
|
||||||
case CMD_FIND_PANE:
|
|
||||||
if (m->s != -1 && m->w == -1 && m->wp == -1 &&
|
|
||||||
(fs->s = session_find_by_id(m->s)) != NULL) {
|
|
||||||
if (tc != NULL && tc->session == fs->s)
|
|
||||||
fs->wl = active_get_effective_winlink(tc, fs->s);
|
|
||||||
else if (c != NULL && c->session == fs->s)
|
|
||||||
fs->wl = active_get_effective_winlink(c, fs->s);
|
|
||||||
else
|
|
||||||
fs->wl = fs->s->curw;
|
|
||||||
if (fs->wl != NULL) {
|
|
||||||
fs->w = fs->wl->window;
|
|
||||||
fs->wp = fs->w->active;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
fs->wp = cmd_mouse_pane(m, &fs->s, &fs->wl);
|
|
||||||
if (fs->wp != NULL) {
|
|
||||||
fs->w = fs->wl->window;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
/* FALLTHROUGH */
|
|
||||||
case CMD_FIND_WINDOW:
|
|
||||||
case CMD_FIND_SESSION:
|
|
||||||
fs->wl = cmd_mouse_window(m, &fs->s);
|
|
||||||
if (fs->s != NULL && m->w == -1 &&
|
|
||||||
tc != NULL && tc->session == fs->s)
|
|
||||||
fs->wl = active_get_effective_winlink(tc, fs->s);
|
|
||||||
else if (fs->s != NULL && m->w == -1 &&
|
|
||||||
c != NULL && c->session == fs->s)
|
|
||||||
fs->wl = active_get_effective_winlink(c, fs->s);
|
|
||||||
if (fs->wl == NULL && fs->s != NULL)
|
|
||||||
fs->wl = fs->s->curw;
|
|
||||||
if (fs->wl != NULL) {
|
|
||||||
fs->w = fs->wl->window;
|
|
||||||
fs->wp = fs->w->active;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
return (fs->wp == NULL ? -1 : 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Split target into pieces and resolve for the given type. Fills in the given
|
* Split target into pieces and resolve for the given type. Fills in the given
|
||||||
* state. Returns 0 on success or -1 on error.
|
* state. Returns 0 on success or -1 on error.
|
||||||
@@ -990,6 +939,7 @@ int
|
|||||||
cmd_find_target(struct cmd_find_state *fs, struct cmdq_item *item,
|
cmd_find_target(struct cmd_find_state *fs, struct cmdq_item *item,
|
||||||
const char *target, enum cmd_find_type type, int flags)
|
const char *target, enum cmd_find_type type, int flags)
|
||||||
{
|
{
|
||||||
|
struct mouse_event *m;
|
||||||
struct client *c;
|
struct client *c;
|
||||||
struct cmd_find_state current;
|
struct cmd_find_state current;
|
||||||
char *colon, *period, *copy = NULL, tmp[256];
|
char *colon, *period, *copy = NULL, tmp[256];
|
||||||
@@ -1065,15 +1015,35 @@ cmd_find_target(struct cmd_find_state *fs, struct cmdq_item *item,
|
|||||||
cmdq_error(item, "no current client");
|
cmdq_error(item, "no current client");
|
||||||
goto error;
|
goto error;
|
||||||
}
|
}
|
||||||
fs->wl = active_get_effective_winlink(c, c->session);
|
fs->wl = c->session->curw;
|
||||||
fs->wp = fs->wl->window->active;
|
fs->wp = c->session->curw->window->active;
|
||||||
fs->w = fs->wl->window;
|
fs->w = c->session->curw->window;
|
||||||
goto found;
|
goto found;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Mouse target is a plain = or {mouse}. */
|
/* Mouse target is a plain = or {mouse}. */
|
||||||
if (strcmp(target, "=") == 0 || strcmp(target, "{mouse}") == 0) {
|
if (strcmp(target, "=") == 0 || strcmp(target, "{mouse}") == 0) {
|
||||||
if (cmd_find_target_from_mouse(fs, item, type) != 0) {
|
m = &cmdq_get_event(item)->m;
|
||||||
|
switch (type) {
|
||||||
|
case CMD_FIND_PANE:
|
||||||
|
fs->wp = cmd_mouse_pane(m, &fs->s, &fs->wl);
|
||||||
|
if (fs->wp != NULL) {
|
||||||
|
fs->w = fs->wl->window;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
/* FALLTHROUGH */
|
||||||
|
case CMD_FIND_WINDOW:
|
||||||
|
case CMD_FIND_SESSION:
|
||||||
|
fs->wl = cmd_mouse_window(m, &fs->s);
|
||||||
|
if (fs->wl == NULL && fs->s != NULL)
|
||||||
|
fs->wl = fs->s->curw;
|
||||||
|
if (fs->wl != NULL) {
|
||||||
|
fs->w = fs->wl->window;
|
||||||
|
fs->wp = fs->w->active;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (fs->wp == NULL) {
|
||||||
if (~flags & CMD_FIND_QUIET)
|
if (~flags & CMD_FIND_QUIET)
|
||||||
cmdq_error(item, "no mouse target");
|
cmdq_error(item, "no mouse target");
|
||||||
goto error;
|
goto error;
|
||||||
|
|||||||
@@ -413,16 +413,13 @@ cmd_join_pane_exec(struct cmd *self, struct cmdq_item *item)
|
|||||||
struct cmd_find_state *current = cmdq_get_current(item);
|
struct cmd_find_state *current = cmdq_get_current(item);
|
||||||
struct cmd_find_state *target = cmdq_get_target(item);
|
struct cmd_find_state *target = cmdq_get_target(item);
|
||||||
struct cmd_find_state *source = cmdq_get_source(item);
|
struct cmd_find_state *source = cmdq_get_source(item);
|
||||||
struct client *c = cmdq_get_client(item);
|
|
||||||
struct client *tc = cmdq_get_target_client(item);
|
|
||||||
struct client *ac;
|
|
||||||
struct session *dst_s;
|
struct session *dst_s;
|
||||||
struct winlink *src_wl, *dst_wl;
|
struct winlink *src_wl, *dst_wl;
|
||||||
struct window *src_w, *dst_w;
|
struct window *src_w, *dst_w;
|
||||||
struct window_pane *src_wp, *dst_wp;
|
struct window_pane *src_wp, *dst_wp;
|
||||||
const char *s;
|
const char *s;
|
||||||
char *cause = NULL;
|
char *cause = NULL;
|
||||||
int flags = 0, dst_idx, local;
|
int flags = 0, dst_idx;
|
||||||
struct layout_cell *lc;
|
struct layout_cell *lc;
|
||||||
|
|
||||||
dst_s = target->s;
|
dst_s = target->s;
|
||||||
@@ -430,10 +427,6 @@ cmd_join_pane_exec(struct cmd *self, struct cmdq_item *item)
|
|||||||
dst_wp = target->wp;
|
dst_wp = target->wp;
|
||||||
dst_w = dst_wl->window;
|
dst_w = dst_wl->window;
|
||||||
dst_idx = dst_wl->idx;
|
dst_idx = dst_wl->idx;
|
||||||
ac = tc;
|
|
||||||
if (ac == NULL || ac->session != dst_s)
|
|
||||||
ac = c;
|
|
||||||
local = active_is_local_window(ac, dst_s);
|
|
||||||
|
|
||||||
if (cmd_get_entry(self) == &cmd_move_pane_entry) {
|
if (cmd_get_entry(self) == &cmd_move_pane_entry) {
|
||||||
if (args_has(args, 'M'))
|
if (args_has(args, 'M'))
|
||||||
@@ -519,18 +512,9 @@ cmd_join_pane_exec(struct cmd *self, struct cmdq_item *item)
|
|||||||
|
|
||||||
if (!args_has(args, 'd')) {
|
if (!args_has(args, 'd')) {
|
||||||
window_set_active_pane(dst_w, src_wp, 1);
|
window_set_active_pane(dst_w, src_wp, 1);
|
||||||
if (local)
|
session_select(dst_s, dst_idx);
|
||||||
active_select_window(ac, dst_s, dst_wl);
|
cmd_find_from_session(current, dst_s, 0);
|
||||||
else
|
server_redraw_session(dst_s);
|
||||||
session_select(dst_s, dst_idx);
|
|
||||||
if (local && ac != NULL && ac->session == dst_s)
|
|
||||||
cmd_find_from_client(current, ac, 0);
|
|
||||||
else
|
|
||||||
cmd_find_from_session(current, dst_s, 0);
|
|
||||||
if (local && ac != NULL && ac->session == dst_s)
|
|
||||||
server_redraw_client(ac);
|
|
||||||
else
|
|
||||||
server_redraw_session(dst_s);
|
|
||||||
} else
|
} else
|
||||||
server_status_session(dst_s);
|
server_status_session(dst_s);
|
||||||
|
|
||||||
|
|||||||
@@ -62,15 +62,12 @@ cmd_move_window_exec(struct cmd *self, struct cmdq_item *item)
|
|||||||
struct args *args = cmd_get_args(self);
|
struct args *args = cmd_get_args(self);
|
||||||
struct cmd_find_state *source = cmdq_get_source(item);
|
struct cmd_find_state *source = cmdq_get_source(item);
|
||||||
struct cmd_find_state target;
|
struct cmd_find_state target;
|
||||||
struct client *c = cmdq_get_client(item);
|
|
||||||
struct client *tc = cmdq_get_target_client(item);
|
|
||||||
struct client *ac;
|
|
||||||
const char *tflag = args_get(args, 't');
|
const char *tflag = args_get(args, 't');
|
||||||
struct session *src = source->s;
|
struct session *src = source->s;
|
||||||
struct session *dst;
|
struct session *dst;
|
||||||
struct winlink *wl = source->wl, *dstwl, *current_wl;
|
struct winlink *wl = source->wl;
|
||||||
char *cause;
|
char *cause;
|
||||||
int idx, kflag, dflag, sflag, before, local;
|
int idx, kflag, dflag, sflag, before;
|
||||||
|
|
||||||
if (args_has(args, 'r')) {
|
if (args_has(args, 'r')) {
|
||||||
if (cmd_find_target(&target, item, tflag, CMD_FIND_SESSION,
|
if (cmd_find_target(&target, item, tflag, CMD_FIND_SESSION,
|
||||||
@@ -93,37 +90,23 @@ cmd_move_window_exec(struct cmd *self, struct cmdq_item *item)
|
|||||||
dflag = args_has(args, 'd');
|
dflag = args_has(args, 'd');
|
||||||
sflag = args_has(args, 's');
|
sflag = args_has(args, 's');
|
||||||
|
|
||||||
ac = tc;
|
|
||||||
if (ac == NULL || ac->session != dst)
|
|
||||||
ac = c;
|
|
||||||
local = (!dflag && active_is_local_window(ac, dst));
|
|
||||||
|
|
||||||
before = args_has(args, 'b');
|
before = args_has(args, 'b');
|
||||||
if (args_has(args, 'a') || before) {
|
if (args_has(args, 'a') || before) {
|
||||||
if (target.wl != NULL)
|
if (target.wl != NULL)
|
||||||
idx = winlink_shuffle_up(dst, target.wl, before);
|
idx = winlink_shuffle_up(dst, target.wl, before);
|
||||||
else {
|
else
|
||||||
current_wl = active_get_effective_winlink(ac, dst);
|
idx = winlink_shuffle_up(dst, dst->curw, before);
|
||||||
idx = winlink_shuffle_up(dst, current_wl, before);
|
|
||||||
}
|
|
||||||
if (idx == -1)
|
if (idx == -1)
|
||||||
return (CMD_RETURN_ERROR);
|
return (CMD_RETURN_ERROR);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (server_link_window(src, wl, dst, idx, kflag, !dflag && !local,
|
if (server_link_window(src, wl, dst, idx, kflag, !dflag, &cause) != 0) {
|
||||||
&cause) != 0) {
|
|
||||||
cmdq_error(item, "%s", cause);
|
cmdq_error(item, "%s", cause);
|
||||||
free(cause);
|
free(cause);
|
||||||
return (CMD_RETURN_ERROR);
|
return (CMD_RETURN_ERROR);
|
||||||
}
|
}
|
||||||
dstwl = winlink_find_by_window(&dst->windows, wl->window);
|
|
||||||
if (cmd_get_entry(self) == &cmd_move_window_entry)
|
if (cmd_get_entry(self) == &cmd_move_window_entry)
|
||||||
server_unlink_window(src, wl);
|
server_unlink_window(src, wl);
|
||||||
if (local && dstwl != NULL) {
|
|
||||||
active_select_window(ac, dst, dstwl);
|
|
||||||
if (ac != NULL && ac->session == dst)
|
|
||||||
server_redraw_client(ac);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Renumber the winlinks in the src session only, the destination
|
* Renumber the winlinks in the src session only, the destination
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
/* $OpenBSD: cmd-new-window.c,v 1.103 2026/07/08 08:07:42 nicm Exp $ */
|
/* $OpenBSD: cmd-new-window.c,v 1.104 2026/09/03 21:04:11 nicm Exp $ */
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>
|
* Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>
|
||||||
@@ -58,11 +58,10 @@ cmd_new_window_exec(struct cmd *self, struct cmdq_item *item)
|
|||||||
struct cmd_find_state *target = cmdq_get_target(item);
|
struct cmd_find_state *target = cmdq_get_target(item);
|
||||||
struct spawn_context sc = { 0 };
|
struct spawn_context sc = { 0 };
|
||||||
struct client *tc = cmdq_get_target_client(item);
|
struct client *tc = cmdq_get_target_client(item);
|
||||||
struct client *ac;
|
|
||||||
struct session *s = target->s;
|
struct session *s = target->s;
|
||||||
struct winlink *wl = target->wl, *new_wl = NULL;
|
struct winlink *wl = target->wl, *new_wl = NULL;
|
||||||
int idx = target->idx, before, count = args_count(args);
|
int idx = target->idx, before;
|
||||||
int detached, local;
|
int count = args_count(args);
|
||||||
char *cause = NULL, *cp, *expanded, *wname = NULL;
|
char *cause = NULL, *cp, *expanded, *wname = NULL;
|
||||||
const char *template, *name;
|
const char *template, *name;
|
||||||
struct cmd_find_state fs;
|
struct cmd_find_state fs;
|
||||||
@@ -76,8 +75,10 @@ cmd_new_window_exec(struct cmd *self, struct cmdq_item *item)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* If -S and -n are given and -t is not and a single window with this
|
* If -S is given, select an existing window instead of creating a
|
||||||
* name already exists, select it.
|
* new one: preferring -t if it already points at a window, or
|
||||||
|
* otherwise -n if one exists with that name. If neither matches,
|
||||||
|
* fall through and create a window as normal.
|
||||||
*/
|
*/
|
||||||
name = args_get(args, 'n');
|
name = args_get(args, 'n');
|
||||||
if (name != NULL) {
|
if (name != NULL) {
|
||||||
@@ -90,37 +91,39 @@ cmd_new_window_exec(struct cmd *self, struct cmdq_item *item)
|
|||||||
wname = clean_name(expanded, 0);
|
wname = clean_name(expanded, 0);
|
||||||
free(expanded);
|
free(expanded);
|
||||||
}
|
}
|
||||||
if (args_has(args, 'S') && wname != NULL && target->idx == -1) {
|
if (args_has(args, 'S')) {
|
||||||
expanded = format_single(item, wname, c, s, NULL, NULL);
|
if (idx != -1)
|
||||||
RB_FOREACH(wl, winlinks, &s->windows) {
|
new_wl = winlink_find_by_index(&s->windows, idx);
|
||||||
if (strcmp(wl->window->name, expanded) != 0)
|
else if (wname != NULL) {
|
||||||
continue;
|
expanded = format_single(item, wname, c, s, NULL, NULL);
|
||||||
if (new_wl == NULL) {
|
RB_FOREACH(wl, winlinks, &s->windows) {
|
||||||
new_wl = wl;
|
if (strcmp(wl->window->name, expanded) != 0)
|
||||||
continue;
|
continue;
|
||||||
|
if (new_wl == NULL) {
|
||||||
|
new_wl = wl;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
cmdq_error(item, "multiple windows named %s",
|
||||||
|
wname);
|
||||||
|
free(wname);
|
||||||
|
free(expanded);
|
||||||
|
return (CMD_RETURN_ERROR);
|
||||||
}
|
}
|
||||||
cmdq_error(item, "multiple windows named %s", wname);
|
|
||||||
free(wname);
|
|
||||||
free(expanded);
|
free(expanded);
|
||||||
return (CMD_RETURN_ERROR);
|
|
||||||
}
|
}
|
||||||
free(expanded);
|
}
|
||||||
if (new_wl != NULL) {
|
|
||||||
free(wname);
|
/* Found an existing window to select instead of creating a new one. */
|
||||||
if (args_has(args, 'd'))
|
if (new_wl != NULL) {
|
||||||
return (CMD_RETURN_NORMAL);
|
free(wname);
|
||||||
if (active_select_window(c, s, new_wl) == 0) {
|
if (args_has(args, 'd'))
|
||||||
if (active_is_local_window(c, s) && c != NULL &&
|
|
||||||
c->session == s)
|
|
||||||
server_redraw_client(c);
|
|
||||||
else
|
|
||||||
server_redraw_session(s);
|
|
||||||
}
|
|
||||||
if (c != NULL && c->session != NULL)
|
|
||||||
active_get_effective_window(c, s)->latest = c;
|
|
||||||
recalculate_sizes();
|
|
||||||
return (CMD_RETURN_NORMAL);
|
return (CMD_RETURN_NORMAL);
|
||||||
}
|
if (session_set_current(s, new_wl) == 0)
|
||||||
|
server_redraw_session(s);
|
||||||
|
if (c != NULL && c->session != NULL)
|
||||||
|
s->curw->window->latest = c;
|
||||||
|
recalculate_sizes();
|
||||||
|
return (CMD_RETURN_NORMAL);
|
||||||
}
|
}
|
||||||
|
|
||||||
before = args_has(args, 'b');
|
before = args_has(args, 'b');
|
||||||
@@ -147,16 +150,10 @@ cmd_new_window_exec(struct cmd *self, struct cmdq_item *item)
|
|||||||
sc.idx = idx;
|
sc.idx = idx;
|
||||||
sc.cwd = args_get(args, 'c');
|
sc.cwd = args_get(args, 'c');
|
||||||
|
|
||||||
ac = tc;
|
|
||||||
if (ac == NULL || ac->session != s)
|
|
||||||
ac = c;
|
|
||||||
detached = args_has(args, 'd');
|
|
||||||
local = (!detached && active_is_local_window(ac, s));
|
|
||||||
|
|
||||||
sc.flags = 0;
|
sc.flags = 0;
|
||||||
if (args_has(args, 'E') || (count == 1 && *args_string(args, 0) == '\0'))
|
if (args_has(args, 'E') || (count == 1 && *args_string(args, 0) == '\0'))
|
||||||
sc.flags |= SPAWN_EMPTY;
|
sc.flags |= SPAWN_EMPTY;
|
||||||
if (detached || local)
|
if (args_has(args, 'd'))
|
||||||
sc.flags |= SPAWN_DETACHED;
|
sc.flags |= SPAWN_DETACHED;
|
||||||
if (args_has(args, 'k'))
|
if (args_has(args, 'k'))
|
||||||
sc.flags |= SPAWN_KILL;
|
sc.flags |= SPAWN_KILL;
|
||||||
@@ -166,13 +163,7 @@ cmd_new_window_exec(struct cmd *self, struct cmdq_item *item)
|
|||||||
free(cause);
|
free(cause);
|
||||||
goto fail;
|
goto fail;
|
||||||
}
|
}
|
||||||
if (local) {
|
if (!args_has(args, 'd') || new_wl == s->curw) {
|
||||||
if (active_select_window(ac, s, new_wl) == 0)
|
|
||||||
cmd_find_from_winlink(current, new_wl, 0);
|
|
||||||
if (ac != NULL && ac->session == s)
|
|
||||||
server_redraw_client(ac);
|
|
||||||
server_status_session_group(s);
|
|
||||||
} else if (!detached || new_wl == s->curw) {
|
|
||||||
cmd_find_from_winlink(current, new_wl, 0);
|
cmd_find_from_winlink(current, new_wl, 0);
|
||||||
server_redraw_session_group(s);
|
server_redraw_session_group(s);
|
||||||
} else
|
} else
|
||||||
|
|||||||
@@ -203,7 +203,7 @@ cmd_refresh_client_exec(struct cmd *self, struct cmdq_item *item)
|
|||||||
if (args_has(args, 'c'))
|
if (args_has(args, 'c'))
|
||||||
tc->pan_window = NULL;
|
tc->pan_window = NULL;
|
||||||
else {
|
else {
|
||||||
w = active_get_effective_window(tc, tc->session);
|
w = tc->session->curw->window;
|
||||||
if (tc->pan_window != w) {
|
if (tc->pan_window != w) {
|
||||||
tc->pan_window = w;
|
tc->pan_window = w;
|
||||||
tc->pan_ox = tty->oox;
|
tc->pan_ox = tty->oox;
|
||||||
|
|||||||
@@ -79,10 +79,7 @@ static void
|
|||||||
cmd_run_shell_print(struct job *job, const char *msg)
|
cmd_run_shell_print(struct job *job, const char *msg)
|
||||||
{
|
{
|
||||||
struct cmd_run_shell_data *cdata = job_get_data(job);
|
struct cmd_run_shell_data *cdata = job_get_data(job);
|
||||||
struct client *c;
|
|
||||||
struct session *s;
|
|
||||||
struct window_pane *wp = NULL;
|
struct window_pane *wp = NULL;
|
||||||
struct window *w;
|
|
||||||
struct cmd_find_state fs;
|
struct cmd_find_state fs;
|
||||||
struct window_mode_entry *wme;
|
struct window_mode_entry *wme;
|
||||||
|
|
||||||
@@ -93,12 +90,8 @@ cmd_run_shell_print(struct job *job, const char *msg)
|
|||||||
cmdq_print(cdata->item, "%s", msg);
|
cmdq_print(cdata->item, "%s", msg);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
c = cdata->client;
|
if (cdata->client != NULL && cdata->client->session != NULL)
|
||||||
if (c != NULL && c->session != NULL) {
|
wp = cdata->client->session->curw->window->active;
|
||||||
s = c->session;
|
|
||||||
w = active_get_effective_window(c, s);
|
|
||||||
wp = w->active;
|
|
||||||
}
|
|
||||||
if (wp == NULL && cmd_find_from_nothing(&fs, 0) == 0)
|
if (wp == NULL && cmd_find_from_nothing(&fs, 0) == 0)
|
||||||
wp = fs.wp;
|
wp = fs.wp;
|
||||||
if (wp == NULL)
|
if (wp == NULL)
|
||||||
|
|||||||
@@ -68,11 +68,10 @@ cmd_select_pane_redraw(struct window *w)
|
|||||||
TAILQ_FOREACH(c, &clients, entry) {
|
TAILQ_FOREACH(c, &clients, entry) {
|
||||||
if (c->session == NULL || (c->flags & CLIENT_CONTROL))
|
if (c->session == NULL || (c->flags & CLIENT_CONTROL))
|
||||||
continue;
|
continue;
|
||||||
if (active_get_effective_window(c, c->session) == w &&
|
if (c->session->curw->window == w && tty_window_bigger(&c->tty))
|
||||||
tty_window_bigger(&c->tty))
|
|
||||||
server_redraw_client(c);
|
server_redraw_client(c);
|
||||||
else {
|
else {
|
||||||
if (active_get_effective_window(c, c->session) == w)
|
if (c->session->curw->window == w)
|
||||||
c->flags |= CLIENT_REDRAWBORDERS;
|
c->flags |= CLIENT_REDRAWBORDERS;
|
||||||
if (session_has(c->session, w))
|
if (session_has(c->session, w))
|
||||||
c->flags |= CLIENT_REDRAWSTATUS;
|
c->flags |= CLIENT_REDRAWSTATUS;
|
||||||
|
|||||||
@@ -33,8 +33,8 @@ const struct cmd_entry cmd_select_window_entry = {
|
|||||||
.name = "select-window",
|
.name = "select-window",
|
||||||
.alias = "selectw",
|
.alias = "selectw",
|
||||||
|
|
||||||
.args = { "LlSnpTt:", 0, 0, NULL },
|
.args = { "lnpTt:", 0, 0, NULL },
|
||||||
.usage = "[-LlnpST] " CMD_TARGET_WINDOW_USAGE,
|
.usage = "[-lnpT] " CMD_TARGET_WINDOW_USAGE,
|
||||||
|
|
||||||
.target = { 't', CMD_FIND_WINDOW, 0 },
|
.target = { 't', CMD_FIND_WINDOW, 0 },
|
||||||
|
|
||||||
@@ -88,9 +88,8 @@ cmd_select_window_exec(struct cmd *self, struct cmdq_item *item)
|
|||||||
struct client *c = cmdq_get_client(item);
|
struct client *c = cmdq_get_client(item);
|
||||||
struct cmd_find_state *current = cmdq_get_current(item);
|
struct cmd_find_state *current = cmdq_get_current(item);
|
||||||
struct cmd_find_state *target = cmdq_get_target(item);
|
struct cmd_find_state *target = cmdq_get_target(item);
|
||||||
struct winlink *wl = target->wl, *current_wl;
|
struct winlink *wl = target->wl;
|
||||||
struct session *s = target->s;
|
struct session *s = target->s;
|
||||||
enum active_window_mode mode;
|
|
||||||
int next, previous, last, activity;
|
int next, previous, last, activity;
|
||||||
|
|
||||||
next = (cmd_get_entry(self) == &cmd_next_window_entry);
|
next = (cmd_get_entry(self) == &cmd_next_window_entry);
|
||||||
@@ -103,104 +102,48 @@ cmd_select_window_exec(struct cmd *self, struct cmdq_item *item)
|
|||||||
if (args_has(args, 'l'))
|
if (args_has(args, 'l'))
|
||||||
last = 1;
|
last = 1;
|
||||||
|
|
||||||
if (args_has(args, 'L') || args_has(args, 'S')) {
|
|
||||||
if (c == NULL || c->session == NULL) {
|
|
||||||
cmdq_error(item, "no current client");
|
|
||||||
return (CMD_RETURN_ERROR);
|
|
||||||
}
|
|
||||||
if (args_has(args, 'S'))
|
|
||||||
mode = ACTIVE_SHARED;
|
|
||||||
else
|
|
||||||
mode = ACTIVE_LOCAL;
|
|
||||||
active_set_local_window(c, s, mode);
|
|
||||||
if (!next &&
|
|
||||||
!previous &&
|
|
||||||
!last &&
|
|
||||||
!args_has(args, 't') &&
|
|
||||||
!args_has(args, 'T')) {
|
|
||||||
if (current->s == s) {
|
|
||||||
if (c->session == s)
|
|
||||||
cmd_find_from_client(current, c, 0);
|
|
||||||
else
|
|
||||||
cmd_find_from_session(current, s, 0);
|
|
||||||
}
|
|
||||||
if (c->session == s)
|
|
||||||
server_redraw_client(c);
|
|
||||||
else
|
|
||||||
server_redraw_session(s);
|
|
||||||
cmdq_insert_hook(s, item, current, "after-select-window");
|
|
||||||
recalculate_sizes();
|
|
||||||
return (CMD_RETURN_NORMAL);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (active_is_local_window(c, s))
|
|
||||||
mode = ACTIVE_LOCAL;
|
|
||||||
else
|
|
||||||
mode = ACTIVE_SHARED;
|
|
||||||
if (next || previous || last) {
|
if (next || previous || last) {
|
||||||
activity = args_has(args, 'a');
|
activity = args_has(args, 'a');
|
||||||
if (next) {
|
if (next) {
|
||||||
if (active_next_window(c, s, activity) != 0) {
|
if (session_next(s, activity) != 0) {
|
||||||
cmdq_error(item, "no next window");
|
cmdq_error(item, "no next window");
|
||||||
return (CMD_RETURN_ERROR);
|
return (CMD_RETURN_ERROR);
|
||||||
}
|
}
|
||||||
} else if (previous) {
|
} else if (previous) {
|
||||||
if (active_previous_window(c, s, activity) != 0) {
|
if (session_previous(s, activity) != 0) {
|
||||||
cmdq_error(item, "no previous window");
|
cmdq_error(item, "no previous window");
|
||||||
return (CMD_RETURN_ERROR);
|
return (CMD_RETURN_ERROR);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (active_last_window(c, s) != 0) {
|
if (session_last(s) != 0) {
|
||||||
cmdq_error(item, "no last window");
|
cmdq_error(item, "no last window");
|
||||||
return (CMD_RETURN_ERROR);
|
return (CMD_RETURN_ERROR);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (mode == ACTIVE_LOCAL && c != NULL && c->session == s)
|
cmd_find_from_session(current, s, 0);
|
||||||
cmd_find_from_client(current, c, 0);
|
server_redraw_session(s);
|
||||||
else
|
|
||||||
cmd_find_from_session(current, s, 0);
|
|
||||||
if (mode == ACTIVE_LOCAL && c != NULL && c->session == s)
|
|
||||||
server_redraw_client(c);
|
|
||||||
else
|
|
||||||
server_redraw_session(s);
|
|
||||||
cmdq_insert_hook(s, item, current, "after-select-window");
|
cmdq_insert_hook(s, item, current, "after-select-window");
|
||||||
} else {
|
} else {
|
||||||
/*
|
/*
|
||||||
* If -T and select-window is invoked on same window as
|
* If -T and select-window is invoked on same window as
|
||||||
* current, switch to previous window.
|
* current, switch to previous window.
|
||||||
*/
|
*/
|
||||||
current_wl = active_get_effective_winlink(c, s);
|
if (args_has(args, 'T') && wl == s->curw) {
|
||||||
if (args_has(args, 'T') && wl == current_wl) {
|
if (session_last(s) != 0) {
|
||||||
if (active_last_window(c, s) != 0) {
|
|
||||||
cmdq_error(item, "no last window");
|
cmdq_error(item, "no last window");
|
||||||
return (-1);
|
return (-1);
|
||||||
}
|
}
|
||||||
if (current->s == s) {
|
if (current->s == s)
|
||||||
if (mode == ACTIVE_LOCAL && c != NULL &&
|
|
||||||
c->session == s)
|
|
||||||
cmd_find_from_client(current, c, 0);
|
|
||||||
else
|
|
||||||
cmd_find_from_session(current, s, 0);
|
|
||||||
}
|
|
||||||
if (mode == ACTIVE_LOCAL && c != NULL && c->session == s)
|
|
||||||
server_redraw_client(c);
|
|
||||||
else
|
|
||||||
server_redraw_session(s);
|
|
||||||
} else if (active_select_window(c, s, wl) == 0) {
|
|
||||||
if (mode == ACTIVE_LOCAL && c != NULL && c->session == s)
|
|
||||||
cmd_find_from_client(current, c, 0);
|
|
||||||
else
|
|
||||||
cmd_find_from_session(current, s, 0);
|
cmd_find_from_session(current, s, 0);
|
||||||
if (mode == ACTIVE_LOCAL && c != NULL && c->session == s)
|
server_redraw_session(s);
|
||||||
server_redraw_client(c);
|
} else if (session_select(s, wl->idx) == 0) {
|
||||||
else
|
cmd_find_from_session(current, s, 0);
|
||||||
server_redraw_session(s);
|
server_redraw_session(s);
|
||||||
}
|
}
|
||||||
cmdq_insert_hook(s, item, current, "after-select-window");
|
cmdq_insert_hook(s, item, current, "after-select-window");
|
||||||
}
|
}
|
||||||
if (c != NULL && c->session != NULL)
|
if (c != NULL && c->session != NULL)
|
||||||
active_get_effective_window(c, s)->latest = c;
|
s->curw->window->latest = c;
|
||||||
recalculate_sizes();
|
recalculate_sizes();
|
||||||
|
|
||||||
return (CMD_RETURN_NORMAL);
|
return (CMD_RETURN_NORMAL);
|
||||||
|
|||||||
@@ -48,14 +48,10 @@ cmd_swap_window_exec(struct cmd *self, struct cmdq_item *item)
|
|||||||
struct args *args = cmd_get_args(self);
|
struct args *args = cmd_get_args(self);
|
||||||
struct cmd_find_state *source = cmdq_get_source(item);
|
struct cmd_find_state *source = cmdq_get_source(item);
|
||||||
struct cmd_find_state *target = cmdq_get_target(item);
|
struct cmd_find_state *target = cmdq_get_target(item);
|
||||||
struct client *c = cmdq_get_client(item);
|
|
||||||
struct client *tc = cmdq_get_target_client(item);
|
|
||||||
struct client *ac;
|
|
||||||
struct session *src = source->s, *dst = target->s;
|
struct session *src = source->s, *dst = target->s;
|
||||||
struct session_group *sg_src, *sg_dst;
|
struct session_group *sg_src, *sg_dst;
|
||||||
struct winlink *wl_src = source->wl, *wl_dst = target->wl;
|
struct winlink *wl_src = source->wl, *wl_dst = target->wl;
|
||||||
struct window *w_src, *w_dst;
|
struct window *w_src, *w_dst;
|
||||||
int local_src, local_dst;
|
|
||||||
|
|
||||||
sg_src = session_group_contains(src);
|
sg_src = session_group_contains(src);
|
||||||
sg_dst = session_group_contains(dst);
|
sg_dst = session_group_contains(dst);
|
||||||
@@ -83,22 +79,10 @@ cmd_swap_window_exec(struct cmd *self, struct cmdq_item *item)
|
|||||||
|
|
||||||
if (marked_pane.wl == wl_src)
|
if (marked_pane.wl == wl_src)
|
||||||
marked_pane.wl = wl_dst;
|
marked_pane.wl = wl_dst;
|
||||||
ac = tc;
|
|
||||||
if (ac == NULL)
|
|
||||||
ac = c;
|
|
||||||
local_dst = active_is_local_window(ac, dst);
|
|
||||||
local_src = active_is_local_window(ac, src);
|
|
||||||
if (args_has(args, 'd')) {
|
if (args_has(args, 'd')) {
|
||||||
if (local_dst)
|
session_select(dst, wl_dst->idx);
|
||||||
active_select_window(ac, dst, wl_dst);
|
if (src != dst)
|
||||||
else
|
session_select(src, wl_src->idx);
|
||||||
session_select(dst, wl_dst->idx);
|
|
||||||
if (src != dst) {
|
|
||||||
if (local_src)
|
|
||||||
active_select_window(ac, src, wl_src);
|
|
||||||
else
|
|
||||||
session_select(src, wl_src->idx);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
session_group_synchronize_from(src);
|
session_group_synchronize_from(src);
|
||||||
server_redraw_session_group(src);
|
server_redraw_session_group(src);
|
||||||
|
|||||||
@@ -53,12 +53,11 @@ cmd_switch_client_exec(struct cmd *self, struct cmdq_item *item)
|
|||||||
struct cmd_find_state target;
|
struct cmd_find_state target;
|
||||||
const char *tflag = args_get(args, 't');
|
const char *tflag = args_get(args, 't');
|
||||||
enum cmd_find_type type;
|
enum cmd_find_type type;
|
||||||
int flags, visible, window_target;
|
int flags, visible, Zflag = args_has(args, 'Z');
|
||||||
int Zflag = args_has(args, 'Z');
|
|
||||||
struct client *c = cmdq_get_client(item);
|
struct client *c = cmdq_get_client(item);
|
||||||
struct client *tc = cmdq_get_target_client(item);
|
struct client *tc = cmdq_get_target_client(item);
|
||||||
struct session *s;
|
struct session *s;
|
||||||
struct winlink *wl, *current_wl;
|
struct winlink *wl;
|
||||||
struct window *w;
|
struct window *w;
|
||||||
struct window_pane *wp;
|
struct window_pane *wp;
|
||||||
const char *tablename;
|
const char *tablename;
|
||||||
@@ -66,13 +65,8 @@ cmd_switch_client_exec(struct cmd *self, struct cmdq_item *item)
|
|||||||
struct sort_criteria sort_crit;
|
struct sort_criteria sort_crit;
|
||||||
uid_t uid;
|
uid_t uid;
|
||||||
|
|
||||||
window_target = 0;
|
if (tflag != NULL &&
|
||||||
if (tflag != NULL) {
|
(tflag[strcspn(tflag, ":.%")] != '\0' || strcmp(tflag, "=") == 0)) {
|
||||||
if (tflag[strcspn(tflag, ":.%")] != '\0' ||
|
|
||||||
strcmp(tflag, "=") == 0)
|
|
||||||
window_target = 1;
|
|
||||||
}
|
|
||||||
if (window_target) {
|
|
||||||
type = CMD_FIND_PANE;
|
type = CMD_FIND_PANE;
|
||||||
flags = 0;
|
flags = 0;
|
||||||
} else {
|
} else {
|
||||||
@@ -143,8 +137,7 @@ cmd_switch_client_exec(struct cmd *self, struct cmdq_item *item)
|
|||||||
} else {
|
} else {
|
||||||
if (cmdq_get_client(item) == NULL)
|
if (cmdq_get_client(item) == NULL)
|
||||||
return (CMD_RETURN_NORMAL);
|
return (CMD_RETURN_NORMAL);
|
||||||
if (window_target && wl != NULL && wp != NULL &&
|
if (wl != NULL && wp != NULL && wp != wl->window->active) {
|
||||||
wp != wl->window->active) {
|
|
||||||
w = wl->window;
|
w = wl->window;
|
||||||
if (w->modal != NULL && wp != w->modal)
|
if (w->modal != NULL && wp != w->modal)
|
||||||
visible = 1;
|
visible = 1;
|
||||||
@@ -157,13 +150,9 @@ cmd_switch_client_exec(struct cmd *self, struct cmdq_item *item)
|
|||||||
if (!visible && window_pop_zoom(w))
|
if (!visible && window_pop_zoom(w))
|
||||||
server_redraw_window(w);
|
server_redraw_window(w);
|
||||||
}
|
}
|
||||||
if (window_target && wl != NULL) {
|
if (wl != NULL) {
|
||||||
active_select_window(tc, s, wl);
|
session_set_current(s, wl);
|
||||||
if (active_is_local_window(tc, s)) {
|
cmd_find_from_session(current, s, 0);
|
||||||
current_wl = active_get_effective_winlink(tc, s);
|
|
||||||
cmd_find_from_winlink(current, current_wl, 0);
|
|
||||||
} else
|
|
||||||
cmd_find_from_session(current, s, 0);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -222,28 +222,6 @@ control_client_session_changed_cb(__unused const char *name,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Notify control clients that a client changed its effective window. */
|
|
||||||
static void
|
|
||||||
control_client_window_changed_cb(__unused const char *name,
|
|
||||||
struct event_payload *ep, __unused void *sink_data)
|
|
||||||
{
|
|
||||||
struct client *cc = event_payload_get_client(ep, "client");
|
|
||||||
struct session *s = event_payload_get_session(ep, "session");
|
|
||||||
struct window *w = event_payload_get_window(ep, "window");
|
|
||||||
struct client *c;
|
|
||||||
|
|
||||||
if (cc == NULL || s == NULL || w == NULL)
|
|
||||||
return;
|
|
||||||
|
|
||||||
TAILQ_FOREACH(c, &clients, entry) {
|
|
||||||
if (!CONTROL_SHOULD_NOTIFY_CLIENT(c))
|
|
||||||
continue;
|
|
||||||
|
|
||||||
control_write(c, "%%client-window-changed %s $%u @%u",
|
|
||||||
cc->name, s->id, w->id);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Notify control clients that a client detached. */
|
/* Notify control clients that a client detached. */
|
||||||
static void
|
static void
|
||||||
control_client_detached_cb(__unused const char *name, struct event_payload *ep,
|
control_client_detached_cb(__unused const char *name, struct event_payload *ep,
|
||||||
@@ -381,7 +359,6 @@ control_build_events(void)
|
|||||||
{ "window-linked", control_window_linked_cb },
|
{ "window-linked", control_window_linked_cb },
|
||||||
{ "window-renamed", control_window_renamed_cb },
|
{ "window-renamed", control_window_renamed_cb },
|
||||||
{ "client-session-changed", control_client_session_changed_cb },
|
{ "client-session-changed", control_client_session_changed_cb },
|
||||||
{ "client-window-changed", control_client_window_changed_cb },
|
|
||||||
{ "client-detached", control_client_detached_cb },
|
{ "client-detached", control_client_detached_cb },
|
||||||
{ "session-renamed", control_session_renamed_cb },
|
{ "session-renamed", control_session_renamed_cb },
|
||||||
{ "session-created", control_session_created_cb },
|
{ "session-created", control_session_created_cb },
|
||||||
|
|||||||
16
control.c
16
control.c
@@ -1,4 +1,4 @@
|
|||||||
/* $OpenBSD: control.c,v 1.66 2026/08/18 07:43:44 nicm Exp $ */
|
/* $OpenBSD: control.c,v 1.67 2026/09/03 21:35:38 nicm Exp $ */
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2012 Nicholas Marriott <nicholas.marriott@gmail.com>
|
* Copyright (c) 2012 Nicholas Marriott <nicholas.marriott@gmail.com>
|
||||||
@@ -455,8 +455,12 @@ control_flush_deferred(struct client *c)
|
|||||||
void
|
void
|
||||||
control_write(struct client *c, const char *fmt, ...)
|
control_write(struct client *c, const char *fmt, ...)
|
||||||
{
|
{
|
||||||
va_list ap;
|
struct control_state *cs = c->control_state;
|
||||||
char *line;
|
char *line;
|
||||||
|
va_list ap;
|
||||||
|
|
||||||
|
if (cs == NULL)
|
||||||
|
return;
|
||||||
|
|
||||||
va_start(ap, fmt);
|
va_start(ap, fmt);
|
||||||
xvasprintf(&line, fmt, ap);
|
xvasprintf(&line, fmt, ap);
|
||||||
@@ -479,6 +483,9 @@ control_write_guard(struct client *c, const char *guard, long t, u_int number,
|
|||||||
struct control_state *cs = c->control_state;
|
struct control_state *cs = c->control_state;
|
||||||
char *line;
|
char *line;
|
||||||
|
|
||||||
|
if (cs == NULL)
|
||||||
|
return;
|
||||||
|
|
||||||
if (strcmp(guard, "begin") == 0)
|
if (strcmp(guard, "begin") == 0)
|
||||||
cs->guard_depth++;
|
cs->guard_depth++;
|
||||||
|
|
||||||
@@ -503,6 +510,9 @@ control_notify_write(struct client *c, const char *fmt, ...)
|
|||||||
va_list ap;
|
va_list ap;
|
||||||
char *line;
|
char *line;
|
||||||
|
|
||||||
|
if (cs == NULL)
|
||||||
|
return;
|
||||||
|
|
||||||
va_start(ap, fmt);
|
va_start(ap, fmt);
|
||||||
xvasprintf(&line, fmt, ap);
|
xvasprintf(&line, fmt, ap);
|
||||||
va_end(ap);
|
va_end(ap);
|
||||||
|
|||||||
89
format.c
89
format.c
@@ -806,7 +806,7 @@ format_cb_window_active_clients(struct format_tree *ft)
|
|||||||
if (client_session == NULL)
|
if (client_session == NULL)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
if (w == active_get_effective_window(loop, client_session))
|
if (w == client_session->curw->window)
|
||||||
n++;
|
n++;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -838,7 +838,7 @@ format_cb_window_active_clients_list(struct format_tree *ft)
|
|||||||
if (client_session == NULL)
|
if (client_session == NULL)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
if (w == active_get_effective_window(loop, client_session)) {
|
if (w == client_session->curw->window) {
|
||||||
if (EVBUFFER_LENGTH(buffer) > 0)
|
if (EVBUFFER_LENGTH(buffer) > 0)
|
||||||
evbuffer_add(buffer, ",", 1);
|
evbuffer_add(buffer, ",", 1);
|
||||||
evbuffer_add_printf(buffer, "%s", loop->name);
|
evbuffer_add_printf(buffer, "%s", loop->name);
|
||||||
@@ -3066,48 +3066,11 @@ format_cb_sixel_support(__unused struct format_tree *ft)
|
|||||||
static void *
|
static void *
|
||||||
format_cb_active_window_index(struct format_tree *ft)
|
format_cb_active_window_index(struct format_tree *ft)
|
||||||
{
|
{
|
||||||
struct winlink *wl;
|
if (ft->s != NULL)
|
||||||
|
return (format_printf("%u", ft->s->curw->idx));
|
||||||
if (ft->s != NULL) {
|
|
||||||
wl = active_get_effective_winlink(ft->c, ft->s);
|
|
||||||
if (wl != NULL)
|
|
||||||
return (format_printf("%u", wl->idx));
|
|
||||||
}
|
|
||||||
return (NULL);
|
return (NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Callback for active_window_id. */
|
|
||||||
static void *
|
|
||||||
format_cb_active_window_id(struct format_tree *ft)
|
|
||||||
{
|
|
||||||
struct winlink *wl;
|
|
||||||
|
|
||||||
if (ft->s != NULL) {
|
|
||||||
wl = active_get_effective_winlink(ft->c, ft->s);
|
|
||||||
if (wl != NULL)
|
|
||||||
return (format_printf("@%u", wl->window->id));
|
|
||||||
}
|
|
||||||
return (NULL);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Callback for local_active_window. */
|
|
||||||
static void *
|
|
||||||
format_cb_local_active_window(struct format_tree *ft)
|
|
||||||
{
|
|
||||||
if (ft->c != NULL && ft->s != NULL)
|
|
||||||
return (format_printf("%d", active_is_local_window(ft->c, ft->s)));
|
|
||||||
return (xstrdup("0"));
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Callback for active_window_mode. */
|
|
||||||
static void *
|
|
||||||
format_cb_active_window_mode(struct format_tree *ft)
|
|
||||||
{
|
|
||||||
if (ft->c != NULL && ft->s != NULL && active_is_local_window(ft->c, ft->s))
|
|
||||||
return (xstrdup("local"));
|
|
||||||
return (xstrdup("shared"));
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Callback for last_window_index. */
|
/* Callback for last_window_index. */
|
||||||
static void *
|
static void *
|
||||||
format_cb_last_window_index(struct format_tree *ft)
|
format_cb_last_window_index(struct format_tree *ft)
|
||||||
@@ -3125,11 +3088,8 @@ format_cb_last_window_index(struct format_tree *ft)
|
|||||||
static void *
|
static void *
|
||||||
format_cb_window_active(struct format_tree *ft)
|
format_cb_window_active(struct format_tree *ft)
|
||||||
{
|
{
|
||||||
struct winlink *wl;
|
|
||||||
|
|
||||||
if (ft->wl != NULL) {
|
if (ft->wl != NULL) {
|
||||||
wl = active_get_effective_winlink(ft->c, ft->wl->session);
|
if (ft->wl == ft->wl->session->curw)
|
||||||
if (ft->wl == wl)
|
|
||||||
return (xstrdup("1"));
|
return (xstrdup("1"));
|
||||||
return (xstrdup("0"));
|
return (xstrdup("0"));
|
||||||
}
|
}
|
||||||
@@ -3209,7 +3169,7 @@ static void *
|
|||||||
format_cb_window_flags(struct format_tree *ft)
|
format_cb_window_flags(struct format_tree *ft)
|
||||||
{
|
{
|
||||||
if (ft->wl != NULL)
|
if (ft->wl != NULL)
|
||||||
return (xstrdup(window_printable_flags(ft->c, ft->wl, 1)));
|
return (xstrdup(window_printable_flags(ft->wl, 1)));
|
||||||
return (NULL);
|
return (NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -3396,7 +3356,7 @@ static void *
|
|||||||
format_cb_window_raw_flags(struct format_tree *ft)
|
format_cb_window_raw_flags(struct format_tree *ft)
|
||||||
{
|
{
|
||||||
if (ft->wl != NULL)
|
if (ft->wl != NULL)
|
||||||
return (xstrdup(window_printable_flags(ft->c, ft->wl, 0)));
|
return (xstrdup(window_printable_flags(ft->wl, 0)));
|
||||||
return (NULL);
|
return (NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -3606,15 +3566,9 @@ struct format_table_entry {
|
|||||||
* Only variables which are added by the caller go into the tree.
|
* Only variables which are added by the caller go into the tree.
|
||||||
*/
|
*/
|
||||||
static const struct format_table_entry format_table[] = {
|
static const struct format_table_entry format_table[] = {
|
||||||
{ "active_window_id", FORMAT_TABLE_STRING,
|
|
||||||
format_cb_active_window_id
|
|
||||||
},
|
|
||||||
{ "active_window_index", FORMAT_TABLE_STRING,
|
{ "active_window_index", FORMAT_TABLE_STRING,
|
||||||
format_cb_active_window_index
|
format_cb_active_window_index
|
||||||
},
|
},
|
||||||
{ "active_window_mode", FORMAT_TABLE_STRING,
|
|
||||||
format_cb_active_window_mode
|
|
||||||
},
|
|
||||||
{ "alternate_on", FORMAT_TABLE_STRING,
|
{ "alternate_on", FORMAT_TABLE_STRING,
|
||||||
format_cb_alternate_on
|
format_cb_alternate_on
|
||||||
},
|
},
|
||||||
@@ -3792,9 +3746,6 @@ static const struct format_table_entry format_table[] = {
|
|||||||
{ "last_window_index", FORMAT_TABLE_STRING,
|
{ "last_window_index", FORMAT_TABLE_STRING,
|
||||||
format_cb_last_window_index
|
format_cb_last_window_index
|
||||||
},
|
},
|
||||||
{ "local_active_window", FORMAT_TABLE_STRING,
|
|
||||||
format_cb_local_active_window
|
|
||||||
},
|
|
||||||
{ "mouse_all_flag", FORMAT_TABLE_STRING,
|
{ "mouse_all_flag", FORMAT_TABLE_STRING,
|
||||||
format_cb_mouse_all_flag
|
format_cb_mouse_all_flag
|
||||||
},
|
},
|
||||||
@@ -5344,18 +5295,15 @@ format_add_window_neighbour(struct format_tree *nft, struct winlink *wl,
|
|||||||
struct session *s, const char *prefix)
|
struct session *s, const char *prefix)
|
||||||
{
|
{
|
||||||
struct options_entry *o;
|
struct options_entry *o;
|
||||||
struct winlink *current;
|
|
||||||
const char *oname;
|
const char *oname;
|
||||||
char *key, *prefixed, *oval;
|
char *key, *prefixed, *oval;
|
||||||
|
|
||||||
current = active_get_effective_winlink(nft->c, s);
|
|
||||||
|
|
||||||
xasprintf(&key, "%s_window_index", prefix);
|
xasprintf(&key, "%s_window_index", prefix);
|
||||||
format_add(nft, key, "%u", wl->idx);
|
format_add(nft, key, "%u", wl->idx);
|
||||||
free(key);
|
free(key);
|
||||||
|
|
||||||
xasprintf(&key, "%s_window_active", prefix);
|
xasprintf(&key, "%s_window_active", prefix);
|
||||||
format_add(nft, key, "%d", wl == current);
|
format_add(nft, key, "%d", wl == s->curw);
|
||||||
free(key);
|
free(key);
|
||||||
|
|
||||||
o = options_first(wl->window->options);
|
o = options_first(wl->window->options);
|
||||||
@@ -5383,10 +5331,10 @@ format_loop_windows(struct format_expand_state *es, const char *fmt)
|
|||||||
struct cmdq_item *item = ft->item;
|
struct cmdq_item *item = ft->item;
|
||||||
struct format_tree *nft;
|
struct format_tree *nft;
|
||||||
struct format_expand_state next;
|
struct format_expand_state next;
|
||||||
char *all, *active_fmt, *use, *expanded, *value;
|
char *all, *active, *use, *expanded, *value;
|
||||||
struct evbuffer *buffer;
|
struct evbuffer *buffer;
|
||||||
size_t size;
|
size_t size;
|
||||||
struct winlink *wl, **l, *active_wl;
|
struct winlink *wl, **l;
|
||||||
struct window *w;
|
struct window *w;
|
||||||
int i, n;
|
int i, n;
|
||||||
|
|
||||||
@@ -5395,9 +5343,9 @@ format_loop_windows(struct format_expand_state *es, const char *fmt)
|
|||||||
return (NULL);
|
return (NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (format_choose(es, fmt, &all, &active_fmt, 0) != 0) {
|
if (format_choose(es, fmt, &all, &active, 0) != 0) {
|
||||||
all = xstrdup(fmt);
|
all = xstrdup(fmt);
|
||||||
active_fmt = NULL;
|
active = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
buffer = evbuffer_new();
|
buffer = evbuffer_new();
|
||||||
@@ -5405,13 +5353,12 @@ format_loop_windows(struct format_expand_state *es, const char *fmt)
|
|||||||
fatalx("out of memory");
|
fatalx("out of memory");
|
||||||
|
|
||||||
l = sort_get_winlinks_session(s, &n, sc);
|
l = sort_get_winlinks_session(s, &n, sc);
|
||||||
active_wl = active_get_effective_winlink(ft->c, s);
|
|
||||||
for (i = 0; i < n; i++) {
|
for (i = 0; i < n; i++) {
|
||||||
wl = l[i];
|
wl = l[i];
|
||||||
w = wl->window;
|
w = wl->window;
|
||||||
format_log(es, "window loop: %u @%u", wl->idx, w->id);
|
format_log(es, "window loop: %u @%u", wl->idx, w->id);
|
||||||
if (active_fmt != NULL && wl == active_wl)
|
if (active != NULL && wl == s->curw)
|
||||||
use = active_fmt;
|
use = active;
|
||||||
else
|
else
|
||||||
use = all;
|
use = all;
|
||||||
nft = format_create(c, item, FORMAT_WINDOW|w->id, ft->flags);
|
nft = format_create(c, item, FORMAT_WINDOW|w->id, ft->flags);
|
||||||
@@ -5420,11 +5367,11 @@ format_loop_windows(struct format_expand_state *es, const char *fmt)
|
|||||||
format_add(nft, "loop_last_flag", "%d", i == n - 1);
|
format_add(nft, "loop_last_flag", "%d", i == n - 1);
|
||||||
|
|
||||||
/* Add neighbour window data to the format tree. */
|
/* Add neighbour window data to the format tree. */
|
||||||
if (i > 0 && l[i - 1] == active_wl)
|
if (i > 0 && l[i - 1] == s->curw)
|
||||||
format_add(nft, "window_after_active", "1");
|
format_add(nft, "window_after_active", "1");
|
||||||
else
|
else
|
||||||
format_add(nft, "window_after_active", "0");
|
format_add(nft, "window_after_active", "0");
|
||||||
if (i + 1 < n && l[i + 1] == active_wl)
|
if (i + 1 < n && l[i + 1] == s->curw)
|
||||||
format_add(nft, "window_before_active", "1");
|
format_add(nft, "window_before_active", "1");
|
||||||
else
|
else
|
||||||
format_add(nft, "window_before_active", "0");
|
format_add(nft, "window_before_active", "0");
|
||||||
@@ -5444,7 +5391,7 @@ format_loop_windows(struct format_expand_state *es, const char *fmt)
|
|||||||
free(expanded);
|
free(expanded);
|
||||||
}
|
}
|
||||||
|
|
||||||
free(active_fmt);
|
free(active);
|
||||||
free(all);
|
free(all);
|
||||||
|
|
||||||
if ((size = EVBUFFER_LENGTH(buffer)) != 0)
|
if ((size = EVBUFFER_LENGTH(buffer)) != 0)
|
||||||
@@ -7032,7 +6979,7 @@ format_defaults(struct format_tree *ft, struct client *c, struct session *s,
|
|||||||
if (s == NULL && c != NULL)
|
if (s == NULL && c != NULL)
|
||||||
s = c->session;
|
s = c->session;
|
||||||
if (wl == NULL && s != NULL)
|
if (wl == NULL && s != NULL)
|
||||||
wl = active_get_effective_winlink(c, s);
|
wl = s->curw;
|
||||||
if (wp == NULL && wl != NULL)
|
if (wp == NULL && wl != NULL)
|
||||||
wp = wl->window->active;
|
wp = wl->window->active;
|
||||||
|
|
||||||
|
|||||||
2
grid.c
2
grid.c
@@ -1760,6 +1760,8 @@ grid_line_flags_string(int flags)
|
|||||||
strlcat(s, "START_OUTPUT,", sizeof s);
|
strlcat(s, "START_OUTPUT,", sizeof s);
|
||||||
if (flags & GRID_LINE_END_OUTPUT)
|
if (flags & GRID_LINE_END_OUTPUT)
|
||||||
strlcat(s, "END_OUTPUT,", sizeof s);
|
strlcat(s, "END_OUTPUT,", sizeof s);
|
||||||
|
if (flags & GRID_LINE_END_OUTPUT_STATUS)
|
||||||
|
strlcat(s, "END_OUTPUT_STATUS,", sizeof s);
|
||||||
if (flags & GRID_LINE_HYPERLINK)
|
if (flags & GRID_LINE_HYPERLINK)
|
||||||
strlcat(s, "HYPERLINK,", sizeof s);
|
strlcat(s, "HYPERLINK,", sizeof s);
|
||||||
if (*s == '\0')
|
if (*s == '\0')
|
||||||
|
|||||||
74
input.c
74
input.c
@@ -3194,13 +3194,14 @@ input_osc_112(struct input_ctx *ictx, const char *p)
|
|||||||
|
|
||||||
/* Parse the OSC 133 D exit status. */
|
/* Parse the OSC 133 D exit status. */
|
||||||
static int
|
static int
|
||||||
input_osc_133_exit_status(const char *p)
|
input_osc_133_exit_status(const char *p, int *present)
|
||||||
{
|
{
|
||||||
const char *end;
|
const char *end;
|
||||||
char *copy;
|
char *copy;
|
||||||
const char *errstr;
|
char *endptr;
|
||||||
long long status;
|
long long status;
|
||||||
|
|
||||||
|
*present = 0;
|
||||||
if (p[1] != ';' || p[2] == '\0' || strchr(p + 2, '=') == p + 2)
|
if (p[1] != ';' || p[2] == '\0' || strchr(p + 2, '=') == p + 2)
|
||||||
return (0);
|
return (0);
|
||||||
end = strchr(p + 2, ';');
|
end = strchr(p + 2, ';');
|
||||||
@@ -3214,9 +3215,11 @@ input_osc_133_exit_status(const char *p)
|
|||||||
free(copy);
|
free(copy);
|
||||||
return (0);
|
return (0);
|
||||||
}
|
}
|
||||||
status = strtonum(copy, 0, 255, &errstr);
|
*present = 1;
|
||||||
|
errno = 0;
|
||||||
|
status = (int)strtol(copy, &endptr, 10);
|
||||||
free(copy);
|
free(copy);
|
||||||
if (errstr != NULL)
|
if (errno != 0 || endptr == copy || status < 0 || status > 255)
|
||||||
return (255);
|
return (255);
|
||||||
return (status);
|
return (status);
|
||||||
}
|
}
|
||||||
@@ -3261,6 +3264,22 @@ input_fire_command_event(struct window_pane *wp, const char *name)
|
|||||||
events_fire(name, ep);
|
events_fire(name, ep);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Check if an OSC 133 prompt is secondary or a continuation. */
|
||||||
|
static int
|
||||||
|
input_osc_133_secondary_prompt(const char *p)
|
||||||
|
{
|
||||||
|
const char *cp;
|
||||||
|
|
||||||
|
while ((cp = strstr(p, ";k=")) != NULL) {
|
||||||
|
cp += 3;
|
||||||
|
if ((*cp == 's' || *cp == 'c') &&
|
||||||
|
(cp[1] == '\0' || cp[1] == ';'))
|
||||||
|
return (1);
|
||||||
|
p = cp;
|
||||||
|
}
|
||||||
|
return (0);
|
||||||
|
}
|
||||||
|
|
||||||
/* Handle the OSC 133 sequence. */
|
/* Handle the OSC 133 sequence. */
|
||||||
static void
|
static void
|
||||||
input_osc_133(struct input_ctx *ictx, const char *p)
|
input_osc_133(struct input_ctx *ictx, const char *p)
|
||||||
@@ -3270,8 +3289,7 @@ input_osc_133(struct input_ctx *ictx, const char *p)
|
|||||||
struct grid *gd = s->grid;
|
struct grid *gd = s->grid;
|
||||||
u_int line = s->cy + gd->hsize;
|
u_int line = s->cy + gd->hsize;
|
||||||
struct grid_line *gl = NULL;
|
struct grid_line *gl = NULL;
|
||||||
const char *cp;
|
int status, status_present;
|
||||||
int status;
|
|
||||||
|
|
||||||
if (line < gd->hsize + gd->sy)
|
if (line < gd->hsize + gd->sy)
|
||||||
gl = grid_get_line(gd, line);
|
gl = grid_get_line(gd, line);
|
||||||
@@ -3280,9 +3298,17 @@ 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);
|
if (!(gl->flags & (GRID_LINE_START_PROMPT|
|
||||||
gl->osc133_data.prompt_col = s->cx;
|
GRID_LINE_SECOND_PROMPT))) {
|
||||||
gl->flags |= GRID_LINE_START_PROMPT;
|
gl->osc133_data.prompt_col = s->cx;
|
||||||
|
if (input_osc_133_secondary_prompt(p))
|
||||||
|
gl->flags |= GRID_LINE_SECOND_PROMPT;
|
||||||
|
else
|
||||||
|
gl->flags |= GRID_LINE_START_PROMPT;
|
||||||
|
log_debug("%s: osc133 %s at %u,%u", __func__,
|
||||||
|
input_osc_133_secondary_prompt(p) ? "secondary prompt" :
|
||||||
|
"prompt", s->cx, line);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (wp != NULL) {
|
if (wp != NULL) {
|
||||||
wp->last_prompt_time = time(NULL);
|
wp->last_prompt_time = time(NULL);
|
||||||
@@ -3291,25 +3317,33 @@ input_osc_133(struct input_ctx *ictx, const char *p)
|
|||||||
break;
|
break;
|
||||||
case 'P':
|
case 'P':
|
||||||
if (gl != NULL) {
|
if (gl != NULL) {
|
||||||
cp = strstr(p, ";k=s");
|
if (!(gl->flags & (GRID_LINE_START_PROMPT|
|
||||||
if (cp != NULL && (cp[4] == ';' || cp[4] == '\0'))
|
GRID_LINE_SECOND_PROMPT))) {
|
||||||
gl->flags |= GRID_LINE_SECOND_PROMPT;
|
gl->osc133_data.prompt_col = s->cx;
|
||||||
else
|
if (input_osc_133_secondary_prompt(p))
|
||||||
gl->flags |= GRID_LINE_START_PROMPT;
|
gl->flags |= GRID_LINE_SECOND_PROMPT;
|
||||||
gl->osc133_data.prompt_col = s->cx;
|
else
|
||||||
|
gl->flags |= GRID_LINE_START_PROMPT;
|
||||||
|
log_debug("%s: osc133 prompt at %u,%u", __func__, s->cx,
|
||||||
|
line);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case 'B':
|
case 'B':
|
||||||
case 'I':
|
case 'I':
|
||||||
if (gl != NULL) {
|
if (gl != NULL && !(gl->flags & GRID_LINE_START_COMMAND)) {
|
||||||
gl->flags |= GRID_LINE_START_COMMAND;
|
gl->flags |= GRID_LINE_START_COMMAND;
|
||||||
gl->osc133_data.cmd_col = s->cx;
|
gl->osc133_data.cmd_col = s->cx;
|
||||||
|
log_debug("%s: osc133 command at %u,%u", __func__, s->cx,
|
||||||
|
line);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case 'C':
|
case 'C':
|
||||||
if (gl != NULL) {
|
if (gl != NULL && !(gl->flags & GRID_LINE_START_OUTPUT)) {
|
||||||
gl->flags |= GRID_LINE_START_OUTPUT;
|
gl->flags |= GRID_LINE_START_OUTPUT;
|
||||||
gl->osc133_data.out_start_col = s->cx;
|
gl->osc133_data.out_start_col = s->cx;
|
||||||
|
log_debug("%s: osc133 output start at %u,%u", __func__, s->cx,
|
||||||
|
line);
|
||||||
}
|
}
|
||||||
if (wp != NULL) {
|
if (wp != NULL) {
|
||||||
wp->cmd_start_time = time(NULL);
|
wp->cmd_start_time = time(NULL);
|
||||||
@@ -3320,7 +3354,7 @@ input_osc_133(struct input_ctx *ictx, const char *p)
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case 'D':
|
case 'D':
|
||||||
status = input_osc_133_exit_status(p);
|
status = input_osc_133_exit_status(p, &status_present);
|
||||||
if (wp != NULL) {
|
if (wp != NULL) {
|
||||||
wp->cmd_end_time = time(NULL);
|
wp->cmd_end_time = time(NULL);
|
||||||
wp->flags &= ~PANE_CMDRUNNING;
|
wp->flags &= ~PANE_CMDRUNNING;
|
||||||
@@ -3329,8 +3363,12 @@ input_osc_133(struct input_ctx *ictx, const char *p)
|
|||||||
}
|
}
|
||||||
if (gl != NULL) {
|
if (gl != NULL) {
|
||||||
gl->flags |= GRID_LINE_END_OUTPUT;
|
gl->flags |= GRID_LINE_END_OUTPUT;
|
||||||
|
if (status_present)
|
||||||
|
gl->flags |= GRID_LINE_END_OUTPUT_STATUS;
|
||||||
gl->osc133_data.out_end_col = s->cx;
|
gl->osc133_data.out_end_col = s->cx;
|
||||||
gl->osc133_data.exit_status = status;
|
gl->osc133_data.exit_status = status;
|
||||||
|
log_debug("%s: osc133 output end at %u,%u (status %d)",
|
||||||
|
__func__, s->cx, line, status);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -31,8 +31,6 @@
|
|||||||
" 'Rename' 'r' {command-prompt -I '#S' {rename-session -- '%%'}}" \
|
" 'Rename' 'r' {command-prompt -I '#S' {rename-session -- '%%'}}" \
|
||||||
" 'Detach' 'd' {detach-client}" \
|
" 'Detach' 'd' {detach-client}" \
|
||||||
" ''" \
|
" ''" \
|
||||||
" '#{?local_active_window,Shared Window,Local Window}' 'L' {select-window #{?local_active_window,-S,-L}}" \
|
|
||||||
" ''" \
|
|
||||||
" 'New Session' 's' {new-session}" \
|
" 'New Session' 's' {new-session}" \
|
||||||
" 'New Window' 'w' {new-window}"
|
" 'New Window' 'w' {new-window}"
|
||||||
#define DEFAULT_WINDOW_MENU \
|
#define DEFAULT_WINDOW_MENU \
|
||||||
@@ -54,7 +52,9 @@
|
|||||||
" '#{?#{m/r:(copy|view)-mode,#{pane_mode}},Go To Top,}' '<' {send -X history-top}" \
|
" '#{?#{m/r:(copy|view)-mode,#{pane_mode}},Go To Top,}' '<' {send -X history-top}" \
|
||||||
" '#{?#{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,Line Numbers Off,Line Numbers On},}' 'L' {send -X line-numbers-toggle}" \
|
||||||
|
" '#{?#{==:#{pane_mode},copy-mode},#{?copy_fold_view,Fold View Off,Fold View On},}' 'O' {send -X fold-view-toggle}" \
|
||||||
|
" '#{?#{==:#{pane_mode},copy-mode},Edit,}' 'e' {if -F '#{selection_present}' {send -X open-selection} {send -X open-output}}" \
|
||||||
" '#{?#{==:#{pane_mode},copy-mode},#{?refresh_active,Refresh Off,Refresh On},}' 'r' {send -X refresh-toggle}" \
|
" '#{?#{==:#{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}" \
|
||||||
@@ -577,6 +577,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 -- '%%' } }",
|
||||||
@@ -585,15 +586,19 @@ key_bindings_init(void)
|
|||||||
"bind -Tcopy-mode Escape { send -X cancel }",
|
"bind -Tcopy-mode Escape { send -X cancel }",
|
||||||
"bind -Tcopy-mode C-[ { send -X cancel }",
|
"bind -Tcopy-mode C-[ { send -X cancel }",
|
||||||
"bind -Tcopy-mode Space { send -X page-down }",
|
"bind -Tcopy-mode Space { send -X page-down }",
|
||||||
|
"bind -Tcopy-mode Tab { send -X toggle-output }",
|
||||||
|
"bind -Tcopy-mode BTab { send -X toggle-output -a }",
|
||||||
"bind -Tcopy-mode , { send -X jump-reverse }",
|
"bind -Tcopy-mode , { send -X jump-reverse }",
|
||||||
"bind -Tcopy-mode \\; { send -X jump-again }",
|
"bind -Tcopy-mode \\; { send -X jump-again }",
|
||||||
"bind -Tcopy-mode F { command-prompt -P -1p'(jump backward)' { send -X jump-backward -- '%%' } }",
|
"bind -Tcopy-mode F { command-prompt -P -1p'(jump backward)' { send -X jump-backward -- '%%' } }",
|
||||||
"bind -Tcopy-mode L { send -X line-numbers-toggle }",
|
"bind -Tcopy-mode L { send -X line-numbers-toggle }",
|
||||||
"bind -Tcopy-mode N { send -X search-reverse }",
|
"bind -Tcopy-mode N { send -X search-reverse }",
|
||||||
|
"bind -Tcopy-mode O { send -X fold-view-toggle }",
|
||||||
"bind -Tcopy-mode P { send -X toggle-position }",
|
"bind -Tcopy-mode P { send -X toggle-position }",
|
||||||
"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 }",
|
||||||
@@ -602,7 +607,7 @@ key_bindings_init(void)
|
|||||||
"bind -Tcopy-mode t { command-prompt -P -1p'(jump to forward)' { send -X jump-to-forward -- '%%' } }",
|
"bind -Tcopy-mode t { command-prompt -P -1p'(jump to forward)' { send -X jump-to-forward -- '%%' } }",
|
||||||
"bind -Tcopy-mode Home { send -X start-of-line }",
|
"bind -Tcopy-mode Home { send -X start-of-line }",
|
||||||
"bind -Tcopy-mode End { send -X end-of-line }",
|
"bind -Tcopy-mode End { send -X end-of-line }",
|
||||||
"bind -Tcopy-mode MouseDown1Pane select-pane",
|
"bind -Tcopy-mode MouseDown1Pane { select-pane; send -X toggle-output -m }",
|
||||||
"bind -Tcopy-mode MouseDrag1Pane { select-pane; send -X begin-selection }",
|
"bind -Tcopy-mode MouseDrag1Pane { select-pane; send -X begin-selection }",
|
||||||
"bind -Tcopy-mode MouseDragEnd1Pane { send -X copy-pipe-and-cancel }",
|
"bind -Tcopy-mode MouseDragEnd1Pane { send -X copy-pipe-and-cancel }",
|
||||||
"bind -Tcopy-mode WheelUpPane { select-pane; send -N5 -X scroll-up }",
|
"bind -Tcopy-mode WheelUpPane { select-pane; send -N5 -X scroll-up }",
|
||||||
@@ -662,6 +667,8 @@ key_bindings_init(void)
|
|||||||
"bind -Tcopy-mode-vi Escape { send -X clear-selection }",
|
"bind -Tcopy-mode-vi Escape { send -X clear-selection }",
|
||||||
"bind -Tcopy-mode-vi C-[ { send -X clear-selection }",
|
"bind -Tcopy-mode-vi C-[ { send -X clear-selection }",
|
||||||
"bind -Tcopy-mode-vi Space { send -X begin-selection }",
|
"bind -Tcopy-mode-vi Space { send -X begin-selection }",
|
||||||
|
"bind -Tcopy-mode-vi Tab { send -X toggle-output }",
|
||||||
|
"bind -Tcopy-mode-vi BTab { send -X toggle-output -a }",
|
||||||
"bind -Tcopy-mode-vi '$' { send -X end-of-line }",
|
"bind -Tcopy-mode-vi '$' { send -X end-of-line }",
|
||||||
"bind -Tcopy-mode-vi , { send -X jump-reverse }",
|
"bind -Tcopy-mode-vi , { send -X jump-reverse }",
|
||||||
"bind -Tcopy-mode-vi / { command-prompt -P -T search -p'(search down)' { send -X search-forward -- '%%' } }",
|
"bind -Tcopy-mode-vi / { command-prompt -P -T search -p'(search down)' { send -X search-forward -- '%%' } }",
|
||||||
@@ -689,7 +696,10 @@ 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 O { send -X fold-view-toggle }",
|
||||||
"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 -- '%%' } }",
|
||||||
"bind -Tcopy-mode-vi V { send -X select-line }",
|
"bind -Tcopy-mode-vi V { send -X select-line }",
|
||||||
@@ -717,7 +727,7 @@ key_bindings_init(void)
|
|||||||
"bind -Tcopy-mode-vi % { send -X next-matching-bracket }",
|
"bind -Tcopy-mode-vi % { send -X next-matching-bracket }",
|
||||||
"bind -Tcopy-mode-vi Home { send -X start-of-line }",
|
"bind -Tcopy-mode-vi Home { send -X start-of-line }",
|
||||||
"bind -Tcopy-mode-vi End { send -X end-of-line }",
|
"bind -Tcopy-mode-vi End { send -X end-of-line }",
|
||||||
"bind -Tcopy-mode-vi MouseDown1Pane { select-pane }",
|
"bind -Tcopy-mode-vi MouseDown1Pane { select-pane; send -X toggle-output -m }",
|
||||||
"bind -Tcopy-mode-vi MouseDrag1Pane { select-pane; send -X begin-selection }",
|
"bind -Tcopy-mode-vi MouseDrag1Pane { select-pane; send -X begin-selection }",
|
||||||
"bind -Tcopy-mode-vi MouseDragEnd1Pane { send -X copy-pipe-and-cancel }",
|
"bind -Tcopy-mode-vi MouseDragEnd1Pane { send -X copy-pipe-and-cancel }",
|
||||||
"bind -Tcopy-mode-vi WheelUpPane { select-pane; send -N5 -X scroll-up }",
|
"bind -Tcopy-mode-vi WheelUpPane { select-pane; send -N5 -X scroll-up }",
|
||||||
|
|||||||
2
menu.c
2
menu.c
@@ -586,7 +586,7 @@ menu_display(struct menu *menu, int flags, int starting_choice,
|
|||||||
struct options *o;
|
struct options *o;
|
||||||
|
|
||||||
if (fs == NULL)
|
if (fs == NULL)
|
||||||
w = active_get_effective_window(c, c->session);
|
w = c->session->curw->window;
|
||||||
else
|
else
|
||||||
w = fs->w;
|
w = fs->w;
|
||||||
o = w->options;
|
o = w->options;
|
||||||
|
|||||||
@@ -114,7 +114,6 @@ static const char *options_table_theme_list[] = {
|
|||||||
static const char *options_table_copy_mode_line_numbers_list[] = {
|
static const char *options_table_copy_mode_line_numbers_list[] = {
|
||||||
"off", "default", "absolute", "relative", "hybrid", NULL
|
"off", "default", "absolute", "relative", "hybrid", NULL
|
||||||
};
|
};
|
||||||
|
|
||||||
/* Status line format. */
|
/* Status line format. */
|
||||||
#define OPTIONS_TABLE_STATUS_FORMAT1 \
|
#define OPTIONS_TABLE_STATUS_FORMAT1 \
|
||||||
"#[align=left range=left #{E:status-left-style}]" \
|
"#[align=left range=left #{E:status-left-style}]" \
|
||||||
@@ -1125,8 +1124,7 @@ const struct options_table_entry options_table[] = {
|
|||||||
{ .name = "status-style",
|
{ .name = "status-style",
|
||||||
.type = OPTIONS_TABLE_STRING,
|
.type = OPTIONS_TABLE_STRING,
|
||||||
.scope = OPTIONS_TABLE_SESSION,
|
.scope = OPTIONS_TABLE_SESSION,
|
||||||
.default_str = "bg=#{?local_active_window,themeblue,themegreen},"
|
.default_str = "bg=themegreen,fg=themeblack",
|
||||||
"fg=themeblack",
|
|
||||||
.flags = OPTIONS_TABLE_IS_STYLE,
|
.flags = OPTIONS_TABLE_IS_STYLE,
|
||||||
.separator = ",",
|
.separator = ",",
|
||||||
.text = "Style of the status line."
|
.text = "Style of the status line."
|
||||||
@@ -1385,6 +1383,13 @@ const struct options_table_entry options_table[] = {
|
|||||||
.text = "Style of search matches in copy mode."
|
.text = "Style of search matches in copy mode."
|
||||||
},
|
},
|
||||||
|
|
||||||
|
{ .name = "copy-mode-exit-status-format",
|
||||||
|
.type = OPTIONS_TABLE_STRING,
|
||||||
|
.scope = OPTIONS_TABLE_WINDOW|OPTIONS_TABLE_PANE,
|
||||||
|
.default_str = "#{?exit_status,#[fg=themered]!, }",
|
||||||
|
.text = "Format of OSC 133 exit status indicator in copy mode."
|
||||||
|
},
|
||||||
|
|
||||||
{ .name = "copy-mode-current-match-style",
|
{ .name = "copy-mode-current-match-style",
|
||||||
.type = OPTIONS_TABLE_STRING,
|
.type = OPTIONS_TABLE_STRING,
|
||||||
.scope = OPTIONS_TABLE_WINDOW,
|
.scope = OPTIONS_TABLE_WINDOW,
|
||||||
@@ -1983,8 +1988,6 @@ const struct options_table_entry options_table[] = {
|
|||||||
"Run when a client is resized."),
|
"Run when a client is resized."),
|
||||||
OPTIONS_TABLE_HOOK("client-session-changed", "",
|
OPTIONS_TABLE_HOOK("client-session-changed", "",
|
||||||
"Run when a client's attached session is changed."),
|
"Run when a client's attached session is changed."),
|
||||||
OPTIONS_TABLE_HOOK("client-window-changed", "",
|
|
||||||
"Run when a client's effective window is changed."),
|
|
||||||
OPTIONS_TABLE_HOOK("client-light-theme", "",
|
OPTIONS_TABLE_HOOK("client-light-theme", "",
|
||||||
"Run when a client switches to a light theme."),
|
"Run when a client switches to a light theme."),
|
||||||
OPTIONS_TABLE_HOOK("client-dark-theme", "",
|
OPTIONS_TABLE_HOOK("client-dark-theme", "",
|
||||||
|
|||||||
8
popup.c
8
popup.c
@@ -103,9 +103,9 @@ popup_reapply_styles(struct popup_data *pd)
|
|||||||
|
|
||||||
if (s == NULL)
|
if (s == NULL)
|
||||||
return;
|
return;
|
||||||
o = active_get_effective_window(c, s)->options;
|
o = s->curw->window->options;
|
||||||
|
|
||||||
ft = format_create_defaults(NULL, c, s, NULL, NULL);
|
ft = format_create_defaults(NULL, c, s, s->curw, NULL);
|
||||||
|
|
||||||
/* Reapply popup style from options. */
|
/* Reapply popup style from options. */
|
||||||
memcpy(&pd->defaults, &grid_default_cell, sizeof pd->defaults);
|
memcpy(&pd->defaults, &grid_default_cell, sizeof pd->defaults);
|
||||||
@@ -570,9 +570,9 @@ popup_display(int flags, enum box_lines lines, struct cmdq_item *item, u_int px,
|
|||||||
struct style sytmp;
|
struct style sytmp;
|
||||||
|
|
||||||
if (s != NULL)
|
if (s != NULL)
|
||||||
o = active_get_effective_window(c, s)->options;
|
o = s->curw->window->options;
|
||||||
else
|
else
|
||||||
o = active_get_effective_window(c, c->session)->options;
|
o = c->session->curw->window->options;
|
||||||
|
|
||||||
if (lines == BOX_LINES_DEFAULT)
|
if (lines == BOX_LINES_DEFAULT)
|
||||||
lines = options_get_number(o, "popup-border-lines");
|
lines = options_get_number(o, "popup-border-lines");
|
||||||
|
|||||||
@@ -1,151 +0,0 @@
|
|||||||
#!/bin/sh
|
|
||||||
|
|
||||||
PATH=/bin:/usr/bin
|
|
||||||
TERM=screen
|
|
||||||
LC_ALL=C.UTF-8
|
|
||||||
LANG=C.UTF-8
|
|
||||||
export TERM LC_ALL LANG
|
|
||||||
|
|
||||||
[ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux)
|
|
||||||
TMUX="$TEST_TMUX -LtestA$$ -f/dev/null"
|
|
||||||
|
|
||||||
TMPDIR=$(mktemp -d)
|
|
||||||
IN1="$TMPDIR/in1"
|
|
||||||
IN2="$TMPDIR/in2"
|
|
||||||
OUT1="$TMPDIR/out1"
|
|
||||||
OUT2="$TMPDIR/out2"
|
|
||||||
PID1=
|
|
||||||
PID2=
|
|
||||||
|
|
||||||
cleanup()
|
|
||||||
{
|
|
||||||
[ -n "$PID1" ] && kill "$PID1" 2>/dev/null
|
|
||||||
[ -n "$PID2" ] && kill "$PID2" 2>/dev/null
|
|
||||||
$TMUX kill-server 2>/dev/null
|
|
||||||
rm -rf "$TMPDIR"
|
|
||||||
}
|
|
||||||
trap cleanup EXIT
|
|
||||||
|
|
||||||
fail()
|
|
||||||
{
|
|
||||||
echo "$1"
|
|
||||||
echo "client1:"
|
|
||||||
cat "$OUT1" 2>/dev/null
|
|
||||||
echo "client2:"
|
|
||||||
cat "$OUT2" 2>/dev/null
|
|
||||||
exit 1
|
|
||||||
}
|
|
||||||
|
|
||||||
wait_for()
|
|
||||||
{
|
|
||||||
file=$1
|
|
||||||
pattern=$2
|
|
||||||
i=0
|
|
||||||
|
|
||||||
while [ "$i" -lt 60 ]; do
|
|
||||||
if grep -F -- "$pattern" "$file" >/dev/null 2>&1; then
|
|
||||||
return 0
|
|
||||||
fi
|
|
||||||
sleep 0.1
|
|
||||||
i=$((i + 1))
|
|
||||||
done
|
|
||||||
fail "missing '$pattern' in $file"
|
|
||||||
}
|
|
||||||
|
|
||||||
send1()
|
|
||||||
{
|
|
||||||
printf '%s\n' "$*" >&3
|
|
||||||
}
|
|
||||||
|
|
||||||
send2()
|
|
||||||
{
|
|
||||||
printf '%s\n' "$*" >&4
|
|
||||||
}
|
|
||||||
|
|
||||||
check_server()
|
|
||||||
{
|
|
||||||
out=$($TMUX display-message -p -t "$1" "$2")
|
|
||||||
[ "$out" = "$3" ] || fail "expected '$3' for $1 $2, got '$out'"
|
|
||||||
}
|
|
||||||
|
|
||||||
$TMUX kill-server 2>/dev/null
|
|
||||||
$TMUX new-session -d -s aw -x 80 -y 24 -n w0 || exit 1
|
|
||||||
$TMUX new-window -d -t aw: -n w1 || exit 1
|
|
||||||
$TMUX new-window -d -t aw: -n w2 || exit 1
|
|
||||||
$TMUX new-session -d -s aw2 -x 80 -y 24 -n x0 || exit 1
|
|
||||||
$TMUX new-window -d -t aw2: -n x1 || exit 1
|
|
||||||
|
|
||||||
mkfifo "$IN1" "$IN2" || exit 1
|
|
||||||
: >"$OUT1"
|
|
||||||
: >"$OUT2"
|
|
||||||
$TMUX -C attach-session -t aw <"$IN1" >"$OUT1" 2>&1 &
|
|
||||||
PID1=$!
|
|
||||||
$TMUX -C attach-session -t aw <"$IN2" >"$OUT2" 2>&1 &
|
|
||||||
PID2=$!
|
|
||||||
exec 3>"$IN1"
|
|
||||||
exec 4>"$IN2"
|
|
||||||
|
|
||||||
send1 'display-message -p C1READY'
|
|
||||||
send2 'display-message -p C2READY'
|
|
||||||
wait_for "$OUT1" C1READY
|
|
||||||
wait_for "$OUT2" C2READY
|
|
||||||
|
|
||||||
# Default shared selection: client 1 changes session current, client 2 follows.
|
|
||||||
send1 'display-message -p "C1A #{active_window_index} #{local_active_window} #{window_index}"'
|
|
||||||
wait_for "$OUT1" 'C1A 0 0 0'
|
|
||||||
send1 'select-window -t :1'
|
|
||||||
wait_for "$OUT2" '%session-window-changed $0 @1'
|
|
||||||
send2 'display-message -p "C2A #{active_window_index} #{local_active_window} #{window_index}"'
|
|
||||||
wait_for "$OUT2" 'C2A 1 0 1'
|
|
||||||
check_server 'aw:' '#{window_index}' '1'
|
|
||||||
|
|
||||||
# Local selection changes only client 1 and becomes the default command target.
|
|
||||||
send1 'select-window -L'
|
|
||||||
send1 'select-window -t :2'
|
|
||||||
send1 'display-message -p "C1B #{active_window_index} #{local_active_window} #{window_index}"'
|
|
||||||
wait_for "$OUT1" 'C1B 2 1 2'
|
|
||||||
send2 'display-message -p "C2B #{active_window_index} #{local_active_window} #{window_index}"'
|
|
||||||
wait_for "$OUT2" 'C2B 1 0 1'
|
|
||||||
check_server 'aw:' '#{window_index}' '1'
|
|
||||||
|
|
||||||
# Local last-window history is independent of the session shared stack.
|
|
||||||
send1 'last-window'
|
|
||||||
send1 'display-message -p "C1C #{active_window_index} #{window_index}"'
|
|
||||||
wait_for "$OUT1" 'C1C 1 1'
|
|
||||||
send1 'next-window'
|
|
||||||
send1 'display-message -p "C1D #{active_window_index} #{window_index}"'
|
|
||||||
wait_for "$OUT1" 'C1D 2 2'
|
|
||||||
check_server 'aw:' '#{window_index}' '1'
|
|
||||||
|
|
||||||
# Session switches preserve per-session local selections.
|
|
||||||
send1 'switch-client -t aw2'
|
|
||||||
send1 'select-window -L'
|
|
||||||
send1 'select-window -t :1'
|
|
||||||
send1 'display-message -p "C1E #{session_name} #{active_window_index} #{local_active_window}"'
|
|
||||||
wait_for "$OUT1" 'C1E aw2 1 1'
|
|
||||||
send1 'switch-client -t aw'
|
|
||||||
send1 'display-message -p "C1F #{session_name} #{active_window_index} #{local_active_window}"'
|
|
||||||
wait_for "$OUT1" 'C1F aw 2 1'
|
|
||||||
|
|
||||||
# Returning to shared mode immediately follows the session current window.
|
|
||||||
send1 'select-window -S'
|
|
||||||
send1 'display-message -p "C1G #{active_window_index} #{local_active_window} #{window_index}"'
|
|
||||||
wait_for "$OUT1" 'C1G 1 0 1'
|
|
||||||
|
|
||||||
# Invalid local windows recover to a valid session window without changing curw.
|
|
||||||
send1 'select-window -L'
|
|
||||||
send1 'select-window -t :2'
|
|
||||||
$TMUX kill-window -t aw:2 || fail 'kill-window failed'
|
|
||||||
send1 'display-message -p "C1H #{active_window_index} #{local_active_window} #{window_index}"'
|
|
||||||
wait_for "$OUT1" 'C1H 1 1 1'
|
|
||||||
check_server 'aw:' '#{window_index}' '1'
|
|
||||||
|
|
||||||
# Creating a window without -d in local mode selects it locally only.
|
|
||||||
send1 'new-window -n w3'
|
|
||||||
send1 'display-message -p "C1I #{local_active_window} #{window_name}"'
|
|
||||||
wait_for "$OUT1" 'C1I 1 w3'
|
|
||||||
send2 'display-message -p "C2C #{local_active_window} #{window_name}"'
|
|
||||||
wait_for "$OUT2" 'C2C 0 w1'
|
|
||||||
check_server 'aw:' '#{window_name}' 'w1'
|
|
||||||
|
|
||||||
exit 0
|
|
||||||
0
regress/cfg-client-lost.sh
Executable file → Normal file
0
regress/cfg-client-lost.sh
Executable file → Normal file
@@ -290,4 +290,92 @@ wait_count ': G4' 7
|
|||||||
fail "window 1 of zzz not killed"
|
fail "window 1 of zzz not killed"
|
||||||
exit_mode q
|
exit_mode q
|
||||||
|
|
||||||
|
# --- help and information preview --------------------------------------------
|
||||||
|
# The complete tree help is taller than the 24-line test terminal, where the
|
||||||
|
# help renderer deliberately leaves the tree unchanged.
|
||||||
|
$TMUX2 resize-window -t out:0 -x 80 -y 50 || exit 1
|
||||||
|
i=0
|
||||||
|
while [ "$i" -lt 50 ]; do
|
||||||
|
# The inner session has a one-line status, leaving a 49-line window.
|
||||||
|
[ "$($TMUX display-message -p -t aaa:0 '#{window_height}')" -ge 49 ] &&
|
||||||
|
break
|
||||||
|
sleep 0.2
|
||||||
|
i=$((i + 1))
|
||||||
|
done
|
||||||
|
[ "$i" -lt 50 ] || fail "attached client did not resize for tree help"
|
||||||
|
$TMUX choose-tree -t aaa:0 -F 'G5' -O index || exit 1
|
||||||
|
wait_count ': G5' 7
|
||||||
|
$TMUX2 send-keys -t out:0 F1 || fail "send-keys F1 failed"
|
||||||
|
wait_for 'Swap current and previous window'
|
||||||
|
# The first key closes help; the second exits the tree.
|
||||||
|
$TMUX send-keys -t aaa:0 q q || fail "send-keys help exit failed"
|
||||||
|
wait_mode aaa:0 0
|
||||||
|
|
||||||
|
$TMUX choose-tree -t aaa:0 -F 'G6' -O index || exit 1
|
||||||
|
wait_count ': G6' 7
|
||||||
|
$TMUX send-keys -t aaa:0 i || fail "send-keys info failed"
|
||||||
|
wait_for 'Session'
|
||||||
|
$TMUX send-keys -t aaa:0 i || fail "send-keys preview failed"
|
||||||
|
exit_mode q
|
||||||
|
|
||||||
|
# --- window swapping and command prompt for current/tagged items -------------
|
||||||
|
# Restrict a window-only tree to two disposable windows in aaa. J (the
|
||||||
|
# Shift-Down equivalent) swaps their window objects while leaving their indexes
|
||||||
|
# in place.
|
||||||
|
$TMUX new-window -d -t aaa: -n swap-a 'cat' || exit 1
|
||||||
|
$TMUX new-window -d -t aaa: -n swap-b 'cat' || exit 1
|
||||||
|
aidx=$($TMUX list-windows -t aaa -F '#{window_index}:#{window_name}' | \
|
||||||
|
awk -F: '$2 == "swap-a" {print $1}')
|
||||||
|
bidx=$($TMUX list-windows -t aaa -F '#{window_index}:#{window_name}' | \
|
||||||
|
awk -F: '$2 == "swap-b" {print $1}')
|
||||||
|
$TMUX choose-tree -w -t aaa:0 -O index -F 'WT #{window_name}' \
|
||||||
|
-f '#{m:swap-*,#{window_name}}' || exit 1
|
||||||
|
wait_count 'WT swap-' 2
|
||||||
|
$TMUX send-keys -t aaa:0 g j J || fail "window tree swap failed"
|
||||||
|
i=0
|
||||||
|
while [ "$i" -lt 50 ]; do
|
||||||
|
aname=$($TMUX display-message -p -t "aaa:$aidx" '#{window_name}')
|
||||||
|
bname=$($TMUX display-message -p -t "aaa:$bidx" '#{window_name}')
|
||||||
|
[ "$aname:$bname" = 'swap-b:swap-a' ] && break
|
||||||
|
sleep 0.2
|
||||||
|
i=$((i + 1))
|
||||||
|
done
|
||||||
|
[ "$i" -lt 50 ] || fail "window tree did not swap the two windows"
|
||||||
|
|
||||||
|
# ':' runs an entered command for the current item and rebuilds the tree when
|
||||||
|
# its queued command completes.
|
||||||
|
$TMUX set-option -g @tree-command '' || exit 1
|
||||||
|
$TMUX send-keys -t aaa:0 : || fail "window tree command prompt failed"
|
||||||
|
wait_for '(current)'
|
||||||
|
$TMUX send-keys -t aaa:0 -l "set-option -g @tree-command '%%'" || exit 1
|
||||||
|
$TMUX send-keys -t aaa:0 Enter || exit 1
|
||||||
|
i=0
|
||||||
|
while [ "$i" -lt 50 ]; do
|
||||||
|
value=$($TMUX show-option -gqv @tree-command)
|
||||||
|
[ -n "$value" ] && break
|
||||||
|
sleep 0.2
|
||||||
|
i=$((i + 1))
|
||||||
|
done
|
||||||
|
[ "$i" -lt 50 ] || fail "window tree entered command did not run"
|
||||||
|
case "$value" in
|
||||||
|
=aaa:*.) ;;
|
||||||
|
*) fail "window tree command target was '$value'" ;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
# Tag both disposable windows and kill them through the tagged confirmation
|
||||||
|
# callback. The mode-hosting window is outside the filter and survives.
|
||||||
|
$TMUX send-keys -t aaa:0 g j t j t X || fail "tagged kill keys failed"
|
||||||
|
wait_for 'Kill 2 tagged?'
|
||||||
|
$TMUX send-keys -t aaa:0 y || fail "tagged kill confirmation failed"
|
||||||
|
i=0
|
||||||
|
while [ "$i" -lt 50 ]; do
|
||||||
|
left=$($TMUX list-windows -t aaa -F '#{window_name}' | \
|
||||||
|
grep -c '^swap-' || true)
|
||||||
|
[ "$left" -eq 0 ] && break
|
||||||
|
sleep 0.2
|
||||||
|
i=$((i + 1))
|
||||||
|
done
|
||||||
|
[ "$i" -lt 50 ] || fail "tagged windows were not killed"
|
||||||
|
exit_mode q
|
||||||
|
|
||||||
exit 0
|
exit 0
|
||||||
|
|||||||
96
regress/clear-on-attach.sh
Normal file
96
regress/clear-on-attach.sh
Normal file
@@ -0,0 +1,96 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
# With clear-on-attach disabled tmux must preserve the terminal contents by
|
||||||
|
# scrolling them away, rather than entering and clearing the alternate screen.
|
||||||
|
# Capture the bytes written by a real attached client for both settings.
|
||||||
|
|
||||||
|
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_ON="$TEST_TMUX -LtestIon$$ -f/dev/null"
|
||||||
|
OUTER_ON="$TEST_TMUX -LtestOon$$ -f/dev/null"
|
||||||
|
INNER_OFF="$TEST_TMUX -LtestIoff$$ -f/dev/null"
|
||||||
|
OUTER_OFF="$TEST_TMUX -LtestOoff$$ -f/dev/null"
|
||||||
|
DIR=$(mktemp -d) || exit 1
|
||||||
|
|
||||||
|
fail()
|
||||||
|
{
|
||||||
|
echo "$*" >&2
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
cleanup()
|
||||||
|
{
|
||||||
|
$OUTER_ON kill-server 2>/dev/null
|
||||||
|
$INNER_ON kill-server 2>/dev/null
|
||||||
|
$OUTER_OFF kill-server 2>/dev/null
|
||||||
|
$INNER_OFF kill-server 2>/dev/null
|
||||||
|
rm -rf "$DIR"
|
||||||
|
}
|
||||||
|
trap cleanup 0 1 15
|
||||||
|
|
||||||
|
wait_for_client()
|
||||||
|
{
|
||||||
|
i=0
|
||||||
|
while [ "$i" -lt 50 ]; do
|
||||||
|
$current_inner list-clients 2>/dev/null | grep -q . && return 0
|
||||||
|
sleep 0.1
|
||||||
|
i=$((i + 1))
|
||||||
|
done
|
||||||
|
fail "inner client did not attach"
|
||||||
|
}
|
||||||
|
|
||||||
|
capture_attach()
|
||||||
|
{
|
||||||
|
setting=$1
|
||||||
|
output=$2
|
||||||
|
go=$DIR/go-$setting
|
||||||
|
if [ "$setting" = on ]; then
|
||||||
|
current_inner=$INNER_ON
|
||||||
|
current_outer=$OUTER_ON
|
||||||
|
else
|
||||||
|
current_inner=$INNER_OFF
|
||||||
|
current_outer=$OUTER_OFF
|
||||||
|
fi
|
||||||
|
|
||||||
|
$current_inner new-session -d -s inner -x40 -y10 \
|
||||||
|
'exec sleep 100' || exit 1
|
||||||
|
$current_inner set-option -g status off || exit 1
|
||||||
|
$current_inner set-option -g clear-on-attach "$setting" || exit 1
|
||||||
|
$current_outer new-session -d -s outer -x40 -y10 \
|
||||||
|
"while [ ! -e '$go' ]; do sleep 0.01; done; exec $current_inner attach-session -t inner" || exit 1
|
||||||
|
$current_outer set-option -g status off || exit 1
|
||||||
|
$current_outer pipe-pane -O "cat >'$output'" || exit 1
|
||||||
|
touch "$go"
|
||||||
|
wait_for_client
|
||||||
|
sleep 0.5
|
||||||
|
$current_outer pipe-pane -O || exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
on=$DIR/on
|
||||||
|
off=$DIR/off
|
||||||
|
capture_attach on "$on"
|
||||||
|
capture_attach off "$off"
|
||||||
|
|
||||||
|
smcup=$(printf '\033[?1049h')
|
||||||
|
grep -Fq "$smcup" "$on" || fail "clear-on-attach on did not use smcup"
|
||||||
|
if grep -Fq "$smcup" "$off"; then
|
||||||
|
fail "clear-on-attach off used smcup"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Some screen terminfo entries have indn (CSI Ps S), while older ones only
|
||||||
|
# have ind (newline). The preserving path uses indn once or repeats ind once
|
||||||
|
# for every line in the terminal plus one.
|
||||||
|
escape=$(printf '\033')
|
||||||
|
if ! LC_ALL=C grep -Eq "${escape}\\[[0-9]+S" "$off"; then
|
||||||
|
hex=$(od -An -tx1 -v "$off" | tr -d ' \n')
|
||||||
|
case "$hex" in
|
||||||
|
*0a0a0a0a0a0a0a0a0a0a0a*) ;;
|
||||||
|
*) fail "clear-on-attach off did not scroll the old contents away" ;;
|
||||||
|
esac
|
||||||
|
fi
|
||||||
|
|
||||||
|
exit 0
|
||||||
82
regress/client-key-order-focus.sh
Normal file
82
regress/client-key-order-focus.sh
Normal file
@@ -0,0 +1,82 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
# send-keys -K injects a key into a client. Commands produced by that key must
|
||||||
|
# be inserted immediately after send-keys, before later commands in the same
|
||||||
|
# command list. Focus events arriving between a prefix and its following key
|
||||||
|
# must be forwarded without cancelling the prefix table.
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
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_for_option()
|
||||||
|
{
|
||||||
|
option=$1
|
||||||
|
expected=$2
|
||||||
|
i=0
|
||||||
|
while [ "$i" -lt 50 ]; do
|
||||||
|
actual=$($INNER show-option -gv "$option" 2>/dev/null)
|
||||||
|
[ "$actual" = "$expected" ] && return 0
|
||||||
|
sleep 0.1
|
||||||
|
i=$((i + 1))
|
||||||
|
done
|
||||||
|
fail "$option is '$actual', expected '$expected'"
|
||||||
|
}
|
||||||
|
|
||||||
|
cleanup
|
||||||
|
$INNER new-session -d -s inner -x40 -y10 'exec sleep 100' || exit 1
|
||||||
|
$INNER set-option -g status off || exit 1
|
||||||
|
$INNER set-option -g focus-events on || exit 1
|
||||||
|
$OUTER new-session -d -s outer -x40 -y10 "$INNER attach -t inner" || exit 1
|
||||||
|
$OUTER set-option -g status off || exit 1
|
||||||
|
wait_for_client
|
||||||
|
|
||||||
|
# The binding generated by send-keys -K must run before the command which
|
||||||
|
# follows send-keys in this command list. Before the queue-ordering fix this
|
||||||
|
# produces AB rather than BA.
|
||||||
|
$INNER set-option -g @order '' || exit 1
|
||||||
|
$INNER bind-key -n a set-option -gF @order '#{@order}B' || exit 1
|
||||||
|
$INNER send-keys -K -c "$client" a \; \
|
||||||
|
set-option -gF @order '#{@order}A' || exit 1
|
||||||
|
wait_for_option @order BA
|
||||||
|
|
||||||
|
# Put the real attached client in the prefix table, deliver FocusIn, then the
|
||||||
|
# bound key. FocusIn is not bindable and must not reset the prefix table.
|
||||||
|
$INNER bind-key x set-option -g @focus-prefix yes || exit 1
|
||||||
|
$OUTER send-keys C-b || exit 1
|
||||||
|
focus_in=$(printf '\033[I')
|
||||||
|
$OUTER send-keys -l "$focus_in" || exit 1
|
||||||
|
$OUTER send-keys x || exit 1
|
||||||
|
wait_for_option @focus-prefix yes
|
||||||
|
|
||||||
|
exit 0
|
||||||
101
regress/clock-mode.sh
Normal file
101
regress/clock-mode.sh
Normal file
@@ -0,0 +1,101 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
# Exercise clock-mode initialization, all four styles, its one-second timer,
|
||||||
|
# resize into the compact renderer, arbitrary-key exit, and cleanup.
|
||||||
|
|
||||||
|
PATH=/bin:/usr/bin
|
||||||
|
TERM=screen
|
||||||
|
LC_ALL=C.UTF-8
|
||||||
|
export PATH TERM LC_ALL
|
||||||
|
|
||||||
|
[ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux)
|
||||||
|
DIR=$(mktemp -d) || exit 1
|
||||||
|
TMUX_TMPDIR=$DIR
|
||||||
|
export TMUX_TMPDIR
|
||||||
|
INNER="$TEST_TMUX -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
|
||||||
|
rm -rf "$DIR"
|
||||||
|
}
|
||||||
|
trap cleanup 0 1 15
|
||||||
|
|
||||||
|
capture()
|
||||||
|
{
|
||||||
|
$OUTER capture-pane -p -t outer:0.0 2>/dev/null
|
||||||
|
}
|
||||||
|
|
||||||
|
wait_mode()
|
||||||
|
{
|
||||||
|
want=$1
|
||||||
|
i=0
|
||||||
|
while [ "$i" -lt 50 ]; do
|
||||||
|
got=$($INNER display-message -p -t clock:0 '#{pane_mode}' 2>/dev/null)
|
||||||
|
[ "$got" = "$want" ] && return 0
|
||||||
|
sleep 0.1
|
||||||
|
i=$((i + 1))
|
||||||
|
done
|
||||||
|
fail "pane mode is '$got', expected '$want'"
|
||||||
|
}
|
||||||
|
|
||||||
|
wait_hashes()
|
||||||
|
{
|
||||||
|
i=0
|
||||||
|
while [ "$i" -lt 50 ]; do
|
||||||
|
captured=$(capture)
|
||||||
|
printf '%s\n' "$captured" | grep -q '#' && return 0
|
||||||
|
sleep 0.1
|
||||||
|
i=$((i + 1))
|
||||||
|
done
|
||||||
|
fail "large clock did not render"
|
||||||
|
}
|
||||||
|
|
||||||
|
$INNER new-session -d -s clock -x80 -y24 '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 -w clock-mode-colour red || exit 1
|
||||||
|
$OUTER new-session -d -s outer -x80 -y24 "$INNER attach -t clock" || exit 1
|
||||||
|
$OUTER set-option -g status off || exit 1
|
||||||
|
$OUTER set-option -g window-size manual || exit 1
|
||||||
|
sleep 1
|
||||||
|
|
||||||
|
for style in 12 24 12-with-seconds 24-with-seconds; do
|
||||||
|
$INNER set-option -w -t clock:0 clock-mode-style "$style" || exit 1
|
||||||
|
$INNER clock-mode -t clock:0 || exit 1
|
||||||
|
wait_mode clock-mode
|
||||||
|
wait_hashes
|
||||||
|
# Let at least one timer callback observe a new second.
|
||||||
|
[ "$style" != 12-with-seconds ] || sleep 1.2
|
||||||
|
$INNER send-keys -t clock:0 x || exit 1
|
||||||
|
wait_mode ''
|
||||||
|
done
|
||||||
|
|
||||||
|
# Resizing an active mode below the large-glyph threshold selects the compact
|
||||||
|
# renderer. A visible time contains a colon and no block-clock hash.
|
||||||
|
$INNER clock-mode -t clock:0 || exit 1
|
||||||
|
wait_mode clock-mode
|
||||||
|
$INNER resize-window -t clock:0 -x20 -y5 || exit 1
|
||||||
|
i=0
|
||||||
|
while [ "$i" -lt 50 ]; do
|
||||||
|
captured=$(capture)
|
||||||
|
printf '%s\n' "$captured" | grep -Eq '[0-9][0-9]?:[0-9][0-9]' && break
|
||||||
|
sleep 0.1
|
||||||
|
i=$((i + 1))
|
||||||
|
done
|
||||||
|
[ "$i" -lt 50 ] || fail "compact clock did not render after resize"
|
||||||
|
printf '%s\n' "$captured" | grep -q '#' &&
|
||||||
|
fail "compact clock still used large glyphs"
|
||||||
|
$INNER send-keys -t clock:0 Enter || exit 1
|
||||||
|
wait_mode ''
|
||||||
|
|
||||||
|
$INNER has-session -t clock || fail "server died during clock-mode tests"
|
||||||
|
exit 0
|
||||||
81
regress/collapse-output.sh
Normal file
81
regress/collapse-output.sh
Normal file
@@ -0,0 +1,81 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
PATH=/bin:/usr/bin
|
||||||
|
TERM=screen
|
||||||
|
|
||||||
|
[ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux)
|
||||||
|
TMUX="$TEST_TMUX -LtestA$$ -f/dev/null"
|
||||||
|
|
||||||
|
$TMUX kill-server 2>/dev/null
|
||||||
|
$TMUX new-session -d -x80 -y20 "sh -c 'printf \"\\033]133;A\\007p\\$ \\033]133;B\\007echo hi\\n\\033]133;C\\007hello\\n\\033]133;D;0\\007separator\\n\\033]133;A\\007p\\$ \\033]133;B\\007\"; exec sleep 100'" || exit 1
|
||||||
|
sleep 1
|
||||||
|
|
||||||
|
$TMUX copy-mode -c || exit 1
|
||||||
|
$TMUX send-keys -X search-backward hello || exit 1
|
||||||
|
hidden=$($TMUX display-message -p '#{copy_cursor_line}')
|
||||||
|
[ "$hidden" != hello ] || exit 1
|
||||||
|
$TMUX send-keys -X search-backward separator || exit 1
|
||||||
|
hidden=$($TMUX display-message -p '#{copy_cursor_line}')
|
||||||
|
[ "$hidden" != separator ] || exit 1
|
||||||
|
|
||||||
|
$TMUX send-keys BTab || exit 1
|
||||||
|
$TMUX send-keys -X search-backward hello || exit 1
|
||||||
|
shown=$($TMUX display-message -p '#{copy_cursor_line}')
|
||||||
|
[ "$shown" = hello ] || exit 1
|
||||||
|
$TMUX send-keys -X select-line || exit 1
|
||||||
|
$TMUX send-keys -X copy-selection || exit 1
|
||||||
|
selected=$($TMUX show-buffer)
|
||||||
|
[ "$selected" = hello ] || exit 1
|
||||||
|
$TMUX send-keys -X cancel || exit 1
|
||||||
|
|
||||||
|
$TMUX kill-server
|
||||||
|
$TMUX new-session -d -x80 -y20 "sh -c 'printf \"first\\n\\033]133;D;0\\007separator\\n\\033]133;A\\007p\\$ \\033]133;B\\007\"; exec sleep 100'" || exit 1
|
||||||
|
sleep 1
|
||||||
|
|
||||||
|
$TMUX copy-mode -c || exit 1
|
||||||
|
$TMUX send-keys -X search-backward separator || exit 1
|
||||||
|
hidden=$($TMUX display-message -p '#{copy_cursor_line}')
|
||||||
|
[ "$hidden" != separator ] || exit 1
|
||||||
|
$TMUX send-keys BTab || exit 1
|
||||||
|
$TMUX send-keys -X search-backward separator || exit 1
|
||||||
|
shown=$($TMUX display-message -p '#{copy_cursor_line}')
|
||||||
|
[ "$shown" = separator ] || exit 1
|
||||||
|
$TMUX send-keys -X cancel || exit 1
|
||||||
|
|
||||||
|
$TMUX kill-server
|
||||||
|
$TMUX new-session -d -x80 -y20 "sh -c 'printf \"\\033]133;A\\007P> \\033]133;B\\007for i in 1 2 3; do\\n\\033]133;A;k=s\\007> \\033]133;B\\007echo \\$i\\n\\033]133;P;k=s\\007> \\033]133;B\\007done\\033]133;C\\007\\nRESULT\\n\\033]133;D;0\\007separator\\n\\033]133;A\\007P> \\033]133;B\\007\"; exec sleep 100'" || exit 1
|
||||||
|
sleep 1
|
||||||
|
|
||||||
|
$TMUX copy-mode -c || exit 1
|
||||||
|
$TMUX send-keys -X search-backward RESULT || exit 1
|
||||||
|
hidden=$($TMUX display-message -p '#{copy_cursor_line}')
|
||||||
|
[ "$hidden" != RESULT ] || exit 1
|
||||||
|
$TMUX send-keys -X search-backward 'echo $i' || exit 1
|
||||||
|
shown=$($TMUX display-message -p '#{copy_cursor_line}')
|
||||||
|
[ "$shown" = 'echo $i' ] || exit 1
|
||||||
|
$TMUX send-keys -X cancel || exit 1
|
||||||
|
|
||||||
|
$TMUX copy-mode -U || exit 1
|
||||||
|
$TMUX send-keys -X search-backward RESULT || exit 1
|
||||||
|
$TMUX send-keys Tab || exit 1
|
||||||
|
cursor=$($TMUX display-message -p '#{copy_cursor_line}')
|
||||||
|
[ "$cursor" = done ] || exit 1
|
||||||
|
$TMUX send-keys -X cancel || exit 1
|
||||||
|
|
||||||
|
$TMUX kill-server
|
||||||
|
$TMUX new-session -d -x80 -y20 "sh -c 'printf \"\\033]133;A\\007P0> \\033]133;B\\007\\r\\n\\033]133;D;0\\007empty-separator\\n\\033]133;A\\007P1> \\033]133;B\\007\"; exec sleep 100'" || exit 1
|
||||||
|
sleep 1
|
||||||
|
|
||||||
|
$TMUX copy-mode -c || exit 1
|
||||||
|
collapsed=$($TMUX capture-pane -p)
|
||||||
|
case "$collapsed" in
|
||||||
|
*"+ P0>"*) ;;
|
||||||
|
*) exit 1 ;;
|
||||||
|
esac
|
||||||
|
$TMUX send-keys -X search-backward empty-separator || exit 1
|
||||||
|
hidden=$($TMUX display-message -p '#{copy_cursor_line}')
|
||||||
|
[ "$hidden" != empty-separator ] || exit 1
|
||||||
|
$TMUX send-keys -X cancel || exit 1
|
||||||
|
|
||||||
|
$TMUX kill-server 2>/dev/null
|
||||||
|
exit 0
|
||||||
150
regress/confirm-before-lock.sh
Normal file
150
regress/confirm-before-lock.sh
Normal file
@@ -0,0 +1,150 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
# Exercise confirm-before's waiting and background callbacks, custom keys and
|
||||||
|
# prompts, default-yes handling and errors. Also cover all three lock command
|
||||||
|
# entry points with a harmless lock-command which leaves a marker.
|
||||||
|
|
||||||
|
PATH=/bin:/usr/bin
|
||||||
|
TERM=screen
|
||||||
|
LC_ALL=C.UTF-8
|
||||||
|
export PATH TERM LC_ALL
|
||||||
|
|
||||||
|
[ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux)
|
||||||
|
DIR=$(mktemp -d) || exit 1
|
||||||
|
TMUX_TMPDIR=$DIR
|
||||||
|
export TMUX_TMPDIR
|
||||||
|
INNER="$TEST_TMUX -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
|
||||||
|
rm -rf "$DIR"
|
||||||
|
}
|
||||||
|
trap cleanup 0 1 15
|
||||||
|
|
||||||
|
capture()
|
||||||
|
{
|
||||||
|
$OUTER capture-pane -p -t outer:0.0 2>/dev/null
|
||||||
|
}
|
||||||
|
|
||||||
|
wait_capture()
|
||||||
|
{
|
||||||
|
marker=$1
|
||||||
|
i=0
|
||||||
|
while [ "$i" -lt 50 ]; do
|
||||||
|
captured=$(capture)
|
||||||
|
printf '%s\n' "$captured" | grep -Fq "$marker" && return 0
|
||||||
|
sleep 0.1
|
||||||
|
i=$((i + 1))
|
||||||
|
done
|
||||||
|
fail "timed out waiting for '$marker'"
|
||||||
|
}
|
||||||
|
|
||||||
|
wait_option()
|
||||||
|
{
|
||||||
|
want=$1
|
||||||
|
i=0
|
||||||
|
while [ "$i" -lt 50 ]; do
|
||||||
|
got=$($INNER show-option -gqv @confirmed 2>/dev/null)
|
||||||
|
[ "$got" = "$want" ] && return 0
|
||||||
|
sleep 0.1
|
||||||
|
i=$((i + 1))
|
||||||
|
done
|
||||||
|
fail "@confirmed is '$got', expected '$want'"
|
||||||
|
}
|
||||||
|
|
||||||
|
wait_file()
|
||||||
|
{
|
||||||
|
marker=$1
|
||||||
|
i=0
|
||||||
|
while [ "$i" -lt 50 ]; do
|
||||||
|
[ -f "$marker" ] && return 0
|
||||||
|
sleep 0.1
|
||||||
|
i=$((i + 1))
|
||||||
|
done
|
||||||
|
fail "lock command did not create $marker"
|
||||||
|
}
|
||||||
|
|
||||||
|
$INNER new-session -d -s test -x80 -y24 'exec sleep 100' || exit 1
|
||||||
|
$INNER set-option -g status on || exit 1
|
||||||
|
$INNER set-option -g status-position bottom || exit 1
|
||||||
|
$INNER set-option -g window-size manual || exit 1
|
||||||
|
$OUTER new-session -d -s outer -x80 -y24 "$INNER attach -t test" || exit 1
|
||||||
|
$OUTER set-option -g status off || exit 1
|
||||||
|
$OUTER set-option -g window-size manual || exit 1
|
||||||
|
sleep 1
|
||||||
|
|
||||||
|
client=$($INNER list-clients -F '#{client_name}')
|
||||||
|
[ -n "$client" ] || fail "inner client did not attach"
|
||||||
|
|
||||||
|
$INNER bind-key -n M-a confirm-before \
|
||||||
|
'set-option -g @confirmed accepted' || exit 1
|
||||||
|
$INNER bind-key -n M-b confirm-before -b -p 'Background?' \
|
||||||
|
'set-option -g @confirmed background' || exit 1
|
||||||
|
$INNER bind-key -n M-c confirm-before -c x -p 'Custom?' \
|
||||||
|
'set-option -g @confirmed custom' || exit 1
|
||||||
|
$INNER bind-key -n M-y confirm-before -y -p 'Default?' \
|
||||||
|
'set-option -g @confirmed default' || exit 1
|
||||||
|
|
||||||
|
# A non-confirming response closes the waiting prompt without running its
|
||||||
|
# command; reopening and accepting inserts the command after the waiting item.
|
||||||
|
$INNER set-option -g @confirmed sentinel || exit 1
|
||||||
|
$OUTER send-keys M-a || exit 1
|
||||||
|
wait_capture "Confirm 'set-option'? (y/n)"
|
||||||
|
$OUTER send-keys n || exit 1
|
||||||
|
sleep 0.2
|
||||||
|
wait_option sentinel
|
||||||
|
$OUTER send-keys M-a || exit 1
|
||||||
|
wait_capture "Confirm 'set-option'? (y/n)"
|
||||||
|
$OUTER send-keys y || exit 1
|
||||||
|
wait_option accepted
|
||||||
|
|
||||||
|
# -b has no waiting queue item and appends its command to the client queue.
|
||||||
|
$OUTER send-keys M-b || exit 1
|
||||||
|
wait_capture 'Background?'
|
||||||
|
$OUTER send-keys y || exit 1
|
||||||
|
wait_option background
|
||||||
|
|
||||||
|
# A custom printable key is accepted, and -y makes Enter affirmative.
|
||||||
|
$OUTER send-keys M-c || exit 1
|
||||||
|
wait_capture 'Custom?'
|
||||||
|
$OUTER send-keys x || exit 1
|
||||||
|
wait_option custom
|
||||||
|
$OUTER send-keys M-y || exit 1
|
||||||
|
wait_capture 'Default?'
|
||||||
|
$OUTER send-keys Enter || exit 1
|
||||||
|
wait_option default
|
||||||
|
|
||||||
|
# Reject invalid multi-character and control confirmation keys.
|
||||||
|
if $INNER confirm-before -b -t "$client" -c xx 'display-message x' \
|
||||||
|
>/dev/null 2>&1; then
|
||||||
|
fail "multi-character confirm key was accepted"
|
||||||
|
fi
|
||||||
|
if $INNER confirm-before -b -t "$client" -c "$(printf '\001')" \
|
||||||
|
'display-message x' >/dev/null 2>&1; then
|
||||||
|
fail "control confirm key was accepted"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# lock-client, lock-session and lock-server all send lock-command to the real
|
||||||
|
# attached client. The command returns immediately and unlocks the client.
|
||||||
|
for kind in client session server; do
|
||||||
|
marker="$DIR/locked-$kind"
|
||||||
|
$INNER set-option -t test lock-command "printf locked >$marker" || exit 1
|
||||||
|
case "$kind" in
|
||||||
|
client) $INNER lock-client -t "$client" || exit 1 ;;
|
||||||
|
session) $INNER lock-session -t test || exit 1 ;;
|
||||||
|
server) $INNER lock-server || exit 1 ;;
|
||||||
|
esac
|
||||||
|
wait_file "$marker"
|
||||||
|
done
|
||||||
|
|
||||||
|
$INNER has-session -t test || fail "server died during prompt or lock tests"
|
||||||
|
exit 0
|
||||||
0
regress/control-client-exit-stalled.sh
Executable file → Normal file
0
regress/control-client-exit-stalled.sh
Executable file → Normal file
63
regress/control-client-popup.sh
Normal file
63
regress/control-client-popup.sh
Normal file
@@ -0,0 +1,63 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
# Popups require a tty overlay and cannot be displayed by a control client.
|
||||||
|
# A popup command from control mode must be ignored cleanly, leaving the
|
||||||
|
# client command queue and server usable.
|
||||||
|
|
||||||
|
PATH=/bin:/usr/bin
|
||||||
|
TERM=screen
|
||||||
|
export PATH TERM
|
||||||
|
|
||||||
|
[ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux)
|
||||||
|
TMUX="$TEST_TMUX -LtestA$$ -f/dev/null"
|
||||||
|
DIR=$(mktemp -d) || exit 1
|
||||||
|
FIFO=$DIR/input
|
||||||
|
OUT=$DIR/output
|
||||||
|
RAN=$DIR/popup-ran
|
||||||
|
|
||||||
|
fail()
|
||||||
|
{
|
||||||
|
echo "$*" >&2
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
cleanup()
|
||||||
|
{
|
||||||
|
exec 3>&-
|
||||||
|
$TMUX kill-server 2>/dev/null
|
||||||
|
rm -rf "$DIR"
|
||||||
|
}
|
||||||
|
trap cleanup 0 1 15
|
||||||
|
|
||||||
|
mkfifo "$FIFO" || exit 1
|
||||||
|
$TMUX new-session -d -s control -x40 -y10 'exec sleep 100' || exit 1
|
||||||
|
$TMUX -C attach-session -t control <"$FIFO" >"$OUT" 2>&1 &
|
||||||
|
control_pid=$!
|
||||||
|
exec 3>"$FIFO"
|
||||||
|
|
||||||
|
i=0
|
||||||
|
while [ "$i" -lt 50 ]; do
|
||||||
|
$TMUX list-clients -F '#{client_flags}' 2>/dev/null |
|
||||||
|
grep -q 'control-mode' && break
|
||||||
|
sleep 0.1
|
||||||
|
i=$((i + 1))
|
||||||
|
done
|
||||||
|
[ "$i" -lt 50 ] || fail "control client did not attach"
|
||||||
|
|
||||||
|
printf '%s\n' "display-popup -E 'touch $RAN'" >&3
|
||||||
|
printf '%s\n' "display-message -p CONTROL-POPUP-ALIVE" >&3
|
||||||
|
|
||||||
|
i=0
|
||||||
|
while [ "$i" -lt 50 ]; do
|
||||||
|
grep -q 'CONTROL-POPUP-ALIVE' "$OUT" 2>/dev/null && break
|
||||||
|
if ! kill -0 "$control_pid" 2>/dev/null; then
|
||||||
|
fail "control client exited after display-popup"
|
||||||
|
fi
|
||||||
|
sleep 0.1
|
||||||
|
i=$((i + 1))
|
||||||
|
done
|
||||||
|
[ "$i" -lt 50 ] || fail "command after control-client popup did not run"
|
||||||
|
$TMUX has-session -t control || fail "server exited after control-client popup"
|
||||||
|
[ ! -e "$RAN" ] || fail "control client started a popup command"
|
||||||
|
|
||||||
|
exit 0
|
||||||
38
regress/control-client-run-shell-exit.sh
Normal file
38
regress/control-client-run-shell-exit.sh
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
# A waited run-shell command can complete after a control client has closed its
|
||||||
|
# input and exited. The command queue still references the client until the job
|
||||||
|
# callback runs, but control_stop has already destroyed c->control_state. Late
|
||||||
|
# command output must be discarded instead of dereferencing the stopped control
|
||||||
|
# state and killing the server.
|
||||||
|
|
||||||
|
PATH=/bin:/usr/bin
|
||||||
|
TERM=screen
|
||||||
|
|
||||||
|
[ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux)
|
||||||
|
TMUX="$TEST_TMUX -Ltest"
|
||||||
|
$TMUX kill-server 2>/dev/null
|
||||||
|
|
||||||
|
OUT=$(mktemp)
|
||||||
|
trap "$TMUX kill-server 2>/dev/null; rm -f $OUT" 0 1 15
|
||||||
|
|
||||||
|
$TMUX -f/dev/null new-session -d -s main || exit 1
|
||||||
|
|
||||||
|
printf '%s\n' 'run-shell "sleep 0.2; echo TEST"' |
|
||||||
|
$TMUX -f/dev/null -C >"$OUT" 2>&1
|
||||||
|
|
||||||
|
sleep 1
|
||||||
|
|
||||||
|
$TMUX has-session -t main 2>/dev/null || {
|
||||||
|
echo "server exited after late run-shell output"
|
||||||
|
cat "$OUT"
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
grep -q '^%exit' "$OUT" || {
|
||||||
|
echo "control client did not exit"
|
||||||
|
cat "$OUT"
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
exit 0
|
||||||
0
regress/control-notify-guard.sh
Executable file → Normal file
0
regress/control-notify-guard.sh
Executable file → Normal file
253
regress/copy-mode-commands.sh
Normal file
253
regress/copy-mode-commands.sh
Normal file
@@ -0,0 +1,253 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
# Exercise the broad non-mouse copy-mode command surface: navigation, search,
|
||||||
|
# jumps, state toggles, line/end-of-line copies, append, and pipe variants.
|
||||||
|
|
||||||
|
PATH=/bin:/usr/bin
|
||||||
|
TERM=screen
|
||||||
|
LC_ALL=C.UTF-8
|
||||||
|
export PATH TERM LC_ALL
|
||||||
|
|
||||||
|
[ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux)
|
||||||
|
DIR=$(mktemp -d) || exit 1
|
||||||
|
TMUX_TMPDIR=$DIR
|
||||||
|
export TMUX_TMPDIR
|
||||||
|
TMUX="$TEST_TMUX -Ltest$$ -f/dev/null"
|
||||||
|
|
||||||
|
fail()
|
||||||
|
{
|
||||||
|
echo "$*" >&2
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
cleanup()
|
||||||
|
{
|
||||||
|
$TMUX kill-server 2>/dev/null
|
||||||
|
rm -rf "$DIR"
|
||||||
|
}
|
||||||
|
trap cleanup 0 1 15
|
||||||
|
|
||||||
|
x()
|
||||||
|
{
|
||||||
|
$TMUX send-keys -t copy:0 -X "$@" || fail "copy command failed: $*"
|
||||||
|
}
|
||||||
|
|
||||||
|
wait_mode()
|
||||||
|
{
|
||||||
|
want=$1
|
||||||
|
i=0
|
||||||
|
while [ "$i" -lt 50 ]; do
|
||||||
|
got=$($TMUX display-message -p -t copy:0 '#{pane_in_mode}' 2>/dev/null)
|
||||||
|
[ "$got" = "$want" ] && return 0
|
||||||
|
sleep 0.1
|
||||||
|
i=$((i + 1))
|
||||||
|
done
|
||||||
|
fail "pane_in_mode is '$got', expected '$want'"
|
||||||
|
}
|
||||||
|
|
||||||
|
fresh()
|
||||||
|
{
|
||||||
|
$TMUX copy-mode -t copy:0 || exit 1
|
||||||
|
wait_mode 1
|
||||||
|
x history-top
|
||||||
|
x start-of-line
|
||||||
|
}
|
||||||
|
|
||||||
|
select_text()
|
||||||
|
{
|
||||||
|
fresh
|
||||||
|
x begin-selection
|
||||||
|
x cursor-right
|
||||||
|
x cursor-right
|
||||||
|
}
|
||||||
|
|
||||||
|
$TMUX new-session -d -s copy -x40 -y8 \
|
||||||
|
"sh -c 'i=0; while [ \$i -lt 40 ]; do printf \" line-%02d needle (a[b]c) end\\n\" \$i; i=\$((i + 1)); done; printf \"\\nparagraph two needle\\n\\nparagraph three\\n\"; printf \"\\033]133;A\\007prompt-one\\n\\033]133;C\\007output-one\\n\\033]133;A\\007prompt-two\\n\\033]133;C\\007output-two\\n\"; exec sleep 100'" ||
|
||||||
|
exit 1
|
||||||
|
$TMUX set-option -g status off || exit 1
|
||||||
|
$TMUX set-option -g history-limit 200 || exit 1
|
||||||
|
|
||||||
|
i=0
|
||||||
|
while [ "$i" -lt 50 ]; do
|
||||||
|
history=$($TMUX display-message -p -t copy:0 '#{history_size}')
|
||||||
|
[ "$history" -ge 30 ] && break
|
||||||
|
sleep 0.1
|
||||||
|
i=$((i + 1))
|
||||||
|
done
|
||||||
|
[ "$i" -lt 50 ] || fail "history did not fill"
|
||||||
|
|
||||||
|
fresh
|
||||||
|
|
||||||
|
# Copy-mode-specific format callbacks expose the word, line, hyperlink and
|
||||||
|
# active search match at the cursor.
|
||||||
|
formats=$($TMUX display-message -p -t copy:0 \
|
||||||
|
'#{copy_cursor_word}|#{copy_cursor_line}|#{copy_cursor_hyperlink}|#{search_match}') ||
|
||||||
|
exit 1
|
||||||
|
[ -n "$formats" ] || fail "copy-mode cursor formats were empty"
|
||||||
|
|
||||||
|
# Cursor, viewport and paragraph commands. Each is asserted by successful
|
||||||
|
# dispatch and the final mode-liveness check; detailed word/selection geometry
|
||||||
|
# is covered by the existing copy-mode tests.
|
||||||
|
for command in back-to-indentation bottom-line top-line middle-line \
|
||||||
|
cursor-centre-vertical cursor-centre-horizontal end-of-line history-bottom \
|
||||||
|
history-top halfpage-down halfpage-up page-down page-up next-paragraph \
|
||||||
|
previous-paragraph next-matching-bracket previous-matching-bracket; do
|
||||||
|
x "$command"
|
||||||
|
done
|
||||||
|
x goto-line 15
|
||||||
|
x set-mark
|
||||||
|
x cursor-down
|
||||||
|
x jump-to-mark
|
||||||
|
|
||||||
|
# OSC 133 prompt and output markers support shell-integration navigation.
|
||||||
|
x history-top
|
||||||
|
x next-prompt
|
||||||
|
case "$($TMUX display-message -p -t copy:0 '#{copy_cursor_line}')" in
|
||||||
|
*prompt-one*) ;;
|
||||||
|
*) fail "next-prompt did not find prompt-one" ;;
|
||||||
|
esac
|
||||||
|
x next-prompt
|
||||||
|
x previous-prompt
|
||||||
|
case "$($TMUX display-message -p -t copy:0 '#{copy_cursor_line}')" in
|
||||||
|
*prompt-one*) ;;
|
||||||
|
*) fail "previous-prompt did not return to prompt-one" ;;
|
||||||
|
esac
|
||||||
|
x next-prompt -o
|
||||||
|
case "$($TMUX display-message -p -t copy:0 '#{copy_cursor_line}')" in
|
||||||
|
*output-one*) ;;
|
||||||
|
*) fail "next-prompt -o did not find output-one" ;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
# Character jumps remember their direction and character for repeat/reverse.
|
||||||
|
x jump-forward e
|
||||||
|
x jump-again
|
||||||
|
x jump-reverse
|
||||||
|
x jump-backward e
|
||||||
|
x jump-to-forward e
|
||||||
|
x jump-to-backward e
|
||||||
|
|
||||||
|
# Direct, text and incremental searches cover both directions and the saved
|
||||||
|
# search used by again/reverse.
|
||||||
|
x search-forward needle
|
||||||
|
x search-again
|
||||||
|
x search-reverse
|
||||||
|
x search-backward needle
|
||||||
|
x search-forward-text needle
|
||||||
|
x search-backward-text needle
|
||||||
|
x search-forward-incremental '=needle'
|
||||||
|
x search-forward-incremental '+needle'
|
||||||
|
x search-backward-incremental '=needle'
|
||||||
|
x search-backward-incremental '-needle'
|
||||||
|
|
||||||
|
# Stateful display and behaviour toggles.
|
||||||
|
for command in rectangle-on rectangle-off rectangle-toggle \
|
||||||
|
line-numbers-on line-numbers-off line-numbers-toggle \
|
||||||
|
refresh-on refresh-now refresh-off refresh-toggle \
|
||||||
|
scroll-exit-on scroll-exit-off scroll-exit-toggle toggle-position; do
|
||||||
|
x "$command"
|
||||||
|
done
|
||||||
|
wait_mode 1
|
||||||
|
x cancel
|
||||||
|
wait_mode 0
|
||||||
|
|
||||||
|
# Line and end-of-line copy commands work without an explicit selection.
|
||||||
|
fresh
|
||||||
|
x select-line
|
||||||
|
x copy-selection-and-cancel
|
||||||
|
case "$($TMUX show-buffer)" in
|
||||||
|
*line-00*) ;;
|
||||||
|
*) fail "select-line did not select the current line" ;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
fresh
|
||||||
|
x copy-line
|
||||||
|
[ -n "$($TMUX show-buffer)" ] || fail "copy-line produced an empty buffer"
|
||||||
|
x cancel
|
||||||
|
|
||||||
|
fresh
|
||||||
|
x copy-end-of-line
|
||||||
|
[ -n "$($TMUX show-buffer)" ] || fail "copy-end-of-line produced an empty buffer"
|
||||||
|
x cancel
|
||||||
|
|
||||||
|
# Append variants retain or close the mode as advertised.
|
||||||
|
$TMUX set-buffer -b append-buffer prefix || exit 1
|
||||||
|
select_text
|
||||||
|
x append-selection
|
||||||
|
appended=$($TMUX show-buffer -b append-buffer)
|
||||||
|
case "$appended" in
|
||||||
|
prefix*) ;;
|
||||||
|
*) fail "append-selection produced '$appended'" ;;
|
||||||
|
esac
|
||||||
|
x cancel
|
||||||
|
|
||||||
|
$TMUX set-buffer -b append-cancel-buffer prefix || exit 1
|
||||||
|
select_text
|
||||||
|
x append-selection-and-cancel
|
||||||
|
wait_mode 0
|
||||||
|
appended=$($TMUX show-buffer -b append-cancel-buffer)
|
||||||
|
case "$appended" in
|
||||||
|
prefix*) ;;
|
||||||
|
*) fail "append-selection-and-cancel produced '$appended'" ;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
# The explicit cancel variants close copy mode.
|
||||||
|
fresh
|
||||||
|
x copy-line-and-cancel
|
||||||
|
wait_mode 0
|
||||||
|
fresh
|
||||||
|
x copy-end-of-line-and-cancel
|
||||||
|
wait_mode 0
|
||||||
|
select_text
|
||||||
|
x copy-selection-and-cancel
|
||||||
|
wait_mode 0
|
||||||
|
|
||||||
|
# Pipe wrappers have distinct command handlers. /dev/null keeps the test
|
||||||
|
# deterministic while exercising asynchronous pipe job creation and cleanup.
|
||||||
|
fresh
|
||||||
|
x copy-pipe-line 'cat >/dev/null'
|
||||||
|
x cancel
|
||||||
|
fresh
|
||||||
|
x copy-pipe-line-and-cancel 'cat >/dev/null'
|
||||||
|
wait_mode 0
|
||||||
|
fresh
|
||||||
|
x copy-pipe-end-of-line 'cat >/dev/null'
|
||||||
|
x cancel
|
||||||
|
fresh
|
||||||
|
x copy-pipe-end-of-line-and-cancel 'cat >/dev/null'
|
||||||
|
wait_mode 0
|
||||||
|
|
||||||
|
select_text
|
||||||
|
x copy-pipe-no-clear 'cat >/dev/null'
|
||||||
|
x pipe-no-clear 'cat >/dev/null'
|
||||||
|
x copy-pipe 'cat >/dev/null'
|
||||||
|
x pipe 'cat >/dev/null'
|
||||||
|
x cancel
|
||||||
|
select_text
|
||||||
|
x copy-pipe-and-cancel 'cat >/dev/null'
|
||||||
|
wait_mode 0
|
||||||
|
select_text
|
||||||
|
x pipe-and-cancel 'cat >/dev/null'
|
||||||
|
wait_mode 0
|
||||||
|
|
||||||
|
# Commands which cancel on downward movement have separate handlers.
|
||||||
|
fresh
|
||||||
|
x history-bottom
|
||||||
|
x bottom-line
|
||||||
|
x cursor-down-and-cancel
|
||||||
|
wait_mode 0
|
||||||
|
fresh
|
||||||
|
x history-bottom
|
||||||
|
x halfpage-down-and-cancel
|
||||||
|
wait_mode 0
|
||||||
|
fresh
|
||||||
|
x history-bottom
|
||||||
|
x page-down-and-cancel
|
||||||
|
wait_mode 0
|
||||||
|
fresh
|
||||||
|
x history-bottom
|
||||||
|
x bottom-line
|
||||||
|
x scroll-down-and-cancel
|
||||||
|
wait_mode 0
|
||||||
|
|
||||||
|
$TMUX has-session -t copy || fail "server died during copy-mode commands"
|
||||||
|
exit 0
|
||||||
102
regress/copy-mode-exit-status.sh
Normal file
102
regress/copy-mode-exit-status.sh
Normal file
@@ -0,0 +1,102 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
# Check OSC 133 exit status indicators in copy mode using a real client.
|
||||||
|
|
||||||
|
PATH=/bin:/usr/bin
|
||||||
|
TERM=screen
|
||||||
|
LC_ALL=C.UTF-8
|
||||||
|
export TERM LC_ALL
|
||||||
|
|
||||||
|
[ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux)
|
||||||
|
TMUX="$TEST_TMUX -LtestA$$ -f/dev/null"
|
||||||
|
TMUX2="$TEST_TMUX -LtestB$$ -f/dev/null"
|
||||||
|
|
||||||
|
fail() {
|
||||||
|
echo "$*" >&2
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
capture() {
|
||||||
|
$TMUX capture-pane -pS0 -E- >$TMP || exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
check_grep() {
|
||||||
|
grep -Fq "$1" $TMP || fail "missing pattern: $1"
|
||||||
|
}
|
||||||
|
|
||||||
|
check_no_grep() {
|
||||||
|
grep -Fq "$1" $TMP && fail "unexpected pattern: $1"
|
||||||
|
}
|
||||||
|
|
||||||
|
$TMUX kill-server 2>/dev/null
|
||||||
|
$TMUX2 kill-server 2>/dev/null
|
||||||
|
|
||||||
|
TMP=$(mktemp)
|
||||||
|
trap "rm -f $TMP; $TMUX kill-server 2>/dev/null; $TMUX2 kill-server 2>/dev/null" 0 1 15
|
||||||
|
|
||||||
|
$TMUX2 new-session -d -x80 -y10 \
|
||||||
|
"printf '\033]133;A\007p\$ \033]133;B\007one\n\033]133;C\007out1\n\033]133;D;0\007\033]133;A\007p\$ \033]133;B\007two\n\033]133;C\007out2\n\033]133;D;123\007\033]133;A\007p\$ \033]133;B\007silent\n\033]133;C\007\033]133;D;7\007\033]133;A\007p\$ \033]133;B\007three\n\033]133;C\007out3\n\033]133;D\007\033]133;A\007p\$ \033]133;B\007'; exec sleep 100" || \
|
||||||
|
exit 1
|
||||||
|
$TMUX2 set -g status off || exit 1
|
||||||
|
$TMUX2 set -g copy-mode-position-format '#[align=left]POS' || exit 1
|
||||||
|
$TMUX2 list-keys -Tcopy-mode O | grep -Fq 'fold-view-toggle' || exit 1
|
||||||
|
$TMUX2 list-keys -Tcopy-mode-vi O | grep -Fq 'fold-view-toggle' || exit 1
|
||||||
|
|
||||||
|
$TMUX new-session -d -x80 -y10 || exit 1
|
||||||
|
$TMUX set -g status off || exit 1
|
||||||
|
$TMUX send -l "$TMUX2 attach" || exit 1
|
||||||
|
$TMUX send Enter || exit 1
|
||||||
|
sleep 1
|
||||||
|
|
||||||
|
$TMUX2 copy-mode -U || exit 1
|
||||||
|
$TMUX2 send -X history-top || exit 1
|
||||||
|
sleep 1
|
||||||
|
capture
|
||||||
|
check_grep "!- p\$ two"
|
||||||
|
check_grep "! p\$ silent"
|
||||||
|
check_no_grep "!+ p\$ silent"
|
||||||
|
check_grep "POS"
|
||||||
|
|
||||||
|
# Rebuilding with collapsed output must not expand copy-mode formats before
|
||||||
|
# the viewport has been restored for the shorter backing grid.
|
||||||
|
$TMUX2 send -X collapse-output -a || exit 1
|
||||||
|
sleep 1
|
||||||
|
capture
|
||||||
|
check_grep "!+ p\$ two"
|
||||||
|
check_grep "! p\$ silent"
|
||||||
|
check_no_grep "!+ p\$ silent"
|
||||||
|
$TMUX2 send -X line-numbers-on || exit 1
|
||||||
|
sleep 1
|
||||||
|
capture
|
||||||
|
check_grep " 3 !+ p\$ two"
|
||||||
|
$TMUX2 send -X fold-view-toggle || exit 1
|
||||||
|
[ "$($TMUX2 display -p '#{copy_fold_view}')" = 0 ] || exit 1
|
||||||
|
sleep 1
|
||||||
|
capture
|
||||||
|
check_grep "out2"
|
||||||
|
check_no_grep "!+ p\$ two"
|
||||||
|
check_grep " 3 p\$ two"
|
||||||
|
$TMUX2 send -X fold-view-toggle || exit 1
|
||||||
|
[ "$($TMUX2 display -p '#{copy_fold_view}')" = 1 ] || exit 1
|
||||||
|
sleep 1
|
||||||
|
capture
|
||||||
|
check_grep " 3 !- p\$ two"
|
||||||
|
|
||||||
|
# A format wider than the standard three-column gutter is not truncated.
|
||||||
|
$TMUX2 send -X cancel || exit 1
|
||||||
|
$TMUX2 set -g copy-mode-exit-status-format \
|
||||||
|
'#[align=right]#{?exit_status,!#{exit_status}, }' || exit 1
|
||||||
|
$TMUX2 copy-mode -c || exit 1
|
||||||
|
$TMUX2 send -X history-top || exit 1
|
||||||
|
sleep 1
|
||||||
|
capture
|
||||||
|
check_grep "!123+ p\$ two"
|
||||||
|
|
||||||
|
$TMUX2 send -X select-line || exit 1
|
||||||
|
$TMUX2 send -X copy-selection || exit 1
|
||||||
|
selection=$($TMUX2 show-buffer)
|
||||||
|
case $selection in
|
||||||
|
*RC=*) fail "exit status was copied" ;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
exit 0
|
||||||
81
regress/copy-mode-selection-mode.sh
Normal file
81
regress/copy-mode-selection-mode.sh
Normal file
@@ -0,0 +1,81 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
# Changing an existing character selection to line mode must reset both ends
|
||||||
|
# to complete lines, including when the cursor subsequently crosses the fixed
|
||||||
|
# end. An invalid regular-expression search must also discard marks left by a
|
||||||
|
# previous successful search.
|
||||||
|
|
||||||
|
PATH=/bin:/usr/bin
|
||||||
|
TERM=screen
|
||||||
|
LC_ALL=C.UTF-8
|
||||||
|
export PATH TERM LC_ALL
|
||||||
|
|
||||||
|
[ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux)
|
||||||
|
TMUX="$TEST_TMUX -LtestA$$ -f/dev/null"
|
||||||
|
|
||||||
|
fail()
|
||||||
|
{
|
||||||
|
echo "$*" >&2
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
cleanup()
|
||||||
|
{
|
||||||
|
$TMUX kill-server 2>/dev/null
|
||||||
|
}
|
||||||
|
trap cleanup 0 1 15
|
||||||
|
|
||||||
|
$TMUX new-session -d -x30 -y6 \
|
||||||
|
"printf 'alpha\nbeta\ngamma\ndelta\n'; exec sleep 100" || exit 1
|
||||||
|
$TMUX set-option -g window-size manual || exit 1
|
||||||
|
sleep 1
|
||||||
|
|
||||||
|
# A valid regular-expression search creates marks. Replacing it with an
|
||||||
|
# invalid expression, growing the mode, and scrolling must not leave an old
|
||||||
|
# mark array indexed with the new geometry (the original bug was an ASan heap
|
||||||
|
# overflow in this sequence).
|
||||||
|
$TMUX copy-mode || exit 1
|
||||||
|
$TMUX send-keys -X history-top || exit 1
|
||||||
|
$TMUX send-keys -X search-forward beta || exit 1
|
||||||
|
[ "$($TMUX display-message -p '#{search_present}')" = 1 ] ||
|
||||||
|
fail "successful search did not create marks"
|
||||||
|
$TMUX send-keys -X search-forward '[' || exit 1
|
||||||
|
$TMUX resize-window -x40 -y8 || exit 1
|
||||||
|
$TMUX send-keys -X scroll-down || exit 1
|
||||||
|
[ "$($TMUX display-message -p '#{search_present}')" = 0 ] ||
|
||||||
|
fail "invalid regular expression left stale marks after resize"
|
||||||
|
$TMUX send-keys -X cancel || exit 1
|
||||||
|
|
||||||
|
# Select part of alpha through part of beta, then change the existing
|
||||||
|
# selection to line mode. Both lines, and no partial columns, must be copied.
|
||||||
|
$TMUX copy-mode || exit 1
|
||||||
|
$TMUX send-keys -X history-top || exit 1
|
||||||
|
$TMUX send-keys -N2 -X cursor-right || exit 1
|
||||||
|
$TMUX send-keys -X begin-selection || exit 1
|
||||||
|
$TMUX send-keys -X cursor-down || exit 1
|
||||||
|
$TMUX send-keys -N2 -X cursor-right || exit 1
|
||||||
|
$TMUX send-keys -X selection-mode line || exit 1
|
||||||
|
[ "$($TMUX display-message -p '#{selection_mode}')" = line ] ||
|
||||||
|
fail "selection did not change to line mode"
|
||||||
|
$TMUX send-keys -X copy-selection || exit 1
|
||||||
|
expected=$(printf 'alpha\nbeta')
|
||||||
|
[ "$($TMUX show-buffer)" = "$expected" ] ||
|
||||||
|
fail "line-mode selection did not expand to complete lines"
|
||||||
|
|
||||||
|
# Start with the cursor above the fixed end so selection-mode itself takes the
|
||||||
|
# reverse-direction path, then extend it through another line.
|
||||||
|
$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 -N2 -X cursor-right || exit 1
|
||||||
|
$TMUX send-keys -X selection-mode line || exit 1
|
||||||
|
$TMUX send-keys -X cursor-up || exit 1
|
||||||
|
$TMUX send-keys -X copy-selection || exit 1
|
||||||
|
expected=$(printf 'alpha\nbeta\ngamma')
|
||||||
|
[ "$($TMUX show-buffer)" = "$expected" ] ||
|
||||||
|
fail "reversed line-mode selection did not keep complete lines"
|
||||||
|
|
||||||
|
exit 0
|
||||||
0
regress/copy-mode-unicode-whitespace.sh
Executable file → Normal file
0
regress/copy-mode-unicode-whitespace.sh
Executable file → Normal file
118
regress/find-window.sh
Normal file
118
regress/find-window.sh
Normal file
@@ -0,0 +1,118 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
# Exercise find-window's content, name and title filters in every combination,
|
||||||
|
# glob/regex and case-insensitive suffixes, plus zoom restoration.
|
||||||
|
|
||||||
|
PATH=/bin:/usr/bin
|
||||||
|
TERM=screen
|
||||||
|
LC_ALL=C.UTF-8
|
||||||
|
export PATH TERM LC_ALL
|
||||||
|
|
||||||
|
[ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux)
|
||||||
|
DIR=$(mktemp -d) || exit 1
|
||||||
|
TMUX_TMPDIR=$DIR
|
||||||
|
export TMUX_TMPDIR
|
||||||
|
INNER="$TEST_TMUX -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
|
||||||
|
rm -rf "$DIR"
|
||||||
|
}
|
||||||
|
trap cleanup 0 1 15
|
||||||
|
|
||||||
|
capture()
|
||||||
|
{
|
||||||
|
$OUTER capture-pane -p -t outer:0.0 2>/dev/null
|
||||||
|
}
|
||||||
|
|
||||||
|
wait_mode()
|
||||||
|
{
|
||||||
|
want=$1
|
||||||
|
i=0
|
||||||
|
while [ "$i" -lt 50 ]; do
|
||||||
|
got=$($INNER display-message -p -t find:base.0 '#{pane_mode}' 2>/dev/null)
|
||||||
|
[ "$got" = "$want" ] && return 0
|
||||||
|
sleep 0.1
|
||||||
|
i=$((i + 1))
|
||||||
|
done
|
||||||
|
fail "pane mode is '$got', expected '$want'"
|
||||||
|
}
|
||||||
|
|
||||||
|
run_find()
|
||||||
|
{
|
||||||
|
label=$1
|
||||||
|
expected=$2
|
||||||
|
shift 2
|
||||||
|
$INNER find-window -t find:base.0 "$@" || fail "$label failed"
|
||||||
|
wait_mode tree-mode
|
||||||
|
i=0
|
||||||
|
while [ "$i" -lt 50 ]; do
|
||||||
|
captured=$(capture)
|
||||||
|
printf '%s\n' "$captured" | grep -Fq "$expected" && break
|
||||||
|
sleep 0.1
|
||||||
|
i=$((i + 1))
|
||||||
|
done
|
||||||
|
[ "$i" -lt 50 ] || fail "$label did not show '$expected'"
|
||||||
|
$INNER send-keys -t find:base.0 q || exit 1
|
||||||
|
wait_mode ''
|
||||||
|
}
|
||||||
|
|
||||||
|
$INNER new-session -d -s find -n base -x80 -y24 \
|
||||||
|
"sh -c 'printf \"BaseBody\\n\"; exec sleep 100'" || exit 1
|
||||||
|
$INNER split-window -d -h -t find:base \
|
||||||
|
"sh -c 'printf \"OtherBody\\n\"; exec sleep 100'" || exit 1
|
||||||
|
$INNER new-window -d -t find: -n NameNeedle 'exec sleep 100' || exit 1
|
||||||
|
$INNER new-window -d -t find: -n body \
|
||||||
|
"sh -c 'printf \"BodyNeedle\\n\"; exec sleep 100'" || exit 1
|
||||||
|
$INNER new-window -d -t find: -n titled 'exec sleep 100' || exit 1
|
||||||
|
$INNER select-pane -t find:titled.0 -T TitleNeedle || 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 -x80 -y24 "$INNER attach -t find:base" || exit 1
|
||||||
|
$OUTER set-option -g status off || exit 1
|
||||||
|
$OUTER set-option -g window-size manual || exit 1
|
||||||
|
sleep 1
|
||||||
|
|
||||||
|
# Wait until the body text is in the pane history before searching it.
|
||||||
|
i=0
|
||||||
|
while [ "$i" -lt 50 ]; do
|
||||||
|
$INNER capture-pane -p -t find:body.0 | grep -Fq BodyNeedle && break
|
||||||
|
sleep 0.1
|
||||||
|
i=$((i + 1))
|
||||||
|
done
|
||||||
|
[ "$i" -lt 50 ] || fail "pane content was not ready"
|
||||||
|
|
||||||
|
# No flags means C+N+T. Then cover all three pairs and all three singletons.
|
||||||
|
run_find default NameNeedle NameNeedle
|
||||||
|
run_find content-name NameNeedle -C -N NameNeedle
|
||||||
|
run_find content-title titled -C -T TitleNeedle
|
||||||
|
run_find name-title titled -N -T TitleNeedle
|
||||||
|
run_find content-only body -C BodyNeedle
|
||||||
|
run_find name-only NameNeedle -N NameNeedle
|
||||||
|
run_find title-only titled -T TitleNeedle
|
||||||
|
|
||||||
|
# Glob case folding and all regular-expression suffix variants.
|
||||||
|
run_find insensitive NameNeedle -N -i nameneedle
|
||||||
|
run_find regex NameNeedle -N -r '^NameNeedle$'
|
||||||
|
run_find regex-insensitive NameNeedle -N -r -i '^nameneedle$'
|
||||||
|
|
||||||
|
# -Z temporarily zooms the target window and restores its prior state on exit.
|
||||||
|
$INNER find-window -Z -t find:base.0 -N NameNeedle || exit 1
|
||||||
|
wait_mode tree-mode
|
||||||
|
[ "$($INNER display-message -p -t find:base '#{window_zoomed_flag}')" = 1 ] ||
|
||||||
|
fail "find-window -Z did not zoom"
|
||||||
|
$INNER send-keys -t find:base.0 q || exit 1
|
||||||
|
wait_mode ''
|
||||||
|
[ "$($INNER display-message -p -t find:base '#{window_zoomed_flag}')" = 0 ] ||
|
||||||
|
fail "find-window -Z did not restore zoom"
|
||||||
|
|
||||||
|
exit 0
|
||||||
@@ -71,7 +71,10 @@ cursor_very_visible
|
|||||||
cursor_x
|
cursor_x
|
||||||
cursor_y
|
cursor_y
|
||||||
history_all_bytes
|
history_all_bytes
|
||||||
|
history_added
|
||||||
history_bytes
|
history_bytes
|
||||||
|
history_collected
|
||||||
|
history_generation
|
||||||
history_limit
|
history_limit
|
||||||
history_size
|
history_size
|
||||||
host
|
host
|
||||||
@@ -103,6 +106,11 @@ pane_at_right
|
|||||||
pane_at_top
|
pane_at_top
|
||||||
pane_bg
|
pane_bg
|
||||||
pane_bottom
|
pane_bottom
|
||||||
|
pane_command_duration
|
||||||
|
pane_command_end_time
|
||||||
|
pane_command_running
|
||||||
|
pane_command_start_time
|
||||||
|
pane_command_status
|
||||||
pane_current_command
|
pane_current_command
|
||||||
pane_current_path
|
pane_current_path
|
||||||
pane_dead
|
pane_dead
|
||||||
@@ -120,16 +128,21 @@ pane_index
|
|||||||
pane_input_off
|
pane_input_off
|
||||||
pane_key_mode
|
pane_key_mode
|
||||||
pane_last
|
pane_last
|
||||||
|
pane_last_output_time
|
||||||
|
pane_last_prompt_time
|
||||||
pane_left
|
pane_left
|
||||||
pane_marked
|
pane_marked
|
||||||
pane_marked_set
|
pane_marked_set
|
||||||
|
pane_modal_flag
|
||||||
pane_mode
|
pane_mode
|
||||||
|
pane_output_generation
|
||||||
pane_path
|
pane_path
|
||||||
pane_pb_progress
|
pane_pb_progress
|
||||||
pane_pb_state
|
pane_pb_state
|
||||||
pane_pid
|
pane_pid
|
||||||
pane_pipe
|
pane_pipe
|
||||||
pane_pipe_pid
|
pane_pipe_pid
|
||||||
|
pane_private_modes
|
||||||
pane_right
|
pane_right
|
||||||
pane_search_string
|
pane_search_string
|
||||||
pane_start_command
|
pane_start_command
|
||||||
@@ -141,6 +154,8 @@ pane_title
|
|||||||
pane_top
|
pane_top
|
||||||
pane_tty
|
pane_tty
|
||||||
pane_unseen_changes
|
pane_unseen_changes
|
||||||
|
pane_unzoomed_height
|
||||||
|
pane_unzoomed_width
|
||||||
pane_width
|
pane_width
|
||||||
pane_x
|
pane_x
|
||||||
pane_y
|
pane_y
|
||||||
@@ -206,7 +221,10 @@ window_layout
|
|||||||
window_linked
|
window_linked
|
||||||
window_linked_sessions
|
window_linked_sessions
|
||||||
window_linked_sessions_list
|
window_linked_sessions_list
|
||||||
|
window_manual_height
|
||||||
|
window_manual_width
|
||||||
window_marked_flag
|
window_marked_flag
|
||||||
|
window_modal_pane
|
||||||
window_name
|
window_name
|
||||||
window_offset_x
|
window_offset_x
|
||||||
window_offset_y
|
window_offset_y
|
||||||
|
|||||||
@@ -218,11 +218,13 @@ $TMUX set-hook -gu pane-mode-changed || fail "unset pane-mode-changed failed"
|
|||||||
|
|
||||||
# pane-exited when a pane's command exits with remain-on-exit off.
|
# pane-exited when a pane's command exits with remain-on-exit off.
|
||||||
$TMUX set -g @x 0 || fail "set @x failed"
|
$TMUX set -g @x 0 || fail "set @x failed"
|
||||||
$TMUX set-hook -g pane-exited 'set -gF @x "#{hook}:#{hook_pane}"' ||
|
$TMUX set-hook -g pane-exited \
|
||||||
|
'set -gF @x "#{hook}:#{hook_pane}:#{hook_exit_status}:#{hook_exit_success}"' ||
|
||||||
fail "set-hook pane-exited failed"
|
fail "set-hook pane-exited failed"
|
||||||
pane=$($TMUX splitw -d -t main:0 -P -F '#{pane_id}' 'true') ||
|
pane=$($TMUX splitw -d -t main:0 -P -F '#{pane_id}' \
|
||||||
fail "split-window true failed"
|
'trap "" HUP; exec </dev/null >/dev/null 2>&1; sleep 1; exit 42') ||
|
||||||
wait_for @x "pane-exited:$pane"
|
fail "split-window exit 42 failed"
|
||||||
|
wait_for @x "pane-exited:$pane:42:0"
|
||||||
$TMUX set-hook -gu pane-exited || fail "unset pane-exited failed"
|
$TMUX set-hook -gu pane-exited || fail "unset pane-exited failed"
|
||||||
|
|
||||||
# pane-died when a pane's command exits with remain-on-exit on.
|
# pane-died when a pane's command exits with remain-on-exit on.
|
||||||
|
|||||||
@@ -5,9 +5,12 @@
|
|||||||
start_pane alternate 10 3 'MAIN\033[?1049hALT\033[?1049lZ\n'
|
start_pane alternate 10 3 'MAIN\033[?1049hALT\033[?1049lZ\n'
|
||||||
check_capture alternate 'MAINZ'
|
check_capture alternate 'MAINZ'
|
||||||
|
|
||||||
start_pane osc133 20 8 'xx\033]133;A\007p>\033]133;B\007cmd\nxy\033]133;P;k=s\007more\nzz\033]133;C\007out\033]133;D;7\007\nq\033]133;C\007bad\033]133;D;-1\007\nqq\033]133;C\007big\033]133;D;300\007\nzzz\033]133;C\007ok\033]133;D\007\n'
|
start_pane osc133 20 12 'xx\033]133;A\007p>\033]133;B\007cmd\nxy\033]133;P;k=s\007more\nxz\033]133;A;k=s\007more\nxw\033]133;P;k=c\007more\nxv\033]133;A;k=c\007more\nzz\033]133;C\007out\033]133;D;7\007\nq\033]133;C\007bad\033]133;D;-1\007\nqq\033]133;C\007big\033]133;D;300\007\nzzz\033]133;C\007ok\033]133;D\007\n'
|
||||||
check_capture osc133 'xxp>cmd
|
check_capture osc133 'xxp>cmd
|
||||||
xymore
|
xymore
|
||||||
|
xzmore
|
||||||
|
xwmore
|
||||||
|
xvmore
|
||||||
zzout
|
zzout
|
||||||
qbad
|
qbad
|
||||||
qqbig
|
qqbig
|
||||||
@@ -15,10 +18,13 @@ zzzok'
|
|||||||
check_raw_matches osc133 \
|
check_raw_matches osc133 \
|
||||||
'L 0 \(0\) flags=START_PROMPT,START_COMMAND\[[0-9a-f]+\].* osc133=2,4,0,0,0' \
|
'L 0 \(0\) flags=START_PROMPT,START_COMMAND\[[0-9a-f]+\].* osc133=2,4,0,0,0' \
|
||||||
'L 1 \(1\) flags=SECOND_PROMPT\[[0-9a-f]+\].* osc133=2,0,0,0,0' \
|
'L 1 \(1\) flags=SECOND_PROMPT\[[0-9a-f]+\].* osc133=2,0,0,0,0' \
|
||||||
'L 2 \(2\) flags=START_OUTPUT,END_OUTPUT\[[0-9a-f]+\].* osc133=0,0,2,5,7' \
|
'L 2 \(2\) flags=SECOND_PROMPT\[[0-9a-f]+\].* osc133=2,0,0,0,0' \
|
||||||
'L 3 \(3\) flags=START_OUTPUT,END_OUTPUT\[[0-9a-f]+\].* osc133=0,0,1,4,255' \
|
'L 3 \(3\) flags=SECOND_PROMPT\[[0-9a-f]+\].* osc133=2,0,0,0,0' \
|
||||||
'L 4 \(4\) flags=START_OUTPUT,END_OUTPUT\[[0-9a-f]+\].* osc133=0,0,2,5,255' \
|
'L 4 \(4\) flags=SECOND_PROMPT\[[0-9a-f]+\].* osc133=2,0,0,0,0' \
|
||||||
'L 5 \(5\) flags=START_OUTPUT,END_OUTPUT\[[0-9a-f]+\].* osc133=0,0,3,5,0'
|
'L 5 \(5\) flags=START_OUTPUT,END_OUTPUT,END_OUTPUT_STATUS\[[0-9a-f]+\].* osc133=0,0,2,5,7' \
|
||||||
|
'L 6 \(6\) flags=START_OUTPUT,END_OUTPUT,END_OUTPUT_STATUS\[[0-9a-f]+\].* osc133=0,0,1,4,255' \
|
||||||
|
'L 7 \(7\) flags=START_OUTPUT,END_OUTPUT,END_OUTPUT_STATUS\[[0-9a-f]+\].* osc133=0,0,2,5,255' \
|
||||||
|
'L 8 \(8\) flags=START_OUTPUT,END_OUTPUT\[[0-9a-f]+\].* osc133=0,0,3,5,0'
|
||||||
|
|
||||||
$TMUX kill-server 2>/dev/null
|
$TMUX kill-server 2>/dev/null
|
||||||
exit $exit_status
|
exit $exit_status
|
||||||
|
|||||||
126
regress/layout-set.sh
Normal file
126
regress/layout-set.sh
Normal file
@@ -0,0 +1,126 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
# Exercise every predefined layout, next/previous cycling, lookup and restore
|
||||||
|
# paths, one-pane early returns, minimum-size handling and floating panes.
|
||||||
|
|
||||||
|
PATH=/bin:/usr/bin
|
||||||
|
TERM=screen
|
||||||
|
export PATH TERM
|
||||||
|
|
||||||
|
[ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux)
|
||||||
|
DIR=$(mktemp -d) || exit 1
|
||||||
|
TMUX_TMPDIR=$DIR
|
||||||
|
export TMUX_TMPDIR
|
||||||
|
TMUX="$TEST_TMUX -Ltest$$ -f/dev/null"
|
||||||
|
SEEN="$DIR/layouts"
|
||||||
|
|
||||||
|
fail()
|
||||||
|
{
|
||||||
|
echo "$*" >&2
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
cleanup()
|
||||||
|
{
|
||||||
|
$TMUX kill-server 2>/dev/null
|
||||||
|
rm -rf "$DIR"
|
||||||
|
}
|
||||||
|
trap cleanup 0 1 15
|
||||||
|
|
||||||
|
layout()
|
||||||
|
{
|
||||||
|
$TMUX display-message -p -t layouts:many '#{window_layout}'
|
||||||
|
}
|
||||||
|
|
||||||
|
check_layout()
|
||||||
|
{
|
||||||
|
name=$1
|
||||||
|
$TMUX select-layout -t layouts:many "$name" ||
|
||||||
|
fail "select-layout $name failed"
|
||||||
|
value=$(layout)
|
||||||
|
[ -n "$value" ] || fail "$name produced an empty layout"
|
||||||
|
if grep -Fxq "$value" "$SEEN"; then
|
||||||
|
fail "$name produced the same layout as an earlier preset"
|
||||||
|
fi
|
||||||
|
printf '%s\n' "$value" >>"$SEEN"
|
||||||
|
[ "$($TMUX list-panes -t layouts:many -F x | wc -l)" -eq 5 ] ||
|
||||||
|
fail "$name lost a pane"
|
||||||
|
[ "$($TMUX list-panes -t layouts:many -F '#{pane_floating_flag}' | \
|
||||||
|
grep -c '^1$')" -eq 1 ] || fail "$name did not preserve the floating pane"
|
||||||
|
}
|
||||||
|
|
||||||
|
$TMUX new-session -d -s layouts -n many -x100 -y40 'exec sleep 100' || exit 1
|
||||||
|
$TMUX set-option -g window-size manual || exit 1
|
||||||
|
$TMUX split-window -d -h -t layouts:many 'exec sleep 100' || exit 1
|
||||||
|
$TMUX split-window -d -v -t layouts:many 'exec sleep 100' || exit 1
|
||||||
|
$TMUX split-window -d -h -t layouts:many 'exec sleep 100' || exit 1
|
||||||
|
floating=$($TMUX new-pane -dP -F '#{pane_id}' -t layouts:many \
|
||||||
|
-x20 -y8 -X10 -Y5 'exec sleep 100') || exit 1
|
||||||
|
: >"$SEEN"
|
||||||
|
|
||||||
|
# Options exercise percentage and explicit secondary-size calculations.
|
||||||
|
$TMUX set-option -w -t layouts:many main-pane-height 30% || exit 1
|
||||||
|
$TMUX set-option -w -t layouts:many other-pane-height 12 || exit 1
|
||||||
|
$TMUX set-option -w -t layouts:many main-pane-width 35% || exit 1
|
||||||
|
$TMUX set-option -w -t layouts:many other-pane-width 20 || exit 1
|
||||||
|
|
||||||
|
for name in even-horizontal even-vertical main-horizontal \
|
||||||
|
main-horizontal-mirrored main-vertical main-vertical-mirrored tiled; do
|
||||||
|
check_layout "$name"
|
||||||
|
done
|
||||||
|
|
||||||
|
# Exact and unique-prefix lookup work; ambiguous and invalid names fail.
|
||||||
|
$TMUX select-layout -t layouts:many even-h || fail "unique layout prefix failed"
|
||||||
|
$TMUX select-layout -t layouts:many main >/dev/null 2>&1 &&
|
||||||
|
fail "ambiguous layout prefix succeeded"
|
||||||
|
$TMUX select-layout -t layouts:many no-such-layout >/dev/null 2>&1 &&
|
||||||
|
fail "invalid layout name succeeded"
|
||||||
|
|
||||||
|
# Cycle through all layouts in both directions, including wraparound. The
|
||||||
|
# aliases and select-layout flags share layout_set_next/previous.
|
||||||
|
first=$(layout)
|
||||||
|
i=0
|
||||||
|
while [ "$i" -lt 7 ]; do
|
||||||
|
$TMUX next-layout -t layouts:many || exit 1
|
||||||
|
i=$((i + 1))
|
||||||
|
done
|
||||||
|
[ "$(layout)" = "$first" ] || fail "next-layout did not wrap"
|
||||||
|
i=0
|
||||||
|
while [ "$i" -lt 7 ]; do
|
||||||
|
$TMUX previous-layout -t layouts:many || exit 1
|
||||||
|
i=$((i + 1))
|
||||||
|
done
|
||||||
|
[ "$(layout)" = "$first" ] || fail "previous-layout did not wrap"
|
||||||
|
$TMUX select-layout -t layouts:many -n || exit 1
|
||||||
|
$TMUX select-layout -t layouts:many -p || exit 1
|
||||||
|
|
||||||
|
# No argument reapplies the last preset; -o restores the previous layout; -E
|
||||||
|
# spreads the current cell without changing the pane set.
|
||||||
|
$TMUX kill-pane -t "$floating" || exit 1
|
||||||
|
$TMUX select-layout -t layouts:many tiled || exit 1
|
||||||
|
tiled=$(layout)
|
||||||
|
$TMUX select-layout -t layouts:many even-horizontal || exit 1
|
||||||
|
$TMUX select-layout -t layouts:many -o || exit 1
|
||||||
|
[ "$(layout)" = "$tiled" ] || fail "select-layout -o did not restore layout"
|
||||||
|
$TMUX select-layout -t layouts:many || exit 1
|
||||||
|
$TMUX select-layout -t layouts:many -E || exit 1
|
||||||
|
|
||||||
|
# Each arranger has a deliberate one-pane early return.
|
||||||
|
$TMUX new-window -d -t layouts: -n one 'exec sleep 100' || exit 1
|
||||||
|
one=$($TMUX display-message -p -t layouts:one '#{window_layout}')
|
||||||
|
for name in even-horizontal even-vertical main-horizontal \
|
||||||
|
main-horizontal-mirrored main-vertical main-vertical-mirrored tiled; do
|
||||||
|
$TMUX select-layout -t layouts:one "$name" || exit 1
|
||||||
|
[ "$($TMUX display-message -p -t layouts:one '#{window_layout}')" = "$one" ] ||
|
||||||
|
fail "$name changed a one-pane layout"
|
||||||
|
done
|
||||||
|
|
||||||
|
# Force the minimum-size paths without requiring a terminal client.
|
||||||
|
$TMUX resize-window -t layouts:many -x10 -y6 || exit 1
|
||||||
|
for name in even-horizontal even-vertical main-horizontal \
|
||||||
|
main-horizontal-mirrored main-vertical main-vertical-mirrored tiled; do
|
||||||
|
$TMUX select-layout -t layouts:many "$name" || exit 1
|
||||||
|
done
|
||||||
|
|
||||||
|
$TMUX has-session -t layouts || fail "server died during layout tests"
|
||||||
|
exit 0
|
||||||
46
regress/list-commands.sh
Normal file
46
regress/list-commands.sh
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
# Exercise list-commands for the full table, a single command, aliases, custom
|
||||||
|
# and empty formats, and lookup errors.
|
||||||
|
|
||||||
|
PATH=/bin:/usr/bin
|
||||||
|
TERM=screen
|
||||||
|
export PATH TERM
|
||||||
|
|
||||||
|
[ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux)
|
||||||
|
DIR=$(mktemp -d) || exit 1
|
||||||
|
TMUX_TMPDIR=$DIR
|
||||||
|
export TMUX_TMPDIR
|
||||||
|
TMUX="$TEST_TMUX -Ltest$$ -f/dev/null"
|
||||||
|
|
||||||
|
cleanup()
|
||||||
|
{
|
||||||
|
$TMUX kill-server 2>/dev/null
|
||||||
|
rm -rf "$DIR"
|
||||||
|
}
|
||||||
|
trap cleanup 0 1 15
|
||||||
|
|
||||||
|
# Keep a server alive across commands. Otherwise a STARTSERVER-only command
|
||||||
|
# may exit between invocations and leave a short socket-cleanup race.
|
||||||
|
$TMUX new-session -d -s list-commands 'exec sleep 100' || exit 1
|
||||||
|
|
||||||
|
all=$($TMUX list-commands) || exit 1
|
||||||
|
printf '%s\n' "$all" | grep -q '^attach-session (attach)' || exit 1
|
||||||
|
printf '%s\n' "$all" | grep -q '^list-commands (lscm)' || exit 1
|
||||||
|
|
||||||
|
one=$($TMUX list-commands -F \
|
||||||
|
'#{command_list_name}|#{command_list_alias}|#{command_list_usage}' \
|
||||||
|
list-commands) || exit 1
|
||||||
|
case "$one" in
|
||||||
|
'list-commands|lscm|'*) ;;
|
||||||
|
*) exit 1 ;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
# Aliases resolve to their command, while an empty expansion prints no line.
|
||||||
|
[ "$($TMUX list-commands -F '#{command_list_name}' lscm)" = list-commands ] ||
|
||||||
|
exit 1
|
||||||
|
[ -z "$($TMUX list-commands -F '' list-commands)" ] || exit 1
|
||||||
|
|
||||||
|
$TMUX list-commands tmux-no-command-$$ >/dev/null 2>&1 && exit 1
|
||||||
|
$TMUX list-commands list >/dev/null 2>&1 && exit 1
|
||||||
|
exit 0
|
||||||
0
regress/mode-kill.sh
Executable file → Normal file
0
regress/mode-kill.sh
Executable file → Normal file
168
regress/output-commands.sh
Normal file
168
regress/output-commands.sh
Normal file
@@ -0,0 +1,168 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
PATH=/bin:/usr/bin
|
||||||
|
TERM=screen
|
||||||
|
|
||||||
|
[ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux)
|
||||||
|
TMUX="$TEST_TMUX -L${TEST_SOCKET:-testO$$} -f/dev/null"
|
||||||
|
OUT=/tmp/tmux-output-commands-$$
|
||||||
|
EXPECTED=$(printf 'one\ntwo')
|
||||||
|
|
||||||
|
cleanup()
|
||||||
|
{
|
||||||
|
$TMUX kill-server 2>/dev/null
|
||||||
|
rm -f "$OUT"
|
||||||
|
}
|
||||||
|
trap cleanup EXIT HUP INT TERM
|
||||||
|
|
||||||
|
$TMUX kill-server 2>/dev/null
|
||||||
|
$TMUX new-session -d -x80 -y20 "sh -c 'printf \"\\033]133;A\\007p\\$ \\033]133;B\\007echo\\n\\033]133;C\\007one\\ntwo\\n\\033]133;D;0\\007separator\\n\\033]133;A\\007p\\$ \\033]133;B\\007broken\\n\\033]133;C\\007unfinished\"; exec sleep 100'" || exit 1
|
||||||
|
sleep 1
|
||||||
|
|
||||||
|
$TMUX copy-mode -U || exit 1
|
||||||
|
$TMUX send-keys -X copy-output || exit 1
|
||||||
|
[ "$($TMUX show-buffer)" = unfinished ] || exit 1
|
||||||
|
|
||||||
|
$TMUX send-keys -X pipe-output "wc -c >$OUT" || exit 1
|
||||||
|
sleep 1
|
||||||
|
[ "$(cat "$OUT")" = 10 ] || exit 1
|
||||||
|
|
||||||
|
$TMUX send-keys -X copy-pipe-output "wc -c >$OUT" output || exit 1
|
||||||
|
sleep 1
|
||||||
|
[ "$(cat "$OUT")" = 10 ] || exit 1
|
||||||
|
[ "$($TMUX show-buffer)" = unfinished ] || exit 1
|
||||||
|
|
||||||
|
$TMUX send-keys -X select-output || exit 1
|
||||||
|
$TMUX send-keys -X copy-selection || exit 1
|
||||||
|
[ "$($TMUX show-buffer)" = unfinished ] || exit 1
|
||||||
|
$TMUX send-keys -X cancel || exit 1
|
||||||
|
|
||||||
|
$TMUX copy-mode -U || exit 1
|
||||||
|
$TMUX send-keys -X search-backward separator || exit 1
|
||||||
|
$TMUX send-keys -X copy-output || exit 1
|
||||||
|
copied=$($TMUX show-buffer)
|
||||||
|
[ "$copied" = "$EXPECTED" ] || exit 1
|
||||||
|
$TMUX send-keys -X cancel || exit 1
|
||||||
|
|
||||||
|
$TMUX copy-mode -U || exit 1
|
||||||
|
$TMUX send-keys -X search-backward one || exit 1
|
||||||
|
$TMUX send-keys -X copy-output || exit 1
|
||||||
|
copied=$($TMUX show-buffer)
|
||||||
|
[ "$copied" = "$EXPECTED" ] || exit 1
|
||||||
|
|
||||||
|
$TMUX send-keys -X pipe-output "wc -l >$OUT" || exit 1
|
||||||
|
sleep 1
|
||||||
|
[ "$(cat "$OUT")" = 2 ] || exit 1
|
||||||
|
|
||||||
|
$TMUX send-keys -X copy-pipe-output "wc -l >$OUT" output || exit 1
|
||||||
|
sleep 1
|
||||||
|
[ "$(cat "$OUT")" = 2 ] || exit 1
|
||||||
|
copied=$($TMUX show-buffer)
|
||||||
|
[ "$copied" = "$EXPECTED" ] || exit 1
|
||||||
|
$TMUX send-keys -X cancel || exit 1
|
||||||
|
|
||||||
|
$TMUX copy-mode -c || exit 1
|
||||||
|
$TMUX send-keys -X search-backward echo || exit 1
|
||||||
|
$TMUX send-keys -X select-output || exit 1
|
||||||
|
$TMUX send-keys -X copy-selection || exit 1
|
||||||
|
selected=$($TMUX show-buffer)
|
||||||
|
[ "$selected" = "$EXPECTED" ] || exit 1
|
||||||
|
$TMUX send-keys -X cancel || exit 1
|
||||||
|
|
||||||
|
$TMUX set-buffer -b keep unchanged || exit 1
|
||||||
|
$TMUX copy-mode -U || exit 1
|
||||||
|
$TMUX send-keys -X search-backward unfinished || exit 1
|
||||||
|
$TMUX send-keys -X copy-output || exit 1
|
||||||
|
[ "$($TMUX show-buffer)" = unfinished ] || exit 1
|
||||||
|
$TMUX send-keys -X cancel || exit 1
|
||||||
|
|
||||||
|
$TMUX new-window -d -n plain "printf 'alpha\\nbeta\\n'; exec sleep 100" || exit 1
|
||||||
|
sleep 1
|
||||||
|
$TMUX copy-mode -t :plain || exit 1
|
||||||
|
$TMUX send-keys -t :plain.0 -X copy-output -a || exit 1
|
||||||
|
all=$($TMUX show-buffer)
|
||||||
|
case "$all" in
|
||||||
|
*alpha*beta*) ;;
|
||||||
|
*) exit 1 ;;
|
||||||
|
esac
|
||||||
|
$TMUX send-keys -t :plain.0 -X cancel || exit 1
|
||||||
|
|
||||||
|
$TMUX set-buffer -b keep unchanged || exit 1
|
||||||
|
$TMUX copy-mode -t :plain || exit 1
|
||||||
|
$TMUX send-keys -t :plain.0 -X copy-output || exit 1
|
||||||
|
[ "$($TMUX show-buffer -b keep)" = unchanged ] || exit 1
|
||||||
|
$TMUX send-keys -t :plain.0 -X cancel || exit 1
|
||||||
|
|
||||||
|
$TMUX copy-mode -t :plain || exit 1
|
||||||
|
$TMUX send-keys -t :plain.0 -X select-output || exit 1
|
||||||
|
$TMUX send-keys -t :plain.0 -X select-output -a || exit 1
|
||||||
|
$TMUX send-keys -t :plain.0 -X copy-selection || exit 1
|
||||||
|
all=$($TMUX show-buffer)
|
||||||
|
case "$all" in
|
||||||
|
*alpha*beta*) ;;
|
||||||
|
*) exit 1 ;;
|
||||||
|
esac
|
||||||
|
$TMUX send-keys -t :plain.0 -X cancel || exit 1
|
||||||
|
|
||||||
|
$TMUX new-window -d -n empty "printf '\\033]133;A\\007p\\$ \\033]133;B\\007echo\\033]133;C\\007one\\n\\033]133;D;0\\007\\033]133;A\\007p\\$ \\033]133;B\\007true\\033]133;C\\033]133;D;0\\007'; exec sleep 100" || exit 1
|
||||||
|
sleep 1
|
||||||
|
$TMUX copy-mode -t :empty || exit 1
|
||||||
|
$TMUX send-keys -t :empty.0 -X copy-output || exit 1
|
||||||
|
[ "$($TMUX show-buffer)" = one ] || exit 1
|
||||||
|
$TMUX send-keys -t :empty.0 -X cancel || exit 1
|
||||||
|
|
||||||
|
$TMUX new-window -d -n prompt "printf '\\033]133;A\\007p\\$ \\033]133;B\\007echo\\033]133;C\\007one\\n\\033]133;D;0\\007\\033]133;A\\007p\\$ \\033]133;B\\007'; exec sleep 100" || exit 1
|
||||||
|
sleep 1
|
||||||
|
$TMUX copy-mode -U -t :prompt || exit 1
|
||||||
|
$TMUX send-keys -t :prompt.0 -X copy-output || exit 1
|
||||||
|
[ "$($TMUX show-buffer)" = one ] || exit 1
|
||||||
|
$TMUX send-keys -t :prompt.0 -X cancel || exit 1
|
||||||
|
|
||||||
|
$TMUX copy-mode -c -t :prompt || exit 1
|
||||||
|
$TMUX send-keys -t :prompt.0 -X expand-output || exit 1
|
||||||
|
$TMUX send-keys -t :prompt.0 -X -N 10 cursor-down || exit 1
|
||||||
|
$TMUX send-keys -t :prompt.0 -X select-output || exit 1
|
||||||
|
[ "$($TMUX display-message -p -t :prompt.0 '#{selection_present}')" = 1 ] || exit 1
|
||||||
|
$TMUX send-keys -t :prompt.0 -X copy-selection || exit 1
|
||||||
|
[ "$($TMUX show-buffer)" = one ] || exit 1
|
||||||
|
$TMUX send-keys -t :prompt.0 -X cancel || exit 1
|
||||||
|
|
||||||
|
$TMUX new-window -d -n below "printf '\\033]133;A\\007p\\$ \\033]133;B\\007old\\n\\033]133;C\\007old1\\nold2\\nold3\\nold4\\nold5\\n\\033]133;D;0\\007separator\\n\\033]133;A\\007p\\$ \\033]133;B\\007ps\\n\\033]133;C\\007PID TTY\\n1 pts/0\\n\\033]133;D;0\\007separator\\n\\033]133;A\\007p\\$ \\033]133;B\\007'; exec sleep 100" || exit 1
|
||||||
|
sleep 1
|
||||||
|
$TMUX copy-mode -c -t :below || exit 1
|
||||||
|
$TMUX send-keys -t :below.0 -X search-backward ps || exit 1
|
||||||
|
$TMUX send-keys -t :below.0 -X expand-output || exit 1
|
||||||
|
$TMUX send-keys -t :below.0 -X -N 100 cursor-down || exit 1
|
||||||
|
$TMUX send-keys -t :below.0 C-o || exit 1
|
||||||
|
[ "$($TMUX display-message -p -t :below.0 '#{selection_present}')" = 1 ] || exit 1
|
||||||
|
$TMUX send-keys -t :below.0 -X copy-selection || exit 1
|
||||||
|
[ "$($TMUX show-buffer)" = "$(printf 'PID TTY\\n1 pts/0')" ] || exit 1
|
||||||
|
$TMUX send-keys -t :below.0 -X cancel || exit 1
|
||||||
|
|
||||||
|
$TMUX new-window -d -n clear "printf 'old1\\nold2\\nold3\\nold4\\nold5\\nold6\\n\\033]133;A\\007p\\$ \\033]133;B\\007echo 1; clear; ps\\n\\033]133;C\\0071\\n\\033[H\\033[2JPID TTY\\n1 pts/0\\n\\033]133;D;0\\007separator\\n\\033]133;A\\007p\\$ \\033]133;B\\007'; exec sleep 100" || exit 1
|
||||||
|
sleep 1
|
||||||
|
$TMUX copy-mode -t :clear || exit 1
|
||||||
|
$TMUX send-keys -t :clear.0 C-o || exit 1
|
||||||
|
$TMUX send-keys -t :clear.0 -X copy-selection || exit 1
|
||||||
|
[ "$($TMUX show-buffer)" = "$(printf 'PID TTY\\n1 pts/0')" ] || exit 1
|
||||||
|
$TMUX send-keys -t :clear.0 -X cancel || exit 1
|
||||||
|
|
||||||
|
$TMUX new-window -d -n same "printf '\\033]133;A\\007p\\$ \\033]133;B\\007echo\\n\\033]133;C\\007one\\033]133;D;0\\007\\033]133;A\\007p\\$ \\033]133;B\\007'; exec sleep 100" || exit 1
|
||||||
|
sleep 1
|
||||||
|
$TMUX copy-mode -U -t :same || exit 1
|
||||||
|
$TMUX send-keys -t :same.0 -X search-backward one || exit 1
|
||||||
|
$TMUX send-keys -t :same.0 -X select-output || exit 1
|
||||||
|
$TMUX send-keys -t :same.0 -X copy-selection || exit 1
|
||||||
|
[ "$($TMUX show-buffer)" = one ] || exit 1
|
||||||
|
$TMUX send-keys -t :same.0 -X cancel || exit 1
|
||||||
|
|
||||||
|
$TMUX copy-mode -t :plain || exit 1
|
||||||
|
$TMUX send-keys -t :plain.0 -X copy-output -a || exit 1
|
||||||
|
all=$($TMUX show-buffer)
|
||||||
|
case "$all" in
|
||||||
|
*alpha*beta*) ;;
|
||||||
|
*) exit 1 ;;
|
||||||
|
esac
|
||||||
|
$TMUX send-keys -t :plain.0 -X cancel || exit 1
|
||||||
|
|
||||||
|
exit 0
|
||||||
165
regress/prompt-words-history.sh
Normal file
165
regress/prompt-words-history.sh
Normal file
@@ -0,0 +1,165 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
# Cover prompt word movement in emacs and vi modes, ambiguous inline command
|
||||||
|
# completion, and the show/clear-prompt-history command surface.
|
||||||
|
|
||||||
|
PATH=/bin:/usr/bin
|
||||||
|
TERM=screen
|
||||||
|
LC_ALL=C.UTF-8
|
||||||
|
export PATH TERM LC_ALL
|
||||||
|
|
||||||
|
[ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux)
|
||||||
|
DIR=$(mktemp -d) || exit 1
|
||||||
|
TMUX_TMPDIR=$DIR
|
||||||
|
export TMUX_TMPDIR
|
||||||
|
INNER="$TEST_TMUX -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
|
||||||
|
rm -rf "$DIR"
|
||||||
|
}
|
||||||
|
trap cleanup 0 1 15
|
||||||
|
|
||||||
|
capture()
|
||||||
|
{
|
||||||
|
$OUTER capture-pane -p -t outer:0.0 2>/dev/null
|
||||||
|
}
|
||||||
|
|
||||||
|
wait_result()
|
||||||
|
{
|
||||||
|
want=$1
|
||||||
|
i=0
|
||||||
|
while [ "$i" -lt 50 ]; do
|
||||||
|
got=$($INNER show-option -gqv @result 2>/dev/null)
|
||||||
|
[ "$got" = "$want" ] && return 0
|
||||||
|
sleep 0.1
|
||||||
|
i=$((i + 1))
|
||||||
|
done
|
||||||
|
fail "prompt result is '$got', expected '$want'"
|
||||||
|
}
|
||||||
|
|
||||||
|
bind_prompt()
|
||||||
|
{
|
||||||
|
initial=$1
|
||||||
|
$INNER bind-key -n M-r command-prompt -I "$initial" -p '(word)' \
|
||||||
|
"set-option -g @result '%%'" || exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
run_prompt()
|
||||||
|
{
|
||||||
|
want=$1
|
||||||
|
shift
|
||||||
|
$INNER set-option -g @result sentinel || exit 1
|
||||||
|
$OUTER send-keys M-r || exit 1
|
||||||
|
sleep 0.2
|
||||||
|
$OUTER send-keys "$@" || exit 1
|
||||||
|
$OUTER send-keys Enter || exit 1
|
||||||
|
wait_result "$want"
|
||||||
|
}
|
||||||
|
|
||||||
|
run_vi_prompt()
|
||||||
|
{
|
||||||
|
want=$1
|
||||||
|
shift
|
||||||
|
$INNER set-option -g @result sentinel || exit 1
|
||||||
|
$OUTER send-keys M-r || exit 1
|
||||||
|
sleep 0.2
|
||||||
|
# Send Escape separately so the terminal's escape-time handling does not
|
||||||
|
# combine it with the first vi command as a Meta key.
|
||||||
|
$OUTER send-keys Escape || exit 1
|
||||||
|
# The inner client must see Escape as a complete key, not the prefix of a
|
||||||
|
# Meta sequence (the default escape-time is 500 milliseconds).
|
||||||
|
sleep 0.7
|
||||||
|
$OUTER send-keys "$@" || exit 1
|
||||||
|
$OUTER send-keys Enter || exit 1
|
||||||
|
wait_result "$want"
|
||||||
|
}
|
||||||
|
|
||||||
|
$INNER new-session -d -s prompt -x80 -y24 'exec sleep 100' || exit 1
|
||||||
|
$INNER set-option -g status on || exit 1
|
||||||
|
$INNER set-option -g status-position bottom || exit 1
|
||||||
|
$INNER set-option -g window-size manual || exit 1
|
||||||
|
$OUTER new-session -d -s outer -x80 -y24 "$INNER attach -t prompt" || exit 1
|
||||||
|
$OUTER set-option -g status off || exit 1
|
||||||
|
$OUTER set-option -g window-size manual || exit 1
|
||||||
|
sleep 1
|
||||||
|
|
||||||
|
# Emacs Meta-f stops after the first word; Meta-b returns to the start of the
|
||||||
|
# previous word. Inserting a marker makes the cursor position observable.
|
||||||
|
$INNER set-option -g status-keys emacs || exit 1
|
||||||
|
bind_prompt 'one two'
|
||||||
|
run_prompt 'oneX two' Home M-f X
|
||||||
|
bind_prompt 'one two'
|
||||||
|
run_prompt 'one Xtwo' M-b X
|
||||||
|
|
||||||
|
# Vi translation and the distinct separator-aware and WORD motions.
|
||||||
|
$INNER set-option -g status-keys vi || exit 1
|
||||||
|
bind_prompt 'one-two three'
|
||||||
|
run_vi_prompt 'one-two Xthree' b i X
|
||||||
|
bind_prompt 'one-two three'
|
||||||
|
run_vi_prompt 'one-two Xthree' B i X
|
||||||
|
bind_prompt 'one-two three'
|
||||||
|
run_vi_prompt 'oneX-two three' 0 w i X
|
||||||
|
bind_prompt 'one-two three'
|
||||||
|
run_vi_prompt 'one-two Xthree' 0 W i X
|
||||||
|
bind_prompt 'one-two three'
|
||||||
|
run_vi_prompt 'oneX-two three' 0 e a X
|
||||||
|
bind_prompt 'one-two three'
|
||||||
|
run_vi_prompt 'one-twoX three' 0 E a X
|
||||||
|
|
||||||
|
# "show-" has several command matches and no longer common prefix. Tab keeps
|
||||||
|
# the input and draws the sorted candidates inline.
|
||||||
|
$INNER set-option -g status-keys emacs || exit 1
|
||||||
|
bind_prompt ''
|
||||||
|
$OUTER send-keys M-r || exit 1
|
||||||
|
sleep 0.2
|
||||||
|
$OUTER send-keys -l 'show-' || exit 1
|
||||||
|
$OUTER send-keys Tab || exit 1
|
||||||
|
sleep 0.2
|
||||||
|
captured=$(capture)
|
||||||
|
printf '%s\n' "$captured" | grep -Fq 'show-buffer' ||
|
||||||
|
fail "ambiguous completion list was not drawn"
|
||||||
|
printf '%s\n' "$captured" | grep -Fq 'show-environment' ||
|
||||||
|
fail "ambiguous completion list was incomplete"
|
||||||
|
$OUTER send-keys Escape || exit 1
|
||||||
|
|
||||||
|
# Add entries to both history rings through real prompts.
|
||||||
|
bind_prompt 'history-command'
|
||||||
|
run_prompt 'history-command'
|
||||||
|
$INNER bind-key -n M-s command-prompt -T search -I history-search \
|
||||||
|
-p '(search-history)' "set-option -g @result '%%'" || exit 1
|
||||||
|
$OUTER send-keys M-s || exit 1
|
||||||
|
$OUTER send-keys Enter || exit 1
|
||||||
|
wait_result history-search
|
||||||
|
|
||||||
|
command_history=$($INNER show-prompt-history -T command) || exit 1
|
||||||
|
printf '%s\n' "$command_history" | grep -Fq 'history-command' ||
|
||||||
|
fail "command history entry missing"
|
||||||
|
search_history=$($INNER show-prompt-history -T search) || exit 1
|
||||||
|
printf '%s\n' "$search_history" | grep -Fq 'history-search' ||
|
||||||
|
fail "search history entry missing"
|
||||||
|
all_history=$($INNER show-prompt-history) || exit 1
|
||||||
|
printf '%s\n' "$all_history" | grep -Fq 'History for command:' || exit 1
|
||||||
|
printf '%s\n' "$all_history" | grep -Fq 'History for search:' || exit 1
|
||||||
|
|
||||||
|
$INNER show-prompt-history -T invalid >/dev/null 2>&1 &&
|
||||||
|
fail "invalid show history type succeeded"
|
||||||
|
$INNER clear-prompt-history -T invalid >/dev/null 2>&1 &&
|
||||||
|
fail "invalid clear history type succeeded"
|
||||||
|
$INNER clear-prompt-history -T command || exit 1
|
||||||
|
$INNER show-prompt-history -T command | grep -Fq history-command &&
|
||||||
|
fail "type-specific history clear failed"
|
||||||
|
$INNER clear-prompt-history || exit 1
|
||||||
|
$INNER show-prompt-history | grep -Fq history-search &&
|
||||||
|
fail "all-history clear failed"
|
||||||
|
|
||||||
|
exit 0
|
||||||
96
regress/screen-redraw-scrollbars-auto-hide.sh
Normal file
96
regress/screen-redraw-scrollbars-auto-hide.sh
Normal file
@@ -0,0 +1,96 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
# Auto-hide scrollbars appear on activity or pointer hover, then disappear
|
||||||
|
# after pane-scrollbars-timeout. Compare captures of the same copy-mode view so
|
||||||
|
# only the overlay scrollbar changes.
|
||||||
|
|
||||||
|
PATH=/bin:/usr/bin
|
||||||
|
TERM=screen
|
||||||
|
LC_ALL=C.UTF-8
|
||||||
|
export PATH TERM LC_ALL
|
||||||
|
|
||||||
|
[ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux)
|
||||||
|
OUTER="$TEST_TMUX -LtestA$$ -f/dev/null"
|
||||||
|
INNER="$TEST_TMUX -LtestB$$ -f/dev/null"
|
||||||
|
DIR=$(mktemp -d) || exit 1
|
||||||
|
HIDDEN=$DIR/hidden
|
||||||
|
VISIBLE=$DIR/visible
|
||||||
|
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
|
||||||
|
|
||||||
|
capture()
|
||||||
|
{
|
||||||
|
$OUTER capture-pane -pe -t outer:0.0 >"$1" || exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
wait_for_client()
|
||||||
|
{
|
||||||
|
i=0
|
||||||
|
while [ "$i" -lt 50 ]; do
|
||||||
|
$INNER list-clients 2>/dev/null | grep -q . && return 0
|
||||||
|
sleep 0.1
|
||||||
|
i=$((i + 1))
|
||||||
|
done
|
||||||
|
fail "inner client did not attach"
|
||||||
|
}
|
||||||
|
|
||||||
|
$INNER new-session -d -s inner -x40 -y12 \
|
||||||
|
"sh -c 'seq 50; 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-window-option pane-scrollbars auto-hide || exit 1
|
||||||
|
$INNER set-window-option pane-scrollbars-timeout 3000 || exit 1
|
||||||
|
$INNER set-window-option pane-scrollbars-style \
|
||||||
|
'bg=colour196,fg=colour231,width=1,pad=0' || exit 1
|
||||||
|
|
||||||
|
$OUTER new-session -d -s outer -x40 -y12 "$INNER attach -t inner" || exit 1
|
||||||
|
$OUTER set-option -g status off || exit 1
|
||||||
|
$OUTER set-option -g window-size manual || exit 1
|
||||||
|
wait_for_client
|
||||||
|
|
||||||
|
# Enter copy mode and choose a stable view. The initial show timer is allowed
|
||||||
|
# to expire before taking the hidden reference capture.
|
||||||
|
$INNER copy-mode -H || exit 1
|
||||||
|
$INNER send-keys -X history-top || exit 1
|
||||||
|
sleep 3.5
|
||||||
|
capture "$HIDDEN"
|
||||||
|
|
||||||
|
# A copy-mode page movement shows the overlay and starts its timer. Capture
|
||||||
|
# that view immediately, then again after the timeout without moving it.
|
||||||
|
$INNER send-keys -X page-down || exit 1
|
||||||
|
sleep 0.1
|
||||||
|
capture "$VISIBLE"
|
||||||
|
sleep 3.5
|
||||||
|
capture "$AFTER"
|
||||||
|
cmp -s "$VISIBLE" "$AFTER" && fail "scrollbar did not hide after timeout"
|
||||||
|
|
||||||
|
# Use the post-movement hidden scene as the reference for pointer hover. SGR
|
||||||
|
# mouse column 40 is the right-hand scrollbar; moving to column 5 restarts the
|
||||||
|
# timer without changing the copy-mode view.
|
||||||
|
cp "$AFTER" "$HIDDEN"
|
||||||
|
hover=$(printf '\033[<35;40;5M')
|
||||||
|
$OUTER send-keys -l "$hover" || exit 1
|
||||||
|
sleep 0.1
|
||||||
|
capture "$VISIBLE"
|
||||||
|
cmp -s "$HIDDEN" "$VISIBLE" && fail "scrollbar did not appear on hover"
|
||||||
|
away=$(printf '\033[<35;5;5M')
|
||||||
|
$OUTER send-keys -l "$away" || exit 1
|
||||||
|
sleep 3.5
|
||||||
|
capture "$AFTER"
|
||||||
|
cmp -s "$HIDDEN" "$AFTER" || fail "scrollbar did not hide after hover"
|
||||||
|
|
||||||
|
exit 0
|
||||||
112
regress/server-access.sh
Normal file
112
regress/server-access.sh
Normal file
@@ -0,0 +1,112 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
# Exercise the unprivileged server-access surface: user and group ACL entries,
|
||||||
|
# read-only/writable transitions, listing, removal, implied addition, and
|
||||||
|
# command errors. Actual connections from another UID require privileges and
|
||||||
|
# deliberately do not form part of this test.
|
||||||
|
|
||||||
|
PATH=/bin:/usr/bin
|
||||||
|
TERM=screen
|
||||||
|
export PATH TERM
|
||||||
|
|
||||||
|
[ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux)
|
||||||
|
TMUX="$TEST_TMUX -LtestA$$ -f/dev/null"
|
||||||
|
OUT=$(mktemp) || exit 1
|
||||||
|
|
||||||
|
fail()
|
||||||
|
{
|
||||||
|
echo "$*" >&2
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
cleanup()
|
||||||
|
{
|
||||||
|
$TMUX kill-server 2>/dev/null
|
||||||
|
rm -f "$OUT"
|
||||||
|
}
|
||||||
|
trap cleanup 0 1 15
|
||||||
|
|
||||||
|
expect_fail()
|
||||||
|
{
|
||||||
|
message=$1
|
||||||
|
shift
|
||||||
|
if $TMUX "$@" >"$OUT" 2>&1; then
|
||||||
|
fail "$message: command unexpectedly succeeded"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
assert_entry()
|
||||||
|
{
|
||||||
|
entry=$1
|
||||||
|
$TMUX server-access -l >"$OUT" || exit 1
|
||||||
|
grep -Fxq "$entry" "$OUT" ||
|
||||||
|
fail "missing ACL entry '$entry'"
|
||||||
|
}
|
||||||
|
|
||||||
|
assert_no_entry()
|
||||||
|
{
|
||||||
|
entry=$1
|
||||||
|
$TMUX server-access -l >"$OUT" || exit 1
|
||||||
|
if grep -Fxq "$entry" "$OUT"; then
|
||||||
|
fail "unexpected ACL entry '$entry'"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
owner_uid=$(id -u)
|
||||||
|
owner_name=$(id -un)
|
||||||
|
|
||||||
|
# nobody is the non-owner test identity. Derive its primary group rather than
|
||||||
|
# assuming whether the system calls it nobody, nogroup, or something else.
|
||||||
|
acl_user=nobody
|
||||||
|
acl_group=$(id -gn "$acl_user" 2>/dev/null) ||
|
||||||
|
fail "the nobody account is unavailable"
|
||||||
|
[ "$owner_name" != "$acl_user" ] || fail "test cannot run as nobody"
|
||||||
|
|
||||||
|
$TMUX new-session -d -s access 'exec sleep 100' || exit 1
|
||||||
|
|
||||||
|
# The owner is installed as writable at server startup (root is intentionally
|
||||||
|
# omitted from display output).
|
||||||
|
if [ "$owner_uid" != 0 ]; then
|
||||||
|
assert_entry "$owner_name (U,W)"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# User ACL lifecycle, including duplicate and missing-entry errors.
|
||||||
|
$TMUX server-access -a "$acl_user" || exit 1
|
||||||
|
assert_entry "$acl_user (U,W)"
|
||||||
|
expect_fail "duplicate user addition" server-access -a "$acl_user"
|
||||||
|
$TMUX server-access -r "$acl_user" || exit 1
|
||||||
|
assert_entry "$acl_user (U,R)"
|
||||||
|
$TMUX server-access -w "$acl_user" || exit 1
|
||||||
|
assert_entry "$acl_user (U,W)"
|
||||||
|
$TMUX server-access -d "$acl_user" || exit 1
|
||||||
|
assert_no_entry "$acl_user (U,W)"
|
||||||
|
expect_fail "removing absent user" server-access -d "$acl_user"
|
||||||
|
|
||||||
|
# -r and -w imply -a when an entry is absent.
|
||||||
|
$TMUX server-access -r "$acl_user" || exit 1
|
||||||
|
assert_entry "$acl_user (U,R)"
|
||||||
|
$TMUX server-access -d "$acl_user" || exit 1
|
||||||
|
|
||||||
|
# The same lifecycle for a group exercises the separate ACL key space.
|
||||||
|
$TMUX server-access -g -a -r "$acl_group" || exit 1
|
||||||
|
assert_entry "$acl_group (G,R)"
|
||||||
|
$TMUX server-access -g -w "$acl_group" || exit 1
|
||||||
|
assert_entry "$acl_group (G,W)"
|
||||||
|
expect_fail "duplicate group addition" server-access -g -a "$acl_group"
|
||||||
|
$TMUX server-access -g -d "$acl_group" || exit 1
|
||||||
|
assert_no_entry "$acl_group (G,W)"
|
||||||
|
expect_fail "removing absent group" server-access -g -d "$acl_group"
|
||||||
|
|
||||||
|
# Parser and lookup errors, plus the immutable owner/root entries.
|
||||||
|
expect_fail "missing ACL subject" server-access -a
|
||||||
|
expect_fail "unknown user" server-access -a "tmux-no-user-$$"
|
||||||
|
expect_fail "unknown group" server-access -g -a "tmux-no-group-$$"
|
||||||
|
expect_fail "conflicting add/delete" server-access -a -d "$acl_user"
|
||||||
|
expect_fail "conflicting read/write" server-access -r -w "$acl_user"
|
||||||
|
expect_fail "changing server owner" server-access -r "$owner_name"
|
||||||
|
if [ "$owner_name" != root ]; then
|
||||||
|
expect_fail "changing root access" server-access -a root
|
||||||
|
fi
|
||||||
|
|
||||||
|
$TMUX has-session -t access || fail "server exited during ACL updates"
|
||||||
|
exit 0
|
||||||
0
regress/server-socket-error.sh
Executable file → Normal file
0
regress/server-socket-error.sh
Executable file → Normal file
0
regress/set-hook-E.sh
Executable file → Normal file
0
regress/set-hook-E.sh
Executable file → Normal file
203
regress/switch-mode.sh
Normal file
203
regress/switch-mode.sh
Normal file
@@ -0,0 +1,203 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
# Exercise switch-mode's session and window lists with a real attached client:
|
||||||
|
# fuzzy filtering, movement and selection, the default and custom commands,
|
||||||
|
# no-match cancellation, resize, zoom restoration, and -k mode teardown.
|
||||||
|
|
||||||
|
PATH=/bin:/usr/bin
|
||||||
|
TERM=screen
|
||||||
|
LC_ALL=C.UTF-8
|
||||||
|
export PATH TERM LC_ALL
|
||||||
|
|
||||||
|
[ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux)
|
||||||
|
DIR=$(mktemp -d) || exit 1
|
||||||
|
TMUX_TMPDIR=$DIR
|
||||||
|
export TMUX_TMPDIR
|
||||||
|
INNER="$TEST_TMUX -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
|
||||||
|
rm -rf "$DIR"
|
||||||
|
}
|
||||||
|
trap cleanup 0 1 15
|
||||||
|
|
||||||
|
capture()
|
||||||
|
{
|
||||||
|
$OUTER capture-pane -p -t outer:0.0 2>/dev/null
|
||||||
|
}
|
||||||
|
|
||||||
|
wait_capture()
|
||||||
|
{
|
||||||
|
marker=$1
|
||||||
|
i=0
|
||||||
|
while [ "$i" -lt 50 ]; do
|
||||||
|
captured=$(capture)
|
||||||
|
printf '%s\n' "$captured" | grep -Fq "$marker" && return 0
|
||||||
|
sleep 0.1
|
||||||
|
i=$((i + 1))
|
||||||
|
done
|
||||||
|
fail "timed out waiting for '$marker'"
|
||||||
|
}
|
||||||
|
|
||||||
|
wait_count()
|
||||||
|
{
|
||||||
|
marker=$1
|
||||||
|
want=$2
|
||||||
|
i=0
|
||||||
|
while [ "$i" -lt 50 ]; do
|
||||||
|
captured=$(capture)
|
||||||
|
count=$(printf '%s\n' "$captured" | grep -Fc "$marker")
|
||||||
|
[ "$count" -eq "$want" ] && return 0
|
||||||
|
sleep 0.1
|
||||||
|
i=$((i + 1))
|
||||||
|
done
|
||||||
|
fail "found $count '$marker' rows, expected $want"
|
||||||
|
}
|
||||||
|
|
||||||
|
wait_format()
|
||||||
|
{
|
||||||
|
target=$1
|
||||||
|
format=$2
|
||||||
|
want=$3
|
||||||
|
i=0
|
||||||
|
while [ "$i" -lt 50 ]; do
|
||||||
|
got=$($INNER display-message -p -t "$target" "$format" \
|
||||||
|
2>/dev/null)
|
||||||
|
[ "$got" = "$want" ] && return 0
|
||||||
|
sleep 0.1
|
||||||
|
i=$((i + 1))
|
||||||
|
done
|
||||||
|
fail "$target format $format is '$got', expected '$want'"
|
||||||
|
}
|
||||||
|
|
||||||
|
wait_client_session()
|
||||||
|
{
|
||||||
|
want=$1
|
||||||
|
i=0
|
||||||
|
while [ "$i" -lt 50 ]; do
|
||||||
|
got=$($INNER list-clients -F '#{client_session}' 2>/dev/null)
|
||||||
|
[ "$got" = "$want" ] && return 0
|
||||||
|
sleep 0.1
|
||||||
|
i=$((i + 1))
|
||||||
|
done
|
||||||
|
fail "client session is '$got', expected '$want'"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Three alphabetically ordered sessions. alpha:main has two panes so -Z can be
|
||||||
|
# observed; the two pick-* windows make a deterministic two-row window filter.
|
||||||
|
$INNER new-session -d -s alpha -n main -x60 -y15 'exec sleep 100' || exit 1
|
||||||
|
$INNER split-window -d -h -t alpha:main 'exec sleep 100' || exit 1
|
||||||
|
$INNER new-window -d -t alpha: -n pick-one 'exec sleep 100' || exit 1
|
||||||
|
$INNER new-session -d -s bravo -n pick-two -x60 -y15 \
|
||||||
|
'exec sleep 100' || exit 1
|
||||||
|
$INNER new-session -d -s charlie -n last -x60 -y15 \
|
||||||
|
'exec sleep 100' || exit 1
|
||||||
|
$INNER set-option -g status off || exit 1
|
||||||
|
$INNER set-option -g window-size manual || exit 1
|
||||||
|
$INNER select-window -t alpha:main || exit 1
|
||||||
|
|
||||||
|
$OUTER new-session -d -s outer -x60 -y15 "$INNER attach -t alpha" || exit 1
|
||||||
|
$OUTER set-option -g status off || exit 1
|
||||||
|
$OUTER set-option -g window-size manual || exit 1
|
||||||
|
wait_client_session alpha
|
||||||
|
|
||||||
|
# Session rows are sorted alpha, bravo, charlie. Up from the first row wraps to
|
||||||
|
# charlie; Enter runs a custom command with the selected session target.
|
||||||
|
$INNER set-option -g @picked '' || exit 1
|
||||||
|
$INNER switch-mode -s -t alpha:main.0 -F 'SESSION #{session_name}' \
|
||||||
|
"set-option -g @picked '%%'" || exit 1
|
||||||
|
wait_count 'SESSION ' 3
|
||||||
|
$INNER send-keys -t alpha:main.0 Up Enter || exit 1
|
||||||
|
wait_format alpha:main.0 '#{@picked}' '=charlie:'
|
||||||
|
wait_format alpha:main.0 '#{pane_in_mode}' 0
|
||||||
|
|
||||||
|
# Fuzzy filtering keeps only bravo for "brv". The default command switches the
|
||||||
|
# real client to that session and closes the mode.
|
||||||
|
$INNER switch-mode -s -t alpha:main.0 -F 'SESSION #{session_name}' || exit 1
|
||||||
|
wait_count 'SESSION ' 3
|
||||||
|
$INNER send-keys -t alpha:main.0 -l brv || exit 1
|
||||||
|
wait_count 'SESSION ' 1
|
||||||
|
printf '%s\n' "$captured" | grep -Fq 'SESSION bravo' ||
|
||||||
|
fail "fuzzy session filter did not select bravo"
|
||||||
|
$INNER send-keys -t alpha:main.0 Enter || exit 1
|
||||||
|
wait_client_session bravo
|
||||||
|
client=$($INNER list-clients -F '#{client_name}')
|
||||||
|
$INNER switch-client -c "$client" -t alpha || exit 1
|
||||||
|
wait_client_session alpha
|
||||||
|
|
||||||
|
# Window mode sees pick-one then pick-two for the "pick" filter. Down selects
|
||||||
|
# the second row, and the custom command receives its window target.
|
||||||
|
$INNER set-option -g @picked '' || exit 1
|
||||||
|
$INNER switch-mode -w -t alpha:main.0 -F \
|
||||||
|
'WINDOW #{session_name}:#{window_name}' \
|
||||||
|
"set-option -g @picked '%%'" || exit 1
|
||||||
|
wait_capture 'WINDOW '
|
||||||
|
$INNER send-keys -t alpha:main.0 -l pick || exit 1
|
||||||
|
wait_count 'WINDOW ' 2
|
||||||
|
$INNER send-keys -t alpha:main.0 Down Enter || exit 1
|
||||||
|
picked=$($INNER show-option -gv @picked)
|
||||||
|
case "$picked" in
|
||||||
|
=bravo:0.) ;;
|
||||||
|
*) fail "window selection produced '$picked', expected '=bravo:0.'" ;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
# Navigation with an empty result set must be harmless, and Escape cancels
|
||||||
|
# without running the custom command.
|
||||||
|
$INNER set-option -g @picked unchanged || exit 1
|
||||||
|
$INNER switch-mode -s -t alpha:main.0 -F 'NONE #{session_name}' \
|
||||||
|
"set-option -g @picked '%%'" || exit 1
|
||||||
|
wait_capture 'NONE '
|
||||||
|
$INNER send-keys -t alpha:main.0 -l no-such-session || exit 1
|
||||||
|
wait_count 'NONE ' 0
|
||||||
|
$INNER send-keys -t alpha:main.0 Up Down PPage NPage Home End || exit 1
|
||||||
|
wait_format alpha:main.0 '#{pane_mode}' switch-mode
|
||||||
|
$INNER send-keys -t alpha:main.0 Escape || exit 1
|
||||||
|
wait_format alpha:main.0 '#{pane_in_mode}' 0
|
||||||
|
[ "$($INNER show-option -gv @picked)" = unchanged ] ||
|
||||||
|
fail "cancelled switch-mode ran its command"
|
||||||
|
|
||||||
|
# -Z temporarily zooms an unzoomed window. Resizing while the mode is open
|
||||||
|
# rebuilds and redraws it; exiting restores the original unzoomed state.
|
||||||
|
$INNER switch-mode -Zs -t alpha:main.0 -F 'RESIZE #{session_name}' || exit 1
|
||||||
|
wait_capture 'RESIZE alpha'
|
||||||
|
wait_format alpha:main.0 '#{window_zoomed_flag}' 1
|
||||||
|
$INNER resize-window -t alpha:main -x50 -y12 || exit 1
|
||||||
|
wait_format alpha:main.0 '#{window_width}x#{window_height}' 50x12
|
||||||
|
wait_format alpha:main.0 '#{pane_mode}' switch-mode
|
||||||
|
$INNER send-keys -t alpha:main.0 Escape || exit 1
|
||||||
|
wait_format alpha:main.0 '#{window_zoomed_flag}' 0
|
||||||
|
|
||||||
|
# If the window was already zoomed, leaving -Z mode must keep it zoomed.
|
||||||
|
$INNER resize-pane -Z -t alpha:main.0 || exit 1
|
||||||
|
$INNER switch-mode -Zs -t alpha:main.0 -F 'ZOOMED #{session_name}' || exit 1
|
||||||
|
wait_capture 'ZOOMED alpha'
|
||||||
|
$INNER send-keys -t alpha:main.0 Escape || exit 1
|
||||||
|
wait_format alpha:main.0 '#{window_zoomed_flag}' 1
|
||||||
|
$INNER resize-pane -Z -t alpha:main.0 || exit 1
|
||||||
|
|
||||||
|
# -k uses the shared mode teardown flag. Put it on a disposable pane and make
|
||||||
|
# sure exiting the mode removes that pane.
|
||||||
|
kill_pane=$($INNER split-window -d -P -F '#{pane_id}' -t alpha:main \
|
||||||
|
'exec sleep 100') || exit 1
|
||||||
|
$INNER switch-mode -ks -t "$kill_pane" -F 'KILL #{session_name}' || exit 1
|
||||||
|
wait_format "$kill_pane" '#{pane_mode}' switch-mode
|
||||||
|
$INNER send-keys -t "$kill_pane" Escape || exit 1
|
||||||
|
i=0
|
||||||
|
while [ "$i" -lt 50 ]; do
|
||||||
|
$INNER list-panes -s -t alpha -F '#{pane_id}' 2>/dev/null | \
|
||||||
|
grep -q -x "$kill_pane" || break
|
||||||
|
sleep 0.1
|
||||||
|
i=$((i + 1))
|
||||||
|
done
|
||||||
|
[ "$i" -lt 50 ] || fail "switch-mode -k did not kill its pane"
|
||||||
|
|
||||||
|
exit 0
|
||||||
160
regress/sync-output-cursor.sh
Normal file
160
regress/sync-output-cursor.sh
Normal file
@@ -0,0 +1,160 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
# A synchronized update elsewhere in the pane must not hide a stationary
|
||||||
|
# cursor while tmux waits for the end of the update.
|
||||||
|
|
||||||
|
PATH=/bin:/usr/bin
|
||||||
|
TERM=screen
|
||||||
|
LC_ALL=C.UTF-8
|
||||||
|
export PATH TERM LC_ALL
|
||||||
|
|
||||||
|
[ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux)
|
||||||
|
|
||||||
|
if [ "$#" -eq 0 ]; then
|
||||||
|
TEST_TMUX="$TEST_TMUX" sh "$0" pane || exit 1
|
||||||
|
TEST_TMUX="$TEST_TMUX" sh "$0" prompt || exit 1
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
case $1 in
|
||||||
|
pane|prompt) MODE=$1 ;;
|
||||||
|
*) echo "usage: $0 [pane|prompt]" >&2; exit 1 ;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
DIR=$(mktemp -d) || exit 1
|
||||||
|
TMUX_TMPDIR=$DIR
|
||||||
|
export TMUX_TMPDIR
|
||||||
|
|
||||||
|
INNER="$TEST_TMUX -Li$$ -f/dev/null"
|
||||||
|
OUTER="$TEST_TMUX -Lo$$ -f/dev/null"
|
||||||
|
CLIENT_BYTES=$DIR/client-bytes
|
||||||
|
CONTROL=$DIR/control
|
||||||
|
EMITTER=$DIR/emitter.pl
|
||||||
|
ASSERT=$DIR/assert-cursor.pl
|
||||||
|
|
||||||
|
fail()
|
||||||
|
{
|
||||||
|
echo "$*" >&2
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
cleanup()
|
||||||
|
{
|
||||||
|
$OUTER kill-server 2>/dev/null
|
||||||
|
$INNER kill-server 2>/dev/null
|
||||||
|
rm -rf "$DIR"
|
||||||
|
}
|
||||||
|
trap cleanup 0 1 15
|
||||||
|
|
||||||
|
wait_for_client()
|
||||||
|
{
|
||||||
|
i=0
|
||||||
|
while [ "$i" -lt 50 ]; do
|
||||||
|
$INNER list-clients -F '#{client_termfeatures}' 2>/dev/null |
|
||||||
|
grep -q 'sync' && return 0
|
||||||
|
sleep 0.1
|
||||||
|
i=$((i + 1))
|
||||||
|
done
|
||||||
|
fail "sync-capable client did not attach"
|
||||||
|
}
|
||||||
|
|
||||||
|
cat >"$EMITTER" <<'PERL'
|
||||||
|
use strict;
|
||||||
|
use warnings;
|
||||||
|
|
||||||
|
my $control = $ENV{CONTROL};
|
||||||
|
(my $dir = $control) =~ s{/[^/]+$}{};
|
||||||
|
|
||||||
|
# Draw a prompt and leave its cursor stationary on the last line.
|
||||||
|
syswrite STDOUT, "\e[2J\e[HWorking .\e[24;1H> ";
|
||||||
|
open my $ready, '>', "$dir/ready" or die "$dir/ready: $!\n";
|
||||||
|
close $ready;
|
||||||
|
while (!-e $control) {
|
||||||
|
select undef, undef, undef, 0.01;
|
||||||
|
}
|
||||||
|
|
||||||
|
# Deliberately split BSU, content, and ESU across pane reads. This mirrors an
|
||||||
|
# animation producer which begins a frame before it has finished rendering it.
|
||||||
|
for my $spinner (qw(| / - \\)) {
|
||||||
|
syswrite STDOUT, "\e[?2026h";
|
||||||
|
select undef, undef, undef, 0.05;
|
||||||
|
syswrite STDOUT, "\e[1;9H$spinner\e[24;3H";
|
||||||
|
select undef, undef, undef, 0.01;
|
||||||
|
syswrite STDOUT, "\e[?2026l";
|
||||||
|
select undef, undef, undef, 0.15;
|
||||||
|
}
|
||||||
|
select undef, undef, undef, 5;
|
||||||
|
PERL
|
||||||
|
|
||||||
|
cat >"$ASSERT" <<'PERL'
|
||||||
|
use strict;
|
||||||
|
use warnings;
|
||||||
|
|
||||||
|
my $path = shift;
|
||||||
|
open my $fh, '<:raw', $path or die "$path: $!\n";
|
||||||
|
local $/;
|
||||||
|
my $bytes = <$fh>;
|
||||||
|
my ($bsu, $esu, $hide, $show) =
|
||||||
|
("\e[?2026h", "\e[?2026l", "\e[?25l", "\e[?25h");
|
||||||
|
my ($sync, $outside, $hides, $shows) = (0, 0, 0, 0);
|
||||||
|
|
||||||
|
for (my $i = 0; $i < length($bytes);) {
|
||||||
|
if (substr($bytes, $i, length($bsu)) eq $bsu) {
|
||||||
|
$sync++;
|
||||||
|
$i += length($bsu);
|
||||||
|
next;
|
||||||
|
}
|
||||||
|
if (substr($bytes, $i, length($esu)) eq $esu) {
|
||||||
|
$sync-- if $sync;
|
||||||
|
$i += length($esu);
|
||||||
|
next;
|
||||||
|
}
|
||||||
|
if (substr($bytes, $i, length($hide)) eq $hide) {
|
||||||
|
$hides++;
|
||||||
|
$outside++ unless $sync;
|
||||||
|
$i += length($hide);
|
||||||
|
next;
|
||||||
|
}
|
||||||
|
if (substr($bytes, $i, length($show)) eq $show) {
|
||||||
|
$shows++;
|
||||||
|
$outside++ unless $sync;
|
||||||
|
$i += length($show);
|
||||||
|
next;
|
||||||
|
}
|
||||||
|
$i++;
|
||||||
|
}
|
||||||
|
|
||||||
|
printf "%s cursor hides: %d; cursor shows: %d; changes outside sync: %d\n",
|
||||||
|
$ENV{MODE}, $hides, $shows, $outside;
|
||||||
|
die "stationary cursor changed outside synchronized output\n" if $outside;
|
||||||
|
PERL
|
||||||
|
|
||||||
|
$INNER new-session -d -s inner -x 80 -y 24 \
|
||||||
|
"CONTROL='$CONTROL' perl '$EMITTER'" || exit 1
|
||||||
|
$INNER set-option -g status off || exit 1
|
||||||
|
$INNER set-option -g window-size manual || exit 1
|
||||||
|
$INNER set-option -as terminal-features '*:sync' || exit 1
|
||||||
|
|
||||||
|
$OUTER new-session -d -s outer -x 80 -y 24 \
|
||||||
|
"$TEST_TMUX -Li$$ -f/dev/null attach-session -t inner" || exit 1
|
||||||
|
$OUTER set-option -g status off || exit 1
|
||||||
|
$OUTER set-option -g window-size manual || exit 1
|
||||||
|
wait_for_client
|
||||||
|
|
||||||
|
if [ "$MODE" = prompt ]; then
|
||||||
|
$OUTER send-keys -t outer:0.0 C-b : || exit 1
|
||||||
|
sleep 0.2
|
||||||
|
fi
|
||||||
|
|
||||||
|
i=0
|
||||||
|
while [ "$i" -lt 50 ] && [ ! -e "$DIR/ready" ]; do
|
||||||
|
sleep 0.1
|
||||||
|
i=$((i + 1))
|
||||||
|
done
|
||||||
|
[ -e "$DIR/ready" ] || fail "application emitter did not become ready"
|
||||||
|
|
||||||
|
$OUTER pipe-pane -O -t outer:0.0 "cat >'$CLIENT_BYTES'" || exit 1
|
||||||
|
: >"$CONTROL"
|
||||||
|
sleep 2
|
||||||
|
$OUTER pipe-pane -t outer:0.0 || exit 1
|
||||||
|
|
||||||
|
MODE=$MODE perl "$ASSERT" "$CLIENT_BYTES"
|
||||||
0
regress/tab-cell-background.sh
Executable file → Normal file
0
regress/tab-cell-background.sh
Executable file → Normal file
58
regress/terminal-feature-utf8.sh
Normal file
58
regress/terminal-feature-utf8.sh
Normal file
@@ -0,0 +1,58 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
# The utf8 terminal feature must mark an attached client as UTF-8 even when
|
||||||
|
# neither its environment nor -u does so. It must also be reported by the
|
||||||
|
# client terminal-feature format lookup.
|
||||||
|
|
||||||
|
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 inner -x40 -y10 'exec sleep 100' || exit 1
|
||||||
|
$INNER set-option -g status off || exit 1
|
||||||
|
|
||||||
|
# A clean C-locale environment avoids the ordinary UTF-8 detection paths.
|
||||||
|
# -T utf8 is therefore solely responsible for setting client_utf8.
|
||||||
|
client_command="env -i PATH=/bin:/usr/bin TERM=screen LC_ALL=C $TEST_TMUX -T utf8 -LtestI$$ -f/dev/null attach-session -t inner"
|
||||||
|
$OUTER new-session -d -s outer -x40 -y10 "$client_command" || exit 1
|
||||||
|
$OUTER set-option -g status off || exit 1
|
||||||
|
|
||||||
|
i=0
|
||||||
|
while [ "$i" -lt 50 ]; do
|
||||||
|
client=$($INNER list-clients -F '#{client_name}' 2>/dev/null)
|
||||||
|
utf8=$($INNER list-clients -F '#{client_utf8}' 2>/dev/null)
|
||||||
|
[ -n "$client" ] && [ "$utf8" = 1 ] && break
|
||||||
|
sleep 0.1
|
||||||
|
i=$((i + 1))
|
||||||
|
done
|
||||||
|
[ "$i" -lt 50 ] || fail "utf8 terminal feature did not set client_utf8"
|
||||||
|
|
||||||
|
features=$($INNER list-clients -F '#{client_termfeatures}')
|
||||||
|
case ",$features," in
|
||||||
|
*,utf8,*) ;;
|
||||||
|
*) fail "utf8 is missing from client_termfeatures: $features" ;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
present=$($INNER display-message -c "$client" -p '#{I/f:utf8}')
|
||||||
|
[ "$present" = 1 ] || fail "utf8 terminal feature lookup returned '$present'"
|
||||||
|
|
||||||
|
exit 0
|
||||||
0
regress/theme-report.sh
Executable file → Normal file
0
regress/theme-report.sh
Executable file → Normal file
@@ -8,7 +8,7 @@
|
|||||||
# This exercises:
|
# This exercises:
|
||||||
# - new-window placement: next free index, explicit index, index in use with
|
# - new-window placement: next free index, explicit index, index in use with
|
||||||
# and without -k, -a (after) and -b (before) insertion with shuffling, and
|
# and without -k, -a (after) and -b (before) insertion with shuffling, and
|
||||||
# -S selecting an existing window by name instead of creating;
|
# -S selecting an existing window by name or target instead of creating;
|
||||||
# - move-window to a free index, to an occupied index with and without -k,
|
# - move-window to a free index, to an occupied index with and without -k,
|
||||||
# -a insertion and -r renumbering (including base-index);
|
# -a insertion and -r renumbering (including base-index);
|
||||||
# - renumber-windows closing gaps;
|
# - renumber-windows closing gaps;
|
||||||
@@ -140,6 +140,36 @@ check_ok new-window -S -t W: -n w3
|
|||||||
check_windows W '0:wB 1:w0 2:w1 3:wA 4:w2 5:w3 9:w9k'
|
check_windows W '0:wB 1:w0 2:w1 3:wA 4:w2 5:w3 9:w9k'
|
||||||
check_fmt 'W:' '#{window_index}:#{window_name}' '5:w3'
|
check_fmt 'W:' '#{window_index}:#{window_name}' '5:w3'
|
||||||
|
|
||||||
|
# -S with a target-window that already exists selects it directly, ignoring
|
||||||
|
# -n (with -d it does not switch).
|
||||||
|
check_ok select-window -t W:0
|
||||||
|
check_ok new-window -S -t W:9 -n ignored
|
||||||
|
check_windows W '0:wB 1:w0 2:w1 3:wA 4:w2 5:w3 9:w9k'
|
||||||
|
check_fmt 'W:' '#{window_index}:#{window_name}' '9:w9k'
|
||||||
|
check_ok select-window -t W:0
|
||||||
|
check_ok new-window -S -d -t W:9 -n ignored
|
||||||
|
check_windows W '0:wB 1:w0 2:w1 3:wA 4:w2 5:w3 9:w9k'
|
||||||
|
check_fmt 'W:' '#{window_index}:#{window_name}' '0:wB'
|
||||||
|
|
||||||
|
# -S with a target-window that does not yet exist still creates it normally.
|
||||||
|
check_ok new-window -S -t W:20 -n w20
|
||||||
|
check_windows W '0:wB 1:w0 2:w1 3:wA 4:w2 5:w3 9:w9k 20:w20'
|
||||||
|
check_fmt 'W:' '#{window_index}:#{window_name}' '20:w20'
|
||||||
|
check_ok kill-window -t W:20
|
||||||
|
|
||||||
|
# -S also works with a relative target-window (+N): selects the window at
|
||||||
|
# that offset if it exists, or creates it there if not.
|
||||||
|
check_ok select-window -t W:0
|
||||||
|
check_ok new-window -S -t W:+1
|
||||||
|
check_fmt 'W:' '#{window_index}:#{window_name}' '1:w0'
|
||||||
|
check_windows W '0:wB 1:w0 2:w1 3:wA 4:w2 5:w3 9:w9k'
|
||||||
|
|
||||||
|
check_ok select-window -t W:0
|
||||||
|
check_ok new-window -S -t W:+6 -n rel6
|
||||||
|
check_fmt 'W:' '#{window_index}:#{window_name}' '6:rel6'
|
||||||
|
check_windows W '0:wB 1:w0 2:w1 3:wA 4:w2 5:w3 6:rel6 9:w9k'
|
||||||
|
check_ok kill-window -t W:6
|
||||||
|
|
||||||
# Clean up to a known arrangement.
|
# Clean up to a known arrangement.
|
||||||
check_ok kill-window -t W:wB
|
check_ok kill-window -t W:wB
|
||||||
check_ok kill-window -t W:wA
|
check_ok kill-window -t W:wA
|
||||||
|
|||||||
4
resize.c
4
resize.c
@@ -348,10 +348,10 @@ recalculate_size_skip_client(struct client *loop, __unused int type,
|
|||||||
* is not the current window - this is used for aggressive-resize.
|
* is not the current window - this is used for aggressive-resize.
|
||||||
* Otherwise skip any session that doesn't contain the window.
|
* Otherwise skip any session that doesn't contain the window.
|
||||||
*/
|
*/
|
||||||
if (active_get_effective_window(loop, loop->session) == NULL)
|
if (loop->session->curw == NULL)
|
||||||
return (1);
|
return (1);
|
||||||
if (current)
|
if (current)
|
||||||
return (active_get_effective_window(loop, loop->session) != w);
|
return (loop->session->curw->window != w);
|
||||||
return (session_has(loop->session, w) == 0);
|
return (session_has(loop->session, w) == 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
/* $OpenBSD: screen-redraw.c,v 1.157 2026/07/24 08:49:23 nicm Exp $ */
|
/* $OpenBSD: screen-redraw.c,v 1.158 2026/09/01 19:50:58 nicm Exp $ */
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2026 Nicholas Marriott <nicholas.marriott@gmail.com>
|
* Copyright (c) 2026 Nicholas Marriott <nicholas.marriott@gmail.com>
|
||||||
@@ -295,7 +295,7 @@ static void
|
|||||||
redraw_set_context(struct client *c, struct redraw_build_ctx *bctx)
|
redraw_set_context(struct client *c, struct redraw_build_ctx *bctx)
|
||||||
{
|
{
|
||||||
struct session *s = c->session;
|
struct session *s = c->session;
|
||||||
struct window *w = active_get_effective_window(c, s);
|
struct window *w = s->curw->window;
|
||||||
|
|
||||||
memset(bctx, 0, sizeof *bctx);
|
memset(bctx, 0, sizeof *bctx);
|
||||||
bctx->c = c;
|
bctx->c = c;
|
||||||
@@ -969,7 +969,7 @@ static struct redraw_scene *
|
|||||||
redraw_make_scene(struct client *c)
|
redraw_make_scene(struct client *c)
|
||||||
{
|
{
|
||||||
struct session *s = c->session;
|
struct session *s = c->session;
|
||||||
struct window *w = active_get_effective_window(c, s);
|
struct window *w = s->curw->window;
|
||||||
struct redraw_build_ctx bctx;
|
struct redraw_build_ctx bctx;
|
||||||
struct redraw_scene *scene;
|
struct redraw_scene *scene;
|
||||||
struct redraw_build_cell *bc, *last;
|
struct redraw_build_cell *bc, *last;
|
||||||
@@ -1079,7 +1079,7 @@ static struct redraw_scene *
|
|||||||
redraw_get_scene(struct client *c)
|
redraw_get_scene(struct client *c)
|
||||||
{
|
{
|
||||||
struct redraw_scene *scene = c->redraw_scene;
|
struct redraw_scene *scene = c->redraw_scene;
|
||||||
struct window *w = active_get_effective_window(c, c->session);
|
struct window *w = c->session->curw->window;
|
||||||
const char *reason = NULL;
|
const char *reason = NULL;
|
||||||
u_int ox, oy, sx, sy;
|
u_int ox, oy, sx, sy;
|
||||||
|
|
||||||
@@ -1140,7 +1140,7 @@ redraw_get_default_border_style(struct redraw_draw_ctx *dctx,
|
|||||||
struct grid_cell *dgc = &dctx->default_gc;
|
struct grid_cell *dgc = &dctx->default_gc;
|
||||||
|
|
||||||
if (~dctx->flags & REDRAW_DEFAULT_SET) {
|
if (~dctx->flags & REDRAW_DEFAULT_SET) {
|
||||||
ft = format_create_defaults(NULL, c, s, NULL, NULL);
|
ft = format_create_defaults(NULL, c, s, s->curw, NULL);
|
||||||
memcpy(dgc, &grid_default_cell, sizeof *dgc);
|
memcpy(dgc, &grid_default_cell, sizeof *dgc);
|
||||||
style_add(dgc, oo, "pane-border-style", ft);
|
style_add(dgc, oo, "pane-border-style", ft);
|
||||||
format_free(ft);
|
format_free(ft);
|
||||||
@@ -1603,16 +1603,14 @@ redraw_set_draw_context(struct redraw_draw_ctx *dctx,
|
|||||||
struct session *s = c->session;
|
struct session *s = c->session;
|
||||||
struct options *oo = s->options;
|
struct options *oo = s->options;
|
||||||
struct tty *tty = &c->tty;
|
struct tty *tty = &c->tty;
|
||||||
struct winlink *wl;
|
|
||||||
u_int lines;
|
u_int lines;
|
||||||
|
|
||||||
memset(dctx, 0, sizeof *dctx);
|
memset(dctx, 0, sizeof *dctx);
|
||||||
dctx->scene = scene;
|
dctx->scene = scene;
|
||||||
|
|
||||||
wl = active_get_effective_winlink(c, s);
|
if (server_is_marked(s, s->curw, marked_pane.wp))
|
||||||
if (server_is_marked(s, wl, marked_pane.wp))
|
|
||||||
dctx->marked = marked_pane.wp;
|
dctx->marked = marked_pane.wp;
|
||||||
dctx->active = active_get_effective_window(c, s)->active;
|
dctx->active = s->curw->window->active;
|
||||||
|
|
||||||
lines = status_line_size(c);
|
lines = status_line_size(c);
|
||||||
if (options_get_number(oo, "status-position") == 0)
|
if (options_get_number(oo, "status-position") == 0)
|
||||||
@@ -1685,7 +1683,7 @@ redraw_draw(struct client *c, struct window_pane *wp, int flags)
|
|||||||
{
|
{
|
||||||
struct redraw_draw_ctx dctx;
|
struct redraw_draw_ctx dctx;
|
||||||
struct session *s = c->session;
|
struct session *s = c->session;
|
||||||
struct window *w = active_get_effective_window(c, s);
|
struct window *w = s->curw->window;
|
||||||
struct tty *tty = &c->tty;
|
struct tty *tty = &c->tty;
|
||||||
struct screen *sl;
|
struct screen *sl;
|
||||||
struct redraw_scene *scene;
|
struct redraw_scene *scene;
|
||||||
@@ -1740,8 +1738,7 @@ redraw_draw(struct client *c, struct window_pane *wp, int flags)
|
|||||||
else
|
else
|
||||||
loop->flags &= ~PANE_NEWSTATUS;
|
loop->flags &= ~PANE_NEWSTATUS;
|
||||||
|
|
||||||
width = redraw_pane_status_width(&dctx, loop,
|
width = redraw_pane_status_width(&dctx, loop, &first);
|
||||||
&first);
|
|
||||||
if (width == 0)
|
if (width == 0)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
@@ -1772,7 +1769,7 @@ redraw_draw(struct client *c, struct window_pane *wp, int flags)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
tty_sync_start(tty);
|
tty_sync_start(tty); /* end in server_client_reset_state */
|
||||||
tty_update_mode(tty, tty->mode & ~CURSOR_MODES, NULL);
|
tty_update_mode(tty, tty->mode & ~CURSOR_MODES, NULL);
|
||||||
|
|
||||||
if (wp != NULL)
|
if (wp != NULL)
|
||||||
@@ -1817,7 +1814,6 @@ redraw_draw(struct client *c, struct window_pane *wp, int flags)
|
|||||||
c->overlay_draw(c, c->overlay_data);
|
c->overlay_draw(c, c->overlay_data);
|
||||||
|
|
||||||
tty_reset(tty);
|
tty_reset(tty);
|
||||||
tty_sync_end(tty);
|
|
||||||
|
|
||||||
#ifdef ENABLE_SIXEL
|
#ifdef ENABLE_SIXEL
|
||||||
if (wp != NULL)
|
if (wp != NULL)
|
||||||
@@ -1885,7 +1881,7 @@ redraw_screen(struct client *c)
|
|||||||
flags |= REDRAW_OVERLAY;
|
flags |= REDRAW_OVERLAY;
|
||||||
if (c->flags & CLIENT_REDRAWMENU)
|
if (c->flags & CLIENT_REDRAWMENU)
|
||||||
flags |= REDRAW_MENU;
|
flags |= REDRAW_MENU;
|
||||||
if (active_get_effective_window(c, c->session)->menu != NULL)
|
if (c->session->curw->window->menu != NULL)
|
||||||
flags |= REDRAW_MENU;
|
flags |= REDRAW_MENU;
|
||||||
if (flags != 0)
|
if (flags != 0)
|
||||||
redraw_draw(c, NULL, flags);
|
redraw_draw(c, NULL, flags);
|
||||||
@@ -1897,7 +1893,7 @@ void
|
|||||||
redraw_pane(struct client *c, struct window_pane *wp)
|
redraw_pane(struct client *c, struct window_pane *wp)
|
||||||
{
|
{
|
||||||
redraw_draw(c, wp, REDRAW_PANE|REDRAW_PANE_SCROLLBAR);
|
redraw_draw(c, wp, REDRAW_PANE|REDRAW_PANE_SCROLLBAR);
|
||||||
if (active_get_effective_window(c, c->session)->menu != NULL)
|
if (c->session->curw->window->menu != NULL)
|
||||||
redraw_draw(c, NULL, REDRAW_MENU);
|
redraw_draw(c, NULL, REDRAW_MENU);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -142,7 +142,7 @@ screen_write_set_client_cb(struct tty_ctx *ttyctx, struct client *c)
|
|||||||
return (0);
|
return (0);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (active_get_effective_window(c, c->session) != wp->window)
|
if (c->session->curw->window != wp->window)
|
||||||
return (0);
|
return (0);
|
||||||
if (wp->layout_cell == NULL)
|
if (wp->layout_cell == NULL)
|
||||||
return (0);
|
return (0);
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
/* $OpenBSD: server-client.c,v 1.509 2026/08/28 07:36:01 nicm Exp $ */
|
/* $OpenBSD: server-client.c,v 1.510 2026/09/01 19:50:58 nicm Exp $ */
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2009 Nicholas Marriott <nicholas.marriott@gmail.com>
|
* Copyright (c) 2009 Nicholas Marriott <nicholas.marriott@gmail.com>
|
||||||
@@ -107,7 +107,7 @@ server_client_set_overlay(struct client *c, u_int delay,
|
|||||||
c->tty.flags |= TTY_FREEZE;
|
c->tty.flags |= TTY_FREEZE;
|
||||||
if (c->overlay_mode == NULL)
|
if (c->overlay_mode == NULL)
|
||||||
c->tty.flags |= TTY_NOCURSOR;
|
c->tty.flags |= TTY_NOCURSOR;
|
||||||
window_update_focus(active_get_effective_window(c, c->session));
|
window_update_focus(c->session->curw->window);
|
||||||
server_redraw_client(c);
|
server_redraw_client(c);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -134,7 +134,7 @@ server_client_clear_overlay(struct client *c)
|
|||||||
|
|
||||||
c->tty.flags &= ~(TTY_FREEZE|TTY_NOCURSOR);
|
c->tty.flags &= ~(TTY_FREEZE|TTY_NOCURSOR);
|
||||||
if (c->session != NULL)
|
if (c->session != NULL)
|
||||||
window_update_focus(active_get_effective_window(c, c->session));
|
window_update_focus(c->session->curw->window);
|
||||||
server_redraw_client(c);
|
server_redraw_client(c);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -379,8 +379,7 @@ server_client_attached_lost(struct client *c)
|
|||||||
found = NULL;
|
found = NULL;
|
||||||
TAILQ_FOREACH(loop, &clients, entry) {
|
TAILQ_FOREACH(loop, &clients, entry) {
|
||||||
s = loop->session;
|
s = loop->session;
|
||||||
if (loop == c || s == NULL ||
|
if (loop == c || s == NULL || s->curw->window != w)
|
||||||
active_get_effective_window(loop, s) != w)
|
|
||||||
continue;
|
continue;
|
||||||
if (found == NULL || timercmp(&loop->activity_time,
|
if (found == NULL || timercmp(&loop->activity_time,
|
||||||
&found->activity_time, >))
|
&found->activity_time, >))
|
||||||
@@ -461,15 +460,15 @@ server_client_set_session(struct client *c, struct session *s)
|
|||||||
c->flags |= CLIENT_FOCUSED;
|
c->flags |= CLIENT_FOCUSED;
|
||||||
|
|
||||||
if (old != NULL && old->curw != NULL)
|
if (old != NULL && old->curw != NULL)
|
||||||
window_update_focus(active_get_effective_window(c, old));
|
window_update_focus(old->curw->window);
|
||||||
if (s != NULL) {
|
if (s != NULL) {
|
||||||
active_get_effective_window(c, s)->latest = c;
|
s->curw->window->latest = c;
|
||||||
recalculate_sizes();
|
recalculate_sizes();
|
||||||
window_update_focus(active_get_effective_window(c, s));
|
window_update_focus(s->curw->window);
|
||||||
session_update_activity(s, NULL);
|
session_update_activity(s, NULL);
|
||||||
session_theme_changed(s);
|
session_theme_changed(s);
|
||||||
gettimeofday(&s->last_attached_time, NULL);
|
gettimeofday(&s->last_attached_time, NULL);
|
||||||
active_get_effective_winlink(c, s)->flags &= ~WINLINK_ALERTFLAGS;
|
s->curw->flags &= ~WINLINK_ALERTFLAGS;
|
||||||
alerts_check_session(s);
|
alerts_check_session(s);
|
||||||
tty_update_client_offset(c);
|
tty_update_client_offset(c);
|
||||||
status_timer_start(c);
|
status_timer_start(c);
|
||||||
@@ -494,7 +493,6 @@ server_client_lost(struct client *c)
|
|||||||
server_client_clear_overlay(c);
|
server_client_clear_overlay(c);
|
||||||
status_prompt_clear(c);
|
status_prompt_clear(c);
|
||||||
status_message_clear(c);
|
status_message_clear(c);
|
||||||
active_remove_client(c);
|
|
||||||
|
|
||||||
RB_FOREACH_SAFE(cf, client_files, &c->files, cf1) {
|
RB_FOREACH_SAFE(cf, client_files, &c->files, cf1) {
|
||||||
cf->error = EINTR;
|
cf->error = EINTR;
|
||||||
@@ -686,7 +684,7 @@ server_client_in_scrollbar_area(struct window_pane *wp, int px, int py)
|
|||||||
static void
|
static void
|
||||||
server_client_update_scrollbar_hover(struct client *c, int type, int px, int py)
|
server_client_update_scrollbar_hover(struct client *c, int type, int px, int py)
|
||||||
{
|
{
|
||||||
struct window *w = active_get_effective_window(c, c->session);
|
struct window *w = c->session->curw->window;
|
||||||
struct window_pane *wp;
|
struct window_pane *wp;
|
||||||
|
|
||||||
if (type != KEYC_TYPE_MOUSEMOVE)
|
if (type != KEYC_TYPE_MOUSEMOVE)
|
||||||
@@ -858,7 +856,7 @@ server_client_check_mouse(struct client *c, struct key_event *event)
|
|||||||
{
|
{
|
||||||
struct mouse_event *m = &event->m;
|
struct mouse_event *m = &event->m;
|
||||||
struct session *s = c->session, *fs;
|
struct session *s = c->session, *fs;
|
||||||
struct window *w = active_get_effective_window(c, s);
|
struct window *w = s->curw->window;
|
||||||
struct winlink *fwl;
|
struct winlink *fwl;
|
||||||
struct window_pane *wp, *fwp, *lwp = NULL;
|
struct window_pane *wp, *fwp, *lwp = NULL;
|
||||||
u_int x, y, sx, sy, px, py, n, sl_mpos = 0;
|
u_int x, y, sx, sy, px, py, n, sl_mpos = 0;
|
||||||
@@ -1361,7 +1359,7 @@ server_client_update_latest(struct client *c)
|
|||||||
|
|
||||||
if (c->session == NULL)
|
if (c->session == NULL)
|
||||||
return;
|
return;
|
||||||
w = active_get_effective_window(c, c->session);
|
w = c->session->curw->window;
|
||||||
|
|
||||||
if (w->latest == c)
|
if (w->latest == c)
|
||||||
return;
|
return;
|
||||||
@@ -1440,7 +1438,7 @@ server_client_key_callback(struct cmdq_item *item, void *data)
|
|||||||
/* Check the client is good to accept input. */
|
/* Check the client is good to accept input. */
|
||||||
if (s == NULL || (c->flags & CLIENT_UNATTACHEDFLAGS))
|
if (s == NULL || (c->flags & CLIENT_UNATTACHEDFLAGS))
|
||||||
goto out;
|
goto out;
|
||||||
wl = active_get_effective_winlink(c, s);
|
wl = s->curw;
|
||||||
|
|
||||||
/* Update the activity timer. */
|
/* Update the activity timer. */
|
||||||
memcpy(&c->last_activity_time, &c->activity_time,
|
memcpy(&c->last_activity_time, &c->activity_time,
|
||||||
@@ -1699,7 +1697,7 @@ out:
|
|||||||
static int
|
static int
|
||||||
server_client_handle_menu_key(struct client *c, struct key_event *event)
|
server_client_handle_menu_key(struct client *c, struct key_event *event)
|
||||||
{
|
{
|
||||||
struct window *w = active_get_effective_window(c, c->session);
|
struct window *w = c->session->curw->window;
|
||||||
struct key_event new_event;
|
struct key_event new_event;
|
||||||
struct mouse_event *m;
|
struct mouse_event *m;
|
||||||
u_int ox, oy, sx, sy;
|
u_int ox, oy, sx, sy;
|
||||||
@@ -1738,7 +1736,6 @@ server_client_handle_key0(struct client *c, struct key_event *event,
|
|||||||
{
|
{
|
||||||
struct session *s = c->session;
|
struct session *s = c->session;
|
||||||
struct cmdq_item *item;
|
struct cmdq_item *item;
|
||||||
struct window *w;
|
|
||||||
struct window_pane *wp;
|
struct window_pane *wp;
|
||||||
|
|
||||||
/* Check the client is good to accept input. */
|
/* Check the client is good to accept input. */
|
||||||
@@ -1809,10 +1806,9 @@ server_client_handle_key0(struct client *c, struct key_event *event,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
w = active_get_effective_window(c, s);
|
wp = s->curw->window->active;
|
||||||
wp = w->active;
|
|
||||||
if (wp == NULL || !window_pane_has_prompt(wp)) {
|
if (wp == NULL || !window_pane_has_prompt(wp)) {
|
||||||
TAILQ_FOREACH(wp, &w->panes, entry) {
|
TAILQ_FOREACH(wp, &s->curw->window->panes, entry) {
|
||||||
if (window_pane_has_prompt(wp) &&
|
if (window_pane_has_prompt(wp) &&
|
||||||
window_pane_is_visible(wp))
|
window_pane_is_visible(wp))
|
||||||
break;
|
break;
|
||||||
@@ -2154,7 +2150,7 @@ static void
|
|||||||
server_client_reset_state(struct client *c)
|
server_client_reset_state(struct client *c)
|
||||||
{
|
{
|
||||||
struct tty *tty = &c->tty;
|
struct tty *tty = &c->tty;
|
||||||
struct window *w = active_get_effective_window(c, c->session);
|
struct window *w = c->session->curw->window;
|
||||||
struct window_pane *wp = w->active, *loop;
|
struct window_pane *wp = w->active, *loop;
|
||||||
struct screen *s = NULL;
|
struct screen *s = NULL;
|
||||||
struct options *oo = c->session->options;
|
struct options *oo = c->session->options;
|
||||||
@@ -2248,7 +2244,7 @@ server_client_reset_state(struct client *c)
|
|||||||
cy += status_line_size(c);
|
cy += status_line_size(c);
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((pane_mode & MODE_SYNC) || !cursor)
|
if (!cursor)
|
||||||
mode &= ~MODE_CURSOR;
|
mode &= ~MODE_CURSOR;
|
||||||
}
|
}
|
||||||
} else if (c->overlay_mode == NULL || s == NULL)
|
} else if (c->overlay_mode == NULL || s == NULL)
|
||||||
@@ -2256,6 +2252,10 @@ server_client_reset_state(struct client *c)
|
|||||||
if (~pane_mode & MODE_SYNC) {
|
if (~pane_mode & MODE_SYNC) {
|
||||||
log_debug("%s: cursor to %u,%u", __func__, cx, cy);
|
log_debug("%s: cursor to %u,%u", __func__, cx, cy);
|
||||||
tty_cursor(tty, cx, cy);
|
tty_cursor(tty, cx, cy);
|
||||||
|
} else {
|
||||||
|
mode &= ~CURSOR_MODES;
|
||||||
|
mode |= tty->mode & CURSOR_MODES;
|
||||||
|
s = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -2434,7 +2434,7 @@ server_client_redraw_timer(__unused int fd, __unused short events,
|
|||||||
static void
|
static void
|
||||||
server_client_check_modes(struct client *c)
|
server_client_check_modes(struct client *c)
|
||||||
{
|
{
|
||||||
struct window *w;
|
struct window *w = c->session->curw->window;
|
||||||
struct window_pane *wp;
|
struct window_pane *wp;
|
||||||
struct window_mode_entry *wme;
|
struct window_mode_entry *wme;
|
||||||
|
|
||||||
@@ -2442,7 +2442,6 @@ server_client_check_modes(struct client *c)
|
|||||||
return;
|
return;
|
||||||
if (~c->flags & CLIENT_REDRAWSTATUS)
|
if (~c->flags & CLIENT_REDRAWSTATUS)
|
||||||
return;
|
return;
|
||||||
w = active_get_effective_window(c, c->session);
|
|
||||||
TAILQ_FOREACH(wp, &w->panes, entry) {
|
TAILQ_FOREACH(wp, &w->panes, entry) {
|
||||||
wme = TAILQ_FIRST(&wp->modes);
|
wme = TAILQ_FIRST(&wp->modes);
|
||||||
if (wme != NULL && wme->mode->update != NULL)
|
if (wme != NULL && wme->mode->update != NULL)
|
||||||
@@ -2455,7 +2454,7 @@ static int
|
|||||||
server_client_any_pane_redraw(struct client *c)
|
server_client_any_pane_redraw(struct client *c)
|
||||||
{
|
{
|
||||||
struct session *s = c->session;
|
struct session *s = c->session;
|
||||||
struct window *w = active_get_effective_window(c, s);
|
struct window *w = s->curw->window;
|
||||||
struct window_pane *wp;
|
struct window_pane *wp;
|
||||||
|
|
||||||
if (c->flags & CLIENT_REDRAWWINDOW)
|
if (c->flags & CLIENT_REDRAWWINDOW)
|
||||||
@@ -2473,7 +2472,7 @@ server_client_check_redraw(struct client *c)
|
|||||||
{
|
{
|
||||||
struct session *s = c->session;
|
struct session *s = c->session;
|
||||||
struct tty *tty = &c->tty;
|
struct tty *tty = &c->tty;
|
||||||
struct window *w = active_get_effective_window(c, s);
|
struct window *w = s->curw->window;
|
||||||
struct window_pane *wp;
|
struct window_pane *wp;
|
||||||
int needed, tflags, mode = tty->mode;
|
int needed, tflags, mode = tty->mode;
|
||||||
struct timeval tv = { .tv_usec = 1000 };
|
struct timeval tv = { .tv_usec = 1000 };
|
||||||
@@ -2612,15 +2611,14 @@ static void
|
|||||||
server_client_set_path(struct client *c)
|
server_client_set_path(struct client *c)
|
||||||
{
|
{
|
||||||
struct session *s = c->session;
|
struct session *s = c->session;
|
||||||
struct window *w = active_get_effective_window(c, s);
|
|
||||||
const char *path;
|
const char *path;
|
||||||
|
|
||||||
if (w == NULL || w->active == NULL)
|
if (s->curw == NULL || s->curw->window->active == NULL)
|
||||||
return;
|
return;
|
||||||
if (w->active->base.path == NULL)
|
if (s->curw->window->active->base.path == NULL)
|
||||||
path = "";
|
path = "";
|
||||||
else
|
else
|
||||||
path = w->active->base.path;
|
path = s->curw->window->active->base.path;
|
||||||
if (c->path == NULL || strcmp(path, c->path) != 0) {
|
if (c->path == NULL || strcmp(path, c->path) != 0) {
|
||||||
free(c->path);
|
free(c->path);
|
||||||
c->path = xstrdup(path);
|
c->path = xstrdup(path);
|
||||||
@@ -2633,12 +2631,11 @@ static void
|
|||||||
server_client_set_progress_bar(struct client *c)
|
server_client_set_progress_bar(struct client *c)
|
||||||
{
|
{
|
||||||
struct session *s = c->session;
|
struct session *s = c->session;
|
||||||
struct window *w = active_get_effective_window(c, s);
|
|
||||||
struct progress_bar *pane_pb;
|
struct progress_bar *pane_pb;
|
||||||
|
|
||||||
if (w == NULL || w->active == NULL)
|
if (s->curw == NULL || s->curw->window->active == NULL)
|
||||||
return;
|
return;
|
||||||
pane_pb = &w->active->base.progress_bar;
|
pane_pb = &s->curw->window->active->base.progress_bar;
|
||||||
if (pane_pb->state == c->progress_bar.state &&
|
if (pane_pb->state == c->progress_bar.state &&
|
||||||
pane_pb->progress == c->progress_bar.progress)
|
pane_pb->progress == c->progress_bar.progress)
|
||||||
return;
|
return;
|
||||||
@@ -3228,7 +3225,7 @@ server_client_print(struct client *c, int parse, struct evbuffer *evb)
|
|||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
wp = active_get_effective_window(c, c->session)->active;
|
wp = c->session->curw->window->active;
|
||||||
wme = TAILQ_FIRST(&wp->modes);
|
wme = TAILQ_FIRST(&wp->modes);
|
||||||
if (wme == NULL || wme->mode != &window_view_mode)
|
if (wme == NULL || wme->mode != &window_view_mode)
|
||||||
window_pane_set_mode(wp, NULL, &window_view_mode, NULL, NULL,
|
window_pane_set_mode(wp, NULL, &window_view_mode, NULL, NULL,
|
||||||
|
|||||||
15
server-fn.c
15
server-fn.c
@@ -1,4 +1,4 @@
|
|||||||
/* $OpenBSD: server-fn.c,v 1.151 2026/08/20 09:19:24 nicm Exp $ */
|
/* $OpenBSD: server-fn.c,v 1.152 2026/09/03 19:12:36 nicm Exp $ */
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>
|
* Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>
|
||||||
@@ -122,7 +122,8 @@ server_redraw_window(struct window *w)
|
|||||||
|
|
||||||
TAILQ_FOREACH(c, &clients, entry) {
|
TAILQ_FOREACH(c, &clients, entry) {
|
||||||
if (c->session != NULL &&
|
if (c->session != NULL &&
|
||||||
active_get_effective_window(c, c->session) == w)
|
c->session->curw != NULL &&
|
||||||
|
c->session->curw->window == w)
|
||||||
server_redraw_client(c);
|
server_redraw_client(c);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -134,7 +135,8 @@ server_redraw_window_menu(struct window *w)
|
|||||||
|
|
||||||
TAILQ_FOREACH(c, &clients, entry) {
|
TAILQ_FOREACH(c, &clients, entry) {
|
||||||
if (c->session != NULL &&
|
if (c->session != NULL &&
|
||||||
active_get_effective_window(c, c->session) == w)
|
c->session->curw != NULL &&
|
||||||
|
c->session->curw->window == w)
|
||||||
c->flags |= CLIENT_REDRAWMENU;
|
c->flags |= CLIENT_REDRAWMENU;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -146,7 +148,8 @@ server_redraw_window_borders(struct window *w)
|
|||||||
|
|
||||||
TAILQ_FOREACH(c, &clients, entry) {
|
TAILQ_FOREACH(c, &clients, entry) {
|
||||||
if (c->session != NULL &&
|
if (c->session != NULL &&
|
||||||
active_get_effective_window(c, c->session) == w)
|
c->session->curw != NULL &&
|
||||||
|
c->session->curw->window == w)
|
||||||
c->flags |= CLIENT_REDRAWBORDERS;
|
c->flags |= CLIENT_REDRAWBORDERS;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -376,9 +379,9 @@ server_destroy_pane(struct window_pane *wp, int notify)
|
|||||||
wp->pipe_fd = -1;
|
wp->pipe_fd = -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
remain_on_exit = options_get_number(wp->options, "remain-on-exit");
|
if (~wp->flags & PANE_STATUSREADY)
|
||||||
if (remain_on_exit != 0 && (~wp->flags & PANE_STATUSREADY))
|
|
||||||
return;
|
return;
|
||||||
|
remain_on_exit = options_get_number(wp->options, "remain-on-exit");
|
||||||
switch (remain_on_exit) {
|
switch (remain_on_exit) {
|
||||||
case 0:
|
case 0:
|
||||||
break;
|
break;
|
||||||
|
|||||||
@@ -203,7 +203,6 @@ session_destroy(struct session *s, int notify, const char *from)
|
|||||||
if (s->curw == NULL)
|
if (s->curw == NULL)
|
||||||
return;
|
return;
|
||||||
s->curw = NULL;
|
s->curw = NULL;
|
||||||
active_remove_session(s);
|
|
||||||
|
|
||||||
RB_REMOVE(sessions, &sessions, s);
|
RB_REMOVE(sessions, &sessions, s);
|
||||||
if (notify)
|
if (notify)
|
||||||
|
|||||||
6
spawn.c
6
spawn.c
@@ -701,7 +701,7 @@ spawn_editor_finish(struct window_pane *wp)
|
|||||||
|
|
||||||
struct spawn_editor_state *
|
struct spawn_editor_state *
|
||||||
spawn_editor(struct client *c, const char *buf, size_t len,
|
spawn_editor(struct client *c, const char *buf, size_t len,
|
||||||
spawn_finish_edit_cb cb, void *arg)
|
const char *editor, spawn_finish_edit_cb cb, void *arg)
|
||||||
{
|
{
|
||||||
struct spawn_editor_state *es;
|
struct spawn_editor_state *es;
|
||||||
struct spawn_context sc = { 0 };
|
struct spawn_context sc = { 0 };
|
||||||
@@ -715,13 +715,13 @@ spawn_editor(struct client *c, const char *buf, size_t len,
|
|||||||
FILE *f;
|
FILE *f;
|
||||||
char *cmd, *cause = NULL;
|
char *cmd, *cause = NULL;
|
||||||
char path[] = _PATH_TMP "tmux.XXXXXXXX";
|
char path[] = _PATH_TMP "tmux.XXXXXXXX";
|
||||||
const char *editor;
|
|
||||||
int fd;
|
int fd;
|
||||||
|
|
||||||
if (w->modal != NULL)
|
if (w->modal != NULL)
|
||||||
return (NULL);
|
return (NULL);
|
||||||
|
|
||||||
editor = options_get_string(global_options, "editor");
|
if (editor == NULL)
|
||||||
|
editor = options_get_string(global_options, "editor");
|
||||||
fd = mkstemp(path);
|
fd = mkstemp(path);
|
||||||
if (fd == -1)
|
if (fd == -1)
|
||||||
return (NULL);
|
return (NULL);
|
||||||
|
|||||||
214
tmux.1
214
tmux.1
@@ -1,4 +1,4 @@
|
|||||||
.\" $OpenBSD: tmux.1,v 1.1163 2026/09/01 12:49:49 nicm Exp $
|
.\" $OpenBSD: tmux.1,v 1.1164 2026/09/03 21:04:11 nicm Exp $
|
||||||
.\"
|
.\"
|
||||||
.\" Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>
|
.\" Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>
|
||||||
.\"
|
.\"
|
||||||
@@ -14,7 +14,7 @@
|
|||||||
.\" IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
|
.\" IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
|
||||||
.\" OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
.\" OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||||
.\"
|
.\"
|
||||||
.Dd $Mdocdate: September 1 2026 $
|
.Dd $Mdocdate: September 3 2026 $
|
||||||
.Dt TMUX 1
|
.Dt TMUX 1
|
||||||
.Os
|
.Os
|
||||||
.Sh NAME
|
.Sh NAME
|
||||||
@@ -2004,6 +2004,25 @@ Same as
|
|||||||
.Ic copy\-pipe
|
.Ic copy\-pipe
|
||||||
but also exit copy mode.
|
but also exit copy mode.
|
||||||
.It Xo
|
.It Xo
|
||||||
|
.Ic copy\-pipe\-output
|
||||||
|
.Op Fl aCP
|
||||||
|
.Op Ar command
|
||||||
|
.Op Ar prefix
|
||||||
|
.Xc
|
||||||
|
Copy the current OSC 133 command output, pipe it to
|
||||||
|
.Ar command ,
|
||||||
|
and clear the selection.
|
||||||
|
.Ar prefix
|
||||||
|
is used to name the new paste buffer.
|
||||||
|
.It Xo
|
||||||
|
.Ic copy\-output
|
||||||
|
.Op Fl aCP
|
||||||
|
.Op Ar prefix
|
||||||
|
.Xc
|
||||||
|
Copy the current OSC 133 command output and clear the selection.
|
||||||
|
.Ar prefix
|
||||||
|
is used to name the new paste buffer.
|
||||||
|
.It Xo
|
||||||
.Ic copy\-selection
|
.Ic copy\-selection
|
||||||
.Op Fl CP
|
.Op Fl CP
|
||||||
.Op Ar prefix
|
.Op Ar prefix
|
||||||
@@ -2169,6 +2188,27 @@ Hide line numbers in copy mode.
|
|||||||
.Xc
|
.Xc
|
||||||
Toggle line numbers in copy mode.
|
Toggle line numbers in copy mode.
|
||||||
.It Xo
|
.It Xo
|
||||||
|
.Ic collapse\-output
|
||||||
|
.Op Fl a
|
||||||
|
.Xc
|
||||||
|
Collapse the output for the command on the cursor line.
|
||||||
|
With
|
||||||
|
.Fl a ,
|
||||||
|
collapse all command output in the copy-mode buffer.
|
||||||
|
Text before the first OSC 133 prompt is treated as an initial command output
|
||||||
|
region.
|
||||||
|
.It Xo
|
||||||
|
.Ic expand\-output
|
||||||
|
.Op Fl a
|
||||||
|
.Xc
|
||||||
|
Expand the output for the command on the cursor line.
|
||||||
|
With
|
||||||
|
.Fl a ,
|
||||||
|
expand all command output in the copy-mode buffer.
|
||||||
|
.Pp
|
||||||
|
The Tab key toggles output for the command on the cursor line and S-Tab
|
||||||
|
toggles all command output.
|
||||||
|
.It Xo
|
||||||
.Ic middle\-line
|
.Ic middle\-line
|
||||||
(vi: M)
|
(vi: M)
|
||||||
(emacs: M\-r)
|
(emacs: M\-r)
|
||||||
@@ -2222,6 +2262,32 @@ but treat all non-whitespace characters as part of a word.
|
|||||||
.Xc
|
.Xc
|
||||||
Switch at which end of the selection the cursor sits.
|
Switch at which end of the selection the cursor sits.
|
||||||
.It Xo
|
.It Xo
|
||||||
|
.Ic open\-output
|
||||||
|
.Op Fl a
|
||||||
|
.Op Ar command
|
||||||
|
.Xc
|
||||||
|
Open the current OSC 133 command output in an editor.
|
||||||
|
If
|
||||||
|
.Ar command
|
||||||
|
is given, it is used as the editor command; otherwise, the
|
||||||
|
.Ic editor
|
||||||
|
option is used.
|
||||||
|
.It Xo
|
||||||
|
.Ic open\-selection
|
||||||
|
.Op Ar command
|
||||||
|
.Xc
|
||||||
|
Open the current selection in an editor.
|
||||||
|
If
|
||||||
|
.Ar command
|
||||||
|
is given, it is used as the editor command; otherwise, the
|
||||||
|
.Ic editor
|
||||||
|
option is used.
|
||||||
|
In emacs mode,
|
||||||
|
.Ql e ,
|
||||||
|
and in vi mode,
|
||||||
|
.Ql M\-e ,
|
||||||
|
opens the selection if one is present, otherwise the command output.
|
||||||
|
.It Xo
|
||||||
.Ic page\-down
|
.Ic page\-down
|
||||||
(vi: C\-f)
|
(vi: C\-f)
|
||||||
(emacs: PageDown)
|
(emacs: PageDown)
|
||||||
@@ -2262,6 +2328,14 @@ Same as
|
|||||||
.Ic pipe
|
.Ic pipe
|
||||||
but also exit copy mode.
|
but also exit copy mode.
|
||||||
.It Xo
|
.It Xo
|
||||||
|
.Ic pipe\-output
|
||||||
|
.Op Fl a
|
||||||
|
.Op Ar command
|
||||||
|
.Xc
|
||||||
|
Pipe the current OSC 133 command output to
|
||||||
|
.Ar command
|
||||||
|
and clear the selection.
|
||||||
|
.It Xo
|
||||||
.Ic previous\-matching\-bracket
|
.Ic previous\-matching\-bracket
|
||||||
(emacs: M\-C\-b)
|
(emacs: M\-C\-b)
|
||||||
.Xc
|
.Xc
|
||||||
@@ -2456,6 +2530,16 @@ backward becomes forward).
|
|||||||
.Xc
|
.Xc
|
||||||
Select the current line.
|
Select the current line.
|
||||||
.It Xo
|
.It Xo
|
||||||
|
.Ic select\-output
|
||||||
|
.Op Fl a
|
||||||
|
.Xc
|
||||||
|
Select the current OSC 133 command output.
|
||||||
|
If it is collapsed, it is expanded first.
|
||||||
|
The default emacs binding is
|
||||||
|
.Ql C\-o
|
||||||
|
and the default vi binding is
|
||||||
|
.Ql M\-o .
|
||||||
|
.It Xo
|
||||||
.Ic select\-word
|
.Ic select\-word
|
||||||
.Xc
|
.Xc
|
||||||
Select the current word.
|
Select the current word.
|
||||||
@@ -2494,6 +2578,12 @@ Toggle the visibility of the position indicator in the top right.
|
|||||||
Move to the top line.
|
Move to the top line.
|
||||||
.El
|
.El
|
||||||
.Pp
|
.Pp
|
||||||
|
The output commands use OSC 133 output text from the C marker to the D marker.
|
||||||
|
If there is no D marker, the output ends at the end of the copy-mode buffer.
|
||||||
|
With
|
||||||
|
.Fl a ,
|
||||||
|
they use the entire copy-mode buffer instead.
|
||||||
|
.Pp
|
||||||
The search commands come in several varieties:
|
The search commands come in several varieties:
|
||||||
.Ql search\-forward
|
.Ql search\-forward
|
||||||
and
|
and
|
||||||
@@ -2539,6 +2629,15 @@ Finding the beginning of command output requires the shell to emit an escape
|
|||||||
sequence (\e033]133;C\e033\e\e) to tell tmux where the output begins.
|
sequence (\e033]133;C\e033\e\e) to tell tmux where the output begins.
|
||||||
If the shell does not send these escape sequences, these commands do nothing.
|
If the shell does not send these escape sequences, these commands do nothing.
|
||||||
.Pp
|
.Pp
|
||||||
|
The output commands operate on text from an OSC 133
|
||||||
|
.Ql C
|
||||||
|
marker up to its matching
|
||||||
|
.Ql D
|
||||||
|
marker, or the current end of the buffer if there is no
|
||||||
|
.Ql D
|
||||||
|
marker.
|
||||||
|
They do nothing if the cursor is not associated with an output range.
|
||||||
|
.Pp
|
||||||
Copy commands may take an optional buffer prefix argument which is used
|
Copy commands may take an optional buffer prefix argument which is used
|
||||||
to generate the buffer name (the default is
|
to generate the buffer name (the default is
|
||||||
.Ql buffer
|
.Ql buffer
|
||||||
@@ -2601,12 +2700,19 @@ The synopsis for the
|
|||||||
command is:
|
command is:
|
||||||
.Bl -tag -width Ds
|
.Bl -tag -width Ds
|
||||||
.It Xo Ic copy\-mode
|
.It Xo Ic copy\-mode
|
||||||
.Op Fl dekHMqSu
|
.Op Fl UcdekHMqSu
|
||||||
.Op Fl s Ar src\-pane
|
.Op Fl s Ar src\-pane
|
||||||
.Op Fl t Ar target\-pane
|
.Op Fl t Ar target\-pane
|
||||||
.Xc
|
.Xc
|
||||||
Enter copy mode.
|
Enter copy mode.
|
||||||
.Pp
|
.Pp
|
||||||
|
.Fl c
|
||||||
|
enters copy mode with OSC 133 command output collapsed.
|
||||||
|
.Fl U
|
||||||
|
enters copy mode with OSC 133 output controls shown and all output expanded.
|
||||||
|
When output is collapsed, the prompt and entered command are shown and text
|
||||||
|
from the output start marker to the next primary prompt marker is hidden.
|
||||||
|
.Pp
|
||||||
.Fl u
|
.Fl u
|
||||||
enters copy mode and scrolls one page up and
|
enters copy mode and scrolls one page up and
|
||||||
.Fl d
|
.Fl d
|
||||||
@@ -2647,6 +2753,60 @@ bind PageDown copy\-mode \-ed
|
|||||||
kills the pane when the mode is exited.
|
kills the pane when the mode is exited.
|
||||||
.El
|
.El
|
||||||
.Pp
|
.Pp
|
||||||
|
The following commands operate on OSC 133 command output. In copy mode, they
|
||||||
|
use the output on the cursor line; otherwise, they use the most recent output
|
||||||
|
in the target pane. If an output has a C marker but no D marker, it ends at the
|
||||||
|
end of the pane buffer. If there is no OSC 133 output, they do nothing unless
|
||||||
|
.Fl a
|
||||||
|
is given, which uses the entire buffer.
|
||||||
|
.Bl -tag -width Ds
|
||||||
|
.It Xo Ic select\-output
|
||||||
|
.Op Fl a
|
||||||
|
.Op Fl t Ar target\-pane
|
||||||
|
.Xc
|
||||||
|
Enter copy mode if necessary and select the output.
|
||||||
|
.It Xo Ic copy\-output
|
||||||
|
.Op Fl aCP
|
||||||
|
.Op Fl t Ar target\-pane
|
||||||
|
.Op Ar prefix
|
||||||
|
.Xc
|
||||||
|
Copy the output to a paste buffer.
|
||||||
|
.It Xo Ic copy\-pipe\-output
|
||||||
|
.Op Fl aCP
|
||||||
|
.Op Fl t Ar target\-pane
|
||||||
|
.Op Ar command
|
||||||
|
.Op Ar prefix
|
||||||
|
.Xc
|
||||||
|
Copy the output to a paste buffer and pipe it to
|
||||||
|
.Ar command .
|
||||||
|
.It Xo Ic pipe\-output
|
||||||
|
.Op Fl a
|
||||||
|
.Op Fl t Ar target\-pane
|
||||||
|
.Op Ar command
|
||||||
|
.Xc
|
||||||
|
Pipe the output to
|
||||||
|
.Ar command .
|
||||||
|
.It Xo Ic open\-output
|
||||||
|
.Op Fl a
|
||||||
|
.Op Fl t Ar target\-pane
|
||||||
|
.Op Ar command
|
||||||
|
.Xc
|
||||||
|
Open the output in an editor. If
|
||||||
|
.Ar command
|
||||||
|
is given, it is used as the editor command; otherwise the
|
||||||
|
.Ic editor
|
||||||
|
option is used.
|
||||||
|
.It Xo Ic open\-selection
|
||||||
|
.Op Fl t Ar target\-pane
|
||||||
|
.Op Ar command
|
||||||
|
.Xc
|
||||||
|
Open the current copy-mode selection in an editor. If
|
||||||
|
.Ar command
|
||||||
|
is given, it is used as the editor command; otherwise the
|
||||||
|
.Ic editor
|
||||||
|
option is used.
|
||||||
|
.El
|
||||||
|
.Pp
|
||||||
A number of preset arrangements of panes are available, these are called
|
A number of preset arrangements of panes are available, these are called
|
||||||
layouts.
|
layouts.
|
||||||
These may be selected with the
|
These may be selected with the
|
||||||
@@ -3590,13 +3750,20 @@ represents the window to be created; if the target already exists an error is
|
|||||||
shown, unless the
|
shown, unless the
|
||||||
.Fl k
|
.Fl k
|
||||||
flag is used, in which case it is destroyed.
|
flag is used, in which case it is destroyed.
|
||||||
|
.Ar window\-name
|
||||||
|
is the name given to the new window, if any.
|
||||||
|
.Pp
|
||||||
If
|
If
|
||||||
.Fl S
|
.Fl S
|
||||||
is given and a window named
|
is given, an existing window is selected instead of creating a new one
|
||||||
.Ar window\-name
|
(unless
|
||||||
already exists, it is selected (unless
|
|
||||||
.Fl d
|
.Fl d
|
||||||
is also given in which case the command does nothing).
|
is also given in which case the command does nothing):
|
||||||
|
.Ar target\-window
|
||||||
|
is preferred if it already identifies an existing window; otherwise a
|
||||||
|
window named
|
||||||
|
.Ar window\-name
|
||||||
|
is selected if one exists.
|
||||||
.Pp
|
.Pp
|
||||||
.Ar shell\-command
|
.Ar shell\-command
|
||||||
is the command to execute.
|
is the command to execute.
|
||||||
@@ -4038,22 +4205,12 @@ and
|
|||||||
.Ic swap\-window .
|
.Ic swap\-window .
|
||||||
.Tg selectw
|
.Tg selectw
|
||||||
.It Xo Ic select\-window
|
.It Xo Ic select\-window
|
||||||
.Op Fl LlnpST
|
.Op Fl lnpT
|
||||||
.Op Fl t Ar target\-window
|
.Op Fl t Ar target\-window
|
||||||
.Xc
|
.Xc
|
||||||
.D1 Pq alias: Ic selectw
|
.D1 Pq alias: Ic selectw
|
||||||
Select the window at
|
Select the window at
|
||||||
.Ar target\-window .
|
.Ar target\-window .
|
||||||
.Fl L
|
|
||||||
makes the attached client use a local current window for the target session.
|
|
||||||
Window navigation from that client then changes only that client's effective
|
|
||||||
window and leaves the session current window unchanged.
|
|
||||||
.Fl S
|
|
||||||
returns the client to the shared session current window.
|
|
||||||
Without
|
|
||||||
.Fl L ,
|
|
||||||
the current window is shared by the session and stored in the session.
|
|
||||||
.Pp
|
|
||||||
.Fl l ,
|
.Fl l ,
|
||||||
.Fl n
|
.Fl n
|
||||||
and
|
and
|
||||||
@@ -5801,6 +5958,20 @@ section.
|
|||||||
.It Ic copy\-mode\-position\-format Ar format
|
.It Ic copy\-mode\-position\-format Ar format
|
||||||
Format of the position indicator in copy mode.
|
Format of the position indicator in copy mode.
|
||||||
.Pp
|
.Pp
|
||||||
|
.It Ic copy\-mode\-exit\-status\-format Ar format
|
||||||
|
Format of the OSC 133 exit status indicator in the copy-mode gutter.
|
||||||
|
The default displays a theme red
|
||||||
|
.Ql !
|
||||||
|
for a nonzero status and a space otherwise.
|
||||||
|
The gutter grows to fit the expanded format.
|
||||||
|
The format is drawn to the left of the collapse control, and its alignment
|
||||||
|
applies within that area.
|
||||||
|
The format is expanded with
|
||||||
|
.Ql exit_status
|
||||||
|
and
|
||||||
|
.Ql exit_status_present ,
|
||||||
|
which is set only when the OSC 133 end-of-output marker includes a status.
|
||||||
|
.Pp
|
||||||
.It Ic copy\-mode\-position\-style Ar style
|
.It Ic copy\-mode\-position\-style Ar style
|
||||||
Set the style of the position indicator in copy mode.
|
Set the style of the position indicator in copy mode.
|
||||||
For how to specify
|
For how to specify
|
||||||
@@ -7276,10 +7447,7 @@ shows the four frames in turn, changing every 200 milliseconds.
|
|||||||
The following variables are available, where appropriate:
|
The following variables are available, where appropriate:
|
||||||
.Bl -column "XXXXXXXXXXXXXXXXXXX" "XXXXX"
|
.Bl -column "XXXXXXXXXXXXXXXXXXX" "XXXXX"
|
||||||
.It Sy "Variable name" Ta Sy "Alias" Ta Sy "Replaced with"
|
.It Sy "Variable name" Ta Sy "Alias" Ta Sy "Replaced with"
|
||||||
.It Li "active_window_id" Ta "" Ta "ID of effective active window"
|
.It Li "active_window_index" Ta "" Ta "Index of active window in session"
|
||||||
.It Li "active_window_index" Ta "" Ta "Index of effective active window"
|
|
||||||
.It Li "local_active_window" Ta "" Ta "1 if client has local window selection"
|
|
||||||
.It Li "active_window_mode" Ta "" Ta "Window selection mode, shared or local"
|
|
||||||
.It Li "alternate_on" Ta "" Ta "1 if pane is in alternate screen"
|
.It Li "alternate_on" Ta "" Ta "1 if pane is in alternate screen"
|
||||||
.It Li "alternate_saved_x" Ta "" Ta "Saved cursor X in alternate screen"
|
.It Li "alternate_saved_x" Ta "" Ta "Saved cursor X in alternate screen"
|
||||||
.It Li "alternate_saved_y" Ta "" Ta "Saved cursor Y in alternate screen"
|
.It Li "alternate_saved_y" Ta "" Ta "Saved cursor Y in alternate screen"
|
||||||
@@ -7516,7 +7684,7 @@ The following variables are available, where appropriate:
|
|||||||
.It Li "uid" Ta "" Ta "Server UID"
|
.It Li "uid" Ta "" Ta "Server UID"
|
||||||
.It Li "user" Ta "" Ta "Server user"
|
.It Li "user" Ta "" Ta "Server user"
|
||||||
.It Li "version" Ta "" Ta "Server version"
|
.It Li "version" Ta "" Ta "Server version"
|
||||||
.It Li "window_active" Ta "" Ta "1 if window is the effective active window"
|
.It Li "window_active" Ta "" Ta "1 if window active"
|
||||||
.It Li "window_active_clients" Ta "" Ta "Number of clients viewing this window"
|
.It Li "window_active_clients" Ta "" Ta "Number of clients viewing this window"
|
||||||
.It Li "window_active_clients_list" Ta "" Ta "List of clients viewing this window"
|
.It Li "window_active_clients_list" Ta "" Ta "List of clients viewing this window"
|
||||||
.It Li "window_active_sessions" Ta "" Ta "Number of sessions on which this window is active"
|
.It Li "window_active_sessions" Ta "" Ta "Number of sessions on which this window is active"
|
||||||
|
|||||||
34
tmux.h
34
tmux.h
@@ -815,6 +815,7 @@ struct colour_palette {
|
|||||||
#define GRID_LINE_START_OUTPUT 0x40
|
#define GRID_LINE_START_OUTPUT 0x40
|
||||||
#define GRID_LINE_END_OUTPUT 0x80
|
#define GRID_LINE_END_OUTPUT 0x80
|
||||||
#define GRID_LINE_HYPERLINK 0x100
|
#define GRID_LINE_HYPERLINK 0x100
|
||||||
|
#define GRID_LINE_END_OUTPUT_STATUS 0x200
|
||||||
|
|
||||||
/* All OSC 133 flags. */
|
/* All OSC 133 flags. */
|
||||||
#define GRID_LINE_OSC133_FLAGS \
|
#define GRID_LINE_OSC133_FLAGS \
|
||||||
@@ -822,7 +823,8 @@ struct colour_palette {
|
|||||||
GRID_LINE_SECOND_PROMPT| \
|
GRID_LINE_SECOND_PROMPT| \
|
||||||
GRID_LINE_START_COMMAND| \
|
GRID_LINE_START_COMMAND| \
|
||||||
GRID_LINE_START_OUTPUT| \
|
GRID_LINE_START_OUTPUT| \
|
||||||
GRID_LINE_END_OUTPUT)
|
GRID_LINE_END_OUTPUT| \
|
||||||
|
GRID_LINE_END_OUTPUT_STATUS)
|
||||||
|
|
||||||
/* Grid string flags. */
|
/* Grid string flags. */
|
||||||
#define GRID_STRING_WITH_SEQUENCES 0x1
|
#define GRID_STRING_WITH_SEQUENCES 0x1
|
||||||
@@ -2777,32 +2779,6 @@ void events_fire_window(const char *, struct window *);
|
|||||||
void events_fire_pane(const char *, struct window_pane *);
|
void events_fire_pane(const char *, struct window_pane *);
|
||||||
void events_fire_winlink(const char *, struct winlink *);
|
void events_fire_winlink(const char *, struct winlink *);
|
||||||
|
|
||||||
/* active.c */
|
|
||||||
enum active_window_mode {
|
|
||||||
ACTIVE_SHARED,
|
|
||||||
ACTIVE_LOCAL
|
|
||||||
};
|
|
||||||
int active_is_local_window(struct client *, struct session *);
|
|
||||||
void active_set_local_window(struct client *, struct session *,
|
|
||||||
enum active_window_mode);
|
|
||||||
struct winlink *active_get_effective_winlink(struct client *,
|
|
||||||
struct session *);
|
|
||||||
struct window *active_get_effective_window(struct client *,
|
|
||||||
struct session *);
|
|
||||||
int active_select_window(struct client *, struct session *,
|
|
||||||
struct winlink *);
|
|
||||||
int active_select_window_index(struct client *, struct session *, int);
|
|
||||||
int active_next_window(struct client *, struct session *, int);
|
|
||||||
int active_previous_window(struct client *, struct session *, int);
|
|
||||||
int active_last_window(struct client *, struct session *);
|
|
||||||
void active_remove_client(struct client *);
|
|
||||||
void active_remove_session(struct session *);
|
|
||||||
void active_remove_window(struct window *);
|
|
||||||
int active_is_effective_window(struct client *, struct session *,
|
|
||||||
struct winlink *);
|
|
||||||
int active_is_last_window(struct client *, struct session *,
|
|
||||||
struct winlink *);
|
|
||||||
|
|
||||||
/* format-draw.c */
|
/* format-draw.c */
|
||||||
void format_draw(struct screen_write_ctx *, const struct grid_cell *,
|
void format_draw(struct screen_write_ctx *, const struct grid_cell *,
|
||||||
u_int, const char *, struct style_ranges *, int);
|
u_int, const char *, struct style_ranges *, int);
|
||||||
@@ -3807,7 +3783,7 @@ int window_pane_is_visible(struct window_pane *);
|
|||||||
int window_pane_exited(struct window_pane *);
|
int window_pane_exited(struct window_pane *);
|
||||||
u_int window_pane_search(struct window_pane *, const char *, int,
|
u_int window_pane_search(struct window_pane *, const char *, int,
|
||||||
int);
|
int);
|
||||||
const char *window_printable_flags(struct client *, struct winlink *, int);
|
const char *window_printable_flags(struct winlink *, int);
|
||||||
const char *window_pane_printable_flags(struct window_pane *);
|
const char *window_pane_printable_flags(struct window_pane *);
|
||||||
struct window_pane *window_pane_find_up(struct window_pane *);
|
struct window_pane *window_pane_find_up(struct window_pane *);
|
||||||
struct window_pane *window_pane_find_down(struct window_pane *);
|
struct window_pane *window_pane_find_down(struct window_pane *);
|
||||||
@@ -4247,7 +4223,7 @@ struct winlink *spawn_window(struct spawn_context *, char **);
|
|||||||
struct window_pane *spawn_pane(struct spawn_context *, char **);
|
struct window_pane *spawn_pane(struct spawn_context *, char **);
|
||||||
typedef void (*spawn_finish_edit_cb)(char *, size_t, void *);
|
typedef void (*spawn_finish_edit_cb)(char *, size_t, void *);
|
||||||
struct spawn_editor_state *spawn_editor(struct client *, const char *, size_t,
|
struct spawn_editor_state *spawn_editor(struct client *, const char *, size_t,
|
||||||
spawn_finish_edit_cb, void *);
|
const char *, spawn_finish_edit_cb, void *);
|
||||||
void spawn_cancel_editor(struct spawn_editor_state *);
|
void spawn_cancel_editor(struct spawn_editor_state *);
|
||||||
pid_t spawn_get_editor_pid(struct spawn_editor_state *);
|
pid_t spawn_get_editor_pid(struct spawn_editor_state *);
|
||||||
void spawn_editor_finish(struct window_pane *);
|
void spawn_editor_finish(struct window_pane *);
|
||||||
|
|||||||
@@ -1020,12 +1020,12 @@ complete_key:
|
|||||||
/* Check for focus events. */
|
/* Check for focus events. */
|
||||||
if (key == KEYC_FOCUS_OUT) {
|
if (key == KEYC_FOCUS_OUT) {
|
||||||
c->flags &= ~CLIENT_FOCUSED;
|
c->flags &= ~CLIENT_FOCUSED;
|
||||||
window_update_focus(active_get_effective_window(c, c->session));
|
window_update_focus(c->session->curw->window);
|
||||||
events_fire_client("client-focus-out", c);
|
events_fire_client("client-focus-out", c);
|
||||||
} else if (key == KEYC_FOCUS_IN) {
|
} else if (key == KEYC_FOCUS_IN) {
|
||||||
c->flags |= CLIENT_FOCUSED;
|
c->flags |= CLIENT_FOCUSED;
|
||||||
events_fire_client("client-focus-in", c);
|
events_fire_client("client-focus-in", c);
|
||||||
window_update_focus(active_get_effective_window(c, c->session));
|
window_update_focus(c->session->curw->window);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Fire the key. */
|
/* Fire the key. */
|
||||||
|
|||||||
9
tty.c
9
tty.c
@@ -955,7 +955,7 @@ int
|
|||||||
tty_window_bigger(struct tty *tty)
|
tty_window_bigger(struct tty *tty)
|
||||||
{
|
{
|
||||||
struct client *c = tty->client;
|
struct client *c = tty->client;
|
||||||
struct window *w = active_get_effective_window(c, c->session);
|
struct window *w = c->session->curw->window;
|
||||||
|
|
||||||
return (tty->sx < w->sx || tty->sy - status_line_size(c) < w->sy);
|
return (tty->sx < w->sx || tty->sy - status_line_size(c) < w->sy);
|
||||||
}
|
}
|
||||||
@@ -977,7 +977,7 @@ static int
|
|||||||
tty_window_offset1(struct tty *tty, u_int *ox, u_int *oy, u_int *sx, u_int *sy)
|
tty_window_offset1(struct tty *tty, u_int *ox, u_int *oy, u_int *sx, u_int *sy)
|
||||||
{
|
{
|
||||||
struct client *c = tty->client;
|
struct client *c = tty->client;
|
||||||
struct window *w = active_get_effective_window(c, c->session);
|
struct window *w = c->session->curw->window;
|
||||||
struct window_pane *wp = w->active;
|
struct window_pane *wp = w->active;
|
||||||
u_int cx, cy, lines;
|
u_int cx, cy, lines;
|
||||||
|
|
||||||
@@ -1044,7 +1044,8 @@ tty_update_window_offset(struct window *w)
|
|||||||
|
|
||||||
TAILQ_FOREACH(c, &clients, entry) {
|
TAILQ_FOREACH(c, &clients, entry) {
|
||||||
if (c->session != NULL &&
|
if (c->session != NULL &&
|
||||||
active_get_effective_window(c, c->session) == w)
|
c->session->curw != NULL &&
|
||||||
|
c->session->curw->window == w)
|
||||||
tty_update_client_offset(c);
|
tty_update_client_offset(c);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1546,7 +1547,7 @@ tty_set_client_cb(struct tty_ctx *ttyctx, struct client *c)
|
|||||||
{
|
{
|
||||||
struct window_pane *wp = ttyctx->arg;
|
struct window_pane *wp = ttyctx->arg;
|
||||||
|
|
||||||
if (active_get_effective_window(c, c->session) != wp->window)
|
if (c->session->curw->window != wp->window)
|
||||||
return (0);
|
return (0);
|
||||||
if (wp->layout_cell == NULL)
|
if (wp->layout_cell == NULL)
|
||||||
return (0);
|
return (0);
|
||||||
|
|||||||
@@ -157,7 +157,7 @@ window_pane_get_border_style(struct window_pane *wp, struct client *c,
|
|||||||
struct grid_cell *saved;
|
struct grid_cell *saved;
|
||||||
int *flag;
|
int *flag;
|
||||||
|
|
||||||
if (wp == active_get_effective_window(c, c->session)->active) {
|
if (wp == c->session->curw->window->active) {
|
||||||
flag = &wp->active_border_gc_set;
|
flag = &wp->active_border_gc_set;
|
||||||
saved = &wp->active_border_gc;
|
saved = &wp->active_border_gc;
|
||||||
option = "pane-active-border-style";
|
option = "pane-active-border-style";
|
||||||
@@ -168,7 +168,7 @@ window_pane_get_border_style(struct window_pane *wp, struct client *c,
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!*flag) {
|
if (!*flag) {
|
||||||
ft = format_create_defaults(NULL, c, s, NULL, wp);
|
ft = format_create_defaults(NULL, c, s, s->curw, wp);
|
||||||
style_apply(saved, wp->options, option, ft);
|
style_apply(saved, wp->options, option, ft);
|
||||||
format_free(ft);
|
format_free(ft);
|
||||||
*flag = 1;
|
*flag = 1;
|
||||||
@@ -197,7 +197,7 @@ window_make_pane_status(struct window_pane *wp, struct client *c, u_int width,
|
|||||||
return (0);
|
return (0);
|
||||||
|
|
||||||
ft = format_create(c, NULL, FORMAT_PANE|wp->id, FORMAT_STATUS);
|
ft = format_create(c, NULL, FORMAT_PANE|wp->id, FORMAT_STATUS);
|
||||||
format_defaults(ft, c, c->session, NULL, wp);
|
format_defaults(ft, c, c->session, c->session->curw, wp);
|
||||||
|
|
||||||
fmt = options_get_string(wp->options, "pane-border-format");
|
fmt = options_get_string(wp->options, "pane-border-format");
|
||||||
expanded = format_expand_time(ft, fmt);
|
expanded = format_expand_time(ft, fmt);
|
||||||
|
|||||||
@@ -606,7 +606,8 @@ window_buffer_start_edit(struct window_buffer_modedata *data,
|
|||||||
ed->name = xstrdup(paste_buffer_name(pb));
|
ed->name = xstrdup(paste_buffer_name(pb));
|
||||||
ed->pb = pb;
|
ed->pb = pb;
|
||||||
|
|
||||||
ed->editor = spawn_editor(c, buf, len, window_buffer_edit_close_cb, ed);
|
ed->editor = spawn_editor(c, buf, len, NULL,
|
||||||
|
window_buffer_edit_close_cb, ed);
|
||||||
if (ed->editor == NULL)
|
if (ed->editor == NULL)
|
||||||
window_buffer_finish_edit(ed);
|
window_buffer_finish_edit(ed);
|
||||||
else {
|
else {
|
||||||
|
|||||||
@@ -269,13 +269,12 @@ window_client_draw_info(__unused void *modedata, void *itemdata,
|
|||||||
struct window_client_itemdata *item = itemdata;
|
struct window_client_itemdata *item = itemdata;
|
||||||
struct client *c = item->c;
|
struct client *c = item->c;
|
||||||
struct screen *s = ctx->s;
|
struct screen *s = ctx->s;
|
||||||
struct window *w;
|
struct window *w = c->session->curw->window;
|
||||||
struct grid_cell gc;
|
struct grid_cell gc;
|
||||||
u_int cx = s->cx, cy = s->cy, i;
|
u_int cx = s->cx, cy = s->cy, i;
|
||||||
struct format_tree *ft;
|
struct format_tree *ft;
|
||||||
char *expanded;
|
char *expanded;
|
||||||
|
|
||||||
w = active_get_effective_window(c, c->session);
|
|
||||||
ft = format_create_defaults(NULL, c, NULL, NULL, NULL);
|
ft = format_create_defaults(NULL, c, NULL, NULL, NULL);
|
||||||
if (c->tty.term->flags & TERM_INVALIDMS)
|
if (c->tty.term->flags & TERM_INVALIDMS)
|
||||||
format_add(ft, "clipboard_invalid", "1");
|
format_add(ft, "clipboard_invalid", "1");
|
||||||
|
|||||||
1584
window-copy.c
1584
window-copy.c
File diff suppressed because it is too large
Load Diff
@@ -2104,8 +2104,8 @@ window_customize_start_edit(struct window_customize_modedata *data,
|
|||||||
buf = "\n";
|
buf = "\n";
|
||||||
len = 1;
|
len = 1;
|
||||||
}
|
}
|
||||||
ed->editor = spawn_editor(c, buf, len, window_customize_edit_close_cb,
|
ed->editor = spawn_editor(c, buf, len, NULL,
|
||||||
ed);
|
window_customize_edit_close_cb, ed);
|
||||||
free(value);
|
free(value);
|
||||||
if (ed->editor == NULL)
|
if (ed->editor == NULL)
|
||||||
window_customize_finish_edit(ed);
|
window_customize_finish_edit(ed);
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
/* $OpenBSD: window-switch.c,v 1.3 2026/08/25 07:23:30 nicm Exp $ */
|
/* $OpenBSD: window-switch.c,v 1.4 2026/09/01 21:03:55 nicm Exp $ */
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2026 Nicholas Marriott <nicholas.marriott@gmail.com>
|
* Copyright (c) 2026 Nicholas Marriott <nicholas.marriott@gmail.com>
|
||||||
@@ -374,6 +374,9 @@ window_switch_init(struct window_mode_entry *wme,
|
|||||||
data->prompt = prompt_create(&pd);
|
data->prompt = prompt_create(&pd);
|
||||||
prompt_update(data->prompt, "(search) ", data->filter);
|
prompt_update(data->prompt, "(search) ", data->filter);
|
||||||
|
|
||||||
|
s = &data->screen;
|
||||||
|
screen_init(s, screen_size_x(&wp->base), screen_size_y(&wp->base), 0);
|
||||||
|
|
||||||
if (!args_has(args, 'Z'))
|
if (!args_has(args, 'Z'))
|
||||||
data->zoomed = -1;
|
data->zoomed = -1;
|
||||||
else {
|
else {
|
||||||
@@ -382,9 +385,6 @@ window_switch_init(struct window_mode_entry *wme,
|
|||||||
server_redraw_window(wp->window);
|
server_redraw_window(wp->window);
|
||||||
}
|
}
|
||||||
|
|
||||||
s = &data->screen;
|
|
||||||
screen_init(s, screen_size_x(&wp->base), screen_size_y(&wp->base), 0);
|
|
||||||
|
|
||||||
window_switch_build(data);
|
window_switch_build(data);
|
||||||
prompt_incremental_start(data->prompt);
|
prompt_incremental_start(data->prompt);
|
||||||
window_switch_draw_screen(wme);
|
window_switch_draw_screen(wme);
|
||||||
|
|||||||
16
window.c
16
window.c
@@ -451,7 +451,6 @@ window_destroy(struct window *w)
|
|||||||
{
|
{
|
||||||
log_debug("window @%u destroyed (%d references)", w->id, w->references);
|
log_debug("window @%u destroyed (%d references)", w->id, w->references);
|
||||||
|
|
||||||
active_remove_window(w);
|
|
||||||
window_unzoom(w, 0);
|
window_unzoom(w, 0);
|
||||||
RB_REMOVE(windows, &windows, w);
|
RB_REMOVE(windows, &windows, w);
|
||||||
|
|
||||||
@@ -673,7 +672,6 @@ void
|
|||||||
window_pane_update_focus(struct window_pane *wp)
|
window_pane_update_focus(struct window_pane *wp)
|
||||||
{
|
{
|
||||||
struct client *c;
|
struct client *c;
|
||||||
struct window *w;
|
|
||||||
int focused = 0;
|
int focused = 0;
|
||||||
|
|
||||||
if (wp != NULL && (~wp->flags & PANE_EXITED)) {
|
if (wp != NULL && (~wp->flags & PANE_EXITED)) {
|
||||||
@@ -681,12 +679,10 @@ window_pane_update_focus(struct window_pane *wp)
|
|||||||
focused = 0;
|
focused = 0;
|
||||||
else {
|
else {
|
||||||
TAILQ_FOREACH(c, &clients, entry) {
|
TAILQ_FOREACH(c, &clients, entry) {
|
||||||
if (c->session == NULL)
|
if (c->session != NULL &&
|
||||||
continue;
|
c->session->attached != 0 &&
|
||||||
w = active_get_effective_window(c, c->session);
|
|
||||||
if (c->session->attached != 0 &&
|
|
||||||
(c->flags & CLIENT_FOCUSED) &&
|
(c->flags & CLIENT_FOCUSED) &&
|
||||||
w == wp->window &&
|
c->session->curw->window == wp->window &&
|
||||||
c->overlay_draw == NULL &&
|
c->overlay_draw == NULL &&
|
||||||
wp->window->menu == NULL) {
|
wp->window->menu == NULL) {
|
||||||
focused = 1;
|
focused = 1;
|
||||||
@@ -1291,7 +1287,7 @@ window_destroy_panes(struct window *w)
|
|||||||
}
|
}
|
||||||
|
|
||||||
const char *
|
const char *
|
||||||
window_printable_flags(struct client *c, struct winlink *wl, int escape)
|
window_printable_flags(struct winlink *wl, int escape)
|
||||||
{
|
{
|
||||||
struct session *s = wl->session;
|
struct session *s = wl->session;
|
||||||
static char flags[32];
|
static char flags[32];
|
||||||
@@ -1306,9 +1302,9 @@ window_printable_flags(struct client *c, struct winlink *wl, int escape)
|
|||||||
flags[pos++] = '!';
|
flags[pos++] = '!';
|
||||||
if (wl->flags & WINLINK_SILENCE)
|
if (wl->flags & WINLINK_SILENCE)
|
||||||
flags[pos++] = '~';
|
flags[pos++] = '~';
|
||||||
if (active_is_effective_window(c, s, wl))
|
if (wl == s->curw)
|
||||||
flags[pos++] = '*';
|
flags[pos++] = '*';
|
||||||
if (active_is_last_window(c, s, wl))
|
if (wl == TAILQ_FIRST(&s->lastw))
|
||||||
flags[pos++] = '-';
|
flags[pos++] = '-';
|
||||||
if (server_check_marked() && wl == marked_pane.wl)
|
if (server_check_marked() && wl == marked_pane.wl)
|
||||||
flags[pos++] = 'M';
|
flags[pos++] = 'M';
|
||||||
|
|||||||
Reference in New Issue
Block a user