255 Commits
3.0a ... 3.1

Author SHA1 Message Date
Nicholas Marriott
63a69fe085 3.1-rc. 2020-02-04 07:21:04 +00:00
Nicholas Marriott
43b36752ce Remove a duplicate entry. 2020-02-03 20:26:03 +00:00
Thomas Adam
19d5f4a0bd Merge branch 'obsd-master' 2020-02-03 14:01:25 +00:00
nicm
265164d251 Instead of passing titles through vis() which doubles backslashes, just
ignore any containing control characters or invalid UTF-8. GitHub issue 2070.
2020-02-03 13:46:27 +00:00
Thomas Adam
6f0241e645 Merge branch 'obsd-master' 2020-01-30 10:01:24 +00:00
nicm
87bcc0c7e0 Remove bind-key -c which doesn't do anything and is undocumented. 2020-01-30 08:02:25 +00:00
Thomas Adam
32be954bdd Merge branch 'obsd-master' 2020-01-29 18:01:24 +00:00
nicm
44dad918f8 Warn if a message type that is no longer used is received. 2020-01-29 16:22:32 +00:00
Thomas Adam
bc36700d05 Merge branch 'obsd-master' 2020-01-29 16:01:24 +00:00
nicm
531daba584 Do not send DA and DSR again if already have a response. 2020-01-29 15:07:49 +00:00
Thomas Adam
8b22da69b6 Merge branch 'obsd-master' 2020-01-29 10:01:25 +00:00
nicm
7a15d10bf4 Remove extra Pp (from jmc) and add a missing word. 2020-01-29 08:28:17 +00:00
Nicholas Marriott
7f3feb1896 Add to CHANGES. 2020-01-28 15:52:04 +00:00
Thomas Adam
7eada28f96 Merge branch 'obsd-master' 2020-01-28 14:01:25 +00:00
nicm
b905c5d455 If ALL clients are readonly, allow them to affect the size, suggested by Thomas Sattler. 2020-01-28 13:23:24 +00:00
nicm
e388702260 Ignore empty commands rather than adding them to the command list rather
than trying to skip them later, fixes problem reported by M Kelly.
2020-01-28 13:10:14 +00:00
Thomas Adam
60ab714451 Merge branch 'obsd-master' 2020-01-28 12:01:28 +00:00
nicm
a6129e9974 If we can identify the terminal as iTerm2 or as tmux, we can be sure
they support 256 and RGB colours, so set those flags too.
2020-01-28 11:39:51 +00:00
nicm
84995ae172 -V also needs to go in usage. 2020-01-28 11:31:31 +00:00
Nicholas Marriott
685eb381de Fix for version changes. 2020-01-28 11:28:30 +00:00
Thomas Adam
ee3d3db364 Merge branch 'obsd-master' 2020-01-28 11:17:08 +00:00
nicm
90e962fff8 Add support for the iTerm2 DSR 1337 sequence to get the terminal version. 2020-01-28 10:59:29 +00:00
nicm
f165221dc4 Reduce a difference with portable tmux by adding the -V flag and
#{version} format; on OpenBSD these just report the OpenBSD version.
2020-01-28 10:44:30 +00:00
nicm
32816eaebd Set up working directory before killing the existing pane on respawn. 2020-01-28 10:21:21 +00:00
Thomas Adam
7cdf5ee9bc Merge branch 'obsd-master' 2020-01-28 10:01:25 +00:00
nicm
24350879cd Add a define for flags meaning a client is not attached, and fix
unattached counter, reported by Thomas Sattler.
2020-01-28 08:06:11 +00:00
Thomas Adam
f3ea318a04 Merge branch 'obsd-master' 2020-01-27 10:01:27 +00:00
nicm
2c38e01b54 Expand description of start-server. 2020-01-27 09:04:47 +00:00
nicm
d0b8d036be Add support for adding a note to a key binding (with bind-key -N) and
use this to add descriptions to the default key bindings. A new -N flag
to list-keys shows key bindings with notes rather than the default
bind-key command used to create them. Change the default ? binding to
use this to show a readable summary of keys.

Also extend command-prompt to return the name of the key pressed and add
a default binding (/) to show the note for the next key pressed

Suggested by Alex Tremblay in GitHub issue 2000.
2020-01-27 08:53:13 +00:00
nicm
2e39b621c9 Change so that assignments may be specified alone - a command isn't
required. GitHub issue 2062.
2020-01-27 08:23:42 +00:00
Thomas Adam
0eb7b54731 Merge branch 'obsd-master' 2020-01-25 18:01:26 +00:00
Nicholas Marriott
74b424075c Use FNM_IGNORECASE if present, from Eric N Vander Weele in GitHub issue 2067. 2020-01-25 16:41:49 +00:00
nicm
9169ee0e87 Mention swap-window -d, GitHub issue 2068. 2020-01-25 16:40:32 +00:00
Nicholas Marriott
cdf138372c Add to CHANGES. 2020-01-14 16:02:22 +00:00
Thomas Adam
a01c9ffc6c Merge branch 'obsd-master' 2020-01-13 14:01:25 +00:00
nicm
da515570dc Stop handling DA and DSR after a second (they should be the first thing
sent) so this should be plenty.
2020-01-13 11:59:21 +00:00
Thomas Adam
8457f54edc Merge branch 'obsd-master' 2020-01-13 10:01:27 +00:00
nicm
835a6c0cf0 Be more specific in the DSR we are looking for so it doesn't get
confused with mouse sequences. Also set a flag and don't bother checking
for it if we have already seen it (same for DA), and don't check if we
never asked for it.
2020-01-13 08:12:53 +00:00
nicm
04eee2410d Treat plausible but invalid keys (like C-BSpace) as literal like any
other unrecognised string passed to send-keys. Reported by Anthony
Sottile in GitHub issue 2049.
2020-01-13 07:51:54 +00:00
Thomas Adam
3e701309a4 Merge branch 'obsd-master' 2020-01-13 00:01:24 +00:00
Thomas Adam
e9b1294331 Merge branch 'obsd-master' 2020-01-12 22:01:26 +00:00
nicm
381333c4a9 Detect iTerm2 and enable DECSLRM. 2020-01-12 22:00:20 +00:00
nicm
193e637de0 The terminal type was never as much use as I expected so remove it in
favour of a couple of flags for the features used (DECSLRM and DECFRA).
Also rename the flag for no xenl to be more obvious while here.
2020-01-12 21:07:07 +00:00
nicm
deb734c7f6 Loop over all DA features, don't skip the first. 2020-01-12 20:20:20 +00:00
Thomas Adam
61b075a263 Merge branch 'obsd-master' 2020-01-08 16:01:23 +00:00
nicm
36eb16ce7d Do not hang in format_trim_* on invalid UTF-8 characters. 2020-01-08 14:40:52 +00:00
Thomas Adam
ed16f51e26 Merge branch 'obsd-master' 2020-01-08 08:01:24 +00:00
nicm
6628e542b5 Add -Z to default switch-client command in tree mode, matches previous
behaviour.
2020-01-08 06:38:55 +00:00
Thomas Adam
507816b1d6 Merge branch 'obsd-master' 2020-01-05 22:01:24 +00:00
nicm
73b8c2ef3c Common function to free key bindings. 2020-01-05 20:39:25 +00:00
Thomas Adam
36169d8a68 Merge branch 'obsd-master' 2020-01-05 14:01:23 +00:00
nicm
7c6c66cc3c Send errors to stdout in control mode so they don't get reordered with
other output, reported by George Nachman in GitHub issue 2048.
2020-01-05 12:51:43 +00:00
Thomas Adam
6d3d47c25b Merge branch 'obsd-master' 2020-01-04 20:01:26 +00:00
nicm
1870cc70ef Add ~ to quoted characters for %%%, reported by tb@. 2020-01-04 18:01:56 +00:00
Thomas Adam
6b0091e185 Merge branch 'obsd-master' 2020-01-02 14:01:26 +00:00
nicm
a770a3bf7e Add CMD_FIND_DEFAULT_MARKED to join-pane like move-pane, from
davidegirardi in GitHub issue 2046.
2020-01-02 13:44:17 +00:00
Thomas Adam
7110226b96 Merge branch 'obsd-master' 2020-01-02 00:01:29 +00:00
nicm
ac85a3e0d3 Document client exit messages. 2020-01-01 22:12:05 +00:00
nicm
9cc603cbad Fix format expansion in window names, reported by Suraj N Kurapati. 2020-01-01 21:51:33 +00:00
Thomas Adam
566ab9aa28 Merge branch 'obsd-master' 2019-12-30 22:01:25 +00:00
nicm
206d878127 Do not let readonly clients limit the size, suggested by Max Barraclough
in GitHub issue 2042.
2019-12-30 21:24:55 +00:00
Thomas Adam
47d06cb023 Merge branch 'obsd-master' 2019-12-27 20:01:24 +00:00
nicm
4ea07716de Support regex search in copy mode, from Anindya Mukherjee in GitHub
issue 2038.
2019-12-27 18:42:49 +00:00
Thomas Adam
0aa6c6f647 Merge branch 'obsd-master' 2019-12-26 16:01:25 +00:00
nicm
88ee5b1a73 Pass correct value into iterator callback for time formats. 2019-12-26 14:48:29 +00:00
Thomas Adam
b931bbb319 Merge branch 'obsd-master' 2019-12-26 12:01:26 +00:00
nicm
817d199cbb Add a number of new formats to inspect what sessions and clients a
window is present or active in. From Tyler Culp in GitHub issue 2034.
2019-12-26 11:04:58 +00:00
Thomas Adam
67d2408279 Merge branch 'obsd-master' 2019-12-24 12:01:25 +00:00
nicm
07e37479c2 Fix name of option, GitHub issue 2030. 2019-12-24 09:57:11 +00:00
Thomas Adam
a6b1cbba02 Merge branch 'obsd-master' 2019-12-21 18:01:24 +00:00
tim
5cd00eda0b Restore source-file -q behaviour, broken in r1.42; OK nicm@ 2019-12-21 17:30:48 +00:00
Thomas Adam
745233d6a1 Merge branch 'obsd-master' 2019-12-19 10:01:26 +00:00
nicm
1764f66b7d When adding a list with multiple commands to the queue, the next item to
insert after needs to be the last one added, not the first. Reported by
Jason Kim in GitHub issue 2023.
2019-12-19 09:22:33 +00:00
Nicholas Marriott
54efe33799 Add back utempter code, reported by Peter Schellenbach. 2019-12-18 15:58:06 +00:00
Thomas Adam
d0cd68d5e4 Merge branch 'obsd-master' 2019-12-18 08:01:23 +00:00
nicm
ef54a08080 Do not rely on errno after glob(3) fails. 2019-12-18 07:48:56 +00:00
Thomas Adam
4223293ed8 Merge branch 'obsd-master' 2019-12-17 12:01:24 +00:00
nicm
f8cb759bdb Use the message that has already been built rather than the va_list. 2019-12-17 11:43:23 +00:00
Nicholas Marriott
3879509426 Define FNM_CASEFOLD to 0 for AIX, from Eric N Vander Weele. 2019-12-16 21:34:36 +00:00
Nicholas Marriott
479d411dda Remove imsg.h. 2019-12-16 20:01:26 +00:00
Thomas Adam
52b6ca5706 Merge branch 'obsd-master' 2019-12-16 18:01:31 +00:00
nicm
1bdd4828bd If /dev/fd/X is a symlink and realpath() expands symlinks, /dev/fd/X
ends up pointing to the wrong place before it is passed to the client.
The path is only used internally so there is no real need for
realpath(), remove it and move the get_path function to file.c where all
the callers are.
2019-12-16 16:39:03 +00:00
nicm
b4520aaf2c Need to include message size in the maximum buffer calculation. 2019-12-16 16:09:28 +00:00
nicm
eaa58d28dc Instead of using large buffers in imsgs, add the data or path onto the end. 2019-12-16 15:48:50 +00:00
Nicholas Marriott
e6b02dec19 Add to CHANGES. 2019-12-13 11:31:53 +00:00
Thomas Adam
00723f1f5c Merge branch 'obsd-master' 2019-12-13 10:01:24 +00:00
nicm
21f9b39f06 Show UTF-8 in choose-buffer mode. From KOIE Hidetaka. 2019-12-13 09:15:13 +00:00
Thomas Adam
e24e9867ec Merge branch 'obsd-master' 2019-12-13 08:01:24 +00:00
nicm
6ce943f4d9 Need to check in the error callback also. 2019-12-13 07:00:22 +00:00
nicm
828001ecc5 Do not spin waiting for exit, instead check in the write callback. 2019-12-13 06:55:12 +00:00
Thomas Adam
58908b045b Merge branch 'obsd-master' 2019-12-12 15:38:33 +00:00
nicm
dcf41ec927 Do not crash in tree modes if the pane is only 1 in width, reported by
KOIE Hidetaka in GitHub issue 2015.
2019-12-12 15:03:13 +00:00
nicm
2b2b193791 Add function to the right file. 2019-12-12 15:01:54 +00:00
Thomas Adam
7922f4ee7b Merge branch 'obsd-master' 2019-12-12 14:33:47 +00:00
nicm
5134666702 Change source-file to use new file code which allows it to read from
stdin.
2019-12-12 12:49:36 +00:00
nicm
268f2b047a Do not check if client is dead if it is NULL. 2019-12-12 11:51:32 +00:00
nicm
c284ebe0ad Rewrite the code for reading and writing files. Now, if the client is
not attached, the server process asks it to open the file, similar to
how works for stdin, stdout, stderr. This makes special files like
/dev/fd/X work (used by some shells). stdin, stdout and stderr and
control mode are now just special cases of the same mechanism. This will
also make it easier to use for other commands that read files such as
source-file.
2019-12-12 11:39:56 +00:00
Thomas Adam
0d99519c3d Merge branch 'obsd-master' 2019-12-11 20:01:25 +00:00
nicm
64fb7e472a Tweak previous to check the wrapped flag and stop if not set. 2019-12-11 18:30:29 +00:00
nicm
ab630f72ed Allow search across wrapped lines and fix some inconsistencies in how th
position is represented, GitHub issue 2014 from Anindya Mukherjee.
2019-12-11 18:23:34 +00:00
Thomas Adam
96abf400a5 Merge branch 'obsd-master' 2019-12-11 14:01:25 +00:00
nicm
f733d3f3eb Do not set cursor colour to default unless it has been changed, GitHub
issue 2013.
2019-12-11 12:13:37 +00:00
Nicholas Marriott
6aeb679066 Add to CHANGES. 2019-12-10 19:02:27 +00:00
Nicholas Marriott
15d7e564dd Add ~/.config/tmux/tmux.conf to the default search path for configuration files
(in Makefile.am, so portable tmux only).
2019-12-10 16:31:01 +00:00
Thomas Adam
5b2048fbb9 Merge branch 'obsd-master' 2019-12-10 16:01:25 +00:00
nicm
55eb3e4773 Make TMUX_CONF a list of files and expand leading $FOO or ~. 2019-12-10 14:22:15 +00:00
Nicholas Marriott
92ecd611f6 Check each _PATH_* define individually (Solaris has paths.h but not all of the
defines). From Eric N Vander Weele.
2019-12-03 18:53:23 +00:00
Thomas Adam
875139f5fa Merge branch 'obsd-master' 2019-12-03 12:01:26 +00:00
nicm
7826d40ff9 Style nits in function arguments. 2019-12-03 10:47:22 +00:00
Thomas Adam
edf96b06a5 Merge branch 'obsd-master' 2019-12-02 20:01:24 +00:00
nicm
ec1b8e5f05 Remove client menu, I don't think it adds anything. 2019-12-02 19:25:52 +00:00
Nicholas Marriott
2173365f4f Fix keys in CHANGES. 2019-12-01 21:15:21 +00:00
Nicholas Marriott
7836779e21 Merge branch '3.0a-rc' 2019-12-01 09:00:46 +00:00
Thomas Adam
866b053f25 Merge branch 'obsd-master' 2019-11-29 18:01:24 +00:00
nicm
67d995d100 If a window appears in only one attached session, there is no point in
worrying about which is the latest client (there is only one).
2019-11-29 16:04:07 +00:00
Thomas Adam
34084fe666 Merge branch 'obsd-master' 2019-11-28 22:01:24 +00:00
nicm
c5d74b1deb Do not crash when a format doesn't exist, reported by Thomas Sattler. 2019-11-28 21:18:38 +00:00
Nicholas Marriott
3bb11ec484 Revert "add missing definition"
This reverts commit 743939ec84.
2019-11-28 14:36:32 +00:00
Thomas Adam
743939ec84 add missing definition 2019-11-28 12:31:43 +00:00
Nicholas Marriott
e00730d149 Fix bad merge. 2019-11-28 12:30:43 +00:00
Thomas Adam
5f5f029e3b Merge branch 'obsd-master' 2019-11-28 12:18:41 +00:00
nicm
fa409194d3 Missing after-kill-pane option. 2019-11-28 10:55:45 +00:00
nicm
08e2828592 Parse out DA features. 2019-11-28 10:17:22 +00:00
nicm
bc5881c4d2 Long lines and spacing fixes. 2019-11-28 09:56:25 +00:00
nicm
c416fe0da4 Add xrecallocarray. 2019-11-28 09:51:58 +00:00
nicm
9ea05b2fb3 Bump the escape sequence timeout to five seconds to allow for longer
legitimate sequences.
2019-11-28 09:50:09 +00:00
nicm
2349b1dbef Make a best effort to set xpixel and ypixel for each pane and add
formats for them.
2019-11-28 09:45:15 +00:00
nicm
067604bf8c Store xpixel/ypixel from TIOCGWINSZ and add formats. 2019-11-28 09:05:34 +00:00
nicm
7fb8eec8f1 status-left and status-right need push-default also, reported by Eric
Pruitt in GitHub issue 1989.
2019-11-28 08:38:04 +00:00
Thomas Adam
c13838436e Merge branch 'obsd-master' 2019-11-27 22:01:24 +00:00
nicm
eb4d60b1ce REG_STARTEND is not portable, but it turns out we don't actually need
it. From Evan Green, GitHub issue 1982.
2019-11-27 20:54:30 +00:00
Thomas Adam
d0655f321f Merge branch 'obsd-master' 2019-11-26 16:01:25 +00:00
nicm
fef8ee23c0 Add default # and * binding with vi(1) keys. 2019-11-26 15:35:56 +00:00
Nicholas Marriott
c16faa4fed Fixes to CHANGES. 2019-11-26 14:12:05 +00:00
Thomas Adam
f378a0b24d Merge branch 'obsd-master' 2019-11-26 00:01:25 +00:00
nicm
58f870ef6e Don't use motion flag uninitialized. 2019-11-25 22:38:36 +00:00
Thomas Adam
eaf526b1ea Merge branch 'obsd-master' 2019-11-25 22:01:56 +00:00
nicm
87a11a9214 Fix a warning in previous. 2019-11-25 20:43:32 +00:00
nicm
c2fde58701 Do not clear search marks on cursor movement with vi(1) keys, from Eric
Pruitt in GitHub issue 1985.
2019-11-25 20:42:18 +00:00
Thomas Adam
daa93b3fdc Merge branch 'obsd-master' 2019-11-25 16:01:27 +00:00
nicm
1ebd8c1234 Add p format modifier for padding to width. 2019-11-25 15:04:15 +00:00
nicm
5d0504ee11 Allow multiple substitutions in a single format. 2019-11-25 15:02:48 +00:00
Thomas Adam
81d4f95c2f Merge branch 'obsd-master' 2019-11-24 20:01:24 +00:00
nicm
20c1f1aec6 Only substitute patterns starting with ^ once. 2019-11-24 18:37:23 +00:00
Thomas Adam
82d0d85675 Merge branch 'obsd-master' 2019-11-20 12:01:23 +00:00
nicm
3c312a9150 Do not check the client readonly flag when there is no client, GitHub issue 1980. 2019-11-20 11:42:51 +00:00
Nicholas Marriott
0ed96c4609 Update CHANGES. 2019-11-18 12:56:24 +00:00
Thomas Adam
0d3f306c8e Merge branch 'obsd-master' 2019-11-18 10:01:25 +00:00
nicm
350a434939 Add -f for full size to join-pane (like split-window), from Theo Buehler. 2019-11-18 09:43:31 +00:00
nicm
4bc445f080 Keep modifiers on backspace when translating it. 2019-11-18 09:42:09 +00:00
Thomas Adam
dbdff241b2 Merge branch 'obsd-master' 2019-11-15 12:01:27 +00:00
nicm
5dfe9db788 Do not add path if it is NULL, duh. 2019-11-15 11:21:32 +00:00
nicm
f3dc38dcae Handle OSC 7 (a VTE extension) and put the result in a new format (pane_path). 2019-11-15 11:16:53 +00:00
Thomas Adam
48cbbb8757 Merge branch 'obsd-master' 2019-11-14 18:01:24 +00:00
nicm
1f966c495c Change window-size default from smallest to latest. 2019-11-14 16:23:23 +00:00
Thomas Adam
b67fd8f472 Merge branch 'obsd-master' 2019-11-14 16:01:25 +00:00
nicm
16b7719418 Fix parsing of DA with only one argument in the response and add 65 for VT520. 2019-11-14 15:37:19 +00:00
Thomas Adam
518a687886 Merge branch 'obsd-master' 2019-11-14 10:01:29 +00:00
nicm
2dbf062a89 Change new-session -A without a session name (that is, no -s option
also) to attach to the best existing session like attach-session rather
than creating a new one.
2019-11-14 08:00:30 +00:00
nicm
eb399e64d5 CUB and CUF are also limited by the margins so use CUP instead when
margins are enabled (we already do this for linefeed).
2019-11-14 07:56:32 +00:00
nicm
08b07b1a08 Add an option to set the key sent by backspace for those whose system
uses ^H rather than ^?. GitHub issue 1969.
2019-11-14 07:55:01 +00:00
Thomas Adam
eb215d3d3f Merge branch 'obsd-master' 2019-11-07 08:01:26 +00:00
nicm
c225262e13 Add -F flag to send-keys to expand formats in search-backward and
forward copy mode commands, this makes it easier to use the cursor_word
and cursor_line formats. From Anindya Mukherjee in GitHub issue 1964.
2019-11-07 07:11:25 +00:00
Thomas Adam
4408df1e8a Merge branch 'obsd-master' 2019-11-01 22:01:24 +00:00
nicm
bad95db878 Limit lazy resize to panes in attached sessions only - those in
unattached are likely to have been resized by something like
split-window where the user probably wants the resize to happen
immediately. GitHub issue 1963.
2019-11-01 20:26:21 +00:00
Thomas Adam
4fc8741794 Merge branch 'obsd-master' 2019-11-01 10:01:25 +00:00
nicm
d9c95c900c Handle the various different forms of rgb colour strings. 2019-11-01 09:09:53 +00:00
Nicholas Marriott
a1006db91b Create FUNDING.yml 2019-10-29 16:59:15 +00:00
Thomas Adam
57948a0dfc Merge branch 'obsd-master' 2019-10-28 10:01:25 +00:00
nicm
ccdebead79 Start with empty rather than NULL window name to avoid NULL printf if
window_name is evaluated early. Reported by Mikolaj Kucharski.
2019-10-28 09:07:59 +00:00
Thomas Adam
4e5f80dc62 Merge branch 'obsd-master' 2019-10-23 16:01:25 +01:00
nicm
6700018ce5 Document Any key, from Jason Felice. 2019-10-23 14:10:13 +00:00
Thomas Adam
b5de0a20d8 Merge branch 'obsd-master' 2019-10-23 10:01:27 +01:00
nicm
f7fb5df543 Use the existing code in format.c to add foramts for word and line at
cursor position in copy mode, from Anindya Mukherjee.
2019-10-23 07:42:05 +00:00
Thomas Adam
004ee66227 Merge branch 'obsd-master' 2019-10-19 22:01:25 +01:00
nicm
56e5067c46 Add formats for cursor and selection position in copy mode, from Jason Felice. 2019-10-19 19:20:14 +00:00
Thomas Adam
1b96902d73 Merge branch 'obsd-master' 2019-10-19 16:01:24 +01:00
nicm
2cb268d51b Do not crash trying to fix layout size if only one cell, from Azat Khuzhin. 2019-10-19 12:40:42 +00:00
Thomas Adam
fb7ce5b5d5 Merge branch 'obsd-master' 2019-10-15 10:01:28 +01:00
nicm
9fd62efcf0 Rewrite options_array_set to be clearer and remove a spurious warning
with newer GCC. From Ben Boeckel.
2019-10-15 08:30:36 +00:00
nicm
0c5e9c6efa Add support for percentage sizes for resize-pane ("-x 10%"). Also change
split-window and join-pane -l to accept similar percentages and
deprecate -p. From Anindya Mukherjee.
2019-10-15 08:25:37 +00:00
Thomas Adam
eb57cbcc29 Merge branch 'obsd-master' 2019-10-14 12:01:26 +01:00
nicm
b598bbcc2e Do not crash with pane_current_command if the pane is newly created and
has no shell set, from Thomas Adam.
2019-10-14 09:24:06 +00:00
nicm
f18cd5b19c Turn automatic-rename back on if the rename escape sequence is used with
an empty name, GitHub issue 1921.
2019-10-14 09:19:40 +00:00
nicm
bbe8ebf9c2 Some old tmux versions can sometimes generate layout strings which have
the incorrect size for the top cell. Previously tmux didn't care but now
that panes can be partly hidden, the size matters and is checked more
strictly. So add some code to fix up the most common problem and a
sanity check to reject layouts with any other size problems. Reported by
Aleksandrs Ledovskis in GitHub issue 1930.
2019-10-14 09:16:48 +00:00
nicm
68d59a16ce Memory leaks, from Igor Wong in GitHub issue 1934. 2019-10-14 08:38:07 +00:00
Thomas Adam
7323ffeef2 Merge branch 'obsd-master' 2019-10-07 10:01:24 +01:00
nicm
4e2cc0ae2a Fix respawn-pane/window if default-command is set, reported by Janos Barbero. 2019-10-07 07:14:07 +00:00
Thomas Adam
341b330a04 Merge branch 'obsd-master' 2019-10-03 14:01:23 +01:00
Thomas Adam
eeedb43ae8 Merge branch 'obsd-master' 2019-10-03 12:01:25 +01:00
nicm
02253d1e5c Use a malloc'd buffer for lsk since commands can be very long, from Gregory Pakosz. 2019-10-03 10:39:08 +00:00
nicm
f4c7141f5d Do not lazily use BUFSIZ for "I don't care what size" when building
strings because it is only guaranteed to be 256 bytes and even the
default 1024 is not always enough. Reported by Gregory Pakosz.
2019-10-03 10:24:05 +00:00
Thomas Adam
8cf21feefd Merge branch 'obsd-master' 2019-09-25 22:01:24 +01:00
nicm
bbd1032a2a Style and line length nits. 2019-09-25 19:05:59 +00:00
Thomas Adam
d89510e1aa Merge branch 'obsd-master' 2019-09-25 00:01:25 +01:00
nicm
e3359f8349 Some minor performance improvements - most notably, don't search the
input state table if the next character matches the same state as before.
2019-09-24 20:44:58 +00:00
Thomas Adam
2534aa4d2d Merge branch 'obsd-master' 2019-09-24 18:01:24 +01:00
nicm
e8adcae0f2 Couple of bits of minor cleanup. 2019-09-24 15:52:14 +00:00
nicm
48c684cbc2 Mouse formats don't work in copy mode so don't try to use them. 2019-09-24 14:50:08 +00:00
Thomas Adam
cb1a626692 Merge branch 'obsd-master' 2019-09-24 12:01:23 +01:00
nicm
232050830b Make select-pane -P set window-active-style also to match previous
behaviour, reported by Thomas Sattler.
2019-09-24 09:58:58 +00:00
Thomas Adam
dd254b90d7 Merge branch 'obsd-master' 2019-09-23 18:01:24 +01:00
nicm
77deef733b Use the correct size for new windows when window-size is latest,
reported by Vamsi Krishna Avula in GitHub issue 1917.
2019-09-23 15:41:11 +00:00
Thomas Adam
24ab1bc714 Merge branch 'obsd-master' 2019-09-19 12:01:30 +01:00
nicm
647887b794 Add a "latest" window-size option which tries to size windows based on
the most recently used client. From Tommie Gannert in GitHub issue 1869
based on earlier changes from me.
2019-09-19 09:02:30 +00:00
nicm
d018477359 Do not use bright when emulating 256 colours on an 8 colour terminal
because it is also bold on some terminals. GitHub issue 1914.
2019-09-19 08:56:37 +00:00
Thomas Adam
827913102e Merge branch 'obsd-master' 2019-09-18 18:01:23 +01:00
nicm
1ee40307b5 Up and Down are already used, use < and > instead. 2019-09-18 15:09:05 +00:00
Thomas Adam
d30b612809 Merge branch 'obsd-master' 2019-09-18 14:01:24 +01:00
nicm
697f938355 Do not set uninitialized signal mask when creating an empty pane. 2019-09-18 11:37:58 +00:00
Thomas Adam
5ae2d421fb Merge branch 'obsd-master' 2019-09-16 16:01:23 +01:00
nicm
83be3afc54 Change menu key bindings to Up and Down and also close it on any mouse
press if opened by key.
2019-09-16 13:27:14 +00:00
Nicholas Marriott
7a1abd66e7 These are in 3.0 now. 2019-09-16 09:01:27 +01:00
Nicholas Marriott
d70d24d360 Merge branch '3.0-rc' 2019-09-16 09:01:09 +01:00
Nicholas Marriott
74f150670a In the right place. 2019-09-16 08:57:56 +01:00
Nicholas Marriott
3c355ec3b0 Add to CHANGES. 2019-09-16 08:57:22 +01:00
Thomas Adam
d346d692eb Merge branch 'obsd-master' 2019-09-16 00:01:25 +01:00
nicm
63e07b245f Add push-default and pop-default in styles to change the default colours
and attributes and use them to restore the previous behaviour of
window-status-style being the default for window-status-format in the
status line. From John Drouhard in GitHub issue 1912.
2019-09-15 21:42:57 +00:00
Thomas Adam
a5e36a4bd6 Merge branch 'obsd-master' 2019-09-13 04:01:25 +01:00
Thomas Adam
658ecb0777 Merge branch 'obsd-master' 2019-09-11 16:48:58 +01:00
nicm
a23ce1b45f Add window_marked_flag, GitHub issue 1887. 2019-09-11 06:43:17 +00:00
nicm
0feae4d8ae Make client exit if pane where input is going is closed. 2019-09-10 19:35:34 +00:00
nicm
4b7e97ba53 Set up format tree for %if, GitHub issue 1896. 2019-09-10 07:50:33 +00:00
nicm
b6b7486423 Clarify server options slightly. 2019-09-09 11:47:25 +00:00
Thomas Adam
2e90841f2e Merge branch 'obsd-master' 2019-09-09 12:02:32 +01:00
nicm
b31515fec3 Add cursor-down-and-cancel, from Mark Kelly. 2019-09-09 08:01:21 +00:00
Nicholas Marriott
9a476c5f29 Fix "make ctags", GitHub issue 1888. 2019-09-08 21:42:26 +01:00
Nicholas Marriott
5423bf6db8 Missing headers from compat/asprintf.c, from cyyever at outlook dot com. 2019-09-08 21:29:22 +01:00
Thomas Adam
648471ecee Merge branch 'obsd-master' 2019-08-29 18:02:29 +01:00
nicm
7ce8135138 It is not longer necessary to double-escape ; in %%%, problem reported
by Theo Buehler.
2019-08-29 07:13:48 +00:00
Thomas Adam
4fa1f961f3 Merge branch 'obsd-master' 2019-08-28 20:02:24 +01:00
nicm
df0334d3b3 The resize event was never deciding to actually resize the pane if there
was output in the pane faster than the timer would fire, so change how
it works to only defer the timer again if the pane was actually resized
within the last timer period. Reported by James Tai in GitHub issue
1880.
2019-08-28 07:34:32 +00:00
Thomas Adam
6640790bdc Merge branch 'obsd-master' 2019-08-27 10:02:31 +01:00
nicm
39c55d5b6f Fix swap-window -d to work as intended, GitHub issue 1879 from Sam Stuewe. 2019-08-26 16:35:41 +00:00
Thomas Adam
2db9a18362 Merge branch 'obsd-master' 2019-08-16 17:02:29 +01:00
nicm
37583f0a69 Add a flag to reverse sort in the various choose modes, from Benjamin
Poirier in GitHub issue 1875.
2019-08-16 11:49:12 +00:00
Thomas Adam
06ad86053c Merge branch 'obsd-master' 2019-08-16 11:02:31 +01:00
nicm
5644d37876 grid_view_delete_cells does need to clear, GitHub issue 1871. 2019-08-16 08:52:25 +00:00
Thomas Adam
ed0f2831b4 Merge branch 'obsd-master' 2019-08-15 11:02:48 +01:00
nicm
21fae50089 Default to previous search string for search-forward and
search-backward, from Leah Neukirchen.
2019-08-14 10:02:24 +00:00
nicm
0f243f0388 Add -Z flag to rotate-window, select-pane, swap-pane, switch-client to
preserve zoomed state. GitHub issue 1839.
2019-08-14 09:58:31 +00:00
Thomas Adam
9b3fefc435 Merge branch 'obsd-master' 2019-08-06 07:02:33 +01:00
nicm
45f4ff5485 Add support for the SD (scroll down) escape sequence, GitHub issue 1861. 2019-08-05 06:42:02 +00:00
Nicholas Marriott
eb8eeab05e Merge branch '3.0-rc' 2019-08-01 18:52:44 +01:00
Thomas Adam
a2d7f380b2 Merge branch 'obsd-master' 2019-08-01 17:02:26 +01:00
nicm
c4744620af Correctly wrap search in copy mode even if at the very top left, GitHub
issue 1845.
2019-08-01 14:31:39 +00:00
nicm
3d660b0023 Select the correct word for select-word when already at the start of a
word, GitHub issue 1820.
2019-08-01 14:30:31 +00:00
nicm
49bf7dc77e xterm 348 now disables margins when resized, so send DECLRMM again. 2019-08-01 11:45:34 +00:00
Thomas Adam
11315c589a Merge branch 'obsd-master' 2019-08-01 11:02:26 +01:00
nicm
2db5f9c215 Add -N to capture-pane to preserve trailing spaces, from Leon Winter. 2019-08-01 08:42:34 +00:00
Thomas Adam
cea87758e7 Merge branch 'obsd-master' 2019-08-01 09:02:26 +01:00
nicm
58bbce09e2 Remove check for same size - size has already been changed so this
breaks reflow.
2019-08-01 07:08:13 +00:00
Thomas Adam
900e583c7a Merge branch 'obsd-master' 2019-07-30 13:02:26 +01:00
nicm
e698ee01dd Reorder some text in the windows & panes section and add some better
explanation of modes.
2019-07-30 10:10:02 +00:00
Nicholas Marriott
2dd9a4fb9c Bump version again. 2019-07-29 11:03:15 +01:00
77 changed files with 4423 additions and 1868 deletions

1
.github/FUNDING.yml vendored Normal file
View File

@@ -0,0 +1 @@
liberapay: tmux

134
CHANGES
View File

@@ -1,3 +1,135 @@
CHANGES FROM 3.0 to 3.1
* Add support for adding a note to a key binding (with bind-key -N) and use
this to add descriptions to the default key bindings. A new -N flag to
list-keys shows key bindings with notes. Change the default ? binding to use
this to show a readable summary of keys. Also extend command-prompt to return
the name of the key pressed and add a default binding (/) to show the note
for the next key pressed.
* Add support for the iTerm2 DSR 1337 sequence to get the terminal version.
* Treat plausible but invalid keys (like C-BSpace) as literal like any other
unrecognised string passed to send-keys
* Detect iTerm2 and enable use of DECSLRM (much faster with horizontally split
windows).
* Add -Z to default switch-client command in tree mode.
* Add ~ to quoted characters for %%%.
* Document client exit messages in the manual page.
* Do not let read-only clients limit the size, unless all clients are
read-only.
* Add a number of new formats to inspect what sessions and clients a window is
present or active in.
* Change file reading and writing to go through the client if necessary. This
fixes commands like "tmux loadb /dev/fd/X". Also modify source-file to
support "-" for standard input, like load-buffer and save-buffer.
* Add ~/.config/tmux/tmux.conf to the default search path for configuration
files.
* Bump the escape sequence timeout to five seconds to allow for longer
legitimate sequences.
* Make a best effort to set xpixel and ypixel for each pane and add formats for
them.
* Add push-default to status-left and status-right in status-format[0].
* Add -F flag to send-keys to expand formats in search-backward and forward
copy mode commands and copy_cursor_word and copy_cursor_line formats for word
and line at cursor in copy mode. Use for default # and * binding with vi(1)
keys.
* Do not clear search marks on cursor movement with vi(1) keys.
* Add p format modifier for padding to width and allow multiple substitutions
in a single format.
* Add -f for full size to join-pane (like split-window).
* Handle OSC 7 (a VTE extension) and put the result in a new format (pane_path).
* Change new-session -A without a session name (that is, no -s option also) to
attach to the best existing session like attach-session rather than creating
a new one.
* Add an option to set the key sent by backspace for those whose system uses ^H
rather than ^?.
* Add formats for cursor and selection position in copy mode.
* Add support for percentage sizes for resize-pane ("-x 10%"). Also change
split-window and join-pane -l to accept similar percentages and no longer
document -p.
* Make select-pane -P set window-active-style also to match previous behaviour.
* Do not use bright when emulating 256 colours on an 8 colour terminal because
it is also bold on some terminals.
* Add a "latest" window-size option which tries to size windows based on the
most recently used client. This is now the default.
* Make select-pane -P set window-active-style also to match previous behaviour.
* Do not truncate list-keys output.
* Turn automatic-rename back on if the \033k rename escape sequence is used
with an empty name.
* Add support for percentage sizes for resize-pane ("-x 10%"). Also change
split-window and join-pane -l to accept similar percentages and deprecate the
-p flag.
* Add -F flag to send-keys to expand formats in search-backward and forward
copy mode commands, this makes it easier to use the cursor_word and
cursor_line formats.
* Add formats for word and line at cursor position in copy mode.
* Add formats for cursor and selection position in copy mode
* Support all the forms of RGB colour strings in OSC sequences rather than
requiring two digits.
* Limit lazy resize to panes in attached sessions only
* Add an option to set the key sent by backspace for those whose system uses ^H
rather than ^?.
* Change new-session -A without a session name (that is, no -s option also) to
attach to the best existing session like attach-session rather than a new
one.
* Change window-size default from smallest to latest.
* Add simple support for OSC 7 (result is available in the pane_path format).
* Add push-default and pop-default for styles which change the colours and
attributes used for #[default]. These are used in status-format to restore
the behaviour of window-status-style being the default for
window-status-format.
* Add window_marked_flag.
* Add cursor-down-and-cancel in copy mode.
* Default to previous search string for search-forward and search-backward.
* Add -Z flag to rotate-window, select-pane, swap-pane, switch-client to
preserve zoomed state.
* Add -N to capture-pane to preserve trailing spaces.
* Add reverse sorting in tree, client and buffer modes.
CHANGES FROM 3.0 to 3.0a CHANGES FROM 3.0 to 3.0a
* Do not require REG_STARTEND. * Do not require REG_STARTEND.
@@ -102,7 +234,7 @@ CHANGES FROM 2.9 to 3.0
* Add the ability to create simple menus. Introduces new command * Add the ability to create simple menus. Introduces new command
display-menu. Default menus are bound to MouseDown3 on the status line; display-menu. Default menus are bound to MouseDown3 on the status line;
MouseDown3 or M-MouseDown3 on panes; MouseDown3 in tree, client and MouseDown3 or M-MouseDown3 on panes; MouseDown3 in tree, client and
buffer modes; and C-b C-m and C-b M-m. buffer modes; and C-b < and >.
* Allow panes to be empty (no command). They can be created either by piping to * Allow panes to be empty (no command). They can be created either by piping to
split-window -I, or by passing an empty command ('') to split-window. Output split-window -I, or by passing an empty command ('') to split-window. Output

View File

@@ -6,11 +6,14 @@ CLEANFILES = tmux.1.mdoc tmux.1.man cmd-parse.c
# Distribution tarball options. # Distribution tarball options.
EXTRA_DIST = \ EXTRA_DIST = \
CHANGES README README.ja COPYING example_tmux.conf compat/*.[ch] \ CHANGES README README.ja COPYING example_tmux.conf \
osdep-*.c mdoc2man.awk tmux.1 osdep-*.c mdoc2man.awk tmux.1
dist_EXTRA_tmux_SOURCES = compat/*.[ch]
# Preprocessor flags. # Preprocessor flags.
AM_CPPFLAGS += @XOPEN_DEFINES@ -DTMUX_CONF="\"$(sysconfdir)/tmux.conf\"" AM_CPPFLAGS += @XOPEN_DEFINES@ \
-DTMUX_VERSION="\"@VERSION@\"" \
-DTMUX_CONF="\"$(sysconfdir)/tmux.conf:~/.tmux.conf:~/.config/tmux/tmux.conf\""
# Additional object files. # Additional object files.
LDADD = $(LIBOBJS) LDADD = $(LIBOBJS)
@@ -130,6 +133,7 @@ dist_tmux_SOURCES = \
control-notify.c \ control-notify.c \
control.c \ control.c \
environ.c \ environ.c \
file.c \
format.c \ format.c \
format-draw.c \ format-draw.c \
grid-view.c \ grid-view.c \

View File

@@ -59,8 +59,8 @@ attributes_fromstring(const char *str)
size_t end; size_t end;
u_int i; u_int i;
struct { struct {
const char* name; const char *name;
int attr; int attr;
} table[] = { } table[] = {
{ "bright", GRID_ATTR_BRIGHT }, { "bright", GRID_ATTR_BRIGHT },
{ "bold", GRID_ATTR_BRIGHT }, { "bold", GRID_ATTR_BRIGHT },

111
cfg.c
View File

@@ -66,12 +66,45 @@ set_cfg_file(const char *path)
cfg_file = xstrdup(path); cfg_file = xstrdup(path);
} }
static char *
expand_cfg_file(const char *path, const char *home)
{
char *expanded, *name;
const char *end;
struct environ_entry *value;
if (strncmp(path, "~/", 2) == 0) {
if (home == NULL)
return (NULL);
xasprintf(&expanded, "%s%s", home, path + 1);
return (expanded);
}
if (*path == '$') {
end = strchr(path, '/');
if (end == NULL)
name = xstrdup(path + 1);
else
name = xstrndup(path + 1, end - path - 1);
value = environ_find(global_environ, name);
free(name);
if (value == NULL)
return (NULL);
if (end == NULL)
end = "";
xasprintf(&expanded, "%s%s", value->value, end);
return (expanded);
}
return (xstrdup(path));
}
void void
start_cfg(void) start_cfg(void)
{ {
const char *home; const char *home = find_home();
int flags = 0;
struct client *c; struct client *c;
char *path, *copy, *next, *expanded;
/* /*
* Configuration files are loaded without a client, so commands are run * Configuration files are loaded without a client, so commands are run
@@ -89,15 +122,21 @@ start_cfg(void)
cmdq_append(c, cfg_item); cmdq_append(c, cfg_item);
} }
if (cfg_file == NULL) if (cfg_file == NULL) {
load_cfg(TMUX_CONF, c, NULL, CMD_PARSE_QUIET, NULL); path = copy = xstrdup(TMUX_CONF);
while ((next = strsep(&path, ":")) != NULL) {
if (cfg_file == NULL && (home = find_home()) != NULL) { expanded = expand_cfg_file(next, home);
xasprintf(&cfg_file, "%s/.tmux.conf", home); if (expanded == NULL) {
flags = CMD_PARSE_QUIET; log_debug("couldn't expand %s", next);
} continue;
if (cfg_file != NULL) }
load_cfg(cfg_file, c, NULL, flags, NULL); log_debug("expanded %s to %s", next, expanded);
load_cfg(expanded, c, NULL, CMD_PARSE_QUIET, NULL);
free(expanded);
}
free(copy);
} else
load_cfg(cfg_file, c, NULL, 0, NULL);
cmdq_append(NULL, cmdq_get_callback(cfg_done, NULL)); cmdq_append(NULL, cmdq_get_callback(cfg_done, NULL));
} }
@@ -145,9 +184,55 @@ load_cfg(const char *path, struct client *c, struct cmdq_item *item, int flags,
new_item0 = cmdq_get_command(pr->cmdlist, NULL, NULL, 0); new_item0 = cmdq_get_command(pr->cmdlist, NULL, NULL, 0);
if (item != NULL) if (item != NULL)
cmdq_insert_after(item, new_item0); new_item0 = cmdq_insert_after(item, new_item0);
else else
cmdq_append(NULL, new_item0); new_item0 = cmdq_append(NULL, new_item0);
cmd_list_free(pr->cmdlist);
if (new_item != NULL)
*new_item = new_item0;
return (0);
}
int
load_cfg_from_buffer(const void *buf, size_t len, const char *path,
struct client *c, struct cmdq_item *item, int flags,
struct cmdq_item **new_item)
{
struct cmd_parse_input pi;
struct cmd_parse_result *pr;
struct cmdq_item *new_item0;
if (new_item != NULL)
*new_item = NULL;
log_debug("loading %s", path);
memset(&pi, 0, sizeof pi);
pi.flags = flags;
pi.file = path;
pi.line = 1;
pi.item = item;
pi.c = c;
pr = cmd_parse_from_buffer(buf, len, &pi);
if (pr->status == CMD_PARSE_EMPTY)
return (0);
if (pr->status == CMD_PARSE_ERROR) {
cfg_add_cause("%s", pr->error);
free(pr->error);
return (-1);
}
if (flags & CMD_PARSE_PARSEONLY) {
cmd_list_free(pr->cmdlist);
return (0);
}
new_item0 = cmdq_get_command(pr->cmdlist, NULL, NULL, 0);
if (item != NULL)
new_item0 = cmdq_insert_after(item, new_item0);
else
new_item0 = cmdq_append(NULL, new_item0);
cmd_list_free(pr->cmdlist); cmd_list_free(pr->cmdlist);
if (new_item != NULL) if (new_item != NULL)

358
client.c
View File

@@ -35,7 +35,6 @@
static struct tmuxproc *client_proc; static struct tmuxproc *client_proc;
static struct tmuxpeer *client_peer; static struct tmuxpeer *client_peer;
static int client_flags; static int client_flags;
static struct event client_stdin;
static enum { static enum {
CLIENT_EXIT_NONE, CLIENT_EXIT_NONE,
CLIENT_EXIT_DETACHED, CLIENT_EXIT_DETACHED,
@@ -46,19 +45,19 @@ static enum {
CLIENT_EXIT_EXITED, CLIENT_EXIT_EXITED,
CLIENT_EXIT_SERVER_EXITED, CLIENT_EXIT_SERVER_EXITED,
} client_exitreason = CLIENT_EXIT_NONE; } client_exitreason = CLIENT_EXIT_NONE;
static int client_exitflag;
static int client_exitval; static int client_exitval;
static enum msgtype client_exittype; static enum msgtype client_exittype;
static const char *client_exitsession; static const char *client_exitsession;
static const char *client_execshell; static const char *client_execshell;
static const char *client_execcmd; static const char *client_execcmd;
static int client_attached; static int client_attached;
static struct client_files client_files = RB_INITIALIZER(&client_files);
static __dead void client_exec(const char *,const char *); static __dead void client_exec(const char *,const char *);
static int client_get_lock(char *); static int client_get_lock(char *);
static int client_connect(struct event_base *, const char *, int); static int client_connect(struct event_base *, const char *, int);
static void client_send_identify(const char *, const char *); static void client_send_identify(const char *, const char *);
static void client_stdin_callback(int, short, void *);
static void client_write(int, const char *, size_t);
static void client_signal(int); static void client_signal(int);
static void client_dispatch(struct imsg *, void *); static void client_dispatch(struct imsg *, void *);
static void client_dispatch_attached(struct imsg *); static void client_dispatch_attached(struct imsg *);
@@ -211,13 +210,34 @@ client_exit_message(void)
return ("unknown reason"); return ("unknown reason");
} }
/* Exit if all streams flushed. */
static void
client_exit(void)
{
struct client_file *cf;
size_t left;
int waiting = 0;
RB_FOREACH (cf, client_files, &client_files) {
if (cf->event == NULL)
continue;
left = EVBUFFER_LENGTH(cf->event->output);
if (left != 0) {
waiting++;
log_debug("file %u %zu bytes left", cf->stream, left);
}
}
if (waiting == 0)
proc_exit(client_proc);
}
/* Client main loop. */ /* Client main loop. */
int int
client_main(struct event_base *base, int argc, char **argv, int flags) client_main(struct event_base *base, int argc, char **argv, int flags)
{ {
struct cmd_parse_result *pr; struct cmd_parse_result *pr;
struct cmd *cmd; struct cmd *cmd;
struct msg_command_data *data; struct msg_command *data;
int cmdflags, fd, i; int cmdflags, fd, i;
const char *ttynam, *cwd; const char *ttynam, *cwd;
pid_t ppid; pid_t ppid;
@@ -291,7 +311,9 @@ client_main(struct event_base *base, int argc, char **argv, int flags)
* *
* "sendfd" is dropped later in client_dispatch_wait(). * "sendfd" is dropped later in client_dispatch_wait().
*/ */
if (pledge("stdio unix sendfd proc exec tty", NULL) != 0) if (pledge(
"stdio rpath wpath cpath unix sendfd proc exec tty",
NULL) != 0)
fatal("pledge failed"); fatal("pledge failed");
/* Free stuff that is not used in the client. */ /* Free stuff that is not used in the client. */
@@ -302,10 +324,7 @@ client_main(struct event_base *base, int argc, char **argv, int flags)
options_free(global_w_options); options_free(global_w_options);
environ_free(global_environ); environ_free(global_environ);
/* Create stdin handler. */ /* Set up control mode. */
setblocking(STDIN_FILENO, 0);
event_set(&client_stdin, STDIN_FILENO, EV_READ|EV_PERSIST,
client_stdin_callback, NULL);
if (client_flags & CLIENT_CONTROLCONTROL) { if (client_flags & CLIENT_CONTROLCONTROL) {
if (tcgetattr(STDIN_FILENO, &saved_tio) != 0) { if (tcgetattr(STDIN_FILENO, &saved_tio) != 0) {
fprintf(stderr, "tcgetattr failed: %s\n", fprintf(stderr, "tcgetattr failed: %s\n",
@@ -428,39 +447,254 @@ client_send_identify(const char *ttynam, const char *cwd)
proc_send(client_peer, MSG_IDENTIFY_DONE, -1, NULL, 0); proc_send(client_peer, MSG_IDENTIFY_DONE, -1, NULL, 0);
} }
/* Callback for client stdin read events. */ /* File write error callback. */
static void static void
client_stdin_callback(__unused int fd, __unused short events, client_write_error_callback(__unused struct bufferevent *bev,
__unused void *arg) __unused short what, void *arg)
{ {
struct msg_stdin_data data; struct client_file *cf = arg;
data.size = read(STDIN_FILENO, data.data, sizeof data.data); log_debug("write error file %d", cf->stream);
if (data.size == -1 && (errno == EINTR || errno == EAGAIN))
return;
proc_send(client_peer, MSG_STDIN, -1, &data, sizeof data); bufferevent_free(cf->event);
if (data.size <= 0) cf->event = NULL;
event_del(&client_stdin);
close(cf->fd);
cf->fd = -1;
if (client_exitflag)
client_exit();
} }
/* Force write to file descriptor. */ /* File write callback. */
static void static void
client_write(int fd, const char *data, size_t size) client_write_callback(__unused struct bufferevent *bev, void *arg)
{ {
ssize_t used; struct client_file *cf = arg;
log_debug("%s: %.*s", __func__, (int)size, data); if (cf->closed && EVBUFFER_LENGTH(cf->event->output) == 0) {
while (size != 0) { bufferevent_free(cf->event);
used = write(fd, data, size); close(cf->fd);
if (used == -1) { RB_REMOVE(client_files, &client_files, cf);
if (errno == EINTR || errno == EAGAIN) file_free(cf);
continue;
break;
}
data += used;
size -= used;
} }
if (client_exitflag)
client_exit();
}
/* Open write file. */
static void
client_write_open(void *data, size_t datalen)
{
struct msg_write_open *msg = data;
const char *path;
struct msg_write_ready reply;
struct client_file find, *cf;
const int flags = O_NONBLOCK|O_WRONLY|O_CREAT;
int error = 0;
if (datalen < sizeof *msg)
fatalx("bad MSG_WRITE_OPEN size");
if (datalen == sizeof *msg)
path = "-";
else
path = (const char *)(msg + 1);
log_debug("open write file %d %s", msg->stream, path);
find.stream = msg->stream;
if ((cf = RB_FIND(client_files, &client_files, &find)) == NULL) {
cf = file_create(NULL, msg->stream, NULL, NULL);
RB_INSERT(client_files, &client_files, cf);
} else {
error = EBADF;
goto reply;
}
if (cf->closed) {
error = EBADF;
goto reply;
}
cf->fd = -1;
if (msg->fd == -1)
cf->fd = open(path, msg->flags|flags, 0644);
else {
if (msg->fd != STDOUT_FILENO && msg->fd != STDERR_FILENO)
errno = EBADF;
else {
cf->fd = dup(msg->fd);
if (client_flags & CLIENT_CONTROL)
close(msg->fd); /* can only be used once */
}
}
if (cf->fd == -1) {
error = errno;
goto reply;
}
cf->event = bufferevent_new(cf->fd, NULL, client_write_callback,
client_write_error_callback, cf);
bufferevent_enable(cf->event, EV_WRITE);
goto reply;
reply:
reply.stream = msg->stream;
reply.error = error;
proc_send(client_peer, MSG_WRITE_READY, -1, &reply, sizeof reply);
}
/* Write to client file. */
static void
client_write_data(void *data, size_t datalen)
{
struct msg_write_data *msg = data;
struct client_file find, *cf;
size_t size = datalen - sizeof *msg;
if (datalen < sizeof *msg)
fatalx("bad MSG_WRITE size");
find.stream = msg->stream;
if ((cf = RB_FIND(client_files, &client_files, &find)) == NULL)
fatalx("unknown stream number");
log_debug("write %zu to file %d", size, cf->stream);
if (cf->event != NULL)
bufferevent_write(cf->event, msg + 1, size);
}
/* Close client file. */
static void
client_write_close(void *data, size_t datalen)
{
struct msg_write_close *msg = data;
struct client_file find, *cf;
if (datalen != sizeof *msg)
fatalx("bad MSG_WRITE_CLOSE size");
find.stream = msg->stream;
if ((cf = RB_FIND(client_files, &client_files, &find)) == NULL)
fatalx("unknown stream number");
log_debug("close file %d", cf->stream);
if (cf->event == NULL || EVBUFFER_LENGTH(cf->event->output) == 0) {
if (cf->event != NULL)
bufferevent_free(cf->event);
if (cf->fd != -1)
close(cf->fd);
RB_REMOVE(client_files, &client_files, cf);
file_free(cf);
}
}
/* File read callback. */
static void
client_read_callback(__unused struct bufferevent *bev, void *arg)
{
struct client_file *cf = arg;
void *bdata;
size_t bsize;
struct msg_read_data *msg;
size_t msglen;
msg = xmalloc(sizeof *msg);
for (;;) {
bdata = EVBUFFER_DATA(cf->event->input);
bsize = EVBUFFER_LENGTH(cf->event->input);
if (bsize == 0)
break;
if (bsize > MAX_IMSGSIZE - IMSG_HEADER_SIZE - sizeof *msg)
bsize = MAX_IMSGSIZE - IMSG_HEADER_SIZE - sizeof *msg;
log_debug("read %zu from file %d", bsize, cf->stream);
msglen = (sizeof *msg) + bsize;
msg = xrealloc(msg, msglen);
msg->stream = cf->stream;
memcpy(msg + 1, bdata, bsize);
proc_send(client_peer, MSG_READ, -1, msg, msglen);
evbuffer_drain(cf->event->input, bsize);
}
free(msg);
}
/* File read error callback. */
static void
client_read_error_callback(__unused struct bufferevent *bev,
__unused short what, void *arg)
{
struct client_file *cf = arg;
struct msg_read_done msg;
log_debug("read error file %d", cf->stream);
msg.stream = cf->stream;
msg.error = 0;
proc_send(client_peer, MSG_READ_DONE, -1, &msg, sizeof msg);
bufferevent_free(cf->event);
close(cf->fd);
RB_REMOVE(client_files, &client_files, cf);
file_free(cf);
}
/* Open read file. */
static void
client_read_open(void *data, size_t datalen)
{
struct msg_read_open *msg = data;
const char *path;
struct msg_read_done reply;
struct client_file find, *cf;
const int flags = O_NONBLOCK|O_RDONLY;
int error = 0;
if (datalen < sizeof *msg)
fatalx("bad MSG_READ_OPEN size");
if (datalen == sizeof *msg)
path = "-";
else
path = (const char *)(msg + 1);
log_debug("open read file %d %s", msg->stream, path);
find.stream = msg->stream;
if ((cf = RB_FIND(client_files, &client_files, &find)) == NULL) {
cf = file_create(NULL, msg->stream, NULL, NULL);
RB_INSERT(client_files, &client_files, cf);
} else {
error = EBADF;
goto reply;
}
if (cf->closed) {
error = EBADF;
goto reply;
}
cf->fd = -1;
if (msg->fd == -1)
cf->fd = open(path, flags);
else {
if (msg->fd != STDIN_FILENO)
errno = EBADF;
else {
cf->fd = dup(msg->fd);
close(msg->fd); /* can only be used once */
}
}
if (cf->fd == -1) {
error = errno;
goto reply;
}
cf->event = bufferevent_new(cf->fd, client_read_callback, NULL,
client_read_error_callback, cf);
bufferevent_enable(cf->event, EV_READ);
return;
reply:
reply.stream = msg->stream;
reply.error = error;
proc_send(client_peer, MSG_READ_DONE, -1, &reply, sizeof reply);
} }
/* Run command in shell; used for -c. */ /* Run command in shell; used for -c. */
@@ -555,12 +789,10 @@ client_dispatch(struct imsg *imsg, __unused void *arg)
static void static void
client_dispatch_wait(struct imsg *imsg) client_dispatch_wait(struct imsg *imsg)
{ {
char *data; char *data;
ssize_t datalen; ssize_t datalen;
struct msg_stdout_data stdoutdata; int retval;
struct msg_stderr_data stderrdata; static int pledge_applied;
int retval;
static int pledge_applied;
/* /*
* "sendfd" is no longer required once all of the identify messages * "sendfd" is no longer required once all of the identify messages
@@ -569,10 +801,12 @@ client_dispatch_wait(struct imsg *imsg)
* get the first message from the server. * get the first message from the server.
*/ */
if (!pledge_applied) { if (!pledge_applied) {
if (pledge("stdio unix proc exec tty", NULL) != 0) if (pledge(
"stdio rpath wpath cpath unix proc exec tty",
NULL) != 0)
fatal("pledge failed"); fatal("pledge failed");
pledge_applied = 1; pledge_applied = 1;
}; }
data = imsg->data; data = imsg->data;
datalen = imsg->hdr.len - IMSG_HEADER_SIZE; datalen = imsg->hdr.len - IMSG_HEADER_SIZE;
@@ -586,38 +820,16 @@ client_dispatch_wait(struct imsg *imsg)
memcpy(&retval, data, sizeof retval); memcpy(&retval, data, sizeof retval);
client_exitval = retval; client_exitval = retval;
} }
proc_exit(client_proc); client_exitflag = 1;
client_exit();
break; break;
case MSG_READY: case MSG_READY:
if (datalen != 0) if (datalen != 0)
fatalx("bad MSG_READY size"); fatalx("bad MSG_READY size");
event_del(&client_stdin);
client_attached = 1; client_attached = 1;
proc_send(client_peer, MSG_RESIZE, -1, NULL, 0); proc_send(client_peer, MSG_RESIZE, -1, NULL, 0);
break; break;
case MSG_STDIN:
if (datalen != 0)
fatalx("bad MSG_STDIN size");
event_add(&client_stdin, NULL);
break;
case MSG_STDOUT:
if (datalen != sizeof stdoutdata)
fatalx("bad MSG_STDOUT size");
memcpy(&stdoutdata, data, sizeof stdoutdata);
client_write(STDOUT_FILENO, stdoutdata.data,
stdoutdata.size);
break;
case MSG_STDERR:
if (datalen != sizeof stderrdata)
fatalx("bad MSG_STDERR size");
memcpy(&stderrdata, data, sizeof stderrdata);
client_write(STDERR_FILENO, stderrdata.data,
stderrdata.size);
break;
case MSG_VERSION: case MSG_VERSION:
if (datalen != 0) if (datalen != 0)
fatalx("bad MSG_VERSION size"); fatalx("bad MSG_VERSION size");
@@ -641,6 +853,24 @@ client_dispatch_wait(struct imsg *imsg)
case MSG_EXITED: case MSG_EXITED:
proc_exit(client_proc); proc_exit(client_proc);
break; break;
case MSG_READ_OPEN:
client_read_open(data, datalen);
break;
case MSG_WRITE_OPEN:
client_write_open(data, datalen);
break;
case MSG_WRITE:
client_write_data(data, datalen);
break;
case MSG_WRITE_CLOSE:
client_write_close(data, datalen);
break;
case MSG_OLDSTDERR:
case MSG_OLDSTDIN:
case MSG_OLDSTDOUT:
fprintf(stderr, "server version is too old for client\n");
proc_exit(client_proc);
break;
} }
} }

View File

@@ -127,6 +127,7 @@ cmd_attach_session(struct cmdq_item *item, const char *tflag, int dflag,
gettimeofday(&s->last_attached_time, NULL); gettimeofday(&s->last_attached_time, NULL);
server_redraw_client(c); server_redraw_client(c);
s->curw->flags &= ~WINLINK_ALERTFLAGS; s->curw->flags &= ~WINLINK_ALERTFLAGS;
s->curw->window->latest = c;
} else { } else {
if (server_client_open(c, &cause) != 0) { if (server_client_open(c, &cause) != 0) {
cmdq_error(item, "open terminal failed: %s", cause); cmdq_error(item, "open terminal failed: %s", cause);
@@ -159,6 +160,7 @@ cmd_attach_session(struct cmdq_item *item, const char *tflag, int dflag,
gettimeofday(&s->last_attached_time, NULL); gettimeofday(&s->last_attached_time, NULL);
server_redraw_client(c); server_redraw_client(c);
s->curw->flags &= ~WINLINK_ALERTFLAGS; s->curw->flags &= ~WINLINK_ALERTFLAGS;
s->curw->window->latest = c;
if (~c->flags & CLIENT_CONTROL) if (~c->flags & CLIENT_CONTROL)
proc_send(c->peer, MSG_READY, -1, NULL, 0); proc_send(c->peer, MSG_READY, -1, NULL, 0);

View File

@@ -33,8 +33,8 @@ const struct cmd_entry cmd_bind_key_entry = {
.name = "bind-key", .name = "bind-key",
.alias = "bind", .alias = "bind",
.args = { "cnrT:", 2, -1 }, .args = { "nrN:T:", 2, -1 },
.usage = "[-cnr] [-T key-table] key " .usage = "[-nr] [-T key-table] [-N note] key "
"command [arguments]", "command [arguments]",
.flags = CMD_AFTERHOOK, .flags = CMD_AFTERHOOK,
@@ -46,10 +46,10 @@ cmd_bind_key_exec(struct cmd *self, struct cmdq_item *item)
{ {
struct args *args = self->args; struct args *args = self->args;
key_code key; key_code key;
const char *tablename; const char *tablename, *note;
struct cmd_parse_result *pr; struct cmd_parse_result *pr;
char **argv = args->argv; char **argv = args->argv;
int argc = args->argc; int argc = args->argc, repeat;
key = key_string_lookup_string(argv[0]); key = key_string_lookup_string(argv[0]);
if (key == KEYC_NONE || key == KEYC_UNKNOWN) { if (key == KEYC_NONE || key == KEYC_UNKNOWN) {
@@ -63,6 +63,7 @@ cmd_bind_key_exec(struct cmd *self, struct cmdq_item *item)
tablename = "root"; tablename = "root";
else else
tablename = "prefix"; tablename = "prefix";
repeat = args_has(args, 'r');
if (argc == 2) if (argc == 2)
pr = cmd_parse_from_string(argv[1], NULL); pr = cmd_parse_from_string(argv[1], NULL);
@@ -79,6 +80,7 @@ cmd_bind_key_exec(struct cmd *self, struct cmdq_item *item)
case CMD_PARSE_SUCCESS: case CMD_PARSE_SUCCESS:
break; break;
} }
key_bindings_add(tablename, key, args_has(args, 'r'), pr->cmdlist); note = args_get(args, 'N');
key_bindings_add(tablename, key, note, repeat, pr->cmdlist);
return (CMD_RETURN_NORMAL); return (CMD_RETURN_NORMAL);
} }

View File

@@ -76,11 +76,12 @@ cmd_break_pane_exec(struct cmd *self, struct cmdq_item *item)
window_lost_pane(w, wp); window_lost_pane(w, wp);
layout_close_pane(wp); layout_close_pane(wp);
w = wp->window = window_create(w->sx, w->sy); w = wp->window = window_create(w->sx, w->sy, w->xpixel, w->ypixel);
options_set_parent(wp->options, w->options); options_set_parent(wp->options, w->options);
wp->flags |= PANE_STYLECHANGED; wp->flags |= PANE_STYLECHANGED;
TAILQ_INSERT_HEAD(&w->panes, wp, entry); TAILQ_INSERT_HEAD(&w->panes, wp, entry);
w->active = wp; w->active = wp;
w->latest = c;
if (!args_has(args, 'n')) { if (!args_has(args, 'n')) {
name = default_window_name(w); name = default_window_name(w);

View File

@@ -39,8 +39,8 @@ const struct cmd_entry cmd_capture_pane_entry = {
.name = "capture-pane", .name = "capture-pane",
.alias = "capturep", .alias = "capturep",
.args = { "ab:CeE:JpPqS:t:", 0, 0 }, .args = { "ab:CeE:JNpPqS:t:", 0, 0 },
.usage = "[-aCeJpPq] " CMD_BUFFER_USAGE " [-E end-line] " .usage = "[-aCeJNpPq] " CMD_BUFFER_USAGE " [-E end-line] "
"[-S start-line] " CMD_TARGET_PANE_USAGE, "[-S start-line] " CMD_TARGET_PANE_USAGE,
.target = { 't', CMD_FIND_PANE, 0 }, .target = { 't', CMD_FIND_PANE, 0 },
@@ -110,7 +110,7 @@ cmd_capture_pane_history(struct args *args, struct cmdq_item *item,
struct grid *gd; struct grid *gd;
const struct grid_line *gl; const struct grid_line *gl;
struct grid_cell *gc = NULL; struct grid_cell *gc = NULL;
int n, with_codes, escape_c0, join_lines; int n, with_codes, escape_c0, join_lines, no_trim;
u_int i, sx, top, bottom, tmp; u_int i, sx, top, bottom, tmp;
char *cause, *buf, *line; char *cause, *buf, *line;
const char *Sflag, *Eflag; const char *Sflag, *Eflag;
@@ -170,11 +170,12 @@ cmd_capture_pane_history(struct args *args, struct cmdq_item *item,
with_codes = args_has(args, 'e'); with_codes = args_has(args, 'e');
escape_c0 = args_has(args, 'C'); escape_c0 = args_has(args, 'C');
join_lines = args_has(args, 'J'); join_lines = args_has(args, 'J');
no_trim = args_has(args, 'N');
buf = NULL; buf = NULL;
for (i = top; i <= bottom; i++) { for (i = top; i <= bottom; i++) {
line = grid_string_cells(gd, 0, i, sx, &gc, with_codes, line = grid_string_cells(gd, 0, i, sx, &gc, with_codes,
escape_c0, !join_lines); escape_c0, !join_lines && !no_trim);
linelen = strlen(line); linelen = strlen(line);
buf = cmd_capture_pane_append(buf, len, line, linelen); buf = cmd_capture_pane_append(buf, len, line, linelen);
@@ -192,7 +193,7 @@ static enum cmd_retval
cmd_capture_pane_exec(struct cmd *self, struct cmdq_item *item) cmd_capture_pane_exec(struct cmd *self, struct cmdq_item *item)
{ {
struct args *args = self->args; struct args *args = self->args;
struct client *c; struct client *c = item->client;
struct window_pane *wp = item->target.wp; struct window_pane *wp = item->target.wp;
char *buf, *cause; char *buf, *cause;
const char *bufname; const char *bufname;
@@ -213,18 +214,15 @@ cmd_capture_pane_exec(struct cmd *self, struct cmdq_item *item)
return (CMD_RETURN_ERROR); return (CMD_RETURN_ERROR);
if (args_has(args, 'p')) { if (args_has(args, 'p')) {
c = item->client; if (!file_can_print(c)) {
if (c == NULL || cmdq_error(item, "can't write output to client");
(c->session != NULL && !(c->flags & CLIENT_CONTROL))) {
cmdq_error(item, "can't write to stdout");
free(buf); free(buf);
return (CMD_RETURN_ERROR); return (CMD_RETURN_ERROR);
} }
evbuffer_add(c->stdout_data, buf, len); file_print_buffer(c, buf, len);
free(buf);
if (args_has(args, 'P') && len > 0) if (args_has(args, 'P') && len > 0)
evbuffer_add(c->stdout_data, "\n", 1); file_print(c, "\n");
server_client_push_stdout(c); free(buf);
} else { } else {
bufname = NULL; bufname = NULL;
if (args_has(args, 'b')) if (args_has(args, 'b'))

View File

@@ -30,8 +30,8 @@ const struct cmd_entry cmd_choose_tree_entry = {
.name = "choose-tree", .name = "choose-tree",
.alias = NULL, .alias = NULL,
.args = { "F:Gf:NO:st:wZ", 0, 1 }, .args = { "F:Gf:NO:rst:wZ", 0, 1 },
.usage = "[-GNsw] [-F format] [-f filter] [-O sort-order] " .usage = "[-GNrswZ] [-F format] [-f filter] [-O sort-order] "
CMD_TARGET_PANE_USAGE " [template]", CMD_TARGET_PANE_USAGE " [template]",
.target = { 't', CMD_FIND_PANE, 0 }, .target = { 't', CMD_FIND_PANE, 0 },
@@ -44,8 +44,8 @@ const struct cmd_entry cmd_choose_client_entry = {
.name = "choose-client", .name = "choose-client",
.alias = NULL, .alias = NULL,
.args = { "F:f:NO:t:Z", 0, 1 }, .args = { "F:f:NO:rt:Z", 0, 1 },
.usage = "[-N] [-F format] [-f filter] [-O sort-order] " .usage = "[-NrZ] [-F format] [-f filter] [-O sort-order] "
CMD_TARGET_PANE_USAGE " [template]", CMD_TARGET_PANE_USAGE " [template]",
.target = { 't', CMD_FIND_PANE, 0 }, .target = { 't', CMD_FIND_PANE, 0 },
@@ -58,8 +58,8 @@ const struct cmd_entry cmd_choose_buffer_entry = {
.name = "choose-buffer", .name = "choose-buffer",
.alias = NULL, .alias = NULL,
.args = { "F:f:NO:t:Z", 0, 1 }, .args = { "F:f:NO:rt:Z", 0, 1 },
.usage = "[-N] [-F format] [-f filter] [-O sort-order] " .usage = "[-NrZ] [-F format] [-f filter] [-O sort-order] "
CMD_TARGET_PANE_USAGE " [template]", CMD_TARGET_PANE_USAGE " [template]",
.target = { 't', CMD_FIND_PANE, 0 }, .target = { 't', CMD_FIND_PANE, 0 },

View File

@@ -40,8 +40,8 @@ const struct cmd_entry cmd_command_prompt_entry = {
.name = "command-prompt", .name = "command-prompt",
.alias = NULL, .alias = NULL,
.args = { "1iI:Np:t:", 0, 1 }, .args = { "1kiI:Np:t:", 0, 1 },
.usage = "[-1Ni] [-I inputs] [-p prompts] " CMD_TARGET_CLIENT_USAGE " " .usage = "[-1kiN] [-I inputs] [-p prompts] " CMD_TARGET_CLIENT_USAGE " "
"[template]", "[template]",
.flags = 0, .flags = 0,
@@ -122,6 +122,8 @@ cmd_command_prompt_exec(struct cmd *self, struct cmdq_item *item)
cdata->flags |= PROMPT_NUMERIC; cdata->flags |= PROMPT_NUMERIC;
else if (args_has(args, 'i')) else if (args_has(args, 'i'))
cdata->flags |= PROMPT_INCREMENTAL; cdata->flags |= PROMPT_INCREMENTAL;
else if (args_has(args, 'k'))
cdata->flags |= PROMPT_KEY;
status_prompt_set(c, prompt, input, cmd_command_prompt_callback, status_prompt_set(c, prompt, input, cmd_command_prompt_callback,
cmd_command_prompt_free, cdata, cdata->flags); cmd_command_prompt_free, cdata, cdata->flags);
free(prompt); free(prompt);

View File

@@ -20,6 +20,7 @@
#include <sys/types.h> #include <sys/types.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h>
#include <unistd.h> #include <unistd.h>
#include "tmux.h" #include "tmux.h"
@@ -34,8 +35,8 @@ const struct cmd_entry cmd_join_pane_entry = {
.name = "join-pane", .name = "join-pane",
.alias = "joinp", .alias = "joinp",
.args = { "bdhvp:l:s:t:", 0, 0 }, .args = { "bdfhvp:l:s:t:", 0, 0 },
.usage = "[-bdhv] [-p percentage|-l size] " CMD_SRCDST_PANE_USAGE, .usage = "[-bdfhv] [-l size] " CMD_SRCDST_PANE_USAGE,
.source = { 's', CMD_FIND_PANE, CMD_FIND_DEFAULT_MARKED }, .source = { 's', CMD_FIND_PANE, CMD_FIND_DEFAULT_MARKED },
.target = { 't', CMD_FIND_PANE, 0 }, .target = { 't', CMD_FIND_PANE, 0 },
@@ -51,7 +52,7 @@ const struct cmd_entry cmd_move_pane_entry = {
.args = { "bdhvp:l:s:t:", 0, 0 }, .args = { "bdhvp:l:s:t:", 0, 0 },
.usage = "[-bdhv] [-p percentage|-l size] " CMD_SRCDST_PANE_USAGE, .usage = "[-bdhv] [-p percentage|-l size] " CMD_SRCDST_PANE_USAGE,
.source = { 's', CMD_FIND_PANE, 0 }, .source = { 's', CMD_FIND_PANE, CMD_FIND_DEFAULT_MARKED },
.target = { 't', CMD_FIND_PANE, 0 }, .target = { 't', CMD_FIND_PANE, 0 },
.flags = 0, .flags = 0,
@@ -67,11 +68,13 @@ cmd_join_pane_exec(struct cmd *self, struct cmdq_item *item)
struct winlink *src_wl, *dst_wl; struct winlink *src_wl, *dst_wl;
struct window *src_w, *dst_w; struct window *src_w, *dst_w;
struct window_pane *src_wp, *dst_wp; struct window_pane *src_wp, *dst_wp;
char *cause; char *cause, *copy;
int size, percentage, dst_idx; const char *errstr, *p;
size_t plen;
int size, percentage, dst_idx, not_same_window;
int flags;
enum layout_type type; enum layout_type type;
struct layout_cell *lc; struct layout_cell *lc;
int not_same_window, flags;
if (self->entry == &cmd_join_pane_entry) if (self->entry == &cmd_join_pane_entry)
not_same_window = 1; not_same_window = 1;
@@ -104,12 +107,28 @@ cmd_join_pane_exec(struct cmd *self, struct cmdq_item *item)
type = LAYOUT_LEFTRIGHT; type = LAYOUT_LEFTRIGHT;
size = -1; size = -1;
if (args_has(args, 'l')) { if ((p = args_get(args, 'l')) != NULL) {
size = args_strtonum(args, 'l', 0, INT_MAX, &cause); plen = strlen(p);
if (cause != NULL) { if (p[plen - 1] == '%') {
cmdq_error(item, "size %s", cause); copy = xstrdup(p);
free(cause); copy[plen - 1] = '\0';
return (CMD_RETURN_ERROR); percentage = strtonum(copy, 0, INT_MAX, &errstr);
free(copy);
if (errstr != NULL) {
cmdq_error(item, "percentage %s", errstr);
return (CMD_RETURN_ERROR);
}
if (type == LAYOUT_TOPBOTTOM)
size = (dst_wp->sy * percentage) / 100;
else
size = (dst_wp->sx * percentage) / 100;
} else {
size = args_strtonum(args, 'l', 0, INT_MAX, &cause);
if (cause != NULL) {
cmdq_error(item, "size %s", cause);
free(cause);
return (CMD_RETURN_ERROR);
}
} }
} else if (args_has(args, 'p')) { } else if (args_has(args, 'p')) {
percentage = args_strtonum(args, 'p', 0, 100, &cause); percentage = args_strtonum(args, 'p', 0, 100, &cause);
@@ -123,10 +142,13 @@ cmd_join_pane_exec(struct cmd *self, struct cmdq_item *item)
else else
size = (dst_wp->sx * percentage) / 100; size = (dst_wp->sx * percentage) / 100;
} }
flags = 0;
if (args_has(args, 'b')) if (args_has(args, 'b'))
flags = SPAWN_BEFORE; flags |= SPAWN_BEFORE;
else if (args_has(args, 'f'))
flags = 0; flags |= SPAWN_FULLSIZE;
lc = layout_split_pane(dst_wp, type, size, flags); lc = layout_split_pane(dst_wp, type, size, flags);
if (lc == NULL) { if (lc == NULL) {
cmdq_error(item, "create pane failed: pane too small"); cmdq_error(item, "create pane failed: pane too small");

View File

@@ -37,7 +37,7 @@ const struct cmd_entry cmd_kill_pane_entry = {
.target = { 't', CMD_FIND_PANE, 0 }, .target = { 't', CMD_FIND_PANE, 0 },
.flags = 0, .flags = CMD_AFTERHOOK,
.exec = cmd_kill_pane_exec .exec = cmd_kill_pane_exec
}; };

View File

@@ -36,8 +36,8 @@ const struct cmd_entry cmd_list_keys_entry = {
.name = "list-keys", .name = "list-keys",
.alias = "lsk", .alias = "lsk",
.args = { "T:", 0, 0 }, .args = { "1NP:T:", 0, 1 },
.usage = "[-T key-table]", .usage = "[-1N] [-P prefix-string] [-T key-table] [key]",
.flags = CMD_STARTSERVER|CMD_AFTERHOOK, .flags = CMD_STARTSERVER|CMD_AFTERHOOK,
.exec = cmd_list_keys_exec .exec = cmd_list_keys_exec
@@ -54,6 +54,88 @@ const struct cmd_entry cmd_list_commands_entry = {
.exec = cmd_list_keys_exec .exec = cmd_list_keys_exec
}; };
static u_int
cmd_list_keys_get_width(const char *tablename, key_code only)
{
struct key_table *table;
struct key_binding *bd;
u_int width, keywidth = 0;
table = key_bindings_get_table(tablename, 0);
if (table == NULL)
return (0);
bd = key_bindings_first(table);
while (bd != NULL) {
if ((only != KEYC_UNKNOWN && bd->key != only) ||
KEYC_IS_MOUSE(bd->key) ||
bd->note == NULL) {
bd = key_bindings_next(table, bd);
continue;
}
width = utf8_cstrwidth(key_string_lookup_key(bd->key));
if (width > keywidth)
keywidth = width;
bd = key_bindings_next(table, bd);
}
return (keywidth);
}
static int
cmd_list_keys_print_notes(struct cmdq_item *item, struct args *args,
const char *tablename, u_int keywidth, key_code only, const char *prefix)
{
struct client *c = cmd_find_client(item, NULL, 1);
struct key_table *table;
struct key_binding *bd;
const char *key;
char *tmp;
int found = 0;
table = key_bindings_get_table(tablename, 0);
if (table == NULL)
return (0);
bd = key_bindings_first(table);
while (bd != NULL) {
if ((only != KEYC_UNKNOWN && bd->key != only) ||
KEYC_IS_MOUSE(bd->key) ||
bd->note == NULL) {
bd = key_bindings_next(table, bd);
continue;
}
found = 1;
key = key_string_lookup_key(bd->key);
tmp = utf8_padcstr(key, keywidth + 1);
if (args_has(args, '1') && c != NULL)
status_message_set(c, "%s%s%s", prefix, tmp, bd->note);
else
cmdq_print(item, "%s%s%s", prefix, tmp, bd->note);
free(tmp);
if (args_has(args, '1'))
break;
bd = key_bindings_next(table, bd);
}
return (found);
}
static char *
cmd_list_keys_get_prefix(struct args *args, key_code *prefix)
{
char *s;
*prefix = options_get_number(global_s_options, "prefix");
if (!args_has(args, 'P')) {
if (*prefix != KEYC_NONE)
xasprintf(&s, "%s ", key_string_lookup_key(*prefix));
else
s = xstrdup("");
} else
s = xstrdup(args_get(args, 'P'));
return (s);
}
static enum cmd_retval static enum cmd_retval
cmd_list_keys_exec(struct cmd *self, struct cmdq_item *item) cmd_list_keys_exec(struct cmd *self, struct cmdq_item *item)
{ {
@@ -61,19 +143,63 @@ cmd_list_keys_exec(struct cmd *self, struct cmdq_item *item)
struct key_table *table; struct key_table *table;
struct key_binding *bd; struct key_binding *bd;
const char *tablename, *r; const char *tablename, *r;
char *key, *cp, *tmp; char *key, *cp, *tmp, *start, *empty;
int repeat, width, tablewidth, keywidth; key_code prefix, only = KEYC_UNKNOWN;
int repeat, width, tablewidth, keywidth, found = 0;
size_t tmpsize, tmpused, cplen; size_t tmpsize, tmpused, cplen;
if (self->entry == &cmd_list_commands_entry) if (self->entry == &cmd_list_commands_entry)
return (cmd_list_keys_commands(self, item)); return (cmd_list_keys_commands(self, item));
if (args->argc != 0) {
only = key_string_lookup_string(args->argv[0]);
if (only == KEYC_UNKNOWN) {
cmdq_error(item, "invalid key: %s", args->argv[0]);
return (CMD_RETURN_ERROR);
}
}
tablename = args_get(args, 'T'); tablename = args_get(args, 'T');
if (tablename != NULL && key_bindings_get_table(tablename, 0) == NULL) { if (tablename != NULL && key_bindings_get_table(tablename, 0) == NULL) {
cmdq_error(item, "table %s doesn't exist", tablename); cmdq_error(item, "table %s doesn't exist", tablename);
return (CMD_RETURN_ERROR); return (CMD_RETURN_ERROR);
} }
if (args_has(args, 'N')) {
if (tablename == NULL) {
start = cmd_list_keys_get_prefix(args, &prefix);
keywidth = cmd_list_keys_get_width("root", only);
if (prefix != KEYC_NONE) {
width = cmd_list_keys_get_width("prefix", only);
if (width == 0)
prefix = KEYC_NONE;
else if (width > keywidth)
keywidth = width;
}
empty = utf8_padcstr("", utf8_cstrwidth(start));
found = cmd_list_keys_print_notes(item, args, "root",
keywidth, only, empty);
if (prefix != KEYC_NONE) {
if (cmd_list_keys_print_notes(item, args,
"prefix", keywidth, only, start))
found = 1;
}
free(empty);
} else {
if (args_has(args, 'P'))
start = xstrdup(args_get(args, 'P'));
else
start = xstrdup("");
keywidth = cmd_list_keys_get_width(tablename, only);
found = cmd_list_keys_print_notes(item, args, tablename,
keywidth, only, start);
}
free(start);
goto out;
}
repeat = 0; repeat = 0;
tablewidth = keywidth = 0; tablewidth = keywidth = 0;
table = key_bindings_first_table (); table = key_bindings_first_table ();
@@ -84,6 +210,10 @@ cmd_list_keys_exec(struct cmd *self, struct cmdq_item *item)
} }
bd = key_bindings_first(table); bd = key_bindings_first(table);
while (bd != NULL) { while (bd != NULL) {
if (only != KEYC_UNKNOWN && bd->key != only) {
bd = key_bindings_next(table, bd);
continue;
}
key = args_escape(key_string_lookup_key(bd->key)); key = args_escape(key_string_lookup_key(bd->key));
if (bd->flags & KEY_BINDING_REPEAT) if (bd->flags & KEY_BINDING_REPEAT)
@@ -113,6 +243,11 @@ cmd_list_keys_exec(struct cmd *self, struct cmdq_item *item)
} }
bd = key_bindings_first(table); bd = key_bindings_first(table);
while (bd != NULL) { while (bd != NULL) {
if (only != KEYC_UNKNOWN && bd->key != only) {
bd = key_bindings_next(table, bd);
continue;
}
found = 1;
key = args_escape(key_string_lookup_key(bd->key)); key = args_escape(key_string_lookup_key(bd->key));
if (!repeat) if (!repeat)
@@ -162,13 +297,18 @@ cmd_list_keys_exec(struct cmd *self, struct cmdq_item *item)
free(tmp); free(tmp);
out:
if (only != KEYC_UNKNOWN && !found) {
cmdq_error(item, "unknown key: %s", args->argv[0]);
return (CMD_RETURN_ERROR);
}
return (CMD_RETURN_NORMAL); return (CMD_RETURN_NORMAL);
} }
static enum cmd_retval static enum cmd_retval
cmd_list_keys_commands(struct cmd *self, struct cmdq_item *item) cmd_list_keys_commands(struct cmd *self, struct cmdq_item *item)
{ {
struct args *args = self->args; struct args *args = self->args;
const struct cmd_entry **entryp; const struct cmd_entry **entryp;
const struct cmd_entry *entry; const struct cmd_entry *entry;
struct format_tree *ft; struct format_tree *ft;

View File

@@ -33,8 +33,6 @@
static enum cmd_retval cmd_load_buffer_exec(struct cmd *, struct cmdq_item *); static enum cmd_retval cmd_load_buffer_exec(struct cmd *, struct cmdq_item *);
static void cmd_load_buffer_callback(struct client *, int, void *);
const struct cmd_entry cmd_load_buffer_entry = { const struct cmd_entry cmd_load_buffer_entry = {
.name = "load-buffer", .name = "load-buffer",
.alias = "loadb", .alias = "loadb",
@@ -48,9 +46,40 @@ const struct cmd_entry cmd_load_buffer_entry = {
struct cmd_load_buffer_data { struct cmd_load_buffer_data {
struct cmdq_item *item; struct cmdq_item *item;
char *bufname; char *name;
}; };
static void
cmd_load_buffer_done(__unused struct client *c, const char *path, int error,
int closed, struct evbuffer *buffer, void *data)
{
struct cmd_load_buffer_data *cdata = data;
struct cmdq_item *item = cdata->item;
void *bdata = EVBUFFER_DATA(buffer);
size_t bsize = EVBUFFER_LENGTH(buffer);
void *copy;
char *cause;
if (!closed)
return;
if (error != 0)
cmdq_error(item, "%s: %s", path, strerror(error));
else if (bsize != 0) {
copy = xmalloc(bsize);
memcpy(copy, bdata, bsize);
if (paste_set(copy, bsize, cdata->name, &cause) != 0) {
cmdq_error(item, "%s", cause);
free(cause);
free(copy);
}
}
cmdq_continue(item);
free(cdata->name);
free(cdata);
}
static enum cmd_retval static enum cmd_retval
cmd_load_buffer_exec(struct cmd *self, struct cmdq_item *item) cmd_load_buffer_exec(struct cmd *self, struct cmdq_item *item)
{ {
@@ -60,124 +89,19 @@ cmd_load_buffer_exec(struct cmd *self, struct cmdq_item *item)
struct session *s = item->target.s; struct session *s = item->target.s;
struct winlink *wl = item->target.wl; struct winlink *wl = item->target.wl;
struct window_pane *wp = item->target.wp; struct window_pane *wp = item->target.wp;
FILE *f; const char *bufname = args_get(args, 'b');
const char *bufname; char *path;
char *pdata = NULL, *new_pdata, *cause;
char *path, *file;
size_t psize;
int ch, error;
bufname = NULL; cdata = xmalloc(sizeof *cdata);
if (args_has(args, 'b')) cdata->item = item;
bufname = args_get(args, 'b'); if (bufname != NULL)
cdata->name = xstrdup(bufname);
else
cdata->name = NULL;
path = format_single(item, args->argv[0], c, s, wl, wp); path = format_single(item, args->argv[0], c, s, wl, wp);
if (strcmp(path, "-") == 0) { file_read(item->client, path, cmd_load_buffer_done, cdata);
free(path);
c = item->client;
cdata = xcalloc(1, sizeof *cdata);
cdata->item = item;
if (bufname != NULL)
cdata->bufname = xstrdup(bufname);
error = server_set_stdin_callback(c, cmd_load_buffer_callback,
cdata, &cause);
if (error != 0) {
cmdq_error(item, "-: %s", cause);
free(cause);
free(cdata);
return (CMD_RETURN_ERROR);
}
return (CMD_RETURN_WAIT);
}
file = server_client_get_path(item->client, path);
free(path); free(path);
f = fopen(file, "rb"); return (CMD_RETURN_WAIT);
if (f == NULL) {
cmdq_error(item, "%s: %s", file, strerror(errno));
goto error;
}
pdata = NULL;
psize = 0;
while ((ch = getc(f)) != EOF) {
/* Do not let the server die due to memory exhaustion. */
if ((new_pdata = realloc(pdata, psize + 2)) == NULL) {
cmdq_error(item, "realloc error: %s", strerror(errno));
goto error;
}
pdata = new_pdata;
pdata[psize++] = ch;
}
if (ferror(f)) {
cmdq_error(item, "%s: read error", file);
goto error;
}
if (pdata != NULL)
pdata[psize] = '\0';
fclose(f);
free(file);
if (paste_set(pdata, psize, bufname, &cause) != 0) {
cmdq_error(item, "%s", cause);
free(pdata);
free(cause);
return (CMD_RETURN_ERROR);
}
return (CMD_RETURN_NORMAL);
error:
free(pdata);
if (f != NULL)
fclose(f);
free(file);
return (CMD_RETURN_ERROR);
}
static void
cmd_load_buffer_callback(struct client *c, int closed, void *data)
{
struct cmd_load_buffer_data *cdata = data;
char *pdata, *cause, *saved;
size_t psize;
if (!closed)
return;
c->stdin_callback = NULL;
server_client_unref(c);
if (c->flags & CLIENT_DEAD)
goto out;
psize = EVBUFFER_LENGTH(c->stdin_data);
if (psize == 0 || (pdata = malloc(psize + 1)) == NULL)
goto out;
memcpy(pdata, EVBUFFER_DATA(c->stdin_data), psize);
pdata[psize] = '\0';
evbuffer_drain(c->stdin_data, psize);
if (paste_set(pdata, psize, cdata->bufname, &cause) != 0) {
/* No context so can't use server_client_msg_error. */
if (~c->flags & CLIENT_UTF8) {
saved = cause;
cause = utf8_sanitize(saved);
free(saved);
}
evbuffer_add_printf(c->stderr_data, "%s", cause);
server_client_push_stderr(c);
free(pdata);
free(cause);
}
out:
cmdq_continue(cdata->item);
free(cdata->bufname);
free(cdata);
} }

View File

@@ -94,26 +94,31 @@ cmd_new_session_exec(struct cmd *self, struct cmdq_item *item)
return (CMD_RETURN_ERROR); return (CMD_RETURN_ERROR);
} }
if (args_has(args, 's')) { tmp = args_get(args, 's');
newname = format_single(item, args_get(args, 's'), c, NULL, if (tmp != NULL) {
NULL, NULL); newname = format_single(item, tmp, c, NULL, NULL, NULL);
if (!session_check_name(newname)) { if (!session_check_name(newname)) {
cmdq_error(item, "bad session name: %s", newname); cmdq_error(item, "bad session name: %s", newname);
goto fail; goto fail;
} }
if ((as = session_find(newname)) != NULL) { }
if (args_has(args, 'A')) { if (args_has(args, 'A')) {
retval = cmd_attach_session(item, if (newname != NULL)
newname, args_has(args, 'D'), as = session_find(newname);
args_has(args, 'X'), 0, NULL, else
args_has(args, 'E')); as = item->target.s;
free(newname); if (as != NULL) {
return (retval); retval = cmd_attach_session(item, as->name,
} args_has(args, 'D'), args_has(args, 'X'), 0, NULL,
cmdq_error(item, "duplicate session: %s", newname); args_has(args, 'E'));
goto fail; free(newname);
return (retval);
} }
} }
if (newname != NULL && session_find(newname) != NULL) {
cmdq_error(item, "duplicate session: %s", newname);
goto fail;
}
/* Is this going to be part of a session group? */ /* Is this going to be part of a session group? */
group = args_get(args, 't'); group = args_get(args, 't');
@@ -259,6 +264,7 @@ cmd_new_session_exec(struct cmd *self, struct cmdq_item *item)
memset(&sc, 0, sizeof sc); memset(&sc, 0, sizeof sc);
sc.item = item; sc.item = item;
sc.s = s; sc.s = s;
sc.c = c;
sc.name = args_get(args, 'n'); sc.name = args_get(args, 'n');
sc.argc = args->argc; sc.argc = args->argc;

View File

@@ -72,6 +72,7 @@ cmd_new_window_exec(struct cmd *self, struct cmdq_item *item)
memset(&sc, 0, sizeof sc); memset(&sc, 0, sizeof sc);
sc.item = item; sc.item = item;
sc.s = s; sc.s = s;
sc.c = c;
sc.name = args_get(args, 'n'); sc.name = args_get(args, 'n');
sc.argc = args->argc; sc.argc = args->argc;

View File

@@ -133,7 +133,12 @@ statements : statement '\n'
free($2); free($2);
} }
statement : condition statement : /* empty */
{
$$ = xmalloc (sizeof *$$);
TAILQ_INIT($$);
}
| condition
{ {
struct cmd_parse_state *ps = &parse_state; struct cmd_parse_state *ps = &parse_state;
@@ -144,11 +149,6 @@ statement : condition
cmd_parse_free_commands($1); cmd_parse_free_commands($1);
} }
} }
| assignment
{
$$ = xmalloc (sizeof *$$);
TAILQ_INIT($$);
}
| commands | commands
{ {
struct cmd_parse_state *ps = &parse_state; struct cmd_parse_state *ps = &parse_state;
@@ -176,26 +176,28 @@ expanded : format
struct cmd_parse_input *pi = ps->input; struct cmd_parse_input *pi = ps->input;
struct format_tree *ft; struct format_tree *ft;
struct client *c = pi->c; struct client *c = pi->c;
struct cmd_find_state *fs; struct cmd_find_state *fsp;
struct cmd_find_state fs;
int flags = FORMAT_NOJOBS; int flags = FORMAT_NOJOBS;
if (cmd_find_valid_state(&pi->fs)) if (cmd_find_valid_state(&pi->fs))
fs = &pi->fs; fsp = &pi->fs;
else else {
fs = NULL; cmd_find_from_client(&fs, c, 0);
fsp = &fs;
}
ft = format_create(NULL, pi->item, FORMAT_NONE, flags); ft = format_create(NULL, pi->item, FORMAT_NONE, flags);
if (fs != NULL) format_defaults(ft, c, fsp->s, fsp->wl, fsp->wp);
format_defaults(ft, c, fs->s, fs->wl, fs->wp);
else
format_defaults(ft, c, NULL, NULL, NULL);
$$ = format_expand(ft, $1); $$ = format_expand(ft, $1);
format_free(ft); format_free(ft);
free($1); free($1);
} }
assignment : /* empty */ optional_assignment : /* empty */
| EQUALS | assignment
assignment : EQUALS
{ {
struct cmd_parse_state *ps = &parse_state; struct cmd_parse_state *ps = &parse_state;
int flags = ps->input->flags; int flags = ps->input->flags;
@@ -339,7 +341,8 @@ commands : command
struct cmd_parse_state *ps = &parse_state; struct cmd_parse_state *ps = &parse_state;
$$ = cmd_parse_new_commands(); $$ = cmd_parse_new_commands();
if (ps->scope == NULL || ps->scope->flag) if ($1->name != NULL &&
(ps->scope == NULL || ps->scope->flag))
TAILQ_INSERT_TAIL($$, $1, entry); TAILQ_INSERT_TAIL($$, $1, entry);
else else
cmd_parse_free_command($1); cmd_parse_free_command($1);
@@ -358,7 +361,8 @@ commands : command
{ {
struct cmd_parse_state *ps = &parse_state; struct cmd_parse_state *ps = &parse_state;
if (ps->scope == NULL || ps->scope->flag) { if ($3->name != NULL &&
(ps->scope == NULL || ps->scope->flag)) {
$$ = $1; $$ = $1;
TAILQ_INSERT_TAIL($$, $3, entry); TAILQ_INSERT_TAIL($$, $3, entry);
} else { } else {
@@ -372,7 +376,15 @@ commands : command
$$ = $1; $$ = $1;
} }
command : assignment TOKEN command : assignment
{
struct cmd_parse_state *ps = &parse_state;
$$ = xcalloc(1, sizeof *$$);
$$->name = NULL;
$$->line = ps->input->line;
}
| optional_assignment TOKEN
{ {
struct cmd_parse_state *ps = &parse_state; struct cmd_parse_state *ps = &parse_state;
@@ -381,7 +393,7 @@ command : assignment TOKEN
$$->line = ps->input->line; $$->line = ps->input->line;
} }
| assignment TOKEN arguments | optional_assignment TOKEN arguments
{ {
struct cmd_parse_state *ps = &parse_state; struct cmd_parse_state *ps = &parse_state;
@@ -696,6 +708,7 @@ cmd_parse_build_commands(struct cmd_parse_commands *cmds,
pr.status = CMD_PARSE_ERROR; pr.status = CMD_PARSE_ERROR;
pr.error = cmd_parse_get_error(pi->file, line, cause); pr.error = cmd_parse_get_error(pi->file, line, cause);
free(cause); free(cause);
cmd_list_free(cmdlist);
goto out; goto out;
} }
cmd_list_append(cmdlist, add); cmd_list_append(cmdlist, add);
@@ -744,6 +757,12 @@ cmd_parse_from_file(FILE *f, struct cmd_parse_input *pi)
struct cmd_parse_result * struct cmd_parse_result *
cmd_parse_from_string(const char *s, struct cmd_parse_input *pi) cmd_parse_from_string(const char *s, struct cmd_parse_input *pi)
{
return (cmd_parse_from_buffer(s, strlen(s), pi));
}
struct cmd_parse_result *
cmd_parse_from_buffer(const void *buf, size_t len, struct cmd_parse_input *pi)
{ {
static struct cmd_parse_result pr; static struct cmd_parse_result pr;
struct cmd_parse_input input; struct cmd_parse_input input;
@@ -756,14 +775,14 @@ cmd_parse_from_string(const char *s, struct cmd_parse_input *pi)
} }
memset(&pr, 0, sizeof pr); memset(&pr, 0, sizeof pr);
if (*s == '\0') { if (len == 0) {
pr.status = CMD_PARSE_EMPTY; pr.status = CMD_PARSE_EMPTY;
pr.cmdlist = NULL; pr.cmdlist = NULL;
pr.error = NULL; pr.error = NULL;
return (&pr); return (&pr);
} }
cmds = cmd_parse_do_buffer(s, strlen(s), pi, &cause); cmds = cmd_parse_do_buffer(buf, len, pi, &cause);
if (cmds == NULL) { if (cmds == NULL) {
pr.status = CMD_PARSE_ERROR; pr.status = CMD_PARSE_ERROR;
pr.error = cause; pr.error = cause;

View File

@@ -53,12 +53,16 @@ cmdq_get(struct client *c)
} }
/* Append an item. */ /* Append an item. */
void struct cmdq_item *
cmdq_append(struct client *c, struct cmdq_item *item) cmdq_append(struct client *c, struct cmdq_item *item)
{ {
struct cmdq_list *queue = cmdq_get(c); struct cmdq_list *queue = cmdq_get(c);
struct cmdq_item *next; struct cmdq_item *next;
TAILQ_FOREACH(next, queue, entry) {
log_debug("%s %s: queue %s (%u)", __func__, cmdq_name(c),
next->name, next->group);
}
do { do {
next = item->next; next = item->next;
item->next = NULL; item->next = NULL;
@@ -73,16 +77,21 @@ cmdq_append(struct client *c, struct cmdq_item *item)
item = next; item = next;
} while (item != NULL); } while (item != NULL);
return (TAILQ_LAST(queue, cmdq_list));
} }
/* Insert an item. */ /* Insert an item. */
void struct cmdq_item *
cmdq_insert_after(struct cmdq_item *after, struct cmdq_item *item) cmdq_insert_after(struct cmdq_item *after, struct cmdq_item *item)
{ {
struct client *c = after->client; struct client *c = after->client;
struct cmdq_list *queue = after->queue; struct cmdq_list *queue = after->queue;
struct cmdq_item *next; struct cmdq_item *next;
TAILQ_FOREACH(next, queue, entry) {
log_debug("%s %s: queue %s (%u)", __func__, cmdq_name(c),
next->name, next->group);
}
do { do {
next = item->next; next = item->next;
item->next = after->next; item->next = after->next;
@@ -100,9 +109,9 @@ cmdq_insert_after(struct cmdq_item *after, struct cmdq_item *item)
after = item; after = item;
item = next; item = next;
} while (item != NULL); } while (item != NULL);
return (after);
} }
/* Insert a hook. */ /* Insert a hook. */
void void
cmdq_insert_hook(struct session *s, struct cmdq_item *item, cmdq_insert_hook(struct session *s, struct cmdq_item *item,
@@ -144,11 +153,10 @@ cmdq_insert_hook(struct session *s, struct cmdq_item *item,
new_item = cmdq_get_command(cmdlist, fs, NULL, CMDQ_NOHOOKS); new_item = cmdq_get_command(cmdlist, fs, NULL, CMDQ_NOHOOKS);
cmdq_format(new_item, "hook", "%s", name); cmdq_format(new_item, "hook", "%s", name);
if (item != NULL) { if (item != NULL)
cmdq_insert_after(item, new_item); item = cmdq_insert_after(item, new_item);
item = new_item; else
} else item = cmdq_append(NULL, new_item);
cmdq_append(NULL, new_item);
a = options_array_next(a); a = options_array_next(a);
} }
@@ -476,13 +484,11 @@ void
cmdq_guard(struct cmdq_item *item, const char *guard, int flags) cmdq_guard(struct cmdq_item *item, const char *guard, int flags)
{ {
struct client *c = item->client; struct client *c = item->client;
long t = item->time;
u_int number = item->number;
if (c == NULL || !(c->flags & CLIENT_CONTROL)) if (c != NULL && (c->flags & CLIENT_CONTROL))
return; file_print(c, "%%%s %ld %u %d\n", guard, t, number, flags);
evbuffer_add_printf(c->stdout_data, "%%%s %ld %u %d\n", guard,
(long)item->time, item->number, flags);
server_client_push_stdout(c);
} }
/* Show message from command. */ /* Show message from command. */
@@ -496,29 +502,29 @@ cmdq_print(struct cmdq_item *item, const char *fmt, ...)
char *tmp, *msg; char *tmp, *msg;
va_start(ap, fmt); va_start(ap, fmt);
xvasprintf(&msg, fmt, ap);
va_end(ap);
log_debug("%s: %s", __func__, msg);
if (c == NULL) if (c == NULL)
/* nothing */; /* nothing */;
else if (c->session == NULL || (c->flags & CLIENT_CONTROL)) { else if (c->session == NULL || (c->flags & CLIENT_CONTROL)) {
if (~c->flags & CLIENT_UTF8) { if (~c->flags & CLIENT_UTF8) {
xvasprintf(&tmp, fmt, ap); tmp = msg;
msg = utf8_sanitize(tmp); msg = utf8_sanitize(tmp);
free(tmp); free(tmp);
evbuffer_add(c->stdout_data, msg, strlen(msg)); }
free(msg); file_print(c, "%s\n", msg);
} else
evbuffer_add_vprintf(c->stdout_data, fmt, ap);
evbuffer_add(c->stdout_data, "\n", 1);
server_client_push_stdout(c);
} else { } else {
wp = c->session->curw->window->active; wp = c->session->curw->window->active;
wme = TAILQ_FIRST(&wp->modes); wme = TAILQ_FIRST(&wp->modes);
if (wme == NULL || wme->mode != &window_view_mode) if (wme == NULL || wme->mode != &window_view_mode)
window_pane_set_mode(wp, &window_view_mode, NULL, NULL); window_pane_set_mode(wp, &window_view_mode, NULL, NULL);
window_copy_vadd(wp, fmt, ap); window_copy_add(wp, "%s", msg);
} }
va_end(ap); free(msg);
} }
/* Show error from command. */ /* Show error from command. */
@@ -529,11 +535,10 @@ cmdq_error(struct cmdq_item *item, const char *fmt, ...)
struct cmd *cmd = item->cmd; struct cmd *cmd = item->cmd;
va_list ap; va_list ap;
char *msg; char *msg;
size_t msglen;
char *tmp; char *tmp;
va_start(ap, fmt); va_start(ap, fmt);
msglen = xvasprintf(&msg, fmt, ap); xvasprintf(&msg, fmt, ap);
va_end(ap); va_end(ap);
log_debug("%s: %s", __func__, msg); log_debug("%s: %s", __func__, msg);
@@ -545,11 +550,11 @@ cmdq_error(struct cmdq_item *item, const char *fmt, ...)
tmp = msg; tmp = msg;
msg = utf8_sanitize(tmp); msg = utf8_sanitize(tmp);
free(tmp); free(tmp);
msglen = strlen(msg);
} }
evbuffer_add(c->stderr_data, msg, msglen); if (c->flags & CLIENT_CONTROL)
evbuffer_add(c->stderr_data, "\n", 1); file_print(c, "%s\n", msg);
server_client_push_stderr(c); else
file_error(c, "%s\n", msg);
c->retval = 1; c->retval = 1;
} else { } else {
*msg = toupper((u_char) *msg); *msg = toupper((u_char) *msg);

View File

@@ -130,7 +130,7 @@ cmd_refresh_client_exec(struct cmd *self, struct cmdq_item *item)
cmdq_error(item, "size too small or too big"); cmdq_error(item, "size too small or too big");
return (CMD_RETURN_ERROR); return (CMD_RETURN_ERROR);
} }
tty_set_size(&c->tty, x, y); tty_set_size(&c->tty, x, y, 0, 0);
c->flags |= CLIENT_SIZECHANGED; c->flags |= CLIENT_SIZECHANGED;
recalculate_sizes(); recalculate_sizes();
} }

View File

@@ -19,6 +19,7 @@
#include <sys/types.h> #include <sys/types.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h>
#include "tmux.h" #include "tmux.h"
@@ -55,10 +56,11 @@ cmd_resize_pane_exec(struct cmd *self, struct cmdq_item *item)
struct window *w = wl->window; struct window *w = wl->window;
struct client *c = item->client; struct client *c = item->client;
struct session *s = item->target.s; struct session *s = item->target.s;
const char *errstr; const char *errstr, *p;
char *cause; char *cause, *copy;
u_int adjust; u_int adjust;
int x, y; int x, y, percentage;
size_t plen;
if (args_has(args, 'M')) { if (args_has(args, 'M')) {
if (cmd_mouse_window(&shared->mouse, &s) == NULL) if (cmd_mouse_window(&shared->mouse, &s) == NULL)
@@ -91,21 +93,58 @@ cmd_resize_pane_exec(struct cmd *self, struct cmdq_item *item)
} }
} }
if (args_has(args, 'x')) { if ((p = args_get(args, 'x')) != NULL) {
x = args_strtonum(args, 'x', PANE_MINIMUM, INT_MAX, &cause); plen = strlen(p);
if (cause != NULL) { if (p[plen - 1] == '%') {
cmdq_error(item, "width %s", cause); copy = xstrdup(p);
free(cause); copy[plen - 1] = '\0';
return (CMD_RETURN_ERROR); percentage = strtonum(copy, 0, INT_MAX, &errstr);
free(copy);
if (errstr != NULL) {
cmdq_error(item, "width %s", errstr);
return (CMD_RETURN_ERROR);
}
x = (w->sx * percentage) / 100;
if (x < PANE_MINIMUM)
x = PANE_MINIMUM;
if (x > INT_MAX)
x = INT_MAX;
} else {
x = args_strtonum(args, 'x', PANE_MINIMUM, INT_MAX,
&cause);
if (cause != NULL) {
cmdq_error(item, "width %s", cause);
free(cause);
return (CMD_RETURN_ERROR);
}
} }
layout_resize_pane_to(wp, LAYOUT_LEFTRIGHT, x); layout_resize_pane_to(wp, LAYOUT_LEFTRIGHT, x);
} }
if (args_has(args, 'y')) { if ((p = args_get(args, 'y')) != NULL) {
y = args_strtonum(args, 'y', PANE_MINIMUM, INT_MAX, &cause); plen = strlen(p);
if (cause != NULL) { if (p[plen - 1] == '%') {
cmdq_error(item, "height %s", cause); copy = xstrdup(p);
free(cause); copy[plen - 1] = '\0';
return (CMD_RETURN_ERROR); percentage = strtonum(copy, 0, INT_MAX, &errstr);
free(copy);
if (errstr != NULL) {
cmdq_error(item, "height %s", errstr);
return (CMD_RETURN_ERROR);
}
y = (w->sy * percentage) / 100;
if (y < PANE_MINIMUM)
y = PANE_MINIMUM;
if (y > INT_MAX)
y = INT_MAX;
}
else {
y = args_strtonum(args, 'y', PANE_MINIMUM, INT_MAX,
&cause);
if (cause != NULL) {
cmdq_error(item, "height %s", cause);
free(cause);
return (CMD_RETURN_ERROR);
}
} }
layout_resize_pane_to(wp, LAYOUT_TOPBOTTOM, y); layout_resize_pane_to(wp, LAYOUT_TOPBOTTOM, y);
} }

View File

@@ -53,6 +53,7 @@ cmd_resize_window_exec(struct cmd *self, struct cmdq_item *item)
const char *errstr; const char *errstr;
char *cause; char *cause;
u_int adjust, sx, sy; u_int adjust, sx, sy;
int xpixel = -1, ypixel = -1;
if (args->argc == 0) if (args->argc == 0)
adjust = 1; adjust = 1;
@@ -97,13 +98,16 @@ cmd_resize_window_exec(struct cmd *self, struct cmdq_item *item)
} else if (args_has(args, 'D')) } else if (args_has(args, 'D'))
sy += adjust; sy += adjust;
if (args_has(args, 'A')) if (args_has(args, 'A')) {
default_window_size(s, w, &sx, &sy, WINDOW_SIZE_LARGEST); default_window_size(NULL, s, w, &sx, &sy, &xpixel, &ypixel,
else if (args_has(args, 'a')) WINDOW_SIZE_LARGEST);
default_window_size(s, w, &sx, &sy, WINDOW_SIZE_SMALLEST); } else if (args_has(args, 'a')) {
default_window_size(NULL, s, w, &sx, &sy, &xpixel, &ypixel,
WINDOW_SIZE_SMALLEST);
}
options_set_number(w->options, "window-size", WINDOW_SIZE_MANUAL); options_set_number(w->options, "window-size", WINDOW_SIZE_MANUAL);
resize_window(w, sx, sy); resize_window(w, sx, sy, xpixel, ypixel);
return (CMD_RETURN_NORMAL); return (CMD_RETURN_NORMAL);
} }

View File

@@ -59,6 +59,7 @@ cmd_respawn_window_exec(struct cmd *self, struct cmdq_item *item)
sc.item = item; sc.item = item;
sc.s = s; sc.s = s;
sc.wl = wl; sc.wl = wl;
sc.c = cmd_find_client(item, NULL, 1);
sc.name = NULL; sc.name = NULL;
sc.argc = args->argc; sc.argc = args->argc;

View File

@@ -31,8 +31,8 @@ const struct cmd_entry cmd_rotate_window_entry = {
.name = "rotate-window", .name = "rotate-window",
.alias = "rotatew", .alias = "rotatew",
.args = { "Dt:U", 0, 0 }, .args = { "Dt:UZ", 0, 0 },
.usage = "[-DU] " CMD_TARGET_WINDOW_USAGE, .usage = "[-DUZ] " CMD_TARGET_WINDOW_USAGE,
.target = { 't', CMD_FIND_WINDOW, 0 }, .target = { 't', CMD_FIND_WINDOW, 0 },
@@ -50,7 +50,7 @@ cmd_rotate_window_exec(struct cmd *self, struct cmdq_item *item)
struct layout_cell *lc; struct layout_cell *lc;
u_int sx, sy, xoff, yoff; u_int sx, sy, xoff, yoff;
server_unzoom_window(w); window_push_zoom(w, args_has(self->args, 'Z'));
if (args_has(self->args, 'D')) { if (args_has(self->args, 'D')) {
wp = TAILQ_LAST(&w->panes, window_panes); wp = TAILQ_LAST(&w->panes, window_panes);
@@ -77,9 +77,6 @@ cmd_rotate_window_exec(struct cmd *self, struct cmdq_item *item)
if ((wp = TAILQ_PREV(w->active, window_panes, entry)) == NULL) if ((wp = TAILQ_PREV(w->active, window_panes, entry)) == NULL)
wp = TAILQ_LAST(&w->panes, window_panes); wp = TAILQ_LAST(&w->panes, window_panes);
window_set_active_pane(w, wp, 1);
cmd_find_from_winlink_pane(current, wl, wp, 0);
server_redraw_window(w);
} else { } else {
wp = TAILQ_FIRST(&w->panes); wp = TAILQ_FIRST(&w->panes);
TAILQ_REMOVE(&w->panes, wp, entry); TAILQ_REMOVE(&w->panes, wp, entry);
@@ -105,10 +102,12 @@ cmd_rotate_window_exec(struct cmd *self, struct cmdq_item *item)
if ((wp = TAILQ_NEXT(w->active, entry)) == NULL) if ((wp = TAILQ_NEXT(w->active, entry)) == NULL)
wp = TAILQ_FIRST(&w->panes); wp = TAILQ_FIRST(&w->panes);
window_set_active_pane(w, wp, 1);
cmd_find_from_winlink_pane(current, wl, wp, 0);
server_redraw_window(w);
} }
window_set_active_pane(w, wp, 1);
cmd_find_from_winlink_pane(current, wl, wp, 0);
window_pop_zoom(w);
server_redraw_window(w);
return (CMD_RETURN_NORMAL); return (CMD_RETURN_NORMAL);
} }

View File

@@ -55,6 +55,20 @@ const struct cmd_entry cmd_show_buffer_entry = {
.exec = cmd_save_buffer_exec .exec = cmd_save_buffer_exec
}; };
static void
cmd_save_buffer_done(__unused struct client *c, const char *path, int error,
__unused int closed, __unused struct evbuffer *buffer, void *data)
{
struct cmdq_item *item = data;
if (!closed)
return;
if (error != 0)
cmdq_error(item, "%s: %s", path, strerror(error));
cmdq_continue(item);
}
static enum cmd_retval static enum cmd_retval
cmd_save_buffer_exec(struct cmd *self, struct cmdq_item *item) cmd_save_buffer_exec(struct cmd *self, struct cmdq_item *item)
{ {
@@ -64,18 +78,17 @@ cmd_save_buffer_exec(struct cmd *self, struct cmdq_item *item)
struct winlink *wl = item->target.wl; struct winlink *wl = item->target.wl;
struct window_pane *wp = item->target.wp; struct window_pane *wp = item->target.wp;
struct paste_buffer *pb; struct paste_buffer *pb;
const char *bufname, *bufdata, *start, *end, *flags; int flags;
char *msg, *path, *file; const char *bufname = args_get(args, 'b'), *bufdata;
size_t size, used, msglen, bufsize; size_t bufsize;
FILE *f; char *path;
if (!args_has(args, 'b')) { if (bufname == NULL) {
if ((pb = paste_get_top(NULL)) == NULL) { if ((pb = paste_get_top(NULL)) == NULL) {
cmdq_error(item, "no buffers"); cmdq_error(item, "no buffers");
return (CMD_RETURN_ERROR); return (CMD_RETURN_ERROR);
} }
} else { } else {
bufname = args_get(args, 'b');
pb = paste_get_name(bufname); pb = paste_get_name(bufname);
if (pb == NULL) { if (pb == NULL) {
cmdq_error(item, "no buffer %s", bufname); cmdq_error(item, "no buffer %s", bufname);
@@ -88,74 +101,13 @@ cmd_save_buffer_exec(struct cmd *self, struct cmdq_item *item)
path = xstrdup("-"); path = xstrdup("-");
else else
path = format_single(item, args->argv[0], c, s, wl, wp); path = format_single(item, args->argv[0], c, s, wl, wp);
if (strcmp(path, "-") == 0) {
free(path);
c = item->client;
if (c == NULL) {
cmdq_error(item, "can't write to stdout");
return (CMD_RETURN_ERROR);
}
if (c->session == NULL || (c->flags & CLIENT_CONTROL))
goto do_stdout;
goto do_print;
}
flags = "wb";
if (args_has(self->args, 'a')) if (args_has(self->args, 'a'))
flags = "ab"; flags = O_APPEND;
else
file = server_client_get_path(item->client, path); flags = 0;
file_write(item->client, path, flags, bufdata, bufsize,
cmd_save_buffer_done, item);
free(path); free(path);
f = fopen(file, flags); return (CMD_RETURN_WAIT);
if (f == NULL) {
cmdq_error(item, "%s: %s", file, strerror(errno));
free(file);
return (CMD_RETURN_ERROR);
}
if (fwrite(bufdata, 1, bufsize, f) != bufsize) {
cmdq_error(item, "%s: write error", file);
fclose(f);
free(file);
return (CMD_RETURN_ERROR);
}
fclose(f);
free(file);
return (CMD_RETURN_NORMAL);
do_stdout:
evbuffer_add(c->stdout_data, bufdata, bufsize);
server_client_push_stdout(c);
return (CMD_RETURN_NORMAL);
do_print:
if (bufsize > (INT_MAX / 4) - 1) {
cmdq_error(item, "buffer too big");
return (CMD_RETURN_ERROR);
}
msg = NULL;
used = 0;
while (used != bufsize) {
start = bufdata + used;
end = memchr(start, '\n', bufsize - used);
if (end != NULL)
size = end - start;
else
size = bufsize - used;
msglen = size * 4 + 1;
msg = xrealloc(msg, msglen);
strvisx(msg, start, size, VIS_OCTAL|VIS_TAB);
cmdq_print(item, "%s", msg);
used += size + (end != NULL);
}
free(msg);
return (CMD_RETURN_NORMAL);
} }

View File

@@ -33,8 +33,8 @@ const struct cmd_entry cmd_select_pane_entry = {
.name = "select-pane", .name = "select-pane",
.alias = "selectp", .alias = "selectp",
.args = { "DdegLlMmP:RT:t:U", 0, 0 }, /* -P and -g deprecated */ .args = { "DdegLlMmP:RT:t:UZ", 0, 0 }, /* -P and -g deprecated */
.usage = "[-DdeLlMmRU] [-T title] " CMD_TARGET_PANE_USAGE, .usage = "[-DdeLlMmRUZ] [-T title] " CMD_TARGET_PANE_USAGE,
.target = { 't', CMD_FIND_PANE, 0 }, .target = { 't', CMD_FIND_PANE, 0 },
@@ -46,8 +46,8 @@ const struct cmd_entry cmd_last_pane_entry = {
.name = "last-pane", .name = "last-pane",
.alias = "lastp", .alias = "lastp",
.args = { "det:", 0, 0 }, .args = { "det:Z", 0, 0 },
.usage = "[-de] " CMD_TARGET_WINDOW_USAGE, .usage = "[-deZ] " CMD_TARGET_WINDOW_USAGE,
.target = { 't', CMD_FIND_WINDOW, 0 }, .target = { 't', CMD_FIND_WINDOW, 0 },
@@ -111,12 +111,15 @@ cmd_select_pane_exec(struct cmd *self, struct cmdq_item *item)
else if (args_has(self->args, 'd')) else if (args_has(self->args, 'd'))
lastwp->flags |= PANE_INPUTOFF; lastwp->flags |= PANE_INPUTOFF;
else { else {
server_unzoom_window(w); if (window_push_zoom(w, args_has(self->args, 'Z')))
server_redraw_window(w);
window_redraw_active_switch(w, lastwp); window_redraw_active_switch(w, lastwp);
if (window_set_active_pane(w, lastwp, 1)) { if (window_set_active_pane(w, lastwp, 1)) {
cmd_find_from_winlink(current, wl, 0); cmd_find_from_winlink(current, wl, 0);
cmd_select_pane_redraw(w); cmd_select_pane_redraw(w);
} }
if (window_pop_zoom(w))
server_redraw_window(w);
} }
return (CMD_RETURN_NORMAL); return (CMD_RETURN_NORMAL);
} }
@@ -163,17 +166,21 @@ cmd_select_pane_exec(struct cmd *self, struct cmdq_item *item)
} }
if (args_has(self->args, 'L')) { if (args_has(self->args, 'L')) {
server_unzoom_window(wp->window); window_push_zoom(w, 1);
wp = window_pane_find_left(wp); wp = window_pane_find_left(wp);
window_pop_zoom(w);
} else if (args_has(self->args, 'R')) { } else if (args_has(self->args, 'R')) {
server_unzoom_window(wp->window); window_push_zoom(w, 1);
wp = window_pane_find_right(wp); wp = window_pane_find_right(wp);
window_pop_zoom(w);
} else if (args_has(self->args, 'U')) { } else if (args_has(self->args, 'U')) {
server_unzoom_window(wp->window); window_push_zoom(w, 1);
wp = window_pane_find_up(wp); wp = window_pane_find_up(wp);
window_pop_zoom(w);
} else if (args_has(self->args, 'D')) { } else if (args_has(self->args, 'D')) {
server_unzoom_window(wp->window); window_push_zoom(w, 1);
wp = window_pane_find_down(wp); wp = window_pane_find_down(wp);
window_pop_zoom(w);
} }
if (wp == NULL) if (wp == NULL)
return (CMD_RETURN_NORMAL); return (CMD_RETURN_NORMAL);
@@ -190,21 +197,24 @@ cmd_select_pane_exec(struct cmd *self, struct cmdq_item *item)
if (args_has(self->args, 'T')) { if (args_has(self->args, 'T')) {
pane_title = format_single(item, args_get(self->args, 'T'), pane_title = format_single(item, args_get(self->args, 'T'),
c, s, wl, wp); c, s, wl, wp);
screen_set_title(&wp->base, pane_title); if (screen_set_title(&wp->base, pane_title))
server_status_window(wp->window); server_status_window(wp->window);
free(pane_title); free(pane_title);
return (CMD_RETURN_NORMAL); return (CMD_RETURN_NORMAL);
} }
if (wp == w->active) if (wp == w->active)
return (CMD_RETURN_NORMAL); return (CMD_RETURN_NORMAL);
server_unzoom_window(wp->window); if (window_push_zoom(w, args_has(self->args, 'Z')))
server_redraw_window(w);
window_redraw_active_switch(w, wp); window_redraw_active_switch(w, wp);
if (window_set_active_pane(w, wp, 1)) { if (window_set_active_pane(w, wp, 1)) {
cmd_find_from_winlink_pane(current, wl, wp, 0); cmd_find_from_winlink_pane(current, wl, wp, 0);
cmdq_insert_hook(s, item, current, "after-select-pane"); cmdq_insert_hook(s, item, current, "after-select-pane");
cmd_select_pane_redraw(w); cmd_select_pane_redraw(w);
} }
if (window_pop_zoom(w))
server_redraw_window(w);
return (CMD_RETURN_NORMAL); return (CMD_RETURN_NORMAL);
} }

View File

@@ -33,8 +33,9 @@ const struct cmd_entry cmd_send_keys_entry = {
.name = "send-keys", .name = "send-keys",
.alias = "send", .alias = "send",
.args = { "HlXRMN:t:", 0, -1 }, .args = { "FHlMN:Rt:X", 0, -1 },
.usage = "[-HlXRM] [-N repeat-count] " CMD_TARGET_PANE_USAGE " key ...", .usage = "[-FHlMRX] [-N repeat-count] " CMD_TARGET_PANE_USAGE
" key ...",
.target = { 't', CMD_FIND_PANE, 0 }, .target = { 't', CMD_FIND_PANE, 0 },
@@ -59,6 +60,9 @@ static struct cmdq_item *
cmd_send_keys_inject_key(struct client *c, struct cmd_find_state *fs, cmd_send_keys_inject_key(struct client *c, struct cmd_find_state *fs,
struct cmdq_item *item, key_code key) struct cmdq_item *item, key_code key)
{ {
struct session *s = fs->s;
struct winlink *wl = fs->wl;
struct window_pane *wp = fs->wp;
struct window_mode_entry *wme; struct window_mode_entry *wme;
struct key_table *table; struct key_table *table;
struct key_binding *bd; struct key_binding *bd;
@@ -67,7 +71,8 @@ cmd_send_keys_inject_key(struct client *c, struct cmd_find_state *fs,
if (wme == NULL || wme->mode->key_table == NULL) { if (wme == NULL || wme->mode->key_table == NULL) {
if (options_get_number(fs->wp->window->options, "xterm-keys")) if (options_get_number(fs->wp->window->options, "xterm-keys"))
key |= KEYC_XTERM; key |= KEYC_XTERM;
window_pane_key(fs->wp, item->client, fs->s, fs->wl, key, NULL); if (window_pane_key(wp, item->client, s, wl, key, NULL) != 0)
return (NULL);
return (item); return (item);
} }
table = key_bindings_get_table(wme->mode->key_table(wme), 1); table = key_bindings_get_table(wme->mode->key_table(wme), 1);
@@ -86,6 +91,7 @@ cmd_send_keys_inject_string(struct client *c, struct cmd_find_state *fs,
struct cmdq_item *item, struct args *args, int i) struct cmdq_item *item, struct args *args, int i)
{ {
const char *s = args->argv[i]; const char *s = args->argv[i];
struct cmdq_item *new_item;
struct utf8_data *ud, *uc; struct utf8_data *ud, *uc;
wchar_t wc; wchar_t wc;
key_code key; key_code key;
@@ -103,8 +109,11 @@ cmd_send_keys_inject_string(struct client *c, struct cmd_find_state *fs,
literal = args_has(args, 'l'); literal = args_has(args, 'l');
if (!literal) { if (!literal) {
key = key_string_lookup_string(s); key = key_string_lookup_string(s);
if (key != KEYC_NONE && key != KEYC_UNKNOWN) if (key != KEYC_NONE && key != KEYC_UNKNOWN) {
return (cmd_send_keys_inject_key(c, fs, item, key)); new_item = cmd_send_keys_inject_key(c, fs, item, key);
if (new_item != NULL)
return (new_item);
}
literal = 1; literal = 1;
} }
if (literal) { if (literal) {

View File

@@ -31,8 +31,6 @@
static enum cmd_retval cmd_source_file_exec(struct cmd *, struct cmdq_item *); static enum cmd_retval cmd_source_file_exec(struct cmd *, struct cmdq_item *);
static enum cmd_retval cmd_source_file_done(struct cmdq_item *, void *);
const struct cmd_entry cmd_source_file_entry = { const struct cmd_entry cmd_source_file_entry = {
.name = "source-file", .name = "source-file",
.alias = "source", .alias = "source",
@@ -44,40 +42,130 @@ const struct cmd_entry cmd_source_file_entry = {
.exec = cmd_source_file_exec .exec = cmd_source_file_exec
}; };
struct cmd_source_file_data {
struct cmdq_item *item;
int flags;
struct cmdq_item *after;
enum cmd_retval retval;
u_int current;
char **files;
u_int nfiles;
};
static enum cmd_retval
cmd_source_file_complete_cb(struct cmdq_item *item, __unused void *data)
{
cfg_print_causes(item);
return (CMD_RETURN_NORMAL);
}
static void
cmd_source_file_complete(struct client *c, struct cmd_source_file_data *cdata)
{
struct cmdq_item *new_item;
if (cfg_finished) {
if (cdata->retval == CMD_RETURN_ERROR && c->session == NULL)
c->retval = 1;
new_item = cmdq_get_callback(cmd_source_file_complete_cb, NULL);
cmdq_insert_after(cdata->after, new_item);
}
free(cdata->files);
free(cdata);
}
static void
cmd_source_file_done(struct client *c, const char *path, int error,
int closed, struct evbuffer *buffer, void *data)
{
struct cmd_source_file_data *cdata = data;
struct cmdq_item *item = cdata->item;
void *bdata = EVBUFFER_DATA(buffer);
size_t bsize = EVBUFFER_LENGTH(buffer);
u_int n;
struct cmdq_item *new_item;
if (!closed)
return;
if (error != 0)
cmdq_error(item, "%s: %s", path, strerror(error));
else if (bsize != 0) {
if (load_cfg_from_buffer(bdata, bsize, path, c, cdata->after,
cdata->flags, &new_item) < 0)
cdata->retval = CMD_RETURN_ERROR;
else if (new_item != NULL)
cdata->after = new_item;
}
n = ++cdata->current;
if (n < cdata->nfiles)
file_read(c, cdata->files[n], cmd_source_file_done, cdata);
else {
cmd_source_file_complete(c, cdata);
cmdq_continue(item);
}
}
static void
cmd_source_file_add(struct cmd_source_file_data *cdata, const char *path)
{
log_debug("%s: %s", __func__, path);
cdata->files = xreallocarray(cdata->files, cdata->nfiles + 1,
sizeof *cdata->files);
cdata->files[cdata->nfiles++] = xstrdup(path);
}
static enum cmd_retval static enum cmd_retval
cmd_source_file_exec(struct cmd *self, struct cmdq_item *item) cmd_source_file_exec(struct cmd *self, struct cmdq_item *item)
{ {
struct args *args = self->args; struct args *args = self->args;
int flags = 0; struct cmd_source_file_data *cdata;
struct client *c = item->client; struct client *c = item->client;
struct cmdq_item *new_item, *after; enum cmd_retval retval = CMD_RETURN_NORMAL;
enum cmd_retval retval; char *pattern, *cwd;
char *pattern, *cwd; const char *path, *error;
const char *path, *error; glob_t g;
glob_t g; int i, result;
int i; u_int j;
u_int j;
cdata = xcalloc(1, sizeof *cdata);
cdata->item = item;
if (args_has(args, 'q')) if (args_has(args, 'q'))
flags |= CMD_PARSE_QUIET; cdata->flags |= CMD_PARSE_QUIET;
if (args_has(args, 'n')) if (args_has(args, 'n'))
flags |= CMD_PARSE_PARSEONLY; cdata->flags |= CMD_PARSE_PARSEONLY;
if (args_has(args, 'v')) if (args_has(args, 'v'))
flags |= CMD_PARSE_VERBOSE; cdata->flags |= CMD_PARSE_VERBOSE;
utf8_stravis(&cwd, server_client_get_cwd(c, NULL), VIS_GLOB); utf8_stravis(&cwd, server_client_get_cwd(c, NULL), VIS_GLOB);
retval = CMD_RETURN_NORMAL;
for (i = 0; i < args->argc; i++) { for (i = 0; i < args->argc; i++) {
path = args->argv[i]; path = args->argv[i];
if (strcmp(path, "-") == 0) {
cmd_source_file_add(cdata, "-");
continue;
}
if (*path == '/') if (*path == '/')
pattern = xstrdup(path); pattern = xstrdup(path);
else else
xasprintf(&pattern, "%s/%s", cwd, path); xasprintf(&pattern, "%s/%s", cwd, path);
log_debug("%s: %s", __func__, pattern); log_debug("%s: %s", __func__, pattern);
if (glob(pattern, 0, NULL, &g) != 0) { if ((result = glob(pattern, 0, NULL, &g)) != 0) {
error = strerror(errno); if (result != GLOB_NOMATCH ||
if (errno != ENOENT || (~flags & CMD_PARSE_QUIET)) { (~cdata->flags & CMD_PARSE_QUIET)) {
if (result == GLOB_NOMATCH)
error = strerror(ENOENT);
else if (result == GLOB_NOSPACE)
error = strerror(ENOMEM);
else
error = strerror(EINVAL);
cmdq_error(item, "%s: %s", path, error); cmdq_error(item, "%s: %s", path, error);
retval = CMD_RETURN_ERROR; retval = CMD_RETURN_ERROR;
} }
@@ -86,30 +174,19 @@ cmd_source_file_exec(struct cmd *self, struct cmdq_item *item)
} }
free(pattern); free(pattern);
after = item; for (j = 0; j < g.gl_pathc; j++)
for (j = 0; j < g.gl_pathc; j++) { cmd_source_file_add(cdata, g.gl_pathv[j]);
path = g.gl_pathv[j];
if (load_cfg(path, c, after, flags, &new_item) < 0)
retval = CMD_RETURN_ERROR;
else if (new_item != NULL)
after = new_item;
}
globfree(&g);
}
if (cfg_finished) {
if (retval == CMD_RETURN_ERROR && c->session == NULL)
c->retval = 1;
new_item = cmdq_get_callback(cmd_source_file_done, NULL);
cmdq_insert_after(item, new_item);
} }
cdata->after = item;
cdata->retval = retval;
if (cdata->nfiles != 0) {
file_read(c, cdata->files[0], cmd_source_file_done, cdata);
retval = CMD_RETURN_WAIT;
} else
cmd_source_file_complete(c, cdata);
free(cwd); free(cwd);
return (retval); return (retval);
} }
static enum cmd_retval
cmd_source_file_done(struct cmdq_item *item, __unused void *data)
{
cfg_print_causes(item);
return (CMD_RETURN_NORMAL);
}

View File

@@ -41,8 +41,7 @@ const struct cmd_entry cmd_split_window_entry = {
.args = { "bc:de:fF:hIl:p:Pt:v", 0, -1 }, .args = { "bc:de:fF:hIl:p:Pt:v", 0, -1 },
.usage = "[-bdefhIPv] [-c start-directory] [-e environment] " .usage = "[-bdefhIPv] [-c start-directory] [-e environment] "
"[-F format] [-p percentage|-l size] " CMD_TARGET_PANE_USAGE "[-F format] [-l size] " CMD_TARGET_PANE_USAGE " [command]",
" [command]",
.target = { 't', CMD_FIND_PANE, 0 }, .target = { 't', CMD_FIND_PANE, 0 },
@@ -64,20 +63,37 @@ cmd_split_window_exec(struct cmd *self, struct cmdq_item *item)
struct layout_cell *lc; struct layout_cell *lc;
struct cmd_find_state fs; struct cmd_find_state fs;
int size, percentage, flags, input; int size, percentage, flags, input;
const char *template, *add; const char *template, *add, *errstr, *p;
char *cause, *cp; char *cause, *cp, *copy;
size_t plen;
struct args_value *value; struct args_value *value;
if (args_has(args, 'h')) if (args_has(args, 'h'))
type = LAYOUT_LEFTRIGHT; type = LAYOUT_LEFTRIGHT;
else else
type = LAYOUT_TOPBOTTOM; type = LAYOUT_TOPBOTTOM;
if (args_has(args, 'l')) { if ((p = args_get(args, 'l')) != NULL) {
size = args_strtonum(args, 'l', 0, INT_MAX, &cause); plen = strlen(p);
if (cause != NULL) { if (p[plen - 1] == '%') {
cmdq_error(item, "create pane failed: -l %s", cause); copy = xstrdup(p);
free(cause); copy[plen - 1] = '\0';
return (CMD_RETURN_ERROR); percentage = strtonum(copy, 0, INT_MAX, &errstr);
free(copy);
if (errstr != NULL) {
cmdq_error(item, "percentage %s", errstr);
return (CMD_RETURN_ERROR);
}
if (type == LAYOUT_TOPBOTTOM)
size = (wp->sy * percentage) / 100;
else
size = (wp->sx * percentage) / 100;
} else {
size = args_strtonum(args, 'l', 0, INT_MAX, &cause);
if (cause != NULL) {
cmdq_error(item, "lines %s", cause);
free(cause);
return (CMD_RETURN_ERROR);
}
} }
} else if (args_has(args, 'p')) { } else if (args_has(args, 'p')) {
percentage = args_strtonum(args, 'p', 0, INT_MAX, &cause); percentage = args_strtonum(args, 'p', 0, INT_MAX, &cause);

View File

@@ -32,8 +32,8 @@ const struct cmd_entry cmd_swap_pane_entry = {
.name = "swap-pane", .name = "swap-pane",
.alias = "swapp", .alias = "swapp",
.args = { "dDs:t:U", 0, 0 }, .args = { "dDs:t:UZ", 0, 0 },
.usage = "[-dDU] " CMD_SRCDST_PANE_USAGE, .usage = "[-dDUZ] " CMD_SRCDST_PANE_USAGE,
.source = { 's', CMD_FIND_PANE, CMD_FIND_DEFAULT_MARKED }, .source = { 's', CMD_FIND_PANE, CMD_FIND_DEFAULT_MARKED },
.target = { 't', CMD_FIND_PANE, 0 }, .target = { 't', CMD_FIND_PANE, 0 },
@@ -45,6 +45,7 @@ const struct cmd_entry cmd_swap_pane_entry = {
static enum cmd_retval static enum cmd_retval
cmd_swap_pane_exec(struct cmd *self, struct cmdq_item *item) cmd_swap_pane_exec(struct cmd *self, struct cmdq_item *item)
{ {
struct args *args = self->args;
struct window *src_w, *dst_w; struct window *src_w, *dst_w;
struct window_pane *tmp_wp, *src_wp, *dst_wp; struct window_pane *tmp_wp, *src_wp, *dst_wp;
struct layout_cell *src_lc, *dst_lc; struct layout_cell *src_lc, *dst_lc;
@@ -54,23 +55,27 @@ cmd_swap_pane_exec(struct cmd *self, struct cmdq_item *item)
dst_wp = item->target.wp; dst_wp = item->target.wp;
src_w = item->source.wl->window; src_w = item->source.wl->window;
src_wp = item->source.wp; src_wp = item->source.wp;
server_unzoom_window(dst_w);
if (args_has(self->args, 'D')) { if (window_push_zoom(dst_w, args_has(args, 'Z')))
server_redraw_window(dst_w);
if (args_has(args, 'D')) {
src_w = dst_w; src_w = dst_w;
src_wp = TAILQ_NEXT(dst_wp, entry); src_wp = TAILQ_NEXT(dst_wp, entry);
if (src_wp == NULL) if (src_wp == NULL)
src_wp = TAILQ_FIRST(&dst_w->panes); src_wp = TAILQ_FIRST(&dst_w->panes);
} else if (args_has(self->args, 'U')) { } else if (args_has(args, 'U')) {
src_w = dst_w; src_w = dst_w;
src_wp = TAILQ_PREV(dst_wp, window_panes, entry); src_wp = TAILQ_PREV(dst_wp, window_panes, entry);
if (src_wp == NULL) if (src_wp == NULL)
src_wp = TAILQ_LAST(&dst_w->panes, window_panes); src_wp = TAILQ_LAST(&dst_w->panes, window_panes);
} }
server_unzoom_window(src_w);
if (src_w != dst_w && window_push_zoom(src_w, args_has(args, 'Z')))
server_redraw_window(src_w);
if (src_wp == dst_wp) if (src_wp == dst_wp)
return (CMD_RETURN_NORMAL); goto out;
tmp_wp = TAILQ_PREV(dst_wp, window_panes, entry); tmp_wp = TAILQ_PREV(dst_wp, window_panes, entry);
TAILQ_REMOVE(&dst_w->panes, dst_wp, entry); TAILQ_REMOVE(&dst_w->panes, dst_wp, entry);
@@ -103,7 +108,7 @@ cmd_swap_pane_exec(struct cmd *self, struct cmdq_item *item)
dst_wp->xoff = xoff; dst_wp->yoff = yoff; dst_wp->xoff = xoff; dst_wp->yoff = yoff;
window_pane_resize(dst_wp, sx, sy); window_pane_resize(dst_wp, sx, sy);
if (!args_has(self->args, 'd')) { if (!args_has(args, 'd')) {
if (src_w != dst_w) { if (src_w != dst_w) {
window_set_active_pane(src_w, dst_wp, 1); window_set_active_pane(src_w, dst_wp, 1);
window_set_active_pane(dst_w, src_wp, 1); window_set_active_pane(dst_w, src_wp, 1);
@@ -126,5 +131,10 @@ cmd_swap_pane_exec(struct cmd *self, struct cmdq_item *item)
server_redraw_window(src_w); server_redraw_window(src_w);
server_redraw_window(dst_w); server_redraw_window(dst_w);
out:
if (window_pop_zoom(src_w))
server_redraw_window(src_w);
if (src_w != dst_w && window_pop_zoom(dst_w))
server_redraw_window(dst_w);
return (CMD_RETURN_NORMAL); return (CMD_RETURN_NORMAL);
} }

View File

@@ -34,8 +34,8 @@ const struct cmd_entry cmd_switch_client_entry = {
.name = "switch-client", .name = "switch-client",
.alias = "switchc", .alias = "switchc",
.args = { "lc:Enpt:rT:", 0, 0 }, .args = { "lc:Enpt:rT:Z", 0, 0 },
.usage = "[-Elnpr] [-c target-client] [-t target-session] " .usage = "[-ElnprZ] [-c target-client] [-t target-session] "
"[-T key-table]", "[-T key-table]",
/* -t is special */ /* -t is special */
@@ -54,6 +54,7 @@ cmd_switch_client_exec(struct cmd *self, struct cmdq_item *item)
struct client *c; struct client *c;
struct session *s; struct session *s;
struct winlink *wl; struct winlink *wl;
struct window *w;
struct window_pane *wp; struct window_pane *wp;
const char *tablename; const char *tablename;
struct key_table *table; struct key_table *table;
@@ -72,6 +73,7 @@ cmd_switch_client_exec(struct cmd *self, struct cmdq_item *item)
return (CMD_RETURN_ERROR); return (CMD_RETURN_ERROR);
s = item->target.s; s = item->target.s;
wl = item->target.wl; wl = item->target.wl;
w = wl->window;
wp = item->target.wp; wp = item->target.wp;
if (args_has(args, 'r')) if (args_has(args, 'r'))
@@ -112,12 +114,15 @@ cmd_switch_client_exec(struct cmd *self, struct cmdq_item *item)
} else { } else {
if (item->client == NULL) if (item->client == NULL)
return (CMD_RETURN_NORMAL); return (CMD_RETURN_NORMAL);
if (wl != NULL && wp != NULL) {
if (window_push_zoom(w, args_has(self->args, 'Z')))
server_redraw_window(w);
window_redraw_active_switch(w, wp);
window_set_active_pane(w, wp, 1);
if (window_pop_zoom(w))
server_redraw_window(w);
}
if (wl != NULL) { if (wl != NULL) {
server_unzoom_window(wl->window);
if (wp != NULL) {
window_redraw_active_switch(wp->window, wp);
window_set_active_pane(wp->window, wp, 1);
}
session_set_current(s, wl); session_set_current(s, wl);
cmd_find_from_session(&item->shared->current, s, 0); cmd_find_from_session(&item->shared->current, s, 0);
} }

2
cmd.c
View File

@@ -660,7 +660,7 @@ char *
cmd_template_replace(const char *template, const char *s, int idx) cmd_template_replace(const char *template, const char *s, int idx)
{ {
char ch, *buf; char ch, *buf;
const char *ptr, *cp, quote[] = "\"\\$;"; const char *ptr, *cp, quote[] = "\"\\$;~";
int replaced, quoted; int replaced, quoted;
size_t len; size_t len;

View File

@@ -21,6 +21,7 @@
#include <sys/ioctl.h> #include <sys/ioctl.h>
#include <sys/uio.h> #include <sys/uio.h>
#include <fnmatch.h>
#include <limits.h> #include <limits.h>
#include <stdio.h> #include <stdio.h>
#include <termios.h> #include <termios.h>
@@ -61,12 +62,31 @@ void warn(const char *, ...);
void warnx(const char *, ...); void warnx(const char *, ...);
#endif #endif
#ifndef HAVE_PATHS_H #ifdef HAVE_PATHS_H
#define _PATH_BSHELL "/bin/sh" #include <paths.h>
#define _PATH_TMP "/tmp/" #endif
#ifndef _PATH_BSHELL
#define _PATH_BSHELL "/bin/sh"
#endif
#ifndef _PATH_TMP
#define _PATH_TMP "/tmp/"
#endif
#ifndef _PATH_DEVNULL
#define _PATH_DEVNULL "/dev/null" #define _PATH_DEVNULL "/dev/null"
#endif
#ifndef _PATH_TTY
#define _PATH_TTY "/dev/tty" #define _PATH_TTY "/dev/tty"
#endif
#ifndef _PATH_DEV
#define _PATH_DEV "/dev/" #define _PATH_DEV "/dev/"
#endif
#ifndef _PATH_DEFPATH
#define _PATH_DEFPATH "/usr/bin:/bin" #define _PATH_DEFPATH "/usr/bin:/bin"
#endif #endif
@@ -98,10 +118,6 @@ void warnx(const char *, ...);
#include "compat/bitstring.h" #include "compat/bitstring.h"
#endif #endif
#ifdef HAVE_PATHS_H
#include <paths.h>
#endif
#ifdef HAVE_LIBUTIL_H #ifdef HAVE_LIBUTIL_H
#include <libutil.h> #include <libutil.h>
#endif #endif
@@ -154,6 +170,14 @@ void warnx(const char *, ...);
#define O_DIRECTORY 0 #define O_DIRECTORY 0
#endif #endif
#ifndef FNM_CASEFOLD
#ifdef FNM_IGNORECASE
#define FNM_CASEFOLD FNM_IGNORECASE
#else
#define FNM_CASEFOLD 0
#endif
#endif
#ifndef INFTIM #ifndef INFTIM
#define INFTIM -1 #define INFTIM -1
#endif #endif

View File

@@ -1,6 +1,6 @@
# configure.ac # configure.ac
AC_INIT([tmux], 3.0a) AC_INIT([tmux], 3.1-rc)
AC_PREREQ([2.60]) AC_PREREQ([2.60])
AC_CONFIG_AUX_DIR(etc) AC_CONFIG_AUX_DIR(etc)

View File

@@ -54,7 +54,7 @@ control_notify_input(struct client *c, struct window_pane *wp,
else else
evbuffer_add_printf(message, "%c", buf[i]); evbuffer_add_printf(message, "%c", buf[i]);
} }
control_write_buffer(c, message); control_write(c, "%s", EVBUFFER_DATA(message));
evbuffer_free(message); evbuffer_free(message);
} }
} }

View File

@@ -30,23 +30,12 @@
void void
control_write(struct client *c, const char *fmt, ...) control_write(struct client *c, const char *fmt, ...)
{ {
va_list ap; va_list ap;
va_start(ap, fmt); va_start(ap, fmt);
evbuffer_add_vprintf(c->stdout_data, fmt, ap); file_vprint(c, fmt, ap);
file_print(c, "\n");
va_end(ap); va_end(ap);
evbuffer_add(c->stdout_data, "\n", 1);
server_client_push_stdout(c);
}
/* Write a buffer, adding a terminal newline. Empties buffer. */
void
control_write_buffer(struct client *c, struct evbuffer *buffer)
{
evbuffer_add_buffer(c->stdout_data, buffer);
evbuffer_add(c->stdout_data, "\n", 1);
server_client_push_stdout(c);
} }
/* Control error callback. */ /* Control error callback. */
@@ -65,20 +54,22 @@ control_error(struct cmdq_item *item, void *data)
} }
/* Control input callback. Read lines and fire commands. */ /* Control input callback. Read lines and fire commands. */
void static void
control_callback(struct client *c, int closed, __unused void *data) control_callback(__unused struct client *c, __unused const char *path,
int error, int closed, struct evbuffer *buffer, __unused void *data)
{ {
char *line; char *line;
struct cmdq_item *item; struct cmdq_item *item;
struct cmd_parse_result *pr; struct cmd_parse_result *pr;
if (closed) if (closed || error != 0)
c->flags |= CLIENT_EXIT; c->flags |= CLIENT_EXIT;
for (;;) { for (;;) {
line = evbuffer_readln(c->stdin_data, NULL, EVBUFFER_EOL_LF); line = evbuffer_readln(buffer, NULL, EVBUFFER_EOL_LF);
if (line == NULL) if (line == NULL)
break; break;
log_debug("%s: %s", __func__, line);
if (*line == '\0') { /* empty line exit */ if (*line == '\0') { /* empty line exit */
free(line); free(line);
c->flags |= CLIENT_EXIT; c->flags |= CLIENT_EXIT;
@@ -104,3 +95,12 @@ control_callback(struct client *c, int closed, __unused void *data)
free(line); free(line);
} }
} }
void
control_start(struct client *c)
{
file_read(c, "-", control_callback, c);
if (c->flags & CLIENT_CONTROLCONTROL)
file_print(c, "\033P1000p");
}

414
file.c Normal file
View File

@@ -0,0 +1,414 @@
/* $OpenBSD$ */
/*
* Copyright (c) 2019 Nicholas Marriott <nicholas.marriott@gmail.com>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER
* IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
* OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include <sys/types.h>
#include <sys/queue.h>
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include "tmux.h"
static int file_next_stream = 3;
RB_GENERATE(client_files, client_file, entry, file_cmp);
static char *
file_get_path(struct client *c, const char *file)
{
char *path;
if (*file == '/')
path = xstrdup(file);
else
xasprintf(&path, "%s/%s", server_client_get_cwd(c, NULL), file);
return (path);
}
int
file_cmp(struct client_file *cf1, struct client_file *cf2)
{
if (cf1->stream < cf2->stream)
return (-1);
if (cf1->stream > cf2->stream)
return (1);
return (0);
}
struct client_file *
file_create(struct client *c, int stream, client_file_cb cb, void *cbdata)
{
struct client_file *cf;
cf = xcalloc(1, sizeof *cf);
cf->c = c;
cf->references = 1;
cf->stream = stream;
cf->buffer = evbuffer_new();
if (cf->buffer == NULL)
fatalx("out of memory");
cf->cb = cb;
cf->data = cbdata;
if (cf->c != NULL) {
RB_INSERT(client_files, &cf->c->files, cf);
cf->c->references++;
}
return (cf);
}
void
file_free(struct client_file *cf)
{
if (--cf->references != 0)
return;
evbuffer_free(cf->buffer);
free(cf->path);
if (cf->c != NULL) {
RB_REMOVE(client_files, &cf->c->files, cf);
server_client_unref(cf->c);
}
free(cf);
}
static void
file_fire_done_cb(__unused int fd, __unused short events, void *arg)
{
struct client_file *cf = arg;
struct client *c = cf->c;
if (cf->cb != NULL && (c == NULL || (~c->flags & CLIENT_DEAD)))
cf->cb(c, cf->path, cf->error, 1, cf->buffer, cf->data);
file_free(cf);
}
void
file_fire_done(struct client_file *cf)
{
event_once(-1, EV_TIMEOUT, file_fire_done_cb, cf, NULL);
}
void
file_fire_read(struct client_file *cf)
{
struct client *c = cf->c;
if (cf->cb != NULL)
cf->cb(c, cf->path, cf->error, 0, cf->buffer, cf->data);
}
int
file_can_print(struct client *c)
{
if (c == NULL)
return (0);
if (c->session != NULL && (~c->flags & CLIENT_CONTROL))
return (0);
return (1);
}
void
file_print(struct client *c, const char *fmt, ...)
{
va_list ap;
va_start(ap, fmt);
file_vprint(c, fmt, ap);
va_end(ap);
}
void
file_vprint(struct client *c, const char *fmt, va_list ap)
{
struct client_file find, *cf;
struct msg_write_open msg;
if (!file_can_print(c))
return;
find.stream = 1;
if ((cf = RB_FIND(client_files, &c->files, &find)) == NULL) {
cf = file_create(c, 1, NULL, NULL);
cf->path = xstrdup("-");
evbuffer_add_vprintf(cf->buffer, fmt, ap);
msg.stream = 1;
msg.fd = STDOUT_FILENO;
msg.flags = 0;
proc_send(c->peer, MSG_WRITE_OPEN, -1, &msg, sizeof msg);
} else {
evbuffer_add_vprintf(cf->buffer, fmt, ap);
file_push(cf);
}
}
void
file_print_buffer(struct client *c, void *data, size_t size)
{
struct client_file find, *cf;
struct msg_write_open msg;
if (!file_can_print(c))
return;
find.stream = 1;
if ((cf = RB_FIND(client_files, &c->files, &find)) == NULL) {
cf = file_create(c, 1, NULL, NULL);
cf->path = xstrdup("-");
evbuffer_add(cf->buffer, data, size);
msg.stream = 1;
msg.fd = STDOUT_FILENO;
msg.flags = 0;
proc_send(c->peer, MSG_WRITE_OPEN, -1, &msg, sizeof msg);
} else {
evbuffer_add(cf->buffer, data, size);
file_push(cf);
}
}
void
file_error(struct client *c, const char *fmt, ...)
{
struct client_file find, *cf;
struct msg_write_open msg;
va_list ap;
if (!file_can_print(c))
return;
va_start(ap, fmt);
find.stream = 2;
if ((cf = RB_FIND(client_files, &c->files, &find)) == NULL) {
cf = file_create(c, 2, NULL, NULL);
cf->path = xstrdup("-");
evbuffer_add_vprintf(cf->buffer, fmt, ap);
msg.stream = 2;
msg.fd = STDERR_FILENO;
msg.flags = 0;
proc_send(c->peer, MSG_WRITE_OPEN, -1, &msg, sizeof msg);
} else {
evbuffer_add_vprintf(cf->buffer, fmt, ap);
file_push(cf);
}
va_end(ap);
}
void
file_write(struct client *c, const char *path, int flags, const void *bdata,
size_t bsize, client_file_cb cb, void *cbdata)
{
struct client_file *cf;
FILE *f;
struct msg_write_open *msg;
size_t msglen;
int fd = -1;
const char *mode;
if (strcmp(path, "-") == 0) {
cf = file_create(c, file_next_stream++, cb, cbdata);
cf->path = xstrdup("-");
fd = STDOUT_FILENO;
if (c == NULL || c->flags & CLIENT_ATTACHED) {
cf->error = EBADF;
goto done;
}
goto skip;
}
cf = file_create(c, file_next_stream++, cb, cbdata);
cf->path = file_get_path(c, path);
if (c == NULL || c->flags & CLIENT_ATTACHED) {
if (flags & O_APPEND)
mode = "ab";
else
mode = "wb";
f = fopen(cf->path, mode);
if (f == NULL) {
cf->error = errno;
goto done;
}
if (fwrite(bdata, 1, bsize, f) != bsize) {
fclose(f);
cf->error = EIO;
goto done;
}
fclose(f);
goto done;
}
skip:
evbuffer_add(cf->buffer, bdata, bsize);
msglen = strlen(cf->path) + 1 + sizeof *msg;
if (msglen > MAX_IMSGSIZE - IMSG_HEADER_SIZE) {
cf->error = E2BIG;
goto done;
}
msg = xmalloc(msglen);
msg->stream = cf->stream;
msg->fd = fd;
msg->flags = flags;
memcpy(msg + 1, cf->path, msglen - sizeof *msg);
if (proc_send(c->peer, MSG_WRITE_OPEN, -1, msg, msglen) != 0) {
free(msg);
cf->error = EINVAL;
goto done;
}
free(msg);
return;
done:
file_fire_done(cf);
}
void
file_read(struct client *c, const char *path, client_file_cb cb, void *cbdata)
{
struct client_file *cf;
FILE *f;
struct msg_read_open *msg;
size_t msglen, size;
int fd = -1;
char buffer[BUFSIZ];
if (strcmp(path, "-") == 0) {
cf = file_create(c, file_next_stream++, cb, cbdata);
cf->path = xstrdup("-");
fd = STDIN_FILENO;
if (c == NULL || c->flags & CLIENT_ATTACHED) {
cf->error = EBADF;
goto done;
}
goto skip;
}
cf = file_create(c, file_next_stream++, cb, cbdata);
cf->path = file_get_path(c, path);
if (c == NULL || c->flags & CLIENT_ATTACHED) {
f = fopen(cf->path, "rb");
if (f == NULL) {
cf->error = errno;
goto done;
}
for (;;) {
size = fread(buffer, 1, sizeof buffer, f);
if (evbuffer_add(cf->buffer, buffer, size) != 0) {
cf->error = ENOMEM;
goto done;
}
if (size != sizeof buffer)
break;
}
if (ferror(f)) {
cf->error = EIO;
goto done;
}
fclose(f);
goto done;
}
skip:
msglen = strlen(cf->path) + 1 + sizeof *msg;
if (msglen > MAX_IMSGSIZE - IMSG_HEADER_SIZE) {
cf->error = E2BIG;
goto done;
}
msg = xmalloc(msglen);
msg->stream = cf->stream;
msg->fd = fd;
memcpy(msg + 1, cf->path, msglen - sizeof *msg);
if (proc_send(c->peer, MSG_READ_OPEN, -1, msg, msglen) != 0) {
free(msg);
cf->error = EINVAL;
goto done;
}
free(msg);
return;
done:
file_fire_done(cf);
}
static void
file_push_cb(__unused int fd, __unused short events, void *arg)
{
struct client_file *cf = arg;
struct client *c = cf->c;
if (~c->flags & CLIENT_DEAD)
file_push(cf);
file_free(cf);
}
void
file_push(struct client_file *cf)
{
struct client *c = cf->c;
struct msg_write_data *msg;
size_t msglen, sent, left;
struct msg_write_close close;
msg = xmalloc(sizeof *msg);
left = EVBUFFER_LENGTH(cf->buffer);
while (left != 0) {
sent = left;
if (sent > MAX_IMSGSIZE - IMSG_HEADER_SIZE - sizeof *msg)
sent = MAX_IMSGSIZE - IMSG_HEADER_SIZE - sizeof *msg;
msglen = (sizeof *msg) + sent;
msg = xrealloc(msg, msglen);
msg->stream = cf->stream;
memcpy(msg + 1, EVBUFFER_DATA(cf->buffer), sent);
if (proc_send(c->peer, MSG_WRITE, -1, msg, msglen) != 0)
break;
evbuffer_drain(cf->buffer, sent);
left = EVBUFFER_LENGTH(cf->buffer);
log_debug("%s: file %d sent %zu, left %zu", c->name, cf->stream,
sent, left);
}
if (left != 0) {
cf->references++;
event_once(-1, EV_TIMEOUT, file_push_cb, cf, NULL);
} else if (cf->stream > 2) {
close.stream = cf->stream;
proc_send(c->peer, MSG_WRITE_CLOSE, -1, &close, sizeof close);
file_fire_done(cf);
}
free(msg);
}

View File

@@ -513,8 +513,8 @@ format_draw(struct screen_write_ctx *octx, const struct grid_cell *base,
int focus_start = -1, focus_end = -1; int focus_start = -1, focus_end = -1;
int list_state = -1, fill = -1; int list_state = -1, fill = -1;
enum style_align list_align = STYLE_ALIGN_DEFAULT; enum style_align list_align = STYLE_ALIGN_DEFAULT;
struct grid_cell gc; struct grid_cell gc, current_default;
struct style sy; struct style sy, saved_sy;
struct utf8_data *ud = &sy.gc.data; struct utf8_data *ud = &sy.gc.data;
const char *cp, *end; const char *cp, *end;
enum utf8_state more; enum utf8_state more;
@@ -523,7 +523,8 @@ format_draw(struct screen_write_ctx *octx, const struct grid_cell *base,
struct format_ranges frs; struct format_ranges frs;
struct style_range *sr; struct style_range *sr;
style_set(&sy, base); memcpy(&current_default, base, sizeof current_default);
style_set(&sy, &current_default);
TAILQ_INIT(&frs); TAILQ_INIT(&frs);
log_debug("%s: %s", __func__, expanded); log_debug("%s: %s", __func__, expanded);
@@ -535,7 +536,7 @@ format_draw(struct screen_write_ctx *octx, const struct grid_cell *base,
for (i = 0; i < TOTAL; i++) { for (i = 0; i < TOTAL; i++) {
screen_init(&s[i], size, 1, 0); screen_init(&s[i], size, 1, 0);
screen_write_start(&ctx[i], NULL, &s[i]); screen_write_start(&ctx[i], NULL, &s[i]);
screen_write_clearendofline(&ctx[i], base->bg); screen_write_clearendofline(&ctx[i], current_default.bg);
width[i] = 0; width[i] = 0;
} }
@@ -581,7 +582,8 @@ format_draw(struct screen_write_ctx *octx, const struct grid_cell *base,
goto out; goto out;
} }
tmp = xstrndup(cp + 2, end - (cp + 2)); tmp = xstrndup(cp + 2, end - (cp + 2));
if (style_parse(&sy, base, tmp) != 0) { style_copy(&saved_sy, &sy);
if (style_parse(&sy, &current_default, tmp) != 0) {
log_debug("%s: invalid style '%s'", __func__, tmp); log_debug("%s: invalid style '%s'", __func__, tmp);
free(tmp); free(tmp);
cp = end + 1; cp = end + 1;
@@ -595,6 +597,15 @@ format_draw(struct screen_write_ctx *octx, const struct grid_cell *base,
if (sy.fill != 8) if (sy.fill != 8)
fill = sy.fill; fill = sy.fill;
/* If this style pushed or popped the default, update it. */
if (sy.default_type == STYLE_DEFAULT_PUSH) {
memcpy(&current_default, &saved_sy.gc, sizeof current_default);
sy.default_type = STYLE_DEFAULT_BASE;
} else if (sy.default_type == STYLE_DEFAULT_POP) {
memcpy(&current_default, base, sizeof current_default);
sy.default_type = STYLE_DEFAULT_BASE;
}
/* Check the list state. */ /* Check the list state. */
switch (sy.list) { switch (sy.list) {
case STYLE_LIST_ON: case STYLE_LIST_ON:
@@ -838,8 +849,10 @@ format_trim_left(const char *expanded, u_int limit)
out += ud.size; out += ud.size;
} }
width += ud.width; width += ud.width;
} else } else {
cp -= ud.have; cp -= ud.have;
cp++;
}
} else if (*cp > 0x1f && *cp < 0x7f) { } else if (*cp > 0x1f && *cp < 0x7f) {
if (width + 1 <= limit) if (width + 1 <= limit)
*out++ = *cp; *out++ = *cp;
@@ -885,8 +898,10 @@ format_trim_right(const char *expanded, u_int limit)
out += ud.size; out += ud.size;
} }
width += ud.width; width += ud.width;
} else } else {
cp -= ud.have; cp -= ud.have;
cp++;
}
} else if (*cp > 0x1f && *cp < 0x7f) { } else if (*cp > 0x1f && *cp < 0x7f) {
if (width >= skip) if (width >= skip)
*out++ = *cp; *out++ = *cp;

370
format.c
View File

@@ -456,6 +456,35 @@ format_cb_pid(__unused struct format_tree *ft, struct format_entry *fe)
xasprintf(&fe->value, "%ld", (long)getpid()); xasprintf(&fe->value, "%ld", (long)getpid());
} }
/* Callback for session_attached_list. */
static void
format_cb_session_attached_list(struct format_tree *ft, struct format_entry *fe)
{
struct session *s = ft->s;
struct client *loop;
struct evbuffer *buffer;
int size;
if (s == NULL)
return;
buffer = evbuffer_new();
if (buffer == NULL)
fatalx("out of memory");
TAILQ_FOREACH(loop, &clients, entry) {
if (loop->session == s) {
if (EVBUFFER_LENGTH(buffer) > 0)
evbuffer_add(buffer, ",", 1);
evbuffer_add_printf(buffer, "%s", loop->name);
}
}
if ((size = EVBUFFER_LENGTH(buffer)) != 0)
xasprintf(&fe->value, "%.*s", size, EVBUFFER_DATA(buffer));
evbuffer_free(buffer);
}
/* Callback for session_alerts. */ /* Callback for session_alerts. */
static void static void
format_cb_session_alerts(struct format_tree *ft, struct format_entry *fe) format_cb_session_alerts(struct format_tree *ft, struct format_entry *fe)
@@ -528,6 +557,128 @@ format_cb_window_stack_index(struct format_tree *ft, struct format_entry *fe)
fe->value = xstrdup("0"); fe->value = xstrdup("0");
} }
/* Callback for window_linked_sessions_list. */
static void
format_cb_window_linked_sessions_list(struct format_tree *ft,
struct format_entry *fe)
{
struct window *w = ft->wl->window;
struct winlink *wl;
struct evbuffer *buffer;
int size;
buffer = evbuffer_new();
if (buffer == NULL)
fatalx("out of memory");
TAILQ_FOREACH(wl, &w->winlinks, wentry) {
if (EVBUFFER_LENGTH(buffer) > 0)
evbuffer_add(buffer, ",", 1);
evbuffer_add_printf(buffer, "%s", wl->session->name);
}
if ((size = EVBUFFER_LENGTH(buffer)) != 0)
xasprintf(&fe->value, "%.*s", size, EVBUFFER_DATA(buffer));
evbuffer_free(buffer);
}
/* Callback for window_active_sessions. */
static void
format_cb_window_active_sessions(struct format_tree *ft,
struct format_entry *fe)
{
struct window *w = ft->wl->window;
struct winlink *wl;
u_int n = 0;
TAILQ_FOREACH(wl, &w->winlinks, wentry) {
if (wl->session->curw == wl)
n++;
}
xasprintf(&fe->value, "%u", n);
}
/* Callback for window_active_sessions_list. */
static void
format_cb_window_active_sessions_list(struct format_tree *ft,
struct format_entry *fe)
{
struct window *w = ft->wl->window;
struct winlink *wl;
struct evbuffer *buffer;
int size;
buffer = evbuffer_new();
if (buffer == NULL)
fatalx("out of memory");
TAILQ_FOREACH(wl, &w->winlinks, wentry) {
if (wl->session->curw == wl) {
if (EVBUFFER_LENGTH(buffer) > 0)
evbuffer_add(buffer, ",", 1);
evbuffer_add_printf(buffer, "%s", wl->session->name);
}
}
if ((size = EVBUFFER_LENGTH(buffer)) != 0)
xasprintf(&fe->value, "%.*s", size, EVBUFFER_DATA(buffer));
evbuffer_free(buffer);
}
/* Callback for window_active_clients. */
static void
format_cb_window_active_clients(struct format_tree *ft, struct format_entry *fe)
{
struct window *w = ft->wl->window;
struct client *loop;
struct session *client_session;
u_int n = 0;
TAILQ_FOREACH(loop, &clients, entry) {
client_session = loop->session;
if (client_session == NULL)
continue;
if (w == client_session->curw->window)
n++;
}
xasprintf(&fe->value, "%u", n);
}
/* Callback for window_active_clients_list. */
static void
format_cb_window_active_clients_list(struct format_tree *ft,
struct format_entry *fe)
{
struct window *w = ft->wl->window;
struct client *loop;
struct session *client_session;
struct evbuffer *buffer;
int size;
buffer = evbuffer_new();
if (buffer == NULL)
fatalx("out of memory");
TAILQ_FOREACH(loop, &clients, entry) {
client_session = loop->session;
if (client_session == NULL)
continue;
if (w == client_session->curw->window) {
if (EVBUFFER_LENGTH(buffer) > 0)
evbuffer_add(buffer, ",", 1);
evbuffer_add_printf(buffer, "%s", loop->name);
}
}
if ((size = EVBUFFER_LENGTH(buffer)) != 0)
xasprintf(&fe->value, "%.*s", size, EVBUFFER_DATA(buffer));
evbuffer_free(buffer);
}
/* Callback for window_layout. */ /* Callback for window_layout. */
static void static void
format_cb_window_layout(struct format_tree *ft, struct format_entry *fe) format_cb_window_layout(struct format_tree *ft, struct format_entry *fe)
@@ -574,7 +725,7 @@ format_cb_current_command(struct format_tree *ft, struct format_entry *fe)
struct window_pane *wp = ft->wp; struct window_pane *wp = ft->wp;
char *cmd; char *cmd;
if (wp == NULL) if (wp == NULL || wp->shell == NULL)
return; return;
cmd = osdep_get_name(wp->fd, wp->tty); cmd = osdep_get_name(wp->fd, wp->tty);
@@ -677,11 +828,52 @@ format_cb_session_group_list(struct format_tree *ft, struct format_entry *fe)
buffer = evbuffer_new(); buffer = evbuffer_new();
if (buffer == NULL) if (buffer == NULL)
fatalx("out of memory"); fatalx("out of memory");
TAILQ_FOREACH(loop, &sg->sessions, gentry) { TAILQ_FOREACH(loop, &sg->sessions, gentry) {
if (EVBUFFER_LENGTH(buffer) > 0) if (EVBUFFER_LENGTH(buffer) > 0)
evbuffer_add(buffer, ",", 1); evbuffer_add(buffer, ",", 1);
evbuffer_add_printf(buffer, "%s", loop->name); evbuffer_add_printf(buffer, "%s", loop->name);
} }
if ((size = EVBUFFER_LENGTH(buffer)) != 0)
xasprintf(&fe->value, "%.*s", size, EVBUFFER_DATA(buffer));
evbuffer_free(buffer);
}
/* Callback for session_group_attached_list. */
static void
format_cb_session_group_attached_list(struct format_tree *ft,
struct format_entry *fe)
{
struct session *s = ft->s, *client_session, *session_loop;
struct session_group *sg;
struct client *loop;
struct evbuffer *buffer;
int size;
if (s == NULL)
return;
sg = session_group_contains(s);
if (sg == NULL)
return;
buffer = evbuffer_new();
if (buffer == NULL)
fatalx("out of memory");
TAILQ_FOREACH(loop, &clients, entry) {
client_session = loop->session;
if (client_session == NULL)
continue;
TAILQ_FOREACH(session_loop, &sg->sessions, gentry) {
if (session_loop == client_session){
if (EVBUFFER_LENGTH(buffer) > 0)
evbuffer_add(buffer, ",", 1);
evbuffer_add_printf(buffer, "%s", loop->name);
}
}
}
if ((size = EVBUFFER_LENGTH(buffer)) != 0) if ((size = EVBUFFER_LENGTH(buffer)) != 0)
xasprintf(&fe->value, "%.*s", size, EVBUFFER_DATA(buffer)); xasprintf(&fe->value, "%.*s", size, EVBUFFER_DATA(buffer));
evbuffer_free(buffer); evbuffer_free(buffer);
@@ -718,30 +910,19 @@ format_cb_cursor_character(struct format_tree *ft, struct format_entry *fe)
xasprintf(&fe->value, "%.*s", (int)gc.data.size, gc.data.data); xasprintf(&fe->value, "%.*s", (int)gc.data.size, gc.data.data);
} }
/* Callback for mouse_word. */ /* Return word at given coordinates. Caller frees. */
static void char *
format_cb_mouse_word(struct format_tree *ft, struct format_entry *fe) format_grid_word(struct grid *gd, u_int x, u_int y)
{ {
struct window_pane *wp;
u_int x, y, end;
struct grid *gd;
struct grid_line *gl; struct grid_line *gl;
struct grid_cell gc; struct grid_cell gc;
const char *ws; const char *ws;
struct utf8_data *ud = NULL; struct utf8_data *ud = NULL;
u_int end;
size_t size = 0; size_t size = 0;
int found = 0; int found = 0;
char *s = NULL;
if (!ft->m.valid)
return;
wp = cmd_mouse_pane(&ft->m, NULL, NULL);
if (wp == NULL)
return;
if (!TAILQ_EMPTY (&wp->modes))
return;
if (cmd_mouse_at(wp, &ft->m, &x, &y, 0) != 0)
return;
gd = wp->base.grid;
ws = options_get_string(global_s_options, "word-separators"); ws = options_get_string(global_s_options, "word-separators");
y = gd->hsize + y; y = gd->hsize + y;
@@ -794,21 +975,19 @@ format_cb_mouse_word(struct format_tree *ft, struct format_entry *fe)
} }
if (size != 0) { if (size != 0) {
ud[size].size = 0; ud[size].size = 0;
fe->value = utf8_tocstr(ud); s = utf8_tocstr(ud);
free(ud); free(ud);
} }
return (s);
} }
/* Callback for mouse_line. */ /* Callback for mouse_word. */
static void static void
format_cb_mouse_line(struct format_tree *ft, struct format_entry *fe) format_cb_mouse_word(struct format_tree *ft, struct format_entry *fe)
{ {
struct window_pane *wp; struct window_pane *wp;
u_int x, y; u_int x, y;
struct grid *gd; char *s;
struct grid_cell gc;
struct utf8_data *ud = NULL;
size_t size = 0;
if (!ft->m.valid) if (!ft->m.valid)
return; return;
@@ -819,7 +998,21 @@ format_cb_mouse_line(struct format_tree *ft, struct format_entry *fe)
return; return;
if (cmd_mouse_at(wp, &ft->m, &x, &y, 0) != 0) if (cmd_mouse_at(wp, &ft->m, &x, &y, 0) != 0)
return; return;
gd = wp->base.grid;
s = format_grid_word(wp->base.grid, x, y);
if (s != NULL)
fe->value = s;
}
/* Return line at given coordinates. Caller frees. */
char *
format_grid_line(struct grid *gd, u_int y)
{
struct grid_cell gc;
struct utf8_data *ud = NULL;
u_int x;
size_t size = 0;
char *s = NULL;
y = gd->hsize + y; y = gd->hsize + y;
for (x = 0; x < grid_line_length(gd, y); x++) { for (x = 0; x < grid_line_length(gd, y); x++) {
@@ -832,9 +1025,33 @@ format_cb_mouse_line(struct format_tree *ft, struct format_entry *fe)
} }
if (size != 0) { if (size != 0) {
ud[size].size = 0; ud[size].size = 0;
fe->value = utf8_tocstr(ud); s = utf8_tocstr(ud);
free(ud); free(ud);
} }
return (s);
}
/* Callback for mouse_line. */
static void
format_cb_mouse_line(struct format_tree *ft, struct format_entry *fe)
{
struct window_pane *wp;
u_int x, y;
char *s;
if (!ft->m.valid)
return;
wp = cmd_mouse_pane(&ft->m, NULL, NULL);
if (wp == NULL)
return;
if (!TAILQ_EMPTY (&wp->modes))
return;
if (cmd_mouse_at(wp, &ft->m, &x, &y, 0) != 0)
return;
s = format_grid_line(wp->base.grid, y);
if (s != NULL)
fe->value = s;
} }
/* Merge a format tree. */ /* Merge a format tree. */
@@ -904,7 +1121,7 @@ format_create(struct client *c, struct cmdq_item *item, int tag, int flags)
ft->flags = flags; ft->flags = flags;
ft->time = time(NULL); ft->time = time(NULL);
format_add(ft, "version", "%s", VERSION); format_add(ft, "version", "%s", getversion());
format_add_cb(ft, "host", format_cb_host); format_add_cb(ft, "host", format_cb_host);
format_add_cb(ft, "host_short", format_cb_host_short); format_add_cb(ft, "host_short", format_cb_host_short);
format_add_cb(ft, "pid", format_cb_pid); format_add_cb(ft, "pid", format_cb_pid);
@@ -949,12 +1166,12 @@ format_each(struct format_tree *ft, void (*cb)(const char *, const char *,
void *), void *arg) void *), void *arg)
{ {
struct format_entry *fe; struct format_entry *fe;
static char s[64]; char s[64];
RB_FOREACH(fe, format_entry_tree, &ft->tree) { RB_FOREACH(fe, format_entry_tree, &ft->tree) {
if (fe->t != 0) { if (fe->t != 0) {
xsnprintf(s, sizeof s, "%lld", (long long)fe->t); xsnprintf(s, sizeof s, "%lld", (long long)fe->t);
cb(fe->key, fe->value, s); cb(fe->key, s, arg);
} else { } else {
if (fe->value == NULL && fe->cb != NULL) { if (fe->value == NULL && fe->cb != NULL) {
fe->cb(ft, fe); fe->cb(ft, fe);
@@ -966,7 +1183,6 @@ format_each(struct format_tree *ft, void (*cb)(const char *, const char *,
} }
} }
/* Add a key-value pair. */ /* Add a key-value pair. */
void void
format_add(struct format_tree *ft, const char *key, const char *fmt, ...) format_add(struct format_tree *ft, const char *key, const char *fmt, ...)
@@ -998,8 +1214,7 @@ format_add(struct format_tree *ft, const char *key, const char *fmt, ...)
static void static void
format_add_tv(struct format_tree *ft, const char *key, struct timeval *tv) format_add_tv(struct format_tree *ft, const char *key, struct timeval *tv)
{ {
struct format_entry *fe; struct format_entry *fe, *fe_now;
struct format_entry *fe_now;
fe = xmalloc(sizeof *fe); fe = xmalloc(sizeof *fe);
fe->key = xstrdup(key); fe->key = xstrdup(key);
@@ -1290,7 +1505,7 @@ format_build_modifiers(struct format_tree *ft, const char **s, u_int *count)
} }
/* Now try single character with arguments. */ /* Now try single character with arguments. */
if (strchr("mCs=", cp[0]) == NULL) if (strchr("mCs=p", cp[0]) == NULL)
break; break;
c = cp[0]; c = cp[0];
@@ -1551,15 +1766,15 @@ static int
format_replace(struct format_tree *ft, const char *key, size_t keylen, format_replace(struct format_tree *ft, const char *key, size_t keylen,
char **buf, size_t *len, size_t *off) char **buf, size_t *len, size_t *off)
{ {
struct window_pane *wp = ft->wp; struct window_pane *wp = ft->wp;
const char *errptr, *copy, *cp, *marker = NULL; const char *errptr, *copy, *cp, *marker = NULL;
char *copy0, *condition, *found, *new; char *copy0, *condition, *found, *new;
char *value, *left, *right; char *value, *left, *right;
size_t valuelen; size_t valuelen;
int modifiers = 0, limit = 0, j; int modifiers = 0, limit = 0, width = 0, j;
struct format_modifier *list, *fm, *cmp = NULL, *search = NULL; struct format_modifier *list, *fm, *cmp = NULL, *search = NULL;
struct format_modifier *sub = NULL; struct format_modifier **sub = NULL;
u_int i, count; u_int i, count, nsub = 0;
/* Make a copy of the key. */ /* Make a copy of the key. */
copy = copy0 = xstrndup(key, keylen); copy = copy0 = xstrndup(key, keylen);
@@ -1588,7 +1803,9 @@ format_replace(struct format_tree *ft, const char *key, size_t keylen,
case 's': case 's':
if (fm->argc < 2) if (fm->argc < 2)
break; break;
sub = fm; sub = xreallocarray (sub, nsub + 1,
sizeof *sub);
sub[nsub++] = fm;
break; break;
case '=': case '=':
if (fm->argc < 1) if (fm->argc < 1)
@@ -1600,6 +1817,14 @@ format_replace(struct format_tree *ft, const char *key, size_t keylen,
if (fm->argc >= 2 && fm->argv[1] != NULL) if (fm->argc >= 2 && fm->argv[1] != NULL)
marker = fm->argv[1]; marker = fm->argv[1];
break; break;
case 'p':
if (fm->argc < 1)
break;
width = strtonum(fm->argv[0], INT_MIN, INT_MAX,
&errptr);
if (errptr != NULL)
width = 0;
break;
case 'l': case 'l':
modifiers |= FORMAT_LITERAL; modifiers |= FORMAT_LITERAL;
break; break;
@@ -1800,10 +2025,10 @@ done:
} }
/* Perform substitution if any. */ /* Perform substitution if any. */
if (sub != NULL) { for (i = 0; i < nsub; i++) {
left = format_expand(ft, sub->argv[0]); left = format_expand(ft, sub[i]->argv[0]);
right = format_expand(ft, sub->argv[1]); right = format_expand(ft, sub[i]->argv[1]);
new = format_sub(sub, value, left, right); new = format_sub(sub[i], value, left, right);
format_log(ft, "substitute '%s' to '%s': %s", left, right, new); format_log(ft, "substitute '%s' to '%s': %s", left, right, new);
free(value); free(value);
value = new; value = new;
@@ -1834,6 +2059,19 @@ done:
format_log(ft, "applied length limit %d: %s", limit, value); format_log(ft, "applied length limit %d: %s", limit, value);
} }
/* Pad the value if needed. */
if (width > 0) {
new = utf8_padcstr(value, width);
free(value);
value = new;
format_log(ft, "applied padding width %d: %s", width, value);
} else if (width < 0) {
new = utf8_rpadcstr(value, -width);
free(value);
value = new;
format_log(ft, "applied padding width %d: %s", width, value);
}
/* Expand the buffer and copy in the value. */ /* Expand the buffer and copy in the value. */
valuelen = strlen(value); valuelen = strlen(value);
while (*len - *off < valuelen + 1) { while (*len - *off < valuelen + 1) {
@@ -1846,12 +2084,15 @@ done:
format_log(ft, "replaced '%s' with '%s'", copy0, value); format_log(ft, "replaced '%s' with '%s'", copy0, value);
free(value); free(value);
free(sub);
format_free_modifiers(list, count); format_free_modifiers(list, count);
free(copy0); free(copy0);
return (0); return (0);
fail: fail:
format_log(ft, "failed %s", copy0); format_log(ft, "failed %s", copy0);
free(sub);
format_free_modifiers(list, count); format_free_modifiers(list, count);
free(copy0); free(copy0);
return (-1); return (-1);
@@ -2091,8 +2332,14 @@ format_defaults_session(struct format_tree *ft, struct session *s)
format_add(ft, "session_group", "%s", sg->name); format_add(ft, "session_group", "%s", sg->name);
format_add(ft, "session_group_size", "%u", format_add(ft, "session_group_size", "%u",
session_group_count (sg)); session_group_count (sg));
format_add(ft, "session_group_attached", "%u",
session_group_attached_count (sg));
format_add(ft, "session_group_many_attached", "%u",
session_group_attached_count (sg) > 1);
format_add_cb(ft, "session_group_list", format_add_cb(ft, "session_group_list",
format_cb_session_group_list); format_cb_session_group_list);
format_add_cb(ft, "session_group_attached_list",
format_cb_session_group_attached_list);
} }
format_add_tv(ft, "session_created", &s->creation_time); format_add_tv(ft, "session_created", &s->creation_time);
@@ -2101,6 +2348,8 @@ format_defaults_session(struct format_tree *ft, struct session *s)
format_add(ft, "session_attached", "%u", s->attached); format_add(ft, "session_attached", "%u", s->attached);
format_add(ft, "session_many_attached", "%d", s->attached > 1); format_add(ft, "session_many_attached", "%d", s->attached > 1);
format_add_cb(ft, "session_attached_list",
format_cb_session_attached_list);
format_add_cb(ft, "session_alerts", format_cb_session_alerts); format_add_cb(ft, "session_alerts", format_cb_session_alerts);
format_add_cb(ft, "session_stack", format_cb_session_stack); format_add_cb(ft, "session_stack", format_cb_session_stack);
@@ -2113,7 +2362,6 @@ format_defaults_client(struct format_tree *ft, struct client *c)
struct session *s; struct session *s;
const char *name; const char *name;
struct tty *tty = &c->tty; struct tty *tty = &c->tty;
const char *types[] = TTY_TYPES;
if (ft->s == NULL) if (ft->s == NULL)
ft->s = c->session; ft->s = c->session;
@@ -2123,14 +2371,14 @@ format_defaults_client(struct format_tree *ft, struct client *c)
format_add(ft, "client_pid", "%ld", (long) c->pid); format_add(ft, "client_pid", "%ld", (long) c->pid);
format_add(ft, "client_height", "%u", tty->sy); format_add(ft, "client_height", "%u", tty->sy);
format_add(ft, "client_width", "%u", tty->sx); format_add(ft, "client_width", "%u", tty->sx);
format_add(ft, "client_cell_width", "%u", tty->xpixel);
format_add(ft, "client_cell_height", "%u", tty->ypixel);
format_add(ft, "client_tty", "%s", c->ttyname); format_add(ft, "client_tty", "%s", c->ttyname);
format_add(ft, "client_control_mode", "%d", format_add(ft, "client_control_mode", "%d",
!!(c->flags & CLIENT_CONTROL)); !!(c->flags & CLIENT_CONTROL));
if (tty->term_name != NULL) if (tty->term_name != NULL)
format_add(ft, "client_termname", "%s", tty->term_name); format_add(ft, "client_termname", "%s", tty->term_name);
if (tty->term_name != NULL)
format_add(ft, "client_termtype", "%s", types[tty->term_type]);
format_add_tv(ft, "client_created", &c->creation_time); format_add_tv(ft, "client_created", &c->creation_time);
format_add_tv(ft, "client_activity", &c->activity_time); format_add_tv(ft, "client_activity", &c->activity_time);
@@ -2174,6 +2422,8 @@ format_defaults_window(struct format_tree *ft, struct window *w)
format_add(ft, "window_name", "%s", w->name); format_add(ft, "window_name", "%s", w->name);
format_add(ft, "window_width", "%u", w->sx); format_add(ft, "window_width", "%u", w->sx);
format_add(ft, "window_height", "%u", w->sy); format_add(ft, "window_height", "%u", w->sy);
format_add(ft, "window_cell_width", "%u", w->xpixel);
format_add(ft, "window_cell_height", "%u", w->ypixel);
format_add_cb(ft, "window_layout", format_cb_window_layout); format_add_cb(ft, "window_layout", format_cb_window_layout);
format_add_cb(ft, "window_visible_layout", format_add_cb(ft, "window_visible_layout",
format_cb_window_visible_layout); format_cb_window_visible_layout);
@@ -2211,12 +2461,25 @@ format_defaults_winlink(struct format_tree *ft, struct winlink *wl)
format_add_cb(ft, "window_stack_index", format_cb_window_stack_index); format_add_cb(ft, "window_stack_index", format_cb_window_stack_index);
format_add(ft, "window_flags", "%s", window_printable_flags(wl)); format_add(ft, "window_flags", "%s", window_printable_flags(wl));
format_add(ft, "window_active", "%d", wl == s->curw); format_add(ft, "window_active", "%d", wl == s->curw);
format_add_cb(ft, "window_active_sessions",
format_cb_window_active_sessions);
format_add_cb(ft, "window_active_sessions_list",
format_cb_window_active_sessions_list);
format_add_cb(ft, "window_active_clients",
format_cb_window_active_clients);
format_add_cb(ft, "window_active_clients_list",
format_cb_window_active_clients_list);
format_add(ft, "window_start_flag", "%d", format_add(ft, "window_start_flag", "%d",
!!(wl == RB_MIN(winlinks, &s->windows))); !!(wl == RB_MIN(winlinks, &s->windows)));
format_add(ft, "window_end_flag", "%d", format_add(ft, "window_end_flag", "%d",
!!(wl == RB_MAX(winlinks, &s->windows))); !!(wl == RB_MAX(winlinks, &s->windows)));
if (server_check_marked() && marked_pane.wl == wl)
format_add(ft, "window_marked_flag", "1");
else
format_add(ft, "window_marked_flag", "0");
format_add(ft, "window_bell_flag", "%d", format_add(ft, "window_bell_flag", "%d",
!!(wl->flags & WINLINK_BELL)); !!(wl->flags & WINLINK_BELL));
format_add(ft, "window_activity_flag", "%d", format_add(ft, "window_activity_flag", "%d",
@@ -2226,6 +2489,11 @@ format_defaults_winlink(struct format_tree *ft, struct winlink *wl)
format_add(ft, "window_last_flag", "%d", format_add(ft, "window_last_flag", "%d",
!!(wl == TAILQ_FIRST(&s->lastw))); !!(wl == TAILQ_FIRST(&s->lastw)));
format_add(ft, "window_linked", "%d", session_is_linked(s, wl->window)); format_add(ft, "window_linked", "%d", session_is_linked(s, wl->window));
format_add_cb(ft, "window_linked_sessions_list",
format_cb_window_linked_sessions_list);
format_add(ft, "window_linked_sessions", "%u",
wl->window->references);
} }
/* Set default format keys for a window pane. */ /* Set default format keys for a window pane. */
@@ -2253,6 +2521,8 @@ format_defaults_pane(struct format_tree *ft, struct window_pane *wp)
format_add(ft, "pane_width", "%u", wp->sx); format_add(ft, "pane_width", "%u", wp->sx);
format_add(ft, "pane_height", "%u", wp->sy); format_add(ft, "pane_height", "%u", wp->sy);
format_add(ft, "pane_title", "%s", wp->base.title); format_add(ft, "pane_title", "%s", wp->base.title);
if (wp->base.path != NULL)
format_add(ft, "pane_path", "%s", wp->base.path);
format_add(ft, "pane_id", "%%%u", wp->id); format_add(ft, "pane_id", "%%%u", wp->id);
format_add(ft, "pane_active", "%d", wp == w->active); format_add(ft, "pane_active", "%d", wp == w->active);
format_add(ft, "pane_input_off", "%d", !!(wp->flags & PANE_INPUTOFF)); format_add(ft, "pane_input_off", "%d", !!(wp->flags & PANE_INPUTOFF));

24
grid.c
View File

@@ -186,23 +186,25 @@ grid_clear_cell(struct grid *gd, u_int px, u_int py, u_int bg)
struct grid_cell *gc; struct grid_cell *gc;
memcpy(gce, &grid_cleared_entry, sizeof *gce); memcpy(gce, &grid_cleared_entry, sizeof *gce);
if (bg & COLOUR_FLAG_RGB) { if (bg != 8) {
grid_get_extended_cell(gl, gce, gce->flags); if (bg & COLOUR_FLAG_RGB) {
gl->flags |= GRID_LINE_EXTENDED; grid_get_extended_cell(gl, gce, gce->flags);
gl->flags |= GRID_LINE_EXTENDED;
gc = &gl->extddata[gce->offset]; gc = &gl->extddata[gce->offset];
memcpy(gc, &grid_cleared_cell, sizeof *gc); memcpy(gc, &grid_cleared_cell, sizeof *gc);
gc->bg = bg; gc->bg = bg;
} else { } else {
if (bg & COLOUR_FLAG_256) if (bg & COLOUR_FLAG_256)
gce->flags |= GRID_FLAG_BG256; gce->flags |= GRID_FLAG_BG256;
gce->data.bg = bg; gce->data.bg = bg;
}
} }
} }
/* Check grid y position. */ /* Check grid y position. */
static int static int
grid_check_y(struct grid *gd, const char* from, u_int py) grid_check_y(struct grid *gd, const char *from, u_int py)
{ {
if (py >= gd->hsize + gd->sy) { if (py >= gd->hsize + gd->sy) {
log_debug("%s: y out of range: %u", from, py); log_debug("%s: y out of range: %u", from, py);

View File

@@ -42,9 +42,6 @@ struct input_key_ent {
}; };
static const struct input_key_ent input_keys[] = { static const struct input_key_ent input_keys[] = {
/* Backspace key. */
{ KEYC_BSPACE, "\177", 0 },
/* Paste keys. */ /* Paste keys. */
{ KEYC_PASTE_START, "\033[200~", 0 }, { KEYC_PASTE_START, "\033[200~", 0 },
{ KEYC_PASTE_END, "\033[201~", 0 }, { KEYC_PASTE_END, "\033[201~", 0 },
@@ -152,14 +149,14 @@ input_split2(u_int c, u_char *dst)
} }
/* Translate a key code into an output key sequence. */ /* Translate a key code into an output key sequence. */
void int
input_key(struct window_pane *wp, key_code key, struct mouse_event *m) input_key(struct window_pane *wp, key_code key, struct mouse_event *m)
{ {
const struct input_key_ent *ike; const struct input_key_ent *ike;
u_int i; u_int i;
size_t dlen; size_t dlen;
char *out; char *out;
key_code justkey; key_code justkey, newkey;
struct utf8_data ud; struct utf8_data ud;
log_debug("writing key 0x%llx (%s) to %%%u", key, log_debug("writing key 0x%llx (%s) to %%%u", key,
@@ -169,14 +166,22 @@ input_key(struct window_pane *wp, key_code key, struct mouse_event *m)
if (KEYC_IS_MOUSE(key)) { if (KEYC_IS_MOUSE(key)) {
if (m != NULL && m->wp != -1 && (u_int)m->wp == wp->id) if (m != NULL && m->wp != -1 && (u_int)m->wp == wp->id)
input_key_mouse(wp, m); input_key_mouse(wp, m);
return; return (0);
} }
/* Literal keys go as themselves (can't be more than eight bits). */ /* Literal keys go as themselves (can't be more than eight bits). */
if (key & KEYC_LITERAL) { if (key & KEYC_LITERAL) {
ud.data[0] = (u_char)key; ud.data[0] = (u_char)key;
bufferevent_write(wp->event, &ud.data[0], 1); bufferevent_write(wp->event, &ud.data[0], 1);
return; return (0);
}
/* Is this backspace? */
if ((key & KEYC_MASK_KEY) == KEYC_BSPACE) {
newkey = options_get_number(global_options, "backspace");
if (newkey >= 0x7f)
newkey = '\177';
key = newkey|(key & KEYC_MASK_MOD);
} }
/* /*
@@ -189,15 +194,15 @@ input_key(struct window_pane *wp, key_code key, struct mouse_event *m)
bufferevent_write(wp->event, "\033", 1); bufferevent_write(wp->event, "\033", 1);
ud.data[0] = justkey; ud.data[0] = justkey;
bufferevent_write(wp->event, &ud.data[0], 1); bufferevent_write(wp->event, &ud.data[0], 1);
return; return (0);
} }
if (justkey > 0x7f && justkey < KEYC_BASE) { if (justkey > 0x7f && justkey < KEYC_BASE) {
if (utf8_split(justkey, &ud) != UTF8_DONE) if (utf8_split(justkey, &ud) != UTF8_DONE)
return; return (-1);
if (key & KEYC_ESCAPE) if (key & KEYC_ESCAPE)
bufferevent_write(wp->event, "\033", 1); bufferevent_write(wp->event, "\033", 1);
bufferevent_write(wp->event, ud.data, ud.size); bufferevent_write(wp->event, ud.data, ud.size);
return; return (0);
} }
/* /*
@@ -208,7 +213,7 @@ input_key(struct window_pane *wp, key_code key, struct mouse_event *m)
if ((out = xterm_keys_lookup(key)) != NULL) { if ((out = xterm_keys_lookup(key)) != NULL) {
bufferevent_write(wp->event, out, strlen(out)); bufferevent_write(wp->event, out, strlen(out));
free(out); free(out);
return; return (0);
} }
} }
key &= ~KEYC_XTERM; key &= ~KEYC_XTERM;
@@ -231,7 +236,7 @@ input_key(struct window_pane *wp, key_code key, struct mouse_event *m)
} }
if (i == nitems(input_keys)) { if (i == nitems(input_keys)) {
log_debug("key 0x%llx missing", key); log_debug("key 0x%llx missing", key);
return; return (-1);
} }
dlen = strlen(ike->data); dlen = strlen(ike->data);
log_debug("found key 0x%llx: \"%s\"", key, ike->data); log_debug("found key 0x%llx: \"%s\"", key, ike->data);
@@ -240,6 +245,7 @@ input_key(struct window_pane *wp, key_code key, struct mouse_event *m)
if (key & KEYC_ESCAPE) if (key & KEYC_ESCAPE)
bufferevent_write(wp->event, "\033", 1); bufferevent_write(wp->event, "\033", 1);
bufferevent_write(wp->event, ike->data, dlen); bufferevent_write(wp->event, ike->data, dlen);
return (0);
} }
/* Translate mouse and output. */ /* Translate mouse and output. */

130
input.c
View File

@@ -20,6 +20,7 @@
#include <netinet/in.h> #include <netinet/in.h>
#include <ctype.h>
#include <resolv.h> #include <resolv.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
@@ -740,7 +741,7 @@ input_timer_callback(__unused int fd, __unused short events, void *arg)
static void static void
input_start_timer(struct input_ctx *ictx) input_start_timer(struct input_ctx *ictx)
{ {
struct timeval tv = { .tv_usec = 100000 }; struct timeval tv = { .tv_sec = 5, .tv_usec = 0 };
event_del(&ictx->timer); event_del(&ictx->timer);
event_add(&ictx->timer, &tv); event_add(&ictx->timer, &tv);
@@ -772,6 +773,7 @@ input_save_state(struct input_ctx *ictx)
ictx->old_mode = s->mode; ictx->old_mode = s->mode;
} }
/* Restore screen state. */
static void static void
input_restore_state(struct input_ctx *ictx) input_restore_state(struct input_ctx *ictx)
{ {
@@ -876,7 +878,7 @@ input_set_state(struct window_pane *wp, const struct input_transition *itr)
void void
input_parse(struct window_pane *wp) input_parse(struct window_pane *wp)
{ {
struct evbuffer *evb = wp->event->input; struct evbuffer *evb = wp->event->input;
input_parse_buffer(wp, EVBUFFER_DATA(evb), EVBUFFER_LENGTH(evb)); input_parse_buffer(wp, EVBUFFER_DATA(evb), EVBUFFER_LENGTH(evb));
evbuffer_drain(evb, EVBUFFER_LENGTH(evb)); evbuffer_drain(evb, EVBUFFER_LENGTH(evb));
@@ -888,7 +890,8 @@ input_parse_buffer(struct window_pane *wp, u_char *buf, size_t len)
{ {
struct input_ctx *ictx = wp->ictx; struct input_ctx *ictx = wp->ictx;
struct screen_write_ctx *sctx = &ictx->ctx; struct screen_write_ctx *sctx = &ictx->ctx;
const struct input_transition *itr; const struct input_state *state = NULL;
const struct input_transition *itr = NULL;
size_t off = 0; size_t off = 0;
if (len == 0) if (len == 0)
@@ -916,16 +919,23 @@ input_parse_buffer(struct window_pane *wp, u_char *buf, size_t len)
ictx->ch = buf[off++]; ictx->ch = buf[off++];
/* Find the transition. */ /* Find the transition. */
itr = ictx->state->transitions; if (ictx->state != state ||
while (itr->first != -1 && itr->last != -1) { itr == NULL ||
if (ictx->ch >= itr->first && ictx->ch <= itr->last) ictx->ch < itr->first ||
break; ictx->ch > itr->last) {
itr++; itr = ictx->state->transitions;
} while (itr->first != -1 && itr->last != -1) {
if (itr->first == -1 || itr->last == -1) { if (ictx->ch >= itr->first &&
/* No transition? Eh? */ ictx->ch <= itr->last)
fatalx("no transition from state"); break;
itr++;
}
if (itr->first == -1 || itr->last == -1) {
/* No transition? Eh? */
fatalx("no transition from state");
}
} }
state = ictx->state;
/* /*
* Any state except print stops the current collection. This is * Any state except print stops the current collection. This is
@@ -1293,6 +1303,7 @@ input_csi_dispatch(struct input_ctx *ictx)
struct input_table_entry *entry; struct input_table_entry *entry;
int i, n, m; int i, n, m;
u_int cx, bg = ictx->cell.cell.bg; u_int cx, bg = ictx->cell.cell.bg;
char *copy, *cp;
if (ictx->flags & INPUT_DISCARD) if (ictx->flags & INPUT_DISCARD)
return (0); return (0);
@@ -1424,6 +1435,13 @@ input_csi_dispatch(struct input_ctx *ictx)
case 6: case 6:
input_reply(ictx, "\033[%u;%uR", s->cy + 1, s->cx + 1); input_reply(ictx, "\033[%u;%uR", s->cy + 1, s->cx + 1);
break; break;
case 1337: /* Terminal version, from iTerm2. */
copy = xstrdup(getversion());
for (cp = copy; *cp != '\0'; cp++)
*cp = toupper((u_char)*cp);
input_reply(ictx, "\033[TMUX %sn", copy);
free(copy);
break;
default: default:
log_debug("%s: unknown '%c'", __func__, ictx->ch); log_debug("%s: unknown '%c'", __func__, ictx->ch);
break; break;
@@ -2195,14 +2213,18 @@ input_exit_osc(struct input_ctx *ictx)
switch (option) { switch (option) {
case 0: case 0:
case 2: case 2:
if (utf8_isvalid(p)) { if (screen_set_title(sctx->s, p))
screen_set_title(sctx->s, p);
server_status_window(ictx->wp->window); server_status_window(ictx->wp->window);
}
break; break;
case 4: case 4:
input_osc_4(ictx, p); input_osc_4(ictx, p);
break; break;
case 7:
if (utf8_isvalid(p)) {
screen_set_path(sctx->s, p);
server_status_window(ictx->wp->window);
}
break;
case 10: case 10:
input_osc_10(ictx, p); input_osc_10(ictx, p);
break; break;
@@ -2250,10 +2272,8 @@ input_exit_apc(struct input_ctx *ictx)
return; return;
log_debug("%s: \"%s\"", __func__, ictx->input_buf); log_debug("%s: \"%s\"", __func__, ictx->input_buf);
if (!utf8_isvalid(ictx->input_buf)) if (screen_set_title(sctx->s, ictx->input_buf))
return; server_status_window(ictx->wp->window);
screen_set_title(sctx->s, ictx->input_buf);
server_status_window(ictx->wp->window);
} }
/* Rename string started. */ /* Rename string started. */
@@ -2271,6 +2291,9 @@ input_enter_rename(struct input_ctx *ictx)
static void static void
input_exit_rename(struct input_ctx *ictx) input_exit_rename(struct input_ctx *ictx)
{ {
struct window_pane *wp = ictx->wp;
struct options_entry *oe;
if (ictx->flags & INPUT_DISCARD) if (ictx->flags & INPUT_DISCARD)
return; return;
if (!options_get_number(ictx->wp->options, "allow-rename")) if (!options_get_number(ictx->wp->options, "allow-rename"))
@@ -2279,6 +2302,13 @@ input_exit_rename(struct input_ctx *ictx)
if (!utf8_isvalid(ictx->input_buf)) if (!utf8_isvalid(ictx->input_buf))
return; return;
if (ictx->input_len == 0) {
oe = options_get(wp->window->options, "automatic-rename");
if (oe != NULL)
options_remove(oe);
return;
}
window_set_name(ictx->wp->window, ictx->input_buf); window_set_name(ictx->wp->window, ictx->input_buf);
options_set_number(ictx->wp->window->options, "automatic-rename", 0); options_set_number(ictx->wp->window->options, "automatic-rename", 0);
server_status_window(ictx->wp->window); server_status_window(ictx->wp->window);
@@ -2320,6 +2350,54 @@ input_top_bit_set(struct input_ctx *ictx)
return (0); return (0);
} }
/* Parse colour from OSC. */
static int
input_osc_parse_colour(const char *p, u_int *r, u_int *g, u_int *b)
{
u_int rsize, gsize, bsize;
const char *cp, *s = p;
if (sscanf(p, "rgb:%x/%x/%x", r, g, b) != 3)
return (0);
p += 4;
cp = strchr(p, '/');
rsize = cp - p;
if (rsize == 1)
(*r) = (*r) | ((*r) << 4);
else if (rsize == 3)
(*r) >>= 4;
else if (rsize == 4)
(*r) >>= 8;
else if (rsize != 2)
return (0);
p = cp + 1;
cp = strchr(p, '/');
gsize = cp - p;
if (gsize == 1)
(*g) = (*g) | ((*g) << 4);
else if (gsize == 3)
(*g) >>= 4;
else if (gsize == 4)
(*g) >>= 8;
else if (gsize != 2)
return (0);
bsize = strlen(cp + 1);
if (bsize == 1)
(*b) = (*b) | ((*b) << 4);
else if (bsize == 3)
(*b) >>= 4;
else if (bsize == 4)
(*b) >>= 8;
else if (bsize != 2)
return (0);
log_debug("%s: %s = %02x%02x%02x", __func__, s, *r, *g, *b);
return (1);
}
/* Handle the OSC 4 sequence for setting (multiple) palette entries. */ /* Handle the OSC 4 sequence for setting (multiple) palette entries. */
static void static void
input_osc_4(struct input_ctx *ictx, const char *p) input_osc_4(struct input_ctx *ictx, const char *p)
@@ -2338,7 +2416,7 @@ input_osc_4(struct input_ctx *ictx, const char *p)
goto bad; goto bad;
s = strsep(&next, ";"); s = strsep(&next, ";");
if (sscanf(s, "rgb:%2x/%2x/%2x", &r, &g, &b) != 3) { if (!input_osc_parse_colour(s, &r, &g, &b)) {
s = next; s = next;
continue; continue;
} }
@@ -2363,8 +2441,11 @@ input_osc_10(struct input_ctx *ictx, const char *p)
u_int r, g, b; u_int r, g, b;
char tmp[16]; char tmp[16];
if (sscanf(p, "rgb:%2x/%2x/%2x", &r, &g, &b) != 3) if (strcmp(p, "?") == 0)
goto bad; return;
if (!input_osc_parse_colour(p, &r, &g, &b))
goto bad;
xsnprintf(tmp, sizeof tmp, "fg=#%02x%02x%02x", r, g, b); xsnprintf(tmp, sizeof tmp, "fg=#%02x%02x%02x", r, g, b);
options_set_style(wp->options, "window-style", 1, tmp); options_set_style(wp->options, "window-style", 1, tmp);
options_set_style(wp->options, "window-active-style", 1, tmp); options_set_style(wp->options, "window-active-style", 1, tmp);
@@ -2384,7 +2465,10 @@ input_osc_11(struct input_ctx *ictx, const char *p)
u_int r, g, b; u_int r, g, b;
char tmp[16]; char tmp[16];
if (sscanf(p, "rgb:%2x/%2x/%2x", &r, &g, &b) != 3) if (strcmp(p, "?") == 0)
return;
if (!input_osc_parse_colour(p, &r, &g, &b))
goto bad; goto bad;
xsnprintf(tmp, sizeof tmp, "bg=#%02x%02x%02x", r, g, b); xsnprintf(tmp, sizeof tmp, "bg=#%02x%02x%02x", r, g, b);
options_set_style(wp->options, "window-style", 1, tmp); options_set_style(wp->options, "window-style", 1, tmp);

View File

@@ -24,12 +24,6 @@
#include "tmux.h" #include "tmux.h"
#define DEFAULT_CLIENT_MENU \
" 'Detach' 'd' {detach-client}" \
" 'Detach & Kill' 'X' {detach-client -P}" \
" 'Detach Others' 'o' {detach-client -a}" \
" ''" \
" 'Lock' 'l' {lock-client}"
#define DEFAULT_SESSION_MENU \ #define DEFAULT_SESSION_MENU \
" 'Next' 'n' {switch-client -n}" \ " 'Next' 'n' {switch-client -n}" \
" 'Previous' 'p' {switch-client -p}" \ " 'Previous' 'p' {switch-client -p}" \
@@ -69,7 +63,6 @@
" '#{?pane_marked,Unmark,Mark}' 'm' {select-pane -m}" \ " '#{?pane_marked,Unmark,Mark}' 'm' {select-pane -m}" \
" '#{?window_zoomed_flag,Unzoom,Zoom}' 'z' {resize-pane -Z}" " '#{?window_zoomed_flag,Unzoom,Zoom}' 'z' {resize-pane -Z}"
static int key_bindings_cmp(struct key_binding *, struct key_binding *); static int key_bindings_cmp(struct key_binding *, struct key_binding *);
RB_GENERATE_STATIC(key_bindings, key_binding, entry, key_bindings_cmp); RB_GENERATE_STATIC(key_bindings, key_binding, entry, key_bindings_cmp);
static int key_table_cmp(struct key_table *, struct key_table *); static int key_table_cmp(struct key_table *, struct key_table *);
@@ -92,6 +85,15 @@ key_bindings_cmp(struct key_binding *bd1, struct key_binding *bd2)
return (0); return (0);
} }
static void
key_bindings_free(struct key_table *table, struct key_binding *bd)
{
RB_REMOVE(key_bindings, &table->key_bindings, bd);
cmd_list_free(bd->cmdlist);
free((void *)bd->note);
free(bd);
}
struct key_table * struct key_table *
key_bindings_get_table(const char *name, int create) key_bindings_get_table(const char *name, int create)
{ {
@@ -133,11 +135,8 @@ key_bindings_unref_table(struct key_table *table)
if (--table->references != 0) if (--table->references != 0)
return; return;
RB_FOREACH_SAFE(bd, key_bindings, &table->key_bindings, bd1) { RB_FOREACH_SAFE(bd, key_bindings, &table->key_bindings, bd1)
RB_REMOVE(key_bindings, &table->key_bindings, bd); key_bindings_free(table, bd);
cmd_list_free(bd->cmdlist);
free(bd);
}
free((void *)table->name); free((void *)table->name);
free(table); free(table);
@@ -165,24 +164,22 @@ key_bindings_next(__unused struct key_table *table, struct key_binding *bd)
} }
void void
key_bindings_add(const char *name, key_code key, int repeat, key_bindings_add(const char *name, key_code key, const char *note, int repeat,
struct cmd_list *cmdlist) struct cmd_list *cmdlist)
{ {
struct key_table *table; struct key_table *table;
struct key_binding bd_find, *bd; struct key_binding *bd;
table = key_bindings_get_table(name, 1); table = key_bindings_get_table(name, 1);
bd_find.key = (key & ~KEYC_XTERM); bd = key_bindings_get(table, key & ~KEYC_XTERM);
bd = RB_FIND(key_bindings, &table->key_bindings, &bd_find); if (bd != NULL)
if (bd != NULL) { key_bindings_free(table, bd);
RB_REMOVE(key_bindings, &table->key_bindings, bd);
cmd_list_free(bd->cmdlist);
free(bd);
}
bd = xcalloc(1, sizeof *bd); bd = xcalloc(1, sizeof *bd);
bd->key = key; bd->key = key;
if (note != NULL)
bd->note = xstrdup(note);
RB_INSERT(key_bindings, &table->key_bindings, bd); RB_INSERT(key_bindings, &table->key_bindings, bd);
if (repeat) if (repeat)
@@ -194,20 +191,16 @@ void
key_bindings_remove(const char *name, key_code key) key_bindings_remove(const char *name, key_code key)
{ {
struct key_table *table; struct key_table *table;
struct key_binding bd_find, *bd; struct key_binding *bd;
table = key_bindings_get_table(name, 0); table = key_bindings_get_table(name, 0);
if (table == NULL) if (table == NULL)
return; return;
bd_find.key = (key & ~KEYC_XTERM); bd = key_bindings_get(table, key & ~KEYC_XTERM);
bd = RB_FIND(key_bindings, &table->key_bindings, &bd_find);
if (bd == NULL) if (bd == NULL)
return; return;
key_bindings_free(table, bd);
RB_REMOVE(key_bindings, &table->key_bindings, bd);
cmd_list_free(bd->cmdlist);
free(bd);
if (RB_EMPTY(&table->key_bindings)) { if (RB_EMPTY(&table->key_bindings)) {
RB_REMOVE(key_tables, &key_tables, table); RB_REMOVE(key_tables, &key_tables, table);
@@ -236,87 +229,88 @@ void
key_bindings_init(void) key_bindings_init(void)
{ {
static const char *defaults[] = { static const char *defaults[] = {
"bind C-b send-prefix", "bind -N 'Send the prefix key' C-b send-prefix",
"bind C-o rotate-window", "bind -N 'Rotate through the panes' C-o rotate-window",
"bind C-z suspend-client", "bind -N 'Suspend the current client' C-z suspend-client",
"bind Space next-layout", "bind -N 'Select next layout' Space next-layout",
"bind ! break-pane", "bind -N 'Break pane to a new window' ! break-pane",
"bind '\"' split-window", "bind -N 'Split window vertically' '\"' split-window",
"bind '#' list-buffers", "bind -N 'List all paste buffers' '#' list-buffers",
"bind '$' command-prompt -I'#S' \"rename-session -- '%%'\"", "bind -N 'Rename current session' '$' command-prompt -I'#S' \"rename-session -- '%%'\"",
"bind % split-window -h", "bind -N 'Split window horizontally' % split-window -h",
"bind & confirm-before -p\"kill-window #W? (y/n)\" kill-window", "bind -N 'Kill current window' & confirm-before -p\"kill-window #W? (y/n)\" kill-window",
"bind \"'\" command-prompt -pindex \"select-window -t ':%%'\"", "bind -N 'Prompt for window index to select' \"'\" command-prompt -pindex \"select-window -t ':%%'\"",
"bind ( switch-client -p", "bind -N 'Switch to previous client' ( switch-client -p",
"bind ) switch-client -n", "bind -N 'Switch to next client' ) switch-client -n",
"bind , command-prompt -I'#W' \"rename-window -- '%%'\"", "bind -N 'Rename current window' , command-prompt -I'#W' \"rename-window -- '%%'\"",
"bind - delete-buffer", "bind -N 'Delete the most recent paste buffer' - delete-buffer",
"bind . command-prompt \"move-window -t '%%'\"", "bind -N 'Move the current window' . command-prompt \"move-window -t '%%'\"",
"bind 0 select-window -t:=0", "bind -N 'Describe key binding' '/' command-prompt -kpkey 'list-keys -1N \"%%%\"'",
"bind 1 select-window -t:=1", "bind -N 'Select window 0' 0 select-window -t:=0",
"bind 2 select-window -t:=2", "bind -N 'Select window 1' 1 select-window -t:=1",
"bind 3 select-window -t:=3", "bind -N 'Select window 2' 2 select-window -t:=2",
"bind 4 select-window -t:=4", "bind -N 'Select window 3' 3 select-window -t:=3",
"bind 5 select-window -t:=5", "bind -N 'Select window 4' 4 select-window -t:=4",
"bind 6 select-window -t:=6", "bind -N 'Select window 5' 5 select-window -t:=5",
"bind 7 select-window -t:=7", "bind -N 'Select window 6' 6 select-window -t:=6",
"bind 8 select-window -t:=8", "bind -N 'Select window 7' 7 select-window -t:=7",
"bind 9 select-window -t:=9", "bind -N 'Select window 8' 8 select-window -t:=8",
"bind : command-prompt", "bind -N 'Select window 9' 9 select-window -t:=9",
"bind \\; last-pane", "bind -N 'Prompt for a command' : command-prompt",
"bind = choose-buffer -Z", "bind -N 'Move to the previously active pane' \\; last-pane",
"bind ? list-keys", "bind -N 'Choose a paste buffer from a list' = choose-buffer -Z",
"bind D choose-client -Z", "bind -N 'List key bindings' ? list-keys -N",
"bind E select-layout -E", "bind -N 'Choose a client from a list' D choose-client -Z",
"bind L switch-client -l", "bind -N 'Spread panes out evenly' E select-layout -E",
"bind M select-pane -M", "bind -N 'Switch to the last client' L switch-client -l",
"bind [ copy-mode", "bind -N 'Clear the marked pane' M select-pane -M",
"bind ] paste-buffer", "bind -N 'Enter copy mode' [ copy-mode",
"bind c new-window", "bind -N 'Paste the most recent paste buffer' ] paste-buffer",
"bind d detach-client", "bind -N 'Create a new window' c new-window",
"bind f command-prompt \"find-window -Z -- '%%'\"", "bind -N 'Detach the current client' d detach-client",
"bind i display-message", "bind -N 'Search for a pane' f command-prompt \"find-window -Z -- '%%'\"",
"bind l last-window", "bind -N 'Display window information' i display-message",
"bind m select-pane -m", "bind -N 'Select the previously current window' l last-window",
"bind n next-window", "bind -N 'Toggle the marked pane' m select-pane -m",
"bind o select-pane -t:.+", "bind -N 'Select the next window' n next-window",
"bind p previous-window", "bind -N 'Select the next pane' o select-pane -t:.+",
"bind q display-panes", "bind -N 'Select the previous pane' p previous-window",
"bind r refresh-client", "bind -N 'Display pane numbers' q display-panes",
"bind s choose-tree -Zs", "bind -N 'Redraw the current client' r refresh-client",
"bind t clock-mode", "bind -N 'Choose a session from a list' s choose-tree -Zs",
"bind w choose-tree -Zw", "bind -N 'Show a clock' t clock-mode",
"bind x confirm-before -p\"kill-pane #P? (y/n)\" kill-pane", "bind -N 'Choose a window from a list' w choose-tree -Zw",
"bind z resize-pane -Z", "bind -N 'Kill the active pane' x confirm-before -p\"kill-pane #P? (y/n)\" kill-pane",
"bind '{' swap-pane -U", "bind -N 'Zoom the active pane' z resize-pane -Z",
"bind '}' swap-pane -D", "bind -N 'Swap the active pane with the pane above' '{' swap-pane -U",
"bind '~' show-messages", "bind -N 'Swap the active pane with the pane below' '}' swap-pane -D",
"bind PPage copy-mode -u", "bind -N 'Show messages' '~' show-messages",
"bind -r Up select-pane -U", "bind -N 'Enter copy mode and scroll up' PPage copy-mode -u",
"bind -r Down select-pane -D", "bind -N 'Select the pane above the active pane' -r Up select-pane -U",
"bind -r Left select-pane -L", "bind -N 'Select the pane below the active pane' -r Down select-pane -D",
"bind -r Right select-pane -R", "bind -N 'Select the pane to the left of the active pane' -r Left select-pane -L",
"bind M-1 select-layout even-horizontal", "bind -N 'Select the pane to the right of the active pane' -r Right select-pane -R",
"bind M-2 select-layout even-vertical", "bind -N 'Set the even-horizontal layout' M-1 select-layout even-horizontal",
"bind M-3 select-layout main-horizontal", "bind -N 'Set the even-vertical layout' M-2 select-layout even-vertical",
"bind M-4 select-layout main-vertical", "bind -N 'Set the main-horizontal layout' M-3 select-layout main-horizontal",
"bind M-5 select-layout tiled", "bind -N 'Set the main-vertical layout' M-4 select-layout main-vertical",
"bind M-n next-window -a", "bind -N 'Select the tiled layout' M-5 select-layout tiled",
"bind M-o rotate-window -D", "bind -N 'Select the next window with an alert' M-n next-window -a",
"bind M-p previous-window -a", "bind -N 'Rotate through the panes in reverse' M-o rotate-window -D",
"bind -r S-Up refresh-client -U 10", "bind -N 'Select the previous window with an alert' M-p previous-window -a",
"bind -r S-Down refresh-client -D 10", "bind -N 'Move the visible part of the window up' -r S-Up refresh-client -U 10",
"bind -r S-Left refresh-client -L 10", "bind -N 'Move the visible part of the window down' -r S-Down refresh-client -D 10",
"bind -r S-Right refresh-client -R 10", "bind -N 'Move the visible part of the window left' -r S-Left refresh-client -L 10",
"bind -r DC refresh-client -c", "bind -N 'Move the visible part of the window right' -r S-Right refresh-client -R 10",
"bind -r M-Up resize-pane -U 5", "bind -N 'Reset so the visible part of the window follows the cursor' -r DC refresh-client -c",
"bind -r M-Down resize-pane -D 5", "bind -N 'Resize the pane up by 5' -r M-Up resize-pane -U 5",
"bind -r M-Left resize-pane -L 5", "bind -N 'Resize the pane down by 5' -r M-Down resize-pane -D 5",
"bind -r M-Right resize-pane -R 5", "bind -N 'Resize the pane left by 5' -r M-Left resize-pane -L 5",
"bind -r C-Up resize-pane -U", "bind -N 'Resize the pane right by 5' -r M-Right resize-pane -R 5",
"bind -r C-Down resize-pane -D", "bind -N 'Resize the pane up' -r C-Up resize-pane -U",
"bind -r C-Left resize-pane -L", "bind -N 'Resize the pane down' -r C-Down resize-pane -D",
"bind -r C-Right resize-pane -R", "bind -N 'Resize the pane left' -r C-Left resize-pane -L",
"bind -N 'Resize the pane right' -r C-Right resize-pane -R",
"bind -n MouseDown1Pane select-pane -t=\\; send-keys -M", "bind -n MouseDown1Pane select-pane -t=\\; send-keys -M",
"bind -n MouseDrag1Border resize-pane -M", "bind -n MouseDrag1Border resize-pane -M",
@@ -326,7 +320,6 @@ key_bindings_init(void)
"bind -n MouseDrag1Pane if -Ft= '#{mouse_any_flag}' 'if -Ft= \"#{pane_in_mode}\" \"copy-mode -M\" \"send-keys -M\"' 'copy-mode -M'", "bind -n MouseDrag1Pane if -Ft= '#{mouse_any_flag}' 'if -Ft= \"#{pane_in_mode}\" \"copy-mode -M\" \"send-keys -M\"' 'copy-mode -M'",
"bind -n WheelUpPane if -Ft= '#{mouse_any_flag}' 'send-keys -M' 'if -Ft= \"#{pane_in_mode}\" \"send-keys -M\" \"copy-mode -et=\"'", "bind -n WheelUpPane if -Ft= '#{mouse_any_flag}' 'send-keys -M' 'if -Ft= \"#{pane_in_mode}\" \"send-keys -M\" \"copy-mode -et=\"'",
"bind -n MouseDown3StatusRight display-menu -t= -xM -yS -T \"#[align=centre]#{client_name}\" " DEFAULT_CLIENT_MENU,
"bind -n MouseDown3StatusLeft display-menu -t= -xM -yS -T \"#[align=centre]#{session_name}\" " DEFAULT_SESSION_MENU, "bind -n MouseDown3StatusLeft display-menu -t= -xM -yS -T \"#[align=centre]#{session_name}\" " DEFAULT_SESSION_MENU,
"bind -n MouseDown3Status display-menu -t= -xW -yS -T \"#[align=centre]#{window_index}:#{window_name}\" " DEFAULT_WINDOW_MENU, "bind -n MouseDown3Status display-menu -t= -xW -yS -T \"#[align=centre]#{window_index}:#{window_name}\" " DEFAULT_WINDOW_MENU,
"bind < display-menu -xW -yS -T \"#[align=centre]#{window_index}:#{window_name}\" " DEFAULT_WINDOW_MENU, "bind < display-menu -xW -yS -T \"#[align=centre]#{window_index}:#{window_name}\" " DEFAULT_WINDOW_MENU,
@@ -403,6 +396,8 @@ key_bindings_init(void)
"bind -Tcopy-mode C-Up send -X scroll-up", "bind -Tcopy-mode C-Up send -X scroll-up",
"bind -Tcopy-mode C-Down send -X scroll-down", "bind -Tcopy-mode C-Down send -X scroll-down",
"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-c send -X cancel",
"bind -Tcopy-mode-vi C-d send -X halfpage-down", "bind -Tcopy-mode-vi C-d send -X halfpage-down",
"bind -Tcopy-mode-vi C-e send -X scroll-down", "bind -Tcopy-mode-vi C-e send -X scroll-down",
@@ -509,12 +504,16 @@ key_bindings_dispatch(struct key_binding *bd, struct cmdq_item *item,
struct cmdq_item *new_item; struct cmdq_item *new_item;
int readonly; int readonly;
readonly = 1; if (c == NULL || (~c->flags & CLIENT_READONLY))
TAILQ_FOREACH(cmd, &bd->cmdlist->list, qentry) { readonly = 1;
if (!(cmd->entry->flags & CMD_READONLY)) else {
readonly = 0; readonly = 1;
TAILQ_FOREACH(cmd, &bd->cmdlist->list, qentry) {
if (~cmd->entry->flags & CMD_READONLY)
readonly = 0;
}
} }
if (!readonly && (c->flags & CLIENT_READONLY)) if (!readonly)
new_item = cmdq_get_callback(key_bindings_read_only, NULL); new_item = cmdq_get_callback(key_bindings_read_only, NULL);
else { else {
new_item = cmdq_get_command(bd->cmdlist, fs, m, 0); new_item = cmdq_get_command(bd->cmdlist, fs, m, 0);
@@ -522,8 +521,8 @@ key_bindings_dispatch(struct key_binding *bd, struct cmdq_item *item,
new_item->shared->flags |= CMDQ_SHARED_REPEAT; new_item->shared->flags |= CMDQ_SHARED_REPEAT;
} }
if (item != NULL) if (item != NULL)
cmdq_insert_after(item, new_item); new_item = cmdq_insert_after(item, new_item);
else else
cmdq_append(c, new_item); new_item = cmdq_append(c, new_item);
return (new_item); return (new_item);
} }

View File

@@ -159,7 +159,7 @@ key_string_get_modifiers(const char **string)
key_code key_code
key_string_lookup_string(const char *string) key_string_lookup_string(const char *string)
{ {
static const char *other = "!#()+,-.0123456789:;<=>?'\r\t"; static const char *other = "!#()+,-.0123456789:;<=>'\r\t";
key_code key; key_code key;
u_int u; u_int u;
key_code modifiers; key_code modifiers;
@@ -196,7 +196,7 @@ key_string_lookup_string(const char *string)
/* Is this a standard ASCII key? */ /* Is this a standard ASCII key? */
if (string[1] == '\0' && (u_char)string[0] <= 127) { if (string[1] == '\0' && (u_char)string[0] <= 127) {
key = (u_char)string[0]; key = (u_char)string[0];
if (key < 32 || key == 127) if (key < 32)
return (KEYC_UNKNOWN); return (KEYC_UNKNOWN);
} else { } else {
/* Try as a UTF-8 key. */ /* Try as a UTF-8 key. */
@@ -226,6 +226,8 @@ key_string_lookup_string(const char *string)
key -= 64; key -= 64;
else if (key == 32) else if (key == 32)
key = 0; key = 0;
else if (key == '?')
key = 127;
else if (key == 63) else if (key == 63)
key = KEYC_BSPACE; key = KEYC_BSPACE;
else else
@@ -329,7 +331,7 @@ key_string_lookup_key(key_code key)
} }
/* Invalid keys are errors. */ /* Invalid keys are errors. */
if (key == 127 || key > 255) { if (key > 255) {
snprintf(out, sizeof out, "Invalid#%llx", key); snprintf(out, sizeof out, "Invalid#%llx", key);
return (out); return (out);
} }
@@ -343,7 +345,9 @@ key_string_lookup_key(key_code key)
} else if (key >= 32 && key <= 126) { } else if (key >= 32 && key <= 126) {
tmp[0] = key; tmp[0] = key;
tmp[1] = '\0'; tmp[1] = '\0';
} else if (key >= 128) } else if (key == 127)
xsnprintf(tmp, sizeof tmp, "C-?");
else if (key >= 128)
xsnprintf(tmp, sizeof tmp, "\\%llo", key); xsnprintf(tmp, sizeof tmp, "\\%llo", key);
strlcat(out, tmp, sizeof out); strlcat(out, tmp, sizeof out);

View File

@@ -221,7 +221,7 @@ layout_parse(struct window *w, const char *layout)
return (-1); return (-1);
/* Resize to the layout size. */ /* Resize to the layout size. */
window_resize(w, lc->sx, lc->sy); window_resize(w, lc->sx, lc->sy, -1, -1);
/* Destroy the old layout and swap to the new. */ /* Destroy the old layout and swap to the new. */
layout_free_cell(w->layout_root); layout_free_cell(w->layout_root);

View File

@@ -163,7 +163,7 @@ layout_set_even(struct window *w, enum layout_type type)
layout_print_cell(w->layout_root, __func__, 1); layout_print_cell(w->layout_root, __func__, 1);
window_resize(w, lc->sx, lc->sy); window_resize(w, lc->sx, lc->sy, -1, -1);
notify_window("window-layout-changed", w); notify_window("window-layout-changed", w);
server_redraw_window(w); server_redraw_window(w);
} }
@@ -262,7 +262,7 @@ layout_set_main_h(struct window *w)
layout_print_cell(w->layout_root, __func__, 1); layout_print_cell(w->layout_root, __func__, 1);
window_resize(w, lc->sx, lc->sy); window_resize(w, lc->sx, lc->sy, -1, -1);
notify_window("window-layout-changed", w); notify_window("window-layout-changed", w);
server_redraw_window(w); server_redraw_window(w);
} }
@@ -349,7 +349,7 @@ layout_set_main_v(struct window *w)
layout_print_cell(w->layout_root, __func__, 1); layout_print_cell(w->layout_root, __func__, 1);
window_resize(w, lc->sx, lc->sy); window_resize(w, lc->sx, lc->sy, -1, -1);
notify_window("window-layout-changed", w); notify_window("window-layout-changed", w);
server_redraw_window(w); server_redraw_window(w);
} }
@@ -458,7 +458,7 @@ layout_set_tiled(struct window *w)
layout_print_cell(w->layout_root, __func__, 1); layout_print_cell(w->layout_root, __func__, 1);
window_resize(w, lc->sx, lc->sy); window_resize(w, lc->sx, lc->sy, -1, -1);
notify_window("window-layout-changed", w); notify_window("window-layout-changed", w);
server_redraw_window(w); server_redraw_window(w);
} }

3
log.c
View File

@@ -130,6 +130,9 @@ log_debug(const char *msg, ...)
{ {
va_list ap; va_list ap;
if (log_file == NULL)
return;
va_start(ap, msg); va_start(ap, msg);
log_vwrite(msg, ap); log_vwrite(msg, ap);
va_end(ap); va_end(ap);

View File

@@ -39,7 +39,7 @@ struct mode_tree_data {
const char **sort_list; const char **sort_list;
u_int sort_size; u_int sort_size;
u_int sort_type; struct mode_tree_sort_criteria sort_crit;
mode_tree_build_cb buildcb; mode_tree_build_cb buildcb;
mode_tree_draw_cb drawcb; mode_tree_draw_cb drawcb;
@@ -334,7 +334,6 @@ mode_tree_start(struct window_pane *wp, struct args *args,
mtd->sort_list = sort_list; mtd->sort_list = sort_list;
mtd->sort_size = sort_size; mtd->sort_size = sort_size;
mtd->sort_type = 0;
mtd->preview = !args_has(args, 'N'); mtd->preview = !args_has(args, 'N');
@@ -342,9 +341,10 @@ mode_tree_start(struct window_pane *wp, struct args *args,
if (sort != NULL) { if (sort != NULL) {
for (i = 0; i < sort_size; i++) { for (i = 0; i < sort_size; i++) {
if (strcasecmp(sort, sort_list[i]) == 0) if (strcasecmp(sort, sort_list[i]) == 0)
mtd->sort_type = i; mtd->sort_crit.field = i;
} }
} }
mtd->sort_crit.reversed = args_has(args, 'r');
if (args_has(args, 'f')) if (args_has(args, 'f'))
mtd->filter = xstrdup(args_get(args, 'f')); mtd->filter = xstrdup(args_get(args, 'f'));
@@ -392,10 +392,10 @@ mode_tree_build(struct mode_tree_data *mtd)
TAILQ_CONCAT(&mtd->saved, &mtd->children, entry); TAILQ_CONCAT(&mtd->saved, &mtd->children, entry);
TAILQ_INIT(&mtd->children); TAILQ_INIT(&mtd->children);
mtd->buildcb(mtd->modedata, mtd->sort_type, &tag, mtd->filter); mtd->buildcb(mtd->modedata, &mtd->sort_crit, &tag, mtd->filter);
mtd->no_matches = TAILQ_EMPTY(&mtd->children); mtd->no_matches = TAILQ_EMPTY(&mtd->children);
if (mtd->no_matches) if (mtd->no_matches)
mtd->buildcb(mtd->modedata, mtd->sort_type, &tag, NULL); mtd->buildcb(mtd->modedata, &mtd->sort_crit, &tag, NULL);
mode_tree_free_items(&mtd->saved); mode_tree_free_items(&mtd->saved);
TAILQ_INIT(&mtd->saved); TAILQ_INIT(&mtd->saved);
@@ -598,6 +598,8 @@ mode_tree_draw(struct mode_tree_data *mtd)
xasprintf(&text, "%-*s%s%s%s: ", keylen, key, start, mti->name, xasprintf(&text, "%-*s%s%s%s: ", keylen, key, start, mti->name,
tag); tag);
width = utf8_cstrwidth(text); width = utf8_cstrwidth(text);
if (width > w)
width = w;
free(start); free(start);
if (mti->tagged) { if (mti->tagged) {
@@ -607,11 +609,11 @@ mode_tree_draw(struct mode_tree_data *mtd)
if (i != mtd->current) { if (i != mtd->current) {
screen_write_clearendofline(&ctx, 8); screen_write_clearendofline(&ctx, 8);
screen_write_puts(&ctx, &gc0, "%s", text); screen_write_nputs(&ctx, w, &gc0, "%s", text);
format_draw(&ctx, &gc0, w - width, mti->text, NULL); format_draw(&ctx, &gc0, w - width, mti->text, NULL);
} else { } else {
screen_write_clearendofline(&ctx, gc.bg); screen_write_clearendofline(&ctx, gc.bg);
screen_write_puts(&ctx, &gc, "%s", text); screen_write_nputs(&ctx, w, &gc, "%s", text);
format_draw(&ctx, &gc, w - width, mti->text, NULL); format_draw(&ctx, &gc, w - width, mti->text, NULL);
} }
free(text); free(text);
@@ -634,8 +636,9 @@ mode_tree_draw(struct mode_tree_data *mtd)
screen_write_cursormove(&ctx, 0, h, 0); screen_write_cursormove(&ctx, 0, h, 0);
screen_write_box(&ctx, w, sy - h); screen_write_box(&ctx, w, sy - h);
xasprintf(&text, " %s (sort: %s)", mti->name, xasprintf(&text, " %s (sort: %s%s)", mti->name,
mtd->sort_list[mtd->sort_type]); mtd->sort_list[mtd->sort_crit.field],
mtd->sort_crit.reversed ? ", reversed" : "");
if (w - 2 >= strlen(text)) { if (w - 2 >= strlen(text)) {
screen_write_cursormove(&ctx, 1, h, 0); screen_write_cursormove(&ctx, 1, h, 0);
screen_write_puts(&ctx, &gc0, "%s", text); screen_write_puts(&ctx, &gc0, "%s", text);
@@ -993,9 +996,13 @@ mode_tree_key(struct mode_tree_data *mtd, struct client *c, key_code *key,
} }
break; break;
case 'O': case 'O':
mtd->sort_type++; mtd->sort_crit.field++;
if (mtd->sort_type == mtd->sort_size) if (mtd->sort_crit.field == mtd->sort_size)
mtd->sort_type = 0; mtd->sort_crit.field = 0;
mode_tree_build(mtd);
break;
case 'r':
mtd->sort_crit.reversed = !mtd->sort_crit.reversed;
mode_tree_build(mtd); mode_tree_build(mtd);
break; break;
case KEYC_LEFT: case KEYC_LEFT:

View File

@@ -89,9 +89,7 @@ notify_insert_hook(struct cmdq_item *item, struct notify_entry *ne)
new_item = cmdq_get_command(cmdlist, &fs, NULL, CMDQ_NOHOOKS); new_item = cmdq_get_command(cmdlist, &fs, NULL, CMDQ_NOHOOKS);
cmdq_format(new_item, "hook", "%s", ne->name); cmdq_format(new_item, "hook", "%s", ne->name);
notify_hook_formats(new_item, s, w, ne->pane); notify_hook_formats(new_item, s, w, ne->pane);
item = cmdq_insert_after(item, new_item);
cmdq_insert_after(item, new_item);
item = new_item;
a = options_array_next(a); a = options_array_next(a);
} }

View File

@@ -63,13 +63,16 @@ static const char *options_table_set_clipboard_list[] = {
"off", "external", "on", NULL "off", "external", "on", NULL
}; };
static const char *options_table_window_size_list[] = { static const char *options_table_window_size_list[] = {
"largest", "smallest", "manual", NULL "largest", "smallest", "manual", "latest", NULL
}; };
/* Status line format. */ /* Status line format. */
#define OPTIONS_TABLE_STATUS_FORMAT1 \ #define OPTIONS_TABLE_STATUS_FORMAT1 \
"#[align=left range=left #{status-left-style}]" \ "#[align=left range=left #{status-left-style}]" \
"#{T;=/#{status-left-length}:status-left}#[norange default]" \ "#[push-default]" \
"#{T;=/#{status-left-length}:status-left}" \
"#[pop-default]" \
"#[norange default]" \
"#[list=on align=#{status-justify}]" \ "#[list=on align=#{status-justify}]" \
"#[list=left-marker]<#[list=right-marker]>#[list=on]" \ "#[list=left-marker]<#[list=right-marker]>#[list=on]" \
"#{W:" \ "#{W:" \
@@ -91,7 +94,9 @@ static const char *options_table_window_size_list[] = {
"}" \ "}" \
"}" \ "}" \
"]" \ "]" \
"#[push-default]" \
"#{T:window-status-format}" \ "#{T:window-status-format}" \
"#[pop-default]" \
"#[norange default]" \ "#[norange default]" \
"#{?window_end_flag,,#{window-status-separator}}" \ "#{?window_end_flag,,#{window-status-separator}}" \
"," \ "," \
@@ -116,12 +121,17 @@ static const char *options_table_window_size_list[] = {
"}" \ "}" \
"}" \ "}" \
"]" \ "]" \
"#[push-default]" \
"#{T:window-status-current-format}" \ "#{T:window-status-current-format}" \
"#[pop-default]" \
"#[norange list=on default]" \ "#[norange list=on default]" \
"#{?window_end_flag,,#{window-status-separator}}" \ "#{?window_end_flag,,#{window-status-separator}}" \
"}" \ "}" \
"#[nolist align=right range=right #{status-right-style}]" \ "#[nolist align=right range=right #{status-right-style}]" \
"#{T;=/#{status-right-length}:status-right}#[norange default]" "#[push-default]" \
"#{T;=/#{status-right-length}:status-right}" \
"#[pop-default]" \
"#[norange default]"
#define OPTIONS_TABLE_STATUS_FORMAT2 \ #define OPTIONS_TABLE_STATUS_FORMAT2 \
"#[align=centre]#{P:#{?pane_active,#[reverse],}" \ "#[align=centre]#{P:#{?pane_active,#[reverse],}" \
"#{pane_index}[#{pane_width}x#{pane_height}]#[default] }" "#{pane_index}[#{pane_width}x#{pane_height}]#[default] }"
@@ -142,6 +152,12 @@ static const char *options_table_status_format_default[] = {
/* Top-level options. */ /* Top-level options. */
const struct options_table_entry options_table[] = { const struct options_table_entry options_table[] = {
/* Server options. */ /* Server options. */
{ .name = "backspace",
.type = OPTIONS_TABLE_KEY,
.scope = OPTIONS_TABLE_SERVER,
.default_num = '\177',
},
{ .name = "buffer-limit", { .name = "buffer-limit",
.type = OPTIONS_TABLE_NUMBER, .type = OPTIONS_TABLE_NUMBER,
.scope = OPTIONS_TABLE_SERVER, .scope = OPTIONS_TABLE_SERVER,
@@ -719,7 +735,7 @@ const struct options_table_entry options_table[] = {
.type = OPTIONS_TABLE_CHOICE, .type = OPTIONS_TABLE_CHOICE,
.scope = OPTIONS_TABLE_WINDOW, .scope = OPTIONS_TABLE_WINDOW,
.choices = options_table_window_size_list, .choices = options_table_window_size_list,
.default_num = WINDOW_SIZE_SMALLEST .default_num = WINDOW_SIZE_LATEST
}, },
{ .name = "window-style", { .name = "window-style",

View File

@@ -296,6 +296,7 @@ options_remove(struct options_entry *o)
else else
options_value_free(o, &o->value); options_value_free(o, &o->value);
RB_REMOVE(options_tree, &oo->tree, o); RB_REMOVE(options_tree, &oo->tree, o);
free((void *)o->name);
free(o); free(o);
} }
@@ -320,6 +321,17 @@ options_array_item(struct options_entry *o, u_int idx)
return (RB_FIND(options_array, &o->value.array, &a)); return (RB_FIND(options_array, &o->value.array, &a));
} }
static struct options_array_item *
options_array_new(struct options_entry *o, u_int idx)
{
struct options_array_item *a;
a = xcalloc(1, sizeof *a);
a->index = idx;
RB_INSERT(options_array, &o->value.array, a);
return (a);
}
static void static void
options_array_free(struct options_entry *o, struct options_array_item *a) options_array_free(struct options_entry *o, struct options_array_item *a)
{ {
@@ -367,7 +379,14 @@ options_array_set(struct options_entry *o, u_int idx, const char *value,
return (-1); return (-1);
} }
if (OPTIONS_IS_COMMAND(o) && value != NULL) { if (value == NULL) {
a = options_array_item(o, idx);
if (a != NULL)
options_array_free(o, a);
return (0);
}
if (OPTIONS_IS_COMMAND(o)) {
pr = cmd_parse_from_string(value, NULL); pr = cmd_parse_from_string(value, NULL);
switch (pr->status) { switch (pr->status) {
case CMD_PARSE_EMPTY: case CMD_PARSE_EMPTY:
@@ -383,34 +402,33 @@ options_array_set(struct options_entry *o, u_int idx, const char *value,
case CMD_PARSE_SUCCESS: case CMD_PARSE_SUCCESS:
break; break;
} }
}
a = options_array_item(o, idx); a = options_array_item(o, idx);
if (value == NULL) { if (a == NULL)
if (a != NULL) a = options_array_new(o, idx);
options_array_free(o, a); else
options_value_free(o, &a->value);
a->value.cmdlist = pr->cmdlist;
return (0); return (0);
} }
if (OPTIONS_IS_STRING(o)) { if (OPTIONS_IS_STRING(o)) {
a = options_array_item(o, idx);
if (a != NULL && append) if (a != NULL && append)
xasprintf(&new, "%s%s", a->value.string, value); xasprintf(&new, "%s%s", a->value.string, value);
else else
new = xstrdup(value); new = xstrdup(value);
if (a == NULL)
a = options_array_new(o, idx);
else
options_value_free(o, &a->value);
a->value.string = new;
return (0);
} }
if (a == NULL) { if (cause != NULL)
a = xcalloc(1, sizeof *a); *cause = xstrdup("wrong array type");
a->index = idx; return (-1);
RB_INSERT(options_array, &o->value.array, a);
} else
options_value_free(o, &a->value);
if (OPTIONS_IS_STRING(o))
a->value.string = new;
else if (OPTIONS_IS_COMMAND(o))
a->value.cmdlist = pr->cmdlist;
return (0);
} }
int int
@@ -592,7 +610,7 @@ options_match(const char *s, int *idx, int *ambiguous)
struct options_entry * struct options_entry *
options_match_get(struct options *oo, const char *s, int *idx, int only, options_match_get(struct options *oo, const char *s, int *idx, int only,
int* ambiguous) int *ambiguous)
{ {
char *name; char *name;
struct options_entry *o; struct options_entry *o;

2
proc.c
View File

@@ -181,7 +181,7 @@ proc_start(const char *name)
memset(&u, 0, sizeof u); memset(&u, 0, sizeof u);
log_debug("%s started (%ld): version %s, socket %s, protocol %d", name, log_debug("%s started (%ld): version %s, socket %s, protocol %d", name,
(long)getpid(), VERSION, socket_path, PROTOCOL_VERSION); (long)getpid(), getversion(), socket_path, PROTOCOL_VERSION);
log_debug("on %s %s %s; libevent %s (%s)", u.sysname, u.release, log_debug("on %s %s %s; libevent %s (%s)", u.sysname, u.release,
u.version, event_get_version(), event_get_method()); u.version, event_get_version(), event_get_method());

View File

@@ -107,6 +107,12 @@ regsub(const char *pattern, const char *with, const char *text, int flags)
start += m[0].rm_eo + 1; start += m[0].rm_eo + 1;
empty = 1; empty = 1;
} }
/* Stop now if anchored to start. */
if (*pattern == '^') {
regsub_copy(&buf, &len, text, start, end);
break;
}
} }
buf[len] = '\0'; buf[len] = '\0';

334
resize.c
View File

@@ -23,7 +23,7 @@
#include "tmux.h" #include "tmux.h"
void void
resize_window(struct window *w, u_int sx, u_int sy) resize_window(struct window *w, u_int sx, u_int sy, int xpixel, int ypixel)
{ {
int zoomed; int zoomed;
@@ -50,7 +50,7 @@ resize_window(struct window *w, u_int sx, u_int sy)
sx = w->layout_root->sx; sx = w->layout_root->sx;
if (sy < w->layout_root->sy) if (sy < w->layout_root->sy)
sy = w->layout_root->sy; sy = w->layout_root->sy;
window_resize(w, sx, sy); window_resize(w, sx, sy, xpixel, ypixel);
log_debug("%s: @%u resized to %u,%u; layout %u,%u", __func__, w->id, log_debug("%s: @%u resized to %u,%u; layout %u,%u", __func__, w->id,
sx, sy, w->layout_root->sx, w->layout_root->sy); sx, sy, w->layout_root->sx, w->layout_root->sy);
@@ -66,68 +66,144 @@ resize_window(struct window *w, u_int sx, u_int sy)
static int static int
ignore_client_size(struct client *c) ignore_client_size(struct client *c)
{ {
struct client *loop;
if (c->session == NULL) if (c->session == NULL)
return (1); return (1);
if (c->flags & CLIENT_NOSIZEFLAGS) if (c->flags & CLIENT_NOSIZEFLAGS)
return (1); return (1);
if (c->flags & CLIENT_READONLY) {
/*
* Ignore readonly clients if there are any attached clients
* that aren't readonly.
*/
TAILQ_FOREACH (loop, &clients, entry) {
if (loop->session == NULL)
continue;
if (loop->flags & CLIENT_NOSIZEFLAGS)
continue;
if (~loop->flags & CLIENT_READONLY)
return (1);
}
}
if ((c->flags & CLIENT_CONTROL) && (~c->flags & CLIENT_SIZECHANGED)) if ((c->flags & CLIENT_CONTROL) && (~c->flags & CLIENT_SIZECHANGED))
return (1); return (1);
return (0); return (0);
} }
void void
default_window_size(struct session *s, struct window *w, u_int *sx, u_int *sy, default_window_size(struct client *c, struct session *s, struct window *w,
int type) u_int *sx, u_int *sy, u_int *xpixel, u_int *ypixel, int type)
{ {
struct client *c; struct client *loop;
u_int cx, cy; u_int cx, cy, n;
const char *value; const char *value;
if (type == -1) if (type == -1)
type = options_get_number(global_w_options, "window-size"); type = options_get_number(global_w_options, "window-size");
if (type == WINDOW_SIZE_MANUAL) switch (type) {
goto manual; case WINDOW_SIZE_LARGEST:
if (type == WINDOW_SIZE_LARGEST) {
*sx = *sy = 0; *sx = *sy = 0;
TAILQ_FOREACH(c, &clients, entry) { *xpixel = *ypixel = 0;
if (ignore_client_size(c)) TAILQ_FOREACH(loop, &clients, entry) {
if (ignore_client_size(loop))
continue; continue;
if (w != NULL && !session_has(c->session, w)) if (w != NULL && !session_has(loop->session, w))
continue; continue;
if (w == NULL && c->session != s) if (w == NULL && loop->session != s)
continue; continue;
cx = c->tty.sx; cx = loop->tty.sx;
cy = c->tty.sy - status_line_size(c); cy = loop->tty.sy - status_line_size(loop);
if (cx > *sx) if (cx > *sx)
*sx = cx; *sx = cx;
if (cy > *sy) if (cy > *sy)
*sy = cy; *sy = cy;
if (loop->tty.xpixel > *xpixel &&
loop->tty.ypixel > *ypixel) {
*xpixel = loop->tty.xpixel;
*ypixel = loop->tty.ypixel;
}
} }
if (*sx == 0 || *sy == 0) if (*sx == 0 || *sy == 0)
goto manual; goto manual;
} else { break;
case WINDOW_SIZE_SMALLEST:
*sx = *sy = UINT_MAX; *sx = *sy = UINT_MAX;
TAILQ_FOREACH(c, &clients, entry) { *xpixel = *ypixel = 0;
if (ignore_client_size(c)) TAILQ_FOREACH(loop, &clients, entry) {
if (ignore_client_size(loop))
continue; continue;
if (w != NULL && !session_has(c->session, w)) if (w != NULL && !session_has(loop->session, w))
continue; continue;
if (w == NULL && c->session != s) if (w == NULL && loop->session != s)
continue; continue;
cx = c->tty.sx; cx = loop->tty.sx;
cy = c->tty.sy - status_line_size(c); cy = loop->tty.sy - status_line_size(loop);
if (cx < *sx) if (cx < *sx)
*sx = cx; *sx = cx;
if (cy < *sy) if (cy < *sy)
*sy = cy; *sy = cy;
if (loop->tty.xpixel > *xpixel &&
loop->tty.ypixel > *ypixel) {
*xpixel = loop->tty.xpixel;
*ypixel = loop->tty.ypixel;
}
} }
if (*sx == UINT_MAX || *sy == UINT_MAX) if (*sx == UINT_MAX || *sy == UINT_MAX)
goto manual; goto manual;
break;
case WINDOW_SIZE_LATEST:
if (c != NULL && !ignore_client_size(c)) {
*sx = c->tty.sx;
*sy = c->tty.sy - status_line_size(c);
*xpixel = c->tty.xpixel;
*ypixel = c->tty.ypixel;
} else {
if (w == NULL)
goto manual;
n = 0;
TAILQ_FOREACH(loop, &clients, entry) {
if (!ignore_client_size(loop) &&
session_has(loop->session, w)) {
if (++n > 1)
break;
}
}
*sx = *sy = UINT_MAX;
*xpixel = *ypixel = 0;
TAILQ_FOREACH(loop, &clients, entry) {
if (ignore_client_size(loop))
continue;
if (n > 1 && loop != w->latest)
continue;
s = loop->session;
cx = loop->tty.sx;
cy = loop->tty.sy - status_line_size(loop);
if (cx < *sx)
*sx = cx;
if (cy < *sy)
*sy = cy;
if (loop->tty.xpixel > *xpixel &&
loop->tty.ypixel > *ypixel) {
*xpixel = loop->tty.xpixel;
*ypixel = loop->tty.ypixel;
}
}
if (*sx == UINT_MAX || *sy == UINT_MAX)
goto manual;
}
break;
case WINDOW_SIZE_MANUAL:
goto manual;
} }
goto done; goto done;
@@ -149,14 +225,145 @@ done:
*sy = WINDOW_MAXIMUM; *sy = WINDOW_MAXIMUM;
} }
void
recalculate_size(struct window *w)
{
struct session *s;
struct client *c;
u_int sx, sy, cx, cy, xpixel = 0, ypixel = 0, n;
int type, current, has, changed;
if (w->active == NULL)
return;
log_debug("%s: @%u is %u,%u", __func__, w->id, w->sx, w->sy);
type = options_get_number(w->options, "window-size");
current = options_get_number(w->options, "aggressive-resize");
changed = 1;
switch (type) {
case WINDOW_SIZE_LARGEST:
sx = sy = 0;
TAILQ_FOREACH(c, &clients, entry) {
if (ignore_client_size(c))
continue;
s = c->session;
if (current)
has = (s->curw->window == w);
else
has = session_has(s, w);
if (!has)
continue;
cx = c->tty.sx;
cy = c->tty.sy - status_line_size(c);
if (cx > sx)
sx = cx;
if (cy > sy)
sy = cy;
if (c->tty.xpixel > xpixel && c->tty.ypixel > ypixel) {
xpixel = c->tty.xpixel;
ypixel = c->tty.ypixel;
}
}
if (sx == 0 || sy == 0)
changed = 0;
break;
case WINDOW_SIZE_SMALLEST:
sx = sy = UINT_MAX;
TAILQ_FOREACH(c, &clients, entry) {
if (ignore_client_size(c))
continue;
s = c->session;
if (current)
has = (s->curw->window == w);
else
has = session_has(s, w);
if (!has)
continue;
cx = c->tty.sx;
cy = c->tty.sy - status_line_size(c);
if (cx < sx)
sx = cx;
if (cy < sy)
sy = cy;
if (c->tty.xpixel > xpixel && c->tty.ypixel > ypixel) {
xpixel = c->tty.xpixel;
ypixel = c->tty.ypixel;
}
}
if (sx == UINT_MAX || sy == UINT_MAX)
changed = 0;
break;
case WINDOW_SIZE_LATEST:
n = 0;
TAILQ_FOREACH(c, &clients, entry) {
if (!ignore_client_size(c) &&
session_has(c->session, w)) {
if (++n > 1)
break;
}
}
sx = sy = UINT_MAX;
TAILQ_FOREACH(c, &clients, entry) {
if (ignore_client_size(c))
continue;
if (n > 1 && c != w->latest)
continue;
s = c->session;
if (current)
has = (s->curw->window == w);
else
has = session_has(s, w);
if (!has)
continue;
cx = c->tty.sx;
cy = c->tty.sy - status_line_size(c);
if (cx < sx)
sx = cx;
if (cy < sy)
sy = cy;
if (c->tty.xpixel > xpixel && c->tty.ypixel > ypixel) {
xpixel = c->tty.xpixel;
ypixel = c->tty.ypixel;
}
}
if (sx == UINT_MAX || sy == UINT_MAX)
changed = 0;
break;
case WINDOW_SIZE_MANUAL:
changed = 0;
break;
}
if (changed && w->sx == sx && w->sy == sy)
changed = 0;
if (!changed) {
tty_update_window_offset(w);
return;
}
log_debug("%s: @%u changed to %u,%u (%ux%u)", __func__, w->id, sx, sy,
xpixel, ypixel);
resize_window(w, sx, sy, xpixel, ypixel);
}
void void
recalculate_sizes(void) recalculate_sizes(void)
{ {
struct session *s; struct session *s;
struct client *c; struct client *c;
struct window *w; struct window *w;
u_int sx, sy, cx, cy;
int type, current, has, changed;
/* /*
* Clear attached count and update saved status line information for * Clear attached count and update saved status line information for
@@ -172,85 +379,18 @@ recalculate_sizes(void)
* client. * client.
*/ */
TAILQ_FOREACH(c, &clients, entry) { TAILQ_FOREACH(c, &clients, entry) {
s = c->session;
if (s != NULL && !(c->flags & CLIENT_UNATTACHEDFLAGS))
s->attached++;
if (ignore_client_size(c)) if (ignore_client_size(c))
continue; continue;
s = c->session;
if (c->tty.sy <= s->statuslines || (c->flags & CLIENT_CONTROL)) if (c->tty.sy <= s->statuslines || (c->flags & CLIENT_CONTROL))
c->flags |= CLIENT_STATUSOFF; c->flags |= CLIENT_STATUSOFF;
else else
c->flags &= ~CLIENT_STATUSOFF; c->flags &= ~CLIENT_STATUSOFF;
s->attached++;
} }
/* Walk each window and adjust the size. */ /* Walk each window and adjust the size. */
RB_FOREACH(w, windows, &windows) { RB_FOREACH(w, windows, &windows)
if (w->active == NULL) recalculate_size(w);
continue;
log_debug("%s: @%u is %u,%u", __func__, w->id, w->sx, w->sy);
type = options_get_number(w->options, "window-size");
if (type == WINDOW_SIZE_MANUAL)
continue;
current = options_get_number(w->options, "aggressive-resize");
changed = 1;
if (type == WINDOW_SIZE_LARGEST) {
sx = sy = 0;
TAILQ_FOREACH(c, &clients, entry) {
if (ignore_client_size(c))
continue;
s = c->session;
if (current)
has = (s->curw->window == w);
else
has = session_has(s, w);
if (!has)
continue;
cx = c->tty.sx;
cy = c->tty.sy - status_line_size(c);
if (cx > sx)
sx = cx;
if (cy > sy)
sy = cy;
}
if (sx == 0 || sy == 0)
changed = 0;
} else {
sx = sy = UINT_MAX;
TAILQ_FOREACH(c, &clients, entry) {
if (ignore_client_size(c))
continue;
s = c->session;
if (current)
has = (s->curw->window == w);
else
has = session_has(s, w);
if (!has)
continue;
cx = c->tty.sx;
cy = c->tty.sy - status_line_size(c);
if (cx < sx)
sx = cx;
if (cy < sy)
sy = cy;
}
if (sx == UINT_MAX || sy == UINT_MAX)
changed = 0;
}
if (w->sx == sx && w->sy == sy)
changed = 0;
if (!changed) {
tty_update_window_offset(w);
continue;
}
log_debug("%s: @%u changed to %u,%u", __func__, w->id, sx, sy);
resize_window(w, sx, sy);
}
} }

View File

@@ -100,7 +100,6 @@ void
screen_write_start(struct screen_write_ctx *ctx, struct window_pane *wp, screen_write_start(struct screen_write_ctx *ctx, struct window_pane *wp,
struct screen *s) struct screen *s)
{ {
char tmp[32];
u_int y; u_int y;
memset(ctx, 0, sizeof *ctx); memset(ctx, 0, sizeof *ctx);
@@ -119,12 +118,17 @@ screen_write_start(struct screen_write_ctx *ctx, struct window_pane *wp,
ctx->scrolled = 0; ctx->scrolled = 0;
ctx->bg = 8; ctx->bg = 8;
if (wp != NULL) { if (log_get_level() != 0) {
snprintf(tmp, sizeof tmp, "pane %%%u (at %u,%u)", wp->id, if (wp != NULL) {
wp->xoff, wp->yoff); log_debug("%s: size %ux%u, pane %%%u (at %u,%u)",
__func__, screen_size_x(ctx->s),
screen_size_y(ctx->s), wp->id, wp->xoff, wp->yoff);
} else {
log_debug("%s: size %ux%u, no pane",
__func__, screen_size_x(ctx->s),
screen_size_y(ctx->s));
}
} }
log_debug("%s: size %ux%u, %s", __func__, screen_size_x(ctx->s),
screen_size_y(ctx->s), wp == NULL ? "no pane" : tmp);
} }
/* Finish writing. */ /* Finish writing. */
@@ -1234,7 +1238,6 @@ screen_write_collect_scroll(struct screen_write_ctx *ctx)
for (y = s->rupper; y < s->rlower; y++) { for (y = s->rupper; y < s->rlower; y++) {
cl = &ctx->list[y + 1]; cl = &ctx->list[y + 1];
TAILQ_CONCAT(&ctx->list[y].items, &cl->items, entry); TAILQ_CONCAT(&ctx->list[y].items, &cl->items, entry);
TAILQ_INIT(&cl->items);
} }
} }
@@ -1323,8 +1326,7 @@ screen_write_collect_end(struct screen_write_ctx *ctx)
} }
} }
memcpy(&gc, &ci->gc, sizeof gc); grid_view_set_cells(s->grid, s->cx, s->cy, &ci->gc, ci->data, ci->used);
grid_view_set_cells(s->grid, s->cx, s->cy, &gc, ci->data, ci->used);
screen_write_set_cursor(ctx, s->cx + ci->used, -1); screen_write_set_cursor(ctx, s->cx + ci->used, -1);
for (xx = s->cx; xx < screen_size_x(s); xx++) { for (xx = s->cx; xx < screen_size_x(s); xx++) {
@@ -1348,8 +1350,7 @@ screen_write_collect_add(struct screen_write_ctx *ctx,
/* /*
* Don't need to check that the attributes and whatnot are still the * Don't need to check that the attributes and whatnot are still the
* same - input_parse will end the collection when anything that isn't * same - input_parse will end the collection when anything that isn't
* a plain character is encountered. Also nothing should make it here * a plain character is encountered.
* that isn't a single ASCII character.
*/ */
collect = 1; collect = 1;
@@ -1635,7 +1636,8 @@ screen_write_overwrite(struct screen_write_ctx *ctx, struct grid_cell *gc,
grid_view_get_cell(gd, xx, s->cy, &tmp_gc); grid_view_get_cell(gd, xx, s->cy, &tmp_gc);
if (~tmp_gc.flags & GRID_FLAG_PADDING) if (~tmp_gc.flags & GRID_FLAG_PADDING)
break; break;
log_debug("%s: overwrite at %u,%u", __func__, xx, s->cy); log_debug("%s: overwrite at %u,%u", __func__, xx,
s->cy);
grid_view_set_cell(gd, xx, s->cy, &grid_default_cell); grid_view_set_cell(gd, xx, s->cy, &grid_default_cell);
done = 1; done = 1;
} }

View File

@@ -151,11 +151,24 @@ screen_set_cursor_colour(struct screen *s, const char *colour)
} }
/* Set screen title. */ /* Set screen title. */
void int
screen_set_title(struct screen *s, const char *title) screen_set_title(struct screen *s, const char *title)
{ {
char *cp;
if (!utf8_isvalid(title))
return (0);
free(s->title); free(s->title);
utf8_stravis(&s->title, title, VIS_OCTAL|VIS_CSTYLE|VIS_TAB|VIS_NL); s->title = xstrdup(title);
return (1);
}
/* Set screen path. */
void
screen_set_path(struct screen *s, const char *path)
{
free(s->path);
utf8_stravis(&s->path, path, VIS_OCTAL|VIS_CSTYLE|VIS_TAB|VIS_NL);
} }
/* Push the current title onto the stack. */ /* Push the current title onto the stack. */

View File

@@ -42,11 +42,18 @@ static void server_client_set_title(struct client *);
static void server_client_reset_state(struct client *); static void server_client_reset_state(struct client *);
static int server_client_assume_paste(struct session *); static int server_client_assume_paste(struct session *);
static void server_client_clear_overlay(struct client *); static void server_client_clear_overlay(struct client *);
static void server_client_resize_event(int, short, void *);
static void server_client_dispatch(struct imsg *, void *); static void server_client_dispatch(struct imsg *, void *);
static void server_client_dispatch_command(struct client *, struct imsg *); static void server_client_dispatch_command(struct client *, struct imsg *);
static void server_client_dispatch_identify(struct client *, struct imsg *); static void server_client_dispatch_identify(struct client *, struct imsg *);
static void server_client_dispatch_shell(struct client *); static void server_client_dispatch_shell(struct client *);
static void server_client_dispatch_write_ready(struct client *,
struct imsg *);
static void server_client_dispatch_read_data(struct client *,
struct imsg *);
static void server_client_dispatch_read_done(struct client *,
struct imsg *);
/* Number of attached clients. */ /* Number of attached clients. */
u_int u_int
@@ -194,16 +201,6 @@ server_client_create(int fd)
TAILQ_INIT(&c->queue); TAILQ_INIT(&c->queue);
c->stdin_data = evbuffer_new();
if (c->stdin_data == NULL)
fatalx("out of memory");
c->stdout_data = evbuffer_new();
if (c->stdout_data == NULL)
fatalx("out of memory");
c->stderr_data = evbuffer_new();
if (c->stderr_data == NULL)
fatalx("out of memory");
c->tty.fd = -1; c->tty.fd = -1;
c->title = NULL; c->title = NULL;
@@ -222,6 +219,8 @@ server_client_create(int fd)
c->prompt_buffer = NULL; c->prompt_buffer = NULL;
c->prompt_index = 0; c->prompt_index = 0;
RB_INIT(&c->files);
c->flags |= CLIENT_FOCUSED; c->flags |= CLIENT_FOCUSED;
c->keytable = key_bindings_get_table("root", 1); c->keytable = key_bindings_get_table("root", 1);
@@ -263,6 +262,7 @@ void
server_client_lost(struct client *c) server_client_lost(struct client *c)
{ {
struct message_entry *msg, *msg1; struct message_entry *msg, *msg1;
struct client_file *cf;
c->flags |= CLIENT_DEAD; c->flags |= CLIENT_DEAD;
@@ -270,8 +270,10 @@ server_client_lost(struct client *c)
status_prompt_clear(c); status_prompt_clear(c);
status_message_clear(c); status_message_clear(c);
if (c->stdin_callback != NULL) RB_FOREACH(cf, client_files, &c->files) {
c->stdin_callback(c, 1, c->stdin_callback_data); cf->error = EINTR;
file_fire_done(cf);
}
TAILQ_REMOVE(&clients, c, entry); TAILQ_REMOVE(&clients, c, entry);
log_debug("lost client %p", c); log_debug("lost client %p", c);
@@ -285,11 +287,6 @@ server_client_lost(struct client *c)
free(c->ttyname); free(c->ttyname);
free(c->term); free(c->term);
evbuffer_free(c->stdin_data);
evbuffer_free(c->stdout_data);
if (c->stderr_data != c->stdout_data)
evbuffer_free(c->stderr_data);
status_free(c); status_free(c);
free(c->title); free(c->title);
@@ -540,7 +537,8 @@ have_event:
where = STATUS_RIGHT; where = STATUS_RIGHT;
break; break;
case STYLE_RANGE_WINDOW: case STYLE_RANGE_WINDOW:
wl = winlink_find_by_index(&s->windows, sr->argument); wl = winlink_find_by_index(&s->windows,
sr->argument);
if (wl == NULL) if (wl == NULL)
return (KEYC_UNKNOWN); return (KEYC_UNKNOWN);
m->w = wl->window->id; m->w = wl->window->id;
@@ -993,6 +991,24 @@ server_client_assume_paste(struct session *s)
return (0); return (0);
} }
/* Has the latest client changed? */
static void
server_client_update_latest(struct client *c)
{
struct window *w;
if (c->session == NULL)
return;
w = c->session->curw->window;
if (w->latest == c)
return;
w->latest = c;
if (options_get_number(w->options, "window-size") == WINDOW_SIZE_LATEST)
recalculate_size(w);
}
/* /*
* Handle data key input from client. This owns and can modify the key event it * Handle data key input from client. This owns and can modify the key event it
* is given and is responsible for freeing it. * is given and is responsible for freeing it.
@@ -1016,7 +1032,7 @@ server_client_key_callback(struct cmdq_item *item, void *data)
key_code key0; key_code key0;
/* Check the client is good to accept input. */ /* Check the client is good to accept input. */
if (s == NULL || (c->flags & (CLIENT_DEAD|CLIENT_SUSPENDED)) != 0) if (s == NULL || (c->flags & CLIENT_UNATTACHEDFLAGS))
goto out; goto out;
wl = s->curw; wl = s->curw;
@@ -1189,6 +1205,8 @@ forward_key:
window_pane_key(wp, c, s, wl, key, m); window_pane_key(wp, c, s, wl, key, m);
out: out:
if (s != NULL)
server_client_update_latest(c);
free(event); free(event);
return (CMD_RETURN_NORMAL); return (CMD_RETURN_NORMAL);
} }
@@ -1201,7 +1219,7 @@ server_client_handle_key(struct client *c, struct key_event *event)
struct cmdq_item *item; struct cmdq_item *item;
/* Check the client is good to accept input. */ /* Check the client is good to accept input. */
if (s == NULL || (c->flags & (CLIENT_DEAD|CLIENT_SUSPENDED)) != 0) if (s == NULL || (c->flags & CLIENT_UNATTACHEDFLAGS))
return (0); return (0);
/* /*
@@ -1245,7 +1263,7 @@ server_client_loop(void)
struct window_pane *wp; struct window_pane *wp;
struct winlink *wl; struct winlink *wl;
struct session *s; struct session *s;
int focus; int focus, attached, resize;
TAILQ_FOREACH(c, &clients, entry) { TAILQ_FOREACH(c, &clients, entry) {
server_client_check_exit(c); server_client_check_exit(c);
@@ -1258,19 +1276,33 @@ server_client_loop(void)
/* /*
* Any windows will have been redrawn as part of clients, so clear * Any windows will have been redrawn as part of clients, so clear
* their flags now. Also check pane focus and resize. * their flags now. Also check pane focus and resize.
*
* As an optimization, panes in windows that are in an attached session
* but not the current window are not resized (this reduces the amount
* of work needed when, for example, resizing an X terminal a
* lot). Windows in no attached session are resized immediately since
* that is likely to have come from a command like split-window and be
* what the user wanted.
*/ */
focus = options_get_number(global_options, "focus-events"); focus = options_get_number(global_options, "focus-events");
RB_FOREACH(w, windows, &windows) { RB_FOREACH(w, windows, &windows) {
attached = resize = 0;
TAILQ_FOREACH(wl, &w->winlinks, wentry) { TAILQ_FOREACH(wl, &w->winlinks, wentry) {
s = wl->session; s = wl->session;
if (s->attached != 0 && s->curw == wl) if (s->attached != 0)
attached = 1;
if (s->attached != 0 && s->curw == wl) {
resize = 1;
break; break;
}
} }
if (!attached)
resize = 1;
TAILQ_FOREACH(wp, &w->panes, entry) { TAILQ_FOREACH(wp, &w->panes, entry) {
if (wp->fd != -1) { if (wp->fd != -1) {
if (focus) if (focus)
server_client_check_focus(wp); server_client_check_focus(wp);
if (wl != NULL) if (resize)
server_client_check_resize(wp); server_client_check_resize(wp);
} }
wp->flags &= ~PANE_REDRAW; wp->flags &= ~PANE_REDRAW;
@@ -1284,7 +1316,6 @@ static int
server_client_resize_force(struct window_pane *wp) server_client_resize_force(struct window_pane *wp)
{ {
struct timeval tv = { .tv_usec = 100000 }; struct timeval tv = { .tv_usec = 100000 };
struct winsize ws;
/* /*
* If we are resizing to the same size as when we entered the loop * If we are resizing to the same size as when we entered the loop
@@ -1305,50 +1336,20 @@ server_client_resize_force(struct window_pane *wp)
wp->sy <= 1) wp->sy <= 1)
return (0); return (0);
memset(&ws, 0, sizeof ws);
ws.ws_col = wp->sx;
ws.ws_row = wp->sy - 1;
if (wp->fd != -1 && ioctl(wp->fd, TIOCSWINSZ, &ws) == -1)
#ifdef __sun
if (errno != EINVAL && errno != ENXIO)
#endif
fatal("ioctl failed");
log_debug("%s: %%%u forcing resize", __func__, wp->id); log_debug("%s: %%%u forcing resize", __func__, wp->id);
window_pane_send_resize(wp, -1);
evtimer_add(&wp->resize_timer, &tv); evtimer_add(&wp->resize_timer, &tv);
wp->flags |= PANE_RESIZEFORCE; wp->flags |= PANE_RESIZEFORCE;
return (1); return (1);
} }
/* Resize timer event. */ /* Resize a pane. */
static void static void
server_client_resize_event(__unused int fd, __unused short events, void *data) server_client_resize_pane(struct window_pane *wp)
{ {
struct window_pane *wp = data;
struct winsize ws;
evtimer_del(&wp->resize_timer);
if (!(wp->flags & PANE_RESIZE))
return;
if (server_client_resize_force(wp))
return;
memset(&ws, 0, sizeof ws);
ws.ws_col = wp->sx;
ws.ws_row = wp->sy;
if (wp->fd != -1 && ioctl(wp->fd, TIOCSWINSZ, &ws) == -1)
#ifdef __sun
/*
* Some versions of Solaris apparently can return an error when
* resizing; don't know why this happens, can't reproduce on
* other platforms and ignoring it doesn't seem to cause any
* issues.
*/
if (errno != EINVAL && errno != ENXIO)
#endif
fatal("ioctl failed");
log_debug("%s: %%%u resize to %u,%u", __func__, wp->id, wp->sx, wp->sy); log_debug("%s: %%%u resize to %u,%u", __func__, wp->id, wp->sx, wp->sy);
window_pane_send_resize(wp, 0);
wp->flags &= ~PANE_RESIZE; wp->flags &= ~PANE_RESIZE;
@@ -1356,35 +1357,55 @@ server_client_resize_event(__unused int fd, __unused short events, void *data)
wp->osy = wp->sy; wp->osy = wp->sy;
} }
/* Start the resize timer. */
static void
server_client_start_resize_timer(struct window_pane *wp)
{
struct timeval tv = { .tv_usec = 250000 };
if (!evtimer_pending(&wp->resize_timer, NULL))
evtimer_add(&wp->resize_timer, &tv);
}
/* Resize timer event. */
static void
server_client_resize_event(__unused int fd, __unused short events, void *data)
{
struct window_pane *wp = data;
evtimer_del(&wp->resize_timer);
if (~wp->flags & PANE_RESIZE)
return;
log_debug("%s: %%%u timer fired (was%s resized)", __func__, wp->id,
(wp->flags & PANE_RESIZED) ? "" : " not");
if (wp->saved_grid == NULL && (wp->flags & PANE_RESIZED)) {
log_debug("%s: %%%u deferring timer", __func__, wp->id);
server_client_start_resize_timer(wp);
} else if (!server_client_resize_force(wp)) {
log_debug("%s: %%%u resizing pane", __func__, wp->id);
server_client_resize_pane(wp);
}
wp->flags &= ~PANE_RESIZED;
}
/* Check if pane should be resized. */ /* Check if pane should be resized. */
static void static void
server_client_check_resize(struct window_pane *wp) server_client_check_resize(struct window_pane *wp)
{ {
struct timeval tv = { .tv_usec = 250000 }; if (~wp->flags & PANE_RESIZE)
if (!(wp->flags & PANE_RESIZE))
return; return;
log_debug("%s: %%%u resize to %u,%u", __func__, wp->id, wp->sx, wp->sy);
if (!event_initialized(&wp->resize_timer)) if (!event_initialized(&wp->resize_timer))
evtimer_set(&wp->resize_timer, server_client_resize_event, wp); evtimer_set(&wp->resize_timer, server_client_resize_event, wp);
/* if (!evtimer_pending(&wp->resize_timer, NULL)) {
* The first resize should happen immediately, so if the timer is not log_debug("%s: %%%u starting timer", __func__, wp->id);
* running, do it now. server_client_resize_pane(wp);
*/ server_client_start_resize_timer(wp);
if (!evtimer_pending(&wp->resize_timer, NULL)) } else
server_client_resize_event(-1, 0, wp); log_debug("%s: %%%u timer running", __func__, wp->id);
/*
* If the pane is in the alternate screen, let the timer expire and
* resize to give the application a chance to redraw. If not, keep
* pushing the timer back.
*/
if (wp->saved_grid != NULL && evtimer_pending(&wp->resize_timer, NULL))
return;
evtimer_del(&wp->resize_timer);
evtimer_add(&wp->resize_timer, &tv);
} }
/* Check whether pane should be focused. */ /* Check whether pane should be focused. */
@@ -1533,17 +1554,17 @@ server_client_click_timer(__unused int fd, __unused short events, void *data)
static void static void
server_client_check_exit(struct client *c) server_client_check_exit(struct client *c)
{ {
struct client_file *cf;
if (~c->flags & CLIENT_EXIT) if (~c->flags & CLIENT_EXIT)
return; return;
if (c->flags & CLIENT_EXITED) if (c->flags & CLIENT_EXITED)
return; return;
if (EVBUFFER_LENGTH(c->stdin_data) != 0) RB_FOREACH(cf, client_files, &c->files) {
return; if (EVBUFFER_LENGTH(cf->buffer) != 0)
if (EVBUFFER_LENGTH(c->stdout_data) != 0) return;
return; }
if (EVBUFFER_LENGTH(c->stderr_data) != 0)
return;
if (c->flags & CLIENT_ATTACHED) if (c->flags & CLIENT_ATTACHED)
notify_client("client-detached", c); notify_client("client-detached", c);
@@ -1554,7 +1575,7 @@ server_client_check_exit(struct client *c)
/* Redraw timer callback. */ /* Redraw timer callback. */
static void static void
server_client_redraw_timer(__unused int fd, __unused short events, server_client_redraw_timer(__unused int fd, __unused short events,
__unused void* data) __unused void *data)
{ {
log_debug("redraw timer fired"); log_debug("redraw timer fired");
} }
@@ -1682,11 +1703,10 @@ server_client_set_title(struct client *c)
static void static void
server_client_dispatch(struct imsg *imsg, void *arg) server_client_dispatch(struct imsg *imsg, void *arg)
{ {
struct client *c = arg; struct client *c = arg;
struct msg_stdin_data stdindata; const char *data;
const char *data; ssize_t datalen;
ssize_t datalen; struct session *s;
struct session *s;
if (c->flags & CLIENT_DEAD) if (c->flags & CLIENT_DEAD)
return; return;
@@ -1713,27 +1733,13 @@ server_client_dispatch(struct imsg *imsg, void *arg)
case MSG_COMMAND: case MSG_COMMAND:
server_client_dispatch_command(c, imsg); server_client_dispatch_command(c, imsg);
break; break;
case MSG_STDIN:
if (datalen != sizeof stdindata)
fatalx("bad MSG_STDIN size");
memcpy(&stdindata, data, sizeof stdindata);
if (c->stdin_callback == NULL)
break;
if (stdindata.size <= 0)
c->stdin_closed = 1;
else {
evbuffer_add(c->stdin_data, stdindata.data,
stdindata.size);
}
c->stdin_callback(c, c->stdin_closed, c->stdin_callback_data);
break;
case MSG_RESIZE: case MSG_RESIZE:
if (datalen != 0) if (datalen != 0)
fatalx("bad MSG_RESIZE size"); fatalx("bad MSG_RESIZE size");
if (c->flags & CLIENT_CONTROL) if (c->flags & CLIENT_CONTROL)
break; break;
server_client_update_latest(c);
server_client_clear_overlay(c); server_client_clear_overlay(c);
tty_resize(&c->tty); tty_resize(&c->tty);
recalculate_sizes(); recalculate_sizes();
@@ -1778,6 +1784,15 @@ server_client_dispatch(struct imsg *imsg, void *arg)
server_client_dispatch_shell(c); server_client_dispatch_shell(c);
break; break;
case MSG_WRITE_READY:
server_client_dispatch_write_ready(c, imsg);
break;
case MSG_READ:
server_client_dispatch_read_data(c, imsg);
break;
case MSG_READ_DONE:
server_client_dispatch_read_done(c, imsg);
break;
} }
} }
@@ -1796,7 +1811,7 @@ server_client_command_done(struct cmdq_item *item, __unused void *data)
static void static void
server_client_dispatch_command(struct client *c, struct imsg *imsg) server_client_dispatch_command(struct client *c, struct imsg *imsg)
{ {
struct msg_command_data data; struct msg_command data;
char *buf; char *buf;
size_t len; size_t len;
int argc; int argc;
@@ -1940,19 +1955,11 @@ server_client_dispatch_identify(struct client *c, struct imsg *imsg)
#endif #endif
if (c->flags & CLIENT_CONTROL) { if (c->flags & CLIENT_CONTROL) {
c->stdin_callback = control_callback;
evbuffer_free(c->stderr_data);
c->stderr_data = c->stdout_data;
if (c->flags & CLIENT_CONTROLCONTROL)
evbuffer_add_printf(c->stdout_data, "\033P1000p");
proc_send(c->peer, MSG_STDIN, -1, NULL, 0);
c->tty.fd = -1;
close(c->fd); close(c->fd);
c->fd = -1; c->fd = -1;
control_start(c);
c->tty.fd = -1;
} else if (c->fd != -1) { } else if (c->fd != -1) {
if (tty_init(&c->tty, c, c->fd, c->term) != 0) { if (tty_init(&c->tty, c, c->fd, c->term) != 0) {
close(c->fd); close(c->fd);
@@ -1992,93 +1999,71 @@ server_client_dispatch_shell(struct client *c)
proc_kill_peer(c->peer); proc_kill_peer(c->peer);
} }
/* Event callback to push more stdout data if any left. */ /* Handle write ready message. */
static void static void
server_client_stdout_cb(__unused int fd, __unused short events, void *arg) server_client_dispatch_write_ready(struct client *c, struct imsg *imsg)
{ {
struct client *c = arg; struct msg_write_ready *msg = imsg->data;
size_t msglen = imsg->hdr.len - IMSG_HEADER_SIZE;
struct client_file find, *cf;
if (~c->flags & CLIENT_DEAD) if (msglen != sizeof *msg)
server_client_push_stdout(c); fatalx("bad MSG_WRITE_READY size");
server_client_unref(c); find.stream = msg->stream;
} if ((cf = RB_FIND(client_files, &c->files, &find)) == NULL)
/* Push stdout to client if possible. */
void
server_client_push_stdout(struct client *c)
{
struct msg_stdout_data data;
size_t sent, left;
left = EVBUFFER_LENGTH(c->stdout_data);
while (left != 0) {
sent = left;
if (sent > sizeof data.data)
sent = sizeof data.data;
memcpy(data.data, EVBUFFER_DATA(c->stdout_data), sent);
data.size = sent;
if (proc_send(c->peer, MSG_STDOUT, -1, &data, sizeof data) != 0)
break;
evbuffer_drain(c->stdout_data, sent);
left = EVBUFFER_LENGTH(c->stdout_data);
log_debug("%s: client %p, sent %zu, left %zu", __func__, c,
sent, left);
}
if (left != 0) {
c->references++;
event_once(-1, EV_TIMEOUT, server_client_stdout_cb, c, NULL);
log_debug("%s: client %p, queued", __func__, c);
}
}
/* Event callback to push more stderr data if any left. */
static void
server_client_stderr_cb(__unused int fd, __unused short events, void *arg)
{
struct client *c = arg;
if (~c->flags & CLIENT_DEAD)
server_client_push_stderr(c);
server_client_unref(c);
}
/* Push stderr to client if possible. */
void
server_client_push_stderr(struct client *c)
{
struct msg_stderr_data data;
size_t sent, left;
if (c->stderr_data == c->stdout_data) {
server_client_push_stdout(c);
return; return;
} if (msg->error != 0) {
cf->error = msg->error;
file_fire_done(cf);
} else
file_push(cf);
}
left = EVBUFFER_LENGTH(c->stderr_data); /* Handle read data message. */
while (left != 0) { static void
sent = left; server_client_dispatch_read_data(struct client *c, struct imsg *imsg)
if (sent > sizeof data.data) {
sent = sizeof data.data; struct msg_read_data *msg = imsg->data;
memcpy(data.data, EVBUFFER_DATA(c->stderr_data), sent); size_t msglen = imsg->hdr.len - IMSG_HEADER_SIZE;
data.size = sent; struct client_file find, *cf;
void *bdata = msg + 1;
size_t bsize = msglen - sizeof *msg;
if (proc_send(c->peer, MSG_STDERR, -1, &data, sizeof data) != 0) if (msglen < sizeof *msg)
break; fatalx("bad MSG_READ_DATA size");
evbuffer_drain(c->stderr_data, sent); find.stream = msg->stream;
if ((cf = RB_FIND(client_files, &c->files, &find)) == NULL)
return;
left = EVBUFFER_LENGTH(c->stderr_data); log_debug("%s: file %d read %zu bytes", c->name, cf->stream, bsize);
log_debug("%s: client %p, sent %zu, left %zu", __func__, c, if (cf->error == 0) {
sent, left); if (evbuffer_add(cf->buffer, bdata, bsize) != 0) {
} cf->error = ENOMEM;
if (left != 0) { file_fire_done(cf);
c->references++; } else
event_once(-1, EV_TIMEOUT, server_client_stderr_cb, c, NULL); file_fire_read(cf);
log_debug("%s: client %p, queued", __func__, c);
} }
} }
/* Handle read done message. */
static void
server_client_dispatch_read_done(struct client *c, struct imsg *imsg)
{
struct msg_read_done *msg = imsg->data;
size_t msglen = imsg->hdr.len - IMSG_HEADER_SIZE;
struct client_file find, *cf;
if (msglen != sizeof *msg)
fatalx("bad MSG_READ_DONE size");
find.stream = msg->stream;
if ((cf = RB_FIND(client_files, &c->files, &find)) == NULL)
return;
log_debug("%s: file %d read done", c->name, cf->stream);
cf->error = msg->error;
file_fire_done(cf);
}
/* Add to client message log. */ /* Add to client message log. */
void void
server_client_add_message(struct client *c, const char *fmt, ...) server_client_add_message(struct client *c, const char *fmt, ...)
@@ -2128,19 +2113,3 @@ server_client_get_cwd(struct client *c, struct session *s)
return (home); return (home);
return ("/"); return ("/");
} }
/* Resolve an absolute path or relative to client working directory. */
char *
server_client_get_path(struct client *c, const char *file)
{
char *path, resolved[PATH_MAX];
if (*file == '/')
path = xstrdup(file);
else
xasprintf(&path, "%s/%s", server_client_get_cwd(c, NULL), file);
if (realpath(path, resolved) == NULL)
return (path);
free(path);
return (xstrdup(resolved));
}

View File

@@ -440,36 +440,6 @@ server_check_unattached(void)
} }
} }
int
server_set_stdin_callback(struct client *c, void (*cb)(struct client *, int,
void *), void *cb_data, char **cause)
{
if (c == NULL || c->session != NULL) {
*cause = xstrdup("no client with stdin");
return (-1);
}
if (c->flags & CLIENT_TERMINAL) {
*cause = xstrdup("stdin is a tty");
return (-1);
}
if (c->stdin_callback != NULL) {
*cause = xstrdup("stdin is in use");
return (-1);
}
c->stdin_callback_data = cb_data;
c->stdin_callback = cb;
c->references++;
if (c->stdin_closed)
c->stdin_callback(c, 1, c->stdin_callback_data);
proc_send(c->peer, MSG_STDIN, -1, NULL, 0);
return (0);
}
void void
server_unzoom_window(struct window *w) server_unzoom_window(struct window *w)
{ {

View File

@@ -568,7 +568,20 @@ session_group_count(struct session_group *sg)
n = 0; n = 0;
TAILQ_FOREACH(s, &sg->sessions, gentry) TAILQ_FOREACH(s, &sg->sessions, gentry)
n++; n++;
return (n);
}
/* Count number of clients attached to sessions in session group. */
u_int
session_group_attached_count(struct session_group *sg)
{
struct session *s;
u_int n;
n = 0;
TAILQ_FOREACH(s, &sg->sessions, gentry)
n += s->attached;
return (n); return (n);
} }

68
spawn.c
View File

@@ -78,12 +78,14 @@ spawn_log(const char *from, struct spawn_context *sc)
struct winlink * struct winlink *
spawn_window(struct spawn_context *sc, char **cause) spawn_window(struct spawn_context *sc, char **cause)
{ {
struct cmdq_item *item = sc->item;
struct client *c = item->client;
struct session *s = sc->s; struct session *s = sc->s;
struct window *w; struct window *w;
struct window_pane *wp; struct window_pane *wp;
struct winlink *wl; struct winlink *wl;
int idx = sc->idx; int idx = sc->idx;
u_int sx, sy; u_int sx, sy, xpixel, ypixel;
spawn_log(__func__, sc); spawn_log(__func__, sc);
@@ -153,8 +155,9 @@ spawn_window(struct spawn_context *sc, char **cause)
xasprintf(cause, "couldn't add window %d", idx); xasprintf(cause, "couldn't add window %d", idx);
return (NULL); return (NULL);
} }
default_window_size(s, NULL, &sx, &sy, -1); default_window_size(sc->c, s, NULL, &sx, &sy, &xpixel, &ypixel,
if ((w = window_create(sx, sy)) == NULL) { -1);
if ((w = window_create(sx, sy, xpixel, ypixel)) == NULL) {
winlink_remove(&s->windows, sc->wl); winlink_remove(&s->windows, sc->wl);
xasprintf(cause, "couldn't create window %d", idx); xasprintf(cause, "couldn't create window %d", idx);
return (NULL); return (NULL);
@@ -162,6 +165,7 @@ spawn_window(struct spawn_context *sc, char **cause)
if (s->curw == NULL) if (s->curw == NULL)
s->curw = sc->wl; s->curw = sc->wl;
sc->wl->session = s; sc->wl->session = s;
w->latest = sc->c;
winlink_set_window(sc->wl, w); winlink_set_window(sc->wl, w);
} else } else
w = NULL; w = NULL;
@@ -178,7 +182,8 @@ spawn_window(struct spawn_context *sc, char **cause)
/* Set the name of the new window. */ /* Set the name of the new window. */
if (~sc->flags & SPAWN_RESPAWN) { if (~sc->flags & SPAWN_RESPAWN) {
if (sc->name != NULL) { if (sc->name != NULL) {
w->name = xstrdup(sc->name); w->name = format_single(item, sc->name, c, s, NULL,
NULL);
options_set_number(w->options, "automatic-rename", 0); options_set_number(w->options, "automatic-rename", 0);
} else } else
w->name = xstrdup(default_window_name(w)); w->name = xstrdup(default_window_name(w));
@@ -214,9 +219,21 @@ spawn_pane(struct spawn_context *sc, char **cause)
u_int hlimit; u_int hlimit;
struct winsize ws; struct winsize ws;
sigset_t set, oldset; sigset_t set, oldset;
key_code key;
spawn_log(__func__, sc); spawn_log(__func__, sc);
/*
* Work out the current working directory. If respawning, use
* the pane's stored one unless specified.
*/
if (sc->cwd != NULL)
cwd = format_single(item, sc->cwd, c, s, NULL, NULL);
else if (~sc->flags & SPAWN_RESPAWN)
cwd = xstrdup(server_client_get_cwd(c, s));
else
cwd = NULL;
/* /*
* If we are respawning then get rid of the old process. Otherwise * If we are respawning then get rid of the old process. Otherwise
* either create a new cell or assign to the one we are given. * either create a new cell or assign to the one we are given.
@@ -227,6 +244,7 @@ spawn_pane(struct spawn_context *sc, char **cause)
window_pane_index(sc->wp0, &idx); window_pane_index(sc->wp0, &idx);
xasprintf(cause, "pane %s:%d.%u still active", xasprintf(cause, "pane %s:%d.%u still active",
s->name, sc->wl->idx, idx); s->name, sc->wl->idx, idx);
free(cwd);
return (NULL); return (NULL);
} }
if (sc->wp0->fd != -1) { if (sc->wp0->fd != -1) {
@@ -247,8 +265,8 @@ spawn_pane(struct spawn_context *sc, char **cause)
} }
/* /*
* Now we have a pane with nothing running in it ready for the new * Now we have a pane with nothing running in it ready for the new process.
* process. Work out the command and arguments. * Work out the command and arguments and store the working directory.
*/ */
if (sc->argc == 0 && (~sc->flags & SPAWN_RESPAWN)) { if (sc->argc == 0 && (~sc->flags & SPAWN_RESPAWN)) {
cmd = options_get_string(s->options, "default-command"); cmd = options_get_string(s->options, "default-command");
@@ -263,6 +281,10 @@ spawn_pane(struct spawn_context *sc, char **cause)
argc = sc->argc; argc = sc->argc;
argv = sc->argv; argv = sc->argv;
} }
if (cwd != NULL) {
free(new_wp->cwd);
new_wp->cwd = cwd;
}
/* /*
* Replace the stored arguments if there are new ones. If not, the * Replace the stored arguments if there are new ones. If not, the
@@ -274,21 +296,6 @@ spawn_pane(struct spawn_context *sc, char **cause)
new_wp->argv = cmd_copy_argv(argc, argv); new_wp->argv = cmd_copy_argv(argc, argv);
} }
/*
* Work out the current working directory. If respawning, use
* the pane's stored one unless specified.
*/
if (sc->cwd != NULL)
cwd = format_single(item, sc->cwd, c, s, NULL, NULL);
else if (~sc->flags & SPAWN_RESPAWN)
cwd = xstrdup(server_client_get_cwd(c, s));
else
cwd = NULL;
if (cwd != NULL) {
free(new_wp->cwd);
new_wp->cwd = cwd;
}
/* Create an environment for this pane. */ /* Create an environment for this pane. */
child = environ_for_session(s, 0); child = environ_for_session(s, 0);
if (sc->environ != NULL) if (sc->environ != NULL)
@@ -334,6 +341,8 @@ spawn_pane(struct spawn_context *sc, char **cause)
memset(&ws, 0, sizeof ws); memset(&ws, 0, sizeof ws);
ws.ws_col = screen_size_x(&new_wp->base); ws.ws_col = screen_size_x(&new_wp->base);
ws.ws_row = screen_size_y(&new_wp->base); ws.ws_row = screen_size_y(&new_wp->base);
ws.ws_xpixel = w->xpixel * ws.ws_col;
ws.ws_ypixel = w->ypixel * ws.ws_row;
/* Block signals until fork has completed. */ /* Block signals until fork has completed. */
sigfillset(&set); sigfillset(&set);
@@ -375,13 +384,17 @@ spawn_pane(struct spawn_context *sc, char **cause)
/* /*
* Update terminal escape characters from the session if available and * Update terminal escape characters from the session if available and
* force VERASE to tmux's \177. * force VERASE to tmux's backspace.
*/ */
if (tcgetattr(STDIN_FILENO, &now) != 0) if (tcgetattr(STDIN_FILENO, &now) != 0)
_exit(1); _exit(1);
if (s->tio != NULL) if (s->tio != NULL)
memcpy(now.c_cc, s->tio->c_cc, sizeof now.c_cc); memcpy(now.c_cc, s->tio->c_cc, sizeof now.c_cc);
now.c_cc[VERASE] = '\177'; key = options_get_number(global_options, "backspace");
if (key >= 0x7f)
now.c_cc[VERASE] = '\177';
else
now.c_cc[VERASE] = key;
if (tcsetattr(STDIN_FILENO, TCSANOW, &now) != 0) if (tcsetattr(STDIN_FILENO, TCSANOW, &now) != 0)
_exit(1); _exit(1);
@@ -424,6 +437,15 @@ spawn_pane(struct spawn_context *sc, char **cause)
_exit(1); _exit(1);
complete: complete:
#ifdef HAVE_UTEMPTER
if (~new_wp->flags & PANE_EMPTY) {
xasprintf(&cp, "tmux(%lu).%%%u", (long)getpid(), new_wp->id);
utempter_add_record(new_wp->fd, cp);
kill(getpid(), SIGCHLD);
free(cp);
}
#endif
new_wp->pipe_off = 0; new_wp->pipe_off = 0;
new_wp->flags &= ~PANE_EXITED; new_wp->flags &= ~PANE_EXITED;

View File

@@ -915,11 +915,17 @@ status_prompt_key(struct client *c, key_code key)
{ {
struct options *oo = c->session->options; struct options *oo = c->session->options;
char *s, *cp, word[64], prefix = '='; char *s, *cp, word[64], prefix = '=';
const char *histstr, *ws = NULL; const char *histstr, *ws = NULL, *keystring;
size_t size, n, off, idx, used; size_t size, n, off, idx, used;
struct utf8_data tmp, *first, *last, *ud; struct utf8_data tmp, *first, *last, *ud;
int keys; int keys;
if (c->prompt_flags & PROMPT_KEY) {
keystring = key_string_lookup_key(key);
c->prompt_inputcb(c, c->prompt_data, keystring, 1);
status_prompt_clear(c);
return (0);
}
size = utf8_strlen(c->prompt_buffer); size = utf8_strlen(c->prompt_buffer);
if (c->prompt_flags & PROMPT_NUMERIC) { if (c->prompt_flags & PROMPT_NUMERIC) {

39
style.c
View File

@@ -36,13 +36,15 @@ static struct style style_default = {
STYLE_ALIGN_DEFAULT, STYLE_ALIGN_DEFAULT,
STYLE_LIST_OFF, STYLE_LIST_OFF,
STYLE_RANGE_NONE, 0 STYLE_RANGE_NONE, 0,
STYLE_DEFAULT_BASE
}; };
/* /*
* Parse an embedded style of the form "fg=colour,bg=colour,bright,...". * Parse an embedded style of the form "fg=colour,bg=colour,bright,...". Note
* Note that this adds onto the given style, so it must have been initialized * that this adds onto the given style, so it must have been initialized
* alredy. * already.
*/ */
int int
style_parse(struct style *sy, const struct grid_cell *base, const char *in) style_parse(struct style *sy, const struct grid_cell *base, const char *in)
@@ -74,7 +76,11 @@ style_parse(struct style *sy, const struct grid_cell *base, const char *in)
sy->gc.bg = base->bg; sy->gc.bg = base->bg;
sy->gc.attr = base->attr; sy->gc.attr = base->attr;
sy->gc.flags = base->flags; sy->gc.flags = base->flags;
} else if (strcasecmp(tmp, "nolist") == 0) } else if (strcasecmp(tmp, "push-default") == 0)
sy->default_type = STYLE_DEFAULT_PUSH;
else if (strcasecmp(tmp, "pop-default") == 0)
sy->default_type = STYLE_DEFAULT_POP;
else if (strcasecmp(tmp, "nolist") == 0)
sy->list = STYLE_LIST_OFF; sy->list = STYLE_LIST_OFF;
else if (strncasecmp(tmp, "list=", 5) == 0) { else if (strncasecmp(tmp, "list=", 5) == 0) {
if (strcasecmp(tmp + 5, "on") == 0) if (strcasecmp(tmp + 5, "on") == 0)
@@ -218,6 +224,14 @@ style_tostring(struct style *sy)
tmp); tmp);
comma = ","; comma = ",";
} }
if (sy->default_type != STYLE_DEFAULT_BASE) {
if (sy->default_type == STYLE_DEFAULT_PUSH)
tmp = "push-default";
else if (sy->default_type == STYLE_DEFAULT_POP)
tmp = "pop-default";
off += xsnprintf(s + off, sizeof s - off, "%s%s", comma, tmp);
comma = ",";
}
if (sy->fill != 8) { if (sy->fill != 8) {
off += xsnprintf(s + off, sizeof s - off, "%sfill=%s", comma, off += xsnprintf(s + off, sizeof s - off, "%sfill=%s", comma,
colour_tostring(sy->fill)); colour_tostring(sy->fill));
@@ -257,21 +271,6 @@ style_apply(struct grid_cell *gc, struct options *oo, const char *name)
gc->attr |= sy->gc.attr; gc->attr |= sy->gc.attr;
} }
/* Apply a style, updating if default. */
void
style_apply_update(struct grid_cell *gc, struct options *oo, const char *name)
{
struct style *sy;
sy = options_get_style(oo, name);
if (sy->gc.fg != 8)
gc->fg = sy->gc.fg;
if (sy->gc.bg != 8)
gc->bg = sy->gc.bg;
if (sy->gc.attr != 0)
gc->attr |= sy->gc.attr;
}
/* Initialize style from cell. */ /* Initialize style from cell. */
void void
style_set(struct style *sy, const struct grid_cell *gc) style_set(struct style *sy, const struct grid_cell *gc)

406
tmux.1
View File

@@ -214,7 +214,6 @@ was given) and off.
Report the Report the
.Nm .Nm
version. version.
.Pp
.It Ar command Op Ar flags .It Ar command Op Ar flags
This specifies one of a set of commands used to control This specifies one of a set of commands used to control
.Nm , .Nm ,
@@ -296,6 +295,12 @@ Prompt to search for text in open windows.
Display some information about the current window. Display some information about the current window.
.It l .It l
Move to the previously selected window. Move to the previously selected window.
.It m
Mark the current pane (see
.Ic select-pane
.Fl m ) .
.It M
Clear the marked pane.
.It n .It n
Change to the next window. Change to the next window.
.It o .It o
@@ -306,12 +311,6 @@ Change to the previous window.
Briefly display pane indexes. Briefly display pane indexes.
.It r .It r
Force redraw of the attached client. Force redraw of the attached client.
.It m
Mark the current pane (see
.Ic select-pane
.Fl m ) .
.It M
Clear the marked pane.
.It s .It s
Select a new session for the attached client interactively. Select a new session for the attached client interactively.
.It t .It t
@@ -556,7 +555,7 @@ Braces may be enclosed inside braces, for example:
.Bd -literal -offset indent .Bd -literal -offset indent
bind x if-shell "true" { bind x if-shell "true" {
if-shell "true" { if-shell "true" {
display "true!" display "true!"
} }
} }
.Ed .Ed
@@ -938,7 +937,9 @@ If
is specified, any other clients attached to the session are detached. is specified, any other clients attached to the session are detached.
If If
.Fl x .Fl x
is given, send SIGHUP to the parent process of the client as well as is given, send
.Dv SIGHUP
to the parent process of the client as well as
detaching the client, typically causing it to exit. detaching the client, typically causing it to exit.
.Fl r .Fl r
signifies the client is read-only (only keys bound to the signifies the client is read-only (only keys bound to the
@@ -989,7 +990,9 @@ option kills all but the client given with
.Fl t . .Fl t .
If If
.Fl P .Fl P
is given, send SIGHUP to the parent process of the client, typically causing it is given, send
.Dv SIGHUP
to the parent process of the client, typically causing it
to exit. to exit.
With With
.Fl E , .Fl E ,
@@ -1156,7 +1159,7 @@ The
.Fl P .Fl P
option prints information about the new session after it has been created. option prints information about the new session after it has been created.
By default, it uses the format By default, it uses the format
.Ql #{session_name}: .Ql #{session_name}:\&
but a different format may be specified with but a different format may be specified with
.Fl F . .Fl F .
.Pp .Pp
@@ -1288,6 +1291,17 @@ shows the parsed commands and line numbers if possible.
Start the Start the
.Nm .Nm
server, if not already running, without creating any sessions. server, if not already running, without creating any sessions.
.Pp
Note that as by default the
.Nm
server will exit with no sessions, this is only useful if a session is created in
.Pa ~/.tmux.conf ,
.Ic exit-empty
is turned off, or another command is run as part of the same command sequence.
For example:
.Bd -literal -offset indent
$ tmux start \\; show -g
.Ed
.It Xo Ic suspend-client .It Xo Ic suspend-client
.Op Fl t Ar target-client .Op Fl t Ar target-client
.Xc .Xc
@@ -1296,7 +1310,7 @@ Suspend a client by sending
.Dv SIGTSTP .Dv SIGTSTP
(tty stop). (tty stop).
.It Xo Ic switch-client .It Xo Ic switch-client
.Op Fl Elnpr .Op Fl ElnprZ
.Op Fl c Ar target-client .Op Fl c Ar target-client
.Op Fl t Ar target-session .Op Fl t Ar target-session
.Op Fl T Ar key-table .Op Fl T Ar key-table
@@ -1313,7 +1327,10 @@ may refer to a pane (a target that contains
.Ql \&. .Ql \&.
or or
.Ql % ) , .Ql % ) ,
in which case the session, window and pane are all changed. to change session, window and pane.
In that case,
.Fl Z
keeps the window zoomed if it was zoomed.
If If
.Fl l , .Fl l ,
.Fl n .Fl n
@@ -1333,7 +1350,8 @@ is used,
option will not be applied. option will not be applied.
.Pp .Pp
.Fl T .Fl T
sets the client's key table; the next key from the client will be interpreted from sets the client's key table; the next key from the client will be interpreted
from
.Ar key-table . .Ar key-table .
This may be used to configure multiple prefix keys, or to bind commands to This may be used to configure multiple prefix keys, or to bind commands to
sequences of keys. sequences of keys.
@@ -1349,11 +1367,41 @@ bind-key -Troot a switch-client -Ttable1
.Ed .Ed
.El .El
.Sh WINDOWS AND PANES .Sh WINDOWS AND PANES
A Each window displayed by
.Nm .Nm
window may be in one of two modes. may be split into one or more
The default permits direct access to the terminal attached to the window. .Em panes ;
The other is copy mode, which permits a section of a window or its each pane takes up a certain area of the display and is a separate terminal.
A window may be split into panes using the
.Ic split-window
command.
Windows may be split horizontally (with the
.Fl h
flag) or vertically.
Panes may be resized with the
.Ic resize-pane
command (bound to
.Ql C-Up ,
.Ql C-Down
.Ql C-Left
and
.Ql C-Right
by default), the current pane may be changed with the
.Ic select-pane
command and the
.Ic rotate-window
and
.Ic swap-pane
commands may be used to swap panes without changing their position.
Panes are numbered beginning from zero in the order they are created.
.Pp
By default, a
.Nm
pane permits direct access to the terminal contained in the pane.
A pane may also be put into one of several modes:
.Bl -dash -offset indent
.It
Copy mode, which permits a section of a window or its
history to be copied to a history to be copied to a
.Em paste buffer .Em paste buffer
for later insertion into another window. for later insertion into another window.
@@ -1362,9 +1410,21 @@ This mode is entered with the
command, bound to command, bound to
.Ql \&[ .Ql \&[
by default. by default.
It is also entered when a command that produces output, such as .It
View mode, which is like copy mode but is entered when a command that produces
output, such as
.Ic list-keys , .Ic list-keys ,
is executed from a key binding. is executed from a key binding.
.It
Choose mode, which allows an item to be chosen from a list.
This may be a client, a session or window or pane, or a buffer.
This mode is entered with the
.Ic choose-buffer ,
.Ic choose-client
and
.Ic choose-tree
commands.
.El
.Pp .Pp
In copy mode an indicator is displayed in the top-right corner of the pane with In copy mode an indicator is displayed in the top-right corner of the pane with
the current position and the number of lines in the history. the current position and the number of lines in the history.
@@ -1405,6 +1465,7 @@ The following commands are supported in copy mode:
.It Li "copy-selection-no-clear [<prefix>]" Ta "" Ta "" .It Li "copy-selection-no-clear [<prefix>]" Ta "" Ta ""
.It Li "copy-selection-and-cancel [<prefix>]" Ta "Enter" Ta "M-w" .It Li "copy-selection-and-cancel [<prefix>]" Ta "Enter" Ta "M-w"
.It Li "cursor-down" Ta "j" Ta "Down" .It Li "cursor-down" Ta "j" Ta "Down"
.It Li "cursor-down-and-cancel" Ta "" Ta ""
.It Li "cursor-left" Ta "h" Ta "Left" .It Li "cursor-left" Ta "h" Ta "Left"
.It Li "cursor-right" Ta "l" Ta "Right" .It Li "cursor-right" Ta "l" Ta "Right"
.It Li "cursor-up" Ta "k" Ta "Up" .It Li "cursor-up" Ta "k" Ta "Up"
@@ -1526,37 +1587,7 @@ bind PageUp copy-mode -eu
.Ed .Ed
.El .El
.Pp .Pp
Each window displayed by A number of preset arrangements of panes are available, these are called layouts.
.Nm
may be split into one or more
.Em panes ;
each pane takes up a certain area of the display and is a separate terminal.
A window may be split into panes using the
.Ic split-window
command.
Windows may be split horizontally (with the
.Fl h
flag) or vertically.
Panes may be resized with the
.Ic resize-pane
command (bound to
.Ql C-Up ,
.Ql C-Down
.Ql C-Left
and
.Ql C-Right
by default), the current pane may be changed with the
.Ic select-pane
command and the
.Ic rotate-window
and
.Ic swap-pane
commands may be used to swap panes without changing their position.
Panes are numbered beginning from zero in the order they are created.
.Pp
A number of preset
.Em layouts
are available.
These may be selected with the These may be selected with the
.Ic select-layout .Ic select-layout
command or cycled with command or cycled with
@@ -1635,7 +1666,7 @@ By default, it uses the format
but a different format may be specified with but a different format may be specified with
.Fl F . .Fl F .
.It Xo Ic capture-pane .It Xo Ic capture-pane
.Op Fl aepPqCJ .Op Fl aepPqCJN
.Op Fl b Ar buffer-name .Op Fl b Ar buffer-name
.Op Fl E Ar end-line .Op Fl E Ar end-line
.Op Fl S Ar start-line .Op Fl S Ar start-line
@@ -1660,8 +1691,10 @@ is given, the output includes escape sequences for text and background
attributes. attributes.
.Fl C .Fl C
also escapes non-printable characters as octal \exxx. also escapes non-printable characters as octal \exxx.
.Fl N
preserves trailing spaces at each line's end and
.Fl J .Fl J
joins wrapped lines and preserves trailing spaces at each line's end. preserves trailing spaces and joins any wrapped lines.
.Fl P .Fl P
captures only any output that the pane has received that is the beginning of an captures only any output that the pane has received that is the beginning of an
as-yet incomplete escape sequence. as-yet incomplete escape sequence.
@@ -1680,7 +1713,7 @@ the end of the visible pane.
The default is to capture only the visible contents of the pane. The default is to capture only the visible contents of the pane.
.It Xo .It Xo
.Ic choose-client .Ic choose-client
.Op Fl NZ .Op Fl NrZ
.Op Fl F Ar format .Op Fl F Ar format
.Op Fl f Ar filter .Op Fl f Ar filter
.Op Fl O Ar sort-order .Op Fl O Ar sort-order
@@ -1709,7 +1742,8 @@ The following keys may be used in client mode:
.It Li "z" Ta "Suspend selected client" .It Li "z" Ta "Suspend selected client"
.It Li "Z" Ta "Suspend tagged clients" .It Li "Z" Ta "Suspend tagged clients"
.It Li "f" Ta "Enter a format to filter items" .It Li "f" Ta "Enter a format to filter items"
.It Li "O" Ta "Change sort order" .It Li "O" Ta "Change sort field"
.It Li "r" Ta "Reverse sort order"
.It Li "v" Ta "Toggle preview" .It Li "v" Ta "Toggle preview"
.It Li "q" Ta "Exit mode" .It Li "q" Ta "Exit mode"
.El .El
@@ -1724,12 +1758,14 @@ If
is not given, "detach-client -t '%%'" is used. is not given, "detach-client -t '%%'" is used.
.Pp .Pp
.Fl O .Fl O
specifies the initial sort order: one of specifies the initial sort field: one of
.Ql name , .Ql name ,
.Ql size , .Ql size ,
.Ql creation , .Ql creation ,
or or
.Ql activity . .Ql activity .
.Fl r
reverses the sort order.
.Fl f .Fl f
specifies an initial filter: the filter is a format - if it evaluates to zero, specifies an initial filter: the filter is a format - if it evaluates to zero,
the item in the list is not shown, otherwise it is shown. the item in the list is not shown, otherwise it is shown.
@@ -1741,7 +1777,7 @@ starts without the preview.
This command works only if at least one client is attached. This command works only if at least one client is attached.
.It Xo .It Xo
.Ic choose-tree .Ic choose-tree
.Op Fl GNswZ .Op Fl GNrswZ
.Op Fl F Ar format .Op Fl F Ar format
.Op Fl f Ar filter .Op Fl f Ar filter
.Op Fl O Ar sort-order .Op Fl O Ar sort-order
@@ -1773,7 +1809,8 @@ The following keys may be used in tree mode:
.It Li "C-t" Ta "Tag all items" .It Li "C-t" Ta "Tag all items"
.It Li "\&:" Ta "Run a command for each tagged item" .It Li "\&:" Ta "Run a command for each tagged item"
.It Li "f" Ta "Enter a format to filter items" .It Li "f" Ta "Enter a format to filter items"
.It Li "O" Ta "Change sort order" .It Li "O" Ta "Change sort field"
.It Li "r" Ta "Reverse sort order"
.It Li "v" Ta "Toggle preview" .It Li "v" Ta "Toggle preview"
.It Li "q" Ta "Exit mode" .It Li "q" Ta "Exit mode"
.El .El
@@ -1788,11 +1825,13 @@ If
is not given, "switch-client -t '%%'" is used. is not given, "switch-client -t '%%'" is used.
.Pp .Pp
.Fl O .Fl O
specifies the initial sort order: one of specifies the initial sort field: one of
.Ql index , .Ql index ,
.Ql name , .Ql name ,
or or
.Ql time . .Ql time .
.Fl r
reverses the sort order.
.Fl f .Fl f
specifies an initial filter: the filter is a format - if it evaluates to zero, specifies an initial filter: the filter is a format - if it evaluates to zero,
the item in the list is not shown, otherwise it is shown. the item in the list is not shown, otherwise it is shown.
@@ -1871,10 +1910,8 @@ zooms the pane.
.Pp .Pp
This command works only if at least one client is attached. This command works only if at least one client is attached.
.It Xo Ic join-pane .It Xo Ic join-pane
.Op Fl bdhv .Op Fl bdfhv
.Oo Fl l .Op Fl l Ar size
.Ar size |
.Fl p Ar percentage Oc
.Op Fl s Ar src-pane .Op Fl s Ar src-pane
.Op Fl t Ar dst-pane .Op Fl t Ar dst-pane
.Xc .Xc
@@ -1925,11 +1962,13 @@ The
option kills all but the window given with option kills all but the window given with
.Fl t . .Fl t .
.It Xo Ic last-pane .It Xo Ic last-pane
.Op Fl de .Op Fl deZ
.Op Fl t Ar target-window .Op Fl t Ar target-window
.Xc .Xc
.D1 (alias: Ic lastp ) .D1 (alias: Ic lastp )
Select the last (previously selected) pane. Select the last (previously selected) pane.
.Fl Z
keeps the window zoomed if it was zoomed.
.Fl e .Fl e
enables or enables or
.Fl d .Fl d
@@ -2009,9 +2048,7 @@ flag, see the
section. section.
.It Xo Ic move-pane .It Xo Ic move-pane
.Op Fl bdhv .Op Fl bdhv
.Oo Fl l .Op Fl l Ar size
.Ar size |
.Fl p Ar percentage Oc
.Op Fl s Ar src-pane .Op Fl s Ar src-pane
.Op Fl t Ar dst-pane .Op Fl t Ar dst-pane
.Xc .Xc
@@ -2220,8 +2257,14 @@ or
.Fl y . .Fl y .
The The
.Ar adjustment .Ar adjustment
is given in lines or cells (the default is 1). is given in lines or columns (the default is 1);
.Pp .Fl x
and
.Fl y
may be a given as a number of lines or columns or followed by
.Ql %
for a percentage of the window size (for example
.Ql -x 10% ) .
With With
.Fl Z , .Fl Z ,
the active pane is toggled between zoomed (occupying the whole of the window) the active pane is toggled between zoomed (occupying the whole of the window)
@@ -2311,7 +2354,7 @@ option has the same meaning as for the
.Ic new-window .Ic new-window
command. command.
.It Xo Ic rotate-window .It Xo Ic rotate-window
.Op Fl DU .Op Fl DUZ
.Op Fl t Ar target-window .Op Fl t Ar target-window
.Xc .Xc
.D1 (alias: Ic rotatew ) .D1 (alias: Ic rotatew )
@@ -2319,6 +2362,8 @@ Rotate the positions of the panes within a window, either upward (numerically
lower) with lower) with
.Fl U .Fl U
or downward (numerically higher). or downward (numerically higher).
.Fl Z
keeps the window zoomed if it was zoomed.
.It Xo Ic select-layout .It Xo Ic select-layout
.Op Fl Enop .Op Fl Enop
.Op Fl t Ar target-pane .Op Fl t Ar target-pane
@@ -2342,7 +2387,7 @@ applies the last set layout if possible (undoes the most recent layout change).
.Fl E .Fl E
spreads the current pane and any panes next to it out evenly. spreads the current pane and any panes next to it out evenly.
.It Xo Ic select-pane .It Xo Ic select-pane
.Op Fl DdeLlMmRU .Op Fl DdeLlMmRUZ
.Op Fl T Ar title .Op Fl T Ar title
.Op Fl t Ar target-pane .Op Fl t Ar target-pane
.Xc .Xc
@@ -2359,6 +2404,8 @@ or
.Fl U .Fl U
is used, respectively the pane below, to the left, to the right, or above the is used, respectively the pane below, to the left, to the right, or above the
target pane is used. target pane is used.
.Fl Z
keeps the window zoomed if it was zoomed.
.Fl l .Fl l
is the same as using the is the same as using the
.Ic last-pane .Ic last-pane
@@ -2409,9 +2456,7 @@ the command behaves like
.Op Fl bdfhIvP .Op Fl bdfhIvP
.Op Fl c Ar start-directory .Op Fl c Ar start-directory
.Op Fl e Ar environment .Op Fl e Ar environment
.Oo Fl l .Op Fl l Ar size
.Ar size |
.Fl p Ar percentage Oc
.Op Fl t Ar target-pane .Op Fl t Ar target-pane
.Op Ar shell-command .Op Ar shell-command
.Op Fl F Ar format .Op Fl F Ar format
@@ -2427,10 +2472,12 @@ a vertical split; if neither is specified,
is assumed. is assumed.
The The
.Fl l .Fl l
and option specifies the size of the new pane in lines (for vertical split) or in
.Fl p columns (for horizontal split);
options specify the size of the new pane in lines (for vertical split) or in .Ar size
cells (for horizontal split), or as a percentage, respectively. may be followed by
.Ql %
to specify a percentage of the available space.
The The
.Fl b .Fl b
option causes the new pane to be created to the left of or above option causes the new pane to be created to the left of or above
@@ -2464,7 +2511,7 @@ All other options have the same meaning as for the
.Ic new-window .Ic new-window
command. command.
.It Xo Ic swap-pane .It Xo Ic swap-pane
.Op Fl dDU .Op Fl dDUZ
.Op Fl s Ar src-pane .Op Fl s Ar src-pane
.Op Fl t Ar dst-pane .Op Fl t Ar dst-pane
.Xc .Xc
@@ -2481,7 +2528,9 @@ swaps with the next pane (after it numerically).
.Fl d .Fl d
instructs instructs
.Nm .Nm
not to change the active pane. not to change the active pane and
.Fl Z
keeps the window zoomed if it was zoomed.
.Pp .Pp
If If
.Fl s .Fl s
@@ -2500,10 +2549,11 @@ This is similar to
except the source and destination windows are swapped. except the source and destination windows are swapped.
It is an error if no window exists at It is an error if no window exists at
.Ar src-window . .Ar src-window .
If
.Fl d
is given, the new window does not become the current window.
.Pp .Pp
Like If
.Ic swap-pane ,
if
.Fl s .Fl s
is omitted and a marked pane is present (see is omitted and a marked pane is present (see
.Ic select-pane .Ic select-pane
@@ -2571,10 +2621,15 @@ bind-key '"' split-window
bind-key "'" new-window bind-key "'" new-window
.Ed .Ed
.Pp .Pp
A command bound to the
.Em Any
key will execute for all keys which do not have a more specific binding.
.Pp
Commands related to key bindings are as follows: Commands related to key bindings are as follows:
.Bl -tag -width Ds .Bl -tag -width Ds
.It Xo Ic bind-key .It Xo Ic bind-key
.Op Fl nr .Op Fl nr
.Op Fl N Ar note
.Op Fl T Ar key-table .Op Fl T Ar key-table
.Ar key Ar command Op Ar arguments .Ar key Ar command Op Ar arguments
.Xc .Xc
@@ -2622,24 +2677,48 @@ The
flag indicates this key may repeat, see the flag indicates this key may repeat, see the
.Ic repeat-time .Ic repeat-time
option. option.
.Fl N
attaches a note to the key (shown with
.Ic list-keys
.Fl N ) .
.Pp .Pp
To view the default bindings and possible commands, see the To view the default bindings and possible commands, see the
.Ic list-keys .Ic list-keys
command. command.
.It Xo Ic list-keys .It Xo Ic list-keys
.Op Fl T Ar key-table .Op Fl 1N
.Op Fl P Ar prefix-string Fl T Ar key-table
.Op key
.Xc .Xc
.D1 (alias: Ic lsk ) .D1 (alias: Ic lsk )
List all key bindings. List all key bindings.
Without By default this shows all keys or any bindings for
.Fl T .Ar key
all key tables are printed. in the syntax of the
.Ic bind-key
command.
.Fl N
instead show keys and attached notes, i
.Ar key-table
if given or in the
.Em root
and
.Em prefix
key tables by default.
.Fl P
specifies a prefix to print before each key.
With With
.Fl 1
only the first matching key and note is shown.
.Pp
Without
.Fl N ,
.Fl T .Fl T
only prints only keys in
.Ar key-table . .Ar key-table ,
otherwise all key tables are printed.
.It Xo Ic send-keys .It Xo Ic send-keys
.Op Fl HlMRX .Op Fl FHlMRX
.Op Fl N Ar repeat-count .Op Fl N Ar repeat-count
.Op Fl t Ar target-pane .Op Fl t Ar target-pane
.Ar key Ar ... .Ar key Ar ...
@@ -2678,7 +2757,9 @@ the
.Sx WINDOWS AND PANES .Sx WINDOWS AND PANES
section. section.
.Fl N .Fl N
specifies a repeat count. specifies a repeat count and
.Fl F
expands formats in arguments where appropriate.
.It Xo Ic send-prefix .It Xo Ic send-prefix
.Op Fl 2 .Op Fl 2
.Op Fl t Ar target-pane .Op Fl t Ar target-pane
@@ -2716,7 +2797,7 @@ and
.Pp .Pp
The The
.Nm .Nm
server has a set of global options which do not apply to any particular server has a set of global server options which do not apply to any particular
window or session or pane. window or session or pane.
These are altered with the These are altered with the
.Ic set-option .Ic set-option
@@ -2890,6 +2971,10 @@ omitted to toggle).
.Pp .Pp
Available server options are: Available server options are:
.Bl -tag -width Ds .Bl -tag -width Ds
.It Ic backspace Ar key
Set the key sent by
.Nm
for backspace.
.It Ic buffer-limit Ar number .It Ic buffer-limit Ar number
Set the number of buffers; as new buffers are added to the top of the stack, Set the number of buffers; as new buffers are added to the top of the stack,
old ones are removed from the bottom if necessary to maintain this maximum old ones are removed from the bottom if necessary to maintain this maximum
@@ -3632,7 +3717,7 @@ see the
section. section.
.Pp .Pp
.It Xo Ic window-size .It Xo Ic window-size
.Ar largest | Ar smallest | Ar manual .Ar largest | Ar smallest | Ar manual | Ar latest
.Xc .Xc
Configure how Configure how
.Nm .Nm
@@ -3647,6 +3732,10 @@ If
the size of a new window is set from the the size of a new window is set from the
.Ic default-size .Ic default-size
option and windows are resized automatically. option and windows are resized automatically.
With
.Ar latest ,
.Nm
uses the size of the client that had the most recent activity.
See also the See also the
.Ic resize-window .Ic resize-window
command and the command and the
@@ -4058,9 +4147,15 @@ appended or prepended to the string if the length has been trimmed, for example
will append will append
.Ql ... .Ql ...
if the pane title is more than five characters. if the pane title is more than five characters.
Similarly,
.Ql p
pads the string to a given width, for example
.Ql #{p10:pane_title}
will result in a width of at least 10 characters.
A positive width pads on the left, a negative on the right.
.Pp .Pp
Prefixing a time variable with Prefixing a time variable with
.Ql t: .Ql t:\&
will convert it to a string, so if will convert it to a string, so if
.Ql #{window_activity} .Ql #{window_activity}
gives gives
@@ -4069,34 +4164,34 @@ gives
gives gives
.Ql Sun Oct 25 09:25:02 2015 . .Ql Sun Oct 25 09:25:02 2015 .
The The
.Ql b: .Ql b:\&
and and
.Ql d: .Ql d:\&
prefixes are prefixes are
.Xr basename 3 .Xr basename 3
and and
.Xr dirname 3 .Xr dirname 3
of the variable respectively. of the variable respectively.
.Ql q: .Ql q:\&
will escape will escape
.Xr sh 1 .Xr sh 1
special characters. special characters.
.Ql E: .Ql E:\&
will expand the format twice, for example will expand the format twice, for example
.Ql #{E:status-left} .Ql #{E:status-left}
is the result of expanding the content of the is the result of expanding the content of the
.Ic status-left .Ic status-left
option rather than the option itself. option rather than the option itself.
.Ql T: .Ql T:\&
is like is like
.Ql E: .Ql E:\&
but also expands but also expands
.Xr strftime 3 .Xr strftime 3
specifiers. specifiers.
.Ql S: , .Ql S:\& ,
.Ql W: .Ql W:\&
or or
.Ql P: .Ql P:\&
will loop over each session, window or pane and insert the format once will loop over each session, window or pane and insert the format once
for each. for each.
For windows and panes, two comma-separated formats may be given: For windows and panes, two comma-separated formats may be given:
@@ -4107,7 +4202,7 @@ For example, to get a list of windows formatted like the status line:
.Ed .Ed
.Pp .Pp
A prefix of the form A prefix of the form
.Ql s/foo/bar/: .Ql s/foo/bar/:\&
will substitute will substitute
.Ql foo .Ql foo
with with
@@ -4116,7 +4211,7 @@ throughout.
The first argument may be an extended regular expression and a final argument may be The first argument may be an extended regular expression and a final argument may be
.Ql i .Ql i
to ignore case, for example to ignore case, for example
.Ql s/a(.)/\e1x/i: .Ql s/a(.)/\e1x/i:\&
would change would change
.Ql abABab .Ql abABab
into into
@@ -4160,6 +4255,8 @@ The following variables are available, where appropriate:
.It Li "buffer_sample" Ta "" Ta "Sample of start of buffer" .It Li "buffer_sample" Ta "" Ta "Sample of start of buffer"
.It Li "buffer_size" Ta "" Ta "Size of the specified buffer in bytes" .It Li "buffer_size" Ta "" Ta "Size of the specified buffer in bytes"
.It Li "client_activity" Ta "" Ta "Time client last had activity" .It Li "client_activity" Ta "" Ta "Time client last had activity"
.It Li "client_cell_height" Ta "" Ta "Height of each client cell in pixels"
.It Li "client_cell_width" Ta "" Ta "Width of each client cell in pixels"
.It Li "client_control_mode" Ta "" Ta "1 if client is in control mode" .It Li "client_control_mode" Ta "" Ta "1 if client is in control mode"
.It Li "client_created" Ta "" Ta "Time client created" .It Li "client_created" Ta "" Ta "Time client created"
.It Li "client_discarded" Ta "" Ta "Bytes discarded when client behind" .It Li "client_discarded" Ta "" Ta "Bytes discarded when client behind"
@@ -4172,15 +4269,18 @@ The following variables are available, where appropriate:
.It Li "client_readonly" Ta "" Ta "1 if client is readonly" .It Li "client_readonly" Ta "" Ta "1 if client is readonly"
.It Li "client_session" Ta "" Ta "Name of the client's session" .It Li "client_session" Ta "" Ta "Name of the client's session"
.It Li "client_termname" Ta "" Ta "Terminal name of client" .It Li "client_termname" Ta "" Ta "Terminal name of client"
.It Li "client_termtype" Ta "" Ta "Terminal type of client"
.It Li "client_tty" Ta "" Ta "Pseudo terminal of client" .It Li "client_tty" Ta "" Ta "Pseudo terminal of client"
.It Li "client_utf8" Ta "" Ta "1 if client supports utf8" .It Li "client_utf8" Ta "" Ta "1 if client supports UTF-8"
.It Li "client_width" Ta "" Ta "Width of client" .It Li "client_width" Ta "" Ta "Width of client"
.It Li "client_written" Ta "" Ta "Bytes written to client" .It Li "client_written" Ta "" Ta "Bytes written to client"
.It Li "command" Ta "" Ta "Name of command in use, if any" .It Li "command" Ta "" Ta "Name of command in use, if any"
.It Li "command_list_alias" Ta "" Ta "Command alias if listing commands" .It Li "command_list_alias" Ta "" Ta "Command alias if listing commands"
.It Li "command_list_name" Ta "" Ta "Command name if listing commands" .It Li "command_list_name" Ta "" Ta "Command name if listing commands"
.It Li "command_list_usage" Ta "" Ta "Command usage if listing commands" .It Li "command_list_usage" Ta "" Ta "Command usage if listing commands"
.It Li "copy_cursor_line" Ta "" Ta "Line the cursor is on in copy mode"
.It Li "copy_cursor_word" Ta "" Ta "Word under cursor in copy mode"
.It Li "copy_cursor_x" Ta "" Ta "Cursor X position in copy mode"
.It Li "copy_cursor_y" Ta "" Ta "Cursor Y position in copy mode"
.It Li "cursor_character" Ta "" Ta "Character at cursor in pane" .It Li "cursor_character" Ta "" Ta "Character at cursor in pane"
.It Li "cursor_flag" Ta "" Ta "Pane cursor flag" .It Li "cursor_flag" Ta "" Ta "Pane cursor flag"
.It Li "cursor_x" Ta "" Ta "Cursor X position in pane" .It Li "cursor_x" Ta "" Ta "Cursor X position in pane"
@@ -4221,7 +4321,7 @@ The following variables are available, where appropriate:
.It Li "pane_current_path" Ta "" Ta "Current path if available" .It Li "pane_current_path" Ta "" Ta "Current path if available"
.It Li "pane_dead" Ta "" Ta "1 if pane is dead" .It Li "pane_dead" Ta "" Ta "1 if pane is dead"
.It Li "pane_dead_status" Ta "" Ta "Exit status of process in dead pane" .It Li "pane_dead_status" Ta "" Ta "Exit status of process in dead pane"
.It Li "pane_format" Ta "" Ta "1 if format is for a pane (not assuming the current)" .It Li "pane_format" Ta "" Ta "1 if format is for a pane"
.It Li "pane_height" Ta "" Ta "Height of pane" .It Li "pane_height" Ta "" Ta "Height of pane"
.It Li "pane_id" Ta "#D" Ta "Unique pane ID" .It Li "pane_id" Ta "#D" Ta "Unique pane ID"
.It Li "pane_in_mode" Ta "" Ta "1 if pane is in a mode" .It Li "pane_in_mode" Ta "" Ta "1 if pane is in a mode"
@@ -4231,6 +4331,7 @@ The following variables are available, where appropriate:
.It Li "pane_marked" Ta "" Ta "1 if this is the marked pane" .It Li "pane_marked" Ta "" Ta "1 if this is the marked pane"
.It Li "pane_marked_set" Ta "" Ta "1 if a marked pane is set" .It Li "pane_marked_set" Ta "" Ta "1 if a marked pane is set"
.It Li "pane_mode" Ta "" Ta "Name of pane mode, if any" .It Li "pane_mode" Ta "" Ta "Name of pane mode, if any"
.It Li "pane_path" Ta "#T" Ta "Path of pane (can be set by application)"
.It Li "pane_pid" Ta "" Ta "PID of first process in pane" .It Li "pane_pid" Ta "" Ta "PID of first process in pane"
.It Li "pane_pipe" Ta "" Ta "1 if pane is being piped" .It Li "pane_pipe" Ta "" Ta "1 if pane is being piped"
.It Li "pane_right" Ta "" Ta "Right of pane" .It Li "pane_right" Ta "" Ta "Right of pane"
@@ -4238,7 +4339,7 @@ The following variables are available, where appropriate:
.It Li "pane_start_command" Ta "" Ta "Command pane started with" .It Li "pane_start_command" Ta "" Ta "Command pane started with"
.It Li "pane_synchronized" Ta "" Ta "1 if pane is synchronized" .It Li "pane_synchronized" Ta "" Ta "1 if pane is synchronized"
.It Li "pane_tabs" Ta "" Ta "Pane tab positions" .It Li "pane_tabs" Ta "" Ta "Pane tab positions"
.It Li "pane_title" Ta "#T" Ta "Title of pane" .It Li "pane_title" Ta "#T" Ta "Title of pane (can be set by application)"
.It Li "pane_top" Ta "" Ta "Top of pane" .It Li "pane_top" Ta "" Ta "Top of pane"
.It Li "pane_tty" Ta "" Ta "Pseudo terminal of pane" .It Li "pane_tty" Ta "" Ta "Pseudo terminal of pane"
.It Li "pane_width" Ta "" Ta "Width of pane" .It Li "pane_width" Ta "" Ta "Width of pane"
@@ -4247,14 +4348,22 @@ The following variables are available, where appropriate:
.It Li "scroll_position" Ta "" Ta "Scroll position in copy mode" .It Li "scroll_position" Ta "" Ta "Scroll position in copy mode"
.It Li "scroll_region_lower" Ta "" Ta "Bottom of scroll region in pane" .It Li "scroll_region_lower" Ta "" Ta "Bottom of scroll region in pane"
.It Li "scroll_region_upper" Ta "" Ta "Top of scroll region in pane" .It Li "scroll_region_upper" Ta "" Ta "Top of scroll region in pane"
.It Li "selection_end_x" Ta "" Ta "X position of the end of the selection"
.It Li "selection_end_y" Ta "" Ta "Y position of the end of the selection"
.It Li "selection_present" Ta "" Ta "1 if selection started in copy mode" .It Li "selection_present" Ta "" Ta "1 if selection started in copy mode"
.It Li "selection_start_x" Ta "" Ta "X position of the start of the selection"
.It Li "selection_start_y" Ta "" Ta "Y position of the start of the selection"
.It Li "session_activity" Ta "" Ta "Time of session last activity" .It Li "session_activity" Ta "" Ta "Time of session last activity"
.It Li "session_alerts" Ta "" Ta "List of window indexes with alerts" .It Li "session_alerts" Ta "" Ta "List of window indexes with alerts"
.It Li "session_attached" Ta "" Ta "Number of clients session is attached to" .It Li "session_attached" Ta "" Ta "Number of clients session is attached to"
.It Li "session_attached_list" Ta "" Ta "List of clients session is attached to"
.It Li "session_created" Ta "" Ta "Time session created" .It Li "session_created" Ta "" Ta "Time session created"
.It Li "session_format" Ta "" Ta "1 if format is for a session (not assuming the current)" .It Li "session_format" Ta "" Ta "1 if format is for a session"
.It Li "session_group" Ta "" Ta "Name of session group" .It Li "session_group" Ta "" Ta "Name of session group"
.It Li "session_group_attached" Ta "" Ta "Number of clients sessions in group are attached to"
.It Li "session_group_attached_list" Ta "" Ta "List of clients sessions in group are attached to"
.It Li "session_group_list" Ta "" Ta "List of sessions in group" .It Li "session_group_list" Ta "" Ta "List of sessions in group"
.It Li "session_group_many_attached" Ta "" Ta "1 if multiple clients attached to sessions in group"
.It Li "session_group_size" Ta "" Ta "Size of session group" .It Li "session_group_size" Ta "" Ta "Size of session group"
.It Li "session_grouped" Ta "" Ta "1 if session in a group" .It Li "session_grouped" Ta "" Ta "1 if session in a group"
.It Li "session_id" Ta "" Ta "Unique session ID" .It Li "session_id" Ta "" Ta "Unique session ID"
@@ -4267,19 +4376,28 @@ The following variables are available, where appropriate:
.It Li "start_time" Ta "" Ta "Server start time" .It Li "start_time" Ta "" Ta "Server start time"
.It Li "version" Ta "" Ta "Server version" .It Li "version" Ta "" Ta "Server version"
.It Li "window_active" Ta "" Ta "1 if window active" .It Li "window_active" Ta "" Ta "1 if window active"
.It Li "window_active_clients" Ta "" Ta "Number of clients viewing this window"
.It Li "window_active_clients_list" Ta "" Ta "List of clients viewing this window"
.It Li "window_active_sessions" Ta "" Ta "Number of sessions on which this window is active"
.It Li "window_active_sessions_list" Ta "" Ta "List of sessions on which this window is active"
.It Li "window_activity" Ta "" Ta "Time of window last activity" .It Li "window_activity" Ta "" Ta "Time of window last activity"
.It Li "window_activity_flag" Ta "" Ta "1 if window has activity" .It Li "window_activity_flag" Ta "" Ta "1 if window has activity"
.It Li "window_bell_flag" Ta "" Ta "1 if window has bell" .It Li "window_bell_flag" Ta "" Ta "1 if window has bell"
.It Li "window_bigger" Ta "" Ta "1 if window is larger than client" .It Li "window_bigger" Ta "" Ta "1 if window is larger than client"
.It Li "window_cell_height" Ta "" Ta "Height of each cell in pixels"
.It Li "window_cell_width" Ta "" Ta "Width of each cell in pixels"
.It Li "window_end_flag" Ta "" Ta "1 if window has the highest index" .It Li "window_end_flag" Ta "" Ta "1 if window has the highest index"
.It Li "window_flags" Ta "#F" Ta "Window flags" .It Li "window_flags" Ta "#F" Ta "Window flags"
.It Li "window_format" Ta "" Ta "1 if format is for a window (not assuming the current)" .It Li "window_format" Ta "" Ta "1 if format is for a window"
.It Li "window_height" Ta "" Ta "Height of window" .It Li "window_height" Ta "" Ta "Height of window"
.It Li "window_id" Ta "" Ta "Unique window ID" .It Li "window_id" Ta "" Ta "Unique window ID"
.It Li "window_index" Ta "#I" Ta "Index of window" .It Li "window_index" Ta "#I" Ta "Index of window"
.It Li "window_last_flag" Ta "" Ta "1 if window is the last used" .It Li "window_last_flag" Ta "" Ta "1 if window is the last used"
.It Li "window_layout" Ta "" Ta "Window layout description, ignoring zoomed window panes" .It Li "window_layout" Ta "" Ta "Window layout description, ignoring zoomed window panes"
.It Li "window_linked" Ta "" Ta "1 if window is linked across sessions" .It Li "window_linked" Ta "" Ta "1 if window is linked across sessions"
.It Li "window_linked_sessions" Ta "" Ta "Number of sessions this window is linked to"
.It Li "window_linked_sessions_list" Ta "" Ta "List of sessions this window is linked to"
.It Li "window_marked_flag" Ta "" Ta "1 if window contains the marked pane"
.It Li "window_name" Ta "#W" Ta "Name of window" .It Li "window_name" Ta "#W" Ta "Name of window"
.It Li "window_offset_x" Ta "" Ta "X offset into window if larger than client" .It Li "window_offset_x" Ta "" Ta "X offset into window if larger than client"
.It Li "window_offset_y" Ta "" Ta "Y offset into window if larger than client" .It Li "window_offset_y" Ta "" Ta "Y offset into window if larger than client"
@@ -4299,7 +4417,7 @@ interface, for example
.Ic status-style .Ic status-style
for the status line. for the status line.
In addition, embedded styles may be specified in format options, such as In addition, embedded styles may be specified in format options, such as
.Ic status-left-format , .Ic status-left ,
by enclosing them in by enclosing them in
.Ql #[ .Ql #[
and and
@@ -4307,7 +4425,9 @@ and
.Pp .Pp
A style may be the single term A style may be the single term
.Ql default .Ql default
to specify the default style (which may inherit from another option) or a space to specify the default style (which may come from an option, for example
.Ic status-style
in the status line) or a space
or comma separated list of the following: or comma separated list of the following:
.Bl -tag -width Ds .Bl -tag -width Ds
.It Ic fg=colour .It Ic fg=colour
@@ -4386,6 +4506,20 @@ and
.Ic list=right-marker .Ic list=right-marker
mark the text to be used to mark that text has been trimmed from the left or mark the text to be used to mark that text has been trimmed from the left or
right of the list if there is not enough space. right of the list if there is not enough space.
.It Xo Ic push-default ,
.Ic pop-default
.Xc
Store the current colours and attributes as the default or reset to the previous
default.
A
.Ic push-default
affects any subsequent use of the
.Ic default
term until a
.Ic pop-default .
Only one default may be pushed (each
.Ic push-default
replaces the previous saved default).
.It Xo Ic range=left , .It Xo Ic range=left ,
.Ic range=right , .Ic range=right ,
.Ic range=window|X , .Ic range=window|X ,
@@ -4602,7 +4736,7 @@ session option.
Commands related to the status line are as follows: Commands related to the status line are as follows:
.Bl -tag -width Ds .Bl -tag -width Ds
.It Xo Ic command-prompt .It Xo Ic command-prompt
.Op Fl 1Ni .Op Fl 1ikN
.Op Fl I Ar inputs .Op Fl I Ar inputs
.Op Fl p Ar prompts .Op Fl p Ar prompts
.Op Fl t Ar target-client .Op Fl t Ar target-client
@@ -4652,6 +4786,10 @@ but any quotation marks are escaped.
.Fl 1 .Fl 1
makes the prompt only accept one key press, in this case the resulting input makes the prompt only accept one key press, in this case the resulting input
is a single character. is a single character.
.Fl k
is like
.Fl 1
but the key press is translated to a key name.
.Fl N .Fl N
makes the prompt only accept numeric key presses. makes the prompt only accept numeric key presses.
.Fl i .Fl i
@@ -4815,7 +4953,7 @@ When the
option is reached, the oldest automatically named buffer is deleted. option is reached, the oldest automatically named buffer is deleted.
Explicitly named buffers are not subject to Explicitly named buffers are not subject to
.Ic buffer-limit .Ic buffer-limit
and may be deleted with and may be deleted with the
.Ic delete-buffer .Ic delete-buffer
command. command.
.Pp .Pp
@@ -4842,7 +4980,7 @@ The buffer commands are as follows:
.Bl -tag -width Ds .Bl -tag -width Ds
.It Xo .It Xo
.Ic choose-buffer .Ic choose-buffer
.Op Fl NZ .Op Fl NZr
.Op Fl F Ar format .Op Fl F Ar format
.Op Fl f Ar filter .Op Fl f Ar filter
.Op Fl O Ar sort-order .Op Fl O Ar sort-order
@@ -4869,7 +5007,8 @@ The following keys may be used in buffer mode:
.It Li "d" Ta "Delete selected buffer" .It Li "d" Ta "Delete selected buffer"
.It Li "D" Ta "Delete tagged buffers" .It Li "D" Ta "Delete tagged buffers"
.It Li "f" Ta "Enter a format to filter items" .It Li "f" Ta "Enter a format to filter items"
.It Li "O" Ta "Change sort order" .It Li "O" Ta "Change sort field"
.It Li "r" Ta "Reverse sort order"
.It Li "v" Ta "Toggle preview" .It Li "v" Ta "Toggle preview"
.It Li "q" Ta "Exit mode" .It Li "q" Ta "Exit mode"
.El .El
@@ -4884,11 +5023,13 @@ If
is not given, "paste-buffer -b '%%'" is used. is not given, "paste-buffer -b '%%'" is used.
.Pp .Pp
.Fl O .Fl O
specifies the initial sort order: one of specifies the initial sort field: one of
.Ql time , .Ql time ,
.Ql name .Ql name
or or
.Ql size . .Ql size .
.Fl r
reverses the sort order.
.Fl f .Fl f
specifies an initial filter: the filter is a format - if it evaluates to zero, specifies an initial filter: the filter is a format - if it evaluates to zero,
the item in the list is not shown, otherwise it is shown. the item in the list is not shown, otherwise it is shown.
@@ -5058,6 +5199,37 @@ channel are made to wait until the channel is unlocked with
.Ic wait-for .Ic wait-for
.Fl U . .Fl U .
.El .El
.Sh EXIT MESSAGES
When a
.Nm
client detaches, it prints a message.
This may be one of:
.Bl -tag -width Ds
.It [detached (from session ...)]
The client was detached normally.
.It [detached and SIGHUP]
The client was detached and its parent sent the
.Dv SIGHUP
signal (for example with
.Ic detach-client
.Fl P ) .
.It [lost tty]
The client's
.Xr tty 4
or
.Xr pty 4
was unexpectedly destroyed.
.It [terminated]
The client was killed with
.Dv SIGTERM .
.It [exited]
The server exited when it had no sessions.
.It [server exited]
The server exited when it received
.Dv SIGTERM .
.It [server exited unexpectedly]
The server crashed or otherwise exited without telling the client the reason.
.El
.Sh TERMINFO EXTENSIONS .Sh TERMINFO EXTENSIONS
.Nm .Nm
understands some unofficial extensions to understands some unofficial extensions to

16
tmux.c
View File

@@ -18,6 +18,7 @@
#include <sys/types.h> #include <sys/types.h>
#include <sys/stat.h> #include <sys/stat.h>
#include <sys/utsname.h>
#include <errno.h> #include <errno.h>
#include <event.h> #include <event.h>
@@ -127,6 +128,7 @@ make_label(const char *label, char **cause)
free(base); free(base);
goto fail; goto fail;
} }
free(base);
if (mkdir(resolved, S_IRWXU) != 0 && errno != EEXIST) if (mkdir(resolved, S_IRWXU) != 0 && errno != EEXIST)
goto fail; goto fail;
@@ -208,6 +210,12 @@ find_home(void)
return (home); return (home);
} }
const char *
getversion(void)
{
return TMUX_VERSION;
}
int int
main(int argc, char **argv) main(int argc, char **argv)
{ {
@@ -234,7 +242,7 @@ main(int argc, char **argv)
flags = 0; flags = 0;
label = path = NULL; label = path = NULL;
while ((opt = getopt(argc, argv, "2c:Cdf:lL:qS:uUVv")) != -1) { while ((opt = getopt(argc, argv, "2c:Cdf:lL:qS:uUvV")) != -1) {
switch (opt) { switch (opt) {
case '2': case '2':
flags |= CLIENT_256COLOURS; flags |= CLIENT_256COLOURS;
@@ -248,12 +256,12 @@ main(int argc, char **argv)
else else
flags |= CLIENT_CONTROL; flags |= CLIENT_CONTROL;
break; break;
case 'V':
printf("%s %s\n", getprogname(), VERSION);
exit(0);
case 'f': case 'f':
set_cfg_file(optarg); set_cfg_file(optarg);
break; break;
case 'V':
printf("%s %s\n", getprogname(), getversion());
exit(0);
case 'l': case 'l':
flags |= CLIENT_LOGIN; flags |= CLIENT_LOGIN;
break; break;

215
tmux.h
View File

@@ -62,9 +62,9 @@ struct winlink;
/* Client-server protocol version. */ /* Client-server protocol version. */
#define PROTOCOL_VERSION 8 #define PROTOCOL_VERSION 8
/* Default global configuration file. */ /* Default configuration files. */
#ifndef TMUX_CONF #ifndef TMUX_CONF
#define TMUX_CONF "/etc/tmux.conf" #define TMUX_CONF "/etc/tmux.conf:~/.tmux.conf"
#endif #endif
/* Minimum layout cell size, NOT including border lines. */ /* Minimum layout cell size, NOT including border lines. */
@@ -80,6 +80,10 @@ struct winlink;
/* Maximum size of data to hold from a pane. */ /* Maximum size of data to hold from a pane. */
#define READ_SIZE 4096 #define READ_SIZE 4096
/* Default pixel cell sizes. */
#define DEFAULT_XPIXEL 16
#define DEFAULT_YPIXEL 32
/* Attribute to make GCC check printf-like arguments. */ /* Attribute to make GCC check printf-like arguments. */
#define printflike(a, b) __attribute__ ((format (printf, a, b))) #define printflike(a, b) __attribute__ ((format (printf, a, b)))
@@ -476,13 +480,21 @@ enum msgtype {
MSG_RESIZE, MSG_RESIZE,
MSG_SHELL, MSG_SHELL,
MSG_SHUTDOWN, MSG_SHUTDOWN,
MSG_STDERR, MSG_OLDSTDERR, /* unused */
MSG_STDIN, MSG_OLDSTDIN, /* unused */
MSG_STDOUT, MSG_OLDSTDOUT, /* unused */
MSG_SUSPEND, MSG_SUSPEND,
MSG_UNLOCK, MSG_UNLOCK,
MSG_WAKEUP, MSG_WAKEUP,
MSG_EXEC, MSG_EXEC,
MSG_READ_OPEN = 300,
MSG_READ,
MSG_READ_DONE,
MSG_WRITE_OPEN,
MSG_WRITE,
MSG_WRITE_READY,
MSG_WRITE_CLOSE
}; };
/* /*
@@ -490,23 +502,41 @@ enum msgtype {
* *
* Don't forget to bump PROTOCOL_VERSION if any of these change! * Don't forget to bump PROTOCOL_VERSION if any of these change!
*/ */
struct msg_command_data { struct msg_command {
int argc; int argc;
}; /* followed by packed argv */ }; /* followed by packed argv */
struct msg_stdin_data { struct msg_read_open {
ssize_t size; int stream;
char data[BUFSIZ]; int fd;
}; /* followed by path */
struct msg_read_data {
int stream;
}; };
struct msg_stdout_data { struct msg_read_done {
ssize_t size; int stream;
char data[BUFSIZ]; int error;
}; };
struct msg_stderr_data { struct msg_write_open {
ssize_t size; int stream;
char data[BUFSIZ]; int fd;
int flags;
}; /* followed by path */
struct msg_write_data {
int stream;
}; /* followed by data */
struct msg_write_ready {
int stream;
int error;
};
struct msg_write_close {
int stream;
}; };
/* Mode keys. */ /* Mode keys. */
@@ -682,6 +712,13 @@ struct style_range {
}; };
TAILQ_HEAD(style_ranges, style_range); TAILQ_HEAD(style_ranges, style_range);
/* Style default. */
enum style_default_type {
STYLE_DEFAULT_BASE,
STYLE_DEFAULT_PUSH,
STYLE_DEFAULT_POP
};
/* Style option. */ /* Style option. */
struct style { struct style {
struct grid_cell gc; struct grid_cell gc;
@@ -692,6 +729,8 @@ struct style {
enum style_range_type range_type; enum style_range_type range_type;
u_int range_argument; u_int range_argument;
enum style_default_type default_type;
}; };
/* Virtual screen. */ /* Virtual screen. */
@@ -699,6 +738,7 @@ struct screen_sel;
struct screen_titles; struct screen_titles;
struct screen { struct screen {
char *title; char *title;
char *path;
struct screen_titles *titles; struct screen_titles *titles;
struct grid *grid; /* grid data */ struct grid *grid; /* grid data */
@@ -845,6 +885,7 @@ struct window_pane {
#define PANE_STATUSDRAWN 0x400 #define PANE_STATUSDRAWN 0x400
#define PANE_EMPTY 0x800 #define PANE_EMPTY 0x800
#define PANE_STYLECHANGED 0x1000 #define PANE_STYLECHANGED 0x1000
#define PANE_RESIZED 0x2000
int argc; int argc;
char **argv; char **argv;
@@ -897,6 +938,7 @@ RB_HEAD(window_pane_tree, window_pane);
/* Window structure. */ /* Window structure. */
struct window { struct window {
u_int id; u_int id;
void *latest;
char *name; char *name;
struct event name_event; struct event name_event;
@@ -918,12 +960,15 @@ struct window {
u_int sx; u_int sx;
u_int sy; u_int sy;
u_int xpixel;
u_int ypixel;
int flags; int flags;
#define WINDOW_BELL 0x1 #define WINDOW_BELL 0x1
#define WINDOW_ACTIVITY 0x2 #define WINDOW_ACTIVITY 0x2
#define WINDOW_SILENCE 0x4 #define WINDOW_SILENCE 0x4
#define WINDOW_ZOOMED 0x8 #define WINDOW_ZOOMED 0x8
#define WINDOW_WASZOOMED 0x10
#define WINDOW_ALERTFLAGS (WINDOW_BELL|WINDOW_ACTIVITY|WINDOW_SILENCE) #define WINDOW_ALERTFLAGS (WINDOW_BELL|WINDOW_ACTIVITY|WINDOW_SILENCE)
int alerts_queued; int alerts_queued;
@@ -961,6 +1006,7 @@ TAILQ_HEAD(winlink_stack, winlink);
#define WINDOW_SIZE_LARGEST 0 #define WINDOW_SIZE_LARGEST 0
#define WINDOW_SIZE_SMALLEST 1 #define WINDOW_SIZE_SMALLEST 1
#define WINDOW_SIZE_MANUAL 2 #define WINDOW_SIZE_MANUAL 2
#define WINDOW_SIZE_LATEST 3
/* Pane border status option. */ /* Pane border status option. */
#define PANE_STATUS_OFF 0 #define PANE_STATUS_OFF 0
@@ -1124,7 +1170,10 @@ struct tty_term {
struct tty_code *codes; struct tty_code *codes;
#define TERM_256COLOURS 0x1 #define TERM_256COLOURS 0x1
#define TERM_EARLYWRAP 0x2 #define TERM_NOXENL 0x2
#define TERM_DECSLRM 0x4
#define TERM_DECFRA 0x8
#define TERM_RGBCOLOURS 0x10
int flags; int flags;
LIST_ENTRY(tty_term) entry; LIST_ENTRY(tty_term) entry;
@@ -1133,9 +1182,12 @@ LIST_HEAD(tty_terms, tty_term);
struct tty { struct tty {
struct client *client; struct client *client;
struct event start_timer;
u_int sx; u_int sx;
u_int sy; u_int sy;
u_int xpixel;
u_int ypixel;
u_int cx; u_int cx;
u_int cy; u_int cy;
@@ -1179,20 +1231,13 @@ struct tty {
#define TTY_OPENED 0x20 #define TTY_OPENED 0x20
#define TTY_FOCUS 0x40 #define TTY_FOCUS 0x40
#define TTY_BLOCK 0x80 #define TTY_BLOCK 0x80
#define TTY_HAVEDA 0x100
#define TTY_HAVEDSR 0x200
int flags; int flags;
struct tty_term *term; struct tty_term *term;
char *term_name; char *term_name;
int term_flags; int term_flags;
enum {
TTY_VT100,
TTY_VT101,
TTY_VT102,
TTY_VT220,
TTY_VT320,
TTY_VT420,
TTY_UNKNOWN
} term_type;
u_int mouse_last_x; u_int mouse_last_x;
u_int mouse_last_y; u_int mouse_last_y;
@@ -1206,8 +1251,6 @@ struct tty {
struct event key_timer; struct event key_timer;
struct tty_key *key_tree; struct tty_key *key_tree;
}; };
#define TTY_TYPES \
{ "VT100", "VT101", "VT102", "VT220", "VT320", "VT420", "Unknown" }
/* TTY command context. */ /* TTY command context. */
struct tty_ctx { struct tty_ctx {
@@ -1216,8 +1259,8 @@ struct tty_ctx {
const struct grid_cell *cell; const struct grid_cell *cell;
int wrapped; int wrapped;
u_int num; u_int num;
void *ptr; void *ptr;
/* /*
* Cursor and region position before the screen was updated - this is * Cursor and region position before the screen was updated - this is
@@ -1446,6 +1489,29 @@ struct status_line {
struct status_line_entry entries[STATUS_LINES_LIMIT]; struct status_line_entry entries[STATUS_LINES_LIMIT];
}; };
/* File in client. */
typedef void (*client_file_cb) (struct client *, const char *, int, int,
struct evbuffer *, void *);
struct client_file {
struct client *c;
int references;
int stream;
char *path;
struct evbuffer *buffer;
struct bufferevent *event;
int fd;
int error;
int closed;
client_file_cb cb;
void *data;
RB_ENTRY (client_file) entry;
};
RB_HEAD(client_files, client_file);
/* Client connection. */ /* Client connection. */
typedef int (*prompt_input_cb)(struct client *, void *, const char *, int); typedef int (*prompt_input_cb)(struct client *, void *, const char *, int);
typedef void (*prompt_free_cb)(void *); typedef void (*prompt_free_cb)(void *);
@@ -1479,13 +1545,6 @@ struct client {
size_t discarded; size_t discarded;
size_t redraw; size_t redraw;
void (*stdin_callback)(struct client *, int, void *);
void *stdin_callback_data;
struct evbuffer *stdin_data;
int stdin_closed;
struct evbuffer *stdout_data;
struct evbuffer *stderr_data;
struct event repeat_timer; struct event repeat_timer;
struct event click_timer; struct event click_timer;
@@ -1526,6 +1585,10 @@ struct client {
CLIENT_REDRAWSTATUSALWAYS| \ CLIENT_REDRAWSTATUSALWAYS| \
CLIENT_REDRAWBORDERS| \ CLIENT_REDRAWBORDERS| \
CLIENT_REDRAWOVERLAY) CLIENT_REDRAWOVERLAY)
#define CLIENT_UNATTACHEDFLAGS \
(CLIENT_DEAD| \
CLIENT_SUSPENDED| \
CLIENT_DETACHING)
#define CLIENT_NOSIZEFLAGS \ #define CLIENT_NOSIZEFLAGS \
(CLIENT_DEAD| \ (CLIENT_DEAD| \
CLIENT_SUSPENDED| \ CLIENT_SUSPENDED| \
@@ -1552,6 +1615,7 @@ struct client {
#define PROMPT_NUMERIC 0x2 #define PROMPT_NUMERIC 0x2
#define PROMPT_INCREMENTAL 0x4 #define PROMPT_INCREMENTAL 0x4
#define PROMPT_NOFORMAT 0x8 #define PROMPT_NOFORMAT 0x8
#define PROMPT_KEY 0x10
int prompt_flags; int prompt_flags;
struct session *session; struct session *session;
@@ -1569,6 +1633,8 @@ struct client {
void *overlay_data; void *overlay_data;
struct event overlay_timer; struct event overlay_timer;
struct client_files files;
TAILQ_ENTRY(client) entry; TAILQ_ENTRY(client) entry;
}; };
TAILQ_HEAD(clients, client); TAILQ_HEAD(clients, client);
@@ -1577,6 +1643,7 @@ TAILQ_HEAD(clients, client);
struct key_binding { struct key_binding {
key_code key; key_code key;
struct cmd_list *cmdlist; struct cmd_list *cmdlist;
const char *note;
int flags; int flags;
#define KEY_BINDING_REPEAT 0x1 #define KEY_BINDING_REPEAT 0x1
@@ -1661,6 +1728,7 @@ struct spawn_context {
struct session *s; struct session *s;
struct winlink *wl; struct winlink *wl;
struct client *c;
struct window_pane *wp0; struct window_pane *wp0;
struct layout_cell *lc; struct layout_cell *lc;
@@ -1683,6 +1751,12 @@ struct spawn_context {
#define SPAWN_EMPTY 0x40 #define SPAWN_EMPTY 0x40
}; };
/* Mode tree sort order. */
struct mode_tree_sort_criteria {
u_int field;
int reversed;
};
/* tmux.c */ /* tmux.c */
extern struct options *global_options; extern struct options *global_options;
extern struct options *global_s_options; extern struct options *global_s_options;
@@ -1697,6 +1771,7 @@ int areshell(const char *);
void setblocking(int, int); void setblocking(int, int);
const char *find_cwd(void); const char *find_cwd(void);
const char *find_home(void); const char *find_home(void);
const char *getversion(void);
/* proc.c */ /* proc.c */
struct imsg; struct imsg;
@@ -1718,6 +1793,8 @@ extern struct client *cfg_client;
void start_cfg(void); void start_cfg(void);
int load_cfg(const char *, struct client *, struct cmdq_item *, int, int load_cfg(const char *, struct client *, struct cmdq_item *, int,
struct cmdq_item **); struct cmdq_item **);
int load_cfg_from_buffer(const void *, size_t, const char *,
struct client *, struct cmdq_item *, int, struct cmdq_item **);
void set_cfg_file(const char *); void set_cfg_file(const char *);
void printflike(1, 2) cfg_add_cause(const char *, ...); void printflike(1, 2) cfg_add_cause(const char *, ...);
void cfg_print_causes(struct cmdq_item *); void cfg_print_causes(struct cmdq_item *);
@@ -1769,6 +1846,8 @@ void format_defaults_pane(struct format_tree *,
void format_defaults_paste_buffer(struct format_tree *, void format_defaults_paste_buffer(struct format_tree *,
struct paste_buffer *); struct paste_buffer *);
void format_lost_client(struct client *); void format_lost_client(struct client *);
char *format_grid_word(struct grid *, u_int, u_int);
char *format_grid_line(struct grid *, u_int);
/* format-draw.c */ /* format-draw.c */
void format_draw(struct screen_write_ctx *, void format_draw(struct screen_write_ctx *,
@@ -1898,7 +1977,7 @@ void tty_putc(struct tty *, u_char);
void tty_putn(struct tty *, const void *, size_t, u_int); void tty_putn(struct tty *, const void *, size_t, u_int);
int tty_init(struct tty *, struct client *, int, char *); int tty_init(struct tty *, struct client *, int, char *);
void tty_resize(struct tty *); void tty_resize(struct tty *);
void tty_set_size(struct tty *, u_int, u_int); void tty_set_size(struct tty *, u_int, u_int, u_int, u_int);
void tty_start_tty(struct tty *); void tty_start_tty(struct tty *);
void tty_stop_tty(struct tty *); void tty_stop_tty(struct tty *);
void tty_set_title(struct tty *, const char *); void tty_set_title(struct tty *, const char *);
@@ -1908,7 +1987,7 @@ void tty_draw_line(struct tty *, struct window_pane *, struct screen *,
int tty_open(struct tty *, char **); int tty_open(struct tty *, char **);
void tty_close(struct tty *); void tty_close(struct tty *);
void tty_free(struct tty *); void tty_free(struct tty *);
void tty_set_type(struct tty *, int); void tty_set_flags(struct tty *, int);
void tty_write(void (*)(struct tty *, const struct tty_ctx *), void tty_write(void (*)(struct tty *, const struct tty_ctx *),
struct tty_ctx *); struct tty_ctx *);
void tty_cmd_alignmenttest(struct tty *, const struct tty_ctx *); void tty_cmd_alignmenttest(struct tty *, const struct tty_ctx *);
@@ -2033,6 +2112,8 @@ void cmd_parse_empty(struct cmd_parse_input *);
struct cmd_parse_result *cmd_parse_from_file(FILE *, struct cmd_parse_input *); struct cmd_parse_result *cmd_parse_from_file(FILE *, struct cmd_parse_input *);
struct cmd_parse_result *cmd_parse_from_string(const char *, struct cmd_parse_result *cmd_parse_from_string(const char *,
struct cmd_parse_input *); struct cmd_parse_input *);
struct cmd_parse_result *cmd_parse_from_buffer(const void *, size_t,
struct cmd_parse_input *);
struct cmd_parse_result *cmd_parse_from_arguments(int, char **, struct cmd_parse_result *cmd_parse_from_arguments(int, char **,
struct cmd_parse_input *); struct cmd_parse_input *);
@@ -2049,8 +2130,8 @@ struct cmdq_item *cmdq_get_command(struct cmd_list *, struct cmd_find_state *,
#define cmdq_get_callback(cb, data) cmdq_get_callback1(#cb, cb, data) #define cmdq_get_callback(cb, data) cmdq_get_callback1(#cb, cb, data)
struct cmdq_item *cmdq_get_callback1(const char *, cmdq_cb, void *); struct cmdq_item *cmdq_get_callback1(const char *, cmdq_cb, void *);
struct cmdq_item *cmdq_get_error(const char *); struct cmdq_item *cmdq_get_error(const char *);
void cmdq_insert_after(struct cmdq_item *, struct cmdq_item *); struct cmdq_item *cmdq_insert_after(struct cmdq_item *, struct cmdq_item *);
void cmdq_append(struct client *, struct cmdq_item *); struct cmdq_item *cmdq_append(struct client *, struct cmdq_item *);
void cmdq_insert_hook(struct session *, struct cmdq_item *, void cmdq_insert_hook(struct session *, struct cmdq_item *,
struct cmd_find_state *, const char *, ...); struct cmd_find_state *, const char *, ...);
void cmdq_continue(struct cmdq_item *); void cmdq_continue(struct cmdq_item *);
@@ -2075,7 +2156,8 @@ void key_bindings_unref_table(struct key_table *);
struct key_binding *key_bindings_get(struct key_table *, key_code); struct key_binding *key_bindings_get(struct key_table *, key_code);
struct key_binding *key_bindings_first(struct key_table *); struct key_binding *key_bindings_first(struct key_table *);
struct key_binding *key_bindings_next(struct key_table *, struct key_binding *); struct key_binding *key_bindings_next(struct key_table *, struct key_binding *);
void key_bindings_add(const char *, key_code, int, struct cmd_list *); void key_bindings_add(const char *, key_code, const char *, int,
struct cmd_list *);
void key_bindings_remove(const char *, key_code); void key_bindings_remove(const char *, key_code);
void key_bindings_remove_table(const char *); void key_bindings_remove_table(const char *);
void key_bindings_init(void); void key_bindings_init(void);
@@ -2092,6 +2174,23 @@ void alerts_reset_all(void);
void alerts_queue(struct window *, int); void alerts_queue(struct window *, int);
void alerts_check_session(struct session *); void alerts_check_session(struct session *);
/* file.c */
int file_cmp(struct client_file *, struct client_file *);
RB_PROTOTYPE(client_files, client_file, entry, file_cmp);
struct client_file *file_create(struct client *, int, client_file_cb, void *);
void file_free(struct client_file *);
void file_fire_done(struct client_file *);
void file_fire_read(struct client_file *);
int file_can_print(struct client *);
void printflike(2, 3) file_print(struct client *, const char *, ...);
void file_vprint(struct client *, const char *, va_list);
void file_print_buffer(struct client *, void *, size_t);
void printflike(2, 3) file_error(struct client *, const char *, ...);
void file_write(struct client *, const char *, int, const void *, size_t,
client_file_cb, void *);
void file_read(struct client *, const char *, client_file_cb, void *);
void file_push(struct client_file *);
/* server.c */ /* server.c */
extern struct tmuxproc *server_proc; extern struct tmuxproc *server_proc;
extern struct clients clients; extern struct clients clients;
@@ -2126,7 +2225,6 @@ void server_client_push_stdout(struct client *);
void server_client_push_stderr(struct client *); void server_client_push_stderr(struct client *);
void printflike(2, 3) server_client_add_message(struct client *, const char *, void printflike(2, 3) server_client_add_message(struct client *, const char *,
...); ...);
char *server_client_get_path(struct client *, const char *);
const char *server_client_get_cwd(struct client *, struct session *); const char *server_client_get_cwd(struct client *, struct session *);
/* server-fn.c */ /* server-fn.c */
@@ -2150,8 +2248,6 @@ void server_unlink_window(struct session *, struct winlink *);
void server_destroy_pane(struct window_pane *, int); void server_destroy_pane(struct window_pane *, int);
void server_destroy_session(struct session *); void server_destroy_session(struct session *);
void server_check_unattached(void); void server_check_unattached(void);
int server_set_stdin_callback(struct client *, void (*)(struct client *,
int, void *), void *, char **);
void server_unzoom_window(struct window *); void server_unzoom_window(struct window *);
/* status.c */ /* status.c */
@@ -2177,9 +2273,10 @@ void status_prompt_load_history(void);
void status_prompt_save_history(void); void status_prompt_save_history(void);
/* resize.c */ /* resize.c */
void resize_window(struct window *, u_int, u_int); void resize_window(struct window *, u_int, u_int, int, int);
void default_window_size(struct session *, struct window *, u_int *, void default_window_size(struct client *, struct session *, struct window *,
u_int *, int); u_int *, u_int *, u_int *, u_int *, int);
void recalculate_size(struct window *);
void recalculate_sizes(void); void recalculate_sizes(void);
/* input.c */ /* input.c */
@@ -2191,7 +2288,7 @@ void input_parse(struct window_pane *);
void input_parse_buffer(struct window_pane *, u_char *, size_t); void input_parse_buffer(struct window_pane *, u_char *, size_t);
/* input-key.c */ /* input-key.c */
void input_key(struct window_pane *, key_code, struct mouse_event *); int input_key(struct window_pane *, key_code, struct mouse_event *);
/* xterm-keys.c */ /* xterm-keys.c */
char *xterm_keys_lookup(key_code); char *xterm_keys_lookup(key_code);
@@ -2329,7 +2426,8 @@ void screen_free(struct screen *);
void screen_reset_tabs(struct screen *); void screen_reset_tabs(struct screen *);
void screen_set_cursor_style(struct screen *, u_int); void screen_set_cursor_style(struct screen *, u_int);
void screen_set_cursor_colour(struct screen *, const char *); void screen_set_cursor_colour(struct screen *, const char *);
void screen_set_title(struct screen *, const char *); int screen_set_title(struct screen *, const char *);
void screen_set_path(struct screen *, const char *);
void screen_push_title(struct screen *); void screen_push_title(struct screen *);
void screen_pop_title(struct screen *); void screen_pop_title(struct screen *);
void screen_resize(struct screen *, u_int, u_int, int); void screen_resize(struct screen *, u_int, u_int, int);
@@ -2369,7 +2467,7 @@ void winlink_stack_remove(struct winlink_stack *, struct winlink *);
struct window *window_find_by_id_str(const char *); struct window *window_find_by_id_str(const char *);
struct window *window_find_by_id(u_int); struct window *window_find_by_id(u_int);
void window_update_activity(struct window *); void window_update_activity(struct window *);
struct window *window_create(u_int, u_int); struct window *window_create(u_int, u_int, u_int, u_int);
void window_pane_set_event(struct window_pane *); void window_pane_set_event(struct window_pane *);
struct window_pane *window_get_active_at(struct window *, u_int, u_int); struct window_pane *window_get_active_at(struct window *, u_int, u_int);
struct window_pane *window_find_string(struct window *, const char *); struct window_pane *window_find_string(struct window *, const char *);
@@ -2380,9 +2478,12 @@ void window_redraw_active_switch(struct window *,
struct window_pane *); struct window_pane *);
struct window_pane *window_add_pane(struct window *, struct window_pane *, struct window_pane *window_add_pane(struct window *, struct window_pane *,
u_int, int); u_int, int);
void window_resize(struct window *, u_int, u_int); void window_resize(struct window *, u_int, u_int, int, int);
void window_pane_send_resize(struct window_pane *, int);
int window_zoom(struct window_pane *); int window_zoom(struct window_pane *);
int window_unzoom(struct window *); int window_unzoom(struct window *);
int window_push_zoom(struct window *, int);
int window_pop_zoom(struct window *);
void window_lost_pane(struct window *, struct window_pane *); void window_lost_pane(struct window *, struct window_pane *);
void window_remove_pane(struct window *, struct window_pane *); void window_remove_pane(struct window *, struct window_pane *);
struct window_pane *window_pane_at_index(struct window *, u_int); struct window_pane *window_pane_at_index(struct window *, u_int);
@@ -2410,7 +2511,7 @@ int window_pane_set_mode(struct window_pane *,
struct args *); struct args *);
void window_pane_reset_mode(struct window_pane *); void window_pane_reset_mode(struct window_pane *);
void window_pane_reset_mode_all(struct window_pane *); void window_pane_reset_mode_all(struct window_pane *);
void window_pane_key(struct window_pane *, struct client *, int window_pane_key(struct window_pane *, struct client *,
struct session *, struct winlink *, key_code, struct session *, struct winlink *, key_code,
struct mouse_event *); struct mouse_event *);
int window_pane_visible(struct window_pane *); int window_pane_visible(struct window_pane *);
@@ -2472,7 +2573,8 @@ u_int layout_set_next(struct window *);
u_int layout_set_previous(struct window *); u_int layout_set_previous(struct window *);
/* mode-tree.c */ /* mode-tree.c */
typedef void (*mode_tree_build_cb)(void *, u_int, uint64_t *, const char *); typedef void (*mode_tree_build_cb)(void *, struct mode_tree_sort_criteria *,
uint64_t *, const char *);
typedef void (*mode_tree_draw_cb)(void *, void *, struct screen_write_ctx *, typedef void (*mode_tree_draw_cb)(void *, void *, struct screen_write_ctx *,
u_int, u_int); u_int, u_int);
typedef int (*mode_tree_search_cb)(void *, void *, const char *); typedef int (*mode_tree_search_cb)(void *, void *, const char *);
@@ -2530,9 +2632,8 @@ char *default_window_name(struct window *);
char *parse_window_name(const char *); char *parse_window_name(const char *);
/* control.c */ /* control.c */
void control_callback(struct client *, int, void *); void control_start(struct client *);
void printflike(2, 3) control_write(struct client *, const char *, ...); void printflike(2, 3) control_write(struct client *, const char *, ...);
void control_write_buffer(struct client *, struct evbuffer *);
/* control-notify.c */ /* control-notify.c */
void control_notify_input(struct client *, struct window_pane *, void control_notify_input(struct client *, struct window_pane *,
@@ -2585,6 +2686,7 @@ void session_group_add(struct session_group *, struct session *);
void session_group_synchronize_to(struct session *); void session_group_synchronize_to(struct session *);
void session_group_synchronize_from(struct session *); void session_group_synchronize_from(struct session *);
u_int session_group_count(struct session_group *); 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_renumber_windows(struct session *);
/* utf8.c */ /* utf8.c */
@@ -2604,6 +2706,7 @@ struct utf8_data *utf8_fromcstr(const char *);
char *utf8_tocstr(struct utf8_data *); char *utf8_tocstr(struct utf8_data *);
u_int utf8_cstrwidth(const char *); u_int utf8_cstrwidth(const char *);
char *utf8_padcstr(const char *, u_int); char *utf8_padcstr(const char *, u_int);
char *utf8_rpadcstr(const char *, u_int);
int utf8_cstrhas(const char *, const struct utf8_data *); int utf8_cstrhas(const char *, const struct utf8_data *);
/* osdep-*.c */ /* osdep-*.c */
@@ -2641,8 +2744,6 @@ int style_parse(struct style *,const struct grid_cell *,
const char *style_tostring(struct style *); const char *style_tostring(struct style *);
void style_apply(struct grid_cell *, struct options *, void style_apply(struct grid_cell *, struct options *,
const char *); const char *);
void style_apply_update(struct grid_cell *, struct options *,
const char *);
int style_equal(struct style *, struct style *); int style_equal(struct style *, struct style *);
void style_set(struct style *, const struct grid_cell *); void style_set(struct style *, const struct grid_cell *);
void style_copy(struct style *, struct style *); void style_copy(struct style *, struct style *);

View File

@@ -52,6 +52,8 @@ static int tty_keys_clipboard(struct tty *, const char *, size_t,
size_t *); size_t *);
static int tty_keys_device_attributes(struct tty *, const char *, size_t, static int tty_keys_device_attributes(struct tty *, const char *, size_t,
size_t *); size_t *);
static int tty_keys_device_status_report(struct tty *, const char *,
size_t, size_t *);
/* Default raw keys. */ /* Default raw keys. */
struct tty_default_key_raw { struct tty_default_key_raw {
@@ -607,6 +609,17 @@ tty_keys_next(struct tty *tty)
goto partial_key; goto partial_key;
} }
/* Is this a device status report response? */
switch (tty_keys_device_status_report(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;
}
/* Is this a mouse key press? */ /* Is this a mouse key press? */
switch (tty_keys_mouse(tty, buf, len, &size, &m)) { switch (tty_keys_mouse(tty, buf, len, &size, &m)) {
case 0: /* yes */ case 0: /* yes */
@@ -1001,13 +1014,14 @@ static int
tty_keys_device_attributes(struct tty *tty, const char *buf, size_t len, tty_keys_device_attributes(struct tty *tty, const char *buf, size_t len,
size_t *size) size_t *size)
{ {
struct client *c = tty->client; struct client *c = tty->client;
u_int i, a, b; u_int i, n = 0;
char tmp[64], *endptr; char tmp[64], *endptr, p[32] = { 0 }, *cp, *next;
static const char *types[] = TTY_TYPES; int flags = 0;
int type;
*size = 0; *size = 0;
if (tty->flags & TTY_HAVEDA)
return (-1);
/* First three bytes are always \033[?. */ /* First three bytes are always \033[?. */
if (buf[0] != '\033') if (buf[0] != '\033')
@@ -1035,39 +1049,81 @@ tty_keys_device_attributes(struct tty *tty, const char *buf, size_t len,
*size = 4 + i; *size = 4 + i;
/* Convert version numbers. */ /* Convert version numbers. */
a = strtoul(tmp, &endptr, 10); cp = tmp;
if (*endptr == ';') { while ((next = strsep(&cp, ";")) != NULL) {
b = strtoul(endptr + 1, &endptr, 10); p[n] = strtoul(next, &endptr, 10);
if (*endptr != '\0' && *endptr != ';') if (*endptr != '\0')
b = 0; p[n] = 0;
} else n++;
a = b = 0; }
/* Store terminal type. */ /* Set terminal flags. */
type = TTY_UNKNOWN; switch (p[0]) {
switch (a) { case 64: /* VT420 */
case 1: flags |= (TERM_DECFRA|TERM_DECSLRM);
if (b == 2)
type = TTY_VT100;
else if (b == 0)
type = TTY_VT101;
break;
case 6:
type = TTY_VT102;
break;
case 62:
type = TTY_VT220;
break;
case 63:
type = TTY_VT320;
break;
case 64:
type = TTY_VT420;
break; break;
} }
tty_set_type(tty, type); for (i = 1; i < n; i++)
log_debug("%s: DA feature: %d", c->name, p[i]);
log_debug("%s: received DA %.*s", c->name, (int)*size, buf);
tty_set_flags(tty, flags);
tty->flags |= TTY_HAVEDA;
return (0);
}
/*
* Handle device status report input. Returns 0 for success, -1 for failure, 1
* for partial.
*/
static int
tty_keys_device_status_report(struct tty *tty, const char *buf, size_t len,
size_t *size)
{
struct client *c = tty->client;
u_int i;
char tmp[64];
int flags = 0;
*size = 0;
if (tty->flags & TTY_HAVEDSR)
return (-1);
/* First three 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);
if (buf[2] != 'I' && buf[2] != 'T')
return (-1);
if (len == 3)
return (1);
/* Copy the rest up to a 'n'. */
for (i = 0; i < (sizeof tmp) - 1 && buf[2 + i] != 'n'; i++) {
if (2 + i == len)
return (1);
tmp[i] = buf[2 + i];
}
if (i == (sizeof tmp) - 1)
return (-1);
tmp[i] = '\0';
*size = 3 + i;
/* Set terminal flags. */
if (strncmp(tmp, "ITERM2 ", 7) == 0)
flags |= (TERM_DECSLRM|TERM_256COLOURS|TERM_RGBCOLOURS);
if (strncmp(tmp, "TMUX ", 5) == 0)
flags |= (TERM_256COLOURS|TERM_RGBCOLOURS);
log_debug("%s: received DSR %.*s", c->name, (int)*size, buf);
tty_set_flags(tty, flags);
tty->flags |= TTY_HAVEDSR;
log_debug("%s: received DA %.*s (%s)", c->name, (int)*size, buf,
types[type]);
return (0); return (0);
} }

View File

@@ -528,11 +528,16 @@ tty_term_find(char *name, int fd, char **cause)
goto error; goto error;
} }
/* Figure out if we have 256 colours (or more). */ /* Set flag if terminal has 256 colours. */
if (tty_term_number(term, TTYC_COLORS) >= 256 || if (tty_term_number(term, TTYC_COLORS) >= 256)
tty_term_has(term, TTYC_RGB))
term->flags |= TERM_256COLOURS; term->flags |= TERM_256COLOURS;
/* Set flag if terminal has RGB colours. */
if ((tty_term_flag(term, TTYC_TC) || tty_term_has(term, TTYC_RGB)) ||
(tty_term_has(term, TTYC_SETRGBF) &&
tty_term_has(term, TTYC_SETRGBB)))
term->flags |= TERM_RGBCOLOURS;
/* /*
* Terminals without xenl (eat newline glitch) wrap at at $COLUMNS - 1 * Terminals without xenl (eat newline glitch) wrap at at $COLUMNS - 1
* rather than $COLUMNS (the cursor can never be beyond $COLUMNS - 1). * rather than $COLUMNS (the cursor can never be beyond $COLUMNS - 1).
@@ -544,7 +549,7 @@ tty_term_find(char *name, int fd, char **cause)
* do the best possible. * do the best possible.
*/ */
if (!tty_term_flag(term, TTYC_XENL)) if (!tty_term_flag(term, TTYC_XENL))
term->flags |= TERM_EARLYWRAP; term->flags |= TERM_NOXENL;
/* Generate ACS table. If none is present, use nearest ASCII. */ /* Generate ACS table. If none is present, use nearest ASCII. */
memset(term->acs, 0, sizeof term->acs); memset(term->acs, 0, sizeof term->acs);
@@ -567,22 +572,7 @@ tty_term_find(char *name, int fd, char **cause)
code->type = TTYCODE_STRING; code->type = TTYCODE_STRING;
} }
/* /* Log the capabilities. */
* On terminals with RGB colour (Tc or RGB), fill in setrgbf and
* setrgbb if they are missing.
*/
if ((tty_term_flag(term, TTYC_TC) || tty_term_flag(term, TTYC_RGB)) &&
!tty_term_has(term, TTYC_SETRGBF) &&
!tty_term_has(term, TTYC_SETRGBB)) {
code = &term->codes[TTYC_SETRGBF];
code->value.string = xstrdup("\033[38;2;%p1%d;%p2%d;%p3%dm");
code->type = TTYCODE_STRING;
code = &term->codes[TTYC_SETRGBB];
code->value.string = xstrdup("\033[48;2;%p1%d;%p2%d;%p3%dm");
code->type = TTYCODE_STRING;
}
/* Log it. */
for (i = 0; i < tty_term_ncodes(); i++) for (i = 0; i < tty_term_ncodes(); i++)
log_debug("%s%s", name, tty_term_describe(term, i)); log_debug("%s%s", name, tty_term_describe(term, i));
@@ -642,7 +632,8 @@ tty_term_string2(struct tty_term *term, enum tty_code_code code, int a, int b)
} }
const char * const char *
tty_term_string3(struct tty_term *term, enum tty_code_code code, int a, int b, int c) tty_term_string3(struct tty_term *term, enum tty_code_code code, int a, int b,
int c)
{ {
return (tparm((char *) tty_term_string(term, code), a, b, c, 0, 0, 0, 0, 0, 0)); return (tparm((char *) tty_term_string(term, code), a, b, c, 0, 0, 0, 0, 0, 0));
} }

119
tty.c
View File

@@ -74,7 +74,7 @@ static void tty_default_attributes(struct tty *, struct window_pane *,
u_int); u_int);
#define tty_use_margin(tty) \ #define tty_use_margin(tty) \
((tty)->term_type == TTY_VT420) ((tty->term->flags|tty->term_flags) & TERM_DECSLRM)
#define tty_pane_full_width(tty, ctx) \ #define tty_pane_full_width(tty, ctx) \
((ctx)->xoff == 0 && screen_size_x((ctx)->wp->screen) >= (tty)->sx) ((ctx)->xoff == 0 && screen_size_x((ctx)->wp->screen) >= (tty)->sx)
@@ -115,9 +115,7 @@ tty_init(struct tty *tty, struct client *c, int fd, char *term)
tty->ccolour = xstrdup(""); tty->ccolour = xstrdup("");
tty->flags = 0; tty->flags = 0;
tty->term_flags = 0; tty->term_flags = 0;
tty->term_type = TTY_UNKNOWN;
return (0); return (0);
} }
@@ -127,29 +125,40 @@ tty_resize(struct tty *tty)
{ {
struct client *c = tty->client; struct client *c = tty->client;
struct winsize ws; struct winsize ws;
u_int sx, sy; u_int sx, sy, xpixel, ypixel;
if (ioctl(tty->fd, TIOCGWINSZ, &ws) != -1) { if (ioctl(tty->fd, TIOCGWINSZ, &ws) != -1) {
sx = ws.ws_col; sx = ws.ws_col;
if (sx == 0) if (sx == 0) {
sx = 80; sx = 80;
xpixel = 0;
} else
xpixel = ws.ws_xpixel / sx;
sy = ws.ws_row; sy = ws.ws_row;
if (sy == 0) if (sy == 0) {
sy = 24; sy = 24;
ypixel = 0;
} else
ypixel = ws.ws_ypixel / sy;
} else { } else {
sx = 80; sx = 80;
sy = 24; sy = 24;
xpixel = 0;
ypixel = 0;
} }
log_debug("%s: %s now %ux%u", __func__, c->name, sx, sy); log_debug("%s: %s now %ux%u (%ux%u)", __func__, c->name, sx, sy,
tty_set_size(tty, sx, sy); xpixel, ypixel);
tty_set_size(tty, sx, sy, xpixel, ypixel);
tty_invalidate(tty); tty_invalidate(tty);
} }
void void
tty_set_size(struct tty *tty, u_int sx, u_int sy) tty_set_size(struct tty *tty, u_int sx, u_int sy, u_int xpixel, u_int ypixel)
{ {
tty->sx = sx; tty->sx = sx;
tty->sy = sy; tty->sy = sy;
tty->xpixel = xpixel;
tty->ypixel = ypixel;
} }
static void static void
@@ -276,11 +285,22 @@ tty_open(struct tty *tty, char **cause)
return (0); return (0);
} }
static void
tty_start_timer_callback(__unused int fd, __unused short events, void *data)
{
struct tty *tty = data;
struct client *c = tty->client;
log_debug("%s: start timer fired", c->name);
tty->flags |= (TTY_HAVEDA|TTY_HAVEDSR);
}
void void
tty_start_tty(struct tty *tty) tty_start_tty(struct tty *tty)
{ {
struct client *c = tty->client; struct client *c = tty->client;
struct termios tio; struct termios tio;
struct timeval tv = { .tv_sec = 1 };
if (tty->fd != -1 && tcgetattr(tty->fd, &tty->tio) == 0) { if (tty->fd != -1 && tcgetattr(tty->fd, &tty->tio) == 0) {
setblocking(tty->fd, 0); setblocking(tty->fd, 0);
@@ -318,13 +338,21 @@ tty_start_tty(struct tty *tty)
tty->flags |= TTY_FOCUS; tty->flags |= TTY_FOCUS;
tty_puts(tty, "\033[?1004h"); tty_puts(tty, "\033[?1004h");
} }
tty_puts(tty, "\033[c"); if (~tty->flags & TTY_HAVEDA)
} tty_puts(tty, "\033[c");
if (~tty->flags & TTY_HAVEDSR)
tty_puts(tty, "\033[1337n");
} else
tty->flags |= (TTY_HAVEDA|TTY_HAVEDSR);
evtimer_set(&tty->start_timer, tty_start_timer_callback, tty);
evtimer_add(&tty->start_timer, &tv);
tty->flags |= TTY_STARTED; tty->flags |= TTY_STARTED;
tty_invalidate(tty); tty_invalidate(tty);
tty_force_cursor_colour(tty, ""); if (*tty->ccolour != '\0')
tty_force_cursor_colour(tty, "");
tty->mouse_drag_flag = 0; tty->mouse_drag_flag = 0;
tty->mouse_drag_update = NULL; tty->mouse_drag_update = NULL;
@@ -340,6 +368,8 @@ tty_stop_tty(struct tty *tty)
return; return;
tty->flags &= ~TTY_STARTED; tty->flags &= ~TTY_STARTED;
evtimer_del(&tty->start_timer);
event_del(&tty->timer); event_del(&tty->timer);
tty->flags &= ~TTY_BLOCK; tty->flags &= ~TTY_BLOCK;
@@ -370,7 +400,8 @@ tty_stop_tty(struct tty *tty)
} }
if (tty->mode & MODE_BRACKETPASTE) if (tty->mode & MODE_BRACKETPASTE)
tty_raw(tty, "\033[?2004l"); tty_raw(tty, "\033[?2004l");
tty_raw(tty, tty_term_string(tty->term, TTYC_CR)); if (*tty->ccolour != '\0')
tty_raw(tty, tty_term_string(tty->term, TTYC_CR));
tty_raw(tty, tty_term_string(tty->term, TTYC_CNORM)); tty_raw(tty, tty_term_string(tty->term, TTYC_CNORM));
if (tty_term_has(tty->term, TTYC_KMOUS)) if (tty_term_has(tty->term, TTYC_KMOUS))
@@ -425,9 +456,9 @@ tty_free(struct tty *tty)
} }
void void
tty_set_type(struct tty *tty, int type) tty_set_flags(struct tty *tty, int flags)
{ {
tty->term_type = type; tty->term_flags |= flags;
if (tty_use_margin(tty)) if (tty_use_margin(tty))
tty_puts(tty, "\033[?69h"); /* DECLRMM */ tty_puts(tty, "\033[?69h"); /* DECLRMM */
@@ -530,7 +561,7 @@ tty_putc(struct tty *tty, u_char ch)
{ {
const char *acs; const char *acs;
if ((tty->term->flags & TERM_EARLYWRAP) && if ((tty->term->flags & TERM_NOXENL) &&
ch >= 0x20 && ch != 0x7f && ch >= 0x20 && ch != 0x7f &&
tty->cy == tty->sy - 1 && tty->cy == tty->sy - 1 &&
tty->cx + 1 >= tty->sx) tty->cx + 1 >= tty->sx)
@@ -556,7 +587,7 @@ tty_putc(struct tty *tty, u_char ch)
* where we think it should be after a line wrap - this * where we think it should be after a line wrap - this
* means it works on sensible terminals as well. * means it works on sensible terminals as well.
*/ */
if (tty->term->flags & TERM_EARLYWRAP) if (tty->term->flags & TERM_NOXENL)
tty_putcode2(tty, TTYC_CUP, tty->cy, tty->cx); tty_putcode2(tty, TTYC_CUP, tty->cy, tty->cx);
} else } else
tty->cx++; tty->cx++;
@@ -566,7 +597,7 @@ tty_putc(struct tty *tty, u_char ch)
void void
tty_putn(struct tty *tty, const void *buf, size_t len, u_int width) tty_putn(struct tty *tty, const void *buf, size_t len, u_int width)
{ {
if ((tty->term->flags & TERM_EARLYWRAP) && if ((tty->term->flags & TERM_NOXENL) &&
tty->cy == tty->sy - 1 && tty->cy == tty->sy - 1 &&
tty->cx + len >= tty->sx) tty->cx + len >= tty->sx)
len = tty->sx - tty->cx - 1; len = tty->sx - tty->cx - 1;
@@ -1116,7 +1147,8 @@ tty_clear_area(struct tty *tty, struct window_pane *wp, u_int py, u_int ny,
* background colour isn't default (because it doesn't work * background colour isn't default (because it doesn't work
* after SGR 0). * after SGR 0).
*/ */
if (tty->term_type == TTY_VT420 && !COLOUR_DEFAULT(bg)) { if (((tty->term->flags|tty->term_flags) & TERM_DECFRA) &&
!COLOUR_DEFAULT(bg)) {
xsnprintf(tmp, sizeof tmp, "\033[32;%u;%u;%u;%u$x", xsnprintf(tmp, sizeof tmp, "\033[32;%u;%u;%u;%u$x",
py + 1, px + 1, py + ny, px + nx); py + 1, px + 1, py + ny, px + nx);
tty_puts(tty, tmp); tty_puts(tty, tmp);
@@ -1811,7 +1843,7 @@ tty_cmd_cells(struct tty *tty, const struct tty_ctx *ctx)
ctx->xoff + ctx->ocx + ctx->num > ctx->ox + ctx->sx)) { ctx->xoff + ctx->ocx + ctx->num > ctx->ox + ctx->sx)) {
if (!ctx->wrapped || if (!ctx->wrapped ||
!tty_pane_full_width(tty, ctx) || !tty_pane_full_width(tty, ctx) ||
(tty->term->flags & TERM_EARLYWRAP) || (tty->term->flags & TERM_NOXENL) ||
ctx->xoff + ctx->ocx != 0 || ctx->xoff + ctx->ocx != 0 ||
ctx->yoff + ctx->ocy != tty->cy + 1 || ctx->yoff + ctx->ocy != tty->cy + 1 ||
tty->cx < tty->sx || tty->cx < tty->sx ||
@@ -1860,7 +1892,7 @@ tty_cell(struct tty *tty, const struct grid_cell *gc, struct window_pane *wp)
const struct grid_cell *gcp; const struct grid_cell *gcp;
/* Skip last character if terminal is stupid. */ /* Skip last character if terminal is stupid. */
if ((tty->term->flags & TERM_EARLYWRAP) && if ((tty->term->flags & TERM_NOXENL) &&
tty->cy == tty->sy - 1 && tty->cy == tty->sy - 1 &&
tty->cx == tty->sx - 1) tty->cx == tty->sx - 1)
return; return;
@@ -2019,7 +2051,7 @@ tty_cursor_pane_unless_wrap(struct tty *tty, const struct tty_ctx *ctx,
{ {
if (!ctx->wrapped || if (!ctx->wrapped ||
!tty_pane_full_width(tty, ctx) || !tty_pane_full_width(tty, ctx) ||
(tty->term->flags & TERM_EARLYWRAP) || (tty->term->flags & TERM_NOXENL) ||
ctx->xoff + cx != 0 || ctx->xoff + cx != 0 ||
ctx->yoff + cy != tty->cy + 1 || ctx->yoff + cy != tty->cy + 1 ||
tty->cx < tty->sx || tty->cx < tty->sx ||
@@ -2106,7 +2138,9 @@ tty_cursor(struct tty *tty, u_int cx, u_int cy)
if ((u_int) abs(change) > cx && tty_term_has(term, TTYC_HPA)) { if ((u_int) abs(change) > cx && tty_term_has(term, TTYC_HPA)) {
tty_putcode1(tty, TTYC_HPA, cx); tty_putcode1(tty, TTYC_HPA, cx);
goto out; goto out;
} else if (change > 0 && tty_term_has(term, TTYC_CUB)) { } else if (change > 0 &&
tty_term_has(term, TTYC_CUB) &&
!tty_use_margin(tty)) {
if (change == 2 && tty_term_has(term, TTYC_CUB1)) { if (change == 2 && tty_term_has(term, TTYC_CUB1)) {
tty_putcode(tty, TTYC_CUB1); tty_putcode(tty, TTYC_CUB1);
tty_putcode(tty, TTYC_CUB1); tty_putcode(tty, TTYC_CUB1);
@@ -2114,7 +2148,9 @@ tty_cursor(struct tty *tty, u_int cx, u_int cy)
} }
tty_putcode1(tty, TTYC_CUB, change); tty_putcode1(tty, TTYC_CUB, change);
goto out; goto out;
} else if (change < 0 && tty_term_has(term, TTYC_CUF)) { } else if (change < 0 &&
tty_term_has(term, TTYC_CUF) &&
!tty_use_margin(tty)) {
tty_putcode1(tty, TTYC_CUF, -change); tty_putcode1(tty, TTYC_CUF, -change);
goto out; goto out;
} }
@@ -2352,11 +2388,10 @@ tty_check_fg(struct tty *tty, struct window_pane *wp, struct grid_cell *gc)
/* Is this a 24-bit colour? */ /* Is this a 24-bit colour? */
if (gc->fg & COLOUR_FLAG_RGB) { if (gc->fg & COLOUR_FLAG_RGB) {
/* Not a 24-bit terminal? Translate to 256-colour palette. */ /* Not a 24-bit terminal? Translate to 256-colour palette. */
if (!tty_term_has(tty->term, TTYC_SETRGBF)) { if ((tty->term->flags|tty->term_flags) & TERM_RGBCOLOURS)
colour_split_rgb(gc->fg, &r, &g, &b);
gc->fg = colour_find_rgb(r, g, b);
} else
return; return;
colour_split_rgb(gc->fg, &r, &g, &b);
gc->fg = colour_find_rgb(r, g, b);
} }
/* How many colours does this terminal have? */ /* How many colours does this terminal have? */
@@ -2374,10 +2409,7 @@ tty_check_fg(struct tty *tty, struct window_pane *wp, struct grid_cell *gc)
gc->fg &= 7; gc->fg &= 7;
if (colours >= 16) if (colours >= 16)
gc->fg += 90; gc->fg += 90;
else }
gc->attr |= GRID_ATTR_BRIGHT;
} else
gc->attr &= ~GRID_ATTR_BRIGHT;
} }
return; return;
} }
@@ -2405,11 +2437,10 @@ tty_check_bg(struct tty *tty, struct window_pane *wp, struct grid_cell *gc)
/* Is this a 24-bit colour? */ /* Is this a 24-bit colour? */
if (gc->bg & COLOUR_FLAG_RGB) { if (gc->bg & COLOUR_FLAG_RGB) {
/* Not a 24-bit terminal? Translate to 256-colour palette. */ /* Not a 24-bit terminal? Translate to 256-colour palette. */
if (!tty_term_has(tty->term, TTYC_SETRGBB)) { if ((tty->term->flags|tty->term_flags) & TERM_RGBCOLOURS)
colour_split_rgb(gc->bg, &r, &g, &b);
gc->bg = colour_find_rgb(r, g, b);
} else
return; return;
colour_split_rgb(gc->bg, &r, &g, &b);
gc->bg = colour_find_rgb(r, g, b);
} }
/* How many colours does this terminal have? */ /* How many colours does this terminal have? */
@@ -2442,7 +2473,8 @@ tty_check_bg(struct tty *tty, struct window_pane *wp, struct grid_cell *gc)
} }
static void static void
tty_check_us(__unused struct tty *tty, struct window_pane *wp, struct grid_cell *gc) tty_check_us(__unused struct tty *tty, struct window_pane *wp,
struct grid_cell *gc)
{ {
int c; int c;
@@ -2585,15 +2617,14 @@ tty_try_colour(struct tty *tty, int colour, const char *type)
} }
if (colour & COLOUR_FLAG_RGB) { if (colour & COLOUR_FLAG_RGB) {
colour_split_rgb(colour & 0xffffff, &r, &g, &b);
if (*type == '3') { if (*type == '3') {
if (!tty_term_has(tty->term, TTYC_SETRGBF)) if (!tty_term_has(tty->term, TTYC_SETRGBF))
return (-1); goto fallback_rgb;
colour_split_rgb(colour & 0xffffff, &r, &g, &b);
tty_putcode3(tty, TTYC_SETRGBF, r, g, b); tty_putcode3(tty, TTYC_SETRGBF, r, g, b);
} else { } else {
if (!tty_term_has(tty->term, TTYC_SETRGBB)) if (!tty_term_has(tty->term, TTYC_SETRGBB))
return (-1); goto fallback_rgb;
colour_split_rgb(colour & 0xffffff, &r, &g, &b);
tty_putcode3(tty, TTYC_SETRGBB, r, g, b); tty_putcode3(tty, TTYC_SETRGBB, r, g, b);
} }
return (0); return (0);
@@ -2606,6 +2637,12 @@ fallback_256:
log_debug("%s: 256 colour fallback: %s", tty->client->name, s); log_debug("%s: 256 colour fallback: %s", tty->client->name, s);
tty_puts(tty, s); tty_puts(tty, s);
return (0); return (0);
fallback_rgb:
xsnprintf(s, sizeof s, "\033[%s;2;%d;%d;%dm", type, r, g, b);
log_debug("%s: RGB colour fallback: %s", tty->client->name, s);
tty_puts(tty, s);
return (0);
} }
static void static void

23
utf8.c
View File

@@ -415,7 +415,7 @@ utf8_cstrwidth(const char *s)
return (width); return (width);
} }
/* Pad UTF-8 string to width. Caller frees. */ /* Pad UTF-8 string to width on the left. Caller frees. */
char * char *
utf8_padcstr(const char *s, u_int width) utf8_padcstr(const char *s, u_int width)
{ {
@@ -436,6 +436,27 @@ utf8_padcstr(const char *s, u_int width)
return (out); return (out);
} }
/* Pad UTF-8 string to width on the right. Caller frees. */
char *
utf8_rpadcstr(const char *s, u_int width)
{
size_t slen;
char *out;
u_int n, i;
n = utf8_cstrwidth(s);
if (n >= width)
return (xstrdup(s));
slen = strlen(s);
out = xmalloc(slen + 1 + (width - n));
for (i = 0; i < width - n; i++)
out[i] = ' ';
memcpy(out + i, s, slen);
out[i + slen] = '\0';
return (out);
}
int int
utf8_cstrhas(const char *s, const struct utf8_data *ud) utf8_cstrhas(const char *s, const struct utf8_data *ud)
{ {

View File

@@ -74,6 +74,7 @@ static const char *window_buffer_sort_list[] = {
"name", "name",
"size" "size"
}; };
static struct mode_tree_sort_criteria *window_buffer_sort;
struct window_buffer_itemdata { struct window_buffer_itemdata {
const char *name; const char *name;
@@ -112,43 +113,29 @@ window_buffer_free_item(struct window_buffer_itemdata *item)
} }
static int static int
window_buffer_cmp_name(const void *a0, const void *b0) window_buffer_cmp(const void *a0, const void *b0)
{ {
const struct window_buffer_itemdata *const *a = a0; const struct window_buffer_itemdata *const *a = a0;
const struct window_buffer_itemdata *const *b = b0; const struct window_buffer_itemdata *const *b = b0;
int result = 0;
return (strcmp((*a)->name, (*b)->name)); if (window_buffer_sort->field == WINDOW_BUFFER_BY_TIME)
} result = (*b)->order - (*a)->order;
else if (window_buffer_sort->field == WINDOW_BUFFER_BY_SIZE)
result = (*b)->size - (*a)->size;
static int /* Use WINDOW_BUFFER_BY_NAME as default order and tie breaker. */
window_buffer_cmp_time(const void *a0, const void *b0) if (result == 0)
{ result = strcmp((*a)->name, (*b)->name);
const struct window_buffer_itemdata *const *a = a0;
const struct window_buffer_itemdata *const *b = b0;
if ((*a)->order > (*b)->order) if (window_buffer_sort->reversed)
return (-1); result = -result;
if ((*a)->order < (*b)->order) return (result);
return (1);
return (strcmp((*a)->name, (*b)->name));
}
static int
window_buffer_cmp_size(const void *a0, const void *b0)
{
const struct window_buffer_itemdata *const *a = a0;
const struct window_buffer_itemdata *const *b = b0;
if ((*a)->size > (*b)->size)
return (-1);
if ((*a)->size < (*b)->size)
return (1);
return (strcmp((*a)->name, (*b)->name));
} }
static void static void
window_buffer_build(void *modedata, u_int sort_type, __unused uint64_t *tag, window_buffer_build(void *modedata, struct mode_tree_sort_criteria *sort_crit,
const char *filter) __unused uint64_t *tag, const char *filter)
{ {
struct window_buffer_modedata *data = modedata; struct window_buffer_modedata *data = modedata;
struct window_buffer_itemdata *item; struct window_buffer_itemdata *item;
@@ -174,20 +161,9 @@ window_buffer_build(void *modedata, u_int sort_type, __unused uint64_t *tag,
item->order = paste_buffer_order(pb); item->order = paste_buffer_order(pb);
} }
switch (sort_type) { window_buffer_sort = sort_crit;
case WINDOW_BUFFER_BY_NAME: qsort(data->item_list, data->item_size, sizeof *data->item_list,
qsort(data->item_list, data->item_size, sizeof *data->item_list, window_buffer_cmp);
window_buffer_cmp_name);
break;
case WINDOW_BUFFER_BY_TIME:
qsort(data->item_list, data->item_size, sizeof *data->item_list,
window_buffer_cmp_time);
break;
case WINDOW_BUFFER_BY_SIZE:
qsort(data->item_list, data->item_size, sizeof *data->item_list,
window_buffer_cmp_size);
break;
}
if (cmd_find_valid_state(&data->fs)) { if (cmd_find_valid_state(&data->fs)) {
s = data->fs.s; s = data->fs.s;
@@ -231,9 +207,9 @@ window_buffer_draw(__unused void *modedata, void *itemdata,
{ {
struct window_buffer_itemdata *item = itemdata; struct window_buffer_itemdata *item = itemdata;
struct paste_buffer *pb; struct paste_buffer *pb;
char line[1024]; const char *pdata, *start, *end;
const char *pdata, *end, *cp; char *buf = NULL;
size_t psize, at; size_t psize, len;
u_int i, cx = ctx->s->cx, cy = ctx->s->cy; u_int i, cx = ctx->s->cx, cy = ctx->s->cy;
pb = paste_get_name(item->name); pb = paste_get_name(item->name);
@@ -242,27 +218,22 @@ window_buffer_draw(__unused void *modedata, void *itemdata,
pdata = end = paste_buffer_data(pb, &psize); pdata = end = paste_buffer_data(pb, &psize);
for (i = 0; i < sy; i++) { for (i = 0; i < sy; i++) {
at = 0; start = end;
while (end != pdata + psize && *end != '\n') { while (end != pdata + psize && *end != '\n')
if ((sizeof line) - at > 5) {
cp = vis(line + at, *end, VIS_OCTAL|VIS_TAB, 0);
at = cp - line;
}
end++; end++;
} buf = xreallocarray(buf, 4, end - start + 1);
if (at > sx) len = utf8_strvis(buf, start, end - start, VIS_OCTAL|VIS_TAB);
at = sx; if (*buf != '\0') {
line[at] = '\0';
if (*line != '\0') {
screen_write_cursormove(ctx, cx, cy + i, 0); screen_write_cursormove(ctx, cx, cy + i, 0);
screen_write_puts(ctx, &grid_default_cell, "%s", line); screen_write_nputs(ctx, sx, &grid_default_cell, "%s",
buf);
} }
if (end == pdata + psize) if (end == pdata + psize)
break; break;
end++; end++;
} }
free(buf);
} }
static int static int
@@ -357,7 +328,7 @@ window_buffer_resize(struct window_mode_entry *wme, u_int sx, u_int sy)
} }
static void static void
window_buffer_do_delete(void* modedata, void *itemdata, window_buffer_do_delete(void *modedata, void *itemdata,
__unused struct client *c, __unused key_code key) __unused struct client *c, __unused key_code key)
{ {
struct window_buffer_modedata *data = modedata; struct window_buffer_modedata *data = modedata;
@@ -371,7 +342,7 @@ window_buffer_do_delete(void* modedata, void *itemdata,
} }
static void static void
window_buffer_do_paste(void* modedata, void *itemdata, struct client *c, window_buffer_do_paste(void *modedata, void *itemdata, struct client *c,
__unused key_code key) __unused key_code key)
{ {
struct window_buffer_modedata *data = modedata; struct window_buffer_modedata *data = modedata;

View File

@@ -75,6 +75,7 @@ static const char *window_client_sort_list[] = {
"creation", "creation",
"activity" "activity"
}; };
static struct mode_tree_sort_criteria *window_client_sort;
struct window_client_itemdata { struct window_client_itemdata {
struct client *c; struct client *c;
@@ -110,60 +111,48 @@ window_client_free_item(struct window_client_itemdata *item)
} }
static int static int
window_client_cmp_name(const void *a0, const void *b0) window_client_cmp(const void *a0, const void *b0)
{ {
const struct window_client_itemdata *const *a = a0; const struct window_client_itemdata *const *a = a0;
const struct window_client_itemdata *const *b = b0; const struct window_client_itemdata *const *b = b0;
const struct window_client_itemdata *itema = *a;
const struct window_client_itemdata *itemb = *b;
struct client *ca = itema->c;
struct client *cb = itemb->c;
int result = 0;
return (strcmp((*a)->c->name, (*b)->c->name)); switch (window_client_sort->field) {
} case WINDOW_CLIENT_BY_SIZE:
result = ca->tty.sx - cb->tty.sx;
if (result == 0)
result = ca->tty.sy - cb->tty.sy;
break;
case WINDOW_CLIENT_BY_CREATION_TIME:
if (timercmp(&ca->creation_time, &cb->creation_time, >))
result = -1;
else if (timercmp(&ca->creation_time, &cb->creation_time, <))
result = 1;
break;
case WINDOW_CLIENT_BY_ACTIVITY_TIME:
if (timercmp(&ca->activity_time, &cb->activity_time, >))
result = -1;
else if (timercmp(&ca->activity_time, &cb->activity_time, <))
result = 1;
break;
}
static int /* Use WINDOW_CLIENT_BY_NAME as default order and tie breaker. */
window_client_cmp_size(const void *a0, const void *b0) if (result == 0)
{ result = strcmp(ca->name, cb->name);
const struct window_client_itemdata *const *a = a0;
const struct window_client_itemdata *const *b = b0;
if ((*a)->c->tty.sx < (*b)->c->tty.sx) if (window_client_sort->reversed)
return (-1); result = -result;
if ((*a)->c->tty.sx > (*b)->c->tty.sx) return (result);
return (1);
if ((*a)->c->tty.sy < (*b)->c->tty.sy)
return (-1);
if ((*a)->c->tty.sy > (*b)->c->tty.sy)
return (1);
return (strcmp((*a)->c->name, (*b)->c->name));
}
static int
window_client_cmp_creation_time(const void *a0, const void *b0)
{
const struct window_client_itemdata *const *a = a0;
const struct window_client_itemdata *const *b = b0;
if (timercmp(&(*a)->c->creation_time, &(*b)->c->creation_time, >))
return (-1);
if (timercmp(&(*a)->c->creation_time, &(*b)->c->creation_time, <))
return (1);
return (strcmp((*a)->c->name, (*b)->c->name));
}
static int
window_client_cmp_activity_time(const void *a0, const void *b0)
{
const struct window_client_itemdata *const *a = a0;
const struct window_client_itemdata *const *b = b0;
if (timercmp(&(*a)->c->activity_time, &(*b)->c->activity_time, >))
return (-1);
if (timercmp(&(*a)->c->activity_time, &(*b)->c->activity_time, <))
return (1);
return (strcmp((*a)->c->name, (*b)->c->name));
} }
static void static void
window_client_build(void *modedata, u_int sort_type, __unused uint64_t *tag, window_client_build(void *modedata, struct mode_tree_sort_criteria *sort_crit,
const char *filter) __unused uint64_t *tag, const char *filter)
{ {
struct window_client_modedata *data = modedata; struct window_client_modedata *data = modedata;
struct window_client_itemdata *item; struct window_client_itemdata *item;
@@ -187,24 +176,9 @@ window_client_build(void *modedata, u_int sort_type, __unused uint64_t *tag,
c->references++; c->references++;
} }
switch (sort_type) { window_client_sort = sort_crit;
case WINDOW_CLIENT_BY_NAME: qsort(data->item_list, data->item_size, sizeof *data->item_list,
qsort(data->item_list, data->item_size, sizeof *data->item_list, window_client_cmp);
window_client_cmp_name);
break;
case WINDOW_CLIENT_BY_SIZE:
qsort(data->item_list, data->item_size, sizeof *data->item_list,
window_client_cmp_size);
break;
case WINDOW_CLIENT_BY_CREATION_TIME:
qsort(data->item_list, data->item_size, sizeof *data->item_list,
window_client_cmp_creation_time);
break;
case WINDOW_CLIENT_BY_ACTIVITY_TIME:
qsort(data->item_list, data->item_size, sizeof *data->item_list,
window_client_cmp_activity_time);
break;
}
for (i = 0; i < data->item_size; i++) { for (i = 0; i < data->item_size; i++) {
item = data->item_list[i]; item = data->item_list[i];
@@ -236,7 +210,7 @@ window_client_draw(__unused void *modedata, void *itemdata,
struct window_pane *wp; struct window_pane *wp;
u_int cx = s->cx, cy = s->cy, lines, at; u_int cx = s->cx, cy = s->cy, lines, at;
if (c->session == NULL || (c->flags & (CLIENT_DEAD|CLIENT_DETACHING))) if (c->session == NULL || (c->flags & CLIENT_UNATTACHEDFLAGS))
return; return;
wp = c->session->curw->window->active; wp = c->session->curw->window->active;
@@ -339,7 +313,7 @@ window_client_resize(struct window_mode_entry *wme, u_int sx, u_int sy)
} }
static void static void
window_client_do_detach(void* modedata, void *itemdata, window_client_do_detach(void *modedata, void *itemdata,
__unused struct client *c, key_code key) __unused struct client *c, key_code key)
{ {
struct window_client_modedata *data = modedata; struct window_client_modedata *data = modedata;

File diff suppressed because it is too large Load Diff

View File

@@ -33,7 +33,7 @@ static void window_tree_key(struct window_mode_entry *,
struct client *, struct session *, struct client *, struct session *,
struct winlink *, key_code, struct mouse_event *); struct winlink *, key_code, struct mouse_event *);
#define WINDOW_TREE_DEFAULT_COMMAND "switch-client -t '%%'" #define WINDOW_TREE_DEFAULT_COMMAND "switch-client -Zt '%%'"
#define WINDOW_TREE_DEFAULT_FORMAT \ #define WINDOW_TREE_DEFAULT_FORMAT \
"#{?pane_format," \ "#{?pane_format," \
@@ -89,6 +89,7 @@ static const char *window_tree_sort_list[] = {
"name", "name",
"time" "time"
}; };
static struct mode_tree_sort_criteria *window_tree_sort;
enum window_tree_type { enum window_tree_type {
WINDOW_TREE_NONE, WINDOW_TREE_NONE,
@@ -184,62 +185,92 @@ window_tree_free_item(struct window_tree_itemdata *item)
} }
static int static int
window_tree_cmp_session_name(const void *a0, const void *b0) window_tree_cmp_session(const void *a0, const void *b0)
{ {
const struct session *const *a = a0; const struct session *const *a = a0;
const struct session *const *b = b0; const struct session *const *b = b0;
const struct session *sa = *a;
const struct session *sb = *b;
int result = 0;
return (strcmp((*a)->name, (*b)->name)); switch (window_tree_sort->field) {
case WINDOW_TREE_BY_INDEX:
result = sa->id - sb->id;
break;
case WINDOW_TREE_BY_TIME:
if (timercmp(&sa->activity_time, &sb->activity_time, >)) {
result = -1;
break;
}
if (timercmp(&sa->activity_time, &sb->activity_time, <)) {
result = 1;
break;
}
/* FALLTHROUGH */
case WINDOW_TREE_BY_NAME:
result = strcmp(sa->name, sb->name);
break;
}
if (window_tree_sort->reversed)
result = -result;
return (result);
} }
static int static int
window_tree_cmp_session_time(const void *a0, const void *b0) window_tree_cmp_window(const void *a0, const void *b0)
{ {
const struct session *const *a = a0; const struct winlink *const *a = a0;
const struct session *const *b = b0; const struct winlink *const *b = b0;
const struct winlink *wla = *a;
const struct winlink *wlb = *b;
struct window *wa = wla->window;
struct window *wb = wlb->window;
int result = 0;
if (timercmp(&(*a)->activity_time, &(*b)->activity_time, >)) switch (window_tree_sort->field) {
return (-1); case WINDOW_TREE_BY_INDEX:
if (timercmp(&(*a)->activity_time, &(*b)->activity_time, <)) result = wla->idx - wlb->idx;
return (1); break;
return (strcmp((*a)->name, (*b)->name)); case WINDOW_TREE_BY_TIME:
if (timercmp(&wa->activity_time, &wb->activity_time, >)) {
result = -1;
break;
}
if (timercmp(&wa->activity_time, &wb->activity_time, <)) {
result = 1;
break;
}
/* FALLTHROUGH */
case WINDOW_TREE_BY_NAME:
result = strcmp(wa->name, wb->name);
break;
}
if (window_tree_sort->reversed)
result = -result;
return (result);
} }
static int static int
window_tree_cmp_window_name(const void *a0, const void *b0) window_tree_cmp_pane(const void *a0, const void *b0)
{ {
const struct winlink *const *a = a0; const struct window_pane *const *a = a0;
const struct winlink *const *b = b0; const struct window_pane *const *b = b0;
int result;
return (strcmp((*a)->window->name, (*b)->window->name)); if (window_tree_sort->field == WINDOW_TREE_BY_TIME)
} result = (*a)->active_point - (*b)->active_point;
else {
static int /*
window_tree_cmp_window_time(const void *a0, const void *b0) * Panes don't have names, so use number order for any other
{ * sort field.
const struct winlink *const *a = a0; */
const struct winlink *const *b = b0; result = (*a)->id - (*b)->id;
}
if (timercmp(&(*a)->window->activity_time, if (window_tree_sort->reversed)
&(*b)->window->activity_time, >)) result = -result;
return (-1); return (result);
if (timercmp(&(*a)->window->activity_time,
&(*b)->window->activity_time, <))
return (1);
return (strcmp((*a)->window->name, (*b)->window->name));
}
static int
window_tree_cmp_pane_time(const void *a0, const void *b0)
{
const struct window_pane *const *a = a0;
const struct window_pane *const *b = b0;
if ((*a)->active_point < (*b)->active_point)
return (-1);
if ((*a)->active_point > (*b)->active_point)
return (1);
return (0);
} }
static void static void
@@ -285,8 +316,9 @@ window_tree_filter_pane(struct session *s, struct winlink *wl,
} }
static int static int
window_tree_build_window(struct session *s, struct winlink *wl, void* modedata, window_tree_build_window(struct session *s, struct winlink *wl,
u_int sort_type, struct mode_tree_item *parent, const char *filter) void *modedata, struct mode_tree_sort_criteria *sort_crit,
struct mode_tree_item *parent, const char *filter)
{ {
struct window_tree_modedata *data = modedata; struct window_tree_modedata *data = modedata;
struct window_tree_itemdata *item; struct window_tree_itemdata *item;
@@ -335,16 +367,8 @@ window_tree_build_window(struct session *s, struct winlink *wl, void* modedata,
if (n == 0) if (n == 0)
goto empty; goto empty;
switch (sort_type) { window_tree_sort = sort_crit;
case WINDOW_TREE_BY_INDEX: qsort(l, n, sizeof *l, window_tree_cmp_pane);
break;
case WINDOW_TREE_BY_NAME:
/* Panes don't have names, so leave in number order. */
break;
case WINDOW_TREE_BY_TIME:
qsort(l, n, sizeof *l, window_tree_cmp_pane_time);
break;
}
for (i = 0; i < n; i++) for (i = 0; i < n; i++)
window_tree_build_pane(s, wl, l[i], modedata, mti); window_tree_build_pane(s, wl, l[i], modedata, mti);
@@ -359,8 +383,8 @@ empty:
} }
static void static void
window_tree_build_session(struct session *s, void* modedata, window_tree_build_session(struct session *s, void *modedata,
u_int sort_type, const char *filter) struct mode_tree_sort_criteria *sort_crit, const char *filter)
{ {
struct window_tree_modedata *data = modedata; struct window_tree_modedata *data = modedata;
struct window_tree_itemdata *item; struct window_tree_itemdata *item;
@@ -392,20 +416,12 @@ window_tree_build_session(struct session *s, void* modedata,
l = xreallocarray(l, n + 1, sizeof *l); l = xreallocarray(l, n + 1, sizeof *l);
l[n++] = wl; l[n++] = wl;
} }
switch (sort_type) { window_tree_sort = sort_crit;
case WINDOW_TREE_BY_INDEX: qsort(l, n, sizeof *l, window_tree_cmp_window);
break;
case WINDOW_TREE_BY_NAME:
qsort(l, n, sizeof *l, window_tree_cmp_window_name);
break;
case WINDOW_TREE_BY_TIME:
qsort(l, n, sizeof *l, window_tree_cmp_window_time);
break;
}
empty = 0; empty = 0;
for (i = 0; i < n; i++) { for (i = 0; i < n; i++) {
if (!window_tree_build_window(s, l[i], modedata, sort_type, mti, if (!window_tree_build_window(s, l[i], modedata, sort_crit, mti,
filter)) filter))
empty++; empty++;
} }
@@ -418,8 +434,8 @@ window_tree_build_session(struct session *s, void* modedata,
} }
static void static void
window_tree_build(void *modedata, u_int sort_type, uint64_t *tag, window_tree_build(void *modedata, struct mode_tree_sort_criteria *sort_crit,
const char *filter) uint64_t *tag, const char *filter)
{ {
struct window_tree_modedata *data = modedata; struct window_tree_modedata *data = modedata;
struct session *s, **l; struct session *s, **l;
@@ -446,19 +462,11 @@ window_tree_build(void *modedata, u_int sort_type, uint64_t *tag,
l = xreallocarray(l, n + 1, sizeof *l); l = xreallocarray(l, n + 1, sizeof *l);
l[n++] = s; l[n++] = s;
} }
switch (sort_type) { window_tree_sort = sort_crit;
case WINDOW_TREE_BY_INDEX: qsort(l, n, sizeof *l, window_tree_cmp_session);
break;
case WINDOW_TREE_BY_NAME:
qsort(l, n, sizeof *l, window_tree_cmp_session_name);
break;
case WINDOW_TREE_BY_TIME:
qsort(l, n, sizeof *l, window_tree_cmp_session_time);
break;
}
for (i = 0; i < n; i++) for (i = 0; i < n; i++)
window_tree_build_session(l[i], modedata, sort_type, filter); window_tree_build_session(l[i], modedata, sort_crit, filter);
free(l); free(l);
switch (data->type) { switch (data->type) {
@@ -965,7 +973,7 @@ window_tree_get_target(struct window_tree_itemdata *item,
} }
static void static void
window_tree_command_each(void* modedata, void* itemdata, struct client *c, window_tree_command_each(void *modedata, void *itemdata, struct client *c,
__unused key_code key) __unused key_code key)
{ {
struct window_tree_modedata *data = modedata; struct window_tree_modedata *data = modedata;
@@ -1022,7 +1030,7 @@ window_tree_command_free(void *modedata)
} }
static void static void
window_tree_kill_each(__unused void* modedata, void* itemdata, window_tree_kill_each(__unused void *modedata, void *itemdata,
__unused struct client *c, __unused key_code key) __unused struct client *c, __unused key_code key)
{ {
struct window_tree_itemdata *item = itemdata; struct window_tree_itemdata *item = itemdata;

118
window.c
View File

@@ -306,12 +306,17 @@ window_update_activity(struct window *w)
} }
struct window * struct window *
window_create(u_int sx, u_int sy) window_create(u_int sx, u_int sy, u_int xpixel, u_int ypixel)
{ {
struct window *w; struct window *w;
if (xpixel == 0)
xpixel = DEFAULT_XPIXEL;
if (ypixel == 0)
ypixel = DEFAULT_YPIXEL;
w = xcalloc(1, sizeof *w); w = xcalloc(1, sizeof *w);
w->name = NULL; w->name = xstrdup("");
w->flags = 0; w->flags = 0;
TAILQ_INIT(&w->panes); TAILQ_INIT(&w->panes);
@@ -322,6 +327,8 @@ window_create(u_int sx, u_int sy)
w->sx = sx; w->sx = sx;
w->sy = sy; w->sy = sy;
w->xpixel = xpixel;
w->ypixel = ypixel;
w->options = options_create(global_w_options); w->options = options_create(global_w_options);
@@ -408,11 +415,49 @@ window_set_name(struct window *w, const char *new_name)
} }
void void
window_resize(struct window *w, u_int sx, u_int sy) window_resize(struct window *w, u_int sx, u_int sy, int xpixel, int ypixel)
{ {
log_debug("%s: @%u resize %ux%u", __func__, w->id, sx, sy); if (xpixel == 0)
xpixel = DEFAULT_XPIXEL;
if (ypixel == 0)
ypixel = DEFAULT_YPIXEL;
log_debug("%s: @%u resize %ux%u (%ux%u)", __func__, w->id, sx, sy,
xpixel == -1 ? w->xpixel : xpixel,
ypixel == -1 ? w->ypixel : ypixel);
w->sx = sx; w->sx = sx;
w->sy = sy; w->sy = sy;
if (xpixel != -1)
w->xpixel = xpixel;
if (ypixel != -1)
w->ypixel = ypixel;
}
void
window_pane_send_resize(struct window_pane *wp, int yadjust)
{
struct window *w = wp->window;
struct winsize ws;
if (wp->fd == -1)
return;
memset(&ws, 0, sizeof ws);
ws.ws_col = wp->sx;
ws.ws_row = wp->sy + yadjust;
ws.ws_xpixel = w->xpixel * ws.ws_col;
ws.ws_ypixel = w->ypixel * ws.ws_row;
if (ioctl(wp->fd, TIOCSWINSZ, &ws) == -1)
#ifdef __sun
/*
* Some versions of Solaris apparently can return an error when
* resizing; don't know why this happens, can't reproduce on
* other platforms and ignoring it doesn't seem to cause any
* issues.
*/
if (errno != EINVAL && errno != ENXIO)
#endif
fatal("ioctl failed");
} }
int int
@@ -585,6 +630,28 @@ window_unzoom(struct window *w)
return (0); return (0);
} }
int
window_push_zoom(struct window *w, int flag)
{
log_debug("%s: @%u %d", __func__, w->id,
flag && (w->flags & WINDOW_ZOOMED));
if (flag && (w->flags & WINDOW_ZOOMED))
w->flags |= WINDOW_WASZOOMED;
else
w->flags &= ~WINDOW_WASZOOMED;
return (window_unzoom(w) == 0);
}
int
window_pop_zoom(struct window *w)
{
log_debug("%s: @%u %d", __func__, w->id,
!!(w->flags & WINDOW_WASZOOMED));
if (w->flags & WINDOW_WASZOOMED)
return (window_zoom(w->active) == 0);
return (0);
}
struct window_pane * struct window_pane *
window_add_pane(struct window *w, struct window_pane *other, u_int hlimit, window_add_pane(struct window *w, struct window_pane *other, u_int hlimit,
int flags) int flags)
@@ -932,7 +999,7 @@ window_pane_resize(struct window_pane *wp, u_int sx, u_int sy)
if (wme != NULL && wme->mode->resize != NULL) if (wme != NULL && wme->mode->resize != NULL)
wme->mode->resize(wme, sx, sy); wme->mode->resize(wme, sx, sy);
wp->flags |= PANE_RESIZE; wp->flags |= (PANE_RESIZE|PANE_RESIZED);
} }
/* /*
@@ -1172,7 +1239,7 @@ window_pane_reset_mode_all(struct window_pane *wp)
window_pane_reset_mode(wp); window_pane_reset_mode(wp);
} }
void int
window_pane_key(struct window_pane *wp, struct client *c, struct session *s, window_pane_key(struct window_pane *wp, struct client *c, struct session *s,
struct winlink *wl, key_code key, struct mouse_event *m) struct winlink *wl, key_code key, struct mouse_event *m)
{ {
@@ -1180,23 +1247,24 @@ window_pane_key(struct window_pane *wp, struct client *c, struct session *s,
struct window_pane *wp2; struct window_pane *wp2;
if (KEYC_IS_MOUSE(key) && m == NULL) if (KEYC_IS_MOUSE(key) && m == NULL)
return; return (-1);
wme = TAILQ_FIRST(&wp->modes); wme = TAILQ_FIRST(&wp->modes);
if (wme != NULL) { if (wme != NULL) {
wp->modelast = time(NULL); wp->modelast = time(NULL);
if (wme->mode->key != NULL) if (wme->mode->key != NULL)
wme->mode->key(wme, c, s, wl, (key & ~KEYC_XTERM), m); wme->mode->key(wme, c, s, wl, (key & ~KEYC_XTERM), m);
return; return (0);
} }
if (wp->fd == -1 || wp->flags & PANE_INPUTOFF) if (wp->fd == -1 || wp->flags & PANE_INPUTOFF)
return; return (0);
input_key(wp, key, m); if (input_key(wp, key, m) != 0)
return (-1);
if (KEYC_IS_MOUSE(key)) if (KEYC_IS_MOUSE(key))
return; return (0);
if (options_get_number(wp->window->options, "synchronize-panes")) { if (options_get_number(wp->window->options, "synchronize-panes")) {
TAILQ_FOREACH(wp2, &wp->window->panes, entry) { TAILQ_FOREACH(wp2, &wp->window->panes, entry) {
if (wp2 != wp && if (wp2 != wp &&
@@ -1207,6 +1275,7 @@ window_pane_key(struct window_pane *wp, struct client *c, struct session *s,
input_key(wp2, key, NULL); input_key(wp2, key, NULL);
} }
} }
return (0);
} }
int int
@@ -1541,31 +1610,28 @@ winlink_shuffle_up(struct session *s, struct winlink *wl)
} }
static void static void
window_pane_input_callback(struct client *c, int closed, void *data) window_pane_input_callback(struct client *c, __unused const char *path,
int error, int closed, struct evbuffer *buffer, void *data)
{ {
struct window_pane_input_data *cdata = data; struct window_pane_input_data *cdata = data;
struct window_pane *wp; struct window_pane *wp;
struct evbuffer *evb = c->stdin_data; u_char *buf = EVBUFFER_DATA(buffer);
u_char *buf = EVBUFFER_DATA(evb); size_t len = EVBUFFER_LENGTH(buffer);
size_t len = EVBUFFER_LENGTH(evb);
wp = window_pane_find_by_id(cdata->wp); wp = window_pane_find_by_id(cdata->wp);
if (wp == NULL || closed || c->flags & CLIENT_DEAD) { if (wp == NULL || closed || error != 0 || c->flags & CLIENT_DEAD) {
if (wp == NULL) if (wp == NULL)
c->flags |= CLIENT_EXIT; c->flags |= CLIENT_EXIT;
evbuffer_drain(evb, len);
c->stdin_callback = NULL;
server_client_unref(c);
evbuffer_drain(buffer, len);
cmdq_continue(cdata->item); cmdq_continue(cdata->item);
free(cdata);
server_client_unref(c);
free(cdata);
return; return;
} }
input_parse_buffer(wp, buf, len); input_parse_buffer(wp, buf, len);
evbuffer_drain(evb, len); evbuffer_drain(buffer, len);
} }
int int
@@ -1584,6 +1650,8 @@ window_pane_start_input(struct window_pane *wp, struct cmdq_item *item,
cdata->item = item; cdata->item = item;
cdata->wp = wp->id; cdata->wp = wp->id;
return (server_set_stdin_callback(c, window_pane_input_callback, cdata, c->references++;
cause)); file_read(c, "-", window_pane_input_callback, cdata);
return (0);
} }

View File

@@ -71,6 +71,20 @@ xreallocarray(void *ptr, size_t nmemb, size_t size)
return new_ptr; return new_ptr;
} }
void *
xrecallocarray(void *ptr, size_t oldnmemb, size_t nmemb, size_t size)
{
void *new_ptr;
if (nmemb == 0 || size == 0)
fatalx("xrecallocarray: zero size");
new_ptr = recallocarray(ptr, oldnmemb, nmemb, size);
if (new_ptr == NULL)
fatalx("xrecallocarray: allocating %zu * %zu bytes: %s",
nmemb, size, strerror(errno));
return new_ptr;
}
char * char *
xstrdup(const char *str) xstrdup(const char *str)
{ {

View File

@@ -27,6 +27,7 @@ void *xmalloc(size_t);
void *xcalloc(size_t, size_t); void *xcalloc(size_t, size_t);
void *xrealloc(void *, size_t); void *xrealloc(void *, size_t);
void *xreallocarray(void *, size_t, size_t); void *xreallocarray(void *, size_t, size_t);
void *xrecallocarray(void *, size_t, size_t, size_t);
char *xstrdup(const char *); char *xstrdup(const char *);
char *xstrndup(const char *, size_t); char *xstrndup(const char *, size_t);
int xasprintf(char **, const char *, ...) int xasprintf(char **, const char *, ...)