233 Commits
3.6 ... master

Author SHA1 Message Date
Thomas Adam
6ca5405db9 Merge branch 'obsd-master' 2026-03-25 12:01:07 +00:00
Nicholas Marriott
fa31f2a3eb Do not really need both. 2026-03-25 10:39:03 +00:00
Nicholas Marriott
d03f9454b9 Make COPYING the same. 2026-03-25 10:38:03 +00:00
Nicholas Marriott
616fb1cbcf Add a LICENSE file to see if it makes GitHub happy. 2026-03-25 10:26:15 +00:00
nicm
b7d1e6257c Use \- for hyphens in tmux.1 to cause newer groff versions to render
them correctly (ASCII hyphen rather than Unicode) which aids copy and
paste. From Keith Thompson in GitHub issue 4948.
2026-03-25 10:08:50 +00:00
Thomas Adam
b101ffaed6 Merge branch 'obsd-master' 2026-03-23 12:01:10 +00:00
nicm
f7dad4f38f Check lastgc is not NULL before using it, GitHub issue 4935 from Pavel
Lavrukhin.
2026-03-23 09:05:59 +00:00
nicm
b88c08f860 Fix a NULL dereference and use after free, GitHub issue 4936 from Pavel
Lavrukhin.
2026-03-23 09:03:43 +00:00
Nicholas Marriott
cc47f4d1c2 Use window options for cursor-style to avoid crash when no pane, from
Arden Packeer in GitHub issue 4942.
2026-03-23 09:00:42 +00:00
nicm
3909d7e92f Use window options for cursor-style to avoid crash when no pane, from
Arden Packeer in GitHub issue 4942.
2026-03-23 08:58:39 +00:00
nicm
d70edfa0a0 Fix issue where popup window gets overwritten by background updates,
from Conor Taylor in GitHub issue 4920.
2026-03-23 08:48:32 +00:00
nicm
d22ab85b84 Protect against overflow when scrollbar is off screen, from san65384 at
gmail dot com in GitHub issue 4933.
2026-03-23 08:45:30 +00:00
Thomas Adam
5c30b145df Merge branch 'obsd-master' 2026-03-18 12:01:09 +00:00
nicm
e446cba6a9 Copy hyperlinks when redrawing popup so they do not vanish, from Antoine
Gaudreau Simard in GitHub issue 4925.
2026-03-18 08:49:27 +00:00
Nicholas Marriott
8a0b4e0d8c Work around systemd killing panes early during system shutdown by creating
dependencies from the panes to the service which started tmux, GitHub issue
4926 from Dmitry Torokhov.
2026-03-18 08:46:16 +00:00
nicm
02a9cdff57 Fix buffer to both for get-clipboard, from Seth Girvan. 2026-03-18 08:41:20 +00:00
nicm
f516f8a1d4 Allow codepoint-widths to accept ranges, from san65384 at gmail dot com
in GitHub issue 4930.
2026-03-18 08:38:54 +00:00
Thomas Adam
bef4865d91 Merge branch 'obsd-master' 2026-03-12 16:01:08 +00:00
nicm
fd62f419ac Add a short builtin help text for each mode accessible with C-h,
based on code from Patrick Motard in GitHub issue 4751.
2026-03-12 12:40:40 +00:00
Thomas Adam
7fa6f320ed Merge branch 'obsd-master' 2026-03-12 12:01:09 +00:00
nicm
19f3fb131b Draw message as one format, allowing prompts and messages to occupy only
a portion of the status bar, overlaying the normal status content rather
than replacing the entire line. A new message-format option now controls
the entire message (like status-format). From Conor Taylor in GitHub
issue 4861.
2026-03-12 07:25:13 +00:00
nicm
551e8fcd24 Fix comparison of menu keys, reported by Vladimir Bauer in GitHub issue
4891.
2026-03-12 07:15:26 +00:00
Thomas Adam
04b4952f0e Merge branch 'obsd-master' 2026-03-09 20:01:07 +00:00
Thomas Adam
0d77555c97 Merge branch 'obsd-master' 2026-03-09 16:01:09 +00:00
nicm
67141fb4bb Add next/previous variables for windows in W: loop, from Conor Taylor in
GitHub issue 4856.
2026-03-09 15:46:01 +00:00
tb
2efa762c2b tmux: move block assigning to prefix a bit down to avoid a small leak
ok nicm
2026-03-09 14:37:26 +00:00
Nicholas Marriott
6c2dd193cf Fix memory leak in sixel_parse() on error path, from Renaud Allard in GitHub
issue 4916.
2026-03-09 14:34:58 +00:00
nicm
881bec958e Fix server crash when control client session is destroyed, from Renaud
Allard in GitHub issue 4916.
2026-03-09 14:33:55 +00:00
nicm
8899c751e5 Check for NULL value in tilde expansion before dereferencing, from
Renaud Allard in GitHub issue 4916.
2026-03-09 14:31:41 +00:00
Thomas Adam
d0caf0a322 Merge branch 'obsd-master' 2026-03-06 12:01:08 +00:00
tb
91b5108eae tmux: don't leak menu if the tty is too small to display it
ok nicm
2026-03-06 08:19:22 +00:00
nicm
2c7f73f9c4 Do not use recallocarray because the stored size may have changed during
reflow so may not match what it expects, fixes crash reported by Caspar
Schutijser.
2026-03-06 08:09:58 +00:00
Thomas Adam
5310592967 Merge branch 'obsd-master' 2026-03-06 04:01:09 +00:00
nicm
41bddae907 Fix incorrect condition for creating client name, from Vlad Apostol in
GitHub issue 4911.
2026-03-05 22:50:14 +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
87 changed files with 5998 additions and 3414 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

@@ -1,9 +1,4 @@
THIS IS FOR INFORMATION ONLY, CODE IS UNDER THE LICENCE AT THE TOP OF ITS FILE.
The README, CHANGES, FAQ and TODO files are licensed under the ISC license. All
other files have a license and copyright notice at their start, typically:
Copyright (c) <author>
Copyright (c) Various Authors
Permission to use, copy, modify, and distribute this software for any
purpose with or without fee is hereby granted, provided that the above

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 \

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;
}
}
@@ -377,8 +370,17 @@ cmd_display_menu_exec(struct cmd *self, struct cmdq_item *item)
flags |= MENU_NOMOUSE;
if (menu_display(menu, flags, starting_choice, item, px, py, tc, lines,
style, selected_style, border_style, target, NULL, NULL) != 0)
return (CMD_RETURN_NORMAL);
goto out;
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));
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);
}
}
prefix = cmd_list_keys_get_prefix(args);
single = args_has(args, '1');
notes_only = args_has(args, 'N');
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;
@@ -1607,7 +1604,9 @@ yylex_token_tilde(char **buf, size_t *len)
if (*name == '\0') {
envent = environ_find(global_environ, "HOME");
if (envent != NULL && *envent->value != '\0')
if (envent != NULL &&
envent->value != NULL &&
*envent->value != '\0')
home = envent->value;
else if ((pw = getpwuid(getuid())) != NULL)
home = pw->pw_dir;

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

@@ -81,7 +81,8 @@ struct systemd_job_watch {
};
static int
job_removed_handler(sd_bus_message *m, void *userdata, sd_bus_error *ret_error)
job_removed_handler(sd_bus_message *m, void *userdata,
__unused sd_bus_error *ret_error)
{
struct systemd_job_watch *watch = userdata;
const char *path = NULL;
@@ -109,7 +110,7 @@ systemd_move_to_new_cgroup(char **cause)
sd_bus_message *m = NULL, *reply = NULL;
sd_bus *bus = NULL;
sd_bus_slot *slot = NULL;
char *name, *desc, *slice;
char *name, *desc, *slice, *unit;
sd_id128_t uuid;
int r;
uint64_t elapsed_usec;
@@ -241,6 +242,26 @@ systemd_move_to_new_cgroup(char **cause)
goto finish;
}
/*
* Try locating systemd unit that started the server, and mark pane units
* as dependent on it. Use "Before" to make sure systemd will not try to
* kill them first.
*/
if (sd_pid_get_user_unit(parent_pid, &unit) == 0 ||
sd_pid_get_unit(parent_pid, &unit) == 0) {
r = sd_bus_message_append(m, "(sv)", "Before", "as", 1, unit);
if (r >= 0) {
r = sd_bus_message_append(m, "(sv)", "PartOf", "as", 1,
unit);
}
free(unit);
if (r < 0) {
xasprintf(cause, "failed to append to properties: %s",
strerror(-r));
goto finish;
}
}
/* End properties array. */
r = sd_bus_message_close_container(m);
if (r < 0) {

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,

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

207
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,94 @@ 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) {
if (s == NULL)
return;
/* 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++;

352
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)
@@ -3311,6 +3340,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
},
@@ -3443,6 +3475,9 @@ static const struct format_table_entry format_table[] = {
{ "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 +3983,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 +4412,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 +4424,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 +4461,9 @@ format_loop_sessions(struct format_expand_state *es, const char *fmt)
free(expanded);
}
free(active);
free(all);
return (value);
}
@@ -4496,44 +4491,42 @@ 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)
/* Add neighbor window variables to the format tree. */
static void
format_add_window_neighbor(struct format_tree *nft, struct winlink *wl,
struct session *s, const char *prefix)
{
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;
struct options_entry *o;
const char *oname;
char *key, *prefixed, *oval;
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;
xasprintf(&key, "%s_window_index", prefix);
format_add(nft, key, "%u", wl->idx);
free(key);
xasprintf(&key, "%s_window_active", prefix);
format_add(nft, key, "%d", wl == s->curw);
free(key);
o = options_first(wl->window->options);
while (o != NULL) {
oname = options_name(o);
if (*oname == '@') {
xasprintf(&prefixed, "%s_%s", prefix, oname);
oval = options_to_string(o, -1, 1);
format_add(nft, prefixed, "%s", oval);
free(oval);
free(prefixed);
}
if (timercmp(&wa->activity_time, &wb->activity_time, <)) {
result = 1;
break;
}
/* FALLTHROUGH */
case FORMAT_LOOP_BY_NAME:
result = strcmp(wa->name, wb->name);
break;
o = options_next(o);
}
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 +4534,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 +4548,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;
@@ -4595,6 +4565,17 @@ format_loop_windows(struct format_expand_state *es, const char *fmt)
nft = format_create(c, item, FORMAT_WINDOW|w->id,
ft->flags|last);
format_defaults(nft, ft->c, ft->s, wl, NULL);
/* Add neighbor window data to the format tree. */
format_add(nft, "window_after_active", "%d",
i > 0 && l[i - 1] == ft->s->curw);
format_add(nft, "window_before_active", "%d",
i + 1 < n && l[i + 1] == ft->s->curw);
if (i + 1 < n)
format_add_window_neighbor(nft, l[i + 1], ft->s, "next");
if (i > 0)
format_add_window_neighbor(nft, l[i - 1], ft->s, "prev");
format_copy_state(&next, es, 0);
next.ft = nft;
expanded = format_expand1(&next, use);
@@ -4613,27 +4594,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 +4606,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 +4619,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 +4653,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 +4840,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 +4857,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 +4971,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 +4991,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 +5010,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 +5023,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 +5425,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 +5448,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 {

40
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)
@@ -493,7 +508,12 @@ 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);
gl->celldata = xreallocarray(gl->celldata, sx,
sizeof *gl->celldata);
if (gl->cellsize < sx) {
memset(gl->celldata + gl->cellsize, 0,
(sx - gl->cellsize) * sizeof *gl->celldata);
}
for (xx = gl->cellsize; xx < sx; xx++)
grid_clear_cell(gd, xx, py, bg);
gl->cellsize = sx;
@@ -1082,18 +1102,22 @@ 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)
continue;
if (flags & GRID_STRING_WITH_SEQUENCES) {
if (lastgc != NULL && (flags & GRID_STRING_WITH_SEQUENCES)) {
grid_string_cells_code(*lastgc, &gc, code, sizeof code,
flags, s, &has_link);
codelen = strlen(code);

View File

@@ -357,7 +357,7 @@ sixel_parse(const char *buf, size_t len, u_int p2, u_int xpixel, u_int ypixel)
return (si);
bad:
free(si);
sixel_free(si);
return (NULL);
}
@@ -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;

57
image.c
View File

@@ -25,12 +25,38 @@
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--;
@@ -46,6 +72,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);
@@ -108,10 +136,11 @@ image_store(struct screen *s, struct sixel_image *si)
image_fallback(&im->fallback, 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 +150,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 +167,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);
}
@@ -159,17 +193,20 @@ image_scroll_up(struct screen *s, u_int lines)
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;
image_log(im, __func__, "3, lines=%u, sy=%u", lines, sy);
new = sixel_scale(im->data, 0, 0, 0, im->sy - sy, sx, sy, 1);
sixel_free(im->data);

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,

293
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;
@@ -854,7 +855,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 +863,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 +900,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);
}
@@ -945,7 +949,7 @@ input_set_state(struct input_ctx *ictx, const struct input_transition *itr)
/* Parse data. */
static void
input_parse(struct input_ctx *ictx, u_char *buf, size_t len)
input_parse(struct input_ctx *ictx, const u_char *buf, size_t len)
{
struct screen_write_ctx *sctx = &ictx->ctx;
const struct input_state *state = NULL;
@@ -1016,7 +1020,7 @@ input_parse_pane(struct window_pane *wp)
/* Parse given input. */
void
input_parse_buffer(struct window_pane *wp, u_char *buf, size_t len)
input_parse_buffer(struct window_pane *wp, const u_char *buf, size_t len)
{
struct input_ctx *ictx = wp->ictx;
struct screen_write_ctx *sctx = &ictx->ctx;
@@ -1047,7 +1051,7 @@ input_parse_buffer(struct window_pane *wp, u_char *buf, size_t len)
/* Parse given input for screen. */
void
input_parse_screen(struct input_ctx *ictx, struct screen *s,
screen_write_init_ctx_cb cb, void *arg, u_char *buf, size_t len)
screen_write_init_ctx_cb cb, void *arg, const u_char *buf, size_t len)
{
struct screen_write_ctx *sctx = &ictx->ctx;
@@ -1135,11 +1139,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));
}
}
@@ -1615,7 +1617,7 @@ input_csi_dispatch(struct input_ctx *ictx)
if (ictx->wp != NULL)
oo = ictx->wp->options;
else
oo = global_options;
oo = global_w_options;
p = options_get_number(oo, "cursor-style");
/* blink for 1,3,5; steady for 0,2,4,6 */
@@ -1623,10 +1625,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 +1633,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 +1906,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 +2011,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);
@@ -2502,7 +2522,7 @@ input_handle_decrqss(struct input_ctx *ictx)
if (wp != NULL)
oo = wp->options;
else
oo = global_options;
oo = global_w_options;
opt_ps = options_get_number(oo, "cursor-style");
/* Sanity clamp: valid Ps are 0..6 per DECSCUSR. */
@@ -2542,8 +2562,9 @@ input_dcs_dispatch(struct input_ctx *ictx)
#endif
if (wp == NULL)
return (0);
oo = wp->options;
oo = global_w_options;
else
oo = wp->options;
if (ictx->flags & INPUT_DISCARD) {
log_debug("%s: %zu bytes (discard)", __func__, len);
@@ -2551,8 +2572,8 @@ input_dcs_dispatch(struct input_ctx *ictx)
}
#ifdef ENABLE_SIXEL
w = wp->window;
if (buf[0] == 'q' && ictx->interm_len == 0) {
if (wp != NULL && buf[0] == 'q' && ictx->interm_len == 0) {
w = wp->window;
if (input_split(ictx) != 0)
return (0);
p2 = input_get(ictx, 1, 0, 0);
@@ -3069,67 +3090,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 +3233,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 +3252,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 +3276,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 +3293,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 +3308,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 +3373,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 +3383,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 +3426,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 +3451,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 +3468,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 +3476,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;

117
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);
}
@@ -292,7 +363,7 @@ menu_key_cb(struct client *c, void *data, struct key_event *event)
name = menu->items[i].name;
if (name == NULL || *name == '-')
continue;
if (event->key == menu->items[i].key) {
if ((event->key & ~KEYC_MASK_FLAGS) == menu->items[i].key) {
md->choice = i;
goto chosen;
}
@@ -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,8 @@ struct mode_tree_data {
mode_tree_height_cb heightcb;
mode_tree_key_cb keycb;
mode_tree_swap_cb swapcb;
mode_tree_sort_cb sortcb;
mode_tree_help_cb helpcb;
struct mode_tree_list children;
struct mode_tree_list saved;
@@ -133,6 +133,36 @@ static const struct menu_item mode_tree_menu_items[] = {
{ NULL, KEYC_NONE, NULL }
};
static const char* mode_tree_help_start[] = {
"\r\033[1m Up, k \033[0m\016x\017 \033[0mMove cursor up\n",
"\r\033[1m Down, j \033[0m\016x\017 \033[0mMove cursor down\n",
"\r\033[1m g \033[0m\016x\017 \033[0mGo to top\n",
"\r\033[1m G \033[0m\016x\017 \033[0mGo to bottom\n",
"\r\033[1m PPage, C-b \033[0m\016x\017 \033[0mPage up\n",
"\r\033[1m NPage, C-f \033[0m\016x\017 \033[0mPage down\n",
"\r\033[1m Left, h \033[0m\016x\017 \033[0mCollapse %1\n",
"\r\033[1m Right, l \033[0m\016x\017 \033[0mExpand %1\n",
"\r\033[1m M-- \033[0m\016x\017 \033[0mCollapse all %1s\n",
"\r\033[1m M-+ \033[0m\016x\017 \033[0mExpand all %1s\n",
"\r\033[1m t \033[0m\016x\017 \033[0mToggle %1 tag\n",
"\r\033[1m T \033[0m\016x\017 \033[0mUntag all %1s\n",
"\r\033[1m C-t \033[0m\016x\017 \033[0mTag all %1s\n",
"\r\033[1m C-s \033[0m\016x\017 \033[0mSearch forward\n",
"\r\033[1m C-r \033[0m\016x\017 \033[0mSearch backward\n",
"\r\033[1m n \033[0m\016x\017 \033[0mRepeat search forward\n",
"\r\033[1m N \033[0m\016x\017 \033[0mRepeat search backward\n",
"\r\033[1m f \033[0m\016x\017 \033[0mFilter %1s\n",
"\r\033[1m O \033[0m\016x\017 \033[0mChange sort order\n",
"\r\033[1m r \033[0m\016x\017 \033[0mReverse sort order\n",
"\r\033[1m v \033[0m\016x\017 \033[0mToggle preview\n",
NULL
};
static const char* mode_tree_help_end[] = {
"\r\033[1m q, Escape \033[0m\016x\017 \033[0mExit mode\033[H",
NULL
};
#define MODE_TREE_HELP_DEFAULT_WIDTH 39
static int
mode_tree_is_lowercase(const char *ptr)
{
@@ -324,7 +354,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 +412,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 +484,11 @@ 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,
mode_tree_help_cb helpcb, 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 +497,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 +504,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 +519,8 @@ mode_tree_start(struct window_pane *wp, struct args *args,
mtd->heightcb = heightcb;
mtd->keycb = keycb;
mtd->swapcb = swapcb;
mtd->sortcb = sortcb;
mtd->helpcb = helpcb;
TAILQ_INIT(&mtd->children);
@@ -566,6 +588,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 +875,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);
@@ -1132,6 +1156,57 @@ mode_tree_display_menu(struct mode_tree_data *mtd, struct client *c, u_int x,
}
}
static void
mode_tree_display_help(__unused struct mode_tree_data *mtd, struct client *c)
{
struct session *s = c->session;
u_int px, py, w, h = 0;
const char **line, **lines = NULL, *item = "item";
char *new_line;
if (mtd->helpcb == NULL)
w = MODE_TREE_HELP_DEFAULT_WIDTH;
else {
lines = mtd->helpcb(&w, &item);
if (w < MODE_TREE_HELP_DEFAULT_WIDTH)
w = MODE_TREE_HELP_DEFAULT_WIDTH;
}
for (line = mode_tree_help_start; *line != NULL; line++)
h++;
for (line = lines; line != NULL && *line != NULL; line++)
h++;
for (line = mode_tree_help_end; *line != NULL; line++)
h++;
if (c->tty.sx < w || c->tty.sy < h)
return;
px = (c->tty.sx - w) / 2;
py = (c->tty.sy - h) / 2;
if (popup_display(POPUP_CLOSEANYKEY|POPUP_NOJOB, BOX_LINES_DEFAULT,
NULL, px, py, w, h, NULL, NULL, 0, NULL, NULL, NULL, c, s, NULL,
NULL, NULL, NULL) != 0)
return;
popup_write(c, "\033[H\033[?25l\033[?7l\033)0", 17);
for (line = mode_tree_help_start; *line != NULL; line++) {
new_line = cmd_template_replace(*line, item, 1);
popup_write(c, new_line, strlen(new_line));
free(new_line);
}
for (line = lines; line != NULL && *line != NULL; line++) {
new_line = cmd_template_replace(*line, item, 1);
popup_write(c, new_line, strlen(new_line));
free(new_line);
}
for (line = mode_tree_help_end; *line != NULL; line++) {
new_line = cmd_template_replace(*line, item, 1);
popup_write(c, new_line, strlen(new_line));
free(new_line);
}
popup_write(c, "\033[H", 3);
}
int
mode_tree_key(struct mode_tree_data *mtd, struct client *c, key_code *key,
struct mouse_event *m, u_int *xp, u_int *yp)
@@ -1139,7 +1214,7 @@ mode_tree_key(struct mode_tree_data *mtd, struct client *c, key_code *key,
struct mode_tree_line *line;
struct mode_tree_item *current, *parent, *mti;
u_int i, x, y;
int choice;
int choice, preview;
if (KEYC_IS_MOUSE(*key) && m != NULL) {
if (cmd_mouse_at(mtd->wp, m, &x, &y, 0) != 0) {
@@ -1151,9 +1226,10 @@ mode_tree_key(struct mode_tree_data *mtd, struct client *c, key_code *key,
if (yp != NULL)
*yp = y;
if (x > mtd->width || y > mtd->height) {
preview = mtd->preview;
if (*key == KEYC_MOUSEDOWN3_PANE)
mode_tree_display_menu(mtd, c, x, y, 1);
if (mtd->preview == MODE_TREE_PREVIEW_OFF)
if (preview == MODE_TREE_PREVIEW_OFF)
*key = KEYC_NONE;
return (0);
}
@@ -1202,6 +1278,10 @@ mode_tree_key(struct mode_tree_data *mtd, struct client *c, key_code *key,
case '\033': /* Escape */
case 'g'|KEYC_CTRL:
return (1);
case KEYC_F1:
case 'h'|KEYC_CTRL:
mode_tree_display_help(mtd, c);
break;
case KEYC_UP:
case 'k':
case KEYC_WHEELUP_PANE:
@@ -1287,9 +1367,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
};
@@ -138,7 +141,7 @@ static const char *options_table_allow_passthrough_list[] = {
"#{T:window-status-format}" \
"#[pop-default]" \
"#[norange default]" \
"#{?loop_last_flag,,#{window-status-separator}}" \
"#{?loop_last_flag,,#{E:window-status-separator}}" \
"," \
"#[range=window|#{window_index} list=focus " \
"#{?#{!=:#{E:window-status-current-style},default}," \
@@ -165,7 +168,7 @@ static const char *options_table_allow_passthrough_list[] = {
"#{T:window-status-current-format}" \
"#[pop-default]" \
"#[norange list=on default]" \
"#{?loop_last_flag,,#{window-status-separator}}" \
"#{?loop_last_flag,,#{E:window-status-separator}}" \
"}" \
"#[nolist align=right range=right #{E:status-right-style}]" \
"#[push-default]" \
@@ -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,
@@ -701,13 +723,24 @@ const struct options_table_entry options_table[] = {
{ .name = "message-command-style",
.type = OPTIONS_TABLE_STRING,
.scope = OPTIONS_TABLE_SESSION,
.default_str = "bg=black,fg=yellow",
.default_str = "bg=black,fg=yellow,fill=black",
.flags = OPTIONS_TABLE_IS_STYLE,
.separator = ",",
.text = "Style of the command prompt when in command mode, if "
"'mode-keys' is set to 'vi'."
},
{ .name = "message-format",
.type = OPTIONS_TABLE_STRING,
.scope = OPTIONS_TABLE_SESSION,
.default_str = "#[#{?#{command_prompt},"
"#{E:message-command-style},"
"#{E:message-style}}]"
"#{message}",
.text = "Format string for the prompt and message area. "
"The '#{message}' placeholder is replaced with the content."
},
{ .name = "message-line",
.type = OPTIONS_TABLE_CHOICE,
.scope = OPTIONS_TABLE_SESSION,
@@ -719,10 +752,13 @@ const struct options_table_entry options_table[] = {
{ .name = "message-style",
.type = OPTIONS_TABLE_STRING,
.scope = OPTIONS_TABLE_SESSION,
.default_str = "bg=yellow,fg=black",
.default_str = "bg=yellow,fg=black,fill=yellow",
.flags = OPTIONS_TABLE_IS_STYLE,
.separator = ",",
.text = "Style of messages and the command prompt."
.text = "Style of messages and the command prompt. "
"A 'fill' attribute controls background clearing and "
"a 'width' attribute (fixed or percentage) constrains "
"the prompt area width."
},
{ .name = "mouse",
@@ -943,7 +979,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 +991,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);
}

161
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,7 +276,13 @@ 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);
if (pd->s.hyperlinks != NULL) {
hyperlinks_free(s.hyperlinks);
s.hyperlinks = hyperlinks_copy(pd->s.hyperlinks);
}
screen_write_start(&ctx, &s);
screen_write_clearscreen(&ctx, 8);
@@ -286,10 +349,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 +617,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 +726,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 +735,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 +747,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 +798,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++;
@@ -773,16 +851,35 @@ popup_display(int flags, enum box_lines lines, struct cmdq_item *item, u_int px,
pd->psx = sx;
pd->psy = sy;
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);
if (flags & POPUP_NOJOB)
pd->ictx = input_init(NULL, NULL, &pd->palette, NULL);
else {
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, 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);
return (0);
}
void
popup_write(struct client *c, const char *data, size_t size)
{
struct popup_data *pd = c->overlay_data;
if (!popup_present(c))
return;
c->overlay_check = NULL;
c->overlay_data = NULL;
input_parse_screen(pd->ictx, &pd->s, popup_init_ctx_cb, pd, data, size);
c->overlay_check = popup_check_cb;
c->overlay_data = pd;
}
static void
popup_editor_free(struct popup_editor *pe)
{

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
@@ -731,6 +732,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 +817,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 +868,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 +944,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,7 +991,15 @@ 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
@@ -1028,12 +1090,18 @@ screen_redraw_draw_scrollbar(struct screen_redraw_ctx *ctx,
int sx = ctx->sx, sy = ctx->sy, xoff = wp->xoff;
int yoff = wp->yoff;
/* Set up style for slider. */
if (ctx->statustop) {
sb_y += ctx->statuslines;
sy += ctx->statuslines;
}
gc = sb_style->gc;
memcpy(&slgc, &gc, sizeof slgc);
slgc.fg = gc.bg;
slgc.bg = gc.fg;
if (sb_x >= sx || sb_y >= sy)
return;
imax = sb_w + sb_pad;
if ((int)imax + sb_x > sx)
imax = sx - sb_x;

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)
@@ -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);
@@ -1306,16 +1354,11 @@ screen_write_clearendofline(struct screen_write_ctx *ctx, u_int bg)
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,7 +1367,7 @@ 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);
@@ -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. */
@@ -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,15 +1932,29 @@ 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
@@ -1889,6 +1964,8 @@ screen_write_collect_end(struct screen_write_ctx *ctx)
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);
@@ -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;
@@ -2377,8 +2472,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 +2496,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

@@ -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. */
@@ -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:
@@ -3624,7 +3708,7 @@ server_client_dispatch_identify(struct client *c, struct imsg *imsg)
c->term_name = xstrdup("unknown");
}
if (c->ttyname == NULL || *c->ttyname != '\0')
if (c->ttyname != NULL && *c->ttyname != '\0')
name = xstrdup(c->ttyname);
else
xasprintf(&name, "client-%ld", (long)c->pid);

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

186
status.c
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. */
@@ -546,6 +551,71 @@ status_message_callback(__unused int fd, __unused short event, void *data)
status_message_clear(c);
}
/*
* Calculate prompt/message area geometry from the style's width and align
* directives: x offset and available width within the status line.
*/
static void
status_prompt_area(struct client *c, u_int *area_x, u_int *area_w)
{
struct session *s = c->session;
struct style *sy;
u_int w;
/* Get width from message-style's width directive. */
sy = options_string_to_style(s->options, "message-style", NULL);
if (sy != NULL && sy->width >= 0) {
if (sy->width_percentage)
w = (c->tty.sx * (u_int)sy->width) / 100;
else
w = (u_int)sy->width;
} else
w = c->tty.sx;
if (w == 0 || w > c->tty.sx)
w = c->tty.sx;
/* Get horizontal position from message-style's align directive. */
if (sy != NULL) {
switch (sy->align) {
case STYLE_ALIGN_CENTRE:
case STYLE_ALIGN_ABSOLUTE_CENTRE:
*area_x = (c->tty.sx - w) / 2;
break;
case STYLE_ALIGN_RIGHT:
*area_x = c->tty.sx - w;
break;
default:
*area_x = 0;
break;
}
} else
*area_x = 0;
*area_w = w;
}
/* Escape # characters in a string so format_draw treats them as literal. */
static char *
status_prompt_escape(const char *s)
{
const char *cp;
char *out, *p;
size_t n = 0;
for (cp = s; *cp != '\0'; cp++) {
if (*cp == '#')
n++;
}
p = out = xmalloc(strlen(s) + n + 1);
for (cp = s; *cp != '\0'; cp++) {
if (*cp == '#')
*p++ = '#';
*p++ = *cp;
}
*p = '\0';
return (out);
}
/* Draw client message on status line of present else on last line. */
int
status_message_redraw(struct client *c)
@@ -554,10 +624,12 @@ status_message_redraw(struct client *c)
struct screen_write_ctx ctx;
struct session *s = c->session;
struct screen old_screen;
size_t len;
u_int lines, offset, messageline;
u_int lines, messageline;
u_int ax, aw;
struct grid_cell gc;
struct format_tree *ft;
const char *msgfmt;
char *expanded, *msg;
if (c->tty.sx == 0 || c->tty.sy == 0)
return (0);
@@ -572,26 +644,36 @@ status_message_redraw(struct client *c)
if (messageline > lines - 1)
messageline = lines - 1;
len = screen_write_strlen("%s", c->message_string);
if (len > c->tty.sx)
len = c->tty.sx;
status_prompt_area(c, &ax, &aw);
ft = format_create_defaults(NULL, c, NULL, NULL, NULL);
style_apply(&gc, s->options, "message-style", ft);
memcpy(&gc, &grid_default_cell, sizeof gc);
/*
* Set #{message} in the format tree. If styles should be ignored in
* the message content, escape # characters so format_draw treats them
* as literal text.
*/
if (c->message_ignore_styles) {
msg = status_prompt_escape(c->message_string);
format_add(ft, "message", "%s", msg);
free(msg);
} else
format_add(ft, "message", "%s", c->message_string);
format_add(ft, "command_prompt", "%d", 0);
msgfmt = options_get_string(s->options, "message-format");
expanded = format_expand_time(ft, msgfmt);
format_free(ft);
screen_write_start(&ctx, sl->active);
screen_write_fast_copy(&ctx, &sl->screen, 0, 0, c->tty.sx, lines);
screen_write_cursormove(&ctx, 0, messageline, 0);
for (offset = 0; offset < c->tty.sx; offset++)
screen_write_putc(&ctx, &gc, ' ');
screen_write_cursormove(&ctx, 0, messageline, 0);
if (c->message_ignore_styles)
screen_write_nputs(&ctx, len, &gc, "%s", c->message_string);
else
format_draw(&ctx, &gc, c->tty.sx, c->message_string, NULL, 0);
screen_write_cursormove(&ctx, ax, messageline, 0);
format_draw(&ctx, &gc, aw, expanded, NULL, 0);
screen_write_stop(&ctx);
free(expanded);
if (grid_compare(sl->active->grid, old_screen.grid) == 0) {
screen_free(&old_screen);
return (0);
@@ -784,9 +866,10 @@ status_prompt_redraw(struct client *c)
struct screen old_screen;
u_int i, lines, offset, left, start, width, n;
u_int pcursor, pwidth, promptline;
u_int ax, aw;
struct grid_cell gc;
struct format_tree *ft = c->prompt_formats;
char *prompt, *tmp;
const char *msgfmt;
char *expanded, *prompt, *tmp;
if (c->tty.sx == 0 || c->tty.sy == 0)
return (0);
@@ -799,7 +882,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);
@@ -808,29 +894,40 @@ status_prompt_redraw(struct client *c)
promptline = lines - 1;
if (c->prompt_mode == PROMPT_COMMAND)
style_apply(&gc, s->options, "message-command-style", ft);
style_apply(&gc, s->options, "message-command-style", NULL);
else
style_apply(&gc, s->options, "message-style", ft);
style_apply(&gc, s->options, "message-style", NULL);
status_prompt_area(c, &ax, &aw);
tmp = utf8_tocstr(c->prompt_buffer);
format_add(c->prompt_formats, "prompt-input", "%s", tmp);
prompt = format_expand_time(c->prompt_formats, c->prompt_string);
free (tmp);
free(tmp);
/*
* Set #{message} to the prompt string and expand message-format.
* format_draw handles fill, alignment, and decorations in one call.
*/
format_add(c->prompt_formats, "message", "%s", prompt);
format_add(c->prompt_formats, "command_prompt", "%d",
c->prompt_mode == PROMPT_COMMAND);
msgfmt = options_get_string(s->options, "message-format");
expanded = format_expand_time(c->prompt_formats, msgfmt);
start = format_width(prompt);
if (start > c->tty.sx)
start = c->tty.sx;
if (start > aw)
start = aw;
screen_write_start(&ctx, sl->active);
screen_write_fast_copy(&ctx, &sl->screen, 0, 0, c->tty.sx, lines);
screen_write_cursormove(&ctx, 0, promptline, 0);
for (offset = 0; offset < c->tty.sx; offset++)
screen_write_putc(&ctx, &gc, ' ');
screen_write_cursormove(&ctx, 0, promptline, 0);
format_draw(&ctx, &gc, start, prompt, NULL, 0);
screen_write_cursormove(&ctx, start, promptline, 0);
screen_write_cursormove(&ctx, ax, promptline, 0);
format_draw(&ctx, &gc, aw, expanded, NULL, 0);
screen_write_cursormove(&ctx, ax + start, promptline, 0);
left = c->tty.sx - start;
free(expanded);
left = aw - start;
if (left == 0)
goto finished;
@@ -849,7 +946,7 @@ status_prompt_redraw(struct client *c)
offset = 0;
if (pwidth > left)
pwidth = left;
c->prompt_cursor = start + pcursor - offset;
c->prompt_cursor = ax + start + pcursor - offset;
width = 0;
for (i = 0; c->prompt_buffer[i].size != 0; i++) {
@@ -931,6 +1028,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 +1055,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 +1472,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;
@@ -1815,7 +1920,7 @@ status_prompt_complete_list_menu(struct client *c, char **list, u_int size,
struct menu_item item;
struct status_prompt_menu *spm;
u_int lines = status_line_size(c), height, i;
u_int py;
u_int py, ax, aw;
if (size <= 1)
return (0);
@@ -1843,11 +1948,13 @@ status_prompt_complete_list_menu(struct client *c, char **list, u_int size,
menu_add_item(menu, &item, NULL, c, NULL);
}
status_prompt_area(c, &ax, &aw);
if (options_get_number(c->session->options, "status-position") == 0)
py = lines;
else
py = c->tty.sy - 3 - height;
offset += utf8_cstrwidth(c->prompt_string);
offset += ax;
if (offset > 2)
offset -= 2;
else
@@ -1874,7 +1981,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, ax, aw;
if (c->tty.sy - lines < 3)
return (NULL);
@@ -1939,11 +2046,13 @@ status_prompt_complete_window_menu(struct client *c, struct session *s,
spm->size = size;
spm->list = list;
status_prompt_area(c, &ax, &aw);
if (options_get_number(c->session->options, "status-position") == 0)
py = lines;
else
py = c->tty.sy - 3 - height;
offset += utf8_cstrwidth(c->prompt_string);
offset += ax;
if (offset > 2)
offset -= 2;
else
@@ -1953,6 +2062,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);
}

55
style.c
View File

@@ -39,7 +39,7 @@ static struct style style_default = {
STYLE_RANGE_NONE, 0, "",
STYLE_WIDTH_DEFAULT, STYLE_PAD_DEFAULT,
STYLE_WIDTH_DEFAULT, 0, STYLE_PAD_DEFAULT,
STYLE_DEFAULT_BASE
};
@@ -218,20 +218,33 @@ 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;
if (end > 7 && tmp[end - 1] == '%') {
tmp[end - 1] = '\0';
n = strtonum(tmp + 6, 0, 100, &errstr);
if (errstr != NULL)
goto error;
sy->width = (int)n;
sy->width_percentage = 1;
} else {
n = strtonum(tmp + 6, 0, UINT_MAX, &errstr);
if (errstr != NULL)
goto error;
sy->width = (int)n;
sy->width_percentage = 0;
}
} 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;
@@ -340,17 +353,21 @@ style_tostring(struct style *sy)
comma = ",";
}
if (gc->attr != 0) {
xsnprintf(s + off, sizeof s - off, "%s%s", comma,
off += xsnprintf(s + off, sizeof s - off, "%s%s", comma,
attributes_tostring(gc->attr));
comma = ",";
}
if (sy->width >= 0) {
xsnprintf(s + off, sizeof s - off, "%swidth=%u", comma,
sy->width);
if (sy->width >= 0) {
if (sy->width_percentage)
off += xsnprintf(s + off, sizeof s - off,
"%swidth=%u%%", comma, sy->width);
else
off += 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 = ",";
}

2864
tmux.1

File diff suppressed because it is too large Load Diff

196
tmux.h
View File

@@ -652,6 +652,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 +728,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 \
@@ -918,6 +920,7 @@ struct style {
char range_string[16];
int width;
int width_percentage;
int pad;
enum style_default_type default_type;
@@ -954,7 +957,7 @@ struct screen_sel;
struct screen_titles;
struct screen {
char *title;
char *path;
char *path;
struct screen_titles *titles;
struct grid *grid; /* grid data */
@@ -1099,6 +1102,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 +1133,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 +1143,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 +1170,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 +1236,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 +1289,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 +1537,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;
@@ -1578,6 +1617,7 @@ struct tty {
size_t discarded;
struct termios tio;
struct visible_ranges r;
struct grid_cell cell;
struct grid_cell last_cell;
@@ -1894,32 +1934,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 +1989,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 +2034,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 +2101,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 +2148,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 +2268,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 +2354,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 +2369,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 +2502,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 +2583,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,7 +2609,15 @@ 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 *);
@@ -2724,6 +2809,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 +2902,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 +2975,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 +3033,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,16 +3067,16 @@ 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 *);
void input_parse_pane(struct window_pane *);
void input_parse_buffer(struct window_pane *, u_char *, size_t);
void input_parse_buffer(struct window_pane *, const u_char *, size_t);
void input_parse_screen(struct input_ctx *, struct screen *,
screen_write_init_ctx_cb, void *, u_char *, size_t);
screen_write_init_ctx_cb, void *, const 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 +3121,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 +3226,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);
@@ -3201,11 +3294,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 +3451,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,8 +3459,10 @@ 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);
typedef const char** (*mode_tree_help_cb)(u_int *, const char**);
u_int mode_tree_count_tagged(struct mode_tree_data *);
void *mode_tree_get_current(struct mode_tree_data *);
const char *mode_tree_get_current_name(struct mode_tree_data *);
@@ -3381,8 +3477,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, mode_tree_help_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 +3588,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 +3611,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 +3625,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 +3684,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 *);
@@ -3598,6 +3696,7 @@ int menu_key_cb(struct client *, void *, struct key_event *);
#define POPUP_CLOSEEXITZERO 0x2
#define POPUP_INTERNAL 0x4
#define POPUP_CLOSEANYKEY 0x8
#define POPUP_NOJOB 0x10
typedef void (*popup_close_cb)(int, void *);
typedef void (*popup_finish_edit_cb)(char *, size_t, void *);
int popup_display(int, enum box_lines, struct cmdq_item *, u_int,
@@ -3605,6 +3704,7 @@ int popup_display(int, enum box_lines, struct cmdq_item *, u_int,
char **, const char *, const char *, struct client *,
struct session *, const char *, const char *,
popup_close_cb, void *);
void popup_write(struct client *, const char *, size_t);
int popup_editor(struct client *, const char *, size_t,
popup_finish_edit_cb, void *);
int popup_present(struct client *);

337
tty-draw.c Normal file
View File

@@ -0,0 +1,337 @@
/* $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)
{
struct visible_ranges *r;
struct visible_range *rr;
u_int i;
/* Nothing to clear. */
if (nx == 0)
return;
/* If genuine BCE is available, can try escape sequences. */
if (tty->client->overlay_check == NULL &&
!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. */
r = tty_check_overlay_range(tty, px, py, nx);
for (i = 0; i < r->used; i++) {
rr = &r->ranges[i];
if (rr->nx != 0) {
if (rr->px != 0 || !wrapped)
tty_cursor(tty, rr->px, py);
if (rr->nx == 1)
tty_putc(tty, ' ');
else if (rr->nx == 2)
tty_putn(tty, " ", 2, 2);
else
tty_repeat_space(tty, rr->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

@@ -909,9 +909,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 +956,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 +1308,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 +1360,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 +1377,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 +1384,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 +1624,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)
@@ -1686,12 +1700,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 +1734,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 +1759,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;

350
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,18 +61,11 @@ 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
static void tty_write_one(void (*)(struct tty *, const struct tty_ctx *),
@@ -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);
}
@@ -445,6 +441,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 +521,8 @@ void
tty_free(struct tty *tty)
{
tty_close(tty);
free(tty->r.ranges);
}
void
@@ -634,7 +633,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 +908,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 +1067,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,7 +1162,8 @@ 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;
struct visible_ranges *r;
struct visible_range *rr;
u_int i;
log_debug("%s: %s, %u at %u,%u", __func__, c->name, nx, px, py);
@@ -1199,12 +1200,13 @@ 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]);
r = tty_check_overlay_range(tty, px, py, nx);
for (i = 0; i < r->used; i++) {
rr = &r->ranges[i];
if (rr->nx != 0) {
tty_cursor(tty, rr->px, py);
tty_repeat_space(tty, rr->nx);
}
}
}
@@ -1371,22 +1373,38 @@ static void
tty_draw_pane(struct tty *tty, const struct tty_ctx *ctx, u_int py)
{
struct screen *s = ctx->s;
u_int nx = ctx->sx, i, x, rx, ry;
u_int nx = ctx->sx, i, x, rx, ry, j;
struct visible_ranges *r;
struct visible_range *rr;
log_debug("%s: %s %u %d", __func__, tty->client->name, py, ctx->bigger);
if (!ctx->bigger) {
tty_draw_line(tty, s, 0, py, nx, ctx->xoff, ctx->yoff + py,
&ctx->defaults, ctx->palette);
r = tty_check_overlay_range(tty, ctx->xoff, ctx->yoff + py, nx);
for (j = 0; j < r->used; j++) {
rr = &r->ranges[j];
if (rr->nx != 0) {
tty_draw_line(tty, s, rr->px - ctx->xoff, py,
rr->nx, rr->px, ctx->yoff + py,
&ctx->defaults, ctx->palette);
}
}
return;
}
if (tty_clamp_line(tty, ctx, 0, py, nx, &i, &x, &rx, &ry)) {
tty_draw_line(tty, s, i, py, rx, x, ry, &ctx->defaults,
ctx->palette);
r = tty_check_overlay_range(tty, x, ry, rx);
for (j = 0; j < r->used; j++) {
rr = &r->ranges[j];
if (rr->nx != 0) {
tty_draw_line(tty, s, i + (rr->px - x), py,
rr->nx, rr->px, ry, &ctx->defaults,
ctx->palette);
}
}
}
}
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,229 +1437,34 @@ 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);
}
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);
return (c->overlay_check(c, c->overlay_data, px, py, nx));
}
#ifdef ENABLE_SIXEL
@@ -2169,7 +1992,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 +2001,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 +2037,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 +2063,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 +2088,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 +2104,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);
}
@@ -2380,7 +2211,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 +3023,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 +3038,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 +3047,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:

80
utf8.c
View File

@@ -292,16 +292,37 @@ utf8_find_in_width_cache(wchar_t wc)
return RB_FIND(utf8_width_cache, &utf8_width_cache, &uw);
}
/* Add to width cache. */
static void
utf8_insert_width_cache(wchar_t wc, u_int width)
{
struct utf8_width_item *uw, *old;
log_debug("Unicode width cache: %08X=%u", (u_int)wc, width);
uw = xcalloc(1, sizeof *uw);
uw->wc = wc;
uw->width = width;
uw->allocated = 1;
old = RB_INSERT(utf8_width_cache, &utf8_width_cache, uw);
if (old != NULL) {
RB_REMOVE(utf8_width_cache, &utf8_width_cache, old);
if (old->allocated)
free(old);
RB_INSERT(utf8_width_cache, &utf8_width_cache, uw);
}
}
/* Parse a single codepoint option. */
static void
utf8_add_to_width_cache(const char *s)
{
struct utf8_width_item *uw, *old;
char *copy, *cp, *endptr;
u_int width;
const char *errstr;
struct utf8_data *ud;
wchar_t wc;
wchar_t wc, wc_start, wc_end;
unsigned long long n;
copy = xstrdup(s);
@@ -321,14 +342,40 @@ utf8_add_to_width_cache(const char *s)
errno = 0;
n = strtoull(copy + 2, &endptr, 16);
if (copy[2] == '\0' ||
*endptr != '\0' ||
n == 0 ||
n > WCHAR_MAX ||
(errno == ERANGE && n == ULLONG_MAX)) {
free(copy);
return;
}
wc = n;
wc_start = n;
if (*endptr == '-') {
endptr++;
if (strncmp(endptr, "U+", 2) != 0) {
free(copy);
return;
}
errno = 0;
n = strtoull(endptr + 2, &endptr, 16);
if (*endptr != '\0' ||
n == 0 ||
n > WCHAR_MAX ||
(errno == ERANGE && n == ULLONG_MAX) ||
(wchar_t)n < wc_start) {
free(copy);
return;
}
wc_end = n;
} else {
if (*endptr != '\0') {
free(copy);
return;
}
wc_end = wc_start;
}
for (wc = wc_start; wc <= wc_end; wc++)
utf8_insert_width_cache(wc, width);
} else {
utf8_no_width = 1;
ud = utf8_fromcstr(copy);
@@ -348,21 +395,8 @@ utf8_add_to_width_cache(const char *s)
return;
}
free(ud);
}
log_debug("Unicode width cache: %08X=%u", (u_int)wc, width);
uw = xcalloc(1, sizeof *uw);
uw->wc = wc;
uw->width = width;
uw->allocated = 1;
old = RB_INSERT(utf8_width_cache, &utf8_width_cache, uw);
if (old != NULL) {
RB_REMOVE(utf8_width_cache, &utf8_width_cache, old);
if (old->allocated)
free(old);
RB_INSERT(utf8_width_cache, &utf8_width_cache, uw);
utf8_insert_width_cache(wc, width);
}
free(copy);
@@ -652,7 +686,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 +724,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 +738,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,33 @@ 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 const char* window_buffer_help_lines[] = {
"\r\033[1m Enter \033[0m\016x\017 \033[0mPaste selected %1\n",
"\r\033[1m p \033[0m\016x\017 \033[0mPaste selected %1\n",
"\r\033[1m P \033[0m\016x\017 \033[0mPaste tagged %1s\n",
"\r\033[1m d \033[0m\016x\017 \033[0mDelete selected %1\n",
"\r\033[1m D \033[0m\016x\017 \033[0mDelete tagged %1s\n",
"\r\033[1m e \033[0m\016x\017 \033[0mOpen %1 in editor\n",
"\r\033[1m f \033[0m\016x\017 \033[0mEnter a filter\n",
NULL
};
static const char**
window_buffer_help(u_int *width, const char **item)
{
*width = 0;
*item = "buffer";
return (window_buffer_help_lines);
}
static struct screen *
window_buffer_init(struct window_mode_entry *wme, struct cmd_find_state *fs,
struct args *args)
@@ -378,8 +375,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, window_buffer_help,
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,34 @@ 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 const char* window_client_help_lines[] = {
"\r\033[1m Enter \033[0m\016x\017 \033[0mChoose selected %1\n",
"\r\033[1m d \033[0m\016x\017 \033[0mDetach selected %1\n",
"\r\033[1m D \033[0m\016x\017 \033[0mDetach tagged %1s\n",
"\r\033[1m x \033[0m\016x\017 \033[0mDetach selected %1\n",
"\r\033[1m X \033[0m\016x\017 \033[0mDetach tagged %1s\n",
"\r\033[1m z \033[0m\016x\017 \033[0mSuspend selected %1\n",
"\r\033[1m Z \033[0m\016x\017 \033[0mSuspend tagged %1s\n",
"\r\033[1m f \033[0m\016x\017 \033[0mEnter a filter\n",
NULL
};
static const char**
window_client_help(u_int *width, const char **item)
{
*width = 0;
*item = "client";
return (window_client_help_lines);
}
static struct screen *
window_client_init(struct window_mode_entry *wme,
__unused struct cmd_find_state *fs, struct args *args)
@@ -306,8 +286,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,
window_client_help, 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;
@@ -868,6 +868,27 @@ window_customize_height(__unused void *modedata, __unused u_int height)
return (12);
}
static const char* window_customize_help_lines[] = {
"\r\033[1m Enter, s \033[0m\016x\017 \033[0mSet %1 value\n",
"\r\033[1m S \033[0m\016x\017 \033[0mSet global %1 value\n",
"\r\033[1m w \033[0m\016x\017 \033[0mSet window %1 value\n",
"\r\033[1m d \033[0m\016x\017 \033[0mSet to default value\n",
"\r\033[1m D \033[0m\016x\017 \033[0mSet tagged %1s to default value\n",
"\r\033[1m u \033[0m\016x\017 \033[0mUnset an %1\n",
"\r\033[1m U \033[0m\016x\017 \033[0mUnset tagged %1s\n",
"\r\033[1m f \033[0m\016x\017 \033[0mEnter a filter\n",
"\r\033[1m v \033[0m\016x\017 \033[0mToggle information\n",
NULL
};
static const char**
window_customize_help(u_int *width, const char **item)
{
*width = 52;
*item = "option";
return (window_customize_help_lines);
}
static struct screen *
window_customize_init(struct window_mode_entry *wme, struct cmd_find_state *fs,
struct args *args)
@@ -891,8 +912,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, window_customize_help,
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,38 @@ 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 const char* window_tree_help_lines[] = {
"\r\033[1m Enter \033[0m\016x\017 \033[0mChoose selected item\n",
"\r\033[1m S-Up \033[0m\016x\017 \033[0mSwap current and previous window\n",
"\r\033[1m S-Down \033[0m\016x\017 \033[0mSwap current and next window\n",
"\r\033[1m x \033[0m\016x\017 \033[0mKill selected item\n",
"\r\033[1m X \033[0m\016x\017 \033[0mKill tagged items\n",
"\r\033[1m < \033[0m\016x\017 \033[0mScroll previews left\n",
"\r\033[1m > \033[0m\016x\017 \033[0mScroll previews right\n",
"\r\033[1m m \033[0m\016x\017 \033[0mSet the marked pane\n",
"\r\033[1m M \033[0m\016x\017 \033[0mClear the marked pane\n",
"\r\033[1m : \033[0m\016x\017 \033[0mRun a command for each tagged item\n",
"\r\033[1m f \033[0m\016x\017 \033[0mEnter a format\n",
"\r\033[1m H \033[0m\016x\017 \033[0mJump to the starting pane\n",
NULL
};
static const char**
window_tree_help(u_int *width, const char **item)
{
*width = 51;
*item = "item";
return (window_tree_help_lines);
}
static struct screen *
window_tree_init(struct window_mode_entry *wme, struct cmd_find_state *fs,
struct args *args)
@@ -1013,8 +922,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,
window_tree_help, 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;
}