529 Commits
3.0 ... 3.1a

Author SHA1 Message Date
Nicholas Marriott
d0fa520788 Update CHANGES. 2020-04-27 09:38:04 +01:00
Nicholas Marriott
646bfe403e Do not close stdout file descriptor in control mode since it will be needed for
printing the exit messages.
2020-04-27 09:36:30 +01:00
Nicholas Marriott
650d38962f tmux 3.1. 2020-04-24 09:57:49 +01:00
Nicholas Marriott
ccd7368cc5 Update CHANGES. 2020-04-22 12:59:50 +01:00
Nicholas Marriott
9077b212c3 job_run needs fewer arguments. 2020-04-06 16:14:09 +01:00
Nicholas Marriott
a4e19bcd80 Various fixes for copy mode from master. 2020-04-06 16:09:49 +01:00
nicm
bc36b473f1 Check previous line rather than an extra line, from Anindya Mukherjee. 2020-04-06 16:07:20 +01:00
nicm
10975961de Only search the visible part of the history when marking (highlighting)
search terms, much faster than searching the whole history.
2020-04-06 16:06:14 +01:00
nicm
8d2af4fb54 Add a 10 second timeout to prevent searches taking too much time, from
Anindya Mukherjee.
2020-04-06 16:04:51 +01:00
nicm
ac050b2583 Stop logging the entire command queue every time we add something,
spotted by tb & sthen.
2020-04-06 16:04:10 +01:00
nicm
3234017260 Add an argument to list-commands to show only a single command. 2020-04-06 16:03:39 +01:00
nicm
938ad5a98c Use new window and new pane as well for -P to new-session or new-window. 2020-04-06 16:03:33 +01:00
Nicholas Marriott
9f378a163f 3.1-rc4. 2020-04-01 10:09:49 +01:00
nicm
a5922546ac Do not go down the regex search path (which is expensive because we need
to convert the grid data into a string for regexec and reverse it to
find the grid position) if the search string does not contain any regex
special characters.
2020-04-01 10:09:03 +01:00
nicm
3476eccf48 Use a comparison to check for wrap and avoid an expensive modulus. 2020-04-01 10:08:54 +01:00
nicm
0dbf414578 Performance improvements for regex searching, most notably:
- Use the grid data directly instead of copying it.

- Special case the most typical one byte character cells and use memcmp
  for multiple bytes instead of a handrolled loop.

- Hoist regcomp out of the loop into the calling functions.

GitHub issue 2143.

Also a man page from from jmc@.
2020-04-01 10:08:39 +01:00
nicm
8dedccaa20 Add non-regex search variants to avoid the performance cost for people
with large histories or long lines.
2020-04-01 10:08:09 +01:00
Nicholas Marriott
e5fd85415d Update CHANGES. 2020-04-01 09:29:44 +01:00
Nicholas Marriott
af4b62d10b 3.1-rc3. 2020-03-18 07:55:33 +00:00
nicm
0c06409c9d getopt is not required to set optarg to NULL when there is no argument
and some do not, so set it explicitly each time.
2020-03-18 07:54:37 +00:00
nicm
617136c234 Turn off mouse mode 1003 as well as the rest when exiting. 2020-03-17 16:16:23 +00:00
nicm
f16085a362 Fix C-Space key string. 2020-03-17 16:16:09 +00:00
nicm
4ffbebedce Terminate the output buffer for control mode output - it is now used as
a string. GitHub issue 2114.
2020-03-17 16:14:51 +00:00
nicm
c0d74661b7 Do not attempt to close a NULL pane when failing to create a new one. 2020-03-17 16:14:25 +00:00
Nicholas Marriott
b21a9b1c4e getopt varies too much between platforms, and we already use compat/getopt.c
for Linux so just use it everywhere.
2020-03-17 16:14:12 +00:00
Nicholas Marriott
444e9f3c58 Bump 3.1-rc up to master. 2020-03-11 06:38:43 +00:00
Thomas Adam
59cb022c42 Merge branch 'obsd-master' 2020-03-07 12:01:25 +00:00
nicm
2991f4aad0 Use correct width of right marker so it doesn't draw over status right
when more than one character. Reported by Tyler Culp.
2020-03-07 10:58:32 +00:00
Thomas Adam
9e4d0b2b6d Merge branch 'obsd-master' 2020-03-06 18:01:26 +00:00
nicm
add75a06cd Update latest client for target session on switch-client. 2020-03-06 15:35:03 +00:00
Thomas Adam
ccd24c9cb2 Merge branch 'obsd-master' 2020-03-02 23:09:48 +00:00
nicm
8be179de46 Use current session for cwd of new sessions, not the new session which
doesn't have one yet. GitHub issue 2091.
2020-03-02 08:30:30 +00:00
nicm
f65b9c0d36 Change mouse selection so that after selecting a word, dragging selects
only words and similar for lines. From Anindya Mukherjee.
2020-02-24 09:53:59 +00:00
Nicholas Marriott
549b3599ef Update CHANGES. 2020-02-20 20:42:26 +00:00
Thomas Adam
4694afbed4 Merge branch 'obsd-master' 2020-02-20 10:01:29 +00:00
nicm
229be034fb Add selection_active format for when the selection is present but not
moving with the cursor, from Mark Kelly.
2020-02-20 07:34:57 +00:00
Thomas Adam
ba542e42b7 Merge branch 'obsd-master' 2020-02-19 16:01:27 +00:00
nicm
b20753f2a3 A few fixes to make modifier keys and dragging work - need to remove the
modifiers before checking for the dragging marker key, and apply them
before looking up the end key. Also fix key-to-string with modifiers for
special keys.
2020-02-19 14:25:00 +00:00
Nicholas Marriott
22e9cf04ca Add GitHub. 2020-02-19 06:01:54 +00:00
Nicholas Marriott
37919a6b6a This site is too stupid. 2020-02-17 12:20:53 +00:00
Nicholas Marriott
fdbc1116ef Add to FUNDING.yml. 2020-02-17 12:19:04 +00:00
Thomas Adam
0c6c8c4efc Merge branch 'obsd-master' 2020-02-15 16:01:25 +00:00
nicm
a1f6bd55b6 Add -a to list-keys to also list keys without notes with -N, suggested
by Shehu Dikko.
2020-02-15 15:08:08 +00:00
Thomas Adam
c391d50cbb Merge branch 'obsd-master' 2020-02-14 16:01:26 +00:00
nicm
58b47bf01b Fix top/bottom pane calculation with pane border status enabled,
reported by Stanislav Spassov.
2020-02-14 13:57:58 +00:00
Nicholas Marriott
9900ccd04e Change lock.yml options. 2020-02-14 11:43:12 +00:00
Nicholas Marriott
24cd726dae Add lock.yml file. 2020-02-14 11:40:32 +00:00
Thomas Adam
6c28d0dd06 Merge branch 'obsd-master' 2020-02-13 10:01:27 +00:00
nicm
f48b041cf2 Do not jump to next word end if already on a word end when selecting a
word. Fixes select-word with single character words and vi(1) keys. From
Mark Kelly.
2020-02-13 09:02:07 +00:00
Thomas Adam
a4d8437bc2 Merge branch 'obsd-master' 2020-02-11 08:01:30 +00:00
nicm
dc882adb2e Remove unused variables from Ben Boeckel, and a Pp from jmc. 2020-02-11 07:01:08 +00:00
Nicholas Marriott
470cba356d Merge branch '3.1-rc' 2020-02-07 16:43:41 +00:00
Nicholas Marriott
ae9ca620bd Remove duplicates, from Thomas Sattler. 2020-02-07 16:42:10 +00:00
Nicholas Marriott
400750bb26 Merge branch '3.1-rc' 2020-02-06 12:49:50 +00:00
nicm
096f0d35a6 Make list-keys description clearer in tmux.1 and remove an unused variable. 2020-02-06 12:49:02 +00:00
Thomas Adam
54553903de Merge branch 'obsd-master' 2020-02-05 14:01:26 +00:00
nicm
fb29242168 Make list-keys description clearer in tmux.1 and remove an unused variable. 2020-02-05 13:06:49 +00:00
Nicholas Marriott
c915cfc7e4 Merge branch '3.1-rc' 2020-02-04 07:46:59 +00:00
Nicholas Marriott
3ad4a7a571 Style nit in CHANGES. 2020-02-04 07:46:26 +00:00
Nicholas Marriott
47174f5130 Next is 3.2. 2020-02-04 07:45:29 +00:00
Nicholas Marriott
4822130b3c Merge branch '3.1-rc' 2020-02-04 07:44:49 +00:00
Nicholas Marriott
0bf153daa6 Update CONTRIBUTING.md 2020-02-04 07:43:34 +00:00
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
Nicholas Marriott
25ae028695 Add to CHANGES. 2019-12-01 08:53:10 +00:00
nicm
1b0cc4503e REG_STARTEND is not portable, but it turns out we don't actually need
it. From Evan Green, GitHub issue 1982.
2019-12-01 08:52:47 +00:00
Nicholas Marriott
a52fe9cf7f Add to CHANGES. 2019-12-01 08:50:45 +00:00
Thomas Adam
866b053f25 Merge branch 'obsd-master' 2019-11-29 18:01:24 +00:00
nicm
48897fbc44 Fix respawn-pane/window if default-command is set, reported by Janos Barbero. 2019-11-29 17:21:32 +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
nicm
ad98fad9a3 Do not crash when a format doesn't exist, reported by Thomas Sattler. 2019-11-28 22:23:40 +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
nicm
640149337f Missing after-kill-pane option. 2019-11-28 12:22:32 +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
Nicholas Marriott
7a30e6b941 Merge branch '3.0-rc' into 3.0a-rc 2019-11-27 20:55:19 +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
Nicholas Marriott
0134574a67 3.0a. 2019-11-27 20:49:59 +00:00
Nicholas Marriott
c942f11ba8 Use a malloc'd buffer for lsk since commands can be very long, from Gregory Pakosz. 2019-11-27 20:48:30 +00:00
nicm
5afe7eb850 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-11-27 20:48:03 +00:00
nicm
b2fd161b07 Do not crash trying to fix layout size if only one cell, from Azat Khuzhin. 2019-11-27 20:47:27 +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
Nicholas Marriott
bbcb199174 3.0 version. 2019-11-26 13:16:51 +00:00
nicm
47c0405b33 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-11-26 13:14:00 +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
nicm
f0712a7569 Do not set uninitialized signal mask when creating an empty pane. 2019-09-25 15:56:53 +01:00
nicm
9d1cecea8e Up and Down are already used, use < and > instead. 2019-09-25 15:56:39 +01:00
nicm
e6995196f2 Change menu key bindings to Up and Down and also close it on any mouse
press if opened by key.
2019-09-25 15:56:30 +01:00
nicm
a74e37d32d Mouse formats don't work in copy mode so don't try to use them. 2019-09-25 15:54:52 +01: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
nicm
6f8f4bb206 Make select-pane -P set window-active-style also to match previous
behaviour, reported by Thomas Sattler.
2019-09-24 12:20:25 +01: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
c739772436 3.0-rc5. 2019-09-16 09:01:56 +01: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
f27cac585c Add to CHANGES. 2019-09-16 09:00:56 +01:00
nicm
846d57e1b8 Make client exit if pane where input is going is closed. 2019-09-16 09:00:08 +01:00
Nicholas Marriott
54670d898f Missing headers from compat/asprintf.c, from cyyever at outlook dot com. 2019-09-16 08:59:57 +01:00
nicm
c45b255a88 It is not longer necessary to double-escape ; in %%%, problem reported
by Theo Buehler.
2019-09-16 08:59:49 +01:00
nicm
5e4f371408 Fix swap-window -d to work as intended, GitHub issue 1879 from Sam Stuewe. 2019-09-16 08:59:36 +01:00
nicm
7e4439beb7 Remove check for same size - size has already been changed so this
breaks reflow.
2019-09-16 08:58:35 +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
nicm
79f09b4d85 Add support for the SD (scroll down) escape sequence, GitHub issue 1861. 2019-08-26 17:28:24 +01:00
nicm
e85ea9f67d grid_view_delete_cells does need to clear, GitHub issue 1871. 2019-08-16 19:34:29 +01: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
nicm
26f2740110 xterm 348 now disables margins when resized, so send DECLRMM again. 2019-08-01 18:52:33 +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
Nicholas Marriott
b8b865fb17 Bump 3.0-rc up to master. 2019-07-29 10:53:04 +01:00
Nicholas Marriott
da552eb73b Merge branch 'master' into 3.0-rc 2019-07-29 10:51:30 +01:00
Thomas Adam
b90a9fcd13 Merge branch 'obsd-master' 2019-07-26 23:02:27 +01:00
Nicholas Marriott
e19622b8db Add to CHANGES. 2019-07-26 21:22:53 +01:00
nicm
8f40f791d9 Change "lost server" message to "server exited unexpectedly", from Neal
McBurnett in GitHub issue 1857.
2019-07-26 20:08:40 +00:00
Thomas Adam
bb9a123ddd Merge branch 'obsd-master' 2019-07-24 23:02:30 +01:00
nicm
90dba3ec66 Mark pane status line jobs with FORMAT_STATUS also so it redraws when
they finish, GitHub issue 1852.
2019-07-24 21:16:17 +00:00
Thomas Adam
47712fc113 Merge branch 'obsd-master' 2019-07-19 09:02:26 +01:00
nicm
bf6d1aeaa4 Add a few vi(1) key aliases, from D Ben Knoble. 2019-07-19 07:20:51 +00:00
nicm
df7b5292ab More man page fixes from lacygoill at lacygoill dot me, GitHub issue
1805.
2019-07-19 07:18:03 +00:00
Thomas Adam
7e7c8faa34 Merge branch 'obsd-master' 2019-07-17 21:02:26 +01:00
nicm
f6a9f6b4ad Use the right client for if -b. 2019-07-17 17:49:23 +00:00
nicm
9e7774bb96 Clear overlay on normal key press. 2019-07-17 17:46:51 +00:00
Nicholas Marriott
7c78ebce45 cc, make, libevent, ncurses are dependencies for all builds so no need to list
them for VCS build.
2019-07-17 08:57:22 +01:00
Nicholas Marriott
80b82c8d14 Mention dependencies earlier. 2019-07-17 08:56:10 +01:00
Thomas Adam
8e33cc61b1 Merge branch 'obsd-master' 2019-07-16 17:02:25 +01:00
nicm
99852f8401 Fix check for wrapping when redrawing entire lines, GitHub issue 1836. 2019-07-16 14:11:52 +00:00
Thomas Adam
b749a39cdb Merge branch 'obsd-master' 2019-07-16 13:02:24 +01:00
nicm
b89f2f28bb Fix grid clear code to correctly clear with the default background
colour rather than ending up with the used count higher than the total
size, GitHub issue 1829.
2019-07-16 10:30:56 +00:00
Thomas Adam
a786a59176 Merge branch 'obsd-master' 2019-07-15 21:02:26 +01:00
nicm
eac055bfaf Simplify code to work out if an extra line is needed in the cell. 2019-07-15 18:43:32 +00:00
nicm
6ceeceab7a Make layout_fix_offsets take a window like layout_fix_panes. 2019-07-15 18:25:07 +00:00
Thomas Adam
c9216493cf Merge branch 'obsd-master' 2019-07-10 17:02:25 +01:00
nicm
91b6145499 The command item changes so can't keep getting the target out of it, need to use
the one from the first item. Fixes crash reported by M Kelly.
2019-07-10 14:33:24 +00:00
Thomas Adam
9cbbdb90bf Merge branch 'obsd-master' 2019-07-10 13:02:26 +01:00
nicm
f4d858e7a0 Add -F to refresh-client to specify flags for control clients - one flag
at the moment, no-output which turns off forwarding pane output. From
Thomas Adam. GitHub issue 1834.
2019-07-10 11:20:10 +00:00
Thomas Adam
98ef369b27 Merge branch 'obsd-master' 2019-07-09 17:02:26 +01:00
nicm
fc2016dbb6 Add a -H flag to send-keys to send literal keys given as hex numbers
(needed for control clients to send mouse sequences). Also add some
format flags for UTF-8 and SGR mouse mode. Requested by Bradley Smith in
GitHub issues 1832 and 1833.
2019-07-09 14:03:12 +00:00
Thomas Adam
177599efb7 Merge branch 'obsd-master' 2019-07-09 15:02:25 +01:00
nicm
ad11d49d64 Do not leak empty lines, GitHub issue 1824. 2019-07-09 13:19:36 +00:00
nicm
b74b8be680 Add j and k for navigation in menus, GitHub issue 1828. 2019-07-09 12:44:47 +00:00
Thomas Adam
ab244cc7ad Merge branch 'obsd-master' 2019-07-08 23:02:26 +01:00
nicm
a4be028b76 Clear search marks before resize, GitHub issue 1823. 2019-07-08 20:29:11 +00:00
Thomas Adam
29b2d07b6b Merge branch 'obsd-master' 2019-07-08 15:02:25 +01:00
nicm
cbe781203f Use the clear history function for the 3J sequence rather than doing it manually. 2019-07-08 11:38:14 +00:00
Thomas Adam
3a4cf62aa9 Merge branch 'obsd-master' 2019-07-06 23:02:26 +01:00
nicm
ddf53d6e4e Correctly adjust mouse position if the status line is at the top and
more than one line. GitHub issue 1822.
2019-07-06 20:56:34 +00:00
nicm
3635b3cd6c Correctly clear underscore colour in grid_get_cell1, also fix struct
grid_cell to avoid padding. Fixes increased memory use reported by Suraj
N Kurapati.
2019-07-06 20:37:29 +00:00
Thomas Adam
bc112a8c89 Merge branch 'obsd-master' 2019-07-05 11:02:26 +01:00
nicm
55c694a467 Do not use uninitialized buffer name. 2019-07-05 07:52:27 +00:00
Thomas Adam
be5af704ad Merge branch 'obsd-master' 2019-07-02 23:02:26 +01:00
nicm
6a489fa7f6 Command prompt key presses need to avoid the command queue, GitHub issue
1817. Also a tmux.1 fix from jmc.
2019-07-02 20:09:19 +00:00
Thomas Adam
b9fbf02ad5 Merge branch 'obsd-master' 2019-07-01 09:02:25 +01:00
nicm
2da050413c Add a "fill" style attribute to clear the entire format drawing area in
a colour, GitHub issue 1815.
2019-07-01 06:56:00 +00:00
Thomas Adam
abcd4bd246 Merge branch 'obsd-master' 2019-06-30 21:02:26 +01:00
nicm
cf30e0f935 Do not double free window if pane fails to start. 2019-06-30 19:21:53 +00:00
Thomas Adam
68c2fc6824 Merge branch 'obsd-master' 2019-06-28 17:02:26 +01:00
deraadt
4ff7bc3eb3 When system calls indicate an error they return -1, not some arbitrary
value < 0.  errno is only updated in this case.  Change all (most?)
callers of syscalls to follow this better, and let's see if this strictness
helps us in the future.
2019-06-28 13:35:05 +00:00
Thomas Adam
b6b4f86cfc Merge branch 'obsd-master' 2019-06-28 09:02:24 +01:00
deraadt
6ce38b7395 asprintf returns -1, not an arbitrary value < 0. Also upon error the
(very sloppy specification) leaves an undefined value in *ret, so it is
wrong to inspect it, the error condition is enough.
discussed a little with nicm, and then much more with millert until we
were exasperated
2019-06-28 05:44:09 +00:00
Thomas Adam
a07df21e79 Merge branch 'obsd-master' 2019-06-27 21:02:25 +01:00
jmc
b434692db2 minor eol issues; 2019-06-27 17:29:16 +00:00
Thomas Adam
e483ce138f Merge branch 'obsd-master' 2019-06-27 17:02:26 +01:00
nicm
dae2868d12 Add support for underscore colours with Setulc capability, mostly from
Kai Moschcau.
2019-06-27 15:17:41 +00:00
nicm
5a501a8ae2 Pass keys that aren't 0-9 on to normal key processing when display-panes
is active (restores previous behaviour).
2019-06-27 15:49:43 +01:00
Thomas Adam
c4a9299956 Merge branch 'obsd-master' 2019-06-26 21:02:26 +01:00
nicm
3a6d90adad Fix a typo in window_pane_find_down (w not wp) and a missing PANE_STATUS_TOP. 2019-06-26 18:44:22 +00:00
nicm
87ea14328c Pass keys that aren't 0-9 on to normal key processing when display-panes
is active (restores previous behaviour).
2019-06-26 18:28:31 +00:00
Thomas Adam
f797ac9ff6 Merge branch 'obsd-master' 2019-06-26 15:02:26 +01:00
nicm
c599ad63f8 Log window and pane resizes. 2019-06-26 13:05:24 +00:00
nicm
80d76612b8 Fix some comments (top/bottom not left/right). 2019-06-26 13:05:06 +00:00
nicm
d83f356218 Add #define for the pane status line option position numbers. 2019-06-26 13:03:47 +00:00
Thomas Adam
a1d4bf20f7 Merge branch 'obsd-master' 2019-06-24 13:02:26 +01:00
nicm
20b938bcb1 Expand arguments to C and s format modifiers (matches m which already expands). 2019-06-24 10:04:29 +00:00
Thomas Adam
c14b0d7c00 Merge branch 'obsd-master' 2019-06-24 11:02:26 +01:00
nicm
bdab595095 Trim trailing spaces when matching. 2019-06-24 08:20:02 +00:00
Thomas Adam
50c4c5917d Merge branch 'obsd-master' 2019-06-23 13:02:26 +01:00
nicm
f1100f97f7 Do not always set scope for panes because the window check might fail,
GitHub issue 1810.
2019-06-23 10:00:29 +00:00
Thomas Adam
69a2f73449 Merge branch 'obsd-master' 2019-06-21 09:02:25 +01:00
nicm
e3c2772d2f Man page fixes from lacygoill at lacygoill dot me. 2019-06-21 07:11:01 +00:00
Thomas Adam
70775b3c28 Merge branch 'obsd-master' 2019-06-20 23:02:28 +01:00
nicm
1d8e545bc1 Add -r to find-window for regex instead of fnmatch. 2019-06-20 20:31:04 +00:00
Thomas Adam
c4a92e5799 Merge branch 'obsd-master' 2019-06-20 21:02:27 +01:00
nicm
97a317a656 Need to always check focus even if not current window. 2019-06-20 19:29:38 +00:00
nicm
cb5e681ef6 FIx return of options_scope_from_name on error. 2019-06-20 18:13:04 +00:00
Thomas Adam
5aae58295e Merge branch 'obsd-master' 2019-06-20 19:02:27 +01:00
Thomas Adam
4a44ae06bf Merge branch 'obsd-master' 2019-06-20 17:02:26 +01:00
nicm
f4e835754c Fix how regex substitution works with empty matches. 2019-06-20 15:40:14 +00:00
Thomas Adam
f8ad72b2ee Merge branch 'obsd-master' 2019-06-20 15:02:26 +01:00
nicm
fc1df91e03 allow-rename and alternate-screen can be pane options. 2019-06-20 13:40:22 +00:00
nicm
c1573727f0 Still need to walk the options tree for user options. 2019-06-20 13:39:17 +00:00
Nicholas Marriott
a0e2c1b4ca Add to CHANGES. 2019-06-20 13:08:22 +01:00
nicm
5f92f92908 Add a per-pane option set. Pane options inherit from window options (so
there should be no change to existing behaviour) and are set and shown
with set-option -p and show-options -p.

Change remain-on-exit and window-style/window-active-style to be pane
options (some others will be changed later).

This makes select-pane -P and -g unnecessary so no longer document them
(they still work) and no longer document set-window-option and
show-window-options in favour of set-option -w and show-options -w.
2019-06-20 11:59:59 +00:00
Thomas Adam
adca856806 Merge branch 'obsd-master' 2019-06-20 11:02:28 +01:00
Thomas Adam
03945276f7 Merge branch 'obsd-master' 2019-06-20 09:02:26 +01:00
nicm
c1ede507d9 Add a helper function to work out option table from name. 2019-06-20 07:41:29 +00:00
nicm
cd1fc42df6 Add a -A flag to show-options to show parent options as well. 2019-06-20 07:10:56 +00:00
nicm
ae541287d3 Expand command formats in %if and move the config file loading later (to
when the first client has identified) so all the client formats are
available, fixes problems reported by Thomas Sattler.
2019-06-20 06:51:36 +00:00
Nicholas Marriott
b3ca410bc3 Use KERN_PROC_CWD on NetBSD, from Leonardo Taccari. 2019-06-20 06:57:37 +01:00
Nicholas Marriott
ec151b79ec Merge branch '3.0-rc' 2019-06-18 19:43:20 +01:00
nicm
797042584e Handle comments more correctly inside {}, from Avi Halachmi. 2019-06-18 19:43:13 +01:00
Nicholas Marriott
df07723e20 Add a config. 2019-06-18 19:42:52 +01:00
Thomas Adam
ee9bc355f5 Merge branch 'obsd-master' 2019-06-18 13:02:28 +01:00
nicm
250fdd08be Handle comments more correctly inside {}, from Avi Halachmi. 2019-06-18 11:17:40 +00:00
nicm
9272fe36e2 Add a cmdq_continue function rather than twiddling the flag directly. 2019-06-18 11:08:42 +00:00
Thomas Adam
5895f1d9f7 Merge branch 'obsd-master' 2019-06-15 09:02:26 +01:00
Nicholas Marriott
fadbf497a4 Merge branch '3.0-rc' 2019-06-15 07:45:58 +01:00
Nicholas Marriott
748633c887 Remove a stray abort. 2019-06-15 07:45:41 +01:00
Nicholas Marriott
d53c2d0ebe Merge branch '3.0-rc' 2019-06-15 07:44:58 +01:00
Nicholas Marriott
08f55414e1 Remove cmd-list.c line in master. 2019-06-15 07:42:49 +01:00
Nicholas Marriott
7a4a286ea2 Merge branch '3.0-rc' 2019-06-15 07:38:01 +01:00
nicm
9ae658983c Do not loop forever if a menu item contains invisible characters,
reported by Thomas Sattler.
2019-06-15 07:37:44 +01:00
nicm
03da0ced46 Use the right format modifier when comparing, and remove a couple of
unused variables.
2019-06-15 06:33:48 +00:00
Thomas Adam
539a6e7fa0 Merge branch 'obsd-master' 2019-06-14 19:02:26 +01:00
nicm
c95cd9ed5e Do not loop forever if a menu item contains invisible characters,
reported by Thomas Sattler.
2019-06-14 15:35:58 +00:00
Nicholas Marriott
4c28ed4e4e Merge branch '3.0-rc' 2019-06-14 16:10:38 +01:00
Nicholas Marriott
ae83a5b010 Add to CHANGES. 2019-06-14 16:09:49 +01:00
Nicholas Marriott
29b70e2757 Merge branch '3.0-rc' 2019-06-14 16:06:20 +01:00
Nicholas Marriott
dcb2bb33a2 Merge branch '3.0-rc' 2019-06-14 16:05:26 +01:00
Nicholas Marriott
8da756c4f0 Restore missing functions. 2019-06-14 16:02:34 +01:00
Nicholas Marriott
7bb8ab1c0e Add a bit to {}. 2019-06-14 15:54:36 +01:00
Thomas Adam
5e9757b30b Merge branch 'obsd-master' 2019-06-14 15:51:09 +01:00
nicm
d1d3bbb458 Show filename with -v for source-file. 2019-06-14 13:34:45 +00:00
nicm
45203582ff A couple of minor parser changes around conditions: 1) only treat #{
specially after a condition, otherwise as a comment (which is more as
most people expect) 2) allow formats to be quoted after a condition.
2019-06-14 12:04:11 +00:00
Nicholas Marriott
4bbf941436 Merge branch '3.0-rc' 2019-06-14 10:34:05 +01:00
nicm
f35f15b107 Use the right client when working out where to save or load the buffer,
reported by kn@.
2019-06-13 21:44:13 +00:00
nicm
eef11b64e1 Do not crash if the environment variable is present but empty. 2019-06-13 21:24:09 +00:00
jmc
b8a9c740bb tweak previous; 2019-06-13 21:04:21 +00:00
nicm
26b9a8e49b Set the cursor x at the same time as changing the y or the end of line
marker may not be redrawn.
2019-06-13 20:38:05 +00:00
nicm
3e72e98e3b Add regular expression support for the format search, match and
substitute modifiers.
2019-06-13 19:46:00 +00:00
Thomas Adam
d5902eeae9 Merge branch 'obsd-master' 2019-06-12 11:02:25 +01:00
nicm
7e6a26cc9d Do not use $TMUX to find the session because for windows in multiple
sessions it is wrong as often as it is right, and for windows in one
session it is pointless. Instead check TMUX_PANE to find the pane and
look for the MRU session as usual. GitHub issue 1793.
2019-06-12 09:10:29 +00:00
nicm
b12df01861 Pass target on to new commands with if -F. 2019-06-12 08:47:07 +00:00
nicm
9d42bd328c Do not always resize the window back to its original size after applying
a layout, let the normal window resize process do it. This means windows
are not resized at all if window-size is manual, and are not resized
multiple times if later attached to a different size terminal.
2019-06-12 08:08:33 +00:00
Thomas Adam
057c04e32a Merge branch 'obsd-master' 2019-06-11 15:02:26 +01:00
Nicholas Marriott
8e971f187a Update README.md 2019-06-11 14:27:40 +01:00
Nicholas Marriott
e8814a7e93 Add to CHANGES. 2019-06-11 14:23:47 +01:00
nicm
1a9f9c09b4 Do not resize panes unless they are in an attached, active window. From
Morten M Neergaard in GitHub issue 1782.
2019-06-11 13:09:00 +00:00
Thomas Adam
d9ad461b5e Merge branch 'obsd-master' 2019-06-09 09:02:26 +01:00
nicm
915097d312 Exiting alternate screen mode should restore cursor position and
attributes even if already outside alternate screen mode. GitHub issue
1789.
2019-06-09 06:50:24 +00:00
Thomas Adam
181841fb67 Merge branch 'obsd-master' 2019-06-09 01:02:26 +01:00
nicm
43796bf131 Do not try to parse command when unsetting, GitHub issue 1788. 2019-06-08 21:48:29 +00:00
Thomas Adam
e13c1e5320 Merge branch 'obsd-master' 2019-06-07 23:02:26 +01:00
nicm
e37f34facc Do not load the config file if the server is exiting because it failed
to start, otherwise commands like lsk which start the server again can
end up looping infinitely. Also make the first client exit
correctly. Problem reported by Wael M Nasreddine.
2019-06-07 20:09:17 +00:00
Thomas Adam
c91680822d Merge branch 'obsd-master' 2019-06-05 23:02:26 +01:00
Nicholas Marriott
37bb993f53 Merge branch '3.0-rc' 2019-06-05 21:06:30 +01:00
Thomas Adam
1ec3fb5f30 Merge branch 'obsd-master' 2019-06-05 21:02:26 +01:00
nicm
8f40796f05 Add a -v flag to source-file to show the commands and line numbers. 2019-06-05 20:00:53 +00:00
nicm
09e90c1645 Need to increment the argument to skip the prefix earlier, fixes
repeated incremental search in copy mode, reported by Kaushal Modi in
GitHub issue 1780.
2019-06-05 19:00:36 +00:00
Nicholas Marriott
f274b1b9d7 Merge branch '3.0-rc' 2019-06-05 15:13:56 +01:00
Thomas Adam
d3f0c72e20 Merge branch 'obsd-master' 2019-06-03 21:02:26 +01:00
nicm
4ca1de1b8b Add new-session -X and attach-session -x to send SIGHUP to parent when
detaching (like detach-client -P). From Colin Watson in GitHub issue
1773.
2019-06-03 18:28:37 +00:00
Thomas Adam
f6933e43c2 Merge branch 'obsd-master' 2019-06-02 09:02:27 +01:00
nicm
900238a306 yacc(1) copies its union so it is not a good place to store
TAILQ_HEADs. Allocate them instead. Found from a problem reported by
sthen@.
2019-06-02 07:10:15 +00:00
Thomas Adam
027a7ac829 Merge branch 'obsd-master' 2019-06-01 19:02:26 +01:00
nicm
2c6c3a1d27 If only one of -x or -y is given, use the calculated size for the
other. Pointed out by Ben Boeckel.
2019-06-01 16:21:37 +00:00
Thomas Adam
8382ae65b7 Merge branch 'obsd-master' 2019-06-01 09:02:27 +01:00
Nicholas Marriott
ec690208a3 Move link. 2019-06-01 08:07:22 +01:00
Nicholas Marriott
b28b8312f2 Mention CHANGES. 2019-06-01 08:05:55 +01:00
Nicholas Marriott
c2d79add31 Should be caps. 2019-06-01 07:28:31 +01:00
Nicholas Marriott
feff55b92f Try this instead. 2019-06-01 07:26:39 +01:00
Nicholas Marriott
3d29b97768 Link to file. 2019-06-01 07:24:57 +01:00
nicm
39ea8a2787 Need stdlib.h, from Ben Boeckel. 2019-06-01 06:20:22 +00:00
Thomas Adam
480cdedcac Merge branch 'obsd-master' 2019-06-01 01:02:25 +01:00
Nicholas Marriott
88ee5a1a00 Add a crashing config. 2019-05-31 22:50:30 +01:00
nicm
2c5f3074bc Fix warnings, from Ben Boeckel. 2019-05-31 21:41:17 +00:00
Nicholas Marriott
9d2ef8bf0d Merge branch '3.0-rc' 2019-05-31 15:18:56 +01:00
Thomas Adam
adf9e77702 Merge branch 'obsd-master' 2019-05-31 15:02:27 +01:00
nicm
82e47403c6 Allow % strings that are all numbers or %s, and fix a double free. Both
reported by George Nachman, GitHub issues 1765 and 1766.
2019-05-31 11:34:09 +00:00
Thomas Adam
41b8bb4eef Merge branch 'obsd-master' 2019-05-30 23:02:27 +01:00
nicm
b26523c26d Remove a leftover abort and some fixes from cppcheck. 2019-05-30 20:54:03 +00:00
Nicholas Marriott
200a1c62c6 Merge branch '3.0-rc' 2019-05-30 13:12:50 +01:00
Thomas Adam
2df757521a Merge branch 'obsd-master' 2019-05-30 13:02:26 +01:00
nicm
8fb796b5b3 No longer need to reduce line number by one. 2019-05-30 10:04:33 +00:00
Thomas Adam
82b25a9d62 Merge branch 'obsd-master' 2019-05-30 11:02:27 +01:00
nicm
89d2c7eb26 I had hoped that non-xenl terminals had died out, at least in fairly
modern OSs, but no - DragonFly BSD's console returns to haunt us. Fix it
at least somewhat. GitHub issue 1763.
2019-05-30 07:42:41 +00:00
Nicholas Marriott
026450c1a8 Add test for xenl terminals. 2019-05-30 07:52:24 +01:00
Nicholas Marriott
c5e2532b67 Merge branch '3.0-rc' 2019-05-29 23:04:28 +01:00
Thomas Adam
414208aab1 Merge branch 'obsd-master' 2019-05-29 23:02:25 +01:00
nicm
7dced37673 Use VIS_CSTYLE for the arguments and add the missing escapes it can
generate to the parser.
2019-05-29 20:05:14 +00:00
Nicholas Marriott
95d68fcba1 Merge branch '3.0-rc' 2019-05-29 20:43:29 +01:00
nicm
c17edd594e The line number needs to be updated only after the \n is processed by
the parser, so store a flag and update it next time around. Also each
new line needs its own shared data.
2019-05-29 19:34:42 +00:00
Nicholas Marriott
2ce8e0110c Don't even need this? 2019-05-29 14:32:49 +01:00
Nicholas Marriott
4425c1e1b3 Tweak again. 2019-05-29 14:24:09 +01:00
Nicholas Marriott
409d52ed41 Try OS X. 2019-05-29 14:19:37 +01:00
Thomas Adam
e90b5dcea3 Merge branch 'obsd-master' 2019-05-29 13:02:35 +01:00
Nicholas Marriott
ffcc60211d Merge branch '3.0-rc' 2019-05-29 12:13:55 +01:00
Nicholas Marriott
480ba99a16 Merge branch '3.0-rc' 2019-05-29 12:07:05 +01:00
nicm
a4424fbebf Support \ooo escapes, from Avi Halachmi. 2019-05-29 10:08:36 +00:00
Nicholas Marriott
eefbbfde77 Merge branch '3.0-rc' 2019-05-28 21:05:00 +01:00
Thomas Adam
c96957583f Merge branch 'obsd-master' 2019-05-28 21:02:28 +01:00
nicm
580cd16f4c Redraw status line if size changes, GitHub issue 1762. Also fix length
of target buffer when pasting into status line.
2019-05-28 18:53:36 +00:00
nicm
4382538e4b Do not read past the end of the argument string if it is empty. 2019-05-28 18:30:30 +00:00
Nicholas Marriott
e7a530fe4c Next will be 3.1. 2019-05-28 15:48:31 +01:00
107 changed files with 7286 additions and 3100 deletions

View File

@@ -2,13 +2,20 @@
Before opening an issue, please ensure that: Before opening an issue, please ensure that:
- Your problem is a specific problem or question or suggestion, not a general
complaint.
- `$TERM` inside tmux is screen, screen-256color, tmux or tmux-256color. Check - `$TERM` inside tmux is screen, screen-256color, tmux or tmux-256color. Check
by running `echo $TERM` inside tmux. by running `echo $TERM` inside tmux.
- You can reproduce the problem with the latest tmux release, or a build from - You can reproduce the problem with the latest tmux release, or a build from
Git master. Git master.
- Your question or issue is not covered in the manual (run man tmux). - Your question or issue is not covered [in the
manual](https://man.openbsd.org/tmux.1) (run `man tmux`).
- Your problem is not mentioned in [the CHANGES
file](https://raw.githubusercontent.com/tmux/tmux/master/CHANGES).
- Nobody else has opened the same issue recently. - Nobody else has opened the same issue recently.

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

@@ -0,0 +1,2 @@
github: nicm
liberapay: tmux

View File

@@ -3,6 +3,9 @@
Please read https://github.com/tmux/tmux/blob/master/.github/CONTRIBUTING.md Please read https://github.com/tmux/tmux/blob/master/.github/CONTRIBUTING.md
before opening an issue. before opening an issue.
If you have upgraded, make sure your issue is not covered in the CHANGES file
for your version: https://raw.githubusercontent.com/tmux/tmux/master/CHANGES
Describe the problem and the steps to reproduce. Add a minimal tmux config if Describe the problem and the steps to reproduce. Add a minimal tmux config if
necessary. Screenshots can be helpful, but no more than one or two. necessary. Screenshots can be helpful, but no more than one or two.

19
.github/README.md vendored
View File

@@ -30,7 +30,8 @@ configure with `--enable-utempter` to enable this.
### From version control ### From version control
To get and build the latest from version control: To get and build the latest from version control - note that this requires
`autoconf`, `automake` and `pkg-config`:
~~~bash ~~~bash
git clone https://github.com/tmux/tmux.git git clone https://github.com/tmux/tmux.git
@@ -39,10 +40,6 @@ sh autogen.sh
./configure && make ./configure && make
~~~ ~~~
(Note that this requires at least a working C compiler, `make`, `autoconf`,
`automake`, `pkg-config` as well as `libevent` and `ncurses` libraries and
headers.)
## Contributing ## Contributing
Bug reports, feature suggestions and especially code contributions are most Bug reports, feature suggestions and especially code contributions are most
@@ -50,14 +47,12 @@ welcome. Please send by email to:
tmux-users@googlegroups.com tmux-users@googlegroups.com
Or open a GitHub issue or pull request. Or open a GitHub issue or pull request. **Please read [this
document](CONTRIBUTING.md) before opening an issue.**
There is [a TODO list](https://github.com/tmux/tmux/wiki/Contributing) which There is [a list of suggestions for contributions](https://github.com/tmux/tmux/wiki/Contributing).
explains some ideas for tmux not yet developed. Please feel free to ask for Please feel free to ask on the mailing list if you're thinking of working on something or need
clarifications on the mailing list if you're thinking of working on these or further information.
need further information.
Please read the CONTRIBUTING file before opening an issue.
## Documentation ## Documentation

10
.github/lock.yml vendored Normal file
View File

@@ -0,0 +1,10 @@
daysUntilLock: 180
skipCreatedBefore: false
exemptLabels: []
lockLabel: false
lockComment: >
This thread has been automatically locked since there has not been
any recent activity after it was closed. Please open a new issue for
related bugs.
setLockReason: false
#only: issues

221
CHANGES
View File

@@ -1,4 +1,215 @@
CHANGES FROM 2.9 to 3.0 CHANGES FROM 3.1 TO 3.1a
* Do not close stdout prematurely in control mode since it is needed to print
exit messages. Prevents hanging when detaching with iTerm2.
CHANGES FROM 3.0a TO 3.1
* Only search the visible part of the history when marking (highlighting)
search terms. This is much faster than searching the whole history and solves
problems with large histories. The count of matches shown is now the visible
matches rather than all matches.
* Search using regular expressions in copy mode. search-forward and
search-backward use regular expressions by default; the incremental versions
do not.
* Turn off mouse mode 1003 as well as the rest when exiting.
* Add selection_active format for when the selection is present but not moving
with the cursor.
* Fix dragging with modifier keys, so binding keys such as C-MouseDrag1Pane and
C-MouseDragEnd1Pane now work.
* Add -a to list-keys to also list keys without notes with -N.
* Do not jump to next word end if already on a word end when selecting a word;
fixes select-word with single character words and vi(1) keys.
* Fix top and bottom pane calculation with pane border status enabled.
* 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].
* 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).
* Do not use bright when emulating 256 colours on an 8 colour terminal because
it is also bold on some terminals.
* 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 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.
* 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.
* Add a "latest" window-size option which tries to size windows based on the
most recently used client. This is now the default.
* 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
* Do not require REG_STARTEND.
* Respawn panes or windows correctly if default-command is set.
* Add missing option for after-kill-pane hook.
* Fix for crash with a format variable that doesn't exist.
* Do not truncate list-keys output on some platforms.
* Do not crash when restoring a layout with only one pane.
CHANGES FROM 2.9 TO 3.0
* Workaround invalid layout strings generated by older tmux versions and add
some additional sanity checks
* xterm 348 now disables margins when resized, so send DECLRMM again after
resize.
* Add support for the SD (scroll down) escape sequence.
* Expand arguments to C and s format modifiers to match the m modifier.
* Add support for underscore colours (Setulc capability must be added with
terminal-overrides as described in tmux(1)).
* Add a "fill" style attribute for the fill colour of the drawing area (where
appropriate).
* New -H flag to send-keys to send literal keys.
* Format variables for pane mouse modes (mouse_utf8_flag and mouse_sgr_flag)
and for origin mode (origin_flag).
* Add -F to refresh-client for flags for control mode clients, only one flag
(no-output) supported at the moment.
* Add a few vi(1) keys for menus.
* Add pane options, set with set-option -p and displayed with show-options -p.
Pane options inherit from window options (so every pane option is also
a window option). The pane style is now configured by setting window-style
and window-active-style in the pane options; select-pane -P and -g now change
the option but are no longer documented.
* Do not document set-window-option and show-window-options. set-option -w and
show-options -w should be used instead.
* Add a -A flag to show-options to show parent options as well (they are marked
with a *).
* Resize panes lazily - do not resize unless they are in an attached, active
window.
* Add regular expression support for the format search, match and substitute
modifiers and make them able to ignore case. find-window now accepts -r to
use regular expressions.
* Do not use $TMUX to find the session because for windows in multiple sessions
it is wrong as often as it is right, and for windows in one session it is
pointless. Instead use TMUX_PANE if it is present.
* Do not always resize the window back to its original size after applying a
layout, keep it at the layout size until it must be resized (for example when
attached and window-size is not manual).
* Add new-session -X and attach-session -x to send SIGHUP to parent when
detaching (like detach-client -P).
* Support for octal escapes in strings (such as \007) and improve list-keys
output so it parses correctly if copied into a configuration file.
* INCOMPATIBLE: Add a new {} syntax to the configuration file. This is a string * INCOMPATIBLE: Add a new {} syntax to the configuration file. This is a string
similar to single quotes but also includes newlines and allows commands that similar to single quotes but also includes newlines and allows commands that
@@ -29,7 +240,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
@@ -64,11 +275,11 @@ CHANGES FROM 2.9 to 3.0
* Add the ability to infer an option type (server, session, window) from its * Add the ability to infer an option type (server, session, window) from its
name to show-options (it was already present in set-option). name to show-options (it was already present in set-option).
CHANGES FROM 2.9 to 2.9a CHANGES FROM 2.9 TO 2.9a
* Fix bugs in select-pane and the main-horizontal and main-vertical layouts. * Fix bugs in select-pane and the main-horizontal and main-vertical layouts.
CHANGES FROM 2.8 to 2.9 CHANGES FROM 2.8 TO 2.9
* Attempt to preserve horizontal cursor position as well as vertical with * Attempt to preserve horizontal cursor position as well as vertical with
reflow. reflow.
@@ -193,7 +404,7 @@ CHANGES FROM 2.8 to 2.9
moves up, down, left or right and -c returns to automatic cursor moves up, down, left or right and -c returns to automatic cursor
tracking. The position is reset when the current window is changed. tracking. The position is reset when the current window is changed.
CHANGES FROM 2.7 to 2.8 CHANGES FROM 2.7 TO 2.8
* Make display-panes block the client until a pane is chosen or it * Make display-panes block the client until a pane is chosen or it
times out. times out.

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)
@@ -88,7 +91,6 @@ dist_tmux_SOURCES = \
cmd-list-panes.c \ cmd-list-panes.c \
cmd-list-sessions.c \ cmd-list-sessions.c \
cmd-list-windows.c \ cmd-list-windows.c \
cmd-list.c \
cmd-load-buffer.c \ cmd-load-buffer.c \
cmd-lock-server.c \ cmd-lock-server.c \
cmd-move-window.c \ cmd-move-window.c \
@@ -131,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 \
@@ -152,6 +155,7 @@ dist_tmux_SOURCES = \
options.c \ options.c \
paste.c \ paste.c \
proc.c \ proc.c \
regsub.c \
resize.c \ resize.c \
screen-redraw.c \ screen-redraw.c \
screen-write.c \ screen-write.c \

6
README
View File

@@ -26,16 +26,14 @@ To build and install tmux from a release tarball, use:
tmux can use the utempter library to update utmp(5), if it is installed - run tmux can use the utempter library to update utmp(5), if it is installed - run
configure with --enable-utempter to enable this. configure with --enable-utempter to enable this.
To get and build the latest from version control: To get and build the latest from version control - note that this requires
autoconf, automake and pkg-config:
$ git clone https://github.com/tmux/tmux.git $ git clone https://github.com/tmux/tmux.git
$ cd tmux $ cd tmux
$ sh autogen.sh $ sh autogen.sh
$ ./configure && make $ ./configure && make
(Note that this requires at least a working C compiler, make, autoconf,
automake, pkg-config as well as libevent and ncurses libraries and headers.)
* Contributing * Contributing
Bug reports, feature suggestions and especially code contributions are most Bug reports, feature suggestions and especially code contributions are most

View File

@@ -37,6 +37,7 @@ TAILQ_HEAD(args_values, args_value);
struct args_entry { struct args_entry {
u_char flag; u_char flag;
struct args_values values; struct args_values values;
u_int count;
RB_ENTRY(args_entry) entry; RB_ENTRY(args_entry) entry;
}; };
@@ -73,6 +74,7 @@ args_parse(const char *template, int argc, char **argv)
optreset = 1; optreset = 1;
optind = 1; optind = 1;
optarg = NULL;
while ((opt = getopt(argc, argv, template)) != -1) { while ((opt = getopt(argc, argv, template)) != -1) {
if (opt < 0) if (opt < 0)
@@ -82,6 +84,7 @@ args_parse(const char *template, int argc, char **argv)
return (NULL); return (NULL);
} }
args_set(args, opt, optarg); args_set(args, opt, optarg);
optarg = NULL;
} }
argc -= optind; argc -= optind;
argv += optind; argv += optind;
@@ -173,6 +176,7 @@ args_print(struct args *args)
size_t len; size_t len;
char *buf; char *buf;
int i; int i;
u_int j;
struct args_entry *entry; struct args_entry *entry;
struct args_value *value; struct args_value *value;
@@ -186,7 +190,8 @@ args_print(struct args *args)
if (*buf == '\0') if (*buf == '\0')
args_print_add(&buf, &len, "-"); args_print_add(&buf, &len, "-");
args_print_add(&buf, &len, "%c", entry->flag); for (j = 0; j < entry->count; j++)
args_print_add(&buf, &len, "%c", entry->flag);
} }
/* Then the flags with arguments. */ /* Then the flags with arguments. */
@@ -212,12 +217,14 @@ args_escape(const char *s)
if (*s == '\0') if (*s == '\0')
return (xstrdup(s)); return (xstrdup(s));
if ((strchr(quoted, s[0]) != NULL || s[0] == '~') && s[1] == '\0') { if (s[0] != ' ' &&
(strchr(quoted, s[0]) != NULL || s[0] == '~') &&
s[1] == '\0') {
xasprintf(&escaped, "\\%c", s[0]); xasprintf(&escaped, "\\%c", s[0]);
return (escaped); return (escaped);
} }
flags = VIS_OCTAL|VIS_TAB|VIS_NL; flags = VIS_OCTAL|VIS_CSTYLE|VIS_TAB|VIS_NL;
if (s[strcspn(s, quoted)] != '\0') if (s[strcspn(s, quoted)] != '\0')
flags |= VIS_DQ; flags |= VIS_DQ;
utf8_stravis(&escaped, s, flags); utf8_stravis(&escaped, s, flags);
@@ -241,7 +248,12 @@ args_escape(const char *s)
int int
args_has(struct args *args, u_char ch) args_has(struct args *args, u_char ch)
{ {
return (args_find(args, ch) != NULL); struct args_entry *entry;
entry = args_find(args, ch);
if (entry == NULL)
return (0);
return (entry->count);
} }
/* Set argument value in the arguments tree. */ /* Set argument value in the arguments tree. */
@@ -255,9 +267,11 @@ args_set(struct args *args, u_char ch, const char *s)
if (entry == NULL) { if (entry == NULL) {
entry = xcalloc(1, sizeof *entry); entry = xcalloc(1, sizeof *entry);
entry->flag = ch; entry->flag = ch;
entry->count = 1;
TAILQ_INIT(&entry->values); TAILQ_INIT(&entry->values);
RB_INSERT(args_tree, &args->tree, entry); RB_INSERT(args_tree, &args->tree, entry);
} } else
entry->count++;
if (s != NULL) { if (s != NULL) {
value = xcalloc(1, sizeof *value); value = xcalloc(1, sizeof *value);

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

115
cfg.c
View File

@@ -52,7 +52,7 @@ cfg_done(__unused struct cmdq_item *item, __unused void *data)
cfg_show_causes(RB_MIN(sessions, &sessions)); cfg_show_causes(RB_MIN(sessions, &sessions));
if (cfg_item != NULL) if (cfg_item != NULL)
cfg_item->flags &= ~CMDQ_WAITING; cmdq_continue(cfg_item);
status_prompt_load_history(); status_prompt_load_history();
@@ -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, NULL, 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, NULL, 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));
} }
@@ -126,6 +165,8 @@ load_cfg(const char *path, struct client *c, struct cmdq_item *item, int flags,
pi.flags = flags; pi.flags = flags;
pi.file = path; pi.file = path;
pi.line = 1; pi.line = 1;
pi.item = item;
pi.c = c;
pr = cmd_parse_from_file(f, &pi); pr = cmd_parse_from_file(f, &pi);
fclose(f); fclose(f);
@@ -143,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(c, 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)

361
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 *);
@@ -202,7 +201,7 @@ client_exit_message(void)
case CLIENT_EXIT_TERMINATED: case CLIENT_EXIT_TERMINATED:
return ("terminated"); return ("terminated");
case CLIENT_EXIT_LOST_SERVER: case CLIENT_EXIT_LOST_SERVER:
return ("lost server"); return ("server exited unexpectedly");
case CLIENT_EXIT_EXITED: case CLIENT_EXIT_EXITED:
return ("exited"); return ("exited");
case CLIENT_EXIT_SERVER_EXITED: case CLIENT_EXIT_SERVER_EXITED:
@@ -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,255 @@ 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 < 0 && (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);
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, 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 +790,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 +802,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 +821,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 +854,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

@@ -37,8 +37,8 @@ const struct cmd_entry cmd_attach_session_entry = {
.name = "attach-session", .name = "attach-session",
.alias = "attach", .alias = "attach",
.args = { "c:dErt:", 0, 0 }, .args = { "c:dErt:x", 0, 0 },
.usage = "[-dEr] [-c working-directory] " CMD_TARGET_SESSION_USAGE, .usage = "[-dErx] [-c working-directory] " CMD_TARGET_SESSION_USAGE,
/* -t is special */ /* -t is special */
@@ -48,7 +48,7 @@ const struct cmd_entry cmd_attach_session_entry = {
enum cmd_retval enum cmd_retval
cmd_attach_session(struct cmdq_item *item, const char *tflag, int dflag, cmd_attach_session(struct cmdq_item *item, const char *tflag, int dflag,
int rflag, const char *cflag, int Eflag) int xflag, int rflag, const char *cflag, int Eflag)
{ {
struct cmd_find_state *current = &item->shared->current; struct cmd_find_state *current = &item->shared->current;
enum cmd_find_type type; enum cmd_find_type type;
@@ -58,6 +58,7 @@ cmd_attach_session(struct cmdq_item *item, const char *tflag, int dflag,
struct winlink *wl; struct winlink *wl;
struct window_pane *wp; struct window_pane *wp;
char *cause; char *cause;
enum msgtype msgtype;
if (RB_EMPTY(&sessions)) { if (RB_EMPTY(&sessions)) {
cmdq_error(item, "no sessions"); cmdq_error(item, "no sessions");
@@ -102,11 +103,15 @@ cmd_attach_session(struct cmdq_item *item, const char *tflag, int dflag,
c->last_session = c->session; c->last_session = c->session;
if (c->session != NULL) { if (c->session != NULL) {
if (dflag) { if (dflag || xflag) {
if (xflag)
msgtype = MSG_DETACHKILL;
else
msgtype = MSG_DETACH;
TAILQ_FOREACH(c_loop, &clients, entry) { TAILQ_FOREACH(c_loop, &clients, entry) {
if (c_loop->session != s || c == c_loop) if (c_loop->session != s || c == c_loop)
continue; continue;
server_client_detach(c_loop, MSG_DETACH); server_client_detach(c_loop, msgtype);
} }
} }
if (!Eflag) if (!Eflag)
@@ -122,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);
@@ -131,11 +137,15 @@ cmd_attach_session(struct cmdq_item *item, const char *tflag, int dflag,
if (rflag) if (rflag)
c->flags |= CLIENT_READONLY; c->flags |= CLIENT_READONLY;
if (dflag) { if (dflag || xflag) {
if (xflag)
msgtype = MSG_DETACHKILL;
else
msgtype = MSG_DETACH;
TAILQ_FOREACH(c_loop, &clients, entry) { TAILQ_FOREACH(c_loop, &clients, entry) {
if (c_loop->session != s || c == c_loop) if (c_loop->session != s || c == c_loop)
continue; continue;
server_client_detach(c_loop, MSG_DETACH); server_client_detach(c_loop, msgtype);
} }
} }
if (!Eflag) if (!Eflag)
@@ -150,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);
@@ -169,6 +180,6 @@ cmd_attach_session_exec(struct cmd *self, struct cmdq_item *item)
struct args *args = self->args; struct args *args = self->args;
return (cmd_attach_session(item, args_get(args, 't'), return (cmd_attach_session(item, args_get(args, 't'),
args_has(args, 'd'), args_has(args, 'r'), args_get(args, 'c'), args_has(args, 'd'), args_has(args, 'x'), args_has(args, 'r'),
args_has(args, 'E'))); args_get(args, 'c'), args_has(args, 'E')));
} }

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,9 +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);
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

@@ -30,8 +30,8 @@ const struct cmd_entry cmd_copy_mode_entry = {
.name = "copy-mode", .name = "copy-mode",
.alias = NULL, .alias = NULL,
.args = { "Met:u", 0, 0 }, .args = { "eHMt:uq", 0, 0 },
.usage = "[-Mu] " CMD_TARGET_PANE_USAGE, .usage = "[-eHMuq] " CMD_TARGET_PANE_USAGE,
.target = { 't', CMD_FIND_PANE, 0 }, .target = { 't', CMD_FIND_PANE, 0 },
@@ -61,6 +61,11 @@ cmd_copy_mode_exec(struct cmd *self, struct cmdq_item *item)
struct session *s; struct session *s;
struct window_pane *wp = item->target.wp; struct window_pane *wp = item->target.wp;
if (args_has(args, 'q')) {
window_pane_reset_mode_all(wp);
return (CMD_RETURN_NORMAL);
}
if (args_has(args, 'M')) { if (args_has(args, 'M')) {
if ((wp = cmd_mouse_pane(&shared->mouse, &s, NULL)) == NULL) if ((wp = cmd_mouse_pane(&shared->mouse, &s, NULL)) == NULL)
return (CMD_RETURN_NORMAL); return (CMD_RETURN_NORMAL);

View File

@@ -109,8 +109,7 @@ cmd_display_message_exec(struct cmd *self, struct cmdq_item *item)
format_defaults(ft, target_c, s, wl, wp); format_defaults(ft, target_c, s, wl, wp);
if (args_has(args, 'a')) { if (args_has(args, 'a')) {
if (item != NULL) format_each(ft, cmd_display_message_each, item);
format_each(ft, cmd_display_message_each, item);
return (CMD_RETURN_NORMAL); return (CMD_RETURN_NORMAL);
} }

View File

@@ -188,7 +188,7 @@ cmd_display_panes_free(struct client *c)
struct cmd_display_panes_data *cdata = c->overlay_data; struct cmd_display_panes_data *cdata = c->overlay_data;
if (cdata->item != NULL) if (cdata->item != NULL)
cdata->item->flags &= ~CMDQ_WAITING; cmdq_continue(cdata->item);
free(cdata->command); free(cdata->command);
free(cdata); free(cdata);
} }
@@ -204,7 +204,7 @@ cmd_display_panes_key(struct client *c, struct key_event *event)
struct cmd_parse_result *pr; struct cmd_parse_result *pr;
if (event->key < '0' || event->key > '9') if (event->key < '0' || event->key > '9')
return (1); return (-1);
wp = window_pane_at_index(w, event->key - '0'); wp = window_pane_at_index(w, event->key - '0');
if (wp == NULL) if (wp == NULL)

View File

@@ -32,8 +32,8 @@ const struct cmd_entry cmd_find_window_entry = {
.name = "find-window", .name = "find-window",
.alias = "findw", .alias = "findw",
.args = { "CNt:TZ", 1, 1 }, .args = { "CNrt:TZ", 1, 1 },
.usage = "[-CNTZ] " CMD_TARGET_PANE_USAGE " match-string", .usage = "[-CNrTZ] " CMD_TARGET_PANE_USAGE " match-string",
.target = { 't', CMD_FIND_PANE, 0 }, .target = { 't', CMD_FIND_PANE, 0 },
@@ -57,30 +57,59 @@ cmd_find_window_exec(struct cmd *self, struct cmdq_item *item)
if (!C && !N && !T) if (!C && !N && !T)
C = N = T = 1; C = N = T = 1;
if (C && N && T) { if (!args_has(args, 'r')) {
xasprintf(&filter, if (C && N && T) {
"#{||:" xasprintf(&filter,
"#{C:%s},#{||:#{m:*%s*,#{window_name}}," "#{||:"
"#{m:*%s*,#{pane_title}}}}", "#{C:%s},#{||:#{m:*%s*,#{window_name}},"
s, s, s); "#{m:*%s*,#{pane_title}}}}",
} else if (C && N) { s, s, s);
xasprintf(&filter, } else if (C && N) {
"#{||:#{C:%s},#{m:*%s*,#{window_name}}}", xasprintf(&filter,
s, s); "#{||:#{C:%s},#{m:*%s*,#{window_name}}}",
} else if (C && T) { s, s);
xasprintf(&filter, } else if (C && T) {
"#{||:#{C:%s},#{m:*%s*,#{pane_title}}}", xasprintf(&filter,
s, s); "#{||:#{C:%s},#{m:*%s*,#{pane_title}}}",
} else if (N && T) { s, s);
xasprintf(&filter, } else if (N && T) {
"#{||:#{m:*%s*,#{window_name}},#{m:*%s*,#{pane_title}}}", xasprintf(&filter,
s, s); "#{||:#{m:*%s*,#{window_name}},"
} else if (C) "#{m:*%s*,#{pane_title}}}",
xasprintf(&filter, "#{C:%s}", s); s, s);
else if (N) } else if (C)
xasprintf(&filter, "#{m:*%s*,#{window_name}}", s); xasprintf(&filter, "#{C:%s}", s);
else else if (N)
xasprintf(&filter, "#{m:*%s*,#{pane_title}}", s); xasprintf(&filter, "#{m:*%s*,#{window_name}}", s);
else
xasprintf(&filter, "#{m:*%s*,#{pane_title}}", s);
} else {
if (C && N && T) {
xasprintf(&filter,
"#{||:"
"#{C/r:%s},#{||:#{m/r:%s,#{window_name}},"
"#{m/r:%s,#{pane_title}}}}",
s, s, s);
} else if (C && N) {
xasprintf(&filter,
"#{||:#{C/r:%s},#{m/r:%s,#{window_name}}}",
s, s);
} else if (C && T) {
xasprintf(&filter,
"#{||:#{C/r:%s},#{m/r:%s,#{pane_title}}}",
s, s);
} else if (N && T) {
xasprintf(&filter,
"#{||:#{m/r:%s,#{window_name}},"
"#{m/r:%s,#{pane_title}}}",
s, s);
} else if (C)
xasprintf(&filter, "#{C/r:%s}", s);
else if (N)
xasprintf(&filter, "#{m/r:%s,#{window_name}}", s);
else
xasprintf(&filter, "#{m/r:%s,#{pane_title}}", s);
}
new_args = args_parse("", 1, &argv); new_args = args_parse("", 1, &argv);
if (args_has(args, 'Z')) if (args_has(args, 'Z'))

View File

@@ -75,38 +75,12 @@ static const char *cmd_find_pane_table[][2] = {
{ NULL, NULL } { NULL, NULL }
}; };
/* Get session from TMUX if present. */
static struct session *
cmd_find_try_TMUX(struct client *c)
{
struct environ_entry *envent;
char tmp[256];
long long pid;
u_int session;
struct session *s;
envent = environ_find(c->environ, "TMUX");
if (envent == NULL)
return (NULL);
if (sscanf(envent->value, "%255[^,],%lld,%d", tmp, &pid, &session) != 3)
return (NULL);
if (pid != getpid())
return (NULL);
log_debug("%s: client %p TMUX %s (session $%u)", __func__, c,
envent->value, session);
s = session_find_by_id(session);
if (s != NULL)
log_debug("%s: session $%u still exists", __func__, s->id);
return (s);
}
/* Find pane containing client if any. */ /* Find pane containing client if any. */
static struct window_pane * static struct window_pane *
cmd_find_inside_pane(struct client *c) cmd_find_inside_pane(struct client *c)
{ {
struct window_pane *wp; struct window_pane *wp;
struct environ_entry *envent;
if (c == NULL) if (c == NULL)
return (NULL); return (NULL);
@@ -115,6 +89,11 @@ cmd_find_inside_pane(struct client *c)
if (wp->fd != -1 && strcmp(wp->tty, c->ttyname) == 0) if (wp->fd != -1 && strcmp(wp->tty, c->ttyname) == 0)
break; break;
} }
if (wp == NULL) {
envent = environ_find(c->environ, "TMUX_PANE");
if (envent != NULL)
wp = window_pane_find_by_id_str(envent->value);
}
if (wp != NULL) if (wp != NULL)
log_debug("%s: got pane %%%u (%s)", __func__, wp->id, wp->tty); log_debug("%s: got pane %%%u (%s)", __func__, wp->id, wp->tty);
return (wp); return (wp);
@@ -879,8 +858,6 @@ cmd_find_from_mouse(struct cmd_find_state *fs, struct mouse_event *m, int flags)
int int
cmd_find_from_client(struct cmd_find_state *fs, struct client *c, int flags) cmd_find_from_client(struct cmd_find_state *fs, struct client *c, int flags)
{ {
struct session *s;
struct winlink *wl;
struct window_pane *wp; struct window_pane *wp;
/* If no client, treat as from nothing. */ /* If no client, treat as from nothing. */
@@ -902,30 +879,6 @@ cmd_find_from_client(struct cmd_find_state *fs, struct client *c, int flags)
if (wp == NULL) if (wp == NULL)
goto unknown_pane; goto unknown_pane;
/* If we have a session in TMUX, see if it has this pane. */
s = cmd_find_try_TMUX(c);
if (s != NULL) {
RB_FOREACH(wl, winlinks, &s->windows) {
if (window_has_pane(wl->window, wp))
break;
}
if (wl != NULL) {
log_debug("%s: session $%u has pane %%%u", __func__,
s->id, wp->id);
fs->s = s;
fs->wl = s->curw; /* use current session */
fs->w = fs->wl->window;
fs->wp = fs->w->active; /* use active pane */
cmd_find_log_state(__func__, fs);
return (0);
} else {
log_debug("%s: session $%u does not have pane %%%u",
__func__, s->id, wp->id);
}
}
/* /*
* Don't have a session, or it doesn't have this pane. Try all * Don't have a session, or it doesn't have this pane. Try all
* sessions. * sessions.
@@ -947,17 +900,7 @@ cmd_find_from_client(struct cmd_find_state *fs, struct client *c, int flags)
return (0); return (0);
unknown_pane: unknown_pane:
/* /* We can't find the pane so need to guess. */
* We're not running in a known pane, but maybe this client has TMUX
* in the environment. That'd give us a session.
*/
s = cmd_find_try_TMUX(c);
if (s != NULL) {
cmd_find_from_session(fs, s, flags);
return (0);
}
/* Otherwise we need to guess. */
return (cmd_find_from_nothing(fs, flags)); return (cmd_find_from_nothing(fs, flags));
} }
@@ -1005,6 +948,8 @@ cmd_find_target(struct cmd_find_state *fs, struct cmdq_item *item,
strlcat(tmp, "CANFAIL,", sizeof tmp); strlcat(tmp, "CANFAIL,", sizeof tmp);
if (*tmp != '\0') if (*tmp != '\0')
tmp[strlen(tmp) - 1] = '\0'; tmp[strlen(tmp) - 1] = '\0';
else
strlcat(tmp, "NONE", sizeof tmp);
log_debug("%s: target %s, type %s, item %p, flags %s", __func__, log_debug("%s: target %s, type %s, item %p, flags %s", __func__,
target == NULL ? "none" : target, s, item, tmp); target == NULL ? "none" : target, s, item, tmp);

View File

@@ -67,10 +67,11 @@ cmd_if_shell_exec(struct cmd *self, struct cmdq_item *item)
struct cmd_if_shell_data *cdata; struct cmd_if_shell_data *cdata;
char *shellcmd, *cmd; char *shellcmd, *cmd;
struct cmdq_item *new_item; struct cmdq_item *new_item;
struct cmd_find_state *fs = &item->target;
struct client *c = cmd_find_client(item, NULL, 1); struct client *c = cmd_find_client(item, NULL, 1);
struct session *s = item->target.s; struct session *s = fs->s;
struct winlink *wl = item->target.wl; struct winlink *wl = fs->wl;
struct window_pane *wp = item->target.wp; struct window_pane *wp = fs->wp;
struct cmd_parse_input pi; struct cmd_parse_input pi;
struct cmd_parse_result *pr; struct cmd_parse_result *pr;
@@ -92,7 +93,7 @@ cmd_if_shell_exec(struct cmd *self, struct cmdq_item *item)
pi.line = self->line; pi.line = self->line;
pi.item = item; pi.item = item;
pi.c = c; pi.c = c;
cmd_find_copy_state(&pi.fs, &item->target); cmd_find_copy_state(&pi.fs, fs);
pr = cmd_parse_from_string(cmd, &pi); pr = cmd_parse_from_string(cmd, &pi);
switch (pr->status) { switch (pr->status) {
@@ -103,7 +104,7 @@ cmd_if_shell_exec(struct cmd *self, struct cmdq_item *item)
free(pr->error); free(pr->error);
return (CMD_RETURN_ERROR); return (CMD_RETURN_ERROR);
case CMD_PARSE_SUCCESS: case CMD_PARSE_SUCCESS:
new_item = cmdq_get_command(pr->cmdlist, NULL, m, 0); new_item = cmdq_get_command(pr->cmdlist, fs, m, 0);
cmdq_insert_after(item, new_item); cmdq_insert_after(item, new_item);
cmd_list_free(pr->cmdlist); cmd_list_free(pr->cmdlist);
break; break;
@@ -120,7 +121,10 @@ cmd_if_shell_exec(struct cmd *self, struct cmdq_item *item)
cdata->cmd_else = NULL; cdata->cmd_else = NULL;
memcpy(&cdata->mouse, m, sizeof cdata->mouse); memcpy(&cdata->mouse, m, sizeof cdata->mouse);
cdata->client = item->client; if (!args_has(args, 'b'))
cdata->client = item->client;
else
cdata->client = c;
if (cdata->client != NULL) if (cdata->client != NULL)
cdata->client->references++; cdata->client->references++;
@@ -137,7 +141,7 @@ cmd_if_shell_exec(struct cmd *self, struct cmdq_item *item)
cdata->input.c = c; cdata->input.c = c;
if (cdata->input.c != NULL) if (cdata->input.c != NULL)
cdata->input.c->references++; cdata->input.c->references++;
cmd_find_copy_state(&cdata->input.fs, &item->target); cmd_find_copy_state(&cdata->input.fs, fs);
if (job_run(shellcmd, s, server_client_get_cwd(item->client, s), NULL, if (job_run(shellcmd, s, server_client_get_cwd(item->client, s), NULL,
cmd_if_shell_callback, cmd_if_shell_free, cdata, 0) == NULL) { cmd_if_shell_callback, cmd_if_shell_free, cdata, 0) == NULL) {
@@ -195,7 +199,7 @@ cmd_if_shell_callback(struct job *job)
out: out:
if (cdata->item != NULL) if (cdata->item != NULL)
cdata->item->flags &= ~CMDQ_WAITING; cmdq_continue(cdata->item);
} }
static void static void

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");
@@ -139,6 +161,8 @@ cmd_join_pane_exec(struct cmd *self, struct cmdq_item *item)
TAILQ_REMOVE(&src_w->panes, src_wp, entry); TAILQ_REMOVE(&src_w->panes, src_wp, entry);
src_wp->window = dst_w; src_wp->window = dst_w;
options_set_parent(src_wp->options, dst_w->options);
src_wp->flags |= PANE_STYLECHANGED;
TAILQ_INSERT_AFTER(&dst_w->panes, dst_wp, src_wp, entry); TAILQ_INSERT_AFTER(&dst_w->panes, dst_wp, src_wp, entry);
layout_assign_pane(lc, src_wp); layout_assign_pane(lc, src_wp);

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 = { "1aNP:T:", 0, 1 },
.usage = "[-T key-table]", .usage = "[-1aN] [-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
@@ -47,13 +47,100 @@ const struct cmd_entry cmd_list_commands_entry = {
.name = "list-commands", .name = "list-commands",
.alias = "lscm", .alias = "lscm",
.args = { "F:", 0, 0 }, .args = { "F:", 0, 1 },
.usage = "[-F format]", .usage = "[-F format] [command]",
.flags = CMD_STARTSERVER|CMD_AFTERHOOK, .flags = CMD_STARTSERVER|CMD_AFTERHOOK,
.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, *note;
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 && !args_has(args, 'a'))) {
bd = key_bindings_next(table, bd);
continue;
}
found = 1;
key = key_string_lookup_key(bd->key);
if (bd->note == NULL)
note = cmd_list_print(bd->cmdlist, 1);
else
note = xstrdup(bd->note);
tmp = utf8_padcstr(key, keywidth + 1);
if (args_has(args, '1') && c != NULL)
status_message_set(c, "%s%s%s", prefix, tmp, note);
else
cmdq_print(item, "%s%s%s", prefix, tmp, note);
free(tmp);
free(note);
if (args_has(args, '1'))
break;
bd = key_bindings_next(table, bd);
}
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,18 +148,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[BUFSIZ]; 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;
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 ();
@@ -83,6 +215,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)
@@ -101,6 +237,9 @@ cmd_list_keys_exec(struct cmd *self, struct cmdq_item *item)
table = key_bindings_next_table(table); table = key_bindings_next_table(table);
} }
tmpsize = 256;
tmp = xmalloc(tmpsize);
table = key_bindings_first_table (); table = key_bindings_first_table ();
while (table != NULL) { while (table != NULL) {
if (tablename != NULL && strcmp(table->name, tablename) != 0) { if (tablename != NULL && strcmp(table->name, tablename) != 0) {
@@ -109,6 +248,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)
@@ -117,20 +261,35 @@ cmd_list_keys_exec(struct cmd *self, struct cmdq_item *item)
r = "-r "; r = "-r ";
else else
r = " "; r = " ";
xsnprintf(tmp, sizeof tmp, "%s-T ", r); tmpused = xsnprintf(tmp, tmpsize, "%s-T ", r);
cp = utf8_padcstr(table->name, tablewidth); cp = utf8_padcstr(table->name, tablewidth);
strlcat(tmp, cp, sizeof tmp); cplen = strlen(cp) + 1;
strlcat(tmp, " ", sizeof tmp); while (tmpused + cplen + 1 >= tmpsize) {
tmpsize *= 2;
tmp = xrealloc(tmp, tmpsize);
}
tmpused = strlcat(tmp, cp, tmpsize);
tmpused = strlcat(tmp, " ", tmpsize);
free(cp); free(cp);
cp = utf8_padcstr(key, keywidth); cp = utf8_padcstr(key, keywidth);
strlcat(tmp, cp, sizeof tmp); cplen = strlen(cp) + 1;
strlcat(tmp, " ", sizeof tmp); while (tmpused + cplen + 1 >= tmpsize) {
tmpsize *= 2;
tmp = xrealloc(tmp, tmpsize);
}
tmpused = strlcat(tmp, cp, tmpsize);
tmpused = strlcat(tmp, " ", tmpsize);
free(cp); free(cp);
cp = cmd_list_print(bd->cmdlist, 1); cp = cmd_list_print(bd->cmdlist, 1);
strlcat(tmp, cp, sizeof tmp); cplen = strlen(cp);
while (tmpused + cplen + 1 >= tmpsize) {
tmpsize *= 2;
tmp = xrealloc(tmp, tmpsize);
}
strlcat(tmp, cp, tmpsize);
free(cp); free(cp);
cmdq_print(item, "bind-key %s", tmp); cmdq_print(item, "bind-key %s", tmp);
@@ -141,19 +300,29 @@ cmd_list_keys_exec(struct cmd *self, struct cmdq_item *item)
table = key_bindings_next_table(table); table = key_bindings_next_table(table);
} }
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;
const char *template, *s; const char *template, *s, *command = NULL;
char *line; char *line;
if (args->argc != 0)
command = args->argv[0];
if ((template = args_get(args, 'F')) == NULL) { if ((template = args_get(args, 'F')) == NULL) {
template = "#{command_list_name}" template = "#{command_list_name}"
"#{?command_list_alias, (#{command_list_alias}),} " "#{?command_list_alias, (#{command_list_alias}),} "
@@ -165,6 +334,11 @@ cmd_list_keys_commands(struct cmd *self, struct cmdq_item *item)
for (entryp = cmd_table; *entryp != NULL; entryp++) { for (entryp = cmd_table; *entryp != NULL; entryp++) {
entry = *entryp; entry = *entryp;
if (command != NULL &&
(strcmp(entry->name, command) != 0 &&
(entry->alias == NULL ||
strcmp(entry->alias, command) != 0)))
continue;
format_add(ft, "command_list_name", "%s", entry->name); format_add(ft, "command_list_name", "%s", entry->name);
if (entry->alias != NULL) if (entry->alias != NULL)

View File

@@ -1,103 +0,0 @@
/* $OpenBSD$ */
/*
* Copyright (c) 2009 Nicholas Marriott <nicholas.marriott@gmail.com>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER
* IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
* OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include <sys/types.h>
#include <stdlib.h>
#include <string.h>
#include "tmux.h"
static u_int cmd_list_next_group = 1;
struct cmd_list *
cmd_list_new(void)
{
struct cmd_list *cmdlist;
cmdlist = xcalloc(1, sizeof *cmdlist);
cmdlist->references = 1;
cmdlist->group = cmd_list_next_group++;
TAILQ_INIT(&cmdlist->list);
return (cmdlist);
}
void
cmd_list_append(struct cmd_list *cmdlist, struct cmd *cmd)
{
cmd->group = cmdlist->group;
TAILQ_INSERT_TAIL(&cmdlist->list, cmd, qentry);
}
void
cmd_list_move(struct cmd_list *cmdlist, struct cmd_list *from)
{
struct cmd *cmd, *cmd1;
TAILQ_FOREACH_SAFE(cmd, &from->list, qentry, cmd1) {
TAILQ_REMOVE(&from->list, cmd, qentry);
TAILQ_INSERT_TAIL(&cmdlist->list, cmd, qentry);
}
cmdlist->group = cmd_list_next_group++;
}
void
cmd_list_free(struct cmd_list *cmdlist)
{
struct cmd *cmd, *cmd1;
if (--cmdlist->references != 0)
return;
TAILQ_FOREACH_SAFE(cmd, &cmdlist->list, qentry, cmd1) {
TAILQ_REMOVE(&cmdlist->list, cmd, qentry);
cmd_free(cmd);
}
free(cmdlist);
}
char *
cmd_list_print(struct cmd_list *cmdlist, int escaped)
{
struct cmd *cmd;
char *buf, *this;
size_t len;
len = 1;
buf = xcalloc(1, len);
TAILQ_FOREACH(cmd, &cmdlist->list, qentry) {
this = cmd_print(cmd);
len += strlen(this) + 4;
buf = xrealloc(buf, len);
strlcat(buf, this, len);
if (TAILQ_NEXT(cmd, qentry) != NULL) {
if (escaped)
strlcat(buf, " \\; ", len);
else
strlcat(buf, " ; ", len);
}
free(this);
}
return (buf);
}

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:
cdata->item->flags &= ~CMDQ_WAITING;
free(cdata->bufname);
free(cdata);
} }

View File

@@ -39,8 +39,8 @@ const struct cmd_entry cmd_new_session_entry = {
.name = "new-session", .name = "new-session",
.alias = "new", .alias = "new",
.args = { "Ac:dDEF:n:Ps:t:x:y:", 0, -1 }, .args = { "Ac:dDEF:n:Ps:t:x:Xy:", 0, -1 },
.usage = "[-AdDEP] [-c start-directory] [-F format] [-n window-name] " .usage = "[-AdDEPX] [-c start-directory] [-F format] [-n window-name] "
"[-s session-name] " CMD_TARGET_SESSION_USAGE " [-x width] " "[-s session-name] " CMD_TARGET_SESSION_USAGE " [-x width] "
"[-y height] [command]", "[-y height] [command]",
@@ -94,25 +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);
0, NULL, args_has(args, 'E')); else
free(newname); as = item->target.s;
return (retval); if (as != NULL) {
} retval = cmd_attach_session(item, as->name,
cmdq_error(item, "duplicate session: %s", newname); args_has(args, 'D'), args_has(args, 'X'), 0, NULL,
goto fail; args_has(args, 'E'));
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');
@@ -258,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;
@@ -327,7 +334,7 @@ cmd_new_session_exec(struct cmd *self, struct cmdq_item *item)
if (args_has(args, 'P')) { if (args_has(args, 'P')) {
if ((template = args_get(args, 'F')) == NULL) if ((template = args_get(args, 'F')) == NULL)
template = NEW_SESSION_TEMPLATE; template = NEW_SESSION_TEMPLATE;
cp = format_single(item, template, c, s, NULL, NULL); cp = format_single(item, template, c, s, s->curw, NULL);
cmdq_print(item, "%s", cp); cmdq_print(item, "%s", cp);
free(cp); free(cp);
} }

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;
@@ -107,7 +108,8 @@ cmd_new_window_exec(struct cmd *self, struct cmdq_item *item)
if (args_has(args, 'P')) { if (args_has(args, 'P')) {
if ((template = args_get(args, 'F')) == NULL) if ((template = args_get(args, 'F')) == NULL)
template = NEW_WINDOW_TEMPLATE; template = NEW_WINDOW_TEMPLATE;
cp = format_single(item, template, c, s, new_wl, NULL); cp = format_single(item, template, c, s, new_wl,
new_wl->window->active);
cmdq_print(item, "%s", cp); cmdq_print(item, "%s", cp);
free(cp); free(cp);
} }

View File

@@ -77,6 +77,8 @@ static char *cmd_parse_get_error(const char *, u_int, const char *);
static void cmd_parse_free_command(struct cmd_parse_command *); static void cmd_parse_free_command(struct cmd_parse_command *);
static struct cmd_parse_commands *cmd_parse_new_commands(void); static struct cmd_parse_commands *cmd_parse_new_commands(void);
static void cmd_parse_free_commands(struct cmd_parse_commands *); static void cmd_parse_free_commands(struct cmd_parse_commands *);
static void cmd_parse_print_commands(struct cmd_parse_input *, u_int,
struct cmd_list *);
%} %}
@@ -131,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;
@@ -142,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;
@@ -174,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;
@@ -337,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);
@@ -356,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 {
@@ -370,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;
@@ -379,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;
@@ -507,6 +521,22 @@ cmd_parse_get_error(const char *file, u_int line, const char *error)
return (s); return (s);
} }
static void
cmd_parse_print_commands(struct cmd_parse_input *pi, u_int line,
struct cmd_list *cmdlist)
{
char *s;
if (pi->item != NULL && (pi->flags & CMD_PARSE_VERBOSE)) {
s = cmd_list_print(cmdlist, 0);
if (pi->file != NULL)
cmdq_print(pi->item, "%s:%u: %s", pi->file, line, s);
else
cmdq_print(pi->item, "%u: %s", line, s);
free(s);
}
}
static void static void
cmd_parse_free_command(struct cmd_parse_command *cmd) cmd_parse_free_command(struct cmd_parse_command *cmd)
{ {
@@ -663,6 +693,7 @@ cmd_parse_build_commands(struct cmd_parse_commands *cmds,
if (cmdlist == NULL || cmd->line != line) { if (cmdlist == NULL || cmd->line != line) {
if (cmdlist != NULL) { if (cmdlist != NULL) {
cmd_parse_print_commands(pi, line, cmdlist);
cmd_list_move(result, cmdlist); cmd_list_move(result, cmdlist);
cmd_list_free(cmdlist); cmd_list_free(cmdlist);
} }
@@ -677,11 +708,13 @@ 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);
} }
if (cmdlist != NULL) { if (cmdlist != NULL) {
cmd_parse_print_commands(pi, line, cmdlist);
cmd_list_move(result, cmdlist); cmd_list_move(result, cmdlist);
cmd_list_free(cmdlist); cmd_list_free(cmdlist);
} }
@@ -724,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;
@@ -736,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;
@@ -1124,17 +1163,54 @@ error:
static int static int
yylex_token_escape(char **buf, size_t *len) yylex_token_escape(char **buf, size_t *len)
{ {
int ch, type; int ch, type, o2, o3;
u_int size, i, tmp; u_int size, i, tmp;
char s[9]; char s[9];
struct utf8_data ud; struct utf8_data ud;
switch (ch = yylex_getc()) { ch = yylex_getc();
if (ch >= '4' && ch <= '7') {
yyerror("invalid octal escape");
return (0);
}
if (ch >= '0' && ch <= '3') {
o2 = yylex_getc();
if (o2 >= '0' && o2 <= '7') {
o3 = yylex_getc();
if (o3 >= '0' && o3 <= '7') {
ch = 64 * (ch - '0') +
8 * (o2 - '0') +
(o3 - '0');
yylex_append1(buf, len, ch);
return (1);
}
}
yyerror("invalid octal escape");
return (0);
}
switch (ch) {
case EOF: case EOF:
return (0); return (0);
case 'a':
ch = '\a';
break;
case 'b':
ch = '\b';
break;
case 'e': case 'e':
ch = '\033'; ch = '\033';
break; break;
case 'f':
ch = '\f';
break;
case 's':
ch = ' ';
break;
case 'v':
ch = '\v';
break;
case 'r': case 'r':
ch = '\r'; ch = '\r';
break; break;
@@ -1188,7 +1264,7 @@ yylex_token_variable(char **buf, size_t *len)
{ {
struct environ_entry *envent; struct environ_entry *envent;
int ch, brackets = 0; int ch, brackets = 0;
char name[BUFSIZ]; char name[1024];
size_t namelen = 0; size_t namelen = 0;
const char *value; const char *value;
@@ -1240,7 +1316,7 @@ yylex_token_tilde(char **buf, size_t *len)
{ {
struct environ_entry *envent; struct environ_entry *envent;
int ch; int ch;
char name[BUFSIZ]; char name[1024];
size_t namelen = 0; size_t namelen = 0;
struct passwd *pw; struct passwd *pw;
const char *home = NULL; const char *home = NULL;
@@ -1281,8 +1357,8 @@ static int
yylex_token_brace(char **buf, size_t *len) yylex_token_brace(char **buf, size_t *len)
{ {
struct cmd_parse_state *ps = &parse_state; struct cmd_parse_state *ps = &parse_state;
int ch, nesting = 1, escape = 0, quote = '\0'; int ch, lines = 0, nesting = 1, escape = 0;
int lines = 0; int quote = '\0', token = 0;
/* /*
* Extract a string up to the matching unquoted '}', including newlines * Extract a string up to the matching unquoted '}', including newlines
@@ -1292,6 +1368,10 @@ yylex_token_brace(char **buf, size_t *len)
* depth, we scan the input as if it was a tmux config file, and ignore * depth, we scan the input as if it was a tmux config file, and ignore
* braces which would be considered quoted, escaped, or in a comment. * braces which would be considered quoted, escaped, or in a comment.
* *
* We update the token state after every character because '#' begins a
* comment only when it begins a token. For simplicity, we treat an
* unquoted directive format as comment.
*
* The result is verbatim copy of the input excluding the final brace. * The result is verbatim copy of the input excluding the final brace.
*/ */
@@ -1311,6 +1391,8 @@ yylex_token_brace(char **buf, size_t *len)
ch == '\n' || ch == '\n' ||
ch == '\\')) { ch == '\\')) {
escape = 0; escape = 0;
if (ch != '\n')
token = 1;
continue; continue;
} }
@@ -1326,7 +1408,7 @@ yylex_token_brace(char **buf, size_t *len)
/* A newline always resets to unquoted. */ /* A newline always resets to unquoted. */
if (ch == '\n') { if (ch == '\n') {
quote = 0; quote = token = 0;
continue; continue;
} }
@@ -1337,33 +1419,47 @@ yylex_token_brace(char **buf, size_t *len)
*/ */
if (ch == quote && quote != '#') if (ch == quote && quote != '#')
quote = 0; quote = 0;
} else { token = 1; /* token continues regardless */
} else {
/* Not inside quotes or comment. */ /* Not inside quotes or comment. */
switch (ch) { switch (ch) {
case '"': case '"':
case '\'': case '\'':
case '#': case '#':
/* Beginning of quote or comment. */ /* Beginning of quote or maybe comment. */
quote = ch; if (ch != '#' || !token)
quote = ch;
token = 1;
break;
case ' ':
case '\t':
case ';':
/* Delimiter - token resets. */
token = 0;
break; break;
case '{': case '{':
nesting++; nesting++;
token = 0; /* new commands set - token resets */
break; break;
case '}': case '}':
nesting--; nesting--;
token = 1; /* same as after quotes */
if (nesting == 0) { if (nesting == 0) {
(*len)--; /* remove closing } */ (*len)--; /* remove closing } */
ps->input->line += lines; ps->input->line += lines;
return (1); return (1);
} }
break; break;
default:
token = 1;
break;
} }
} }
} }
/* /*
* Update line count after error as reporting the opening line * Update line count after error as reporting the opening line is more
* is more useful than EOF. * useful than EOF.
*/ */
yyerror("unterminated brace string"); yyerror("unterminated brace string");
ps->input->line += lines; ps->input->line += lines;

View File

@@ -53,7 +53,7 @@ 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);
@@ -73,10 +73,11 @@ 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;
@@ -100,9 +101,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 +145,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);
} }
@@ -156,6 +156,13 @@ cmdq_insert_hook(struct session *s, struct cmdq_item *item,
free(name); free(name);
} }
/* Continue processing command queue. */
void
cmdq_continue(struct cmdq_item *item)
{
item->flags &= ~CMDQ_WAITING;
}
/* Remove an item. */ /* Remove an item. */
static void static void
cmdq_remove(struct cmdq_item *item) cmdq_remove(struct cmdq_item *item)
@@ -469,13 +476,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. */
@@ -489,29 +494,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. */
@@ -522,11 +527,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);
@@ -538,11 +542,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

@@ -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"
@@ -33,8 +34,9 @@ const struct cmd_entry cmd_refresh_client_entry = {
.name = "refresh-client", .name = "refresh-client",
.alias = "refresh", .alias = "refresh",
.args = { "cC:DlLRSt:U", 0, 1 }, .args = { "cC:DF:lLRSt:U", 0, 1 },
.usage = "[-cDlLRSU] [-C size] " CMD_TARGET_CLIENT_USAGE " [adjustment]", .usage = "[-cDlLRSU] [-C XxY] [-F flags] " CMD_TARGET_CLIENT_USAGE
" [adjustment]",
.flags = CMD_AFTERHOOK, .flags = CMD_AFTERHOOK,
.exec = cmd_refresh_client_exec .exec = cmd_refresh_client_exec
@@ -48,6 +50,7 @@ cmd_refresh_client_exec(struct cmd *self, struct cmdq_item *item)
struct tty *tty; struct tty *tty;
struct window *w; struct window *w;
const char *size, *errstr; const char *size, *errstr;
char *copy, *next, *s;
u_int x, y, adjust; u_int x, y, adjust;
if ((c = cmd_find_client(item, args_get(args, 't'), 0)) == NULL) if ((c = cmd_find_client(item, args_get(args, 't'), 0)) == NULL)
@@ -107,28 +110,43 @@ cmd_refresh_client_exec(struct cmd *self, struct cmdq_item *item)
if (args_has(args, 'l')) { if (args_has(args, 'l')) {
if (c->session != NULL) if (c->session != NULL)
tty_putcode_ptr2(&c->tty, TTYC_MS, "", "?"); tty_putcode_ptr2(&c->tty, TTYC_MS, "", "?");
} else if (args_has(args, 'C')) { return (CMD_RETURN_NORMAL);
if ((size = args_get(args, 'C')) == NULL) { }
cmdq_error(item, "missing size");
return (CMD_RETURN_ERROR); if (args_has(args, 'C') || args_has(args, 'F')) {
if (args_has(args, 'C')) {
if (!(c->flags & CLIENT_CONTROL)) {
cmdq_error(item, "not a control client");
return (CMD_RETURN_ERROR);
}
size = args_get(args, 'C');
if (sscanf(size, "%u,%u", &x, &y) != 2 &&
sscanf(size, "%ux%u", &x, &y) != 2) {
cmdq_error(item, "bad size argument");
return (CMD_RETURN_ERROR);
}
if (x < WINDOW_MINIMUM || x > WINDOW_MAXIMUM ||
y < WINDOW_MINIMUM || y > WINDOW_MAXIMUM) {
cmdq_error(item, "size too small or too big");
return (CMD_RETURN_ERROR);
}
tty_set_size(&c->tty, x, y, 0, 0);
c->flags |= CLIENT_SIZECHANGED;
recalculate_sizes();
} }
if (sscanf(size, "%u,%u", &x, &y) != 2 && if (args_has(args, 'F')) {
sscanf(size, "%ux%u", &x, &y)) { if (!(c->flags & CLIENT_CONTROL)) {
cmdq_error(item, "bad size argument"); cmdq_error(item, "not a control client");
return (CMD_RETURN_ERROR); return (CMD_RETURN_ERROR);
}
s = copy = xstrdup(args_get(args, 'F'));
while ((next = strsep(&s, ",")) != NULL) {
/* Unknown flags are ignored. */
if (strcmp(next, "no-output") == 0)
c->flags |= CLIENT_CONTROL_NOOUTPUT;
}
free(copy);
} }
if (x < WINDOW_MINIMUM || x > WINDOW_MAXIMUM ||
y < WINDOW_MINIMUM || y > WINDOW_MAXIMUM) {
cmdq_error(item, "size too small or too big");
return (CMD_RETURN_ERROR);
}
if (!(c->flags & CLIENT_CONTROL)) {
cmdq_error(item, "not a control client");
return (CMD_RETURN_ERROR);
}
tty_set_size(&c->tty, x, y);
c->flags |= CLIENT_SIZECHANGED;
recalculate_sizes();
return (CMD_RETURN_NORMAL); return (CMD_RETURN_NORMAL);
} }

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);
} }
@@ -144,13 +183,13 @@ cmd_resize_pane_mouse_update(struct client *c, struct mouse_event *m)
w = wl->window; w = wl->window;
y = m->y + m->oy; x = m->x + m->ox; y = m->y + m->oy; x = m->x + m->ox;
if (m->statusat == 0 && y > 0) if (m->statusat == 0 && y >= m->statuslines)
y--; y -= m->statuslines;
else if (m->statusat > 0 && y >= (u_int)m->statusat) else if (m->statusat > 0 && y >= (u_int)m->statusat)
y = m->statusat - 1; y = m->statusat - 1;
ly = m->ly + m->oy; lx = m->lx + m->ox; ly = m->ly + m->oy; lx = m->lx + m->ox;
if (m->statusat == 0 && ly > 0) if (m->statusat == 0 && ly >= m->statuslines)
ly--; ly -= m->statuslines;
else if (m->statusat > 0 && ly >= (u_int)m->statusat) else if (m->statusat > 0 && ly >= (u_int)m->statusat)
ly = m->statusat - 1; ly = m->statusat - 1;

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

@@ -155,7 +155,7 @@ cmd_run_shell_callback(struct job *job)
free(msg); free(msg);
if (cdata->item != NULL) if (cdata->item != NULL)
cdata->item->flags &= ~CMDQ_WAITING; cmdq_continue(cdata->item);
} }
static void static void

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 }, .args = { "DdegLlMmP:RT:t:UZ", 0, 0 }, /* -P and -g deprecated */
.usage = "[-DdegLlMmRU] [-P style] [-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 },
@@ -90,9 +90,10 @@ cmd_select_pane_exec(struct cmd *self, struct cmdq_item *item)
struct window *w = wl->window; struct window *w = wl->window;
struct session *s = item->target.s; struct session *s = item->target.s;
struct window_pane *wp = item->target.wp, *lastwp, *markedwp; struct window_pane *wp = item->target.wp, *lastwp, *markedwp;
struct style *sy = &wp->style;
char *pane_title; char *pane_title;
const char *style; const char *style;
struct style *sy;
struct options_entry *o;
if (self->entry == &cmd_last_pane_entry || args_has(args, 'l')) { if (self->entry == &cmd_last_pane_entry || args_has(args, 'l')) {
lastwp = w->last; lastwp = w->last;
@@ -110,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);
} }
@@ -144,30 +148,39 @@ cmd_select_pane_exec(struct cmd *self, struct cmdq_item *item)
if (args_has(self->args, 'P') || args_has(self->args, 'g')) { if (args_has(self->args, 'P') || args_has(self->args, 'g')) {
if ((style = args_get(args, 'P')) != NULL) { if ((style = args_get(args, 'P')) != NULL) {
style_set(sy, &grid_default_cell); o = options_set_style(wp->options, "window-style", 0,
if (style_parse(sy, &grid_default_cell, style) == -1) { style);
if (o == NULL) {
cmdq_error(item, "bad style: %s", style); cmdq_error(item, "bad style: %s", style);
return (CMD_RETURN_ERROR); return (CMD_RETURN_ERROR);
} }
wp->flags |= PANE_REDRAW; options_set_style(wp->options, "window-active-style", 0,
style);
wp->flags |= (PANE_REDRAW|PANE_STYLECHANGED);
} }
if (args_has(self->args, 'g')) if (args_has(self->args, 'g')) {
sy = options_get_style(wp->options, "window-style");
cmdq_print(item, "%s", style_tostring(sy)); cmdq_print(item, "%s", style_tostring(sy));
}
return (CMD_RETURN_NORMAL); return (CMD_RETURN_NORMAL);
} }
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);
@@ -184,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 = { "lXRMN:t:", 0, -1 }, .args = { "FHlMN:Rt:X", 0, -1 },
.usage = "[-lXRM] [-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 },
@@ -56,9 +57,12 @@ const struct cmd_entry cmd_send_prefix_entry = {
}; };
static struct cmdq_item * static struct cmdq_item *
cmd_send_keys_inject(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(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);
@@ -81,20 +86,60 @@ cmd_send_keys_inject(struct client *c, struct cmd_find_state *fs,
return (item); return (item);
} }
static struct cmdq_item *
cmd_send_keys_inject_string(struct client *c, struct cmd_find_state *fs,
struct cmdq_item *item, struct args *args, int i)
{
const char *s = args->argv[i];
struct cmdq_item *new_item;
struct utf8_data *ud, *uc;
wchar_t wc;
key_code key;
char *endptr;
long n;
int literal;
if (args_has(args, 'H')) {
n = strtol(s, &endptr, 16);
if (*s =='\0' || n < 0 || n > 0xff || *endptr != '\0')
return (item);
return (cmd_send_keys_inject_key(c, fs, item, KEYC_LITERAL|n));
}
literal = args_has(args, 'l');
if (!literal) {
key = key_string_lookup_string(s);
if (key != KEYC_NONE && key != KEYC_UNKNOWN) {
new_item = cmd_send_keys_inject_key(c, fs, item, key);
if (new_item != NULL)
return (new_item);
}
literal = 1;
}
if (literal) {
ud = utf8_fromcstr(s);
for (uc = ud; uc->size != 0; uc++) {
if (utf8_combine(uc, &wc) != UTF8_DONE)
continue;
item = cmd_send_keys_inject_key(c, fs, item, wc);
}
free(ud);
}
return (item);
}
static enum cmd_retval static enum cmd_retval
cmd_send_keys_exec(struct cmd *self, struct cmdq_item *item) cmd_send_keys_exec(struct cmd *self, struct cmdq_item *item)
{ {
struct args *args = self->args; struct args *args = self->args;
struct client *c = cmd_find_client(item, NULL, 1); struct client *c = cmd_find_client(item, NULL, 1);
struct cmd_find_state *fs = &item->target;
struct window_pane *wp = item->target.wp; struct window_pane *wp = item->target.wp;
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 mouse_event *m = &item->shared->mouse; struct mouse_event *m = &item->shared->mouse;
struct cmd_find_state *fs = &item->target;
struct window_mode_entry *wme = TAILQ_FIRST(&wp->modes); struct window_mode_entry *wme = TAILQ_FIRST(&wp->modes);
struct utf8_data *ud, *uc; int i;
wchar_t wc;
int i, literal;
key_code key; key_code key;
u_int np = 1; u_int np = 1;
char *cause = NULL; char *cause = NULL;
@@ -141,7 +186,7 @@ cmd_send_keys_exec(struct cmd *self, struct cmdq_item *item)
key = options_get_number(s->options, "prefix2"); key = options_get_number(s->options, "prefix2");
else else
key = options_get_number(s->options, "prefix"); key = options_get_number(s->options, "prefix");
cmd_send_keys_inject(c, fs, item, key); cmd_send_keys_inject_key(c, fs, item, key);
return (CMD_RETURN_NORMAL); return (CMD_RETURN_NORMAL);
} }
@@ -151,28 +196,8 @@ cmd_send_keys_exec(struct cmd *self, struct cmdq_item *item)
} }
for (; np != 0; np--) { for (; np != 0; np--) {
for (i = 0; i < args->argc; i++) { for (i = 0; i < args->argc; i++)
literal = args_has(args, 'l'); item = cmd_send_keys_inject_string(c, fs, item, args, i);
if (!literal) {
key = key_string_lookup_string(args->argv[i]);
if (key != KEYC_NONE && key != KEYC_UNKNOWN) {
item = cmd_send_keys_inject(c, fs, item,
key);
} else
literal = 1;
}
if (literal) {
ud = utf8_fromcstr(args->argv[i]);
for (uc = ud; uc->size != 0; uc++) {
if (utf8_combine(uc, &wc) != UTF8_DONE)
continue;
item = cmd_send_keys_inject(c, fs, item,
wc);
}
free(ud);
}
}
} }
return (CMD_RETURN_NORMAL); return (CMD_RETURN_NORMAL);

View File

@@ -43,10 +43,10 @@ const struct cmd_entry cmd_set_option_entry = {
.name = "set-option", .name = "set-option",
.alias = "set", .alias = "set",
.args = { "aFgoqst:uw", 1, 2 }, .args = { "aFgopqst:uw", 1, 2 },
.usage = "[-aFgosquw] [-t target-window] option [value]", .usage = "[-aFgopqsuw] " CMD_TARGET_PANE_USAGE " option [value]",
.target = { 't', CMD_FIND_WINDOW, CMD_FIND_CANFAIL }, .target = { 't', CMD_FIND_PANE, CMD_FIND_CANFAIL },
.flags = CMD_AFTERHOOK, .flags = CMD_AFTERHOOK,
.exec = cmd_set_option_exec .exec = cmd_set_option_exec
@@ -88,20 +88,24 @@ cmd_set_option_exec(struct cmd *self, struct cmdq_item *item)
struct session *s = fs->s; struct session *s = fs->s;
struct winlink *wl = fs->wl; struct winlink *wl = fs->wl;
struct window *w; struct window *w;
enum options_table_scope scope; struct window_pane *wp;
struct options *oo; struct options *oo;
struct options_entry *parent, *o; struct options_entry *parent, *o;
char *name, *argument, *value = NULL, *cause; char *name, *argument, *value = NULL, *cause;
const char *target;
int window, idx, already, error, ambiguous; int window, idx, already, error, ambiguous;
int scope;
struct style *sy; struct style *sy;
window = (self->entry == &cmd_set_window_option_entry);
/* Expand argument. */ /* Expand argument. */
c = cmd_find_client(item, NULL, 1); c = cmd_find_client(item, NULL, 1);
argument = format_single(item, args->argv[0], c, s, wl, NULL); argument = format_single(item, args->argv[0], c, s, wl, NULL);
/* If set-hook -R, fire the hook straight away. */
if (self->entry == &cmd_set_hook_entry && args_has(args, 'R')) { if (self->entry == &cmd_set_hook_entry && args_has(args, 'R')) {
notify_hook(item, argument); notify_hook(item, argument);
free(argument);
return (CMD_RETURN_NORMAL); return (CMD_RETURN_NORMAL);
} }
@@ -123,25 +127,8 @@ cmd_set_option_exec(struct cmd *self, struct cmdq_item *item)
else else
value = xstrdup(args->argv[1]); value = xstrdup(args->argv[1]);
/* /* Get the scope and table for the option .*/
* Figure out the scope: for user options it comes from the arguments, scope = options_scope_from_name(args, window, name, fs, &oo, &cause);
* otherwise from the option name.
*/
if (*name == '@') {
window = (self->entry == &cmd_set_window_option_entry);
scope = options_scope_from_flags(args, window, fs, &oo, &cause);
} else {
if (options_get_only(global_options, name) != NULL)
scope = OPTIONS_TABLE_SERVER;
else if (options_get_only(global_s_options, name) != NULL)
scope = OPTIONS_TABLE_SESSION;
else if (options_get_only(global_w_options, name) != NULL)
scope = OPTIONS_TABLE_WINDOW;
else {
scope = OPTIONS_TABLE_NONE;
xasprintf(&cause, "unknown option: %s", argument);
}
}
if (scope == OPTIONS_TABLE_NONE) { if (scope == OPTIONS_TABLE_NONE) {
if (args_has(args, 'q')) if (args_has(args, 'q'))
goto out; goto out;
@@ -149,35 +136,6 @@ cmd_set_option_exec(struct cmd *self, struct cmdq_item *item)
free(cause); free(cause);
goto fail; goto fail;
} }
/* Which table should this option go into? */
if (scope == OPTIONS_TABLE_SERVER)
oo = global_options;
else if (scope == OPTIONS_TABLE_SESSION) {
if (args_has(self->args, 'g'))
oo = global_s_options;
else if (s == NULL) {
target = args_get(args, 't');
if (target != NULL)
cmdq_error(item, "no such session: %s", target);
else
cmdq_error(item, "no current session");
goto fail;
} else
oo = s->options;
} else if (scope == OPTIONS_TABLE_WINDOW) {
if (args_has(self->args, 'g'))
oo = global_w_options;
else if (wl == NULL) {
target = args_get(args, 't');
if (target != NULL)
cmdq_error(item, "no such window: %s", target);
else
cmdq_error(item, "no current window");
goto fail;
} else
oo = wl->window->options;
}
o = options_get_only(oo, name); o = options_get_only(oo, name);
parent = options_get(oo, name); parent = options_get(oo, name);
@@ -292,8 +250,8 @@ cmd_set_option_exec(struct cmd *self, struct cmdq_item *item)
alerts_reset_all(); alerts_reset_all();
if (strcmp(name, "window-style") == 0 || if (strcmp(name, "window-style") == 0 ||
strcmp(name, "window-active-style") == 0) { strcmp(name, "window-active-style") == 0) {
RB_FOREACH(w, windows, &windows) RB_FOREACH(wp, window_pane_tree, &all_window_panes)
w->flags |= WINDOW_STYLECHANGED; wp->flags |= PANE_STYLECHANGED;
} }
if (strcmp(name, "pane-border-status") == 0) { if (strcmp(name, "pane-border-status") == 0) {
RB_FOREACH(w, windows, &windows) RB_FOREACH(w, windows, &windows)

View File

@@ -30,18 +30,18 @@
static enum cmd_retval cmd_show_options_exec(struct cmd *, struct cmdq_item *); static enum cmd_retval cmd_show_options_exec(struct cmd *, struct cmdq_item *);
static void cmd_show_options_print(struct cmd *, struct cmdq_item *, static void cmd_show_options_print(struct cmd *, struct cmdq_item *,
struct options_entry *, int); struct options_entry *, int, int);
static enum cmd_retval cmd_show_options_all(struct cmd *, struct cmdq_item *, static enum cmd_retval cmd_show_options_all(struct cmd *, struct cmdq_item *,
struct options *); int, struct options *);
const struct cmd_entry cmd_show_options_entry = { const struct cmd_entry cmd_show_options_entry = {
.name = "show-options", .name = "show-options",
.alias = "show", .alias = "show",
.args = { "gHqst:vw", 0, 1 }, .args = { "AgHpqst:vw", 0, 1 },
.usage = "[-gHqsvw] [-t target-session|target-window] [option]", .usage = "[-AgHpqsvw] " CMD_TARGET_PANE_USAGE " [option]",
.target = { 't', CMD_FIND_WINDOW, CMD_FIND_CANFAIL }, .target = { 't', CMD_FIND_PANE, CMD_FIND_CANFAIL },
.flags = CMD_AFTERHOOK, .flags = CMD_AFTERHOOK,
.exec = cmd_show_options_exec .exec = cmd_show_options_exec
@@ -82,13 +82,12 @@ cmd_show_options_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 options *oo; struct options *oo;
enum options_table_scope scope;
char *argument, *name = NULL, *cause; char *argument, *name = NULL, *cause;
const char *target; int window, idx, ambiguous, parent, scope;
int window, idx, ambiguous;
struct options_entry *o; struct options_entry *o;
window = (self->entry == &cmd_show_window_options_entry); window = (self->entry == &cmd_show_window_options_entry);
if (args->argc == 0) { if (args->argc == 0) {
scope = options_scope_from_flags(args, window, fs, &oo, &cause); scope = options_scope_from_flags(args, window, fs, &oo, &cause);
if (scope == OPTIONS_TABLE_NONE) { if (scope == OPTIONS_TABLE_NONE) {
@@ -98,7 +97,7 @@ cmd_show_options_exec(struct cmd *self, struct cmdq_item *item)
free(cause); free(cause);
return (CMD_RETURN_ERROR); return (CMD_RETURN_ERROR);
} }
return (cmd_show_options_all(self, item, oo)); return (cmd_show_options_all(self, item, scope, oo));
} }
argument = format_single(item, args->argv[0], c, s, wl, NULL); argument = format_single(item, args->argv[0], c, s, wl, NULL);
@@ -112,49 +111,7 @@ cmd_show_options_exec(struct cmd *self, struct cmdq_item *item)
cmdq_error(item, "invalid option: %s", argument); cmdq_error(item, "invalid option: %s", argument);
goto fail; goto fail;
} }
if (*name == '@') scope = options_scope_from_name(args, window, name, fs, &oo, &cause);
scope = options_scope_from_flags(args, window, fs, &oo, &cause);
else {
if (options_get_only(global_options, name) != NULL)
scope = OPTIONS_TABLE_SERVER;
else if (options_get_only(global_s_options, name) != NULL)
scope = OPTIONS_TABLE_SESSION;
else if (options_get_only(global_w_options, name) != NULL)
scope = OPTIONS_TABLE_WINDOW;
else {
scope = OPTIONS_TABLE_NONE;
xasprintf(&cause, "unknown option: %s", argument);
}
if (scope == OPTIONS_TABLE_SERVER)
oo = global_options;
else if (scope == OPTIONS_TABLE_SESSION) {
if (args_has(self->args, 'g'))
oo = global_s_options;
else if (s == NULL) {
target = args_get(args, 't');
if (target != NULL) {
cmdq_error(item, "no such session: %s",
target);
} else
cmdq_error(item, "no current session");
goto fail;
} else
oo = s->options;
} else if (scope == OPTIONS_TABLE_WINDOW) {
if (args_has(self->args, 'g'))
oo = global_w_options;
else if (wl == NULL) {
target = args_get(args, 't');
if (target != NULL) {
cmdq_error(item, "no such window: %s",
target);
} else
cmdq_error(item, "no current window");
goto fail;
} else
oo = wl->window->options;
}
}
if (scope == OPTIONS_TABLE_NONE) { if (scope == OPTIONS_TABLE_NONE) {
if (args_has(args, 'q')) if (args_has(args, 'q'))
goto fail; goto fail;
@@ -163,8 +120,13 @@ cmd_show_options_exec(struct cmd *self, struct cmdq_item *item)
goto fail; goto fail;
} }
o = options_get_only(oo, name); o = options_get_only(oo, name);
if (args_has(args, 'A') && o == NULL) {
o = options_get(oo, name);
parent = 1;
} else
parent = 0;
if (o != NULL) if (o != NULL)
cmd_show_options_print(self, item, o, idx); cmd_show_options_print(self, item, o, idx, parent);
free(name); free(name);
free(argument); free(argument);
@@ -178,7 +140,7 @@ fail:
static void static void
cmd_show_options_print(struct cmd *self, struct cmdq_item *item, cmd_show_options_print(struct cmd *self, struct cmdq_item *item,
struct options_entry *o, int idx) struct options_entry *o, int idx, int parent)
{ {
struct options_array_item *a; struct options_array_item *a;
const char *name = options_name(o); const char *name = options_name(o);
@@ -197,7 +159,8 @@ cmd_show_options_print(struct cmd *self, struct cmdq_item *item,
} }
while (a != NULL) { while (a != NULL) {
idx = options_array_item_index(a); idx = options_array_item_index(a);
cmd_show_options_print(self, item, o, idx); cmd_show_options_print(self, item, o, idx,
parent);
a = options_array_next(a); a = options_array_next(a);
} }
return; return;
@@ -209,50 +172,81 @@ cmd_show_options_print(struct cmd *self, struct cmdq_item *item,
cmdq_print(item, "%s", value); cmdq_print(item, "%s", value);
else if (options_isstring(o)) { else if (options_isstring(o)) {
escaped = args_escape(value); escaped = args_escape(value);
cmdq_print(item, "%s %s", name, escaped); if (parent)
cmdq_print(item, "%s* %s", name, escaped);
else
cmdq_print(item, "%s %s", name, escaped);
free(escaped); free(escaped);
} else } else {
cmdq_print(item, "%s %s", name, value); if (parent)
cmdq_print(item, "%s* %s", name, value);
else
cmdq_print(item, "%s %s", name, value);
}
free(value); free(value);
free(tmp); free(tmp);
} }
static enum cmd_retval static enum cmd_retval
cmd_show_options_all(struct cmd *self, struct cmdq_item *item, cmd_show_options_all(struct cmd *self, struct cmdq_item *item, int scope,
struct options *oo) struct options *oo)
{ {
const struct options_table_entry *oe;
struct options_entry *o; struct options_entry *o;
struct options_array_item *a; struct options_array_item *a;
const char *name;
u_int idx; u_int idx;
const struct options_table_entry *oe; int parent;
o = options_first(oo); o = options_first(oo);
while (o != NULL) { while (o != NULL) {
oe = options_table_entry(o); if (options_table_entry(o) == NULL)
cmd_show_options_print(self, item, o, -1, 0);
o = options_next(o);
}
for (oe = options_table; oe->name != NULL; oe++) {
if (~oe->scope & scope)
continue;
if ((self->entry != &cmd_show_hooks_entry && if ((self->entry != &cmd_show_hooks_entry &&
!args_has(self->args, 'H') && !args_has(self->args, 'H') &&
oe != NULL && oe != NULL &&
(oe->flags & OPTIONS_TABLE_IS_HOOK)) || (oe->flags & OPTIONS_TABLE_IS_HOOK)) ||
(self->entry == &cmd_show_hooks_entry && (self->entry == &cmd_show_hooks_entry &&
(oe == NULL || (oe == NULL ||
(~oe->flags & OPTIONS_TABLE_IS_HOOK)))) { (~oe->flags & OPTIONS_TABLE_IS_HOOK))))
o = options_next(o);
continue; continue;
}
o = options_get_only(oo, oe->name);
if (o == NULL) {
if (!args_has(self->args, 'A'))
continue;
o = options_get(oo, oe->name);
if (o == NULL)
continue;
parent = 1;
} else
parent = 0;
if (!options_isarray(o)) if (!options_isarray(o))
cmd_show_options_print(self, item, o, -1); cmd_show_options_print(self, item, o, -1, parent);
else if ((a = options_array_first(o)) == NULL) { else if ((a = options_array_first(o)) == NULL) {
if (!args_has(self->args, 'v')) if (!args_has(self->args, 'v')) {
cmdq_print(item, "%s", options_name(o)); name = options_name(o);
if (parent)
cmdq_print(item, "%s*", name);
else
cmdq_print(item, "%s", name);
}
} else { } else {
while (a != NULL) { while (a != NULL) {
idx = options_array_item_index(a); idx = options_array_item_index(a);
cmd_show_options_print(self, item, o, idx); cmd_show_options_print(self, item, o, idx,
parent);
a = options_array_next(a); a = options_array_next(a);
} }
} }
o = options_next(o);
} }
return (CMD_RETURN_NORMAL); return (CMD_RETURN_NORMAL);
} }

View File

@@ -31,51 +31,141 @@
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",
.args = { "nq", 1, -1 }, .args = { "nqv", 1, -1 },
.usage = "[-nq] path ...", .usage = "[-nqv] path ...",
.flags = 0, .flags = 0,
.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'))
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;
} }
@@ -84,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);
@@ -137,7 +153,6 @@ cmd_split_window_exec(struct cmd *self, struct cmdq_item *item)
sc.flags |= SPAWN_DETACHED; sc.flags |= SPAWN_DETACHED;
if ((new_wp = spawn_pane(&sc, &cause)) == NULL) { if ((new_wp = spawn_pane(&sc, &cause)) == NULL) {
layout_close_pane(new_wp);
cmdq_error(item, "create pane failed: %s", cause); cmdq_error(item, "create pane failed: %s", cause);
free(cause); free(cause);
return (CMD_RETURN_ERROR); return (CMD_RETURN_ERROR);

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);
@@ -90,7 +95,11 @@ cmd_swap_pane_exec(struct cmd *self, struct cmdq_item *item)
src_wp->layout_cell = dst_lc; src_wp->layout_cell = dst_lc;
src_wp->window = dst_w; src_wp->window = dst_w;
options_set_parent(src_wp->options, dst_w->options);
src_wp->flags |= PANE_STYLECHANGED;
dst_wp->window = src_w; dst_wp->window = src_w;
options_set_parent(dst_wp->options, src_w->options);
dst_wp->flags |= PANE_STYLECHANGED;
sx = src_wp->sx; sy = src_wp->sy; sx = src_wp->sx; sy = src_wp->sy;
xoff = src_wp->xoff; yoff = src_wp->yoff; xoff = src_wp->xoff; yoff = src_wp->yoff;
@@ -99,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);
@@ -122,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

@@ -77,10 +77,10 @@ cmd_swap_window_exec(struct cmd *self, struct cmdq_item *item)
wl_src->window = w_dst; wl_src->window = w_dst;
TAILQ_INSERT_TAIL(&w_dst->winlinks, wl_src, wentry); TAILQ_INSERT_TAIL(&w_dst->winlinks, wl_src, wentry);
if (!args_has(self->args, 'd')) { if (args_has(self->args, 'd')) {
session_select(src, wl_src->idx); session_select(dst, wl_dst->idx);
if (src != dst) if (src != dst)
session_select(dst, wl_dst->idx); session_select(src, wl_src->idx);
} }
session_group_synchronize_from(src); session_group_synchronize_from(src);
server_redraw_session_group(src); server_redraw_session_group(src);

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);
} }
@@ -137,10 +142,11 @@ cmd_switch_client_exec(struct cmd *self, struct cmdq_item *item)
session_update_activity(s, NULL); session_update_activity(s, NULL);
gettimeofday(&s->last_attached_time, NULL); gettimeofday(&s->last_attached_time, NULL);
recalculate_sizes();
server_check_unattached(); server_check_unattached();
server_redraw_client(c); server_redraw_client(c);
s->curw->flags &= ~WINLINK_ALERTFLAGS; s->curw->flags &= ~WINLINK_ALERTFLAGS;
s->curw->window->latest = c;
recalculate_sizes();
alerts_check_session(s); alerts_check_session(s);
return (CMD_RETURN_NORMAL); return (CMD_RETURN_NORMAL);

View File

@@ -153,7 +153,7 @@ cmd_wait_for_signal(__unused struct cmdq_item *item, const char *name,
log_debug("signal wait channel %s, with waiters", wc->name); log_debug("signal wait channel %s, with waiters", wc->name);
TAILQ_FOREACH_SAFE(wi, &wc->waiters, entry, wi1) { TAILQ_FOREACH_SAFE(wi, &wc->waiters, entry, wi1) {
wi->item->flags &= ~CMDQ_WAITING; cmdq_continue(wi->item);
TAILQ_REMOVE(&wc->waiters, wi, entry); TAILQ_REMOVE(&wc->waiters, wi, entry);
free(wi); free(wi);
@@ -229,7 +229,7 @@ cmd_wait_for_unlock(struct cmdq_item *item, const char *name,
} }
if ((wi = TAILQ_FIRST(&wc->lockers)) != NULL) { if ((wi = TAILQ_FIRST(&wc->lockers)) != NULL) {
wi->item->flags &= ~CMDQ_WAITING; cmdq_continue(wi->item);
TAILQ_REMOVE(&wc->lockers, wi, entry); TAILQ_REMOVE(&wc->lockers, wi, entry);
free(wi); free(wi);
} else { } else {
@@ -248,13 +248,13 @@ cmd_wait_for_flush(void)
RB_FOREACH_SAFE(wc, wait_channels, &wait_channels, wc1) { RB_FOREACH_SAFE(wc, wait_channels, &wait_channels, wc1) {
TAILQ_FOREACH_SAFE(wi, &wc->waiters, entry, wi1) { TAILQ_FOREACH_SAFE(wi, &wc->waiters, entry, wi1) {
wi->item->flags &= ~CMDQ_WAITING; cmdq_continue(wi->item);
TAILQ_REMOVE(&wc->waiters, wi, entry); TAILQ_REMOVE(&wc->waiters, wi, entry);
free(wi); free(wi);
} }
wc->woken = 1; wc->woken = 1;
TAILQ_FOREACH_SAFE(wi, &wc->lockers, entry, wi1) { TAILQ_FOREACH_SAFE(wi, &wc->lockers, entry, wi1) {
wi->item->flags &= ~CMDQ_WAITING; cmdq_continue(wi->item);
TAILQ_REMOVE(&wc->lockers, wi, entry); TAILQ_REMOVE(&wc->lockers, wi, entry);
free(wi); free(wi);
} }

91
cmd.c
View File

@@ -204,6 +204,8 @@ const struct cmd_entry *cmd_table[] = {
NULL NULL
}; };
static u_int cmd_list_next_group = 1;
void printflike(3, 4) void printflike(3, 4)
cmd_log_argv(int argc, char **argv, const char *fmt, ...) cmd_log_argv(int argc, char **argv, const char *fmt, ...)
{ {
@@ -382,7 +384,7 @@ cmd_find(const char *name, char **cause)
{ {
const struct cmd_entry **loop, *entry, *found = NULL; const struct cmd_entry **loop, *entry, *found = NULL;
int ambiguous; int ambiguous;
char s[BUFSIZ]; char s[8192];
ambiguous = 0; ambiguous = 0;
for (loop = cmd_table; *loop != NULL; loop++) { for (loop = cmd_table; *loop != NULL; loop++) {
@@ -501,6 +503,83 @@ cmd_print(struct cmd *cmd)
return (out); return (out);
} }
struct cmd_list *
cmd_list_new(void)
{
struct cmd_list *cmdlist;
cmdlist = xcalloc(1, sizeof *cmdlist);
cmdlist->references = 1;
cmdlist->group = cmd_list_next_group++;
TAILQ_INIT(&cmdlist->list);
return (cmdlist);
}
void
cmd_list_append(struct cmd_list *cmdlist, struct cmd *cmd)
{
cmd->group = cmdlist->group;
TAILQ_INSERT_TAIL(&cmdlist->list, cmd, qentry);
}
void
cmd_list_move(struct cmd_list *cmdlist, struct cmd_list *from)
{
struct cmd *cmd, *cmd1;
TAILQ_FOREACH_SAFE(cmd, &from->list, qentry, cmd1) {
TAILQ_REMOVE(&from->list, cmd, qentry);
TAILQ_INSERT_TAIL(&cmdlist->list, cmd, qentry);
}
cmdlist->group = cmd_list_next_group++;
}
void
cmd_list_free(struct cmd_list *cmdlist)
{
struct cmd *cmd, *cmd1;
if (--cmdlist->references != 0)
return;
TAILQ_FOREACH_SAFE(cmd, &cmdlist->list, qentry, cmd1) {
TAILQ_REMOVE(&cmdlist->list, cmd, qentry);
cmd_free(cmd);
}
free(cmdlist);
}
char *
cmd_list_print(struct cmd_list *cmdlist, int escaped)
{
struct cmd *cmd;
char *buf, *this;
size_t len;
len = 1;
buf = xcalloc(1, len);
TAILQ_FOREACH(cmd, &cmdlist->list, qentry) {
this = cmd_print(cmd);
len += strlen(this) + 4;
buf = xrealloc(buf, len);
strlcat(buf, this, len);
if (TAILQ_NEXT(cmd, qentry) != NULL) {
if (escaped)
strlcat(buf, " \\; ", len);
else
strlcat(buf, " ; ", len);
}
free(this);
}
return (buf);
}
/* Adjust current mouse position for a pane. */ /* Adjust current mouse position for a pane. */
int int
cmd_mouse_at(struct window_pane *wp, struct mouse_event *m, u_int *xp, cmd_mouse_at(struct window_pane *wp, struct mouse_event *m, u_int *xp,
@@ -517,8 +596,8 @@ cmd_mouse_at(struct window_pane *wp, struct mouse_event *m, u_int *xp,
} }
log_debug("%s: x=%u, y=%u%s", __func__, x, y, last ? " (last)" : ""); log_debug("%s: x=%u, y=%u%s", __func__, x, y, last ? " (last)" : "");
if (m->statusat == 0 && y > 0) if (m->statusat == 0 && y >= m->statuslines)
y--; y -= m->statuslines;
if (x < wp->xoff || x >= wp->xoff + wp->sx) if (x < wp->xoff || x >= wp->xoff + wp->sx)
return (-1); return (-1);
@@ -581,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;
@@ -612,10 +691,6 @@ cmd_template_replace(const char *template, const char *s, int idx)
for (cp = s; *cp != '\0'; cp++) { for (cp = s; *cp != '\0'; cp++) {
if (quoted && strchr(quote, *cp) != NULL) if (quoted && strchr(quote, *cp) != NULL)
buf[len++] = '\\'; buf[len++] = '\\';
if (quoted && *cp == ';') {
buf[len++] = '\\';
buf[len++] = '\\';
}
buf[len++] = *cp; buf[len++] = *cp;
} }
buf[len] = '\0'; buf[len] = '\0';

View File

@@ -230,11 +230,85 @@ colour_fromstring(const char *s)
return (-1); return (-1);
} }
/* Convert 256 colour palette to 16. */ /* Convert 256 colour to RGB colour. */
u_char int
colour_256to16(u_char c) colour_256toRGB(int c)
{ {
static const u_char table[256] = { static const int table[256] = {
0x000000, 0x800000, 0x008000, 0x808000,
0x000080, 0x800080, 0x008080, 0xc0c0c0,
0x808080, 0xff0000, 0x00ff00, 0xffff00,
0x0000ff, 0xff00ff, 0x00ffff, 0xffffff,
0x000000, 0x00005f, 0x000087, 0x0000af,
0x0000d7, 0x0000ff, 0x005f00, 0x005f5f,
0x005f87, 0x005faf, 0x005fd7, 0x005fff,
0x008700, 0x00875f, 0x008787, 0x0087af,
0x0087d7, 0x0087ff, 0x00af00, 0x00af5f,
0x00af87, 0x00afaf, 0x00afd7, 0x00afff,
0x00d700, 0x00d75f, 0x00d787, 0x00d7af,
0x00d7d7, 0x00d7ff, 0x00ff00, 0x00ff5f,
0x00ff87, 0x00ffaf, 0x00ffd7, 0x00ffff,
0x5f0000, 0x5f005f, 0x5f0087, 0x5f00af,
0x5f00d7, 0x5f00ff, 0x5f5f00, 0x5f5f5f,
0x5f5f87, 0x5f5faf, 0x5f5fd7, 0x5f5fff,
0x5f8700, 0x5f875f, 0x5f8787, 0x5f87af,
0x5f87d7, 0x5f87ff, 0x5faf00, 0x5faf5f,
0x5faf87, 0x5fafaf, 0x5fafd7, 0x5fafff,
0x5fd700, 0x5fd75f, 0x5fd787, 0x5fd7af,
0x5fd7d7, 0x5fd7ff, 0x5fff00, 0x5fff5f,
0x5fff87, 0x5fffaf, 0x5fffd7, 0x5fffff,
0x870000, 0x87005f, 0x870087, 0x8700af,
0x8700d7, 0x8700ff, 0x875f00, 0x875f5f,
0x875f87, 0x875faf, 0x875fd7, 0x875fff,
0x878700, 0x87875f, 0x878787, 0x8787af,
0x8787d7, 0x8787ff, 0x87af00, 0x87af5f,
0x87af87, 0x87afaf, 0x87afd7, 0x87afff,
0x87d700, 0x87d75f, 0x87d787, 0x87d7af,
0x87d7d7, 0x87d7ff, 0x87ff00, 0x87ff5f,
0x87ff87, 0x87ffaf, 0x87ffd7, 0x87ffff,
0xaf0000, 0xaf005f, 0xaf0087, 0xaf00af,
0xaf00d7, 0xaf00ff, 0xaf5f00, 0xaf5f5f,
0xaf5f87, 0xaf5faf, 0xaf5fd7, 0xaf5fff,
0xaf8700, 0xaf875f, 0xaf8787, 0xaf87af,
0xaf87d7, 0xaf87ff, 0xafaf00, 0xafaf5f,
0xafaf87, 0xafafaf, 0xafafd7, 0xafafff,
0xafd700, 0xafd75f, 0xafd787, 0xafd7af,
0xafd7d7, 0xafd7ff, 0xafff00, 0xafff5f,
0xafff87, 0xafffaf, 0xafffd7, 0xafffff,
0xd70000, 0xd7005f, 0xd70087, 0xd700af,
0xd700d7, 0xd700ff, 0xd75f00, 0xd75f5f,
0xd75f87, 0xd75faf, 0xd75fd7, 0xd75fff,
0xd78700, 0xd7875f, 0xd78787, 0xd787af,
0xd787d7, 0xd787ff, 0xd7af00, 0xd7af5f,
0xd7af87, 0xd7afaf, 0xd7afd7, 0xd7afff,
0xd7d700, 0xd7d75f, 0xd7d787, 0xd7d7af,
0xd7d7d7, 0xd7d7ff, 0xd7ff00, 0xd7ff5f,
0xd7ff87, 0xd7ffaf, 0xd7ffd7, 0xd7ffff,
0xff0000, 0xff005f, 0xff0087, 0xff00af,
0xff00d7, 0xff00ff, 0xff5f00, 0xff5f5f,
0xff5f87, 0xff5faf, 0xff5fd7, 0xff5fff,
0xff8700, 0xff875f, 0xff8787, 0xff87af,
0xff87d7, 0xff87ff, 0xffaf00, 0xffaf5f,
0xffaf87, 0xffafaf, 0xffafd7, 0xffafff,
0xffd700, 0xffd75f, 0xffd787, 0xffd7af,
0xffd7d7, 0xffd7ff, 0xffff00, 0xffff5f,
0xffff87, 0xffffaf, 0xffffd7, 0xffffff,
0x080808, 0x121212, 0x1c1c1c, 0x262626,
0x303030, 0x3a3a3a, 0x444444, 0x4e4e4e,
0x585858, 0x626262, 0x6c6c6c, 0x767676,
0x808080, 0x8a8a8a, 0x949494, 0x9e9e9e,
0xa8a8a8, 0xb2b2b2, 0xbcbcbc, 0xc6c6c6,
0xd0d0d0, 0xdadada, 0xe4e4e4, 0xeeeeee
};
return (table[c & 0xff] | COLOUR_FLAG_RGB);
}
/* Convert 256 colour to 16 colour. */
int
colour_256to16(int c)
{
static const char table[256] = {
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
0, 4, 4, 4, 12, 12, 2, 6, 4, 4, 12, 12, 2, 2, 6, 4, 0, 4, 4, 4, 12, 12, 2, 6, 4, 4, 12, 12, 2, 2, 6, 4,
12, 12, 2, 2, 2, 6, 12, 12, 10, 10, 10, 10, 14, 12, 10, 10, 12, 12, 2, 2, 2, 6, 12, 12, 10, 10, 10, 10, 14, 12, 10, 10,
@@ -253,5 +327,5 @@ colour_256to16(u_char c)
8, 8, 8, 8, 7, 7, 7, 7, 7, 7, 15, 15, 15, 15, 15, 15 8, 8, 8, 8, 7, 7, 7, 7, 7, 7, 15, 15, 15, 15, 15, 15
}; };
return (table[c]); return (table[c & 0xff]);
} }

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
@@ -346,7 +370,6 @@ int utf8proc_mbtowc(wchar_t *, const char *, size_t);
int utf8proc_wctomb(char *, wchar_t); int utf8proc_wctomb(char *, wchar_t);
#endif #endif
#ifndef HAVE_GETOPT
/* getopt.c */ /* getopt.c */
extern int BSDopterr; extern int BSDopterr;
extern int BSDoptind; extern int BSDoptind;
@@ -360,6 +383,5 @@ int BSDgetopt(int, char *const *, const char *);
#define optopt BSDoptopt #define optopt BSDoptopt
#define optreset BSDoptreset #define optreset BSDoptreset
#define optarg BSDoptarg #define optarg BSDoptarg
#endif
#endif /* COMPAT_H */ #endif /* COMPAT_H */

View File

@@ -19,8 +19,10 @@
#include <stdarg.h> #include <stdarg.h>
#include <stdio.h> #include <stdio.h>
#include <string.h> #include <string.h>
#include <stdlib.h>
#include "compat.h" #include "compat.h"
#include "xmalloc.h"
int int
asprintf(char **ret, const char *fmt, ...) asprintf(char **ret, const char *fmt, ...)

View File

@@ -1,6 +1,6 @@
# configure.ac # configure.ac
AC_INIT([tmux], 3.0-rc3) AC_INIT([tmux], 3.1a)
AC_PREREQ([2.60]) AC_PREREQ([2.60])
AC_CONFIG_AUX_DIR(etc) AC_CONFIG_AUX_DIR(etc)
@@ -125,6 +125,12 @@ AC_FUNC_STRNLEN
# Look for clock_gettime. Must come before event_init. # Look for clock_gettime. Must come before event_init.
AC_SEARCH_LIBS(clock_gettime, rt) AC_SEARCH_LIBS(clock_gettime, rt)
# Always use our getopt because 1) glibc's doesn't enforce argument order 2)
# musl does not set optarg to NULL for flags without arguments (although it is
# not required to, but it is helpful) 3) there are probably other weird
# implementations.
AC_LIBOBJ(getopt)
# Look for libevent. # Look for libevent.
PKG_CHECK_MODULES( PKG_CHECK_MODULES(
LIBEVENT, LIBEVENT,
@@ -424,40 +430,6 @@ else
AC_LIBOBJ(unvis) AC_LIBOBJ(unvis)
fi fi
# Look for getopt. glibc's getopt does not enforce argument order and the ways
# of making it do so are stupid, so just use our own instead.
AC_CHECK_FUNC(getopt, found_getopt=yes, found_getopt=no)
if test "x$found_getopt" != xno; then
AC_MSG_CHECKING(if getopt is suitable)
AC_EGREP_CPP(
yes,
[
#include <features.h>
#ifdef __GLIBC__
yes
#endif
],
[
found_getopt=no
AC_MSG_RESULT(no)
],
AC_MSG_RESULT(yes))
fi
if test "x$found_getopt" != xno; then
AC_CHECK_DECLS(
[optarg, optind, optreset],
,
found_getopt=no,
[
#include <unistd.h>
])
fi
if test "x$found_getopt" != xno; then
AC_DEFINE(HAVE_GETOPT)
else
AC_LIBOBJ(getopt)
fi
# Look for fdforkpty and forkpty in libutil. # Look for fdforkpty and forkpty in libutil.
AC_SEARCH_LIBS(fdforkpty, util, found_fdforkpty=yes, found_fdforkpty=no) AC_SEARCH_LIBS(fdforkpty, util, found_fdforkpty=yes, found_fdforkpty=no)
if test "x$found_fdforkpty" = xyes; then if test "x$found_fdforkpty" = xyes; then

View File

@@ -36,6 +36,9 @@ control_notify_input(struct client *c, struct window_pane *wp,
if (c->session == NULL) if (c->session == NULL)
return; return;
if (c->flags & CLIENT_CONTROL_NOOUTPUT)
return;
/* /*
* Only write input if the window pane is linked to a window belonging * Only write input if the window pane is linked to a window belonging
* to the client's session. * to the client's session.
@@ -51,7 +54,8 @@ 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); evbuffer_add(message, "", 1);
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,21 +54,24 @@ 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);
c->flags |= CLIENT_EXIT; c->flags |= CLIENT_EXIT;
break; break;
} }
@@ -103,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

@@ -142,7 +142,8 @@ format_draw_put_list(struct screen_write_ctx *octx,
width -= list_left->cx; width -= list_left->cx;
} }
if (start + width < list->cx && width > list_right->cx) { if (start + width < list->cx && width > list_right->cx) {
screen_write_cursormove(octx, ocx + offset + width - 1, ocy, 0); screen_write_cursormove(octx, ocx + offset + width -
list_right->cx, ocy, 0);
screen_write_fast_copy(octx, list_right, 0, 0, list_right->cx, screen_write_fast_copy(octx, list_right, 0, 0, list_right->cx,
1); 1);
width -= list_right->cx; width -= list_right->cx;
@@ -511,9 +512,10 @@ format_draw(struct screen_write_ctx *octx, const struct grid_cell *base,
u_int ocx = os->cx, ocy = os->cy, i, width[TOTAL]; u_int ocx = os->cx, ocy = os->cy, i, width[TOTAL];
u_int map[] = { LEFT, LEFT, CENTRE, RIGHT }; u_int map[] = { LEFT, LEFT, CENTRE, RIGHT };
int focus_start = -1, focus_end = -1; int focus_start = -1, focus_end = -1;
int list_state = -1; int list_state = -1, fill = -1;
enum style_align list_align = STYLE_ALIGN_DEFAULT; enum style_align list_align = STYLE_ALIGN_DEFAULT;
struct style sy; struct grid_cell gc, current_default;
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;
@@ -522,7 +524,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);
@@ -534,7 +537,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;
} }
@@ -564,7 +567,7 @@ format_draw(struct screen_write_ctx *octx, const struct grid_cell *base,
cp++; cp++;
} }
/* Draw the cell to th current screen. */ /* Draw the cell to the current screen. */
screen_write_cell(&ctx[current], &sy.gc); screen_write_cell(&ctx[current], &sy.gc);
width[current] += ud->width; width[current] += ud->width;
continue; continue;
@@ -580,7 +583,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;
@@ -590,6 +594,19 @@ format_draw(struct screen_write_ctx *octx, const struct grid_cell *base,
style_tostring(&sy)); style_tostring(&sy));
free(tmp); free(tmp);
/* If this style has a fill colour, store it for later. */
if (sy.fill != 8)
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:
@@ -711,6 +728,14 @@ format_draw(struct screen_write_ctx *octx, const struct grid_cell *base,
fr->argument, names[fr->index], fr->start, fr->end); fr->argument, names[fr->index], fr->start, fr->end);
} }
/* Clear the available area. */
if (fill != -1) {
memcpy(&gc, &grid_default_cell, sizeof gc);
gc.bg = fill;
for (i = 0; i < available; i++)
screen_write_putc(octx, &gc, ' ');
}
/* /*
* Draw the screens. How they are arranged depends on where the list * Draw the screens. How they are arranged depends on where the list
* appearsq. * appearsq.
@@ -791,7 +816,8 @@ format_width(const char *expanded)
} else if (*cp > 0x1f && *cp < 0x7f) { } else if (*cp > 0x1f && *cp < 0x7f) {
width++; width++;
cp++; cp++;
} } else
cp++;
} }
return (width); return (width);
} }
@@ -824,8 +850,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;
@@ -871,8 +899,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;

540
format.c
View File

@@ -23,6 +23,7 @@
#include <errno.h> #include <errno.h>
#include <fnmatch.h> #include <fnmatch.h>
#include <libgen.h> #include <libgen.h>
#include <regex.h>
#include <stdarg.h> #include <stdarg.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
@@ -455,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)
@@ -527,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)
@@ -573,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);
@@ -676,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);
@@ -702,6 +895,44 @@ format_cb_pane_in_mode(struct format_tree *ft, struct format_entry *fe)
xasprintf(&fe->value, "%u", n); xasprintf(&fe->value, "%u", n);
} }
/* Callback for pane_at_top. */
static void
format_cb_pane_at_top(struct format_tree *ft, struct format_entry *fe)
{
struct window_pane *wp = ft->wp;
struct window *w = wp->window;
int status, flag;
if (wp == NULL)
return;
status = options_get_number(w->options, "pane-border-status");
if (status == PANE_STATUS_TOP)
flag = (wp->yoff == 1);
else
flag = (wp->yoff == 0);
xasprintf(&fe->value, "%d", flag);
}
/* Callback for pane_at_bottom. */
static void
format_cb_pane_at_bottom(struct format_tree *ft, struct format_entry *fe)
{
struct window_pane *wp = ft->wp;
struct window *w = wp->window;
int status, flag;
if (wp == NULL)
return;
status = options_get_number(w->options, "pane-border-status");
if (status == PANE_STATUS_BOTTOM)
flag = (wp->yoff + wp->sy == w->sy - 1);
else
flag = (wp->yoff + wp->sy == w->sy);
xasprintf(&fe->value, "%d", flag);
}
/* Callback for cursor_character. */ /* Callback for cursor_character. */
static void static void
format_cb_cursor_character(struct format_tree *ft, struct format_entry *fe) format_cb_cursor_character(struct format_tree *ft, struct format_entry *fe)
@@ -717,28 +948,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 (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;
@@ -791,30 +1013,44 @@ 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;
wp = cmd_mouse_pane(&ft->m, NULL, NULL); wp = cmd_mouse_pane(&ft->m, NULL, NULL);
if (wp == NULL) if (wp == NULL)
return; return;
if (!TAILQ_EMPTY (&wp->modes))
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++) {
@@ -827,9 +1063,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. */
@@ -899,7 +1159,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);
@@ -944,12 +1204,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);
@@ -961,7 +1221,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, ...)
@@ -993,8 +1252,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);
@@ -1067,6 +1325,8 @@ format_find(struct format_tree *ft, const char *key, int modifiers)
if (~modifiers & FORMAT_TIMESTRING) { if (~modifiers & FORMAT_TIMESTRING) {
o = options_parse_get(global_options, key, &idx, 0); o = options_parse_get(global_options, key, &idx, 0);
if (o == NULL && ft->wp != NULL)
o = options_parse_get(ft->wp->options, key, &idx, 0);
if (o == NULL && ft->w != NULL) if (o == NULL && ft->w != NULL)
o = options_parse_get(ft->w->options, key, &idx, 0); o = options_parse_get(ft->w->options, key, &idx, 0);
if (o == NULL) if (o == NULL)
@@ -1097,11 +1357,10 @@ format_find(struct format_tree *ft, const char *key, int modifiers)
xasprintf(&found, "%lld", (long long)fe->t); xasprintf(&found, "%lld", (long long)fe->t);
goto found; goto found;
} }
if (fe->value == NULL && fe->cb != NULL) { if (fe->value == NULL && fe->cb != NULL)
fe->cb(ft, fe); fe->cb(ft, fe);
if (fe->value == NULL) if (fe->value == NULL)
fe->value = xstrdup(""); fe->value = xstrdup("");
}
found = xstrdup(fe->value); found = xstrdup(fe->value);
goto found; goto found;
} }
@@ -1263,7 +1522,7 @@ format_build_modifiers(struct format_tree *ft, const char **s, u_int *count)
cp++; cp++;
/* Check single character modifiers with no arguments. */ /* Check single character modifiers with no arguments. */
if (strchr("lmCbdtqETSWP<>", cp[0]) != NULL && if (strchr("lbdtqETSWP<>", cp[0]) != NULL &&
format_is_end(cp[1])) { format_is_end(cp[1])) {
format_add_modifier(&list, count, cp, 1, NULL, 0); format_add_modifier(&list, count, cp, 1, NULL, 0);
cp++; cp++;
@@ -1284,7 +1543,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("s=", cp[0]) == NULL) if (strchr("mCs=p", cp[0]) == NULL)
break; break;
c = cp[0]; c = cp[0];
@@ -1345,39 +1604,67 @@ format_build_modifiers(struct format_tree *ft, const char **s, u_int *count)
return list; return list;
} }
/* Match against an fnmatch(3) pattern or regular expression. */
static char *
format_match(struct format_modifier *fm, const char *pattern, const char *text)
{
const char *s = "";
regex_t r;
int flags = 0;
if (fm->argc >= 1)
s = fm->argv[0];
if (strchr(s, 'r') == NULL) {
if (strchr(s, 'i') != NULL)
flags |= FNM_CASEFOLD;
if (fnmatch(pattern, text, flags) != 0)
return (xstrdup("0"));
} else {
flags = REG_EXTENDED|REG_NOSUB;
if (strchr(s, 'i') != NULL)
flags |= REG_ICASE;
if (regcomp(&r, pattern, flags) != 0)
return (xstrdup("0"));
if (regexec(&r, text, 0, NULL, 0) != 0) {
regfree(&r);
return (xstrdup("0"));
}
regfree(&r);
}
return (xstrdup("1"));
}
/* Perform substitution in string. */ /* Perform substitution in string. */
static char * static char *
format_substitute(const char *source, const char *from, const char *to) format_sub(struct format_modifier *fm, const char *text, const char *pattern,
const char *with)
{ {
char *copy, *new; char *value;
const char *cp; int flags = REG_EXTENDED;
size_t fromlen, tolen, newlen, used;
fromlen = strlen(from); if (fm->argc >= 3 && strchr(fm->argv[2], 'i') != NULL)
tolen = strlen(to); flags |= REG_ICASE;
value = regsub(pattern, with, text, flags);
if (value == NULL)
return (xstrdup(text));
return (value);
}
newlen = strlen(source) + 1; /* Search inside pane. */
copy = new = xmalloc(newlen); static char *
format_search(struct format_modifier *fm, struct window_pane *wp, const char *s)
{
int ignore = 0, regex = 0;
char *value;
for (cp = source; *cp != '\0'; /* nothing */) { if (fm->argc >= 1) {
if (strncmp(cp, from, fromlen) != 0) { if (strchr(fm->argv[0], 'i') != NULL)
*new++ = *cp++; ignore = 1;
continue; if (strchr(fm->argv[0], 'r') != NULL)
} regex = 1;
used = new - copy;
newlen += tolen;
copy = xrealloc(copy, newlen);
new = copy + used;
memcpy(new, to, tolen);
new += tolen;
cp += fromlen;
} }
xasprintf(&value, "%u", window_pane_search(wp, s, regex, ignore));
*new = '\0'; return (value);
return (copy);
} }
/* Loop over sessions. */ /* Loop over sessions. */
@@ -1517,16 +1804,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; 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;
int j;
/* Make a copy of the key. */ /* Make a copy of the key. */
copy = copy0 = xstrndup(key, keylen); copy = copy0 = xstrndup(key, keylen);
@@ -1553,20 +1839,30 @@ format_replace(struct format_tree *ft, const char *key, size_t keylen,
search = fm; search = fm;
break; break;
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 && fm->argc != 2) if (fm->argc < 1)
break; break;
limit = strtonum(fm->argv[0], INT_MIN, INT_MAX, limit = strtonum(fm->argv[0], INT_MIN, INT_MAX,
&errptr); &errptr);
if (errptr != NULL) if (errptr != NULL)
limit = 0; limit = 0;
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;
@@ -1630,13 +1926,15 @@ format_replace(struct format_tree *ft, const char *key, size_t keylen,
goto fail; goto fail;
} else if (search != NULL) { } else if (search != NULL) {
/* Search in pane. */ /* Search in pane. */
new = format_expand(ft, copy);
if (wp == NULL) { if (wp == NULL) {
format_log(ft, "search '%s' but no pane", copy); format_log(ft, "search '%s' but no pane", new);
value = xstrdup("0"); value = xstrdup("0");
} else { } else {
format_log(ft, "search '%s' pane %%%u", copy, wp->id); format_log(ft, "search '%s' pane %%%u", new, wp->id);
xasprintf(&value, "%u", window_pane_search(wp, copy)); value = format_search(fm, wp, new);
} }
free(new);
} else if (cmp != NULL) { } else if (cmp != NULL) {
/* Comparison of left and right. */ /* Comparison of left and right. */
if (format_choose(ft, copy, &left, &right, 1) != 0) { if (format_choose(ft, copy, &left, &right, 1) != 0) {
@@ -1687,12 +1985,8 @@ format_replace(struct format_tree *ft, const char *key, size_t keylen,
value = xstrdup("1"); value = xstrdup("1");
else else
value = xstrdup("0"); value = xstrdup("0");
} else if (strcmp(cmp->modifier, "m") == 0) { } else if (strcmp(cmp->modifier, "m") == 0)
if (fnmatch(left, right, 0) == 0) value = format_match(cmp, left, right);
value = xstrdup("1");
else
value = xstrdup("0");
}
free(right); free(right);
free(left); free(left);
@@ -1769,12 +2063,15 @@ done:
} }
/* Perform substitution if any. */ /* Perform substitution if any. */
if (sub != NULL) { for (i = 0; i < nsub; i++) {
new = format_substitute(value, sub->argv[0], sub->argv[1]); left = format_expand(ft, sub[i]->argv[0]);
format_log(ft, "substituted '%s' to '%s: %s", sub->argv[0], right = format_expand(ft, sub[i]->argv[1]);
sub->argv[1], new); new = format_sub(sub[i], value, left, right);
format_log(ft, "substitute '%s' to '%s': %s", left, right, new);
free(value); free(value);
value = new; value = new;
free(right);
free(left);
} }
/* Truncate the value if needed. */ /* Truncate the value if needed. */
@@ -1800,6 +2097,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) {
@@ -1812,12 +2122,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);
@@ -1998,10 +2311,10 @@ void
format_defaults(struct format_tree *ft, struct client *c, struct session *s, format_defaults(struct format_tree *ft, struct client *c, struct session *s,
struct winlink *wl, struct window_pane *wp) struct winlink *wl, struct window_pane *wp)
{ {
if (c != NULL) if (c != NULL && c->name != NULL)
log_debug("%s: c=%s", __func__, c->name); log_debug("%s: c=%s", __func__, c->name);
else else
log_debug("%s: s=none", __func__); log_debug("%s: c=none", __func__);
if (s != NULL) if (s != NULL)
log_debug("%s: s=$%u", __func__, s->id); log_debug("%s: s=$%u", __func__, s->id);
else else
@@ -2057,8 +2370,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);
@@ -2067,6 +2386,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);
@@ -2079,7 +2400,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;
@@ -2089,14 +2409,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);
@@ -2140,6 +2460,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);
@@ -2177,12 +2499,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",
@@ -2192,6 +2527,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. */
@@ -2219,6 +2559,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));
@@ -2242,9 +2584,9 @@ format_defaults_pane(struct format_tree *ft, struct window_pane *wp)
format_add(ft, "pane_right", "%u", wp->xoff + wp->sx - 1); format_add(ft, "pane_right", "%u", wp->xoff + wp->sx - 1);
format_add(ft, "pane_bottom", "%u", wp->yoff + wp->sy - 1); format_add(ft, "pane_bottom", "%u", wp->yoff + wp->sy - 1);
format_add(ft, "pane_at_left", "%d", wp->xoff == 0); format_add(ft, "pane_at_left", "%d", wp->xoff == 0);
format_add(ft, "pane_at_top", "%d", wp->yoff == 0); format_add_cb(ft, "pane_at_top", format_cb_pane_at_top);
format_add(ft, "pane_at_right", "%d", wp->xoff + wp->sx == w->sx); format_add(ft, "pane_at_right", "%d", wp->xoff + wp->sx == w->sx);
format_add(ft, "pane_at_bottom", "%d", wp->yoff + wp->sy == w->sy); format_add_cb(ft, "pane_at_bottom", format_cb_pane_at_bottom);
wme = TAILQ_FIRST(&wp->modes); wme = TAILQ_FIRST(&wp->modes);
if (wme != NULL) { if (wme != NULL) {
@@ -2286,6 +2628,8 @@ format_defaults_pane(struct format_tree *ft, struct window_pane *wp)
!!(wp->base.mode & MODE_KKEYPAD)); !!(wp->base.mode & MODE_KKEYPAD));
format_add(ft, "wrap_flag", "%d", format_add(ft, "wrap_flag", "%d",
!!(wp->base.mode & MODE_WRAP)); !!(wp->base.mode & MODE_WRAP));
format_add(ft, "origin_flag", "%d",
!!(wp->base.mode & MODE_ORIGIN));
format_add(ft, "mouse_any_flag", "%d", format_add(ft, "mouse_any_flag", "%d",
!!(wp->base.mode & ALL_MOUSE_MODES)); !!(wp->base.mode & ALL_MOUSE_MODES));
@@ -2295,6 +2639,10 @@ format_defaults_pane(struct format_tree *ft, struct window_pane *wp)
!!(wp->base.mode & MODE_MOUSE_BUTTON)); !!(wp->base.mode & MODE_MOUSE_BUTTON));
format_add(ft, "mouse_all_flag", "%d", format_add(ft, "mouse_all_flag", "%d",
!!(wp->base.mode & MODE_MOUSE_ALL)); !!(wp->base.mode & MODE_MOUSE_ALL));
format_add(ft, "mouse_utf8_flag", "%d",
!!(wp->base.mode & MODE_MOUSE_UTF8));
format_add(ft, "mouse_sgr_flag", "%d",
!!(wp->base.mode & MODE_MOUSE_SGR));
format_add_cb(ft, "pane_tabs", format_cb_pane_tabs); format_add_cb(ft, "pane_tabs", format_cb_pane_tabs);
} }

55
grid.c
View File

@@ -37,12 +37,12 @@
/* Default grid cell data. */ /* Default grid cell data. */
const struct grid_cell grid_default_cell = { const struct grid_cell grid_default_cell = {
0, 0, 8, 8, { { ' ' }, 0, 1, 1 } { { ' ' }, 0, 1, 1 }, 0, 0, 8, 8, 0
}; };
/* Cleared grid cell data. */ /* Cleared grid cell data. */
const struct grid_cell grid_cleared_cell = { const struct grid_cell grid_cleared_cell = {
GRID_FLAG_CLEARED, 0, 8, 8, { { ' ' }, 0, 1, 1 } { { ' ' }, 0, 1, 1 }, 0, GRID_FLAG_CLEARED, 8, 8, 0
}; };
static const struct grid_cell_entry grid_cleared_entry = { static const struct grid_cell_entry grid_cleared_entry = {
GRID_FLAG_CLEARED, { .data = { 0, 8, 8, ' ' } } GRID_FLAG_CLEARED, { .data = { 0, 8, 8, ' ' } }
@@ -82,6 +82,8 @@ grid_need_extended_cell(const struct grid_cell_entry *gce,
return (1); return (1);
if ((gc->fg & COLOUR_FLAG_RGB) || (gc->bg & COLOUR_FLAG_RGB)) if ((gc->fg & COLOUR_FLAG_RGB) || (gc->bg & COLOUR_FLAG_RGB))
return (1); return (1);
if (gc->us != 0) /* only supports 256 or RGB */
return (1);
return (0); return (0);
} }
@@ -184,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);
@@ -473,6 +477,7 @@ grid_get_cell1(struct grid_line *gl, u_int px, struct grid_cell *gc)
gc->bg = gce->data.bg; gc->bg = gce->data.bg;
if (gce->flags & GRID_FLAG_BG256) if (gce->flags & GRID_FLAG_BG256)
gc->bg |= COLOUR_FLAG_256; gc->bg |= COLOUR_FLAG_256;
gc->us = 0;
utf8_set(&gc->data, gce->data.data); utf8_set(&gc->data, gce->data.data);
} }
@@ -544,7 +549,7 @@ void
grid_clear(struct grid *gd, u_int px, u_int py, u_int nx, u_int ny, u_int bg) grid_clear(struct grid *gd, u_int px, u_int py, u_int nx, u_int ny, u_int bg)
{ {
struct grid_line *gl; struct grid_line *gl;
u_int xx, yy; u_int xx, yy, ox, sx;
if (nx == 0 || ny == 0) if (nx == 0 || ny == 0)
return; return;
@@ -561,16 +566,20 @@ grid_clear(struct grid *gd, u_int px, u_int py, u_int nx, u_int ny, u_int bg)
for (yy = py; yy < py + ny; yy++) { for (yy = py; yy < py + ny; yy++) {
gl = &gd->linedata[yy]; gl = &gd->linedata[yy];
if (px + nx >= gd->sx && px < gl->cellused)
gl->cellused = px; sx = gd->sx;
if (px > gl->cellsize && COLOUR_DEFAULT(bg)) if (sx > gl->cellsize)
continue; sx = gl->cellsize;
if (px + nx >= gl->cellsize && COLOUR_DEFAULT(bg)) { ox = nx;
gl->cellsize = px; if (COLOUR_DEFAULT(bg)) {
continue; if (px > sx)
continue;
if (px + nx > sx)
ox = sx - px;
} }
grid_expand_line(gd, yy, px + nx, 8); /* default bg first */
for (xx = px; xx < px + nx; xx++) grid_expand_line(gd, yy, px + ox, 8); /* default bg first */
for (xx = px; xx < px + ox; xx++)
grid_clear_cell(gd, xx, yy, bg); grid_clear_cell(gd, xx, yy, bg);
} }
} }

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,7 +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). */
if (key & KEYC_LITERAL) {
ud.data[0] = (u_char)key;
bufferevent_write(wp->event, &ud.data[0], 1);
return (0);
}
/* Is this backspace? */
if ((key & KEYC_MASK_KEY) == KEYC_BSPACE) {
newkey = options_get_number(global_options, "backspace");
if (newkey >= 0x7f)
newkey = '\177';
key = newkey|(key & KEYC_MASK_MOD);
} }
/* /*
@@ -182,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);
} }
/* /*
@@ -201,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;
@@ -224,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);
@@ -233,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. */

165
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>
@@ -244,6 +245,7 @@ enum input_csi_type {
INPUT_CSI_RM, INPUT_CSI_RM,
INPUT_CSI_RM_PRIVATE, INPUT_CSI_RM_PRIVATE,
INPUT_CSI_SCP, INPUT_CSI_SCP,
INPUT_CSI_SD,
INPUT_CSI_SGR, INPUT_CSI_SGR,
INPUT_CSI_SM, INPUT_CSI_SM,
INPUT_CSI_SM_PRIVATE, INPUT_CSI_SM_PRIVATE,
@@ -270,6 +272,7 @@ static const struct input_table_entry input_csi_table[] = {
{ 'M', "", INPUT_CSI_DL }, { 'M', "", INPUT_CSI_DL },
{ 'P', "", INPUT_CSI_DCH }, { 'P', "", INPUT_CSI_DCH },
{ 'S', "", INPUT_CSI_SU }, { 'S', "", INPUT_CSI_SU },
{ 'T', "", INPUT_CSI_SD },
{ 'X', "", INPUT_CSI_ECH }, { 'X', "", INPUT_CSI_ECH },
{ 'Z', "", INPUT_CSI_CBT }, { 'Z', "", INPUT_CSI_CBT },
{ '`', "", INPUT_CSI_HPA }, { '`', "", INPUT_CSI_HPA },
@@ -738,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);
@@ -770,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)
{ {
@@ -874,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));
@@ -886,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)
@@ -914,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
@@ -1291,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);
@@ -1422,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;
@@ -1525,6 +1545,11 @@ input_csi_dispatch(struct input_ctx *ictx)
if (n != -1) if (n != -1)
screen_write_scrollup(sctx, n, bg); screen_write_scrollup(sctx, n, bg);
break; break;
case INPUT_CSI_SD:
n = input_get(ictx, 0, 1, 1);
if (n != -1)
screen_write_scrolldown(sctx, n, bg);
break;
case INPUT_CSI_TBC: case INPUT_CSI_TBC:
switch (input_get(ictx, 0, 0, 0)) { switch (input_get(ictx, 0, 0, 0)) {
case -1: case -1:
@@ -1829,6 +1854,8 @@ input_csi_dispatch_sgr_256_do(struct input_ctx *ictx, int fgbg, int c)
gc->fg = c | COLOUR_FLAG_256; gc->fg = c | COLOUR_FLAG_256;
else if (fgbg == 48) else if (fgbg == 48)
gc->bg = c | COLOUR_FLAG_256; gc->bg = c | COLOUR_FLAG_256;
else if (fgbg == 58)
gc->us = c | COLOUR_FLAG_256;
} }
return (1); return (1);
} }
@@ -1862,6 +1889,8 @@ input_csi_dispatch_sgr_rgb_do(struct input_ctx *ictx, int fgbg, int r, int g,
gc->fg = colour_join_rgb(r, g, b); gc->fg = colour_join_rgb(r, g, b);
else if (fgbg == 48) else if (fgbg == 48)
gc->bg = colour_join_rgb(r, g, b); gc->bg = colour_join_rgb(r, g, b);
else if (fgbg == 58)
gc->us = colour_join_rgb(r, g, b);
return (1); return (1);
} }
@@ -1938,7 +1967,7 @@ input_csi_dispatch_sgr_colon(struct input_ctx *ictx, u_int i)
} }
return; return;
} }
if (n < 2 || (p[0] != 38 && p[0] != 48)) if (n < 2 || (p[0] != 38 && p[0] != 48 && p[0] != 58))
return; return;
switch (p[1]) { switch (p[1]) {
case 2: case 2:
@@ -1983,7 +2012,7 @@ input_csi_dispatch_sgr(struct input_ctx *ictx)
if (n == -1) if (n == -1)
continue; continue;
if (n == 38 || n == 48) { if (n == 38 || n == 48 || n == 58) {
i++; i++;
switch (input_get(ictx, i, 0, -1)) { switch (input_get(ictx, i, 0, -1)) {
case 2: case 2:
@@ -2078,6 +2107,9 @@ input_csi_dispatch_sgr(struct input_ctx *ictx)
case 55: case 55:
gc->attr &= ~GRID_ATTR_OVERLINE; gc->attr &= ~GRID_ATTR_OVERLINE;
break; break;
case 59:
gc->us = 0;
break;
case 90: case 90:
case 91: case 91:
case 92: case 92:
@@ -2181,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;
@@ -2236,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. */
@@ -2257,14 +2291,24 @@ 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->window->options, "allow-rename")) if (!options_get_number(ictx->wp->options, "allow-rename"))
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 (!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);
@@ -2306,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)
@@ -2324,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;
} }
@@ -2347,12 +2439,17 @@ input_osc_10(struct input_ctx *ictx, const char *p)
{ {
struct window_pane *wp = ictx->wp; struct window_pane *wp = ictx->wp;
u_int r, g, b; u_int r, g, b;
char tmp[16];
if (sscanf(p, "rgb:%2x/%2x/%2x", &r, &g, &b) != 3) if (strcmp(p, "?") == 0)
goto bad; return;
wp->style.gc.fg = colour_join_rgb(r, g, b); if (!input_osc_parse_colour(p, &r, &g, &b))
wp->flags |= PANE_REDRAW; goto bad;
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-active-style", 1, tmp);
wp->flags |= (PANE_REDRAW|PANE_STYLECHANGED);
return; return;
@@ -2366,12 +2463,17 @@ input_osc_11(struct input_ctx *ictx, const char *p)
{ {
struct window_pane *wp = ictx->wp; struct window_pane *wp = ictx->wp;
u_int r, g, b; u_int r, g, b;
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);
wp->style.gc.bg = colour_join_rgb(r, g, b); options_set_style(wp->options, "window-style", 1, tmp);
wp->flags |= PANE_REDRAW; options_set_style(wp->options, "window-active-style", 1, tmp);
wp->flags |= (PANE_REDRAW|PANE_STYLECHANGED);
return; return;
@@ -2409,7 +2511,6 @@ input_osc_52(struct input_ctx *ictx, const char *p)
outlen = 4 * ((len + 2) / 3) + 1; outlen = 4 * ((len + 2) / 3) + 1;
out = xmalloc(outlen); out = xmalloc(outlen);
if ((outlen = b64_ntop(buf, len, out, outlen)) == -1) { if ((outlen = b64_ntop(buf, len, out, outlen)) == -1) {
abort();
free(out); free(out);
return; return;
} }

2
job.c
View File

@@ -117,7 +117,7 @@ job_run(const char *cmd, struct session *s, const char *cwd,
close(out[0]); close(out[0]);
nullfd = open(_PATH_DEVNULL, O_RDWR, 0); nullfd = open(_PATH_DEVNULL, O_RDWR, 0);
if (nullfd < 0) if (nullfd == -1)
fatal("open failed"); fatal("open failed");
if (dup2(nullfd, STDERR_FILENO) == -1) if (dup2(nullfd, STDERR_FILENO) == -1)
fatal("dup2 failed"); fatal("dup2 failed");

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,13 +320,12 @@ 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 C-m 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,
"bind -n MouseDown3Pane if -Ft= '#{||:#{mouse_any_flag},#{pane_in_mode}}' 'select-pane -t=; send-keys -M' {display-menu -t= -xM -yM -T \"#[align=centre]#{pane_index} (#{pane_id})\" " DEFAULT_PANE_MENU "}", "bind -n MouseDown3Pane if -Ft= '#{||:#{mouse_any_flag},#{pane_in_mode}}' 'select-pane -t=; send-keys -M' {display-menu -t= -xM -yM -T \"#[align=centre]#{pane_index} (#{pane_id})\" " DEFAULT_PANE_MENU "}",
"bind -n M-MouseDown3Pane display-menu -t= -xM -yM -T \"#[align=centre]#{pane_index} (#{pane_id})\" " DEFAULT_PANE_MENU, "bind -n M-MouseDown3Pane display-menu -t= -xM -yM -T \"#[align=centre]#{pane_index} (#{pane_id})\" " DEFAULT_PANE_MENU,
"bind M-m display-menu -xP -yP -T \"#[align=centre]#{pane_index} (#{pane_id})\" " DEFAULT_PANE_MENU, "bind > display-menu -xP -yP -T \"#[align=centre]#{pane_index} (#{pane_id})\" " DEFAULT_PANE_MENU,
"bind -Tcopy-mode C-Space send -X begin-selection", "bind -Tcopy-mode C-Space send -X begin-selection",
"bind -Tcopy-mode C-a send -X start-of-line", "bind -Tcopy-mode C-a send -X start-of-line",
@@ -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
@@ -240,58 +242,24 @@ key_string_lookup_string(const char *string)
const char * const char *
key_string_lookup_key(key_code key) key_string_lookup_key(key_code key)
{ {
static char out[32]; static char out[32];
char tmp[8]; char tmp[8];
u_int i; const char *s;
struct utf8_data ud; u_int i;
size_t off; struct utf8_data ud;
size_t off;
*out = '\0'; *out = '\0';
/* Handle no key. */ /* Literal keys are themselves. */
if (key == KEYC_NONE) if (key & KEYC_LITERAL) {
return ("None"); snprintf(out, sizeof out, "%c", (int)(key & 0xff));
/* Handle special keys. */
if (key == KEYC_UNKNOWN)
return ("Unknown");
if (key == KEYC_ANY)
return ("Any");
if (key == KEYC_FOCUS_IN)
return ("FocusIn");
if (key == KEYC_FOCUS_OUT)
return ("FocusOut");
if (key == KEYC_PASTE_START)
return ("PasteStart");
if (key == KEYC_PASTE_END)
return ("PasteEnd");
if (key == KEYC_MOUSE)
return ("Mouse");
if (key == KEYC_DRAGGING)
return ("Dragging");
if (key == KEYC_MOUSEMOVE_PANE)
return ("MouseMovePane");
if (key == KEYC_MOUSEMOVE_STATUS)
return ("MouseMoveStatus");
if (key == KEYC_MOUSEMOVE_STATUS_LEFT)
return ("MouseMoveStatusLeft");
if (key == KEYC_MOUSEMOVE_STATUS_RIGHT)
return ("MouseMoveStatusRight");
if (key == KEYC_MOUSEMOVE_BORDER)
return ("MouseMoveBorder");
if (key >= KEYC_USER && key < KEYC_USER + KEYC_NUSER) {
snprintf(out, sizeof out, "User%u", (u_int)(key - KEYC_USER));
return (out); return (out);
} }
/* /* Display C-@ as C-Space. */
* Special case: display C-@ as C-Space. Could do this below in
* the (key >= 0 && key <= 32), but this way we let it be found
* in key_string_table, for the unlikely chance that we might
* change its name.
*/
if ((key & KEYC_MASK_KEY) == 0) if ((key & KEYC_MASK_KEY) == 0)
key = ' ' | KEYC_CTRL | (key & KEYC_MASK_MOD); key = ' ' | KEYC_CTRL | (key & KEYC_MASK_MOD);
/* Fill in the modifiers. */ /* Fill in the modifiers. */
if (key & KEYC_CTRL) if (key & KEYC_CTRL)
@@ -302,6 +270,69 @@ key_string_lookup_key(key_code key)
strlcat(out, "S-", sizeof out); strlcat(out, "S-", sizeof out);
key &= KEYC_MASK_KEY; key &= KEYC_MASK_KEY;
/* Handle no key. */
if (key == KEYC_NONE)
return ("None");
/* Handle special keys. */
if (key == KEYC_UNKNOWN) {
s = "Unknown";
goto append;
}
if (key == KEYC_ANY) {
s = "Any";
goto append;
}
if (key == KEYC_FOCUS_IN) {
s = "FocusIn";
goto append;
}
if (key == KEYC_FOCUS_OUT) {
s = "FocusOut";
goto append;
}
if (key == KEYC_PASTE_START) {
s = "PasteStart";
goto append;
}
if (key == KEYC_PASTE_END) {
s = "PasteEnd";
goto append;
}
if (key == KEYC_MOUSE) {
s = "Mouse";
goto append;
}
if (key == KEYC_DRAGGING) {
s = "Dragging";
goto append;
}
if (key == KEYC_MOUSEMOVE_PANE) {
s = "MouseMovePane";
goto append;
}
if (key == KEYC_MOUSEMOVE_STATUS) {
s = "MouseMoveStatus";
goto append;
}
if (key == KEYC_MOUSEMOVE_STATUS_LEFT) {
s = "MouseMoveStatusLeft";
goto append;
}
if (key == KEYC_MOUSEMOVE_STATUS_RIGHT) {
s = "MouseMoveStatusRight";
goto append;
}
if (key == KEYC_MOUSEMOVE_BORDER) {
s = "MouseMoveBorder";
goto append;
}
if (key >= KEYC_USER && key < KEYC_USER + KEYC_NUSER) {
snprintf(tmp, sizeof tmp, "User%u", (u_int)(key - KEYC_USER));
strlcat(out, tmp, sizeof out);
return (out);
}
/* Try the key against the string table. */ /* Try the key against the string table. */
for (i = 0; i < nitems(key_string_table); i++) { for (i = 0; i < nitems(key_string_table); i++) {
if (key == key_string_table[i].key) if (key == key_string_table[i].key)
@@ -323,7 +354,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);
} }
@@ -337,9 +368,15 @@ 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);
return (out); return (out);
append:
strlcat(out, s, sizeof out);
return (out);
} }

View File

@@ -60,7 +60,7 @@ layout_checksum(const char *layout)
char * char *
layout_dump(struct layout_cell *root) layout_dump(struct layout_cell *root)
{ {
char layout[BUFSIZ], *out; char layout[8192], *out;
*layout = '\0'; *layout = '\0';
if (layout_append(root, layout, sizeof layout) != 0) if (layout_append(root, layout, sizeof layout) != 0)
@@ -116,13 +116,49 @@ layout_append(struct layout_cell *lc, char *buf, size_t len)
return (0); return (0);
} }
/* Check layout sizes fit. */
static int
layout_check(struct layout_cell *lc)
{
struct layout_cell *lcchild;
u_int n = 0;
switch (lc->type) {
case LAYOUT_WINDOWPANE:
break;
case LAYOUT_LEFTRIGHT:
TAILQ_FOREACH(lcchild, &lc->cells, entry) {
if (lcchild->sy != lc->sy)
return (0);
if (!layout_check(lcchild))
return (0);
n += lcchild->sx + 1;
}
if (n - 1 != lc->sx)
return (0);
break;
case LAYOUT_TOPBOTTOM:
TAILQ_FOREACH(lcchild, &lc->cells, entry) {
if (lcchild->sx != lc->sx)
return (0);
if (!layout_check(lcchild))
return (0);
n += lcchild->sy + 1;
}
if (n - 1 != lc->sy)
return (0);
break;
}
return (1);
}
/* Parse a layout string and arrange window as layout. */ /* Parse a layout string and arrange window as layout. */
int int
layout_parse(struct window *w, const char *layout) layout_parse(struct window *w, const char *layout)
{ {
struct layout_cell *lc, *lcchild; struct layout_cell *lc, *lcchild;
struct window_pane *wp; struct window_pane *wp;
u_int npanes, ncells, sx, sy; u_int npanes, ncells, sx = 0, sy = 0;
u_short csum; u_short csum;
/* Check validity. */ /* Check validity. */
@@ -153,9 +189,39 @@ layout_parse(struct window *w, const char *layout)
layout_destroy_cell(w, lcchild, &lc); layout_destroy_cell(w, lcchild, &lc);
} }
/* Save the old window size and resize to the layout size. */ /*
sx = w->sx; sy = w->sy; * It appears older versions of tmux were able to generate layouts with
window_resize(w, lc->sx, lc->sy); * an incorrect top cell size - if it is larger than the top child then
* correct that (if this is still wrong the check code will catch it).
*/
switch (lc->type) {
case LAYOUT_WINDOWPANE:
break;
case LAYOUT_LEFTRIGHT:
TAILQ_FOREACH(lcchild, &lc->cells, entry) {
sy = lcchild->sy + 1;
sx += lcchild->sx + 1;
}
break;
case LAYOUT_TOPBOTTOM:
TAILQ_FOREACH(lcchild, &lc->cells, entry) {
sx = lcchild->sx + 1;
sy += lcchild->sy + 1;
}
break;
}
if (lc->type != LAYOUT_WINDOWPANE && (lc->sx != sx || lc->sy != sy)) {
log_debug("fix layout %u,%u to %u,%u", lc->sx, lc->sy, sx,sy);
layout_print_cell(lc, __func__, 0);
lc->sx = sx - 1; lc->sy = sy - 1;
}
/* Check the new layout. */
if (!layout_check(lc))
return (-1);
/* Resize to the layout size. */
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);
@@ -166,12 +232,9 @@ layout_parse(struct window *w, const char *layout)
layout_assign(&wp, lc); layout_assign(&wp, lc);
/* Update pane offsets and sizes. */ /* Update pane offsets and sizes. */
layout_fix_offsets(lc); layout_fix_offsets(w);
layout_fix_panes(w); layout_fix_panes(w);
recalculate_sizes();
/* Then resize the layout back to the original window size. */
layout_resize(w, sx, sy);
window_resize(w, sx, sy);
layout_print_cell(lc, __func__, 0); layout_print_cell(lc, __func__, 0);

View File

@@ -158,12 +158,12 @@ layout_set_even(struct window *w, enum layout_type type)
layout_spread_cell(w, lc); layout_spread_cell(w, lc);
/* Fix cell offsets. */ /* Fix cell offsets. */
layout_fix_offsets(lc); layout_fix_offsets(w);
layout_fix_panes(w); layout_fix_panes(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);
} }
@@ -257,12 +257,12 @@ layout_set_main_h(struct window *w)
} }
/* Fix cell offsets. */ /* Fix cell offsets. */
layout_fix_offsets(lc); layout_fix_offsets(w);
layout_fix_panes(w); layout_fix_panes(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);
} }
@@ -344,12 +344,12 @@ layout_set_main_v(struct window *w)
} }
/* Fix cell offsets. */ /* Fix cell offsets. */
layout_fix_offsets(lc); layout_fix_offsets(w);
layout_fix_panes(w); layout_fix_panes(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);
} }
@@ -453,12 +453,12 @@ layout_set_tiled(struct window *w)
} }
/* Fix cell offsets. */ /* Fix cell offsets. */
layout_fix_offsets(lc); layout_fix_offsets(w);
layout_fix_panes(w); layout_fix_panes(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);
} }

141
layout.c
View File

@@ -39,7 +39,6 @@ static int layout_resize_pane_grow(struct window *, struct layout_cell *,
enum layout_type, int, int); enum layout_type, int, int);
static int layout_resize_pane_shrink(struct window *, struct layout_cell *, static int layout_resize_pane_shrink(struct window *, struct layout_cell *,
enum layout_type, int); enum layout_type, int);
static int layout_need_status(struct layout_cell *, int);
static u_int layout_new_pane_size(struct window *, u_int, static u_int layout_new_pane_size(struct window *, u_int,
struct layout_cell *, enum layout_type, u_int, u_int, struct layout_cell *, enum layout_type, u_int, u_int,
u_int); u_int);
@@ -199,9 +198,9 @@ layout_make_node(struct layout_cell *lc, enum layout_type type)
lc->wp = NULL; lc->wp = NULL;
} }
/* Fix cell offsets based on their sizes. */ /* Fix cell offsets for a child cell. */
void static void
layout_fix_offsets(struct layout_cell *lc) layout_fix_offsets1(struct layout_cell *lc)
{ {
struct layout_cell *lcchild; struct layout_cell *lcchild;
u_int xoff, yoff; u_int xoff, yoff;
@@ -212,7 +211,7 @@ layout_fix_offsets(struct layout_cell *lc)
lcchild->xoff = xoff; lcchild->xoff = xoff;
lcchild->yoff = lc->yoff; lcchild->yoff = lc->yoff;
if (lcchild->type != LAYOUT_WINDOWPANE) if (lcchild->type != LAYOUT_WINDOWPANE)
layout_fix_offsets(lcchild); layout_fix_offsets1(lcchild);
xoff += lcchild->sx + 1; xoff += lcchild->sx + 1;
} }
} else { } else {
@@ -221,61 +220,92 @@ layout_fix_offsets(struct layout_cell *lc)
lcchild->xoff = lc->xoff; lcchild->xoff = lc->xoff;
lcchild->yoff = yoff; lcchild->yoff = yoff;
if (lcchild->type != LAYOUT_WINDOWPANE) if (lcchild->type != LAYOUT_WINDOWPANE)
layout_fix_offsets(lcchild); layout_fix_offsets1(lcchild);
yoff += lcchild->sy + 1; yoff += lcchild->sy + 1;
} }
} }
} }
/* /* Update cell offsets based on their sizes. */
* Returns 1 if we need to reserve space for the pane status line. This is the void
* case for the most upper panes only. layout_fix_offsets(struct window *w)
*/
static int
layout_need_status(struct layout_cell *lc, int at_top)
{ {
struct layout_cell *first_lc; struct layout_cell *lc = w->layout_root;
if (lc->parent != NULL) { lc->xoff = 0;
if (lc->parent->type == LAYOUT_LEFTRIGHT) lc->yoff = 0;
return (layout_need_status(lc->parent, at_top));
if (at_top) layout_fix_offsets1(lc);
first_lc = TAILQ_FIRST(&lc->parent->cells); }
else
first_lc = TAILQ_LAST(&lc->parent->cells,layout_cells); /* Is this a top cell? */
if (lc == first_lc) static int
return (layout_need_status(lc->parent, at_top)); layout_cell_is_top(struct window *w, struct layout_cell *lc)
return (0); {
struct layout_cell *next;
while (lc != w->layout_root) {
next = lc->parent;
if (next->type == LAYOUT_TOPBOTTOM &&
lc != TAILQ_FIRST(&next->cells))
return (0);
lc = next;
} }
return (1); return (1);
} }
/* Is this a bottom cell? */
static int
layout_cell_is_bottom(struct window *w, struct layout_cell *lc)
{
struct layout_cell *next;
while (lc != w->layout_root) {
next = lc->parent;
if (next->type == LAYOUT_TOPBOTTOM &&
lc != TAILQ_LAST(&next->cells, layout_cells))
return (0);
lc = next;
}
return (1);
}
/*
* Returns 1 if we need to add an extra line for the pane status line. This is
* the case for the most upper or lower panes only.
*/
static int
layout_add_border(struct window *w, struct layout_cell *lc, int status)
{
if (status == PANE_STATUS_TOP)
return (layout_cell_is_top(w, lc));
if (status == PANE_STATUS_BOTTOM)
return (layout_cell_is_bottom(w, lc));
return (0);
}
/* Update pane offsets and sizes based on their cells. */ /* Update pane offsets and sizes based on their cells. */
void void
layout_fix_panes(struct window *w) layout_fix_panes(struct window *w)
{ {
struct window_pane *wp; struct window_pane *wp;
struct layout_cell *lc; struct layout_cell *lc;
int shift, status; int status;
status = options_get_number(w->options, "pane-border-status"); status = options_get_number(w->options, "pane-border-status");
TAILQ_FOREACH(wp, &w->panes, entry) { TAILQ_FOREACH(wp, &w->panes, entry) {
if ((lc = wp->layout_cell) == NULL) if ((lc = wp->layout_cell) == NULL)
continue; continue;
if (status != 0)
shift = layout_need_status(lc, status == 1);
else
shift = 0;
wp->xoff = lc->xoff; wp->xoff = lc->xoff;
wp->yoff = lc->yoff; wp->yoff = lc->yoff;
if (shift && status == 1) if (layout_add_border(w, lc, status)) {
wp->yoff += 1; if (status == PANE_STATUS_TOP)
wp->yoff++;
window_pane_resize(wp, lc->sx, lc->sy - shift); window_pane_resize(wp, lc->sx, lc->sy - 1);
} else
window_pane_resize(wp, lc->sx, lc->sy);
} }
} }
@@ -312,13 +342,15 @@ layout_resize_check(struct window *w, struct layout_cell *lc,
status = options_get_number(w->options, "pane-border-status"); status = options_get_number(w->options, "pane-border-status");
if (lc->type == LAYOUT_WINDOWPANE) { if (lc->type == LAYOUT_WINDOWPANE) {
/* Space available in this cell only. */ /* Space available in this cell only. */
minimum = PANE_MINIMUM; if (type == LAYOUT_LEFTRIGHT) {
if (type == LAYOUT_LEFTRIGHT)
available = lc->sx; available = lc->sx;
else { minimum = PANE_MINIMUM;
} else {
available = lc->sy; available = lc->sy;
if (status != 0) if (layout_add_border(w, lc, status))
minimum += layout_need_status(lc, status == 1); minimum = PANE_MINIMUM + 1;
else
minimum = PANE_MINIMUM;
} }
if (available > minimum) if (available > minimum)
available -= minimum; available -= minimum;
@@ -507,7 +539,7 @@ layout_resize(struct window *w, u_int sx, u_int sy)
layout_resize_adjust(w, lc, LAYOUT_TOPBOTTOM, ychange); layout_resize_adjust(w, lc, LAYOUT_TOPBOTTOM, ychange);
/* Fix cell offsets. */ /* Fix cell offsets. */
layout_fix_offsets(lc); layout_fix_offsets(w);
layout_fix_panes(w); layout_fix_panes(w);
} }
@@ -567,7 +599,7 @@ layout_resize_layout(struct window *w, struct layout_cell *lc,
} }
/* Fix cell offsets. */ /* Fix cell offsets. */
layout_fix_offsets(w->layout_root); layout_fix_offsets(w);
layout_fix_panes(w); layout_fix_panes(w);
notify_window("window-layout-changed", w); notify_window("window-layout-changed", w);
} }
@@ -861,9 +893,10 @@ layout_split_pane(struct window_pane *wp, enum layout_type type, int size,
return (NULL); return (NULL);
break; break;
case LAYOUT_TOPBOTTOM: case LAYOUT_TOPBOTTOM:
minimum = PANE_MINIMUM * 2 + 1; if (layout_add_border(wp->window, lc, status))
if (status != 0) minimum = PANE_MINIMUM * 2 + 2;
minimum += layout_need_status(lc, status == 1); else
minimum = PANE_MINIMUM * 2 + 1;
if (sy < minimum) if (sy < minimum)
return (NULL); return (NULL);
break; break;
@@ -988,7 +1021,7 @@ layout_split_pane(struct window_pane *wp, enum layout_type type, int size,
if (full_size) { if (full_size) {
if (!resize_first) if (!resize_first)
layout_resize_child_cells(wp->window, lc); layout_resize_child_cells(wp->window, lc);
layout_fix_offsets(wp->window->layout_root); layout_fix_offsets(wp->window);
} else } else
layout_make_leaf(lc, wp); layout_make_leaf(lc, wp);
@@ -1006,7 +1039,7 @@ layout_close_pane(struct window_pane *wp)
/* Fix pane offsets and sizes. */ /* Fix pane offsets and sizes. */
if (w->layout_root != NULL) { if (w->layout_root != NULL) {
layout_fix_offsets(w->layout_root); layout_fix_offsets(w);
layout_fix_panes(w); layout_fix_panes(w);
} }
notify_window("window-layout-changed", w); notify_window("window-layout-changed", w);
@@ -1021,7 +1054,7 @@ layout_spread_cell(struct window *w, struct layout_cell *parent)
number = 0; number = 0;
TAILQ_FOREACH (lc, &parent->cells, entry) TAILQ_FOREACH (lc, &parent->cells, entry)
number++; number++;
if (number <= 1) if (number <= 1)
return (0); return (0);
status = options_get_number(w->options, "pane-border-status"); status = options_get_number(w->options, "pane-border-status");
@@ -1029,9 +1062,10 @@ layout_spread_cell(struct window *w, struct layout_cell *parent)
if (parent->type == LAYOUT_LEFTRIGHT) if (parent->type == LAYOUT_LEFTRIGHT)
size = parent->sx; size = parent->sx;
else if (parent->type == LAYOUT_TOPBOTTOM) { else if (parent->type == LAYOUT_TOPBOTTOM) {
size = parent->sy; if (layout_add_border(w, parent, status))
if (status != 0) size = parent->sy - 1;
size -= layout_need_status(parent, status == 1); else
size = parent->sy;
} else } else
return (0); return (0);
if (size < number - 1) if (size < number - 1)
@@ -1049,9 +1083,10 @@ layout_spread_cell(struct window *w, struct layout_cell *parent)
change = each - (int)lc->sx; change = each - (int)lc->sx;
layout_resize_adjust(w, lc, LAYOUT_LEFTRIGHT, change); layout_resize_adjust(w, lc, LAYOUT_LEFTRIGHT, change);
} else if (parent->type == LAYOUT_TOPBOTTOM) { } else if (parent->type == LAYOUT_TOPBOTTOM) {
this = each; if (layout_add_border(w, lc, status))
if (status != 0) this = each + 1;
this += layout_need_status(lc, status == 1); else
this = each;
change = this - (int)lc->sy; change = this - (int)lc->sy;
layout_resize_adjust(w, lc, LAYOUT_TOPBOTTOM, change); layout_resize_adjust(w, lc, LAYOUT_TOPBOTTOM, change);
} }
@@ -1073,7 +1108,7 @@ layout_spread_out(struct window_pane *wp)
do { do {
if (layout_spread_cell(w, parent)) { if (layout_spread_cell(w, parent)) {
layout_fix_offsets(parent); layout_fix_offsets(w);
layout_fix_panes(w); layout_fix_panes(w);
break; break;
} }

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

27
menu.c
View File

@@ -161,7 +161,7 @@ menu_free_cb(struct client *c)
struct menu_data *md = c->overlay_data; struct menu_data *md = c->overlay_data;
if (md->item != NULL) if (md->item != NULL)
md->item->flags &= ~CMDQ_WAITING; cmdq_continue(md->item);
if (md->cb != NULL) if (md->cb != NULL)
md->cb(md->menu, UINT_MAX, KEYC_NONE, md->data); md->cb(md->menu, UINT_MAX, KEYC_NONE, md->data);
@@ -185,8 +185,11 @@ menu_key_cb(struct client *c, struct key_event *event)
const char *name; const char *name;
if (KEYC_IS_MOUSE(event->key)) { if (KEYC_IS_MOUSE(event->key)) {
if (md->flags & MENU_NOMOUSE) if (md->flags & MENU_NOMOUSE) {
if (MOUSE_BUTTONS(m->b) != 0)
return (1);
return (0); return (0);
}
if (m->x < md->px || if (m->x < md->px ||
m->x > md->px + 4 + menu->width || m->x > md->px + 4 + menu->width ||
m->y < md->py + 1 || m->y < md->py + 1 ||
@@ -206,8 +209,18 @@ menu_key_cb(struct client *c, struct key_event *event)
c->flags |= CLIENT_REDRAWOVERLAY; c->flags |= CLIENT_REDRAWOVERLAY;
return (0); return (0);
} }
for (i = 0; i < (u_int)count; i++) {
name = menu->items[i].name;
if (name == NULL || *name == '-')
continue;
if (event->key == menu->items[i].key) {
md->choice = i;
goto chosen;
}
}
switch (event->key) { switch (event->key) {
case KEYC_UP: case KEYC_UP:
case 'k':
if (old == -1) if (old == -1)
old = 0; old = 0;
do { do {
@@ -220,6 +233,7 @@ menu_key_cb(struct client *c, struct key_event *event)
c->flags |= CLIENT_REDRAWOVERLAY; c->flags |= CLIENT_REDRAWOVERLAY;
return (0); return (0);
case KEYC_DOWN: case KEYC_DOWN:
case 'j':
if (old == -1) if (old == -1)
old = 0; old = 0;
do { do {
@@ -239,15 +253,6 @@ menu_key_cb(struct client *c, struct key_event *event)
case 'q': case 'q':
return (1); return (1);
} }
for (i = 0; i < (u_int)count; i++) {
name = menu->items[i].name;
if (name == NULL || *name == '-')
continue;
if (event->key == menu->items[i].key) {
md->choice = i;
goto chosen;
}
}
return (0); return (0);
chosen: chosen:

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);
@@ -480,7 +480,7 @@ mode_tree_add(struct mode_tree_data *mtd, struct mode_tree_item *parent,
saved = mode_tree_find_item(&mtd->saved, tag); saved = mode_tree_find_item(&mtd->saved, tag);
if (saved != NULL) { if (saved != NULL) {
if (parent == NULL || (parent != NULL && parent->expanded)) if (parent == NULL || parent->expanded)
mti->tagged = saved->tagged; mti->tagged = saved->tagged;
mti->expanded = saved->expanded; mti->expanded = saved->expanded;
} else if (expanded == -1) } else if (expanded == -1)
@@ -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);
@@ -933,6 +936,7 @@ mode_tree_key(struct mode_tree_data *mtd, struct client *c, key_code *key,
case '\016': /* C-n */ case '\016': /* C-n */
mode_tree_down(mtd, 1); mode_tree_down(mtd, 1);
break; break;
case 'g':
case KEYC_PPAGE: case KEYC_PPAGE:
case '\002': /* C-b */ case '\002': /* C-b */
for (i = 0; i < mtd->height; i++) { for (i = 0; i < mtd->height; i++) {
@@ -941,6 +945,7 @@ mode_tree_key(struct mode_tree_data *mtd, struct client *c, key_code *key,
mode_tree_up(mtd, 1); mode_tree_up(mtd, 1);
} }
break; break;
case 'G':
case KEYC_NPAGE: case KEYC_NPAGE:
case '\006': /* C-f */ case '\006': /* C-f */
for (i = 0; i < mtd->height; i++) { for (i = 0; i < mtd->height; i++) {
@@ -991,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:
@@ -1019,6 +1028,8 @@ mode_tree_key(struct mode_tree_data *mtd, struct client *c, key_code *key,
mode_tree_build(mtd); mode_tree_build(mtd);
} }
break; break;
case '?':
case '/':
case '\023': /* C-s */ case '\023': /* C-s */
mtd->references++; mtd->references++;
status_prompt_set(c, "(search) ", "", status_prompt_set(c, "(search) ", "",

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,
@@ -562,13 +578,13 @@ const struct options_table_entry options_table[] = {
{ .name = "allow-rename", { .name = "allow-rename",
.type = OPTIONS_TABLE_FLAG, .type = OPTIONS_TABLE_FLAG,
.scope = OPTIONS_TABLE_WINDOW, .scope = OPTIONS_TABLE_WINDOW|OPTIONS_TABLE_PANE,
.default_num = 0 .default_num = 0
}, },
{ .name = "alternate-screen", { .name = "alternate-screen",
.type = OPTIONS_TABLE_FLAG, .type = OPTIONS_TABLE_FLAG,
.scope = OPTIONS_TABLE_WINDOW, .scope = OPTIONS_TABLE_WINDOW|OPTIONS_TABLE_PANE,
.default_num = 1 .default_num = 1
}, },
@@ -688,7 +704,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_pane_status_list, .choices = options_table_pane_status_list,
.default_num = 0 .default_num = PANE_STATUS_OFF
}, },
{ .name = "pane-border-style", { .name = "pane-border-style",
@@ -699,7 +715,7 @@ const struct options_table_entry options_table[] = {
{ .name = "remain-on-exit", { .name = "remain-on-exit",
.type = OPTIONS_TABLE_FLAG, .type = OPTIONS_TABLE_FLAG,
.scope = OPTIONS_TABLE_WINDOW, .scope = OPTIONS_TABLE_WINDOW|OPTIONS_TABLE_PANE,
.default_num = 0 .default_num = 0
}, },
@@ -711,7 +727,7 @@ const struct options_table_entry options_table[] = {
{ .name = "window-active-style", { .name = "window-active-style",
.type = OPTIONS_TABLE_STYLE, .type = OPTIONS_TABLE_STYLE,
.scope = OPTIONS_TABLE_WINDOW, .scope = OPTIONS_TABLE_WINDOW|OPTIONS_TABLE_PANE,
.default_str = "default" .default_str = "default"
}, },
@@ -719,12 +735,12 @@ 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",
.type = OPTIONS_TABLE_STYLE, .type = OPTIONS_TABLE_STYLE,
.scope = OPTIONS_TABLE_WINDOW, .scope = OPTIONS_TABLE_WINDOW|OPTIONS_TABLE_PANE,
.default_str = "default" .default_str = "default"
}, },
@@ -794,6 +810,7 @@ const struct options_table_entry options_table[] = {
OPTIONS_TABLE_HOOK("after-copy-mode", ""), OPTIONS_TABLE_HOOK("after-copy-mode", ""),
OPTIONS_TABLE_HOOK("after-display-message", ""), OPTIONS_TABLE_HOOK("after-display-message", ""),
OPTIONS_TABLE_HOOK("after-display-panes", ""), OPTIONS_TABLE_HOOK("after-display-panes", ""),
OPTIONS_TABLE_HOOK("after-kill-pane", ""),
OPTIONS_TABLE_HOOK("after-list-buffers", ""), OPTIONS_TABLE_HOOK("after-list-buffers", ""),
OPTIONS_TABLE_HOOK("after-list-clients", ""), OPTIONS_TABLE_HOOK("after-list-clients", ""),
OPTIONS_TABLE_HOOK("after-list-keys", ""), OPTIONS_TABLE_HOOK("after-list-keys", ""),

161
options.c
View File

@@ -100,7 +100,7 @@ options_parent_table_entry(struct options *oo, const char *s)
if (oo->parent == NULL) if (oo->parent == NULL)
fatalx("no parent options for %s", s); fatalx("no parent options for %s", s);
o = options_get_only(oo->parent, s); o = options_get(oo->parent, s);
if (o == NULL) if (o == NULL)
fatalx("%s not in parent options", s); fatalx("%s not in parent options", s);
return (o->tableentry); return (o->tableentry);
@@ -178,6 +178,12 @@ options_free(struct options *oo)
free(oo); free(oo);
} }
void
options_set_parent(struct options *oo, struct options *parent)
{
oo->parent = parent;
}
struct options_entry * struct options_entry *
options_first(struct options *oo) options_first(struct options *oo)
{ {
@@ -290,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);
} }
@@ -314,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)
{ {
@@ -361,11 +379,19 @@ 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:
*cause = xstrdup("empty command"); if (cause != NULL)
*cause = xstrdup("empty command");
return (-1); return (-1);
case CMD_PARSE_ERROR: case CMD_PARSE_ERROR:
if (cause != NULL) if (cause != NULL)
@@ -376,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
@@ -544,7 +569,7 @@ options_parse_get(struct options *oo, const char *s, int *idx, int only)
} }
char * char *
options_match(const char *s, int *idx, int* ambiguous) options_match(const char *s, int *idx, int *ambiguous)
{ {
const struct options_table_entry *oe, *found; const struct options_table_entry *oe, *found;
char *name; char *name;
@@ -585,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;
@@ -724,20 +749,102 @@ options_set_style(struct options *oo, const char *name, int append,
return (o); return (o);
} }
enum options_table_scope int
options_scope_from_name(struct args *args, int window,
const char *name, struct cmd_find_state *fs, struct options **oo,
char **cause)
{
struct session *s = fs->s;
struct winlink *wl = fs->wl;
struct window_pane *wp = fs->wp;
const char *target = args_get(args, 't');
const struct options_table_entry *oe;
int scope = OPTIONS_TABLE_NONE;
if (*name == '@')
return (options_scope_from_flags(args, window, fs, oo, cause));
for (oe = options_table; oe->name != NULL; oe++) {
if (strcmp(oe->name, name) == 0)
break;
}
if (oe->name == NULL) {
xasprintf(cause, "unknown option: %s", name);
return (OPTIONS_TABLE_NONE);
}
switch (oe->scope) {
case OPTIONS_TABLE_SERVER:
*oo = global_options;
scope = OPTIONS_TABLE_SERVER;
break;
case OPTIONS_TABLE_SESSION:
if (args_has(args, 'g')) {
*oo = global_s_options;
scope = OPTIONS_TABLE_SESSION;
} else if (s == NULL && target != NULL)
xasprintf(cause, "no such session: %s", target);
else if (s == NULL)
xasprintf(cause, "no current session");
else {
*oo = s->options;
scope = OPTIONS_TABLE_SESSION;
}
break;
case OPTIONS_TABLE_WINDOW|OPTIONS_TABLE_PANE:
if (args_has(args, 'p')) {
if (wp == NULL && target != NULL)
xasprintf(cause, "no such pane: %s", target);
else if (wp == NULL)
xasprintf(cause, "no current pane");
else {
*oo = wp->options;
scope = OPTIONS_TABLE_PANE;
}
break;
}
/* FALLTHROUGH */
case OPTIONS_TABLE_WINDOW:
if (args_has(args, 'g')) {
*oo = global_w_options;
scope = OPTIONS_TABLE_WINDOW;
} else if (wl == NULL && target != NULL)
xasprintf(cause, "no such window: %s", target);
else if (wl == NULL)
xasprintf(cause, "no current window");
else {
*oo = wl->window->options;
scope = OPTIONS_TABLE_WINDOW;
}
break;
}
return (scope);
}
int
options_scope_from_flags(struct args *args, int window, options_scope_from_flags(struct args *args, int window,
struct cmd_find_state *fs, struct options **oo, char **cause) struct cmd_find_state *fs, struct options **oo, char **cause)
{ {
struct session *s = fs->s; struct session *s = fs->s;
struct winlink *wl = fs->wl; struct winlink *wl = fs->wl;
const char *target= args_get(args, 't'); struct window_pane *wp = fs->wp;
const char *target = args_get(args, 't');
if (args_has(args, 's')) { if (args_has(args, 's')) {
*oo = global_options; *oo = global_options;
return (OPTIONS_TABLE_SERVER); return (OPTIONS_TABLE_SERVER);
} }
if (window || args_has(args, 'w')) { if (args_has(args, 'p')) {
if (wp == NULL) {
if (target != NULL)
xasprintf(cause, "no such pane: %s", target);
else
xasprintf(cause, "no current pane");
return (OPTIONS_TABLE_NONE);
}
*oo = wp->options;
return (OPTIONS_TABLE_PANE);
} else if (window || args_has(args, 'w')) {
if (args_has(args, 'g')) { if (args_has(args, 'g')) {
*oo = global_w_options; *oo = global_w_options;
return (OPTIONS_TABLE_WINDOW); return (OPTIONS_TABLE_WINDOW);

View File

@@ -133,13 +133,27 @@ char *
osdep_get_cwd(int fd) osdep_get_cwd(int fd)
{ {
static char target[PATH_MAX + 1]; static char target[PATH_MAX + 1];
char *path;
pid_t pgrp; pid_t pgrp;
#ifdef KERN_PROC_CWD
int mib[4];
size_t len;
#else
char *path;
ssize_t n; ssize_t n;
#endif
if ((pgrp = tcgetpgrp(fd)) == -1) if ((pgrp = tcgetpgrp(fd)) == -1)
return (NULL); return (NULL);
#ifdef KERN_PROC_CWD
mib[0] = CTL_KERN;
mib[1] = KERN_PROC_ARGS;
mib[2] = pgrp;
mib[3] = KERN_PROC_CWD;
len = sizeof(target);
if (sysctl(mib, __arraycount(mib), target, &len, NULL, 0) == 0)
return (target);
#else
xasprintf(&path, "/proc/%lld/cwd", (long long) pgrp); xasprintf(&path, "/proc/%lld/cwd", (long long) pgrp);
n = readlink(path, target, sizeof(target) - 1); n = readlink(path, target, sizeof(target) - 1);
free(path); free(path);
@@ -147,6 +161,7 @@ osdep_get_cwd(int fd)
target[n] = '\0'; target[n] = '\0';
return (target); return (target);
} }
#endif
return (NULL); return (NULL);
} }

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

@@ -1,7 +1,5 @@
#!/bin/sh #!/bin/sh
# new-session without clients should be the right size
PATH=/bin:/usr/bin PATH=/bin:/usr/bin
TERM=screen TERM=screen
@@ -19,6 +17,7 @@ EOF
$TMUX -f$TMP start </dev/null || exit 1 $TMUX -f$TMP start </dev/null || exit 1
sleep 1 sleep 1
$TMUX lsw -aF '#{session_name},#{window_name}'|sort >$TMP || exit 1 $TMUX lsw -aF '#{session_name},#{window_name}'|sort >$TMP || exit 1
$TMUX kill-server 2>/dev/null
cat <<EOF|cmp -s $TMP - || exit 1 cat <<EOF|cmp -s $TMP - || exit 1
bar,bar0 bar,bar0
bar,bar1 bar,bar1
@@ -27,7 +26,6 @@ foo,foo0
foo,foo1 foo,foo1
foo,foo2 foo,foo2
EOF EOF
$TMUX kill-server 2>/dev/null
cat <<EOF >$TMP cat <<EOF >$TMP
new -sfoo -nfoo0 new -sfoo -nfoo0
@@ -40,6 +38,7 @@ EOF
$TMUX -f$TMP start </dev/null || exit 1 $TMUX -f$TMP start </dev/null || exit 1
sleep 1 sleep 1
$TMUX lsw -aF '#{session_name},#{window_name}'|sort >$TMP || exit 1 $TMUX lsw -aF '#{session_name},#{window_name}'|sort >$TMP || exit 1
$TMUX kill-server 2>/dev/null
cat <<EOF|cmp -s $TMP - || exit 1 cat <<EOF|cmp -s $TMP - || exit 1
bar,bar0 bar,bar0
bar,bar1 bar,bar1
@@ -48,6 +47,5 @@ foo,foo0
foo,foo1 foo,foo1
foo,foo2 foo,foo2
EOF EOF
$TMUX kill-server 2>/dev/null
exit 0 exit 0

View File

@@ -0,0 +1,8 @@
%if #{l:1}
set -g status-style fg=cyan,bg='#001040'
%elif #{l:1}
set -g status-style fg=white,bg='#400040'
%else
set -g status-style fg=white,bg='#800000'
%endif
bind ^X last-window

View File

@@ -0,0 +1,93 @@
# -----------------------------------------------------------------------------
# This config is targeted for tmux 2.1+ and should be placed in $HOME.
#
# Read the "Plugin Manager" section (bottom) before trying to use this config!
# -----------------------------------------------------------------------------
# -----------------------------------------------------------------------------
# Global options
# -----------------------------------------------------------------------------
# Set a new prefix / leader key.
set -g prefix `
bind ` send-prefix
# Allow opening multiple terminals to view the same session at different sizes.
setw -g aggressive-resize on
# Remove delay when switching between Vim modes.
set -s escape-time 0
# Allow Vim's FocusGained to work when your terminal gains focus.
# Requires Vim plugin: https://github.com/tmux-plugins/vim-tmux-focus-events
set -g focus-events on
# Add a bit more scroll history in the buffer.
set -g history-limit 50000
# Enable color support inside of tmux.
set -g default-terminal "screen-256color"
# Ensure window titles get renamed automatically.
setw -g automatic-rename
# Start windows and panes index at 1, not 0.
set -g base-index 1
setw -g pane-base-index 1
# Enable full mouse support.
set -g mouse on
# Status bar optimized for Gruvbox.
set -g status-fg colour244
set -g status-bg default
set -g status-left ''
set -g status-right-length 0
#set -g status-right-length 20
#set -g status-right '%a %Y-%m-%d %H:%M'
set -g pane-border-fg default
set -g pane-border-bg default
set -g pane-active-border-fg colour250
set -g pane-active-border-bg default
set-window-option -g window-status-current-attr bold
set-window-option -g window-status-current-fg colour223
# -----------------------------------------------------------------------------
# Key bindings
# -----------------------------------------------------------------------------
# Unbind default keys
unbind C-b
unbind '"'
unbind %
# Reload the tmux config.
bind-key r source-file ~/.tmux.conf
# Split panes.
bind-key h split-window -v
bind-key v split-window -h
# Move around panes with ALT + arrow keys.
bind-key -n M-Up select-pane -U
bind-key -n M-Left select-pane -L
bind-key -n M-Down select-pane -D
bind-key -n M-Right select-pane -R
# -----------------------------------------------------------------------------
# Plugin Manager - https://github.com/tmux-plugins/tpm
# In order to use the plugins below you need to install TPM and the plugins.
# Step 1) git clone https://github.com/tmux-plugins/tpm ~/.tmux/plugins/tpm
# Step 2) Reload tmux if it's already started with `r
# Step 3) Launch tmux and hit `I (capital i) to fetch any plugins
# -----------------------------------------------------------------------------
# List of plugins.
set -g @plugin 'tmux-plugins/tpm'
set -g @plugin 'tmux-plugins/tmux-resurrect'
set -g @plugin 'tmux-plugins/tmux-yank'
# Initialize TPM (keep this line at the very bottom of your tmux.conf).
run -b '~/.tmux/plugins/tpm/tpm'

29
regress/xenl-terminal.sh Normal file
View File

@@ -0,0 +1,29 @@
#!/bin/sh
PATH=/bin:/usr/bin
TERM=screen
[ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux)
TMUX="$TEST_TMUX -Ltest"
$TMUX kill-server 2>/dev/null
TMUX2="$TEST_TMUX -Ltest2"
$TMUX2 kill-server 2>/dev/null
TMP=$(mktemp)
trap "rm -f $TMP" 0 1 15
$TMUX2 -f/dev/null new -d || exit 1
$TMUX2 set -as terminal-overrides ',*:xenl@' || exit 1
$TMUX2 set -g status-right 'RRR' || exit 1
$TMUX2 set -g status-left 'LLL' || exit 1
$TMUX2 set -g window-status-current-format 'WWW' || exit 1
$TMUX -f/dev/null new -x20 -y2 -d "$TMUX2 attach" || exit 1
sleep 1
$TMUX capturep -p|tail -1 >$TMP || exit 1
$TMUX kill-server 2>/dev/null
$TMUX2 kill-server 2>/dev/null
cat <<EOF|cmp -s $TMP - || exit 1
LLLWWW RR
EOF
exit 0

121
regsub.c Normal file
View File

@@ -0,0 +1,121 @@
/* $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 <regex.h>
#include <string.h>
#include "tmux.h"
static void
regsub_copy(char **buf, size_t *len, const char *text, size_t start,
size_t end)
{
size_t add = end - start;
*buf = xrealloc(*buf, (*len) + add + 1);
memcpy((*buf) + *len, text + start, add);
(*len) += add;
}
static void
regsub_expand(char **buf, size_t *len, const char *with, const char *text,
regmatch_t *m, u_int n)
{
const char *cp;
u_int i;
for (cp = with; *cp != '\0'; cp++) {
if (*cp == '\\') {
cp++;
if (*cp >= '0' && *cp <= '9') {
i = *cp - '0';
if (i < n && m[i].rm_so != m[i].rm_eo) {
regsub_copy(buf, len, text, m[i].rm_so,
m[i].rm_eo);
continue;
}
}
}
*buf = xrealloc(*buf, (*len) + 2);
(*buf)[(*len)++] = *cp;
}
}
char *
regsub(const char *pattern, const char *with, const char *text, int flags)
{
regex_t r;
regmatch_t m[10];
ssize_t start, end, last, len = 0;
int empty = 0;
char *buf = NULL;
if (*text == '\0')
return (xstrdup(""));
if (regcomp(&r, pattern, flags) != 0)
return (NULL);
start = 0;
last = 0;
end = strlen(text);
while (start <= end) {
if (regexec(&r, text + start, nitems(m), m, 0) != 0) {
regsub_copy(&buf, &len, text, start, end);
break;
}
/*
* Append any text not part of this match (from the end of the
* last match).
*/
regsub_copy(&buf, &len, text, last, m[0].rm_so + start);
/*
* If the last match was empty and this one isn't (it is either
* later or has matched text), expand this match. If it is
* empty, move on one character and try again from there.
*/
if (empty ||
start + m[0].rm_so != last ||
m[0].rm_so != m[0].rm_eo) {
regsub_expand(&buf, &len, with, text + start, m,
nitems(m));
last = start + m[0].rm_eo;
start += m[0].rm_eo;
empty = 0;
} else {
last = start + m[0].rm_eo;
start += m[0].rm_eo + 1;
empty = 1;
}
/* Stop now if anchored to start. */
if (*pattern == '^') {
regsub_copy(&buf, &len, text, start, end);
break;
}
}
buf[len] = '\0';
regfree(&r);
return (buf);
}

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

@@ -45,10 +45,6 @@ static void screen_redraw_draw_pane(struct screen_redraw_ctx *,
#define CELL_BORDERS " xqlkmjwvtun~" #define CELL_BORDERS " xqlkmjwvtun~"
#define CELL_STATUS_OFF 0
#define CELL_STATUS_TOP 1
#define CELL_STATUS_BOTTOM 2
/* Check if cell is on the border of a particular pane. */ /* Check if cell is on the border of a particular pane. */
static int static int
screen_redraw_cell_border1(struct window_pane *wp, u_int px, u_int py) screen_redraw_cell_border1(struct window_pane *wp, u_int px, u_int py)
@@ -112,12 +108,12 @@ screen_redraw_check_cell(struct client *c, u_int px, u_int py, int pane_status,
if (px > w->sx || py > w->sy) if (px > w->sx || py > w->sy)
return (CELL_OUTSIDE); return (CELL_OUTSIDE);
if (pane_status != CELL_STATUS_OFF) { if (pane_status != PANE_STATUS_OFF) {
TAILQ_FOREACH(wp, &w->panes, entry) { TAILQ_FOREACH(wp, &w->panes, entry) {
if (!window_pane_visible(wp)) if (!window_pane_visible(wp))
continue; continue;
if (pane_status == CELL_STATUS_TOP) if (pane_status == PANE_STATUS_TOP)
line = wp->yoff - 1; line = wp->yoff - 1;
else else
line = wp->yoff + wp->sy; line = wp->yoff + wp->sy;
@@ -153,7 +149,7 @@ screen_redraw_check_cell(struct client *c, u_int px, u_int py, int pane_status,
borders |= 8; borders |= 8;
if (px <= w->sx && screen_redraw_cell_border(c, px + 1, py)) if (px <= w->sx && screen_redraw_cell_border(c, px + 1, py))
borders |= 4; borders |= 4;
if (pane_status == CELL_STATUS_TOP) { if (pane_status == PANE_STATUS_TOP) {
if (py != 0 && screen_redraw_cell_border(c, px, py - 1)) if (py != 0 && screen_redraw_cell_border(c, px, py - 1))
borders |= 2; borders |= 2;
} else { } else {
@@ -208,9 +204,9 @@ screen_redraw_check_is(u_int px, u_int py, int type, int pane_status,
border = screen_redraw_cell_border1(wantwp, px, py); border = screen_redraw_cell_border1(wantwp, px, py);
if (border == 0 || border == -1) if (border == 0 || border == -1)
return (0); return (0);
if (pane_status == CELL_STATUS_TOP && border == 4) if (pane_status == PANE_STATUS_TOP && border == 4)
return (0); return (0);
if (pane_status == CELL_STATUS_BOTTOM && border == 3) if (pane_status == PANE_STATUS_BOTTOM && border == 3)
return (0); return (0);
/* If there are more than two panes, that's enough. */ /* If there are more than two panes, that's enough. */
@@ -222,7 +218,7 @@ screen_redraw_check_is(u_int px, u_int py, int type, int pane_status,
return (1); return (1);
/* With status lines mark the entire line. */ /* With status lines mark the entire line. */
if (pane_status != CELL_STATUS_OFF) if (pane_status != PANE_STATUS_OFF)
return (1); return (1);
/* Check if the pane covers the whole width. */ /* Check if the pane covers the whole width. */
@@ -270,7 +266,7 @@ screen_redraw_make_pane_status(struct client *c, struct window *w,
fmt = options_get_string(w->options, "pane-border-format"); fmt = options_get_string(w->options, "pane-border-format");
ft = format_create(c, NULL, FORMAT_PANE|wp->id, 0); ft = format_create(c, NULL, FORMAT_PANE|wp->id, FORMAT_STATUS);
format_defaults(ft, c, NULL, NULL, wp); format_defaults(ft, c, NULL, NULL, wp);
expanded = format_expand_time(ft, fmt); expanded = format_expand_time(ft, fmt);
@@ -324,7 +320,7 @@ screen_redraw_draw_pane_status(struct screen_redraw_ctx *ctx)
s = &wp->status_screen; s = &wp->status_screen;
size = wp->status_size; size = wp->status_size;
if (ctx->pane_status == CELL_STATUS_TOP) if (ctx->pane_status == PANE_STATUS_TOP)
yoff = wp->yoff - 1; yoff = wp->yoff - 1;
else else
yoff = wp->yoff + wp->sy; yoff = wp->yoff + wp->sy;
@@ -386,7 +382,7 @@ screen_redraw_update(struct client *c, int flags)
if (c->overlay_draw != NULL) if (c->overlay_draw != NULL)
flags |= CLIENT_REDRAWOVERLAY; flags |= CLIENT_REDRAWOVERLAY;
if (options_get_number(wo, "pane-border-status") != CELL_STATUS_OFF) { if (options_get_number(wo, "pane-border-status") != PANE_STATUS_OFF) {
redraw = 0; redraw = 0;
TAILQ_FOREACH(wp, &w->panes, entry) { TAILQ_FOREACH(wp, &w->panes, entry) {
if (screen_redraw_make_pane_status(c, w, wp)) if (screen_redraw_make_pane_status(c, w, wp))
@@ -441,7 +437,7 @@ screen_redraw_screen(struct client *c)
screen_redraw_set_context(c, &ctx); screen_redraw_set_context(c, &ctx);
if (flags & (CLIENT_REDRAWWINDOW|CLIENT_REDRAWBORDERS)) { if (flags & (CLIENT_REDRAWWINDOW|CLIENT_REDRAWBORDERS)) {
if (ctx.pane_status != CELL_STATUS_OFF) if (ctx.pane_status != PANE_STATUS_OFF)
screen_redraw_draw_pane_status(&ctx); screen_redraw_draw_pane_status(&ctx);
screen_redraw_draw_borders(&ctx); screen_redraw_draw_borders(&ctx);
} }

View File

@@ -36,7 +36,7 @@ static const struct grid_cell *screen_write_combine(struct screen_write_ctx *,
const struct utf8_data *, u_int *); const struct utf8_data *, u_int *);
static const struct grid_cell screen_write_pad_cell = { static const struct grid_cell screen_write_pad_cell = {
GRID_FLAG_PADDING, 0, 8, 8, { { 0 }, 0, 0, 0 } { { 0 }, 0, 0, 0 }, 0, GRID_FLAG_PADDING, 0, 8, 8
}; };
struct screen_write_collect_item { struct screen_write_collect_item {
@@ -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. */
@@ -1088,6 +1092,31 @@ screen_write_scrollup(struct screen_write_ctx *ctx, u_int lines, u_int bg)
ctx->scrolled += lines; ctx->scrolled += lines;
} }
/* Scroll down. */
void
screen_write_scrolldown(struct screen_write_ctx *ctx, u_int lines, u_int bg)
{
struct screen *s = ctx->s;
struct grid *gd = s->grid;
struct tty_ctx ttyctx;
u_int i;
screen_write_initctx(ctx, &ttyctx);
ttyctx.bg = bg;
if (lines == 0)
lines = 1;
else if (lines > s->rlower - s->rupper + 1)
lines = s->rlower - s->rupper + 1;
for (i = 0; i < lines; i++)
grid_view_scroll_region_down(gd, s->rupper, s->rlower, bg);
screen_write_collect_flush(ctx, 0);
ttyctx.num = lines;
tty_write(tty_cmd_scrolldown, &ttyctx);
}
/* Carriage return (cursor to start of line). */ /* Carriage return (cursor to start of line). */
void void
screen_write_carriagereturn(struct screen_write_ctx *ctx) screen_write_carriagereturn(struct screen_write_ctx *ctx)
@@ -1169,11 +1198,7 @@ screen_write_clearscreen(struct screen_write_ctx *ctx, u_int bg)
void void
screen_write_clearhistory(struct screen_write_ctx *ctx) screen_write_clearhistory(struct screen_write_ctx *ctx)
{ {
struct screen *s = ctx->s; grid_clear_history(ctx->s->grid);
struct grid *gd = s->grid;
grid_move_lines(gd, 0, gd->hsize, gd->sy, 8);
gd->hscrolled = gd->hsize = 0;
} }
/* Clear a collected line. */ /* Clear a collected line. */
@@ -1213,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);
} }
} }
@@ -1302,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++) {
@@ -1327,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;
@@ -1614,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,22 @@ 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)
{ {
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);
@@ -522,9 +519,10 @@ have_event:
/* Is this on the status line? */ /* Is this on the status line? */
m->statusat = status_at_line(c); m->statusat = status_at_line(c);
m->statuslines = status_line_size(c);
if (m->statusat != -1 && if (m->statusat != -1 &&
y >= (u_int)m->statusat && y >= (u_int)m->statusat &&
y < m->statusat + status_line_size(c)) { y < m->statusat + m->statuslines) {
sr = status_get_range(c, x, y - m->statusat); sr = status_get_range(c, x, y - m->statusat);
if (sr == NULL) { if (sr == NULL) {
where = STATUS_DEFAULT; where = STATUS_DEFAULT;
@@ -539,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;
@@ -553,8 +552,8 @@ have_event:
/* Not on status line. Adjust position and check for border or pane. */ /* Not on status line. Adjust position and check for border or pane. */
if (where == NOWHERE) { if (where == NOWHERE) {
px = x; px = x;
if (m->statusat == 0 && y > 0) if (m->statusat == 0 && y >= m->statuslines)
py = y - 1; py = y - m->statuslines;
else if (m->statusat > 0 && y >= (u_int)m->statusat) else if (m->statusat > 0 && y >= (u_int)m->statusat)
py = m->statusat - 1; py = m->statusat - 1;
else else
@@ -661,8 +660,7 @@ have_event:
break; break;
} }
c->tty.mouse_drag_flag = 0; c->tty.mouse_drag_flag = 0;
goto out;
return (key);
} }
/* Convert to a key binding. */ /* Convert to a key binding. */
@@ -957,6 +955,7 @@ have_event:
if (key == KEYC_UNKNOWN) if (key == KEYC_UNKNOWN)
return (KEYC_UNKNOWN); return (KEYC_UNKNOWN);
out:
/* Apply modifiers if any. */ /* Apply modifiers if any. */
if (b & MOUSE_MASK_META) if (b & MOUSE_MASK_META)
key |= KEYC_ESCAPE; key |= KEYC_ESCAPE;
@@ -965,6 +964,8 @@ have_event:
if (b & MOUSE_MASK_SHIFT) if (b & MOUSE_MASK_SHIFT)
key |= KEYC_SHIFT; key |= KEYC_SHIFT;
if (log_get_level() != 0)
log_debug("mouse key is %s", key_string_lookup_key (key));
return (key); return (key);
} }
@@ -992,6 +993,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.
@@ -1015,7 +1034,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;
@@ -1024,16 +1043,6 @@ server_client_key_callback(struct cmdq_item *item, void *data)
fatal("gettimeofday failed"); fatal("gettimeofday failed");
session_update_activity(s, &c->activity_time); session_update_activity(s, &c->activity_time);
/* Handle status line. */
if (~c->flags & CLIENT_READONLY)
status_message_clear(c);
if (c->prompt_string != NULL) {
if (c->flags & CLIENT_READONLY)
goto out;
if (status_prompt_key(c, key) == 0)
goto out;
}
/* Check for mouse keys. */ /* Check for mouse keys. */
m->valid = 0; m->valid = 0;
if (key == KEYC_MOUSE) { if (key == KEYC_MOUSE) {
@@ -1050,7 +1059,7 @@ server_client_key_callback(struct cmdq_item *item, void *data)
* Mouse drag is in progress, so fire the callback (now that * Mouse drag is in progress, so fire the callback (now that
* the mouse event is valid). * the mouse event is valid).
*/ */
if (key == KEYC_DRAGGING) { if ((key & KEYC_MASK_KEY) == KEYC_DRAGGING) {
c->tty.mouse_drag_update(c, m); c->tty.mouse_drag_update(c, m);
goto out; goto out;
} }
@@ -1198,6 +1207,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);
} }
@@ -1210,17 +1221,30 @@ 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);
/* /*
* Key presses in overlay mode are a special case. The queue might be * Key presses in overlay mode and the command prompt are a special
* blocked so they need to be processed immediately rather than queued. * case. The queue might be blocked so they need to be processed
* immediately rather than queued.
*/ */
if ((~c->flags & CLIENT_READONLY) && c->overlay_key != NULL) { if (~c->flags & CLIENT_READONLY) {
if (c->overlay_key(c, event) != 0) status_message_clear(c);
server_client_clear_overlay(c); if (c->prompt_string != NULL) {
return (0); if (status_prompt_key(c, event->key) == 0)
return (0);
}
if (c->overlay_key != NULL) {
switch (c->overlay_key(c, event)) {
case 0:
return (0);
case 1:
server_client_clear_overlay(c);
return (0);
}
}
server_client_clear_overlay(c);
} }
/* /*
@@ -1239,7 +1263,9 @@ server_client_loop(void)
struct client *c; struct client *c;
struct window *w; struct window *w;
struct window_pane *wp; struct window_pane *wp;
int focus; struct winlink *wl;
struct session *s;
int focus, attached, resize;
TAILQ_FOREACH(c, &clients, entry) { TAILQ_FOREACH(c, &clients, entry) {
server_client_check_exit(c); server_client_check_exit(c);
@@ -1252,14 +1278,34 @@ 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) {
s = wl->session;
if (s->attached != 0)
attached = 1;
if (s->attached != 0 && s->curw == wl) {
resize = 1;
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);
server_client_check_resize(wp); if (resize)
server_client_check_resize(wp);
} }
wp->flags &= ~PANE_REDRAW; wp->flags &= ~PANE_REDRAW;
} }
@@ -1272,7 +1318,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
@@ -1293,50 +1338,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;
@@ -1344,35 +1359,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. */
@@ -1521,26 +1556,28 @@ 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)
{ {
if (!(c->flags & CLIENT_EXIT)) struct client_file *cf;
if (~c->flags & CLIENT_EXIT)
return;
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);
proc_send(c->peer, MSG_EXIT, -1, &c->retval, sizeof c->retval); proc_send(c->peer, MSG_EXIT, -1, &c->retval, sizeof c->retval);
c->flags &= ~CLIENT_EXIT; c->flags |= CLIENT_EXITED;
} }
/* 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");
} }
@@ -1668,11 +1705,9 @@ 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; ssize_t datalen;
const char *data; struct session *s;
ssize_t datalen;
struct session *s;
if (c->flags & CLIENT_DEAD) if (c->flags & CLIENT_DEAD)
return; return;
@@ -1682,7 +1717,6 @@ server_client_dispatch(struct imsg *imsg, void *arg)
return; return;
} }
data = imsg->data;
datalen = imsg->hdr.len - IMSG_HEADER_SIZE; datalen = imsg->hdr.len - IMSG_HEADER_SIZE;
switch (imsg->hdr.type) { switch (imsg->hdr.type) {
@@ -1699,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();
@@ -1764,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;
} }
} }
@@ -1782,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;
@@ -1926,39 +1955,34 @@ 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; close(c->fd);
c->fd = -1;
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);
control_start(c);
c->tty.fd = -1; c->tty.fd = -1;
} else if (c->fd != -1) {
close(c->fd); if (tty_init(&c->tty, c, c->fd, c->term) != 0) {
c->fd = -1; close(c->fd);
c->fd = -1;
return; } else {
if (c->flags & CLIENT_UTF8)
c->tty.flags |= TTY_UTF8;
if (c->flags & CLIENT_256COLOURS)
c->tty.term_flags |= TERM_256COLOURS;
tty_resize(&c->tty);
c->flags |= CLIENT_TERMINAL;
}
} }
if (c->fd == -1) /*
return; * If this is the first client that has finished identifying, load
if (tty_init(&c->tty, c, c->fd, c->term) != 0) { * configuration files.
close(c->fd); */
c->fd = -1; if ((~c->flags & CLIENT_EXIT) &&
return; !cfg_finished &&
} c == TAILQ_FIRST(&clients) &&
if (c->flags & CLIENT_UTF8) TAILQ_NEXT(c, entry) == NULL)
c->tty.flags |= TTY_UTF8; start_cfg();
if (c->flags & CLIENT_256COLOURS)
c->tty.term_flags |= TERM_256COLOURS;
tty_resize(&c->tty);
if (!(c->flags & CLIENT_CONTROL))
c->flags |= CLIENT_TERMINAL;
} }
/* Handle shell message. */ /* Handle shell message. */
@@ -1975,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, ...)
@@ -2111,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

@@ -308,7 +308,7 @@ server_destroy_pane(struct window_pane *wp, int notify)
wp->fd = -1; wp->fd = -1;
} }
if (options_get_number(w->options, "remain-on-exit")) { if (options_get_number(wp->options, "remain-on-exit")) {
if (~wp->flags & PANE_STATUSREADY) if (~wp->flags & PANE_STATUSREADY)
return; return;
@@ -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

@@ -43,7 +43,7 @@
struct clients clients; struct clients clients;
struct tmuxproc *server_proc; struct tmuxproc *server_proc;
static int server_fd; static int server_fd = -1;
static int server_exit; static int server_exit;
static struct event server_ev_accept; static struct event server_ev_accept;
@@ -209,9 +209,7 @@ server_start(struct tmuxproc *client, struct event_base *base, int lockfd,
c->flags |= CLIENT_EXIT; c->flags |= CLIENT_EXIT;
} }
start_cfg();
server_add_accept(0); server_add_accept(0);
proc_loop(server_proc, server_loop); proc_loop(server_proc, server_loop);
job_kill_all(); job_kill_all();
@@ -363,6 +361,9 @@ server_add_accept(int timeout)
{ {
struct timeval tv = { timeout, 0 }; struct timeval tv = { timeout, 0 };
if (server_fd == -1)
return;
if (event_initialized(&server_ev_accept)) if (event_initialized(&server_ev_accept))
event_del(&server_ev_accept); event_del(&server_ev_accept);

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);
} }

92
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;
@@ -170,17 +174,16 @@ spawn_window(struct spawn_context *sc, char **cause)
/* Spawn the pane. */ /* Spawn the pane. */
wp = spawn_pane(sc, cause); wp = spawn_pane(sc, cause);
if (wp == NULL) { if (wp == NULL) {
if (~sc->flags & SPAWN_RESPAWN) { if (~sc->flags & SPAWN_RESPAWN)
window_destroy(w);
winlink_remove(&s->windows, sc->wl); winlink_remove(&s->windows, sc->wl);
}
return (NULL); return (NULL);
} }
/* 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));
@@ -216,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, item->target.s, NULL, NULL);
else if (~sc->flags & SPAWN_RESPAWN)
cwd = xstrdup(server_client_get_cwd(c, item->target.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.
@@ -229,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) {
@@ -249,10 +265,10 @@ 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) { if (sc->argc == 0 && (~sc->flags & SPAWN_RESPAWN)) {
cmd = options_get_string(s->options, "default-command"); cmd = options_get_string(s->options, "default-command");
if (cmd != NULL && *cmd != '\0') { if (cmd != NULL && *cmd != '\0') {
argc = 1; argc = 1;
@@ -265,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
@@ -276,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)
@@ -332,6 +337,17 @@ spawn_pane(struct spawn_context *sc, char **cause)
cmd_log_argv(new_wp->argc, new_wp->argv, "%s", __func__); cmd_log_argv(new_wp->argc, new_wp->argv, "%s", __func__);
environ_log(child, "%s: environment ", __func__); environ_log(child, "%s: environment ", __func__);
/* Initialize the window size. */
memset(&ws, 0, sizeof ws);
ws.ws_col = screen_size_x(&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. */
sigfillset(&set);
sigprocmask(SIG_BLOCK, &set, &oldset);
/* If the command is empty, don't fork a child process. */ /* If the command is empty, don't fork a child process. */
if (sc->flags & SPAWN_EMPTY) { if (sc->flags & SPAWN_EMPTY) {
new_wp->flags |= PANE_EMPTY; new_wp->flags |= PANE_EMPTY;
@@ -340,15 +356,6 @@ spawn_pane(struct spawn_context *sc, char **cause)
goto complete; goto complete;
} }
/* Initialize the window size. */
memset(&ws, 0, sizeof ws);
ws.ws_col = screen_size_x(&new_wp->base);
ws.ws_row = screen_size_y(&new_wp->base);
/* Block signals until fork has completed. */
sigfillset(&set);
sigprocmask(SIG_BLOCK, &set, &oldset);
/* Fork the new process. */ /* Fork the new process. */
new_wp->pid = fdforkpty(ptm_fd, &new_wp->fd, new_wp->tty, NULL, &ws); new_wp->pid = fdforkpty(ptm_fd, &new_wp->fd, new_wp->tty, NULL, &ws);
if (new_wp->pid == -1) { if (new_wp->pid == -1) {
@@ -377,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);
@@ -426,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) {

53
style.c
View File

@@ -30,18 +30,21 @@
/* Default style. */ /* Default style. */
static struct style style_default = { static struct style style_default = {
{ 0, 0, 8, 8, { { ' ' }, 0, 1, 1 } }, { { { ' ' }, 0, 1, 1 }, 0, 0, 8, 8, 0 },
8,
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)
@@ -73,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)
@@ -127,6 +134,10 @@ style_parse(struct style *sy, const struct grid_cell *base, const char *in)
sy->align = STYLE_ALIGN_RIGHT; sy->align = STYLE_ALIGN_RIGHT;
else else
goto error; goto error;
} else if (end > 5 && strncasecmp(tmp, "fill=", 5) == 0) {
if ((value = colour_fromstring(tmp + 5)) == -1)
goto error;
sy->fill = value;
} else if (end > 3 && strncasecmp(tmp + 1, "g=", 2) == 0) { } else if (end > 3 && strncasecmp(tmp + 1, "g=", 2) == 0) {
if ((value = colour_fromstring(tmp + 3)) == -1) if ((value = colour_fromstring(tmp + 3)) == -1)
goto error; goto error;
@@ -213,6 +224,19 @@ 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) {
off += xsnprintf(s + off, sizeof s - off, "%sfill=%s", comma,
colour_tostring(sy->fill));
comma = ",";
}
if (gc->fg != 8) { if (gc->fg != 8) {
off += xsnprintf(s + off, sizeof s - off, "%sfg=%s", comma, off += xsnprintf(s + off, sizeof s - off, "%sfg=%s", comma,
colour_tostring(gc->fg)); colour_tostring(gc->fg));
@@ -247,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)
@@ -290,6 +299,8 @@ style_equal(struct style *sy1, struct style *sy2)
return (0); return (0);
if ((gc1->attr & STYLE_ATTR_MASK) != (gc2->attr & STYLE_ATTR_MASK)) if ((gc1->attr & STYLE_ATTR_MASK) != (gc2->attr & STYLE_ATTR_MASK))
return (0); return (0);
if (sy1->fill != sy2->fill)
return (0);
if (sy1->align != sy2->align) if (sy1->align != sy2->align)
return (0); return (0);
return (1); return (1);

949
tmux.1

File diff suppressed because it is too large Load Diff

22
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;
@@ -321,11 +329,11 @@ main(int argc, char **argv)
global_s_options = options_create(NULL); global_s_options = options_create(NULL);
global_w_options = options_create(NULL); global_w_options = options_create(NULL);
for (oe = options_table; oe->name != NULL; oe++) { for (oe = options_table; oe->name != NULL; oe++) {
if (oe->scope == OPTIONS_TABLE_SERVER) if (oe->scope & OPTIONS_TABLE_SERVER)
options_default(global_options, oe); options_default(global_options, oe);
if (oe->scope == OPTIONS_TABLE_SESSION) if (oe->scope & OPTIONS_TABLE_SESSION)
options_default(global_s_options, oe); options_default(global_s_options, oe);
if (oe->scope == OPTIONS_TABLE_WINDOW) if (oe->scope & OPTIONS_TABLE_WINDOW)
options_default(global_w_options, oe); options_default(global_w_options, oe);
} }

284
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)))
@@ -113,9 +117,10 @@ struct winlink;
#define KEYC_CTRL 0x400000000000ULL #define KEYC_CTRL 0x400000000000ULL
#define KEYC_SHIFT 0x800000000000ULL #define KEYC_SHIFT 0x800000000000ULL
#define KEYC_XTERM 0x1000000000000ULL #define KEYC_XTERM 0x1000000000000ULL
#define KEYC_LITERAL 0x2000000000000ULL
/* Mask to obtain key w/o modifiers. */ /* Mask to obtain key w/o modifiers. */
#define KEYC_MASK_MOD (KEYC_ESCAPE|KEYC_CTRL|KEYC_SHIFT|KEYC_XTERM) #define KEYC_MASK_MOD (KEYC_ESCAPE|KEYC_CTRL|KEYC_SHIFT|KEYC_XTERM|KEYC_LITERAL)
#define KEYC_MASK_KEY (~KEYC_MASK_MOD) #define KEYC_MASK_KEY (~KEYC_MASK_MOD)
/* Is this a mouse key? */ /* Is this a mouse key? */
@@ -421,6 +426,7 @@ enum tty_code_code {
TTYC_REV, TTYC_REV,
TTYC_RGB, TTYC_RGB,
TTYC_RI, TTYC_RI,
TTYC_RIN,
TTYC_RMACS, TTYC_RMACS,
TTYC_RMCUP, TTYC_RMCUP,
TTYC_RMKX, TTYC_RMKX,
@@ -429,6 +435,7 @@ enum tty_code_code {
TTYC_SETAF, TTYC_SETAF,
TTYC_SETRGBB, TTYC_SETRGBB,
TTYC_SETRGBF, TTYC_SETRGBF,
TTYC_SETULC,
TTYC_SGR0, TTYC_SGR0,
TTYC_SITM, TTYC_SITM,
TTYC_SMACS, TTYC_SMACS,
@@ -473,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
}; };
/* /*
@@ -487,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. */
@@ -597,12 +630,13 @@ enum utf8_state {
/* Grid cell data. */ /* Grid cell data. */
struct grid_cell { struct grid_cell {
u_char flags; struct utf8_data data; /* 21 bytes */
u_short attr; u_short attr;
u_char flags;
int fg; int fg;
int bg; int bg;
struct utf8_data data; int us;
}; } __packed;
struct grid_cell_entry { struct grid_cell_entry {
u_char flags; u_char flags;
union { union {
@@ -678,15 +712,25 @@ 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;
int fill;
enum style_align align; enum style_align align;
enum style_list list; enum style_list list;
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. */
@@ -694,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 */
@@ -812,6 +857,7 @@ struct window_pane {
u_int active_point; u_int active_point;
struct window *window; struct window *window;
struct options *options;
struct layout_cell *layout_cell; struct layout_cell *layout_cell;
struct layout_cell *saved_layout_cell; struct layout_cell *saved_layout_cell;
@@ -838,6 +884,8 @@ struct window_pane {
#define PANE_STATUSREADY 0x200 #define PANE_STATUSREADY 0x200
#define PANE_STATUSDRAWN 0x400 #define PANE_STATUSDRAWN 0x400
#define PANE_EMPTY 0x800 #define PANE_EMPTY 0x800
#define PANE_STYLECHANGED 0x1000
#define PANE_RESIZED 0x2000
int argc; int argc;
char **argv; char **argv;
@@ -856,7 +904,8 @@ struct window_pane {
struct input_ctx *ictx; struct input_ctx *ictx;
struct style style; struct style cached_style;
struct style cached_active_style;
int *palette; int *palette;
int pipe_fd; int pipe_fd;
@@ -878,7 +927,9 @@ struct window_pane {
TAILQ_HEAD (, window_mode_entry) modes; TAILQ_HEAD (, window_mode_entry) modes;
struct event modetimer; struct event modetimer;
time_t modelast; time_t modelast;
char *searchstr; char *searchstr;
int searchregex;
TAILQ_ENTRY(window_pane) entry; TAILQ_ENTRY(window_pane) entry;
RB_ENTRY(window_pane) tree_entry; RB_ENTRY(window_pane) tree_entry;
@@ -889,6 +940,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;
@@ -910,13 +962,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_STYLECHANGED 0x10 #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;
@@ -924,9 +978,6 @@ struct window {
struct options *options; struct options *options;
struct style style;
struct style active_style;
u_int references; u_int references;
TAILQ_HEAD(, winlink) winlinks; TAILQ_HEAD(, winlink) winlinks;
@@ -957,6 +1008,12 @@ 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. */
#define PANE_STATUS_OFF 0
#define PANE_STATUS_TOP 1
#define PANE_STATUS_BOTTOM 2
/* Layout direction. */ /* Layout direction. */
enum layout_type { enum layout_type {
@@ -1115,7 +1172,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;
@@ -1124,9 +1184,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;
@@ -1170,20 +1233,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;
@@ -1197,8 +1253,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 {
@@ -1207,8 +1261,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
@@ -1326,6 +1380,7 @@ struct cmd_parse_input {
#define CMD_PARSE_QUIET 0x1 #define CMD_PARSE_QUIET 0x1
#define CMD_PARSE_PARSEONLY 0x2 #define CMD_PARSE_PARSEONLY 0x2
#define CMD_PARSE_NOALIAS 0x4 #define CMD_PARSE_NOALIAS 0x4
#define CMD_PARSE_VERBOSE 0x8
const char *file; const char *file;
u_int line; u_int line;
@@ -1436,6 +1491,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 *);
@@ -1469,13 +1547,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;
@@ -1491,7 +1562,7 @@ struct client {
#define CLIENT_REPEAT 0x20 #define CLIENT_REPEAT 0x20
#define CLIENT_SUSPENDED 0x40 #define CLIENT_SUSPENDED 0x40
#define CLIENT_ATTACHED 0x80 #define CLIENT_ATTACHED 0x80
/* 0x100 unused */ #define CLIENT_EXITED 0x100
#define CLIENT_DEAD 0x200 #define CLIENT_DEAD 0x200
#define CLIENT_REDRAWBORDERS 0x400 #define CLIENT_REDRAWBORDERS 0x400
#define CLIENT_READONLY 0x800 #define CLIENT_READONLY 0x800
@@ -1509,12 +1580,17 @@ struct client {
#define CLIENT_STATUSOFF 0x800000 #define CLIENT_STATUSOFF 0x800000
#define CLIENT_REDRAWSTATUSALWAYS 0x1000000 #define CLIENT_REDRAWSTATUSALWAYS 0x1000000
#define CLIENT_REDRAWOVERLAY 0x2000000 #define CLIENT_REDRAWOVERLAY 0x2000000
#define CLIENT_CONTROL_NOOUTPUT 0x4000000
#define CLIENT_ALLREDRAWFLAGS \ #define CLIENT_ALLREDRAWFLAGS \
(CLIENT_REDRAWWINDOW| \ (CLIENT_REDRAWWINDOW| \
CLIENT_REDRAWSTATUS| \ CLIENT_REDRAWSTATUS| \
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| \
@@ -1541,6 +1617,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;
@@ -1558,6 +1635,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);
@@ -1566,6 +1645,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
@@ -1606,12 +1686,11 @@ enum options_table_type {
OPTIONS_TABLE_COMMAND OPTIONS_TABLE_COMMAND
}; };
enum options_table_scope { #define OPTIONS_TABLE_NONE 0
OPTIONS_TABLE_NONE, #define OPTIONS_TABLE_SERVER 0x1
OPTIONS_TABLE_SERVER, #define OPTIONS_TABLE_SESSION 0x2
OPTIONS_TABLE_SESSION, #define OPTIONS_TABLE_WINDOW 0x4
OPTIONS_TABLE_WINDOW #define OPTIONS_TABLE_PANE 0x8
};
#define OPTIONS_TABLE_IS_ARRAY 0x1 #define OPTIONS_TABLE_IS_ARRAY 0x1
#define OPTIONS_TABLE_IS_HOOK 0x2 #define OPTIONS_TABLE_IS_HOOK 0x2
@@ -1619,7 +1698,7 @@ enum options_table_scope {
struct options_table_entry { struct options_table_entry {
const char *name; const char *name;
enum options_table_type type; enum options_table_type type;
enum options_table_scope scope; int scope;
int flags; int flags;
u_int minimum; u_int minimum;
@@ -1651,6 +1730,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;
@@ -1673,6 +1753,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;
@@ -1687,6 +1773,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;
@@ -1708,6 +1795,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 *);
@@ -1759,6 +1848,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 *,
@@ -1781,6 +1872,7 @@ void notify_pane(const char *, struct window_pane *);
/* options.c */ /* options.c */
struct options *options_create(struct options *); struct options *options_create(struct options *);
void options_free(struct options *); void options_free(struct options *);
void options_set_parent(struct options *, struct options *);
struct options_entry *options_first(struct options *); struct options_entry *options_first(struct options *);
struct options_entry *options_next(struct options_entry *); struct options_entry *options_next(struct options_entry *);
struct options_entry *options_empty(struct options *, struct options_entry *options_empty(struct options *,
@@ -1820,7 +1912,10 @@ struct options_entry *options_set_number(struct options *, const char *,
long long); long long);
struct options_entry *options_set_style(struct options *, const char *, int, struct options_entry *options_set_style(struct options *, const char *, int,
const char *); const char *);
enum options_table_scope options_scope_from_flags(struct args *, int, int options_scope_from_name(struct args *, int,
const char *, struct cmd_find_state *, struct options **,
char **);
int options_scope_from_flags(struct args *, int,
struct cmd_find_state *, struct options **, char **); struct cmd_find_state *, struct options **, char **);
/* options-table.c */ /* options-table.c */
@@ -1884,7 +1979,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 *);
@@ -1894,7 +1989,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 *);
@@ -1914,6 +2009,7 @@ void tty_cmd_insertcharacter(struct tty *, const struct tty_ctx *);
void tty_cmd_insertline(struct tty *, const struct tty_ctx *); void tty_cmd_insertline(struct tty *, const struct tty_ctx *);
void tty_cmd_linefeed(struct tty *, const struct tty_ctx *); void tty_cmd_linefeed(struct tty *, const struct tty_ctx *);
void tty_cmd_scrollup(struct tty *, const struct tty_ctx *); void tty_cmd_scrollup(struct tty *, const struct tty_ctx *);
void tty_cmd_scrolldown(struct tty *, const struct tty_ctx *);
void tty_cmd_reverseindex(struct tty *, const struct tty_ctx *); void tty_cmd_reverseindex(struct tty *, const struct tty_ctx *);
void tty_cmd_setselection(struct tty *, const struct tty_ctx *); void tty_cmd_setselection(struct tty *, const struct tty_ctx *);
void tty_cmd_rawstring(struct tty *, const struct tty_ctx *); void tty_cmd_rawstring(struct tty *, const struct tty_ctx *);
@@ -2011,13 +2107,15 @@ extern const struct cmd_entry *cmd_table[];
/* cmd-attach-session.c */ /* cmd-attach-session.c */
enum cmd_retval cmd_attach_session(struct cmdq_item *, const char *, int, int, enum cmd_retval cmd_attach_session(struct cmdq_item *, const char *, int, int,
const char *, int); int, const char *, int);
/* cmd-parse.c */ /* cmd-parse.c */
void cmd_parse_empty(struct cmd_parse_input *); 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 *);
@@ -2034,10 +2132,11 @@ 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 printflike(3, 4) cmdq_format(struct cmdq_item *, const char *, void printflike(3, 4) cmdq_format(struct cmdq_item *, const char *,
const char *, ...); const char *, ...);
u_int cmdq_next(struct client *); u_int cmdq_next(struct client *);
@@ -2059,7 +2158,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);
@@ -2076,6 +2176,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;
@@ -2110,7 +2227,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 */
@@ -2134,8 +2250,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 */
@@ -2161,9 +2275,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 */
@@ -2175,7 +2290,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);
@@ -2187,7 +2302,8 @@ int colour_join_rgb(u_char, u_char, u_char);
void colour_split_rgb(int, u_char *, u_char *, u_char *); void colour_split_rgb(int, u_char *, u_char *, u_char *);
const char *colour_tostring(int); const char *colour_tostring(int);
int colour_fromstring(const char *s); int colour_fromstring(const char *s);
u_char colour_256to16(u_char); int colour_256toRGB(int);
int colour_256to16(int);
/* attributes.c */ /* attributes.c */
const char *attributes_tostring(int); const char *attributes_tostring(int);
@@ -2288,6 +2404,7 @@ void screen_write_reverseindex(struct screen_write_ctx *, u_int);
void screen_write_scrollregion(struct screen_write_ctx *, u_int, u_int); void screen_write_scrollregion(struct screen_write_ctx *, u_int, u_int);
void screen_write_linefeed(struct screen_write_ctx *, int, u_int); void screen_write_linefeed(struct screen_write_ctx *, int, u_int);
void screen_write_scrollup(struct screen_write_ctx *, u_int, u_int); void screen_write_scrollup(struct screen_write_ctx *, u_int, u_int);
void screen_write_scrolldown(struct screen_write_ctx *, u_int, u_int);
void screen_write_carriagereturn(struct screen_write_ctx *); void screen_write_carriagereturn(struct screen_write_ctx *);
void screen_write_clearendofscreen(struct screen_write_ctx *, u_int); void screen_write_clearendofscreen(struct screen_write_ctx *, u_int);
void screen_write_clearstartofscreen(struct screen_write_ctx *, u_int); void screen_write_clearstartofscreen(struct screen_write_ctx *, u_int);
@@ -2311,7 +2428,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);
@@ -2351,8 +2469,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_destroy(struct window *);
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 *);
@@ -2363,9 +2480,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);
@@ -2393,11 +2513,12 @@ 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 *);
u_int window_pane_search(struct window_pane *, const char *); u_int window_pane_search(struct window_pane *, const char *, int,
int);
const char *window_printable_flags(struct winlink *); const char *window_printable_flags(struct winlink *);
struct window_pane *window_pane_find_up(struct window_pane *); struct window_pane *window_pane_find_up(struct window_pane *);
struct window_pane *window_pane_find_down(struct window_pane *); struct window_pane *window_pane_find_down(struct window_pane *);
@@ -2425,7 +2546,7 @@ void layout_set_size(struct layout_cell *, u_int, u_int, u_int,
u_int); u_int);
void layout_make_leaf(struct layout_cell *, struct window_pane *); void layout_make_leaf(struct layout_cell *, struct window_pane *);
void layout_make_node(struct layout_cell *, enum layout_type); void layout_make_node(struct layout_cell *, enum layout_type);
void layout_fix_offsets(struct layout_cell *); void layout_fix_offsets(struct window *);
void layout_fix_panes(struct window *); void layout_fix_panes(struct window *);
void layout_resize_adjust(struct window *, struct layout_cell *, void layout_resize_adjust(struct window *, struct layout_cell *,
enum layout_type, int); enum layout_type, int);
@@ -2454,7 +2575,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 *);
@@ -2512,9 +2634,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 *,
@@ -2567,6 +2688,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 */
@@ -2586,6 +2708,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 */
@@ -2623,8 +2746,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 *);
@@ -2634,4 +2755,7 @@ int style_is_default(struct style *);
struct winlink *spawn_window(struct spawn_context *, char **); struct winlink *spawn_window(struct spawn_context *, char **);
struct window_pane *spawn_pane(struct spawn_context *, char **); struct window_pane *spawn_pane(struct spawn_context *, char **);
/* regsub.c */
char *regsub(const char *, const char *, const char *, int);
#endif /* TMUX_H */ #endif /* TMUX_H */

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

@@ -242,6 +242,7 @@ static const struct tty_term_code_entry tty_term_codes[] = {
[TTYC_REV] = { TTYCODE_STRING, "rev" }, [TTYC_REV] = { TTYCODE_STRING, "rev" },
[TTYC_RGB] = { TTYCODE_FLAG, "RGB" }, [TTYC_RGB] = { TTYCODE_FLAG, "RGB" },
[TTYC_RI] = { TTYCODE_STRING, "ri" }, [TTYC_RI] = { TTYCODE_STRING, "ri" },
[TTYC_RIN] = { TTYCODE_STRING, "rin" },
[TTYC_RMACS] = { TTYCODE_STRING, "rmacs" }, [TTYC_RMACS] = { TTYCODE_STRING, "rmacs" },
[TTYC_RMCUP] = { TTYCODE_STRING, "rmcup" }, [TTYC_RMCUP] = { TTYCODE_STRING, "rmcup" },
[TTYC_RMKX] = { TTYCODE_STRING, "rmkx" }, [TTYC_RMKX] = { TTYCODE_STRING, "rmkx" },
@@ -249,6 +250,7 @@ static const struct tty_term_code_entry tty_term_codes[] = {
[TTYC_SETAF] = { TTYCODE_STRING, "setaf" }, [TTYC_SETAF] = { TTYCODE_STRING, "setaf" },
[TTYC_SETRGBB] = { TTYCODE_STRING, "setrgbb" }, [TTYC_SETRGBB] = { TTYCODE_STRING, "setrgbb" },
[TTYC_SETRGBF] = { TTYCODE_STRING, "setrgbf" }, [TTYC_SETRGBF] = { TTYCODE_STRING, "setrgbf" },
[TTYC_SETULC] = { TTYCODE_STRING, "Setulc" },
[TTYC_SE] = { TTYCODE_STRING, "Se" }, [TTYC_SE] = { TTYCODE_STRING, "Se" },
[TTYC_SGR0] = { TTYCODE_STRING, "sgr0" }, [TTYC_SGR0] = { TTYCODE_STRING, "sgr0" },
[TTYC_SITM] = { TTYCODE_STRING, "sitm" }, [TTYC_SITM] = { TTYCODE_STRING, "sitm" },
@@ -279,7 +281,7 @@ static char *
tty_term_strip(const char *s) tty_term_strip(const char *s)
{ {
const char *ptr; const char *ptr;
static char buf[BUFSIZ]; static char buf[8192];
size_t len; size_t len;
/* Ignore strings with no padding. */ /* Ignore strings with no padding. */
@@ -307,7 +309,7 @@ tty_term_strip(const char *s)
static char * static char *
tty_term_override_next(const char *s, size_t *offset) tty_term_override_next(const char *s, size_t *offset)
{ {
static char value[BUFSIZ]; static char value[8192];
size_t n = 0, at = *offset; size_t n = 0, at = *offset;
if (s[at] == '\0') if (s[at] == '\0')
@@ -526,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).
@@ -542,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);
@@ -565,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));
@@ -640,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));
} }
@@ -691,7 +684,7 @@ tty_term_describe(struct tty_term *term, enum tty_code_code code)
break; break;
case TTYCODE_STRING: case TTYCODE_STRING:
strnvis(out, term->codes[code].value.string, sizeof out, strnvis(out, term->codes[code].value.string, sizeof out,
VIS_OCTAL|VIS_TAB|VIS_NL); VIS_OCTAL|VIS_CSTYLE|VIS_TAB|VIS_NL);
xsnprintf(s, sizeof s, "%4u: %s: (string) %s", xsnprintf(s, sizeof s, "%4u: %s: (string) %s",
code, tty_term_codes[code].name, code, tty_term_codes[code].name,
out); out);

309
tty.c
View File

@@ -49,8 +49,11 @@ static void tty_check_fg(struct tty *, struct window_pane *,
struct grid_cell *); struct grid_cell *);
static void tty_check_bg(struct tty *, struct window_pane *, static void tty_check_bg(struct tty *, struct window_pane *,
struct grid_cell *); struct grid_cell *);
static void tty_check_us(struct tty *, struct window_pane *,
struct grid_cell *);
static void tty_colours_fg(struct tty *, const struct grid_cell *); static void tty_colours_fg(struct tty *, const struct grid_cell *);
static void tty_colours_bg(struct tty *, const struct grid_cell *); static void tty_colours_bg(struct tty *, const struct grid_cell *);
static void tty_colours_us(struct tty *, const struct grid_cell *);
static void tty_region_pane(struct tty *, const struct tty_ctx *, u_int, static void tty_region_pane(struct tty *, const struct tty_ctx *, u_int,
u_int); u_int);
@@ -71,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)
@@ -112,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);
} }
@@ -124,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
@@ -273,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);
@@ -307,21 +330,31 @@ tty_start_tty(struct tty *tty)
log_debug("%s: using UTF-8 for ACS", c->name); log_debug("%s: using UTF-8 for ACS", c->name);
tty_putcode(tty, TTYC_CNORM); tty_putcode(tty, TTYC_CNORM);
if (tty_term_has(tty->term, TTYC_KMOUS)) if (tty_term_has(tty->term, TTYC_KMOUS)) {
tty_puts(tty, "\033[?1000l\033[?1002l\033[?1006l\033[?1005l"); tty_puts(tty, "\033[?1000l\033[?1002l\033[?1003l");
tty_puts(tty, "\033[?1006l\033[?1005l");
}
if (tty_term_flag(tty->term, TTYC_XT)) { if (tty_term_flag(tty->term, TTYC_XT)) {
if (options_get_number(global_options, "focus-events")) { if (options_get_number(global_options, "focus-events")) {
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;
@@ -337,6 +370,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;
@@ -367,11 +402,14 @@ 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)) {
tty_raw(tty, "\033[?1000l\033[?1002l\033[?1006l\033[?1005l"); tty_raw(tty, "\033[?1000l\033[?1002l\033[?1003l");
tty_raw(tty, "\033[?1006l\033[?1005l");
}
if (tty_term_flag(tty->term, TTYC_XT)) { if (tty_term_flag(tty->term, TTYC_XT)) {
if (tty->flags & TTY_FOCUS) { if (tty->flags & TTY_FOCUS) {
@@ -422,9 +460,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 */
@@ -527,6 +565,12 @@ tty_putc(struct tty *tty, u_char ch)
{ {
const char *acs; const char *acs;
if ((tty->term->flags & TERM_NOXENL) &&
ch >= 0x20 && ch != 0x7f &&
tty->cy == tty->sy - 1 &&
tty->cx + 1 >= tty->sx)
return;
if (tty->cell.attr & GRID_ATTR_CHARSET) { if (tty->cell.attr & GRID_ATTR_CHARSET) {
acs = tty_acs_get(tty, ch); acs = tty_acs_get(tty, ch);
if (acs != NULL) if (acs != NULL)
@@ -547,7 +591,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++;
@@ -557,6 +601,11 @@ 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_NOXENL) &&
tty->cy == tty->sy - 1 &&
tty->cx + len >= tty->sx)
len = tty->sx - tty->cx - 1;
tty_add(tty, buf, len); tty_add(tty, buf, len);
if (tty->cx + width > tty->sx) { if (tty->cx + width > tty->sx) {
tty->cx = (tty->cx + width) - tty->sx; tty->cx = (tty->cx + width) - tty->sx;
@@ -1049,17 +1098,17 @@ tty_clamp_area(struct tty *tty, const struct tty_ctx *ctx, u_int px, u_int py,
*y = ctx->yoff + py - ctx->oy; *y = ctx->yoff + py - ctx->oy;
*ry = ny; *ry = ny;
} else if (yoff < ctx->oy && yoff + ny > ctx->oy + ctx->sy) { } else if (yoff < ctx->oy && yoff + ny > ctx->oy + ctx->sy) {
/* Both left and right not visible. */ /* Both top and bottom not visible. */
*j = ctx->oy; *j = ctx->oy;
*y = 0; *y = 0;
*ry = ctx->sy; *ry = ctx->sy;
} else if (yoff < ctx->oy) { } else if (yoff < ctx->oy) {
/* Left not visible. */ /* Top not visible. */
*j = ctx->oy - (ctx->yoff + py); *j = ctx->oy - (ctx->yoff + py);
*y = 0; *y = 0;
*ry = ny - *j; *ry = ny - *j;
} else { } else {
/* Right not visible. */ /* Bottom not visible. */
*j = 0; *j = 0;
*y = (ctx->yoff + py) - ctx->oy; *y = (ctx->yoff + py) - ctx->oy;
*ry = ctx->sy - *y; *ry = ctx->sy - *y;
@@ -1102,7 +1151,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);
@@ -1203,7 +1253,7 @@ tty_draw_line(struct tty *tty, struct window_pane *wp, struct screen *s,
const struct grid_cell *gcp; const struct grid_cell *gcp;
struct grid_line *gl; struct grid_line *gl;
u_int i, j, ux, sx, width; u_int i, j, ux, sx, width;
int flags, cleared = 0; int flags, cleared = 0, wrapped = 0;
char buf[512]; char buf[512];
size_t len; size_t len;
u_int cellsize; u_int cellsize;
@@ -1260,8 +1310,10 @@ tty_draw_line(struct tty *tty, struct window_pane *wp, struct screen *s,
tty_putcode(tty, TTYC_EL1); tty_putcode(tty, TTYC_EL1);
cleared = 1; cleared = 1;
} }
} else } else {
log_debug("%s: wrapped line %u", __func__, aty); log_debug("%s: wrapped line %u", __func__, aty);
wrapped = 1;
}
memcpy(&last, &grid_default_cell, sizeof last); memcpy(&last, &grid_default_cell, sizeof last);
len = 0; len = 0;
@@ -1276,6 +1328,7 @@ tty_draw_line(struct tty *tty, struct window_pane *wp, struct screen *s,
gcp->attr != last.attr || gcp->attr != last.attr ||
gcp->fg != last.fg || gcp->fg != last.fg ||
gcp->bg != last.bg || gcp->bg != last.bg ||
gcp->us != last.us ||
ux + width + gcp->data.width > nx || ux + width + gcp->data.width > nx ||
(sizeof buf) - len < gcp->data.size)) { (sizeof buf) - len < gcp->data.size)) {
tty_attributes(tty, &last, wp); tty_attributes(tty, &last, wp);
@@ -1284,13 +1337,15 @@ tty_draw_line(struct tty *tty, struct window_pane *wp, struct screen *s,
tty_clear_line(tty, wp, aty, atx + ux, width, tty_clear_line(tty, wp, aty, atx + ux, width,
last.bg); last.bg);
} else { } else {
tty_cursor(tty, atx + ux, aty); if (!wrapped || atx != 0 || ux != 0)
tty_cursor(tty, atx + ux, aty);
tty_putn(tty, buf, len, width); tty_putn(tty, buf, len, width);
} }
ux += width; ux += width;
len = 0; len = 0;
width = 0; width = 0;
wrapped = 0;
} }
if (gcp->flags & GRID_FLAG_SELECTED) if (gcp->flags & GRID_FLAG_SELECTED)
@@ -1324,7 +1379,8 @@ tty_draw_line(struct tty *tty, struct window_pane *wp, struct screen *s,
log_debug("%s: %zu cleared (end)", __func__, len); log_debug("%s: %zu cleared (end)", __func__, len);
tty_clear_line(tty, wp, aty, atx + ux, width, last.bg); tty_clear_line(tty, wp, aty, atx + ux, width, last.bg);
} else { } else {
tty_cursor(tty, atx + ux, aty); if (!wrapped || atx != 0 || ux != 0)
tty_cursor(tty, atx + ux, aty);
tty_putn(tty, buf, len, width); tty_putn(tty, buf, len, width);
} }
ux += width; ux += width;
@@ -1538,10 +1594,11 @@ tty_cmd_reverseindex(struct tty *tty, const struct tty_ctx *ctx)
return; return;
if (ctx->bigger || if (ctx->bigger ||
!tty_pane_full_width(tty, ctx) || (!tty_pane_full_width(tty, ctx) && !tty_use_margin(tty)) ||
tty_fake_bce(tty, wp, 8) || tty_fake_bce(tty, wp, 8) ||
!tty_term_has(tty->term, TTYC_CSR) || !tty_term_has(tty->term, TTYC_CSR) ||
!tty_term_has(tty->term, TTYC_RI) || (!tty_term_has(tty->term, TTYC_RI) &&
!tty_term_has(tty->term, TTYC_RIN)) ||
ctx->wp->sx == 1 || ctx->wp->sx == 1 ||
ctx->wp->sy == 1) { ctx->wp->sy == 1) {
tty_redraw_region(tty, ctx); tty_redraw_region(tty, ctx);
@@ -1551,10 +1608,13 @@ tty_cmd_reverseindex(struct tty *tty, const struct tty_ctx *ctx)
tty_default_attributes(tty, wp, ctx->bg); tty_default_attributes(tty, wp, ctx->bg);
tty_region_pane(tty, ctx, ctx->orupper, ctx->orlower); tty_region_pane(tty, ctx, ctx->orupper, ctx->orlower);
tty_margin_off(tty); tty_margin_pane(tty, ctx);
tty_cursor_pane(tty, ctx, ctx->ocx, ctx->orupper); tty_cursor_pane(tty, ctx, ctx->ocx, ctx->orupper);
tty_putcode(tty, TTYC_RI); if (tty_term_has(tty->term, TTYC_RI))
tty_putcode(tty, TTYC_RI);
else
tty_putcode1(tty, TTYC_RIN, 1);
} }
void void
@@ -1632,6 +1692,38 @@ tty_cmd_scrollup(struct tty *tty, const struct tty_ctx *ctx)
} }
} }
void
tty_cmd_scrolldown(struct tty *tty, const struct tty_ctx *ctx)
{
struct window_pane *wp = ctx->wp;
u_int i;
if (ctx->bigger ||
(!tty_pane_full_width(tty, ctx) && !tty_use_margin(tty)) ||
tty_fake_bce(tty, wp, 8) ||
!tty_term_has(tty->term, TTYC_CSR) ||
(!tty_term_has(tty->term, TTYC_RI) &&
!tty_term_has(tty->term, TTYC_RIN)) ||
wp->sx == 1 ||
wp->sy == 1) {
tty_redraw_region(tty, ctx);
return;
}
tty_default_attributes(tty, wp, ctx->bg);
tty_region_pane(tty, ctx, ctx->orupper, ctx->orlower);
tty_margin_pane(tty, ctx);
tty_cursor_pane(tty, ctx, ctx->ocx, ctx->orupper);
if (tty_term_has(tty->term, TTYC_RIN))
tty_putcode1(tty, TTYC_RIN, ctx->num);
else {
for (i = 0; i < ctx->num; i++)
tty_putcode(tty, TTYC_RI);
}
}
void void
tty_cmd_clearendofscreen(struct tty *tty, const struct tty_ctx *ctx) tty_cmd_clearendofscreen(struct tty *tty, const struct tty_ctx *ctx)
{ {
@@ -1755,7 +1847,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 ||
@@ -1804,7 +1896,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;
@@ -1859,6 +1951,8 @@ tty_invalidate(struct tty *tty)
tty->rlower = tty->rright = UINT_MAX; tty->rlower = tty->rright = UINT_MAX;
if (tty->flags & TTY_STARTED) { if (tty->flags & TTY_STARTED) {
if (tty_use_margin(tty))
tty_puts(tty, "\033[?69h"); /* DECLRMM */
tty_putcode(tty, TTYC_SGR0); tty_putcode(tty, TTYC_SGR0);
tty->mode = ALL_MODES; tty->mode = ALL_MODES;
@@ -1961,7 +2055,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 ||
@@ -2048,7 +2142,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);
@@ -2056,7 +2152,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;
} }
@@ -2121,10 +2219,11 @@ tty_attributes(struct tty *tty, const struct grid_cell *gc,
/* Ignore cell if it is the same as the last one. */ /* Ignore cell if it is the same as the last one. */
if (wp != NULL && if (wp != NULL &&
(int)wp->id == tty->last_wp && (int)wp->id == tty->last_wp &&
~(wp->window->flags & WINDOW_STYLECHANGED) && ~(wp->flags & PANE_STYLECHANGED) &&
gc->attr == tty->last_cell.attr && gc->attr == tty->last_cell.attr &&
gc->fg == tty->last_cell.fg && gc->fg == tty->last_cell.fg &&
gc->bg == tty->last_cell.bg) gc->bg == tty->last_cell.bg &&
gc->us == tty->last_cell.us)
return; return;
tty->last_wp = (wp != NULL ? (int)wp->id : -1); tty->last_wp = (wp != NULL ? (int)wp->id : -1);
memcpy(&tty->last_cell, gc, sizeof tty->last_cell); memcpy(&tty->last_cell, gc, sizeof tty->last_cell);
@@ -2152,14 +2251,18 @@ tty_attributes(struct tty *tty, const struct grid_cell *gc,
/* Fix up the colours if necessary. */ /* Fix up the colours if necessary. */
tty_check_fg(tty, wp, &gc2); tty_check_fg(tty, wp, &gc2);
tty_check_bg(tty, wp, &gc2); tty_check_bg(tty, wp, &gc2);
tty_check_us(tty, wp, &gc2);
/* If any bits are being cleared, reset everything. */ /*
if (tc->attr & ~gc2.attr) * If any bits are being cleared or the underline colour is now default,
* reset everything.
*/
if ((tc->attr & ~gc2.attr) || (tc->us != gc2.us && gc2.us == 0))
tty_reset(tty); tty_reset(tty);
/* /*
* Set the colours. This may call tty_reset() (so it comes next) and * Set the colours. This may call tty_reset() (so it comes next) and
* may add to (NOT remove) the desired attributes by changing new_attr. * may add to (NOT remove) the desired attributes.
*/ */
tty_colours(tty, &gc2); tty_colours(tty, &gc2);
@@ -2212,7 +2315,7 @@ tty_colours(struct tty *tty, const struct grid_cell *gc)
int have_ax; int have_ax;
/* No changes? Nothing is necessary. */ /* No changes? Nothing is necessary. */
if (gc->fg == tc->fg && gc->bg == tc->bg) if (gc->fg == tc->fg && gc->bg == tc->bg && gc->us == tc->us)
return; return;
/* /*
@@ -2260,6 +2363,10 @@ tty_colours(struct tty *tty, const struct grid_cell *gc)
*/ */
if (!COLOUR_DEFAULT(gc->bg) && gc->bg != tc->bg) if (!COLOUR_DEFAULT(gc->bg) && gc->bg != tc->bg)
tty_colours_bg(tty, gc); tty_colours_bg(tty, gc);
/* Set the underscore color. */
if (gc->us != tc->us)
tty_colours_us(tty, gc);
} }
static void static void
@@ -2285,11 +2392,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? */
@@ -2307,10 +2413,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;
} }
@@ -2338,11 +2441,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? */
@@ -2374,6 +2476,23 @@ tty_check_bg(struct tty *tty, struct window_pane *wp, struct grid_cell *gc)
gc->bg -= 90; gc->bg -= 90;
} }
static void
tty_check_us(__unused struct tty *tty, struct window_pane *wp,
struct grid_cell *gc)
{
int c;
/* Perform substitution if this pane has a palette. */
if (~gc->flags & GRID_FLAG_NOPALETTE) {
if ((c = window_pane_get_palette(wp, gc->us)) != -1)
gc->us = c;
}
/* Underscore colour is set as RGB so convert a 256 colour to RGB. */
if (gc->us & COLOUR_FLAG_256)
gc->us = colour_256toRGB (gc->us);
}
static void static void
tty_colours_fg(struct tty *tty, const struct grid_cell *gc) tty_colours_fg(struct tty *tty, const struct grid_cell *gc)
{ {
@@ -2438,6 +2557,31 @@ save_bg:
tc->bg = gc->bg; tc->bg = gc->bg;
} }
static void
tty_colours_us(struct tty *tty, const struct grid_cell *gc)
{
struct grid_cell *tc = &tty->cell;
u_int c;
u_char r, g, b;
/* Must be an RGB colour - this should never happen. */
if (~gc->us & COLOUR_FLAG_RGB)
return;
/*
* Setulc follows the ncurses(3) one argument "direct colour"
* capability format. Calculate the colour value.
*/
colour_split_rgb(gc->us, &r, &g, &b);
c = (65536 * r) + (256 * g) + b;
/* Write the colour. */
tty_putcode1(tty, TTYC_SETULC, c);
/* Save the new values in the terminal current cell. */
tc->us = gc->us;
}
static int static int
tty_try_colour(struct tty *tty, int colour, const char *type) tty_try_colour(struct tty *tty, int colour, const char *type)
{ {
@@ -2477,15 +2621,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);
@@ -2498,35 +2641,39 @@ 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
tty_default_colours(struct grid_cell *gc, struct window_pane *wp) tty_default_colours(struct grid_cell *gc, struct window_pane *wp)
{ {
struct window *w = wp->window; struct options *oo = wp->options;
struct options *oo = w->options; struct style *style, *active_style;
struct style *active, *pane, *window; int c;
int c;
if (w->flags & WINDOW_STYLECHANGED) { if (wp->flags & PANE_STYLECHANGED) {
w->flags &= ~WINDOW_STYLECHANGED; wp->flags &= ~PANE_STYLECHANGED;
active = options_get_style(oo, "window-active-style");
style_copy(&w->active_style, active); active_style = options_get_style(oo, "window-active-style");
window = options_get_style(oo, "window-style"); style = options_get_style(oo, "window-style");
style_copy(&w->style, window);
style_copy(&wp->cached_active_style, active_style);
style_copy(&wp->cached_style, style);
} else { } else {
active = &w->active_style; active_style = &wp->cached_active_style;
window = &w->style; style = &wp->cached_style;
} }
pane = &wp->style;
if (gc->fg == 8) { if (gc->fg == 8) {
if (pane->gc.fg != 8) if (wp == wp->window->active && active_style->gc.fg != 8)
gc->fg = pane->gc.fg; gc->fg = active_style->gc.fg;
else if (wp == w->active && active->gc.fg != 8)
gc->fg = active->gc.fg;
else else
gc->fg = window->gc.fg; gc->fg = style->gc.fg;
if (gc->fg != 8) { if (gc->fg != 8) {
c = window_pane_get_palette(wp, gc->fg); c = window_pane_get_palette(wp, gc->fg);
@@ -2536,12 +2683,10 @@ tty_default_colours(struct grid_cell *gc, struct window_pane *wp)
} }
if (gc->bg == 8) { if (gc->bg == 8) {
if (pane->gc.bg != 8) if (wp == wp->window->active && active_style->gc.bg != 8)
gc->bg = pane->gc.bg; gc->bg = active_style->gc.bg;
else if (wp == w->active && active->gc.bg != 8)
gc->bg = active->gc.bg;
else else
gc->bg = window->gc.bg; gc->bg = style->gc.bg;
if (gc->bg != 8) { if (gc->bg != 8) {
c = window_pane_get_palette(wp, gc->bg); c = window_pane_get_palette(wp, gc->bg);

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)
{ {

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