Compare commits

..

295 Commits

Author SHA1 Message Date
Dane Jensen
4a91d5601e Merge branch 'master' into floating_panes 2026-07-02 12:10:44 -07:00
Dane Jensen
7de5eb1254 Merge branch 'master' into floating_panes 2026-07-01 14:37:05 -07:00
Dane Jensen
9e0ca948bf Fixed merge errors. 2026-06-29 15:34:45 -07:00
Dane Jensen
8e070f1a34 Merge branch 'master' into floating_panes 2026-06-29 15:25:54 -07:00
Dane Jensen
6ec0afe5f7 Merge branch 'master' into floating_panes 2026-06-22 11:32:30 -07:00
Dane Jensen
41e731fc51 Fixed keybind for toggling between floating and tiled. 2026-06-21 13:16:34 -07:00
Dane Jensen
19c8da04d0 Merge branch 'master' into floating_panes 2026-06-21 13:12:34 -07:00
Dane Jensen
8925000c42 Removed cmd-tile-float-pane.c after moving functionality into cmd-break-pane.c and cmd-join-pane.c 2026-06-20 12:35:36 -07:00
Dane Jensen
568c99be4e Removed cmd-tile-float-pane.c. 2026-06-20 12:32:35 -07:00
Dane Jensen
69fc42afc9 Floating and tiling functionality moved into break-pane and join-pane 2026-06-20 12:28:04 -07:00
Dane Jensen
19843cd826 bugfix
There is a signature and functionality mismatch with the recent merge in
`layout_floating_pane` that has been fixed. It now more gracefully
handles inputs.
2026-06-20 12:22:08 -07:00
Dane Jensen
4eb96fc144 Merge branch 'master' into floating_panes 2026-06-20 12:12:51 -07:00
Dane Jensen
3d396f13ed A few fixes. 2026-06-19 11:33:24 -07:00
Dane Jensen
7d8401d707 formatting fix. 2026-06-19 11:33:24 -07:00
Dane Jensen
c3aac57dda Updated documentation. 2026-06-19 11:33:24 -07:00
Dane Jensen
5602571c01 Moved functionality to float and tile panes into break-pane and
`join-pane`.
2026-06-19 11:33:24 -07:00
Dane Jensen
c04236052c Merge branch 'master' into floating_panes 2026-06-19 11:31:46 -07:00
Dane Jensen
584cafa284 fix(floating_panes): Fixed tiled cell detection and removed dead guard.
When checking for space to split a layout cell, the window pane associated with that cell is needed to check for options. This fixes an incomplete scan for that window pane while updating the documentation to be more clear. There was also a redundant guard that just added noise that is now removed.
2026-06-18 15:05:38 -07:00
Dane Jensen
8a4a150a1a Fixed tiled cell detection and removed dead guard. 2026-06-18 15:01:21 -07:00
Dane Jensen
de41b93b73 Merge branch 'master' into floating_panes 2026-06-18 12:54:11 -07:00
Dane Jensen
4aa1dd3ae8 Merge pull request #5239 from daneofmanythings/fp--missing-flag-move-pane
fix(floating_panes): Added missing `-D` flag from `move-pane` .args field.
2026-06-17 15:01:03 -07:00
Dane Jensen
be33d01663 Added missing -D flag from move-pane .args field. 2026-06-17 14:59:34 -07:00
Dane Jensen
fdcb93a467 Merge branch 'master' into floating_panes 2026-06-17 14:32:01 -07:00
Dane Jensen
c54f7ec95e Remove extra code. 2026-06-16 13:43:13 -07:00
Dane Jensen
f8448764e2 Merge branch 'master' into floating_panes 2026-06-16 13:38:33 -07:00
Dane Jensen
f71e81c483 Merge pull request #5230 from daneofmanythings/fp--is_visible
fix(floating_panes): changed `window_pane_visible` to `window_pane_is_visible`
2026-06-15 21:20:03 -07:00
Dane Jensen
9bd53b8565 changed window_pane_visible to window_pane_is_visible 2026-06-15 21:18:47 -07:00
Dane Jensen
dde9dda1a4 Merge branch 'master' into floating_panes 2026-06-15 16:52:21 -07:00
Dane Jensen
55c8b0c2a7 Merge branch 'master' into floating_panes 2026-06-15 13:00:51 -07:00
Nicholas Marriott
fd1fca51fa Remove old code. 2026-06-15 08:59:43 +01:00
Dane Jensen
00620b57eb Bug fix for parsing floating args. 2026-06-14 22:14:08 -07:00
Dane Jensen
bba9f38636 Bug fix for parsing floating args. 2026-06-14 22:11:57 -07:00
Dane Jensen
9c37acd002 hide-pane and show-pane now use the new layout mechanics. 2026-06-14 21:57:11 -07:00
Dane Jensen
195a9b357a hide-pane and show-pane now use the new layout mechanics. 2026-06-14 21:48:03 -07:00
Dane Jensen
cd4c4bf723 tile-pane and float-pane now use the new layout mechanics
The layout position of every cell is kept throughout the state changing actions, and can therefore be restored.
2026-06-14 20:07:42 -07:00
Dane Jensen
e244be2540 Bug fixes and adjustments from feedback. 2026-06-14 20:02:24 -07:00
Dane Jensen
5dad31b06e slight renaming. 2026-06-14 19:59:41 -07:00
Dane Jensen
64ded18960 Updating tile-pane and float-pane to use new layout mechanics. 2026-06-14 19:59:37 -07:00
Dane Jensen
4dd496790a Merge branch 'master' into floating_panes 2026-06-14 19:54:43 -07:00
Dane Jensen
db9e782d50 Merge branch 'master' into floating_panes 2026-06-12 12:07:41 -07:00
Dane Jensen
d4869ebdc5 Enabled swapping with floating panes from the pane menu. 2026-06-11 14:57:37 -07:00
Dane Jensen
d1fe0d449b Enabled swapping marked panes from the pane mouse menu. 2026-06-11 14:55:43 -07:00
Dane Jensen
e2b52abd47 Merge pull request #5197 from daneofmanythings/fp--hidden-pane-flag-location
fix(floating_panes): Move the hidden panes format logic to their alphabetical positions.
2026-06-11 14:52:31 -07:00
Dane Jensen
f58eb4dc0e put the hidden panes format specifiers in the correct positions. 2026-06-11 14:48:42 -07:00
Dane Jensen
8497b588a2 Merge master into floating_panes 2026-06-11 12:05:11 -07:00
Dane Jensen
57641f67dd Merge pull request #5193 from daneofmanythings/fp--swap-floating-panes
fix(floating_panes): `swap-pane` can now affect floating panes.
2026-06-10 23:35:16 -07:00
Dane Jensen
b675c2149c This guard doesn't need to be here. Why restrict this? 2026-06-10 15:10:36 -07:00
Dane Jensen
4fbc30267c remove old logic. 2026-06-10 15:09:10 -07:00
Dane Jensen
2a830aa567 Merge branch 'master' into floating_panes 2026-06-10 12:47:50 -07:00
Dane Jensen
84229d4735 Floating layout cells can now be placed anywhere in the layout tree.
Allowing layout cells to exist anywhere in the layout tree will enable floating and tiling commands to restore position in the tree with little effort. This enables that by slightly altering the floating pane creation logic, and adding some utility functions. These functions will be soon be used elsewhere as well.
2026-06-09 22:59:13 +00:00
Dane Jensen
e370ce5a00 Added function comment. 2026-06-09 15:44:24 -07:00
Dane Jensen
2a1ad05671 There was an bug with the earlier commit, reproduction: "splitw; newp
-t0; killp -t0". Added logic to handle previously unforseen states, like
a floating cell before a tiled cell at the top of the screen.
2026-06-09 15:37:30 -07:00
Dane Jensen
7568bff8e6 Layout cells for floating panes are now inserted after the cell of a
provided window pane.
2026-06-09 13:33:45 -07:00
Dane Jensen
6766c8ec1d Initial commit. 2026-06-09 13:33:45 -07:00
Dane Jensen
2955c1fdc7 Merge master into floating_panes. 2026-06-09 20:33:15 +00:00
Dane Jensen
45e9e541a4 Merge branch 'master' into fp-merge 2026-06-09 13:29:32 -07:00
Dane Jensen
b2ac4475cc Merge pull request #5182 from daneofmanythings/fp-merge
Merging master into floating panes.
2026-06-08 23:01:12 +00:00
Dane Jensen
a7952492c1 Merged master into floating_panes 2026-06-08 15:58:38 -07:00
Nicholas Marriott
0b1089e105 Merge branch 'master' into floating_panes 2026-06-04 14:10:25 +01:00
Dane Jensen
6b3bdaff97 The backporting of work on staging left a few scattered compile errors.
fix(floating_panes): Leftover compile errors
2026-06-04 00:51:01 +00:00
Dane Jensen
7c908bca7a Fixed vestigal compile errors 2026-06-03 17:46:29 -07:00
Nicholas Marriott
90ede52728 Merge branch 'master' into floating_panes 2026-06-02 20:36:08 +01:00
Nicholas Marriott
17974ea7a0 Merge branch 'master' into floating_panes 2026-06-01 21:36:29 +01:00
Nicholas Marriott
25852623f0 Merge branch 'master' into floating_panes 2026-06-01 21:30:20 +01:00
Michael Grant
446aa08286 Bug fix vertical position of scrollbar had wrong vertical offset when status-position top. 2026-06-01 20:52:51 +01:00
Michael Grant
12b3cc3750 Merge branch 'floating_panes' of github.com:tmux/tmux into floating_panes 2026-06-01 20:20:58 +01:00
Michael Grant
6dd288d459 Fix bug: pane-border-status top, can't resize floating panes by dragging the border. 2026-06-01 20:20:42 +01:00
Nicholas Marriott
d8547fc721 Merge branch 'master' into floating_panes 2026-06-01 19:35:29 +01:00
Nicholas Marriott
68fdc4fcbe Merge branch 'master' into floating_panes 2026-06-01 15:28:25 +01:00
Nicholas Marriott
398cb6fb4a Merge branch 'master' into floating_panes 2026-06-01 12:20:03 +01:00
Nicholas Marriott
7302549903 Merge branch 'master' into floating_panes 2026-06-01 08:44:44 +01:00
Nicholas Marriott
e971bc79d6 Merge branch 'master' into floating_panes 2026-05-30 12:48:11 +01:00
Nicholas Marriott
650ad28f12 Merge branch 'master' into floating_panes 2026-05-30 11:59:37 +01:00
Nicholas Marriott
2637477eea Merge branch 'master' into floating_panes 2026-05-30 09:28:19 +01:00
Nicholas Marriott
e5c69afa98 Merge branch 'master' into floating_panes 2026-05-29 09:40:53 +01:00
Nicholas Marriott
ec901e9e31 Merge branch 'master' into floating_panes 2026-05-28 12:54:25 +01:00
Nicholas Marriott
d814529285 Merge branch 'master' into floating_panes 2026-05-28 12:07:54 +01:00
Nicholas Marriott
82dc3d57f2 Merge branch 'master' into floating_panes 2026-05-28 10:15:31 +01:00
Nicholas Marriott
3f77cb95c5 Change tty_draw_pane to draw in the right place I think. 2026-05-28 09:39:44 +01:00
Nicholas Marriott
68e9ff797c Merge branch 'master' into floating_panes 2026-05-28 09:39:25 +01:00
Nicholas Marriott
97ee6e2587 Break screen_write_collect_flush into some helper functions. 2026-05-28 08:24:54 +01:00
Nicholas Marriott
e0215480cd Merge branch 'master' into floating_panes 2026-05-27 22:22:38 +01:00
Nicholas Marriott
bd33d9c47d Merge branch 'master' into floating_panes 2026-05-27 22:21:39 +01:00
Nicholas Marriott
3a025d39db Merge branch 'master' into floating_panes 2026-05-27 21:43:14 +01:00
Nicholas Marriott
1ed454d102 Do not reduce the end position when removing padding. 2026-05-27 21:41:51 +01:00
Nicholas Marriott
52250d518d Redraw line directly for wide cells in screen_write_cell, fix up selection and
generally tidy.
2026-05-27 21:41:48 +01:00
Nicholas Marriott
bfa2ff2bdf Remove unused variable. 2026-05-27 20:33:41 +01:00
Nicholas Marriott
c62b27f014 Add a helper to replace a loop. 2026-05-27 20:33:03 +01:00
Nicholas Marriott
20cf0d3ea9 Merge branch 'master' into floating_panes 2026-05-27 20:27:48 +01:00
Nicholas Marriott
02caf7ce4a Remove a difference from master. 2026-05-27 19:36:20 +01:00
Nicholas Marriott
e1c7367a2e Merge branch 'master' into floating_panes 2026-05-27 19:31:56 +01:00
Nicholas Marriott
fc4641c5a5 Merge branch 'master' into floating_panes 2026-05-27 16:54:02 +01:00
Nicholas Marriott
8f9e2c7db2 Trivial tidying up of FP clears. 2026-05-27 14:30:57 +01:00
Nicholas Marriott
46be03d88e Clamp range with to size of window (visible ranges cannot be outside window). 2026-05-26 13:02:07 +01:00
Nicholas Marriott
f9bd5eb79e If a pane is outside the window, treat as obscured. 2026-05-26 12:48:16 +01:00
Nicholas Marriott
d89a4ab3df Redraw obscured panes instead of scrolling also. 2026-05-26 11:41:06 +01:00
Nicholas Marriott
57b18d8a18 Move redraw of overlapped panes into common functions. 2026-05-26 11:20:28 +01:00
Nicholas Marriott
95465ca03a Merge branch 'master' into floating_panes 2026-05-25 16:19:05 +01:00
Nicholas Marriott
6513808720 And a bad merge. 2026-05-25 15:50:25 +01:00
Nicholas Marriott
6c1773542d Some trivial bits from master. 2026-05-25 15:49:22 +01:00
Nicholas Marriott
c60422241b Merge branch 'master' into floating_panes 2026-05-25 09:21:36 +01:00
Michael Grant
92dcb22e92 Clean up in screen_redraw_draw_pane and fix an offset bug. 2026-05-25 08:17:57 +01:00
Nicholas Marriott
f75b1e5f07 Do not check overlays in tty_draw_line_clear, the caller should be doing it. 2026-05-24 12:25:51 +01:00
Nicholas Marriott
fb9e47d8c9 Some chanegs I made in the wrong branch. 2026-05-24 12:10:46 +01:00
Nicholas Marriott
43ae6e0a6b tty_clear_area should not work out popup ranges because tty_clear_line is going
to do it anyway.
2026-05-24 12:07:42 +01:00
Nicholas Marriott
724d6d7c95 Change to fall into redraw for alignment test. 2026-05-24 11:43:35 +01:00
Michael Grant
95c20aef20 Fix the alignmenttest. 2026-05-24 11:22:44 +01:00
Nicholas Marriott
791f77b879 Some missing calls to tty_check_overlay_range. 2026-05-24 10:18:28 +01:00
Nicholas Marriott
a6986864db Merge branch 'master' into floating_panes 2026-05-24 10:05:56 +01:00
Michael Grant
30866d06d5 Add visible range checks to screen_write.c functions and remove checks from tty.c. 2026-05-24 09:58:30 +01:00
Michael Grant
8b474429b7 Merge branch 'floating_panes' of github.com:tmux/tmux into floating_panes 2026-05-23 06:16:15 +01:00
Michael Grant
91b4e02805 Fix merge messup where i is no longer x, use px and ri->px instead here. 2026-05-23 06:15:12 +01:00
Nicholas Marriott
e65e653ea5 Merge branch 'master' into floating_panes 2026-05-22 21:31:28 +01:00
Nicholas Marriott
29a1a1f8b0 Check overlay/BCE first to avoid walking panes unless needed. 2026-05-22 18:03:01 +01:00
Nicholas Marriott
71de737ee8 Merge branch 'master' into floating_panes 2026-05-22 17:10:08 +01:00
Nicholas Marriott
55fbacb469 Fix merge error. 2026-05-22 13:07:54 +01:00
Nicholas Marriott
2a6aac4c14 Merge branch 'master' into floating_panes 2026-05-22 12:44:49 +01:00
Nicholas Marriott
4fd8431ad0 Merge branch 'master' into floating_panes 2026-05-22 12:39:09 +01:00
Nicholas Marriott
d3e8dd3623 Use a flags bit for obscured rather than an int. 2026-05-22 10:58:37 +01:00
Nicholas Marriott
1fa6ebacf4 Merge branch 'master' into floating_panes 2026-05-22 10:40:29 +01:00
Nicholas Marriott
8fa822207d Cache obscure flag in screen_write_ctx. 2026-05-22 09:07:38 +01:00
Nicholas Marriott
52ad3c8425 Change pane_is_obscured to take the context since it always uses its pane. 2026-05-22 09:03:51 +01:00
Nicholas Marriott
e5abcd217c Start at the current pane in the zindex list for working out if a pane is
obscured.
2026-05-22 08:58:48 +01:00
Nicholas Marriott
4a0118aea1 Merge branch 'master' into floating_panes 2026-05-21 12:07:17 +01:00
Dane Jensen
90a748f7fb Added logic to prevent hidden panes from being selected as the window's active pane.
Additional logic added to find any valid pane if the best fit checks fail.
2026-05-20 19:33:30 +00:00
Dane Jensen
2434653867 fix: added conditionals and more finding logic to avoid displaying hidden panes. 2026-05-20 12:19:56 -07:00
Nicholas Marriott
1def587884 Remove code that was already applied. 2026-05-20 13:47:11 +01:00
Nicholas Marriott
c56bc9ed05 Store pane ID for mouse last pane instead of a pointer to the pane. 2026-05-20 13:41:13 +01:00
Nicholas Marriott
2a9ba51907 Merge branch 'master' into floating_panes 2026-05-20 12:06:49 +01:00
Nicholas Marriott
406ae3d8a6 Remove some trivial differences. 2026-05-20 09:46:38 +01:00
Nicholas Marriott
8a674265f3 Merge branch 'master' into floating_panes 2026-05-20 09:35:03 +01:00
Dane Jensen
ed43033c39 Changed the semantics from un/minimise-pane to hide/show-pane
Several functions and flags were semantically changed. No logic was altered.
2026-05-20 04:14:59 +00:00
Dane Jensen
0a7b008b21 Changed minimised semantics to hide semantics 2026-05-19 21:09:53 -07:00
Nicholas Marriott
f8e908b89c More trivial merge errors. 2026-05-19 13:26:27 +01:00
Nicholas Marriott
2695c5305e More merge fixes. 2026-05-19 13:23:21 +01:00
Nicholas Marriott
55fdfef9d1 Merge fixes. 2026-05-19 13:19:21 +01:00
Nicholas Marriott
2f6da393e2 Merge branch 'master' into floating_panes 2026-05-19 13:05:14 +01:00
Nicholas Marriott
57b17bbb70 window_pane_move is not used. 2026-05-19 10:54:35 +01:00
Dane Jensen
7c23da598d Fixed null dereference in window.c:window_pane_float_geometry when there is no target wp.
Conditions for the bug: start tmux, run `minimise-pane; new-pane`. Now fixed.
2026-05-19 00:21:48 +00:00
Dane Jensen
b7cc218a03 Fixed null dereference. 2026-05-18 17:14:19 -07:00
Dane Jensen
0ae8019e7f Better api design and abstraction for new-pane and split-window.
While working on #5043, feedback was given by @mgrant0 regarding api design that also required addressing in `new-pane` and `split-window`. This MR addresses that feedback (no subflags, flag restriction for semantic reasons, unified 'float' and 'tile' flags across commands).
2026-05-18 22:46:53 +00:00
Dane Jensen
c3fc0229e5 style fixes 2026-05-18 15:41:28 -07:00
Dane Jensen
76196b8ffa Moved new-pane into split-window. 2026-05-18 13:48:50 -07:00
Nicholas Marriott
fd4e683151 Merge branch 'master' into floating_panes 2026-05-18 11:10:09 +01:00
Nicholas Marriott
2b05d8531d Merge branch 'master' into floating_panes 2026-05-17 15:55:09 +01:00
Nicholas Marriott
8e966688a7 If outside floating pane, do not carry on to check as if tiled pane. 2026-05-17 15:49:21 +01:00
Michael Grant
c4ec9234e9 Fix to ignore minimised panes when redoing the layout. 2026-05-15 18:23:39 +01:00
Michael Grant
2e0eabbf0a Fix screen_redraw_get_visible_ranges to ignore minimised panes. 2026-05-15 18:20:29 +01:00
Michael Grant
1b2435fab6 Fix bug where tty output of command outputing was getting written to the window aafter it was minimised. 2026-05-13 22:46:32 +01:00
Michael Grant
1ff3dafe4e Fix crash when minimising pane. 2026-05-13 22:28:14 +01:00
Nicholas Marriott
6b16e93f1b Merge branch 'master' into floating_panes 2026-05-12 12:38:31 +01:00
Nicholas Marriott
b7467a3110 Merge pull request #5039 from daneofmanythings/layout-null-dereference
fix: fixed site of null dereference in layout.c
2026-05-12 10:27:54 +01:00
Nicholas Marriott
4224ecd2ab Merge pull request #5042 from daneofmanythings/floating_panes--two-panes-crash
fix(floating_panes): null dereference in `screen_redraw_two_panes` from `screen_redraw_border_arrows`
2026-05-01 12:54:38 +01:00
Dane Jensen
e18c10d340 Passing in '&type' to avoid null dereference 2026-04-27 21:51:02 -07:00
Dane Jensen
0719dcf2e7 fixed site of null dereference in layout.c 2026-04-25 15:05:58 -07:00
Nicholas Marriott
3400a312dd Merge pull request #5026 from daneofmanythings/floating_panes--new-pane-doc-fixup
docs(floating_panes): A couple documentation fixes from #4979
2026-04-24 16:06:12 +03:00
Michael Grant
b2f9144e10 Merge pull request #5029 from daneofmanythings/floating_panes--fix-join-pane
fix(floating_panes): Z-index related crashing in `join-pane`
2026-04-24 13:56:37 +01:00
Dane Jensen
a74f173902 more. 2026-04-23 21:53:06 -07:00
Dane Jensen
a1cd68e3f8 Fixed z-index related crash in join-pane 2026-04-23 20:23:50 -07:00
Dane Jensen
eec2c19ad1 oversights. 2026-04-23 17:07:53 -07:00
Michael Grant
05fdd04262 Fix the z-index of the pane. (nic's fix.) 2026-04-24 00:49:22 +01:00
Nicholas Marriott
3e36ddbceb Merge pull request #4979 from daneofmanythings/floating_panes--new-pane
refactor: Combined the code for `split-window` and `new-pane`
2026-04-22 10:29:07 +03:00
Dane Jensen
0a55e5ca54 Fixed logical error in handing default cascading. 2026-04-17 12:44:50 -07:00
Dane Jensen
a7ad0c4434 Option flag adjustment for eventual consistency. 2026-04-16 11:38:33 -07:00
Michael Grant
271bac5347 Merge branch 'master' into floating_panes 2026-04-15 23:26:02 -04:00
Dane Jensen
914ffc8887 touchup. 2026-04-15 12:33:08 -07:00
Dane Jensen
7a4e35e317 doc fix. 2026-04-14 13:08:19 -07:00
Dane Jensen
1bc85cb59e Adjustment from feedback and slight touchups. 2026-04-14 12:40:59 -07:00
Dane Jensen
f992c68fd8 slight touchup 2026-04-13 22:04:08 -07:00
Dane Jensen
4bb7a86753 Tested the new commands and updated documentation. 2026-04-13 21:39:30 -07:00
Dane Jensen
950194fb63 Migrated cmd-split-window.c into cmd-new-pane.c for code reuse. 2026-04-06 20:10:15 -07:00
Michael Grant
b528fc438c Merge. 2026-04-05 19:40:56 -04:00
Michael Grant
811604a663 Fix bugs with tiling floating panes. 2026-04-05 19:04:10 -04:00
Michael Grant
4232bf9e2f Add new tile-pane and float-pane commands. 2026-03-30 18:01:55 -04:00
Michael Grant
8a3620e438 Initial commit. 2026-03-30 17:47:28 -04:00
Michael Grant
1408861511 Fix minimise window gesture to be like MacOS and Windows where if you click an active window in the status line it minimises it and if its already minimised it unminimises it. The ux feel now is natural. 2026-03-28 09:25:19 +00:00
Michael Grant
44229e8af6 Fix a crash when clicking a floating pane in the status line which was minimised after it was made active by window-tree. Correct the way window-tree displays floating panes so they can be easily unminimmised. 2026-03-28 08:35:35 +00:00
Michael Grant
d002323de2 Merge branch 'master' into floating_panes 2026-03-26 20:51:42 +00:00
Michael Grant
999c7246c3 Formatting. 2026-03-26 08:46:28 +00:00
Michael Grant
c10a597bf0 add -R to be able to set floating pane inactive border style. 2026-03-24 18:15:24 +00:00
Michael Grant
75ed7b27c6 Add -S and -s options to style floating panes. 2026-03-24 17:49:55 +00:00
Michael Grant
ef01e9daf8 Reviewed and removed the xxx fix-me comments. 2026-03-24 14:35:59 +00:00
Michael Grant
866cb2d16f Update tmux.1 man page to add new-pane floating pane section. 2026-03-24 14:29:26 +00:00
Michael Grant
6a62d49720 Add options to new-pane: -k -m message to wait for key before closing a floating pane. 2026-03-24 14:23:58 +00:00
Michael Grant
f201d246fd Fix next-layout to ignore floating panes. 2026-03-24 12:41:04 +00:00
Michael Grant
0328fe44e4 Fix sixel image writing so that it is aware of floating panes. 2026-03-22 09:28:15 +00:00
Michael Grant
bb85983a29 Merge pull request #4929 from daneofmanythings/floating_panes-pane_status_format
feat: Panes' status format now displays title/flag information
2026-03-20 09:46:44 +00:00
Michael Grant
4341d6a095 Merge branch 'master' into floating_panes 2026-03-20 08:05:13 +00:00
Michael Grant
10a9ce1ed2 Prohibit swapping 2 floating panes, that doesn't make sense. 2026-03-20 07:59:25 +00:00
Michael Grant
943490cfa1 Fix issue when a wp disappears in the middle of a drag event. 2026-03-20 07:49:49 +00:00
Michael Grant
7e6bbc63ab Fix a slew of possible int vs u_int bugs which would likely have caused an overflow crash. 2026-03-18 13:04:21 +00:00
Michael Grant
0d195698f8 Fix dispay bugs where the cursor was on a line that had 2 visible ranges because it was partially obscured by a floating pane. 2026-03-18 09:38:17 +00:00
Dane Jensen
08fd890a89 Added status format functionality to panes along with flags 2026-03-17 22:50:04 -07:00
Michael Grant
ab0081294c Fix scrolling issue when a floating pane overlaps a tiled pane. 2026-03-17 17:08:02 +00:00
Michael Grant
c42a939e98 Fix some int/u_int bugs and added some logging. 2026-03-17 17:05:57 +00:00
Michael Grant
6c9e2f7e6c Fix no cursor in popup. 2026-03-16 12:07:41 +00:00
Michael Grant
992f04f328 Merge branch 'master' into floating_panes 2026-03-16 09:12:08 +00:00
Michael Grant
8a90fce476 Fix for window size taller than tty. 2026-03-10 13:21:01 +00:00
Michael Grant
e928e80a42 Fix scrollbar issue not respecting oy when window size taller than tty. 2026-03-09 16:28:06 +00:00
Michael Grant
28c04b21f6 Minor fixup. 2026-03-09 08:35:24 +00:00
Michael Grant
46427f1dd7 Merge branch 'master' into floating_panes 2026-03-09 08:31:58 +00:00
Michael Grant
528e856566 Merge pull request #4893 from daneofmanythings/floating_panes__float_format
feat(format variable): added format variable for floating panes.
2026-03-08 17:14:16 +00:00
Michael Grant
14a934d412 Fix screen clearing issue to surgically clear around floating panes. 2026-03-08 17:11:55 +00:00
Dane Jensen
0faed7aeb5 added format flag for floating panes. 2026-03-04 12:08:43 -08:00
Michael Grant
8e6450c8fc Merge branch 'master' into floating_panes 2026-03-04 13:16:52 +00:00
Michael Grant
6898e26a7a Fix bug with utf-8 extended characters not respecting visible range. 2026-02-24 01:19:24 +00:00
Michael Grant
e2cf40ef99 Fix crash cause by not having a wp pointer in tty.c. 2026-02-23 08:08:31 +00:00
Michael Grant
96515c9f47 Merge branch 'master' into floating_panes 2026-02-18 09:28:38 +00:00
Michael Grant
96e18faa93 Merge branch 'master' into floating_panes 2026-02-11 09:40:21 +00:00
Michael Grant
476c6e89ad Check the overlay visible ranges when we have a tty and check the panes visible ranges when we have a wp. Overlays checked at tty level (for most part) and pane overlays checked at screen level where possible to keep separation of abstraction relatively clean. 2026-02-11 09:30:48 +00:00
Michael Grant
b46a96d454 Second try with static visual_ranges using calloc. 2026-01-29 14:34:18 +00:00
Michael Grant
f67cf7d053 Return static range as last resort. 2026-01-29 11:46:21 +00:00
Nicholas Marriott
2ad0f4a9e7 Merge branch 'master' into floating_panes 2026-01-29 08:43:41 +00:00
Michael Grant
1ec9ca497b Merge with visible_ranges banch. Convert visible_ranges to new style. 2026-01-28 15:26:00 +00:00
Michael Grant
dd5d0f4f0c Merge master. 2026-01-23 09:29:39 +00:00
Nicholas Marriott
d1a6ce8e7f Associate each visible_ranges with some other object (tty, popup_data, etc) so
it is easier to keep track of its lifecycle, but still avoid allocating for
each use.
2026-01-21 21:29:56 +00:00
Michael Grant
b22537e8a4 Conflict resolve. 2026-01-21 19:36:05 +00:00
Michael Grant
a25c14d472 Change overlay_ranges into visible_ranges. 2026-01-21 19:34:19 +00:00
Nicholas Marriott
7730d38339 Skip correct width when moving to next position. 2026-01-21 15:58:11 +00:00
Nicholas Marriott
58e498c9d3 Use right cell for empty check. 2026-01-21 15:35:33 +00:00
Nicholas Marriott
35485f2b5e Fix selection with tty_draw_line. 2026-01-21 12:23:46 +00:00
Nicholas Marriott
b108653f02 Change overlay_ranges to visible_ranges. 2026-01-20 21:18:33 +00:00
Nicholas Marriott
25f72cf240 Rewrite tty_draw_line to be simpler and not to check overlay ranges. 2026-01-20 21:18:20 +00:00
Thomas Adam
d1df8dcccc focus-follows-mouse: honour floating panes
Make sure the z-ordering of floating panes is honoured when
focus-follows-mouse is in use.
2026-01-09 19:00:17 +00:00
Michael Grant
a50d00aa47 Merge pull request #4799 from tmux/master
Sync from master
2026-01-09 11:30:16 +01:00
Michael Grant
34add944f2 Merge branch 'master' into feature-floating-window-panes 2026-01-07 23:41:29 +01:00
Michael Grant
95f85efc49 Fix some of the offset issues when windows size is greater than tty size. 2026-01-07 22:28:23 +00:00
Michael Grant
f2f6a05e2c Add cmd-minimise-pane.c 2026-01-06 16:44:38 +00:00
Michael Grant
bd442a27ee Fix some redraw issues. Fix scrollbar left/right display issues. 2025-12-24 10:45:17 +00:00
Michael Grant
73c0eb96c3 Merge branch 'master' into feature-floating-window-panes 2025-12-18 23:14:59 +01:00
Michael Grant
6a4a4a432b Add support to minimise panes, both tiled and floating. New PREFIX _ key binding to minimise a pane. New functions minimise-pane and unminimise-pane. Add double-click on pane in status to minimise pane. Single click on pane in status unminimises pane. 2025-12-18 18:19:17 +00:00
Michael Grant
263529e886 Cleanup from previous commit. 2025-12-09 20:11:44 +00:00
Michael Grant
818797ff08 Fix scrolling issue that collided with PR #4738 Fix y offset of mouse if status at top. 2025-12-09 17:47:36 +00:00
Michael Grant
d263dbe9dd Merge branch 'tmux:master' into feature-floating-window-panes 2025-12-09 13:35:31 +00:00
Michael Grant
67319ad9d9 Fix tty-clear-area and make aware of floating panes. 2025-12-09 13:34:26 +00:00
Michael Grant
2591df66cc Bugfix status line at top and floating panes. 2025-12-09 00:14:06 +00:00
Michael Grant
2ac78bccb5 Bugfix status line at top. 2025-12-08 15:01:54 +00:00
Michael Grant
47de44001e Merge changes from master. 2025-12-08 14:30:57 +00:00
Michael Grant
eaa467618b 1. Rework floating panes to have a stub layout_cell, 2. Add new <..> format to list-windows & select-layout for floating anes, 3. Fix zooming to work with floating panes, 4. Fix several display issues. 2025-12-08 14:28:17 +00:00
Michael Grant
ac01f15bfe Merge branch 'master' into feature-floating-window-panes 2025-12-06 21:14:38 +00:00
Michael Grant
5df5217e2e Merge branch 'tmux:master' into feature-floating-window-panes 2025-11-28 10:34:17 +00:00
Michael Grant
a6ec2b76c2 Merge branch 'tmux:master' into feature-floating-window-panes 2025-11-19 14:23:05 +00:00
Michael Grant
bc01b300c4 Merge branch 'tmux:master' into feature-floating-window-panes 2025-11-14 21:10:00 +00:00
Michael Grant
c8d4d8cb66 Bugfix fix some uninitialised variable warnings. 2025-11-14 11:02:12 +01:00
Michael Grant
315202b007 Merge branch 'tmux:master' into feature-floating-window-panes 2025-11-14 09:29:06 +00:00
Michael Grant
7194fdca38 Fix many display problems with floating windows up against the window edge and borders of other windows and many off-by-one errors. 2025-11-10 23:07:06 +01:00
Michael Grant
31d0945059 Fix dragging a window to y==0 when pane border status enabled. window_get_active_at() needs to return the pane at the top of the window when called with y==0, otherwise it returns null as if there is no pane at the top line. 2025-11-10 23:04:03 +01:00
Michael Grant
466e79d572 Bugfix for size of pane_border status not properly clipping. 2025-11-07 22:53:42 +01:00
Michael Grant
3bb4f72a4b Add new key binding ctrl-b * to create new floating pane. New panes created at increasing offsets. 2025-11-07 12:25:59 +01:00
Michael Grant
329e9d54ab Add support for clicking on a floating pane border to make it active. Including bugfix to click bottom border of floating panes. 2025-11-07 11:43:41 +01:00
Michael Grant
9c45dd693b Comment cleanup. 2025-11-07 01:24:39 +01:00
Michael Grant
cc671e4495 Fix mouse drag in copy-mode to properly select around floating panes. 2025-11-07 01:24:19 +01:00
Michael Grant
61ca158de1 Fix pane borders, including fixing 2 side-by-side or top-bottom panes with split coulering. 2025-11-06 12:16:45 +01:00
Michael Grant
7e0038c691 Bugfix split pane border colours. 2025-11-04 15:48:23 +01:00
Michael Grant
04792d0656 Bugfix, max margin should be right side of window. 2025-11-03 22:38:35 +01:00
Michael Grant
6c5cc40039 Bugfix, more changes to allow xoff and yoff to be int. 2025-11-03 21:56:15 +01:00
Michael Grant
6b462474f0 Bugfix so floating wp can be moved to top of window. 2025-11-02 11:52:21 +01:00
Michael Grant
9a061a2fee Bugfix floating panes display when off window edge. 2025-11-02 09:56:57 +01:00
Michael Grant
6dd552d689 Fix floating pane redraw bugs. Allow floating panes to be partly out of the window. This required changing xoff and yoff from u_int to int and it required a fair bit of casting for example when xoff is added to sx or comparing px to xoff. It makes sense for px and sx to be u_int since they refers to things which should never be negative. 2025-11-01 21:47:54 +01:00
Michael Grant
34e858ea05 Add support to drag and resize floating window panes. 2025-10-30 21:42:32 +01:00
Michael Grant
39d2839e37 Bugfix visible_ranges calculation, no longer need to inject vr into tty_draw_line. 2025-10-28 12:50:13 +00:00
Michael Grant
7980d00e8c Attempt to take care of case of partially obscured wide characters by floating panes. (Not yet tested!) 2025-10-28 11:01:27 +00:00
Michael Grant
25ce5b4281 Add separate z-index list. Each window has its own z-order list of panes now. 2025-10-28 08:07:32 +00:00
Michael Grant
0cd3ab6299 Fix active border colour if only floating panes in the window. 2025-10-27 23:35:53 +00:00
Michael Grant
28d038fb16 Bugfix calculation error on bottom border when pane border status on and scrollbar enabled. 2025-10-27 22:18:07 +00:00
Michael Grant
10dc308e6b Add checking the redraw of floating panes and the pane border status. 2025-10-27 21:28:31 +00:00
Michael Grant
6344bab6cc Fix typo with visible_ranges struct name. Add support for checking visual ranges to tty_draw_pane. 2025-10-27 19:54:21 +00:00
Michael Grant
984fbacccf Change the visible_ranges array to work more like the overlay_ranges array, except be able to grow. 2025-10-26 14:18:27 +00:00
Michael Grant
cc83ca6628 Add new newp command. 2025-10-26 14:02:57 +00:00
Michael Grant
04fc6ea11b Renamed floating window panes simple new-pane. 2025-10-24 14:24:26 +01:00
Michael Grant
8a9e2fccbd Move floating window stuff to new file: cmd-new-pane.c. 2025-10-24 10:25:18 +01:00
Michael Grant
cc1324e2d9 Cleanup and simplification. Array of visual ranges now just simple array. 2025-10-24 09:53:12 +01:00
Michael Grant
b2226d1608 Add scrollbar width to right border calculation. 2025-10-24 00:06:18 +01:00
Michael Grant
8ce0af3fc9 Fix a fencepost error (not sure why wp->sy+1 isn't the border). 2025-10-23 23:59:49 +01:00
Michael Grant
b315a6c3d1 Add mouse detection on top & left borders of floating panes. 2025-10-23 23:25:48 +01:00
Michael Grant
baf642b7d2 window_redraw_active_switch now brings floating pane to front. Fix compile warning in window_add_pane. 2025-10-23 20:30:32 +01:00
Michael Grant
8db76e9057 Bugfix fix redraw of overlapping floating panes. 2025-10-23 18:05:04 +01:00
Michael Grant
e3b7bf9b31 Add -x, -y, -w, -h args to set the xoff, yoff, sx, and sy of the pane. 2025-10-23 00:12:54 +01:00
Michael Grant
38724f2e86 Return error if you try to split a floating pane. 2025-10-22 23:37:58 +01:00
Michael Grant
65fc09fac9 Hide cursor behind floating panes. 2025-10-22 23:29:21 +01:00
Michael Grant
3197b715b3 Bugfix display of above-below windows with a floating pane. 2025-10-22 22:50:10 +01:00
Michael Grant
26362dfc72 Bugfix display of 2 side-by-side panes with overlapping floating pane. 2025-10-22 22:07:42 +01:00
Michael Grant
275781ce45 Merge branch 'feature-floating-window-panes' of github.com:mgrant0/tmux into feature-floating-window-panes 2025-10-21 09:17:13 +01:00
Michael Grant
72dbbfedce Manage visible ranges when drawing scrollbars. 2025-10-21 09:13:33 +01:00
Michael Grant
a6c6eabb5a Merge branch 'tmux:master' into feature-floating-window-panes 2025-10-21 08:14:38 +01:00
Michael Grant
379e4d976c Fix scrolling, redraw, and borders of floating panes. 2025-10-19 14:11:08 +01:00
Michael Grant
a6514e2656 Merge branch 'feature-floating-window-panes' of github.com:mgrant0/tmux into feature-floating-window-panes 2025-10-16 10:24:40 +01:00
Michael Grant
032af23e18 Merge branch 'tmux:master' into feature-floating-window-panes 2025-10-16 09:35:25 +01:00
Michael Grant
b5f9293014 Possible fixes leak 2025-10-13 08:45:32 +01:00
Michael Grant
a37db55689 Bugfix. 2025-10-12 10:38:47 +01:00
Michael Grant
1b959d0eff Cleanup before reworking collect_scroll. 2025-10-12 05:48:30 +01:00
Michael Grant
ce03f1abea Cleanup - screen_redraw_get_visible_ranges returns a value rather than pass addr of arg. Bugfix to redraw code. 2025-10-11 18:07:22 +01:00
Michael Grant
5faf41b695 Add support in screen-write.c to properly display cmd output when there are floating panes on the screen. 2025-10-10 23:59:47 +01:00
Michael Grant
7634daa834 Add function screen_redraw_get_visual_ranges to figure out what parts of floating panes obscure a target pane being redrawn. 2025-10-06 22:19:25 +01:00
Michael Grant
968f439672 Initial commit. Add new new-floating-window command to create panes without a layout_cell indicating they are floating panes. 2025-10-04 09:15:51 +01:00
293 changed files with 5176 additions and 25234 deletions

View File

@@ -16,10 +16,8 @@ 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@89ae32b08ed1a541efecbab17912962a5e38981c # v6 - uses: dessant/lock-threads@v6
with: with:
github-token: ${{ github.token }} github-token: ${{ github.token }}
issue-inactive-days: '30' issue-inactive-days: '30'

View File

@@ -16,8 +16,7 @@ jobs:
regress: regress:
name: ${{ matrix.name }} name: ${{ matrix.name }}
runs-on: ${{ matrix.runner }} runs-on: ${{ matrix.runner }}
if: github.repository == 'tmux/tmux' timeout-minutes: 45
timeout-minutes: 300
strategy: strategy:
fail-fast: false fail-fast: false
@@ -26,21 +25,16 @@ jobs:
- name: ubuntu-24.04-x64 - name: ubuntu-24.04-x64
runner: ubuntu-24.04 runner: ubuntu-24.04
make: make make: make
configure: --enable-utf8proc --enable-asan # - name: ubuntu-24.04-arm64
#- name: ubuntu-24.04-arm64 # runner: ubuntu-24.04-arm
# runner: ubuntu-24.04-arm # make: make
# make: make # - name: macos-26-arm64
# configure: --enable-utf8proc --enable-asan # runner: macos-26
- name: macos-26-arm64 # make: gmake
runner: macos-26
make: gmake
configure: --enable-utf8proc --enable-asan
steps: steps:
- name: checkout - name: checkout
uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4 uses: actions/checkout@v4
with:
persist-credentials: false
- name: dependencies - name: dependencies
if: runner.os == 'Linux' if: runner.os == 'Linux'
@@ -64,7 +58,6 @@ jobs:
automake \ automake \
bison \ bison \
libevent \ libevent \
jemalloc \
make \ make \
ncurses \ ncurses \
utf8proc \ utf8proc \
@@ -72,19 +65,11 @@ jobs:
- name: build - name: build
run: | run: |
sh autogen.sh sh autogen.sh
./configure ${{ matrix.configure }} ./configure --enable-utf8proc
${{ matrix.make }} -j"$(getconf _NPROCESSORS_ONLN)" ${{ matrix.make }} -j"$(getconf _NPROCESSORS_ONLN)"
- name: test - name: test
run: | run: |
cd regress cd regress
${{ matrix.make }} ${{ matrix.make }}
- name: logs
if: failure()
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
with:
name: regress-logs-${{ matrix.name }}
path: regress/logs/*.log
if-no-files-found: ignore

1
.gitignore vendored
View File

@@ -21,7 +21,6 @@ configure
core core
etc/ etc/
fuzz/*-fuzzer fuzz/*-fuzzer
regress/logs/
tags tags
tmux tmux
tmux.1.* tmux.1.*

179
CHANGES
View File

@@ -1,182 +1,3 @@
CHANGES FROM 3.7c TO 3.8
* Many improvements to floating panes:
- new-pane and split-window can now set the title with -T and border lines
with -B, and new-pane -W waits for the pane command to exit;
- dragging with Ctrl now creates a new floating pane;
- 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.
A modal pane is now used for the editor in buffer mode;
- move-pane and resize-pane can now move and resize floating panes, including
mouse dragging;
- split-window in a floating pane creates a new floating pane that does not
overlap the active pane, if possible;
- break-pane can float a tiled pane, join-pane can tile a floating pane, and
swap-pane works with floating panes;
- choose-tree -h and choose-client -h hide the pane containing the mode, and
-k for choose commands kills the pane when the mode exits, which is useful
for modes in floating panes;
- 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;
- floating panes are unzoomed before creation to avoid a crash.
* Add support for themes and improve default colours:
- tmux now has builtin light and dark colour themes for terminals with 256 or
more colours; a new theme option controls whether tmux detects the terminal
theme, uses terminal colours, or forces light or dark (set to terminal for
the old colours);
- styles can use new theme colour names such as themeblack, themewhite and
themegreen, and colours in styles are expanded as formats;
- the terminal's own theme is reported to panes instead of being guessed from
the background, and inferred more accurately when theme reporting is not
supported (Ayman Bagabas, issue 5343);
- tree-mode-selection-style has been added, tree mode selection styling now
covers the whole line, and built-in modes use improved theme colours.
* Improvements to customize mode:
- hooks and environment variables are now shown;
- e opens the selected value in an editor;
- C toggles showing only changed items;
- array item keys can now be changed.
* Hooks (and control mode notifications) are now handled internally by using
events, each of which may carry keys and values as a payload. The control
mode subscriptions mechanism has been extended to allow general-purpose
"monitors" which check a format every second and trigger a hook when it
changes or becomes true:
- set-hook -B adds a monitor hook using the same subscription syntax as
refresh-client -B, and show-hooks -B lists them. -T runs the hook only when
the format is true;
- wait-for -E waits for hooks, notifications or user events, and set-hook -E
fires an event immediately;
- hooks have additional keys available. wait-for -v can be used to see the
keys sent with a hook event;
- new hooks to cover areas including client create and destroy, pane
activity, pane mode and prompt changes, pane movement and resizing, session
group changes and window creation, close and zoom;
- OSC 133 escape sequences now trigger events: pane-command-started,
pane-command-finished and pane-shell-prompt.
* Add switch-mode, a fast switcher bound by default to Tab for windows and BTab
(S-Tab) for sessions.
* Pane scrollbars now support auto-hide. This replaces the previous modal
behaviour where the pane was resized. With auto-hide, the scrollbar appears
while scrolling or while hovered and disappears after pane-scrollbars-timeout
(Michael Grant).
* Extend the fill-character option so both inside and outside the window can be
changed.
* Change set-option and set-hooks to use formats and add a -F flag to each.
* Add a #{A/count:frames} modifier to show a series of frames as an animation
in the status line (issue 5412 from Fernando Daciuk).
* Change command templates so %% is escaped for single quotes while %1 remains
unquoted, preventing single quotes in session or target names from
terminating quoted command sections (reported by Aliz Hammond).
* display-panes is now a mode rather than an overlay, and can be run inside
another pane. The -b flag has been removed.
* 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).
* Menus now belong to the window, so appear on all clients.
* Dragging over an existing copy mode selection now adjusts it (Michael Grant).
* Rectangle selection in copy mode may extend past the end of the current line
to match vi's virtualedit=block behaviour (Mark Kelly, issue 5227).
* With mode-keys set to vi, scrolling in copy mode now keeps the cursor in the
same position relative to the text (Arseniy Simonov, issue 5216).
* Copy mode no longer exits at the bottom while a selection is in progress
(issue 5349).
* Add style attributes for dimming colours (dim=) and for hyperlinks (link= and
nolink) (issues 4842 and 4280 from Moritz Angermann).
* New format modifiers: O: loops over options, V: loops over environments,
the I modifier reports client terminal information, t/d gives a time
difference in seconds, c/f and c/b emit colours, and the m operator supports
multiple terms and fuzzy matching.
* Add or improve format variables including client_colours,
pane_start_command_list, window_manual_width, window_manual_height,
pane_last_output_time, pane_modal_flag and window_modal_pane.
* Add additional pane sort orders, and a z sort order for choose-tree so
floating panes can be sorted by z-index.
* Add -f filters to kill-pane -a, kill-window -a and kill-session -a (issue
4782).
* Allow server ACLs to use groups as well as users (issue 4917).
* The mouse option now defaults to on.
* Fix send-keys -K so keys are inserted in the correct place in the input
queue, like keys from key bindings (issue 3476).
* 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
already exiting (Ben Maurer, issue 5357).
* Fix grouped sessions sometimes being left as unusable command targets while
they are being killed (Bryce Miller, issue 5180).
* Fix choose mode filtering when more than one pane exists (issue 5326), and
fix an infinite loop in customize mode when a filter does not match.
* Fix regsub when the pattern is anchored at the start (issue 5341).
* Fix redraw issues around synchronized updates, copy mode, tabs, padding,
status lines, menus, popups, wide characters and floating panes. This
includes fixes for flicker with alternate screen applications and scrollbars
(Michael Grant, issue 5351; Noam Stolero, issue 5350; Aung Myo Kyaw, issue
5098).
* Do not crash looking for the next or previous session (issue 5344), or when
no client is available.
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
* Fix so that the end of a synchronized update again triggers a redraw. * Fix so that the end of a synchronized update again triggers a redraw.

View File

@@ -4,7 +4,7 @@ CLEANFILES = tmux.1.mdoc tmux.1.man cmd-parse.c
# Distribution tarball options. # Distribution tarball options.
EXTRA_DIST = \ EXTRA_DIST = \
CHANGES README COPYING example_tmux.conf \ CHANGES README README.ja COPYING example_tmux.conf \
osdep-*.c mdoc2man.awk tmux.1 osdep-*.c mdoc2man.awk tmux.1
dist_EXTRA_tmux_SOURCES = compat/*.[ch] dist_EXTRA_tmux_SOURCES = compat/*.[ch]
@@ -13,8 +13,7 @@ AM_CPPFLAGS += @XOPEN_DEFINES@ \
-DTMUX_VERSION='"@VERSION@"' \ -DTMUX_VERSION='"@VERSION@"' \
-DTMUX_CONF='"$(sysconfdir)/tmux.conf:~/.tmux.conf:$$XDG_CONFIG_HOME/tmux/tmux.conf:~/.config/tmux/tmux.conf"' \ -DTMUX_CONF='"$(sysconfdir)/tmux.conf:~/.tmux.conf:$$XDG_CONFIG_HOME/tmux/tmux.conf:~/.config/tmux/tmux.conf"' \
-DTMUX_LOCK_CMD='"@DEFAULT_LOCK_CMD@"' \ -DTMUX_LOCK_CMD='"@DEFAULT_LOCK_CMD@"' \
-DTMUX_TERM='"@DEFAULT_TERM@"' \ -DTMUX_TERM='"@DEFAULT_TERM@"'
-DTMUX_MOUSE=1
# Additional object files. # Additional object files.
LDADD = $(LIBOBJS) LDADD = $(LIBOBJS)
@@ -43,8 +42,7 @@ AM_CPPFLAGS += -DDEBUG
endif endif
AM_CPPFLAGS += -iquote. AM_CPPFLAGS += -iquote.
if IS_ASAN if IS_ASAN
AM_CPPFLAGS += -DASAN AM_CFLAGS += -fsanitize=address
AM_CFLAGS += -fsanitize=address -fno-omit-frame-pointer
AM_LDFLAGS += -fsanitize=address AM_LDFLAGS += -fsanitize=address
endif endif
endif endif
@@ -85,7 +83,6 @@ endif
# List of sources. # List of sources.
dist_tmux_SOURCES = \ dist_tmux_SOURCES = \
active.c \
alerts.c \ alerts.c \
arguments.c \ arguments.c \
attributes.c \ attributes.c \
@@ -102,8 +99,10 @@ dist_tmux_SOURCES = \
cmd-detach-client.c \ cmd-detach-client.c \
cmd-display-menu.c \ cmd-display-menu.c \
cmd-display-message.c \ cmd-display-message.c \
cmd-display-panes.c \
cmd-find-window.c \ cmd-find-window.c \
cmd-find.c \ cmd-find.c \
cmd-hide-pane.c \
cmd-if-shell.c \ cmd-if-shell.c \
cmd-join-pane.c \ cmd-join-pane.c \
cmd-kill-pane.c \ cmd-kill-pane.c \
@@ -161,16 +160,13 @@ dist_tmux_SOURCES = \
control-notify.c \ control-notify.c \
control.c \ control.c \
environ.c \ environ.c \
events-payload.c \
events.c \
file.c \ file.c \
format-draw.c \
format.c \ format.c \
format-draw.c \
fuzzy.c \ fuzzy.c \
grid-reader.c \ grid-reader.c \
grid-view.c \ grid-view.c \
grid.c \ grid.c \
hooks.c \
hyperlinks.c \ hyperlinks.c \
input-keys.c \ input-keys.c \
input.c \ input.c \
@@ -183,15 +179,15 @@ dist_tmux_SOURCES = \
log.c \ log.c \
menu.c \ menu.c \
mode-tree.c \ mode-tree.c \
monitor.c \
names.c \ names.c \
notify.c \
options-table.c \ options-table.c \
options.c \ options.c \
paste.c \ paste.c \
popup.c \ popup.c \
proc.c \ proc.c \
prompt-history.c \
prompt.c \ prompt.c \
prompt-history.c \
regsub.c \ regsub.c \
resize.c \ resize.c \
screen-redraw.c \ screen-redraw.c \
@@ -206,9 +202,9 @@ dist_tmux_SOURCES = \
spawn.c \ spawn.c \
status.c \ status.c \
style.c \ style.c \
tmux-protocol.h \
tmux.c \ tmux.c \
tmux.h \ tmux.h \
tmux-protocol.h \
tty-acs.c \ tty-acs.c \
tty-draw.c \ tty-draw.c \
tty-features.c \ tty-features.c \
@@ -223,7 +219,6 @@ dist_tmux_SOURCES = \
window-clock.c \ window-clock.c \
window-copy.c \ window-copy.c \
window-customize.c \ window-customize.c \
window-panes.c \
window-switch.c \ window-switch.c \
window-tree.c \ window-tree.c \
window-visible.c \ window-visible.c \

62
README.ja Normal file
View File

@@ -0,0 +1,62 @@
tmuxへようこそ!
tmuxはターミナルマルチプレクサーです。複数のターミナルを一つのスクリーン内に作成し、操作することができます。
バックグラウンドで処理を実行中に一度スクリーンから離れて後から復帰することも可能です。
OpenBSD、FreeBSD、NetBSD、Linux、macOS、Solarisで実行できます。
tmuxはlibevent 2.x.に依存します。 下記からダウンロードしてください。
http://libevent.org
また、ncursesも必要です。こちらからどうぞ。
http://invisible-island.net/ncurses/
tarballでのtmuxのビルドとインストール方法。
$ ./configure && make
$ sudo make install
tmuxはutmp(5)をアップデートするためにutempterを使うことができます。もしインストール済みであればオプション「--enable-utempter」をつけて実行してください。
リポジトリから最新バージョンを手に入れるためには下記を実行。
$ git clone https://github.com/tmux/tmux.git
$ cd tmux
$ sh autogen.sh
$ ./configure && make
(ビルドのためにはlibevent、ncurses libraries、headersに加えて、C compiler、make、autoconf、automake、pkg-configが必要です。)
詳しい情報はhttp://git-scm.comをご覧ください。修正はメール<tmux-users@googlegroups.com>宛、もしくはhttps://github.com/tmux/tmux/issuesにて受け付けています。
tmuxのドキュメントについてはtmux.1マニュアルをご覧ください。こちらのコマンドで参照可能です。
$ nroff -mdoc tmux.1|less
サンプル設定は本リポジトリのexample_tmux.confに
また、bash-completionファイルは下記にあります。
https://github.com/scop/bash-completion/blob/main/completions/tmux
「-v」や「-vv」を指定することでデバッグモードでの起動が可能です。カレントディレクトリにサーバーやクライアントのログファイルが生成されます。
議論やバグレポート用のメーリングリストにはこちらから参加可能です。
https://groups.google.com/forum/#!forum/tmux-users
gitコミットについての連絡先
https://groups.google.com/forum/#!forum/tmux-git
購読は<tmux-users+subscribe@googlegroups.com>までメールをお願いします。
バグレポートや機能追加(特にコードへの貢献)は大歓迎です。こちらにご連絡ください。
tmux-users@googlegroups.com
本ファイル、CHANGES、 FAQ、SYNCINGそしてTODOはISC licenseで保護されています。
その他のファイルのライセンスや著作権については、ファイルの上部に明記されています。
-- Nicholas Marriott <nicholas.marriott@gmail.com>

View File

@@ -1 +0,0 @@
Please report security issues to nicholas.marriott@gmail.com.

173
SYNCING Normal file
View File

@@ -0,0 +1,173 @@
Preamble
========
Tmux portable relies on repositories "tmux" and "tmux-obsd".
Here's a description of them:
* "tmux" is the portable version, the one which contains code for other
operating systems, and autotools, etc., which isn't found or needed in the
OpenBSD base system.
* "tmux-obsd" is the version of tmux in OpenBSD base system which provides
the basis of the portable tmux version.
Note: The "tmux-obsd" repository is actually handled by "git cvsimport"
running at 15 minute intervals, so a commit made to OpenBSD's tmux CVS
repository will take at least that long to appear in this git repository.
(It might take longer, depending on the CVS mirror used to import the
OpenBSD code).
If you've never used git before, git tracks meta-data about the committer
and the author, as part of a commit, hence:
% git config [--global] user.name "Your name"
% git config [--global] user.email "you@yourdomain.com"
Note that, if you already have this in the global ~/.gitconfig option, then
this will be used. Setting this per-repository would involve not using the
"--global" flag above. If you wish to use the same credentials always,
pass the "--global" option, as shown.
This is a one-off operation once the repository has been cloned, assuming
this information has ever been set before.
Cloning repositories
====================
This involves having both tmux and tmux-obsd cloned, as in:
% cd /some/where/useful
% git clone https://github.com/tmux/tmux.git
% git clone https://github.com/ThomasAdam/tmux-obsd.git
Note that you do not need additional checkouts to manage the sync -- an
existing clone of either repositories will suffice. So if you already have
these checkouts existing, skip that.
Adding in git-remotes
=====================
Because the portable "tmux" git repository and the "tmux-obsd"
repository do not inherently share any history between each other, the
history has been faked between them. This "faking of history" is something
which has to be told to git for the purposes of comparing the "tmux" and
"tmux-obsd" repositories for syncing. To do this, we must reference the
clone of the "tmux-obsd" repository from the "tmux" repository, as
shown by the following command:
% cd /path/to/tmux
% git remote add obsd-tmux file:///path/to/tmux-obsd
So that now, the remote "obsd-tmux" can be used to reference branches and
commits from the "tmux-obsd" repository, but from the context of the
portable "tmux" repository, which makes sense because it's the "tmux"
repository which will have the updates applied to them.
Fetching updates
================
To ensure the latest commits from "tmux-obsd" can be found from within
"tmux", we have to ensure the "master" branch from "tmux-obsd" is
up-to-date first, and then reference that update in "tmux", as in:
% cd /path/to/tmux-obsd
% git checkout master
% git pull
Then back in "tmux":
% cd /path/to/tmux
% git fetch obsd-tmux
Creating the necessary branches
===============================
Now that "tmux" can see commits and branches from "tmux-obsd" by way
of the remote name "obsd-tmux", we can now create the master branch from
"tmux-obsd" in the "tmux" repository:
% git checkout -b obsd-master obsd-tmux/master
Adding in the fake history points
=================================
To tie both the "master" branch from "tmux" and the "obsd-master"
branch from "tmux-obsd" together, the fake history points added to the
"tmux" repository need to be added. To do this, we must add an
additional refspec line, as in:
% cd /path/to/tmux
% git config --add remote.origin.fetch '+refs/replace/*:refs/replace/*'
% git fetch origin
Performing the Sync
===================
Make sure the "master" branch is checked out:
% git checkout master
The following will show commits on OpenBSD not yet synched with "tmux":
% git log master..obsd-master
From there, merge the result in, fixing up any conflicts which might arise.
% git merge obsd-master
Then ensure things look correct by BUILDING the result of that sync:
% make clean && ./autogen.sh && ./configure && make
Compare the git merge result with what's on origin/master -- that is, check
which commits you're about to push:
% git log origin/master..master
And if happy:
% git push origin master
Keeping an eye on libutil in OpenBSD
====================================
A lot of the compat/ code in tmux comes from libutil, especially imsg.
Sometimes the API can change, etc., which might cause interesting problems
trying to run the portable version of tmux. It's worth checking
periodically for any changes to libutil in OpenBSD and syncing those files
to compat/ as and when appropriate.
Release tmux for next version
=============================
1. Update and commit README and CHANGES. The former should be checked for
anything outdated and updated with a list of things that might break
upgrades and the latter should mention all the major changes since
the last version.
2. Make sure configure.ac has the new version number.
3. Tag with:
% git tag -a 2.X
Where "2.X" is the next version.
Push the tag out with:
% git push --tags
4. Build the tarball with 'make dist'.
5. Check the tarball. If it's good, go here to select the tag just pushed:
https://github.com/tmux/tmux/tags
Click the "Add release notes", upload the tarball and add a link in the
description field to the CHANGES file.
6. Clone the tmux.github.io repository, and change the RELEASE version in the
Makefile. Commit it, and run 'make' to replace %%RELEASE%%. Push the
result out.
7. Change version back to master in configure.ac.

View File

@@ -1,234 +0,0 @@
# Preamble
tmux portable is maintained from two repositories:
* `tmux` is the portable repository. It contains the portability layer,
autotools build files, regression tests, documentation, and code needed for
platforms outside OpenBSD.
* `tmux-openbsd-cutover` is the OpenBSD tmux import repository. Its `master`
branch is the OpenBSD tmux history after the OpenBSD Git cutover. Portable
merges this branch when importing OpenBSD changes.
To create the commits in the tmux-openbsd-cutover repo, the source repo is the
Github mirror of OpenBSD's src repo:
```text
https://github.com/openbsd/src.git
```
The update automation filters the OpenBSD source tree to `usr.bin/tmux/`,
publishes that filtered history as `openbsd-git` in the cutover repository,
cherry-picks new filtered commits onto cutover `master`, then merges cutover
`master` into portable `master`.
If you've never used git before, configure your identity before committing:
```sh
git config [--global] user.name "Your name"
git config [--global] user.email "you@yourdomain.com"
```
# Repository layout
The usual local layout is:
```sh
cd /some/where/useful
git clone https://github.com/tmux/tmux.git tmux-portable
git clone https://github.com/ThomasAdam/tmux-obsd.git tmux-openbsd-cutover
```
The exact directory names do not matter, but the examples below use:
```text
/path/to/tmux-portable
/path/to/tmux-openbsd-cutover
```
The cutover repository has three important branches:
* `master` is the branch portable consumes. It should contain tmux source
only, not automation files.
* `openbsd-git` is the raw filtered OpenBSD tmux branch generated from
OpenBSD src `master`.
* `automation` is an orphan branch containing the GitHub Actions workflow. It
is separate so that workflow files are not merged into portable.
# Adding the OpenBSD remote to portable
In the portable repository, add the cutover repository as a remote:
```sh
cd /path/to/tmux-portable
git remote add tmux-openbsd /path/to/tmux-openbsd-cutover
git config remote.tmux-openbsd.tagOpt --no-tags
```
If the remote already exists, update it instead:
```sh
git remote set-url tmux-openbsd /path/to/tmux-openbsd-cutover
git config remote.tmux-openbsd.tagOpt --no-tags
```
Fetch the cutover master branch explicitly:
```sh
git fetch --no-tags tmux-openbsd master:refs/remotes/tmux-openbsd/master
```
# Automated syncing
The normal sync is performed by the GitHub Actions workflow in the
`automation` branch of the cutover repository. That workflow:
1. Fetches or clones OpenBSD src using a blobless clone.
2. Filters `usr.bin/tmux/` into a local `tmux-openbsd` branch.
3. Updates `tmux-openbsd-cutover/openbsd-git`.
4. Cherry-picks new `openbsd-git` commits onto cutover `master`.
5. Merges cutover `master` into portable `master`.
6. Pushes the changed repositories.
The workflow deliberately runs from the orphan `automation` branch but checks
out cutover `master` as the branch to update. This keeps `.github/` out of
cutover `master`, so portable does not import workflow files when it merges
OpenBSD changes.
# Manual portable merge
If the workflow fails while merging into portable, do the merge locally and
push the result.
Start from an up-to-date portable master:
```sh
cd /path/to/tmux-portable
git fetch origin
git checkout master
git pull --ff-only origin master
```
Fetch the cutover branch:
```sh
git remote add tmux-openbsd /path/to/tmux-openbsd-cutover 2>/dev/null || true
git config remote.tmux-openbsd.tagOpt --no-tags
git fetch --no-tags tmux-openbsd master:refs/remotes/tmux-openbsd/master
```
Merge it:
```sh
git merge --no-ff --log refs/remotes/tmux-openbsd/master
```
Resolve conflicts by deciding whether portable or OpenBSD owns the file.
Useful commands:
```sh
git checkout --ours path/to/file
git add path/to/file
```
This keeps the portable version of a conflicted file.
```sh
git checkout --theirs path/to/file
git add path/to/file
```
This takes the OpenBSD/cutover version of a conflicted file.
Before committing, inspect the result:
```sh
git status
git diff --check
git diff --cached --stat
```
Then finish and publish:
```sh
git commit
git push origin master
```
If the merge attempt is wrong, abort it:
```sh
git merge --abort
```
After pushing a manually resolved merge, rerun the workflow. It should either
no-op or continue from the now-merged portable state.
# Manual cutover update
Normally this is handled by the workflow. If it must be done manually, update
`openbsd-git` in the cutover repository from a filtered OpenBSD src checkout,
then cherry-pick the new filtered commits onto cutover `master`.
The important range is:
```text
origin/openbsd-git..openbsd-git
```
where `origin/openbsd-git` is the previously published filtered OpenBSD tmux
branch and `openbsd-git` is the newly generated filtered branch.
In the cutover repository:
```sh
cd /path/to/tmux-openbsd-cutover
git checkout master
git cherry-pick origin/openbsd-git..openbsd-git
git push origin master openbsd-git
```
Do not put workflow files on cutover `master`; keep them on the orphan
`automation` branch.
# Keeping an eye on libutil in OpenBSD
A lot of the `compat/` code in tmux comes from OpenBSD libraries, especially
imsg. Sometimes APIs change in OpenBSD in ways that require corresponding
portable changes. It is worth checking periodically for relevant OpenBSD
libutil changes and syncing those files into `compat/` as appropriate.
# Release tmux for next version
1. Update and commit README and CHANGES. The former should be checked for
anything outdated and updated with a list of things that might break
upgrades and the latter should mention all the major changes since the last
version.
2. Make sure `configure.ac` has the new version number.
3. Tag with:
```sh
% git tag -a 2.X
```
Where `2.X` is the next version.
Push the tag out with:
```sh
% git push --tags
```
4. Build the tarball with `make dist`.
5. Check the tarball. If it is good, go here to select the tag just pushed:
```text
https://github.com/tmux/tmux/tags
```
Click "Add release notes", upload the tarball and add a link in the
description field to the CHANGES file.
6. Clone the tmux.github.io repository, and change the RELEASE version in the
Makefile. Commit it, and run `make` to replace `%%RELEASE%%`. Push the
result out.
7. Change version back to master in `configure.ac`.

449
active.c
View File

@@ -1,449 +0,0 @@
/* $OpenBSD$ */
/*
* Copyright (c) 2026 Nicholas Marriott <nicholas.marriott@gmail.com>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include <sys/types.h>
#include <stdlib.h>
#include "tmux.h"
/* Local last-window stack entry. */
struct active_window_entry {
u_int window;
TAILQ_ENTRY(active_window_entry) entry;
};
/* Local last-window stack. */
TAILQ_HEAD(active_window_stack, active_window_entry);
/* Per-client and per-session local active window state. */
struct active_window {
struct client *client;
struct session *session;
u_int window;
struct active_window_stack lastw;
RB_ENTRY(active_window) entry;
};
RB_HEAD(active_windows, active_window);
static int
active_window_cmp(struct active_window *aw1, struct active_window *aw2)
{
uintptr_t c1 = (uintptr_t)aw1->client;
uintptr_t c2 = (uintptr_t)aw2->client;
uintptr_t s1 = (uintptr_t)aw1->session;
uintptr_t s2 = (uintptr_t)aw2->session;
if (c1 < c2)
return (-1);
if (c1 > c2)
return (1);
if (s1 < s2)
return (-1);
if (s1 > s2)
return (1);
return (0);
}
RB_GENERATE_STATIC(active_windows, active_window, entry, active_window_cmp);
static struct active_window *active_get(struct client *, struct session *);
static struct active_window *active_add(struct client *, struct session *,
struct winlink *);
static void active_free(struct active_window *);
static struct winlink *active_resolve(struct active_window *);
static void active_stack_remove(struct active_window_stack *, u_int);
static void active_stack_push(struct active_window_stack *, u_int);
static void active_changed(struct client *, struct session *,
struct winlink *, struct winlink *);
/* All local active window states. */
static struct active_windows active_windows = RB_INITIALIZER(&active_windows);
/* Find local active window state for a client and session. */
static struct active_window *
active_get(struct client *c, struct session *s)
{
struct active_window aw;
if (c == NULL || s == NULL)
return (NULL);
aw.client = c;
aw.session = s;
return (RB_FIND(active_windows, &active_windows, &aw));
}
/* Add local active window state for a client and session. */
static struct active_window *
active_add(struct client *c, struct session *s, struct winlink *wl)
{
struct active_window *aw;
aw = active_get(c, s);
if (aw == NULL) {
aw = xcalloc(1, sizeof *aw);
aw->client = c;
aw->session = s;
TAILQ_INIT(&aw->lastw);
RB_INSERT(active_windows, &active_windows, aw);
}
if (wl != NULL)
aw->window = wl->window->id;
return (aw);
}
/* Free local active window state. */
static void
active_free(struct active_window *aw)
{
struct active_window_entry *awe;
while (!TAILQ_EMPTY(&aw->lastw)) {
awe = TAILQ_FIRST(&aw->lastw);
TAILQ_REMOVE(&aw->lastw, awe, entry);
free(awe);
}
RB_REMOVE(active_windows, &active_windows, aw);
free(aw);
}
/* Resolve local active window state to a valid winlink. */
static struct winlink *
active_resolve(struct active_window *aw)
{
struct winlink *wl, *next;
u_int window;
if (aw == NULL || aw->session->curw == NULL)
return (NULL);
wl = winlink_find_by_window_id(&aw->session->windows, aw->window);
if (wl != NULL)
return (wl);
while (!TAILQ_EMPTY(&aw->lastw)) {
window = TAILQ_FIRST(&aw->lastw)->window;
next = winlink_find_by_window_id(&aw->session->windows, window);
active_stack_remove(&aw->lastw, window);
if (next != NULL) {
aw->window = next->window->id;
return (next);
}
}
aw->window = aw->session->curw->window->id;
return (aw->session->curw);
}
/* Remove a window from a local last-window stack. */
static void
active_stack_remove(struct active_window_stack *stack, u_int window)
{
struct active_window_entry *awe, *awe1;
TAILQ_FOREACH_SAFE(awe, stack, entry, awe1) {
if (awe->window == window) {
TAILQ_REMOVE(stack, awe, entry);
free(awe);
}
}
}
/* Push a window onto a local last-window stack. */
static void
active_stack_push(struct active_window_stack *stack, u_int window)
{
struct active_window_entry *awe;
active_stack_remove(stack, window);
awe = xcalloc(1, sizeof *awe);
awe->window = window;
TAILQ_INSERT_HEAD(stack, awe, entry);
}
/* Notify the server that a client's effective window changed. */
static void
active_changed(struct client *c, struct session *s, struct winlink *wl,
struct winlink *old)
{
struct event_payload *ep;
struct cmd_find_state fs;
if (wl == NULL || wl == old)
return;
if (options_get_number(global_options, "focus-events")) {
if (old != NULL)
window_update_focus(old->window);
window_update_focus(wl->window);
}
winlink_clear_flags(wl);
window_update_activity(wl->window);
tty_update_window_offset(wl->window);
if (c != NULL && c->session == s)
wl->window->latest = c;
ep = event_payload_create();
cmd_find_from_winlink(&fs, wl, 0);
event_payload_set_target(ep, &fs);
event_payload_set_client(ep, "client", c);
event_payload_set_session(ep, "session", s);
event_payload_set_window(ep, "window", wl->window);
event_payload_set_window(ep, "new_window", wl->window);
event_payload_set_int(ep, "window_index", wl->idx);
event_payload_set_int(ep, "new_window_index", wl->idx);
if (old != NULL) {
event_payload_set_window(ep, "old_window", old->window);
event_payload_set_int(ep, "old_window_index", old->idx);
}
events_fire("client-window-changed", ep);
if (c != NULL) {
c->flags |= CLIENT_REDRAWWINDOW|CLIENT_REDRAWSTATUS|
CLIENT_REDRAWBORDERS;
tty_update_client_offset(c);
status_update_cache(s);
}
recalculate_sizes();
}
/* Return if a client has local window selection for a session. */
int
active_is_local_window(struct client *c, struct session *s)
{
return (active_get(c, s) != NULL);
}
/* Make a client use local or shared window selection for a session. */
void
active_set_local_window(struct client *c, struct session *s,
enum active_window_mode mode)
{
struct active_window *aw;
struct winlink *wl, *old;
if (c == NULL || s == NULL || s->curw == NULL)
return;
old = active_get_effective_winlink(c, s);
if (mode == ACTIVE_LOCAL) {
active_add(c, s, old);
if (c != NULL && c->session == s)
c->flags |= CLIENT_REDRAWSTATUS;
return;
}
aw = active_get(c, s);
if (aw != NULL) {
active_free(aw);
wl = active_get_effective_winlink(c, s);
active_changed(c, s, wl, old);
if (c != NULL && c->session == s)
c->flags |= CLIENT_REDRAWSTATUS;
}
}
/* Return the effective winlink for a client and session. */
struct winlink *
active_get_effective_winlink(struct client *c, struct session *s)
{
struct active_window *aw;
if (s == NULL)
return (NULL);
aw = active_get(c, s);
if (aw != NULL)
return (active_resolve(aw));
return (s->curw);
}
/* Return the effective window for a client and session. */
struct window *
active_get_effective_window(struct client *c, struct session *s)
{
struct winlink *wl;
wl = active_get_effective_winlink(c, s);
if (wl == NULL)
return (NULL);
return (wl->window);
}
/* Select a winlink for a client and session. */
int
active_select_window(struct client *c, struct session *s, struct winlink *wl)
{
struct active_window *aw;
struct winlink *old;
int changed;
if (wl == NULL)
return (-1);
aw = active_get(c, s);
if (aw == NULL)
return (session_set_current(s, wl));
old = active_resolve(aw);
if (old == wl)
return (1);
if (old != NULL)
active_stack_push(&aw->lastw, old->window->id);
active_stack_remove(&aw->lastw, wl->window->id);
aw->window = wl->window->id;
changed = (old != wl);
active_changed(c, s, wl, old);
return (changed ? 0 : 1);
}
/* Select a window by index. */
int
active_select_window_index(struct client *c, struct session *s, int idx)
{
struct winlink *wl;
wl = winlink_find_by_index(&s->windows, idx);
return (active_select_window(c, s, wl));
}
/* Select the next window. */
int
active_next_window(struct client *c, struct session *s, int alert)
{
struct winlink *wl;
wl = active_get_effective_winlink(c, s);
if (wl == NULL)
return (-1);
do {
wl = winlink_next(wl);
if (wl == NULL)
wl = RB_MIN(winlinks, &s->windows);
if (!alert || (wl->flags & WINLINK_ALERTFLAGS))
return (active_select_window(c, s, wl));
} while (wl != active_get_effective_winlink(c, s));
return (-1);
}
/* Select the previous window. */
int
active_previous_window(struct client *c, struct session *s, int alert)
{
struct winlink *wl;
wl = active_get_effective_winlink(c, s);
if (wl == NULL)
return (-1);
do {
wl = winlink_previous(wl);
if (wl == NULL)
wl = RB_MAX(winlinks, &s->windows);
if (!alert || (wl->flags & WINLINK_ALERTFLAGS))
return (active_select_window(c, s, wl));
} while (wl != active_get_effective_winlink(c, s));
return (-1);
}
/* Select the last window. */
int
active_last_window(struct client *c, struct session *s)
{
struct active_window *aw;
struct winlink *wl;
u_int window;
aw = active_get(c, s);
if (aw == NULL)
return (session_last(s));
while (!TAILQ_EMPTY(&aw->lastw)) {
window = TAILQ_FIRST(&aw->lastw)->window;
wl = winlink_find_by_window_id(&s->windows, window);
if (wl != NULL)
return (active_select_window(c, s, wl));
active_stack_remove(&aw->lastw, window);
}
return (-1);
}
/* Remove all state for a client. */
void
active_remove_client(struct client *c)
{
struct active_window *aw, *aw1;
RB_FOREACH_SAFE(aw, active_windows, &active_windows, aw1) {
if (aw->client == c)
active_free(aw);
}
}
/* Remove all state for a session. */
void
active_remove_session(struct session *s)
{
struct active_window *aw, *aw1;
RB_FOREACH_SAFE(aw, active_windows, &active_windows, aw1) {
if (aw->session == s)
active_free(aw);
}
}
/* Remove a window from all local state. */
void
active_remove_window(struct window *w)
{
struct active_window *aw;
RB_FOREACH(aw, active_windows, &active_windows) {
active_stack_remove(&aw->lastw, w->id);
if (aw->window == w->id && aw->session->curw != NULL)
aw->window = aw->session->curw->window->id;
}
}
/* Return if this winlink is the effective winlink. */
int
active_is_effective_window(struct client *c, struct session *s,
struct winlink *wl)
{
return (wl != NULL && wl == active_get_effective_winlink(c, s));
}
/* Return if this winlink is the last effective winlink. */
int
active_is_last_window(struct client *c, struct session *s, struct winlink *wl)
{
struct active_window *aw;
struct active_window_entry *awe;
if (wl == NULL)
return (0);
aw = active_get(c, s);
if (aw == NULL)
return (wl == TAILQ_FIRST(&s->lastw));
TAILQ_FOREACH(awe, &aw->lastw, entry) {
if (winlink_find_by_window_id(&s->windows, awe->window) == NULL)
continue;
return (wl->window->id == awe->window);
}
return (0);
}

View File

@@ -1,4 +1,4 @@
/* $OpenBSD: alerts.c,v 1.35 2026/07/10 13:38:45 nicm Exp $ */ /* $OpenBSD$ */
/* /*
* Copyright (c) 2015 Nicholas Marriott <nicholas.marriott@gmail.com> * Copyright (c) 2015 Nicholas Marriott <nicholas.marriott@gmail.com>
@@ -205,7 +205,7 @@ alerts_check_bell(struct window *w)
} }
if (!alerts_action_applies(wl, "bell-action")) if (!alerts_action_applies(wl, "bell-action"))
continue; continue;
events_fire_winlink("alert-bell", wl); notify_winlink("alert-bell", wl);
if (s->flags & SESSION_ALERTED) if (s->flags & SESSION_ALERTED)
continue; continue;
@@ -241,7 +241,7 @@ alerts_check_activity(struct window *w)
} }
if (!alerts_action_applies(wl, "activity-action")) if (!alerts_action_applies(wl, "activity-action"))
continue; continue;
events_fire_winlink("alert-activity", wl); notify_winlink("alert-activity", wl);
if (s->flags & SESSION_ALERTED) if (s->flags & SESSION_ALERTED)
continue; continue;
@@ -277,7 +277,7 @@ alerts_check_silence(struct window *w)
} }
if (!alerts_action_applies(wl, "silence-action")) if (!alerts_action_applies(wl, "silence-action"))
continue; continue;
events_fire_winlink("alert-silence", wl); notify_winlink("alert-silence", wl);
if (s->flags & SESSION_ALERTED) if (s->flags & SESSION_ALERTED)
continue; continue;
@@ -314,7 +314,7 @@ alerts_set_message(struct winlink *wl, const char *type, const char *option)
tty_putcode(&c->tty, TTYC_BEL); tty_putcode(&c->tty, TTYC_BEL);
if (visual == VISUAL_OFF) if (visual == VISUAL_OFF)
continue; continue;
if (active_get_effective_winlink(c, c->session) == wl) { if (c->session->curw == wl) {
status_message_set(c, -1, 1, 0, 0, status_message_set(c, -1, 1, 0, 0,
"%s in current window", type); "%s in current window", type);
} else { } else {

View File

@@ -1,4 +1,4 @@
/* $OpenBSD: arguments.c,v 1.67 2026/08/25 06:04:33 nicm Exp $ */ /* $OpenBSD$ */
/* /*
* Copyright (c) 2010 Nicholas Marriott <nicholas.marriott@gmail.com> * Copyright (c) 2010 Nicholas Marriott <nicholas.marriott@gmail.com>
@@ -762,6 +762,8 @@ 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);
} }
@@ -825,10 +827,8 @@ 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));
} }

View File

@@ -1,4 +1,4 @@
/* $OpenBSD: attributes.c,v 1.13 2026/07/02 08:51:05 nicm Exp $ */ /* $OpenBSD$ */
/* /*
* Copyright (c) 2009 Joshua Elsasser <josh@elsasser.org> * Copyright (c) 2009 Joshua Elsasser <josh@elsasser.org>

19
cfg.c
View File

@@ -1,4 +1,4 @@
/* $OpenBSD: cfg.c,v 1.91 2026/08/03 13:38:42 nicm Exp $ */ /* $OpenBSD$ */
/* /*
* Copyright (c) 2008 Nicholas Marriott <nicholas.marriott@gmail.com> * Copyright (c) 2008 Nicholas Marriott <nicholas.marriott@gmail.com>
@@ -223,15 +223,13 @@ 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_notify_write(c, "%%config-error %s", cause); control_write(c, "%%config-error %s", cfg_causes[i]);
else else
cmdq_print(item, "%s", cause); cmdq_print(item, "%s", cfg_causes[i]);
free(cause); free(cfg_causes[i]);
} }
free(cfg_causes); free(cfg_causes);
@@ -246,16 +244,14 @@ 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++) {
cause = cfg_causes[i]; control_write(c, "%%config-error %s", cfg_causes[i]);
control_notify_write(c, "%%config-error %s", cause); free(cfg_causes[i]);
free(cause);
} }
goto out; goto out;
} }
@@ -272,8 +268,7 @@ cfg_show_causes(struct session *s)
wme = TAILQ_FIRST(&wp->modes); wme = TAILQ_FIRST(&wp->modes);
if (wme == NULL || wme->mode != &window_view_mode) if (wme == NULL || wme->mode != &window_view_mode)
window_pane_set_mode(wp, NULL, &window_view_mode, NULL, NULL, window_pane_set_mode(wp, NULL, &window_view_mode, NULL, NULL);
NULL);
for (i = 0; i < cfg_ncauses; i++) { for (i = 0; i < cfg_ncauses; i++) {
window_copy_add(wp, 0, "%s", cfg_causes[i]); window_copy_add(wp, 0, "%s", cfg_causes[i]);
free(cfg_causes[i]); free(cfg_causes[i]);

View File

@@ -1,4 +1,4 @@
/* $OpenBSD: client.c,v 1.168 2026/08/17 20:14:31 nicm Exp $ */ /* $OpenBSD$ */
/* /*
* Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com> * Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>
@@ -239,8 +239,9 @@ client_main(struct event_base *base, int argc, char **argv, uint64_t flags,
pid_t ppid; pid_t ppid;
enum msgtype msg; enum msgtype msg;
struct termios tio, saved_tio; struct termios tio, saved_tio;
size_t size; size_t size, linesize = 0;
char **caps = NULL, *cause; ssize_t linelen;
char *line = NULL, **caps = NULL, *cause;
u_int ncaps = 0; u_int ncaps = 0;
struct args_value *values; struct args_value *values;
@@ -276,7 +277,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_WRITE_ACK; client_flags = flags;
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. */
@@ -406,6 +407,11 @@ client_main(struct event_base *base, int argc, char **argv, uint64_t flags,
client_exec(client_execshell, client_execcmd); client_exec(client_execshell, client_execcmd);
} }
/* Restore streams to blocking. */
setblocking(STDIN_FILENO, 1);
setblocking(STDOUT_FILENO, 1);
setblocking(STDERR_FILENO, 1);
/* Print the exit message, if any, and exit. */ /* Print the exit message, if any, and exit. */
if (client_attached) { if (client_attached) {
if (client_exitreason != CLIENT_EXIT_NONE) if (client_exitreason != CLIENT_EXIT_NONE)
@@ -420,8 +426,15 @@ client_main(struct event_base *base, int argc, char **argv, uint64_t flags,
else else
printf("%%exit\n"); printf("%%exit\n");
fflush(stdout); fflush(stdout);
if (client_flags & CLIENT_CONTROL_WAITEXIT) if (client_flags & CLIENT_CONTROL_WAITEXIT) {
control_wait_exit(STDIN_FILENO); setvbuf(stdin, NULL, _IOLBF, 0);
for (;;) {
linelen = getline(&line, &linesize, stdin);
if (linelen <= 1)
break;
}
free(line);
}
if (client_flags & CLIENT_CONTROLCONTROL) { if (client_flags & CLIENT_CONTROLCONTROL) {
printf("\033\\"); printf("\033\\");
fflush(stdout); fflush(stdout);
@@ -429,12 +442,6 @@ client_main(struct event_base *base, int argc, char **argv, uint64_t flags,
} }
} else if (client_exitreason != CLIENT_EXIT_NONE) } else if (client_exitreason != CLIENT_EXIT_NONE)
fprintf(stderr, "%s\n", client_exit_message()); fprintf(stderr, "%s\n", client_exit_message());
/* Restore the streams to blocking. */
setblocking(STDIN_FILENO, 1);
setblocking(STDOUT_FILENO, 1);
setblocking(STDERR_FILENO, 1);
return (client_exitval); return (client_exitval);
} }
@@ -712,9 +719,6 @@ 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;
} }
} }
@@ -801,8 +805,5 @@ 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;
} }
} }

View File

@@ -1,4 +1,4 @@
/* $OpenBSD: cmd-attach-session.c,v 1.91 2026/07/10 13:38:45 nicm Exp $ */ /* $OpenBSD$ */
/* /*
* Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com> * Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>
@@ -93,7 +93,7 @@ cmd_attach_session(struct cmdq_item *item, const char *tflag, int dflag,
if (wl != NULL) { if (wl != NULL) {
if (wp != NULL) if (wp != NULL)
window_set_active_pane(wp->window, wp, 1); window_set_active_pane(wp->window, wp, 1);
active_select_window(c, s, wl); session_set_current(s, wl);
if (wp != NULL) if (wp != NULL)
cmd_find_from_winlink_pane(current, wl, wp, 0); cmd_find_from_winlink_pane(current, wl, wp, 0);
else else
@@ -163,7 +163,7 @@ cmd_attach_session(struct cmdq_item *item, const char *tflag, int dflag,
if (~c->flags & CLIENT_CONTROL) if (~c->flags & CLIENT_CONTROL)
proc_send(c->peer, MSG_READY, -1, NULL, 0); proc_send(c->peer, MSG_READY, -1, NULL, 0);
events_fire_client("client-attached", c); notify_client("client-attached", c);
c->flags |= CLIENT_ATTACHED; c->flags |= CLIENT_ATTACHED;
} }

View File

@@ -1,4 +1,4 @@
/* $OpenBSD: cmd-bind-key.c,v 1.47 2025/04/09 07:03:04 nicm Exp $ */ /* $OpenBSD$ */
/* /*
* Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com> * Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>

View File

@@ -1,4 +1,4 @@
/* $OpenBSD: cmd-break-pane.c,v 1.76 2026/08/17 07:04:45 nicm Exp $ */ /* $OpenBSD$ */
/* /*
* Copyright (c) 2009 Nicholas Marriott <nicholas.marriott@gmail.com> * Copyright (c) 2009 Nicholas Marriott <nicholas.marriott@gmail.com>
@@ -51,26 +51,32 @@ cmd_break_pane_float(struct cmdq_item *item, struct args *args,
struct window *w, struct window_pane *wp) struct window *w, struct window_pane *wp)
{ {
struct layout_cell *lc = wp->layout_cell; struct layout_cell *lc = wp->layout_cell;
u_int sx = lc->saved_sx, sy = lc->saved_sy;
int ox = lc->saved_xoff, oy = lc->saved_yoff;
char *cause = NULL; char *cause = NULL;
enum pane_lines lines = window_get_pane_lines(w); enum pane_lines lines = window_get_pane_lines(w);
struct layout_geometry *fg = &lc->fg;
if (window_pane_is_floating(wp)) { if (window_pane_is_floating(wp)) {
cmdq_error(item, "pane is already floating"); cmdq_error(item, "pane is already floating");
return (CMD_RETURN_ERROR); return (CMD_RETURN_ERROR);
} }
if (window_pane_is_hidden(wp)) {
cmdq_error(item, "can't float a hidden pane");
return (CMD_RETURN_ERROR);
}
if (w->flags & WINDOW_ZOOMED) { if (w->flags & WINDOW_ZOOMED) {
cmdq_error(item, "can't float a pane while window is zoomed"); cmdq_error(item, "can't float a pane while window is zoomed");
return (CMD_RETURN_ERROR); return (CMD_RETURN_ERROR);
} }
if (layout_floating_args_parse(item, args, lines, w, fg, &cause) != 0) { if (layout_floating_args_parse(item, args, lines, w, &sx, &sy, &ox, &oy,
&cause) != 0) {
cmdq_error(item, "failed to float pane: %s", cause); cmdq_error(item, "failed to float pane: %s", cause);
free(cause); free(cause);
return (CMD_RETURN_ERROR); return (CMD_RETURN_ERROR);
} }
layout_remove_tile(w, lc); layout_remove_tile(w, lc);
layout_set_size(lc, fg->sx, fg->sy, fg->xoff, fg->yoff); layout_set_size(lc, sx, sy, ox, oy);
lc->flags |= LAYOUT_CELL_FLOATING; lc->flags |= LAYOUT_CELL_FLOATING;
TAILQ_REMOVE(&w->z_index, wp, zentry); TAILQ_REMOVE(&w->z_index, wp, zentry);
@@ -80,7 +86,7 @@ cmd_break_pane_float(struct cmdq_item *item, struct args *args,
window_set_active_pane(w, wp, 1); window_set_active_pane(w, wp, 1);
layout_fix_offsets(w); layout_fix_offsets(w);
layout_fix_panes(w, NULL); layout_fix_panes(w, NULL);
events_fire_window("window-layout-changed", w); notify_window("window-layout-changed", w);
server_redraw_window(w); server_redraw_window(w);
return (CMD_RETURN_NORMAL); return (CMD_RETURN_NORMAL);
@@ -93,25 +99,16 @@ cmd_break_pane_exec(struct cmd *self, struct cmdq_item *item)
struct cmd_find_state *current = cmdq_get_current(item); struct cmd_find_state *current = cmdq_get_current(item);
struct cmd_find_state *target = cmdq_get_target(item); struct cmd_find_state *target = cmdq_get_target(item);
struct cmd_find_state *source = cmdq_get_source(item); struct cmd_find_state *source = cmdq_get_source(item);
struct client *c = cmdq_get_client(item);
struct client *tc = cmdq_get_target_client(item); struct client *tc = cmdq_get_target_client(item);
struct client *ac;
struct winlink *wl = source->wl; struct winlink *wl = source->wl;
struct winlink *current_wl;
struct session *src_s = source->s; struct session *src_s = source->s;
struct session *dst_s = target->s; struct session *dst_s = target->s;
struct window_pane *wp = source->wp; struct window_pane *wp = source->wp;
struct window *w = wl->window, *old_w = w; struct window *w = wl->window;
char *cause, *cp; char *newname, *cause, *cp;
int idx = target->idx, before, old_idx = wl->idx; int idx = target->idx, before;
int detached, local;
const char *template, *name = args_get(args, 'n'); const char *template, *name = args_get(args, 'n');
if (wp == w->modal) {
cmdq_error(item, "pane is modal");
return (CMD_RETURN_ERROR);
}
if (args_has(args, 'W')) if (args_has(args, 'W'))
return (cmd_break_pane_float(item, args, w, wp)); return (cmd_break_pane_float(item, args, w, wp));
@@ -121,18 +118,11 @@ cmd_break_pane_exec(struct cmd *self, struct cmdq_item *item)
} }
before = args_has(args, 'b'); before = args_has(args, 'b');
ac = tc;
if (ac == NULL || ac->session != dst_s)
ac = c;
detached = args_has(args, 'd');
local = (!detached && active_is_local_window(ac, dst_s));
if (args_has(args, 'a') || before) { if (args_has(args, 'a') || before) {
if (target->wl != NULL) if (target->wl != NULL)
idx = winlink_shuffle_up(dst_s, target->wl, before); idx = winlink_shuffle_up(dst_s, target->wl, before);
else { else
current_wl = active_get_effective_winlink(ac, dst_s); idx = winlink_shuffle_up(dst_s, dst_s->curw, before);
idx = winlink_shuffle_up(dst_s, current_wl, before);
}
if (idx == -1) if (idx == -1)
return (CMD_RETURN_ERROR); return (CMD_RETURN_ERROR);
} }
@@ -140,7 +130,7 @@ cmd_break_pane_exec(struct cmd *self, struct cmdq_item *item)
if (window_count_panes(w, 1) == 1) { if (window_count_panes(w, 1) == 1) {
if (server_link_window(src_s, wl, dst_s, idx, 0, if (server_link_window(src_s, wl, dst_s, idx, 0,
!detached && !local, &cause) != 0) { !args_has(args, 'd'), &cause) != 0) {
cmdq_error(item, "%s", cause); cmdq_error(item, "%s", cause);
free(cause); free(cause);
return (CMD_RETURN_ERROR); return (CMD_RETURN_ERROR);
@@ -153,14 +143,6 @@ cmd_break_pane_exec(struct cmd *self, struct cmdq_item *item)
wl = winlink_find_by_window(&dst_s->windows, w); wl = winlink_find_by_window(&dst_s->windows, w);
if (wl == NULL) if (wl == NULL)
return (CMD_RETURN_ERROR); return (CMD_RETURN_ERROR);
if (local) {
active_select_window(ac, dst_s, wl);
if (ac != NULL && ac->session == dst_s)
cmd_find_from_client(current, ac, 0);
if (ac != NULL && ac->session == dst_s)
server_redraw_client(ac);
}
window_fire_pane_moved(wp, old_w, old_idx, w, wl->idx);
goto out; goto out;
} }
if (idx != -1 && winlink_find_by_index(&dst_s->windows, idx) != NULL) { if (idx != -1 && winlink_find_by_index(&dst_s->windows, idx) != NULL) {
@@ -175,7 +157,6 @@ cmd_break_pane_exec(struct cmd *self, struct cmdq_item *item)
layout_close_pane(wp); layout_close_pane(wp);
w = wp->window = window_create(w->sx, w->sy, w->xpixel, w->ypixel); w = wp->window = window_create(w->sx, w->sy, w->xpixel, w->ypixel);
window_add_ref(w, __func__);
options_set_parent(wp->options, w->options); options_set_parent(wp->options, w->options);
wp->flags |= (PANE_STYLECHANGED|PANE_THEMECHANGED); wp->flags |= (PANE_STYLECHANGED|PANE_THEMECHANGED);
TAILQ_INSERT_HEAD(&w->panes, wp, entry); TAILQ_INSERT_HEAD(&w->panes, wp, entry);
@@ -183,37 +164,25 @@ cmd_break_pane_exec(struct cmd *self, struct cmdq_item *item)
w->active = wp; w->active = wp;
w->latest = tc; w->latest = tc;
free(w->name); if (name == NULL) {
if (name == NULL) newname = default_window_name(w);
w->name = default_window_name(w); window_set_name(w, newname, 0);
else { free(newname);
w->name = clean_name(name, 0); } else {
window_set_name(w, name, 0);
options_set_number(w->options, "automatic-rename", 0); options_set_number(w->options, "automatic-rename", 0);
} }
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);
window_remove_ref(w, __func__); if (idx == -1)
events_fire_window("window-created", w); idx = -1 - options_get_number(dst_s->options, "base-index");
window_fire_pane_moved(wp, old_w, old_idx, w, wl->idx); wl = session_attach(dst_s, w, idx, &cause); /* can't fail */
if (!detached) { if (!args_has(args, 'd')) {
if (local) session_select(dst_s, wl->idx);
active_select_window(ac, dst_s, wl); cmd_find_from_session(current, dst_s, 0);
else
session_select(dst_s, wl->idx);
if (local && ac != NULL && ac->session == dst_s)
cmd_find_from_client(current, ac, 0);
else
cmd_find_from_session(current, dst_s, 0);
if (local && ac != NULL && ac->session == dst_s)
server_redraw_client(ac);
} }
server_redraw_session(src_s); server_redraw_session(src_s);

View File

@@ -1,4 +1,4 @@
/* $OpenBSD: cmd-capture-pane.c,v 1.68 2026/07/20 11:16:33 nicm Exp $ */ /* $OpenBSD$ */
/* /*
* Copyright (c) 2009 Jonathan Alvarado <radobobo@users.sourceforge.net> * Copyright (c) 2009 Jonathan Alvarado <radobobo@users.sourceforge.net>
@@ -134,7 +134,6 @@ cmd_capture_pane_grid(struct window_pane *wp, size_t *len)
struct screen *s = &wp->base; struct screen *s = &wp->base;
struct grid *gd = s->grid; struct grid *gd = s->grid;
struct grid_line *gl; struct grid_line *gl;
struct osc133_data *od;
char *buf = xstrdup(""), *line; char *buf = xstrdup(""), *line;
char p[11]; char p[11];
u_int yy, xx, total = gd->hsize + gd->sy; u_int yy, xx, total = gd->hsize + gd->sy;
@@ -150,20 +149,9 @@ cmd_capture_pane_grid(struct window_pane *wp, size_t *len)
snprintf(p, sizeof p, "-"); snprintf(p, sizeof p, "-");
else else
snprintf(p, sizeof p, "%u", yy - gd->hsize); snprintf(p, sizeof p, "%u", yy - gd->hsize);
od = &gl->osc133_data; xasprintf(&line, "\tL %u (%s) flags=%s[%x] %u/%u\n", yy,
if (gl->flags & GRID_LINE_OSC133_FLAGS) { p, grid_line_flags_string(gl->flags), gl->flags,
xasprintf(&line, "\tL %u (%s) flags=%s[%x] " gl->cellused, gl->cellsize);
"%u/%u osc133=%u,%u,%u,%u,%u\n", yy, p,
grid_line_flags_string(gl->flags), gl->flags,
gl->cellused, gl->cellsize, od->prompt_col,
od->cmd_col, od->out_start_col,
od->out_end_col, od->exit_status);
} else {
xasprintf(&line, "\tL %u (%s) flags=%s[%x] "
"%u/%u\n", yy, p,
grid_line_flags_string(gl->flags), gl->flags,
gl->cellused, gl->cellsize);
}
buf = cmd_capture_pane_append(buf, len, line, strlen(line)); buf = cmd_capture_pane_append(buf, len, line, strlen(line));
free(line); free(line);

View File

@@ -1,4 +1,4 @@
/* $OpenBSD: cmd-choose-tree.c,v 1.58 2026/07/14 17:17:17 nicm Exp $ */ /* $OpenBSD$ */
/* /*
* Copyright (c) 2012 Thomas Adam <thomas@xteddy.org> * Copyright (c) 2012 Thomas Adam <thomas@xteddy.org>
@@ -97,21 +97,6 @@ const struct cmd_entry cmd_switch_mode_entry = {
.exec = cmd_choose_tree_exec .exec = cmd_choose_tree_exec
}; };
const struct cmd_entry cmd_display_panes_entry = {
.name = "display-panes",
.alias = "displayp",
.args = { "d:kNs:t:Z", 0, 1, cmd_choose_tree_args_parse },
.usage = "[-kNZ] [-d duration] [-s source-window] "
CMD_TARGET_PANE_USAGE " [template]",
.source = { 's', CMD_FIND_WINDOW, 0 },
.target = { 't', CMD_FIND_PANE, 0 },
.flags = CMD_AFTERHOOK,
.exec = cmd_choose_tree_exec
};
static enum args_parse_type static enum args_parse_type
cmd_choose_tree_args_parse(__unused struct args *args, __unused u_int idx, cmd_choose_tree_args_parse(__unused struct args *args, __unused u_int idx,
__unused char **cause) __unused char **cause)
@@ -146,11 +131,9 @@ cmd_choose_tree_exec(struct cmd *self, struct cmdq_item *item)
mode = &window_customize_mode; mode = &window_customize_mode;
else if (cmd_get_entry(self) == &cmd_switch_mode_entry) else if (cmd_get_entry(self) == &cmd_switch_mode_entry)
mode = &window_switch_mode; mode = &window_switch_mode;
else if (cmd_get_entry(self) == &cmd_display_panes_entry)
mode = &window_panes_mode;
else else
mode = &window_tree_mode; mode = &window_tree_mode;
window_pane_set_mode(wp, NULL, mode, item, target, args); window_pane_set_mode(wp, NULL, mode, target, args);
return (CMD_RETURN_NORMAL); return (CMD_RETURN_NORMAL);
} }

View File

@@ -1,4 +1,4 @@
/* $OpenBSD: cmd-command-prompt.c,v 1.76 2026/08/25 06:04:33 nicm Exp $ */ /* $OpenBSD$ */
/* /*
* Copyright (c) 2008 Nicholas Marriott <nicholas.marriott@gmail.com> * Copyright (c) 2008 Nicholas Marriott <nicholas.marriott@gmail.com>
@@ -242,11 +242,9 @@ 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);

View File

@@ -1,4 +1,4 @@
/* $OpenBSD: cmd-confirm-before.c,v 1.61 2026/06/24 10:55:39 nicm Exp $ */ /* $OpenBSD$ */
/* /*
* Copyright (c) 2009 Tiago Cunha <me@tiagocunha.org> * Copyright (c) 2009 Tiago Cunha <me@tiagocunha.org>

View File

@@ -1,4 +1,4 @@
/* $OpenBSD: cmd-copy-mode.c,v 1.54 2026/07/14 17:17:17 nicm Exp $ */ /* $OpenBSD$ */
/* /*
* Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com> * Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>
@@ -30,8 +30,8 @@ const struct cmd_entry cmd_copy_mode_entry = {
.name = "copy-mode", .name = "copy-mode",
.alias = NULL, .alias = NULL,
.args = { "dekHMqSs:t:u", 0, 0, NULL }, .args = { "deHMqSs:t:u", 0, 0, NULL },
.usage = "[-dekHMqSu] [-s src-pane] " CMD_TARGET_PANE_USAGE, .usage = "[-deHMqSu] [-s src-pane] " CMD_TARGET_PANE_USAGE,
.source = { 's', CMD_FIND_PANE, 0 }, .source = { 's', CMD_FIND_PANE, 0 },
.target = { 't', CMD_FIND_PANE, 0 }, .target = { 't', CMD_FIND_PANE, 0 },
@@ -79,8 +79,7 @@ cmd_copy_mode_exec(struct cmd *self, struct cmdq_item *item)
} }
if (cmd_get_entry(self) == &cmd_clock_mode_entry) { if (cmd_get_entry(self) == &cmd_clock_mode_entry) {
window_pane_set_mode(wp, NULL, &window_clock_mode, item, NULL, window_pane_set_mode(wp, NULL, &window_clock_mode, NULL, NULL);
NULL);
return (CMD_RETURN_NORMAL); return (CMD_RETURN_NORMAL);
} }
@@ -91,8 +90,7 @@ cmd_copy_mode_exec(struct cmd *self, struct cmdq_item *item)
line_numbers = 1; line_numbers = 1;
if (event != NULL && KEYC_IS_MOUSE(event->key)) if (event != NULL && KEYC_IS_MOUSE(event->key))
line_numbers = 0; line_numbers = 0;
if (!window_pane_set_mode(wp, swp, &window_copy_mode, item, NULL, if (!window_pane_set_mode(wp, swp, &window_copy_mode, NULL, args)) {
args)) {
window_copy_set_line_numbers(wp, line_numbers); window_copy_set_line_numbers(wp, line_numbers);
if (args_has(args, 'M')) if (args_has(args, 'M'))
window_copy_start_drag(c, &event->m); window_copy_start_drag(c, &event->m);

View File

@@ -1,4 +1,4 @@
/* $OpenBSD: cmd-detach-client.c,v 1.39 2026/05/22 15:22:43 nicm Exp $ */ /* $OpenBSD$ */
/* /*
* Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com> * Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>

View File

@@ -1,4 +1,4 @@
/* $OpenBSD: cmd-display-menu.c,v 1.53 2026/08/31 07:46:55 nicm Exp $ */ /* $OpenBSD$ */
/* /*
* Copyright (c) 2019 Nicholas Marriott <nicholas.marriott@gmail.com> * Copyright (c) 2019 Nicholas Marriott <nicholas.marriott@gmail.com>
@@ -92,7 +92,7 @@ cmd_display_menu_args_parse(struct args *args, u_int idx, __unused char **cause)
} }
static int static int
cmd_display_menu_get_popup_pos(struct client *tc, struct cmdq_item *item, cmd_display_menu_get_pos(struct client *tc, struct cmdq_item *item,
struct args *args, u_int *px, u_int *py, u_int w, u_int h) struct args *args, u_int *px, u_int *py, u_int w, u_int h)
{ {
struct tty *tty = &tc->tty; struct tty *tty = &tc->tty;
@@ -126,10 +126,6 @@ cmd_display_menu_get_popup_pos(struct client *tc, struct cmdq_item *item,
format_add(ft, "popup_mouse_y", "%u", event->m.y); format_add(ft, "popup_mouse_y", "%u", event->m.y);
} }
/* Position of the previous menu, for -x/-y L. */
format_add(ft, "popup_last_x", "%u", target->w->menu_last_px);
format_add(ft, "popup_last_y", "%u", target->w->menu_last_py + h);
/* /*
* If there are any status lines, add this window position and the * If there are any status lines, add this window position and the
* status line position. * status line position.
@@ -242,8 +238,6 @@ cmd_display_menu_get_popup_pos(struct client *tc, struct cmdq_item *item,
xp = "#{popup_pane_left}"; xp = "#{popup_pane_left}";
else if (strcmp(xp, "M") == 0) else if (strcmp(xp, "M") == 0)
xp = "#{popup_mouse_centre_x}"; xp = "#{popup_mouse_centre_x}";
else if (strcmp(xp, "L") == 0)
xp = "#{popup_last_x}";
else if (strcmp(xp, "W") == 0) else if (strcmp(xp, "W") == 0)
xp = "#{popup_window_status_line_x}"; xp = "#{popup_window_status_line_x}";
p = format_expand(ft, xp); p = format_expand(ft, xp);
@@ -264,8 +258,6 @@ cmd_display_menu_get_popup_pos(struct client *tc, struct cmdq_item *item,
yp = "#{popup_pane_bottom}"; yp = "#{popup_pane_bottom}";
else if (strcmp(yp, "M") == 0) else if (strcmp(yp, "M") == 0)
yp = "#{popup_mouse_top}"; yp = "#{popup_mouse_top}";
else if (strcmp(yp, "L") == 0)
yp = "#{popup_last_y}";
else if (strcmp(yp, "S") == 0) else if (strcmp(yp, "S") == 0)
yp = "#{popup_status_line_y}"; yp = "#{popup_status_line_y}";
else if (strcmp(yp, "W") == 0) else if (strcmp(yp, "W") == 0)
@@ -288,184 +280,6 @@ cmd_display_menu_get_popup_pos(struct client *tc, struct cmdq_item *item,
return (1); return (1);
} }
static int
cmd_display_menu_get_menu_pos(struct client *tc, struct cmdq_item *item,
struct args *args, u_int *px, u_int *py, u_int w, u_int h)
{
struct cmd_find_state *target = cmdq_get_target(item);
struct key_event *event = cmdq_get_event(item);
struct session *s = tc->session;
struct winlink *wl = target->wl;
struct window *window = target->w;
struct window_pane *wp = target->wp;
struct style_ranges *ranges = NULL;
struct style_range *sr = NULL;
const char *xp, *yp;
char *p;
u_int line, ox, oy, sx, sy, lines, position;
long n, max_x, max_y, mouse_x = 0, mouse_y = 0;
struct format_tree *ft;
max_x = window->sx > w ? window->sx - w : 0;
max_y = window->sy > h ? window->sy - h : 0;
tty_window_offset(&tc->tty, &ox, &oy, &sx, &sy);
ft = format_create_from_target(item);
if (event->m.valid) {
mouse_x = event->m.x + ox;
if (event->m.statusat == 0) {
if (event->m.y >= event->m.statuslines)
mouse_y = event->m.y - event->m.statuslines + oy;
else
mouse_y = oy;
} else if (event->m.statusat > 0 &&
event->m.y >= (u_int)event->m.statusat) {
mouse_y = oy + sy - 1;
} else
mouse_y = event->m.y + oy;
format_add(ft, "popup_mouse_x", "%ld", mouse_x);
format_add(ft, "popup_mouse_y", "%ld", mouse_y);
}
format_add(ft, "popup_last_x", "%u", window->menu_last_px);
format_add(ft, "popup_last_y", "%u", window->menu_last_py + h);
lines = status_line_size(tc);
position = options_get_number(s->options, "status-position");
if (status_at_line(tc) != -1 && lines != 0) {
for (line = 0; line < lines; line++) {
ranges = &tc->status.entries[line].ranges;
TAILQ_FOREACH(sr, ranges, entry) {
if (sr->type != STYLE_RANGE_WINDOW)
continue;
if (sr->argument == (u_int)wl->idx)
break;
}
if (sr != NULL)
break;
}
if (sr != NULL) {
format_add(ft, "popup_window_status_line_x", "%u",
sr->start + ox);
if (position == 0) {
format_add(ft, "popup_window_status_line_y",
"%u", h);
} else {
format_add(ft, "popup_window_status_line_y",
"%u", window->sy);
}
}
if (position == 0)
format_add(ft, "popup_status_line_y", "%u", h);
else
format_add(ft, "popup_status_line_y", "%u", window->sy);
}
format_add(ft, "popup_width", "%u", w);
format_add(ft, "popup_height", "%u", h);
n = ((long)window->sx - 1) / 2 - w / 2;
if (n < 0)
format_add(ft, "popup_centre_x", "%u", 0);
else
format_add(ft, "popup_centre_x", "%ld", n);
n = ((long)window->sy - 1) / 2 + h / 2;
if (n >= window->sy)
format_add(ft, "popup_centre_y", "%ld", max_y);
else
format_add(ft, "popup_centre_y", "%ld", n);
if (event->m.valid) {
n = mouse_x - w / 2;
if (n < 0)
format_add(ft, "popup_mouse_centre_x", "%u", 0);
else
format_add(ft, "popup_mouse_centre_x", "%ld", n);
n = mouse_y - h / 2;
if (n + h >= window->sy)
format_add(ft, "popup_mouse_centre_y", "%ld", max_y);
else
format_add(ft, "popup_mouse_centre_y", "%ld", n);
n = mouse_y + h;
if (n >= window->sy)
format_add(ft, "popup_mouse_top", "%u", window->sy - 1);
else
format_add(ft, "popup_mouse_top", "%ld", n);
n = mouse_y - h;
if (n < 0)
format_add(ft, "popup_mouse_bottom", "%u", 0);
else
format_add(ft, "popup_mouse_bottom", "%ld", n);
}
n = wp->yoff + h;
if (n >= window->sy)
format_add(ft, "popup_pane_top", "%ld", max_y);
else
format_add(ft, "popup_pane_top", "%ld", n);
format_add(ft, "popup_pane_bottom", "%u", wp->yoff + wp->sy);
format_add(ft, "popup_pane_left", "%u", wp->xoff);
n = (long)wp->xoff + wp->sx - w;
if (n < 0)
format_add(ft, "popup_pane_right", "%u", 0);
else
format_add(ft, "popup_pane_right", "%ld", n);
xp = args_get(args, 'x');
if (xp == NULL || strcmp(xp, "C") == 0)
xp = "#{popup_centre_x}";
else if (strcmp(xp, "R") == 0)
xp = "#{popup_pane_right}";
else if (strcmp(xp, "P") == 0)
xp = "#{popup_pane_left}";
else if (strcmp(xp, "M") == 0)
xp = "#{popup_mouse_centre_x}";
else if (strcmp(xp, "L") == 0)
xp = "#{popup_last_x}";
else if (strcmp(xp, "W") == 0)
xp = "#{popup_window_status_line_x}";
p = format_expand(ft, xp);
n = strtol(p, NULL, 10);
if (n < 0)
n = 0;
if (n > max_x)
n = max_x;
*px = n;
log_debug("%s: -x: %s = %s = %u (-w %u)", __func__, xp, p, *px, w);
free(p);
yp = args_get(args, 'y');
if (yp == NULL || strcmp(yp, "C") == 0)
yp = "#{popup_centre_y}";
else if (strcmp(yp, "P") == 0)
yp = "#{popup_pane_bottom}";
else if (strcmp(yp, "M") == 0)
yp = "#{popup_mouse_top}";
else if (strcmp(yp, "L") == 0)
yp = "#{popup_last_y}";
else if (strcmp(yp, "S") == 0)
yp = "#{popup_status_line_y}";
else if (strcmp(yp, "W") == 0)
yp = "#{popup_window_status_line_y}";
p = format_expand(ft, yp);
n = strtol(p, NULL, 10);
if (n < h)
n = 0;
else
n -= h;
if (n < 0)
n = 0;
if (n > max_y)
n = max_y;
*py = n;
log_debug("%s: -y: %s = %s = %u (-h %u)", __func__, yp, p, *py, h);
free(p);
format_free(ft);
return (1);
}
static enum cmd_retval static enum cmd_retval
cmd_display_menu_exec(struct cmd *self, struct cmdq_item *item) cmd_display_menu_exec(struct cmd *self, struct cmdq_item *item)
{ {
@@ -483,9 +297,13 @@ cmd_display_menu_exec(struct cmd *self, struct cmdq_item *item)
char *title, *cause = NULL; char *title, *cause = NULL;
int flags = 0, starting_choice = 0; int flags = 0, starting_choice = 0;
u_int px, py, i, count = args_count(args); u_int px, py, i, count = args_count(args);
struct options *o = target->w->options; struct options *o = target->s->curw->window->options;
struct options_entry *oe; struct options_entry *oe;
if (tc->overlay_draw != NULL)
return (CMD_RETURN_NORMAL);
if (args_has(args, 'C')) { if (args_has(args, 'C')) {
if (strcmp(args_get(args, 'C'), "-") == 0) if (strcmp(args_get(args, 'C'), "-") == 0)
starting_choice = -1; starting_choice = -1;
@@ -531,8 +349,8 @@ cmd_display_menu_exec(struct cmd *self, struct cmdq_item *item)
} }
if (menu->count == 0) if (menu->count == 0)
goto out; goto out;
if (!cmd_display_menu_get_menu_pos(tc, item, args, &px, &py, if (!cmd_display_menu_get_pos(tc, item, args, &px, &py, menu->width + 4,
menu->width + 4, menu->count + 2)) menu->count + 2))
goto out; goto out;
value = args_get(args, 'b'); value = args_get(args, 'b');
@@ -553,7 +371,7 @@ cmd_display_menu_exec(struct cmd *self, struct cmdq_item *item)
if (menu_display(menu, flags, starting_choice, item, px, py, tc, lines, if (menu_display(menu, flags, starting_choice, item, px, py, tc, lines,
style, selected_style, border_style, target, NULL, NULL) != 0) style, selected_style, border_style, target, NULL, NULL) != 0)
goto out; goto out;
return (CMD_RETURN_NORMAL); return (CMD_RETURN_WAIT);
out: out:
menu_free(menu); menu_free(menu);
@@ -584,15 +402,13 @@ cmd_display_popup_exec(struct cmd *self, struct cmdq_item *item)
u_int px, py, w, h, count = args_count(args); u_int px, py, w, h, count = args_count(args);
struct args_value *av; struct args_value *av;
struct environ *env = NULL; struct environ *env = NULL;
struct options *o = target->w->options; struct options *o = s->curw->window->options;
struct options_entry *oe; struct options_entry *oe;
if (args_has(args, 'C')) { if (args_has(args, 'C')) {
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);
@@ -621,8 +437,7 @@ cmd_display_popup_exec(struct cmd *self, struct cmdq_item *item)
w = tty->sx; w = tty->sx;
if (h > tty->sy) if (h > tty->sy)
h = tty->sy; h = tty->sy;
if (!cmd_display_menu_get_popup_pos(tc, item, args, &px, &py, if (!cmd_display_menu_get_pos(tc, item, args, &px, &py, w, h))
w, h))
goto out; goto out;
value = args_get(args, 'd'); value = args_get(args, 'd');

View File

@@ -1,4 +1,4 @@
/* $OpenBSD: cmd-display-message.c,v 1.65 2026/02/23 08:46:57 nicm Exp $ */ /* $OpenBSD$ */
/* /*
* Copyright (c) 2009 Tiago Cunha <me@tiagocunha.org> * Copyright (c) 2009 Tiago Cunha <me@tiagocunha.org>

393
cmd-display-panes.c Normal file
View File

@@ -0,0 +1,393 @@
/* $OpenBSD$ */
/*
* Copyright (c) 2009 Nicholas Marriott <nicholas.marriott@gmail.com>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER
* IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
* OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include <sys/types.h>
#include <stdlib.h>
#include <string.h>
#include "tmux.h"
/*
* Display panes on a client.
*/
static enum args_parse_type cmd_display_panes_args_parse(struct args *,
u_int, char **);
static enum cmd_retval cmd_display_panes_exec(struct cmd *,
struct cmdq_item *);
const struct cmd_entry cmd_display_panes_entry = {
.name = "display-panes",
.alias = "displayp",
.args = { "bd:Nt:", 0, 1, cmd_display_panes_args_parse },
.usage = "[-bN] [-d duration] " CMD_TARGET_CLIENT_USAGE " [template]",
.flags = CMD_AFTERHOOK|CMD_CLIENT_TFLAG,
.exec = cmd_display_panes_exec
};
struct cmd_display_panes_data {
struct cmdq_item *item;
struct args_command_state *state;
};
struct cmd_display_panes_ctx {
struct client *c;
int ox;
int oy;
u_int sx;
u_int sy;
u_int statuslines;
int statustop;
};
static enum args_parse_type
cmd_display_panes_args_parse(__unused struct args *args, __unused u_int idx,
__unused char **cause)
{
return (ARGS_PARSE_COMMANDS_OR_STRING);
}
static void
cmd_display_panes_put(struct cmd_display_panes_ctx *ctx,
struct window_pane *wp, u_int cx, u_int cy, const char *buf, size_t len)
{
struct client *c = ctx->c;
struct tty *tty = &c->tty;
struct visible_ranges *r;
struct visible_range *ri;
u_int i, j;
r = window_visible_ranges(wp, ctx->ox + cx, ctx->oy + cy, len, NULL);
for (i = 0; i < r->used; i++) {
ri = &r->ranges[i];
for (j = ri->px; j < ri->px + ri->nx; j++) {
tty_cursor(tty, j - ctx->ox, cy);
tty_putn(tty, &buf[j - ri->px], 1, 1);
}
}
}
static void
cmd_display_panes_draw_format(struct cmd_display_panes_ctx *ctx,
struct window_pane *wp, u_int xoff, u_int yoff, u_int sx,
const struct grid_cell *gc)
{
struct client *c = ctx->c;
struct tty *tty = &c->tty;
struct session *s = c->session;
struct screen *sc = wp->screen, screen;
struct screen_write_ctx sctx;
struct visible_ranges *r;
struct visible_range *ri;
const char *format;
char *expanded;
u_int i, px = ctx->ox + xoff;
format = options_get_string(s->options, "display-panes-format");
expanded = format_single(NULL, format, c, s, s->curw, wp);
screen_init(&screen, sx, 1, 0);
screen_write_start(&sctx, &screen);
screen_write_fast_copy(&sctx, sc, 0, sc->grid->hsize, sx, 1);
screen_write_cursormove(&sctx, 0, 0, 0);
format_draw(&sctx, gc, sx, expanded, NULL, 0);
screen_write_stop(&sctx);
free(expanded);
r = window_visible_ranges(wp, px, wp->yoff, sx, NULL);
for (i = 0; i < r->used; i++) {
ri = &r->ranges[i];
tty_draw_line(tty, &screen, ri->px - px, 0, ri->nx,
ri->px - ctx->ox, yoff, NULL);
}
screen_free(&screen);
}
static void
cmd_display_panes_draw_pane(struct cmd_display_panes_ctx *ctx,
struct window_pane *wp)
{
struct client *c = ctx->c;
struct tty *tty = &c->tty;
struct session *s = c->session;
struct options *oo = s->options;
struct window *w = wp->window;
struct grid_cell fgc, bgc;
u_int pane, idx, px, py, i, j, xoff, yoff, sx, sy;
u_int cx, cy;
const char *name;
struct format_tree *ft;
char buf[16], lbuf[16], *ptr;
size_t len, llen;
if (wp->xoff + (int)wp->sx <= ctx->ox ||
wp->xoff >= ctx->ox + (int)ctx->sx ||
wp->yoff + (int)wp->sy <= ctx->oy ||
wp->yoff >= ctx->oy + (int)ctx->sy)
return;
if (wp->xoff >= ctx->ox && wp->xoff + wp->sx <= ctx->ox + ctx->sx) {
/* All visible. */
xoff = wp->xoff - ctx->ox;
sx = wp->sx;
} else if (wp->xoff < ctx->ox &&
wp->xoff + wp->sx > ctx->ox + ctx->sx) {
/* Both left and right not visible. */
xoff = 0;
sx = ctx->sx;
} else if (wp->xoff < ctx->ox) {
/* Left not visible. */
xoff = 0;
sx = wp->sx - (ctx->ox - wp->xoff);
} else {
/* Right not visible. */
xoff = wp->xoff - ctx->ox;
sx = wp->sx - xoff;
}
if (wp->yoff >= ctx->oy && wp->yoff + wp->sy <= ctx->oy + ctx->sy) {
/* All visible. */
yoff = wp->yoff - ctx->oy;
sy = wp->sy;
} else if (wp->yoff < ctx->oy &&
wp->yoff + wp->sy > ctx->oy + ctx->sy) {
/* Both top and bottom not visible. */
yoff = 0;
sy = ctx->sy;
} else if (wp->yoff < ctx->oy) {
/* Top not visible. */
yoff = 0;
sy = wp->sy - (ctx->oy - wp->yoff);
} else {
/* Bottom not visible. */
yoff = wp->yoff - ctx->oy;
sy = wp->sy - yoff;
}
if (ctx->statustop)
yoff += ctx->statuslines;
px = sx / 2;
py = sy / 2;
if (window_pane_index(wp, &pane) != 0)
fatalx("index not found");
len = xsnprintf(buf, sizeof buf, "%u", pane);
if (sx < len)
return;
if (w->active == wp)
name = "display-panes-active-colour";
else
name = "display-panes-colour";
ft = format_create_defaults(NULL, c, s, NULL, wp);
style_apply(&fgc, oo, name, ft);
format_free(ft);
memcpy(&bgc, &grid_default_cell, sizeof bgc);
bgc.bg = fgc.fg;
if (pane > 9 && pane < 35)
llen = xsnprintf(lbuf, sizeof lbuf, "%c", 'a' + (pane - 10));
else
llen = 0;
if (sx < len * 6 || sy < 5) {
tty_attributes(tty, &fgc, NULL);
if (sx >= len + llen + 1) {
len += llen + 1;
cx = xoff + px - len / 2;
cy = yoff + py;
cmd_display_panes_put(ctx, wp, cx, cy, buf, len);
cx += len;
cmd_display_panes_put(ctx, wp, cx, cy, " ", 1);
cx++;
cmd_display_panes_put(ctx, wp, cx, cy, lbuf, llen);
} else {
cx = xoff + px - len / 2;
cy = yoff + py;
cmd_display_panes_put(ctx, wp, cx, cy, buf, len);
}
goto out;
}
px -= len * 3;
py -= 2;
tty_attributes(tty, &bgc, NULL);
for (ptr = buf; *ptr != '\0'; ptr++) {
if (*ptr < '0' || *ptr > '9')
continue;
idx = *ptr - '0';
for (j = 0; j < 5; j++) {
for (i = px; i < px + 5; i++) {
if (!window_clock_table[idx][j][i - px])
continue;
cx = xoff + i;
cy = yoff + py + j;
cmd_display_panes_put(ctx, wp, cx, cy, " ", 1);
}
}
px += 6;
}
if (sy <= 6)
goto out;
cmd_display_panes_draw_format(ctx, wp, xoff, yoff, sx, &fgc);
if (llen != 0) {
tty_attributes(tty, &fgc, NULL);
cx = xoff + sx / 2 + len * 3 - llen - 1;
cy = yoff + py + 5;
cmd_display_panes_put(ctx, wp, cx, cy, lbuf, llen);
}
out:
tty_cursor(tty, 0, 0);
}
static void
cmd_display_panes_draw(struct client *c, __unused void *data)
{
struct session *s = c->session;
struct window *w = s->curw->window;
struct window_pane *wp;
struct cmd_display_panes_ctx ctx;
u_int lines;
log_debug("%s: %s @%u", __func__, c->name, w->id);
memset(&ctx, 0, sizeof ctx);
ctx.c = c;
tty_window_offset(&c->tty, &ctx.ox, &ctx.oy, &ctx.sx, &ctx.sy);
if (options_get_number(s->options, "status-position") == 0) {
lines = status_line_size(c);
if (c->message_string != NULL || c->prompt != NULL)
lines = (lines == 0 ? 1 : lines);
ctx.statuslines = lines;
ctx.statustop = 1;
}
TAILQ_FOREACH(wp, &w->panes, entry) {
if (window_pane_is_visible(wp))
cmd_display_panes_draw_pane(&ctx, wp);
}
}
static void
cmd_display_panes_free(__unused struct client *c, void *data)
{
struct cmd_display_panes_data *cdata = data;
if (cdata->item != NULL)
cmdq_continue(cdata->item);
args_make_commands_free(cdata->state);
free(cdata);
}
static int
cmd_display_panes_key(struct client *c, void *data, struct key_event *event)
{
struct cmd_display_panes_data *cdata = data;
char *expanded, *error;
struct cmdq_item *item = cdata->item, *new_item;
struct cmd_list *cmdlist;
struct window *w = c->session->curw->window;
struct window_pane *wp;
u_int index;
key_code key;
if (event->key >= '0' && event->key <= '9')
index = event->key - '0';
else if ((event->key & KEYC_MASK_MODIFIERS) == 0) {
key = (event->key & KEYC_MASK_KEY);
if (key >= 'a' && key <= 'z')
index = 10 + (key - 'a');
else
return (-1);
} else
return (-1);
wp = window_pane_at_index(w, index);
if (wp == NULL)
return (1);
window_unzoom(w, 1);
xasprintf(&expanded, "%%%u", wp->id);
cmdlist = args_make_commands(cdata->state, 1, &expanded, &error);
if (cmdlist == NULL) {
cmdq_append(c, cmdq_get_error(error));
free(error);
} else if (item == NULL) {
new_item = cmdq_get_command(cmdlist, NULL);
cmdq_append(c, new_item);
} else {
new_item = cmdq_get_command(cmdlist, cmdq_get_state(item));
cmdq_insert_after(item, new_item);
}
free(expanded);
return (1);
}
static enum cmd_retval
cmd_display_panes_exec(struct cmd *self, struct cmdq_item *item)
{
struct args *args = cmd_get_args(self);
struct client *tc = cmdq_get_target_client(item);
struct session *s = tc->session;
u_int delay;
char *cause;
struct cmd_display_panes_data *cdata;
int wait = !args_has(args, 'b');
if (tc->overlay_draw != NULL)
return (CMD_RETURN_NORMAL);
if (args_has(args, 'd')) {
delay = args_strtonum(args, 'd', 0, UINT_MAX, &cause);
if (cause != NULL) {
cmdq_error(item, "delay %s", cause);
free(cause);
return (CMD_RETURN_ERROR);
}
} else
delay = options_get_number(s->options, "display-panes-time");
cdata = xcalloc(1, sizeof *cdata);
if (wait)
cdata->item = item;
cdata->state = args_make_commands_prepare(self, item, 0,
"select-pane -t \"%%%\"", wait, 0);
if (args_has(args, 'N')) {
server_client_set_overlay(tc, delay, NULL, NULL,
cmd_display_panes_draw, NULL, cmd_display_panes_free, NULL,
cdata);
} else {
server_client_set_overlay(tc, delay, NULL, NULL,
cmd_display_panes_draw, cmd_display_panes_key,
cmd_display_panes_free, NULL, cdata);
}
if (!wait)
return (CMD_RETURN_NORMAL);
return (CMD_RETURN_WAIT);
}

View File

@@ -1,4 +1,4 @@
/* $OpenBSD: cmd-find-window.c,v 1.57 2026/07/14 17:17:17 nicm Exp $ */ /* $OpenBSD$ */
/* /*
* Copyright (c) 2009 Nicholas Marriott <nicholas.marriott@gmail.com> * Copyright (c) 2009 Nicholas Marriott <nicholas.marriott@gmail.com>
@@ -109,8 +109,7 @@ cmd_find_window_exec(struct cmd *self, struct cmdq_item *item)
args_set(new_args, 'Z', NULL, 0); args_set(new_args, 'Z', NULL, 0);
args_set(new_args, 'f', filter, 0); args_set(new_args, 'f', filter, 0);
window_pane_set_mode(wp, NULL, &window_tree_mode, item, target, window_pane_set_mode(wp, NULL, &window_tree_mode, target, new_args);
new_args);
args_free(new_args); args_free(new_args);
return (CMD_RETURN_NORMAL); return (CMD_RETURN_NORMAL);

View File

@@ -1,4 +1,4 @@
/* $OpenBSD: cmd-find.c,v 1.87 2026/07/17 12:42:51 nicm Exp $ */ /* $OpenBSD$ */
/* /*
* Copyright (c) 2015 Nicholas Marriott <nicholas.marriott@gmail.com> * Copyright (c) 2015 Nicholas Marriott <nicholas.marriott@gmail.com>
@@ -884,13 +884,13 @@ cmd_find_from_client(struct cmd_find_state *fs, struct client *c, int flags)
if (c->session != NULL) { if (c->session != NULL) {
cmd_find_clear_state(fs, flags); cmd_find_clear_state(fs, flags);
fs->wl = active_get_effective_winlink(c, c->session); fs->wp = server_client_get_pane(c);
fs->wp = fs->wl->window->active;
if (fs->wp == NULL) { if (fs->wp == NULL) {
cmd_find_from_session(fs, c->session, flags); cmd_find_from_session(fs, c->session, flags);
return (0); return (0);
} }
fs->s = c->session; fs->s = c->session;
fs->wl = fs->s->curw;
fs->w = fs->wl->window; fs->w = fs->wl->window;
cmd_find_log_state(__func__, fs); cmd_find_log_state(__func__, fs);
@@ -931,57 +931,6 @@ unknown_pane:
return (cmd_find_from_nothing(fs, flags)); return (cmd_find_from_nothing(fs, flags));
} }
/* Find state from mouse target. */
static int
cmd_find_target_from_mouse(struct cmd_find_state *fs, struct cmdq_item *item,
enum cmd_find_type type)
{
struct mouse_event *m = &cmdq_get_event(item)->m;
struct client *c = cmdq_get_client(item);
struct client *tc = cmdq_get_target_client(item);
switch (type) {
case CMD_FIND_PANE:
if (m->s != -1 && m->w == -1 && m->wp == -1 &&
(fs->s = session_find_by_id(m->s)) != NULL) {
if (tc != NULL && tc->session == fs->s)
fs->wl = active_get_effective_winlink(tc, fs->s);
else if (c != NULL && c->session == fs->s)
fs->wl = active_get_effective_winlink(c, fs->s);
else
fs->wl = fs->s->curw;
if (fs->wl != NULL) {
fs->w = fs->wl->window;
fs->wp = fs->w->active;
break;
}
}
fs->wp = cmd_mouse_pane(m, &fs->s, &fs->wl);
if (fs->wp != NULL) {
fs->w = fs->wl->window;
break;
}
/* FALLTHROUGH */
case CMD_FIND_WINDOW:
case CMD_FIND_SESSION:
fs->wl = cmd_mouse_window(m, &fs->s);
if (fs->s != NULL && m->w == -1 &&
tc != NULL && tc->session == fs->s)
fs->wl = active_get_effective_winlink(tc, fs->s);
else if (fs->s != NULL && m->w == -1 &&
c != NULL && c->session == fs->s)
fs->wl = active_get_effective_winlink(c, fs->s);
if (fs->wl == NULL && fs->s != NULL)
fs->wl = fs->s->curw;
if (fs->wl != NULL) {
fs->w = fs->wl->window;
fs->wp = fs->w->active;
}
break;
}
return (fs->wp == NULL ? -1 : 0);
}
/* /*
* Split target into pieces and resolve for the given type. Fills in the given * Split target into pieces and resolve for the given type. Fills in the given
* state. Returns 0 on success or -1 on error. * state. Returns 0 on success or -1 on error.
@@ -990,6 +939,7 @@ int
cmd_find_target(struct cmd_find_state *fs, struct cmdq_item *item, cmd_find_target(struct cmd_find_state *fs, struct cmdq_item *item,
const char *target, enum cmd_find_type type, int flags) const char *target, enum cmd_find_type type, int flags)
{ {
struct mouse_event *m;
struct client *c; struct client *c;
struct cmd_find_state current; struct cmd_find_state current;
char *colon, *period, *copy = NULL, tmp[256]; char *colon, *period, *copy = NULL, tmp[256];
@@ -1044,15 +994,20 @@ cmd_find_target(struct cmd_find_state *fs, struct cmdq_item *item,
} else if (cmd_find_from_client(&current, cmdq_get_client(item), } else if (cmd_find_from_client(&current, cmdq_get_client(item),
flags) == 0) { flags) == 0) {
fs->current = &current; fs->current = &current;
/* No active pane, window empty, return the window instead. */
if (current.wp == NULL) {
type = CMD_FIND_WINDOW;
}
log_debug("%s: current is from client", __func__); log_debug("%s: current is from client", __func__);
} else { } else {
if (~flags & CMD_FIND_QUIET) if (~flags & CMD_FIND_QUIET)
cmdq_error(item, "no current target"); cmdq_error(item, "no current target");
goto error; goto error;
} }
/*
if (!cmd_find_valid_state(fs->current)) if (!cmd_find_valid_state(fs->current))
fatalx("invalid current find state"); fatalx("invalid current find state");
*/
/* An empty or NULL target is the current. */ /* An empty or NULL target is the current. */
if (target == NULL || *target == '\0') if (target == NULL || *target == '\0')
goto current; goto current;
@@ -1065,15 +1020,35 @@ cmd_find_target(struct cmd_find_state *fs, struct cmdq_item *item,
cmdq_error(item, "no current client"); cmdq_error(item, "no current client");
goto error; goto error;
} }
fs->wl = active_get_effective_winlink(c, c->session); fs->wl = c->session->curw;
fs->wp = fs->wl->window->active; fs->wp = c->session->curw->window->active;
fs->w = fs->wl->window; fs->w = c->session->curw->window;
goto found; goto found;
} }
/* Mouse target is a plain = or {mouse}. */ /* Mouse target is a plain = or {mouse}. */
if (strcmp(target, "=") == 0 || strcmp(target, "{mouse}") == 0) { if (strcmp(target, "=") == 0 || strcmp(target, "{mouse}") == 0) {
if (cmd_find_target_from_mouse(fs, item, type) != 0) { m = &cmdq_get_event(item)->m;
switch (type) {
case CMD_FIND_PANE:
fs->wp = cmd_mouse_pane(m, &fs->s, &fs->wl);
if (fs->wp != NULL) {
fs->w = fs->wl->window;
break;
}
/* FALLTHROUGH */
case CMD_FIND_WINDOW:
case CMD_FIND_SESSION:
fs->wl = cmd_mouse_window(m, &fs->s);
if (fs->wl == NULL && fs->s != NULL)
fs->wl = fs->s->curw;
if (fs->wl != NULL) {
fs->w = fs->wl->window;
fs->wp = fs->w->active;
}
goto found;
}
if (fs->wp == NULL) {
if (~flags & CMD_FIND_QUIET) if (~flags & CMD_FIND_QUIET)
cmdq_error(item, "no mouse target"); cmdq_error(item, "no mouse target");
goto error; goto error;

213
cmd-hide-pane.c Normal file
View File

@@ -0,0 +1,213 @@
/* $OpenBSD$ */
/*
* Copyright (c) 2026 Michael Grant <mgrant@gmail.com>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER
* IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
* OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include <sys/types.h>
#include <stdlib.h>
#include <string.h>
#include "tmux.h"
/*
* Hide or show panes.
*/
static enum cmd_retval cmd_hide_pane_exec(struct cmd *, struct cmdq_item *);
static enum cmd_retval cmd_show_pane_exec(struct cmd *, struct cmdq_item *);
const struct cmd_entry cmd_hide_pane_entry = {
.name = "hide-pane",
.alias = "hidep",
.args = { "at:", 0, 1, NULL },
.usage = "[-a] " CMD_TARGET_PANE_USAGE,
.target = { 't', CMD_FIND_PANE, 0 },
.flags = CMD_AFTERHOOK,
.exec = cmd_hide_pane_exec
};
const struct cmd_entry cmd_show_pane_entry = {
.name = "show-pane",
.alias = "showp",
.args = { "at:", 0, 1, NULL },
.usage = "[-a] " CMD_TARGET_PANE_USAGE,
.target = { 't', CMD_FIND_PANE, 0 },
.flags = CMD_AFTERHOOK,
.exec = cmd_show_pane_exec
};
static enum cmd_retval
cmd_hide_pane_hide(struct window *w, struct window_pane *wp)
{
struct window_pane *wpp = NULL;
struct layout_cell *lc = wp->layout_cell;
if (window_pane_is_hidden(wp))
return (CMD_RETURN_NORMAL);
if (wp == w->active) {
/* Find previous active pane. */
TAILQ_FOREACH(wpp, &w->last_panes, sentry) {
if (wpp != wp && window_pane_is_visible(wpp))
break;
}
if (wpp == NULL) {
TAILQ_FOREACH(wpp, &w->z_index, zentry) {
if (wpp != wp &&
window_pane_is_visible(wpp))
break;
}
}
}
lc->flags |= LAYOUT_CELL_HIDDEN;
layout_remove_tile(w, lc);
layout_fix_offsets(w);
layout_fix_panes(w, NULL);
window_pane_stack_remove(&w->last_panes, wp);
if (wpp != NULL) {
window_set_active_pane(w, wpp, 1);
} else if (wp == w->active) {
/* No visible previous active pane; null active pane
* to show dots background. */
w->active = NULL;
if (options_get_number(global_options, "focus-events"))
window_pane_update_focus(wp);
notify_window("window-pane-changed", w);
notify_window("window-layout-changed", w);
server_redraw_window(w);
} else {
notify_window("window-layout-changed", w);
server_redraw_window(w);
}
return (CMD_RETURN_NORMAL);
}
static enum cmd_retval
cmd_hide_pane_exec(struct cmd *self, struct cmdq_item *item)
{
struct args *args = cmd_get_args(self);
struct cmd_find_state *target = cmdq_get_target(item);
struct winlink *wl = target->wl;
struct window *w = wl->window;
struct window_pane *wp, *active_pane = w->active;
u_int id;
char *cause = NULL;
enum cmd_retval rv;
window_unzoom(w, 1);
if (args_has(args, 'a')) {
TAILQ_FOREACH(wp, &w->z_index, zentry) {
if (!window_pane_is_visible(wp) || wp == active_pane)
continue;
rv = cmd_hide_pane_hide(w, wp);
if (rv != CMD_RETURN_NORMAL)
return (rv);
}
return (CMD_RETURN_NORMAL);
} else {
wp = target->wp;
if (wp == NULL) {
id = args_strtonum_and_expand(args, 't', 0, INT_MAX,
item, &cause);
if (cause != NULL) {
cmdq_error(item, "%s target pane", cause);
return (CMD_RETURN_ERROR);
}
wp = window_pane_find_by_id(id);
}
if (wp == NULL) {
cmdq_error(item, "No target pane to hide.");
return (CMD_RETURN_ERROR);
}
return (cmd_hide_pane_hide(w, wp));
}
}
static enum cmd_retval
cmd_hide_pane_show(struct window *w, struct window_pane *wp)
{
struct layout_cell *lc = wp->layout_cell;
if (!window_pane_is_hidden(wp))
return (CMD_RETURN_NORMAL);
lc->flags &= ~LAYOUT_CELL_HIDDEN;
layout_insert_tile(w, lc);
layout_fix_offsets(w);
layout_fix_panes(w, NULL);
window_set_active_pane(w, wp, 1);
notify_window("window-layout-changed", w);
server_redraw_window(w);
return (CMD_RETURN_NORMAL);
}
static enum cmd_retval
cmd_show_pane_exec(struct cmd *self, struct cmdq_item *item)
{
struct args *args = cmd_get_args(self);
struct cmd_find_state *target = cmdq_get_target(item);
struct winlink *wl = target->wl;
struct window *w = wl->window;
struct window_pane *wp;
u_int id;
char *cause = NULL;
enum cmd_retval rv;
window_unzoom(w, 1);
if (args_has(args, 'a')) {
TAILQ_FOREACH(wp, &w->z_index, zentry) {
if (!window_pane_is_visible(wp))
continue;
rv = cmd_hide_pane_show(w, wp);
if (rv != CMD_RETURN_NORMAL)
return (rv);
}
return (CMD_RETURN_NORMAL);
} else {
wp = target->wp;
if (wp == NULL) {
id = args_strtonum_and_expand(args, 't', 0, INT_MAX,
item, &cause);
if (cause != NULL) {
cmdq_error(item, "%s target pane", cause);
return (CMD_RETURN_ERROR);
}
wp = window_pane_find_by_id(id);
}
if (wp == NULL) {
cmdq_error(item, "No target pane to show.");
return (CMD_RETURN_ERROR);
}
return (cmd_hide_pane_show(w, wp));
}
}

View File

@@ -1,4 +1,4 @@
/* $OpenBSD: cmd-if-shell.c,v 1.87 2026/08/25 06:04:33 nicm Exp $ */ /* $OpenBSD$ */
/* /*
* Copyright (c) 2009 Tiago Cunha <me@tiagocunha.org> * Copyright (c) 2009 Tiago Cunha <me@tiagocunha.org>
@@ -98,7 +98,6 @@ 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);
} }
@@ -165,11 +164,9 @@ 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:

View File

@@ -1,4 +1,4 @@
/* $OpenBSD: cmd-join-pane.c,v 1.74 2026/08/03 20:29:52 nicm Exp $ */ /* $OpenBSD$ */
/* /*
* Copyright (c) 2011 George Nachman <tmux@georgester.com> * Copyright (c) 2011 George Nachman <tmux@georgester.com>
@@ -71,9 +71,8 @@ cmd_join_pane_place(struct cmdq_item *item, struct winlink *wl,
struct window *w = wl->window; struct window *w = wl->window;
struct layout_cell *lc = wp->layout_cell; struct layout_cell *lc = wp->layout_cell;
struct window_pane *owp; struct window_pane *owp;
int wx = w->sx, wy = w->sy; int wx = w->sx, wy = w->sy, px = lc->sx;
int px = lc->g.sx, py = lc->g.sy; int py = lc->sy, xoff = lc->xoff, yoff = lc->yoff;
int xoff = lc->g.xoff, yoff = lc->g.yoff;
int border = 1; int border = 1;
if (window_pane_get_pane_lines(wp) == PANE_LINES_NONE) if (window_pane_get_pane_lines(wp) == PANE_LINES_NONE)
@@ -181,13 +180,12 @@ cmd_join_pane_place(struct cmdq_item *item, struct winlink *wl,
return (CMD_RETURN_ERROR); return (CMD_RETURN_ERROR);
} }
if (xoff != lc->g.xoff || yoff != lc->g.yoff) { if (xoff != lc->xoff || yoff != lc->yoff) {
lc->g.xoff = xoff; lc->xoff = xoff;
lc->g.yoff = yoff; lc->yoff = yoff;
layout_fix_panes(w, NULL); layout_fix_panes(w, NULL);
} }
redraw_invalidate_scene(w); notify_window("window-layout-changed", w);
events_fire_window("window-layout-changed", w);
server_redraw_window(w); server_redraw_window(w);
return (CMD_RETURN_NORMAL); return (CMD_RETURN_NORMAL);
@@ -202,7 +200,7 @@ cmd_join_pane_move(struct cmdq_item *item, struct args *args,
const char *errstr, *argval; const char *errstr, *argval;
const char flags[] = { 'U', 'D', 'L', 'R' }; const char flags[] = { 'U', 'D', 'L', 'R' };
char *cause = NULL, flag; char *cause = NULL, flag;
int xoff = lc->g.xoff, yoff = lc->g.yoff, adjust; int xoff = lc->xoff, yoff = lc->yoff, adjust;
u_int i; u_int i;
enum pane_lines lines = window_pane_get_pane_lines(wp); enum pane_lines lines = window_pane_get_pane_lines(wp);
@@ -253,11 +251,11 @@ cmd_join_pane_move(struct cmdq_item *item, struct args *args,
xoff += adjust; xoff += adjust;
} }
if (xoff != lc->g.xoff || yoff != lc->g.yoff) { if (xoff != lc->xoff || yoff != lc->yoff) {
lc->g.xoff = xoff; lc->xoff = xoff;
lc->g.yoff = yoff; lc->yoff = yoff;
layout_fix_panes(w, NULL); layout_fix_panes(w, NULL);
events_fire_window("window-layout-changed", w); notify_window("window-layout-changed", w);
server_redraw_window(w); server_redraw_window(w);
} }
@@ -321,8 +319,8 @@ cmd_join_pane_mouse_move(struct client *c, struct mouse_event *m)
ly = m->statusat - 1; ly = m->statusat - 1;
if (x != lx || y != ly) { if (x != lx || y != ly) {
lc->g.xoff += x - lx; lc->xoff += x - lx;
lc->g.yoff += y - ly; lc->yoff += y - ly;
layout_fix_panes(w, NULL); layout_fix_panes(w, NULL);
server_redraw_window(w); server_redraw_window(w);
server_redraw_window_borders(w); server_redraw_window_borders(w);
@@ -359,8 +357,7 @@ cmd_join_pane_zindex(struct cmdq_item *item, struct winlink *wl,
else else
TAILQ_INSERT_TAIL(&w->z_index, wp, zentry); TAILQ_INSERT_TAIL(&w->z_index, wp, zentry);
redraw_invalidate_scene(w); notify_window("window-layout-changed", w);
events_fire_window("window-layout-changed", w);
server_redraw_window(w); server_redraw_window(w);
return (CMD_RETURN_NORMAL); return (CMD_RETURN_NORMAL);
@@ -376,21 +373,21 @@ cmd_join_pane_tile(struct cmdq_item *item, struct args *args, struct window *w,
cmdq_error(item, "pane is not floating"); cmdq_error(item, "pane is not floating");
return (CMD_RETURN_ERROR); return (CMD_RETURN_ERROR);
} }
if (window_pane_is_hidden(wp)) {
cmdq_error(item, "can't tile a hidden pane");
return (CMD_RETURN_ERROR);
}
if (w->flags & WINDOW_ZOOMED) { if (w->flags & WINDOW_ZOOMED) {
cmdq_error(item, "can't tile a pane while window is zoomed"); cmdq_error(item, "can't tile a pane while window is zoomed");
return (CMD_RETURN_ERROR); return (CMD_RETURN_ERROR);
} }
lc->fg.sx = lc->g.sx; layout_save_size(lc);
lc->fg.sy = lc->g.sy; lc->flags &= ~LAYOUT_CELL_FLOATING;
lc->fg.xoff = lc->g.xoff; if (layout_insert_tile(w, lc) == 0) {
lc->fg.yoff = lc->g.yoff;
if (layout_insert_tile(w, lc) != 0) {
cmdq_error(item, "no space for a new pane"); cmdq_error(item, "no space for a new pane");
return (CMD_RETURN_ERROR); return (CMD_RETURN_ERROR);
} }
lc->flags &= ~LAYOUT_CELL_FLOATING;
TAILQ_REMOVE(&w->z_index, wp, zentry); TAILQ_REMOVE(&w->z_index, wp, zentry);
TAILQ_INSERT_TAIL(&w->z_index, wp, zentry); TAILQ_INSERT_TAIL(&w->z_index, wp, zentry);
@@ -399,8 +396,7 @@ cmd_join_pane_tile(struct cmdq_item *item, struct args *args, struct window *w,
window_set_active_pane(w, wp, 1); window_set_active_pane(w, wp, 1);
layout_fix_offsets(w); layout_fix_offsets(w);
layout_fix_panes(w, NULL); layout_fix_panes(w, NULL);
redraw_invalidate_scene(w); notify_window("window-layout-changed", w);
events_fire_window("window-layout-changed", w);
server_redraw_window(w); server_redraw_window(w);
return (CMD_RETURN_NORMAL); return (CMD_RETURN_NORMAL);
@@ -413,84 +409,68 @@ cmd_join_pane_exec(struct cmd *self, struct cmdq_item *item)
struct cmd_find_state *current = cmdq_get_current(item); struct cmd_find_state *current = cmdq_get_current(item);
struct cmd_find_state *target = cmdq_get_target(item); struct cmd_find_state *target = cmdq_get_target(item);
struct cmd_find_state *source = cmdq_get_source(item); struct cmd_find_state *source = cmdq_get_source(item);
struct client *c = cmdq_get_client(item);
struct client *tc = cmdq_get_target_client(item);
struct client *ac;
struct session *dst_s; struct session *dst_s;
struct winlink *src_wl, *dst_wl; struct winlink *src_wl, *dst_wl;
struct window *src_w, *dst_w; struct window *src_w, *dst_w;
struct window_pane *src_wp, *dst_wp; struct window_pane *src_wp, *dst_wp;
const char *s; const char *s;
char *cause = NULL; char *cause = NULL;
int flags = 0, dst_idx, local; int flags = 0, dst_idx;
struct layout_cell *lc; struct layout_cell *lc, *src_lc;
dst_s = target->s; dst_s = target->s;
dst_wl = target->wl; dst_wl = target->wl;
dst_wp = target->wp; dst_wp = target->wp;
dst_w = dst_wl->window; dst_w = dst_wl->window;
dst_idx = dst_wl->idx; dst_idx = dst_wl->idx;
ac = tc; server_unzoom_window(dst_w);
if (ac == NULL || ac->session != dst_s)
ac = c;
local = active_is_local_window(ac, dst_s);
if (cmd_get_entry(self) == &cmd_move_pane_entry) { if (cmd_get_entry(self) == &cmd_move_pane_entry) {
if (args_has(args, 'M')) if (args_has(args, 'M'))
return (cmd_join_pane_mouse_update(item)); return (cmd_join_pane_mouse_update(item));
if (args_has(args, 'P') || if (!window_pane_is_floating(dst_wp)) {
args_has(args, 'z') || cmdq_error(item, "pane is not floating");
args_has(args, 'X') || 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));
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'))
if (!window_pane_is_floating(dst_wp)) {
cmdq_error(item, "pane is not floating");
return (CMD_RETURN_ERROR);
}
server_unzoom_window(dst_w);
if ((s = args_get(args, 'P')) != NULL)
return (cmd_join_pane_place(item, dst_wl, dst_wp, s));
if ((s = args_get(args, 'z')) != NULL)
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));
}
} }
src_wl = source->wl; src_wl = source->wl;
src_wp = source->wp; src_wp = source->wp;
src_lc = src_wp->layout_cell;
src_w = src_wl->window; src_w = src_wl->window;
if (src_wp == src_w->modal || dst_wp == dst_w->modal) {
cmdq_error(item, "pane is modal");
return (CMD_RETURN_ERROR);
}
server_unzoom_window(dst_w);
server_unzoom_window(src_w); server_unzoom_window(src_w);
if (!args_has(args, 't') && window_pane_is_floating(src_wp))
return (cmd_join_pane_tile(item, args, src_w, src_wp));
if (src_wp == dst_wp) { if (src_wp == dst_wp) {
if (window_pane_is_floating(src_wp))
return (cmd_join_pane_tile(item, args, src_w, src_wp));
cmdq_error(item, "source and target panes must be different"); cmdq_error(item, "source and target panes must be different");
return (CMD_RETURN_ERROR); return (CMD_RETURN_ERROR);
} }
if (args_has(args, 'h'))
flags |= SPAWN_HORIZONTAL;
if (args_has(args, 'b'))
flags |= SPAWN_BEFORE;
if (args_has(args, 'f'))
flags |= SPAWN_FULLSIZE;
lc = layout_get_tiled_cell(item, args, dst_w, dst_wp, flags, &cause); lc = layout_get_tiled_cell(item, args, dst_w, dst_wp, flags, &cause);
if (cause != NULL) { if (cause != NULL) {
cmdq_error(item, "size or position %s", cause); cmdq_error(item, "size or position %s", cause);
free(cause); free(cause);
return (CMD_RETURN_ERROR); return (CMD_RETURN_ERROR);
} }
if (window_pane_is_floating(src_wp)) {
lc->saved_sx = src_lc->sx;
lc->saved_sy = src_lc->sy;
lc->saved_xoff = src_lc->xoff;
lc->saved_yoff = src_lc->yoff;
}
layout_close_pane(src_wp); layout_close_pane(src_wp);
@@ -519,27 +499,17 @@ cmd_join_pane_exec(struct cmd *self, struct cmdq_item *item)
if (!args_has(args, 'd')) { if (!args_has(args, 'd')) {
window_set_active_pane(dst_w, src_wp, 1); window_set_active_pane(dst_w, src_wp, 1);
if (local) session_select(dst_s, dst_idx);
active_select_window(ac, dst_s, dst_wl); cmd_find_from_session(current, dst_s, 0);
else server_redraw_session(dst_s);
session_select(dst_s, dst_idx);
if (local && ac != NULL && ac->session == dst_s)
cmd_find_from_client(current, ac, 0);
else
cmd_find_from_session(current, dst_s, 0);
if (local && ac != NULL && ac->session == dst_s)
server_redraw_client(ac);
else
server_redraw_session(dst_s);
} else } else
server_status_session(dst_s); server_status_session(dst_s);
window_fire_pane_moved(src_wp, src_w, src_wl->idx, dst_w, dst_idx);
if (window_count_panes(src_w, 1) == 0) if (window_count_panes(src_w, 1) == 0)
server_kill_window(src_w, 1); server_kill_window(src_w, 1);
else else
events_fire_window("window-layout-changed", src_w); notify_window("window-layout-changed", src_w);
events_fire_window("window-layout-changed", dst_w); notify_window("window-layout-changed", dst_w);
return (CMD_RETURN_NORMAL); return (CMD_RETURN_NORMAL);
} }

View File

@@ -1,4 +1,4 @@
/* $OpenBSD: cmd-kill-pane.c,v 1.34 2026/06/09 21:22:22 nicm Exp $ */ /* $OpenBSD$ */
/* /*
* Copyright (c) 2009 Nicholas Marriott <nicholas.marriott@gmail.com> * Copyright (c) 2009 Nicholas Marriott <nicholas.marriott@gmail.com>

View File

@@ -1,4 +1,4 @@
/* $OpenBSD: cmd-kill-server.c,v 1.20 2021/08/21 10:22:39 nicm Exp $ */ /* $OpenBSD$ */
/* /*
* Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com> * Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>

View File

@@ -1,4 +1,4 @@
/* $OpenBSD: cmd-kill-session.c,v 1.31 2026/06/09 12:57:40 nicm Exp $ */ /* $OpenBSD$ */
/* /*
* Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com> * Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>

View File

@@ -1,4 +1,4 @@
/* $OpenBSD: cmd-kill-window.c,v 1.30 2026/06/09 12:57:40 nicm Exp $ */ /* $OpenBSD$ */
/* /*
* Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com> * Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>

View File

@@ -1,4 +1,4 @@
/* $OpenBSD: cmd-list-buffers.c,v 1.41 2026/02/27 08:25:12 nicm Exp $ */ /* $OpenBSD$ */
/* /*
* Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com> * Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>

View File

@@ -1,4 +1,4 @@
/* $OpenBSD: cmd-list-clients.c,v 1.42 2026/02/27 08:25:12 nicm Exp $ */ /* $OpenBSD$ */
/* /*
* Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com> * Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>

View File

@@ -1,4 +1,4 @@
/* $OpenBSD: cmd-list-commands.c,v 1.15 2026/02/24 08:22:13 nicm Exp $ */ /* $OpenBSD$ */
/* /*
* Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com> * Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>

View File

@@ -1,4 +1,4 @@
/* $OpenBSD: cmd-list-keys.c,v 1.78 2026/07/01 13:12:17 nicm Exp $ */ /* $OpenBSD$ */
/* /*
* Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com> * Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>

View File

@@ -1,4 +1,4 @@
/* $OpenBSD: cmd-list-panes.c,v 1.40 2026/06/01 14:01:09 nicm Exp $ */ /* $OpenBSD$ */
/* /*
* Copyright (c) 2009 Nicholas Marriott <nicholas.marriott@gmail.com> * Copyright (c) 2009 Nicholas Marriott <nicholas.marriott@gmail.com>

View File

@@ -1,4 +1,4 @@
/* $OpenBSD: cmd-list-sessions.c,v 1.36 2026/02/27 08:25:12 nicm Exp $ */ /* $OpenBSD$ */
/* /*
* Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com> * Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>

View File

@@ -1,4 +1,4 @@
/* $OpenBSD: cmd-list-windows.c,v 1.50 2026/02/27 08:25:12 nicm Exp $ */ /* $OpenBSD$ */
/* /*
* Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com> * Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>

View File

@@ -1,4 +1,4 @@
/* $OpenBSD: cmd-load-buffer.c,v 1.66 2025/10/28 07:32:26 nicm Exp $ */ /* $OpenBSD$ */
/* /*
* Copyright (c) 2009 Tiago Cunha <me@tiagocunha.org> * Copyright (c) 2009 Tiago Cunha <me@tiagocunha.org>

View File

@@ -1,4 +1,4 @@
/* $OpenBSD: cmd-lock-server.c,v 1.31 2021/08/21 10:22:39 nicm Exp $ */ /* $OpenBSD$ */
/* /*
* Copyright (c) 2008 Nicholas Marriott <nicholas.marriott@gmail.com> * Copyright (c) 2008 Nicholas Marriott <nicholas.marriott@gmail.com>

View File

@@ -1,4 +1,4 @@
/* $OpenBSD: cmd-move-window.c,v 1.34 2021/08/21 10:22:39 nicm Exp $ */ /* $OpenBSD$ */
/* /*
* Copyright (c) 2008 Nicholas Marriott <nicholas.marriott@gmail.com> * Copyright (c) 2008 Nicholas Marriott <nicholas.marriott@gmail.com>
@@ -62,15 +62,12 @@ cmd_move_window_exec(struct cmd *self, struct cmdq_item *item)
struct args *args = cmd_get_args(self); struct args *args = cmd_get_args(self);
struct cmd_find_state *source = cmdq_get_source(item); struct cmd_find_state *source = cmdq_get_source(item);
struct cmd_find_state target; struct cmd_find_state target;
struct client *c = cmdq_get_client(item);
struct client *tc = cmdq_get_target_client(item);
struct client *ac;
const char *tflag = args_get(args, 't'); const char *tflag = args_get(args, 't');
struct session *src = source->s; struct session *src = source->s;
struct session *dst; struct session *dst;
struct winlink *wl = source->wl, *dstwl, *current_wl; struct winlink *wl = source->wl;
char *cause; char *cause;
int idx, kflag, dflag, sflag, before, local; int idx, kflag, dflag, sflag, before;
if (args_has(args, 'r')) { if (args_has(args, 'r')) {
if (cmd_find_target(&target, item, tflag, CMD_FIND_SESSION, if (cmd_find_target(&target, item, tflag, CMD_FIND_SESSION,
@@ -93,37 +90,23 @@ cmd_move_window_exec(struct cmd *self, struct cmdq_item *item)
dflag = args_has(args, 'd'); dflag = args_has(args, 'd');
sflag = args_has(args, 's'); sflag = args_has(args, 's');
ac = tc;
if (ac == NULL || ac->session != dst)
ac = c;
local = (!dflag && active_is_local_window(ac, dst));
before = args_has(args, 'b'); before = args_has(args, 'b');
if (args_has(args, 'a') || before) { if (args_has(args, 'a') || before) {
if (target.wl != NULL) if (target.wl != NULL)
idx = winlink_shuffle_up(dst, target.wl, before); idx = winlink_shuffle_up(dst, target.wl, before);
else { else
current_wl = active_get_effective_winlink(ac, dst); idx = winlink_shuffle_up(dst, dst->curw, before);
idx = winlink_shuffle_up(dst, current_wl, before);
}
if (idx == -1) if (idx == -1)
return (CMD_RETURN_ERROR); return (CMD_RETURN_ERROR);
} }
if (server_link_window(src, wl, dst, idx, kflag, !dflag && !local, if (server_link_window(src, wl, dst, idx, kflag, !dflag, &cause) != 0) {
&cause) != 0) {
cmdq_error(item, "%s", cause); cmdq_error(item, "%s", cause);
free(cause); free(cause);
return (CMD_RETURN_ERROR); return (CMD_RETURN_ERROR);
} }
dstwl = winlink_find_by_window(&dst->windows, wl->window);
if (cmd_get_entry(self) == &cmd_move_window_entry) if (cmd_get_entry(self) == &cmd_move_window_entry)
server_unlink_window(src, wl); server_unlink_window(src, wl);
if (local && dstwl != NULL) {
active_select_window(ac, dst, dstwl);
if (ac != NULL && ac->session == dst)
server_redraw_client(ac);
}
/* /*
* Renumber the winlinks in the src session only, the destination * Renumber the winlinks in the src session only, the destination

View File

@@ -1,4 +1,4 @@
/* $OpenBSD: cmd-new-session.c,v 1.152 2026/07/10 13:38:45 nicm Exp $ */ /* $OpenBSD$ */
/* /*
* Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com> * Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>
@@ -325,7 +325,7 @@ cmd_new_session_exec(struct cmd *self, struct cmdq_item *item)
session_group_synchronize_to(s); session_group_synchronize_to(s);
session_select(s, RB_MIN(winlinks, &s->windows)->idx); session_select(s, RB_MIN(winlinks, &s->windows)->idx);
} }
events_fire_session("session-created", s); notify_session("session-created", s);
/* /*
* Set the client to the new session. If a command client exists, it is * Set the client to the new session. If a command client exists, it is

View File

@@ -1,4 +1,4 @@
/* $OpenBSD: cmd-new-window.c,v 1.103 2026/07/08 08:07:42 nicm Exp $ */ /* $OpenBSD$ */
/* /*
* Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com> * Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>
@@ -38,8 +38,8 @@ const struct cmd_entry cmd_new_window_entry = {
.name = "new-window", .name = "new-window",
.alias = "neww", .alias = "neww",
.args = { "abc:de:EF:kn:PSt:", 0, -1, NULL }, .args = { "abc:de:F:kn:PSt:", 0, -1, NULL },
.usage = "[-abdEkPS] [-c start-directory] [-e environment] [-F format] " .usage = "[-abdkPS] [-c start-directory] [-e environment] [-F format] "
"[-n window-name] " CMD_TARGET_WINDOW_USAGE "[-n window-name] " CMD_TARGET_WINDOW_USAGE
" [shell-command [argument ...]]", " [shell-command [argument ...]]",
@@ -58,23 +58,14 @@ cmd_new_window_exec(struct cmd *self, struct cmdq_item *item)
struct cmd_find_state *target = cmdq_get_target(item); struct cmd_find_state *target = cmdq_get_target(item);
struct spawn_context sc = { 0 }; struct spawn_context sc = { 0 };
struct client *tc = cmdq_get_target_client(item); struct client *tc = cmdq_get_target_client(item);
struct client *ac;
struct session *s = target->s; struct session *s = target->s;
struct winlink *wl = target->wl, *new_wl = NULL; struct winlink *wl = target->wl, *new_wl = NULL;
int idx = target->idx, before, count = args_count(args); int idx = target->idx, before;
int detached, local;
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;
struct args_value *av; struct args_value *av;
if (args_has(args, 'E') &&
count != 0 &&
(count != 1 || *args_string(args, 0) != '\0')) {
cmdq_error(item, "command cannot be given for empty pane");
return (CMD_RETURN_ERROR);
}
/* /*
* If -S and -n are given and -t is not and a single window with this * If -S and -n are given and -t is not and a single window with this
* name already exists, select it. * name already exists, select it.
@@ -109,15 +100,10 @@ cmd_new_window_exec(struct cmd *self, struct cmdq_item *item)
free(wname); free(wname);
if (args_has(args, 'd')) if (args_has(args, 'd'))
return (CMD_RETURN_NORMAL); return (CMD_RETURN_NORMAL);
if (active_select_window(c, s, new_wl) == 0) { if (session_set_current(s, new_wl) == 0)
if (active_is_local_window(c, s) && c != NULL && server_redraw_session(s);
c->session == s)
server_redraw_client(c);
else
server_redraw_session(s);
}
if (c != NULL && c->session != NULL) if (c != NULL && c->session != NULL)
active_get_effective_window(c, s)->latest = c; s->curw->window->latest = c;
recalculate_sizes(); recalculate_sizes();
return (CMD_RETURN_NORMAL); return (CMD_RETURN_NORMAL);
} }
@@ -147,16 +133,8 @@ cmd_new_window_exec(struct cmd *self, struct cmdq_item *item)
sc.idx = idx; sc.idx = idx;
sc.cwd = args_get(args, 'c'); sc.cwd = args_get(args, 'c');
ac = tc;
if (ac == NULL || ac->session != s)
ac = c;
detached = args_has(args, 'd');
local = (!detached && active_is_local_window(ac, s));
sc.flags = 0; sc.flags = 0;
if (args_has(args, 'E') || (count == 1 && *args_string(args, 0) == '\0')) if (args_has(args, 'd'))
sc.flags |= SPAWN_EMPTY;
if (detached || local)
sc.flags |= SPAWN_DETACHED; sc.flags |= SPAWN_DETACHED;
if (args_has(args, 'k')) if (args_has(args, 'k'))
sc.flags |= SPAWN_KILL; sc.flags |= SPAWN_KILL;
@@ -166,13 +144,7 @@ cmd_new_window_exec(struct cmd *self, struct cmdq_item *item)
free(cause); free(cause);
goto fail; goto fail;
} }
if (local) { if (!args_has(args, 'd') || new_wl == s->curw) {
if (active_select_window(ac, s, new_wl) == 0)
cmd_find_from_winlink(current, new_wl, 0);
if (ac != NULL && ac->session == s)
server_redraw_client(ac);
server_status_session_group(s);
} else if (!detached || new_wl == s->curw) {
cmd_find_from_winlink(current, new_wl, 0); cmd_find_from_winlink(current, new_wl, 0);
server_redraw_session_group(s); server_redraw_session_group(s);
} else } else

View File

@@ -1,4 +1,4 @@
/* $OpenBSD: cmd-parse.y,v 1.59 2026/08/31 07:51:56 nicm Exp $ */ /* $OpenBSD$ */
/* /*
* Copyright (c) 2019 Nicholas Marriott <nicholas.marriott@gmail.com> * Copyright (c) 2019 Nicholas Marriott <nicholas.marriott@gmail.com>
@@ -802,7 +802,6 @@ 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);
} }
@@ -815,7 +814,6 @@ 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);
} }

View File

@@ -1,4 +1,4 @@
/* $OpenBSD: cmd-paste-buffer.c,v 1.44 2026/03/04 07:19:32 tb Exp $ */ /* $OpenBSD$ */
/* /*
* Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com> * Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>

View File

@@ -1,4 +1,4 @@
/* $OpenBSD: cmd-pipe-pane.c,v 1.64 2026/07/27 14:25:46 nicm Exp $ */ /* $OpenBSD$ */
/* /*
* Copyright (c) 2009 Nicholas Marriott <nicholas.marriott@gmail.com> * Copyright (c) 2009 Nicholas Marriott <nicholas.marriott@gmail.com>
@@ -194,13 +194,9 @@ static void
cmd_pipe_pane_read_callback(__unused struct bufferevent *bufev, void *data) cmd_pipe_pane_read_callback(__unused struct bufferevent *bufev, void *data)
{ {
struct window_pane *wp = data; struct window_pane *wp = data;
struct evbuffer *evb; struct evbuffer *evb = wp->pipe_event->input;
size_t available; size_t available;
if (wp->pipe_event == NULL)
return;
evb = wp->pipe_event->input;
available = EVBUFFER_LENGTH(evb); available = EVBUFFER_LENGTH(evb);
log_debug("%%%u pipe read %zu", wp->id, available); log_debug("%%%u pipe read %zu", wp->id, available);

View File

@@ -1,4 +1,4 @@
/* $OpenBSD: cmd-queue.c,v 1.123 2026/08/24 20:34:26 nicm Exp $ */ /* $OpenBSD$ */
/* /*
* Copyright (c) 2013 Nicholas Marriott <nicholas.marriott@gmail.com> * Copyright (c) 2013 Nicholas Marriott <nicholas.marriott@gmail.com>
@@ -147,13 +147,6 @@ cmdq_get_name(struct cmdq_item *item)
return (item->name); return (item->name);
} }
/* Get item command. */
struct cmd *
cmdq_get_cmd(struct cmdq_item *item)
{
return (item->cmd);
}
/* Get item client. */ /* Get item client. */
struct client * struct client *
cmdq_get_client(struct cmdq_item *item) cmdq_get_client(struct cmdq_item *item)
@@ -357,53 +350,74 @@ cmdq_insert_after(struct cmdq_item *after, struct cmdq_item *item)
/* Insert a hook. */ /* Insert a hook. */
void void
cmdq_insert_hook(__unused struct session *s, struct cmdq_item *item, cmdq_insert_hook(struct session *s, struct cmdq_item *item,
struct cmd_find_state *current, const char *fmt, ...) struct cmd_find_state *current, const char *fmt, ...)
{ {
struct cmdq_state *state = item->state;
struct cmd *cmd = item->cmd; struct cmd *cmd = item->cmd;
struct args *args = cmd_get_args(cmd); struct args *args = cmd_get_args(cmd);
struct args_entry *ae; struct args_entry *ae;
struct args_value *av; struct args_value *av;
struct event_payload *ep; struct options *oo;
va_list ap; va_list ap;
char *name, tmp[32], flag, *arguments; char *name, tmp[32], flag, *arguments;
u_int i; u_int i;
const char *value; const char *value;
struct cmdq_item *new_item;
struct cmdq_state *new_state;
struct options_entry *o;
struct options_array_item *a;
struct cmd_list *cmdlist;
if (item->state->flags & CMDQ_STATE_NOHOOKS) if (item->state->flags & CMDQ_STATE_NOHOOKS)
return; return;
if (s == NULL)
oo = global_s_options;
else
oo = s->options;
va_start(ap, fmt); va_start(ap, fmt);
xvasprintf(&name, fmt, ap); xvasprintf(&name, fmt, ap);
va_end(ap); va_end(ap);
ep = event_payload_create(); o = options_get(oo, name);
if (current != NULL) if (o == NULL) {
event_payload_set_target(ep, current); free(name);
event_payload_set_pointer(ep, "_cmdq_item", item, NULL, NULL); return;
}
log_debug("running hook %s (parent %p)", name, item);
/*
* The hooks get a new state because they should not update the current
* target or formats for any subsequent commands.
*/
new_state = cmdq_new_state(current, &state->event, CMDQ_STATE_NOHOOKS);
cmdq_add_format(new_state, "hook", "%s", name);
arguments = args_print(args); arguments = args_print(args);
event_payload_set_string(ep, "arguments", "%s", arguments); cmdq_add_format(new_state, "hook_arguments", "%s", arguments);
free(arguments); free(arguments);
for (i = 0; i < args_count(args); i++) { for (i = 0; i < args_count(args); i++) {
xsnprintf(tmp, sizeof tmp, "argument_%u", i); xsnprintf(tmp, sizeof tmp, "hook_argument_%d", i);
event_payload_set_string(ep, tmp, "%s", args_string(args, i)); cmdq_add_format(new_state, tmp, "%s", args_string(args, i));
} }
flag = args_first(args, &ae); flag = args_first(args, &ae);
while (flag != 0) { while (flag != 0) {
value = args_get(args, flag); value = args_get(args, flag);
xsnprintf(tmp, sizeof tmp, "flag_%c", flag); if (value == NULL) {
if (value == NULL) xsnprintf(tmp, sizeof tmp, "hook_flag_%c", flag);
event_payload_set_string(ep, tmp, "1"); cmdq_add_format(new_state, tmp, "1");
else } else {
event_payload_set_string(ep, tmp, "%s", value); xsnprintf(tmp, sizeof tmp, "hook_flag_%c", flag);
cmdq_add_format(new_state, tmp, "%s", value);
}
i = 0; i = 0;
av = args_first_value(args, flag); av = args_first_value(args, flag);
while (av != NULL) { while (av != NULL) {
xsnprintf(tmp, sizeof tmp, "flag_%c_%u", flag, i); xsnprintf(tmp, sizeof tmp, "hook_flag_%c_%d", flag, i);
event_payload_set_string(ep, tmp, "%s", av->string); cmdq_add_format(new_state, tmp, "%s", av->string);
i++; i++;
av = args_next_value(av); av = args_next_value(av);
} }
@@ -411,7 +425,20 @@ cmdq_insert_hook(__unused struct session *s, struct cmdq_item *item,
flag = args_next(&ae); flag = args_next(&ae);
} }
events_fire(name, ep); a = options_array_first(o);
while (a != NULL) {
cmdlist = options_array_item_value(a)->cmdlist;
if (cmdlist != NULL) {
new_item = cmdq_get_command(cmdlist, new_state);
if (item != NULL)
item = cmdq_insert_after(item, new_item);
else
item = cmdq_append(NULL, new_item);
}
a = options_array_next(a);
}
cmdq_free_state(new_state);
free(name); free(name);
} }
@@ -802,7 +829,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_guard(c, guard, t, number, flags); control_write(c, "%%%s %ld %u %d", guard, t, number, flags);
} }
/* Show message from command. */ /* Show message from command. */
@@ -850,18 +877,7 @@ 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) { cfg_add_cause("%s:%u: %s", file, line, msg);
if (file != NULL)
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) {

View File

@@ -1,4 +1,4 @@
/* $OpenBSD: cmd-refresh-client.c,v 1.55 2026/07/17 08:37:29 nicm Exp $ */ /* $OpenBSD$ */
/* /*
* Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com> * Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>
@@ -46,17 +46,36 @@ const struct cmd_entry cmd_refresh_client_entry = {
static void static void
cmd_refresh_client_update_subscription(struct client *tc, const char *value) cmd_refresh_client_update_subscription(struct client *tc, const char *value)
{ {
char *name, *format; char *copy, *split, *name, *what;
enum monitor_type type; enum control_sub_type subtype;
int id; int subid = -1;
if (monitor_parse(value, &name, &type, &id, &format) != 0) { copy = name = xstrdup(value);
control_remove_sub(tc, value); if ((split = strchr(copy, ':')) == NULL) {
return; control_remove_sub(tc, copy);
goto out;
} }
control_add_sub(tc, name, type, id, format); *split++ = '\0';
free(name);
free(format); what = split;
if ((split = strchr(what, ':')) == NULL)
goto out;
*split++ = '\0';
if (strcmp(what, "%*") == 0)
subtype = CONTROL_SUB_ALL_PANES;
else if (sscanf(what, "%%%d", &subid) == 1 && subid >= 0)
subtype = CONTROL_SUB_PANE;
else if (strcmp(what, "@*") == 0)
subtype = CONTROL_SUB_ALL_WINDOWS;
else if (sscanf(what, "@%d", &subid) == 1 && subid >= 0)
subtype = CONTROL_SUB_WINDOW;
else
subtype = CONTROL_SUB_SESSION;
control_add_sub(tc, name, subtype, subid, split);
out:
free(copy);
} }
static enum cmd_retval static enum cmd_retval
@@ -66,6 +85,7 @@ cmd_refresh_client_control_client_size(struct cmd *self, struct cmdq_item *item)
struct client *tc = cmdq_get_target_client(item); struct client *tc = cmdq_get_target_client(item);
const char *size = args_get(args, 'C'); const char *size = args_get(args, 'C');
u_int w, x, y; u_int w, x, y;
struct client_window *cw;
if (sscanf(size, "@%u:%ux%u", &w, &x, &y) == 3) { if (sscanf(size, "@%u:%ux%u", &w, &x, &y) == 3) {
if (x < WINDOW_MINIMUM || x > WINDOW_MAXIMUM || if (x < WINDOW_MINIMUM || x > WINDOW_MAXIMUM ||
@@ -75,16 +95,22 @@ cmd_refresh_client_control_client_size(struct cmd *self, struct cmdq_item *item)
} }
log_debug("%s: client %s window @%u: size %ux%u", __func__, log_debug("%s: client %s window @%u: size %ux%u", __func__,
tc->name, w, x, y); tc->name, w, x, y);
control_set_window_size(tc, w, x, y); cw = server_client_add_client_window(tc, w);
cw->sx = x;
cw->sy = y;
tc->flags |= CLIENT_WINDOWSIZECHANGED; tc->flags |= CLIENT_WINDOWSIZECHANGED;
recalculate_sizes_now(1); recalculate_sizes_now(1);
return (CMD_RETURN_NORMAL); return (CMD_RETURN_NORMAL);
} }
if (sscanf(size, "@%u:", &w) == 1) { if (sscanf(size, "@%u:", &w) == 1) {
log_debug("%s: client %s window @%u: no size", __func__, cw = server_client_get_client_window(tc, w);
tc->name, w); if (cw != NULL) {
control_clear_window_size(tc, w); log_debug("%s: client %s window @%u: no size", __func__,
recalculate_sizes_now(1); tc->name, w);
cw->sx = 0;
cw->sy = 0;
recalculate_sizes_now(1);
}
return (CMD_RETURN_NORMAL); return (CMD_RETURN_NORMAL);
} }
@@ -203,7 +229,7 @@ cmd_refresh_client_exec(struct cmd *self, struct cmdq_item *item)
if (args_has(args, 'c')) if (args_has(args, 'c'))
tc->pan_window = NULL; tc->pan_window = NULL;
else { else {
w = active_get_effective_window(tc, tc->session); w = tc->session->curw->window;
if (tc->pan_window != w) { if (tc->pan_window != w) {
tc->pan_window = w; tc->pan_window = w;
tc->pan_ox = tty->oox; tc->pan_ox = tty->oox;

View File

@@ -1,4 +1,4 @@
/* $OpenBSD: cmd-rename-session.c,v 1.39 2026/07/10 13:38:45 nicm Exp $ */ /* $OpenBSD$ */
/* /*
* Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com> * Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>
@@ -49,8 +49,6 @@ cmd_rename_session_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 session *s = target->s; struct session *s = target->s;
struct event_payload *ep;
struct cmd_find_state fs;
char *newname, *tmp; char *newname, *tmp;
tmp = format_single_from_target(item, args_string(args, 0)); tmp = format_single_from_target(item, args_string(args, 0));
@@ -71,20 +69,13 @@ cmd_rename_session_exec(struct cmd *self, struct cmdq_item *item)
return (CMD_RETURN_ERROR); return (CMD_RETURN_ERROR);
} }
ep = event_payload_create();
cmd_find_from_session(&fs, s, 0);
event_payload_set_target(ep, &fs);
event_payload_set_session(ep, "session", s);
event_payload_set_string(ep, "old_name", "%s", s->name);
event_payload_set_string(ep, "new_name", "%s", newname);
RB_REMOVE(sessions, &sessions, s); RB_REMOVE(sessions, &sessions, s);
free(s->name); free(s->name);
s->name = newname; s->name = newname;
RB_INSERT(sessions, &sessions, s); RB_INSERT(sessions, &sessions, s);
server_status_session(s); server_status_session(s);
events_fire("session-renamed", ep); notify_session("session-renamed", s);
return (CMD_RETURN_NORMAL); return (CMD_RETURN_NORMAL);
} }

View File

@@ -1,4 +1,4 @@
/* $OpenBSD: cmd-rename-window.c,v 1.30 2026/06/29 18:17:28 nicm Exp $ */ /* $OpenBSD$ */
/* /*
* Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com> * Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>

View File

@@ -1,4 +1,4 @@
/* $OpenBSD: cmd-resize-pane.c,v 1.68 2026/08/31 07:44:39 nicm Exp $ */ /* $OpenBSD$ */
/* /*
* Copyright (c) 2009 Nicholas Marriott <nicholas.marriott@gmail.com> * Copyright (c) 2009 Nicholas Marriott <nicholas.marriott@gmail.com>
@@ -92,7 +92,6 @@ cmd_resize_pane_exec(struct cmd *self, struct cmdq_item *item)
return (CMD_RETURN_NORMAL); return (CMD_RETURN_NORMAL);
} }
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);
@@ -183,7 +182,7 @@ cmd_resize_pane_exec(struct cmd *self, struct cmdq_item *item)
if (lc->parent != NULL) if (lc->parent != NULL)
layout_fix_offsets(w); layout_fix_offsets(w);
layout_fix_panes(w, NULL); layout_fix_panes(w, NULL);
events_fire_window("window-layout-changed", w); notify_window("window-layout-changed", w);
server_redraw_window(w); server_redraw_window(w);
return (CMD_RETURN_NORMAL); return (CMD_RETURN_NORMAL);
@@ -271,10 +270,10 @@ cmd_resize_pane_mouse_resize_move_floating(struct client *c,
if ((lx == left || lx == left + 1) && ly == wp->yoff - 1) { if ((lx == left || lx == left + 1) && ly == wp->yoff - 1) {
/* Top left corner. */ /* Top left corner. */
new_sx = lc->g.sx + (lx - x); new_sx = lc->sx + (lx - x);
if (new_sx < PANE_MINIMUM) if (new_sx < PANE_MINIMUM)
new_sx = PANE_MINIMUM; new_sx = PANE_MINIMUM;
new_sy = lc->g.sy + (ly - y); new_sy = lc->sy + (ly - y);
if (new_sy < PANE_MINIMUM) if (new_sy < PANE_MINIMUM)
new_sy = PANE_MINIMUM; new_sy = PANE_MINIMUM;
new_xoff = x + 1; /* because mouse is on border at xoff - 1 */ new_xoff = x + 1; /* because mouse is on border at xoff - 1 */
@@ -284,65 +283,65 @@ cmd_resize_pane_mouse_resize_move_floating(struct client *c,
} else if ((lx == right + 1 || lx == right) && } else if ((lx == right + 1 || lx == right) &&
ly == wp->yoff - 1) { ly == wp->yoff - 1) {
/* Top right corner. */ /* Top right corner. */
new_sx = x - lc->g.xoff; new_sx = x - lc->xoff;
if (new_sx < PANE_MINIMUM) if (new_sx < PANE_MINIMUM)
new_sx = PANE_MINIMUM; new_sx = PANE_MINIMUM;
new_sy = lc->g.sy + (ly - y); new_sy = lc->sy + (ly - y);
if (new_sy < PANE_MINIMUM) if (new_sy < PANE_MINIMUM)
new_sy = PANE_MINIMUM; new_sy = PANE_MINIMUM;
new_yoff = y + 1; new_yoff = y + 1;
layout_set_size(lc, new_sx, new_sy, lc->g.xoff, new_yoff); layout_set_size(lc, new_sx, new_sy, lc->xoff, new_yoff);
resizes++; resizes++;
} else if ((lx == left || lx == left + 1) && } else if ((lx == left || lx == left + 1) &&
ly == wp->yoff + sy) { ly == wp->yoff + sy) {
/* Bottom left corner. */ /* Bottom left corner. */
new_sx = lc->g.sx + (lx - x); new_sx = lc->sx + (lx - x);
if (new_sx < PANE_MINIMUM) if (new_sx < PANE_MINIMUM)
new_sx = PANE_MINIMUM; new_sx = PANE_MINIMUM;
new_sy = y - lc->g.yoff; new_sy = y - lc->yoff;
if (new_sy < PANE_MINIMUM) if (new_sy < PANE_MINIMUM)
return; return;
new_xoff = x + 1; new_xoff = x + 1;
layout_set_size(lc, new_sx, new_sy, new_xoff, lc->g.yoff); layout_set_size(lc, new_sx, new_sy, new_xoff, lc->yoff);
resizes++; resizes++;
} else if ((lx == right + 1 || lx == right) && } else if ((lx == right + 1 || lx == right) &&
ly == wp->yoff + sy) { ly == wp->yoff + sy) {
/* Bottom right corner. */ /* Bottom right corner. */
new_sx = x - lc->g.xoff; new_sx = x - lc->xoff;
if (new_sx < PANE_MINIMUM) if (new_sx < PANE_MINIMUM)
new_sx = PANE_MINIMUM; new_sx = PANE_MINIMUM;
new_sy = y - lc->g.yoff; new_sy = y - lc->yoff;
if (new_sy < PANE_MINIMUM) if (new_sy < PANE_MINIMUM)
new_sy = PANE_MINIMUM; new_sy = PANE_MINIMUM;
layout_set_size(lc, new_sx, new_sy, lc->g.xoff, lc->g.yoff); layout_set_size(lc, new_sx, new_sy, lc->xoff, lc->yoff);
resizes++; resizes++;
} else if (lx == right) { } else if (lx == right) {
/* Right border. */ /* Right border. */
new_sx = x - lc->g.xoff; new_sx = x - lc->xoff;
if (new_sx < PANE_MINIMUM) if (new_sx < PANE_MINIMUM)
return; return;
layout_set_size(lc, new_sx, lc->g.sy, lc->g.xoff, lc->g.yoff); layout_set_size(lc, new_sx, lc->sy, lc->xoff, lc->yoff);
resizes++; resizes++;
} else if (lx == left) { } else if (lx == left) {
/* Left border. */ /* Left border. */
new_sx = lc->g.sx + (lx - x); new_sx = lc->sx + (lx - x);
if (new_sx < PANE_MINIMUM) if (new_sx < PANE_MINIMUM)
return; return;
new_xoff = x + 1; new_xoff = x + 1;
layout_set_size(lc, new_sx, lc->g.sy, new_xoff, lc->g.yoff); layout_set_size(lc, new_sx, lc->sy, new_xoff, lc->yoff);
resizes++; resizes++;
} else if (ly == wp->yoff + sy) { } else if (ly == wp->yoff + sy) {
/* Bottom border. */ /* Bottom border. */
new_sy = y - lc->g.yoff; new_sy = y - lc->yoff;
if (new_sy < PANE_MINIMUM) if (new_sy < PANE_MINIMUM)
return; return;
layout_set_size(lc, lc->g.sx, new_sy, lc->g.xoff, lc->g.yoff); layout_set_size(lc, lc->sx, new_sy, lc->xoff, lc->yoff);
resizes++; resizes++;
} else if (ly == wp->yoff - 1) { } else if (ly == wp->yoff - 1) {
/* Top border (move instead of resize). */ /* Top border (move instead of resize). */
new_xoff = lc->g.xoff + (x - lx); new_xoff = lc->xoff + (x - lx);
new_yoff = y + 1; new_yoff = y + 1;
layout_set_size(lc, lc->g.sx, lc->g.sy, new_xoff, new_yoff); layout_set_size(lc, lc->sx, lc->sy, new_xoff, new_yoff);
resizes++; resizes++;
} }
if (resizes != 0) { if (resizes != 0) {

View File

@@ -1,4 +1,4 @@
/* $OpenBSD: cmd-resize-window.c,v 1.10 2023/06/30 13:19:32 nicm Exp $ */ /* $OpenBSD$ */
/* /*
* Copyright (c) 2018 Nicholas Marriott <nicholas.marriott@gmail.com> * Copyright (c) 2018 Nicholas Marriott <nicholas.marriott@gmail.com>

View File

@@ -1,4 +1,4 @@
/* $OpenBSD: cmd-respawn-pane.c,v 1.40 2026/07/03 16:09:49 nicm Exp $ */ /* $OpenBSD$ */
/* /*
* Copyright (c) 2008 Nicholas Marriott <nicholas.marriott@gmail.com> * Copyright (c) 2008 Nicholas Marriott <nicholas.marriott@gmail.com>
@@ -34,8 +34,8 @@ const struct cmd_entry cmd_respawn_pane_entry = {
.name = "respawn-pane", .name = "respawn-pane",
.alias = "respawnp", .alias = "respawnp",
.args = { "c:e:Ekt:", 0, -1, NULL }, .args = { "c:e:kt:", 0, -1, NULL },
.usage = "[-Ek] [-c start-directory] [-e environment] " .usage = "[-k] [-c start-directory] [-e environment] "
CMD_TARGET_PANE_USAGE " [shell-command [argument ...]]", CMD_TARGET_PANE_USAGE " [shell-command [argument ...]]",
.target = { 't', CMD_FIND_PANE, 0 }, .target = { 't', CMD_FIND_PANE, 0 },
@@ -75,8 +75,6 @@ cmd_respawn_pane_exec(struct cmd *self, struct cmdq_item *item)
sc.cwd = args_get(args, 'c'); sc.cwd = args_get(args, 'c');
sc.flags = SPAWN_RESPAWN; sc.flags = SPAWN_RESPAWN;
if (args_has(args, 'E'))
sc.flags |= SPAWN_EMPTY;
if (args_has(args, 'k')) if (args_has(args, 'k'))
sc.flags |= SPAWN_KILL; sc.flags |= SPAWN_KILL;

View File

@@ -1,4 +1,4 @@
/* $OpenBSD: cmd-respawn-window.c,v 1.51 2026/07/03 16:09:49 nicm Exp $ */ /* $OpenBSD$ */
/* /*
* Copyright (c) 2008 Nicholas Marriott <nicholas.marriott@gmail.com> * Copyright (c) 2008 Nicholas Marriott <nicholas.marriott@gmail.com>
@@ -34,8 +34,8 @@ const struct cmd_entry cmd_respawn_window_entry = {
.name = "respawn-window", .name = "respawn-window",
.alias = "respawnw", .alias = "respawnw",
.args = { "c:e:Ekt:", 0, -1, NULL }, .args = { "c:e:kt:", 0, -1, NULL },
.usage = "[-Ek] [-c start-directory] [-e environment] " .usage = "[-k] [-c start-directory] [-e environment] "
CMD_TARGET_WINDOW_USAGE " [shell-command [argument ...]]", CMD_TARGET_WINDOW_USAGE " [shell-command [argument ...]]",
.target = { 't', CMD_FIND_WINDOW, 0 }, .target = { 't', CMD_FIND_WINDOW, 0 },
@@ -74,8 +74,6 @@ cmd_respawn_window_exec(struct cmd *self, struct cmdq_item *item)
sc.cwd = args_get(args, 'c'); sc.cwd = args_get(args, 'c');
sc.flags = SPAWN_RESPAWN; sc.flags = SPAWN_RESPAWN;
if (args_has(args, 'E'))
sc.flags |= SPAWN_EMPTY;
if (args_has(args, 'k')) if (args_has(args, 'k'))
sc.flags |= SPAWN_KILL; sc.flags |= SPAWN_KILL;

View File

@@ -1,4 +1,4 @@
/* $OpenBSD: cmd-rotate-window.c,v 1.34 2026/06/22 08:47:45 nicm Exp $ */ /* $OpenBSD$ */
/* /*
* Copyright (c) 2009 Nicholas Marriott <nicholas.marriott@gmail.com> * Copyright (c) 2009 Nicholas Marriott <nicholas.marriott@gmail.com>

View File

@@ -1,4 +1,4 @@
/* $OpenBSD: cmd-run-shell.c,v 1.94 2026/08/25 06:04:33 nicm Exp $ */ /* $OpenBSD$ */
/* /*
* Copyright (c) 2009 Tiago Cunha <me@tiagocunha.org> * Copyright (c) 2009 Tiago Cunha <me@tiagocunha.org>
@@ -79,10 +79,7 @@ static void
cmd_run_shell_print(struct job *job, const char *msg) cmd_run_shell_print(struct job *job, const char *msg)
{ {
struct cmd_run_shell_data *cdata = job_get_data(job); struct cmd_run_shell_data *cdata = job_get_data(job);
struct client *c;
struct session *s;
struct window_pane *wp = NULL; struct window_pane *wp = NULL;
struct window *w;
struct cmd_find_state fs; struct cmd_find_state fs;
struct window_mode_entry *wme; struct window_mode_entry *wme;
@@ -93,12 +90,8 @@ cmd_run_shell_print(struct job *job, const char *msg)
cmdq_print(cdata->item, "%s", msg); cmdq_print(cdata->item, "%s", msg);
return; return;
} }
c = cdata->client; if (cdata->item != NULL && cdata->client != NULL)
if (c != NULL && c->session != NULL) { wp = server_client_get_pane(cdata->client);
s = c->session;
w = active_get_effective_window(c, s);
wp = w->active;
}
if (wp == NULL && cmd_find_from_nothing(&fs, 0) == 0) if (wp == NULL && cmd_find_from_nothing(&fs, 0) == 0)
wp = fs.wp; wp = fs.wp;
if (wp == NULL) if (wp == NULL)
@@ -107,8 +100,7 @@ cmd_run_shell_print(struct job *job, const char *msg)
wme = TAILQ_FIRST(&wp->modes); wme = TAILQ_FIRST(&wp->modes);
if (wme == NULL || wme->mode != &window_view_mode) if (wme == NULL || wme->mode != &window_view_mode)
window_pane_set_mode(wp, NULL, &window_view_mode, NULL, NULL, window_pane_set_mode(wp, NULL, &window_view_mode, NULL, NULL);
NULL);
window_copy_add(wp, 1, "%s", msg); window_copy_add(wp, 1, "%s", msg);
} }
@@ -240,11 +232,9 @@ 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)

View File

@@ -1,4 +1,4 @@
/* $OpenBSD: cmd-save-buffer.c,v 1.57 2025/10/28 07:32:26 nicm Exp $ */ /* $OpenBSD$ */
/* /*
* Copyright (c) 2009 Tiago Cunha <me@tiagocunha.org> * Copyright (c) 2009 Tiago Cunha <me@tiagocunha.org>

View File

@@ -1,4 +1,4 @@
/* $OpenBSD: cmd-select-layout.c,v 1.43 2026/07/10 13:38:45 nicm Exp $ */ /* $OpenBSD$ */
/* /*
* Copyright (c) 2009 Nicholas Marriott <nicholas.marriott@gmail.com> * Copyright (c) 2009 Nicholas Marriott <nicholas.marriott@gmail.com>
@@ -139,7 +139,7 @@ changed:
free(oldlayout); free(oldlayout);
recalculate_sizes(); recalculate_sizes();
server_redraw_window(w); server_redraw_window(w);
events_fire_window("window-layout-changed", w); notify_window("window-layout-changed", w);
return (CMD_RETURN_NORMAL); return (CMD_RETURN_NORMAL);
error: error:

View File

@@ -1,4 +1,4 @@
/* $OpenBSD: cmd-select-pane.c,v 1.78 2026/08/20 09:19:24 nicm Exp $ */ /* $OpenBSD$ */
/* /*
* Copyright (c) 2009 Nicholas Marriott <nicholas.marriott@gmail.com> * Copyright (c) 2009 Nicholas Marriott <nicholas.marriott@gmail.com>
@@ -68,11 +68,10 @@ cmd_select_pane_redraw(struct window *w)
TAILQ_FOREACH(c, &clients, entry) { TAILQ_FOREACH(c, &clients, entry) {
if (c->session == NULL || (c->flags & CLIENT_CONTROL)) if (c->session == NULL || (c->flags & CLIENT_CONTROL))
continue; continue;
if (active_get_effective_window(c, c->session) == w && if (c->session->curw->window == w && tty_window_bigger(&c->tty))
tty_window_bigger(&c->tty))
server_redraw_client(c); server_redraw_client(c);
else { else {
if (active_get_effective_window(c, c->session) == w) if (c->session->curw->window == w)
c->flags |= CLIENT_REDRAWBORDERS; c->flags |= CLIENT_REDRAWBORDERS;
if (session_has(c->session, w)) if (session_has(c->session, w))
c->flags |= CLIENT_REDRAWSTATUS; c->flags |= CLIENT_REDRAWSTATUS;
@@ -81,69 +80,6 @@ cmd_select_pane_redraw(struct window *w)
} }
} }
static enum cmd_retval
cmd_select_pane_marked_pane(struct cmd *self, struct cmdq_item *item)
{
struct args *args = cmd_get_args(self);
struct cmd_find_state *target = cmdq_get_target(item);
struct event_payload *ep;
struct cmd_find_state fs;
struct winlink *wl = target->wl;
struct window_pane *wp = target->wp, *lwp = NULL, *mwp;
struct session *s = target->s;
if (args_has(args, 'm') && !window_pane_is_visible(wp))
return (CMD_RETURN_NORMAL);
if (server_check_marked())
lwp = marked_pane.wp;
if (args_has(args, 'M') || server_is_marked(s, wl, wp))
server_clear_marked();
else
server_set_marked(s, wl, wp);
mwp = marked_pane.wp;
ep = event_payload_create();
if (mwp != NULL)
cmd_find_from_pane(&fs, mwp, 0);
else if (lwp != NULL)
cmd_find_from_pane(&fs, lwp, 0);
else
cmd_find_from_pane(&fs, wp, 0);
event_payload_set_target(ep, &fs);
if (mwp != NULL) {
event_payload_set_pane(ep, "pane", mwp);
event_payload_set_pane(ep, "new_pane", mwp);
event_payload_set_window(ep, "window", mwp->window);
} else if (lwp != NULL) {
event_payload_set_pane(ep, "pane", lwp);
event_payload_set_window(ep, "window", lwp->window);
} else {
event_payload_set_pane(ep, "pane", wp);
event_payload_set_window(ep, "window", wp->window);
}
if (lwp != NULL)
event_payload_set_pane(ep, "old_pane", lwp);
event_payload_set_int(ep, "marked", mwp != NULL);
events_fire("marked-pane-changed", ep);
if (lwp != NULL) {
lwp->flags |= (PANE_REDRAW|PANE_STYLECHANGED|PANE_THEMECHANGED);
server_redraw_window_borders(lwp->window);
server_status_window(lwp->window);
}
if (mwp != NULL) {
mwp->flags |= (PANE_REDRAW|PANE_STYLECHANGED|PANE_THEMECHANGED);
server_redraw_window_borders(mwp->window);
server_status_window(mwp->window);
}
if (window_pane_is_floating(wp)) {
window_redraw_active_switch(wp->window, wp);
window_set_active_pane(wp->window, wp, 1);
}
return (CMD_RETURN_NORMAL);
}
static enum cmd_retval static enum cmd_retval
cmd_select_pane_exec(struct cmd *self, struct cmdq_item *item) cmd_select_pane_exec(struct cmd *self, struct cmdq_item *item)
{ {
@@ -151,17 +87,15 @@ cmd_select_pane_exec(struct cmd *self, struct cmdq_item *item)
const struct cmd_entry *entry = cmd_get_entry(self); const struct cmd_entry *entry = cmd_get_entry(self);
struct cmd_find_state *current = cmdq_get_current(item); struct cmd_find_state *current = cmdq_get_current(item);
struct cmd_find_state *target = cmdq_get_target(item); struct cmd_find_state *target = cmdq_get_target(item);
struct event_payload *ep; struct client *c = cmdq_get_client(item);
struct cmd_find_state fs;
struct winlink *wl = target->wl; struct winlink *wl = target->wl;
struct window *w = wl->window; struct window *w = wl->window;
struct session *s = target->s; struct session *s = target->s;
struct window_pane *wp = target->wp, *lastwp; struct window_pane *wp = target->wp, *activewp, *lastwp, *markedwp;
struct options *oo = wp->options; struct options *oo = wp->options;
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')) {
/* /*
@@ -187,25 +121,51 @@ 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 (w->modal != NULL && lastwp != w->modal) if (window_push_zoom(w, 0, args_has(args, 'Z')))
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 (!visible && window_pop_zoom(w)) if (window_pop_zoom(w))
server_redraw_window(w); server_redraw_window(w);
} }
return (CMD_RETURN_NORMAL); return (CMD_RETURN_NORMAL);
} }
if (args_has(args, 'm') || args_has(args, 'M')) if (args_has(args, 'm') || args_has(args, 'M')) {
return (cmd_select_pane_marked_pane(self, item)); if (args_has(args, 'm') && !window_pane_is_visible(wp))
return (CMD_RETURN_NORMAL);
if (server_check_marked())
lastwp = marked_pane.wp;
else
lastwp = NULL;
if (args_has(args, 'M') || server_is_marked(s, wl, wp))
server_clear_marked();
else
server_set_marked(s, wl, wp);
markedwp = marked_pane.wp;
if (lastwp != NULL) {
lastwp->flags |= (PANE_REDRAW|PANE_STYLECHANGED|
PANE_THEMECHANGED);
server_redraw_window_borders(lastwp->window);
server_status_window(lastwp->window);
}
if (markedwp != NULL) {
markedwp->flags |= (PANE_REDRAW|PANE_STYLECHANGED|
PANE_THEMECHANGED);
server_redraw_window_borders(markedwp->window);
server_status_window(markedwp->window);
}
if (window_pane_is_floating(wp)) {
window_redraw_active_switch(w, wp);
window_set_active_pane(w, wp, 1);
}
return (CMD_RETURN_NORMAL);
}
style = args_get(args, 'P'); style = args_get(args, 'P');
if (style != NULL) { if (style != NULL) {
@@ -258,13 +218,7 @@ cmd_select_pane_exec(struct cmd *self, struct cmdq_item *item)
if (args_has(args, 'T')) { if (args_has(args, 'T')) {
title = format_single_from_target(item, args_get(args, 'T')); title = format_single_from_target(item, args_get(args, 'T'));
if (screen_set_title(&wp->base, title, 0)) { if (screen_set_title(&wp->base, title, 0)) {
ep = event_payload_create(); notify_pane("pane-title-changed", wp);
cmd_find_from_pane(&fs, wp, 0);
event_payload_set_target(ep, &fs);
event_payload_set_pane(ep, "pane", wp);
event_payload_set_window(ep, "window", wp->window);
event_payload_set_string(ep, "new_title", "%s", title);
events_fire("pane-title-changed", ep);
server_redraw_window_borders(wp->window); server_redraw_window_borders(wp->window);
server_status_window(wp->window); server_status_window(wp->window);
} }
@@ -272,20 +226,22 @@ cmd_select_pane_exec(struct cmd *self, struct cmdq_item *item)
return (CMD_RETURN_NORMAL); return (CMD_RETURN_NORMAL);
} }
if (wp == w->active) if (c != NULL && c->session != NULL && (c->flags & CLIENT_ACTIVEPANE))
return (CMD_RETURN_NORMAL); activewp = server_client_get_pane(c);
if (w->modal != NULL && wp != w->modal)
visible = 1;
else else
visible = window_pane_is_visible(wp); activewp = w->active;
if (!visible && window_push_zoom(w, 0, Zflag)) if (wp == activewp)
return (CMD_RETURN_NORMAL);
if (window_push_zoom(w, 0, args_has(args, 'Z')))
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 (c != NULL && c->session != NULL && (c->flags & CLIENT_ACTIVEPANE))
server_client_set_pane(c, wp);
else 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 (!visible && window_pop_zoom(w)) if (window_pop_zoom(w))
server_redraw_window(w); server_redraw_window(w);
return (CMD_RETURN_NORMAL); return (CMD_RETURN_NORMAL);

View File

@@ -1,4 +1,4 @@
/* $OpenBSD: cmd-select-window.c,v 1.30 2021/08/21 10:22:39 nicm Exp $ */ /* $OpenBSD$ */
/* /*
* Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com> * Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>
@@ -33,8 +33,8 @@ const struct cmd_entry cmd_select_window_entry = {
.name = "select-window", .name = "select-window",
.alias = "selectw", .alias = "selectw",
.args = { "LlSnpTt:", 0, 0, NULL }, .args = { "lnpTt:", 0, 0, NULL },
.usage = "[-LlnpST] " CMD_TARGET_WINDOW_USAGE, .usage = "[-lnpT] " CMD_TARGET_WINDOW_USAGE,
.target = { 't', CMD_FIND_WINDOW, 0 }, .target = { 't', CMD_FIND_WINDOW, 0 },
@@ -88,9 +88,8 @@ cmd_select_window_exec(struct cmd *self, struct cmdq_item *item)
struct client *c = cmdq_get_client(item); struct client *c = cmdq_get_client(item);
struct cmd_find_state *current = cmdq_get_current(item); struct cmd_find_state *current = cmdq_get_current(item);
struct cmd_find_state *target = cmdq_get_target(item); struct cmd_find_state *target = cmdq_get_target(item);
struct winlink *wl = target->wl, *current_wl; struct winlink *wl = target->wl;
struct session *s = target->s; struct session *s = target->s;
enum active_window_mode mode;
int next, previous, last, activity; int next, previous, last, activity;
next = (cmd_get_entry(self) == &cmd_next_window_entry); next = (cmd_get_entry(self) == &cmd_next_window_entry);
@@ -103,104 +102,48 @@ cmd_select_window_exec(struct cmd *self, struct cmdq_item *item)
if (args_has(args, 'l')) if (args_has(args, 'l'))
last = 1; last = 1;
if (args_has(args, 'L') || args_has(args, 'S')) {
if (c == NULL || c->session == NULL) {
cmdq_error(item, "no current client");
return (CMD_RETURN_ERROR);
}
if (args_has(args, 'S'))
mode = ACTIVE_SHARED;
else
mode = ACTIVE_LOCAL;
active_set_local_window(c, s, mode);
if (!next &&
!previous &&
!last &&
!args_has(args, 't') &&
!args_has(args, 'T')) {
if (current->s == s) {
if (c->session == s)
cmd_find_from_client(current, c, 0);
else
cmd_find_from_session(current, s, 0);
}
if (c->session == s)
server_redraw_client(c);
else
server_redraw_session(s);
cmdq_insert_hook(s, item, current, "after-select-window");
recalculate_sizes();
return (CMD_RETURN_NORMAL);
}
}
if (active_is_local_window(c, s))
mode = ACTIVE_LOCAL;
else
mode = ACTIVE_SHARED;
if (next || previous || last) { if (next || previous || last) {
activity = args_has(args, 'a'); activity = args_has(args, 'a');
if (next) { if (next) {
if (active_next_window(c, s, activity) != 0) { if (session_next(s, activity) != 0) {
cmdq_error(item, "no next window"); cmdq_error(item, "no next window");
return (CMD_RETURN_ERROR); return (CMD_RETURN_ERROR);
} }
} else if (previous) { } else if (previous) {
if (active_previous_window(c, s, activity) != 0) { if (session_previous(s, activity) != 0) {
cmdq_error(item, "no previous window"); cmdq_error(item, "no previous window");
return (CMD_RETURN_ERROR); return (CMD_RETURN_ERROR);
} }
} else { } else {
if (active_last_window(c, s) != 0) { if (session_last(s) != 0) {
cmdq_error(item, "no last window"); cmdq_error(item, "no last window");
return (CMD_RETURN_ERROR); return (CMD_RETURN_ERROR);
} }
} }
if (mode == ACTIVE_LOCAL && c != NULL && c->session == s) cmd_find_from_session(current, s, 0);
cmd_find_from_client(current, c, 0); server_redraw_session(s);
else
cmd_find_from_session(current, s, 0);
if (mode == ACTIVE_LOCAL && c != NULL && c->session == s)
server_redraw_client(c);
else
server_redraw_session(s);
cmdq_insert_hook(s, item, current, "after-select-window"); cmdq_insert_hook(s, item, current, "after-select-window");
} else { } else {
/* /*
* If -T and select-window is invoked on same window as * If -T and select-window is invoked on same window as
* current, switch to previous window. * current, switch to previous window.
*/ */
current_wl = active_get_effective_winlink(c, s); if (args_has(args, 'T') && wl == s->curw) {
if (args_has(args, 'T') && wl == current_wl) { if (session_last(s) != 0) {
if (active_last_window(c, s) != 0) {
cmdq_error(item, "no last window"); cmdq_error(item, "no last window");
return (-1); return (-1);
} }
if (current->s == s) { if (current->s == s)
if (mode == ACTIVE_LOCAL && c != NULL &&
c->session == s)
cmd_find_from_client(current, c, 0);
else
cmd_find_from_session(current, s, 0);
}
if (mode == ACTIVE_LOCAL && c != NULL && c->session == s)
server_redraw_client(c);
else
server_redraw_session(s);
} else if (active_select_window(c, s, wl) == 0) {
if (mode == ACTIVE_LOCAL && c != NULL && c->session == s)
cmd_find_from_client(current, c, 0);
else
cmd_find_from_session(current, s, 0); cmd_find_from_session(current, s, 0);
if (mode == ACTIVE_LOCAL && c != NULL && c->session == s) server_redraw_session(s);
server_redraw_client(c); } else if (session_select(s, wl->idx) == 0) {
else cmd_find_from_session(current, s, 0);
server_redraw_session(s); server_redraw_session(s);
} }
cmdq_insert_hook(s, item, current, "after-select-window"); cmdq_insert_hook(s, item, current, "after-select-window");
} }
if (c != NULL && c->session != NULL) if (c != NULL && c->session != NULL)
active_get_effective_window(c, s)->latest = c; s->curw->window->latest = c;
recalculate_sizes(); recalculate_sizes();
return (CMD_RETURN_NORMAL); return (CMD_RETURN_NORMAL);

View File

@@ -1,4 +1,4 @@
/* $OpenBSD: cmd-send-keys.c,v 1.81 2026/06/11 19:13:34 nicm Exp $ */ /* $OpenBSD$ */
/* /*
* Copyright (c) 2008 Nicholas Marriott <nicholas.marriott@gmail.com> * Copyright (c) 2008 Nicholas Marriott <nicholas.marriott@gmail.com>

View File

@@ -1,4 +1,4 @@
/* $OpenBSD: cmd-server-access.c,v 1.6 2026/06/09 12:58:40 nicm Exp $ */ /* $OpenBSD$ */
/* /*
* Copyright (c) 2021 Dallas Lyons <dallasdlyons@gmail.com> * Copyright (c) 2021 Dallas Lyons <dallasdlyons@gmail.com>

View File

@@ -1,4 +1,4 @@
/* $OpenBSD: cmd-set-buffer.c,v 1.38 2026/08/24 07:05:23 nicm Exp $ */ /* $OpenBSD$ */
/* /*
* Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com> * Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>
@@ -100,7 +100,6 @@ 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);
} }
@@ -108,10 +107,8 @@ 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);
@@ -130,7 +127,6 @@ 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:

View File

@@ -1,4 +1,4 @@
/* $OpenBSD: cmd-set-environment.c,v 1.29 2025/04/09 06:27:43 nicm Exp $ */ /* $OpenBSD$ */
/* /*
* Copyright (c) 2009 Nicholas Marriott <nicholas.marriott@gmail.com> * Copyright (c) 2009 Nicholas Marriott <nicholas.marriott@gmail.com>

View File

@@ -1,4 +1,4 @@
/* $OpenBSD: cmd-set-option.c,v 1.146 2026/07/10 15:20:06 nicm Exp $ */ /* $OpenBSD$ */
/* /*
* Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com> * Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>
@@ -31,10 +31,6 @@ static enum args_parse_type cmd_set_option_args_parse(struct args *,
u_int, char **); u_int, char **);
static enum cmd_retval cmd_set_option_exec(struct cmd *, static enum cmd_retval cmd_set_option_exec(struct cmd *,
struct cmdq_item *); struct cmdq_item *);
static enum cmd_retval cmd_set_hook_event_exec(struct cmd *,
struct cmdq_item *);
static enum cmd_retval cmd_set_hook_monitor_exec(struct cmdq_item *,
struct args *, int);
const struct cmd_entry cmd_set_option_entry = { const struct cmd_entry cmd_set_option_entry = {
.name = "set-option", .name = "set-option",
@@ -66,9 +62,8 @@ const struct cmd_entry cmd_set_hook_entry = {
.name = "set-hook", .name = "set-hook",
.alias = NULL, .alias = NULL,
.args = { "agpERTt:uB:w", 0, 2, cmd_set_option_args_parse }, .args = { "agpRt:uw", 1, 2, cmd_set_option_args_parse },
.usage = "[-agpERTuw] [-B name:what:format] " CMD_TARGET_PANE_USAGE " " .usage = "[-agpRuw] " CMD_TARGET_PANE_USAGE " hook [command]",
"[hook] [command]",
.target = { 't', CMD_FIND_PANE, CMD_FIND_CANFAIL }, .target = { 't', CMD_FIND_PANE, CMD_FIND_CANFAIL },
@@ -77,152 +72,14 @@ const struct cmd_entry cmd_set_hook_entry = {
}; };
static enum args_parse_type static enum args_parse_type
cmd_set_option_args_parse(struct args *args, u_int idx, cmd_set_option_args_parse(__unused struct args *args, u_int idx,
__unused char **cause) __unused char **cause)
{ {
if (args_has(args, 'B'))
return (ARGS_PARSE_COMMANDS_OR_STRING);
if (idx == 1) if (idx == 1)
return (ARGS_PARSE_COMMANDS_OR_STRING); return (ARGS_PARSE_COMMANDS_OR_STRING);
return (ARGS_PARSE_STRING); return (ARGS_PARSE_STRING);
} }
static enum cmd_retval
cmd_set_hook_event_exec(struct cmd *self, struct cmdq_item *item)
{
struct args *args = cmd_get_args(self);
struct cmd_find_state *target = cmdq_get_target(item);
struct event_payload *ep;
struct client *c;
char *argument;
if (args_count(args) == 0) {
cmdq_error(item, "missing argument");
return (CMD_RETURN_ERROR);
}
if (args_count(args) != 1) {
cmdq_error(item, "too many arguments");
return (CMD_RETURN_ERROR);
}
argument = format_single_from_target(item, args_string(args, 0));
if (*argument != '@') {
cmdq_error(item, "event name must start with @");
free(argument);
return (CMD_RETURN_ERROR);
}
ep = event_payload_create();
event_payload_set_target(ep, target);
c = cmdq_get_client(item);
if (c != NULL)
event_payload_set_client(ep, "client", c);
if (target->s != NULL)
event_payload_set_session(ep, "session", target->s);
if (target->w != NULL)
event_payload_set_window(ep, "window", target->w);
if (target->wl != NULL)
event_payload_set_int(ep, "window_index", target->wl->idx);
else if (target->idx != -1)
event_payload_set_int(ep, "window_index", target->idx);
if (target->wp != NULL)
event_payload_set_pane(ep, "pane", target->wp);
events_fire(argument, ep);
free(argument);
return (CMD_RETURN_NORMAL);
}
static enum cmd_retval
cmd_set_hook_monitor_exec(struct cmdq_item *item, struct args *args, int window)
{
struct cmd_find_state *target = cmdq_get_target(item), fs;
struct options *oo;
struct options_entry *o;
struct session *s = NULL;
char *cause = NULL, *name = NULL, *format = NULL;
char *expanded = NULL, *newvalue = NULL;
const char *value, *old;
enum monitor_type type;
int id, scope, flags = 0;
if (args_count(args) > 1) {
cmdq_error(item, "too many arguments");
return (CMD_RETURN_ERROR);
}
value = args_get(args, 'B');
if (args_has(args, 'u')) {
if (monitor_parse(value, &name, &type, &id, &format) != 0)
name = xstrdup(value);
free(format);
format = NULL;
} else {
if (monitor_parse(value, &name, &type, &id, &format) != 0) {
cmdq_error(item, "invalid subscription: %s", value);
return (CMD_RETURN_ERROR);
}
}
if (*name != '@') {
cmdq_error(item, "monitor hook name must start with @");
goto fail;
}
scope = options_scope_from_name(args, window, name, target, &oo,
&cause);
if (scope == OPTIONS_TABLE_NONE) {
cmdq_error(item, "%s", cause);
free(cause);
goto fail;
}
cmd_find_copy_state(&fs, target);
if (args_has(args, 'u')) {
hooks_monitor_remove(oo, name);
goto out;
}
if (args_count(args) != 0) {
value = args_string(args, 0);
if (args_has(args, 'F')) {
expanded = format_single_from_target(item, value);
value = expanded;
}
o = options_get_only(oo, name);
if (!args_has(args, 'o') || o == NULL) {
if (args_has(args, 'a') && o != NULL) {
old = options_get_string(oo, name);
xasprintf(&newvalue, "%s%s", old, value);
value = newvalue;
}
options_set_string(oo, name, 0, "%s", value);
options_push_changes(name);
}
}
if (oo != global_options &&
oo != global_s_options &&
oo != global_w_options)
s = target->s;
if (args_has(args, 'T'))
flags |= MONITOR_NOTIFY_TRUE;
hooks_monitor_add(item, oo, name, type, id, format, flags, &fs, s);
out:
free(newvalue);
free(expanded);
free(name);
free(format);
return (CMD_RETURN_NORMAL);
fail:
free(newvalue);
free(expanded);
free(name);
free(format);
return (CMD_RETURN_ERROR);
}
static enum cmd_retval static enum cmd_retval
cmd_set_option_exec(struct cmd *self, struct cmdq_item *item) cmd_set_option_exec(struct cmd *self, struct cmdq_item *item)
{ {
@@ -232,34 +89,26 @@ cmd_set_option_exec(struct cmd *self, struct cmdq_item *item)
struct window_pane *loop; struct window_pane *loop;
struct options *oo; struct options *oo;
struct options_entry *parent, *o, *po; struct options_entry *parent, *o, *po;
char *name, *argument, *cause; char *name, *argument, *expanded = NULL;
char *expanded = NULL, *array_key = NULL; char *cause;
const char *value; const char *value;
int window, already, error, ambiguous; int window, idx, already, error, ambiguous;
int scope; int scope;
window = (cmd_get_entry(self) == &cmd_set_window_option_entry); window = (cmd_get_entry(self) == &cmd_set_window_option_entry);
if (cmd_get_entry(self) == &cmd_set_hook_entry && args_has(args, 'E'))
return (cmd_set_hook_event_exec(self, item));
if (cmd_get_entry(self) == &cmd_set_hook_entry && args_has(args, 'B'))
return (cmd_set_hook_monitor_exec(item, args, window));
if (args_count(args) == 0) {
cmdq_error(item, "missing argument");
return (CMD_RETURN_ERROR);
}
/* Expand argument. */ /* Expand argument. */
argument = format_single_from_target(item, args_string(args, 0)); argument = format_single_from_target(item, args_string(args, 0));
/* If set-hook -R, fire the hook straight away. */ /* If set-hook -R, fire the hook straight away. */
if (cmd_get_entry(self) == &cmd_set_hook_entry && args_has(args, 'R')) { if (cmd_get_entry(self) == &cmd_set_hook_entry && args_has(args, 'R')) {
hooks_run(item, argument); notify_hook(item, argument);
free(argument); free(argument);
return (CMD_RETURN_NORMAL); return (CMD_RETURN_NORMAL);
} }
/* Parse option name and array key. */ /* Parse option name and index. */
name = options_match(argument, &array_key, &ambiguous); name = options_match(argument, &idx, &ambiguous);
if (name == NULL) { if (name == NULL) {
if (args_has(args, 'q')) if (args_has(args, 'q'))
goto out; goto out;
@@ -291,23 +140,21 @@ cmd_set_option_exec(struct cmd *self, struct cmdq_item *item)
o = options_get_only(oo, name); o = options_get_only(oo, name);
parent = options_get(oo, name); parent = options_get(oo, name);
/* Check that array options and keys match up. */ /* Check that array options and indexes match up. */
if (array_key != NULL && (*name == '@' || !options_is_array(parent))) { if (idx != -1 && (*name == '@' || !options_is_array(parent))) {
cmdq_error(item, "not an array: %s", argument); cmdq_error(item, "not an array: %s", argument);
goto fail; goto fail;
} }
/* With -o, check this option is not already set. */ /* With -o, check this option is not already set. */
if (!args_has(args, 'u') && args_has(args, 'o')) { if (!args_has(args, 'u') && args_has(args, 'o')) {
if (array_key == NULL) if (idx == -1)
already = (o != NULL); already = (o != NULL);
else { else {
if (o == NULL) if (o == NULL)
already = 0; already = 0;
else if (options_array_get(o, array_key) != NULL)
already = 1;
else else
already = 0; already = (options_array_get(o, idx) != NULL);
} }
if (already) { if (already) {
if (args_has(args, 'q')) if (args_has(args, 'q'))
@@ -323,8 +170,7 @@ cmd_set_option_exec(struct cmd *self, struct cmdq_item *item)
po = options_get_only(loop->options, name); po = options_get_only(loop->options, name);
if (po == NULL) if (po == NULL)
continue; continue;
if (options_remove_or_default(po, array_key, if (options_remove_or_default(po, idx, &cause) != 0) {
&cause) != 0) {
cmdq_error(item, "%s", cause); cmdq_error(item, "%s", cause);
free(cause); free(cause);
goto fail; goto fail;
@@ -334,7 +180,7 @@ cmd_set_option_exec(struct cmd *self, struct cmdq_item *item)
if (args_has(args, 'u') || args_has(args, 'U')) { if (args_has(args, 'u') || args_has(args, 'U')) {
if (o == NULL) if (o == NULL)
goto out; goto out;
if (options_remove_or_default(o, array_key, &cause) != 0) { if (options_remove_or_default(o, idx, &cause) != 0) {
cmdq_error(item, "%s", cause); cmdq_error(item, "%s", cause);
free(cause); free(cause);
goto fail; goto fail;
@@ -345,9 +191,7 @@ cmd_set_option_exec(struct cmd *self, struct cmdq_item *item)
goto fail; goto fail;
} }
options_set_string(oo, name, append, "%s", value); options_set_string(oo, name, append, "%s", value);
if (cmd_get_entry(self) == &cmd_set_hook_entry) } else if (idx == -1 && !options_is_array(parent)) {
hooks_add_event(name);
} else if (array_key == NULL && !options_is_array(parent)) {
error = options_from_string(oo, options_table_entry(parent), error = options_from_string(oo, options_table_entry(parent),
options_table_entry(parent)->name, value, options_table_entry(parent)->name, value,
args_has(args, 'a'), &cause); args_has(args, 'a'), &cause);
@@ -363,7 +207,7 @@ cmd_set_option_exec(struct cmd *self, struct cmdq_item *item)
} }
if (o == NULL) if (o == NULL)
o = options_empty(oo, options_table_entry(parent)); o = options_empty(oo, options_table_entry(parent));
if (array_key == NULL) { if (idx == -1) {
if (!append) if (!append)
options_array_clear(o); options_array_clear(o);
if (options_array_assign(o, value, &cause) != 0) { if (options_array_assign(o, value, &cause) != 0) {
@@ -371,7 +215,7 @@ cmd_set_option_exec(struct cmd *self, struct cmdq_item *item)
free(cause); free(cause);
goto fail; goto fail;
} }
} else if (options_array_set(o, array_key, value, append, } else if (options_array_set(o, idx, value, append,
&cause) != 0) { &cause) != 0) {
cmdq_error(item, "%s", cause); cmdq_error(item, "%s", cause);
free(cause); free(cause);
@@ -385,13 +229,11 @@ out:
free(argument); free(argument);
free(expanded); free(expanded);
free(name); free(name);
free(array_key);
return (CMD_RETURN_NORMAL); return (CMD_RETURN_NORMAL);
fail: fail:
free(argument); free(argument);
free(expanded); free(expanded);
free(name); free(name);
free(array_key);
return (CMD_RETURN_ERROR); return (CMD_RETURN_ERROR);
} }

View File

@@ -1,4 +1,4 @@
/* $OpenBSD: cmd-show-environment.c,v 1.29 2025/04/09 06:27:43 nicm Exp $ */ /* $OpenBSD$ */
/* /*
* Copyright (c) 2009 Nicholas Marriott <nicholas.marriott@gmail.com> * Copyright (c) 2009 Nicholas Marriott <nicholas.marriott@gmail.com>

View File

@@ -1,4 +1,4 @@
/* $OpenBSD: cmd-show-messages.c,v 1.37 2025/11/18 08:37:54 nicm Exp $ */ /* $OpenBSD$ */
/* /*
* Copyright (c) 2009 Nicholas Marriott <nicholas.marriott@gmail.com> * Copyright (c) 2009 Nicholas Marriott <nicholas.marriott@gmail.com>

View File

@@ -1,4 +1,4 @@
/* $OpenBSD: cmd-show-options.c,v 1.76 2026/07/27 19:15:58 nicm Exp $ */ /* $OpenBSD$ */
/* /*
* Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com> * Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>
@@ -27,23 +27,10 @@
* Show options. * Show options.
*/ */
#define SHOW_OPTIONS_TEMPLATE \
"#{?option_value_only," \
"#{option_value}," \
"#{option_name}#{?option_has_array_key," \
"[#{option_array_key}],}" \
"#{?option_is_parent,*,}" \
"#{?option_has_value, " \
"#{?option_is_string,#{q/a:option_value},#{option_value}},}}"
#define SHOW_HOOKS_MONITOR_TEMPLATE \
"#{option_name}:#{hook_monitor_target}:#{hook_monitor_format}"
static enum cmd_retval cmd_show_options_exec(struct cmd *, struct cmdq_item *); static enum cmd_retval cmd_show_options_exec(struct cmd *, struct cmdq_item *);
static void cmd_show_options_print(struct cmd *, struct cmdq_item *, static void cmd_show_options_print(struct cmd *, struct cmdq_item *,
struct options_entry *, const char *, int); struct options_entry *, int, int);
static void cmd_show_hooks_print_monitor(struct cmd *,
struct cmdq_item *, struct options_entry *);
static enum cmd_retval cmd_show_options_all(struct cmd *, struct cmdq_item *, static enum cmd_retval cmd_show_options_all(struct cmd *, struct cmdq_item *,
int, struct options *); int, struct options *);
@@ -51,8 +38,8 @@ const struct cmd_entry cmd_show_options_entry = {
.name = "show-options", .name = "show-options",
.alias = "show", .alias = "show",
.args = { "AgF:Hpqst:vw", 0, 1, NULL }, .args = { "AgHpqst:vw", 0, 1, NULL },
.usage = "[-AgHpqsvw] [-F format] " CMD_TARGET_PANE_USAGE " [option]", .usage = "[-AgHpqsvw] " CMD_TARGET_PANE_USAGE " [option]",
.target = { 't', CMD_FIND_PANE, CMD_FIND_CANFAIL }, .target = { 't', CMD_FIND_PANE, CMD_FIND_CANFAIL },
@@ -64,8 +51,8 @@ const struct cmd_entry cmd_show_window_options_entry = {
.name = "show-window-options", .name = "show-window-options",
.alias = "showw", .alias = "showw",
.args = { "F:gvt:", 0, 1, NULL }, .args = { "gvt:", 0, 1, NULL },
.usage = "[-gv] [-F format] " CMD_TARGET_WINDOW_USAGE " [option]", .usage = "[-gv] " CMD_TARGET_WINDOW_USAGE " [option]",
.target = { 't', CMD_FIND_WINDOW, CMD_FIND_CANFAIL }, .target = { 't', CMD_FIND_WINDOW, CMD_FIND_CANFAIL },
@@ -77,8 +64,8 @@ const struct cmd_entry cmd_show_hooks_entry = {
.name = "show-hooks", .name = "show-hooks",
.alias = NULL, .alias = NULL,
.args = { "BF:gpt:w", 0, 1, NULL }, .args = { "gpt:w", 0, 1, NULL },
.usage = "[-Bgpw] [-F format] " CMD_TARGET_PANE_USAGE " [hook]", .usage = "[-gpw] " CMD_TARGET_PANE_USAGE " [hook]",
.target = { 't', CMD_FIND_PANE, CMD_FIND_CANFAIL }, .target = { 't', CMD_FIND_PANE, CMD_FIND_CANFAIL },
@@ -93,8 +80,7 @@ cmd_show_options_exec(struct cmd *self, struct cmdq_item *item)
struct cmd_find_state *target = cmdq_get_target(item); struct cmd_find_state *target = cmdq_get_target(item);
struct options *oo; struct options *oo;
char *argument, *name = NULL, *cause; char *argument, *name = NULL, *cause;
char *array_key = NULL; int window, idx, ambiguous, parent, scope;
int window, ambiguous, parent, print_parent, scope;
struct options_entry *o; struct options_entry *o;
window = (cmd_get_entry(self) == &cmd_show_window_options_entry); window = (cmd_get_entry(self) == &cmd_show_window_options_entry);
@@ -109,20 +95,11 @@ cmd_show_options_exec(struct cmd *self, struct cmdq_item *item)
free(cause); free(cause);
return (CMD_RETURN_ERROR); return (CMD_RETURN_ERROR);
} }
if (cmd_get_entry(self) == &cmd_show_hooks_entry &&
args_has(args, 'B')) {
o = options_first(oo);
while (o != NULL) {
cmd_show_hooks_print_monitor(self, item, o);
o = options_next(o);
}
return (CMD_RETURN_NORMAL);
}
return (cmd_show_options_all(self, item, scope, oo)); return (cmd_show_options_all(self, item, scope, oo));
} }
argument = format_single_from_target(item, args_string(args, 0)); argument = format_single_from_target(item, args_string(args, 0));
name = options_match(argument, &array_key, &ambiguous); name = options_match(argument, &idx, &ambiguous);
if (name == NULL) { if (name == NULL) {
if (args_has(args, 'q')) if (args_has(args, 'q'))
goto out; goto out;
@@ -147,20 +124,9 @@ cmd_show_options_exec(struct cmd *self, struct cmdq_item *item)
parent = 1; parent = 1;
} else } else
parent = 0; parent = 0;
if (o != NULL) { if (o != NULL)
if (cmd_get_entry(self) == &cmd_show_hooks_entry && cmd_show_options_print(self, item, o, idx, parent);
args_has(args, 'B')) else if (*name == '@') {
cmd_show_hooks_print_monitor(self, item, o);
else {
print_parent = parent;
if (array_key == NULL && options_is_array(o) &&
options_array_first(o) == NULL) {
print_parent = 0;
}
cmd_show_options_print(self, item, o, array_key,
print_parent);
}
} else if (*name == '@') {
if (args_has(args, 'q')) if (args_has(args, 'q'))
goto out; goto out;
cmdq_error(item, "invalid option: %s", argument); cmdq_error(item, "invalid option: %s", argument);
@@ -169,171 +135,64 @@ cmd_show_options_exec(struct cmd *self, struct cmdq_item *item)
out: out:
free(name); free(name);
free(array_key);
free(argument); free(argument);
return (CMD_RETURN_NORMAL); return (CMD_RETURN_NORMAL);
fail: fail:
free(name); free(name);
free(array_key);
free(argument); free(argument);
return (CMD_RETURN_ERROR); return (CMD_RETURN_ERROR);
} }
static void static void
cmd_show_options_print(struct cmd *self, struct cmdq_item *item, cmd_show_options_print(struct cmd *self, struct cmdq_item *item,
struct options_entry *o, const char *array_key, int parent) struct options_entry *o, int idx, int parent)
{ {
struct args *args = cmd_get_args(self); struct args *args = cmd_get_args(self);
struct options_array_item *a; struct options_array_item *a;
struct format_tree *ft; const char *name = options_name(o);
const char *name = options_name(o); char *value, *tmp = NULL, *escaped;
const char *template = args_get(args, 'F');
char *value, *line;
struct timeval tv = { 0 };
u_int fire_count;
time_t fire_time;
int is_hook = 0, is_user = 0;
int has_value = 1;
const struct options_table_entry *oe = options_table_entry(o);
if (array_key != NULL) if (idx != -1) {
value = options_to_string(o, array_key, 0); xasprintf(&tmp, "%s[%d]", name, idx);
else if (options_is_array(o)) { name = tmp;
a = options_array_first(o); } else {
if (a != NULL) { if (options_is_array(o)) {
a = options_array_first(o);
if (a == NULL) {
if (!args_has(args, 'v'))
cmdq_print(item, "%s", name);
return;
}
while (a != NULL) { while (a != NULL) {
array_key = options_array_item_key(a); idx = options_array_item_index(a);
cmd_show_options_print(self, item, o, array_key, cmd_show_options_print(self, item, o, idx,
parent); parent);
a = options_array_next(a); a = options_array_next(a);
} }
return; return;
} }
if (template == NULL && args_has(args, 'v'))
return;
value = xstrdup("");
has_value = 0;
} else
value = options_to_string(o, NULL, 0);
if (template == NULL)
template = SHOW_OPTIONS_TEMPLATE;
if (oe != NULL && (oe->flags & OPTIONS_TABLE_IS_HOOK))
is_hook = 1;
else if (oe == NULL)
is_user = 1;
ft = format_create_from_target(item);
format_add(ft, "option_name", "%s", name);
format_add(ft, "option_value", "%s", value);
format_add(ft, "option_value_only", "%d", args_has(args, 'v'));
format_add(ft, "option_is_parent", "%d", parent);
format_add(ft, "option_is_array", "%d", options_is_array(o));
format_add(ft, "option_is_string", "%d", options_is_string(o));
format_add(ft, "option_is_hook", "%d", is_hook);
format_add(ft, "option_is_user", "%d", is_user);
format_add(ft, "option_has_value", "%d", has_value);
if (cmd_get_entry(self) == &cmd_show_hooks_entry) {
fire_count = options_get_fire_count(o);
format_add(ft, "hook_fire_count", "%u", fire_count);
fire_time = options_get_fire_time(o);
if (fire_time != 0) {
tv.tv_sec = fire_time;
format_add_tv(ft, "hook_fire_time", &tv);
}
} }
if (array_key != NULL) {
format_add(ft, "option_array_key", "%s", array_key); value = options_to_string(o, idx, 0);
format_add(ft, "option_has_array_key", "1"); if (args_has(args, 'v'))
cmdq_print(item, "%s", value);
else if (options_is_string(o)) {
escaped = args_escape(value);
if (parent)
cmdq_print(item, "%s* %s", name, escaped);
else
cmdq_print(item, "%s %s", name, escaped);
free(escaped);
} else { } else {
format_add(ft, "option_array_key", "%s", ""); if (parent)
format_add(ft, "option_has_array_key", "0"); cmdq_print(item, "%s* %s", name, value);
else
cmdq_print(item, "%s %s", name, value);
} }
line = format_expand(ft, template);
format_free(ft);
cmdq_print(item, "%s", line);
free(line);
free(value); free(value);
}
static void free(tmp);
cmd_show_hooks_print_monitor(struct cmd *self, struct cmdq_item *item,
struct options_entry *o)
{
struct args *args = cmd_get_args(self);
struct format_tree *ft;
enum monitor_type type;
const char *template = args_get(args, 'F'), *format;
char *value, *target, *line;
struct timeval tv = { 0 };
u_int fire_count;
time_t fire_time;
int id;
value = hooks_monitor_to_string(o);
if (value == NULL)
return;
if (!hooks_monitor_get(o, &type, &id, &format)) {
free(value);
return;
}
if (template == NULL)
template = SHOW_HOOKS_MONITOR_TEMPLATE;
switch (type) {
case MONITOR_SESSION:
target = xstrdup("");
break;
case MONITOR_PANE:
xasprintf(&target, "%%%d", id);
break;
case MONITOR_ALL_PANES:
target = xstrdup("%*");
break;
case MONITOR_WINDOW:
xasprintf(&target, "@%d", id);
break;
case MONITOR_ALL_WINDOWS:
target = xstrdup("@*");
break;
}
ft = format_create_from_target(item);
format_add(ft, "option_name", "%s", options_name(o));
format_add(ft, "option_value", "%s", value);
format_add(ft, "option_value_only", "%d", 0);
format_add(ft, "option_is_parent", "%d", 0);
format_add(ft, "option_is_array", "%d", 0);
format_add(ft, "option_is_string", "%d", 1);
format_add(ft, "option_is_hook", "%d", 1);
format_add(ft, "option_is_user", "%d", 1);
format_add(ft, "option_has_value", "%d", 1);
format_add(ft, "option_array_key", "%s", "");
format_add(ft, "option_has_array_key", "0");
format_add(ft, "hook_monitor_target", "%s", target);
format_add(ft, "hook_monitor_format", "%s", format);
fire_count = hooks_monitor_get_fire_count(o);
format_add(ft, "hook_fire_count", "%u", fire_count);
fire_time = hooks_monitor_get_fire_time(o);
if (fire_time != 0) {
tv.tv_sec = fire_time;
format_add_tv(ft, "hook_fire_time", &tv);
}
line = format_expand(ft, template);
format_free(ft);
cmdq_print(item, "%s", line);
free(line);
free(target);
free(value);
} }
static enum cmd_retval static enum cmd_retval
@@ -343,29 +202,18 @@ cmd_show_options_all(struct cmd *self, struct cmdq_item *item, int scope,
struct args *args = cmd_get_args(self); struct args *args = cmd_get_args(self);
const struct options_table_entry *oe; const struct options_table_entry *oe;
struct options_entry *o; struct options_entry *o;
struct options_array_item *a;
const char *name; const char *name;
int parent, is_user_hook; u_int idx;
int parent;
o = options_first(oo); if (cmd_get_entry(self) != &cmd_show_hooks_entry) {
while (o != NULL) { o = options_first(oo);
if (options_table_entry(o) == NULL) { while (o != NULL) {
name = options_name(o); if (options_table_entry(o) == NULL)
is_user_hook = 0; cmd_show_options_print(self, item, o, -1, 0);
if (*name == '@') { o = options_next(o);
if (hooks_is_event(name) ||
options_get_monitor_data(o) != NULL) {
is_user_hook = 1;
}
}
if (cmd_get_entry(self) != &cmd_show_hooks_entry) {
if (!is_user_hook || args_has(args, 'H')) {
cmd_show_options_print(self, item, o,
NULL, 0);
}
} else if (is_user_hook)
cmd_show_options_print(self, item, o, NULL, 0);
} }
o = options_next(o);
} }
for (oe = options_table; oe->name != NULL; oe++) { for (oe = options_table; oe->name != NULL; oe++) {
if (~oe->scope & scope) if (~oe->scope & scope)
@@ -389,7 +237,24 @@ cmd_show_options_all(struct cmd *self, struct cmdq_item *item, int scope,
} else } else
parent = 0; parent = 0;
cmd_show_options_print(self, item, o, NULL, parent); if (!options_is_array(o))
cmd_show_options_print(self, item, o, -1, parent);
else if ((a = options_array_first(o)) == NULL) {
if (!args_has(args, 'v')) {
name = options_name(o);
if (parent)
cmdq_print(item, "%s*", name);
else
cmdq_print(item, "%s", name);
}
} else {
while (a != NULL) {
idx = options_array_item_index(a);
cmd_show_options_print(self, item, o, idx,
parent);
a = options_array_next(a);
}
}
} }
return (CMD_RETURN_NORMAL); return (CMD_RETURN_NORMAL);
} }

View File

@@ -1,4 +1,4 @@
/* $OpenBSD: cmd-show-prompt-history.c,v 1.5 2026/06/25 11:39:11 nicm Exp $ */ /* $OpenBSD$ */
/* /*
* Copyright (c) 2021 Anindya Mukherjee <anindya49@hotmail.com> * Copyright (c) 2021 Anindya Mukherjee <anindya49@hotmail.com>

View File

@@ -1,4 +1,4 @@
/* $OpenBSD: cmd-source-file.c,v 1.63 2026/08/18 08:05:05 nicm Exp $ */ /* $OpenBSD$ */
/* /*
* Copyright (c) 2008 Tiago Cunha <me@tiagocunha.org> * Copyright (c) 2008 Tiago Cunha <me@tiagocunha.org>
@@ -50,7 +50,6 @@ 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;
@@ -61,24 +60,10 @@ struct cmd_source_file_data {
u_int nfiles; u_int nfiles;
}; };
static void
cmd_source_file_free_data(struct cmd_source_file_data *cdata)
{
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 static enum cmd_retval
cmd_source_file_complete_cb(struct cmdq_item *item, void *data) cmd_source_file_complete_cb(struct cmdq_item *item, __unused void *data)
{ {
struct cmd_source_file_data *cdata = data; struct client *c = cmdq_get_client(item);
struct client *c = cdata->client;
if (c == NULL) { if (c == NULL) {
cmd_source_file_depth--; cmd_source_file_depth--;
@@ -89,36 +74,36 @@ cmd_source_file_complete_cb(struct cmdq_item *item, 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 cmd_source_file_data *cdata) cmd_source_file_complete(struct client *c, 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); if (cdata->retval == CMD_RETURN_ERROR &&
return; c != NULL &&
c->session == NULL)
c->retval = 1;
new_item = cmdq_get_callback(cmd_source_file_complete_cb, NULL);
cmdq_insert_after(cdata->after, new_item);
} }
if (cdata->retval == CMD_RETURN_ERROR && for (i = 0; i < cdata->nfiles; i++)
c != NULL && free(cdata->files[i]);
c->session == NULL) free(cdata->files);
c->retval = 1; free(cdata);
new_item = cmdq_get_callback(cmd_source_file_complete_cb, cdata);
cmdq_insert_after(cdata->after, new_item);
} }
static void static void
cmd_source_file_done(__unused struct client *oc, const char *path, cmd_source_file_done(struct client *c, const char *path, int error,
int error, int closed, struct evbuffer *buffer, void *data) 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;
@@ -142,7 +127,7 @@ cmd_source_file_done(__unused struct client *oc, const char *path,
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(cdata); cmd_source_file_complete(c, cdata);
cmdq_continue(item); cmdq_continue(item);
} }
} }
@@ -202,9 +187,6 @@ 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;
@@ -267,7 +249,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(cdata); cmd_source_file_complete(c, cdata);
free(cwd); free(cwd);
return (retval); return (retval);

View File

@@ -1,4 +1,4 @@
/* $OpenBSD: cmd-split-window.c,v 1.150 2026/08/20 09:19:24 nicm Exp $ */ /* $OpenBSD$ */
/* /*
* Copyright (c) 2009 Nicholas Marriott <nicholas.marriott@gmail.com> * Copyright (c) 2009 Nicholas Marriott <nicholas.marriott@gmail.com>
@@ -33,15 +33,13 @@
#define SPLIT_WINDOW_TEMPLATE "#{session_name}:#{window_index}.#{pane_index}" #define SPLIT_WINDOW_TEMPLATE "#{session_name}:#{window_index}.#{pane_index}"
static enum cmd_retval cmd_split_window_exec(struct cmd *, struct cmdq_item *); static enum cmd_retval cmd_split_window_exec(struct cmd *, struct cmdq_item *);
static void cmd_split_window_mouse_resize(struct client *,
struct mouse_event *);
const struct cmd_entry cmd_new_pane_entry = { const struct cmd_entry cmd_new_pane_entry = {
.name = "new-pane", .name = "new-pane",
.alias = "newp", .alias = "newp",
.args = { "AbB:Cc:de:EfF:hIkl:KLMm:Op:PR:s:S:t:T:vWx:X:y:Y:Z", 0, -1, NULL }, .args = { "bB:c:de:EfF:hIkl:Lm:p:PR:s:S:t:T:vWx:X:y:Y:Z", 0, -1, NULL },
.usage = "[-AbCdefhIkKLMOPvWZ] [-B border-lines] " .usage = "[-bdefhIklPvWZ] [-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] "
@@ -59,12 +57,12 @@ const struct cmd_entry cmd_split_window_entry = {
.name = "split-window", .name = "split-window",
.alias = "splitw", .alias = "splitw",
.args = { "bB:c:de:EfF:hIkl:m:p:PR:s:S:t:T:vWZ", 0, -1, NULL }, .args = { "bc:de:EfF:hIkl:m:p:PR:s:S:t:T:vWZ", 0, -1, NULL },
.usage = "[-bdefhIklPvWZ] [-B border-lines] [-c start-directory] " .usage = "[-bdefhIklPvWZ] [-c start-directory] [-e environment] "
"[-e environment] [-F format] [-l size] [-m message] " "[-F format] [-l size] [-m message] [-p percentage] "
"[-p percentage] [-s style] [-S active-border-style] " "[-s style] [-S active-border-style] "
"[-R inactive-border-style] [-T title] " "[-R inactive-border-style] [-T title] " CMD_TARGET_PANE_USAGE " "
CMD_TARGET_PANE_USAGE " [shell-command [argument ...]]", "[shell-command [argument ...]]",
.target = { 't', CMD_FIND_PANE, 0 }, .target = { 't', CMD_FIND_PANE, 0 },
@@ -83,13 +81,10 @@ cmd_split_window_exec(struct cmd *self, struct cmdq_item *item)
struct session *s = target->s; struct session *s = target->s;
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, *new_wp = NULL; struct window_pane *wp = target->wp, *new_wp;
struct layout_cell *lc = NULL; struct layout_cell *lc = NULL;
struct event_payload *ep;
struct cmd_find_state fs; struct cmd_find_state fs;
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;
@@ -97,57 +92,19 @@ 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;
}
if (args_has(args, 'O')) { flags = is_floating ? SPAWN_FLOATING : 0;
if (!is_floating) {
cmdq_error(item, "modal pane must be floating");
return (CMD_RETURN_ERROR);
}
if (w->modal != NULL) {
cmdq_error(item, "window already has a modal pane");
return (CMD_RETURN_ERROR);
}
}
if (args_has(args, 'M') && is_floating) {
if (event == NULL || !event->m.valid || tc == NULL)
return (CMD_RETURN_NORMAL);
}
if (is_floating)
flags |= SPAWN_FLOATING;
if (args_has(args, 'h'))
flags |= SPAWN_HORIZONTAL;
if (args_has(args, 'b')) if (args_has(args, 'b'))
flags |= SPAWN_BEFORE; flags |= SPAWN_BEFORE;
if (args_has(args, 'f')) if (args_has(args, 'f'))
flags |= SPAWN_FULLSIZE; flags |= SPAWN_FULLSIZE;
if (args_has(args, 'd'))
flags |= SPAWN_DETACHED;
if (args_has(args, 'Z'))
flags |= SPAWN_ZOOM;
if (args_has(args, 'O'))
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)
empty = 1; empty = 1;
else else
empty = args_has(args, 'E'); empty = args_has(args, 'E');
@@ -172,16 +129,13 @@ cmd_split_window_exec(struct cmd *self, struct cmdq_item *item)
} }
} }
if (flags & SPAWN_FLOATING) { if (is_floating)
lc = layout_get_floating_cell(item, args, lines, w, wp, flags, lc = layout_get_floating_cell(item, args, lines, w, wp, &cause);
&cause); else
} else
lc = layout_get_tiled_cell(item, args, w, wp, flags, &cause); lc = layout_get_tiled_cell(item, args, w, wp, flags, &cause);
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);
} }
@@ -203,22 +157,18 @@ cmd_split_window_exec(struct cmd *self, struct cmdq_item *item)
sc.idx = -1; sc.idx = -1;
sc.cwd = args_get(args, 'c'); sc.cwd = args_get(args, 'c');
sc.flags = flags; sc.flags = flags;
if (args_has(args, 'd'))
sc.flags |= SPAWN_DETACHED;
if (args_has(args, 'Z'))
sc.flags |= SPAWN_ZOOM;
if ((new_wp = spawn_pane(&sc, &cause)) == NULL) { if ((new_wp = spawn_pane(&sc, &cause)) == NULL) {
cmdq_error(item, "create pane failed: %s", cause); cmdq_error(item, "create pane failed: %s", cause);
free(cause); free(cause);
/*
* spawn_pane has already torn the half-built pane down (its
* fork-failure path removes the pane and destroys the layout
* cell), so new_wp is NULL and there is nothing for fail to do.
*/
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'))
new_wp->flags |= PANE_CLOSEONCLICK;
style = args_get(args, 's'); style = args_get(args, 's');
if (style != NULL) { if (style != NULL) {
@@ -262,19 +212,17 @@ cmd_split_window_exec(struct cmd *self, struct cmdq_item *item)
if (args_has(args, 'T')) { if (args_has(args, 'T')) {
title = format_single_from_target(item, args_get(args, 'T')); title = format_single_from_target(item, args_get(args, 'T'));
screen_set_title(&new_wp->base, title, 0); screen_set_title(&new_wp->base, title, 0);
ep = event_payload_create(); notify_pane("pane-title-changed", new_wp);
cmd_find_from_pane(&fs, new_wp, 0);
event_payload_set_target(ep, &fs);
event_payload_set_pane(ep, "pane", new_wp);
event_payload_set_window(ep, "window", new_wp->window);
event_payload_set_string(ep, "new_title", "%s", title);
events_fire("pane-title-changed", ep);
free(title); free(title);
} }
if (input) { if (input) {
switch (window_pane_start_input(new_wp, item, &cause)) { switch (window_pane_start_input(new_wp, item, &cause)) {
case -1: case -1:
server_client_remove_pane(new_wp);
if (!is_floating)
layout_close_pane(new_wp);
window_remove_pane(wp->window, new_wp);
cmdq_error(item, "%s", cause); cmdq_error(item, "%s", cause);
free(cause); free(cause);
goto fail; goto fail;
@@ -283,24 +231,15 @@ cmd_split_window_exec(struct cmd *self, struct cmdq_item *item)
break; break;
} }
} }
if (~flags & SPAWN_DETACHED) if (!args_has(args, 'd'))
cmd_find_from_winlink_pane(current, wl, new_wp, 0); cmd_find_from_winlink_pane(current, wl, new_wp, 0);
if (restore_zoom) { if (!is_floating) {
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);
} }
server_redraw_session(s); server_redraw_session(s);
if (args_has(args, 'M') && is_floating) {
tc->tty.mouse_last_pane = new_wp->id;
tc->tty.mouse_drag_update = cmd_split_window_mouse_resize;
cmd_split_window_mouse_resize(tc, &event->m);
}
if (args_has(args, 'P')) { if (args_has(args, 'P')) {
if ((template = args_get(args, 'F')) == NULL) if ((template = args_get(args, 'F')) == NULL)
template = SPLIT_WINDOW_TEMPLATE; template = SPLIT_WINDOW_TEMPLATE;
@@ -330,93 +269,11 @@ cmd_split_window_exec(struct cmd *self, struct cmdq_item *item)
return (CMD_RETURN_NORMAL); return (CMD_RETURN_NORMAL);
fail: fail:
/*
* If the pane was spawned before we failed, tear it down here; this
* also destroys its layout cell. spawn_pane's own failure path has
* already done this, so new_wp is NULL in that case.
*/
if (new_wp != NULL) {
server_client_remove_pane(new_wp);
if (!is_floating)
layout_close_pane(new_wp);
window_remove_pane(wp->window, new_wp);
}
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);
layout_destroy_cell(w, lc, &w->layout_root);
return (CMD_RETURN_ERROR); return (CMD_RETURN_ERROR);
} }
static void
cmd_split_window_mouse_resize(struct client *c, struct mouse_event *m)
{
struct window_pane *wp;
struct window *w;
struct layout_cell *lc;
enum pane_lines lines;
u_int sx, sy;
int x, y, xoff, yoff, border;
if (c->tty.mouse_last_pane == -1)
return;
wp = window_pane_find_by_id(c->tty.mouse_last_pane);
if (wp == NULL || !window_pane_is_floating(wp)) {
c->tty.mouse_drag_update = NULL;
return;
}
w = wp->window;
lc = wp->layout_cell;
x = m->x + m->ox;
y = m->y + m->oy;
if (m->statusat == 0 && y >= (int)m->statuslines)
y -= m->statuslines;
else if (m->statusat > 0 && y >= m->statusat)
y = m->statusat - 1;
lines = window_pane_get_pane_lines(wp);
border = (lines != PANE_LINES_NONE);
if (x >= (int)c->tty.mouse_drag_x) {
xoff = c->tty.mouse_drag_x + border;
sx = x - c->tty.mouse_drag_x + 1;
} else {
sx = c->tty.mouse_drag_x - x + 1;
xoff = c->tty.mouse_drag_x - sx + 1;
if (border)
xoff++;
}
if (y >= (int)c->tty.mouse_drag_y) {
yoff = c->tty.mouse_drag_y + border;
sy = y - c->tty.mouse_drag_y + 1;
} else {
sy = c->tty.mouse_drag_y - y + 1;
yoff = c->tty.mouse_drag_y - sy + 1;
if (border)
yoff++;
}
if (border) {
if (sx <= 2)
sx = PANE_MINIMUM;
else
sx -= 2;
if (sy <= 2)
sy = PANE_MINIMUM;
else
sy -= 2;
}
if (sx < PANE_MINIMUM)
sx = PANE_MINIMUM;
if (sy < PANE_MINIMUM)
sy = PANE_MINIMUM;
layout_set_size(lc, sx, sy, xoff, yoff);
layout_fix_panes(w, NULL);
server_redraw_window(w);
server_redraw_window_borders(w);
}

View File

@@ -1,4 +1,4 @@
/* $OpenBSD: cmd-swap-pane.c,v 1.55 2026/07/15 13:02:33 nicm Exp $ */ /* $OpenBSD$ */
/* /*
* Copyright (c) 2009 Nicholas Marriott <nicholas.marriott@gmail.com> * Copyright (c) 2009 Nicholas Marriott <nicholas.marriott@gmail.com>
@@ -42,22 +42,6 @@ const struct cmd_entry cmd_swap_pane_entry = {
.exec = cmd_swap_pane_exec .exec = cmd_swap_pane_exec
}; };
static struct window_pane *
cmd_swap_pane_next_tiled_pane(struct window_pane *wp)
{
while (wp != NULL && !layout_cell_is_tiled(wp->layout_cell))
wp = TAILQ_NEXT(wp, entry);
return (wp);
}
static struct window_pane *
cmd_swap_pane_prev_tiled_pane(struct window_pane *wp)
{
while (wp != NULL && !layout_cell_is_tiled(wp->layout_cell))
wp = TAILQ_PREV(wp, window_panes, entry);
return (wp);
}
static enum cmd_retval static enum cmd_retval
cmd_swap_pane_exec(struct cmd *self, struct cmdq_item *item) cmd_swap_pane_exec(struct cmd *self, struct cmdq_item *item)
{ {
@@ -68,47 +52,25 @@ cmd_swap_pane_exec(struct cmd *self, struct cmdq_item *item)
struct window_pane *tmp_wp, *src_wp, *dst_wp; struct window_pane *tmp_wp, *src_wp, *dst_wp;
struct layout_cell *src_lc, *dst_lc; struct layout_cell *src_lc, *dst_lc;
u_int sx, sy, xoff, yoff; u_int sx, sy, xoff, yoff;
int src_idx, dst_idx;
dst_w = target->wl->window; dst_w = target->wl->window;
dst_wp = target->wp; dst_wp = target->wp;
dst_idx = target->wl->idx;
src_w = source->wl->window; src_w = source->wl->window;
src_wp = source->wp; src_wp = source->wp;
src_idx = source->wl->idx;
if (src_wp == src_w->modal || dst_wp == dst_w->modal) {
cmdq_error(item, "pane is modal");
return (CMD_RETURN_ERROR);
}
if (window_push_zoom(dst_w, 0, args_has(args, 'Z'))) if (window_push_zoom(dst_w, 0, args_has(args, 'Z')))
server_redraw_window(dst_w); server_redraw_window(dst_w);
if (args_has(args, 'D')) { if (args_has(args, 'D')) {
if (window_pane_is_floating(dst_wp)) {
cmdq_error(item, "cannot swap down on floating pane");
return (CMD_RETURN_ERROR);
}
src_w = dst_w; src_w = dst_w;
src_wp = TAILQ_NEXT(dst_wp, entry); src_wp = TAILQ_NEXT(dst_wp, entry);
src_wp = cmd_swap_pane_next_tiled_pane(src_wp); if (src_wp == NULL)
if (src_wp == NULL) {
src_wp = TAILQ_FIRST(&dst_w->panes); src_wp = TAILQ_FIRST(&dst_w->panes);
src_wp = cmd_swap_pane_next_tiled_pane(src_wp);
}
} else if (args_has(args, 'U')) { } else if (args_has(args, 'U')) {
if (window_pane_is_floating(dst_wp)) {
cmdq_error(item, "cannot swap up on floating pane");
return (CMD_RETURN_ERROR);
}
src_w = dst_w; src_w = dst_w;
src_wp = TAILQ_PREV(dst_wp, window_panes, entry); src_wp = TAILQ_PREV(dst_wp, window_panes, entry);
src_wp = cmd_swap_pane_prev_tiled_pane(src_wp); if (src_wp == NULL)
if (src_wp == NULL) {
src_wp = TAILQ_LAST(&dst_w->panes, window_panes); src_wp = TAILQ_LAST(&dst_w->panes, window_panes);
src_wp = cmd_swap_pane_prev_tiled_pane(src_wp);
}
} }
if (src_w != dst_w && window_push_zoom(src_w, 0, args_has(args, 'Z'))) if (src_w != dst_w && window_push_zoom(src_w, 0, args_has(args, 'Z')))
@@ -188,13 +150,9 @@ cmd_swap_pane_exec(struct cmd *self, struct cmdq_item *item)
redraw_invalidate_scene(dst_w); redraw_invalidate_scene(dst_w);
server_redraw_window(dst_w); server_redraw_window(dst_w);
if (src_w != dst_w) { notify_window("window-layout-changed", src_w);
window_fire_pane_moved(src_wp, src_w, src_idx, dst_w, dst_idx);
window_fire_pane_moved(dst_wp, dst_w, dst_idx, src_w, src_idx);
}
events_fire_window("window-layout-changed", src_w);
if (src_w != dst_w) if (src_w != dst_w)
events_fire_window("window-layout-changed", dst_w); notify_window("window-layout-changed", dst_w);
out: out:
if (window_pop_zoom(src_w)) if (window_pop_zoom(src_w))

View File

@@ -1,4 +1,4 @@
/* $OpenBSD: cmd-swap-window.c,v 1.30 2026/07/27 08:03:01 nicm Exp $ */ /* $OpenBSD$ */
/* /*
* Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com> * Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>
@@ -38,7 +38,7 @@ const struct cmd_entry cmd_swap_window_entry = {
.source = { 's', CMD_FIND_WINDOW, CMD_FIND_DEFAULT_MARKED }, .source = { 's', CMD_FIND_WINDOW, CMD_FIND_DEFAULT_MARKED },
.target = { 't', CMD_FIND_WINDOW, 0 }, .target = { 't', CMD_FIND_WINDOW, 0 },
.flags = CMD_AFTERHOOK, .flags = 0,
.exec = cmd_swap_window_exec .exec = cmd_swap_window_exec
}; };
@@ -48,14 +48,10 @@ cmd_swap_window_exec(struct cmd *self, struct cmdq_item *item)
struct args *args = cmd_get_args(self); struct args *args = cmd_get_args(self);
struct cmd_find_state *source = cmdq_get_source(item); struct cmd_find_state *source = cmdq_get_source(item);
struct cmd_find_state *target = cmdq_get_target(item); struct cmd_find_state *target = cmdq_get_target(item);
struct client *c = cmdq_get_client(item);
struct client *tc = cmdq_get_target_client(item);
struct client *ac;
struct session *src = source->s, *dst = target->s; struct session *src = source->s, *dst = target->s;
struct session_group *sg_src, *sg_dst; struct session_group *sg_src, *sg_dst;
struct winlink *wl_src = source->wl, *wl_dst = target->wl; struct winlink *wl_src = source->wl, *wl_dst = target->wl;
struct window *w_src, *w_dst; struct window *w_src, *w_dst;
int local_src, local_dst;
sg_src = session_group_contains(src); sg_src = session_group_contains(src);
sg_dst = session_group_contains(dst); sg_dst = session_group_contains(dst);
@@ -83,22 +79,10 @@ cmd_swap_window_exec(struct cmd *self, struct cmdq_item *item)
if (marked_pane.wl == wl_src) if (marked_pane.wl == wl_src)
marked_pane.wl = wl_dst; marked_pane.wl = wl_dst;
ac = tc;
if (ac == NULL)
ac = c;
local_dst = active_is_local_window(ac, dst);
local_src = active_is_local_window(ac, src);
if (args_has(args, 'd')) { if (args_has(args, 'd')) {
if (local_dst) session_select(dst, wl_dst->idx);
active_select_window(ac, dst, wl_dst); if (src != dst)
else session_select(src, wl_src->idx);
session_select(dst, wl_dst->idx);
if (src != dst) {
if (local_src)
active_select_window(ac, src, wl_src);
else
session_select(src, wl_src->idx);
}
} }
session_group_synchronize_from(src); session_group_synchronize_from(src);
server_redraw_session_group(src); server_redraw_session_group(src);

View File

@@ -1,4 +1,4 @@
/* $OpenBSD: cmd-switch-client.c,v 1.75 2026/08/20 09:19:24 nicm Exp $ */ /* $OpenBSD$ */
/* /*
* Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com> * Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>
@@ -53,12 +53,11 @@ cmd_switch_client_exec(struct cmd *self, struct cmdq_item *item)
struct cmd_find_state target; struct cmd_find_state target;
const char *tflag = args_get(args, 't'); const char *tflag = args_get(args, 't');
enum cmd_find_type type; enum cmd_find_type type;
int flags, visible, window_target; int flags;
int Zflag = args_has(args, 'Z');
struct client *c = cmdq_get_client(item); struct client *c = cmdq_get_client(item);
struct client *tc = cmdq_get_target_client(item); struct client *tc = cmdq_get_target_client(item);
struct session *s; struct session *s;
struct winlink *wl, *current_wl; struct winlink *wl;
struct window *w; struct window *w;
struct window_pane *wp; struct window_pane *wp;
const char *tablename; const char *tablename;
@@ -66,13 +65,8 @@ cmd_switch_client_exec(struct cmd *self, struct cmdq_item *item)
struct sort_criteria sort_crit; struct sort_criteria sort_crit;
uid_t uid; uid_t uid;
window_target = 0; if (tflag != NULL &&
if (tflag != NULL) { (tflag[strcspn(tflag, ":.%")] != '\0' || strcmp(tflag, "=") == 0)) {
if (tflag[strcspn(tflag, ":.%")] != '\0' ||
strcmp(tflag, "=") == 0)
window_target = 1;
}
if (window_target) {
type = CMD_FIND_PANE; type = CMD_FIND_PANE;
flags = 0; flags = 0;
} else { } else {
@@ -143,27 +137,18 @@ cmd_switch_client_exec(struct cmd *self, struct cmdq_item *item)
} else { } else {
if (cmdq_get_client(item) == NULL) if (cmdq_get_client(item) == NULL)
return (CMD_RETURN_NORMAL); return (CMD_RETURN_NORMAL);
if (window_target && wl != NULL && wp != NULL && if (wl != NULL && wp != NULL && wp != wl->window->active) {
wp != wl->window->active) {
w = wl->window; w = wl->window;
if (w->modal != NULL && wp != w->modal) if (window_push_zoom(w, 0, args_has(args, 'Z')))
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 (!visible && window_pop_zoom(w)) if (window_pop_zoom(w))
server_redraw_window(w); server_redraw_window(w);
} }
if (window_target && wl != NULL) { if (wl != NULL) {
active_select_window(tc, s, wl); session_set_current(s, wl);
if (active_is_local_window(tc, s)) { cmd_find_from_session(current, s, 0);
current_wl = active_get_effective_winlink(tc, s);
cmd_find_from_winlink(current, current_wl, 0);
} else
cmd_find_from_session(current, s, 0);
} }
} }

View File

@@ -1,4 +1,4 @@
/* $OpenBSD: cmd-unbind-key.c,v 1.34 2021/08/21 10:22:39 nicm Exp $ */ /* $OpenBSD$ */
/* /*
* Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com> * Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>

View File

@@ -1,4 +1,4 @@
/* $OpenBSD: cmd-wait-for.c,v 1.23 2026/07/10 13:38:45 nicm Exp $ */ /* $OpenBSD$ */
/* /*
* Copyright (c) 2013 Nicholas Marriott <nicholas.marriott@gmail.com> * Copyright (c) 2013 Nicholas Marriott <nicholas.marriott@gmail.com>
@@ -34,8 +34,8 @@ const struct cmd_entry cmd_wait_for_entry = {
.name = "wait-for", .name = "wait-for",
.alias = "wait", .alias = "wait",
.args = { "EF:LSUlvw:", 1, 1, NULL }, .args = { "LSU", 1, 1, NULL },
.usage = "[-ELSUlv] [-F format] [-w waiter] name", .usage = "[-L|-S|-U] channel",
.flags = 0, .flags = 0,
.exec = cmd_wait_for_exec .exec = cmd_wait_for_exec
@@ -46,17 +46,6 @@ struct wait_item {
TAILQ_ENTRY(wait_item) entry; TAILQ_ENTRY(wait_item) entry;
}; };
struct wait_event_item {
struct cmdq_item *item;
struct events_sink *sink;
char *name;
char *filter;
int verbose;
TAILQ_ENTRY(wait_event_item) entry;
};
static TAILQ_HEAD(, wait_event_item) wait_event_items =
TAILQ_HEAD_INITIALIZER(wait_event_items);
struct wait_channel { struct wait_channel {
const char *name; const char *name;
int locked; int locked;
@@ -87,28 +76,9 @@ static enum cmd_retval cmd_wait_for_lock(struct cmdq_item *, const char *,
struct wait_channel *); struct wait_channel *);
static enum cmd_retval cmd_wait_for_unlock(struct cmdq_item *, const char *, static enum cmd_retval cmd_wait_for_unlock(struct cmdq_item *, const char *,
struct wait_channel *); struct wait_channel *);
static enum cmd_retval cmd_wait_for_event(struct cmdq_item *, const char *,
struct args *);
static void cmd_wait_for_event_cb(const char *,
struct event_payload *, void *);
static void cmd_wait_for_event_free(struct wait_event_item *);
static enum cmd_retval cmd_wait_for_event_list(struct cmdq_item *,
const char *);
static enum cmd_retval cmd_wait_for_event_wake(struct cmdq_item *,
const char *, struct args *);
static enum cmd_retval cmd_wait_for_list(struct cmdq_item *,
struct wait_channel *);
static enum cmd_retval cmd_wait_for_wake(struct cmdq_item *, const char *,
struct args *, struct wait_channel *);
static struct wait_channel *cmd_wait_for_add(const char *); static struct wait_channel *cmd_wait_for_add(const char *);
static void cmd_wait_for_remove(struct wait_channel *); static void cmd_wait_for_remove(struct wait_channel *);
static void cmd_wait_for_remove_empty(
struct wait_channel *);
static const char *cmd_wait_for_item_client_name(
struct cmdq_item *);
static const char *cmd_wait_for_client_name(
struct wait_event_item *);
static struct wait_channel * static struct wait_channel *
cmd_wait_for_add(const char *name) cmd_wait_for_add(const char *name)
@@ -147,53 +117,16 @@ cmd_wait_for_remove(struct wait_channel *wc)
free(wc); free(wc);
} }
static void
cmd_wait_for_remove_empty(struct wait_channel *wc)
{
if (wc->locked || wc->woken)
return;
if (!TAILQ_EMPTY(&wc->waiters) || !TAILQ_EMPTY(&wc->lockers))
return;
log_debug("remove empty wait channel %s", wc->name);
RB_REMOVE(wait_channels, &wait_channels, wc);
free((void *)wc->name);
free(wc);
}
static const char *
cmd_wait_for_item_client_name(struct cmdq_item *item)
{
struct client *c = cmdq_get_client(item);
if (c == NULL || c->name == NULL)
return ("");
return (c->name);
}
static const char *
cmd_wait_for_client_name(struct wait_event_item *wei)
{
return (cmd_wait_for_item_client_name(wei->item));
}
static enum cmd_retval static enum cmd_retval
cmd_wait_for_exec(struct cmd *self, struct cmdq_item *item) cmd_wait_for_exec(struct cmd *self, struct cmdq_item *item)
{ {
struct args *args = cmd_get_args(self); struct args *args = cmd_get_args(self);
const char *name = args_string(args, 0); const char *name = args_string(args, 0);
struct wait_channel *wc, find = { .name = name }; struct wait_channel *wc, find;
if (args_has(args, 'E'))
return (cmd_wait_for_event(item, name, args));
find.name = name;
wc = RB_FIND(wait_channels, &wait_channels, &find); wc = RB_FIND(wait_channels, &wait_channels, &find);
if (args_has(args, 'l'))
return (cmd_wait_for_list(item, wc));
if (args_has(args, 'w'))
return (cmd_wait_for_wake(item, name, args, wc));
if (args_has(args, 'S')) if (args_has(args, 'S'))
return (cmd_wait_for_signal(item, name, wc)); return (cmd_wait_for_signal(item, name, wc));
if (args_has(args, 'L')) if (args_has(args, 'L'))
@@ -203,179 +136,6 @@ cmd_wait_for_exec(struct cmd *self, struct cmdq_item *item)
return (cmd_wait_for_wait(item, name, wc)); return (cmd_wait_for_wait(item, name, wc));
} }
static void
cmd_wait_for_event_print(struct wait_event_item *wei, struct event_payload *ep)
{
struct event_payload_item *epi;
const char *key;
char *value;
epi = event_payload_first(ep);
while (epi != NULL) {
key = event_payload_item_name(epi);
if (*key != '_') {
value = event_payload_item_print(epi);
cmdq_print(wei->item, "%s=%s", key, value);
free(value);
}
epi = event_payload_next(epi);
}
}
static void
cmd_wait_for_event_cb(__unused const char *name, struct event_payload *ep,
void *item_data)
{
struct wait_event_item *wei = item_data;
struct format_tree *ft;
char *expanded;
int flag;
if (wei->verbose)
cmd_wait_for_event_print(wei, ep);
if (wei->filter != NULL) {
ft = format_create(cmdq_get_client(wei->item), wei->item,
FORMAT_NONE, FORMAT_NOJOBS);
event_payload_add_formats(ep, ft, NULL);
expanded = format_expand(ft, wei->filter);
flag = format_true(expanded);
free(expanded);
format_free(ft);
if (!flag)
return;
}
TAILQ_REMOVE(&wait_event_items, wei, entry);
cmdq_continue(wei->item);
cmd_wait_for_event_free(wei);
}
static void
cmd_wait_for_event_free(struct wait_event_item *wei)
{
events_remove_sink(wei->sink);
free(wei->name);
free(wei->filter);
free(wei);
}
static enum cmd_retval
cmd_wait_for_event(struct cmdq_item *item, const char *name, struct args *args)
{
struct wait_event_item *wei;
const char *filter = args_get(args, 'F');
if (!hooks_valid_event_name(name)) {
cmdq_error(item, "invalid event: %s", name);
return (CMD_RETURN_ERROR);
}
if (args_has(args, 'l'))
return (cmd_wait_for_event_list(item, name));
if (args_has(args, 'w'))
return (cmd_wait_for_event_wake(item, name, args));
if (cmdq_get_client(item) == NULL) {
cmdq_error(item, "not able to wait");
return (CMD_RETURN_ERROR);
}
wei = xcalloc(1, sizeof *wei);
wei->item = item;
wei->name = xstrdup(name);
wei->filter = (filter != NULL ? xstrdup(filter) : NULL);
wei->verbose = args_has(args, 'v');
wei->sink = events_add_sink(name, cmd_wait_for_event_cb, wei);
TAILQ_INSERT_TAIL(&wait_event_items, wei, entry);
return (CMD_RETURN_WAIT);
}
static enum cmd_retval
cmd_wait_for_event_list(struct cmdq_item *item, const char *name)
{
struct wait_event_item *wei;
TAILQ_FOREACH(wei, &wait_event_items, entry) {
if (strcmp(wei->name, name) == 0)
cmdq_print(item, "%s", cmd_wait_for_client_name(wei));
}
return (CMD_RETURN_NORMAL);
}
static enum cmd_retval
cmd_wait_for_event_wake(struct cmdq_item *item, const char *name,
struct args *args)
{
struct wait_event_item *wei, *wei1;
const char *client_name = args_get(args, 'w');
TAILQ_FOREACH_SAFE(wei, &wait_event_items, entry, wei1) {
if (strcmp(wei->name, name) != 0)
continue;
if (strcmp(cmd_wait_for_client_name(wei), client_name) != 0)
continue;
TAILQ_REMOVE(&wait_event_items, wei, entry);
cmdq_continue(wei->item);
cmd_wait_for_event_free(wei);
return (CMD_RETURN_NORMAL);
}
cmdq_error(item, "waiter %s not found", client_name);
return (CMD_RETURN_ERROR);
}
static enum cmd_retval
cmd_wait_for_list(struct cmdq_item *item, struct wait_channel *wc)
{
struct wait_item *wi;
if (wc == NULL)
return (CMD_RETURN_NORMAL);
TAILQ_FOREACH(wi, &wc->waiters, entry)
cmdq_print(item, "%s", cmd_wait_for_item_client_name(wi->item));
TAILQ_FOREACH(wi, &wc->lockers, entry)
cmdq_print(item, "%s", cmd_wait_for_item_client_name(wi->item));
return (CMD_RETURN_NORMAL);
}
static enum cmd_retval
cmd_wait_for_wake(__unused struct cmdq_item *item, const char *name,
struct args *args, struct wait_channel *wc)
{
struct wait_item *wi, *wi1;
const char *client_name = args_get(args, 'w');
if (wc != NULL) {
TAILQ_FOREACH_SAFE(wi, &wc->waiters, entry, wi1) {
name = cmd_wait_for_item_client_name(wi->item);
if (strcmp(name, client_name) != 0)
continue;
cmdq_continue(wi->item);
TAILQ_REMOVE(&wc->waiters, wi, entry);
free(wi);
cmd_wait_for_remove_empty(wc);
return (CMD_RETURN_NORMAL);
}
TAILQ_FOREACH_SAFE(wi, &wc->lockers, entry, wi1) {
name = cmd_wait_for_item_client_name(wi->item);
if (strcmp(name, client_name) != 0)
continue;
cmdq_continue(wi->item);
TAILQ_REMOVE(&wc->lockers, wi, entry);
free(wi);
cmd_wait_for_remove_empty(wc);
return (CMD_RETURN_NORMAL);
}
}
return (CMD_RETURN_NORMAL);
}
static enum cmd_retval static enum cmd_retval
cmd_wait_for_signal(__unused struct cmdq_item *item, const char *name, cmd_wait_for_signal(__unused struct cmdq_item *item, const char *name,
struct wait_channel *wc) struct wait_channel *wc)
@@ -485,13 +245,6 @@ cmd_wait_for_flush(void)
{ {
struct wait_channel *wc, *wc1; struct wait_channel *wc, *wc1;
struct wait_item *wi, *wi1; struct wait_item *wi, *wi1;
struct wait_event_item *wei, *wei1;
TAILQ_FOREACH_SAFE(wei, &wait_event_items, entry, wei1) {
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) { RB_FOREACH_SAFE(wc, wait_channels, &wait_channels, wc1) {
TAILQ_FOREACH_SAFE(wi, &wc->waiters, entry, wi1) { TAILQ_FOREACH_SAFE(wi, &wc->waiters, entry, wi1) {

61
cmd.c
View File

@@ -1,4 +1,4 @@
/* $OpenBSD: cmd.c,v 1.189 2026/07/17 08:29:34 nicm Exp $ */ /* $OpenBSD$ */
/* /*
* Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com> * Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>
@@ -69,6 +69,7 @@ extern const struct cmd_entry cmd_load_buffer_entry;
extern const struct cmd_entry cmd_lock_client_entry; extern const struct cmd_entry cmd_lock_client_entry;
extern const struct cmd_entry cmd_lock_server_entry; extern const struct cmd_entry cmd_lock_server_entry;
extern const struct cmd_entry cmd_lock_session_entry; extern const struct cmd_entry cmd_lock_session_entry;
extern const struct cmd_entry cmd_hide_pane_entry;
extern const struct cmd_entry cmd_move_pane_entry; extern const struct cmd_entry cmd_move_pane_entry;
extern const struct cmd_entry cmd_move_window_entry; extern const struct cmd_entry cmd_move_window_entry;
extern const struct cmd_entry cmd_new_pane_entry; extern const struct cmd_entry cmd_new_pane_entry;
@@ -106,6 +107,7 @@ extern const struct cmd_entry cmd_show_environment_entry;
extern const struct cmd_entry cmd_show_hooks_entry; extern const struct cmd_entry cmd_show_hooks_entry;
extern const struct cmd_entry cmd_show_messages_entry; extern const struct cmd_entry cmd_show_messages_entry;
extern const struct cmd_entry cmd_show_options_entry; extern const struct cmd_entry cmd_show_options_entry;
extern const struct cmd_entry cmd_show_pane_entry;
extern const struct cmd_entry cmd_show_prompt_history_entry; extern const struct cmd_entry cmd_show_prompt_history_entry;
extern const struct cmd_entry cmd_show_window_options_entry; extern const struct cmd_entry cmd_show_window_options_entry;
extern const struct cmd_entry cmd_source_file_entry; extern const struct cmd_entry cmd_source_file_entry;
@@ -163,6 +165,7 @@ const struct cmd_entry *cmd_table[] = {
&cmd_lock_client_entry, &cmd_lock_client_entry,
&cmd_lock_server_entry, &cmd_lock_server_entry,
&cmd_lock_session_entry, &cmd_lock_session_entry,
&cmd_hide_pane_entry,
&cmd_move_pane_entry, &cmd_move_pane_entry,
&cmd_move_window_entry, &cmd_move_window_entry,
&cmd_new_pane_entry, &cmd_new_pane_entry,
@@ -200,6 +203,7 @@ const struct cmd_entry *cmd_table[] = {
&cmd_show_hooks_entry, &cmd_show_hooks_entry,
&cmd_show_messages_entry, &cmd_show_messages_entry,
&cmd_show_options_entry, &cmd_show_options_entry,
&cmd_show_pane_entry,
&cmd_show_prompt_history_entry, &cmd_show_prompt_history_entry,
&cmd_show_window_options_entry, &cmd_show_window_options_entry,
&cmd_source_file_entry, &cmd_source_file_entry,
@@ -832,27 +836,20 @@ cmd_mouse_pane(struct mouse_event *m, struct session **sp,
if (!window_has_pane(wl->window, wp)) if (!window_has_pane(wl->window, wp))
return (NULL); return (NULL);
} }
if (wl->window->modal != NULL && wp != wl->window->modal)
return (NULL);
if (wlp != NULL) if (wlp != NULL)
*wlp = wl; *wlp = wl;
return (wp); return (wp);
} }
/* /* Replace the first %% or %idx in template by s. */
* Replace the first %% or any %idx in template by s. %% is intended for use in
* single quotes, so ' is escaped. %%% and %idx% are for double quotes, so a
* list of special characters is escaped. %idx is left unescaped.
*/
char * char *
cmd_template_replace(const char *template, const char *s, int idx) cmd_template_replace(const char *template, const char *s, int idx)
{ {
char ch, *buf; char ch, *buf;
const char *ptr, *cp, dquote[] = "\"\\$;~"; const char *ptr, *cp, quote[] = "\"\\$;~";
int replaced; int replaced, quoted;
size_t len, slen; size_t len;
enum { NQ, SQ, DQ } quote;
if (strchr(template, '%') == NULL) if (strchr(template, '%') == NULL)
return (xstrdup(template)); return (xstrdup(template));
@@ -866,52 +863,30 @@ cmd_template_replace(const char *template, const char *s, int idx)
while (*ptr != '\0') { while (*ptr != '\0') {
switch (ch = *ptr++) { switch (ch = *ptr++) {
case '%': case '%':
if (*ptr >= '1' && *ptr <= '9' && *ptr - '0' == idx) { if (*ptr < '1' || *ptr > '9' || *ptr - '0' != idx) {
ptr++;
quote = NQ;
if (*ptr == '%') {
quote = DQ;
ptr++;
}
} else {
if (*ptr != '%' || replaced) if (*ptr != '%' || replaced)
break; break;
replaced = 1; replaced = 1;
ptr++;
quote = SQ;
if (*ptr == '%') {
quote = DQ;
ptr++;
}
} }
ptr++;
slen = strlen(s); quoted = (*ptr == '%');
if (slen >= SIZE_MAX / 4 || if (quoted)
len > SIZE_MAX - (slen * 4) - 1) ptr++;
fatalx("argument too long");
buf = xrealloc(buf, len + (slen * 4) + 1); buf = xrealloc(buf, len + (strlen(s) * 3) + 1);
for (cp = s; *cp != '\0'; cp++) { for (cp = s; *cp != '\0'; cp++) {
if (quote == SQ && *cp == '\'') { if (quoted && strchr(quote, *cp) != NULL)
buf[len++] = '\'';
buf[len++] = '\\';
buf[len++] = '\'';
buf[len++] = '\'';
continue;
}
if (quote == DQ && strchr(dquote, *cp) != NULL)
buf[len++] = '\\'; buf[len++] = '\\';
buf[len++] = *cp; buf[len++] = *cp;
} }
buf[len] = '\0'; buf[len] = '\0';
continue; continue;
} }
if (len > SIZE_MAX - 2)
fatalx("argument too long");
buf = xrealloc(buf, len + 2); buf = xrealloc(buf, len + 2);
buf[len++] = ch; buf[len++] = ch;
buf[len] = '\0'; buf[len] = '\0';
} }
log_debug("%s: %s -> %s", __func__, template, buf);
return (buf); return (buf);
} }

View File

@@ -1,4 +1,4 @@
/* $OpenBSD: colour.c,v 1.35 2026/07/06 14:29:10 nicm Exp $ */ /* $OpenBSD$ */
/* /*
* Copyright (c) 2008 Nicholas Marriott <nicholas.marriott@gmail.com> * Copyright (c) 2008 Nicholas Marriott <nicholas.marriott@gmail.com>
@@ -1294,8 +1294,7 @@ colour_palette_from_option(struct colour_palette *p, struct options *oo)
{ {
struct options_entry *o; struct options_entry *o;
struct options_array_item *a; struct options_array_item *a;
union options_value *ov; u_int i, n;
u_int i;
int c; int c;
if (p == NULL) if (p == NULL)
@@ -1313,11 +1312,12 @@ colour_palette_from_option(struct colour_palette *p, struct options *oo)
p->default_palette = xcalloc(256, sizeof *p->default_palette); p->default_palette = xcalloc(256, sizeof *p->default_palette);
for (i = 0; i < 256; i++) for (i = 0; i < 256; i++)
p->default_palette[i] = -1; p->default_palette[i] = -1;
for (i = 0; i < 256; i++) { while (a != NULL) {
ov = options_array_getv(o, "%u", i); n = options_array_item_index(a);
if (ov != NULL) { if (n < 256) {
c = ov->number; c = options_array_item_value(a)->number;
p->default_palette[i] = c; p->default_palette[n] = c;
} }
a = options_array_next(a);
} }
} }

View File

@@ -410,6 +410,11 @@ int asprintf(char **, const char *, ...);
int vasprintf(char **, const char *, va_list); int vasprintf(char **, const char *, va_list);
#endif #endif
#ifndef HAVE_FGETLN
/* fgetln.c */
char *fgetln(FILE *, size_t *);
#endif
#ifndef HAVE_GETLINE #ifndef HAVE_GETLINE
/* getline.c */ /* getline.c */
ssize_t getline(char **, size_t *, FILE *); ssize_t getline(char **, size_t *, FILE *);

61
compat/fgetln.c Normal file
View File

@@ -0,0 +1,61 @@
/*
* Copyright (c) 2015 Joerg Jung <jung@openbsd.org>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
/*
* portable fgetln() version, NOT reentrant
*/
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include "compat.h"
char *
fgetln(FILE *fp, size_t *len)
{
static char *buf = NULL;
static size_t bufsz = 0;
size_t r = 0;
char *p;
int c, e;
if (!fp || !len) {
errno = EINVAL;
return NULL;
}
if (!buf) {
if (!(buf = calloc(1, BUFSIZ)))
return NULL;
bufsz = BUFSIZ;
}
while ((c = getc(fp)) != EOF) {
buf[r++] = c;
if (r == bufsz) {
if (!(p = reallocarray(buf, 2, bufsz))) {
e = errno;
free(buf);
errno = e;
buf = NULL, bufsz = 0;
return NULL;
}
buf = p, bufsz = 2 * bufsz;
}
if (c == '\n')
break;
}
return (*len = r) ? buf : NULL;
}

View File

@@ -74,14 +74,8 @@ getdelim(char **buf, size_t *bufsiz, int delimiter, FILE *fp)
} }
if (ptr + 2 >= eptr) { if (ptr + 2 >= eptr) {
char *nbuf; char *nbuf;
size_t nbufsiz; size_t nbufsiz = *bufsiz * 2;
ssize_t d = ptr - *buf; ssize_t d = ptr - *buf;
if (*bufsiz > SIZE_MAX / 2) {
errno = EOVERFLOW;
return -1;
}
nbufsiz = *bufsiz * 2;
if ((nbuf = realloc(*buf, nbufsiz)) == NULL) if ((nbuf = realloc(*buf, nbufsiz)) == NULL)
return -1; return -1;
*buf = nbuf; *buf = nbuf;

View File

@@ -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;
socklen_t len = sizeof uc; int 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);

View File

@@ -1,6 +1,6 @@
# configure.ac # configure.ac
AC_INIT([tmux], next-3.8) AC_INIT([tmux], next-3.7)
AC_PREREQ([2.60]) AC_PREREQ([2.60])
AC_CONFIG_AUX_DIR(etc) AC_CONFIG_AUX_DIR(etc)
@@ -76,7 +76,9 @@ AM_CONDITIONAL(IS_OPTIMIZED, test "x$enable_optimizations" = xyes)
# Is this --enable-asan? # Is this --enable-asan?
AC_ARG_ENABLE( AC_ARG_ENABLE(
asan, asan,
AS_HELP_STRING(--enable-asan, enable ASAN build flags) AS_HELP_STRING(--enable-asan, enable ASAN build flags),
,
enable_asan=no
) )
AM_CONDITIONAL(IS_ASAN, test "x$enable_asan" = xyes) AM_CONDITIONAL(IS_ASAN, test "x$enable_asan" = xyes)
@@ -175,6 +177,7 @@ AC_REPLACE_FUNCS([ \
clock_gettime \ clock_gettime \
closefrom \ closefrom \
explicit_bzero \ explicit_bzero \
fgetln \
freezero \ freezero \
getdtablecount \ getdtablecount \
getdtablesize \ getdtablesize \
@@ -238,7 +241,6 @@ AC_LIBOBJ(getopt_long)
# Look for libevent. Try libevent_core or libevent with pkg-config first then # Look for libevent. Try libevent_core or libevent with pkg-config first then
# look for the library. # look for the library.
libevent_version=off
PKG_CHECK_MODULES( PKG_CHECK_MODULES(
LIBEVENT_CORE, LIBEVENT_CORE,
[libevent_core >= 2], [libevent_core >= 2],
@@ -247,8 +249,6 @@ PKG_CHECK_MODULES(
CPPFLAGS="$AM_CPPFLAGS $SAVED_CPPFLAGS" CPPFLAGS="$AM_CPPFLAGS $SAVED_CPPFLAGS"
LIBS="$LIBEVENT_CORE_LIBS $LIBS" LIBS="$LIBEVENT_CORE_LIBS $LIBS"
found_libevent=yes found_libevent=yes
libevent_version=`$PKG_CONFIG --modversion libevent_core 2>/dev/null`
test "x$libevent_version" = x && libevent_version=on
], ],
found_libevent=no found_libevent=no
) )
@@ -261,8 +261,6 @@ if test x$found_libevent = xno; then
CPPFLAGS="$AM_CPPFLAGS $SAVED_CPPFLAGS" CPPFLAGS="$AM_CPPFLAGS $SAVED_CPPFLAGS"
LIBS="$LIBEVENT_LIBS $LIBS" LIBS="$LIBEVENT_LIBS $LIBS"
found_libevent=yes found_libevent=yes
libevent_version=`$PKG_CONFIG --modversion libevent 2>/dev/null`
test "x$libevent_version" = x && libevent_version=on
], ],
found_libevent=no found_libevent=no
) )
@@ -271,8 +269,7 @@ if test x$found_libevent = xno; then
AC_SEARCH_LIBS( AC_SEARCH_LIBS(
event_init, event_init,
[event_core event event-1.4], [event_core event event-1.4],
[found_libevent=yes found_libevent=yes,
libevent_version=on],
found_libevent=no found_libevent=no
) )
fi fi
@@ -299,7 +296,6 @@ fi
# Look for ncurses or curses. Try pkg-config first then directly for the # Look for ncurses or curses. Try pkg-config first then directly for the
# library. # library.
ncurses_version=off
PKG_CHECK_MODULES( PKG_CHECK_MODULES(
LIBTINFOW, LIBTINFOW,
tinfow, tinfow,
@@ -308,8 +304,6 @@ PKG_CHECK_MODULES(
CPPFLAGS="$LIBTINFOW_CFLAGS $SAVED_CPPFLAGS" CPPFLAGS="$LIBTINFOW_CFLAGS $SAVED_CPPFLAGS"
LIBS="$LIBTINFOW_LIBS $LIBS" LIBS="$LIBTINFOW_LIBS $LIBS"
found_ncurses=yes found_ncurses=yes
ncurses_version=`$PKG_CONFIG --modversion tinfow 2>/dev/null`
test "x$ncurses_version" = x && ncurses_version=on
], ],
found_ncurses=no found_ncurses=no
) )
@@ -322,8 +316,6 @@ if test "x$found_ncurses" = xno; then
CPPFLAGS="$LIBTINFO_CFLAGS $SAVED_CPPFLAGS" CPPFLAGS="$LIBTINFO_CFLAGS $SAVED_CPPFLAGS"
LIBS="$LIBTINFO_LIBS $LIBS" LIBS="$LIBTINFO_LIBS $LIBS"
found_ncurses=yes found_ncurses=yes
ncurses_version=`$PKG_CONFIG --modversion tinfo 2>/dev/null`
test "x$ncurses_version" = x && ncurses_version=on
], ],
found_ncurses=no found_ncurses=no
) )
@@ -337,8 +329,6 @@ if test "x$found_ncurses" = xno; then
CPPFLAGS="$LIBNCURSESW_CFLAGS $SAVED_CPPFLAGS" CPPFLAGS="$LIBNCURSESW_CFLAGS $SAVED_CPPFLAGS"
LIBS="$LIBNCURSESW_LIBS $LIBS" LIBS="$LIBNCURSESW_LIBS $LIBS"
found_ncurses=yes found_ncurses=yes
ncurses_version=`$PKG_CONFIG --modversion ncursesw 2>/dev/null`
test "x$ncurses_version" = x && ncurses_version=on
], ],
found_ncurses=no found_ncurses=no
) )
@@ -352,8 +342,6 @@ if test "x$found_ncurses" = xno; then
CPPFLAGS="$LIBNCURSES_CFLAGS $SAVED_CPPFLAGS" CPPFLAGS="$LIBNCURSES_CFLAGS $SAVED_CPPFLAGS"
LIBS="$LIBNCURSES_LIBS $LIBS" LIBS="$LIBNCURSES_LIBS $LIBS"
found_ncurses=yes found_ncurses=yes
ncurses_version=`$PKG_CONFIG --modversion ncurses 2>/dev/null`
test "x$ncurses_version" = x && ncurses_version=on
], ],
found_ncurses=no found_ncurses=no
) )
@@ -362,8 +350,7 @@ if test "x$found_ncurses" = xno; then
AC_SEARCH_LIBS( AC_SEARCH_LIBS(
setupterm, setupterm,
[tinfow tinfo terminfo ncursesw ncurses], [tinfow tinfo terminfo ncursesw ncurses],
[found_ncurses=yes found_ncurses=yes,
ncurses_version=on],
found_ncurses=no found_ncurses=no
) )
if test "x$found_ncurses" = xyes; then if test "x$found_ncurses" = xyes; then
@@ -393,7 +380,6 @@ else
LIBS="$LIBS -lcurses" LIBS="$LIBS -lcurses"
CPPFLAGS="$CPPFLAGS -DHAVE_CURSES_H" CPPFLAGS="$CPPFLAGS -DHAVE_CURSES_H"
AC_DEFINE(HAVE_CURSES_H) AC_DEFINE(HAVE_CURSES_H)
ncurses_version=on
else else
AC_MSG_ERROR("curses not found") AC_MSG_ERROR("curses not found")
fi fi
@@ -404,7 +390,6 @@ AC_CHECK_FUNCS([ \
]) ])
# Look for utempter. # Look for utempter.
utempter_version=off
AC_ARG_ENABLE( AC_ARG_ENABLE(
utempter, utempter,
AS_HELP_STRING(--enable-utempter, use utempter if it is installed) AS_HELP_STRING(--enable-utempter, use utempter if it is installed)
@@ -421,34 +406,17 @@ if test "x$enable_utempter" = xyes; then
fi fi
if test "x$enable_utempter" = xyes; then if test "x$enable_utempter" = xyes; then
AC_DEFINE(HAVE_UTEMPTER) AC_DEFINE(HAVE_UTEMPTER)
utempter_version=on
else else
AC_MSG_ERROR("utempter not found") AC_MSG_ERROR("utempter not found")
fi fi
fi fi
# Look for utf8proc. # Look for utf8proc.
utf8proc_version=off
AC_ARG_ENABLE( AC_ARG_ENABLE(
utf8proc, utf8proc,
AS_HELP_STRING(--enable-utf8proc, use utf8proc if it is installed) AS_HELP_STRING(--enable-utf8proc, use utf8proc if it is installed)
) )
try_utf8proc=no
require_utf8proc=no
if test "x$enable_utf8proc" = xyes; then if test "x$enable_utf8proc" = xyes; then
try_utf8proc=yes
elif test "x$enable_utf8proc" = x; then
case "$host_os" in
*darwin*)
try_utf8proc=yes
require_utf8proc=yes
;;
esac
fi
if test "x$try_utf8proc" = xyes; then
SAVED_AM_CPPFLAGS="$AM_CPPFLAGS"
SAVED_OLD_CPPFLAGS="$CPPFLAGS"
SAVED_LIBS="$LIBS"
PKG_CHECK_MODULES( PKG_CHECK_MODULES(
LIBUTF8PROC, LIBUTF8PROC,
libutf8proc, libutf8proc,
@@ -456,10 +424,7 @@ if test "x$try_utf8proc" = xyes; then
AM_CPPFLAGS="$LIBUTF8PROC_CFLAGS $AM_CPPFLAGS" AM_CPPFLAGS="$LIBUTF8PROC_CFLAGS $AM_CPPFLAGS"
CPPFLAGS="$LIBUTF8PROC_CFLAGS $SAVED_CPPFLAGS" CPPFLAGS="$LIBUTF8PROC_CFLAGS $SAVED_CPPFLAGS"
LIBS="$LIBUTF8PROC_LIBS $LIBS" LIBS="$LIBUTF8PROC_LIBS $LIBS"
utf8proc_version=`$PKG_CONFIG --modversion libutf8proc 2>/dev/null` ]
test "x$utf8proc_version" = x && utf8proc_version=on
],
[:]
) )
AC_CHECK_HEADER(utf8proc.h, enable_utf8proc=yes, enable_utf8proc=no) AC_CHECK_HEADER(utf8proc.h, enable_utf8proc=yes, enable_utf8proc=no)
if test "x$enable_utf8proc" = xyes; then if test "x$enable_utf8proc" = xyes; then
@@ -472,23 +437,13 @@ if test "x$try_utf8proc" = xyes; then
fi fi
if test "x$enable_utf8proc" = xyes; then if test "x$enable_utf8proc" = xyes; then
AC_DEFINE(HAVE_UTF8PROC) AC_DEFINE(HAVE_UTF8PROC)
test "x$utf8proc_version" = xoff && utf8proc_version=on
else else
AM_CPPFLAGS="$SAVED_AM_CPPFLAGS" AC_MSG_ERROR("utf8proc not found")
CPPFLAGS="$SAVED_OLD_CPPFLAGS"
LIBS="$SAVED_LIBS"
utf8proc_version=off
if test "x$require_utf8proc" = xyes; then
enable_utf8proc=
else
AC_MSG_ERROR("utf8proc not found")
fi
fi fi
fi fi
AM_CONDITIONAL(HAVE_UTF8PROC, [test "x$enable_utf8proc" = xyes]) AM_CONDITIONAL(HAVE_UTF8PROC, [test "x$enable_utf8proc" = xyes])
# Check for systemd support. # Check for systemd support.
systemd_version=off
AC_ARG_ENABLE( AC_ARG_ENABLE(
systemd, systemd,
AS_HELP_STRING(--enable-systemd, enable systemd integration) AS_HELP_STRING(--enable-systemd, enable systemd integration)
@@ -502,8 +457,6 @@ if test x"$enable_systemd" = xyes; then
CPPFLAGS="$AM_CPPFLAGS $SAVED_CPPFLAGS" CPPFLAGS="$AM_CPPFLAGS $SAVED_CPPFLAGS"
LIBS="$SYSTEMD_LIBS $LIBS" LIBS="$SYSTEMD_LIBS $LIBS"
found_systemd=yes found_systemd=yes
systemd_version=`$PKG_CONFIG --modversion libsystemd 2>/dev/null`
test "x$systemd_version" = x && systemd_version=on
], ],
found_systemd=no found_systemd=no
) )
@@ -628,34 +581,11 @@ if test "x$found_malloc_trim" = xyes; then
fi fi
# Build against jemalloc if requested. # Build against jemalloc if requested.
jemalloc_version=off
AC_ARG_ENABLE( AC_ARG_ENABLE(
jemalloc, jemalloc,
AS_HELP_STRING(--enable-jemalloc, use jemalloc if it is installed) AS_HELP_STRING(--enable-jemalloc, use jemalloc if it is installed)
) )
try_jemalloc=no
require_jemalloc=no
if test "x$enable_asan" = xyes && test "x$enable_jemalloc" = xyes; then
AC_MSG_ERROR([--enable-asan and --enable-jemalloc cannot be used together])
fi
if test "x$enable_jemalloc" = xyes; then if test "x$enable_jemalloc" = xyes; then
try_jemalloc=yes
elif test "x$enable_jemalloc" = x; then
case "$host_os" in
*darwin*)
if test "x$enable_asan" = xyes; then
enable_jemalloc=no
else
try_jemalloc=yes
require_jemalloc=yes
fi
;;
esac
fi
if test "x$try_jemalloc" = xyes; then
SAVED_AM_CPPFLAGS="$AM_CPPFLAGS"
SAVED_OLD_CPPFLAGS="$CPPFLAGS"
SAVED_LIBS="$LIBS"
PKG_CHECK_MODULES( PKG_CHECK_MODULES(
JEMALLOC, JEMALLOC,
jemalloc, jemalloc,
@@ -663,26 +593,9 @@ if test "x$try_jemalloc" = xyes; then
AM_CPPFLAGS="$JEMALLOC_CFLAGS $AM_CPPFLAGS" AM_CPPFLAGS="$JEMALLOC_CFLAGS $AM_CPPFLAGS"
CPPFLAGS="$AM_CPPFLAGS $SAVED_CPPFLAGS" CPPFLAGS="$AM_CPPFLAGS $SAVED_CPPFLAGS"
LIBS="$LIBS $JEMALLOC_LIBS" LIBS="$LIBS $JEMALLOC_LIBS"
jemalloc_version=`$PKG_CONFIG --modversion jemalloc 2>/dev/null`
test "x$jemalloc_version" = x && jemalloc_version=on
], ],
enable_jemalloc=no AC_MSG_ERROR("jemalloc not found")
) )
if test "x$enable_jemalloc" = xno; then
AM_CPPFLAGS="$SAVED_AM_CPPFLAGS"
CPPFLAGS="$SAVED_OLD_CPPFLAGS"
LIBS="$SAVED_LIBS"
jemalloc_version=off
if test "x$require_jemalloc" = xyes; then
enable_jemalloc=
else
AC_MSG_ERROR("jemalloc not found")
fi
else
enable_jemalloc=yes
AC_DEFINE(HAVE_JEMALLOC)
test "x$jemalloc_version" = xoff && jemalloc_version=on
fi
fi fi
# Check for CMSG_DATA. On some platforms like HP-UX this requires UNIX 95 # Check for CMSG_DATA. On some platforms like HP-UX this requires UNIX 95
@@ -1029,20 +942,6 @@ case "$host_os" in
AC_MSG_NOTICE([]) AC_MSG_NOTICE([])
AC_MSG_ERROR([must give --enable-utf8proc or --disable-utf8proc]) AC_MSG_ERROR([must give --enable-utf8proc or --disable-utf8proc])
fi fi
#
# macOS calloc(3) does not appear to always zero memory correctly,
# so complain and suggest using jemalloc instead.
#
if test "x$enable_jemalloc" = x; then
AC_MSG_NOTICE([])
AC_MSG_NOTICE([ macOS calloc(3) appears not to correctly])
AC_MSG_NOTICE([ zero allocations in some circumstances;])
AC_MSG_NOTICE([ to avoid this, configuring with])
AC_MSG_NOTICE([ --enable-jemalloc is recommended. To build])
AC_MSG_NOTICE([ without anyway, use --disable-jemalloc])
AC_MSG_NOTICE([])
AC_MSG_ERROR([must give --enable-jemalloc or --disable-jemalloc])
fi
;; ;;
*dragonfly*) *dragonfly*)
AC_MSG_RESULT(dragonfly) AC_MSG_RESULT(dragonfly)
@@ -1130,31 +1029,6 @@ AC_MSG_CHECKING(lock-command)
AC_MSG_RESULT($DEFAULT_LOCK_CMD) AC_MSG_RESULT($DEFAULT_LOCK_CMD)
AC_SUBST(DEFAULT_LOCK_CMD) AC_SUBST(DEFAULT_LOCK_CMD)
# Print a summary.
AC_MSG_NOTICE([])
if test "x$enable_asan" = xyes; then
AC_MSG_NOTICE([ASAN: on])
else
AC_MSG_NOTICE([ASAN: off])
fi
if test "x$enable_sixel" = xyes; then
AC_MSG_NOTICE([SIXEL: on])
else
AC_MSG_NOTICE([SIXEL: off])
fi
if test "x$enable_debug" = xyes; then
AC_MSG_NOTICE([debug: on])
else
AC_MSG_NOTICE([debug: off])
fi
AC_MSG_NOTICE([jemalloc: $jemalloc_version])
AC_MSG_NOTICE([libevent: $libevent_version])
AC_MSG_NOTICE([ncurses: $ncurses_version])
AC_MSG_NOTICE([systemd: $systemd_version])
AC_MSG_NOTICE([utempter: $utempter_version])
AC_MSG_NOTICE([utf8proc: $utf8proc_version])
AC_MSG_NOTICE([])
# Save our CFLAGS/CPPFLAGS/LDFLAGS for the Makefile and restore the old user # Save our CFLAGS/CPPFLAGS/LDFLAGS for the Makefile and restore the old user
# variables. # variables.
AC_SUBST(AM_CPPFLAGS) AC_SUBST(AM_CPPFLAGS)

View File

@@ -1,4 +1,4 @@
/* $OpenBSD: control-notify.c,v 1.38 2026/08/03 13:38:42 nicm Exp $ */ /* $OpenBSD$ */
/* /*
* Copyright (c) 2012 Nicholas Marriott <nicholas.marriott@gmail.com> * Copyright (c) 2012 Nicholas Marriott <nicholas.marriott@gmail.com>
@@ -23,58 +23,31 @@
#include "tmux.h" #include "tmux.h"
/* Should this client be sent events? */
#define CONTROL_SHOULD_NOTIFY_CLIENT(c) \ #define CONTROL_SHOULD_NOTIFY_CLIENT(c) \
((c) != NULL && \ ((c) != NULL && ((c)->flags & CLIENT_CONTROL) && \
((c)->flags & CLIENT_CONTROL) && \
(~(c)->flags & CLIENT_EXIT) && \
(c)->control_state != NULL) (c)->control_state != NULL)
/* Notify control clients that pane mode changed. */ void
static void control_notify_pane_mode_changed(int pane)
control_pane_mode_changed_cb(__unused const char *name,
struct event_payload *ep, __unused void *sink_data)
{ {
struct window_pane *wp; struct client *c;
struct client *c;
char *value;
wp = event_payload_get_pane(ep, "pane");
if (wp != NULL) {
TAILQ_FOREACH(c, &clients, entry) {
if (!CONTROL_SHOULD_NOTIFY_CLIENT(c))
continue;
control_notify_write(c, "%%pane-mode-changed %%%u",
wp->id);
}
return;
}
value = event_payload_print(ep, "pane");
if (value == NULL)
return;
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, "%%pane-mode-changed %s", value);
control_write(c, "%%pane-mode-changed %%%u", pane);
} }
free(value);
} }
/* Notify control clients that window layout changed. */ void
static void control_notify_window_layout_changed(struct window *w)
control_window_layout_changed_cb(__unused const char *name,
struct event_payload *ep, __unused void *sink_data)
{ {
struct client *c; struct client *c;
struct session *s; struct session *s;
struct winlink *wl; struct winlink *wl;
struct window *w = event_payload_get_window(ep, "window"); const char *template;
const char *template; char *cp;
char *cp;
if (w == NULL)
return;
template = "%layout-change #{window_id} #{window_layout} " template = "%layout-change #{window_id} #{window_layout} "
"#{window_visible_layout} #{window_raw_flags}"; "#{window_visible_layout} #{window_raw_flags}";
@@ -94,116 +67,91 @@ control_window_layout_changed_cb(__unused const char *name,
continue; continue;
s = c->session; s = c->session;
if (winlink_find_by_window_id(&s->windows, w->id) != NULL) if (winlink_find_by_window_id(&s->windows, w->id) != NULL)
control_notify_write(c, "%s", cp); control_write(c, "%s", cp);
} }
free(cp); free(cp);
} }
/* Notify control clients that window pane changed. */ void
static void control_notify_window_pane_changed(struct window *w)
control_window_pane_changed_cb(__unused const char *name,
struct event_payload *ep, __unused void *sink_data)
{ {
struct client *c; struct client *c;
struct window *w = event_payload_get_window(ep, "window");
if (w == NULL || w->active == NULL) if (w->active == 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))
continue; continue;
control_notify_write(c, "%%window-pane-changed @%u %%%u", w->id, control_write(c, "%%window-pane-changed @%u %%%u", w->id,
w->active->id); w->active->id);
} }
} }
/* Notify control clients that a window was unlinked. */ void
static void control_notify_window_unlinked(__unused struct session *s, struct window *w)
control_window_unlinked_cb(__unused const char *name, struct event_payload *ep,
__unused void *sink_data)
{ {
struct client *c; struct client *c;
struct session *cs; struct session *cs;
struct window *w = event_payload_get_window(ep, "window");
if (w == NULL)
return;
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;
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_notify_write(c, "%%window-close @%u", w->id); control_write(c, "%%window-close @%u", w->id);
else { else
control_notify_write(c, "%%unlinked-window-close @%u", control_write(c, "%%unlinked-window-close @%u", w->id);
w->id);
}
} }
} }
/* Notify control clients that a window was linked. */ void
static void control_notify_window_linked(__unused struct session *s, struct window *w)
control_window_linked_cb(__unused const char *name, struct event_payload *ep,
__unused void *sink_data)
{ {
struct client *c; struct client *c;
struct session *cs; struct session *cs;
struct window *w = event_payload_get_window(ep, "window");
if (w == NULL)
return;
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;
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_notify_write(c, "%%window-add @%u", w->id); control_write(c, "%%window-add @%u", w->id);
else { else
control_notify_write(c, "%%unlinked-window-add @%u", control_write(c, "%%unlinked-window-add @%u", w->id);
w->id);
}
} }
} }
/* Notify control clients that a window was renamed. */ void
static void control_notify_window_renamed(struct window *w)
control_window_renamed_cb(__unused const char *name, struct event_payload *ep,
__unused void *sink_data)
{ {
struct client *c; struct client *c;
struct session *cs; struct session *cs;
struct window *w = event_payload_get_window(ep, "window");
if (w == NULL)
return;
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;
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_notify_write(c, "%%window-renamed @%u %s", control_write(c, "%%window-renamed @%u %s", w->id,
w->id, w->name); w->name);
} else { } else {
control_notify_write(c, control_write(c, "%%unlinked-window-renamed @%u %s",
"%%unlinked-window-renamed @%u %s", w->id, w->name); w->id, w->name);
} }
} }
} }
/* Notify control clients that a client changed session. */ void
static void control_notify_client_session_changed(struct client *cc)
control_client_session_changed_cb(__unused const char *name,
struct event_payload *ep, __unused void *sink_data)
{ {
struct client *cc = event_payload_get_client(ep, "client"); struct client *c;
struct client *c; struct session *s;
struct session *s;
if (cc == NULL || cc->session == NULL) if (cc->session == NULL)
return; return;
s = cc->session; s = cc->session;
@@ -212,107 +160,68 @@ control_client_session_changed_cb(__unused const char *name,
continue; continue;
if (cc == c) { if (cc == c) {
control_notify_write(c, "%%session-changed $%u %s", control_write(c, "%%session-changed $%u %s", s->id,
s->id, s->name); s->name);
} else { } else {
control_notify_write(c, control_write(c, "%%client-session-changed %s $%u %s",
"%%client-session-changed %s $%u %s", cc->name, cc->name, s->id, s->name);
s->id, s->name);
} }
} }
} }
/* Notify control clients that a client changed its effective window. */ void
static void control_notify_client_detached(struct client *cc)
control_client_window_changed_cb(__unused const char *name,
struct event_payload *ep, __unused void *sink_data)
{ {
struct client *cc = event_payload_get_client(ep, "client"); struct client *c;
struct session *s = event_payload_get_session(ep, "session");
struct window *w = event_payload_get_window(ep, "window");
struct client *c;
if (cc == NULL || s == NULL || w == NULL)
return;
TAILQ_FOREACH(c, &clients, entry) { TAILQ_FOREACH(c, &clients, entry) {
if (!CONTROL_SHOULD_NOTIFY_CLIENT(c)) if (CONTROL_SHOULD_NOTIFY_CLIENT(c))
continue; control_write(c, "%%client-detached %s", cc->name);
control_write(c, "%%client-window-changed %s $%u @%u",
cc->name, s->id, w->id);
} }
} }
/* Notify control clients that a client detached. */ void
static void control_notify_session_renamed(struct session *s)
control_client_detached_cb(__unused const char *name, struct event_payload *ep,
__unused void *sink_data)
{
struct client *cc = event_payload_get_client(ep, "client");
struct client *c;
if (cc == NULL)
return;
TAILQ_FOREACH(c, &clients, entry) {
if (!CONTROL_SHOULD_NOTIFY_CLIENT(c))
continue;
control_notify_write(c, "%%client-detached %s", cc->name);
}
}
/* Notify control clients that a session was renamed. */
static void
control_session_renamed_cb(__unused const char *name, struct event_payload *ep,
__unused void *sink_data)
{
struct session *s = event_payload_get_session(ep, "session");
struct client *c;
if (s == NULL)
return;
TAILQ_FOREACH(c, &clients, entry) {
if (!CONTROL_SHOULD_NOTIFY_CLIENT(c))
continue;
control_notify_write(c, "%%session-renamed $%u %s", s->id,
s->name);
}
}
/* Notify control clients that sessions changed. */
static void
control_session_created_cb(__unused const char *name,
__unused struct event_payload *ep, __unused void *sink_data)
{ {
struct client *c; struct client *c;
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, "%%session-renamed $%u %s", s->id, s->name);
} }
} }
/* Notify control clients that sessions changed. */ void
static void control_notify_session_created(__unused struct session *s)
control_session_closed_cb(__unused const char *name,
__unused struct event_payload *ep, __unused void *sink_data)
{ {
struct client *c; struct client *c;
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");
} }
} }
/* Notify control clients that the current window changed. */ void
static void control_notify_session_closed(__unused struct session *s)
control_session_window_changed_cb(__unused const char *name, {
struct event_payload *ep, __unused void *sink_data) struct client *c;
TAILQ_FOREACH(c, &clients, entry) {
if (!CONTROL_SHOULD_NOTIFY_CLIENT(c))
continue;
control_write(c, "%%sessions-changed");
}
}
void
control_notify_session_window_changed(struct session *s)
{ {
struct session *s = event_payload_get_session(ep, "session");
struct client *c; struct client *c;
/* /*
@@ -320,78 +229,40 @@ control_session_window_changed_cb(__unused const char *name,
* session has been destroyed (which sets curw to NULL) but is kept * session has been destroyed (which sets curw to NULL) but is kept
* alive by the notification's reference. Skip the notification. * alive by the notification's reference. Skip the notification.
*/ */
if (s == NULL || s->curw == NULL) if (s->curw == 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))
continue; continue;
control_notify_write(c, "%%session-window-changed $%u @%u",
s->id, s->curw->window->id); control_write(c, "%%session-window-changed $%u @%u", s->id,
s->curw->window->id);
} }
} }
/* Notify control clients that a paste buffer changed. */
static void
control_paste_buffer_changed_cb(__unused const char *name,
struct event_payload *ep, __unused void *sink_data)
{
const char *pbname = event_payload_get_string(ep, "paste_buffer");
struct client *c;
if (pbname == NULL)
return;
TAILQ_FOREACH(c, &clients, entry) {
if (!CONTROL_SHOULD_NOTIFY_CLIENT(c))
continue;
control_notify_write(c, "%%paste-buffer-changed %s", pbname);
}
}
/* Notify control clients that a paste buffer was deleted. */
static void
control_paste_buffer_deleted_cb(__unused const char *name,
struct event_payload *ep, __unused void *sink_data)
{
const char *pbname = event_payload_get_string(ep, "paste_buffer");
struct client *c;
if (pbname == NULL)
return;
TAILQ_FOREACH(c, &clients, entry) {
if (!CONTROL_SHOULD_NOTIFY_CLIENT(c))
continue;
control_notify_write(c, "%%paste-buffer-deleted %s", pbname);
}
}
/* Build control event sinks. */
void void
control_build_events(void) control_notify_paste_buffer_changed(const char *name)
{ {
/* Control event sink callbacks. */ struct client *c;
static struct {
const char *name;
events_cb cb;
} events[] = {
{ "pane-mode-changed", control_pane_mode_changed_cb },
{ "window-layout-changed", control_window_layout_changed_cb },
{ "window-pane-changed", control_window_pane_changed_cb },
{ "window-unlinked", control_window_unlinked_cb },
{ "window-linked", control_window_linked_cb },
{ "window-renamed", control_window_renamed_cb },
{ "client-session-changed", control_client_session_changed_cb },
{ "client-window-changed", control_client_window_changed_cb },
{ "client-detached", control_client_detached_cb },
{ "session-renamed", control_session_renamed_cb },
{ "session-created", control_session_created_cb },
{ "session-closed", control_session_closed_cb },
{ "session-window-changed", control_session_window_changed_cb },
{ "paste-buffer-changed", control_paste_buffer_changed_cb },
{ "paste-buffer-deleted", control_paste_buffer_deleted_cb }
};
u_int i;
for (i = 0; i < nitems(events); i++) TAILQ_FOREACH(c, &clients, entry) {
events_add_sink(events[i].name, events[i].cb, NULL); if (!CONTROL_SHOULD_NOTIFY_CLIENT(c))
continue;
control_write(c, "%%paste-buffer-changed %s", name);
}
}
void
control_notify_paste_buffer_deleted(const char *name)
{
struct client *c;
TAILQ_FOREACH(c, &clients, entry) {
if (!CONTROL_SHOULD_NOTIFY_CLIENT(c))
continue;
control_write(c, "%%paste-buffer-deleted %s", name);
}
} }

754
control.c
View File

@@ -1,4 +1,4 @@
/* $OpenBSD: control.c,v 1.66 2026/08/18 07:43:44 nicm Exp $ */ /* $OpenBSD$ */
/* /*
* Copyright (c) 2012 Nicholas Marriott <nicholas.marriott@gmail.com> * Copyright (c) 2012 Nicholas Marriott <nicholas.marriott@gmail.com>
@@ -19,9 +19,6 @@
#include <sys/types.h> #include <sys/types.h>
#include <errno.h>
#include <event.h>
#include <poll.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#include <time.h> #include <time.h>
@@ -53,17 +50,6 @@ 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;
@@ -89,20 +75,45 @@ struct control_pane {
}; };
RB_HEAD(control_panes, control_pane); RB_HEAD(control_panes, control_pane);
/* Control client window size. */ /* Subscription pane. */
struct control_window { struct control_sub_pane {
u_int window; u_int pane;
u_int sx; u_int idx;
u_int sy; char *last;
RB_ENTRY(control_window) entry; RB_ENTRY(control_sub_pane) entry;
}; };
RB_HEAD(control_windows, control_window); RB_HEAD(control_sub_panes, control_sub_pane);
/* Subscription window. */
struct control_sub_window {
u_int window;
u_int idx;
char *last;
RB_ENTRY(control_sub_window) entry;
};
RB_HEAD(control_sub_windows, control_sub_window);
/* Control client subscription. */
struct control_sub {
char *name;
char *format;
enum control_sub_type type;
u_int id;
char *last;
struct control_sub_panes panes;
struct control_sub_windows windows;
RB_ENTRY(control_sub) entry;
};
RB_HEAD(control_subs, control_sub);
/* Control client state. */ /* Control client state. */
struct control_state { struct control_state {
struct control_panes panes; struct control_panes panes;
struct control_windows windows;
TAILQ_HEAD(, control_pane) pending_list; TAILQ_HEAD(, control_pane) pending_list;
u_int pending_count; u_int pending_count;
@@ -112,14 +123,8 @@ struct control_state {
struct bufferevent *read_event; struct bufferevent *read_event;
struct bufferevent *write_event; struct bufferevent *write_event;
struct monitor_set *subs; struct control_subs subs;
struct event subs_timer;
/*
* 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. */
@@ -149,17 +154,74 @@ control_pane_cmp(struct control_pane *cp1, struct control_pane *cp2)
} }
RB_GENERATE_STATIC(control_panes, control_pane, entry, control_pane_cmp); RB_GENERATE_STATIC(control_panes, control_pane, entry, control_pane_cmp);
/* Compare control windows. */ /* Compare client subs. */
static int static int
control_window_cmp(struct control_window *cw1, struct control_window *cw2) control_sub_cmp(struct control_sub *csub1, struct control_sub *csub2)
{ {
if (cw1->window < cw2->window) return (strcmp(csub1->name, csub2->name));
}
RB_GENERATE_STATIC(control_subs, control_sub, entry, control_sub_cmp);
/* Compare client subscription panes. */
static int
control_sub_pane_cmp(struct control_sub_pane *csp1,
struct control_sub_pane *csp2)
{
if (csp1->pane < csp2->pane)
return (-1); return (-1);
if (cw1->window > cw2->window) if (csp1->pane > csp2->pane)
return (1);
if (csp1->idx < csp2->idx)
return (-1);
if (csp1->idx > csp2->idx)
return (1); return (1);
return (0); return (0);
} }
RB_GENERATE_STATIC(control_windows, control_window, entry, control_window_cmp); RB_GENERATE_STATIC(control_sub_panes, control_sub_pane, entry,
control_sub_pane_cmp);
/* Compare client subscription windows. */
static int
control_sub_window_cmp(struct control_sub_window *csw1,
struct control_sub_window *csw2)
{
if (csw1->window < csw2->window)
return (-1);
if (csw1->window > csw2->window)
return (1);
if (csw1->idx < csw2->idx)
return (-1);
if (csw1->idx > csw2->idx)
return (1);
return (0);
}
RB_GENERATE_STATIC(control_sub_windows, control_sub_window, entry,
control_sub_window_cmp);
/* Free a subscription. */
static void
control_free_sub(struct control_state *cs, struct control_sub *csub)
{
struct control_sub_pane *csp, *csp1;
struct control_sub_window *csw, *csw1;
RB_FOREACH_SAFE(csp, control_sub_panes, &csub->panes, csp1) {
RB_REMOVE(control_sub_panes, &csub->panes, csp);
free(csp->last);
free(csp);
}
RB_FOREACH_SAFE(csw, control_sub_windows, &csub->windows, csw1) {
RB_REMOVE(control_sub_windows, &csub->windows, csw);
free(csw->last);
free(csw);
}
free(csub->last);
RB_REMOVE(control_subs, &cs->subs, csub);
free(csub->name);
free(csub->format);
free(csub);
}
/* Free a block. */ /* Free a block. */
static void static void
@@ -202,66 +264,6 @@ control_add_pane(struct client *c, struct window_pane *wp)
return (cp); return (cp);
} }
/* Get window for this client. */
static struct control_window *
control_get_window(struct client *c, u_int window)
{
struct control_state *cs = c->control_state;
struct control_window cw = { .window = window };
if (cs == NULL)
return (NULL);
return (RB_FIND(control_windows, &cs->windows, &cw));
}
/* Set window size for this client. */
void
control_set_window_size(struct client *c, u_int window, u_int sx, u_int sy)
{
struct control_state *cs = c->control_state;
struct control_window *cw;
if (cs == NULL)
return;
cw = control_get_window(c, window);
if (cw == NULL) {
cw = xcalloc(1, sizeof *cw);
cw->window = window;
RB_INSERT(control_windows, &cs->windows, cw);
}
cw->sx = sx;
cw->sy = sy;
}
/* Get window size for this client. */
int
control_get_window_size(struct client *c, u_int window, u_int *sx, u_int *sy)
{
struct control_window *cw;
if ((cw = control_get_window(c, window)) == NULL)
return (0);
*sx = cw->sx;
*sy = cw->sy;
return (1);
}
/* Clear window size for this client. */
void
control_clear_window_size(struct client *c, u_int window)
{
struct control_state *cs = c->control_state;
struct control_window *cw;
if (cs == NULL)
return;
cw = control_get_window(c, window);
if (cw != NULL) {
RB_REMOVE(control_windows, &cs->windows, cw);
free(cw);
}
}
/* Discard output for a pane. */ /* Discard output for a pane. */
static void static void
control_discard_pane(struct client *c, struct control_pane *cp) control_discard_pane(struct client *c, struct control_pane *cp)
@@ -370,7 +372,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_notify_write(c, "%%continue %%%u", wp->id); control_write(c, "%%continue %%%u", wp->id);
} }
} }
@@ -384,139 +386,52 @@ 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_notify_write(c, "%%pause %%%u", wp->id); control_write(c, "%%pause %%%u", wp->id);
} }
} }
/* /* Write a line. */
* Reset a pane after its buffer has been replaced: drop any output still static void printflike(2, 0)
* queued from the old buffer and start again from the pane's own offset. control_vwrite(struct client *c, const char *fmt, va_list ap)
*/
void
control_reset_pane(struct client *c, struct window_pane *wp)
{ {
struct control_pane *cp; struct control_state *cs = c->control_state;
char *s;
if (c->control_state == NULL) xvasprintf(&s, fmt, ap);
return; log_debug("%s: %s: writing line: %s", __func__, c->name, s);
cp = control_get_pane(c, wp);
if (cp == NULL) bufferevent_write(cs->write_event, s, strlen(s));
return; bufferevent_write(cs->write_event, "\n", 1);
control_discard_pane(c, cp);
memcpy(&cp->offset, &wp->offset, sizeof cp->offset); bufferevent_enable(cs->write_event, EV_WRITE);
memcpy(&cp->queued, &wp->offset, sizeof cp->queued); free(s);
} }
/* Write an already-formatted line, queueing it behind %output if needed. */ /* Write a line. */
static void void
control_write_line(struct client *c, char *line) control_write(struct client *c, const char *fmt, ...)
{ {
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;
va_start(ap, fmt);
if (TAILQ_EMPTY(&cs->all_blocks)) { if (TAILQ_EMPTY(&cs->all_blocks)) {
log_debug("%s: %s: writing line: %s", __func__, c->name, line); control_vwrite(c, fmt, ap);
bufferevent_write(cs->write_event, line, strlen(line)); va_end(ap);
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);
cb->line = line; xvasprintf(&cb->line, fmt, ap);
TAILQ_INSERT_TAIL(&cs->all_blocks, cb, all_entry); TAILQ_INSERT_TAIL(&cs->all_blocks, cb, all_entry);
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, ...)
{
va_list ap;
char *line;
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 (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;
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. */
@@ -543,7 +458,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_notify_write(c, "%%pause %%%u", wp->id); control_write(c, "%%pause %%%u", wp->id);
} else { } else {
if (age < CONTROL_MAXIMUM_AGE) if (age < CONTROL_MAXIMUM_AGE)
return (0); return (0);
@@ -566,7 +481,7 @@ control_write_output(struct client *c, struct window_pane *wp)
if (winlink_find_by_window(&c->session->windows, wp->window) == NULL) if (winlink_find_by_window(&c->session->windows, wp->window) == NULL)
return; return;
if (c->flags & (CONTROL_IGNORE_FLAGS|CLIENT_EXIT)) { if (c->flags & CONTROL_IGNORE_FLAGS) {
cp = control_get_pane(c, wp); cp = control_get_pane(c, wp);
if (cp != NULL) if (cp != NULL)
goto ignore; goto ignore;
@@ -676,53 +591,6 @@ control_all_done(struct client *c)
return (EVBUFFER_LENGTH(cs->write_event->output) == 0); return (EVBUFFER_LENGTH(cs->write_event->output) == 0);
} }
/*
* Wait for the terminal to send an empty line or close, used by a control
* client after printing %exit so a wrapping terminal (such as iTerm2) can
* finish reading.
*/
void
control_wait_exit(int fd)
{
struct pollfd pfd;
struct evbuffer *evb;
char *line;
int n;
evb = evbuffer_new();
if (evb == NULL)
fatalx("out of memory");
for (;;) {
line = evbuffer_readln(evb, NULL, EVBUFFER_EOL_LF);
if (line != NULL) {
if (*line == '\0') { /* empty line, stop */
free(line);
break;
}
free(line);
continue; /* drain buffered lines first */
}
memset(&pfd, 0, sizeof pfd);
pfd.fd = fd;
pfd.events = POLLIN;
if (poll(&pfd, 1, INFTIM) == -1) {
if (errno == EINTR)
continue;
break;
}
n = evbuffer_read(evb, fd, -1);
if (n == 0)
break;
if (n == -1 && errno != EAGAIN && errno != EINTR)
break;
}
evbuffer_free(evb);
}
/* Flush all blocks until output. */ /* Flush all blocks until output. */
static void static void
control_flush_all_blocks(struct client *c) control_flush_all_blocks(struct client *c)
@@ -898,33 +766,6 @@ control_write_callback(__unused struct bufferevent *bufev, void *data)
bufferevent_disable(cs->write_event, EV_WRITE); bufferevent_disable(cs->write_event, EV_WRITE);
} }
/* Write a subscription change. */
static void
control_sub_change(struct monitor_change *change, __unused void *data)
{
struct client *c = change->c;
struct session *s = change->s;
struct winlink *wl = change->wl;
struct window_pane *wp = change->wp;
struct window *w;
if (wp != NULL) {
w = wp->window;
control_notify_write(c,
"%%subscription-changed %s $%u @%u %u %%%u : %s",
change->name, s->id, w->id, wl->idx, wp->id, change->value);
} else if (wl != NULL) {
w = wl->window;
control_notify_write(c,
"%%subscription-changed %s $%u @%u %u - : %s",
change->name, s->id, w->id, wl->idx, change->value);
} else {
control_notify_write(c,
"%%subscription-changed %s $%u - - - : %s",
change->name, s->id, change->value);
}
}
/* Initialize for control mode. */ /* Initialize for control mode. */
void void
control_start(struct client *c) control_start(struct client *c)
@@ -940,11 +781,9 @@ control_start(struct client *c)
cs = c->control_state = xcalloc(1, sizeof *cs); cs = c->control_state = xcalloc(1, sizeof *cs);
RB_INIT(&cs->panes); RB_INIT(&cs->panes);
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); RB_INIT(&cs->subs);
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,
control_write_callback, control_error_callback, c); control_write_callback, control_error_callback, c);
@@ -987,48 +826,27 @@ 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);
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)
{ {
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_sub *csub, *csub1;
struct control_line *cl, *cl1;
if (cs == NULL) if (cs == NULL)
return; return;
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);
RB_FOREACH_SAFE(csub, control_subs, &cs->subs, csub1)
control_free_sub(cs, csub);
if (evtimer_initialized(&cs->subs_timer))
evtimer_del(&cs->subs_timer);
control_reset_offsets(c); control_reset_offsets(c);
RB_FOREACH_SAFE(cw, control_windows, &cs->windows, cw1) {
RB_REMOVE(control_windows, &cs->windows, cw);
free(cw);
}
TAILQ_FOREACH_SAFE(cb, &cs->all_blocks, all_entry, cb1) TAILQ_FOREACH_SAFE(cb, &cs->all_blocks, all_entry, cb1)
control_free_block(cs, cb); control_free_block(cs, cb);
@@ -1036,14 +854,313 @@ control_stop(struct client *c)
free(cs); free(cs);
} }
/* Check session subscription. */
static void
control_check_subs_session(struct client *c, struct control_sub *csub,
struct format_tree *ft)
{
struct session *s = c->session;
char *value;
value = format_expand(ft, csub->format);
if (csub->last != NULL && strcmp(value, csub->last) == 0) {
free(value);
return;
}
control_write(c,
"%%subscription-changed %s $%u - - - : %s",
csub->name, s->id, value);
free(csub->last);
csub->last = value;
}
/* Check pane subscription. */
static void
control_check_subs_pane(struct client *c, struct control_sub *csub)
{
struct session *s = c->session;
struct window_pane *wp;
struct window *w;
struct winlink *wl;
struct format_tree *ft;
char *value;
struct control_sub_pane *csp, find;
wp = window_pane_find_by_id(csub->id);
if (wp == NULL || wp->fd == -1)
return;
w = wp->window;
TAILQ_FOREACH(wl, &w->winlinks, wentry) {
if (wl->session != s)
continue;
ft = format_create_defaults(NULL, c, s, wl, wp);
value = format_expand(ft, csub->format);
format_free(ft);
find.pane = wp->id;
find.idx = wl->idx;
csp = RB_FIND(control_sub_panes, &csub->panes, &find);
if (csp == NULL) {
csp = xcalloc(1, sizeof *csp);
csp->pane = wp->id;
csp->idx = wl->idx;
RB_INSERT(control_sub_panes, &csub->panes, csp);
}
if (csp->last != NULL && strcmp(value, csp->last) == 0) {
free(value);
continue;
}
control_write(c,
"%%subscription-changed %s $%u @%u %u %%%u : %s",
csub->name, s->id, w->id, wl->idx, wp->id, value);
free(csp->last);
csp->last = value;
}
}
/* Check all-panes subscription for a pane. */
static void
control_check_subs_all_panes_one(struct client *c, struct control_sub *csub,
struct format_tree *ft, struct winlink *wl, struct window_pane *wp)
{
struct session *s = c->session;
struct window *w = wl->window;
char *value;
struct control_sub_pane *csp, find;
value = format_expand(ft, csub->format);
find.pane = wp->id;
find.idx = wl->idx;
csp = RB_FIND(control_sub_panes, &csub->panes, &find);
if (csp == NULL) {
csp = xcalloc(1, sizeof *csp);
csp->pane = wp->id;
csp->idx = wl->idx;
RB_INSERT(control_sub_panes, &csub->panes, csp);
}
if (csp->last != NULL && strcmp(value, csp->last) == 0) {
free(value);
return;
}
control_write(c,
"%%subscription-changed %s $%u @%u %u %%%u : %s",
csub->name, s->id, w->id, wl->idx, wp->id, value);
free(csp->last);
csp->last = value;
}
/* Check window subscription. */
static void
control_check_subs_window(struct client *c, struct control_sub *csub)
{
struct session *s = c->session;
struct window *w;
struct winlink *wl;
struct format_tree *ft;
char *value;
struct control_sub_window *csw, find;
w = window_find_by_id(csub->id);
if (w == NULL)
return;
TAILQ_FOREACH(wl, &w->winlinks, wentry) {
if (wl->session != s)
continue;
ft = format_create_defaults(NULL, c, s, wl, NULL);
value = format_expand(ft, csub->format);
format_free(ft);
find.window = w->id;
find.idx = wl->idx;
csw = RB_FIND(control_sub_windows, &csub->windows, &find);
if (csw == NULL) {
csw = xcalloc(1, sizeof *csw);
csw->window = w->id;
csw->idx = wl->idx;
RB_INSERT(control_sub_windows, &csub->windows, csw);
}
if (csw->last != NULL && strcmp(value, csw->last) == 0) {
free(value);
continue;
}
control_write(c,
"%%subscription-changed %s $%u @%u %u - : %s",
csub->name, s->id, w->id, wl->idx, value);
free(csw->last);
csw->last = value;
}
}
/* Check all-windows subscription for a window. */
static void
control_check_subs_all_windows_one(struct client *c, struct control_sub *csub,
struct format_tree *ft, struct winlink *wl)
{
struct session *s = c->session;
struct window *w = wl->window;
char *value;
struct control_sub_window *csw, find;
value = format_expand(ft, csub->format);
find.window = w->id;
find.idx = wl->idx;
csw = RB_FIND(control_sub_windows, &csub->windows, &find);
if (csw == NULL) {
csw = xcalloc(1, sizeof *csw);
csw->window = w->id;
csw->idx = wl->idx;
RB_INSERT(control_sub_windows, &csub->windows, csw);
}
if (csw->last != NULL && strcmp(value, csw->last) == 0) {
free(value);
return;
}
control_write(c,
"%%subscription-changed %s $%u @%u %u - : %s",
csub->name, s->id, w->id, wl->idx, value);
free(csw->last);
csw->last = value;
}
/* Check subscriptions timer. */
static void
control_check_subs_timer(__unused int fd, __unused short events, void *data)
{
struct client *c = data;
struct control_state *cs = c->control_state;
struct control_sub *csub, *csub1;
struct session *s = c->session;
struct format_tree *ft;
struct winlink *wl;
struct window_pane *wp;
struct timeval tv = { .tv_sec = 1 };
int have_session = 0, have_all_panes = 0;
int have_all_windows = 0;
log_debug("%s: timer fired", __func__);
evtimer_add(&cs->subs_timer, &tv);
if (s == NULL)
return;
/* Find which subscription types are present. */
RB_FOREACH(csub, control_subs, &cs->subs) {
switch (csub->type) {
case CONTROL_SUB_SESSION:
have_session = 1;
break;
case CONTROL_SUB_ALL_PANES:
have_all_panes = 1;
break;
case CONTROL_SUB_ALL_WINDOWS:
have_all_windows = 1;
break;
default:
break;
}
}
/* Check session subscriptions. */
if (have_session) {
ft = format_create_defaults(NULL, c, s, NULL, NULL);
RB_FOREACH_SAFE(csub, control_subs, &cs->subs, csub1) {
if (csub->type == CONTROL_SUB_SESSION)
control_check_subs_session(c, csub, ft);
}
format_free(ft);
}
/* Check pane and window subscriptions. */
RB_FOREACH_SAFE(csub, control_subs, &cs->subs, csub1) {
switch (csub->type) {
case CONTROL_SUB_PANE:
control_check_subs_pane(c, csub);
break;
case CONTROL_SUB_WINDOW:
control_check_subs_window(c, csub);
break;
case CONTROL_SUB_SESSION:
case CONTROL_SUB_ALL_PANES:
case CONTROL_SUB_ALL_WINDOWS:
break;
}
}
/* Check all-panes subscriptions. */
if (have_all_panes) {
RB_FOREACH(wl, winlinks, &s->windows) {
TAILQ_FOREACH(wp, &wl->window->panes, entry) {
ft = format_create_defaults(NULL, c, s, wl, wp);
RB_FOREACH_SAFE(csub, control_subs, &cs->subs,
csub1) {
if (csub->type != CONTROL_SUB_ALL_PANES)
continue;
control_check_subs_all_panes_one(c,
csub, ft, wl, wp);
}
format_free(ft);
}
}
}
/* Check all-windows subscriptions. */
if (have_all_windows) {
RB_FOREACH(wl, winlinks, &s->windows) {
ft = format_create_defaults(NULL, c, s, wl, NULL);
RB_FOREACH_SAFE(csub, control_subs, &cs->subs,
csub1) {
if (csub->type != CONTROL_SUB_ALL_WINDOWS)
continue;
control_check_subs_all_windows_one(c, csub, ft,
wl);
}
format_free(ft);
}
}
}
/* Add a subscription. */ /* Add a subscription. */
void void
control_add_sub(struct client *c, const char *name, enum monitor_type type, control_add_sub(struct client *c, const char *name, enum control_sub_type type,
int id, const char *format) int id, const char *format)
{ {
struct control_state *cs = c->control_state; struct control_state *cs = c->control_state;
struct control_sub *csub, find;
struct timeval tv = { .tv_sec = 1 };
monitor_add(cs->subs, name, type, id, format, MONITOR_NOTIFY_INITIAL); find.name = (char *)name;
if ((csub = RB_FIND(control_subs, &cs->subs, &find)) != NULL)
control_free_sub(cs, csub);
csub = xcalloc(1, sizeof *csub);
csub->name = xstrdup(name);
csub->type = type;
csub->id = id;
csub->format = xstrdup(format);
RB_INSERT(control_subs, &cs->subs, csub);
RB_INIT(&csub->panes);
RB_INIT(&csub->windows);
if (!evtimer_initialized(&cs->subs_timer))
evtimer_set(&cs->subs_timer, control_check_subs_timer, c);
if (!evtimer_pending(&cs->subs_timer, NULL))
evtimer_add(&cs->subs_timer, &tv);
} }
/* Remove a subscription. */ /* Remove a subscription. */
@@ -1051,6 +1168,11 @@ void
control_remove_sub(struct client *c, const char *name) control_remove_sub(struct client *c, const char *name)
{ {
struct control_state *cs = c->control_state; struct control_state *cs = c->control_state;
struct control_sub *csub, find;
monitor_remove(cs->subs, name); find.name = (char *)name;
if ((csub = RB_FIND(control_subs, &cs->subs, &find)) != NULL)
control_free_sub(cs, csub);
if (RB_EMPTY(&cs->subs))
evtimer_del(&cs->subs_timer);
} }

View File

@@ -1,4 +1,4 @@
/* $OpenBSD: environ.c,v 1.31 2026/06/13 20:07:30 nicm Exp $ */ /* $OpenBSD$ */
/* /*
* Copyright (c) 2009 Nicholas Marriott <nicholas.marriott@gmail.com> * Copyright (c) 2009 Nicholas Marriott <nicholas.marriott@gmail.com>

View File

@@ -1,672 +0,0 @@
/* $OpenBSD: events-payload.c,v 1.1 2026/07/10 13:38:45 nicm Exp $ */
/*
* Copyright (c) 2026 Nicholas Marriott <nicholas.marriott@gmail.com>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER
* IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
* OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include <sys/types.h>
#include <event.h>
#include <stdarg.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include "tmux.h"
/* Event payload item. */
struct event_payload_item {
char *name;
enum event_payload_type type;
union {
char *string;
time_t time;
int number;
u_int unsigned_number;
struct client *client;
struct session *session;
struct window *window;
struct window_pane *pane;
struct {
void *ptr;
event_payload_free_cb free_cb;
event_payload_print_cb print_cb;
} pointer;
};
RB_ENTRY(event_payload_item) entry;
};
RB_HEAD(event_payload_tree, event_payload_item);
struct event_payload {
struct event_payload_tree items;
struct cmd_find_state target;
};
static int
event_payload_cmp(struct event_payload_item *epi1,
struct event_payload_item *epi2)
{
return (strcmp(epi1->name, epi2->name));
}
RB_GENERATE_STATIC(event_payload_tree, event_payload_item, entry,
event_payload_cmp);
/* Find an item. */
static struct event_payload_item *
event_payload_find(struct event_payload *ep, const char *name)
{
struct event_payload_item find = { .name = (char *)name };
return (RB_FIND(event_payload_tree, &ep->items, &find));
}
/* Free the target in a payload. */
static void
event_payload_free_target(struct event_payload *ep)
{
struct cmd_find_state *target = &ep->target;
if (target->s != NULL)
session_remove_ref(target->s, __func__);
if (target->w != NULL)
window_remove_ref(target->w, __func__);
if (target->wp != NULL)
window_pane_remove_ref(target->wp, __func__);
cmd_find_clear_state(target, 0);
}
/* Free the value in an item. */
static void
event_payload_free_value(struct event_payload_item *epi)
{
switch (epi->type) {
case EVENT_PAYLOAD_STRING:
free(epi->string);
break;
case EVENT_PAYLOAD_CLIENT:
server_client_unref(epi->client);
break;
case EVENT_PAYLOAD_SESSION:
session_remove_ref(epi->session, __func__);
break;
case EVENT_PAYLOAD_WINDOW:
window_remove_ref(epi->window, __func__);
break;
case EVENT_PAYLOAD_PANE:
window_pane_remove_ref(epi->pane, __func__);
break;
case EVENT_PAYLOAD_POINTER:
if (epi->pointer.free_cb != NULL)
epi->pointer.free_cb(epi->pointer.ptr);
break;
case EVENT_PAYLOAD_INT:
case EVENT_PAYLOAD_UINT:
case EVENT_PAYLOAD_TIME:
break;
}
}
/* Set an item. */
static void
event_payload_set_item(struct event_payload *ep, const char *name,
struct event_payload_item *new)
{
struct event_payload_item *old;
new->name = xstrdup(name);
old = RB_INSERT(event_payload_tree, &ep->items, new);
if (old != NULL) {
RB_REMOVE(event_payload_tree, &ep->items, old);
event_payload_free_value(old);
free(old->name);
free(old);
RB_INSERT(event_payload_tree, &ep->items, new);
}
}
/* Create an event payload. */
struct event_payload *
event_payload_create(void)
{
struct event_payload *ep;
ep = xcalloc(1, sizeof *ep);
RB_INIT(&ep->items);
cmd_find_clear_state(&ep->target, 0);
return (ep);
}
/* Free an event payload. */
void
event_payload_free(struct event_payload *ep)
{
struct event_payload_item *epi, *epi1;
if (ep != NULL) {
RB_FOREACH_SAFE(epi, event_payload_tree, &ep->items, epi1) {
RB_REMOVE(event_payload_tree, &ep->items, epi);
event_payload_free_value(epi);
free(epi->name);
free(epi);
}
event_payload_free_target(ep);
free(ep);
}
}
/* Set the target. */
void
event_payload_set_target(struct event_payload *ep, struct cmd_find_state *fs)
{
struct cmd_find_state *target = &ep->target;
event_payload_free_target(ep);
if (fs->s != NULL) {
session_add_ref(fs->s, __func__);
target->s = fs->s;
}
if (fs->wl != NULL) {
target->idx = fs->wl->idx;
if (target->s == NULL) {
session_add_ref(fs->wl->session, __func__);
target->s = fs->wl->session;
}
} else
target->idx = -1;
if (fs->w != NULL) {
window_add_ref(fs->w, __func__);
target->w = fs->w;
} else if (fs->wl != NULL) {
window_add_ref(fs->wl->window, __func__);
target->w = fs->wl->window;
}
if (fs->wp != NULL) {
window_pane_add_ref(fs->wp, __func__);
target->wp = fs->wp;
}
}
/* Get the target. */
int
event_payload_get_target(struct event_payload *ep, struct cmd_find_state *fs)
{
struct cmd_find_state *t = &ep->target;
struct winlink *wl = NULL;
int flags = fs->flags;
if (t->idx != -1 &&
t->s != NULL &&
t->w != NULL &&
session_alive(t->s)) {
wl = winlink_find_by_index(&t->s->windows, t->idx);
if (wl != NULL && wl->window != t->w)
wl = NULL;
}
cmd_find_clear_state(fs, flags);
fs->s = t->s;
fs->w = t->w;
fs->wp = t->wp;
fs->wl = wl;
fs->idx = (wl != NULL ? wl->idx : -1);
if (cmd_find_valid_state(fs))
return (1);
if (wl != NULL &&
t->wp != NULL &&
window_has_pane(wl->window, t->wp)) {
cmd_find_from_winlink_pane(fs, wl, t->wp, flags);
if (cmd_find_valid_state(fs))
return (1);
}
if (t->wp != NULL &&
cmd_find_from_pane(fs, t->wp, flags) == 0 &&
cmd_find_valid_state(fs))
return (1);
if (wl != NULL) {
cmd_find_from_winlink(fs, wl, flags);
if (cmd_find_valid_state(fs))
return (1);
}
if (t->s != NULL &&
t->w != NULL &&
session_alive(t->s) &&
cmd_find_from_session_window(fs, t->s, t->w, flags) == 0 &&
cmd_find_valid_state(fs))
return (1);
if (t->s != NULL && session_alive(t->s)) {
cmd_find_from_session(fs, t->s, flags);
if (cmd_find_valid_state(fs))
return (1);
}
if (cmd_find_from_nothing(fs, flags) == 0)
return (1);
cmd_find_clear_state(fs, flags);
return (0);
}
/* Set a string item. */
void
event_payload_set_string(struct event_payload *ep, const char *name,
const char *fmt, ...)
{
struct event_payload_item *epi;
va_list ap;
va_start(ap, fmt);
epi = xcalloc(1, sizeof *epi);
epi->type = EVENT_PAYLOAD_STRING;
xvasprintf(&epi->string, fmt, ap);
event_payload_set_item(ep, name, epi);
va_end(ap);
}
/* Set a time item. */
void
event_payload_set_time(struct event_payload *ep, const char *name,
time_t value)
{
struct event_payload_item *epi;
epi = xcalloc(1, sizeof *epi);
epi->type = EVENT_PAYLOAD_TIME;
epi->time = value;
event_payload_set_item(ep, name, epi);
}
/* Set a number item. */
void
event_payload_set_int(struct event_payload *ep, const char *name, int value)
{
struct event_payload_item *epi;
epi = xcalloc(1, sizeof *epi);
epi->type = EVENT_PAYLOAD_INT;
epi->number = value;
event_payload_set_item(ep, name, epi);
}
/* Set an unsigned number item. */
void
event_payload_set_uint(struct event_payload *ep, const char *name, u_int value)
{
struct event_payload_item *epi;
epi = xcalloc(1, sizeof *epi);
epi->type = EVENT_PAYLOAD_UINT;
epi->unsigned_number = value;
event_payload_set_item(ep, name, epi);
}
/* Set a client item. */
void
event_payload_set_client(struct event_payload *ep, const char *name,
struct client *c)
{
struct event_payload_item *epi;
c->references++;
epi = xcalloc(1, sizeof *epi);
epi->type = EVENT_PAYLOAD_CLIENT;
epi->client = c;
event_payload_set_item(ep, name, epi);
}
/* Set a session item. */
void
event_payload_set_session(struct event_payload *ep, const char *name,
struct session *s)
{
struct event_payload_item *epi;
session_add_ref(s, __func__);
epi = xcalloc(1, sizeof *epi);
epi->type = EVENT_PAYLOAD_SESSION;
epi->session = s;
event_payload_set_item(ep, name, epi);
}
/* Set a window item. */
void
event_payload_set_window(struct event_payload *ep, const char *name,
struct window *w)
{
struct event_payload_item *epi;
window_add_ref(w, __func__);
epi = xcalloc(1, sizeof *epi);
epi->type = EVENT_PAYLOAD_WINDOW;
epi->window = w;
event_payload_set_item(ep, name, epi);
}
/* Set a pane item. */
void
event_payload_set_pane(struct event_payload *ep, const char *name,
struct window_pane *wp)
{
struct event_payload_item *epi;
window_pane_add_ref(wp, __func__);
epi = xcalloc(1, sizeof *epi);
epi->type = EVENT_PAYLOAD_PANE;
epi->pane = wp;
event_payload_set_item(ep, name, epi);
}
/* Set a pointer item. */
void
event_payload_set_pointer(struct event_payload *ep, const char *name,
void *ptr, event_payload_free_cb free_cb, event_payload_print_cb print_cb)
{
struct event_payload_item *epi;
epi = xcalloc(1, sizeof *epi);
epi->type = EVENT_PAYLOAD_POINTER;
epi->pointer.ptr = ptr;
epi->pointer.free_cb = free_cb;
epi->pointer.print_cb = print_cb;
event_payload_set_item(ep, name, epi);
}
/* Get a string item. */
const char *
event_payload_get_string(struct event_payload *ep, const char *name)
{
struct event_payload_item *epi;
epi = event_payload_find(ep, name);
if (epi == NULL || epi->type != EVENT_PAYLOAD_STRING)
return (NULL);
return (epi->string);
}
/* Print a payload item. */
static void
event_payload_add_item(struct event_payload_item *epi, struct evbuffer *evb)
{
switch (epi->type) {
case EVENT_PAYLOAD_STRING:
evbuffer_add_printf(evb, "%s", epi->string);
break;
case EVENT_PAYLOAD_TIME:
evbuffer_add_printf(evb, "%lld", (long long)epi->time);
break;
case EVENT_PAYLOAD_INT:
evbuffer_add_printf(evb, "%d", epi->number);
break;
case EVENT_PAYLOAD_UINT:
evbuffer_add_printf(evb, "%u", epi->unsigned_number);
break;
case EVENT_PAYLOAD_CLIENT:
evbuffer_add_printf(evb, "%s", epi->client->name);
break;
case EVENT_PAYLOAD_SESSION:
evbuffer_add_printf(evb, "$%u", epi->session->id);
break;
case EVENT_PAYLOAD_WINDOW:
evbuffer_add_printf(evb, "@%u", epi->window->id);
break;
case EVENT_PAYLOAD_PANE:
evbuffer_add_printf(evb, "%%%u", epi->pane->id);
break;
case EVENT_PAYLOAD_POINTER:
if (epi->pointer.print_cb != NULL)
epi->pointer.print_cb(epi->pointer.ptr, evb);
else
evbuffer_add_printf(evb, "%p", epi->pointer.ptr);
break;
}
}
/* Print a payload item. */
char *
event_payload_item_print(struct event_payload_item *epi)
{
struct evbuffer *evb;
char *value = NULL;
size_t size;
evb = evbuffer_new();
if (evb == NULL)
fatalx("out of memory");
event_payload_add_item(epi, evb);
if ((size = EVBUFFER_LENGTH(evb)) != 0)
value = xmemdup(EVBUFFER_DATA(evb), size);
else
value = xstrdup("");
evbuffer_free(evb);
return (value);
}
/* Print an item value. */
char *
event_payload_print(struct event_payload *ep, const char *name)
{
struct event_payload_item *epi;
epi = event_payload_find(ep, name);
if (epi == NULL)
return (NULL);
return (event_payload_item_print(epi));
}
/* Add payload items as formats. */
void
event_payload_add_formats(struct event_payload *ep, struct format_tree *ft,
const char *prefix)
{
struct event_payload_item *epi;
char *name, *value;
const char *key;
if (prefix == NULL)
prefix = "";
RB_FOREACH(epi, event_payload_tree, &ep->items) {
key = epi->name;
if (*key == '_')
continue;
value = event_payload_item_print(epi);
xasprintf(&name, "%s%s", prefix, key);
format_add(ft, name, "%s", value);
free(name);
free(value);
if (epi->type == EVENT_PAYLOAD_SESSION) {
xasprintf(&name, "%s%s_name", prefix, key);
format_add(ft, name, "%s", epi->session->name);
free(name);
} else if (epi->type == EVENT_PAYLOAD_WINDOW) {
xasprintf(&name, "%s%s_name", prefix, key);
format_add(ft, name, "%s", epi->window->name);
free(name);
}
}
}
/* Get the first payload item. */
struct event_payload_item *
event_payload_first(struct event_payload *ep)
{
return (RB_MIN(event_payload_tree, &ep->items));
}
/* Get the next payload item. */
struct event_payload_item *
event_payload_next(struct event_payload_item *epi)
{
return (RB_NEXT(event_payload_tree, , epi));
}
/* Get a payload item name. */
const char *
event_payload_item_name(struct event_payload_item *epi)
{
return (epi->name);
}
/* Get a payload item type. */
enum event_payload_type
event_payload_item_type(struct event_payload_item *epi)
{
return (epi->type);
}
/* Log a payload. */
void
event_payload_log(struct event_payload *ep, const char *fmt, ...)
{
struct event_payload_item *epi;
struct evbuffer *evb;
va_list ap;
char *prefix;
va_start(ap, fmt);
xvasprintf(&prefix, fmt, ap);
va_end(ap);
evb = evbuffer_new();
if (evb == NULL)
fatalx("out of memory");
if (ep != NULL) {
RB_FOREACH(epi, event_payload_tree, &ep->items) {
if (EVBUFFER_LENGTH(evb) != 0)
evbuffer_add_printf(evb, ", ");
evbuffer_add_printf(evb, "%s=", epi->name);
event_payload_add_item(epi, evb);
}
}
log_debug("%s%.*s", prefix, (int)EVBUFFER_LENGTH(evb),
(char *)EVBUFFER_DATA(evb));
evbuffer_free(evb);
free(prefix);
}
/* Get a time item. */
time_t
event_payload_get_time(struct event_payload *ep, const char *name)
{
struct event_payload_item *epi;
epi = event_payload_find(ep, name);
if (epi == NULL || epi->type != EVENT_PAYLOAD_TIME)
return (0);
return (epi->time);
}
/* Get a number item. */
int
event_payload_get_int(struct event_payload *ep, const char *name, int *value)
{
struct event_payload_item *epi;
epi = event_payload_find(ep, name);
if (epi == NULL || epi->type != EVENT_PAYLOAD_INT)
return (-1);
*value = epi->number;
return (0);
}
/* Get an unsigned number item. */
int
event_payload_get_uint(struct event_payload *ep, const char *name, u_int *value)
{
struct event_payload_item *epi;
epi = event_payload_find(ep, name);
if (epi == NULL || epi->type != EVENT_PAYLOAD_UINT)
return (-1);
*value = epi->unsigned_number;
return (0);
}
/* Get a client item. */
struct client *
event_payload_get_client(struct event_payload *ep, const char *name)
{
struct event_payload_item *epi;
epi = event_payload_find(ep, name);
if (epi == NULL || epi->type != EVENT_PAYLOAD_CLIENT)
return (NULL);
return (epi->client);
}
/* Get a session item. */
struct session *
event_payload_get_session(struct event_payload *ep, const char *name)
{
struct event_payload_item *epi;
epi = event_payload_find(ep, name);
if (epi == NULL || epi->type != EVENT_PAYLOAD_SESSION)
return (NULL);
return (epi->session);
}
/* Get a window item. */
struct window *
event_payload_get_window(struct event_payload *ep, const char *name)
{
struct event_payload_item *epi;
epi = event_payload_find(ep, name);
if (epi == NULL || epi->type != EVENT_PAYLOAD_WINDOW)
return (NULL);
return (epi->window);
}
/* Get a pane item. */
struct window_pane *
event_payload_get_pane(struct event_payload *ep, const char *name)
{
struct event_payload_item *epi;
epi = event_payload_find(ep, name);
if (epi == NULL || epi->type != EVENT_PAYLOAD_PANE)
return (NULL);
return (epi->pane);
}
/* Get a pointer item. */
void *
event_payload_get_pointer(struct event_payload *ep, const char *name)
{
struct event_payload_item *epi;
epi = event_payload_find(ep, name);
if (epi == NULL || epi->type != EVENT_PAYLOAD_POINTER)
return (NULL);
return (epi->pointer.ptr);
}

199
events.c
View File

@@ -1,199 +0,0 @@
/* $OpenBSD: events.c,v 1.1 2026/07/10 13:38:45 nicm Exp $ */
/*
* Copyright (c) 2026 Nicholas Marriott <nicholas.marriott@gmail.com>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include <sys/types.h>
#include <stdlib.h>
#include <string.h>
#include "tmux.h"
/* Event sink. */
struct events_sink {
char *name;
events_cb cb;
void *data;
int dead;
u_int generation;
TAILQ_ENTRY(events_sink) entry;
};
TAILQ_HEAD(events_sinks, events_sink);
static struct events_sinks events_sinks = TAILQ_HEAD_INITIALIZER(events_sinks);
static u_int events_dispatching;
static u_int events_generation;
/* Free an event sink. */
static void
events_free_sink(struct events_sink *es)
{
TAILQ_REMOVE(&events_sinks, es, entry);
free(es->name);
free(es);
}
/* Free dead event sinks. */
static void
events_free_dead(void)
{
struct events_sink *es, *es1;
TAILQ_FOREACH_SAFE(es, &events_sinks, entry, es1) {
if (es->dead)
events_free_sink(es);
}
}
/* Add an event sink. */
struct events_sink *
events_add_sink(const char *name, events_cb cb, void *data)
{
struct events_sink *es;
es = xcalloc(1, sizeof *es);
es->name = xstrdup(name);
es->cb = cb;
es->data = data;
es->generation = ++events_generation;
TAILQ_INSERT_TAIL(&events_sinks, es, entry);
return (es);
}
/* Remove an event sink. */
void
events_remove_sink(struct events_sink *es)
{
if (es != NULL && !es->dead) {
if (events_dispatching != 0)
es->dead = 1;
else
events_free_sink(es);
}
}
/* Fire an event. */
void
events_fire(const char *name, struct event_payload *ep)
{
struct events_sink *es;
u_int generation = events_generation;
event_payload_set_string(ep, "event", "%s", name);
if (log_get_level() != 0)
event_payload_log(ep, "%s: %s: ", __func__, name);
events_dispatching++;
TAILQ_FOREACH(es, &events_sinks, entry) {
if (es->dead || es->generation > generation)
continue;
if (strcmp(es->name, name) == 0)
es->cb(name, ep, es->data);
}
if (--events_dispatching == 0)
events_free_dead();
event_payload_free(ep);
}
/* Fire a client event. */
void
events_fire_client(const char *name, struct client *c)
{
struct event_payload *ep;
struct cmd_find_state fs;
ep = event_payload_create();
cmd_find_from_client(&fs, c, 0);
event_payload_set_target(ep, &fs);
event_payload_set_client(ep, "client", c);
if (fs.s != NULL)
event_payload_set_session(ep, "session", fs.s);
if (fs.w != NULL)
event_payload_set_window(ep, "window", fs.w);
if (fs.wl != NULL)
event_payload_set_int(ep, "window_index", fs.wl->idx);
else if (fs.idx != -1)
event_payload_set_int(ep, "window_index", fs.idx);
if (fs.wp != NULL)
event_payload_set_pane(ep, "pane", fs.wp);
events_fire(name, ep);
}
/* Fire a session event. */
void
events_fire_session(const char *name, struct session *s)
{
struct event_payload *ep;
struct cmd_find_state fs;
ep = event_payload_create();
if (session_alive(s)) {
cmd_find_from_session(&fs, s, 0);
event_payload_set_target(ep, &fs);
}
event_payload_set_session(ep, "session", s);
events_fire(name, ep);
}
/* Fire a window event. */
void
events_fire_window(const char *name, struct window *w)
{
struct event_payload *ep;
struct cmd_find_state fs;
ep = event_payload_create();
cmd_find_from_window(&fs, w, 0);
event_payload_set_target(ep, &fs);
event_payload_set_window(ep, "window", w);
events_fire(name, ep);
}
/* Fire a pane event. */
void
events_fire_pane(const char *name, struct window_pane *wp)
{
struct event_payload *ep;
struct cmd_find_state fs;
ep = event_payload_create();
cmd_find_from_pane(&fs, wp, 0);
event_payload_set_target(ep, &fs);
event_payload_set_pane(ep, "pane", wp);
event_payload_set_window(ep, "window", wp->window);
events_fire(name, ep);
}
/* Fire a winlink event. */
void
events_fire_winlink(const char *name, struct winlink *wl)
{
struct event_payload *ep;
struct cmd_find_state fs;
ep = event_payload_create();
cmd_find_from_winlink(&fs, wl, 0);
event_payload_set_target(ep, &fs);
event_payload_set_session(ep, "session", wl->session);
event_payload_set_window(ep, "window", wl->window);
event_payload_set_int(ep, "window_index", wl->idx);
events_fire(name, ep);
}

87
file.c
View File

@@ -1,4 +1,4 @@
/* $OpenBSD: file.c,v 1.22 2026/08/17 07:56:56 nicm Exp $ */ /* $OpenBSD$ */
/* /*
* Copyright (c) 2019 Nicholas Marriott <nicholas.marriott@gmail.com> * Copyright (c) 2019 Nicholas Marriott <nicholas.marriott@gmail.com>
@@ -185,7 +185,6 @@ file_can_print(struct client *c)
{ {
if (c == NULL || if (c == NULL ||
(c->flags & CLIENT_ATTACHED) || (c->flags & CLIENT_ATTACHED) ||
(c->flags & CLIENT_DEAD) ||
(c->flags & CLIENT_CONTROL)) (c->flags & CLIENT_CONTROL))
return (0); return (0);
return (1); return (1);
@@ -502,8 +501,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);
} }
@@ -528,48 +526,14 @@ 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, short what, file_write_error_callback(__unused struct bufferevent *bev, __unused 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;
@@ -577,9 +541,7 @@ file_write_error_callback(__unused struct bufferevent *bev, short what,
close(cf->fd); close(cf->fd);
cf->fd = -1; cf->fd = -1;
if (cf->closed) if (cf->cb != NULL)
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);
} }
@@ -591,10 +553,15 @@ 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->closed && EVBUFFER_LENGTH(cf->event->output) == 0) if (cf->cb != NULL)
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). */
@@ -695,10 +662,14 @@ 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) {
file_write_finished(cf); if (cf->event != NULL)
bufferevent_free(cf->event);
if (cf->fd != -1)
close(cf->fd);
RB_REMOVE(client_files, files, cf);
file_free(cf);
} }
} }
@@ -857,28 +828,6 @@ 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)

View File

@@ -1,4 +1,4 @@
/* $OpenBSD: format-draw.c,v 1.34 2026/07/21 07:20:18 nicm Exp $ */ /* $OpenBSD$ */
/* /*
* Copyright (c) 2019 Nicholas Marriott <nicholas.marriott@gmail.com> * Copyright (c) 2019 Nicholas Marriott <nicholas.marriott@gmail.com>
@@ -958,7 +958,7 @@ format_draw(struct screen_write_ctx *octx, const struct grid_cell *base,
if (srs != NULL) { if (srs != NULL) {
if (fr != NULL && !format_is_type(fr, &sy)) { if (fr != NULL && !format_is_type(fr, &sy)) {
if (s[current].cx != fr->start) { if (s[current].cx != fr->start) {
fr->end = s[current].cx; fr->end = s[current].cx + 1;
TAILQ_INSERT_TAIL(&frs, fr, entry); TAILQ_INSERT_TAIL(&frs, fr, entry);
} else } else
free(fr); free(fr);

1057
format.c

File diff suppressed because it is too large Load Diff

View File

@@ -1,4 +1,4 @@
/* $OpenBSD: fuzzy.c,v 1.1 2026/06/26 14:40:30 nicm Exp $ */ /* $OpenBSD$ */
/* /*
* Copyright (c) 2026 Nicholas Marriott <nicholas.marriott@gmail.com> * Copyright (c) 2026 Nicholas Marriott <nicholas.marriott@gmail.com>

View File

@@ -1,4 +1,4 @@
/* $OpenBSD: grid-reader.c,v 1.11 2026/07/29 17:42:56 nicm Exp $ */ /* $OpenBSD$ */
/* /*
* Copyright (c) 2020 Anindya Mukherjee <anindya49@hotmail.com> * Copyright (c) 2020 Anindya Mukherjee <anindya49@hotmail.com>
@@ -54,8 +54,11 @@ grid_reader_cursor_right(struct grid_reader *gr, int wrap, int all, int onemore)
px = gr->gd->sx; px = gr->gd->sx;
else if (onemore) else if (onemore)
px = grid_reader_line_length(gr); px = grid_reader_line_length(gr);
else else {
px = grid_line_limit(gr->gd, gr->cy); px = grid_reader_line_length(gr);
if (px != 0)
px--;
}
if (wrap && gr->cx >= px && gr->cy < gr->gd->hsize + gr->gd->sy - 1) { if (wrap && gr->cx >= px && gr->cy < gr->gd->hsize + gr->gd->sy - 1) {
grid_reader_cursor_start_of_line(gr, 0); grid_reader_cursor_start_of_line(gr, 0);

View File

@@ -1,4 +1,4 @@
/* $OpenBSD: grid-view.c,v 1.39 2026/08/03 12:58:53 nicm Exp $ */ /* $OpenBSD$ */
/* /*
* 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, int bg) grid_view_set_padding(struct grid *gd, u_int px, u_int py)
{ {
grid_set_padding(gd, grid_view_x(gd, px), grid_view_y(gd, py), bg); grid_set_padding(gd, grid_view_x(gd, px), grid_view_y(gd, py));
} }
/* Set cells. */ /* Set cells. */

128
grid.c
View File

@@ -1,4 +1,4 @@
/* $OpenBSD: grid.c,v 1.158 2026/09/01 12:49:49 nicm Exp $ */ /* $OpenBSD$ */
/* /*
* Copyright (c) 2008 Nicholas Marriott <nicholas.marriott@gmail.com> * Copyright (c) 2008 Nicholas Marriott <nicholas.marriott@gmail.com>
@@ -60,6 +60,21 @@ static const struct grid_cell_entry grid_cleared_entry = {
}; };
#ifdef __APPLE__ #ifdef __APPLE__
static void
grid_check_lines(struct grid *gd)
{
u_int i, j;
for (i = 0; i < gd->hsize + gd->sy; i++) {
for (j = i + 1; j < gd->hsize + gd->sy; j++) {
if (gd->linedata[i].celldata != NULL)
assert(gd->linedata[i].celldata != gd->linedata[j].celldata);
if (gd->linedata[i].extddata != NULL)
assert(gd->linedata[i].extddata != gd->linedata[j].extddata);
}
}
}
void void
grid_check_is_clear(struct grid *gd) grid_check_is_clear(struct grid *gd)
{ {
@@ -89,6 +104,11 @@ grid_check_is_clear(struct grid *gd)
} }
} }
#else #else
static void
grid_check_lines(__unused struct grid *gd)
{
}
void void
grid_check_is_clear(__unused struct grid *gd) grid_check_is_clear(__unused struct grid *gd)
{ {
@@ -233,25 +253,6 @@ grid_get_line(struct grid *gd, u_int line)
return (&gd->linedata[line]); return (&gd->linedata[line]);
} }
/* Get line time. */
time_t
grid_line_time(const struct grid_line *gl)
{
if (gl->time == 0)
return (0);
return (start_time.tv_sec + gl->time - 1);
}
/* Set line time. */
static void
grid_line_set_time(struct grid_line *gl)
{
if (current_time == 0)
gl->time = 0;
else
gl->time = current_time - start_time.tv_sec + 1;
}
/* Adjust number of lines. */ /* Adjust number of lines. */
void void
grid_adjust_lines(struct grid *gd, u_int lines) grid_adjust_lines(struct grid *gd, u_int lines)
@@ -385,9 +386,6 @@ grid_create(u_int sx, u_int sy, u_int hlimit)
if (gd->sy != 0) if (gd->sy != 0)
gd->linedata = xcalloc(gd->sy, sizeof *gd->linedata); gd->linedata = xcalloc(gd->sy, sizeof *gd->linedata);
#ifdef __APPLE__
assert(gd->hsize == 0);
#endif
grid_check_is_clear(gd); grid_check_is_clear(gd);
return (gd); return (gd);
} }
@@ -505,9 +503,11 @@ grid_scroll_history(struct grid *gd, u_int bg)
gd->hscrolled++; gd->hscrolled++;
grid_compact_line(&gd->linedata[gd->hsize]); grid_compact_line(&gd->linedata[gd->hsize]);
grid_line_set_time(&gd->linedata[gd->hsize]); gd->linedata[gd->hsize].time = current_time;
gd->hsize++; gd->hsize++;
gd->scroll_added++; gd->scroll_added++;
grid_check_lines(gd);
} }
/* Clear the history. */ /* Clear the history. */
@@ -547,7 +547,7 @@ grid_scroll_history_region(struct grid *gd, u_int upper, u_int lower, u_int bg)
/* Move the line into the history. */ /* Move the line into the history. */
memcpy(gl_history, gl_upper, sizeof *gl_history); memcpy(gl_history, gl_upper, sizeof *gl_history);
grid_line_set_time(gl_history); gl_history->time = current_time;
/* Then move the region up and clear the bottom line. */ /* Then move the region up and clear the bottom line. */
memmove(gl_upper, gl_upper + 1, (lower - upper) * sizeof *gl_upper); memmove(gl_upper, gl_upper + 1, (lower - upper) * sizeof *gl_upper);
@@ -557,6 +557,8 @@ grid_scroll_history_region(struct grid *gd, u_int upper, u_int lower, u_int bg)
gd->hscrolled++; gd->hscrolled++;
gd->hsize++; gd->hsize++;
gd->scroll_added++; gd->scroll_added++;
grid_check_lines(gd);
} }
/* Expand line to fit to cell. */ /* Expand line to fit to cell. */
@@ -682,13 +684,9 @@ 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, int bg) grid_set_padding(struct grid *gd, u_int px, u_int py)
{ {
struct grid_cell gc; grid_set_cell(gd, px, py, &grid_padding_cell);
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. */
@@ -822,6 +820,8 @@ grid_move_lines(struct grid *gd, u_int dy, u_int py, u_int ny, u_int bg)
} }
if (py != 0 && (py < dy || py >= dy + ny)) if (py != 0 && (py < dy || py >= dy + ny))
gd->linedata[py - 1].flags &= ~GRID_LINE_WRAPPED; gd->linedata[py - 1].flags &= ~GRID_LINE_WRAPPED;
grid_check_lines(gd);
} }
/* Move a group of cells. */ /* Move a group of cells. */
@@ -1311,6 +1311,8 @@ grid_duplicate_lines(struct grid *dst, u_int dy, struct grid *src, u_int sy,
sy++; sy++;
dy++; dy++;
} }
grid_check_lines(dst);
} }
/* Mark line as dead. */ /* Mark line as dead. */
@@ -1607,6 +1609,8 @@ grid_reflow(struct grid *gd, u_int sx)
gd->linedata = target->linedata; gd->linedata = target->linedata;
free(target); free(target);
gd->scroll_generation++; gd->scroll_generation++;
grid_check_lines(gd);
} }
/* Convert to position based on wrapped lines. */ /* Convert to position based on wrapped lines. */
@@ -1635,7 +1639,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, ey = gd->hsize + gd->sy - 1; u_int yy, ay = 0;
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)
@@ -1649,7 +1653,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 (yy < ey && gd->linedata[yy].flags & GRID_LINE_WRAPPED) while (gd->linedata[yy].flags & GRID_LINE_WRAPPED)
yy++; yy++;
wx = gd->linedata[yy].cellused; wx = gd->linedata[yy].cellused;
} else { } else {
@@ -1685,55 +1689,27 @@ grid_line_length(struct grid *gd, u_int py)
return (px); return (px);
} }
/* Get last position on line, not including padding. */
u_int
grid_line_limit(struct grid *gd, u_int py)
{
struct grid_cell gc;
u_int px;
px = grid_line_length(gd, py);
if (px == 0)
return (0);
px--;
while (px > 0) {
grid_get_cell(gd, px, py, &gc);
if (~gc.flags & GRID_FLAG_PADDING)
break;
px--;
}
return (px);
}
/* Check if character is in set. */ /* Check if character is in set. */
int int
grid_in_set(struct grid *gd, u_int px, u_int py, const char *set) 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 (gc.flags & GRID_FLAG_PADDING) { if (strchr(set, '\t')) {
if (!has_tab && !has_space) if (gc.flags & GRID_FLAG_PADDING) {
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) && return (tmp_gc.data.width - (px - pxx));
(tmp_gc.flags & GRID_FLAG_TAB)) || } else if (gc.flags & GRID_FLAG_TAB)
(has_space && utf8_has_whitespace(&tmp_gc.data))) return (gc.data.width);
return (tmp_gc.data.width - (px - pxx));
return (0);
} }
if ((has_tab || has_space) && (gc.flags & GRID_FLAG_TAB)) if (gc.flags & GRID_FLAG_PADDING)
return (gc.data.width); return (0);
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));
} }
@@ -1752,14 +1728,8 @@ grid_line_flags_string(int flags)
strlcat(s, "DEAD,", sizeof s); strlcat(s, "DEAD,", sizeof s);
if (flags & GRID_LINE_START_PROMPT) if (flags & GRID_LINE_START_PROMPT)
strlcat(s, "START_PROMPT,", sizeof s); strlcat(s, "START_PROMPT,", sizeof s);
if (flags & GRID_LINE_SECOND_PROMPT)
strlcat(s, "SECOND_PROMPT,", sizeof s);
if (flags & GRID_LINE_START_COMMAND)
strlcat(s, "START_COMMAND,", sizeof s);
if (flags & GRID_LINE_START_OUTPUT) if (flags & GRID_LINE_START_OUTPUT)
strlcat(s, "START_OUTPUT,", sizeof s); strlcat(s, "START_OUTPUT,", sizeof s);
if (flags & GRID_LINE_END_OUTPUT)
strlcat(s, "END_OUTPUT,", sizeof s);
if (flags & GRID_LINE_HYPERLINK) if (flags & GRID_LINE_HYPERLINK)
strlcat(s, "HYPERLINK,", sizeof s); strlcat(s, "HYPERLINK,", sizeof s);
if (*s == '\0') if (*s == '\0')

Some files were not shown because too many files have changed in this diff Show More