mirror of
https://github.com/tmux/tmux.git
synced 2026-09-25 14:08:23 +00:00
Compare commits
208
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
17d32ae40d | ||
|
|
e4129d92c2 | ||
|
|
6a5de0d8e3 | ||
|
|
ff7efbe37c | ||
|
|
ff7d45ad05 | ||
|
|
15a7944576 | ||
|
|
de69122034 | ||
|
|
f38558ee12 | ||
|
|
38c7eb7413 | ||
|
|
ad785736e9 | ||
|
|
61687f2a5b | ||
|
|
ed2b349c3e | ||
|
|
92d29e54f4 | ||
|
|
a34d2b6b21 | ||
|
|
550231e83a | ||
|
|
102d5185c1 | ||
|
|
1d9029c8d7 | ||
|
|
a23ff880a2 | ||
|
|
32dc7d5095 | ||
|
|
06ef15955c | ||
|
|
ce9bdf6734 | ||
|
|
757b15f2ca | ||
|
|
fcb4e9760a | ||
|
|
03436d273a | ||
|
|
7fdb1c90b7 | ||
|
|
55012ded8d | ||
|
|
de04ae2c65 | ||
|
|
3b931d9c20 | ||
|
|
6f387eec2b | ||
|
|
80f522f577 | ||
|
|
5aeacf1cae | ||
|
|
7774731b95 | ||
|
|
0544f0b210 | ||
|
|
a3129249d1 | ||
|
|
930c81b819 | ||
|
|
fc695b4e86 | ||
|
|
36fb7ab084 | ||
|
|
90a7dd4654 | ||
|
|
bf43fdc0c7 | ||
|
|
077d6e53cd | ||
|
|
4e19f7a8dc | ||
|
|
7941f7b863 | ||
|
|
c7102ebbe5 | ||
|
|
1b87cb752e | ||
|
|
d9692f7ecf | ||
|
|
98637576bd | ||
|
|
d8bdb23108 | ||
|
|
2191c9b172 | ||
|
|
01d1d6580a | ||
|
|
96f4c7b9fa | ||
|
|
6a24d5437b | ||
|
|
8396a63128 | ||
|
|
d44bfda26d | ||
|
|
00f3899aea | ||
|
|
557967c36c | ||
|
|
73db0a54e5 | ||
|
|
578e07fcbc | ||
|
|
93169e2e62 | ||
|
|
db24c029be | ||
|
|
a6f9a206b7 | ||
|
|
ce253864f8 | ||
|
|
79f1f43830 | ||
|
|
7b666e62be | ||
|
|
5d5f66239a | ||
|
|
4b1586fa81 | ||
|
|
9fa390aee6 | ||
|
|
ad632726bd | ||
|
|
1dd1717462 | ||
|
|
73db721dab | ||
|
|
6bb6c286a9 | ||
|
|
ca34d190b5 | ||
|
|
57a13664cc | ||
|
|
59dc0e75c4 | ||
|
|
2d6aed1d92 | ||
|
|
3dd575ce55 | ||
|
|
60e871658f | ||
|
|
c49dfdf496 | ||
|
|
9b0ad73a4b | ||
|
|
bd8218e85d | ||
|
|
8f3101d820 | ||
|
|
77309c07b0 | ||
|
|
c8e889694d | ||
|
|
b402687fcd | ||
|
|
0530a7d89a | ||
|
|
6bdb9ba54c | ||
|
|
f2bfd611e2 | ||
|
|
9c1d5a080b | ||
|
|
b28244e2fe | ||
|
|
8b3834c34f | ||
|
|
af3e4d2e5b | ||
|
|
c552d1bf16 | ||
|
|
6a1ecdcce3 | ||
|
|
39357c829c | ||
|
|
40381bdcd9 | ||
|
|
7bad4f7722 | ||
|
|
65e811570b | ||
|
|
dd31900f80 | ||
|
|
013ada58c8 | ||
|
|
78dad45f3b | ||
|
|
52b452948f | ||
|
|
267746603a | ||
|
|
b26b240613 | ||
|
|
8e48c889dd | ||
|
|
f4445a310a | ||
|
|
4157b64754 | ||
|
|
0d2aafe7b7 | ||
|
|
6f4c9b32af | ||
|
|
eb77a75ab7 | ||
|
|
0851650c6b | ||
|
|
1459c90a7f | ||
|
|
af29c080ba | ||
|
|
50ea32162f | ||
|
|
2d5328a860 | ||
|
|
e5db7598b2 | ||
|
|
dc78d8f9ea | ||
|
|
022b8451e3 | ||
|
|
5b54c7d41a | ||
|
|
f94036b2b0 | ||
|
|
7d358208b9 | ||
|
|
c8ff8ea96f | ||
|
|
402d366bba | ||
|
|
41e701ad84 | ||
|
|
f763f94556 | ||
|
|
c9f5ecb4b5 | ||
|
|
7792a74c76 | ||
|
|
4cabc2ae85 | ||
|
|
995b395795 | ||
|
|
3d44f75d09 | ||
|
|
c1f947a3c5 | ||
|
|
696a16cca2 | ||
|
|
936bb6e7cd | ||
|
|
8dfa903346 | ||
|
|
95a7170709 | ||
|
|
554c4bbde4 | ||
|
|
ac23f21a2c | ||
|
|
6696151008 | ||
|
|
50d0348b64 | ||
|
|
bf7bbba7ea | ||
|
|
b28b1b7b9a | ||
|
|
8499d8007e | ||
|
|
e5a2058c7c | ||
|
|
e94fcd96f9 | ||
|
|
c5017e6da8 | ||
|
|
0169f9e18c | ||
|
|
b170c19274 | ||
|
|
22b3679752 | ||
|
|
4714f478fe | ||
|
|
72c5037f06 | ||
|
|
6238793ad7 | ||
|
|
da641e4e8d | ||
|
|
19a085a672 | ||
|
|
f672a0f677 | ||
|
|
db194ced5b | ||
|
|
11ed661e2c | ||
|
|
b2de803871 | ||
|
|
30abbe36a8 | ||
|
|
62044f02df | ||
|
|
a18d4e0067 | ||
|
|
0f241b7447 | ||
|
|
efaa1fe31c | ||
|
|
1729bb8e8a | ||
|
|
9fdd04ae61 | ||
|
|
87a8bda023 | ||
|
|
d384557b63 | ||
|
|
534a4f81fc | ||
|
|
a0d3585c6c | ||
|
|
ec34ff52cc | ||
|
|
0ac8138ce3 | ||
|
|
c41cad4c5f | ||
|
|
f466d6289e | ||
|
|
851c5a933d | ||
|
|
10048f6579 | ||
|
|
a598b2a910 | ||
|
|
2ec1038ffd | ||
|
|
cbb1da66c3 | ||
|
|
35ab660042 | ||
|
|
40a485db9c | ||
|
|
641e75e6e2 | ||
|
|
2e394bef11 | ||
|
|
4a9dbac435 | ||
|
|
d60dbcf91c | ||
|
|
bd483a63cf | ||
|
|
d5dc0ba855 | ||
|
|
e56a042153 | ||
|
|
1995b5559d | ||
|
|
fe384e06bc | ||
|
|
0198ee9e51 | ||
|
|
72e705cbd6 | ||
|
|
d5afb67a81 | ||
|
|
3894397b07 | ||
|
|
36381f0884 | ||
|
|
5cd1976a4a | ||
|
|
c9ae3f6a02 | ||
|
|
d2322d0114 | ||
|
|
7a431ec7b9 | ||
|
|
114aa68082 | ||
|
|
f07a76d959 | ||
|
|
ca0d77855f | ||
|
|
cd3f577c85 | ||
|
|
9228f97d7d | ||
|
|
6db5175e4e | ||
|
|
fba5ece05c | ||
|
|
01a775a46c | ||
|
|
4918360ea1 | ||
|
|
433a5cf580 | ||
|
|
fc9270c943 | ||
|
|
fa8c5789a6 | ||
|
|
cce56fa19b |
@@ -16,8 +16,10 @@ concurrency:
|
|||||||
jobs:
|
jobs:
|
||||||
action:
|
action:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
if: github.repository == 'tmux/tmux'
|
||||||
|
timeout-minutes: 10
|
||||||
steps:
|
steps:
|
||||||
- uses: dessant/lock-threads@v6
|
- uses: dessant/lock-threads@89ae32b08ed1a541efecbab17912962a5e38981c # v6
|
||||||
with:
|
with:
|
||||||
github-token: ${{ github.token }}
|
github-token: ${{ github.token }}
|
||||||
issue-inactive-days: '30'
|
issue-inactive-days: '30'
|
||||||
|
|||||||
@@ -16,7 +16,8 @@ jobs:
|
|||||||
regress:
|
regress:
|
||||||
name: ${{ matrix.name }}
|
name: ${{ matrix.name }}
|
||||||
runs-on: ${{ matrix.runner }}
|
runs-on: ${{ matrix.runner }}
|
||||||
timeout-minutes: 45
|
if: github.repository == 'tmux/tmux'
|
||||||
|
timeout-minutes: 300
|
||||||
|
|
||||||
strategy:
|
strategy:
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
@@ -37,7 +38,9 @@ jobs:
|
|||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: checkout
|
- name: checkout
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4
|
||||||
|
with:
|
||||||
|
persist-credentials: false
|
||||||
|
|
||||||
- name: dependencies
|
- name: dependencies
|
||||||
if: runner.os == 'Linux'
|
if: runner.os == 'Linux'
|
||||||
@@ -76,11 +79,11 @@ jobs:
|
|||||||
- name: test
|
- name: test
|
||||||
run: |
|
run: |
|
||||||
cd regress
|
cd regress
|
||||||
${{ matrix.make }}
|
${{ matrix.make }} -j"$(getconf _NPROCESSORS_ONLN)"
|
||||||
|
|
||||||
- name: logs
|
- name: logs
|
||||||
if: failure()
|
if: failure()
|
||||||
uses: actions/upload-artifact@v4
|
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
|
||||||
with:
|
with:
|
||||||
name: regress-logs-${{ matrix.name }}
|
name: regress-logs-${{ matrix.name }}
|
||||||
path: regress/logs/*.log
|
path: regress/logs/*.log
|
||||||
|
|||||||
@@ -1,7 +1,4 @@
|
|||||||
CHANGES FROM 3.7b TO 3.8
|
CHANGES FROM 3.7c TO 3.8
|
||||||
|
|
||||||
* Build with jemalloc on macOS to avoid what appears to be a bug in the system
|
|
||||||
calloc(3) (issue 5385).
|
|
||||||
|
|
||||||
* Many improvements to floating panes:
|
* Many improvements to floating panes:
|
||||||
|
|
||||||
@@ -12,7 +9,12 @@ CHANGES FROM 3.7b 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. -D closes the modal pane with
|
||||||
|
Escape or C-c. 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;
|
||||||
@@ -27,10 +29,18 @@ CHANGES FROM 3.7b 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;
|
||||||
|
|
||||||
- pane-border-status has top-floating and bottom-floating, and there are new
|
- command-prompt -P opens a prompt inside a pane rather than on the status
|
||||||
default bindings under C-b g for common move and resize operations;
|
line;
|
||||||
|
|
||||||
- floating panes are unzoomed before creation to avoid a crash.
|
- 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.
|
||||||
|
|
||||||
|
* Layout strings now use a JSON subset format which includes floating panes.
|
||||||
|
The old format is still accepted; control mode clients receive old layouts
|
||||||
|
unless they set the new-layouts flag.
|
||||||
|
|
||||||
|
* Add remain-on-exit failed-key to keep a pane open after a failed command
|
||||||
|
until a key is pressed.
|
||||||
|
|
||||||
* Add support for themes and improve default colours:
|
* Add support for themes and improve default colours:
|
||||||
|
|
||||||
@@ -77,7 +87,7 @@ CHANGES FROM 3.7b 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.
|
||||||
@@ -90,10 +100,29 @@ CHANGES FROM 3.7b 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 an appesc terminal feature for mintty's application escape key mode,
|
||||||
|
avoiding unsupported sequences on other terminals (Pete Dietl, issue 5626).
|
||||||
|
|
||||||
|
* Preserve existing Ss and Se terminfo capabilities for cursor styles and
|
||||||
|
restoring the default cursor (issue 5476).
|
||||||
|
|
||||||
* Add a #{A/count:frames} modifier to show a series of frames as an animation
|
* 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).
|
||||||
@@ -108,6 +137,12 @@ CHANGES FROM 3.7b 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 capture-pane -I to include the time each line entered history.
|
||||||
|
|
||||||
|
* Various memory leak fixes from Heon Jeong.
|
||||||
|
|
||||||
|
* 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).
|
||||||
@@ -121,7 +156,12 @@ CHANGES FROM 3.7b 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).
|
||||||
|
|
||||||
* Fix scrollbar initial state so scrollbars appear on new windows (issue 5339).
|
* 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).
|
||||||
@@ -133,7 +173,9 @@ CHANGES FROM 3.7b 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,15 +187,34 @@ CHANGES FROM 3.7b TO 3.8
|
|||||||
|
|
||||||
* The mouse option now defaults to on.
|
* The mouse option now defaults to on.
|
||||||
|
|
||||||
|
* The active-pane client flag has been removed.
|
||||||
|
|
||||||
* Fix send-keys -K so keys are inserted in the correct place in the input
|
* Fix send-keys -K so keys are inserted in the correct place in the input
|
||||||
queue, like keys from key bindings (issue 3476).
|
queue, like keys from key bindings (issue 3476).
|
||||||
|
|
||||||
* 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). Bound buffered command replies so a control mode
|
||||||
|
client that stops reading cannot consume unlimited memory (issue 5565), and
|
||||||
|
fix a collision between the output discard and bracketed paste flags (issue
|
||||||
|
5622).
|
||||||
|
|
||||||
* 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), and prevent reuse of a
|
||||||
|
session with no windows while detaching (Heon Jeong, issue 5620).
|
||||||
|
|
||||||
|
* Free wait-for clients when they disconnect and do not run their remaining
|
||||||
|
queued commands (issue 5614).
|
||||||
|
|
||||||
|
* Expand formats in run-shell -c like other start-directory flags (reported by
|
||||||
|
Saul Nogueras).
|
||||||
|
|
||||||
|
* Fix session alert flag formats to check all windows (issue 5599).
|
||||||
|
|
||||||
|
* Do not clear the marked pane when clicking on a pane border.
|
||||||
|
|
||||||
* Fix choose mode filtering when more than one pane exists (issue 5326), and
|
* Fix choose mode filtering when more than one pane exists (issue 5326), and
|
||||||
fix an infinite loop in customize mode when a filter does not match.
|
fix an infinite loop in customize mode when a filter does not match.
|
||||||
@@ -164,12 +225,30 @@ CHANGES FROM 3.7b TO 3.8
|
|||||||
status lines, menus, popups, wide characters and floating panes. This
|
status lines, menus, popups, wide characters and floating panes. This
|
||||||
includes fixes for flicker with alternate screen applications and scrollbars
|
includes fixes for flicker with alternate screen applications and scrollbars
|
||||||
(Michael Grant, issue 5351; Noam Stolero, issue 5350; Aung Myo Kyaw, issue
|
(Michael Grant, issue 5351; Noam Stolero, issue 5350; Aung Myo Kyaw, issue
|
||||||
5098).
|
5098), and misplaced wrapped lines during partial redraws (Ben Maurer, issue
|
||||||
|
5625).
|
||||||
|
|
||||||
* Do not crash looking for the next or previous session (issue 5344), or when
|
* Do not crash looking for the next or previous session (issue 5344), or when
|
||||||
no client is available.
|
no client is available.
|
||||||
|
|
||||||
* Check time periodically in loops rather than for every item (issue 5367).
|
* Fix crashes when destroying a zoomed pane (Romain Francoise, issue 5591),
|
||||||
|
copying from a blank source pane (Kaixuan Li), or opening a mode prompt when
|
||||||
|
the client is offset into the window.
|
||||||
|
|
||||||
|
* Avoid an infinite loop when assigning a width to WCHAR_MAX (issue 5602).
|
||||||
|
|
||||||
|
CHANGES FROM 3.7b TO 3.7c
|
||||||
|
|
||||||
|
* Build with jemalloc on macOS to avoid what appears to be a bug in calloc
|
||||||
|
(issue 5385).
|
||||||
|
|
||||||
|
* Fix scrollbar initial state so they appear on new windows (issue 5339).
|
||||||
|
|
||||||
|
* Check time periodically in loops rather than every one (issue 5367).
|
||||||
|
|
||||||
|
* Use message-style again as default for message-format.
|
||||||
|
|
||||||
|
* Unzoom before creating floating panes to avoid a crash.
|
||||||
|
|
||||||
CHANGES FROM 3.7a TO 3.7b
|
CHANGES FROM 3.7a TO 3.7b
|
||||||
|
|
||||||
|
|||||||
@@ -174,6 +174,7 @@ dist_tmux_SOURCES = \
|
|||||||
input-keys.c \
|
input-keys.c \
|
||||||
input.c \
|
input.c \
|
||||||
job.c \
|
job.c \
|
||||||
|
json.c \
|
||||||
key-bindings.c \
|
key-bindings.c \
|
||||||
key-string.c \
|
key-string.c \
|
||||||
layout-custom.c \
|
layout-custom.c \
|
||||||
|
|||||||
+4
-4
@@ -1,4 +1,4 @@
|
|||||||
/* $OpenBSD: arguments.c,v 1.66 2026/06/26 09:54:56 nicm Exp $ */
|
/* $OpenBSD: arguments.c,v 1.67 2026/08/25 06:04:33 nicm Exp $ */
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2010 Nicholas Marriott <nicholas.marriott@gmail.com>
|
* Copyright (c) 2010 Nicholas Marriott <nicholas.marriott@gmail.com>
|
||||||
@@ -762,8 +762,6 @@ args_make_commands_now(struct cmd *self, struct cmdq_item *item, u_int idx,
|
|||||||
cmdq_error(item, "%s", error);
|
cmdq_error(item, "%s", error);
|
||||||
free(error);
|
free(error);
|
||||||
}
|
}
|
||||||
else
|
|
||||||
cmdlist->references++;
|
|
||||||
args_make_commands_free(state);
|
args_make_commands_free(state);
|
||||||
return (cmdlist);
|
return (cmdlist);
|
||||||
}
|
}
|
||||||
@@ -827,8 +825,10 @@ args_make_commands(struct args_command_state *state, int argc, char **argv,
|
|||||||
int i;
|
int i;
|
||||||
|
|
||||||
if (state->cmdlist != NULL) {
|
if (state->cmdlist != NULL) {
|
||||||
if (argc == 0)
|
if (argc == 0) {
|
||||||
|
state->cmdlist->references++;
|
||||||
return (state->cmdlist);
|
return (state->cmdlist);
|
||||||
|
}
|
||||||
return (cmd_list_copy(state->cmdlist, argc, argv));
|
return (cmd_list_copy(state->cmdlist, argc, argv));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
/* $OpenBSD: cfg.c,v 1.90 2026/07/14 17:17:17 nicm Exp $ */
|
/* $OpenBSD: cfg.c,v 1.91 2026/08/03 13:38:42 nicm Exp $ */
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2008 Nicholas Marriott <nicholas.marriott@gmail.com>
|
* Copyright (c) 2008 Nicholas Marriott <nicholas.marriott@gmail.com>
|
||||||
@@ -223,13 +223,15 @@ cfg_print_causes(struct cmdq_item *item)
|
|||||||
{
|
{
|
||||||
struct client *c = cmdq_get_client(item);
|
struct client *c = cmdq_get_client(item);
|
||||||
u_int i;
|
u_int i;
|
||||||
|
char *cause;
|
||||||
|
|
||||||
for (i = 0; i < cfg_ncauses; i++) {
|
for (i = 0; i < cfg_ncauses; i++) {
|
||||||
|
cause = cfg_causes[i];
|
||||||
if (c != NULL && (c->flags & CLIENT_CONTROL))
|
if (c != NULL && (c->flags & CLIENT_CONTROL))
|
||||||
control_write(c, "%%config-error %s", cfg_causes[i]);
|
control_notify_write(c, "%%config-error %s", cause);
|
||||||
else
|
else
|
||||||
cmdq_print(item, "%s", cfg_causes[i]);
|
cmdq_print(item, "%s", cause);
|
||||||
free(cfg_causes[i]);
|
free(cause);
|
||||||
}
|
}
|
||||||
|
|
||||||
free(cfg_causes);
|
free(cfg_causes);
|
||||||
@@ -244,14 +246,16 @@ cfg_show_causes(struct session *s)
|
|||||||
struct window_pane *wp;
|
struct window_pane *wp;
|
||||||
struct window_mode_entry *wme;
|
struct window_mode_entry *wme;
|
||||||
u_int i;
|
u_int i;
|
||||||
|
char *cause;
|
||||||
|
|
||||||
if (cfg_ncauses == 0)
|
if (cfg_ncauses == 0)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (c != NULL && (c->flags & CLIENT_CONTROL)) {
|
if (c != NULL && (c->flags & CLIENT_CONTROL)) {
|
||||||
for (i = 0; i < cfg_ncauses; i++) {
|
for (i = 0; i < cfg_ncauses; i++) {
|
||||||
control_write(c, "%%config-error %s", cfg_causes[i]);
|
cause = cfg_causes[i];
|
||||||
free(cfg_causes[i]);
|
control_notify_write(c, "%%config-error %s", cause);
|
||||||
|
free(cause);
|
||||||
}
|
}
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
/* $OpenBSD: client.c,v 1.166 2026/07/10 15:45:11 nicm Exp $ */
|
/* $OpenBSD: client.c,v 1.168 2026/08/17 20:14:31 nicm Exp $ */
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>
|
* Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>
|
||||||
@@ -276,7 +276,7 @@ client_main(struct event_base *base, int argc, char **argv, uint64_t flags,
|
|||||||
proc_set_signals(client_proc, client_signal);
|
proc_set_signals(client_proc, client_signal);
|
||||||
|
|
||||||
/* Save the flags. */
|
/* Save the flags. */
|
||||||
client_flags = flags;
|
client_flags = flags|CLIENT_WRITE_ACK;
|
||||||
log_debug("flags are %#llx", (unsigned long long)client_flags);
|
log_debug("flags are %#llx", (unsigned long long)client_flags);
|
||||||
|
|
||||||
/* Initialize the client socket and start the server. */
|
/* Initialize the client socket and start the server. */
|
||||||
@@ -712,6 +712,9 @@ client_dispatch_wait(struct imsg *imsg)
|
|||||||
fprintf(stderr, "server version is too old for client\n");
|
fprintf(stderr, "server version is too old for client\n");
|
||||||
proc_exit(client_proc);
|
proc_exit(client_proc);
|
||||||
break;
|
break;
|
||||||
|
default:
|
||||||
|
log_debug("unknown message type %u", imsg->hdr.type);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -798,5 +801,8 @@ client_dispatch_attached(struct imsg *imsg)
|
|||||||
system(data);
|
system(data);
|
||||||
proc_send(client_peer, MSG_UNLOCK, -1, NULL, 0);
|
proc_send(client_peer, MSG_UNLOCK, -1, NULL, 0);
|
||||||
break;
|
break;
|
||||||
|
default:
|
||||||
|
log_debug("unknown message type %u", imsg->hdr.type);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+5
-4
@@ -1,4 +1,4 @@
|
|||||||
/* $OpenBSD: cmd-break-pane.c,v 1.75 2026/07/23 09:38:27 nicm Exp $ */
|
/* $OpenBSD: cmd-break-pane.c,v 1.76 2026/08/17 07:04:45 nicm Exp $ */
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2009 Nicholas Marriott <nicholas.marriott@gmail.com>
|
* Copyright (c) 2009 Nicholas Marriott <nicholas.marriott@gmail.com>
|
||||||
@@ -174,13 +174,14 @@ cmd_break_pane_exec(struct cmd *self, struct cmdq_item *item)
|
|||||||
}
|
}
|
||||||
window_set_fill_cells(w);
|
window_set_fill_cells(w);
|
||||||
|
|
||||||
|
if (idx == -1)
|
||||||
|
idx = -1 - options_get_number(dst_s->options, "base-index");
|
||||||
|
wl = session_attach(dst_s, w, idx, &cause); /* can't fail */
|
||||||
|
|
||||||
layout_init(w, wp);
|
layout_init(w, wp);
|
||||||
wp->flags |= PANE_CHANGED;
|
wp->flags |= PANE_CHANGED;
|
||||||
colour_palette_from_option(&wp->palette, wp->options);
|
colour_palette_from_option(&wp->palette, wp->options);
|
||||||
|
|
||||||
if (idx == -1)
|
|
||||||
idx = -1 - options_get_number(dst_s->options, "base-index");
|
|
||||||
wl = session_attach(dst_s, w, idx, &cause); /* can't fail */
|
|
||||||
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);
|
||||||
|
|||||||
+13
-7
@@ -1,4 +1,4 @@
|
|||||||
/* $OpenBSD: cmd-capture-pane.c,v 1.68 2026/07/20 11:16:33 nicm Exp $ */
|
/* $OpenBSD: cmd-capture-pane.c,v 1.69 2026/09/08 07:31:59 nicm Exp $ */
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2009 Jonathan Alvarado <radobobo@users.sourceforge.net>
|
* Copyright (c) 2009 Jonathan Alvarado <radobobo@users.sourceforge.net>
|
||||||
@@ -42,8 +42,8 @@ const struct cmd_entry cmd_capture_pane_entry = {
|
|||||||
.name = "capture-pane",
|
.name = "capture-pane",
|
||||||
.alias = "capturep",
|
.alias = "capturep",
|
||||||
|
|
||||||
.args = { "ab:CeE:FHJLMNpPqRS:Tt:", 0, 0, NULL },
|
.args = { "ab:CeE:FHIJLMNpPqRS:Tt:", 0, 0, NULL },
|
||||||
.usage = "[-aCeFHJLMNpPqRT] " CMD_BUFFER_USAGE " [-E end-line] "
|
.usage = "[-aCeFHIJLMNpPqRT] " CMD_BUFFER_USAGE " [-E end-line] "
|
||||||
"[-S start-line] " CMD_TARGET_PANE_USAGE,
|
"[-S start-line] " CMD_TARGET_PANE_USAGE,
|
||||||
|
|
||||||
.target = { 't', CMD_FIND_PANE, 0 },
|
.target = { 't', CMD_FIND_PANE, 0 },
|
||||||
@@ -200,7 +200,7 @@ cmd_capture_pane_pending(struct args *args, struct window_pane *wp,
|
|||||||
tmp[0] = line[i];
|
tmp[0] = line[i];
|
||||||
tmp[1] = '\0';
|
tmp[1] = '\0';
|
||||||
} else
|
} else
|
||||||
xsnprintf(tmp, sizeof tmp, "\\%03hho", line[i]);
|
snprintf(tmp, sizeof tmp, "\\%03hho", line[i]);
|
||||||
buf = cmd_capture_pane_append(buf, len, tmp,
|
buf = cmd_capture_pane_append(buf, len, tmp,
|
||||||
strlen(tmp));
|
strlen(tmp));
|
||||||
}
|
}
|
||||||
@@ -259,7 +259,7 @@ cmd_capture_pane_history(struct args *args, struct cmdq_item *item,
|
|||||||
struct grid_cell *gc = NULL;
|
struct grid_cell *gc = NULL;
|
||||||
struct window_mode_entry *wme;
|
struct window_mode_entry *wme;
|
||||||
int n, join_lines, number_lines, flags = 0;
|
int n, join_lines, number_lines, flags = 0;
|
||||||
int show_flags, hyperlinks;
|
int show_flags, show_time, hyperlinks;
|
||||||
u_int *links = NULL, nlinks = 0;
|
u_int *links = NULL, nlinks = 0;
|
||||||
u_int i, sx, top, bottom, tmp;
|
u_int i, sx, top, bottom, tmp;
|
||||||
char *cause, *buf = NULL, *line, b[64], *cp;
|
char *cause, *buf = NULL, *line, b[64], *cp;
|
||||||
@@ -342,6 +342,7 @@ cmd_capture_pane_history(struct args *args, struct cmdq_item *item,
|
|||||||
flags |= GRID_STRING_TRIM_SPACES;
|
flags |= GRID_STRING_TRIM_SPACES;
|
||||||
number_lines = args_has(args, 'L');
|
number_lines = args_has(args, 'L');
|
||||||
show_flags = args_has(args, 'F');
|
show_flags = args_has(args, 'F');
|
||||||
|
show_time = args_has(args, 'I');
|
||||||
hyperlinks = args_has(args, 'H');
|
hyperlinks = args_has(args, 'H');
|
||||||
if (hyperlinks)
|
if (hyperlinks)
|
||||||
links = xreallocarray(NULL, gd->sx, sizeof *links);
|
links = xreallocarray(NULL, gd->sx, sizeof *links);
|
||||||
@@ -358,6 +359,7 @@ cmd_capture_pane_history(struct args *args, struct cmdq_item *item,
|
|||||||
free(line);
|
free(line);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
gl = grid_peek_line(gd, i);
|
||||||
|
|
||||||
if (number_lines) {
|
if (number_lines) {
|
||||||
if (i >= gd->hsize)
|
if (i >= gd->hsize)
|
||||||
@@ -368,11 +370,16 @@ cmd_capture_pane_history(struct args *args, struct cmdq_item *item,
|
|||||||
if (n >= 0)
|
if (n >= 0)
|
||||||
buf = cmd_capture_pane_append(buf, len, b, n);
|
buf = cmd_capture_pane_append(buf, len, b, n);
|
||||||
}
|
}
|
||||||
|
if (show_time) {
|
||||||
|
n = snprintf(b, sizeof b, "%llu ",
|
||||||
|
(unsigned long long)grid_line_time(gl));
|
||||||
|
if (n >= 0)
|
||||||
|
buf = cmd_capture_pane_append(buf, len, b, n);
|
||||||
|
}
|
||||||
if (show_flags) {
|
if (show_flags) {
|
||||||
cp = b;
|
cp = b;
|
||||||
*cp = '\0';
|
*cp = '\0';
|
||||||
|
|
||||||
gl = grid_peek_line(gd, i);
|
|
||||||
if (gl->flags & GRID_LINE_DEAD)
|
if (gl->flags & GRID_LINE_DEAD)
|
||||||
*cp++ = 'D';
|
*cp++ = 'D';
|
||||||
if (gl->flags & GRID_LINE_HYPERLINK)
|
if (gl->flags & GRID_LINE_HYPERLINK)
|
||||||
@@ -393,7 +400,6 @@ cmd_capture_pane_history(struct args *args, struct cmdq_item *item,
|
|||||||
}
|
}
|
||||||
buf = cmd_capture_pane_append(buf, len, line, linelen);
|
buf = cmd_capture_pane_append(buf, len, line, linelen);
|
||||||
|
|
||||||
gl = grid_peek_line(gd, i);
|
|
||||||
if (!join_lines || !(gl->flags & GRID_LINE_WRAPPED))
|
if (!join_lines || !(gl->flags & GRID_LINE_WRAPPED))
|
||||||
buf[(*len)++] = '\n';
|
buf[(*len)++] = '\n';
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
/* $OpenBSD: cmd-command-prompt.c,v 1.75 2026/06/25 11:39:11 nicm Exp $ */
|
/* $OpenBSD: cmd-command-prompt.c,v 1.76 2026/08/25 06:04:33 nicm Exp $ */
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2008 Nicholas Marriott <nicholas.marriott@gmail.com>
|
* Copyright (c) 2008 Nicholas Marriott <nicholas.marriott@gmail.com>
|
||||||
@@ -242,9 +242,11 @@ cmd_command_prompt_callback(struct client *c, void *data, const char *s,
|
|||||||
} else if (item == NULL) {
|
} else if (item == NULL) {
|
||||||
new_item = cmdq_get_command(cmdlist, NULL);
|
new_item = cmdq_get_command(cmdlist, NULL);
|
||||||
cmdq_append(c, new_item);
|
cmdq_append(c, new_item);
|
||||||
|
cmd_list_free(cmdlist);
|
||||||
} else {
|
} else {
|
||||||
new_item = cmdq_get_command(cmdlist, cmdq_get_state(item));
|
new_item = cmdq_get_command(cmdlist, cmdq_get_state(item));
|
||||||
cmdq_insert_after(item, new_item);
|
cmdq_insert_after(item, new_item);
|
||||||
|
cmd_list_free(cmdlist);
|
||||||
}
|
}
|
||||||
cmd_free_argv(argc, argv);
|
cmd_free_argv(argc, argv);
|
||||||
|
|
||||||
|
|||||||
+3
-1
@@ -1,4 +1,4 @@
|
|||||||
/* $OpenBSD: cmd-display-menu.c,v 1.52 2026/07/14 19:07:03 nicm Exp $ */
|
/* $OpenBSD: cmd-display-menu.c,v 1.53 2026/08/31 07:46:55 nicm Exp $ */
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2019 Nicholas Marriott <nicholas.marriott@gmail.com>
|
* Copyright (c) 2019 Nicholas Marriott <nicholas.marriott@gmail.com>
|
||||||
@@ -591,6 +591,8 @@ cmd_display_popup_exec(struct cmd *self, struct cmdq_item *item)
|
|||||||
server_client_clear_overlay(tc);
|
server_client_clear_overlay(tc);
|
||||||
return (CMD_RETURN_NORMAL);
|
return (CMD_RETURN_NORMAL);
|
||||||
}
|
}
|
||||||
|
if (tc->flags & CLIENT_CONTROL)
|
||||||
|
return (CMD_RETURN_NORMAL);
|
||||||
if (!modify && tc->overlay_draw != NULL)
|
if (!modify && tc->overlay_draw != NULL)
|
||||||
return (CMD_RETURN_NORMAL);
|
return (CMD_RETURN_NORMAL);
|
||||||
|
|
||||||
|
|||||||
+23
-7
@@ -1,4 +1,4 @@
|
|||||||
/* $OpenBSD: cmd-display-message.c,v 1.65 2026/02/23 08:46:57 nicm Exp $ */
|
/* $OpenBSD: cmd-display-message.c,v 1.66 2026/09/08 08:33:10 nicm Exp $ */
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2009 Tiago Cunha <me@tiagocunha.org>
|
* Copyright (c) 2009 Tiago Cunha <me@tiagocunha.org>
|
||||||
@@ -39,8 +39,8 @@ const struct cmd_entry cmd_display_message_entry = {
|
|||||||
.name = "display-message",
|
.name = "display-message",
|
||||||
.alias = "display",
|
.alias = "display",
|
||||||
|
|
||||||
.args = { "aCc:d:lINpt:F:v", 0, 1, NULL },
|
.args = { "aCc:d:jlINpt:F:v", 0, 1, NULL },
|
||||||
.usage = "[-aCIlNpv] [-c target-client] [-d delay] [-F format] "
|
.usage = "[-aCIjlNpv] [-c target-client] [-d delay] [-F format] "
|
||||||
CMD_TARGET_PANE_USAGE " [message]",
|
CMD_TARGET_PANE_USAGE " [message]",
|
||||||
|
|
||||||
.target = { 't', CMD_FIND_PANE, CMD_FIND_CANFAIL },
|
.target = { 't', CMD_FIND_PANE, CMD_FIND_CANFAIL },
|
||||||
@@ -67,14 +67,15 @@ cmd_display_message_exec(struct cmd *self, struct cmdq_item *item)
|
|||||||
struct winlink *wl = target->wl;
|
struct winlink *wl = target->wl;
|
||||||
struct window_pane *wp = target->wp;
|
struct window_pane *wp = target->wp;
|
||||||
const char *template;
|
const char *template;
|
||||||
char *msg, *cause;
|
char *msg, *cause = NULL;
|
||||||
int delay = -1, flags, Nflag = args_has(args, 'N');
|
int delay = -1, flags, Nflag = args_has(args, 'N');
|
||||||
int Cflag = args_has(args, 'C');
|
int Cflag = args_has(args, 'C');
|
||||||
struct format_tree *ft;
|
struct format_tree *ft;
|
||||||
u_int count = args_count(args);
|
u_int count = args_count(args);
|
||||||
struct evbuffer *evb;
|
struct evbuffer *evb;
|
||||||
|
struct json_node *jn;
|
||||||
|
|
||||||
if (args_has(args, 'I')) {
|
if (args_has(args, 'I') && !args_has(args, 'j')) {
|
||||||
if (wp == NULL)
|
if (wp == NULL)
|
||||||
return (CMD_RETURN_NORMAL);
|
return (CMD_RETURN_NORMAL);
|
||||||
switch (window_pane_start_input(wp, item, &cause)) {
|
switch (window_pane_start_input(wp, item, &cause)) {
|
||||||
@@ -107,7 +108,9 @@ cmd_display_message_exec(struct cmd *self, struct cmdq_item *item)
|
|||||||
template = args_string(args, 0);
|
template = args_string(args, 0);
|
||||||
else
|
else
|
||||||
template = args_get(args, 'F');
|
template = args_get(args, 'F');
|
||||||
if (template == NULL)
|
if (args_has(args, 'j') && template == NULL)
|
||||||
|
template = "";
|
||||||
|
else if (template == NULL)
|
||||||
template = DISPLAY_MESSAGE_TEMPLATE;
|
template = DISPLAY_MESSAGE_TEMPLATE;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -129,7 +132,7 @@ cmd_display_message_exec(struct cmd *self, struct cmdq_item *item)
|
|||||||
ft = format_create(cmdq_get_client(item), item, FORMAT_NONE, flags);
|
ft = format_create(cmdq_get_client(item), item, FORMAT_NONE, flags);
|
||||||
format_defaults(ft, c, s, wl, wp);
|
format_defaults(ft, c, s, wl, wp);
|
||||||
|
|
||||||
if (args_has(args, 'a')) {
|
if (args_has(args, 'a') && !args_has(args, 'j')) {
|
||||||
format_each(ft, cmd_display_message_each, item);
|
format_each(ft, cmd_display_message_each, item);
|
||||||
format_free(ft);
|
format_free(ft);
|
||||||
return (CMD_RETURN_NORMAL);
|
return (CMD_RETURN_NORMAL);
|
||||||
@@ -139,6 +142,19 @@ cmd_display_message_exec(struct cmd *self, struct cmdq_item *item)
|
|||||||
msg = xstrdup(template);
|
msg = xstrdup(template);
|
||||||
else
|
else
|
||||||
msg = format_expand_time(ft, template);
|
msg = format_expand_time(ft, template);
|
||||||
|
if (args_has(args, 'j')) {
|
||||||
|
jn = json_parse(msg, &cause);
|
||||||
|
if (jn == NULL) {
|
||||||
|
cmdq_error(item, "%s", cause);
|
||||||
|
free(cause);
|
||||||
|
free(msg);
|
||||||
|
format_free(ft);
|
||||||
|
return (CMD_RETURN_ERROR);
|
||||||
|
}
|
||||||
|
free(msg);
|
||||||
|
msg = json_to_string(jn);
|
||||||
|
json_destroy_node(jn);
|
||||||
|
}
|
||||||
|
|
||||||
if (cmdq_get_client(item) == NULL)
|
if (cmdq_get_client(item) == NULL)
|
||||||
cmdq_error(item, "%s", msg);
|
cmdq_error(item, "%s", msg);
|
||||||
|
|||||||
+11
-7
@@ -1,4 +1,4 @@
|
|||||||
/* $OpenBSD: cmd-find.c,v 1.87 2026/07/17 12:42:51 nicm Exp $ */
|
/* $OpenBSD: cmd-find.c,v 1.88 2026/09/07 12:05:12 nicm Exp $ */
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2015 Nicholas Marriott <nicholas.marriott@gmail.com>
|
* Copyright (c) 2015 Nicholas Marriott <nicholas.marriott@gmail.com>
|
||||||
@@ -388,9 +388,11 @@ cmd_find_get_window_with_session(struct cmd_find_state *fs, const char *window)
|
|||||||
|
|
||||||
/* Try as an offset. */
|
/* Try as an offset. */
|
||||||
if (!exact && (window[0] == '+' || window[0] == '-')) {
|
if (!exact && (window[0] == '+' || window[0] == '-')) {
|
||||||
if (window[1] != '\0')
|
if (window[1] != '\0') {
|
||||||
n = strtonum(window + 1, 1, INT_MAX, NULL);
|
n = strtonum(window + 1, 1, INT_MAX, &errstr);
|
||||||
else
|
if (errstr != NULL)
|
||||||
|
return (-1);
|
||||||
|
} else
|
||||||
n = 1;
|
n = 1;
|
||||||
s = fs->s;
|
s = fs->s;
|
||||||
if (fs->flags & CMD_FIND_WINDOW_INDEX) {
|
if (fs->flags & CMD_FIND_WINDOW_INDEX) {
|
||||||
@@ -626,9 +628,11 @@ cmd_find_get_pane_with_window(struct cmd_find_state *fs, const char *pane)
|
|||||||
|
|
||||||
/* Try as an offset. */
|
/* Try as an offset. */
|
||||||
if (pane[0] == '+' || pane[0] == '-') {
|
if (pane[0] == '+' || pane[0] == '-') {
|
||||||
if (pane[1] != '\0')
|
if (pane[1] != '\0') {
|
||||||
n = strtonum(pane + 1, 1, INT_MAX, NULL);
|
n = strtonum(pane + 1, 1, INT_MAX, &errstr);
|
||||||
else
|
if (errstr != NULL)
|
||||||
|
return (-1);
|
||||||
|
} else
|
||||||
n = 1;
|
n = 1;
|
||||||
wp = fs->w->active;
|
wp = fs->w->active;
|
||||||
if (pane[0] == '+')
|
if (pane[0] == '+')
|
||||||
|
|||||||
+4
-1
@@ -1,4 +1,4 @@
|
|||||||
/* $OpenBSD: cmd-if-shell.c,v 1.86 2025/08/01 09:05:51 nicm Exp $ */
|
/* $OpenBSD: cmd-if-shell.c,v 1.87 2026/08/25 06:04:33 nicm Exp $ */
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2009 Tiago Cunha <me@tiagocunha.org>
|
* Copyright (c) 2009 Tiago Cunha <me@tiagocunha.org>
|
||||||
@@ -98,6 +98,7 @@ cmd_if_shell_exec(struct cmd *self, struct cmdq_item *item)
|
|||||||
return (CMD_RETURN_ERROR);
|
return (CMD_RETURN_ERROR);
|
||||||
new_item = cmdq_get_command(cmdlist, cmdq_get_state(item));
|
new_item = cmdq_get_command(cmdlist, cmdq_get_state(item));
|
||||||
cmdq_insert_after(item, new_item);
|
cmdq_insert_after(item, new_item);
|
||||||
|
cmd_list_free(cmdlist);
|
||||||
return (CMD_RETURN_NORMAL);
|
return (CMD_RETURN_NORMAL);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -164,9 +165,11 @@ cmd_if_shell_callback(struct job *job)
|
|||||||
} else if (item == NULL) {
|
} else if (item == NULL) {
|
||||||
new_item = cmdq_get_command(cmdlist, NULL);
|
new_item = cmdq_get_command(cmdlist, NULL);
|
||||||
cmdq_append(c, new_item);
|
cmdq_append(c, new_item);
|
||||||
|
cmd_list_free(cmdlist);
|
||||||
} else {
|
} else {
|
||||||
new_item = cmdq_get_command(cmdlist, cmdq_get_state(item));
|
new_item = cmdq_get_command(cmdlist, cmdq_get_state(item));
|
||||||
cmdq_insert_after(item, new_item);
|
cmdq_insert_after(item, new_item);
|
||||||
|
cmd_list_free(cmdlist);
|
||||||
}
|
}
|
||||||
|
|
||||||
out:
|
out:
|
||||||
|
|||||||
+25
-18
@@ -1,4 +1,4 @@
|
|||||||
/* $OpenBSD: cmd-join-pane.c,v 1.73 2026/07/17 15:24:30 nicm Exp $ */
|
/* $OpenBSD: cmd-join-pane.c,v 1.75 2026/09/21 10:33:16 nicm Exp $ */
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2011 George Nachman <tmux@georgester.com>
|
* Copyright (c) 2011 George Nachman <tmux@georgester.com>
|
||||||
@@ -133,7 +133,7 @@ cmd_join_pane_place(struct cmdq_item *item, struct winlink *wl,
|
|||||||
} else if (strcmp(position, "back") == 0) {
|
} else if (strcmp(position, "back") == 0) {
|
||||||
TAILQ_REMOVE(&w->z_index, wp, zentry);
|
TAILQ_REMOVE(&w->z_index, wp, zentry);
|
||||||
TAILQ_FOREACH(owp, &w->z_index, zentry) {
|
TAILQ_FOREACH(owp, &w->z_index, zentry) {
|
||||||
if (!window_pane_is_floating(owp))
|
if (!window_pane_is_floating_with_hidden(owp))
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (owp != NULL)
|
if (owp != NULL)
|
||||||
@@ -142,24 +142,30 @@ cmd_join_pane_place(struct cmdq_item *item, struct winlink *wl,
|
|||||||
TAILQ_INSERT_TAIL(&w->z_index, wp, zentry);
|
TAILQ_INSERT_TAIL(&w->z_index, wp, zentry);
|
||||||
} else if (strcmp(position, "forward") == 0) {
|
} else if (strcmp(position, "forward") == 0) {
|
||||||
owp = TAILQ_PREV(wp, window_panes_zindex, zentry);
|
owp = TAILQ_PREV(wp, window_panes_zindex, zentry);
|
||||||
|
while (owp != NULL && owp->layout_cell == NULL)
|
||||||
|
owp = TAILQ_PREV(owp, window_panes_zindex, zentry);
|
||||||
if (owp != NULL) {
|
if (owp != NULL) {
|
||||||
TAILQ_REMOVE(&w->z_index, wp, zentry);
|
TAILQ_REMOVE(&w->z_index, wp, zentry);
|
||||||
TAILQ_INSERT_BEFORE(owp, wp, zentry);
|
TAILQ_INSERT_BEFORE(owp, wp, zentry);
|
||||||
}
|
}
|
||||||
} else if (strcmp(position, "backward") == 0) {
|
} else if (strcmp(position, "backward") == 0) {
|
||||||
owp = TAILQ_NEXT(wp, zentry);
|
owp = TAILQ_NEXT(wp, zentry);
|
||||||
|
while (owp != NULL && owp->layout_cell == NULL)
|
||||||
|
owp = TAILQ_NEXT(owp, zentry);
|
||||||
if (owp != NULL && window_pane_is_floating(owp)) {
|
if (owp != NULL && window_pane_is_floating(owp)) {
|
||||||
TAILQ_REMOVE(&w->z_index, wp, zentry);
|
TAILQ_REMOVE(&w->z_index, wp, zentry);
|
||||||
TAILQ_INSERT_AFTER(&w->z_index, owp, wp, zentry);
|
TAILQ_INSERT_AFTER(&w->z_index, owp, wp, zentry);
|
||||||
}
|
}
|
||||||
} else if (strcmp(position, "forward-loop") == 0) {
|
} else if (strcmp(position, "forward-loop") == 0) {
|
||||||
owp = TAILQ_PREV(wp, window_panes_zindex, zentry);
|
owp = TAILQ_PREV(wp, window_panes_zindex, zentry);
|
||||||
|
while (owp != NULL && owp->layout_cell == NULL)
|
||||||
|
owp = TAILQ_PREV(owp, window_panes_zindex, zentry);
|
||||||
TAILQ_REMOVE(&w->z_index, wp, zentry);
|
TAILQ_REMOVE(&w->z_index, wp, zentry);
|
||||||
if (owp != NULL)
|
if (owp != NULL)
|
||||||
TAILQ_INSERT_BEFORE(owp, wp, zentry);
|
TAILQ_INSERT_BEFORE(owp, wp, zentry);
|
||||||
else {
|
else {
|
||||||
TAILQ_FOREACH(owp, &w->z_index, zentry) {
|
TAILQ_FOREACH(owp, &w->z_index, zentry) {
|
||||||
if (!window_pane_is_floating(owp))
|
if (!window_pane_is_floating_with_hidden(owp))
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (owp != NULL)
|
if (owp != NULL)
|
||||||
@@ -169,6 +175,8 @@ cmd_join_pane_place(struct cmdq_item *item, struct winlink *wl,
|
|||||||
}
|
}
|
||||||
} else if (strcmp(position, "backward-loop") == 0) {
|
} else if (strcmp(position, "backward-loop") == 0) {
|
||||||
owp = TAILQ_NEXT(wp, zentry);
|
owp = TAILQ_NEXT(wp, zentry);
|
||||||
|
while (owp != NULL && owp->layout_cell == NULL)
|
||||||
|
owp = TAILQ_NEXT(owp, zentry);
|
||||||
if (owp != NULL && window_pane_is_floating(owp)) {
|
if (owp != NULL && window_pane_is_floating(owp)) {
|
||||||
TAILQ_REMOVE(&w->z_index, wp, zentry);
|
TAILQ_REMOVE(&w->z_index, wp, zentry);
|
||||||
TAILQ_INSERT_AFTER(&w->z_index, owp, wp, zentry);
|
TAILQ_INSERT_AFTER(&w->z_index, owp, wp, zentry);
|
||||||
@@ -347,8 +355,10 @@ cmd_join_pane_zindex(struct cmdq_item *item, struct winlink *wl,
|
|||||||
|
|
||||||
n = 0;
|
n = 0;
|
||||||
TAILQ_FOREACH(owp, &w->z_index, zentry) {
|
TAILQ_FOREACH(owp, &w->z_index, zentry) {
|
||||||
if (!window_pane_is_floating(owp))
|
if (!window_pane_is_floating_with_hidden(owp))
|
||||||
break;
|
break;
|
||||||
|
if (owp->layout_cell == NULL)
|
||||||
|
continue;
|
||||||
if (n >= z)
|
if (n >= z)
|
||||||
break;
|
break;
|
||||||
n++;
|
n++;
|
||||||
@@ -431,25 +441,22 @@ cmd_join_pane_exec(struct cmd *self, struct cmdq_item *item)
|
|||||||
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'))
|
||||||
return (cmd_join_pane_mouse_update(item));
|
return (cmd_join_pane_mouse_update(item));
|
||||||
if (!window_pane_is_floating(dst_wp)) {
|
if (args_has(args, 'P') ||
|
||||||
cmdq_error(item, "pane is not floating");
|
args_has(args, 'z') ||
|
||||||
return (CMD_RETURN_ERROR);
|
args_has(args, 'X') ||
|
||||||
}
|
|
||||||
if ((s = args_get(args, 'P')) != NULL) {
|
|
||||||
server_unzoom_window(dst_w);
|
|
||||||
return (cmd_join_pane_place(item, dst_wl, dst_wp, s));
|
|
||||||
}
|
|
||||||
if ((s = args_get(args, 'z')) != NULL) {
|
|
||||||
server_unzoom_window(dst_w);
|
|
||||||
return (cmd_join_pane_zindex(item, dst_wl, dst_wp, s));
|
|
||||||
}
|
|
||||||
if (args_has(args, 'X') ||
|
|
||||||
args_has(args, 'Y') ||
|
args_has(args, 'Y') ||
|
||||||
args_has(args, 'U') ||
|
args_has(args, 'U') ||
|
||||||
args_has(args, 'D') ||
|
args_has(args, 'D') ||
|
||||||
args_has(args, 'L') ||
|
args_has(args, 'L') ||
|
||||||
args_has(args, 'R')) {
|
args_has(args, 'R')) {
|
||||||
server_unzoom_window(dst_w);
|
if (!window_pane_is_floating(dst_wp)) {
|
||||||
|
cmdq_error(item, "pane is not floating");
|
||||||
|
return (CMD_RETURN_ERROR);
|
||||||
|
}
|
||||||
|
if ((s = args_get(args, 'P')) != NULL)
|
||||||
|
return (cmd_join_pane_place(item, dst_wl, dst_wp, s));
|
||||||
|
if ((s = args_get(args, 'z')) != NULL)
|
||||||
|
return (cmd_join_pane_zindex(item, dst_wl, dst_wp, s));
|
||||||
return (cmd_join_pane_move(item, args, dst_wl, dst_wp));
|
return (cmd_join_pane_move(item, args, dst_wl, dst_wp));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+3
-2
@@ -1,4 +1,4 @@
|
|||||||
/* $OpenBSD: cmd-list-panes.c,v 1.40 2026/06/01 14:01:09 nicm Exp $ */
|
/* $OpenBSD: cmd-list-panes.c,v 1.41 2026/09/08 10:20:08 nicm Exp $ */
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2009 Nicholas Marriott <nicholas.marriott@gmail.com>
|
* Copyright (c) 2009 Nicholas Marriott <nicholas.marriott@gmail.com>
|
||||||
@@ -97,6 +97,7 @@ cmd_list_panes_window(struct cmd *self, struct session *s, struct winlink *wl,
|
|||||||
struct cmdq_item *item, int type)
|
struct cmdq_item *item, int type)
|
||||||
{
|
{
|
||||||
struct args *args = cmd_get_args(self);
|
struct args *args = cmd_get_args(self);
|
||||||
|
struct client *c = cmdq_get_client(item);
|
||||||
struct window_pane *wp, **l;
|
struct window_pane *wp, **l;
|
||||||
u_int i, n;
|
u_int i, n;
|
||||||
struct format_tree *ft;
|
struct format_tree *ft;
|
||||||
@@ -147,7 +148,7 @@ cmd_list_panes_window(struct cmd *self, struct session *s, struct winlink *wl,
|
|||||||
wp = l[i];
|
wp = l[i];
|
||||||
ft = format_create(cmdq_get_client(item), item, FORMAT_NONE, 0);
|
ft = format_create(cmdq_get_client(item), item, FORMAT_NONE, 0);
|
||||||
format_add(ft, "line", "%u", n);
|
format_add(ft, "line", "%u", n);
|
||||||
format_defaults(ft, NULL, s, wl, wp);
|
format_defaults(ft, c, s, wl, wp);
|
||||||
|
|
||||||
if (filter != NULL) {
|
if (filter != NULL) {
|
||||||
expanded = format_expand(ft, filter);
|
expanded = format_expand(ft, filter);
|
||||||
|
|||||||
+3
-2
@@ -1,4 +1,4 @@
|
|||||||
/* $OpenBSD: cmd-list-sessions.c,v 1.36 2026/02/27 08:25:12 nicm Exp $ */
|
/* $OpenBSD: cmd-list-sessions.c,v 1.37 2026/09/08 10:20:08 nicm Exp $ */
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>
|
* Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>
|
||||||
@@ -53,6 +53,7 @@ static enum cmd_retval
|
|||||||
cmd_list_sessions_exec(struct cmd *self, struct cmdq_item *item)
|
cmd_list_sessions_exec(struct cmd *self, struct cmdq_item *item)
|
||||||
{
|
{
|
||||||
struct args *args = cmd_get_args(self);
|
struct args *args = cmd_get_args(self);
|
||||||
|
struct client *c = cmdq_get_client(item);
|
||||||
struct session **l;
|
struct session **l;
|
||||||
u_int n, i;
|
u_int n, i;
|
||||||
struct format_tree *ft;
|
struct format_tree *ft;
|
||||||
@@ -76,7 +77,7 @@ cmd_list_sessions_exec(struct cmd *self, struct cmdq_item *item)
|
|||||||
for (i = 0; i < n; i++) {
|
for (i = 0; i < n; i++) {
|
||||||
ft = format_create(cmdq_get_client(item), item, FORMAT_NONE, 0);
|
ft = format_create(cmdq_get_client(item), item, FORMAT_NONE, 0);
|
||||||
format_add(ft, "line", "%u", i);
|
format_add(ft, "line", "%u", i);
|
||||||
format_defaults(ft, NULL, l[i], NULL, NULL);
|
format_defaults(ft, c, l[i], NULL, NULL);
|
||||||
|
|
||||||
if (filter != NULL) {
|
if (filter != NULL) {
|
||||||
expanded = format_expand(ft, filter);
|
expanded = format_expand(ft, filter);
|
||||||
|
|||||||
+3
-2
@@ -1,4 +1,4 @@
|
|||||||
/* $OpenBSD: cmd-list-windows.c,v 1.50 2026/02/27 08:25:12 nicm Exp $ */
|
/* $OpenBSD: cmd-list-windows.c,v 1.51 2026/09/08 10:20:08 nicm Exp $ */
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>
|
* Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>
|
||||||
@@ -60,6 +60,7 @@ cmd_list_windows_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 *target = cmdq_get_target(item);
|
struct cmd_find_state *target = cmdq_get_target(item);
|
||||||
|
struct client *c = cmdq_get_client(item);
|
||||||
struct winlink *wl, **l;
|
struct winlink *wl, **l;
|
||||||
struct session *s;
|
struct session *s;
|
||||||
u_int i, n;
|
u_int i, n;
|
||||||
@@ -94,7 +95,7 @@ cmd_list_windows_exec(struct cmd *self, struct cmdq_item *item)
|
|||||||
s = wl->session;
|
s = wl->session;
|
||||||
ft = format_create(cmdq_get_client(item), item, FORMAT_NONE, 0);
|
ft = format_create(cmdq_get_client(item), item, FORMAT_NONE, 0);
|
||||||
format_add(ft, "line", "%u", n);
|
format_add(ft, "line", "%u", n);
|
||||||
format_defaults(ft, NULL, s, wl, NULL);
|
format_defaults(ft, c, s, wl, NULL);
|
||||||
|
|
||||||
if (filter != NULL) {
|
if (filter != NULL) {
|
||||||
expanded = format_expand(ft, filter);
|
expanded = format_expand(ft, filter);
|
||||||
|
|||||||
+17
-7
@@ -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>
|
||||||
@@ -60,7 +60,8 @@ cmd_new_window_exec(struct cmd *self, struct cmdq_item *item)
|
|||||||
struct client *tc = cmdq_get_target_client(item);
|
struct client *tc = cmdq_get_target_client(item);
|
||||||
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 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;
|
||||||
@@ -74,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) {
|
||||||
@@ -88,7 +91,10 @@ 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')) {
|
||||||
|
if (idx != -1)
|
||||||
|
new_wl = winlink_find_by_index(&s->windows, idx);
|
||||||
|
else if (wname != NULL) {
|
||||||
expanded = format_single(item, wname, c, s, NULL, NULL);
|
expanded = format_single(item, wname, c, s, NULL, NULL);
|
||||||
RB_FOREACH(wl, winlinks, &s->windows) {
|
RB_FOREACH(wl, winlinks, &s->windows) {
|
||||||
if (strcmp(wl->window->name, expanded) != 0)
|
if (strcmp(wl->window->name, expanded) != 0)
|
||||||
@@ -97,12 +103,17 @@ cmd_new_window_exec(struct cmd *self, struct cmdq_item *item)
|
|||||||
new_wl = wl;
|
new_wl = wl;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
cmdq_error(item, "multiple windows named %s", wname);
|
cmdq_error(item, "multiple windows named %s",
|
||||||
|
wname);
|
||||||
free(wname);
|
free(wname);
|
||||||
free(expanded);
|
free(expanded);
|
||||||
return (CMD_RETURN_ERROR);
|
return (CMD_RETURN_ERROR);
|
||||||
}
|
}
|
||||||
free(expanded);
|
free(expanded);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Found an existing window to select instead of creating a new one. */
|
||||||
if (new_wl != NULL) {
|
if (new_wl != NULL) {
|
||||||
free(wname);
|
free(wname);
|
||||||
if (args_has(args, 'd'))
|
if (args_has(args, 'd'))
|
||||||
@@ -114,7 +125,6 @@ cmd_new_window_exec(struct cmd *self, struct cmdq_item *item)
|
|||||||
recalculate_sizes();
|
recalculate_sizes();
|
||||||
return (CMD_RETURN_NORMAL);
|
return (CMD_RETURN_NORMAL);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
before = args_has(args, 'b');
|
before = args_has(args, 'b');
|
||||||
if (args_has(args, 'a') || before) {
|
if (args_has(args, 'a') || before) {
|
||||||
|
|||||||
+4
-2
@@ -1,4 +1,4 @@
|
|||||||
/* $OpenBSD: cmd-parse.y,v 1.58 2026/04/27 12:31:11 nicm Exp $ */
|
/* $OpenBSD: cmd-parse.y,v 1.60 2026/09/08 10:20:08 nicm Exp $ */
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2019 Nicholas Marriott <nicholas.marriott@gmail.com>
|
* Copyright (c) 2019 Nicholas Marriott <nicholas.marriott@gmail.com>
|
||||||
@@ -210,7 +210,7 @@ expanded : format
|
|||||||
cmd_find_from_client(&fs, c, 0);
|
cmd_find_from_client(&fs, c, 0);
|
||||||
fsp = &fs;
|
fsp = &fs;
|
||||||
}
|
}
|
||||||
ft = format_create(NULL, pi->item, FORMAT_NONE, flags);
|
ft = format_create(c, pi->item, FORMAT_NONE, flags);
|
||||||
format_defaults(ft, c, fsp->s, fsp->wl, fsp->wp);
|
format_defaults(ft, c, fsp->s, fsp->wl, fsp->wp);
|
||||||
|
|
||||||
$$ = format_expand(ft, $1);
|
$$ = format_expand(ft, $1);
|
||||||
@@ -802,6 +802,7 @@ cmd_parse_expand_alias(struct cmd_parse_command *cmd,
|
|||||||
if (last == NULL) {
|
if (last == NULL) {
|
||||||
pr->status = CMD_PARSE_SUCCESS;
|
pr->status = CMD_PARSE_SUCCESS;
|
||||||
pr->cmdlist = cmd_list_new();
|
pr->cmdlist = cmd_list_new();
|
||||||
|
cmd_parse_free_commands(cmds);
|
||||||
return (1);
|
return (1);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -814,6 +815,7 @@ cmd_parse_expand_alias(struct cmd_parse_command *cmd,
|
|||||||
pi->flags |= CMD_PARSE_NOALIAS;
|
pi->flags |= CMD_PARSE_NOALIAS;
|
||||||
cmd_parse_build_commands(cmds, pi, pr);
|
cmd_parse_build_commands(cmds, pi, pr);
|
||||||
pi->flags &= ~CMD_PARSE_NOALIAS;
|
pi->flags &= ~CMD_PARSE_NOALIAS;
|
||||||
|
cmd_parse_free_commands(cmds);
|
||||||
return (1);
|
return (1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+16
-2
@@ -1,4 +1,4 @@
|
|||||||
/* $OpenBSD: cmd-queue.c,v 1.121 2026/07/14 17:17:17 nicm Exp $ */
|
/* $OpenBSD: cmd-queue.c,v 1.124 2026/09/22 06:46:50 nicm Exp $ */
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2013 Nicholas Marriott <nicholas.marriott@gmail.com>
|
* Copyright (c) 2013 Nicholas Marriott <nicholas.marriott@gmail.com>
|
||||||
@@ -580,6 +580,9 @@ cmdq_fire_command(struct cmdq_item *item)
|
|||||||
int flags, quiet = 0;
|
int flags, quiet = 0;
|
||||||
char *tmp;
|
char *tmp;
|
||||||
|
|
||||||
|
if (item->client != NULL && (item->client->flags & CLIENT_DEAD))
|
||||||
|
return (CMD_RETURN_ERROR);
|
||||||
|
|
||||||
if (cfg_finished)
|
if (cfg_finished)
|
||||||
cmdq_add_message(item);
|
cmdq_add_message(item);
|
||||||
if (log_get_level() > 1) {
|
if (log_get_level() > 1) {
|
||||||
@@ -802,7 +805,7 @@ cmdq_guard(struct cmdq_item *item, const char *guard, int flags)
|
|||||||
u_int number = item->number;
|
u_int number = item->number;
|
||||||
|
|
||||||
if (c != NULL && (c->flags & CLIENT_CONTROL))
|
if (c != NULL && (c->flags & CLIENT_CONTROL))
|
||||||
control_write(c, "%%%s %ld %u %d", guard, t, number, flags);
|
control_write_guard(c, guard, t, number, flags);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Show message from command. */
|
/* Show message from command. */
|
||||||
@@ -850,7 +853,18 @@ cmdq_error(struct cmdq_item *item, const char *fmt, ...)
|
|||||||
|
|
||||||
if (c == NULL) {
|
if (c == NULL) {
|
||||||
cmd_get_source(cmd, &file, &line);
|
cmd_get_source(cmd, &file, &line);
|
||||||
|
if (!cfg_finished) {
|
||||||
|
if (file != NULL)
|
||||||
cfg_add_cause("%s:%u: %s", file, line, msg);
|
cfg_add_cause("%s:%u: %s", file, line, msg);
|
||||||
|
else
|
||||||
|
cfg_add_cause("%s", msg);
|
||||||
|
} else {
|
||||||
|
if (file != NULL) {
|
||||||
|
server_add_message("message: %s:%u: %s", file,
|
||||||
|
line, msg);
|
||||||
|
} else
|
||||||
|
server_add_message("message: %s", msg);
|
||||||
|
}
|
||||||
} else if (c->session == NULL || (c->flags & CLIENT_CONTROL)) {
|
} else if (c->session == NULL || (c->flags & CLIENT_CONTROL)) {
|
||||||
server_add_message("%s message: %s", c->name, msg);
|
server_add_message("%s message: %s", c->name, msg);
|
||||||
if (~c->flags & CLIENT_UTF8) {
|
if (~c->flags & CLIENT_UTF8) {
|
||||||
|
|||||||
+3
-1
@@ -1,4 +1,4 @@
|
|||||||
/* $OpenBSD: cmd-resize-pane.c,v 1.67 2026/07/10 13:38:45 nicm Exp $ */
|
/* $OpenBSD: cmd-resize-pane.c,v 1.69 2026/09/21 10:33:16 nicm Exp $ */
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2009 Nicholas Marriott <nicholas.marriott@gmail.com>
|
* Copyright (c) 2009 Nicholas Marriott <nicholas.marriott@gmail.com>
|
||||||
@@ -91,7 +91,9 @@ cmd_resize_pane_exec(struct cmd *self, struct cmdq_item *item)
|
|||||||
server_redraw_window(w);
|
server_redraw_window(w);
|
||||||
return (CMD_RETURN_NORMAL);
|
return (CMD_RETURN_NORMAL);
|
||||||
}
|
}
|
||||||
|
if (!window_pane_is_floating(wp))
|
||||||
server_unzoom_window(w);
|
server_unzoom_window(w);
|
||||||
|
lc = wp->layout_cell; /* may have been replaced by unzoom */
|
||||||
|
|
||||||
if (args_has(args, 'x')) {
|
if (args_has(args, 'x')) {
|
||||||
x = args_percentage(args, 'x', 0, PANE_MAXIMUM, w->sx, &cause);
|
x = args_percentage(args, 'x', 0, PANE_MAXIMUM, w->sx, &cause);
|
||||||
|
|||||||
+4
-2
@@ -1,4 +1,4 @@
|
|||||||
/* $OpenBSD: cmd-run-shell.c,v 1.93 2026/07/17 12:42:51 nicm Exp $ */
|
/* $OpenBSD: cmd-run-shell.c,v 1.95 2026/09/20 07:59:55 nicm Exp $ */
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2009 Tiago Cunha <me@tiagocunha.org>
|
* Copyright (c) 2009 Tiago Cunha <me@tiagocunha.org>
|
||||||
@@ -164,7 +164,7 @@ cmd_run_shell_exec(struct cmd *self, struct cmdq_item *item)
|
|||||||
if (cdata->client != NULL)
|
if (cdata->client != NULL)
|
||||||
cdata->client->references++;
|
cdata->client->references++;
|
||||||
if (args_has(args, 'c'))
|
if (args_has(args, 'c'))
|
||||||
cdata->cwd = xstrdup(args_get(args, 'c'));
|
cdata->cwd = format_single_from_target(item, args_get(args, 'c'));
|
||||||
else
|
else
|
||||||
cdata->cwd = xstrdup(server_client_get_cwd(c, s));
|
cdata->cwd = xstrdup(server_client_get_cwd(c, s));
|
||||||
|
|
||||||
@@ -233,9 +233,11 @@ cmd_run_shell_timer(__unused int fd, __unused short events, void* arg)
|
|||||||
} else if (item == NULL) {
|
} else if (item == NULL) {
|
||||||
new_item = cmdq_get_command(cmdlist, NULL);
|
new_item = cmdq_get_command(cmdlist, NULL);
|
||||||
cmdq_append(c, new_item);
|
cmdq_append(c, new_item);
|
||||||
|
cmd_list_free(cmdlist);
|
||||||
} else {
|
} else {
|
||||||
new_item = cmdq_get_command(cmdlist, cmdq_get_state(item));
|
new_item = cmdq_get_command(cmdlist, cmdq_get_state(item));
|
||||||
cmdq_insert_after(item, new_item);
|
cmdq_insert_after(item, new_item);
|
||||||
|
cmd_list_free(cmdlist);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (cdata->item != NULL)
|
if (cdata->item != NULL)
|
||||||
|
|||||||
+9
-4
@@ -1,4 +1,4 @@
|
|||||||
/* $OpenBSD: cmd-select-layout.c,v 1.43 2026/07/10 13:38:45 nicm Exp $ */
|
/* $OpenBSD: cmd-select-layout.c,v 1.45 2026/09/09 07:03:39 nicm Exp $ */
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2009 Nicholas Marriott <nicholas.marriott@gmail.com>
|
* Copyright (c) 2009 Nicholas Marriott <nicholas.marriott@gmail.com>
|
||||||
@@ -73,12 +73,13 @@ cmd_select_layout_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 *target = cmdq_get_target(item);
|
struct cmd_find_state *target = cmdq_get_target(item);
|
||||||
|
struct client *c = cmdq_get_target_client(item);
|
||||||
struct winlink *wl = target->wl;
|
struct winlink *wl = target->wl;
|
||||||
struct window *w = wl->window;
|
struct window *w = wl->window;
|
||||||
struct window_pane *wp = target->wp;
|
struct window_pane *wp = target->wp;
|
||||||
const char *layoutname;
|
const char *layoutname;
|
||||||
char *oldlayout, *cause;
|
char *oldlayout, *cause = NULL;
|
||||||
int next, previous, layout;
|
int next, previous, layout, flags = 0;
|
||||||
|
|
||||||
server_unzoom_window(w);
|
server_unzoom_window(w);
|
||||||
|
|
||||||
@@ -89,8 +90,12 @@ cmd_select_layout_exec(struct cmd *self, struct cmdq_item *item)
|
|||||||
if (args_has(args, 'p'))
|
if (args_has(args, 'p'))
|
||||||
previous = 1;
|
previous = 1;
|
||||||
|
|
||||||
|
if (c != NULL &&
|
||||||
|
(c->flags & CLIENT_CONTROL) &&
|
||||||
|
(~c->flags & CLIENT_CONTROL_NEWLAYOUTS))
|
||||||
|
flags |= LAYOUT_CUSTOM_OLD_FORMAT;
|
||||||
oldlayout = w->old_layout;
|
oldlayout = w->old_layout;
|
||||||
w->old_layout = layout_dump(w, w->layout_root);
|
w->old_layout = layout_dump(w, w->layout_root, flags);
|
||||||
|
|
||||||
if (next || previous) {
|
if (next || previous) {
|
||||||
if (next)
|
if (next)
|
||||||
|
|||||||
+14
-5
@@ -1,4 +1,4 @@
|
|||||||
/* $OpenBSD: cmd-select-pane.c,v 1.77 2026/07/17 12:42:51 nicm Exp $ */
|
/* $OpenBSD: cmd-select-pane.c,v 1.78 2026/08/20 09:19:24 nicm Exp $ */
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2009 Nicholas Marriott <nicholas.marriott@gmail.com>
|
* Copyright (c) 2009 Nicholas Marriott <nicholas.marriott@gmail.com>
|
||||||
@@ -160,6 +160,7 @@ cmd_select_pane_exec(struct cmd *self, struct cmdq_item *item)
|
|||||||
char *title;
|
char *title;
|
||||||
const char *style;
|
const char *style;
|
||||||
struct options_entry *o;
|
struct options_entry *o;
|
||||||
|
int visible, Zflag = args_has(args, 'Z');
|
||||||
|
|
||||||
if (entry == &cmd_last_pane_entry || args_has(args, 'l')) {
|
if (entry == &cmd_last_pane_entry || args_has(args, 'l')) {
|
||||||
/*
|
/*
|
||||||
@@ -185,14 +186,18 @@ cmd_select_pane_exec(struct cmd *self, struct cmdq_item *item)
|
|||||||
server_redraw_window_borders(lastwp->window);
|
server_redraw_window_borders(lastwp->window);
|
||||||
server_status_window(lastwp->window);
|
server_status_window(lastwp->window);
|
||||||
} else {
|
} else {
|
||||||
if (window_push_zoom(w, 0, args_has(args, 'Z')))
|
if (w->modal != NULL && lastwp != w->modal)
|
||||||
|
visible = 1;
|
||||||
|
else
|
||||||
|
visible = window_pane_is_visible(lastwp);
|
||||||
|
if (!visible && window_push_zoom(w, 0, Zflag))
|
||||||
server_redraw_window(w);
|
server_redraw_window(w);
|
||||||
window_redraw_active_switch(w, lastwp);
|
window_redraw_active_switch(w, lastwp);
|
||||||
if (window_set_active_pane(w, lastwp, 1)) {
|
if (window_set_active_pane(w, lastwp, 1)) {
|
||||||
cmd_find_from_winlink(current, wl, 0);
|
cmd_find_from_winlink(current, wl, 0);
|
||||||
cmd_select_pane_redraw(w);
|
cmd_select_pane_redraw(w);
|
||||||
}
|
}
|
||||||
if (window_pop_zoom(w))
|
if (!visible && window_pop_zoom(w))
|
||||||
server_redraw_window(w);
|
server_redraw_window(w);
|
||||||
}
|
}
|
||||||
return (CMD_RETURN_NORMAL);
|
return (CMD_RETURN_NORMAL);
|
||||||
@@ -268,14 +273,18 @@ cmd_select_pane_exec(struct cmd *self, struct cmdq_item *item)
|
|||||||
|
|
||||||
if (wp == w->active)
|
if (wp == w->active)
|
||||||
return (CMD_RETURN_NORMAL);
|
return (CMD_RETURN_NORMAL);
|
||||||
if (window_push_zoom(w, 0, args_has(args, 'Z')))
|
if (w->modal != NULL && wp != w->modal)
|
||||||
|
visible = 1;
|
||||||
|
else
|
||||||
|
visible = window_pane_is_visible(wp);
|
||||||
|
if (!visible && window_push_zoom(w, 0, Zflag))
|
||||||
server_redraw_window(w);
|
server_redraw_window(w);
|
||||||
window_redraw_active_switch(w, wp);
|
window_redraw_active_switch(w, wp);
|
||||||
if (window_set_active_pane(w, wp, 1))
|
if (window_set_active_pane(w, wp, 1))
|
||||||
cmd_find_from_winlink_pane(current, wl, wp, 0);
|
cmd_find_from_winlink_pane(current, wl, wp, 0);
|
||||||
cmdq_insert_hook(s, item, current, "after-select-pane");
|
cmdq_insert_hook(s, item, current, "after-select-pane");
|
||||||
cmd_select_pane_redraw(w);
|
cmd_select_pane_redraw(w);
|
||||||
if (window_pop_zoom(w))
|
if (!visible && window_pop_zoom(w))
|
||||||
server_redraw_window(w);
|
server_redraw_window(w);
|
||||||
|
|
||||||
return (CMD_RETURN_NORMAL);
|
return (CMD_RETURN_NORMAL);
|
||||||
|
|||||||
+6
-2
@@ -1,4 +1,4 @@
|
|||||||
/* $OpenBSD: cmd-set-buffer.c,v 1.37 2026/02/15 17:43:26 nicm Exp $ */
|
/* $OpenBSD: cmd-set-buffer.c,v 1.38 2026/08/24 07:05:23 nicm Exp $ */
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>
|
* Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>
|
||||||
@@ -100,6 +100,7 @@ cmd_set_buffer_exec(struct cmd *self, struct cmdq_item *item)
|
|||||||
cmdq_error(item, "%s", cause);
|
cmdq_error(item, "%s", cause);
|
||||||
goto fail;
|
goto fail;
|
||||||
}
|
}
|
||||||
|
free(bufname);
|
||||||
return (CMD_RETURN_NORMAL);
|
return (CMD_RETURN_NORMAL);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -107,8 +108,10 @@ cmd_set_buffer_exec(struct cmd *self, struct cmdq_item *item)
|
|||||||
cmdq_error(item, "no data specified");
|
cmdq_error(item, "no data specified");
|
||||||
goto fail;
|
goto fail;
|
||||||
}
|
}
|
||||||
if ((newsize = strlen(args_string(args, 0))) == 0)
|
if ((newsize = strlen(args_string(args, 0))) == 0) {
|
||||||
|
free(bufname);
|
||||||
return (CMD_RETURN_NORMAL);
|
return (CMD_RETURN_NORMAL);
|
||||||
|
}
|
||||||
|
|
||||||
if (args_has(args, 'a') && pb != NULL) {
|
if (args_has(args, 'a') && pb != NULL) {
|
||||||
olddata = paste_buffer_data(pb, &bufsize);
|
olddata = paste_buffer_data(pb, &bufsize);
|
||||||
@@ -127,6 +130,7 @@ cmd_set_buffer_exec(struct cmd *self, struct cmdq_item *item)
|
|||||||
if (args_has(args, 'w') && tc != NULL)
|
if (args_has(args, 'w') && tc != NULL)
|
||||||
tty_set_selection(&tc->tty, "", bufdata, bufsize);
|
tty_set_selection(&tc->tty, "", bufdata, bufsize);
|
||||||
|
|
||||||
|
free(bufname);
|
||||||
return (CMD_RETURN_NORMAL);
|
return (CMD_RETURN_NORMAL);
|
||||||
|
|
||||||
fail:
|
fail:
|
||||||
|
|||||||
+36
-18
@@ -1,4 +1,4 @@
|
|||||||
/* $OpenBSD: cmd-source-file.c,v 1.62 2025/11/18 08:42:09 nicm Exp $ */
|
/* $OpenBSD: cmd-source-file.c,v 1.63 2026/08/18 08:05:05 nicm Exp $ */
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2008 Tiago Cunha <me@tiagocunha.org>
|
* Copyright (c) 2008 Tiago Cunha <me@tiagocunha.org>
|
||||||
@@ -50,6 +50,7 @@ const struct cmd_entry cmd_source_file_entry = {
|
|||||||
|
|
||||||
struct cmd_source_file_data {
|
struct cmd_source_file_data {
|
||||||
struct cmdq_item *item;
|
struct cmdq_item *item;
|
||||||
|
struct client *client;
|
||||||
int flags;
|
int flags;
|
||||||
|
|
||||||
struct cmdq_item *after;
|
struct cmdq_item *after;
|
||||||
@@ -60,10 +61,24 @@ struct cmd_source_file_data {
|
|||||||
u_int nfiles;
|
u_int nfiles;
|
||||||
};
|
};
|
||||||
|
|
||||||
static enum cmd_retval
|
static void
|
||||||
cmd_source_file_complete_cb(struct cmdq_item *item, __unused void *data)
|
cmd_source_file_free_data(struct cmd_source_file_data *cdata)
|
||||||
{
|
{
|
||||||
struct client *c = cmdq_get_client(item);
|
u_int i;
|
||||||
|
|
||||||
|
for (i = 0; i < cdata->nfiles; i++)
|
||||||
|
free(cdata->files[i]);
|
||||||
|
free(cdata->files);
|
||||||
|
if (cdata->client != NULL)
|
||||||
|
server_client_unref(cdata->client);
|
||||||
|
free(cdata);
|
||||||
|
}
|
||||||
|
|
||||||
|
static enum cmd_retval
|
||||||
|
cmd_source_file_complete_cb(struct cmdq_item *item, void *data)
|
||||||
|
{
|
||||||
|
struct cmd_source_file_data *cdata = data;
|
||||||
|
struct client *c = cdata->client;
|
||||||
|
|
||||||
if (c == NULL) {
|
if (c == NULL) {
|
||||||
cmd_source_file_depth--;
|
cmd_source_file_depth--;
|
||||||
@@ -74,36 +89,36 @@ cmd_source_file_complete_cb(struct cmdq_item *item, __unused void *data)
|
|||||||
}
|
}
|
||||||
|
|
||||||
cfg_print_causes(item);
|
cfg_print_causes(item);
|
||||||
|
cmd_source_file_free_data(cdata);
|
||||||
return (CMD_RETURN_NORMAL);
|
return (CMD_RETURN_NORMAL);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
cmd_source_file_complete(struct client *c, struct cmd_source_file_data *cdata)
|
cmd_source_file_complete(struct cmd_source_file_data *cdata)
|
||||||
{
|
{
|
||||||
|
struct client *c = cdata->client;
|
||||||
struct cmdq_item *new_item;
|
struct cmdq_item *new_item;
|
||||||
u_int i;
|
|
||||||
|
|
||||||
if (cfg_finished) {
|
if (!cfg_finished) {
|
||||||
|
cmd_source_file_free_data(cdata);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (cdata->retval == CMD_RETURN_ERROR &&
|
if (cdata->retval == CMD_RETURN_ERROR &&
|
||||||
c != NULL &&
|
c != NULL &&
|
||||||
c->session == NULL)
|
c->session == NULL)
|
||||||
c->retval = 1;
|
c->retval = 1;
|
||||||
new_item = cmdq_get_callback(cmd_source_file_complete_cb, NULL);
|
new_item = cmdq_get_callback(cmd_source_file_complete_cb, cdata);
|
||||||
cmdq_insert_after(cdata->after, new_item);
|
cmdq_insert_after(cdata->after, new_item);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (i = 0; i < cdata->nfiles; i++)
|
|
||||||
free(cdata->files[i]);
|
|
||||||
free(cdata->files);
|
|
||||||
free(cdata);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
static void
|
||||||
cmd_source_file_done(struct client *c, const char *path, int error,
|
cmd_source_file_done(__unused struct client *oc, const char *path,
|
||||||
int closed, struct evbuffer *buffer, void *data)
|
int error, int closed, struct evbuffer *buffer, void *data)
|
||||||
{
|
{
|
||||||
struct cmd_source_file_data *cdata = data;
|
struct cmd_source_file_data *cdata = data;
|
||||||
struct cmdq_item *item = cdata->item;
|
struct cmdq_item *item = cdata->item;
|
||||||
|
struct client *c = cdata->client;
|
||||||
void *bdata = EVBUFFER_DATA(buffer);
|
void *bdata = EVBUFFER_DATA(buffer);
|
||||||
size_t bsize = EVBUFFER_LENGTH(buffer);
|
size_t bsize = EVBUFFER_LENGTH(buffer);
|
||||||
u_int n;
|
u_int n;
|
||||||
@@ -127,7 +142,7 @@ cmd_source_file_done(struct client *c, const char *path, int error,
|
|||||||
if (n < cdata->nfiles)
|
if (n < cdata->nfiles)
|
||||||
file_read(c, cdata->files[n], cmd_source_file_done, cdata);
|
file_read(c, cdata->files[n], cmd_source_file_done, cdata);
|
||||||
else {
|
else {
|
||||||
cmd_source_file_complete(c, cdata);
|
cmd_source_file_complete(cdata);
|
||||||
cmdq_continue(item);
|
cmdq_continue(item);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -187,6 +202,9 @@ cmd_source_file_exec(struct cmd *self, struct cmdq_item *item)
|
|||||||
|
|
||||||
cdata = xcalloc(1, sizeof *cdata);
|
cdata = xcalloc(1, sizeof *cdata);
|
||||||
cdata->item = item;
|
cdata->item = item;
|
||||||
|
cdata->client = c;
|
||||||
|
if (c != NULL)
|
||||||
|
c->references++;
|
||||||
|
|
||||||
if (args_has(args, 'q'))
|
if (args_has(args, 'q'))
|
||||||
cdata->flags |= CMD_PARSE_QUIET;
|
cdata->flags |= CMD_PARSE_QUIET;
|
||||||
@@ -249,7 +267,7 @@ cmd_source_file_exec(struct cmd *self, struct cmdq_item *item)
|
|||||||
file_read(c, cdata->files[0], cmd_source_file_done, cdata);
|
file_read(c, cdata->files[0], cmd_source_file_done, cdata);
|
||||||
retval = CMD_RETURN_WAIT;
|
retval = CMD_RETURN_WAIT;
|
||||||
} else
|
} else
|
||||||
cmd_source_file_complete(c, cdata);
|
cmd_source_file_complete(cdata);
|
||||||
|
|
||||||
free(cwd);
|
free(cwd);
|
||||||
return (retval);
|
return (retval);
|
||||||
|
|||||||
+29
-7
@@ -1,4 +1,4 @@
|
|||||||
/* $OpenBSD: cmd-split-window.c,v 1.146 2026/07/21 12:28:43 nicm Exp $ */
|
/* $OpenBSD: cmd-split-window.c,v 1.151 2026/09/10 11:02:18 nicm Exp $ */
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2009 Nicholas Marriott <nicholas.marriott@gmail.com>
|
* Copyright (c) 2009 Nicholas Marriott <nicholas.marriott@gmail.com>
|
||||||
@@ -40,8 +40,8 @@ const struct cmd_entry cmd_new_pane_entry = {
|
|||||||
.name = "new-pane",
|
.name = "new-pane",
|
||||||
.alias = "newp",
|
.alias = "newp",
|
||||||
|
|
||||||
.args = { "bB:Cc:de:EfF:hIkl:LMm:Op:PR:s:S:t:T:vWx:X:y:Y:Z", 0, -1, NULL },
|
.args = { "AbB:Cc:Dde:EfF:hIkl:KLMm:Op:PR:s:S:t:T:vWx:X:y:Y:Z", 0, -1, NULL },
|
||||||
.usage = "[-bCdefhIklMOPvWZ] [-B border-lines] "
|
.usage = "[-AbCDefhIkKLMOPvWZ] [-B border-lines] "
|
||||||
"[-c start-directory] [-e environment] "
|
"[-c start-directory] [-e environment] "
|
||||||
"[-F format] [-l size] [-m message] [-p percentage] "
|
"[-F format] [-l size] [-m message] [-p percentage] "
|
||||||
"[-s style] [-S active-border-style] "
|
"[-s style] [-S active-border-style] "
|
||||||
@@ -89,6 +89,7 @@ cmd_split_window_exec(struct cmd *self, struct cmdq_item *item)
|
|||||||
struct cmd_find_state fs;
|
struct cmd_find_state fs;
|
||||||
struct key_event *event = cmdq_get_event(item);
|
struct key_event *event = cmdq_get_event(item);
|
||||||
int input, empty, is_floating, flags = 0;
|
int input, empty, is_floating, flags = 0;
|
||||||
|
int restore_zoom = 0;
|
||||||
const char *template, *style, *value;
|
const char *template, *style, *value;
|
||||||
char *cause = NULL, *cp, *title;
|
char *cause = NULL, *cp, *title;
|
||||||
const struct options_table_entry *oe;
|
const struct options_table_entry *oe;
|
||||||
@@ -96,9 +97,16 @@ cmd_split_window_exec(struct cmd *self, struct cmdq_item *item)
|
|||||||
enum pane_lines lines;
|
enum pane_lines lines;
|
||||||
u_int count = args_count(args);
|
u_int count = args_count(args);
|
||||||
|
|
||||||
|
if (window_active_pane_is_over_zoom(w))
|
||||||
|
restore_zoom = 1;
|
||||||
|
|
||||||
if (cmd_get_entry(self) == &cmd_new_pane_entry)
|
if (cmd_get_entry(self) == &cmd_new_pane_entry)
|
||||||
is_floating = !args_has(args, 'L');
|
is_floating = !args_has(args, 'L');
|
||||||
else {
|
else {
|
||||||
|
if (!window_pane_is_visible(wp))
|
||||||
|
restore_zoom = 0;
|
||||||
|
if (!restore_zoom)
|
||||||
|
window_unzoom(w, 1);
|
||||||
is_floating = window_pane_is_floating(wp);
|
is_floating = window_pane_is_floating(wp);
|
||||||
flags |= SPAWN_SPLIT;
|
flags |= SPAWN_SPLIT;
|
||||||
}
|
}
|
||||||
@@ -132,7 +140,11 @@ cmd_split_window_exec(struct cmd *self, struct cmdq_item *item)
|
|||||||
if (args_has(args, 'Z'))
|
if (args_has(args, 'Z'))
|
||||||
flags |= SPAWN_ZOOM;
|
flags |= SPAWN_ZOOM;
|
||||||
if (args_has(args, 'O'))
|
if (args_has(args, 'O'))
|
||||||
flags |= SPAWN_MODAL;
|
flags |= SPAWN_MODAL|SPAWN_FLOATOVERZOOM;
|
||||||
|
if (is_floating && args_has(args, 'A'))
|
||||||
|
flags |= SPAWN_FLOATOVERZOOM;
|
||||||
|
if ((w->flags & WINDOW_ZOOMED) && (flags & SPAWN_FLOATOVERZOOM))
|
||||||
|
restore_zoom = 1;
|
||||||
|
|
||||||
input = args_has(args, 'I');
|
input = args_has(args, 'I');
|
||||||
if (input || (count == 1 && *args_string(args, 0) == '\0'))
|
if (input || (count == 1 && *args_string(args, 0) == '\0'))
|
||||||
@@ -168,6 +180,8 @@ cmd_split_window_exec(struct cmd *self, struct cmdq_item *item)
|
|||||||
if (cause != NULL) {
|
if (cause != NULL) {
|
||||||
cmdq_error(item, "%s", cause);
|
cmdq_error(item, "%s", cause);
|
||||||
free(cause);
|
free(cause);
|
||||||
|
if (restore_zoom)
|
||||||
|
window_pop_zoom(w);
|
||||||
return (CMD_RETURN_ERROR);
|
return (CMD_RETURN_ERROR);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -201,8 +215,12 @@ cmd_split_window_exec(struct cmd *self, struct cmdq_item *item)
|
|||||||
*/
|
*/
|
||||||
goto fail;
|
goto fail;
|
||||||
}
|
}
|
||||||
|
if (args_has(args, 'K') && args_has(args, 'O'))
|
||||||
|
new_wp->flags |= PANE_CAPTUREALLKEYS;
|
||||||
if (args_has(args, 'C') && args_has(args, 'O'))
|
if (args_has(args, 'C') && args_has(args, 'O'))
|
||||||
new_wp->flags |= PANE_CLOSEONCLICK;
|
new_wp->flags |= PANE_CLOSEONCLICK;
|
||||||
|
if (args_has(args, 'D') && args_has(args, 'O'))
|
||||||
|
new_wp->flags |= PANE_CLOSEONCANCEL;
|
||||||
|
|
||||||
style = args_get(args, 's');
|
style = args_get(args, 's');
|
||||||
if (style != NULL) {
|
if (style != NULL) {
|
||||||
@@ -270,7 +288,10 @@ cmd_split_window_exec(struct cmd *self, struct cmdq_item *item)
|
|||||||
if (~flags & SPAWN_DETACHED)
|
if (~flags & SPAWN_DETACHED)
|
||||||
cmd_find_from_winlink_pane(current, wl, new_wp, 0);
|
cmd_find_from_winlink_pane(current, wl, new_wp, 0);
|
||||||
|
|
||||||
if ((~flags & SPAWN_FLOATING) && !args_has(args, 'O')) {
|
if (restore_zoom) {
|
||||||
|
window_pop_zoom(wp->window);
|
||||||
|
server_redraw_window(wp->window);
|
||||||
|
} else if ((~flags & SPAWN_FLOATING) && !args_has(args, 'O')) {
|
||||||
window_pop_zoom(wp->window);
|
window_pop_zoom(wp->window);
|
||||||
server_redraw_window(wp->window);
|
server_redraw_window(wp->window);
|
||||||
}
|
}
|
||||||
@@ -321,8 +342,9 @@ fail:
|
|||||||
if (!is_floating)
|
if (!is_floating)
|
||||||
layout_close_pane(new_wp);
|
layout_close_pane(new_wp);
|
||||||
window_remove_pane(wp->window, new_wp);
|
window_remove_pane(wp->window, new_wp);
|
||||||
} else if (args_has(args, 'O'))
|
}
|
||||||
window_pop_modal_zoom(wp->window);
|
if (restore_zoom || (~flags & SPAWN_FLOATING))
|
||||||
|
window_pop_zoom(wp->window);
|
||||||
if (sc.argv != NULL)
|
if (sc.argv != NULL)
|
||||||
cmd_free_argv(sc.argc, sc.argv);
|
cmd_free_argv(sc.argc, sc.argv);
|
||||||
environ_free(sc.environ);
|
environ_free(sc.environ);
|
||||||
|
|||||||
+8
-4
@@ -1,4 +1,4 @@
|
|||||||
/* $OpenBSD: cmd-switch-client.c,v 1.74 2026/05/22 15:22:43 nicm Exp $ */
|
/* $OpenBSD: cmd-switch-client.c,v 1.75 2026/08/20 09:19:24 nicm Exp $ */
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>
|
* Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>
|
||||||
@@ -53,7 +53,7 @@ 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;
|
int flags, visible, 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;
|
||||||
@@ -139,11 +139,15 @@ cmd_switch_client_exec(struct cmd *self, struct cmdq_item *item)
|
|||||||
return (CMD_RETURN_NORMAL);
|
return (CMD_RETURN_NORMAL);
|
||||||
if (wl != NULL && wp != NULL && wp != wl->window->active) {
|
if (wl != NULL && wp != NULL && wp != wl->window->active) {
|
||||||
w = wl->window;
|
w = wl->window;
|
||||||
if (window_push_zoom(w, 0, args_has(args, 'Z')))
|
if (w->modal != NULL && wp != w->modal)
|
||||||
|
visible = 1;
|
||||||
|
else
|
||||||
|
visible = window_pane_is_visible(wp);
|
||||||
|
if (!visible && window_push_zoom(w, 0, Zflag))
|
||||||
server_redraw_window(w);
|
server_redraw_window(w);
|
||||||
window_redraw_active_switch(w, wp);
|
window_redraw_active_switch(w, wp);
|
||||||
window_set_active_pane(w, wp, 1);
|
window_set_active_pane(w, wp, 1);
|
||||||
if (window_pop_zoom(w))
|
if (!visible && window_pop_zoom(w))
|
||||||
server_redraw_window(w);
|
server_redraw_window(w);
|
||||||
}
|
}
|
||||||
if (wl != NULL) {
|
if (wl != NULL) {
|
||||||
|
|||||||
+35
-1
@@ -1,4 +1,4 @@
|
|||||||
/* $OpenBSD: cmd-wait-for.c,v 1.23 2026/07/10 13:38:45 nicm Exp $ */
|
/* $OpenBSD: cmd-wait-for.c,v 1.24 2026/09/22 06:46:50 nicm Exp $ */
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2013 Nicholas Marriott <nicholas.marriott@gmail.com>
|
* Copyright (c) 2013 Nicholas Marriott <nicholas.marriott@gmail.com>
|
||||||
@@ -480,6 +480,40 @@ cmd_wait_for_unlock(struct cmdq_item *item, const char *name,
|
|||||||
return (CMD_RETURN_NORMAL);
|
return (CMD_RETURN_NORMAL);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
cmd_wait_for_client_lost(struct client *c)
|
||||||
|
{
|
||||||
|
struct wait_channel *wc, *wc1;
|
||||||
|
struct wait_item *wi, *wi1;
|
||||||
|
struct wait_event_item *wei, *wei1;
|
||||||
|
|
||||||
|
TAILQ_FOREACH_SAFE(wei, &wait_event_items, entry, wei1) {
|
||||||
|
if (cmdq_get_client(wei->item) == c) {
|
||||||
|
TAILQ_REMOVE(&wait_event_items, wei, entry);
|
||||||
|
cmdq_continue(wei->item);
|
||||||
|
cmd_wait_for_event_free(wei);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
RB_FOREACH_SAFE(wc, wait_channels, &wait_channels, wc1) {
|
||||||
|
TAILQ_FOREACH_SAFE(wi, &wc->waiters, entry, wi1) {
|
||||||
|
if (cmdq_get_client(wi->item) == c) {
|
||||||
|
cmdq_continue(wi->item);
|
||||||
|
TAILQ_REMOVE(&wc->waiters, wi, entry);
|
||||||
|
free(wi);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
TAILQ_FOREACH_SAFE(wi, &wc->lockers, entry, wi1) {
|
||||||
|
if (cmdq_get_client(wi->item) == c) {
|
||||||
|
cmdq_continue(wi->item);
|
||||||
|
TAILQ_REMOVE(&wc->lockers, wi, entry);
|
||||||
|
free(wi);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
cmd_wait_for_remove_empty(wc);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
cmd_wait_for_flush(void)
|
cmd_wait_for_flush(void)
|
||||||
{
|
{
|
||||||
|
|||||||
+1
-1
@@ -31,7 +31,7 @@ getpeereid(int s, uid_t *uid, gid_t *gid)
|
|||||||
{
|
{
|
||||||
#ifdef HAVE_SO_PEERCRED
|
#ifdef HAVE_SO_PEERCRED
|
||||||
struct ucred uc;
|
struct ucred uc;
|
||||||
int len = sizeof uc;
|
socklen_t len = sizeof uc;
|
||||||
|
|
||||||
if (getsockopt(s, SOL_SOCKET, SO_PEERCRED, &uc, &len) == -1)
|
if (getsockopt(s, SOL_SOCKET, SO_PEERCRED, &uc, &len) == -1)
|
||||||
return (-1);
|
return (-1);
|
||||||
|
|||||||
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
# configure.ac
|
# configure.ac
|
||||||
|
|
||||||
AC_INIT([tmux], next-3.8)
|
AC_INIT([tmux], 3.8-rc2)
|
||||||
AC_PREREQ([2.60])
|
AC_PREREQ([2.60])
|
||||||
|
|
||||||
AC_CONFIG_AUX_DIR(etc)
|
AC_CONFIG_AUX_DIR(etc)
|
||||||
|
|||||||
+50
-40
@@ -1,4 +1,4 @@
|
|||||||
/* $OpenBSD: control-notify.c,v 1.37 2026/07/10 13:38:45 nicm Exp $ */
|
/* $OpenBSD: control-notify.c,v 1.39 2026/09/08 10:20:08 nicm Exp $ */
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2012 Nicholas Marriott <nicholas.marriott@gmail.com>
|
* Copyright (c) 2012 Nicholas Marriott <nicholas.marriott@gmail.com>
|
||||||
@@ -44,7 +44,8 @@ control_pane_mode_changed_cb(__unused const char *name,
|
|||||||
TAILQ_FOREACH(c, &clients, entry) {
|
TAILQ_FOREACH(c, &clients, entry) {
|
||||||
if (!CONTROL_SHOULD_NOTIFY_CLIENT(c))
|
if (!CONTROL_SHOULD_NOTIFY_CLIENT(c))
|
||||||
continue;
|
continue;
|
||||||
control_write(c, "%%pane-mode-changed %%%u", wp->id);
|
control_notify_write(c, "%%pane-mode-changed %%%u",
|
||||||
|
wp->id);
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -53,8 +54,9 @@ control_pane_mode_changed_cb(__unused const char *name,
|
|||||||
if (value == NULL)
|
if (value == NULL)
|
||||||
return;
|
return;
|
||||||
TAILQ_FOREACH(c, &clients, entry) {
|
TAILQ_FOREACH(c, &clients, entry) {
|
||||||
if (CONTROL_SHOULD_NOTIFY_CLIENT(c))
|
if (!CONTROL_SHOULD_NOTIFY_CLIENT(c))
|
||||||
control_write(c, "%%pane-mode-changed %s", value);
|
continue;
|
||||||
|
control_notify_write(c, "%%pane-mode-changed %s", value);
|
||||||
}
|
}
|
||||||
free(value);
|
free(value);
|
||||||
}
|
}
|
||||||
@@ -68,6 +70,7 @@ control_window_layout_changed_cb(__unused const char *name,
|
|||||||
struct session *s;
|
struct session *s;
|
||||||
struct winlink *wl;
|
struct winlink *wl;
|
||||||
struct window *w = event_payload_get_window(ep, "window");
|
struct window *w = event_payload_get_window(ep, "window");
|
||||||
|
struct format_tree *ft;
|
||||||
const char *template;
|
const char *template;
|
||||||
char *cp;
|
char *cp;
|
||||||
|
|
||||||
@@ -82,20 +85,26 @@ control_window_layout_changed_cb(__unused const char *name,
|
|||||||
* and we don't need to inform the client about the layout change
|
* and we don't need to inform the client about the layout change
|
||||||
* because the whole window will go away soon.
|
* because the whole window will go away soon.
|
||||||
*/
|
*/
|
||||||
wl = TAILQ_FIRST(&w->winlinks);
|
if (TAILQ_FIRST(&w->winlinks) == NULL || w->layout_root == NULL)
|
||||||
if (wl == NULL || w->layout_root == NULL)
|
|
||||||
return;
|
return;
|
||||||
cp = format_single(NULL, template, NULL, NULL, wl, NULL);
|
|
||||||
|
|
||||||
TAILQ_FOREACH(c, &clients, entry) {
|
TAILQ_FOREACH(c, &clients, entry) {
|
||||||
if (!CONTROL_SHOULD_NOTIFY_CLIENT(c) || c->session == NULL)
|
if (!CONTROL_SHOULD_NOTIFY_CLIENT(c) || c->session == NULL)
|
||||||
continue;
|
continue;
|
||||||
s = c->session;
|
s = c->session;
|
||||||
if (winlink_find_by_window_id(&s->windows, w->id) != NULL)
|
wl = winlink_find_by_window_id(&s->windows, w->id);
|
||||||
control_write(c, "%s", cp);
|
if (wl == NULL)
|
||||||
}
|
continue;
|
||||||
|
|
||||||
|
ft = format_create(c, NULL, FORMAT_NONE, 0);
|
||||||
|
format_defaults(ft, c, s, wl, NULL);
|
||||||
|
cp = format_expand(ft, template);
|
||||||
|
format_free(ft);
|
||||||
|
|
||||||
|
control_notify_write(c, "%s", cp);
|
||||||
free(cp);
|
free(cp);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* Notify control clients that window pane changed. */
|
/* Notify control clients that window pane changed. */
|
||||||
static void
|
static void
|
||||||
@@ -111,7 +120,7 @@ control_window_pane_changed_cb(__unused const char *name,
|
|||||||
if (!CONTROL_SHOULD_NOTIFY_CLIENT(c))
|
if (!CONTROL_SHOULD_NOTIFY_CLIENT(c))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
control_write(c, "%%window-pane-changed @%u %%%u", w->id,
|
control_notify_write(c, "%%window-pane-changed @%u %%%u", w->id,
|
||||||
w->active->id);
|
w->active->id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -133,9 +142,11 @@ control_window_unlinked_cb(__unused const char *name, struct event_payload *ep,
|
|||||||
cs = c->session;
|
cs = c->session;
|
||||||
|
|
||||||
if (winlink_find_by_window_id(&cs->windows, w->id) != NULL)
|
if (winlink_find_by_window_id(&cs->windows, w->id) != NULL)
|
||||||
control_write(c, "%%window-close @%u", w->id);
|
control_notify_write(c, "%%window-close @%u", w->id);
|
||||||
else
|
else {
|
||||||
control_write(c, "%%unlinked-window-close @%u", w->id);
|
control_notify_write(c, "%%unlinked-window-close @%u",
|
||||||
|
w->id);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -156,9 +167,11 @@ control_window_linked_cb(__unused const char *name, struct event_payload *ep,
|
|||||||
cs = c->session;
|
cs = c->session;
|
||||||
|
|
||||||
if (winlink_find_by_window_id(&cs->windows, w->id) != NULL)
|
if (winlink_find_by_window_id(&cs->windows, w->id) != NULL)
|
||||||
control_write(c, "%%window-add @%u", w->id);
|
control_notify_write(c, "%%window-add @%u", w->id);
|
||||||
else
|
else {
|
||||||
control_write(c, "%%unlinked-window-add @%u", w->id);
|
control_notify_write(c, "%%unlinked-window-add @%u",
|
||||||
|
w->id);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -179,11 +192,11 @@ control_window_renamed_cb(__unused const char *name, struct event_payload *ep,
|
|||||||
cs = c->session;
|
cs = c->session;
|
||||||
|
|
||||||
if (winlink_find_by_window_id(&cs->windows, w->id) != NULL) {
|
if (winlink_find_by_window_id(&cs->windows, w->id) != NULL) {
|
||||||
control_write(c, "%%window-renamed @%u %s", w->id,
|
control_notify_write(c, "%%window-renamed @%u %s",
|
||||||
w->name);
|
|
||||||
} else {
|
|
||||||
control_write(c, "%%unlinked-window-renamed @%u %s",
|
|
||||||
w->id, w->name);
|
w->id, w->name);
|
||||||
|
} else {
|
||||||
|
control_notify_write(c,
|
||||||
|
"%%unlinked-window-renamed @%u %s", w->id, w->name);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -206,11 +219,12 @@ control_client_session_changed_cb(__unused const char *name,
|
|||||||
continue;
|
continue;
|
||||||
|
|
||||||
if (cc == c) {
|
if (cc == c) {
|
||||||
control_write(c, "%%session-changed $%u %s", s->id,
|
control_notify_write(c, "%%session-changed $%u %s",
|
||||||
s->name);
|
s->id, s->name);
|
||||||
} else {
|
} else {
|
||||||
control_write(c, "%%client-session-changed %s $%u %s",
|
control_notify_write(c,
|
||||||
cc->name, s->id, s->name);
|
"%%client-session-changed %s $%u %s", cc->name,
|
||||||
|
s->id, s->name);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -226,8 +240,9 @@ control_client_detached_cb(__unused const char *name, struct event_payload *ep,
|
|||||||
if (cc == NULL)
|
if (cc == NULL)
|
||||||
return;
|
return;
|
||||||
TAILQ_FOREACH(c, &clients, entry) {
|
TAILQ_FOREACH(c, &clients, entry) {
|
||||||
if (CONTROL_SHOULD_NOTIFY_CLIENT(c))
|
if (!CONTROL_SHOULD_NOTIFY_CLIENT(c))
|
||||||
control_write(c, "%%client-detached %s", cc->name);
|
continue;
|
||||||
|
control_notify_write(c, "%%client-detached %s", cc->name);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -244,8 +259,8 @@ control_session_renamed_cb(__unused const char *name, struct event_payload *ep,
|
|||||||
TAILQ_FOREACH(c, &clients, entry) {
|
TAILQ_FOREACH(c, &clients, entry) {
|
||||||
if (!CONTROL_SHOULD_NOTIFY_CLIENT(c))
|
if (!CONTROL_SHOULD_NOTIFY_CLIENT(c))
|
||||||
continue;
|
continue;
|
||||||
|
control_notify_write(c, "%%session-renamed $%u %s", s->id,
|
||||||
control_write(c, "%%session-renamed $%u %s", s->id, s->name);
|
s->name);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -259,8 +274,7 @@ control_session_created_cb(__unused const char *name,
|
|||||||
TAILQ_FOREACH(c, &clients, entry) {
|
TAILQ_FOREACH(c, &clients, entry) {
|
||||||
if (!CONTROL_SHOULD_NOTIFY_CLIENT(c))
|
if (!CONTROL_SHOULD_NOTIFY_CLIENT(c))
|
||||||
continue;
|
continue;
|
||||||
|
control_notify_write(c, "%%sessions-changed");
|
||||||
control_write(c, "%%sessions-changed");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -274,8 +288,7 @@ control_session_closed_cb(__unused const char *name,
|
|||||||
TAILQ_FOREACH(c, &clients, entry) {
|
TAILQ_FOREACH(c, &clients, entry) {
|
||||||
if (!CONTROL_SHOULD_NOTIFY_CLIENT(c))
|
if (!CONTROL_SHOULD_NOTIFY_CLIENT(c))
|
||||||
continue;
|
continue;
|
||||||
|
control_notify_write(c, "%%sessions-changed");
|
||||||
control_write(c, "%%sessions-changed");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -298,9 +311,8 @@ control_session_window_changed_cb(__unused const char *name,
|
|||||||
TAILQ_FOREACH(c, &clients, entry) {
|
TAILQ_FOREACH(c, &clients, entry) {
|
||||||
if (!CONTROL_SHOULD_NOTIFY_CLIENT(c))
|
if (!CONTROL_SHOULD_NOTIFY_CLIENT(c))
|
||||||
continue;
|
continue;
|
||||||
|
control_notify_write(c, "%%session-window-changed $%u @%u",
|
||||||
control_write(c, "%%session-window-changed $%u @%u", s->id,
|
s->id, s->curw->window->id);
|
||||||
s->curw->window->id);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -317,8 +329,7 @@ control_paste_buffer_changed_cb(__unused const char *name,
|
|||||||
TAILQ_FOREACH(c, &clients, entry) {
|
TAILQ_FOREACH(c, &clients, entry) {
|
||||||
if (!CONTROL_SHOULD_NOTIFY_CLIENT(c))
|
if (!CONTROL_SHOULD_NOTIFY_CLIENT(c))
|
||||||
continue;
|
continue;
|
||||||
|
control_notify_write(c, "%%paste-buffer-changed %s", pbname);
|
||||||
control_write(c, "%%paste-buffer-changed %s", pbname);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -335,8 +346,7 @@ control_paste_buffer_deleted_cb(__unused const char *name,
|
|||||||
TAILQ_FOREACH(c, &clients, entry) {
|
TAILQ_FOREACH(c, &clients, entry) {
|
||||||
if (!CONTROL_SHOULD_NOTIFY_CLIENT(c))
|
if (!CONTROL_SHOULD_NOTIFY_CLIENT(c))
|
||||||
continue;
|
continue;
|
||||||
|
control_notify_write(c, "%%paste-buffer-deleted %s", pbname);
|
||||||
control_write(c, "%%paste-buffer-deleted %s", pbname);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
/* $OpenBSD: control.c,v 1.62 2026/07/17 08:37:29 nicm Exp $ */
|
/* $OpenBSD: control.c,v 1.68 2026/09/09 08:30:05 nicm Exp $ */
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2012 Nicholas Marriott <nicholas.marriott@gmail.com>
|
* Copyright (c) 2012 Nicholas Marriott <nicholas.marriott@gmail.com>
|
||||||
@@ -53,6 +53,17 @@ struct control_block {
|
|||||||
TAILQ_ENTRY(control_block) all_entry;
|
TAILQ_ENTRY(control_block) all_entry;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
* A notification line deferred because it was generated while a command's
|
||||||
|
* %begin/%end guard block was open. Notifications must never appear inside a
|
||||||
|
* guard block, so they are held here and flushed once the block closes.
|
||||||
|
*/
|
||||||
|
struct control_line {
|
||||||
|
char *line;
|
||||||
|
|
||||||
|
TAILQ_ENTRY(control_line) entry;
|
||||||
|
};
|
||||||
|
|
||||||
/* Control client pane. */
|
/* Control client pane. */
|
||||||
struct control_pane {
|
struct control_pane {
|
||||||
u_int pane;
|
u_int pane;
|
||||||
@@ -97,11 +108,19 @@ struct control_state {
|
|||||||
u_int pending_count;
|
u_int pending_count;
|
||||||
|
|
||||||
TAILQ_HEAD(, control_block) all_blocks;
|
TAILQ_HEAD(, control_block) all_blocks;
|
||||||
|
size_t queued_reply_bytes;
|
||||||
|
|
||||||
struct bufferevent *read_event;
|
struct bufferevent *read_event;
|
||||||
struct bufferevent *write_event;
|
struct bufferevent *write_event;
|
||||||
|
|
||||||
struct monitor_set *subs;
|
struct monitor_set *subs;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Depth of open %begin/%end guard blocks and notifications deferred
|
||||||
|
* until the outermost block closes.
|
||||||
|
*/
|
||||||
|
int guard_depth;
|
||||||
|
TAILQ_HEAD(, control_line) deferred;
|
||||||
};
|
};
|
||||||
|
|
||||||
/* Low and high watermarks. */
|
/* Low and high watermarks. */
|
||||||
@@ -114,6 +133,9 @@ struct control_state {
|
|||||||
/* Maximum age for clients that are not using pause mode. */
|
/* Maximum age for clients that are not using pause mode. */
|
||||||
#define CONTROL_MAXIMUM_AGE 300000
|
#define CONTROL_MAXIMUM_AGE 300000
|
||||||
|
|
||||||
|
/* Maximum buffered command replies for a client that is not reading. */
|
||||||
|
#define CONTROL_MAXIMUM_REPLY_BUFFER (64 * 1024 * 1024)
|
||||||
|
|
||||||
/* Flags to ignore client. */
|
/* Flags to ignore client. */
|
||||||
#define CONTROL_IGNORE_FLAGS \
|
#define CONTROL_IGNORE_FLAGS \
|
||||||
(CLIENT_CONTROL_NOOUTPUT| \
|
(CLIENT_CONTROL_NOOUTPUT| \
|
||||||
@@ -147,6 +169,15 @@ RB_GENERATE_STATIC(control_windows, control_window, entry, control_window_cmp);
|
|||||||
static void
|
static void
|
||||||
control_free_block(struct control_state *cs, struct control_block *cb)
|
control_free_block(struct control_state *cs, struct control_block *cb)
|
||||||
{
|
{
|
||||||
|
size_t size;
|
||||||
|
|
||||||
|
if (cb->size == 0 && cb->line != NULL) {
|
||||||
|
size = strlen(cb->line) + 1;
|
||||||
|
if (cs->queued_reply_bytes > size)
|
||||||
|
cs->queued_reply_bytes -= size;
|
||||||
|
else
|
||||||
|
cs->queued_reply_bytes = 0;
|
||||||
|
}
|
||||||
free(cb->line);
|
free(cb->line);
|
||||||
TAILQ_REMOVE(&cs->all_blocks, cb, all_entry);
|
TAILQ_REMOVE(&cs->all_blocks, cb, all_entry);
|
||||||
free(cb);
|
free(cb);
|
||||||
@@ -352,7 +383,7 @@ control_continue_pane(struct client *c, struct window_pane *wp)
|
|||||||
cp->flags &= ~CONTROL_PANE_PAUSED;
|
cp->flags &= ~CONTROL_PANE_PAUSED;
|
||||||
memcpy(&cp->offset, &wp->offset, sizeof cp->offset);
|
memcpy(&cp->offset, &wp->offset, sizeof cp->offset);
|
||||||
memcpy(&cp->queued, &wp->offset, sizeof cp->queued);
|
memcpy(&cp->queued, &wp->offset, sizeof cp->queued);
|
||||||
control_write(c, "%%continue %%%u", wp->id);
|
control_notify_write(c, "%%continue %%%u", wp->id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -366,52 +397,186 @@ control_pause_pane(struct client *c, struct window_pane *wp)
|
|||||||
if (~cp->flags & CONTROL_PANE_PAUSED) {
|
if (~cp->flags & CONTROL_PANE_PAUSED) {
|
||||||
cp->flags |= CONTROL_PANE_PAUSED;
|
cp->flags |= CONTROL_PANE_PAUSED;
|
||||||
control_discard_pane(c, cp);
|
control_discard_pane(c, cp);
|
||||||
control_write(c, "%%pause %%%u", wp->id);
|
control_notify_write(c, "%%pause %%%u", wp->id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Write a line. */
|
/*
|
||||||
static void printflike(2, 0)
|
* Reset a pane after its buffer has been replaced: drop any output still
|
||||||
control_vwrite(struct client *c, const char *fmt, va_list ap)
|
* queued from the old buffer and start again from the pane's own offset.
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
control_reset_pane(struct client *c, struct window_pane *wp)
|
||||||
|
{
|
||||||
|
struct control_pane *cp;
|
||||||
|
|
||||||
|
if (c->control_state == NULL)
|
||||||
|
return;
|
||||||
|
cp = control_get_pane(c, wp);
|
||||||
|
if (cp == NULL)
|
||||||
|
return;
|
||||||
|
control_discard_pane(c, cp);
|
||||||
|
memcpy(&cp->offset, &wp->offset, sizeof cp->offset);
|
||||||
|
memcpy(&cp->queued, &wp->offset, sizeof cp->queued);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Check if the replies buffered for a client, including one about to be
|
||||||
|
* added, have grown too large and kill it if so. Returns 1 if further output
|
||||||
|
* for the client should be dropped.
|
||||||
|
*/
|
||||||
|
static int
|
||||||
|
control_check_reply_buffer(struct client *c, size_t added)
|
||||||
{
|
{
|
||||||
struct control_state *cs = c->control_state;
|
struct control_state *cs = c->control_state;
|
||||||
char *s;
|
size_t size;
|
||||||
|
|
||||||
xvasprintf(&s, fmt, ap);
|
if (c->flags & CLIENT_CONTROL_DISCARD)
|
||||||
log_debug("%s: %s: writing line: %s", __func__, c->name, s);
|
return (1);
|
||||||
|
size = EVBUFFER_LENGTH(cs->write_event->output);
|
||||||
|
size += cs->queued_reply_bytes;
|
||||||
|
size += added;
|
||||||
|
if (size < CONTROL_MAXIMUM_REPLY_BUFFER)
|
||||||
|
return (0);
|
||||||
|
|
||||||
bufferevent_write(cs->write_event, s, strlen(s));
|
log_debug("%s: %s: %zu bytes of replies buffered", __func__, c->name,
|
||||||
bufferevent_write(cs->write_event, "\n", 1);
|
size);
|
||||||
|
if (~c->flags & CLIENT_EXIT) {
|
||||||
bufferevent_enable(cs->write_event, EV_WRITE);
|
c->exit_message = xstrdup("too far behind");
|
||||||
free(s);
|
c->flags |= CLIENT_EXIT;
|
||||||
|
control_discard(c);
|
||||||
|
}
|
||||||
|
c->flags |= CLIENT_CONTROL_DISCARD;
|
||||||
|
return (1);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Write a line. */
|
/* Write an already-formatted line, queueing it behind %output if needed. */
|
||||||
void
|
static void
|
||||||
control_write(struct client *c, const char *fmt, ...)
|
control_write_line(struct client *c, char *line)
|
||||||
{
|
{
|
||||||
struct control_state *cs = c->control_state;
|
struct control_state *cs = c->control_state;
|
||||||
struct control_block *cb;
|
struct control_block *cb;
|
||||||
va_list ap;
|
size_t size = strlen(line) + 1;
|
||||||
|
|
||||||
va_start(ap, fmt);
|
if (control_check_reply_buffer(c, size)) {
|
||||||
|
free(line);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (TAILQ_EMPTY(&cs->all_blocks)) {
|
if (TAILQ_EMPTY(&cs->all_blocks)) {
|
||||||
control_vwrite(c, fmt, ap);
|
log_debug("%s: %s: writing line: %s", __func__, c->name, line);
|
||||||
va_end(ap);
|
bufferevent_write(cs->write_event, line, size - 1);
|
||||||
|
bufferevent_write(cs->write_event, "\n", 1);
|
||||||
|
bufferevent_enable(cs->write_event, EV_WRITE);
|
||||||
|
free(line);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
cb = xcalloc(1, sizeof *cb);
|
cb = xcalloc(1, sizeof *cb);
|
||||||
xvasprintf(&cb->line, fmt, ap);
|
cb->line = line;
|
||||||
TAILQ_INSERT_TAIL(&cs->all_blocks, cb, all_entry);
|
TAILQ_INSERT_TAIL(&cs->all_blocks, cb, all_entry);
|
||||||
|
cs->queued_reply_bytes += size;
|
||||||
cb->t = get_timer();
|
cb->t = get_timer();
|
||||||
|
|
||||||
log_debug("%s: %s: storing line: %s", __func__, c->name, cb->line);
|
log_debug("%s: %s: storing line: %s", __func__, c->name, cb->line);
|
||||||
bufferevent_enable(cs->write_event, EV_WRITE);
|
bufferevent_enable(cs->write_event, EV_WRITE);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Flush notifications that were deferred while a guard block was open. */
|
||||||
|
static void
|
||||||
|
control_flush_deferred(struct client *c)
|
||||||
|
{
|
||||||
|
struct control_state *cs = c->control_state;
|
||||||
|
struct control_line *cl, *cl1;
|
||||||
|
|
||||||
|
TAILQ_FOREACH_SAFE(cl, &cs->deferred, entry, cl1) {
|
||||||
|
TAILQ_REMOVE(&cs->deferred, cl, entry);
|
||||||
|
control_write_line(c, cl->line);
|
||||||
|
free(cl);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Write a line of command output or error text. This is a sink for arbitrary
|
||||||
|
* user-controlled text (command output, capture-pane, error messages), so it
|
||||||
|
* must never try to interpret the content: guard tracking is done only in
|
||||||
|
* control_write_guard.
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
control_write(struct client *c, const char *fmt, ...)
|
||||||
|
{
|
||||||
|
struct control_state *cs = c->control_state;
|
||||||
|
char *line;
|
||||||
|
va_list ap;
|
||||||
|
|
||||||
|
if (cs == NULL)
|
||||||
|
return;
|
||||||
|
|
||||||
|
va_start(ap, fmt);
|
||||||
|
xvasprintf(&line, fmt, ap);
|
||||||
va_end(ap);
|
va_end(ap);
|
||||||
|
|
||||||
|
control_write_line(c, line);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Write a %begin, %end or %error guard around a command's output. This is the
|
||||||
|
* only place guard lines are produced, so the block depth is maintained here;
|
||||||
|
* when the outermost block closes any deferred notifications are flushed after
|
||||||
|
* it. "guard" is always one of the fixed strings from cmdq_guard, never user
|
||||||
|
* text.
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
control_write_guard(struct client *c, const char *guard, long t, u_int number,
|
||||||
|
int flags)
|
||||||
|
{
|
||||||
|
struct control_state *cs = c->control_state;
|
||||||
|
char *line;
|
||||||
|
|
||||||
|
if (cs == NULL)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (strcmp(guard, "begin") == 0)
|
||||||
|
cs->guard_depth++;
|
||||||
|
|
||||||
|
xasprintf(&line, "%%%s %ld %u %d", guard, t, number, flags);
|
||||||
|
control_write_line(c, line);
|
||||||
|
|
||||||
|
if (strcmp(guard, "begin") != 0 && cs->guard_depth > 0 &&
|
||||||
|
--cs->guard_depth == 0)
|
||||||
|
control_flush_deferred(c);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Write a notification line. Notifications must never appear inside a command's
|
||||||
|
* %begin/%end guard block, so if one is open the line is deferred until it
|
||||||
|
* closes.
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
control_notify_write(struct client *c, const char *fmt, ...)
|
||||||
|
{
|
||||||
|
struct control_state *cs = c->control_state;
|
||||||
|
struct control_line *cl;
|
||||||
|
va_list ap;
|
||||||
|
char *line;
|
||||||
|
|
||||||
|
if (cs == NULL)
|
||||||
|
return;
|
||||||
|
|
||||||
|
va_start(ap, fmt);
|
||||||
|
xvasprintf(&line, fmt, ap);
|
||||||
|
va_end(ap);
|
||||||
|
|
||||||
|
if (cs->guard_depth == 0) {
|
||||||
|
control_write_line(c, line);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
log_debug("%s: %s: deferring notification: %s", __func__, c->name,
|
||||||
|
line);
|
||||||
|
cl = xcalloc(1, sizeof *cl);
|
||||||
|
cl->line = line;
|
||||||
|
TAILQ_INSERT_TAIL(&cs->deferred, cl, entry);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Check age for this pane. */
|
/* Check age for this pane. */
|
||||||
@@ -438,7 +603,7 @@ control_check_age(struct client *c, struct window_pane *wp,
|
|||||||
return (0);
|
return (0);
|
||||||
cp->flags |= CONTROL_PANE_PAUSED;
|
cp->flags |= CONTROL_PANE_PAUSED;
|
||||||
control_discard_pane(c, cp);
|
control_discard_pane(c, cp);
|
||||||
control_write(c, "%%pause %%%u", wp->id);
|
control_notify_write(c, "%%pause %%%u", wp->id);
|
||||||
} else {
|
} else {
|
||||||
if (age < CONTROL_MAXIMUM_AGE)
|
if (age < CONTROL_MAXIMUM_AGE)
|
||||||
return (0);
|
return (0);
|
||||||
@@ -805,14 +970,17 @@ control_sub_change(struct monitor_change *change, __unused void *data)
|
|||||||
|
|
||||||
if (wp != NULL) {
|
if (wp != NULL) {
|
||||||
w = wp->window;
|
w = wp->window;
|
||||||
control_write(c, "%%subscription-changed %s $%u @%u %u %%%u : %s",
|
control_notify_write(c,
|
||||||
|
"%%subscription-changed %s $%u @%u %u %%%u : %s",
|
||||||
change->name, s->id, w->id, wl->idx, wp->id, change->value);
|
change->name, s->id, w->id, wl->idx, wp->id, change->value);
|
||||||
} else if (wl != NULL) {
|
} else if (wl != NULL) {
|
||||||
w = wl->window;
|
w = wl->window;
|
||||||
control_write(c, "%%subscription-changed %s $%u @%u %u - : %s",
|
control_notify_write(c,
|
||||||
|
"%%subscription-changed %s $%u @%u %u - : %s",
|
||||||
change->name, s->id, w->id, wl->idx, change->value);
|
change->name, s->id, w->id, wl->idx, change->value);
|
||||||
} else {
|
} else {
|
||||||
control_write(c, "%%subscription-changed %s $%u - - - : %s",
|
control_notify_write(c,
|
||||||
|
"%%subscription-changed %s $%u - - - : %s",
|
||||||
change->name, s->id, change->value);
|
change->name, s->id, change->value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -835,6 +1003,7 @@ control_start(struct client *c)
|
|||||||
RB_INIT(&cs->windows);
|
RB_INIT(&cs->windows);
|
||||||
TAILQ_INIT(&cs->pending_list);
|
TAILQ_INIT(&cs->pending_list);
|
||||||
TAILQ_INIT(&cs->all_blocks);
|
TAILQ_INIT(&cs->all_blocks);
|
||||||
|
TAILQ_INIT(&cs->deferred);
|
||||||
cs->subs = monitor_create_client(c, control_sub_change, NULL);
|
cs->subs = monitor_create_client(c, control_sub_change, NULL);
|
||||||
|
|
||||||
cs->read_event = bufferevent_new(c->fd, control_read_callback,
|
cs->read_event = bufferevent_new(c->fd, control_read_callback,
|
||||||
@@ -878,6 +1047,20 @@ control_discard(struct client *c)
|
|||||||
bufferevent_disable(cs->read_event, EV_READ);
|
bufferevent_disable(cs->read_event, EV_READ);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Discard all tmux-owned queued control blocks and stop writing. */
|
||||||
|
void
|
||||||
|
control_discard_all(struct client *c)
|
||||||
|
{
|
||||||
|
struct control_state *cs = c->control_state;
|
||||||
|
struct control_block *cb, *cb1;
|
||||||
|
|
||||||
|
control_discard(c);
|
||||||
|
TAILQ_FOREACH_SAFE(cb, &cs->all_blocks, all_entry, cb1)
|
||||||
|
control_free_block(cs, cb);
|
||||||
|
cs->queued_reply_bytes = 0;
|
||||||
|
bufferevent_disable(cs->write_event, EV_WRITE);
|
||||||
|
}
|
||||||
|
|
||||||
/* Stop control mode. */
|
/* Stop control mode. */
|
||||||
void
|
void
|
||||||
control_stop(struct client *c)
|
control_stop(struct client *c)
|
||||||
@@ -885,12 +1068,19 @@ control_stop(struct client *c)
|
|||||||
struct control_state *cs = c->control_state;
|
struct control_state *cs = c->control_state;
|
||||||
struct control_block *cb, *cb1;
|
struct control_block *cb, *cb1;
|
||||||
struct control_window *cw, *cw1;
|
struct control_window *cw, *cw1;
|
||||||
|
struct control_line *cl, *cl1;
|
||||||
|
|
||||||
if (cs == NULL)
|
if (cs == NULL)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
monitor_destroy(cs->subs);
|
monitor_destroy(cs->subs);
|
||||||
|
|
||||||
|
TAILQ_FOREACH_SAFE(cl, &cs->deferred, entry, cl1) {
|
||||||
|
TAILQ_REMOVE(&cs->deferred, cl, entry);
|
||||||
|
free(cl->line);
|
||||||
|
free(cl);
|
||||||
|
}
|
||||||
|
|
||||||
if (~c->flags & CLIENT_CONTROLCONTROL)
|
if (~c->flags & CLIENT_CONTROLCONTROL)
|
||||||
bufferevent_free(cs->write_event);
|
bufferevent_free(cs->write_event);
|
||||||
bufferevent_free(cs->read_event);
|
bufferevent_free(cs->read_event);
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
/* $OpenBSD: file.c,v 1.21 2026/07/26 15:08:15 nicm Exp $ */
|
/* $OpenBSD: file.c,v 1.22 2026/08/17 07:56:56 nicm Exp $ */
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2019 Nicholas Marriott <nicholas.marriott@gmail.com>
|
* Copyright (c) 2019 Nicholas Marriott <nicholas.marriott@gmail.com>
|
||||||
@@ -502,6 +502,7 @@ file_push(struct client_file *cf)
|
|||||||
} else if (cf->stream > 2) {
|
} else if (cf->stream > 2) {
|
||||||
close.stream = cf->stream;
|
close.stream = cf->stream;
|
||||||
proc_send(cf->peer, MSG_WRITE_CLOSE, -1, &close, sizeof close);
|
proc_send(cf->peer, MSG_WRITE_CLOSE, -1, &close, sizeof close);
|
||||||
|
if (cf->c == NULL || (~cf->c->flags & CLIENT_WRITE_ACK))
|
||||||
file_fire_done(cf);
|
file_fire_done(cf);
|
||||||
}
|
}
|
||||||
free(msg);
|
free(msg);
|
||||||
@@ -527,14 +528,48 @@ file_write_left(struct client_files *files)
|
|||||||
return (waiting != 0);
|
return (waiting != 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Finish writing a client file. */
|
||||||
|
static void
|
||||||
|
file_write_finished(struct client_file *cf)
|
||||||
|
{
|
||||||
|
struct msg_write_done msg;
|
||||||
|
|
||||||
|
if (cf->event != NULL) {
|
||||||
|
bufferevent_free(cf->event);
|
||||||
|
cf->event = NULL;
|
||||||
|
}
|
||||||
|
if (cf->fd != -1) {
|
||||||
|
if (close(cf->fd) != 0 && cf->error == 0)
|
||||||
|
cf->error = errno;
|
||||||
|
cf->fd = -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
msg.stream = cf->stream;
|
||||||
|
msg.error = cf->error;
|
||||||
|
proc_send(cf->peer, MSG_WRITE_DONE, -1, &msg, sizeof msg);
|
||||||
|
|
||||||
|
if (cf->cb != NULL)
|
||||||
|
cf->cb(NULL, NULL, 0, -1, NULL, cf->data);
|
||||||
|
file_free(cf);
|
||||||
|
}
|
||||||
|
|
||||||
/* Client file write error callback. */
|
/* Client file write error callback. */
|
||||||
static void
|
static void
|
||||||
file_write_error_callback(__unused struct bufferevent *bev, __unused short what,
|
file_write_error_callback(__unused struct bufferevent *bev, short what,
|
||||||
void *arg)
|
void *arg)
|
||||||
{
|
{
|
||||||
struct client_file *cf = arg;
|
struct client_file *cf = arg;
|
||||||
|
int error;
|
||||||
|
|
||||||
|
if (what & EVBUFFER_ERROR)
|
||||||
|
error = errno;
|
||||||
|
else
|
||||||
|
error = EIO;
|
||||||
|
if (error == 0)
|
||||||
|
error = EIO;
|
||||||
|
|
||||||
log_debug("write error file %d", cf->stream);
|
log_debug("write error file %d", cf->stream);
|
||||||
|
cf->error = error;
|
||||||
|
|
||||||
bufferevent_free(cf->event);
|
bufferevent_free(cf->event);
|
||||||
cf->event = NULL;
|
cf->event = NULL;
|
||||||
@@ -542,7 +577,9 @@ file_write_error_callback(__unused struct bufferevent *bev, __unused short what,
|
|||||||
close(cf->fd);
|
close(cf->fd);
|
||||||
cf->fd = -1;
|
cf->fd = -1;
|
||||||
|
|
||||||
if (cf->cb != NULL)
|
if (cf->closed)
|
||||||
|
file_write_finished(cf);
|
||||||
|
else if (cf->cb != NULL)
|
||||||
cf->cb(NULL, NULL, 0, -1, NULL, cf->data);
|
cf->cb(NULL, NULL, 0, -1, NULL, cf->data);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -554,15 +591,10 @@ file_write_callback(__unused struct bufferevent *bev, void *arg)
|
|||||||
|
|
||||||
log_debug("write check file %d", cf->stream);
|
log_debug("write check file %d", cf->stream);
|
||||||
|
|
||||||
if (cf->cb != NULL)
|
if (cf->closed && EVBUFFER_LENGTH(cf->event->output) == 0)
|
||||||
|
file_write_finished(cf);
|
||||||
|
else if (cf->cb != NULL)
|
||||||
cf->cb(NULL, NULL, 0, -1, NULL, cf->data);
|
cf->cb(NULL, NULL, 0, -1, NULL, cf->data);
|
||||||
|
|
||||||
if (cf->closed && EVBUFFER_LENGTH(cf->event->output) == 0) {
|
|
||||||
bufferevent_free(cf->event);
|
|
||||||
close(cf->fd);
|
|
||||||
RB_REMOVE(client_files, cf->tree, cf);
|
|
||||||
file_free(cf);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Handle a file write open message (client). */
|
/* Handle a file write open message (client). */
|
||||||
@@ -663,14 +695,10 @@ file_write_close(struct client_files *files, struct imsg *imsg)
|
|||||||
if ((cf = RB_FIND(client_files, files, &find)) == NULL)
|
if ((cf = RB_FIND(client_files, files, &find)) == NULL)
|
||||||
fatalx("unknown stream number");
|
fatalx("unknown stream number");
|
||||||
log_debug("close file %d", cf->stream);
|
log_debug("close file %d", cf->stream);
|
||||||
|
cf->closed = 1;
|
||||||
|
|
||||||
if (cf->event == NULL || EVBUFFER_LENGTH(cf->event->output) == 0) {
|
if (cf->event == NULL || EVBUFFER_LENGTH(cf->event->output) == 0) {
|
||||||
if (cf->event != NULL)
|
file_write_finished(cf);
|
||||||
bufferevent_free(cf->event);
|
|
||||||
if (cf->fd != -1)
|
|
||||||
close(cf->fd);
|
|
||||||
RB_REMOVE(client_files, files, cf);
|
|
||||||
file_free(cf);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -829,6 +857,28 @@ file_write_ready(struct client_files *files, struct imsg *imsg)
|
|||||||
return (0);
|
return (0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Handle a write done message (server). */
|
||||||
|
int
|
||||||
|
file_write_done(struct client_files *files, struct imsg *imsg)
|
||||||
|
{
|
||||||
|
struct msg_write_done *msg = imsg->data;
|
||||||
|
size_t msglen = imsg->hdr.len - IMSG_HEADER_SIZE;
|
||||||
|
struct client_file find, *cf;
|
||||||
|
|
||||||
|
if (msglen != sizeof *msg)
|
||||||
|
return (-1);
|
||||||
|
find.stream = msg->stream;
|
||||||
|
if ((cf = RB_FIND(client_files, files, &find)) == NULL)
|
||||||
|
return (0);
|
||||||
|
if (cf->c == NULL || (~cf->c->flags & CLIENT_WRITE_ACK))
|
||||||
|
return (0);
|
||||||
|
|
||||||
|
log_debug("file %d write done", cf->stream);
|
||||||
|
cf->error = msg->error;
|
||||||
|
file_fire_done(cf);
|
||||||
|
return (0);
|
||||||
|
}
|
||||||
|
|
||||||
/* Handle read data message (server). */
|
/* Handle read data message (server). */
|
||||||
int
|
int
|
||||||
file_read_data(struct client_files *files, struct imsg *imsg)
|
file_read_data(struct client_files *files, struct imsg *imsg)
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
/* $OpenBSD: format.c,v 1.410 2026/07/29 20:43:20 nicm Exp $ */
|
/* $OpenBSD: format.c,v 1.418 2026/09/20 08:19:31 nicm Exp $ */
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2011 Nicholas Marriott <nicholas.marriott@gmail.com>
|
* Copyright (c) 2011 Nicholas Marriott <nicholas.marriott@gmail.com>
|
||||||
@@ -855,26 +855,42 @@ format_cb_window_active_clients_list(struct format_tree *ft)
|
|||||||
static void *
|
static void *
|
||||||
format_cb_window_layout(struct format_tree *ft)
|
format_cb_window_layout(struct format_tree *ft)
|
||||||
{
|
{
|
||||||
|
struct client *c = ft->client;
|
||||||
struct window *w = ft->w;
|
struct window *w = ft->w;
|
||||||
|
struct layout_cell *lcroot;
|
||||||
|
int flags = 0;
|
||||||
|
|
||||||
if (w == NULL)
|
if (w == NULL)
|
||||||
return (NULL);
|
return (NULL);
|
||||||
|
|
||||||
if (w->saved_layout_root != NULL)
|
if (w->saved_layout_root != NULL)
|
||||||
return (layout_dump(w, w->saved_layout_root));
|
lcroot = w->saved_layout_root;
|
||||||
return (layout_dump(w, w->layout_root));
|
else
|
||||||
|
lcroot = w->layout_root;
|
||||||
|
|
||||||
|
if (c != NULL &&
|
||||||
|
(c->flags & CLIENT_CONTROL) &&
|
||||||
|
(~c->flags & CLIENT_CONTROL_NEWLAYOUTS))
|
||||||
|
flags |= LAYOUT_CUSTOM_OLD_FORMAT;
|
||||||
|
return (layout_dump(w, lcroot, flags));
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Callback for window_visible_layout. */
|
/* Callback for window_visible_layout. */
|
||||||
static void *
|
static void *
|
||||||
format_cb_window_visible_layout(struct format_tree *ft)
|
format_cb_window_visible_layout(struct format_tree *ft)
|
||||||
{
|
{
|
||||||
|
struct client *c = ft->client;
|
||||||
struct window *w = ft->w;
|
struct window *w = ft->w;
|
||||||
|
int flags = 0;
|
||||||
|
|
||||||
if (w == NULL)
|
if (w == NULL)
|
||||||
return (NULL);
|
return (NULL);
|
||||||
|
|
||||||
return (layout_dump(w, w->layout_root));
|
if (c != NULL &&
|
||||||
|
(c->flags & CLIENT_CONTROL) &&
|
||||||
|
(~c->flags & CLIENT_CONTROL_NEWLAYOUTS))
|
||||||
|
flags |= LAYOUT_CUSTOM_OLD_FORMAT;
|
||||||
|
return (layout_dump(w, w->layout_root, flags));
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Callback for pane_start_command. */
|
/* Callback for pane_start_command. */
|
||||||
@@ -1887,6 +1903,37 @@ format_cb_cursor_blinking(struct format_tree *ft)
|
|||||||
return (NULL);
|
return (NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Callback for history_added. */
|
||||||
|
static void *
|
||||||
|
format_cb_history_added(struct format_tree *ft)
|
||||||
|
{
|
||||||
|
if (ft->wp != NULL)
|
||||||
|
return (format_printf("%u", ft->wp->base.grid->scroll_added));
|
||||||
|
return (NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Callback for history_collected. */
|
||||||
|
static void *
|
||||||
|
format_cb_history_collected(struct format_tree *ft)
|
||||||
|
{
|
||||||
|
struct window_pane *wp = ft->wp;
|
||||||
|
|
||||||
|
if (wp != NULL)
|
||||||
|
return (format_printf("%u", wp->base.grid->scroll_collected));
|
||||||
|
return (NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Callback for history_generation. */
|
||||||
|
static void *
|
||||||
|
format_cb_history_generation(struct format_tree *ft)
|
||||||
|
{
|
||||||
|
struct window_pane *wp = ft->wp;
|
||||||
|
|
||||||
|
if (wp != NULL)
|
||||||
|
return (format_printf("%u", wp->base.grid->scroll_generation));
|
||||||
|
return (NULL);
|
||||||
|
}
|
||||||
|
|
||||||
/* Callback for history_limit. */
|
/* Callback for history_limit. */
|
||||||
static void *
|
static void *
|
||||||
format_cb_history_limit(struct format_tree *ft)
|
format_cb_history_limit(struct format_tree *ft)
|
||||||
@@ -2101,6 +2148,60 @@ format_cb_synchronized_output_flag(struct format_tree *ft)
|
|||||||
return (NULL);
|
return (NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Callback for pane_private_modes. */
|
||||||
|
static void *
|
||||||
|
format_cb_pane_private_modes(struct format_tree *ft)
|
||||||
|
{
|
||||||
|
static const struct {
|
||||||
|
int mode;
|
||||||
|
int number;
|
||||||
|
} table[] = {
|
||||||
|
{ MODE_KCURSOR, 1 }, /* DECCKM */
|
||||||
|
{ MODE_ORIGIN, 6 }, /* DECOM */
|
||||||
|
{ MODE_WRAP, 7 }, /* DECAWM */
|
||||||
|
{ MODE_CURSOR_BLINKING, 12 }, /* cursor blinking */
|
||||||
|
{ MODE_CURSOR, 25 }, /* DECTCEM */
|
||||||
|
{ MODE_MOUSE_STANDARD, 1000 }, /* mouse normal tracking */
|
||||||
|
{ MODE_MOUSE_BUTTON, 1002 }, /* mouse button tracking */
|
||||||
|
{ MODE_MOUSE_ALL, 1003 }, /* mouse any tracking */
|
||||||
|
{ MODE_FOCUSON, 1004 }, /* focus reporting */
|
||||||
|
{ MODE_MOUSE_UTF8, 1005 }, /* mouse: UTF-8 */
|
||||||
|
{ MODE_MOUSE_SGR, 1006 }, /* mouse: SGR */
|
||||||
|
{ MODE_BRACKETPASTE, 2004 }, /* bracketed paste */
|
||||||
|
{ MODE_SYNC, 2026 }, /* synchronized output */
|
||||||
|
{ MODE_THEME_UPDATES, 2031 }, /* theme update notifications */
|
||||||
|
};
|
||||||
|
int mode;
|
||||||
|
char *value = NULL, *tmp;
|
||||||
|
u_int i;
|
||||||
|
|
||||||
|
if (ft->wp == NULL)
|
||||||
|
return (NULL);
|
||||||
|
mode = ft->wp->base.mode;
|
||||||
|
|
||||||
|
for (i = 0; i < nitems(table); i++) {
|
||||||
|
if (~mode & table[i].mode)
|
||||||
|
continue;
|
||||||
|
/*
|
||||||
|
* Only report cursor blinking when set by the application, not
|
||||||
|
* when it comes from the cursor-style option.
|
||||||
|
*/
|
||||||
|
if (table[i].mode == MODE_CURSOR_BLINKING &&
|
||||||
|
(~mode & MODE_CURSOR_BLINKING_SET))
|
||||||
|
continue;
|
||||||
|
if (value == NULL)
|
||||||
|
xasprintf(&value, "%d", table[i].number);
|
||||||
|
else {
|
||||||
|
xasprintf(&tmp, "%s,%d", value, table[i].number);
|
||||||
|
free(value);
|
||||||
|
value = tmp;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (value == NULL)
|
||||||
|
return (xstrdup(""));
|
||||||
|
return (value);
|
||||||
|
}
|
||||||
|
|
||||||
/* Callback for pane_active. */
|
/* Callback for pane_active. */
|
||||||
static void *
|
static void *
|
||||||
format_cb_pane_active(struct format_tree *ft)
|
format_cb_pane_active(struct format_tree *ft)
|
||||||
@@ -2222,6 +2323,19 @@ format_cb_pane_last_output_time(struct format_tree *ft)
|
|||||||
return (NULL);
|
return (NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Callback for pane_output_generation. */
|
||||||
|
static void *
|
||||||
|
format_cb_pane_output_generation(struct format_tree *ft)
|
||||||
|
{
|
||||||
|
unsigned long long value;
|
||||||
|
|
||||||
|
if (ft->wp != NULL) {
|
||||||
|
value = ft->wp->output_generation;
|
||||||
|
return (format_printf("%llu", value));
|
||||||
|
}
|
||||||
|
return (NULL);
|
||||||
|
}
|
||||||
|
|
||||||
/* Callback for pane_last_prompt_time. */
|
/* Callback for pane_last_prompt_time. */
|
||||||
static void *
|
static void *
|
||||||
format_cb_pane_last_prompt_time(struct format_tree *ft)
|
format_cb_pane_last_prompt_time(struct format_tree *ft)
|
||||||
@@ -2762,10 +2876,10 @@ format_cb_session_activity_flag(struct format_tree *ft)
|
|||||||
|
|
||||||
if (ft->s != NULL) {
|
if (ft->s != NULL) {
|
||||||
RB_FOREACH(wl, winlinks, &ft->s->windows) {
|
RB_FOREACH(wl, winlinks, &ft->s->windows) {
|
||||||
if (ft->wl->flags & WINLINK_ACTIVITY)
|
if (wl->flags & WINLINK_ACTIVITY)
|
||||||
return (xstrdup("1"));
|
return (xstrdup("1"));
|
||||||
return (xstrdup("0"));
|
|
||||||
}
|
}
|
||||||
|
return (xstrdup("0"));
|
||||||
}
|
}
|
||||||
return (NULL);
|
return (NULL);
|
||||||
}
|
}
|
||||||
@@ -2780,8 +2894,8 @@ format_cb_session_bell_flag(struct format_tree *ft)
|
|||||||
RB_FOREACH(wl, winlinks, &ft->s->windows) {
|
RB_FOREACH(wl, winlinks, &ft->s->windows) {
|
||||||
if (wl->flags & WINLINK_BELL)
|
if (wl->flags & WINLINK_BELL)
|
||||||
return (xstrdup("1"));
|
return (xstrdup("1"));
|
||||||
return (xstrdup("0"));
|
|
||||||
}
|
}
|
||||||
|
return (xstrdup("0"));
|
||||||
}
|
}
|
||||||
return (NULL);
|
return (NULL);
|
||||||
}
|
}
|
||||||
@@ -2794,10 +2908,10 @@ format_cb_session_silence_flag(struct format_tree *ft)
|
|||||||
|
|
||||||
if (ft->s != NULL) {
|
if (ft->s != NULL) {
|
||||||
RB_FOREACH(wl, winlinks, &ft->s->windows) {
|
RB_FOREACH(wl, winlinks, &ft->s->windows) {
|
||||||
if (ft->wl->flags & WINLINK_SILENCE)
|
if (wl->flags & WINLINK_SILENCE)
|
||||||
return (xstrdup("1"));
|
return (xstrdup("1"));
|
||||||
return (xstrdup("0"));
|
|
||||||
}
|
}
|
||||||
|
return (xstrdup("0"));
|
||||||
}
|
}
|
||||||
return (NULL);
|
return (NULL);
|
||||||
}
|
}
|
||||||
@@ -3609,12 +3723,21 @@ static const struct format_table_entry format_table[] = {
|
|||||||
{ "cursor_y", FORMAT_TABLE_STRING,
|
{ "cursor_y", FORMAT_TABLE_STRING,
|
||||||
format_cb_cursor_y
|
format_cb_cursor_y
|
||||||
},
|
},
|
||||||
|
{ "history_added", FORMAT_TABLE_STRING,
|
||||||
|
format_cb_history_added
|
||||||
|
},
|
||||||
{ "history_all_bytes", FORMAT_TABLE_STRING,
|
{ "history_all_bytes", FORMAT_TABLE_STRING,
|
||||||
format_cb_history_all_bytes
|
format_cb_history_all_bytes
|
||||||
},
|
},
|
||||||
{ "history_bytes", FORMAT_TABLE_STRING,
|
{ "history_bytes", FORMAT_TABLE_STRING,
|
||||||
format_cb_history_bytes
|
format_cb_history_bytes
|
||||||
},
|
},
|
||||||
|
{ "history_collected", FORMAT_TABLE_STRING,
|
||||||
|
format_cb_history_collected
|
||||||
|
},
|
||||||
|
{ "history_generation", FORMAT_TABLE_STRING,
|
||||||
|
format_cb_history_generation
|
||||||
|
},
|
||||||
{ "history_limit", FORMAT_TABLE_STRING,
|
{ "history_limit", FORMAT_TABLE_STRING,
|
||||||
format_cb_history_limit
|
format_cb_history_limit
|
||||||
},
|
},
|
||||||
@@ -3795,6 +3918,9 @@ static const struct format_table_entry format_table[] = {
|
|||||||
{ "pane_mode", FORMAT_TABLE_STRING,
|
{ "pane_mode", FORMAT_TABLE_STRING,
|
||||||
format_cb_pane_mode
|
format_cb_pane_mode
|
||||||
},
|
},
|
||||||
|
{ "pane_output_generation", FORMAT_TABLE_STRING,
|
||||||
|
format_cb_pane_output_generation
|
||||||
|
},
|
||||||
{ "pane_path", FORMAT_TABLE_STRING,
|
{ "pane_path", FORMAT_TABLE_STRING,
|
||||||
format_cb_pane_path
|
format_cb_pane_path
|
||||||
},
|
},
|
||||||
@@ -3813,6 +3939,9 @@ static const struct format_table_entry format_table[] = {
|
|||||||
{ "pane_pipe_pid", FORMAT_TABLE_STRING,
|
{ "pane_pipe_pid", FORMAT_TABLE_STRING,
|
||||||
format_cb_pane_pipe_pid
|
format_cb_pane_pipe_pid
|
||||||
},
|
},
|
||||||
|
{ "pane_private_modes", FORMAT_TABLE_STRING,
|
||||||
|
format_cb_pane_private_modes
|
||||||
|
},
|
||||||
{ "pane_right", FORMAT_TABLE_STRING,
|
{ "pane_right", FORMAT_TABLE_STRING,
|
||||||
format_cb_pane_right
|
format_cb_pane_right
|
||||||
},
|
},
|
||||||
@@ -4200,6 +4329,8 @@ format_log_debug_cb(const char *key, const char *value, void *arg)
|
|||||||
void
|
void
|
||||||
format_log_debug(struct format_tree *ft, const char *prefix)
|
format_log_debug(struct format_tree *ft, const char *prefix)
|
||||||
{
|
{
|
||||||
|
if (log_get_level() == 0)
|
||||||
|
return;
|
||||||
format_each(ft, format_log_debug_cb, (void *)prefix);
|
format_each(ft, format_log_debug_cb, (void *)prefix);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -4328,7 +4459,7 @@ format_quote_shell(const char *s)
|
|||||||
|
|
||||||
at = out = xmalloc(strlen(s) * 2 + 1);
|
at = out = xmalloc(strlen(s) * 2 + 1);
|
||||||
for (cp = s; *cp != '\0'; cp++) {
|
for (cp = s; *cp != '\0'; cp++) {
|
||||||
if (strchr("|&;<>()$`\\\"'*?[# =%\n\t", *cp) != NULL)
|
if (strchr("|&;<>(){}$`\\\"'*?[# =%\n\t", *cp) != NULL)
|
||||||
*at++ = '\\';
|
*at++ = '\\';
|
||||||
*at++ = *cp;
|
*at++ = *cp;
|
||||||
}
|
}
|
||||||
@@ -5119,6 +5250,7 @@ format_loop_sessions(struct format_expand_state *es, const char *fmt)
|
|||||||
format_log(es, "session loop: $%u", s->id);
|
format_log(es, "session loop: $%u", s->id);
|
||||||
if (active != NULL &&
|
if (active != NULL &&
|
||||||
ft->c != NULL &&
|
ft->c != NULL &&
|
||||||
|
ft->c->session != NULL &&
|
||||||
s->id == ft->c->session->id)
|
s->id == ft->c->session->id)
|
||||||
use = active;
|
use = active;
|
||||||
else
|
else
|
||||||
|
|||||||
+3
-3
@@ -1,4 +1,4 @@
|
|||||||
/* $OpenBSD: grid-view.c,v 1.38 2026/01/22 08:55:01 nicm Exp $ */
|
/* $OpenBSD: grid-view.c,v 1.39 2026/08/03 12:58:53 nicm Exp $ */
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2008 Nicholas Marriott <nicholas.marriott@gmail.com>
|
* Copyright (c) 2008 Nicholas Marriott <nicholas.marriott@gmail.com>
|
||||||
@@ -47,9 +47,9 @@ grid_view_set_cell(struct grid *gd, u_int px, u_int py,
|
|||||||
|
|
||||||
/* Set padding. */
|
/* Set padding. */
|
||||||
void
|
void
|
||||||
grid_view_set_padding(struct grid *gd, u_int px, u_int py)
|
grid_view_set_padding(struct grid *gd, u_int px, u_int py, int bg)
|
||||||
{
|
{
|
||||||
grid_set_padding(gd, grid_view_x(gd, px), grid_view_y(gd, py));
|
grid_set_padding(gd, grid_view_x(gd, px), grid_view_y(gd, py), bg);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Set cells. */
|
/* Set cells. */
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
/* $OpenBSD: grid.c,v 1.155 2026/07/29 17:42:56 nicm Exp $ */
|
/* $OpenBSD: grid.c,v 1.158 2026/09/01 12:49:49 nicm Exp $ */
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2008 Nicholas Marriott <nicholas.marriott@gmail.com>
|
* Copyright (c) 2008 Nicholas Marriott <nicholas.marriott@gmail.com>
|
||||||
@@ -682,9 +682,13 @@ grid_set_cell(struct grid *gd, u_int px, u_int py, const struct grid_cell *gc)
|
|||||||
|
|
||||||
/* Set padding at position. */
|
/* Set padding at position. */
|
||||||
void
|
void
|
||||||
grid_set_padding(struct grid *gd, u_int px, u_int py)
|
grid_set_padding(struct grid *gd, u_int px, u_int py, int bg)
|
||||||
{
|
{
|
||||||
grid_set_cell(gd, px, py, &grid_padding_cell);
|
struct grid_cell gc;
|
||||||
|
|
||||||
|
memcpy(&gc, &grid_padding_cell, sizeof gc);
|
||||||
|
gc.bg = bg;
|
||||||
|
grid_set_cell(gd, px, py, &gc);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Set cells at position. */
|
/* Set cells at position. */
|
||||||
@@ -1631,7 +1635,7 @@ grid_wrap_position(struct grid *gd, u_int px, u_int py, u_int *wx, u_int *wy)
|
|||||||
void
|
void
|
||||||
grid_unwrap_position(struct grid *gd, u_int *px, u_int *py, u_int wx, u_int wy)
|
grid_unwrap_position(struct grid *gd, u_int *px, u_int *py, u_int wx, u_int wy)
|
||||||
{
|
{
|
||||||
u_int yy, ay = 0;
|
u_int yy, ay = 0, ey = gd->hsize + gd->sy - 1;
|
||||||
|
|
||||||
for (yy = 0; yy < gd->hsize + gd->sy - 1; yy++) {
|
for (yy = 0; yy < gd->hsize + gd->sy - 1; yy++) {
|
||||||
if (ay == wy)
|
if (ay == wy)
|
||||||
@@ -1645,7 +1649,7 @@ grid_unwrap_position(struct grid *gd, u_int *px, u_int *py, u_int wx, u_int wy)
|
|||||||
* until we find the end or the line now containing wx.
|
* until we find the end or the line now containing wx.
|
||||||
*/
|
*/
|
||||||
if (wx == UINT_MAX) {
|
if (wx == UINT_MAX) {
|
||||||
while (gd->linedata[yy].flags & GRID_LINE_WRAPPED)
|
while (yy < ey && gd->linedata[yy].flags & GRID_LINE_WRAPPED)
|
||||||
yy++;
|
yy++;
|
||||||
wx = gd->linedata[yy].cellused;
|
wx = gd->linedata[yy].cellused;
|
||||||
} else {
|
} else {
|
||||||
@@ -1707,21 +1711,29 @@ grid_in_set(struct grid *gd, u_int px, u_int py, const char *set)
|
|||||||
{
|
{
|
||||||
struct grid_cell gc, tmp_gc;
|
struct grid_cell gc, tmp_gc;
|
||||||
u_int pxx;
|
u_int pxx;
|
||||||
|
int has_tab, has_space;
|
||||||
|
|
||||||
|
has_tab = (strchr(set, '\t') != NULL);
|
||||||
|
has_space = (strchr(set, ' ') != NULL);
|
||||||
|
|
||||||
grid_get_cell(gd, px, py, &gc);
|
grid_get_cell(gd, px, py, &gc);
|
||||||
if (strchr(set, '\t')) {
|
|
||||||
if (gc.flags & GRID_FLAG_PADDING) {
|
if (gc.flags & GRID_FLAG_PADDING) {
|
||||||
|
if (!has_tab && !has_space)
|
||||||
|
return (0);
|
||||||
pxx = px;
|
pxx = px;
|
||||||
do
|
do
|
||||||
grid_get_cell(gd, --pxx, py, &tmp_gc);
|
grid_get_cell(gd, --pxx, py, &tmp_gc);
|
||||||
while (pxx > 0 && tmp_gc.flags & GRID_FLAG_PADDING);
|
while (pxx > 0 && tmp_gc.flags & GRID_FLAG_PADDING);
|
||||||
if (tmp_gc.flags & GRID_FLAG_TAB)
|
if (((has_tab || has_space) &&
|
||||||
|
(tmp_gc.flags & GRID_FLAG_TAB)) ||
|
||||||
|
(has_space && utf8_has_whitespace(&tmp_gc.data)))
|
||||||
return (tmp_gc.data.width - (px - pxx));
|
return (tmp_gc.data.width - (px - pxx));
|
||||||
} else if (gc.flags & GRID_FLAG_TAB)
|
|
||||||
return (gc.data.width);
|
|
||||||
}
|
|
||||||
if (gc.flags & GRID_FLAG_PADDING)
|
|
||||||
return (0);
|
return (0);
|
||||||
|
}
|
||||||
|
if ((has_tab || has_space) && (gc.flags & GRID_FLAG_TAB))
|
||||||
|
return (gc.data.width);
|
||||||
|
if (has_space && utf8_has_whitespace(&gc.data))
|
||||||
|
return (gc.data.width == 0 ? 1 : gc.data.width);
|
||||||
return (utf8_cstrhas(set, &gc.data));
|
return (utf8_cstrhas(set, &gc.data));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -84,6 +84,11 @@ image_fallback(char **ret, u_int sx, u_int sy)
|
|||||||
char *buf, *label;
|
char *buf, *label;
|
||||||
u_int py, size, lsize;
|
u_int py, size, lsize;
|
||||||
|
|
||||||
|
if (sy == 0) {
|
||||||
|
*ret = xstrdup("");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
/* Allocate first line. */
|
/* Allocate first line. */
|
||||||
lsize = xasprintf(&label, "SIXEL IMAGE (%ux%u)\r\n", sx, sy) + 1;
|
lsize = xasprintf(&label, "SIXEL IMAGE (%ux%u)\r\n", sx, sy) + 1;
|
||||||
if (sx < lsize - 3)
|
if (sx < lsize - 3)
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
/* $OpenBSD: input.c,v 1.269 2026/07/20 11:16:33 nicm Exp $ */
|
/* $OpenBSD: input.c,v 1.271 2026/08/31 19:34:09 nicm Exp $ */
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>
|
* Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>
|
||||||
@@ -1049,6 +1049,7 @@ input_parse_buffer(struct window_pane *wp, const u_char *buf, size_t len)
|
|||||||
if (len == 0)
|
if (len == 0)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
wp->output_generation++;
|
||||||
window_update_activity(wp->window);
|
window_update_activity(wp->window);
|
||||||
if (~wp->flags & PANE_ACTIVITY) {
|
if (~wp->flags & PANE_ACTIVITY) {
|
||||||
wp->flags |= PANE_ACTIVITY;
|
wp->flags |= PANE_ACTIVITY;
|
||||||
@@ -1978,7 +1979,7 @@ input_csi_dispatch_rm_private(struct input_ctx *ictx)
|
|||||||
screen_write_mode_clear(sctx, MODE_BRACKETPASTE);
|
screen_write_mode_clear(sctx, MODE_BRACKETPASTE);
|
||||||
break;
|
break;
|
||||||
case 2026:
|
case 2026:
|
||||||
screen_write_stop_sync(ictx->wp);
|
screen_write_end_sync(sctx);
|
||||||
break;
|
break;
|
||||||
case 2031:
|
case 2031:
|
||||||
screen_write_mode_clear(sctx, MODE_THEME_UPDATES);
|
screen_write_mode_clear(sctx, MODE_THEME_UPDATES);
|
||||||
|
|||||||
+5
-4
@@ -1,4 +1,4 @@
|
|||||||
/* $OpenBSD: key-bindings.c,v 1.190 2026/07/22 19:23:59 nicm Exp $ */
|
/* $OpenBSD: key-bindings.c,v 1.193 2026/09/21 20:45:48 nicm Exp $ */
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>
|
* Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>
|
||||||
@@ -421,6 +421,7 @@ key_bindings_init(void)
|
|||||||
"bind -N 'Spread panes out evenly' E { select-layout -E }",
|
"bind -N 'Spread panes out evenly' E { select-layout -E }",
|
||||||
"bind -N 'Switch to the last client' L { switch-client -l }",
|
"bind -N 'Switch to the last client' L { switch-client -l }",
|
||||||
"bind -N 'Clear the marked pane' M { select-pane -M }",
|
"bind -N 'Clear the marked pane' M { select-pane -M }",
|
||||||
|
"bind -N 'Change the pane title' T { command-prompt -I'#T' { select-pane -T '%%' } }",
|
||||||
"bind -N 'Enter copy mode' [ { copy-mode }",
|
"bind -N 'Enter copy mode' [ { copy-mode }",
|
||||||
"bind -N 'Paste the most recent paste buffer' ] { paste-buffer -p }",
|
"bind -N 'Paste the most recent paste buffer' ] { paste-buffer -p }",
|
||||||
"bind -N 'Create a new window' c { new-window }",
|
"bind -N 'Create a new window' c { new-window }",
|
||||||
@@ -522,7 +523,7 @@ key_bindings_init(void)
|
|||||||
"bind -n TripleClick1Pane { select-pane -t=; if -F '#{||:#{pane_in_mode},#{mouse_any_flag}}' { send -M } { copy-mode -H; send -X select-line; run -d0.3; send -X copy-pipe-and-cancel } }",
|
"bind -n TripleClick1Pane { select-pane -t=; if -F '#{||:#{pane_in_mode},#{mouse_any_flag}}' { send -M } { copy-mode -H; send -X select-line; run -d0.3; send -X copy-pipe-and-cancel } }",
|
||||||
|
|
||||||
/* Mouse button 1 on border. */
|
/* Mouse button 1 on border. */
|
||||||
"bind -n MouseDown1Border { select-pane -M }",
|
"bind -n MouseDown1Border { select-pane -t= }",
|
||||||
|
|
||||||
/* Mouse button 1 drag on border. */
|
/* Mouse button 1 drag on border. */
|
||||||
"bind -n MouseDrag1Border { resize-pane -M }",
|
"bind -n MouseDrag1Border { resize-pane -M }",
|
||||||
@@ -595,7 +596,7 @@ key_bindings_init(void)
|
|||||||
"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 }",
|
||||||
"bind -Tcopy-mode q { send -X cancel }",
|
"bind -Tcopy-mode q { send -X cancel }",
|
||||||
"bind -Tcopy-mode r { send -X refresh-toggle }",
|
"bind -Tcopy-mode r { send -X refresh-now }",
|
||||||
"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 }",
|
||||||
@@ -705,7 +706,7 @@ key_bindings_init(void)
|
|||||||
"bind -Tcopy-mode-vi n { send -X search-again }",
|
"bind -Tcopy-mode-vi n { send -X search-again }",
|
||||||
"bind -Tcopy-mode-vi o { send -X other-end }",
|
"bind -Tcopy-mode-vi o { send -X other-end }",
|
||||||
"bind -Tcopy-mode-vi q { send -X cancel }",
|
"bind -Tcopy-mode-vi q { send -X cancel }",
|
||||||
"bind -Tcopy-mode-vi r { send -X refresh-toggle }",
|
"bind -Tcopy-mode-vi r { send -X refresh-now }",
|
||||||
"bind -Tcopy-mode-vi t { command-prompt -P -1p'(jump to forward)' { send -X jump-to-forward -- '%%' } }",
|
"bind -Tcopy-mode-vi t { command-prompt -P -1p'(jump to forward)' { send -X jump-to-forward -- '%%' } }",
|
||||||
"bind -Tcopy-mode-vi v { send -X rectangle-toggle }",
|
"bind -Tcopy-mode-vi v { send -X rectangle-toggle }",
|
||||||
"bind -Tcopy-mode-vi w { send -X next-word }",
|
"bind -Tcopy-mode-vi w { send -X next-word }",
|
||||||
|
|||||||
+962
-138
File diff suppressed because it is too large
Load Diff
@@ -1,4 +1,4 @@
|
|||||||
/* $OpenBSD: layout.c,v 1.96 2026/07/15 13:02:33 nicm Exp $ */
|
/* $OpenBSD: layout.c,v 1.101 2026/09/20 08:42:46 nicm Exp $ */
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2009 Nicholas Marriott <nicholas.marriott@gmail.com>
|
* Copyright (c) 2009 Nicholas Marriott <nicholas.marriott@gmail.com>
|
||||||
@@ -106,7 +106,7 @@ layout_free_cell(struct layout_cell *lc, int only_nodes)
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case LAYOUT_WINDOWPANE:
|
case LAYOUT_WINDOWPANE:
|
||||||
if (lc->wp != NULL) {
|
if (lc->wp != NULL && lc->wp->layout_cell != NULL) {
|
||||||
lc->wp->layout_cell->parent = NULL;
|
lc->wp->layout_cell->parent = NULL;
|
||||||
lc->wp->layout_cell = NULL;
|
lc->wp->layout_cell = NULL;
|
||||||
}
|
}
|
||||||
@@ -233,29 +233,6 @@ layout_make_node(struct layout_cell *lc, enum layout_type type)
|
|||||||
lc->wp = NULL;
|
lc->wp = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Fix z-indexes. */
|
|
||||||
void
|
|
||||||
layout_fix_zindexes(struct window *w, struct layout_cell *lc)
|
|
||||||
{
|
|
||||||
struct layout_cell *lcchild;
|
|
||||||
|
|
||||||
if (lc == NULL)
|
|
||||||
return;
|
|
||||||
|
|
||||||
switch (lc->type) {
|
|
||||||
case LAYOUT_WINDOWPANE:
|
|
||||||
TAILQ_INSERT_TAIL(&w->z_index, lc->wp, zentry);
|
|
||||||
break;
|
|
||||||
case LAYOUT_LEFTRIGHT:
|
|
||||||
case LAYOUT_TOPBOTTOM:
|
|
||||||
TAILQ_FOREACH(lcchild, &lc->cells, entry)
|
|
||||||
layout_fix_zindexes(w, lcchild);
|
|
||||||
return;
|
|
||||||
default:
|
|
||||||
fatalx("bad layout type");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
int
|
int
|
||||||
layout_cell_is_tiled(struct layout_cell *lc)
|
layout_cell_is_tiled(struct layout_cell *lc)
|
||||||
{
|
{
|
||||||
@@ -265,7 +242,7 @@ layout_cell_is_tiled(struct layout_cell *lc)
|
|||||||
return is_leaf && !is_floating;
|
return is_leaf && !is_floating;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int
|
int
|
||||||
layout_cell_has_tiled_child(struct layout_cell *lc)
|
layout_cell_has_tiled_child(struct layout_cell *lc)
|
||||||
{
|
{
|
||||||
struct layout_cell *lcchild;
|
struct layout_cell *lcchild;
|
||||||
@@ -509,18 +486,20 @@ layout_fix_panes(struct window *w, struct window_pane *skip)
|
|||||||
|
|
||||||
/* Count the number of available cells in a layout. */
|
/* Count the number of available cells in a layout. */
|
||||||
u_int
|
u_int
|
||||||
layout_count_cells(struct layout_cell *lc)
|
layout_count_cells(struct layout_cell *lc, int with_floating)
|
||||||
{
|
{
|
||||||
struct layout_cell *lcchild;
|
struct layout_cell *lcchild;
|
||||||
u_int count = 0;
|
u_int count = 0;
|
||||||
|
|
||||||
switch (lc->type) {
|
switch (lc->type) {
|
||||||
case LAYOUT_WINDOWPANE:
|
case LAYOUT_WINDOWPANE:
|
||||||
|
if (lc->flags & LAYOUT_CELL_FLOATING && !with_floating)
|
||||||
|
return 0;
|
||||||
return (1);
|
return (1);
|
||||||
case LAYOUT_LEFTRIGHT:
|
case LAYOUT_LEFTRIGHT:
|
||||||
case LAYOUT_TOPBOTTOM:
|
case LAYOUT_TOPBOTTOM:
|
||||||
TAILQ_FOREACH(lcchild, &lc->cells, entry)
|
TAILQ_FOREACH(lcchild, &lc->cells, entry)
|
||||||
count += layout_count_cells(lcchild);
|
count += layout_count_cells(lcchild, with_floating);
|
||||||
return (count);
|
return (count);
|
||||||
default:
|
default:
|
||||||
fatalx("bad layout type");
|
fatalx("bad layout type");
|
||||||
@@ -539,6 +518,10 @@ layout_resize_check(struct window *w, struct layout_cell *lc,
|
|||||||
|
|
||||||
status = window_get_pane_status(w);
|
status = window_get_pane_status(w);
|
||||||
|
|
||||||
|
/* Floating cells do not take space from the tiled layout. */
|
||||||
|
if (!layout_cell_is_tiled(lc) && !layout_cell_has_tiled_child(lc))
|
||||||
|
return (0);
|
||||||
|
|
||||||
if (lc->type == LAYOUT_WINDOWPANE) {
|
if (lc->type == LAYOUT_WINDOWPANE) {
|
||||||
/* Space available in this cell only. */
|
/* Space available in this cell only. */
|
||||||
if (type == LAYOUT_LEFTRIGHT) {
|
if (type == LAYOUT_LEFTRIGHT) {
|
||||||
@@ -568,6 +551,9 @@ layout_resize_check(struct window *w, struct layout_cell *lc,
|
|||||||
/* Different type: minimum of available space in child cells. */
|
/* Different type: minimum of available space in child cells. */
|
||||||
minimum = UINT_MAX;
|
minimum = UINT_MAX;
|
||||||
TAILQ_FOREACH(lcchild, &lc->cells, entry) {
|
TAILQ_FOREACH(lcchild, &lc->cells, entry) {
|
||||||
|
if (!layout_cell_is_tiled(lcchild) &&
|
||||||
|
!layout_cell_has_tiled_child(lcchild))
|
||||||
|
continue;
|
||||||
available = layout_resize_check(w, lcchild, type);
|
available = layout_resize_check(w, lcchild, type);
|
||||||
if (available < minimum)
|
if (available < minimum)
|
||||||
minimum = available;
|
minimum = available;
|
||||||
@@ -661,7 +647,7 @@ layout_resize_set_size(struct window *w, struct layout_cell *lc,
|
|||||||
|
|
||||||
/* Find and return the nearest neighbour to a cell in a specific direction. */
|
/* Find and return the nearest neighbour to a cell in a specific direction. */
|
||||||
static struct layout_cell *
|
static struct layout_cell *
|
||||||
layout_cell_get_neighbour_direction(struct layout_cell *lc, int direction)
|
layout_cell_get_neighbour_dir(struct layout_cell *lc, int direction)
|
||||||
{
|
{
|
||||||
struct layout_cell *lcn = lc;
|
struct layout_cell *lcn = lc;
|
||||||
|
|
||||||
@@ -695,9 +681,9 @@ layout_cell_get_neighbour(struct layout_cell *lc)
|
|||||||
if (lc == TAILQ_LAST(&lcparent->cells, layout_cells))
|
if (lc == TAILQ_LAST(&lcparent->cells, layout_cells))
|
||||||
direction = !direction;
|
direction = !direction;
|
||||||
|
|
||||||
lcother = layout_cell_get_neighbour_direction(lc, direction);
|
lcother = layout_cell_get_neighbour_dir(lc, direction);
|
||||||
if (lcother == NULL)
|
if (lcother == NULL)
|
||||||
lcother = layout_cell_get_neighbour_direction(lc, !direction);
|
lcother = layout_cell_get_neighbour_dir(lc, !direction);
|
||||||
|
|
||||||
return (lcother);
|
return (lcother);
|
||||||
}
|
}
|
||||||
@@ -714,7 +700,7 @@ layout_destroy_cell(struct window *w, struct layout_cell *lc,
|
|||||||
/* If no parent, this is the last pane in a window. */
|
/* If no parent, this is the last pane in a window. */
|
||||||
lcparent = lc->parent;
|
lcparent = lc->parent;
|
||||||
if (lcparent == NULL) {
|
if (lcparent == NULL) {
|
||||||
if (lc->wp != NULL)
|
if (*lcroot == lc)
|
||||||
*lcroot = NULL;
|
*lcroot = NULL;
|
||||||
layout_free_cell(lc, 0);
|
layout_free_cell(lc, 0);
|
||||||
return;
|
return;
|
||||||
@@ -782,6 +768,49 @@ layout_free(struct window *w, int only_nodes)
|
|||||||
layout_free_cell(w->layout_root, only_nodes);
|
layout_free_cell(w->layout_root, only_nodes);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Move and resize floating panes so they stay inside the window. */
|
||||||
|
static void
|
||||||
|
layout_clamp_floating_panes(struct window *w, u_int sx, u_int sy)
|
||||||
|
{
|
||||||
|
struct window_pane *wp;
|
||||||
|
struct layout_cell *lc;
|
||||||
|
u_int pad, avail, csx, csy;
|
||||||
|
|
||||||
|
TAILQ_FOREACH(wp, &w->z_index, zentry) {
|
||||||
|
lc = wp->layout_cell;
|
||||||
|
if (lc == NULL || (~lc->flags & LAYOUT_CELL_FLOATING))
|
||||||
|
continue;
|
||||||
|
if (window_pane_get_pane_lines(wp) == PANE_LINES_NONE)
|
||||||
|
pad = 0;
|
||||||
|
else
|
||||||
|
pad = 1;
|
||||||
|
|
||||||
|
csx = lc->g.sx;
|
||||||
|
avail = (sx > 2 * pad) ? sx - 2 * pad : 0;
|
||||||
|
if (csx > avail)
|
||||||
|
csx = (avail > PANE_MINIMUM) ? avail : PANE_MINIMUM;
|
||||||
|
csy = lc->g.sy;
|
||||||
|
avail = (sy > 2 * pad) ? sy - 2 * pad : 0;
|
||||||
|
if (csy > avail)
|
||||||
|
csy = (avail > PANE_MINIMUM) ? avail : PANE_MINIMUM;
|
||||||
|
if (csx != lc->g.sx || csy != lc->g.sy)
|
||||||
|
layout_set_size(lc, csx, csy, lc->g.xoff, lc->g.yoff);
|
||||||
|
|
||||||
|
if (lc->g.xoff + lc->g.sx + pad > sx) {
|
||||||
|
if (lc->g.sx + 2 * pad >= sx)
|
||||||
|
lc->g.xoff = pad;
|
||||||
|
else
|
||||||
|
lc->g.xoff = sx - lc->g.sx - pad;
|
||||||
|
}
|
||||||
|
if (lc->g.yoff + lc->g.sy + pad > sy) {
|
||||||
|
if (lc->g.sy + 2 * pad >= sy)
|
||||||
|
lc->g.yoff = pad;
|
||||||
|
else
|
||||||
|
lc->g.yoff = sy - lc->g.sy - pad;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* Resize the entire layout after window resize. */
|
/* Resize the entire layout after window resize. */
|
||||||
void
|
void
|
||||||
layout_resize(struct window *w, u_int sx, u_int sy)
|
layout_resize(struct window *w, u_int sx, u_int sy)
|
||||||
@@ -802,8 +831,11 @@ layout_resize(struct window *w, u_int sx, u_int sy)
|
|||||||
* out proportionately - this should leave the layout fitting the new
|
* out proportionately - this should leave the layout fitting the new
|
||||||
* window size.
|
* window size.
|
||||||
*/
|
*/
|
||||||
if (lc->type == LAYOUT_WINDOWPANE && (lc->flags & LAYOUT_CELL_FLOATING))
|
if (lc->type == LAYOUT_WINDOWPANE && (lc->flags & LAYOUT_CELL_FLOATING)) {
|
||||||
|
layout_clamp_floating_panes(w, sx, sy);
|
||||||
|
layout_fix_panes(w, NULL);
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
xchange = sx - lc->g.sx;
|
xchange = sx - lc->g.sx;
|
||||||
xlimit = layout_resize_check(w, lc, LAYOUT_LEFTRIGHT);
|
xlimit = layout_resize_check(w, lc, LAYOUT_LEFTRIGHT);
|
||||||
if (xchange < 0 && xchange < -xlimit)
|
if (xchange < 0 && xchange < -xlimit)
|
||||||
@@ -833,6 +865,7 @@ layout_resize(struct window *w, u_int sx, u_int sy)
|
|||||||
|
|
||||||
/* Fix cell offsets. */
|
/* Fix cell offsets. */
|
||||||
layout_fix_offsets(w);
|
layout_fix_offsets(w);
|
||||||
|
layout_clamp_floating_panes(w, sx, sy);
|
||||||
layout_fix_panes(w, NULL);
|
layout_fix_panes(w, NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -860,7 +893,7 @@ layout_resize_pane_to(struct window_pane *wp, enum layout_type type,
|
|||||||
size = lc->g.sx;
|
size = lc->g.sx;
|
||||||
else
|
else
|
||||||
size = lc->g.sy;
|
size = lc->g.sy;
|
||||||
if (lc == TAILQ_LAST(&lcparent->cells, layout_cells))
|
if (layout_cell_is_last_tiled(lc))
|
||||||
change = size - new_size;
|
change = size - new_size;
|
||||||
else
|
else
|
||||||
change = new_size - size;
|
change = new_size - size;
|
||||||
@@ -985,11 +1018,11 @@ layout_resize_pane(struct window_pane *wp, enum layout_type type, int change,
|
|||||||
if (lcparent == NULL)
|
if (lcparent == NULL)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
/* If this is the last cell, move back one. */
|
/* If this is the last tiled cell, move back one. */
|
||||||
if (lc == TAILQ_LAST(&lcparent->cells, layout_cells)) {
|
if (layout_cell_is_last_tiled(lc)) {
|
||||||
do
|
lc = layout_cell_get_neighbour_dir(lc, 0);
|
||||||
lc = TAILQ_PREV(lc, layout_cells, entry);
|
if (lc == NULL)
|
||||||
while (lc->flags & LAYOUT_CELL_FLOATING);
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
layout_resize_layout(wp->window, lc, type, change, opposite);
|
layout_resize_layout(wp->window, lc, type, change, opposite);
|
||||||
@@ -1007,22 +1040,22 @@ layout_resize_pane_grow(struct window *w, struct layout_cell *lc,
|
|||||||
lcadd = lc;
|
lcadd = lc;
|
||||||
|
|
||||||
/* Look towards the tail for a suitable cell for reduction. */
|
/* Look towards the tail for a suitable cell for reduction. */
|
||||||
lcremove = TAILQ_NEXT(lc, entry);
|
lcremove = layout_cell_get_neighbour_dir(lc, 1);
|
||||||
while (lcremove != NULL) {
|
while (lcremove != NULL) {
|
||||||
size = layout_resize_check(w, lcremove, type);
|
size = layout_resize_check(w, lcremove, type);
|
||||||
if (size > 0)
|
if (size > 0)
|
||||||
break;
|
break;
|
||||||
lcremove = TAILQ_NEXT(lcremove, entry);
|
lcremove = layout_cell_get_neighbour_dir(lcremove, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* If none found, look towards the head. */
|
/* If none found, look towards the head. */
|
||||||
if (opposite && lcremove == NULL) {
|
if (opposite && lcremove == NULL) {
|
||||||
lcremove = TAILQ_PREV(lc, layout_cells, entry);
|
lcremove = layout_cell_get_neighbour_dir(lc, 0);
|
||||||
while (lcremove != NULL) {
|
while (lcremove != NULL) {
|
||||||
size = layout_resize_check(w, lcremove, type);
|
size = layout_resize_check(w, lcremove, type);
|
||||||
if (size > 0)
|
if (size > 0)
|
||||||
break;
|
break;
|
||||||
lcremove = TAILQ_PREV(lcremove, layout_cells, entry);
|
lcremove = layout_cell_get_neighbour_dir(lcremove, 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (lcremove == NULL)
|
if (lcremove == NULL)
|
||||||
@@ -1050,13 +1083,13 @@ layout_resize_pane_shrink(struct window *w, struct layout_cell *lc,
|
|||||||
size = layout_resize_check(w, lcremove, type);
|
size = layout_resize_check(w, lcremove, type);
|
||||||
if (size != 0)
|
if (size != 0)
|
||||||
break;
|
break;
|
||||||
lcremove = TAILQ_PREV(lcremove, layout_cells, entry);
|
lcremove = layout_cell_get_neighbour_dir(lcremove, 0);
|
||||||
} while (lcremove != NULL);
|
} while (lcremove != NULL);
|
||||||
if (lcremove == NULL)
|
if (lcremove == NULL)
|
||||||
return (0);
|
return (0);
|
||||||
|
|
||||||
/* And add onto the next cell (from the original cell). */
|
/* And add onto the next cell (from the original cell). */
|
||||||
lcadd = TAILQ_NEXT(lc, entry);
|
lcadd = layout_cell_get_neighbour_dir(lc, 1);
|
||||||
if (lcadd == NULL)
|
if (lcadd == NULL)
|
||||||
return (0);
|
return (0);
|
||||||
|
|
||||||
@@ -1650,7 +1683,10 @@ layout_get_tiled_cell(struct cmdq_item *item, struct args *args,
|
|||||||
return (NULL);
|
return (NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
window_push_zoom(wp->window, 1, (flags & SPAWN_ZOOM));
|
if (window_active_pane_is_over_zoom(w))
|
||||||
|
window_push_zoom(w, 0, 1);
|
||||||
|
else
|
||||||
|
window_push_zoom(w, 1, (flags & SPAWN_ZOOM));
|
||||||
lc = layout_split_pane(wp, type, size, flags);
|
lc = layout_split_pane(wp, type, size, flags);
|
||||||
if (lc == NULL)
|
if (lc == NULL)
|
||||||
*cause = xstrdup("no space for a new pane");
|
*cause = xstrdup("no space for a new pane");
|
||||||
@@ -1677,8 +1713,10 @@ layout_get_floating_cell(struct cmdq_item *item, struct args *args,
|
|||||||
return (NULL);
|
return (NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (flags & SPAWN_MODAL)
|
if (flags & SPAWN_FLOATOVERZOOM)
|
||||||
window_push_modal_zoom(w);
|
window_push_zoom(wp->window, 0, 1);
|
||||||
|
else if (window_active_pane_is_over_zoom(w))
|
||||||
|
window_push_zoom(wp->window, 0, 1);
|
||||||
else
|
else
|
||||||
window_push_zoom(wp->window, 1, (flags & SPAWN_ZOOM));
|
window_push_zoom(wp->window, 1, (flags & SPAWN_ZOOM));
|
||||||
lcnew = layout_floating_pane(w, wp, &fg);
|
lcnew = layout_floating_pane(w, wp, &fg);
|
||||||
@@ -1690,7 +1728,7 @@ layout_floating_args_parse(struct cmdq_item *item, struct args *args,
|
|||||||
enum pane_lines lines, struct window *w, struct layout_geometry *lg,
|
enum pane_lines lines, struct window *w, struct layout_geometry *lg,
|
||||||
char **cause)
|
char **cause)
|
||||||
{
|
{
|
||||||
int sx, sy, ox, oy;
|
int sx, sy, ox, oy, pad;
|
||||||
char *error = NULL;
|
char *error = NULL;
|
||||||
|
|
||||||
sx = lg->sx == UINT_MAX ? w->sx / 2 : lg->sx;
|
sx = lg->sx == UINT_MAX ? w->sx / 2 : lg->sx;
|
||||||
@@ -1739,12 +1777,20 @@ layout_floating_args_parse(struct cmdq_item *item, struct args *args,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!window_has_floating_panes(w)) {
|
||||||
|
w->last_new_pane_x = 0;
|
||||||
|
w->last_new_pane_y = 0;
|
||||||
|
}
|
||||||
if (ox == INT_MAX) {
|
if (ox == INT_MAX) {
|
||||||
if (w->last_new_pane_x == 0)
|
if (w->last_new_pane_x == 0)
|
||||||
ox = 4;
|
ox = 4;
|
||||||
else {
|
else {
|
||||||
|
if (lines != PANE_LINES_NONE)
|
||||||
|
pad = 1;
|
||||||
|
else
|
||||||
|
pad = 0;
|
||||||
ox = w->last_new_pane_x + 4;
|
ox = w->last_new_pane_x + 4;
|
||||||
if (w->last_new_pane_x > w->sx)
|
if (ox + sx + pad > (int)w->sx)
|
||||||
ox = 4;
|
ox = 4;
|
||||||
}
|
}
|
||||||
w->last_new_pane_x = ox;
|
w->last_new_pane_x = ox;
|
||||||
@@ -1755,8 +1801,12 @@ layout_floating_args_parse(struct cmdq_item *item, struct args *args,
|
|||||||
if (w->last_new_pane_y == 0)
|
if (w->last_new_pane_y == 0)
|
||||||
oy = 2;
|
oy = 2;
|
||||||
else {
|
else {
|
||||||
|
if (lines != PANE_LINES_NONE)
|
||||||
|
pad = 1;
|
||||||
|
else
|
||||||
|
pad = 0;
|
||||||
oy = w->last_new_pane_y + 2;
|
oy = w->last_new_pane_y + 2;
|
||||||
if (w->last_new_pane_y > w->sy)
|
if (oy + sy + pad > (int)w->sy)
|
||||||
oy = 2;
|
oy = 2;
|
||||||
}
|
}
|
||||||
w->last_new_pane_y = oy;
|
w->last_new_pane_y = oy;
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
/* $OpenBSD: menu.c,v 1.69 2026/07/14 19:07:03 nicm Exp $ */
|
/* $OpenBSD: menu.c,v 1.70 2026/08/17 07:33:55 nicm Exp $ */
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2019 Nicholas Marriott <nicholas.marriott@gmail.com>
|
* Copyright (c) 2019 Nicholas Marriott <nicholas.marriott@gmail.com>
|
||||||
@@ -337,6 +337,7 @@ menu_key(struct client *c, struct menu_data *md, struct key_event *event)
|
|||||||
struct cmdq_state *state;
|
struct cmdq_state *state;
|
||||||
enum cmd_parse_status status;
|
enum cmd_parse_status status;
|
||||||
char *error;
|
char *error;
|
||||||
|
key_code key;
|
||||||
|
|
||||||
if (KEYC_IS_MOUSE(event->key)) {
|
if (KEYC_IS_MOUSE(event->key)) {
|
||||||
/*
|
/*
|
||||||
@@ -387,7 +388,8 @@ menu_key(struct client *c, struct menu_data *md, struct key_event *event)
|
|||||||
name = menu->items[i].name;
|
name = menu->items[i].name;
|
||||||
if (name == NULL || *name == '-')
|
if (name == NULL || *name == '-')
|
||||||
continue;
|
continue;
|
||||||
if ((event->key & ~KEYC_MASK_FLAGS) == menu->items[i].key) {
|
key = (event->key & ~KEYC_MASK_FLAGS);
|
||||||
|
if (key == (menu->items[i].key & ~KEYC_MASK_FLAGS)) {
|
||||||
md->choice = i;
|
md->choice = i;
|
||||||
goto chosen;
|
goto chosen;
|
||||||
}
|
}
|
||||||
|
|||||||
+2
-2
@@ -1,4 +1,4 @@
|
|||||||
/* $OpenBSD: mode-tree.c,v 1.100 2026/07/14 19:07:03 nicm Exp $ */
|
/* $OpenBSD: mode-tree.c,v 1.101 2026/08/05 07:50:21 nicm Exp $ */
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2017 Nicholas Marriott <nicholas.marriott@gmail.com>
|
* Copyright (c) 2017 Nicholas Marriott <nicholas.marriott@gmail.com>
|
||||||
@@ -47,7 +47,7 @@ enum mode_tree_preview {
|
|||||||
"#[acs]x" MODE_TREE_PREFIX_STYLE " }," \
|
"#[acs]x" MODE_TREE_PREFIX_STYLE " }," \
|
||||||
"#{mode_tree_repeat}}" \
|
"#{mode_tree_repeat}}" \
|
||||||
"#{?mode_tree_branch," \
|
"#{?mode_tree_branch," \
|
||||||
"#[acs]#{?mode_tree_last,mq,tq}" MODE_TREE_PREFIX_STYLE "> ,}" \
|
"#[acs]#{?mode_tree_last,mq,tq}+" MODE_TREE_PREFIX_STYLE " ,}" \
|
||||||
"#{?mode_tree_has_children," \
|
"#{?mode_tree_has_children," \
|
||||||
"#{?mode_tree_expanded,#[fg=themered]-" MODE_TREE_PREFIX_STYLE " ," \
|
"#{?mode_tree_expanded,#[fg=themered]-" MODE_TREE_PREFIX_STYLE " ," \
|
||||||
"#[fg=themegreen]+" MODE_TREE_PREFIX_STYLE " }," \
|
"#[fg=themegreen]+" MODE_TREE_PREFIX_STYLE " }," \
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
/* $OpenBSD: monitor.c,v 1.7 2026/07/27 19:15:58 nicm Exp $ */
|
/* $OpenBSD: monitor.c,v 1.8 2026/09/07 10:15:25 nicm Exp $ */
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2026 Nicholas Marriott <nicholas.marriott@gmail.com>
|
* Copyright (c) 2026 Nicholas Marriott <nicholas.marriott@gmail.com>
|
||||||
@@ -614,8 +614,10 @@ monitor_parse(const char *value, char **name, enum monitor_type *type, int *id,
|
|||||||
*type = MONITOR_ALL_WINDOWS;
|
*type = MONITOR_ALL_WINDOWS;
|
||||||
else if (sscanf(what, "@%d", id) == 1 && *id >= 0)
|
else if (sscanf(what, "@%d", id) == 1 && *id >= 0)
|
||||||
*type = MONITOR_WINDOW;
|
*type = MONITOR_WINDOW;
|
||||||
else
|
else if (*what == '\0')
|
||||||
*type = MONITOR_SESSION;
|
*type = MONITOR_SESSION;
|
||||||
|
else
|
||||||
|
goto fail;
|
||||||
*name = xstrdup(copy);
|
*name = xstrdup(copy);
|
||||||
*format = xstrdup(split);
|
*format = xstrdup(split);
|
||||||
|
|
||||||
|
|||||||
+16
-4
@@ -1,4 +1,4 @@
|
|||||||
/* $OpenBSD: options-table.c,v 1.242 2026/07/27 08:03:01 nicm Exp $ */
|
/* $OpenBSD: options-table.c,v 1.245 2026/09/10 11:02:18 nicm Exp $ */
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2011 Nicholas Marriott <nicholas.marriott@gmail.com>
|
* Copyright (c) 2011 Nicholas Marriott <nicholas.marriott@gmail.com>
|
||||||
@@ -91,7 +91,7 @@ static const char *options_table_window_size_list[] = {
|
|||||||
"largest", "smallest", "manual", "latest", NULL
|
"largest", "smallest", "manual", "latest", NULL
|
||||||
};
|
};
|
||||||
static const char *options_table_remain_on_exit_list[] = {
|
static const char *options_table_remain_on_exit_list[] = {
|
||||||
"off", "on", "failed", "key", NULL
|
"off", "on", "failed", "key", "failed-key", NULL
|
||||||
};
|
};
|
||||||
static const char *options_table_destroy_unattached_list[] = {
|
static const char *options_table_destroy_unattached_list[] = {
|
||||||
"off", "on", "keep-last", "keep-group", NULL
|
"off", "on", "keep-last", "keep-group", NULL
|
||||||
@@ -305,6 +305,16 @@ const struct options_table_entry options_table[] = {
|
|||||||
"When this is reached, the oldest buffer is deleted."
|
"When this is reached, the oldest buffer is deleted."
|
||||||
},
|
},
|
||||||
|
|
||||||
|
{ .name = "clear-on-attach",
|
||||||
|
.type = OPTIONS_TABLE_FLAG,
|
||||||
|
.scope = OPTIONS_TABLE_SERVER,
|
||||||
|
.default_num = 1,
|
||||||
|
.text = "Whether to use the alternate screen and clear it when "
|
||||||
|
"a client is attached. When disabled, tmux does not "
|
||||||
|
"enter the alternate screen on attach so terminal "
|
||||||
|
"content before tmux remains in scrollback."
|
||||||
|
},
|
||||||
|
|
||||||
{ .name = "command-alias",
|
{ .name = "command-alias",
|
||||||
.type = OPTIONS_TABLE_STRING,
|
.type = OPTIONS_TABLE_STRING,
|
||||||
.scope = OPTIONS_TABLE_SERVER,
|
.scope = OPTIONS_TABLE_SERVER,
|
||||||
@@ -1240,7 +1250,8 @@ const struct options_table_entry options_table[] = {
|
|||||||
* underscore.
|
* underscore.
|
||||||
*/
|
*/
|
||||||
.default_str = "!\"#$%&'()*+,-./:;<=>?@[\\]^`{|}~",
|
.default_str = "!\"#$%&'()*+,-./:;<=>?@[\\]^`{|}~",
|
||||||
.text = "Characters considered to separate words."
|
.text = "Characters considered to separate words; a space matches "
|
||||||
|
"any character with the Unicode White_Space property."
|
||||||
},
|
},
|
||||||
|
|
||||||
/* Window options. */
|
/* Window options. */
|
||||||
@@ -1687,7 +1698,8 @@ const struct options_table_entry options_table[] = {
|
|||||||
.choices = options_table_remain_on_exit_list,
|
.choices = options_table_remain_on_exit_list,
|
||||||
.default_num = 0,
|
.default_num = 0,
|
||||||
.text = "Whether panes should remain ('on'), remain until a key is "
|
.text = "Whether panes should remain ('on'), remain until a key is "
|
||||||
"pressed ('key') or be automatically killed ('off' or "
|
"pressed after any exit ('key') or after a failure "
|
||||||
|
"('failed-key'), or be automatically killed ('off' or "
|
||||||
"'failed') when the program inside exits."
|
"'failed') when the program inside exits."
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
/* $OpenBSD: popup.c,v 1.76 2026/07/14 19:07:03 nicm Exp $ */
|
/* $OpenBSD: popup.c,v 1.77 2026/08/28 08:02:16 nicm Exp $ */
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2020 Nicholas Marriott <nicholas.marriott@gmail.com>
|
* Copyright (c) 2020 Nicholas Marriott <nicholas.marriott@gmail.com>
|
||||||
@@ -79,6 +79,7 @@ popup_free(struct popup_data *pd)
|
|||||||
|
|
||||||
if (pd->job != NULL)
|
if (pd->job != NULL)
|
||||||
job_free(pd->job);
|
job_free(pd->job);
|
||||||
|
if (pd->ictx != NULL)
|
||||||
input_free(pd->ictx);
|
input_free(pd->ictx);
|
||||||
|
|
||||||
free(pd->r.ranges);
|
free(pd->r.ranges);
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
/* $OpenBSD: proc.c,v 1.31 2026/06/08 21:38:19 nicm Exp $ */
|
/* $OpenBSD: proc.c,v 1.32 2026/08/04 13:16:03 claudio Exp $ */
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2015 Nicholas Marriott <nicholas.marriott@gmail.com>
|
* Copyright (c) 2015 Nicholas Marriott <nicholas.marriott@gmail.com>
|
||||||
@@ -79,7 +79,7 @@ static void
|
|||||||
proc_event_cb(__unused int fd, short events, void *arg)
|
proc_event_cb(__unused int fd, short events, void *arg)
|
||||||
{
|
{
|
||||||
struct tmuxpeer *peer = arg;
|
struct tmuxpeer *peer = arg;
|
||||||
ssize_t n;
|
int n;
|
||||||
struct imsg imsg;
|
struct imsg imsg;
|
||||||
|
|
||||||
if (!(peer->flags & PEER_BAD) && (events & EV_READ)) {
|
if (!(peer->flags & PEER_BAD) && (events & EV_READ)) {
|
||||||
@@ -88,7 +88,7 @@ proc_event_cb(__unused int fd, short events, void *arg)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
for (;;) {
|
for (;;) {
|
||||||
if ((n = imsg_get(&peer->ibuf, &imsg)) == -1) {
|
if ((n = imsgbuf_get(&peer->ibuf, &imsg)) == -1) {
|
||||||
peer->dispatchcb(NULL, peer->arg);
|
peer->dispatchcb(NULL, peer->arg);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
/* $OpenBSD: prompt.c,v 1.5 2026/07/13 10:29:17 nicm Exp $ */
|
/* $OpenBSD: prompt.c,v 1.7 2026/09/09 07:53:03 nicm Exp $ */
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2026 Nicholas Marriott <nicholas.marriott@gmail.com>
|
* Copyright (c) 2026 Nicholas Marriott <nicholas.marriott@gmail.com>
|
||||||
@@ -1334,7 +1334,7 @@ process_key:
|
|||||||
if (pr->index != size) {
|
if (pr->index != size) {
|
||||||
memmove(pr->buffer + pr->index,
|
memmove(pr->buffer + pr->index,
|
||||||
pr->buffer + pr->index + 1,
|
pr->buffer + pr->index + 1,
|
||||||
(size + 1 - pr->index) *
|
(size - pr->index) *
|
||||||
sizeof *pr->buffer);
|
sizeof *pr->buffer);
|
||||||
goto changed;
|
goto changed;
|
||||||
}
|
}
|
||||||
@@ -1493,9 +1493,11 @@ append_key:
|
|||||||
utf8_set(&tmp, key);
|
utf8_set(&tmp, key);
|
||||||
if (key <= 0x1f || key == 0x7f)
|
if (key <= 0x1f || key == 0x7f)
|
||||||
tmp.width = 2;
|
tmp.width = 2;
|
||||||
} else if (KEYC_IS_UNICODE(key))
|
} else if (KEYC_IS_UNICODE(key)) {
|
||||||
utf8_to_data(key, &tmp);
|
utf8_to_data(key, &tmp);
|
||||||
else
|
if (tmp.size == 0)
|
||||||
|
return (PROMPT_KEY_HANDLED);
|
||||||
|
} else
|
||||||
return (PROMPT_KEY_HANDLED);
|
return (PROMPT_KEY_HANDLED);
|
||||||
|
|
||||||
pr->buffer = xreallocarray(pr->buffer, size + 2,
|
pr->buffer = xreallocarray(pr->buffer, size + 2,
|
||||||
|
|||||||
+23
-20
@@ -1,33 +1,17 @@
|
|||||||
TESTS!= echo *.sh
|
TESTS!= echo *.sh
|
||||||
LOGDIR=logs
|
LOGDIR=logs
|
||||||
|
|
||||||
.PHONY: all
|
.PHONY: all prepare $(TESTS)
|
||||||
.NOTPARALLEL: all
|
|
||||||
|
|
||||||
all:
|
all: prepare $(TESTS)
|
||||||
@mkdir -p "$(LOGDIR)"; \
|
@failed=0; failures=; \
|
||||||
rm -f "$(LOGDIR)"/*.log; \
|
|
||||||
failed=0; failures=; \
|
|
||||||
for test in $(TESTS); do \
|
for test in $(TESTS); do \
|
||||||
base=$${test##*/}; \
|
base=$${test##*/}; \
|
||||||
log="$(LOGDIR)/$${base%.sh}.log"; \
|
log="$(LOGDIR)/$${base%.sh}.log"; \
|
||||||
rm -f "$$log"; \
|
if [ -f "$$log" ]; then \
|
||||||
printf '%-40s ' "$$test"; \
|
|
||||||
start=$$(date +%s); \
|
|
||||||
env -i LC_CTYPE=C.UTF-8 MallocNanoZone=0 \
|
|
||||||
sh -x "$$test" >"$$log" 2>&1; \
|
|
||||||
if [ $$? -eq 0 ]; then \
|
|
||||||
end=$$(date +%s); \
|
|
||||||
rm -f "$$log"; \
|
|
||||||
echo "PASS ($$((end - start))s)"; \
|
|
||||||
else \
|
|
||||||
end=$$(date +%s); \
|
|
||||||
echo "FAIL ($$((end - start))s)"; \
|
|
||||||
echo " log: $$log"; \
|
|
||||||
failed=1; \
|
failed=1; \
|
||||||
failures="$$failures $$test"; \
|
failures="$$failures $$test"; \
|
||||||
fi; \
|
fi; \
|
||||||
sleep 1; \
|
|
||||||
done; \
|
done; \
|
||||||
if [ "$$failed" -ne 0 ]; then \
|
if [ "$$failed" -ne 0 ]; then \
|
||||||
echo; \
|
echo; \
|
||||||
@@ -39,3 +23,22 @@ all:
|
|||||||
rmdir "$(LOGDIR)" 2>/dev/null || true; \
|
rmdir "$(LOGDIR)" 2>/dev/null || true; \
|
||||||
fi; \
|
fi; \
|
||||||
exit $$failed
|
exit $$failed
|
||||||
|
|
||||||
|
prepare:
|
||||||
|
@mkdir -p "$(LOGDIR)"
|
||||||
|
@rm -f "$(LOGDIR)"/*.log
|
||||||
|
|
||||||
|
$(TESTS): prepare
|
||||||
|
@base="$@"; base=$${base##*/}; \
|
||||||
|
log="$(LOGDIR)/$${base%.sh}.log"; \
|
||||||
|
start=$$(date +%s); \
|
||||||
|
if env -i LC_CTYPE=C.UTF-8 MallocNanoZone=0 \
|
||||||
|
sh -x "$@" >"$$log" 2>&1; then \
|
||||||
|
end=$$(date +%s); \
|
||||||
|
rm -f "$$log"; \
|
||||||
|
printf '%-40s PASS (%ss)\n' "$@" "$$((end - start))"; \
|
||||||
|
else \
|
||||||
|
end=$$(date +%s); \
|
||||||
|
printf '%-40s FAIL (%ss)\n log: %s\n' \
|
||||||
|
"$@" "$$((end - start))" "$$log"; \
|
||||||
|
fi
|
||||||
|
|||||||
+32
-2
@@ -2,8 +2,9 @@
|
|||||||
|
|
||||||
# monitor-activity, monitor-bell and monitor-silence: both the
|
# monitor-activity, monitor-bell and monitor-silence: both the
|
||||||
# alert-activity, alert-bell and alert-silence hooks and the winlink alert
|
# alert-activity, alert-bell and alert-silence hooks and the winlink alert
|
||||||
# flags (window_activity_flag, window_bell_flag, window_silence_flag and
|
# flags (window_activity_flag, window_bell_flag, window_silence_flag,
|
||||||
# the #, !, ~ characters in window_flags). The sessions are detached so
|
# their session equivalents and the #, !, ~ characters in window_flags).
|
||||||
|
# The sessions are detached so
|
||||||
# alert flags are set even on the current window; the *-action options
|
# alert flags are set even on the current window; the *-action options
|
||||||
# still decide whether the hooks fire. Panes run cat: activity is
|
# still decide whether the hooks fire. Panes run cat: activity is
|
||||||
# generated by the tty echo of send-keys and a bell by sending a BEL and
|
# generated by the tty echo of send-keys and a bell by sending a BEL and
|
||||||
@@ -111,6 +112,24 @@ flags_have()
|
|||||||
esac
|
esac
|
||||||
}
|
}
|
||||||
|
|
||||||
|
assert_session_flag()
|
||||||
|
{
|
||||||
|
flag="#{session_${1}_flag}"
|
||||||
|
expected=$2
|
||||||
|
|
||||||
|
# Only mon exists here. Check both window contexts and list-sessions.
|
||||||
|
for target in mon:w0 "mon:$3"; do
|
||||||
|
value=$($TMUX display -pt "$target" "$flag") ||
|
||||||
|
fail "display $flag failed"
|
||||||
|
[ "$value" = "$expected" ] ||
|
||||||
|
fail "expected $flag for $target to be '$expected' but got '$value'"
|
||||||
|
done
|
||||||
|
value=$($TMUX list-sessions -F "$flag") ||
|
||||||
|
fail "list-sessions $flag failed"
|
||||||
|
[ "$value" = "$expected" ] ||
|
||||||
|
fail "expected list-sessions $flag to be '$expected' but got '$value'"
|
||||||
|
}
|
||||||
|
|
||||||
flags_lack()
|
flags_lack()
|
||||||
{
|
{
|
||||||
target=$1
|
target=$1
|
||||||
@@ -150,10 +169,12 @@ $TMUX set-hook -g alert-silence \
|
|||||||
# alert-bell and sets the bell flag, shown as ! in window_flags.
|
# alert-bell and sets the bell flag, shown as ! in window_flags.
|
||||||
$TMUX neww -d -t mon: -n bellw cat || fail "new-window bellw failed"
|
$TMUX neww -d -t mon: -n bellw cat || fail "new-window bellw failed"
|
||||||
assert_fmt_unchanged mon:bellw '#{window_bell_flag}' 0
|
assert_fmt_unchanged mon:bellw '#{window_bell_flag}' 0
|
||||||
|
assert_session_flag bell 0 bellw
|
||||||
flags_lack mon:bellw '!'
|
flags_lack mon:bellw '!'
|
||||||
bell mon:bellw
|
bell mon:bellw
|
||||||
wait_for @log '|alert-bell:mon:bellw'
|
wait_for @log '|alert-bell:mon:bellw'
|
||||||
wait_for_fmt mon:bellw '#{window_bell_flag}' 1
|
wait_for_fmt mon:bellw '#{window_bell_flag}' 1
|
||||||
|
assert_session_flag bell 1 bellw
|
||||||
flags_have mon:bellw '!'
|
flags_have mon:bellw '!'
|
||||||
|
|
||||||
# Bells are not deduplicated: a second bell fires the hook again even
|
# Bells are not deduplicated: a second bell fires the hook again even
|
||||||
@@ -164,6 +185,7 @@ wait_for @log '|alert-bell:mon:bellw|alert-bell:mon:bellw'
|
|||||||
# Selecting the window clears the alert flags.
|
# Selecting the window clears the alert flags.
|
||||||
$TMUX selectw -t mon:bellw || fail "select-window bellw failed"
|
$TMUX selectw -t mon:bellw || fail "select-window bellw failed"
|
||||||
wait_for_fmt mon:bellw '#{window_bell_flag}' 0
|
wait_for_fmt mon:bellw '#{window_bell_flag}' 0
|
||||||
|
assert_session_flag bell 0 bellw
|
||||||
flags_lack mon:bellw '!'
|
flags_lack mon:bellw '!'
|
||||||
$TMUX selectw -t mon:w0 || fail "select-window w0 failed"
|
$TMUX selectw -t mon:w0 || fail "select-window w0 failed"
|
||||||
|
|
||||||
@@ -173,6 +195,7 @@ $TMUX set -g @log '' || fail "reset @log failed"
|
|||||||
bell mon:w0
|
bell mon:w0
|
||||||
wait_for @log '|alert-bell:mon:w0'
|
wait_for @log '|alert-bell:mon:w0'
|
||||||
wait_for_fmt mon:w0 '#{window_bell_flag}' 1
|
wait_for_fmt mon:w0 '#{window_bell_flag}' 1
|
||||||
|
assert_session_flag bell 1 bellw
|
||||||
$TMUX selectw -t mon:bellw || fail "select-window bellw failed"
|
$TMUX selectw -t mon:bellw || fail "select-window bellw failed"
|
||||||
$TMUX selectw -t mon:w0 || fail "select-window w0 failed"
|
$TMUX selectw -t mon:w0 || fail "select-window w0 failed"
|
||||||
wait_for_fmt mon:w0 '#{window_bell_flag}' 0
|
wait_for_fmt mon:w0 '#{window_bell_flag}' 0
|
||||||
@@ -199,6 +222,7 @@ $TMUX neww -d -t mon: -n actw cat || fail "new-window actw failed"
|
|||||||
$TMUX set -g @log '' || fail "reset @log failed"
|
$TMUX set -g @log '' || fail "reset @log failed"
|
||||||
activity mon:actw
|
activity mon:actw
|
||||||
assert_fmt_unchanged mon:actw '#{window_activity_flag}' 0
|
assert_fmt_unchanged mon:actw '#{window_activity_flag}' 0
|
||||||
|
assert_session_flag activity 0 actw
|
||||||
assert_unchanged @log ''
|
assert_unchanged @log ''
|
||||||
|
|
||||||
# With monitor-activity on, output fires alert-activity and sets the
|
# With monitor-activity on, output fires alert-activity and sets the
|
||||||
@@ -208,6 +232,7 @@ $TMUX set -wt mon:actw monitor-activity on ||
|
|||||||
activity mon:actw
|
activity mon:actw
|
||||||
wait_for @log '|alert-activity:mon:actw'
|
wait_for @log '|alert-activity:mon:actw'
|
||||||
wait_for_fmt mon:actw '#{window_activity_flag}' 1
|
wait_for_fmt mon:actw '#{window_activity_flag}' 1
|
||||||
|
assert_session_flag activity 1 actw
|
||||||
flags_have mon:actw '#'
|
flags_have mon:actw '#'
|
||||||
|
|
||||||
# While the flag is set further activity does not fire the hook again.
|
# While the flag is set further activity does not fire the hook again.
|
||||||
@@ -234,6 +259,7 @@ exec 3>"$OUT/fifo"
|
|||||||
wait_for_fmt mon: '#{session_attached}' 1
|
wait_for_fmt mon: '#{session_attached}' 1
|
||||||
$TMUX selectw -t mon:actw || fail "select-window actw failed"
|
$TMUX selectw -t mon:actw || fail "select-window actw failed"
|
||||||
wait_for_fmt mon:actw '#{window_activity_flag}' 0
|
wait_for_fmt mon:actw '#{window_activity_flag}' 0
|
||||||
|
assert_session_flag activity 0 actw
|
||||||
flags_lack mon:actw '#'
|
flags_lack mon:actw '#'
|
||||||
$TMUX selectw -t mon:w0 || fail "select-window w0 failed"
|
$TMUX selectw -t mon:w0 || fail "select-window w0 failed"
|
||||||
exec 3>&-
|
exec 3>&-
|
||||||
@@ -250,6 +276,7 @@ $TMUX set -wt mon:w0 monitor-activity on ||
|
|||||||
$TMUX set -g @log '' || fail "reset @log failed"
|
$TMUX set -g @log '' || fail "reset @log failed"
|
||||||
activity mon:w0
|
activity mon:w0
|
||||||
wait_for_fmt mon:w0 '#{window_activity_flag}' 1
|
wait_for_fmt mon:w0 '#{window_activity_flag}' 1
|
||||||
|
assert_session_flag activity 1 actw
|
||||||
assert_unchanged @log ''
|
assert_unchanged @log ''
|
||||||
$TMUX set -wut mon:w0 monitor-activity ||
|
$TMUX set -wut mon:w0 monitor-activity ||
|
||||||
fail "unset monitor-activity w0 failed"
|
fail "unset monitor-activity w0 failed"
|
||||||
@@ -273,16 +300,19 @@ $TMUX selectw -t mon:w0 || fail "select-window w0 failed"
|
|||||||
# running.
|
# running.
|
||||||
$TMUX neww -d -t mon: -n silw cat || fail "new-window silw failed"
|
$TMUX neww -d -t mon: -n silw cat || fail "new-window silw failed"
|
||||||
assert_fmt_unchanged mon:silw '#{window_silence_flag}' 0
|
assert_fmt_unchanged mon:silw '#{window_silence_flag}' 0
|
||||||
|
assert_session_flag silence 0 silw
|
||||||
$TMUX set -g @log '' || fail "reset @log failed"
|
$TMUX set -g @log '' || fail "reset @log failed"
|
||||||
$TMUX set -wt mon:silw monitor-silence 1 || fail "set monitor-silence failed"
|
$TMUX set -wt mon:silw monitor-silence 1 || fail "set monitor-silence failed"
|
||||||
wait_for @log '|alert-silence:mon:silw'
|
wait_for @log '|alert-silence:mon:silw'
|
||||||
wait_for_fmt mon:silw '#{window_silence_flag}' 1
|
wait_for_fmt mon:silw '#{window_silence_flag}' 1
|
||||||
|
assert_session_flag silence 1 silw
|
||||||
flags_have mon:silw '~'
|
flags_have mon:silw '~'
|
||||||
assert_unchanged @log '|alert-silence:mon:silw'
|
assert_unchanged @log '|alert-silence:mon:silw'
|
||||||
$TMUX set -wt mon:silw monitor-silence 0 ||
|
$TMUX set -wt mon:silw monitor-silence 0 ||
|
||||||
fail "reset monitor-silence failed"
|
fail "reset monitor-silence failed"
|
||||||
$TMUX selectw -t mon:silw || fail "select-window silw failed"
|
$TMUX selectw -t mon:silw || fail "select-window silw failed"
|
||||||
wait_for_fmt mon:silw '#{window_silence_flag}' 0
|
wait_for_fmt mon:silw '#{window_silence_flag}' 0
|
||||||
|
assert_session_flag silence 0 silw
|
||||||
flags_lack mon:silw '~'
|
flags_lack mon:silw '~'
|
||||||
$TMUX selectw -t mon:w0 || fail "select-window w0 failed"
|
$TMUX selectw -t mon:w0 || fail "select-window w0 failed"
|
||||||
|
|
||||||
|
|||||||
+22
-1
@@ -28,7 +28,7 @@ $TMUX kill-server 2>/dev/null
|
|||||||
|
|
||||||
TMP=$(mktemp)
|
TMP=$(mktemp)
|
||||||
TMP2=$(mktemp)
|
TMP2=$(mktemp)
|
||||||
trap 'rm -f "$TMP" "$TMP2"; $TMUX kill-server 2>/dev/null' 0 1 15
|
trap 'rm -f "$TMP" "$TMP2" "$TMP".*; $TMUX kill-server 2>/dev/null' 0 1 15
|
||||||
|
|
||||||
# check_ok $cmd...
|
# check_ok $cmd...
|
||||||
#
|
#
|
||||||
@@ -270,6 +270,27 @@ if [ $? -eq 0 ]; then
|
|||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
# A write error after a successful open must be reported to the server.
|
||||||
|
dd if=/dev/zero of="$TMP.big" bs=100000 count=1 2>/dev/null
|
||||||
|
check_ok load-buffer -b write-error "$TMP.big"
|
||||||
|
mkfifo "$TMP.fifo" || exit 1
|
||||||
|
dd if="$TMP.fifo" of="$TMP.got" bs=1 count=8 2>/dev/null &
|
||||||
|
reader=$!
|
||||||
|
exec 3>"$TMP.fifo"
|
||||||
|
out=$($TMUX save-buffer -b write-error "$TMP.fifo" 3>&- 2>&1)
|
||||||
|
status=$?
|
||||||
|
exec 3>&-
|
||||||
|
wait "$reader"
|
||||||
|
if [ $status -eq 0 ]; then
|
||||||
|
echo "save-buffer after write error succeeded."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
if [ "$(wc -c <"$TMP.got")" -ne 8 ]; then
|
||||||
|
echo "save-buffer write error test wrote wrong amount."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
check_ok delete-buffer -b write-error
|
||||||
|
|
||||||
# save-buffer - writes to stdout and load-buffer - reads from stdin.
|
# save-buffer - writes to stdout and load-buffer - reads from stdin.
|
||||||
out=$($TMUX save-buffer -b sb -)
|
out=$($TMUX save-buffer -b sb -)
|
||||||
if [ "$out" != "data" ]; then
|
if [ "$out" != "data" ]; then
|
||||||
|
|||||||
@@ -0,0 +1,38 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
# capture-pane -I line timestamps
|
||||||
|
|
||||||
|
PATH=/bin:/usr/bin
|
||||||
|
TERM=screen
|
||||||
|
|
||||||
|
[ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux)
|
||||||
|
TMUX="$TEST_TMUX -LtestA$$ -f/dev/null"
|
||||||
|
TMP=$(mktemp)
|
||||||
|
trap '$TMUX kill-server 2>/dev/null; rm -f "$TMP"' 0 1 15
|
||||||
|
$TMUX kill-server 2>/dev/null
|
||||||
|
|
||||||
|
before=$(date +%s)
|
||||||
|
$TMUX new-session -d -x 40 -y 5 'seq 1 12; sleep 10' || exit 1
|
||||||
|
sleep 1
|
||||||
|
after=$(date +%s)
|
||||||
|
|
||||||
|
$TMUX capture-pane -pILF -S - -E - >"$TMP" || exit 1
|
||||||
|
awk -v before="$before" -v after="$after" '
|
||||||
|
$1 !~ /^-?[0-9]+$/ || $2 !~ /^[0-9]+$/ { exit 1 }
|
||||||
|
$4 == "1" {
|
||||||
|
if ($2 < before || $2 > after)
|
||||||
|
exit 1
|
||||||
|
history = 1
|
||||||
|
}
|
||||||
|
$4 == "9" {
|
||||||
|
if ($2 != 0)
|
||||||
|
exit 1
|
||||||
|
visible = 1
|
||||||
|
}
|
||||||
|
END { if (!history || !visible) exit 1 }
|
||||||
|
' "$TMP" || {
|
||||||
|
cat "$TMP"
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
exit 0
|
||||||
Executable → Regular
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
@@ -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
|
||||||
@@ -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
|
||||||
@@ -0,0 +1,53 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
# Width cache ranges must include their endpoint without overflowing wchar_t.
|
||||||
|
|
||||||
|
PATH=/bin:/usr/bin
|
||||||
|
TERM=screen
|
||||||
|
|
||||||
|
[ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux)
|
||||||
|
TMUX="$TEST_TMUX -LtestA$$ -f/dev/null"
|
||||||
|
SERVER=
|
||||||
|
WATCHDOG=
|
||||||
|
|
||||||
|
cleanup()
|
||||||
|
{
|
||||||
|
if [ -n "$WATCHDOG" ]; then
|
||||||
|
kill "$WATCHDOG" 2>/dev/null
|
||||||
|
wait "$WATCHDOG" 2>/dev/null
|
||||||
|
fi
|
||||||
|
[ -n "$SERVER" ] && kill -9 "$SERVER" 2>/dev/null
|
||||||
|
}
|
||||||
|
trap cleanup 0
|
||||||
|
trap 'exit 1' 1 2 15
|
||||||
|
|
||||||
|
$TMUX new-session -d 'exec sleep 60' || exit 1
|
||||||
|
SERVER=$($TMUX display-message -p '#{pid}') || exit 1
|
||||||
|
|
||||||
|
# kill-server cannot stop a server stuck rebuilding the width cache.
|
||||||
|
(
|
||||||
|
sleep 5
|
||||||
|
kill -9 "$SERVER" 2>/dev/null
|
||||||
|
) &
|
||||||
|
WATCHDOG=$!
|
||||||
|
|
||||||
|
$TMUX set -g codepoint-widths 'U+1F600=2' || exit 1
|
||||||
|
|
||||||
|
# Cover both signed and unsigned 32-bit wchar_t. Values above WCHAR_MAX
|
||||||
|
# are ignored by the parser on platforms where they cannot be represented.
|
||||||
|
for value in U+7FFFFFFF=1 U+7FFFFFFE-U+7FFFFFFF=2 \
|
||||||
|
U+FFFFFFFF=1 U+FFFFFFFE-U+FFFFFFFF=2; do
|
||||||
|
$TMUX set -g codepoint-widths "$value" || exit 1
|
||||||
|
[ "$($TMUX display-message -p alive)" = alive ] || exit 1
|
||||||
|
done
|
||||||
|
|
||||||
|
# Check that an ordinary range still includes both endpoints and stops there.
|
||||||
|
$TMUX set -g codepoint-widths 'U+03B1-U+03B3=2' || exit 1
|
||||||
|
$TMUX set -g @text 'αβγδ' || exit 1
|
||||||
|
[ "$($TMUX display-message -p '#{w:@text}')" = 7 ] || exit 1
|
||||||
|
$TMUX set -gu codepoint-widths || exit 1
|
||||||
|
[ "$($TMUX display-message -p '#{w:@text}')" = 4 ] || exit 1
|
||||||
|
|
||||||
|
$TMUX kill-server || exit 1
|
||||||
|
SERVER=
|
||||||
|
exit 0
|
||||||
@@ -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,0 +1,77 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
# A control client whose terminal has stopped accepting output (for example
|
||||||
|
# its ssh connection or terminal emulator died) never drains the output that
|
||||||
|
# server_client_check_exit waits on before sending MSG_EXIT, and may then
|
||||||
|
# never close its socket either. This must not prevent the server from
|
||||||
|
# exiting: after kill-server the server previously stayed alive forever with
|
||||||
|
# server_exit set, closing every new connection immediately, so every new
|
||||||
|
# client failed with "server exited unexpectedly" until the server was killed
|
||||||
|
# by hand. The server now bounds the exit handshake: it discards output it
|
||||||
|
# can never deliver and drops the client if it still does not close.
|
||||||
|
#
|
||||||
|
# The stalled terminal is simulated by a fifo whose read end is held open but
|
||||||
|
# never read.
|
||||||
|
|
||||||
|
PATH=/bin:/usr/bin
|
||||||
|
TERM=screen
|
||||||
|
|
||||||
|
[ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux)
|
||||||
|
TMUX="$TEST_TMUX -Ltest$$"
|
||||||
|
$TMUX kill-server 2>/dev/null
|
||||||
|
|
||||||
|
DIR=$(mktemp -d)
|
||||||
|
FIFO=$DIR/fifo
|
||||||
|
SERVER=
|
||||||
|
|
||||||
|
mkfifo "$FIFO" || exit 1
|
||||||
|
|
||||||
|
cleanup() {
|
||||||
|
[ -n "$SERVER" ] && kill -9 "$SERVER" 2>/dev/null
|
||||||
|
$TMUX kill-server 2>/dev/null
|
||||||
|
exec 8<&- 2>/dev/null
|
||||||
|
rm -rf "$DIR"
|
||||||
|
}
|
||||||
|
trap cleanup 0 1 15
|
||||||
|
|
||||||
|
# A detached session whose pane floods printable output forever.
|
||||||
|
$TMUX -f/dev/null new -d -x 80 -y 24 -s rt 'cat /dev/zero | tr "\000" x' || exit 1
|
||||||
|
SERVER=$($TMUX display -pt rt '#{pid}')
|
||||||
|
|
||||||
|
# Attach a control client with output down the fifo; stdin held open by sleep
|
||||||
|
# so it stays attached. The fifo fills and is never read, so a backlog forms
|
||||||
|
# in the server that can never be delivered.
|
||||||
|
( sleep 60 ) | $TMUX -f/dev/null -C attach -t rt >"$FIFO" 2>&1 &
|
||||||
|
exec 8<"$FIFO"
|
||||||
|
|
||||||
|
n=0
|
||||||
|
while [ $n -lt 50 ]; do
|
||||||
|
$TMUX lsc -F '#{client_name}' 2>/dev/null | grep -q . && break
|
||||||
|
sleep 0.1
|
||||||
|
n=$((n + 1))
|
||||||
|
done
|
||||||
|
$TMUX lsc -F '#{client_name}' 2>/dev/null | grep -q . ||
|
||||||
|
{ echo "control client did not attach"; exit 1; }
|
||||||
|
|
||||||
|
# Let the pane flood fill the fifo so the client's output is stuck.
|
||||||
|
sleep 2
|
||||||
|
|
||||||
|
# Ask the server to exit. This sets CLIENT_EXIT on the stalled client, whose
|
||||||
|
# output can never drain.
|
||||||
|
$TMUX kill-server 2>/dev/null
|
||||||
|
|
||||||
|
# The server must exit within the exit timeout (10 seconds) plus slack,
|
||||||
|
# rather than staying in limbo forever rejecting new clients.
|
||||||
|
n=0
|
||||||
|
while [ $n -lt 100 ]; do
|
||||||
|
if ! kill -0 "$SERVER" 2>/dev/null; then
|
||||||
|
SERVER=
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
sleep 0.2
|
||||||
|
n=$((n + 1))
|
||||||
|
done
|
||||||
|
|
||||||
|
echo "server did not exit after kill-server; new clients see:"
|
||||||
|
$TMUX ls 2>&1
|
||||||
|
exit 1
|
||||||
@@ -19,7 +19,7 @@ PATH=/bin:/usr/bin
|
|||||||
TERM=screen
|
TERM=screen
|
||||||
|
|
||||||
[ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux)
|
[ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux)
|
||||||
TMUX="$TEST_TMUX -Ltest"
|
TMUX="$TEST_TMUX -Ltest$$"
|
||||||
$TMUX kill-server 2>/dev/null
|
$TMUX kill-server 2>/dev/null
|
||||||
|
|
||||||
DIR=$(mktemp -d)
|
DIR=$(mktemp -d)
|
||||||
|
|||||||
Executable
+123
@@ -0,0 +1,123 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
# Check that control client flags are available in every format expansion path
|
||||||
|
# used to produce layouts. The layout strings themselves are tested separately.
|
||||||
|
|
||||||
|
PATH=/bin:/usr/bin
|
||||||
|
TERM=screen
|
||||||
|
LC_ALL=C.UTF-8
|
||||||
|
LANG=C.UTF-8
|
||||||
|
export PATH TERM LC_ALL LANG
|
||||||
|
|
||||||
|
[ -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
|
||||||
|
CFG=$DIR/flags.conf
|
||||||
|
PID=
|
||||||
|
|
||||||
|
fail()
|
||||||
|
{
|
||||||
|
echo "$*" >&2
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
cleanup()
|
||||||
|
{
|
||||||
|
exec 3>&-
|
||||||
|
[ -n "$PID" ] && kill "$PID" 2>/dev/null
|
||||||
|
$TMUX kill-server 2>/dev/null
|
||||||
|
rm -rf "$DIR"
|
||||||
|
}
|
||||||
|
trap cleanup EXIT
|
||||||
|
|
||||||
|
wait_for()
|
||||||
|
{
|
||||||
|
pattern=$1
|
||||||
|
i=0
|
||||||
|
|
||||||
|
while [ "$i" -lt 50 ]; do
|
||||||
|
grep -F -- "$pattern" "$OUT" >/dev/null 2>&1 && return 0
|
||||||
|
if [ -n "$PID" ] && ! kill -0 "$PID" 2>/dev/null; then
|
||||||
|
fail "control client exited waiting for: $pattern"
|
||||||
|
fi
|
||||||
|
sleep 0.1
|
||||||
|
i=$((i + 1))
|
||||||
|
done
|
||||||
|
fail "missing: $pattern"
|
||||||
|
}
|
||||||
|
|
||||||
|
send()
|
||||||
|
{
|
||||||
|
printf '%s\n' "$*" >&3
|
||||||
|
}
|
||||||
|
|
||||||
|
check_commands()
|
||||||
|
{
|
||||||
|
name=$1
|
||||||
|
expected=$2
|
||||||
|
match='#{m:*new-layouts*,#{client_flags}}'
|
||||||
|
|
||||||
|
send "display-message -p 'DISPLAY-$name $match'"
|
||||||
|
wait_for "DISPLAY-$name $expected"
|
||||||
|
send "list-panes -F 'PANES-$name $match'"
|
||||||
|
wait_for "PANES-$name $expected"
|
||||||
|
send "list-windows -F 'WINDOWS-$name $match'"
|
||||||
|
wait_for "WINDOWS-$name $expected"
|
||||||
|
send "list-sessions -F 'SESSIONS-$name $match'"
|
||||||
|
wait_for "SESSIONS-$name $expected"
|
||||||
|
send "list-clients -F 'CLIENTS-$name $match'"
|
||||||
|
wait_for "CLIENTS-$name $expected"
|
||||||
|
}
|
||||||
|
|
||||||
|
check_config()
|
||||||
|
{
|
||||||
|
name=$1
|
||||||
|
expected=$2
|
||||||
|
|
||||||
|
send "source-file '$CFG'"
|
||||||
|
send "display-message -p 'CONFIG-$name #{@config-new-layouts}'"
|
||||||
|
wait_for "CONFIG-$name $expected"
|
||||||
|
}
|
||||||
|
|
||||||
|
cat >"$CFG" <<'EOF'
|
||||||
|
%if #{m:*new-layouts*,#{client_flags}}
|
||||||
|
set-option -g @config-new-layouts 1
|
||||||
|
%else
|
||||||
|
set-option -g @config-new-layouts 0
|
||||||
|
%endif
|
||||||
|
EOF
|
||||||
|
|
||||||
|
$TMUX kill-server 2>/dev/null
|
||||||
|
$TMUX new-session -d -s layouts -x 80 -y 24 || exit 1
|
||||||
|
$TMUX split-window -h -t layouts: || exit 1
|
||||||
|
|
||||||
|
mkfifo "$FIFO" || exit 1
|
||||||
|
: >"$OUT"
|
||||||
|
$TMUX -C attach-session -t layouts <"$FIFO" >"$OUT" 2>&1 &
|
||||||
|
PID=$!
|
||||||
|
exec 3>"$FIFO"
|
||||||
|
|
||||||
|
send 'display-message -p READY'
|
||||||
|
wait_for READY
|
||||||
|
|
||||||
|
check_commands OFF 0
|
||||||
|
check_config OFF 0
|
||||||
|
|
||||||
|
send 'refresh-client -f new-layouts'
|
||||||
|
check_commands ON 1
|
||||||
|
check_config ON 1
|
||||||
|
|
||||||
|
send 'refresh-client -f !new-layouts'
|
||||||
|
check_commands OFF-AGAIN 0
|
||||||
|
check_config OFF-AGAIN 0
|
||||||
|
|
||||||
|
# The list commands also run for unattached command clients. Supplying that
|
||||||
|
# client to formats must not make the session loop dereference a NULL session.
|
||||||
|
$TMUX list-sessions -F '#{S:all,active}' >/dev/null ||
|
||||||
|
fail "session loop failed for unattached command client"
|
||||||
|
$TMUX has-session -t layouts || fail "server exited"
|
||||||
|
|
||||||
|
exit 0
|
||||||
@@ -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
|
||||||
@@ -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
|
||||||
@@ -30,8 +30,11 @@ killw
|
|||||||
EOF
|
EOF
|
||||||
sleep 1
|
sleep 1
|
||||||
$TMUX has || exit 1
|
$TMUX has || exit 1
|
||||||
$TMUX lsp -aF '#{pane_id} #{window_layout}' >$TMP || exit 1
|
# Use a control client to request legacy layouts, keeping only pane lines
|
||||||
cat <<EOF|cmp -s $TMP - || exit 1
|
# from the control protocol output.
|
||||||
|
$TMUX -C lsp -aF '#{pane_id} #{window_layout}' |
|
||||||
|
grep '^%[0-9]' >$TMP || exit 1
|
||||||
|
cat <<EOF|cmp $TMP - || exit 1
|
||||||
%0 f5ab,200x200,0,0[200x50,0,0,0,200x149,0,51,3]
|
%0 f5ab,200x200,0,0[200x50,0,0,0,200x149,0,51,3]
|
||||||
%3 f5ab,200x200,0,0[200x50,0,0,0,200x149,0,51,3]
|
%3 f5ab,200x200,0,0[200x50,0,0,0,200x149,0,51,3]
|
||||||
%2 dcbd,200x200,0,0[200x100,0,0,2,200x99,0,101,4]
|
%2 dcbd,200x200,0,0[200x100,0,0,2,200x99,0,101,4]
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ PATH=/bin:/usr/bin
|
|||||||
TERM=screen
|
TERM=screen
|
||||||
|
|
||||||
[ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux)
|
[ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux)
|
||||||
TMUX="$TEST_TMUX -Ltest"
|
TMUX="$TEST_TMUX -Ltest$$"
|
||||||
$TMUX kill-server 2>/dev/null
|
$TMUX kill-server 2>/dev/null
|
||||||
|
|
||||||
FIFO=$(mktemp -u)
|
FIFO=$(mktemp -u)
|
||||||
|
|||||||
@@ -0,0 +1,158 @@
|
|||||||
|
#!/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)
|
||||||
|
IN="$TMPDIR/in"
|
||||||
|
OUT="$TMPDIR/out"
|
||||||
|
BADCFG="$TMPDIR/bad.conf"
|
||||||
|
PID=
|
||||||
|
|
||||||
|
cleanup()
|
||||||
|
{
|
||||||
|
exec 3>&- 2>/dev/null
|
||||||
|
[ -n "$PID" ] && kill "$PID" 2>/dev/null
|
||||||
|
$TMUX kill-server 2>/dev/null
|
||||||
|
rm -rf "$TMPDIR"
|
||||||
|
}
|
||||||
|
trap cleanup EXIT
|
||||||
|
|
||||||
|
fail()
|
||||||
|
{
|
||||||
|
echo "$1" >&2
|
||||||
|
[ -s "$OUT" ] && cat "$OUT" >&2
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
wait_for()
|
||||||
|
{
|
||||||
|
pattern=$1
|
||||||
|
timeout=${2:-60}
|
||||||
|
i=0
|
||||||
|
|
||||||
|
while [ "$i" -lt "$timeout" ]; do
|
||||||
|
grep -F -- "$pattern" "$OUT" >/dev/null 2>&1 && return 0
|
||||||
|
sleep 0.1
|
||||||
|
i=$((i + 1))
|
||||||
|
done
|
||||||
|
fail "missing: $pattern"
|
||||||
|
}
|
||||||
|
|
||||||
|
wait_for_count()
|
||||||
|
{
|
||||||
|
pattern=$1
|
||||||
|
expected=$2
|
||||||
|
timeout=${3:-60}
|
||||||
|
i=0
|
||||||
|
|
||||||
|
while [ "$i" -lt "$timeout" ]; do
|
||||||
|
count=$(grep -F -c -- "$pattern" "$OUT" 2>/dev/null)
|
||||||
|
[ "$count" -ge "$expected" ] && return 0
|
||||||
|
sleep 0.1
|
||||||
|
i=$((i + 1))
|
||||||
|
done
|
||||||
|
fail "missing count $expected for: $pattern"
|
||||||
|
}
|
||||||
|
|
||||||
|
send()
|
||||||
|
{
|
||||||
|
printf '%s\n' "$1" >&3 || fail "failed to send: $1"
|
||||||
|
}
|
||||||
|
|
||||||
|
# A notification is a standalone protocol message. It must never be part of
|
||||||
|
# the response between the real %begin and matching %end or %error for a
|
||||||
|
# command. Ignore guard-looking command output while a real guard is open.
|
||||||
|
check_guards()
|
||||||
|
{
|
||||||
|
awk '
|
||||||
|
function notification(line) {
|
||||||
|
return line ~ /^%(sessions-changed|session-changed |unlinked-window-add )/ ||
|
||||||
|
line ~ /^%(pause |continue |config-error )/
|
||||||
|
}
|
||||||
|
!open && $0 ~ /^%begin [0-9]+ [0-9]+ [0-9]+$/ {
|
||||||
|
open = 1
|
||||||
|
time = $2
|
||||||
|
number = $3
|
||||||
|
flags = $4
|
||||||
|
next
|
||||||
|
}
|
||||||
|
open && ($0 == "%end " time " " number " " flags ||
|
||||||
|
$0 == "%error " time " " number " " flags) {
|
||||||
|
open = 0
|
||||||
|
next
|
||||||
|
}
|
||||||
|
notification($0) {
|
||||||
|
seen[$1]++
|
||||||
|
if (open) {
|
||||||
|
print "notification inside guard: " $0 > "/dev/stderr"
|
||||||
|
bad = 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
END {
|
||||||
|
if (open) {
|
||||||
|
print "unterminated command guard" > "/dev/stderr"
|
||||||
|
bad = 1
|
||||||
|
}
|
||||||
|
if (seen["%sessions-changed"] < 2 ||
|
||||||
|
seen["%session-changed"] < 1 ||
|
||||||
|
seen["%unlinked-window-add"] < 2 ||
|
||||||
|
seen["%pause"] < 1 || seen["%continue"] < 1 ||
|
||||||
|
seen["%config-error"] < 1) {
|
||||||
|
print "missing expected notification" > "/dev/stderr"
|
||||||
|
bad = 1
|
||||||
|
}
|
||||||
|
exit bad
|
||||||
|
}' "$OUT" || fail "bad control protocol notification ordering"
|
||||||
|
}
|
||||||
|
|
||||||
|
$TMUX kill-server 2>/dev/null
|
||||||
|
$TMUX new-session -d -s guard -x 80 -y 24 || exit 1
|
||||||
|
pane=$($TMUX display-message -p -t guard:0.0 '#{pane_id}') || exit 1
|
||||||
|
|
||||||
|
mkfifo "$IN" || exit 1
|
||||||
|
: >"$OUT"
|
||||||
|
$TMUX -C attach-session -t guard <"$IN" >"$OUT" 2>&1 &
|
||||||
|
PID=$!
|
||||||
|
exec 3>"$IN"
|
||||||
|
|
||||||
|
# Attaching generates a session notification synchronously from inside the
|
||||||
|
# attach command. It must follow the closing guard.
|
||||||
|
wait_for '%session-changed '
|
||||||
|
|
||||||
|
# Guard-looking output is arbitrary command output, not protocol state. Make
|
||||||
|
# new-session both generate notifications and print an unmatched fake %begin;
|
||||||
|
# it must not leave those notifications permanently deferred.
|
||||||
|
send "set-option -g @fake-begin '%begin 1 2 3'"
|
||||||
|
send "new-session -d -P -F '#{@fake-begin}' -s fake-begin"
|
||||||
|
wait_for_count '%sessions-changed' 1
|
||||||
|
|
||||||
|
# Likewise, fake %end output from a command that generates multiple
|
||||||
|
# notifications must not flush them before the command actually ends.
|
||||||
|
send "set-option -g @fake-end '%end 1 2 3'"
|
||||||
|
send "new-session -d -P -F '#{@fake-end}' -s fake-end"
|
||||||
|
wait_for_count '%sessions-changed' 2
|
||||||
|
|
||||||
|
# Exercise notification paths outside control-notify.c.
|
||||||
|
send "refresh-client -A '$pane:pause'"
|
||||||
|
wait_for "%pause $pane"
|
||||||
|
send "refresh-client -A '$pane:continue'"
|
||||||
|
wait_for "%continue $pane"
|
||||||
|
|
||||||
|
printf '%s\n' 'not-a-command' >"$BADCFG"
|
||||||
|
send "source-file '$BADCFG'"
|
||||||
|
wait_for '%config-error '
|
||||||
|
|
||||||
|
# A final command guarantees all earlier command guards have completed before
|
||||||
|
# the transcript is checked.
|
||||||
|
send "display-message -p 'guard-check-done'"
|
||||||
|
wait_for 'guard-check-done'
|
||||||
|
check_guards
|
||||||
|
|
||||||
|
exit 0
|
||||||
@@ -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
|
||||||
@@ -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
|
||||||
@@ -117,6 +117,7 @@ $TMUX send-keys -X copy-selection
|
|||||||
# Test that vi cursor movement does not stop on the padding cell of a wide
|
# Test that vi cursor movement does not stop on the padding cell of a wide
|
||||||
# character at the end of a line.
|
# character at the end of a line.
|
||||||
$TMUX kill-server 2>/dev/null
|
$TMUX kill-server 2>/dev/null
|
||||||
|
sleep 1
|
||||||
$TMUX new -d -x20 -y5 \
|
$TMUX new -d -x20 -y5 \
|
||||||
"printf 'abc中\nxyz\n'; exec cat" || exit 1
|
"printf 'abc中\nxyz\n'; exec cat" || exit 1
|
||||||
$TMUX set-window-option -g mode-keys vi
|
$TMUX set-window-option -g mode-keys vi
|
||||||
|
|||||||
@@ -0,0 +1,163 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
PATH=/bin:/usr/bin
|
||||||
|
TERM=screen
|
||||||
|
|
||||||
|
[ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux)
|
||||||
|
|
||||||
|
fail()
|
||||||
|
{
|
||||||
|
echo "$1"
|
||||||
|
[ -z "$TMUX" ] || $TMUX kill-server 2>/dev/null
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
goto_cell()
|
||||||
|
{
|
||||||
|
row=$1
|
||||||
|
column=$2
|
||||||
|
|
||||||
|
$TMUX send-keys -X history-top || fail "$mode: history-top failed"
|
||||||
|
$TMUX send-keys -X start-of-line || fail "$mode: start-of-line failed"
|
||||||
|
[ "$row" -eq 0 ] ||
|
||||||
|
$TMUX send-keys -X -N "$row" cursor-down ||
|
||||||
|
fail "$mode: cursor-down failed"
|
||||||
|
[ "$column" -eq 0 ] ||
|
||||||
|
$TMUX send-keys -X -N "$column" cursor-right ||
|
||||||
|
fail "$mode: cursor-right failed"
|
||||||
|
}
|
||||||
|
|
||||||
|
check_cursor()
|
||||||
|
{
|
||||||
|
expected=$1
|
||||||
|
actual=$($TMUX display-message -p '#{copy_cursor_x},#{copy_cursor_y}')
|
||||||
|
[ "$actual" = "$expected" ] ||
|
||||||
|
fail "$mode: expected cursor $expected, got $actual"
|
||||||
|
}
|
||||||
|
|
||||||
|
check_word()
|
||||||
|
{
|
||||||
|
row=$1
|
||||||
|
column=$2
|
||||||
|
expected=$3
|
||||||
|
|
||||||
|
goto_cell "$row" "$column"
|
||||||
|
$TMUX set-buffer sentinel || fail "$mode: set-buffer failed"
|
||||||
|
$TMUX send-keys -X select-word || fail "$mode: select-word failed"
|
||||||
|
$TMUX send-keys -X copy-selection || fail "$mode: copy-selection failed"
|
||||||
|
actual=$($TMUX show-buffer 2>/dev/null)
|
||||||
|
[ "$actual" = "$expected" ] ||
|
||||||
|
fail "$mode: expected word '$expected', got '$actual'"
|
||||||
|
}
|
||||||
|
|
||||||
|
check_next_word()
|
||||||
|
{
|
||||||
|
row=$1
|
||||||
|
expected=$2
|
||||||
|
|
||||||
|
goto_cell "$row" 0
|
||||||
|
$TMUX send-keys -X next-word || fail "$mode: next-word failed"
|
||||||
|
$TMUX set-buffer sentinel || fail "$mode: set-buffer failed"
|
||||||
|
$TMUX send-keys -X select-word || fail "$mode: select-word failed"
|
||||||
|
$TMUX send-keys -X copy-selection || fail "$mode: copy-selection failed"
|
||||||
|
actual=$($TMUX show-buffer 2>/dev/null)
|
||||||
|
[ "$actual" = "$expected" ] ||
|
||||||
|
fail "$mode: expected next word '$expected', got '$actual'"
|
||||||
|
}
|
||||||
|
|
||||||
|
for mode in emacs vi; do
|
||||||
|
TMUX="$TEST_TMUX -Lunicode-whitespace-$mode-$$ -f/dev/null"
|
||||||
|
$TMUX kill-server 2>/dev/null
|
||||||
|
$TMUX new-session -d -x16 -y20 \
|
||||||
|
"printf 'aa bb\naa\tbb\naa\302\240bb\naa\343\200\200bb\naa:bb\naaaaaaaaaaaaaaa\302\240b\nabcdefghijklmnopq\nhardone\nhardtwo\naa\341\232\200bb\naa\342\200\200bb\naa\342\200\250bb\naa\342\200\251bb\naa\342\200\257bb\naa\342\201\237bb\nxa\302\205b c\n'; exec cat" ||
|
||||||
|
fail "$mode: new-session failed"
|
||||||
|
$TMUX set-option -g window-size manual || fail "$mode: set size failed"
|
||||||
|
$TMUX set-window-option -g mode-keys "$mode" ||
|
||||||
|
fail "$mode: set mode-keys failed"
|
||||||
|
$TMUX set-window-option -g word-separators "" ||
|
||||||
|
fail "$mode: clear word-separators failed"
|
||||||
|
$TMUX copy-mode || fail "$mode: copy-mode failed"
|
||||||
|
|
||||||
|
# ASCII space and TAB retain their existing behaviour.
|
||||||
|
goto_cell 0 0
|
||||||
|
$TMUX send-keys -X next-space || fail "$mode: ASCII next-space failed"
|
||||||
|
check_cursor 3,0
|
||||||
|
goto_cell 1 0
|
||||||
|
$TMUX send-keys -X next-space || fail "$mode: TAB next-space failed"
|
||||||
|
check_cursor 8,1
|
||||||
|
$TMUX send-keys -X previous-space ||
|
||||||
|
fail "$mode: TAB previous-space failed"
|
||||||
|
check_cursor 0,1
|
||||||
|
|
||||||
|
# NBSP is whitespace for selection and word movement.
|
||||||
|
goto_cell 2 0
|
||||||
|
$TMUX send-keys -X next-word || fail "$mode: NBSP next-word failed"
|
||||||
|
check_cursor 3,2
|
||||||
|
$TMUX send-keys -X previous-word ||
|
||||||
|
fail "$mode: NBSP previous-word failed"
|
||||||
|
check_cursor 0,2
|
||||||
|
check_word 2 0 aa
|
||||||
|
goto_cell 2 0
|
||||||
|
$TMUX send-keys -X next-word-end ||
|
||||||
|
fail "$mode: NBSP next-word-end failed"
|
||||||
|
if [ "$mode" = emacs ]; then
|
||||||
|
check_cursor 2,2
|
||||||
|
else
|
||||||
|
check_cursor 1,2
|
||||||
|
fi
|
||||||
|
|
||||||
|
# U+3000 is two columns wide; both its leading and padding cells must be
|
||||||
|
# skipped as one whitespace character in either direction.
|
||||||
|
goto_cell 3 0
|
||||||
|
$TMUX send-keys -X next-word || fail "$mode: U+3000 next-word failed"
|
||||||
|
check_cursor 4,3
|
||||||
|
$TMUX send-keys -X previous-word ||
|
||||||
|
fail "$mode: U+3000 previous-word failed"
|
||||||
|
check_cursor 0,3
|
||||||
|
goto_cell 3 0
|
||||||
|
$TMUX send-keys -X next-space-end ||
|
||||||
|
fail "$mode: U+3000 next-space-end failed"
|
||||||
|
if [ "$mode" = emacs ]; then
|
||||||
|
check_cursor 2,3
|
||||||
|
else
|
||||||
|
check_cursor 1,3
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Other characters in word-separators remain literal when space enables
|
||||||
|
# Unicode whitespace matching.
|
||||||
|
$TMUX set-window-option -g word-separators ': ' ||
|
||||||
|
fail "$mode: set custom word-separators failed"
|
||||||
|
goto_cell 4 0
|
||||||
|
$TMUX send-keys -X next-word || fail "$mode: literal next-word failed"
|
||||||
|
check_cursor 2,4
|
||||||
|
$TMUX set-window-option -g word-separators "" ||
|
||||||
|
fail "$mode: restore word-separators failed"
|
||||||
|
|
||||||
|
# Whitespace in the last column remains a boundary across a soft wrap.
|
||||||
|
goto_cell 5 0
|
||||||
|
$TMUX send-keys -X next-space ||
|
||||||
|
fail "$mode: wrapped NBSP next-space failed"
|
||||||
|
check_cursor 0,6
|
||||||
|
|
||||||
|
# A word split only by a soft wrap remains one word, while a hard line
|
||||||
|
# boundary still terminates selection.
|
||||||
|
check_word 7 0 abcdefghijklmnopq
|
||||||
|
check_word 9 0 hardone
|
||||||
|
|
||||||
|
# Check the remaining representative White_Space ranges and U+0085
|
||||||
|
# combined into a cell with another character.
|
||||||
|
check_next_word 11 bb
|
||||||
|
check_next_word 12 bb
|
||||||
|
check_next_word 13 bb
|
||||||
|
check_next_word 14 bb
|
||||||
|
check_next_word 15 bb
|
||||||
|
check_next_word 16 bb
|
||||||
|
goto_cell 17 0
|
||||||
|
$TMUX send-keys -X next-word ||
|
||||||
|
fail "$mode: combined U+0085 next-word failed"
|
||||||
|
check_cursor 2,17
|
||||||
|
|
||||||
|
$TMUX kill-server 2>/dev/null
|
||||||
|
done
|
||||||
|
|
||||||
|
exit 0
|
||||||
@@ -153,11 +153,13 @@ wait_format "$p0" '#{pane_in_mode}' '0'
|
|||||||
wait_option @picked "$p1"
|
wait_option @picked "$p1"
|
||||||
|
|
||||||
# Commands after display-panes run immediately while the mode remains.
|
# Commands after display-panes run immediately while the mode remains.
|
||||||
|
# Record the mode in the same command queue so client startup cannot race
|
||||||
|
# the 500ms timeout.
|
||||||
$TMUX set -g @after none || fail "set @after failed"
|
$TMUX set -g @after none || fail "set @after failed"
|
||||||
$TMUX display-panes -Nd 500 -t "$p0" \; set -g @after fast ||
|
$TMUX display-panes -Nd 500 -t "$p0" \; \
|
||||||
|
set -gF -t "$p0" @after '#{pane_mode}' ||
|
||||||
fail "display-panes immediate command failed"
|
fail "display-panes immediate command failed"
|
||||||
wait_option @after fast
|
wait_option @after 'panes-mode'
|
||||||
wait_format "$p0" '#{pane_mode}' 'panes-mode'
|
|
||||||
wait_format "$p0" '#{pane_in_mode}' '0'
|
wait_format "$p0" '#{pane_in_mode}' '0'
|
||||||
|
|
||||||
# Existing zoom is restored on exit.
|
# Existing zoom is restored on exit.
|
||||||
|
|||||||
@@ -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
|
||||||
@@ -199,5 +199,218 @@ must_fail $TMUX resize-pane -t "$id" -x 0
|
|||||||
must_fail $TMUX resize-pane -t "$id" -y 0
|
must_fail $TMUX resize-pane -t "$id" -y 0
|
||||||
$TMUX kill-pane -t "$id" || exit 1
|
$TMUX kill-pane -t "$id" || exit 1
|
||||||
|
|
||||||
|
# --- Tiled pane resize with floating cells in the layout ---
|
||||||
|
|
||||||
|
$TMUX set-option -w -u pane-border-lines || exit 1
|
||||||
|
base=$($TMUX display-message -p '#{pane_id}')
|
||||||
|
|
||||||
|
# A floating cell after the last tiled cell must not become the recipient of a
|
||||||
|
# relative or absolute resize. This is the sequence from GitHub issue 5135.
|
||||||
|
floating=$($TMUX new-pane -dPF '#{pane_id}' -t "$base" \
|
||||||
|
-x 50% -y 50% -X 50% -Y 50% 'sleep 100') ||
|
||||||
|
fail "new-pane for tiled resize test failed"
|
||||||
|
lower=$($TMUX split-window -dPF '#{pane_id}' -t "$base" 'sleep 100') ||
|
||||||
|
fail "split-window for tiled resize test failed"
|
||||||
|
|
||||||
|
$TMUX resize-pane -t "$lower" -U 5 || fail "relative tiled resize failed"
|
||||||
|
must_equal "$($TMUX display-message -p -t "$base" '#{pane_height}')" 7
|
||||||
|
must_equal "$($TMUX display-message -p -t "$lower" '#{pane_top}')" 8
|
||||||
|
must_equal "$($TMUX display-message -p -t "$lower" '#{pane_height}')" 16
|
||||||
|
must_equal "$($TMUX display-message -p -t "$floating" '#{pane_top}')" 13
|
||||||
|
must_equal "$($TMUX display-message -p -t "$floating" '#{pane_height}')" 10
|
||||||
|
|
||||||
|
$TMUX resize-pane -t "$lower" -y 11 || fail "absolute tiled reset failed"
|
||||||
|
$TMUX resize-pane -t "$lower" -y 16 || fail "absolute tiled resize failed"
|
||||||
|
must_equal "$($TMUX display-message -p -t "$base" '#{pane_height}')" 7
|
||||||
|
must_equal "$($TMUX display-message -p -t "$lower" '#{pane_top}')" 8
|
||||||
|
must_equal "$($TMUX display-message -p -t "$lower" '#{pane_height}')" 16
|
||||||
|
must_equal "$($TMUX display-message -p -t "$floating" '#{pane_height}')" 10
|
||||||
|
|
||||||
|
$TMUX kill-pane -t "$floating" || exit 1
|
||||||
|
$TMUX kill-pane -t "$lower" || exit 1
|
||||||
|
|
||||||
|
# A floating cell between tiled siblings must be skipped when finding the cell
|
||||||
|
# which donates space to a resize.
|
||||||
|
lower=$($TMUX split-window -dPF '#{pane_id}' -t "$base" 'sleep 100') ||
|
||||||
|
fail "split-window for middle floating cell test failed"
|
||||||
|
floating=$($TMUX new-pane -dPF '#{pane_id}' -t "$base" \
|
||||||
|
-x 20 -y 8 -X 30 -Y 8 'sleep 100') ||
|
||||||
|
fail "new-pane for middle floating cell test failed"
|
||||||
|
|
||||||
|
$TMUX resize-pane -t "$base" -D 3 || fail "resize past floating cell failed"
|
||||||
|
must_equal "$($TMUX display-message -p -t "$base" '#{pane_height}')" 15
|
||||||
|
must_equal "$($TMUX display-message -p -t "$lower" '#{pane_top}')" 16
|
||||||
|
must_equal "$($TMUX display-message -p -t "$lower" '#{pane_height}')" 8
|
||||||
|
must_equal "$($TMUX display-message -p -t "$floating" '#{pane_height}')" 6
|
||||||
|
|
||||||
|
$TMUX kill-pane -t "$floating" || exit 1
|
||||||
|
$TMUX kill-pane -t "$lower" || exit 1
|
||||||
|
|
||||||
|
# A small floating child must not limit the available tiled space in a nested
|
||||||
|
# layout with a different split direction.
|
||||||
|
lower=$($TMUX split-window -dPF '#{pane_id}' -t "$base" 'sleep 100') ||
|
||||||
|
fail "split-window for nested floating cell test failed"
|
||||||
|
right=$($TMUX split-window -dhPF '#{pane_id}' -t "$lower" 'sleep 100') ||
|
||||||
|
fail "horizontal split for nested floating cell test failed"
|
||||||
|
floating=$($TMUX new-pane -dPF '#{pane_id}' -t "$lower" \
|
||||||
|
-x 20 -y 3 -X 30 -Y 10 'sleep 100') ||
|
||||||
|
fail "new-pane for nested floating cell test failed"
|
||||||
|
|
||||||
|
$TMUX resize-pane -t "$base" -D 3 || fail "nested tiled resize failed"
|
||||||
|
must_equal "$($TMUX display-message -p -t "$base" '#{pane_height}')" 15
|
||||||
|
must_equal "$($TMUX display-message -p -t "$lower" '#{pane_top}')" 16
|
||||||
|
must_equal "$($TMUX display-message -p -t "$lower" '#{pane_height}')" 8
|
||||||
|
must_equal "$($TMUX display-message -p -t "$right" '#{pane_top}')" 16
|
||||||
|
must_equal "$($TMUX display-message -p -t "$right" '#{pane_height}')" 8
|
||||||
|
must_equal "$($TMUX display-message -p -t "$floating" '#{pane_height}')" 1
|
||||||
|
|
||||||
|
$TMUX kill-pane -t "$floating" || exit 1
|
||||||
|
$TMUX kill-pane -t "$right" || exit 1
|
||||||
|
$TMUX kill-pane -t "$lower" || exit 1
|
||||||
|
|
||||||
|
# --- Floating panes clamped when the window shrinks (issue #5581, PR #5582) ---
|
||||||
|
#
|
||||||
|
# layout_resize clamps floating panes back inside the window when it shrinks:
|
||||||
|
# move them and, only if they cannot fit, shrink them (never below
|
||||||
|
# PANE_MINIMUM). A floating cell is the pane's content, so with the default
|
||||||
|
# single-line border the clamp counts 1 cell of border per side, exactly as
|
||||||
|
# layout_floating_args_parse does on creation; with no border it counts 0.
|
||||||
|
# Each case below gets its own window, since resize-window fixes a window at
|
||||||
|
# a manual size for the rest of its life.
|
||||||
|
|
||||||
|
# Case 1: a lone floating pane -- break-pane -W on a window's only pane --
|
||||||
|
# takes the early-return path in layout_resize, added during PR #5582's
|
||||||
|
# review round, since there is no tiled tree to walk.
|
||||||
|
win=$($TMUX new-window -dPF '#{window_id}') ||
|
||||||
|
fail "new-window for lone float failed"
|
||||||
|
|
||||||
|
# -x 20 -y 6 -> pane 18x4; -X 60 -Y 18 -> pane_left 61, pane_top 19: with the
|
||||||
|
# border, the footprint is columns 60-79, rows 18-23, flush with the 80x24
|
||||||
|
# window's right and bottom edges, so the float fits before it is shrunk.
|
||||||
|
$TMUX break-pane -W -s "$win" -x 20 -y 6 -X 60 -Y 18 ||
|
||||||
|
fail "break-pane -W for lone float failed"
|
||||||
|
must_equal "$($TMUX display-message -p -t "$win" '#{pane_width}')" 18
|
||||||
|
must_equal "$($TMUX display-message -p -t "$win" '#{pane_height}')" 4
|
||||||
|
must_equal "$($TMUX display-message -p -t "$win" '#{pane_left}')" 61
|
||||||
|
must_equal "$($TMUX display-message -p -t "$win" '#{pane_top}')" 19
|
||||||
|
|
||||||
|
# Shrink to 40x16. The float still fits at its own size (18 <= 40-2, 4 <=
|
||||||
|
# 16-2) so only its position moves, flush to the new right/bottom edges:
|
||||||
|
# xoff = 40 - 18 - 1 = 21; yoff = 16 - 4 - 1 = 11.
|
||||||
|
$TMUX resize-window -t "$win" -x 40 -y 16 ||
|
||||||
|
fail "resize-window (lone float) failed"
|
||||||
|
must_equal "$($TMUX display-message -p -t "$win" '#{pane_width}')" 18
|
||||||
|
must_equal "$($TMUX display-message -p -t "$win" '#{pane_height}')" 4
|
||||||
|
must_equal "$($TMUX display-message -p -t "$win" '#{pane_left}')" 21
|
||||||
|
must_equal "$($TMUX display-message -p -t "$win" '#{pane_top}')" 11
|
||||||
|
|
||||||
|
# Case 5: grow the window back. The clamp must not chase the window back
|
||||||
|
# outward -- it only ever pulls a float in, never restores where it was.
|
||||||
|
$TMUX resize-window -t "$win" -x 80 -y 24 ||
|
||||||
|
fail "resize-window grow (lone float) failed"
|
||||||
|
must_equal "$($TMUX display-message -p -t "$win" '#{pane_width}')" 18
|
||||||
|
must_equal "$($TMUX display-message -p -t "$win" '#{pane_height}')" 4
|
||||||
|
must_equal "$($TMUX display-message -p -t "$win" '#{pane_left}')" 21
|
||||||
|
must_equal "$($TMUX display-message -p -t "$win" '#{pane_top}')" 11
|
||||||
|
$TMUX kill-window -t "$win" || exit 1
|
||||||
|
|
||||||
|
# Case 2: the same float, but with a tiled sibling surviving alongside it, so
|
||||||
|
# the window's root cell stays tiled and layout_resize takes the normal path
|
||||||
|
# (the clamp call after layout_fix_offsets) instead of the early return
|
||||||
|
# above. Same geometry as case 1, so the same numbers should come out.
|
||||||
|
win=$($TMUX new-window -dPF '#{window_id}') ||
|
||||||
|
fail "new-window for tiled sibling failed"
|
||||||
|
$TMUX split-window -t "$win" 'sleep 100' ||
|
||||||
|
fail "split-window for tiled sibling failed"
|
||||||
|
$TMUX break-pane -W -s "$win" -x 20 -y 6 -X 60 -Y 18 ||
|
||||||
|
fail "break-pane -W for tiled sibling failed"
|
||||||
|
must_equal "$($TMUX display-message -p -t "$win" '#{pane_width}')" 18
|
||||||
|
must_equal "$($TMUX display-message -p -t "$win" '#{pane_height}')" 4
|
||||||
|
must_equal "$($TMUX display-message -p -t "$win" '#{pane_left}')" 61
|
||||||
|
must_equal "$($TMUX display-message -p -t "$win" '#{pane_top}')" 19
|
||||||
|
|
||||||
|
$TMUX resize-window -t "$win" -x 40 -y 16 ||
|
||||||
|
fail "resize-window (tiled sibling) failed"
|
||||||
|
must_equal "$($TMUX display-message -p -t "$win" '#{pane_width}')" 18
|
||||||
|
must_equal "$($TMUX display-message -p -t "$win" '#{pane_height}')" 4
|
||||||
|
must_equal "$($TMUX display-message -p -t "$win" '#{pane_left}')" 21
|
||||||
|
must_equal "$($TMUX display-message -p -t "$win" '#{pane_top}')" 11
|
||||||
|
$TMUX kill-window -t "$win" || exit 1
|
||||||
|
|
||||||
|
# Case 3: new-pane float, the original repro from issue #5581 and the PR
|
||||||
|
# body -- also the normal path, via the tiled base pane new-window creates.
|
||||||
|
win=$($TMUX new-window -dPF '#{window_id}') ||
|
||||||
|
fail "new-window for new-pane repro failed"
|
||||||
|
$TMUX resize-window -t "$win" -x 120 -y 40 ||
|
||||||
|
fail "resize-window to 120x40 failed"
|
||||||
|
|
||||||
|
# -x 30 -y 10 -> pane 28x8; -X 85 -Y 25 -> pane_left 86, pane_top 26.
|
||||||
|
floating=$($TMUX new-pane -t "$win" -dPF '#{pane_id}' \
|
||||||
|
-x 30 -y 10 -X 85 -Y 25 'sleep 100') ||
|
||||||
|
fail "new-pane for new-pane repro failed"
|
||||||
|
must_equal "$($TMUX display-message -p -t "$floating" '#{pane_width}')" 28
|
||||||
|
must_equal "$($TMUX display-message -p -t "$floating" '#{pane_height}')" 8
|
||||||
|
must_equal "$($TMUX display-message -p -t "$floating" '#{pane_left}')" 86
|
||||||
|
must_equal "$($TMUX display-message -p -t "$floating" '#{pane_top}')" 26
|
||||||
|
|
||||||
|
# Shrink to 60x20: xoff = 60 - 28 - 1 = 31; yoff = 20 - 8 - 1 = 11.
|
||||||
|
$TMUX resize-window -t "$win" -x 60 -y 20 ||
|
||||||
|
fail "resize-window (new-pane repro) failed"
|
||||||
|
must_equal "$($TMUX display-message -p -t "$floating" '#{pane_width}')" 28
|
||||||
|
must_equal "$($TMUX display-message -p -t "$floating" '#{pane_height}')" 8
|
||||||
|
must_equal "$($TMUX display-message -p -t "$floating" '#{pane_left}')" 31
|
||||||
|
must_equal "$($TMUX display-message -p -t "$floating" '#{pane_top}')" 11
|
||||||
|
$TMUX kill-window -t "$win" || exit 1
|
||||||
|
|
||||||
|
# Case 4: a float that already fits inside the shrunk window is left alone --
|
||||||
|
# assert position and size are both unchanged, not just one of them.
|
||||||
|
win=$($TMUX new-window -dPF '#{window_id}') ||
|
||||||
|
fail "new-window for untouched float failed"
|
||||||
|
|
||||||
|
# -x 20 -y 6 -> pane 18x4; -X 8 -Y 3 -> pane_left 9, pane_top 4 (as at the top
|
||||||
|
# of this file). Footprint columns 8-27, rows 3-8: well inside 60x20 too.
|
||||||
|
floating=$($TMUX new-pane -t "$win" -dPF '#{pane_id}' \
|
||||||
|
-x 20 -y 6 -X 8 -Y 3 'sleep 100') ||
|
||||||
|
fail "new-pane for untouched float failed"
|
||||||
|
must_equal "$($TMUX display-message -p -t "$floating" '#{pane_width}')" 18
|
||||||
|
must_equal "$($TMUX display-message -p -t "$floating" '#{pane_height}')" 4
|
||||||
|
must_equal "$($TMUX display-message -p -t "$floating" '#{pane_left}')" 9
|
||||||
|
must_equal "$($TMUX display-message -p -t "$floating" '#{pane_top}')" 4
|
||||||
|
|
||||||
|
$TMUX resize-window -t "$win" -x 60 -y 20 ||
|
||||||
|
fail "resize-window (untouched float) failed"
|
||||||
|
must_equal "$($TMUX display-message -p -t "$floating" '#{pane_width}')" 18
|
||||||
|
must_equal "$($TMUX display-message -p -t "$floating" '#{pane_height}')" 4
|
||||||
|
must_equal "$($TMUX display-message -p -t "$floating" '#{pane_left}')" 9
|
||||||
|
must_equal "$($TMUX display-message -p -t "$floating" '#{pane_top}')" 4
|
||||||
|
$TMUX kill-window -t "$win" || exit 1
|
||||||
|
|
||||||
|
# Case 6: pad = 0, via the pane-border-lines window option set to none. The
|
||||||
|
# border arithmetic differs here (no -2/+1 adjustment either on creation or
|
||||||
|
# in the clamp).
|
||||||
|
win=$($TMUX new-window -dPF '#{window_id}') ||
|
||||||
|
fail "new-window for pad=0 float failed"
|
||||||
|
$TMUX set-option -w -t "$win" pane-border-lines none ||
|
||||||
|
fail "set pane-border-lines none failed"
|
||||||
|
|
||||||
|
# No border: -x 20 -y 6 -> pane 20x6 directly; -X 60 -Y 18 -> pane_left 60,
|
||||||
|
# pane_top 18 directly. Footprint (== content, no border) is columns 60-79,
|
||||||
|
# rows 18-23: flush right/bottom of the 80x24 window, same as case 1.
|
||||||
|
$TMUX break-pane -W -s "$win" -x 20 -y 6 -X 60 -Y 18 ||
|
||||||
|
fail "break-pane -W for pad=0 float failed"
|
||||||
|
must_equal "$($TMUX display-message -p -t "$win" '#{pane_width}')" 20
|
||||||
|
must_equal "$($TMUX display-message -p -t "$win" '#{pane_height}')" 6
|
||||||
|
must_equal "$($TMUX display-message -p -t "$win" '#{pane_left}')" 60
|
||||||
|
must_equal "$($TMUX display-message -p -t "$win" '#{pane_top}')" 18
|
||||||
|
|
||||||
|
# Shrink to 40x16 with pad=0: xoff = 40 - 20 - 0 = 20; yoff = 16 - 6 - 0 = 10.
|
||||||
|
$TMUX resize-window -t "$win" -x 40 -y 16 ||
|
||||||
|
fail "resize-window (pad=0 float) failed"
|
||||||
|
must_equal "$($TMUX display-message -p -t "$win" '#{pane_width}')" 20
|
||||||
|
must_equal "$($TMUX display-message -p -t "$win" '#{pane_height}')" 6
|
||||||
|
must_equal "$($TMUX display-message -p -t "$win" '#{pane_left}')" 20
|
||||||
|
must_equal "$($TMUX display-message -p -t "$win" '#{pane_top}')" 10
|
||||||
|
$TMUX kill-window -t "$win" || exit 1
|
||||||
|
|
||||||
$TMUX kill-server 2>/dev/null
|
$TMUX kill-server 2>/dev/null
|
||||||
exit 0
|
exit 0
|
||||||
|
|||||||
@@ -0,0 +1,158 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
# Moving, resizing and reordering an over-zoom float must leave the tiled
|
||||||
|
# pane zoomed, and the changes must survive an explicit unzoom.
|
||||||
|
|
||||||
|
PATH=/bin:/usr/bin
|
||||||
|
TERM=screen
|
||||||
|
|
||||||
|
[ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux)
|
||||||
|
TMUX="$TEST_TMUX -LtestA$$ -f/dev/null"
|
||||||
|
trap '$TMUX kill-server 2>/dev/null' 0
|
||||||
|
trap 'exit 1' 1 2 15
|
||||||
|
|
||||||
|
fail()
|
||||||
|
{
|
||||||
|
echo "$*" >&2
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
run()
|
||||||
|
{
|
||||||
|
$TMUX "$@" || fail "failed: $*"
|
||||||
|
}
|
||||||
|
|
||||||
|
check()
|
||||||
|
{
|
||||||
|
got=$(run display-message -p -t "$1" "$2") || exit 1
|
||||||
|
[ "$got" = "$3" ] || fail "$1 $2: got '$got', expected '$3'"
|
||||||
|
}
|
||||||
|
|
||||||
|
check_zoom()
|
||||||
|
{
|
||||||
|
check "$base" '#{window_zoomed_flag}:#{pane_zoomed_flag}' '1:1'
|
||||||
|
check "$float" '#{pane_floating_flag}:#{pane_active}' "1:$active"
|
||||||
|
check "$base" '#{@unzoomed}' ''
|
||||||
|
}
|
||||||
|
|
||||||
|
run new-session -d -x 80 -y 24
|
||||||
|
base=$(run display-message -p '#{pane_id}') || exit 1
|
||||||
|
other=$(run split-window -dPF '#{pane_id}') || exit 1
|
||||||
|
layout=$(run display-message -p '#{window_layout}') || exit 1
|
||||||
|
run set-hook -g window-unzoomed 'set -g @unzoomed 1'
|
||||||
|
|
||||||
|
# Cover creation before and after zoom, with both the float and the tiled
|
||||||
|
# pane active. Modal panes use the same over-zoom flag.
|
||||||
|
for mode in before after detached modal; do
|
||||||
|
if [ "$mode" != before ]; then
|
||||||
|
run resize-pane -Z -t "$base"
|
||||||
|
fi
|
||||||
|
case "$mode" in
|
||||||
|
detached) flags=-Ad; active=0 ;;
|
||||||
|
modal) flags=-O; active=1 ;;
|
||||||
|
*) flags=-A; active=1 ;;
|
||||||
|
esac
|
||||||
|
float=$(run new-pane "$flags" -PF '#{pane_id}' -t "$base" \
|
||||||
|
-x 20 -y 8 -X 8 -Y 3 '') || exit 1
|
||||||
|
if [ "$mode" = before ]; then
|
||||||
|
run resize-pane -Z -t "$base"
|
||||||
|
fi
|
||||||
|
run set -gu @unzoomed
|
||||||
|
check_zoom
|
||||||
|
|
||||||
|
run move-pane -t "$float" -D
|
||||||
|
check "$float" '#{pane_top}' 5
|
||||||
|
check_zoom
|
||||||
|
run move-pane -t "$float" -U 2 -R 3 -L 1
|
||||||
|
check "$float" '#{pane_left}:#{pane_top}' '11:3'
|
||||||
|
check_zoom
|
||||||
|
run move-pane -t "$float" -X 25% -Y 25%
|
||||||
|
check "$float" '#{pane_left}:#{pane_top}' '21:7'
|
||||||
|
check_zoom
|
||||||
|
run move-pane -t "$float" -P bottom-right
|
||||||
|
check "$float" '#{pane_left}:#{pane_top}' '61:17'
|
||||||
|
check_zoom
|
||||||
|
run resize-pane -t "$float" -x 30 -y 10
|
||||||
|
check "$float" '#{pane_width}:#{pane_height}' '28:8'
|
||||||
|
check_zoom
|
||||||
|
run resize-pane -t "$float" -U 1 -L 2
|
||||||
|
check "$float" '#{pane_left}:#{pane_top}:#{pane_width}:#{pane_height}' \
|
||||||
|
'59:16:30:9'
|
||||||
|
check_zoom
|
||||||
|
run move-pane -t "$float" -P centre
|
||||||
|
check "$float" '#{pane_left}:#{pane_top}' '25:7'
|
||||||
|
check_zoom
|
||||||
|
|
||||||
|
# Failed commands must not change zoom either.
|
||||||
|
for command in 'move-pane -D invalid' 'move-pane -P invalid' \
|
||||||
|
'move-pane -z invalid' 'resize-pane -x invalid' \
|
||||||
|
'resize-pane -D invalid'; do
|
||||||
|
$TMUX $command -t "$float" 2>/dev/null &&
|
||||||
|
fail "unexpected success: $command"
|
||||||
|
check_zoom
|
||||||
|
done
|
||||||
|
|
||||||
|
run resize-pane -Z -t "$base"
|
||||||
|
check "$float" '#{pane_left}:#{pane_top}:#{pane_width}:#{pane_height}' \
|
||||||
|
'25:7:30:9'
|
||||||
|
run kill-pane -t "$float"
|
||||||
|
check "$base" '#{window_layout}' "$layout"
|
||||||
|
done
|
||||||
|
|
||||||
|
# Put an ordinary (hidden while zoomed) float between two over-zoom floats
|
||||||
|
# in the stacking order. Reordering must skip it when counting visible
|
||||||
|
# positions, but keep all floats ahead of the tiled panes after unzoom.
|
||||||
|
back=$(run new-pane -AdPF '#{pane_id}' -t "$base" '') || exit 1
|
||||||
|
hidden=$(run new-pane -dPF '#{pane_id}' -t "$base" '') || exit 1
|
||||||
|
float=$(run new-pane -AdPF '#{pane_id}' -t "$base" '') || exit 1
|
||||||
|
active=0
|
||||||
|
run resize-pane -Z -t "$base"
|
||||||
|
run set -gu @unzoomed
|
||||||
|
|
||||||
|
for position in backward back forward-loop; do
|
||||||
|
run move-pane -t "$float" -P front
|
||||||
|
run move-pane -t "$float" -P "$position"
|
||||||
|
check "$float" '#{pane_z}' 1
|
||||||
|
check "$back" '#{pane_z}' 0
|
||||||
|
check_zoom
|
||||||
|
done
|
||||||
|
for position in forward front backward-loop; do
|
||||||
|
run move-pane -t "$float" -P back
|
||||||
|
run move-pane -t "$float" -P "$position"
|
||||||
|
check "$float" '#{pane_z}' 0
|
||||||
|
check "$back" '#{pane_z}' 1
|
||||||
|
check_zoom
|
||||||
|
done
|
||||||
|
for z in 1 0 99; do
|
||||||
|
run move-pane -t "$float" -z "$z"
|
||||||
|
want=$z
|
||||||
|
[ "$z" = 99 ] && want=1
|
||||||
|
check "$float" '#{pane_z}' "$want"
|
||||||
|
check_zoom
|
||||||
|
done
|
||||||
|
run resize-pane -Z -t "$base"
|
||||||
|
check "$float" '#{pane_z}' 2
|
||||||
|
check "$base" '#{pane_z}' 4
|
||||||
|
run kill-pane -t "$hidden"
|
||||||
|
run kill-pane -t "$back"
|
||||||
|
|
||||||
|
# Explicit zoom toggling and operations on tiled panes retain their
|
||||||
|
# existing behaviour. The over-zoom flag alone does not make a pane a float:
|
||||||
|
# zooming the float itself gives it a tiled cell until unzoom.
|
||||||
|
run resize-pane -Z -t "$float"
|
||||||
|
check "$float" '#{pane_zoomed_flag}:#{pane_floating_flag}' '1:0'
|
||||||
|
run resize-pane -t "$float" -x 25
|
||||||
|
check "$float" '#{window_zoomed_flag}:#{pane_floating_flag}:#{pane_width}' \
|
||||||
|
'0:1:23'
|
||||||
|
run resize-pane -Z -t "$base"
|
||||||
|
run resize-pane -t "$other" -D 1
|
||||||
|
check "$base" '#{window_zoomed_flag}' 0
|
||||||
|
|
||||||
|
# A hidden ordinary float must still be unzoomed before resizing it.
|
||||||
|
hidden=$(run new-pane -dPF '#{pane_id}' -t "$base" '') || exit 1
|
||||||
|
run resize-pane -Z -t "$base"
|
||||||
|
run resize-pane -t "$hidden" -x 25
|
||||||
|
check "$hidden" '#{window_zoomed_flag}:#{pane_floating_flag}:#{pane_width}' \
|
||||||
|
'0:1:23'
|
||||||
|
|
||||||
|
exit 0
|
||||||
@@ -7,6 +7,8 @@ TERM=screen
|
|||||||
|
|
||||||
[ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux)
|
[ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux)
|
||||||
TMUX="$TEST_TMUX -LtestA$$ -f/dev/null"
|
TMUX="$TEST_TMUX -LtestA$$ -f/dev/null"
|
||||||
|
trap '$TMUX kill-server 2>/dev/null' 0
|
||||||
|
trap 'exit 1' 1 2 15
|
||||||
|
|
||||||
# test_format $format $expected_result
|
# test_format $format $expected_result
|
||||||
test_format()
|
test_format()
|
||||||
|
|||||||
@@ -18,6 +18,24 @@ export TZ LANG LC_ALL
|
|||||||
|
|
||||||
[ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux)
|
[ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux)
|
||||||
TMUX="$TEST_TMUX -LtestA$$ -f/dev/null"
|
TMUX="$TEST_TMUX -LtestA$$ -f/dev/null"
|
||||||
|
FIFO1="${TMPDIR:-/tmp}/fmt-l-$$-1"
|
||||||
|
FIFO2="${TMPDIR:-/tmp}/fmt-l-$$-2"
|
||||||
|
HOLD1=
|
||||||
|
HOLD2=
|
||||||
|
CC1=
|
||||||
|
CC2=
|
||||||
|
|
||||||
|
cleanup()
|
||||||
|
{
|
||||||
|
for pid in $HOLD1 $HOLD2 $CC1 $CC2; do
|
||||||
|
kill "$pid" 2>/dev/null
|
||||||
|
wait "$pid" 2>/dev/null
|
||||||
|
done
|
||||||
|
$TMUX kill-server 2>/dev/null
|
||||||
|
rm -f "$FIFO1" "$FIFO2"
|
||||||
|
}
|
||||||
|
trap cleanup 0
|
||||||
|
trap 'exit 1' 1 2 15
|
||||||
|
|
||||||
ESC=$(printf '\033')
|
ESC=$(printf '\033')
|
||||||
|
|
||||||
@@ -579,8 +597,6 @@ assert_alive "verbose loop expansion"
|
|||||||
|
|
||||||
# L loops over attached clients. Attach two control-mode clients, each held
|
# L loops over attached clients. Attach two control-mode clients, each held
|
||||||
# open by a background process keeping a FIFO's write end open.
|
# open by a background process keeping a FIFO's write end open.
|
||||||
FIFO1="${TMPDIR:-/tmp}/fmt-l-$$-1"
|
|
||||||
FIFO2="${TMPDIR:-/tmp}/fmt-l-$$-2"
|
|
||||||
rm -f "$FIFO1" "$FIFO2"
|
rm -f "$FIFO1" "$FIFO2"
|
||||||
mkfifo "$FIFO1" "$FIFO2" || exit 1
|
mkfifo "$FIFO1" "$FIFO2" || exit 1
|
||||||
# Hold the write ends open so the control clients stay attached.
|
# Hold the write ends open so the control clients stay attached.
|
||||||
@@ -605,9 +621,9 @@ test_format "#{L/nr:x}" "xx"
|
|||||||
test_format "#{L/r:x}" "xx"
|
test_format "#{L/r:x}" "xx"
|
||||||
# Now detach one and confirm the count drops to one.
|
# Now detach one and confirm the count drops to one.
|
||||||
kill $HOLD2 2>/dev/null
|
kill $HOLD2 2>/dev/null
|
||||||
|
wait $HOLD2 2>/dev/null
|
||||||
|
HOLD2=
|
||||||
sleep 1
|
sleep 1
|
||||||
test_format "#{L:x}" "x"
|
test_format "#{L:x}" "x"
|
||||||
kill $HOLD1 $CC1 $CC2 2>/dev/null
|
|
||||||
rm -f "$FIFO1" "$FIFO2"
|
|
||||||
|
|
||||||
exit 0
|
exit 0
|
||||||
|
|||||||
@@ -7,6 +7,8 @@ TERM=screen
|
|||||||
|
|
||||||
[ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux)
|
[ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux)
|
||||||
TMUX="$TEST_TMUX -LtestA$$ -f/dev/null"
|
TMUX="$TEST_TMUX -LtestA$$ -f/dev/null"
|
||||||
|
trap '$TMUX kill-server 2>/dev/null' 0
|
||||||
|
trap 'exit 1' 1 2 15
|
||||||
|
|
||||||
# test_format $format $expected_result
|
# test_format $format $expected_result
|
||||||
test_format()
|
test_format()
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -76,14 +76,14 @@ $TMUX set-hook -g session-closed \
|
|||||||
fail "set-hook session-closed failed"
|
fail "set-hook session-closed failed"
|
||||||
|
|
||||||
# The only pane of the only window of a session exits: pane-exited, then
|
# The only pane of the only window of a session exits: pane-exited, then
|
||||||
# window-unlinked, then session-closed, each seeing the dead object in the
|
# session-closed, then window-unlinked, each seeing the dead object in the
|
||||||
# hook formats.
|
# hook formats.
|
||||||
pane=$($TMUX new -d -s doomed -n dwin -P -F '#{pane_id}' 'true') ||
|
pane=$($TMUX new -d -s doomed -n dwin -P -F '#{pane_id}' 'true') ||
|
||||||
fail "new-session doomed failed"
|
fail "new-session doomed failed"
|
||||||
wait_for @log \
|
wait_for @log \
|
||||||
"|pane-exited:$pane|window-unlinked:doomed:dwin|session-closed:doomed"
|
"|pane-exited:$pane|session-closed:doomed|window-unlinked:doomed:dwin"
|
||||||
assert_unchanged @log \
|
assert_unchanged @log \
|
||||||
"|pane-exited:$pane|window-unlinked:doomed:dwin|session-closed:doomed"
|
"|pane-exited:$pane|session-closed:doomed|window-unlinked:doomed:dwin"
|
||||||
|
|
||||||
# The dead pane, window and session cannot be used as targets but the
|
# The dead pane, window and session cannot be used as targets but the
|
||||||
# server survives.
|
# server survives.
|
||||||
@@ -110,14 +110,14 @@ $TMUX set-hook -g pane-exited \
|
|||||||
'set -gF @log "#{@log}|pane-exited:#{hook_pane}"' ||
|
'set -gF @log "#{@log}|pane-exited:#{hook_pane}"' ||
|
||||||
fail "restore pane-exited hook failed"
|
fail "restore pane-exited hook failed"
|
||||||
|
|
||||||
# kill-window on the last window: window-unlinked then session-closed but
|
# kill-window on the last window: session-closed then window-unlinked but
|
||||||
# no pane-exited for the panes in the killed window.
|
# no pane-exited for the panes in the killed window.
|
||||||
$TMUX set -g @log '' || fail "reset @log failed"
|
$TMUX set -g @log '' || fail "reset @log failed"
|
||||||
$TMUX new -d -s doomed2 -n dwin2 || fail "new-session doomed2 failed"
|
$TMUX new -d -s doomed2 -n dwin2 || fail "new-session doomed2 failed"
|
||||||
$TMUX splitw -d -t doomed2:0 || fail "split-window doomed2 failed"
|
$TMUX splitw -d -t doomed2:0 || fail "split-window doomed2 failed"
|
||||||
$TMUX kill-window -t doomed2:0 || fail "kill-window failed"
|
$TMUX kill-window -t doomed2:0 || fail "kill-window failed"
|
||||||
wait_for @log '|window-unlinked:doomed2:dwin2|session-closed:doomed2'
|
wait_for @log '|session-closed:doomed2|window-unlinked:doomed2:dwin2'
|
||||||
assert_unchanged @log '|window-unlinked:doomed2:dwin2|session-closed:doomed2'
|
assert_unchanged @log '|session-closed:doomed2|window-unlinked:doomed2:dwin2'
|
||||||
$TMUX has -t main || fail "server died after kill-window chain"
|
$TMUX has -t main || fail "server died after kill-window chain"
|
||||||
|
|
||||||
# kill-session: session-closed fires first, then window-unlinked for its
|
# kill-session: session-closed fires first, then window-unlinked for its
|
||||||
|
|||||||
@@ -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.
|
||||||
|
|||||||
@@ -12,7 +12,8 @@ $TMUX kill-server 2>/dev/null
|
|||||||
|
|
||||||
TMP=$(mktemp)
|
TMP=$(mktemp)
|
||||||
OUT=$(mktemp)
|
OUT=$(mktemp)
|
||||||
trap "rm -f $TMP $OUT" 0 1 15
|
trap 'rm -f "$TMP" "$OUT"; $TMUX kill-server 2>/dev/null' 0
|
||||||
|
trap 'exit 1' 1 2 15
|
||||||
|
|
||||||
cat <<EOF >$TMP
|
cat <<EOF >$TMP
|
||||||
if 'true' 'wibble wobble'
|
if 'true' 'wibble wobble'
|
||||||
|
|||||||
@@ -4,6 +4,9 @@ TERM=screen
|
|||||||
[ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux)
|
[ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux)
|
||||||
TMUX="$TEST_TMUX -LtestA$$ -f/dev/null"
|
TMUX="$TEST_TMUX -LtestA$$ -f/dev/null"
|
||||||
|
|
||||||
|
# Keep panes alive until the next case or cleanup, without reading replies.
|
||||||
|
INPUT_HOLD='while :; do sleep 3600; done'
|
||||||
|
|
||||||
TMP=$(mktemp)
|
TMP=$(mktemp)
|
||||||
EXP=$(mktemp)
|
EXP=$(mktemp)
|
||||||
trap 'rm -f "$TMP" "$EXP"; $TMUX kill-server 2>/dev/null' 0 1 15
|
trap 'rm -f "$TMP" "$EXP"; $TMUX kill-server 2>/dev/null' 0 1 15
|
||||||
@@ -37,10 +40,10 @@ start_pane_hlimit()
|
|||||||
|
|
||||||
$TMUX kill-server 2>/dev/null
|
$TMUX kill-server 2>/dev/null
|
||||||
sleep 0.1
|
sleep 0.1
|
||||||
$TMUX new-session -d -x 1 -y 1 -s test-setup "sleep 2" || exit 1
|
$TMUX new-session -d -x 1 -y 1 -s test-setup "$INPUT_HOLD" || exit 1
|
||||||
$TMUX set-option -g history-limit "$hlimit" || exit 1
|
$TMUX set-option -g history-limit "$hlimit" || exit 1
|
||||||
$TMUX new-session -d -x "$sx" -y "$sy" -s "$name" \
|
$TMUX new-session -d -x "$sx" -y "$sy" -s "$name" \
|
||||||
"printf '$seq'; sleep 2" || exit 1
|
"printf '$seq'; $INPUT_HOLD" || exit 1
|
||||||
$TMUX kill-session -t test-setup
|
$TMUX kill-session -t test-setup
|
||||||
sleep 0.3
|
sleep 0.3
|
||||||
}
|
}
|
||||||
|
|||||||
+13
-1
@@ -22,7 +22,19 @@ assert_key () {
|
|||||||
clear-history -t$W \; \
|
clear-history -t$W \; \
|
||||||
send-keys -t$W "$key" 'EOL' || exit 1
|
send-keys -t$W "$key" 'EOL' || exit 1
|
||||||
|
|
||||||
actual_code=$($TMUX capturep -pt$W | \
|
# cat echoes the keys back asynchronously, so wait for the EOL marker
|
||||||
|
# to reach the pane instead of capturing straight away.
|
||||||
|
i=0
|
||||||
|
while [ $i -lt 50 ]; do
|
||||||
|
screen=$($TMUX capturep -pt$W)
|
||||||
|
case "$screen" in
|
||||||
|
*EOL*) break ;;
|
||||||
|
esac
|
||||||
|
i=$((i + 1))
|
||||||
|
sleep 0.1
|
||||||
|
done
|
||||||
|
|
||||||
|
actual_code=$(printf '%s\n' "$screen" | \
|
||||||
head -1 | \
|
head -1 | \
|
||||||
sed -e 's/EOL.*$//')
|
sed -e 's/EOL.*$//')
|
||||||
|
|
||||||
|
|||||||
@@ -2,6 +2,38 @@
|
|||||||
|
|
||||||
. ./input-common.inc
|
. ./input-common.inc
|
||||||
|
|
||||||
|
# Wait for the terminator and following text to reach the parser.
|
||||||
|
check_discard()
|
||||||
|
{
|
||||||
|
name=$1
|
||||||
|
printf 'OK\n' >"$EXP"
|
||||||
|
i=0
|
||||||
|
while [ "$i" -lt 300 ]; do
|
||||||
|
capture_grid "$name" >"$TMP"
|
||||||
|
cmp -s "$TMP" "$EXP" && return 0
|
||||||
|
sleep 0.05
|
||||||
|
i=$((i + 1))
|
||||||
|
done
|
||||||
|
fail "$name (timed out waiting for discard)"
|
||||||
|
}
|
||||||
|
|
||||||
|
# The missing-terminator timer may expire before the string limit is reached
|
||||||
|
# on a slow build. Clear any payload printed after the timeout, then check
|
||||||
|
# that the string did not change the title and normal output has resumed.
|
||||||
|
test_discard()
|
||||||
|
{
|
||||||
|
name=$1
|
||||||
|
prefix=$2
|
||||||
|
start_cmd "$name" 8 3 "$INPUT_HOLD"
|
||||||
|
$TMUX select-pane -T discard-test || exit 1
|
||||||
|
$TMUX respawn-pane -k \
|
||||||
|
"perl -e 'print qq{$prefix}, q{x} x 1100000, qq{\e\\\\\e[H\e[2JOK}'; $INPUT_HOLD" || exit 1
|
||||||
|
check_discard "$name"
|
||||||
|
$TMUX display-message -p -t "$name:" '#{pane_title}' >"$TMP" || exit 1
|
||||||
|
printf 'discard-test\n' >"$EXP"
|
||||||
|
cmp "$TMP" "$EXP" || fail "$name title"
|
||||||
|
}
|
||||||
|
|
||||||
start_cmd csi-param-discard 8 3 \
|
start_cmd csi-param-discard 8 3 \
|
||||||
"perl -e 'print qq{\e[}, q{1} x 80, qq{\030OK}'; sleep 2"
|
"perl -e 'print qq{\e[}, q{1} x 80, qq{\030OK}'; sleep 2"
|
||||||
check_capture csi-param-discard 'OK'
|
check_capture csi-param-discard 'OK'
|
||||||
@@ -10,13 +42,8 @@ start_cmd csi-interm-discard 8 3 \
|
|||||||
"perl -e 'print qq{\e[ \030OK}'; sleep 2"
|
"perl -e 'print qq{\e[ \030OK}'; sleep 2"
|
||||||
check_capture csi-interm-discard 'OK'
|
check_capture csi-interm-discard 'OK'
|
||||||
|
|
||||||
start_cmd osc-discard 8 3 \
|
test_discard osc-discard '\e]2;'
|
||||||
"perl -e 'print qq{\e]2;}, q{x} x 1100000, qq{\e\\\\OK}'; sleep 2"
|
test_discard apc-discard '\e_'
|
||||||
check_capture osc-discard 'OK'
|
|
||||||
|
|
||||||
start_cmd apc-discard 8 3 \
|
|
||||||
"perl -e 'print qq{\e_}, q{x} x 1100000, qq{\e\\\\OK}'; sleep 2"
|
|
||||||
check_capture apc-discard 'OK'
|
|
||||||
|
|
||||||
start_pane unknown-csi 8 3 '\033[?9999zOK'
|
start_pane unknown-csi 8 3 '\033[?9999zOK'
|
||||||
check_capture unknown-csi 'OK'
|
check_capture unknown-csi 'OK'
|
||||||
|
|||||||
@@ -51,7 +51,7 @@ $TMUX kill-server 2>/dev/null
|
|||||||
sleep 0.1
|
sleep 0.1
|
||||||
$TMUX new-session -d -x 5 -y 3 -s history \; \
|
$TMUX new-session -d -x 5 -y 3 -s history \; \
|
||||||
set-option -g history-limit 3 \; \
|
set-option -g history-limit 3 \; \
|
||||||
respawn-pane -k "printf '01\n02\n03\n04\n05\n06'; sleep 2" || exit 1
|
respawn-pane -k "printf '01\n02\n03\n04\n05\n06'; $INPUT_HOLD" || exit 1
|
||||||
sleep 0.3
|
sleep 0.3
|
||||||
$TMUX capture-pane -pN -t history: -S - -E - | normalize_capture >"$TMP"
|
$TMUX capture-pane -pN -t history: -S - -E - | normalize_capture >"$TMP"
|
||||||
printf "%s\n" '01
|
printf "%s\n" '01
|
||||||
|
|||||||
Executable
+137
@@ -0,0 +1,137 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
# Test parsing and printing JSON with display-message -j.
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
fail()
|
||||||
|
{
|
||||||
|
echo "$1"
|
||||||
|
$TMUX kill-server 2>/dev/null
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
check()
|
||||||
|
{
|
||||||
|
actual=$($TMUX display-message -plj "$1" 2>&1) ||
|
||||||
|
fail "JSON was rejected: $1: $actual"
|
||||||
|
[ "$actual" = "$2" ] ||
|
||||||
|
fail "JSON output mismatch: $1: expected $2, got $actual"
|
||||||
|
}
|
||||||
|
|
||||||
|
check_fail()
|
||||||
|
{
|
||||||
|
if $TMUX display-message -plj "$1" >/dev/null 2>&1; then
|
||||||
|
fail "invalid JSON was accepted: $1"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
$TMUX new-session -d || exit 1
|
||||||
|
|
||||||
|
check '{}' '{}'
|
||||||
|
check ' { "z":true, "a":-9223372036854775808, "m":[{}, {"x":false}] } ' \
|
||||||
|
'{"a":-9223372036854775808,"m":[{},{"x":false}],"z":true}'
|
||||||
|
check '{"max":9223372036854775807,"min":-9223372036854775808}' \
|
||||||
|
'{"max":9223372036854775807,"min":-9223372036854775808}'
|
||||||
|
check '{" key":" value","{key":"[value"}' \
|
||||||
|
'{" key":" value","{key":"[value"}'
|
||||||
|
check '{"brace":"{value","bracket":"[value","colon":":value",'\
|
||||||
|
'"comma":",value","space":" value"}' \
|
||||||
|
'{"brace":"{value","bracket":"[value","colon":":value",'\
|
||||||
|
'"comma":",value","space":" value"}'
|
||||||
|
check '{"esc":"\"\\\/\b\f\n\r\t\u0041"}' \
|
||||||
|
'{"esc":"\"\\\/\b\f\n\r\t\u0041"}'
|
||||||
|
check '{"unicode":"\u0123\uabcd\uABCD"}' \
|
||||||
|
'{"unicode":"\u0123\uabcd\uABCD"}'
|
||||||
|
|
||||||
|
actual=$($TMUX display-message -palj '{}' 2>&1) ||
|
||||||
|
fail "display-message -aj rejected JSON: $actual"
|
||||||
|
[ "$actual" = '{}' ] ||
|
||||||
|
fail "display-message -aj did not ignore -a: got $actual"
|
||||||
|
actual=$($TMUX display-message -pIlj '{}' 2>&1) ||
|
||||||
|
fail "display-message -Ij rejected JSON: $actual"
|
||||||
|
[ "$actual" = '{}' ] ||
|
||||||
|
fail "display-message -Ij did not ignore -I: got $actual"
|
||||||
|
|
||||||
|
check_fail ''
|
||||||
|
check_fail ' '
|
||||||
|
check_fail '[]'
|
||||||
|
check_fail 'true'
|
||||||
|
check_fail '1'
|
||||||
|
check_fail '"string"'
|
||||||
|
check_fail '{}{}'
|
||||||
|
|
||||||
|
check_fail '{'
|
||||||
|
check_fail '{x:1}'
|
||||||
|
check_fail '{"":1}'
|
||||||
|
check_fail '{"x" 1}'
|
||||||
|
check_fail '{"x",1}'
|
||||||
|
check_fail '{"x":}'
|
||||||
|
check_fail '{"x":,}'
|
||||||
|
check_fail '{"x":1'
|
||||||
|
check_fail '{"x":1 "y":2}'
|
||||||
|
check_fail '{"x":1,,"y":2}'
|
||||||
|
check_fail '{"x":1,}'
|
||||||
|
check_fail '{"x":1,"x":2}'
|
||||||
|
check_fail '{"x":{}}{}'
|
||||||
|
check_fail '{"x":{}} trailing'
|
||||||
|
check_fail '{"x":{'
|
||||||
|
check_fail '{"x":{}'
|
||||||
|
|
||||||
|
check_fail '{"x":['
|
||||||
|
check_fail '{"x":[}'
|
||||||
|
check_fail '{"x":[{}'
|
||||||
|
check_fail '{"x":[{},}'
|
||||||
|
check_fail '{"x":[{},]}'
|
||||||
|
check_fail '{"x":[{}{}]}'
|
||||||
|
check_fail '{"x":[{},,{}]}'
|
||||||
|
check_fail '{"x":[{"y":}]}'
|
||||||
|
check_fail '{"x":""}'
|
||||||
|
check_fail '{"x":null}'
|
||||||
|
check_fail '{"x":[null]}'
|
||||||
|
check_fail '{"x":[true]}'
|
||||||
|
check_fail '{"x":["string"]}'
|
||||||
|
check_fail '{"x":[[]]}'
|
||||||
|
check_fail '{"x":1.0}'
|
||||||
|
check_fail '{"x":1e2}'
|
||||||
|
check_fail '{"x":+1}'
|
||||||
|
check_fail '{"x":-}'
|
||||||
|
check_fail '{"x":0x10}'
|
||||||
|
check_fail '{"x":01}'
|
||||||
|
check_fail '{"x":-01}'
|
||||||
|
check_fail '{"x":[1]}'
|
||||||
|
check_fail '{"x":9223372036854775808}'
|
||||||
|
check_fail '{"x":-9223372036854775809}'
|
||||||
|
check_fail '{"x":tru}'
|
||||||
|
check_fail '{"x":True}'
|
||||||
|
check_fail '{"x":falsee}'
|
||||||
|
check_fail '{"x":"bad\q"}'
|
||||||
|
check_fail '{"x":"bad\x20"}'
|
||||||
|
check_fail '{"x":"bad\u123"}'
|
||||||
|
check_fail '{"x":"bad\u12x4"}'
|
||||||
|
check_fail '{"x":"unterminated}'
|
||||||
|
check_fail '{"x":"escaped quote\"}'
|
||||||
|
check_fail '{"x":"line
|
||||||
|
break"}'
|
||||||
|
|
||||||
|
# The maximum object nesting depth is 200.
|
||||||
|
json='{}'
|
||||||
|
n=1
|
||||||
|
while [ "$n" -lt 200 ]; do
|
||||||
|
json='{"x":'"$json"'}'
|
||||||
|
n=$((n + 1))
|
||||||
|
done
|
||||||
|
check "$json" "$json"
|
||||||
|
check_fail '{"x":'"$json"'}'
|
||||||
|
|
||||||
|
if $TMUX display-message -pj >/dev/null 2>&1; then
|
||||||
|
fail "display-message -j accepted a missing message"
|
||||||
|
fi
|
||||||
|
|
||||||
|
$TMUX kill-server 2>/dev/null
|
||||||
|
exit 0
|
||||||
Executable
+28
@@ -0,0 +1,28 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
# Killing a session whose window is zoomed must not crash the server:
|
||||||
|
# window_destroy used to unzoom the window, which resized the panes and
|
||||||
|
# fired pane-resized with the window in the payload, and releasing that
|
||||||
|
# reference destroyed the window a second time.
|
||||||
|
|
||||||
|
PATH=/bin:/usr/bin
|
||||||
|
TERM=screen
|
||||||
|
|
||||||
|
[ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux)
|
||||||
|
TMUX="$TEST_TMUX -LtestA$$ -f/dev/null"
|
||||||
|
$TMUX kill-server 2>/dev/null
|
||||||
|
|
||||||
|
$TMUX -f/dev/null new -d -sfoo || exit 1
|
||||||
|
$TMUX split-window -d -h -tfoo:0 || exit 1
|
||||||
|
$TMUX resize-pane -Z -tfoo:0 || exit 1
|
||||||
|
$TMUX new -d -sbar || exit 1
|
||||||
|
$TMUX kill-session -tfoo || exit 1
|
||||||
|
$TMUX has-session -tbar || exit 1
|
||||||
|
|
||||||
|
# The same with the zoomed window in a session killed by kill-server.
|
||||||
|
$TMUX new -d -sbaz || exit 1
|
||||||
|
$TMUX split-window -d -v -tbaz:0 || exit 1
|
||||||
|
$TMUX resize-pane -Z -tbaz:0 || exit 1
|
||||||
|
$TMUX kill-server || exit 1
|
||||||
|
|
||||||
|
exit 0
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,77 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
# Version 1 layouts must reject excessive nesting without losing the session or
|
||||||
|
# changing its layout. Check both split types and the depth limit boundary.
|
||||||
|
|
||||||
|
PATH=/bin:/usr/bin
|
||||||
|
TERM=screen
|
||||||
|
export PATH TERM
|
||||||
|
|
||||||
|
[ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux)
|
||||||
|
DIR=$(mktemp -d) || exit 1
|
||||||
|
TMUX="$TEST_TMUX -S$DIR/socket -f/dev/null"
|
||||||
|
|
||||||
|
fail()
|
||||||
|
{
|
||||||
|
echo "$*" >&2
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
cleanup()
|
||||||
|
{
|
||||||
|
$TMUX kill-server 2>/dev/null
|
||||||
|
rm -rf "$DIR"
|
||||||
|
}
|
||||||
|
trap cleanup 0
|
||||||
|
trap 'exit 1' 1 2 15
|
||||||
|
|
||||||
|
# Generate a checksum-valid v1 layout with one child in every nested group.
|
||||||
|
layout()
|
||||||
|
{
|
||||||
|
awk -v depth="$1" -v open="$2" 'BEGIN {
|
||||||
|
closing = (open == "{" ? "}" : "]")
|
||||||
|
body = ""
|
||||||
|
for (i = 0; i < depth; i++)
|
||||||
|
body = body "1x1,0,0" open
|
||||||
|
body = body "1x1,0,0"
|
||||||
|
for (i = 0; i < depth; i++)
|
||||||
|
body = body closing
|
||||||
|
|
||||||
|
ord["0"] = 48; ord["1"] = 49; ord["x"] = 120
|
||||||
|
ord[","] = 44; ord["{"] = 123; ord["}"] = 125
|
||||||
|
ord["["] = 91; ord["]"] = 93
|
||||||
|
csum = 0
|
||||||
|
for (i = 1; i <= length(body); i++) {
|
||||||
|
c = substr(body, i, 1)
|
||||||
|
bit = csum % 2
|
||||||
|
csum = int(csum / 2) + bit * 32768
|
||||||
|
csum = (csum + ord[c]) % 65536
|
||||||
|
}
|
||||||
|
printf "%04x,%s", csum, body
|
||||||
|
}'
|
||||||
|
}
|
||||||
|
|
||||||
|
$TMUX new-session -d -s deep -x80 -y24 'exec sleep 100' || exit 1
|
||||||
|
|
||||||
|
for open in '{' '['; do
|
||||||
|
# A valid checksum and nesting up to the limit must still be accepted.
|
||||||
|
value=$(layout 1000 "$open") || fail "could not generate layout"
|
||||||
|
$TMUX select-layout -t deep "$value" ||
|
||||||
|
fail "layout at the depth limit was rejected ($open)"
|
||||||
|
before=$($TMUX display-message -p -t deep \
|
||||||
|
'#{pane_id} #{pane_width} #{pane_height} #{window_layout}') || exit 1
|
||||||
|
|
||||||
|
for depth in 1001 1500; do
|
||||||
|
value=$(layout "$depth" "$open") || fail "could not generate layout"
|
||||||
|
$TMUX select-layout -t deep "$value" >/dev/null 2>&1 &&
|
||||||
|
fail "excessive nesting was accepted ($open, $depth)"
|
||||||
|
$TMUX has-session -t deep ||
|
||||||
|
fail "server died on excessive nesting ($open, $depth)"
|
||||||
|
after=$($TMUX display-message -p -t deep \
|
||||||
|
'#{pane_id} #{pane_width} #{pane_height} #{window_layout}') || exit 1
|
||||||
|
[ "$before" = "$after" ] ||
|
||||||
|
fail "pane or layout changed after rejection ($open, $depth)"
|
||||||
|
done
|
||||||
|
done
|
||||||
|
|
||||||
|
exit 0
|
||||||
@@ -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
|
||||||
@@ -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
|
||||||
+224
-1
@@ -105,6 +105,24 @@ drag()
|
|||||||
sleep 1
|
sleep 1
|
||||||
}
|
}
|
||||||
|
|
||||||
|
meta_drag()
|
||||||
|
{
|
||||||
|
scol="$1"
|
||||||
|
srow="$2"
|
||||||
|
ecol="$3"
|
||||||
|
erow="$4"
|
||||||
|
|
||||||
|
seq=$(printf '\033[<8;%s;%sM' "$scol" "$srow")
|
||||||
|
$TMUX2 send-keys -t "$OUTER" -l "$seq" 2>/dev/null
|
||||||
|
sleep 0.2
|
||||||
|
seq=$(printf '\033[<40;%s;%sM' "$ecol" "$erow")
|
||||||
|
$TMUX2 send-keys -t "$OUTER" -l "$seq" 2>/dev/null
|
||||||
|
sleep 0.2
|
||||||
|
seq=$(printf '\033[<8;%s;%sm' "$ecol" "$erow")
|
||||||
|
$TMUX2 send-keys -t "$OUTER" -l "$seq" 2>/dev/null
|
||||||
|
sleep 1
|
||||||
|
}
|
||||||
|
|
||||||
cleanup
|
cleanup
|
||||||
|
|
||||||
check_ok new-session -d -s modal -x 80 -y 24 'cat'
|
check_ok new-session -d -s modal -x 80 -y 24 'cat'
|
||||||
@@ -127,12 +145,16 @@ case "$(fmt "$modal" '#{pane_flags}')" in
|
|||||||
*O*) ;;
|
*O*) ;;
|
||||||
*) fail "modal pane flags do not include O" ;;
|
*) fail "modal pane flags do not include O" ;;
|
||||||
esac
|
esac
|
||||||
|
case "$(fmt "$modal" '#{pane_flags}')" in
|
||||||
|
*A*) ;;
|
||||||
|
*) fail "modal pane flags do not include A" ;;
|
||||||
|
esac
|
||||||
case "$(fmt modal:0 '#{window_flags}')" in
|
case "$(fmt modal:0 '#{window_flags}')" in
|
||||||
*O*) ;;
|
*O*) ;;
|
||||||
*) fail "modal window flags do not include O" ;;
|
*) fail "modal window flags do not include O" ;;
|
||||||
esac
|
esac
|
||||||
must_equal "$(fmt modal:0 '#{window_modal_pane}')" "$modal"
|
must_equal "$(fmt modal:0 '#{window_modal_pane}')" "$modal"
|
||||||
must_equal "$(fmt modal:0 '#{window_zoomed_flag}')" 0
|
must_equal "$(fmt modal:0 '#{window_zoomed_flag}')" 1
|
||||||
|
|
||||||
check_fail "window already has a modal pane" \
|
check_fail "window already has a modal pane" \
|
||||||
new-pane -O -x 10 -y 4 'cat'
|
new-pane -O -x 10 -y 4 'cat'
|
||||||
@@ -159,12 +181,14 @@ under=$($TMUX split-window -PF '#{pane_id}' -t "$p0" 'cat') ||
|
|||||||
sleep 1
|
sleep 1
|
||||||
must_equal "$(fmt modal:0 '#{pane_id}')" "$modal"
|
must_equal "$(fmt modal:0 '#{pane_id}')" "$modal"
|
||||||
must_equal "$(fmt "$under" '#{pane_active}')" 0
|
must_equal "$(fmt "$under" '#{pane_active}')" 0
|
||||||
|
must_equal "$(fmt modal:0 '#{window_zoomed_flag}')" 1
|
||||||
|
|
||||||
float=$($TMUX new-pane -PF '#{pane_id}' -x 10 -y 4 -X 5 -Y 3 'cat') ||
|
float=$($TMUX new-pane -PF '#{pane_id}' -x 10 -y 4 -X 5 -Y 3 'cat') ||
|
||||||
fail "new floating pane under modal failed"
|
fail "new floating pane under modal failed"
|
||||||
sleep 1
|
sleep 1
|
||||||
must_equal "$(fmt modal:0 '#{pane_id}')" "$modal"
|
must_equal "$(fmt modal:0 '#{pane_id}')" "$modal"
|
||||||
must_equal "$(fmt "$float" '#{pane_active}')" 0
|
must_equal "$(fmt "$float" '#{pane_active}')" 0
|
||||||
|
must_equal "$(fmt modal:0 '#{window_zoomed_flag}')" 1
|
||||||
|
|
||||||
check_ok new-window -d -t modal: -n other 'cat'
|
check_ok new-window -d -t modal: -n other 'cat'
|
||||||
check_ok select-window -t modal:other
|
check_ok select-window -t modal:other
|
||||||
@@ -303,6 +327,205 @@ check_ok kill-pane -t "$modal"
|
|||||||
sleep 1
|
sleep 1
|
||||||
must_equal "$(fmt modal:0 '#{window_modal_pane}')" ''
|
must_equal "$(fmt modal:0 '#{window_modal_pane}')" ''
|
||||||
must_equal "$(fmt modal:0 '#{pane_id}')" "$p0"
|
must_equal "$(fmt modal:0 '#{pane_id}')" "$p0"
|
||||||
|
must_equal "$(fmt "$p0" '#{window_zoomed_flag}:#{pane_zoomed_flag}')" 0:0
|
||||||
|
|
||||||
|
ignored=$($TMUX new-pane -KdPF '#{pane_id}' -t "$p0" 'cat') ||
|
||||||
|
fail "new-pane -K without -O failed"
|
||||||
|
check_ok kill-pane -t "$ignored"
|
||||||
|
|
||||||
|
$TMUX set -g @modal-prefix no
|
||||||
|
$TMUX set -g @modal-root no
|
||||||
|
$TMUX bind -n z set -g @modal-root yes
|
||||||
|
|
||||||
|
modal=$($TMUX new-pane -ODKPF '#{pane_id}' -t "$p0" \
|
||||||
|
-x 20 -y 5 -X 20 -Y 10 'cat') ||
|
||||||
|
fail "new-pane -ODK failed"
|
||||||
|
sleep 1
|
||||||
|
$TMUX2 send-keys -t "$OUTER" C-b x z Enter
|
||||||
|
sleep 1
|
||||||
|
must_equal "$($TMUX show -gv @modal-prefix)" no
|
||||||
|
must_equal "$($TMUX show -gv @modal-root)" no
|
||||||
|
case "$($TMUX capture-pane -pt "$modal")" in
|
||||||
|
*xz*) ;;
|
||||||
|
*) fail "keys did not reach key-capturing modal pane" ;;
|
||||||
|
esac
|
||||||
|
left=$(fmt "$modal" '#{pane_left}')
|
||||||
|
top=$(fmt "$modal" '#{pane_top}')
|
||||||
|
meta_drag $((left + 2)) $((top + 2)) $((left + 7)) $((top + 4))
|
||||||
|
new_left=$(fmt "$modal" '#{pane_left}')
|
||||||
|
new_top=$(fmt "$modal" '#{pane_top}')
|
||||||
|
[ "$new_left" -gt "$left" ] || [ "$new_top" -gt "$top" ] ||
|
||||||
|
fail "key-capturing modal pane did not move"
|
||||||
|
$TMUX2 send-keys -t "$OUTER" Escape
|
||||||
|
sleep 1
|
||||||
|
must_equal "$(fmt modal:0 '#{window_modal_pane}')" ''
|
||||||
|
|
||||||
|
modal=$($TMUX new-pane -ODKPF '#{pane_id}' -t "$p0" \
|
||||||
|
-x 20 -y 5 -X 20 -Y 10 'trap "" INT; exec cat') ||
|
||||||
|
fail "new-pane -ODK failed"
|
||||||
|
sleep 1
|
||||||
|
$TMUX2 send-keys -t "$OUTER" C-c
|
||||||
|
sleep 1
|
||||||
|
must_equal "$(fmt modal:0 '#{window_modal_pane}')" ''
|
||||||
|
|
||||||
|
# A dead modal does not close on Escape or C-c without -D.
|
||||||
|
modal=$($TMUX new-pane -OPF '#{pane_id}' -t "$p0" \
|
||||||
|
-x 20 -y 5 -X 20 -Y 10 'sleep 1') ||
|
||||||
|
fail "new-pane -O failed"
|
||||||
|
check_ok set-option -p -t "$modal" remain-on-exit on
|
||||||
|
sleep 2
|
||||||
|
must_equal "$(fmt "$modal" '#{pane_dead}:#{pane_modal_flag}')" 1:1
|
||||||
|
$TMUX2 send-keys -t "$OUTER" Escape
|
||||||
|
sleep 1
|
||||||
|
must_equal "$(fmt modal:0 '#{window_modal_pane}')" "$modal"
|
||||||
|
check_ok kill-pane -t "$modal"
|
||||||
|
sleep 1
|
||||||
|
|
||||||
|
# failed-key closes successful panes and retains failed panes until a key.
|
||||||
|
modal=$($TMUX new-pane -OPF '#{pane_id}' -t "$p0" \
|
||||||
|
-x 20 -y 5 -X 20 -Y 10 'sleep 1') ||
|
||||||
|
fail "new-pane -O failed"
|
||||||
|
check_ok set-option -p -t "$modal" remain-on-exit failed-key
|
||||||
|
sleep 2
|
||||||
|
must_equal "$(fmt modal:0 '#{window_modal_pane}')" ''
|
||||||
|
|
||||||
|
modal=$($TMUX new-pane -OPF '#{pane_id}' -t "$p0" \
|
||||||
|
-x 20 -y 5 -X 20 -Y 10 'sleep 1; exit 1') ||
|
||||||
|
fail "new-pane -O failed"
|
||||||
|
check_ok set-option -p -t "$modal" remain-on-exit failed-key
|
||||||
|
sleep 2
|
||||||
|
must_equal "$(fmt "$modal" '#{pane_dead}:#{pane_modal_flag}')" 1:1
|
||||||
|
must_equal "$($TMUX show-options -pv -t "$modal" remain-on-exit)" failed-key
|
||||||
|
$TMUX2 send-keys -t "$OUTER" a
|
||||||
|
sleep 1
|
||||||
|
must_equal "$(fmt modal:0 '#{window_modal_pane}')" ''
|
||||||
|
|
||||||
|
# A nonmodal floating pane may remain above zoom, and switching between it and
|
||||||
|
# the zoomed tiled pane must not unzoom the window.
|
||||||
|
check_ok new-window -d -t modal: -n float-over-zoom 'cat'
|
||||||
|
base=$(fmt modal:float-over-zoom '#{pane_id}')
|
||||||
|
check_ok split-window -dh -t "$base" 'cat'
|
||||||
|
check_ok resize-pane -Z -t "$base"
|
||||||
|
over=$($TMUX new-pane -APF '#{pane_id}' -t "$base" \
|
||||||
|
-x 20 -y 5 -X 20 -Y 10 'cat') ||
|
||||||
|
fail "new-pane -A failed"
|
||||||
|
must_equal "$(fmt "$over" '#{pane_floating_flag}:#{pane_active}')" 1:1
|
||||||
|
case "$(fmt "$over" '#{pane_flags}')" in
|
||||||
|
*A*) ;;
|
||||||
|
*) fail "float-over-zoom pane flags do not include A" ;;
|
||||||
|
esac
|
||||||
|
must_equal "$(fmt "$base" '#{window_zoomed_flag}:#{pane_zoomed_flag}')" 1:1
|
||||||
|
check_ok select-pane -t "$base"
|
||||||
|
must_equal "$(fmt "$base" '#{window_zoomed_flag}:#{pane_active}')" 1:1
|
||||||
|
check_ok select-pane -t "$over"
|
||||||
|
must_equal "$(fmt "$over" '#{window_zoomed_flag}:#{pane_active}')" 1:1
|
||||||
|
client=$($TMUX list-clients -F '#{client_name}' | head -1)
|
||||||
|
[ -n "$client" ] || fail "no client for switch-client test"
|
||||||
|
check_ok switch-client -c "$client" -t "$base"
|
||||||
|
must_equal "$(fmt "$base" '#{window_zoomed_flag}:#{pane_active}')" 1:1
|
||||||
|
check_ok switch-client -c "$client" -t "$over"
|
||||||
|
must_equal "$(fmt "$over" '#{window_zoomed_flag}:#{pane_active}')" 1:1
|
||||||
|
check_ok kill-pane -t "$over"
|
||||||
|
must_equal "$(fmt "$base" '#{window_zoomed_flag}:#{pane_zoomed_flag}')" 1:1
|
||||||
|
check_ok resize-pane -Z -t "$base"
|
||||||
|
|
||||||
|
ignored=$($TMUX new-pane -ALdPF '#{pane_id}' -t "$base" 'cat') ||
|
||||||
|
fail "new-pane -A -L failed"
|
||||||
|
case "$(fmt "$ignored" '#{pane_flags}')" in
|
||||||
|
*A*) fail "tiled pane flags include A" ;;
|
||||||
|
*) ;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
# Existing floating panes are filtered when zoom begins: -A panes remain in the
|
||||||
|
# visible layout and ordinary floating panes do not.
|
||||||
|
check_ok new-window -d -t modal: -n existing-over-zoom 'cat'
|
||||||
|
base=$(fmt modal:existing-over-zoom '#{pane_id}')
|
||||||
|
check_ok split-window -dh -t "$base" 'cat'
|
||||||
|
over=$($TMUX new-pane -AdPF '#{pane_id}' -t "$base" \
|
||||||
|
-x 20 -y 5 -X 20 -Y 10 'cat') ||
|
||||||
|
fail "pre-existing new-pane -A failed"
|
||||||
|
under=$($TMUX new-pane -dPF '#{pane_id}' -t "$base" \
|
||||||
|
-x 15 -y 4 -X 2 -Y 2 'cat') ||
|
||||||
|
fail "pre-existing ordinary new-pane failed"
|
||||||
|
check_ok select-window -t modal:existing-over-zoom
|
||||||
|
check_ok select-pane -t "$base"
|
||||||
|
check_ok resize-pane -Z -t "$base"
|
||||||
|
must_equal "$(fmt "$over" '#{pane_floating_flag}')" 1
|
||||||
|
must_equal "$(fmt "$under" '#{pane_floating_flag}')" 0
|
||||||
|
must_equal "$(fmt "$base" '#{window_zoomed_flag}:#{pane_zoomed_flag}')" 1:1
|
||||||
|
|
||||||
|
# Geometry changed in the visible zoom layout is copied back when unzooming.
|
||||||
|
check_ok select-pane -t "$over"
|
||||||
|
left=$(fmt "$over" '#{pane_left}')
|
||||||
|
top=$(fmt "$over" '#{pane_top}')
|
||||||
|
meta_drag $((left + 2)) $((top + 2)) $((left + 7)) $((top + 4))
|
||||||
|
new_left=$(fmt "$over" '#{pane_left}')
|
||||||
|
new_top=$(fmt "$over" '#{pane_top}')
|
||||||
|
[ "$new_left" -gt "$left" ] || [ "$new_top" -gt "$top" ] ||
|
||||||
|
fail "float-over-zoom pane did not move"
|
||||||
|
must_equal "$(fmt "$base" '#{window_zoomed_flag}:#{pane_zoomed_flag}')" 1:1
|
||||||
|
check_ok resize-pane -Z -t "$base"
|
||||||
|
must_equal "$(fmt "$over" '#{pane_left}:#{pane_top}')" \
|
||||||
|
"$new_left:$new_top"
|
||||||
|
must_equal "$(fmt "$under" '#{pane_floating_flag}')" 1
|
||||||
|
|
||||||
|
# Resizing with the over-zoom pane active restores the original zoom target.
|
||||||
|
check_ok resize-pane -Z -t "$base"
|
||||||
|
check_ok select-pane -t "$over"
|
||||||
|
check_ok resize-window -t modal:existing-over-zoom -x 90 -y 30
|
||||||
|
must_equal "$(fmt "$base" \
|
||||||
|
'#{window_width}x#{window_height}:#{window_zoomed_flag}:#{pane_zoomed_flag}')" \
|
||||||
|
90x30:1:1
|
||||||
|
must_equal "$(fmt "$over" '#{pane_floating_flag}:#{pane_active}')" 1:1
|
||||||
|
|
||||||
|
# Natural pane exit uses a different removal path from kill-pane and must also
|
||||||
|
# preserve zoom.
|
||||||
|
dying=$($TMUX new-pane -AdPF '#{pane_id}' -t "$base" \
|
||||||
|
-x 12 -y 4 -X 4 -Y 3 'true') ||
|
||||||
|
fail "short-lived new-pane -A failed"
|
||||||
|
i=0
|
||||||
|
while $TMUX list-panes -a -F '#{pane_id}' | grep -qx "$dying"; do
|
||||||
|
i=$((i + 1))
|
||||||
|
[ $i -gt 50 ] && fail "short-lived float-over-zoom pane did not exit"
|
||||||
|
sleep 0.1
|
||||||
|
done
|
||||||
|
must_equal "$(fmt "$base" '#{window_zoomed_flag}:#{pane_zoomed_flag}')" 1:1
|
||||||
|
|
||||||
|
# A pane with -A is also above a zoom target which was itself floating. The
|
||||||
|
# temporary tiled target must sit behind retained floating panes, then return
|
||||||
|
# to the normal floating z order when unzoomed.
|
||||||
|
check_ok new-window -d -t modal: -n floating-zoom-target 'cat'
|
||||||
|
base=$(fmt modal:floating-zoom-target '#{pane_id}')
|
||||||
|
check_ok split-window -dh -t "$base" 'cat'
|
||||||
|
target=$($TMUX new-pane -dPF '#{pane_id}' -t "$base" \
|
||||||
|
-x 30 -y 10 -X 10 -Y 5 'cat') ||
|
||||||
|
fail "floating zoom target creation failed"
|
||||||
|
over=$($TMUX new-pane -AdPF '#{pane_id}' -t "$base" \
|
||||||
|
-x 15 -y 5 -X 15 -Y 8 'cat') ||
|
||||||
|
fail "float-over-zoom pane creation failed"
|
||||||
|
check_ok select-window -t modal:floating-zoom-target
|
||||||
|
check_ok select-pane -t "$target"
|
||||||
|
must_equal "$(fmt "$target" '#{pane_z}')" 0
|
||||||
|
must_equal "$(fmt "$over" '#{pane_z}')" 1
|
||||||
|
|
||||||
|
check_ok resize-pane -Z -t "$target"
|
||||||
|
must_equal "$(fmt "$target" \
|
||||||
|
'#{window_zoomed_flag}:#{pane_zoomed_flag}:#{pane_floating_flag}:#{pane_z}')" \
|
||||||
|
1:1:0:2
|
||||||
|
must_equal "$(fmt "$over" '#{pane_floating_flag}:#{pane_z}')" 1:0
|
||||||
|
check_ok select-pane -t "$over"
|
||||||
|
must_equal "$(fmt "$over" '#{window_zoomed_flag}:#{pane_active}')" 1:1
|
||||||
|
check_ok resize-pane -Z -t "$target"
|
||||||
|
must_equal "$(fmt "$over" '#{pane_active}:#{pane_z}')" 1:0
|
||||||
|
must_equal "$(fmt "$target" '#{pane_floating_flag}:#{pane_z}')" 1:1
|
||||||
|
|
||||||
|
# If the target remains active, it returns to the front on unzoom.
|
||||||
|
check_ok select-pane -t "$target"
|
||||||
|
check_ok resize-pane -Z -t "$target"
|
||||||
|
must_equal "$(fmt "$over" '#{pane_floating_flag}:#{pane_z}')" 1:0
|
||||||
|
check_ok resize-pane -Z -t "$target"
|
||||||
|
must_equal "$(fmt "$target" '#{pane_active}:#{pane_z}')" 1:0
|
||||||
|
must_equal "$(fmt "$over" '#{pane_z}')" 1
|
||||||
|
|
||||||
cleanup
|
cleanup
|
||||||
exit 0
|
exit 0
|
||||||
|
|||||||
Executable → Regular
@@ -269,6 +269,22 @@ test_customize_mode()
|
|||||||
assert_alive "customize-mode exit"
|
assert_alive "customize-mode exit"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
test_customize_break_pane()
|
||||||
|
{
|
||||||
|
start_client option-break
|
||||||
|
side=$($TMUX split-window -d -P -F '#{pane_id}' \
|
||||||
|
-t option-break:0 'cat') || fail "customize split failed"
|
||||||
|
|
||||||
|
$TMUX customize-mode -t "$side" || fail "customize-mode failed"
|
||||||
|
wait_mode "$side" 1
|
||||||
|
$TMUX break-pane -d -s "$side" || fail "customize break-pane failed"
|
||||||
|
|
||||||
|
assert_alive "customize-mode break-pane"
|
||||||
|
wait_mode "$side" 1
|
||||||
|
$TMUX send-keys -t "$side" q || fail "customize-mode quit failed"
|
||||||
|
wait_mode "$side" 0
|
||||||
|
}
|
||||||
|
|
||||||
test_copy_mode()
|
test_copy_mode()
|
||||||
{
|
{
|
||||||
start_client copy-a 'i=0; while [ $i -lt 200 ]; do echo "copy mutation line $i"; i=$((i + 1)); done; cat'
|
start_client copy-a 'i=0; while [ $i -lt 200 ]; do echo "copy mutation line $i"; i=$((i + 1)); done; cat'
|
||||||
@@ -302,6 +318,7 @@ test_choose_tree
|
|||||||
test_choose_buffer
|
test_choose_buffer
|
||||||
test_choose_client
|
test_choose_client
|
||||||
test_customize_mode
|
test_customize_mode
|
||||||
|
test_customize_break_pane
|
||||||
test_copy_mode
|
test_copy_mode
|
||||||
cleanup
|
cleanup
|
||||||
exit 0
|
exit 0
|
||||||
|
|||||||
+184
-7
@@ -19,7 +19,10 @@
|
|||||||
# - respawn-pane/respawn-window refusing a live pane without -k, working on a
|
# - respawn-pane/respawn-window refusing a live pane without -k, working on a
|
||||||
# dead pane (remain-on-exit) and killing with -k;
|
# dead pane (remain-on-exit) and killing with -k;
|
||||||
# - resize-pane -x/-y in cells and percent, -L/-R/-U/-D adjustments and -Z
|
# - resize-pane -x/-y in cells and percent, -L/-R/-U/-D adjustments and -Z
|
||||||
# zoom/unzoom (including implicit unzoom on split).
|
# zoom/unzoom, layout round trips, zoom target selection, pane removal,
|
||||||
|
# window resizing and implicit unzoom on layout changes;
|
||||||
|
# - swap-pane preserving one or two zoomed windows, including cross-window
|
||||||
|
# swaps of hidden panes and zoom targets.
|
||||||
#
|
#
|
||||||
# window-ops.sh covers window-level commands and buffers.sh paste buffers.
|
# window-ops.sh covers window-level commands and buffers.sh paste buffers.
|
||||||
|
|
||||||
@@ -101,6 +104,25 @@ assert_alive()
|
|||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# Compare layout geometry and pane ordering, ignoring the active pane and
|
||||||
|
# last-pane history in the JSON layout. Selection is checked separately.
|
||||||
|
pane_layout()
|
||||||
|
{
|
||||||
|
$TMUX display-message -p -t "$1" '#{window_layout}' |
|
||||||
|
sed 's/,"a":true//g; s/,"l":[0-9][0-9]*//g'
|
||||||
|
}
|
||||||
|
|
||||||
|
check_layout()
|
||||||
|
{
|
||||||
|
out=$(pane_layout "$1")
|
||||||
|
if [ "$out" != "$2" ]; then
|
||||||
|
echo "Layout for '$1' wrong."
|
||||||
|
echo "Expected: '$2'"
|
||||||
|
echo "But got: '$out'"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
# ---------------------------------------------------------------------------
|
# ---------------------------------------------------------------------------
|
||||||
# split-window geometry.
|
# split-window geometry.
|
||||||
|
|
||||||
@@ -144,7 +166,8 @@ check_panes P:0 "0:$p3 1:$p0 2:$p2 3:$p1"
|
|||||||
# break-pane and join-pane.
|
# break-pane and join-pane.
|
||||||
|
|
||||||
# break-pane moves a pane to a new window; -P -F prints where it went and -n
|
# break-pane moves a pane to a new window; -P -F prints where it went and -n
|
||||||
# names the new window.
|
# names the new window. Moving a pane out of a zoomed window unzooms it first.
|
||||||
|
check_ok resize-pane -Z -t "$p0"
|
||||||
out=$($TMUX break-pane -d -P -F '#{window_index}:#{pane_id}' -n broken \
|
out=$($TMUX break-pane -d -P -F '#{window_index}:#{pane_id}' -n broken \
|
||||||
-s "$p1" -t P:)
|
-s "$p1" -t P:)
|
||||||
if [ "$out" != "1:$p1" ]; then
|
if [ "$out" != "1:$p1" ]; then
|
||||||
@@ -153,10 +176,14 @@ if [ "$out" != "1:$p1" ]; then
|
|||||||
fi
|
fi
|
||||||
check_fmt 'P:1' '#{window_name}:#{window_panes}' 'broken:1'
|
check_fmt 'P:1' '#{window_name}:#{window_panes}' 'broken:1'
|
||||||
check_fmt 'P:0' '#{window_panes}' '3'
|
check_fmt 'P:0' '#{window_panes}' '3'
|
||||||
|
check_fmt 'P:0' '#{window_zoomed_flag}' '0'
|
||||||
|
|
||||||
# join-pane -v moves it back (the source window, left empty, is destroyed).
|
# join-pane -v moves it back (the source window, left empty, is destroyed) and
|
||||||
|
# also unzooms the destination before changing its layout.
|
||||||
|
check_ok resize-pane -Z -t "$p0"
|
||||||
check_ok join-pane -d -v -s P:broken.0 -t "$p2"
|
check_ok join-pane -d -v -s P:broken.0 -t "$p2"
|
||||||
check_fmt 'P:0' '#{window_panes}' '4'
|
check_fmt 'P:0' '#{window_panes}' '4'
|
||||||
|
check_fmt 'P:0' '#{window_zoomed_flag}' '0'
|
||||||
if $TMUX has-session -t P:broken 2>/dev/null; then
|
if $TMUX has-session -t P:broken 2>/dev/null; then
|
||||||
echo "Window 'broken' still exists after join-pane."
|
echo "Window 'broken' still exists after join-pane."
|
||||||
exit 1
|
exit 1
|
||||||
@@ -192,13 +219,13 @@ check_fail "invalid window name: $(printf 'a\377b')" \
|
|||||||
break-pane -d -n "$(printf 'a\377b')" -s "$p1" -t P:
|
break-pane -d -n "$(printf 'a\377b')" -s "$p1" -t P:
|
||||||
|
|
||||||
# join-pane can move a pane from one window to another without destroying
|
# join-pane can move a pane from one window to another without destroying
|
||||||
# the source window if other panes remain. (On this branch move-pane is
|
# the source window if other panes remain. move-pane does the same when no
|
||||||
# reserved for floating panes, covered by floating-pane-geometry.sh.)
|
# floating-pane movement flags are given.
|
||||||
check_ok new-window -d -t P:5 -n other
|
check_ok new-window -d -t P:5 -n other
|
||||||
check_ok join-pane -d -s "$p1" -t P:5.0
|
check_ok join-pane -d -s "$p1" -t P:5.0
|
||||||
check_fmt 'P:5' '#{window_panes}' '2'
|
check_fmt 'P:5' '#{window_panes}' '2'
|
||||||
check_fmt 'P:0' '#{window_panes}' '3'
|
check_fmt 'P:0' '#{window_panes}' '3'
|
||||||
check_ok join-pane -d -v -s "$p1" -t "$p2"
|
check_ok move-pane -d -v -s "$p1" -t "$p2"
|
||||||
check_fmt 'P:0' '#{window_panes}' '4'
|
check_fmt 'P:0' '#{window_panes}' '4'
|
||||||
check_fmt 'P:5' '#{window_panes}' '1'
|
check_fmt 'P:5' '#{window_panes}' '1'
|
||||||
|
|
||||||
@@ -299,6 +326,122 @@ check_fmt 'P:0' '#{window_panes}' '5'
|
|||||||
p6=$($TMUX display-message -p -t P:0.2 '#{pane_id}')
|
p6=$($TMUX display-message -p -t P:0.2 '#{pane_id}')
|
||||||
check_ok kill-pane -t "$p6"
|
check_ok kill-pane -t "$p6"
|
||||||
|
|
||||||
|
# Zoom and unzoom preserve the exact tiled layout. Selecting another pane
|
||||||
|
# without -Z unzooms, while -Z transfers zoom to the selected pane.
|
||||||
|
layout=$(pane_layout P:0)
|
||||||
|
check_ok select-pane -t "$p0"
|
||||||
|
check_ok resize-pane -Z -t "$p0"
|
||||||
|
check_ok select-pane -t "$p2"
|
||||||
|
check_fmt "$p2" '#{window_zoomed_flag}:#{pane_active}' '0:1'
|
||||||
|
check_layout P:0 "$layout"
|
||||||
|
|
||||||
|
check_ok select-pane -t "$p0"
|
||||||
|
check_ok resize-pane -Z -t "$p0"
|
||||||
|
check_ok select-pane -Z -t "$p2"
|
||||||
|
check_fmt "$p2" '#{window_zoomed_flag}:#{pane_zoomed_flag}:#{pane_active}' \
|
||||||
|
'1:1:1'
|
||||||
|
check_ok resize-pane -Z -t "$p2"
|
||||||
|
check_layout P:0 "$layout"
|
||||||
|
|
||||||
|
# Directional selection temporarily restores the full layout to find its
|
||||||
|
# neighbour, then follows the same unzoom or -Z transfer rules.
|
||||||
|
check_ok select-pane -t "$p0"
|
||||||
|
check_ok resize-pane -Z -t "$p0"
|
||||||
|
check_ok select-pane -t "$p0"
|
||||||
|
check_fmt "$p0" '#{window_zoomed_flag}:#{pane_zoomed_flag}:#{pane_active}' \
|
||||||
|
'1:1:1'
|
||||||
|
check_ok select-pane -D -t "$p0"
|
||||||
|
check_fmt "$p2" '#{window_zoomed_flag}:#{pane_active}' '0:1'
|
||||||
|
|
||||||
|
check_ok select-pane -t "$p0"
|
||||||
|
check_ok resize-pane -Z -t "$p0"
|
||||||
|
check_ok select-pane -D -Z -t "$p0"
|
||||||
|
check_fmt "$p2" '#{window_zoomed_flag}:#{pane_zoomed_flag}:#{pane_active}' \
|
||||||
|
'1:1:1'
|
||||||
|
check_ok resize-pane -Z -t "$p2"
|
||||||
|
check_layout P:0 "$layout"
|
||||||
|
|
||||||
|
# The last-pane path has separate zoom handling, both with and without -Z.
|
||||||
|
check_ok select-pane -t "$p0"
|
||||||
|
check_ok select-pane -t "$p2"
|
||||||
|
check_ok resize-pane -Z -t "$p2"
|
||||||
|
check_ok select-pane -l -t P:0
|
||||||
|
check_fmt "$p0" '#{window_zoomed_flag}:#{pane_zoomed_flag}:#{pane_active}' \
|
||||||
|
'0:0:1'
|
||||||
|
|
||||||
|
check_ok select-pane -t "$p0"
|
||||||
|
check_ok select-pane -t "$p2"
|
||||||
|
check_ok resize-pane -Z -t "$p2"
|
||||||
|
check_ok select-pane -l -Z -t P:0
|
||||||
|
check_fmt "$p0" '#{window_zoomed_flag}:#{pane_zoomed_flag}:#{pane_active}' \
|
||||||
|
'1:1:1'
|
||||||
|
check_ok resize-pane -Z -t "$p0"
|
||||||
|
check_layout P:0 "$layout"
|
||||||
|
|
||||||
|
# Killing either a hidden ordinary pane or the zoom target unzooms.
|
||||||
|
check_ok new-window -d -t P:12 -n zoom-kill 'cat'
|
||||||
|
zk0=$($TMUX display-message -p -t P:12.0 '#{pane_id}')
|
||||||
|
zk1=$($TMUX split-window -d -P -F '#{pane_id}' -t P:12.0 'cat')
|
||||||
|
zk2=$($TMUX split-window -d -P -F '#{pane_id}' -t P:12.0 'cat')
|
||||||
|
check_ok resize-pane -Z -t "$zk0"
|
||||||
|
check_ok kill-pane -t "$zk1"
|
||||||
|
check_fmt "$zk0" '#{window_panes}:#{window_zoomed_flag}:#{pane_zoomed_flag}' \
|
||||||
|
'2:0:0'
|
||||||
|
check_ok resize-pane -Z -t "$zk0"
|
||||||
|
check_ok kill-pane -t "$zk0"
|
||||||
|
check_fmt "$zk2" '#{window_panes}:#{window_zoomed_flag}:#{pane_zoomed_flag}' \
|
||||||
|
'1:0:0'
|
||||||
|
check_ok kill-window -t P:12
|
||||||
|
|
||||||
|
# The same cases through natural process exit exercise server_destroy_pane.
|
||||||
|
check_ok new-window -d -t P:13 -n zoom-exit 'cat'
|
||||||
|
ze0=$($TMUX display-message -p -t P:13.0 '#{pane_id}')
|
||||||
|
ze1=$($TMUX split-window -d -P -F '#{pane_id}' -t P:13.0 'cat')
|
||||||
|
ze2=$($TMUX split-window -d -P -F '#{pane_id}' -t P:13.0 'cat')
|
||||||
|
check_ok resize-pane -Z -t "$ze0"
|
||||||
|
check_ok send-keys -t "$ze1" C-d
|
||||||
|
i=0
|
||||||
|
while [ "$($TMUX display-message -p -t P:13 '#{window_panes}')" != 2 ]; do
|
||||||
|
i=$((i + 1))
|
||||||
|
[ $i -gt 50 ] && { echo "Hidden pane did not exit."; exit 1; }
|
||||||
|
sleep 0.1
|
||||||
|
done
|
||||||
|
check_fmt "$ze0" '#{window_zoomed_flag}:#{pane_zoomed_flag}' '0:0'
|
||||||
|
check_ok resize-pane -Z -t "$ze2"
|
||||||
|
check_ok send-keys -t "$ze2" C-d
|
||||||
|
i=0
|
||||||
|
while [ "$($TMUX display-message -p -t P:13 '#{window_panes}')" != 1 ]; do
|
||||||
|
i=$((i + 1))
|
||||||
|
[ $i -gt 50 ] && { echo "Zoomed pane did not exit."; exit 1; }
|
||||||
|
sleep 0.1
|
||||||
|
done
|
||||||
|
check_fmt "$ze0" '#{window_zoomed_flag}:#{pane_zoomed_flag}' '0:0'
|
||||||
|
check_ok kill-window -t P:13
|
||||||
|
|
||||||
|
# resize-window restores both tiled and floating zoom targets after rebuilding
|
||||||
|
# the layout at the new size.
|
||||||
|
check_ok new-window -d -t P:14 -n zoom-resize 'cat'
|
||||||
|
zr0=$($TMUX display-message -p -t P:14.0 '#{pane_id}')
|
||||||
|
check_ok split-window -d -t P:14.0 'cat'
|
||||||
|
check_ok resize-pane -Z -t "$zr0"
|
||||||
|
check_ok resize-window -t P:14 -x 90 -y 30
|
||||||
|
check_fmt "$zr0" \
|
||||||
|
'#{window_width}x#{window_height}:#{window_zoomed_flag}:#{pane_zoomed_flag}' \
|
||||||
|
'90x30:1:1'
|
||||||
|
check_ok resize-pane -Z -t "$zr0"
|
||||||
|
zrf=$($TMUX new-pane -dP -F '#{pane_id}' -t P:14 -x 20 -y 6 'cat')
|
||||||
|
zrf_size=$($TMUX display-message -p -t "$zrf" \
|
||||||
|
'#{pane_width}x#{pane_height}')
|
||||||
|
check_ok resize-pane -Z -t "$zrf"
|
||||||
|
check_ok resize-window -t P:14 -x 100 -y 32
|
||||||
|
check_fmt "$zrf" \
|
||||||
|
'#{window_width}x#{window_height}:#{window_zoomed_flag}:#{pane_zoomed_flag}' \
|
||||||
|
'100x32:1:1'
|
||||||
|
check_ok resize-pane -Z -t "$zrf"
|
||||||
|
check_fmt "$zrf" '#{pane_floating_flag}:#{pane_width}x#{pane_height}' \
|
||||||
|
"1:$zrf_size"
|
||||||
|
check_ok kill-window -t P:14
|
||||||
|
|
||||||
# ---------------------------------------------------------------------------
|
# ---------------------------------------------------------------------------
|
||||||
# kill-pane.
|
# kill-pane.
|
||||||
|
|
||||||
@@ -307,8 +450,10 @@ check_ok kill-pane -t "$p3"
|
|||||||
check_panes P:0 "0:$p0 1:$p2 2:$p1"
|
check_panes P:0 "0:$p0 1:$p2 2:$p1"
|
||||||
|
|
||||||
# -a kills every pane except the target.
|
# -a kills every pane except the target.
|
||||||
|
check_ok resize-pane -Z -t "$p0"
|
||||||
check_ok kill-pane -a -t "$p0"
|
check_ok kill-pane -a -t "$p0"
|
||||||
check_panes P:0 "0:$p0"
|
check_panes P:0 "0:$p0"
|
||||||
|
check_fmt "$p0" '#{window_zoomed_flag}:#{pane_zoomed_flag}' '0:0'
|
||||||
|
|
||||||
# Killing the last pane in a window kills the window.
|
# Killing the last pane in a window kills the window.
|
||||||
check_ok new-window -d -t P:7 -n goner
|
check_ok new-window -d -t P:7 -n goner
|
||||||
@@ -516,6 +661,10 @@ check_panes P:3 "0:$r0 1:$r1 2:$r2"
|
|||||||
# Swapping a pane with itself quietly does nothing.
|
# Swapping a pane with itself quietly does nothing.
|
||||||
check_ok swap-pane -d -s "$r1" -t "$r1"
|
check_ok swap-pane -d -s "$r1" -t "$r1"
|
||||||
check_panes P:3 "0:$r0 1:$r1 2:$r2"
|
check_panes P:3 "0:$r0 1:$r1 2:$r2"
|
||||||
|
check_ok resize-pane -Z -t "$r1"
|
||||||
|
check_ok swap-pane -d -Z -s "$r1" -t "$r1"
|
||||||
|
check_fmt "$r1" '#{window_zoomed_flag}:#{pane_zoomed_flag}' '1:1'
|
||||||
|
check_ok resize-pane -Z -t "$r1"
|
||||||
|
|
||||||
# Panes can be swapped between different windows.
|
# Panes can be swapped between different windows.
|
||||||
check_ok swap-pane -d -s "$o0" -t "$r1"
|
check_ok swap-pane -d -s "$o0" -t "$r1"
|
||||||
@@ -525,10 +674,38 @@ check_ok swap-pane -d -s "$r1" -t "$o0"
|
|||||||
check_panes P:3 "0:$r0 1:$r1 2:$r2"
|
check_panes P:3 "0:$r0 1:$r1 2:$r2"
|
||||||
check_panes P:5 "0:$o0"
|
check_panes P:5 "0:$o0"
|
||||||
|
|
||||||
|
# With both windows zoomed, -Z preserves each window's zoom when hidden panes
|
||||||
|
# are exchanged and when the zoom targets themselves are exchanged.
|
||||||
|
o1=$($TMUX split-window -dP -F '#{pane_id}' -t P:5.0)
|
||||||
|
check_ok resize-pane -Z -t "$r0"
|
||||||
|
check_ok resize-pane -Z -t "$o0"
|
||||||
|
check_ok swap-pane -d -Z -s "$r1" -t "$o1"
|
||||||
|
check_fmt "$r0" \
|
||||||
|
'#{window_name}:#{window_zoomed_flag}:#{pane_zoomed_flag}' 'swaps:1:1'
|
||||||
|
check_fmt "$o0" \
|
||||||
|
'#{window_name}:#{window_zoomed_flag}:#{pane_zoomed_flag}' 'other:1:1'
|
||||||
|
check_fmt "$r1" '#{window_name}' 'other'
|
||||||
|
check_fmt "$o1" '#{window_name}' 'swaps'
|
||||||
|
|
||||||
|
check_ok swap-pane -d -Z -s "$r0" -t "$o0"
|
||||||
|
check_fmt "$o0" \
|
||||||
|
'#{window_name}:#{window_zoomed_flag}:#{pane_zoomed_flag}' 'swaps:1:1'
|
||||||
|
check_fmt "$r0" \
|
||||||
|
'#{window_name}:#{window_zoomed_flag}:#{pane_zoomed_flag}' 'other:1:1'
|
||||||
|
check_ok resize-pane -Z -t "$o0"
|
||||||
|
check_ok resize-pane -Z -t "$r0"
|
||||||
|
|
||||||
|
# Restore the original panes before the remaining swap tests.
|
||||||
|
check_ok swap-pane -d -s "$r0" -t "$o0"
|
||||||
|
check_ok swap-pane -d -s "$r1" -t "$o1"
|
||||||
|
check_ok kill-pane -t "$o1"
|
||||||
|
check_panes P:3 "0:$r0 1:$r1 2:$r2"
|
||||||
|
check_panes P:5 "0:$o0"
|
||||||
|
|
||||||
# -Z keeps the window zoomed across the swap.
|
# -Z keeps the window zoomed across the swap.
|
||||||
check_ok resize-pane -Z -t "$r0"
|
check_ok resize-pane -Z -t "$r0"
|
||||||
check_ok swap-pane -d -Z -s "$r0" -t "$r1"
|
check_ok swap-pane -d -Z -s "$r0" -t "$r1"
|
||||||
check_fmt 'P:3' '#{window_zoomed_flag}' '1'
|
check_fmt "$r0" '#{window_zoomed_flag}:#{pane_zoomed_flag}' '1:1'
|
||||||
check_ok resize-pane -Z -t P:3
|
check_ok resize-pane -Z -t P:3
|
||||||
check_panes P:3 "0:$r1 1:$r0 2:$r2"
|
check_panes P:3 "0:$r1 1:$r0 2:$r2"
|
||||||
|
|
||||||
|
|||||||
@@ -35,9 +35,9 @@ check_alive()
|
|||||||
|
|
||||||
# A pipe-pane -I child may write after the pane process has exited. With
|
# A pipe-pane -I child may write after the pane process has exited. With
|
||||||
# remain-on-exit, the pane stays around but its bufferevent has been freed.
|
# remain-on-exit, the pane stays around but its bufferevent has been freed.
|
||||||
check_ok new-session -d -s pipe -x 80 -y 24 'sleep 0.2'
|
check_ok new-session -d -s pipe -x 80 -y 24 'sleep 1'
|
||||||
check_ok set-option -t pipe:0 remain-on-exit on
|
check_ok set-option -t pipe:0 remain-on-exit on
|
||||||
check_ok pipe-pane -t pipe:0.0 -I 'sleep 0.6; printf x'
|
check_ok pipe-pane -t pipe:0.0 -I 'sleep 2; printf x'
|
||||||
|
|
||||||
i=0
|
i=0
|
||||||
while [ "$($TMUX display-message -p -t pipe:0.0 '#{pane_dead}')" != "1" ]; do
|
while [ "$($TMUX display-message -p -t pipe:0.0 '#{pane_dead}')" != "1" ]; do
|
||||||
@@ -46,7 +46,7 @@ while [ "$($TMUX display-message -p -t pipe:0.0 '#{pane_dead}')" != "1" ]; do
|
|||||||
sleep 0.1
|
sleep 0.1
|
||||||
done
|
done
|
||||||
|
|
||||||
sleep 0.7
|
sleep 2
|
||||||
check_alive
|
check_alive
|
||||||
|
|
||||||
$TMUX kill-server 2>/dev/null
|
$TMUX kill-server 2>/dev/null
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user