Compare commits

...

715 Commits
3.3a ... master

Author SHA1 Message Date
Thomas Adam
96244dd727 Merge branch 'obsd-master' 2025-06-20 18:01:08 +01:00
Thomas Adam
b38b5d3df2 Merge branch 'obsd-master' 2025-06-20 16:01:08 +01:00
nicm
ad3e6ff054 Add noattr and use in mode-style to allow whether attributes are ignored
or used to be configured. GitHub issue 4498.
2025-06-20 14:54:33 +00:00
nicm
522652913f Add S: to list sessions with modifiers for sorting, from Michael Grant. 2025-06-20 13:31:59 +00:00
Thomas Adam
43e88c892d Merge branch 'obsd-master' 2025-06-16 02:01:07 +01:00
nicm
d82d9468b4 Expand mode-style with E: so # is correctly processed, GitHub issue
4533.
2025-06-15 22:24:51 +00:00
nicm
d7f75ac985 Do not replace SHELL when using /bin/sh. From someone in GitHub issue 4528. 2025-06-15 21:57:58 +00:00
Thomas Adam
1dbceaa790 Merge branch 'obsd-master' 2025-05-28 14:01:08 +01:00
nicm
77ca59acac Add missing theme hooks, from Eric NICOLAS in GitHub issue 4519. 2025-05-28 09:53:57 +00:00
Thomas Adam
7be0613657 Merge branch 'obsd-master' 2025-05-22 12:01:07 +01:00
Thomas Adam
b9ad9186fe Merge branch 'obsd-master' 2025-05-22 10:01:08 +01:00
nicm
3f4b154b70 Bump UTF8_SIZE to the maximum 32 because there are some crazy long UTF-8
sequences out there (GitHub issue 4506). This should not significantly
increase typical memory consumption because we only store each Unicode
character once in the shared cache.
2025-05-22 08:01:29 +00:00
Nicholas Marriott
ff73c95547 New bash completion URL, from David Mandelberg. 2025-05-22 08:52:10 +01:00
Nicholas Marriott
aa0d05f676 Typos, from someone in GitHub issue 4511. 2025-05-22 08:51:14 +01:00
nicm
545832e9fa Typo, from someone in GitHub issue 4511. 2025-05-22 07:49:24 +00:00
nicm
a0ac2a5d63 When there are more than two horizontal windows and the active window is
in not on an edge, correctly highlight both its left and right borders.
GitHub issue 4513 from Michael Grant.
2025-05-22 07:46:38 +00:00
nicm
833c7fbf6d Add a set-default style attribute which replaces the current default
colours and attributes completely, useful at the start of compound
format strings (like status-format) to set the default colours for all
the following options.
2025-05-22 07:43:38 +00:00
Thomas Adam
faf2a44890 Merge branch 'obsd-master' 2025-05-12 14:01:10 +01:00
Thomas Adam
d194ceb604 Merge branch 'obsd-master' 2025-05-12 12:01:10 +01:00
nicm
37a2c98d3d Expand prompts when they are used rather than ahead of time, so the
input can be used as part of the format.
2025-05-12 10:34:13 +00:00
nicm
79b02998a6 Add R format modifier to repeat an argument. 2025-05-12 10:26:19 +00:00
nicm
1ce1e7ef27 Add -E to run-shell to forward stderr as well as stdout, from github at
jyn dot dev in GitHub issue 4246.
2025-05-12 10:16:42 +00:00
nicm
7499d925da Do not downgrade styled underscores to standard underscore if the
terminal does not support them, this matches what would happen if the
application tried to use them on a terminal without support.
2025-05-12 09:50:00 +00:00
nicm
367f17a4ff Preserve colours in selection if the option style is default, GitHub
issue 4498.
2025-05-12 09:17:42 +00:00
Thomas Adam
3d2b26dcfe Merge branch 'obsd-master' 2025-05-01 10:01:11 +01:00
nicm
6106a0f2c4 Add an option variation-selector-always-wide to instruct tmux not to
always interpret VS16 as a wide character and assume the terminal does
likewise. This is behaviour seen in a number of newer terminals' Unicode
14 support but not in older terminals; it seems to be a little
contentious and is currently difficult to detect.

Probably in the long run tmux should pick a behaviour, look at a (new)
terminfo(5) capability to tell it what the terminal will do, and emulate
as required, but at this point I'm not sure that is worth it for
something where support is mixed, seems to be in flux, and that mostly
only matters for emojis.

GitHub issues 3923 and 4475 and others before that.
2025-05-01 07:12:00 +00:00
nicm
1926c1b683 Don't map 256 to white-on-white either, and tidy code a bit. 2025-05-01 06:59:32 +00:00
Nicholas Marriott
6cc1a607fc optarg should be "extern char *" not "extern const char *". 2025-05-01 07:53:02 +01:00
Nicholas Marriott
1905afb3d8 tmux has its own warnx so there is no need for logit (and this gets rid of a
warning).
2025-04-29 17:39:56 +01:00
Nicholas Marriott
345a45083d Run the regress/style-trim.sh test in a plain Bash session, from Koichi Murase. 2025-04-29 17:29:32 +01:00
Nicholas Marriott
3a3babf597 Use printf not echo, from Koichi Murase. 2025-04-28 14:51:27 +01:00
Nicholas Marriott
f367e3a6c8 Switch to getopt_long from OpenSSH, from Koichi Murase in GitHub issue 4492. 2025-04-28 09:02:15 +01:00
Nicholas Marriott
f2283b0cb5 Fix some regress tests. 2025-04-28 08:57:29 +01:00
Nicholas Marriott
26081ac125
Create config.yml 2025-04-28 07:27:57 +01:00
Thomas Adam
36fba4cc54 Merge branch 'obsd-master' 2025-04-25 16:01:08 +01:00
Thomas Adam
58b4286edb Merge branch 'obsd-master' 2025-04-25 14:01:10 +01:00
nicm
b0b6a6f665 Revert change to handle command parsing failures in the client because
it breaks aliases.
2025-04-25 12:25:32 +00:00
nicm
63e5989f53 Remove a stray period, reported by David Mandelberg. 2025-04-25 11:39:39 +00:00
Nicholas Marriott
9da5707d3e Add a run-shell output test. 2025-04-25 12:06:51 +01:00
Thomas Adam
55292b72d1 Merge branch 'obsd-master' 2025-04-25 12:01:09 +01:00
Nicholas Marriott
06b1ad6241 Tests for boolean expressions from David Mandelberg. 2025-04-25 09:28:57 +01:00
nicm
1efe41b9b9 Add more features for boolean expressions in formats: 1) extend && and
|| to support arbitrarily many arguments and 2) add ! and !! for not and
not-not.
2025-04-25 08:28:21 +00:00
Nicholas Marriott
1198eed632 Currently the DCS dispatcher detects Sixel sequences based entirely on the
final character of the sequence, ignoring any intermediates. This means that
other DCS sequences with a q final (like DECRQSS), can be mistakenly
interpreted as Sixel. Add a check to make sure there are no intermediates
before dispatching potential Sixel sequences. From James Holderness in GitHub
issue 4488.
2025-04-25 08:26:26 +01:00
Thomas Adam
0772fe822a Merge branch 'obsd-master' 2025-04-24 12:01:10 +01:00
nicm
f53fac1b56 Do not add a trailing / if there is nothing to follow it, Johannes
Altmanninger in GitHub issue 4472.
2025-04-24 08:55:40 +00:00
nicm
68f2ac9296 Fix examples with too many backslashes, pointed out by David Mandelberg. 2025-04-24 08:54:40 +00:00
Thomas Adam
b4f8340f16 Merge branch 'obsd-master' 2025-04-22 16:01:12 +01:00
nicm
d2f73c17d5 Add calls to layout_fix_panes after a pane swap to fix case if one of
the panes is in alternate screen mode and had a scrollbar. From Michael
Grant in GitHub issue 4481.
2025-04-22 12:36:03 +00:00
nicm
5db914b745 Add notes to menu keys, from someone in GitHub issue 4478. 2025-04-22 12:34:56 +00:00
nicm
cd3d2910ee Fix description of pane_in_mode, from Julian Prein in GitHub issue 4469. 2025-04-22 12:33:35 +00:00
Nicholas Marriott
de7653f5a4 Update format tests for #? changes, from David Mandelberg. 2025-04-22 13:23:33 +01:00
nicm
b905039ed2 Improve #? conditional expression in formats:
1) add support for else if, so
   #{?cond1,value1,#{?cond2,value2,else-value}} can be changed to
   #{?cond1,value1,cond2,value2,else-value};

2) add default empty string if there's no else value, so
   #{?cond1,value1,} can be changed to #{?cond1,value1}.

From David Mandelberg in GitHub issue 4451.
2025-04-22 12:23:26 +00:00
Nicholas Marriott
f0a85d0469 Move cgroup dbus requests to the child to avoid a race where a spawned child
that quickly forks will have only the parent process moved to the newly created
cgroup. From Daniel De Graaf, GitHub issue 4435.
2025-04-14 07:27:02 +01:00
Thomas Adam
ac2779f4d8 Merge branch 'obsd-master' 2025-04-09 10:01:12 +01:00
nicm
68ffe65499 Fix documentation around optional arguments. This includes:
- Syncing between the usage string in code and in the man page.
- Adding optional arguments that were not mentioned (such as
  shell-command arguments).
- Adding square brackets around arguments that are actually optional.

From Julian Prein (julian at druck dot dev) in GitHub issue 4419.
2025-04-09 07:03:04 +00:00
nicm
3e14e7c48b Formats can use environment variables, from David Mandelberg. 2025-04-09 06:51:31 +00:00
nicm
b526e678b5 Make some usages more consistent and add -h to show usage, GitHub issue
4455 from David Mandelberg.
2025-04-09 06:27:43 +00:00
Thomas Adam
d3c39375d5 Merge branch 'obsd-master' 2025-04-03 14:01:10 +01:00
nicm
a7991dcbad Fix padding for word function as well, GitHub issue 4425. 2025-04-03 11:52:25 +00:00
nicm
47dff97834 Use backing grid for word in copy mode for wrapped flags, from someone
in GitHub issue 4447.
2025-04-03 11:51:27 +00:00
Thomas Adam
2905395695 Merge branch 'obsd-master' 2025-04-02 12:01:13 +01:00
nicm
4bf38da4d4 Only align panes and windows, not sessions, from David Mandelberg in
GitHub issue 4444.
2025-04-02 09:31:00 +00:00
nicm
9b37b9285e Popup window should not be draggable while mouse still pressed, and do
not try to work out theme if no pane. From Michael Grant in GitHub issue
4330.
2025-04-02 09:12:05 +00:00
Thomas Adam
dfdced2ab1 Merge branch 'obsd-master' 2025-03-31 00:01:10 +01:00
Nicholas Marriott
c2a95a372f Add a note about closed issues, from David Mandelberg. 2025-03-30 23:04:22 +01:00
nicm
ef923d28ff Do not try to trigger theme changed if session is NULL. 2025-03-30 22:01:55 +00:00
nicm
53b0e0bc02 Missing space, from David Mandelberg. 2025-03-30 21:47:01 +00:00
Thomas Adam
096c4b3e63 Merge branch 'obsd-master' 2025-03-28 20:01:10 +00:00
Thomas Adam
b8189d6ee3 Merge branch 'obsd-master' 2025-03-28 18:01:11 +00:00
nicm
f41dc91357 Only copy the key string not two bytes extra, found by David Mandelberg. 2025-03-28 17:15:25 +00:00
nicm
ef0c12ad9e Fix read of uninitialized memory for jobs with JOB_PTY flag. From David
Mandelberg.
2025-03-28 17:03:49 +00:00
Thomas Adam
9e1f110db0 Merge branch 'obsd-master' 2025-03-24 22:01:11 +00:00
nicm
483b2b3edb Correctly skip wide characters in hyperlinks, from someone in GitHub
issue 4425.
2025-03-24 20:17:24 +00:00
nicm
34a35b0f09 Expand formats with the pane modifier in tree mode so that #() doesn't
always use the same value. From Michael Grant in GitHub issues 4412 and
4420.
2025-03-24 20:13:03 +00:00
nicm
aca3ffb30a Add default-client-command to set the command used is tmux is run
without a command (the default stays new-session). From David Mandelberg
in GitHub issue 4422.
2025-03-24 20:01:03 +00:00
Thomas Adam
c3c4524def Merge branch 'obsd-master' 2025-03-21 16:01:11 +00:00
nicm
f101762d1b Fix mouse_hyperlink format in copy mode. From someone in GitHub issue
4418.
2025-03-21 14:04:26 +00:00
nicm
a541be3951 Add S-Up and S-Down to move windows in tree mode. From David Mandelberg
in GitHub issue 4415.
2025-03-21 13:36:42 +00:00
nicm
b7d640e764 Add some additional cursor format variables. From shiro at usagi dot io
in GitHub issue 4414.
2025-03-21 13:26:39 +00:00
Thomas Adam
969f6a60c3 portable: SYNCING: correct tmux-openbsd
The tmux-openbsd repository is called tmux-obsd.

Noticed via Github issue #4419
2025-03-21 12:46:06 +00:00
Thomas Adam
6703cb85d9 Merge branch 'obsd-master' 2025-03-21 04:01:10 +00:00
jsg
111e16e772 remove prototypes for removed functions 2025-03-21 02:10:42 +00:00
Thomas Adam
3eb93383a3 Merge branch 'obsd-master' 2025-03-17 22:01:10 +00:00
nicm
817b621d20 If there is an active query, set escape time temporarily to a higher
value (the old default - 500). Some Windows terminals are very slow to
respond, or the network may be slow. From github at jyn dot dev.
2025-03-17 20:43:29 +00:00
nicm
5eb30c1543 Handle padding cells correctly for regular expression searching, GitHub issue 4399 from
github at jyn dot dev.
2025-03-17 20:33:20 +00:00
Thomas Adam
d4b8635f50 Merge branch 'obsd-master' 2025-03-11 10:01:12 +00:00
nicm
4e4fe3eb39 Cleanup window_get_active_at function. GitHub issue 4401 from Michael
Grant.
2025-03-11 08:14:26 +00:00
Thomas Adam
882fb4d295 Merge branch 'obsd-master' 2025-03-04 10:01:15 +00:00
nicm
eaf70c955b Add mode 2031 support to automatically report dark or light theme. tmux
will guess the theme from the background colour on terminals which do
not themselves support the escape sequence. Written by Jonathan
Slenders, GitHub issue 4353.
2025-03-04 08:45:04 +00:00
nicm
3543d79048 Free fill character string if it cannot be used, GitHub issue 4394. 2025-03-04 08:03:19 +00:00
Thomas Adam
94783addfc Merge branch 'obsd-master' 2025-02-26 10:01:14 +00:00
nicm
91c0de60b4 Also need the implied meta paste keys in the list for output. 2025-02-26 09:02:00 +00:00
nicm
21f7db4c4d Do not allow meta prefix on paste start and end sequences, GitHub issue 4387. 2025-02-26 08:55:27 +00:00
nicm
f224d61f37 Document the use of ';' as a modifier separator, from Matt Liggett in
GitHub issue 4384.
2025-02-26 07:50:36 +00:00
nicm
d938ab5dd7 If command parsing fails in the client, report the error rather than
trying to send the command to the server. GitHub issue 4372 from Nikola
Tadic.
2025-02-26 07:47:46 +00:00
nicm
9a8f46e554 Fix colouring of pane border when scrollbars are enabled, GitHub issue
4378 from Michael Grant.
2025-02-26 07:42:52 +00:00
nicm
27ee0c9c3b Add the width of the scrollbars to the calculation of the width of the
window panes when finding the adjacent panes, GitHub issue 4370 from
Michael Grant.
2025-02-26 07:39:50 +00:00
Thomas Adam
251a87e2d2 Merge branch 'obsd-master' 2025-02-20 16:01:14 +00:00
nicm
084e6ee9ec Add a -M flag to capture-pane to use the copy mode screen, GitHub issue
4358.
2025-02-20 13:39:58 +00:00
nicm
18331e39bf Reset overlay_resize pointer when clearing overlay. 2025-02-20 13:32:07 +00:00
Nicholas Marriott
250c88efdc Add .swp, from Nikola Tadic. 2025-02-20 13:31:07 +00:00
Thomas Adam
9a377485be Merge branch 'obsd-master' 2025-02-13 18:01:08 +00:00
nicm
47a56c11f2 Add a note about C-r and C-s behaviour, GitHub issue 4309.
Also add a missing word, from jmc@.
2025-02-13 16:31:25 +00:00
Nicholas Marriott
c4b9716873 Look for imsg_add not _init now. 2025-02-13 16:24:33 +00:00
Thomas Adam
ec119b2f9e Merge branch 'obsd-master' 2025-02-10 10:01:11 +00:00
nicm
5d1a6acc84 Align index numbers in trees, from David Mandelberg, GitHub issue 4360. 2025-02-10 08:18:23 +00:00
nicm
80eb460fc9 Add display-message -C flag to update pane while message is displayed,
GitHub issue 4363 from Vitaly Ostrosablin.
2025-02-10 08:14:32 +00:00
Thomas Adam
ef68debc8d Merge branch 'obsd-master' 2025-01-27 12:01:13 +00:00
nicm
4c12ac9fb8 Make list-commands command show only one command if an argument is
given, from Ilya Grigoriev in GitHub issue 4352.
2025-01-27 09:16:05 +00:00
nicm
244bb726e2 Add some missing spaces, from Ilya Grigoriev. 2025-01-27 09:05:22 +00:00
Nicholas Marriott
6ab268c7bb Remove old issue template. 2025-01-20 13:06:03 +00:00
Nicholas Marriott
0c9f8ff189
Update issue templates 2025-01-20 13:02:24 +00:00
Nicholas Marriott
dd7d04be95
Update issue templates 2025-01-20 13:00:07 +00:00
Thomas Adam
58392d29da Merge branch 'obsd-master' 2025-01-17 18:01:08 +00:00
nicm
9260f5dc96 Do not update focus on client's without a session. 2025-01-17 15:53:01 +00:00
Thomas Adam
62a6c16b43 Merge branch 'obsd-master' 2025-01-13 12:01:11 +00:00
nicm
31e8d4676a Count line numbers correctly inside strings, reported by Pedro Navarro
in GitHub issue 4325.
2025-01-13 08:58:34 +00:00
Thomas Adam
d3cbe00f78 Merge branch 'obsd-master' 2025-01-12 16:01:11 +00:00
nicm
97fe3563fa Do not crash if moving popup that has exited to a pane, from Michael
Grant in GitHub issue 4312.
2025-01-12 14:36:28 +00:00
nicm
37ad1e2f6d Map bright black (colour 8) to white (7) if the background is black on
terminals with only eight colours so the text is not invisible. From
Dmytro Bagrii in GitHub issue 4322.
2025-01-12 14:20:49 +00:00
Thomas Adam
00894d188d Merge branch 'obsd-master' 2025-01-02 12:01:10 +00:00
nicm
2a5eba7899 Check backspace against VERASE earlier before it is translated to an
internal key and do not go through the mapping on output. Fixes problems
reported by Ben Price in GitHub issue 4284 and by tb@.
2025-01-02 10:34:45 +00:00
Thomas Adam
bf30492d57 Merge branch 'obsd-master' 2025-01-01 18:01:10 +00:00
nicm
e00853ee82 Add an option allowing users to override the width of individual Unicode
codepoints (overriding tmux's default list).
2025-01-01 15:17:36 +00:00
Thomas Adam
e75f3a0060 Merge branch 'obsd-master' 2024-12-20 10:01:10 +00:00
nicm
eece41547e Only map S-Tag in mode 2, not mode 1. GitHub issue 4304. 2024-12-20 07:10:51 +00:00
Thomas Adam
c311202b73 Merge branch 'obsd-master' 2024-12-17 10:01:11 +00:00
nicm
e149d29803 Assign excess space more evenly when spreading out cells, from Torbjorn
Lonnemark.
2024-12-17 08:40:24 +00:00
Thomas Adam
190ddda572 Merge branch 'obsd-master' 2024-12-16 12:01:12 +00:00
Thomas Adam
6b470c5222 Merge branch 'obsd-master' 2024-12-16 10:01:11 +00:00
nicm
6b32d195e8 Add a nesting limit to source-file, from Fadi Afani in GitHub issue
4223.
2024-12-16 09:13:09 +00:00
nicm
bec6ce54c1 Memory leak, GitHub issue 4298. 2024-12-16 08:54:34 +00:00
nicm
106d1c3538 Missing main-vertical-mirrored from layout list, from charlotte at
lottia dot net.
2024-12-16 08:51:41 +00:00
Thomas Adam
ae8f2208c9 Merge branch 'obsd-master' 2024-12-06 12:01:11 +00:00
nicm
5c3cf2f08b Preserve modifiers on backspace. 2024-12-06 09:07:40 +00:00
nicm
102f34090d Do not write bracketed paste keys themselves if the pane has not asked
for them.
2024-12-06 09:06:56 +00:00
Thomas Adam
99790c90aa Merge branch 'obsd-master' 2024-12-04 22:01:08 +00:00
nicm
6d792e4123 Fix backspace option for new key format, GitHub issue 4284. 2024-12-04 19:11:15 +00:00
Thomas Adam
98e322d5a5 Merge branch 'obsd-master' 2024-12-03 14:01:12 +00:00
jmc
01edce40f3 M-1 to M-7 for 7 preset layouts; from bunkmate
ok nicm
2024-12-03 11:18:34 +00:00
Thomas Adam
0549f87ccc Merge branch 'obsd-master' 2024-11-28 10:01:11 +00:00
nicm
72cd0eac29 Add extended keys flag for foot terminal. 2024-11-28 08:49:14 +00:00
Thomas Adam
f0c68533c4 Merge branch 'obsd-master' 2024-11-27 12:01:08 +00:00
Nicholas Marriott
9e4a9c51cc Use evbuffer_readline if readln is not available. It doesn't work properly but
at least it builds.
2024-11-27 10:37:45 +00:00
Nicholas Marriott
6874ec0dcc Remove endian.h. 2024-11-27 10:31:51 +00:00
Nicholas Marriott
252f41818e Update imsg and remove workaround. 2024-11-27 10:30:52 +00:00
nicm
d361f21065 Do not check for latest client in callback since the type may be latest
but with no window, fixes new-session -x and -y with another attached
client. GitHub issue 4268.
2024-11-27 10:12:20 +00:00
nicm
feb090abb3 Do not stop drag on double or triple click, GitHub issue 4272. 2024-11-27 10:10:20 +00:00
Thomas Adam
db771ec6e3 Merge branch 'obsd-master' 2024-11-26 18:01:10 +00:00
nicm
6f7db82b18 Add copy-mode-position-style and copy-mode-selection-style for copy
mode (they default to mode-style as before).
2024-11-26 15:52:41 +00:00
nicm
67cc7f6dc6 Enter is now sent from single prompt as \r not empty string. 2024-11-26 15:51:48 +00:00
Thomas Adam
db978db271 Merge branch 'obsd-master' 2024-11-25 14:01:12 +00:00
nicm
363f35f076 Do not try to terminate an empty buffer. 2024-11-25 12:32:24 +00:00
Thomas Adam
49b7276f2a Merge branch 'obsd-master' 2024-11-25 10:01:11 +00:00
nicm
f57131e11b Use cursor style from global options instead of default for popups, from
Alexander Arch.
2024-11-25 08:36:46 +00:00
nicm
420af9e108 Do not rely on window reference count for linked formats because they
are also used for notifications, GitHub issue 4258.
2024-11-25 08:34:01 +00:00
Thomas Adam
ceaf9b7452 Merge branch 'obsd-master' 2024-11-22 14:01:11 +00:00
nicm
5fd45b3892 Do not strvis output to terminal from commands. 2024-11-22 12:58:05 +00:00
nicm
64d82d5988 Document command prompt escape sequence, from Von Welch. 2024-11-22 12:36:13 +00:00
Nicholas Marriott
0f308bd18f imsg no longer associates file descriptors with the imsg they were sent with,
work around this for the moment (it is not clear if this is intentional).
2024-11-22 09:58:47 +00:00
Nicholas Marriott
1365f1ce52 Add an __unused. 2024-11-22 08:07:21 +00:00
Nicholas Marriott
7fc4e54efe And arpa/inet.h needs to stay... 2024-11-22 08:06:30 +00:00
Nicholas Marriott
c98c08c232 Need to remove endian.h as well. 2024-11-22 08:05:02 +00:00
Nicholas Marriott
990c724bd8 Bring in updated imsg. 2024-11-22 08:03:55 +00:00
Thomas Adam
b82c7b40b0 Merge branch 'obsd-master' 2024-11-21 16:01:11 +00:00
claudio
70299c6646 Convert sbin and usr.bin to check for imsgbuf_init failure and add
imsgbuf_allow_fdpass where needed.

OK tb@
2024-11-21 13:35:20 +00:00
Thomas Adam
ea6c5053c8 Merge branch 'obsd-master' 2024-11-21 10:01:09 +00:00
nicm
3f4fd45d5c Fix save-buffer documentation - it writes to stdout. From Ilya Grigoriev. 2024-11-21 07:37:21 +00:00
nicm
7c7e88253e When the mouse clicked on the border between two horizontal panes, the
location was not being set properly. Pulled the checking of this into
separate function for clarity. Fixes dragging on horizontal pane border.
From Michael Grant.
2024-11-21 07:34:38 +00:00
Thomas Adam
bb94a5437d Merge branch 'obsd-master' 2024-11-21 00:01:13 +00:00
nicm
273f9b2027 Fix word navigation on lines with tabs, from Alexander Arch. 2024-11-20 20:54:02 +00:00
Nicholas Marriott
b6d0b4be30 Define SD_ID128_UUID_FORMAT_STR if missing, from Marc Reisner. 2024-11-19 17:55:12 +00:00
Thomas Adam
2dce45f427 Merge branch 'obsd-master' 2024-11-18 10:01:12 +00:00
nicm
a3ede3106a Check all %if in the list when deciding whether to process an
assignment, not just the most recent.
2024-11-18 08:29:35 +00:00
Thomas Adam
dd9722184b Merge branch 'obsd-master' 2024-11-16 18:01:08 +00:00
nicm
a854e36f2b Do not call layout_fix_panes if wp is NULL (that is, a popup). 2024-11-16 16:49:50 +00:00
Thomas Adam
be594ff8a4 Merge branch 'obsd-master' 2024-11-15 16:01:11 +00:00
nicm
c66628e52b Add no-detach-on-destroy client option (useful for control mode
clients). From laur dot aliste at gmail dot com, GitHub issue 4242.
2024-11-15 14:09:04 +00:00
nicm
350a151ee4 Add two new style parameters, width and pad, which apply to scrollbars.
From Michael Grant, GitHub issue 4241.
2024-11-15 13:12:20 +00:00
Thomas Adam
d35458e3fe Merge branch 'obsd-master' 2024-11-15 10:01:11 +00:00
nicm
d6883c0266 Turn off scrollbar when pane is in alternate screen, from Michael Grant,
GitHub issue 4231.
2024-11-15 09:01:16 +00:00
Thomas Adam
563ed05353 Merge branch 'obsd-master' 2024-11-12 12:01:10 +00:00
nicm
f527412d9b Tidy up loop, from Alexander Arch. 2024-11-12 10:06:35 +00:00
nicm
713cacab1e Mouse support on the scrollbars, from Michael Grant. 2024-11-12 09:32:56 +00:00
Thomas Adam
846f813565 Merge branch 'obsd-master' 2024-11-11 10:01:08 +00:00
nicm
c26d71d3e9 Add an option to control the input buffer size, from Ken Lau. 2024-11-11 08:41:05 +00:00
Thomas Adam
c8677d3272 Merge branch 'obsd-master' 2024-11-08 10:01:08 +00:00
nicm
596ea62dc3 Some fixes for searching for tabs, from Alexander Arch. 2024-11-08 08:51:36 +00:00
Thomas Adam
1fe30bb2e8 Merge branch 'obsd-master' 2024-11-06 20:33:45 +00:00
nicm
809d659e64 Xr to Ic, from jmc. 2024-11-05 21:07:19 +00:00
nicm
09f4e43189 Add support for a scrollbar at the side of each pane. New options
pane-scrollbars turn them on or off, pane-scrollbars-position sets the
position (left or right), and pane-scrollbars-style to set the colours.
Mouse support will come later. From Michael Grant in GitHub issue 4221.
2024-11-05 09:41:17 +00:00
Thomas Adam
679bbdcf8f Merge branch 'obsd-master' 2024-11-04 10:01:09 +00:00
nicm
a0c79aa87b Do not make padding cells extended (there can be a lot of them), from
Alexander Arch .
2024-11-04 08:52:13 +00:00
Nicholas Marriott
46f3846659 Add -Wno-macro-redefined for macOS. 2024-10-31 12:57:40 +00:00
Thomas Adam
bbc3cc558c Merge branch 'obsd-master' 2024-10-28 10:01:08 +00:00
nicm
c8bd42de16 Match tab cells when searching, from Alexander Arch in GitHub issue
4201.
2024-10-28 08:16:51 +00:00
nicm
62e15e905b Treat tabs as a word separator, from Alexander Arch in GitHub issue
4201.
2024-10-28 08:16:06 +00:00
nicm
125a7b9177 Fix issues in the command prompt: set PROMPT_QUOTENEXT after quoting
than before, meaning that accidentally scrolling the mouse wheel doesn't
break quoting; and move the cursor correctly over wide characters. From
Alexander Arch in GitHub issue 4212.
2024-10-28 08:11:59 +00:00
Thomas Adam
895044c52b Merge branch 'obsd-master' 2024-10-25 22:01:10 +01:00
nicm
40c01c2d37 Allow tabs even on terminals without UTF-8, reported by jmc. 2024-10-25 19:36:38 +00:00
Thomas Adam
911d768b71 Merge branch 'obsd-master' 2024-10-25 18:01:11 +01:00
nicm
71a503e40c Allow control characters to be entered at the command prompt prefixed
with with C-v, from  Alexander Arch in GitHub issue 4206.
2024-10-25 15:32:51 +00:00
nicm
487b0ee124 Do not attempt to search for zero length strings, from Alexander Arch in
GitHub issue 4209.
2024-10-25 15:19:15 +00:00
nicm
eaec0a48f4 Do not stop stop at first padding in format_grid_line and handle tabs. 2024-10-25 15:13:10 +00:00
nicm
fdbc6cdea5 Flag tabs if possible in the grid cell so they can be preserved on
copying and capture-pane. From Alexander Arch in GitHub issue 4201.
2024-10-25 15:00:18 +00:00
Thomas Adam
9623ec3ee4 Merge branch 'obsd-master' 2024-10-25 10:01:07 +01:00
nicm
63582c154c Add a helper function for cell data comparison, from Alexander Arch. 2024-10-25 07:57:49 +00:00
Thomas Adam
914815e70f Merge branch 'obsd-master' 2024-10-21 16:01:07 +01:00
nicm
354926a956 Bump up the maximum for repeat-time (some people want it to be
effectively infinite).
2024-10-21 12:42:06 +00:00
nicm
df303770ea Add function to get current offset from copy mode, from Michael Grant. 2024-10-21 12:39:49 +00:00
Thomas Adam
a269c33a54 Merge branch 'obsd-master' 2024-10-21 10:01:08 +01:00
nicm
eb04f9314b Adjust how Ctrl and Meta keys are sent to use standard representation if
available in mode 1, from Stanislav Kljuhhin, GitHub issue 4188.
2024-10-21 07:38:06 +00:00
Thomas Adam
a9d0461839 Merge branch 'obsd-master' 2024-10-17 20:01:07 +01:00
nicm
71aa4fe767 Switch pane-colors and cursor-style options to be in alphabetical order,
from Teubel Gyorgy, GitHub issue 4191.
2024-10-17 17:22:01 +00:00
nicm
cfd3c02306 Allow attributes in menu style, from Japin Li in GitHub issue 4194. 2024-10-17 17:10:41 +00:00
Thomas Adam
35104059ed Merge branch 'obsd-master' 2024-10-15 00:01:10 +01:00
nicm
934035db71 Set ACS flag for REP. Reported by Romain Francoise, GitHub issue 4182. 2024-10-14 20:26:45 +00:00
Nicholas Marriott
8ff65230e1 Optimize sixel_print. Previously, the algorithm scanned each pixel several
times; once to find out which colors are active, and then once for every single
active color to actually construct the output string.

Now it constructs the compressed sixel patterns in the first pass (now x * 12
iters), so we can reduce the second pass (the really expensive part, at active
colors * x * 6 iters) to just appending these to the output buffer.

From nincsnevem662 at gmail dot com in GitHub issue 4184.
2024-10-14 11:05:59 +01:00
Thomas Adam
cb00e869ea Merge branch 'obsd-master' 2024-10-12 12:01:09 +01:00
nicm
f8b56fdc3f Call realpath on the source file to match -f on the command line, GitHub
issue 4180.
2024-10-12 08:20:32 +00:00
nicm
2d8b6fcf23 Do not rename a buffer to itself, GitHub issue 4181. 2024-10-12 08:13:52 +00:00
Nicholas Marriott
6ddee22f81 Portable tmux needs to check ENABLE_SIXEL. 2024-10-10 12:07:29 +01:00
Thomas Adam
66c738bc10 Merge branch 'obsd-master' 2024-10-10 12:01:08 +01:00
nicm
bcc47add38 Add a sixel_support format variable which is 1 if SIXEL is supported
(always 0 on OpenBSD), requested by Misaki Masa in GitHub issue 4177
2024-10-10 10:41:33 +00:00
Thomas Adam
d326c51d79 Merge branch 'obsd-master' 2024-10-08 12:01:09 +01:00
nicm
940fdf7ed0 Draw pane status line on the correct line when at the bottom, fixes
issues if the window size is smaller than the entire terminal. GitHub
issue 3943.
2024-10-08 09:40:50 +00:00
Thomas Adam
4f5d6d97d3 Merge branch 'obsd-master' 2024-10-08 10:01:08 +01:00
nicm
00412b570b Put the prompt cursor attributes into the status line screen and use
that rather than updating the current screen (since there might not be
one).
2024-10-08 06:29:44 +00:00
Thomas Adam
6a35b8ad07 Merge branch 'obsd-master' 2024-10-07 16:01:08 +01:00
nicm
735082b7c2 Add prompt-cursor-colour and prompt-cursor-style to set the style of the
cursor in the command prompt and remove the emulated cursor, from
Alexander Arch in GitHub issue 4170.
2024-10-07 12:58:36 +00:00
Thomas Adam
5b7bdc82c8 Merge branch 'obsd-master' 2024-10-07 12:01:08 +01:00
nicm
a3dea81b49 Add initial-repeat-time option to allow the first repeat time to be
increased and later reduced, from David le Blanc in GitHub issue 4164.
2024-10-07 08:50:47 +00:00
Thomas Adam
486221f206 Merge branch 'obsd-master' 2024-10-06 12:01:07 +01:00
nicm
9528d7470b Try to stay near the same line in modes if the current one is removed. 2024-10-06 09:30:22 +00:00
Thomas Adam
933d6b90ed Merge branch 'obsd-master' 2024-10-05 16:01:07 +01:00
nicm
a869693405 Send focus events to pane when entering or leaving popup, GitHub issue
3991.
2024-10-05 12:10:16 +00:00
Thomas Adam
114977dd25 Merge branch 'obsd-master' 2024-10-05 04:01:08 +01:00
Thomas Adam
aba8ffbe7b Merge branch 'obsd-master' 2024-10-05 02:01:07 +01:00
nicm
455a2b3705 Remove some debugging left behind. 2024-10-05 00:35:35 +00:00
nicm
e0638c48cd Add copy-mode-position-format to configure the position indicator. 2024-10-05 00:32:55 +00:00
nicm
41f6b691e3 Document missing window-layout-changed hook. 2024-10-04 22:36:11 +00:00
Nicholas Marriott
0f30fa34e3 Add install step. 2024-10-04 23:29:36 +01:00
Thomas Adam
e486f5ffdc Merge branch 'obsd-master' 2024-10-04 22:01:09 +01:00
nicm
5a68730e2f Add -y flag to disable confirmation prompts in modes, GitHub issue 4152. 2024-10-04 19:16:13 +00:00
Thomas Adam
16b44d2a5b Merge branch 'obsd-master' 2024-10-04 18:01:07 +01:00
nicm
34775fc235 Do not translate BSpace as Unicode, GitHub issue 4156. 2024-10-04 14:55:17 +00:00
Thomas Adam
f2fd8c854a Merge branch 'obsd-master' 2024-10-04 10:01:08 +01:00
nicm
9f2a853d87 Rework of copy mode commands ("send-keys -X") to parse the arguments so
that flags may be detected propertly rather than just looking for
strings ("-O" and so on). Also add -C and -P flags to the copy commands:
-C prevents the commands from sending the text to the clipboard and -P
prevents them from adding the text as a paste buffer.

Note some of the default key bindings change to add "--" and any similar
custom key bindings using "send-keys -X" may need a similar change.

GitHub issue 4153.
2024-10-04 07:03:08 +00:00
Nicholas Marriott
7a78cdf78c Three SIXEL improvements from nincsnevem662 at gmail dot com in GitHub issue
4149:

- Pass P2 on the received image through when forwarding the image to the
  outside terminal to preserve transparency;

- Preserve raster attribute dimensions which may be smaller than the actual
  image (used to crop out parts of the image to work around the limitation that
  rows must come in groups of six);

- To avoid collapsing empty sixel lines, no longer ignore duplicate "-" new
  line specifiers.
2024-10-04 07:49:07 +01:00
Thomas Adam
2df15ad08c Merge branch 'obsd-master' 2024-10-03 08:01:07 +01:00
nicm
780a87be9a Improve fix for shifted keys so it works for all the keys it should,
Stanislav Kljuhhin in GitHub issue 4146.
2024-10-03 05:41:59 +00:00
Nicholas Marriott
1e303b6a9d Bypass permission check for Cygwin, based on a different change by Yuya Adachi
via Rafael Kitover; GitHub issue 4148.
2024-10-02 14:04:06 +01:00
Thomas Adam
8d2aee34ab Merge branch 'obsd-master' 2024-10-02 14:01:10 +01:00
nicm
05116cefe6 Add MSYSTEM to default update-environment. 2024-10-02 11:51:15 +00:00
nicm
ce4be58eef Add a define for the socket permissions check so it can be overridden
more easily (for Cgywin).
2024-10-02 11:48:16 +00:00
Nicholas Marriott
8d6eb4be40 Set client stdout file descriptor also for Cgywin, from Michael Wild via Rafael
Kitover in GitHub issue 4148.
2024-10-02 12:16:24 +01:00
Thomas Adam
38c38f1c64 Merge branch 'obsd-master' 2024-10-02 10:01:08 +01:00
nicm
ddf6af79e3 Report shifted keys like S-A as A not as S-A in mode 1 extended keys,
from Stanislav Kljuhhin.
2024-10-02 08:06:45 +00:00
Thomas Adam
826ba515be Merge branch 'obsd-master' 2024-10-01 14:01:08 +01:00
Nicholas Marriott
729bded8bf Fix up regression tests. 2024-10-01 13:40:42 +01:00
nicm
b95a06d2d4 Add a way to make the preview larger in tree mode, GitHub issue 4124. 2024-10-01 10:10:29 +00:00
Thomas Adam
157d748949 Merge branch 'obsd-master' 2024-10-01 10:01:10 +01:00
nicm
1c1f4c1219 Use global cursor style and colour options for modes instead of default,
GitHub issue 4117.
2024-10-01 08:01:19 +00:00
nicm
17bab32794 Change pasting to bypass the output key processing entirely and write
what was originally received. Fixes problems with pasted text being
interpreted as extended keys reported by Mark Kelly.
2024-10-01 06:15:47 +00:00
Thomas Adam
25b1cc1e8e Merge branch 'obsd-master' 2024-09-30 12:01:11 +01:00
nicm
89adec0ca5 On some Windows terminals, if TIOCWINSZ does not return xpixel and
ypixel (they are zero), if this is the case then try the query escape
sequences. From Dmitry Galchinsky in GitHub issue 4099.
2024-09-30 08:10:20 +00:00
nicm
f95d055e04 Only use default-shell for popups, return to /bin/sh for run-shell,
if-shell and #() - these have been documented as using /bin/sh for a
long time and scripts rely on it. Pointed out by Gregory Pakosz.
2024-09-30 07:54:51 +00:00
Thomas Adam
7b148f7b5b Merge branch 'obsd-master' 2024-09-29 22:01:09 +01:00
nicm
9bd039d1bc Fix grey colour, from Magnus Gross. 2024-09-29 20:05:42 +00:00
Nicholas Marriott
7c30056d96 Bump to 3.6. 2024-09-27 08:58:00 +01:00
Nicholas Marriott
ac44566c9c tmux 3.5. 2024-09-27 08:56:39 +01:00
Thomas Adam
64f1076d97 Merge branch 'obsd-master' 2024-09-17 00:01:09 +01:00
nicm
489c69f5ed Add copy mode commands which were missed when descriptions were added,
from Julian Prein, GitHub issue 4121.
2024-09-16 20:46:58 +00:00
nicm
f897049935 Change the behaviour of extended-keys always slightly so that
applications can still enter mode 2 if they want, they just cannot turn
extended keys off entirely. From Stanislav Kljuhhin.
2024-09-16 20:38:48 +00:00
nicm
d8b66110f7 Add a prefix timeout option, from Conor Taylor in GitHub issue 4108. 2024-09-16 20:28:22 +00:00
Nicholas Marriott
8b1a3bb2e5 Only default --enable-debug if the user has not turned it off, from Ken Lau. 2024-09-16 21:19:43 +01:00
Thomas Adam
37771a5a8d Merge branch 'obsd-master' 2024-09-11 22:01:10 +01:00
nicm
3a8a31d8d2 Mouse move keys are not useful as key bindings because we do not turn
them on unless the application requests them. Ignore them so they do not
cause the prefix to be canceled, GitHub issue 4111.
2024-09-11 19:12:33 +00:00
Nicholas Marriott
c36ffcbe56 Typo from Bastian Venthur. 2024-09-06 13:09:43 +01:00
Thomas Adam
3d8ead8a97 Merge branch 'obsd-master' 2024-08-28 10:27:21 +01:00
nicm
141cd78407 Display hyperlinks in copy mode and add copy_cursor_hyperlink format to
get the hyperlink under the cursor.
2024-08-27 07:49:07 +00:00
nicm
d0c8124661 Add search_count and search_count_partial formats in copy mode, GitHub
issue 4091.
2024-08-27 07:31:26 +00:00
nicm
2917bc5274 Do not reset mouse pane if clicked on status line, it may have been set
by a range.
2024-08-27 07:25:27 +00:00
nicm
2d1e93447e Use strtonum instead of atoi. 2024-08-26 13:02:15 +00:00
nicm
31b6c9356c C-h should not be treated specially and represented internally as \b but
as C-h like the other Ctrl keys. Backspace is already handled separately
if it VERASE.
2024-08-26 07:45:05 +00:00
nicm
9e2a7c28f5 Pass the screen_redraw_ctx struct into more functions instead of
individual arguments (for example for the pane status), from Michael
Grant.
2024-08-26 07:34:40 +00:00
nicm
73b2277af8 Client flags was changed to uint64_t a while ago, fix a few cases where
it is still int (do not matter now but will with some new flags). From
Michael Grant.
2024-08-26 07:30:46 +00:00
nicm
a84c109604 Add window_pane_mode helper function to tell if a pane is in copy mode,
from Michael Grant.
2024-08-26 07:14:40 +00:00
nicm
4823acca8f Add copy-mode -d flag to scroll a page down if in copy mode already,
from Michael Grant.
2024-08-26 07:09:34 +00:00
Thomas Adam
34807388b0 Merge branch 'obsd-master' 2024-08-23 16:01:10 +01:00
nicm
08be883297 Ignore internal function keys if they have not got an entry in the key
table.
2024-08-23 13:25:39 +00:00
Thomas Adam
13bd0e46db Merge branch 'obsd-master' 2024-08-23 14:01:08 +01:00
nicm
6e9a914014 Check for exact match for layout name before looking for a prefix match. 2024-08-23 10:19:06 +00:00
Thomas Adam
7990e5fa8f Merge branch 'obsd-master' 2024-08-22 12:01:07 +01:00
nicm
4860a58d07 Clear overlay when command prompt is entered. Also fix some spacing in
man page pointed out by jmc.
2024-08-22 09:05:51 +00:00
Thomas Adam
99af9f23bd Merge branch 'obsd-master' 2024-08-22 08:01:08 +01:00
nicm
9ebbe2cca7 Short Ctrl keys like ^A need to be converted to lowercase so they end up
as 'a'|KEYC_CTRL to match the new internal representation. Problem
reported by naddy@.
2024-08-22 05:39:55 +00:00
Thomas Adam
692bae9ea6 Merge branch 'obsd-master' 2024-08-21 08:01:09 +01:00
nicm
a6645c4de4 Mention that load- and save-buffer can use stdin, from Ramon Fischer. 2024-08-21 05:06:45 +00:00
nicm
06292baadc Add mirrored versions of the main-horizontal and main-vertical layouts where
the main pane is bottom or right instead of top or left, from Sherwyn Sen.
2024-08-21 05:03:13 +00:00
nicm
ceda0a68ae C-Space and Meta keys should not be translated in mode 1 extended keys. 2024-08-21 04:55:57 +00:00
nicm
4fa90c9acf Set the default for extended-keys back to off because it appears emacs turns
the keys on but does not correctly handle them except in xterm (!). Also fix so
that off takes effect as expected.
2024-08-21 04:37:42 +00:00
nicm
c7e61a01e5 Revamp extended keys support to more closely match xterm and support
mode 2 as well as mode 1. From Stanislav Kljuhhin (GitHub issue 4038).

This changes tmux to always request mode 2 from parent terminal, change
to an unambiguous internal representation of keys, and adds an option
(extended-keys-format) to control the format similar to the xterm(1)
formatOtherKeys resource.
2024-08-21 04:17:09 +00:00
Thomas Adam
963e824f5f Merge branch 'obsd-master' 2024-08-19 12:01:09 +01:00
nicm
de6bce057a Allow REP to work with Unicode characters, GitHub issue 3687. 2024-08-19 08:31:36 +00:00
nicm
937ba1d8dd Both terminators \007 and \033\\ leave the index pointing to the final
character of the terminator, so correct the size calculation to always
add one. GitHub issue 4082.
2024-08-19 08:29:16 +00:00
Thomas Adam
651891c3bd Merge branch 'obsd-master' 2024-08-04 12:01:09 +01:00
nicm
4008e2ff6d Make a little effort to treate CRLF as LF in config files. GitHub issue
3720.
2024-08-04 09:42:23 +00:00
nicm
b88130d24b The Linux console has some bugs with bright colours. It seems likely
that it is emulating them by setting a bright (or bold) flag; however,
when the colour is changed from a bright colour (say SGR 96) to a
non-bright (say SGR 36), the flag is not reset, so the new colour
remains as bright. SGR 39 (default colour) also does not reset, so you
end up with the bright default colour. Work around this by sending SGR 0
when switching away from a bright colour, and disable AX for TERM=linux.
Also make the check for AX simpler and do not check for the op
capability is not actually used. GitHub issue 3976.
2024-08-04 09:35:30 +00:00
nicm
fc7ee7efc7 -l should be before -r, pointed out by jmc a while ago. 2024-08-04 09:01:18 +00:00
Nicholas Marriott
d0eb3fe543 Use terminal-features instead of terminal-overrides to enable truecolor support
in example_tmux.conf, from Simon Hengel.
2024-08-04 09:59:18 +01:00
Nicholas Marriott
2ac0faf119 If built with systemd, remove some environment variables it uses. From Ciprian
Dorin Craciun, GitHub issue 4035.
2024-08-04 09:58:13 +01:00
Nicholas Marriott
775789fbd5 Rename header guards on compat/queue.h to avoid it conflicting if the system
header is included first. This matters on some platforms (macOS) where queue.h
is old. From Saagar Jha in GitHub issue 4041.
2024-08-04 09:57:26 +01:00
nicm
7b6fbe7262 Adjust the logic when deleting last buffer to better preserve the
selection: if selecting the element below the deleted one fails (because
as the last one), select the one above it instead. From Daniel Mueller,
GitHub issue 4043.
2024-08-04 08:53:43 +00:00
Thomas Adam
109d2bda1a Merge branch 'obsd-master' 2024-07-22 18:01:09 +01:00
nicm
ddd4e57c65 Expand full array option values if no index is provided, GitHub issue
4051.
2024-07-22 15:27:42 +00:00
Nicholas Marriott
3c2621b41b Support building with jemalloc memory allocator, from Romain Francoise. 2024-07-15 11:25:15 +01:00
Nicholas Marriott
5039be657c utf8proc bits for utf8_fromwc. 2024-07-12 14:30:56 +01:00
Thomas Adam
d02254f754 Merge branch 'obsd-master' 2024-07-12 14:01:09 +01:00
nicm
aa1353947e UTF-8 keys now contain the internal representation and not the Unicode
codepoint, so convert extended keys properly. From Stanislav Kljuhhin.
2024-07-12 11:21:18 +00:00
Nicholas Marriott
171004dfd0 Use mdoc on Illumos which uses mandoc, from Andy Fiddaman. 2024-07-08 10:57:32 +01:00
Thomas Adam
c773fe89e7 Merge branch 'obsd-master' 2024-06-24 12:01:10 +01:00
nicm
093b5a5518 Add a way (refresh-client -r) for control mode clients to provide OSC 10
and 11 responses to tmux so they can set the default foreground and
background colours, from George Nachman in GitHub issue 4014.
2024-06-24 08:30:50 +00:00
nicm
db1665868f Check the underline style colour against the correct default value again
(it was changed from 0 to 8), from Romain Francoise.
2024-06-24 08:11:46 +00:00
Thomas Adam
c07e856d24 Merge branch 'obsd-master' 2024-05-24 16:01:08 +01:00
nicm
692ce59bce Do not escape $ unless DQ is set, that is the only case where we need to
escape it.
2024-05-24 12:41:24 +00:00
nicm
9e7c1aee48 Add N to search backwards in tree modes, from Fadi Afani in GitHub issue
3982.
2024-05-24 12:39:06 +00:00
Thomas Adam
4c2eedca5a Merge branch 'obsd-master' 2024-05-19 06:01:09 +01:00
jsg
ac6c1e9589 remove prototype with no matching function 2024-05-19 03:27:58 +00:00
Thomas Adam
0903790b00 Merge branch 'obsd-master' 2024-05-18 12:01:09 +01:00
jsg
03de52653e remove prototypes with no matching function; ok nicm@ 2024-05-18 08:51:26 +00:00
jsg
da06719309 remove externs with no matching var; ok nicm@ 2024-05-18 08:50:11 +00:00
Thomas Adam
fc84097379 Merge branch 'obsd-master' 2024-05-15 14:01:09 +01:00
Thomas Adam
4fd725c6e1 Merge branch 'obsd-master' 2024-05-15 12:01:10 +01:00
nicm
d39dcea30a Use default-shell for command prompt #() and popups as well 2024-05-15 09:59:12 +00:00
nicm
bfd65398a9 Fix memory leaks reported by Lu Ming Yin. 2024-05-15 08:39:30 +00:00
Thomas Adam
452d987e0e Merge branch 'obsd-master' 2024-05-14 14:01:10 +01:00
Thomas Adam
8ef899f315 Merge branch 'obsd-master' 2024-05-14 12:01:09 +01:00
nicm
a18d1146aa Add missing time.h to tty.c (from Ismail Donmez), also remove some stray
spaces.
2024-05-14 10:11:09 +00:00
nicm
5b5004e5ac Revert part of the change for GitHub issue 3675 because it does not work
correctly, it was intended to skip lines that are already being searched
as part of a previous wrapped line but in fact is skipping all lines
except the last in wrapped lines.

Also revert the search-wrapped-lines option (I didn't realize it was
intended to work around this).
2024-05-14 09:32:37 +00:00
Thomas Adam
6ff8f8fbf9 Merge branch 'obsd-master' 2024-05-14 10:01:10 +01:00
nicm
c9616700ca Add a command-error hook when a command fails, from Hugh Davenport in
GitHub issue 3973.
2024-05-14 07:52:19 +00:00
nicm
4c928dce74 Add an option to disable unwrapping lines for searching, from
meanderingprogrammer at gmail dot com, GitHub issue 3975.
2024-05-14 07:40:39 +00:00
nicm
fb37d52dde Restore previous behaviour or writing to stdout if available. 2024-05-14 07:33:01 +00:00
Thomas Adam
363d9c401e Merge branch 'obsd-master' 2024-05-13 14:01:10 +01:00
nicm
8643ece345 Fix memory leak, from Fadi Afani. 2024-05-13 11:45:05 +00:00
Nicholas Marriott
9ba433e521 Use printf not echo -e, from Joyce Lin. 2024-05-13 12:42:14 +01:00
Nicholas Marriott
3823fa2c57 Send SIGHUP since some programs ignore SIGTERM, from Eduardo Grajeda in GitHub
issue 3958.
2024-04-30 12:38:58 +01:00
Thomas Adam
0a8571b6fe Merge branch 'obsd-master' 2024-04-23 16:09:50 +01:00
jsg
ea9f416c99 correct indentation; no functional change
ok tb@
2024-04-23 13:34:51 +00:00
Thomas Adam
036d8993e6 Merge branch 'obsd-master' 2024-04-15 12:01:11 +01:00
nicm
e8530c9fee Fixes for memory leaks reported by Lu Ming Yin, fixes from Howard Chu. 2024-04-15 08:19:55 +00:00
Nicholas Marriott
dd4c0109a9 Missing headers for Android, from Biswapriyo Nath. 2024-04-15 09:07:41 +01:00
Thomas Adam
43530d4397 Merge branch 'obsd-master' 2024-04-10 10:01:13 +01:00
nicm
553d4cba79 Add an option allow-set-title to forbid applications from changing the
pane title, from someone in GitHub issue 3930.
2024-04-10 07:36:25 +00:00
nicm
c62a9ca16b Correct handling of mouse up events (don't ignore all but the last
released button), and always process down event for double click. From
Rudy Dellomas III in GitHub issue 3919.
2024-04-10 07:29:15 +00:00
nicm
424f13fe13 Do not get muddled and crash if focusing a pane that is exiting,
reported by Saul Nogueras in GitHub issue 3776.
2024-04-10 07:15:21 +00:00
Thomas Adam
4bb6da75ba Merge branch 'obsd-master' 2024-04-05 02:01:09 +01:00
nicm
a28175dbfd Pick newest session as documented, not oldest, from Magnus Gross. 2024-04-04 22:44:40 +00:00
Thomas Adam
fc204bb5e5 Merge branch 'obsd-master' 2024-03-26 12:01:11 +00:00
nicm
6207a45139 Fix selection present check, reported by M Kelly. 2024-03-26 10:20:20 +00:00
Thomas Adam
3c3643f580 Merge branch 'obsd-master' 2024-03-21 14:01:10 +00:00
nicm
89c1c43ef9 Write padding character into the right position. 2024-03-21 12:10:57 +00:00
nicm
2e9d7ebf15 Reduce escape-time default to 10 milliseconds, 500 is far too long for
modern terminals and networks. Case made by Kurtis Rader in GitHub issue
3844.
2024-03-21 11:53:11 +00:00
nicm
d8ddeec7db Add -M to always turn mouse on in a menu, GitHub issue 3779. 2024-03-21 11:51:32 +00:00
nicm
6f0254e6a8 Look for feature code 21 for DECSLRM and 28 for DECFRA in the device
attributes and also accept level 1 (there is no hardware with this but
some emulators may use it). Pointed out by James Holderness.
2024-03-21 11:47:55 +00:00
Nicholas Marriott
aa17f0e0c1 Fix crash if SIXEL colour register is invalid and remove SIXEL images before
reflow to avoid a different crash, from Anindya Mukherjee.
2024-03-21 11:37:09 +00:00
nicm
0ae8b681b2 Use -p for default paste-buffer command in buffer mode, it will only do
anything if the application asked for it. From Gregory Anders.
2024-03-21 11:32:49 +00:00
nicm
6c0067c103 Do not notify window-layout-changed if the window is about to be
destroyed (since it may have been freed by the time the notify happens),
from Romain Francoise in GitHub issue 3860.
2024-03-21 11:30:42 +00:00
nicm
5458cb2850 Revert detach-client part of last, did not intend this to go in. 2024-03-21 11:27:18 +00:00
nicm
0c374868ca Do not consider a selection present if it is empty, from Michael Grant
(GitHub issue 3869). Also a typo fix from GitHub issue 3877.
2024-03-21 11:26:28 +00:00
Nicholas Marriott
bf5d3f2e26 Typo, GitHub issue 3877. 2024-03-21 11:19:59 +00:00
Nicholas Marriott
d5ef837f63 Remove duplicate .tmux.conf mention, from Valentin Rylenko. 2024-03-21 11:18:49 +00:00
Thomas Adam
b79e28b2c3 Merge branch 'obsd-master' 2024-03-13 14:01:09 +00:00
nicm
8ffd5458ff Make the attach-session description clearer - do not mention creating a
client which is not important, explicitly say the session must exist,
and mention new-session and new-session -A. Prompted by Theo.
2024-03-13 11:25:50 +00:00
Thomas Adam
b54e1fc4f7 Merge branch 'obsd-master' 2024-03-07 00:01:10 +00:00
Nicholas Marriott
bdb6321229 Update lock.yml. 2024-03-06 21:45:26 +00:00
nicm
bd29a48b56 Check for the right flag to fix split-window -p, from Bryan Childs. 2024-03-06 21:32:39 +00:00
Nicholas Marriott
f3f1c3db58 Add missing headers, from Marvin Schmidt. 2024-03-06 21:29:28 +00:00
Nicholas Marriott
608d113486 next-3.5 2024-02-13 10:20:18 +00:00
Nicholas Marriott
9ae69c3795 3.4. 2024-02-13 10:17:07 +00:00
Thomas Adam
0960862950 Merge branch 'obsd-master' 2024-02-13 10:01:10 +00:00
Nicholas Marriott
44ad25b367 Update CHANGES. 2024-02-13 09:12:08 +00:00
nicm
40b97b1715 Add two new values for the destroy-unattached option to destroy sessions
only if they are not members of sessions groups, from Mark Huang, GitHub
issue 3806.
2024-02-13 08:10:23 +00:00
nicm
4bdb855020 Do not allow paste into panes which have exited, from Romain Francoise
in GitHub issue 3830.
2024-02-13 08:03:50 +00:00
Thomas Adam
ea7136fb83 Merge branch 'obsd-master' 2024-01-22 18:01:09 +00:00
nicm
428f8a9b28 Increase buffer size to avoid truncating styles, GitHub issue 3809 from
Ricardo Bittencourt.
2024-01-22 16:34:46 +00:00
Nicholas Marriott
84faada25b Remove existing defines. 2024-01-17 10:59:07 +00:00
Nicholas Marriott
55d0abad89 Need htonll and ntohll. 2024-01-17 10:57:32 +00:00
Nicholas Marriott
7d91b4b90b htobe is not portable. 2024-01-17 09:47:35 +00:00
Nicholas Marriott
66369416fc Update imsg. 2024-01-17 09:41:53 +00:00
Thomas Adam
001e26d0bb Merge branch 'obsd-master' 2024-01-16 14:01:09 +00:00
claudio
2e39d698d2 Use imsg_get_fd() instead of direct access to imsg.fd
The change in proc.c can be further simplified once imsg_free() takes
care of unclaimed file descriptors.

OK nicm@
2024-01-16 13:09:11 +00:00
Thomas Adam
e809c2ec35 Merge branch 'obsd-master' 2024-01-03 19:17:10 +00:00
Nicholas Marriott
4266d3efc8 Assignment should be inside SIXEL. 2023-12-28 03:12:27 +00:00
nicm
40a20bc8ae Only wrap pattern in *s if using a regular expression. 2023-12-27 20:42:01 +00:00
nicm
f7bf7e9671 Remove flags from the prefix before comparing with the received key so
that modifier keys with flags work correctly, GitHub issue 3764.
2023-12-27 20:23:59 +00:00
nicm
008ecd4592 groff apparently generates broken output for some common characters in
mdoc, so escaped versions have to be used instead. From Alexis
Hildebrandt in GitHub issue 3762.
2023-12-27 20:20:50 +00:00
nicm
73a2b12088 Always initialize screen mode, GitHub issue 3750 from Ding Fei. 2023-12-27 20:17:13 +00:00
Nicholas Marriott
605bf21ff2 Do not use NULL window, GitHub issue 3747. 2023-12-27 20:15:57 +00:00
nicm
f028445407 Correctly handle window ops with no pane, GitHub issue 3747. 2023-12-27 20:13:35 +00:00
Thomas Adam
bdf8e614af Merge branch 'obsd-master' 2023-11-14 22:01:09 +00:00
nicm
4dea352dee Don't strdup NULL filename. 2023-11-14 20:01:11 +00:00
Thomas Adam
151875d144 Merge branch 'obsd-master' 2023-11-14 18:01:10 +00:00
nicm
88fd1f00b8 Handle NULL client (in config file) when showing a status message; also
copy the file when processing if-shell since it may be freed. GitHub
issue 3746.
2023-11-14 15:59:49 +00:00
nicm
1a14d6d2e1 Use SM 2026 for Sync which is more widely supported now. 2023-11-14 15:38:33 +00:00
Thomas Adam
381c00a74e Merge branch 'obsd-master' 2023-11-02 22:58:45 +00:00
nicm
5aadee6df4 next-prompt can have 1 argument. 2023-11-02 10:38:14 +00:00
Nicholas Marriott
a5545dbc9f Allow attributes to have only two parameters, from Tim Culverhouse. 2023-11-01 10:37:41 +00:00
nicm
fdf465925e Do not allow combined UTF-8 characters that are too long, GitHub issue
3729.
2023-10-30 16:05:30 +00:00
nicm
36e1ac6556 Unzoom window at start of destroy so it doesn't happen later (when
destroying panes) after the layout has been freed, GitHub issue 3717.
2023-10-23 08:12:00 +00:00
nicm
ffa376edf7 Switch to tiparm_s (added in ncurses 6.4-20230424) instead of tparm,
which allows ncurses to validate the capabilities correctly.
2023-10-17 09:55:32 +00:00
Thomas Adam
b777780720 Merge branch 'obsd-master' 2023-09-19 12:01:11 +01:00
nicm
347cd0b5f8 Fix a couple of mouse mode flag names. 2023-09-19 08:35:44 +00:00
Nicholas Marriott
0ca28b362e Add combined character test. 2023-09-19 09:29:20 +01:00
Nicholas Marriott
51b80b985e Restore utf8proc code. 2023-09-19 09:29:04 +01:00
Nicholas Marriott
789a98982e Reply to SMGRAPHICS. 2023-09-19 09:27:59 +01:00
Thomas Adam
b202a2f1b5 Merge branch 'obsd-master' 2023-09-17 21:03:06 +01:00
nicm
7e79108f8a Remove next- and previous-prompt added in error. GitHub issue 3696. 2023-09-16 16:18:29 +00:00
nicm
f09cde2542 Change UTF-8 combining to inspect the previous character at the cursor
position rather than keeping the last character from the input stream,
this is how most terminals work and fixes problems with displaying these
characters in vim. GitHub issue 3600.
2023-09-15 15:49:05 +00:00
Thomas Adam
9f9156c030 Merge branch 'obsd-master' 2023-09-15 10:01:11 +01:00
nicm
d394293ba5 Add -t to source-file, GitHub issue 3473. 2023-09-15 06:31:49 +00:00
Thomas Adam
c57a09269b Merge branch 'obsd-master' 2023-09-14 16:01:10 +01:00
nicm
8191c58737 Reset combine flag only if text is actually processed. 2023-09-14 13:01:35 +00:00
Thomas Adam
f68d35c529 Merge branch 'obsd-master' 2023-09-08 10:01:10 +01:00
nicm
c02bc4dbe9 On second thoughts, do check DA2 for DECFRA and DECSLRM since that will
catch terminals that say they are VT520 even if we can't use DA1
(because of VTE).
2023-09-08 07:05:06 +00:00
nicm
4872811ba7 Use DECSLRM and DECFRA only at level 4 rather than checking the terminal
id.
2023-09-08 06:52:31 +00:00
Nicholas Marriott
1a1290f30b Only remove images if reverse index actually scrolls. 2023-09-07 22:02:11 +01:00
Nicholas Marriott
7be7ca7195 Shut autoconf up. 2023-09-07 18:24:28 +01:00
Thomas Adam
32197fa52d Merge branch 'obsd-master' 2023-09-07 14:01:11 +01:00
nicm
9653a52a6a Use DECSLRM and DECFRA on terminals pretending to be VT520 or VT525 as
well as VT420.
2023-09-07 10:21:46 +00:00
Thomas Adam
d60c8942ce Merge branch 'obsd-master' 2023-09-07 10:01:10 +01:00
nicm
c99f9e92e0 Accept 65 for VT525 as well. 2023-09-07 07:19:21 +00:00
Thomas Adam
e26356607e Merge branch 'obsd-master' 2023-09-04 10:01:10 +01:00
nicm
43e5e80343 Skip wrapped lines in top level search loop because they will be
combined in the inner loop (in window_copy_search_rl_regex and the
others), avoids searching the same text multiple times. Also add a line
length limit for regex searches. GitHub issue 3675.
2023-09-04 08:01:43 +00:00
Thomas Adam
1742138f05 Merge branch 'obsd-master' 2023-09-02 22:01:09 +01:00
nicm
c767d62329 Request terminal colours again on SIGWINCH but at most once every 30
seconds, GitHub issue 3582.
2023-09-02 20:03:10 +00:00
Thomas Adam
7ad29b9831 Merge branch 'obsd-master' 2023-09-02 12:01:09 +01:00
nicm
d209fe9b1e Setulc only does RGB colour so add Setulc1 to do non-RGB colours, GitHub
issue 3627.
2023-09-02 09:17:23 +00:00
nicm
c5542637d7 Set visited flag on last windows when linking session. 2023-09-02 08:38:37 +00:00
Thomas Adam
e7c829fc67 Merge branch 'obsd-master' 2023-09-01 20:01:10 +01:00
nicm
579829eef2 Only compare the actual size of the UTF-8 character, not all of it. 2023-09-01 18:43:54 +00:00
Nicholas Marriott
3aa20f6e75 Use %05X not %08X. 2023-09-01 19:37:27 +01:00
nicm
f78279bb2e Add missing -T to getopt string. 2023-09-01 16:40:38 +00:00
Thomas Adam
a99d7c6314 makefile: fixup bad merge 2023-09-01 17:13:55 +01:00
Thomas Adam
cf1ed67fcc Merge branch 'obsd-master' 2023-09-01 17:09:41 +01:00
Thomas Adam
1aec420465 Merge branch 'obsd-master' 2023-09-01 17:06:27 +01:00
nicm
16e4b39359 Clear combine flag when a non-UTF-8 set of characters is encountered. 2023-09-01 16:01:54 +00:00
nicm
9456258ccc Rewrite combined character handling to be more consistent and to support
newer Unicode combined characters (which we have to "know" are combined
since they are not width zero). GitHub issue 3600.
2023-09-01 14:29:11 +00:00
nicm
c41d59f232 Expand name before looking for window with -S, GitHub issue 3670. 2023-09-01 14:24:46 +00:00
Nicholas Marriott
d682ef88e6 Bump width and height to 10000. 2023-09-01 14:54:27 +01:00
nicm
c1e6e54e6e Add detach-on-destroy previous and next, mostly from Alexis Hildebrandt. 2023-09-01 13:48:54 +00:00
Nicholas Marriott
62f657845e Fix merge error, from Jakub Łukasiewicz. 2023-08-26 20:57:44 +01:00
Thomas Adam
a9841a6d1e portable: fixup merge 2023-08-23 20:55:23 +01:00
Thomas Adam
70ecf17f85 Merge branch 'obsd-master' 2023-08-23 20:37:42 +01:00
nicm
71d453f169 Add -c to run-shell to set working directory, from someone in GitHub
issue 3661.
2023-08-23 08:40:25 +00:00
Nicholas Marriott
071849f82f Improve logging of SIXEL parsing errors. 2023-08-23 09:30:20 +01:00
nicm
52084b2948 Log what input_dcs_dispatch does with the input data. 2023-08-23 08:30:07 +00:00
Nicholas Marriott
dfbc6b1888 Merge SIXEL branch.
Squashed commit of the following:

commit 6ebc3feb4671d9b25b3db99d3c16b2323b8e3d02
Author: topcat001 <anindya49@hotmail.com>
Date:   Sun Aug 20 16:09:51 2023 -0700

    Remove redundant {}.

commit 6f013fce39602c259a5be2d690d548c73e51cccc
Author: topcat001 <anindya49@hotmail.com>
Date:   Sun Aug 20 16:02:15 2023 -0700

    Revert "Do not defer redraw if it is just the status line (will need to do more here I"

    This reverts commit 0a15bbf3f1972dc84c5c84d5128024c1bc4c0074.

commit e6322b4196d73c975ba2e73633e6de9c46779059
Author: topcat001 <anindya49@hotmail.com>
Date:   Sun Aug 20 15:46:59 2023 -0700

    Fix placeholder label and clean up.

commit 5896ac52a1f72056a75480b3e1ada328f239df9b
Merge: ad982330 e3a8b843
Author: Nicholas Marriott <nicholas.marriott@gmail.com>
Date:   Fri Aug 18 17:00:03 2023 +0100

    Merge branch 'master' into sixel

commit ad98233066b72547aee7fa0c87838847ee7f1ece
Author: topcat001 <anindya49@hotmail.com>
Date:   Tue Aug 15 13:57:08 2023 -0700

    Better text placeholder.

commit 312d83252c27fc4d09d09d121bf7573336e3cdca
Merge: 14b8b524 3d93b0c5
Author: topcat001 <anindya49@hotmail.com>
Date:   Tue Aug 15 13:39:22 2023 -0700

    Merge remote-tracking branch 'origin/master' into sixel

commit 14b8b524523a7d5a4e42f7dfa346905c604c91e2
Merge: 4baf7642 fda39377
Author: topcat001 <anindya49@hotmail.com>
Date:   Sat Jul 22 17:29:10 2023 -0700

    Merge branch 'master' into sixel

commit 4baf76422fadb216bf27b47645b52da3379e7dea
Author: Nicholas Marriott <nicholas.marriott@gmail.com>
Date:   Wed Jun 21 07:43:53 2023 +0100

    Both files can go on one line.

commit 4c92acf6ff24dde37ad41cd168ea2d3bcefb8567
Author: topcat001 <anindya49@hotmail.com>
Date:   Sat Jun 17 17:53:01 2023 -0700

    Merge topcat001/tmux/sixel.

commit 6794facc82e98f8448c192913cf62fe6e10fde63
Merge: 7b85f5ad f41c536f
Author: topcat001 <anindya49@hotmail.com>
Date:   Sat Jun 17 17:21:02 2023 -0700

    Merge remote-tracking branch 'origin/master' into sixel

commit 7b85f5adf9a5094db580ca98e4d2231d8d5b5a4f
Author: Nicholas Marriott <nicholas.marriott@gmail.com>
Date:   Thu Jun 8 12:55:03 2023 +0100

    Do not require passthrough for SIXEL.

commit a6ee55e0925cac35d011c188db2da0421fc09be1
Merge: 6da391f4 fe385b18
Author: Nicholas Marriott <nicholas.marriott@gmail.com>
Date:   Thu Jun 8 12:19:55 2023 +0100

    Merge branch 'master' into sixel

commit 6da391f460414ed3dde23e5ab6ca3fe8e988ce51
Merge: 0d71e585 0eb5d254
Author: topcat001 <anindya49@hotmail.com>
Date:   Sat May 20 17:05:55 2023 -0700

    Merge branch 'master' into sixel

commit 0d71e5853ffe797f90b815ac3af25ac0ad92ab07
Merge: 64368a1a fbe6fe7f
Author: topcat001 <anindya49@hotmail.com>
Date:   Sat Apr 29 17:32:07 2023 -0700

    Merge branch 'master' into sixel

commit 64368a1a63f04fb877b57e4286c9a2e1efe966c9
Merge: c630a56a 22eb0334
Author: topcat001 <anindya49@hotmail.com>
Date:   Thu Mar 30 14:21:09 2023 -0700

    Merge branch 'master' into sixel

commit c630a56a621b9761eed558cbd566a36cb09adf8f
Merge: 34c96c4c aaa043a2
Author: topcat001 <anindya49@hotmail.com>
Date:   Thu Nov 10 18:53:01 2022 -0800

    Merge branch 'master' into sixel

commit 34c96c4c4a33f86b49c8a53dc48b2b817db24e95
Merge: 2a1e16a2 50f4e0fa
Author: topcat001 <anindya49@hotmail.com>
Date:   Sat Nov 5 18:05:36 2022 -0700

    Merge branch 'master' into sixel

commit 2a1e16a24dc75741c66f5d72fa5bf26b73507993
Merge: a82f14c7 d001a94d
Author: topcat001 <anindya49@hotmail.com>
Date:   Thu Oct 27 16:01:35 2022 -0700

    Merge branch 'master' into sixel

commit a82f14c7b23a239a2114c756ef73bba8609ebe33
Merge: 742c0634 f7b30ed3
Author: topcat001 <anindya49@hotmail.com>
Date:   Sun Aug 28 13:43:07 2022 -0700

    Merge branch 'master' into sixel

commit 742c0634734e6b2840762e58b0bf27626b5ac24b
Merge: 906c92a5 87b248f3
Author: Nicholas Marriott <nicholas.marriott@gmail.com>
Date:   Fri Apr 1 10:14:15 2022 +0100

    Merge branch 'master' into sixel

commit 906c92a5f458b8843e7abd1d6f419dc091f8063c
Merge: 6680a024 138ffc7c
Author: Nicholas Marriott <nicholas.marriott@gmail.com>
Date:   Wed Dec 8 10:37:33 2021 +0000

    Merge branch 'master' into sixel

commit 6680a024be5e173a27c10e9a0be6c9072576086d
Author: Nicholas Marriott <nicholas.marriott@gmail.com>
Date:   Thu Oct 7 13:59:08 2021 +0100

    Fix build.

commit ebd2c585937f18045d334226d4a0cc788fe14353
Merge: 90dc0519 fed7b29c
Author: Nicholas Marriott <nicholas.marriott@gmail.com>
Date:   Thu Oct 7 13:19:48 2021 +0100

    Merge branch 'master' into sixel

commit 90dc05191cbba8de6d9d77ee7f9726325abe844e
Merge: a282439f 4694afbe
Author: Nicholas Marriott <nicholas.marriott@gmail.com>
Date:   Thu Feb 20 20:37:32 2020 +0000

    Merge branch 'master' into sixel

commit a282439fcb2f597927a5ba33d2c378c90eec8b42
Author: Nicholas Marriott <nicholas.marriott@gmail.com>
Date:   Thu Jan 30 09:12:53 2020 +0000

    Add missing declarations.

commit 3a741aacd108538f99239c68bfa2cd416bf0eb46
Merge: 40ad0107 339832b9
Author: Nicholas Marriott <nicholas.marriott@gmail.com>
Date:   Thu Jan 30 09:11:01 2020 +0000

    Merge branch 'sixel-passthrough' into sixel

commit 339832b92c298538f398754f6d3fc21d15d13326
Author: Nicholas Marriott <nicholas.marriott@gmail.com>
Date:   Thu Jan 30 09:04:51 2020 +0000

    Bad merge.

commit 92ed9fc0b20440f2bc553757e6bfe3126fe84be4
Merge: 5bb07548 32be954b
Author: Nicholas Marriott <nicholas.marriott@gmail.com>
Date:   Thu Jan 30 09:03:38 2020 +0000

    Merge branch 'master' into sixel-passthrough

commit 40ad01073d73a531b4e85b0138f78bf0b472b354
Merge: dd3c72f1 61b075a2
Author: Nicholas Marriott <nicholas.marriott@gmail.com>
Date:   Sun Jan 12 20:03:41 2020 +0000

    Merge branch 'master' into sixel

commit 5bb075487f5897d7402adb880e678043c0f7f3e0
Merge: 7c033a74 54efe337
Author: Nicholas Marriott <nicholas.marriott@gmail.com>
Date:   Wed Dec 18 20:24:42 2019 +0000

    Merge branch 'master' into sixel-passthrough

commit dd3c72f132c911b0ba61b56a56f46510704d3392
Merge: 1a0e5fe9 54efe337
Author: Nicholas Marriott <nicholas.marriott@gmail.com>
Date:   Wed Dec 18 20:24:26 2019 +0000

    Merge branch 'master' into sixel

commit 1a0e5fe933e89932f2f658936c52eb50644fbef4
Merge: cf071ffe 15d7e564
Author: Nicholas Marriott <nicholas.marriott@gmail.com>
Date:   Tue Dec 10 16:34:11 2019 +0000

    Merge branch 'master' into sixel

commit cf071ffecd5a0d33008fd0a8b66a22f6855c7a8d
Author: Nicholas Marriott <nicholas.marriott@gmail.com>
Date:   Mon Dec 9 15:41:56 2019 +0000

    Remove images when reflow happens.

commit 2006b7a5631787a7086c6bae364e62d0a0b5948a
Author: Nicholas Marriott <nicholas.marriott@gmail.com>
Date:   Thu Dec 5 09:27:15 2019 +0000

    More invalidation of images.

commit b642eac4503cc89cde01103f7bacca57cc9c1a2b
Author: Nicholas Marriott <nicholas.marriott@gmail.com>
Date:   Thu Dec 5 09:11:24 2019 +0000

    Redraw and scroll images and part of invalidating them.

commit 7566e37a461bf245bd2e54e1d522e919071e9c44
Author: Nicholas Marriott <nicholas.marriott@gmail.com>
Date:   Thu Dec 5 08:51:24 2019 +0000

    Call sixel_scale with the right number of arguments.

commit 62c0280b23cf67fc43f691392a8eca5cd7ff0727
Author: Nicholas Marriott <nicholas.marriott@gmail.com>
Date:   Thu Dec 5 08:48:58 2019 +0000

    Correctly remove when not visible.

commit 86c5098a887f2cd09b828e051ccf0fab21bf4f6a
Author: Nicholas Marriott <nicholas.marriott@gmail.com>
Date:   Thu Dec 5 08:32:25 2019 +0000

    Add helpers to scroll image up and a flag to copy the colours.

commit 49f2f0a8f1e72389f4128aa38119ed124cdc31c5
Author: Nicholas Marriott <nicholas.marriott@gmail.com>
Date:   Thu Dec 5 00:02:55 2019 +0000

    Store images, currently at most 10.

commit 3aebcc67099ccbc5964c744fc1435931c1a78583
Merge: 146ee3f6 92ecd611
Author: Nicholas Marriott <nicholas.marriott@gmail.com>
Date:   Wed Dec 4 19:27:16 2019 +0000

    Merge branch 'master' into sixel

commit 7c033a74e25957d333217cb71a7658b860583501
Merge: 0a15bbf3 92ecd611
Author: Nicholas Marriott <nicholas.marriott@gmail.com>
Date:   Wed Dec 4 12:41:09 2019 +0000

    Merge branch 'master' into sixel-passthrough

commit 146ee3f6f8ee2629c6a88b6900a71f3e6fd14e4d
Author: Nicholas Marriott <nicholas.marriott@gmail.com>
Date:   Sat Nov 30 09:47:53 2019 +0000

    Don't write image as text yet.

commit 0a15bbf3f1972dc84c5c84d5128024c1bc4c0074
Author: Nicholas Marriott <nicholas.marriott@gmail.com>
Date:   Sat Nov 30 09:15:35 2019 +0000

    Do not defer redraw if it is just the status line (will need to do more here I
    think).

commit a5b1e209417b7d3f5b0099642dd317c312f79377
Author: Nicholas Marriott <nicholas.marriott@gmail.com>
Date:   Thu Nov 28 14:20:22 2019 +0000

    Add a flag to disable blocking while sending a SIXEL image (turned off when the
    buffer hits 0 size).

commit 968382aa6a4b9c71fbc221aa4f0e899f6a83a260
Author: Nicholas Marriott <nicholas.marriott@gmail.com>
Date:   Thu Nov 28 12:35:18 2019 +0000

    Pass through SIXEL DCS sequences (treat similarly to the passthrough escape
    sequence) if it appears the terminal outside supports them.

commit b1904c9b8db514133d3372aac13b2ff0b2093cc3
Author: Nicholas Marriott <nicholas.marriott@gmail.com>
Date:   Sat Nov 30 09:17:18 2019 +0000

    Store SIXELs as a box for the moment.

commit 5d8dbcdf3d76d0e69b8f2d21eff48f819dcec199
Author: Nicholas Marriott <nicholas.marriott@gmail.com>
Date:   Sat Nov 30 09:15:35 2019 +0000

    Do not defer redraw if it is just the status line (will need to do more here I
    think).

commit 0c999a402ece7b40e6ae84547893421b52d508ff
Merge: 28961dd5 866b053f
Author: Nicholas Marriott <nicholas.marriott@gmail.com>
Date:   Fri Nov 29 18:54:09 2019 +0000

    Merge branch 'master' into sixel

commit 28961dd5a38dd5a7b703ed0e6625fa9a65556d35
Author: Nicholas Marriott <nicholas.marriott@gmail.com>
Date:   Thu Nov 28 14:24:57 2019 +0000

    Add an image.

commit d2e3f3c1cca5410570c5392340d14e96ae7a354c
Author: Nicholas Marriott <nicholas.marriott@gmail.com>
Date:   Thu Nov 28 14:20:22 2019 +0000

    Add a flag to disable blocking while sending a SIXEL image (turned off when the
    buffer hits 0 size).

commit e01df67ca106e57f5c689e75f313f8cda6f8b805
Author: Nicholas Marriott <nicholas.marriott@gmail.com>
Date:   Thu Nov 28 13:21:40 2019 +0000

    Crop and scale images as needed when drawing them.

commit e24acc0b5c3ac19dcacebdea243dcc7784215ffa
Author: Nicholas Marriott <nicholas.marriott@gmail.com>
Date:   Thu Nov 28 12:38:02 2019 +0000

    Simple SIXEL parse and modify API.

commit b34111b3da1e4b1769a976cd40486144f7b4f5a2
Author: Nicholas Marriott <nicholas.marriott@gmail.com>
Date:   Thu Nov 28 12:35:18 2019 +0000

    Pass through SIXEL DCS sequences (treat similarly to the passthrough escape
    sequence) if it appears the terminal outside supports them.
2023-08-22 08:43:35 +01:00
Thomas Adam
e3a8b8434c Merge branch 'obsd-master' 2023-08-17 18:01:09 +01:00
nicm
8636848e63 Add a session, pane and user mouse range types for the status line and
add format variables for mouse_status_line and mouse_status_range so
they can be associated with different commands in the key bindings.
GitHub issue 3652.
2023-08-17 14:10:28 +00:00
Thomas Adam
3d93b0c52e Merge branch 'obsd-master' 2023-08-15 12:01:09 +01:00
nicm
d9942c769e Add meta bindings for status line menus as well as the existing pane one
for terminals which steal the mouse menu button.
2023-08-15 09:51:48 +00:00
Thomas Adam
6a45e6c256 Merge branch 'obsd-master' 2023-08-15 10:01:09 +01:00
nicm
b770a429c6 Add an option menu-selected-style to configure the currently selected
menu item, from Alexis Hildebrandt.
2023-08-15 07:01:47 +00:00
Thomas Adam
11e69f6025 Merge branch 'obsd-master' 2023-08-11 20:01:09 +01:00
nicm
57837bbf67 Do not crash if in buffer mode and the last buffer is deleted using the
command.
2023-08-11 17:09:00 +00:00
Thomas Adam
4c60afde78 Merge branch 'obsd-master' 2023-08-08 12:01:10 +01:00
Thomas Adam
ed689cd54e Merge branch 'obsd-master' 2023-08-08 10:01:11 +01:00
nicm
7a44984069 Add flag to next-prompt/previous-prompt to go to command output instead,
from Magnus Gross.
2023-08-08 08:21:29 +00:00
nicm
dee72ed41f Add options and flags for menu styles similar to those existing for
popups, from Alexis Hildebrandt. GitHub issue 3650.
2023-08-08 08:08:47 +00:00
nicm
1071ef8fc5 Extend the menu drawing function to support custom characters and
styles, from Alexis Hildebrandt.
2023-08-08 07:41:04 +00:00
nicm
2b535bc173 Fix a couple of rounded border characters, from Alexis Hildebrandt. 2023-08-08 07:19:48 +00:00
Thomas Adam
b791f00bf1 Merge branch 'obsd-master' 2023-08-07 14:01:12 +01:00
Thomas Adam
3e82ad5340 Merge branch 'obsd-master' 2023-08-07 12:01:10 +01:00
nicm
7b1030293c Free title earlier, from Alexis Hildebrandt. 2023-08-07 10:52:00 +00:00
nicm
a5fd80bbc3 Trim can generate strings longer than the original if there are many #s,
so create a bigger buffer. Reported by Robert Morris.
2023-08-07 10:04:29 +00:00
Thomas Adam
fda3937734 Merge branch 'obsd-master' 2023-07-19 16:01:09 +01:00
nicm
b13c230749 Correct visited flag when the last window list is rebuilt by renumbering
windows, appears to fix hang reported by Mark Kelly.
2023-07-19 13:03:36 +00:00
Thomas Adam
715f39a53a Merge branch 'obsd-master' 2023-07-14 22:01:10 +01:00
nicm
2f74e811f1 Set extended keys flag again after reset, from Eric T Johnson. 2023-07-14 19:32:59 +00:00
Thomas Adam
828efc7bcf Merge branch 'obsd-master' 2023-07-13 18:01:10 +01:00
nicm
84936b832f Use 8 for underscore colour defaults instead of 0 which is less
confusing, and fix writing tge default colour. GitHub issue 3627.
2023-07-13 06:03:48 +00:00
Thomas Adam
c8494dff7b Merge branch 'obsd-master' 2023-07-11 20:01:10 +01:00
nicm
8fcc212e7a Remove Ns and Li and change Nm to Ic, suggested by jmc. 2023-07-11 16:09:09 +00:00
Thomas Adam
0e281530cb Merge branch 'obsd-master' 2023-07-11 10:01:10 +01:00
nicm
efded95ed7 Add descriptions of copy mode commands, from Michael Bianco. 2023-07-11 07:34:23 +00:00
Thomas Adam
18870913c5 Merge branch 'obsd-master' 2023-07-10 14:01:12 +01:00
nicm
63b7282377 It should no longer be necessary to ignore SIGCHLD because it is now
blocked around daemon(), and doing so causes trouble with newer libevent
(it cannot restore the original handler). Reported by Azat Khuzhin in
GitHub issue 3626.
2023-07-10 12:00:08 +00:00
Thomas Adam
269dab4b3e Merge branch 'obsd-master' 2023-07-10 12:01:12 +01:00
nicm
4ece43a029 Loop around waitpid in client, from Azat Khuzhin. 2023-07-10 09:35:46 +00:00
nicm
8b3e2eab5a Use a stack for last panes line windows, from Thomas Bertschinger in
GitHub issue 3588.
2023-07-10 09:24:53 +00:00
Thomas Adam
e4c4ceb286 Merge branch 'obsd-master' 2023-07-10 02:01:11 +01:00
nicm
b7e22d00b4 Call closefrom after removing signals because newer libevent doesn't
like its signal fd being closed Azat Khuzhin.
2023-07-09 22:54:52 +00:00
Thomas Adam
1a11c972ae Merge branch 'obsd-master' 2023-07-03 20:01:12 +01:00
nicm
43b841f188 Add support for marking lines with a shell prompt based on the OSC 133
extension, from Munif Tanjim in GitHub issue 3596.
2023-07-03 16:47:43 +00:00
Thomas Adam
f9b0460840 Merge branch 'obsd-master' 2023-07-03 14:01:10 +01:00
Thomas Adam
659d876cd5 Merge branch 'obsd-master' 2023-07-03 12:01:09 +01:00
nicm
ac43186dff Do not risk writing over the end of the buffer when it ends in #
(because strchr \0 will be non-NULL), reported by Robert Morris in
GitHub issue 3610.
2023-07-03 10:48:26 +00:00
nicm
e79fb214f8 Another warning fix for GCC from Thomas Klausner. 2023-07-03 08:37:14 +00:00
Thomas Adam
9cf58d1a52 Merge branch 'obsd-master' 2023-07-01 02:01:10 +01:00
nicm
a2a02fd7d7 Change a few types to fix warnings, from Thomas Klausner. 2023-06-30 21:55:08 +00:00
Thomas Adam
237ee6f231 Merge branch 'obsd-master' 2023-06-30 16:01:10 +01:00
nicm
4e57894e85 Get rid of some warnings with GCC 10, from Thomas Klausner. 2023-06-30 13:19:32 +00:00
Nicholas Marriott
8c9fbbf4f3 Add libterminfo for NetBSD, from Thomas Klausner. 2023-06-29 15:31:32 +01:00
Thomas Adam
3f3d61bd58 Merge branch 'obsd-master' 2023-06-26 12:01:09 +01:00
Thomas Adam
80d4f4afc6 Merge branch 'obsd-master' 2023-06-26 10:01:10 +01:00
nicm
2546216019 When exiting alternate screen, there is no need to reflow when going
back to old size since the contents will be overwritten. GitHub issue
3510.
2023-06-26 08:14:19 +00:00
nicm
ff8882a24f Add "us" to styles for underscore colour, GitHub issue 3589. 2023-06-26 07:17:40 +00:00
Thomas Adam
ffe2410639 Merge branch 'obsd-master' 2023-06-25 20:01:09 +01:00
nicm
9e14c1f88d SGR 0 should not end hyperlink, reported by Lucas Trzesniewski. 2023-06-25 15:53:07 +00:00
Thomas Adam
29a5dfc7c0 Merge branch 'obsd-master' 2023-06-21 08:01:08 +01:00
nicm
645bf8b3ab Check fdopen return value, from Christian Menges. 2023-06-21 06:28:18 +00:00
Thomas Adam
f41c536ff3 Merge branch 'obsd-master' 2023-06-08 14:01:09 +01:00
nicm
bdd05bdbd3 Fix mismatch between function prototype and definition, from Anindya
Mukherjee.
2023-06-08 11:17:28 +00:00
Nicholas Marriott
fe385b180f Try utf8proc with pkg-config, from Alex Wu. 2023-06-08 09:10:43 +01:00
Thomas Adam
0eb5d25453 Merge branch 'obsd-master' 2023-05-19 10:01:09 +01:00
nicm
1d98394b41 Add format for server_sessions, from Magnus Gross. 2023-05-19 07:46:34 +00:00
Thomas Adam
149d9cc851 Merge branch 'obsd-master' 2023-05-08 12:01:09 +01:00
tb
204d8f31d7 Reorder struct grid_cell_entry
On aarch64 with llvm 15, the new -Wunaligned-access emits noise on every
one of tmux's source files. This avoids this warning by moving a u_char
to the end of the struct. This does not change the size of the struct on
any architecture.

ok nicm
2023-05-08 10:03:39 +00:00
Thomas Adam
fbe6fe7f55 Merge branch 'obsd-master' 2023-04-28 12:52:21 +01:00
Nicholas Marriott
168eab11a7 Cast both strings for tparm. 2023-04-28 07:23:53 +01:00
nicm
41b318ac7c Add options to change the confirm key and default behaviour of
confirm-before. From Elias Assaf in GitHub issue 3548; prompted by an
earlier change from Yutaro Yoshii in GitHub issue 3496.
2023-04-28 06:12:27 +00:00
nicm
bf636d9575 Do not fatal if tparm fails, instead just log it (not working sequences
are better than exiting).
2023-04-28 05:59:35 +00:00
Nicholas Marriott
39d41d0810 Use ncurses' new tparm_s function (added in 6.4-20230424) instead of tparm so
it does not object to string arguments in capabilities it doesn't already know.
2023-04-28 06:44:40 +01:00
Thomas Adam
9d8131c190 Merge branch 'obsd-master' 2023-04-27 19:16:08 +01:00
Nicholas Marriott
0ff991b25f Set default lock command to vlock on Linux if present at build time, from Josh
Boyer in GitHub issue 3527.
2023-04-25 17:23:32 +01:00
Nicholas Marriott
15c70e562c Include NCURSES_VERSION_PATCH in the log. 2023-04-25 16:48:49 +01:00
nicm
8f34504736 Tidy tparm wrapper functions to have more obvious names and check tparm
return value.
2023-04-25 09:31:50 +00:00
nicm
551e0c36d9 Invalidate cached tty state after changing features since they may
change what the terminal can do and need mouse sequences or similar to
be sent again, GitHub issue 3513.
2023-04-25 09:24:44 +00:00
nicm
48eba4c195 Ignore the user keys range when checking if a key is Unicode. 2023-04-17 18:22:24 +00:00
nicm
9f605178c3 It seems silly to use progname for version, just always say tmux. 2023-04-17 18:00:19 +00:00
nicm
bcafe51378 Make the check if printing is allowed the same as writing which is less
confusing.
2023-04-17 17:58:35 +00:00
nicm
280fe77edd Discard mouse sequences that have the right form but actually are
invalid (for example have column zero rather than one).
2023-04-17 17:57:35 +00:00
Nicholas Marriott
b9524f5b72 Add support for spawning panes in separate cgroups with systemd and a configure
flag to disable. From Eric T Johnson yut23 AT gvljohnsons DOT com in GitHub
issue 3514.
2023-04-03 08:54:06 +01:00
nicm
a2018b2c3f Clarify text for new -A slightly, GitHub issue 3508. 2023-04-03 07:39:37 +00:00
nicm
c21af7e446 Add a format to show if there are unseen changes while in a mode, from
Dan Aloni in GitHub issue 3498.
2023-03-27 08:47:57 +00:00
nicm
d73078838d For passthrough, don't write to clients attached to different sessions,
based on a fix from Sergei Grechanik.
2023-03-27 08:31:32 +00:00
Thomas Adam
22eb0334c3 Merge branch 'obsd-master' 2023-03-15 22:01:09 +00:00
nicm
a9ac614691 Do not leak screen in popups, GitHub issue 3492. 2023-03-15 19:23:22 +00:00
Thomas Adam
1d0f68dee9 Merge branch 'obsd-master' 2023-03-15 10:01:10 +00:00
nicm
ac4bb89d43 Fix command prompt not to always append argument but only if there has
actually been expansion. GitHub issue 3493.
2023-03-15 08:15:39 +00:00
Thomas Adam
b55f34029a Merge branch 'obsd-master' 2023-02-10 16:01:10 +00:00
nicm
907f58cc3c Fix cursor position after zero width space, GitHub issue 3469. 2023-02-10 14:01:43 +00:00
Thomas Adam
023b0f76c3 Merge branch 'obsd-master' 2023-02-07 14:01:11 +00:00
Thomas Adam
ddaeebc213 Merge branch 'obsd-master' 2023-02-07 12:01:13 +00:00
nicm
1262e685b8 Remove old buffer when renaming rather than complaining, GitHub issue
3467 from Jean-Philippe Paradis.
2023-02-07 10:56:04 +00:00
nicm
0bd78b42c0 Add an L modifier like P, W, S to loop over clients. Also fix some long
lines in tmux(1).
2023-02-07 10:21:01 +00:00
Thomas Adam
4a0126f7fb Merge branch 'obsd-master' 2023-02-06 12:01:12 +00:00
nicm
7acc8d703d Add -f to list-clients like the other list commands, from Andy Walker in
GitHub issue 3449.
2023-02-06 09:20:30 +00:00
Thomas Adam
77118f3a9f portable: remove vis.h
This is included portably across different systems.
2023-02-06 01:55:02 +00:00
Thomas Adam
e25926d3c4 Merge branch 'obsd-master' 2023-02-06 00:01:11 +00:00
nicm
0cb75f1332 Do not allow multiple line separators in a row. 2023-02-05 21:26:48 +00:00
nicm
93b1b78150 Extend display-message to work for control clients. GitHub issue 3449. 2023-02-05 21:15:32 +00:00
Thomas Adam
493922dc4b Merge branch 'obsd-master' 2023-02-02 12:01:11 +00:00
nicm
f10854cfc5 Add a missing error message which causes an invalid layout name to crash. 2023-02-02 09:24:59 +00:00
nicm
993e7a937f Tweak note for D key binding, from Clark Wang. 2023-02-02 09:06:44 +00:00
Thomas Adam
f5af3cfb21 Merge branch 'obsd-master' 2023-01-23 12:01:11 +00:00
nicm
e7e112fbd0 Too many \s in example, GitHub issue 3445. 2023-01-23 09:33:51 +00:00
Thomas Adam
c42087c789 Merge branch 'obsd-master' 2023-01-21 00:01:11 +00:00
nicm
3aa458ea63 Add a flag to display-menu to select the manu item chosen first, GitHub
issue 3442.
2023-01-20 21:36:00 +00:00
Thomas Adam
c4a6f403bb Merge branch 'obsd-master' 2023-01-17 12:01:09 +00:00
nicm
9789ea3fb4 Support -1 without -N for list-keys. 2023-01-17 10:40:51 +00:00
Thomas Adam
f416ae1c12 Merge branch 'obsd-master' 2023-01-17 10:01:09 +00:00
nicm
d578cf8d3f Update palette when moving a pane, GitHub issue 3437. 2023-01-17 06:50:55 +00:00
Thomas Adam
789cb91f31 Merge branch 'obsd-master' 2023-01-16 14:01:10 +00:00
nicm
eb1f8d70a7 Mark keys sent by command and skip paste handling for them. 2023-01-16 11:26:14 +00:00
Thomas Adam
42895efac3 Merge branch 'obsd-master' 2023-01-12 22:01:09 +00:00
nicm
483cc77c1c Have tmux recognise pasted texts wrapped in bracket paste sequences,
rather than only forwarding them to the program inside. From Andrew
Onyshchuk in GitHub issue 3431.
2023-01-12 18:49:11 +00:00
Thomas Adam
9b1ea8b16d Merge branch 'obsd-master' 2023-01-09 16:01:11 +00:00
nicm
b41892622d Fix behaviour with \007 (used the wrong tree for last change). 2023-01-09 14:12:41 +00:00
Thomas Adam
9051220243 Merge branch 'obsd-master' 2023-01-09 10:01:09 +00:00
nicm
c0031f8b85 Accept \007 as terminator to OSC 10 or 11. 2023-01-09 07:57:14 +00:00
Thomas Adam
c1a30ed995 Merge branch 'obsd-master' 2023-01-09 02:01:10 +00:00
nicm
565de3f54b Fix parsing of optional arguments so that and accept a - starting an
argument.
2023-01-08 23:34:46 +00:00
Thomas Adam
153ae758c9 portable: fixup merge with utf8.c 2023-01-08 23:27:54 +00:00
Thomas Adam
5086377f30 Merge branch 'obsd-master' 2023-01-08 23:26:09 +00:00
nicm
7c0789d2d2 Have client return 1 if process is interrupted to an input pane. 2023-01-08 22:17:04 +00:00
Nicholas Marriott
2a32565e0c Restore code to handle wcwidth failure so that unknown codepoints still
do the most likely right thing. GitHub issue 3427, patch based on an
diff from Jesse Luehrs in GitHub issue 3003.
2023-01-08 22:15:38 +00:00
nicm
7ced0a03d2 Restore code to handle wcwidth failure so that unknown codepoints still
do the most likely right thing. GitHub issue 3427, patch based on an
diff from Jesse Luehrs in GitHub issue 3003.
2023-01-08 22:15:30 +00:00
nicm
cb51942669 Quotes are now required in select-layout example. 2023-01-08 21:00:01 +00:00
Nicholas Marriott
093fb53773 Missing #endif. 2023-01-06 11:38:41 +00:00
Thomas Adam
21e00e4635 Merge branch 'obsd-master' 2023-01-06 09:02:00 +00:00
nicm
09afc6c8ee If a pane is killed, cancel reading from the file. GitHub issue 3422. 2023-01-06 07:09:27 +00:00
nicm
a41a927441 Query the client terminal for foreground and background colours and if
OSC 10 or 11 is received but no colour has been set inside tmux, return
the colour from the first attached client (probably most people will
have all light or or all dark terminals).
2023-01-03 11:43:24 +00:00
jmc
3fe01ff09c spelling fixes; from paul tagliamonte
amendments to his diff are noted on tech
2022-12-26 19:16:03 +00:00
kn
b5ab4d2c13 Denote multiple arguments with 'arg ...' not 'args'
A few programs used the plural in their synopsis which doesn't read as
clear as the obvious triple-dot notation.

mdoc(7) .Ar defaults to "file ..." if no arguments are given and consistent
use of 'arg ...' matches that behaviour.

Cleanup a few markups of the same argument so the text keeps reading
naturally;  omit unhelpful parts like 'if optional arguments are given,
they are passed along' for tools like time(1) and timeout(1) that obviously
execute commands with whatever arguments where given -- just like doas(1)
which doesn't mention arguments in its DESCRIPTION in the first place.

For expr(1) the difference between 'expressions' and 'expression ...' is
crucial, as arguments must be passed as individual words.

Feedback millert jmc schwarze deraadt
OK jmc
2022-12-22 19:53:23 +00:00
nicm
4d79d463ef Allow send-keys without a client again, reported by Stefan Hagen. 2022-12-19 07:30:10 +00:00
nicm
7cb48fc40b Do not escape tabs in output (iTerm2 needs them). GitHub issue 3414. 2022-12-16 08:22:05 +00:00
nicm
8bd17bff49 Make U+FE0F VARIATION SELECTOR-16 change the width from 1 to 2. GitHub
issue 3409.
2022-12-16 08:19:58 +00:00
nicm
3b3f42053a Add send-keys -K to handle keys directly as if typed (so look up in key
table). GitHub issue 3361.
2022-12-16 08:13:40 +00:00
Nicholas Marriott
70ff8cfe1e No vis.h in portable. 2022-12-07 12:30:36 +00:00
Thomas Adam
6249a4b866 Merge branch 'obsd-master' 2022-12-07 12:01:09 +00:00
nicm
7e497c7f23 Process escape sequences in show-buffer, GitHub issue 3401. 2022-12-07 09:44:44 +00:00
Thomas Adam
1536b7e206 Merge branch 'obsd-master' 2022-11-11 10:01:09 +00:00
Nicholas Marriott
e46d0632a5 Add key regression tests from Aaron Jensen. 2022-11-11 08:47:55 +00:00
nicm
20da167377 Tweak previous to set and log the feature instead of just setting the
flag.
2022-11-11 08:44:11 +00:00
nicm
fe475bd856 Parse primary device attributes as well as secondary and add a SIXEL
flag (not used yet), from Anindya Mukherjee.
2022-11-11 08:37:55 +00:00
nicm
079f48e8a6 Document alternative delimiters for substitution, from Jim Wisniewski. 2022-11-11 08:27:17 +00:00
Thomas Adam
aaa043a20f Merge branch 'obsd-master' 2022-11-11 02:01:10 +00:00
jmc
48f41e4a41 - sort options; from josiah frentsos
ok nicm

- add -N to SYNOPSIS

- sort usage()
2022-11-10 22:58:39 +00:00
Thomas Adam
6fb80527f3 Merge branch 'obsd-master' 2022-11-08 12:01:11 +00:00
nicm
f86eba2129 Fix C-S-Tab without extended keys, from Aaron Jensen. 2022-11-08 10:04:31 +00:00
Thomas Adam
50f4e0fac9 Merge branch 'obsd-master' 2022-11-04 10:01:10 +00:00
nicm
77c135349a Unescape the string for the literal operator (l:) so special characters
work.
2022-11-04 08:03:23 +00:00
Thomas Adam
c449512be4 Merge branch 'obsd-master' 2022-11-03 10:01:11 +00:00
nicm
17290b9121 If there are no buffers, reset mode as soon as any key pressed. Fixes
crash reported by Gaoyang Zhang in GitHub issue 3373.
2022-11-03 08:41:53 +00:00
nicm
3be369522b Add a -l flag to display-message to disable format expansion, from Aaron
Jensen. GitHub issue 3372.
2022-11-03 08:33:57 +00:00
Thomas Adam
dbfbd8a195 Merge branch 'obsd-master' 2022-11-02 08:01:11 +00:00
nicm
9614f51560 Instead of always setting the extended flag, set it only when searching.
Allows send-keys to work. From Aaron Jensen.
2022-11-02 07:36:07 +00:00
Thomas Adam
36896f6dd0 Merge branch 'obsd-master' 2022-11-01 12:01:10 +00:00
nicm
2d08235987 Add modified Tab key sequences, from Aaron Jensen, GitHub issue 3368. 2022-11-01 09:54:13 +00:00
nicm
2291045116 Use active pane in target window not current window for +/-. GitHub
issue 3370.
2022-11-01 09:46:14 +00:00
Thomas Adam
9dd1f442c5 Merge branch 'obsd-master' 2022-10-28 16:01:10 +01:00
nicm
8edece2cdb Add paste-buffer-deleted notification and fix name of paste-buffer-changed. 2022-10-28 13:00:02 +00:00
Thomas Adam
d001a94d7b Merge branch 'obsd-master' 2022-10-25 22:01:10 +01:00
nicm
c2580cfe24 Initialize context before testing it. 2022-10-25 17:53:31 +00:00
Thomas Adam
dafd6f462f Merge branch 'obsd-master' 2022-10-25 12:01:10 +01:00
nicm
2111142cf1 Fix a memory leak, from Japin Li in GitHub issue 3358. 2022-10-25 09:12:05 +00:00
nicm
0fc961b22e Do not fire redraw callback if NULL. 2022-10-25 09:04:49 +00:00
Nicholas Marriott
5ce34add77 Do not attempt to connect to the socket as a client if systemd is active, from
Julien Moutinho in GitHub issue 3345.
2022-10-18 15:58:06 +01:00
Thomas Adam
934f357149 Merge branch 'obsd-master' 2022-10-17 14:01:10 +01:00
nicm
ff2766b024 Preserve marked pane when renumbering windows. 2022-10-17 10:59:42 +00:00
Thomas Adam
9ef854f5a9 Merge branch 'obsd-master' 2022-09-28 12:01:09 +01:00
nicm
a10452be2d Add scroll-top and scroll-bottom commands to scroll so cursor is at top
or bottom. From Anindya Mukherjee, GitHub issue 3334.
2022-09-28 07:59:50 +00:00
nicm
9cc8e40aa0 Add a -T flag to capture-pane to stop at the last used cell instead of
the full width. Restore the previous behaviour by making it default to
off unless -J is used (the only time it matters). Fixes mosh unit tests;
GitHub issue 3339.
2022-09-28 07:55:29 +00:00
Thomas Adam
f49f92737f Merge branch 'obsd-master' 2022-09-22 17:10:38 +01:00
Nicholas Marriott
19344ec890 Add headers and fix type, from Marvin Schmidt. GitHub issue 3332. 2022-09-19 07:03:17 +01:00
nicm
a2cc601c3d Don't use options from pane if pane is NULL. 2022-09-12 12:02:17 +00:00
nicm
9ab1ba36cd Use correct option name. 2022-09-10 17:01:33 +00:00
nicm
f03c3ca6c3 Add message-line option to control where message and prompt go, from
Varun Kumar E in GitHub issue 3324.
2022-09-09 11:02:23 +00:00
Nicholas Marriott
0a0ded3268 Regress typos. 2022-09-07 07:28:26 +01:00
Thomas Adam
6da520c5a1 Merge branch 'obsd-master' 2022-08-31 12:01:10 +01:00
nicm
68dc9af9ac Fix window size report, from Vincent Bernat. 2022-08-31 08:07:05 +00:00
Thomas Adam
f7b30ed3d4 Merge branch 'obsd-master' 2022-08-24 10:01:13 +01:00
nicm
e867528209 Check for NULL returns from bufferevent_new. 2022-08-24 07:22:30 +00:00
Thomas Adam
038dfb27a8 Merge branch 'obsd-master' 2022-08-23 12:01:10 +01:00
nicm
416c27c995 Add scroll-middle copy mode command to make cursor line in the middle,
from Varun Kumar E in GitHub issue 3307.
2022-08-23 08:14:19 +00:00
Nicholas Marriott
19344efa78 Fix fallback implementaion of getpeereid, from Pino Toscano. 2022-08-22 08:21:42 +01:00
Thomas Adam
9c34aad21c Merge branch 'obsd-master' 2022-08-15 13:54:47 +01:00
nicm
7c2dcd7238 Notify when a paste buffer is deleted, GitHub issue 3302 from George
Nachman.
2022-08-15 09:10:34 +00:00
nicm
03149bf7f6 Add a Nobr terminfo capability to tell tmux the terminal does not use
bright colours for bold (makes a difference to how tmux applies palette
differences). From Damien Tardy-Panis in GitHub issue 3301.
2022-08-15 08:54:03 +00:00
nicm
497021d0db Add some const, from Markus F X J Oberhumer. 2022-08-15 08:41:13 +00:00
nicm
cfdc5b62ad Don't stop at first match when updating environment. 2022-08-15 08:37:03 +00:00
Thomas Adam
9b08e5139b Merge branch 'obsd-master' 2022-08-11 12:01:10 +01:00
nicm
e139f977b1 vi(1) Home/End bindings, from Markus F X J Oberhumer. 2022-08-11 09:11:26 +00:00
Thomas Adam
9abf5d9fe5 Merge branch 'obsd-master' 2022-08-10 18:01:11 +01:00
nicm
273577ba0a Fix check of home directory (&& not ||), from Markus F X J Oberhumer,
GitHub issue 3297.
2022-08-10 14:03:59 +00:00
Thomas Adam
e15058e60f Merge branch 'obsd-master' 2022-08-04 16:01:09 +01:00
nicm
de5cd54124 Change g and G to go to top and bottom of menu, GitHub issue 3286. 2022-08-04 12:06:09 +00:00
Thomas Adam
c6cf09450a Merge branch 'obsd-master' 2022-08-03 16:01:11 +01:00
nicm
c6e7568471 Do not crash when searching for .* with extremely long lines. Reported
by Torbjorn Lonnemark, GitHub issue 3272.
2022-08-03 13:27:48 +00:00
Thomas Adam
7b8ececd8d Merge branch 'obsd-master' 2022-08-02 14:01:09 +01:00
nicm
42ba6c1b22 Add a third state "all" to allow-passthrough to work even in invisible
panes, from Sergei Grechanik in GitHub issue 3274.
2022-08-02 11:09:26 +00:00
Thomas Adam
9a2fdf8fd4 Merge branch 'obsd-master' 2022-08-02 12:01:09 +01:00
Nicholas Marriott
00812c9053 Check for $YACC, from Wei Shih in GitHub issue 3267. 2022-08-02 11:52:09 +01:00
nicm
33c59100ae Fix validation of missing percentage arguments. 2022-08-02 09:23:34 +00:00
nicm
36d904011a -u is no longer equivalent to -TUTF-8 so don't say it is. 2022-08-02 08:57:01 +00:00
Thomas Adam
9d9445a48e Merge branch 'obsd-master' 2022-07-22 10:01:10 +01:00
nicm
a8da24771c Clear marks when the search string changes. From Anindya Mukherjee,
GitHub issue 3255.
2022-07-22 07:14:07 +00:00
Thomas Adam
ab1d18d00f Merge branch 'obsd-master' 2022-07-19 10:01:08 +01:00
nicm
ee431d482a Do not ignore the "off" flag when checking if a pane should be stopped,
GitHub issue 3250.
2022-07-19 07:10:13 +00:00
Nicholas Marriott
e06c09889c Add permissions for workflow, GitHub issue 3202. 2022-07-19 07:54:11 +01:00
nicm
86dfbda0e4 Process modifiers as bits rather than using a switch, from Koichi Murase. 2022-07-19 06:51:31 +00:00
Nicholas Marriott
697cebb4c1 Include curses properly for hyperlinks ifdef, from chrysn at fsfe dot org. 2022-07-19 07:48:48 +01:00
nicm
3c65475561 Fix memory leak, from Gabriel Souza Franco. 2022-07-19 06:46:57 +00:00
Thomas Adam
dc6bc0e95a Merge branch 'obsd-master' 2022-07-06 12:01:09 +01:00
Thomas Adam
b130e951cc Merge branch 'obsd-master' 2022-07-06 10:01:10 +01:00
Nicholas Marriott
9e19f132f2 Errors are now displayed on attach so use control mode to test
instead.
2022-07-06 09:54:53 +01:00
nicm
1afe22086f Show config errors on attach if they were not shown when the session
was created.
2022-07-06 08:40:52 +00:00
Nicholas Marriott
8e8b9865d1 Add hyperlink test, from Jeff Chiang. 2022-07-06 09:33:30 +01:00
nicm
a39827a85c Remove debugging code. 2022-07-06 08:32:28 +00:00
nicm
9e03df5500 Defer reading from control client until the command line command has
completed.
2022-07-06 08:31:59 +00:00
nicm
dd602eaa61 Mention whether time is creation/activity for sort orders. 2022-07-06 07:51:37 +00:00
nicm
d0d2c39dec Support hyperlinks with capture-pane -e and add a mouse_hyperlink
format, GitHub issue 3247 from Jeff Chiang.
2022-07-06 07:36:36 +00:00
Thomas Adam
57fec74966 Merge branch 'obsd-master' 2022-07-04 12:01:10 +01:00
nicm
9360e0ef32 Sort panes by index not by ID, GitHub issue 3249. 2022-07-04 08:39:45 +00:00
Nicholas Marriott
f08c019d41 Do not set Hls for hyperlinks on ncurses older than 5.9 (for example macOS). 2022-06-30 16:46:26 +01:00
Thomas Adam
c3af8f6b16 hyperlinks: remove vis.h
Not used on Linux.
2022-06-30 16:44:43 +01:00
Thomas Adam
01c4919f5f Merge branch 'obsd-master' 2022-06-30 16:37:18 +01:00
nicm
cdacc12ce3 Add support for OSC 8 hyperlinks (a VTE extension now supported by other
terminals such as iTerm2). Originally written by me then extended and
completed by first Will Noble and later Jeff Chiang. GitHub issues 911,
2621, 2890, 3240.
2022-06-30 09:55:53 +00:00
Thomas Adam
d8c527a5f9 Merge branch 'obsd-master' 2022-06-27 12:01:09 +01:00
nicm
b22edcf3a5 Tweak previous - find end of style correctly. 2022-06-27 09:16:54 +00:00
nicm
786cff8db9 Do not expand single character format aliases inside #[] since they
interfere with colours. GitHub issue 3239 from Magnus Gross.
2022-06-27 09:14:49 +00:00
Thomas Adam
b63afaea61 Merge branch 'obsd-master' 2022-06-21 12:01:09 +01:00
nicm
9c89f7c2af Store time lines are scrolled into history and display in copy mode. 2022-06-21 09:30:01 +00:00
Thomas Adam
d46870ede5 Merge branch 'obsd-master' 2022-06-20 12:01:09 +01:00
nicm
a888ce9963 Do not display configuration file errors in a pane when in control mode,
instead report them with a %config-error notification. GitHub issue 3193.
2022-06-20 07:59:37 +00:00
Thomas Adam
8ff3091d16 Merge branch 'obsd-master' 2022-06-17 10:01:10 +01:00
nicm
d9f84854ac Check cursor options when a pane is created, not just when they are changed. 2022-06-17 07:28:05 +00:00
Thomas Adam
89fe2680a9 Merge branch 'obsd-master' 2022-06-16 16:01:08 +01:00
nicm
7cee982f90 Keep cursor on selected item on menu (useful for blind people), GitHub
issue 3225.
2022-06-16 13:27:39 +00:00
Nicholas Marriott
42358cc521 Typos from Bastian Venthur. 2022-06-15 08:01:50 +01:00
Thomas Adam
06869ff22f Merge branch 'obsd-master' 2022-06-14 10:01:08 +01:00
nicm
616bde08ac kf* terminfo capabilities are poorly defined and rxvt uses them in a
different way from xterm, so add a feature flag for rxvt to make tmux
ignore the capabilities and instead rely on its builtin definitions.
2022-06-14 07:29:00 +00:00
Thomas Adam
6d0828b81c Merge branch 'obsd-master' 2022-06-11 20:01:09 +01:00
nicm
42ddf02ffc Fix size of flags output buffer. 2022-06-11 16:59:33 +00:00
Thomas Adam
56390e0a39 Merge branch 'obsd-master' 2022-06-10 16:01:11 +01:00
nicm
18a5835aff Ignore OSC if the first argument is not properly terminated. 2022-06-10 11:55:30 +00:00
Nicholas Marriott
67960dcc9a Merge tag '3.3a'
3.3a
2022-06-09 13:07:18 +01:00
Thomas Adam
810daefdd1 Merge branch 'obsd-master' 2022-06-09 12:01:09 +01:00
nicm
ccc9dc3bb4 If an application gives the first parameter to OSC 52, validate and pass
on to outside terminal. GitHub issue 3192.
2022-06-09 09:12:55 +00:00
Thomas Adam
be2eb57d62 Merge branch 'obsd-master' 2022-06-07 14:01:09 +01:00
nicm
c07d582e24 Expand arguments to some commands where it makes sense, GitHub issue
3204 from Anindya Mukherjee.
2022-06-07 10:02:19 +00:00
Thomas Adam
afb3a5fe71 Merge branch 'obsd-master' 2022-06-04 10:01:09 +01:00
nicm
020c403dff When picking a buffer because one isn't specified by the user, ignore
named buffers. GitHub issue 3212 from David le Blanc.
2022-06-04 07:42:07 +00:00
Thomas Adam
e77e11ec6b Merge branch 'obsd-master' 2022-06-03 12:01:09 +01:00
nicm
3edda3c5e7 Do not unintentionally turn off all mouse mode when button is also present. 2022-06-03 08:09:16 +00:00
Thomas Adam
1184dc08d4 Merge branch 'obsd-master' 2022-06-03 00:01:08 +01:00
nicm
18838fbc87 Do not attempt to use client in config file (it will be NULL), GitHub
issue 3206.
2022-06-02 21:19:32 +00:00
Nicholas Marriott
be2617036f Remove extra definition of getpeereid. From Eric N Vander Weele in GitHub issue
3209.
2022-06-02 21:45:53 +01:00
nicm
0f6227f46b When deleting or renaming a buffer and a buffer name is specified,
complain if the buffer doesn't exist instead of silently deleting or
renaming the most recent buffer. GitHub issue 3205.
2022-06-02 20:41:21 +00:00
Thomas Adam
c1ac007576 Merge branch 'obsd-master' 2022-06-01 18:01:08 +01:00
nicm
201a8d8e7e If escape-time is 0, force to 1 instead - not waiting at all is asking
for problems on some platforms.
2022-06-01 15:43:22 +00:00
Nicholas Marriott
b566cd57bf Now back to 3.4. 2022-06-01 08:50:54 +01:00
152 changed files with 15240 additions and 3665 deletions

View File

@ -77,3 +77,8 @@ The log files are:
- `tmux-out*.log`: output log file.
Please attach the log files to your issue.
## What does it mean if an issue is closed?
All it means is that work on the issue is not planned for the near future. See
the issue's comments to find out if contributions would be welcome.

1
.github/ISSUE_TEMPLATE/config.yml vendored Normal file
View File

@ -0,0 +1 @@
blank_issues_enabled: false

View File

@ -1,3 +1,12 @@
---
name: Use this issue template
about: Please read https://github.com/tmux/tmux/blob/master/.github/CONTRIBUTING.md
title: ''
labels: ''
assignees: ''
---
### Issue description
Please read https://github.com/tmux/tmux/blob/master/.github/CONTRIBUTING.md

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/imomaliev/tmux-bash-completion
https://github.com/scop/bash-completion/blob/main/completions/tmux
For debugging, run tmux with `-v` or `-vv` to generate server and client log
files in the current directory.

View File

@ -3,21 +3,32 @@ name: 'Lock Threads'
on:
schedule:
- cron: '0 0 * * *'
workflow_dispatch:
permissions:
issues: write
pull-requests: write
discussions: write
concurrency:
group: lock-threads
jobs:
lock:
action:
runs-on: ubuntu-latest
steps:
- uses: dessant/lock-threads@v2
- uses: dessant/lock-threads@v5
with:
github-token: ${{ github.token }}
issue-lock-inactive-days: '30'
pr-lock-inactive-days: '60'
issue-lock-comment: >
issue-inactive-days: '30'
issue-comment: >
This issue has been automatically locked since there
has not been any recent activity after it was closed.
Please open a new issue for related bugs.
pr-lock-comment: >
pr-inactive-days: '60'
pr-comment: >
This pull request has been automatically locked since there
has not been any recent activity after it was closed.
Please open a new issue for related bugs.
discussion-inactive-days: '60'
discussion-comment: >
This discussion has been automatically locked since there
has not been any recent activity after it was closed.

35
.gitignore vendored
View File

@ -1,23 +1,24 @@
*.o
*~
*.diff
*.patch
*.core
core
tags
*.dSYM
*.diff
*.o
*.patch
*.swp
*~
.deps/
compat/.dirstamp
aclocal.m4
autom4te.cache/
config.log
config.status
etc/
tmux
.dirstamp
Makefile
Makefile.in
configure
tmux.1.*
*.dSYM
aclocal.m4
autom4te.cache/
cmd-parse.c
compat/.dirstamp
config.log
config.status
configure
core
etc/
fuzz/*-fuzzer
.dirstamp
tags
tmux
tmux.1.*

204
CHANGES
View File

@ -1,3 +1,205 @@
CHANGES FROM 3.4 TO 3.5
* Revamp extended keys support to more closely match xterm and support mode 2
as well as mode 1. This is a substantial change to key handling which changes
tmux to always request mode 2 from parent terminal, changes to an unambiguous
internal representation of keys, and adds an option (extended-keys-format) to
control the format similar to the xterm(1) formatOtherKeys resource.
* Clear an overlay (popup or menu) when command prompt is entered.
* Add copy-mode -d flag to scroll a page down if in copy mode already (matching
-e).
* Display hyperlinks in copy mode and add copy_cursor_hyperlink format to get
the hyperlink under the cursor.
* Add a prefix timeout option.
* Mouse move keys are not useful as key bindings because we do not turn them on
unless the application requests them. Ignore them so they do not cause the
prefix to be canceled
* Add search_count and search_count_partial formats in copy mode.
* Do not reset mouse pane if clicked on status line,
* Add mirrored versions of the main-horizontal and main-vertical layouts where
the main pane is bottom or right instead of top or left.
* Allow REP to work with Unicode characters.
* Fix size calculation of terminators for clipboard escape sequences.
* Treat CRLF as LF in config files where it is easy to do so.
* The Linux console has some bugs with bright colours, so add some workarounds
for it.
* If built with systemd, remove some environment variables it uses.
* Adjust the logic when deleting last buffer to better preserve the selection:
if selecting the element below the deleted one fails (because as the last
one), select the one above it instead.
* Add --enable-jemalloc to build with jemalloc memory allocator (since glibc
malloc is so poor).
* Add a way (refresh-client -r) for control mode clients to provide OSC 10 and
11 responses to tmux so they can set the default foreground and background
colours.
* Add N to search backwards in tree modes.
* Use default-shell for command prompt, #() and popups.
* Revert part of a change intended to improve search performance by skipping
parts of lines already searched, but which in fact skipped the ends of lines
altogether.
* Add a command-error hook when a command fails.
* Add an option allow-set-title to forbid applications from changing the pane
title.
* Correct handling of mouse up events (don't ignore all but the last released
button), and always process down event for double click.
* Fix a crash if focusing a pane that is exiting.
* Pick newest session (as documented) when looking for next session for
detach-on-destroy.
* Reduce default escape-time to 10 milliseconds.
* Add display-menu -M to always turn mouse on in a menu.
* Look for feature code 21 for DECSLRM and 28 for DECFRA in the device
attributes and also accept level 1.
* Fix crash if built with SIXEL and the SIXEL colour register is invalid; also
remove SIXEL images before reflow.
* Do not notify window-layout-changed if the window is about to be destroyed.
* Do not consider a selection present if it is empty for the selection_active
and selection_present format variables.
* Fix split-window -p.
CHANGES FROM 3.3a TO 3.4
* Add options keep-last and keep-group to destroy-unattached to keep the last
session whether in a group.
* Don't allow paste-buffer into dead panes.
* Add -t to source-file.
* Rewrite combined character handling to be more consistent and to support
newer Unicode combined characters.
* Add basic support for SIXEL if built with --enable-sixel.
* Add a session, pane and user mouse range types for the status line and add
format variables for mouse_status_line and mouse_status_range so they can be
associated with different commands in the key bindings.
* Add flag (-o) to next-prompt/previous-prompt to go to OSC 133 command output.
* Add options and flags for menu styles (menu-style, menu-border-style) similar
to those existing for popups.
* Add support for marking lines with a shell prompt based on the OSC 133 extension.
* Check for libterminfo for NetBSD.
* Add "us" to styles for underscore colour.
* Add flags (-c and -y) to change the confirm key and default behaviour of
confirm-before.
* Use ncurses' new tparm_s function (added in 6.4-20230424) instead of tparm so
it does not object to string arguments in c apabilities it doesn't already
know. Also ignore errors from tparm if using previous ncurses versions.
* Set default lock command to vlock on Linux if present at build time.
* Discard mouse sequences that have the right form but actually are invalid.
* Add support for spawning panes in separate cgroups with systemd and a
configure flag (--disable-cgroups) to turn off.
* Add a format (pane_unseen_changes) to show if there are unseen changes while
in a mode.
* Remove old buffer when renaming rather than complaining.
* Add an L modifier like P, W, S to loop over clients.
* Add -f to list-clients like the other list commands.
* Extend display-message to work for control clients.
* Add a flag to display-menu to select the manu item selected when the menu is
open.
* Have tmux recognise pasted text wrapped in bracket paste sequences, rather
than only forwarding them to the program inside.
* Have client return 1 if process is interrupted to an input pane.
* Query the client terminal for foreground and background colours and if OSC 10
or 11 is received but no colour has been set inside tmux, return the colour
from the first attached client.
* Add send-keys -K to handle keys directly as if typed (so look up in key
table).
* Process escape sequences in show-buffer.
* Add a -l flag to display-message to disable format expansion.
* Add paste-buffer-deleted notification and fix name of paste-buffer-changed.
* Do not attempt to connect to the socket as a client if systemd is active.
* Add scroll-top and scroll-bottom commands to scroll so cursor is at top or
bottom.
* Add a -T flag to capture-pane to stop at the last used cell instead of the
full width. Restore the previous behaviour by making it default to off unless
-J is used.
* Add message-line option to control where message and prompt go.
* Notification when a paste buffer is deleted.
* Add a Nobr terminfo(5) capability to tell tmux the terminal does not use bright
colours for bold.
* Change g and G to go to top and bottom in menus.
* Add a third state "all" to allow-passthrough to work even in invisible panes.
* Add support for OSC 8 hyperlinks.
* Store the time lines are scrolled into history and display in copy mode.
* Add a %config-error reply to control mode for configuration file errors since
reporting them in view mode is useless.
* A new feature flag (ignorefkeys) to ignore terminfo(5) function key
definitions for rxvt.
* Pass through first argument to OSC 52 (which clipboards to set) if the
application provides it.
* Expand arguments to send-keys, capture-pane, split-window, join-pane where it
makes sense to do so.
* Ignore named buffers when choosing a buffer if one is not specified by the user.
CHANGES FROM 3.3 TO 3.3a
* Do not crash when run-shell produces output from a config file.
@ -1302,7 +1504,7 @@ Incompatible Changes
bind -Tcopy-mode C-r command-prompt -i -p'search up' "send -X search-backward-incremental '%%'"
There are also some new commmands available with send -X, such as
There are also some new commands available with send -X, such as
copy-pipe-and-cancel.
* set-remain-on-exit has gone -- can be achieved with hooks instead.
* Hooks: before hooks have been removed and only a selection of commands now

View File

@ -1,5 +1,3 @@
# Makefile.am
# Obvious program stuff.
bin_PROGRAMS = tmux
CLEANFILES = tmux.1.mdoc tmux.1.man cmd-parse.c
@ -14,6 +12,7 @@ dist_EXTRA_tmux_SOURCES = compat/*.[ch]
AM_CPPFLAGS += @XOPEN_DEFINES@ \
-DTMUX_VERSION='"@VERSION@"' \
-DTMUX_CONF='"$(sysconfdir)/tmux.conf:~/.tmux.conf:$$XDG_CONFIG_HOME/tmux/tmux.conf:~/.config/tmux/tmux.conf"' \
-DTMUX_LOCK_CMD='"@DEFAULT_LOCK_CMD@"' \
-DTMUX_TERM='"@DEFAULT_TERM@"'
# Additional object files.
@ -31,7 +30,7 @@ AM_CFLAGS += -Wundef -Wbad-function-cast -Winline -Wcast-align
AM_CFLAGS += -Wdeclaration-after-statement -Wno-pointer-sign -Wno-attributes
AM_CFLAGS += -Wno-unused-result -Wno-format-y2k
if IS_DARWIN
AM_CFLAGS += -Wno-deprecated-declarations -Wno-cast-align
AM_CFLAGS += -Wno-deprecated-declarations -Wno-cast-align -Wno-macro-redefined
endif
AM_CPPFLAGS += -DDEBUG
endif
@ -67,6 +66,11 @@ if IS_HAIKU
AM_CPPFLAGS += -D_BSD_SOURCE
endif
# Set flags for Cygwin.
if IS_CYGWIN
AM_CPPFLAGS += -DTMUX_SOCK_PERM=0
endif
# List of sources.
dist_tmux_SOURCES = \
alerts.c \
@ -150,6 +154,7 @@ dist_tmux_SOURCES = \
grid-reader.c \
grid-view.c \
grid.c \
hyperlinks.c \
input-keys.c \
input.c \
job.c \
@ -189,6 +194,7 @@ dist_tmux_SOURCES = \
tty-keys.c \
tty-term.c \
tty.c \
utf8-combined.c \
utf8.c \
window-buffer.c \
window-client.c \
@ -216,6 +222,11 @@ if HAVE_UTF8PROC
nodist_tmux_SOURCES += compat/utf8proc.c
endif
# Enable sixel support.
if ENABLE_SIXEL
dist_tmux_SOURCES += image.c image-sixel.c
endif
if NEED_FUZZING
check_PROGRAMS = fuzz/input-fuzzer
fuzz_input_fuzzer_LDFLAGS = $(FUZZING_LIBS)

3
README
View File

@ -36,6 +36,7 @@ autoconf, automake and pkg-config:
$ cd tmux
$ sh autogen.sh
$ ./configure && make
$ sudo make install
* Contributing
@ -65,7 +66,7 @@ Also see the tmux FAQ at:
A bash(1) completion file is at:
https://github.com/imomaliev/tmux-bash-completion
https://github.com/scop/bash-completion/blob/main/completions/tmux
For debugging, run tmux with -v and -vv to generate server and client log files
in the current directory.

View File

@ -38,7 +38,7 @@ tmuxのドキュメントについてはtmux.1マニュアルをご覧くださ
サンプル設定は本リポジトリのexample_tmux.confに
また、bash-completionファイルは下記にあります。
https://github.com/imomaliev/tmux-bash-completion
https://github.com/scop/bash-completion/blob/main/completions/tmux
「-v」や「-vv」を指定することでデバッグモードでの起動が可能です。カレントディレクトリにサーバーやクライアントのログファイルが生成されます。

32
SYNCING
View File

@ -1,17 +1,17 @@
Preamble
========
Tmux portable relies on repositories "tmux" and "tmux-openbsd".
Tmux portable relies on repositories "tmux" and "tmux-obsd".
Here's a description of them:
* "tmux" is the portable version, the one which contains code for other
operating systems, and autotools, etc., which isn't found or needed in the
OpenBSD base system.
* "tmux-openbsd" is the version of tmux in OpenBSD base system which provides
* "tmux-obsd" is the version of tmux in OpenBSD base system which provides
the basis of the portable tmux version.
Note: The "tmux-openbsd" repository is actually handled by "git cvsimport"
Note: The "tmux-obsd" repository is actually handled by "git cvsimport"
running at 15 minute intervals, so a commit made to OpenBSD's tmux CVS
repository will take at least that long to appear in this git repository.
(It might take longer, depending on the CVS mirror used to import the
@ -34,11 +34,11 @@ this information has ever been set before.
Cloning repositories
====================
This involves having both tmux and tmux-openbsd cloned, as in:
This involves having both tmux and tmux-obsd cloned, as in:
% cd /some/where/useful
% git clone https://github.com/tmux/tmux.git
% git clone https://github.com/ThomasAdam/tmux-openbsd.git
% git clone https://github.com/ThomasAdam/tmux-obsd.git
Note that you do not need additional checkouts to manage the sync -- an
existing clone of either repositories will suffice. So if you already have
@ -47,30 +47,30 @@ these checkouts existing, skip that.
Adding in git-remotes
=====================
Because the portable "tmux" git repository and the "tmux-openbsd"
Because the portable "tmux" git repository and the "tmux-obsd"
repository do not inherently share any history between each other, the
history has been faked between them. This "faking of history" is something
which has to be told to git for the purposes of comparing the "tmux" and
"tmux-openbsd" repositories for syncing. To do this, we must reference the
clone of the "tmux-openbsd" repository from the "tmux" repository, as
"tmux-obsd" repositories for syncing. To do this, we must reference the
clone of the "tmux-obsd" repository from the "tmux" repository, as
shown by the following command:
% cd /path/to/tmux
% git remote add obsd-tmux file:///path/to/tmux-openbsd
% git remote add obsd-tmux file:///path/to/tmux-obsd
So that now, the remote "obsd-tmux" can be used to reference branches and
commits from the "tmux-openbsd" repository, but from the context of the
commits from the "tmux-obsd" repository, but from the context of the
portable "tmux" repository, which makes sense because it's the "tmux"
repository which will have the updates applied to them.
Fetching updates
================
To ensure the latest commits from "tmux-openbsd" can be found from within
"tmux", we have to ensure the "master" branch from "tmux-openbsd" is
To ensure the latest commits from "tmux-obsd" can be found from within
"tmux", we have to ensure the "master" branch from "tmux-obsd" is
up-to-date first, and then reference that update in "tmux", as in:
% cd /path/to/tmux-openbsd
% cd /path/to/tmux-obsd
% git checkout master
% git pull
@ -82,9 +82,9 @@ Then back in "tmux":
Creating the necessary branches
===============================
Now that "tmux" can see commits and branches from "tmux-openbsd" by way
Now that "tmux" can see commits and branches from "tmux-obsd" by way
of the remote name "obsd-tmux", we can now create the master branch from
"tmux-openbsd" in the "tmux" repository:
"tmux-obsd" in the "tmux" repository:
% git checkout -b obsd-master obsd-tmux/master
@ -92,7 +92,7 @@ Adding in the fake history points
=================================
To tie both the "master" branch from "tmux" and the "obsd-master"
branch from "tmux-openbsd" together, the fake history points added to the
branch from "tmux-obsd" together, the fake history points added to the
"tmux" repository need to be added. To do this, we must add an
additional refspec line, as in:

View File

@ -315,11 +315,11 @@ alerts_set_message(struct winlink *wl, const char *type, const char *option)
if (visual == VISUAL_OFF)
continue;
if (c->session->curw == wl) {
status_message_set(c, -1, 1, 0, "%s in current window",
type);
status_message_set(c, -1, 1, 0, 0,
"%s in current window", type);
} else {
status_message_set(c, -1, 1, 0, "%s in window %d", type,
wl->idx);
status_message_set(c, -1, 1, 0, 0,
"%s in window %d", type, wl->idx);
}
}
}

View File

@ -37,6 +37,10 @@ struct args_entry {
u_char flag;
struct args_values values;
u_int count;
int flags;
#define ARGS_ENTRY_OPTIONAL_VALUE 0x1
RB_ENTRY(args_entry) entry;
};
@ -94,6 +98,22 @@ args_copy_value(struct args_value *to, struct args_value *from)
}
}
/* Type to string. */
static const char *
args_type_to_string (enum args_type type)
{
switch (type)
{
case ARGS_NONE:
return "NONE";
case ARGS_STRING:
return "STRING";
case ARGS_COMMANDS:
return "COMMANDS";
}
return "INVALID";
}
/* Get value as string. */
static const char *
args_value_as_string(struct args_value *value)
@ -122,6 +142,103 @@ args_create(void)
return (args);
}
/* Parse a single flag. */
static int
args_parse_flag_argument(struct args_value *values, u_int count, char **cause,
struct args *args, u_int *i, const char *string, int flag,
int optional_argument)
{
struct args_value *argument, *new;
const char *s;
new = xcalloc(1, sizeof *new);
if (*string != '\0') {
new->type = ARGS_STRING;
new->string = xstrdup(string);
goto out;
}
if (*i == count)
argument = NULL;
else {
argument = &values[*i];
if (argument->type != ARGS_STRING) {
xasprintf(cause, "-%c argument must be a string", flag);
args_free_value(new);
free(new);
return (-1);
}
}
if (argument == NULL) {
args_free_value(new);
free(new);
if (optional_argument) {
log_debug("%s: -%c (optional)", __func__, flag);
args_set(args, flag, NULL, ARGS_ENTRY_OPTIONAL_VALUE);
return (0); /* either - or end */
}
xasprintf(cause, "-%c expects an argument", flag);
return (-1);
}
args_copy_value(new, argument);
(*i)++;
out:
s = args_value_as_string(new);
log_debug("%s: -%c = %s", __func__, flag, s);
args_set(args, flag, new, 0);
return (0);
}
/* Parse flags argument. */
static int
args_parse_flags(const struct args_parse *parse, struct args_value *values,
u_int count, char **cause, struct args *args, u_int *i)
{
struct args_value *value;
u_char flag;
const char *found, *string;
int optional_argument;
value = &values[*i];
if (value->type != ARGS_STRING)
return (1);
string = value->string;
log_debug("%s: next %s", __func__, string);
if (*string++ != '-' || *string == '\0')
return (1);
(*i)++;
if (string[0] == '-' && string[1] == '\0')
return (1);
for (;;) {
flag = *string++;
if (flag == '\0')
return (0);
if (flag == '?')
return (-1);
if (!isalnum(flag)) {
xasprintf(cause, "invalid flag -%c", flag);
return (-1);
}
found = strchr(parse->template, flag);
if (found == NULL) {
xasprintf(cause, "unknown flag -%c", flag);
return (-1);
}
if (found[1] != ':') {
log_debug("%s: -%c", __func__, flag);
args_set(args, flag, NULL, 0);
continue;
}
optional_argument = (found[2] == ':');
return (args_parse_flag_argument(values, count, cause, args, i,
string, flag, optional_argument));
}
}
/* Parse arguments into a new argument set. */
struct args *
args_parse(const struct args_parse *parse, struct args_value *values,
@ -131,95 +248,30 @@ args_parse(const struct args_parse *parse, struct args_value *values,
u_int i;
enum args_parse_type type;
struct args_value *value, *new;
u_char flag;
const char *found, *string, *s;
int optional_argument;
const char *s;
int stop;
if (count == 0)
return (args_create());
args = args_create();
for (i = 1; i < count; /* nothing */) {
value = &values[i];
if (value->type != ARGS_STRING)
break;
string = value->string;
if (*string++ != '-' || *string == '\0')
break;
i++;
if (string[0] == '-' && string[1] == '\0')
break;
for (;;) {
flag = *string++;
if (flag == '\0')
break;
if (flag == '?') {
stop = args_parse_flags(parse, values, count, cause, args, &i);
if (stop == -1) {
args_free(args);
return (NULL);
}
if (!isalnum(flag)) {
xasprintf(cause, "invalid flag -%c", flag);
args_free(args);
return (NULL);
}
found = strchr(parse->template, flag);
if (found == NULL) {
xasprintf(cause, "unknown flag -%c", flag);
args_free(args);
return (NULL);
}
if (*++found != ':') {
log_debug("%s: -%c", __func__, flag);
args_set(args, flag, NULL);
continue;
}
if (*found == ':') {
optional_argument = 1;
found++;
}
new = xcalloc(1, sizeof *new);
if (*string != '\0') {
new->type = ARGS_STRING;
new->string = xstrdup(string);
} else {
if (i == count) {
if (optional_argument) {
log_debug("%s: -%c", __func__,
flag);
args_set(args, flag, NULL);
continue;
}
xasprintf(cause,
"-%c expects an argument",
flag);
args_free(args);
return (NULL);
}
if (values[i].type != ARGS_STRING) {
xasprintf(cause,
"-%c argument must be a string",
flag);
args_free(args);
return (NULL);
}
args_copy_value(new, &values[i++]);
}
s = args_value_as_string(new);
log_debug("%s: -%c = %s", __func__, flag, s);
args_set(args, flag, new);
if (stop == 1)
break;
}
}
log_debug("%s: flags end at %u of %u", __func__, i, count);
if (i != count) {
for (/* nothing */; i < count; i++) {
value = &values[i];
s = args_value_as_string(value);
log_debug("%s: %u = %s (type %d)", __func__, i, s,
value->type);
log_debug("%s: %u = %s (type %s)", __func__, i, s,
args_type_to_string (value->type));
if (parse->cb != NULL) {
type = parse->cb(args, args->count, cause);
@ -323,13 +375,13 @@ args_copy(struct args *args, int argc, char **argv)
RB_FOREACH(entry, args_tree, &args->tree) {
if (TAILQ_EMPTY(&entry->values)) {
for (i = 0; i < entry->count; i++)
args_set(new_args, entry->flag, NULL);
args_set(new_args, entry->flag, NULL, 0);
continue;
}
TAILQ_FOREACH(value, &entry->values, entry) {
new_value = xcalloc(1, sizeof *new_value);
args_copy_copy_value(new_value, value, argc, argv);
args_set(new_args, entry->flag, new_value);
args_set(new_args, entry->flag, new_value, 0);
}
}
if (args->count == 0)
@ -487,6 +539,7 @@ args_print(struct args *args)
char *buf;
u_int i, j;
struct args_entry *entry;
struct args_entry *last = NULL;
struct args_value *value;
len = 1;
@ -494,6 +547,8 @@ args_print(struct args *args)
/* Process the flags first. */
RB_FOREACH(entry, args_tree, &args->tree) {
if (entry->flags & ARGS_ENTRY_OPTIONAL_VALUE)
continue;
if (!TAILQ_EMPTY(&entry->values))
continue;
@ -505,6 +560,16 @@ args_print(struct args *args)
/* Then the flags with arguments. */
RB_FOREACH(entry, args_tree, &args->tree) {
if (entry->flags & ARGS_ENTRY_OPTIONAL_VALUE) {
if (*buf != '\0')
args_print_add(&buf, &len, " -%c", entry->flag);
else
args_print_add(&buf, &len, "-%c", entry->flag);
last = entry;
continue;
}
if (TAILQ_EMPTY(&entry->values))
continue;
TAILQ_FOREACH(value, &entry->values, entry) {
if (*buf != '\0')
args_print_add(&buf, &len, " -%c", entry->flag);
@ -512,7 +577,10 @@ args_print(struct args *args)
args_print_add(&buf, &len, "-%c", entry->flag);
args_print_add_value(&buf, &len, value);
}
last = entry;
}
if (last && (last->flags & ARGS_ENTRY_OPTIONAL_VALUE))
args_print_add(&buf, &len, " --");
/* And finally the argument vector. */
for (i = 0; i < args->count; i++)
@ -582,7 +650,7 @@ args_has(struct args *args, u_char flag)
/* Set argument value in the arguments tree. */
void
args_set(struct args *args, u_char flag, struct args_value *value)
args_set(struct args *args, u_char flag, struct args_value *value, int flags)
{
struct args_entry *entry;
@ -591,12 +659,15 @@ args_set(struct args *args, u_char flag, struct args_value *value)
entry = xcalloc(1, sizeof *entry);
entry->flag = flag;
entry->count = 1;
entry->flags = flags;
TAILQ_INIT(&entry->values);
RB_INSERT(args_tree, &args->tree, entry);
} else
entry->count++;
if (value != NULL && value->type != ARGS_NONE)
TAILQ_INSERT_TAIL(&entry->values, value, entry);
else
free(value);
}
/* Get argument value. Will be NULL if it isn't present. */
@ -696,6 +767,7 @@ args_make_commands_prepare(struct cmd *self, struct cmdq_item *item, u_int idx,
struct args_value *value;
struct args_command_state *state;
const char *cmd;
const char *file;
state = xcalloc(1, sizeof *state);
@ -722,7 +794,9 @@ args_make_commands_prepare(struct cmd *self, struct cmdq_item *item, u_int idx,
if (wait)
state->pi.item = item;
cmd_get_source(self, &state->pi.file, &state->pi.line);
cmd_get_source(self, &file, &state->pi.line);
if (file != NULL)
state->pi.file = xstrdup(file);
state->pi.c = tc;
if (state->pi.c != NULL)
state->pi.c->references++;
@ -747,6 +821,8 @@ args_make_commands(struct args_command_state *state, int argc, char **argv,
}
cmd = xstrdup(state->cmd);
log_debug("%s: %s", __func__, cmd);
cmd_log_argv(argc, argv, __func__);
for (i = 0; i < argc; i++) {
new_cmd = cmd_template_replace(cmd, argv[i], i + 1);
log_debug("%s: %%%u %s: %s", __func__, i + 1, argv[i], new_cmd);
@ -775,6 +851,7 @@ args_make_commands_free(struct args_command_state *state)
cmd_list_free(state->cmdlist);
if (state->pi.c != NULL)
server_client_unref(state->pi.c);
free((void *)state->pi.file);
free(state->cmd);
free(state);
}
@ -848,6 +925,41 @@ args_strtonum(struct args *args, u_char flag, long long minval,
return (ll);
}
/* Convert an argument value to a number, and expand formats. */
long long
args_strtonum_and_expand(struct args *args, u_char flag, long long minval,
long long maxval, struct cmdq_item *item, char **cause)
{
const char *errstr;
char *formatted;
long long ll;
struct args_entry *entry;
struct args_value *value;
if ((entry = args_find(args, flag)) == NULL) {
*cause = xstrdup("missing");
return (0);
}
value = TAILQ_LAST(&entry->values, args_values);
if (value == NULL ||
value->type != ARGS_STRING ||
value->string == NULL) {
*cause = xstrdup("missing");
return (0);
}
formatted = format_single_from_target(item, value->string);
ll = strtonum(formatted, minval, maxval, &errstr);
free(formatted);
if (errstr != NULL) {
*cause = xstrdup(errstr);
return (0);
}
*cause = NULL;
return (ll);
}
/* Convert an argument to a number which may be a percentage. */
long long
args_percentage(struct args *args, u_char flag, long long minval,
@ -860,6 +972,10 @@ args_percentage(struct args *args, u_char flag, long long minval,
*cause = xstrdup("missing");
return (0);
}
if (TAILQ_EMPTY(&entry->values)) {
*cause = xstrdup("empty");
return (0);
}
value = TAILQ_LAST(&entry->values, args_values)->string;
return (args_string_percentage(value, minval, maxval, curval, cause));
}
@ -874,6 +990,10 @@ args_string_percentage(const char *value, long long minval, long long maxval,
size_t valuelen = strlen(value);
char *copy;
if (valuelen == 0) {
*cause = xstrdup("empty");
return (0);
}
if (value[valuelen - 1] == '%') {
copy = xstrdup(value);
copy[valuelen - 1] = '\0';
@ -904,3 +1024,74 @@ args_string_percentage(const char *value, long long minval, long long maxval,
*cause = NULL;
return (ll);
}
/*
* Convert an argument to a number which may be a percentage, and expand
* formats.
*/
long long
args_percentage_and_expand(struct args *args, u_char flag, long long minval,
long long maxval, long long curval, struct cmdq_item *item, char **cause)
{
const char *value;
struct args_entry *entry;
if ((entry = args_find(args, flag)) == NULL) {
*cause = xstrdup("missing");
return (0);
}
if (TAILQ_EMPTY(&entry->values)) {
*cause = xstrdup("empty");
return (0);
}
value = TAILQ_LAST(&entry->values, args_values)->string;
return (args_string_percentage_and_expand(value, minval, maxval, curval,
item, cause));
}
/*
* Convert a string to a number which may be a percentage, and expand formats.
*/
long long
args_string_percentage_and_expand(const char *value, long long minval,
long long maxval, long long curval, struct cmdq_item *item, char **cause)
{
const char *errstr;
long long ll;
size_t valuelen = strlen(value);
char *copy, *f;
if (value[valuelen - 1] == '%') {
copy = xstrdup(value);
copy[valuelen - 1] = '\0';
f = format_single_from_target(item, copy);
ll = strtonum(f, 0, 100, &errstr);
free(f);
free(copy);
if (errstr != NULL) {
*cause = xstrdup(errstr);
return (0);
}
ll = (curval * ll) / 100;
if (ll < minval) {
*cause = xstrdup("too small");
return (0);
}
if (ll > maxval) {
*cause = xstrdup("too large");
return (0);
}
} else {
f = format_single_from_target(item, value);
ll = strtonum(f, minval, maxval, &errstr);
free(f);
if (errstr != NULL) {
*cause = xstrdup(errstr);
return (0);
}
}
*cause = NULL;
return (ll);
}

45
cfg.c
View File

@ -51,8 +51,7 @@ cfg_done(__unused struct cmdq_item *item, __unused void *data)
return (CMD_RETURN_NORMAL);
cfg_finished = 1;
if (!RB_EMPTY(&sessions))
cfg_show_causes(RB_MIN(sessions, &sessions));
cfg_show_causes(NULL);
if (cfg_item != NULL)
cmdq_continue(cfg_item);
@ -67,6 +66,7 @@ start_cfg(void)
{
struct client *c;
u_int i;
int flags = 0;
/*
* Configuration files are loaded without a client, so commands are run
@ -84,19 +84,17 @@ start_cfg(void)
cmdq_append(c, cfg_item);
}
for (i = 0; i < cfg_nfiles; i++) {
if (cfg_quiet)
load_cfg(cfg_files[i], c, NULL, CMD_PARSE_QUIET, NULL);
else
load_cfg(cfg_files[i], c, NULL, 0, NULL);
}
flags = CMD_PARSE_QUIET;
for (i = 0; i < cfg_nfiles; i++)
load_cfg(cfg_files[i], c, NULL, NULL, flags, NULL);
cmdq_append(NULL, cmdq_get_callback(cfg_done, NULL));
}
int
load_cfg(const char *path, struct client *c, struct cmdq_item *item, int flags,
struct cmdq_item **new_item)
load_cfg(const char *path, struct client *c, struct cmdq_item *item,
struct cmd_find_state *current, int flags, struct cmdq_item **new_item)
{
FILE *f;
struct cmd_parse_input pi;
@ -135,7 +133,7 @@ load_cfg(const char *path, struct client *c, struct cmdq_item *item, int flags,
}
if (item != NULL)
state = cmdq_copy_state(cmdq_get_state(item));
state = cmdq_copy_state(cmdq_get_state(item), current);
else
state = cmdq_new_state(NULL, NULL, 0);
cmdq_add_format(state, "current_file", "%s", pi.file);
@ -155,8 +153,8 @@ load_cfg(const char *path, struct client *c, struct cmdq_item *item, int flags,
int
load_cfg_from_buffer(const void *buf, size_t len, const char *path,
struct client *c, struct cmdq_item *item, int flags,
struct cmdq_item **new_item)
struct client *c, struct cmdq_item *item, struct cmd_find_state *current,
int flags, struct cmdq_item **new_item)
{
struct cmd_parse_input pi;
struct cmd_parse_result *pr;
@ -187,7 +185,7 @@ load_cfg_from_buffer(const void *buf, size_t len, const char *path,
}
if (item != NULL)
state = cmdq_copy_state(cmdq_get_state(item));
state = cmdq_copy_state(cmdq_get_state(item), current);
else
state = cmdq_new_state(NULL, NULL, 0);
cmdq_add_format(state, "current_file", "%s", pi.file);
@ -238,11 +236,29 @@ cfg_print_causes(struct cmdq_item *item)
void
cfg_show_causes(struct session *s)
{
struct client *c = TAILQ_FIRST(&clients);
struct window_pane *wp;
struct window_mode_entry *wme;
u_int i;
if (s == NULL || cfg_ncauses == 0)
if (cfg_ncauses == 0)
return;
if (c != NULL && (c->flags & CLIENT_CONTROL)) {
for (i = 0; i < cfg_ncauses; i++) {
control_write(c, "%%config-error %s", cfg_causes[i]);
free(cfg_causes[i]);
}
goto out;
}
if (s == NULL) {
if (c != NULL && c->session != NULL)
s = c->session;
else
s = RB_MIN(sessions, &sessions);
}
if (s == NULL || s->attached == 0) /* wait for an attached session */
return;
wp = s->curw->window->active;
@ -254,6 +270,7 @@ cfg_show_causes(struct session *s)
free(cfg_causes[i]);
}
out:
free(cfg_causes);
cfg_causes = NULL;
cfg_ncauses = 0;

View File

@ -245,9 +245,6 @@ client_main(struct event_base *base, int argc, char **argv, uint64_t flags,
u_int ncaps = 0;
struct args_value *values;
/* Ignore SIGCHLD now or daemon() in the server will leave a zombie. */
signal(SIGCHLD, SIG_IGN);
/* Set up the initial command. */
if (shell_command != NULL) {
msg = MSG_SHELL;
@ -284,6 +281,12 @@ client_main(struct event_base *base, int argc, char **argv, uint64_t flags,
log_debug("flags are %#llx", (unsigned long long)client_flags);
/* Initialize the client socket and start the server. */
#ifdef HAVE_SYSTEMD
if (systemd_activated()) {
/* socket-based activation, do not even try to be a client. */
fd = server_start(client_proc, flags, base, 0, NULL);
} else
#endif
fd = client_connect(base, socket_path, client_flags);
if (fd == -1) {
if (errno == ECONNREFUSED) {
@ -449,11 +452,12 @@ client_send_identify(const char *ttynam, const char *termname, char **caps,
{
char **ss;
size_t sslen;
int fd, flags = client_flags;
int fd;
uint64_t flags = client_flags;
pid_t pid;
u_int i;
proc_send(client_peer, MSG_IDENTIFY_FLAGS, -1, &flags, sizeof flags);
proc_send(client_peer, MSG_IDENTIFY_LONGFLAGS, -1, &flags, sizeof flags);
proc_send(client_peer, MSG_IDENTIFY_LONGFLAGS, -1, &client_flags,
sizeof client_flags);
@ -494,20 +498,10 @@ client_send_identify(const char *ttynam, const char *termname, char **caps,
static __dead void
client_exec(const char *shell, const char *shellcmd)
{
const char *name, *ptr;
char *argv0;
log_debug("shell %s, command %s", shell, shellcmd);
ptr = strrchr(shell, '/');
if (ptr != NULL && *(ptr + 1) != '\0')
name = ptr + 1;
else
name = shell;
if (client_flags & CLIENT_LOGIN)
xasprintf(&argv0, "-%s", name);
else
xasprintf(&argv0, "%s", name);
argv0 = shell_argv0(shell, !!(client_flags & CLIENT_LOGIN));
setenv("SHELL", shell, 1);
proc_clear_signals(client_proc, 1);
@ -527,11 +521,22 @@ client_signal(int sig)
{
struct sigaction sigact;
int status;
pid_t pid;
log_debug("%s: %s", __func__, strsignal(sig));
if (sig == SIGCHLD)
waitpid(WAIT_ANY, &status, WNOHANG);
else if (!client_attached) {
if (sig == SIGCHLD) {
for (;;) {
pid = waitpid(WAIT_ANY, &status, WNOHANG);
if (pid == 0)
break;
if (pid == -1) {
if (errno == ECHILD)
break;
log_debug("waitpid failed: %s",
strerror(errno));
}
}
} else if (!client_attached) {
if (sig == SIGTERM || sig == SIGHUP)
proc_exit(client_proc);
} else {
@ -694,6 +699,9 @@ client_dispatch_wait(struct imsg *imsg)
!(client_flags & CLIENT_CONTROL), client_file_check_cb,
NULL);
break;
case MSG_READ_CANCEL:
file_read_cancel(&client_files, imsg);
break;
case MSG_WRITE_OPEN:
file_write_open(&client_files, client_peer, imsg, 1,
!(client_flags & CLIENT_CONTROL), client_file_check_cb,

View File

@ -158,6 +158,9 @@ cmd_attach_session(struct cmdq_item *item, const char *tflag, int dflag,
c->flags |= CLIENT_ATTACHED;
}
if (cfg_finished)
cfg_show_causes(s);
return (CMD_RETURN_NORMAL);
}

View File

@ -38,7 +38,7 @@ const struct cmd_entry cmd_bind_key_entry = {
.args = { "nrN:T:", 1, -1, cmd_bind_key_args_parse },
.usage = "[-nr] [-T key-table] [-N note] key "
"[command [arguments]]",
"[command [argument ...]]",
.flags = CMD_AFTERHOOK,
.exec = cmd_bind_key_exec

View File

@ -99,7 +99,7 @@ cmd_break_pane_exec(struct cmd *self, struct cmdq_item *item)
w = wp->window = window_create(w->sx, w->sy, w->xpixel, w->ypixel);
options_set_parent(wp->options, w->options);
wp->flags |= PANE_STYLECHANGED;
wp->flags |= (PANE_STYLECHANGED|PANE_THEMECHANGED);
TAILQ_INSERT_HEAD(&w->panes, wp, entry);
w->active = wp;
w->latest = tc;
@ -115,6 +115,7 @@ cmd_break_pane_exec(struct cmd *self, struct cmdq_item *item)
layout_init(w, wp);
wp->flags |= PANE_CHANGED;
colour_palette_from_option(&wp->palette, wp->options);
if (idx == -1)
idx = -1 - options_get_number(dst_s->options, "base-index");

View File

@ -39,8 +39,8 @@ const struct cmd_entry cmd_capture_pane_entry = {
.name = "capture-pane",
.alias = "capturep",
.args = { "ab:CeE:JNpPqS:t:", 0, 0, NULL },
.usage = "[-aCeJNpPq] " CMD_BUFFER_USAGE " [-E end-line] "
.args = { "ab:CeE:JMNpPqS:Tt:", 0, 0, NULL },
.usage = "[-aCeJMNpPqT] " CMD_BUFFER_USAGE " [-E end-line] "
"[-S start-line] " CMD_TARGET_PANE_USAGE,
.target = { 't', CMD_FIND_PANE, 0 },
@ -53,8 +53,8 @@ const struct cmd_entry cmd_clear_history_entry = {
.name = "clear-history",
.alias = "clearhist",
.args = { "t:", 0, 0, NULL },
.usage = CMD_TARGET_PANE_USAGE,
.args = { "Ht:", 0, 0, NULL },
.usage = "[-H] " CMD_TARGET_PANE_USAGE,
.target = { 't', CMD_FIND_PANE, 0 },
@ -109,8 +109,10 @@ cmd_capture_pane_history(struct args *args, struct cmdq_item *item,
{
struct grid *gd;
const struct grid_line *gl;
struct screen *s;
struct grid_cell *gc = NULL;
int n, with_codes, escape_c0, join_lines, no_trim;
struct window_mode_entry *wme;
int n, join_lines, flags = 0;
u_int i, sx, top, bottom, tmp;
char *cause, *buf, *line;
const char *Sflag, *Eflag;
@ -126,14 +128,27 @@ cmd_capture_pane_history(struct args *args, struct cmdq_item *item,
}
return (xstrdup(""));
}
} else
s = &wp->base;
} else if (args_has(args, 'M')) {
wme = TAILQ_FIRST(&wp->modes);
if (wme != NULL && wme->mode->get_screen != NULL) {
s = wme->mode->get_screen (wme);
gd = s->grid;
} else {
s = &wp->base;
gd = wp->base.grid;
}
} else {
s = &wp->base;
gd = wp->base.grid;
}
Sflag = args_get(args, 'S');
if (Sflag != NULL && strcmp(Sflag, "-") == 0)
top = 0;
else {
n = args_strtonum(args, 'S', INT_MIN, SHRT_MAX, &cause);
n = args_strtonum_and_expand(args, 'S', INT_MIN, SHRT_MAX,
item, &cause);
if (cause != NULL) {
top = gd->hsize;
free(cause);
@ -149,7 +164,8 @@ cmd_capture_pane_history(struct args *args, struct cmdq_item *item,
if (Eflag != NULL && strcmp(Eflag, "-") == 0)
bottom = gd->hsize + gd->sy - 1;
else {
n = args_strtonum(args, 'E', INT_MIN, SHRT_MAX, &cause);
n = args_strtonum_and_expand(args, 'E', INT_MIN, SHRT_MAX,
item, &cause);
if (cause != NULL) {
bottom = gd->hsize + gd->sy - 1;
free(cause);
@ -167,15 +183,19 @@ cmd_capture_pane_history(struct args *args, struct cmdq_item *item,
top = tmp;
}
with_codes = args_has(args, 'e');
escape_c0 = args_has(args, 'C');
join_lines = args_has(args, 'J');
no_trim = args_has(args, 'N');
if (args_has(args, 'e'))
flags |= GRID_STRING_WITH_SEQUENCES;
if (args_has(args, 'C'))
flags |= GRID_STRING_ESCAPE_SEQUENCES;
if (!join_lines && !args_has(args, 'T'))
flags |= GRID_STRING_EMPTY_CELLS;
if (!join_lines && !args_has(args, 'N'))
flags |= GRID_STRING_TRIM_SPACES;
buf = NULL;
for (i = top; i <= bottom; i++) {
line = grid_string_cells(gd, 0, i, sx, &gc, with_codes,
escape_c0, !join_lines && !no_trim);
line = grid_string_cells(gd, 0, i, sx, &gc, flags, s);
linelen = strlen(line);
buf = cmd_capture_pane_append(buf, len, line, linelen);
@ -202,6 +222,8 @@ cmd_capture_pane_exec(struct cmd *self, struct cmdq_item *item)
if (cmd_get_entry(self) == &cmd_clear_history_entry) {
window_pane_reset_mode_all(wp);
grid_clear_history(wp->base.grid);
if (args_has(args, 'H'))
screen_reset_hyperlinks(wp->screen);
return (CMD_RETURN_NORMAL);
}

View File

@ -33,7 +33,7 @@ const struct cmd_entry cmd_choose_tree_entry = {
.name = "choose-tree",
.alias = NULL,
.args = { "F:f:GK:NO:rst:wZ", 0, 1, cmd_choose_tree_args_parse },
.args = { "F:f:GK:NO:rst:wyZ", 0, 1, cmd_choose_tree_args_parse },
.usage = "[-GNrswZ] [-F format] [-f filter] [-K key-format] "
"[-O sort-order] " CMD_TARGET_PANE_USAGE " [template]",
@ -47,7 +47,7 @@ const struct cmd_entry cmd_choose_client_entry = {
.name = "choose-client",
.alias = NULL,
.args = { "F:f:K:NO:rt:Z", 0, 1, cmd_choose_tree_args_parse },
.args = { "F:f:K:NO:rt:yZ", 0, 1, cmd_choose_tree_args_parse },
.usage = "[-NrZ] [-F format] [-f filter] [-K key-format] "
"[-O sort-order] " CMD_TARGET_PANE_USAGE " [template]",
@ -61,7 +61,7 @@ const struct cmd_entry cmd_choose_buffer_entry = {
.name = "choose-buffer",
.alias = NULL,
.args = { "F:f:K:NO:rt:Z", 0, 1, cmd_choose_tree_args_parse },
.args = { "F:f:K:NO:rt:yZ", 0, 1, cmd_choose_tree_args_parse },
.usage = "[-NrZ] [-F format] [-f filter] [-K key-format] "
"[-O sort-order] " CMD_TARGET_PANE_USAGE " [template]",
@ -75,7 +75,7 @@ const struct cmd_entry cmd_customize_mode_entry = {
.name = "customize-mode",
.alias = NULL,
.args = { "F:f:Nt:Z", 0, 0, NULL },
.args = { "F:f:Nt:yZ", 0, 0, NULL },
.usage = "[-NZ] [-F format] [-f filter] " CMD_TARGET_PANE_USAGE,
.target = { 't', CMD_FIND_PANE, 0 },
@ -100,7 +100,7 @@ cmd_choose_tree_exec(struct cmd *self, struct cmdq_item *item)
const struct window_mode *mode;
if (cmd_get_entry(self) == &cmd_choose_buffer_entry) {
if (paste_get_top(NULL) == NULL)
if (paste_is_empty())
return (CMD_RETURN_NORMAL);
mode = &window_buffer_mode;
} else if (cmd_get_entry(self) == &cmd_choose_client_entry) {

View File

@ -44,7 +44,7 @@ const struct cmd_entry cmd_command_prompt_entry = {
.args = { "1bFkiI:Np:t:T:", 0, 1, cmd_command_prompt_args_parse },
.usage = "[-1bFkiN] [-I inputs] [-p prompts] " CMD_TARGET_CLIENT_USAGE
" [-T type] [template]",
" [-T prompt-type] [template]",
.flags = CMD_CLIENT_TFLAG,
.exec = cmd_command_prompt_exec
@ -143,6 +143,7 @@ cmd_command_prompt_exec(struct cmd *self, struct cmdq_item *item)
cdata->prompt_type = status_prompt_type(type);
if (cdata->prompt_type == PROMPT_TYPE_INVALID) {
cmdq_error(item, "unknown type: %s", type);
cmd_command_prompt_free(cdata);
return (CMD_RETURN_ERROR);
}
} else
@ -179,10 +180,10 @@ cmd_command_prompt_callback(struct client *c, void *data, const char *s,
if (s == NULL)
goto out;
if (done) {
if (cdata->flags & PROMPT_INCREMENTAL)
goto out;
cmd_append_argv(&cdata->argc, &cdata->argv, s);
if (++cdata->current != cdata->count) {
prompt = &cdata->prompts[cdata->current];
@ -193,8 +194,11 @@ cmd_command_prompt_callback(struct client *c, void *data, const char *s,
argc = cdata->argc;
argv = cmd_copy_argv(cdata->argc, cdata->argv);
if (!done)
cmd_append_argv(&argc, &argv, s);
if (done) {
cmd_free_argv(cdata->argc, cdata->argv);
cdata->argc = argc;
cdata->argv = cmd_copy_argv(argc, argv);
}

View File

@ -41,8 +41,9 @@ const struct cmd_entry cmd_confirm_before_entry = {
.name = "confirm-before",
.alias = "confirm",
.args = { "bp:t:", 1, 1, cmd_confirm_before_args_parse },
.usage = "[-b] [-p prompt] " CMD_TARGET_CLIENT_USAGE " command",
.args = { "bc:p:t:y", 1, 1, cmd_confirm_before_args_parse },
.usage = "[-by] [-c confirm-key] [-p prompt] " CMD_TARGET_CLIENT_USAGE
" command",
.flags = CMD_CLIENT_TFLAG,
.exec = cmd_confirm_before_exec
@ -51,6 +52,8 @@ const struct cmd_entry cmd_confirm_before_entry = {
struct cmd_confirm_before_data {
struct cmdq_item *item;
struct cmd_list *cmdlist;
u_char confirm_key;
int default_yes;
};
static enum args_parse_type
@ -68,22 +71,40 @@ cmd_confirm_before_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);
char *new_prompt;
const char *prompt, *cmd;
const char *confirm_key, *prompt, *cmd;
int wait = !args_has(args, 'b');
cdata = xcalloc(1, sizeof *cdata);
cdata->cmdlist = args_make_commands_now(self, item, 0, 1);
if (cdata->cmdlist == NULL)
if (cdata->cmdlist == NULL) {
free(cdata);
return (CMD_RETURN_ERROR);
}
if (wait)
cdata->item = item;
cdata->default_yes = args_has(args, 'y');
if ((confirm_key = args_get(args, 'c')) != NULL) {
if (confirm_key[1] == '\0' &&
confirm_key[0] > 31 &&
confirm_key[0] < 127)
cdata->confirm_key = confirm_key[0];
else {
cmdq_error(item, "invalid confirm key");
free(cdata);
return (CMD_RETURN_ERROR);
}
}
else
cdata->confirm_key = 'y';
if ((prompt = args_get(args, 'p')) != NULL)
xasprintf(&new_prompt, "%s ", prompt);
else {
cmd = cmd_get_entry(cmd_list_first(cdata->cmdlist))->name;
xasprintf(&new_prompt, "Confirm '%s'? (y/n) ", cmd);
xasprintf(&new_prompt, "Confirm '%s'? (%c/n) ", cmd,
cdata->confirm_key);
}
status_prompt_set(tc, target, new_prompt, NULL,
@ -107,9 +128,9 @@ cmd_confirm_before_callback(struct client *c, void *data, const char *s,
if (c->flags & CLIENT_DEAD)
goto out;
if (s == NULL || *s == '\0')
if (s == NULL)
goto out;
if (tolower((u_char)s[0]) != 'y' || s[1] != '\0')
if (s[0] != cdata->confirm_key && (s[0] != '\r' || !cdata->default_yes))
goto out;
retcode = 0;

View File

@ -30,8 +30,8 @@ const struct cmd_entry cmd_copy_mode_entry = {
.name = "copy-mode",
.alias = NULL,
.args = { "eHMs:t:uq", 0, 0, NULL },
.usage = "[-eHMuq] [-s src-pane] " CMD_TARGET_PANE_USAGE,
.args = { "deHMqSs:t:u", 0, 0, NULL },
.usage = "[-deHMqSu] [-s src-pane] " CMD_TARGET_PANE_USAGE,
.source = { 's', CMD_FIND_PANE, 0 },
.target = { 't', CMD_FIND_PANE, 0 },
@ -91,6 +91,13 @@ cmd_copy_mode_exec(struct cmd *self, struct cmdq_item *item)
}
if (args_has(args, 'u'))
window_copy_pageup(wp, 0);
if (args_has(args, 'd'))
window_copy_pagedown(wp, 0, args_has(args, 'e'));
if (args_has(args, 'S')) {
window_copy_scroll(wp, c->tty.mouse_slider_mpos, event->m.y,
args_has(args, 'e'));
return (CMD_RETURN_NORMAL);
}
return (CMD_RETURN_NORMAL);
}

View File

@ -38,9 +38,11 @@ const struct cmd_entry cmd_display_menu_entry = {
.name = "display-menu",
.alias = "menu",
.args = { "c:t:OT:x:y:", 1, -1, cmd_display_menu_args_parse },
.usage = "[-O] [-c target-client] " CMD_TARGET_PANE_USAGE " [-T title] "
"[-x position] [-y position] name key command ...",
.args = { "b:c:C:H:s:S:MOt:T:x:y:", 1, -1, cmd_display_menu_args_parse },
.usage = "[-MO] [-b border-lines] [-c target-client] "
"[-C starting-choice] [-H selected-style] [-s style] "
"[-S border-style] " CMD_TARGET_PANE_USAGE " [-T title] "
"[-x position] [-y position] name [key] [command] ...",
.target = { 't', CMD_FIND_PANE, 0 },
@ -57,7 +59,7 @@ const struct cmd_entry cmd_display_popup_entry = {
"[-d start-directory] [-e environment] [-h height] "
"[-s style] [-S border-style] " CMD_TARGET_PANE_USAGE
" [-T title] [-w width] [-x position] [-y position] "
"[shell-command]",
"[shell-command [argument ...]]",
.target = { 't', CMD_FIND_PANE, 0 },
@ -274,6 +276,7 @@ cmd_display_menu_get_position(struct client *tc, struct cmdq_item *item,
log_debug("%s: -y: %s = %s = %u (-h %u)", __func__, yp, p, *py, h);
free(p);
format_free(ft);
return (1);
}
@ -286,19 +289,41 @@ cmd_display_menu_exec(struct cmd *self, struct cmdq_item *item)
struct client *tc = cmdq_get_target_client(item);
struct menu *menu = NULL;
struct menu_item menu_item;
const char *key, *name;
char *title;
int flags = 0;
const char *key, *name, *value;
const char *style = args_get(args, 's');
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;
int flags = 0, starting_choice = 0;
u_int px, py, i, count = args_count(args);
struct options *o = target->s->curw->window->options;
struct options_entry *oe;
if (tc->overlay_draw != NULL)
return (CMD_RETURN_NORMAL);
if (args_has(args, 'C')) {
if (strcmp(args_get(args, 'C'), "-") == 0)
starting_choice = -1;
else {
starting_choice = args_strtonum(args, 'C', 0, UINT_MAX,
&cause);
if (cause != NULL) {
cmdq_error(item, "starting choice %s", cause);
free(cause);
return (CMD_RETURN_ERROR);
}
}
}
if (args_has(args, 'T'))
title = format_single_from_target(item, args_get(args, 'T'));
else
title = xstrdup("");
menu = menu_create(title);
free(title);
for (i = 0; i != count; /* nothing */) {
name = args_string(args, i++);
@ -309,7 +334,6 @@ cmd_display_menu_exec(struct cmd *self, struct cmdq_item *item)
if (count - i < 2) {
cmdq_error(item, "not enough arguments");
free(title);
menu_free(menu);
return (CMD_RETURN_ERROR);
}
@ -321,7 +345,6 @@ cmd_display_menu_exec(struct cmd *self, struct cmdq_item *item)
menu_add_item(menu, &menu_item, item, tc, target);
}
free(title);
if (menu == NULL) {
cmdq_error(item, "invalid menu arguments");
return (CMD_RETURN_ERROR);
@ -336,12 +359,24 @@ cmd_display_menu_exec(struct cmd *self, struct cmdq_item *item)
return (CMD_RETURN_NORMAL);
}
value = args_get(args, 'b');
if (value != NULL) {
oe = options_get(o, "menu-border-lines");
lines = options_find_choice(options_table_entry(oe), value,
&cause);
if (lines == -1) {
cmdq_error(item, "menu-border-lines %s", cause);
free(cause);
return (CMD_RETURN_ERROR);
}
}
if (args_has(args, 'O'))
flags |= MENU_STAYOPEN;
if (!event->m.valid)
if (!event->m.valid && !args_has(args, 'M'))
flags |= MENU_NOMOUSE;
if (menu_display(menu, flags, item, px, py, tc, target, NULL,
NULL) != 0)
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);
return (CMD_RETURN_WAIT);
}
@ -454,11 +489,13 @@ cmd_display_popup_exec(struct cmd *self, struct cmdq_item *item)
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);
free(cwd);
free(title);
cmd_free_argv(argc, argv);
return (CMD_RETURN_WAIT);

View File

@ -39,8 +39,8 @@ const struct cmd_entry cmd_display_message_entry = {
.name = "display-message",
.alias = "display",
.args = { "ac:d:INpt:F:v", 0, 1, NULL },
.usage = "[-aINpv] [-c target-client] [-d delay] [-F format] "
.args = { "aCc:d:lINpt:F:v", 0, 1, NULL },
.usage = "[-aCIlNpv] [-c target-client] [-d delay] [-F format] "
CMD_TARGET_PANE_USAGE " [message]",
.target = { 't', CMD_FIND_PANE, CMD_FIND_CANFAIL },
@ -68,9 +68,11 @@ cmd_display_message_exec(struct cmd *self, struct cmdq_item *item)
struct window_pane *wp = target->wp;
const char *template;
char *msg, *cause;
int delay = -1, flags;
int delay = -1, flags, Nflag = args_has(args, 'N');
int Cflag = args_has(args, 'C');
struct format_tree *ft;
u_int count = args_count(args);
struct evbuffer *evb;
if (args_has(args, 'I')) {
if (wp == NULL)
@ -132,15 +134,24 @@ cmd_display_message_exec(struct cmd *self, struct cmdq_item *item)
return (CMD_RETURN_NORMAL);
}
if (args_has(args, 'l'))
msg = xstrdup(template);
else
msg = format_expand_time(ft, template);
if (cmdq_get_client(item) == NULL)
cmdq_error(item, "%s", msg);
else if (args_has(args, 'p'))
cmdq_print(item, "%s", msg);
else if (tc != NULL) {
status_message_set(tc, delay, 0, args_has(args, 'N'), "%s",
msg);
}
else if (tc != NULL && (tc->flags & CLIENT_CONTROL)) {
evb = evbuffer_new();
if (evb == NULL)
fatalx("out of memory");
evbuffer_add_printf(evb, "%%message %s", msg);
server_client_print(tc, 0, evb);
evbuffer_free(evb);
} else if (tc != NULL)
status_message_set(tc, delay, 0, Nflag, Cflag, "%s", msg);
free(msg);
format_free(ft);

View File

@ -144,7 +144,7 @@ cmd_display_panes_draw_pane(struct screen_redraw_ctx *ctx,
llen = 0;
if (sx < len * 6 || sy < 5) {
tty_attributes(tty, &fgc, &grid_default_cell, NULL);
tty_attributes(tty, &fgc, &grid_default_cell, NULL, NULL);
if (sx >= len + llen + 1) {
len += llen + 1;
tty_cursor(tty, xoff + px - len / 2, yoff + py);
@ -161,7 +161,7 @@ cmd_display_panes_draw_pane(struct screen_redraw_ctx *ctx,
px -= len * 3;
py -= 2;
tty_attributes(tty, &bgc, &grid_default_cell, NULL);
tty_attributes(tty, &bgc, &grid_default_cell, NULL, NULL);
for (ptr = buf; *ptr != '\0'; ptr++) {
if (*ptr < '0' || *ptr > '9')
continue;
@ -179,7 +179,7 @@ cmd_display_panes_draw_pane(struct screen_redraw_ctx *ctx,
if (sy <= 6)
goto out;
tty_attributes(tty, &fgc, &grid_default_cell, NULL);
tty_attributes(tty, &fgc, &grid_default_cell, NULL, NULL);
if (rlen != 0 && sx >= rlen) {
tty_cursor(tty, xoff + sx - rlen, yoff);
tty_putn(tty, rbuf, rlen, rlen);
@ -246,7 +246,7 @@ cmd_display_panes_key(struct client *c, void *data, struct key_event *event)
wp = window_pane_at_index(w, index);
if (wp == NULL)
return (1);
window_unzoom(w);
window_unzoom(w, 1);
xasprintf(&expanded, "%%%u", wp->id);

View File

@ -48,6 +48,7 @@ cmd_find_window_exec(struct cmd *self, struct cmdq_item *item)
struct cmd_find_state *target = cmdq_get_target(item);
struct window_pane *wp = target->wp;
const char *s = args_string(args, 0), *suffix = "";
const char *star = "*";
struct args_value *filter;
int C, N, T;
@ -55,6 +56,8 @@ cmd_find_window_exec(struct cmd *self, struct cmdq_item *item)
N = args_has(args, 'N');
T = args_has(args, 'T');
if (args_has(args, 'r'))
star = "";
if (args_has(args, 'r') && args_has(args, 'i'))
suffix = "/ri";
else if (args_has(args, 'r'))
@ -71,40 +74,40 @@ cmd_find_window_exec(struct cmd *self, struct cmdq_item *item)
if (C && N && T) {
xasprintf(&filter->string,
"#{||:"
"#{C%s:%s},#{||:#{m%s:*%s*,#{window_name}},"
"#{m%s:*%s*,#{pane_title}}}}",
suffix, s, suffix, s, suffix, s);
"#{C%s:%s},#{||:#{m%s:%s%s%s,#{window_name}},"
"#{m%s:%s%s%s,#{pane_title}}}}",
suffix, s, suffix, star, s, star, suffix, star, s, star);
} else if (C && N) {
xasprintf(&filter->string,
"#{||:#{C%s:%s},#{m%s:*%s*,#{window_name}}}",
suffix, s, suffix, s);
"#{||:#{C%s:%s},#{m%s:%s%s%s,#{window_name}}}",
suffix, s, suffix, star, s, star);
} else if (C && T) {
xasprintf(&filter->string,
"#{||:#{C%s:%s},#{m%s:*%s*,#{pane_title}}}",
suffix, s, suffix, s);
"#{||:#{C%s:%s},#{m%s:%s%s%s,#{pane_title}}}",
suffix, s, suffix, star, s, star);
} else if (N && T) {
xasprintf(&filter->string,
"#{||:#{m%s:*%s*,#{window_name}},"
"#{m%s:*%s*,#{pane_title}}}",
suffix, s, suffix, s);
"#{||:#{m%s:%s%s%s,#{window_name}},"
"#{m%s:%s%s%s,#{pane_title}}}",
suffix, star, s, star, suffix, star, s, star);
} else if (C) {
xasprintf(&filter->string,
"#{C%s:%s}",
suffix, s);
} else if (N) {
xasprintf(&filter->string,
"#{m%s:*%s*,#{window_name}}",
suffix, s);
"#{m%s:%s%s%s,#{window_name}}",
suffix, star, s, star);
} else {
xasprintf(&filter->string,
"#{m%s:*%s*,#{pane_title}}",
suffix, s);
"#{m%s:%s%s%s,#{pane_title}}",
suffix, star, s, star);
}
new_args = args_create();
if (args_has(args, 'Z'))
args_set(new_args, 'Z', NULL);
args_set(new_args, 'f', filter);
args_set(new_args, 'Z', NULL, 0);
args_set(new_args, 'f', filter, 0);
window_pane_set_mode(wp, NULL, &window_tree_mode, target, new_args);
args_free(new_args);

View File

@ -582,27 +582,27 @@ cmd_find_get_pane_with_window(struct cmd_find_state *fs, const char *pane)
/* Try special characters. */
if (strcmp(pane, "!") == 0) {
fs->wp = fs->w->last;
fs->wp = TAILQ_FIRST(&fs->w->last_panes);
if (fs->wp == NULL)
return (-1);
return (0);
} else if (strcmp(pane, "{up-of}") == 0) {
fs->wp = window_pane_find_up(fs->current->wp);
fs->wp = window_pane_find_up(fs->w->active);
if (fs->wp == NULL)
return (-1);
return (0);
} else if (strcmp(pane, "{down-of}") == 0) {
fs->wp = window_pane_find_down(fs->current->wp);
fs->wp = window_pane_find_down(fs->w->active);
if (fs->wp == NULL)
return (-1);
return (0);
} else if (strcmp(pane, "{left-of}") == 0) {
fs->wp = window_pane_find_left(fs->current->wp);
fs->wp = window_pane_find_left(fs->w->active);
if (fs->wp == NULL)
return (-1);
return (0);
} else if (strcmp(pane, "{right-of}") == 0) {
fs->wp = window_pane_find_right(fs->current->wp);
fs->wp = window_pane_find_right(fs->w->active);
if (fs->wp == NULL)
return (-1);
return (0);
@ -614,7 +614,7 @@ cmd_find_get_pane_with_window(struct cmd_find_state *fs, const char *pane)
n = strtonum(pane + 1, 1, INT_MAX, NULL);
else
n = 1;
wp = fs->current->wp;
wp = fs->w->active;
if (pane[0] == '+')
fs->wp = window_pane_next_by_number(fs->w, wp, n);
else

View File

@ -157,7 +157,7 @@ cmd_if_shell_callback(struct job *job)
if (cmdlist == NULL) {
if (cdata->item == NULL) {
*error = toupper((u_char)*error);
status_message_set(c, -1, 1, 0, "%s", error);
status_message_set(c, -1, 1, 0, 0, "%s", error);
} else
cmdq_error(cdata->item, "%s", error);
free(error);

View File

@ -71,10 +71,11 @@ cmd_join_pane_exec(struct cmd *self, struct cmdq_item *item)
struct window *src_w, *dst_w;
struct window_pane *src_wp, *dst_wp;
char *cause = NULL;
int size, percentage, dst_idx;
int size, dst_idx;
int flags;
enum layout_type type;
struct layout_cell *lc;
u_int curval = 0;
dst_s = target->s;
dst_wl = target->wl;
@ -97,23 +98,30 @@ cmd_join_pane_exec(struct cmd *self, struct cmdq_item *item)
if (args_has(args, 'h'))
type = LAYOUT_LEFTRIGHT;
/* If the 'p' flag is dropped then this bit can be moved into 'l'. */
if (args_has(args, 'l') || args_has(args, 'p')) {
if (args_has(args, 'f')) {
if (type == LAYOUT_TOPBOTTOM)
curval = dst_w->sy;
else
curval = dst_w->sx;
} else {
if (type == LAYOUT_TOPBOTTOM)
curval = dst_wp->sy;
else
curval = dst_wp->sx;
}
}
size = -1;
if (args_has(args, 'l')) {
if (type == LAYOUT_TOPBOTTOM) {
size = args_percentage(args, 'l', 0, INT_MAX,
dst_wp->sy, &cause);
} else {
size = args_percentage(args, 'l', 0, INT_MAX,
dst_wp->sx, &cause);
}
size = args_percentage_and_expand(args, 'l', 0, INT_MAX, curval,
item, &cause);
} else if (args_has(args, 'p')) {
percentage = args_strtonum(args, 'p', 0, 100, &cause);
if (cause == NULL) {
if (type == LAYOUT_TOPBOTTOM)
size = (dst_wp->sy * percentage) / 100;
else
size = (dst_wp->sx * percentage) / 100;
}
size = args_strtonum_and_expand(args, 'l', 0, 100, item,
&cause);
if (cause == NULL)
size = curval * size / 100;
}
if (cause != NULL) {
cmdq_error(item, "size %s", cause);
@ -141,12 +149,13 @@ cmd_join_pane_exec(struct cmd *self, struct cmdq_item *item)
src_wp->window = dst_w;
options_set_parent(src_wp->options, dst_w->options);
src_wp->flags |= PANE_STYLECHANGED;
src_wp->flags |= (PANE_STYLECHANGED|PANE_THEMECHANGED);
if (flags & SPAWN_BEFORE)
TAILQ_INSERT_BEFORE(dst_wp, src_wp, entry);
else
TAILQ_INSERT_AFTER(&dst_w->panes, dst_wp, src_wp, entry);
layout_assign_pane(lc, src_wp, 0);
colour_palette_from_option(&src_wp->palette, src_wp->options);
recalculate_sizes();

View File

@ -41,8 +41,8 @@ const struct cmd_entry cmd_list_clients_entry = {
.name = "list-clients",
.alias = "lsc",
.args = { "F:t:", 0, 0, NULL },
.usage = "[-F format] " CMD_TARGET_SESSION_USAGE,
.args = { "F:f:t:", 0, 0, NULL },
.usage = "[-F format] [-f filter] " CMD_TARGET_SESSION_USAGE,
.target = { 't', CMD_FIND_SESSION, 0 },
@ -58,9 +58,10 @@ cmd_list_clients_exec(struct cmd *self, struct cmdq_item *item)
struct client *c;
struct session *s;
struct format_tree *ft;
const char *template;
const char *template, *filter;
u_int idx;
char *line;
char *line, *expanded;
int flag;
if (args_has(args, 't'))
s = target->s;
@ -69,6 +70,7 @@ cmd_list_clients_exec(struct cmd *self, struct cmdq_item *item)
if ((template = args_get(args, 'F')) == NULL)
template = LIST_CLIENTS_TEMPLATE;
filter = args_get(args, 'f');
idx = 0;
TAILQ_FOREACH(c, &clients, entry) {
@ -79,9 +81,17 @@ cmd_list_clients_exec(struct cmd *self, struct cmdq_item *item)
format_add(ft, "line", "%u", idx);
format_defaults(ft, c, NULL, NULL, NULL);
if (filter != NULL) {
expanded = format_expand(ft, filter);
flag = format_true(expanded);
free(expanded);
} else
flag = 1;
if (flag) {
line = format_expand(ft, template);
cmdq_print(item, "%s", line);
free(line);
}
format_free(ft);

View File

@ -114,8 +114,8 @@ cmd_list_keys_print_notes(struct cmdq_item *item, struct args *args,
note = xstrdup(bd->note);
tmp = utf8_padcstr(key, keywidth + 1);
if (args_has(args, '1') && tc != NULL) {
status_message_set(tc, -1, 1, 0, "%s%s%s", prefix, tmp,
note);
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);
@ -148,6 +148,7 @@ static enum cmd_retval
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;
@ -296,9 +297,15 @@ cmd_list_keys_exec(struct cmd *self, struct cmdq_item *item)
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);
@ -314,34 +321,13 @@ out:
return (CMD_RETURN_NORMAL);
}
static enum cmd_retval
cmd_list_keys_commands(struct cmd *self, struct cmdq_item *item)
static void
cmd_list_single_command(const struct cmd_entry *entry, struct format_tree *ft,
const char *template, 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, *s, *command;
const char *s;
char *line;
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);
for (entryp = cmd_table; *entryp != NULL; entryp++) {
entry = *entryp;
if (command != NULL &&
(strcmp(entry->name, command) != 0 &&
(entry->alias == NULL ||
strcmp(entry->alias, command) != 0)))
continue;
format_add(ft, "command_list_name", "%s", entry->name);
if (entry->alias != NULL)
s = entry->alias;
@ -360,6 +346,41 @@ cmd_list_keys_commands(struct cmd *self, struct cmdq_item *item)
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);
return (CMD_RETURN_ERROR);
}
}
format_free(ft);
return (CMD_RETURN_NORMAL);
}

View File

@ -77,7 +77,7 @@ cmd_load_buffer_done(__unused struct client *c, const char *path, int error,
} else if (tc != NULL &&
tc->session != NULL &&
(~tc->flags & CLIENT_DEAD))
tty_set_selection(&tc->tty, copy, bsize);
tty_set_selection(&tc->tty, "", copy, bsize);
if (tc != NULL)
server_client_unref(tc);
}

View File

@ -43,7 +43,7 @@ const struct cmd_entry cmd_new_session_entry = {
.usage = "[-AdDEPX] [-c start-directory] [-e environment] [-F format] "
"[-f flags] [-n window-name] [-s session-name] "
CMD_TARGET_SESSION_USAGE " [-x width] [-y height] "
"[shell-command]",
"[shell-command [argument ...]]",
.target = { 't', CMD_FIND_SESSION, CMD_FIND_CANFAIL },
@ -333,13 +333,6 @@ cmd_new_session_exec(struct cmd *self, struct cmdq_item *item)
server_client_set_key_table(c, NULL);
}
/*
* If there are still configuration file errors to display, put the new
* session's current window into more mode and display them now.
*/
if (cfg_finished)
cfg_show_causes(s);
/* Print if requested. */
if (args_has(args, 'P')) {
if ((template = args_get(args, 'F')) == NULL)
@ -357,6 +350,9 @@ cmd_new_session_exec(struct cmd *self, struct cmdq_item *item)
cmd_find_from_session(&fs, s, 0);
cmdq_insert_hook(s, item, &fs, "after-new-session");
if (cfg_finished)
cfg_show_causes(s);
if (sc.argv != NULL)
cmd_free_argv(sc.argc, sc.argv);
free(cwd);

View File

@ -40,7 +40,8 @@ const struct cmd_entry cmd_new_window_entry = {
.args = { "abc:de:F:kn:PSt:", 0, -1, NULL },
.usage = "[-abdkPS] [-c start-directory] [-e environment] [-F format] "
"[-n window-name] " CMD_TARGET_WINDOW_USAGE " [shell-command]",
"[-n window-name] " CMD_TARGET_WINDOW_USAGE
" [shell-command [argument ...]]",
.target = { 't', CMD_FIND_WINDOW, CMD_FIND_WINDOW_INDEX },
@ -60,7 +61,7 @@ cmd_new_window_exec(struct cmd *self, struct cmdq_item *item)
struct session *s = target->s;
struct winlink *wl = target->wl, *new_wl = NULL;
int idx = target->idx, before;
char *cause = NULL, *cp;
char *cause = NULL, *cp, *expanded;
const char *template, *name;
struct cmd_find_state fs;
struct args_value *av;
@ -71,16 +72,19 @@ cmd_new_window_exec(struct cmd *self, struct cmdq_item *item)
*/
name = args_get(args, 'n');
if (args_has(args, 'S') && name != NULL && target->idx == -1) {
expanded = format_single(item, name, c, s, NULL, NULL);
RB_FOREACH(wl, winlinks, &s->windows) {
if (strcmp(wl->window->name, name) != 0)
if (strcmp(wl->window->name, expanded) != 0)
continue;
if (new_wl == NULL) {
new_wl = wl;
continue;
}
cmdq_error(item, "multiple windows named %s", name);
free(expanded);
return (CMD_RETURN_ERROR);
}
free(expanded);
if (new_wl != NULL) {
if (args_has(args, 'd'))
return (CMD_RETURN_NORMAL);

View File

@ -223,9 +223,16 @@ assignment : EQUALS
{
struct cmd_parse_state *ps = &parse_state;
int flags = ps->input->flags;
int flag = 1;
struct cmd_parse_scope *scope;
if ((~flags & CMD_PARSE_PARSEONLY) &&
(ps->scope == NULL || ps->scope->flag))
if (ps->scope != NULL) {
flag = ps->scope->flag;
TAILQ_FOREACH(scope, &ps->stack, entry)
flag = flag && scope->flag;
}
if ((~flags & CMD_PARSE_PARSEONLY) && flag)
environ_put(global_environ, $1, 0);
free($1);
}
@ -234,9 +241,16 @@ hidden_assignment : HIDDEN EQUALS
{
struct cmd_parse_state *ps = &parse_state;
int flags = ps->input->flags;
int flag = 1;
struct cmd_parse_scope *scope;
if ((~flags & CMD_PARSE_PARSEONLY) &&
(ps->scope == NULL || ps->scope->flag))
if (ps->scope != NULL) {
flag = ps->scope->flag;
TAILQ_FOREACH(scope, &ps->stack, entry)
flag = flag && scope->flag;
}
if ((~flags & CMD_PARSE_PARSEONLY) && flag)
environ_put(global_environ, $2, ENVIRON_HIDDEN);
free($2);
}
@ -1086,7 +1100,8 @@ cmd_parse_from_arguments(struct args_value *values, u_int count,
arg->type = CMD_PARSE_STRING;
arg->string = copy;
TAILQ_INSERT_TAIL(&cmd->arguments, arg, entry);
}
} else
free(copy);
} else if (values[i].type == ARGS_COMMANDS) {
arg = xcalloc(1, sizeof *arg);
arg->type = CMD_PARSE_PARSED_COMMANDS;
@ -1272,6 +1287,16 @@ yylex(void)
continue;
}
if (ch == '\r') {
/*
* Treat \r\n as \n.
*/
ch = yylex_getc();
if (ch != '\n') {
yylex_ungetc(ch);
ch = '\r';
}
}
if (ch == '\n') {
/*
* End of line. Update the line number.
@ -1602,6 +1627,7 @@ yylex_token_tilde(char **buf, size_t *len)
static char *
yylex_token(int ch)
{
struct cmd_parse_state *ps = &parse_state;
char *buf;
size_t len;
enum { START,
@ -1614,13 +1640,34 @@ yylex_token(int ch)
for (;;) {
/* EOF or \n are always the end of the token. */
if (ch == EOF || (state == NONE && ch == '\n'))
if (ch == EOF) {
log_debug("%s: end at EOF", __func__);
break;
}
if (state == NONE && ch == '\r') {
ch = yylex_getc();
if (ch != '\n') {
yylex_ungetc(ch);
ch = '\r';
}
}
if (ch == '\n') {
if (state == NONE) {
log_debug("%s: end at EOL", __func__);
break;
}
ps->input->line++;
}
/* Whitespace or ; or } ends a token unless inside quotes. */
if ((ch == ' ' || ch == '\t' || ch == ';' || ch == '}') &&
state == NONE)
if (state == NONE && (ch == ' ' || ch == '\t')) {
log_debug("%s: end at WS", __func__);
break;
}
if (state == NONE && (ch == ';' || ch == '}')) {
log_debug("%s: end at %c", __func__, ch);
break;
}
/*
* Spaces and comments inside quotes after \n are removed but

View File

@ -54,6 +54,11 @@ cmd_paste_buffer_exec(struct cmd *self, struct cmdq_item *item)
size_t seplen, bufsize;
int bracket = args_has(args, 'p');
if (window_pane_exited(wp)) {
cmdq_error(item, "target pane has exited");
return (CMD_RETURN_ERROR);
}
bufname = NULL;
if (args_has(args, 'b'))
bufname = args_get(args, 'b');

View File

@ -68,7 +68,7 @@ cmd_pipe_pane_exec(struct cmd *self, struct cmdq_item *item)
sigset_t set, oldset;
/* Do nothing if pane is dead. */
if (wp->fd == -1 || (wp->flags & PANE_EXITED)) {
if (window_pane_exited(wp)) {
cmdq_error(item, "target pane has exited");
return (CMD_RETURN_ERROR);
}

View File

@ -236,8 +236,10 @@ cmdq_link_state(struct cmdq_state *state)
/* Make a copy of a state. */
struct cmdq_state *
cmdq_copy_state(struct cmdq_state *state)
cmdq_copy_state(struct cmdq_state *state, struct cmd_find_state *current)
{
if (current != NULL)
return (cmdq_new_state(current, &state->event, state->flags));
return (cmdq_new_state(&state->current, &state->event, state->flags));
}
@ -662,9 +664,18 @@ cmdq_fire_command(struct cmdq_item *item)
out:
item->client = saved;
if (retval == CMD_RETURN_ERROR)
if (retval == CMD_RETURN_ERROR) {
fsp = NULL;
if (cmd_find_valid_state(&item->target))
fsp = &item->target;
else if (cmd_find_valid_state(&item->state->current))
fsp = &item->state->current;
else if (cmd_find_from_client(&fs, item->client, 0) == 0)
fsp = &fs;
cmdq_insert_hook(fsp != NULL ? fsp->s : NULL, item, fsp,
"command-error");
cmdq_guard(item, "error", flags);
else
} else
cmdq_guard(item, "end", flags);
return (retval);
}
@ -821,45 +832,30 @@ cmdq_guard(struct cmdq_item *item, const char *guard, int flags)
control_write(c, "%%%s %ld %u %d", guard, t, number, flags);
}
/* Show message from command. */
void
cmdq_print_data(struct cmdq_item *item, struct evbuffer *evb)
{
server_client_print(item->client, 1, evb);
}
/* Show message from command. */
void
cmdq_print(struct cmdq_item *item, const char *fmt, ...)
{
struct client *c = item->client;
struct window_pane *wp;
struct window_mode_entry *wme;
va_list ap;
char *tmp, *msg;
struct evbuffer *evb;
evb = evbuffer_new();
if (evb == NULL)
fatalx("out of memory");
va_start(ap, fmt);
xvasprintf(&msg, fmt, ap);
evbuffer_add_vprintf(evb, fmt, ap);
va_end(ap);
log_debug("%s: %s", __func__, msg);
if (c == NULL)
/* nothing */;
else if (c->session == NULL || (c->flags & CLIENT_CONTROL)) {
if (~c->flags & CLIENT_UTF8) {
tmp = msg;
msg = utf8_sanitize(tmp);
free(tmp);
}
if (c->flags & CLIENT_CONTROL)
control_write(c, "%s", msg);
else
file_print(c, "%s\n", msg);
} else {
wp = server_client_get_pane(c);
wme = TAILQ_FIRST(&wp->modes);
if (wme == NULL || wme->mode != &window_view_mode) {
window_pane_set_mode(wp, NULL, &window_view_mode, NULL,
NULL);
}
window_copy_add(wp, 0, "%s", msg);
}
free(msg);
cmdq_print_data(item, evb);
evbuffer_free(evb);
}
/* Show error from command. */
@ -896,7 +892,7 @@ cmdq_error(struct cmdq_item *item, const char *fmt, ...)
c->retval = 1;
} else {
*msg = toupper((u_char) *msg);
status_message_set(c, -1, 1, 0, "%s", msg);
status_message_set(c, -1, 1, 0, 0, "%s", msg);
}
free(msg);

View File

@ -34,9 +34,10 @@ const struct cmd_entry cmd_refresh_client_entry = {
.name = "refresh-client",
.alias = "refresh",
.args = { "A:B:cC:Df:F:l::LRSt:U", 0, 1, NULL },
.args = { "A:B:cC:Df:r:F:l::LRSt:U", 0, 1, NULL },
.usage = "[-cDlLRSU] [-A pane:state] [-B name:what:format] "
"[-C XxY] [-f flags] " CMD_TARGET_CLIENT_USAGE " [adjustment]",
"[-C XxY] [-f flags] [-r pane:report] " CMD_TARGET_CLIENT_USAGE
" [adjustment]",
.flags = CMD_AFTERHOOK|CMD_CLIENT_TFLAG,
.exec = cmd_refresh_client_exec
@ -193,6 +194,34 @@ cmd_refresh_client_clipboard(struct cmd *self, struct cmdq_item *item)
return (CMD_RETURN_NORMAL);
}
static void
cmd_refresh_report(struct tty *tty, const char *value)
{
struct window_pane *wp;
u_int pane;
size_t size = 0;
char *copy, *split;
if (*value != '%')
return;
copy = xstrdup(value);
if ((split = strchr(copy, ':')) == NULL)
goto out;
*split++ = '\0';
if (sscanf(copy, "%%%u", &pane) != 1)
goto out;
wp = window_pane_find_by_id(pane);
if (wp == NULL)
goto out;
tty_keys_colours(tty, split, strlen(split), &size, &wp->control_fg,
&wp->control_bg);
out:
free(copy);
}
static enum cmd_retval
cmd_refresh_client_exec(struct cmd *self, struct cmdq_item *item)
{
@ -262,6 +291,8 @@ cmd_refresh_client_exec(struct cmd *self, struct cmdq_item *item)
server_client_set_flags(tc, args_get(args, 'F'));
if (args_has(args, 'f'))
server_client_set_flags(tc, args_get(args, 'f'));
if (args_has(args, 'r'))
cmd_refresh_report(tty, args_get(args, 'r'));
if (args_has(args, 'A')) {
if (~tc->flags & CLIENT_CONTROL)

View File

@ -87,7 +87,7 @@ cmd_resize_pane_exec(struct cmd *self, struct cmdq_item *item)
if (args_has(args, 'Z')) {
if (w->flags & WINDOW_ZOOMED)
window_unzoom(w);
window_unzoom(w, 1);
else
window_zoom(wp);
server_redraw_window(w);

View File

@ -53,8 +53,7 @@ cmd_resize_window_exec(struct cmd *self, struct cmdq_item *item)
struct session *s = target->s;
const char *errstr;
char *cause;
u_int adjust, sx, sy;
int xpixel = -1, ypixel = -1;
u_int adjust, sx, sy, xpixel = 0, ypixel = 0;
if (args_count(args) == 0)
adjust = 1;

View File

@ -36,7 +36,7 @@ const struct cmd_entry cmd_respawn_pane_entry = {
.args = { "c:e:kt:", 0, -1, NULL },
.usage = "[-k] [-c start-directory] [-e environment] "
CMD_TARGET_PANE_USAGE " [shell-command]",
CMD_TARGET_PANE_USAGE " [shell-command [argument ...]]",
.target = { 't', CMD_FIND_PANE, 0 },

View File

@ -36,7 +36,7 @@ const struct cmd_entry cmd_respawn_window_entry = {
.args = { "c:e:kt:", 0, -1, NULL },
.usage = "[-k] [-c start-directory] [-e environment] "
CMD_TARGET_WINDOW_USAGE " [shell-command]",
CMD_TARGET_WINDOW_USAGE " [shell-command [argument ...]]",
.target = { 't', CMD_FIND_WINDOW, 0 },

View File

@ -44,8 +44,9 @@ const struct cmd_entry cmd_run_shell_entry = {
.name = "run-shell",
.alias = "run",
.args = { "bd:Ct:", 0, 1, cmd_run_shell_args_parse },
.usage = "[-bC] [-d delay] " CMD_TARGET_PANE_USAGE " [shell-command]",
.args = { "bd:Ct:Es:c:", 0, 1, cmd_run_shell_args_parse },
.usage = "[-bCE] [-c start-directory] [-d delay] " CMD_TARGET_PANE_USAGE
" [shell-command]",
.target = { 't', CMD_FIND_PANE, CMD_FIND_CANFAIL },
@ -84,12 +85,18 @@ cmd_run_shell_print(struct job *job, const char *msg)
if (cdata->wp_id != -1)
wp = window_pane_find_by_id(cdata->wp_id);
if (wp == NULL && cdata->item != NULL && cdata->client != NULL)
if (wp == NULL) {
if (cdata->item != NULL) {
cmdq_print(cdata->item, "%s", msg);
return;
}
if (cdata->item != NULL && cdata->client != NULL)
wp = server_client_get_pane(cdata->client);
if (wp == NULL && cmd_find_from_nothing(&fs, 0) == 0)
wp = fs.wp;
if (wp == NULL)
return;
}
wme = TAILQ_FIRST(&wp->modes);
if (wme == NULL || wme->mode != &window_view_mode)
@ -103,6 +110,7 @@ cmd_run_shell_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 cmd_run_shell_data *cdata;
struct client *c = cmdq_get_client(item);
struct client *tc = cmdq_get_target_client(item);
struct session *s = target->s;
struct window_pane *wp = target->wp;
@ -137,7 +145,7 @@ cmd_run_shell_exec(struct cmd *self, struct cmdq_item *item)
cdata->wp_id = -1;
if (wait) {
cdata->client = cmdq_get_client(item);
cdata->client = c;
cdata->item = item;
} else {
cdata->client = tc;
@ -145,8 +153,13 @@ cmd_run_shell_exec(struct cmd *self, struct cmdq_item *item)
}
if (cdata->client != NULL)
cdata->client->references++;
if (args_has(args, 'c'))
cdata->cwd = xstrdup(args_get(args, 'c'));
else
cdata->cwd = xstrdup(server_client_get_cwd(c, s));
cdata->cwd = xstrdup(server_client_get_cwd(cmdq_get_client(item), s));
if (args_has(args, 'E'))
cdata->flags |= JOB_SHOWSTDERR;
cdata->s = s;
if (s != NULL)
@ -194,7 +207,7 @@ cmd_run_shell_timer(__unused int fd, __unused short events, void* arg)
if (cmdlist == NULL) {
if (cdata->item == NULL) {
*error = toupper((u_char)*error);
status_message_set(c, -1, 1, 0, "%s", error);
status_message_set(c, -1, 1, 0, 0, "%s", error);
} else
cmdq_error(cdata->item, "%s", error);
free(error);

View File

@ -78,7 +78,8 @@ cmd_save_buffer_exec(struct cmd *self, struct cmdq_item *item)
int flags;
const char *bufname = args_get(args, 'b'), *bufdata;
size_t bufsize;
char *path, *tmp;
char *path;
struct evbuffer *evb;
if (bufname == NULL) {
if ((pb = paste_get_top(NULL)) == NULL) {
@ -96,10 +97,12 @@ cmd_save_buffer_exec(struct cmd *self, struct cmdq_item *item)
if (cmd_get_entry(self) == &cmd_show_buffer_entry) {
if (c->session != NULL || (c->flags & CLIENT_CONTROL)) {
utf8_stravisx(&tmp, bufdata, bufsize,
VIS_OCTAL|VIS_CSTYLE|VIS_TAB);
cmdq_print(item, "%s", tmp);
free(tmp);
evb = evbuffer_new();
if (evb == NULL)
fatalx("out of memory");
evbuffer_add(evb, bufdata, bufsize);
cmdq_print_data(item, evb);
evbuffer_free(evb);
return (CMD_RETURN_NORMAL);
}
path = xstrdup("-");

View File

@ -98,7 +98,11 @@ cmd_select_pane_exec(struct cmd *self, struct cmdq_item *item)
struct options_entry *o;
if (entry == &cmd_last_pane_entry || args_has(args, 'l')) {
lastwp = w->last;
/*
* Check for no last pane found in case the other pane was
* spawned without being visited (for example split-window -d).
*/
lastwp = TAILQ_FIRST(&w->last_panes);
if (lastwp == NULL && window_count_panes(w) == 2) {
lastwp = TAILQ_PREV(w->active, window_panes, entry);
if (lastwp == NULL)
@ -145,12 +149,14 @@ cmd_select_pane_exec(struct cmd *self, struct cmdq_item *item)
markedwp = marked_pane.wp;
if (lastwp != NULL) {
lastwp->flags |= (PANE_REDRAW|PANE_STYLECHANGED);
lastwp->flags |= (PANE_REDRAW|PANE_STYLECHANGED|
PANE_THEMECHANGED);
server_redraw_window_borders(lastwp->window);
server_status_window(lastwp->window);
}
if (markedwp != NULL) {
markedwp->flags |= (PANE_REDRAW|PANE_STYLECHANGED);
markedwp->flags |= (PANE_REDRAW|PANE_STYLECHANGED|
PANE_THEMECHANGED);
server_redraw_window_borders(markedwp->window);
server_status_window(markedwp->window);
}
@ -165,7 +171,7 @@ cmd_select_pane_exec(struct cmd *self, struct cmdq_item *item)
return (CMD_RETURN_ERROR);
}
options_set_string(oo, "window-active-style", 0, "%s", style);
wp->flags |= (PANE_REDRAW|PANE_STYLECHANGED);
wp->flags |= (PANE_REDRAW|PANE_STYLECHANGED|PANE_THEMECHANGED);
}
if (args_has(args, 'g')) {
cmdq_print(item, "%s", options_get_string(oo, "window-style"));

View File

@ -33,13 +33,13 @@ const struct cmd_entry cmd_send_keys_entry = {
.name = "send-keys",
.alias = "send",
.args = { "FHlMN:Rt:X", 0, -1, NULL },
.usage = "[-FHlMRX] [-N repeat-count] " CMD_TARGET_PANE_USAGE
" key ...",
.args = { "c:FHKlMN:Rt:X", 0, -1, NULL },
.usage = "[-FHKlMRX] [-c target-client] [-N repeat-count] "
CMD_TARGET_PANE_USAGE " [key ...]",
.target = { 't', CMD_FIND_PANE, 0 },
.flags = CMD_AFTERHOOK,
.flags = CMD_AFTERHOOK|CMD_CLIENT_CFLAG|CMD_CLIENT_CANFAIL,
.exec = cmd_send_keys_exec
};
@ -58,7 +58,7 @@ const struct cmd_entry cmd_send_prefix_entry = {
static struct cmdq_item *
cmd_send_keys_inject_key(struct cmdq_item *item, struct cmdq_item *after,
key_code key)
struct args *args, key_code key)
{
struct cmd_find_state *target = cmdq_get_target(item);
struct client *tc = cmdq_get_target_client(item);
@ -66,8 +66,22 @@ cmd_send_keys_inject_key(struct cmdq_item *item, struct cmdq_item *after,
struct winlink *wl = target->wl;
struct window_pane *wp = target->wp;
struct window_mode_entry *wme;
struct key_table *table;
struct key_table *table = NULL;
struct key_binding *bd;
struct key_event *event;
if (args_has(args, 'K')) {
if (tc == NULL)
return (item);
event = xcalloc(1, sizeof *event);
event->key = key|KEYC_SENT;
memset(&event->m, 0, sizeof event->m);
if (server_client_handle_key(tc, event) == 0) {
free(event->buf);
free(event);
}
return (item);
}
wme = TAILQ_FIRST(&wp->modes);
if (wme == NULL || wme->mode->key_table == NULL) {
@ -102,14 +116,16 @@ cmd_send_keys_inject_string(struct cmdq_item *item, struct cmdq_item *after,
n = strtol(s, &endptr, 16);
if (*s =='\0' || n < 0 || n > 0xff || *endptr != '\0')
return (item);
return (cmd_send_keys_inject_key(item, after, KEYC_LITERAL|n));
return (cmd_send_keys_inject_key(item, after, args,
KEYC_LITERAL|n));
}
literal = args_has(args, 'l');
if (!literal) {
key = key_string_lookup_string(s);
if (key != KEYC_NONE && key != KEYC_UNKNOWN) {
after = cmd_send_keys_inject_key(item, after, key);
after = cmd_send_keys_inject_key(item, after, args,
key);
if (after != NULL)
return (after);
}
@ -125,7 +141,8 @@ cmd_send_keys_inject_string(struct cmdq_item *item, struct cmdq_item *after,
continue;
key = uc;
}
after = cmd_send_keys_inject_key(item, after, key);
after = cmd_send_keys_inject_key(item, after, args,
key);
}
free(ud);
}
@ -151,7 +168,8 @@ cmd_send_keys_exec(struct cmd *self, struct cmdq_item *item)
char *cause = NULL;
if (args_has(args, 'N')) {
np = args_strtonum(args, 'N', 1, UINT_MAX, &cause);
np = args_strtonum_and_expand(args, 'N', 1, UINT_MAX, item,
&cause);
if (cause != NULL) {
cmdq_error(item, "repeat count %s", cause);
free(cause);
@ -192,21 +210,21 @@ cmd_send_keys_exec(struct cmd *self, struct cmdq_item *item)
key = options_get_number(s->options, "prefix2");
else
key = options_get_number(s->options, "prefix");
cmd_send_keys_inject_key(item, item, key);
cmd_send_keys_inject_key(item, item, args, key);
return (CMD_RETURN_NORMAL);
}
if (args_has(args, 'R')) {
colour_palette_clear(&wp->palette);
input_reset(wp->ictx, 1);
wp->flags |= (PANE_STYLECHANGED|PANE_REDRAW);
wp->flags |= (PANE_STYLECHANGED|PANE_THEMECHANGED|PANE_REDRAW);
}
if (count == 0) {
if (args_has(args, 'N') || args_has(args, 'R'))
return (CMD_RETURN_NORMAL);
for (; np != 0; np--)
cmd_send_keys_inject_key(item, NULL, event->key);
cmd_send_keys_inject_key(item, NULL, args, event->key);
return (CMD_RETURN_NORMAL);
}

View File

@ -35,7 +35,7 @@ const struct cmd_entry cmd_set_buffer_entry = {
.args = { "ab:t:n:w", 0, 1, NULL },
.usage = "[-aw] " CMD_BUFFER_USAGE " [-n new-buffer-name] "
CMD_TARGET_CLIENT_USAGE " data",
CMD_TARGET_CLIENT_USAGE " [data]",
.flags = CMD_AFTERHOOK|CMD_CLIENT_TFLAG|CMD_CLIENT_CANFAIL,
.exec = cmd_set_buffer_exec
@ -69,8 +69,13 @@ cmd_set_buffer_exec(struct cmd *self, struct cmdq_item *item)
pb = paste_get_name(bufname);
if (cmd_get_entry(self) == &cmd_delete_buffer_entry) {
if (pb == NULL)
if (pb == NULL) {
if (bufname != NULL) {
cmdq_error(item, "unknown buffer: %s", bufname);
return (CMD_RETURN_ERROR);
}
pb = paste_get_top(&bufname);
}
if (pb == NULL) {
cmdq_error(item, "no buffer");
return (CMD_RETURN_ERROR);
@ -80,8 +85,13 @@ cmd_set_buffer_exec(struct cmd *self, struct cmdq_item *item)
}
if (args_has(args, 'n')) {
if (pb == NULL)
if (pb == NULL) {
if (bufname != NULL) {
cmdq_error(item, "unknown buffer: %s", bufname);
return (CMD_RETURN_ERROR);
}
pb = paste_get_top(&bufname);
}
if (pb == NULL) {
cmdq_error(item, "no buffer");
return (CMD_RETURN_ERROR);
@ -121,7 +131,7 @@ cmd_set_buffer_exec(struct cmd *self, struct cmdq_item *item)
return (CMD_RETURN_ERROR);
}
if (args_has(args, 'w') && tc != NULL)
tty_set_selection(&tc->tty, bufdata, bufsize);
tty_set_selection(&tc->tty, "", bufdata, bufsize);
return (CMD_RETURN_NORMAL);
}

View File

@ -35,7 +35,7 @@ const struct cmd_entry cmd_set_environment_entry = {
.alias = "setenv",
.args = { "Fhgrt:u", 1, 2, NULL },
.usage = "[-Fhgru] " CMD_TARGET_SESSION_USAGE " name [value]",
.usage = "[-Fhgru] " CMD_TARGET_SESSION_USAGE " variable [value]",
.target = { 't', CMD_FIND_SESSION, CMD_FIND_CANFAIL },

View File

@ -39,7 +39,7 @@ const struct cmd_entry cmd_show_environment_entry = {
.alias = "showenv",
.args = { "hgst:", 0, 1, NULL },
.usage = "[-hgs] " CMD_TARGET_SESSION_USAGE " [name]",
.usage = "[-hgs] " CMD_TARGET_SESSION_USAGE " [variable]",
.target = { 't', CMD_FIND_SESSION, CMD_FIND_CANFAIL },

View File

@ -65,7 +65,7 @@ const struct cmd_entry cmd_show_hooks_entry = {
.alias = NULL,
.args = { "gpt:w", 0, 1, NULL },
.usage = "[-gpw] " CMD_TARGET_PANE_USAGE,
.usage = "[-gpw] " CMD_TARGET_PANE_USAGE " [hook]",
.target = { 't', CMD_FIND_PANE, CMD_FIND_CANFAIL },

View File

@ -32,7 +32,7 @@ const struct cmd_entry cmd_show_prompt_history_entry = {
.alias = "showphist",
.args = { "T:", 0, 0, NULL },
.usage = "[-T type]",
.usage = "[-T prompt-type]",
.flags = CMD_AFTERHOOK,
.exec = cmd_show_prompt_history_exec
@ -43,7 +43,7 @@ const struct cmd_entry cmd_clear_prompt_history_entry = {
.alias = "clearphist",
.args = { "T:", 0, 0, NULL },
.usage = "[-T type]",
.usage = "[-T prompt-type]",
.flags = CMD_AFTERHOOK,
.exec = cmd_show_prompt_history_exec

View File

@ -29,14 +29,19 @@
* Sources a configuration file.
*/
#define CMD_SOURCE_FILE_DEPTH_LIMIT 50
static u_int cmd_source_file_depth;
static enum cmd_retval cmd_source_file_exec(struct cmd *, struct cmdq_item *);
const struct cmd_entry cmd_source_file_entry = {
.name = "source-file",
.alias = "source",
.args = { "Fnqv", 1, -1, NULL },
.usage = "[-Fnqv] path ...",
.args = { "t:Fnqv", 1, -1, NULL },
.usage = "[-Fnqv] " CMD_TARGET_PANE_USAGE " path ...",
.target = { 't', CMD_FIND_PANE, CMD_FIND_CANFAIL },
.flags = 0,
.exec = cmd_source_file_exec
@ -57,6 +62,16 @@ struct cmd_source_file_data {
static enum cmd_retval
cmd_source_file_complete_cb(struct cmdq_item *item, __unused void *data)
{
struct client *c = cmdq_get_client(item);
if (c == NULL) {
cmd_source_file_depth--;
log_debug("%s: depth now %u", __func__, cmd_source_file_depth);
} else {
c->source_file_depth--;
log_debug("%s: depth now %u", __func__, c->source_file_depth);
}
cfg_print_causes(item);
return (CMD_RETURN_NORMAL);
}
@ -92,6 +107,7 @@ cmd_source_file_done(struct client *c, const char *path, int error,
size_t bsize = EVBUFFER_LENGTH(buffer);
u_int n;
struct cmdq_item *new_item;
struct cmd_find_state *target = cmdq_get_target(item);
if (!closed)
return;
@ -100,7 +116,7 @@ cmd_source_file_done(struct client *c, const char *path, int error,
cmdq_error(item, "%s: %s", path, strerror(error));
else if (bsize != 0) {
if (load_cfg_from_buffer(bdata, bsize, path, c, cdata->after,
cdata->flags, &new_item) < 0)
target, cdata->flags, &new_item) < 0)
cdata->retval = CMD_RETURN_ERROR;
else if (new_item != NULL)
cdata->after = new_item;
@ -118,7 +134,16 @@ cmd_source_file_done(struct client *c, const char *path, int error,
static void
cmd_source_file_add(struct cmd_source_file_data *cdata, const char *path)
{
char resolved[PATH_MAX];
if (realpath(path, resolved) == NULL) {
log_debug("%s: realpath(\"%s\") failed: %s", __func__,
path, strerror(errno));
} else
path = resolved;
log_debug("%s: %s", __func__, path);
cdata->files = xreallocarray(cdata->files, cdata->nfiles + 1,
sizeof *cdata->files);
cdata->files[cdata->nfiles++] = xstrdup(path);
@ -137,6 +162,22 @@ cmd_source_file_exec(struct cmd *self, struct cmdq_item *item)
int result;
u_int i, j;
if (c == NULL) {
if (cmd_source_file_depth >= CMD_SOURCE_FILE_DEPTH_LIMIT) {
cmdq_error(item, "too many nested files");
return (CMD_RETURN_ERROR);
}
cmd_source_file_depth++;
log_debug("%s: depth now %u", __func__, cmd_source_file_depth);
} else {
if (c->source_file_depth >= CMD_SOURCE_FILE_DEPTH_LIMIT) {
cmdq_error(item, "too many nested files");
return (CMD_RETURN_ERROR);
}
c->source_file_depth++;
log_debug("%s: depth now %u", __func__, c->source_file_depth);
}
cdata = xcalloc(1, sizeof *cdata);
cdata->item = item;

View File

@ -42,7 +42,7 @@ const struct cmd_entry cmd_split_window_entry = {
.args = { "bc:de:fF:hIl:p:Pt:vZ", 0, -1, NULL },
.usage = "[-bdefhIPvZ] [-c start-directory] [-e environment] "
"[-F format] [-l size] " CMD_TARGET_PANE_USAGE
"[shell-command]",
" [shell-command [argument ...]]",
.target = { 't', CMD_FIND_PANE, 0 },
@ -65,67 +65,46 @@ cmd_split_window_exec(struct cmd *self, struct cmdq_item *item)
enum layout_type type;
struct layout_cell *lc;
struct cmd_find_state fs;
int size, percentage, flags, input;
const char *template, *errstr, *p;
char *cause, *cp, *copy;
size_t plen;
int size, flags, input;
const char *template;
char *cause = NULL, *cp;
struct args_value *av;
u_int count = args_count(args);
u_int count = args_count(args), curval = 0;
type = LAYOUT_TOPBOTTOM;
if (args_has(args, 'h'))
type = LAYOUT_LEFTRIGHT;
else
type = LAYOUT_TOPBOTTOM;
if ((p = args_get(args, 'l')) != NULL) {
plen = strlen(p);
if (p[plen - 1] == '%') {
copy = xstrdup(p);
copy[plen - 1] = '\0';
percentage = strtonum(copy, 0, INT_MAX, &errstr);
free(copy);
if (errstr != NULL) {
cmdq_error(item, "percentage %s", errstr);
return (CMD_RETURN_ERROR);
}
/* If the 'p' flag is dropped then this bit can be moved into 'l'. */
if (args_has(args, 'l') || args_has(args, 'p')) {
if (args_has(args, 'f')) {
if (type == LAYOUT_TOPBOTTOM)
size = (w->sy * percentage) / 100;
curval = w->sy;
else
size = (w->sx * percentage) / 100;
curval = w->sx;
} else {
if (type == LAYOUT_TOPBOTTOM)
size = (wp->sy * percentage) / 100;
curval = wp->sy;
else
size = (wp->sx * percentage) / 100;
}
} else {
size = args_strtonum(args, 'l', 0, INT_MAX, &cause);
if (cause != NULL) {
cmdq_error(item, "lines %s", cause);
free(cause);
return (CMD_RETURN_ERROR);
curval = wp->sx;
}
}
} else if (args_has(args, 'p')) {
percentage = args_strtonum(args, 'p', 0, INT_MAX, &cause);
if (cause != NULL) {
cmdq_error(item, "create pane failed: -p %s", cause);
free(cause);
return (CMD_RETURN_ERROR);
}
if (args_has(args, 'f')) {
if (type == LAYOUT_TOPBOTTOM)
size = (w->sy * percentage) / 100;
else
size = (w->sx * percentage) / 100;
} else {
if (type == LAYOUT_TOPBOTTOM)
size = (wp->sy * percentage) / 100;
else
size = (wp->sx * percentage) / 100;
}
} else
size = -1;
if (args_has(args, 'l')) {
size = args_percentage_and_expand(args, 'l', 0, INT_MAX, curval,
item, &cause);
} else if (args_has(args, 'p')) {
size = args_strtonum_and_expand(args, 'p', 0, 100, item,
&cause);
if (cause == NULL)
size = curval * size / 100;
}
if (cause != NULL) {
cmdq_error(item, "size %s", cause);
free(cause);
return (CMD_RETURN_ERROR);
}
window_push_zoom(wp->window, 1, args_has(args, 'Z'));
input = (args_has(args, 'I') && count == 0);

View File

@ -101,10 +101,10 @@ cmd_swap_pane_exec(struct cmd *self, struct cmdq_item *item)
src_wp->window = dst_w;
options_set_parent(src_wp->options, dst_w->options);
src_wp->flags |= PANE_STYLECHANGED;
src_wp->flags |= (PANE_STYLECHANGED|PANE_THEMECHANGED);
dst_wp->window = src_w;
options_set_parent(dst_wp->options, src_w->options);
dst_wp->flags |= PANE_STYLECHANGED;
dst_wp->flags |= (PANE_STYLECHANGED|PANE_THEMECHANGED);
sx = src_wp->sx; sy = src_wp->sy;
xoff = src_wp->xoff; yoff = src_wp->yoff;
@ -128,13 +128,16 @@ cmd_swap_pane_exec(struct cmd *self, struct cmdq_item *item)
window_set_active_pane(dst_w, src_wp, 1);
}
if (src_w != dst_w) {
if (src_w->last == src_wp)
src_w->last = NULL;
if (dst_w->last == dst_wp)
dst_w->last = NULL;
}
window_pane_stack_remove(&src_w->last_panes, src_wp);
window_pane_stack_remove(&dst_w->last_panes, dst_wp);
colour_palette_from_option(&src_wp->palette, src_wp->options);
colour_palette_from_option(&dst_wp->palette, dst_wp->options);
layout_fix_panes(src_w, NULL);
server_redraw_window(src_w);
}
layout_fix_panes(dst_w, NULL);
server_redraw_window(dst_w);
notify_window("window-layout-changed", src_w);
if (src_w != dst_w)
notify_window("window-layout-changed", dst_w);

12
cmd.c
View File

@ -47,7 +47,6 @@ extern const struct cmd_entry cmd_display_menu_entry;
extern const struct cmd_entry cmd_display_message_entry;
extern const struct cmd_entry cmd_display_popup_entry;
extern const struct cmd_entry cmd_display_panes_entry;
extern const struct cmd_entry cmd_down_pane_entry;
extern const struct cmd_entry cmd_find_window_entry;
extern const struct cmd_entry cmd_has_session_entry;
extern const struct cmd_entry cmd_if_shell_entry;
@ -117,7 +116,6 @@ extern const struct cmd_entry cmd_swap_window_entry;
extern const struct cmd_entry cmd_switch_client_entry;
extern const struct cmd_entry cmd_unbind_key_entry;
extern const struct cmd_entry cmd_unlink_window_entry;
extern const struct cmd_entry cmd_up_pane_entry;
extern const struct cmd_entry cmd_wait_for_entry;
const struct cmd_entry *cmd_table[] = {
@ -446,7 +444,7 @@ cmd_get_alias(const char *name)
}
/* Look up a command entry by name. */
static const struct cmd_entry *
const struct cmd_entry *
cmd_find(const char *name, char **cause)
{
const struct cmd_entry **loop, *entry, *found = NULL;
@ -638,7 +636,7 @@ cmd_list_free(struct cmd_list *cmdlist)
/* Copy a command list, expanding %s in arguments. */
struct cmd_list *
cmd_list_copy(struct cmd_list *cmdlist, int argc, char **argv)
cmd_list_copy(const struct cmd_list *cmdlist, int argc, char **argv)
{
struct cmd *cmd;
struct cmd_list *new_cmdlist;
@ -669,7 +667,7 @@ cmd_list_copy(struct cmd_list *cmdlist, int argc, char **argv)
/* Get a command list as a string. */
char *
cmd_list_print(struct cmd_list *cmdlist, int escaped)
cmd_list_print(const struct cmd_list *cmdlist, int escaped)
{
struct cmd *cmd, *next;
char *buf, *this;
@ -812,10 +810,14 @@ cmd_mouse_pane(struct mouse_event *m, struct session **sp,
if ((wl = cmd_mouse_window(m, sp)) == NULL)
return (NULL);
if (m->wp == -1)
wp = wl->window->active;
else {
if ((wp = window_pane_find_by_id(m->wp)) == NULL)
return (NULL);
if (!window_has_pane(wl->window, wp))
return (NULL);
}
if (wlp != NULL)
*wlp = wl;

View File

@ -182,6 +182,46 @@ colour_tostring(int c)
return ("invalid");
}
/* Convert background colour to theme. */
enum client_theme
colour_totheme(int c)
{
int r, g, b, brightness;
if (c == -1)
return (THEME_UNKNOWN);
if (c & COLOUR_FLAG_RGB) {
r = (c >> 16) & 0xff;
g = (c >> 8) & 0xff;
b = (c >> 0) & 0xff;
brightness = r + g + b;
if (brightness > 382)
return (THEME_LIGHT);
return (THEME_DARK);
}
if (c & COLOUR_FLAG_256)
return (colour_totheme(colour_256toRGB(c)));
switch (c) {
case 0:
case 90:
return (THEME_DARK);
case 7:
case 97:
return (THEME_LIGHT);
default:
if (c >= 0 && c <= 7)
return (colour_totheme(colour_256toRGB(c)));
if (c >= 90 && c <= 97)
return (colour_totheme(colour_256toRGB(8 + c - 90)));
break;
}
return (THEME_UNKNOWN);
}
/* Convert colour from string. */
int
colour_fromstring(const char *s)
@ -944,11 +984,15 @@ colour_byname(const char *name)
};
u_int i;
int c;
const char *errstr;
if (strncmp(name, "grey", 4) == 0 || strncmp(name, "gray", 4) == 0) {
if (!isdigit((u_char)name[4]))
if (name[4] == '\0')
return (0xbebebe|COLOUR_FLAG_RGB);
c = round(2.55 * atoi(name + 4));
c = strtonum(name + 4, 0, 100, &errstr);
if (errstr != NULL)
return (-1);
c = round(2.55 * c);
if (c < 0 || c > 255)
return (-1);
return (colour_join_rgb(c, c, c));
@ -960,6 +1004,47 @@ colour_byname(const char *name)
return (-1);
}
/* Parse colour from an X11 string. */
int
colour_parseX11(const char *p)
{
double c, m, y, k = 0;
u_int r, g, b;
size_t len = strlen(p);
int colour = -1;
char *copy;
if ((len == 12 && sscanf(p, "rgb:%02x/%02x/%02x", &r, &g, &b) == 3) ||
(len == 7 && sscanf(p, "#%02x%02x%02x", &r, &g, &b) == 3) ||
sscanf(p, "%d,%d,%d", &r, &g, &b) == 3)
colour = colour_join_rgb(r, g, b);
else if ((len == 18 &&
sscanf(p, "rgb:%04x/%04x/%04x", &r, &g, &b) == 3) ||
(len == 13 && sscanf(p, "#%04x%04x%04x", &r, &g, &b) == 3))
colour = colour_join_rgb(r >> 8, g >> 8, b >> 8);
else if ((sscanf(p, "cmyk:%lf/%lf/%lf/%lf", &c, &m, &y, &k) == 4 ||
sscanf(p, "cmy:%lf/%lf/%lf", &c, &m, &y) == 3) &&
c >= 0 && c <= 1 && m >= 0 && m <= 1 &&
y >= 0 && y <= 1 && k >= 0 && k <= 1) {
colour = colour_join_rgb(
(1 - c) * (1 - k) * 255,
(1 - m) * (1 - k) * 255,
(1 - y) * (1 - k) * 255);
} else {
while (len != 0 && *p == ' ') {
p++;
len--;
}
while (len != 0 && p[len - 1] == ' ')
len--;
copy = xstrndup(p, len);
colour = colour_byname(copy);
free(copy);
}
log_debug("%s: %s = %s", __func__, p, colour_tostring(colour));
return (colour);
}
/* Initialize palette. */
void
colour_palette_init(struct colour_palette *p)
@ -1069,5 +1154,4 @@ colour_palette_from_option(struct colour_palette *p, struct options *oo)
}
a = options_array_next(a);
}
}

View File

@ -38,6 +38,14 @@
#include <event2/bufferevent_compat.h>
#else
#include <event.h>
#ifndef EVBUFFER_EOL_LF
/*
* This doesn't really work because evbuffer_readline is broken, but gets us to
* build with very old (older than 1.4.14) libevent.
*/
#define EVBUFFER_EOL_LF
#define evbuffer_readln(a, b, c) evbuffer_readline(a)
#endif
#endif
#ifdef HAVE_MALLOC_TRIM
@ -289,6 +297,11 @@ void explicit_bzero(void *, size_t);
int getdtablecount(void);
#endif
#ifndef HAVE_GETDTABLESIZE
/* getdtablesize.c */
int getdtablesize(void);
#endif
#ifndef HAVE_CLOSEFROM
/* closefrom.c */
void closefrom(int);
@ -334,6 +347,18 @@ char *strndup(const char *, size_t);
void *memmem(const void *, size_t, const void *, size_t);
#endif
#ifndef HAVE_HTONLL
/* htonll.c */
#undef htonll
uint64_t htonll(uint64_t);
#endif
#ifndef HAVE_NTOHLL
/* ntohll.c */
#undef ntohll
uint64_t ntohll(uint64_t);
#endif
#ifndef HAVE_GETPEEREID
/* getpeereid.c */
int getpeereid(int, uid_t *, gid_t *);
@ -423,7 +448,9 @@ void *recallocarray(void *, size_t, size_t, size_t);
#ifdef HAVE_SYSTEMD
/* systemd.c */
int systemd_activated(void);
int systemd_create_socket(int, char **);
int systemd_move_to_new_cgroup(char **);
#endif
#ifdef HAVE_UTF8PROC

View File

@ -27,7 +27,7 @@
#endif
int
clock_gettime(int clock, struct timespec *ts)
clock_gettime(__unused int clock, struct timespec *ts)
{
struct timeval tv;

View File

@ -1,115 +0,0 @@
/*
* Copyright (c) 1987, 1993, 1994
* The Regents of the University of California. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
/* OPENBSD ORIGINAL: lib/libc/stdlib/getopt.c */
#include "compat.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int BSDopterr = 1, /* if error message should be printed */
BSDoptind = 1, /* index into parent argv vector */
BSDoptopt, /* character checked for validity */
BSDoptreset; /* reset getopt */
char *BSDoptarg; /* argument associated with option */
#define BADCH (int)'?'
#define BADARG (int)':'
#define EMSG ""
/*
* getopt --
* Parse argc/argv argument vector.
*/
int
BSDgetopt(int nargc, char *const *nargv, const char *ostr)
{
static const char *place = EMSG; /* option letter processing */
char *oli; /* option letter list index */
if (ostr == NULL)
return (-1);
if (BSDoptreset || !*place) { /* update scanning pointer */
BSDoptreset = 0;
if (BSDoptind >= nargc || *(place = nargv[BSDoptind]) != '-') {
place = EMSG;
return (-1);
}
if (place[1] && *++place == '-') { /* found "--" */
if (place[1])
return (BADCH);
++BSDoptind;
place = EMSG;
return (-1);
}
} /* option letter okay? */
if ((BSDoptopt = (int)*place++) == (int)':' ||
!(oli = strchr(ostr, BSDoptopt))) {
/*
* if the user didn't specify '-' as an option,
* assume it means -1.
*/
if (BSDoptopt == (int)'-')
return (-1);
if (!*place)
++BSDoptind;
if (BSDopterr && *ostr != ':')
(void)fprintf(stderr,
"%s: unknown option -- %c\n", getprogname(),
BSDoptopt);
return (BADCH);
}
if (*++oli != ':') { /* don't need argument */
BSDoptarg = NULL;
if (!*place)
++BSDoptind;
}
else { /* need an argument */
if (*place) /* no white space */
BSDoptarg = (char *)place;
else if (nargc <= ++BSDoptind) { /* no arg */
place = EMSG;
if (*ostr == ':')
return (BADARG);
if (BSDopterr)
(void)fprintf(stderr,
"%s: option requires an argument -- %c\n",
getprogname(), BSDoptopt);
return (BADCH);
}
else /* white space */
BSDoptarg = nargv[BSDoptind];
place = EMSG;
++BSDoptind;
}
return (BSDoptopt); /* dump back option letter */
}

577
compat/getopt_long.c Normal file
View File

@ -0,0 +1,577 @@
/* This file is obtained from OpenSSH:
* Repository: https://github.com/openssh/openssh-portable
* Commit: b5b405fee7f3e79d44e2d2971a4b6b4cc53f112e
* File: /openbsd-compat/getopt_long.c */
/*
* Copyright (c) 2002 Todd C. Miller <Todd.Miller@courtesan.com>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*
* Sponsored in part by the Defense Advanced Research Projects
* Agency (DARPA) and Air Force Research Laboratory, Air Force
* Materiel Command, USAF, under agreement number F39502-99-1-0512.
*/
/*-
* Copyright (c) 2000 The NetBSD Foundation, Inc.
* All rights reserved.
*
* This code is derived from software contributed to The NetBSD Foundation
* by Dieter Baron and Thomas Klausner.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
/* OPENBSD ORIGINAL: lib/libc/stdlib/getopt_long.c */
#include "compat.h"
/* The following macro constants are taken from getopt.h of OpenSSH:
* Repository: https://github.com/openssh/openssh-portable
* Commit: b5b405fee7f3e79d44e2d2971a4b6b4cc53f112e
* File: /openbsd-compat/getopt.h
*
* ---- BEGIN - Copyright notice and license of getopt.h ----
* Copyright (c) 2000 The NetBSD Foundation, Inc.
* All rights reserved.
*
* This code is derived from software contributed to The NetBSD Foundation
* by Dieter Baron and Thomas Klausner.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
* ---- END ----
*/
#define no_argument 0
#define required_argument 1
#define optional_argument 2
#if !defined(HAVE_GETOPT) || !defined(HAVE_GETOPT_OPTRESET)
#if 0
#include <err.h>
#include <getopt.h>
#endif
#include <errno.h>
#include <stdlib.h>
#include <string.h>
#include <stdarg.h>
struct option {
/* name of long option */
const char *name;
/*
* one of no_argument, required_argument, and optional_argument:
* whether option takes an argument
*/
int has_arg;
/* if not NULL, set *flag to val when option found */
int *flag;
/* if flag not NULL, value to set *flag to; else return value */
int val;
};
int opterr = 1; /* if error message should be printed */
int optind = 1; /* index into parent argv vector */
int optopt = '?'; /* character checked for validity */
int optreset; /* reset getopt */
char *optarg; /* argument associated with option */
#define PRINT_ERROR ((opterr) && (*options != ':'))
#define FLAG_PERMUTE 0x01 /* permute non-options to the end of argv */
#define FLAG_ALLARGS 0x02 /* treat non-options as args to option "-1" */
#define FLAG_LONGONLY 0x04 /* operate as getopt_long_only */
/* return values */
#define BADCH (int)'?'
#define BADARG ((*options == ':') ? (int)':' : (int)'?')
#define INORDER (int)1
#define EMSG (char *)""
static int getopt_internal(int, char * const *, const char *,
const struct option *, int *, int);
static int parse_long_options(char * const *, const char *,
const struct option *, int *, int);
static int gcd(int, int);
static void permute_args(int, int, int, char * const *);
static char *place = EMSG; /* option letter processing */
/* XXX: set optreset to 1 rather than these two */
static int nonopt_start = -1; /* first non option argument (for permute) */
static int nonopt_end = -1; /* first option after non options (for permute) */
/* Error messages */
static const char recargchar[] = "option requires an argument -- %c";
static const char recargstring[] = "option requires an argument -- %s";
static const char ambig[] = "ambiguous option -- %.*s";
static const char noarg[] = "option doesn't take an argument -- %.*s";
static const char illoptchar[] = "unknown option -- %c";
static const char illoptstring[] = "unknown option -- %s";
/*
* Compute the greatest common divisor of a and b.
*/
static int
gcd(int a, int b)
{
int c;
c = a % b;
while (c != 0) {
a = b;
b = c;
c = a % b;
}
return (b);
}
/*
* Exchange the block from nonopt_start to nonopt_end with the block
* from nonopt_end to opt_end (keeping the same order of arguments
* in each block).
*/
static void
permute_args(int panonopt_start, int panonopt_end, int opt_end,
char * const *nargv)
{
int cstart, cyclelen, i, j, ncycle, nnonopts, nopts, pos;
char *swap;
/*
* compute lengths of blocks and number and size of cycles
*/
nnonopts = panonopt_end - panonopt_start;
nopts = opt_end - panonopt_end;
ncycle = gcd(nnonopts, nopts);
cyclelen = (opt_end - panonopt_start) / ncycle;
for (i = 0; i < ncycle; i++) {
cstart = panonopt_end+i;
pos = cstart;
for (j = 0; j < cyclelen; j++) {
if (pos >= panonopt_end)
pos -= nnonopts;
else
pos += nopts;
swap = nargv[pos];
/* LINTED const cast */
((char **) nargv)[pos] = nargv[cstart];
/* LINTED const cast */
((char **)nargv)[cstart] = swap;
}
}
}
/*
* parse_long_options --
* Parse long options in argc/argv argument vector.
* Returns -1 if short_too is set and the option does not match long_options.
*/
static int
parse_long_options(char * const *nargv, const char *options,
const struct option *long_options, int *idx, int short_too)
{
char *current_argv, *has_equal;
size_t current_argv_len;
int i, match;
current_argv = place;
match = -1;
optind++;
if ((has_equal = strchr(current_argv, '=')) != NULL) {
/* argument found (--option=arg) */
current_argv_len = has_equal - current_argv;
has_equal++;
} else
current_argv_len = strlen(current_argv);
for (i = 0; long_options[i].name; i++) {
/* find matching long option */
if (strncmp(current_argv, long_options[i].name,
current_argv_len))
continue;
if (strlen(long_options[i].name) == current_argv_len) {
/* exact match */
match = i;
break;
}
/*
* If this is a known short option, don't allow
* a partial match of a single character.
*/
if (short_too && current_argv_len == 1)
continue;
if (match == -1) /* partial match */
match = i;
else {
/* ambiguous abbreviation */
if (PRINT_ERROR)
warnx(ambig, (int)current_argv_len,
current_argv);
optopt = 0;
return (BADCH);
}
}
if (match != -1) { /* option found */
if (long_options[match].has_arg == no_argument
&& has_equal) {
if (PRINT_ERROR)
warnx(noarg, (int)current_argv_len,
current_argv);
/*
* XXX: GNU sets optopt to val regardless of flag
*/
if (long_options[match].flag == NULL)
optopt = long_options[match].val;
else
optopt = 0;
return (BADARG);
}
if (long_options[match].has_arg == required_argument ||
long_options[match].has_arg == optional_argument) {
if (has_equal)
optarg = has_equal;
else if (long_options[match].has_arg ==
required_argument) {
/*
* optional argument doesn't use next nargv
*/
optarg = nargv[optind++];
}
}
if ((long_options[match].has_arg == required_argument)
&& (optarg == NULL)) {
/*
* Missing argument; leading ':' indicates no error
* should be generated.
*/
if (PRINT_ERROR)
warnx(recargstring,
current_argv);
/*
* XXX: GNU sets optopt to val regardless of flag
*/
if (long_options[match].flag == NULL)
optopt = long_options[match].val;
else
optopt = 0;
--optind;
return (BADARG);
}
} else { /* unknown option */
if (short_too) {
--optind;
return (-1);
}
if (PRINT_ERROR)
warnx(illoptstring, current_argv);
optopt = 0;
return (BADCH);
}
if (idx)
*idx = match;
if (long_options[match].flag) {
*long_options[match].flag = long_options[match].val;
return (0);
} else
return (long_options[match].val);
}
/*
* getopt_internal --
* Parse argc/argv argument vector. Called by user level routines.
*/
static int
getopt_internal(int nargc, char * const *nargv, const char *options,
const struct option *long_options, int *idx, int flags)
{
char *oli; /* option letter list index */
int optchar, short_too;
static int posixly_correct = -1;
if (options == NULL)
return (-1);
/*
* XXX Some GNU programs (like cvs) set optind to 0 instead of
* XXX using optreset. Work around this braindamage.
*/
if (optind == 0)
optind = optreset = 1;
/*
* Disable GNU extensions if POSIXLY_CORRECT is set or options
* string begins with a '+'.
*/
if (posixly_correct == -1 || optreset)
posixly_correct = (getenv("POSIXLY_CORRECT") != NULL);
if (*options == '-')
flags |= FLAG_ALLARGS;
else if (posixly_correct || *options == '+')
flags &= ~FLAG_PERMUTE;
if (*options == '+' || *options == '-')
options++;
optarg = NULL;
if (optreset)
nonopt_start = nonopt_end = -1;
start:
if (optreset || !*place) { /* update scanning pointer */
optreset = 0;
if (optind >= nargc) { /* end of argument vector */
place = EMSG;
if (nonopt_end != -1) {
/* do permutation, if we have to */
permute_args(nonopt_start, nonopt_end,
optind, nargv);
optind -= nonopt_end - nonopt_start;
}
else if (nonopt_start != -1) {
/*
* If we skipped non-options, set optind
* to the first of them.
*/
optind = nonopt_start;
}
nonopt_start = nonopt_end = -1;
return (-1);
}
if (*(place = nargv[optind]) != '-' ||
(place[1] == '\0' && strchr(options, '-') == NULL)) {
place = EMSG; /* found non-option */
if (flags & FLAG_ALLARGS) {
/*
* GNU extension:
* return non-option as argument to option 1
*/
optarg = nargv[optind++];
return (INORDER);
}
if (!(flags & FLAG_PERMUTE)) {
/*
* If no permutation wanted, stop parsing
* at first non-option.
*/
return (-1);
}
/* do permutation */
if (nonopt_start == -1)
nonopt_start = optind;
else if (nonopt_end != -1) {
permute_args(nonopt_start, nonopt_end,
optind, nargv);
nonopt_start = optind -
(nonopt_end - nonopt_start);
nonopt_end = -1;
}
optind++;
/* process next argument */
goto start;
}
if (nonopt_start != -1 && nonopt_end == -1)
nonopt_end = optind;
/*
* If we have "-" do nothing, if "--" we are done.
*/
if (place[1] != '\0' && *++place == '-' && place[1] == '\0') {
optind++;
place = EMSG;
/*
* We found an option (--), so if we skipped
* non-options, we have to permute.
*/
if (nonopt_end != -1) {
permute_args(nonopt_start, nonopt_end,
optind, nargv);
optind -= nonopt_end - nonopt_start;
}
nonopt_start = nonopt_end = -1;
return (-1);
}
}
/*
* Check long options if:
* 1) we were passed some
* 2) the arg is not just "-"
* 3) either the arg starts with -- we are getopt_long_only()
*/
if (long_options != NULL && place != nargv[optind] &&
(*place == '-' || (flags & FLAG_LONGONLY))) {
short_too = 0;
if (*place == '-')
place++; /* --foo long option */
else if (*place != ':' && strchr(options, *place) != NULL)
short_too = 1; /* could be short option too */
optchar = parse_long_options(nargv, options, long_options,
idx, short_too);
if (optchar != -1) {
place = EMSG;
return (optchar);
}
}
if ((optchar = (int)*place++) == (int)':' ||
(optchar == (int)'-' && *place != '\0') ||
(oli = strchr(options, optchar)) == NULL) {
/*
* If the user specified "-" and '-' isn't listed in
* options, return -1 (non-option) as per POSIX.
* Otherwise, it is an unknown option character (or ':').
*/
if (optchar == (int)'-' && *place == '\0')
return (-1);
if (!*place)
++optind;
if (PRINT_ERROR)
warnx(illoptchar, optchar);
optopt = optchar;
return (BADCH);
}
if (long_options != NULL && optchar == 'W' && oli[1] == ';') {
/* -W long-option */
if (*place) /* no space */
/* NOTHING */;
else if (++optind >= nargc) { /* no arg */
place = EMSG;
if (PRINT_ERROR)
warnx(recargchar, optchar);
optopt = optchar;
return (BADARG);
} else /* white space */
place = nargv[optind];
optchar = parse_long_options(nargv, options, long_options,
idx, 0);
place = EMSG;
return (optchar);
}
if (*++oli != ':') { /* doesn't take argument */
if (!*place)
++optind;
} else { /* takes (optional) argument */
optarg = NULL;
if (*place) /* no white space */
optarg = place;
else if (oli[1] != ':') { /* arg not optional */
if (++optind >= nargc) { /* no arg */
place = EMSG;
if (PRINT_ERROR)
warnx(recargchar, optchar);
optopt = optchar;
return (BADARG);
} else
optarg = nargv[optind];
}
place = EMSG;
++optind;
}
/* dump back option letter */
return (optchar);
}
/*
* getopt --
* Parse argc/argv argument vector.
*
* [eventually this will replace the BSD getopt]
*/
int
getopt(int nargc, char * const *nargv, const char *options)
{
/*
* We don't pass FLAG_PERMUTE to getopt_internal() since
* the BSD getopt(3) (unlike GNU) has never done this.
*
* Furthermore, since many privileged programs call getopt()
* before dropping privileges it makes sense to keep things
* as simple (and bug-free) as possible.
*/
return (getopt_internal(nargc, nargv, options, NULL, NULL, 0));
}
#if 0
/*
* getopt_long --
* Parse argc/argv argument vector.
*/
int
getopt_long(int nargc, char * const *nargv, const char *options,
const struct option *long_options, int *idx)
{
return (getopt_internal(nargc, nargv, options, long_options, idx,
FLAG_PERMUTE));
}
/*
* getopt_long_only --
* Parse argc/argv argument vector.
*/
int
getopt_long_only(int nargc, char * const *nargv, const char *options,
const struct option *long_options, int *idx)
{
return (getopt_internal(nargc, nargv, options, long_options, idx,
FLAG_PERMUTE|FLAG_LONGONLY));
}
#endif
#endif /* !defined(HAVE_GETOPT) || !defined(HAVE_OPTRESET) */

View File

@ -18,6 +18,7 @@
#include <sys/socket.h>
#include <stdio.h>
#include <unistd.h>
#ifdef HAVE_UCRED_H
#include <ucred.h>
@ -49,6 +50,8 @@ getpeereid(int s, uid_t *uid, gid_t *gid)
ucred_free(ucred);
return (0);
#else
return (getuid());
*uid = geteuid();
*gid = getegid();
return (0);
#endif
}

31
compat/htonll.c Normal file
View File

@ -0,0 +1,31 @@
/*
* Copyright (c) 2024 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 <arpa/inet.h>
#include <sys/types.h>
#include "compat.h"
uint64_t
htonll(uint64_t v)
{
uint32_t b;
uint32_t t;
b = htonl (v & 0xffffffff);
t = htonl (v >> 32);
return ((uint64_t)b << 32 | t);
}

File diff suppressed because it is too large Load Diff

View File

@ -1,6 +1,7 @@
/* $OpenBSD: imsg.c,v 1.16 2017/12/14 09:27:44 kettenis Exp $ */
/* $OpenBSD: imsg.c,v 1.37 2024/11/26 13:57:31 claudio Exp $ */
/*
* Copyright (c) 2023 Claudio Jeker <claudio@openbsd.org>
* Copyright (c) 2003, 2004 Henning Brauer <henning@openbsd.org>
*
* Permission to use, copy, modify, and distribute this software for any
@ -21,6 +22,7 @@
#include <sys/uio.h>
#include <errno.h>
#include <stddef.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
@ -28,203 +30,288 @@
#include "compat.h"
#include "imsg.h"
int imsg_fd_overhead = 0;
#define IMSG_ALLOW_FDPASS 0x01
#define IMSG_FD_MARK 0x80000000U
static int imsg_get_fd(struct imsgbuf *);
static struct ibuf *imsg_parse_hdr(struct ibuf *, void *, int *);
int
imsgbuf_init(struct imsgbuf *imsgbuf, int fd)
{
imsgbuf->w = msgbuf_new_reader(IMSG_HEADER_SIZE, imsg_parse_hdr,
imsgbuf);
if (imsgbuf->w == NULL)
return (-1);
imsgbuf->pid = getpid();
imsgbuf->maxsize = MAX_IMSGSIZE;
imsgbuf->fd = fd;
imsgbuf->flags = 0;
return (0);
}
void
imsg_init(struct imsgbuf *ibuf, int fd)
imsgbuf_allow_fdpass(struct imsgbuf *imsgbuf)
{
msgbuf_init(&ibuf->w);
memset(&ibuf->r, 0, sizeof(ibuf->r));
ibuf->fd = fd;
ibuf->w.fd = fd;
ibuf->pid = getpid();
TAILQ_INIT(&ibuf->fds);
}
ssize_t
imsg_read(struct imsgbuf *ibuf)
{
struct msghdr msg;
struct cmsghdr *cmsg;
union {
struct cmsghdr hdr;
char buf[CMSG_SPACE(sizeof(int) * 1)];
} cmsgbuf;
struct iovec iov;
ssize_t n = -1;
int fd;
struct imsg_fd *ifd;
memset(&msg, 0, sizeof(msg));
memset(&cmsgbuf, 0, sizeof(cmsgbuf));
iov.iov_base = ibuf->r.buf + ibuf->r.wpos;
iov.iov_len = sizeof(ibuf->r.buf) - ibuf->r.wpos;
msg.msg_iov = &iov;
msg.msg_iovlen = 1;
msg.msg_control = &cmsgbuf.buf;
msg.msg_controllen = sizeof(cmsgbuf.buf);
if ((ifd = calloc(1, sizeof(struct imsg_fd))) == NULL)
return (-1);
again:
if (getdtablecount() + imsg_fd_overhead +
(int)((CMSG_SPACE(sizeof(int))-CMSG_SPACE(0))/sizeof(int))
>= getdtablesize()) {
errno = EAGAIN;
free(ifd);
return (-1);
}
if ((n = recvmsg(ibuf->fd, &msg, 0)) == -1) {
if (errno == EINTR)
goto again;
goto fail;
}
ibuf->r.wpos += n;
for (cmsg = CMSG_FIRSTHDR(&msg); cmsg != NULL;
cmsg = CMSG_NXTHDR(&msg, cmsg)) {
if (cmsg->cmsg_level == SOL_SOCKET &&
cmsg->cmsg_type == SCM_RIGHTS) {
int i;
int j;
/*
* We only accept one file descriptor. Due to C
* padding rules, our control buffer might contain
* more than one fd, and we must close them.
*/
j = ((char *)cmsg + cmsg->cmsg_len -
(char *)CMSG_DATA(cmsg)) / sizeof(int);
for (i = 0; i < j; i++) {
fd = ((int *)CMSG_DATA(cmsg))[i];
if (ifd != NULL) {
ifd->fd = fd;
TAILQ_INSERT_TAIL(&ibuf->fds, ifd,
entry);
ifd = NULL;
} else
close(fd);
}
}
/* we do not handle other ctl data level */
}
fail:
free(ifd);
return (n);
}
ssize_t
imsg_get(struct imsgbuf *ibuf, struct imsg *imsg)
{
size_t av, left, datalen;
av = ibuf->r.wpos;
if (IMSG_HEADER_SIZE > av)
return (0);
memcpy(&imsg->hdr, ibuf->r.buf, sizeof(imsg->hdr));
if (imsg->hdr.len < IMSG_HEADER_SIZE ||
imsg->hdr.len > MAX_IMSGSIZE) {
errno = ERANGE;
return (-1);
}
if (imsg->hdr.len > av)
return (0);
datalen = imsg->hdr.len - IMSG_HEADER_SIZE;
ibuf->r.rptr = ibuf->r.buf + IMSG_HEADER_SIZE;
if (datalen == 0)
imsg->data = NULL;
else if ((imsg->data = malloc(datalen)) == NULL)
return (-1);
if (imsg->hdr.flags & IMSGF_HASFD)
imsg->fd = imsg_get_fd(ibuf);
else
imsg->fd = -1;
memcpy(imsg->data, ibuf->r.rptr, datalen);
if (imsg->hdr.len < av) {
left = av - imsg->hdr.len;
memmove(&ibuf->r.buf, ibuf->r.buf + imsg->hdr.len, left);
ibuf->r.wpos = left;
} else
ibuf->r.wpos = 0;
return (datalen + IMSG_HEADER_SIZE);
imsgbuf->flags |= IMSG_ALLOW_FDPASS;
}
int
imsg_compose(struct imsgbuf *ibuf, uint32_t type, uint32_t peerid, pid_t pid,
int fd, const void *data, uint16_t datalen)
imsgbuf_set_maxsize(struct imsgbuf *imsgbuf, uint32_t maxsize)
{
if (maxsize < IMSG_HEADER_SIZE || maxsize & IMSG_FD_MARK) {
errno = EINVAL;
return (-1);
}
imsgbuf->maxsize = maxsize;
return (0);
}
int
imsgbuf_read(struct imsgbuf *imsgbuf)
{
if (imsgbuf->flags & IMSG_ALLOW_FDPASS)
return msgbuf_read(imsgbuf->fd, imsgbuf->w);
else
return ibuf_read(imsgbuf->fd, imsgbuf->w);
}
int
imsgbuf_write(struct imsgbuf *imsgbuf)
{
if (imsgbuf->flags & IMSG_ALLOW_FDPASS)
return msgbuf_write(imsgbuf->fd, imsgbuf->w);
else
return ibuf_write(imsgbuf->fd, imsgbuf->w);
}
int
imsgbuf_flush(struct imsgbuf *imsgbuf)
{
while (imsgbuf_queuelen(imsgbuf) > 0) {
if (imsgbuf_write(imsgbuf) == -1)
return (-1);
}
return (0);
}
void
imsgbuf_clear(struct imsgbuf *imsgbuf)
{
msgbuf_free(imsgbuf->w);
imsgbuf->w = NULL;
}
uint32_t
imsgbuf_queuelen(struct imsgbuf *imsgbuf)
{
return msgbuf_queuelen(imsgbuf->w);
}
ssize_t
imsg_get(struct imsgbuf *imsgbuf, struct imsg *imsg)
{
struct imsg m;
struct ibuf *buf;
if ((buf = msgbuf_get(imsgbuf->w)) == NULL)
return (0);
if (ibuf_get(buf, &m.hdr, sizeof(m.hdr)) == -1)
return (-1);
if (ibuf_size(buf))
m.data = ibuf_data(buf);
else
m.data = NULL;
m.buf = buf;
m.hdr.len &= ~IMSG_FD_MARK;
*imsg = m;
return (ibuf_size(buf) + IMSG_HEADER_SIZE);
}
int
imsg_get_ibuf(struct imsg *imsg, struct ibuf *ibuf)
{
if (ibuf_size(imsg->buf) == 0) {
errno = EBADMSG;
return (-1);
}
return ibuf_get_ibuf(imsg->buf, ibuf_size(imsg->buf), ibuf);
}
int
imsg_get_data(struct imsg *imsg, void *data, size_t len)
{
if (len == 0) {
errno = EINVAL;
return (-1);
}
if (ibuf_size(imsg->buf) != len) {
errno = EBADMSG;
return (-1);
}
return ibuf_get(imsg->buf, data, len);
}
int
imsg_get_fd(struct imsg *imsg)
{
return ibuf_fd_get(imsg->buf);
}
uint32_t
imsg_get_id(struct imsg *imsg)
{
return (imsg->hdr.peerid);
}
size_t
imsg_get_len(struct imsg *imsg)
{
return ibuf_size(imsg->buf);
}
pid_t
imsg_get_pid(struct imsg *imsg)
{
return (imsg->hdr.pid);
}
uint32_t
imsg_get_type(struct imsg *imsg)
{
return (imsg->hdr.type);
}
int
imsg_compose(struct imsgbuf *imsgbuf, uint32_t type, uint32_t id, pid_t pid,
int fd, const void *data, size_t datalen)
{
struct ibuf *wbuf;
if ((wbuf = imsg_create(ibuf, type, peerid, pid, datalen)) == NULL)
if ((wbuf = imsg_create(imsgbuf, type, id, pid, datalen)) == NULL)
return (-1);
if (imsg_add(wbuf, data, datalen) == -1)
return (-1);
wbuf->fd = fd;
imsg_close(ibuf, wbuf);
ibuf_fd_set(wbuf, fd);
imsg_close(imsgbuf, wbuf);
return (1);
}
int
imsg_composev(struct imsgbuf *ibuf, uint32_t type, uint32_t peerid, pid_t pid,
imsg_composev(struct imsgbuf *imsgbuf, uint32_t type, uint32_t id, pid_t pid,
int fd, const struct iovec *iov, int iovcnt)
{
struct ibuf *wbuf;
int i, datalen = 0;
int i;
size_t datalen = 0;
for (i = 0; i < iovcnt; i++)
datalen += iov[i].iov_len;
if ((wbuf = imsg_create(ibuf, type, peerid, pid, datalen)) == NULL)
if ((wbuf = imsg_create(imsgbuf, type, id, pid, datalen)) == NULL)
return (-1);
for (i = 0; i < iovcnt; i++)
if (imsg_add(wbuf, iov[i].iov_base, iov[i].iov_len) == -1)
return (-1);
wbuf->fd = fd;
imsg_close(ibuf, wbuf);
ibuf_fd_set(wbuf, fd);
imsg_close(imsgbuf, wbuf);
return (1);
}
/* ARGSUSED */
/*
* Enqueue imsg with payload from ibuf buf. fd passing is not possible
* with this function.
*/
int
imsg_compose_ibuf(struct imsgbuf *imsgbuf, uint32_t type, uint32_t id,
pid_t pid, struct ibuf *buf)
{
struct ibuf *hdrbuf = NULL;
struct imsg_hdr hdr;
int save_errno;
if (ibuf_size(buf) + IMSG_HEADER_SIZE > imsgbuf->maxsize) {
errno = ERANGE;
goto fail;
}
hdr.type = type;
hdr.len = ibuf_size(buf) + IMSG_HEADER_SIZE;
hdr.peerid = id;
if ((hdr.pid = pid) == 0)
hdr.pid = imsgbuf->pid;
if ((hdrbuf = ibuf_open(IMSG_HEADER_SIZE)) == NULL)
goto fail;
if (imsg_add(hdrbuf, &hdr, sizeof(hdr)) == -1)
goto fail;
ibuf_close(imsgbuf->w, hdrbuf);
ibuf_close(imsgbuf->w, buf);
return (1);
fail:
save_errno = errno;
ibuf_free(buf);
ibuf_free(hdrbuf);
errno = save_errno;
return (-1);
}
/*
* Forward imsg to another channel. Any attached fd is closed.
*/
int
imsg_forward(struct imsgbuf *imsgbuf, struct imsg *msg)
{
struct ibuf *wbuf;
size_t len;
ibuf_rewind(msg->buf);
ibuf_skip(msg->buf, sizeof(msg->hdr));
len = ibuf_size(msg->buf);
if ((wbuf = imsg_create(imsgbuf, msg->hdr.type, msg->hdr.peerid,
msg->hdr.pid, len)) == NULL)
return (-1);
if (len != 0) {
if (ibuf_add_ibuf(wbuf, msg->buf) == -1) {
ibuf_free(wbuf);
return (-1);
}
}
imsg_close(imsgbuf, wbuf);
return (1);
}
struct ibuf *
imsg_create(struct imsgbuf *ibuf, uint32_t type, uint32_t peerid, pid_t pid,
uint16_t datalen)
imsg_create(struct imsgbuf *imsgbuf, uint32_t type, uint32_t id, pid_t pid,
size_t datalen)
{
struct ibuf *wbuf;
struct imsg_hdr hdr;
datalen += IMSG_HEADER_SIZE;
if (datalen > MAX_IMSGSIZE) {
if (datalen > imsgbuf->maxsize) {
errno = ERANGE;
return (NULL);
}
hdr.type = type;
hdr.flags = 0;
hdr.peerid = peerid;
hdr.peerid = id;
if ((hdr.pid = pid) == 0)
hdr.pid = ibuf->pid;
if ((wbuf = ibuf_dynamic(datalen, MAX_IMSGSIZE)) == NULL) {
hdr.pid = imsgbuf->pid;
if ((wbuf = ibuf_dynamic(datalen, imsgbuf->maxsize)) == NULL) {
return (NULL);
}
if (imsg_add(wbuf, &hdr, sizeof(hdr)) == -1)
@ -234,7 +321,7 @@ imsg_create(struct imsgbuf *ibuf, uint32_t type, uint32_t peerid, pid_t pid,
}
int
imsg_add(struct ibuf *msg, const void *data, uint16_t datalen)
imsg_add(struct ibuf *msg, const void *data, size_t datalen)
{
if (datalen)
if (ibuf_add(msg, data, datalen) == -1) {
@ -245,58 +332,47 @@ imsg_add(struct ibuf *msg, const void *data, uint16_t datalen)
}
void
imsg_close(struct imsgbuf *ibuf, struct ibuf *msg)
imsg_close(struct imsgbuf *imsgbuf, struct ibuf *msg)
{
struct imsg_hdr *hdr;
uint32_t len;
hdr = (struct imsg_hdr *)msg->buf;
hdr->flags &= ~IMSGF_HASFD;
if (msg->fd != -1)
hdr->flags |= IMSGF_HASFD;
hdr->len = (uint16_t)msg->wpos;
ibuf_close(&ibuf->w, msg);
len = ibuf_size(msg);
if (ibuf_fd_avail(msg))
len |= IMSG_FD_MARK;
(void)ibuf_set_h32(msg, offsetof(struct imsg_hdr, len), len);
ibuf_close(imsgbuf->w, msg);
}
void
imsg_free(struct imsg *imsg)
{
freezero(imsg->data, imsg->hdr.len - IMSG_HEADER_SIZE);
ibuf_free(imsg->buf);
}
static int
imsg_get_fd(struct imsgbuf *ibuf)
static struct ibuf *
imsg_parse_hdr(struct ibuf *buf, void *arg, int *fd)
{
int fd;
struct imsg_fd *ifd;
struct imsgbuf *imsgbuf = arg;
struct imsg_hdr hdr;
struct ibuf *b;
uint32_t len;
if ((ifd = TAILQ_FIRST(&ibuf->fds)) == NULL)
return (-1);
if (ibuf_get(buf, &hdr, sizeof(hdr)) == -1)
return (NULL);
fd = ifd->fd;
TAILQ_REMOVE(&ibuf->fds, ifd, entry);
free(ifd);
len = hdr.len & ~IMSG_FD_MARK;
return (fd);
if (len < IMSG_HEADER_SIZE || len > imsgbuf->maxsize) {
errno = ERANGE;
return (NULL);
}
if ((b = ibuf_open(len)) == NULL)
return (NULL);
if (hdr.len & IMSG_FD_MARK) {
ibuf_fd_set(b, *fd);
*fd = -1;
}
int
imsg_flush(struct imsgbuf *ibuf)
{
while (ibuf->w.queued)
if (msgbuf_write(&ibuf->w) <= 0)
return (-1);
return (0);
}
void
imsg_clear(struct imsgbuf *ibuf)
{
int fd;
msgbuf_clear(&ibuf->w);
while ((fd = imsg_get_fd(ibuf)) != -1)
close(fd);
return b;
}

View File

@ -1,6 +1,7 @@
/* $OpenBSD: imsg.h,v 1.5 2019/01/20 02:50:03 bcook Exp $ */
/* $OpenBSD: imsg.h,v 1.19 2024/11/26 13:57:31 claudio Exp $ */
/*
* Copyright (c) 2023 Claudio Jeker <claudio@openbsd.org>
* Copyright (c) 2006, 2007 Pierre-Yves Ritschard <pyr@openbsd.org>
* Copyright (c) 2006, 2007, 2008 Reyk Floeter <reyk@openbsd.org>
* Copyright (c) 2003, 2004 Henning Brauer <henning@openbsd.org>
@ -21,7 +22,7 @@
#ifndef _IMSG_H_
#define _IMSG_H_
#include <stdint.h>
#include <sys/types.h>
#define IBUF_READ_SIZE 65535
#define IMSG_HEADER_SIZE sizeof(struct imsg_hdr)
@ -37,77 +38,116 @@ struct ibuf {
int fd;
};
struct msgbuf {
TAILQ_HEAD(, ibuf) bufs;
uint32_t queued;
int fd;
};
struct ibuf_read {
unsigned char buf[IBUF_READ_SIZE];
unsigned char *rptr;
size_t wpos;
};
struct imsg_fd {
TAILQ_ENTRY(imsg_fd) entry;
int fd;
};
struct msgbuf;
struct imsgbuf {
TAILQ_HEAD(, imsg_fd) fds;
struct ibuf_read r;
struct msgbuf w;
int fd;
struct msgbuf *w;
pid_t pid;
uint32_t maxsize;
int fd;
int flags;
};
#define IMSGF_HASFD 1
struct imsg_hdr {
uint32_t type;
uint16_t len;
uint16_t flags;
uint32_t len;
uint32_t peerid;
uint32_t pid;
};
struct imsg {
struct imsg_hdr hdr;
int fd;
void *data;
struct ibuf *buf;
};
struct iovec;
/* buffer.c */
/* imsg-buffer.c */
struct ibuf *ibuf_open(size_t);
struct ibuf *ibuf_dynamic(size_t, size_t);
int ibuf_add(struct ibuf *, const void *, size_t);
int ibuf_add_ibuf(struct ibuf *, const struct ibuf *);
int ibuf_add_zero(struct ibuf *, size_t);
int ibuf_add_n8(struct ibuf *, uint64_t);
int ibuf_add_n16(struct ibuf *, uint64_t);
int ibuf_add_n32(struct ibuf *, uint64_t);
int ibuf_add_n64(struct ibuf *, uint64_t);
int ibuf_add_h16(struct ibuf *, uint64_t);
int ibuf_add_h32(struct ibuf *, uint64_t);
int ibuf_add_h64(struct ibuf *, uint64_t);
void *ibuf_reserve(struct ibuf *, size_t);
void *ibuf_seek(struct ibuf *, size_t, size_t);
size_t ibuf_size(struct ibuf *);
size_t ibuf_left(struct ibuf *);
int ibuf_set(struct ibuf *, size_t, const void *, size_t);
int ibuf_set_n8(struct ibuf *, size_t, uint64_t);
int ibuf_set_n16(struct ibuf *, size_t, uint64_t);
int ibuf_set_n32(struct ibuf *, size_t, uint64_t);
int ibuf_set_n64(struct ibuf *, size_t, uint64_t);
int ibuf_set_h16(struct ibuf *, size_t, uint64_t);
int ibuf_set_h32(struct ibuf *, size_t, uint64_t);
int ibuf_set_h64(struct ibuf *, size_t, uint64_t);
void *ibuf_data(const struct ibuf *);
size_t ibuf_size(const struct ibuf *);
size_t ibuf_left(const struct ibuf *);
int ibuf_truncate(struct ibuf *, size_t);
void ibuf_rewind(struct ibuf *);
void ibuf_close(struct msgbuf *, struct ibuf *);
int ibuf_write(struct msgbuf *);
void ibuf_from_buffer(struct ibuf *, void *, size_t);
void ibuf_from_ibuf(struct ibuf *, const struct ibuf *);
int ibuf_get(struct ibuf *, void *, size_t);
int ibuf_get_ibuf(struct ibuf *, size_t, struct ibuf *);
int ibuf_get_n8(struct ibuf *, uint8_t *);
int ibuf_get_n16(struct ibuf *, uint16_t *);
int ibuf_get_n32(struct ibuf *, uint32_t *);
int ibuf_get_n64(struct ibuf *, uint64_t *);
int ibuf_get_h16(struct ibuf *, uint16_t *);
int ibuf_get_h32(struct ibuf *, uint32_t *);
int ibuf_get_h64(struct ibuf *, uint64_t *);
char *ibuf_get_string(struct ibuf *, size_t);
int ibuf_skip(struct ibuf *, size_t);
void ibuf_free(struct ibuf *);
void msgbuf_init(struct msgbuf *);
int ibuf_fd_avail(struct ibuf *);
int ibuf_fd_get(struct ibuf *);
void ibuf_fd_set(struct ibuf *, int);
struct msgbuf *msgbuf_new(void);
struct msgbuf *msgbuf_new_reader(size_t,
struct ibuf *(*)(struct ibuf *, void *, int *), void *);
void msgbuf_free(struct msgbuf *);
void msgbuf_clear(struct msgbuf *);
int msgbuf_write(struct msgbuf *);
void msgbuf_drain(struct msgbuf *, size_t);
uint32_t msgbuf_queuelen(struct msgbuf *);
int ibuf_write(int, struct msgbuf *);
int msgbuf_write(int, struct msgbuf *);
int ibuf_read(int, struct msgbuf *);
int msgbuf_read(int, struct msgbuf *);
struct ibuf *msgbuf_get(struct msgbuf *);
/* imsg.c */
void imsg_init(struct imsgbuf *, int);
ssize_t imsg_read(struct imsgbuf *);
int imsgbuf_init(struct imsgbuf *, int);
void imsgbuf_allow_fdpass(struct imsgbuf *imsgbuf);
int imsgbuf_set_maxsize(struct imsgbuf *, uint32_t);
int imsgbuf_read(struct imsgbuf *);
int imsgbuf_write(struct imsgbuf *);
int imsgbuf_flush(struct imsgbuf *);
void imsgbuf_clear(struct imsgbuf *);
uint32_t imsgbuf_queuelen(struct imsgbuf *);
ssize_t imsg_get(struct imsgbuf *, struct imsg *);
int imsg_get_ibuf(struct imsg *, struct ibuf *);
int imsg_get_data(struct imsg *, void *, size_t);
int imsg_get_fd(struct imsg *);
uint32_t imsg_get_id(struct imsg *);
size_t imsg_get_len(struct imsg *);
pid_t imsg_get_pid(struct imsg *);
uint32_t imsg_get_type(struct imsg *);
int imsg_forward(struct imsgbuf *, struct imsg *);
int imsg_compose(struct imsgbuf *, uint32_t, uint32_t, pid_t, int,
const void *, uint16_t);
const void *, size_t);
int imsg_composev(struct imsgbuf *, uint32_t, uint32_t, pid_t, int,
const struct iovec *, int);
struct ibuf *imsg_create(struct imsgbuf *, uint32_t, uint32_t, pid_t, uint16_t);
int imsg_add(struct ibuf *, const void *, uint16_t);
int imsg_compose_ibuf(struct imsgbuf *, uint32_t, uint32_t, pid_t,
struct ibuf *);
struct ibuf *imsg_create(struct imsgbuf *, uint32_t, uint32_t, pid_t, size_t);
int imsg_add(struct ibuf *, const void *, size_t);
void imsg_close(struct imsgbuf *, struct ibuf *);
void imsg_free(struct imsg *);
int imsg_flush(struct imsgbuf *);
void imsg_clear(struct imsgbuf *);
#endif

31
compat/ntohll.c Normal file
View File

@ -0,0 +1,31 @@
/*
* Copyright (c) 2024 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 <arpa/inet.h>
#include <sys/types.h>
#include "compat.h"
uint64_t
ntohll(uint64_t v)
{
uint32_t b;
uint32_t t;
b = ntohl (v & 0xffffffff);
t = ntohl (v >> 32);
return ((uint64_t)b << 32 | t);
}

View File

@ -32,8 +32,8 @@
* @(#)queue.h 8.5 (Berkeley) 8/20/94
*/
#ifndef _SYS_QUEUE_H_
#define _SYS_QUEUE_H_
#ifndef _COMPAT_QUEUE_H_
#define _COMPAT_QUEUE_H_
/*
* This file defines five types of data structures: singly-linked lists,
@ -530,4 +530,4 @@ struct { \
} \
} while (0)
#endif /* !_SYS_QUEUE_H_ */
#endif /* !_COMPAT_QUEUE_H_ */

View File

@ -17,6 +17,7 @@
#include <sys/types.h>
#include <stdarg.h>
#include <stdlib.h>
#include <string.h>
#include "compat.h"

View File

@ -19,17 +19,35 @@
#include <sys/types.h>
#include <sys/un.h>
#include <systemd/sd-bus.h>
#include <systemd/sd-daemon.h>
#include <systemd/sd-login.h>
#include <systemd/sd-id128.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include "tmux.h"
#ifndef SD_ID128_UUID_FORMAT_STR
#define SD_ID128_UUID_FORMAT_STR \
"%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x"
#endif
int
systemd_activated(void)
{
return (sd_listen_fds(0) >= 1);
}
int
systemd_create_socket(int flags, char **cause)
{
int fds;
int fd;
struct sockaddr_un sa;
int addrlen = sizeof sa;
socklen_t addrlen = sizeof sa;
fds = sd_listen_fds(0);
if (fds > 1) { /* too many file descriptors */
@ -56,3 +74,253 @@ fail:
xasprintf(cause, "systemd socket error (%s)", strerror(errno));
return (-1);
}
struct systemd_job_watch {
const char *path;
int done;
};
static int
job_removed_handler(sd_bus_message *m, void *userdata, sd_bus_error *ret_error)
{
struct systemd_job_watch *watch = userdata;
const char *path = NULL;
uint32_t id;
int r;
/* This handler could be called during the sd_bus_call. */
if (watch->path == NULL)
return 0;
r = sd_bus_message_read(m, "uo", &id, &path);
if (r < 0)
return (r);
if (strcmp(path, watch->path) == 0)
watch->done = 1;
return (0);
}
int
systemd_move_to_new_cgroup(char **cause)
{
sd_bus_error error = SD_BUS_ERROR_NULL;
sd_bus_message *m = NULL, *reply = NULL;
sd_bus *bus = NULL;
sd_bus_slot *slot = NULL;
char *name, *desc, *slice;
sd_id128_t uuid;
int r;
uint64_t elapsed_usec;
pid_t pid, parent_pid;
struct timeval start, now;
struct systemd_job_watch watch = {};
gettimeofday(&start, NULL);
/* Connect to the session bus. */
r = sd_bus_default_user(&bus);
if (r < 0) {
xasprintf(cause, "failed to connect to session bus: %s",
strerror(-r));
goto finish;
}
/* Start watching for JobRemoved events */
r = sd_bus_match_signal(bus, &slot,
"org.freedesktop.systemd1",
"/org/freedesktop/systemd1",
"org.freedesktop.systemd1.Manager",
"JobRemoved",
job_removed_handler,
&watch);
if (r < 0) {
xasprintf(cause, "failed to create match signal: %s",
strerror(-r));
goto finish;
}
/* Start building the method call. */
r = sd_bus_message_new_method_call(bus, &m,
"org.freedesktop.systemd1",
"/org/freedesktop/systemd1",
"org.freedesktop.systemd1.Manager",
"StartTransientUnit");
if (r < 0) {
xasprintf(cause, "failed to create bus message: %s",
strerror(-r));
goto finish;
}
/* Generate a unique name for the new scope, to avoid collisions. */
r = sd_id128_randomize(&uuid);
if (r < 0) {
xasprintf(cause, "failed to generate uuid: %s", strerror(-r));
goto finish;
}
xasprintf(&name, "tmux-spawn-" SD_ID128_UUID_FORMAT_STR ".scope",
SD_ID128_FORMAT_VAL(uuid));
r = sd_bus_message_append(m, "s", name);
free(name);
if (r < 0) {
xasprintf(cause, "failed to append to bus message: %s",
strerror(-r));
goto finish;
}
/* Mode: fail if there's a queued unit with the same name. */
r = sd_bus_message_append(m, "s", "fail");
if (r < 0) {
xasprintf(cause, "failed to append to bus message: %s",
strerror(-r));
goto finish;
}
/* Start properties array. */
r = sd_bus_message_open_container(m, 'a', "(sv)");
if (r < 0) {
xasprintf(cause, "failed to start properties array: %s",
strerror(-r));
goto finish;
}
pid = getpid();
parent_pid = getppid();
xasprintf(&desc, "tmux child pane %ld launched by process %ld",
(long)pid, (long)parent_pid);
r = sd_bus_message_append(m, "(sv)", "Description", "s", desc);
free(desc);
if (r < 0) {
xasprintf(cause, "failed to append to properties: %s",
strerror(-r));
goto finish;
}
/*
* Make sure that the session shells are terminated with SIGHUP since
* bash and friends tend to ignore SIGTERM.
*/
r = sd_bus_message_append(m, "(sv)", "SendSIGHUP", "b", 1);
if (r < 0) {
xasprintf(cause, "failed to append to properties: %s",
strerror(-r));
goto finish;
}
/*
* Inherit the slice from the parent process, or default to
* "app-tmux.slice" if that fails.
*/
r = sd_pid_get_user_slice(parent_pid, &slice);
if (r < 0) {
slice = xstrdup("app-tmux.slice");
}
r = sd_bus_message_append(m, "(sv)", "Slice", "s", slice);
free(slice);
if (r < 0) {
xasprintf(cause, "failed to append to properties: %s",
strerror(-r));
goto finish;
}
/* PIDs to add to the scope: length - 1 array of uint32_t. */
r = sd_bus_message_append(m, "(sv)", "PIDs", "au", 1, pid);
if (r < 0) {
xasprintf(cause, "failed to append to properties: %s",
strerror(-r));
goto finish;
}
/* Clean up the scope even if it fails. */
r = sd_bus_message_append(m, "(sv)", "CollectMode", "s",
"inactive-or-failed");
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) {
xasprintf(cause, "failed to end properties array: %s",
strerror(-r));
goto finish;
}
/* aux is currently unused and should be passed an empty array. */
r = sd_bus_message_append(m, "a(sa(sv))", 0);
if (r < 0) {
xasprintf(cause, "failed to append to bus message: %s",
strerror(-r));
goto finish;
}
/* Call the method with a timeout of 1 second = 1e6 us. */
r = sd_bus_call(bus, m, 1000000, &error, &reply);
if (r < 0) {
if (error.message != NULL) {
/* We have a specific error message from sd-bus. */
xasprintf(cause, "StartTransientUnit call failed: %s",
error.message);
} else {
xasprintf(cause, "StartTransientUnit call failed: %s",
strerror(-r));
}
goto finish;
}
/* Get the job (object path) from the reply */
r = sd_bus_message_read(reply, "o", &watch.path);
if (r < 0) {
xasprintf(cause, "failed to parse method reply: %s",
strerror(-r));
goto finish;
}
while (!watch.done) {
/* Process events including callbacks. */
r = sd_bus_process(bus, NULL);
if (r < 0) {
xasprintf(cause,
"failed waiting for cgroup allocation: %s",
strerror(-r));
goto finish;
}
/*
* A positive return means we handled an event and should keep
* processing; zero indicates no events available, so wait.
*/
if (r > 0)
continue;
gettimeofday(&now, NULL);
elapsed_usec = (now.tv_sec - start.tv_sec) * 1000000 +
now.tv_usec - start.tv_usec;
if (elapsed_usec >= 1000000) {
xasprintf(cause,
"timeout waiting for cgroup allocation");
goto finish;
}
r = sd_bus_wait(bus, 1000000 - elapsed_usec);
if (r < 0) {
xasprintf(cause,
"failed waiting for cgroup allocation: %s",
strerror(-r));
goto finish;
}
}
finish:
sd_bus_error_free(&error);
sd_bus_message_unref(m);
sd_bus_message_unref(reply);
sd_bus_slot_unref(slot);
sd_bus_unref(bus);
return (r);
}

View File

@ -1,6 +1,6 @@
# configure.ac
AC_INIT([tmux], 3.3a)
AC_INIT([tmux], next-3.6)
AC_PREREQ([2.60])
AC_CONFIG_AUX_DIR(etc)
@ -44,7 +44,7 @@ fi
# Set up the compiler in two different ways and say yes we may want to install.
AC_PROG_CC
AM_PROG_CC_C_O
AC_PROG_CC_C99
m4_version_prereq(2.70, [AC_PROG_CC], [AC_PROG_CC_C99])
AC_PROG_CPP
AC_PROG_EGREP
AC_PROG_INSTALL
@ -56,10 +56,11 @@ AC_USE_SYSTEM_EXTENSIONS
test "$sysconfdir" = '${prefix}/etc' && sysconfdir=/etc
# Is this --enable-debug?
case "x$VERSION" in xnext*) enable_debug=yes;; esac
AC_ARG_ENABLE(
debug,
AS_HELP_STRING(--enable-debug, enable debug build flags),
,
[case "x$VERSION" in xnext*) enable_debug=yes;; esac]
)
AM_CONDITIONAL(IS_DEBUG, test "x$enable_debug" = xyes)
@ -148,7 +149,7 @@ AC_CHECK_FUNCS([ \
prctl \
proc_pidinfo \
getpeerucred \
sysconf \
sysconf
])
# Check for functions with a compatibility implementation.
@ -165,7 +166,9 @@ AC_REPLACE_FUNCS([ \
getpeereid \
getline \
getprogname \
htonll \
memmem \
ntohll \
setenv \
setproctitle \
strcasestr \
@ -216,7 +219,7 @@ AC_SEARCH_LIBS(clock_gettime, rt)
# musl does not set optarg to NULL for flags without arguments (although it is
# not required to, but it is helpful) 3) there are probably other weird
# implementations.
AC_LIBOBJ(getopt)
AC_LIBOBJ(getopt_long)
# Look for libevent. Try libevent_core or libevent with pkg-config first then
# look for the library.
@ -267,6 +270,12 @@ if test "x$found_libevent" = xno; then
AC_MSG_ERROR("libevent not found")
fi
# Look for yacc.
AC_CHECK_PROG(found_yacc, $YACC, yes, no)
if test "x$found_yacc" = xno; then
AC_MSG_ERROR("yacc not found")
fi
# Look for ncurses or curses. Try pkg-config first then directly for the
# library.
PKG_CHECK_MODULES(
@ -309,7 +318,7 @@ fi
if test "x$found_ncurses" = xno; then
AC_SEARCH_LIBS(
setupterm,
[tinfo ncurses ncursesw],
[tinfo terminfo ncurses ncursesw],
found_ncurses=yes,
found_ncurses=no
)
@ -344,6 +353,10 @@ else
AC_MSG_ERROR("curses not found")
fi
fi
AC_CHECK_FUNCS([ \
tiparm \
tiparm_s \
])
# Look for utempter.
AC_ARG_ENABLE(
@ -373,6 +386,15 @@ AC_ARG_ENABLE(
AS_HELP_STRING(--enable-utf8proc, use utf8proc if it is installed)
)
if test "x$enable_utf8proc" = xyes; then
PKG_CHECK_MODULES(
LIBUTF8PROC,
libutf8proc,
[
AM_CPPFLAGS="$LIBUTF8PROC_CFLAGS $AM_CPPFLAGS"
CPPFLAGS="$LIBUTF8PROC_CFLAGS $SAVED_CPPFLAGS"
LIBS="$LIBUTF8PROC_LIBS $LIBS"
]
)
AC_CHECK_HEADER(utf8proc.h, enable_utf8proc=yes, enable_utf8proc=no)
if test "x$enable_utf8proc" = xyes; then
AC_SEARCH_LIBS(
@ -414,6 +436,31 @@ if test x"$enable_systemd" = xyes; then
fi
fi
AM_CONDITIONAL(HAVE_SYSTEMD, [test "x$found_systemd" = xyes])
AC_ARG_ENABLE(
cgroups,
AS_HELP_STRING(--disable-cgroups, disable adding panes to new cgroups with systemd)
)
if test "x$enable_cgroups" = x; then
# Default to the same as $enable_systemd.
enable_cgroups=$enable_systemd
fi
if test "x$enable_cgroups" = xyes; then
if test "x$found_systemd" = xyes; then
AC_DEFINE(ENABLE_CGROUPS)
else
AC_MSG_ERROR("cgroups requires systemd to be enabled")
fi
fi
# Enable sixel support.
AC_ARG_ENABLE(
sixel,
AS_HELP_STRING(--enable-sixel, enable sixel images)
)
if test "x$enable_sixel" = xyes; then
AC_DEFINE(ENABLE_SIXEL)
fi
AM_CONDITIONAL(ENABLE_SIXEL, [test "x$enable_sixel" = xyes])
# Check for b64_ntop. If we have b64_ntop, we assume b64_pton as well.
AC_MSG_CHECKING(for b64_ntop)
@ -502,6 +549,24 @@ if test "x$found_malloc_trim" = xyes; then
AC_DEFINE(HAVE_MALLOC_TRIM)
fi
# Build against jemalloc if requested.
AC_ARG_ENABLE(
jemalloc,
AS_HELP_STRING(--enable-jemalloc, use jemalloc if it is installed)
)
if test "x$enable_jemalloc" = xyes; then
PKG_CHECK_MODULES(
JEMALLOC,
jemalloc,
[
AM_CPPFLAGS="$JEMALLOC_CFLAGS $AM_CPPFLAGS"
CPPFLAGS="$AM_CPPFLAGS $SAVED_CPPFLAGS"
LIBS="$LIBS $JEMALLOC_LIBS"
],
AC_MSG_ERROR("jemalloc not found")
)
fi
# Check for CMSG_DATA. On some platforms like HP-UX this requires UNIX 95
# (_XOPEN_SOURCE and _XOPEN_SOURCE_EXTENDED) (see xopen_networking(7)). On
# others, UNIX 03 (_XOPEN_SOURCE 600, see standards(7) on Solaris).
@ -573,9 +638,9 @@ else
AC_LIBOBJ(err)
fi
# Look for imsg_init in libutil.
AC_SEARCH_LIBS(imsg_init, util, found_imsg_init=yes, found_imsg_init=no)
if test "x$found_imsg_init" = xyes; then
# Look for imsg_add in libutil.
AC_SEARCH_LIBS(imsg_add, util, found_imsg_add=yes, found_imsg_add=no)
if test "x$found_imsg_add" = xyes; then
AC_DEFINE(HAVE_IMSG)
else
AC_LIBOBJ(imsg)
@ -880,8 +945,13 @@ case "$host_os" in
MANFORMAT=mdoc
;;
*)
if test `uname -o 2>/dev/null` = illumos; then
# Illumos uses mandoc.
MANFORMAT=mdoc
else
# Solaris 2.0 to 11.3 use AT&T nroff.
MANFORMAT=man
fi
;;
esac
;;
@ -912,9 +982,23 @@ AM_CONDITIONAL(IS_NETBSD, test "x$PLATFORM" = xnetbsd)
AM_CONDITIONAL(IS_OPENBSD, test "x$PLATFORM" = xopenbsd)
AM_CONDITIONAL(IS_SUNOS, test "x$PLATFORM" = xsunos)
AM_CONDITIONAL(IS_HPUX, test "x$PLATFORM" = xhpux)
AM_CONDITIONAL(IS_CYGWIN, test "x$PLATFORM" = xcygwin)
AM_CONDITIONAL(IS_HAIKU, test "x$PLATFORM" = xhaiku)
AM_CONDITIONAL(IS_UNKNOWN, test "x$PLATFORM" = xunknown)
# Set the default lock command
DEFAULT_LOCK_CMD="lock -np"
AC_MSG_CHECKING(lock-command)
if test "x$PLATFORM" = xlinux; then
AC_CHECK_PROG(found_vlock, vlock, yes, no)
if test "x$found_vlock" = xyes; then
DEFAULT_LOCK_CMD="vlock"
fi
fi
AC_MSG_RESULT($DEFAULT_LOCK_CMD)
AC_SUBST(DEFAULT_LOCK_CMD)
# Save our CFLAGS/CPPFLAGS/LDFLAGS for the Makefile and restore the old user
# variables.
AC_SUBST(AM_CPPFLAGS)

View File

@ -234,3 +234,29 @@ control_notify_session_window_changed(struct session *s)
s->curw->window->id);
}
}
void
control_notify_paste_buffer_changed(const char *name)
{
struct client *c;
TAILQ_FOREACH(c, &clients, entry) {
if (!CONTROL_SHOULD_NOTIFY_CLIENT(c))
continue;
control_write(c, "%%paste-buffer-changed %s", name);
}
}
void
control_notify_paste_buffer_deleted(const char *name)
{
struct client *c;
TAILQ_FOREACH(c, &clients, entry) {
if (!CONTROL_SHOULD_NOTIFY_CLIENT(c))
continue;
control_write(c, "%%paste-buffer-deleted %s", name);
}
}

View File

@ -775,13 +775,16 @@ control_start(struct client *c)
cs->read_event = bufferevent_new(c->fd, control_read_callback,
control_write_callback, control_error_callback, c);
bufferevent_enable(cs->read_event, EV_READ);
if (cs->read_event == NULL)
fatalx("out of memory");
if (c->flags & CLIENT_CONTROLCONTROL)
cs->write_event = cs->read_event;
else {
cs->write_event = bufferevent_new(c->out_fd, NULL,
control_write_callback, control_error_callback, c);
if (cs->write_event == NULL)
fatalx("out of memory");
}
bufferevent_setwatermark(cs->write_event, EV_WRITE, CONTROL_BUFFER_LOW,
0);
@ -792,6 +795,13 @@ control_start(struct client *c)
}
}
/* Control client ready. */
void
control_ready(struct client *c)
{
bufferevent_enable(c->control_state->read_event, EV_READ);
}
/* Discard all output for a client. */
void
control_discard(struct client *c)

View File

@ -182,9 +182,11 @@ void
environ_update(struct options *oo, struct environ *src, struct environ *dst)
{
struct environ_entry *envent;
struct environ_entry *envent1;
struct options_entry *o;
struct options_array_item *a;
union options_value *ov;
int found;
o = options_get(oo, "update-environment");
if (o == NULL)
@ -192,14 +194,15 @@ environ_update(struct options *oo, struct environ *src, struct environ *dst)
a = options_array_first(o);
while (a != NULL) {
ov = options_array_item_value(a);
RB_FOREACH(envent, environ, src) {
if (fnmatch(ov->string, envent->name, 0) == 0)
break;
}
if (envent == NULL)
environ_clear(dst, ov->string);
else
found = 0;
RB_FOREACH_SAFE(envent, environ, src, envent1) {
if (fnmatch(ov->string, envent->name, 0) == 0) {
environ_set(dst, envent->name, 0, "%s", envent->value);
found = 1;
}
}
if (!found)
environ_clear(dst, ov->string);
a = options_array_next(a);
}
}
@ -261,6 +264,12 @@ environ_for_session(struct session *s, int no_TERM)
environ_set(env, "TERM_PROGRAM_VERSION", 0, "%s", getversion());
}
#ifdef HAVE_SYSTEMD
environ_clear(env, "LISTEN_PID");
environ_clear(env, "LISTEN_FDS");
environ_clear(env, "LISTEN_FDNAMES");
#endif
if (s != NULL)
idx = s->id;
else

View File

@ -14,7 +14,7 @@ set -g status-bg red
%endif
# Enable RGB colour if running in xterm(1)
set-option -sa terminal-overrides ",xterm*:Tc"
set-option -sa terminal-features ",xterm*:RGB"
# Change the default $TERM to tmux-256color
set -g default-terminal "tmux-256color"

54
file.c
View File

@ -149,7 +149,8 @@ file_fire_done_cb(__unused int fd, __unused short events, void *arg)
struct client_file *cf = arg;
struct client *c = cf->c;
if (cf->cb != NULL && (c == NULL || (~c->flags & CLIENT_DEAD)))
if (cf->cb != NULL &&
(cf->closed || c == NULL || (~c->flags & CLIENT_DEAD)))
cf->cb(c, cf->path, cf->error, 1, cf->buffer, cf->data);
file_free(cf);
}
@ -173,9 +174,9 @@ file_fire_read(struct client_file *cf)
int
file_can_print(struct client *c)
{
if (c == NULL)
return (0);
if (c->session != NULL && (~c->flags & CLIENT_CONTROL))
if (c == NULL ||
(c->flags & CLIENT_ATTACHED) ||
(c->flags & CLIENT_CONTROL))
return (0);
return (1);
}
@ -352,7 +353,7 @@ done:
}
/* Read a file. */
void
struct client_file *
file_read(struct client *c, const char *path, client_file_cb cb, void *cbdata)
{
struct client_file *cf;
@ -420,10 +421,27 @@ skip:
goto done;
}
free(msg);
return;
return cf;
done:
file_fire_done(cf);
return NULL;
}
/* Cancel a file read. */
void
file_cancel(struct client_file *cf)
{
struct msg_read_cancel msg;
log_debug("read cancel file %d", cf->stream);
if (cf->closed)
return;
cf->closed = 1;
msg.stream = cf->stream;
proc_send(cf->peer, MSG_READ_CANCEL, -1, &msg, sizeof msg);
}
/* Push event, fired if there is more writing to be done. */
@ -585,6 +603,8 @@ file_write_open(struct client_files *files, struct tmuxpeer *peer,
cf->event = bufferevent_new(cf->fd, NULL, file_write_callback,
file_write_error_callback, cf);
if (cf->event == NULL)
fatalx("out of memory");
bufferevent_enable(cf->event, EV_WRITE);
goto reply;
@ -744,6 +764,8 @@ file_read_open(struct client_files *files, struct tmuxpeer *peer,
cf->event = bufferevent_new(cf->fd, file_read_callback, NULL,
file_read_error_callback, cf);
if (cf->event == NULL)
fatalx("out of memory");
bufferevent_enable(cf->event, EV_READ);
return;
@ -753,6 +775,24 @@ reply:
proc_send(peer, MSG_READ_DONE, -1, &reply, sizeof reply);
}
/* Handle a read cancel message (client). */
void
file_read_cancel(struct client_files *files, struct imsg *imsg)
{
struct msg_read_cancel *msg = imsg->data;
size_t msglen = imsg->hdr.len - IMSG_HEADER_SIZE;
struct client_file find, *cf;
if (msglen != sizeof *msg)
fatalx("bad MSG_READ_CANCEL size");
find.stream = msg->stream;
if ((cf = RB_FIND(client_files, files, &find)) == NULL)
fatalx("unknown stream number");
log_debug("cancel file %d", cf->stream);
file_read_error_callback(NULL, 0, cf);
}
/* Handle a write ready message (server). */
void
file_write_ready(struct client_files *files, struct imsg *imsg)
@ -790,7 +830,7 @@ file_read_data(struct client_files *files, struct imsg *imsg)
return;
log_debug("file %d read %zu bytes", cf->stream, bsize);
if (cf->error == 0) {
if (cf->error == 0 && !cf->closed) {
if (evbuffer_add(cf->buffer, bdata, bsize) != 0) {
cf->error = ENOMEM;
file_fire_done(cf);

View File

@ -33,6 +33,7 @@ struct format_range {
enum style_range_type type;
u_int argument;
char string[16];
TAILQ_ENTRY(format_range) entry;
};
@ -44,9 +45,18 @@ format_is_type(struct format_range *fr, struct style *sy)
{
if (fr->type != sy->range_type)
return (0);
if (fr->type == STYLE_RANGE_WINDOW &&
fr->argument != sy->range_argument)
return (0);
switch (fr->type) {
case STYLE_RANGE_NONE:
case STYLE_RANGE_LEFT:
case STYLE_RANGE_RIGHT:
return (1);
case STYLE_RANGE_PANE:
case STYLE_RANGE_WINDOW:
case STYLE_RANGE_SESSION:
return (fr->argument == sy->range_argument);
case STYLE_RANGE_USER:
return (strcmp(fr->string, sy->range_string) == 0);
}
return (1);
}
@ -709,7 +719,7 @@ format_draw(struct screen_write_ctx *octx, const struct grid_cell *base,
int focus_start = -1, focus_end = -1;
int list_state = -1, fill = -1, even;
enum style_align list_align = STYLE_ALIGN_DEFAULT;
struct grid_cell gc, current_default;
struct grid_cell gc, current_default, base_default;
struct style sy, saved_sy;
struct utf8_data *ud = &sy.gc.data;
const char *cp, *end;
@ -719,7 +729,9 @@ format_draw(struct screen_write_ctx *octx, const struct grid_cell *base,
struct format_ranges frs;
struct style_range *sr;
memcpy(&base_default, base, sizeof base_default);
memcpy(&current_default, base, sizeof current_default);
base = &base_default;
style_set(&sy, &current_default);
TAILQ_INIT(&frs);
log_debug("%s: %s", __func__, expanded);
@ -837,6 +849,12 @@ format_draw(struct screen_write_ctx *octx, const struct grid_cell *base,
} else if (sy.default_type == STYLE_DEFAULT_POP) {
memcpy(&current_default, base, sizeof current_default);
sy.default_type = STYLE_DEFAULT_BASE;
} else if (sy.default_type == STYLE_DEFAULT_SET) {
memcpy(&base_default, &saved_sy.gc,
sizeof base_default);
memcpy(&current_default, &saved_sy.gc,
sizeof current_default);
sy.default_type = STYLE_DEFAULT_BASE;
}
/* Check the list state. */
@ -942,6 +960,8 @@ format_draw(struct screen_write_ctx *octx, const struct grid_cell *base,
fr->type = sy.range_type;
fr->argument = sy.range_argument;
strlcpy(fr->string, sy.range_string,
sizeof fr->string);
}
}
@ -1013,13 +1033,39 @@ format_draw(struct screen_write_ctx *octx, const struct grid_cell *base,
sr = xcalloc(1, sizeof *sr);
sr->type = fr->type;
sr->argument = fr->argument;
strlcpy(sr->string, fr->string, sizeof sr->string);
sr->start = fr->start;
sr->end = fr->end;
TAILQ_INSERT_TAIL(srs, sr, entry);
log_debug("%s: range %d|%u at %u-%u", __func__, sr->type,
switch (sr->type) {
case STYLE_RANGE_NONE:
break;
case STYLE_RANGE_LEFT:
log_debug("%s: range left at %u-%u", __func__,
sr->start, sr->end);
break;
case STYLE_RANGE_RIGHT:
log_debug("%s: range right at %u-%u", __func__,
sr->start, sr->end);
break;
case STYLE_RANGE_PANE:
log_debug("%s: range pane|%%%u at %u-%u", __func__,
sr->argument, sr->start, sr->end);
break;
case STYLE_RANGE_WINDOW:
log_debug("%s: range window|%u at %u-%u", __func__,
sr->argument, sr->start, sr->end);
break;
case STYLE_RANGE_SESSION:
log_debug("%s: range session|$%u at %u-%u", __func__,
sr->argument, sr->start, sr->end);
break;
case STYLE_RANGE_USER:
log_debug("%s: range user|%u at %u-%u", __func__,
sr->argument, sr->start, sr->end);
break;
}
format_free_range(&frs, fr);
}
@ -1083,7 +1129,7 @@ format_trim_left(const char *expanded, u_int limit)
struct utf8_data ud;
enum utf8_state more;
out = copy = xcalloc(1, strlen(expanded) + 1);
out = copy = xcalloc(2, strlen(expanded) + 1);
while (*cp != '\0') {
if (width >= limit)
break;
@ -1150,7 +1196,7 @@ format_trim_right(const char *expanded, u_int limit)
return (xstrdup(expanded));
skip = total_width - limit;
out = copy = xcalloc(1, strlen(expanded) + 1);
out = copy = xcalloc(2, strlen(expanded) + 1);
while (*cp != '\0') {
if (*cp == '#') {
end = format_leading_hashes(cp, &n, &leading_width);

743
format.c

File diff suppressed because it is too large Load Diff

View File

@ -35,7 +35,7 @@ LLVMFuzzerTestOneInput(const u_char *data, size_t size)
int error;
/*
* Since AFL doesn't support -max_len paramenter we have to
* Since AFL doesn't support -max_len parameter we have to
* discard long inputs manually.
*/
if (size > FUZZER_MAXLEN)

View File

@ -180,19 +180,14 @@ grid_reader_handle_wrap(struct grid_reader *gr, u_int *xx, u_int *yy)
int
grid_reader_in_set(struct grid_reader *gr, const char *set)
{
struct grid_cell gc;
grid_get_cell(gr->gd, gr->cx, gr->cy, &gc);
if (gc.flags & GRID_FLAG_PADDING)
return (0);
return (utf8_cstrhas(set, &gc.data));
return (grid_in_set(gr->gd, gr->cx, gr->cy, set));
}
/* Move cursor to the start of the next word. */
void
grid_reader_cursor_next_word(struct grid_reader *gr, const char *separators)
{
u_int xx, yy;
u_int xx, yy, width;
/* Do not break up wrapped words. */
if (grid_get_line(gr->gd, gr->cy)->flags & GRID_LINE_WRAPPED)
@ -229,8 +224,8 @@ grid_reader_cursor_next_word(struct grid_reader *gr, const char *separators)
}
}
while (grid_reader_handle_wrap(gr, &xx, &yy) &&
grid_reader_in_set(gr, WHITESPACE))
gr->cx++;
(width = grid_reader_in_set(gr, WHITESPACE)))
gr->cx += width;
}
/* Move cursor to the end of the next word. */
@ -338,6 +333,20 @@ grid_reader_cursor_previous_word(struct grid_reader *gr, const char *separators,
gr->cy = oldy;
}
/* Compare grid cell to UTF-8 data. Return 1 if equal, 0 if not. */
static int
grid_reader_cell_equals_data(const struct grid_cell *gc,
const struct utf8_data *ud)
{
if (gc->flags & GRID_FLAG_PADDING)
return (0);
if (gc->flags & GRID_FLAG_TAB && ud->size == 1 && *ud->data == '\t')
return (1);
if (gc->data.size != ud->size)
return (0);
return (memcmp(gc->data.data, ud->data, gc->data.size) == 0);
}
/* Jump forward to character. */
int
grid_reader_cursor_jump(struct grid_reader *gr, const struct utf8_data *jc)
@ -352,9 +361,7 @@ grid_reader_cursor_jump(struct grid_reader *gr, const struct utf8_data *jc)
xx = grid_line_length(gr->gd, py);
while (px < xx) {
grid_get_cell(gr->gd, px, py, &gc);
if (!(gc.flags & GRID_FLAG_PADDING) &&
gc.data.size == jc->size &&
memcmp(gc.data.data, jc->data, gc.data.size) == 0) {
if (grid_reader_cell_equals_data(&gc, jc)) {
gr->cx = px;
gr->cy = py;
return (1);
@ -382,9 +389,7 @@ grid_reader_cursor_jump_back(struct grid_reader *gr, const struct utf8_data *jc)
for (py = gr->cy + 1; py > 0; py--) {
for (px = xx; px > 0; px--) {
grid_get_cell(gr->gd, px - 1, py - 1, &gc);
if (!(gc.flags & GRID_FLAG_PADDING) &&
gc.data.size == jc->size &&
memcmp(gc.data.data, jc->data, gc.data.size) == 0) {
if (grid_reader_cell_equals_data(&gc, jc)) {
gr->cx = px - 1;
gr->cy = py - 1;
return (1);
@ -415,7 +420,9 @@ grid_reader_cursor_back_to_indentation(struct grid_reader *gr)
xx = grid_line_length(gr->gd, py);
for (px = 0; px < xx; px++) {
grid_get_cell(gr->gd, px, py, &gc);
if (gc.data.size != 1 || *gc.data.data != ' ') {
if ((gc.data.size != 1 || *gc.data.data != ' ') &&
~gc.flags & GRID_FLAG_TAB &&
~gc.flags & GRID_FLAG_PADDING) {
gr->cx = px;
gr->cy = py;
return;

View File

@ -231,5 +231,5 @@ grid_view_string_cells(struct grid *gd, u_int px, u_int py, u_int nx)
px = grid_view_x(gd, px);
py = grid_view_y(gd, py);
return (grid_string_cells(gd, px, py, nx, NULL, 0, 0, 0));
return (grid_string_cells(gd, px, py, nx, NULL, 0, NULL));
}

188
grid.c
View File

@ -37,7 +37,7 @@
/* Default grid cell data. */
const struct grid_cell grid_default_cell = {
{ { ' ' }, 0, 1, 1 }, 0, 0, 8, 8, 0
{ { ' ' }, 0, 1, 1 }, 0, 0, 8, 8, 8, 0
};
/*
@ -45,15 +45,15 @@ const struct grid_cell grid_default_cell = {
* appears in the grid - because of this, they are always extended cells.
*/
static const struct grid_cell grid_padding_cell = {
{ { '!' }, 0, 0, 0 }, 0, GRID_FLAG_PADDING, 8, 8, 0
{ { '!' }, 0, 0, 0 }, 0, GRID_FLAG_PADDING, 8, 8, 8, 0
};
/* Cleared grid cell data. */
static const struct grid_cell grid_cleared_cell = {
{ { ' ' }, 0, 1, 1 }, 0, GRID_FLAG_CLEARED, 8, 8, 0
{ { ' ' }, 0, 1, 1 }, 0, GRID_FLAG_CLEARED, 8, 8, 8, 0
};
static const struct grid_cell_entry grid_cleared_entry = {
GRID_FLAG_CLEARED, { .data = { 0, 8, 8, ' ' } }
{ .data = { 0, 8, 8, ' ' } }, GRID_FLAG_CLEARED
};
/* Store cell in entry. */
@ -84,11 +84,15 @@ grid_need_extended_cell(const struct grid_cell_entry *gce,
return (1);
if (gc->attr > 0xff)
return (1);
if (gc->data.size != 1 || gc->data.width != 1)
if (gc->data.size > 1 || gc->data.width > 1)
return (1);
if ((gc->fg & COLOUR_FLAG_RGB) || (gc->bg & COLOUR_FLAG_RGB))
return (1);
if (gc->us != 0) /* only supports 256 or RGB */
if (gc->us != 8) /* only supports 256 or RGB */
return (1);
if (gc->link != 0)
return (1);
if (gc->flags & GRID_FLAG_TAB)
return (1);
return (0);
}
@ -122,6 +126,9 @@ grid_extended_cell(struct grid_line *gl, struct grid_cell_entry *gce,
fatalx("offset too big");
gl->flags |= GRID_LINE_EXTENDED;
if (gc->flags & GRID_FLAG_TAB)
uc = gc->data.width;
else
utf8_from_data(&gc->data, &uc);
gee = &gl->extddata[gce->offset];
@ -131,6 +138,7 @@ grid_extended_cell(struct grid_line *gl, struct grid_cell_entry *gce,
gee->fg = gc->fg;
gee->bg = gc->bg;
gee->us = gc->us;
gee->link = gc->link;
return (gee);
}
@ -227,9 +235,15 @@ 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;;
if (gc1->fg != gc2->fg || gc1->bg != gc2->bg)
return (0);
if (gc1->attr != gc2->attr || gc1->flags != gc2->flags)
if (gc1->attr != gc2->attr)
return (0);
if ((flags1 & ~GRID_FLAG_CLEARED) != (flags2 & ~GRID_FLAG_CLEARED))
return (0);
if (gc1->link != gc2->link)
return (0);
return (1);
}
@ -247,6 +261,16 @@ grid_cells_equal(const struct grid_cell *gc1, const struct grid_cell *gc2)
return (memcmp(gc1->data.data, gc2->data.data, gc1->data.size) == 0);
}
/* Set grid cell to a tab. */
void
grid_set_tab(struct grid_cell *gc, u_int width)
{
memset(gc->data.data, 0, sizeof gc->data.data);
gc->flags |= GRID_FLAG_TAB;
gc->data.width = gc->data.size = gc->data.have = width;
memset(gc->data.data, ' ', gc->data.size);
}
/* Free one line. */
static void
grid_free_line(struct grid *gd, u_int py)
@ -399,6 +423,7 @@ grid_scroll_history(struct grid *gd, u_int bg)
gd->hscrolled++;
grid_compact_line(&gd->linedata[gd->hsize]);
gd->linedata[gd->hsize].time = current_time;
gd->hsize++;
}
@ -438,6 +463,7 @@ grid_scroll_history_region(struct grid *gd, u_int upper, u_int lower, u_int bg)
/* Move the line into the history. */
memcpy(gl_history, gl_upper, sizeof *gl_history);
gl_history->time = current_time;
/* Then move the region up and clear the bottom line. */
memmove(gl_upper, gl_upper + 1, (lower - upper) * sizeof *gl_upper);
@ -507,6 +533,11 @@ grid_get_cell1(struct grid_line *gl, u_int px, struct grid_cell *gc)
gc->fg = gee->fg;
gc->bg = gee->bg;
gc->us = gee->us;
gc->link = gee->link;
if (gc->flags & GRID_FLAG_TAB)
grid_set_tab(gc, gee->data);
else
utf8_to_data(gee->data, &gc->data);
}
return;
@ -520,8 +551,9 @@ grid_get_cell1(struct grid_line *gl, u_int px, struct grid_cell *gc)
gc->bg = gce->data.bg;
if (gce->flags & GRID_FLAG_BG256)
gc->bg |= COLOUR_FLAG_256;
gc->us = 0;
gc->us = 8;
utf8_set(&gc->data, gce->data.data);
gc->link = 0;
}
/* Get cell for reading. */
@ -852,16 +884,22 @@ grid_string_cells_us(const struct grid_cell *gc, int *values)
/* Add on SGR code. */
static void
grid_string_cells_add_code(char *buf, size_t len, u_int n, int *s, int *newc,
int *oldc, size_t nnewc, size_t noldc, int escape_c0)
int *oldc, size_t nnewc, size_t noldc, int flags)
{
u_int i;
char tmp[64];
int reset = (n != 0 && s[0] == 0);
if (nnewc != 0 &&
(nnewc != noldc ||
memcmp(newc, oldc, nnewc * sizeof newc[0]) != 0 ||
(n != 0 && s[0] == 0))) {
if (escape_c0)
if (nnewc == 0)
return; /* no code to add */
if (!reset &&
nnewc == noldc &&
memcmp(newc, oldc, nnewc * sizeof newc[0]) == 0)
return; /* no reset and colour unchanged */
if (reset && (newc[0] == 49 || newc[0] == 39))
return; /* reset and colour default */
if (flags & GRID_STRING_ESCAPE_SEQUENCES)
strlcat(buf, "\\033[", len);
else
strlcat(buf, "\033[", len);
@ -874,6 +912,32 @@ grid_string_cells_add_code(char *buf, size_t len, u_int n, int *s, int *newc,
}
strlcat(buf, "m", len);
}
static int
grid_string_cells_add_hyperlink(char *buf, size_t len, const char *id,
const char *uri, int flags)
{
char *tmp;
if (strlen(uri) + strlen(id) + 17 >= len)
return (0);
if (flags & GRID_STRING_ESCAPE_SEQUENCES)
strlcat(buf, "\\033]8;", len);
else
strlcat(buf, "\033]8;", len);
if (*id != '\0') {
xasprintf(&tmp, "id=%s;", id);
strlcat(buf, tmp, len);
free(tmp);
} else
strlcat(buf, ";", len);
strlcat(buf, uri, len);
if (flags & GRID_STRING_ESCAPE_SEQUENCES)
strlcat(buf, "\\033\\\\", len);
else
strlcat(buf, "\033\\", len);
return (1);
}
/*
@ -882,14 +946,16 @@ grid_string_cells_add_code(char *buf, size_t len, u_int n, int *s, int *newc,
*/
static void
grid_string_cells_code(const struct grid_cell *lastgc,
const struct grid_cell *gc, char *buf, size_t len, int escape_c0)
const struct grid_cell *gc, char *buf, size_t len, int flags,
struct screen *sc, int *has_link)
{
int oldc[64], newc[64], s[128];
size_t noldc, nnewc, n, i;
u_int attr = gc->attr, lastattr = lastgc->attr;
char tmp[64];
const char *uri, *id;
struct {
static const struct {
u_int mask;
u_int code;
} attrs[] = {
@ -913,7 +979,7 @@ grid_string_cells_code(const struct grid_cell *lastgc,
for (i = 0; i < nitems(attrs); i++) {
if (((~attr & attrs[i].mask) &&
(lastattr & attrs[i].mask)) ||
(lastgc->us != 0 && gc->us == 0)) {
(lastgc->us != 8 && gc->us == 8)) {
s[n++] = 0;
lastattr &= GRID_ATTR_CHARSET;
break;
@ -928,7 +994,7 @@ grid_string_cells_code(const struct grid_cell *lastgc,
/* Write the attributes. */
*buf = '\0';
if (n > 0) {
if (escape_c0)
if (flags & GRID_STRING_ESCAPE_SEQUENCES)
strlcat(buf, "\\033[", len);
else
strlcat(buf, "\033[", len);
@ -950,46 +1016,59 @@ grid_string_cells_code(const struct grid_cell *lastgc,
nnewc = grid_string_cells_fg(gc, newc);
noldc = grid_string_cells_fg(lastgc, oldc);
grid_string_cells_add_code(buf, len, n, s, newc, oldc, nnewc, noldc,
escape_c0);
flags);
/* If the background colour changed, append its parameters. */
nnewc = grid_string_cells_bg(gc, newc);
noldc = grid_string_cells_bg(lastgc, oldc);
grid_string_cells_add_code(buf, len, n, s, newc, oldc, nnewc, noldc,
escape_c0);
flags);
/* If the underscore colour changed, append its parameters. */
nnewc = grid_string_cells_us(gc, newc);
noldc = grid_string_cells_us(lastgc, oldc);
grid_string_cells_add_code(buf, len, n, s, newc, oldc, nnewc, noldc,
escape_c0);
flags);
/* Append shift in/shift out if needed. */
if ((attr & GRID_ATTR_CHARSET) && !(lastattr & GRID_ATTR_CHARSET)) {
if (escape_c0)
if (flags & GRID_STRING_ESCAPE_SEQUENCES)
strlcat(buf, "\\016", len); /* SO */
else
strlcat(buf, "\016", len); /* SO */
}
if (!(attr & GRID_ATTR_CHARSET) && (lastattr & GRID_ATTR_CHARSET)) {
if (escape_c0)
if (flags & GRID_STRING_ESCAPE_SEQUENCES)
strlcat(buf, "\\017", len); /* SI */
else
strlcat(buf, "\017", len); /* SI */
}
/* Add hyperlink if changed. */
if (sc != NULL && sc->hyperlinks != NULL && lastgc->link != gc->link) {
if (hyperlinks_get(sc->hyperlinks, gc->link, &uri, &id, NULL)) {
*has_link = grid_string_cells_add_hyperlink(buf, len,
id, uri, flags);
} else if (*has_link) {
grid_string_cells_add_hyperlink(buf, len, "", "",
flags);
*has_link = 0;
}
}
}
/* Convert cells into a string. */
char *
grid_string_cells(struct grid *gd, u_int px, u_int py, u_int nx,
struct grid_cell **lastgc, int with_codes, int escape_c0, int trim)
struct grid_cell **lastgc, int flags, struct screen *s)
{
struct grid_cell gc;
static struct grid_cell lastgc1;
const char *data;
char *buf, code[128];
char *buf, code[8192];
size_t len, off, size, codelen;
u_int xx;
u_int xx, end;
int has_link = 0;
const struct grid_line *gl;
if (lastgc != NULL && *lastgc == NULL) {
@ -1002,27 +1081,38 @@ grid_string_cells(struct grid *gd, u_int px, u_int py, u_int nx,
off = 0;
gl = grid_peek_line(gd, py);
if (flags & GRID_STRING_EMPTY_CELLS)
end = gl->cellsize;
else
end = gl->cellused;
for (xx = px; xx < px + nx; xx++) {
if (gl == NULL || xx >= gl->cellused)
if (gl == NULL || xx >= end)
break;
grid_get_cell(gd, xx, py, &gc);
if (gc.flags & GRID_FLAG_PADDING)
continue;
if (with_codes) {
if (flags & GRID_STRING_WITH_SEQUENCES) {
grid_string_cells_code(*lastgc, &gc, code, sizeof code,
escape_c0);
flags, s, &has_link);
codelen = strlen(code);
memcpy(*lastgc, &gc, sizeof **lastgc);
} else
codelen = 0;
if (gc.flags & GRID_FLAG_TAB) {
data = "\t";
size = 1;
} else {
data = gc.data.data;
size = gc.data.size;
if (escape_c0 && size == 1 && *data == '\\') {
if ((flags & GRID_STRING_ESCAPE_SEQUENCES) &&
size == 1 &&
*data == '\\') {
data = "\\\\";
size = 2;
}
}
while (len < off + size + codelen + 1) {
buf = xreallocarray(buf, 2, len);
@ -1037,7 +1127,19 @@ grid_string_cells(struct grid *gd, u_int px, u_int py, u_int nx,
off += size;
}
if (trim) {
if (has_link) {
grid_string_cells_add_hyperlink(code, sizeof code, "", "",
flags);
codelen = strlen(code);
while (len < off + size + codelen + 1) {
buf = xreallocarray(buf, 2, len);
len *= 2;
}
memcpy(buf + off, code, codelen);
off += codelen;
}
if (flags & GRID_STRING_TRIM_SPACES) {
while (off > 0 && buf[off - 1] == ' ')
off--;
}
@ -1459,3 +1561,27 @@ grid_line_length(struct grid *gd, u_int py)
}
return (px);
}
/* Check if character is in set. */
int
grid_in_set(struct grid *gd, u_int px, u_int py, const char *set)
{
struct grid_cell gc, tmp_gc;
u_int pxx;
grid_get_cell(gd, px, py, &gc);
if (strchr(set, '\t')) {
if (gc.flags & GRID_FLAG_PADDING) {
pxx = px;
do
grid_get_cell(gd, --pxx, py, &tmp_gc);
while (pxx > 0 && tmp_gc.flags & GRID_FLAG_PADDING);
if (tmp_gc.flags & GRID_FLAG_TAB)
return (tmp_gc.data.width - (px - pxx));
} else if (gc.flags & GRID_FLAG_TAB)
return (gc.data.width);
}
if (gc.flags & GRID_FLAG_PADDING)
return (0);
return (utf8_cstrhas(set, &gc.data));
}

239
hyperlinks.c Normal file
View File

@ -0,0 +1,239 @@
/* $OpenBSD$ */
/*
* Copyright (c) 2021 Will <author@will.party>
* Copyright (c) 2022 Jeff Chiang <pobomp@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"
/*
* OSC 8 hyperlinks, described at:
*
* https://gist.github.com/egmontkob/eb114294efbcd5adb1944c9f3cb5feda
*
* Each hyperlink and ID combination is assigned a number ("inner" in this
* file) which is stored in an extended grid cell and maps into a tree here.
*
* Each URI has one inner number and one external ID (which tmux uses to send
* the hyperlink to the terminal) and one internal ID (which is received from
* the sending application inside tmux).
*
* Anonymous hyperlinks are each unique and are not reused even if they have
* the same URI (terminals will not want to tie them together).
*/
#define MAX_HYPERLINKS 5000
static long long hyperlinks_next_external_id = 1;
static u_int global_hyperlinks_count;
struct hyperlinks_uri {
struct hyperlinks *tree;
u_int inner;
const char *internal_id;
const char *external_id;
const char *uri;
TAILQ_ENTRY(hyperlinks_uri) list_entry;
RB_ENTRY(hyperlinks_uri) by_inner_entry;
RB_ENTRY(hyperlinks_uri) by_uri_entry; /* by internal ID and URI */
};
RB_HEAD(hyperlinks_by_uri_tree, hyperlinks_uri);
RB_HEAD(hyperlinks_by_inner_tree, hyperlinks_uri);
TAILQ_HEAD(hyperlinks_list, hyperlinks_uri);
static struct hyperlinks_list global_hyperlinks =
TAILQ_HEAD_INITIALIZER(global_hyperlinks);
struct hyperlinks {
u_int next_inner;
struct hyperlinks_by_inner_tree by_inner;
struct hyperlinks_by_uri_tree by_uri;
u_int references;
};
static int
hyperlinks_by_uri_cmp(struct hyperlinks_uri *left, struct hyperlinks_uri *right)
{
int r;
if (*left->internal_id == '\0' || *right->internal_id == '\0') {
/*
* If both URIs are anonymous, use the inner for comparison so
* that they do not match even if the URI is the same - each
* anonymous URI should be unique.
*/
if (*left->internal_id != '\0')
return (-1);
if (*right->internal_id != '\0')
return (1);
return (left->inner - right->inner);
}
r = strcmp(left->internal_id, right->internal_id);
if (r != 0)
return (r);
return (strcmp(left->uri, right->uri));
}
RB_PROTOTYPE_STATIC(hyperlinks_by_uri_tree, hyperlinks_uri, by_uri_entry,
hyperlinks_by_uri_cmp);
RB_GENERATE_STATIC(hyperlinks_by_uri_tree, hyperlinks_uri, by_uri_entry,
hyperlinks_by_uri_cmp);
static int
hyperlinks_by_inner_cmp(struct hyperlinks_uri *left,
struct hyperlinks_uri *right)
{
return (left->inner - right->inner);
}
RB_PROTOTYPE_STATIC(hyperlinks_by_inner_tree, hyperlinks_uri, by_inner_entry,
hyperlinks_by_inner_cmp);
RB_GENERATE_STATIC(hyperlinks_by_inner_tree, hyperlinks_uri, by_inner_entry,
hyperlinks_by_inner_cmp);
/* Remove a hyperlink. */
static void
hyperlinks_remove(struct hyperlinks_uri *hlu)
{
struct hyperlinks *hl = hlu->tree;
TAILQ_REMOVE(&global_hyperlinks, hlu, list_entry);
global_hyperlinks_count--;
RB_REMOVE(hyperlinks_by_inner_tree, &hl->by_inner, hlu);
RB_REMOVE(hyperlinks_by_uri_tree, &hl->by_uri, hlu);
free((void *)hlu->internal_id);
free((void *)hlu->external_id);
free((void *)hlu->uri);
free(hlu);
}
/* Store a new hyperlink or return if it already exists. */
u_int
hyperlinks_put(struct hyperlinks *hl, const char *uri_in,
const char *internal_id_in)
{
struct hyperlinks_uri find, *hlu;
char *uri, *internal_id, *external_id;
/*
* Anonymous URI are stored with an empty internal ID and the tree
* comparator will make sure they never match each other (so each
* anonymous URI is unique).
*/
if (internal_id_in == NULL)
internal_id_in = "";
utf8_stravis(&uri, uri_in, VIS_OCTAL|VIS_CSTYLE);
utf8_stravis(&internal_id, internal_id_in, VIS_OCTAL|VIS_CSTYLE);
if (*internal_id_in != '\0') {
find.uri = uri;
find.internal_id = internal_id;
hlu = RB_FIND(hyperlinks_by_uri_tree, &hl->by_uri, &find);
if (hlu != NULL) {
free (uri);
free (internal_id);
return (hlu->inner);
}
}
xasprintf(&external_id, "tmux%llX", hyperlinks_next_external_id++);
hlu = xcalloc(1, sizeof *hlu);
hlu->inner = hl->next_inner++;
hlu->internal_id = internal_id;
hlu->external_id = external_id;
hlu->uri = uri;
hlu->tree = hl;
RB_INSERT(hyperlinks_by_uri_tree, &hl->by_uri, hlu);
RB_INSERT(hyperlinks_by_inner_tree, &hl->by_inner, hlu);
TAILQ_INSERT_TAIL(&global_hyperlinks, hlu, list_entry);
if (++global_hyperlinks_count == MAX_HYPERLINKS)
hyperlinks_remove(TAILQ_FIRST(&global_hyperlinks));
return (hlu->inner);
}
/* Get hyperlink by inner number. */
int
hyperlinks_get(struct hyperlinks *hl, u_int inner, const char **uri_out,
const char **internal_id_out, const char **external_id_out)
{
struct hyperlinks_uri find, *hlu;
find.inner = inner;
hlu = RB_FIND(hyperlinks_by_inner_tree, &hl->by_inner, &find);
if (hlu == NULL)
return (0);
if (internal_id_out != NULL)
*internal_id_out = hlu->internal_id;
if (external_id_out != NULL)
*external_id_out = hlu->external_id;
*uri_out = hlu->uri;
return (1);
}
/* Initialize hyperlink set. */
struct hyperlinks *
hyperlinks_init(void)
{
struct hyperlinks *hl;
hl = xcalloc(1, sizeof *hl);
hl->next_inner = 1;
RB_INIT(&hl->by_uri);
RB_INIT(&hl->by_inner);
hl->references = 1;
return (hl);
}
/* Copy hyperlink set. */
struct hyperlinks *
hyperlinks_copy(struct hyperlinks *hl)
{
hl->references++;
return (hl);
}
/* Free all hyperlinks but not the set itself. */
void
hyperlinks_reset(struct hyperlinks *hl)
{
struct hyperlinks_uri *hlu, *hlu1;
RB_FOREACH_SAFE(hlu, hyperlinks_by_inner_tree, &hl->by_inner, hlu1)
hyperlinks_remove(hlu);
}
/* Free hyperlink set. */
void
hyperlinks_free(struct hyperlinks *hl)
{
if (--hl->references == 0) {
hyperlinks_reset(hl);
free(hl);
}
}

679
image-sixel.c Normal file
View File

@ -0,0 +1,679 @@
/* $OpenBSD$ */
/*
* Copyright (c) 2019 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"
#define SIXEL_WIDTH_LIMIT 10000
#define SIXEL_HEIGHT_LIMIT 10000
struct sixel_line {
u_int x;
uint16_t *data;
};
struct sixel_image {
u_int x;
u_int y;
u_int xpixel;
u_int ypixel;
u_int set_ra;
u_int ra_x;
u_int ra_y;
u_int *colours;
u_int ncolours;
u_int p2;
u_int dx;
u_int dy;
u_int dc;
struct sixel_line *lines;
};
struct sixel_chunk {
u_int next_x;
u_int next_y;
u_int count;
char pattern;
char next_pattern;
size_t len;
size_t used;
char *data;
};
static int
sixel_parse_expand_lines(struct sixel_image *si, u_int y)
{
if (y <= si->y)
return (0);
if (y > SIXEL_HEIGHT_LIMIT)
return (1);
si->lines = xrecallocarray(si->lines, si->y, y, sizeof *si->lines);
si->y = y;
return (0);
}
static int
sixel_parse_expand_line(struct sixel_image *si, struct sixel_line *sl, u_int x)
{
if (x <= sl->x)
return (0);
if (x > SIXEL_WIDTH_LIMIT)
return (1);
if (x > si->x)
si->x = x;
sl->data = xrecallocarray(sl->data, sl->x, si->x, sizeof *sl->data);
sl->x = si->x;
return (0);
}
static u_int
sixel_get_pixel(struct sixel_image *si, u_int x, u_int y)
{
struct sixel_line *sl;
if (y >= si->y)
return (0);
sl = &si->lines[y];
if (x >= sl->x)
return (0);
return (sl->data[x]);
}
static int
sixel_set_pixel(struct sixel_image *si, u_int x, u_int y, u_int c)
{
struct sixel_line *sl;
if (sixel_parse_expand_lines(si, y + 1) != 0)
return (1);
sl = &si->lines[y];
if (sixel_parse_expand_line(si, sl, x + 1) != 0)
return (1);
sl->data[x] = c;
return (0);
}
static int
sixel_parse_write(struct sixel_image *si, u_int ch)
{
struct sixel_line *sl;
u_int i;
if (sixel_parse_expand_lines(si, si->dy + 6) != 0)
return (1);
sl = &si->lines[si->dy];
for (i = 0; i < 6; i++) {
if (sixel_parse_expand_line(si, sl, si->dx + 1) != 0)
return (1);
if (ch & (1 << i))
sl->data[si->dx] = si->dc;
sl++;
}
return (0);
}
static const char *
sixel_parse_attributes(struct sixel_image *si, const char *cp, const char *end)
{
const char *last;
char *endptr;
u_int x, y;
last = cp;
while (last != end) {
if (*last != ';' && (*last < '0' || *last > '9'))
break;
last++;
}
strtoul(cp, &endptr, 10);
if (endptr == last || *endptr != ';')
return (last);
strtoul(endptr + 1, &endptr, 10);
if (endptr == last)
return (last);
if (*endptr != ';') {
log_debug("%s: missing ;", __func__);
return (NULL);
}
x = strtoul(endptr + 1, &endptr, 10);
if (endptr == last || *endptr != ';') {
log_debug("%s: missing ;", __func__);
return (NULL);
}
if (x > SIXEL_WIDTH_LIMIT) {
log_debug("%s: image is too wide", __func__);
return (NULL);
}
y = strtoul(endptr + 1, &endptr, 10);
if (endptr != last) {
log_debug("%s: extra ;", __func__);
return (NULL);
}
if (y > SIXEL_HEIGHT_LIMIT) {
log_debug("%s: image is too tall", __func__);
return (NULL);
}
si->x = x;
sixel_parse_expand_lines(si, y);
si->set_ra = 1;
si->ra_x = x;
si->ra_y = y;
return (last);
}
static const char *
sixel_parse_colour(struct sixel_image *si, const char *cp, const char *end)
{
const char *last;
char *endptr;
u_int c, type, r, g, b;
last = cp;
while (last != end) {
if (*last != ';' && (*last < '0' || *last > '9'))
break;
last++;
}
c = strtoul(cp, &endptr, 10);
if (c > SIXEL_COLOUR_REGISTERS) {
log_debug("%s: too many colours", __func__);
return (NULL);
}
si->dc = c + 1;
if (endptr == last || *endptr != ';')
return (last);
type = strtoul(endptr + 1, &endptr, 10);
if (endptr == last || *endptr != ';') {
log_debug("%s: missing ;", __func__);
return (NULL);
}
r = strtoul(endptr + 1, &endptr, 10);
if (endptr == last || *endptr != ';') {
log_debug("%s: missing ;", __func__);
return (NULL);
}
g = strtoul(endptr + 1, &endptr, 10);
if (endptr == last || *endptr != ';') {
log_debug("%s: missing ;", __func__);
return (NULL);
}
b = strtoul(endptr + 1, &endptr, 10);
if (endptr != last) {
log_debug("%s: missing ;", __func__);
return (NULL);
}
if (type != 1 && type != 2) {
log_debug("%s: invalid type %d", __func__, type);
return (NULL);
}
if (c + 1 > si->ncolours) {
si->colours = xrecallocarray(si->colours, si->ncolours, c + 1,
sizeof *si->colours);
si->ncolours = c + 1;
}
si->colours[c] = (type << 24) | (r << 16) | (g << 8) | b;
return (last);
}
static const char *
sixel_parse_repeat(struct sixel_image *si, const char *cp, const char *end)
{
const char *last;
char tmp[32], ch;
u_int n = 0, i;
const char *errstr = NULL;
last = cp;
while (last != end) {
if (*last < '0' || *last > '9')
break;
tmp[n++] = *last++;
if (n == (sizeof tmp) - 1) {
log_debug("%s: repeat not terminated", __func__);
return (NULL);
}
}
if (n == 0 || last == end) {
log_debug("%s: repeat not terminated", __func__);
return (NULL);
}
tmp[n] = '\0';
n = strtonum(tmp, 1, SIXEL_WIDTH_LIMIT, &errstr);
if (n == 0 || errstr != NULL) {
log_debug("%s: repeat too wide", __func__);
return (NULL);
}
ch = (*last++) - 0x3f;
for (i = 0; i < n; i++) {
if (sixel_parse_write(si, ch) != 0) {
log_debug("%s: width limit reached", __func__);
return (NULL);
}
si->dx++;
}
return (last);
}
struct sixel_image *
sixel_parse(const char *buf, size_t len, u_int p2, u_int xpixel, u_int ypixel)
{
struct sixel_image *si;
const char *cp = buf, *end = buf + len;
char ch;
if (len == 0 || len == 1 || *cp++ != 'q') {
log_debug("%s: empty image", __func__);
return (NULL);
}
si = xcalloc (1, sizeof *si);
si->xpixel = xpixel;
si->ypixel = ypixel;
si->p2 = p2;
while (cp != end) {
ch = *cp++;
switch (ch) {
case '"':
cp = sixel_parse_attributes(si, cp, end);
if (cp == NULL)
goto bad;
break;
case '#':
cp = sixel_parse_colour(si, cp, end);
if (cp == NULL)
goto bad;
break;
case '!':
cp = sixel_parse_repeat(si, cp, end);
if (cp == NULL)
goto bad;
break;
case '-':
si->dx = 0;
si->dy += 6;
break;
case '$':
si->dx = 0;
break;
default:
if (ch < 0x20)
break;
if (ch < 0x3f || ch > 0x7e)
goto bad;
if (sixel_parse_write(si, ch - 0x3f) != 0) {
log_debug("%s: width limit reached", __func__);
goto bad;
}
si->dx++;
break;
}
}
if (si->x == 0 || si->y == 0)
goto bad;
return (si);
bad:
free(si);
return (NULL);
}
void
sixel_free(struct sixel_image *si)
{
u_int y;
for (y = 0; y < si->y; y++)
free(si->lines[y].data);
free(si->lines);
free(si->colours);
free(si);
}
void
sixel_log(struct sixel_image *si)
{
struct sixel_line *sl;
char s[SIXEL_WIDTH_LIMIT + 1];
u_int i, x, y, cx, cy;
sixel_size_in_cells(si, &cx, &cy);
log_debug("%s: image %ux%u (%ux%u)", __func__, si->x, si->y, cx, cy);
for (i = 0; i < si->ncolours; i++)
log_debug("%s: colour %u is %07x", __func__, i, si->colours[i]);
for (y = 0; y < si->y; y++) {
sl = &si->lines[y];
for (x = 0; x < si->x; x++) {
if (x >= sl->x)
s[x] = '_';
else if (sl->data[x] != 0)
s[x] = '0' + (sl->data[x] - 1) % 10;
else
s[x] = '.';
}
s[x] = '\0';
log_debug("%s: %4u: %s", __func__, y, s);
}
}
void
sixel_size_in_cells(struct sixel_image *si, u_int *x, u_int *y)
{
if ((si->x % si->xpixel) == 0)
*x = (si->x / si->xpixel);
else
*x = 1 + (si->x / si->xpixel);
if ((si->y % si->ypixel) == 0)
*y = (si->y / si->ypixel);
else
*y = 1 + (si->y / si->ypixel);
}
struct sixel_image *
sixel_scale(struct sixel_image *si, u_int xpixel, u_int ypixel, u_int ox,
u_int oy, u_int sx, u_int sy, int colours)
{
struct sixel_image *new;
u_int cx, cy, pox, poy, psx, psy, tsx, tsy, px, py;
u_int x, y, i;
/*
* We want to get the section of the image at ox,oy in image cells and
* map it onto the same size in terminal cells, remembering that we
* can only draw vertical sections of six pixels.
*/
sixel_size_in_cells(si, &cx, &cy);
if (ox >= cx)
return (NULL);
if (oy >= cy)
return (NULL);
if (ox + sx >= cx)
sx = cx - ox;
if (oy + sy >= cy)
sy = cy - oy;
if (xpixel == 0)
xpixel = si->xpixel;
if (ypixel == 0)
ypixel = si->ypixel;
pox = ox * si->xpixel;
poy = oy * si->ypixel;
psx = sx * si->xpixel;
psy = sy * si->ypixel;
tsx = sx * xpixel;
tsy = ((sy * ypixel) / 6) * 6;
new = xcalloc (1, sizeof *si);
new->xpixel = xpixel;
new->ypixel = ypixel;
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 */
new->ra_x = new->ra_x > pox ? new->ra_x - pox : 0;
new->ra_y = new->ra_y > poy ? new->ra_y - poy : 0;
/* resize */
new->ra_x = new->ra_x * xpixel / si->xpixel;
new->ra_y = new->ra_y * ypixel / si->ypixel;
for (y = 0; y < tsy; y++) {
py = poy + ((double)y * psy / tsy);
for (x = 0; x < tsx; x++) {
px = pox + ((double)x * psx / tsx);
sixel_set_pixel(new, x, y, sixel_get_pixel(si, px, py));
}
}
if (colours) {
new->colours = xmalloc(si->ncolours * sizeof *new->colours);
for (i = 0; i < si->ncolours; i++)
new->colours[i] = si->colours[i];
new->ncolours = si->ncolours;
}
return (new);
}
static void
sixel_print_add(char **buf, size_t *len, size_t *used, const char *s,
size_t slen)
{
if (*used + slen >= *len + 1) {
(*len) *= 2;
*buf = xrealloc(*buf, *len);
}
memcpy(*buf + *used, s, slen);
(*used) += slen;
}
static void
sixel_print_repeat(char **buf, size_t *len, size_t *used, u_int count, char ch)
{
char tmp[16];
size_t tmplen;
if (count == 1)
sixel_print_add(buf, len, used, &ch, 1);
else if (count == 2) {
sixel_print_add(buf, len, used, &ch, 1);
sixel_print_add(buf, len, used, &ch, 1);
} else if (count == 3) {
sixel_print_add(buf, len, used, &ch, 1);
sixel_print_add(buf, len, used, &ch, 1);
sixel_print_add(buf, len, used, &ch, 1);
} else if (count != 0) {
tmplen = xsnprintf(tmp, sizeof tmp, "!%u%c", count, ch);
sixel_print_add(buf, len, used, tmp, tmplen);
}
}
static void
sixel_print_compress_colors(struct sixel_image *si, struct sixel_chunk *chunks,
u_int y, u_int *active, u_int *nactive)
{
u_int i, x, c, dx, colors[6];
struct sixel_chunk *chunk = NULL;
struct sixel_line *sl;
for (x = 0; x < si->x; x++) {
for (i = 0; i < 6; i++) {
colors[i] = 0;
if (y + i < si->y) {
sl = &si->lines[y + i];
if (x < sl->x && sl->data[x] != 0) {
colors[i] = sl->data[x];
c = sl->data[x] - 1;
chunks[c].next_pattern |= 1 << i;
}
}
}
for (i = 0; i < 6; i++) {
if (colors[i] == 0)
continue;
c = colors[i] - 1;
chunk = &chunks[c];
if (chunk->next_x == x + 1)
continue;
if (chunk->next_y < y + 1) {
chunk->next_y = y + 1;
active[(*nactive)++] = c;
}
dx = x - chunk->next_x;
if (chunk->pattern != chunk->next_pattern || dx != 0) {
sixel_print_repeat(&chunk->data, &chunk->len,
&chunk->used, chunk->count,
chunk->pattern + 0x3f);
sixel_print_repeat(&chunk->data, &chunk->len,
&chunk->used, dx, '?');
chunk->pattern = chunk->next_pattern;
chunk->count = 0;
}
chunk->count++;
chunk->next_pattern = 0;
chunk->next_x = x + 1;
}
}
}
char *
sixel_print(struct sixel_image *si, struct sixel_image *map, size_t *size)
{
char *buf, tmp[64];
size_t len, used = 0, tmplen;
u_int *colours, ncolours, i, c, y, *active, nactive;
struct sixel_chunk *chunks, *chunk;
if (map != NULL) {
colours = map->colours;
ncolours = map->ncolours;
} else {
colours = si->colours;
ncolours = si->ncolours;
}
if (ncolours == 0)
return (NULL);
len = 8192;
buf = xmalloc(len);
tmplen = xsnprintf(tmp, sizeof tmp, "\033P0;%uq", si->p2);
sixel_print_add(&buf, &len, &used, tmp, tmplen);
if (si->set_ra) {
tmplen = xsnprintf(tmp, sizeof tmp, "\"1;1;%u;%u", si->ra_x,
si->ra_y);
sixel_print_add(&buf, &len, &used, tmp, tmplen);
}
chunks = xcalloc(ncolours, sizeof *chunks);
active = xcalloc(ncolours, sizeof *active);
for (i = 0; i < ncolours; i++) {
c = colours[i];
tmplen = xsnprintf(tmp, sizeof tmp, "#%u;%u;%u;%u;%u",
i, c >> 24, (c >> 16) & 0xff, (c >> 8) & 0xff, c & 0xff);
sixel_print_add(&buf, &len, &used, tmp, tmplen);
chunk = &chunks[i];
chunk->len = 8;
chunk->data = xmalloc(chunk->len);
}
for (y = 0; y < si->y; y += 6) {
nactive = 0;
sixel_print_compress_colors(si, chunks, y, active, &nactive);
for (i = 0; i < nactive; i++) {
c = active[i];
chunk = &chunks[c];
tmplen = xsnprintf(tmp, sizeof tmp, "#%u", c);
sixel_print_add(&buf, &len, &used, tmp, tmplen);
sixel_print_add(&buf, &len, &used, chunk->data,
chunk->used);
sixel_print_repeat(&buf, &len, &used, chunk->count,
chunk->pattern + 0x3f);
sixel_print_add(&buf, &len, &used, "$", 1);
chunk->used = chunk->next_x = chunk->count = 0;
}
if (buf[used - 1] == '$')
used--;
sixel_print_add(&buf, &len, &used, "-", 1);
}
if (buf[used - 1] == '-')
used--;
sixel_print_add(&buf, &len, &used, "\033\\", 2);
buf[used] = '\0';
if (size != NULL)
*size = used;
for (i = 0; i < ncolours; i++)
free(chunks[i].data);
free(active);
free(chunks);
return (buf);
}
struct screen *
sixel_to_screen(struct sixel_image *si)
{
struct screen *s;
struct screen_write_ctx ctx;
struct grid_cell gc;
u_int x, y, sx, sy;
sixel_size_in_cells(si, &sx, &sy);
s = xmalloc(sizeof *s);
screen_init(s, sx, sy, 0);
memcpy(&gc, &grid_default_cell, sizeof gc);
gc.attr |= (GRID_ATTR_CHARSET|GRID_ATTR_DIM);
utf8_set(&gc.data, '~');
screen_write_start(&ctx, s);
if (sx == 1 || sy == 1) {
for (y = 0; y < sy; y++) {
for (x = 0; x < sx; x++)
grid_view_set_cell(s->grid, x, y, &gc);
}
} else {
screen_write_box(&ctx, sx, sy, BOX_LINES_DEFAULT, NULL, NULL);
for (y = 1; y < sy - 1; y++) {
for (x = 1; x < sx - 1; x++)
grid_view_set_cell(s->grid, x, y, &gc);
}
}
screen_write_stop(&ctx);
return (s);
}

186
image.c Normal file
View File

@ -0,0 +1,186 @@
/* $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"
static struct images all_images = TAILQ_HEAD_INITIALIZER(all_images);
static u_int all_images_count;
static void
image_free(struct image *im)
{
struct screen *s = im->s;
TAILQ_REMOVE(&all_images, im, all_entry);
all_images_count--;
TAILQ_REMOVE(&s->images, im, entry);
sixel_free(im->data);
free(im->fallback);
free(im);
}
int
image_free_all(struct screen *s)
{
struct image *im, *im1;
int redraw = !TAILQ_EMPTY(&s->images);
TAILQ_FOREACH_SAFE(im, &s->images, entry, im1)
image_free(im);
return (redraw);
}
/* Create text placeholder for an image. */
static void
image_fallback(char **ret, u_int sx, u_int sy)
{
char *buf, *label;
u_int py, size, lsize;
/* Allocate first line. */
lsize = xasprintf(&label, "SIXEL IMAGE (%ux%u)\r\n", sx, sy) + 1;
if (sx < lsize - 3)
size = lsize - 1;
else
size = sx + 2;
/* Remaining lines. Every placeholder line has \r\n at the end. */
size += (sx + 2) * (sy - 1) + 1;
*ret = buf = xmalloc(size);
/* Render first line. */
if (sx < lsize - 3) {
memcpy(buf, label, lsize);
buf += lsize - 1;
} else {
memcpy(buf, label, lsize - 3);
buf += lsize - 3;
memset(buf, '+', sx - lsize + 3);
buf += sx - lsize + 3;
snprintf(buf, 3, "\r\n");
buf += 2;
}
/* Remaining lines. */
for (py = 1; py < sy; py++) {
memset(buf, '+', sx);
buf += sx;
snprintf(buf, 3, "\r\n");
buf += 2;
}
free(label);
}
struct image*
image_store(struct screen *s, struct sixel_image *si)
{
struct image *im;
im = xcalloc(1, sizeof *im);
im->s = s;
im->data = si;
im->px = s->cx;
im->py = s->cy;
sixel_size_in_cells(si, &im->sx, &im->sy);
image_fallback(&im->fallback, im->sx, im->sy);
TAILQ_INSERT_TAIL(&s->images, im, entry);
TAILQ_INSERT_TAIL(&all_images, im, all_entry);
if (++all_images_count == 10/*XXX*/)
image_free(TAILQ_FIRST(&all_images));
return (im);
}
int
image_check_line(struct screen *s, u_int py, u_int ny)
{
struct image *im, *im1;
int redraw = 0;
TAILQ_FOREACH_SAFE(im, &s->images, entry, im1) {
if (py + ny > im->py && py < im->py + im->sy) {
image_free(im);
redraw = 1;
}
}
return (redraw);
}
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;
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;
}
return (redraw);
}
int
image_scroll_up(struct screen *s, u_int lines)
{
struct image *im, *im1;
int redraw = 0;
u_int sx, sy;
struct sixel_image *new;
TAILQ_FOREACH_SAFE(im, &s->images, entry, im1) {
if (im->py >= lines) {
im->py -= lines;
redraw = 1;
continue;
}
if (im->py + im->sy <= lines) {
image_free(im);
redraw = 1;
continue;
}
sx = im->sx;
sy = (im->py + im->sy) - lines;
new = sixel_scale(im->data, 0, 0, 0, im->sy - sy, sx, sy, 1);
sixel_free(im->data);
im->data = new;
im->py = 0;
sixel_size_in_cells(im->data, &im->sx, &im->sy);
free(im->fallback);
image_fallback(&im->fallback, im->sx, im->sy);
redraw = 1;
}
return (redraw);
}

View File

@ -53,9 +53,15 @@ static struct input_key_entry input_key_defaults[] = {
{ .key = KEYC_PASTE_START,
.data = "\033[200~"
},
{ .key = KEYC_PASTE_START|KEYC_IMPLIED_META,
.data = "\033[200~"
},
{ .key = KEYC_PASTE_END,
.data = "\033[201~"
},
{ .key = KEYC_PASTE_END|KEYC_IMPLIED_META,
.data = "\033[201~"
},
/* Function keys. */
{ .key = KEYC_F1,
@ -306,7 +312,13 @@ 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,
@ -412,118 +424,18 @@ input_key_write(const char *from, struct bufferevent *bev, const char *data,
bufferevent_write(bev, data, size);
}
/* Translate a key code into an output key sequence. */
int
input_key(struct screen *s, struct bufferevent *bev, key_code key)
/*
* Encode and write an extended key escape sequence in one of the two
* possible formats, depending on the configured output mode.
*/
static int
input_key_extended(struct bufferevent *bev, key_code key)
{
struct input_key_entry *ike;
key_code justkey, newkey, outkey, modifiers;
struct utf8_data ud;
char tmp[64], modifier;
struct utf8_data ud;
wchar_t wc;
/* Mouse keys need a pane. */
if (KEYC_IS_MOUSE(key))
return (0);
/* Literal keys go as themselves (can't be more than eight bits). */
if (key & KEYC_LITERAL) {
ud.data[0] = (u_char)key;
input_key_write(__func__, bev, &ud.data[0], 1);
return (0);
}
/* Is this backspace? */
if ((key & KEYC_MASK_KEY) == KEYC_BSPACE) {
newkey = options_get_number(global_options, "backspace");
if (newkey >= 0x7f)
newkey = '\177';
key = newkey|(key & (KEYC_MASK_MODIFIERS|KEYC_MASK_FLAGS));
}
/*
* If this is a normal 7-bit key, just send it, with a leading escape
* if necessary. If it is a UTF-8 key, split it and send it.
*/
justkey = (key & ~(KEYC_META|KEYC_IMPLIED_META));
if (justkey <= 0x7f) {
if (key & KEYC_META)
input_key_write(__func__, bev, "\033", 1);
ud.data[0] = justkey;
input_key_write(__func__, bev, &ud.data[0], 1);
return (0);
}
if (KEYC_IS_UNICODE(justkey)) {
if (key & KEYC_META)
input_key_write(__func__, bev, "\033", 1);
utf8_to_data(justkey, &ud);
input_key_write(__func__, bev, ud.data, ud.size);
return (0);
}
/*
* Look up in the tree. If not in application keypad or cursor mode,
* remove the flags from the key.
*/
if (~s->mode & MODE_KKEYPAD)
key &= ~KEYC_KEYPAD;
if (~s->mode & MODE_KCURSOR)
key &= ~KEYC_CURSOR;
ike = input_key_get(key);
if (ike == NULL && (key & KEYC_META) && (~key & KEYC_IMPLIED_META))
ike = input_key_get(key & ~KEYC_META);
if (ike == NULL && (key & KEYC_CURSOR))
ike = input_key_get(key & ~KEYC_CURSOR);
if (ike == NULL && (key & KEYC_KEYPAD))
ike = input_key_get(key & ~KEYC_KEYPAD);
if (ike != NULL) {
log_debug("found key 0x%llx: \"%s\"", key, ike->data);
if ((key & KEYC_META) && (~key & KEYC_IMPLIED_META))
input_key_write(__func__, bev, "\033", 1);
input_key_write(__func__, bev, ike->data, strlen(ike->data));
return (0);
}
/* No builtin key sequence; construct an extended key sequence. */
if (~s->mode & MODE_KEXTENDED) {
if ((key & KEYC_MASK_MODIFIERS) != KEYC_CTRL)
goto missing;
justkey = (key & KEYC_MASK_KEY);
switch (justkey) {
case ' ':
case '2':
key = 0|(key & ~KEYC_MASK_KEY);
break;
case '|':
key = 28|(key & ~KEYC_MASK_KEY);
break;
case '6':
key = 30|(key & ~KEYC_MASK_KEY);
break;
case '-':
case '/':
key = 31|(key & ~KEYC_MASK_KEY);
break;
case '?':
key = 127|(key & ~KEYC_MASK_KEY);
break;
default:
if (justkey >= 'A' && justkey <= '_')
key = (justkey - 'A')|(key & ~KEYC_MASK_KEY);
else if (justkey >= 'a' && justkey <= '~')
key = (justkey - 96)|(key & ~KEYC_MASK_KEY);
else
return (0);
break;
}
return (input_key(s, bev, key & ~KEYC_CTRL));
}
outkey = (key & KEYC_MASK_KEY);
modifiers = (key & KEYC_MASK_MODIFIERS);
if (outkey < 32 && outkey != 9 && outkey != 13 && outkey != 27) {
outkey = 64 + outkey;
modifiers |= KEYC_CTRL;
}
switch (modifiers) {
switch (key & KEYC_MASK_MODIFIERS) {
case KEYC_SHIFT:
modifier = '2';
break;
@ -546,17 +458,261 @@ input_key(struct screen *s, struct bufferevent *bev, key_code key)
modifier = '8';
break;
default:
goto missing;
return (-1);
}
xsnprintf(tmp, sizeof tmp, "\033[%llu;%cu", outkey, modifier);
if (KEYC_IS_UNICODE(key)) {
utf8_to_data(key & KEYC_MASK_KEY, &ud);
if (utf8_towc(&ud, &wc) == UTF8_DONE)
key = wc;
else
return (-1);
} else
key &= KEYC_MASK_KEY;
if (options_get_number(global_options, "extended-keys-format") == 1)
xsnprintf(tmp, sizeof tmp, "\033[27;%c;%llu~", modifier, key);
else
xsnprintf(tmp, sizeof tmp, "\033[%llu;%cu", key, modifier);
input_key_write(__func__, bev, tmp, strlen(tmp));
return (0);
}
missing:
log_debug("key 0x%llx missing", key);
/*
* Outputs the key in the "standard" mode. This is by far the most
* complicated output mode, with a lot of remapping in order to
* emulate quirks of terminals that today can be only found in museums.
*/
static int
input_key_vt10x(struct bufferevent *bev, key_code key)
{
struct utf8_data ud;
key_code onlykey;
char *p;
static const char *standard_map[2] = {
"1!9(0)=+;:'\",<.>/-8? 2",
"119900=+;;'',,..\x1f\x1f\x7f\x7f\0\0",
};
log_debug("%s: key in %llx", __func__, key);
if (key & KEYC_META)
input_key_write(__func__, bev, "\033", 1);
/*
* There's no way to report modifiers for unicode keys in standard mode
* so lose the modifiers.
*/
if (KEYC_IS_UNICODE(key)) {
utf8_to_data(key, &ud);
input_key_write(__func__, bev, ud.data, ud.size);
return (0);
}
/*
* Prevent TAB, CR and LF from being swallowed by the C0 remapping
* logic.
*/
onlykey = key & KEYC_MASK_KEY;
if (onlykey == '\r' || onlykey == '\n' || onlykey == '\t')
key &= ~KEYC_CTRL;
/*
* Convert keys with Ctrl modifier into corresponding C0 control codes,
* with the exception of *some* keys, which are remapped into printable
* ASCII characters.
*
* There is no special handling for Shift modifier, which is pretty
* much redundant anyway, as no terminal will send <base key>|SHIFT,
* but only <shifted key>|SHIFT.
*/
if (key & KEYC_CTRL) {
p = strchr(standard_map[0], onlykey);
if (p != NULL)
key = standard_map[1][p - standard_map[0]];
else if (onlykey >= '3' && onlykey <= '7')
key = onlykey - '\030';
else if (onlykey >= '@' && onlykey <= '~')
key = onlykey & 0x1f;
else
return (-1);
}
log_debug("%s: key out %llx", __func__, key);
ud.data[0] = key & 0x7f;
input_key_write(__func__, bev, &ud.data[0], 1);
return (0);
}
/* Pick keys that are reported as vt10x keys in modifyOtherKeys=1 mode. */
static int
input_key_mode1(struct bufferevent *bev, key_code key)
{
key_code onlykey;
log_debug("%s: key in %llx", __func__, key);
/* A regular or shifted key + Meta. */
if ((key & (KEYC_CTRL | KEYC_META)) == KEYC_META)
return (input_key_vt10x(bev, key));
/*
* As per
* https://invisible-island.net/xterm/modified-keys-us-pc105.html.
*/
onlykey = key & KEYC_MASK_KEY;
if ((key & KEYC_CTRL) &&
(onlykey == ' ' ||
onlykey == '/' ||
onlykey == '@' ||
onlykey == '^' ||
(onlykey >= '2' && onlykey <= '8') ||
(onlykey >= '@' && onlykey <= '~')))
return (input_key_vt10x(bev, key));
return (-1);
}
/* Translate a key code into an output key sequence. */
int
input_key(struct screen *s, struct bufferevent *bev, key_code key)
{
struct input_key_entry *ike = NULL;
key_code newkey;
struct utf8_data ud;
/* Mouse keys need a pane. */
if (KEYC_IS_MOUSE(key))
return (0);
/* Literal keys go as themselves (can't be more than eight bits). */
if (key & KEYC_LITERAL) {
ud.data[0] = (u_char)key;
input_key_write(__func__, bev, &ud.data[0], 1);
return (0);
}
/* Is this backspace? */
if ((key & KEYC_MASK_KEY) == KEYC_BSPACE) {
newkey = options_get_number(global_options, "backspace");
log_debug("%s: key 0x%llx is backspace -> 0x%llx", __func__,
key, newkey);
if ((key & KEYC_MASK_MODIFIERS) == 0) {
ud.data[0] = 255;
if ((newkey & KEYC_MASK_MODIFIERS) == 0)
ud.data[0] = newkey;
else if ((newkey & KEYC_MASK_MODIFIERS) == KEYC_CTRL) {
newkey &= KEYC_MASK_KEY;
if (newkey >= 'A' && newkey <= 'Z')
ud.data[0] = newkey - 0x40;
else if (newkey >= 'a' && newkey <= 'z')
ud.data[0] = newkey - 0x60;
}
if (ud.data[0] != 255)
input_key_write(__func__, bev, &ud.data[0], 1);
return (0);
}
key = newkey|(key & (KEYC_MASK_FLAGS|KEYC_MASK_MODIFIERS));
}
/* Is this backtab? */
if ((key & KEYC_MASK_KEY) == KEYC_BTAB) {
if (s->mode & MODE_KEYS_EXTENDED_2) {
/* When in xterm extended mode, remap into S-Tab. */
key = '\011' | (key & ~KEYC_MASK_KEY) | KEYC_SHIFT;
} else {
/* Otherwise clear modifiers. */
key &= ~KEYC_MASK_MODIFIERS;
}
}
/*
* A trivial case, that is a 7-bit key, excluding C0 control characters
* that can't be entered from the keyboard, and no modifiers; or a UTF-8
* key and no modifiers.
*/
if (!(key & ~KEYC_MASK_KEY)) {
if (key == C0_HT ||
key == C0_CR ||
key == C0_ESC ||
(key >= 0x20 && key <= 0x7f)) {
ud.data[0] = key;
input_key_write(__func__, bev, &ud.data[0], 1);
return (0);
}
if (KEYC_IS_UNICODE(key)) {
utf8_to_data(key, &ud);
input_key_write(__func__, bev, ud.data, ud.size);
return (0);
}
}
/*
* Look up the standard VT10x keys in the tree. If not in application
* keypad or cursor mode, remove the respective flags from the key.
*/
if (~s->mode & MODE_KKEYPAD)
key &= ~KEYC_KEYPAD;
if (~s->mode & MODE_KCURSOR)
key &= ~KEYC_CURSOR;
if (ike == NULL)
ike = input_key_get(key);
if (ike == NULL && (key & KEYC_META) && (~key & KEYC_IMPLIED_META))
ike = input_key_get(key & ~KEYC_META);
if (ike == NULL && (key & KEYC_CURSOR))
ike = input_key_get(key & ~KEYC_CURSOR);
if (ike == NULL && (key & KEYC_KEYPAD))
ike = input_key_get(key & ~KEYC_KEYPAD);
if (ike != NULL) {
log_debug("%s: found key 0x%llx: \"%s\"", __func__, key,
ike->data);
if (KEYC_IS_PASTE(key) && (~s->mode & MODE_BRACKETPASTE))
return (0);
if ((key & KEYC_META) && (~key & KEYC_IMPLIED_META))
input_key_write(__func__, bev, "\033", 1);
input_key_write(__func__, bev, ike->data, strlen(ike->data));
return (0);
}
/* Ignore internal function key codes. */
if ((key >= KEYC_BASE && key < KEYC_BASE_END) ||
(key >= KEYC_USER && key < KEYC_USER_END)) {
log_debug("%s: ignoring key 0x%llx", __func__, key);
return (0);
}
/*
* No builtin key sequence; construct an extended key sequence
* depending on the client mode.
*
* If something invalid reaches here, an invalid output may be
* produced. For example Ctrl-Shift-2 is invalid (as there's
* no way to enter it). The correct form is Ctrl-Shift-@, at
* least in US English keyboard layout.
*/
switch (s->mode & EXTENDED_KEY_MODES) {
case MODE_KEYS_EXTENDED_2:
/*
* The simplest mode to handle - *all* modified keys are
* reported in the extended form.
*/
return (input_key_extended(bev, key));
case MODE_KEYS_EXTENDED:
/*
* Some keys are still reported in standard mode, to maintain
* compatibility with applications unaware of extended keys.
*/
if (input_key_mode1(bev, key) == -1)
return (input_key_extended(bev, key));
return (0);
default:
/* The standard mode. */
return (input_key_vt10x(bev, key));
}
}
/* Get mouse event string. */
int
input_key_get_mouse(struct screen *s, struct mouse_event *m, u_int x, u_int y,

457
input.c
View File

@ -93,7 +93,6 @@ struct input_ctx {
size_t param_len;
#define INPUT_BUF_START 32
#define INPUT_BUF_LIMIT 1048576
u_char *input_buf;
size_t input_len;
size_t input_space;
@ -109,10 +108,11 @@ struct input_ctx {
int utf8started;
int ch;
int last;
struct utf8_data last;
int flags;
#define INPUT_DISCARD 0x1
#define INPUT_LAST 0x2
const struct input_state *state;
@ -133,8 +133,9 @@ static void printflike(2, 3) input_reply(struct input_ctx *, const char *, ...);
static void input_set_state(struct input_ctx *,
const struct input_transition *);
static void input_reset_cell(struct input_ctx *);
static void input_report_current_theme(struct input_ctx *);
static void input_osc_4(struct input_ctx *, const char *);
static void input_osc_8(struct input_ctx *, const char *);
static void input_osc_10(struct input_ctx *, const char *);
static void input_osc_11(struct input_ctx *, const char *);
static void input_osc_12(struct input_ctx *, const char *);
@ -143,6 +144,7 @@ static void input_osc_104(struct input_ctx *, const char *);
static void input_osc_110(struct input_ctx *, const char *);
static void input_osc_111(struct input_ctx *, const char *);
static void input_osc_112(struct input_ctx *, const char *);
static void input_osc_133(struct input_ctx *, const char *);
/* Transition entry/exit handlers. */
static void input_clear(struct input_ctx *);
@ -167,6 +169,7 @@ static void input_csi_dispatch_rm(struct input_ctx *);
static void input_csi_dispatch_rm_private(struct input_ctx *);
static void input_csi_dispatch_sm(struct input_ctx *);
static void input_csi_dispatch_sm_private(struct input_ctx *);
static void input_csi_dispatch_sm_graphics(struct input_ctx *);
static void input_csi_dispatch_winops(struct input_ctx *);
static void input_csi_dispatch_sgr_256(struct input_ctx *, int, u_int *);
static void input_csi_dispatch_sgr_rgb(struct input_ctx *, int, u_int *);
@ -201,7 +204,7 @@ enum input_esc_type {
INPUT_ESC_SCSG0_ON,
INPUT_ESC_SCSG1_OFF,
INPUT_ESC_SCSG1_ON,
INPUT_ESC_ST,
INPUT_ESC_ST
};
/* Escape command table. */
@ -240,6 +243,7 @@ enum input_csi_type {
INPUT_CSI_DECSTBM,
INPUT_CSI_DL,
INPUT_CSI_DSR,
INPUT_CSI_DSR_PRIVATE,
INPUT_CSI_ECH,
INPUT_CSI_ED,
INPUT_CSI_EL,
@ -248,6 +252,7 @@ enum input_csi_type {
INPUT_CSI_IL,
INPUT_CSI_MODOFF,
INPUT_CSI_MODSET,
INPUT_CSI_QUERY_PRIVATE,
INPUT_CSI_RCP,
INPUT_CSI_REP,
INPUT_CSI_RM,
@ -256,12 +261,13 @@ enum input_csi_type {
INPUT_CSI_SD,
INPUT_CSI_SGR,
INPUT_CSI_SM,
INPUT_CSI_SM_GRAPHICS,
INPUT_CSI_SM_PRIVATE,
INPUT_CSI_SU,
INPUT_CSI_TBC,
INPUT_CSI_VPA,
INPUT_CSI_WINOPS,
INPUT_CSI_XDA,
INPUT_CSI_XDA
};
/* Control (CSI) command table. */
@ -281,6 +287,7 @@ static const struct input_table_entry input_csi_table[] = {
{ 'M', "", INPUT_CSI_DL },
{ 'P', "", INPUT_CSI_DCH },
{ 'S', "", INPUT_CSI_SU },
{ 'S', "?", INPUT_CSI_SM_GRAPHICS },
{ 'T', "", INPUT_CSI_SD },
{ 'X', "", INPUT_CSI_ECH },
{ 'Z', "", INPUT_CSI_CBT },
@ -299,12 +306,14 @@ static const struct input_table_entry input_csi_table[] = {
{ 'm', ">", INPUT_CSI_MODSET },
{ 'n', "", INPUT_CSI_DSR },
{ 'n', ">", INPUT_CSI_MODOFF },
{ 'n', "?", INPUT_CSI_DSR_PRIVATE },
{ 'p', "?$", INPUT_CSI_QUERY_PRIVATE },
{ 'q', " ", INPUT_CSI_DECSCUSR },
{ 'q', ">", INPUT_CSI_XDA },
{ 'r', "", INPUT_CSI_DECSTBM },
{ 's', "", INPUT_CSI_SCP },
{ 't', "", INPUT_CSI_WINOPS },
{ 'u', "", INPUT_CSI_RCP },
{ 'u', "", INPUT_CSI_RCP }
};
/* Input transition. */
@ -723,6 +732,9 @@ static const struct input_transition input_state_consume_st_table[] = {
{ -1, -1, NULL, NULL }
};
/* Maximum of bytes allowed to read in a single input. */
static size_t input_buffer_size = INPUT_BUF_DEFAULT_SIZE;
/* Input table compare. */
static int
input_table_compare(const void *key, const void *value)
@ -862,8 +874,6 @@ input_reset(struct input_ctx *ictx, int clear)
input_clear(ictx);
ictx->last = -1;
ictx->state = &input_state_ground;
ictx->flags = 0;
}
@ -970,6 +980,10 @@ input_parse_buffer(struct window_pane *wp, u_char *buf, size_t len)
window_update_activity(wp->window);
wp->flags |= PANE_CHANGED;
/* Flag new input while in a mode. */
if (!TAILQ_EMPTY(&wp->modes))
wp->flags |= PANE_UNSEENCHANGES;
/* NULL wp if there is a mode set as don't want to update the tty. */
if (TAILQ_EMPTY(&wp->modes))
screen_write_start_pane(sctx, wp, &wp->base);
@ -1085,6 +1099,7 @@ input_reply(struct input_ctx *ictx, const char *fmt, ...)
xvasprintf(&reply, fmt, ap);
va_end(ap);
log_debug("%s: %s", __func__, reply);
bufferevent_write(bev, reply, strlen(reply));
free(reply);
}
@ -1136,10 +1151,11 @@ input_print(struct input_ctx *ictx)
ictx->cell.cell.attr |= GRID_ATTR_CHARSET;
else
ictx->cell.cell.attr &= ~GRID_ATTR_CHARSET;
utf8_set(&ictx->cell.cell.data, ictx->ch);
screen_write_collect_add(sctx, &ictx->cell.cell);
ictx->last = ictx->ch;
utf8_copy(&ictx->last, &ictx->cell.cell.data);
ictx->flags |= INPUT_LAST;
ictx->cell.cell.attr &= ~GRID_ATTR_CHARSET;
@ -1183,7 +1199,7 @@ input_input(struct input_ctx *ictx)
available = ictx->input_space;
while (ictx->input_len + 1 >= available) {
available *= 2;
if (available > INPUT_BUF_LIMIT) {
if (available > input_buffer_size) {
ictx->flags |= INPUT_DISCARD;
return (0);
}
@ -1203,6 +1219,10 @@ input_c0_dispatch(struct input_ctx *ictx)
struct screen_write_ctx *sctx = &ictx->ctx;
struct window_pane *wp = ictx->wp;
struct screen *s = sctx->s;
struct grid_cell gc, first_gc;
u_int cx = s->cx, line = s->cy + s->grid->hsize;
u_int width;
int has_content = 0;
ictx->utf8started = 0; /* can't be valid UTF-8 */
@ -1224,11 +1244,28 @@ input_c0_dispatch(struct input_ctx *ictx)
break;
/* Find the next tab point, or use the last column if none. */
grid_get_cell(s->grid, s->cx, line, &first_gc);
do {
s->cx++;
if (bit_test(s->tabs, s->cx))
if (!has_content) {
grid_get_cell(s->grid, cx, line, &gc);
if (gc.data.size != 1 ||
*gc.data.data != ' ' ||
!grid_cells_look_equal(&gc, &first_gc))
has_content = 1;
}
cx++;
if (bit_test(s->tabs, cx))
break;
} while (s->cx < screen_size_x(s) - 1);
} while (cx < screen_size_x(s) - 1);
width = cx - s->cx;
if (has_content || width > sizeof gc.data.data)
s->cx = cx;
else {
grid_get_cell(s->grid, s->cx, line, &gc);
grid_set_tab(&gc, width);
screen_write_collect_add(sctx, &gc);
}
break;
case '\012': /* LF */
case '\013': /* VT */
@ -1251,7 +1288,7 @@ input_c0_dispatch(struct input_ctx *ictx)
break;
}
ictx->last = -1;
ictx->flags &= ~INPUT_LAST;
return (0);
}
@ -1327,7 +1364,7 @@ input_esc_dispatch(struct input_ctx *ictx)
break;
}
ictx->last = -1;
ictx->flags &= ~INPUT_LAST;
return (0);
}
@ -1338,14 +1375,14 @@ input_csi_dispatch(struct input_ctx *ictx)
struct screen_write_ctx *sctx = &ictx->ctx;
struct screen *s = sctx->s;
struct input_table_entry *entry;
int i, n, m;
int i, n, m, ek, set;
u_int cx, bg = ictx->cell.cell.bg;
if (ictx->flags & INPUT_DISCARD)
return (0);
log_debug("%s: '%c' \"%s\" \"%s\"",
__func__, ictx->ch, ictx->interm_buf, ictx->param_buf);
log_debug("%s: '%c' \"%s\" \"%s\"", __func__, ictx->ch,
ictx->interm_buf, ictx->param_buf);
if (input_split(ictx) != 0)
return (0);
@ -1396,18 +1433,36 @@ input_csi_dispatch(struct input_ctx *ictx)
break;
case INPUT_CSI_MODSET:
n = input_get(ictx, 0, 0, 0);
m = input_get(ictx, 1, 0, 0);
if (options_get_number(global_options, "extended-keys") == 2)
if (n != 4)
break;
if (n == 0 || (n == 4 && m == 0))
screen_write_mode_clear(sctx, MODE_KEXTENDED);
else if (n == 4 && (m == 1 || m == 2))
screen_write_mode_set(sctx, MODE_KEXTENDED);
m = input_get(ictx, 1, 0, 0);
/*
* Set the extended key reporting mode as per the client
* request, unless "extended-keys" is set to "off".
*/
ek = options_get_number(global_options, "extended-keys");
if (ek == 0)
break;
screen_write_mode_clear(sctx, EXTENDED_KEY_MODES);
if (m == 2)
screen_write_mode_set(sctx, MODE_KEYS_EXTENDED_2);
else if (m == 1 || ek == 2)
screen_write_mode_set(sctx, MODE_KEYS_EXTENDED);
break;
case INPUT_CSI_MODOFF:
n = input_get(ictx, 0, 0, 0);
if (n == 4)
screen_write_mode_clear(sctx, MODE_KEXTENDED);
if (n != 4)
break;
/*
* Clear the extended key reporting mode as per the client
* request, unless "extended-keys always" forces into mode 1.
*/
screen_write_mode_clear(sctx,
MODE_KEYS_EXTENDED|MODE_KEYS_EXTENDED_2);
if (options_get_number(global_options, "extended-keys") == 2)
screen_write_mode_set(sctx, MODE_KEYS_EXTENDED);
break;
case INPUT_CSI_WINOPS:
input_csi_dispatch_winops(ictx);
@ -1436,7 +1491,11 @@ input_csi_dispatch(struct input_ctx *ictx)
case -1:
break;
case 0:
#ifdef ENABLE_SIXEL
input_reply(ictx, "\033[?1;2;4c");
#else
input_reply(ictx, "\033[?1;2c");
#endif
break;
default:
log_debug("%s: unknown '%c'", __func__, ictx->ch);
@ -1476,6 +1535,20 @@ input_csi_dispatch(struct input_ctx *ictx)
if (n != -1)
screen_write_deleteline(sctx, n, bg);
break;
case INPUT_CSI_DSR_PRIVATE:
switch (input_get(ictx, 0, 0, 0)) {
case 996:
input_report_current_theme(ictx);
break;
}
break;
case INPUT_CSI_QUERY_PRIVATE:
switch (input_get(ictx, 0, 0, 0)) {
case 2031:
input_reply(ictx, "\033[?2031;2$y");
break;
}
break;
case INPUT_CSI_DSR:
switch (input_get(ictx, 0, 0, 0)) {
case -1:
@ -1560,12 +1633,17 @@ input_csi_dispatch(struct input_ctx *ictx)
if (n > m)
n = m;
if (ictx->last == -1)
if (~ictx->flags & INPUT_LAST)
break;
ictx->ch = ictx->last;
set = ictx->cell.set == 0 ? ictx->cell.g0set : ictx->cell.g1set;
if (set == 1)
ictx->cell.cell.attr |= GRID_ATTR_CHARSET;
else
ictx->cell.cell.attr &= ~GRID_ATTR_CHARSET;
utf8_copy(&ictx->cell.cell.data, &ictx->last);
for (i = 0; i < n; i++)
input_print(ictx);
screen_write_collect_add(sctx, &ictx->cell.cell);
break;
case INPUT_CSI_RCP:
input_restore_state(ictx);
@ -1588,6 +1666,9 @@ input_csi_dispatch(struct input_ctx *ictx)
case INPUT_CSI_SM_PRIVATE:
input_csi_dispatch_sm_private(ictx);
break;
case INPUT_CSI_SM_GRAPHICS:
input_csi_dispatch_sm_graphics(ictx);
break;
case INPUT_CSI_SU:
n = input_get(ictx, 0, 1, 1);
if (n != -1)
@ -1632,7 +1713,7 @@ input_csi_dispatch(struct input_ctx *ictx)
}
ictx->last = -1;
ictx->flags &= ~INPUT_LAST;
return (0);
}
@ -1718,6 +1799,9 @@ input_csi_dispatch_rm_private(struct input_ctx *ictx)
case 2004:
screen_write_mode_clear(sctx, MODE_BRACKETPASTE);
break;
case 2031:
screen_write_mode_clear(sctx, MODE_THEME_UPDATES);
break;
default:
log_debug("%s: unknown '%c'", __func__, ictx->ch);
break;
@ -1754,7 +1838,6 @@ static void
input_csi_dispatch_sm_private(struct input_ctx *ictx)
{
struct screen_write_ctx *sctx = &ictx->ctx;
struct window_pane *wp = ictx->wp;
struct grid_cell *gc = &ictx->cell.cell;
u_int i;
@ -1796,17 +1879,7 @@ input_csi_dispatch_sm_private(struct input_ctx *ictx)
screen_write_mode_set(sctx, MODE_MOUSE_ALL);
break;
case 1004:
if (sctx->s->mode & MODE_FOCUSON)
break;
screen_write_mode_set(sctx, MODE_FOCUSON);
if (wp == NULL)
break;
if (!options_get_number(global_options, "focus-events"))
break;
if (wp->flags & PANE_FOCUSED)
bufferevent_write(wp->event, "\033[I", 3);
else
bufferevent_write(wp->event, "\033[O", 3);
break;
case 1005:
screen_write_mode_set(sctx, MODE_MOUSE_UTF8);
@ -1824,6 +1897,9 @@ input_csi_dispatch_sm_private(struct input_ctx *ictx)
case 2004:
screen_write_mode_set(sctx, MODE_BRACKETPASTE);
break;
case 2031:
screen_write_mode_set(sctx, MODE_THEME_UPDATES);
break;
default:
log_debug("%s: unknown '%c'", __func__, ictx->ch);
break;
@ -1831,6 +1907,26 @@ input_csi_dispatch_sm_private(struct input_ctx *ictx)
}
}
/* Handle CSI graphics SM. */
static void
input_csi_dispatch_sm_graphics(__unused struct input_ctx *ictx)
{
#ifdef ENABLE_SIXEL
int n, m, o;
if (ictx->param_list_len > 3)
return;
n = input_get(ictx, 0, 0, 0);
m = input_get(ictx, 1, 0, 0);
o = input_get(ictx, 2, 0, 0);
if (n == 1 && (m == 1 || m == 2 || m == 4))
input_reply(ictx, "\033[?%d;0;%uS", n, SIXEL_COLOUR_REGISTERS);
else
input_reply(ictx, "\033[?%d;3;%dS", n, o);
#endif
}
/* Handle CSI window operations. */
static void
input_csi_dispatch_winops(struct input_ctx *ictx)
@ -1838,9 +1934,13 @@ input_csi_dispatch_winops(struct input_ctx *ictx)
struct screen_write_ctx *sctx = &ictx->ctx;
struct screen *s = sctx->s;
struct window_pane *wp = ictx->wp;
struct window *w = NULL;
u_int x = screen_size_x(s), y = screen_size_y(s);
int n, m;
if (wp != NULL)
w = wp->window;
m = 0;
while ((n = input_get(ictx, m, 0, -1)) != -1) {
switch (n) {
@ -1851,8 +1951,6 @@ input_csi_dispatch_winops(struct input_ctx *ictx)
case 7:
case 11:
case 13:
case 14:
case 19:
case 20:
case 21:
case 24:
@ -1870,6 +1968,30 @@ input_csi_dispatch_winops(struct input_ctx *ictx)
if (input_get(ictx, m, 0, -1) == -1)
return;
break;
case 14:
if (w == NULL)
break;
input_reply(ictx, "\033[4;%u;%ut", y * w->ypixel,
x * w->xpixel);
break;
case 15:
if (w == NULL)
break;
input_reply(ictx, "\033[5;%u;%ut", y * w->ypixel,
x * w->xpixel);
break;
case 16:
if (w == NULL)
break;
input_reply(ictx, "\033[6;%u;%ut", w->ypixel,
w->xpixel);
break;
case 18:
input_reply(ictx, "\033[8;%u;%ut", y, x);
break;
case 19:
input_reply(ictx, "\033[9;%u;%ut", y, x);
break;
case 22:
m++;
switch (input_get(ictx, m, 0, -1)) {
@ -1892,14 +2014,11 @@ input_csi_dispatch_winops(struct input_ctx *ictx)
if (wp == NULL)
break;
notify_pane("pane-title-changed", wp);
server_redraw_window_borders(wp->window);
server_status_window(wp->window);
server_redraw_window_borders(w);
server_status_window(w);
break;
}
break;
case 18:
input_reply(ictx, "\033[8;%u;%ut", x, y);
break;
default:
log_debug("%s: unknown '%c'", __func__, ictx->ch);
break;
@ -2070,7 +2189,7 @@ static void
input_csi_dispatch_sgr(struct input_ctx *ictx)
{
struct grid_cell *gc = &ictx->cell.cell;
u_int i;
u_int i, link;
int n;
if (ictx->param_list_len == 0) {
@ -2102,7 +2221,9 @@ input_csi_dispatch_sgr(struct input_ctx *ictx)
switch (n) {
case 0:
link = gc->link;
memcpy(gc, &grid_default_cell, sizeof *gc);
gc->link = link;
break;
case 1:
gc->attr |= GRID_ATTR_BRIGHT;
@ -2188,7 +2309,7 @@ input_csi_dispatch_sgr(struct input_ctx *ictx)
gc->attr &= ~GRID_ATTR_OVERLINE;
break;
case 59:
gc->us = 0;
gc->us = 8;
break;
case 90:
case 91:
@ -2233,7 +2354,7 @@ input_enter_dcs(struct input_ctx *ictx)
input_clear(ictx);
input_start_timer(ictx);
ictx->last = -1;
ictx->flags &= ~INPUT_LAST;
}
/* DCS terminator (ST) received. */
@ -2246,17 +2367,44 @@ input_dcs_dispatch(struct input_ctx *ictx)
size_t len = ictx->input_len;
const char prefix[] = "tmux;";
const u_int prefixlen = (sizeof prefix) - 1;
long long allow_passthrough = 0;
#ifdef ENABLE_SIXEL
struct window *w;
struct sixel_image *si;
int p2;
#endif
if (wp == NULL)
return (0);
if (ictx->flags & INPUT_DISCARD)
if (ictx->flags & INPUT_DISCARD) {
log_debug("%s: %zu bytes (discard)", __func__, len);
return (0);
if (!options_get_number(ictx->wp->options, "allow-passthrough"))
}
#ifdef ENABLE_SIXEL
w = wp->window;
if (buf[0] == 'q' && ictx->interm_len == 0) {
if (input_split(ictx) != 0)
return (0);
p2 = input_get(ictx, 1, 0, 0);
if (p2 == -1)
p2 = 0;
si = sixel_parse(buf, len, p2, w->xpixel, w->ypixel);
if (si != NULL)
screen_write_sixelimage(sctx, si, ictx->cell.cell.bg);
}
#endif
allow_passthrough = options_get_number(wp->options, "allow-passthrough");
if (!allow_passthrough)
return (0);
log_debug("%s: \"%s\"", __func__, buf);
if (len >= prefixlen && strncmp(buf, prefix, prefixlen) == 0)
screen_write_rawstring(sctx, buf + prefixlen, len - prefixlen);
if (len >= prefixlen && strncmp(buf, prefix, prefixlen) == 0) {
screen_write_rawstring(sctx, buf + prefixlen, len - prefixlen,
allow_passthrough == 2);
}
return (0);
}
@ -2269,7 +2417,7 @@ input_enter_osc(struct input_ctx *ictx)
input_clear(ictx);
input_start_timer(ictx);
ictx->last = -1;
ictx->flags &= ~INPUT_LAST;
}
/* OSC terminator (ST) received. */
@ -2292,13 +2440,17 @@ input_exit_osc(struct input_ctx *ictx)
option = 0;
while (*p >= '0' && *p <= '9')
option = option * 10 + *p++ - '0';
if (*p != ';' && *p != '\0')
return;
if (*p == ';')
p++;
switch (option) {
case 0:
case 2:
if (screen_set_title(sctx->s, p) && wp != NULL) {
if (wp != NULL &&
options_get_number(wp->options, "allow-set-title") &&
screen_set_title(sctx->s, p)) {
notify_pane("pane-title-changed", wp);
server_redraw_window_borders(wp->window);
server_status_window(wp->window);
@ -2316,6 +2468,9 @@ input_exit_osc(struct input_ctx *ictx)
}
}
break;
case 8:
input_osc_8(ictx, p);
break;
case 10:
input_osc_10(ictx, p);
break;
@ -2340,6 +2495,9 @@ input_exit_osc(struct input_ctx *ictx)
case 112:
input_osc_112(ictx, p);
break;
case 133:
input_osc_133(ictx, p);
break;
default:
log_debug("%s: unknown '%u'", __func__, option);
break;
@ -2354,7 +2512,7 @@ input_enter_apc(struct input_ctx *ictx)
input_clear(ictx);
input_start_timer(ictx);
ictx->last = -1;
ictx->flags &= ~INPUT_LAST;
}
/* APC terminator (ST) received. */
@ -2383,7 +2541,7 @@ input_enter_rename(struct input_ctx *ictx)
input_clear(ictx);
input_start_timer(ictx);
ictx->last = -1;
ictx->flags &= ~INPUT_LAST;
}
/* Rename terminator (ST) received. */
@ -2427,7 +2585,7 @@ input_top_bit_set(struct input_ctx *ictx)
struct screen_write_ctx *sctx = &ictx->ctx;
struct utf8_data *ud = &ictx->utf8data;
ictx->last = -1;
ictx->flags &= ~INPUT_LAST;
if (!ictx->utf8started) {
if (utf8_open(ud, ictx->ch) != UTF8_MORE)
@ -2453,50 +2611,12 @@ input_top_bit_set(struct input_ctx *ictx)
utf8_copy(&ictx->cell.cell.data, ud);
screen_write_collect_add(sctx, &ictx->cell.cell);
utf8_copy(&ictx->last, &ictx->cell.cell.data);
ictx->flags |= INPUT_LAST;
return (0);
}
/* Parse colour from OSC. */
static int
input_osc_parse_colour(const char *p)
{
double c, m, y, k = 0;
u_int r, g, b;
size_t len = strlen(p);
int colour = -1;
char *copy;
if ((len == 12 && sscanf(p, "rgb:%02x/%02x/%02x", &r, &g, &b) == 3) ||
(len == 7 && sscanf(p, "#%02x%02x%02x", &r, &g, &b) == 3) ||
sscanf(p, "%d,%d,%d", &r, &g, &b) == 3)
colour = colour_join_rgb(r, g, b);
else if ((len == 18 &&
sscanf(p, "rgb:%04x/%04x/%04x", &r, &g, &b) == 3) ||
(len == 13 && sscanf(p, "#%04x%04x%04x", &r, &g, &b) == 3))
colour = colour_join_rgb(r >> 8, g >> 8, b >> 8);
else if ((sscanf(p, "cmyk:%lf/%lf/%lf/%lf", &c, &m, &y, &k) == 4 ||
sscanf(p, "cmy:%lf/%lf/%lf", &c, &m, &y) == 3) &&
c >= 0 && c <= 1 && m >= 0 && m <= 1 &&
y >= 0 && y <= 1 && k >= 0 && k <= 1) {
colour = colour_join_rgb(
(1 - c) * (1 - k) * 255,
(1 - m) * (1 - k) * 255,
(1 - y) * (1 - k) * 255);
} else {
while (len != 0 && *p == ' ') {
p++;
len--;
}
while (len != 0 && p[len - 1] == ' ')
len--;
copy = xstrndup(p, len);
colour = colour_byname(copy);
free(copy);
}
log_debug("%s: %s = %s", __func__, p, colour_tostring(colour));
return (colour);
}
/* Reply to a colour request. */
static void
input_osc_colour_reply(struct input_ctx *ictx, u_int n, int c)
@ -2545,7 +2665,7 @@ input_osc_4(struct input_ctx *ictx, const char *p)
input_osc_colour_reply(ictx, 4, c);
continue;
}
if ((c = input_osc_parse_colour(s)) == -1) {
if ((c = colour_parseX11(s)) == -1) {
s = next;
continue;
}
@ -2560,6 +2680,48 @@ input_osc_4(struct input_ctx *ictx, const char *p)
free(copy);
}
/* Handle the OSC 8 sequence for embedding hyperlinks. */
static void
input_osc_8(struct input_ctx *ictx, const char *p)
{
struct hyperlinks *hl = ictx->ctx.s->hyperlinks;
struct grid_cell *gc = &ictx->cell.cell;
const char *start, *end, *uri;
char *id = NULL;
for (start = p; (end = strpbrk(start, ":;")) != NULL; start = end + 1) {
if (end - start >= 4 && strncmp(start, "id=", 3) == 0) {
if (id != NULL)
goto bad;
id = xstrndup(start + 3, end - start - 3);
}
/* The first ; is the end of parameters and start of the URI. */
if (*end == ';')
break;
}
if (end == NULL || *end != ';')
goto bad;
uri = end + 1;
if (*uri == '\0') {
gc->link = 0;
free(id);
return;
}
gc->link = hyperlinks_put(hl, uri, id);
if (id == NULL)
log_debug("hyperlink (anonymous) %s = %u", uri, gc->link);
else
log_debug("hyperlink (id=%s) %s = %u", id, uri, gc->link);
free(id);
return;
bad:
log_debug("bad OSC 8 %s", p);
free(id);
}
/* Handle the OSC 10 sequence for setting and querying foreground colour. */
static void
input_osc_10(struct input_ctx *ictx, const char *p)
@ -2569,14 +2731,21 @@ input_osc_10(struct input_ctx *ictx, const char *p)
int c;
if (strcmp(p, "?") == 0) {
if (wp != NULL) {
if (wp == NULL)
return;
c = window_pane_get_fg_control_client(wp);
if (c == -1) {
tty_default_colours(&defaults, wp);
input_osc_colour_reply(ictx, 10, defaults.fg);
if (COLOUR_DEFAULT(defaults.fg))
c = window_pane_get_fg(wp);
else
c = defaults.fg;
}
input_osc_colour_reply(ictx, 10, c);
return;
}
if ((c = input_osc_parse_colour(p)) == -1) {
if ((c = colour_parseX11(p)) == -1) {
log_debug("bad OSC 10: %s", p);
return;
}
@ -2609,25 +2778,24 @@ static void
input_osc_11(struct input_ctx *ictx, const char *p)
{
struct window_pane *wp = ictx->wp;
struct grid_cell defaults;
int c;
if (strcmp(p, "?") == 0) {
if (wp != NULL) {
tty_default_colours(&defaults, wp);
input_osc_colour_reply(ictx, 11, defaults.bg);
}
if (wp == NULL)
return;
c = window_pane_get_bg(wp);
input_osc_colour_reply(ictx, 11, c);
return;
}
if ((c = input_osc_parse_colour(p)) == -1) {
if ((c = colour_parseX11(p)) == -1) {
log_debug("bad OSC 11: %s", p);
return;
}
if (ictx->palette != NULL) {
ictx->palette->bg = c;
if (wp != NULL)
wp->flags |= PANE_STYLECHANGED;
wp->flags |= (PANE_STYLECHANGED|PANE_THEMECHANGED);
screen_write_fullredraw(&ictx->ctx);
}
}
@ -2643,7 +2811,7 @@ input_osc_111(struct input_ctx *ictx, const char *p)
if (ictx->palette != NULL) {
ictx->palette->bg = 8;
if (wp != NULL)
wp->flags |= PANE_STYLECHANGED;
wp->flags |= (PANE_STYLECHANGED|PANE_THEMECHANGED);
screen_write_fullredraw(&ictx->ctx);
}
}
@ -2665,7 +2833,7 @@ input_osc_12(struct input_ctx *ictx, const char *p)
return;
}
if ((c = input_osc_parse_colour(p)) == -1) {
if ((c = colour_parseX11(p)) == -1) {
log_debug("bad OSC 12: %s", p);
return;
}
@ -2680,6 +2848,27 @@ input_osc_112(struct input_ctx *ictx, const char *p)
screen_set_cursor_colour(ictx->ctx.s, -1);
}
/* Handle the OSC 133 sequence. */
static void
input_osc_133(struct input_ctx *ictx, const char *p)
{
struct grid *gd = ictx->ctx.s->grid;
u_int line = ictx->ctx.s->cy + gd->hsize;
struct grid_line *gl;
if (line > gd->hsize + gd->sy - 1)
return;
gl = grid_get_line(gd, line);
switch (*p) {
case 'A':
gl->flags |= GRID_LINE_START_PROMPT;
break;
case 'C':
gl->flags |= GRID_LINE_START_OUTPUT;
break;
}
}
/* Handle the OSC 52 sequence for setting the clipboard. */
static void
@ -2693,6 +2882,9 @@ input_osc_52(struct input_ctx *ictx, const char *p)
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;
if (wp == NULL)
return;
@ -2707,6 +2899,12 @@ input_osc_52(struct input_ctx *ictx, const char *p)
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];
}
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);
@ -2728,7 +2926,7 @@ input_osc_52(struct input_ctx *ictx, const char *p)
}
screen_write_start_pane(&ctx, wp, NULL);
screen_write_setselection(&ctx, out, outlen);
screen_write_setselection(&ctx, flags, out, outlen);
screen_write_stop(&ctx);
notify_pane("pane-set-clipboard", wp);
@ -2777,9 +2975,11 @@ input_reply_clipboard(struct bufferevent *bev, const char *buf, size_t len,
const char *end)
{
char *out = NULL;
size_t outlen = 0;
int outlen = 0;
if (buf != NULL && len != 0) {
if (len >= ((size_t)INT_MAX * 3 / 4) - 1)
return;
outlen = 4 * ((len + 2) / 3) + 1;
out = xmalloc(outlen);
if ((outlen = b64_ntop(buf, len, out, outlen)) == -1) {
@ -2794,3 +2994,26 @@ input_reply_clipboard(struct bufferevent *bev, const char *buf, size_t len,
bufferevent_write(bev, end, strlen(end));
free(out);
}
/* Set input buffer size. */
void
input_set_buffer_size(size_t buffer_size)
{
log_debug("%s: %lu -> %lu", __func__, input_buffer_size, buffer_size);
input_buffer_size = buffer_size;
}
static void
input_report_current_theme(struct input_ctx *ictx)
{
switch (window_pane_get_theme(ictx->wp)) {
case THEME_DARK:
input_reply(ictx, "\033[?997;1n");
break;
case THEME_LIGHT:
input_reply(ictx, "\033[?997;2n");
break;
case THEME_UNKNOWN:
break;
}
}

63
job.c
View File

@ -69,27 +69,43 @@ static LIST_HEAD(joblist, job) all_jobs = LIST_HEAD_INITIALIZER(all_jobs);
/* Start a job running. */
struct job *
job_run(const char *cmd, int argc, char **argv, struct environ *e, struct session *s,
const char *cwd, job_update_cb updatecb, job_complete_cb completecb,
job_free_cb freecb, void *data, int flags, int sx, int sy)
job_run(const char *cmd, int argc, char **argv, struct environ *e,
struct session *s, const char *cwd, job_update_cb updatecb,
job_complete_cb completecb, job_free_cb freecb, void *data, int flags,
int sx, int sy)
{
struct job *job;
struct environ *env;
pid_t pid;
int nullfd, out[2], master;
const char *home;
int nullfd, out[2], master, do_close = 1;
const char *home, *shell;
sigset_t set, oldset;
struct winsize ws;
char **argvp, tty[TTY_NAME_MAX];
char **argvp, tty[TTY_NAME_MAX], *argv0;
struct options *oo;
/*
* Do not set TERM during .tmux.conf, it is nice to be able to use
* if-shell to decide on default-terminal based on outside TERM.
* Do not set TERM during .tmux.conf (second argument here), it is nice
* to be able to use if-shell to decide on default-terminal based on
* outside TERM.
*/
env = environ_for_session(s, !cfg_finished);
if (e != NULL)
environ_copy(e, env);
if (~flags & JOB_DEFAULTSHELL)
shell = _PATH_BSHELL;
else {
if (s != NULL)
oo = s->options;
else
oo = global_s_options;
shell = options_get_string(oo, "default-shell");
if (!checkshell(shell))
shell = _PATH_BSHELL;
}
argv0 = shell_argv0(shell, 0);
sigfillset(&set);
sigprocmask(SIG_BLOCK, &set, &oldset);
@ -105,10 +121,11 @@ job_run(const char *cmd, int argc, char **argv, struct environ *e, struct sessio
}
if (cmd == NULL) {
cmd_log_argv(argc, argv, "%s:", __func__);
log_debug("%s: cwd=%s", __func__, cwd == NULL ? "" : cwd);
log_debug("%s: cwd=%s, shell=%s", __func__,
cwd == NULL ? "" : cwd, shell);
} else {
log_debug("%s: cmd=%s, cwd=%s", __func__, cmd,
cwd == NULL ? "" : cwd);
log_debug("%s: cmd=%s, cwd=%s, shell=%s", __func__, cmd,
cwd == NULL ? "" : cwd, shell);
}
switch (pid) {
@ -133,12 +150,15 @@ job_run(const char *cmd, int argc, char **argv, struct environ *e, struct sessio
if (~flags & JOB_PTY) {
if (dup2(out[1], STDIN_FILENO) == -1)
fatal("dup2 failed");
do_close = do_close && out[1] != STDIN_FILENO;
if (dup2(out[1], STDOUT_FILENO) == -1)
fatal("dup2 failed");
if (out[1] != STDIN_FILENO && out[1] != STDOUT_FILENO)
close(out[1]);
close(out[0]);
do_close = do_close && out[1] != STDOUT_FILENO;
if (flags & JOB_SHOWSTDERR) {
if (dup2(out[1], STDERR_FILENO) == -1)
fatal("dup2 failed");
do_close = do_close && out[1] != STDERR_FILENO;
} else {
nullfd = open(_PATH_DEVNULL, O_RDWR);
if (nullfd == -1)
fatal("open failed");
@ -147,10 +167,16 @@ job_run(const char *cmd, int argc, char **argv, struct environ *e, struct sessio
if (nullfd != STDERR_FILENO)
close(nullfd);
}
if (do_close)
close(out[1]);
close(out[0]);
}
closefrom(STDERR_FILENO + 1);
if (cmd != NULL) {
execl(_PATH_BSHELL, "sh", "-c", cmd, (char *) NULL);
if (flags & JOB_DEFAULTSHELL)
setenv("SHELL", shell, 1);
execl(shell, argv0, "-c", cmd, (char *)NULL);
fatal("execl failed");
} else {
argvp = cmd_copy_argv(argc, argv);
@ -161,8 +187,9 @@ job_run(const char *cmd, int argc, char **argv, struct environ *e, struct sessio
sigprocmask(SIG_SETMASK, &oldset, NULL);
environ_free(env);
free(argv0);
job = xmalloc(sizeof *job);
job = xcalloc(1, sizeof *job);
job->state = JOB_RUNNING;
job->flags = flags;
@ -171,6 +198,7 @@ job_run(const char *cmd, int argc, char **argv, struct environ *e, struct sessio
else
job->cmd = cmd_stringify_argv(argc, argv);
job->pid = pid;
if (flags & JOB_PTY)
strlcpy(job->tty, tty, sizeof job->tty);
job->status = 0;
@ -200,6 +228,7 @@ job_run(const char *cmd, int argc, char **argv, struct environ *e, struct sessio
fail:
sigprocmask(SIG_SETMASK, &oldset, NULL);
environ_free(env);
free(argv0);
return (NULL);
}

View File

@ -49,11 +49,14 @@
" '#{?#{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}" \
" ''" \
" '#{?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,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}\"}" \
" '#{?mouse_line,Copy Line,}' 'l' {copy-mode -q; set-buffer -- \"#{q:mouse_line}\"}" \
" ''" \
" '#{?mouse_hyperlink,Type #[underscore]#{=/9/...:mouse_hyperlink},}' 'C-h' {copy-mode -q; send-keys -l -- \"#{q:mouse_hyperlink}\"}" \
" '#{?mouse_hyperlink,Copy #[underscore]#{=/9/...:mouse_hyperlink},}' 'h' {copy-mode -q; set-buffer -- \"#{q:mouse_hyperlink}\"}" \
" ''" \
" 'Horizontal Split' 'h' {split-window -h}" \
" 'Vertical Split' 'v' {split-window -v}" \
" ''" \
@ -194,11 +197,12 @@ key_bindings_add(const char *name, key_code key, const char *note, int repeat,
bd = key_bindings_get(table, key & ~KEYC_MASK_FLAGS);
if (cmdlist == NULL) {
if (bd != NULL) {
if (note != NULL) {
free((void *)bd->note);
if (note != NULL)
bd->note = xstrdup(note);
else
bd->note = NULL;
}
if (repeat)
bd->flags |= KEY_BINDING_REPEAT;
}
return;
}
@ -341,7 +345,7 @@ key_bindings_init_done(__unused struct cmdq_item *item, __unused void *data)
void
key_bindings_init(void)
{
static const char *defaults[] = {
static const char *const defaults[] = {
/* Prefix keys. */
"bind -N 'Send the prefix key' C-b { send-prefix }",
"bind -N 'Rotate through the panes' C-o { rotate-window }",
@ -374,7 +378,7 @@ key_bindings_init(void)
"bind -N 'Move to the previously active pane' \\; { last-pane }",
"bind -N 'Choose a paste buffer from a list' = { choose-buffer -Z }",
"bind -N 'List key bindings' ? { list-keys -N }",
"bind -N 'Choose a client from a list' D { choose-client -Z }",
"bind -N 'Choose and detach a client from a list' D { choose-client -Z }",
"bind -N 'Spread panes out evenly' E { select-layout -E }",
"bind -N 'Switch to the last client' L { switch-client -l }",
"bind -N 'Clear the marked pane' M { select-pane -M }",
@ -410,6 +414,8 @@ key_bindings_init(void)
"bind -N 'Set the main-horizontal layout' M-3 { select-layout main-horizontal }",
"bind -N 'Set the main-vertical layout' M-4 { select-layout main-vertical }",
"bind -N 'Select the tiled layout' M-5 { select-layout tiled }",
"bind -N 'Set the main-horizontal-mirrored layout' M-6 { select-layout main-horizontal-mirrored }",
"bind -N 'Set the main-vertical-mirrored layout' M-7 { select-layout main-vertical-mirrored }",
"bind -N 'Select the next window with an alert' M-n { next-window -a }",
"bind -N 'Rotate through the panes in reverse' M-o { rotate-window -D }",
"bind -N 'Select the previous window with an alert' M-p { previous-window -a }",
@ -428,8 +434,9 @@ key_bindings_init(void)
"bind -N 'Resize the pane right' -r C-Right { resize-pane -R }",
/* Menu keys */
"bind < { display-menu -xW -yW -T '#[align=centre]#{window_index}:#{window_name}' " DEFAULT_WINDOW_MENU " }",
"bind > { display-menu -xP -yP -T '#[align=centre]#{pane_index} (#{pane_id})' " DEFAULT_PANE_MENU " }",
"bind -N 'Display window menu' < { display-menu -xW -yW -T '#[align=centre]#{window_index}:#{window_name}' " DEFAULT_WINDOW_MENU " }",
"bind -N 'Display pane menu' > { display-menu -xP -yP -T '#[align=centre]#{pane_index} (#{pane_id})' " DEFAULT_PANE_MENU " }",
/* Mouse button 1 down on pane. */
"bind -n MouseDown1Pane { select-pane -t=; send -M }",
@ -463,14 +470,21 @@ key_bindings_init(void)
/* Mouse button 3 down on status left. */
"bind -n MouseDown3StatusLeft { display-menu -t= -xM -yW -T '#[align=centre]#{session_name}' " DEFAULT_SESSION_MENU " }",
"bind -n M-MouseDown3StatusLeft { display-menu -t= -xM -yW -T '#[align=centre]#{session_name}' " DEFAULT_SESSION_MENU " }",
/* Mouse button 3 down on status line. */
"bind -n MouseDown3Status { display-menu -t= -xW -yW -T '#[align=centre]#{window_index}:#{window_name}' " DEFAULT_WINDOW_MENU "}",
"bind -n M-MouseDown3Status { display-menu -t= -xW -yW -T '#[align=centre]#{window_index}:#{window_name}' " DEFAULT_WINDOW_MENU "}",
/* Mouse button 3 down on pane. */
"bind -n MouseDown3Pane { if -Ft= '#{||:#{mouse_any_flag},#{&&:#{pane_in_mode},#{?#{m/r:(copy|view)-mode,#{pane_mode}},0,1}}}' { select-pane -t=; send -M } { display-menu -t= -xM -yM -T '#[align=centre]#{pane_index} (#{pane_id})' " DEFAULT_PANE_MENU " } }",
"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 }",
/* Copy mode (emacs) keys. */
"bind -Tcopy-mode C-Space { send -X begin-selection }",
"bind -Tcopy-mode C-a { send -X start-of-line }",
@ -482,26 +496,26 @@ key_bindings_init(void)
"bind -Tcopy-mode C-k { send -X copy-pipe-end-of-line-and-cancel }",
"bind -Tcopy-mode C-n { send -X cursor-down }",
"bind -Tcopy-mode C-p { send -X cursor-up }",
"bind -Tcopy-mode C-r { command-prompt -T search -ip'(search up)' -I'#{pane_search_string}' { send -X search-backward-incremental '%%' } }",
"bind -Tcopy-mode C-s { command-prompt -T search -ip'(search down)' -I'#{pane_search_string}' { send -X search-forward-incremental '%%' } }",
"bind -Tcopy-mode C-r { command-prompt -T search -ip'(search up)' -I'#{pane_search_string}' { send -X search-backward-incremental -- '%%' } }",
"bind -Tcopy-mode C-s { command-prompt -T search -ip'(search down)' -I'#{pane_search_string}' { send -X search-forward-incremental -- '%%' } }",
"bind -Tcopy-mode C-v { send -X page-down }",
"bind -Tcopy-mode C-w { send -X copy-pipe-and-cancel }",
"bind -Tcopy-mode Escape { send -X cancel }",
"bind -Tcopy-mode Space { send -X page-down }",
"bind -Tcopy-mode , { send -X jump-reverse }",
"bind -Tcopy-mode \\; { send -X jump-again }",
"bind -Tcopy-mode F { command-prompt -1p'(jump backward)' { send -X jump-backward '%%' } }",
"bind -Tcopy-mode F { command-prompt -1p'(jump backward)' { send -X jump-backward -- '%%' } }",
"bind -Tcopy-mode N { send -X search-reverse }",
"bind -Tcopy-mode P { send -X toggle-position }",
"bind -Tcopy-mode R { send -X rectangle-toggle }",
"bind -Tcopy-mode T { command-prompt -1p'(jump to backward)' { send -X jump-to-backward '%%' } }",
"bind -Tcopy-mode T { command-prompt -1p'(jump to backward)' { send -X jump-to-backward -- '%%' } }",
"bind -Tcopy-mode X { send -X set-mark }",
"bind -Tcopy-mode f { command-prompt -1p'(jump forward)' { send -X jump-forward '%%' } }",
"bind -Tcopy-mode g { command-prompt -p'(goto line)' { send -X goto-line '%%' } }",
"bind -Tcopy-mode f { command-prompt -1p'(jump forward)' { send -X jump-forward -- '%%' } }",
"bind -Tcopy-mode g { command-prompt -p'(goto line)' { send -X goto-line -- '%%' } }",
"bind -Tcopy-mode n { send -X search-again }",
"bind -Tcopy-mode q { send -X cancel }",
"bind -Tcopy-mode r { send -X refresh-from-pane }",
"bind -Tcopy-mode t { command-prompt -1p'(jump to forward)' { send -X jump-to-forward '%%' } }",
"bind -Tcopy-mode t { command-prompt -1p'(jump to forward)' { send -X jump-to-forward -- '%%' } }",
"bind -Tcopy-mode Home { send -X start-of-line }",
"bind -Tcopy-mode End { send -X end-of-line }",
"bind -Tcopy-mode MouseDown1Pane select-pane",
@ -546,8 +560,8 @@ key_bindings_init(void)
"bind -Tcopy-mode C-Down { send -X scroll-down }",
/* Copy mode (vi) keys. */
"bind -Tcopy-mode-vi '#' { send -FX search-backward '#{copy_cursor_word}' }",
"bind -Tcopy-mode-vi * { send -FX search-forward '#{copy_cursor_word}' }",
"bind -Tcopy-mode-vi '#' { send -FX search-backward -- '#{copy_cursor_word}' }",
"bind -Tcopy-mode-vi * { send -FX search-forward -- '#{copy_cursor_word}' }",
"bind -Tcopy-mode-vi C-c { send -X cancel }",
"bind -Tcopy-mode-vi C-d { send -X halfpage-down }",
"bind -Tcopy-mode-vi C-e { send -X scroll-down }",
@ -563,7 +577,7 @@ key_bindings_init(void)
"bind -Tcopy-mode-vi Space { send -X begin-selection }",
"bind -Tcopy-mode-vi '$' { send -X end-of-line }",
"bind -Tcopy-mode-vi , { send -X jump-reverse }",
"bind -Tcopy-mode-vi / { command-prompt -T search -p'(search down)' { send -X search-forward '%%' } }",
"bind -Tcopy-mode-vi / { command-prompt -T search -p'(search down)' { send -X search-forward -- '%%' } }",
"bind -Tcopy-mode-vi 0 { send -X start-of-line }",
"bind -Tcopy-mode-vi 1 { command-prompt -Np'(repeat)' -I1 { send -N '%%' } }",
"bind -Tcopy-mode-vi 2 { command-prompt -Np'(repeat)' -I2 { send -N '%%' } }",
@ -574,14 +588,14 @@ key_bindings_init(void)
"bind -Tcopy-mode-vi 7 { command-prompt -Np'(repeat)' -I7 { send -N '%%' } }",
"bind -Tcopy-mode-vi 8 { command-prompt -Np'(repeat)' -I8 { send -N '%%' } }",
"bind -Tcopy-mode-vi 9 { command-prompt -Np'(repeat)' -I9 { send -N '%%' } }",
"bind -Tcopy-mode-vi : { command-prompt -p'(goto line)' { send -X goto-line '%%' } }",
"bind -Tcopy-mode-vi : { command-prompt -p'(goto line)' { send -X goto-line -- '%%' } }",
"bind -Tcopy-mode-vi \\; { send -X jump-again }",
"bind -Tcopy-mode-vi ? { command-prompt -T search -p'(search up)' { send -X search-backward '%%' } }",
"bind -Tcopy-mode-vi ? { command-prompt -T search -p'(search up)' { send -X search-backward -- '%%' } }",
"bind -Tcopy-mode-vi A { send -X append-selection-and-cancel }",
"bind -Tcopy-mode-vi B { send -X previous-space }",
"bind -Tcopy-mode-vi D { send -X copy-pipe-end-of-line-and-cancel }",
"bind -Tcopy-mode-vi E { send -X next-space-end }",
"bind -Tcopy-mode-vi F { command-prompt -1p'(jump backward)' { send -X jump-backward '%%' } }",
"bind -Tcopy-mode-vi F { command-prompt -1p'(jump backward)' { send -X jump-backward -- '%%' } }",
"bind -Tcopy-mode-vi G { send -X history-bottom }",
"bind -Tcopy-mode-vi H { send -X top-line }",
"bind -Tcopy-mode-vi J { send -X scroll-down }",
@ -590,29 +604,32 @@ key_bindings_init(void)
"bind -Tcopy-mode-vi M { send -X middle-line }",
"bind -Tcopy-mode-vi N { send -X search-reverse }",
"bind -Tcopy-mode-vi P { send -X toggle-position }",
"bind -Tcopy-mode-vi T { command-prompt -1p'(jump to backward)' { send -X jump-to-backward '%%' } }",
"bind -Tcopy-mode-vi T { command-prompt -1p'(jump to backward)' { send -X jump-to-backward -- '%%' } }",
"bind -Tcopy-mode-vi V { send -X select-line }",
"bind -Tcopy-mode-vi W { send -X next-space }",
"bind -Tcopy-mode-vi X { send -X set-mark }",
"bind -Tcopy-mode-vi ^ { send -X back-to-indentation }",
"bind -Tcopy-mode-vi b { send -X previous-word }",
"bind -Tcopy-mode-vi e { send -X next-word-end }",
"bind -Tcopy-mode-vi f { command-prompt -1p'(jump forward)' { send -X jump-forward '%%' } }",
"bind -Tcopy-mode-vi f { command-prompt -1p'(jump forward)' { send -X jump-forward -- '%%' } }",
"bind -Tcopy-mode-vi g { send -X history-top }",
"bind -Tcopy-mode-vi h { send -X cursor-left }",
"bind -Tcopy-mode-vi j { send -X cursor-down }",
"bind -Tcopy-mode-vi k { send -X cursor-up }",
"bind -Tcopy-mode-vi z { send -X scroll-middle }",
"bind -Tcopy-mode-vi l { send -X cursor-right }",
"bind -Tcopy-mode-vi n { send -X search-again }",
"bind -Tcopy-mode-vi o { send -X other-end }",
"bind -Tcopy-mode-vi q { send -X cancel }",
"bind -Tcopy-mode-vi r { send -X refresh-from-pane }",
"bind -Tcopy-mode-vi t { command-prompt -1p'(jump to forward)' { send -X jump-to-forward '%%' } }",
"bind -Tcopy-mode-vi t { command-prompt -1p'(jump to forward)' { send -X jump-to-forward -- '%%' } }",
"bind -Tcopy-mode-vi v { send -X rectangle-toggle }",
"bind -Tcopy-mode-vi w { send -X next-word }",
"bind -Tcopy-mode-vi '{' { send -X previous-paragraph }",
"bind -Tcopy-mode-vi '}' { send -X next-paragraph }",
"bind -Tcopy-mode-vi % { send -X next-matching-bracket }",
"bind -Tcopy-mode-vi Home { send -X start-of-line }",
"bind -Tcopy-mode-vi End { send -X end-of-line }",
"bind -Tcopy-mode-vi MouseDown1Pane { select-pane }",
"bind -Tcopy-mode-vi MouseDrag1Pane { select-pane; send -X begin-selection }",
"bind -Tcopy-mode-vi MouseDragEnd1Pane { send -X copy-pipe-and-cancel }",

View File

@ -18,6 +18,7 @@
#include <sys/types.h>
#include <ctype.h>
#include <stdlib.h>
#include <string.h>
#include <wchar.h>
@ -56,12 +57,47 @@ static const struct {
{ "PPage", KEYC_PPAGE|KEYC_IMPLIED_META },
{ "PageUp", KEYC_PPAGE|KEYC_IMPLIED_META },
{ "PgUp", KEYC_PPAGE|KEYC_IMPLIED_META },
{ "Tab", '\011' },
{ "BTab", KEYC_BTAB },
{ "Space", ' ' },
{ "BSpace", KEYC_BSPACE },
{ "Enter", '\r' },
{ "Escape", '\033' },
/*
* C0 control characters, with the exception of Tab, Enter,
* and Esc, should never appear as keys. We still render them,
* so to be able to spot them in logs in case of an abnormality.
*/
{ "[NUL]", C0_NUL },
{ "[SOH]", C0_SOH },
{ "[STX]", C0_STX },
{ "[ETX]", C0_ETX },
{ "[EOT]", C0_EOT },
{ "[ENQ]", C0_ENQ },
{ "[ASC]", C0_ASC },
{ "[BEL]", C0_BEL },
{ "[BS]", C0_BS },
{ "Tab", C0_HT },
{ "[LF]", C0_LF },
{ "[VT]", C0_VT },
{ "[FF]", C0_FF },
{ "Enter", C0_CR },
{ "[SO]", C0_SO },
{ "[SI]", C0_SI },
{ "[DLE]", C0_DLE },
{ "[DC1]", C0_DC1 },
{ "[DC2]", C0_DC2 },
{ "[DC3]", C0_DC3 },
{ "[DC4]", C0_DC4 },
{ "[NAK]", C0_NAK },
{ "[SYN]", C0_SYN },
{ "[ETB]", C0_ETB },
{ "[CAN]", C0_CAN },
{ "[EM]", C0_EM },
{ "[SUB]", C0_SUB },
{ "Escape", C0_ESC },
{ "[FS]", C0_FS },
{ "[GS]", C0_GS },
{ "[RS]", C0_RS },
{ "[US]", C0_US },
/* Arrow keys. */
{ "Up", KEYC_UP|KEYC_CURSOR|KEYC_IMPLIED_META },
@ -206,8 +242,7 @@ key_string_get_modifiers(const char **string)
key_code
key_string_lookup_string(const char *string)
{
static const char *other = "!#()+,-.0123456789:;<=>'\r\t\177`/";
key_code key, modifiers;
key_code key, modifiers = 0;
u_int u, i;
struct utf8_data ud, *udp;
enum utf8_state more;
@ -244,12 +279,15 @@ key_string_lookup_string(const char *string)
return (uc);
}
/* Check for modifiers. */
modifiers = 0;
/* Check for short Ctrl key. */
if (string[0] == '^' && string[1] != '\0') {
if (string[2] == '\0')
return (tolower((u_char)string[1])|KEYC_CTRL);
modifiers |= KEYC_CTRL;
string++;
}
/* Check for modifiers. */
modifiers |= key_string_get_modifiers(&string);
if (string == NULL || string[0] == '\0')
return (KEYC_UNKNOWN);
@ -281,26 +319,6 @@ key_string_lookup_string(const char *string)
key &= ~KEYC_IMPLIED_META;
}
/* Convert the standard control keys. */
if (key <= 127 &&
(modifiers & KEYC_CTRL) &&
strchr(other, key) == NULL &&
key != 9 &&
key != 13 &&
key != 27) {
if (key >= 97 && key <= 122)
key -= 96;
else if (key >= 64 && key <= 95)
key -= 64;
else if (key == 32)
key = 0;
else if (key == 63)
key = 127;
else
return (KEYC_UNKNOWN);
modifiers &= ~KEYC_CTRL;
}
return (key|modifiers);
}
@ -324,10 +342,6 @@ key_string_lookup_key(key_code key, int with_flags)
goto out;
}
/* Display C-@ as C-Space. */
if ((key & (KEYC_MASK_KEY|KEYC_MASK_MODIFIERS)) == 0)
key = ' '|KEYC_CTRL;
/* Fill in the modifiers. */
if (key & KEYC_CTRL)
strlcat(out, "C-", sizeof out);
@ -396,7 +410,7 @@ key_string_lookup_key(key_code key, int with_flags)
s = "MouseMoveBorder";
goto append;
}
if (key >= KEYC_USER && key < KEYC_USER + KEYC_NUSER) {
if (key >= KEYC_USER && key < KEYC_USER_END) {
snprintf(tmp, sizeof tmp, "User%u", (u_int)(key - KEYC_USER));
strlcat(out, tmp, sizeof out);
goto out;
@ -427,13 +441,8 @@ key_string_lookup_key(key_code key, int with_flags)
goto out;
}
/* Check for standard or control key. */
if (key <= 32) {
if (key == 0 || key > 26)
xsnprintf(tmp, sizeof tmp, "C-%c", (int)(64 + key));
else
xsnprintf(tmp, sizeof tmp, "C-%c", (int)(96 + key));
} else if (key >= 32 && key <= 126) {
/* Printable ASCII keys. */
if (key > 32 && key <= 126) {
tmp[0] = key;
tmp[1] = '\0';
} else if (key == 127)
@ -460,6 +469,8 @@ out:
strlcat(out, "I", sizeof out);
if (saved & KEYC_BUILD_MODIFIERS)
strlcat(out, "B", sizeof out);
if (saved & KEYC_SENT)
strlcat(out, "S", sizeof out);
strlcat(out, "]", sizeof out);
}
return (out);

View File

@ -162,8 +162,10 @@ layout_parse(struct window *w, const char *layout, char **cause)
u_short csum;
/* Check validity. */
if (sscanf(layout, "%hx,", &csum) != 1)
if (sscanf(layout, "%hx,", &csum) != 1) {
*cause = xstrdup("invalid layout");
return (-1);
}
layout += 5;
if (csum != layout_checksum(layout)) {
*cause = xstrdup("invalid layout");
@ -228,7 +230,7 @@ layout_parse(struct window *w, const char *layout, char **cause)
/* Check the new layout. */
if (!layout_check(lc)) {
*cause = xstrdup("size mismatch after applying layout");
return (-1);
goto fail;
}
/* Resize to the layout size. */

View File

@ -31,7 +31,9 @@
static void layout_set_even_h(struct window *);
static void layout_set_even_v(struct window *);
static void layout_set_main_h(struct window *);
static void layout_set_main_h_mirrored(struct window *);
static void layout_set_main_v(struct window *);
static void layout_set_main_v_mirrored(struct window *);
static void layout_set_tiled(struct window *);
static const struct {
@ -41,7 +43,9 @@ static const struct {
{ "even-horizontal", layout_set_even_h },
{ "even-vertical", layout_set_even_v },
{ "main-horizontal", layout_set_main_h },
{ "main-horizontal-mirrored", layout_set_main_h_mirrored },
{ "main-vertical", layout_set_main_v },
{ "main-vertical-mirrored", layout_set_main_v_mirrored },
{ "tiled", layout_set_tiled },
};
@ -51,6 +55,10 @@ layout_set_lookup(const char *name)
u_int i;
int matched = -1;
for (i = 0; i < nitems(layout_sets); i++) {
if (strcmp(layout_sets[i].name, name) == 0)
return (i);
}
for (i = 0; i < nitems(layout_sets); i++) {
if (strncmp(layout_sets[i].name, name, strlen(name)) == 0) {
if (matched != -1) /* ambiguous */
@ -279,6 +287,104 @@ layout_set_main_h(struct window *w)
server_redraw_window(w);
}
static void
layout_set_main_h_mirrored(struct window *w)
{
struct window_pane *wp;
struct layout_cell *lc, *lcmain, *lcother, *lcchild;
u_int n, mainh, otherh, sx, sy;
char *cause;
const char *s;
layout_print_cell(w->layout_root, __func__, 1);
/* Get number of panes. */
n = window_count_panes(w);
if (n <= 1)
return;
n--; /* take off main pane */
/* Find available height - take off one line for the border. */
sy = w->sy - 1;
/* Get the main pane height. */
s = options_get_string(w->options, "main-pane-height");
mainh = args_string_percentage(s, 0, sy, sy, &cause);
if (cause != NULL) {
mainh = 24;
free(cause);
}
/* Work out the other pane height. */
if (mainh + PANE_MINIMUM >= sy) {
if (sy <= PANE_MINIMUM + PANE_MINIMUM)
mainh = PANE_MINIMUM;
else
mainh = sy - PANE_MINIMUM;
otherh = PANE_MINIMUM;
} else {
s = options_get_string(w->options, "other-pane-height");
otherh = args_string_percentage(s, 0, sy, sy, &cause);
if (cause != NULL || otherh == 0) {
otherh = sy - mainh;
free(cause);
} else if (otherh > sy || sy - otherh < mainh)
otherh = sy - mainh;
else
mainh = sy - otherh;
}
/* Work out what width is needed. */
sx = (n * (PANE_MINIMUM + 1)) - 1;
if (sx < w->sx)
sx = w->sx;
/* Free old tree and create a new root. */
layout_free(w);
lc = w->layout_root = layout_create_cell(NULL);
layout_set_size(lc, sx, mainh + otherh + 1, 0, 0);
layout_make_node(lc, LAYOUT_TOPBOTTOM);
/* Create the other pane. */
lcother = layout_create_cell(lc);
layout_set_size(lcother, sx, otherh, 0, 0);
if (n == 1) {
wp = TAILQ_NEXT(TAILQ_FIRST(&w->panes), entry);
layout_make_leaf(lcother, wp);
TAILQ_INSERT_TAIL(&lc->cells, lcother, entry);
} else {
layout_make_node(lcother, LAYOUT_LEFTRIGHT);
TAILQ_INSERT_TAIL(&lc->cells, lcother, entry);
/* Add the remaining panes as children. */
TAILQ_FOREACH(wp, &w->panes, entry) {
if (wp == TAILQ_FIRST(&w->panes))
continue;
lcchild = layout_create_cell(lcother);
layout_set_size(lcchild, PANE_MINIMUM, otherh, 0, 0);
layout_make_leaf(lcchild, wp);
TAILQ_INSERT_TAIL(&lcother->cells, lcchild, entry);
}
layout_spread_cell(w, lcother);
}
/* Create the main pane. */
lcmain = layout_create_cell(lc);
layout_set_size(lcmain, sx, mainh, 0, 0);
layout_make_leaf(lcmain, TAILQ_FIRST(&w->panes));
TAILQ_INSERT_TAIL(&lc->cells, lcmain, entry);
/* Fix cell offsets. */
layout_fix_offsets(w);
layout_fix_panes(w, NULL);
layout_print_cell(w->layout_root, __func__, 1);
window_resize(w, lc->sx, lc->sy, -1, -1);
notify_window("window-layout-changed", w);
server_redraw_window(w);
}
static void
layout_set_main_v(struct window *w)
{
@ -377,6 +483,104 @@ layout_set_main_v(struct window *w)
server_redraw_window(w);
}
static void
layout_set_main_v_mirrored(struct window *w)
{
struct window_pane *wp;
struct layout_cell *lc, *lcmain, *lcother, *lcchild;
u_int n, mainw, otherw, sx, sy;
char *cause;
const char *s;
layout_print_cell(w->layout_root, __func__, 1);
/* Get number of panes. */
n = window_count_panes(w);
if (n <= 1)
return;
n--; /* take off main pane */
/* Find available width - take off one line for the border. */
sx = w->sx - 1;
/* Get the main pane width. */
s = options_get_string(w->options, "main-pane-width");
mainw = args_string_percentage(s, 0, sx, sx, &cause);
if (cause != NULL) {
mainw = 80;
free(cause);
}
/* Work out the other pane width. */
if (mainw + PANE_MINIMUM >= sx) {
if (sx <= PANE_MINIMUM + PANE_MINIMUM)
mainw = PANE_MINIMUM;
else
mainw = sx - PANE_MINIMUM;
otherw = PANE_MINIMUM;
} else {
s = options_get_string(w->options, "other-pane-width");
otherw = args_string_percentage(s, 0, sx, sx, &cause);
if (cause != NULL || otherw == 0) {
otherw = sx - mainw;
free(cause);
} else if (otherw > sx || sx - otherw < mainw)
otherw = sx - mainw;
else
mainw = sx - otherw;
}
/* Work out what height is needed. */
sy = (n * (PANE_MINIMUM + 1)) - 1;
if (sy < w->sy)
sy = w->sy;
/* Free old tree and create a new root. */
layout_free(w);
lc = w->layout_root = layout_create_cell(NULL);
layout_set_size(lc, mainw + otherw + 1, sy, 0, 0);
layout_make_node(lc, LAYOUT_LEFTRIGHT);
/* Create the other pane. */
lcother = layout_create_cell(lc);
layout_set_size(lcother, otherw, sy, 0, 0);
if (n == 1) {
wp = TAILQ_NEXT(TAILQ_FIRST(&w->panes), entry);
layout_make_leaf(lcother, wp);
TAILQ_INSERT_TAIL(&lc->cells, lcother, entry);
} else {
layout_make_node(lcother, LAYOUT_TOPBOTTOM);
TAILQ_INSERT_TAIL(&lc->cells, lcother, entry);
/* Add the remaining panes as children. */
TAILQ_FOREACH(wp, &w->panes, entry) {
if (wp == TAILQ_FIRST(&w->panes))
continue;
lcchild = layout_create_cell(lcother);
layout_set_size(lcchild, otherw, PANE_MINIMUM, 0, 0);
layout_make_leaf(lcchild, wp);
TAILQ_INSERT_TAIL(&lcother->cells, lcchild, entry);
}
layout_spread_cell(w, lcother);
}
/* Create the main pane. */
lcmain = layout_create_cell(lc);
layout_set_size(lcmain, mainw, sy, 0, 0);
layout_make_leaf(lcmain, TAILQ_FIRST(&w->panes));
TAILQ_INSERT_TAIL(&lc->cells, lcmain, entry);
/* Fix cell offsets. */
layout_fix_offsets(w);
layout_fix_panes(w, NULL);
layout_print_cell(w->layout_root, __func__, 1);
window_resize(w, lc->sx, lc->sy, -1, -1);
notify_window("window-layout-changed", w);
server_redraw_window(w);
}
void
layout_set_tiled(struct window *w)
{

View File

@ -275,7 +275,8 @@ layout_cell_is_bottom(struct window *w, struct layout_cell *lc)
* the case for the most upper or lower panes only.
*/
static int
layout_add_border(struct window *w, struct layout_cell *lc, int status)
layout_add_horizontal_border(struct window *w, struct layout_cell *lc,
int status)
{
if (status == PANE_STATUS_TOP)
return (layout_cell_is_top(w, lc));
@ -290,22 +291,53 @@ layout_fix_panes(struct window *w, struct window_pane *skip)
{
struct window_pane *wp;
struct layout_cell *lc;
int status;
int status, scrollbars, sb_pos, sb_w, sb_pad;
u_int sx, sy;
status = options_get_number(w->options, "pane-border-status");
scrollbars = options_get_number(w->options, "pane-scrollbars");
sb_pos = options_get_number(w->options, "pane-scrollbars-position");
TAILQ_FOREACH(wp, &w->panes, entry) {
if ((lc = wp->layout_cell) == NULL || wp == skip)
continue;
wp->xoff = lc->xoff;
wp->yoff = lc->yoff;
sx = lc->sx;
sy = lc->sy;
if (layout_add_border(w, lc, status)) {
if (layout_add_horizontal_border(w, lc, status)) {
if (status == PANE_STATUS_TOP)
wp->yoff++;
window_pane_resize(wp, lc->sx, lc->sy - 1);
} else
window_pane_resize(wp, lc->sx, lc->sy);
sy--;
}
if (window_pane_show_scrollbar(wp, scrollbars)) {
sb_w = wp->scrollbar_style.width;
sb_pad = wp->scrollbar_style.pad;
if (sb_w < 1)
sb_w = 1;
if (sb_pad < 0)
sb_pad = 0;
if (sb_pos == PANE_SCROLLBARS_LEFT) {
if ((int)sx - sb_w < PANE_MINIMUM) {
wp->xoff = wp->xoff +
(int)sx - PANE_MINIMUM;
sx = PANE_MINIMUM;
} else {
sx = sx - sb_w - sb_pad;
wp->xoff = wp->xoff + sb_w + sb_pad;
}
} else /* sb_pos == PANE_SCROLLBARS_RIGHT */
if ((int)sx - sb_w - sb_pad < PANE_MINIMUM)
sx = PANE_MINIMUM;
else
sx = sx - sb_w - sb_pad;
wp->flags |= PANE_REDRAWSCROLLBAR;
}
window_pane_resize(wp, sx, sy);
}
}
@ -336,18 +368,25 @@ layout_resize_check(struct window *w, struct layout_cell *lc,
enum layout_type type)
{
struct layout_cell *lcchild;
struct style *sb_style = &w->active->scrollbar_style;
u_int available, minimum;
int status;
int status, scrollbars;
status = options_get_number(w->options, "pane-border-status");
scrollbars = options_get_number(w->options, "pane-scrollbars");
if (lc->type == LAYOUT_WINDOWPANE) {
/* Space available in this cell only. */
if (type == LAYOUT_LEFTRIGHT) {
available = lc->sx;
if (scrollbars)
minimum = PANE_MINIMUM + sb_style->width +
sb_style->pad;
else
minimum = PANE_MINIMUM;
} else {
available = lc->sy;
if (layout_add_border(w, lc, status))
if (layout_add_horizontal_border(w, lc, status))
minimum = PANE_MINIMUM + 1;
else
minimum = PANE_MINIMUM;
@ -870,9 +909,11 @@ layout_split_pane(struct window_pane *wp, enum layout_type type, int size,
int flags)
{
struct layout_cell *lc, *lcparent, *lcnew, *lc1, *lc2;
struct style *sb_style = &wp->scrollbar_style;
u_int sx, sy, xoff, yoff, size1, size2, minimum;
u_int new_size, saved_size, resize_first = 0;
int full_size = (flags & SPAWN_FULLSIZE), status;
int scrollbars;
/*
* If full_size is specified, add a new cell at the top of the window
@ -883,6 +924,7 @@ layout_split_pane(struct window_pane *wp, enum layout_type type, int size,
else
lc = wp->layout_cell;
status = options_get_number(wp->window->options, "pane-border-status");
scrollbars = options_get_number(wp->window->options, "pane-scrollbars");
/* Copy the old cell size. */
sx = lc->sx;
@ -893,11 +935,16 @@ layout_split_pane(struct window_pane *wp, enum layout_type type, int size,
/* Check there is enough space for the two new panes. */
switch (type) {
case LAYOUT_LEFTRIGHT:
if (sx < PANE_MINIMUM * 2 + 1)
if (scrollbars) {
minimum = PANE_MINIMUM * 2 + sb_style->width +
sb_style->pad;
} else
minimum = PANE_MINIMUM * 2 + 1;
if (sx < minimum)
return (NULL);
break;
case LAYOUT_TOPBOTTOM:
if (layout_add_border(wp->window, lc, status))
if (layout_add_horizontal_border(wp->window, lc, status))
minimum = PANE_MINIMUM * 2 + 2;
else
minimum = PANE_MINIMUM * 2 + 1;
@ -1053,8 +1100,9 @@ int
layout_spread_cell(struct window *w, struct layout_cell *parent)
{
struct layout_cell *lc;
u_int number, each, size, this;
int change, changed, status;
struct style *sb_style = &w->active->scrollbar_style;
u_int number, each, size, this, remainder;
int change, changed, status, scrollbars;
number = 0;
TAILQ_FOREACH (lc, &parent->cells, entry)
@ -1062,11 +1110,16 @@ 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 (parent->type == LAYOUT_LEFTRIGHT) {
if (scrollbars)
size = parent->sx - sb_style->width + sb_style->pad;
else
size = parent->sx;
}
else if (parent->type == LAYOUT_TOPBOTTOM) {
if (layout_add_border(w, parent, status))
if (layout_add_horizontal_border(w, parent, status))
size = parent->sy - 1;
else
size = parent->sy;
@ -1077,20 +1130,31 @@ layout_spread_cell(struct window *w, struct layout_cell *parent)
each = (size - (number - 1)) / number;
if (each == 0)
return (0);
/*
* Remaining space after assigning that which can be evenly
* distributed.
*/
remainder = size - (number * (each + 1)) + 1;
changed = 0;
TAILQ_FOREACH (lc, &parent->cells, entry) {
if (TAILQ_NEXT(lc, entry) == NULL)
each = size - ((each + 1) * (number - 1));
change = 0;
if (parent->type == LAYOUT_LEFTRIGHT) {
change = each - (int)lc->sx;
if (remainder > 0) {
change++;
remainder--;
}
layout_resize_adjust(w, lc, LAYOUT_LEFTRIGHT, change);
} else if (parent->type == LAYOUT_TOPBOTTOM) {
if (layout_add_border(w, lc, status))
if (layout_add_horizontal_border(w, lc, status))
this = each + 1;
else
this = each;
if (remainder > 0) {
this++;
remainder--;
}
change = this - (int)lc->sy;
layout_resize_adjust(w, lc, LAYOUT_TOPBOTTOM, change);
}

180
menu.c
View File

@ -27,6 +27,11 @@ struct menu_data {
struct cmdq_item *item;
int flags;
struct grid_cell style;
struct grid_cell border_style;
struct grid_cell selected_style;
enum box_lines border_lines;
struct cmd_find_state fs;
struct screen s;
@ -64,6 +69,8 @@ menu_add_item(struct menu *menu, const struct menu_item *item,
line = (item == NULL || item->name == NULL || *item->name == '\0');
if (line && menu->count == 0)
return;
if (line && menu->items[menu->count - 1].name == NULL)
return;
menu->items = xreallocarray(menu->items, menu->count + 1,
sizeof *menu->items);
@ -160,11 +167,16 @@ menu_free(struct menu *menu)
}
struct screen *
menu_mode_cb(__unused struct client *c, void *data, __unused u_int *cx,
__unused u_int *cy)
menu_mode_cb(__unused struct client *c, void *data, u_int *cx, u_int *cy)
{
struct menu_data *md = data;
*cx = md->px + 2;
if (md->choice == -1)
*cy = md->py;
else
*cy = md->py + 1 + md->choice;
return (&md->s);
}
@ -190,13 +202,17 @@ menu_draw_cb(struct client *c, void *data,
struct menu *menu = md->menu;
struct screen_write_ctx ctx;
u_int i, px = md->px, py = md->py;
struct grid_cell gc;
style_apply(&gc, c->session->curw->window->options, "mode-style", NULL);
screen_write_start(&ctx, s);
screen_write_clearscreen(&ctx, 8);
screen_write_menu(&ctx, menu, md->choice, &gc);
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);
}
screen_write_menu(&ctx, menu, md->choice, md->border_lines,
&md->style, &md->border_style, &md->selected_style);
screen_write_stop(&ctx);
for (i = 0; i < screen_size_y(&md->s); i++) {
@ -318,36 +334,73 @@ menu_key_cb(struct client *c, void *data, struct key_event *event)
} while ((name == NULL || *name == '-') && md->choice != old);
c->flags |= CLIENT_REDRAWOVERLAY;
return (0);
case 'g':
case KEYC_PPAGE:
case '\002': /* C-b */
if (md->choice > 5)
md->choice -= 5;
else
case 'b'|KEYC_CTRL:
if (md->choice < 6)
md->choice = 0;
while (md->choice != count && (name == NULL || *name == '-'))
else {
i = 5;
while (i > 0) {
md->choice--;
name = menu->items[md->choice].name;
if (md->choice != 0 &&
(name != NULL && *name != '-'))
i--;
else if (md->choice == 0)
break;
}
}
c->flags |= CLIENT_REDRAWOVERLAY;
break;
case KEYC_NPAGE:
if (md->choice > count - 6) {
md->choice = count - 1;
name = menu->items[md->choice].name;
} else {
i = 5;
while (i > 0) {
md->choice++;
if (md->choice == count)
md->choice = -1;
name = menu->items[md->choice].name;
if (md->choice != count - 1 &&
(name != NULL && *name != '-'))
i++;
else if (md->choice == count - 1)
break;
}
}
while (name == NULL || *name == '-') {
md->choice--;
name = menu->items[md->choice].name;
}
c->flags |= CLIENT_REDRAWOVERLAY;
break;
case 'g':
case KEYC_HOME:
md->choice = 0;
name = menu->items[md->choice].name;
while (name == NULL || *name == '-') {
md->choice++;
name = menu->items[md->choice].name;
}
c->flags |= CLIENT_REDRAWOVERLAY;
break;
case 'G':
case KEYC_NPAGE:
if (md->choice > count - 6)
case KEYC_END:
md->choice = count - 1;
else
md->choice += 5;
while (md->choice != -1 && (name == NULL || *name == '-'))
name = menu->items[md->choice].name;
while (name == NULL || *name == '-') {
md->choice--;
name = menu->items[md->choice].name;
}
c->flags |= CLIENT_REDRAWOVERLAY;
break;
case '\006': /* C-f */
case 'f'|KEYC_CTRL:
break;
case '\r':
goto chosen;
case '\033': /* Escape */
case '\003': /* C-c */
case '\007': /* C-g */
case 'c'|KEYC_CTRL:
case 'g'|KEYC_CTRL:
case 'q':
return (1);
}
@ -384,14 +437,35 @@ chosen:
return (1);
}
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, struct cmdq_item *item, u_int px,
u_int py, struct client *c, struct cmd_find_state *fs, menu_choice_cb cb,
menu_prepare(struct menu *menu, int flags, int starting_choice,
struct cmdq_item *item, u_int px, u_int py, struct client *c,
enum box_lines lines, const char *style, const char *selected_style,
const char *border_style, struct cmd_find_state *fs, menu_choice_cb cb,
void *data)
{
struct menu_data *md;
u_int i;
int choice;
const char *name;
struct options *o = c->session->curw->window->options;
if (c->tty.sx < menu->width + 4 || c->tty.sy < menu->count + 2)
return (NULL);
@ -400,9 +474,18 @@ menu_prepare(struct menu *menu, int flags, struct cmdq_item *item, u_int px,
if (py + menu->count + 2 > c->tty.sy)
py = c->tty.sy - menu->count - 2;
if (lines == BOX_LINES_DEFAULT)
lines = options_get_number(o, "menu-border-lines");
md = xcalloc(1, sizeof *md);
md->item = item;
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 (fs != NULL)
cmd_find_copy_state(&md->fs, fs);
@ -415,18 +498,38 @@ menu_prepare(struct menu *menu, int flags, struct cmdq_item *item, u_int px,
md->py = py;
md->menu = menu;
md->choice = -1;
if (md->flags & MENU_NOMOUSE) {
for (i = 0; i < menu->count; i++) {
name = menu->items[i].name;
if (name != NULL && *name != '-')
if (starting_choice >= (int)menu->count) {
starting_choice = menu->count - 1;
choice = starting_choice + 1;
for (;;) {
name = menu->items[choice - 1].name;
if (name != NULL && *name != '-') {
md->choice = choice - 1;
break;
}
if (i != menu->count)
md->choice = i;
else
md->choice = -1;
} else
md->choice = -1;
if (--choice == 0)
choice = menu->count;
if (choice == starting_choice + 1)
break;
}
} else if (starting_choice >= 0) {
choice = starting_choice;
for (;;) {
name = menu->items[choice].name;
if (name != NULL && *name != '-') {
md->choice = choice;
break;
}
if (++choice == (int)menu->count)
choice = 0;
if (choice == starting_choice)
break;
}
}
}
md->cb = cb;
md->data = data;
@ -434,13 +537,16 @@ menu_prepare(struct menu *menu, int flags, struct cmdq_item *item, u_int px,
}
int
menu_display(struct menu *menu, int flags, struct cmdq_item *item, u_int px,
u_int py, struct client *c, struct cmd_find_state *fs, menu_choice_cb cb,
menu_display(struct menu *menu, int flags, int starting_choice,
struct cmdq_item *item, u_int px, u_int py, struct client *c,
enum box_lines lines, const char *style, const char *selected_style,
const char *border_style, struct cmd_find_state *fs, menu_choice_cb cb,
void *data)
{
struct menu_data *md;
md = menu_prepare(menu, flags, item, px, py, c, fs, cb, data);
md = menu_prepare(menu, flags, starting_choice, item, px, py, c, lines,
style, selected_style, border_style, fs, cb, data);
if (md == NULL)
return (-1);
server_client_set_overlay(c, 0, NULL, menu_mode_cb, menu_draw_cb,

View File

@ -25,6 +25,17 @@
#include "tmux.h"
enum mode_tree_search_dir {
MODE_TREE_SEARCH_FORWARD,
MODE_TREE_SEARCH_BACKWARD
};
enum mode_tree_preview {
MODE_TREE_PREVIEW_OFF,
MODE_TREE_PREVIEW_NORMAL,
MODE_TREE_PREVIEW_BIG
};
struct mode_tree_item;
TAILQ_HEAD(mode_tree_list, mode_tree_item);
@ -47,6 +58,7 @@ struct mode_tree_data {
mode_tree_menu_cb menucb;
mode_tree_height_cb heightcb;
mode_tree_key_cb keycb;
mode_tree_swap_cb swapcb;
struct mode_tree_list children;
struct mode_tree_list saved;
@ -55,6 +67,7 @@ struct mode_tree_data {
u_int line_size;
u_int depth;
u_int maxdepth;
u_int width;
u_int height;
@ -68,6 +81,7 @@ struct mode_tree_data {
char *search;
char *filter;
int no_matches;
enum mode_tree_search_dir search_dir;
};
struct mode_tree_item {
@ -88,6 +102,7 @@ struct mode_tree_item {
int draw_as_parent;
int no_tag;
int align;
struct mode_tree_list children;
TAILQ_ENTRY(mode_tree_item) entry;
@ -184,6 +199,8 @@ mode_tree_build_lines(struct mode_tree_data *mtd,
int flat = 1;
mtd->depth = depth;
if (depth > mtd->maxdepth)
mtd->maxdepth = depth;
TAILQ_FOREACH(mti, mtl, entry) {
mtd->line_list = xreallocarray(mtd->line_list,
mtd->line_size + 1, sizeof *mtd->line_list);
@ -255,19 +272,50 @@ mode_tree_up(struct mode_tree_data *mtd, int wrap)
}
}
void
int
mode_tree_down(struct mode_tree_data *mtd, int wrap)
{
if (mtd->current == mtd->line_size - 1) {
if (wrap) {
mtd->current = 0;
mtd->offset = 0;
}
} else
return (0);
} else {
mtd->current++;
if (mtd->current > mtd->offset + mtd->height - 1)
mtd->offset++;
}
return (1);
}
static void
mode_tree_swap(struct mode_tree_data *mtd, int direction)
{
u_int current_depth = mtd->line_list[mtd->current].depth;
u_int swap_with, swap_with_depth;
if (mtd->swapcb == NULL)
return;
/* Find the next line at the same depth with the same parent . */
swap_with = mtd->current;
do {
if (direction < 0 && swap_with < (u_int)-direction)
return;
if (direction > 0 && swap_with + direction >= mtd->line_size)
return;
swap_with += direction;
swap_with_depth = mtd->line_list[swap_with].depth;
} while (swap_with_depth > current_depth);
if (swap_with_depth != current_depth)
return;
if (mtd->swapcb(mtd->line_list[mtd->current].item->itemdata,
mtd->line_list[swap_with].item->itemdata)) {
mtd->current = swap_with;
mode_tree_build(mtd);
}
}
void *
@ -342,8 +390,13 @@ mode_tree_set_current(struct mode_tree_data *mtd, uint64_t tag)
mtd->offset = 0;
return (1);
}
mtd->current = 0;
if (mtd->current >= mtd->line_size) {
mtd->current = mtd->line_size - 1;
if (mtd->current > mtd->height - 1)
mtd->offset = mtd->current - mtd->height + 1;
else
mtd->offset = 0;
}
return (0);
}
@ -388,9 +441,9 @@ struct mode_tree_data *
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, void *modedata,
const struct menu_item *menu, const char **sort_list, u_int sort_size,
struct screen **s)
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)
{
struct mode_tree_data *mtd;
const char *sort;
@ -406,7 +459,12 @@ mode_tree_start(struct window_pane *wp, struct args *args,
mtd->sort_list = sort_list;
mtd->sort_size = sort_size;
mtd->preview = !args_has(args, 'N');
if (args_has(args, 'N') > 1)
mtd->preview = MODE_TREE_PREVIEW_BIG;
else if (args_has(args, 'N'))
mtd->preview = MODE_TREE_PREVIEW_OFF;
else
mtd->preview = MODE_TREE_PREVIEW_NORMAL;
sort = args_get(args, 'O');
if (sort != NULL) {
@ -428,6 +486,7 @@ mode_tree_start(struct window_pane *wp, struct args *args,
mtd->menucb = menucb;
mtd->heightcb = heightcb;
mtd->keycb = keycb;
mtd->swapcb = swapcb;
TAILQ_INIT(&mtd->children);
@ -462,12 +521,21 @@ mode_tree_set_height(struct mode_tree_data *mtd)
if (height < screen_size_y(s))
mtd->height = screen_size_y(s) - height;
} else {
if (mtd->preview == MODE_TREE_PREVIEW_NORMAL) {
mtd->height = (screen_size_y(s) / 3) * 2;
if (mtd->height > mtd->line_size)
mtd->height = screen_size_y(s) / 2;
}
if (mtd->height < 10)
mtd->height = screen_size_y(s);
} else if (mtd->preview == MODE_TREE_PREVIEW_BIG) {
mtd->height = screen_size_y(s) / 4;
if (mtd->height > mtd->line_size)
mtd->height = mtd->line_size;
if (mtd->height < 2)
mtd->height = 2;
} else
mtd->height = screen_size_y(s);
}
if (screen_size_y(s) - mtd->height < 2)
mtd->height = screen_size_y(s);
}
@ -495,14 +563,15 @@ mode_tree_build(struct mode_tree_data *mtd)
TAILQ_INIT(&mtd->saved);
mode_tree_clear_lines(mtd);
mtd->maxdepth = 0;
mode_tree_build_lines(mtd, &mtd->children, 0);
if (tag == UINT64_MAX)
if (mtd->line_list != NULL && tag == UINT64_MAX)
tag = mtd->line_list[mtd->current].item->tag;
mode_tree_set_current(mtd, tag);
mtd->width = screen_size_x(s);
if (mtd->preview)
if (mtd->preview != MODE_TREE_PREVIEW_OFF)
mode_tree_set_height(mtd);
else
mtd->height = screen_size_y(s);
@ -599,6 +668,16 @@ mode_tree_no_tag(struct mode_tree_item *mti)
mti->no_tag = 1;
}
/*
* Set the alignment of mti->name: -1 to align left, 0 (default) to not align,
* or 1 to align right.
*/
void
mode_tree_align(struct mode_tree_item *mti, int align)
{
mti->align = align;
}
void
mode_tree_remove(struct mode_tree_data *mtd, struct mode_tree_item *mti)
{
@ -625,7 +704,7 @@ mode_tree_draw(struct mode_tree_data *mtd)
char *text, *start, *key;
const char *tag, *symbol;
size_t size, n;
int keylen, pad;
int keylen, pad, alignlen[mtd->maxdepth + 1];
if (mtd->line_size == 0)
return;
@ -649,6 +728,16 @@ mode_tree_draw(struct mode_tree_data *mtd)
keylen = mti->keylen + 3;
}
for (i = 0; i < mtd->maxdepth + 1; i++)
alignlen[i] = 0;
for (i = 0; i < mtd->line_size; i++) {
line = &mtd->line_list[i];
mti = line->item;
if (mti->align &&
(int)strlen(mti->name) > alignlen[line->depth])
alignlen[line->depth] = strlen(mti->name);
}
for (i = 0; i < mtd->line_size; i++) {
if (i < mtd->offset)
continue;
@ -698,8 +787,9 @@ mode_tree_draw(struct mode_tree_data *mtd)
tag = "*";
else
tag = "";
xasprintf(&text, "%-*s%s%s%s%s", keylen, key, start, mti->name,
tag, (mti->text != NULL) ? ": " : "" );
xasprintf(&text, "%-*s%s%*s%s%s", keylen, key, start,
mti->align * alignlen[line->depth], mti->name, tag,
(mti->text != NULL) ? ": " : "" );
width = utf8_cstrwidth(text);
if (width > w)
width = w;
@ -734,8 +824,11 @@ mode_tree_draw(struct mode_tree_data *mtd)
}
}
if (mtd->preview == MODE_TREE_PREVIEW_OFF)
goto done;
sy = screen_size_y(s);
if (!mtd->preview || sy <= 4 || h <= 4 || sy - h <= 4 || w <= 4)
if (sy <= 4 || h < 2 || sy - h <= 4 || w <= 4)
goto done;
line = &mtd->line_list[mtd->current];
@ -786,7 +879,49 @@ done:
}
static struct mode_tree_item *
mode_tree_search_for(struct mode_tree_data *mtd)
mode_tree_search_backward(struct mode_tree_data *mtd)
{
struct mode_tree_item *mti, *last, *prev;
if (mtd->search == NULL)
return (NULL);
mti = last = mtd->line_list[mtd->current].item;
for (;;) {
if ((prev = TAILQ_PREV(mti, mode_tree_list, entry)) != NULL) {
/* Point to the last child in the previous subtree. */
while (!TAILQ_EMPTY(&prev->children))
prev = TAILQ_LAST(&prev->children, mode_tree_list);
mti = prev;
} else {
/* If prev is NULL, jump to the parent. */
mti = mti->parent;
}
if (mti == NULL) {
/* Point to the last child in the last root subtree. */
prev = TAILQ_LAST(&mtd->children, mode_tree_list);
while (!TAILQ_EMPTY(&prev->children))
prev = TAILQ_LAST(&prev->children, mode_tree_list);
mti = prev;
}
if (mti == last)
break;
if (mtd->searchcb == NULL) {
if (strstr(mti->name, mtd->search) != NULL)
return (mti);
continue;
}
if (mtd->searchcb(mtd->modedata, mti->itemdata, mtd->search))
return (mti);
}
return (NULL);
}
static struct mode_tree_item *
mode_tree_search_forward(struct mode_tree_data *mtd)
{
struct mode_tree_item *mti, *last, *next;
@ -832,7 +967,10 @@ mode_tree_search_set(struct mode_tree_data *mtd)
struct mode_tree_item *mti, *loop;
uint64_t tag;
mti = mode_tree_search_for(mtd);
if (mtd->search_dir == MODE_TREE_SEARCH_FORWARD)
mti = mode_tree_search_forward(mtd);
else
mti = mode_tree_search_backward(mtd);
if (mti == NULL)
return;
tag = mti->tag;
@ -962,10 +1100,13 @@ mode_tree_display_menu(struct mode_tree_data *mtd, struct client *c, u_int x,
x -= (menu->width + 4) / 2;
else
x = 0;
if (menu_display(menu, 0, NULL, x, y, c, NULL, mode_tree_menu_callback,
mtm) != 0)
if (menu_display(menu, 0, 0, NULL, x, y, c, BOX_LINES_DEFAULT, NULL,
NULL, NULL, NULL, mode_tree_menu_callback, mtm) != 0) {
mode_tree_remove_ref(mtd);
free(mtm);
menu_free(menu);
}
}
int
mode_tree_key(struct mode_tree_data *mtd, struct client *c, key_code *key,
@ -988,7 +1129,7 @@ mode_tree_key(struct mode_tree_data *mtd, struct client *c, key_code *key,
if (x > mtd->width || y > mtd->height) {
if (*key == KEYC_MOUSEDOWN3_PANE)
mode_tree_display_menu(mtd, c, x, y, 1);
if (!mtd->preview)
if (mtd->preview == MODE_TREE_PREVIEW_OFF)
*key = KEYC_NONE;
return (0);
}
@ -1035,22 +1176,30 @@ mode_tree_key(struct mode_tree_data *mtd, struct client *c, key_code *key,
switch (*key) {
case 'q':
case '\033': /* Escape */
case '\007': /* C-g */
case 'g'|KEYC_CTRL:
return (1);
case KEYC_UP:
case 'k':
case KEYC_WHEELUP_PANE:
case '\020': /* C-p */
case 'p'|KEYC_CTRL:
mode_tree_up(mtd, 1);
break;
case KEYC_DOWN:
case 'j':
case KEYC_WHEELDOWN_PANE:
case '\016': /* C-n */
case 'n'|KEYC_CTRL:
mode_tree_down(mtd, 1);
break;
case KEYC_UP|KEYC_SHIFT:
case 'K':
mode_tree_swap(mtd, -1);
break;
case KEYC_DOWN|KEYC_SHIFT:
case 'J':
mode_tree_swap(mtd, 1);
break;
case KEYC_PPAGE:
case '\002': /* C-b */
case 'b'|KEYC_CTRL:
for (i = 0; i < mtd->height; i++) {
if (mtd->current == 0)
break;
@ -1058,7 +1207,7 @@ mode_tree_key(struct mode_tree_data *mtd, struct client *c, key_code *key,
}
break;
case KEYC_NPAGE:
case '\006': /* C-f */
case 'f'|KEYC_CTRL:
for (i = 0; i < mtd->height; i++) {
if (mtd->current == mtd->line_size - 1)
break;
@ -1102,7 +1251,7 @@ mode_tree_key(struct mode_tree_data *mtd, struct client *c, key_code *key,
for (i = 0; i < mtd->line_size; i++)
mtd->line_list[i].item->tagged = 0;
break;
case '\024': /* C-t */
case 't'|KEYC_CTRL:
for (i = 0; i < mtd->line_size; i++) {
if ((mtd->line_list[i].item->parent == NULL &&
!mtd->line_list[i].item->no_tag) ||
@ -1158,13 +1307,18 @@ mode_tree_key(struct mode_tree_data *mtd, struct client *c, key_code *key,
break;
case '?':
case '/':
case '\023': /* C-s */
case 's'|KEYC_CTRL:
mtd->references++;
status_prompt_set(c, NULL, "(search) ", "",
mode_tree_search_callback, mode_tree_search_free, mtd,
PROMPT_NOFORMAT, PROMPT_TYPE_SEARCH);
break;
case 'n':
mtd->search_dir = MODE_TREE_SEARCH_FORWARD;
mode_tree_search_set(mtd);
break;
case 'N':
mtd->search_dir = MODE_TREE_SEARCH_BACKWARD;
mode_tree_search_set(mtd);
break;
case 'f':
@ -1174,9 +1328,19 @@ mode_tree_key(struct mode_tree_data *mtd, struct client *c, key_code *key,
PROMPT_NOFORMAT, PROMPT_TYPE_SEARCH);
break;
case 'v':
mtd->preview = !mtd->preview;
switch (mtd->preview) {
case MODE_TREE_PREVIEW_OFF:
mtd->preview = MODE_TREE_PREVIEW_BIG;
break;
case MODE_TREE_PREVIEW_NORMAL:
mtd->preview = MODE_TREE_PREVIEW_OFF;
break;
case MODE_TREE_PREVIEW_BIG:
mtd->preview = MODE_TREE_PREVIEW_NORMAL;
break;
}
mode_tree_build(mtd);
if (mtd->preview)
if (mtd->preview != MODE_TREE_PREVIEW_OFF)
mode_tree_check_selected(mtd);
break;
}
@ -1198,7 +1362,7 @@ mode_tree_run_command(struct client *c, struct cmd_find_state *fs,
if (status == CMD_PARSE_ERROR) {
if (c != NULL) {
*error = toupper((u_char)*error);
status_message_set(c, -1, 1, 0, "%s", error);
status_message_set(c, -1, 1, 0, 0, "%s", error);
}
free(error);
}

View File

@ -32,6 +32,7 @@ struct notify_entry {
struct session *session;
struct window *window;
int pane;
const char *pbname;
};
static struct cmdq_item *
@ -149,6 +150,10 @@ notify_callback(struct cmdq_item *item, void *data)
control_notify_session_closed(ne->session);
if (strcmp(ne->name, "session-window-changed") == 0)
control_notify_session_window_changed(ne->session);
if (strcmp(ne->name, "paste-buffer-changed") == 0)
control_notify_paste_buffer_changed(ne->pbname);
if (strcmp(ne->name, "paste-buffer-deleted") == 0)
control_notify_paste_buffer_deleted(ne->pbname);
notify_insert_hook(item, ne);
@ -164,6 +169,7 @@ notify_callback(struct cmdq_item *item, void *data)
format_free(ne->formats);
free((void *)ne->name);
free((void *)ne->pbname);
free(ne);
return (CMD_RETURN_NORMAL);
@ -171,7 +177,8 @@ notify_callback(struct cmdq_item *item, void *data)
static void
notify_add(const char *name, struct cmd_find_state *fs, struct client *c,
struct session *s, struct window *w, struct window_pane *wp)
struct session *s, struct window *w, struct window_pane *wp,
const char *pbname)
{
struct notify_entry *ne;
struct cmdq_item *item;
@ -186,7 +193,8 @@ notify_add(const char *name, struct cmd_find_state *fs, struct client *c,
ne->client = c;
ne->session = s;
ne->window = w;
ne->pane = (wp != NULL ? wp->id : -1);
ne->pane = (wp != NULL ? (int)wp->id : -1);
ne->pbname = (pbname != NULL ? xstrdup(pbname) : NULL);
ne->formats = format_create(NULL, NULL, 0, FORMAT_NOJOBS);
format_add(ne->formats, "hook", "%s", name);
@ -232,7 +240,7 @@ notify_hook(struct cmdq_item *item, const char *name)
ne.client = cmdq_get_client(item);
ne.session = target->s;
ne.window = target->w;
ne.pane = (target->wp != NULL ? target->wp->id : -1);
ne.pane = (target->wp != NULL ? (int)target->wp->id : -1);
ne.formats = format_create(NULL, NULL, 0, FORMAT_NOJOBS);
format_add(ne.formats, "hook", "%s", name);
@ -248,7 +256,7 @@ notify_client(const char *name, struct client *c)
struct cmd_find_state fs;
cmd_find_from_client(&fs, c, 0);
notify_add(name, &fs, c, NULL, NULL, NULL);
notify_add(name, &fs, c, NULL, NULL, NULL, NULL);
}
void
@ -260,7 +268,7 @@ notify_session(const char *name, struct session *s)
cmd_find_from_session(&fs, s, 0);
else
cmd_find_from_nothing(&fs, 0);
notify_add(name, &fs, NULL, s, NULL, NULL);
notify_add(name, &fs, NULL, s, NULL, NULL, NULL);
}
void
@ -269,7 +277,7 @@ notify_winlink(const char *name, struct winlink *wl)
struct cmd_find_state fs;
cmd_find_from_winlink(&fs, wl, 0);
notify_add(name, &fs, NULL, wl->session, wl->window, NULL);
notify_add(name, &fs, NULL, wl->session, wl->window, NULL, NULL);
}
void
@ -278,7 +286,7 @@ notify_session_window(const char *name, struct session *s, struct window *w)
struct cmd_find_state fs;
cmd_find_from_session_window(&fs, s, w, 0);
notify_add(name, &fs, NULL, s, w, NULL);
notify_add(name, &fs, NULL, s, w, NULL, NULL);
}
void
@ -287,7 +295,7 @@ notify_window(const char *name, struct window *w)
struct cmd_find_state fs;
cmd_find_from_window(&fs, w, 0);
notify_add(name, &fs, NULL, NULL, w, NULL);
notify_add(name, &fs, NULL, NULL, w, NULL, NULL);
}
void
@ -296,5 +304,20 @@ notify_pane(const char *name, struct window_pane *wp)
struct cmd_find_state fs;
cmd_find_from_pane(&fs, wp, 0);
notify_add(name, &fs, NULL, NULL, NULL, wp);
notify_add(name, &fs, NULL, NULL, NULL, wp, NULL);
}
void
notify_paste_buffer(const char *pbname, int deleted)
{
struct cmd_find_state fs;
cmd_find_clear_state(&fs, 0);
if (deleted) {
notify_add("paste-buffer-deleted", &fs, NULL, NULL, NULL, NULL,
pbname);
} else {
notify_add("paste-buffer-changed", &fs, NULL, NULL, NULL, NULL,
pbname);
}
}

View File

@ -41,6 +41,9 @@ static const char *options_table_clock_mode_style_list[] = {
static const char *options_table_status_list[] = {
"off", "on", "2", "3", "4", "5", NULL
};
static const char *options_table_message_line_list[] = {
"0", "1", "2", "3", "4", NULL
};
static const char *options_table_status_keys_list[] = {
"emacs", "vi", NULL
};
@ -60,6 +63,12 @@ static const char *options_table_cursor_style_list[] = {
"default", "blinking-block", "block", "blinking-underline", "underline",
"blinking-bar", "bar", NULL
};
static const char *options_table_pane_scrollbars_list[] = {
"off", "modal", "on", NULL
};
static const char *options_table_pane_scrollbars_position_list[] = {
"right", "left", NULL
};
static const char *options_table_pane_status_list[] = {
"off", "top", "bottom", NULL
};
@ -81,12 +90,21 @@ static const char *options_table_window_size_list[] = {
static const char *options_table_remain_on_exit_list[] = {
"off", "on", "failed", NULL
};
static const char *options_table_destroy_unattached_list[] = {
"off", "on", "keep-last", "keep-group", NULL
};
static const char *options_table_detach_on_destroy_list[] = {
"off", "on", "no-detached", NULL
"off", "on", "no-detached", "previous", "next", NULL
};
static const char *options_table_extended_keys_list[] = {
"off", "on", "always", NULL
};
static const char *options_table_extended_keys_format_list[] = {
"csi-u", "xterm", NULL
};
static const char *options_table_allow_passthrough_list[] = {
"off", "on", "all", NULL
};
/* Status line format. */
#define OPTIONS_TABLE_STATUS_FORMAT1 \
@ -195,6 +213,7 @@ const struct options_name_map options_other_names[] = {
{ "display-panes-active-color", "display-panes-active-colour" },
{ "clock-mode-color", "clock-mode-colour" },
{ "cursor-color", "cursor-colour" },
{ "prompt-cursor-color", "prompt-cursor-colour" },
{ "pane-colors", "pane-colours" },
{ NULL, NULL }
};
@ -234,6 +253,15 @@ const struct options_table_entry options_table[] = {
"Each entry is an alias and a command separated by '='."
},
{ .name = "codepoint-widths",
.type = OPTIONS_TABLE_STRING,
.scope = OPTIONS_TABLE_SERVER,
.flags = OPTIONS_TABLE_IS_ARRAY,
.default_str = "",
.separator = ",",
.text = "Array of override widths for Unicode codepoints."
},
{ .name = "copy-command",
.type = OPTIONS_TABLE_STRING,
.scope = OPTIONS_TABLE_SERVER,
@ -257,6 +285,13 @@ const struct options_table_entry options_table[] = {
.text = "Style of the cursor."
},
{ .name = "default-client-command",
.type = OPTIONS_TABLE_COMMAND,
.scope = OPTIONS_TABLE_SERVER,
.default_str = "new-session",
.text = "Default command to run when tmux is run without a command."
},
{ .name = "default-terminal",
.type = OPTIONS_TABLE_STRING,
.scope = OPTIONS_TABLE_SERVER,
@ -276,7 +311,7 @@ const struct options_table_entry options_table[] = {
.scope = OPTIONS_TABLE_SERVER,
.minimum = 0,
.maximum = INT_MAX,
.default_num = 500,
.default_num = 10,
.unit = "milliseconds",
.text = "Time to wait before assuming a key is Escape."
},
@ -305,6 +340,14 @@ const struct options_table_entry options_table[] = {
"that support it."
},
{ .name = "extended-keys-format",
.type = OPTIONS_TABLE_CHOICE,
.scope = OPTIONS_TABLE_SERVER,
.choices = options_table_extended_keys_format_list,
.default_num = 1,
.text = "The format of emitted extended key sequences."
},
{ .name = "focus-events",
.type = OPTIONS_TABLE_FLAG,
.scope = OPTIONS_TABLE_SERVER,
@ -320,6 +363,51 @@ const struct options_table_entry options_table[] = {
"Empty does not write a history file."
},
{ .name = "input-buffer-size",
.type = OPTIONS_TABLE_NUMBER,
.scope = OPTIONS_TABLE_SERVER,
.minimum = INPUT_BUF_DEFAULT_SIZE,
.maximum = UINT_MAX,
.default_num = INPUT_BUF_DEFAULT_SIZE,
.text = "Number of bytes accepted in a single input before dropping."
},
{ .name = "menu-style",
.type = OPTIONS_TABLE_STRING,
.scope = OPTIONS_TABLE_WINDOW,
.flags = OPTIONS_TABLE_IS_STYLE,
.default_str = "default",
.separator = ",",
.text = "Default style of menu."
},
{ .name = "menu-selected-style",
.type = OPTIONS_TABLE_STRING,
.scope = OPTIONS_TABLE_WINDOW,
.flags = OPTIONS_TABLE_IS_STYLE,
.default_str = "bg=yellow,fg=black",
.separator = ",",
.text = "Default style of selected menu item."
},
{ .name = "menu-border-style",
.type = OPTIONS_TABLE_STRING,
.scope = OPTIONS_TABLE_WINDOW,
.default_str = "default",
.flags = OPTIONS_TABLE_IS_STYLE,
.separator = ",",
.text = "Default style of menu borders."
},
{ .name = "menu-border-lines",
.type = OPTIONS_TABLE_CHOICE,
.scope = OPTIONS_TABLE_WINDOW,
.choices = options_table_popup_border_lines_list,
.default_num = BOX_LINES_SINGLE,
.text = "Type of characters used to draw menu border lines. Some of "
"these are only supported on terminals with UTF-8 support."
},
{ .name = "message-limit",
.type = OPTIONS_TABLE_NUMBER,
.scope = OPTIONS_TABLE_SERVER,
@ -329,6 +417,17 @@ const struct options_table_entry options_table[] = {
.text = "Maximum number of server messages to keep."
},
{ .name = "prefix-timeout",
.type = OPTIONS_TABLE_NUMBER,
.scope = OPTIONS_TABLE_SERVER,
.minimum = 0,
.maximum = INT_MAX,
.default_num = 0,
.unit = "milliseconds",
.text = "The timeout for the prefix key if no subsequent key is "
"pressed. Zero means disabled."
},
{ .name = "prompt-history-limit",
.type = OPTIONS_TABLE_NUMBER,
.scope = OPTIONS_TABLE_SERVER,
@ -352,7 +451,7 @@ const struct options_table_entry options_table[] = {
.type = OPTIONS_TABLE_STRING,
.scope = OPTIONS_TABLE_SERVER,
.flags = OPTIONS_TABLE_IS_ARRAY,
.default_str = "",
.default_str = "linux*:AX@",
.separator = ",",
.text = "List of terminal capabilities overrides."
},
@ -362,7 +461,8 @@ const struct options_table_entry options_table[] = {
.scope = OPTIONS_TABLE_SERVER,
.flags = OPTIONS_TABLE_IS_ARRAY,
.default_str = "xterm*:clipboard:ccolour:cstyle:focus:title,"
"screen*:title",
"screen*:title,"
"rxvt*:ignorefkeys",
.separator = ",",
.text = "List of terminal features, used if they cannot be "
"automatically detected."
@ -379,6 +479,14 @@ const struct options_table_entry options_table[] = {
"'User0', 'User1' and so on."
},
{ .name = "variation-selector-always-wide",
.type = OPTIONS_TABLE_FLAG,
.scope = OPTIONS_TABLE_SERVER,
.default_num = 1,
.text = "If the Unicode VS16 codepoint should always be treated as a "
"wide character."
},
/* Session options. */
{ .name = "activity-action",
.type = OPTIONS_TABLE_CHOICE,
@ -440,11 +548,12 @@ const struct options_table_entry options_table[] = {
},
{ .name = "destroy-unattached",
.type = OPTIONS_TABLE_FLAG,
.type = OPTIONS_TABLE_CHOICE,
.scope = OPTIONS_TABLE_SESSION,
.choices = options_table_destroy_unattached_list,
.default_num = 0,
.text = "Whether to destroy sessions when they have no attached "
"clients."
"clients, or keep the last session whether in the group."
},
{ .name = "detach-on-destroy",
@ -502,6 +611,18 @@ const struct options_table_entry options_table[] = {
"If changed, the new value applies only to new panes."
},
{ .name = "initial-repeat-time",
.type = OPTIONS_TABLE_NUMBER,
.scope = OPTIONS_TABLE_SESSION,
.minimum = 0,
.maximum = 2000000,
.default_num = 0,
.unit = "milliseconds",
.text = "Time to wait for a key binding to repeat the first time the "
"key is pressed, if it is bound with the '-r' flag. "
"Subsequent presses use the 'repeat-time' option."
},
{ .name = "key-table",
.type = OPTIONS_TABLE_STRING,
.scope = OPTIONS_TABLE_SESSION,
@ -523,7 +644,7 @@ const struct options_table_entry options_table[] = {
{ .name = "lock-command",
.type = OPTIONS_TABLE_STRING,
.scope = OPTIONS_TABLE_SESSION,
.default_str = "lock -np",
.default_str = TMUX_LOCK_CMD,
.text = "Shell command to run to lock a client."
},
@ -537,13 +658,21 @@ const struct options_table_entry options_table[] = {
"'mode-keys' is set to 'vi'."
},
{ .name = "message-line",
.type = OPTIONS_TABLE_CHOICE,
.scope = OPTIONS_TABLE_SESSION,
.choices = options_table_message_line_list,
.default_num = 0,
.text = "Position (line) of messages and the command prompt."
},
{ .name = "message-style",
.type = OPTIONS_TABLE_STRING,
.scope = OPTIONS_TABLE_SESSION,
.default_str = "bg=yellow,fg=black",
.flags = OPTIONS_TABLE_IS_STYLE,
.separator = ",",
.text = "Style of the command prompt."
.text = "Style of messages and the command prompt."
},
{ .name = "mouse",
@ -558,7 +687,7 @@ const struct options_table_entry options_table[] = {
{ .name = "prefix",
.type = OPTIONS_TABLE_KEY,
.scope = OPTIONS_TABLE_SESSION,
.default_num = '\002',
.default_num = 'b'|KEYC_CTRL,
.text = "The prefix key."
},
@ -581,7 +710,7 @@ const struct options_table_entry options_table[] = {
.type = OPTIONS_TABLE_NUMBER,
.scope = OPTIONS_TABLE_SESSION,
.minimum = 0,
.maximum = SHRT_MAX,
.maximum = 2000000,
.default_num = 500,
.unit = "milliseconds",
.text = "Time to wait for a key binding to repeat, if it is bound "
@ -742,11 +871,26 @@ const struct options_table_entry options_table[] = {
.text = "Style of the status line."
},
{ .name = "prompt-cursor-colour",
.type = OPTIONS_TABLE_COLOUR,
.scope = OPTIONS_TABLE_SESSION,
.default_num = 6,
.text = "Colour of the cursor when in the command prompt."
},
{ .name = "prompt-cursor-style",
.type = OPTIONS_TABLE_CHOICE,
.scope = OPTIONS_TABLE_SESSION,
.choices = options_table_cursor_style_list,
.default_num = 0,
.text = "Style of the cursor when in the command prompt."
},
{ .name = "update-environment",
.type = OPTIONS_TABLE_STRING,
.scope = OPTIONS_TABLE_SESSION,
.flags = OPTIONS_TABLE_IS_ARRAY,
.default_str = "DISPLAY KRB5CCNAME SSH_ASKPASS SSH_AUTH_SOCK "
.default_str = "DISPLAY KRB5CCNAME MSYSTEM SSH_ASKPASS SSH_AUTH_SOCK "
"SSH_AGENT_PID SSH_CONNECTION WINDOWID XAUTHORITY",
.text = "List of environment variables to update in the session "
"environment when a client is attached."
@ -802,11 +946,14 @@ const struct options_table_entry options_table[] = {
},
{ .name = "allow-passthrough",
.type = OPTIONS_TABLE_FLAG,
.type = OPTIONS_TABLE_CHOICE,
.scope = OPTIONS_TABLE_WINDOW|OPTIONS_TABLE_PANE,
.choices = options_table_allow_passthrough_list,
.default_num = 0,
.text = "Whether applications are allowed to use the escape sequence "
"to bypass tmux."
"to bypass tmux. Can be 'off' (disallowed), 'on' (allowed "
"if the pane is visible), or 'all' (allowed even if the pane "
"is invisible)."
},
{ .name = "allow-rename",
@ -817,6 +964,14 @@ const struct options_table_entry options_table[] = {
"to rename windows."
},
{ .name = "allow-set-title",
.type = OPTIONS_TABLE_FLAG,
.scope = OPTIONS_TABLE_WINDOW|OPTIONS_TABLE_PANE,
.default_num = 1,
.text = "Whether applications are allowed to use the escape sequence "
"to set the pane title."
},
{ .name = "alternate-screen",
.type = OPTIONS_TABLE_FLAG,
.scope = OPTIONS_TABLE_WINDOW|OPTIONS_TABLE_PANE,
@ -882,6 +1037,36 @@ const struct options_table_entry options_table[] = {
.text = "Style of the marked line in copy mode."
},
{ .name = "copy-mode-position-format",
.type = OPTIONS_TABLE_STRING,
.scope = OPTIONS_TABLE_WINDOW|OPTIONS_TABLE_PANE,
.default_str = "#[align=right]"
"#{t/p:top_line_time}#{?#{e|>:#{top_line_time},0}, ,}"
"[#{scroll_position}/#{history_size}]"
"#{?search_timed_out, (timed out),"
"#{?search_count, (#{search_count}"
"#{?search_count_partial,+,} results),}}",
.text = "Format of the position indicator in copy mode."
},
{ .name = "copy-mode-position-style",
.type = OPTIONS_TABLE_STRING,
.scope = OPTIONS_TABLE_WINDOW,
.default_str = "#{E:mode-style}",
.flags = OPTIONS_TABLE_IS_STYLE,
.separator = ",",
.text = "Style of position indicator in copy mode."
},
{ .name = "copy-mode-selection-style",
.type = OPTIONS_TABLE_STRING,
.scope = OPTIONS_TABLE_WINDOW,
.default_str = "#{E:mode-style}",
.flags = OPTIONS_TABLE_IS_STYLE,
.separator = ",",
.text = "Style of selection in copy mode."
},
{ .name = "fill-character",
.type = OPTIONS_TABLE_STRING,
.scope = OPTIONS_TABLE_WINDOW,
@ -916,8 +1101,8 @@ const struct options_table_entry options_table[] = {
{ .name = "mode-style",
.type = OPTIONS_TABLE_STRING,
.scope = OPTIONS_TABLE_WINDOW,
.default_str = "bg=yellow,fg=black",
.flags = OPTIONS_TABLE_IS_STYLE,
.default_str = "noattr,bg=yellow,fg=black",
.separator = ",",
.text = "Style of indicators and highlighting in modes."
},
@ -1032,6 +1217,31 @@ const struct options_table_entry options_table[] = {
.text = "The default colour palette for colours zero to 255."
},
{ .name = "pane-scrollbars",
.type = OPTIONS_TABLE_CHOICE,
.scope = OPTIONS_TABLE_WINDOW,
.choices = options_table_pane_scrollbars_list,
.default_num = PANE_SCROLLBARS_OFF,
.text = "Pane scrollbar state."
},
{ .name = "pane-scrollbars-style",
.type = OPTIONS_TABLE_STRING,
.scope = OPTIONS_TABLE_WINDOW|OPTIONS_TABLE_PANE,
.default_str = "bg=black,fg=white,width=1,pad=0",
.flags = OPTIONS_TABLE_IS_STYLE,
.separator = ",",
.text = "Style of the pane scrollbar."
},
{ .name = "pane-scrollbars-position",
.type = OPTIONS_TABLE_CHOICE,
.scope = OPTIONS_TABLE_WINDOW,
.choices = options_table_pane_scrollbars_position_list,
.default_num = PANE_SCROLLBARS_RIGHT,
.text = "Pane scrollbar position."
},
{ .name = "popup-style",
.type = OPTIONS_TABLE_STRING,
.scope = OPTIONS_TABLE_WINDOW,
@ -1259,6 +1469,9 @@ const struct options_table_entry options_table[] = {
OPTIONS_TABLE_HOOK("client-focus-out", ""),
OPTIONS_TABLE_HOOK("client-resized", ""),
OPTIONS_TABLE_HOOK("client-session-changed", ""),
OPTIONS_TABLE_HOOK("client-light-theme", ""),
OPTIONS_TABLE_HOOK("client-dark-theme", ""),
OPTIONS_TABLE_HOOK("command-error", ""),
OPTIONS_TABLE_PANE_HOOK("pane-died", ""),
OPTIONS_TABLE_PANE_HOOK("pane-exited", ""),
OPTIONS_TABLE_PANE_HOOK("pane-focus-in", ""),

112
options.c
View File

@ -260,6 +260,7 @@ options_default(struct options *oo, const struct options_table_entry *oe)
struct options_entry *o;
union options_value *ov;
u_int i;
struct cmd_parse_result *pr;
o = options_empty(oo, oe);
ov = &o->value;
@ -278,6 +279,17 @@ options_default(struct options *oo, const struct options_table_entry *oe)
case OPTIONS_TABLE_STRING:
ov->string = xstrdup(oe->default_str);
break;
case OPTIONS_TABLE_COMMAND:
pr = cmd_parse_from_string(oe->default_str, NULL);
switch (pr->status) {
case CMD_PARSE_ERROR:
free(pr->error);
break;
case CMD_PARSE_SUCCESS:
ov->cmdlist = pr->cmdlist;
break;
}
break;
default:
ov->number = oe->default_num;
break;
@ -578,10 +590,28 @@ char *
options_to_string(struct options_entry *o, int idx, int numeric)
{
struct options_array_item *a;
char *result = NULL;
char *last = NULL;
char *next;
if (OPTIONS_IS_ARRAY(o)) {
if (idx == -1)
if (idx == -1) {
RB_FOREACH(a, options_array, &o->value.array) {
next = options_value_to_string(o, &a->value,
numeric);
if (last == NULL)
result = next;
else {
xasprintf(&result, "%s %s", last, next);
free(last);
free(next);
}
last = result;
}
if (result == NULL)
return (xstrdup(""));
return (result);
}
a = options_array_item(o, idx);
if (a == NULL)
return (xstrdup(""));
@ -719,6 +749,19 @@ options_get_number(struct options *oo, const char *name)
return (o->value.number);
}
const struct cmd_list *
options_get_command(struct options *oo, const char *name)
{
struct options_entry *o;
o = options_get(oo, name);
if (o == NULL)
fatalx("missing option %s", name);
if (!OPTIONS_IS_COMMAND(o))
fatalx("option %s is not a command", name);
return (o->value.cmdlist);
}
struct options_entry *
options_set_string(struct options *oo, const char *name, int append,
const char *fmt, ...)
@ -780,6 +823,30 @@ options_set_number(struct options *oo, const char *name, long long value)
return (o);
}
struct options_entry *
options_set_command(struct options *oo, const char *name,
struct cmd_list *value)
{
struct options_entry *o;
if (*name == '@')
fatalx("user option %s must be a string", name);
o = options_get_only(oo, name);
if (o == NULL) {
o = options_default(oo, options_parent_table_entry(oo, name));
if (o == NULL)
return (NULL);
}
if (!OPTIONS_IS_COMMAND(o))
fatalx("option %s is not a command", name);
if (o->value.cmdlist != NULL)
cmd_list_free(o->value.cmdlist);
o->value.cmdlist = value;
return (o);
}
int
options_scope_from_name(struct args *args, int window,
const char *name, struct cmd_find_state *fs, struct options **oo,
@ -1036,6 +1103,7 @@ options_from_string(struct options *oo, const struct options_table_entry *oe,
const char *errstr, *new;
char *old;
key_code key;
struct cmd_parse_result *pr;
if (oe != NULL) {
if (value == NULL &&
@ -1094,6 +1162,15 @@ options_from_string(struct options *oo, const struct options_table_entry *oe,
case OPTIONS_TABLE_CHOICE:
return (options_from_string_choice(oe, oo, name, value, cause));
case OPTIONS_TABLE_COMMAND:
pr = cmd_parse_from_string(value, NULL);
switch (pr->status) {
case CMD_PARSE_ERROR:
*cause = pr->error;
return (-1);
case CMD_PARSE_SUCCESS:
options_set_command(oo, name, pr->cmdlist);
return (0);
}
break;
}
return (-1);
@ -1106,7 +1183,6 @@ options_push_changes(const char *name)
struct session *s;
struct window *w;
struct window_pane *wp;
int c;
log_debug("%s: %s", __func__, name);
@ -1119,18 +1195,12 @@ options_push_changes(const char *name)
}
}
if (strcmp(name, "cursor-colour") == 0) {
RB_FOREACH(wp, window_pane_tree, &all_window_panes) {
c = options_get_number(wp->options, name);
wp->screen->default_ccolour = c;
}
RB_FOREACH(wp, window_pane_tree, &all_window_panes)
window_pane_default_cursor(wp);
}
if (strcmp(name, "cursor-style") == 0) {
RB_FOREACH(wp, window_pane_tree, &all_window_panes) {
wp->screen->default_mode = 0;
screen_set_cursor_style(options_get_number(wp->options,
name), &wp->screen->default_cstyle,
&wp->screen->default_mode);
}
RB_FOREACH(wp, window_pane_tree, &all_window_panes)
window_pane_default_cursor(wp);
}
if (strcmp(name, "fill-character") == 0) {
RB_FOREACH(w, windows, &windows)
@ -1154,16 +1224,30 @@ options_push_changes(const char *name)
if (strcmp(name, "window-style") == 0 ||
strcmp(name, "window-active-style") == 0) {
RB_FOREACH(wp, window_pane_tree, &all_window_panes)
wp->flags |= PANE_STYLECHANGED;
wp->flags |= (PANE_STYLECHANGED|PANE_THEMECHANGED);
}
if (strcmp(name, "pane-colours") == 0) {
RB_FOREACH(wp, window_pane_tree, &all_window_panes)
colour_palette_from_option(&wp->palette, wp->options);
}
if (strcmp(name, "pane-border-status") == 0) {
if (strcmp(name, "pane-border-status") == 0 ||
strcmp(name, "pane-scrollbars") == 0 ||
strcmp(name, "pane-scrollbars-position") == 0) {
RB_FOREACH(w, windows, &windows)
layout_fix_panes(w, NULL);
}
if (strcmp(name, "pane-scrollbars-style") == 0) {
RB_FOREACH(wp, window_pane_tree, &all_window_panes) {
style_set_scrollbar_style_from_option(
&wp->scrollbar_style, wp->options);
}
RB_FOREACH(w, windows, &windows)
layout_fix_panes(w, NULL);
}
if (strcmp(name, "codepoint-widths") == 0)
utf8_update_width_cache();
if (strcmp(name, "input-buffer-size") == 0)
input_set_buffer_size(options_get_number(global_options, name));
RB_FOREACH(s, sessions, &sessions)
status_update_cache(s);

28
paste.c
View File

@ -111,6 +111,12 @@ paste_walk(struct paste_buffer *pb)
return (RB_NEXT(paste_time_tree, &paste_by_time, pb));
}
int
paste_is_empty(void)
{
return RB_ROOT(&paste_by_time) == NULL;
}
/* Get the most recent automatic buffer. */
struct paste_buffer *
paste_get_top(const char **name)
@ -118,6 +124,8 @@ paste_get_top(const char **name)
struct paste_buffer *pb;
pb = RB_MIN(paste_time_tree, &paste_by_time);
while (pb != NULL && !pb->automatic)
pb = RB_NEXT(paste_time_tree, &paste_by_time, pb);
if (pb == NULL)
return (NULL);
if (name != NULL)
@ -142,6 +150,8 @@ paste_get_name(const char *name)
void
paste_free(struct paste_buffer *pb)
{
notify_paste_buffer(pb->name, 1);
RB_REMOVE(paste_name_tree, &paste_by_name, pb);
RB_REMOVE(paste_time_tree, &paste_by_time, pb);
if (pb->automatic)
@ -198,6 +208,8 @@ paste_add(const char *prefix, char *data, size_t size)
pb->order = paste_next_order++;
RB_INSERT(paste_name_tree, &paste_by_name, pb);
RB_INSERT(paste_time_tree, &paste_by_time, pb);
notify_paste_buffer(pb->name, 0);
}
/* Rename a paste buffer. */
@ -228,11 +240,10 @@ paste_rename(const char *oldname, const char *newname, char **cause)
}
pb_new = paste_get_name(newname);
if (pb_new != NULL) {
if (cause != NULL)
xasprintf(cause, "buffer %s already exists", newname);
return (-1);
}
if (pb_new == pb)
return (0);
if (pb_new != NULL)
paste_free(pb_new);
RB_REMOVE(paste_name_tree, &paste_by_name, pb);
@ -245,6 +256,9 @@ paste_rename(const char *oldname, const char *newname, char **cause)
RB_INSERT(paste_name_tree, &paste_by_name, pb);
notify_paste_buffer(oldname, 1);
notify_paste_buffer(newname, 0);
return (0);
}
@ -293,6 +307,8 @@ paste_set(char *data, size_t size, const char *name, char **cause)
RB_INSERT(paste_name_tree, &paste_by_name, pb);
RB_INSERT(paste_time_tree, &paste_by_time, pb);
notify_paste_buffer(name, 0);
return (0);
}
@ -303,6 +319,8 @@ paste_replace(struct paste_buffer *pb, char *data, size_t size)
free(pb->data);
pb->data = data;
pb->size = size;
notify_paste_buffer(pb->name, 0);
}
/* Convert start of buffer into a nice string. */

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