Compare commits

..

270 Commits
3.5 ... 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
107 changed files with 6264 additions and 2250 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.

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.*

View File

@ -389,7 +389,7 @@ CHANGES FROM 3.2 TO 3.2a
* Do not expand the filename given to -f so it can contain colons.
* Fixes for problems with extended keys and modifiers, scroll region,
source-file, crosscompiling, format modifiers and other minor issues.
source-file, cross compiling, format modifiers and other minor issues.
CHANGES FROM 3.1c TO 3.2

View File

@ -30,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
@ -66,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 \

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

@ -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;

View File

@ -39,8 +39,8 @@ const struct cmd_entry cmd_capture_pane_entry = {
.name = "capture-pane",
.alias = "capturep",
.args = { "ab:CeE:JNpPqS:Tt:", 0, 0, NULL },
.usage = "[-aCeJNpPqT] " 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 },
@ -107,14 +107,16 @@ static char *
cmd_capture_pane_history(struct args *args, struct cmdq_item *item,
struct window_pane *wp, size_t *len)
{
struct grid *gd;
const struct grid_line *gl;
struct grid_cell *gc = NULL;
int n, join_lines, flags = 0;
u_int i, sx, top, bottom, tmp;
char *cause, *buf, *line;
const char *Sflag, *Eflag;
size_t linelen;
struct grid *gd;
const struct grid_line *gl;
struct screen *s;
struct grid_cell *gc = NULL;
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;
size_t linelen;
sx = screen_size_x(&wp->base);
if (args_has(args, 'a')) {
@ -126,8 +128,20 @@ 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)
@ -181,7 +195,7 @@ cmd_capture_pane_history(struct args *args, struct cmdq_item *item,
buf = NULL;
for (i = top; i <= bottom; i++) {
line = grid_string_cells(gd, 0, i, sx, &gc, flags, wp->screen);
line = grid_string_cells(gd, 0, i, sx, &gc, flags, s);
linelen = strlen(line);
buf = cmd_capture_pane_append(buf, len, line, linelen);

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 },

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

View File

@ -42,7 +42,7 @@ const struct cmd_entry cmd_confirm_before_entry = {
.alias = "confirm",
.args = { "bc:p:t:y", 1, 1, cmd_confirm_before_args_parse },
.usage = "[-by] [-c confirm_key] [-p prompt] " CMD_TARGET_CLIENT_USAGE
.usage = "[-by] [-c confirm-key] [-p prompt] " CMD_TARGET_CLIENT_USAGE
" command",
.flags = CMD_CLIENT_TFLAG,
@ -130,7 +130,7 @@ cmd_confirm_before_callback(struct client *c, void *data, const char *s,
if (s == NULL)
goto out;
if (s[0] != cdata->confirm_key && (s[0] != '\0' || !cdata->default_yes))
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 = { "deHMs:t:uq", 0, 0, NULL },
.usage = "[-deHMuq] [-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 },
@ -92,7 +92,12 @@ 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'));
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

@ -41,8 +41,8 @@ const struct cmd_entry cmd_display_menu_entry = {
.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 ...",
"[-S border-style] " CMD_TARGET_PANE_USAGE " [-T title] "
"[-x position] [-y position] name [key] [command] ...",
.target = { 't', CMD_FIND_PANE, 0 },
@ -58,8 +58,8 @@ const struct cmd_entry cmd_display_popup_entry = {
.usage = "[-BCE] [-b border-lines] [-c target-client] "
"[-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]",
" [-T title] [-w width] [-x position] [-y position] "
"[shell-command [argument ...]]",
.target = { 't', CMD_FIND_PANE, 0 },

View File

@ -39,8 +39,8 @@ const struct cmd_entry cmd_display_message_entry = {
.name = "display-message",
.alias = "display",
.args = { "ac:d:lINpt:F:v", 0, 1, NULL },
.usage = "[-aIlNpv] [-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 },
@ -69,6 +69,7 @@ cmd_display_message_exec(struct cmd *self, struct cmdq_item *item)
const char *template;
char *msg, *cause;
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;
@ -150,7 +151,7 @@ cmd_display_message_exec(struct cmd *self, struct cmdq_item *item)
server_client_print(tc, 0, evb);
evbuffer_free(evb);
} else if (tc != NULL)
status_message_set(tc, delay, 0, Nflag, "%s", msg);
status_message_set(tc, delay, 0, Nflag, Cflag, "%s", msg);
free(msg);
format_free(ft);

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

@ -149,7 +149,7 @@ 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

View File

@ -91,7 +91,7 @@ cmd_list_keys_print_notes(struct cmdq_item *item, struct args *args,
struct key_binding *bd;
const char *key;
char *tmp, *note;
int found = 0;
int found = 0;
table = key_bindings_get_table(tablename, 0);
if (table == NULL)
@ -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);
@ -298,8 +298,8 @@ cmd_list_keys_exec(struct cmd *self, struct cmdq_item *item)
free(cp);
if (args_has(args, '1') && tc != NULL) {
status_message_set(tc, -1, 1, 0, "bind-key %s",
tmp);
status_message_set(tc, -1, 1, 0, 0,
"bind-key %s", tmp);
} else
cmdq_print(item, "bind-key %s", tmp);
free(key);
@ -321,6 +321,31 @@ out:
return (CMD_RETURN_NORMAL);
}
static void
cmd_list_single_command(const struct cmd_entry *entry, struct format_tree *ft,
const char *template, struct cmdq_item *item)
{
const char *s;
char *line;
format_add(ft, "command_list_name", "%s", entry->name);
if (entry->alias != NULL)
s = entry->alias;
else
s = "";
format_add(ft, "command_list_alias", "%s", s);
if (entry->usage != NULL)
s = entry->usage;
else
s = "";
format_add(ft, "command_list_usage", "%s", s);
line = format_expand(ft, template);
if (*line != '\0')
cmdq_print(item, "%s", line);
free(line);
}
static enum cmd_retval
cmd_list_keys_commands(struct cmd *self, struct cmdq_item *item)
{
@ -328,8 +353,8 @@ cmd_list_keys_commands(struct cmd *self, struct cmdq_item *item)
const struct cmd_entry **entryp;
const struct cmd_entry *entry;
struct format_tree *ft;
const char *template, *s, *command;
char *line;
const char *template, *command;
char *cause;
if ((template = args_get(args, 'F')) == NULL) {
template = "#{command_list_name}"
@ -341,30 +366,19 @@ cmd_list_keys_commands(struct cmd *self, struct cmdq_item *item)
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;
else
s = "";
format_add(ft, "command_list_alias", "%s", s);
if (entry->usage != NULL)
s = entry->usage;
else
s = "";
format_add(ft, "command_list_usage", "%s", s);
line = format_expand(ft, template);
if (*line != '\0')
cmdq_print(item, "%s", line);
free(line);
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);

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 },

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 },

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);
}
@ -1613,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,
@ -1636,9 +1651,12 @@ yylex_token(int ch)
ch = '\r';
}
}
if (state == NONE && ch == '\n') {
log_debug("%s: end at EOL", __func__);
break;
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. */

View File

@ -834,9 +834,9 @@ cmdq_guard(struct cmdq_item *item, const char *guard, int flags)
/* Show message from command. */
void
cmdq_print_data(struct cmdq_item *item, int parse, struct evbuffer *evb)
cmdq_print_data(struct cmdq_item *item, struct evbuffer *evb)
{
server_client_print(item->client, parse, evb);
server_client_print(item->client, 1, evb);
}
/* Show message from command. */
@ -854,7 +854,7 @@ cmdq_print(struct cmdq_item *item, const char *fmt, ...)
evbuffer_add_vprintf(evb, fmt, ap);
va_end(ap);
cmdq_print_data(item, 0, evb);
cmdq_print_data(item, evb);
evbuffer_free(evb);
}
@ -892,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

@ -36,7 +36,7 @@ const struct cmd_entry cmd_refresh_client_entry = {
.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] [-r pane:report]" CMD_TARGET_CLIENT_USAGE
"[-C XxY] [-f flags] [-r pane:report] " CMD_TARGET_CLIENT_USAGE
" [adjustment]",
.flags = CMD_AFTERHOOK|CMD_CLIENT_TFLAG,

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,8 @@ const struct cmd_entry cmd_run_shell_entry = {
.name = "run-shell",
.alias = "run",
.args = { "bd:Ct:c:", 0, 2, cmd_run_shell_args_parse },
.usage = "[-bC] [-c start-directory] [-d delay] " CMD_TARGET_PANE_USAGE
.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 },
@ -158,6 +158,9 @@ cmd_run_shell_exec(struct cmd *self, struct cmdq_item *item)
else
cdata->cwd = xstrdup(server_client_get_cwd(c, s));
if (args_has(args, 'E'))
cdata->flags |= JOB_SHOWSTDERR;
cdata->s = s;
if (s != NULL)
session_add_ref(s, __func__);
@ -204,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

@ -101,7 +101,7 @@ cmd_save_buffer_exec(struct cmd *self, struct cmdq_item *item)
if (evb == NULL)
fatalx("out of memory");
evbuffer_add(evb, bufdata, bufsize);
cmdq_print_data(item, 1, evb);
cmdq_print_data(item, evb);
evbuffer_free(evb);
return (CMD_RETURN_NORMAL);
}

View File

@ -149,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);
}
@ -169,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

@ -35,7 +35,7 @@ const struct cmd_entry cmd_send_keys_entry = {
.args = { "c:FHKlMN:Rt:X", 0, -1, NULL },
.usage = "[-FHKlMRX] [-c target-client] [-N repeat-count] "
CMD_TARGET_PANE_USAGE " key ...",
CMD_TARGET_PANE_USAGE " [key ...]",
.target = { 't', CMD_FIND_PANE, 0 },
@ -73,11 +73,13 @@ cmd_send_keys_inject_key(struct cmdq_item *item, struct cmdq_item *after,
if (args_has(args, 'K')) {
if (tc == NULL)
return (item);
event = xmalloc(sizeof *event);
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)
if (server_client_handle_key(tc, event) == 0) {
free(event->buf);
free(event);
}
return (item);
}
@ -215,7 +217,7 @@ cmd_send_keys_exec(struct cmd *self, struct cmdq_item *item)
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) {

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

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,6 +29,9 @@
* 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 = {
@ -59,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);
}
@ -121,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);
@ -140,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 },

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;
@ -132,9 +132,12 @@ cmd_swap_pane_exec(struct cmd *self, struct cmdq_item *item)
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);
}
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);

6
cmd.c
View File

@ -444,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;
@ -636,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;
@ -667,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;

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)
@ -948,7 +988,7 @@ colour_byname(const char *name)
if (strncmp(name, "grey", 4) == 0 || strncmp(name, "gray", 4) == 0) {
if (name[4] == '\0')
return (-1);
return (0xbebebe|COLOUR_FLAG_RGB);
c = strtonum(name + 4, 0, 100, &errstr);
if (errstr != NULL)
return (-1);

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
@ -442,7 +450,7 @@ void *recallocarray(void *, size_t, size_t, size_t);
/* systemd.c */
int systemd_activated(void);
int systemd_create_socket(int, char **);
int systemd_move_pid_to_new_cgroup(pid_t, char **);
int systemd_move_to_new_cgroup(char **);
#endif
#ifdef HAVE_UTF8PROC
@ -458,11 +466,11 @@ int utf8proc_wctomb(char *, wchar_t);
#endif
/* getopt.c */
extern int BSDopterr;
extern int BSDoptind;
extern int BSDoptopt;
extern int BSDoptreset;
extern char *BSDoptarg;
extern int BSDopterr;
extern int BSDoptind;
extern int BSDoptopt;
extern int BSDoptreset;
extern char *BSDoptarg;
int BSDgetopt(int, char *const *, const char *);
#define getopt(ac, av, o) BSDgetopt(ac, av, o)
#define opterr BSDopterr

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

@ -1,4 +1,4 @@
/* $OpenBSD: imsg-buffer.c,v 1.18 2023/12/12 15:47:41 claudio Exp $ */
/* $OpenBSD: imsg-buffer.c,v 1.31 2024/11/26 13:57:31 claudio Exp $ */
/*
* Copyright (c) 2023 Claudio Jeker <claudio@openbsd.org>
@ -45,25 +45,37 @@
#undef be64toh
#define be64toh ntohll
static int ibuf_realloc(struct ibuf *, size_t);
static void ibuf_enqueue(struct msgbuf *, struct ibuf *);
static void ibuf_dequeue(struct msgbuf *, struct ibuf *);
struct msgbuf {
TAILQ_HEAD(, ibuf) bufs;
TAILQ_HEAD(, ibuf) rbufs;
uint32_t queued;
char *rbuf;
struct ibuf *rpmsg;
struct ibuf *(*readhdr)(struct ibuf *, void *, int *);
void *rarg;
size_t roff;
size_t hdrsize;
};
static void msgbuf_read_enqueue(struct msgbuf *, struct ibuf *);
static void msgbuf_enqueue(struct msgbuf *, struct ibuf *);
static void msgbuf_dequeue(struct msgbuf *, struct ibuf *);
static void msgbuf_drain(struct msgbuf *, size_t);
#define IBUF_FD_MARK_ON_STACK -2
struct ibuf *
ibuf_open(size_t len)
{
struct ibuf *buf;
if (len == 0) {
errno = EINVAL;
return (NULL);
}
if ((buf = calloc(1, sizeof(struct ibuf))) == NULL)
return (NULL);
if ((buf->buf = calloc(len, 1)) == NULL) {
free(buf);
return (NULL);
if (len > 0) {
if ((buf->buf = calloc(len, 1)) == NULL) {
free(buf);
return (NULL);
}
}
buf->size = buf->max = len;
buf->fd = -1;
@ -96,39 +108,36 @@ ibuf_dynamic(size_t len, size_t max)
return (buf);
}
static int
ibuf_realloc(struct ibuf *buf, size_t len)
{
unsigned char *b;
/* on static buffers max is eq size and so the following fails */
if (len > SIZE_MAX - buf->wpos || buf->wpos + len > buf->max) {
errno = ERANGE;
return (-1);
}
b = recallocarray(buf->buf, buf->size, buf->wpos + len, 1);
if (b == NULL)
return (-1);
buf->buf = b;
buf->size = buf->wpos + len;
return (0);
}
void *
ibuf_reserve(struct ibuf *buf, size_t len)
{
void *b;
if (len > SIZE_MAX - buf->wpos || buf->max == 0) {
if (len > SIZE_MAX - buf->wpos) {
errno = ERANGE;
return (NULL);
}
if (buf->fd == IBUF_FD_MARK_ON_STACK) {
/* can not grow stack buffers */
errno = EINVAL;
return (NULL);
}
if (buf->wpos + len > buf->size)
if (ibuf_realloc(buf, len) == -1)
if (buf->wpos + len > buf->size) {
unsigned char *nb;
/* check if buffer is allowed to grow */
if (buf->wpos + len > buf->max) {
errno = ERANGE;
return (NULL);
}
nb = realloc(buf->buf, buf->wpos + len);
if (nb == NULL)
return (NULL);
memset(nb + buf->size, 0, buf->wpos + len - buf->size);
buf->buf = nb;
buf->size = buf->wpos + len;
}
b = buf->buf + buf->wpos;
buf->wpos += len;
@ -153,13 +162,6 @@ ibuf_add_ibuf(struct ibuf *buf, const struct ibuf *from)
return ibuf_add(buf, ibuf_data(from), ibuf_size(from));
}
/* remove after tree is converted */
int
ibuf_add_buf(struct ibuf *buf, const struct ibuf *from)
{
return ibuf_add_ibuf(buf, from);
}
int
ibuf_add_n8(struct ibuf *buf, uint64_t value)
{
@ -367,7 +369,8 @@ ibuf_size(const struct ibuf *buf)
size_t
ibuf_left(const struct ibuf *buf)
{
if (buf->max == 0)
/* on stack buffers have no space left */
if (buf->fd == IBUF_FD_MARK_ON_STACK)
return (0);
return (buf->max - buf->wpos);
}
@ -379,8 +382,8 @@ ibuf_truncate(struct ibuf *buf, size_t len)
buf->wpos = buf->rpos + len;
return (0);
}
if (buf->max == 0) {
/* only allow to truncate down */
if (buf->fd == IBUF_FD_MARK_ON_STACK) {
/* only allow to truncate down for stack buffers */
errno = ERANGE;
return (-1);
}
@ -396,7 +399,7 @@ ibuf_rewind(struct ibuf *buf)
void
ibuf_close(struct msgbuf *msgbuf, struct ibuf *buf)
{
ibuf_enqueue(msgbuf, buf);
msgbuf_enqueue(msgbuf, buf);
}
void
@ -405,7 +408,7 @@ ibuf_from_buffer(struct ibuf *buf, void *data, size_t len)
memset(buf, 0, sizeof(*buf));
buf->buf = data;
buf->size = buf->wpos = len;
buf->fd = -1;
buf->fd = IBUF_FD_MARK_ON_STACK;
}
void
@ -440,6 +443,24 @@ ibuf_get_ibuf(struct ibuf *buf, size_t len, struct ibuf *new)
return (0);
}
int
ibuf_get_h16(struct ibuf *buf, uint16_t *value)
{
return ibuf_get(buf, value, sizeof(*value));
}
int
ibuf_get_h32(struct ibuf *buf, uint32_t *value)
{
return ibuf_get(buf, value, sizeof(*value));
}
int
ibuf_get_h64(struct ibuf *buf, uint64_t *value)
{
return ibuf_get(buf, value, sizeof(*value));
}
int
ibuf_get_n8(struct ibuf *buf, uint8_t *value)
{
@ -476,22 +497,21 @@ ibuf_get_n64(struct ibuf *buf, uint64_t *value)
return (rv);
}
int
ibuf_get_h16(struct ibuf *buf, uint16_t *value)
char *
ibuf_get_string(struct ibuf *buf, size_t len)
{
return ibuf_get(buf, value, sizeof(*value));
}
char *str;
int
ibuf_get_h32(struct ibuf *buf, uint32_t *value)
{
return ibuf_get(buf, value, sizeof(*value));
}
if (ibuf_size(buf) < len) {
errno = EBADMSG;
return (NULL);
}
int
ibuf_get_h64(struct ibuf *buf, uint64_t *value)
{
return ibuf_get(buf, value, sizeof(*value));
str = strndup(ibuf_data(buf), len);
if (str == NULL)
return (NULL);
buf->rpos += len;
return (str);
}
int
@ -511,9 +531,10 @@ ibuf_free(struct ibuf *buf)
{
if (buf == NULL)
return;
if (buf->max == 0) /* if buf lives on the stack */
abort(); /* abort before causing more harm */
if (buf->fd != -1)
/* if buf lives on the stack abort before causing more harm */
if (buf->fd == IBUF_FD_MARK_ON_STACK)
abort();
if (buf->fd >= 0)
close(buf->fd);
freezero(buf->buf, buf->size);
free(buf);
@ -522,7 +543,7 @@ ibuf_free(struct ibuf *buf)
int
ibuf_fd_avail(struct ibuf *buf)
{
return (buf->fd != -1);
return (buf->fd >= 0);
}
int
@ -530,6 +551,9 @@ ibuf_fd_get(struct ibuf *buf)
{
int fd;
/* negative fds are internal use and equivalent to -1 */
if (buf->fd < 0)
return (-1);
fd = buf->fd;
buf->fd = -1;
return (fd);
@ -538,15 +562,107 @@ ibuf_fd_get(struct ibuf *buf)
void
ibuf_fd_set(struct ibuf *buf, int fd)
{
if (buf->max == 0) /* if buf lives on the stack */
abort(); /* abort before causing more harm */
if (buf->fd != -1)
/* if buf lives on the stack abort before causing more harm */
if (buf->fd == IBUF_FD_MARK_ON_STACK)
abort();
if (buf->fd >= 0)
close(buf->fd);
buf->fd = fd;
buf->fd = -1;
if (fd >= 0)
buf->fd = fd;
}
struct msgbuf *
msgbuf_new(void)
{
struct msgbuf *msgbuf;
if ((msgbuf = calloc(1, sizeof(*msgbuf))) == NULL)
return (NULL);
msgbuf->queued = 0;
TAILQ_INIT(&msgbuf->bufs);
TAILQ_INIT(&msgbuf->rbufs);
return msgbuf;
}
struct msgbuf *
msgbuf_new_reader(size_t hdrsz,
struct ibuf *(*readhdr)(struct ibuf *, void *, int *), void *arg)
{
struct msgbuf *msgbuf;
char *buf;
if (hdrsz == 0 || hdrsz > IBUF_READ_SIZE / 2) {
errno = EINVAL;
return (NULL);
}
if ((buf = malloc(IBUF_READ_SIZE)) == NULL)
return (NULL);
msgbuf = msgbuf_new();
if (msgbuf == NULL) {
free(buf);
return (NULL);
}
msgbuf->rbuf = buf;
msgbuf->hdrsize = hdrsz;
msgbuf->readhdr = readhdr;
msgbuf->rarg = arg;
return (msgbuf);
}
void
msgbuf_free(struct msgbuf *msgbuf)
{
if (msgbuf == NULL)
return;
msgbuf_clear(msgbuf);
free(msgbuf->rbuf);
free(msgbuf);
}
uint32_t
msgbuf_queuelen(struct msgbuf *msgbuf)
{
return (msgbuf->queued);
}
void
msgbuf_clear(struct msgbuf *msgbuf)
{
struct ibuf *buf;
/* write side */
while ((buf = TAILQ_FIRST(&msgbuf->bufs)) != NULL)
msgbuf_dequeue(msgbuf, buf);
msgbuf->queued = 0;
/* read side */
while ((buf = TAILQ_FIRST(&msgbuf->rbufs)) != NULL) {
TAILQ_REMOVE(&msgbuf->rbufs, buf, entry);
ibuf_free(buf);
}
msgbuf->roff = 0;
ibuf_free(msgbuf->rpmsg);
msgbuf->rpmsg = NULL;
}
struct ibuf *
msgbuf_get(struct msgbuf *msgbuf)
{
struct ibuf *buf;
if ((buf = TAILQ_FIRST(&msgbuf->rbufs)) != NULL)
TAILQ_REMOVE(&msgbuf->rbufs, buf, entry);
return buf;
}
int
ibuf_write(struct msgbuf *msgbuf)
ibuf_write(int fd, struct msgbuf *msgbuf)
{
struct iovec iov[IOV_MAX];
struct ibuf *buf;
@ -561,63 +677,25 @@ ibuf_write(struct msgbuf *msgbuf)
iov[i].iov_len = ibuf_size(buf);
i++;
}
if (i == 0)
return (0); /* nothing queued */
again:
if ((n = writev(msgbuf->fd, iov, i)) == -1) {
again:
if ((n = writev(fd, iov, i)) == -1) {
if (errno == EINTR)
goto again;
if (errno == ENOBUFS)
errno = EAGAIN;
if (errno == EAGAIN || errno == ENOBUFS)
/* lets retry later again */
return (0);
return (-1);
}
if (n == 0) { /* connection closed */
errno = 0;
return (0);
}
msgbuf_drain(msgbuf, n);
return (1);
}
void
msgbuf_init(struct msgbuf *msgbuf)
{
msgbuf->queued = 0;
msgbuf->fd = -1;
TAILQ_INIT(&msgbuf->bufs);
}
static void
msgbuf_drain(struct msgbuf *msgbuf, size_t n)
{
struct ibuf *buf, *next;
for (buf = TAILQ_FIRST(&msgbuf->bufs); buf != NULL && n > 0;
buf = next) {
next = TAILQ_NEXT(buf, entry);
if (n >= ibuf_size(buf)) {
n -= ibuf_size(buf);
ibuf_dequeue(msgbuf, buf);
} else {
buf->rpos += n;
n = 0;
}
}
}
void
msgbuf_clear(struct msgbuf *msgbuf)
{
struct ibuf *buf;
while ((buf = TAILQ_FIRST(&msgbuf->bufs)) != NULL)
ibuf_dequeue(msgbuf, buf);
return (0);
}
int
msgbuf_write(struct msgbuf *msgbuf)
msgbuf_write(int fd, struct msgbuf *msgbuf)
{
struct iovec iov[IOV_MAX];
struct ibuf *buf, *buf0 = NULL;
@ -645,6 +723,9 @@ msgbuf_write(struct msgbuf *msgbuf)
buf0 = buf;
}
if (i == 0)
return (0); /* nothing queued */
msg.msg_iov = iov;
msg.msg_iovlen = i;
@ -658,20 +739,16 @@ msgbuf_write(struct msgbuf *msgbuf)
*(int *)CMSG_DATA(cmsg) = buf0->fd;
}
again:
if ((n = sendmsg(msgbuf->fd, &msg, 0)) == -1) {
again:
if ((n = sendmsg(fd, &msg, 0)) == -1) {
if (errno == EINTR)
goto again;
if (errno == ENOBUFS)
errno = EAGAIN;
if (errno == EAGAIN || errno == ENOBUFS)
/* lets retry later again */
return (0);
return (-1);
}
if (n == 0) { /* connection closed */
errno = 0;
return (0);
}
/*
* assumption: fd got sent if sendmsg sent anything
* this works because fds are passed one at a time
@ -683,28 +760,209 @@ again:
msgbuf_drain(msgbuf, n);
return (1);
return (0);
}
uint32_t
msgbuf_queuelen(struct msgbuf *msgbuf)
static int
ibuf_read_process(struct msgbuf *msgbuf, int fd)
{
return (msgbuf->queued);
struct ibuf rbuf, msg;
ssize_t sz;
ibuf_from_buffer(&rbuf, msgbuf->rbuf, msgbuf->roff);
do {
if (msgbuf->rpmsg == NULL) {
if (ibuf_size(&rbuf) < msgbuf->hdrsize)
break;
/* get size from header */
ibuf_from_buffer(&msg, ibuf_data(&rbuf),
msgbuf->hdrsize);
if ((msgbuf->rpmsg = msgbuf->readhdr(&msg,
msgbuf->rarg, &fd)) == NULL)
goto fail;
}
if (ibuf_left(msgbuf->rpmsg) <= ibuf_size(&rbuf))
sz = ibuf_left(msgbuf->rpmsg);
else
sz = ibuf_size(&rbuf);
/* neither call below can fail */
if (ibuf_get_ibuf(&rbuf, sz, &msg) == -1 ||
ibuf_add_ibuf(msgbuf->rpmsg, &msg) == -1)
goto fail;
if (ibuf_left(msgbuf->rpmsg) == 0) {
msgbuf_read_enqueue(msgbuf, msgbuf->rpmsg);
msgbuf->rpmsg = NULL;
}
} while (ibuf_size(&rbuf) > 0);
if (ibuf_size(&rbuf) > 0)
memmove(msgbuf->rbuf, ibuf_data(&rbuf), ibuf_size(&rbuf));
msgbuf->roff = ibuf_size(&rbuf);
if (fd != -1)
close(fd);
return (1);
fail:
/* XXX how to properly clean up is unclear */
if (fd != -1)
close(fd);
return (-1);
}
int
ibuf_read(int fd, struct msgbuf *msgbuf)
{
struct iovec iov;
ssize_t n;
if (msgbuf->rbuf == NULL) {
errno = EINVAL;
return (-1);
}
iov.iov_base = msgbuf->rbuf + msgbuf->roff;
iov.iov_len = IBUF_READ_SIZE - msgbuf->roff;
again:
if ((n = readv(fd, &iov, 1)) == -1) {
if (errno == EINTR)
goto again;
if (errno == EAGAIN)
/* lets retry later again */
return (1);
return (-1);
}
if (n == 0) /* connection closed */
return (0);
msgbuf->roff += n;
/* new data arrived, try to process it */
return (ibuf_read_process(msgbuf, -1));
}
int
msgbuf_read(int fd, struct msgbuf *msgbuf)
{
struct msghdr msg;
struct cmsghdr *cmsg;
union {
struct cmsghdr hdr;
char buf[CMSG_SPACE(sizeof(int) * 1)];
} cmsgbuf;
struct iovec iov;
ssize_t n;
int fdpass = -1;
if (msgbuf->rbuf == NULL) {
errno = EINVAL;
return (-1);
}
memset(&msg, 0, sizeof(msg));
memset(&cmsgbuf, 0, sizeof(cmsgbuf));
iov.iov_base = msgbuf->rbuf + msgbuf->roff;
iov.iov_len = IBUF_READ_SIZE - msgbuf->roff;
msg.msg_iov = &iov;
msg.msg_iovlen = 1;
msg.msg_control = &cmsgbuf.buf;
msg.msg_controllen = sizeof(cmsgbuf.buf);
again:
if ((n = recvmsg(fd, &msg, 0)) == -1) {
if (errno == EINTR)
goto again;
if (errno == EMSGSIZE)
/*
* Not enough fd slots: fd passing failed, retry
* to receive the message without fd.
* imsg_get_fd() will return -1 in that case.
*/
goto again;
if (errno == EAGAIN)
/* lets retry later again */
return (1);
return (-1);
}
if (n == 0) /* connection closed */
return (0);
msgbuf->roff += 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, j, f;
/*
* 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++) {
f = ((int *)CMSG_DATA(cmsg))[i];
if (i == 0)
fdpass = f;
else
close(f);
}
}
/* we do not handle other ctl data level */
}
/* new data arrived, try to process it */
return (ibuf_read_process(msgbuf, fdpass));
}
static void
ibuf_enqueue(struct msgbuf *msgbuf, struct ibuf *buf)
msgbuf_read_enqueue(struct msgbuf *msgbuf, struct ibuf *buf)
{
if (buf->max == 0) /* if buf lives on the stack */
abort(); /* abort before causing more harm */
/* if buf lives on the stack abort before causing more harm */
if (buf->fd == IBUF_FD_MARK_ON_STACK)
abort();
TAILQ_INSERT_TAIL(&msgbuf->rbufs, buf, entry);
}
static void
msgbuf_enqueue(struct msgbuf *msgbuf, struct ibuf *buf)
{
/* if buf lives on the stack abort before causing more harm */
if (buf->fd == IBUF_FD_MARK_ON_STACK)
abort();
TAILQ_INSERT_TAIL(&msgbuf->bufs, buf, entry);
msgbuf->queued++;
}
static void
ibuf_dequeue(struct msgbuf *msgbuf, struct ibuf *buf)
msgbuf_dequeue(struct msgbuf *msgbuf, struct ibuf *buf)
{
TAILQ_REMOVE(&msgbuf->bufs, buf, entry);
msgbuf->queued--;
ibuf_free(buf);
}
static void
msgbuf_drain(struct msgbuf *msgbuf, size_t n)
{
struct ibuf *buf, *next;
for (buf = TAILQ_FIRST(&msgbuf->bufs); buf != NULL && n > 0;
buf = next) {
next = TAILQ_NEXT(buf, entry);
if (n >= ibuf_size(buf)) {
n -= ibuf_size(buf);
msgbuf_dequeue(msgbuf, buf);
} else {
buf->rpos += n;
n = 0;
}
}
}

View File

@ -1,4 +1,4 @@
/* $OpenBSD: imsg.c,v 1.23 2023/12/12 15:47:41 claudio Exp $ */
/* $OpenBSD: imsg.c,v 1.37 2024/11/26 13:57:31 claudio Exp $ */
/*
* Copyright (c) 2023 Claudio Jeker <claudio@openbsd.org>
@ -22,6 +22,7 @@
#include <sys/uio.h>
#include <errno.h>
#include <stddef.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
@ -29,158 +30,110 @@
#include "compat.h"
#include "imsg.h"
struct imsg_fd {
TAILQ_ENTRY(imsg_fd) entry;
int fd;
};
#define IMSG_ALLOW_FDPASS 0x01
#define IMSG_FD_MARK 0x80000000U
int imsg_fd_overhead = 0;
static struct ibuf *imsg_parse_hdr(struct ibuf *, void *, int *);
static int imsg_dequeue_fd(struct imsgbuf *);
void
imsg_init(struct imsgbuf *imsgbuf, int fd)
int
imsgbuf_init(struct imsgbuf *imsgbuf, int fd)
{
msgbuf_init(&imsgbuf->w);
memset(&imsgbuf->r, 0, sizeof(imsgbuf->r));
imsgbuf->fd = fd;
imsgbuf->w.fd = fd;
imsgbuf->w = msgbuf_new_reader(IMSG_HEADER_SIZE, imsg_parse_hdr,
imsgbuf);
if (imsgbuf->w == NULL)
return (-1);
imsgbuf->pid = getpid();
TAILQ_INIT(&imsgbuf->fds);
imsgbuf->maxsize = MAX_IMSGSIZE;
imsgbuf->fd = fd;
imsgbuf->flags = 0;
return (0);
}
ssize_t
imsg_read(struct imsgbuf *imsgbuf)
void
imsgbuf_allow_fdpass(struct imsgbuf *imsgbuf)
{
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;
imsgbuf->flags |= IMSG_ALLOW_FDPASS;
}
memset(&msg, 0, sizeof(msg));
memset(&cmsgbuf, 0, sizeof(cmsgbuf));
iov.iov_base = imsgbuf->r.buf + imsgbuf->r.wpos;
iov.iov_len = sizeof(imsgbuf->r.buf) - imsgbuf->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);
int
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);
}
if ((n = recvmsg(imsgbuf->fd, &msg, 0)) == -1) {
if (errno == EINTR)
goto again;
goto fail;
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);
}
imsgbuf->r.wpos += n;
void
imsgbuf_clear(struct imsgbuf *imsgbuf)
{
msgbuf_free(imsgbuf->w);
imsgbuf->w = NULL;
}
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(&imsgbuf->fds, ifd,
entry);
ifd = NULL;
} else
close(fd);
}
}
/* we do not handle other ctl data level */
}
fail:
free(ifd);
return (n);
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;
size_t av, left, datalen;
struct ibuf *buf;
av = imsgbuf->r.wpos;
if (IMSG_HEADER_SIZE > av)
if ((buf = msgbuf_get(imsgbuf->w)) == NULL)
return (0);
memcpy(&m.hdr, imsgbuf->r.buf, sizeof(m.hdr));
if (m.hdr.len < IMSG_HEADER_SIZE ||
m.hdr.len > MAX_IMSGSIZE) {
errno = ERANGE;
if (ibuf_get(buf, &m.hdr, sizeof(m.hdr)) == -1)
return (-1);
}
if (m.hdr.len > av)
return (0);
m.fd = -1;
m.buf = NULL;
m.data = NULL;
datalen = m.hdr.len - IMSG_HEADER_SIZE;
imsgbuf->r.rptr = imsgbuf->r.buf + IMSG_HEADER_SIZE;
if (datalen != 0) {
if ((m.buf = ibuf_open(datalen)) == NULL)
return (-1);
if (ibuf_add(m.buf, imsgbuf->r.rptr, datalen) == -1) {
/* this should never fail */
ibuf_free(m.buf);
return (-1);
}
m.data = ibuf_data(m.buf);
}
if (m.hdr.flags & IMSGF_HASFD)
m.fd = imsg_dequeue_fd(imsgbuf);
if (m.hdr.len < av) {
left = av - m.hdr.len;
memmove(&imsgbuf->r.buf, imsgbuf->r.buf + m.hdr.len, left);
imsgbuf->r.wpos = left;
} else
imsgbuf->r.wpos = 0;
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 (datalen + IMSG_HEADER_SIZE);
return (ibuf_size(buf) + IMSG_HEADER_SIZE);
}
int
imsg_get_ibuf(struct imsg *imsg, struct ibuf *ibuf)
{
if (imsg->buf == NULL) {
if (ibuf_size(imsg->buf) == 0) {
errno = EBADMSG;
return (-1);
}
@ -194,7 +147,7 @@ imsg_get_data(struct imsg *imsg, void *data, size_t len)
errno = EINVAL;
return (-1);
}
if (imsg->buf == NULL || ibuf_size(imsg->buf) != len) {
if (ibuf_size(imsg->buf) != len) {
errno = EBADMSG;
return (-1);
}
@ -204,10 +157,7 @@ imsg_get_data(struct imsg *imsg, void *data, size_t len)
int
imsg_get_fd(struct imsg *imsg)
{
int fd = imsg->fd;
imsg->fd = -1;
return fd;
return ibuf_fd_get(imsg->buf);
}
uint32_t
@ -219,8 +169,6 @@ imsg_get_id(struct imsg *imsg)
size_t
imsg_get_len(struct imsg *imsg)
{
if (imsg->buf == NULL)
return 0;
return ibuf_size(imsg->buf);
}
@ -290,14 +238,13 @@ imsg_compose_ibuf(struct imsgbuf *imsgbuf, uint32_t type, uint32_t id,
struct imsg_hdr hdr;
int save_errno;
if (ibuf_size(buf) + IMSG_HEADER_SIZE > MAX_IMSGSIZE) {
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.flags = 0;
hdr.peerid = id;
if ((hdr.pid = pid) == 0)
hdr.pid = imsgbuf->pid;
@ -307,8 +254,8 @@ imsg_compose_ibuf(struct imsgbuf *imsgbuf, uint32_t type, uint32_t id,
if (imsg_add(hdrbuf, &hdr, sizeof(hdr)) == -1)
goto fail;
ibuf_close(&imsgbuf->w, hdrbuf);
ibuf_close(&imsgbuf->w, buf);
ibuf_close(imsgbuf->w, hdrbuf);
ibuf_close(imsgbuf->w, buf);
return (1);
fail:
@ -326,24 +273,18 @@ int
imsg_forward(struct imsgbuf *imsgbuf, struct imsg *msg)
{
struct ibuf *wbuf;
size_t len = 0;
size_t len;
if (msg->fd != -1) {
close(msg->fd);
msg->fd = -1;
}
if (msg->buf != NULL) {
ibuf_rewind(msg->buf);
len = ibuf_size(msg->buf);
}
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 (msg->buf != NULL) {
if (ibuf_add_buf(wbuf, msg->buf) == -1) {
if (len != 0) {
if (ibuf_add_ibuf(wbuf, msg->buf) == -1) {
ibuf_free(wbuf);
return (-1);
}
@ -361,17 +302,16 @@ imsg_create(struct imsgbuf *imsgbuf, uint32_t type, uint32_t id, pid_t pid,
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 = id;
if ((hdr.pid = pid) == 0)
hdr.pid = imsgbuf->pid;
if ((wbuf = ibuf_dynamic(datalen, MAX_IMSGSIZE)) == NULL) {
if ((wbuf = ibuf_dynamic(datalen, imsgbuf->maxsize)) == NULL) {
return (NULL);
}
if (imsg_add(wbuf, &hdr, sizeof(hdr)) == -1)
@ -395,15 +335,13 @@ void
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;
len = ibuf_size(msg);
if (ibuf_fd_avail(msg))
hdr->flags |= IMSGF_HASFD;
hdr->len = ibuf_size(msg);
ibuf_close(&imsgbuf->w, msg);
len |= IMSG_FD_MARK;
(void)ibuf_set_h32(msg, offsetof(struct imsg_hdr, len), len);
ibuf_close(imsgbuf->w, msg);
}
void
@ -412,37 +350,29 @@ imsg_free(struct imsg *imsg)
ibuf_free(imsg->buf);
}
static int
imsg_dequeue_fd(struct imsgbuf *imsgbuf)
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(&imsgbuf->fds)) == NULL)
return (-1);
if (ibuf_get(buf, &hdr, sizeof(hdr)) == -1)
return (NULL);
fd = ifd->fd;
TAILQ_REMOVE(&imsgbuf->fds, ifd, entry);
free(ifd);
len = hdr.len & ~IMSG_FD_MARK;
return (fd);
}
int
imsg_flush(struct imsgbuf *imsgbuf)
{
while (imsgbuf->w.queued)
if (msgbuf_write(&imsgbuf->w) <= 0)
return (-1);
return (0);
}
void
imsg_clear(struct imsgbuf *imsgbuf)
{
int fd;
msgbuf_clear(&imsgbuf->w);
while ((fd = imsg_dequeue_fd(imsgbuf)) != -1)
close(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;
}
return b;
}

View File

@ -1,4 +1,4 @@
/* $OpenBSD: imsg.h,v 1.8 2023/12/12 15:47:41 claudio Exp $ */
/* $OpenBSD: imsg.h,v 1.19 2024/11/26 13:57:31 claudio Exp $ */
/*
* Copyright (c) 2023 Claudio Jeker <claudio@openbsd.org>
@ -38,40 +38,25 @@ struct ibuf {
int fd;
};
struct msgbuf {
TAILQ_HEAD(, ibuf) bufs;
uint32_t queued;
int fd;
};
struct msgbuf;
struct ibuf_read {
unsigned char buf[IBUF_READ_SIZE];
unsigned char *rptr;
size_t wpos;
};
struct imsg_fd;
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;
};
@ -82,7 +67,6 @@ struct iovec;
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_buf(struct ibuf *, const struct ibuf *);
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);
@ -119,20 +103,33 @@ 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 *);
int ibuf_fd_avail(struct ibuf *);
int ibuf_fd_get(struct ibuf *);
void ibuf_fd_set(struct ibuf *, int);
int ibuf_write(struct msgbuf *);
void msgbuf_init(struct msgbuf *);
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 *);
uint32_t msgbuf_queuelen(struct msgbuf *);
int msgbuf_write(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);
@ -152,7 +149,5 @@ 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

View File

@ -30,6 +30,11 @@
#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)
{
@ -70,16 +75,49 @@ fail:
return (-1);
}
int
systemd_move_pid_to_new_cgroup(pid_t pid, char **cause)
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)
{
sd_bus_error error = SD_BUS_ERROR_NULL;
sd_bus_message *m = NULL, *reply = NULL;
sd_bus *bus = NULL;
char *name, *desc, *slice;
sd_id128_t uuid;
int r;
pid_t parent_pid;
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);
@ -89,6 +127,20 @@ systemd_move_pid_to_new_cgroup(pid_t pid, char **cause)
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",
@ -133,7 +185,8 @@ systemd_move_pid_to_new_cgroup(pid_t pid, char **cause)
goto finish;
}
parent_pid = getpid();
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);
@ -218,10 +271,55 @@ systemd_move_pid_to_new_cgroup(pid_t pid, char **cause)
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.5)
AC_INIT([tmux], next-3.6)
AC_PREREQ([2.60])
AC_CONFIG_AUX_DIR(etc)
@ -219,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.
@ -638,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)
@ -982,6 +982,7 @@ 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)

View File

@ -719,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;
@ -729,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);
@ -847,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. */

538
format.c
View File

@ -104,6 +104,9 @@ format_job_cmp(struct format_job *fj1, struct format_job *fj2)
#define FORMAT_CHARACTER 0x10000
#define FORMAT_COLOUR 0x20000
#define FORMAT_CLIENTS 0x40000
#define FORMAT_NOT 0x80000
#define FORMAT_NOT_NOT 0x100000
#define FORMAT_REPEAT 0x200000
/* Limit on recursion. */
#define FORMAT_LOOP_LIMIT 100
@ -129,6 +132,18 @@ enum format_type {
FORMAT_TYPE_PANE
};
/* Format loop sort type. */
enum format_loop_sort_type {
FORMAT_LOOP_BY_INDEX,
FORMAT_LOOP_BY_NAME,
FORMAT_LOOP_BY_TIME,
};
static struct format_loop_sort_criteria {
u_int field;
int reversed;
} format_loop_sort_criteria;
struct format_tree {
enum format_type type;
@ -203,7 +218,7 @@ static const char *format_upper[] = {
"window_name", /* W */
NULL, /* X */
NULL, /* Y */
NULL /* Z */
NULL /* Z */
};
/* Single-character lowercase aliases. */
@ -863,7 +878,7 @@ format_cb_history_bytes(struct format_tree *ft)
struct window_pane *wp = ft->wp;
struct grid *gd;
struct grid_line *gl;
size_t size = 0;
size_t size = 0;
u_int i;
char *value;
@ -1119,6 +1134,20 @@ format_cb_cursor_character(struct format_tree *ft)
return (value);
}
/* Callback for cursor_colour. */
static void *
format_cb_cursor_colour(struct format_tree *ft)
{
struct window_pane *wp = ft->wp;
if (wp == NULL || wp->screen == NULL)
return (NULL);
if (wp->screen->ccolour != -1)
return (xstrdup(colour_tostring(wp->screen->ccolour)));
return (xstrdup(colour_tostring(wp->screen->default_ccolour)));
}
/* Callback for mouse_word. */
static void *
format_cb_mouse_word(struct format_tree *ft)
@ -1159,6 +1188,12 @@ format_cb_mouse_hyperlink(struct format_tree *ft)
return (NULL);
if (cmd_mouse_at(wp, &ft->m, &x, &y, 0) != 0)
return (NULL);
if (!TAILQ_EMPTY(&wp->modes)) {
if (window_pane_mode(wp) != WINDOW_PANE_NO_MODE)
return (window_copy_get_hyperlink(wp, x, y));
return (NULL);
}
gd = wp->base.grid;
return (format_grid_hyperlink(gd, x, gd->hsize + y, wp->screen));
}
@ -1544,6 +1579,23 @@ format_cb_client_written(struct format_tree *ft)
return (NULL);
}
/* Callback for client_theme. */
static void *
format_cb_client_theme(struct format_tree *ft)
{
if (ft->c != NULL) {
switch (ft->c->theme) {
case THEME_DARK:
return (xstrdup("dark"));
case THEME_LIGHT:
return (xstrdup("light"));
case THEME_UNKNOWN:
return (NULL);
}
}
return (NULL);
}
/* Callback for config_files. */
static void *
format_cb_config_files(__unused struct format_tree *ft)
@ -1576,6 +1628,37 @@ format_cb_cursor_flag(struct format_tree *ft)
return (NULL);
}
/* Callback for cursor_shape. */
static void *
format_cb_cursor_shape(struct format_tree *ft)
{
if (ft->wp != NULL && ft->wp->screen != NULL) {
switch (ft->wp->screen->cstyle) {
case SCREEN_CURSOR_BLOCK:
return (xstrdup("block"));
case SCREEN_CURSOR_UNDERLINE:
return (xstrdup("underline"));
case SCREEN_CURSOR_BAR:
return (xstrdup("bar"));
default:
return (xstrdup("default"));
}
}
return (NULL);
}
/* Callback for cursor_very_visible. */
static void *
format_cb_cursor_very_visible(struct format_tree *ft)
{
if (ft->wp != NULL && ft->wp->screen != NULL) {
if (ft->wp->screen->mode & MODE_CURSOR_VERY_VISIBLE)
return (xstrdup("1"));
return (xstrdup("0"));
}
return (NULL);
}
/* Callback for cursor_x. */
static void *
format_cb_cursor_x(struct format_tree *ft)
@ -1594,6 +1677,18 @@ format_cb_cursor_y(struct format_tree *ft)
return (NULL);
}
/* Callback for cursor_blinking. */
static void *
format_cb_cursor_blinking(struct format_tree *ft)
{
if (ft->wp != NULL && ft->wp->screen != NULL) {
if (ft->wp->screen->mode & MODE_CURSOR_BLINKING)
return (xstrdup("1"));
return (xstrdup("0"));
}
return (NULL);
}
/* Callback for history_limit. */
static void *
format_cb_history_limit(struct format_tree *ft)
@ -2320,6 +2415,17 @@ format_cb_version(__unused struct format_tree *ft)
return (xstrdup(getversion()));
}
/* Callback for sixel_support. */
static void *
format_cb_sixel_support(__unused struct format_tree *ft)
{
#ifdef ENABLE_SIXEL
return (xstrdup("1"));
#else
return (xstrdup("0"));
#endif
}
/* Callback for active_window_index. */
static void *
format_cb_active_window_index(struct format_tree *ft)
@ -2483,9 +2589,20 @@ format_cb_window_last_flag(struct format_tree *ft)
static void *
format_cb_window_linked(struct format_tree *ft)
{
struct winlink *wl;
struct session *s;
int found = 0;
if (ft->wl != NULL) {
if (session_is_linked(ft->wl->session, ft->wl->window))
return (xstrdup("1"));
RB_FOREACH(s, sessions, &sessions) {
RB_FOREACH(wl, winlinks, &s->windows) {
if (wl->window == ft->wl->window) {
if (found)
return (xstrdup("1"));
found = 1;
}
}
}
return (xstrdup("0"));
}
return (NULL);
@ -2495,9 +2612,27 @@ format_cb_window_linked(struct format_tree *ft)
static void *
format_cb_window_linked_sessions(struct format_tree *ft)
{
if (ft->wl != NULL)
return (format_printf("%u", ft->wl->window->references));
return (NULL);
struct window *w;
struct session_group *sg;
struct session *s;
u_int n = 0;
if (ft->wl == NULL)
return (NULL);
w = ft->wl->window;
RB_FOREACH(sg, session_groups, &session_groups) {
s = TAILQ_FIRST(&sg->sessions);
if (winlink_find_by_window(&s->windows, w) != NULL)
n++;
}
RB_FOREACH(s, sessions, &sessions) {
if (session_group_contains(s) != NULL)
continue;
if (winlink_find_by_window(&s->windows, w) != NULL)
n++;
}
return (format_printf("%u", n));
}
/* Callback for window_marked_flag. */
@ -2841,6 +2976,9 @@ static const struct format_table_entry format_table[] = {
{ "client_termtype", FORMAT_TABLE_STRING,
format_cb_client_termtype
},
{ "client_theme", FORMAT_TABLE_STRING,
format_cb_client_theme
},
{ "client_tty", FORMAT_TABLE_STRING,
format_cb_client_tty
},
@ -2862,12 +3000,24 @@ static const struct format_table_entry format_table[] = {
{ "config_files", FORMAT_TABLE_STRING,
format_cb_config_files
},
{ "cursor_blinking", FORMAT_TABLE_STRING,
format_cb_cursor_blinking
},
{ "cursor_character", FORMAT_TABLE_STRING,
format_cb_cursor_character
},
{ "cursor_colour", FORMAT_TABLE_STRING,
format_cb_cursor_colour
},
{ "cursor_flag", FORMAT_TABLE_STRING,
format_cb_cursor_flag
},
{ "cursor_shape", FORMAT_TABLE_STRING,
format_cb_cursor_shape
},
{ "cursor_very_visible", FORMAT_TABLE_STRING,
format_cb_cursor_very_visible
},
{ "cursor_x", FORMAT_TABLE_STRING,
format_cb_cursor_x
},
@ -3147,6 +3297,9 @@ static const struct format_table_entry format_table[] = {
{ "session_windows", FORMAT_TABLE_STRING,
format_cb_session_windows
},
{ "sixel_support", FORMAT_TABLE_STRING,
format_cb_sixel_support
},
{ "socket_path", FORMAT_TABLE_STRING,
format_cb_socket_path
},
@ -3532,7 +3685,7 @@ format_quote_style(const char *s)
char *
format_pretty_time(time_t t, int seconds)
{
struct tm now_tm, tm;
struct tm now_tm, tm;
time_t now, age;
char s[9];
@ -3710,7 +3863,7 @@ format_unescape(const char *s)
*s == '#' &&
strchr(",#{}:", s[1]) != NULL) {
*cp++ = *++s;
continue;
continue;
}
if (*s == '}')
brackets--;
@ -3852,10 +4005,10 @@ format_build_modifiers(struct format_expand_state *es, const char **s,
/*
* Modifiers are a ; separated list of the forms:
* l,m,C,a,b,c,d,n,t,w,q,E,T,S,W,P,<,>
* l,m,C,a,b,c,d,n,t,w,q,E,T,S,W,P,R,<,>
* =a
* =/a
* =/a/
* =/a/
* s/a/b/
* s/a/b
* ||,&&,!=,==,<=,>=
@ -3869,7 +4022,7 @@ format_build_modifiers(struct format_expand_state *es, const char **s,
cp++;
/* Check single character modifiers with no arguments. */
if (strchr("labcdnwETSWPL<>", cp[0]) != NULL &&
if (strchr("labcdnwETSWPL!<>", cp[0]) != NULL &&
format_is_end(cp[1])) {
format_add_modifier(&list, count, cp, 1, NULL, 0);
cp++;
@ -3879,6 +4032,7 @@ format_build_modifiers(struct format_expand_state *es, const char **s,
/* Then try double character with no arguments. */
if ((memcmp("||", cp, 2) == 0 ||
memcmp("&&", cp, 2) == 0 ||
memcmp("!!", cp, 2) == 0 ||
memcmp("!=", cp, 2) == 0 ||
memcmp("==", cp, 2) == 0 ||
memcmp("<=", cp, 2) == 0 ||
@ -3890,7 +4044,7 @@ format_build_modifiers(struct format_expand_state *es, const char **s,
}
/* Now try single character with arguments. */
if (strchr("mCNst=peq", cp[0]) == NULL)
if (strchr("mCNSst=pReq", cp[0]) == NULL)
break;
c = cp[0];
@ -4014,6 +4168,60 @@ format_search(struct format_modifier *fm, struct window_pane *wp, const char *s)
return (value);
}
/* Handle unary boolean operators, "!" and "!!". */
static char *
format_bool_op_1(struct format_expand_state *es, const char *fmt, int not)
{
int result;
char *expanded;
expanded = format_expand1(es, fmt);
result = format_true(expanded);
if (not)
result = !result;
free(expanded);
return (xstrdup(result ? "1" : "0"));
}
/* Handle n-ary boolean operators, "&&" and "||". */
static char *
format_bool_op_n(struct format_expand_state *es, const char *fmt, int and)
{
int result;
const char *cp1, *cp2;
char *raw, *expanded;
result = and ? 1 : 0;
cp1 = fmt;
while (and ? result : !result) {
cp2 = format_skip(cp1, ",");
if (cp2 == NULL)
raw = xstrdup(cp1);
else
raw = xstrndup(cp1, cp2 - cp1);
expanded = format_expand1(es, raw);
free(raw);
format_log(es, "operator %s has operand: %s",
and ? "&&" : "||", expanded);
if (and)
result = result && format_true(expanded);
else
result = result || format_true(expanded);
free(expanded);
if (cp2 == NULL)
break;
else
cp1 = cp2 + 1;
}
return (xstrdup(result ? "1" : "0"));
}
/* Does session name exist? */
static char *
format_session_name(struct format_expand_state *es, const char *fmt)
@ -4032,29 +4240,86 @@ format_session_name(struct format_expand_state *es, const char *fmt)
return (xstrdup("0"));
}
static int
format_cmp_session(const void *a0, const void *b0)
{
const struct session *const *a = a0;
const struct session *const *b = b0;
const struct session *sa = *a;
const struct session *sb = *b;
int result = 0;
switch (format_loop_sort_criteria.field) {
case FORMAT_LOOP_BY_INDEX:
result = sa->id - sb->id;
break;
case FORMAT_LOOP_BY_TIME:
if (timercmp(&sa->activity_time, &sb->activity_time, >)) {
result = -1;
break;
}
if (timercmp(&sa->activity_time, &sb->activity_time, <)) {
result = 1;
break;
}
/* FALLTHROUGH */
case FORMAT_LOOP_BY_NAME:
result = strcmp(sa->name, sb->name);
break;
}
if (format_loop_sort_criteria.reversed)
result = -result;
return (result);
}
/* Loop over sessions. */
static char *
format_loop_sessions(struct format_expand_state *es, const char *fmt)
{
struct format_tree *ft = es->ft;
struct client *c = ft->client;
struct cmdq_item *item = ft->item;
struct format_tree *nft;
struct format_expand_state next;
char *expanded, *value;
size_t valuelen;
struct session *s;
struct format_tree *ft = es->ft;
struct client *c = ft->client;
struct cmdq_item *item = ft->item;
struct format_tree *nft;
struct format_expand_state next;
char *all, *active, *use, *expanded, *value;
size_t valuelen;
struct session *s;
int i, n;
static struct session **l = NULL;
static int lsz = 0;
if (format_choose(es, fmt, &all, &active, 0) != 0) {
all = xstrdup(fmt);
active = NULL;
}
n = 0;
RB_FOREACH(s, sessions, &sessions) {
if (lsz <= n) {
lsz += 100;
l = xreallocarray(l, lsz, sizeof *l);
}
l[n++] = s;
}
qsort(l, n, sizeof *l, format_cmp_session);
value = xcalloc(1, 1);
valuelen = 1;
RB_FOREACH(s, sessions, &sessions) {
for (i = 0; i < n; i++) {
s = l[i];
format_log(es, "session loop: $%u", s->id);
if (active != NULL && s->id == ft->c->session->id)
use = active;
else
use = all;
nft = format_create(c, item, FORMAT_NONE, ft->flags);
format_defaults(nft, ft->c, s, NULL, NULL);
format_copy_state(&next, es, 0);
next.ft = nft;
expanded = format_expand1(&next, fmt);
expanded = format_expand1(&next, use);
format_free(next.ft);
valuelen += strlen(expanded);
@ -4380,7 +4645,8 @@ format_replace(struct format_expand_state *es, const char *key, size_t keylen,
{
struct format_tree *ft = es->ft;
struct window_pane *wp = ft->wp;
const char *errstr, *copy, *cp, *marker = NULL;
const char *errstr, *copy, *cp, *cp2;
const char *marker = NULL;
const char *time_format = NULL;
char *copy0, *condition, *found, *new;
char *value, *left, *right;
@ -4389,8 +4655,10 @@ format_replace(struct format_expand_state *es, const char *key, size_t keylen,
int j, c;
struct format_modifier *list, *cmp = NULL, *search = NULL;
struct format_modifier **sub = NULL, *mexp = NULL, *fm;
u_int i, count, nsub = 0;
struct format_modifier *bool_op_n = NULL;
u_int i, count, nsub = 0, nrep;
struct format_expand_state next;
struct format_loop_sort_criteria *sc = &format_loop_sort_criteria;
/* Make a copy of the key. */
copy = copy0 = xstrndup(key, keylen);
@ -4413,6 +4681,9 @@ format_replace(struct format_expand_state *es, const char *key, size_t keylen,
case '>':
cmp = fm;
break;
case '!':
modifiers |= FORMAT_NOT;
break;
case 'C':
search = fm;
break;
@ -4498,6 +4769,19 @@ format_replace(struct format_expand_state *es, const char *key, size_t keylen,
break;
case 'S':
modifiers |= FORMAT_SESSIONS;
if (fm->argc < 1)
break;
if (strchr(fm->argv[0], 'i') != NULL)
sc->field = FORMAT_LOOP_BY_INDEX;
else if (strchr(fm->argv[0], 'n') != NULL)
sc->field = FORMAT_LOOP_BY_NAME;
else if (strchr(fm->argv[0], 't') != NULL)
sc->field = FORMAT_LOOP_BY_TIME;
else sc->field = FORMAT_LOOP_BY_INDEX;
if (strchr(fm->argv[0], 'r') != NULL)
sc->reversed = 1;
else
sc->reversed = 0;
break;
case 'W':
modifiers |= FORMAT_WINDOWS;
@ -4508,11 +4792,17 @@ format_replace(struct format_expand_state *es, const char *key, size_t keylen,
case 'L':
modifiers |= FORMAT_CLIENTS;
break;
case 'R':
modifiers |= FORMAT_REPEAT;
break;
}
} else if (fm->size == 2) {
if (strcmp(fm->modifier, "||") == 0 ||
strcmp(fm->modifier, "&&") == 0 ||
strcmp(fm->modifier, "==") == 0 ||
strcmp(fm->modifier, "&&") == 0)
bool_op_n = fm;
else if (strcmp(fm->modifier, "!!") == 0)
modifiers |= FORMAT_NOT_NOT;
else if (strcmp(fm->modifier, "==") == 0 ||
strcmp(fm->modifier, "!=") == 0 ||
strcmp(fm->modifier, ">=") == 0 ||
strcmp(fm->modifier, "<=") == 0)
@ -4551,7 +4841,7 @@ format_replace(struct format_expand_state *es, const char *key, size_t keylen,
goto done;
}
/* Is this a loop, comparison or condition? */
/* Is this a loop, operator, comparison or condition? */
if (modifiers & FORMAT_SESSIONS) {
value = format_loop_sessions(es, copy);
if (value == NULL)
@ -4587,6 +4877,35 @@ format_replace(struct format_expand_state *es, const char *key, size_t keylen,
value = format_search(search, wp, new);
}
free(new);
} else if (modifiers & FORMAT_REPEAT) {
/* Repeat multiple times. */
if (format_choose(es, copy, &left, &right, 1) != 0) {
format_log(es, "repeat syntax error: %s", copy);
goto fail;
}
nrep = strtonum(right, 1, 10000, &errstr);
if (errstr != NULL)
value = xstrdup("");
else {
value = xstrdup("");
for (i = 0; i < nrep; i++) {
xasprintf(&new, "%s%s", value, left);
free(value);
value = new;
}
}
free(right);
free(left);
} else if (modifiers & FORMAT_NOT) {
value = format_bool_op_1(es, copy, 1);
} else if (modifiers & FORMAT_NOT_NOT) {
value = format_bool_op_1(es, copy, 0);
} else if (bool_op_n != NULL) {
/* n-ary boolean operator. */
if (strcmp(bool_op_n->modifier, "||") == 0)
value = format_bool_op_n(es, copy, 0);
else if (strcmp(bool_op_n->modifier, "&&") == 0)
value = format_bool_op_n(es, copy, 1);
} else if (cmp != NULL) {
/* Comparison of left and right. */
if (format_choose(es, copy, &left, &right, 1) != 0) {
@ -4597,17 +4916,7 @@ format_replace(struct format_expand_state *es, const char *key, size_t keylen,
format_log(es, "compare %s left is: %s", cmp->modifier, left);
format_log(es, "compare %s right is: %s", cmp->modifier, right);
if (strcmp(cmp->modifier, "||") == 0) {
if (format_true(left) || format_true(right))
value = xstrdup("1");
else
value = xstrdup("0");
} else if (strcmp(cmp->modifier, "&&") == 0) {
if (format_true(left) && format_true(right))
value = xstrdup("1");
else
value = xstrdup("0");
} else if (strcmp(cmp->modifier, "==") == 0) {
if (strcmp(cmp->modifier, "==") == 0) {
if (strcmp(left, right) == 0)
value = xstrdup("1");
else
@ -4643,53 +4952,81 @@ format_replace(struct format_expand_state *es, const char *key, size_t keylen,
free(right);
free(left);
} else if (*copy == '?') {
/* Conditional: check first and choose second or third. */
cp = format_skip(copy + 1, ",");
if (cp == NULL) {
format_log(es, "condition syntax error: %s", copy + 1);
goto fail;
}
condition = xstrndup(copy + 1, cp - (copy + 1));
format_log(es, "condition is: %s", condition);
found = format_find(ft, condition, modifiers, time_format);
if (found == NULL) {
/*
* If the condition not found, try to expand it. If
* the expansion doesn't have any effect, then assume
* false.
*/
found = format_expand1(es, condition);
if (strcmp(found, condition) == 0) {
free(found);
found = xstrdup("");
/*
* Conditional: For each pair of (condition, value), check the
* condition and return the value if true. If no condition
* matches, return the last unpaired arg if there is one, or the
* empty string if not.
*/
cp = copy + 1;
while (1) {
cp2 = format_skip(cp, ",");
if (cp2 == NULL) {
format_log(es,
"condition '%s' not found; assuming false",
"no condition matched in '%s'; using last "
"arg", copy + 1);
value = format_expand1(es, cp);
break;
}
condition = xstrndup(cp, cp2 - cp);
format_log(es, "condition is: %s", condition);
found = format_find(ft, condition, modifiers,
time_format);
if (found == NULL) {
/*
* If the condition not found, try to expand it.
* If the expansion doesn't have any effect,
* then assume false.
*/
found = format_expand1(es, condition);
if (strcmp(found, condition) == 0) {
free(found);
found = xstrdup("");
format_log(es,
"condition '%s' not found; "
"assuming false",
condition);
}
} else {
format_log(es, "condition '%s' found: %s",
condition, found);
}
cp = cp2 + 1;
cp2 = format_skip(cp, ",");
if (format_true(found)) {
format_log(es, "condition '%s' is true",
condition);
if (cp2 == NULL)
value = format_expand1(es, cp);
else {
right = xstrndup(cp, cp2 - cp);
value = format_expand1(es, right);
free(right);
}
free(condition);
free(found);
break;
} else {
format_log(es, "condition '%s' is false",
condition);
}
} else {
format_log(es, "condition '%s' found: %s", condition,
found);
}
if (format_choose(es, cp + 1, &left, &right, 0) != 0) {
format_log(es, "condition '%s' syntax error: %s",
condition, cp + 1);
free(condition);
free(found);
goto fail;
}
if (format_true(found)) {
format_log(es, "condition '%s' is true", condition);
value = format_expand1(es, left);
} else {
format_log(es, "condition '%s' is false", condition);
value = format_expand1(es, right);
}
free(right);
free(left);
free(condition);
free(found);
if (cp2 == NULL) {
format_log(es,
"no condition matched in '%s'; using empty "
"string", copy + 1);
value = xstrdup("");
break;
}
cp = cp2 + 1;
}
} else if (mexp != NULL) {
value = format_replace_expression(mexp, es, copy);
if (value == NULL)
@ -4819,7 +5156,7 @@ format_expand1(struct format_expand_state *es, const char *fmt)
char *buf, *out, *name;
const char *ptr, *s, *style_end = NULL;
size_t off, len, n, outlen;
int ch, brackets;
int ch, brackets;
char expanded[8192];
if (fmt == NULL || *fmt == '\0')
@ -5178,6 +5515,16 @@ format_defaults_paste_buffer(struct format_tree *ft, struct paste_buffer *pb)
ft->pb = pb;
}
static int
format_is_word_separator(const char *ws, const struct grid_cell *gc)
{
if (utf8_cstrhas(ws, &gc->data))
return (1);
if (gc->flags & GRID_FLAG_TAB)
return (1);
return gc->data.size == 1 && *gc->data.data == ' ';
}
/* Return word at given coordinates. Caller frees. */
char *
format_grid_word(struct grid *gd, u_int x, u_int y)
@ -5195,10 +5542,8 @@ format_grid_word(struct grid *gd, u_int x, u_int y)
for (;;) {
grid_get_cell(gd, x, y, &gc);
if (gc.flags & GRID_FLAG_PADDING)
break;
if (utf8_cstrhas(ws, &gc.data) ||
(gc.data.size == 1 && *gc.data.data == ' ')) {
if ((~gc.flags & GRID_FLAG_PADDING) &&
format_is_word_separator(ws, &gc)) {
found = 1;
break;
}
@ -5234,9 +5579,8 @@ format_grid_word(struct grid *gd, u_int x, u_int y)
grid_get_cell(gd, x, y, &gc);
if (gc.flags & GRID_FLAG_PADDING)
break;
if (utf8_cstrhas(ws, &gc.data) ||
(gc.data.size == 1 && *gc.data.data == ' '))
continue;
if (format_is_word_separator(ws, &gc))
break;
ud = xreallocarray(ud, size + 2, sizeof *ud);
@ -5263,10 +5607,13 @@ format_grid_line(struct grid *gd, u_int y)
for (x = 0; x < grid_line_length(gd, y); x++) {
grid_get_cell(gd, x, y, &gc);
if (gc.flags & GRID_FLAG_PADDING)
break;
continue;
ud = xreallocarray(ud, size + 2, sizeof *ud);
memcpy(&ud[size++], &gc.data, sizeof *ud);
if (gc.flags & GRID_FLAG_TAB)
utf8_set(&ud[size++], '\t');
else
memcpy(&ud[size++], &gc.data, sizeof *ud);
}
if (size != 0) {
ud[size].size = 0;
@ -5283,9 +5630,14 @@ format_grid_hyperlink(struct grid *gd, u_int x, u_int y, struct screen* s)
const char *uri;
struct grid_cell gc;
grid_get_cell(gd, x, y, &gc);
if (gc.flags & GRID_FLAG_PADDING)
return (NULL);
for (;;) {
grid_get_cell(gd, x, y, &gc);
if (~gc.flags & GRID_FLAG_PADDING)
break;
if (x == 0)
return (NULL);
x--;
}
if (s->hyperlinks == NULL || gc.link == 0)
return (NULL);
if (!hyperlinks_get(s->hyperlinks, gc.link, &uri, NULL, NULL))

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;

74
grid.c
View File

@ -84,7 +84,7 @@ 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);
@ -92,6 +92,8 @@ grid_need_extended_cell(const struct grid_cell_entry *gce,
return (1);
if (gc->link != 0)
return (1);
if (gc->flags & GRID_FLAG_TAB)
return (1);
return (0);
}
@ -124,7 +126,10 @@ grid_extended_cell(struct grid_line *gl, struct grid_cell_entry *gce,
fatalx("offset too big");
gl->flags |= GRID_LINE_EXTENDED;
utf8_from_data(&gc->data, &uc);
if (gc->flags & GRID_FLAG_TAB)
uc = gc->data.width;
else
utf8_from_data(&gc->data, &uc);
gee = &gl->extddata[gce->offset];
gee->data = uc;
@ -230,9 +235,13 @@ 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);
@ -252,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)
@ -515,7 +534,11 @@ grid_get_cell1(struct grid_line *gl, u_int px, struct grid_cell *gc)
gc->bg = gee->bg;
gc->us = gee->us;
gc->link = gee->link;
utf8_to_data(gee->data, &gc->data);
if (gc->flags & GRID_FLAG_TAB)
grid_set_tab(gc, gee->data);
else
utf8_to_data(gee->data, &gc->data);
}
return;
}
@ -1077,13 +1100,18 @@ grid_string_cells(struct grid *gd, u_int px, u_int py, u_int nx,
} else
codelen = 0;
data = gc.data.data;
size = gc.data.size;
if ((flags & GRID_STRING_ESCAPE_SEQUENCES) &&
size == 1 &&
*data == '\\') {
data = "\\\\";
size = 2;
if (gc.flags & GRID_FLAG_TAB) {
data = "\t";
size = 1;
} else {
data = gc.data.data;
size = gc.data.size;
if ((flags & GRID_STRING_ESCAPE_SEQUENCES) &&
size == 1 &&
*data == '\\') {
data = "\\\\";
size = 2;
}
}
while (len < off + size + codelen + 1) {
@ -1533,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));
}

View File

@ -37,8 +37,13 @@ struct sixel_image {
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;
@ -47,6 +52,19 @@ struct sixel_image {
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)
{
@ -166,6 +184,10 @@ sixel_parse_attributes(struct sixel_image *si, const char *cp, const char *end)
si->x = x;
sixel_parse_expand_lines(si, y);
si->set_ra = 1;
si->ra_x = x;
si->ra_y = y;
return (last);
}
@ -268,7 +290,7 @@ sixel_parse_repeat(struct sixel_image *si, const char *cp, const char *end)
}
struct sixel_image *
sixel_parse(const char *buf, size_t len, u_int xpixel, u_int ypixel)
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;
@ -282,6 +304,7 @@ sixel_parse(const char *buf, size_t len, u_int xpixel, u_int ypixel)
si = xcalloc (1, sizeof *si);
si->xpixel = xpixel;
si->ypixel = ypixel;
si->p2 = p2;
while (cp != end) {
ch = *cp++;
@ -423,6 +446,18 @@ sixel_scale(struct sixel_image *si, u_int xpixel, u_int ypixel, u_int ox,
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);
@ -474,13 +509,65 @@ sixel_print_repeat(char **buf, size_t *len, size_t *used, u_int count, char ch)
}
}
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], *contains, data, last = 0;
char *buf, tmp[64];
size_t len, used = 0, tmplen;
u_int *colours, ncolours, i, c, x, y, count;
struct sixel_line *sl;
u_int *colours, ncolours, i, c, y, *active, nactive;
struct sixel_chunk *chunks, *chunk;
if (map != NULL) {
colours = map->colours;
@ -492,70 +579,55 @@ sixel_print(struct sixel_image *si, struct sixel_image *map, size_t *size)
if (ncolours == 0)
return (NULL);
contains = xcalloc(1, ncolours);
len = 8192;
buf = xmalloc(len);
sixel_print_add(&buf, &len, &used, "\033Pq", 3);
tmplen = xsnprintf(tmp, sizeof tmp, "\"1;1;%u;%u", si->x, si->y);
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) {
memset(contains, 0, ncolours);
for (x = 0; x < si->x; x++) {
for (i = 0; i < 6; i++) {
if (y + i >= si->y)
break;
sl = &si->lines[y + i];
if (x < sl->x && sl->data[x] != 0)
contains[sl->data[x] - 1] = 1;
}
}
nactive = 0;
sixel_print_compress_colors(si, chunks, y, active, &nactive);
for (c = 0; c < ncolours; c++) {
if (!contains[c])
continue;
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);
count = 0;
for (x = 0; x < si->x; x++) {
data = 0;
for (i = 0; i < 6; i++) {
if (y + i >= si->y)
break;
sl = &si->lines[y + i];
if (x < sl->x && sl->data[x] == c + 1)
data |= (1 << i);
}
data += 0x3f;
if (data != last) {
sixel_print_repeat(&buf, &len, &used,
count, last);
last = data;
count = 1;
} else
count++;
}
sixel_print_repeat(&buf, &len, &used, count, data);
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--;
if (buf[used - 1] != '-')
sixel_print_add(&buf, &len, &used, "-", 1);
sixel_print_add(&buf, &len, &used, "-", 1);
}
if (buf[used - 1] == '$' || buf[used - 1] == '-')
if (buf[used - 1] == '-')
used--;
sixel_print_add(&buf, &len, &used, "\033\\", 2);
@ -564,7 +636,11 @@ sixel_print(struct sixel_image *si, struct sixel_image *map, size_t *size)
if (size != NULL)
*size = used;
free(contains);
for (i = 0; i < ncolours; i++)
free(chunks[i].data);
free(active);
free(chunks);
return (buf);
}

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,
@ -307,6 +313,12 @@ 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,
@ -498,9 +510,12 @@ input_key_vt10x(struct bufferevent *bev, key_code key)
return (0);
}
/* Prevent TAB and RET from being swallowed by C0 remapping logic. */
/*
* Prevent TAB, CR and LF from being swallowed by the C0 remapping
* logic.
*/
onlykey = key & KEYC_MASK_KEY;
if (onlykey == '\r' || onlykey == '\t')
if (onlykey == '\r' || onlykey == '\n' || onlykey == '\t')
key &= ~KEYC_CTRL;
/*
@ -539,12 +554,16 @@ input_key_mode1(struct bufferevent *bev, key_code key)
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_META | KEYC_CTRL)) == KEYC_CTRL &&
if ((key & KEYC_CTRL) &&
(onlykey == ' ' ||
onlykey == '/' ||
onlykey == '@' ||
@ -553,13 +572,6 @@ input_key_mode1(struct bufferevent *bev, key_code key)
(onlykey >= '@' && onlykey <= '~')))
return (input_key_vt10x(bev, key));
/*
* A regular key + Meta. In the absence of a standard to back this, we
* mimic what iTerm 2 does.
*/
if ((key & (KEYC_CTRL | KEYC_META)) == KEYC_META)
return (input_key_vt10x(bev, key));
return (-1);
}
@ -585,14 +597,29 @@ input_key(struct screen *s, struct bufferevent *bev, key_code key)
/* 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));
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 & EXTENDED_KEY_MODES) != 0) {
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 {
@ -641,8 +668,7 @@ input_key(struct screen *s, struct bufferevent *bev, key_code key)
if (ike != NULL) {
log_debug("%s: found key 0x%llx: \"%s\"", __func__, key,
ike->data);
if ((key == KEYC_PASTE_START || key == KEYC_PASTE_END) &&
(~s->mode & MODE_BRACKETPASTE))
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);

198
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;
@ -134,7 +133,7 @@ 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 *);
@ -244,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,
@ -252,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,
@ -260,8 +261,8 @@ enum input_csi_type {
INPUT_CSI_SD,
INPUT_CSI_SGR,
INPUT_CSI_SM,
INPUT_CSI_SM_PRIVATE,
INPUT_CSI_SM_GRAPHICS,
INPUT_CSI_SM_PRIVATE,
INPUT_CSI_SU,
INPUT_CSI_TBC,
INPUT_CSI_VPA,
@ -305,6 +306,8 @@ 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 },
@ -729,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)
@ -1145,7 +1151,6 @@ 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);
@ -1194,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);
}
@ -1214,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 */
@ -1235,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 */
@ -1349,7 +1375,7 @@ 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, ek;
int i, n, m, ek, set;
u_int cx, bg = ictx->cell.cell.bg;
if (ictx->flags & INPUT_DISCARD)
@ -1509,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:
@ -1596,6 +1636,11 @@ input_csi_dispatch(struct input_ctx *ictx)
if (~ictx->flags & INPUT_LAST)
break;
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++)
screen_write_collect_add(sctx, &ictx->cell.cell);
@ -1754,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;
@ -1849,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;
@ -2320,6 +2371,7 @@ input_dcs_dispatch(struct input_ctx *ictx)
#ifdef ENABLE_SIXEL
struct window *w;
struct sixel_image *si;
int p2;
#endif
if (wp == NULL)
@ -2332,8 +2384,13 @@ input_dcs_dispatch(struct input_ctx *ictx)
#ifdef ENABLE_SIXEL
w = wp->window;
if (buf[0] == 'q') {
si = sixel_parse(buf, len, w->xpixel, w->ypixel);
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);
}
@ -2664,84 +2721,6 @@ bad:
free(id);
}
/*
* Get a client with a foreground for the pane. There isn't much to choose
* between them so just use the first.
*/
static int
input_get_fg_client(struct window_pane *wp)
{
struct window *w = wp->window;
struct client *loop;
TAILQ_FOREACH(loop, &clients, entry) {
if (loop->flags & CLIENT_UNATTACHEDFLAGS)
continue;
if (loop->session == NULL || !session_has(loop->session, w))
continue;
if (loop->tty.fg == -1)
continue;
return (loop->tty.fg);
}
return (-1);
}
/* Get a client with a background for the pane. */
static int
input_get_bg_client(struct window_pane *wp)
{
struct window *w = wp->window;
struct client *loop;
TAILQ_FOREACH(loop, &clients, entry) {
if (loop->flags & CLIENT_UNATTACHEDFLAGS)
continue;
if (loop->session == NULL || !session_has(loop->session, w))
continue;
if (loop->tty.bg == -1)
continue;
return (loop->tty.bg);
}
return (-1);
}
/*
* If any control mode client exists that has provided a bg color, return it.
* Otherwise, return -1.
*/
static int
input_get_bg_control_client(struct window_pane *wp)
{
struct client *c;
if (wp->control_bg == -1)
return (-1);
TAILQ_FOREACH(c, &clients, entry) {
if (c->flags & CLIENT_CONTROL)
return (wp->control_bg);
}
return (-1);
}
/*
* If any control mode client exists that has provided a fg color, return it.
* Otherwise, return -1.
*/
static int
input_get_fg_control_client(struct window_pane *wp)
{
struct client *c;
if (wp->control_fg == -1)
return (-1);
TAILQ_FOREACH(c, &clients, entry) {
if (c->flags & CLIENT_CONTROL)
return (wp->control_fg);
}
return (-1);
}
/* Handle the OSC 10 sequence for setting and querying foreground colour. */
static void
@ -2754,11 +2733,11 @@ input_osc_10(struct input_ctx *ictx, const char *p)
if (strcmp(p, "?") == 0) {
if (wp == NULL)
return;
c = input_get_fg_control_client(wp);
c = window_pane_get_fg_control_client(wp);
if (c == -1) {
tty_default_colours(&defaults, wp);
if (COLOUR_DEFAULT(defaults.fg))
c = input_get_fg_client(wp);
c = window_pane_get_fg(wp);
else
c = defaults.fg;
}
@ -2799,20 +2778,12 @@ 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)
return;
c = input_get_bg_control_client(wp);
if (c == -1) {
tty_default_colours(&defaults, wp);
if (COLOUR_DEFAULT(defaults.bg))
c = input_get_bg_client(wp);
else
c = defaults.bg;
}
c = window_pane_get_bg(wp);
input_osc_colour_reply(ictx, 11, c);
return;
}
@ -2824,7 +2795,7 @@ input_osc_11(struct input_ctx *ictx, const char *p)
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);
}
}
@ -2840,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);
}
}
@ -3023,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;
}
}

58
job.c
View File

@ -69,18 +69,20 @@ 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;
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], *argv0;
struct options *oo;
/*
* Do not set TERM during .tmux.conf (second argument here), it is nice
@ -91,12 +93,17 @@ job_run(const char *cmd, int argc, char **argv, struct environ *e, struct sessio
if (e != NULL)
environ_copy(e, env);
if (s != NULL)
shell = options_get_string(s->options, "default-shell");
else
shell = options_get_string(global_s_options, "default-shell");
if (!checkshell(shell))
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);
@ -143,24 +150,32 @@ 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)
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");
if (dup2(nullfd, STDERR_FILENO) == -1)
fatal("dup2 failed");
if (nullfd != STDERR_FILENO)
close(nullfd);
}
if (do_close)
close(out[1]);
close(out[0]);
nullfd = open(_PATH_DEVNULL, O_RDWR);
if (nullfd == -1)
fatal("open failed");
if (dup2(nullfd, STDERR_FILENO) == -1)
fatal("dup2 failed");
if (nullfd != STDERR_FILENO)
close(nullfd);
}
closefrom(STDERR_FILENO + 1);
if (cmd != NULL) {
setenv("SHELL", shell, 1);
if (flags & JOB_DEFAULTSHELL)
setenv("SHELL", shell, 1);
execl(shell, argv0, "-c", cmd, (char *)NULL);
fatal("execl failed");
} else {
@ -174,7 +189,7 @@ job_run(const char *cmd, int argc, char **argv, struct environ *e, struct sessio
environ_free(env);
free(argv0);
job = xmalloc(sizeof *job);
job = xcalloc(1, sizeof *job);
job->state = JOB_RUNNING;
job->flags = flags;
@ -183,7 +198,8 @@ 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;
strlcpy(job->tty, tty, sizeof job->tty);
if (flags & JOB_PTY)
strlcpy(job->tty, tty, sizeof job->tty);
job->status = 0;
LIST_INSERT_HEAD(&all_jobs, job, entry);

View File

@ -49,7 +49,7 @@
" '#{?#{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}\"}" \
@ -197,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) {
free((void *)bd->note);
if (note != NULL)
if (note != NULL) {
free((void *)bd->note);
bd->note = xstrdup(note);
else
bd->note = NULL;
}
if (repeat)
bd->flags |= KEY_BINDING_REPEAT;
}
return;
}
@ -433,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 }",
@ -478,6 +480,11 @@ key_bindings_init(void)
"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 }",
@ -489,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",
@ -553,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 }",
@ -570,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 '%%' } }",
@ -581,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 }",
@ -597,14 +604,14 @@ 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 }",
@ -615,7 +622,7 @@ key_bindings_init(void)
"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 }",

110
layout.c
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;
minimum = PANE_MINIMUM;
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;
@ -869,10 +908,12 @@ struct layout_cell *
layout_split_pane(struct window_pane *wp, enum layout_type type, int size,
int flags)
{
struct layout_cell *lc, *lcparent, *lcnew, *lc1, *lc2;
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;
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)
size = parent->sx;
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);
}

1
menu.c
View File

@ -453,7 +453,6 @@ menu_set_style(struct client *c, struct grid_cell *gc, const char *style,
gc->bg = sytmp.gc.bg;
}
}
gc->attr = 0;
}
struct menu_data *

View File

@ -30,6 +30,12 @@ enum mode_tree_search_dir {
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);
@ -52,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;
@ -60,6 +67,7 @@ struct mode_tree_data {
u_int line_size;
u_int depth;
u_int maxdepth;
u_int width;
u_int height;
@ -94,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;
@ -190,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);
@ -278,6 +289,35 @@ mode_tree_down(struct mode_tree_data *mtd, int wrap)
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 *
mode_tree_get_current(struct mode_tree_data *mtd)
{
@ -350,8 +390,13 @@ mode_tree_set_current(struct mode_tree_data *mtd, uint64_t tag)
mtd->offset = 0;
return (1);
}
mtd->current = 0;
mtd->offset = 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);
}
@ -396,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;
@ -414,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) {
@ -436,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);
@ -470,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 {
mtd->height = (screen_size_y(s) / 3) * 2;
if (mtd->height > mtd->line_size)
mtd->height = screen_size_y(s) / 2;
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 (mtd->height < 10)
mtd->height = screen_size_y(s);
if (screen_size_y(s) - mtd->height < 2)
mtd->height = screen_size_y(s);
}
@ -503,6 +563,7 @@ 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 (mtd->line_list != NULL && tag == UINT64_MAX)
@ -510,7 +571,7 @@ mode_tree_build(struct mode_tree_data *mtd)
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);
@ -607,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)
{
@ -633,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;
@ -657,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;
@ -706,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;
@ -742,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];
@ -1016,8 +1101,11 @@ mode_tree_display_menu(struct mode_tree_data *mtd, struct client *c, u_int x,
else
x = 0;
if (menu_display(menu, 0, 0, NULL, x, y, c, BOX_LINES_DEFAULT, NULL,
NULL, NULL, NULL, mode_tree_menu_callback, mtm) != 0)
NULL, NULL, NULL, mode_tree_menu_callback, mtm) != 0) {
mode_tree_remove_ref(mtd);
free(mtm);
menu_free(menu);
}
}
int
@ -1041,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);
}
@ -1102,6 +1190,14 @@ mode_tree_key(struct mode_tree_data *mtd, struct client *c, key_code *key,
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 'b'|KEYC_CTRL:
for (i = 0; i < mtd->height; i++) {
@ -1232,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;
}
@ -1256,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

@ -63,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
};
@ -207,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 }
};
@ -246,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,
@ -269,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,
@ -340,6 +363,15 @@ 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,
@ -373,7 +405,7 @@ const struct options_table_entry options_table[] = {
.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."
"these are only supported on terminals with UTF-8 support."
},
{ .name = "message-limit",
@ -393,7 +425,7 @@ const struct options_table_entry options_table[] = {
.default_num = 0,
.unit = "milliseconds",
.text = "The timeout for the prefix key if no subsequent key is "
"pressed. Zero means disabled."
"pressed. Zero means disabled."
},
{ .name = "prompt-history-limit",
@ -430,7 +462,7 @@ const struct options_table_entry options_table[] = {
.flags = OPTIONS_TABLE_IS_ARRAY,
.default_str = "xterm*:clipboard:ccolour:cstyle:focus:title,"
"screen*:title,"
"rxvt*:ignorefkeys",
"rxvt*:ignorefkeys",
.separator = ",",
.text = "List of terminal features, used if they cannot be "
"automatically detected."
@ -447,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,
@ -571,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,
@ -658,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 "
@ -819,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."
@ -970,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,
@ -1005,7 +1102,7 @@ const struct options_table_entry options_table[] = {
.type = OPTIONS_TABLE_STRING,
.scope = OPTIONS_TABLE_WINDOW,
.flags = OPTIONS_TABLE_IS_STYLE,
.default_str = "bg=yellow,fg=black",
.default_str = "noattr,bg=yellow,fg=black",
.separator = ",",
.text = "Style of indicators and highlighting in modes."
},
@ -1120,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,
@ -1161,12 +1283,12 @@ const struct options_table_entry options_table[] = {
.scope = OPTIONS_TABLE_WINDOW|OPTIONS_TABLE_PANE,
.default_str = "Pane is dead ("
"#{?#{!=:#{pane_dead_status},},"
"status #{pane_dead_status},}"
"status #{pane_dead_status},}"
"#{?#{!=:#{pane_dead_signal},},"
"signal #{pane_dead_signal},}, "
"signal #{pane_dead_signal},}, "
"#{t:pane_dead_time})",
.text = "Message shown after the program in a pane has exited, if "
"remain-on-exit is enabled."
"remain-on-exit is enabled."
},
{ .name = "scroll-on-clear",
@ -1347,6 +1469,8 @@ 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", ""),

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;
@ -737,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, ...)
@ -798,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,
@ -1054,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 &&
@ -1112,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);
@ -1165,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);

View File

@ -240,6 +240,8 @@ paste_rename(const char *oldname, const char *newname, char **cause)
}
pb_new = paste_get_name(newname);
if (pb_new == pb)
return (0);
if (pb_new != NULL)
paste_free(pb_new);

13
popup.c
View File

@ -352,9 +352,11 @@ popup_make_pane(struct popup_data *pd, enum layout_type type)
new_wp = window_add_pane(wp->window, NULL, hlimit, 0);
layout_assign_pane(lc, new_wp, 0);
new_wp->fd = job_transfer(pd->job, &new_wp->pid, new_wp->tty,
sizeof new_wp->tty);
pd->job = NULL;
if (pd->job != NULL) {
new_wp->fd = job_transfer(pd->job, &new_wp->pid, new_wp->tty,
sizeof new_wp->tty);
pd->job = NULL;
}
screen_set_title(&pd->s, new_wp->base.title);
screen_free(&new_wp->base);
@ -528,7 +530,7 @@ popup_key_cb(struct client *c, void *data, struct key_event *event)
(border == LEFT || border == TOP))
goto menu;
if (((m->b & MOUSE_MASK_MODIFIERS) == MOUSE_MASK_META) ||
border != NONE) {
(border != NONE && !MOUSE_DRAG(m->lb))) {
if (!MOUSE_DRAG(m->b))
goto out;
if (MOUSE_BUTTONS(m->lb) == MOUSE_BUTTON_1)
@ -691,6 +693,7 @@ popup_display(int flags, enum box_lines lines, struct cmdq_item *item, u_int px,
pd->border_cell.attr = 0;
screen_init(&pd->s, jx, jy, 0);
screen_set_default_cursor(&pd->s, global_w_options);
colour_palette_init(&pd->palette);
colour_palette_from_option(&pd->palette, global_w_options);
@ -717,7 +720,7 @@ popup_display(int flags, enum box_lines lines, struct cmdq_item *item, u_int px,
pd->job = job_run(shellcmd, argc, argv, env, s, cwd,
popup_job_update_cb, popup_job_complete_cb, NULL, pd,
JOB_NOWAIT|JOB_PTY|JOB_KEEPWRITE, jx, jy);
JOB_NOWAIT|JOB_PTY|JOB_KEEPWRITE|JOB_DEFAULTSHELL, jx, jy);
pd->ictx = input_init(NULL, job_get_event(pd->job), &pd->palette);
server_client_set_overlay(c, 0, popup_check_cb, popup_mode_cb,

22
proc.c
View File

@ -78,8 +78,7 @@ proc_event_cb(__unused int fd, short events, void *arg)
struct imsg imsg;
if (!(peer->flags & PEER_BAD) && (events & EV_READ)) {
if (((n = imsg_read(&peer->ibuf)) == -1 && errno != EAGAIN) ||
n == 0) {
if (imsgbuf_read(&peer->ibuf) != 1) {
peer->dispatchcb(NULL, peer->arg);
return;
}
@ -93,9 +92,6 @@ proc_event_cb(__unused int fd, short events, void *arg)
log_debug("peer %p message %d", peer, imsg.hdr.type);
if (peer_check_version(peer, &imsg) != 0) {
fd = imsg_get_fd(&imsg);
if (fd != -1)
close(fd);
imsg_free(&imsg);
break;
}
@ -106,13 +102,13 @@ proc_event_cb(__unused int fd, short events, void *arg)
}
if (events & EV_WRITE) {
if (msgbuf_write(&peer->ibuf.w) <= 0 && errno != EAGAIN) {
if (imsgbuf_write(&peer->ibuf) == -1) {
peer->dispatchcb(NULL, peer->arg);
return;
}
}
if ((peer->flags & PEER_BAD) && peer->ibuf.w.queued == 0) {
if ((peer->flags & PEER_BAD) && imsgbuf_queuelen(&peer->ibuf) == 0) {
peer->dispatchcb(NULL, peer->arg);
return;
}
@ -153,7 +149,7 @@ proc_update_event(struct tmuxpeer *peer)
event_del(&peer->event);
events = EV_READ;
if (peer->ibuf.w.queued > 0)
if (imsgbuf_queuelen(&peer->ibuf) > 0)
events |= EV_WRITE;
event_set(&peer->event, peer->ibuf.fd, events, proc_event_cb, peer);
@ -225,7 +221,7 @@ proc_exit(struct tmuxproc *tp)
struct tmuxpeer *peer;
TAILQ_FOREACH(peer, &tp->peers, entry)
imsg_flush(&peer->ibuf);
imsgbuf_flush(&peer->ibuf);
tp->exit = 1;
}
@ -313,7 +309,9 @@ proc_add_peer(struct tmuxproc *tp, int fd,
peer->dispatchcb = dispatchcb;
peer->arg = arg;
imsg_init(&peer->ibuf, fd);
if (imsgbuf_init(&peer->ibuf, fd) == -1)
fatal("imsgbuf_init");
imsgbuf_allow_fdpass(&peer->ibuf);
event_set(&peer->event, fd, EV_READ, proc_event_cb, peer);
if (getpeereid(fd, &peer->uid, &gid) != 0)
@ -333,7 +331,7 @@ proc_remove_peer(struct tmuxpeer *peer)
log_debug("remove peer %p", peer);
event_del(&peer->event);
imsg_clear(&peer->ibuf);
imsgbuf_clear(&peer->ibuf);
close(peer->ibuf.fd);
free(peer);
@ -348,7 +346,7 @@ proc_kill_peer(struct tmuxpeer *peer)
void
proc_flush_peer(struct tmuxpeer *peer)
{
imsg_flush(&peer->ibuf);
imsgbuf_flush(&peer->ibuf);
}
void

View File

@ -16,7 +16,7 @@ do_test() {
$TMUX -f/dev/null new -d "
printf '$1'
$TMUX capturep -peS0 -E1 >$TMP"
echo $2 > $TMP2
printf "$2\n" > $TMP2
sleep 1
cmp $TMP $TMP2 || exit 1
return 0

View File

@ -22,8 +22,8 @@ $TMUX -f/dev/null new -d "
sleep 1
(
printf '\033[1m\033[31m\033[42mabc\033[0m\033[31m\033[49mdef\n'
printf '\033[39m\033[100m bright bg\n'
printf '\033[1m\033[31m\033[42mabc\033[0m\033[31mdef\033[39m\n'
printf '\033[100m bright bg \033[49m\n'
) | cmp - $TMP || exit 1
$TMUX has 2>/dev/null && exit 1

View File

@ -6,7 +6,7 @@
👍🏻3
👍🏻 👍🏻4
🤷5
♂️ 7
7
🤷8
🤷9
🤷10

View File

@ -35,7 +35,7 @@ set-option -g default-command 'tmux pipe-pane -o "cat >>~/tmux_logs/output-`date
# from http://stackoverflow.com/questions/17255031/how-to-copy-from-tmux-running-in-putty-to-windows-clipbard
#
# for some reason this is wrapping at 80 cols, using save- instead of show- helps
# -b for background is needed because xclip continues to run to service the clipboard paste reqeusts until the
# -b for background is needed because xclip continues to run to service the clipboard paste requests until the
# clipboard buffer is replaced with some new contents
#bind C-y run-shell -b "tmux save-buffer - | DISPLAY=$(<~/.xdisplay) xclip -selection clipboard -in && tmux display-message 'xclipped successfully'"
bind C-y save-buffer ~/etc/clipboard.pipe
@ -75,4 +75,4 @@ set-window-option -g window-status-current-format "#{?pane_synchronized,#[bg=red
set-option -g window-status-current-style bg=blue
# Toggle input on a pane (from Thomas Sattler)
bind-key R if -F '#{pane_input_off}' "select-pane -e; select-pane -P fg=default" "select-pane -d; select-pane -P fg=yellow"
bind-key R if -F '#{pane_input_off}' "select-pane -e; select-pane -P fg=default" "select-pane -d; select-pane -P fg=yellow"

View File

@ -42,7 +42,7 @@ $TMUX send-keys -X begin-selection
$TMUX send-keys -X next-word-end
$TMUX send-keys -X next-word-end
$TMUX send-keys -X copy-selection
[ "$($TMUX show-buffer)" = "$(printf "words\n Indented")" ] || exit 1
[ "$($TMUX show-buffer)" = "$(printf "words\n\tIndented")" ] || exit 1
# Test that `next-word` wraps around un-indented line breaks.
$TMUX send-keys -X next-word

View File

@ -41,7 +41,7 @@ $TMUX send-keys -X begin-selection
$TMUX send-keys -X next-word-end
$TMUX send-keys -X next-word-end
$TMUX send-keys -X copy-selection
[ "$($TMUX show-buffer)" = "$(printf "words\n Indented")" ] || exit 1
[ "$($TMUX show-buffer)" = "$(printf "words\n\tIndented")" ] || exit 1
# Test that `next-word` wraps around un-indented line breaks.
$TMUX send-keys -X next-word

View File

@ -60,7 +60,13 @@ test_conditional_with_session_name()
$TMUX kill-server 2>/dev/null
$TMUX -f/dev/null new-session -d || exit 1
$TMUX rename-session "Summer" || exit 1 # used later in conditionals
# used later in conditionals
$TMUX rename-session "Summer" || exit 1
$TMUX set @true 1 || exit 1
$TMUX set @false 0 || exit 1
$TMUX set @warm Summer || exit 1
$TMUX set @cold Winter || exit 1
# Plain string without substitutions et al
test_format "abc xyz" "abc xyz"
@ -77,13 +83,21 @@ test_format "###}" "#}" # not a "basic" one but interesting nevertheless
test_format "#{pane_in_mode}" "0"
# Simple conditionals
test_format "#{?}" ""
test_format "#{?abc}" "abc"
test_conditional_with_pane_in_mode "#{?pane_in_mode,abc}" "abc" ""
test_conditional_with_pane_in_mode "#{?pane_in_mode,abc,xyz}" "abc" "xyz"
test_conditional_with_pane_in_mode "#{?pane_in_mode,abc,@true,xyz}" "abc" "xyz"
test_format "#{?@false,abc,@false,xyz}" ""
test_format "#{?@false,abc,@false,xyz,default}" "default"
# Expansion in conditionals
test_conditional_with_pane_in_mode "#{?pane_in_mode,#{session_name},xyz}" "Summer" "xyz"
test_format "#{?#{@warm}}" "Summer"
test_conditional_with_pane_in_mode "#{?#{pane_in_mode},#{@warm}}" "Summer" ""
test_conditional_with_pane_in_mode "#{?#{pane_in_mode},#{@warm},#{@cold}}" "Summer" "Winter"
# Basic escapes in conditionals
# First argument
# Value of an (else-)if-condition
test_conditional_with_pane_in_mode "#{?pane_in_mode,##,xyz}" "#" "xyz"
test_conditional_with_pane_in_mode "#{?pane_in_mode,#,,xyz}" "," "xyz"
test_conditional_with_pane_in_mode "#{?pane_in_mode,{,xyz}" "{" "xyz"
@ -91,7 +105,7 @@ test_conditional_with_pane_in_mode "#{?pane_in_mode,##{,xyz}" "#{" "xyz"
test_conditional_with_pane_in_mode "#{?pane_in_mode,#},xyz}" "}" "xyz"
# not a "basic" one but interesting nevertheless
test_conditional_with_pane_in_mode "#{?pane_in_mode,###},xyz}" "#}" "xyz"
# Second argument
# Default value if no condition matches
test_conditional_with_pane_in_mode "#{?pane_in_mode,abc,##}" "abc" "#"
test_conditional_with_pane_in_mode "#{?pane_in_mode,abc,#,}" "abc" ","
test_conditional_with_pane_in_mode "#{?pane_in_mode,abc,{}" "abc" "{"
@ -105,9 +119,6 @@ test_conditional_with_pane_in_mode "#{?pane_in_mode,#},{}" "}" "{"
test_conditional_with_pane_in_mode "#{?pane_in_mode,##{,###}}" "#{" "#}"
test_conditional_with_pane_in_mode "#{?pane_in_mode,###},##{}" "#}" "#{"
# Conditionals split on the second comma (this is not documented)
test_conditional_with_pane_in_mode "#{?pane_in_mode,abc,xyz,bonus}" "abc" "xyz,bonus"
# Curly brackets {...} do not capture a comma inside of conditionals as the
# conditional ends on the first '}'
test_conditional_with_pane_in_mode "#{?pane_in_mode,{abc,xyz},bonus}" "{abc,bonus}" "xyz,bonus}"
@ -116,12 +127,12 @@ test_conditional_with_pane_in_mode "#{?pane_in_mode,{abc,xyz},bonus}" "{abc,bonu
# invalid format: #{abc,xyz} is not a known variable name.
#test_conditional_with_pane_in_mode "#{?pane_in_mode,#{abc,xyz},bonus}" "" "bonus"
# Parenthesis (...) do not captura a comma
test_conditional_with_pane_in_mode "#{?pane_in_mode,(abc,xyz),bonus}" "(abc" "xyz),bonus"
# Parenthesis (...) do not capture a comma, and "xyz)" is a false condition
test_conditional_with_pane_in_mode "#{?pane_in_mode,(abc,xyz),bonus}" "(abc" ""
test_conditional_with_pane_in_mode "#{?pane_in_mode,(abc#,xyz),bonus}" "(abc,xyz)" "bonus"
# Brackets [...] do not captura a comma
test_conditional_with_pane_in_mode "#{?pane_in_mode,[abc,xyz],bonus}" "[abc" "xyz],bonus"
# Brackets [...] do not capture a comma, and "xyz]" is a false condition
test_conditional_with_pane_in_mode "#{?pane_in_mode,[abc,xyz],bonus}" "[abc" ""
test_conditional_with_pane_in_mode "#{?pane_in_mode,[abc#,xyz],bonus}" "[abc,xyz]" "bonus"
@ -138,12 +149,12 @@ test_conditional_with_pane_in_mode "#{?pane_in_mode,#(echo #,),xyz}" "" "xyz"
#test_conditional_with_pane_in_mode "#{?pane_in_mode,#(echo #,),xyz}" "," "xyz"
# invalid format: '#(' is not closed in the first argument of #{?,,}.
#test_conditional_with_pane_in_mode "#{?pane_in_mode,#(echo ,),xyz}" "" "),xyz"
test_conditional_with_pane_in_mode "#{?pane_in_mode,#(echo ,)xyz}" "" ")xyz"
# Escape comma inside of #[...]
test_conditional_with_pane_in_mode "#{?pane_in_mode,#[fg=default#,bg=default]abc,xyz}" "#[fg=default,bg=default]abc" "xyz"
# invalid format: '#[' is not closed in the first argument of #{?,,}
#test_conditional_with_pane_in_mode "#{?pane_in_mode,#[fg=default,bg=default]abc,xyz}" "" "bg=default]abc,xyz"
# invalid style: '#[' is not closed in the first argument of #{?,,}
test_conditional_with_pane_in_mode "#{?pane_in_mode,#[fg=default,bg=default]abc}" "#[fg=default" "bg=default]abc"
# Conditionals with comparison
test_conditional_with_session_name "#{?#{==:#{session_name},Summer},abc,xyz}" "abc" "xyz"
@ -164,6 +175,47 @@ test_conditional_with_pane_in_mode "#{?#{==:#{?pane_in_mode,#{session_name},#(ec
# Tests for boolean expressions
# "0" and the empty string are false, everything else is true.
test_format "#{!!:0}" "0"
test_format "#{!!:}" "0"
test_format "#{!!:1}" "1"
test_format "#{!!:2}" "1"
test_format "#{!!:non-empty string}" "1"
test_format "#{!!:-0}" "1"
test_format "#{!!:0.0}" "1"
# Logical operators
test_format "#{!:0}" "1"
test_format "#{!:1}" "0"
test_format "#{&&:0}" "0"
test_format "#{&&:1}" "1"
test_format "#{&&:0,0}" "0"
test_format "#{&&:0,1}" "0"
test_format "#{&&:1,0}" "0"
test_format "#{&&:1,1}" "1"
test_format "#{&&:0,0,0}" "0"
test_format "#{&&:0,1,1}" "0"
test_format "#{&&:1,0,1}" "0"
test_format "#{&&:1,1,0}" "0"
test_format "#{&&:1,1,1}" "1"
test_format "#{||:0}" "0"
test_format "#{||:1}" "1"
test_format "#{||:0,0}" "0"
test_format "#{||:0,1}" "1"
test_format "#{||:1,0}" "1"
test_format "#{||:1,1}" "1"
test_format "#{||:0,0,0}" "0"
test_format "#{||:1,0,0}" "1"
test_format "#{||:0,1,0}" "1"
test_format "#{||:0,0,1}" "1"
test_format "#{||:1,1,1}" "1"
# Format test for the literal option
# Note: The behavior for #{l:...} with escapes is sometimes weird as #{l:...}
# respects the escapes.
@ -172,7 +224,7 @@ test_format "#{l:#{pane_in_mode}}" "#{pane_in_mode}"
test_format "#{l:#{?pane_in_mode,#{?#{==:#{session_name},Summer},ABC,XYZ},xyz}}" "#{?pane_in_mode,#{?#{==:#{session_name},Summer},ABC,XYZ},xyz}"
# With escapes (which escape but are returned literally)
test_format "#{l:##{}" "##{"
test_format "#{l:##{}" "#{"
test_format "#{l:#{#}}}" "#{#}}"
# Invalid formats:

View File

@ -6,10 +6,10 @@ TERM=screen
[ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux)
TMUX="$TEST_TMUX -Ltest"
$TMUX kill-server 2>/dev/null
sleep 1
$TMUX -f/dev/null new -x20 -y2 -d || exit 1
sleep 0.1
sleep 1
$TMUX set -g escape-time 0
exit_status=0
@ -17,11 +17,14 @@ assert_key () {
key=$1
expected_code=$2
$TMUX new-window -- sh -c 'stty raw -echo && cat -tv'
$TMUX send-keys "$key" $
W=$($TMUX new-window -P -- sh -c 'stty raw -echo && cat -tv')
$TMUX send-keys -t$W "$key" 'EOL' || exit 1
sleep 0.2
actual_code=$($TMUX capturep -p | head -1 | sed -e 's/\$$//')
$TMUX kill-window
actual_code=$($TMUX capturep -pt$W | \
head -1 | \
sed -e 's/EOL.*$//')
$TMUX kill-window -t$W || exit 1
if [ "$actual_code" = "$expected_code" ]; then
if [ -n "$VERBOSE" ]; then
@ -205,7 +208,7 @@ assert_key 'PageUp' '^[[5~'
assert_key 'PgUp' '^[[5~'
assert_key 'BTab' '^[[Z'
assert_key 'C-S-Tab' '^[[Z'
assert_key 'C-S-Tab' '^I'
assert_key 'Up' '^[[A'
assert_key 'Down' '^[[B'
@ -291,8 +294,8 @@ assert_extended_key 'Insert' '^[[2;_~'
assert_extended_key 'DC' '^[[3;_~'
assert_extended_key 'Delete' '^[[3;_~'
assert_key 'C-Tab' "^[[9;5u"
assert_key 'C-S-Tab' "^[[1;5Z"
assert_key 'C-Tab' "^[[27;5;9~"
assert_key 'C-S-Tab' "^[[27;6;9~"
$TMUX kill-server 2>/dev/null

View File

@ -3,16 +3,16 @@
# new session environment
PATH=/bin:/usr/bin
TERM=screen
[ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux)
TMUX="$TEST_TMUX -Ltest"
$TMUX kill-server 2>/dev/null
TERM=$($TMUX start \; show -gv default-terminal)
TMP=$(mktemp)
OUT=$(mktemp)
SCRIPT=$(mktemp)
trap "rm -f $TMP $OUT $SCRIPT" 0 1 15
#trap "rm -f $TMP $OUT $SCRIPT" 0 1 15
cat <<EOF >$SCRIPT
(
@ -32,7 +32,7 @@ EOF
$TMUX -f$TMP start) || exit 1
sleep 1
(cat <<EOF|cmp -s - $OUT) || exit 1
TERM=screen
TERM=$TERM
PWD=/
PATH=1
SHELL=/bin/sh
@ -43,7 +43,7 @@ EOF
$TMUX -f$TMP new -d -- /bin/sh $SCRIPT) || exit 1
sleep 1
(cat <<EOF|cmp -s - $OUT) || exit 1
TERM=screen
TERM=$TERM
PWD=/
PATH=2
SHELL=/bin/sh
@ -54,7 +54,7 @@ EOF
$TMUX -f/dev/null new -d source $TMP) || exit 1
sleep 1
(cat <<EOF|cmp -s - $OUT) || exit 1
TERM=screen
TERM=$TERM
PWD=/
PATH=2
SHELL=/bin/sh

View File

@ -0,0 +1,25 @@
#!/bin/sh
# 4476
# run-shell should go to stdout if present without -t
PATH=/bin:/usr/bin
TERM=screen
[ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux)
TMUX="$TEST_TMUX -Ltest"
$TMUX kill-server 2>/dev/null
TMP=$(mktemp)
trap "rm -f $TMP" 0 1 15
$TMUX -f/dev/null new -d "$TMUX run 'echo foo' >$TMP; sleep 10" || exit 1
sleep 1 && [ "$(cat $TMP)" = "foo" ] || exit 1
$TMUX -f/dev/null new -d "$TMUX run -t: 'echo foo' >$TMP; sleep 10" || exit 1
sleep 1 && [ "$(cat $TMP)" = "" ] || exit 1
[ "$($TMUX display -p '#{pane_mode}')" = "view-mode" ] || exit 1
$TMUX kill-server 2>/dev/null
exit 0

View File

@ -3,13 +3,26 @@
PATH=/bin:/usr/bin
TERM=screen
shell=
if command -v bash >/dev/null 2>&1; then
# If Bash is available, we start a plain Bash session (without any user
# configuration files) for testing.
#
# Note: We disable the command history by passing "+o history". If an
# interactive Bash session is started without any configuration files,
# the user's command history may be truncated to the default maximum
# size of 500. To avoid breaking the user's command history, we disable
# the command history.
shell='bash --noprofile --norc +o history'
fi
[ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux)
TMUX="$TEST_TMUX -Ltest"
$TMUX kill-server 2>/dev/null
TMUX2="$TEST_TMUX -Ltest2"
$TMUX2 kill-server 2>/dev/null
$TMUX2 -f/dev/null new -d "$TMUX -f/dev/null new"
$TMUX2 -f/dev/null new -d "$TMUX -f/dev/null new -- $shell"
sleep 2
$TMUX set -g status-style fg=default,bg=default

View File

@ -14,7 +14,7 @@ trap "rm -f $TMP" 0 1 15
$TMUX2 -f/dev/null new -d || exit 1
$TMUX -f/dev/null new -d "$TMUX2 attach" || exit 1
sleep 0.1
sleep 1
exit_status=0
@ -60,38 +60,38 @@ assert_key () {
}
assert_key 0x00 'C-Space' # -- 'Escape 0x00' 'M-C-Space'
assert_key 0x01 'C-a' -- 'Escape 0x01' 'M-C-a'
assert_key 0x02 'C-b' -- 'Escape 0x02' 'M-C-b'
assert_key 0x03 'C-c' -- 'Escape 0x03' 'M-C-c'
assert_key 0x04 'C-d' -- 'Escape 0x04' 'M-C-d'
assert_key 0x05 'C-e' -- 'Escape 0x05' 'M-C-e'
assert_key 0x06 'C-f' -- 'Escape 0x06' 'M-C-f'
assert_key 0x07 'C-g' -- 'Escape 0x07' 'M-C-g'
assert_key 0x08 'C-h' -- 'Escape 0x08' 'M-C-h'
assert_key 0x00 'C-Space' # -- 'Escape 0x00' 'C-M-Space'
assert_key 0x01 'C-a' -- 'Escape 0x01' 'C-M-a'
assert_key 0x02 'C-b' -- 'Escape 0x02' 'C-M-b'
assert_key 0x03 'C-c' -- 'Escape 0x03' 'C-M-c'
assert_key 0x04 'C-d' -- 'Escape 0x04' 'C-M-d'
assert_key 0x05 'C-e' -- 'Escape 0x05' 'C-M-e'
assert_key 0x06 'C-f' -- 'Escape 0x06' 'C-M-f'
assert_key 0x07 'C-g' -- 'Escape 0x07' 'C-M-g'
assert_key 0x08 'C-h' -- 'Escape 0x08' 'C-M-h'
assert_key 0x09 'Tab' -- 'Escape 0x09' 'M-Tab'
assert_key 0x0A 'C-j' -- 'Escape 0x0A' 'M-C-j'
assert_key 0x0B 'C-k' -- 'Escape 0x0B' 'M-C-k'
assert_key 0x0C 'C-l' -- 'Escape 0x0C' 'M-C-l'
assert_key 0x0A 'C-j' -- 'Escape 0x0A' 'C-M-j'
assert_key 0x0B 'C-k' -- 'Escape 0x0B' 'C-M-k'
assert_key 0x0C 'C-l' -- 'Escape 0x0C' 'C-M-l'
assert_key 0x0D 'Enter' -- 'Escape 0x0D' 'M-Enter'
assert_key 0x0E 'C-n' -- 'Escape 0x0E' 'M-C-n'
assert_key 0x0F 'C-o' -- 'Escape 0x0F' 'M-C-o'
assert_key 0x10 'C-p' -- 'Escape 0x10' 'M-C-p'
assert_key 0x11 'C-q' -- 'Escape 0x11' 'M-C-q'
assert_key 0x12 'C-r' -- 'Escape 0x12' 'M-C-r'
assert_key 0x13 'C-s' -- 'Escape 0x13' 'M-C-s'
assert_key 0x14 'C-t' -- 'Escape 0x14' 'M-C-t'
assert_key 0x15 'C-u' -- 'Escape 0x15' 'M-C-u'
assert_key 0x16 'C-v' -- 'Escape 0x16' 'M-C-v'
assert_key 0x17 'C-w' -- 'Escape 0x17' 'M-C-w'
assert_key 0x18 'C-x' -- 'Escape 0x18' 'M-C-x'
assert_key 0x19 'C-y' -- 'Escape 0x19' 'M-C-y'
assert_key 0x1A 'C-z' -- 'Escape 0x1A' 'M-C-z'
assert_key 0x0E 'C-n' -- 'Escape 0x0E' 'C-M-n'
assert_key 0x0F 'C-o' -- 'Escape 0x0F' 'C-M-o'
assert_key 0x10 'C-p' -- 'Escape 0x10' 'C-M-p'
assert_key 0x11 'C-q' -- 'Escape 0x11' 'C-M-q'
assert_key 0x12 'C-r' -- 'Escape 0x12' 'C-M-r'
assert_key 0x13 'C-s' -- 'Escape 0x13' 'C-M-s'
assert_key 0x14 'C-t' -- 'Escape 0x14' 'C-M-t'
assert_key 0x15 'C-u' -- 'Escape 0x15' 'C-M-u'
assert_key 0x16 'C-v' -- 'Escape 0x16' 'C-M-v'
assert_key 0x17 'C-w' -- 'Escape 0x17' 'C-M-w'
assert_key 0x18 'C-x' -- 'Escape 0x18' 'C-M-x'
assert_key 0x19 'C-y' -- 'Escape 0x19' 'C-M-y'
assert_key 0x1A 'C-z' -- 'Escape 0x1A' 'C-M-z'
assert_key 0x1B 'Escape' -- 'Escape 0x1B' 'M-Escape'
assert_key 0x1C "C-\\" -- 'Escape 0x1C' "M-C-\\"
assert_key 0x1D 'C-]' -- 'Escape 0x1D' 'M-C-]'
assert_key 0x1E 'C-^' -- 'Escape 0x1E' 'M-C-^'
assert_key 0x1F 'C-_' -- 'Escape 0x1F' 'M-C-_'
assert_key 0x1C "C-\\" -- 'Escape 0x1C' "C-M-\\"
assert_key 0x1D 'C-]' -- 'Escape 0x1D' 'C-M-]'
assert_key 0x1E 'C-^' -- 'Escape 0x1E' 'C-M-^'
assert_key 0x1F 'C-_' -- 'Escape 0x1F' 'C-M-_'
assert_key 0x20 'Space' -- 'Escape 0x20' 'M-Space'
assert_key 0x21 '!' -- 'Escape 0x21' 'M-!'
assert_key 0x22 '"' -- 'Escape 0x22' 'M-"'
@ -304,7 +304,7 @@ assert_extended_key () {
key_name=$2
assert_key "Escape [${code};5u" "C-$key_name"
assert_key "Escape [${code};7u" "M-C-$key_name"
assert_key "Escape [${code};7u" "C-M-$key_name"
}
# Extended keys

View File

@ -264,16 +264,9 @@ skip:
}
static int
default_window_size_skip_client(struct client *loop, int type,
default_window_size_skip_client(struct client *loop, __unused int type,
__unused int current, struct session *s, struct window *w)
{
/*
* Latest checks separately, so do not check here. Otherwise only
* include clients where the session contains the window or where the
* session is the given session.
*/
if (type == WINDOW_SIZE_LATEST)
return (0);
if (w != NULL && !session_has(loop->session, w))
return (1);
if (w == NULL && loop->session != s)
@ -305,12 +298,12 @@ default_window_size(struct client *c, struct session *s, struct window *w,
goto done;
}
/*
* Ignore the given client if it is a control client - the creating
* client should only affect the size if it is not a control client.
*/
if (c != NULL && (c->flags & CLIENT_CONTROL))
c = NULL;
/*
* Ignore the given client if it is a control client - the creating
* client should only affect the size if it is not a control client.
*/
if (c != NULL && (c->flags & CLIENT_CONTROL))
c = NULL;
/*
* Look for a client to base the size on. If none exists (or the type

View File

@ -30,6 +30,11 @@ static void screen_redraw_draw_pane(struct screen_redraw_ctx *,
struct window_pane *);
static void screen_redraw_set_context(struct client *,
struct screen_redraw_ctx *);
static void screen_redraw_draw_pane_scrollbars(struct screen_redraw_ctx *);
static void screen_redraw_draw_scrollbar(struct screen_redraw_ctx *,
struct window_pane *, int, int, int, u_int, u_int, u_int);
static void screen_redraw_draw_pane_scrollbar(struct screen_redraw_ctx *,
struct window_pane *);
#define START_ISOLATE "\342\201\246"
#define END_ISOLATE "\342\201\251"
@ -113,9 +118,15 @@ screen_redraw_pane_border(struct screen_redraw_ctx *ctx, struct window_pane *wp,
u_int px, u_int py)
{
struct options *oo = wp->window->options;
int split = 0;
u_int ex = wp->xoff + wp->sx, ey = wp->yoff + wp->sy;
int pane_status = ctx->pane_status;
int hsplit = 0, vsplit = 0, pane_status = ctx->pane_status;
int pane_scrollbars = ctx->pane_scrollbars, sb_w = 0;
int sb_pos;
if (pane_scrollbars != 0)
sb_pos = ctx->pane_scrollbars_pos;
else
sb_pos = 0;
/* Inside pane. */
if (px >= wp->xoff && px < ex && py >= wp->yoff && py < ey)
@ -125,62 +136,68 @@ screen_redraw_pane_border(struct screen_redraw_ctx *ctx, struct window_pane *wp,
switch (options_get_number(oo, "pane-border-indicators")) {
case PANE_BORDER_COLOUR:
case PANE_BORDER_BOTH:
split = 1;
hsplit = screen_redraw_two_panes(wp->window, 0);
vsplit = screen_redraw_two_panes(wp->window, 1);
break;
}
/* Left/right borders. */
if (pane_status == PANE_STATUS_OFF) {
if (screen_redraw_two_panes(wp->window, 0) && split) {
if (wp->xoff == 0 && px == wp->sx && py <= wp->sy / 2)
return (SCREEN_REDRAW_BORDER_RIGHT);
if (wp->xoff != 0 &&
px == wp->xoff - 1 &&
py > wp->sy / 2)
return (SCREEN_REDRAW_BORDER_LEFT);
} else {
if ((wp->yoff == 0 || py >= wp->yoff - 1) && py <= ey) {
if (wp->xoff != 0 && px == wp->xoff - 1)
/* Are scrollbars enabled? */
if (window_pane_show_scrollbar(wp, pane_scrollbars))
sb_w = wp->scrollbar_style.width + wp->scrollbar_style.pad;
/*
* Left/right borders. The wp->sy / 2 test is to colour only half the
* active window's border when there are two panes.
*/
if ((wp->yoff == 0 || py >= wp->yoff - 1) && py <= ey) {
if (sb_pos == PANE_SCROLLBARS_LEFT) {
if (wp->xoff - sb_w == 0 && px == wp->sx + sb_w)
if (!hsplit || (hsplit && py <= wp->sy / 2))
return (SCREEN_REDRAW_BORDER_RIGHT);
if (wp->xoff - sb_w != 0) {
if (px == wp->xoff - sb_w - 1 &&
(!hsplit || (hsplit && py > wp->sy / 2)))
return (SCREEN_REDRAW_BORDER_LEFT);
if (px == ex)
if (px == wp->xoff + wp->sx + sb_w - 1)
return (SCREEN_REDRAW_BORDER_RIGHT);
}
} else { /* sb_pos == PANE_SCROLLBARS_RIGHT or disabled*/
if (wp->xoff == 0 && px == wp->sx + sb_w)
if (!hsplit || (hsplit && py <= wp->sy / 2))
return (SCREEN_REDRAW_BORDER_RIGHT);
if (wp->xoff != 0) {
if (px == wp->xoff - 1 &&
(!hsplit || (hsplit && py > wp->sy / 2)))
return (SCREEN_REDRAW_BORDER_LEFT);
if (px == wp->xoff + wp->sx + sb_w)
return (SCREEN_REDRAW_BORDER_RIGHT);
}
}
} else {
if ((wp->yoff == 0 || py >= wp->yoff - 1) && py <= ey) {
if (wp->xoff != 0 && px == wp->xoff - 1)
return (SCREEN_REDRAW_BORDER_LEFT);
if (px == ex)
return (SCREEN_REDRAW_BORDER_RIGHT);
}
}
/* Top/bottom borders. */
if (pane_status == PANE_STATUS_OFF) {
if (screen_redraw_two_panes(wp->window, 1) && split) {
if (wp->yoff == 0 && py == wp->sy && px <= wp->sx / 2)
return (SCREEN_REDRAW_BORDER_BOTTOM);
if (wp->yoff != 0 &&
py == wp->yoff - 1 &&
px > wp->sx / 2)
return (SCREEN_REDRAW_BORDER_TOP);
} else {
if ((wp->xoff == 0 || px >= wp->xoff - 1) && px <= ex) {
if (vsplit && pane_status == PANE_STATUS_OFF && sb_w == 0) {
if (wp->yoff == 0 && py == wp->sy && px <= wp->sx / 2)
return (SCREEN_REDRAW_BORDER_BOTTOM);
if (wp->yoff != 0 && py == wp->yoff - 1 && px > wp->sx / 2)
return (SCREEN_REDRAW_BORDER_TOP);
} else {
if (sb_pos == PANE_SCROLLBARS_LEFT) {
if ((wp->xoff - sb_w == 0 || px >= wp->xoff - sb_w) &&
(px <= ex || (sb_w != 0 && px < ex + sb_w))) {
if (wp->yoff != 0 && py == wp->yoff - 1)
return (SCREEN_REDRAW_BORDER_TOP);
if (py == ey)
return (SCREEN_REDRAW_BORDER_BOTTOM);
}
} else { /* sb_pos == PANE_SCROLLBARS_RIGHT */
if ((wp->xoff == 0 || px >= wp->xoff) &&
(px <= ex || (sb_w != 0 && px < ex + sb_w))) {
if (wp->yoff != 0 && py == wp->yoff - 1)
return (SCREEN_REDRAW_BORDER_TOP);
if (py == ey)
return (SCREEN_REDRAW_BORDER_BOTTOM);
}
}
} else if (pane_status == PANE_STATUS_TOP) {
if ((wp->xoff == 0 || px >= wp->xoff - 1) && px <= ex) {
if (wp->yoff != 0 && py == wp->yoff - 1)
return (SCREEN_REDRAW_BORDER_TOP);
}
} else {
if ((wp->xoff == 0 || px >= wp->xoff - 1) && px <= ex) {
if (py == ey)
return (SCREEN_REDRAW_BORDER_BOTTOM);
}
}
@ -195,13 +212,17 @@ screen_redraw_cell_border(struct screen_redraw_ctx *ctx, u_int px, u_int py)
struct client *c = ctx->c;
struct window *w = c->session->curw->window;
struct window_pane *wp;
u_int sy = w->sy;
if (ctx->pane_status == PANE_STATUS_BOTTOM)
sy--;
/* Outside the window? */
if (px > w->sx || py > w->sy)
if (px > w->sx || py > sy)
return (0);
/* On the window border? */
if (px == w->sx || py == w->sy)
if (px == w->sx || py == sy)
return (1);
/* Check all the panes. */
@ -231,13 +252,20 @@ screen_redraw_type_of_cell(struct screen_redraw_ctx *ctx, u_int px, u_int py)
u_int sx = w->sx, sy = w->sy;
int borders = 0;
if (pane_status == PANE_STATUS_BOTTOM)
sy--;
/* Is this outside the window? */
if (px > sx || py > sy)
return (CELL_OUTSIDE);
/*
* Construct a bitmask of whether the cells to the left (bit 4), right,
* Construct a bitmask of whether the cells to the left (bit 8), right,
* top, and bottom (bit 1) of this cell are borders.
*
* bits 8 4 2 1: 2
* 8 + 4
* 1
*/
if (px == 0 || screen_redraw_cell_border(ctx, px - 1, py))
borders |= 8;
@ -253,7 +281,7 @@ screen_redraw_type_of_cell(struct screen_redraw_ctx *ctx, u_int px, u_int py)
if (py == 0 ||
screen_redraw_cell_border(ctx, px, py - 1))
borders |= 2;
if (py != sy - 1 &&
if (py != sy &&
screen_redraw_cell_border(ctx, px, py + 1))
borders |= 1;
} else {
@ -305,14 +333,17 @@ screen_redraw_check_cell(struct screen_redraw_ctx *ctx, u_int px, u_int py,
struct window *w = c->session->curw->window;
struct window_pane *wp, *active;
int pane_status = ctx->pane_status;
int border;
u_int sx = w->sx, sy = w->sy;
int border, pane_scrollbars = ctx->pane_scrollbars;
u_int right, line;
int sb_pos = ctx->pane_scrollbars_pos;
int sb_w;
*wpp = NULL;
if (px > w->sx || py > w->sy)
if (px > sx || py > sy)
return (CELL_OUTSIDE);
if (px == w->sx || py == w->sy) /* window border */
if (px == sx || py == sy) /* window border */
return (screen_redraw_type_of_cell(ctx, px, py));
if (pane_status != PANE_STATUS_OFF) {
@ -324,7 +355,7 @@ screen_redraw_check_cell(struct screen_redraw_ctx *ctx, u_int px, u_int py,
if (pane_status == PANE_STATUS_TOP)
line = wp->yoff - 1;
else
line = wp->yoff + wp->sy;
line = wp->yoff + sy;
right = wp->xoff + 2 + wp->status_size - 1;
if (py == line && px >= wp->xoff + 2 && px <= right)
@ -343,6 +374,35 @@ screen_redraw_check_cell(struct screen_redraw_ctx *ctx, u_int px, u_int py,
goto next2;
*wpp = wp;
/* Check if CELL_SCROLLBAR */
if (window_pane_show_scrollbar(wp, pane_scrollbars)) {
if (pane_status == PANE_STATUS_TOP)
line = wp->yoff - 1;
else
line = wp->yoff + wp->sy;
/*
* Check if py could lie within a scrollbar. If the
* pane is at the top then py == 0 to sy; if the pane
* is not at the top, then yoff to yoff + sy.
*/
sb_w = wp->scrollbar_style.width +
wp->scrollbar_style.pad;
if ((pane_status && py != line) ||
(wp->yoff == 0 && py < wp->sy) ||
(py >= wp->yoff && py < wp->yoff + wp->sy)) {
/* Check if px lies within a scrollbar. */
if ((sb_pos == PANE_SCROLLBARS_RIGHT &&
(px >= wp->xoff + wp->sx &&
px < wp->xoff + wp->sx + sb_w)) ||
(sb_pos == PANE_SCROLLBARS_LEFT &&
(px >= wp->xoff - sb_w &&
px < wp->xoff)))
return (CELL_SCROLLBAR);
}
}
/*
* If definitely inside, return. If not on border, skip.
* Otherwise work out the cell.
@ -386,11 +446,15 @@ screen_redraw_make_pane_status(struct client *c, struct window_pane *wp,
const char *fmt;
struct format_tree *ft;
char *expanded;
int pane_status = rctx->pane_status;
int pane_status = rctx->pane_status, sb_w = 0;
int pane_scrollbars = rctx->pane_scrollbars;
u_int width, i, cell_type, px, py;
struct screen_write_ctx ctx;
struct screen old;
if (window_pane_show_scrollbar(wp, pane_scrollbars))
sb_w = wp->scrollbar_style.width + wp->scrollbar_style.pad;
ft = format_create(c, NULL, FORMAT_PANE|wp->id, FORMAT_STATUS);
format_defaults(ft, c, c->session, c->session->curw, wp);
@ -404,7 +468,7 @@ screen_redraw_make_pane_status(struct client *c, struct window_pane *wp,
if (wp->sx < 4)
wp->status_size = width = 0;
else
wp->status_size = width = wp->sx - 4;
wp->status_size = width = wp->sx + sb_w - 2;
memcpy(&old, &wp->status_screen, sizeof old);
screen_init(&wp->status_screen, width, 1, 0);
@ -502,14 +566,13 @@ screen_redraw_draw_pane_status(struct screen_redraw_ctx *ctx)
/* Update status line and change flags if unchanged. */
static uint64_t
screen_redraw_update(struct client *c, uint64_t flags)
screen_redraw_update(struct screen_redraw_ctx *ctx, uint64_t flags)
{
struct client *c = ctx->c;
struct window *w = c->session->curw->window;
struct window_pane *wp;
struct options *wo = w->options;
int redraw;
enum pane_lines lines;
struct screen_redraw_ctx ctx;
if (c->message_string != NULL)
redraw = status_message_redraw(c);
@ -523,17 +586,17 @@ screen_redraw_update(struct client *c, uint64_t flags)
if (c->overlay_draw != NULL)
flags |= CLIENT_REDRAWOVERLAY;
if (options_get_number(wo, "pane-border-status") != PANE_STATUS_OFF) {
screen_redraw_set_context(c, &ctx);
lines = options_get_number(wo, "pane-border-lines");
if (ctx->pane_status != PANE_STATUS_OFF) {
lines = ctx->pane_lines;
redraw = 0;
TAILQ_FOREACH(wp, &w->panes, entry) {
if (screen_redraw_make_pane_status(c, wp, &ctx, lines))
if (screen_redraw_make_pane_status(c, wp, ctx, lines))
redraw = 1;
}
if (redraw)
flags |= CLIENT_REDRAWBORDERS;
}
return (flags);
}
@ -560,6 +623,10 @@ screen_redraw_set_context(struct client *c, struct screen_redraw_ctx *ctx)
ctx->pane_status = options_get_number(wo, "pane-border-status");
ctx->pane_lines = options_get_number(wo, "pane-border-lines");
ctx->pane_scrollbars = options_get_number(wo, "pane-scrollbars");
ctx->pane_scrollbars_pos = options_get_number(wo,
"pane-scrollbars-position");
tty_window_offset(&c->tty, &ctx->ox, &ctx->oy, &ctx->sx, &ctx->sy);
log_debug("%s: %s @%u ox=%u oy=%u sx=%u sy=%u %u/%d", __func__, c->name,
@ -577,23 +644,26 @@ screen_redraw_screen(struct client *c)
if (c->flags & CLIENT_SUSPENDED)
return;
flags = screen_redraw_update(c, c->flags);
screen_redraw_set_context(c, &ctx);
flags = screen_redraw_update(&ctx, c->flags);
if ((flags & CLIENT_ALLREDRAWFLAGS) == 0)
return;
screen_redraw_set_context(c, &ctx);
tty_sync_start(&c->tty);
tty_update_mode(&c->tty, c->tty.mode, NULL);
if (flags & (CLIENT_REDRAWWINDOW|CLIENT_REDRAWBORDERS)) {
log_debug("%s: redrawing borders", c->name);
screen_redraw_draw_borders(&ctx);
if (ctx.pane_status != PANE_STATUS_OFF)
screen_redraw_draw_pane_status(&ctx);
screen_redraw_draw_borders(&ctx);
screen_redraw_draw_pane_scrollbars(&ctx);
}
if (flags & CLIENT_REDRAWWINDOW) {
log_debug("%s: redrawing panes", c->name);
screen_redraw_draw_panes(&ctx);
screen_redraw_draw_pane_scrollbars(&ctx);
}
if (ctx.statuslines != 0 &&
(flags & (CLIENT_REDRAWSTATUS|CLIENT_REDRAWSTATUSALWAYS))) {
@ -608,11 +678,12 @@ screen_redraw_screen(struct client *c)
tty_reset(&c->tty);
}
/* Redraw a single pane. */
/* Redraw a single pane and its scrollbar. */
void
screen_redraw_pane(struct client *c, struct window_pane *wp)
screen_redraw_pane(struct client *c, struct window_pane *wp,
int redraw_scrollbar_only)
{
struct screen_redraw_ctx ctx;
struct screen_redraw_ctx ctx;
if (!window_pane_visible(wp))
return;
@ -621,7 +692,11 @@ screen_redraw_pane(struct client *c, struct window_pane *wp)
tty_sync_start(&c->tty);
tty_update_mode(&c->tty, c->tty.mode, NULL);
screen_redraw_draw_pane(&ctx, wp);
if (!redraw_scrollbar_only)
screen_redraw_draw_pane(&ctx, wp);
if (window_pane_show_scrollbar(wp, ctx.pane_scrollbars))
screen_redraw_draw_pane_scrollbar(&ctx, wp);
tty_reset(&c->tty);
}
@ -667,8 +742,7 @@ screen_redraw_draw_borders_cell(struct screen_redraw_ctx *ctx, u_int i, u_int j)
const struct grid_cell *tmp;
struct overlay_ranges r;
u_int cell_type, x = ctx->ox + i, y = ctx->oy + j;
int arrows = 0, border;
int isolates;
int arrows = 0, border, isolates;
if (c->overlay_check != NULL) {
c->overlay_check(c, c->overlay_data, x, y, 1, &r);
@ -677,7 +751,7 @@ screen_redraw_draw_borders_cell(struct screen_redraw_ctx *ctx, u_int i, u_int j)
}
cell_type = screen_redraw_check_cell(ctx, x, y, &wp);
if (cell_type == CELL_INSIDE)
if (cell_type == CELL_INSIDE || cell_type == CELL_SCROLLBAR)
return;
if (wp == NULL) {
@ -866,3 +940,127 @@ screen_redraw_draw_pane(struct screen_redraw_ctx *ctx, struct window_pane *wp)
tty_draw_images(c, wp, s);
#endif
}
/* Draw the panes scrollbars */
static void
screen_redraw_draw_pane_scrollbars(struct screen_redraw_ctx *ctx)
{
struct client *c = ctx->c;
struct window *w = c->session->curw->window;
struct window_pane *wp;
log_debug("%s: %s @%u", __func__, c->name, w->id);
TAILQ_FOREACH(wp, &w->panes, entry) {
if (window_pane_show_scrollbar(wp, ctx->pane_scrollbars) &&
window_pane_visible(wp))
screen_redraw_draw_pane_scrollbar(ctx, wp);
}
}
/* Draw pane scrollbar. */
void
screen_redraw_draw_pane_scrollbar(struct screen_redraw_ctx *ctx,
struct window_pane *wp)
{
struct screen *s = wp->screen;
double percent_view;
u_int sb = ctx->pane_scrollbars, total_height, sb_h = wp->sy;
u_int sb_pos = ctx->pane_scrollbars_pos, slider_h, slider_y;
int sb_w = wp->scrollbar_style.width;
int sb_pad = wp->scrollbar_style.pad;
int cm_y, cm_size, xoff = wp->xoff, ox = ctx->ox;
int sb_x, sb_y = (int)(wp->yoff - ctx->oy); /* sb top */
if (window_pane_mode(wp) == WINDOW_PANE_NO_MODE) {
if (sb == PANE_SCROLLBARS_MODAL)
return;
/* Show slider at the bottom of the scrollbar. */
total_height = screen_size_y(s) + screen_hsize(s);
percent_view = (double)sb_h / total_height;
slider_h = (double)sb_h * percent_view;
slider_y = sb_h - slider_h;
} else {
if (TAILQ_FIRST(&wp->modes) == NULL)
return;
if (window_copy_get_current_offset(wp, &cm_y, &cm_size) == 0)
return;
total_height = cm_size + sb_h;
percent_view = (double)sb_h / (cm_size + sb_h);
slider_h = (double)sb_h * percent_view;
slider_y = (sb_h + 1) * ((double)cm_y / total_height);
}
if (sb_pos == PANE_SCROLLBARS_LEFT)
sb_x = xoff - sb_w - sb_pad - ox;
else
sb_x = xoff + wp->sx - ox;
if (slider_h < 1)
slider_h = 1;
if (slider_y >= sb_h)
slider_y = sb_h - 1;
screen_redraw_draw_scrollbar(ctx, wp, sb_pos, sb_x, sb_y, sb_h,
slider_h, slider_y);
/* Store current position and height of the slider */
wp->sb_slider_y = slider_y; /* top of slider y pos in scrollbar */
wp->sb_slider_h = slider_h; /* height of slider */
}
static void
screen_redraw_draw_scrollbar(struct screen_redraw_ctx *ctx,
struct window_pane *wp, int sb_pos, int sb_x, int sb_y, u_int sb_h,
u_int slider_h, u_int slider_y)
{
struct client *c = ctx->c;
struct tty *tty = &c->tty;
struct grid_cell gc, slgc, *gcp;
struct style *sb_style = &wp->scrollbar_style;
u_int i, j, imax, jmax;
u_int sb_w = sb_style->width, sb_pad = sb_style->pad;
int px, py, ox = ctx->ox, oy = ctx->oy;
int sx = ctx->sx, sy = ctx->sy, xoff = wp->xoff;
int yoff = wp->yoff;
/* Set up style for slider. */
gc = sb_style->gc;
memcpy(&slgc, &gc, sizeof slgc);
slgc.fg = gc.bg;
slgc.bg = gc.fg;
imax = sb_w + sb_pad;
if ((int)imax + sb_x > sx)
imax = sx - sb_x;
jmax = sb_h;
if ((int)jmax + sb_y > sy)
jmax = sy - sb_y;
for (j = 0; j < jmax; j++) {
py = sb_y + j;
for (i = 0; i < imax; i++) {
px = sb_x + i;
if (px < xoff - ox - (int)sb_w - (int)sb_pad ||
px >= sx || px < 0 ||
py < yoff - oy - 1 ||
py >= sy || py < 0)
continue;
tty_cursor(tty, px, py);
if ((sb_pos == PANE_SCROLLBARS_LEFT &&
i >= sb_w && i < sb_w + sb_pad) ||
(sb_pos == PANE_SCROLLBARS_RIGHT &&
i < sb_pad)) {
tty_cell(tty, &grid_default_cell,
&grid_default_cell, NULL, NULL);
} else {
if (j >= slider_y && j < slider_y + slider_h)
gcp = &slgc;
else
gcp = &gc;
tty_cell(tty, gcp, &grid_default_cell, NULL,
NULL);
}
}
}
}

View File

@ -151,7 +151,7 @@ screen_write_set_client_cb(struct tty_ctx *ttyctx, struct client *c)
*/
log_debug("%s: adding %%%u to deferred redraw", __func__,
wp->id);
wp->flags |= PANE_REDRAW;
wp->flags |= (PANE_REDRAW|PANE_REDRAWSCROLLBAR);
return (-1);
}
@ -377,7 +377,7 @@ screen_write_strlen(const char *fmt, ...)
if (more == UTF8_DONE)
size += ud.width;
} else {
if (*ptr > 0x1f && *ptr < 0x7f)
if (*ptr == '\t' || (*ptr > 0x1f && *ptr < 0x7f))
size++;
ptr++;
}
@ -547,7 +547,7 @@ screen_write_vnputs(struct screen_write_ctx *ctx, ssize_t maxlen,
else if (*ptr == '\n') {
screen_write_linefeed(ctx, 0, 8);
screen_write_carriagereturn(ctx);
} else if (*ptr > 0x1f && *ptr < 0x7f) {
} else if (*ptr == '\t' || (*ptr > 0x1f && *ptr < 0x7f)) {
size++;
screen_write_putc(ctx, &gc, *ptr);
}
@ -567,9 +567,11 @@ screen_write_fast_copy(struct screen_write_ctx *ctx, struct screen *src,
u_int px, u_int py, u_int nx, u_int ny)
{
struct screen *s = ctx->s;
struct window_pane *wp = ctx->wp;
struct tty_ctx ttyctx;
struct grid *gd = src->grid;
struct grid_cell gc;
u_int xx, yy, cx, cy;
u_int xx, yy, cx = s->cx, cy = s->cy;
if (nx == 0 || ny == 0)
return;
@ -578,18 +580,28 @@ screen_write_fast_copy(struct screen_write_ctx *ctx, struct screen *src,
for (yy = py; yy < py + ny; yy++) {
if (yy >= gd->hsize + gd->sy)
break;
cx = s->cx;
s->cx = cx;
if (wp != NULL)
screen_write_initctx(ctx, &ttyctx, 0);
for (xx = px; xx < px + nx; xx++) {
if (xx >= grid_get_line(gd, yy)->cellsize)
break;
grid_get_cell(gd, xx, yy, &gc);
if (xx + gc.data.width > px + nx)
break;
grid_view_set_cell(ctx->s->grid, cx, cy, &gc);
cx++;
grid_view_set_cell(ctx->s->grid, s->cx, s->cy, &gc);
if (wp != NULL) {
ttyctx.cell = &gc;
tty_write(tty_cmd_cell, &ttyctx);
ttyctx.ocx++;
}
s->cx++;
}
cy++;
s->cy++;
}
s->cx = cx;
s->cy = cy;
}
/* Select character set for drawing border lines. */
@ -1780,6 +1792,9 @@ screen_write_collect_flush(struct screen_write_ctx *ctx, int scroll_only,
ttyctx.num = ctx->scrolled;
ttyctx.bg = ctx->bg;
tty_write(tty_cmd_scrollup, &ttyctx);
if (ctx->wp != NULL)
ctx->wp->flags |= PANE_REDRAWSCROLLBAR;
}
ctx->scrolled = 0;
ctx->bg = 8;
@ -1899,6 +1914,8 @@ screen_write_collect_add(struct screen_write_ctx *ctx,
collect = 1;
if (gc->data.width != 1 || gc->data.size != 1 || *gc->data.data >= 0x7f)
collect = 0;
else if (gc->flags & GRID_FLAG_TAB)
collect = 0;
else if (gc->attr & GRID_ATTR_CHARSET)
collect = 0;
else if (~s->mode & MODE_WRAP)
@ -2088,9 +2105,11 @@ screen_write_combine(struct screen_write_ctx *ctx, const struct grid_cell *gc)
*/
if (utf8_is_zwj(ud))
zero_width = 1;
else if (utf8_is_vs(ud))
zero_width = force_wide = 1;
else if (ud->width == 0)
else if (utf8_is_vs(ud)) {
zero_width = 1;
if (options_get_number(global_options, "variation-selector-always-wide"))
force_wide = 1;
} else if (ud->width == 0)
zero_width = 1;
/* Cannot combine empty character or at left. */
@ -2221,7 +2240,17 @@ screen_write_overwrite(struct screen_write_ctx *ctx, struct grid_cell *gc,
break;
log_debug("%s: overwrite at %u,%u", __func__, xx,
s->cy);
grid_view_set_cell(gd, xx, s->cy, &grid_default_cell);
if (gc->flags & GRID_FLAG_TAB) {
memcpy(&tmp_gc, gc, sizeof tmp_gc);
memset(tmp_gc.data.data, 0,
sizeof tmp_gc.data.data);
*tmp_gc.data.data = ' ';
tmp_gc.data.width = tmp_gc.data.size =
tmp_gc.data.have = 1;
grid_view_set_cell(gd, xx, s->cy, &tmp_gc);
} else
grid_view_set_cell(gd, xx, s->cy,
&grid_default_cell);
done = 1;
}
}
@ -2332,6 +2361,9 @@ screen_write_alternateon(struct screen_write_ctx *ctx, struct grid_cell *gc,
screen_write_collect_flush(ctx, 0, __func__);
screen_alternate_on(ctx->s, gc, cursor);
if (wp != NULL)
layout_fix_panes(wp->window, NULL);
screen_write_initctx(ctx, &ttyctx, 1);
if (ttyctx.redraw_cb != NULL)
ttyctx.redraw_cb(&ttyctx);
@ -2351,6 +2383,9 @@ screen_write_alternateoff(struct screen_write_ctx *ctx, struct grid_cell *gc,
screen_write_collect_flush(ctx, 0, __func__);
screen_alternate_off(ctx->s, gc, cursor);
if (wp != NULL)
layout_fix_panes(wp->window, NULL);
screen_write_initctx(ctx, &ttyctx, 1);
if (ttyctx.redraw_cb != NULL)
ttyctx.redraw_cb(&ttyctx);

View File

@ -114,7 +114,7 @@ screen_reinit(struct screen *s)
if (options_get_number(global_options, "extended-keys") == 2)
s->mode = (s->mode & ~EXTENDED_KEY_MODES)|MODE_KEYS_EXTENDED;
if (s->saved_grid != NULL)
if (SCREEN_IS_ALTERNATE(s))
screen_alternate_off(s, NULL, 0);
s->saved_cx = UINT_MAX;
s->saved_cy = UINT_MAX;
@ -155,7 +155,7 @@ screen_free(struct screen *s)
if (s->write_list != NULL)
screen_write_free_list(s);
if (s->saved_grid != NULL)
if (SCREEN_IS_ALTERNATE(s))
grid_destroy(s->saved_grid);
grid_destroy(s->grid);
@ -182,6 +182,20 @@ screen_reset_tabs(struct screen *s)
bit_set(s->tabs, i);
}
/* Set default cursor style and colour from options. */
void
screen_set_default_cursor(struct screen *s, struct options *oo)
{
int c;
c = options_get_number(oo, "cursor-colour");
s->default_ccolour = c;
c = options_get_number(oo, "cursor-style");
s->default_mode = 0;
screen_set_cursor_style(c, &s->default_cstyle, &s->default_mode);
}
/* Set screen cursor style and mode. */
void
screen_set_cursor_style(u_int style, enum screen_cursor_style *cstyle,
@ -574,10 +588,12 @@ screen_select_cell(struct screen *s, struct grid_cell *dst,
return;
memcpy(dst, &s->sel->cell, sizeof *dst);
if (COLOUR_DEFAULT(dst->fg))
dst->fg = src->fg;
if (COLOUR_DEFAULT(dst->bg))
dst->bg = src->bg;
utf8_copy(&dst->data, &src->data);
dst->attr = dst->attr & ~GRID_ATTR_CHARSET;
dst->attr |= src->attr & GRID_ATTR_CHARSET;
dst->attr = src->attr;
dst->flags = src->flags;
}
@ -614,7 +630,7 @@ screen_alternate_on(struct screen *s, struct grid_cell *gc, int cursor)
{
u_int sx, sy;
if (s->saved_grid != NULL)
if (SCREEN_IS_ALTERNATE(s))
return;
sx = screen_size_x(s);
sy = screen_size_y(s);
@ -643,7 +659,7 @@ screen_alternate_off(struct screen *s, struct grid_cell *gc, int cursor)
* If the current size is different, temporarily resize to the old size
* before copying back.
*/
if (s->saved_grid != NULL)
if (SCREEN_IS_ALTERNATE(s))
screen_resize(s, s->saved_grid->sx, s->saved_grid->sy, 0);
/*
@ -658,7 +674,7 @@ screen_alternate_off(struct screen *s, struct grid_cell *gc, int cursor)
}
/* If not in the alternate screen, do nothing more. */
if (s->saved_grid == NULL) {
if (!SCREEN_IS_ALTERNATE(s)) {
if (s->cx > screen_size_x(s) - 1)
s->cx = screen_size_x(s) - 1;
if (s->cy > screen_size_y(s) - 1)

File diff suppressed because it is too large Load Diff

View File

@ -426,7 +426,7 @@ void
server_destroy_session(struct session *s)
{
struct client *c;
struct session *s_new = NULL;
struct session *s_new = NULL, *cs_new, *use_s;
int detach_on_destroy;
detach_on_destroy = options_get_number(s->options, "detach-on-destroy");
@ -438,15 +438,26 @@ server_destroy_session(struct session *s)
s_new = session_previous_session(s);
else if (detach_on_destroy == 4)
s_new = session_next_session(s);
if (s_new == s)
s_new = NULL;
/*
* If no suitable new session was found above, then look for any
* session as an alternative in case a client needs it.
*/
if (s_new == NULL &&
(detach_on_destroy == 1 || detach_on_destroy == 2))
cs_new = server_find_session(s, server_newer_session);
TAILQ_FOREACH(c, &clients, entry) {
if (c->session != s)
continue;
use_s = s_new;
if (use_s == NULL && (c->flags & CLIENT_NO_DETACH_ON_DESTROY))
use_s = cs_new;
c->session = NULL;
c->last_session = NULL;
server_client_set_session(c, s_new);
if (s_new == NULL)
server_client_set_session(c, use_s);
if (use_s == NULL)
c->flags |= CLIENT_EXIT;
}
recalculate_sizes();

View File

@ -207,6 +207,7 @@ server_start(struct tmuxproc *client, uint64_t flags, struct event_base *base,
fatal("pledge failed");
input_key_build();
utf8_update_width_cache();
RB_INIT(&windows);
RB_INIT(&all_window_panes);
TAILQ_INIT(&clients);

View File

@ -31,12 +31,9 @@ u_int next_session_id;
struct session_groups session_groups = RB_INITIALIZER(&session_groups);
static void session_free(int, short, void *);
static void session_lock_timer(int, short, void *);
static struct winlink *session_next_alert(struct winlink *);
static struct winlink *session_previous_alert(struct winlink *);
static void session_group_remove(struct session *);
static void session_group_synchronize1(struct session *, struct session *);
@ -47,12 +44,12 @@ session_cmp(struct session *s1, struct session *s2)
}
RB_GENERATE(sessions, session, entry, session_cmp);
static int
int
session_group_cmp(struct session_group *s1, struct session_group *s2)
{
return (strcmp(s1->name, s2->name));
}
RB_GENERATE_STATIC(session_groups, session_group, entry, session_group_cmp);
RB_GENERATE(session_groups, session_group, entry, session_group_cmp);
/*
* Find if session is still alive. This is true if it is still on the global
@ -270,19 +267,16 @@ session_lock_timer(__unused int fd, __unused short events, void *arg)
void
session_update_activity(struct session *s, struct timeval *from)
{
struct timeval *last = &s->last_activity_time;
struct timeval tv;
memcpy(last, &s->activity_time, sizeof *last);
if (from == NULL)
gettimeofday(&s->activity_time, NULL);
else
memcpy(&s->activity_time, from, sizeof s->activity_time);
log_debug("session $%u %s activity %lld.%06d (last %lld.%06d)", s->id,
log_debug("session $%u %s activity %lld.%06d", s->id,
s->name, (long long)s->activity_time.tv_sec,
(int)s->activity_time.tv_usec, (long long)last->tv_sec,
(int)last->tv_usec);
(int)s->activity_time.tv_usec);
if (evtimer_initialized(&s->lock_timer))
evtimer_del(&s->lock_timer);
@ -367,7 +361,7 @@ session_detach(struct session *s, struct winlink *wl)
if (RB_EMPTY(&s->windows))
return (1);
return (0);
return (0);
}
/* Return if session has window. */
@ -757,3 +751,18 @@ session_renumber_windows(struct session *s)
RB_FOREACH_SAFE(wl, winlinks, &old_wins, wl1)
winlink_remove(&old_wins, wl);
}
/* Set the PANE_THEMECHANGED flag for every pane in this session. */
void
session_theme_changed(struct session *s)
{
struct window_pane *wp;
struct winlink *wl;
if (s != NULL) {
RB_FOREACH(wl, winlinks, &s->windows) {
TAILQ_FOREACH(wp, &wl->window->panes, entry)
wp->flags |= PANE_THEMECHANGED;
}
}
}

26
spawn.c
View File

@ -229,8 +229,9 @@ spawn_pane(struct spawn_context *sc, char **cause)
if (sc->cwd != NULL) {
cwd = format_single(item, sc->cwd, c, target->s, NULL, NULL);
if (*cwd != '/') {
xasprintf(&new_cwd, "%s/%s", server_client_get_cwd(c,
target->s), cwd);
xasprintf(&new_cwd, "%s%s%s",
server_client_get_cwd(c, target->s),
*cwd != '\0' ? "/" : "", cwd);
free(cwd);
cwd = new_cwd;
}
@ -382,20 +383,19 @@ spawn_pane(struct spawn_context *sc, char **cause)
/* In the parent process, everything is done now. */
if (new_wp->pid != 0) {
#if defined(HAVE_SYSTEMD) && defined(ENABLE_CGROUPS)
/*
* Move the child process into a new cgroup for systemd-oomd
* isolation.
*/
if (systemd_move_pid_to_new_cgroup(new_wp->pid, cause) < 0) {
log_debug("%s: moving pane to new cgroup failed: %s",
__func__, *cause);
free (*cause);
}
#endif
goto complete;
}
#if defined(HAVE_SYSTEMD) && defined(ENABLE_CGROUPS)
/*
* Move the child process into a new cgroup for systemd-oomd isolation.
*/
if (systemd_move_to_new_cgroup(cause) < 0) {
log_debug("%s: moving pane to new cgroup failed: %s",
__func__, *cause);
free (*cause);
}
#endif
/*
* Child process. Change to the working directory or home if that
* fails.

177
status.c
View File

@ -470,7 +470,7 @@ status_redraw(struct client *c)
/* Set a status line message. */
void
status_message_set(struct client *c, int delay, int ignore_styles,
int ignore_keys, const char *fmt, ...)
int ignore_keys, int no_freeze, const char *fmt, ...)
{
struct timeval tv;
va_list ap;
@ -514,7 +514,9 @@ status_message_set(struct client *c, int delay, int ignore_styles,
c->message_ignore_keys = ignore_keys;
c->message_ignore_styles = ignore_styles;
c->tty.flags |= (TTY_NOCURSOR|TTY_FREEZE);
if (!no_freeze)
c->tty.flags |= TTY_FREEZE;
c->tty.flags |= TTY_NOCURSOR;
c->flags |= CLIENT_REDRAWSTATUS;
}
@ -598,6 +600,19 @@ status_message_redraw(struct client *c)
return (1);
}
/* Accept prompt immediately. */
static enum cmd_retval
status_prompt_accept(__unused struct cmdq_item *item, void *data)
{
struct client *c = data;
if (c->prompt_string != NULL) {
c->prompt_inputcb(c, c->prompt_data, "y", 1);
status_prompt_clear(c);
}
return (CMD_RETURN_NORMAL);
}
/* Enable status line prompt. */
void
status_prompt_set(struct client *c, struct cmd_find_state *fs,
@ -616,17 +631,18 @@ status_prompt_set(struct client *c, struct cmd_find_state *fs,
if (input == NULL)
input = "";
if (flags & PROMPT_NOFORMAT)
tmp = xstrdup(input);
else
tmp = format_expand_time(ft, input);
status_message_clear(c);
status_prompt_clear(c);
status_push_screen(c);
c->prompt_string = format_expand_time(ft, msg);
c->prompt_formats = ft;
c->prompt_string = xstrdup (msg);
if (flags & PROMPT_NOFORMAT)
tmp = xstrdup(input);
else
tmp = format_expand_time(ft, input);
if (flags & PROMPT_INCREMENTAL) {
c->prompt_last = xstrdup(tmp);
c->prompt_buffer = utf8_fromcstr("");
@ -635,6 +651,7 @@ status_prompt_set(struct client *c, struct cmd_find_state *fs,
c->prompt_buffer = utf8_fromcstr(tmp);
}
c->prompt_index = utf8_strlen(c->prompt_buffer);
free(tmp);
c->prompt_inputcb = inputcb;
c->prompt_freecb = freecb;
@ -647,14 +664,14 @@ status_prompt_set(struct client *c, struct cmd_find_state *fs,
c->prompt_mode = PROMPT_ENTRY;
if (~flags & PROMPT_INCREMENTAL)
c->tty.flags |= (TTY_NOCURSOR|TTY_FREEZE);
c->tty.flags |= TTY_FREEZE;
c->flags |= CLIENT_REDRAWSTATUS;
if (flags & PROMPT_INCREMENTAL)
c->prompt_inputcb(c, c->prompt_data, "=", 0);
free(tmp);
format_free(ft);
if ((flags & PROMPT_SINGLE) && (flags & PROMPT_ACCEPT))
cmdq_append(c, cmdq_get_callback(status_prompt_accept, c));
}
/* Remove status line prompt. */
@ -670,6 +687,9 @@ status_prompt_clear(struct client *c)
free(c->prompt_last);
c->prompt_last = NULL;
format_free(c->prompt_formats);
c->prompt_formats = NULL;
free(c->prompt_string);
c->prompt_string = NULL;
@ -689,27 +709,69 @@ status_prompt_clear(struct client *c)
void
status_prompt_update(struct client *c, const char *msg, const char *input)
{
struct format_tree *ft;
char *tmp;
ft = format_create(c, NULL, FORMAT_NONE, 0);
format_defaults(ft, c, NULL, NULL, NULL);
tmp = format_expand_time(ft, input);
char *tmp;
free(c->prompt_string);
c->prompt_string = format_expand_time(ft, msg);
c->prompt_string = xstrdup(msg);
free(c->prompt_buffer);
tmp = format_expand_time(c->prompt_formats, input);
c->prompt_buffer = utf8_fromcstr(tmp);
c->prompt_index = utf8_strlen(c->prompt_buffer);
free(tmp);
memset(c->prompt_hindex, 0, sizeof c->prompt_hindex);
c->flags |= CLIENT_REDRAWSTATUS;
}
free(tmp);
format_free(ft);
/* Redraw character. Return 1 if can continue redrawing, 0 otherwise. */
static int
status_prompt_redraw_character(struct screen_write_ctx *ctx, u_int offset,
u_int pwidth, u_int *width, struct grid_cell *gc,
const struct utf8_data *ud)
{
u_char ch;
if (*width < offset) {
*width += ud->width;
return (1);
}
if (*width >= offset + pwidth)
return (0);
*width += ud->width;
if (*width > offset + pwidth)
return (0);
ch = *ud->data;
if (ud->size == 1 && (ch <= 0x1f || ch == 0x7f)) {
gc->data.data[0] = '^';
gc->data.data[1] = (ch == 0x7f) ? '?' : ch|0x40;
gc->data.size = gc->data.have = 2;
gc->data.width = 2;
} else
utf8_copy(&gc->data, ud);
screen_write_cell(ctx, gc);
return (1);
}
/*
* Redraw quote indicator '^' if necessary. Return 1 if can continue redrawing,
* 0 otherwise.
*/
static int
status_prompt_redraw_quote(const struct client *c, u_int pcursor,
struct screen_write_ctx *ctx, u_int offset, u_int pwidth, u_int *width,
struct grid_cell *gc)
{
struct utf8_data ud;
if (c->prompt_flags & PROMPT_QUOTENEXT && ctx->s->cx == pcursor + 1) {
utf8_set(&ud, '^');
return (status_prompt_redraw_character(ctx, offset, pwidth,
width, gc, &ud));
}
return (1);
}
/* Draw client prompt on status line of present else on last line. */
@ -720,10 +782,11 @@ status_prompt_redraw(struct client *c)
struct screen_write_ctx ctx;
struct session *s = c->session;
struct screen old_screen;
u_int i, lines, offset, left, start, width;
u_int i, lines, offset, left, start, width, n;
u_int pcursor, pwidth, promptline;
struct grid_cell gc, cursorgc;
struct format_tree *ft;
struct grid_cell gc;
struct format_tree *ft = c->prompt_formats;
char *prompt, *tmp;
if (c->tty.sx == 0 || c->tty.sy == 0)
return (0);
@ -734,21 +797,27 @@ status_prompt_redraw(struct client *c)
lines = 1;
screen_init(sl->active, c->tty.sx, lines, 0);
n = options_get_number(s->options, "prompt-cursor-colour");
sl->active->default_ccolour = n;
n = options_get_number(s->options, "prompt-cursor-style");
screen_set_cursor_style(n, &sl->active->default_cstyle,
&sl->active->default_mode);
promptline = status_prompt_line_at(c);
if (promptline > lines - 1)
promptline = lines - 1;
ft = format_create_defaults(NULL, c, NULL, NULL, NULL);
if (c->prompt_mode == PROMPT_COMMAND)
style_apply(&gc, s->options, "message-command-style", ft);
else
style_apply(&gc, s->options, "message-style", ft);
format_free(ft);
memcpy(&cursorgc, &gc, sizeof cursorgc);
cursorgc.attr ^= GRID_ATTR_REVERSE;
tmp = utf8_tocstr(c->prompt_buffer);
format_add(c->prompt_formats, "prompt-input", "%s", tmp);
prompt = format_expand_time(c->prompt_formats, c->prompt_string);
free (tmp);
start = format_width(c->prompt_string);
start = format_width(prompt);
if (start > c->tty.sx)
start = c->tty.sx;
@ -758,7 +827,7 @@ status_prompt_redraw(struct client *c)
for (offset = 0; offset < c->tty.sx; offset++)
screen_write_putc(&ctx, &gc, ' ');
screen_write_cursormove(&ctx, 0, promptline, 0);
format_draw(&ctx, &gc, start, c->prompt_string, NULL, 0);
format_draw(&ctx, &gc, start, prompt, NULL, 0);
screen_write_cursormove(&ctx, start, promptline, 0);
left = c->tty.sx - start;
@ -767,6 +836,8 @@ status_prompt_redraw(struct client *c)
pcursor = utf8_strwidth(c->prompt_buffer, c->prompt_index);
pwidth = utf8_strwidth(c->prompt_buffer, -1);
if (c->prompt_flags & PROMPT_QUOTENEXT)
pwidth++;
if (pcursor >= left) {
/*
* The cursor would be outside the screen so start drawing
@ -778,30 +849,19 @@ status_prompt_redraw(struct client *c)
offset = 0;
if (pwidth > left)
pwidth = left;
c->prompt_cursor = start + c->prompt_index - offset;
c->prompt_cursor = start + pcursor - offset;
width = 0;
for (i = 0; c->prompt_buffer[i].size != 0; i++) {
if (width < offset) {
width += c->prompt_buffer[i].width;
continue;
}
if (width >= offset + pwidth)
if (!status_prompt_redraw_quote(c, pcursor, &ctx, offset,
pwidth, &width, &gc))
break;
width += c->prompt_buffer[i].width;
if (width > offset + pwidth)
if (!status_prompt_redraw_character(&ctx, offset, pwidth,
&width, &gc, &c->prompt_buffer[i]))
break;
if (i != c->prompt_index) {
utf8_copy(&gc.data, &c->prompt_buffer[i]);
screen_write_cell(&ctx, &gc);
} else {
utf8_copy(&cursorgc.data, &c->prompt_buffer[i]);
screen_write_cell(&ctx, &cursorgc);
}
}
if (sl->active->cx < screen_size_x(sl->active) && c->prompt_index >= i)
screen_write_putc(&ctx, &cursorgc, ' ');
status_prompt_redraw_quote(c, pcursor, &ctx, offset, pwidth, &width,
&gc);
finished:
screen_write_stop(&ctx);
@ -852,6 +912,7 @@ status_prompt_translate_key(struct client *c, key_code key, key_code *new_key)
case 'p'|KEYC_CTRL:
case 't'|KEYC_CTRL:
case 'u'|KEYC_CTRL:
case 'v'|KEYC_CTRL:
case 'w'|KEYC_CTRL:
case 'y'|KEYC_CTRL:
case '\n':
@ -1250,6 +1311,19 @@ status_prompt_key(struct client *c, key_code key)
}
key &= ~KEYC_MASK_FLAGS;
if (c->prompt_flags & (PROMPT_SINGLE|PROMPT_QUOTENEXT)) {
if ((key & KEYC_MASK_KEY) == KEYC_BSPACE)
key = 0x7f;
else if ((key & KEYC_MASK_KEY) > 0x7f) {
if (!KEYC_IS_UNICODE(key))
return (0);
key &= KEYC_MASK_KEY;
} else
key &= (key & KEYC_CTRL) ? 0x1f : KEYC_MASK_KEY;
c->prompt_flags &= ~PROMPT_QUOTENEXT;
goto append_key;
}
keys = options_get_number(c->session->options, "status-keys");
if (keys == MODEKEY_VI) {
switch (status_prompt_translate_key(c, key, &key)) {
@ -1472,6 +1546,9 @@ process_key:
} else
prefix = '+';
goto changed;
case 'v'|KEYC_CTRL:
c->prompt_flags |= PROMPT_QUOTENEXT;
break;
default:
goto append_key;
}
@ -1480,9 +1557,11 @@ process_key:
return (0);
append_key:
if (key <= 0x7f)
if (key <= 0x7f) {
utf8_set(&tmp, key);
else if (KEYC_IS_UNICODE(key))
if (key <= 0x1f || key == 0x7f)
tmp.width = 2;
} else if (KEYC_IS_UNICODE(key))
utf8_to_data(key, &tmp);
else
return (0);

52
style.c
View File

@ -39,6 +39,8 @@ static struct style style_default = {
STYLE_RANGE_NONE, 0, "",
STYLE_WIDTH_DEFAULT, STYLE_PAD_DEFAULT,
STYLE_DEFAULT_BASE
};
@ -96,6 +98,8 @@ style_parse(struct style *sy, const struct grid_cell *base, const char *in)
sy->default_type = STYLE_DEFAULT_PUSH;
else if (strcasecmp(tmp, "pop-default") == 0)
sy->default_type = STYLE_DEFAULT_POP;
else if (strcasecmp(tmp, "set-default") == 0)
sy->default_type = STYLE_DEFAULT_SET;
else if (strcasecmp(tmp, "nolist") == 0)
sy->list = STYLE_LIST_OFF;
else if (strncasecmp(tmp, "list=", 5) == 0) {
@ -213,9 +217,21 @@ style_parse(struct style *sy, const struct grid_cell *base, const char *in)
} else if (strcasecmp(tmp, "none") == 0)
sy->gc.attr = 0;
else if (end > 2 && strncasecmp(tmp, "no", 2) == 0) {
if ((value = attributes_fromstring(tmp + 2)) == -1)
if (strcmp(tmp + 2, "attr") == 0)
value = 0xffff & ~GRID_ATTR_CHARSET;
else if ((value = attributes_fromstring(tmp + 2)) == -1)
goto error;
sy->gc.attr &= ~value;
} else if (end > 6 && strncasecmp(tmp, "width=", 6) == 0) {
n = strtonum(tmp + 6, 0, UINT_MAX, &errstr);
if (errstr != NULL)
goto error;
sy->width = (int)n;
} else if (end > 4 && strncasecmp(tmp, "pad=", 4) == 0) {
n = strtonum(tmp + 4, 0, UINT_MAX, &errstr);
if (errstr != NULL)
goto error;
sy->pad = (int)n;
} else {
if ((value = attributes_fromstring(tmp)) == -1)
goto error;
@ -298,6 +314,8 @@ style_tostring(struct style *sy)
tmp = "push-default";
else if (sy->default_type == STYLE_DEFAULT_POP)
tmp = "pop-default";
else if (sy->default_type == STYLE_DEFAULT_SET)
tmp = "set-default";
off += xsnprintf(s + off, sizeof s - off, "%s%s", comma, tmp);
comma = ",";
}
@ -326,7 +344,16 @@ style_tostring(struct style *sy)
attributes_tostring(gc->attr));
comma = ",";
}
if (sy->width >= 0) {
xsnprintf(s + off, sizeof s - off, "%swidth=%u", comma,
sy->width);
comma = ",";
}
if (sy->pad >= 0) {
xsnprintf(s + off, sizeof s - off, "%spad=%u", comma,
sy->pad);
comma = ",";
}
if (*s == '\0')
return ("default");
return (s);
@ -381,3 +408,24 @@ style_copy(struct style *dst, struct style *src)
{
memcpy(dst, src, sizeof *dst);
}
void
style_set_scrollbar_style_from_option(struct style *sb_style, struct options *oo)
{
struct style *sy;
sy = options_string_to_style(oo, "pane-scrollbars-style", NULL);
if (sy == NULL) {
style_set(sb_style, &grid_default_cell);
sb_style->width = PANE_SCROLLBARS_DEFAULT_WIDTH;
sb_style->pad = PANE_SCROLLBARS_DEFAULT_PADDING;
utf8_set(&sb_style->gc.data, PANE_SCROLLBARS_CHARACTER);
} else {
style_copy(sb_style, sy);
if (sb_style->width < 1)
sb_style->width = PANE_SCROLLBARS_DEFAULT_WIDTH;
if (sb_style->pad < 0)
sb_style->pad = PANE_SCROLLBARS_DEFAULT_PADDING;
utf8_set(&sb_style->gc.data, PANE_SCROLLBARS_CHARACTER);
}
}

462
tmux.1

File diff suppressed because it is too large Load Diff

26
tmux.c
View File

@ -43,20 +43,20 @@ const char *socket_path;
int ptm_fd = -1;
const char *shell_command;
static __dead void usage(void);
static __dead void usage(int);
static char *make_label(const char *, char **);
static int areshell(const char *);
static const char *getshell(void);
static __dead void
usage(void)
usage(int status)
{
fprintf(stderr,
"usage: %s [-2CDlNuVv] [-c shell-command] [-f file] [-L socket-name]\n"
fprintf(status ? stderr : stdout,
"usage: %s [-2CDhlNuVv] [-c shell-command] [-f file] [-L socket-name]\n"
" [-S socket-path] [-T features] [command [flags]]\n",
getprogname());
exit(1);
exit(status);
}
static const char *
@ -222,7 +222,7 @@ make_label(const char *label, char **cause)
xasprintf(cause, "%s is not a directory", base);
goto fail;
}
if (sb.st_uid != uid || (sb.st_mode & S_IRWXO) != 0) {
if (sb.st_uid != uid || (sb.st_mode & TMUX_SOCK_PERM) != 0) {
xasprintf(cause, "directory %s has unsafe permissions", base);
goto fail;
}
@ -379,7 +379,7 @@ main(int argc, char **argv)
environ_set(global_environ, "PWD", 0, "%s", cwd);
expand_paths(TMUX_CONF, &cfg_files, &cfg_nfiles, 1);
while ((opt = getopt(argc, argv, "2c:CDdf:lL:NqS:T:uUvV")) != -1) {
while ((opt = getopt(argc, argv, "2c:CDdf:hlL:NqS:T:uUvV")) != -1) {
switch (opt) {
case '2':
tty_add_features(&feat, "256", ":,");
@ -408,9 +408,11 @@ main(int argc, char **argv)
cfg_files[cfg_nfiles++] = xstrdup(optarg);
cfg_quiet = 0;
break;
case 'V':
case 'h':
usage(0);
case 'V':
printf("tmux %s\n", getversion());
exit(0);
exit(0);
case 'l':
flags |= CLIENT_LOGIN;
break;
@ -437,16 +439,16 @@ main(int argc, char **argv)
log_add_level();
break;
default:
usage();
usage(1);
}
}
argc -= optind;
argv += optind;
if (shell_command != NULL && argc != 0)
usage();
usage(1);
if ((flags & CLIENT_NOFORK) && argc != 0)
usage();
usage(1);
if ((ptm_fd = getptmfd()) == -1)
err(1, "getptmfd");

183
tmux.h
View File

@ -84,6 +84,9 @@ struct winlink;
#ifndef TMUX_SOCK
#define TMUX_SOCK "$TMUX_TMPDIR:" _PATH_TMP
#endif
#ifndef TMUX_SOCK_PERM
#define TMUX_SOCK_PERM (7 /* o+rwx */)
#endif
#ifndef TMUX_TERM
#define TMUX_TERM "screen"
#endif
@ -171,23 +174,34 @@ struct winlink;
(((key) & KEYC_MASK_KEY) < KEYC_USER || \
((key) & KEYC_MASK_KEY) >= KEYC_USER_END))
/* Is this a paste key? */
#define KEYC_IS_PASTE(key) \
(((key) & KEYC_MASK_KEY) == KEYC_PASTE_START || \
((key) & KEYC_MASK_KEY) == KEYC_PASTE_END)
/* Multiple click timeout. */
#define KEYC_CLICK_TIMEOUT 300
/* Mouse key codes. */
#define KEYC_MOUSE_KEY(name) \
KEYC_ ## name ## _PANE, \
KEYC_ ## name ## _STATUS, \
KEYC_ ## name ## _STATUS_LEFT, \
KEYC_ ## name ## _STATUS_RIGHT, \
KEYC_ ## name ## _STATUS_DEFAULT, \
#define KEYC_MOUSE_KEY(name) \
KEYC_ ## name ## _PANE, \
KEYC_ ## name ## _STATUS, \
KEYC_ ## name ## _STATUS_LEFT, \
KEYC_ ## name ## _STATUS_RIGHT, \
KEYC_ ## name ## _STATUS_DEFAULT, \
KEYC_ ## name ## _SCROLLBAR_UP, \
KEYC_ ## name ## _SCROLLBAR_SLIDER, \
KEYC_ ## name ## _SCROLLBAR_DOWN, \
KEYC_ ## name ## _BORDER
#define KEYC_MOUSE_STRING(name, s) \
{ #s "Pane", KEYC_ ## name ## _PANE }, \
{ #s "Status", KEYC_ ## name ## _STATUS }, \
{ #s "StatusLeft", KEYC_ ## name ## _STATUS_LEFT }, \
{ #s "StatusRight", KEYC_ ## name ## _STATUS_RIGHT }, \
{ #s "StatusDefault", KEYC_ ## name ## _STATUS_DEFAULT }, \
#define KEYC_MOUSE_STRING(name, s) \
{ #s "Pane", KEYC_ ## name ## _PANE }, \
{ #s "Status", KEYC_ ## name ## _STATUS }, \
{ #s "StatusLeft", KEYC_ ## name ## _STATUS_LEFT }, \
{ #s "StatusRight", KEYC_ ## name ## _STATUS_RIGHT }, \
{ #s "StatusDefault", KEYC_ ## name ## _STATUS_DEFAULT }, \
{ #s "ScrollbarUp", KEYC_ ## name ## _SCROLLBAR_UP }, \
{ #s "ScrollbarSlider", KEYC_ ## name ## _SCROLLBAR_SLIDER }, \
{ #s "ScrollbarDown", KEYC_ ## name ## _SCROLLBAR_DOWN }, \
{ #s "Border", KEYC_ ## name ## _BORDER }
/*
@ -364,6 +378,10 @@ enum {
KEYC_KP_ZERO,
KEYC_KP_PERIOD,
/* Theme reporting. */
KEYC_REPORT_DARK_THEME,
KEYC_REPORT_LIGHT_THEME,
/* End of special keys. */
KEYC_BASE_END
};
@ -605,7 +623,7 @@ enum tty_code_code {
};
/* Character classes. */
#define WHITESPACE " "
#define WHITESPACE "\t "
/* Mode keys. */
#define MODEKEY_EMACS 0
@ -631,6 +649,7 @@ enum tty_code_code {
#define MODE_CURSOR_VERY_VISIBLE 0x10000
#define MODE_CURSOR_BLINKING_SET 0x20000
#define MODE_KEYS_EXTENDED_2 0x40000
#define MODE_THEME_UPDATES 0x80000
#define ALL_MODES 0xffffff
#define ALL_MOUSE_MODES (MODE_MOUSE_STANDARD|MODE_MOUSE_BUTTON|MODE_MOUSE_ALL)
@ -652,7 +671,7 @@ typedef u_int utf8_char;
* characters as well. It can't be more than 32 bytes without changes to how
* characters are stored.
*/
#define UTF8_SIZE 21
#define UTF8_SIZE 32
struct utf8_data {
u_char data[UTF8_SIZE];
@ -715,6 +734,7 @@ struct colour_palette {
#define GRID_FLAG_SELECTED 0x10
#define GRID_FLAG_NOPALETTE 0x20
#define GRID_FLAG_CLEARED 0x40
#define GRID_FLAG_TAB 0x80
/* Grid line flags. */
#define GRID_LINE_WRAPPED 0x1
@ -744,6 +764,7 @@ struct colour_palette {
#define CELL_RIGHTJOIN 10
#define CELL_JOIN 11
#define CELL_OUTSIDE 12
#define CELL_SCROLLBAR 13
/* Cell borders. */
#define CELL_BORDERS " xqlkmjwvtun~"
@ -861,11 +882,16 @@ struct style_range {
};
TAILQ_HEAD(style_ranges, style_range);
/* Default style width and pad. */
#define STYLE_WIDTH_DEFAULT -1
#define STYLE_PAD_DEFAULT -1
/* Style default. */
enum style_default_type {
STYLE_DEFAULT_BASE,
STYLE_DEFAULT_PUSH,
STYLE_DEFAULT_POP
STYLE_DEFAULT_POP,
STYLE_DEFAULT_SET
};
/* Style option. */
@ -881,6 +907,9 @@ struct style {
u_int range_argument;
char range_string[16];
int width;
int pad;
enum style_default_type default_type;
};
@ -1012,6 +1041,9 @@ struct screen_redraw_ctx {
int pane_status;
enum pane_lines pane_lines;
int pane_scrollbars;
int pane_scrollbars_pos;
struct grid_cell no_pane_gc;
int no_pane_gc_set;
@ -1065,6 +1097,7 @@ struct window_mode {
struct mouse_event *);
void (*formats)(struct window_mode_entry *,
struct format_tree *);
struct screen *(*get_screen)(struct window_mode_entry *);
};
/* Active window mode. */
@ -1129,7 +1162,12 @@ struct window_pane {
#define PANE_STATUSDRAWN 0x400
#define PANE_EMPTY 0x800
#define PANE_STYLECHANGED 0x1000
#define PANE_UNSEENCHANGES 0x2000
#define PANE_THEMECHANGED 0x2000
#define PANE_UNSEENCHANGES 0x4000
#define PANE_REDRAWSCROLLBAR 0x8000
u_int sb_slider_y;
u_int sb_slider_h;
int argc;
char **argv;
@ -1177,6 +1215,8 @@ struct window_pane {
int control_bg;
int control_fg;
struct style scrollbar_style;
TAILQ_ENTRY(window_pane) entry; /* link in list of all panes */
TAILQ_ENTRY(window_pane) sentry; /* link in list of last visited */
RB_ENTRY(window_pane) tree_entry;
@ -1272,6 +1312,23 @@ TAILQ_HEAD(winlink_stack, winlink);
#define PANE_STATUS_TOP 1
#define PANE_STATUS_BOTTOM 2
/* Pane scrollbars option. */
#define PANE_SCROLLBARS_OFF 0
#define PANE_SCROLLBARS_MODAL 1
#define PANE_SCROLLBARS_ALWAYS 2
/* Pane scrollbars position option. */
#define PANE_SCROLLBARS_RIGHT 0
#define PANE_SCROLLBARS_LEFT 1
/* Pane scrollbars width, padding and fill character. */
#define PANE_SCROLLBARS_DEFAULT_PADDING 0
#define PANE_SCROLLBARS_DEFAULT_WIDTH 1
#define PANE_SCROLLBARS_CHARACTER ' '
/* True if screen in alternate screen. */
#define SCREEN_IS_ALTERNATE(s) ((s)->saved_grid != NULL)
/* Layout direction. */
enum layout_type {
LAYOUT_LEFTRIGHT,
@ -1342,8 +1399,7 @@ struct session {
struct options *options;
#define SESSION_PASTING 0x1
#define SESSION_ALERTED 0x2
#define SESSION_ALERTED 0x1
int flags;
u_int attached;
@ -1421,8 +1477,11 @@ struct mouse_event {
/* Key event. */
struct key_event {
key_code key;
struct mouse_event m;
key_code key;
struct mouse_event m;
char *buf;
size_t len;
};
/* Terminal definition. */
@ -1508,6 +1567,7 @@ struct tty {
#define TTY_HAVEXDA 0x200
#define TTY_SYNCING 0x400
#define TTY_HAVEDA2 0x800 /* Secondary DA. */
#define TTY_WINSIZEQUERY 0x1000
#define TTY_ALL_REQUEST_FLAGS \
(TTY_HAVEDA|TTY_HAVEDA2|TTY_HAVEXDA)
int flags;
@ -1518,6 +1578,9 @@ struct tty {
u_int mouse_last_y;
u_int mouse_last_b;
int mouse_drag_flag;
int mouse_scrolling_flag;
int mouse_slider_mpos;
void (*mouse_drag_update)(struct client *,
struct mouse_event *);
void (*mouse_drag_release)(struct client *,
@ -1809,6 +1872,16 @@ struct overlay_ranges {
u_int nx[OVERLAY_MAX_RANGES];
};
/*
* Client theme, this is worked out from the background colour if not reported
* by terminal.
*/
enum client_theme {
THEME_UNKNOWN,
THEME_LIGHT,
THEME_DARK
};
/* Client connection. */
typedef int (*prompt_input_cb)(struct client *, void *, const char *, int);
typedef void (*prompt_free_cb)(void *);
@ -1839,6 +1912,7 @@ struct client {
struct timeval creation_time;
struct timeval activity_time;
struct timeval last_activity_time;
struct environ *environ;
struct format_job_tree *jobs;
@ -1867,6 +1941,7 @@ struct client {
struct mouse_event click_event;
struct status_line status;
enum client_theme theme;
#define CLIENT_TERMINAL 0x1
#define CLIENT_LOGIN 0x2
@ -1905,13 +1980,17 @@ struct client {
#define CLIENT_WINDOWSIZECHANGED 0x400000000ULL
#define CLIENT_CLIPBOARDBUFFER 0x800000000ULL
#define CLIENT_BRACKETPASTING 0x1000000000ULL
#define CLIENT_ASSUMEPASTING 0x2000000000ULL
#define CLIENT_REDRAWSCROLLBARS 0x4000000000ULL
#define CLIENT_NO_DETACH_ON_DESTROY 0x8000000000ULL
#define CLIENT_ALLREDRAWFLAGS \
(CLIENT_REDRAWWINDOW| \
CLIENT_REDRAWSTATUS| \
CLIENT_REDRAWSTATUSALWAYS| \
CLIENT_REDRAWBORDERS| \
CLIENT_REDRAWOVERLAY| \
CLIENT_REDRAWPANES)
CLIENT_REDRAWPANES| \
CLIENT_REDRAWSCROLLBARS)
#define CLIENT_UNATTACHEDFLAGS \
(CLIENT_DEAD| \
CLIENT_SUSPENDED| \
@ -1935,8 +2014,10 @@ struct client {
char *exit_message;
struct key_table *keytable;
key_code last_key;
uint64_t redraw_panes;
uint64_t redraw_scrollbars;
int message_ignore_keys;
int message_ignore_styles;
@ -1944,6 +2025,7 @@ struct client {
struct event message_timer;
char *prompt_string;
struct format_tree *prompt_formats;
struct utf8_data *prompt_buffer;
char *prompt_last;
size_t prompt_index;
@ -1961,6 +2043,8 @@ struct client {
#define PROMPT_INCREMENTAL 0x4
#define PROMPT_NOFORMAT 0x8
#define PROMPT_KEY 0x10
#define PROMPT_ACCEPT 0x20
#define PROMPT_QUOTENEXT 0x40
int prompt_flags;
enum prompt_type prompt_type;
int prompt_cursor;
@ -1984,6 +2068,7 @@ struct client {
struct event overlay_timer;
struct client_files files;
u_int source_file_depth;
u_int *clipboard_panes;
u_int clipboard_npanes;
@ -2310,10 +2395,13 @@ struct options_entry *options_match_get(struct options *, const char *, int *,
int, int *);
const char *options_get_string(struct options *, const char *);
long long options_get_number(struct options *, const char *);
const struct cmd_list *options_get_command(struct options *, const char *);
struct options_entry * printflike(4, 5) options_set_string(struct options *,
const char *, int, const char *, ...);
struct options_entry *options_set_number(struct options *, const char *,
long long);
struct options_entry *options_set_command(struct options *, const char *,
struct cmd_list *);
int options_scope_from_name(struct args *, int,
const char *, struct cmd_find_state *, struct options **,
char **);
@ -2341,6 +2429,8 @@ typedef void (*job_free_cb) (void *);
#define JOB_NOWAIT 0x1
#define JOB_KEEPWRITE 0x2
#define JOB_PTY 0x4
#define JOB_DEFAULTSHELL 0x8
#define JOB_SHOWSTDERR 0x10
struct job *job_run(const char *, int, char **, struct environ *,
struct session *, const char *, job_update_cb,
job_complete_cb, job_free_cb, void *, int, int, int);
@ -2403,6 +2493,7 @@ void tty_cell(struct tty *, const struct grid_cell *,
int tty_init(struct tty *, struct client *);
void tty_resize(struct tty *);
void tty_set_size(struct tty *, u_int, u_int, u_int, u_int);
void tty_invalidate(struct tty *);
void tty_start_tty(struct tty *);
void tty_send_requests(struct tty *);
void tty_repeat_requests(struct tty *);
@ -2575,6 +2666,7 @@ int cmd_find_from_nothing(struct cmd_find_state *, int);
/* cmd.c */
extern const struct cmd_entry *cmd_table[];
const struct cmd_entry *cmd_find(const char *, char **);
void printflike(3, 4) cmd_log_argv(int, char **, const char *, ...);
void cmd_prepend_argv(int *, char ***, const char *);
void cmd_append_argv(int *, char ***, const char *);
@ -2594,12 +2686,12 @@ struct cmd *cmd_copy(struct cmd *, int, char **);
void cmd_free(struct cmd *);
char *cmd_print(struct cmd *);
struct cmd_list *cmd_list_new(void);
struct cmd_list *cmd_list_copy(struct cmd_list *, int, char **);
struct cmd_list *cmd_list_copy(const struct cmd_list *, int, char **);
void cmd_list_append(struct cmd_list *, struct cmd *);
void cmd_list_append_all(struct cmd_list *, struct cmd_list *);
void cmd_list_move(struct cmd_list *, struct cmd_list *);
void cmd_list_free(struct cmd_list *);
char *cmd_list_print(struct cmd_list *, int);
char *cmd_list_print(const struct cmd_list *, int);
struct cmd *cmd_list_first(struct cmd_list *);
struct cmd *cmd_list_next(struct cmd *);
int cmd_list_all_have(struct cmd_list *, int);
@ -2665,7 +2757,7 @@ u_int cmdq_next(struct client *);
struct cmdq_item *cmdq_running(struct client *);
void cmdq_guard(struct cmdq_item *, const char *, int);
void printflike(2, 3) cmdq_print(struct cmdq_item *, const char *, ...);
void cmdq_print_data(struct cmdq_item *, int, struct evbuffer *);
void cmdq_print_data(struct cmdq_item *, struct evbuffer *);
void printflike(2, 3) cmdq_error(struct cmdq_item *, const char *, ...);
/* cmd-wait-for.c */
@ -2824,7 +2916,7 @@ struct style_range *status_get_range(struct client *, u_int, u_int);
void status_init(struct client *);
void status_free(struct client *);
int status_redraw(struct client *);
void printflike(5, 6) status_message_set(struct client *, int, int, int,
void printflike(6, 7) status_message_set(struct client *, int, int, int, int,
const char *, ...);
void status_message_clear(struct client *);
int status_message_redraw(struct client *);
@ -2849,6 +2941,7 @@ void recalculate_sizes(void);
void recalculate_sizes_now(int);
/* input.c */
#define INPUT_BUF_DEFAULT_SIZE 1048576
struct input_ctx *input_init(struct window_pane *, struct bufferevent *,
struct colour_palette *);
void input_free(struct input_ctx *);
@ -2860,6 +2953,7 @@ void input_parse_screen(struct input_ctx *, struct screen *,
screen_write_init_ctx_cb, void *, u_char *, size_t);
void input_reply_clipboard(struct bufferevent *, const char *, size_t,
const char *);
void input_set_buffer_size(size_t);
/* input-key.c */
void input_key_build(void);
@ -2874,7 +2968,8 @@ int colour_join_rgb(u_char, u_char, u_char);
void colour_split_rgb(int, u_char *, u_char *, u_char *);
int colour_force_rgb(int);
const char *colour_tostring(int);
int colour_fromstring(const char *s);
enum client_theme colour_totheme(int);
int colour_fromstring(const char *);
int colour_256toRGB(int);
int colour_256to16(int);
int colour_byname(const char *);
@ -2893,6 +2988,7 @@ int attributes_fromstring(const char *);
/* grid.c */
extern const struct grid_cell grid_default_cell;
void grid_empty_line(struct grid *, u_int, u_int);
void grid_set_tab(struct grid_cell *, u_int);
int grid_cells_equal(const struct grid_cell *, const struct grid_cell *);
int grid_cells_look_equal(const struct grid_cell *,
const struct grid_cell *);
@ -2924,6 +3020,7 @@ void grid_reflow(struct grid *, u_int);
void grid_wrap_position(struct grid *, u_int, u_int, u_int *, u_int *);
void grid_unwrap_position(struct grid *, u_int *, u_int *, u_int, u_int);
u_int grid_line_length(struct grid *, u_int);
int grid_in_set(struct grid *, u_int, u_int, const char *);
/* grid-reader.c */
void grid_reader_start(struct grid_reader *, struct grid *, u_int, u_int);
@ -3047,7 +3144,7 @@ void screen_write_alternateoff(struct screen_write_ctx *,
/* screen-redraw.c */
void screen_redraw_screen(struct client *);
void screen_redraw_pane(struct client *, struct window_pane *);
void screen_redraw_pane(struct client *, struct window_pane *, int);
/* screen.c */
void screen_init(struct screen *, u_int, u_int, u_int);
@ -3055,6 +3152,7 @@ void screen_reinit(struct screen *);
void screen_free(struct screen *);
void screen_reset_tabs(struct screen *);
void screen_reset_hyperlinks(struct screen *);
void screen_set_default_cursor(struct screen *, struct options *);
void screen_set_cursor_style(u_int, enum screen_cursor_style *, int *);
void screen_set_cursor_colour(struct screen *, int);
int screen_set_title(struct screen *, const char *);
@ -3142,6 +3240,8 @@ void window_pane_reset_mode_all(struct window_pane *);
int window_pane_key(struct window_pane *, struct client *,
struct session *, struct winlink *, key_code,
struct mouse_event *);
void window_pane_paste(struct window_pane *, key_code, char *,
size_t);
int window_pane_visible(struct window_pane *);
int window_pane_exited(struct window_pane *);
u_int window_pane_search(struct window_pane *, const char *, int,
@ -3169,6 +3269,14 @@ void window_pane_update_used_data(struct window_pane *,
void window_set_fill_character(struct window *);
void window_pane_default_cursor(struct window_pane *);
int window_pane_mode(struct window_pane *);
int window_pane_show_scrollbar(struct window_pane *, int);
int window_pane_get_bg(struct window_pane *);
int window_pane_get_fg(struct window_pane *);
int window_pane_get_fg_control_client(struct window_pane *);
int window_pane_get_bg_control_client(struct window_pane *);
int window_get_bg_client(struct window_pane *);
enum client_theme window_pane_get_theme(struct window_pane *);
void window_pane_send_theme_update(struct window_pane *);
/* layout.c */
u_int layout_count_cells(struct layout_cell *);
@ -3222,6 +3330,7 @@ typedef int (*mode_tree_search_cb)(void *, void *, const char *);
typedef void (*mode_tree_menu_cb)(void *, struct client *, key_code);
typedef u_int (*mode_tree_height_cb)(void *, u_int);
typedef key_code (*mode_tree_key_cb)(void *, void *, u_int);
typedef int (*mode_tree_swap_cb)(void *, void *);
typedef void (*mode_tree_each_cb)(void *, void *, struct client *, key_code);
u_int mode_tree_count_tagged(struct mode_tree_data *);
void *mode_tree_get_current(struct mode_tree_data *);
@ -3236,8 +3345,9 @@ void mode_tree_up(struct mode_tree_data *, int);
int mode_tree_down(struct mode_tree_data *, int);
struct mode_tree_data *mode_tree_start(struct window_pane *, struct args *,
mode_tree_build_cb, mode_tree_draw_cb, mode_tree_search_cb,
mode_tree_menu_cb, mode_tree_height_cb, mode_tree_key_cb, void *,
const struct menu_item *, const char **, u_int, struct screen **);
mode_tree_menu_cb, mode_tree_height_cb, mode_tree_key_cb,
mode_tree_swap_cb, void *, const struct menu_item *, const char **,
u_int, struct screen **);
void mode_tree_zoom(struct mode_tree_data *, struct args *);
void mode_tree_build(struct mode_tree_data *);
void mode_tree_free(struct mode_tree_data *);
@ -3247,6 +3357,7 @@ struct mode_tree_item *mode_tree_add(struct mode_tree_data *,
const char *, int);
void mode_tree_draw_as_parent(struct mode_tree_item *);
void mode_tree_no_tag(struct mode_tree_item *);
void mode_tree_align(struct mode_tree_item *, int);
void mode_tree_remove(struct mode_tree_data *, struct mode_tree_item *);
void mode_tree_draw(struct mode_tree_data *);
int mode_tree_key(struct mode_tree_data *, struct client *, key_code *,
@ -3274,11 +3385,15 @@ void printflike(3, 4) window_copy_add(struct window_pane *, int, const char *,
...);
void printflike(3, 0) window_copy_vadd(struct window_pane *, int, const char *,
va_list);
void window_copy_scroll(struct window_pane *, int, u_int, int);
void window_copy_pageup(struct window_pane *, int);
void window_copy_pagedown(struct window_pane *, int, int);
void window_copy_start_drag(struct client *, struct mouse_event *);
char *window_copy_get_word(struct window_pane *, u_int, u_int);
char *window_copy_get_line(struct window_pane *, u_int);
int window_copy_get_current_offset(struct window_pane *, u_int *,
u_int *);
char *window_copy_get_hyperlink(struct window_pane *, u_int, u_int);
/* window-option.c */
extern const struct window_mode window_customize_mode;
@ -3325,9 +3440,12 @@ void control_notify_paste_buffer_deleted(const char *);
/* session.c */
extern struct sessions sessions;
extern struct session_groups session_groups;
extern u_int next_session_id;
int session_cmp(struct session *, struct session *);
RB_PROTOTYPE(sessions, session, entry, session_cmp);
int session_group_cmp(struct session_group *, struct session_group *s2);
RB_PROTOTYPE(session_groups, session_group, entry, session_group_cmp);
int session_alive(struct session *);
struct session *session_find(const char *);
struct session *session_find_by_id_str(const char *);
@ -3360,11 +3478,12 @@ void session_group_synchronize_from(struct session *);
u_int session_group_count(struct session_group *);
u_int session_group_attached_count(struct session_group *);
void session_renumber_windows(struct session *);
void session_theme_changed(struct session *);
/* utf8.c */
enum utf8_state utf8_towc (const struct utf8_data *, wchar_t *);
enum utf8_state utf8_fromwc(wchar_t wc, struct utf8_data *);
int utf8_in_table(wchar_t, const wchar_t *, u_int);
void utf8_update_width_cache(void);
utf8_char utf8_build_one(u_char);
enum utf8_state utf8_from_data(const struct utf8_data *, utf8_char *);
void utf8_to_data(utf8_char, struct utf8_data *);
@ -3463,6 +3582,8 @@ void style_apply(struct grid_cell *, struct options *,
const char *, struct format_tree *);
void style_set(struct style *, const struct grid_cell *);
void style_copy(struct style *, struct style *);
void style_set_scrollbar_style_from_option(struct style *,
struct options *);
/* spawn.c */
struct winlink *spawn_window(struct spawn_context *, char **);
@ -3481,7 +3602,7 @@ int image_scroll_up(struct screen *, u_int);
/* image-sixel.c */
#define SIXEL_COLOUR_REGISTERS 1024
struct sixel_image *sixel_parse(const char *, size_t, u_int, u_int);
struct sixel_image *sixel_parse(const char *, size_t, u_int, u_int, u_int);
void sixel_free(struct sixel_image *);
void sixel_log(struct sixel_image *);
void sixel_size_in_cells(struct sixel_image *, u_int *, u_int *);

View File

@ -488,6 +488,10 @@ tty_default_features(int *feat, const char *name, u_int version)
.features = TTY_FEATURES_BASE_MODERN_XTERM
",cstyle,extkeys,margins,usstyle,sync,osc7,hyperlinks"
},
{ .name = "foot",
.features = TTY_FEATURES_BASE_MODERN_XTERM
",cstyle,extkeys"
},
{ .name = "XTerm",
/*
* xterm also supports DECSLRM and DECFRA, but they can be

View File

@ -208,11 +208,15 @@ static const struct tty_default_key_raw tty_default_raw_keys[] = {
{ "\033[O", KEYC_FOCUS_OUT },
/* Paste keys. */
{ "\033[200~", KEYC_PASTE_START },
{ "\033[201~", KEYC_PASTE_END },
{ "\033[200~", KEYC_PASTE_START|KEYC_IMPLIED_META },
{ "\033[201~", KEYC_PASTE_END|KEYC_IMPLIED_META },
/* Extended keys. */
{ "\033[1;5Z", '\011'|KEYC_CTRL|KEYC_SHIFT },
/* Theme reporting. */
{ "\033[?997;1n", KEYC_REPORT_DARK_THEME },
{ "\033[?997;2n", KEYC_REPORT_LIGHT_THEME },
};
/* Default xterm keys. */
@ -654,6 +658,74 @@ tty_keys_next1(struct tty *tty, const char *buf, size_t len, key_code *key,
return (-1);
}
/* Process window size change escape sequences. */
static int
tty_keys_winsz(struct tty *tty, const char *buf, size_t len, size_t *size)
{
struct client *c = tty->client;
size_t end;
char tmp[64];
u_int sx, sy, xpixel, ypixel, char_x, char_y;
*size = 0;
/* If we did not request this, ignore it. */
if (!(tty->flags & TTY_WINSIZEQUERY))
return (-1);
/* First two bytes are always \033[. */
if (buf[0] != '\033')
return (-1);
if (len == 1)
return (1);
if (buf[1] != '[')
return (-1);
if (len == 2)
return (1);
/*
* Stop at either 't' or anything that isn't a
* number or ';'.
*/
for (end = 2; end < len && end != sizeof tmp; end++) {
if (buf[end] == 't')
break;
if (!isdigit((u_char)buf[end]) && buf[end] != ';')
break;
}
if (end == len)
return (1);
if (end == sizeof tmp || buf[end] != 't')
return (-1);
/* Copy to the buffer. */
memcpy(tmp, buf + 2, end - 2);
tmp[end - 2] = '\0';
/* Try to parse the window size sequence. */
if (sscanf(tmp, "8;%u;%u", &sy, &sx) == 2) {
/* Window size in characters. */
tty_set_size(tty, sx, sy, tty->xpixel, tty->ypixel);
*size = end + 1;
return (0);
} else if (sscanf(tmp, "4;%u;%u", &ypixel, &xpixel) == 2) {
/* Window size in pixels. */
char_x = (xpixel && tty->sx) ? xpixel / tty->sx : 0;
char_y = (ypixel && tty->sy) ? ypixel / tty->sy : 0;
tty_set_size(tty, tty->sx, tty->sy, char_x, char_y);
tty_invalidate(tty);
tty->flags &= ~TTY_WINSIZEQUERY;
*size = end + 1;
return (0);
}
log_debug("%s: unrecognized window size sequence: %s", c->name, tmp);
return (-1);
}
/* Process at least one key in the buffer. Return 0 if no keys present. */
int
tty_keys_next(struct tty *tty)
@ -723,10 +795,12 @@ tty_keys_next(struct tty *tty)
switch (tty_keys_colours(tty, buf, len, &size, &tty->fg, &tty->bg)) {
case 0: /* yes */
key = KEYC_UNKNOWN;
session_theme_changed(c->session);
goto complete_key;
case -1: /* no, or not valid */
break;
case 1: /* partial */
session_theme_changed(c->session);
goto partial_key;
}
@ -754,6 +828,17 @@ tty_keys_next(struct tty *tty)
goto partial_key;
}
/* Check for window size query */
switch (tty_keys_winsz(tty, buf, len, &size)) {
case 0: /* yes */
key = KEYC_UNKNOWN;
goto complete_key;
case -1: /* no, or not valid */
break;
case 1: /* partial */
goto partial_key;
}
first_key:
/* Try to lookup complete key. */
n = tty_keys_next1(tty, buf, len, &key, &size, expired);
@ -806,6 +891,17 @@ first_key:
if ((key & KEYC_MASK_KEY) == C0_NUL)
key = ' ' | KEYC_CTRL | (key & KEYC_META);
/*
* Check for backspace key using termios VERASE - the terminfo
* kbs entry is extremely unreliable, so cannot be safely
* used. termios should have a better idea.
*/
bspace = tty->tio.c_cc[VERASE];
if (bspace != _POSIX_VDISABLE && key == bspace) {
log_debug("%s: key %#llx is backspace", c->name, key);
key = KEYC_BSPACE;
}
/*
* Fix up all C0 control codes that don't have a dedicated key into
* corresponding Ctrl keys. Convert characters in the A-Z range into
@ -841,6 +937,11 @@ partial_key:
delay = options_get_number(global_options, "escape-time");
if (delay == 0)
delay = 1;
if ((tty->flags & TTY_ALL_REQUEST_FLAGS) != TTY_ALL_REQUEST_FLAGS) {
log_debug("%s: increasing delay for active DA query", c->name);
if (delay < 500)
delay = 500;
}
tv.tv_sec = delay / 1000;
tv.tv_usec = (delay % 1000) * 1000L;
@ -856,18 +957,6 @@ partial_key:
complete_key:
log_debug("%s: complete key %.*s %#llx", c->name, (int)size, buf, key);
/*
* Check for backspace key using termios VERASE - the terminfo
* kbs entry is extremely unreliable, so cannot be safely
* used. termios should have a better idea.
*/
bspace = tty->tio.c_cc[VERASE];
if (bspace != _POSIX_VDISABLE && (key & KEYC_MASK_KEY) == bspace)
key = (key & KEYC_MASK_MODIFIERS)|KEYC_BSPACE;
/* Remove data from buffer. */
evbuffer_drain(tty->in, size);
/* Remove key timer. */
if (event_initialized(&tty->key_timer))
evtimer_del(&tty->key_timer);
@ -886,13 +975,23 @@ complete_key:
/* Fire the key. */
if (key != KEYC_UNKNOWN) {
event = xmalloc(sizeof *event);
event = xcalloc(1, sizeof *event);
event->key = key;
memcpy(&event->m, &m, sizeof event->m);
if (!server_client_handle_key(c, event))
event->buf = xmalloc(size);
event->len = size;
memcpy (event->buf, buf, event->len);
if (!server_client_handle_key(c, event)) {
free(event->buf);
free(event);
}
}
/* Remove data from buffer. */
evbuffer_drain(tty->in, size);
return (1);
discard_key:
@ -930,7 +1029,7 @@ tty_keys_extended_key(struct tty *tty, const char *buf, size_t len,
u_int number, modifiers;
char tmp[64];
cc_t bspace;
key_code nkey;
key_code nkey, onlykey;
struct utf8_data ud;
utf8_char uc;
@ -962,8 +1061,8 @@ tty_keys_extended_key(struct tty *tty, const char *buf, size_t len,
return (-1);
/* Copy to the buffer. */
memcpy(tmp, buf + 2, end);
tmp[end] = '\0';
memcpy(tmp, buf + 2, end - 2);
tmp[end - 2] = '\0';
/* Try to parse either form of key. */
if (buf[end] == '~') {
@ -983,7 +1082,7 @@ tty_keys_extended_key(struct tty *tty, const char *buf, size_t len,
nkey = number;
/* Convert UTF-32 codepoint into internal representation. */
if (nkey & ~0x7f) {
if (nkey != KEYC_BSPACE && nkey & ~0x7f) {
if (utf8_fromwc(nkey, &ud) == UTF8_DONE &&
utf8_from_data(&ud, &uc) == UTF8_DONE)
nkey = uc;
@ -994,13 +1093,7 @@ tty_keys_extended_key(struct tty *tty, const char *buf, size_t len,
/* Update the modifiers. */
if (modifiers > 0) {
modifiers--;
/*
* The Shift modifier may not be reported in some input modes,
* which is unfortunate, as in general case determining if a
* character is shifted or not requires knowing the input
* keyboard layout. So we only fix up the trivial case.
*/
if (modifiers & 1 || (nkey >= 'A' && nkey <= 'Z'))
if (modifiers & 1)
nkey |= KEYC_SHIFT;
if (modifiers & 2)
nkey |= (KEYC_META|KEYC_IMPLIED_META); /* Alt */
@ -1014,6 +1107,26 @@ tty_keys_extended_key(struct tty *tty, const char *buf, size_t len,
if ((nkey & KEYC_MASK_KEY) == '\011' && (nkey & KEYC_SHIFT))
nkey = KEYC_BTAB | (nkey & ~KEYC_MASK_KEY & ~KEYC_SHIFT);
/*
* Deal with the Shift modifier when present alone. The problem is that
* in mode 2 some terminals would report shifted keys, like S-a, as
* just A, and some as S-A.
*
* Because we need an unambiguous internal representation, and because
* restoring the Shift modifier when it's missing would require knowing
* the keyboard layout, and because S-A would cause a lot of issues
* downstream, we choose to lose the Shift for all printable
* characters.
*
* That still leaves some ambiguity, such as C-S-A vs. C-A, but that's
* OK, and applications can handle that.
*/
onlykey = nkey & KEYC_MASK_KEY;
if (((onlykey > 0x20 && onlykey < 0x7f) ||
KEYC_IS_UNICODE(nkey)) &&
(nkey & KEYC_MASK_MODIFIERS) == KEYC_SHIFT)
nkey &= ~KEYC_SHIFT;
if (log_get_level() != 0) {
log_debug("%s: extended key %.*s is %llx (%s)", c->name,
(int)*size, buf, nkey, key_string_lookup_key(nkey, 1));
@ -1492,6 +1605,8 @@ tty_keys_extended_device_attributes(struct tty *tty, const char *buf,
tty_default_features(features, "XTerm", 0);
else if (strncmp(tmp, "mintty ", 7) == 0)
tty_default_features(features, "mintty", 0);
else if (strncmp(tmp, "foot(", 5) == 0)
tty_default_features(features, "foot", 0);
log_debug("%s: received extended DA %.*s", c->name, (int)*size, buf);
free(c->term_type);

64
tty.c
View File

@ -42,7 +42,6 @@ static void tty_cursor_pane(struct tty *, const struct tty_ctx *, u_int,
u_int);
static void tty_cursor_pane_unless_wrap(struct tty *,
const struct tty_ctx *, u_int, u_int);
static void tty_invalidate(struct tty *);
static void tty_colours(struct tty *, const struct grid_cell *);
static void tty_check_fg(struct tty *, struct colour_palette *,
struct grid_cell *);
@ -140,6 +139,14 @@ tty_resize(struct tty *tty)
ypixel = 0;
} else
ypixel = ws.ws_ypixel / sy;
if ((xpixel == 0 || ypixel == 0) &&
tty->out != NULL &&
!(tty->flags & TTY_WINSIZEQUERY) &&
(tty->term->flags & TERM_VT100LIKE)) {
tty_puts(tty, "\033[18t\033[14t");
tty->flags |= TTY_WINSIZEQUERY;
}
} else {
sx = 80;
sy = 24;
@ -349,6 +356,11 @@ tty_start_tty(struct tty *tty)
if (tty_term_has(tty->term, TTYC_ENBP))
tty_putcode(tty, TTYC_ENBP);
if (tty->term->flags & TERM_VT100LIKE) {
/* Subscribe to theme changes and request theme now. */
tty_puts(tty, "\033[?2031h\033[?996n");
}
evtimer_set(&tty->start_timer, tty_start_timer_callback, tty);
evtimer_add(&tty->start_timer, &tv);
@ -461,6 +473,9 @@ tty_stop_tty(struct tty *tty)
tty_raw(tty, tty_term_string(tty->term, TTYC_DSMG));
tty_raw(tty, tty_term_string(tty->term, TTYC_RMCUP));
if (tty->term->flags & TERM_VT100LIKE)
tty_raw(tty, "\033[?2031l");
setblocking(c->fd, 1);
}
@ -1359,6 +1374,8 @@ tty_check_codeset(struct tty *tty, const struct grid_cell *gc)
/* Characters less than 0x7f are always fine, no matter what. */
if (gc->data.size == 1 && *gc->data.data < 0x7f)
return (gc);
if (gc->flags & GRID_FLAG_TAB)
return (gc);
/* UTF-8 terminal and a UTF-8 character - fine. */
if (tty->client->flags & CLIENT_UTF8)
@ -2369,7 +2386,7 @@ tty_reset(struct tty *tty)
memcpy(&tty->last_cell, &grid_default_cell, sizeof tty->last_cell);
}
static void
void
tty_invalidate(struct tty *tty)
{
memcpy(&tty->cell, &grid_default_cell, sizeof tty->cell);
@ -2739,8 +2756,7 @@ tty_attributes(struct tty *tty, const struct grid_cell *gc,
if (changed & GRID_ATTR_ITALICS)
tty_set_italics(tty);
if (changed & GRID_ATTR_ALL_UNDERSCORE) {
if ((changed & GRID_ATTR_UNDERSCORE) ||
!tty_term_has(tty->term, TTYC_SMULX))
if (changed & GRID_ATTR_UNDERSCORE)
tty_putcode(tty, TTYC_SMUL);
else if (changed & GRID_ATTR_UNDERSCORE_2)
tty_putcode_i(tty, TTYC_SMULX, 2);
@ -2865,13 +2881,23 @@ tty_check_fg(struct tty *tty, struct colour_palette *palette,
/* Is this a 256-colour colour? */
if (gc->fg & COLOUR_FLAG_256) {
/* And not a 256 colour mode? */
if (colours < 256) {
gc->fg = colour_256to16(gc->fg);
if (gc->fg & 8) {
gc->fg &= 7;
if (colours >= 16)
gc->fg += 90;
}
if (colours >= 256)
return;
gc->fg = colour_256to16(gc->fg);
if (~gc->fg & 8)
return;
gc->fg &= 7;
if (colours >= 16)
gc->fg += 90;
else {
/*
* Mapping to black-on-black or white-on-white is not
* much use, so change the foreground.
*/
if (gc->fg == 0 && gc->bg == 0)
gc->fg = 7;
else if (gc->fg == 7 && gc->bg == 7)
gc->fg = 0;
}
return;
}
@ -2919,14 +2945,14 @@ tty_check_bg(struct tty *tty, struct colour_palette *palette,
* palette. Bold background doesn't exist portably, so just
* discard the bold bit if set.
*/
if (colours < 256) {
gc->bg = colour_256to16(gc->bg);
if (gc->bg & 8) {
gc->bg &= 7;
if (colours >= 16)
gc->bg += 90;
}
}
if (colours >= 256)
return;
gc->bg = colour_256to16(gc->bg);
if (~gc->bg & 8)
return;
gc->bg &= 7;
if (colours >= 16)
gc->bg += 90;
return;
}

View File

@ -24,40 +24,6 @@
#include "tmux.h"
static const wchar_t utf8_modifier_table[] = {
0x1F1E6,
0x1F1E7,
0x1F1E8,
0x1F1E9,
0x1F1EA,
0x1F1EB,
0x1F1EC,
0x1F1ED,
0x1F1EE,
0x1F1EF,
0x1F1F0,
0x1F1F1,
0x1F1F2,
0x1F1F3,
0x1F1F4,
0x1F1F5,
0x1F1F6,
0x1F1F7,
0x1F1F8,
0x1F1F9,
0x1F1FA,
0x1F1FB,
0x1F1FC,
0x1F1FD,
0x1F1FE,
0x1F1FF,
0x1F3FB,
0x1F3FC,
0x1F3FD,
0x1F3FE,
0x1F3FF
};
/* Has this got a zero width joiner at the end? */
int
utf8_has_zwj(const struct utf8_data *ud)
@ -93,8 +59,39 @@ utf8_is_modifier(const struct utf8_data *ud)
if (utf8_towc(ud, &wc) != UTF8_DONE)
return (0);
if (!utf8_in_table(wc, utf8_modifier_table,
nitems(utf8_modifier_table)))
return (0);
return (1);
switch (wc) {
case 0x1F1E6:
case 0x1F1E7:
case 0x1F1E8:
case 0x1F1E9:
case 0x1F1EA:
case 0x1F1EB:
case 0x1F1EC:
case 0x1F1ED:
case 0x1F1EE:
case 0x1F1EF:
case 0x1F1F0:
case 0x1F1F1:
case 0x1F1F2:
case 0x1F1F3:
case 0x1F1F4:
case 0x1F1F5:
case 0x1F1F6:
case 0x1F1F7:
case 0x1F1F8:
case 0x1F1F9:
case 0x1F1FA:
case 0x1F1FB:
case 0x1F1FC:
case 0x1F1FD:
case 0x1F1FE:
case 0x1F1FF:
case 0x1F3FB:
case 0x1F3FC:
case 0x1F3FD:
case 0x1F3FE:
case 0x1F3FF:
return (1);
}
return (0);
}

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