Compare commits

..

207 Commits

Author SHA1 Message Date
Nicholas Marriott
1a9ac07961 Add test images. 2026-03-13 16:48:39 +00:00
Thomas Adam
274ebb2613 Feedback from nicm 2026-03-12 08:20:05 +00:00
Thomas Adam
9c3ec2b8eb Refactor kitty images to use unified image cache API
The original kitty implementation used a passthrough approach where images
were forwarded directly to the outer terminal without being stored in tmux's
image cache.

This refactors kitty images to work like sixel images.
2026-03-05 17:21:40 +00:00
Thomas Adam
aefdb34846 rename: ENABLE_SIXEL to ENABLE_SIXEL_IMAGES 2026-03-05 17:21:18 +00:00
Thomas Adam
5a233e26ec rename ENABLE_KITTY to ENABLE_KITTY_IMAGES 2026-03-05 17:21:18 +00:00
Thomas Adam
1634db8589 configure: deprecate enable-sixel
This moves --enable-sixel to --enable-sixel-images
2026-03-05 17:21:18 +00:00
Thomas Adam
3b7e9fb175 format: add image_support
This adds a new image_support format which returns:

   "sixel" -- if sixel is compiled in
   "kitty" -- if kitty is compiled in
   "kitty,sixel" -- if compiled with both kitty and sixel
2026-03-05 17:21:18 +00:00
Thomas Adam
cf6cbe430c Add support for the kitty graphics protocol
Kitty-capable terminals (kitty, ghostty, and others) can display
inline images via APC escape sequences.

Kitty's image support uses a passthrough model: APC sequences from
programs running inside panes are relayed verbatim to the outer
terminal with cursor positioning adjusted for the pane offset.

The outer terminal handles all image rendering and therefore itself must
be kitty-aware.
2026-03-05 17:21:18 +00:00
Thomas Adam
d9d2b2f1ee Merge branch 'obsd-master' 2026-03-05 12:01:09 +00:00
nicm
1e208abd93 Add pane_pipe_pid with pipe fd and call setpgid to make it easier to kill. 2026-03-05 09:22:08 +00:00
Thomas Adam
d32088b58a Merge branch 'obsd-master' 2026-03-04 12:01:09 +00:00
nicm
6d37cc94a7 Make -c work with new-session -A, from Jody Frankowski in GitHub issue
4906.
2026-03-04 08:16:47 +00:00
nicm
55722a7ed7 Another memory leak, from Huihui Huang. 2026-03-04 08:15:24 +00:00
tb
d2fa20a390 tmux: use VIS_NOSLASH to avoid annoying double escaping on paste
from nicm
2026-03-04 07:19:32 +00:00
tb
bcbad050d4 tmux: tc can be NULL, so check before deref to avoid crashing the server
ok nicm
2026-03-04 07:17:01 +00:00
Thomas Adam
27698c468b Merge branch 'obsd-master' 2026-03-03 16:01:09 +00:00
nicm
49bb43047d Allow copy mode to work for readonly clients, except for copy commands,
from Dane Jensen.
2026-03-03 12:26:14 +00:00
nicm
77ce0a837d Check window is not NULL, from Chema Gonzalez in GitHub issue 4908. 2026-03-03 12:24:18 +00:00
Thomas Adam
bbbfa8f360 Merge branch 'obsd-master' 2026-03-02 12:01:09 +00:00
Nicholas Marriott
d781d2eaa1 Add regression test from Ilya Grigoriev, GitHub issue 4818. 2026-03-02 09:32:45 +00:00
nicm
f33b14bda6 Revert r1.343 for the moment since it breaks behaviour (#() in
status-left) that we need to keep.
2026-03-02 08:48:57 +00:00
nicm
e603549563 Do not leak active/all strings in format_loop_windows; from Huihui Huang
in GitHub issue 4898.
2026-03-02 08:41:43 +00:00
nicm
e80e0c761a Fix incorrect placement of It Xo, from Dane Jensen. 2026-03-02 08:39:44 +00:00
nicm
9e804202b6 Fix memory leak, from Emmanuel Ugwu in GitHub issue 4900. 2026-03-02 08:38:23 +00:00
Thomas Adam
0c678b1e1a mailcap: update entry for Thomas Adam 2026-03-01 17:01:08 +00:00
Thomas Adam
4cb29deb93 Merge branch 'obsd-master' 2026-02-27 12:01:08 +00:00
nicm
5413953d30 Validate -O flags, from Dane Jensen in GitHub issue 4889. 2026-02-27 08:25:12 +00:00
nicm
50e51b4513 Fix memory leak, from Chris Lewis, reported by Huihui Huang. 2026-02-27 08:23:02 +00:00
Thomas Adam
ee0647aba6 Merge branch 'obsd-master' 2026-02-26 16:01:09 +00:00
Nicholas Marriott
35be70f1f0 Add DECRPM test from David Turnbull. 2026-02-26 11:32:15 +00:00
nicm
dbb23d2182 Do not free buffer after adding to paste (since it now owns it). 2026-02-26 11:01:48 +00:00
Nicholas Marriott
478eaea982 Update base64 compat. 2026-02-26 10:55:41 +00:00
Thomas Adam
0800e51d41 Merge branch 'obsd-master' 2026-02-26 10:18:15 +00:00
nicm
5ff385f8a9 Tweak previous to reset cursor again as well. 2026-02-25 07:59:45 +00:00
nicm
5b3c642195 Pass paste buffer through vis(3) when pasting to prevent buffers
containing for example the bracket end sequence causing issues. -S flag
disables. Reported by Mason Davis.
2026-02-25 07:53:41 +00:00
nicm
bd6e201926 Do not expand #() in E: and T:, from Mason Davis. 2026-02-24 18:06:41 +00:00
Nicholas Marriott
08779aa2a3 Fix breakage in fuzzer, from David Korczynski, GitHub issue 4871. 2026-02-24 08:27:59 +00:00
nicm
09bd686327 Add sorting (-O flag) and a custom format (-F) to list-keys, from Dane
Jensen in GitHub issue 4845.
2026-02-24 08:20:52 +00:00
nicm
9316476a73 Add commands to turn exit on scroll on, off, toggle. From xcdnlgd at
hotmail dot com in GitHub issue 4884.
2026-02-24 08:00:43 +00:00
nicm
00946007d8 Respond to DECRQM 2026, from David Turnbull in GitHub issue 4887. 2026-02-24 07:50:27 +00:00
Thomas Adam
bc47bc7c22 Merge branch 'obsd-master' 2026-02-23 12:01:08 +00:00
Thomas Adam
dc693601f4 Merge branch 'obsd-master' 2026-02-23 10:01:08 +00:00
nicm
f1f9c63cdb Free history entries properly, from Huihui Huang in GitHub issue 4870. 2026-02-23 09:12:57 +00:00
nicm
0cc4f0fd76 Use buffer size for b64_pton, from someone in GitHub issue 4882. 2026-02-23 09:08:07 +00:00
nicm
0dc1b5adfb Do not leak list on failure, reported by Huihui Huang. 2026-02-23 08:58:40 +00:00
nicm
a76e6eca6b Another memory leak from Huihui Huang. 2026-02-23 08:54:56 +00:00
nicm
8c7278b53a Memory leaks in cmd_display_menu, from Huihui Huang. 2026-02-23 08:50:00 +00:00
nicm
50a3b4c777 Free format on -a, reported by Huihui Huang. 2026-02-23 08:46:57 +00:00
nicm
3094ca1da5 Fix memory leaks, reported by Huihui Huang in GitHub issue 4872. 2026-02-23 08:45:27 +00:00
nicm
23ad10c849 Set cell to default when off screen to avoid crash when logging it. 2026-02-23 08:29:30 +00:00
Thomas Adam
c9fb989a31 Merge branch 'obsd-master' 2026-02-23 08:11:38 +00:00
Nicholas Marriott
8356578a54 Update tmux bash completion file link in README, from Hyun Seungmin. 2026-02-20 08:43:35 +00:00
nicm
0310404155 Reuse the same extended slot when clearing non-RGB cells as well. From
Michael K Darling in GitHub issue 4865.
2026-02-20 08:41:23 +00:00
nicm
03f8690f9c Pass which clipboard is set through to the terminal, from Axel Lindskog
in GitHub issue 4858.
2026-02-18 09:10:31 +00:00
Thomas Adam
4cc3de4f84 Merge branch 'obsd-master' 2026-02-17 08:51:05 +00:00
nicm
fedd4440f0 Reuse extended entry when clearing RGB cell, to prevent memory growth
when cells are repeatedly cleared (they are only compacted when scrolled
off screen). From Michael K Darling in GitHub issue 4862.
2026-02-17 07:42:58 +00:00
nicm
55cedc24fa Format layout change string once per window in control notify, from
Conor Taylor in GitHub issue 4848.
2026-02-16 08:45:38 +00:00
nicm
c9162837a0 Pull format allocation outside of loop for control subs, from Conor
Taylor in GitHub issue 4848.
2026-02-16 08:42:57 +00:00
nicm
f218463976 grid_peek_cell can return NULL, so check for it. From Conor Taylor in
GitHub issue 4848.
2026-02-16 08:02:04 +00:00
nicm
1f0c54f7ea Initialize bufname, reported by Mark Kelly. 2026-02-15 17:43:26 +00:00
Thomas Adam
d3522c328c Merge branch 'obsd-master' 2026-02-12 11:10:01 +00:00
nicm
7e50eb0e83 Make paste_get_top return a copy of the buffer name which is more
sensible and avoids a double free pointed out by DongHan Kim.
2026-02-11 08:30:37 +00:00
nicm
5b455abecc Do not write before buffer when parsing empty clipboard or palette
replies, or try to allocate zero bytes with an empty clipboard sequence.
Reported by DongHan Kim.
2026-02-11 08:23:21 +00:00
nicm
7d41761e84 Fix clients_calculate_size for manual type when window is NULL. From
Elias Oenal in GitHub issue 4849.
2026-02-10 10:02:11 +00:00
nicm
25f6d8b1e9 Implement some obvious missing sort orders, from Dane Jensen in GitHub
issue 4813.
2026-02-10 09:55:53 +00:00
nicm
5a33616e65 Check for no window when updating clients, GitHub issue 4851. 2026-02-10 09:00:30 +00:00
nicm
1cf17b06ae Batch printable output in control mode, from Conor Taylor in GitHub issue 4848. 2026-02-10 08:40:03 +00:00
nicm
aa03706ed0 Remove redundant call to tty_attributes, from Conor Taylor in GitHub
issue 4848.
2026-02-10 08:34:43 +00:00
nicm
d8794e2b30 Check log level before log_debug in tty_draw_line, from Conor Taylor in
GitHub issue 4848.
2026-02-10 08:31:45 +00:00
nicm
b7939eb266 Don't call event_add again if the event is already pending, from Conor
Taylor in GitHub issue 4848.
2026-02-10 08:30:21 +00:00
nicm
19b9a34c48 Only loop over clients if table actually found, from Conor Taylor in
GitHub issue 4848.
2026-02-10 08:28:53 +00:00
nicm
f016e08153 Fix wrong TAILQ member in input_cancel_requests from Conor Taylor in
GitHub issue 4848.
2026-02-10 08:27:17 +00:00
nicm
7b1c503086 Clear search counts when clearing marks in case of repeated search,
reported by Daniel Pereira in GitHub issue 4817.
2026-02-06 10:28:42 +00:00
nicm
5865001e4a Also check PANE_STATUSREADY for pane_dead format to match
pane_dead_status. GitHub issue 4841 from Joshua Pollack.
2026-02-06 10:23:26 +00:00
nicm
62944da74b Make OSC 52 work in popups, from gogongxt at 163 dot com in GitHub issue
4797.
2026-02-03 09:07:44 +00:00
nicm
588013bb44 Refresh copy mode when style changes, from Josh Cooper in GitHub issue 4830. 2026-02-03 08:53:58 +00:00
Thomas Adam
615c27c117 Merge branch 'obsd-master' 2026-02-02 19:06:25 +00:00
nicm
3c3d9ce3a9 Break sorting out into a common file so formats and modes use the same
code. Also add -O for sorting to the list commands. From Dane Jensen in
GitHub issue 4813.
2026-02-02 10:08:30 +00:00
nicm
66011fe48b Do not treat cells as empty unless the background colour stays the same.
Fixes invisible clock in clock mode, reported by tb@.
2026-01-29 09:08:19 +00:00
Thomas Adam
d7f59eccaf Merge branch 'obsd-master' 2026-01-28 17:25:42 +00:00
Thomas Adam
e652213855 Merge branch 'obsd-master' 2026-01-23 10:50:55 +00:00
nicm
f70150a663 Replace overlay_ranges with visible_ranges which can hold more than
three ranges (will be needed for floating panes); move the visible
ranges checks outside of tty_draw_line and rewrite it to fix issues with
partially-obscured wide characters. With Michael Grant.
2026-01-23 10:45:53 +00:00
Nicholas Marriott
fe52f76913 Fiddle with some more warnings. 2026-01-22 15:02:42 +00:00
Nicholas Marriott
0d9c3c895c Stick the pragmas outside the function (make GCC 4 happier) and under __GNUC__. 2026-01-22 14:29:29 +00:00
Nicholas Marriott
6525bb7cef Line length/style nits. 2026-01-22 14:16:39 +00:00
Michael Grant
0790e74f84 Fix strftime warning in gcc. 2026-01-22 14:11:00 +00:00
nicm
195a9cfd88 When history-limit is changed, apply to existing panes, not just new
ones. GitHub issue 4705.
2026-01-22 08:55:01 +00:00
nicm
ecbf8d76d0 Reevaluate menu and popup styles on each draw to allow them to change
when options change, from Josh Cooper in GitHub issues 4828 and 4829.
2026-01-22 07:52:24 +00:00
nicm
818745a605 Set PANE_STYLECHANGED when user options change, from Josh Cooper in
GitHub issue 4825.
2026-01-22 07:42:30 +00:00
nicm
26aacd0e32 Handle theme keys earlier so they are processed even if a popup is open.
From Josh Cooper in GitHub issue 4827.
2026-01-22 07:39:35 +00:00
nicm
8e06739e26 Fix window-size=latest not resizing on switch-client in session groups.
From Ilya Grigoriev in GitHub issue 4818.
2026-01-20 22:50:08 +00:00
nicm
ab1f2ef71c Add a function to convert a screen to a string, from Michael Grant. 2026-01-20 16:32:05 +00:00
Nicholas Marriott
d2e09cb259 Add a --enable-optimizations configure flag. 2026-01-20 15:42:22 +00:00
Thomas Adam
bf8ea85bd7 Merge branch 'obsd-master' 2026-01-19 10:01:08 +00:00
nicm
9c0aeaff40 Now the copy mode indicator can be changed, we need to redraw it when
the cursor is moved. GitHub issue 4774.
2026-01-19 08:20:51 +00:00
Thomas Adam
95b4bb51c6 Merge branch 'obsd-master' 2026-01-14 22:01:08 +00:00
nicm
1e5f93b7b6 Add -e flag to command-prompt to close if empty, from Dane Jensen in
GitHub issue 4812.
2026-01-14 19:43:43 +00:00
Nicholas Marriott
b89d46bb00 Add a regression test from Ilya Grigoriev. 2026-01-13 20:29:21 +00:00
Thomas Adam
97d61131df Merge branch 'obsd-master' 2026-01-12 10:01:08 +00:00
nicm
5721767921 Correctly draw indicators when pane-border-indicators is set to both.
Reported by Ilya Grigoriev in GitHub issue 4780.
2026-01-12 07:50:16 +00:00
nicm
a443531280 Remember last pane or type of location for double and triple clicks and
correctly handle it changes between first and second or second and
third. GitHub issue 4795 from Shaobo Song.
2026-01-12 07:48:36 +00:00
Thomas Adam
ad083677c5 Merge branch 'obsd-master' 2026-01-11 14:01:08 +00:00
nicm
426467856d Add paste to the default pane menu, GitHub issue 4763. 2026-01-11 11:48:46 +00:00
Thomas Adam
844042744e Merge branch 'obsd-master' 2026-01-09 18:01:07 +00:00
nicm
65b3a95be8 Removing padding needs to go through screen_write_collect_trim or there
may end up multiple items covering the same region.
2026-01-09 15:04:00 +00:00
Thomas Adam
ca95950148 Merge branch 'obsd-master' 2026-01-08 14:01:08 +00:00
nicm
6234d79852 Do not set manual size if no window. 2026-01-08 12:05:02 +00:00
Thomas Adam
a5d8b92012 Merge branch 'obsd-master' 2026-01-08 10:01:08 +00:00
nicm
890cfc6dc3 Copy palette responses before processing them since they are not strings. 2026-01-08 07:54:23 +00:00
Thomas Adam
0e6fc2e88b Merge branch 'obsd-master' 2026-01-07 22:01:07 +00:00
nicm
c8ccd420be Reduce request timeout to 500 milliseconds to match the extended
escape-time, and discard palette requests if receiving a reply for a
different index.
2026-01-07 20:16:32 +00:00
nicm
ff4f6b9066 Extend escape timeout if there are active forwarded requests not just
tmux's own requests. GitHub issue 4793.
2026-01-07 20:03:34 +00:00
Thomas Adam
7cffa2de78 Merge branch 'obsd-master' 2026-01-07 20:01:09 +00:00
nicm
e2afaaea75 Correct redrawing of wide characters when overwritten. Reported by Jake
Stewart in GitHub issue 4737.
2026-01-07 18:29:15 +00:00
Thomas Adam
75d3974c12 Merge branch 'obsd-master' 2026-01-07 10:01:08 +00:00
nicm
583f12ea71 Work out the default command from the queue in case it has been set from
the config file, GitHub issue 4791.
2026-01-07 08:16:20 +00:00
Thomas Adam
3de7a8ad8d Merge branch 'obsd-master' 2026-01-06 22:01:07 +00:00
nicm
f6c9052082 If cannot find a terminator for palette responses, treat as a partial
key not complete. GitHub issue 4749.
2026-01-06 20:09:42 +00:00
nicm
f226804149 Do not send theme unless it has changed, and do not send immediately
when updates are enabled. GitHub issue 5787.
2026-01-06 20:05:57 +00:00
Thomas Adam
cf94486e32 Merge branch 'obsd-master' 2026-01-06 16:01:07 +00:00
nicm
035a2f35d4 Clear trimmed lines after moving, from Antony Raj in GitHub issue 4790. 2026-01-06 14:33:05 +00:00
Thomas Adam
9b7732eac4 Merge branch 'obsd-master' 2026-01-06 12:01:07 +00:00
nicm
5f9dac8abc Do not use ;;s in list-keys output as it is confusing and cannot be
parsed on input, from Patrick Motard in GitHub issue 4750.
2026-01-06 10:17:29 +00:00
nicm
ccd4dd7ff2 Do not log theme if pane is NULL. 2026-01-06 09:11:15 +00:00
Thomas Adam
77417b7cbe Merge branch 'obsd-master' 2026-01-05 10:01:08 +00:00
nicm
6a7cd79a63 Redraw pane borders when entering or leaving alternate screen, from Mike
Jonkmans in GitHub issue 4788.
2026-01-05 08:32:19 +00:00
nicm
baa3b51b3e Do not use client if there isn't one, GitHub issue 4789. 2026-01-05 08:30:30 +00:00
Thomas Adam
abeba4910c Merge branch 'obsd-master' 2026-01-04 10:01:08 +00:00
Nicholas Marriott
356d0aedbd Remove an unused declaration (from OpenBSD, same change also sent by Mike
Jonkmans).
2026-01-04 08:41:38 +00:00
nicm
45f23f3a59 Minor tidying of cmd_list_print from Pavel Roskin. 2026-01-04 08:38:26 +00:00
nicm
6ef7375ade Add some missing logging bits for themes. 2026-01-04 08:05:14 +00:00
Thomas Adam
00030aabf7 Merge branch 'obsd-master' 2025-12-25 20:01:08 +00:00
nicm
f72832cc08 Add focus-follows-mouse option, from Barry Wasdell in GitHub issue 4771. 2025-12-25 18:07:00 +00:00
nicm
b5c33ca2b7 Add selection_mode format variable for copy mode, from Mike Jonkmans in
GitHub issue 4773.
2025-12-25 18:05:15 +00:00
Thomas Adam
d607399850 Merge branch 'obsd-master' 2025-12-22 10:01:08 +00:00
nicm
d005803934 Add prompt-command-cursor-style, from Joshua Cooper in GitHub issue 4765. 2025-12-22 08:41:01 +00:00
nicm
a22ec275b4 With status-keys vi, move the cursor left by one when pressing Escape to
enter command mode, like vi. GitHub issue 4767 from Joshua Cooper.
2025-12-22 08:39:35 +00:00
nicm
188f963fe0 Add {current}/{active} for -t for current window or active pane, from
Manuel Einfalt in GitHub issue 4766.
2025-12-22 08:35:04 +00:00
Thomas Adam
9c5df33325 Merge branch 'obsd-master' 2025-12-19 10:01:07 +00:00
nicm
74f60951cc Do not leak items if not using them because of synchronized update. 2025-12-19 08:46:25 +00:00
Nicholas Marriott
54202fcaad Check image size against available screen size properly, GitHub issue 4739. 2025-12-18 13:44:17 +00:00
Nicholas Marriott
fe645c5bcc Fix image scrolling at exact screen height, and add more logging for images. 2025-12-18 09:05:37 +00:00
Thomas Adam
86c15d7ae9 Merge branch 'obsd-master' 2025-12-17 14:01:08 +00:00
Thomas Adam
3c36d5e433 Merge branch 'obsd-master' 2025-12-17 12:01:07 +00:00
nicm
1c7e164c22 Add support for applications to use synchronized output mode (DECSET
2026) to prevent screen tearing during rapid updates. When an
application sends SM ?2026, tmux buffers output until RM ?2026 is received
or a 1-second timeout expires.

From Chris Lloyd with the assistance of Claude Code, GitHub issue 4744.
2025-12-17 11:49:29 +00:00
nicm
ce7eb22e3a Do not have a default prompt cursor colour because some terminals
(urxvt, st) do not support the reset sequence. GitHub issue 4759.
2025-12-17 10:20:21 +00:00
Thomas Adam
739a97140f Merge branch 'obsd-master' 2025-12-17 10:01:07 +00:00
nicm
99ed397e9c Make clock mode seconds synchronized to the second, GitHub issue 4760
from Joao Pedro.
2025-12-17 08:38:12 +00:00
Thomas Adam
d2c7668bc2 Merge branch 'obsd-master' 2025-12-16 00:01:08 +00:00
nicm
7abf3e8bdf Note that switch-client only changes the key table for one key, pointed
out by Jeenu Viswambharan.
2025-12-15 21:21:25 +00:00
Thomas Adam
506b4db762 Merge branch 'obsd-master' 2025-12-15 15:19:29 +00:00
bket
21c27fdcae Simplify argument move using TAILQ_CONCAT()
Replace the manual loop moving each argument from cmd->arguments to
last->arguments with a single TAILQ_CONCAT() call. This makes the code
clearer and more efficient, while preserving identical behavior.

OK nicm@
2025-12-11 04:17:17 +00:00
nicm
672e89a640 Add a scroll-to-mouse command for copy mode to scroll to the mouse
position and bind to the scrollbar, brings the scrollbar keys into line
with the other mouse keys. From Michael Grant, GitHub issue 4731.
2025-12-10 21:24:43 +00:00
Nicholas Marriott
4b810ae493 Add a define for max images and bump to 20 for the moment. 2025-12-09 08:46:00 +00:00
nicm
52e2a7d990 Fix key code for M-BSpace, GitHub issue 4717. 2025-12-09 08:13:59 +00:00
Nicholas Marriott
2e5e9c0298 Fix calculation of scaled SIXEL size, from nincsnevem662 at gmail dot com in
GitHub issue 4739.
2025-12-08 21:41:30 +00:00
nicm
afa05ae15e Use correct style for bottom line when pane status line is on, GitHub
issue 4732.
2025-12-08 21:32:50 +00:00
nicm
f58b8d0d6a Setting working directory after fork means there is a race with
pane_current_path (especially on platforms with systemd which have to
take time to do some additional faffing around). To avoid this, change
it before fork and back in the parent afterwards. GitHub issue 4719.
2025-12-08 08:04:35 +00:00
Nicholas Marriott
5c73d3878d FIx combine test again... 2025-12-07 22:11:45 +00:00
Nicholas Marriott
ef0a7e328c Merge branch 'tmux-3.6a' 2025-12-05 06:20:32 +00:00
Nicholas Marriott
cc117b5048 Update CHANGES. 2025-12-05 05:39:46 +00:00
nicm
faebe7a70a Do not read over buffer if format is a single #, and do not loop forever
if UTF-8 is unfinished in a format. Reported by Giorgi Kobakhia im
GitHub issue 4735.
2025-12-05 05:38:05 +00:00
nicm
01962e25dc Allow drag in alternate screen again, GitHub issue 4743 reported by Brad
King.
2025-12-05 05:37:57 +00:00
nicm
ff207eb583 Fix y offset of mouse if status at top. GitHub issue 4738 from Michael
Grant.
2025-12-05 05:37:48 +00:00
Thomas Adam
8e922ab690 Merge branch 'obsd-master' 2025-12-05 02:01:07 +00:00
Thomas Adam
1a3db6b65e Merge branch 'obsd-master' 2025-12-05 00:01:08 +00:00
nicm
987e05ff31 Allow drag in alternate screen again, GitHub issue 4743 reported by Brad
King.
2025-12-04 22:50:34 +00:00
Nicholas Marriott
2fc123cf4a Update CHANGES. 2025-12-04 20:54:44 +00:00
nicm
3b57077d01 Add a missing skin tone, from Jake Stewart in GitHub issue 4736. 2025-12-04 20:53:01 +00:00
nicm
33c1ba1549 Allow characters to be combined in either order, reported by Jake
Stewart in GitHub issue 4726.
2025-12-04 20:52:55 +00:00
nicm
bd16b22dac Do not read over buffer if format is a single #, and do not loop forever
if UTF-8 is unfinished in a format. Reported by Giorgi Kobakhia im
GitHub issue 4735.
2025-12-04 20:49:57 +00:00
Thomas Adam
0929d8ddfa Merge branch 'obsd-master' 2025-12-04 16:01:08 +00:00
nicm
9d6c69ebde Fix y offset of mouse if status at top. GitHub issue 4738 from Michael
Grant.
2025-12-04 14:45:32 +00:00
Thomas Adam
1bcd360dfe Merge branch 'obsd-master' 2025-12-04 08:01:07 +00:00
nicm
1f2210a3ce Add a missing skin tone, from Jake Stewart in GitHub issue 4736. 2025-12-04 06:04:21 +00:00
nicm
2fe1378d3a Allow characters to be combined in either order, reported by Jake
Stewart in GitHub issue 4726.
2025-12-04 06:02:27 +00:00
Thomas Adam
796539c60b Merge branch 'obsd-master' 2025-12-03 10:01:09 +00:00
Thomas Adam
700936b2ad Merge branch 'obsd-master' 2025-12-03 08:01:07 +00:00
nicm
a28dbe3a59 Improve code readability in colour_palette_get and colour_palette_set.
GitHub issue 4730 from Pavel Roskin.
2025-12-03 07:41:38 +00:00
nicm
820df0f551 Add check that the pane is not in alternate screen mode when in
copy-mode. From Michael Grant in GitHub issue 4728.
2025-12-03 07:38:46 +00:00
nicm
ee9cf1bbaa Fix the size caluation for left-right windows used to spread out cells
horizontally evenly. From Michael Grant in GitHub issue 4724.
2025-12-03 07:35:32 +00:00
Nicholas Marriott
52917abe21 Update CHANGES. 2025-12-02 14:26:30 +00:00
nicm
a40f98df0a Change noattr to be an explicit attribute in the style so that it works
correctly and does not delete attributes set in the style itself, GitHub
issue 4713.
2025-12-02 14:25:05 +00:00
nicm
2c78a5aceb Add horizontal border case to server_client_check_mouse_in_pane to fix
mouse resizing. GitHub issue 4720 from Michael Grant, reported by
someone in GitHub issue 4715.
2025-12-02 14:24:57 +00:00
Thomas Adam
02a8e3fd34 Merge branch 'obsd-master' 2025-12-02 10:01:08 +00:00
nicm
322adfbdde Add a get-clipboard option which when enabled (the default is off) uses
the same mechanism as palette requests to request clipboard from the
terminal and forward to the requesting pane. Remove the now-redundant
forward-to-pane ability from "refresh-client -l". GitHub issue 4275.
2025-12-02 08:20:32 +00:00
Thomas Adam
194d0a0e25 Merge branch 'obsd-master' 2025-12-01 10:01:08 +00:00
Nicholas Marriott
b2d6ebaa10 Fix combine-test.result, GitHub issue 4717. 2025-12-01 08:22:12 +00:00
Nicholas Marriott
bfecbb0685 Fix combine-test.result, GitHub issue 4717. 2025-12-01 08:21:04 +00:00
nicm
e4c552f5a5 Change noattr to be an explicit attribute in the style so that it works
correctly and does not delete attributes set in the style itself, GitHub
issue 4713.
2025-12-01 08:14:29 +00:00
nicm
04f32073c0 Add horizontal border case to server_client_check_mouse_in_pane to fix
mouse resizing. GitHub issue 4720 from Michael Grant, reported by
someone in GitHub issue 4715.
2025-12-01 08:04:26 +00:00
Thomas Adam
e9afd2bb5e Merge branch 'obsd-master' 2025-11-28 12:01:09 +00:00
nicm
55d660a548 Do not remove TERM etc for commands run from config file, reported by
Dennis Eriksen.
2025-11-28 09:42:48 +00:00
nicm
dfaf47d97c session_index was never actually implemented, remove from man page. 2025-11-28 09:14:17 +00:00
Nicholas Marriott
640e1a7643 Update CHANGES. 2025-11-27 07:26:09 +00:00
nicm
2a0b078e15 Place cursor on correct line if message-line is not 0, reported by
Alexis Hildebrandt.
2025-11-27 07:24:50 +00:00
nicm
0af04295f3 Newer libevents do not allow event_del on a zero'd event. 2025-11-27 07:24:47 +00:00
Thomas Adam
3542bfa5b8 Merge branch 'obsd-master' 2025-11-26 22:01:08 +00:00
nicm
e3a54ed0f4 Newer libevents do not allow event_del on a zero'd event. 2025-11-26 19:02:03 +00:00
Nicholas Marriott
ec4b5b52af Version and CHANGES. 2025-11-26 19:00:17 +00:00
nicm
a0dfef3b04 Place cursor on correct line if message-line is not 0, reported by
Alexis Hildebrandt.
2025-11-26 18:57:18 +00:00
Nicholas Marriott
6db6a30ab5 Need signal.h for utempter, from Yasuhiro Kimura. 2025-11-26 18:43:11 +00:00
Nicholas Marriott
bfa2f73335 Need signal.h for utempter, from Yasuhiro Kimura. 2025-11-26 10:41:17 +00:00
Nicholas Marriott
1decccace7 Update version. 2025-11-26 08:44:41 +00:00
Nicholas Marriott
ee53d49059 Merge branch 'tmux-3.6' 2025-11-26 08:44:18 +00:00
Thomas Adam
f949f9a23a Merge branch 'obsd-master' 2025-11-26 00:01:08 +00:00
nicm
f0dec832b8 Add seconds options for clock mode, from augustus7613 dot mail at pm dot
me in GitHub issue 4697.
2025-11-25 21:24:27 +00:00
90 changed files with 5109 additions and 2148 deletions

2
.github/README.md vendored
View File

@@ -79,7 +79,7 @@ A small example configuration is in `example_tmux.conf`.
And a bash(1) completion file at:
https://github.com/scop/bash-completion/blob/main/completions/tmux
https://github.com/scop/bash-completion/blob/main/completions-core/tmux.bash
For debugging, run tmux with `-v` or `-vv` to generate server and client log
files in the current directory.

View File

@@ -29,6 +29,7 @@ Ted Unangst <tedu@openbsd.org> tedu <tedu>
Theo de Raadt <deraadt@openbsd.org> Theo Deraadt <deraadt@openbsd.org>
Theo de Raadt <deraadt@openbsd.org> deraadt <deraadt>
Thomas Adam <thomas@xteddy.org> Thomas <thomas@xteddy.org>
Thomas Adam <thomas@xteddy.org> Thomas Adam <thomas.adam22@gmail.com>
Thomas Adam <thomas@xteddy.org> Thomas Adam <thomas.adam@smoothwall.net>
Thomas Adam <thomas@xteddy.org> n6tadam <n6tadam@xteddy.org>
Tim van der Molen <tim@openbsd.org> tim <tim>

26
CHANGES
View File

@@ -1,3 +1,29 @@
CHANGES FROM 3.6 TO 3.6a
* Fix a buffer overread and an infinite loop in format processing (reported by
Giorgi Kobakhia, issue 4735).
* Allow drag in alternate screen again (issue 4743 reported by Brad King).
* Fix y offset of mouse if status at top (issue 4738 from Michael Grant).
* Add a missing skin tone (from Jake Stewart, issue 4736).
* Allow characters to be combined in either order (issue 4726, reported by Jake
Stewart).
* Fix horizontal mouse resizing when pane status lines are on (from Michael
Grant, issue 4720).
* Fix noattr so it does not delete attributes set in the style itself (issue
4713).
* Newer libevents do not allow event_del on a zero'd event (issue 4706).
* Place cursor on correct line if message-line is not 0 (issue 4707).
* Fix compile error on FreeBSD (from Yasuhiro Kimura, issue 4701).
CHANGES FROM 3.5a TO 3.6
* Add seconds options for clock mode (issue 4697).

View File

@@ -20,17 +20,23 @@ LDADD = $(LIBOBJS)
# Set flags for gcc.
if IS_GCC
AM_CFLAGS += -std=gnu99 -O2
AM_CFLAGS += -std=gnu99
if IS_OPTIMIZED
AM_CFLAGS += -O2
else
AM_CFLAGS += -O0
endif
if IS_DEBUG
AM_CFLAGS += -g
AM_CFLAGS += -Wno-long-long -Wall -W -Wformat=2
AM_CFLAGS += -Wno-long-long -Wall -W -Wformat=2 -Wno-use-after-free
AM_CFLAGS += -Wmissing-prototypes -Wstrict-prototypes -Wmissing-declarations
AM_CFLAGS += -Wwrite-strings -Wshadow -Wpointer-arith -Wsign-compare
AM_CFLAGS += -Wundef -Wbad-function-cast -Winline -Wcast-align
AM_CFLAGS += -Wundef -Wbad-function-cast -Winline -Wno-cast-align
AM_CFLAGS += -Wdeclaration-after-statement -Wno-pointer-sign -Wno-attributes
AM_CFLAGS += -Wno-unused-result -Wno-format-y2k
AM_CFLAGS += -Wno-unused-result -Wno-format-y2k -Wno-unknown-warning-option
AM_CFLAGS += -Wno-maybe-uninitialized
if IS_DARWIN
AM_CFLAGS += -Wno-deprecated-declarations -Wno-cast-align -Wno-macro-redefined
AM_CFLAGS += -Wno-deprecated-declarations -Wno-macro-redefined
endif
AM_CPPFLAGS += -DDEBUG
endif
@@ -100,6 +106,7 @@ dist_tmux_SOURCES = \
cmd-kill-window.c \
cmd-list-buffers.c \
cmd-list-clients.c \
cmd-list-commands.c \
cmd-list-keys.c \
cmd-list-panes.c \
cmd-list-sessions.c \
@@ -183,6 +190,7 @@ dist_tmux_SOURCES = \
server-fn.c \
server.c \
session.c \
sort.c \
spawn.c \
status.c \
style.c \
@@ -190,6 +198,7 @@ dist_tmux_SOURCES = \
tmux.h \
tmux-protocol.h \
tty-acs.c \
tty-draw.c \
tty-features.c \
tty-keys.c \
tty-term.c \
@@ -223,8 +232,18 @@ nodist_tmux_SOURCES += compat/utf8proc.c
endif
# Enable sixel support.
if ENABLE_SIXEL
if ENABLE_SIXEL_IMAGES
dist_tmux_SOURCES += image.c image-sixel.c
else
# If not sixel, still need image.c for kitty.
if ENABLE_KITTY_IMAGES
dist_tmux_SOURCES += image.c
endif
endif
# Enable kitty graphics protocol support.
if ENABLE_KITTY_IMAGES
dist_tmux_SOURCES += image-kitty.c
endif
if NEED_FUZZING

View File

@@ -31,7 +31,7 @@ attributes_tostring(int attr)
if (attr == 0)
return ("none");
len = xsnprintf(buf, sizeof buf, "%s%s%s%s%s%s%s%s%s%s%s%s%s%s",
len = xsnprintf(buf, sizeof buf, "%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s",
(attr & GRID_ATTR_CHARSET) ? "acs," : "",
(attr & GRID_ATTR_BRIGHT) ? "bright," : "",
(attr & GRID_ATTR_DIM) ? "dim," : "",
@@ -45,7 +45,8 @@ attributes_tostring(int attr)
(attr & GRID_ATTR_UNDERSCORE_3) ? "curly-underscore," : "",
(attr & GRID_ATTR_UNDERSCORE_4) ? "dotted-underscore," : "",
(attr & GRID_ATTR_UNDERSCORE_5) ? "dashed-underscore," : "",
(attr & GRID_ATTR_OVERLINE) ? "overline," : "");
(attr & GRID_ATTR_OVERLINE) ? "overline," : "",
(attr & GRID_ATTR_NOATTR) ? "noattr," : "");
if (len > 0)
buf[len - 1] = '\0';

View File

@@ -98,6 +98,13 @@ cmd_choose_tree_exec(struct cmd *self, struct cmdq_item *item)
struct cmd_find_state *target = cmdq_get_target(item);
struct window_pane *wp = target->wp;
const struct window_mode *mode;
enum sort_order order;
order = sort_order_from_string(args_get(args, 'O'));
if (order == SORT_END && args_has(args, 'O')) {
cmdq_error(item, "invalid sort order");
return (CMD_RETURN_ERROR);
}
if (cmd_get_entry(self) == &cmd_choose_buffer_entry) {
if (paste_is_empty())

View File

@@ -42,8 +42,8 @@ const struct cmd_entry cmd_command_prompt_entry = {
.name = "command-prompt",
.alias = NULL,
.args = { "1bFkliI:Np:t:T:", 0, 1, cmd_command_prompt_args_parse },
.usage = "[-1bFkliN] [-I inputs] [-p prompts] " CMD_TARGET_CLIENT_USAGE
.args = { "1beFiklI:Np:t:T:", 0, 1, cmd_command_prompt_args_parse },
.usage = "[-1beFiklN] [-I inputs] [-p prompts] " CMD_TARGET_CLIENT_USAGE
" [-T prompt-type] [template]",
.flags = CMD_CLIENT_TFLAG,
@@ -84,7 +84,7 @@ cmd_command_prompt_exec(struct cmd *self, struct cmdq_item *item)
struct client *tc = cmdq_get_target_client(item);
struct cmd_find_state *target = cmdq_get_target(item);
const char *type, *s, *input;
struct cmd_command_prompt_cdata *cdata;
struct cmd_command_prompt_cdata *cdata;
char *tmp, *prompts, *prompt, *next_prompt;
char *inputs = NULL, *next_input;
u_int count = args_count(args);
@@ -163,6 +163,8 @@ cmd_command_prompt_exec(struct cmd *self, struct cmdq_item *item)
cdata->flags |= PROMPT_INCREMENTAL;
else if (args_has(args, 'k'))
cdata->flags |= PROMPT_KEY;
else if (args_has(args, 'e'))
cdata->flags |= PROMPT_BSPACE_EXIT;
status_prompt_set(tc, target, cdata->prompts[0].prompt,
cdata->prompts[0].input, cmd_command_prompt_callback,
cmd_command_prompt_free, cdata, cdata->flags, cdata->prompt_type);
@@ -234,7 +236,7 @@ out:
static void
cmd_command_prompt_free(void *data)
{
struct cmd_command_prompt_cdata *cdata = data;
struct cmd_command_prompt_cdata *cdata = data;
u_int i;
for (i = 0; i < cdata->count; i++) {

View File

@@ -92,6 +92,7 @@ cmd_confirm_before_exec(struct cmd *self, struct cmdq_item *item)
cdata->confirm_key = confirm_key[0];
else {
cmdq_error(item, "invalid confirm key");
cmd_list_free(cdata->cmdlist);
free(cdata);
return (CMD_RETURN_ERROR);
}

View File

@@ -36,7 +36,7 @@ const struct cmd_entry cmd_copy_mode_entry = {
.source = { 's', CMD_FIND_PANE, 0 },
.target = { 't', CMD_FIND_PANE, 0 },
.flags = CMD_AFTERHOOK,
.flags = CMD_AFTERHOOK|CMD_READONLY,
.exec = cmd_copy_mode_exec
};

View File

@@ -294,7 +294,7 @@ cmd_display_menu_exec(struct cmd *self, struct cmdq_item *item)
const char *border_style = args_get(args, 'S');
const char *selected_style = args_get(args, 'H');
enum box_lines lines = BOX_LINES_DEFAULT;
char *title, *cause;
char *title, *cause = NULL;
int flags = 0, starting_choice = 0;
u_int px, py, i, count = args_count(args);
struct options *o = target->s->curw->window->options;
@@ -312,8 +312,7 @@ cmd_display_menu_exec(struct cmd *self, struct cmdq_item *item)
&cause);
if (cause != NULL) {
cmdq_error(item, "starting choice %s", cause);
free(cause);
return (CMD_RETURN_ERROR);
goto fail;
}
}
}
@@ -334,8 +333,7 @@ cmd_display_menu_exec(struct cmd *self, struct cmdq_item *item)
if (count - i < 2) {
cmdq_error(item, "not enough arguments");
menu_free(menu);
return (CMD_RETURN_ERROR);
goto fail;
}
key = args_string(args, i++);
@@ -347,17 +345,13 @@ cmd_display_menu_exec(struct cmd *self, struct cmdq_item *item)
}
if (menu == NULL) {
cmdq_error(item, "invalid menu arguments");
return (CMD_RETURN_ERROR);
}
if (menu->count == 0) {
menu_free(menu);
return (CMD_RETURN_NORMAL);
goto fail;
}
if (menu->count == 0)
goto out;
if (!cmd_display_menu_get_pos(tc, item, args, &px, &py, menu->width + 4,
menu->count + 2)) {
menu_free(menu);
return (CMD_RETURN_NORMAL);
}
menu->count + 2))
goto out;
value = args_get(args, 'b');
if (value != NULL) {
@@ -366,8 +360,7 @@ cmd_display_menu_exec(struct cmd *self, struct cmdq_item *item)
&cause);
if (lines == -1) {
cmdq_error(item, "menu-border-lines %s", cause);
free(cause);
return (CMD_RETURN_ERROR);
goto fail;
}
}
@@ -379,6 +372,15 @@ cmd_display_menu_exec(struct cmd *self, struct cmdq_item *item)
style, selected_style, border_style, target, NULL, NULL) != 0)
return (CMD_RETURN_NORMAL);
return (CMD_RETURN_WAIT);
out:
menu_free(menu);
return (CMD_RETURN_NORMAL);
fail:
free(cause);
menu_free(menu);
return (CMD_RETURN_ERROR);
}
static enum cmd_retval
@@ -393,7 +395,7 @@ cmd_display_popup_exec(struct cmd *self, struct cmdq_item *item)
const char *style = args_get(args, 's');
const char *border_style = args_get(args, 'S');
char *cwd = NULL, *cause = NULL, **argv = NULL;
char *title;
char *title = NULL;
int modify = popup_present(tc);
int flags = -1, argc = 0;
enum box_lines lines = BOX_LINES_DEFAULT;
@@ -417,8 +419,7 @@ cmd_display_popup_exec(struct cmd *self, struct cmdq_item *item)
&cause);
if (cause != NULL) {
cmdq_error(item, "height %s", cause);
free(cause);
return (CMD_RETURN_ERROR);
goto fail;
}
}
@@ -428,8 +429,7 @@ cmd_display_popup_exec(struct cmd *self, struct cmdq_item *item)
&cause);
if (cause != NULL) {
cmdq_error(item, "width %s", cause);
free(cause);
return (CMD_RETURN_ERROR);
goto fail;
}
}
@@ -438,7 +438,7 @@ cmd_display_popup_exec(struct cmd *self, struct cmdq_item *item)
if (h > tty->sy)
h = tty->sy;
if (!cmd_display_menu_get_pos(tc, item, args, &px, &py, w, h))
return (CMD_RETURN_NORMAL);
goto out;
value = args_get(args, 'd');
if (value != NULL)
@@ -478,8 +478,7 @@ cmd_display_popup_exec(struct cmd *self, struct cmdq_item *item)
&cause);
if (cause != NULL) {
cmdq_error(item, "popup-border-lines %s", cause);
free(cause);
return (CMD_RETURN_ERROR);
goto fail;
}
}
@@ -507,22 +506,29 @@ cmd_display_popup_exec(struct cmd *self, struct cmdq_item *item)
if (modify) {
popup_modify(tc, title, style, border_style, lines, flags);
free(title);
return (CMD_RETURN_NORMAL);
goto out;
}
if (popup_display(flags, lines, item, px, py, w, h, env, shellcmd, argc,
argv, cwd, title, tc, s, style, border_style, NULL, NULL) != 0) {
cmd_free_argv(argc, argv);
if (env != NULL)
environ_free(env);
free(cwd);
free(title);
return (CMD_RETURN_NORMAL);
}
if (env != NULL)
environ_free(env);
argv, cwd, title, tc, s, style, border_style, NULL, NULL) != 0)
goto out;
environ_free(env);
free(cwd);
free(title);
cmd_free_argv(argc, argv);
return (CMD_RETURN_WAIT);
out:
cmd_free_argv(argc, argv);
environ_free(env);
free(cwd);
free(title);
return (CMD_RETURN_NORMAL);
fail:
free(cause);
cmd_free_argv(argc, argv);
environ_free(env);
free(cwd);
free(title);
return (CMD_RETURN_ERROR);
}

View File

@@ -131,6 +131,7 @@ cmd_display_message_exec(struct cmd *self, struct cmdq_item *item)
if (args_has(args, 'a')) {
format_each(ft, cmd_display_message_each, item);
format_free(ft);
return (CMD_RETURN_NORMAL);
}
@@ -155,6 +156,5 @@ cmd_display_message_exec(struct cmd *self, struct cmdq_item *item)
free(msg);
format_free(ft);
return (CMD_RETURN_NORMAL);
}

View File

@@ -924,6 +924,7 @@ cmd_find_target(struct cmd_find_state *fs, struct cmdq_item *item,
const char *target, enum cmd_find_type type, int flags)
{
struct mouse_event *m;
struct client *c;
struct cmd_find_state current;
char *colon, *period, *copy = NULL, tmp[256];
const char *session, *window, *pane, *s;
@@ -990,6 +991,20 @@ cmd_find_target(struct cmd_find_state *fs, struct cmdq_item *item,
if (target == NULL || *target == '\0')
goto current;
if (strcmp(target, "@") == 0 ||
strcmp(target, "{active}") == 0 ||
strcmp(target, "{current}") == 0) {
c = cmdq_get_client(item);
if (c == NULL) {
cmdq_error(item, "no current client");
goto error;
}
fs->wl = c->session->curw;
fs->wp = c->session->curw->window->active;
fs->w = c->session->curw->window;
goto found;
}
/* Mouse target is a plain = or {mouse}. */
if (strcmp(target, "=") == 0 || strcmp(target, "{mouse}") == 0) {
m = &cmdq_get_event(item)->m;

View File

@@ -36,8 +36,8 @@ const struct cmd_entry cmd_list_buffers_entry = {
.name = "list-buffers",
.alias = "lsb",
.args = { "F:f:", 0, 0, NULL },
.usage = "[-F format] [-f filter]",
.args = { "F:f:O:r", 0, 0, NULL },
.usage = "[-F format] [-f filter] [-O order]",
.flags = CMD_AFTERHOOK,
.exec = cmd_list_buffers_exec
@@ -46,21 +46,30 @@ const struct cmd_entry cmd_list_buffers_entry = {
static enum cmd_retval
cmd_list_buffers_exec(struct cmd *self, struct cmdq_item *item)
{
struct args *args = cmd_get_args(self);
struct paste_buffer *pb;
struct format_tree *ft;
const char *template, *filter;
char *line, *expanded;
int flag;
struct args *args = cmd_get_args(self);
struct paste_buffer **l;
struct format_tree *ft;
const char *template, *filter;
char *line, *expanded;
int flag;
u_int i, n;
struct sort_criteria sort_crit;
if ((template = args_get(args, 'F')) == NULL)
template = LIST_BUFFERS_TEMPLATE;
filter = args_get(args, 'f');
pb = NULL;
while ((pb = paste_walk(pb)) != NULL) {
sort_crit.order = sort_order_from_string(args_get(args, 'O'));
if (sort_crit.order == SORT_END && args_has(args, 'O')) {
cmdq_error(item, "invalid sort order");
return (CMD_RETURN_ERROR);
}
sort_crit.reversed = args_has(args, 'r');
l = sort_get_buffers(&n, &sort_crit);
for (i = 0; i < n; i++) {
ft = format_create(cmdq_get_client(item), item, FORMAT_NONE, 0);
format_defaults_paste_buffer(ft, pb);
format_defaults_paste_buffer(ft, l[i]);
if (filter != NULL) {
expanded = format_expand(ft, filter);

View File

@@ -41,8 +41,8 @@ const struct cmd_entry cmd_list_clients_entry = {
.name = "list-clients",
.alias = "lsc",
.args = { "F:f:t:", 0, 0, NULL },
.usage = "[-F format] [-f filter] " CMD_TARGET_SESSION_USAGE,
.args = { "F:f:O:rt:", 0, 0, NULL },
.usage = "[-F format] [-f filter] [-O order]" CMD_TARGET_SESSION_USAGE,
.target = { 't', CMD_FIND_SESSION, 0 },
@@ -53,15 +53,16 @@ const struct cmd_entry cmd_list_clients_entry = {
static enum cmd_retval
cmd_list_clients_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 client *c;
struct session *s;
struct format_tree *ft;
const char *template, *filter;
u_int idx;
char *line, *expanded;
int flag;
struct args *args = cmd_get_args(self);
struct cmd_find_state *target = cmdq_get_target(item);
struct client **l;
struct session *s;
struct format_tree *ft;
const char *template, *filter;
u_int i, n;
char *line, *expanded;
int flag;
struct sort_criteria sort_crit;
if (args_has(args, 't'))
s = target->s;
@@ -72,14 +73,21 @@ cmd_list_clients_exec(struct cmd *self, struct cmdq_item *item)
template = LIST_CLIENTS_TEMPLATE;
filter = args_get(args, 'f');
idx = 0;
TAILQ_FOREACH(c, &clients, entry) {
if (c->session == NULL || (s != NULL && s != c->session))
sort_crit.order = sort_order_from_string(args_get(args, 'O'));
if (sort_crit.order == SORT_END && args_has(args, 'O')) {
cmdq_error(item, "invalid sort order");
return (CMD_RETURN_ERROR);
}
sort_crit.reversed = args_has(args, 'r');
l = sort_get_clients(&n, &sort_crit);
for (i = 0; i < n; i++) {
if (l[i]->session == NULL || (s != NULL && s != l[i]->session))
continue;
ft = format_create(cmdq_get_client(item), item, FORMAT_NONE, 0);
format_add(ft, "line", "%u", idx);
format_defaults(ft, c, NULL, NULL, NULL);
format_add(ft, "line", "%u", i);
format_defaults(ft, l[i], NULL, NULL, NULL);
if (filter != NULL) {
expanded = format_expand(ft, filter);
@@ -94,8 +102,6 @@ cmd_list_clients_exec(struct cmd *self, struct cmdq_item *item)
}
format_free(ft);
idx++;
}
return (CMD_RETURN_NORMAL);

107
cmd-list-commands.c Normal file
View File

@@ -0,0 +1,107 @@
/* $OpenBSD$ */
/*
* Copyright (c) 2007 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"
/*
* List all commands.
*/
#define LIST_COMMANDS_TEMPLATE \
"#{command_list_name}" \
"#{?command_list_alias, (#{command_list_alias}),} " \
"#{command_list_usage}"
static enum cmd_retval cmd_list_commands(struct cmd *, struct cmdq_item *);
const struct cmd_entry cmd_list_commands_entry = {
.name = "list-commands",
.alias = "lscm",
.args = { "F:", 0, 1, NULL },
.usage = "[-F format] [command]",
.flags = CMD_STARTSERVER|CMD_AFTERHOOK,
.exec = cmd_list_commands
};
static void
cmd_list_single_command(const struct cmd_entry *entry, struct format_tree *ft,
const char *template, struct cmdq_item *item)
{
const char *s;
char *line;
format_add(ft, "command_list_name", "%s", entry->name);
if (entry->alias != NULL)
s = entry->alias;
else
s = "";
format_add(ft, "command_list_alias", "%s", s);
if (entry->usage != NULL)
s = entry->usage;
else
s = "";
format_add(ft, "command_list_usage", "%s", s);
line = format_expand(ft, template);
if (*line != '\0')
cmdq_print(item, "%s", line);
free(line);
}
static enum cmd_retval
cmd_list_commands(struct cmd *self, struct cmdq_item *item)
{
struct args *args = cmd_get_args(self);
const struct cmd_entry **entryp;
const struct cmd_entry *entry;
struct format_tree *ft;
const char *template, *command;
char *cause;
if ((template = args_get(args, 'F')) == NULL)
template = LIST_COMMANDS_TEMPLATE;
ft = format_create(cmdq_get_client(item), item, FORMAT_NONE, 0);
format_defaults(ft, NULL, NULL, NULL, NULL);
command = args_string(args, 0);
if (command == NULL) {
for (entryp = cmd_table; *entryp != NULL; entryp++)
cmd_list_single_command(*entryp, ft, template, item);
} else {
entry = cmd_find(command, &cause);
if (entry != NULL)
cmd_list_single_command(entry, ft, template, item);
else {
cmdq_error(item, "%s", cause);
free(cause);
format_free(ft);
return (CMD_RETURN_ERROR);
}
}
format_free(ft);
return (CMD_RETURN_NORMAL);
}

View File

@@ -27,121 +27,140 @@
* List key bindings.
*/
static enum cmd_retval cmd_list_keys_exec(struct cmd *, struct cmdq_item *);
#define LIST_KEYS_TEMPLATE \
"#{?notes_only," \
"#{key_prefix} " \
"#{p|#{key_string_width}:key_string} " \
"#{?key_note,#{key_note},#{key_command}}" \
"," \
"bind-key #{?key_has_repeat,#{?key_repeat,-r, },} " \
"-T #{p|#{key_table_width}:key_table} " \
"#{p|#{key_string_width}:key_string} " \
"#{key_command}}"
static enum cmd_retval cmd_list_keys_commands(struct cmd *,
struct cmdq_item *);
static enum cmd_retval cmd_list_keys_exec(struct cmd *, struct cmdq_item *);
const struct cmd_entry cmd_list_keys_entry = {
.name = "list-keys",
.alias = "lsk",
.args = { "1aNP:T:", 0, 1, NULL },
.usage = "[-1aN] [-P prefix-string] [-T key-table] [key]",
.args = { "1aF:NO:P:rT:", 0, 1, NULL },
.usage = "[-1aNr] [-F format] [-O order] [-P prefix-string]"
"[-T key-table] [key]",
.flags = CMD_STARTSERVER|CMD_AFTERHOOK,
.exec = cmd_list_keys_exec
};
const struct cmd_entry cmd_list_commands_entry = {
.name = "list-commands",
.alias = "lscm",
static char *
cmd_list_keys_get_prefix(struct args *args)
{
key_code prefix;
.args = { "F:", 0, 1, NULL },
.usage = "[-F format] [command]",
if (args_has(args, 'P'))
return (xstrdup(args_get(args, 'P')));
.flags = CMD_STARTSERVER|CMD_AFTERHOOK,
.exec = cmd_list_keys_exec
};
prefix = options_get_number(global_s_options, "prefix");
if (prefix == KEYC_NONE)
return (xstrdup(""));
return (xstrdup(key_string_lookup_key(prefix, 0)));
}
static u_int
cmd_list_keys_get_width(const char *tablename, key_code only)
cmd_list_keys_get_width(struct key_binding **l, u_int n)
{
struct key_table *table;
struct key_binding *bd;
u_int width, keywidth = 0;
u_int i, width, keywidth = 0;
table = key_bindings_get_table(tablename, 0);
if (table == NULL)
return (0);
bd = key_bindings_first(table);
while (bd != NULL) {
if ((only != KEYC_UNKNOWN && bd->key != only) ||
KEYC_IS_MOUSE(bd->key) ||
bd->note == NULL ||
*bd->note == '\0') {
bd = key_bindings_next(table, bd);
continue;
}
width = utf8_cstrwidth(key_string_lookup_key(bd->key, 0));
for (i = 0; i < n; i++) {
width = utf8_cstrwidth(key_string_lookup_key(l[i]->key, 0));
if (width > keywidth)
keywidth = width;
bd = key_bindings_next(table, bd);
}
return (keywidth);
}
static int
cmd_list_keys_print_notes(struct cmdq_item *item, struct args *args,
const char *tablename, u_int keywidth, key_code only, const char *prefix)
static u_int
cmd_list_keys_get_table_width(struct key_binding **l, u_int n)
{
struct client *tc = cmdq_get_target_client(item);
struct key_table *table;
struct key_binding *bd;
const char *key;
char *tmp, *note;
int found = 0;
u_int i, width, tablewidth = 0;
table = key_bindings_get_table(tablename, 0);
if (table == NULL)
return (0);
bd = key_bindings_first(table);
while (bd != NULL) {
if ((only != KEYC_UNKNOWN && bd->key != only) ||
KEYC_IS_MOUSE(bd->key) ||
((bd->note == NULL || *bd->note == '\0') &&
!args_has(args, 'a'))) {
bd = key_bindings_next(table, bd);
continue;
}
found = 1;
key = key_string_lookup_key(bd->key, 0);
if (bd->note == NULL || *bd->note == '\0')
note = cmd_list_print(bd->cmdlist, 1);
else
note = xstrdup(bd->note);
tmp = utf8_padcstr(key, keywidth + 1);
if (args_has(args, '1') && tc != NULL) {
status_message_set(tc, -1, 1, 0, 0, "%s%s%s", prefix,
tmp, note);
} else
cmdq_print(item, "%s%s%s", prefix, tmp, note);
free(tmp);
free(note);
if (args_has(args, '1'))
break;
bd = key_bindings_next(table, bd);
for (i = 0; i < n; i++) {
width = utf8_cstrwidth(l[i]->tablename);
if (width > tablewidth)
tablewidth = width;
}
return (found);
return (tablewidth);
}
static char *
cmd_list_keys_get_prefix(struct args *args, key_code *prefix)
static struct key_binding **
cmd_get_root_and_prefix(u_int *n, struct sort_criteria *sort_crit)
{
char *s;
const char *tables[] = { "prefix", "root" };
struct key_table *t;
struct key_binding **lt;
u_int i, ltsz, len = 0, offset = 0;
static struct key_binding **l = NULL;
static u_int lsz = 0;
*prefix = options_get_number(global_s_options, "prefix");
if (!args_has(args, 'P')) {
if (*prefix != KEYC_NONE)
xasprintf(&s, "%s ", key_string_lookup_key(*prefix, 0));
else
s = xstrdup("");
} else
s = xstrdup(args_get(args, 'P'));
return (s);
for (i = 0; i < nitems(tables); i++) {
t = key_bindings_get_table(tables[i], 0);
lt = sort_get_key_bindings_table(t, &ltsz, sort_crit);
len += ltsz;
if (lsz <= len) {
lsz = len + 100;
l = xreallocarray(l, lsz, sizeof *l);
}
memcpy(l + offset, lt, ltsz * sizeof *l);
offset += ltsz;
}
*n = len;
return (l);
}
static void
cmd_filter_key_list(int filter_notes, int filter_key, key_code only,
struct key_binding **l, u_int *n)
{
key_code key;
u_int i, j = 0;
for (i = 0; i < *n; i++) {
key = l[i]->key & (KEYC_MASK_KEY|KEYC_MASK_MODIFIERS);
if (filter_key && only != key)
continue;
if (filter_notes && l[i]->note == NULL)
continue;
l[j++] = l[i];
}
*n = j;
}
static void
cmd_format_add_key_binding(struct format_tree *ft,
const struct key_binding *bd, const char *prefix)
{
const char *s;
if (bd->flags & KEY_BINDING_REPEAT)
format_add(ft, "key_repeat", "1");
else
format_add(ft, "key_repeat", "0");
if (bd->note != NULL)
format_add(ft, "key_note", "%s", bd->note);
else
format_add(ft, "key_note", "%s", "");
format_add(ft, "key_prefix", "%s", prefix);
format_add(ft, "key_table", "%s", bd->tablename);
s = key_string_lookup_key(bd->key, 0);
format_add(ft, "key_string", "%s", s);
s = cmd_list_print(bd->cmdlist,
CMD_LIST_PRINT_ESCAPED|CMD_LIST_PRINT_NO_GROUPS);
format_add(ft, "key_command", "%s", s);
}
static enum cmd_retval
@@ -149,16 +168,16 @@ cmd_list_keys_exec(struct cmd *self, struct cmdq_item *item)
{
struct args *args = cmd_get_args(self);
struct client *tc = cmdq_get_target_client(item);
struct key_table *table;
struct key_binding *bd;
const char *tablename, *r, *keystr;
char *key, *cp, *tmp, *start, *empty;
key_code prefix, only = KEYC_UNKNOWN;
int repeat, width, tablewidth, keywidth, found = 0;
size_t tmpsize, tmpused, cplen;
if (cmd_get_entry(self) == &cmd_list_commands_entry)
return (cmd_list_keys_commands(self, item));
struct format_tree *ft;
struct key_table *table = NULL;
struct key_binding **l;
key_code only = KEYC_UNKNOWN;
const char *template, *tablename, *keystr;
char *line;
char *prefix = NULL;
u_int i, n;
int single, notes_only, filter_notes, filter_key;
struct sort_criteria sort_crit;
if ((keystr = args_string(args, 0)) != NULL) {
only = key_string_lookup_string(keystr);
@@ -169,218 +188,64 @@ cmd_list_keys_exec(struct cmd *self, struct cmdq_item *item)
only &= (KEYC_MASK_KEY|KEYC_MASK_MODIFIERS);
}
tablename = args_get(args, 'T');
if (tablename != NULL && key_bindings_get_table(tablename, 0) == NULL) {
cmdq_error(item, "table %s doesn't exist", tablename);
sort_crit.order = sort_order_from_string(args_get(args, 'O'));
if (sort_crit.order == SORT_END && args_has(args, 'O')) {
cmdq_error(item, "invalid sort order");
return (CMD_RETURN_ERROR);
}
sort_crit.reversed = args_has(args, 'r');
if (args_has(args, 'N')) {
if (tablename == NULL) {
start = cmd_list_keys_get_prefix(args, &prefix);
keywidth = cmd_list_keys_get_width("root", only);
if (prefix != KEYC_NONE) {
width = cmd_list_keys_get_width("prefix", only);
if (width == 0)
prefix = KEYC_NONE;
else if (width > keywidth)
keywidth = width;
}
empty = utf8_padcstr("", utf8_cstrwidth(start));
prefix = cmd_list_keys_get_prefix(args);
single = args_has(args, '1');
notes_only = args_has(args, 'N');
found = cmd_list_keys_print_notes(item, args, "root",
keywidth, only, empty);
if (prefix != KEYC_NONE) {
if (cmd_list_keys_print_notes(item, args,
"prefix", keywidth, only, start))
found = 1;
}
free(empty);
} else {
if (args_has(args, 'P'))
start = xstrdup(args_get(args, 'P'));
else
start = xstrdup("");
keywidth = cmd_list_keys_get_width(tablename, only);
found = cmd_list_keys_print_notes(item, args, tablename,
keywidth, only, start);
}
free(start);
goto out;
}
repeat = 0;
tablewidth = keywidth = 0;
table = key_bindings_first_table();
while (table != NULL) {
if (tablename != NULL && strcmp(table->name, tablename) != 0) {
table = key_bindings_next_table(table);
continue;
}
bd = key_bindings_first(table);
while (bd != NULL) {
if (only != KEYC_UNKNOWN && bd->key != only) {
bd = key_bindings_next(table, bd);
continue;
}
key = args_escape(key_string_lookup_key(bd->key, 0));
if (bd->flags & KEY_BINDING_REPEAT)
repeat = 1;
width = utf8_cstrwidth(table->name);
if (width > tablewidth)
tablewidth = width;
width = utf8_cstrwidth(key);
if (width > keywidth)
keywidth = width;
free(key);
bd = key_bindings_next(table, bd);
}
table = key_bindings_next_table(table);
}
tmpsize = 256;
tmp = xmalloc(tmpsize);
table = key_bindings_first_table();
while (table != NULL) {
if (tablename != NULL && strcmp(table->name, tablename) != 0) {
table = key_bindings_next_table(table);
continue;
}
bd = key_bindings_first(table);
while (bd != NULL) {
if (only != KEYC_UNKNOWN && bd->key != only) {
bd = key_bindings_next(table, bd);
continue;
}
found = 1;
key = args_escape(key_string_lookup_key(bd->key, 0));
if (!repeat)
r = "";
else if (bd->flags & KEY_BINDING_REPEAT)
r = "-r ";
else
r = " ";
tmpused = xsnprintf(tmp, tmpsize, "%s-T ", r);
cp = utf8_padcstr(table->name, tablewidth);
cplen = strlen(cp) + 1;
while (tmpused + cplen + 1 >= tmpsize) {
tmpsize *= 2;
tmp = xrealloc(tmp, tmpsize);
}
strlcat(tmp, cp, tmpsize);
tmpused = strlcat(tmp, " ", tmpsize);
free(cp);
cp = utf8_padcstr(key, keywidth);
cplen = strlen(cp) + 1;
while (tmpused + cplen + 1 >= tmpsize) {
tmpsize *= 2;
tmp = xrealloc(tmp, tmpsize);
}
strlcat(tmp, cp, tmpsize);
tmpused = strlcat(tmp, " ", tmpsize);
free(cp);
cp = cmd_list_print(bd->cmdlist, 1);
cplen = strlen(cp);
while (tmpused + cplen + 1 >= tmpsize) {
tmpsize *= 2;
tmp = xrealloc(tmp, tmpsize);
}
strlcat(tmp, cp, tmpsize);
free(cp);
if (args_has(args, '1') && tc != NULL) {
status_message_set(tc, -1, 1, 0, 0,
"bind-key %s", tmp);
} else
cmdq_print(item, "bind-key %s", tmp);
free(key);
if (args_has(args, '1'))
break;
bd = key_bindings_next(table, bd);
}
table = key_bindings_next_table(table);
}
free(tmp);
out:
if (only != KEYC_UNKNOWN && !found) {
cmdq_error(item, "unknown key: %s", args_string(args, 0));
return (CMD_RETURN_ERROR);
}
return (CMD_RETURN_NORMAL);
}
static void
cmd_list_single_command(const struct cmd_entry *entry, struct format_tree *ft,
const char *template, struct cmdq_item *item)
{
const char *s;
char *line;
format_add(ft, "command_list_name", "%s", entry->name);
if (entry->alias != NULL)
s = entry->alias;
else
s = "";
format_add(ft, "command_list_alias", "%s", s);
if (entry->usage != NULL)
s = entry->usage;
else
s = "";
format_add(ft, "command_list_usage", "%s", s);
line = format_expand(ft, template);
if (*line != '\0')
cmdq_print(item, "%s", line);
free(line);
}
static enum cmd_retval
cmd_list_keys_commands(struct cmd *self, struct cmdq_item *item)
{
struct args *args = cmd_get_args(self);
const struct cmd_entry **entryp;
const struct cmd_entry *entry;
struct format_tree *ft;
const char *template, *command;
char *cause;
if ((template = args_get(args, 'F')) == NULL) {
template = "#{command_list_name}"
"#{?command_list_alias, (#{command_list_alias}),} "
"#{command_list_usage}";
}
ft = format_create(cmdq_get_client(item), item, FORMAT_NONE, 0);
format_defaults(ft, NULL, NULL, NULL, NULL);
command = args_string(args, 0);
if (command == NULL) {
for (entryp = cmd_table; *entryp != NULL; entryp++)
cmd_list_single_command(*entryp, ft, template, item);
} else {
entry = cmd_find(command, &cause);
if (entry != NULL)
cmd_list_single_command(entry, ft, template, item);
else {
cmdq_error(item, "%s", cause);
free(cause);
format_free(ft);
if ((tablename = args_get(args, 'T')) != NULL) {
table = key_bindings_get_table(tablename, 0);
if (table == NULL) {
cmdq_error(item, "table %s doesn't exist", tablename);
return (CMD_RETURN_ERROR);
}
}
if ((template = args_get(args, 'F')) == NULL)
template = LIST_KEYS_TEMPLATE;
if (table)
l = sort_get_key_bindings_table(table, &n, &sort_crit);
else if (notes_only)
l = cmd_get_root_and_prefix(&n, &sort_crit);
else
l = sort_get_key_bindings(&n, &sort_crit);
filter_notes = notes_only && !args_has(args, 'a');
filter_key = only != KEYC_UNKNOWN;
if (filter_notes || filter_key)
cmd_filter_key_list(filter_notes, filter_key, only, l, &n);
if (single)
n = 1;
ft = format_create(cmdq_get_client(item), item, FORMAT_NONE, 0);
format_defaults(ft, NULL, NULL, NULL, NULL);
format_add(ft, "notes_only", "%d", notes_only);
format_add(ft, "key_has_repeat", "%d", key_bindings_has_repeat(l, n));
format_add(ft, "key_string_width", "%u", cmd_list_keys_get_width(l, n));
format_add(ft, "key_table_width", "%u",
cmd_list_keys_get_table_width(l, n));
for (i = 0; i < n; i++) {
cmd_format_add_key_binding(ft, l[i], prefix);
line = format_expand(ft, template);
if ((single && tc != NULL) || n == 1)
status_message_set(tc, -1, 1, 0, 0, "%s", line);
else if (*line != '\0')
cmdq_print(item, "%s", line);
free(line);
if (single)
break;
}
format_free(ft);
free(prefix);
return (CMD_RETURN_NORMAL);
}

View File

@@ -38,8 +38,9 @@ const struct cmd_entry cmd_list_panes_entry = {
.name = "list-panes",
.alias = "lsp",
.args = { "asF:f:t:", 0, 0, NULL },
.usage = "[-as] [-F format] [-f filter] " CMD_TARGET_WINDOW_USAGE,
.args = { "aF:f:O:rst:", 0, 0, NULL },
.usage = "[-asr] [-F format] [-f filter] [-O order]"
CMD_TARGET_WINDOW_USAGE,
.target = { 't', CMD_FIND_WINDOW, 0 },
@@ -54,6 +55,13 @@ cmd_list_panes_exec(struct cmd *self, struct cmdq_item *item)
struct cmd_find_state *target = cmdq_get_target(item);
struct session *s = target->s;
struct winlink *wl = target->wl;
enum sort_order order;
order = sort_order_from_string(args_get(args, 'O'));
if (order == SORT_END && args_has(args, 'O')) {
cmdq_error(item, "invalid sort order");
return (CMD_RETURN_ERROR);
}
if (args_has(args, 'a'))
cmd_list_panes_server(self, item);
@@ -89,12 +97,13 @@ cmd_list_panes_window(struct cmd *self, struct session *s, struct winlink *wl,
struct cmdq_item *item, int type)
{
struct args *args = cmd_get_args(self);
struct window_pane *wp;
u_int n;
struct window_pane *wp, **l;
u_int i, n;
struct format_tree *ft;
const char *template, *filter;
char *line, *expanded;
int flag;
struct sort_criteria sort_crit;
template = args_get(args, 'F');
if (template == NULL) {
@@ -124,8 +133,12 @@ cmd_list_panes_window(struct cmd *self, struct session *s, struct winlink *wl,
}
filter = args_get(args, 'f');
n = 0;
TAILQ_FOREACH(wp, &wl->window->panes, entry) {
sort_crit.order = sort_order_from_string(args_get(args, 'O'));
sort_crit.reversed = args_has(args, 'r');
l = sort_get_panes_window(wl->window, &n, &sort_crit);
for (i = 0; i < n; i++) {
wp = l[i];
ft = format_create(cmdq_get_client(item), item, FORMAT_NONE, 0);
format_add(ft, "line", "%u", n);
format_defaults(ft, NULL, s, wl, wp);
@@ -143,6 +156,5 @@ cmd_list_panes_window(struct cmd *self, struct session *s, struct winlink *wl,
}
format_free(ft);
n++;
}
}

View File

@@ -42,8 +42,8 @@ const struct cmd_entry cmd_list_sessions_entry = {
.name = "list-sessions",
.alias = "ls",
.args = { "F:f:", 0, 0, NULL },
.usage = "[-F format] [-f filter]",
.args = { "F:f:O:r", 0, 0, NULL },
.usage = "[-r] [-F format] [-f filter] [-O order]",
.flags = CMD_AFTERHOOK,
.exec = cmd_list_sessions_exec
@@ -52,23 +52,31 @@ const struct cmd_entry cmd_list_sessions_entry = {
static enum cmd_retval
cmd_list_sessions_exec(struct cmd *self, struct cmdq_item *item)
{
struct args *args = cmd_get_args(self);
struct session *s;
u_int n;
struct format_tree *ft;
const char *template, *filter;
char *line, *expanded;
int flag;
struct args *args = cmd_get_args(self);
struct session **l;
u_int n, i;
struct format_tree *ft;
const char *template, *filter;
char *line, *expanded;
int flag;
struct sort_criteria sort_crit;
if ((template = args_get(args, 'F')) == NULL)
template = LIST_SESSIONS_TEMPLATE;
filter = args_get(args, 'f');
n = 0;
RB_FOREACH(s, sessions, &sessions) {
sort_crit.order = sort_order_from_string(args_get(args, 'O'));
if (sort_crit.order == SORT_END && args_has(args, 'O')) {
cmdq_error(item, "invalid sort order");
return (CMD_RETURN_ERROR);
}
sort_crit.reversed = args_has(args, 'r');
l = sort_get_sessions(&n, &sort_crit);
for (i = 0; i < n; i++) {
ft = format_create(cmdq_get_client(item), item, FORMAT_NONE, 0);
format_add(ft, "line", "%u", n);
format_defaults(ft, NULL, s, NULL, NULL);
format_add(ft, "line", "%u", i);
format_defaults(ft, NULL, l[i], NULL, NULL);
if (filter != NULL) {
expanded = format_expand(ft, filter);
@@ -83,7 +91,6 @@ cmd_list_sessions_exec(struct cmd *self, struct cmdq_item *item)
}
format_free(ft);
n++;
}
return (CMD_RETURN_NORMAL);

View File

@@ -41,16 +41,13 @@
static enum cmd_retval cmd_list_windows_exec(struct cmd *, struct cmdq_item *);
static void cmd_list_windows_server(struct cmd *, struct cmdq_item *);
static void cmd_list_windows_session(struct cmd *, struct session *,
struct cmdq_item *, int);
const struct cmd_entry cmd_list_windows_entry = {
.name = "list-windows",
.alias = "lsw",
.args = { "F:f:at:", 0, 0, NULL },
.usage = "[-a] [-F format] [-f filter] " CMD_TARGET_SESSION_USAGE,
.args = { "aF:f:O:rt:", 0, 0, NULL },
.usage = "[-ar] [-F format] [-f filter] [-O order]"
CMD_TARGET_SESSION_USAGE,
.target = { 't', CMD_FIND_SESSION, 0 },
@@ -63,51 +60,38 @@ cmd_list_windows_exec(struct cmd *self, struct cmdq_item *item)
{
struct args *args = cmd_get_args(self);
struct cmd_find_state *target = cmdq_get_target(item);
if (args_has(args, 'a'))
cmd_list_windows_server(self, item);
else
cmd_list_windows_session(self, target->s, item, 0);
return (CMD_RETURN_NORMAL);
}
static void
cmd_list_windows_server(struct cmd *self, struct cmdq_item *item)
{
struct session *s;
RB_FOREACH(s, sessions, &sessions)
cmd_list_windows_session(self, s, item, 1);
}
static void
cmd_list_windows_session(struct cmd *self, struct session *s,
struct cmdq_item *item, int type)
{
struct args *args = cmd_get_args(self);
struct winlink *wl;
u_int n;
struct winlink *wl, **l;
struct session *s;
u_int i, n;
struct format_tree *ft;
const char *template, *filter;
char *line, *expanded;
int flag;
struct sort_criteria sort_crit;
template = args_get(args, 'F');
if (template == NULL) {
switch (type) {
case 0:
template = LIST_WINDOWS_TEMPLATE;
break;
case 1:
template = LIST_WINDOWS_WITH_SESSION_TEMPLATE;
break;
}
}
filter = args_get(args, 'f');
n = 0;
RB_FOREACH(wl, winlinks, &s->windows) {
sort_crit.order = sort_order_from_string(args_get(args, 'O'));
if (sort_crit.order == SORT_END && args_has(args, 'O')) {
cmdq_error(item, "invalid sort order");
return (CMD_RETURN_ERROR);
}
sort_crit.reversed = args_has(args, 'r');
if (args_has(args, 'a')) {
l = sort_get_winlinks(&n, &sort_crit);
if (template == NULL)
template = LIST_WINDOWS_WITH_SESSION_TEMPLATE;
} else {
l = sort_get_winlinks_session(target->s, &n, &sort_crit);
if (template == NULL)
template = LIST_WINDOWS_TEMPLATE;
}
for (i = 0; i < n; i++) {
wl = l[i];
s = wl->session;
ft = format_create(cmdq_get_client(item), item, FORMAT_NONE, 0);
format_add(ft, "line", "%u", n);
format_defaults(ft, NULL, s, wl, NULL);
@@ -125,6 +109,7 @@ cmd_list_windows_session(struct cmd *self, struct session *s,
}
format_free(ft);
n++;
}
return (CMD_RETURN_NORMAL);
}

View File

@@ -117,8 +117,9 @@ cmd_new_session_exec(struct cmd *self, struct cmdq_item *item)
as = target->s;
if (as != NULL) {
retval = cmd_attach_session(item, as->name,
args_has(args, 'D'), args_has(args, 'X'), 0, NULL,
args_has(args, 'E'), args_get(args, 'f'));
args_has(args, 'D'), args_has(args, 'X'), 0,
args_get(args, 'c'), args_has(args, 'E'),
args_get(args, 'f'));
free(newname);
return (retval);
}

View File

@@ -758,7 +758,7 @@ static int
cmd_parse_expand_alias(struct cmd_parse_command *cmd,
struct cmd_parse_input *pi, struct cmd_parse_result *pr)
{
struct cmd_parse_argument *arg, *arg1, *first;
struct cmd_parse_argument *first;
struct cmd_parse_commands *cmds;
struct cmd_parse_command *last;
char *alias, *name, *cause;
@@ -798,10 +798,7 @@ cmd_parse_expand_alias(struct cmd_parse_command *cmd,
TAILQ_REMOVE(&cmd->arguments, first, entry);
cmd_parse_free_argument(first);
TAILQ_FOREACH_SAFE(arg, &cmd->arguments, entry, arg1) {
TAILQ_REMOVE(&cmd->arguments, arg, entry);
TAILQ_INSERT_TAIL(&last->arguments, arg, entry);
}
TAILQ_CONCAT(&last->arguments, &cmd->arguments, entry);
cmd_parse_log_commands(cmds, __func__);
pi->flags |= CMD_PARSE_NOALIAS;

View File

@@ -33,8 +33,8 @@ const struct cmd_entry cmd_paste_buffer_entry = {
.name = "paste-buffer",
.alias = "pasteb",
.args = { "db:prs:t:", 0, 0, NULL },
.usage = "[-dpr] [-s separator] " CMD_BUFFER_USAGE " "
.args = { "db:prSs:t:", 0, 0, NULL },
.usage = "[-dprS] [-s separator] " CMD_BUFFER_USAGE " "
CMD_TARGET_PANE_USAGE,
.target = { 't', CMD_FIND_PANE, 0 },
@@ -43,6 +43,17 @@ const struct cmd_entry cmd_paste_buffer_entry = {
.exec = cmd_paste_buffer_exec
};
static void
cmd_paste_buffer_paste(struct window_pane *wp, const char *buf, size_t len)
{
char *cp;
size_t n;
n = utf8_stravisx(&cp, buf, len, VIS_SAFE|VIS_NOSLASH);
bufferevent_write(wp->event, cp, n);
free(cp);
}
static enum cmd_retval
cmd_paste_buffer_exec(struct cmd *self, struct cmdq_item *item)
{
@@ -51,7 +62,7 @@ cmd_paste_buffer_exec(struct cmd *self, struct cmdq_item *item)
struct window_pane *wp = target->wp;
struct paste_buffer *pb;
const char *sepstr, *bufname, *bufdata, *bufend, *line;
size_t seplen, bufsize;
size_t seplen, bufsize, len;
int bracket = args_has(args, 'p');
if (window_pane_exited(wp)) {
@@ -93,14 +104,22 @@ cmd_paste_buffer_exec(struct cmd *self, struct cmdq_item *item)
line = memchr(bufdata, '\n', bufend - bufdata);
if (line == NULL)
break;
bufferevent_write(wp->event, bufdata, line - bufdata);
len = line - bufdata;
if (args_has(args, 'S'))
bufferevent_write(wp->event, bufdata, len);
else
cmd_paste_buffer_paste(wp, bufdata, len);
bufferevent_write(wp->event, sepstr, seplen);
bufdata = line + 1;
}
if (bufdata != bufend)
bufferevent_write(wp->event, bufdata, bufend - bufdata);
if (bufdata != bufend) {
len = bufend - bufdata;
if (args_has(args, 'S'))
bufferevent_write(wp->event, bufdata, len);
else
cmd_paste_buffer_paste(wp, bufdata, len);
}
if (bracket && (wp->screen->mode & MODE_BRACKETPASTE))
bufferevent_write(wp->event, "\033[201~", 6);

View File

@@ -123,7 +123,7 @@ cmd_pipe_pane_exec(struct cmd *self, struct cmdq_item *item)
/* Fork the child. */
sigfillset(&set);
sigprocmask(SIG_BLOCK, &set, &oldset);
switch (fork()) {
switch ((wp->pipe_pid = fork())) {
case -1:
sigprocmask(SIG_SETMASK, &oldset, NULL);
cmdq_error(item, "fork error: %s", strerror(errno));
@@ -136,6 +136,9 @@ cmd_pipe_pane_exec(struct cmd *self, struct cmdq_item *item)
sigprocmask(SIG_SETMASK, &oldset, NULL);
close(pipe_fd[0]);
if (setpgid(0, 0) == -1)
_exit(1);
null_fd = open(_PATH_DEVNULL, O_WRONLY);
if (out) {
if (dup2(pipe_fd[1], STDIN_FILENO) == -1)

View File

@@ -34,7 +34,7 @@ const struct cmd_entry cmd_refresh_client_entry = {
.name = "refresh-client",
.alias = "refresh",
.args = { "A:B:cC:Df:r:F:l::LRSt:U", 0, 1, NULL },
.args = { "A:B:cC:Df:r:F:lLRSt:U", 0, 1, NULL },
.usage = "[-cDlLRSU] [-A pane:state] [-B name:what:format] "
"[-C XxY] [-f flags] [-r pane:report] " CMD_TARGET_CLIENT_USAGE
" [adjustment]",
@@ -163,37 +163,6 @@ out:
free(copy);
}
static enum cmd_retval
cmd_refresh_client_clipboard(struct cmd *self, struct cmdq_item *item)
{
struct args *args = cmd_get_args(self);
struct client *tc = cmdq_get_target_client(item);
const char *p;
u_int i;
struct cmd_find_state fs;
p = args_get(args, 'l');
if (p == NULL) {
if (tc->flags & CLIENT_CLIPBOARDBUFFER)
return (CMD_RETURN_NORMAL);
tc->flags |= CLIENT_CLIPBOARDBUFFER;
} else {
if (cmd_find_target(&fs, item, p, CMD_FIND_PANE, 0) != 0)
return (CMD_RETURN_ERROR);
for (i = 0; i < tc->clipboard_npanes; i++) {
if (tc->clipboard_panes[i] == fs.wp->id)
break;
}
if (i != tc->clipboard_npanes)
return (CMD_RETURN_NORMAL);
tc->clipboard_panes = xreallocarray(tc->clipboard_panes,
tc->clipboard_npanes + 1, sizeof *tc->clipboard_panes);
tc->clipboard_panes[tc->clipboard_npanes++] = fs.wp->id;
}
tty_clipboard_query(&tc->tty);
return (CMD_RETURN_NORMAL);
}
static void
cmd_refresh_report(struct tty *tty, const char *value)
{
@@ -284,8 +253,10 @@ cmd_refresh_client_exec(struct cmd *self, struct cmdq_item *item)
return (CMD_RETURN_NORMAL);
}
if (args_has(args, 'l'))
return (cmd_refresh_client_clipboard(self, item));
if (args_has(args, 'l')) {
tty_clipboard_query(&tc->tty);
return (CMD_RETURN_NORMAL);
}
if (args_has(args, 'F')) /* -F is an alias for -f */
server_client_set_flags(tc, args_get(args, 'F'));

View File

@@ -39,7 +39,8 @@ const struct cmd_entry cmd_send_keys_entry = {
.target = { 't', CMD_FIND_PANE, 0 },
.flags = CMD_AFTERHOOK|CMD_CLIENT_CFLAG|CMD_CLIENT_CANFAIL,
.flags = CMD_AFTERHOOK|CMD_CLIENT_CFLAG|CMD_CLIENT_CANFAIL|
CMD_READONLY,
.exec = cmd_send_keys_exec
};
@@ -167,6 +168,11 @@ cmd_send_keys_exec(struct cmd *self, struct cmdq_item *item)
u_int count = args_count(args);
char *cause = NULL;
if (tc != NULL && tc->flags & CLIENT_READONLY && !args_has(args, 'X')) {
cmdq_error(item, "client is read-only");
return (CMD_RETURN_ERROR);
}
if (args_has(args, 'N')) {
np = args_strtonum_and_expand(args, 'N', 1, UINT_MAX, item,
&cause);

View File

@@ -90,6 +90,7 @@ cmd_server_access_exec(struct cmd *self, struct cmdq_item *item)
pw = getpwnam(name);
if (pw == NULL) {
cmdq_error(item, "unknown user: %s", name);
free(name);
return (CMD_RETURN_ERROR);
}
free(name);

View File

@@ -57,30 +57,30 @@ cmd_set_buffer_exec(struct cmd *self, struct cmdq_item *item)
{
struct args *args = cmd_get_args(self);
struct client *tc = cmdq_get_target_client(item);
struct paste_buffer *pb;
char *bufdata, *cause;
const char *bufname, *olddata;
size_t bufsize, newsize;
struct paste_buffer *pb = NULL;
char *bufname = NULL, *bufdata = NULL, *cause = NULL;
const char *olddata;
size_t bufsize = 0, newsize;
bufname = args_get(args, 'b');
if (bufname == NULL)
pb = NULL;
else
if (args_get(args, 'b') != NULL) {
bufname = xstrdup(args_get(args, 'b'));
pb = paste_get_name(bufname);
}
if (cmd_get_entry(self) == &cmd_delete_buffer_entry) {
if (pb == NULL) {
if (bufname != NULL) {
cmdq_error(item, "unknown buffer: %s", bufname);
return (CMD_RETURN_ERROR);
goto fail;
}
pb = paste_get_top(&bufname);
}
if (pb == NULL) {
cmdq_error(item, "no buffer");
return (CMD_RETURN_ERROR);
goto fail;
}
paste_free(pb);
free(bufname);
return (CMD_RETURN_NORMAL);
}
@@ -88,32 +88,28 @@ cmd_set_buffer_exec(struct cmd *self, struct cmdq_item *item)
if (pb == NULL) {
if (bufname != NULL) {
cmdq_error(item, "unknown buffer: %s", bufname);
return (CMD_RETURN_ERROR);
goto fail;
}
pb = paste_get_top(&bufname);
}
if (pb == NULL) {
cmdq_error(item, "no buffer");
return (CMD_RETURN_ERROR);
goto fail;
}
if (paste_rename(bufname, args_get(args, 'n'), &cause) != 0) {
cmdq_error(item, "%s", cause);
free(cause);
return (CMD_RETURN_ERROR);
goto fail;
}
return (CMD_RETURN_NORMAL);
}
if (args_count(args) != 1) {
cmdq_error(item, "no data specified");
return (CMD_RETURN_ERROR);
goto fail;
}
if ((newsize = strlen(args_string(args, 0))) == 0)
return (CMD_RETURN_NORMAL);
bufsize = 0;
bufdata = NULL;
if (args_has(args, 'a') && pb != NULL) {
olddata = paste_buffer_data(pb, &bufsize);
bufdata = xmalloc(bufsize);
@@ -126,12 +122,16 @@ cmd_set_buffer_exec(struct cmd *self, struct cmdq_item *item)
if (paste_set(bufdata, bufsize, bufname, &cause) != 0) {
cmdq_error(item, "%s", cause);
free(bufdata);
free(cause);
return (CMD_RETURN_ERROR);
goto fail;
}
if (args_has(args, 'w') && tc != NULL)
tty_set_selection(&tc->tty, "", bufdata, bufsize);
return (CMD_RETURN_NORMAL);
fail:
free(bufdata);
free(bufname);
free(cause);
return (CMD_RETURN_ERROR);
}

View File

@@ -60,6 +60,9 @@ cmd_show_prompt_history_exec(struct cmd *self, struct cmdq_item *item)
if (cmd_get_entry(self) == &cmd_clear_prompt_history_entry) {
if (typestr == NULL) {
for (tidx = 0; tidx < PROMPT_NTYPES; tidx++) {
for (hidx = 0; hidx < status_prompt_hsize[tidx];
hidx++)
free(status_prompt_hlist[tidx][hidx]);
free(status_prompt_hlist[tidx]);
status_prompt_hlist[tidx] = NULL;
status_prompt_hsize[tidx] = 0;
@@ -70,6 +73,8 @@ cmd_show_prompt_history_exec(struct cmd *self, struct cmdq_item *item)
cmdq_error(item, "invalid type: %s", typestr);
return (CMD_RETURN_ERROR);
}
for (hidx = 0; hidx < status_prompt_hsize[type]; hidx++)
free(status_prompt_hlist[type][hidx]);
free(status_prompt_hlist[type]);
status_prompt_hlist[type] = NULL;
status_prompt_hsize[type] = 0;

View File

@@ -34,9 +34,9 @@ const struct cmd_entry cmd_switch_client_entry = {
.name = "switch-client",
.alias = "switchc",
.args = { "lc:EFnpt:rT:Z", 0, 0, NULL },
.args = { "c:EFlnO:pt:rT:Z", 0, 0, NULL },
.usage = "[-ElnprZ] [-c target-client] [-t target-session] "
"[-T key-table]",
"[-T key-table] [-O order]",
/* -t is special */
@@ -60,6 +60,7 @@ cmd_switch_client_exec(struct cmd *self, struct cmdq_item *item)
struct window_pane *wp;
const char *tablename;
struct key_table *table;
struct sort_criteria sort_crit;
if (tflag != NULL &&
(tflag[strcspn(tflag, ":.%")] != '\0' || strcmp(tflag, "=") == 0)) {
@@ -95,13 +96,22 @@ cmd_switch_client_exec(struct cmd *self, struct cmdq_item *item)
return (CMD_RETURN_NORMAL);
}
sort_crit.order = sort_order_from_string(args_get(args, 'O'));
if (sort_crit.order == SORT_END && args_has(args, 'O')) {
cmdq_error(item, "invalid sort order");
return (CMD_RETURN_ERROR);
}
sort_crit.reversed = args_has(args, 'r');
if (args_has(args, 'n')) {
if ((s = session_next_session(tc->session)) == NULL) {
s = session_next_session(tc->session, &sort_crit);
if (s == NULL) {
cmdq_error(item, "can't find next session");
return (CMD_RETURN_ERROR);
}
} else if (args_has(args, 'p')) {
if ((s = session_previous_session(tc->session)) == NULL) {
s = session_previous_session(tc->session, &sort_crit);
if (s == NULL) {
cmdq_error(item, "can't find previous session");
return (CMD_RETURN_ERROR);
}

23
cmd.c
View File

@@ -676,11 +676,16 @@ cmd_list_copy(const struct cmd_list *cmdlist, int argc, char **argv)
/* Get a command list as a string. */
char *
cmd_list_print(const struct cmd_list *cmdlist, int escaped)
cmd_list_print(const struct cmd_list *cmdlist, int flags)
{
struct cmd *cmd, *next;
char *buf, *this;
size_t len;
const char *separator;
int escaped = flags & CMD_LIST_PRINT_ESCAPED;
int no_groups = flags & CMD_LIST_PRINT_NO_GROUPS;
const char *single_separator = escaped ? " \\; " : " ; ";
const char *double_separator = escaped ? " \\;\\; " : " ;; ";
len = 1;
buf = xcalloc(1, len);
@@ -695,17 +700,11 @@ cmd_list_print(const struct cmd_list *cmdlist, int escaped)
next = TAILQ_NEXT(cmd, qentry);
if (next != NULL) {
if (cmd->group != next->group) {
if (escaped)
strlcat(buf, " \\;\\; ", len);
else
strlcat(buf, " ;; ", len);
} else {
if (escaped)
strlcat(buf, " \\; ", len);
else
strlcat(buf, " ; ", len);
}
if (!no_groups && cmd->group != next->group)
separator = double_separator;
else
separator = single_separator;
strlcat(buf, separator, len);
}
free(this);

View File

@@ -1082,22 +1082,22 @@ colour_palette_free(struct colour_palette *p)
/* Get a colour from a palette. */
int
colour_palette_get(struct colour_palette *p, int c)
colour_palette_get(struct colour_palette *p, int n)
{
if (p == NULL)
return (-1);
if (c >= 90 && c <= 97)
c = 8 + c - 90;
else if (c & COLOUR_FLAG_256)
c &= ~COLOUR_FLAG_256;
else if (c >= 8)
if (n >= 90 && n <= 97)
n = 8 + n - 90;
else if (n & COLOUR_FLAG_256)
n &= ~COLOUR_FLAG_256;
else if (n >= 8)
return (-1);
if (p->palette != NULL && p->palette[c] != -1)
return (p->palette[c]);
if (p->default_palette != NULL && p->default_palette[c] != -1)
return (p->default_palette[c]);
if (p->palette != NULL && p->palette[n] != -1)
return (p->palette[n]);
if (p->default_palette != NULL && p->default_palette[n] != -1)
return (p->default_palette[n]);
return (-1);
}
@@ -1107,15 +1107,14 @@ colour_palette_set(struct colour_palette *p, int n, int c)
{
u_int i;
if (p == NULL || n > 255)
if (p == NULL || n < 0 || n > 255)
return (0);
if (c == -1 && p->palette == NULL)
return (0);
if (c != -1 && p->palette == NULL) {
if (p->palette == NULL)
p->palette = xcalloc(256, sizeof *p->palette);
if (p->palette == NULL) {
p->palette = xcalloc(256, sizeof *p->palette);
for (i = 0; i < 256; i++)
p->palette[i] = -1;
}

View File

@@ -388,7 +388,7 @@ int clock_gettime(int, struct timespec *);
/* base64.c */
#undef b64_ntop
#undef b64_pton
int b64_ntop(const char *, size_t, char *, size_t);
int b64_ntop(const u_char *, size_t, char *, size_t);
int b64_pton(const char *, u_char *, size_t);
#endif

View File

@@ -1,4 +1,4 @@
/* $OpenBSD: base64.c,v 1.8 2015/01/16 16:48:51 deraadt Exp $ */
/* $OpenBSD: base64.c,v 1.15 2021/10/25 14:41:09 jca Exp $ */
/*
* Copyright (c) 1996 by Internet Software Consortium.
@@ -46,15 +46,15 @@
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <arpa/nameser.h>
#include <ctype.h>
#include <resolv.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "compat.h"
static const char Base64[] =
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
static const char Pad64 = '=';
@@ -107,9 +107,9 @@ static const char Pad64 = '=';
end of the data is performed using the '=' character.
Since all base64 input is an integral number of octets, only the
-------------------------------------------------
-------------------------------------------------
following cases can arise:
(1) the final quantum of encoding input is an integral
multiple of 24 bits; here, the final unit of encoded
output will be an integral multiple of 4 characters
@@ -123,15 +123,12 @@ static const char Pad64 = '=';
*/
int
b64_ntop(src, srclength, target, targsize)
u_char const *src;
size_t srclength;
char *target;
size_t targsize;
b64_ntop(unsigned char const *src, size_t srclength, char *target,
size_t targsize)
{
size_t datalength = 0;
u_char input[3];
u_char output[4];
unsigned char input[3];
unsigned char output[4];
int i;
while (2 < srclength) {
@@ -152,14 +149,14 @@ b64_ntop(src, srclength, target, targsize)
target[datalength++] = Base64[output[2]];
target[datalength++] = Base64[output[3]];
}
/* Now we worry about padding. */
if (0 != srclength) {
/* Get what's left. */
input[0] = input[1] = input[2] = '\0';
for (i = 0; i < srclength; i++)
input[i] = *src++;
output[0] = input[0] >> 2;
output[1] = ((input[0] & 0x03) << 4) + (input[1] >> 4);
output[2] = ((input[1] & 0x0f) << 2) + (input[2] >> 6);
@@ -187,13 +184,10 @@ b64_ntop(src, srclength, target, targsize)
*/
int
b64_pton(src, target, targsize)
char const *src;
u_char *target;
size_t targsize;
b64_pton(char const *src, unsigned char *target, size_t targsize)
{
int tarindex, state, ch;
u_char nextbyte;
unsigned char nextbyte;
char *pos;
state = 0;
@@ -207,7 +201,7 @@ b64_pton(src, target, targsize)
break;
pos = strchr(Base64, ch);
if (pos == 0) /* A non-base64 character. */
if (pos == 0) /* A non-base64 character. */
return (-1);
switch (state) {

View File

@@ -1,4 +1,4 @@
/* $OpenBSD: imsg-buffer.c,v 1.35 2025/06/04 09:06:56 claudio Exp $ */
/* $OpenBSD: imsg-buffer.c,v 1.36 2025/08/25 08:29:49 claudio Exp $ */
/*
* Copyright (c) 2023 Claudio Jeker <claudio@openbsd.org>
@@ -699,8 +699,6 @@ msgbuf_queuelen(struct msgbuf *msgbuf)
void
msgbuf_clear(struct msgbuf *msgbuf)
{
struct ibuf *buf;
/* write side */
ibufq_flush(&msgbuf->bufs);

View File

@@ -1,6 +1,6 @@
# configure.ac
AC_INIT([tmux], 3.6)
AC_INIT([tmux], next-3.7)
AC_PREREQ([2.60])
AC_CONFIG_AUX_DIR(etc)
@@ -64,6 +64,15 @@ AC_ARG_ENABLE(
)
AM_CONDITIONAL(IS_DEBUG, test "x$enable_debug" = xyes)
# Is this --enable-optimizations?
AC_ARG_ENABLE(
optimizations,
AS_HELP_STRING(--enable-optimizations, enable optimization build flags),
,
enable_optimizations=yes
)
AM_CONDITIONAL(IS_OPTIMIZED, test "x$enable_optimizations" = xyes)
# Is this a static build?
AC_ARG_ENABLE(
static,
@@ -454,13 +463,32 @@ fi
# Enable sixel support.
AC_ARG_ENABLE(
sixel,
AS_HELP_STRING(--enable-sixel, enable sixel images)
sixel-images,
AS_HELP_STRING(--enable-sixel-images, enable sixel images)
)
if test "x$enable_sixel" = xyes; then
AC_DEFINE(ENABLE_SIXEL)
AC_ARG_ENABLE(
sixel,
AS_HELP_STRING(--enable-sixel support),
[
enable_sixel_images="$enableval"
]
)
if test "x$enable_sixel_images" = xyes; then
AC_DEFINE(ENABLE_SIXEL_IMAGES)
fi
AM_CONDITIONAL(ENABLE_SIXEL, [test "x$enable_sixel" = xyes])
AM_CONDITIONAL(ENABLE_SIXEL_IMAGES, [test "x$enable_sixel_images" = xyes])
# Enable kitty graphics protocol support.
AC_ARG_ENABLE(
kitty-images,
AS_HELP_STRING(--enable-kitty-images, enable kitty terminal graphics)
)
if test "x$enable_kitty_images" = xyes; then
AC_DEFINE(ENABLE_KITTY_IMAGES)
fi
AM_CONDITIONAL(ENABLE_KITTY_IMAGES, [test "x$enable_kitty_images" = xyes])
# Check for b64_ntop. If we have b64_ntop, we assume b64_pton as well.
AC_MSG_CHECKING(for b64_ntop)

View File

@@ -51,29 +51,24 @@ control_notify_window_layout_changed(struct window *w)
template = "%layout-change #{window_id} #{window_layout} "
"#{window_visible_layout} #{window_raw_flags}";
/*
* When the last pane in a window is closed it won't have a layout root
* and we don't need to inform the client about the layout change
* because the whole window will go away soon.
*/
wl = TAILQ_FIRST(&w->winlinks);
if (wl == NULL || w->layout_root == NULL)
return;
cp = format_single(NULL, template, NULL, NULL, wl, NULL);
TAILQ_FOREACH(c, &clients, entry) {
if (!CONTROL_SHOULD_NOTIFY_CLIENT(c) || c->session == NULL)
continue;
s = c->session;
if (winlink_find_by_window_id(&s->windows, w->id) == NULL)
continue;
/*
* When the last pane in a window is closed it won't have a
* layout root and we don't need to inform the client about the
* layout change because the whole window will go away soon.
*/
if (w->layout_root == NULL)
continue;
wl = winlink_find_by_window(&s->windows, w);
if (wl != NULL) {
cp = format_single(NULL, template, c, NULL, wl, NULL);
if (winlink_find_by_window_id(&s->windows, w->id) != NULL)
control_write(c, "%s", cp);
free(cp);
}
}
free(cp);
}
void

204
control.c
View File

@@ -610,7 +610,7 @@ control_append_data(struct client *c, struct control_pane *cp, uint64_t age,
struct evbuffer *message, struct window_pane *wp, size_t size)
{
u_char *new_data;
size_t new_size;
size_t new_size, start;
u_int i;
if (message == NULL) {
@@ -629,10 +629,16 @@ control_append_data(struct client *c, struct control_pane *cp, uint64_t age,
if (new_size < size)
fatalx("not enough data: %zu < %zu", new_size, size);
for (i = 0; i < size; i++) {
if (new_data[i] < ' ' || new_data[i] == '\\')
if (new_data[i] < ' ' || new_data[i] == '\\') {
evbuffer_add_printf(message, "\\%03o", new_data[i]);
else
evbuffer_add_printf(message, "%c", new_data[i]);
} else {
start = i;
while (i + 1 < size &&
new_data[i + 1] >= ' ' &&
new_data[i + 1] != '\\')
i++;
evbuffer_add(message, new_data + start, i - start + 1);
}
}
window_pane_update_used_data(wp, &cp->offset, size);
return (message);
@@ -840,15 +846,13 @@ control_stop(struct client *c)
/* Check session subscription. */
static void
control_check_subs_session(struct client *c, struct control_sub *csub)
control_check_subs_session(struct client *c, struct control_sub *csub,
struct format_tree *ft)
{
struct session *s = c->session;
struct format_tree *ft;
char *value;
ft = format_create_defaults(NULL, c, s, NULL, NULL);
value = format_expand(ft, csub->format);
format_free(ft);
if (csub->last != NULL && strcmp(value, csub->last) == 0) {
free(value);
@@ -909,48 +913,38 @@ control_check_subs_pane(struct client *c, struct control_sub *csub)
}
}
/* Check all panes subscription. */
/* Check all-panes subscription for a pane. */
static void
control_check_subs_all_panes(struct client *c, struct control_sub *csub)
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_pane *wp;
struct window *w;
struct winlink *wl;
struct format_tree *ft;
struct window *w = wl->window;
char *value;
struct control_sub_pane *csp, find;
RB_FOREACH(wl, winlinks, &s->windows) {
w = wl->window;
TAILQ_FOREACH(wp, &w->panes, entry) {
ft = format_create_defaults(NULL, c, s, wl, wp);
value = format_expand(ft, csub->format);
format_free(ft);
value = format_expand(ft, csub->format);
find.pane = wp->id;
find.idx = wl->idx;
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;
}
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. */
@@ -999,45 +993,38 @@ control_check_subs_window(struct client *c, struct control_sub *csub)
}
}
/* Check all windows subscription. */
/* Check all-windows subscription for a window. */
static void
control_check_subs_all_windows(struct client *c, struct control_sub *csub)
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;
struct winlink *wl;
struct format_tree *ft;
struct window *w = wl->window;
char *value;
struct control_sub_window *csw, find;
RB_FOREACH(wl, winlinks, &s->windows) {
w = wl->window;
value = format_expand(ft, csub->format);
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;
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;
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. */
@@ -1047,30 +1034,91 @@ 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);
RB_FOREACH_SAFE(csub, control_subs, &cs->subs, csub1) {
/* Find which subscription types are present. */
RB_FOREACH(csub, control_subs, &cs->subs) {
switch (csub->type) {
case CONTROL_SUB_SESSION:
control_check_subs_session(c, csub);
break;
case CONTROL_SUB_PANE:
control_check_subs_pane(c, csub);
have_session = 1;
break;
case CONTROL_SUB_ALL_PANES:
control_check_subs_all_panes(c, csub);
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:
control_check_subs_all_windows(c, csub);
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. */

View File

@@ -57,6 +57,9 @@ environ_free(struct environ *env)
{
struct environ_entry *envent, *envent1;
if (env == NULL)
return;
RB_FOREACH_SAFE(envent, environ, env, envent1) {
RB_REMOVE(environ, env, envent);
free(envent->name);
@@ -263,11 +266,6 @@ environ_for_session(struct session *s, int no_TERM)
environ_set(env, "TERM_PROGRAM", 0, "%s", "tmux");
environ_set(env, "TERM_PROGRAM_VERSION", 0, "%s", getversion());
environ_set(env, "COLORTERM", 0, "truecolor");
} else {
environ_unset(env, "TERM");
environ_unset(env, "TERM_PROGRAM");
environ_unset(env, "TERM_PROGRAM_VERSION");
environ_unset(env, "COLORTERM");
}
#ifdef HAVE_SYSTEMD

1
file.c
View File

@@ -55,6 +55,7 @@ file_get_path(struct client *c, const char *file)
if (*path == '/')
return (path);
xasprintf(&full_path, "%s/%s", server_client_get_cwd(c, NULL), path);
free(path);
return (full_path);
}

View File

@@ -1104,8 +1104,6 @@ format_width(const char *expanded)
more = utf8_append(&ud, *cp);
if (more == UTF8_DONE)
width += ud.width;
else
cp -= ud.have;
} else if (*cp > 0x1f && *cp < 0x7f) {
width++;
cp++;

340
format.c
View File

@@ -132,17 +132,7 @@ enum format_type {
FORMAT_TYPE_PANE
};
/* Format loop sort type. */
enum format_loop_sort_type {
FORMAT_LOOP_BY_INDEX,
FORMAT_LOOP_BY_NAME,
FORMAT_LOOP_BY_TIME,
};
static struct format_loop_sort_criteria {
enum format_loop_sort_type field;
int reversed;
} format_loop_sort_criteria;
static struct sort_criteria sort_crit;
struct format_tree {
enum format_type type;
@@ -454,6 +444,20 @@ format_job_tidy(struct format_job_tree *jobs, int force)
}
}
/* Work around needless -Wformat-nonliteral gcc warning. */
#ifdef __GNUC__
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wformat-nonliteral"
#endif
static size_t
format_strftime(char *s, size_t max, const char *fmt, const struct tm *tm)
{
return (strftime(s, max, fmt, tm));
}
#ifdef __GNUC__
#pragma GCC diagnostic pop
#endif
/* Tidy old jobs for all clients. */
void
format_tidy_jobs(void)
@@ -1947,6 +1951,18 @@ format_cb_origin_flag(struct format_tree *ft)
return (NULL);
}
/* Callback for synchronized_output_flag. */
static void *
format_cb_synchronized_output_flag(struct format_tree *ft)
{
if (ft->wp != NULL) {
if (ft->wp->base.mode & MODE_SYNC)
return (xstrdup("1"));
return (xstrdup("0"));
}
return (NULL);
}
/* Callback for pane_active. */
static void *
format_cb_pane_active(struct format_tree *ft)
@@ -1996,8 +2012,10 @@ format_cb_pane_bottom(struct format_tree *ft)
static void *
format_cb_pane_dead(struct format_tree *ft)
{
if (ft->wp != NULL) {
if (ft->wp->fd == -1)
struct window_pane *wp = ft->wp;
if (wp != NULL) {
if (wp->fd == -1 && (wp->flags & PANE_STATUSREADY))
return (xstrdup("1"));
return (xstrdup("0"));
}
@@ -2221,6 +2239,17 @@ format_cb_pane_pipe(struct format_tree *ft)
return (NULL);
}
/* Callback for pane_pipe_pid. */
static void *
format_cb_pane_pipe_pid(struct format_tree *ft)
{
char *value = NULL;
if (ft->wp != NULL && ft->wp->pipe_fd != -1)
xasprintf(&value, "%ld", (long)ft->wp->pipe_pid);
return (value);
}
/* Callback for pane_right. */
static void *
format_cb_pane_right(struct format_tree *ft)
@@ -2533,12 +2562,16 @@ format_cb_version(__unused struct format_tree *ft)
/* Callback for sixel_support. */
static void *
format_cb_sixel_support(__unused struct format_tree *ft)
format_cb_image_support(__unused struct format_tree *ft)
{
#ifdef ENABLE_SIXEL
return (xstrdup("1"));
#if defined(ENABLE_SIXEL_IMAGES) && defined(ENABLE_KITTY_IMAGES)
return (xstrdup("kitty,sixel"));
#elif defined(ENABLE_SIXEL_IMAGES)
return (xstrdup("sixel"));
#elif defined(ENABLE_KITTY_IMAGES)
return (xstrdup("kitty"));
#else
return (xstrdup("0"));
return (NULL);
#endif
}
@@ -3161,6 +3194,9 @@ static const struct format_table_entry format_table[] = {
{ "host_short", FORMAT_TABLE_STRING,
format_cb_host_short
},
{ "image_support", FORMAT_TABLE_STRING,
format_cb_image_support
},
{ "insert_flag", FORMAT_TABLE_STRING,
format_cb_insert_flag
},
@@ -3311,6 +3347,9 @@ static const struct format_table_entry format_table[] = {
{ "pane_pipe", FORMAT_TABLE_STRING,
format_cb_pane_pipe
},
{ "pane_pipe_pid", FORMAT_TABLE_STRING,
format_cb_pane_pipe_pid
},
{ "pane_right", FORMAT_TABLE_STRING,
format_cb_pane_right
},
@@ -3434,15 +3473,15 @@ static const struct format_table_entry format_table[] = {
{ "session_windows", FORMAT_TABLE_STRING,
format_cb_session_windows
},
{ "sixel_support", FORMAT_TABLE_STRING,
format_cb_sixel_support
},
{ "socket_path", FORMAT_TABLE_STRING,
format_cb_socket_path
},
{ "start_time", FORMAT_TABLE_TIME,
format_cb_start_time
},
{ "synchronized_output_flag", FORMAT_TABLE_STRING,
format_cb_synchronized_output_flag
},
{ "tree_mode_format", FORMAT_TABLE_STRING,
format_cb_tree_mode_format
},
@@ -3948,7 +3987,7 @@ found:
else {
if (time_format != NULL) {
localtime_r(&t, &tm);
strftime(s, sizeof s, time_format, &tm);
format_strftime(s, sizeof s, time_format, &tm);
} else {
ctime_r(&t, s);
s[strcspn(s, "\n")] = '\0';
@@ -4377,44 +4416,11 @@ format_session_name(struct format_expand_state *es, const char *fmt)
return (xstrdup("0"));
}
static int
format_cmp_session(const void *a0, const void *b0)
{
struct format_loop_sort_criteria *sc = &format_loop_sort_criteria;
const struct session *const *a = a0;
const struct session *const *b = b0;
const struct session *sa = *a;
const struct session *sb = *b;
int result = 0;
switch (sc->field) {
case FORMAT_LOOP_BY_INDEX:
result = sa->id - sb->id;
break;
case FORMAT_LOOP_BY_TIME:
if (timercmp(&sa->activity_time, &sb->activity_time, >)) {
result = -1;
break;
}
if (timercmp(&sa->activity_time, &sb->activity_time, <)) {
result = 1;
break;
}
/* FALLTHROUGH */
case FORMAT_LOOP_BY_NAME:
result = strcmp(sa->name, sb->name);
break;
}
if (sc->reversed)
result = -result;
return (result);
}
/* Loop over sessions. */
static char *
format_loop_sessions(struct format_expand_state *es, const char *fmt)
{
struct sort_criteria *sc = &sort_crit;
struct format_tree *ft = es->ft;
struct client *c = ft->client;
struct cmdq_item *item = ft->item;
@@ -4422,34 +4428,24 @@ format_loop_sessions(struct format_expand_state *es, const char *fmt)
struct format_expand_state next;
char *all, *active, *use, *expanded, *value;
size_t valuelen;
struct session *s;
struct session *s, **l;
int i, n, last = 0;
static struct session **l = NULL;
static int lsz = 0;
if (format_choose(es, fmt, &all, &active, 0) != 0) {
all = xstrdup(fmt);
active = NULL;
}
n = 0;
RB_FOREACH(s, sessions, &sessions) {
if (lsz <= n) {
lsz += 100;
l = xreallocarray(l, lsz, sizeof *l);
}
l[n++] = s;
}
qsort(l, n, sizeof *l, format_cmp_session);
value = xcalloc(1, 1);
valuelen = 1;
l = sort_get_sessions(&n, sc);
for (i = 0; i < n; i++) {
s = l[i];
format_log(es, "session loop: $%u", s->id);
if (active != NULL && s->id == ft->c->session->id)
if (active != NULL &&
ft->c != NULL &&
s->id == ft->c->session->id)
use = active;
else
use = all;
@@ -4469,6 +4465,9 @@ format_loop_sessions(struct format_expand_state *es, const char *fmt)
free(expanded);
}
free(active);
free(all);
return (value);
}
@@ -4496,44 +4495,11 @@ format_window_name(struct format_expand_state *es, const char *fmt)
return (xstrdup("0"));
}
static int
format_cmp_window(const void *a0, const void *b0)
{
struct format_loop_sort_criteria *sc = &format_loop_sort_criteria;
const struct winlink *const *a = a0;
const struct winlink *const *b = b0;
const struct window *wa = (*a)->window;
const struct window *wb = (*b)->window;
int result = 0;
switch (sc->field) {
case FORMAT_LOOP_BY_INDEX:
break;
case FORMAT_LOOP_BY_TIME:
if (timercmp(&wa->activity_time, &wb->activity_time, >)) {
result = -1;
break;
}
if (timercmp(&wa->activity_time, &wb->activity_time, <)) {
result = 1;
break;
}
/* FALLTHROUGH */
case FORMAT_LOOP_BY_NAME:
result = strcmp(wa->name, wb->name);
break;
}
if (sc->reversed)
result = -result;
return (result);
}
/* Loop over windows. */
static char *
format_loop_windows(struct format_expand_state *es, const char *fmt)
{
struct format_loop_sort_criteria *sc = &format_loop_sort_criteria;
struct sort_criteria *sc = &sort_crit;
struct format_tree *ft = es->ft;
struct client *c = ft->client;
struct cmdq_item *item = ft->item;
@@ -4541,11 +4507,9 @@ format_loop_windows(struct format_expand_state *es, const char *fmt)
struct format_expand_state next;
char *all, *active, *use, *expanded, *value;
size_t valuelen;
struct winlink *wl;
struct winlink *wl, **l;
struct window *w;
int i, n, last = 0;
static struct winlink **l = NULL;
static int lsz = 0;
if (ft->s == NULL) {
format_log(es, "window loop but no session");
@@ -4557,31 +4521,10 @@ format_loop_windows(struct format_expand_state *es, const char *fmt)
active = NULL;
}
n = 0;
RB_FOREACH(wl, winlinks, &ft->s->windows) {
if (lsz <= n) {
lsz += 100;
l = xreallocarray(l, lsz, sizeof *l);
}
l[n++] = wl;
}
if (sc->field != FORMAT_LOOP_BY_INDEX)
qsort(l, n, sizeof *l, format_cmp_window);
else {
/* Use order in the tree as index order. */
if (sc->reversed) {
for (i = 0; i < n / 2; i++) {
wl = l[i];
l[i] = l[n - 1 - i];
l[n - 1 - i] = wl;
}
}
}
value = xcalloc(1, 1);
valuelen = 1;
l = sort_get_winlinks_session(ft->s, &n, sc);
for (i = 0; i < n; i++) {
wl = l[i];
w = wl->window;
@@ -4613,27 +4556,11 @@ format_loop_windows(struct format_expand_state *es, const char *fmt)
return (value);
}
static int
format_cmp_pane(const void *a0, const void *b0)
{
struct format_loop_sort_criteria *sc = &format_loop_sort_criteria;
const struct window_pane *const *a = a0;
const struct window_pane *const *b = b0;
const struct window_pane *wpa = *a;
const struct window_pane *wpb = *b;
int result = 0;
if (sc->reversed)
result = wpb->id - wpa->id;
else
result = wpa->id - wpb->id;
return (result);
}
/* Loop over panes. */
static char *
format_loop_panes(struct format_expand_state *es, const char *fmt)
{
struct sort_criteria *sc = &sort_crit;
struct format_tree *ft = es->ft;
struct client *c = ft->client;
struct cmdq_item *item = ft->item;
@@ -4641,10 +4568,8 @@ format_loop_panes(struct format_expand_state *es, const char *fmt)
struct format_expand_state next;
char *all, *active, *use, *expanded, *value;
size_t valuelen;
struct window_pane *wp;
struct window_pane *wp, **l;
int i, n, last = 0;
static struct window_pane **l = NULL;
static int lsz = 0;
if (ft->w == NULL) {
format_log(es, "pane loop but no window");
@@ -4656,20 +4581,10 @@ format_loop_panes(struct format_expand_state *es, const char *fmt)
active = NULL;
}
n = 0;
TAILQ_FOREACH(wp, &ft->w->panes, entry) {
if (lsz <= n) {
lsz += 100;
l = xreallocarray(l, lsz, sizeof *l);
}
l[n++] = wp;
}
qsort(l, n, sizeof *l, format_cmp_pane);
value = xcalloc(1, 1);
valuelen = 1;
l = sort_get_panes_window(ft->w, &n, sc);
for (i = 0; i < n; i++) {
wp = l[i];
format_log(es, "pane loop: %%%u", wp->id);
@@ -4700,80 +4615,24 @@ format_loop_panes(struct format_expand_state *es, const char *fmt)
return (value);
}
static int
format_cmp_client(const void *a0, const void *b0)
{
struct format_loop_sort_criteria *sc = &format_loop_sort_criteria;
const struct client *const *a = a0;
const struct client *const *b = b0;
const struct client *ca = *a;
const struct client *cb = *b;
int result = 0;
switch (sc->field) {
case FORMAT_LOOP_BY_INDEX:
break;
case FORMAT_LOOP_BY_TIME:
if (timercmp(&ca->activity_time, &cb->activity_time, >)) {
result = -1;
break;
}
if (timercmp(&ca->activity_time, &cb->activity_time, <)) {
result = 1;
break;
}
/* FALLTHROUGH */
case FORMAT_LOOP_BY_NAME:
result = strcmp(ca->name, cb->name);
break;
}
if (sc->reversed)
result = -result;
return (result);
}
/* Loop over clients. */
static char *
format_loop_clients(struct format_expand_state *es, const char *fmt)
{
struct format_loop_sort_criteria *sc = &format_loop_sort_criteria;
struct sort_criteria *sc = &sort_crit;
struct format_tree *ft = es->ft;
struct client *c;
struct client *c, **l;
struct cmdq_item *item = ft->item;
struct format_tree *nft;
struct format_expand_state next;
char *expanded, *value;
size_t valuelen;
int i, n, last = 0;
static struct client **l = NULL;
static int lsz = 0;
value = xcalloc(1, 1);
valuelen = 1;
n = 0;
TAILQ_FOREACH(c, &clients, entry) {
if (lsz <= n) {
lsz += 100;
l = xreallocarray(l, lsz, sizeof *l);
}
l[n++] = c;
}
if (sc->field != FORMAT_LOOP_BY_INDEX)
qsort(l, n, sizeof *l, format_cmp_client);
else {
/* Use order in the list as index order. */
if (sc->reversed) {
for (i = 0; i < n / 2; i++) {
c = l[i];
l[i] = l[n - 1 - i];
l[n - 1 - i] = c;
}
}
}
l = sort_get_clients(&n, sc);
for (i = 0; i < n; i++) {
c = l[i];
format_log(es, "client loop: %s", c->name);
@@ -4943,7 +4802,7 @@ static int
format_replace(struct format_expand_state *es, const char *key, size_t keylen,
char **buf, size_t *len, size_t *off)
{
struct format_loop_sort_criteria *sc = &format_loop_sort_criteria;
struct sort_criteria *sc = &sort_crit;
struct format_tree *ft = es->ft;
struct window_pane *wp = ft->wp;
const char *errstr, *copy, *cp, *cp2;
@@ -4960,6 +4819,10 @@ format_replace(struct format_expand_state *es, const char *key, size_t keylen,
u_int i, count, nsub = 0, nrep;
struct format_expand_state next;
/* Set sorting defaults. */
sc->order = SORT_ORDER;
sc->reversed = 0;
/* Make a copy of the key. */
copy = copy0 = xstrndup(key, keylen);
@@ -5070,18 +4933,18 @@ format_replace(struct format_expand_state *es, const char *key, size_t keylen,
case 'S':
modifiers |= FORMAT_SESSIONS;
if (fm->argc < 1) {
sc->field = FORMAT_LOOP_BY_INDEX;
sc->order= SORT_INDEX;
sc->reversed = 0;
break;
}
if (strchr(fm->argv[0], 'i') != NULL)
sc->field = FORMAT_LOOP_BY_INDEX;
sc->order = SORT_INDEX;
else if (strchr(fm->argv[0], 'n') != NULL)
sc->field = FORMAT_LOOP_BY_NAME;
sc->order = SORT_NAME;
else if (strchr(fm->argv[0], 't') != NULL)
sc->field = FORMAT_LOOP_BY_TIME;
sc->order = SORT_ACTIVITY;
else
sc->field = FORMAT_LOOP_BY_INDEX;
sc->order = SORT_INDEX;
if (strchr(fm->argv[0], 'r') != NULL)
sc->reversed = 1;
else
@@ -5090,18 +4953,18 @@ format_replace(struct format_expand_state *es, const char *key, size_t keylen,
case 'W':
modifiers |= FORMAT_WINDOWS;
if (fm->argc < 1) {
sc->field = FORMAT_LOOP_BY_INDEX;
sc->order = SORT_ORDER;
sc->reversed = 0;
break;
}
if (strchr(fm->argv[0], 'i') != NULL)
sc->field = FORMAT_LOOP_BY_INDEX;
sc->order = SORT_ORDER;
else if (strchr(fm->argv[0], 'n') != NULL)
sc->field = FORMAT_LOOP_BY_NAME;
sc->order = SORT_NAME;
else if (strchr(fm->argv[0], 't') != NULL)
sc->field = FORMAT_LOOP_BY_TIME;
sc->order = SORT_ACTIVITY;
else
sc->field = FORMAT_LOOP_BY_INDEX;
sc->order = SORT_ORDER;
if (strchr(fm->argv[0], 'r') != NULL)
sc->reversed = 1;
else
@@ -5109,6 +4972,7 @@ format_replace(struct format_expand_state *es, const char *key, size_t keylen,
break;
case 'P':
modifiers |= FORMAT_PANES;
sc->order = SORT_CREATION;
if (fm->argc < 1) {
sc->reversed = 0;
break;
@@ -5121,18 +4985,18 @@ format_replace(struct format_expand_state *es, const char *key, size_t keylen,
case 'L':
modifiers |= FORMAT_CLIENTS;
if (fm->argc < 1) {
sc->field = FORMAT_LOOP_BY_INDEX;
sc->order = SORT_ORDER;
sc->reversed = 0;
break;
}
if (strchr(fm->argv[0], 'i') != NULL)
sc->field = FORMAT_LOOP_BY_INDEX;
sc->order = SORT_ORDER;
else if (strchr(fm->argv[0], 'n') != NULL)
sc->field = FORMAT_LOOP_BY_NAME;
sc->order = SORT_NAME;
else if (strchr(fm->argv[0], 't') != NULL)
sc->field = FORMAT_LOOP_BY_TIME;
sc->order = SORT_ACTIVITY;
else
sc->field = FORMAT_LOOP_BY_INDEX;
sc->order = SORT_ORDER;
if (strchr(fm->argv[0], 'r') != NULL)
sc->reversed = 1;
else
@@ -5523,7 +5387,8 @@ format_expand1(struct format_expand_state *es, const char *fmt)
es->time = time(NULL);
localtime_r(&es->time, &es->tm);
}
if (strftime(expanded, sizeof expanded, fmt, &es->tm) == 0) {
if (format_strftime(expanded, sizeof expanded, fmt,
&es->tm) == 0) {
format_log(es, "format is too long");
return (xstrdup(""));
}
@@ -5545,7 +5410,8 @@ format_expand1(struct format_expand_state *es, const char *fmt)
buf[off++] = *fmt++;
continue;
}
fmt++;
if (*fmt++ == '\0')
break;
ch = (u_char)*fmt++;
switch (ch) {

View File

@@ -44,7 +44,7 @@ LLVMFuzzerTestOneInput(const u_char *data, size_t size)
w = window_create(PANE_WIDTH, PANE_HEIGHT, 0, 0);
wp = window_add_pane(w, NULL, 0, 0);
bufferevent_pair_new(libevent, BEV_OPT_CLOSE_ON_FREE, vpty);
wp->ictx = input_init(wp, vpty[0], NULL);
wp->ictx = input_init(wp, vpty[0], NULL, NULL);
window_add_ref(w, __func__);
wp->fd = open("/dev/null", O_WRONLY);

View File

@@ -82,7 +82,7 @@ grid_view_clear_history(struct grid *gd, u_int bg)
/* Scroll the lines into the history. */
for (yy = 0; yy < last; yy++) {
grid_collect_history(gd);
grid_collect_history(gd, 0);
grid_scroll_history(gd, bg);
}
if (last < gd->sy)
@@ -107,7 +107,7 @@ grid_view_scroll_region_up(struct grid *gd, u_int rupper, u_int rlower,
u_int bg)
{
if (gd->flags & GRID_HISTORY) {
grid_collect_history(gd);
grid_collect_history(gd, 0);
if (rupper == 0 && rlower == gd->sy - 1)
grid_scroll_history(gd, bg);
else {

39
grid.c
View File

@@ -205,9 +205,17 @@ grid_clear_cell(struct grid *gd, u_int px, u_int py, u_int bg)
struct grid_line *gl = &gd->linedata[py];
struct grid_cell_entry *gce = &gl->celldata[px];
struct grid_extd_entry *gee;
u_int old_offset = gce->offset;
int had_extd = (gce->flags & GRID_FLAG_EXTENDED);
memcpy(gce, &grid_cleared_entry, sizeof *gce);
if (bg != 8) {
if (had_extd && old_offset < gl->extdsize) {
gce->flags |= GRID_FLAG_EXTENDED;
gce->offset = old_offset;
gee = grid_extended_cell(gl, gce, &grid_cleared_cell);
if (bg != 8)
gee->bg = bg;
} else if (bg != 8) {
if (bg & COLOUR_FLAG_RGB) {
grid_get_extended_cell(gl, gce, gce->flags);
gee = grid_extended_cell(gl, gce, &grid_cleared_cell);
@@ -235,7 +243,7 @@ grid_check_y(struct grid *gd, const char *from, u_int py)
int
grid_cells_look_equal(const struct grid_cell *gc1, const struct grid_cell *gc2)
{
int flags1 = gc1->flags, flags2 = gc2->flags;;
int flags1 = gc1->flags, flags2 = gc2->flags;
if (gc1->fg != gc2->fg || gc1->bg != gc2->bg)
return (0);
@@ -361,9 +369,13 @@ grid_compare(struct grid *ga, struct grid *gb)
static void
grid_trim_history(struct grid *gd, u_int ny)
{
u_int remaining;
grid_free_lines(gd, 0, ny);
remaining = gd->hsize + gd->sy - ny;
memmove(&gd->linedata[0], &gd->linedata[ny],
(gd->hsize + gd->sy - ny) * (sizeof *gd->linedata));
remaining * (sizeof *gd->linedata));
memset(&gd->linedata[remaining], 0, ny * (sizeof *gd->linedata));
}
/*
@@ -371,14 +383,17 @@ grid_trim_history(struct grid *gd, u_int ny)
* and shift up.
*/
void
grid_collect_history(struct grid *gd)
grid_collect_history(struct grid *gd, int all)
{
u_int ny;
if (gd->hsize == 0 || gd->hsize < gd->hlimit)
return;
ny = gd->hlimit / 10;
if (all)
ny = gd->hsize - gd->hlimit;
else
ny = gd->hlimit / 10;
if (ny < 1)
ny = 1;
if (ny > gd->hsize)
@@ -480,7 +495,7 @@ static void
grid_expand_line(struct grid *gd, u_int py, u_int sx, u_int bg)
{
struct grid_line *gl;
u_int xx;
u_int xx, old_cellsize;
gl = &gd->linedata[py];
if (sx <= gl->cellsize)
@@ -493,8 +508,10 @@ grid_expand_line(struct grid *gd, u_int py, u_int sx, u_int bg)
else if (gd->sx > sx)
sx = gd->sx;
gl->celldata = xreallocarray(gl->celldata, sx, sizeof *gl->celldata);
for (xx = gl->cellsize; xx < sx; xx++)
old_cellsize = gl->cellsize;
gl->celldata = xrecallocarray(gl->celldata, old_cellsize, sx,
sizeof *gl->celldata);
for (xx = old_cellsize; xx < sx; xx++)
grid_clear_cell(gd, xx, py, bg);
gl->cellsize = sx;
}
@@ -1082,12 +1099,16 @@ grid_string_cells(struct grid *gd, u_int px, u_int py, u_int nx,
off = 0;
gl = grid_peek_line(gd, py);
if (gl == NULL) {
buf[0] = '\0';
return (buf);
}
if (flags & GRID_STRING_EMPTY_CELLS)
end = gl->cellsize;
else
end = gl->cellused;
for (xx = px; xx < px + nx; xx++) {
if (gl == NULL || xx >= end)
if (xx >= end)
break;
grid_get_cell(gd, xx, py, &gc);
if (gc.flags & GRID_FLAG_PADDING)

325
image-kitty.c Normal file
View File

@@ -0,0 +1,325 @@
/* $OpenBSD$ */
/*
* Copyright (c) 2026 Thomas Adam <thomas@xteddy.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 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"
/*
* kitty_image stores the raw decoded pixel data and metadata from a kitty
* graphics protocol APC sequence. It is used to re-emit the sequence to the
* outer terminal on redraw.
*/
struct kitty_image {
/* Control-data fields parsed from the APC sequence. */
char action; /* a=: 'T'=transmit+display, 't', 'p', 'd' */
u_int format; /* f=: 32=RGBA, 24=RGB, 100=PNG */
char medium; /* t=: 'd'=direct, 'f'=file, 't'=tmp, 's'=shm */
u_int pixel_w; /* s=: source image pixel width */
u_int pixel_h; /* v=: source image pixel height */
u_int cols; /* c=: display columns (0=auto) */
u_int rows; /* r=: display rows (0=auto) */
u_int image_id; /* i=: image id (0=unassigned) */
u_int image_num; /* I=: image number */
u_int placement_id; /* p=: placement id */
u_int more; /* m=: 1=more chunks coming, 0=last */
u_int quiet; /* q=: suppress responses */
int z_index; /* z=: z-index */
char compression; /* o=: 'z'=zlib, 0=none */
char delete_what; /* d=: delete target (used with a=d) */
/* Cell size at the time of parsing (from the owning window). */
u_int xpixel;
u_int ypixel;
/* Original base64-encoded payload (concatenated across all chunks). */
char *encoded;
size_t encodedlen;
char *ctrl;
size_t ctrllen;
};
/*
* Parse control-data key=value pairs from a kitty APC sequence.
* Format: key=value,key=value,...
*/
static int
kitty_parse_control(const char *ctrl, size_t ctrllen, struct kitty_image *ki)
{
const char *p = ctrl, *end = ctrl + ctrllen, *errstr;
char key[4], val[32];
size_t klen, vlen;
while (p < end) {
klen = 0;
while (p < end && *p != '=' && klen < sizeof(key) - 1)
key[klen++] = *p++;
key[klen] = '\0';
if (p >= end || *p != '=')
return (-1);
p++;
vlen = 0;
while (p < end && *p != ',' && vlen < sizeof(val) - 1)
val[vlen++] = *p++;
val[vlen] = '\0';
if (p < end && *p == ',')
p++;
if (klen != 1)
continue;
switch (key[0]) {
case 'a':
ki->action = val[0];
break;
case 'f':
ki->format = strtonum(val, 0, UINT_MAX, &errstr);
if (errstr != NULL)
return (-1);
break;
case 't':
ki->medium = val[0];
break;
case 's':
ki->pixel_w = strtonum(val, 0, UINT_MAX, &errstr);
if (errstr != NULL)
return (-1);
break;
case 'v':
ki->pixel_h = strtonum(val, 0, UINT_MAX, &errstr);
if (errstr != NULL)
return (-1);
break;
case 'c':
ki->cols = strtonum(val, 0, UINT_MAX, &errstr);
if (errstr != NULL)
return (-1);
break;
case 'r':
ki->rows = strtonum(val, 0, UINT_MAX, &errstr);
if (errstr != NULL)
return (-1);
break;
case 'i':
ki->image_id = strtonum(val, 0, UINT_MAX, &errstr);
if (errstr != NULL)
return (-1);
break;
case 'I':
ki->image_num = strtonum(val, 0, UINT_MAX, &errstr);
if (errstr != NULL)
return (-1);
break;
case 'p':
ki->placement_id = strtonum(val, 0, UINT_MAX, &errstr);
if (errstr != NULL)
return (-1);
break;
case 'm':
ki->more = strtonum(val, 0, UINT_MAX, &errstr);
if (errstr != NULL)
return (-1);
break;
case 'q':
ki->quiet = strtonum(val, 0, UINT_MAX, &errstr);
if (errstr != NULL)
return (-1);
break;
case 'z':
ki->z_index = strtonum(val, INT_MIN, INT_MAX, &errstr);
if (errstr != NULL)
return (-1);
break;
case 'o':
ki->compression = val[0];
break;
case 'd':
ki->delete_what = val[0];
break;
}
}
return (0);
}
/*
* Parse a kitty APC body (after the leading 'G').
* Stores the original control string and base64 payload verbatim for
* pass-through re-emission to the outer terminal.
*/
struct kitty_image *
kitty_parse(const u_char *buf, size_t len, u_int xpixel, u_int ypixel)
{
struct kitty_image *ki;
const u_char *semi;
const char *ctrl;
size_t ctrllen, paylen;
if (len == 0)
return (NULL);
semi = memchr(buf, ';', len);
if (semi != NULL) {
ctrl = (const char *)buf;
ctrllen = semi - buf;
paylen = len - ctrllen - 1;
} else {
ctrl = (const char *)buf;
ctrllen = len;
paylen = 0;
}
ki = xcalloc(1, sizeof *ki);
ki->xpixel = xpixel;
ki->ypixel = ypixel;
ki->action = 'T';
ki->format = 32;
ki->medium = 'd';
if (kitty_parse_control(ctrl, ctrllen, ki) != 0) {
free(ki);
return (NULL);
}
if (paylen > 0) {
ki->encoded = xmalloc(paylen + 1);
memcpy(ki->encoded, semi + 1, paylen);
ki->encoded[paylen] = '\0';
ki->encodedlen = paylen;
}
ki->ctrl = xmalloc(ctrllen + 1);
memcpy(ki->ctrl, ctrl, ctrllen);
ki->ctrl[ctrllen] = '\0';
ki->ctrllen = ctrllen;
return (ki);
}
void
kitty_free(struct kitty_image *ki)
{
if (ki == NULL)
return;
free(ki->encoded);
free(ki->ctrl);
free(ki);
}
/*
* Get the size in cells of a kitty image. If cols/rows are 0 (auto),
* calculate from pixel dimensions. Returns size via sx/sy pointers.
*/
void
kitty_size_in_cells(struct kitty_image *ki, u_int *sx, u_int *sy)
{
*sx = ki->cols;
*sy = ki->rows;
/*
* If cols/rows are 0, they mean "auto" - calculate from
* pixel dimensions.
*/
if (*sx == 0 && ki->pixel_w > 0 && ki->xpixel > 0) {
*sx = (ki->pixel_w + ki->xpixel - 1) / ki->xpixel;
}
if (*sy == 0 && ki->pixel_h > 0 && ki->ypixel > 0) {
*sy = (ki->pixel_h + ki->ypixel - 1) / ki->ypixel;
}
/* If still 0, use a reasonable default */
if (*sx == 0)
*sx = 10;
if (*sy == 0)
*sy = 10;
}
char
kitty_get_action(struct kitty_image *ki)
{
return (ki->action);
}
u_int
kitty_get_image_id(struct kitty_image *ki)
{
return (ki->image_id);
}
u_int
kitty_get_rows(struct kitty_image *ki)
{
return (ki->rows);
}
/*
* Serialize a kitty_image back into an APC escape sequence for transmission
* to the terminal. This recreates the original command that was parsed.
*/
char *
kitty_print(struct kitty_image *ki, size_t *outlen)
{
char *out;
size_t total, pos;
if (ki == NULL || ki->ctrl == NULL)
return (NULL);
/* Calculate total length: ESC _ G + ctrl + ; + encoded + ESC \ */
total = 3 + ki->ctrllen; /* \033_G + ctrl */
if (ki->encoded != NULL && ki->encodedlen > 0) {
total += 1 + ki->encodedlen; /* ; + encoded */
}
total += 2; /* \033\\ */
out = xmalloc(total + 1);
*outlen = total;
/* Build the sequence */
pos = 0;
memcpy(out + pos, "\033_G", 3);
pos += 3;
memcpy(out + pos, ki->ctrl, ki->ctrllen);
pos += ki->ctrllen;
if (ki->encoded != NULL && ki->encodedlen > 0) {
out[pos++] = ';';
memcpy(out + pos, ki->encoded, ki->encodedlen);
pos += ki->encodedlen;
}
memcpy(out + pos, "\033\\", 2);
pos += 2;
out[pos] = '\0';
return (out);
}
char *
kitty_delete_all(size_t *outlen)
{
char *out;
out = xstrdup("\033_Ga=d,d=a\033\\");
*outlen = strlen(out);
return (out);
}

View File

@@ -455,12 +455,12 @@ sixel_scale(struct sixel_image *si, u_int xpixel, u_int ypixel, u_int ox,
new->p2 = si->p2;
new->set_ra = si->set_ra;
/* clamp to slice end */
new->ra_x = si->ra_x < psx ? si->ra_x : psx;
new->ra_y = si->ra_y < psy ? si->ra_y : psy;
/* subtract slice origin */
/* subtract offset */
new->ra_x = new->ra_x > pox ? new->ra_x - pox : 0;
new->ra_y = new->ra_y > poy ? new->ra_y - poy : 0;
/* clamp to size */
new->ra_x = si->ra_x < psx ? si->ra_x : psx;
new->ra_y = si->ra_y < psy ? si->ra_y : psy;
/* resize */
new->ra_x = new->ra_x * xpixel / si->xpixel;
new->ra_y = new->ra_y * ypixel / si->ypixel;

167
image.c
View File

@@ -25,17 +25,58 @@
static struct images all_images = TAILQ_HEAD_INITIALIZER(all_images);
static u_int all_images_count;
#define MAX_IMAGE_COUNT 20
static void printflike(3, 4)
image_log(struct image *im, const char* from, const char* fmt, ...)
{
va_list ap;
char s[128];
if (log_get_level() == 0)
return;
if (fmt == NULL) {
log_debug("%s: %p (%ux%u %u,%u)", from, im, im->sx, im->sy,
im->px, im->py);
return;
}
va_start(ap, fmt);
vsnprintf(s, sizeof s, fmt, ap);
va_end(ap);
log_debug("%s: %p (%ux%u %u,%u): %s", from, im, im->sx, im->sy,
im->px, im->py, s);
}
static void
image_free(struct image *im)
{
struct screen *s = im->s;
image_log(im, __func__, NULL);
TAILQ_REMOVE(&all_images, im, all_entry);
all_images_count--;
TAILQ_REMOVE(&s->images, im, entry);
sixel_free(im->data);
switch (im->type) {
#ifdef ENABLE_SIXEL_IMAGES
case IMAGE_SIXEL:
sixel_free(im->data.sixel);
break;
#endif
#ifdef ENABLE_KITTY_IMAGES
case IMAGE_KITTY:
kitty_free(im->data.kitty);
break;
#endif
default:
break;
}
free(im->fallback);
free(im);
}
@@ -46,6 +87,8 @@ image_free_all(struct screen *s)
struct image *im, *im1;
int redraw = !TAILQ_EMPTY(&s->images);
if (redraw)
log_debug ("%s", __func__);
TAILQ_FOREACH_SAFE(im, &s->images, entry, im1)
image_free(im);
return (redraw);
@@ -53,13 +96,30 @@ image_free_all(struct screen *s)
/* Create text placeholder for an image. */
static void
image_fallback(char **ret, u_int sx, u_int sy)
image_fallback(char **ret, enum image_type type, u_int sx, u_int sy)
{
char *buf, *label;
u_int py, size, lsize;
const char *type_name;
switch (type) {
#ifdef ENABLE_SIXEL_IMAGES
case IMAGE_SIXEL:
type_name = "SIXEL";
break;
#endif
#ifdef ENABLE_KITTY_IMAGES
case IMAGE_KITTY:
type_name = "KITTY";
break;
#endif
default:
type_name = "UNKNOWN";
break;
}
/* Allocate first line. */
lsize = xasprintf(&label, "SIXEL IMAGE (%ux%u)\r\n", sx, sy) + 1;
lsize = xasprintf(&label, "%s IMAGE (%ux%u)\r\n", type_name, sx, sy) + 1;
if (sx < lsize - 3)
size = lsize - 1;
else
@@ -94,24 +154,42 @@ image_fallback(char **ret, u_int sx, u_int sy)
}
struct image*
image_store(struct screen *s, struct sixel_image *si)
image_store(struct screen *s, enum image_type type, void *data)
{
struct image *im;
im = xcalloc(1, sizeof *im);
im->type = type;
im->s = s;
im->data = si;
im->px = s->cx;
im->py = s->cy;
sixel_size_in_cells(si, &im->sx, &im->sy);
image_fallback(&im->fallback, im->sx, im->sy);
switch (type) {
#ifdef ENABLE_SIXEL_IMAGES
case IMAGE_SIXEL:
im->data.sixel = data;
sixel_size_in_cells(im->data.sixel, &im->sx, &im->sy);
break;
#endif
#ifdef ENABLE_KITTY_IMAGES
case IMAGE_KITTY:
im->data.kitty = data;
kitty_size_in_cells(im->data.kitty, &im->sx, &im->sy);
break;
#endif
default:
break;
}
image_fallback(&im->fallback, type, im->sx, im->sy);
image_log(im, __func__, NULL);
TAILQ_INSERT_TAIL(&s->images, im, entry);
TAILQ_INSERT_TAIL(&all_images, im, all_entry);
if (++all_images_count == 10/*XXX*/)
if (++all_images_count == MAX_IMAGE_COUNT)
image_free(TAILQ_FIRST(&all_images));
return (im);
@@ -121,10 +199,12 @@ int
image_check_line(struct screen *s, u_int py, u_int ny)
{
struct image *im, *im1;
int redraw = 0;
int redraw = 0, in;
TAILQ_FOREACH_SAFE(im, &s->images, entry, im1) {
if (py + ny > im->py && py < im->py + im->sy) {
in = (py + ny > im->py && py < im->py + im->sy);
image_log(im, __func__, "py=%u, ny=%u, in=%d", py, ny, in);
if (in) {
image_free(im);
redraw = 1;
}
@@ -136,15 +216,18 @@ int
image_check_area(struct screen *s, u_int px, u_int py, u_int nx, u_int ny)
{
struct image *im, *im1;
int redraw = 0;
int redraw = 0, in;
TAILQ_FOREACH_SAFE(im, &s->images, entry, im1) {
if (py + ny <= im->py || py >= im->py + im->sy)
continue;
if (px + nx <= im->px || px >= im->px + im->sx)
continue;
image_free(im);
redraw = 1;
in = (py < im->py + im->sy &&
py + ny > im->py &&
px < im->px + im->sx &&
px + nx > im->px);
image_log(im, __func__, "py=%u, ny=%u, in=%d", py, ny, in);
if (in) {
image_free(im);
redraw = 1;
}
}
return (redraw);
}
@@ -154,33 +237,61 @@ image_scroll_up(struct screen *s, u_int lines)
{
struct image *im, *im1;
int redraw = 0;
u_int sx, sy;
#ifdef ENABLE_SIXEL_IMAGES
struct sixel_image *new;
u_int sx, sy;
#endif
TAILQ_FOREACH_SAFE(im, &s->images, entry, im1) {
if (im->py >= lines) {
image_log(im, __func__, "1, lines=%u", lines);
im->py -= lines;
redraw = 1;
continue;
}
if (im->py + im->sy <= lines) {
image_log(im, __func__, "2, lines=%u", lines);
image_free(im);
redraw = 1;
continue;
}
sx = im->sx;
sy = (im->py + im->sy) - lines;
new = sixel_scale(im->data, 0, 0, 0, im->sy - sy, sx, sy, 1);
sixel_free(im->data);
im->data = new;
/* Image is partially scrolled off - need to crop it */
switch (im->type) {
#ifdef ENABLE_SIXEL_IMAGES
case IMAGE_SIXEL:
sx = im->sx;
sy = (im->py + im->sy) - lines;
image_log(im, __func__, "sixel, lines=%u, sy=%u",
lines, sy);
im->py = 0;
sixel_size_in_cells(im->data, &im->sx, &im->sy);
new = sixel_scale(im->data.sixel, 0, 0, 0, im->sy - sy,
sx, sy, 1);
sixel_free(im->data.sixel);
im->data.sixel = new;
free(im->fallback);
image_fallback(&im->fallback, im->sx, im->sy);
redraw = 1;
im->py = 0;
sixel_size_in_cells(im->data.sixel, &im->sx, &im->sy);
free(im->fallback);
image_fallback(&im->fallback, im->type, im->sx, im->sy);
redraw = 1;
break;
#endif
#ifdef ENABLE_KITTY_IMAGES
case IMAGE_KITTY:
/*
* For kitty images, we can't rescale - the terminal
* owns the placement. Just adjust position and let
* the terminal handle clipping.
*/
im->py = 0;
redraw = 1;
break;
#endif
default:
break;
}
}
return (redraw);
}

View File

@@ -313,12 +313,6 @@ static struct input_key_entry input_key_defaults[] = {
{ .key = KEYC_DC|KEYC_BUILD_MODIFIERS,
.data = "\033[3;_~"
},
{ .key = KEYC_REPORT_DARK_THEME,
.data = "\033[?997;1n"
},
{ .key = KEYC_REPORT_LIGHT_THEME,
.data = "\033[?997;2n"
},
};
static const key_code input_key_modifiers[] = {
0,

375
input.c
View File

@@ -63,7 +63,7 @@ struct input_request {
struct input_ctx *ictx;
enum input_request_type type;
time_t t;
uint64_t t;
enum input_end_type end;
int idx;
@@ -72,7 +72,7 @@ struct input_request {
TAILQ_ENTRY(input_request) entry;
TAILQ_ENTRY(input_request) centry;
};
#define INPUT_REQUEST_TIMEOUT 2
#define INPUT_REQUEST_TIMEOUT 500
/* Input parser cell. */
struct input_cell {
@@ -101,6 +101,7 @@ struct input_ctx {
struct bufferevent *event;
struct screen_write_ctx ctx;
struct colour_palette *palette;
struct client *c;
struct input_cell cell;
struct input_cell old_cell;
@@ -144,6 +145,7 @@ struct input_ctx {
*/
struct evbuffer *since_ground;
struct event ground_timer;
};
/* Helper functions. */
@@ -179,6 +181,7 @@ static void input_ground(struct input_ctx *);
static void input_enter_dcs(struct input_ctx *);
static void input_enter_osc(struct input_ctx *);
static void input_exit_osc(struct input_ctx *);
static int input_da1_has_sixel(struct input_ctx *);
static void input_enter_apc(struct input_ctx *);
static void input_exit_apc(struct input_ctx *);
static void input_enter_rename(struct input_ctx *);
@@ -854,7 +857,7 @@ input_restore_state(struct input_ctx *ictx)
/* Initialise input parser. */
struct input_ctx *
input_init(struct window_pane *wp, struct bufferevent *bev,
struct colour_palette *palette)
struct colour_palette *palette, struct client *c)
{
struct input_ctx *ictx;
@@ -862,6 +865,7 @@ input_init(struct window_pane *wp, struct bufferevent *bev,
ictx->wp = wp;
ictx->event = bev;
ictx->palette = palette;
ictx->c = c;
ictx->input_space = INPUT_BUF_START;
ictx->input_buf = xmalloc(INPUT_BUF_START);
@@ -898,6 +902,8 @@ input_free(struct input_ctx *ictx)
evbuffer_free(ictx->since_ground);
event_del(&ictx->ground_timer);
screen_write_stop_sync(ictx->wp);
free(ictx);
}
@@ -1135,11 +1141,9 @@ input_get(struct input_ctx *ictx, u_int validx, int minval, int defval)
static void
input_send_reply(struct input_ctx *ictx, const char *reply)
{
struct bufferevent *bev = ictx->event;
if (bev != NULL) {
if (ictx->event != NULL) {
log_debug("%s: %s", __func__, reply);
bufferevent_write(bev, reply, strlen(reply));
bufferevent_write(ictx->event, reply, strlen(reply));
}
}
@@ -1379,6 +1383,10 @@ input_esc_dispatch(struct input_ctx *ictx)
input_reset_cell(ictx);
screen_write_reset(sctx);
screen_write_fullredraw(sctx);
#ifdef ENABLE_KITTY_IMAGES
if (ictx->wp != NULL)
tty_kitty_delete_all_pane(ictx->wp);
#endif
break;
case INPUT_ESC_IND:
screen_write_linefeed(sctx, 0, ictx->cell.cell.bg);
@@ -1554,11 +1562,10 @@ input_csi_dispatch(struct input_ctx *ictx)
case -1:
break;
case 0:
#ifdef ENABLE_SIXEL
input_reply(ictx, 1, "\033[?1;2;4c");
#else
input_reply(ictx, 1, "\033[?1;2c");
#endif
if (input_da1_has_sixel(ictx))
input_reply(ictx, 1, "\033[?1;2;4c");
else
input_reply(ictx, 1, "\033[?1;2c");
break;
default:
log_debug("%s: unknown '%c'", __func__, ictx->ch);
@@ -1623,10 +1630,6 @@ input_csi_dispatch(struct input_ctx *ictx)
}
input_reply(ictx, 1, "\033[?12;%d$y", n);
break;
case 2004: /* bracketed paste */
n = (s->mode & MODE_BRACKETPASTE) ? 1 : 2;
input_reply(ictx, 1, "\033[?2004;%d$y", n);
break;
case 1004: /* focus reporting */
n = (s->mode & MODE_FOCUSON) ? 1 : 2;
input_reply(ictx, 1, "\033[?1004;%d$y", n);
@@ -1635,6 +1638,14 @@ input_csi_dispatch(struct input_ctx *ictx)
n = (s->mode & MODE_MOUSE_SGR) ? 1 : 2;
input_reply(ictx, 1, "\033[?1006;%d$y", n);
break;
case 2004: /* bracketed paste */
n = (s->mode & MODE_BRACKETPASTE) ? 1 : 2;
input_reply(ictx, 1, "\033[?2004;%d$y", n);
break;
case 2026: /* synchronized output */
n = (s->mode & MODE_SYNC) ? 1 : 2;
input_reply(ictx, 1, "\033[?2026;%d$y", n);
break;
case 2031:
input_reply(ictx, 1, "\033[?2031;2$y");
break;
@@ -1900,6 +1911,13 @@ input_csi_dispatch_rm_private(struct input_ctx *ictx)
break;
case 2031:
screen_write_mode_clear(sctx, MODE_THEME_UPDATES);
if (ictx->wp != NULL)
ictx->wp->flags &= ~PANE_THEMECHANGED;
break;
case 2026: /* synchronized output */
screen_write_stop_sync(ictx->wp);
if (ictx->wp != NULL)
ictx->wp->flags |= PANE_REDRAW;
break;
default:
log_debug("%s: unknown '%c'", __func__, ictx->ch);
@@ -1998,6 +2016,13 @@ input_csi_dispatch_sm_private(struct input_ctx *ictx)
break;
case 2031:
screen_write_mode_set(sctx, MODE_THEME_UPDATES);
if (ictx->wp != NULL) {
ictx->wp->last_theme = window_pane_get_theme(ictx->wp);
ictx->wp->flags &= ~PANE_THEMECHANGED;
}
break;
case 2026: /* synchronized output */
screen_write_start_sync(ictx->wp);
break;
default:
log_debug("%s: unknown '%c'", __func__, ictx->ch);
@@ -2010,7 +2035,7 @@ input_csi_dispatch_sm_private(struct input_ctx *ictx)
static void
input_csi_dispatch_sm_graphics(__unused struct input_ctx *ictx)
{
#ifdef ENABLE_SIXEL
#ifdef ENABLE_SIXEL_IMAGES
int n, m, o;
if (ictx->param_list_len > 3)
@@ -2535,7 +2560,7 @@ input_dcs_dispatch(struct input_ctx *ictx)
const char prefix[] = "tmux;";
const u_int prefixlen = (sizeof prefix) - 1;
long long allow_passthrough = 0;
#ifdef ENABLE_SIXEL
#ifdef ENABLE_SIXEL_IMAGES
struct window *w;
struct sixel_image *si;
int p2;
@@ -2550,7 +2575,7 @@ input_dcs_dispatch(struct input_ctx *ictx)
return (0);
}
#ifdef ENABLE_SIXEL
#ifdef ENABLE_SIXEL_IMAGES
w = wp->window;
if (buf[0] == 'q' && ictx->interm_len == 0) {
if (input_split(ictx) != 0)
@@ -2696,6 +2721,79 @@ input_enter_apc(struct input_ctx *ictx)
ictx->flags &= ~INPUT_LAST;
}
/*
* Check if any client viewing this pane has an outer terminal that supports
* sixel, so we can report it in the DA1 response.
*/
static int
input_da1_has_sixel(__unused struct input_ctx *ictx)
{
#ifdef ENABLE_SIXEL_IMAGES
struct window_pane *wp = ictx->wp;
struct client *c;
if (wp == NULL)
return (0);
TAILQ_FOREACH(c, &clients, entry) {
if (c->session == NULL)
continue;
if (c->session->curw->window != wp->window)
continue;
if (c->tty.term->flags & TERM_SIXEL)
return (1);
}
#endif
return (0);
}
#ifdef ENABLE_KITTY_IMAGES
/* Handle a kitty graphics APC sequence. */
static void
input_apc_kitty_image(struct input_ctx *ictx)
{
struct screen_write_ctx *sctx = &ictx->ctx;
struct window_pane *wp = ictx->wp;
struct window *w;
struct kitty_image *ki;
if (wp == NULL)
return;
w = wp->window;
ki = kitty_parse(ictx->input_buf + 1, ictx->input_len - 1,
w->xpixel, w->ypixel);
if (ki == NULL)
return;
/* Handle query commands. */
if (kitty_get_action(ki) == 'q') {
if (kitty_get_image_id(ki) != 0)
input_reply(ictx, 0, "\033_Gi=%u;OK\033\\",
kitty_get_image_id(ki));
else
input_reply(ictx, 0, "\033_Ga=q;OK\033\\");
kitty_free(ki);
return;
}
/* Store image placements and trigger a redraw. */
if (kitty_get_action(ki) == 'T' || kitty_get_action(ki) == 't' ||
kitty_get_action(ki) == 'p') {
screen_write_kittyimage(sctx, ki);
} else {
/* For other actions (delete, etc.), pass through. */
char *apc;
size_t apclen;
apclen = xasprintf(&apc, "\033_%s\033\\", ictx->input_buf);
tty_kitty_passthrough(wp, apc, apclen, sctx->s->cx,
sctx->s->cy);
free(apc);
kitty_free(ki);
}
}
#endif
/* APC terminator (ST) received. */
static void
input_exit_apc(struct input_ctx *ictx)
@@ -2707,6 +2805,13 @@ input_exit_apc(struct input_ctx *ictx)
return;
log_debug("%s: \"%s\"", __func__, ictx->input_buf);
#ifdef ENABLE_KITTY_IMAGES
if (ictx->input_len >= 1 && ictx->input_buf[0] == 'G') {
input_apc_kitty_image(ictx);
return;
}
#endif
if (wp != NULL &&
options_get_number(wp->options, "allow-set-title") &&
screen_set_title(sctx->s, ictx->input_buf)) {
@@ -3069,67 +3174,110 @@ input_osc_133(struct input_ctx *ictx, const char *p)
}
}
/* Handle OSC 52 reply. */
static void
input_osc_52_reply(struct input_ctx *ictx, char clip)
{
struct bufferevent *ev = ictx->event;
struct paste_buffer *pb;
int state;
const char *buf;
size_t len;
state = options_get_number(global_options, "get-clipboard");
if (state == 0)
return;
if (state == 1) {
if ((pb = paste_get_top(NULL)) == NULL)
return;
buf = paste_buffer_data(pb, &len);
if (ictx->input_end == INPUT_END_BEL)
input_reply_clipboard(ev, buf, len, "\007", clip);
else
input_reply_clipboard(ev, buf, len, "\033\\", clip);
return;
}
input_add_request(ictx, INPUT_REQUEST_CLIPBOARD, ictx->input_end);
}
/*
* Parse and decode OSC 52 clipboard data. Returns 0 on failure or if handled
* as a query. On success, returns 1 and sets *out, *outlen, and *flags (caller
* must free *out).
*/
static int
input_osc_52_parse(struct input_ctx *ictx, const char *p, u_char **out,
int *outlen, char *clip)
{
char *end;
size_t len;
const char *allow = "cpqs01234567";
u_int i, j = 0;
if (options_get_number(global_options, "set-clipboard") != 2)
return (0);
if ((end = strchr(p, ';')) == NULL)
return (0);
end++;
if (*end == '\0')
return (0);
log_debug("%s: %s", __func__, end);
for (i = 0; p + i != end; i++) {
if (strchr(allow, p[i]) != NULL && strchr(clip, p[i]) == NULL)
clip[j++] = p[i];
}
log_debug("%s: %.*s %s", __func__, (int)(end - p - 1), p, clip);
if (strcmp(end, "?") == 0) {
input_osc_52_reply(ictx, *clip);
return (0);
}
len = (strlen(end) / 4) * 3;
if (len == 0)
return (0);
*out = xmalloc(len);
if ((*outlen = b64_pton(end, *out, len)) == -1) {
free(*out);
*out = NULL;
return (0);
}
return (1);
}
/* Handle the OSC 52 sequence for setting the clipboard. */
static void
input_osc_52(struct input_ctx *ictx, const char *p)
{
struct window_pane *wp = ictx->wp;
char *end;
const char *buf = NULL;
size_t len = 0;
struct screen_write_ctx ctx;
u_char *out;
int outlen, state;
struct screen_write_ctx ctx;
struct paste_buffer *pb;
const char* allow = "cpqs01234567";
char flags[sizeof "cpqs01234567"] = "";
u_int i, j = 0;
int outlen;
char clip[sizeof "cpqs01234567"] = "";
if (wp == NULL)
return;
state = options_get_number(global_options, "set-clipboard");
if (state != 2)
if (!input_osc_52_parse(ictx, p, &out, &outlen, clip))
return;
if ((end = strchr(p, ';')) == NULL)
return;
end++;
if (*end == '\0')
return;
log_debug("%s: %s", __func__, end);
for (i = 0; p + i != end; i++) {
if (strchr(allow, p[i]) != NULL && strchr(flags, p[i]) == NULL)
flags[j++] = p[i];
if (wp == NULL) {
/* Popup window. */
if (ictx->c == NULL) {
free(out);
return;
}
tty_set_selection(&ictx->c->tty, clip, out, outlen);
paste_add(NULL, out, outlen);
} else {
/* Normal window. */
screen_write_start_pane(&ctx, wp, NULL);
screen_write_setselection(&ctx, clip, out, outlen);
screen_write_stop(&ctx);
notify_pane("pane-set-clipboard", wp);
paste_add(NULL, out, outlen);
}
log_debug("%s: %.*s %s", __func__, (int)(end - p - 1), p, flags);
if (strcmp(end, "?") == 0) {
if ((pb = paste_get_top(NULL)) != NULL)
buf = paste_buffer_data(pb, &len);
if (ictx->input_end == INPUT_END_BEL)
input_reply_clipboard(ictx->event, buf, len, "\007");
else
input_reply_clipboard(ictx->event, buf, len, "\033\\");
return;
}
len = (strlen(end) / 4) * 3;
if (len == 0)
return;
out = xmalloc(len);
if ((outlen = b64_pton(end, out, len)) == -1) {
free(out);
return;
}
screen_write_start_pane(&ctx, wp, NULL);
screen_write_setselection(&ctx, flags, out, outlen);
screen_write_stop(&ctx);
notify_pane("pane-set-clipboard", wp);
paste_add(NULL, out, outlen);
}
/* Handle the OSC 104 sequence for unsetting (multiple) palette entries. */
@@ -3169,9 +3317,10 @@ input_osc_104(struct input_ctx *ictx, const char *p)
free(copy);
}
/* Send a clipboard reply. */
void
input_reply_clipboard(struct bufferevent *bev, const char *buf, size_t len,
const char *end)
const char *end, char clip)
{
char *out = NULL;
int outlen = 0;
@@ -3187,7 +3336,10 @@ input_reply_clipboard(struct bufferevent *bev, const char *buf, size_t len,
}
}
bufferevent_write(bev, "\033]52;;", 6);
bufferevent_write(bev, "\033]52;", 5);
if (clip != 0)
bufferevent_write(bev, &clip, 1);
bufferevent_write(bev, ";", 1);
if (outlen != 0)
bufferevent_write(bev, out, outlen);
bufferevent_write(bev, end, strlen(end));
@@ -3208,7 +3360,7 @@ input_request_timer_callback(__unused int fd, __unused short events, void *arg)
{
struct input_ctx *ictx = arg;
struct input_request *ir, *ir1;
time_t t = time(NULL);
uint64_t t = get_timer();
TAILQ_FOREACH_SAFE(ir, &ictx->requests, entry, ir1) {
if (ir->t >= t - INPUT_REQUEST_TIMEOUT)
@@ -3225,7 +3377,7 @@ input_request_timer_callback(__unused int fd, __unused short events, void *arg)
static void
input_start_request_timer(struct input_ctx *ictx)
{
struct timeval tv = { .tv_sec = 0, .tv_usec = 500000 };
struct timeval tv = { .tv_sec = 0, .tv_usec = 100000 };
event_del(&ictx->request_timer);
event_add(&ictx->request_timer, &tv);
@@ -3240,7 +3392,7 @@ input_make_request(struct input_ctx *ictx, enum input_request_type type)
ir = xcalloc (1, sizeof *ir);
ir->type = type;
ir->ictx = ictx;
ir->t = time(NULL);
ir->t = get_timer();
if (++ictx->request_count == 1)
input_start_request_timer(ictx);
@@ -3305,6 +3457,9 @@ input_add_request(struct input_ctx *ictx, enum input_request_type type, int idx)
xsnprintf(s, sizeof s, "\033]4;%d;?\033\\", idx);
tty_puts(&c->tty, s);
break;
case INPUT_REQUEST_CLIPBOARD:
tty_putcode_ss(&c->tty, TTYC_MS, "", "?");
break;
case INPUT_REQUEST_QUEUE:
break;
}
@@ -3312,6 +3467,40 @@ input_add_request(struct input_ctx *ictx, enum input_request_type type, int idx)
return (0);
}
/* Handle a palette reply. */
static void
input_request_palette_reply(struct input_request *ir, void *data)
{
struct input_request_palette_data *pd = data;
input_osc_colour_reply(ir->ictx, 0, 4, pd->idx, pd->c, ir->end);
}
/* Handle a clipboard reply. */
static void
input_request_clipboard_reply(struct input_request *ir, void *data)
{
struct input_ctx *ictx = ir->ictx;
struct bufferevent *ev = ictx->event;
struct input_request_clipboard_data *cd = data;
int state;
char *copy;
state = options_get_number(global_options, "get-clipboard");
if (state == 0 || state == 1)
return;
if (state == 3) {
copy = xmalloc(cd->len);
memcpy(copy, cd->buf, cd->len);
paste_add(NULL, copy, cd->len);
}
if (ir->idx == INPUT_END_BEL)
input_reply_clipboard(ev, cd->buf, cd->len, "\007", cd->clip);
else
input_reply_clipboard(ev, cd->buf, cd->len, "\033\\", cd->clip);
}
/* Handle a reply to a request. */
void
input_request_reply(struct client *c, enum input_request_type type, void *data)
@@ -3321,11 +3510,22 @@ input_request_reply(struct client *c, enum input_request_type type, void *data)
int complete = 0;
TAILQ_FOREACH_SAFE(ir, &c->input_requests, centry, ir1) {
if (ir->type == type && pd->idx == ir->idx) {
if (ir->type != type) {
input_free_request(ir);
continue;
}
if (type == INPUT_REQUEST_PALETTE) {
if (pd->idx != ir->idx) {
input_free_request(ir);
continue;
}
found = ir;
break;
}
if (type == INPUT_REQUEST_CLIPBOARD) {
found = ir;
break;
}
input_free_request(ir);
}
if (found == NULL)
return;
@@ -3335,8 +3535,11 @@ input_request_reply(struct client *c, enum input_request_type type, void *data)
break;
if (ir->type == INPUT_REQUEST_QUEUE)
input_send_reply(ir->ictx, ir->data);
else if (ir == found && ir->type == INPUT_REQUEST_PALETTE) {
input_osc_colour_reply(ir->ictx, 0, 4, pd->idx, pd->c, ir->end);
else if (ir == found) {
if (ir->type == INPUT_REQUEST_PALETTE)
input_request_palette_reply(ir, data);
else if (ir->type == INPUT_REQUEST_CLIPBOARD)
input_request_clipboard_reply(ir, data);
complete = 1;
}
input_free_request(ir);
@@ -3349,7 +3552,7 @@ input_cancel_requests(struct client *c)
{
struct input_request *ir, *ir1;
TAILQ_FOREACH_SAFE(ir, &c->input_requests, entry, ir1)
TAILQ_FOREACH_SAFE(ir, &c->input_requests, centry, ir1)
input_free_request(ir);
}
@@ -3357,14 +3560,24 @@ input_cancel_requests(struct client *c)
static void
input_report_current_theme(struct input_ctx *ictx)
{
switch (window_pane_get_theme(ictx->wp)) {
struct window_pane *wp = ictx->wp;
if (wp != NULL) {
wp->last_theme = window_pane_get_theme(wp);
wp->flags &= ~PANE_THEMECHANGED;
switch (wp->last_theme) {
case THEME_DARK:
log_debug("%s: %%%u dark theme", __func__, wp->id);
input_reply(ictx, 0, "\033[?997;1n");
break;
case THEME_LIGHT:
log_debug("%s: %%%u light theme", __func__, wp->id);
input_reply(ictx, 0, "\033[?997;2n");
break;
case THEME_UNKNOWN:
log_debug("%s: %%%u unknown theme", __func__, wp->id);
break;
}
}
}

View File

@@ -49,6 +49,8 @@
" '#{?#{m/r:(copy|view)-mode,#{pane_mode}},Go To Top,}' '<' {send -X history-top}" \
" '#{?#{m/r:(copy|view)-mode,#{pane_mode}},Go To Bottom,}' '>' {send -X history-bottom}" \
" ''" \
" '#{?#{&&:#{buffer_size},#{!:#{pane_in_mode}}},Paste #[underscore]#{=/9/...:buffer_sample},}' 'p' {paste-buffer}" \
" ''" \
" '#{?mouse_word,Search For #[underscore]#{=/9/...:mouse_word},}' 'C-r' {if -F '#{?#{m/r:(copy|view)-mode,#{pane_mode}},0,1}' 'copy-mode -t='; send -Xt= search-backward -- \"#{q:mouse_word}\"}" \
" '#{?mouse_word,Type #[underscore]#{=/9/...:mouse_word},}' 'C-y' {copy-mode -q; send-keys -l -- \"#{q:mouse_word}\"}" \
" '#{?mouse_word,Copy #[underscore]#{=/9/...:mouse_word},}' 'c' {copy-mode -q; set-buffer -- \"#{q:mouse_word}\"}" \
@@ -213,6 +215,7 @@ key_bindings_add(const char *name, key_code key, const char *note, int repeat,
bd = xcalloc(1, sizeof *bd);
bd->key = (key & ~KEYC_MASK_FLAGS);
bd->tablename = table->name;
if (note != NULL)
bd->note = xstrdup(note);
RB_INSERT(key_bindings, &table->key_bindings, bd);
@@ -295,12 +298,12 @@ key_bindings_remove_table(const char *name)
table = key_bindings_get_table(name, 0);
if (table != NULL) {
RB_REMOVE(key_tables, &key_tables, table);
TAILQ_FOREACH(c, &clients, entry) {
if (c->keytable == table)
server_client_set_key_table(c, NULL);
}
key_bindings_unref_table(table);
}
TAILQ_FOREACH(c, &clients, entry) {
if (c->keytable == table)
server_client_set_key_table(c, NULL);
}
}
void
@@ -439,9 +442,10 @@ key_bindings_init(void)
/* Mouse button 1 down on pane. */
"bind -n MouseDown1Pane { select-pane -t=; send -M }",
"bind -n C-MouseDown1Pane { swap-pane -s@ }",
/* Mouse button 1 drag on pane. */
"bind -n MouseDrag1Pane { if -F '#{||:#{alternate_on},#{pane_in_mode},#{mouse_any_flag}}' { send -M } { copy-mode -M } }",
"bind -n MouseDrag1Pane { if -F '#{||:#{pane_in_mode},#{mouse_any_flag}}' { send -M } { copy-mode -M } }",
/* Mouse wheel up on pane. */
"bind -n WheelUpPane { if -F '#{||:#{alternate_on},#{pane_in_mode},#{mouse_any_flag}}' { send -M } { copy-mode -e } }",
@@ -460,6 +464,7 @@ key_bindings_init(void)
/* Mouse button 1 down on status line. */
"bind -n MouseDown1Status { switch-client -t= }",
"bind -n C-MouseDown1Status { swap-window -t@ }",
/* Mouse wheel down on status line. */
"bind -n WheelDownStatus { next-window }",
@@ -480,9 +485,9 @@ key_bindings_init(void)
"bind -n M-MouseDown3Pane { display-menu -t= -xM -yM -T '#[align=centre]#{pane_index} (#{pane_id})' " DEFAULT_PANE_MENU " }",
/* Mouse on scrollbar. */
"bind -n MouseDown1ScrollbarUp { copy-mode -u }",
"bind -n MouseDown1ScrollbarDown { copy-mode -d }",
"bind -n MouseDrag1ScrollbarSlider { copy-mode -S }",
"bind -n MouseDown1ScrollbarUp { if -Ft= '#{pane_in_mode}' { send -X page-up } {copy-mode -u } }",
"bind -n MouseDown1ScrollbarDown { if -Ft= '#{pane_in_mode}' { send -X page-down } {copy-mode -d } }",
"bind -n MouseDrag1ScrollbarSlider { if -Ft= '#{pane_in_mode}' { send -X scroll-to-mouse } { copy-mode -S } }",
/* Copy mode (emacs) keys. */
"bind -Tcopy-mode C-Space { send -X begin-selection }",
@@ -683,6 +688,7 @@ key_bindings_dispatch(struct key_binding *bd, struct cmdq_item *item,
readonly = 1;
else
readonly = cmd_list_all_have(bd->cmdlist, CMD_READONLY);
if (!readonly)
new_item = cmdq_get_callback(key_bindings_read_only, NULL);
else {
@@ -698,3 +704,15 @@ key_bindings_dispatch(struct key_binding *bd, struct cmdq_item *item,
new_item = cmdq_append(c, new_item);
return (new_item);
}
int
key_bindings_has_repeat(struct key_binding **l, u_int n)
{
u_int i;
for (i = 0; i < n; i++) {
if (l[i]->flags & KEY_BINDING_REPEAT)
return (1);
}
return (0);
}

View File

@@ -382,6 +382,14 @@ key_string_lookup_key(key_code key, int with_flags)
s = "PasteEnd";
goto append;
}
if (key == KEYC_REPORT_DARK_THEME) {
s = "ReportDarkTheme";
goto append;
}
if (key == KEYC_REPORT_LIGHT_THEME) {
s = "ReportLightTheme";
goto append;
}
if (key == KEYC_MOUSE) {
s = "Mouse";
goto append;

View File

@@ -1100,9 +1100,8 @@ int
layout_spread_cell(struct window *w, struct layout_cell *parent)
{
struct layout_cell *lc;
struct style *sb_style = &w->active->scrollbar_style;
u_int number, each, size, this, remainder;
int change, changed, status, scrollbars;
int change, changed, status;
number = 0;
TAILQ_FOREACH (lc, &parent->cells, entry)
@@ -1110,14 +1109,9 @@ layout_spread_cell(struct window *w, struct layout_cell *parent)
if (number <= 1)
return (0);
status = options_get_number(w->options, "pane-border-status");
scrollbars = options_get_number(w->options, "pane-scrollbars");
if (parent->type == LAYOUT_LEFTRIGHT) {
if (scrollbars)
size = parent->sx - sb_style->width + sb_style->pad;
else
size = parent->sx;
}
if (parent->type == LAYOUT_LEFTRIGHT)
size = parent->sx;
else if (parent->type == LAYOUT_TOPBOTTOM) {
if (layout_add_horizontal_border(w, parent, status))
size = parent->sy - 1;

115
menu.c
View File

@@ -27,13 +27,18 @@ struct menu_data {
struct cmdq_item *item;
int flags;
struct grid_cell style;
struct grid_cell border_style;
struct grid_cell selected_style;
char *style;
char *border_style;
char *selected_style;
struct grid_cell style_gc;
struct grid_cell border_style_gc;
struct grid_cell selected_style_gc;
enum box_lines border_lines;
struct cmd_find_state fs;
struct screen s;
struct visible_ranges r;
u_int px;
u_int py;
@@ -85,6 +90,7 @@ menu_add_item(struct menu *menu, const struct menu_item *item,
else
s = format_single(qitem, item->name, c, NULL, NULL, NULL);
if (*s == '\0') { /* no item if empty after format expanded */
free(s);
menu->count--;
return;
}
@@ -156,6 +162,9 @@ menu_free(struct menu *menu)
{
u_int i;
if (menu == NULL)
return;
for (i = 0; i < menu->count; i++) {
free((void *)menu->items[i].name);
free((void *)menu->items[i].command);
@@ -181,15 +190,70 @@ menu_mode_cb(__unused struct client *c, void *data, u_int *cx, u_int *cy)
}
/* Return parts of the input range which are not obstructed by the menu. */
void
struct visible_ranges *
menu_check_cb(__unused struct client *c, void *data, u_int px, u_int py,
u_int nx, struct overlay_ranges *r)
u_int nx)
{
struct menu_data *md = data;
struct menu *menu = md->menu;
server_client_overlay_range(md->px, md->py, menu->width + 4,
menu->count + 2, px, py, nx, r);
menu->count + 2, px, py, nx, &md->r);
return (&md->r);
}
static void
menu_reapply_styles(struct menu_data *md, struct client *c)
{
struct session *s = c->session;
struct options *o;
struct format_tree *ft;
struct style sytmp;
if (s == NULL)
return;
o = s->curw->window->options;
ft = format_create_defaults(NULL, c, s, s->curw, NULL);
/* Reapply menu style from options. */
memcpy(&md->style_gc, &grid_default_cell, sizeof md->style_gc);
style_apply(&md->style_gc, o, "menu-style", ft);
if (md->style != NULL) {
style_set(&sytmp, &grid_default_cell);
if (style_parse(&sytmp, &md->style_gc, md->style) == 0) {
md->style_gc.fg = sytmp.gc.fg;
md->style_gc.bg = sytmp.gc.bg;
}
}
/* Reapply selected style from options. */
memcpy(&md->selected_style_gc, &grid_default_cell,
sizeof md->selected_style_gc);
style_apply(&md->selected_style_gc, o, "menu-selected-style", ft);
if (md->selected_style != NULL) {
style_set(&sytmp, &grid_default_cell);
if (style_parse(&sytmp, &md->selected_style_gc,
md->selected_style) == 0) {
md->selected_style_gc.fg = sytmp.gc.fg;
md->selected_style_gc.bg = sytmp.gc.bg;
}
}
/* Reapply border style from options. */
memcpy(&md->border_style_gc, &grid_default_cell,
sizeof md->border_style_gc);
style_apply(&md->border_style_gc, o, "menu-border-style", ft);
if (md->border_style != NULL) {
style_set(&sytmp, &grid_default_cell);
if (style_parse(&sytmp, &md->border_style_gc,
md->border_style) == 0) {
md->border_style_gc.fg = sytmp.gc.fg;
md->border_style_gc.bg = sytmp.gc.bg;
}
}
format_free(ft);
}
void
@@ -203,16 +267,18 @@ menu_draw_cb(struct client *c, void *data,
struct screen_write_ctx ctx;
u_int i, px = md->px, py = md->py;
menu_reapply_styles(md, c);
screen_write_start(&ctx, s);
screen_write_clearscreen(&ctx, 8);
if (md->border_lines != BOX_LINES_NONE) {
screen_write_box(&ctx, menu->width + 4, menu->count + 2,
md->border_lines, &md->border_style, menu->title);
md->border_lines, &md->border_style_gc, menu->title);
}
screen_write_menu(&ctx, menu, md->choice, md->border_lines,
&md->style, &md->border_style, &md->selected_style);
&md->style_gc, &md->border_style_gc, &md->selected_style_gc);
screen_write_stop(&ctx);
for (i = 0; i < screen_size_y(&md->s); i++) {
@@ -232,8 +298,13 @@ menu_free_cb(__unused struct client *c, void *data)
if (md->cb != NULL)
md->cb(md->menu, UINT_MAX, KEYC_NONE, md->data);
free(md->r.ranges);
screen_free(&md->s);
menu_free(md->menu);
free(md->style);
free(md->selected_style);
free(md->border_style);
free(md);
}
@@ -470,24 +541,6 @@ menu_resize_cb(struct client *c, void *data)
md->py = ny;
}
static void
menu_set_style(struct client *c, struct grid_cell *gc, const char *style,
const char *option)
{
struct style sytmp;
struct options *o = c->session->curw->window->options;
memcpy(gc, &grid_default_cell, sizeof *gc);
style_apply(gc, o, option, NULL);
if (style != NULL) {
style_set(&sytmp, &grid_default_cell);
if (style_parse(&sytmp, gc, style) == 0) {
gc->fg = sytmp.gc.fg;
gc->bg = sytmp.gc.bg;
}
}
}
struct menu_data *
menu_prepare(struct menu *menu, int flags, int starting_choice,
struct cmdq_item *item, u_int px, u_int py, struct client *c,
@@ -515,10 +568,12 @@ menu_prepare(struct menu *menu, int flags, int starting_choice,
md->flags = flags;
md->border_lines = lines;
menu_set_style(c, &md->style, style, "menu-style");
menu_set_style(c, &md->selected_style, selected_style,
"menu-selected-style");
menu_set_style(c, &md->border_style, border_style, "menu-border-style");
if (style != NULL)
md->style = xstrdup(style);
if (selected_style != NULL)
md->selected_style = xstrdup(selected_style);
if (border_style != NULL)
md->border_style = xstrdup(border_style);
if (fs != NULL)
cmd_find_copy_state(&md->fs, fs);

View File

@@ -48,9 +48,7 @@ struct mode_tree_data {
void *modedata;
const struct menu_item *menu;
const char **sort_list;
u_int sort_size;
struct mode_tree_sort_criteria sort_crit;
struct sort_criteria sort_crit;
mode_tree_build_cb buildcb;
mode_tree_draw_cb drawcb;
@@ -59,6 +57,7 @@ struct mode_tree_data {
mode_tree_height_cb heightcb;
mode_tree_key_cb keycb;
mode_tree_swap_cb swapcb;
mode_tree_sort_cb sortcb;
struct mode_tree_list children;
struct mode_tree_list saved;
@@ -324,7 +323,7 @@ mode_tree_swap(struct mode_tree_data *mtd, int direction)
return;
if (mtd->swapcb(mtd->line_list[mtd->current].item->itemdata,
mtd->line_list[swap_with].item->itemdata)) {
mtd->line_list[swap_with].item->itemdata, &mtd->sort_crit)) {
mtd->current = swap_with;
mode_tree_build(mtd);
}
@@ -382,7 +381,7 @@ mode_tree_expand(struct mode_tree_data *mtd, uint64_t tag)
u_int found;
if (!mode_tree_get_tag(mtd, tag, &found))
return;
return;
if (!mtd->line_list[found].item->expanded) {
mtd->line_list[found].item->expanded = 1;
mode_tree_build(mtd);
@@ -454,12 +453,10 @@ mode_tree_start(struct window_pane *wp, struct args *args,
mode_tree_build_cb buildcb, mode_tree_draw_cb drawcb,
mode_tree_search_cb searchcb, mode_tree_menu_cb menucb,
mode_tree_height_cb heightcb, mode_tree_key_cb keycb,
mode_tree_swap_cb swapcb, void *modedata, const struct menu_item *menu,
const char **sort_list, u_int sort_size, struct screen **s)
mode_tree_swap_cb swapcb, mode_tree_sort_cb sortcb, void *modedata,
const struct menu_item *menu, struct screen **s)
{
struct mode_tree_data *mtd;
const char *sort;
u_int i;
mtd = xcalloc(1, sizeof *mtd);
mtd->references = 1;
@@ -468,9 +465,6 @@ mode_tree_start(struct window_pane *wp, struct args *args,
mtd->modedata = modedata;
mtd->menu = menu;
mtd->sort_list = sort_list;
mtd->sort_size = sort_size;
if (args_has(args, 'N') > 1)
mtd->preview = MODE_TREE_PREVIEW_BIG;
else if (args_has(args, 'N'))
@@ -478,13 +472,7 @@ mode_tree_start(struct window_pane *wp, struct args *args,
else
mtd->preview = MODE_TREE_PREVIEW_NORMAL;
sort = args_get(args, 'O');
if (sort != NULL) {
for (i = 0; i < sort_size; i++) {
if (strcasecmp(sort, sort_list[i]) == 0)
mtd->sort_crit.field = i;
}
}
mtd->sort_crit.order = sort_order_from_string(args_get(args, 'O'));
mtd->sort_crit.reversed = args_has(args, 'r');
if (args_has(args, 'f'))
@@ -499,6 +487,7 @@ mode_tree_start(struct window_pane *wp, struct args *args,
mtd->heightcb = heightcb;
mtd->keycb = keycb;
mtd->swapcb = swapcb;
mtd->sortcb = sortcb;
TAILQ_INIT(&mtd->children);
@@ -566,6 +555,8 @@ mode_tree_build(struct mode_tree_data *mtd)
TAILQ_CONCAT(&mtd->saved, &mtd->children, entry);
TAILQ_INIT(&mtd->children);
if (mtd->sortcb != NULL)
mtd->sortcb(&mtd->sort_crit);
mtd->buildcb(mtd->modedata, &mtd->sort_crit, &tag, mtd->filter);
mtd->no_matches = TAILQ_EMPTY(&mtd->children);
if (mtd->no_matches)
@@ -851,9 +842,9 @@ mode_tree_draw(struct mode_tree_data *mtd)
screen_write_cursormove(&ctx, 0, h, 0);
screen_write_box(&ctx, w, sy - h, BOX_LINES_DEFAULT, NULL, NULL);
if (mtd->sort_list != NULL) {
if (mtd->sort_crit.order_seq != NULL) {
xasprintf(&text, " %s (sort: %s%s)", mti->name,
mtd->sort_list[mtd->sort_crit.field],
sort_order_to_string(mtd->sort_crit.order),
mtd->sort_crit.reversed ? ", reversed" : "");
} else
xasprintf(&text, " %s", mti->name);
@@ -1287,9 +1278,7 @@ mode_tree_key(struct mode_tree_data *mtd, struct client *c, key_code *key,
}
break;
case 'O':
mtd->sort_crit.field++;
if (mtd->sort_crit.field >= mtd->sort_size)
mtd->sort_crit.field = 0;
sort_next_order(&mtd->sort_crit);
mode_tree_build(mtd);
break;
case 'r':

View File

@@ -84,6 +84,9 @@ static const char *options_table_popup_border_lines_list[] = {
static const char *options_table_set_clipboard_list[] = {
"off", "external", "on", NULL
};
static const char *options_table_get_clipboard_list[] = {
"off", "buffer", "request", "both", NULL
};
static const char *options_table_window_size_list[] = {
"largest", "smallest", "manual", "latest", NULL
};
@@ -405,6 +408,18 @@ const struct options_table_entry options_table[] = {
.text = "Whether to send focus events to applications."
},
{ .name = "get-clipboard",
.type = OPTIONS_TABLE_CHOICE,
.scope = OPTIONS_TABLE_SERVER,
.choices = options_table_get_clipboard_list,
.default_num = 1,
.text = "When an application requests the clipboard, whether to "
"ignore the request ('off'); respond with the newest buffer "
"('buffer'); request the clipboard from the most recently "
"used terminal ('request'); or to request the clipboard, "
"create a buffer, and send it to the application ('both')."
},
{ .name = "history-file",
.type = OPTIONS_TABLE_STRING,
.scope = OPTIONS_TABLE_SERVER,
@@ -649,6 +664,13 @@ const struct options_table_entry options_table[] = {
.text = "Time for which status line messages should appear."
},
{ .name = "focus-follows-mouse",
.type = OPTIONS_TABLE_FLAG,
.scope = OPTIONS_TABLE_SESSION,
.default_num = 0,
.text = "Whether moving the mouse into a pane selects it."
},
{ .name = "history-limit",
.type = OPTIONS_TABLE_NUMBER,
.scope = OPTIONS_TABLE_SESSION,
@@ -943,7 +965,7 @@ const struct options_table_entry options_table[] = {
{ .name = "prompt-cursor-colour",
.type = OPTIONS_TABLE_COLOUR,
.scope = OPTIONS_TABLE_SESSION,
.default_num = 6,
.default_num = -1,
.text = "Colour of the cursor when in the command prompt."
},
@@ -955,6 +977,15 @@ const struct options_table_entry options_table[] = {
.text = "Style of the cursor when in the command prompt."
},
{ .name = "prompt-command-cursor-style",
.type = OPTIONS_TABLE_CHOICE,
.scope = OPTIONS_TABLE_SESSION,
.choices = options_table_cursor_style_list,
.default_num = 0,
.text = "Style of the cursor in the command prompt when in command "
"mode, if 'status-keys' is set to 'vi'."
},
{ .name = "session-status-current-style",
.type = OPTIONS_TABLE_STRING,
.scope = OPTIONS_TABLE_WINDOW,

View File

@@ -749,7 +749,7 @@ options_get_number(struct options *oo, const char *name)
return (o->value.number);
}
const struct cmd_list *
struct cmd_list *
options_get_command(struct options *oo, const char *name)
{
struct options_entry *o;
@@ -1226,6 +1226,10 @@ options_push_changes(const char *name)
RB_FOREACH(wp, window_pane_tree, &all_window_panes)
wp->flags |= (PANE_STYLECHANGED|PANE_THEMECHANGED);
}
if (*name == '@') {
RB_FOREACH(wp, window_pane_tree, &all_window_panes)
wp->flags |= PANE_STYLECHANGED;
}
if (strcmp(name, "pane-colours") == 0) {
RB_FOREACH(wp, window_pane_tree, &all_window_panes)
colour_palette_from_option(&wp->palette, wp->options);
@@ -1248,6 +1252,10 @@ options_push_changes(const char *name)
utf8_update_width_cache();
if (strcmp(name, "input-buffer-size") == 0)
input_set_buffer_size(options_get_number(global_options, name));
if (strcmp(name, "history-limit") == 0) {
RB_FOREACH(s, sessions, &sessions)
session_update_history(s);
}
RB_FOREACH(s, sessions, &sessions)
status_update_cache(s);

17
paste.c
View File

@@ -29,19 +29,6 @@
* string!
*/
struct paste_buffer {
char *data;
size_t size;
char *name;
time_t created;
int automatic;
u_int order;
RB_ENTRY(paste_buffer) name_entry;
RB_ENTRY(paste_buffer) time_entry;
};
static u_int paste_next_index;
static u_int paste_next_order;
static u_int paste_num_automatic;
@@ -119,7 +106,7 @@ paste_is_empty(void)
/* Get the most recent automatic buffer. */
struct paste_buffer *
paste_get_top(const char **name)
paste_get_top(char **name)
{
struct paste_buffer *pb;
@@ -129,7 +116,7 @@ paste_get_top(const char **name)
if (pb == NULL)
return (NULL);
if (name != NULL)
*name = pb->name;
*name = xstrdup(pb->name);
return (pb);
}

132
popup.c
View File

@@ -32,6 +32,8 @@ struct popup_data {
int flags;
char *title;
char *style;
char *border_style;
struct grid_cell border_cell;
enum box_lines border_lines;
@@ -39,6 +41,9 @@ struct popup_data {
struct grid_cell defaults;
struct colour_palette palette;
struct visible_ranges r;
struct visible_ranges or[2];
struct job *job;
struct input_ctx *ictx;
int status;
@@ -98,6 +103,49 @@ static const struct menu_item popup_internal_menu_items[] = {
{ NULL, KEYC_NONE, NULL }
};
static void
popup_reapply_styles(struct popup_data *pd)
{
struct client *c = pd->c;
struct session *s = c->session;
struct options *o;
struct format_tree *ft;
struct style sytmp;
if (s == NULL)
return;
o = s->curw->window->options;
ft = format_create_defaults(NULL, c, s, s->curw, NULL);
/* Reapply popup style from options. */
memcpy(&pd->defaults, &grid_default_cell, sizeof pd->defaults);
style_apply(&pd->defaults, o, "popup-style", ft);
if (pd->style != NULL) {
style_set(&sytmp, &grid_default_cell);
if (style_parse(&sytmp, &pd->defaults, pd->style) == 0) {
pd->defaults.fg = sytmp.gc.fg;
pd->defaults.bg = sytmp.gc.bg;
}
}
pd->defaults.attr = 0;
/* Reapply border style from options. */
memcpy(&pd->border_cell, &grid_default_cell, sizeof pd->border_cell);
style_apply(&pd->border_cell, o, "popup-border-style", ft);
if (pd->border_style != NULL) {
style_set(&sytmp, &grid_default_cell);
if (style_parse(&sytmp, &pd->border_cell,
pd->border_style) == 0) {
pd->border_cell.fg = sytmp.gc.fg;
pd->border_cell.bg = sytmp.gc.bg;
}
}
pd->border_cell.attr = 0;
format_free(ft);
}
static void
popup_redraw_cb(const struct tty_ctx *ttyctx)
{
@@ -164,48 +212,57 @@ popup_mode_cb(__unused struct client *c, void *data, u_int *cx, u_int *cy)
}
/* Return parts of the input range which are not obstructed by the popup. */
static void
popup_check_cb(struct client* c, void *data, u_int px, u_int py, u_int nx,
struct overlay_ranges *r)
static struct visible_ranges *
popup_check_cb(struct client* c, void *data, u_int px, u_int py, u_int nx)
{
struct popup_data *pd = data;
struct overlay_ranges or[2];
struct visible_ranges *r = &pd->r;
struct visible_ranges *mr;
u_int i, j, k = 0;
if (pd->md != NULL) {
/* Check each returned range for the menu against the popup. */
menu_check_cb(c, pd->md, px, py, nx, r);
for (i = 0; i < 2; i++) {
/*
* Work out the visible ranges for the menu (that is, the
* ranges not covered by the menu). A menu should have at most
* two ranges and we rely on this being the case.
*/
mr = menu_check_cb(c, pd->md, px, py, nx);
if (mr->used > 2)
fatalx("too many menu ranges");
/*
* Walk the ranges still visible under the menu and check if
* each is visible under the popup as well. At most there can be
* three total ranges if popup and menu do not intersect.
*/
for (i = 0; i < mr->used; i++) {
server_client_overlay_range(pd->px, pd->py, pd->sx,
pd->sy, r->px[i], py, r->nx[i], &or[i]);
pd->sy, r->ranges[i].px, py, r->ranges[i].nx,
&pd->or[i]);
}
/*
* or has up to OVERLAY_MAX_RANGES non-overlapping ranges,
* ordered from left to right. Collect them in the output.
* We now have nonoverlapping ranges from left to right.
* Combine them together into the output.
*/
for (i = 0; i < 2; i++) {
/* Each or[i] only has 2 ranges. */
for (j = 0; j < 2; j++) {
if (or[i].nx[j] > 0) {
r->px[k] = or[i].px[j];
r->nx[k] = or[i].nx[j];
k++;
}
server_client_ensure_ranges(r, 3);
for (i = 0; i < mr->used; i++) {
for (j = 0; j < pd->or[i].used; j++) {
if (pd->or[i].ranges[j].nx == 0)
continue;
if (k >= 3)
fatalx("too many popup & menu ranges");
r->ranges[k].px = pd->or[i].ranges[j].px;
r->ranges[k].nx = pd->or[i].ranges[j].nx;
k++;
}
}
/* Zero remaining ranges if any. */
for (i = k; i < OVERLAY_MAX_RANGES; i++) {
r->px[i] = 0;
r->nx[i] = 0;
}
return;
return (r);
}
server_client_overlay_range(pd->px, pd->py, pd->sx, pd->sy, px, py, nx,
r);
return (r);
}
static void
@@ -219,6 +276,8 @@ popup_draw_cb(struct client *c, void *data, struct screen_redraw_ctx *rctx)
struct colour_palette *palette = &pd->palette;
struct grid_cell defaults;
popup_reapply_styles(pd);
screen_init(&s, pd->sx, pd->sy, 0);
screen_write_start(&ctx, &s);
screen_write_clearscreen(&ctx, 8);
@@ -286,10 +345,15 @@ popup_free_cb(struct client *c, void *data)
job_free(pd->job);
input_free(pd->ictx);
free(pd->or[0].ranges);
free(pd->or[1].ranges);
free(pd->r.ranges);
screen_free(&pd->s);
colour_palette_free(&pd->palette);
free(pd->title);
free(pd->style);
free(pd->border_style);
free(pd);
}
@@ -549,7 +613,7 @@ popup_key_cb(struct client *c, void *data, struct key_event *event)
(event->key == '\033' || event->key == ('c'|KEYC_CTRL)))
return (1);
if (pd->job == NULL && (pd->flags & POPUP_CLOSEANYKEY) &&
!KEYC_IS_MOUSE(event->key) && !KEYC_IS_PASTE(event->key))
!KEYC_IS_MOUSE(event->key) && !KEYC_IS_PASTE(event->key))
return (1);
if (pd->job != NULL) {
if (KEYC_IS_MOUSE(event->key)) {
@@ -658,6 +722,8 @@ popup_modify(struct client *c, const char *title, const char *style,
pd->title = xstrdup(title);
}
if (border_style != NULL) {
free(pd->border_style);
pd->border_style = xstrdup(border_style);
style_set(&sytmp, &pd->border_cell);
if (style_parse(&sytmp, &pd->border_cell, border_style) == 0) {
pd->border_cell.fg = sytmp.gc.fg;
@@ -665,6 +731,8 @@ popup_modify(struct client *c, const char *title, const char *style,
}
}
if (style != NULL) {
free(pd->style);
pd->style = xstrdup(style);
style_set(&sytmp, &pd->defaults);
if (style_parse(&sytmp, &pd->defaults, style) == 0) {
pd->defaults.fg = sytmp.gc.fg;
@@ -675,7 +743,8 @@ popup_modify(struct client *c, const char *title, const char *style,
if (lines == BOX_LINES_NONE && pd->border_lines != lines) {
screen_resize(&pd->s, pd->sx, pd->sy, 1);
job_resize(pd->job, pd->sx, pd->sy);
} else if (pd->border_lines == BOX_LINES_NONE && pd->border_lines != lines) {
} else if (pd->border_lines == BOX_LINES_NONE &&
pd->border_lines != lines) {
screen_resize(&pd->s, pd->sx - 2, pd->sy - 2, 1);
job_resize(pd->job, pd->sx - 2, pd->sy - 2);
}
@@ -725,8 +794,13 @@ popup_display(int flags, enum box_lines lines, struct cmdq_item *item, u_int px,
pd = xcalloc(1, sizeof *pd);
pd->item = item;
pd->flags = flags;
if (title != NULL)
pd->title = xstrdup(title);
if (style != NULL)
pd->style = xstrdup(style);
if (border_style != NULL)
pd->border_style = xstrdup(border_style);
pd->c = c;
pd->c->references++;
@@ -776,7 +850,7 @@ popup_display(int flags, enum box_lines lines, struct cmdq_item *item, u_int px,
pd->job = job_run(shellcmd, argc, argv, env, s, cwd,
popup_job_update_cb, popup_job_complete_cb, NULL, pd,
JOB_NOWAIT|JOB_PTY|JOB_KEEPWRITE|JOB_DEFAULTSHELL, jx, jy);
pd->ictx = input_init(NULL, job_get_event(pd->job), &pd->palette);
pd->ictx = input_init(NULL, job_get_event(pd->job), &pd->palette, c);
server_client_set_overlay(c, 0, popup_check_cb, popup_mode_cb,
popup_draw_cb, popup_key_cb, popup_free_cb, popup_resize_cb, pd);

52
regress/border-arrows.sh Normal file
View File

@@ -0,0 +1,52 @@
#!/bin/sh
# Test for GitHub issue #4780 - pane-border-indicators both arrows missing
# on second pane in a two-pane horizontal split.
#
# When pane-border-indicators is set to "both", arrow indicators should
# appear when EITHER pane is selected. Before the fix, arrows only appeared
# when the LEFT pane was selected.
PATH=/bin:/usr/bin
TERM=screen
[ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux)
TMUX="$TEST_TMUX -Ltest"
$TMUX kill-server 2>/dev/null
TMUX_OUTER="$TEST_TMUX -Ltest2"
$TMUX_OUTER kill-server 2>/dev/null
trap "$TMUX kill-server 2>/dev/null; $TMUX_OUTER kill-server 2>/dev/null" 0 1 15
# Start outer tmux that will capture the inner tmux's rendering
$TMUX_OUTER -f/dev/null new -d -x80 -y24 "$TMUX -f/dev/null new -x78 -y22" || exit 1
sleep 1
# Set pane-border-indicators to "both" in inner tmux
$TMUX set -g pane-border-indicators both || exit 1
# Create horizontal split (two panes side by side)
$TMUX splitw -h || exit 1
sleep 1
# Helper to check for arrow characters in captured output
has_arrow() {
echo "$1" | grep -qE '(←|→|↑|↓)'
}
# Test 1: Select left pane (pane 0) and check for arrows
$TMUX selectp -t 0
sleep 1
left_output=$($TMUX_OUTER capturep -Cep 2>/dev/null)
has_arrow "$left_output" || exit 1
# Test 2: Select right pane (pane 1) and check for arrows
# This is the case that failed before the fix
$TMUX selectp -t 1
sleep 1
right_output=$($TMUX_OUTER capturep -Cep 2>/dev/null)
has_arrow "$right_output" || exit 1
$TMUX kill-server 2>/dev/null
$TMUX_OUTER kill-server 2>/dev/null
exit 0

82
regress/decrqm-sync.sh Normal file
View File

@@ -0,0 +1,82 @@
#!/bin/sh
# Test DECRPM response for mode 2026 (synchronized output).
#
# DECRQM (ESC[?2026$p) should elicit DECRPM (ESC[?2026;Ps$y) where
# Ps=1 when MODE_SYNC is active, Ps=2 when reset.
PATH=/bin:/usr/bin
TERM=screen
[ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux)
TMUX="$TEST_TMUX -Ltest"
$TMUX kill-server 2>/dev/null
sleep 1
TMP=$(mktemp)
TMP2=$(mktemp)
trap "rm -f $TMP $TMP2; $TMUX kill-server 2>/dev/null" 0 1 15
$TMUX -f/dev/null new -d -x80 -y24 || exit 1
sleep 1
# Keep the session alive regardless of pane exits.
$TMUX set -g remain-on-exit on
exit_status=0
# query_decrpm <outfile> [setup_seq]
# Spawn a pane that optionally sends setup_seq, then sends DECRQM for
# mode 2026 and captures the response into outfile in cat -v form.
query_decrpm () {
_outfile=$1
_setup=$2
$TMUX respawnw -k -t:0 -- sh -c "
exec 2>/dev/null
stty raw -echo
${_setup:+printf '$_setup'; sleep 0.2}
printf '\033[?2026\$p'
dd bs=1 count=11 2>/dev/null | cat -v > $_outfile
sleep 0.2
" || exit 1
sleep 2
}
# ------------------------------------------------------------------
# Test 1: mode 2026 should be reset by default (Ps=2)
# ------------------------------------------------------------------
query_decrpm "$TMP"
actual=$(cat "$TMP")
expected='^[[?2026;2$y'
if [ "$actual" = "$expected" ]; then
if [ -n "$VERBOSE" ]; then
echo "[PASS] DECRQM 2026 (default/reset) -> $actual"
fi
else
echo "[FAIL] DECRQM 2026 (default/reset): expected '$expected', got '$actual'"
exit_status=1
fi
# ------------------------------------------------------------------
# Test 2: set mode 2026 (SM ?2026), then query (expect Ps=1)
# ------------------------------------------------------------------
query_decrpm "$TMP2" '\033[?2026h'
actual=$(cat "$TMP2")
expected='^[[?2026;1$y'
if [ "$actual" = "$expected" ]; then
if [ -n "$VERBOSE" ]; then
echo "[PASS] DECRQM 2026 (set) -> $actual"
fi
else
echo "[FAIL] DECRQM 2026 (set): expected '$expected', got '$actual'"
exit_status=1
fi
$TMUX kill-server 2>/dev/null
exit $exit_status

70
regress/session-group-resize.sh Executable file
View File

@@ -0,0 +1,70 @@
#!/bin/sh
# Test that window-size=latest resizes windows correctly when switching
# windows in session groups. When a client switches to a window, it should
# resize immediately to match that client's size.
#
# Tests both switch-client and select-window, which use different code paths.
PATH=/bin:/usr/bin
TERM=screen
[ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux)
TMUX="$TEST_TMUX -Ltest"
$TMUX kill-server 2>/dev/null
TMP1=$(mktemp)
TMP2=$(mktemp)
TMP3=$(mktemp)
trap "rm -f $TMP1 $TMP2 $TMP3" 0 1 15
# Create a session with two windows, staying on window 0.
$TMUX -f/dev/null new -d -s test -x 20 -y 6 || exit 1
$TMUX neww -t test || exit 1
$TMUX selectw -t test:0 || exit 1
# Attach a small 20x6 client in control-mode and have it select window 1. This makes
# the small client the "latest" for window 1. The sleep keeps stdin open so the
# control client stays attached.
(echo "refresh-client -C 20,6"; echo "selectw -t :1"; sleep 5) |
$TMUX -f/dev/null -C attach -t test >$TMP1 2>&1 &
# Wait for small client to be on window 1.
n=0
while [ $n -lt 20 ]; do
$TMUX lsc -F '#{client_name} #{window_index}' 2>/dev/null | grep -q " 1$" && break
sleep 0.1
n=$((n + 1))
done
# Create a grouped session with a larger 30x10 client, also in control mode. It
# starts on window 0 (inherited), then switches to window 1 with
# `switch-client`.
(echo "refresh-client -C 30,10"; echo "switch-client -t :=1"; sleep 5) |
$TMUX -f/dev/null -C new -t test -x 30 -y 10 >$TMP2 2>&1 &
# Wait briefly for the switch-client command to execute, then check.
# The resize should happen immediately (within 0.2s).
sleep 0.2
OUT1=$($TMUX display -t test:1 -p '#{window_width}x#{window_height}' 2>/dev/null)
# Also test selectw (select-window) which uses a different code path.
# Create a third grouped session with a 25x8 client, switch to window 1
# using selectw instead of switch-client.
(echo "refresh-client -C 25,8"; echo "selectw -t :1"; sleep 5) |
$TMUX -f/dev/null -C new -t test -x 25 -y 8 >$TMP3 2>&1 &
sleep 0.2
OUT2=$($TMUX display -t test:1 -p '#{window_width}x#{window_height}' 2>/dev/null)
# Clean up - kill server (terminates clients). Don't wait for background
# sleeps; they'll be orphaned but harmless.
$TMUX kill-server 2>/dev/null
# Window 1 should have resized to 30x10 (the second client's size).
[ "$OUT1" = "30x10" ] || { echo "switch-client resize failed: $OUT1"; exit 1; }
# Window 1 should have resized to 25x8 (the third client's size).
[ "$OUT2" = "25x8" ] || { echo "selectw resize failed: $OUT2"; exit 1; }
exit 0

View File

@@ -127,7 +127,7 @@ clients_calculate_size(int type, int current, struct client *c,
if (type == WINDOW_SIZE_LARGEST) {
*sx = 0;
*sy = 0;
} else if (type == WINDOW_SIZE_MANUAL) {
} else if (w != NULL && type == WINDOW_SIZE_MANUAL) {
*sx = w->manual_sx;
*sy = w->manual_sy;
log_debug("%s: manual size %ux%u", __func__, *sx, *sy);
@@ -250,7 +250,7 @@ skip:
/* Return whether a suitable size was found. */
if (type == WINDOW_SIZE_MANUAL) {
log_debug("%s: type is manual", __func__);
return (1);
return (w != NULL);
}
if (type == WINDOW_SIZE_LARGEST) {
log_debug("%s: type is largest", __func__);

View File

@@ -165,7 +165,7 @@ screen_redraw_pane_border(struct screen_redraw_ctx *ctx, struct window_pane *wp,
if (px == wp->xoff + wp->sx + sb_w - 1)
return (SCREEN_REDRAW_BORDER_RIGHT);
}
} else { /* sb_pos == PANE_SCROLLBARS_RIGHT or disabled*/
} else { /* sb_pos == PANE_SCROLLBARS_RIGHT or disabled */
if (wp->xoff == 0 && px == wp->sx + sb_w)
if (!hsplit || (hsplit && py <= wp->sy / 2))
return (SCREEN_REDRAW_BORDER_RIGHT);
@@ -197,9 +197,11 @@ screen_redraw_pane_border(struct screen_redraw_ctx *ctx, struct window_pane *wp,
} else { /* sb_pos == PANE_SCROLLBARS_RIGHT */
if ((wp->xoff == 0 || px >= wp->xoff) &&
(px <= ex || (sb_w != 0 && px < ex + sb_w))) {
if (wp->yoff != 0 && py == wp->yoff - 1)
if (pane_status != PANE_STATUS_BOTTOM &&
wp->yoff != 0 &&
py == wp->yoff - 1)
return (SCREEN_REDRAW_BORDER_TOP);
if (py == ey)
if (pane_status != PANE_STATUS_TOP && py == ey)
return (SCREEN_REDRAW_BORDER_BOTTOM);
}
}
@@ -380,7 +382,6 @@ screen_redraw_check_cell(struct screen_redraw_ctx *ctx, u_int px, u_int py,
/* Check if CELL_SCROLLBAR */
if (window_pane_show_scrollbar(wp, pane_scrollbars)) {
if (pane_status == PANE_STATUS_TOP)
line = wp->yoff - 1;
else
@@ -666,6 +667,16 @@ screen_redraw_screen(struct client *c)
}
if (flags & CLIENT_REDRAWWINDOW) {
log_debug("%s: redrawing panes", c->name);
#ifdef ENABLE_KITTY_IMAGES
/*
* Delete all kitty image placements before redrawing panes.
* This must happen unconditionally — even when the new window
* has no images — so that images from the previous window
* (or from a `reset` in the shell) are cleared from the outer
* terminal before new content is drawn over them.
*/
tty_kitty_delete_all(&c->tty);
#endif
screen_redraw_draw_panes(&ctx);
screen_redraw_draw_pane_scrollbars(&ctx);
}
@@ -696,8 +707,12 @@ screen_redraw_pane(struct client *c, struct window_pane *wp,
tty_sync_start(&c->tty);
tty_update_mode(&c->tty, c->tty.mode, NULL);
if (!redraw_scrollbar_only)
if (!redraw_scrollbar_only) {
#ifdef ENABLE_KITTY_IMAGES
tty_kitty_delete_all(&c->tty);
#endif
screen_redraw_draw_pane(&ctx, wp);
}
if (window_pane_show_scrollbar(wp, ctx.pane_scrollbars))
screen_redraw_draw_pane_scrollbar(&ctx, wp);
@@ -731,6 +746,78 @@ screen_redraw_draw_borders_style(struct screen_redraw_ctx *ctx, u_int x,
return (&wp->border_gc);
}
/* Draw arrow indicator if enabled. */
static void
screen_redraw_draw_border_arrows(struct screen_redraw_ctx *ctx, u_int i,
u_int j, u_int cell_type, struct window_pane *wp,
struct window_pane *active, struct grid_cell *gc)
{
struct client *c = ctx->c;
struct session *s = c->session;
struct window *w = s->curw->window;
struct options *oo = w->options;
u_int x = ctx->ox + i, y = ctx->oy + j;
int value, arrows = 0, border;
if (wp == NULL)
return;
if (i != wp->xoff + 1 && j != wp->yoff + 1)
return;
value = options_get_number(oo, "pane-border-indicators");
if (value != PANE_BORDER_ARROWS && value != PANE_BORDER_BOTH)
return;
border = screen_redraw_pane_border(ctx, active, x, y);
if (border == SCREEN_REDRAW_INSIDE)
return;
if (i == wp->xoff + 1) {
if (border == SCREEN_REDRAW_OUTSIDE) {
if (screen_redraw_two_panes(wp->window, 1)) {
if (active == TAILQ_FIRST(&w->panes))
border = SCREEN_REDRAW_BORDER_BOTTOM;
else
border = SCREEN_REDRAW_BORDER_TOP;
arrows = 1;
}
} else {
if (cell_type == CELL_LEFTRIGHT)
arrows = 1;
else if (cell_type == CELL_TOPJOIN &&
border == SCREEN_REDRAW_BORDER_BOTTOM)
arrows = 1;
else if (cell_type == CELL_BOTTOMJOIN &&
border == SCREEN_REDRAW_BORDER_TOP)
arrows = 1;
}
}
if (j == wp->yoff + 1) {
if (border == SCREEN_REDRAW_OUTSIDE) {
if (screen_redraw_two_panes(wp->window, 0)) {
if (active == TAILQ_FIRST(&w->panes))
border = SCREEN_REDRAW_BORDER_RIGHT;
else
border = SCREEN_REDRAW_BORDER_LEFT;
arrows = 1;
}
} else {
if (cell_type == CELL_TOPBOTTOM)
arrows = 1;
else if (cell_type == CELL_LEFTJOIN &&
border == SCREEN_REDRAW_BORDER_RIGHT)
arrows = 1;
else if (cell_type == CELL_RIGHTJOIN &&
border == SCREEN_REDRAW_BORDER_LEFT)
arrows = 1;
}
}
if (arrows) {
gc->attr |= GRID_ATTR_CHARSET;
utf8_set(&gc->data, BORDER_MARKERS[border]);
}
}
/* Draw a border cell. */
static void
screen_redraw_draw_borders_cell(struct screen_redraw_ctx *ctx, u_int i, u_int j)
@@ -744,13 +831,14 @@ screen_redraw_draw_borders_cell(struct screen_redraw_ctx *ctx, u_int i, u_int j)
struct window_pane *wp, *active = server_client_get_pane(c);
struct grid_cell gc;
const struct grid_cell *tmp;
struct overlay_ranges r;
u_int cell_type, x = ctx->ox + i, y = ctx->oy + j;
int arrows = 0, border, isolates;
u_int cell_type;
u_int x = ctx->ox + i, y = ctx->oy + j;
int isolates;
struct visible_ranges *r;
if (c->overlay_check != NULL) {
c->overlay_check(c, c->overlay_data, x, y, 1, &r);
if (r.nx[0] + r.nx[1] == 0)
r = c->overlay_check(c, c->overlay_data, x, y, 1);
if (server_client_ranges_is_empty(r))
return;
}
@@ -794,32 +882,7 @@ screen_redraw_draw_borders_cell(struct screen_redraw_ctx *ctx, u_int i, u_int j)
if (isolates)
tty_puts(tty, END_ISOLATE);
switch (options_get_number(oo, "pane-border-indicators")) {
case PANE_BORDER_ARROWS:
case PANE_BORDER_BOTH:
arrows = 1;
break;
}
if (wp != NULL && arrows) {
border = screen_redraw_pane_border(ctx, active, x, y);
if (((i == wp->xoff + 1 &&
(cell_type == CELL_LEFTRIGHT ||
(cell_type == CELL_TOPJOIN &&
border == SCREEN_REDRAW_BORDER_BOTTOM) ||
(cell_type == CELL_BOTTOMJOIN &&
border == SCREEN_REDRAW_BORDER_TOP))) ||
(j == wp->yoff + 1 &&
(cell_type == CELL_TOPBOTTOM ||
(cell_type == CELL_LEFTJOIN &&
border == SCREEN_REDRAW_BORDER_RIGHT) ||
(cell_type == CELL_RIGHTJOIN &&
border == SCREEN_REDRAW_BORDER_LEFT)))) &&
screen_redraw_check_is(ctx, x, y, active)) {
gc.attr |= GRID_ATTR_CHARSET;
utf8_set(&gc.data, BORDER_MARKERS[border]);
}
}
screen_redraw_draw_border_arrows(ctx, i, j, cell_type, wp, active, &gc);
tty_cell(tty, &gc, &grid_default_cell, NULL, NULL);
if (isolates)
@@ -895,7 +958,12 @@ screen_redraw_draw_pane(struct screen_redraw_ctx *ctx, struct window_pane *wp)
struct screen *s = wp->screen;
struct colour_palette *palette = &wp->palette;
struct grid_cell defaults;
u_int i, j, top, x, y, width;
struct visible_ranges *r;
struct visible_range *rr;
u_int i, j, k, top, x, y, width;
if (wp->base.mode & MODE_SYNC)
screen_write_stop_sync(wp);
log_debug("%s: %s @%u %%%u", __func__, c->name, w->id, wp->id);
@@ -937,10 +1005,18 @@ screen_redraw_draw_pane(struct screen_redraw_ctx *ctx, struct window_pane *wp)
__func__, c->name, wp->id, i, j, x, y, width);
tty_default_colours(&defaults, wp);
tty_draw_line(tty, s, i, j, width, x, y, &defaults, palette);
r = tty_check_overlay_range(tty, x, y, width);
for (k = 0; k < r->used; k++) {
rr = &r->ranges[k];
if (rr->nx != 0) {
tty_draw_line(tty, s, rr->px - wp->xoff, j,
rr->nx, rr->px, y, &defaults, palette);
}
}
}
#ifdef ENABLE_SIXEL
#ifdef ENABLE_SIXEL_IMAGES
tty_draw_images(c, wp, s);
#endif
}
@@ -1028,6 +1104,11 @@ screen_redraw_draw_scrollbar(struct screen_redraw_ctx *ctx,
int sx = ctx->sx, sy = ctx->sy, xoff = wp->xoff;
int yoff = wp->yoff;
if (ctx->statustop) {
sb_y += ctx->statuslines;
sy += ctx->statuslines;
}
/* Set up style for slider. */
gc = sb_style->gc;
memcpy(&slgc, &gc, sizeof slgc);

View File

@@ -25,6 +25,8 @@
static struct screen_write_citem *screen_write_collect_trim(
struct screen_write_ctx *, u_int, u_int, u_int, int *);
static void screen_write_collect_insert(struct screen_write_ctx *,
struct screen_write_citem *);
static void screen_write_collect_clear(struct screen_write_ctx *, u_int,
u_int);
static void screen_write_collect_scroll(struct screen_write_ctx *, u_int);
@@ -61,9 +63,9 @@ screen_write_get_citem(void)
ci = TAILQ_FIRST(&screen_write_citem_freelist);
if (ci != NULL) {
TAILQ_REMOVE(&screen_write_citem_freelist, ci, entry);
memset(ci, 0, sizeof *ci);
return (ci);
TAILQ_REMOVE(&screen_write_citem_freelist, ci, entry);
memset(ci, 0, sizeof *ci);
return (ci);
}
return (xcalloc(1, sizeof *ci));
}
@@ -71,7 +73,7 @@ screen_write_get_citem(void)
static void
screen_write_free_citem(struct screen_write_citem *ci)
{
TAILQ_INSERT_TAIL(&screen_write_citem_freelist, ci, entry);
TAILQ_INSERT_TAIL(&screen_write_citem_freelist, ci, entry);
}
static void
@@ -894,6 +896,52 @@ screen_write_mode_clear(struct screen_write_ctx *ctx, int mode)
log_debug("%s: %s", __func__, screen_mode_to_string(mode));
}
/* Sync timeout callback. */
static void
screen_write_sync_callback(__unused int fd, __unused short events, void *arg)
{
struct window_pane *wp = arg;
log_debug("%s: %%%u sync timer expired", __func__, wp->id);
evtimer_del(&wp->sync_timer);
if (wp->base.mode & MODE_SYNC) {
wp->base.mode &= ~MODE_SYNC;
wp->flags |= PANE_REDRAW;
}
}
/* Start sync mode. */
void
screen_write_start_sync(struct window_pane *wp)
{
struct timeval tv = { .tv_sec = 1, .tv_usec = 0 };
if (wp == NULL)
return;
wp->base.mode |= MODE_SYNC;
if (!event_initialized(&wp->sync_timer))
evtimer_set(&wp->sync_timer, screen_write_sync_callback, wp);
evtimer_add(&wp->sync_timer, &tv);
log_debug("%s: %%%u started sync mode", __func__, wp->id);
}
/* Stop sync mode. */
void
screen_write_stop_sync(struct window_pane *wp)
{
if (wp == NULL)
return;
if (event_initialized(&wp->sync_timer))
evtimer_del(&wp->sync_timer);
wp->base.mode &= ~MODE_SYNC;
log_debug("%s: %%%u stopped sync mode", __func__, wp->id);
}
/* Cursor up by ny. */
void
screen_write_cursorup(struct screen_write_ctx *ctx, u_int ny)
@@ -1024,7 +1072,7 @@ screen_write_alignmenttest(struct screen_write_ctx *ctx)
memcpy(&gc, &grid_default_cell, sizeof gc);
utf8_set(&gc.data, 'E');
#ifdef ENABLE_SIXEL
#ifdef ENABLE_SIXEL_IMAGES
if (image_free_all(s) && ctx->wp != NULL)
ctx->wp->flags |= PANE_REDRAW;
#endif
@@ -1063,7 +1111,7 @@ screen_write_insertcharacter(struct screen_write_ctx *ctx, u_int nx, u_int bg)
if (s->cx > screen_size_x(s) - 1)
return;
#ifdef ENABLE_SIXEL
#ifdef ENABLE_SIXEL_IMAGES
if (image_check_line(s, s->cy, 1) && ctx->wp != NULL)
ctx->wp->flags |= PANE_REDRAW;
#endif
@@ -1096,7 +1144,7 @@ screen_write_deletecharacter(struct screen_write_ctx *ctx, u_int nx, u_int bg)
if (s->cx > screen_size_x(s) - 1)
return;
#ifdef ENABLE_SIXEL
#ifdef ENABLE_SIXEL_IMAGES
if (image_check_line(s, s->cy, 1) && ctx->wp != NULL)
ctx->wp->flags |= PANE_REDRAW;
#endif
@@ -1129,7 +1177,7 @@ screen_write_clearcharacter(struct screen_write_ctx *ctx, u_int nx, u_int bg)
if (s->cx > screen_size_x(s) - 1)
return;
#ifdef ENABLE_SIXEL
#ifdef ENABLE_SIXEL_IMAGES
if (image_check_line(s, s->cy, 1) && ctx->wp != NULL)
ctx->wp->flags |= PANE_REDRAW;
#endif
@@ -1152,14 +1200,14 @@ screen_write_insertline(struct screen_write_ctx *ctx, u_int ny, u_int bg)
struct grid *gd = s->grid;
struct tty_ctx ttyctx;
#ifdef ENABLE_SIXEL
#ifdef ENABLE_SIXEL_IMAGES
u_int sy = screen_size_y(s);
#endif
if (ny == 0)
ny = 1;
#ifdef ENABLE_SIXEL
#ifdef ENABLE_SIXEL_IMAGES
if (image_check_line(s, s->cy, sy - s->cy) && ctx->wp != NULL)
ctx->wp->flags |= PANE_REDRAW;
#endif
@@ -1212,7 +1260,7 @@ screen_write_deleteline(struct screen_write_ctx *ctx, u_int ny, u_int bg)
if (ny == 0)
ny = 1;
#ifdef ENABLE_SIXEL
#ifdef ENABLE_SIXEL_IMAGES
if (image_check_line(s, s->cy, sy - s->cy) && ctx->wp != NULL)
ctx->wp->flags |= PANE_REDRAW;
#endif
@@ -1265,7 +1313,7 @@ screen_write_clearline(struct screen_write_ctx *ctx, u_int bg)
if (gl->cellsize == 0 && COLOUR_DEFAULT(bg))
return;
#ifdef ENABLE_SIXEL
#ifdef ENABLE_SIXEL_IMAGES
if (image_check_line(s, s->cy, 1) && ctx->wp != NULL)
ctx->wp->flags |= PANE_REDRAW;
#endif
@@ -1288,7 +1336,7 @@ screen_write_clearendofline(struct screen_write_ctx *ctx, u_int bg)
struct screen *s = ctx->s;
struct grid_line *gl;
u_int sx = screen_size_x(s);
struct screen_write_citem *ci = ctx->item, *before;
struct screen_write_citem *ci = ctx->item;
if (s->cx == 0) {
screen_write_clearline(ctx, bg);
@@ -1299,23 +1347,18 @@ screen_write_clearendofline(struct screen_write_ctx *ctx, u_int bg)
if (s->cx > sx - 1 || (s->cx >= gl->cellsize && COLOUR_DEFAULT(bg)))
return;
#ifdef ENABLE_SIXEL
#ifdef ENABLE_SIXEL_IMAGES
if (image_check_line(s, s->cy, 1) && ctx->wp != NULL)
ctx->wp->flags |= PANE_REDRAW;
#endif
grid_view_clear(s->grid, s->cx, s->cy, sx - s->cx, 1, bg);
before = screen_write_collect_trim(ctx, s->cy, s->cx, sx - s->cx, NULL);
ci->x = s->cx;
ci->used = sx - s->cx;
ci->type = CLEAR;
ci->bg = bg;
if (before == NULL)
TAILQ_INSERT_TAIL(&ctx->s->write_list[s->cy].items, ci, entry);
else
TAILQ_INSERT_BEFORE(before, ci, entry);
ctx->item = screen_write_get_citem();
screen_write_collect_insert(ctx, ci);
}
/* Clear to start of line from cursor. */
@@ -1324,14 +1367,14 @@ screen_write_clearstartofline(struct screen_write_ctx *ctx, u_int bg)
{
struct screen *s = ctx->s;
u_int sx = screen_size_x(s);
struct screen_write_citem *ci = ctx->item, *before;
struct screen_write_citem *ci = ctx->item;
if (s->cx >= sx - 1) {
screen_write_clearline(ctx, bg);
return;
}
#ifdef ENABLE_SIXEL
#ifdef ENABLE_SIXEL_IMAGES
if (image_check_line(s, s->cy, 1) && ctx->wp != NULL)
ctx->wp->flags |= PANE_REDRAW;
#endif
@@ -1341,16 +1384,11 @@ screen_write_clearstartofline(struct screen_write_ctx *ctx, u_int bg)
else
grid_view_clear(s->grid, 0, s->cy, s->cx + 1, 1, bg);
before = screen_write_collect_trim(ctx, s->cy, 0, s->cx + 1, NULL);
ci->x = 0;
ci->used = s->cx + 1;
ci->type = CLEAR;
ci->bg = bg;
if (before == NULL)
TAILQ_INSERT_TAIL(&ctx->s->write_list[s->cy].items, ci, entry);
else
TAILQ_INSERT_BEFORE(before, ci, entry);
ctx->item = screen_write_get_citem();
screen_write_collect_insert(ctx, ci);
}
/* Move cursor to px,py. */
@@ -1384,7 +1422,7 @@ screen_write_reverseindex(struct screen_write_ctx *ctx, u_int bg)
struct tty_ctx ttyctx;
if (s->cy == s->rupper) {
#ifdef ENABLE_SIXEL
#ifdef ENABLE_SIXEL_IMAGES
if (image_free_all(s) && ctx->wp != NULL)
ctx->wp->flags |= PANE_REDRAW;
#endif
@@ -1431,7 +1469,7 @@ screen_write_linefeed(struct screen_write_ctx *ctx, int wrapped, u_int bg)
struct screen *s = ctx->s;
struct grid *gd = s->grid;
struct grid_line *gl;
#ifdef ENABLE_SIXEL
#ifdef ENABLE_SIXEL_IMAGES
int redraw = 0;
#endif
u_int rupper = s->rupper, rlower = s->rlower;
@@ -1449,7 +1487,7 @@ screen_write_linefeed(struct screen_write_ctx *ctx, int wrapped, u_int bg)
}
if (s->cy == s->rlower) {
#ifdef ENABLE_SIXEL
#ifdef ENABLE_SIXEL_IMAGES
if (rlower == screen_size_y(s) - 1)
redraw = image_scroll_up(s, 1);
else
@@ -1482,7 +1520,7 @@ screen_write_scrollup(struct screen_write_ctx *ctx, u_int lines, u_int bg)
ctx->bg = bg;
}
#ifdef ENABLE_SIXEL
#ifdef ENABLE_SIXEL_IMAGES
if (image_scroll_up(s, lines) && ctx->wp != NULL)
ctx->wp->flags |= PANE_REDRAW;
#endif
@@ -1511,7 +1549,7 @@ screen_write_scrolldown(struct screen_write_ctx *ctx, u_int lines, u_int bg)
else if (lines > s->rlower - s->rupper + 1)
lines = s->rlower - s->rupper + 1;
#ifdef ENABLE_SIXEL
#ifdef ENABLE_SIXEL_IMAGES
if (image_free_all(s) && ctx->wp != NULL)
ctx->wp->flags |= PANE_REDRAW;
#endif
@@ -1540,7 +1578,7 @@ screen_write_clearendofscreen(struct screen_write_ctx *ctx, u_int bg)
struct tty_ctx ttyctx;
u_int sx = screen_size_x(s), sy = screen_size_y(s);
#ifdef ENABLE_SIXEL
#ifdef ENABLE_SIXEL_IMAGES
if (image_check_line(s, s->cy, sy - s->cy) && ctx->wp != NULL)
ctx->wp->flags |= PANE_REDRAW;
#endif
@@ -1574,7 +1612,7 @@ screen_write_clearstartofscreen(struct screen_write_ctx *ctx, u_int bg)
struct tty_ctx ttyctx;
u_int sx = screen_size_x(s);
#ifdef ENABLE_SIXEL
#ifdef ENABLE_SIXEL_IMAGES
if (image_check_line(s, 0, s->cy - 1) && ctx->wp != NULL)
ctx->wp->flags |= PANE_REDRAW;
#endif
@@ -1602,7 +1640,7 @@ screen_write_clearscreen(struct screen_write_ctx *ctx, u_int bg)
struct tty_ctx ttyctx;
u_int sx = screen_size_x(s), sy = screen_size_y(s);
#ifdef ENABLE_SIXEL
#ifdef ENABLE_SIXEL_IMAGES
if (image_free_all(s) && ctx->wp != NULL)
ctx->wp->flags |= PANE_REDRAW;
#endif
@@ -1782,6 +1820,17 @@ screen_write_collect_flush(struct screen_write_ctx *ctx, int scroll_only,
u_int y, cx, cy, last, items = 0;
struct tty_ctx ttyctx;
if (s->mode & MODE_SYNC) {
for (y = 0; y < screen_size_y(s); y++) {
cl = &ctx->s->write_list[y];
TAILQ_FOREACH_SAFE(ci, &cl->items, entry, tmp) {
TAILQ_REMOVE(&cl->items, ci, entry);
screen_write_free_citem(ci);
}
}
return;
}
if (ctx->scrolled != 0) {
log_debug("%s: scrolled %u (region %u-%u)", __func__,
ctx->scrolled, s->rupper, s->rlower);
@@ -1807,6 +1856,8 @@ screen_write_collect_flush(struct screen_write_ctx *ctx, int scroll_only,
cl = &ctx->s->write_list[y];
last = UINT_MAX;
TAILQ_FOREACH_SAFE(ci, &cl->items, entry, tmp) {
log_debug("collect list: x=%u (last %u), y=%u, used=%u",
ci->x, last, y, ci->used);
if (last != UINT_MAX && ci->x <= last) {
fatalx("collect list not in order: %u <= %u",
ci->x, last);
@@ -1837,29 +1888,39 @@ screen_write_collect_flush(struct screen_write_ctx *ctx, int scroll_only,
log_debug("%s: flushed %u items (%s)", __func__, items, from);
}
/* Finish and store collected cells. */
/* Insert an item on current line. */
void
screen_write_collect_end(struct screen_write_ctx *ctx)
screen_write_collect_insert(struct screen_write_ctx *ctx,
struct screen_write_citem *ci)
{
struct screen *s = ctx->s;
struct screen_write_citem *ci = ctx->item, *before;
struct screen_write_cline *cl = &s->write_list[s->cy];
struct grid_cell gc;
u_int xx;
int wrapped = ci->wrapped;
struct screen_write_citem *before;
if (ci->used == 0)
return;
before = screen_write_collect_trim(ctx, s->cy, s->cx, ci->used,
&wrapped);
ci->x = s->cx;
ci->wrapped = wrapped;
before = screen_write_collect_trim(ctx, s->cy, ci->x, ci->used,
&ci->wrapped);
if (before == NULL)
TAILQ_INSERT_TAIL(&cl->items, ci, entry);
else
TAILQ_INSERT_BEFORE(before, ci, entry);
ctx->item = screen_write_get_citem();
}
/* Finish and store collected cells. */
void
screen_write_collect_end(struct screen_write_ctx *ctx)
{
struct screen *s = ctx->s;
struct screen_write_citem *ci = ctx->item, *bci = NULL, *aci;
struct screen_write_cline *cl = &s->write_list[s->cy];
struct grid_cell gc;
u_int xx;
if (ci->used == 0)
return;
ci->x = s->cx;
screen_write_collect_insert(ctx, ci);
log_debug("%s: %u %.*s (at %u,%u)", __func__, ci->used,
(int)ci->used, cl->data + ci->x, s->cx, s->cy);
@@ -1871,24 +1932,40 @@ screen_write_collect_end(struct screen_write_ctx *ctx)
break;
grid_view_set_cell(s->grid, xx, s->cy,
&grid_default_cell);
log_debug("%s: padding erased (before) at %u (cx %u)",
__func__, xx, s->cx);
}
if (xx != s->cx) {
if (xx == 0)
grid_view_get_cell(s->grid, 0, s->cy, &gc);
if (gc.data.width > 1) {
if (gc.data.width > 1 ||
(gc.flags & GRID_FLAG_PADDING)) {
grid_view_set_cell(s->grid, xx, s->cy,
&grid_default_cell);
log_debug("%s: padding erased (before) at %u "
"(cx %u)", __func__, xx, s->cx);
}
}
if (xx != s->cx) {
bci = ctx->item;
bci->type = CLEAR;
bci->x = xx;
bci->bg = 8;
bci->used = s->cx - xx;
log_debug("%s: padding erased (before): from %u, "
"size %u", __func__, bci->x, bci->used);
}
}
#ifdef ENABLE_SIXEL
#ifdef ENABLE_SIXEL_IMAGES
if (image_check_area(s, s->cx, s->cy, ci->used, 1) && ctx->wp != NULL)
ctx->wp->flags |= PANE_REDRAW;
#endif
grid_view_set_cells(s->grid, s->cx, s->cy, &ci->gc, cl->data + ci->x,
ci->used);
if (bci != NULL)
screen_write_collect_insert(ctx, bci);
screen_write_set_cursor(ctx, s->cx + ci->used, -1);
for (xx = s->cx; xx < screen_size_x(s); xx++) {
@@ -1896,6 +1973,18 @@ screen_write_collect_end(struct screen_write_ctx *ctx)
if (~gc.flags & GRID_FLAG_PADDING)
break;
grid_view_set_cell(s->grid, xx, s->cy, &grid_default_cell);
log_debug("%s: padding erased (after) at %u (cx %u)",
__func__, xx, s->cx);
}
if (xx != s->cx) {
aci = ctx->item;
aci->type = CLEAR;
aci->x = s->cx;
aci->bg = 8;
aci->used = xx - s->cx;
log_debug("%s: padding erased (after): from %u, size %u",
__func__, aci->x, aci->used);
screen_write_collect_insert(ctx, aci);
}
}
@@ -1966,7 +2055,7 @@ screen_write_cell(struct screen_write_ctx *ctx, const struct grid_cell *gc)
struct tty_ctx ttyctx;
u_int sx = screen_size_x(s), sy = screen_size_y(s);
u_int width = ud->width, xx, not_wrap;
int selected, skip = 1;
int selected, skip = 1, redraw = 0;
/* Ignore padding cells. */
if (gc->flags & GRID_FLAG_PADDING)
@@ -2008,8 +2097,10 @@ screen_write_cell(struct screen_write_ctx *ctx, const struct grid_cell *gc)
gl = grid_get_line(s->grid, s->grid->hsize + s->cy);
if (gl->flags & GRID_LINE_EXTENDED) {
grid_view_get_cell(gd, s->cx, s->cy, &now_gc);
if (screen_write_overwrite(ctx, &now_gc, width))
if (screen_write_overwrite(ctx, &now_gc, width)) {
redraw = 1;
skip = 0;
}
}
/*
@@ -2080,12 +2171,13 @@ screen_write_cell(struct screen_write_ctx *ctx, const struct grid_cell *gc)
}
/* Write to the screen. */
if (!skip) {
if (!skip && !(s->mode & MODE_SYNC)) {
if (selected) {
screen_select_cell(s, &tmp_gc, gc);
ttyctx.cell = &tmp_gc;
} else
ttyctx.cell = gc;
ttyctx.num = redraw ? 2 : 0;
tty_write(tty_cmd_cell, &ttyctx);
}
}
@@ -2152,6 +2244,8 @@ screen_write_combine(struct screen_write_ctx *ctx, const struct grid_cell *gc)
case HANGULJAMO_STATE_NOT_HANGULJAMO:
if (utf8_should_combine(&last.data, ud))
force_wide = 1;
else if (utf8_should_combine(ud, &last.data))
force_wide = 1;
else if (!utf8_has_zwj(&last.data))
return (0);
break;
@@ -2276,14 +2370,14 @@ screen_write_overwrite(struct screen_write_ctx *ctx, struct grid_cell *gc,
/* Set external clipboard. */
void
screen_write_setselection(struct screen_write_ctx *ctx, const char *flags,
screen_write_setselection(struct screen_write_ctx *ctx, const char *clip,
u_char *str, u_int len)
{
struct tty_ctx ttyctx;
screen_write_initctx(ctx, &ttyctx, 0);
ttyctx.ptr = str;
ttyctx.ptr2 = (void *)flags;
ttyctx.ptr2 = (void *)clip;
ttyctx.num = len;
tty_write(tty_cmd_setselection, &ttyctx);
@@ -2304,7 +2398,7 @@ screen_write_rawstring(struct screen_write_ctx *ctx, u_char *str, u_int len,
tty_write(tty_cmd_rawstring, &ttyctx);
}
#ifdef ENABLE_SIXEL
#ifdef ENABLE_SIXEL_IMAGES
/* Write a SIXEL image. */
void
screen_write_sixelimage(struct screen_write_ctx *ctx, struct sixel_image *si,
@@ -2316,8 +2410,11 @@ screen_write_sixelimage(struct screen_write_ctx *ctx, struct sixel_image *si,
u_int x, y, sx, sy, cx = s->cx, cy = s->cy, i, lines;
struct sixel_image *new;
if (screen_size_y(s) == 1)
return;
sixel_size_in_cells(si, &x, &y);
if (x > screen_size_x(s) || y > screen_size_y(s)) {
if (x > screen_size_x(s) || y > screen_size_y(s) - 1) {
if (x > screen_size_x(s) - cx)
sx = screen_size_x(s) - cx;
else
@@ -2328,16 +2425,14 @@ screen_write_sixelimage(struct screen_write_ctx *ctx, struct sixel_image *si,
sy = y;
new = sixel_scale(si, 0, 0, 0, y - sy, sx, sy, 1);
sixel_free(si);
si = new;
/* Bail out if the image cannot be scaled. */
if (si == NULL)
if (new == NULL)
return;
sixel_size_in_cells(si, &x, &y);
sixel_size_in_cells(new, &x, &y);
si = new;
}
sy = screen_size_y(s) - cy;
if (sy < y) {
if (sy <= y) {
lines = y - sy + 1;
if (image_scroll_up(s, lines) && ctx->wp != NULL)
ctx->wp->flags |= PANE_REDRAW;
@@ -2355,7 +2450,7 @@ screen_write_sixelimage(struct screen_write_ctx *ctx, struct sixel_image *si,
screen_write_collect_flush(ctx, 0, __func__);
screen_write_initctx(ctx, &ttyctx, 0);
ttyctx.ptr = image_store(s, si);
ttyctx.ptr = image_store(s, IMAGE_SIXEL, si);
tty_write(tty_cmd_sixelimage, &ttyctx);
@@ -2363,6 +2458,38 @@ screen_write_sixelimage(struct screen_write_ctx *ctx, struct sixel_image *si,
}
#endif
#ifdef ENABLE_KITTY_IMAGES
void
screen_write_kittyimage(struct screen_write_ctx *ctx, struct kitty_image *ki)
{
struct screen *s = ctx->s;
struct tty_ctx ttyctx;
struct image *im;
if (ki == NULL)
return;
/* Store the image in the cache. */
im = image_store(s, IMAGE_KITTY, ki);
/* Trigger a tty write to send to all terminals. */
if (im != NULL && ctx->wp != NULL) {
screen_write_collect_flush(ctx, 0, __func__);
screen_write_initctx(ctx, &ttyctx, 0);
ttyctx.ptr = im;
ttyctx.arg = ctx->wp;
ttyctx.ocx = s->cx;
ttyctx.ocy = s->cy;
ttyctx.set_client_cb = tty_set_client_cb;
tty_write(tty_cmd_kittyimage, &ttyctx);
}
/* Move cursor past the image. */
if (kitty_get_rows(ki) > 0)
screen_write_cursormove(ctx, 0, s->cy + kitty_get_rows(ki), 0);
}
#endif
/* Turn alternate screen on. */
void
screen_write_alternateon(struct screen_write_ctx *ctx, struct grid_cell *gc,
@@ -2377,8 +2504,10 @@ screen_write_alternateon(struct screen_write_ctx *ctx, struct grid_cell *gc,
screen_write_collect_flush(ctx, 0, __func__);
screen_alternate_on(ctx->s, gc, cursor);
if (wp != NULL)
if (wp != NULL) {
layout_fix_panes(wp->window, NULL);
server_redraw_window_borders(wp->window);
}
screen_write_initctx(ctx, &ttyctx, 1);
if (ttyctx.redraw_cb != NULL)
@@ -2399,8 +2528,10 @@ screen_write_alternateoff(struct screen_write_ctx *ctx, struct grid_cell *gc,
screen_write_collect_flush(ctx, 0, __func__);
screen_alternate_off(ctx->s, gc, cursor);
if (wp != NULL)
if (wp != NULL) {
layout_fix_panes(wp->window, NULL);
server_redraw_window_borders(wp->window);
}
screen_write_initctx(ctx, &ttyctx, 1);
if (ttyctx.redraw_cb != NULL)

View File

@@ -89,7 +89,7 @@ screen_init(struct screen *s, u_int sx, u_int sy, u_int hlimit)
s->tabs = NULL;
s->sel = NULL;
#ifdef ENABLE_SIXEL
#ifdef ENABLE_IMAGES
TAILQ_INIT(&s->images);
TAILQ_INIT(&s->saved_images);
#endif
@@ -127,7 +127,7 @@ screen_reinit(struct screen *s)
screen_clear_selection(s);
screen_free_titles(s);
#ifdef ENABLE_SIXEL
#ifdef ENABLE_IMAGES
image_free_all(s);
#endif
@@ -164,7 +164,7 @@ screen_free(struct screen *s)
hyperlinks_free(s->hyperlinks);
screen_free_titles(s);
#ifdef ENABLE_SIXEL
#ifdef ENABLE_IMAGES
image_free_all(s);
#endif
}
@@ -324,7 +324,7 @@ screen_resize_cursor(struct screen *s, u_int sx, u_int sy, int reflow,
if (sy != screen_size_y(s))
screen_resize_y(s, sy, eat_empty, &cy);
#ifdef ENABLE_SIXEL
#ifdef ENABLE_IMAGES
image_free_all(s);
#endif
@@ -581,12 +581,12 @@ screen_check_selection(struct screen *s, u_int px, u_int py)
}
/* Get selected grid cell. */
void
int
screen_select_cell(struct screen *s, struct grid_cell *dst,
const struct grid_cell *src)
{
if (s->sel == NULL || s->sel->hidden)
return;
return (0);
memcpy(dst, &s->sel->cell, sizeof *dst);
if (COLOUR_DEFAULT(dst->fg))
@@ -594,8 +594,13 @@ screen_select_cell(struct screen *s, struct grid_cell *dst,
if (COLOUR_DEFAULT(dst->bg))
dst->bg = src->bg;
utf8_copy(&dst->data, &src->data);
dst->attr = src->attr;
dst->flags = src->flags;
if (dst->attr & GRID_ATTR_NOATTR)
dst->attr |= (src->attr & GRID_ATTR_CHARSET);
else
dst->attr |= src->attr;
return (1);
}
/* Reflow wrapped lines. */
@@ -644,7 +649,7 @@ screen_alternate_on(struct screen *s, struct grid_cell *gc, int cursor)
}
memcpy(&s->saved_cell, gc, sizeof s->saved_cell);
#ifdef ENABLE_SIXEL
#ifdef ENABLE_IMAGES
TAILQ_CONCAT(&s->saved_images, &s->images, entry);
#endif
@@ -702,7 +707,7 @@ screen_alternate_off(struct screen *s, struct grid_cell *gc, int cursor)
grid_destroy(s->saved_grid);
s->saved_grid = NULL;
#ifdef ENABLE_SIXEL
#ifdef ENABLE_IMAGES
image_free_all(s);
TAILQ_CONCAT(&s->images, &s->saved_images, entry);
#endif
@@ -761,6 +766,79 @@ screen_mode_to_string(int mode)
strlcat(tmp, "KEYS_EXTENDED,", sizeof tmp);
if (mode & MODE_KEYS_EXTENDED_2)
strlcat(tmp, "KEYS_EXTENDED_2,", sizeof tmp);
if (mode & MODE_THEME_UPDATES)
strlcat(tmp, "THEME_UPDATES,", sizeof tmp);
tmp[strlen(tmp) - 1] = '\0';
return (tmp);
}
const char *
screen_print(struct screen *s)
{
static char *buf;
static size_t len = 16384;
const char *acs;
u_int x, y;
int n;
size_t last = 0;
struct utf8_data ud;
struct grid_line *gl;
struct grid_cell_entry *gce;
if (buf == NULL)
buf = xmalloc(len);
for (y = 0; y < screen_hsize(s) + s->grid->sy; y++) {
n = snprintf(buf + last, len - last, "%.4d \"", y);
if (n <= 0 || (u_int)n >= len - last)
goto out;
last += n;
gl = &s->grid->linedata[y];
for (x = 0; x < gl->cellused; x++) {
gce = &gl->celldata[x];
if (gce->flags & GRID_FLAG_PADDING)
continue;
if (~gce->flags & GRID_FLAG_EXTENDED) {
if (last + 2 >= len)
goto out;
buf[last++] = gce->data.data;
} else if (gce->flags & GRID_FLAG_TAB) {
if (last + 2 >= len)
goto out;
buf[last++] = '\t';
} else if (gce->flags & GRID_ATTR_CHARSET) {
acs = tty_acs_get(NULL, gce->data.data);
if (acs != NULL)
n = strlen(acs);
else {
acs = &gce->data.data;
n = 1;
}
if (last + n + 1 >= len)
goto out;
memcpy(buf + last, acs, n);
last += n;
} else {
utf8_to_data(gl->extddata[gce->offset].data,
&ud);
if (ud.size > 0) {
if (last + ud.size + 1 >= len)
goto out;
memcpy(buf + last, ud.data, ud.size);
last += ud.size;
}
}
}
if (last + 3 >= len)
goto out;
buf[last++] = '"';
buf[last++] = '\n';
}
out:
buf[last] = '\0';
return (buf);
}

View File

@@ -161,38 +161,58 @@ server_client_clear_overlay(struct client *c)
server_redraw_client(c);
}
/* Are these ranges empty? That is, nothing is visible. */
int
server_client_ranges_is_empty(struct visible_ranges *r)
{
u_int i;
for (i = 0; i < r->used; i++) {
if (r->ranges[i].nx != 0)
return (0);
}
return (1);
}
/* Ensure we have space for at least n ranges. */
void
server_client_ensure_ranges(struct visible_ranges *r, u_int n)
{
if (r->size >= n)
return;
r->ranges = xrecallocarray(r->ranges, r->size, n, sizeof *r->ranges);
r->size = n;
}
/*
* Given overlay position and dimensions, return parts of the input range which
* are visible.
*/
void
server_client_overlay_range(u_int x, u_int y, u_int sx, u_int sy, u_int px,
u_int py, u_int nx, struct overlay_ranges *r)
u_int py, u_int nx, struct visible_ranges *r)
{
u_int ox, onx;
/* Return up to 2 ranges. */
r->px[2] = 0;
r->nx[2] = 0;
/* Trivial case of no overlap in the y direction. */
if (py < y || py > y + sy - 1) {
r->px[0] = px;
r->nx[0] = nx;
r->px[1] = 0;
r->nx[1] = 0;
server_client_ensure_ranges(r, 1);
r->ranges[0].px = px;
r->ranges[0].nx = nx;
r->used = 1;
return;
}
server_client_ensure_ranges(r, 2);
/* Visible bit to the left of the popup. */
if (px < x) {
r->px[0] = px;
r->nx[0] = x - px;
if (r->nx[0] > nx)
r->nx[0] = nx;
r->ranges[0].px = px;
r->ranges[0].nx = x - px;
if (r->ranges[0].nx > nx)
r->ranges[0].nx = nx;
} else {
r->px[0] = 0;
r->nx[0] = 0;
r->ranges[0].px = 0;
r->ranges[0].nx = 0;
}
/* Visible bit to the right of the popup. */
@@ -201,12 +221,13 @@ server_client_overlay_range(u_int x, u_int y, u_int sx, u_int sy, u_int px,
ox = px;
onx = px + nx;
if (onx > ox) {
r->px[1] = ox;
r->nx[1] = onx - ox;
r->ranges[1].px = ox;
r->ranges[1].nx = onx - ox;
} else {
r->px[1] = 0;
r->nx[1] = 0;
r->ranges[1].px = 0;
r->ranges[1].nx = 0;
}
r->used = 2;
}
/* Check if this client is inside this server. */
@@ -311,6 +332,8 @@ server_client_create(int fd)
evtimer_set(&c->repeat_timer, server_client_repeat_timer, c);
evtimer_set(&c->click_timer, server_client_click_timer, c);
c->click_wp = -1;
TAILQ_INIT(&c->input_requests);
TAILQ_INSERT_TAIL(&clients, c, entry);
@@ -401,13 +424,13 @@ server_client_set_session(struct client *c, struct session *s)
if (old != NULL && old->curw != NULL)
window_update_focus(old->curw->window);
if (s != NULL) {
s->curw->window->latest = c;
recalculate_sizes();
window_update_focus(s->curw->window);
session_update_activity(s, NULL);
session_theme_changed(s);
gettimeofday(&s->last_attached_time, NULL);
s->curw->flags &= ~WINLINK_ALERTFLAGS;
s->curw->window->latest = c;
alerts_check_session(s);
tty_update_client_offset(c);
status_timer_start(c);
@@ -613,7 +636,8 @@ server_client_check_mouse_in_pane(struct window_pane *wp, u_int px, u_int py,
line = wp->yoff + wp->sy;
/* Check if point is within the pane or scrollbar. */
if (((pane_status != PANE_STATUS_OFF && py != line) ||
if (((pane_status != PANE_STATUS_OFF &&
py != line && py != wp->yoff + wp->sy) ||
(wp->yoff == 0 && py < wp->sy) ||
(py >= wp->yoff && py < wp->yoff + wp->sy)) &&
((sb_pos == PANE_SCROLLBARS_RIGHT &&
@@ -732,21 +756,17 @@ server_client_check_mouse(struct client *c, struct key_event *event)
if (c->flags & CLIENT_DOUBLECLICK) {
evtimer_del(&c->click_timer);
c->flags &= ~CLIENT_DOUBLECLICK;
if (m->b == c->click_button) {
type = SECOND;
x = m->x, y = m->y, b = m->b;
log_debug("second-click at %u,%u", x, y);
c->flags |= CLIENT_TRIPLECLICK;
}
type = SECOND;
x = m->x, y = m->y, b = m->b;
log_debug("second-click at %u,%u", x, y);
c->flags |= CLIENT_TRIPLECLICK;
} else if (c->flags & CLIENT_TRIPLECLICK) {
evtimer_del(&c->click_timer);
c->flags &= ~CLIENT_TRIPLECLICK;
if (m->b == c->click_button) {
type = TRIPLE;
x = m->x, y = m->y, b = m->b;
log_debug("triple-click at %u,%u", x, y);
goto have_event;
}
type = TRIPLE;
x = m->x, y = m->y, b = m->b;
log_debug("triple-click at %u,%u", x, y);
goto have_event;
}
/* DOWN is the only remaining event type. */
@@ -756,17 +776,6 @@ server_client_check_mouse(struct client *c, struct key_event *event)
log_debug("down at %u,%u", x, y);
c->flags |= CLIENT_DOUBLECLICK;
}
if (KEYC_CLICK_TIMEOUT != 0) {
memcpy(&c->click_event, m, sizeof c->click_event);
c->click_button = m->b;
log_debug("click timer started");
tv.tv_sec = KEYC_CLICK_TIMEOUT / 1000;
tv.tv_usec = (KEYC_CLICK_TIMEOUT % 1000) * 1000L;
evtimer_del(&c->click_timer);
evtimer_add(&c->click_timer, &tv);
}
}
have_event:
@@ -882,6 +891,34 @@ have_event:
}
}
/* Reset click type or add a click timer if needed. */
if (type == DOWN ||
type == SECOND ||
type == TRIPLE) {
if (type != DOWN &&
(m->b != c->click_button ||
where != (enum mouse_where)c->click_where ||
m->wp != c->click_wp)) {
type = DOWN;
log_debug("click sequence reset at %u,%u", x, y);
c->flags &= ~CLIENT_TRIPLECLICK;
c->flags |= CLIENT_DOUBLECLICK;
}
if (type != TRIPLE && KEYC_CLICK_TIMEOUT != 0) {
memcpy(&c->click_event, m, sizeof c->click_event);
c->click_button = m->b;
c->click_where = where;
c->click_wp = m->wp;
log_debug("click timer started");
tv.tv_sec = KEYC_CLICK_TIMEOUT / 1000;
tv.tv_usec = (KEYC_CLICK_TIMEOUT % 1000) * 1000L;
evtimer_del(&c->click_timer);
evtimer_add(&c->click_timer, &tv);
}
}
/* Stop dragging if needed. */
if (type != DRAG &&
type != WHEEL &&
@@ -1059,8 +1096,16 @@ have_event:
case NOTYPE:
break;
case MOVE:
if (where == PANE)
if (where == PANE) {
key = KEYC_MOUSEMOVE_PANE;
if (wp != NULL &&
wp != w->active &&
options_get_number(s->options, "focus-follows-mouse")) {
window_set_active_pane(w, wp, 1);
server_redraw_window_borders(w);
server_status_window(w);
}
}
if (where == STATUS)
key = KEYC_MOUSEMOVE_STATUS;
if (where == STATUS_LEFT)
@@ -1270,7 +1315,11 @@ have_event:
if (c->tty.mouse_scrolling_flag == 0 &&
where == SCROLLBAR_SLIDER) {
c->tty.mouse_scrolling_flag = 1;
c->tty.mouse_slider_mpos = sl_mpos;
if (m->statusat == 0) {
c->tty.mouse_slider_mpos = sl_mpos +
m->statuslines;
} else
c->tty.mouse_slider_mpos = sl_mpos;
}
break;
case WHEEL:
@@ -2385,16 +2434,6 @@ server_client_key_callback(struct cmdq_item *item, void *data)
event->key = key;
}
/* Handle theme reporting keys. */
if (key == KEYC_REPORT_LIGHT_THEME) {
server_client_report_theme(c, THEME_LIGHT);
goto out;
}
if (key == KEYC_REPORT_DARK_THEME) {
server_client_report_theme(c, THEME_DARK);
goto out;
}
/* Find affected pane. */
if (!KEYC_IS_MOUSE(key) || cmd_find_from_mouse(&fs, m, 0) != 0)
cmd_find_from_client(&fs, c, 0);
@@ -2613,6 +2652,19 @@ server_client_handle_key(struct client *c, struct key_event *event)
if (s == NULL || (c->flags & CLIENT_UNATTACHEDFLAGS))
return (0);
/*
* Handle theme reporting keys before overlays so they work even when a
* popup is open.
*/
if (event->key == KEYC_REPORT_LIGHT_THEME) {
server_client_report_theme(c, THEME_LIGHT);
return (0);
}
if (event->key == KEYC_REPORT_DARK_THEME) {
server_client_report_theme(c, THEME_DARK);
return (0);
}
/*
* Key presses in overlay mode and the command prompt are a special
* case. The queue might be blocked so they need to be processed
@@ -2653,18 +2705,31 @@ server_client_handle_key(struct client *c, struct key_event *event)
void
server_client_loop(void)
{
struct client *c;
struct window *w;
struct window_pane *wp;
struct client *c;
struct window *w;
struct window_pane *wp;
struct window_mode_entry *wme;
/* Check for window resize. This is done before redrawing. */
RB_FOREACH(w, windows, &windows)
server_client_check_window_resize(w);
/* Notify modes that pane styles may have changed. */
RB_FOREACH(w, windows, &windows) {
TAILQ_FOREACH(wp, &w->panes, entry) {
if (wp->flags & PANE_STYLECHANGED) {
wme = TAILQ_FIRST(&wp->modes);
if (wme != NULL &&
wme->mode->style_changed != NULL)
wme->mode->style_changed(wme);
}
}
}
/* Check clients. */
TAILQ_FOREACH(c, &clients, entry) {
server_client_check_exit(c);
if (c->session != NULL) {
if (c->session != NULL && c->session->curw != NULL) {
server_client_check_modes(c);
server_client_check_redraw(c);
server_client_reset_state(c);
@@ -2901,8 +2966,8 @@ server_client_reset_state(struct client *c)
struct window_pane *wp = server_client_get_pane(c), *loop;
struct screen *s = NULL;
struct options *oo = c->session->options;
int mode = 0, cursor, flags, n;
u_int cx = 0, cy = 0, ox, oy, sx, sy;
int mode = 0, cursor, flags;
u_int cx = 0, cy = 0, ox, oy, sx, sy, n;
if (c->flags & (CLIENT_CONTROL|CLIENT_SUSPENDED))
return;
@@ -2934,13 +2999,13 @@ server_client_reset_state(struct client *c)
if (c->prompt_string != NULL) {
n = options_get_number(oo, "status-position");
if (n == 0)
cy = 0;
cy = status_prompt_line_at(c);
else {
n = status_line_size(c);
if (n == 0)
cy = tty->sy - 1;
else
n = status_line_size(c) - status_prompt_line_at(c);
if (n <= tty->sy)
cy = tty->sy - n;
else
cy = tty->sy - 1;
}
cx = c->prompt_cursor;
} else if (c->overlay_draw == NULL) {
@@ -2964,7 +3029,8 @@ server_client_reset_state(struct client *c)
/*
* Set mouse mode if requested. To support dragging, always use button
* mode.
* mode. For focus-follows-mouse, we need all-motion mode to receive
* movement events.
*/
if (options_get_number(oo, "mouse")) {
if (c->overlay_draw == NULL) {
@@ -2974,7 +3040,9 @@ server_client_reset_state(struct client *c)
mode |= MODE_MOUSE_ALL;
}
}
if (~mode & MODE_MOUSE_ALL)
if (options_get_number(oo, "focus-follows-mouse"))
mode |= MODE_MOUSE_ALL;
else if (~mode & MODE_MOUSE_ALL)
mode |= MODE_MOUSE_BUTTON;
}
@@ -3427,6 +3495,24 @@ server_client_read_only(struct cmdq_item *item, __unused void *data)
return (CMD_RETURN_ERROR);
}
/* Callback for default command. */
static enum cmd_retval
server_client_default_command(struct cmdq_item *item, __unused void *data)
{
struct client *c = cmdq_get_client(item);
struct cmd_list *cmdlist;
struct cmdq_item *new_item;
cmdlist = options_get_command(global_options, "default-client-command");
if ((c->flags & CLIENT_READONLY) &&
!cmd_list_all_have(cmdlist, CMD_READONLY))
new_item = cmdq_get_callback(server_client_read_only, NULL);
else
new_item = cmdq_get_command(cmdlist, NULL);
cmdq_insert_after(item, new_item);
return (CMD_RETURN_NORMAL);
}
/* Callback when command is done. */
static enum cmd_retval
server_client_command_done(struct cmdq_item *item, __unused void *data)
@@ -3455,7 +3541,6 @@ server_client_dispatch_command(struct client *c, struct imsg *imsg)
struct cmd_parse_result *pr;
struct args_value *values;
struct cmdq_item *new_item;
struct cmd_list *cmdlist;
if (c->flags & CLIENT_EXIT)
return (0);
@@ -3476,8 +3561,8 @@ server_client_dispatch_command(struct client *c, struct imsg *imsg)
argc = data.argc;
if (argc == 0) {
cmdlist = cmd_list_copy(options_get_command(global_options,
"default-client-command"), 0, NULL);
new_item = cmdq_get_callback(server_client_default_command,
NULL);
} else {
values = args_from_vector(argc, argv);
pr = cmd_parse_from_arguments(values, argc, NULL);
@@ -3491,18 +3576,17 @@ server_client_dispatch_command(struct client *c, struct imsg *imsg)
args_free_values(values, argc);
free(values);
cmd_free_argv(argc, argv);
cmdlist = pr->cmdlist;
if ((c->flags & CLIENT_READONLY) &&
!cmd_list_all_have(pr->cmdlist, CMD_READONLY)) {
new_item = cmdq_get_callback(server_client_read_only,
NULL);
} else
new_item = cmdq_get_command(pr->cmdlist, NULL);
cmd_list_free(pr->cmdlist);
}
if ((c->flags & CLIENT_READONLY) &&
!cmd_list_all_have(cmdlist, CMD_READONLY))
new_item = cmdq_get_callback(server_client_read_only, NULL);
else
new_item = cmdq_get_command(cmdlist, NULL);
cmdq_append(c, new_item);
cmdq_append(c, cmdq_get_callback(server_client_command_done, NULL));
cmd_list_free(cmdlist);
return (0);
error:

View File

@@ -20,6 +20,7 @@
#include <sys/wait.h>
#include <sys/uio.h>
#include <signal.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
@@ -95,7 +96,9 @@ server_redraw_window(struct window *w)
struct client *c;
TAILQ_FOREACH(c, &clients, entry) {
if (c->session != NULL && c->session->curw->window == w)
if (c->session != NULL &&
c->session->curw != NULL &&
c->session->curw->window == w)
server_redraw_client(c);
}
}
@@ -106,7 +109,9 @@ server_redraw_window_borders(struct window *w)
struct client *c;
TAILQ_FOREACH(c, &clients, entry) {
if (c->session != NULL && c->session->curw->window == w)
if (c->session != NULL &&
c->session->curw != NULL &&
c->session->curw->window == w)
c->flags |= CLIENT_REDRAWBORDERS;
}
}
@@ -440,9 +445,9 @@ server_destroy_session(struct session *s)
else if (detach_on_destroy == 2)
s_new = server_find_session(s, server_newer_detached_session);
else if (detach_on_destroy == 3)
s_new = session_previous_session(s);
s_new = session_previous_session(s, NULL);
else if (detach_on_destroy == 4)
s_new = session_next_session(s);
s_new = session_next_session(s, NULL);
/*
* If no suitable new session was found above, then look for any

View File

@@ -293,36 +293,48 @@ session_update_activity(struct session *s, struct timeval *from)
/* Find the next usable session. */
struct session *
session_next_session(struct session *s)
session_next_session(struct session *s, struct sort_criteria *sort_crit)
{
struct session *s2;
struct session **l;
u_int n, i;
if (RB_EMPTY(&sessions) || !session_alive(s))
return (NULL);
s2 = RB_NEXT(sessions, &sessions, s);
if (s2 == NULL)
s2 = RB_MIN(sessions, &sessions);
if (s2 == s)
return (NULL);
return (s2);
l = sort_get_sessions(&n, sort_crit);
for (i = 0; i < n; i++) {
if (l[i] == s)
break;
}
if (i == n)
fatalx("session %s not found in sorted list", s->name);
i++;
if (i == n)
i = 0;
return (l[i]);
}
/* Find the previous usable session. */
struct session *
session_previous_session(struct session *s)
session_previous_session(struct session *s, struct sort_criteria *sort_crit)
{
struct session *s2;
struct session **l;
u_int n, i;
if (RB_EMPTY(&sessions) || !session_alive(s))
return (NULL);
s2 = RB_PREV(sessions, &sessions, s);
if (s2 == NULL)
s2 = RB_MAX(sessions, &sessions);
if (s2 == s)
return (NULL);
return (s2);
l = sort_get_sessions(&n, sort_crit);
for (i = 0; i < n; i++) {
if (l[i] == s)
break;
}
if (i == n)
fatalx("session %s not found in sorted list", s->name);
if (i == 0)
i = n;
i--;
return (l[i]);
}
/* Attach a window to a session. */
@@ -766,3 +778,29 @@ session_theme_changed(struct session *s)
}
}
}
/* Update history for all panes. */
void
session_update_history(struct session *s)
{
struct winlink *wl;
struct window_pane *wp;
struct grid *gd;
u_int limit, osize;
limit = options_get_number(s->options, "history-limit");
RB_FOREACH(wl, winlinks, &s->windows) {
TAILQ_FOREACH(wp, &wl->window->panes, entry) {
gd = wp->base.grid;
osize = gd->hsize;
gd->hlimit = limit;
grid_collect_history(gd, 1);
if (gd->hsize != osize) {
log_debug("%s: %%%u %u -> %u", __func__, wp->id,
osize, gd->hsize);
}
}
}
}

649
sort.c Normal file
View File

@@ -0,0 +1,649 @@
/* $OpenBSD$ */
/*
* Copyright (c) 2026 Dane Jensen <dhcjensen@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"
static struct sort_criteria *sort_criteria;
static void
sort_qsort(void *l, u_int len, u_int size, int (*cmp)(const void *, const void *),
struct sort_criteria *sort_crit)
{
u_int i;
void *tmp, **ll;
if (sort_crit->order == SORT_END)
return;
if (sort_crit->order == SORT_ORDER) {
if (sort_crit->reversed) {
ll = l;
for (i = 0; i < len / 2; i++) {
tmp = ll[i];
ll[i] = ll[len - 1 - i];
ll[len - 1 - i] = tmp;
}
}
} else {
sort_criteria = sort_crit;
qsort(l, len, size, cmp);
}
}
static int
sort_buffer_cmp(const void *a0, const void *b0)
{
struct sort_criteria *sort_crit = sort_criteria;
const struct paste_buffer *const *a = a0;
const struct paste_buffer *const *b = b0;
const struct paste_buffer *pa = *a;
const struct paste_buffer *pb = *b;
int result = 0;
switch (sort_crit->order) {
case SORT_NAME:
result = strcmp(pa->name, pb->name);
break;
case SORT_CREATION:
result = pa->order - pb->order;
break;
case SORT_SIZE:
result = pa->size - pb->size;
break;
case SORT_ACTIVITY:
case SORT_INDEX:
case SORT_MODIFIER:
case SORT_ORDER:
case SORT_END:
break;
}
if (result == 0)
result = strcmp(pa->name, pb->name);
if (sort_crit->reversed)
result = -result;
return (result);
}
static int
sort_client_cmp(const void *a0, const void *b0)
{
struct sort_criteria *sort_crit = sort_criteria;
const struct client *const *a = a0;
const struct client *const *b = b0;
const struct client *ca = *a;
const struct client *cb = *b;
int result = 0;
switch (sort_crit->order) {
case SORT_NAME:
result = strcmp(ca->name, cb->name);
break;
case SORT_SIZE:
result = ca->tty.sx - cb->tty.sx;
if (result == 0)
result = ca->tty.sy - cb->tty.sy;
break;
case SORT_CREATION:
if (timercmp(&ca->creation_time, &cb->creation_time, >))
result = 1;
else if (timercmp(&ca->creation_time, &cb->creation_time, <))
result = -1;
break;
case SORT_ACTIVITY:
if (timercmp(&ca->activity_time, &cb->activity_time, >))
result = -1;
else if (timercmp(&ca->activity_time, &cb->activity_time, <))
result = 1;
break;
case SORT_INDEX:
case SORT_MODIFIER:
case SORT_ORDER:
case SORT_END:
break;
}
if (result == 0)
result = strcmp(ca->name, cb->name);
if (sort_crit->reversed)
result = -result;
return (result);
}
static int
sort_session_cmp(const void *a0, const void *b0)
{
struct sort_criteria *sort_crit = sort_criteria;
const struct session *const *a = a0;
const struct session *const *b = b0;
const struct session *sa = *a;
const struct session *sb = *b;
int result = 0;
switch (sort_crit->order) {
case SORT_INDEX:
result = sa->id - sb->id;
break;
case SORT_CREATION:
if (timercmp(&sa->creation_time, &sb->creation_time, >)) {
result = 1;
break;
}
if (timercmp(&sa->creation_time, &sb->creation_time, <)) {
result = -1;
break;
}
break;
case SORT_ACTIVITY:
if (timercmp(&sa->activity_time, &sb->activity_time, >)) {
result = -1;
break;
}
if (timercmp(&sa->activity_time, &sb->activity_time, <)) {
result = 1;
break;
}
break;
case SORT_NAME:
result = strcmp(sa->name, sb->name);
break;
case SORT_MODIFIER:
case SORT_ORDER:
case SORT_SIZE:
case SORT_END:
break;
}
if (result == 0)
result = strcmp(sa->name, sb->name);
if (sort_crit->reversed)
result = -result;
return (result);
}
static int
sort_pane_cmp(const void *a0, const void *b0)
{
struct sort_criteria *sort_crit = sort_criteria;
struct window_pane *a = *(struct window_pane **)a0;
struct window_pane *b = *(struct window_pane **)b0;
int result = 0;
u_int ai, bi;
switch (sort_crit->order) {
case SORT_ACTIVITY:
result = a->active_point - b->active_point;
break;
case SORT_CREATION:
result = a->id - b->id;
break;
case SORT_SIZE:
result = a->sx * a->sy - b->sx * b->sy;
break;
case SORT_INDEX:
window_pane_index(a, &ai);
window_pane_index(b, &bi);
result = ai - bi;
break;
case SORT_NAME:
result = strcmp(a->screen->title, b->screen->title);
break;
case SORT_MODIFIER:
case SORT_ORDER:
case SORT_END:
break;
}
if (result == 0)
result = strcmp(a->screen->title, b->screen->title);
if (sort_crit->reversed)
result = -result;
return (result);
}
static int
sort_winlink_cmp(const void *a0, const void *b0)
{
struct sort_criteria *sort_crit = sort_criteria;
const struct winlink *const *a = a0;
const struct winlink *const *b = b0;
const struct winlink *wla = *a;
const struct winlink *wlb = *b;
struct window *wa = wla->window;
struct window *wb = wlb->window;
int result = 0;
switch (sort_crit->order) {
case SORT_INDEX:
result = wla->idx - wlb->idx;
break;
case SORT_CREATION:
if (timercmp(&wa->creation_time, &wb->creation_time, >)) {
result = -1;
break;
}
if (timercmp(&wa->creation_time, &wb->creation_time, <)) {
result = 1;
break;
}
break;
case SORT_ACTIVITY:
if (timercmp(&wa->activity_time, &wb->activity_time, >)) {
result = -1;
break;
}
if (timercmp(&wa->activity_time, &wb->activity_time, <)) {
result = 1;
break;
}
break;
case SORT_NAME:
result = strcmp(wa->name, wb->name);
break;
case SORT_SIZE:
result = wa->sx * wa->sy - wb->sx * wb->sy;
break;
case SORT_MODIFIER:
case SORT_ORDER:
case SORT_END:
break;
}
if (result == 0)
result = strcmp(wa->name, wb->name);
if (sort_crit->reversed)
result = -result;
return (result);
}
static int
sort_key_binding_cmp(const void *a0, const void *b0)
{
struct sort_criteria *sort_crit = sort_criteria;
const struct key_binding *a = *(struct key_binding **)a0;
const struct key_binding *b = *(struct key_binding **)b0;
int result = 0;
switch (sort_crit->order) {
case SORT_INDEX:
result = a->key - b->key;
break;
case SORT_MODIFIER:
result = (a->key & KEYC_MASK_MODIFIERS) -
(b->key & KEYC_MASK_MODIFIERS);
break;
case SORT_NAME:
result = strcasecmp(a->tablename, b->tablename) == 0;
break;
case SORT_ACTIVITY:
case SORT_CREATION:
case SORT_ORDER:
case SORT_SIZE:
case SORT_END:
break;
}
if (result == 0)
result = strcasecmp(a->tablename, b->tablename) == 0;
if (sort_crit->reversed)
result = -result;
return (result);
}
void
sort_next_order(struct sort_criteria *sort_crit)
{
u_int i;
if (sort_crit->order_seq == NULL)
return;
for (i = 0; sort_crit->order_seq[i] != SORT_END; i++) {
if (sort_crit->order == sort_crit->order_seq[i])
break;
}
if (sort_crit->order_seq[i] == SORT_END)
i = 0;
else {
i++;
if (sort_crit->order_seq[i] == SORT_END)
i = 0;
}
sort_crit->order = sort_crit->order_seq[i];
}
enum sort_order
sort_order_from_string(const char* order)
{
if (order != NULL) {
if (strcasecmp(order, "activity") == 0)
return (SORT_ACTIVITY);
if (strcasecmp(order, "creation") == 0)
return (SORT_CREATION);
if (strcasecmp(order, "index") == 0 ||
strcasecmp(order, "key") == 0)
return (SORT_INDEX);
if (strcasecmp(order, "modifier") == 0)
return (SORT_MODIFIER);
if (strcasecmp(order, "name") == 0 ||
strcasecmp(order, "title") == 0)
return (SORT_NAME);
if (strcasecmp(order, "order") == 0)
return (SORT_ORDER);
if (strcasecmp(order, "size") == 0)
return (SORT_SIZE);
}
return (SORT_END);
}
const char *
sort_order_to_string(enum sort_order order)
{
if (order == SORT_ACTIVITY)
return "activity";
if (order == SORT_CREATION)
return "creation";
if (order == SORT_INDEX)
return "index";
if (order == SORT_MODIFIER)
return "modifier";
if (order == SORT_NAME)
return "name";
if (order == SORT_ORDER)
return "order";
if (order == SORT_SIZE)
return "size";
return (NULL);
}
int
sort_would_window_tree_swap(struct sort_criteria *sort_crit,
struct winlink *wla, struct winlink *wlb)
{
if (sort_crit->order == SORT_INDEX)
return (0);
sort_criteria = sort_crit;
return (sort_winlink_cmp(&wla, &wlb) != 0);
}
struct paste_buffer **
sort_get_buffers(u_int *n, struct sort_criteria *sort_crit)
{
struct paste_buffer *pb = NULL;
u_int i;
static struct paste_buffer **l = NULL;
static u_int lsz = 0;
i = 0;
while ((pb = paste_walk(pb)) != NULL) {
if (lsz <= i) {
lsz += 100;
l = xreallocarray(l, lsz, sizeof *l);
}
l[i++] = pb;
}
sort_qsort(l, i, sizeof *l, sort_buffer_cmp, sort_crit);
*n = i;
return (l);
}
struct client **
sort_get_clients(u_int *n, struct sort_criteria *sort_crit)
{
struct client *c;
u_int i;
static struct client **l = NULL;
static u_int lsz = 0;
i = 0;
TAILQ_FOREACH(c, &clients, entry) {
if (lsz <= i) {
lsz += 100;
l = xreallocarray(l, lsz, sizeof *l);
}
l[i++] = c;
}
sort_qsort(l, i, sizeof *l, sort_client_cmp, sort_crit);
*n = i;
return (l);
}
struct session **
sort_get_sessions(u_int *n, struct sort_criteria *sort_crit)
{
struct session *s;
u_int i;
static struct session **l = NULL;
static u_int lsz = 0;
i = 0;
RB_FOREACH(s, sessions, &sessions) {
if (lsz <= i) {
lsz += 100;
l = xreallocarray(l, lsz, sizeof *l);
}
l[i++] = s;
}
sort_qsort(l, i, sizeof *l, sort_session_cmp, sort_crit);
*n = i;
return (l);
}
struct window_pane **
sort_get_panes(u_int *n, struct sort_criteria *sort_crit)
{
struct session *s;
struct winlink *wl;
struct window *w;
struct window_pane *wp;
u_int i;
static struct window_pane **l = NULL;
static u_int lsz = 0;
i = 0;
RB_FOREACH(s, sessions, &sessions) {
RB_FOREACH(wl, winlinks, &s->windows) {
w = wl->window;
TAILQ_FOREACH(wp, &w->panes, entry) {
if (lsz <= i) {
lsz += 100;
l = xreallocarray(l, lsz, sizeof *l);
}
l[i++] = wp;
}
}
}
sort_qsort(l, i, sizeof *l, sort_pane_cmp, sort_crit);
*n = i;
return (l);
}
struct window_pane **
sort_get_panes_session(struct session *s, u_int *n,
struct sort_criteria *sort_crit)
{
struct winlink *wl = NULL;
struct window *w = NULL;
struct window_pane *wp = NULL;
u_int i;
static struct window_pane **l = NULL;
static u_int lsz = 0;
i = 0;
RB_FOREACH(wl, winlinks, &s->windows) {
TAILQ_FOREACH(wp, &w->panes, entry) {
if (lsz <= i) {
lsz += 100;
l = xreallocarray(l, lsz, sizeof *l);
}
l[i++] = wp;
}
}
sort_qsort(l, i, sizeof *l, sort_pane_cmp, sort_crit);
*n = i;
return (l);
}
struct window_pane **
sort_get_panes_window(struct window *w, u_int *n,
struct sort_criteria *sort_crit)
{
struct window_pane *wp;
u_int i;
static struct window_pane **l = NULL;
static u_int lsz = 0;
i = 0;
TAILQ_FOREACH(wp, &w->panes, entry) {
if (lsz <= i) {
lsz += 100;
l = xreallocarray(l, lsz, sizeof *l);
}
l[i++] = wp;
}
sort_qsort(l, i, sizeof *l, sort_pane_cmp, sort_crit);
*n = i;
return (l);
}
struct winlink **
sort_get_winlinks(u_int *n, struct sort_criteria *sort_crit)
{
struct session *s;
struct winlink *wl;
u_int i;
static struct winlink **l = NULL;
static u_int lsz = 0;
i = 0;
RB_FOREACH(s, sessions, &sessions) {
RB_FOREACH(wl, winlinks, &s->windows) {
if (lsz <= i) {
lsz += 100;
l = xreallocarray(l, lsz, sizeof *l);
}
l[i++] = wl;
}
}
sort_qsort(l, i, sizeof *l, sort_winlink_cmp, sort_crit);
*n = i;
return (l);
}
struct winlink **
sort_get_winlinks_session(struct session *s, u_int *n,
struct sort_criteria *sort_crit)
{
struct winlink *wl;
u_int i;
static struct winlink **l = NULL;
static u_int lsz = 0;
i = 0;
RB_FOREACH(wl, winlinks, &s->windows) {
if (lsz <= i) {
lsz += 100;
l = xreallocarray(l, lsz, sizeof *l);
}
l[i++] = wl;
}
sort_qsort(l, i, sizeof *l, sort_winlink_cmp, sort_crit);
*n = i;
return (l);
}
struct key_binding **
sort_get_key_bindings(u_int *n, struct sort_criteria *sort_crit)
{
struct key_table *table;
struct key_binding *bd;
u_int i = 0;
static struct key_binding **l = NULL;
static u_int lsz = 0;
table = key_bindings_first_table();
while (table != NULL) {
bd = key_bindings_first(table);
while (bd != NULL) {
if (lsz <= i) {
lsz += 100;
l = xreallocarray(l, lsz, sizeof *l);
}
l[i++] = bd;
bd = key_bindings_next(table, bd);
}
table = key_bindings_next_table(table);
}
sort_qsort(l, i, sizeof *l, sort_key_binding_cmp, sort_crit);
*n = i;
return (l);
}
struct key_binding **
sort_get_key_bindings_table(struct key_table *table, u_int *n,
struct sort_criteria *sort_crit)
{
struct key_binding *bd;
u_int i = 0;
static struct key_binding **l = NULL;
static u_int lsz = 0;
bd = key_bindings_first(table);
while (bd != NULL) {
if (lsz <= i) {
lsz += 100;
l = xreallocarray(l, lsz, sizeof *l);
}
l[i++] = bd;
bd = key_bindings_next(table, bd);
}
sort_qsort(l, i, sizeof *l, sort_key_binding_cmp, sort_crit);
*n = i;
return (l);
}

36
spawn.c
View File

@@ -211,7 +211,9 @@ spawn_pane(struct spawn_context *sc, char **cause)
struct environ *child;
struct environ_entry *ee;
char **argv, *cp, **argvp, *argv0, *cwd, *new_cwd;
const char *cmd, *tmp;
char path[PATH_MAX];
const char *cmd, *tmp, *home = find_home();
const char *actual_cwd = NULL;
int argc;
u_int idx;
struct termios now;
@@ -366,6 +368,16 @@ spawn_pane(struct spawn_context *sc, char **cause)
goto complete;
}
/* Store current working directory and change to new one. */
if (getcwd(path, sizeof path) != NULL) {
if (chdir(new_wp->cwd) == 0)
actual_cwd = new_wp->cwd;
else if (home != NULL && chdir(home) == 0)
actual_cwd = home;
else if (chdir("/") == 0)
actual_cwd = "/";
}
/* Fork the new process. */
new_wp->pid = fdforkpty(ptm_fd, &new_wp->fd, new_wp->tty, NULL, &ws);
if (new_wp->pid == -1) {
@@ -381,8 +393,15 @@ spawn_pane(struct spawn_context *sc, char **cause)
return (NULL);
}
/* In the parent process, everything is done now. */
/*
* In the parent process, everything is done now. Change the working
* directory back.
*/
if (new_wp->pid != 0) {
if (actual_cwd != NULL &&
chdir(path) != 0 &&
(home == NULL || chdir(home) != 0))
chdir("/");
goto complete;
}
@@ -397,17 +416,10 @@ spawn_pane(struct spawn_context *sc, char **cause)
}
#endif
/*
* Child process. Change to the working directory or home if that
* fails.
* Child process. Set PWD to the working directory.
*/
if (chdir(new_wp->cwd) == 0)
environ_set(child, "PWD", 0, "%s", new_wp->cwd);
else if ((tmp = find_home()) != NULL && chdir(tmp) == 0)
environ_set(child, "PWD", 0, "%s", tmp);
else if (chdir("/") == 0)
environ_set(child, "PWD", 0, "/");
else
fatal("chdir failed");
if (actual_cwd != NULL)
environ_set(child, "PWD", 0, "%s", actual_cwd);
/*
* Update terminal escape characters from the session if available and

View File

@@ -264,14 +264,19 @@ status_line_size(struct client *c)
}
/* Get the prompt line number for client's session. 1 means at the bottom. */
static u_int
u_int
status_prompt_line_at(struct client *c)
{
struct session *s = c->session;
u_int line, lines;
if (c->flags & (CLIENT_STATUSOFF|CLIENT_CONTROL))
return (1);
return (options_get_number(s->options, "message-line"));
lines = status_line_size(c);
if (lines == 0)
return (0);
line = options_get_number(s->options, "message-line");
if (line >= lines)
return (lines - 1);
return (line);
}
/* Get window at window list position. */
@@ -799,7 +804,10 @@ status_prompt_redraw(struct client *c)
n = options_get_number(s->options, "prompt-cursor-colour");
sl->active->default_ccolour = n;
n = options_get_number(s->options, "prompt-cursor-style");
if (c->prompt_mode == PROMPT_COMMAND)
n = options_get_number(s->options, "prompt-command-cursor-style");
else
n = options_get_number(s->options, "prompt-cursor-style");
screen_set_cursor_style(n, &sl->active->default_cstyle,
&sl->active->default_mode);
@@ -931,6 +939,8 @@ status_prompt_translate_key(struct client *c, key_code key, key_code *new_key)
return (1);
case '\033': /* Escape */
c->prompt_mode = PROMPT_COMMAND;
if (c->prompt_index != 0)
c->prompt_index--;
c->flags |= CLIENT_REDRAWSTATUS;
return (0);
}
@@ -956,10 +966,11 @@ status_prompt_translate_key(struct client *c, key_code key, key_code *new_key)
*new_key = 'u'|KEYC_CTRL;
return (1);
case 'i':
case '\033': /* Escape */
c->prompt_mode = PROMPT_ENTRY;
c->flags |= CLIENT_REDRAWSTATUS;
return (0);
case '\033': /* Escape */
return (0);
}
switch (key) {
@@ -1372,6 +1383,11 @@ process_key:
break;
case KEYC_BSPACE:
case 'h'|KEYC_CTRL:
if (c->prompt_flags & PROMPT_BSPACE_EXIT && size == 0) {
if (c->prompt_inputcb(c, c->prompt_data, NULL, 1) == 0)
status_prompt_clear(c);
break;
}
if (c->prompt_index != 0) {
if (c->prompt_index == size)
c->prompt_buffer[--c->prompt_index].size = 0;
@@ -1874,7 +1890,7 @@ status_prompt_complete_window_menu(struct client *c, struct session *s,
struct winlink *wl;
char **list = NULL, *tmp;
u_int lines = status_line_size(c), height;
u_int py, size = 0;
u_int py, size = 0, i;
if (c->tty.sy - lines < 3)
return (NULL);
@@ -1953,6 +1969,9 @@ status_prompt_complete_window_menu(struct client *c, struct session *s,
BOX_LINES_DEFAULT, NULL, NULL, NULL, NULL,
status_prompt_menu_callback, spm) != 0) {
menu_free(menu);
for (i = 0; i < size; i++)
free(list[i]);
free(list);
free(spm);
return (NULL);
}

35
style.c
View File

@@ -218,20 +218,23 @@ style_parse(struct style *sy, const struct grid_cell *base, const char *in)
sy->gc.attr = 0;
else if (end > 2 && strncasecmp(tmp, "no", 2) == 0) {
if (strcmp(tmp + 2, "attr") == 0)
value = 0xffff & ~GRID_ATTR_CHARSET;
else if ((value = attributes_fromstring(tmp + 2)) == -1)
goto error;
sy->gc.attr &= ~value;
sy->gc.attr |= GRID_ATTR_NOATTR;
else {
value = attributes_fromstring(tmp + 2);
if (value == -1)
goto error;
sy->gc.attr &= ~value;
}
} else if (end > 6 && strncasecmp(tmp, "width=", 6) == 0) {
n = strtonum(tmp + 6, 0, UINT_MAX, &errstr);
if (errstr != NULL)
goto error;
sy->width = (int)n;
n = strtonum(tmp + 6, 0, UINT_MAX, &errstr);
if (errstr != NULL)
goto error;
sy->width = (int)n;
} else if (end > 4 && strncasecmp(tmp, "pad=", 4) == 0) {
n = strtonum(tmp + 4, 0, UINT_MAX, &errstr);
if (errstr != NULL)
goto error;
sy->pad = (int)n;
n = strtonum(tmp + 4, 0, UINT_MAX, &errstr);
if (errstr != NULL)
goto error;
sy->pad = (int)n;
} else {
if ((value = attributes_fromstring(tmp)) == -1)
goto error;
@@ -344,13 +347,13 @@ style_tostring(struct style *sy)
attributes_tostring(gc->attr));
comma = ",";
}
if (sy->width >= 0) {
xsnprintf(s + off, sizeof s - off, "%swidth=%u", comma,
if (sy->width >= 0) {
xsnprintf(s + off, sizeof s - off, "%swidth=%u", comma,
sy->width);
comma = ",";
}
if (sy->pad >= 0) {
xsnprintf(s + off, sizeof s - off, "%spad=%u", comma,
if (sy->pad >= 0) {
xsnprintf(s + off, sizeof s - off, "%spad=%u", comma,
sy->pad);
comma = ",";
}

256
tmux.1
View File

@@ -841,6 +841,7 @@ Each has a single-character alternative form.
.It Li "{last}" Ta "!" Ta "The last (previously current) window"
.It Li "{next}" Ta "+" Ta "The next window by number"
.It Li "{previous}" Ta "-" Ta "The previous window by number"
.It Li "{current}" Ta "@" Ta "The current window"
.El
.Pp
.Ar target-pane
@@ -873,6 +874,7 @@ The following special tokens are available for the pane index:
.It Li "{down-of}" Ta "" Ta "The pane below the active pane"
.It Li "{left-of}" Ta "" Ta "The pane to the left of the active pane"
.It Li "{right-of}" Ta "" Ta "The pane to the right of the active pane"
.It Li "{active}" Ta "@" Ta "The active pane"
.El
.Pp
The tokens
@@ -1183,8 +1185,10 @@ flag clears alerts (bell, activity, or silence) in all windows linked to the
session.
.Tg lsc
.It Xo Ic list-clients
.Op Fl r
.Op Fl F Ar format
.Op Fl f Ar filter
.Op Fl O Ar sort-order
.Op Fl t Ar target-session
.Xc
.D1 Pq alias: Ic lsc
@@ -1197,6 +1201,16 @@ Only clients for which the filter is true are shown.
See the
.Sx FORMATS
section.
.Fl O
specifies the sort order: one of
.Ql name ,
.Ql size ,
.Ql creation
(time), or
.Ql activity
(time).
.Fl r
reverses the sort order.
If
.Ar target-session
is specified, list only clients connected to that session.
@@ -1212,8 +1226,10 @@ or - if omitted - of all commands supported by
.Nm .
.Tg ls
.It Xo Ic list-sessions
.Op Fl r
.Op Fl F Ar format
.Op Fl f Ar filter
.Op Fl O Ar sort-order
.Xc
.D1 Pq alias: Ic ls
List all sessions managed by the server.
@@ -1225,6 +1241,16 @@ Only sessions for which the filter is true are shown.
See the
.Sx FORMATS
section.
.Fl O
specifies the sort order: one of
.Ql index ,
.Ql name ,
.Ql creation
(time), or
.Ql activity
(time).
.Fl r
reverses the sort order.
.Tg lockc
.It Ic lock-client Op Fl t Ar target-client
.D1 Pq alias: Ic lockc
@@ -1363,12 +1389,11 @@ and sets an environment variable for the newly created session; it may be
specified multiple times.
.Tg refresh
.It Xo Ic refresh-client
.Op Fl cDLRSU
.Op Fl cDlLRSU
.Op Fl A Ar pane:state
.Op Fl B Ar name:what:format
.Op Fl C Ar size
.Op Fl f Ar flags
.Op Fl l Op Ar target-pane
.Op Fl r Ar pane:report
.Op Fl t Ar target-client
.Op Ar adjustment
@@ -1489,11 +1514,7 @@ a colon, then a report escape sequence.
.Fl l
requests the clipboard from the client using the
.Xr xterm 1
escape sequence.
If
.Ar target-pane
is given, the clipboard is sent (in encoded form), otherwise it is stored in a
new paste buffer.
escape sequence and stores it in a new paste buffer.
.Pp
.Fl L ,
.Fl R ,
@@ -1626,6 +1647,7 @@ Suspend a client by sending
.It Xo Ic switch-client
.Op Fl ElnprZ
.Op Fl c Ar target-client
.Op Fl O Ar sort-order
.Op Fl t Ar target-session
.Op Fl T Ar key-table
.Xc
@@ -1652,6 +1674,18 @@ or
.Fl p
is used, the client is moved to the last, next or previous session
respectively.
.Fl O
may be used with
.Fl n
and
.Fl p
to specify the field to sort on: one of
.Ql name ,
.Ql size ,
.Ql creation
(time), or
.Ql activity
(time).
.Fl r
toggles the client
.Ic read-only
@@ -1668,9 +1702,10 @@ is used,
option will not be applied.
.Pp
.Fl T
sets the client's key table; the next key from the client will be interpreted
from
sets the client's key table; the next key will be looked up using
.Ar key-table .
After that key, the client is returned to its default key table (normally
.Em root ) .
This may be used to configure multiple prefix keys, or to bind commands to
sequences of keys.
For example, to make typing
@@ -2212,12 +2247,31 @@ Same as
.Ic scroll-down
but also exit copy mode if the cursor reaches the bottom.
.It Xo
.Ic scroll-exit-on
.Xc
Turn on exiting copy mode when scrolling to the end of the buffer.
.It Xo
.Ic scroll-exit-off
.Xc
Turn off exiting copy mode when scrolling to the end of the buffer.
.It Xo
.Ic scroll-exit-toggle
.Xc
Toggle exiting copy mode when scrolling to the end of the buffer.
.It Xo
.Ic scroll-middle
(vi: z)
.Xc
Scroll so that the current line becomes the middle one while keeping the
cursor on that line.
.It Xo
.Ic scroll-to-mouse
.Xc
Scroll pane in copy-mode when bound to a mouse drag event.
.Fl e
causes copy mode to exit when at the bottom.
.Pp
.It Xo
.Ic scroll-top
.Xc
Scroll down until the current line is at the top while keeping the cursor on
@@ -2454,12 +2508,10 @@ cancels copy mode and any other modes.
.Fl M
begins a mouse drag (only valid if bound to a mouse key binding, see
.Sx MOUSE SUPPORT ) .
.Pp
.Fl S
scrolls when bound to a mouse drag event; for example,
.Ic copy-mode -Se
is bound to
.Ar MouseDrag1ScrollbarSlider
by default.
enters copy-mode and scrolls when bound to a mouse drag event; See
.Ic scroll-to-mouse .
.Pp
.Fl s
copies from
@@ -2666,7 +2718,7 @@ The following keys may be used in client mode:
.It Li "z" Ta "Suspend selected client"
.It Li "Z" Ta "Suspend tagged clients"
.It Li "f" Ta "Enter a format to filter items"
.It Li "O" Ta "Change sort field"
.It Li "O" Ta "Change sort order"
.It Li "r" Ta "Reverse sort order"
.It Li "v" Ta "Toggle preview"
.It Li "q" Ta "Exit mode"
@@ -2682,12 +2734,11 @@ If
is not given, "detach-client -t \[aq]%%\[aq]" is used.
.Pp
.Fl O
specifies the initial sort field: one of
specifies the initial sort order: one of
.Ql name ,
.Ql size ,
.Ql creation
(time),
or
(time), or
.Ql activity
(time).
.Fl r
@@ -2754,7 +2805,7 @@ The following keys may be used in tree mode:
.It Li "\&:" Ta "Run a command for each tagged item"
.It Li "f" Ta "Enter a format to filter items"
.It Li "H" Ta "Jump to the starting pane"
.It Li "O" Ta "Change sort field"
.It Li "O" Ta "Change sort order"
.It Li "r" Ta "Reverse sort order"
.It Li "v" Ta "Toggle preview"
.It Li "q" Ta "Exit mode"
@@ -2772,12 +2823,12 @@ If
is not given, "switch-client -t \[aq]%%\[aq]" is used.
.Pp
.Fl O
specifies the initial sort field: one of
specifies the initial sort order: one of
.Ql index ,
.Ql name ,
or
.Ql time
(activity).
.Ql activity
(time).
.Fl r
reverses the sort order.
.Fl f
@@ -3023,9 +3074,10 @@ If
is given, the newly linked window is not selected.
.Tg lsp
.It Xo Ic list-panes
.Op Fl as
.Op Fl ars
.Op Fl F Ar format
.Op Fl f Ar filter
.Op Fl O Ar sort-order
.Op Fl t Ar target
.Xc
.D1 Pq alias: Ic lsp
@@ -3050,11 +3102,25 @@ Only panes for which the filter is true are shown.
See the
.Sx FORMATS
section.
.Fl O
specifies the sort order: one of
.Ql name
(title),
.Ql index ,
.Ql size
(area),
.Ql creation
(time), or
.Ql activity
(time).
.Fl r
reverses the sort order.
.Tg lsw
.It Xo Ic list-windows
.Op Fl a
.Op Fl ar
.Op Fl F Ar format
.Op Fl f Ar filter
.Op Fl O Ar sort-order
.Op Fl t Ar target-session
.Xc
.D1 Pq alias: Ic lsw
@@ -3071,6 +3137,19 @@ Only windows for which the filter is true are shown.
See the
.Sx FORMATS
section.
.Fl O
specifies the sort order: one of
.Ql index ,
.Ql name ,
.Ql size
(area),
.Ql creation
(time),
or
.Ql activity
(time).
.Fl r
reverses the sort order.
.Tg movep
.It Xo Ic move-pane
.Op Fl bdfhv
@@ -3760,24 +3839,45 @@ To view the default bindings and possible commands, see the
command.
.Tg lsk
.It Xo Ic list-keys
.Op Fl 1aN
.Op Fl 1aNr
.Op Fl F Ar format
.Op Fl O Ar sort-order
.Op Fl P Ar prefix-string
.Op Fl T Ar key-table
.Op Ar key
.Xc
.D1 Pq alias: Ic lsk
List key bindings.
There are two forms: the default lists keys as
.Fl F
specifies the format of each line.
See the
.Sx FORMATS
section.
.Fl T
specifies a
.Ar key-table
to list from.
.Fl 1
lists only the first matching key.
.Fl O
specifies the sort order: one of
.Ql key ,
.Ql modifier ,
.Ql name
(table name).
.Fl r
reverses the sort order.
.Pp
If no
.Ar format
is given, there are two forms: the default lists keys as
.Ic bind-key
commands;
.Fl N
lists only keys with attached notes and shows only the key and note for each
key.
.Pp
With the default form, all key tables are listed by default.
.Fl T
lists only keys in
.Ar key-table .
With the default form, all key tables are listed unless specified otherwise.
.Pp
With the
.Fl N
@@ -3790,9 +3890,7 @@ key tables are listed by default;
also lists only keys in
.Ar key-table .
.Fl P
specifies a prefix to print before each key and
.Fl 1
lists only the first matching key.
specifies a prefix to print before each key.
.Fl a
lists the command for keys that do not have a note rather than skipping them.
.Tg send
@@ -4241,6 +4339,38 @@ passed through to applications running in
.Nm .
Attached clients should be detached and attached again after changing this
option.
.It Xo Ic focus-follows-mouse
.Op Ic on | off
.Xc
When enabled and
.Ic mouse
is on, moving the mouse into a pane selects it.
.It Xo Ic get-clipboard
.Op Ic both | request | buffer | off
.Xc
Controls the behaviour when an application requests the clipboard from
.Nm .
.Pp
If
.Ic off ,
the request is ignored;
if
.Ic buffer ,
.Nm
responds with the newest paste buffer;
.Ic request
causes
.Nm
to request the clipboard from the most recently used client (if possible) and
send the reply (if any) back to the application;
.Ic buffer
is the same as
.Ic request
but also creates a paste buffer.
.Pp
See also the
.Ic set-clipboard
option.
.It Ic history-file Ar path
If not empty, a file to which
.Nm
@@ -4550,9 +4680,7 @@ If set to 0, messages and indicators are displayed until a key is pressed.
.Ar time
is in milliseconds.
.It Ic history-limit Ar lines
Set the maximum number of lines held in window history.
This setting applies only to new windows - existing window histories are not
resized and retain the limit at the point they were created.
Set the maximum number of lines held in pane history.
.It Ic initial-repeat-time Ar time
Set the time in milliseconds for the initial repeat when a key is bound with the
.Fl r
@@ -4669,6 +4797,12 @@ Set the style of the cursor in the command prompt.
See the
.Ic cursor-style
options for available styles.
.It Ic prompt-command-cursor-style Ar style
Set the style of the cursor in the command prompt when vi keys are enabled and
the prompt is in command mode.
See the
.Ic cursor-style
options for available styles.
.It Xo Ic renumber-windows
.Op Ic on | off
.Xc
@@ -6132,10 +6266,19 @@ The following variables are available, where appropriate:
.It Li "hook_window_name" Ta "" Ta "Name of window where hook was run, if any"
.It Li "host" Ta "#H" Ta "Hostname of local host"
.It Li "host_short" Ta "#h" Ta "Hostname of local host (no domain name)"
.It Li "image_support" Ta "" Ta "Returns the string of the support image (sixel, kitty)"
.It Li "insert_flag" Ta "" Ta "Pane insert flag"
.It Li "key_string" Ta "" Ta "String representation of the key binding"
.It Li "key_repeat" Ta "" Ta "1 if key binding is repeatable"
.It Li "key_note" Ta "" Ta "Note of the key binding"
.It Li "key_prefix" Ta "" Ta "Global key prefix"
.It Li "key_table" Ta "" Ta "Table name of the key binding"
.It Li "key_command" Ta "" Ta "Command of the key binding"
.It Li "key_has_repeat" Ta "" Ta "1 if list contain a repeatable key"
.It Li "key_string_width" Ta "" Ta "Maximum key_string width in list"
.It Li "key_table_width" Ta "" Ta "Maximum key_table width in list"
.It Li "keypad_cursor_flag" Ta "" Ta "Pane keypad cursor flag"
.It Li "keypad_flag" Ta "" Ta "Pane keypad flag"
.It Li "last_session_index" Ta "" Ta "Index of last session"
.It Li "last_window_index" Ta "" Ta "Index of last window in session"
.It Li "line" Ta "" Ta "Line number in the list"
.It Li "loop_last_flag" Ta "" Ta "1 if last window, pane, session, client in the W:, P:, S:, or L: loop"
@@ -6183,6 +6326,7 @@ The following variables are available, where appropriate:
.It Li "pane_path" Ta "" Ta "Path of pane (can be set by application)"
.It Li "pane_pid" Ta "" Ta "PID of first process in pane"
.It Li "pane_pipe" Ta "" Ta "1 if pane is being piped"
.It Li "pane_pipe_pid" Ta "" Ta "PID of pipe process, if any"
.It Li "pane_right" Ta "" Ta "Right of pane"
.It Li "pane_search_string" Ta "" Ta "Last search string in copy mode"
.It Li "pane_start_command" Ta "" Ta "Command pane started with"
@@ -6206,6 +6350,7 @@ The following variables are available, where appropriate:
.It Li "selection_active" Ta "" Ta "1 if selection started and changes with the cursor in copy mode"
.It Li "selection_end_x" Ta "" Ta "X position of the end of the selection"
.It Li "selection_end_y" Ta "" Ta "Y position of the end of the selection"
.It Li "selection_mode" Ta "" Ta "Selection mode"
.It Li "selection_present" Ta "" Ta "1 if selection started in copy mode"
.It Li "selection_start_x" Ta "" Ta "X position of the start of the selection"
.It Li "selection_start_y" Ta "" Ta "Y position of the start of the selection"
@@ -6227,7 +6372,6 @@ The following variables are available, where appropriate:
.It Li "session_group_size" Ta "" Ta "Size of session group"
.It Li "session_grouped" Ta "" Ta "1 if session in a group"
.It Li "session_id" Ta "" Ta "Unique session ID"
.It Li "session_index" Ta "" Ta "Index of session"
.It Li "session_last_attached" Ta "" Ta "Time session last attached"
.It Li "session_many_attached" Ta "" Ta "1 if multiple clients attached"
.It Li "session_marked" Ta "" Ta "1 if this session contains the marked pane"
@@ -6237,8 +6381,8 @@ The following variables are available, where appropriate:
.It Li "session_stack" Ta "" Ta "Window indexes in most recent order"
.It Li "session_windows" Ta "" Ta "Number of windows in session"
.It Li "socket_path" Ta "" Ta "Server socket path"
.It Li "sixel_support" Ta "" Ta "1 if server has support for SIXEL"
.It Li "start_time" Ta "" Ta "Server start time"
.It Li "synchronized_output_flag" Ta "" Ta "1 if pane has synchronized output enabled"
.It Li "uid" Ta "" Ta "Server UID"
.It Li "user" Ta "" Ta "Server user"
.It Li "version" Ta "" Ta "Server version"
@@ -6677,7 +6821,7 @@ See
for possible values for
.Ar prompt-type .
.It Xo Ic command-prompt
.Op Fl 1bFiklN
.Op Fl 1beFiklN
.Op Fl I Ar inputs
.Op Fl p Ar prompts
.Op Fl t Ar target-client
@@ -6750,6 +6894,10 @@ makes the prompt only accept numeric key presses.
.Fl i
executes the command every time the prompt input changes instead of when the
user exits the command prompt.
.Fl e
makes
.Em BSpace
cancel an empty prompt.
.Pp
.Fl T
tells
@@ -7204,7 +7352,7 @@ The following keys may be used in buffer mode:
.It Li "D" Ta "Delete tagged buffers"
.It Li "e" Ta "Open the buffer in an editor"
.It Li "f" Ta "Enter a format to filter items"
.It Li "O" Ta "Change sort field"
.It Li "O" Ta "Change sort order"
.It Li "r" Ta "Reverse sort order"
.It Li "v" Ta "Toggle preview"
.It Li "q" Ta "Exit mode"
@@ -7220,10 +7368,10 @@ If
is not given, "paste-buffer -p -b \[aq]%%\[aq]" is used.
.Pp
.Fl O
specifies the initial sort field: one of
.Ql time
(creation),
.Ql name
specifies the initial sort order: one of
.Ql creation
(time),
.Ql name ,
or
.Ql size .
.Fl r
@@ -7256,8 +7404,10 @@ Delete the buffer named
or the most recently added automatically named buffer if not specified.
.Tg lsb
.It Xo Ic list-buffers
.Op Fl r
.Op Fl F Ar format
.Op Fl f Ar filter
.Op Fl O Ar sort-order
.Xc
.D1 Pq alias: Ic lsb
List the global buffers.
@@ -7269,6 +7419,15 @@ Only buffers for which the filter is true are shown.
See the
.Sx FORMATS
section.
.Fl O
specifies the sort order: one of
.Ql name ,
.Ql size ,
or
.Ql creation
(time).
.Fl r
reverses the sort order.
.It Xo Ic load-buffer
.Op Fl w
.Op Fl b Ar buffer-name
@@ -7293,7 +7452,7 @@ is
the contents are read from stdin.
.Tg pasteb
.It Xo Ic paste-buffer
.Op Fl dpr
.Op Fl dprS
.Op Fl b Ar buffer-name
.Op Fl s Ar separator
.Op Fl t Ar target-pane
@@ -7301,9 +7460,14 @@ the contents are read from stdin.
.D1 Pq alias: Ic pasteb
Insert the contents of a paste buffer into the specified pane.
If not specified, paste into the current one.
By default, control characters are sanitized with
.Xr vis 3 ;
.Fl S
disables this.
With
.Fl d ,
also delete the paste buffer.
.Pp
When output, any linefeed (LF) characters in the paste buffer are replaced with
a separator, by default carriage return (CR).
A custom separator may be specified using the

261
tmux.h
View File

@@ -68,10 +68,19 @@ struct screen_write_cline;
struct screen_write_ctx;
struct session;
#ifdef ENABLE_SIXEL
/* Convenience macro: defined if any image protocol is compiled in. */
#if defined(ENABLE_SIXEL_IMAGES) || defined(ENABLE_KITTY_IMAGES)
#define ENABLE_IMAGES
#endif
#ifdef ENABLE_SIXEL_IMAGES
struct sixel_image;
#endif
#ifdef ENABLE_KITTY_IMAGES
struct kitty_image;
#endif
struct tty_ctx;
struct tty_code;
struct tty_key;
@@ -652,6 +661,7 @@ enum tty_code_code {
#define MODE_CURSOR_BLINKING_SET 0x20000
#define MODE_KEYS_EXTENDED_2 0x40000
#define MODE_THEME_UPDATES 0x80000
#define MODE_SYNC 0x100000
#define ALL_MODES 0xffffff
#define ALL_MOUSE_MODES (MODE_MOUSE_STANDARD|MODE_MOUSE_BUTTON|MODE_MOUSE_ALL)
@@ -727,6 +737,7 @@ struct colour_palette {
#define GRID_ATTR_UNDERSCORE_4 0x800
#define GRID_ATTR_UNDERSCORE_5 0x1000
#define GRID_ATTR_OVERLINE 0x2000
#define GRID_ATTR_NOATTR 0x4000
/* All underscore attributes. */
#define GRID_ATTR_ALL_UNDERSCORE \
@@ -923,11 +934,25 @@ struct style {
enum style_default_type default_type;
};
#ifdef ENABLE_SIXEL
#ifdef ENABLE_IMAGES
/* Image types. */
enum image_type {
IMAGE_SIXEL,
IMAGE_KITTY
};
/* Image. */
struct image {
enum image_type type;
struct screen *s;
struct sixel_image *data;
union {
#ifdef ENABLE_SIXEL_IMAGES
struct sixel_image *sixel;
#endif
#ifdef ENABLE_KITTY_IMAGES
struct kitty_image *kitty;
#endif
} data;
char *fallback;
u_int px;
@@ -954,7 +979,7 @@ struct screen_sel;
struct screen_titles;
struct screen {
char *title;
char *path;
char *path;
struct screen_titles *titles;
struct grid *grid; /* grid data */
@@ -982,7 +1007,7 @@ struct screen {
bitstr_t *tabs;
struct screen_sel *sel;
#ifdef ENABLE_SIXEL
#ifdef ENABLE_IMAGES
struct images images;
struct images saved_images;
#endif
@@ -1099,6 +1124,7 @@ struct window_mode {
void (*free)(struct window_mode_entry *);
void (*resize)(struct window_mode_entry *, u_int, u_int);
void (*update)(struct window_mode_entry *);
void (*style_changed)(struct window_mode_entry *);
void (*key)(struct window_mode_entry *, struct client *,
struct session *, struct winlink *, key_code,
struct mouse_event *);
@@ -1129,6 +1155,7 @@ struct window_mode_entry {
/* Type of request to client. */
enum input_request_type {
INPUT_REQUEST_PALETTE,
INPUT_REQUEST_CLIPBOARD,
INPUT_REQUEST_QUEUE
};
@@ -1138,6 +1165,13 @@ struct input_request_palette_data {
int c;
};
/* Clipboard request reply data. */
struct input_request_clipboard_data {
char *buf;
size_t len;
char clip;
};
/* Request sent to client on behalf of pane. */
TAILQ_HEAD(input_requests, input_request);
@@ -1158,6 +1192,16 @@ struct window_pane_resize {
};
TAILQ_HEAD(window_pane_resizes, window_pane_resize);
/*
* Client theme, this is worked out from the background colour if not reported
* by terminal.
*/
enum client_theme {
THEME_UNKNOWN,
THEME_LIGHT,
THEME_DARK
};
/* Child window structure. */
struct window_pane {
u_int id;
@@ -1214,14 +1258,17 @@ struct window_pane {
struct window_pane_resizes resize_queue;
struct event resize_timer;
struct event sync_timer;
struct input_ctx *ictx;
struct grid_cell cached_gc;
struct grid_cell cached_active_gc;
struct colour_palette palette;
enum client_theme last_theme;
int pipe_fd;
pid_t pipe_pid;
struct bufferevent *pipe_event;
struct window_pane_offset pipe_offset;
@@ -1264,6 +1311,7 @@ struct window {
struct event offset_timer;
struct timeval activity_time;
struct timeval creation_time;
struct window_pane *active;
struct window_panes last_panes;
@@ -1511,6 +1559,19 @@ struct key_event {
size_t len;
};
/* Visible range array element. */
struct visible_range {
u_int px; /* start */
u_int nx; /* length */
};
/* Visible areas not obstructed. */
struct visible_ranges {
struct visible_range *ranges; /* dynamically allocated array */
u_int used; /* number of entries in ranges */
u_int size; /* allocated capacity of ranges */
};
/* Terminal definition. */
struct tty_term {
char *name;
@@ -1528,6 +1589,7 @@ struct tty_term {
#define TERM_RGBCOLOURS 0x10
#define TERM_VT100LIKE 0x20
#define TERM_SIXEL 0x40
#define TERM_KITTY 0x80
int flags;
LIST_ENTRY(tty_term) entry;
@@ -1578,6 +1640,7 @@ struct tty {
size_t discarded;
struct termios tio;
struct visible_ranges r;
struct grid_cell cell;
struct grid_cell last_cell;
@@ -1894,32 +1957,15 @@ struct client_window {
};
RB_HEAD(client_windows, client_window);
/* Visible areas not obstructed by overlays. */
#define OVERLAY_MAX_RANGES 3
struct overlay_ranges {
u_int px[OVERLAY_MAX_RANGES];
u_int nx[OVERLAY_MAX_RANGES];
};
/*
* Client theme, this is worked out from the background colour if not reported
* by terminal.
*/
enum client_theme {
THEME_UNKNOWN,
THEME_LIGHT,
THEME_DARK
};
/* Client connection. */
typedef int (*prompt_input_cb)(struct client *, void *, const char *, int);
typedef void (*prompt_free_cb)(void *);
typedef void (*overlay_check_cb)(struct client*, void *, u_int, u_int, u_int,
struct overlay_ranges *);
typedef struct visible_ranges *(*overlay_check_cb)(struct client*, void *,
u_int, u_int, u_int);
typedef struct screen *(*overlay_mode_cb)(struct client *, void *, u_int *,
u_int *);
u_int *);
typedef void (*overlay_draw_cb)(struct client *, void *,
struct screen_redraw_ctx *);
struct screen_redraw_ctx *);
typedef int (*overlay_key_cb)(struct client *, void *, struct key_event *);
typedef void (*overlay_free_cb)(struct client *, void *);
typedef void (*overlay_resize_cb)(struct client *, void *);
@@ -1966,6 +2012,8 @@ struct client {
struct event repeat_timer;
struct event click_timer;
int click_where;
int click_wp;
u_int click_button;
struct mouse_event click_event;
@@ -2009,7 +2057,7 @@ struct client {
#define CLIENT_CONTROL_PAUSEAFTER 0x100000000ULL
#define CLIENT_CONTROL_WAITEXIT 0x200000000ULL
#define CLIENT_WINDOWSIZECHANGED 0x400000000ULL
#define CLIENT_CLIPBOARDBUFFER 0x800000000ULL
/* 0x800000000ULL unused */
#define CLIENT_BRACKETPASTING 0x1000000000ULL
#define CLIENT_ASSUMEPASTING 0x2000000000ULL
#define CLIENT_REDRAWSCROLLBARS 0x4000000000ULL
@@ -2076,6 +2124,7 @@ struct client {
#define PROMPT_KEY 0x10
#define PROMPT_ACCEPT 0x20
#define PROMPT_QUOTENEXT 0x40
#define PROMPT_BSPACE_EXIT 0x80
int prompt_flags;
enum prompt_type prompt_type;
int prompt_cursor;
@@ -2122,6 +2171,7 @@ struct key_binding {
key_code key;
struct cmd_list *cmdlist;
const char *note;
const char *tablename;
int flags;
#define KEY_BINDING_REPEAT 0x1
@@ -2241,10 +2291,37 @@ struct spawn_context {
#define SPAWN_ZOOM 0x80
};
/* Mode tree sort order. */
struct mode_tree_sort_criteria {
u_int field;
int reversed;
/* Paste buffer. */
struct paste_buffer {
char *data;
size_t size;
char *name;
time_t created;
int automatic;
u_int order;
RB_ENTRY(paste_buffer) name_entry;
RB_ENTRY(paste_buffer) time_entry;
};
/* Sort orders. */
enum sort_order {
SORT_ACTIVITY,
SORT_CREATION,
SORT_INDEX,
SORT_MODIFIER,
SORT_NAME,
SORT_ORDER,
SORT_SIZE,
SORT_END,
};
/* Sort criteria. */
struct sort_criteria {
enum sort_order order;
int reversed;
enum sort_order *order_seq; /* available sort orders */
};
/* tmux.c */
@@ -2300,14 +2377,13 @@ void cfg_print_causes(struct cmdq_item *);
void cfg_show_causes(struct session *);
/* paste.c */
struct paste_buffer;
const char *paste_buffer_name(struct paste_buffer *);
u_int paste_buffer_order(struct paste_buffer *);
time_t paste_buffer_created(struct paste_buffer *);
const char *paste_buffer_data(struct paste_buffer *, size_t *);
struct paste_buffer *paste_walk(struct paste_buffer *);
int paste_is_empty(void);
struct paste_buffer *paste_get_top(const char **);
struct paste_buffer *paste_get_top(char **);
struct paste_buffer *paste_get_name(const char *);
void paste_free(struct paste_buffer *);
void paste_add(const char *, char *, size_t);
@@ -2316,6 +2392,28 @@ int paste_set(char *, size_t, const char *, char **);
void paste_replace(struct paste_buffer *, char *, size_t);
char *paste_make_sample(struct paste_buffer *);
/* sort.c */
void sort_next_order(struct sort_criteria *);
enum sort_order sort_order_from_string(const char *);
const char *sort_order_to_string(enum sort_order);
int sort_would_window_tree_swap(struct sort_criteria *,
struct winlink *, struct winlink *);
struct paste_buffer **sort_get_buffers(u_int *, struct sort_criteria *);
struct client **sort_get_clients(u_int *, struct sort_criteria *);
struct session **sort_get_sessions(u_int *, struct sort_criteria *);
struct window_pane **sort_get_panes(u_int *, struct sort_criteria *);
struct window_pane **sort_get_panes_session(struct session *, u_int *,
struct sort_criteria *);
struct window_pane **sort_get_panes_window(struct window *, u_int *,
struct sort_criteria *);
struct winlink **sort_get_winlinks(u_int *, struct sort_criteria *);
struct winlink **sort_get_winlinks_session(struct session *, u_int *,
struct sort_criteria *);
struct key_binding **sort_get_key_bindings(u_int *,
struct sort_criteria *);
struct key_binding **sort_get_key_bindings_table(struct key_table *,
u_int *, struct sort_criteria *);
/* format.c */
#define FORMAT_STATUS 0x1
#define FORMAT_FORCE 0x2
@@ -2427,7 +2525,7 @@ struct options_entry *options_match_get(struct options *, const char *, int *,
int, int *);
const char *options_get_string(struct options *, const char *);
long long options_get_number(struct options *, const char *);
const struct cmd_list *options_get_command(struct options *, const char *);
struct cmd_list *options_get_command(struct options *, const char *);
struct options_entry * printflike(4, 5) options_set_string(struct options *,
const char *, int, const char *, ...);
struct options_entry *options_set_number(struct options *, const char *,
@@ -2508,6 +2606,8 @@ void tty_reset(struct tty *);
void tty_region_off(struct tty *);
void tty_margin_off(struct tty *);
void tty_cursor(struct tty *, u_int, u_int);
int tty_fake_bce(const struct tty *, const struct grid_cell *, u_int);
void tty_repeat_space(struct tty *, u_int);
void tty_clipboard_query(struct tty *);
void tty_putcode(struct tty *, enum tty_code_code);
void tty_putcode_i(struct tty *, enum tty_code_code, int);
@@ -2532,11 +2632,19 @@ void tty_repeat_requests(struct tty *, int);
void tty_stop_tty(struct tty *);
void tty_set_title(struct tty *, const char *);
void tty_set_path(struct tty *, const char *);
void tty_default_attributes(struct tty *, const struct grid_cell *,
struct colour_palette *, u_int, struct hyperlinks *);
void tty_update_mode(struct tty *, int, struct screen *);
const struct grid_cell *tty_check_codeset(struct tty *,
const struct grid_cell *);
struct visible_ranges *tty_check_overlay_range(struct tty *, u_int, u_int,
u_int);
/* tty-draw.c */
void tty_draw_line(struct tty *, struct screen *, u_int, u_int, u_int,
u_int, u_int, const struct grid_cell *, struct colour_palette *);
#ifdef ENABLE_SIXEL
#ifdef ENABLE_IMAGES
void tty_draw_images(struct client *, struct window_pane *, struct screen *);
#endif
@@ -2570,10 +2678,22 @@ void tty_cmd_reverseindex(struct tty *, const struct tty_ctx *);
void tty_cmd_setselection(struct tty *, const struct tty_ctx *);
void tty_cmd_rawstring(struct tty *, const struct tty_ctx *);
#ifdef ENABLE_SIXEL
#ifdef ENABLE_IMAGES
int tty_set_client_cb(struct tty_ctx *, struct client *);
#endif
#ifdef ENABLE_SIXEL_IMAGES
void tty_cmd_sixelimage(struct tty *, const struct tty_ctx *);
#endif
#ifdef ENABLE_KITTY_IMAGES
void tty_cmd_kittyimage(struct tty *, const struct tty_ctx *);
void tty_kitty_delete_all(struct tty *);
void tty_kitty_delete_all_pane(struct window_pane *);
void tty_kitty_passthrough(struct window_pane *, const char *, size_t,
u_int, u_int);
#endif
void tty_cmd_syncstart(struct tty *, const struct tty_ctx *);
void tty_default_colours(struct grid_cell *, struct window_pane *);
@@ -2724,6 +2844,8 @@ void cmd_list_append(struct cmd_list *, struct cmd *);
void cmd_list_append_all(struct cmd_list *, struct cmd_list *);
void cmd_list_move(struct cmd_list *, struct cmd_list *);
void cmd_list_free(struct cmd_list *);
#define CMD_LIST_PRINT_ESCAPED 0x1
#define CMD_LIST_PRINT_NO_GROUPS 0x2
char *cmd_list_print(const struct cmd_list *, int);
struct cmd *cmd_list_first(struct cmd_list *);
struct cmd *cmd_list_next(struct cmd *);
@@ -2815,6 +2937,7 @@ void key_bindings_reset(const char *, key_code);
void key_bindings_remove_table(const char *);
void key_bindings_reset_table(const char *);
void key_bindings_init(void);
int key_bindings_has_repeat(struct key_binding **, u_int);
struct cmdq_item *key_bindings_dispatch(struct key_binding *,
struct cmdq_item *, struct client *, struct key_event *,
struct cmd_find_state *);
@@ -2887,8 +3010,10 @@ void server_client_set_overlay(struct client *, u_int, overlay_check_cb,
overlay_mode_cb, overlay_draw_cb, overlay_key_cb,
overlay_free_cb, overlay_resize_cb, void *);
void server_client_clear_overlay(struct client *);
void server_client_ensure_ranges(struct visible_ranges *, u_int);
int server_client_ranges_is_empty(struct visible_ranges *);
void server_client_overlay_range(u_int, u_int, u_int, u_int, u_int, u_int,
u_int, struct overlay_ranges *);
u_int, struct visible_ranges *);
void server_client_set_key_table(struct client *, const char *);
const char *server_client_get_key_table(struct client *);
int server_client_check_nested(struct client *);
@@ -2943,6 +3068,7 @@ extern u_int status_prompt_hsize[];
void status_timer_start(struct client *);
void status_timer_start_all(void);
void status_update_cache(struct session *);
u_int status_prompt_line_at(struct client *);
int status_at_line(struct client *);
u_int status_line_size(struct client *);
struct style_range *status_get_range(struct client *, u_int, u_int);
@@ -2976,7 +3102,7 @@ void recalculate_sizes_now(int);
/* input.c */
#define INPUT_BUF_DEFAULT_SIZE 1048576
struct input_ctx *input_init(struct window_pane *, struct bufferevent *,
struct colour_palette *);
struct colour_palette *, struct client *);
void input_free(struct input_ctx *);
void input_reset(struct input_ctx *, int);
struct evbuffer *input_pending(struct input_ctx *);
@@ -2985,7 +3111,7 @@ void input_parse_buffer(struct window_pane *, u_char *, size_t);
void input_parse_screen(struct input_ctx *, struct screen *,
screen_write_init_ctx_cb, void *, u_char *, size_t);
void input_reply_clipboard(struct bufferevent *, const char *, size_t,
const char *);
const char *, char);
void input_set_buffer_size(size_t);
void input_request_reply(struct client *, enum input_request_type, void *);
void input_cancel_requests(struct client *);
@@ -3030,7 +3156,7 @@ int grid_cells_look_equal(const struct grid_cell *,
struct grid *grid_create(u_int, u_int, u_int);
void grid_destroy(struct grid *);
int grid_compare(struct grid *, struct grid *);
void grid_collect_history(struct grid *);
void grid_collect_history(struct grid *, int);
void grid_remove_history(struct grid *, u_int );
void grid_scroll_history(struct grid *, u_int);
void grid_scroll_history_region(struct grid *, u_int, u_int, u_int);
@@ -3135,6 +3261,8 @@ void screen_write_preview(struct screen_write_ctx *, struct screen *, u_int,
void screen_write_backspace(struct screen_write_ctx *);
void screen_write_mode_set(struct screen_write_ctx *, int);
void screen_write_mode_clear(struct screen_write_ctx *, int);
void screen_write_start_sync(struct window_pane *);
void screen_write_stop_sync(struct window_pane *);
void screen_write_cursorup(struct screen_write_ctx *, u_int);
void screen_write_cursordown(struct screen_write_ctx *, u_int);
void screen_write_cursorright(struct screen_write_ctx *, u_int);
@@ -3168,10 +3296,15 @@ void screen_write_setselection(struct screen_write_ctx *, const char *,
u_char *, u_int);
void screen_write_rawstring(struct screen_write_ctx *, u_char *, u_int,
int);
#ifdef ENABLE_SIXEL
#ifdef ENABLE_SIXEL_IMAGES
void screen_write_sixelimage(struct screen_write_ctx *,
struct sixel_image *, u_int);
#endif
#ifdef ENABLE_KITTY_IMAGES
void screen_write_kittyimage(struct screen_write_ctx *,
struct kitty_image *);
#endif
void screen_write_alternateon(struct screen_write_ctx *,
struct grid_cell *, int);
void screen_write_alternateoff(struct screen_write_ctx *,
@@ -3201,11 +3334,12 @@ void screen_set_selection(struct screen *, u_int, u_int, u_int, u_int,
void screen_clear_selection(struct screen *);
void screen_hide_selection(struct screen *);
int screen_check_selection(struct screen *, u_int, u_int);
void screen_select_cell(struct screen *, struct grid_cell *,
int screen_select_cell(struct screen *, struct grid_cell *,
const struct grid_cell *);
void screen_alternate_on(struct screen *, struct grid_cell *, int);
void screen_alternate_off(struct screen *, struct grid_cell *, int);
const char *screen_mode_to_string(int);
const char *screen_print(struct screen *);
/* window.c */
extern struct windows windows;
@@ -3357,7 +3491,7 @@ u_int layout_set_next(struct window *);
u_int layout_set_previous(struct window *);
/* mode-tree.c */
typedef void (*mode_tree_build_cb)(void *, struct mode_tree_sort_criteria *,
typedef void (*mode_tree_build_cb)(void *, struct sort_criteria *,
uint64_t *, const char *);
typedef void (*mode_tree_draw_cb)(void *, void *, struct screen_write_ctx *,
u_int, u_int);
@@ -3365,7 +3499,8 @@ typedef int (*mode_tree_search_cb)(void *, void *, const char *, int);
typedef void (*mode_tree_menu_cb)(void *, struct client *, key_code);
typedef u_int (*mode_tree_height_cb)(void *, u_int);
typedef key_code (*mode_tree_key_cb)(void *, void *, u_int);
typedef int (*mode_tree_swap_cb)(void *, void *);
typedef int (*mode_tree_swap_cb)(void *, void *, struct sort_criteria *);
typedef void (*mode_tree_sort_cb)(struct sort_criteria *);
typedef void (*mode_tree_each_cb)(void *, void *, struct client *, key_code);
u_int mode_tree_count_tagged(struct mode_tree_data *);
void *mode_tree_get_current(struct mode_tree_data *);
@@ -3381,8 +3516,8 @@ int mode_tree_down(struct mode_tree_data *, int);
struct mode_tree_data *mode_tree_start(struct window_pane *, struct args *,
mode_tree_build_cb, mode_tree_draw_cb, mode_tree_search_cb,
mode_tree_menu_cb, mode_tree_height_cb, mode_tree_key_cb,
mode_tree_swap_cb, void *, const struct menu_item *, const char **,
u_int, struct screen **);
mode_tree_swap_cb, mode_tree_sort_cb, void *,
const struct menu_item *, struct screen **);
void mode_tree_zoom(struct mode_tree_data *, struct args *);
void mode_tree_build(struct mode_tree_data *);
void mode_tree_free(struct mode_tree_data *);
@@ -3492,8 +3627,9 @@ void session_add_ref(struct session *, const char *);
void session_remove_ref(struct session *, const char *);
char *session_check_name(const char *);
void session_update_activity(struct session *, struct timeval *);
struct session *session_next_session(struct session *);
struct session *session_previous_session(struct session *);
struct session *session_next_session(struct session *, struct sort_criteria *);
struct session *session_previous_session(struct session *,
struct sort_criteria *);
struct winlink *session_attach(struct session *, struct window *, int,
char **);
int session_detach(struct session *, struct winlink *);
@@ -3514,6 +3650,7 @@ u_int session_group_count(struct session_group *);
u_int session_group_attached_count(struct session_group *);
void session_renumber_windows(struct session *);
void session_theme_changed(struct session *);
void session_update_history(struct session *);
/* utf8.c */
enum utf8_state utf8_towc (const struct utf8_data *, wchar_t *);
@@ -3527,9 +3664,9 @@ void utf8_copy(struct utf8_data *, const struct utf8_data *);
enum utf8_state utf8_open(struct utf8_data *, u_char);
enum utf8_state utf8_append(struct utf8_data *, u_char);
int utf8_isvalid(const char *);
int utf8_strvis(char *, const char *, size_t, int);
int utf8_stravis(char **, const char *, int);
int utf8_stravisx(char **, const char *, size_t, int);
size_t utf8_strvis(char *, const char *, size_t, int);
size_t utf8_stravis(char **, const char *, int);
size_t utf8_stravisx(char **, const char *, size_t, int);
char *utf8_sanitize(const char *);
size_t utf8_strlen(const struct utf8_data *);
u_int utf8_strwidth(const struct utf8_data *, ssize_t);
@@ -3586,8 +3723,8 @@ int menu_display(struct menu *, int, int, struct cmdq_item *,
const char *, const char *, struct cmd_find_state *,
menu_choice_cb, void *);
struct screen *menu_mode_cb(struct client *, void *, u_int *, u_int *);
void menu_check_cb(struct client *, void *, u_int, u_int, u_int,
struct overlay_ranges *);
struct visible_ranges *menu_check_cb(struct client *, void *, u_int, u_int,
u_int);
void menu_draw_cb(struct client *, void *,
struct screen_redraw_ctx *);
void menu_free_cb(struct client *, void *);
@@ -3631,14 +3768,16 @@ struct window_pane *spawn_pane(struct spawn_context *, char **);
/* regsub.c */
char *regsub(const char *, const char *, const char *, int);
#ifdef ENABLE_SIXEL
#ifdef ENABLE_IMAGES
/* image.c */
int image_free_all(struct screen *);
struct image *image_store(struct screen *, struct sixel_image *);
struct image *image_store(struct screen *, enum image_type, void *);
int image_check_line(struct screen *, u_int, u_int);
int image_check_area(struct screen *, u_int, u_int, u_int, u_int);
int image_scroll_up(struct screen *, u_int);
#endif
#ifdef ENABLE_SIXEL_IMAGES
/* image-sixel.c */
#define SIXEL_COLOUR_REGISTERS 1024
struct sixel_image *sixel_parse(const char *, size_t, u_int, u_int, u_int);
@@ -3652,6 +3791,18 @@ char *sixel_print(struct sixel_image *, struct sixel_image *,
struct screen *sixel_to_screen(struct sixel_image *);
#endif
#ifdef ENABLE_KITTY_IMAGES
/* image-kitty.c */
struct kitty_image *kitty_parse(const u_char *, size_t, u_int, u_int);
void kitty_free(struct kitty_image *);
void kitty_size_in_cells(struct kitty_image *, u_int *, u_int *);
char kitty_get_action(struct kitty_image *);
u_int kitty_get_image_id(struct kitty_image *);
u_int kitty_get_rows(struct kitty_image *);
char *kitty_print(struct kitty_image *, size_t *);
char *kitty_delete_all(size_t *);
#endif
/* server-acl.c */
void server_acl_init(void);
struct server_acl_user *server_acl_user_find(uid_t);

BIN
tools/image.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 62 KiB

1
tools/image.kitty Normal file

File diff suppressed because one or more lines are too long

324
tty-draw.c Normal file
View File

@@ -0,0 +1,324 @@
/* $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 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 <string.h>
#include "tmux.h"
enum tty_draw_line_state {
TTY_DRAW_LINE_FIRST,
TTY_DRAW_LINE_FLUSH,
TTY_DRAW_LINE_NEW1,
TTY_DRAW_LINE_NEW2,
TTY_DRAW_LINE_EMPTY,
TTY_DRAW_LINE_SAME,
TTY_DRAW_LINE_PAD,
TTY_DRAW_LINE_DONE
};
static const char* tty_draw_line_states[] = {
"FIRST",
"FLUSH",
"NEW1",
"NEW2",
"EMPTY",
"SAME",
"PAD",
"DONE"
};
/* Clear part of the line. */
static void
tty_draw_line_clear(struct tty *tty, u_int px, u_int py, u_int nx,
const struct grid_cell *defaults, u_int bg, int wrapped)
{
/* Nothing to clear. */
if (nx == 0)
return;
/* If genuine BCE is available, can try escape sequences. */
if (!wrapped && nx >= 10 && !tty_fake_bce(tty, defaults, bg)) {
/* Off the end of the line, use EL if available. */
if (px + nx >= tty->sx && tty_term_has(tty->term, TTYC_EL)) {
tty_cursor(tty, px, py);
tty_putcode(tty, TTYC_EL);
return;
}
/* At the start of the line. Use EL1. */
if (px == 0 && tty_term_has(tty->term, TTYC_EL1)) {
tty_cursor(tty, px + nx - 1, py);
tty_putcode(tty, TTYC_EL1);
return;
}
/* Section of line. Use ECH if possible. */
if (tty_term_has(tty->term, TTYC_ECH)) {
tty_cursor(tty, px, py);
tty_putcode_i(tty, TTYC_ECH, nx);
return;
}
}
/* Couldn't use an escape sequence, use spaces. */
if (px != 0 || !wrapped)
tty_cursor(tty, px, py);
if (nx == 1)
tty_putc(tty, ' ');
else if (nx == 2)
tty_putn(tty, " ", 2, 2);
else
tty_repeat_space(tty, nx);
}
/* Is this cell empty? */
static u_int
tty_draw_line_get_empty(const struct grid_cell *gc, u_int nx)
{
u_int empty = 0;
if (gc->data.width != 1 && gc->data.width > nx)
empty = nx;
else if (gc->attr == 0 && gc->link == 0) {
if (gc->flags & GRID_FLAG_CLEARED)
empty = 1;
else if (gc->flags & GRID_FLAG_TAB)
empty = gc->data.width;
else if (gc->data.size == 1 && *gc->data.data == ' ')
empty = 1;
}
return (empty);
}
/* Draw a line from screen to tty. */
void
tty_draw_line(struct tty *tty, struct screen *s, u_int px, u_int py, u_int nx,
u_int atx, u_int aty, const struct grid_cell *defaults,
struct colour_palette *palette)
{
struct grid *gd = s->grid;
const struct grid_cell *gcp;
struct grid_cell gc, ngc, last;
struct grid_line *gl;
u_int i, j, last_i, cx, ex, width;
u_int cellsize, bg;
int flags, empty, wrapped = 0;
char buf[1000];
size_t len;
enum tty_draw_line_state current_state, next_state;
/*
* py is the line in the screen to draw. px is the start x and nx is
* the width to draw. atx,aty is the line on the terminal to draw it.
*/
log_debug("%s: px=%u py=%u nx=%u atx=%u aty=%u", __func__, px, py, nx,
atx, aty);
/*
* Clamp the width to cellsize - note this is not cellused, because
* there may be empty background cells after it (from BCE).
*/
cellsize = grid_get_line(gd, gd->hsize + py)->cellsize;
if (screen_size_x(s) > cellsize)
ex = cellsize;
else {
ex = screen_size_x(s);
if (px > ex)
return;
if (px + nx > ex)
nx = ex - px;
}
if (ex < nx)
ex = nx;
log_debug("%s: drawing %u-%u,%u (end %u) at %u,%u; defaults: fg=%d, "
"bg=%d", __func__, px, px + nx, py, ex, atx, aty, defaults->fg,
defaults->bg);
/*
* If there is padding at the start, we must have truncated a wide
* character. Clear it.
*/
cx = 0;
for (i = px; i < px + nx; i++) {
grid_view_get_cell(gd, i, py, &gc);
if (~gc.flags & GRID_FLAG_PADDING)
break;
cx++;
}
if (cx != 0) {
/* Find the previous cell for the background colour. */
for (i = px + 1; i > 0; i--) {
grid_view_get_cell(gd, i - 1, py, &gc);
if (~gc.flags & GRID_FLAG_PADDING)
break;
}
if (i == 0)
bg = defaults->bg;
else {
bg = gc.bg;
if (gc.flags & GRID_FLAG_SELECTED) {
memcpy(&ngc, &gc, sizeof ngc);
if (screen_select_cell(s, &ngc, &gc))
bg = ngc.bg;
}
}
tty_attributes(tty, &last, defaults, palette, s->hyperlinks);
log_debug("%s: clearing %u padding cells", __func__, cx);
tty_draw_line_clear(tty, atx, aty, cx, defaults, bg, 0);
if (cx == ex)
return;
atx += cx;
px += cx;
nx -= cx;
ex -= cx;
}
/* Did the previous line wrap on to this one? */
if (py != 0 && atx == 0 && tty->cx >= tty->sx && nx == tty->sx) {
gl = grid_get_line(gd, gd->hsize + py - 1);
if (gl->flags & GRID_LINE_WRAPPED)
wrapped = 1;
}
/* Turn off cursor while redrawing and reset region and margins. */
flags = (tty->flags & TTY_NOCURSOR);
tty->flags |= TTY_NOCURSOR;
tty_update_mode(tty, tty->mode, s);
tty_region_off(tty);
tty_margin_off(tty);
/* Start with the default cell as the last cell. */
memcpy(&last, &grid_default_cell, sizeof last);
last.bg = defaults->bg;
tty_default_attributes(tty, defaults, palette, 8, s->hyperlinks);
/* Loop over each character in the range. */
last_i = i = 0;
len = 0;
width = 0;
current_state = TTY_DRAW_LINE_FIRST;
for (;;) {
/* Work out the next state. */
if (i == nx) {
/*
* If this is the last cell, we are done. But we need to
* go through the loop again to flush anything in
* the buffer.
*/
empty = 0;
next_state = TTY_DRAW_LINE_DONE;
gcp = &grid_default_cell;
} else {
/* Get the current cell. */
grid_view_get_cell(gd, px + i, py, &gc);
/* Update for codeset if needed. */
gcp = tty_check_codeset(tty, &gc);
/* And for selection. */
if (gcp->flags & GRID_FLAG_SELECTED) {
memcpy(&ngc, gcp, sizeof ngc);
if (screen_select_cell(s, &ngc, gcp))
gcp = &ngc;
}
/* Work out the the empty width. */
if (i >= ex)
empty = 1;
else if (gcp->bg != last.bg)
empty = 0;
else
empty = tty_draw_line_get_empty(gcp, nx - i);
/* Work out the next state. */
if (empty != 0)
next_state = TTY_DRAW_LINE_EMPTY;
else if (current_state == TTY_DRAW_LINE_FIRST)
next_state = TTY_DRAW_LINE_SAME;
else if (gcp->flags & GRID_FLAG_PADDING)
next_state = TTY_DRAW_LINE_PAD;
else if (grid_cells_look_equal(gcp, &last)) {
if (gcp->data.size > (sizeof buf) - len)
next_state = TTY_DRAW_LINE_FLUSH;
else
next_state = TTY_DRAW_LINE_SAME;
} else if (current_state == TTY_DRAW_LINE_NEW1)
next_state = TTY_DRAW_LINE_NEW2;
else
next_state = TTY_DRAW_LINE_NEW1;
}
if (log_get_level() != 0) {
log_debug("%s: cell %u empty %u, bg %u; state: "
"current %s, next %s", __func__, px + i, empty,
gcp->bg, tty_draw_line_states[current_state],
tty_draw_line_states[next_state]);
}
/* If the state has changed, flush any collected data. */
if (next_state != current_state) {
if (current_state == TTY_DRAW_LINE_EMPTY) {
tty_attributes(tty, &last, defaults, palette,
s->hyperlinks);
tty_draw_line_clear(tty, atx + last_i, aty,
i - last_i, defaults, last.bg, wrapped);
wrapped = 0;
} else if (next_state != TTY_DRAW_LINE_SAME &&
len != 0) {
tty_attributes(tty, &last, defaults, palette,
s->hyperlinks);
if (atx + i - width != 0 || !wrapped)
tty_cursor(tty, atx + i - width, aty);
if (~last.attr & GRID_ATTR_CHARSET)
tty_putn(tty, buf, len, width);
else {
for (j = 0; j < len; j++)
tty_putc(tty, buf[j]);
}
len = 0;
width = 0;
wrapped = 0;
}
last_i = i;
}
/* Append the cell if it is not empty and not padding. */
if (next_state != TTY_DRAW_LINE_EMPTY &&
next_state != TTY_DRAW_LINE_PAD) {
memcpy(buf + len, gcp->data.data, gcp->data.size);
len += gcp->data.size;
width += gcp->data.width;
}
/* If this is the last cell, we are done. */
if (next_state == TTY_DRAW_LINE_DONE)
break;
/* Otherwise move to the next. */
current_state = next_state;
memcpy(&last, gcp, sizeof last);
if (empty != 0)
i += empty;
else
i += gcp->data.width;
}
tty->flags = (tty->flags & ~TTY_NOCURSOR)|flags;
tty_update_mode(tty, tty->mode, s);
}

View File

@@ -357,6 +357,17 @@ static const struct tty_feature tty_feature_sixel = {
TERM_SIXEL
};
/* Terminal has kitty graphics protocol capability. */
static const char *const tty_feature_kitty_capabilities[] = {
"Kty",
NULL
};
static const struct tty_feature tty_feature_kitty = {
"kitty",
tty_feature_kitty_capabilities,
TERM_KITTY
};
/* Available terminal features. */
static const struct tty_feature *const tty_features[] = {
&tty_feature_256,
@@ -368,6 +379,7 @@ static const struct tty_feature *const tty_features[] = {
&tty_feature_extkeys,
&tty_feature_focus,
&tty_feature_ignorefkeys,
&tty_feature_kitty,
&tty_feature_margins,
&tty_feature_mouse,
&tty_feature_osc7,
@@ -500,7 +512,19 @@ tty_default_features(int *feat, const char *name, u_int version)
*/
.features = TTY_FEATURES_BASE_MODERN_XTERM
",ccolour,cstyle,extkeys,focus"
}
},
#ifdef ENABLE_KITTY_IMAGES
{ .name = "kitty",
.features = TTY_FEATURES_BASE_MODERN_XTERM
",ccolour,cstyle,extkeys,focus,sync,hyperlinks"
",kitty"
},
{ .name = "ghostty",
.features = TTY_FEATURES_BASE_MODERN_XTERM
",ccolour,cstyle,extkeys,focus,sync,hyperlinks"
",kitty"
},
#endif
};
u_int i;

View File

@@ -60,6 +60,10 @@ static int tty_keys_device_attributes2(struct tty *, const char *, size_t,
static int tty_keys_extended_device_attributes(struct tty *, const char *,
size_t, size_t *);
static int tty_keys_palette(struct tty *, const char *, size_t, size_t *);
#ifdef ENABLE_KITTY_IMAGES
static int tty_keys_kitty_graphics(struct tty *, const char *, size_t,
size_t *);
#endif
/* A key tree entry. */
struct tty_key {
@@ -759,6 +763,19 @@ tty_keys_next(struct tty *tty)
goto partial_key;
}
#ifdef ENABLE_KITTY_IMAGES
/* Is this a kitty graphics protocol response? ESC _ G ... ESC \ */
switch (tty_keys_kitty_graphics(tty, buf, len, &size)) {
case 0: /* yes */
key = KEYC_UNKNOWN;
goto complete_key;
case -1: /* no, or not valid */
break;
case 1: /* partial */
goto partial_key;
}
#endif
/* Is this a primary device attributes response? */
switch (tty_keys_device_attributes(tty, buf, len, &size)) {
case 0: /* yes */
@@ -909,9 +926,15 @@ first_key:
* used. termios should have a better idea.
*/
bspace = tty->tio.c_cc[VERASE];
if (bspace != _POSIX_VDISABLE && key == bspace) {
log_debug("%s: key %#llx is backspace", c->name, key);
key = KEYC_BSPACE;
if (bspace != _POSIX_VDISABLE) {
if (key == bspace) {
log_debug("%s: key %#llx is BSpace", c->name, key);
key = KEYC_BSPACE;
}
if (key == (bspace|KEYC_META)) {
log_debug("%s: key %#llx is M-BSpace", c->name, key);
key = KEYC_BSPACE|KEYC_META;
}
}
/*
@@ -950,7 +973,8 @@ partial_key:
if (delay == 0)
delay = 1;
if ((tty->flags & (TTY_WAITFG|TTY_WAITBG) ||
(tty->flags & TTY_ALL_REQUEST_FLAGS) != TTY_ALL_REQUEST_FLAGS)) {
(tty->flags & TTY_ALL_REQUEST_FLAGS) != TTY_ALL_REQUEST_FLAGS) ||
!TAILQ_EMPTY(&c->input_requests)) {
log_debug("%s: increasing delay for active query", c->name);
if (delay < 500)
delay = 500;
@@ -1301,12 +1325,11 @@ tty_keys_mouse(struct tty *tty, const char *buf, size_t len, size_t *size,
static int
tty_keys_clipboard(struct tty *tty, const char *buf, size_t len, size_t *size)
{
struct client *c = tty->client;
struct window_pane *wp;
size_t end, terminator = 0, needed;
char *copy, *out;
int outlen;
u_int i;
struct client *c = tty->client;
size_t end, terminator = 0, needed;
char *copy, *out, clip = 0;
int outlen;
struct input_request_clipboard_data cd;
*size = 0;
@@ -1354,7 +1377,14 @@ tty_keys_clipboard(struct tty *tty, const char *buf, size_t len, size_t *size)
/* Adjust end so that it points to the start of the terminator. */
end -= terminator - 1;
/* Get the second argument. */
/*
* Save which clipboard was used from the second argument. If more than
* one is specified (should not happen), ignore the argument.
*/
if (end >= 2 && buf[0] != ';' && buf[1] == ';')
clip = buf[0];
/* Skip the second argument. */
while (end != 0 && *buf != ';') {
buf++;
end--;
@@ -1364,12 +1394,6 @@ tty_keys_clipboard(struct tty *tty, const char *buf, size_t len, size_t *size)
buf++;
end--;
/* If we did not request this, ignore it. */
if (~tty->flags & TTY_OSC52QUERY)
return (0);
tty->flags &= ~TTY_OSC52QUERY;
evtimer_del(&tty->clipboard_timer);
/* It has to be a string so copy it. */
copy = xmalloc(end + 1);
memcpy(copy, buf, end);
@@ -1377,29 +1401,34 @@ tty_keys_clipboard(struct tty *tty, const char *buf, size_t len, size_t *size)
/* Convert from base64. */
needed = (end / 4) * 3;
if (needed == 0) {
free(copy);
return (0);
}
out = xmalloc(needed);
if ((outlen = b64_pton(copy, out, len)) == -1) {
if ((outlen = b64_pton(copy, out, needed)) == -1) {
free(out);
free(copy);
return (0);
}
free(copy);
/* Create a new paste buffer and forward to panes. */
log_debug("%s: %.*s", __func__, outlen, out);
if (c->flags & CLIENT_CLIPBOARDBUFFER) {
paste_add(NULL, out, outlen);
c->flags &= ~CLIENT_CLIPBOARDBUFFER;
}
for (i = 0; i < c->clipboard_npanes; i++) {
wp = window_pane_find_by_id(c->clipboard_panes[i]);
if (wp != NULL)
input_reply_clipboard(wp->event, out, outlen, "\033\\");
}
free(c->clipboard_panes);
c->clipboard_panes = NULL;
c->clipboard_npanes = 0;
/* Set reply if any. */
cd.buf = out;
cd.len = outlen;
cd.clip = clip;
input_request_reply(c, INPUT_REQUEST_CLIPBOARD, &cd);
/* Create a buffer if requested. */
if (tty->flags & TTY_OSC52QUERY) {
paste_add(NULL, out, outlen);
out = NULL;
evtimer_del(&tty->clipboard_timer);
tty->flags &= ~TTY_OSC52QUERY;
}
free(out);
return (0);
}
@@ -1612,8 +1641,10 @@ tty_keys_extended_device_attributes(struct tty *tty, const char *buf,
}
if (i == (sizeof tmp) - 1)
return (-1);
tmp[i - 1] = '\0';
*size = 5 + i;
if (i == 0)
return (0);
tmp[i - 1] = '\0';
/* Add terminal features. */
if (strncmp(tmp, "iTerm2 ", 7) == 0)
@@ -1626,6 +1657,14 @@ tty_keys_extended_device_attributes(struct tty *tty, const char *buf,
tty_default_features(features, "mintty", 0);
else if (strncmp(tmp, "foot(", 5) == 0)
tty_default_features(features, "foot", 0);
#ifdef ENABLE_KITTY_IMAGES
else if (strncmp(tmp, "kitty ", 6) == 0 ||
strcmp(tmp, "kitty") == 0)
tty_default_features(features, "kitty", 0);
else if (strncmp(tmp, "ghostty ", 8) == 0 ||
strcmp(tmp, "ghostty") == 0)
tty_default_features(features, "ghostty", 0);
#endif
log_debug("%s: received extended DA %.*s", c->name, (int)*size, buf);
free(c->term_type);
@@ -1686,12 +1725,15 @@ tty_keys_colours(struct tty *tty, const char *buf, size_t len, size_t *size,
}
if (i == (sizeof tmp) - 1)
return (-1);
*size = 6 + i;
if (i == 0)
return (0);
if (tmp[i - 1] == '\033')
tmp[i - 1] = '\0';
else
tmp[i] = '\0';
*size = 6 + i;
/* Work out the colour. */
n = colour_parseX11(tmp);
if (n != -1 && buf[3] == '0') {
if (c != NULL)
@@ -1717,7 +1759,7 @@ static int
tty_keys_palette(struct tty *tty, const char *buf, size_t len, size_t *size)
{
struct client *c = tty->client;
u_int i, start;
u_int i;
char tmp[128], *endptr;
int idx;
struct input_request_palette_data pd;
@@ -1742,32 +1784,35 @@ tty_keys_palette(struct tty *tty, const char *buf, size_t len, size_t *size)
if (len == 4)
return (1);
/* Copy the rest up to \033\ or \007. */
for (i = 0; i < (sizeof tmp) - 1; i++) {
if (4 + i == len)
return (1);
if (buf[4 + i - 1] == '\033' && buf[4 + i] == '\\')
break;
if (buf[4 + i] == '\007')
break;
tmp[i] = buf[4 + i];
}
if (i == (sizeof tmp) - 1)
return (-1);
*size = 5 + i;
if (i == 0)
return (0);
if (tmp[i - 1] == '\033')
tmp[i - 1] = '\0';
else
tmp[i] = '\0';
/* Parse index. */
idx = strtol(buf + 4, &endptr, 10);
if (endptr == buf + 4 || *endptr != ';')
idx = strtol(tmp, &endptr, 10);
if (*endptr != ';')
return (-1);
if (idx < 0 || idx > 255)
return (-1);
/* Copy the rest up to \033\ or \007. */
start = (endptr - buf) + 1;
for (i = start; i < len && i - start < sizeof tmp; i++) {
if (buf[i - 1] == '\033' && buf[i] == '\\')
break;
if (buf[i] == '\007')
break;
tmp[i - start] = buf[i];
}
if (i - start == sizeof tmp)
return (-1);
if (i > 0 && buf[i - 1] == '\033')
tmp[i - start - 1] = '\0';
else
tmp[i - start] = '\0';
*size = i + 1;
/* Work out the colour. */
pd.c = colour_parseX11(tmp);
pd.c = colour_parseX11(endptr + 1);
if (pd.c == -1)
return (0);
pd.idx = idx;
@@ -1775,3 +1820,74 @@ tty_keys_palette(struct tty *tty, const char *buf, size_t len, size_t *size)
return (0);
}
#ifdef ENABLE_KITTY_IMAGES
/*
* Handle kitty graphics protocol response from outer terminal.
* Format: ESC _ G <key=value,...> ; <message> ESC \
* The response to our capability probe (a=q) is:
* ESC _ G i=31;OK ESC \ (supported)
* ESC _ G i=31;ENOSYS:... (not supported)
* Returns 0 for success, -1 for not a kitty response, 1 for partial.
*/
static int
tty_keys_kitty_graphics(struct tty *tty, const char *buf, size_t len,
size_t *size)
{
struct client *c = tty->client;
int *features = &c->term_features;
const char *semi;
size_t i;
char tmp[256];
*size = 0;
/*
* Kitty APC response starts with ESC _ G (3 bytes).
* The 8-bit C1 equivalent 0x9f could also be used but is rare.
*/
if (buf[0] != '\033')
return (-1);
if (len == 1)
return (1);
if (buf[1] != '_')
return (-1);
if (len == 2)
return (1);
if (buf[2] != 'G')
return (-1);
if (len == 3)
return (1);
/* Collect body up to ESC \ (ST). */
for (i = 0; i < sizeof(tmp) - 1; i++) {
if (3 + i + 1 >= len)
return (1); /* partial */
if (buf[3 + i] == '\033' && buf[3 + i + 1] == '\\') {
tmp[i] = '\0';
*size = 3 + i + 2;
break;
}
tmp[i] = buf[3 + i];
}
if (i == sizeof(tmp) - 1)
return (-1); /* too long, not a valid response */
log_debug("%s: kitty graphics response: %s", c->name, tmp);
/*
* Check if the message (after the semicolon) starts with "OK".
* The format is: i=31;OK or i=31;ENOSYS:...
*/
semi = strchr(tmp, ';');
if (semi != NULL && strncmp(semi + 1, "OK", 2) == 0) {
log_debug("%s: kitty graphics supported", c->name);
tty_add_features(features, "kitty", ",");
tty_update_features(tty);
} else {
log_debug("%s: kitty graphics not supported", c->name);
}
return (0);
}
#endif

View File

@@ -461,6 +461,9 @@ tty_term_apply_overrides(struct tty_term *term)
/* Log the SIXEL flag. */
log_debug("SIXEL flag is %d", !!(term->flags & TERM_SIXEL));
/* Log the KITTY flag. */
log_debug("KITTY flag is %d", !!(term->flags & TERM_KITTY));
/* Update the RGB flag if the terminal has RGB colours. */
if (tty_term_has(term, TTYC_SETRGBF) &&
tty_term_has(term, TTYC_SETRGBB))

501
tty.c
View File

@@ -35,6 +35,8 @@
static int tty_log_fd = -1;
static void tty_start_timer_callback(int, short, void *);
static void tty_clipboard_query_callback(int, short, void *);
static void tty_set_italics(struct tty *);
static int tty_try_colour(struct tty *, int, const char *);
static void tty_force_cursor_colour(struct tty *, int);
@@ -59,20 +61,13 @@ static void tty_region(struct tty *, u_int, u_int);
static void tty_margin_pane(struct tty *, const struct tty_ctx *);
static void tty_margin(struct tty *, u_int, u_int);
static int tty_large_region(struct tty *, const struct tty_ctx *);
static int tty_fake_bce(const struct tty *, const struct grid_cell *,
u_int);
static void tty_redraw_region(struct tty *, const struct tty_ctx *);
static void tty_emulate_repeat(struct tty *, enum tty_code_code,
enum tty_code_code, u_int);
static void tty_repeat_space(struct tty *, u_int);
static void tty_draw_pane(struct tty *, const struct tty_ctx *, u_int);
static void tty_default_attributes(struct tty *, const struct grid_cell *,
struct colour_palette *, u_int, struct hyperlinks *);
static int tty_check_overlay(struct tty *, u_int, u_int);
static void tty_check_overlay_range(struct tty *, u_int, u_int, u_int,
struct overlay_ranges *);
#ifdef ENABLE_SIXEL
#ifdef ENABLE_IMAGES
static void tty_write_one(void (*)(struct tty *, const struct tty_ctx *),
struct client *, struct tty_ctx *);
#endif
@@ -296,6 +291,8 @@ tty_open(struct tty *tty, char **cause)
if (tty->out == NULL)
fatal("out of memory");
evtimer_set(&tty->clipboard_timer, tty_clipboard_query_callback, tty);
evtimer_set(&tty->start_timer, tty_start_timer_callback, tty);
evtimer_set(&tty->timer, tty_timer_callback, tty);
tty_start_tty(tty);
@@ -327,7 +324,6 @@ tty_start_start_timer(struct tty *tty)
log_debug("%s: start timer started", c->name);
evtimer_del(&tty->start_timer);
evtimer_set(&tty->start_timer, tty_start_timer_callback, tty);
evtimer_add(&tty->start_timer, &tv);
}
@@ -395,6 +391,23 @@ tty_send_requests(struct tty *tty)
return;
if (tty->term->flags & TERM_VT100LIKE) {
#ifdef ENABLE_KITTY_IMAGES
/*
* Send the kitty graphics capability probe BEFORE the DA1
* request. Per the kitty spec, a supporting terminal sends
* its APC response before processing any subsequent requests.
* So the reply ordering will be:
* 1. ESC _ G i=31;OK ESC \ (kitty response, if supported)
* 2. ESC [ ? ... c (DA1 response)
* 3. ESC [ > ... c (DA2 response)
* 4. ESC P > | ... ESC \ (XDA response)
* which is exactly what our parsers expect.
* Only probe if the kitty feature isn't already enabled.
*/
if (~tty->term->flags & TERM_KITTY)
tty_puts(tty,
"\033_Gi=31,s=1,v=1,a=q,t=d,f=24;AAAA\033\\");
#endif
if (~tty->flags & TTY_HAVEDA)
tty_puts(tty, "\033[c");
if (~tty->flags & TTY_HAVEDA2)
@@ -445,6 +458,7 @@ tty_stop_tty(struct tty *tty)
tty->flags &= ~TTY_STARTED;
evtimer_del(&tty->start_timer);
evtimer_del(&tty->clipboard_timer);
event_del(&tty->timer);
tty->flags &= ~TTY_BLOCK;
@@ -524,6 +538,8 @@ void
tty_free(struct tty *tty)
{
tty_close(tty);
free(tty->r.ranges);
}
void
@@ -634,7 +650,8 @@ tty_add(struct tty *tty, const char *buf, size_t len)
if (tty_log_fd != -1)
write(tty_log_fd, buf, len);
if (tty->flags & TTY_STARTED)
if ((tty->flags & TTY_STARTED) &&
!event_pending(&tty->event_out, EV_WRITE, NULL))
event_add(&tty->event_out, NULL);
}
@@ -908,7 +925,7 @@ tty_emulate_repeat(struct tty *tty, enum tty_code_code code,
}
}
static void
void
tty_repeat_space(struct tty *tty, u_int n)
{
static char s[500];
@@ -1067,7 +1084,7 @@ tty_large_region(__unused struct tty *tty, const struct tty_ctx *ctx)
* Return if BCE is needed but the terminal doesn't have it - it'll need to be
* emulated.
*/
static int
int
tty_fake_bce(const struct tty *tty, const struct grid_cell *gc, u_int bg)
{
if (tty_term_flag(tty->term, TTYC_BCE))
@@ -1162,8 +1179,6 @@ tty_clear_line(struct tty *tty, const struct grid_cell *defaults, u_int py,
u_int px, u_int nx, u_int bg)
{
struct client *c = tty->client;
struct overlay_ranges r;
u_int i;
log_debug("%s: %s, %u at %u,%u", __func__, c->name, nx, px, py);
@@ -1199,13 +1214,8 @@ tty_clear_line(struct tty *tty, const struct grid_cell *defaults, u_int py,
* Couldn't use an escape sequence, use spaces. Clear only the visible
* bit if there is an overlay.
*/
tty_check_overlay_range(tty, px, py, nx, &r);
for (i = 0; i < OVERLAY_MAX_RANGES; i++) {
if (r.nx[i] == 0)
continue;
tty_cursor(tty, r.px[i], py);
tty_repeat_space(tty, r.nx[i]);
}
tty_cursor(tty, px, py);
tty_repeat_space(tty, nx);
}
/* Clear a line, adjusting to visible part of pane. */
@@ -1386,7 +1396,7 @@ tty_draw_pane(struct tty *tty, const struct tty_ctx *ctx, u_int py)
}
}
static const struct grid_cell *
const struct grid_cell *
tty_check_codeset(struct tty *tty, const struct grid_cell *gc)
{
static struct grid_cell new;
@@ -1419,234 +1429,39 @@ tty_check_codeset(struct tty *tty, const struct grid_cell *gc)
return (&new);
}
/*
* Check if a single character is obstructed by the overlay and return a
* boolean.
*/
/* Check if a single character is covered by the overlay. */
static int
tty_check_overlay(struct tty *tty, u_int px, u_int py)
{
struct overlay_ranges r;
struct visible_ranges *r;
/*
* A unit width range will always return nx[2] == 0 from a check, even
* with multiple overlays, so it's sufficient to check just the first
* two entries.
* With a single character, if there is anything visible (that is, the
* range is not empty), it must be that character.
*/
tty_check_overlay_range(tty, px, py, 1, &r);
if (r.nx[0] + r.nx[1] == 0)
return (0);
return (1);
r = tty_check_overlay_range(tty, px, py, 1);
return (!server_client_ranges_is_empty(r));
}
/* Return parts of the input range which are visible. */
static void
tty_check_overlay_range(struct tty *tty, u_int px, u_int py, u_int nx,
struct overlay_ranges *r)
struct visible_ranges *
tty_check_overlay_range(struct tty *tty, u_int px, u_int py, u_int nx)
{
struct client *c = tty->client;
if (c->overlay_check == NULL) {
r->px[0] = px;
r->nx[0] = nx;
r->px[1] = 0;
r->nx[1] = 0;
r->px[2] = 0;
r->nx[2] = 0;
return;
server_client_ensure_ranges(&tty->r, 1);
tty->r.ranges[0].px = px;
tty->r.ranges[0].nx = nx;
tty->r.used = 1;
return (&tty->r);
}
c->overlay_check(c, c->overlay_data, px, py, nx, r);
return (c->overlay_check(c, c->overlay_data, px, py, nx));
}
void
tty_draw_line(struct tty *tty, struct screen *s, u_int px, u_int py, u_int nx,
u_int atx, u_int aty, const struct grid_cell *defaults,
struct colour_palette *palette)
{
struct grid *gd = s->grid;
struct grid_cell gc, last;
const struct grid_cell *gcp;
struct grid_line *gl;
struct client *c = tty->client;
struct overlay_ranges r;
u_int i, j, ux, sx, width, hidden, eux, nxx;
u_int cellsize;
int flags, cleared = 0, wrapped = 0;
char buf[512];
size_t len;
log_debug("%s: px=%u py=%u nx=%u atx=%u aty=%u", __func__,
px, py, nx, atx, aty);
log_debug("%s: defaults: fg=%d, bg=%d", __func__, defaults->fg,
defaults->bg);
/*
* py is the line in the screen to draw.
* px is the start x and nx is the width to draw.
* atx,aty is the line on the terminal to draw it.
*/
flags = (tty->flags & TTY_NOCURSOR);
tty->flags |= TTY_NOCURSOR;
tty_update_mode(tty, tty->mode, s);
tty_region_off(tty);
tty_margin_off(tty);
/*
* Clamp the width to cellsize - note this is not cellused, because
* there may be empty background cells after it (from BCE).
*/
sx = screen_size_x(s);
if (nx > sx)
nx = sx;
cellsize = grid_get_line(gd, gd->hsize + py)->cellsize;
if (sx > cellsize)
sx = cellsize;
if (sx > tty->sx)
sx = tty->sx;
if (sx > nx)
sx = nx;
ux = 0;
if (py == 0)
gl = NULL;
else
gl = grid_get_line(gd, gd->hsize + py - 1);
if (gl == NULL ||
(~gl->flags & GRID_LINE_WRAPPED) ||
atx != 0 ||
tty->cx < tty->sx ||
nx < tty->sx) {
if (nx < tty->sx &&
atx == 0 &&
px + sx != nx &&
tty_term_has(tty->term, TTYC_EL1) &&
!tty_fake_bce(tty, defaults, 8) &&
c->overlay_check == NULL) {
tty_default_attributes(tty, defaults, palette, 8,
s->hyperlinks);
tty_cursor(tty, nx - 1, aty);
tty_putcode(tty, TTYC_EL1);
cleared = 1;
}
} else {
log_debug("%s: wrapped line %u", __func__, aty);
wrapped = 1;
}
memcpy(&last, &grid_default_cell, sizeof last);
len = 0;
width = 0;
for (i = 0; i < sx; i++) {
grid_view_get_cell(gd, px + i, py, &gc);
gcp = tty_check_codeset(tty, &gc);
if (len != 0 &&
(!tty_check_overlay(tty, atx + ux + width, aty) ||
(gcp->attr & GRID_ATTR_CHARSET) ||
gcp->flags != last.flags ||
gcp->attr != last.attr ||
gcp->fg != last.fg ||
gcp->bg != last.bg ||
gcp->us != last.us ||
gcp->link != last.link ||
ux + width + gcp->data.width > nx ||
(sizeof buf) - len < gcp->data.size)) {
tty_attributes(tty, &last, defaults, palette,
s->hyperlinks);
if (last.flags & GRID_FLAG_CLEARED) {
log_debug("%s: %zu cleared", __func__, len);
tty_clear_line(tty, defaults, aty, atx + ux,
width, last.bg);
} else {
if (!wrapped || atx != 0 || ux != 0)
tty_cursor(tty, atx + ux, aty);
tty_putn(tty, buf, len, width);
}
ux += width;
len = 0;
width = 0;
wrapped = 0;
}
if (gcp->flags & GRID_FLAG_SELECTED)
screen_select_cell(s, &last, gcp);
else
memcpy(&last, gcp, sizeof last);
tty_check_overlay_range(tty, atx + ux, aty, gcp->data.width,
&r);
hidden = 0;
for (j = 0; j < OVERLAY_MAX_RANGES; j++)
hidden += r.nx[j];
hidden = gcp->data.width - hidden;
if (hidden != 0 && hidden == gcp->data.width) {
if (~gcp->flags & GRID_FLAG_PADDING)
ux += gcp->data.width;
} else if (hidden != 0 || ux + gcp->data.width > nx) {
if (~gcp->flags & GRID_FLAG_PADDING) {
tty_attributes(tty, &last, defaults, palette,
s->hyperlinks);
for (j = 0; j < OVERLAY_MAX_RANGES; j++) {
if (r.nx[j] == 0)
continue;
/* Effective width drawn so far. */
eux = r.px[j] - atx;
if (eux < nx) {
tty_cursor(tty, r.px[j], aty);
nxx = nx - eux;
if (r.nx[j] > nxx)
r.nx[j] = nxx;
tty_repeat_space(tty, r.nx[j]);
ux = eux + r.nx[j];
}
}
}
} else if (gcp->attr & GRID_ATTR_CHARSET) {
tty_attributes(tty, &last, defaults, palette,
s->hyperlinks);
tty_cursor(tty, atx + ux, aty);
for (j = 0; j < gcp->data.size; j++)
tty_putc(tty, gcp->data.data[j]);
ux += gcp->data.width;
} else if (~gcp->flags & GRID_FLAG_PADDING) {
memcpy(buf + len, gcp->data.data, gcp->data.size);
len += gcp->data.size;
width += gcp->data.width;
}
}
if (len != 0 && ((~last.flags & GRID_FLAG_CLEARED) || last.bg != 8)) {
tty_attributes(tty, &last, defaults, palette, s->hyperlinks);
if (last.flags & GRID_FLAG_CLEARED) {
log_debug("%s: %zu cleared (end)", __func__, len);
tty_clear_line(tty, defaults, aty, atx + ux, width,
last.bg);
} else {
if (!wrapped || atx != 0 || ux != 0)
tty_cursor(tty, atx + ux, aty);
tty_putn(tty, buf, len, width);
}
ux += width;
}
if (!cleared && ux < nx) {
log_debug("%s: %u to end of line (%zu cleared)", __func__,
nx - ux, len);
tty_default_attributes(tty, defaults, palette, 8,
s->hyperlinks);
tty_clear_line(tty, defaults, aty, atx + ux, nx - ux, 8);
}
tty->flags = (tty->flags & ~TTY_NOCURSOR) | flags;
tty_update_mode(tty, tty->mode, s);
}
#ifdef ENABLE_SIXEL
#ifdef ENABLE_IMAGES
/* Update context for client. */
static int
int
tty_set_client_cb(struct tty_ctx *ttyctx, struct client *c)
{
struct window_pane *wp = ttyctx->arg;
@@ -1691,7 +1506,22 @@ tty_draw_images(struct client *c, struct window_pane *wp, struct screen *s)
ttyctx.arg = wp;
ttyctx.set_client_cb = tty_set_client_cb;
ttyctx.allow_invisible_panes = 1;
tty_write_one(tty_cmd_sixelimage, c, &ttyctx);
/* Call the appropriate rendering function based on image type */
switch (im->type) {
#ifdef ENABLE_SIXEL_IMAGES
case IMAGE_SIXEL:
tty_write_one(tty_cmd_sixelimage, c, &ttyctx);
break;
#endif
#ifdef ENABLE_KITTY_IMAGES
case IMAGE_KITTY:
tty_write_one(tty_cmd_kittyimage, c, &ttyctx);
break;
#endif
default:
break;
}
}
}
#endif
@@ -1769,7 +1599,7 @@ tty_write(void (*cmdfn)(struct tty *, const struct tty_ctx *),
}
}
#ifdef ENABLE_SIXEL
#ifdef ENABLE_IMAGES
/* Only write to the incoming tty instead of every client. */
static void
tty_write_one(void (*cmdfn)(struct tty *, const struct tty_ctx *),
@@ -2169,7 +1999,7 @@ tty_cmd_cell(struct tty *tty, const struct tty_ctx *ctx)
{
const struct grid_cell *gcp = ctx->cell;
struct screen *s = ctx->s;
struct overlay_ranges r;
struct visible_ranges *r;
u_int px, py, i, vis = 0;
px = ctx->xoff + ctx->ocx - ctx->wox;
@@ -2178,11 +2008,17 @@ tty_cmd_cell(struct tty *tty, const struct tty_ctx *ctx)
(gcp->data.width == 1 && !tty_check_overlay(tty, px, py)))
return;
if (ctx->num == 2) {
tty_draw_line(tty, s, 0, s->cy, screen_size_x(s),
ctx->xoff - ctx->wox, py, &ctx->defaults, ctx->palette);
return;
}
/* Handle partially obstructed wide characters. */
if (gcp->data.width > 1) {
tty_check_overlay_range(tty, px, py, gcp->data.width, &r);
for (i = 0; i < OVERLAY_MAX_RANGES; i++)
vis += r.nx[i];
r = tty_check_overlay_range(tty, px, py, gcp->data.width);
for (i = 0; i < r->used; i++)
vis += r->ranges[i].nx;
if (vis < gcp->data.width) {
tty_draw_line(tty, s, s->cx, s->cy, gcp->data.width,
px, py, &ctx->defaults, ctx->palette);
@@ -2208,7 +2044,8 @@ tty_cmd_cell(struct tty *tty, const struct tty_ctx *ctx)
void
tty_cmd_cells(struct tty *tty, const struct tty_ctx *ctx)
{
struct overlay_ranges r;
struct visible_ranges *r;
struct visible_range *rr;
u_int i, px, py, cx;
char *cp = ctx->ptr;
@@ -2233,20 +2070,21 @@ tty_cmd_cells(struct tty *tty, const struct tty_ctx *ctx)
tty_margin_off(tty);
tty_cursor_pane_unless_wrap(tty, ctx, ctx->ocx, ctx->ocy);
tty_attributes(tty, ctx->cell, &ctx->defaults, ctx->palette, ctx->s->hyperlinks);
tty_attributes(tty, ctx->cell, &ctx->defaults, ctx->palette,
ctx->s->hyperlinks);
/* Get tty position from pane position for overlay check. */
px = ctx->xoff + ctx->ocx - ctx->wox;
py = ctx->yoff + ctx->ocy - ctx->woy;
tty_check_overlay_range(tty, px, py, ctx->num, &r);
for (i = 0; i < OVERLAY_MAX_RANGES; i++) {
if (r.nx[i] == 0)
continue;
/* Convert back to pane position for printing. */
cx = r.px[i] - ctx->xoff + ctx->wox;
tty_cursor_pane_unless_wrap(tty, ctx, cx, ctx->ocy);
tty_putn(tty, cp + r.px[i] - px, r.nx[i], r.nx[i]);
r = tty_check_overlay_range(tty, px, py, ctx->num);
for (i = 0; i < r->used; i++) {
rr = &r->ranges[i];
if (rr->nx != 0) {
cx = rr->px - ctx->xoff + ctx->wox;
tty_cursor_pane_unless_wrap(tty, ctx, cx, ctx->ocy);
tty_putn(tty, cp + rr->px - px, rr->nx, rr->nx);
}
}
}
@@ -2257,7 +2095,7 @@ tty_cmd_setselection(struct tty *tty, const struct tty_ctx *ctx)
}
void
tty_set_selection(struct tty *tty, const char *flags, const char *buf,
tty_set_selection(struct tty *tty, const char *clip, const char *buf,
size_t len)
{
char *encoded;
@@ -2273,7 +2111,7 @@ tty_set_selection(struct tty *tty, const char *flags, const char *buf,
b64_ntop(buf, len, encoded, size);
tty->flags |= TTY_NOBLOCK;
tty_putcode_ss(tty, TTYC_MS, flags, encoded);
tty_putcode_ss(tty, TTYC_MS, clip, encoded);
free(encoded);
}
@@ -2286,12 +2124,12 @@ tty_cmd_rawstring(struct tty *tty, const struct tty_ctx *ctx)
tty_invalidate(tty);
}
#ifdef ENABLE_SIXEL
#ifdef ENABLE_SIXEL_IMAGES
void
tty_cmd_sixelimage(struct tty *tty, const struct tty_ctx *ctx)
{
struct image *im = ctx->ptr;
struct sixel_image *si = im->data;
struct sixel_image *si = im->data.sixel;
struct sixel_image *new;
char *data;
size_t size;
@@ -2338,6 +2176,142 @@ tty_cmd_sixelimage(struct tty *tty, const struct tty_ctx *ctx)
}
#endif
#ifdef ENABLE_KITTY_IMAGES
static int
tty_has_kitty(struct tty *tty)
{
return (tty->term->flags & TERM_KITTY);
}
void
tty_cmd_kittyimage(struct tty *tty, const struct tty_ctx *ctx)
{
struct image *im = ctx->ptr;
char *data;
size_t size;
u_int cx = ctx->ocx, cy = ctx->ocy;
int fallback = 0;
if (im == NULL || im->data.kitty == NULL)
return;
/* Check if this terminal supports kitty graphics. */
if (!tty_has_kitty(tty))
fallback = 1;
log_debug("%s: image at %u,%u (fallback=%d)", __func__, cx, cy,
fallback);
if (fallback == 1) {
/* Use text fallback for non-kitty terminals. */
data = xstrdup(im->fallback);
size = strlen(data);
} else {
/* Re-serialize the kitty image command. */
data = kitty_print(im->data.kitty, &size);
}
if (data != NULL) {
log_debug("%s: %zu bytes", __func__, size);
tty_region_off(tty);
tty_margin_off(tty);
tty_cursor(tty, cx + ctx->xoff, cy + ctx->yoff);
tty->flags |= TTY_NOBLOCK;
tty_add(tty, data, size);
tty_invalidate(tty);
free(data);
}
}
/*
* Pass a kitty APC sequence directly to all attached kitty-capable clients
* showing the given pane. The outer terminal's cursor is first moved to
* the pane-relative position (cx, cy) so that images placed at "current
* cursor" land in the right spot. Pass cx=cy=UINT_MAX to skip cursor
* positioning (e.g. for delete commands that don't depend on position).
*/
void
tty_kitty_passthrough(struct window_pane *wp, const char *data, size_t len,
u_int cx, u_int cy)
{
struct client *c;
struct tty *tty;
u_int x, y;
TAILQ_FOREACH(c, &clients, entry) {
if (c->session == NULL || c->tty.term == NULL)
continue;
if (c->flags & CLIENT_SUSPENDED)
continue;
if (c->tty.flags & TTY_FREEZE)
continue;
tty = &c->tty;
if (!tty_has_kitty(tty))
continue;
if (c->session->curw->window != wp->window)
continue;
if (!window_pane_visible(wp))
continue;
/* Position cursor at the correct screen location. */
if (cx != UINT_MAX && cy != UINT_MAX) {
x = wp->xoff + cx;
y = wp->yoff + cy;
if (status_at_line(c) == 0)
y += status_line_size(c);
tty_region_off(tty);
tty_margin_off(tty);
tty_cursor(tty, x, y);
}
tty->flags |= TTY_NOBLOCK;
tty_add(tty, data, len);
tty_invalidate(tty);
}
}
/*
* Delete all kitty image placements from the outer terminal unconditionally.
* Called directly (not via tty_write) so it fires on every full window
* redraw regardless of whether the current window has any stored images.
*/
void
tty_kitty_delete_all(struct tty *tty)
{
char *data;
size_t size;
if (!tty_has_kitty(tty))
return;
if ((data = kitty_delete_all(&size)) == NULL)
return;
tty->flags |= TTY_NOBLOCK;
tty_add(tty, data, size);
tty_invalidate(tty);
free(data);
}
/*
* Delete all kitty image placements via passthrough for a specific pane.
* Used on terminal reset (RIS) so images are cleared from the outer terminal.
*/
void
tty_kitty_delete_all_pane(struct window_pane *wp)
{
char *data;
size_t size;
if ((data = kitty_delete_all(&size)) == NULL)
return;
tty_kitty_passthrough(wp, data, size, UINT_MAX, UINT_MAX);
free(data);
}
#endif
void
tty_cmd_syncstart(struct tty *tty, const struct tty_ctx *ctx)
{
@@ -2380,7 +2354,6 @@ tty_cell(struct tty *tty, const struct grid_cell *gc,
/* If it is a single character, write with putc to handle ACS. */
if (gcp->data.size == 1) {
tty_attributes(tty, gcp, defaults, palette, hl);
if (*gcp->data.data < 0x20 || *gcp->data.data == 0x7f)
return;
tty_putc(tty, *gcp->data.data);
@@ -3193,7 +3166,7 @@ tty_default_colours(struct grid_cell *gc, struct window_pane *wp)
}
}
static void
void
tty_default_attributes(struct tty *tty, const struct grid_cell *defaults,
struct colour_palette *palette, u_int bg, struct hyperlinks *hl)
{
@@ -3208,12 +3181,6 @@ static void
tty_clipboard_query_callback(__unused int fd, __unused short events, void *data)
{
struct tty *tty = data;
struct client *c = tty->client;
c->flags &= ~CLIENT_CLIPBOARDBUFFER;
free(c->clipboard_panes);
c->clipboard_panes = NULL;
c->clipboard_npanes = 0;
tty->flags &= ~TTY_OSC52QUERY;
}
@@ -3223,11 +3190,9 @@ tty_clipboard_query(struct tty *tty)
{
struct timeval tv = { .tv_sec = TTY_QUERY_TIMEOUT };
if ((~tty->flags & TTY_STARTED) || (tty->flags & TTY_OSC52QUERY))
return;
tty_putcode_ss(tty, TTYC_MS, "", "?");
tty->flags |= TTY_OSC52QUERY;
evtimer_set(&tty->clipboard_timer, tty_clipboard_query_callback, tty);
evtimer_add(&tty->clipboard_timer, &tv);
if ((tty->flags & TTY_STARTED) && (~tty->flags & TTY_OSC52QUERY)) {
tty_putcode_ss(tty, TTYC_MS, "", "?");
tty->flags |= TTY_OSC52QUERY;
evtimer_add(&tty->clipboard_timer, &tv);
}
}

View File

@@ -122,6 +122,7 @@ utf8_should_combine(const struct utf8_data *with, const struct utf8_data *add)
case 0x1F47C:
case 0x1F481:
case 0x1F482:
case 0x1F483:
case 0x1F485:
case 0x1F486:
case 0x1F487:

10
utf8.c
View File

@@ -652,7 +652,7 @@ utf8_append(struct utf8_data *ud, u_char ch)
* bytes available for each character from src (for \abc or UTF-8) plus space
* for \0.
*/
int
size_t
utf8_strvis(char *dst, const char *src, size_t len, int flag)
{
struct utf8_data ud;
@@ -690,11 +690,11 @@ utf8_strvis(char *dst, const char *src, size_t len, int flag)
}
/* Same as utf8_strvis but allocate the buffer. */
int
size_t
utf8_stravis(char **dst, const char *src, int flag)
{
char *buf;
int len;
size_t len;
buf = xreallocarray(NULL, 4, strlen(src) + 1);
len = utf8_strvis(buf, src, strlen(src), flag);
@@ -704,11 +704,11 @@ utf8_stravis(char **dst, const char *src, int flag)
}
/* Same as utf8_strvis but allocate the buffer. */
int
size_t
utf8_stravisx(char **dst, const char *src, size_t srclen, int flag)
{
char *buf;
int len;
size_t len;
buf = xreallocarray(NULL, 4, srclen + 1);
len = utf8_strvis(buf, src, srclen, flag);

View File

@@ -76,18 +76,6 @@ const struct window_mode window_buffer_mode = {
.key = window_buffer_key,
};
enum window_buffer_sort_type {
WINDOW_BUFFER_BY_TIME,
WINDOW_BUFFER_BY_NAME,
WINDOW_BUFFER_BY_SIZE,
};
static const char *window_buffer_sort_list[] = {
"time",
"name",
"size"
};
static struct mode_tree_sort_criteria *window_buffer_sort;
struct window_buffer_itemdata {
const char *name;
u_int order;
@@ -113,6 +101,13 @@ struct window_buffer_editdata {
struct paste_buffer *pb;
};
static enum sort_order window_buffer_order_seq[] = {
SORT_CREATION,
SORT_NAME,
SORT_SIZE,
SORT_END,
};
static struct window_buffer_itemdata *
window_buffer_add_item(struct window_buffer_modedata *data)
{
@@ -131,35 +126,14 @@ window_buffer_free_item(struct window_buffer_itemdata *item)
free(item);
}
static int
window_buffer_cmp(const void *a0, const void *b0)
{
const struct window_buffer_itemdata *const *a = a0;
const struct window_buffer_itemdata *const *b = b0;
int result = 0;
if (window_buffer_sort->field == WINDOW_BUFFER_BY_TIME)
result = (*b)->order - (*a)->order;
else if (window_buffer_sort->field == WINDOW_BUFFER_BY_SIZE)
result = (*b)->size - (*a)->size;
/* Use WINDOW_BUFFER_BY_NAME as default order and tie breaker. */
if (result == 0)
result = strcmp((*a)->name, (*b)->name);
if (window_buffer_sort->reversed)
result = -result;
return (result);
}
static void
window_buffer_build(void *modedata, struct mode_tree_sort_criteria *sort_crit,
window_buffer_build(void *modedata, struct sort_criteria *sort_crit,
__unused uint64_t *tag, const char *filter)
{
struct window_buffer_modedata *data = modedata;
struct window_buffer_itemdata *item;
u_int i;
struct paste_buffer *pb = NULL;
u_int i, n;
struct paste_buffer *pb, **l;
char *text, *cp;
struct format_tree *ft;
struct session *s = NULL;
@@ -172,17 +146,14 @@ window_buffer_build(void *modedata, struct mode_tree_sort_criteria *sort_crit,
data->item_list = NULL;
data->item_size = 0;
while ((pb = paste_walk(pb)) != NULL) {
l = sort_get_buffers(&n, sort_crit);
for (i = 0; i < n; i++) {
item = window_buffer_add_item(data);
item->name = xstrdup(paste_buffer_name(pb));
paste_buffer_data(pb, &item->size);
item->order = paste_buffer_order(pb);
item->name = xstrdup(paste_buffer_name(l[i]));
paste_buffer_data(l[i], &item->size);
item->order = paste_buffer_order(l[i]);
}
window_buffer_sort = sort_crit;
qsort(data->item_list, data->item_size, sizeof *data->item_list,
window_buffer_cmp);
if (cmd_find_valid_state(&data->fs)) {
s = data->fs.s;
wl = data->fs.wl;
@@ -216,7 +187,6 @@ window_buffer_build(void *modedata, struct mode_tree_sort_criteria *sort_crit,
format_free(ft);
}
}
static void
@@ -351,6 +321,14 @@ window_buffer_get_key(void *modedata, void *itemdata, u_int line)
return (key);
}
static void
window_buffer_sort(struct sort_criteria *sort_crit)
{
sort_crit->order_seq = window_buffer_order_seq;
if (sort_crit->order == SORT_END)
sort_crit->order = sort_crit->order_seq[0];
}
static struct screen *
window_buffer_init(struct window_mode_entry *wme, struct cmd_find_state *fs,
struct args *args)
@@ -378,8 +356,8 @@ window_buffer_init(struct window_mode_entry *wme, struct cmd_find_state *fs,
data->data = mode_tree_start(wp, args, window_buffer_build,
window_buffer_draw, window_buffer_search, window_buffer_menu, NULL,
window_buffer_get_key, NULL, data, window_buffer_menu_items,
window_buffer_sort_list, nitems(window_buffer_sort_list), &s);
window_buffer_get_key, NULL, window_buffer_sort, data,
window_buffer_menu_items, &s);
mode_tree_zoom(data->data, args);
mode_tree_build(data->data);

View File

@@ -71,20 +71,6 @@ const struct window_mode window_client_mode = {
.key = window_client_key,
};
enum window_client_sort_type {
WINDOW_CLIENT_BY_NAME,
WINDOW_CLIENT_BY_SIZE,
WINDOW_CLIENT_BY_CREATION_TIME,
WINDOW_CLIENT_BY_ACTIVITY_TIME,
};
static const char *window_client_sort_list[] = {
"name",
"size",
"creation",
"activity"
};
static struct mode_tree_sort_criteria *window_client_sort;
struct window_client_itemdata {
struct client *c;
};
@@ -101,6 +87,14 @@ struct window_client_modedata {
u_int item_size;
};
static enum sort_order window_client_order_seq[] = {
SORT_NAME,
SORT_SIZE,
SORT_CREATION,
SORT_ACTIVITY,
SORT_END,
};
static struct window_client_itemdata *
window_client_add_item(struct window_client_modedata *data)
{
@@ -119,54 +113,14 @@ window_client_free_item(struct window_client_itemdata *item)
free(item);
}
static int
window_client_cmp(const void *a0, const void *b0)
{
const struct window_client_itemdata *const *a = a0;
const struct window_client_itemdata *const *b = b0;
const struct window_client_itemdata *itema = *a;
const struct window_client_itemdata *itemb = *b;
struct client *ca = itema->c;
struct client *cb = itemb->c;
int result = 0;
switch (window_client_sort->field) {
case WINDOW_CLIENT_BY_SIZE:
result = ca->tty.sx - cb->tty.sx;
if (result == 0)
result = ca->tty.sy - cb->tty.sy;
break;
case WINDOW_CLIENT_BY_CREATION_TIME:
if (timercmp(&ca->creation_time, &cb->creation_time, >))
result = -1;
else if (timercmp(&ca->creation_time, &cb->creation_time, <))
result = 1;
break;
case WINDOW_CLIENT_BY_ACTIVITY_TIME:
if (timercmp(&ca->activity_time, &cb->activity_time, >))
result = -1;
else if (timercmp(&ca->activity_time, &cb->activity_time, <))
result = 1;
break;
}
/* Use WINDOW_CLIENT_BY_NAME as default order and tie breaker. */
if (result == 0)
result = strcmp(ca->name, cb->name);
if (window_client_sort->reversed)
result = -result;
return (result);
}
static void
window_client_build(void *modedata, struct mode_tree_sort_criteria *sort_crit,
window_client_build(void *modedata, struct sort_criteria *sort_crit,
__unused uint64_t *tag, const char *filter)
{
struct window_client_modedata *data = modedata;
struct window_client_itemdata *item;
u_int i;
struct client *c;
u_int i, n;
struct client *c, **l;
char *text, *cp;
for (i = 0; i < data->item_size; i++)
@@ -175,20 +129,18 @@ window_client_build(void *modedata, struct mode_tree_sort_criteria *sort_crit,
data->item_list = NULL;
data->item_size = 0;
TAILQ_FOREACH(c, &clients, entry) {
if (c->session == NULL || (c->flags & CLIENT_UNATTACHEDFLAGS))
l = sort_get_clients(&n, sort_crit);
for (i = 0; i < n; i++) {
if (l[i]->session == NULL ||
(l[i]->flags & CLIENT_UNATTACHEDFLAGS))
continue;
item = window_client_add_item(data);
item->c = c;
item->c = l[i];
c->references++;
l[i]->references++;
}
window_client_sort = sort_crit;
qsort(data->item_list, data->item_size, sizeof *data->item_list,
window_client_cmp);
for (i = 0; i < data->item_size; i++) {
item = data->item_list[i];
c = item->c;
@@ -280,6 +232,14 @@ window_client_get_key(void *modedata, void *itemdata, u_int line)
return (key);
}
static void
window_client_sort(struct sort_criteria *sort_crit)
{
sort_crit->order_seq = window_client_order_seq;
if (sort_crit->order == SORT_END)
sort_crit->order = sort_crit->order_seq[0];
}
static struct screen *
window_client_init(struct window_mode_entry *wme,
__unused struct cmd_find_state *fs, struct args *args)
@@ -306,8 +266,8 @@ window_client_init(struct window_mode_entry *wme,
data->data = mode_tree_start(wp, args, window_client_build,
window_client_draw, NULL, window_client_menu, NULL,
window_client_get_key, NULL, data, window_client_menu_items,
window_client_sort_list, nitems(window_client_sort_list), &s);
window_client_get_key, NULL, window_client_sort, data,
window_client_menu_items, &s);
mode_tree_zoom(data->data, args);
mode_tree_build(data->data);

View File

@@ -123,6 +123,26 @@ const char window_clock_table[14][5][5] = {
{ 1,0,0,0,1 } },
};
static void
window_clock_start_timer(struct window_mode_entry *wme)
{
struct window_clock_mode_data *data = wme->data;
struct timeval tv;
struct timespec ts;
long delay;
clock_gettime(CLOCK_REALTIME, &ts);
delay = 1000000 - (ts.tv_nsec / 1000);
tv.tv_sec = delay / 1000000;
tv.tv_usec = delay % 1000000;
if (tv.tv_sec < 0 || (tv.tv_sec == 0 && tv.tv_usec <= 0)) {
tv.tv_sec = 1;
tv.tv_usec = 0;
}
evtimer_add(&data->timer, &tv);
}
static void
window_clock_timer_callback(__unused int fd, __unused short events, void *arg)
{
@@ -131,23 +151,20 @@ window_clock_timer_callback(__unused int fd, __unused short events, void *arg)
struct window_clock_mode_data *data = wme->data;
struct tm now, then;
time_t t;
struct timeval tv = { .tv_sec = 1 };
evtimer_del(&data->timer);
evtimer_add(&data->timer, &tv);
if (TAILQ_FIRST(&wp->modes) != wme)
return;
t = time(NULL);
gmtime_r(&t, &now);
gmtime_r(&data->tim, &then);
if (now.tm_sec == then.tm_sec)
return;
data->tim = t;
window_clock_draw_screen(wme);
wp->flags |= PANE_REDRAW;
if (now.tm_sec != then.tm_sec) {
data->tim = t;
window_clock_draw_screen(wme);
wp->flags |= PANE_REDRAW;
}
window_clock_start_timer(wme);
}
static struct screen *
@@ -157,13 +174,12 @@ window_clock_init(struct window_mode_entry *wme,
struct window_pane *wp = wme->wp;
struct window_clock_mode_data *data;
struct screen *s;
struct timeval tv = { .tv_sec = 1 };
wme->data = data = xmalloc(sizeof *data);
data->tim = time(NULL);
evtimer_set(&data->timer, window_clock_timer_callback, wme);
evtimer_add(&data->timer, &tv);
window_clock_start_timer(wme);
s = &data->screen;
screen_init(s, screen_size_x(&wp->base), screen_size_y(&wp->base), 0);
@@ -212,7 +228,6 @@ window_clock_draw_screen(struct window_mode_entry *wme)
struct screen *s = &data->screen;
struct grid_cell gc;
char tim[64], *ptr;
const char *timeformat;
time_t t;
struct tm *tm;
u_int i, j, x, y, idx;
@@ -226,20 +241,18 @@ window_clock_draw_screen(struct window_mode_entry *wme)
tm = localtime(&t);
if (style == 0 || style == 2) {
if (style == 2)
timeformat = "%l:%M:%S ";
strftime(tim, sizeof tim, "%l:%M:%S ", localtime(&t));
else
timeformat = "%l:%M ";
strftime(tim, sizeof tim, timeformat, localtime(&t));
strftime(tim, sizeof tim, "%l:%M ", localtime(&t));
if (tm->tm_hour >= 12)
strlcat(tim, "PM", sizeof tim);
else
strlcat(tim, "AM", sizeof tim);
} else {
if (style == 3)
timeformat = "%H:%M:%S";
strftime(tim, sizeof tim, "%H:%M:%S", tm);
else
timeformat = "%H:%M";
strftime(tim, sizeof tim, timeformat, tm);
strftime(tim, sizeof tim, "%H:%M", tm);
}
screen_write_clearscreen(&ctx, 8);

View File

@@ -51,6 +51,7 @@ static void window_copy_redraw_selection(struct window_mode_entry *, u_int);
static void window_copy_redraw_lines(struct window_mode_entry *, u_int,
u_int);
static void window_copy_redraw_screen(struct window_mode_entry *);
static void window_copy_style_changed(struct window_mode_entry *);
static void window_copy_write_line(struct window_mode_entry *,
struct screen_write_ctx *, u_int);
static void window_copy_write_lines(struct window_mode_entry *,
@@ -158,6 +159,7 @@ const struct window_mode window_copy_mode = {
.init = window_copy_init,
.free = window_copy_free,
.resize = window_copy_resize,
.style_changed = window_copy_style_changed,
.key_table = window_copy_key_table,
.command = window_copy_command,
.formats = window_copy_formats,
@@ -170,6 +172,7 @@ const struct window_mode window_view_mode = {
.init = window_copy_view_init,
.free = window_copy_free,
.resize = window_copy_resize,
.style_changed = window_copy_style_changed,
.key_table = window_copy_key_table,
.command = window_copy_command,
.formats = window_copy_formats,
@@ -352,7 +355,7 @@ window_copy_clone_screen(struct screen *src, struct screen *hint, u_int *cx,
if (trim) {
while (sy > screen_hsize(src)) {
gl = grid_peek_line(src->grid, sy - 1);
if (gl->cellused != 0)
if (gl == NULL || gl->cellused != 0)
break;
sy--;
}
@@ -491,7 +494,7 @@ window_copy_view_init(struct window_mode_entry *wme,
data->backing = xmalloc(sizeof *data->backing);
screen_init(data->backing, sx, screen_size_y(base), UINT_MAX);
data->ictx = input_init(NULL, NULL, NULL);
data->ictx = input_init(NULL, NULL, NULL, NULL);
data->mx = data->cx;
data->my = screen_hsize(data->backing) + data->cy - data->oy;
data->showmark = 0;
@@ -627,7 +630,7 @@ window_copy_scroll1(struct window_mode_entry *wme, struct window_pane *wp,
new_slider_y = sb_top - wp->yoff + (sb_height - slider_height);
} else {
/* Slider is somewhere in the middle. */
new_slider_y = my - wp->yoff - sl_mpos + 1;
new_slider_y = my - wp->yoff - sl_mpos;
}
if (TAILQ_FIRST(&wp->modes) == NULL ||
@@ -956,6 +959,18 @@ window_copy_formats(struct window_mode_entry *wme, struct format_tree *ft)
format_add(ft, "selection_present", "0");
}
switch (data->selflag) {
case SEL_CHAR:
format_add(ft, "selection_mode", "char");
break;
case SEL_WORD:
format_add(ft, "selection_mode", "word");
break;
case SEL_LINE:
format_add(ft, "selection_mode", "line");
break;
}
format_add(ft, "search_present", "%d", data->searchmark != NULL);
format_add(ft, "search_timed_out", "%d", data->timeout);
if (data->searchcount != -1) {
@@ -1479,6 +1494,20 @@ window_copy_cmd_scroll_middle(struct window_copy_cmd_state *cs)
return (window_copy_cmd_scroll_to(cs, mid_value));
}
/* Scroll the pane to the mouse in the scrollbar. */
static enum window_copy_cmd_action
window_copy_cmd_scroll_to_mouse(struct window_copy_cmd_state *cs)
{
struct window_mode_entry *wme = cs->wme;
struct window_pane *wp = wme->wp;
struct client *c = cs->c;
struct mouse_event *m = cs->m;
int scroll_exit = args_has(cs->wargs, 'e');
window_copy_scroll(wp, c->tty.mouse_slider_mpos, m->y, scroll_exit);
return (WINDOW_COPY_CMD_NOTHING);
}
/* Scroll line containing the cursor to the top. */
static enum window_copy_cmd_action
window_copy_cmd_scroll_top(struct window_copy_cmd_state *cs)
@@ -2098,6 +2127,36 @@ window_copy_cmd_rectangle_toggle(struct window_copy_cmd_state *cs)
return (WINDOW_COPY_CMD_NOTHING);
}
static enum window_copy_cmd_action
window_copy_cmd_scroll_exit_on(struct window_copy_cmd_state *cs)
{
struct window_copy_mode_data *data = cs->wme->data;
data->scroll_exit = 1;
return (WINDOW_COPY_CMD_NOTHING);
}
static enum window_copy_cmd_action
window_copy_cmd_scroll_exit_off(struct window_copy_cmd_state *cs)
{
struct window_copy_mode_data *data = cs->wme->data;
data->scroll_exit = 0;
return (WINDOW_COPY_CMD_NOTHING);
}
static enum window_copy_cmd_action
window_copy_cmd_scroll_exit_toggle(struct window_copy_cmd_state *cs)
{
struct window_copy_mode_data *data = cs->wme->data;
data->scroll_exit = !data->scroll_exit;
return (WINDOW_COPY_CMD_NOTHING);
}
static enum window_copy_cmd_action
window_copy_cmd_scroll_down(struct window_copy_cmd_state *cs)
{
@@ -2673,15 +2732,24 @@ window_copy_cmd_refresh_from_pane(struct window_copy_cmd_state *cs)
struct window_mode_entry *wme = cs->wme;
struct window_pane *wp = wme->swp;
struct window_copy_mode_data *data = wme->data;
u_int oy_from_top;
if (data->viewmode)
return (WINDOW_COPY_CMD_NOTHING);
oy_from_top = screen_hsize(data->backing) - data->oy;
screen_free(data->backing);
free(data->backing);
data->backing = window_copy_clone_screen(&wp->base, &data->screen, NULL,
NULL, wme->swp != wme->wp);
if (oy_from_top <= screen_hsize(data->backing))
data->oy = screen_hsize(data->backing) - oy_from_top;
else {
data->cy = 0;
data->oy = screen_hsize(data->backing);
}
window_copy_size_changed(wme);
return (WINDOW_COPY_CMD_REDRAW);
}
@@ -2691,446 +2759,559 @@ static const struct {
u_int minargs;
u_int maxargs;
struct args_parse args;
#define WINDOW_COPY_CMD_FLAG_READONLY 0x1
int flags;
enum window_copy_cmd_clear clear;
enum window_copy_cmd_action (*f)(struct window_copy_cmd_state *);
} window_copy_cmd_table[] = {
{ .command = "append-selection",
.args = { "", 0, 0, NULL },
.flags = 0,
.clear = WINDOW_COPY_CMD_CLEAR_ALWAYS,
.f = window_copy_cmd_append_selection
},
{ .command = "append-selection-and-cancel",
.args = { "", 0, 0, NULL },
.flags = 0,
.clear = WINDOW_COPY_CMD_CLEAR_ALWAYS,
.f = window_copy_cmd_append_selection_and_cancel
},
{ .command = "back-to-indentation",
.args = { "", 0, 0, NULL },
.flags = WINDOW_COPY_CMD_FLAG_READONLY,
.clear = WINDOW_COPY_CMD_CLEAR_ALWAYS,
.f = window_copy_cmd_back_to_indentation
},
{ .command = "begin-selection",
.args = { "", 0, 0, NULL },
.flags = 0,
.clear = WINDOW_COPY_CMD_CLEAR_ALWAYS,
.f = window_copy_cmd_begin_selection
},
{ .command = "bottom-line",
.args = { "", 0, 0, NULL },
.flags = WINDOW_COPY_CMD_FLAG_READONLY,
.clear = WINDOW_COPY_CMD_CLEAR_EMACS_ONLY,
.f = window_copy_cmd_bottom_line
},
{ .command = "cancel",
.args = { "", 0, 0, NULL },
.flags = WINDOW_COPY_CMD_FLAG_READONLY,
.clear = WINDOW_COPY_CMD_CLEAR_ALWAYS,
.f = window_copy_cmd_cancel
},
{ .command = "clear-selection",
.args = { "", 0, 0, NULL },
.flags = 0,
.clear = WINDOW_COPY_CMD_CLEAR_ALWAYS,
.f = window_copy_cmd_clear_selection
},
{ .command = "copy-end-of-line",
.args = { "CP", 0, 1, NULL },
.flags = 0,
.clear = WINDOW_COPY_CMD_CLEAR_ALWAYS,
.f = window_copy_cmd_copy_end_of_line
},
{ .command = "copy-end-of-line-and-cancel",
.args = { "CP", 0, 1, NULL },
.flags = 0,
.clear = WINDOW_COPY_CMD_CLEAR_ALWAYS,
.f = window_copy_cmd_copy_end_of_line_and_cancel
},
{ .command = "copy-pipe-end-of-line",
.args = { "CP", 0, 2, NULL },
.flags = 0,
.clear = WINDOW_COPY_CMD_CLEAR_ALWAYS,
.f = window_copy_cmd_copy_pipe_end_of_line
},
{ .command = "copy-pipe-end-of-line-and-cancel",
.args = { "CP", 0, 2, NULL },
.flags = 0,
.clear = WINDOW_COPY_CMD_CLEAR_ALWAYS,
.f = window_copy_cmd_copy_pipe_end_of_line_and_cancel
},
{ .command = "copy-line",
.args = { "CP", 0, 1, NULL },
.flags = 0,
.clear = WINDOW_COPY_CMD_CLEAR_ALWAYS,
.f = window_copy_cmd_copy_line
},
{ .command = "copy-line-and-cancel",
.args = { "CP", 0, 1, NULL },
.flags = 0,
.clear = WINDOW_COPY_CMD_CLEAR_ALWAYS,
.f = window_copy_cmd_copy_line_and_cancel
},
{ .command = "copy-pipe-line",
.args = { "CP", 0, 2, NULL },
.flags = 0,
.clear = WINDOW_COPY_CMD_CLEAR_ALWAYS,
.f = window_copy_cmd_copy_pipe_line
},
{ .command = "copy-pipe-line-and-cancel",
.args = { "CP", 0, 2, NULL },
.flags = 0,
.clear = WINDOW_COPY_CMD_CLEAR_ALWAYS,
.f = window_copy_cmd_copy_pipe_line_and_cancel
},
{ .command = "copy-pipe-no-clear",
.args = { "CP", 0, 2, NULL },
.flags = 0,
.clear = WINDOW_COPY_CMD_CLEAR_NEVER,
.f = window_copy_cmd_copy_pipe_no_clear
},
{ .command = "copy-pipe",
.args = { "CP", 0, 2, NULL },
.flags = 0,
.clear = WINDOW_COPY_CMD_CLEAR_ALWAYS,
.f = window_copy_cmd_copy_pipe
},
{ .command = "copy-pipe-and-cancel",
.args = { "CP", 0, 2, NULL },
.flags = 0,
.clear = WINDOW_COPY_CMD_CLEAR_ALWAYS,
.f = window_copy_cmd_copy_pipe_and_cancel
},
{ .command = "copy-selection-no-clear",
.args = { "CP", 0, 1, NULL },
.flags = 0,
.clear = WINDOW_COPY_CMD_CLEAR_NEVER,
.f = window_copy_cmd_copy_selection_no_clear
},
{ .command = "copy-selection",
.args = { "CP", 0, 1, NULL },
.flags = 0,
.clear = WINDOW_COPY_CMD_CLEAR_ALWAYS,
.f = window_copy_cmd_copy_selection
},
{ .command = "copy-selection-and-cancel",
.args = { "CP", 0, 1, NULL },
.flags = 0,
.clear = WINDOW_COPY_CMD_CLEAR_ALWAYS,
.f = window_copy_cmd_copy_selection_and_cancel
},
{ .command = "cursor-down",
.args = { "", 0, 0, NULL },
.flags = WINDOW_COPY_CMD_FLAG_READONLY,
.clear = WINDOW_COPY_CMD_CLEAR_EMACS_ONLY,
.f = window_copy_cmd_cursor_down
},
{ .command = "cursor-down-and-cancel",
.args = { "", 0, 0, NULL },
.flags = WINDOW_COPY_CMD_FLAG_READONLY,
.clear = WINDOW_COPY_CMD_CLEAR_ALWAYS,
.f = window_copy_cmd_cursor_down_and_cancel
},
{ .command = "cursor-left",
.args = { "", 0, 0, NULL },
.flags = WINDOW_COPY_CMD_FLAG_READONLY,
.clear = WINDOW_COPY_CMD_CLEAR_EMACS_ONLY,
.f = window_copy_cmd_cursor_left
},
{ .command = "cursor-right",
.args = { "", 0, 0, NULL },
.flags = WINDOW_COPY_CMD_FLAG_READONLY,
.clear = WINDOW_COPY_CMD_CLEAR_EMACS_ONLY,
.f = window_copy_cmd_cursor_right
},
{ .command = "cursor-up",
.args = { "", 0, 0, NULL },
.flags = WINDOW_COPY_CMD_FLAG_READONLY,
.clear = WINDOW_COPY_CMD_CLEAR_EMACS_ONLY,
.f = window_copy_cmd_cursor_up
},
{ .command = "cursor-centre-vertical",
.args = { "", 0, 0, NULL },
.flags = WINDOW_COPY_CMD_FLAG_READONLY,
.clear = WINDOW_COPY_CMD_CLEAR_EMACS_ONLY,
.f = window_copy_cmd_centre_vertical,
},
{ .command = "cursor-centre-horizontal",
.args = { "", 0, 0, NULL },
.flags = WINDOW_COPY_CMD_FLAG_READONLY,
.clear = WINDOW_COPY_CMD_CLEAR_EMACS_ONLY,
.f = window_copy_cmd_centre_horizontal,
},
{ .command = "end-of-line",
.args = { "", 0, 0, NULL },
.flags = WINDOW_COPY_CMD_FLAG_READONLY,
.clear = WINDOW_COPY_CMD_CLEAR_EMACS_ONLY,
.f = window_copy_cmd_end_of_line
},
{ .command = "goto-line",
.args = { "", 1, 1, NULL },
.flags = WINDOW_COPY_CMD_FLAG_READONLY,
.clear = WINDOW_COPY_CMD_CLEAR_EMACS_ONLY,
.f = window_copy_cmd_goto_line
},
{ .command = "halfpage-down",
.args = { "", 0, 0, NULL },
.flags = WINDOW_COPY_CMD_FLAG_READONLY,
.clear = WINDOW_COPY_CMD_CLEAR_EMACS_ONLY,
.f = window_copy_cmd_halfpage_down
},
{ .command = "halfpage-down-and-cancel",
.args = { "", 0, 0, NULL },
.flags = WINDOW_COPY_CMD_FLAG_READONLY,
.clear = WINDOW_COPY_CMD_CLEAR_ALWAYS,
.f = window_copy_cmd_halfpage_down_and_cancel
},
{ .command = "halfpage-up",
.args = { "", 0, 0, NULL },
.flags = WINDOW_COPY_CMD_FLAG_READONLY,
.clear = WINDOW_COPY_CMD_CLEAR_EMACS_ONLY,
.f = window_copy_cmd_halfpage_up
},
{ .command = "history-bottom",
.args = { "", 0, 0, NULL },
.flags = WINDOW_COPY_CMD_FLAG_READONLY,
.clear = WINDOW_COPY_CMD_CLEAR_EMACS_ONLY,
.f = window_copy_cmd_history_bottom
},
{ .command = "history-top",
.args = { "", 0, 0, NULL },
.flags = WINDOW_COPY_CMD_FLAG_READONLY,
.clear = WINDOW_COPY_CMD_CLEAR_EMACS_ONLY,
.f = window_copy_cmd_history_top
},
{ .command = "jump-again",
.args = { "", 0, 0, NULL },
.flags = 0,
.clear = WINDOW_COPY_CMD_CLEAR_EMACS_ONLY,
.f = window_copy_cmd_jump_again
},
{ .command = "jump-backward",
.args = { "", 1, 1, NULL },
.flags = 0,
.clear = WINDOW_COPY_CMD_CLEAR_EMACS_ONLY,
.f = window_copy_cmd_jump_backward
},
{ .command = "jump-forward",
.args = { "", 1, 1, NULL },
.flags = 0,
.clear = WINDOW_COPY_CMD_CLEAR_EMACS_ONLY,
.f = window_copy_cmd_jump_forward
},
{ .command = "jump-reverse",
.args = { "", 0, 0, NULL },
.flags = 0,
.clear = WINDOW_COPY_CMD_CLEAR_EMACS_ONLY,
.f = window_copy_cmd_jump_reverse
},
{ .command = "jump-to-backward",
.args = { "", 1, 1, NULL },
.flags = 0,
.clear = WINDOW_COPY_CMD_CLEAR_EMACS_ONLY,
.f = window_copy_cmd_jump_to_backward
},
{ .command = "jump-to-forward",
.args = { "", 1, 1, NULL },
.flags = 0,
.clear = WINDOW_COPY_CMD_CLEAR_EMACS_ONLY,
.f = window_copy_cmd_jump_to_forward
},
{ .command = "jump-to-mark",
.args = { "", 0, 0, NULL },
.flags = WINDOW_COPY_CMD_FLAG_READONLY,
.clear = WINDOW_COPY_CMD_CLEAR_ALWAYS,
.f = window_copy_cmd_jump_to_mark
},
{ .command = "next-prompt",
.args = { "o", 0, 0, NULL },
.flags = WINDOW_COPY_CMD_FLAG_READONLY,
.clear = WINDOW_COPY_CMD_CLEAR_ALWAYS,
.f = window_copy_cmd_next_prompt
},
{ .command = "previous-prompt",
.args = { "o", 0, 0, NULL },
.flags = WINDOW_COPY_CMD_FLAG_READONLY,
.clear = WINDOW_COPY_CMD_CLEAR_ALWAYS,
.f = window_copy_cmd_previous_prompt
},
{ .command = "middle-line",
.args = { "", 0, 0, NULL },
.flags = WINDOW_COPY_CMD_FLAG_READONLY,
.clear = WINDOW_COPY_CMD_CLEAR_EMACS_ONLY,
.f = window_copy_cmd_middle_line
},
{ .command = "next-matching-bracket",
.args = { "", 0, 0, NULL },
.flags = WINDOW_COPY_CMD_FLAG_READONLY,
.clear = WINDOW_COPY_CMD_CLEAR_ALWAYS,
.f = window_copy_cmd_next_matching_bracket
},
{ .command = "next-paragraph",
.args = { "", 0, 0, NULL },
.flags = WINDOW_COPY_CMD_FLAG_READONLY,
.clear = WINDOW_COPY_CMD_CLEAR_EMACS_ONLY,
.f = window_copy_cmd_next_paragraph
},
{ .command = "next-space",
.args = { "", 0, 0, NULL },
.flags = WINDOW_COPY_CMD_FLAG_READONLY,
.clear = WINDOW_COPY_CMD_CLEAR_EMACS_ONLY,
.f = window_copy_cmd_next_space
},
{ .command = "next-space-end",
.args = { "", 0, 0, NULL },
.flags = WINDOW_COPY_CMD_FLAG_READONLY,
.clear = WINDOW_COPY_CMD_CLEAR_EMACS_ONLY,
.f = window_copy_cmd_next_space_end
},
{ .command = "next-word",
.args = { "", 0, 0, NULL },
.flags = WINDOW_COPY_CMD_FLAG_READONLY,
.clear = WINDOW_COPY_CMD_CLEAR_EMACS_ONLY,
.f = window_copy_cmd_next_word
},
{ .command = "next-word-end",
.args = { "", 0, 0, NULL },
.flags = WINDOW_COPY_CMD_FLAG_READONLY,
.clear = WINDOW_COPY_CMD_CLEAR_EMACS_ONLY,
.f = window_copy_cmd_next_word_end
},
{ .command = "other-end",
.args = { "", 0, 0, NULL },
.flags = 0,
.clear = WINDOW_COPY_CMD_CLEAR_EMACS_ONLY,
.f = window_copy_cmd_other_end
},
{ .command = "page-down",
.args = { "", 0, 0, NULL },
.flags = WINDOW_COPY_CMD_FLAG_READONLY,
.clear = WINDOW_COPY_CMD_CLEAR_EMACS_ONLY,
.f = window_copy_cmd_page_down
},
{ .command = "page-down-and-cancel",
.args = { "", 0, 0, NULL },
.flags = WINDOW_COPY_CMD_FLAG_READONLY,
.clear = WINDOW_COPY_CMD_CLEAR_ALWAYS,
.f = window_copy_cmd_page_down_and_cancel
},
{ .command = "page-up",
.args = { "", 0, 0, NULL },
.flags = WINDOW_COPY_CMD_FLAG_READONLY,
.clear = WINDOW_COPY_CMD_CLEAR_EMACS_ONLY,
.f = window_copy_cmd_page_up
},
{ .command = "pipe-no-clear",
.args = { "", 0, 1, NULL },
.flags = 0,
.clear = WINDOW_COPY_CMD_CLEAR_NEVER,
.f = window_copy_cmd_pipe_no_clear
},
{ .command = "pipe",
.args = { "", 0, 1, NULL },
.flags = 0,
.clear = WINDOW_COPY_CMD_CLEAR_ALWAYS,
.f = window_copy_cmd_pipe
},
{ .command = "pipe-and-cancel",
.args = { "", 0, 1, NULL },
.flags = 0,
.clear = WINDOW_COPY_CMD_CLEAR_ALWAYS,
.f = window_copy_cmd_pipe_and_cancel
},
{ .command = "previous-matching-bracket",
.args = { "", 0, 0, NULL },
.flags = WINDOW_COPY_CMD_FLAG_READONLY,
.clear = WINDOW_COPY_CMD_CLEAR_ALWAYS,
.f = window_copy_cmd_previous_matching_bracket
},
{ .command = "previous-paragraph",
.args = { "", 0, 0, NULL },
.flags = WINDOW_COPY_CMD_FLAG_READONLY,
.clear = WINDOW_COPY_CMD_CLEAR_EMACS_ONLY,
.f = window_copy_cmd_previous_paragraph
},
{ .command = "previous-space",
.args = { "", 0, 0, NULL },
.flags = WINDOW_COPY_CMD_FLAG_READONLY,
.clear = WINDOW_COPY_CMD_CLEAR_EMACS_ONLY,
.f = window_copy_cmd_previous_space
},
{ .command = "previous-word",
.args = { "", 0, 0, NULL },
.flags = WINDOW_COPY_CMD_FLAG_READONLY,
.clear = WINDOW_COPY_CMD_CLEAR_EMACS_ONLY,
.f = window_copy_cmd_previous_word
},
{ .command = "rectangle-on",
.args = { "", 0, 0, NULL },
.flags = 0,
.clear = WINDOW_COPY_CMD_CLEAR_ALWAYS,
.f = window_copy_cmd_rectangle_on
},
{ .command = "rectangle-off",
.args = { "", 0, 0, NULL },
.flags = 0,
.clear = WINDOW_COPY_CMD_CLEAR_ALWAYS,
.f = window_copy_cmd_rectangle_off
},
{ .command = "rectangle-toggle",
.args = { "", 0, 0, NULL },
.flags = 0,
.clear = WINDOW_COPY_CMD_CLEAR_ALWAYS,
.f = window_copy_cmd_rectangle_toggle
},
{ .command = "refresh-from-pane",
.args = { "", 0, 0, NULL },
.flags = WINDOW_COPY_CMD_FLAG_READONLY,
.clear = WINDOW_COPY_CMD_CLEAR_ALWAYS,
.f = window_copy_cmd_refresh_from_pane
},
{ .command = "scroll-bottom",
.args = { "", 0, 0, NULL },
.flags = WINDOW_COPY_CMD_FLAG_READONLY,
.clear = WINDOW_COPY_CMD_CLEAR_ALWAYS,
.f = window_copy_cmd_scroll_bottom
},
{ .command = "scroll-down",
.args = { "", 0, 0, NULL },
.flags = WINDOW_COPY_CMD_FLAG_READONLY,
.clear = WINDOW_COPY_CMD_CLEAR_EMACS_ONLY,
.f = window_copy_cmd_scroll_down
},
{ .command = "scroll-down-and-cancel",
.args = { "", 0, 0, NULL },
.flags = WINDOW_COPY_CMD_FLAG_READONLY,
.clear = WINDOW_COPY_CMD_CLEAR_ALWAYS,
.f = window_copy_cmd_scroll_down_and_cancel
},
{ .command = "scroll-exit-on",
.args = { "", 0, 0, NULL },
.clear = WINDOW_COPY_CMD_CLEAR_ALWAYS,
.f = window_copy_cmd_scroll_exit_on
},
{ .command = "scroll-exit-off",
.args = { "", 0, 0, NULL },
.clear = WINDOW_COPY_CMD_CLEAR_ALWAYS,
.f = window_copy_cmd_scroll_exit_off
},
{ .command = "scroll-exit-toggle",
.args = { "", 0, 0, NULL },
.clear = WINDOW_COPY_CMD_CLEAR_ALWAYS,
.f = window_copy_cmd_scroll_exit_toggle
},
{ .command = "scroll-middle",
.args = { "", 0, 0, NULL },
.flags = WINDOW_COPY_CMD_FLAG_READONLY,
.clear = WINDOW_COPY_CMD_CLEAR_ALWAYS,
.f = window_copy_cmd_scroll_middle
},
{ .command = "scroll-to-mouse",
.args = { "e", 0, 0, NULL },
.flags = WINDOW_COPY_CMD_FLAG_READONLY,
.clear = WINDOW_COPY_CMD_CLEAR_EMACS_ONLY,
.f = window_copy_cmd_scroll_to_mouse
},
{ .command = "scroll-top",
.args = { "", 0, 0, NULL },
.flags = WINDOW_COPY_CMD_FLAG_READONLY,
.clear = WINDOW_COPY_CMD_CLEAR_ALWAYS,
.f = window_copy_cmd_scroll_top
},
{ .command = "scroll-up",
.args = { "", 0, 0, NULL },
.flags = WINDOW_COPY_CMD_FLAG_READONLY,
.clear = WINDOW_COPY_CMD_CLEAR_EMACS_ONLY,
.f = window_copy_cmd_scroll_up
},
{ .command = "search-again",
.args = { "", 0, 0, NULL },
.flags = 0,
.clear = WINDOW_COPY_CMD_CLEAR_ALWAYS,
.f = window_copy_cmd_search_again
},
{ .command = "search-backward",
.args = { "", 0, 1, NULL },
.flags = 0,
.clear = WINDOW_COPY_CMD_CLEAR_ALWAYS,
.f = window_copy_cmd_search_backward
},
{ .command = "search-backward-text",
.args = { "", 0, 1, NULL },
.flags = 0,
.clear = WINDOW_COPY_CMD_CLEAR_ALWAYS,
.f = window_copy_cmd_search_backward_text
},
{ .command = "search-backward-incremental",
.args = { "", 1, 1, NULL },
.flags = 0,
.clear = WINDOW_COPY_CMD_CLEAR_ALWAYS,
.f = window_copy_cmd_search_backward_incremental
},
{ .command = "search-forward",
.args = { "", 0, 1, NULL },
.flags = 0,
.clear = WINDOW_COPY_CMD_CLEAR_ALWAYS,
.f = window_copy_cmd_search_forward
},
{ .command = "search-forward-text",
.args = { "", 0, 1, NULL },
.flags = 0,
.clear = WINDOW_COPY_CMD_CLEAR_ALWAYS,
.f = window_copy_cmd_search_forward_text
},
{ .command = "search-forward-incremental",
.args = { "", 1, 1, NULL },
.flags = 0,
.clear = WINDOW_COPY_CMD_CLEAR_ALWAYS,
.f = window_copy_cmd_search_forward_incremental
},
{ .command = "search-reverse",
.args = { "", 0, 0, NULL },
.flags = 0,
.clear = WINDOW_COPY_CMD_CLEAR_ALWAYS,
.f = window_copy_cmd_search_reverse
},
{ .command = "select-line",
.args = { "", 0, 0, NULL },
.flags = 0,
.clear = WINDOW_COPY_CMD_CLEAR_ALWAYS,
.f = window_copy_cmd_select_line
},
{ .command = "select-word",
.args = { "", 0, 0, NULL },
.flags = 0,
.clear = WINDOW_COPY_CMD_CLEAR_ALWAYS,
.f = window_copy_cmd_select_word
},
{ .command = "selection-mode",
.args = { "", 0, 1, NULL },
.flags = 0,
.clear = 0,
.f = window_copy_cmd_selection_mode
},
{ .command = "set-mark",
.args = { "", 0, 0, NULL },
.flags = WINDOW_COPY_CMD_FLAG_READONLY,
.clear = WINDOW_COPY_CMD_CLEAR_ALWAYS,
.f = window_copy_cmd_set_mark
},
{ .command = "start-of-line",
.args = { "", 0, 0, NULL },
.flags = WINDOW_COPY_CMD_FLAG_READONLY,
.clear = WINDOW_COPY_CMD_CLEAR_EMACS_ONLY,
.f = window_copy_cmd_start_of_line
},
{ .command = "stop-selection",
.args = { "", 0, 0, NULL },
.flags = 0,
.clear = WINDOW_COPY_CMD_CLEAR_ALWAYS,
.f = window_copy_cmd_stop_selection
},
{ .command = "toggle-position",
.args = { "", 0, 0, NULL },
.flags = WINDOW_COPY_CMD_FLAG_READONLY,
.clear = WINDOW_COPY_CMD_CLEAR_NEVER,
.f = window_copy_cmd_toggle_position
},
{ .command = "top-line",
.args = { "", 0, 0, NULL },
.flags = WINDOW_COPY_CMD_FLAG_READONLY,
.clear = WINDOW_COPY_CMD_CLEAR_EMACS_ONLY,
.f = window_copy_cmd_top_line
}
@@ -3170,6 +3351,14 @@ window_copy_command(struct window_mode_entry *wme, struct client *c,
action = WINDOW_COPY_CMD_NOTHING;
for (i = 0; i < nitems(window_copy_cmd_table); i++) {
if (strcmp(window_copy_cmd_table[i].command, command) == 0) {
if (c->flags & CLIENT_READONLY &&
(~window_copy_cmd_table[i].flags &
WINDOW_COPY_CMD_FLAG_READONLY)) {
status_message_set(c, -1, 1, 0, 0,
"client is read-only");
return;
}
cs.wargs = args_parse(&window_copy_cmd_table[i].args,
args_values(args), count, &error);
@@ -3206,6 +3395,15 @@ window_copy_command(struct window_mode_entry *wme, struct client *c,
window_pane_reset_mode(wp);
else if (action == WINDOW_COPY_CMD_REDRAW)
window_copy_redraw_screen(wme);
else if (action == WINDOW_COPY_CMD_NOTHING) {
/*
* Nothing is not actually nothing - most commands at least
* move the cursor (what would be the point of a command that
* literally does nothing?) and in that case we need to redraw
* the first line to update the indicator.
*/
window_copy_redraw_lines(wme, 0, 1);
}
}
static void
@@ -3577,6 +3775,10 @@ window_copy_stringify(struct grid *gd, u_int py, u_int first, u_int last,
buf = xrealloc(buf, bufsize);
gl = grid_peek_line(gd, py);
if (gl == NULL) {
buf[*size - 1] = '\0';
return (buf);
}
bx = *size - 1;
for (ax = first; ax < last; ax++) {
d = window_copy_cellstring(gl, ax, &dlen, &allocated);
@@ -3622,6 +3824,10 @@ window_copy_cstrtocellpos(struct grid *gd, u_int ncells, u_int *ppx, u_int *ppy,
px = *ppx;
pywrap = *ppy;
gl = grid_peek_line(gd, pywrap);
if (gl == NULL) {
free(cells);
return;
}
while (cell < ncells) {
cells[cell].d = window_copy_cellstring(gl, px,
&cells[cell].dlen, &cells[cell].allocated);
@@ -3631,6 +3837,8 @@ window_copy_cstrtocellpos(struct grid *gd, u_int ncells, u_int *ppx, u_int *ppy,
px = 0;
pywrap++;
gl = grid_peek_line(gd, pywrap);
if (gl == NULL)
break;
}
}
@@ -4032,7 +4240,7 @@ window_copy_visible_lines(struct window_copy_mode_data *data, u_int *start,
for (*start = gd->hsize - data->oy; *start > 0; (*start)--) {
gl = grid_peek_line(gd, (*start) - 1);
if (~gl->flags & GRID_LINE_WRAPPED)
if (gl == NULL || ~gl->flags & GRID_LINE_WRAPPED)
break;
}
*end = gd->hsize - data->oy + gd->sy;
@@ -4223,6 +4431,9 @@ window_copy_clear_marks(struct window_mode_entry *wme)
{
struct window_copy_mode_data *data = wme->data;
data->searchcount = -1;
data->searchmore = 0;
free(data->searchmark);
data->searchmark = NULL;
}
@@ -4550,6 +4761,16 @@ window_copy_redraw_screen(struct window_mode_entry *wme)
window_copy_redraw_lines(wme, 0, screen_size_y(&data->screen));
}
static void
window_copy_style_changed(struct window_mode_entry *wme)
{
struct window_copy_mode_data *data = wme->data;
if (data->screen.sel != NULL)
window_copy_set_selection(wme, 0, 1);
window_copy_redraw_screen(wme);
}
static void
window_copy_synchronize_cursor_end(struct window_mode_entry *wme, int begin,
int no_reset)
@@ -4991,9 +5212,9 @@ static void
window_copy_append_selection(struct window_mode_entry *wme)
{
struct window_pane *wp = wme->wp;
char *buf;
char *buf, *bufname = NULL;
struct paste_buffer *pb;
const char *bufdata, *bufname = NULL;
const char *bufdata;
size_t len, bufsize;
struct screen_write_ctx ctx;
@@ -5018,6 +5239,7 @@ window_copy_append_selection(struct window_mode_entry *wme)
}
if (paste_set(buf, len, bufname, NULL) != 0)
free(buf);
free(bufname);
}
static void

View File

@@ -525,7 +525,7 @@ window_customize_build_keys(struct window_customize_modedata *data,
static void
window_customize_build(void *modedata,
__unused struct mode_tree_sort_criteria *sort_crit, __unused uint64_t *tag,
__unused struct sort_criteria *sort_crit, __unused uint64_t *tag,
const char *filter)
{
struct window_customize_modedata *data = modedata;
@@ -891,8 +891,8 @@ window_customize_init(struct window_mode_entry *wme, struct cmd_find_state *fs,
data->data = mode_tree_start(wp, args, window_customize_build,
window_customize_draw, NULL, window_customize_menu,
window_customize_height, NULL, NULL, data,
window_customize_menu_items, NULL, 0, &s);
window_customize_height, NULL, NULL, NULL, data,
window_customize_menu_items, &s);
mode_tree_zoom(data->data, args);
mode_tree_build(data->data);

View File

@@ -89,18 +89,6 @@ const struct window_mode window_tree_mode = {
.key = window_tree_key,
};
enum window_tree_sort_type {
WINDOW_TREE_BY_INDEX,
WINDOW_TREE_BY_NAME,
WINDOW_TREE_BY_TIME,
};
static const char *window_tree_sort_list[] = {
"index",
"name",
"time"
};
static struct mode_tree_sort_criteria *window_tree_sort;
enum window_tree_type {
WINDOW_TREE_NONE,
WINDOW_TREE_SESSION,
@@ -144,6 +132,13 @@ struct window_tree_modedata {
u_int each;
};
static enum sort_order window_tree_order_seq[] = {
SORT_INDEX,
SORT_NAME,
SORT_ACTIVITY,
SORT_END,
};
static void
window_tree_pull_item(struct window_tree_itemdata *item, struct session **sp,
struct winlink **wlp, struct window_pane **wp)
@@ -196,98 +191,6 @@ window_tree_free_item(struct window_tree_itemdata *item)
free(item);
}
static int
window_tree_cmp_session(const void *a0, const void *b0)
{
const struct session *const *a = a0;
const struct session *const *b = b0;
const struct session *sa = *a;
const struct session *sb = *b;
int result = 0;
switch (window_tree_sort->field) {
case WINDOW_TREE_BY_INDEX:
result = sa->id - sb->id;
break;
case WINDOW_TREE_BY_TIME:
if (timercmp(&sa->activity_time, &sb->activity_time, >)) {
result = -1;
break;
}
if (timercmp(&sa->activity_time, &sb->activity_time, <)) {
result = 1;
break;
}
/* FALLTHROUGH */
case WINDOW_TREE_BY_NAME:
result = strcmp(sa->name, sb->name);
break;
}
if (window_tree_sort->reversed)
result = -result;
return (result);
}
static int
window_tree_cmp_window(const void *a0, const void *b0)
{
const struct winlink *const *a = a0;
const struct winlink *const *b = b0;
const struct winlink *wla = *a;
const struct winlink *wlb = *b;
struct window *wa = wla->window;
struct window *wb = wlb->window;
int result = 0;
switch (window_tree_sort->field) {
case WINDOW_TREE_BY_INDEX:
result = wla->idx - wlb->idx;
break;
case WINDOW_TREE_BY_TIME:
if (timercmp(&wa->activity_time, &wb->activity_time, >)) {
result = -1;
break;
}
if (timercmp(&wa->activity_time, &wb->activity_time, <)) {
result = 1;
break;
}
/* FALLTHROUGH */
case WINDOW_TREE_BY_NAME:
result = strcmp(wa->name, wb->name);
break;
}
if (window_tree_sort->reversed)
result = -result;
return (result);
}
static int
window_tree_cmp_pane(const void *a0, const void *b0)
{
struct window_pane **a = (struct window_pane **)a0;
struct window_pane **b = (struct window_pane **)b0;
int result;
u_int ai, bi;
if (window_tree_sort->field == WINDOW_TREE_BY_TIME)
result = (*a)->active_point - (*b)->active_point;
else {
/*
* Panes don't have names, so use number order for any other
* sort field.
*/
window_pane_index(*a, &ai);
window_pane_index(*b, &bi);
result = ai - bi;
}
if (window_tree_sort->reversed)
result = -result;
return (result);
}
static void
window_tree_build_pane(struct session *s, struct winlink *wl,
struct window_pane *wp, void *modedata, struct mode_tree_item *parent)
@@ -339,7 +242,7 @@ window_tree_filter_pane(struct session *s, struct winlink *wl,
static int
window_tree_build_window(struct session *s, struct winlink *wl,
void *modedata, struct mode_tree_sort_criteria *sort_crit,
void *modedata, struct sort_criteria *sort_crit,
struct mode_tree_item *parent, const char *filter)
{
struct window_tree_modedata *data = modedata;
@@ -381,24 +284,13 @@ window_tree_build_window(struct session *s, struct winlink *wl,
goto empty;
}
l = NULL;
n = 0;
TAILQ_FOREACH(wp, &wl->window->panes, entry) {
if (!window_tree_filter_pane(s, wl, wp, filter))
continue;
l = xreallocarray(l, n + 1, sizeof *l);
l[n++] = wp;
}
l = sort_get_panes_window(wl->window, &n, sort_crit);
if (n == 0)
goto empty;
window_tree_sort = sort_crit;
qsort(l, n, sizeof *l, window_tree_cmp_pane);
for (i = 0; i < n; i++)
window_tree_build_pane(s, wl, l[i], modedata, mti);
free(l);
for (i = 0; i < n; i++) {
if (window_tree_filter_pane(s, wl, l[i], filter))
window_tree_build_pane(s, wl, l[i], modedata, mti);
}
return (1);
empty:
@@ -410,7 +302,7 @@ empty:
static void
window_tree_build_session(struct session *s, void *modedata,
struct mode_tree_sort_criteria *sort_crit, const char *filter)
struct sort_criteria *sort_crit, const char *filter)
{
struct window_tree_modedata *data = modedata;
struct window_tree_itemdata *item;
@@ -440,15 +332,7 @@ window_tree_build_session(struct session *s, void *modedata,
expanded);
free(text);
l = NULL;
n = 0;
RB_FOREACH(wl, winlinks, &s->windows) {
l = xreallocarray(l, n + 1, sizeof *l);
l[n++] = wl;
}
window_tree_sort = sort_crit;
qsort(l, n, sizeof *l, window_tree_cmp_window);
l = sort_get_winlinks_session(s, &n, sort_crit);
empty = 0;
for (i = 0; i < n; i++) {
if (!window_tree_build_window(s, l[i], modedata, sort_crit, mti,
@@ -460,11 +344,10 @@ window_tree_build_session(struct session *s, void *modedata,
data->item_size--;
mode_tree_remove(data->data, mti);
}
free(l);
}
static void
window_tree_build(void *modedata, struct mode_tree_sort_criteria *sort_crit,
window_tree_build(void *modedata, struct sort_criteria *sort_crit,
uint64_t *tag, const char *filter)
{
struct window_tree_modedata *data = modedata;
@@ -480,24 +363,19 @@ window_tree_build(void *modedata, struct mode_tree_sort_criteria *sort_crit,
data->item_list = NULL;
data->item_size = 0;
l = NULL;
n = 0;
RB_FOREACH(s, sessions, &sessions) {
l = sort_get_sessions(&n, sort_crit);
if (n == 0)
return;
s = l[n - 1];
for (i = 0; i < n; i++) {
if (data->squash_groups &&
(sg = session_group_contains(s)) != NULL) {
if ((sg == current && s != data->fs.s) ||
(sg != current && s != TAILQ_FIRST(&sg->sessions)))
continue;
}
l = xreallocarray(l, n + 1, sizeof *l);
l[n++] = s;
}
window_tree_sort = sort_crit;
qsort(l, n, sizeof *l, window_tree_cmp_session);
for (i = 0; i < n; i++)
window_tree_build_session(l[i], modedata, sort_crit, filter);
free(l);
}
switch (data->type) {
case WINDOW_TREE_NONE:
@@ -924,7 +802,8 @@ window_tree_get_key(void *modedata, void *itemdata, u_int line)
}
static int
window_tree_swap(void *cur_itemdata, void *other_itemdata)
window_tree_swap(void *cur_itemdata, void *other_itemdata,
struct sort_criteria *sort_crit)
{
struct window_tree_itemdata *cur = cur_itemdata;
struct window_tree_itemdata *other = other_itemdata;
@@ -945,14 +824,12 @@ window_tree_swap(void *cur_itemdata, void *other_itemdata)
if (cur_session != other_session)
return (0);
if (window_tree_sort->field != WINDOW_TREE_BY_INDEX &&
window_tree_cmp_window(&cur_winlink, &other_winlink) != 0) {
/*
* Swapping indexes would not swap positions in the tree, so
* prevent swapping to avoid confusing the user.
*/
/*
* Swapping indexes would not swap positions in the tree, so prevent
* swapping to avoid confusing the user.
*/
if (sort_would_window_tree_swap(sort_crit, cur_winlink, other_winlink))
return (0);
}
other_window = other_winlink->window;
TAILQ_REMOVE(&other_window->winlinks, other_winlink, wentry);
@@ -975,6 +852,14 @@ window_tree_swap(void *cur_itemdata, void *other_itemdata)
return (1);
}
static void
window_tree_sort(struct sort_criteria *sort_crit)
{
sort_crit->order_seq = window_tree_order_seq;
if (sort_crit->order == SORT_END)
sort_crit->order = sort_crit->order_seq[0];
}
static struct screen *
window_tree_init(struct window_mode_entry *wme, struct cmd_find_state *fs,
struct args *args)
@@ -1013,8 +898,8 @@ window_tree_init(struct window_mode_entry *wme, struct cmd_find_state *fs,
data->data = mode_tree_start(wp, args, window_tree_build,
window_tree_draw, window_tree_search, window_tree_menu, NULL,
window_tree_get_key, window_tree_swap, data, window_tree_menu_items,
window_tree_sort_list, nitems(window_tree_sort_list), &s);
window_tree_get_key, window_tree_swap, window_tree_sort, data,
window_tree_menu_items, &s);
mode_tree_zoom(data->data, args);
mode_tree_build(data->data);

View File

@@ -328,6 +328,9 @@ window_create(u_int sx, u_int sy, u_int xpixel, u_int ypixel)
RB_INSERT(windows, &windows, w);
window_set_fill_character(w);
if (gettimeofday(&w->creation_time, NULL) != 0)
fatal("gettimeofday failed");
window_update_activity(w);
log_debug("%s: @%u create %ux%u (%ux%u)", __func__, w->id, sx, sy,
@@ -933,7 +936,7 @@ window_pane_create(struct window *w, u_int sx, u_int sy, u_int hlimit)
wp = xcalloc(1, sizeof *wp);
wp->window = w;
wp->options = options_create(w->options);
wp->flags = (PANE_STYLECHANGED|PANE_THEMECHANGED);
wp->flags = PANE_STYLECHANGED;
wp->id = next_window_pane_id++;
RB_INSERT(window_pane_tree, &all_window_panes, wp);
@@ -1001,6 +1004,8 @@ window_pane_destroy(struct window_pane *wp)
if (event_initialized(&wp->resize_timer))
event_del(&wp->resize_timer);
if (event_initialized(&wp->sync_timer))
event_del(&wp->sync_timer);
TAILQ_FOREACH_SAFE(r, &wp->resize_queue, entry, r1) {
TAILQ_REMOVE(&wp->resize_queue, r, entry);
free(r);
@@ -1066,7 +1071,7 @@ window_pane_set_event(struct window_pane *wp)
NULL, window_pane_error_callback, wp);
if (wp->event == NULL)
fatalx("out of memory");
wp->ictx = input_init(wp, wp->event, &wp->palette);
wp->ictx = input_init(wp, wp->event, &wp->palette, NULL);
bufferevent_enable(wp->event, EV_READ|EV_WRITE);
}
@@ -1080,6 +1085,8 @@ window_pane_resize(struct window_pane *wp, u_int sx, u_int sy)
if (sx == wp->sx && sy == wp->sy)
return;
screen_write_stop_sync(wp);
r = xmalloc(sizeof *r);
r->sx = sx;
r->sy = sy;
@@ -1780,7 +1787,7 @@ window_pane_mode(struct window_pane *wp)
int
window_pane_show_scrollbar(struct window_pane *wp, int sb_option)
{
if (SCREEN_IS_ALTERNATE(wp->screen))
if (SCREEN_IS_ALTERNATE(&wp->base))
return (0);
if (sb_option == PANE_SCROLLBARS_ALWAYS ||
(sb_option == PANE_SCROLLBARS_MODAL &&
@@ -1933,6 +1940,8 @@ window_pane_get_theme(struct window_pane *wp)
void
window_pane_send_theme_update(struct window_pane *wp)
{
enum client_theme theme;
if (wp == NULL || window_pane_exited(wp))
return;
if (~wp->flags & PANE_THEMECHANGED)
@@ -1940,16 +1949,23 @@ window_pane_send_theme_update(struct window_pane *wp)
if (~wp->screen->mode & MODE_THEME_UPDATES)
return;
switch (window_pane_get_theme(wp)) {
theme = window_pane_get_theme(wp);
if (theme == wp->last_theme)
return;
wp->last_theme = theme;
wp->flags &= ~PANE_THEMECHANGED;
switch (theme) {
case THEME_LIGHT:
input_key_pane(wp, KEYC_REPORT_LIGHT_THEME, NULL);
log_debug("%s: %%%u light theme", __func__, wp->id);
bufferevent_write(wp->event, "\033[?997;2n", 9);
break;
case THEME_DARK:
input_key_pane(wp, KEYC_REPORT_DARK_THEME, NULL);
log_debug("%s: %%%u dark theme", __func__, wp->id);
bufferevent_write(wp->event, "\033[?997;1n", 9);
break;
case THEME_UNKNOWN:
log_debug("%s: %%%u unknown theme", __func__, wp->id);
break;
}
wp->flags &= ~PANE_THEMECHANGED;
}