462 Commits
2.5 ... 2.7

Author SHA1 Message Date
nicm
8a81993ae1 Do not crash on empty window, reported by Jamie Macdonald in GitHub
issue 1299. Patch from Thomas Adam.
2018-03-29 13:19:19 +01:00
nicm
785ce66ab9 Fix a regression: do not warn about no client in rename-window. 2018-03-26 07:39:12 +01:00
nicm
ea295ac397 Fix size calculation when spreading out panes. 2018-03-23 10:07:54 +00:00
Nicholas Marriott
2cd0ba5057 CHANGES for 2.7. 2018-03-22 11:25:58 +00:00
Nicholas Marriott
ced74bd72c 2.7-rc. 2018-03-22 11:18:05 +00:00
Nicholas Marriott
50e3e3e72f Remove EVENT_* variables from environment after initializing libevent so they
are not carried into child processes; from Henry Qin.
2018-03-21 08:15:15 +00:00
Thomas Adam
c8a706117f Merge branch 'obsd-master' 2018-03-17 18:02:26 +00:00
nicm
0b3911631b Fix negative window index range check (> not <). Reported by Juan Pablo
in GitHub issue 1283.
2018-03-17 16:48:17 +00:00
Thomas Adam
0ca78ee51f Merge branch 'obsd-master' 2018-03-16 16:02:28 +00:00
nicm
f87d80737e Insert full size panes at the right position, from KOIE Hidetaka in
GitHub issue 1284.
2018-03-16 15:15:39 +00:00
Thomas Adam
9fd9952752 Merge branch 'obsd-master' 2018-03-08 10:02:26 +00:00
nicm
19f3a5c612 Add a missing client-detached hook when the server shuts down, and do
not exit until jobs started from run-shell/if-shell have finished (add a
job flags member and a flag to indicate other jobs). GitHub issue 1245.
2018-03-08 08:09:10 +00:00
Thomas Adam
3c451a64b5 Merge branch 'obsd-master' 2018-03-05 14:02:29 +00:00
nicm
85c48aafff For some reason tmux treats SGR 10 as SGR 0. It has done since the first
version and I'm not sure why since no other terminal appears to. Change
to just ignore SGR 10 instead.
2018-03-05 12:32:28 +00:00
Nicholas Marriott
61ea49c6dd Fix cmp use. 2018-03-02 15:08:34 +00:00
Thomas Adam
3b8f92359d Merge branch 'obsd-master' 2018-03-01 14:02:27 +00:00
nicm
182357f24b Expand formats in window and session names. 2018-03-01 12:53:08 +00:00
Thomas Adam
0ca9664ecf Merge branch 'obsd-master' 2018-02-28 10:02:29 +00:00
nicm
508e2f0b3a Add -Z flag to choose-tree, choose-client, choose-buffer to
automatically zoom the pane when the mode is entered and unzoom when it
exits, assuming the pane is not already zoomed. Add -Z to the default
key bindings.
2018-02-28 08:55:44 +00:00
Thomas Adam
f5a37d0071 Merge branch 'obsd-master' 2018-02-26 10:02:30 +00:00
nicm
4d72b8fff7 C-g for modes too, from Mike Hamrick. 2018-02-26 08:09:56 +00:00
Thomas Adam
9464b94f64 Merge branch 'obsd-master' 2018-02-22 14:02:30 +00:00
Thomas Adam
c2aa40449c Merge branch 'obsd-master' 2018-02-22 12:02:31 +00:00
nicm
3f3f13fbd7 Remove an unused variable. 2018-02-22 11:42:41 +00:00
nicm
e97daead43 Check prefix when retrying so it is checked while repeat flag is
set. GitHub issue 1239.
2018-02-22 10:58:12 +00:00
nicm
623f4b12d3 Add exit-empty option to exit server if no sessions (defaults to on). 2018-02-22 10:54:51 +00:00
Thomas Adam
5a44e18490 Merge branch 'obsd-master' 2018-02-20 12:02:23 +00:00
nicm
ab6f0bb348 Do not leak memory when working out job name in formats. 2018-02-20 10:43:46 +00:00
Thomas Adam
4e8b1b9ac2 Merge branch 'obsd-master' 2018-02-19 22:02:29 +00:00
Nicholas Marriott
d81aec2439 Update CHANGES. 2018-02-19 21:24:17 +00:00
nicm
6ae04dd5a0 Support ISO colon-separated SGR. 2018-02-19 21:20:10 +00:00
Thomas Adam
968296bb07 Merge branch 'obsd-master' 2018-02-16 12:02:29 +00:00
nicm
320abba341 Reflowing the grid in-place involved way too much memmove() for a big
performance cost with a large history. Instead change back to using a
second grid and copying modified lines over which is much faster (this
doesn't revert to the old code however which didn't support UTF-8
properly). GitHub issue 1249.
2018-02-16 09:51:41 +00:00
Thomas Adam
88711e885e Merge branch 'obsd-master' 2018-02-05 10:02:31 +00:00
nicm
7f4513ec34 Add struct status_line to hold status line members of struct client, not
used yet but will be soon. From Thomas Adam.
2018-02-05 08:21:54 +00:00
Thomas Adam
2d5101621b Merge branch 'obsd-master' 2018-02-05 08:02:27 +00:00
nicm
0817132f97 Show if filter is active/no matches in modes. 2018-02-05 06:51:41 +00:00
Thomas Adam
24abfb72eb Merge branch 'obsd-master' 2018-02-04 12:02:24 +00:00
nicm
fe7a871a23 Upstream ncurses has introduced terminfo capabilities to specify RGB
colour ("true" or "direct" colour). These consist of new entries (such
as "xterm-direct") which have a different setaf/setab implementation,
colors and pairs set to 0x1000000 and 0x10000, and a new RGB flag.

The setaf/setab definitions seem to be geared towards what ncurses (or
emacs maybe) needs, in that the new versions do only ANSI and RGB
colours (they can't be used for the 256 colour palette); they rely on
the silly ISO colon-separated version of SGR; and they use a weird
multiplication scheme so they still only need one argument. The higher
values of colors and pairs require a recent ncurses to parse.

tmux can use the RGB flag to detect RGB colour support (keeping the old
Tc extension for backwards compatibility for now). However, as we still
want to send 256 colour information unchanged when possible, the new
setaf/setab are awkward. So when RGB is present, reserve setaf/setab
only for ANSI colours and use the escape sequences directly for 256 and
RGB colours. (To my knowledge no recent terminal uses unusual escape
sequences for these in any case.)
2018-02-04 10:10:39 +00:00
Nicholas Marriott
4e4c500879 Add more to TODO. 2018-01-29 12:46:52 +00:00
Nicholas Marriott
0407d847a4 Add to TODO. 2018-01-29 12:44:31 +00:00
Thomas Adam
19afd842bf Merge branch 'obsd-master' 2018-01-18 16:02:25 +00:00
nicm
17d4c39f24 Discard all but the last line when reading from a #() command - the
callback is just going to be fired again straight away to go through all
the lines, it is better just to use the last one straight away.
2018-01-18 14:28:11 +00:00
Thomas Adam
d9e740f86d Merge branch 'obsd-master' 2018-01-18 08:02:30 +00:00
nicm
c9037fde1c Remove unused hooks_run function, from Thomas Adam. 2018-01-18 07:10:53 +00:00
Thomas Adam
84ddc72744 Merge branch 'obsd-master' 2018-01-17 11:26:10 +00:00
nicm
75842bfe66 Fix drawing of ACS characters (they need to go character-at-a-time),
accidentally broken in last commit.
2018-01-16 17:03:18 +00:00
nicm
5849b73b81 Add -I to pipe-pane to connect pane stdin as well as stdout, suggested
by Kristof Kovacs in GitHub issue 1186.
2018-01-16 09:00:38 +00:00
Nicholas Marriott
58e9d12f23 msys is apparently a cygwin variant that doesn't say it is cygwin... 2018-01-16 08:29:15 +00:00
nicm
53b25635da Another redundant check, GitHub issue 1219. 2018-01-15 15:30:03 +00:00
nicm
481703d669 Some unused code, GitHub issue 1219. 2018-01-15 15:27:03 +00:00
nicm
b0c1cefeda Do not collect top-bit-set characters in case they need to be replaced. 2018-01-12 16:43:47 +00:00
nicm
2c5a6f9af5 Simplify character replacement on non-UTF-8 terminals and make a common
function.
2018-01-12 16:41:00 +00:00
nicm
c03565611e Simplify UTF-8 states down into one state. 2018-01-12 16:32:12 +00:00
nicm
f32fd2df69 Improve error message if creating socket parent directory fails, from
Thomas Adam for GitHub issue 1215.
2018-01-12 10:22:02 +00:00
nicm
829fe38ab1 Improve logging for layout cells. 2018-01-12 10:16:03 +00:00
Thomas Adam
06684c93de Merge branch 'obsd-master' 2018-01-01 12:01:13 +00:00
nicm
d17c90583a Prefer PWD for current directory if present in client, from Wei Zhao in
GitHub issue 1183.
2018-01-01 11:19:08 +00:00
nicm
fe26f977e6 Add C-g at command prompt for emacs people, GitHub issue 1213. 2018-01-01 11:03:54 +00:00
Thomas Adam
6e99a2f4bb Merge branch 'obsd-master' 2017-12-31 22:01:15 +00:00
nicm
c9896d9554 Initialize the size of new panes created by the even-* layout correctly;
reported by Andreas Kahari and Anton Lindqvist.
2017-12-31 20:00:44 +00:00
Thomas Adam
ad417f6eb7 Merge branch 'obsd-master' 2017-12-28 14:01:14 +00:00
nicm
299c552e33 Redrawing status is needed after changing window flags or title does not
update if status line is off, GitHub issue 1191.
2017-12-28 12:10:50 +00:00
Thomas Adam
cd46568ebe Merge branch 'obsd-master' 2017-12-27 14:43:20 +00:00
nicm
937f8ed095 Draw command prompt correctly with status line off. 2017-12-27 13:55:42 +00:00
Nicholas Marriott
6ce8fe0537 Still need to globfree on failure. 2017-12-27 13:54:37 +00:00
nicm
c363c236aa Fix memory leak in screen_redraw_make_pane_status. 2017-12-22 23:16:41 +00:00
Thomas Adam
e19df0e869 Merge branch 'obsd-master' 2017-12-22 12:01:22 +00:00
nicm
7ba5ad4cfb Do not try to set default value on user options (they don't have one),
from Charles Howard in GitHub issue 1161.
2017-12-22 10:18:51 +00:00
nicm
5c82432200 Remove duplicate WheelUp/WheelDown entries in list, GitHub issue 1184. 2017-12-22 10:16:36 +00:00
Thomas Adam
74ecc866cf Merge branch 'obsd-master' 2017-12-19 16:01:20 +00:00
nicm
b20a00f93e Report better error from server when socket create fails, GitHub issue
1201.
2017-12-19 15:00:39 +00:00
Thomas Adam
641a885af8 Merge branch 'obsd-master' 2017-12-19 00:01:18 +00:00
nicm
62144b9f57 Do not try to put more in command message than will fit when sending
(the server will treat as a fatal error). GitHub issue 1200.
2017-12-18 22:13:36 +00:00
Thomas Adam
43a1294ed9 Merge branch 'obsd-master' 2017-12-18 14:01:18 +00:00
nicm
58f6456af7 Remove unused variable from Thomas Adam. 2017-12-18 12:39:34 +00:00
Thomas Adam
2c6af068d7 Merge branch 'obsd-master' 2017-11-17 12:01:17 +00:00
nicm
695dc5a153 Allow formats in selectp -T, from Thomas Adam. 2017-11-17 09:52:18 +00:00
Thomas Adam
5fddddbe21 Merge branch 'obsd-master' 2017-11-16 12:01:18 +00:00
nicm
e5ae9dd53d Add -and-cancel variants for scrolling commands to exit copy mode when
the bottom is reached, from Stephen Hicks.
2017-11-16 11:16:15 +00:00
Thomas Adam
102df8dc80 Merge branch 'obsd-master' 2017-11-15 22:01:22 +00:00
Nicholas Marriott
e58d16b2df Add to CHANGES. 2017-11-15 20:14:49 +00:00
Thomas Adam
e755ca37b3 Merge branch 'obsd-master' 2017-11-15 20:01:22 +00:00
nicm
3b649d2fcd Add a common function for spreading out cells and use it for the two
even layouts and to add a -E flag to select-layout to spread out cells
evenly without changing parent cells.
2017-11-15 19:59:27 +00:00
nicm
533a5719c5 Completely rewrite the reflow code to correctly handle double width
characters (previously they were not accounted for).
2017-11-15 19:21:24 +00:00
nicm
aeda2e5808 If there is a double width character at the very end of the line with
not enough room to draw it, just leave it out.
2017-11-15 19:18:57 +00:00
Thomas Adam
392da897ff Merge branch 'obsd-master' 2017-11-13 14:01:18 +00:00
nicm
d81fa579c3 When searching in copy mode, do not scroll if the result is already on
screen. GitHub issue 1150.
2017-11-13 11:49:11 +00:00
Thomas Adam
515da63d2b Merge branch 'obsd-master' 2017-11-10 00:01:19 +00:00
nicm
384736e955 If we successfully change the directory, set PWD too to give the shell a
hint in case of symlinks.
2017-11-09 23:02:13 +00:00
Thomas Adam
6f3b6c8d92 Merge branch 'obsd-master' 2017-11-03 18:01:21 +00:00
nicm
a2681ffcee Clear key properly if on space with nothing in it. 2017-11-03 17:11:20 +00:00
nicm
50a5f84cb4 Support mouse on preview in tree mode. 2017-11-03 17:02:33 +00:00
Thomas Adam
24c387206c Merge branch 'obsd-master' 2017-11-03 16:01:17 +00:00
nicm
ba93a647f1 Change mouse in modes so that one click moves the cursor and a double
click chooses the line.
2017-11-03 14:23:44 +00:00
Thomas Adam
e1606172dd Merge branch 'obsd-master' 2017-11-03 00:01:20 +00:00
Thomas Adam
c9ec33d0d0 Merge branch 'obsd-master' 2017-11-02 22:01:20 +00:00
nicm
43264dfbf4 Make the mode draw function use the parent screen directly rather than
its own to avoid copying twice.
2017-11-02 22:00:42 +00:00
nicm
8d37f699ad Add a "fast" version of screen_write_copy for tree mode that doesn't do
all the checks and selection and marking stuff needed for copy mode.
2017-11-02 21:29:17 +00:00
Thomas Adam
8c29f7413b Merge branch 'obsd-master' 2017-11-02 20:01:26 +00:00
nicm
17655e5ba6 Format for group list of "other sessions" is a bit weird, just list all
the sessions in the group.
2017-11-02 18:52:05 +00:00
nicm
95850e1aca Tweak previous slightly so that current session is chosen if it is in
the group rather than first.
2017-11-02 18:43:51 +00:00
nicm
c1f62f1fde Only show the first member of session groups in tree mode (-G flag
disables).
2017-11-02 18:27:35 +00:00
nicm
3887d95bca There is no point in reflowing panes which have not changed width. 2017-11-02 18:26:38 +00:00
Thomas Adam
cf782c4f54 Merge branch 'obsd-master' 2017-10-26 10:01:18 +01:00
nicm
e91e8a2a6c Fix crash exiting command prompt (from Alex Maese in GitHub issue 1139)
and a man page tweak from jmc.
2017-10-26 08:17:12 +00:00
Thomas Adam
d36ac3db15 Merge branch 'obsd-master' 2017-10-25 18:01:17 +01:00
nicm
be4c01697c Note that notifications are also hooks. 2017-10-25 15:20:10 +00:00
Thomas Adam
0072bc65e6 Merge branch 'obsd-master' 2017-10-25 16:01:22 +01:00
Nicholas Marriott
37f83adca8 Update CHANGES. 2017-10-25 15:26:54 +01:00
nicm
578a63bbc9 Default allow-rename to off because it is ridiculous that applications
are even able to do this and confusing when they do.
2017-10-25 14:14:52 +00:00
Thomas Adam
6b83ca0077 Merge branch 'obsd-master' 2017-10-25 14:01:26 +01:00
nicm
78ae4ee82c h/l keys for expand and collapse. 2017-10-25 12:13:20 +00:00
Nicholas Marriott
d6edd06749 Merge branch 'master' of github.com:tmux/tmux 2017-10-25 12:29:23 +01:00
Nicholas Marriott
6e8d29e9a2 Update TODO. 2017-10-25 12:29:10 +01:00
nicm
8dd776106d Add P key to paste tagged in buffer mode, and trim some code that should
no longer be necessary.
2017-10-25 11:26:11 +00:00
Thomas Adam
e85213a944 Merge branch 'obsd-master' 2017-10-22 15:47:07 +01:00
nicm
26f1857154 Use window_pane_index() when drawing pane numbers (so pane-base-index is
applied), from Thomas Adam. GitHub issue 1125.
2017-10-22 13:16:54 +00:00
Nicholas Marriott
f7a037ba26 Apparently vim(1) now has syntax highlighting built in, GitHub issue 1124. 2017-10-22 14:14:14 +01:00
Thomas Adam
61114c6c72 Merge branch 'obsd-master' 2017-10-20 16:01:17 +01:00
nicm
d3e8709ab5 Clear status line with spaces again so reverse works, spotted by sthen. 2017-10-20 13:10:54 +00:00
Nicholas Marriott
37531673a3 Need compat for queue.h. 2017-10-20 13:26:54 +01:00
Thomas Adam
31901e3c07 Merge branch 'obsd-master'
Conflicts:
	server-fn.c
2017-10-20 12:36:29 +01:00
Nicholas Marriott
a34de2e378 Save and restore LIBS when checking for b64_ntop, reported by Ralf Friedl. 2017-10-17 10:35:35 +01:00
nicm
2f6935a630 Infrastructure for drawing status lines of more than one line in height,
still only one is allowed but this lets tmux draw bigger ones.
2017-10-16 19:30:53 +00:00
nicm
a5fd5782f8 Show exit status and time in the remain-on-exit pane text, mostly from
Timo Boettcher in GitHub issue 1103.
2017-10-12 11:32:27 +00:00
Nicholas Marriott
fb02df66cc Merge branch 'master' of github.com:tmux/tmux 2017-10-12 11:56:32 +01:00
Nicholas Marriott
d10def5b0b Check missed during merge. 2017-10-12 11:56:06 +01:00
Thomas Adam
2357bfb254 Merge branch 'obsd-master' 2017-10-11 16:01:17 +01:00
Thomas Adam
87babfa473 Merge branch 'obsd-master' 2017-10-11 14:01:17 +01:00
nicm
eb9839fd32 Box around label in preview. 2017-10-11 12:57:49 +00:00
nicm
6e5121be7e Clear to end of line properly with UTF-8 present. 2017-10-11 11:26:58 +00:00
Thomas Adam
4efd41f3af Merge branch 'obsd-master' 2017-10-11 10:01:19 +01:00
nicm
99351c9cae Add C-n and C-p keys for tree mode, and choose the right initial line
when no panes.
2017-10-11 08:08:16 +00:00
Nicholas Marriott
60074a6bc6 Merge branch 'master' of github.com:tmux/tmux 2017-10-11 08:03:55 +01:00
Nicholas Marriott
a3967de9a5 Include headers if found regardless of forkpty. 2017-10-11 08:03:31 +01:00
Thomas Adam
044019d9d6 Merge branch 'obsd-master' 2017-10-09 14:01:16 +01:00
nicm
db44151a37 kind should be S-Down not S-Up. 2017-10-09 11:35:35 +00:00
Thomas Adam
ceab7154d4 Merge branch 'obsd-master' 2017-10-08 18:40:41 +01:00
nicm
0b4c408168 Fix description of history_size, from Campbell Barton. 2017-10-08 16:45:01 +00:00
Thomas Adam
f069c0ba09 Merge branch 'obsd-master' 2017-10-06 20:01:17 +01:00
nicm
9c4caf49a2 Support %else in config files to match %if, from Brad Town in GitHub
issue 1071.
2017-10-06 18:02:30 +00:00
Thomas Adam
2be01ab4ec Merge branch 'obsd-master' 2017-10-06 13:33:32 +01:00
nicm
b462063cd5 Add -- to some key bindings so leading -s work. 2017-10-05 13:43:34 +00:00
Nicholas Marriott
8aaf86a6ea Merge branch '2.6-rc' 2017-10-05 14:39:33 +01:00
Nicholas Marriott
bd71cbbe27 2.6. 2017-10-05 14:31:23 +01:00
nicm
88517ceebb Add support for the xterm(1) title stack, from Brad Town, GitHub issue
1075.
2017-10-05 13:29:18 +00:00
nicm
6a292f09ba When writing batches of characters to the screen, we need to clear
padding or later UTF-8 characters could be displayed incorrectly. GitHub
issue 1090.
2017-10-05 08:12:24 +00:00
nicm
71ec616e4d Initialize alerts timer event where it is used, avoids crash with new windows. 2017-09-22 17:58:30 +01:00
Thomas Adam
ff526e43de Merge branch 'obsd-master' 2017-09-22 12:01:13 +01:00
nicm
d563aa7c7b Initialize alerts timer event where it is used, avoids crash with new windows. 2017-09-22 09:04:46 +00:00
Thomas Adam
ae5a62a514 Merge branch 'obsd-master' 2017-09-13 10:01:10 +01:00
nicm
c86d83f835 Remove unused (always 1) arguments from some functions, from Daniel
Mueller in GitHub issue 1073.
2017-09-13 07:31:07 +00:00
Thomas Adam
9a1b9f15a1 Merge branch 'obsd-master' 2017-09-11 22:01:10 +01:00
nicm
af2c7ce646 Check event is initialized before delete; fixes crash reported by
Michael Nickerson in GitHub issue 1068.
2017-09-11 20:11:45 +00:00
Nicholas Marriott
b541a97821 2.6-rc3. 2017-09-11 10:12:25 +01:00
Nicholas Marriott
a8b84b7cfa 2.6-rc3. 2017-09-11 10:08:28 +01:00
Nicholas Marriott
495e2ed17f Merge branch 'master' into 2.6-rc 2017-09-11 10:08:15 +01:00
Thomas Adam
d8c397d1b7 Merge branch 'obsd-master' 2017-09-11 10:01:11 +01:00
nicm
d8d6c2746e Mention that filter is a format. 2017-09-11 06:53:06 +00:00
nicm
6fdaaa0637 Do not free more lines than are available in the history. 2017-09-11 06:40:46 +00:00
Nicholas Marriott
034b19b734 2.6-rc2. 2017-09-10 16:08:22 +01:00
Nicholas Marriott
cb8eba1530 Merge branch 'master' into 2.6-rc 2017-09-10 16:07:44 +01:00
Thomas Adam
7aa8b8a25c Merge branch 'obsd-master' 2017-09-10 16:01:14 +01:00
Nicholas Marriott
abcbfcb0e8 Merge branch 'master' into 2.6-rc 2017-09-10 15:38:02 +01:00
nicm
70bc07a358 Previously, extended cell data was never reduced in size even when the
cell was overwritten. With a large history this can be a substantial
amount of memory. To reduce this, compact each extended cell list to
only cells in use as it is scrolled off the visible screen into the
history. From Dan Aloni in GitHub issue 1062.
2017-09-10 14:36:12 +00:00
Thomas Adam
7f83b53027 Merge branch 'obsd-master'
Conflicts:
	server-client.c
2017-09-10 11:39:45 +01:00
nicm
8405fcdd9b Apply timeout to CAN and RS which also wait for ST. 2017-09-10 08:01:23 +00:00
nicm
f56f09ea38 Fix a few errors in how the selected line is chosen after resize,
reported by Felix Rosencrantz in GitHub issue 1059.
2017-09-08 16:28:41 +00:00
Nicholas Marriott
c62cfe64c8 Add to CHANGES. 2017-09-08 14:22:34 +01:00
nicm
78cf3c14ca When removing a key table clear it out of clients, fixes issue with
unbind -a reported by Thomas Sattler.
2017-09-08 08:45:27 +00:00
nicm
89e057dc4a Do not fail if unset an option that is already unset, reported by Thomas
Sattler.
2017-09-07 13:18:44 +00:00
nicm
466066c3a1 Do not attempt to use TIOCSWINSZ on a -1 file descriptor (possible if
the pane has already died).
2017-09-06 07:12:41 +00:00
Thomas Adam
ff3d05d92f Merge branch 'obsd-master' 2017-09-04 12:01:11 +01:00
nicm
eadd79acec Move to current mouse position not last when clcking in copy mode; fixes
GitHub issue 1055. Also a man page fix from jmc.
2017-09-04 09:18:51 +00:00
Thomas Adam
d019821281 Merge branch 'obsd-master' 2017-09-02 20:01:18 +01:00
nicm
f4848b437f Add selectp -T to set pane title. 2017-09-02 17:51:54 +00:00
Nicholas Marriott
e941e532fa Mention GitHub for code. 2017-08-30 21:49:31 +01:00
Nicholas Marriott
a1986c5973 Add to CHANGES. 2017-08-30 21:24:16 +01:00
Nicholas Marriott
6e2b3f435a Add to CHANGES. 2017-08-30 21:23:26 +01:00
Nicholas Marriott
07d3c4d882 Merge branch 'master' into 2.6-rc 2017-08-30 20:04:37 +01:00
Thomas Adam
f81e87f1e2 Merge branch 'obsd-master' 2017-08-30 20:01:11 +01:00
nicm
6abfd9b8ff Instead of overloading the line clear function to mean free if
background is default (8), introduce an explicit free function and use
it where a free alone is needed. Likewise, use memmove directly rather
than grid_move_lines where it makes sense. Based on a memory leak fix by
Dan Aloni in GitHub issue 1051.
2017-08-30 18:13:47 +00:00
Nicholas Marriott
07c679b52d Merge branch 'master' into 2.6-rc 2017-08-30 12:04:09 +01:00
Nicholas Marriott
b4c633cc40 Merge branch 'master' of github.com:tmux/tmux 2017-08-30 12:03:59 +01:00
Thomas Adam
54c5070767 Merge branch 'obsd-master' 2017-08-30 12:01:10 +01:00
nicm
17cf1b21c6 Pass flags into cmd_find_from_* to fix prefer-unattached, reported by
Thomas Sattler.
2017-08-30 10:33:57 +00:00
Nicholas Marriott
8f364053ca Add to TODO. 2017-08-30 11:21:20 +01:00
Nicholas Marriott
2e4e521629 2.6-rc version. 2017-08-30 09:34:27 +01:00
Nicholas Marriott
fa20f19494 Fix position of -v, pointed out by Thomas Sattler. 2017-08-30 09:33:53 +01:00
Nicholas Marriott
c1d8b0f74e Back to master. 2017-08-29 22:19:46 +01:00
Nicholas Marriott
3815e4f05e This is not true now... 2017-08-29 22:19:27 +01:00
Nicholas Marriott
72488b526b Merge branch 'master' into 2.6-rc 2017-08-29 22:06:22 +01:00
Nicholas Marriott
5fec6c598e Merge branch 'master' of github.com:tmux/tmux 2017-08-29 22:06:06 +01:00
Thomas Adam
dee6bb5a31 Merge branch 'obsd-master' 2017-08-29 22:01:11 +01:00
Nicholas Marriott
566b9623b3 Merge branch 'master' into 2.6-rc 2017-08-29 21:42:15 +01:00
Nicholas Marriott
3f3fb43850 More style. 2017-08-29 21:42:05 +01:00
Nicholas Marriott
2248b886fe 2.6-rc version. 2017-08-29 21:37:51 +01:00
Nicholas Marriott
82b30f2322 Style of headings. 2017-08-29 21:34:56 +01:00
Nicholas Marriott
6b841a036a Fix example from Adam Spiers. 2017-08-29 21:32:09 +01:00
nicm
a7d1ee5433 Redraw rectangle selections properly when cursor at end, GitHub issue 992. 2017-08-29 20:26:25 +00:00
Thomas Adam
0f7160eb2f Merge branch 'obsd-master' 2017-08-29 12:01:25 +01:00
Nicholas Marriott
91d6bff8b8 Merge branch 'master' of github.com:tmux/tmux 2017-08-29 11:13:54 +01:00
Nicholas Marriott
5cdccf78a1 Update CHANGES. 2017-08-29 11:13:35 +01:00
nicm
5fc0be5045 Support REP escape sequence (\033[b). 2017-08-29 09:28:45 +00:00
nicm
9852bd743c Check for complete keys before escape prefix, allows keys to be defined
with a leading escape. GitHub issue 1048.
2017-08-29 09:18:48 +00:00
Thomas Adam
7d3bf6453e Merge branch 'obsd-master' 2017-08-28 14:01:17 +01:00
nicm
fe4467ad2b Do not forbid targets to specify non-visible panes - the checks for
visibility are better where the target is used. GitHub issue 1049.
2017-08-28 12:36:38 +00:00
Thomas Adam
b2322b3893 Merge branch 'obsd-master' 2017-08-27 11:48:44 +01:00
nicm
fccfc4e4be Do not allow the current line of screen when the preview is toggled,
from Thomas Adam.
2017-08-27 09:08:36 +00:00
Thomas Adam
e65cc09276 Merge branch 'obsd-master' 2017-08-27 10:01:15 +01:00
nicm
25cf126de8 Use kind and kri for S-Up/Down as well as kUP and kDN. 2017-08-27 08:33:55 +00:00
Thomas Adam
1492c9d7d9 Merge branch 'obsd-master' 2017-08-24 12:01:10 +01:00
nicm
3c63ad4a9c When tty is error or closed, remove client. Reported by Thomas Sattler. 2017-08-24 08:48:37 +00:00
Thomas Adam
3b40f8e42c Merge branch 'obsd-master' 2017-08-23 12:01:13 +01:00
nicm
08b125194e Key (v) and flag (-N) to toggle preview in choose modes. 2017-08-23 09:39:11 +00:00
nicm
1d60dd5872 Fix searching when match is at end of line, from Brad Town. 2017-08-23 09:18:22 +00:00
nicm
f0ce29c341 Allow multiple bells even if there is an existing bell (but not activity
or silence), from Brad Town.
2017-08-23 09:16:39 +00:00
nicm
e1b3dc89d2 Run alert hooks based on the options rather than unconditionally, from
Brad Town.
2017-08-23 09:14:21 +00:00
Nicholas Marriott
0f708dd6e2 Add to TODO. 2017-08-22 13:02:20 +01:00
Thomas Adam
730312e60f Merge branch 'obsd-master' 2017-08-22 00:01:10 +01:00
nicm
bbe9da063e Same as previous for \r alone. 2017-08-21 21:02:58 +00:00
nicm
7ec2a2b9ce Do not emit \r\n to move to column 0 if there are margins, because it
will instead move to the margin left.
2017-08-21 21:01:21 +00:00
Thomas Adam
ccdc369025 Merge branch 'obsd-master' 2017-08-20 00:01:22 +01:00
nicm
768740ae98 Fix example for user-keys. 2017-08-19 20:40:16 +00:00
Thomas Adam
07a13697e1 Merge branch 'obsd-master' 2017-08-17 12:01:17 +01:00
nicm
8daa1d5f54 Add monitor-bell window option to match the activity and silence
options, from Brad Town.
2017-08-17 08:37:38 +00:00
Nicholas Marriott
de86bf1856 Add to CHANGES. 2017-08-16 16:23:58 +01:00
Thomas Adam
2103a09430 Merge branch 'obsd-master' 2017-08-16 14:01:15 +01:00
nicm
c6a8ad23a1 Add -d flag to display-panes to specify timeout, and make 0 mean no
timeout. From Laurens Post.
2017-08-16 12:12:54 +00:00
nicm
c1ec28a34b Rename BELL_* values to ALERT_* now they are used by more than bells,
based on a diff from Brad Town.
2017-08-16 11:46:08 +00:00
Thomas Adam
0824850bbc Merge branch 'obsd-master' 2017-08-09 16:01:10 +01:00
Thomas Adam
27c3852103 Merge branch 'obsd-master'
Conflicts:
	tmux.1
2017-08-09 15:07:18 +01:00
nicm
ac2ba0961b Fix filtering so it works after the change to only show windows if they
have multiple panes.
2017-08-09 13:44:36 +00:00
Nicholas Marriott
237b7a50f4 Update CHANGES and TODO. 2017-08-09 12:48:54 +01:00
nicm
5dd5543fe4 Add -F to choose-tree, choose-client, choose-buffer to specify the
format of each line, as well as adding a couple of formats needed for
the default display.
2017-08-09 11:43:45 +00:00
Thomas Adam
4bb5bb9450 Merge branch 'obsd-master' 2017-08-08 12:01:15 +01:00
nicm
31b06571aa Hooks for after-select-pane and after-select-window. 2017-08-08 09:21:20 +00:00
Thomas Adam
e7b1e05bbd Merge branch 'obsd-master' 2017-08-02 14:01:10 +01:00
nicm
6f9b9655d7 Add selection_present format so commands in copy mode can use it, GitHub
issue 1028.
2017-08-02 11:10:48 +00:00
Nicholas Marriott
45ee118b26 Merge branch 'master' of github.com:tmux/tmux 2017-07-28 15:14:35 +01:00
Nicholas Marriott
0d6fc7eb1e I already mentioned these... revert previous. 2017-07-28 15:14:01 +01:00
Thomas Adam
ed8ddf2449 Merge branch 'obsd-master' 2017-07-28 14:01:13 +01:00
nicm
b4c9f6edba Show pane title in window list for windows with only one pane. 2017-07-28 10:59:58 +00:00
Nicholas Marriott
a704f57971 This is a big meaningless. 2017-07-27 23:25:39 +01:00
Nicholas Marriott
6b60a5c6d6 Update CHANGES. 2017-07-27 23:22:54 +01:00
Thomas Adam
147740ed40 Merge branch 'obsd-master' 2017-07-27 14:01:13 +01:00
nicm
3df7c91f1a Add pane_at_left/right/top/bottom formats, from Amos Bird. 2017-07-27 10:42:05 +00:00
Thomas Adam
58744de3eb Merge branch 'obsd-master' 2017-07-26 18:01:16 +01:00
nicm
b1bd0c7fc1 Always reset the alerts timer so it works even if activity and silence
are enabled on the same window.
2017-07-26 16:16:25 +00:00
nicm
76887b1d27 Make bell, activity and silence alerting more consistent:
- remove the bell-on-alert option;

- add activity-action and silence-action options with the same possible
  values as the existing bell-action;

- add "both" value for the visual-bell, visual-activity and
  visual-silence options to trigger both a bell and a message.

This means all three work the same way. Based on changes from Yvain Thonnart.
2017-07-26 16:14:08 +00:00
Nicholas Marriott
06a2644ab2 Update CHANGES. 2017-07-26 11:25:33 +01:00
Nicholas Marriott
ef9afddd1a Merge branch 'master' of github.com:tmux/tmux 2017-07-26 08:41:45 +01:00
Nicholas Marriott
3065b21375 Mention autoconf and automake. 2017-07-26 08:38:37 +01:00
Thomas Adam
358df10191 Merge branch 'obsd-master' 2017-07-22 02:01:13 +01:00
nicm
3bb426d92c Use the actual width written rather than the possible width to clear. 2017-07-21 22:55:45 +00:00
Thomas Adam
26db50d6df Merge branch 'obsd-master' 2017-07-21 16:01:13 +01:00
nicm
8c6ad55320 Trim trailing spaces from full line when it is clearly OK to do so. 2017-07-21 14:25:29 +00:00
nicm
e0d49ad758 Allow ispunct() as well as isalnum() when parsing initial window names. 2017-07-21 12:58:02 +00:00
Thomas Adam
e725b96a59 Merge branch 'obsd-master' 2017-07-21 12:01:16 +01:00
nicm
11e2af6df7 Add -c for respawn-pane and respawn-window, from J Raynor. 2017-07-21 09:17:19 +00:00
Thomas Adam
acbbc93501 Merge branch 'obsd-master' 2017-07-14 22:01:10 +01:00
nicm
932f6cfbfc Because ignore SIGCHLD early, letting signal_del restore it doesn't work
correctly, so set it explicitly back to default (and the others for good
measure).
2017-07-14 18:49:07 +00:00
Thomas Adam
1265e212e4 Merge branch 'obsd-master' 2017-07-14 10:01:11 +01:00
Nicholas Marriott
e4cd8751a2 Update CHANGES. 2017-07-14 09:14:23 +01:00
nicm
2678fe53f5 Fix redraw defer code in the presence of multiple clients - the timer
may be needed for all of them, so don't delete it on the first; and
don't skip setting the redraw flag if the timer is already running.

Reported by Pol Van Aubel in GitHub issue 1003.
2017-07-14 08:04:23 +00:00
Thomas Adam
e3698e6e1f Merge branch 'obsd-master' 2017-07-12 16:01:11 +01:00
nicm
8b84fc177c Line up keys in tree mode, and don't expand windows with one pane. From
Thomas Adam.
2017-07-12 14:31:06 +00:00
Thomas Adam
5122f3477f Merge branch 'obsd-master' 2017-07-12 14:01:13 +01:00
Thomas Adam
1076a2e26c Merge branch 'obsd-master'
Conflicts:
	cmd-pipe-pane.c
	proc.c
	tmux.c
	window.c
2017-07-12 13:43:08 +01:00
nicm
fba6140a4a Do not need to set up USR2 twice. 2017-07-12 12:35:31 +00:00
nicm
51112221ee Block signals between forking and clearing signal handlers (or calling
event_reinit) - if the child gets a signal and fires the libevent signal
handler during this period it could write a signal into the parent's
signal pipe. GitHub issue 1001 from Aaron van Geffen.
2017-07-12 10:04:51 +00:00
nicm
0453ad0146 Move signal code into proc.c. 2017-07-12 09:24:17 +00:00
nicm
ed3cfaafb2 Make shell_command a global like other stuff rather than making it an
exception and using callback argument.
2017-07-12 09:21:25 +00:00
nicm
d0d42dc4cb proc_send_s now seems unnecessary. 2017-07-12 09:07:52 +00:00
Thomas Adam
fbbf5a108b Merge branch 'obsd-master' 2017-07-10 00:01:15 +01:00
nicm
58b796608f Some extra logging to show why tmux might exit. 2017-07-09 22:33:09 +00:00
Thomas Adam
05062e7d2d Merge branch 'obsd-master' 2017-07-07 18:01:15 +01:00
nicm
bfaa885f10 Fix size of rightmost preview section. 2017-07-07 16:27:26 +00:00
nicm
9913cce3ba Add a pane_pipe format to show if pipe-pane is active, GitHub issue 990. 2017-07-07 14:39:45 +00:00
Thomas Adam
1029f2b277 Merge branch 'obsd-master' 2017-07-07 10:01:09 +01:00
nicm
1f7ca973c5 When working out the current client (for example for switch-client with
no target), prefer clients attached to the current session if there is
one. GitHub issue 995 from Jan Larres.
2017-07-07 07:13:14 +00:00
Thomas Adam
6b1ceca86a Merge branch 'obsd-master' 2017-07-05 00:01:10 +01:00
nicm
53d4ed22e8 < and > keys to scroll preview list left and right in tree mode. 2017-07-04 22:21:31 +00:00
Thomas Adam
feb044bd2d Merge branch 'obsd-master' 2017-07-04 14:01:15 +01:00
nicm
bedf8bd437 Handle 0 size of preview box in caller. 2017-07-04 12:26:14 +00:00
Thomas Adam
6cb4a3bb19 Merge branch 'obsd-master' 2017-07-04 02:01:13 +01:00
nicm
4039802fce Change session and window preview so that the current window or pane is
always shown.
2017-07-03 22:48:02 +00:00
Thomas Adam
5e98770936 Merge branch 'obsd-master' 2017-07-03 16:01:14 +01:00
nicm
6ee0afb579 Change previous to not wait for both process exit and pty close -
instead if there is a pipe-pane active, do not exit until all data is
read (including any libevent hasn't seen yet). Fixes problem reported by
Theo Buehler and still seems to solve the original issue.
2017-07-03 12:38:50 +00:00
Thomas Adam
4e01036cb6 Merge branch 'obsd-master' 2017-07-03 10:01:14 +01:00
nicm
28687f2d55 Do not close panes until process has exited and any outstanding data
has been written to the pipe-pane event if there is one. GitHub issue 991.
2017-07-03 08:16:03 +00:00
nicm
42285ac989 Try C.UTF-8 which is also a commonly useful locale on some platforms,
from Romain Francoise.
2017-07-03 08:08:30 +00:00
Thomas Adam
6fba9a39b7 Merge branch 'obsd-master' 2017-07-01 00:01:21 +01:00
nicm
fa677fc0e1 Don't write over right border. 2017-06-30 22:37:35 +00:00
nicm
b565644c81 Fix previous when we end up able to show no panes. 2017-06-30 22:36:11 +00:00
nicm
7247553c77 Try to show a better preview of sessions and windows in tree mode. 2017-06-30 22:24:08 +00:00
Thomas Adam
f059fe3ef2 Merge branch 'obsd-master' 2017-06-30 00:01:14 +01:00
nicm
8b0fd63ddb Use 100 as the example for command-alias because the defaults are from 0
to (currently) 5.
2017-06-29 22:02:19 +00:00
Thomas Adam
336beeb09a Merge branch 'obsd-master' 2017-06-28 14:01:13 +01:00
nicm
a00b0d13ed Apply the xterm key flag when needed for send-keys, fixes problem
reported by Franky Spamschleuder.
2017-06-28 11:36:39 +00:00
Thomas Adam
1ca920bbeb Merge branch 'obsd-master' 2017-06-28 10:01:16 +01:00
nicm
1e376be13d Fix visual-silence (check accidentally the wrong way round), from Brad
Town. Plus some tmux.1 fixes from jmc@.
2017-06-28 06:45:31 +00:00
Thomas Adam
6995497e5b Merge branch 'obsd-master' 2017-06-23 18:01:11 +01:00
nicm
95ed7d48c8 Add user-keys option to allow user-defined keys to be set, from Dan
Aloni.
2017-06-23 15:36:52 +00:00
Thomas Adam
a61200776d Merge branch 'obsd-master' 2017-06-16 18:01:13 +01:00
nicm
a67df17763 Tweak some logging. 2017-06-16 15:12:38 +00:00
Thomas Adam
233bae6992 Merge branch 'obsd-master' 2017-06-16 14:01:14 +01:00
nicm
d685604d04 Log terminal capabilities for each new terminal. 2017-06-16 11:50:06 +00:00
Thomas Adam
91dec25fc9 Merge branch 'obsd-master' 2017-06-14 10:01:10 +01:00
nicm
af93453190 Don't overwrite error message when it is available in
cmd_string_parse. Reported by Jimi Damon in GitHub issue 975.
2017-06-14 07:42:41 +00:00
Nicholas Marriott
6e57401610 New test. 2017-06-14 08:37:58 +01:00
nicm
dec00d3579 Add missing error message when no target, GitHub issue 971. 2017-06-14 07:37:17 +00:00
Thomas Adam
154c95d0c9 Merge branch 'obsd-master' 2017-06-13 13:48:37 +01:00
nicm
ac7080b31b Remove xterm flag from key before checking prefix, reported by Peter
Fern in GitHub issue 974.
2017-06-13 07:12:33 +00:00
Thomas Adam
a073d11c3e Merge branch 'obsd-master' 2017-06-12 14:01:14 +01:00
nicm
e028ab3476 Need to flush out the linefeed after wrapper. GitHub issue 970. 2017-06-12 10:57:35 +00:00
Thomas Adam
5362f956f0 Merge branch 'obsd-master' 2017-06-12 10:01:15 +01:00
nicm
8037159f93 Add explicit keys for the bracketed paste sequences, both to avoid mix
ups with other keys and to make logs clearer.
2017-06-12 07:04:24 +00:00
Thomas Adam
4dbab75855 Merge branch 'obsd-master' 2017-06-09 18:01:14 +01:00
nicm
adcd5aff6f Extend filters (f key) to buffer and client mode and add -f flag to
specify to command.
2017-06-09 16:01:39 +00:00
nicm
bab4da5133 Add -O option to choose-* to set initial sort order. 2017-06-09 15:29:15 +00:00
nicm
3ec28ceb9b Default sort for buffer mode should be time not name. 2017-06-09 15:17:20 +00:00
Thomas Adam
ed45052d6d Merge branch 'obsd-master' 2017-06-09 16:01:13 +01:00
Nicholas Marriott
411640c032 Merge branch 'master' of github.com:tmux/tmux 2017-06-09 15:57:13 +01:00
Nicholas Marriott
eea93638c6 Set AM_CFLAGS for ncurses libraries, reported by Peter Schow. 2017-06-09 15:56:50 +01:00
nicm
a2ca51c27a Use brackets around prompts which looks better and matches the other modes. 2017-06-09 14:00:46 +00:00
Thomas Adam
e640907d24 Merge branch 'obsd-master' 2017-06-09 12:01:17 +01:00
nicm
74b2deae1c Add a hook when the clipboard is set. 2017-06-09 09:21:24 +00:00
Nicholas Marriott
143ccd27b4 Add memmem to compat. 2017-06-09 08:53:58 +01:00
Nicholas Marriott
a4d2fa1b6d Update TODO. 2017-06-08 17:21:30 +01:00
Nicholas Marriott
065c360730 Typo. 2017-06-08 14:08:04 +01:00
Nicholas Marriott
738d9aece7 Note what to have for building Git. 2017-06-08 13:40:24 +01:00
Nicholas Marriott
8c38409aef Merge branch 'master' of github.com:tmux/tmux 2017-06-08 13:29:59 +01:00
Nicholas Marriott
61ed6425bd Move FAQ online and do not ship TODO. 2017-06-08 13:29:36 +01:00
Thomas Adam
6e8c93afdd Merge branch 'obsd-master' 2017-06-08 10:01:10 +01:00
nicm
8c4ae1c938 Add size to client descriptions in list, suggested by Greg Hurrell. 2017-06-08 07:48:04 +00:00
Thomas Adam
4aa02c3743 Merge branch 'obsd-master' 2017-06-07 18:01:13 +01:00
nicm
356fab7bcb Return 1 if name matches not 0, also fix some spaces. 2017-06-07 15:27:46 +00:00
Nicholas Marriott
9543f076fd Use osdep_get_name. 2017-06-07 16:21:01 +01:00
Thomas Adam
86d6666fe7 Merge branch 'obsd-master' 2017-06-07 16:01:10 +01:00
nicm
405cc337f3 Add simple searching (C-s and n) to the various choose modes: by name
for client and tree, and by name and content for buffer.
2017-06-07 14:37:30 +00:00
Nicholas Marriott
772dacc89b Tweak title. 2017-06-07 11:27:04 +01:00
Nicholas Marriott
696d2889f4 Mention U8, from Carles Cufi. 2017-06-07 11:19:04 +01:00
Nicholas Marriott
9316b9b57a Typo. 2017-06-07 11:15:36 +01:00
Nicholas Marriott
fb288ef9a7 Remove a bunch of out-of-date stuff from the FAQ or stuff that is documentation
for other programs.
2017-06-07 11:13:05 +01:00
Thomas Adam
7077980055 Merge branch 'obsd-master' 2017-06-06 18:01:13 +01:00
nicm
50b27c8c0d Continue and pass keys through if they are repeated keys, so that the
first key after a repeated key doesn't get lost.
2017-06-06 15:49:35 +00:00
nicm
bbc35b0b19 Do not pass a state into commands when fired on individual items in tree
mode, rely on the %% target substitution in the command for the chosen
pane and leave the default target as the current pane (where the mode
is). Otherwise, joinp and similar end up with -t and -s the
same. Reported by Jacob Niehus in GitHub issue 960.
2017-06-06 15:07:35 +00:00
nicm
d7280917da Delete input event when evbuffer_read() fails to avoid just spinning
around a dead file descriptor. Seems to fix a problem reported by Greg
Hurrell in GitHub issue 941.
2017-06-06 14:53:28 +00:00
Nicholas Marriott
78352fdd32 Add a small dance to daemon() to reattach tmux to the user's namespace (which
allows access to the clipboard) on OS X 10.10 and above.

Chis Johnsen has done much work on and documentation of this issue, the code is
copied (with some tweaks) from his reattach-to-user-namespace at:

    https://github.com/ChrisJohnsen/tmux-MacOSX-pasteboard

Tested by Enrico Ghirardi.

Will see how this goes, if it breaks stuff it might go away again.
2017-06-06 07:59:53 +01:00
Nicholas Marriott
63f8a2cb89 Merge branch 'master' of github.com:tmux/tmux 2017-06-05 12:02:10 +01:00
Nicholas Marriott
e1686c26dd Update CHANGES. 2017-06-05 12:01:58 +01:00
Thomas Adam
a999f6f876 Linux build fixes
Linux doesn't have vis.h
2017-06-05 12:00:52 +01:00
Thomas Adam
e62e17d046 Merge branch 'obsd-master'
Conflicts:
	tmux.1
	window.c
2017-06-05 11:59:38 +01:00
nicm
2f04108f3a Do not leak command, from David CARLIER. 2017-06-04 15:36:33 +00:00
Nicholas Marriott
1c83c0ebcd tparm() fix for Solaris. 2017-06-04 11:27:35 +01:00
Nicholas Marriott
8ec6c323b6 signal.h for utempter, from David CARLIER. 2017-06-04 11:23:48 +01:00
nicm
c5b7faaefc Add a timeout to prevent the sequences which wait for a specific
terminator (OSC, APC and DCS) waiting forever, which helps to avoid
garbage (cat /dev/random) locking up panes completely. This (and the
last commit) prompted by a discussion with theo.
2017-06-04 09:22:34 +00:00
nicm
467ece53e6 Remove unused variable. 2017-06-04 09:02:57 +00:00
nicm
8149bc3fa6 Be more strict about escape sequences that rename windows or set titles:
ignore any that not valid UTF-8 outright, and for good measure pass the
result through our UTF-8-aware vis(3).
2017-06-04 09:02:36 +00:00
nicm
adf5628087 Support SIGUSR2 to stop and start logging for an existing server. Also
we currently only have two log levels so just use -v and -vv rather than
-v and -vvvv, and clarify the man page entry for -v.
2017-06-04 08:25:57 +00:00
Nicholas Marriott
67cd496b39 Update CHANGES. 2017-06-04 09:09:31 +01:00
nicm
184039044a Typo/style; plus man page escaping from jmc. 2017-06-04 08:02:20 +00:00
Thomas Adam
757eb060cd Merge branch 'obsd-master' 2017-06-03 20:01:10 +01:00
nicm
3442066054 Make set-clipboard a three-state option so tmux itself can ignore the
sequencess.
2017-06-03 17:43:01 +00:00
Thomas Adam
80235d6fdd Merge branch 'obsd-master' 2017-06-03 10:01:10 +01:00
nicm
493a1846d0 Foreground colours with the bright attribute set need to use the bright
entries in the palette. GitHub issue 954.
2017-06-03 07:15:23 +00:00
Thomas Adam
73b9328c1f Merge branch 'obsd-master'
Conflicts:
	window-buffer.c
2017-06-01 18:29:36 +01:00
nicm
248aa54bfd Style and spacing nits. 2017-05-31 17:56:48 +00:00
nicm
70cc8f2c7e Shut up a warning. 2017-05-31 16:44:33 +00:00
Nicholas Marriott
92d86586b7 Update TODO. 2017-05-31 17:38:34 +01:00
Nicholas Marriott
a8e0363914 Add setrgbf setrgbb. 2017-05-31 17:22:43 +01:00
Nicholas Marriott
35008200bd Differences to OpenBSD. 2017-05-31 16:34:39 +01:00
Nicholas Marriott
113356c848 Build fixes. 2017-05-31 16:29:07 +01:00
nicm
b3d0ed4057 time.h here too. 2017-05-31 15:27:57 +00:00
nicm
61c0189bb1 Need time.h. 2017-05-31 15:26:41 +00:00
Thomas Adam
f17ecaa495 Merge branch 'obsd-master'
Conflicts:
	Makefile.am
	cfg.c
	server-client.c
2017-05-31 15:56:13 +01:00
Nicholas Marriott
9c4d0d454a Some changes that will appear when we sync up. 2017-05-31 13:56:07 +01:00
nicm
d60663ea86 Some applications like vi(1) and tmux until 10 minutes or so ago, do not
redraw on SIGWINCH if the size returns to the original size between the
original SIGWINCH and when they get around to calling TIOCGWINSZ. So use
the existing resize timer to introduce a small delay between the two
resizes.
2017-05-31 11:00:00 +00:00
nicm
ea6428a5d2 It is not OK to ignore SIGWINCH if SIOCGWINSZ reports the size has
unchanged, because it may have changed and changed back in the time
between us getting the signal and calling ioctl(). Always redraw when we
see SIGWINCH.
2017-05-31 10:29:15 +00:00
nicm
80c6b487dc Because we defer actually resizing applications (calling TIOCSWINSZ)
until the end of the server loop, tmux may have gone through several
internal resizes in between. This can be a problem if the final size is
the same as the initial size (what the application things it currently
is), because the application may choose not to redraw, assuming the
screen state is unchanged, when in fact tmux has thrown away parts of
the screen, assuming the application will redraw them.

To avoid this, do an extra resize if the new size is the same size as
the initial size. This should force the application to redraw when tmux
needs it to, while retaining the benefits of deferring (so we now resize
at most two times instead of at most one - and only two very rarely).

Fixes a problem with break-pane and zoomed panes reported by Michal
Mazurek.
2017-05-31 10:15:51 +00:00
nicm
7eb496c00c Look for setrgbf and setrgbb terminfo extensions for RGB colour. This is
the most reasonable of the various (some bizarre) suggestions for
capabilities.
2017-05-31 08:43:44 +00:00
nicm
aad4e4ddb1 Rewrite of choose mode, both to simplify and tidy the code and to add
some modern features.

Now the common code is in mode-tree.c, which provides an API used by the
three modes now separated into window-{buffer,client,tree}.c. Buffer
mode shows buffers, client mode clients and tree mode a tree of
sessions, windows and panes.

Each mode has a common set of key bindings plus a few that are specific
to the mode. Other changes are:

- each mode has a preview pane: for buffers this is the buffer content
  (very useful), for others it is a preview of the pane;

- items may be sorted in different ways ('O' key);

- multiple items may be tagged and an operation applied to all of them
  (for example, to delete multiple buffers at once);

- in tree mode a command may be run on the selected item (session,
  window, pane) or on tagged items (key ':');

- displayed items may be filtered in tree mode by using a format (this
  is used to implement find-window) (key 'f');

- the custom format (-F) for the display is no longer available;

- shortcut keys change from 0-9, a-z, A-Z which was always a bit weird
  with keys used for other uses to 0-9, M-a to M-z.

Now that the code is simpler, other improvements will come later.

Primary key bindings for each mode are documented under the commands in
the man page (choose-buffer, choose-client, choose-tree).

Parts written by Thomas Adam.
2017-05-30 21:44:59 +00:00
Thomas Adam
cea83c0e1f Merge branch 'obsd-master' 2017-05-30 10:01:12 +01:00
nicm
bd39fcbeea Preserve search string when entering prompt again. 2017-05-30 08:13:48 +00:00
Thomas Adam
0d073907b5 Merge branch 'obsd-master' 2017-05-30 00:01:14 +01:00
Thomas Adam
5ee6dc2120 Merge branch 'obsd-master' 2017-05-29 22:01:15 +01:00
nicm
64552ae304 Add a flag to stop the prompt input being expanded. 2017-05-29 20:42:53 +00:00
nicm
b95e5827c1 Store a copy of the old status line, will be needed soon for new choose mode. 2017-05-29 20:41:29 +00:00
nicm
8a214b2f8e Function to count clients. 2017-05-29 20:37:30 +00:00
Thomas Adam
5bc5fe5e7e Merge branch 'obsd-master' 2017-05-29 20:01:12 +01:00
nicm
a2ace9da24 Add ||, && format operators and C: to search pane content. 2017-05-29 18:06:34 +00:00
Thomas Adam
d3959a2118 Merge branch 'obsd-master' 2017-05-29 18:01:14 +01:00
nicm
1257501499 Add m: for fnmatch(3) format matching. 2017-05-29 15:43:48 +00:00
Thomas Adam
bfd7209053 Merge branch 'obsd-master' 2017-05-29 10:01:15 +01:00
nicm
1883d299bf Do not factor in screen_hsize() for the visible copy mode screen when
adjusting the selection, it should never have any useful history (and
when it does, after resize, we shouldn't use it). From Michal Mazurek.
2017-05-29 07:58:33 +00:00
nicm
15253448af Tweak text to mention initial size, from John Hood. 2017-05-29 07:46:32 +00:00
Nicholas Marriott
b017dc7e32 Update release text. 2017-05-29 08:40:33 +01:00
Nicholas Marriott
b5a6458cde Merge branch '2.5-rc' 2017-05-29 08:23:03 +01:00
Nicholas Marriott
de45957c42 Add to CHANGES. 2017-05-29 08:11:47 +01:00
Thomas Adam
f4a42738af Merge branch 'obsd-master' 2017-05-29 02:01:15 +01:00
nicm
d5158620bb Support OSC 10 and 11 to set foreground and background colours, from
"bertnp" in GitHub issue 942.
2017-05-28 23:23:40 +00:00
Nicholas Marriott
bf5a196c78 Changes in master. 2017-05-28 22:10:17 +01:00
Thomas Adam
5d7dfefa36 Merge branch 'obsd-master' 2017-05-28 22:01:11 +01:00
nicm
dbc8cae18c Change so that sessions created detached (-d or no client) are always
80x24 and the status line is not applied until they attach. Also make -x
and -y work for control clients whether the session is detached or not.
2017-05-28 19:46:55 +00:00
Nicholas Marriott
1e1e0f1fbb Add test for -x and -y too. 2017-05-28 20:15:45 +01:00
nicm
385bf084a5 Also recalculate session sizes when refreh-client -C is used. GitHub
issue 947.
2017-05-28 19:00:52 +00:00
Nicholas Marriott
eb1f362687 Do not need /dev/null. 2017-05-28 19:53:08 +01:00
Nicholas Marriott
5386e6583d Add some tests. 2017-05-28 19:52:51 +01:00
Thomas Adam
d1497527c6 Merge branch 'obsd-master' 2017-05-17 18:01:14 +01:00
nicm
91d202da7e Tidy command prompt callbacks and pass in the client. 2017-05-17 15:20:23 +00:00
Nicholas Marriott
3888bf9d12 Need to check libutil for fparseln. 2017-05-17 12:48:42 +01:00
Thomas Adam
3df4e78492 Merge branch 'obsd-master' 2017-05-16 16:01:28 +01:00
Nicholas Marriott
b74b6dc77f Missed during merge. 2017-05-16 14:15:35 +01:00
nicm
31625c2d17 Line length and spaces to tabs. 2017-05-16 12:57:26 +00:00
Thomas Adam
dcdaf5c8b9 Merge branch 'obsd-master' 2017-05-15 20:01:14 +01:00
Thomas Adam
36181775cd Merge branch 'obsd-master' 2017-05-15 18:01:14 +01:00
nicm
1ba7f1d03f Check the terminfo(5) U8 capability and disable using UTF-8 for ACS if
it is present and zero. This is useful for users with terminals or fonts
that do not correctly support UTF-8 line drawing characters. GitHub
issue 927, reported by Hiroaki Yamazoe and Akinori Hattori.
2017-05-15 16:44:04 +00:00
nicm
b160de5cb4 Notify layout changed when choosing predefined layouts, from Joshua Brot. 2017-05-15 14:57:29 +00:00
Thomas Adam
7edaedf3a1 Merge branch 'obsd-master' 2017-05-15 10:01:16 +01:00
nicm
cb5fcb3d22 The Konsole SU bug means it can't clear the entire scroll region (it
ignores if >= size, not if > as I first thought). So we can't
effectively fix it in code - remove the workarounds which just cause
bugs on other terminals.
2017-05-15 07:54:44 +00:00
Thomas Adam
58f8421eac Merge branch 'obsd-master' 2017-05-13 10:01:16 +01:00
nicm
7a4c66b7f5 Scroll the right number of lines off the region when clearing. 2017-05-13 07:41:59 +00:00
nicm
d58c3793d6 Some other unused variables. 2017-05-13 07:30:50 +00:00
Nicholas Marriott
ea190d862a Merge branch '2.5-rc' 2017-05-13 08:20:01 +01:00
Thomas Adam
9f9f8c8e76 Merge branch 'obsd-master' 2017-05-13 02:01:13 +01:00
nicm
50f1f1dce9 Compare >= for columns not >. 2017-05-12 23:10:19 +00:00
nicm
0264ef094a Can scroll away full lines to clear them too. 2017-05-12 23:06:43 +00:00
nicm
f688653710 Remove an unused variable. 2017-05-12 22:43:15 +00:00
Thomas Adam
886d896098 Merge branch 'obsd-master' 2017-05-12 18:01:14 +01:00
Nicholas Marriott
8331000764 Merge branch '2.5-rc' 2017-05-12 16:18:31 +01:00
nicm
0cd74723e1 When expanding a line in order to clear it, we need to use the default
background colour - there may be portions that we do not want to clear
with the new background colour.
2017-05-12 15:18:13 +00:00
Thomas Adam
20e30593a5 Merge branch 'obsd-master' 2017-05-12 16:01:14 +01:00
nicm
e2a18e2b37 Need to store bg for ECH. 2017-05-12 14:56:56 +00:00
nicm
da724fe1c0 Cannot rely on cursor position after DL and IL (some terminals move to
column 0, some do not).
2017-05-12 14:13:54 +00:00
nicm
ffd8beb6f6 Need to clear tty context before using it. 2017-05-12 13:29:05 +00:00
nicm
5d3cf2ff15 Only redraw single client, and tweak some logging. 2017-05-12 13:27:57 +00:00
Thomas Adam
7f813dcb6a Merge branch 'obsd-master' 2017-05-12 14:01:17 +01:00
nicm
18bab30792 Scrolling needs to use background colour. 2017-05-12 13:00:56 +00:00
nicm
60f7b05c0c Regions can't be smaller than 2 so don't try to clear them by scrolling if so. 2017-05-12 11:19:24 +00:00
nicm
7f626c8959 Can use INDN to clear regions with default background colour if margins
are supported.
2017-05-12 11:13:43 +00:00
nicm
886d50dcab ECH needs to use background colour. 2017-05-12 10:50:11 +00:00
nicm
7d3e2c83d4 Store copy mode search string in pane so search-again command works even
if you exit and reenter copy mode (it doesn't remember the position,
just the search string), suggested by espie@.
2017-05-12 10:45:38 +00:00
Nicholas Marriott
1cdc4568bd Merge branch '2.5-rc' 2017-05-11 23:28:56 +01:00
Thomas Adam
99582befc4 Merge branch 'obsd-master' 2017-05-11 14:01:10 +01:00
nicm
c0d3f204b0 Clear to start of screen needs to use background colour. 2017-05-11 11:39:30 +00:00
nicm
989cdca95f Need to redraw out to cellsize (total cells used in a line) rather than
cellused (only non-space cells) because there may be cells with a
nondefault background.
2017-05-11 11:38:49 +00:00
Thomas Adam
def8f852e3 Merge branch 'obsd-master' 2017-05-11 10:01:10 +01:00
nicm
349cdd6110 Make environ_log prefix take a format. 2017-05-11 07:34:54 +00:00
nicm
c54a5b3690 Change how we resolve which pane is dragging when there are multiple
options - choose the largest pane, which is more likely to be the one the
user wants to resize. Prompted by a report from Thomas Sattler.
2017-05-11 07:24:42 +00:00
Thomas Adam
6d961d672d Merge branch 'obsd-master' 2017-05-10 22:01:13 +01:00
Thomas Adam
247ec2ad88 Merge branch 'obsd-master' 2017-05-10 20:01:17 +01:00
nicm
8ab2753521 Move to the right cursor position before using spaces to clear. 2017-05-10 18:40:13 +00:00
nicm
2dc9bfd93a Prevent control clients from affecting the session size until they have
specified a size with refresh-client -C. Prompted by a different change
with the same purpose from George Nachman.
2017-05-10 16:48:36 +00:00
nicm
9dc6946ebf We can use ECH to clear sections of lines, so use it for internal panes
(that don't touch an edge). Move all the tty clear code into two common
functions rather than having the same bunch of checks everywhere.
2017-05-10 16:47:03 +00:00
Thomas Adam
0868512bbc Merge branch 'obsd-master' 2017-05-10 16:01:10 +01:00
nicm
b519551153 Expand formats in option names and add -F flag to do so in option values as well. 2017-05-10 13:05:41 +00:00
Thomas Adam
f8b3f1622d Merge branch 'obsd-master' 2017-05-10 14:01:11 +01:00
nicm
0e3c5ebe1a Insert copy mode bindings at the right place in the command queue. 2017-05-10 10:46:59 +00:00
Nicholas Marriott
daef51e038 Typo. 2017-05-09 23:18:48 +01:00
Nicholas Marriott
e82c42661b Back to master. 2017-05-09 23:11:01 +01:00
106 changed files with 7595 additions and 4504 deletions

200
CHANGES
View File

@@ -1,4 +1,177 @@
CHANGES FROM 2.4 to 2.5 09 May 2017 CHANGES FROM 2.6 TO 2.7
* Remove EVENT_* variables from environment on platforms where tmux uses them
so they do not pass on to panes.
* Fixed for hooks at server exit.
* Remove SGR 10 (was equivalent to SGR 0 but no other terminal seems to do
this).
* Expand formats in window and session names.
* Add -Z flag to choose-tree, choose-client, choose-buffer to automatically
zoom the pane when the mode is entered and unzoom when it exits, assuming the
pane is not already zoomed. This is now part of the default key bindings.
* Add C-g to exit modes with emacs keys.
* Add exit-empty option to exit server if no sessions (defaults to on).
* Show if a filter is present in choose modes.
* Add pipe-pane -I to to connect stdin of the child process.
* Performance improvements for reflow.
* Use RGB terminfo(5) capability to detect RGB colour terminals (the existing
Tc extension remains unchanged).
* Support for ISO colon-separated SGR sequences.
* Add select-layout -E to spread panes out evenly (bound to E key).
* Support wide characters properly when reflowing.
* Pass PWD to new panes as a hint to shells, as well as calling chdir().
* Performance improvements for the various choose modes.
* Only show first member of session groups in tree mode (-G flag to choose-tree
to show all).
* Support %else in config files to match %if; from Brad Town in GitHub issue
1071.
* Fix "kind" terminfo(5) capability to be S-Down not S-Up.
* Add a box around the preview label in tree mode.
* Show exit status and time in the remain-on-exit pane text; from Timo
Boettcher in GitHub issue 1103.
* Correctly use pane-base-index in tree mode.
* Change the allow-rename option default to off.
* Support for xterm(1) title stack escape sequences (GitHub issue 1075 from
Brad Town).
* Correctly remove padding cells to fix a UTF-8 display problem (GitHub issue
1090).
CHANGES FROM 2.5 TO 2.6, 05 October 2017
* Add select-pane -T to set pane title.
* Fix memory leak when lines with BCE are removed from history.
* Fix (again) the "prefer unattached" behaviour of attach-session.
* Reorder how keys are checked to allow keys to be specified that have a
leading escape. GitHub issue 1048.
* Support REP escape sequence (\033[b).
* Run alert hooks based on options rather than always, and allow further bells
even if there is an existing bell.
* Add -d flag to display-panes to override display-panes-time.
* Add selection_present format when in copy mode (allows key bindings that do
something different if there is a selection).
* Add pane_at_left, pane_at_right, pane_at_top and pane_at_bottom formats.
* Make bell, activity and silence alerting more consistent by: removing the
bell-on-alert option; adding activity-action and silence-action options with
the same possible values as the existing bell-action; adding a "both" value
for the visual-bell, visual-activity and visual-silence options to trigger
both a bell and a message.
* Add a pane_pipe format to show if pipe-pane is active.
* Block signals between forking and resetting signal handlers so that the
libevent signal handler doesn't get called in the child and incorrectly write
into the signal pipe that it still shares with the parent. GitHub issue 1001.
* Allow punctuation in pane_current_command.
* Add -c for respawn-pane and respawn-window.
* Wait for any remaining data to flush when a pane is closed while pipe-pane is
in use.
* Fix working out current client with no target. GitHub issue 995.
* Try to fallback to C.UTF-8 as well as en_US.UTF-8 when looking for a UTF-8
locale.
* Add user-keys option for user-defined key escape sequences (mapped to User0
to User999 keys).
* Add pane-set-clipboard hook.
* FAQ file has moved out of repository to online.
* Fix problem with high CPU usage when a client dies unexpectedly. GitHub issue
941.
* Do a dance on OS X 10.10 and above to return tmux to the user namespace,
allowing access to the clipboard.
* Do not allow escape sequences which expect a specific terminator (APC, DSC,
OSC) to wait for forever - use a small timeout. This reduces the chance of
the pane locking up completely when sent garbage (cat /dev/random or
similar).
* Support SIGUSR2 to toggle logging on a running server, also generate the
"out" log file with -vv not -vvvv.
* Make set-clipboard a three state option: on (tmux both sends to outside
terminal and accepts from applications inside); external (tmux sends outside
but does not accept inside); and off.
* Fix OSC 4 palette setting for bright foreground colours. GitHub issue 954.
* Use setrgbf and setrgbb terminfo(5) capabilities to set RGB colours, if they
are available. (Tc is still supported as well.)
* Fix redrawing panes when they are resized several times but end up with the
size unchanged (for example, splitw/resizep -Z/breakp).
* Major rewrite of choose mode. Now includes preview, sorting, searching and
tagging; commands that can be executed directly from the mode (for example,
to delete one or more buffers); and filtering in tree mode.
* choose-window and choose-session are now aliases of choose-tree (in the
command-alias option).
* Support OSC 10 and OSC 11 to set foreground and background colours.
* Check the U8 capability to determine whether to use UTF-8 line drawing
characters for ACS.
* Some missing notifications for layout changes.
* Control mode clients now do not affect session sizes until they issue
refresh-client -C. new-session -x and -y works with control clients even if
the session is not detached.
* All new sessions that are unattached (whether with -d or started with no
terminal) are now created with size 80 x 24. Whether the status line is on or
off does not affect the size of new sessions until they are attached.
* Expand formats in option names and add -F flag to expand them in option values.
* Remember the search string for a pane even if copy mode is exited and entered
again.
* Some further BCE fixes (scroll up, reverse index).
* Improvements to how terminals are cleared (entirely or partially).
CHANGES FROM 2.4 TO 2.5, 09 May 2017
* Reset updated flag when restarting #() command so that new output is properly * Reset updated flag when restarting #() command so that new output is properly
recognised. GitHub issue 922. recognised. GitHub issue 922.
@@ -83,7 +256,7 @@ CHANGES FROM 2.4 to 2.5 09 May 2017
* Do not redraw a client unless we realistically think it can accept the data - * Do not redraw a client unless we realistically think it can accept the data -
defer redraws until the client has nothing else waiting to write. defer redraws until the client has nothing else waiting to write.
CHANGES FROM 2.3 to 2.4 20 April 2017 CHANGES FROM 2.3 TO 2.4, 20 April 2017
Incompatible Changes Incompatible Changes
==================== ====================
@@ -113,7 +286,7 @@ Incompatible Changes
and history is now used for searching, jumping, and so on instead of a custom and history is now used for searching, jumping, and so on instead of a custom
one. The default C-r binding is now: one. The default C-r binding is now:
bind -Tcopy-mode C-r command-prompt -p'search up' "send -X search-backward-incremental '%%'" bind -Tcopy-mode C-r command-prompt -i -p'search up' "send -X search-backward-incremental '%%'"
There are also some new commmands available with send -X, such as There are also some new commmands available with send -X, such as
copy-pipe-and-cancel. copy-pipe-and-cancel.
@@ -163,7 +336,7 @@ Normal Changes
set -ag syntax should work without change). set -ag syntax should work without change).
* There have been substantial performance improvements. * There have been substantial performance improvements.
CHANGES FROM 2.2 to 2.3 29 September 2016 CHANGES FROM 2.2 TO 2.3, 29 September 2016
Incompatible Changes Incompatible Changes
==================== ====================
@@ -184,7 +357,7 @@ Normal Changes
* 'display-panes' can now accept a command to run, rather than always * 'display-panes' can now accept a command to run, rather than always
selecting the pane. selecting the pane.
CHANGES FROM 2.1 to 2.2 10 April 2016 CHANGES FROM 2.1 TO 2.2, 10 April 2016
Incompatible Changes Incompatible Changes
==================== ====================
@@ -249,8 +422,7 @@ Normal Changes
* RGB (24bit) colour support. The 'Tc' flag must be set in the external TERM * RGB (24bit) colour support. The 'Tc' flag must be set in the external TERM
entry (using terminal-overrides or a custom terminfo entry). entry (using terminal-overrides or a custom terminfo entry).
CHANGES FROM 2.0 TO 2.1, 18 October 2015
CHANGES FROM 2.0 to 2.1 18 October 2015
Incompatible Changes Incompatible Changes
==================== ====================
@@ -301,7 +473,7 @@ Normal Changes
* Copy mode is exited if the history is cleared whilst in copy-mode. * Copy mode is exited if the history is cleared whilst in copy-mode.
* 'copy-mode' learned '-e' to exit copy-mode when scrolling to end. * 'copy-mode' learned '-e' to exit copy-mode when scrolling to end.
CHANGES FROM 1.9a to 2.0 6 March 2015 CHANGES FROM 1.9a TO 2.0, 06 March 2015
Incompatible Changes Incompatible Changes
==================== ====================
@@ -360,9 +532,9 @@ Normal Changes
* 'split-window' and 'join-window' understand -b to create the pane to the left * 'split-window' and 'join-window' understand -b to create the pane to the left
or above the target pane. or above the target pane.
CHANGES FROM 1.9 to 1.9a 22 February 2014 CHANGES FROM 1.9 TO 1.9a, 22 February 2014
NOTE: This is a bug-fix release to address some important bugs which just NOTE: This is a bug-fix release to address some important bugs which just
missed the 1.9 deadline, but were found afterwards. missed the 1.9 deadline, but were found afterwards.
Normal Changes Normal Changes
@@ -371,7 +543,7 @@ Normal Changes
* Fix crash due to uninitialized lastwp member of layout_cell * Fix crash due to uninitialized lastwp member of layout_cell
* Fix -fg/-bg/-style with 256 colour terminals. * Fix -fg/-bg/-style with 256 colour terminals.
CHANGES FROM 1.8 to 1.9, 20 February 2014 CHANGES FROM 1.8 TO 1.9, 20 February 2014
NOTE: This release has bumped the tmux protocol version. It is therefore NOTE: This release has bumped the tmux protocol version. It is therefore
advised that the prior tmux server is restarted when this version of tmux is advised that the prior tmux server is restarted when this version of tmux is
@@ -426,7 +598,7 @@ Normal Changes
* tmux now supports 256 colours running under fbterm. * tmux now supports 256 colours running under fbterm.
* Many bug fixes! * Many bug fixes!
CHANGES FROM 1.7 to 1.8, 26 March 2013 CHANGES FROM 1.7 TO 1.8, 26 March 2013
Incompatible Changes Incompatible Changes
==================== ====================
@@ -477,7 +649,7 @@ Normal Changes
* Lots and lots of bug fixes, fixing memory-leaks, etc. * Lots and lots of bug fixes, fixing memory-leaks, etc.
* Various manpage improvements. * Various manpage improvements.
CHANGES FROM 1.6 to 1.7, 13 October 2012 CHANGES FROM 1.6 TO 1.7, 13 October 2012
* tmux configuration files now support line-continuation with a "\" at the * tmux configuration files now support line-continuation with a "\" at the
end of a line. end of a line.
@@ -868,7 +1040,7 @@ CHANGES FROM 1.0 TO 1.1, 05 November 2009
* xterm-keys rewrite. * xterm-keys rewrite.
* Additional code reduction, and bug fixes. * Additional code reduction, and bug fixes.
CHANGES FROM 0.9 TO 1.0, 20 Sept 2009 CHANGES FROM 0.9 TO 1.0, 20 September 2009
* Option to alter the format of the window title set by tmux. * Option to alter the format of the window title set by tmux.
* Backoff for a while after multiple incorrect password attempts. * Backoff for a while after multiple incorrect password attempts.

View File

@@ -27,7 +27,7 @@ case before opening an issue.
To run tmux without a config and get logs, run: To run tmux without a config and get logs, run:
tmux -Ltest kill-server tmux -Ltest kill-server
tmux -vvvv -Ltest -f/dev/null new tmux -vv -Ltest -f/dev/null new
Then reproduce the problem, exit tmux, and attach the tmux-server-*.log file Then reproduce the problem, exit tmux, and attach the tmux-server-*.log file
from the current directory to the issue. from the current directory to the issue.

462
FAQ
View File

@@ -1,462 +0,0 @@
tmux frequently asked questions
******************************************************************************
* PLEASE NOTE: most display problems are due to incorrect TERM! Before *
* reporting problems make SURE that TERM settings are correct inside and *
* outside tmux. *
* *
* Inside tmux TERM must be "screen" or similar (such as "screen-256color"). *
* Don't bother reporting problems where it isn't! *
* *
* Outside, it must match your terminal: particularly, use "rxvt" for rxvt *
* and derivatives. *
******************************************************************************
* How is tmux different from GNU screen?
tmux and GNU screen have many similarities. Some of the main differences I am
aware of are (bearing in mind I haven't used screen for a few years now):
- tmux uses a client-server model. Each server has single Unix domain socket in
/tmp and within one server there are multiple sessions which may be attached
to multiple clients (terminals).
This has advantages, notably: windows may be linked simultaneously to
multiple sessions; windows may be moved freely between sessions; and a client
may be switched between sessions easily (C-b D). There is one major
disadvantage: if the server crashes, game over, all sessions die. In
practice, however, tmux is quite stable and gets more so as people report any
bugs they hit :-).
This model is different from screen, where typically each new screen instance
is independent. tmux supports the same behaviour by using multiple servers
with the -L option but it is not typically recommended.
- Different command interfaces. One of the goals of tmux is that the shell
should be easily usable as a scripting language - almost all tmux commands
can be used from the shell and behave identically whether used from the
shell, from a key binding or from the command prompt. Personally I also find
tmux's command interface much more consistent and clearer, but this is
subjective.
- tmux calls window names (what you see in the status line) "names", screen
calls them "titles".
- tmux has a multiple paste buffers. Not a major one but comes in handy quite a
lot.
- tmux supports automatically renaming windows to the running application
without gross hacks using escape sequences. Its even on by default.
- tmux has a choice of vi or emacs key layouts. Again, not major, but I use
emacs so if tmux did support only one key set it would be emacs and then all
the vi users would get humpy. Key bindings may be completely reconfigured in
any case.
- tmux has an option to limit the window size.
- tmux has search in windows (C-b f).
- The window split (pane) model is different. tmux has two objects, windows and
panes; screen has just windows. This difference has several implications:
* In screen you can have a window appear in several layouts, in tmux a pane
can only be in one window (fixing this is a big todo item but quite
invasive).
* tmux layouts are immutable and do not get changed unless you modify them.
* In tmux, all panes are closed when you kill a window.
* tmux panes do not have individual names, titles and so on.
I think tmux's model is much easier to manage and navigate within a window,
but breaking panes off from and joining them to windows is more clumsy.
tmux also has support for preset pane layouts.
- tmux's status line syntax is more readable and easier to use. I think it'd be
hard for anyone to argue with this. tmux doesn't support running a command
constantly and always using the last line of its output, commands must be run
again each time.
- tmux has modern, easily extended code. Again hard to argue screen is better
if you have looked at the code.
- tmux depends on libevent. I don't see this as a disadvantage: libevent is
small and portable, and on modern systems with current package management
systems dependencies are not an issue. libevent brings advantages in code
simplicity and performance.
- screen allows the window to be bigger than the terminal and can pan around
it. tmux limits the size to the largest attached client. This is a big todo
item for tmux but it is not trivial.
- screen has builtin serial and telnet support; this is bloat and is unlikely
to be added to tmux.
- Environment handling is different.
- tmux tends to be more demanding on the terminal so tends to show up terminal
and application bugs which screen does not.
- screen has wider platform support, for example IRIX, and for odd terminals.
* I found a bug! What do I do?
Check the latest version of tmux from Git to see if the problem is still
reproducible. Sometimes the length of time between releases means a lot of
fixes can be sitting in Git and the problem might already be fixed.
Please send bug reports by email to nicholas.marriott@gmail.com or
tmux-users@googlegroups.com. Please include as much of the following
information as possible:
- the version of tmux you are running;
- the operating system you are using and its version;
- the terminal emulator you are using and the TERM setting when tmux was
started;
- a description of the problem;
- if the problem is repeatable, the steps to repeat the problem;
- for screen corruption issues, a screenshot and the output of "infocmp $TERM"
from outside tmux are often very useful.
* Why doesn't tmux do $x?
Please send feature requests by email to tmux-users@googlegroups.com.
* Why do you use the screen terminal description inside tmux? It sucks.
It is already widely available. It is planned to change to something else such
as xterm-xfree86 at some point, if possible.
* I don't see any colour in my terminal! Help!
On some platforms, common terminal descriptions such as xterm do not include
colour. screen ignores this, tmux does not. If the terminal emulator in use
supports colour, use a value for TERM which correctly lists this, such as
xterm-color.
* tmux freezes my terminal when I attach to a session. I even have to kill -9
the shell it was started from to recover!
Some consoles really really don't like attempts to set the window title. Tell
tmux not to do this by turning off the "set-titles" option (you can do this
in .tmux.conf):
set -g set-titles off
If this doesn't fix it, send a bug report.
* Why is C-b the prefix key? How do I change it?
The default key is C-b because the prototype of tmux was originally developed
inside screen and C-b was chosen not to clash with the screen meta key. It
also has the advantage of not interfering with the use of C-a for start-of-line
in emacs and the shell (although it does interfere with previous-character).
Changing is simple: change the "prefix-key" option, and - if required - move
the binding of the "send-prefix" command from C-b (C-b C-b sends C-b by
default) to the new key. For example:
set -g prefix C-a
unbind C-b
bind C-a send-prefix
* How do I use UTF-8?
When running tmux in a UTF-8 capable terminal, UTF-8 must be turned on in tmux;
as of release 0.9, tmux attempts to autodetect a UTF-8-capable terminal by
checking the LC_ALL, LC_CTYPE and LANG environment variables. list-clients may
be used to check if this is detected correctly; if not, the -u command-line
flag may be specified when creating or attaching a client to a tmux session:
$ tmux -u new
Since the 1.0 release, tmux will turn on UTF-8 related options automatically
(ie status-utf8, and utf8) if the above conditions are met.
* How do I use a 256 colour terminal?
Provided the underlying terminal supports 256 colours, it is usually sufficient
to add the following to ~/.tmux.conf:
set -g default-terminal "screen-256color"
Note that some platforms do not support "screen-256color" ("infocmp
screen-256color" will return an error) - in this case see the next entry in
this FAQ.
tmux attempts to detect a 256 colour terminal both by looking at the colors
terminfo entry and by looking for the string "256col" in the TERM environment
variable.
If both these methods fail, the -2 flag may be passed to tmux when attaching
to a session to indicate the terminal supports 256 colours.
* vim or $otherprogram doesn't display 256 colours. What's up?
Some programs attempt to detect the number of colours a terminal is capable of
by checking the colors terminfo or Co termcap entry. However, this is not
reliable, and in any case is missing from the "screen" terminal description
used inside tmux.
There are two options (aside from using "screen-256color") to allow programs to
recognise they are running on a 256-colour terminal inside tmux:
- Manually force the application to use 256 colours always or if TERM is set to
screen. For vim, you can do this by overriding the t_Co option, see
http://vim.wikia.com/wiki/256_colors_in_vim.
- Creating a custom terminfo file that includes colors#256 in ~/.terminfo and
using it instead. These may be compiled with tic(1).
* How do I make Ctrl-PgUp and Ctrl-PgDn work in vim?
tmux supports passing through ctrl (and where supported by the client terminal,
alt and shift) modifiers to function keys using xterm(1)-style key sequences.
This may be enabled per window, or globally with the tmux command:
setw -g xterm-keys on
Because the TERM variable inside tmux must be set to "screen", vim will not
automatically detect these keys are available; however, the appropriate key
sequences can be overridden in .vimrc using the following:
if &term == "screen"
set t_kN=^[[6;*~
set t_kP=^[[5;*~
endif
And similarly for any other keys for which modifiers are desired.
Please note that the "xterm-keys" setting may affect other programs, in the
same way as running them in a standard xterm; for example most shells do not
expect to receive xterm(1)-style key sequences so this setting may prevent keys
such as ctrl-left and ctrl-right working correctly. tmux also passes through
the ctrl (bit 5 set, for example ^[[5~ to ^[[5^) modifier in non-xterm(1) mode;
it may be possible to configure vim to accept these, an example of how to do so
would be welcome.
vim users may also want to set the "ttyfast" option inside tmux.
* How do I make ctrl and shift arrow keys work in emacs?
The terminal-init-screen function in term/screen.el is called for new frames,
but it doesn't configure any function keys.
If the tmux xterm-keys option is on, it is enough to define the same keys as
xterm. Add the following to init.el or .emacs to do this:
(defadvice terminal-init-screen
;; The advice is named `tmux', and is run before `terminal-init-screen' runs.
(before tmux activate)
;; Docstring. This describes the advice and is made available inside emacs;
;; for example when doing C-h f terminal-init-screen RET
"Apply xterm keymap, allowing use of keys passed through tmux."
;; This is the elisp code that is run before `terminal-init-screen'.
(if (getenv "TMUX")
(let ((map (copy-keymap xterm-function-map)))
(set-keymap-parent map (keymap-parent input-decode-map))
(set-keymap-parent input-decode-map map))))
And ensure .tmux.conf contains "set -g xterm-keys on".
Alternatively, the screen.el file can be copied to the load path and
customized.
* Why doesn't elinks set the window title inside tmux?
There isn't a way to detect if a terminal supports setting the window title, so
elinks attempts to guess by looking at the environment. Rather than looking for
TERM=screen, it uses the STY variable to detect if it is running in screen;
tmux does not use this so the check fails. A workaround is to set STY before
running elinks.
The following shell function does this, and also clears the window title on
exit (elinks, for some strange reason, sets it to the value of TERM):
elinks() {
STY= `which elinks` $*
echo -ne \\033]0\;\\007;
}
* What is the proper way to escape characters with #(command)?
When using the #(command) construction to include the output from a command in
the status line, the command will be parsed twice. First, when it's read by the
configuration file or the command-prompt parser, and second when the status
line is being drawn and the command is passed to the shell. For example, to
echo the string "(test)" to the status line, either single or double quotes
could be used:
set -g status-right "#(echo \\\\(test\\\\))"
set -g status-right '#(echo \\\(test\\\))'
In both cases, the status-right option will be set to the string "#(echo
\\(test\\))" and the command executed will be "echo \(test\)".
* tmux uses too much CPU. What do I do?
Automatic window renaming may use a lot of CPU, particularly on slow computers:
if this is a problem, turn it off with "setw -g automatic-rename off". If this
doesn't fix it, please report the problem.
* I use PuTTY and my tmux window pane separators are all qqqqqqqqq's!
PuTTY is using a character set translation that doesn't support ACS line
drawing. With a Unicode font, try setting PuTTY to use a different translation
on the Window -> Translation configuration page. For example, change UTF-8 to
ISO-8859-1 or CP437. It may also be necessary to adjust the way PuTTY treats
line drawing characters in the lower part of the same configuration page.
* What is the best way to display the load average? Why no #L?
It isn't possible to get the load average portably in code and it is preferable
not to add portability goop. The following works on at least Linux, *BSD and OS
X:
uptime|awk '{split(substr($0, index($0, "load")), a, ":"); print a[2]}'
* How do I attach the same session to multiple clients but with a different
current window, like screen -x?
One or more of the windows can be linked into multiple sessions manually with
link-window, or a grouped session with all the windows can be created with
new-session -t.
* Ctrl and arrow keys doesn't work in putty! What do I do?
putty inverts the sense of the cursor key mode on ctrl, which is a bit hard for
tmux to detect properly. To get ctrl keys right, change the terminfo settings
so kUP5 (Ctrl-Up etc) are the adjusted versions, and disable smkx/rmkx so tmux
doesn't change the mode. For example with this line in .tmux.conf (assuming you
have TERM set to xterm):
set -g terminal-overrides "xterm*:kLFT5=\eOD:kRIT5=\eOC:kUP5=\eOA:kDN5=\eOB:smkx@:rmkx@"
Note that this will only work in tmux 1.2 and above.
* How can I blank the tmux window?
GNU screen has a feature whereby it will blank the screen after a period of
inactivity. To do the same thing in tmux, use the lock-command setting, for
example (with GNU bash):
set -g lock-command 'tput civis && read -s -n1'
This will remove the cursor and tell the shell to quit once a key has been
pressed. For zsh, use "read -s -k1".
In addition, it's possible to have both blanking and locking (for instance via
lock(1) or vlock(1)) by using the following:
bind x set lock-command '/usr/bin/vlock' \; lock-client \; set lock-command 'tput civis && read -s -n1'
* I don't see italics! Or less and vim show italics and reverse the wrong way round!
GNU screen does not support italics and the "screen" terminfo description uses
the italics escape sequence incorrectly.
As of tmux 2.1, if default-terminal is set to "screen" or matches "screen-*",
tmux will behave like screen and italics will be disabled.
To enable italics, create a new terminfo entry called "tmux" (some platforms
may already have this, you can check with "infocmp tmux"):
$ cat <<EOF|tic -x -
tmux|tmux terminal multiplexer,
ritm=\E[23m, rmso=\E[27m, sitm=\E[3m, smso=\E[7m, Ms@,
use=xterm+tmux, use=screen,
tmux-256color|tmux with 256 colors,
use=xterm+256setaf, use=tmux,
EOF
$
And tell tmux to use it in ~/.tmux.conf:
set -g default-terminal "tmux"
If using urxvt, make sure you have an italics capable font enabled. for
example, add to ~/.Xdefaults:
urxvt.italicFont: xft:Bitstream Vera Sans Mono:italic:autohint=true
* How can I make tmux use my terminal's scrollback buffer?
Normally, tmux enables the terminal's "alternate screen". Most terminals (such
as xterm) do not save scrollback for the alternate screen. You might prefer
tmux to use the normal screen, so it uses your terminal's scrollback
buffer. This way, you can access the scrollback buffer as usual, for example
using the mouse wheel - although there is no guarantee output inside tmux will
always (or ever) be added to the scrollback.
You can make tmux use the normal screen by telling it that your terminal does
not have an alternate screen. Put the following in ~/.tmux.conf:
set -ga terminal-overrides ',xterm*:smcup@:rmcup@'
Adjust if your $TERM does not start with xterm.
tmux will still emulate the alternate screen for applications run under tmux,
so you don't really lose anything with this setting. The only disadvantage is
that when you exit tmux, it will not restore whatever was there before you
started.
* How do I see the default configuration?
Show the default session options by starting a new tmux server with no
configuration file:
$ tmux -Lfoo -f/dev/null start\; show -g
Or the default window options:
$ tmux -Lfoo -f/dev/null start\; show -gw
* How do I copy a selection from tmux to the system's clipboard?
When running in xterm(1), tmux can automatically send copied text to the
clipboard. This is controlled by the set-clipboard option and also needs this
X resource to be set:
XTerm*disallowedWindowOps: 20,21,SetXprop
For rxvt-unicode (urxvt), there is an unofficial Perl extension here:
http://anti.teamidiot.de/static/nei/*/Code/urxvt/
Otherwise a key binding for copy mode using xclip (or xsel) works:
bind -temacs-copy C-y copy-pipe "xclip -i >/dev/null"
Or for inside and outside copy mode with the prefix key:
bind C-y run -b "tmux save-buffer - | xclip -i"
On OS X, look at the pbcopy(1) and pbpaste(1) commands.
* Why don't some commands work inside tmux on OS X?
Apple requires some undocumented, unsupported fiddling to allow commands that
interact with the GUI to work. Neither tmux itself nor most shells do this, so
an external program is required. This can be found here:
https://github.com/ChrisJohnsen/tmux-MacOSX-pasteboard
Affected commands may include say(1), pbcopy(1), pbpaste(1) and ssh(1).
* Why do I see dots around a session when I attach to it?
tmux limits the size of the window to the smallest attached session. If
it didn't do this then it would be impossible to see the entire window.
The dots mark the size of the window tmux can display.
To avoid this, detach all other clients when attaching:
$ tmux attach -d
Or from inside tmux by detaching individual clients with C-b D or all
using:
C-b : attach -d

View File

@@ -6,7 +6,7 @@ CLEANFILES = tmux.1.mdoc tmux.1.man
# Distribution tarball options. # Distribution tarball options.
EXTRA_DIST = \ EXTRA_DIST = \
CHANGES FAQ README TODO COPYING example_tmux.conf compat/*.[ch] \ CHANGES README COPYING example_tmux.conf compat/*.[ch] \
osdep-*.c mdoc2man.awk tmux.1 osdep-*.c mdoc2man.awk tmux.1
# Preprocessor flags. # Preprocessor flags.
@@ -66,8 +66,6 @@ dist_tmux_SOURCES = \
cmd-bind-key.c \ cmd-bind-key.c \
cmd-break-pane.c \ cmd-break-pane.c \
cmd-capture-pane.c \ cmd-capture-pane.c \
cmd-choose-buffer.c \
cmd-choose-client.c \
cmd-choose-tree.c \ cmd-choose-tree.c \
cmd-command-prompt.c \ cmd-command-prompt.c \
cmd-confirm-before.c \ cmd-confirm-before.c \
@@ -145,6 +143,7 @@ dist_tmux_SOURCES = \
layout-set.c \ layout-set.c \
layout.c \ layout.c \
log.c \ log.c \
mode-tree.c \
names.c \ names.c \
notify.c \ notify.c \
options-table.c \ options-table.c \
@@ -159,7 +158,6 @@ dist_tmux_SOURCES = \
server-fn.c \ server-fn.c \
server.c \ server.c \
session.c \ session.c \
signal.c \
status.c \ status.c \
style.c \ style.c \
tmux.c \ tmux.c \
@@ -169,9 +167,11 @@ dist_tmux_SOURCES = \
tty-term.c \ tty-term.c \
tty.c \ tty.c \
utf8.c \ utf8.c \
window-choose.c \ window-buffer.c \
window-client.c \
window-clock.c \ window-clock.c \
window-copy.c \ window-copy.c \
window-tree.c \
window.c \ window.c \
xmalloc.c \ xmalloc.c \
xmalloc.h \ xmalloc.h \

14
README
View File

@@ -29,21 +29,19 @@ To get and build the latest from version control:
$ 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.)
For more information see http://git-scm.com. Patches should be sent by email to For more information see http://git-scm.com. Patches should be sent by email to
the mailing list at tmux-users@googlegroups.com. the mailing list at tmux-users@googlegroups.com or submitted through GitHub at
https://github.com/tmux/tmux/issues.
For documentation on using tmux, see the tmux.1 manpage. It can be viewed from For documentation on using tmux, see the tmux.1 manpage. It can be viewed from
the source tree with: the source tree with:
$ nroff -mdoc tmux.1|less $ nroff -mdoc tmux.1|less
Some common questions are answered in the FAQ file, a rough todo list is in the A small example configuration in example_tmux.conf.
TODO file and an example configuration in example_tmux.conf.
A vim(1) syntax file is available at:
https://github.com/ericpruitt/tmux.vim
https://raw.githubusercontent.com/ericpruitt/tmux.vim/master/vim/syntax/tmux.vim
And a bash(1) completion file at: And a bash(1) completion file at:

15
SYNCING
View File

@@ -145,7 +145,9 @@ Release tmux for next version
upgrades and the latter should mention all the major changes since upgrades and the latter should mention all the major changes since
the last version. the last version.
2. Tag with: 2. Make sure configure.ac has the new version number.
3. Tag with:
% git tag -a 2.X % git tag -a 2.X
@@ -155,18 +157,17 @@ Release tmux for next version
% git push --tags % git push --tags
3. Build the tarball with 'make dist'. 4. Build the tarball with 'make dist'.
4. Check the tarball. If it's good, go here to select the tag just pushed: 5. Check the tarball. If it's good, go here to select the tag just pushed:
https://github.com/tmux/tmux/tags https://github.com/tmux/tmux/tags
Click the "Add release notes", upload the tarball and add a link in the Click the "Add release notes", upload the tarball and add a link in the
description field to the CHANGES file. description field to the CHANGES file.
5. Clone the tmux.github.io repository, and change the RELEASE version in the 6. Clone the tmux.github.io repository, and change the RELEASE version in the
Makefile. Commit it, and run 'make' to replace %%VERSION%%. Push the Makefile. Commit it, and run 'make' to replace %%RELEASE%%. Push the
result out. result out.
6. Bump version in tmux/tmux.git configure.ac and uncomment "enable_debug=yes" 7. Change version back to master in configure.ac.
to create a debug build by default.

28
TODO
View File

@@ -1,7 +1,6 @@
- command bits and pieces: - command bits and pieces:
* allow multiple targets: fnmatch for -t/-c, for example detach all * allow multiple targets: fnmatch for -t/-c, for example detach all
clients with -t* clients with -t*
* add -c for new-session like new-window
* ' and " should be parsed the same (eg "\e" vs '\e') in config * ' and " should be parsed the same (eg "\e" vs '\e') in config
and command prompt and command prompt
* last-pane across sessions * last-pane across sessions
@@ -25,14 +24,6 @@
* comparison operators like < and > (for #{version}?) * comparison operators like < and > (for #{version}?)
* %else statement in config file * %else statement in config file
- choose mode improvements:
* choose-pane command (augment choose-tree to do this?)
* flag to choose-* for sort order
* two choices (first one then second, for swap-pane and join-pane)
* choose modes should ditch the key bindings and just have fixed keys, and
be more customized to their purpose (d to delete a buffer for choose-buffer,
a preview of buffer contents, etc)
- improve monitor-*: - improve monitor-*:
* straighten out rules for multiple clients * straighten out rules for multiple clients
* think about what happens across sessions * think about what happens across sessions
@@ -58,6 +49,7 @@
* improve word and line selection in copy mode (for example when * improve word and line selection in copy mode (for example when
dragging it should select by word. compare how xterm works. GitHub dragging it should select by word. compare how xterm works. GitHub
issue 682) issue 682)
* key to search for word under cursor (GitHub issue 1240)
- layout stuff - layout stuff
* way to tag a layout as a number/name * way to tag a layout as a number/name
@@ -118,10 +110,24 @@
* this doesn't work, need pane reference count probably: * this doesn't work, need pane reference count probably:
bind -n DoubleClick3Status confirm-before -p "kill-window #I? (y/n)" kill-window bind -n DoubleClick3Status confirm-before -p "kill-window #I? (y/n)" kill-window
* respawn -c flag same as neww etc * respawn -c flag same as neww etc
* marker lines in history (GitHub issue 1042)
* tree mode stuff: make command prompt (:) common code so all modes get it,
predefined filters, tag-all key, ...
* drag panes and windows around to move/swap them in choose mode
- hooks - hooks
* more hooks for various things * more hooks for various things
* finish after hooks for special commands * finish after hooks for special commands. these do not have a hook at
the moment:
attach-session detach-client kill-server respawn-window
swap-window break-pane find-window kill-session rotate-window
switch-client choose-tree if-shell kill-window run-shell
wait-for command-prompt join-pane move-window source-file
confirm-before kill-pane respawn-pane swap-pane
at the moment AFTERHOOK uses current only if target is not valid,
but target is ALWAYS valid - it should use current if no -t flag?
then select-* could use AFTERHOOK
* multiple hooks with the same name? * multiple hooks with the same name?
* finish hooks for notifys * finish hooks for notifys
* for session_closed, if no sessions at all, perhaps fake up a temporary one * for session_closed, if no sessions at all, perhaps fake up a
temporary one

157
alerts.c
View File

@@ -30,13 +30,13 @@ static int alerts_enabled(struct window *, int);
static void alerts_callback(int, short, void *); static void alerts_callback(int, short, void *);
static void alerts_reset(struct window *); static void alerts_reset(struct window *);
static int alerts_action_applies(struct winlink *, const char *);
static int alerts_check_all(struct window *); static int alerts_check_all(struct window *);
static int alerts_check_bell(struct window *); static int alerts_check_bell(struct window *);
static int alerts_check_activity(struct window *); static int alerts_check_activity(struct window *);
static int alerts_check_silence(struct window *); static int alerts_check_silence(struct window *);
static void printflike(2, 3) alerts_set_message(struct session *, const char *, static void alerts_set_message(struct winlink *, const char *,
...); const char *);
static void alerts_ring_bell(struct session *);
static TAILQ_HEAD(, window) alerts_list = TAILQ_HEAD_INITIALIZER(alerts_list); static TAILQ_HEAD(, window) alerts_list = TAILQ_HEAD_INITIALIZER(alerts_list);
@@ -46,7 +46,6 @@ alerts_timer(__unused int fd, __unused short events, void *arg)
struct window *w = arg; struct window *w = arg;
log_debug("@%u alerts timer expired", w->id); log_debug("@%u alerts timer expired", w->id);
alerts_reset(w);
alerts_queue(w, WINDOW_SILENCE); alerts_queue(w, WINDOW_SILENCE);
} }
@@ -69,12 +68,33 @@ alerts_callback(__unused int fd, __unused short events, __unused void *arg)
alerts_fired = 0; alerts_fired = 0;
} }
static int
alerts_action_applies(struct winlink *wl, const char *name)
{
int action;
/*
* {bell,activity,silence}-action determines when to alert: none means
* nothing happens, current means only do something for the current
* window and other means only for windows other than the current.
*/
action = options_get_number(wl->session->options, name);
if (action == ALERT_ANY)
return (1);
if (action == ALERT_CURRENT)
return (wl == wl->session->curw);
if (action == ALERT_OTHER)
return (wl != wl->session->curw);
return (0);
}
static int static int
alerts_check_all(struct window *w) alerts_check_all(struct window *w)
{ {
int alerts; int alerts;
alerts = alerts_check_bell(w); alerts = alerts_check_bell(w);
alerts |= alerts_check_activity(w); alerts |= alerts_check_activity(w);
alerts |= alerts_check_silence(w); alerts |= alerts_check_silence(w);
return (alerts); return (alerts);
@@ -92,8 +112,10 @@ alerts_check_session(struct session *s)
static int static int
alerts_enabled(struct window *w, int flags) alerts_enabled(struct window *w, int flags)
{ {
if (flags & WINDOW_BELL) if (flags & WINDOW_BELL) {
return (1); if (options_get_number(w->options, "monitor-bell"))
return (1);
}
if (flags & WINDOW_ACTIVITY) { if (flags & WINDOW_ACTIVITY) {
if (options_get_number(w->options, "monitor-activity")) if (options_get_number(w->options, "monitor-activity"))
return (1); return (1);
@@ -119,6 +141,9 @@ alerts_reset(struct window *w)
{ {
struct timeval tv; struct timeval tv;
if (!event_initialized(&w->alerts_timer))
evtimer_set(&w->alerts_timer, alerts_timer, w);
w->flags &= ~WINDOW_SILENCE; w->flags &= ~WINDOW_SILENCE;
event_del(&w->alerts_timer); event_del(&w->alerts_timer);
@@ -133,11 +158,7 @@ alerts_reset(struct window *w)
void void
alerts_queue(struct window *w, int flags) alerts_queue(struct window *w, int flags)
{ {
if (w->flags & WINDOW_ACTIVITY) alerts_reset(w);
alerts_reset(w);
if (!event_initialized(&w->alerts_timer))
evtimer_set(&w->alerts_timer, alerts_timer, w);
if ((w->flags & flags) != flags) { if ((w->flags & flags) != flags) {
w->flags |= flags; w->flags |= flags;
@@ -162,58 +183,36 @@ alerts_queue(struct window *w, int flags)
static int static int
alerts_check_bell(struct window *w) alerts_check_bell(struct window *w)
{ {
struct window *ws;
struct winlink *wl; struct winlink *wl;
struct session *s; struct session *s;
struct client *c;
int action, visual;
if (~w->flags & WINDOW_BELL) if (~w->flags & WINDOW_BELL)
return (0); return (0);
if (!options_get_number(w->options, "monitor-bell"))
return (0);
TAILQ_FOREACH(wl, &w->winlinks, wentry) TAILQ_FOREACH(wl, &w->winlinks, wentry)
wl->session->flags &= ~SESSION_ALERTED; wl->session->flags &= ~SESSION_ALERTED;
TAILQ_FOREACH(wl, &w->winlinks, wentry) { TAILQ_FOREACH(wl, &w->winlinks, wentry) {
if (wl->flags & WINLINK_BELL) /*
continue; * Bells are allowed even if there is an existing bell (so do
* not check WINLINK_BELL).
*/
s = wl->session; s = wl->session;
if (s->curw != wl) { if (s->curw != wl) {
wl->flags |= WINLINK_BELL; wl->flags |= WINLINK_BELL;
notify_winlink("alert-bell", wl); server_status_session(s);
} }
if (!alerts_action_applies(wl, "bell-action"))
continue;
notify_winlink("alert-bell", wl);
if (s->flags & SESSION_ALERTED) if (s->flags & SESSION_ALERTED)
continue; continue;
s->flags |= SESSION_ALERTED; s->flags |= SESSION_ALERTED;
action = options_get_number(s->options, "bell-action"); alerts_set_message(wl, "Bell", "visual-bell");
if (action == BELL_NONE)
return (0);
visual = options_get_number(s->options, "visual-bell");
TAILQ_FOREACH(c, &clients, entry) {
if (c->session != s || c->flags & CLIENT_CONTROL)
continue;
ws = c->session->curw->window;
if (action == BELL_CURRENT && ws != w)
action = BELL_NONE;
if (action == BELL_OTHER && ws != w)
action = BELL_NONE;
if (!visual) {
if (action != BELL_NONE)
tty_putcode(&c->tty, TTYC_BEL);
continue;
}
if (action == BELL_CURRENT)
status_message_set(c, "Bell in current window");
else if (action != BELL_NONE) {
status_message_set(c, "Bell in window %d",
wl->idx);
}
}
} }
return (WINDOW_BELL); return (WINDOW_BELL);
@@ -237,20 +236,19 @@ alerts_check_activity(struct window *w)
if (wl->flags & WINLINK_ACTIVITY) if (wl->flags & WINLINK_ACTIVITY)
continue; continue;
s = wl->session; s = wl->session;
if (s->curw == wl) if (s->curw != wl) {
wl->flags |= WINLINK_ACTIVITY;
server_status_session(s);
}
if (!alerts_action_applies(wl, "activity-action"))
continue; continue;
wl->flags |= WINLINK_ACTIVITY;
notify_winlink("alert-activity", wl); notify_winlink("alert-activity", wl);
if (s->flags & SESSION_ALERTED) if (s->flags & SESSION_ALERTED)
continue; continue;
s->flags |= SESSION_ALERTED; s->flags |= SESSION_ALERTED;
if (options_get_number(s->options, "bell-on-alert")) alerts_set_message(wl, "Activity", "visual-activity");
alerts_ring_bell(s);
if (options_get_number(s->options, "visual-activity"))
alerts_set_message(s, "Activity in window %d", wl->idx);
} }
return (WINDOW_ACTIVITY); return (WINDOW_ACTIVITY);
@@ -264,7 +262,7 @@ alerts_check_silence(struct window *w)
if (~w->flags & WINDOW_SILENCE) if (~w->flags & WINDOW_SILENCE)
return (0); return (0);
if (!options_get_number(w->options, "monitor-silence")) if (options_get_number(w->options, "monitor-silence") == 0)
return (0); return (0);
TAILQ_FOREACH(wl, &w->winlinks, wentry) TAILQ_FOREACH(wl, &w->winlinks, wentry)
@@ -274,51 +272,52 @@ alerts_check_silence(struct window *w)
if (wl->flags & WINLINK_SILENCE) if (wl->flags & WINLINK_SILENCE)
continue; continue;
s = wl->session; s = wl->session;
if (s->curw == wl) if (s->curw != wl) {
wl->flags |= WINLINK_SILENCE;
server_status_session(s);
}
if (!alerts_action_applies(wl, "silence-action"))
continue; continue;
wl->flags |= WINLINK_SILENCE;
notify_winlink("alert-silence", wl); notify_winlink("alert-silence", wl);
if (s->flags & SESSION_ALERTED) if (s->flags & SESSION_ALERTED)
continue; continue;
s->flags |= SESSION_ALERTED; s->flags |= SESSION_ALERTED;
if (options_get_number(s->options, "bell-on-alert")) alerts_set_message(wl, "Silence", "visual-silence");
alerts_ring_bell(s);
if (!options_get_number(s->options, "visual-silence"))
alerts_set_message(s, "Silence in window %d", wl->idx);
} }
return (WINDOW_SILENCE); return (WINDOW_SILENCE);
} }
static void static void
alerts_set_message(struct session *s, const char *fmt, ...) alerts_set_message(struct winlink *wl, const char *type, const char *option)
{ {
struct client *c; struct client *c;
va_list ap; int visual;
char *message;
va_start(ap, fmt); /*
xvasprintf(&message, fmt, ap); * We have found an alert (bell, activity or silence), so we need to
va_end(ap); * pass it on to the user. For each client attached to this session,
* decide whether a bell, message or both is needed.
*
* If visual-{bell,activity,silence} is on, then a message is
* substituted for a bell; if it is off, a bell is sent as normal; both
* mean both a bell and message is sent.
*/
visual = options_get_number(wl->session->options, option);
TAILQ_FOREACH(c, &clients, entry) { TAILQ_FOREACH(c, &clients, entry) {
if (c->session == s) if (c->session != wl->session || c->flags & CLIENT_CONTROL)
status_message_set(c, "%s", message); continue;
}
free(message); if (visual == VISUAL_OFF || visual == VISUAL_BOTH)
}
static void
alerts_ring_bell(struct session *s)
{
struct client *c;
TAILQ_FOREACH(c, &clients, entry) {
if (c->session == s && !(c->flags & CLIENT_CONTROL))
tty_putcode(&c->tty, TTYC_BEL); tty_putcode(&c->tty, TTYC_BEL);
if (visual == VISUAL_OFF)
continue;
if (c->session->curw == wl)
status_message_set(c, "%s in current window", type);
else
status_message_set(c, "%s in window %d", type, wl->idx);
} }
} }

View File

@@ -34,7 +34,6 @@ struct args_entry {
RB_ENTRY(args_entry) entry; RB_ENTRY(args_entry) entry;
}; };
static void args_set(struct args *, u_char, const char *);
static struct args_entry *args_find(struct args *, u_char); static struct args_entry *args_find(struct args *, u_char);
static int args_cmp(struct args_entry *, struct args_entry *); static int args_cmp(struct args_entry *, struct args_entry *);
@@ -191,11 +190,11 @@ args_print(struct args *args)
int int
args_has(struct args *args, u_char ch) args_has(struct args *args, u_char ch)
{ {
return (args_find(args, ch) == NULL ? 0 : 1); return (args_find(args, ch) != NULL);
} }
/* Set argument value in the arguments tree. */ /* Set argument value in the arguments tree. */
static void void
args_set(struct args *args, u_char ch, const char *value) args_set(struct args *args, u_char ch, const char *value)
{ {
struct args_entry *entry; struct args_entry *entry;

177
cfg.c
View File

@@ -23,10 +23,20 @@
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#include <unistd.h>
#include "tmux.h" #include "tmux.h"
/* Condition for %if, %elif, %else and %endif. */
struct cfg_cond {
size_t line; /* line number of %if */
int met; /* condition was met */
int skip; /* skip later %elif/%else */
int saw_else; /* saw a %else */
TAILQ_ENTRY(cfg_cond) entry;
};
TAILQ_HEAD(cfg_conds, cfg_cond);
static char *cfg_file; static char *cfg_file;
int cfg_finished; int cfg_finished;
static char **cfg_causes; static char **cfg_causes;
@@ -102,6 +112,124 @@ start_cfg(void)
cmdq_append(NULL, cmdq_get_callback(cfg_done, NULL)); cmdq_append(NULL, cmdq_get_callback(cfg_done, NULL));
} }
static int
cfg_check_condition(const char *path, size_t line, const char *p, int *skip)
{
struct format_tree *ft;
char *s;
int result;
while (isspace((u_char)*p))
p++;
if (p[0] == '\0') {
cfg_add_cause("%s:%zu: invalid condition", path, line);
*skip = 1;
return (0);
}
ft = format_create(NULL, NULL, FORMAT_NONE, FORMAT_NOJOBS);
s = format_expand(ft, p);
result = format_true(s);
free(s);
format_free(ft);
*skip = result;
return (result);
}
static void
cfg_handle_if(const char *path, size_t line, struct cfg_conds *conds,
const char *p)
{
struct cfg_cond *cond;
struct cfg_cond *parent = TAILQ_FIRST(conds);
/*
* Add a new condition. If a previous condition exists and isn't
* currently met, this new one also can't be met.
*/
cond = xcalloc(1, sizeof *cond);
cond->line = line;
if (parent == NULL || parent->met)
cond->met = cfg_check_condition(path, line, p, &cond->skip);
else
cond->skip = 1;
cond->saw_else = 0;
TAILQ_INSERT_HEAD(conds, cond, entry);
}
static void
cfg_handle_elif(const char *path, size_t line, struct cfg_conds *conds,
const char *p)
{
struct cfg_cond *cond = TAILQ_FIRST(conds);
/*
* If a previous condition exists and wasn't met, check this
* one instead and change the state.
*/
if (cond == NULL || cond->saw_else)
cfg_add_cause("%s:%zu: unexpected %%elif", path, line);
else if (!cond->skip)
cond->met = cfg_check_condition(path, line, p, &cond->skip);
else
cond->met = 0;
}
static void
cfg_handle_else(const char *path, size_t line, struct cfg_conds *conds)
{
struct cfg_cond *cond = TAILQ_FIRST(conds);
/*
* If a previous condition exists and wasn't met and wasn't already
* %else, use this one instead.
*/
if (cond == NULL || cond->saw_else) {
cfg_add_cause("%s:%zu: unexpected %%else", path, line);
return;
}
cond->saw_else = 1;
cond->met = !cond->skip;
cond->skip = 1;
}
static void
cfg_handle_endif(const char *path, size_t line, struct cfg_conds *conds)
{
struct cfg_cond *cond = TAILQ_FIRST(conds);
/*
* Remove previous condition if one exists.
*/
if (cond == NULL) {
cfg_add_cause("%s:%zu: unexpected %%endif", path, line);
return;
}
TAILQ_REMOVE(conds, cond, entry);
free(cond);
}
static void
cfg_handle_directive(const char *p, const char *path, size_t line,
struct cfg_conds *conds)
{
int n = 0;
while (p[n] != '\0' && !isspace((u_char)p[n]))
n++;
if (strncmp(p, "%if", n) == 0)
cfg_handle_if(path, line, conds, p + n);
else if (strncmp(p, "%elif", n) == 0)
cfg_handle_elif(path, line, conds, p + n);
else if (strcmp(p, "%else") == 0)
cfg_handle_else(path, line, conds);
else if (strcmp(p, "%endif") == 0)
cfg_handle_endif(path, line, conds);
else
cfg_add_cause("%s:%zu: invalid directive: %s", path, line, p);
}
int int
load_cfg(const char *path, struct client *c, struct cmdq_item *item, int quiet) load_cfg(const char *path, struct client *c, struct cmdq_item *item, int quiet)
{ {
@@ -109,11 +237,13 @@ load_cfg(const char *path, struct client *c, struct cmdq_item *item, int quiet)
const char delim[3] = { '\\', '\\', '\0' }; const char delim[3] = { '\\', '\\', '\0' };
u_int found = 0; u_int found = 0;
size_t line = 0; size_t line = 0;
char *buf, *cause1, *p, *q, *s; char *buf, *cause1, *p, *q;
struct cmd_list *cmdlist; struct cmd_list *cmdlist;
struct cmdq_item *new_item; struct cmdq_item *new_item;
int condition = 0; struct cfg_cond *cond, *cond1;
struct format_tree *ft; struct cfg_conds conds;
TAILQ_INIT(&conds);
log_debug("loading %s", path); log_debug("loading %s", path);
if ((f = fopen(path, "rb")) == NULL) { if ((f = fopen(path, "rb")) == NULL) {
@@ -137,33 +267,12 @@ load_cfg(const char *path, struct client *c, struct cmdq_item *item, int quiet)
while (q != p && isspace((u_char)*q)) while (q != p && isspace((u_char)*q))
*q-- = '\0'; *q-- = '\0';
if (condition != 0 && strcmp(p, "%endif") == 0) { if (*p == '%') {
condition = 0; cfg_handle_directive(p, path, line, &conds);
continue; continue;
} }
if (strncmp(p, "%if ", 4) == 0) { cond = TAILQ_FIRST(&conds);
if (condition != 0) { if (cond != NULL && !cond->met)
cfg_add_cause("%s:%zu: nested %%if", path,
line);
continue;
}
ft = format_create(NULL, NULL, FORMAT_NONE,
FORMAT_NOJOBS);
s = p + 3;
while (isspace((u_char)*s))
s++;
s = format_expand(ft, s);
if (*s != '\0' && (s[0] != '0' || s[1] != '\0'))
condition = 1;
else
condition = -1;
free(s);
format_free(ft);
continue;
}
if (condition == -1)
continue; continue;
cmdlist = cmd_string_parse(p, path, line, &cause1); cmdlist = cmd_string_parse(p, path, line, &cause1);
@@ -177,8 +286,6 @@ load_cfg(const char *path, struct client *c, struct cmdq_item *item, int quiet)
} }
free(buf); free(buf);
if (cmdlist == NULL)
continue;
new_item = cmdq_get_command(cmdlist, NULL, NULL, 0); new_item = cmdq_get_command(cmdlist, NULL, NULL, 0);
if (item != NULL) if (item != NULL)
cmdq_insert_after(item, new_item); cmdq_insert_after(item, new_item);
@@ -190,6 +297,12 @@ load_cfg(const char *path, struct client *c, struct cmdq_item *item, int quiet)
} }
fclose(f); fclose(f);
TAILQ_FOREACH_REVERSE_SAFE(cond, &conds, cfg_conds, entry, cond1) {
cfg_add_cause("%s:%zu: unterminated %%if", path, cond->line);
TAILQ_REMOVE(&conds, cond, entry);
free(cond);
}
return (found); return (found);
} }
@@ -233,7 +346,7 @@ cfg_show_causes(struct session *s)
return; return;
wp = s->curw->window->active; wp = s->curw->window->active;
window_pane_set_mode(wp, &window_copy_mode); window_pane_set_mode(wp, &window_copy_mode, NULL, NULL);
window_copy_init_for_output(wp); window_copy_init_for_output(wp);
for (i = 0; i < cfg_ncauses; i++) { for (i = 0; i < cfg_ncauses; i++) {
window_copy_add(wp, "%s", cfg_causes[i]); window_copy_add(wp, "%s", cfg_causes[i]);

View File

@@ -63,7 +63,7 @@ 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 *);
static void client_dispatch_wait(struct imsg *, const char *); static void client_dispatch_wait(struct imsg *);
static const char *client_exit_message(void); static const char *client_exit_message(void);
/* /*
@@ -156,7 +156,7 @@ retry:
close(lockfd); close(lockfd);
return (-1); return (-1);
} }
fd = server_start(base, lockfd, lockfile); fd = server_start(client_proc, base, lockfd, lockfile);
} }
if (locked && lockfd >= 0) { if (locked && lockfd >= 0) {
@@ -214,8 +214,7 @@ client_exit_message(void)
/* 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)
const char *shellcmd)
{ {
struct cmd *cmd; struct cmd *cmd;
struct cmd_list *cmdlist; struct cmd_list *cmdlist;
@@ -236,7 +235,7 @@ client_main(struct event_base *base, int argc, char **argv, int flags,
/* Set up the initial command. */ /* Set up the initial command. */
cmdflags = 0; cmdflags = 0;
if (shellcmd != NULL) { if (shell_command != NULL) {
msg = MSG_SHELL; msg = MSG_SHELL;
cmdflags = CMD_STARTSERVER; cmdflags = CMD_STARTSERVER;
} else if (argc == 0) { } else if (argc == 0) {
@@ -261,7 +260,8 @@ client_main(struct event_base *base, int argc, char **argv, int flags,
} }
/* Create client process structure (starts logging). */ /* Create client process structure (starts logging). */
client_proc = proc_start("client", base, 0, client_signal); client_proc = proc_start("client");
proc_set_signals(client_proc, client_signal);
/* Initialize the client socket and start the server. */ /* Initialize the client socket and start the server. */
fd = client_connect(base, socket_path, cmdflags & CMD_STARTSERVER); fd = client_connect(base, socket_path, cmdflags & CMD_STARTSERVER);
@@ -275,14 +275,13 @@ client_main(struct event_base *base, int argc, char **argv, int flags,
} }
return (1); return (1);
} }
client_peer = proc_add_peer(client_proc, fd, client_dispatch, client_peer = proc_add_peer(client_proc, fd, client_dispatch, NULL);
(void *)shellcmd);
/* Save these before pledge(). */ /* Save these before pledge(). */
if ((cwd = getcwd(path, sizeof path)) == NULL) { if ((cwd = getenv("PWD")) == NULL &&
if ((cwd = find_home()) == NULL) (cwd = getcwd(path, sizeof path)) == NULL &&
cwd = "/"; (cwd = find_home()) == NULL)
} cwd = "/";
if ((ttynam = ttyname(STDIN_FILENO)) == NULL) if ((ttynam = ttyname(STDIN_FILENO)) == NULL)
ttynam = ""; ttynam = "";
@@ -339,6 +338,10 @@ client_main(struct event_base *base, int argc, char **argv, int flags,
size = 0; size = 0;
for (i = 0; i < argc; i++) for (i = 0; i < argc; i++)
size += strlen(argv[i]) + 1; size += strlen(argv[i]) + 1;
if (size > MAX_IMSGSIZE - (sizeof *data)) {
fprintf(stderr, "command too long\n");
return (1);
}
data = xmalloc((sizeof *data) + size); data = xmalloc((sizeof *data) + size);
/* Prepare command for server. */ /* Prepare command for server. */
@@ -367,7 +370,6 @@ client_main(struct event_base *base, int argc, char **argv, int flags,
if (client_exittype == MSG_EXEC) { if (client_exittype == MSG_EXEC) {
if (client_flags & CLIENT_CONTROLCONTROL) if (client_flags & CLIENT_CONTROLCONTROL)
tcsetattr(STDOUT_FILENO, TCSAFLUSH, &saved_tio); tcsetattr(STDOUT_FILENO, TCSAFLUSH, &saved_tio);
clear_signals(0);
client_exec(client_execshell, client_execcmd); client_exec(client_execshell, client_execcmd);
} }
@@ -451,6 +453,7 @@ client_write(int fd, const char *data, size_t size)
{ {
ssize_t used; ssize_t used;
log_debug("%s: %.*s", __func__, (int)size, data);
while (size != 0) { while (size != 0) {
used = write(fd, data, size); used = write(fd, data, size);
if (used == -1) { if (used == -1) {
@@ -483,6 +486,8 @@ client_exec(const char *shell, const char *shellcmd)
xasprintf(&argv0, "%s", name); xasprintf(&argv0, "%s", name);
setenv("SHELL", shell, 1); setenv("SHELL", shell, 1);
proc_clear_signals(client_proc, 1);
setblocking(STDIN_FILENO, 1); setblocking(STDIN_FILENO, 1);
setblocking(STDOUT_FILENO, 1); setblocking(STDOUT_FILENO, 1);
setblocking(STDERR_FILENO, 1); setblocking(STDERR_FILENO, 1);
@@ -534,7 +539,7 @@ client_signal(int sig)
/* Callback for client read events. */ /* Callback for client read events. */
static void static void
client_dispatch(struct imsg *imsg, void *arg) client_dispatch(struct imsg *imsg, __unused void *arg)
{ {
if (imsg == NULL) { if (imsg == NULL) {
client_exitreason = CLIENT_EXIT_LOST_SERVER; client_exitreason = CLIENT_EXIT_LOST_SERVER;
@@ -546,12 +551,12 @@ client_dispatch(struct imsg *imsg, void *arg)
if (client_attached) if (client_attached)
client_dispatch_attached(imsg); client_dispatch_attached(imsg);
else else
client_dispatch_wait(imsg, arg); client_dispatch_wait(imsg);
} }
/* Dispatch imsgs when in wait state (before MSG_READY). */ /* Dispatch imsgs when in wait state (before MSG_READY). */
static void static void
client_dispatch_wait(struct imsg *imsg, const char *shellcmd) client_dispatch_wait(struct imsg *imsg)
{ {
char *data; char *data;
ssize_t datalen; ssize_t datalen;
@@ -630,8 +635,7 @@ client_dispatch_wait(struct imsg *imsg, const char *shellcmd)
if (datalen == 0 || data[datalen - 1] != '\0') if (datalen == 0 || data[datalen - 1] != '\0')
fatalx("bad MSG_SHELL string"); fatalx("bad MSG_SHELL string");
clear_signals(0); client_exec(data, shell_command);
client_exec(data, shellcmd);
/* NOTREACHED */ /* NOTREACHED */
case MSG_DETACH: case MSG_DETACH:
case MSG_DETACHKILL: case MSG_DETACHKILL:

View File

@@ -90,9 +90,9 @@ cmd_attach_session(struct cmdq_item *item, const char *tflag, int dflag,
window_set_active_pane(wp->window, wp); window_set_active_pane(wp->window, wp);
session_set_current(s, wl); session_set_current(s, wl);
if (wp != NULL) if (wp != NULL)
cmd_find_from_winlink_pane(current, wl, wp); cmd_find_from_winlink_pane(current, wl, wp, 0);
else else
cmd_find_from_winlink(current, wl); cmd_find_from_winlink(current, wl, 0);
} }
if (cflag != NULL) { if (cflag != NULL) {

View File

@@ -97,7 +97,7 @@ cmd_break_pane_exec(struct cmd *self, struct cmdq_item *item)
wl = session_attach(dst_s, w, idx, &cause); /* can't fail */ wl = session_attach(dst_s, w, idx, &cause); /* can't fail */
if (!args_has(self->args, 'd')) { if (!args_has(self->args, 'd')) {
session_select(dst_s, wl->idx); session_select(dst_s, wl->idx);
cmd_find_from_session(current, dst_s); cmd_find_from_session(current, dst_s, 0);
} }
server_redraw_session(src_s); server_redraw_session(src_s);

View File

@@ -1,101 +0,0 @@
/* $OpenBSD$ */
/*
* Copyright (c) 2010 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 <ctype.h>
#include <stdlib.h>
#include "tmux.h"
/*
* Enter choice mode to choose a buffer.
*/
#define CHOOSE_BUFFER_TEMPLATE \
"#{buffer_name}: #{buffer_size} bytes: #{buffer_sample}"
static enum cmd_retval cmd_choose_buffer_exec(struct cmd *,
struct cmdq_item *);
const struct cmd_entry cmd_choose_buffer_entry = {
.name = "choose-buffer",
.alias = NULL,
.args = { "F:t:", 0, 1 },
.usage = CMD_TARGET_WINDOW_USAGE " [-F format] [template]",
.target = { 't', CMD_FIND_WINDOW, 0 },
.flags = 0,
.exec = cmd_choose_buffer_exec
};
static enum cmd_retval
cmd_choose_buffer_exec(struct cmd *self, struct cmdq_item *item)
{
struct args *args = self->args;
struct client *c = cmd_find_client(item, NULL, 1);
struct winlink *wl = item->target.wl;
struct window_choose_data *cdata;
struct paste_buffer *pb;
char *action, *action_data;
const char *template;
u_int idx;
if (c == NULL) {
cmdq_error(item, "no client available");
return (CMD_RETURN_ERROR);
}
if ((template = args_get(args, 'F')) == NULL)
template = CHOOSE_BUFFER_TEMPLATE;
if (paste_get_top(NULL) == NULL)
return (CMD_RETURN_NORMAL);
if (window_pane_set_mode(wl->window->active, &window_choose_mode) != 0)
return (CMD_RETURN_NORMAL);
if (args->argc != 0)
action = xstrdup(args->argv[0]);
else
action = xstrdup("paste-buffer -b '%%'");
idx = 0;
pb = NULL;
while ((pb = paste_walk(pb)) != NULL) {
cdata = window_choose_data_create(TREE_OTHER, c, c->session);
cdata->idx = idx;
cdata->ft_template = xstrdup(template);
format_defaults_paste_buffer(cdata->ft, pb);
xasprintf(&action_data, "%s", paste_buffer_name(pb));
cdata->command = cmd_template_replace(action, action_data, 1);
free(action_data);
window_choose_add(wl->window->active, cdata);
idx++;
}
free(action);
window_choose_ready(wl->window->active, 0, NULL);
return (CMD_RETURN_NORMAL);
}

View File

@@ -1,135 +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 <ctype.h>
#include <stdlib.h>
#include "tmux.h"
/*
* Enter choice mode to choose a client.
*/
#define CHOOSE_CLIENT_TEMPLATE \
"#{client_name}: #{session_name} " \
"[#{client_width}x#{client_height} #{client_termname}]" \
"#{?client_utf8, (utf8),}#{?client_readonly, (ro),} " \
"(last used #{t:client_activity})"
static enum cmd_retval cmd_choose_client_exec(struct cmd *,
struct cmdq_item *);
static void cmd_choose_client_callback(struct window_choose_data *);
const struct cmd_entry cmd_choose_client_entry = {
.name = "choose-client",
.alias = NULL,
.args = { "F:t:", 0, 1 },
.usage = CMD_TARGET_WINDOW_USAGE " [-F format] [template]",
.target = { 't', CMD_FIND_WINDOW, 0 },
.flags = 0,
.exec = cmd_choose_client_exec
};
struct cmd_choose_client_data {
struct client *client;
};
static enum cmd_retval
cmd_choose_client_exec(struct cmd *self, struct cmdq_item *item)
{
struct args *args = self->args;
struct client *c = cmd_find_client(item, NULL, 1);
struct client *c1;
struct window_choose_data *cdata;
struct winlink *wl = item->target.wl;
const char *template;
char *action;
u_int idx, cur;
if (c == NULL) {
cmdq_error(item, "no client available");
return (CMD_RETURN_ERROR);
}
if (window_pane_set_mode(wl->window->active, &window_choose_mode) != 0)
return (CMD_RETURN_NORMAL);
if ((template = args_get(args, 'F')) == NULL)
template = CHOOSE_CLIENT_TEMPLATE;
if (args->argc != 0)
action = xstrdup(args->argv[0]);
else
action = xstrdup("detach-client -t '%%'");
cur = idx = 0;
TAILQ_FOREACH(c1, &clients, entry) {
if (c1->session == NULL)
continue;
if (c1 == item->client)
cur = idx;
cdata = window_choose_data_create(TREE_OTHER, c, c->session);
cdata->idx = idx;
cdata->ft_template = xstrdup(template);
format_add(cdata->ft, "line", "%u", idx);
format_defaults(cdata->ft, c1, NULL, NULL, NULL);
cdata->command = cmd_template_replace(action, c1->name, 1);
window_choose_add(wl->window->active, cdata);
idx++;
}
free(action);
window_choose_ready(wl->window->active, cur,
cmd_choose_client_callback);
return (CMD_RETURN_NORMAL);
}
static void
cmd_choose_client_callback(struct window_choose_data *cdata)
{
struct client *c;
u_int idx;
if (cdata == NULL)
return;
if (cdata->start_client->flags & CLIENT_DEAD)
return;
idx = 0;
TAILQ_FOREACH(c, &clients, entry) {
if (idx == cdata->idx)
break;
idx++;
}
if (c == NULL || c->session == NULL)
return;
window_choose_data_run(cdata);
}

View File

@@ -18,66 +18,51 @@
#include <sys/types.h> #include <sys/types.h>
#include <ctype.h>
#include <stdlib.h>
#include <string.h>
#include "tmux.h" #include "tmux.h"
#define CMD_CHOOSE_TREE_WINDOW_ACTION "select-window -t '%%'"
#define CMD_CHOOSE_TREE_SESSION_ACTION "switch-client -t '%%'"
/* /*
* Enter choice mode to choose a session and/or window. * Enter a mode.
*/ */
#define CHOOSE_TREE_SESSION_TEMPLATE \
"#{session_name}: #{session_windows} windows" \
"#{?session_grouped, (group ,}" \
"#{session_group}#{?session_grouped,),}" \
"#{?session_attached, (attached),}"
#define CHOOSE_TREE_WINDOW_TEMPLATE \
"#{window_index}: #{window_name}#{window_flags} " \
"\"#{pane_title}\""
static enum cmd_retval cmd_choose_tree_exec(struct cmd *, struct cmdq_item *); static enum cmd_retval cmd_choose_tree_exec(struct cmd *, struct cmdq_item *);
const struct cmd_entry cmd_choose_tree_entry = { const struct cmd_entry cmd_choose_tree_entry = {
.name = "choose-tree", .name = "choose-tree",
.alias = NULL, .alias = NULL,
.args = { "S:W:swub:c:t:", 0, 1 }, .args = { "F:Gf:NO:st:wZ", 0, 1 },
.usage = "[-suw] [-b session-template] [-c window template] " .usage = "[-GNsw] [-F format] [-f filter] [-O sort-order] "
"[-S format] [-W format] " CMD_TARGET_WINDOW_USAGE, CMD_TARGET_PANE_USAGE,
.target = { 't', CMD_FIND_WINDOW, 0 }, .target = { 't', CMD_FIND_PANE, 0 },
.flags = 0, .flags = 0,
.exec = cmd_choose_tree_exec .exec = cmd_choose_tree_exec
}; };
const struct cmd_entry cmd_choose_session_entry = { const struct cmd_entry cmd_choose_client_entry = {
.name = "choose-session", .name = "choose-client",
.alias = NULL, .alias = NULL,
.args = { "F:t:", 0, 1 }, .args = { "F:f:NO:t:Z", 0, 1 },
.usage = CMD_TARGET_WINDOW_USAGE " [-F format] [template]", .usage = "[-N] [-F format] [-f filter] [-O sort-order] "
CMD_TARGET_PANE_USAGE,
.target = { 't', CMD_FIND_WINDOW, 0 }, .target = { 't', CMD_FIND_PANE, 0 },
.flags = 0, .flags = 0,
.exec = cmd_choose_tree_exec .exec = cmd_choose_tree_exec
}; };
const struct cmd_entry cmd_choose_window_entry = { const struct cmd_entry cmd_choose_buffer_entry = {
.name = "choose-window", .name = "choose-buffer",
.alias = NULL, .alias = NULL,
.args = { "F:t:", 0, 1 }, .args = { "F:f:NO:t:Z", 0, 1 },
.usage = CMD_TARGET_WINDOW_USAGE "[-F format] [template]", .usage = "[-N] [-F format] [-f filter] [-O sort-order] "
CMD_TARGET_PANE_USAGE,
.target = { 't', CMD_FIND_WINDOW, 0 }, .target = { 't', CMD_FIND_PANE, 0 },
.flags = 0, .flags = 0,
.exec = cmd_choose_tree_exec .exec = cmd_choose_tree_exec
@@ -87,167 +72,20 @@ static enum cmd_retval
cmd_choose_tree_exec(struct cmd *self, struct cmdq_item *item) cmd_choose_tree_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 window_pane *wp = item->target.wp;
struct winlink *wl = item->target.wl, *wm; const struct window_mode *mode;
struct session *s = item->target.s, *s2;
struct window_choose_data *wcd = NULL;
const char *ses_template, *win_template;
char *final_win_action, *cur_win_template;
char *final_win_template_middle;
char *final_win_template_last;
const char *ses_action, *win_action;
u_int cur_win, idx_ses, win_ses, win_max;
u_int wflag, sflag;
ses_template = win_template = NULL; if (self->entry == &cmd_choose_buffer_entry) {
ses_action = win_action = NULL; if (paste_get_top(NULL) == NULL)
return (CMD_RETURN_NORMAL);
if (c == NULL) { mode = &window_buffer_mode;
cmdq_error(item, "no client available"); } else if (self->entry == &cmd_choose_client_entry) {
return (CMD_RETURN_ERROR); if (server_client_how_many() == 0)
} return (CMD_RETURN_NORMAL);
mode = &window_client_mode;
if (window_pane_set_mode(wl->window->active, &window_choose_mode) != 0)
return (CMD_RETURN_NORMAL);
/* Sort out which command this is. */
wflag = sflag = 0;
if (self->entry == &cmd_choose_session_entry) {
sflag = 1;
if ((ses_template = args_get(args, 'F')) == NULL)
ses_template = CHOOSE_TREE_SESSION_TEMPLATE;
if (args->argc != 0)
ses_action = args->argv[0];
else
ses_action = CMD_CHOOSE_TREE_SESSION_ACTION;
} else if (self->entry == &cmd_choose_window_entry) {
wflag = 1;
if ((win_template = args_get(args, 'F')) == NULL)
win_template = CHOOSE_TREE_WINDOW_TEMPLATE;
if (args->argc != 0)
win_action = args->argv[0];
else
win_action = CMD_CHOOSE_TREE_WINDOW_ACTION;
} else {
wflag = args_has(args, 'w');
sflag = args_has(args, 's');
if ((ses_action = args_get(args, 'b')) == NULL)
ses_action = CMD_CHOOSE_TREE_SESSION_ACTION;
if ((win_action = args_get(args, 'c')) == NULL)
win_action = CMD_CHOOSE_TREE_WINDOW_ACTION;
if ((ses_template = args_get(args, 'S')) == NULL)
ses_template = CHOOSE_TREE_SESSION_TEMPLATE;
if ((win_template = args_get(args, 'W')) == NULL)
win_template = CHOOSE_TREE_WINDOW_TEMPLATE;
}
/*
* If not asking for windows and sessions, assume no "-ws" given and
* hence display the entire tree outright.
*/
if (!wflag && !sflag)
wflag = sflag = 1;
/*
* If we're drawing in tree mode, including sessions, then pad the
* window template, otherwise just render the windows as a flat list
* without any padding.
*/
if (wflag && sflag) {
xasprintf(&final_win_template_middle,
" \001tq\001> %s", win_template);
xasprintf(&final_win_template_last,
" \001mq\001> %s", win_template);
} else if (wflag) {
final_win_template_middle = xstrdup(win_template);
final_win_template_last = xstrdup(win_template);
} else } else
final_win_template_middle = final_win_template_last = NULL; mode = &window_tree_mode;
idx_ses = cur_win = -1;
RB_FOREACH(s2, sessions, &sessions) {
idx_ses++;
/*
* If we're just choosing windows, jump straight there. Note
* that this implies the current session, so only choose
* windows when the session matches this one.
*/
if (wflag && !sflag) {
if (s != s2)
continue;
goto windows_only;
}
wcd = window_choose_add_session(wl->window->active,
c, s2, ses_template, ses_action, idx_ses);
/* If we're just choosing sessions, skip choosing windows. */
if (sflag && !wflag) {
if (s == s2)
cur_win = idx_ses;
continue;
}
windows_only:
win_ses = win_max = -1;
RB_FOREACH(wm, winlinks, &s2->windows)
win_max++;
RB_FOREACH(wm, winlinks, &s2->windows) {
win_ses++;
if (sflag && wflag)
idx_ses++;
if (wm == s2->curw && s == s2) {
if (wflag && !sflag) {
/*
* Then we're only counting windows.
* So remember which is the current
* window in the list.
*/
cur_win = win_ses;
} else
cur_win = idx_ses;
}
xasprintf(&final_win_action, "%s %s %s",
wcd != NULL ? wcd->command : "",
wcd != NULL ? ";" : "", win_action);
if (win_ses != win_max)
cur_win_template = final_win_template_middle;
else
cur_win_template = final_win_template_last;
window_choose_add_window(wl->window->active,
c, s2, wm, cur_win_template,
final_win_action,
(wflag && !sflag) ? win_ses : idx_ses);
free(final_win_action);
}
/*
* If we're just drawing windows, don't consider moving on to
* other sessions as we only list windows in this session.
*/
if (wflag && !sflag)
break;
}
free(final_win_template_middle);
free(final_win_template_last);
window_choose_ready(wl->window->active, cur_win, NULL);
if (args_has(args, 'u')) {
window_choose_expand_all(wl->window->active);
window_choose_set_current(wl->window->active, cur_win);
}
window_pane_set_mode(wp, mode, &item->target, args);
return (CMD_RETURN_NORMAL); return (CMD_RETURN_NORMAL);
} }

View File

@@ -32,7 +32,8 @@
static enum cmd_retval cmd_command_prompt_exec(struct cmd *, static enum cmd_retval cmd_command_prompt_exec(struct cmd *,
struct cmdq_item *); struct cmdq_item *);
static int cmd_command_prompt_callback(void *, const char *, int); static int cmd_command_prompt_callback(struct client *, void *,
const char *, int);
static void cmd_command_prompt_free(void *); static void cmd_command_prompt_free(void *);
const struct cmd_entry cmd_command_prompt_entry = { const struct cmd_entry cmd_command_prompt_entry = {
@@ -48,17 +49,16 @@ const struct cmd_entry cmd_command_prompt_entry = {
}; };
struct cmd_command_prompt_cdata { struct cmd_command_prompt_cdata {
struct client *c; int flags;
int flags;
char *inputs; char *inputs;
char *next_input; char *next_input;
char *prompts; char *prompts;
char *next_prompt; char *next_prompt;
char *template; char *template;
int idx; int idx;
}; };
static enum cmd_retval static enum cmd_retval
@@ -78,7 +78,6 @@ cmd_command_prompt_exec(struct cmd *self, struct cmdq_item *item)
return (CMD_RETURN_NORMAL); return (CMD_RETURN_NORMAL);
cdata = xcalloc(1, sizeof *cdata); cdata = xcalloc(1, sizeof *cdata);
cdata->c = c;
cdata->inputs = NULL; cdata->inputs = NULL;
cdata->next_input = NULL; cdata->next_input = NULL;
@@ -142,10 +141,10 @@ cmd_command_prompt_error(struct cmdq_item *item, void *data)
} }
static int static int
cmd_command_prompt_callback(void *data, const char *s, int done) cmd_command_prompt_callback(struct client *c, void *data, const char *s,
int done)
{ {
struct cmd_command_prompt_cdata *cdata = data; struct cmd_command_prompt_cdata *cdata = data;
struct client *c = cdata->c;
struct cmd_list *cmdlist; struct cmd_list *cmdlist;
struct cmdq_item *new_item; struct cmdq_item *new_item;
char *cause, *new_template, *prompt, *ptr; char *cause, *new_template, *prompt, *ptr;
@@ -193,7 +192,7 @@ cmd_command_prompt_callback(void *data, const char *s, int done)
if (!done) if (!done)
free(new_template); free(new_template);
if (c->prompt_callbackfn != cmd_command_prompt_callback) if (c->prompt_inputcb != cmd_command_prompt_callback)
return (1); return (1);
return (0); return (0);
} }

View File

@@ -31,7 +31,8 @@
static enum cmd_retval cmd_confirm_before_exec(struct cmd *, static enum cmd_retval cmd_confirm_before_exec(struct cmd *,
struct cmdq_item *); struct cmdq_item *);
static int cmd_confirm_before_callback(void *, const char *, int); static int cmd_confirm_before_callback(struct client *, void *,
const char *, int);
static void cmd_confirm_before_free(void *); static void cmd_confirm_before_free(void *);
const struct cmd_entry cmd_confirm_before_entry = { const struct cmd_entry cmd_confirm_before_entry = {
@@ -46,8 +47,7 @@ const struct cmd_entry cmd_confirm_before_entry = {
}; };
struct cmd_confirm_before_data { struct cmd_confirm_before_data {
char *cmd; char *cmd;
struct client *client;
}; };
static enum cmd_retval static enum cmd_retval
@@ -74,9 +74,6 @@ cmd_confirm_before_exec(struct cmd *self, struct cmdq_item *item)
cdata = xmalloc(sizeof *cdata); cdata = xmalloc(sizeof *cdata);
cdata->cmd = xstrdup(args->argv[0]); cdata->cmd = xstrdup(args->argv[0]);
cdata->client = c;
cdata->client->references++;
status_prompt_set(c, new_prompt, NULL, status_prompt_set(c, new_prompt, NULL,
cmd_confirm_before_callback, cmd_confirm_before_free, cdata, cmd_confirm_before_callback, cmd_confirm_before_free, cdata,
PROMPT_SINGLE); PROMPT_SINGLE);
@@ -97,10 +94,10 @@ cmd_confirm_before_error(struct cmdq_item *item, void *data)
} }
static int static int
cmd_confirm_before_callback(void *data, const char *s, __unused int done) cmd_confirm_before_callback(struct client *c, void *data, const char *s,
__unused int done)
{ {
struct cmd_confirm_before_data *cdata = data; struct cmd_confirm_before_data *cdata = data;
struct client *c = cdata->client;
struct cmd_list *cmdlist; struct cmd_list *cmdlist;
struct cmdq_item *new_item; struct cmdq_item *new_item;
char *cause; char *cause;
@@ -135,9 +132,6 @@ static void
cmd_confirm_before_free(void *data) cmd_confirm_before_free(void *data)
{ {
struct cmd_confirm_before_data *cdata = data; struct cmd_confirm_before_data *cdata = data;
struct client *c = cdata->client;
server_client_unref(c);
free(cdata->cmd); free(cdata->cmd);
free(cdata); free(cdata);

View File

@@ -60,6 +60,7 @@ cmd_copy_mode_exec(struct cmd *self, struct cmdq_item *item)
struct client *c = item->client; struct client *c = item->client;
struct session *s; struct session *s;
struct window_pane *wp = item->target.wp; struct window_pane *wp = item->target.wp;
int flag;
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)
@@ -69,12 +70,13 @@ cmd_copy_mode_exec(struct cmd *self, struct cmdq_item *item)
} }
if (self->entry == &cmd_clock_mode_entry) { if (self->entry == &cmd_clock_mode_entry) {
window_pane_set_mode(wp, &window_clock_mode); window_pane_set_mode(wp, &window_clock_mode, NULL, NULL);
return (CMD_RETURN_NORMAL); return (CMD_RETURN_NORMAL);
} }
if (wp->mode != &window_copy_mode) { if (wp->mode != &window_copy_mode) {
if (window_pane_set_mode(wp, &window_copy_mode) != 0) flag = window_pane_set_mode(wp, &window_copy_mode, NULL, NULL);
if (flag != 0)
return (CMD_RETURN_NORMAL); return (CMD_RETURN_NORMAL);
window_copy_init_from_pane(wp, args_has(self->args, 'e')); window_copy_init_from_pane(wp, args_has(self->args, 'e'));
} }

View File

@@ -37,8 +37,8 @@ const struct cmd_entry cmd_display_panes_entry = {
.name = "display-panes", .name = "display-panes",
.alias = "displayp", .alias = "displayp",
.args = { "t:", 0, 1 }, .args = { "d:t:", 0, 1 },
.usage = CMD_TARGET_CLIENT_USAGE, .usage = "[-d duration] " CMD_TARGET_CLIENT_USAGE,
.flags = CMD_AFTERHOOK, .flags = CMD_AFTERHOOK,
.exec = cmd_display_panes_exec .exec = cmd_display_panes_exec
@@ -49,6 +49,9 @@ cmd_display_panes_exec(struct cmd *self, struct cmdq_item *item)
{ {
struct args *args = self->args; struct args *args = self->args;
struct client *c; struct client *c;
struct session *s;
u_int delay;
char *cause;
if ((c = cmd_find_client(item, args_get(args, 't'), 0)) == NULL) if ((c = cmd_find_client(item, args_get(args, 't'), 0)) == NULL)
return (CMD_RETURN_ERROR); return (CMD_RETURN_ERROR);
@@ -61,8 +64,18 @@ cmd_display_panes_exec(struct cmd *self, struct cmdq_item *item)
c->identify_callback_data = xstrdup(args->argv[0]); c->identify_callback_data = xstrdup(args->argv[0]);
else else
c->identify_callback_data = xstrdup("select-pane -t '%%'"); c->identify_callback_data = xstrdup("select-pane -t '%%'");
s = c->session;
server_client_set_identify(c); if (args_has(args, 'd')) {
delay = args_strtonum(args, 'd', 0, UINT_MAX, &cause);
if (cause != NULL) {
cmdq_error(item, "delay %s", cause);
free(cause);
return (CMD_RETURN_ERROR);
}
} else
delay = options_get_number(s->options, "display-panes-time");
server_client_set_identify(c, delay);
return (CMD_RETURN_NORMAL); return (CMD_RETURN_NORMAL);
} }

View File

@@ -18,9 +18,7 @@
#include <sys/types.h> #include <sys/types.h>
#include <fnmatch.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h>
#include "tmux.h" #include "tmux.h"
@@ -28,216 +26,69 @@
* Find window containing text. * Find window containing text.
*/ */
#define FIND_WINDOW_TEMPLATE \
"#{window_index}: #{window_name} " \
"[#{window_width}x#{window_height}] " \
"(#{window_panes} panes) #{window_find_matches}"
static enum cmd_retval cmd_find_window_exec(struct cmd *, struct cmdq_item *); static enum cmd_retval cmd_find_window_exec(struct cmd *, struct cmdq_item *);
static void cmd_find_window_callback(struct window_choose_data *);
/* Flags for determining matching behavior. */
#define CMD_FIND_WINDOW_BY_TITLE 0x1
#define CMD_FIND_WINDOW_BY_CONTENT 0x2
#define CMD_FIND_WINDOW_BY_NAME 0x4
#define CMD_FIND_WINDOW_ALL \
(CMD_FIND_WINDOW_BY_TITLE | \
CMD_FIND_WINDOW_BY_CONTENT | \
CMD_FIND_WINDOW_BY_NAME)
const struct cmd_entry cmd_find_window_entry = { const struct cmd_entry cmd_find_window_entry = {
.name = "find-window", .name = "find-window",
.alias = "findw", .alias = "findw",
.args = { "F:CNt:T", 1, 4 }, .args = { "CNt:T", 1, 1 },
.usage = "[-CNT] [-F format] " CMD_TARGET_WINDOW_USAGE " match-string", .usage = "[-CNT] " CMD_TARGET_PANE_USAGE " match-string",
.target = { 't', CMD_FIND_WINDOW, 0 }, .target = { 't', CMD_FIND_PANE, 0 },
.flags = 0, .flags = 0,
.exec = cmd_find_window_exec .exec = cmd_find_window_exec
}; };
struct cmd_find_window_data {
struct winlink *wl;
char *list_ctx;
u_int pane_id;
TAILQ_ENTRY(cmd_find_window_data) entry;
};
TAILQ_HEAD(cmd_find_window_list, cmd_find_window_data);
static u_int cmd_find_window_match_flags(struct args *);
static void cmd_find_window_match(struct cmd_find_window_list *, int,
struct winlink *, const char *, const char *);
static u_int
cmd_find_window_match_flags(struct args *args)
{
u_int match_flags = 0;
/* Turn on flags based on the options. */
if (args_has(args, 'T'))
match_flags |= CMD_FIND_WINDOW_BY_TITLE;
if (args_has(args, 'C'))
match_flags |= CMD_FIND_WINDOW_BY_CONTENT;
if (args_has(args, 'N'))
match_flags |= CMD_FIND_WINDOW_BY_NAME;
/* If none of the flags were set, default to matching anything. */
if (match_flags == 0)
match_flags = CMD_FIND_WINDOW_ALL;
return (match_flags);
}
static void
cmd_find_window_match(struct cmd_find_window_list *find_list,
int match_flags, struct winlink *wl, const char *str,
const char *searchstr)
{
struct cmd_find_window_data *find_data;
struct window_pane *wp;
u_int i, line;
char *sres;
find_data = xcalloc(1, sizeof *find_data);
i = 0;
TAILQ_FOREACH(wp, &wl->window->panes, entry) {
i++;
if ((match_flags & CMD_FIND_WINDOW_BY_NAME) &&
fnmatch(searchstr, wl->window->name, 0) == 0) {
find_data->list_ctx = xstrdup("");
break;
}
if ((match_flags & CMD_FIND_WINDOW_BY_TITLE) &&
fnmatch(searchstr, wp->base.title, 0) == 0) {
xasprintf(&find_data->list_ctx,
"pane %u title: \"%s\"", i - 1, wp->base.title);
break;
}
if (match_flags & CMD_FIND_WINDOW_BY_CONTENT &&
(sres = window_pane_search(wp, str, &line)) != NULL) {
xasprintf(&find_data->list_ctx,
"pane %u line %u: \"%s\"", i - 1, line + 1, sres);
free(sres);
break;
}
}
if (find_data->list_ctx != NULL) {
find_data->wl = wl;
find_data->pane_id = i - 1;
TAILQ_INSERT_TAIL(find_list, find_data, entry);
} else
free(find_data);
}
static enum cmd_retval static enum cmd_retval
cmd_find_window_exec(struct cmd *self, struct cmdq_item *item) cmd_find_window_exec(struct cmd *self, struct cmdq_item *item)
{ {
struct args *args = self->args; struct args *args = self->args, *new_args;
struct cmd_find_state *current = &item->shared->current; struct window_pane *wp = item->target.wp;
struct client *c = cmd_find_client(item, NULL, 1); const char *s = args->argv[0];
struct window_choose_data *cdata; char *filter, *argv = { NULL };
struct session *s = item->target.s; int C, N, T;
struct winlink *wl = item->target.wl, *wm;
struct cmd_find_window_list find_list;
struct cmd_find_window_data *find_data;
struct cmd_find_window_data *find_data1;
char *str, *searchstr;
const char *template;
u_int i, match_flags;
if (c == NULL) { C = args_has(args, 'C');
cmdq_error(item, "no client available"); N = args_has(args, 'N');
return (CMD_RETURN_ERROR); T = args_has(args, 'T');
}
if ((template = args_get(args, 'F')) == NULL) if (!C && !N && !T)
template = FIND_WINDOW_TEMPLATE; C = N = T = 1;
match_flags = cmd_find_window_match_flags(args); if (C && N && T) {
str = args->argv[0]; xasprintf(&filter,
"#{||:"
"#{C:%s},#{||:#{m:*%s*,#{window_name}},"
"#{m:*%s*,#{pane_title}}}}",
s, s, s);
} else if (C && N) {
xasprintf(&filter,
"#{||:#{C:%s},#{m:*%s*,#{window_name}}}",
s, s);
} else if (C && T) {
xasprintf(&filter,
"#{||:#{C:%s},#{m:*%s*,#{pane_title}}}",
s, s);
} else if (N && T) {
xasprintf(&filter,
"#{||:#{m:*%s*,#{window_name}},#{m:*%s*,#{pane_title}}}",
s, s);
} else if (C)
xasprintf(&filter, "#{C:%s}", s);
else if (N)
xasprintf(&filter, "#{m:*%s*,#{window_name}}", s);
else
xasprintf(&filter, "#{m:*%s*,#{pane_title}}", s);
TAILQ_INIT(&find_list); new_args = args_parse("", 1, &argv);
args_set(new_args, 'f', filter);
xasprintf(&searchstr, "*%s*", str); window_pane_set_mode(wp, &window_tree_mode, &item->target, new_args);
RB_FOREACH(wm, winlinks, &s->windows)
cmd_find_window_match(&find_list, match_flags, wm, str, searchstr);
free(searchstr);
if (TAILQ_EMPTY(&find_list)) { args_free(new_args);
cmdq_error(item, "no windows matching: %s", str); free(filter);
return (CMD_RETURN_ERROR);
}
if (TAILQ_NEXT(TAILQ_FIRST(&find_list), entry) == NULL) {
if (session_select(s, TAILQ_FIRST(&find_list)->wl->idx) == 0) {
cmd_find_from_session(current, s);
server_redraw_session(s);
}
recalculate_sizes();
goto out;
}
if (window_pane_set_mode(wl->window->active, &window_choose_mode) != 0)
goto out;
i = 0;
TAILQ_FOREACH(find_data, &find_list, entry) {
cdata = window_choose_data_create(TREE_OTHER, c, c->session);
cdata->idx = find_data->wl->idx;
cdata->wl = find_data->wl;
cdata->ft_template = xstrdup(template);
cdata->pane_id = find_data->pane_id;
format_add(cdata->ft, "line", "%u", i);
format_add(cdata->ft, "window_find_matches", "%s",
find_data->list_ctx);
format_defaults(cdata->ft, NULL, s, find_data->wl, NULL);
window_choose_add(wl->window->active, cdata);
i++;
}
window_choose_ready(wl->window->active, 0, cmd_find_window_callback);
out:
TAILQ_FOREACH_SAFE(find_data, &find_list, entry, find_data1) {
free(find_data->list_ctx);
TAILQ_REMOVE(&find_list, find_data, entry);
free(find_data);
}
return (CMD_RETURN_NORMAL); return (CMD_RETURN_NORMAL);
} }
static void
cmd_find_window_callback(struct window_choose_data *cdata)
{
struct session *s;
struct window_pane *wp;
if (cdata == NULL)
return;
s = cdata->start_session;
if (!session_alive(s))
return;
wp = window_pane_at_index(cdata->wl->window, cdata->pane_id);
if (wp != NULL && window_pane_visible(wp))
window_set_active_pane(cdata->wl->window, wp);
if (session_select(s, cdata->idx) == 0) {
server_redraw_session(s);
recalculate_sizes();
}
}

View File

@@ -95,6 +95,22 @@ cmd_find_try_TMUX(struct client *c)
return (session_find_by_id(session)); return (session_find_by_id(session));
} }
/* Find pane containing client if any. */
static struct window_pane *
cmd_find_inside_pane(struct client *c)
{
struct window_pane *wp;
if (c == NULL)
return (NULL);
RB_FOREACH(wp, window_pane_tree, &all_window_panes) {
if (strcmp(wp->tty, c->ttyname) == 0)
break;
}
return (wp);
}
/* Is this client better? */ /* Is this client better? */
static int static int
cmd_find_client_better(struct client *c, struct client *than) cmd_find_client_better(struct client *c, struct client *than)
@@ -373,7 +389,7 @@ cmd_find_get_window_with_session(struct cmd_find_state *fs, const char *window)
return (-1); return (-1);
fs->idx = s->curw->idx + n; fs->idx = s->curw->idx + n;
} else { } else {
if (n < s->curw->idx) if (n > s->curw->idx)
return (-1); return (-1);
fs->idx = s->curw->idx - n; fs->idx = s->curw->idx - n;
} }
@@ -493,7 +509,7 @@ cmd_find_get_pane(struct cmd_find_state *fs, const char *pane, int only)
/* Check for pane ids starting with %. */ /* Check for pane ids starting with %. */
if (*pane == '%') { if (*pane == '%') {
fs->wp = window_pane_find_by_id_str(pane); fs->wp = window_pane_find_by_id_str(pane);
if (fs->wp == NULL || window_pane_outside(fs->wp)) if (fs->wp == NULL)
return (-1); return (-1);
fs->w = fs->wp->window; fs->w = fs->wp->window;
return (cmd_find_best_session_with_window(fs)); return (cmd_find_best_session_with_window(fs));
@@ -530,7 +546,7 @@ cmd_find_get_pane_with_session(struct cmd_find_state *fs, const char *pane)
/* Check for pane ids starting with %. */ /* Check for pane ids starting with %. */
if (*pane == '%') { if (*pane == '%') {
fs->wp = window_pane_find_by_id_str(pane); fs->wp = window_pane_find_by_id_str(pane);
if (fs->wp == NULL || window_pane_outside(fs->wp)) if (fs->wp == NULL)
return (-1); return (-1);
fs->w = fs->wp->window; fs->w = fs->wp->window;
return (cmd_find_best_winlink_with_window(fs)); return (cmd_find_best_winlink_with_window(fs));
@@ -562,7 +578,7 @@ cmd_find_get_pane_with_window(struct cmd_find_state *fs, const char *pane)
/* Check for pane ids starting with %. */ /* Check for pane ids starting with %. */
if (*pane == '%') { if (*pane == '%') {
fs->wp = window_pane_find_by_id_str(pane); fs->wp = window_pane_find_by_id_str(pane);
if (fs->wp == NULL || window_pane_outside(fs->wp)) if (fs->wp == NULL)
return (-1); return (-1);
if (fs->wp->window != fs->w) if (fs->wp->window != fs->w)
return (-1); return (-1);
@@ -571,30 +587,28 @@ cmd_find_get_pane_with_window(struct cmd_find_state *fs, const char *pane)
/* Try special characters. */ /* Try special characters. */
if (strcmp(pane, "!") == 0) { if (strcmp(pane, "!") == 0) {
if (fs->w->last == NULL)
return (-1);
fs->wp = fs->w->last; fs->wp = fs->w->last;
if (fs->wp == NULL || window_pane_outside(fs->wp)) if (fs->wp == NULL)
return (-1); return (-1);
return (0); return (0);
} else if (strcmp(pane, "{up-of}") == 0) { } else if (strcmp(pane, "{up-of}") == 0) {
fs->wp = window_pane_find_up(fs->w->active); fs->wp = window_pane_find_up(fs->w->active);
if (fs->wp == NULL || window_pane_outside(fs->wp)) if (fs->wp == NULL)
return (-1); return (-1);
return (0); return (0);
} else if (strcmp(pane, "{down-of}") == 0) { } else if (strcmp(pane, "{down-of}") == 0) {
fs->wp = window_pane_find_down(fs->w->active); fs->wp = window_pane_find_down(fs->w->active);
if (fs->wp == NULL || window_pane_outside(fs->wp)) if (fs->wp == NULL)
return (-1); return (-1);
return (0); return (0);
} else if (strcmp(pane, "{left-of}") == 0) { } else if (strcmp(pane, "{left-of}") == 0) {
fs->wp = window_pane_find_left(fs->w->active); fs->wp = window_pane_find_left(fs->w->active);
if (fs->wp == NULL || window_pane_outside(fs->wp)) if (fs->wp == NULL)
return (-1); return (-1);
return (0); return (0);
} else if (strcmp(pane, "{right-of}") == 0) { } else if (strcmp(pane, "{right-of}") == 0) {
fs->wp = window_pane_find_right(fs->w->active); fs->wp = window_pane_find_right(fs->w->active);
if (fs->wp == NULL || window_pane_outside(fs->wp)) if (fs->wp == NULL)
return (-1); return (-1);
return (0); return (0);
} }
@@ -610,7 +624,7 @@ cmd_find_get_pane_with_window(struct cmd_find_state *fs, const char *pane)
fs->wp = window_pane_next_by_number(fs->w, wp, n); fs->wp = window_pane_next_by_number(fs->w, wp, n);
else else
fs->wp = window_pane_previous_by_number(fs->w, wp, n); fs->wp = window_pane_previous_by_number(fs->w, wp, n);
if (fs->wp != NULL && !window_pane_outside(fs->wp)) if (fs->wp != NULL)
return (0); return (0);
} }
@@ -618,13 +632,13 @@ cmd_find_get_pane_with_window(struct cmd_find_state *fs, const char *pane)
idx = strtonum(pane, 0, INT_MAX, &errstr); idx = strtonum(pane, 0, INT_MAX, &errstr);
if (errstr == NULL) { if (errstr == NULL) {
fs->wp = window_pane_at_index(fs->w, idx); fs->wp = window_pane_at_index(fs->w, idx);
if (fs->wp != NULL && !window_pane_outside(fs->wp)) if (fs->wp != NULL)
return (0); return (0);
} }
/* Try as a description. */ /* Try as a description. */
fs->wp = window_find_string(fs->w, pane); fs->wp = window_find_string(fs->w, pane);
if (fs->wp != NULL && !window_pane_outside(fs->wp)) if (fs->wp != NULL)
return (0); return (0);
return (-1); return (-1);
@@ -672,9 +686,7 @@ cmd_find_valid_state(struct cmd_find_state *fs)
if (fs->w != fs->wl->window) if (fs->w != fs->wl->window)
return (0); return (0);
if (!window_has_pane(fs->w, fs->wp)) return (window_has_pane(fs->w, fs->wp));
return (0);
return (!window_pane_outside(fs->wp));
} }
/* Copy a state. */ /* Copy a state. */
@@ -713,9 +725,9 @@ cmd_find_log_state(const char *prefix, struct cmd_find_state *fs)
/* Find state from a session. */ /* Find state from a session. */
void void
cmd_find_from_session(struct cmd_find_state *fs, struct session *s) cmd_find_from_session(struct cmd_find_state *fs, struct session *s, int flags)
{ {
cmd_find_clear_state(fs, 0); cmd_find_clear_state(fs, flags);
fs->s = s; fs->s = s;
fs->wl = fs->s->curw; fs->wl = fs->s->curw;
@@ -727,9 +739,9 @@ cmd_find_from_session(struct cmd_find_state *fs, struct session *s)
/* Find state from a winlink. */ /* Find state from a winlink. */
void void
cmd_find_from_winlink(struct cmd_find_state *fs, struct winlink *wl) cmd_find_from_winlink(struct cmd_find_state *fs, struct winlink *wl, int flags)
{ {
cmd_find_clear_state(fs, 0); cmd_find_clear_state(fs, flags);
fs->s = wl->session; fs->s = wl->session;
fs->wl = wl; fs->wl = wl;
@@ -742,14 +754,14 @@ cmd_find_from_winlink(struct cmd_find_state *fs, struct winlink *wl)
/* Find state from a session and window. */ /* Find state from a session and window. */
int int
cmd_find_from_session_window(struct cmd_find_state *fs, struct session *s, cmd_find_from_session_window(struct cmd_find_state *fs, struct session *s,
struct window *w) struct window *w, int flags)
{ {
cmd_find_clear_state(fs, 0); cmd_find_clear_state(fs, flags);
fs->s = s; fs->s = s;
fs->w = w; fs->w = w;
if (cmd_find_best_winlink_with_window(fs) != 0) { if (cmd_find_best_winlink_with_window(fs) != 0) {
cmd_find_clear_state(fs, 0); cmd_find_clear_state(fs, flags);
return (-1); return (-1);
} }
fs->wp = fs->w->active; fs->wp = fs->w->active;
@@ -760,17 +772,17 @@ cmd_find_from_session_window(struct cmd_find_state *fs, struct session *s,
/* Find state from a window. */ /* Find state from a window. */
int int
cmd_find_from_window(struct cmd_find_state *fs, struct window *w) cmd_find_from_window(struct cmd_find_state *fs, struct window *w, int flags)
{ {
cmd_find_clear_state(fs, 0); cmd_find_clear_state(fs, flags);
fs->w = w; fs->w = w;
if (cmd_find_best_session_with_window(fs) != 0) { if (cmd_find_best_session_with_window(fs) != 0) {
cmd_find_clear_state(fs, 0); cmd_find_clear_state(fs, flags);
return (-1); return (-1);
} }
if (cmd_find_best_winlink_with_window(fs) != 0) { if (cmd_find_best_winlink_with_window(fs) != 0) {
cmd_find_clear_state(fs, 0); cmd_find_clear_state(fs, flags);
return (-1); return (-1);
} }
fs->wp = fs->w->active; fs->wp = fs->w->active;
@@ -782,9 +794,9 @@ cmd_find_from_window(struct cmd_find_state *fs, struct window *w)
/* Find state from a winlink and pane. */ /* Find state from a winlink and pane. */
void void
cmd_find_from_winlink_pane(struct cmd_find_state *fs, struct winlink *wl, cmd_find_from_winlink_pane(struct cmd_find_state *fs, struct winlink *wl,
struct window_pane *wp) struct window_pane *wp, int flags)
{ {
cmd_find_clear_state(fs, 0); cmd_find_clear_state(fs, flags);
fs->s = wl->session; fs->s = wl->session;
fs->wl = wl; fs->wl = wl;
@@ -797,14 +809,10 @@ cmd_find_from_winlink_pane(struct cmd_find_state *fs, struct winlink *wl,
/* Find state from a pane. */ /* Find state from a pane. */
int int
cmd_find_from_pane(struct cmd_find_state *fs, struct window_pane *wp) cmd_find_from_pane(struct cmd_find_state *fs, struct window_pane *wp, int flags)
{ {
if (cmd_find_from_window(fs, wp->window) != 0) if (cmd_find_from_window(fs, wp->window, flags) != 0)
return (-1); return (-1);
if (window_pane_outside(wp)) {
cmd_find_clear_state(fs, 0);
return (-1);
}
fs->wp = wp; fs->wp = wp;
cmd_find_log_state(__func__, fs); cmd_find_log_state(__func__, fs);
@@ -813,13 +821,13 @@ cmd_find_from_pane(struct cmd_find_state *fs, struct window_pane *wp)
/* Find state from nothing. */ /* Find state from nothing. */
int int
cmd_find_from_nothing(struct cmd_find_state *fs) cmd_find_from_nothing(struct cmd_find_state *fs, int flags)
{ {
cmd_find_clear_state(fs, 0); cmd_find_clear_state(fs, flags);
fs->s = cmd_find_best_session(NULL, 0, fs->flags); fs->s = cmd_find_best_session(NULL, 0, flags);
if (fs->s == NULL) { if (fs->s == NULL) {
cmd_find_clear_state(fs, 0); cmd_find_clear_state(fs, flags);
return (-1); return (-1);
} }
fs->wl = fs->s->curw; fs->wl = fs->s->curw;
@@ -833,16 +841,16 @@ cmd_find_from_nothing(struct cmd_find_state *fs)
/* Find state from mouse. */ /* Find state from mouse. */
int int
cmd_find_from_mouse(struct cmd_find_state *fs, struct mouse_event *m) cmd_find_from_mouse(struct cmd_find_state *fs, struct mouse_event *m, int flags)
{ {
cmd_find_clear_state(fs, 0); cmd_find_clear_state(fs, flags);
if (!m->valid) if (!m->valid)
return (-1); return (-1);
fs->wp = cmd_mouse_pane(m, &fs->s, &fs->wl); fs->wp = cmd_mouse_pane(m, &fs->s, &fs->wl);
if (fs->wp == NULL) { if (fs->wp == NULL) {
cmd_find_clear_state(fs, 0); cmd_find_clear_state(fs, flags);
return (-1); return (-1);
} }
fs->w = fs->wl->window; fs->w = fs->wl->window;
@@ -853,7 +861,7 @@ cmd_find_from_mouse(struct cmd_find_state *fs, struct mouse_event *m)
/* Find state from client. */ /* Find state from client. */
int int
cmd_find_from_client(struct cmd_find_state *fs, struct client *c) cmd_find_from_client(struct cmd_find_state *fs, struct client *c, int flags)
{ {
struct session *s; struct session *s;
struct winlink *wl; struct winlink *wl;
@@ -861,23 +869,20 @@ cmd_find_from_client(struct cmd_find_state *fs, struct client *c)
/* If no client, treat as from nothing. */ /* If no client, treat as from nothing. */
if (c == NULL) if (c == NULL)
return (cmd_find_from_nothing(fs)); return (cmd_find_from_nothing(fs, flags));
/* If this is an attached client, all done. */ /* If this is an attached client, all done. */
if (c->session != NULL) { if (c->session != NULL) {
cmd_find_from_session(fs, c->session); cmd_find_from_session(fs, c->session, flags);
return (0); return (0);
} }
cmd_find_clear_state(fs, 0); cmd_find_clear_state(fs, flags);
/* /*
* If this is an unattached client running in a pane, we can use that * If this is an unattached client running in a pane, we can use that
* to limit the list of sessions to those containing that pane. * to limit the list of sessions to those containing that pane.
*/ */
RB_FOREACH(wp, window_pane_tree, &all_window_panes) { wp = cmd_find_inside_pane(c);
if (strcmp(wp->tty, c->ttyname) == 0)
break;
}
if (wp == NULL) if (wp == NULL)
goto unknown_pane; goto unknown_pane;
@@ -905,16 +910,12 @@ cmd_find_from_client(struct cmd_find_state *fs, struct client *c)
*/ */
fs->w = wp->window; fs->w = wp->window;
if (cmd_find_best_session_with_window(fs) != 0) { if (cmd_find_best_session_with_window(fs) != 0) {
if (wp != NULL) { /*
/* * The window may have been destroyed but the pane
* The window may have been destroyed but the pane * still on all_window_panes due to something else
* still on all_window_panes due to something else * holding a reference.
* holding a reference. */
*/ goto unknown_pane;
goto unknown_pane;
}
cmd_find_clear_state(fs, 0);
return (-1);
} }
fs->wl = fs->s->curw; fs->wl = fs->s->curw;
fs->w = fs->wl->window; fs->w = fs->wl->window;
@@ -930,12 +931,12 @@ unknown_pane:
*/ */
s = cmd_find_try_TMUX(c); s = cmd_find_try_TMUX(c);
if (s != NULL) { if (s != NULL) {
cmd_find_from_session(fs, s); cmd_find_from_session(fs, s, flags);
return (0); return (0);
} }
/* Otherwise we need to guess. */ /* Otherwise we need to guess. */
return (cmd_find_from_nothing(fs)); return (cmd_find_from_nothing(fs, flags));
} }
/* /*
@@ -949,7 +950,7 @@ cmd_find_target(struct cmd_find_state *fs, struct cmdq_item *item,
struct mouse_event *m; struct mouse_event *m;
struct cmd_find_state current; struct cmd_find_state current;
char *colon, *period, *copy = NULL; char *colon, *period, *copy = NULL;
const char *session, *window, *pane; const char *session, *window, *pane, *s;
int window_only = 0, pane_only = 0; int window_only = 0, pane_only = 0;
/* Can fail flag implies quiet. */ /* Can fail flag implies quiet. */
@@ -957,10 +958,18 @@ cmd_find_target(struct cmd_find_state *fs, struct cmdq_item *item,
flags |= CMD_FIND_QUIET; flags |= CMD_FIND_QUIET;
/* Log the arguments. */ /* Log the arguments. */
if (target == NULL) if (type == CMD_FIND_PANE)
log_debug("%s: target none, type %d", __func__, type); s = "pane";
else if (type == CMD_FIND_WINDOW)
s = "window";
else if (type == CMD_FIND_SESSION)
s = "session";
else else
log_debug("%s: target %s, type %d", __func__, target, type); s = "unknown";
if (target == NULL)
log_debug("%s: target none, type %s", __func__, s);
else
log_debug("%s: target %s, type %s", __func__, target, s);
log_debug("%s: item %p, flags %#x", __func__, item, flags); log_debug("%s: item %p, flags %#x", __func__, item, flags);
/* Clear new state. */ /* Clear new state. */
@@ -973,11 +982,14 @@ cmd_find_target(struct cmd_find_state *fs, struct cmdq_item *item,
} else if (cmd_find_valid_state(&item->shared->current)) { } else if (cmd_find_valid_state(&item->shared->current)) {
fs->current = &item->shared->current; fs->current = &item->shared->current;
log_debug("%s: current is from queue", __func__); log_debug("%s: current is from queue", __func__);
} else if (cmd_find_from_client(&current, item->client) == 0) { } else if (cmd_find_from_client(&current, item->client, flags) == 0) {
fs->current = &current; fs->current = &current;
log_debug("%s: current is from client", __func__); log_debug("%s: current is from client", __func__);
} else } else {
if (~flags & CMD_FIND_QUIET)
cmdq_error(item, "no current target");
goto error; goto error;
}
if (!cmd_find_valid_state(fs->current)) if (!cmd_find_valid_state(fs->current))
fatalx("invalid current find state"); fatalx("invalid current find state");
@@ -991,7 +1003,7 @@ cmd_find_target(struct cmd_find_state *fs, struct cmdq_item *item,
switch (type) { switch (type) {
case CMD_FIND_PANE: case CMD_FIND_PANE:
fs->wp = cmd_mouse_pane(m, &fs->s, &fs->wl); fs->wp = cmd_mouse_pane(m, &fs->s, &fs->wl);
if (fs->wp != NULL && !window_pane_outside(fs->wp)) if (fs->wp != NULL)
fs->w = fs->wl->window; fs->w = fs->wl->window;
break; break;
case CMD_FIND_WINDOW: case CMD_FIND_WINDOW:
@@ -1220,30 +1232,46 @@ no_pane:
goto error; goto error;
} }
/* Find the current client. */
static struct client *
cmd_find_current_client(struct cmdq_item *item, int quiet)
{
struct client *c;
struct session *s;
struct window_pane *wp;
struct cmd_find_state fs;
if (item->client != NULL && item->client->session != NULL)
return (item->client);
c = NULL;
if ((wp = cmd_find_inside_pane(item->client)) != NULL) {
cmd_find_clear_state(&fs, CMD_FIND_QUIET);
fs.w = wp->window;
if (cmd_find_best_session_with_window(&fs) == 0)
c = cmd_find_best_client(fs.s);
} else {
s = cmd_find_best_session(NULL, 0, CMD_FIND_QUIET);
if (s != NULL)
c = cmd_find_best_client(s);
}
if (c == NULL && !quiet)
cmdq_error(item, "no current client");
log_debug("%s: no target, return %p", __func__, c);
return (c);
}
/* Find the target client or report an error and return NULL. */ /* Find the target client or report an error and return NULL. */
struct client * struct client *
cmd_find_client(struct cmdq_item *item, const char *target, int quiet) cmd_find_client(struct cmdq_item *item, const char *target, int quiet)
{ {
struct client *c; struct client *c;
struct session *s;
char *copy; char *copy;
size_t size; size_t size;
/* A NULL argument means the current client. */ /* A NULL argument means the current client. */
if (target == NULL) { if (target == NULL)
c = NULL; return (cmd_find_current_client(item, quiet));
if (item->client != NULL && item->client->session != NULL)
c = item->client;
else {
s = cmd_find_best_session(NULL, 0, CMD_FIND_QUIET);
if (s != NULL)
c = cmd_find_best_client(s);
}
if (c == NULL && !quiet)
cmdq_error(item, "no current client");
log_debug("%s: no target, return %p", __func__, c);
return (c);
}
copy = xstrdup(target); copy = xstrdup(target);
/* Trim a single trailing colon if any. */ /* Trim a single trailing colon if any. */

View File

@@ -129,7 +129,7 @@ cmd_if_shell_exec(struct cmd *self, struct cmdq_item *item)
memcpy(&cdata->mouse, &shared->mouse, sizeof cdata->mouse); memcpy(&cdata->mouse, &shared->mouse, sizeof cdata->mouse);
job_run(shellcmd, s, cwd, NULL, cmd_if_shell_callback, job_run(shellcmd, s, cwd, NULL, cmd_if_shell_callback,
cmd_if_shell_free, cdata); cmd_if_shell_free, cdata, 0);
free(shellcmd); free(shellcmd);
if (args_has(args, 'b')) if (args_has(args, 'b'))

View File

@@ -146,7 +146,7 @@ cmd_join_pane_exec(struct cmd *self, struct cmdq_item *item)
if (!args_has(args, 'd')) { if (!args_has(args, 'd')) {
window_set_active_pane(dst_w, src_wp); window_set_active_pane(dst_w, src_wp);
session_select(dst_s, dst_idx); session_select(dst_s, dst_idx);
cmd_find_from_session(current, dst_s); cmd_find_from_session(current, dst_s, 0);
server_redraw_session(dst_s); server_redraw_session(dst_s);
} else } else
server_status_session(dst_s); server_status_session(dst_s);

View File

@@ -61,12 +61,12 @@ cmd_kill_session_exec(struct cmd *self, struct cmdq_item *item)
RB_FOREACH_SAFE(sloop, sessions, &sessions, stmp) { RB_FOREACH_SAFE(sloop, sessions, &sessions, stmp) {
if (sloop != s) { if (sloop != s) {
server_destroy_session(sloop); server_destroy_session(sloop);
session_destroy(sloop); session_destroy(sloop, __func__);
} }
} }
} else { } else {
server_destroy_session(s); server_destroy_session(s);
session_destroy(s); session_destroy(s, __func__);
} }
return (CMD_RETURN_NORMAL); return (CMD_RETURN_NORMAL);
} }

View File

@@ -59,7 +59,8 @@ cmd_load_buffer_exec(struct cmd *self, struct cmdq_item *item)
struct client *c = item->client; struct client *c = item->client;
FILE *f; FILE *f;
const char *path, *bufname; const char *path, *bufname;
char *pdata, *new_pdata, *cause, *file; char *pdata = NULL, *new_pdata, *cause;
char *file;
size_t psize; size_t psize;
int ch, error; int ch, error;
@@ -89,8 +90,7 @@ cmd_load_buffer_exec(struct cmd *self, struct cmdq_item *item)
f = fopen(file, "rb"); f = fopen(file, "rb");
if (f == NULL) { if (f == NULL) {
cmdq_error(item, "%s: %s", file, strerror(errno)); cmdq_error(item, "%s: %s", file, strerror(errno));
free(file); goto error;
return (CMD_RETURN_ERROR);
} }
pdata = NULL; pdata = NULL;

View File

@@ -73,13 +73,15 @@ cmd_new_session_exec(struct cmd *self, struct cmdq_item *item)
struct environ *env; struct environ *env;
struct termios tio, *tiop; struct termios tio, *tiop;
struct session_group *sg; struct session_group *sg;
const char *newname, *errstr, *template, *group, *prefix; const char *errstr, *template, *group, *prefix;
const char *path, *cmd, *cwd, *to_free = NULL; const char *path, *cmd, *tmp;
char **argv, *cause, *cp; char **argv, *cause, *cp, *newname, *cwd = NULL;
int detached, already_attached, idx, argc; int detached, already_attached, idx, argc;
int is_control = 0;
u_int sx, sy; u_int sx, sy;
struct environ_entry *envent; struct environ_entry *envent;
struct cmd_find_state fs; struct cmd_find_state fs;
enum cmd_retval retval;
if (self->entry == &cmd_has_session_entry) { if (self->entry == &cmd_has_session_entry) {
/* /*
@@ -94,20 +96,24 @@ cmd_new_session_exec(struct cmd *self, struct cmdq_item *item)
return (CMD_RETURN_ERROR); return (CMD_RETURN_ERROR);
} }
newname = args_get(args, 's'); newname = NULL;
if (newname != NULL) { if (args_has(args, 's')) {
newname = format_single(item, args_get(args, 's'), 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);
return (CMD_RETURN_ERROR); goto error;
} }
if ((as = session_find(newname)) != NULL) { if ((as = session_find(newname)) != NULL) {
if (args_has(args, 'A')) { if (args_has(args, 'A')) {
return (cmd_attach_session(item, retval = cmd_attach_session(item,
newname, args_has(args, 'D'), newname, args_has(args, 'D'),
0, NULL, args_has(args, 'E'))); 0, NULL, args_has(args, 'E'));
free(newname);
return (retval);
} }
cmdq_error(item, "duplicate session: %s", newname); cmdq_error(item, "duplicate session: %s", newname);
return (CMD_RETURN_ERROR); goto error;
} }
} }
@@ -139,6 +145,8 @@ cmd_new_session_exec(struct cmd *self, struct cmdq_item *item)
detached = args_has(args, 'd'); detached = args_has(args, 'd');
if (c == NULL) if (c == NULL)
detached = 1; detached = 1;
else if (c->flags & CLIENT_CONTROL)
is_control = 1;
/* Is this client already attached? */ /* Is this client already attached? */
already_attached = 0; already_attached = 0;
@@ -146,13 +154,12 @@ cmd_new_session_exec(struct cmd *self, struct cmdq_item *item)
already_attached = 1; already_attached = 1;
/* Get the new session working directory. */ /* Get the new session working directory. */
if (args_has(args, 'c')) { if ((tmp = args_get(args, 'c')) != NULL)
cwd = args_get(args, 'c'); cwd = format_single(item, tmp, c, NULL, NULL, NULL);
to_free = cwd = format_single(item, cwd, c, NULL, NULL, NULL); else if (c != NULL && c->session == NULL && c->cwd != NULL)
} else if (c != NULL && c->session == NULL && c->cwd != NULL) cwd = xstrdup(c->cwd);
cwd = c->cwd;
else else
cwd = "."; cwd = xstrdup(".");
/* /*
* If this is a new client, check for nesting and save the termios * If this is a new client, check for nesting and save the termios
@@ -185,29 +192,31 @@ cmd_new_session_exec(struct cmd *self, struct cmdq_item *item)
} }
/* Find new session size. */ /* Find new session size. */
if (c != NULL) { if (!detached) {
sx = c->tty.sx; sx = c->tty.sx;
sy = c->tty.sy; sy = c->tty.sy;
if (!is_control &&
sy > 0 &&
options_get_number(global_s_options, "status"))
sy--;
} else { } else {
sx = 80; sx = 80;
sy = 24; sy = 24;
} }
if (detached && args_has(args, 'x')) { if ((is_control || detached) && args_has(args, 'x')) {
sx = strtonum(args_get(args, 'x'), 1, USHRT_MAX, &errstr); sx = strtonum(args_get(args, 'x'), 1, USHRT_MAX, &errstr);
if (errstr != NULL) { if (errstr != NULL) {
cmdq_error(item, "width %s", errstr); cmdq_error(item, "width %s", errstr);
goto error; goto error;
} }
} }
if (detached && args_has(args, 'y')) { if ((is_control || detached) && args_has(args, 'y')) {
sy = strtonum(args_get(args, 'y'), 1, USHRT_MAX, &errstr); sy = strtonum(args_get(args, 'y'), 1, USHRT_MAX, &errstr);
if (errstr != NULL) { if (errstr != NULL) {
cmdq_error(item, "height %s", errstr); cmdq_error(item, "height %s", errstr);
goto error; goto error;
} }
} }
if (sy > 0 && options_get_number(global_s_options, "status"))
sy--;
if (sx == 0) if (sx == 0)
sx = 1; sx = 1;
if (sy == 0) if (sy == 0)
@@ -255,10 +264,12 @@ cmd_new_session_exec(struct cmd *self, struct cmdq_item *item)
} }
/* Set the initial window name if one given. */ /* Set the initial window name if one given. */
if (argc >= 0 && args_has(args, 'n')) { if (argc >= 0 && (tmp = args_get(args, 'n')) != NULL) {
cp = format_single(item, tmp, c, s, NULL, NULL);
w = s->curw->window; w = s->curw->window;
window_set_name(w, args_get(args, 'n')); window_set_name(w, cp);
options_set_number(w->options, "automatic-rename", 0); options_set_number(w->options, "automatic-rename", 0);
free(cp);
} }
/* /*
@@ -319,19 +330,18 @@ cmd_new_session_exec(struct cmd *self, struct cmdq_item *item)
if (!detached) { if (!detached) {
c->flags |= CLIENT_ATTACHED; c->flags |= CLIENT_ATTACHED;
cmd_find_from_session(&item->shared->current, s); cmd_find_from_session(&item->shared->current, s, 0);
} }
if (to_free != NULL) cmd_find_from_session(&fs, s, 0);
free((void *)to_free);
cmd_find_from_session(&fs, s);
hooks_insert(s->hooks, item, &fs, "after-new-session"); hooks_insert(s->hooks, item, &fs, "after-new-session");
free(cwd);
free(newname);
return (CMD_RETURN_NORMAL); return (CMD_RETURN_NORMAL);
error: error:
if (to_free != NULL) free(cwd);
free((void *)to_free); free(newname);
return (CMD_RETURN_ERROR); return (CMD_RETURN_ERROR);
} }

View File

@@ -57,8 +57,8 @@ cmd_new_window_exec(struct cmd *self, struct cmdq_item *item)
struct winlink *wl = item->target.wl; struct winlink *wl = item->target.wl;
struct client *c = cmd_find_client(item, NULL, 1); struct client *c = cmd_find_client(item, NULL, 1);
int idx = item->target.idx; int idx = item->target.idx;
const char *cmd, *path, *template, *cwd, *to_free; const char *cmd, *path, *template, *tmp;
char **argv, *cause, *cp; char **argv, *cause, *cp, *cwd, *name;
int argc, detached; int argc, detached;
struct environ_entry *envent; struct environ_entry *envent;
struct cmd_find_state fs; struct cmd_find_state fs;
@@ -93,14 +93,17 @@ cmd_new_window_exec(struct cmd *self, struct cmdq_item *item)
if (envent != NULL) if (envent != NULL)
path = envent->value; path = envent->value;
to_free = NULL; if ((tmp = args_get(args, 'c')) != NULL)
if (args_has(args, 'c')) { cwd = format_single(item, tmp, c, s, NULL, NULL);
cwd = args_get(args, 'c'); else if (item->client != NULL && item->client->session == NULL)
to_free = cwd = format_single(item, cwd, c, s, NULL, NULL); cwd = xstrdup(item->client->cwd);
} else if (item->client != NULL && item->client->session == NULL)
cwd = item->client->cwd;
else else
cwd = s->cwd; cwd = xstrdup(s->cwd);
if ((tmp = args_get(args, 'n')) != NULL)
name = format_single(item, tmp, c, s, NULL, NULL);
else
name = NULL;
wl = NULL; wl = NULL;
if (idx != -1) if (idx != -1)
@@ -124,7 +127,7 @@ cmd_new_window_exec(struct cmd *self, struct cmdq_item *item)
if (idx == -1) if (idx == -1)
idx = -1 - options_get_number(s->options, "base-index"); idx = -1 - options_get_number(s->options, "base-index");
wl = session_new(s, args_get(args, 'n'), argc, argv, path, cwd, idx, wl = session_new(s, name, argc, argv, path, cwd, idx,
&cause); &cause);
if (wl == NULL) { if (wl == NULL) {
cmdq_error(item, "create window failed: %s", cause); cmdq_error(item, "create window failed: %s", cause);
@@ -133,7 +136,7 @@ cmd_new_window_exec(struct cmd *self, struct cmdq_item *item)
} }
if (!detached) { if (!detached) {
session_select(s, wl->idx); session_select(s, wl->idx);
cmd_find_from_winlink(current, wl); cmd_find_from_winlink(current, wl, 0);
server_redraw_session_group(s); server_redraw_session_group(s);
} else } else
server_status_session_group(s); server_status_session_group(s);
@@ -146,16 +149,15 @@ cmd_new_window_exec(struct cmd *self, struct cmdq_item *item)
free(cp); free(cp);
} }
if (to_free != NULL) cmd_find_from_winlink(&fs, wl, 0);
free((void *)to_free);
cmd_find_from_winlink(&fs, wl);
hooks_insert(s->hooks, item, &fs, "after-new-window"); hooks_insert(s->hooks, item, &fs, "after-new-window");
free(name);
free(cwd);
return (CMD_RETURN_NORMAL); return (CMD_RETURN_NORMAL);
error: error:
if (to_free != NULL) free(name);
free((void *)to_free); free(cwd);
return (CMD_RETURN_ERROR); return (CMD_RETURN_ERROR);
} }

View File

@@ -21,6 +21,7 @@
#include <errno.h> #include <errno.h>
#include <fcntl.h> #include <fcntl.h>
#include <signal.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#include <time.h> #include <time.h>
@@ -34,14 +35,16 @@
static enum cmd_retval cmd_pipe_pane_exec(struct cmd *, struct cmdq_item *); static enum cmd_retval cmd_pipe_pane_exec(struct cmd *, struct cmdq_item *);
static void cmd_pipe_pane_read_callback(struct bufferevent *, void *);
static void cmd_pipe_pane_write_callback(struct bufferevent *, void *);
static void cmd_pipe_pane_error_callback(struct bufferevent *, short, void *); static void cmd_pipe_pane_error_callback(struct bufferevent *, short, void *);
const struct cmd_entry cmd_pipe_pane_entry = { const struct cmd_entry cmd_pipe_pane_entry = {
.name = "pipe-pane", .name = "pipe-pane",
.alias = "pipep", .alias = "pipep",
.args = { "ot:", 0, 1 }, .args = { "IOot:", 0, 1 },
.usage = "[-o] " CMD_TARGET_PANE_USAGE " [command]", .usage = "[-IOo] " CMD_TARGET_PANE_USAGE " [command]",
.target = { 't', CMD_FIND_PANE, 0 }, .target = { 't', CMD_FIND_PANE, 0 },
@@ -58,8 +61,9 @@ cmd_pipe_pane_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;
char *cmd; char *cmd;
int old_fd, pipe_fd[2], null_fd; int old_fd, pipe_fd[2], null_fd, in, out;
struct format_tree *ft; struct format_tree *ft;
sigset_t set, oldset;
/* Destroy the old pipe. */ /* Destroy the old pipe. */
old_fd = wp->pipe_fd; old_fd = wp->pipe_fd;
@@ -67,6 +71,11 @@ cmd_pipe_pane_exec(struct cmd *self, struct cmdq_item *item)
bufferevent_free(wp->pipe_event); bufferevent_free(wp->pipe_event);
close(wp->pipe_fd); close(wp->pipe_fd);
wp->pipe_fd = -1; wp->pipe_fd = -1;
if (window_pane_destroy_ready(wp)) {
server_destroy_pane(wp, 1);
return (CMD_RETURN_NORMAL);
}
} }
/* If no pipe command, that is enough. */ /* If no pipe command, that is enough. */
@@ -82,6 +91,15 @@ cmd_pipe_pane_exec(struct cmd *self, struct cmdq_item *item)
if (args_has(self->args, 'o') && old_fd != -1) if (args_has(self->args, 'o') && old_fd != -1)
return (CMD_RETURN_NORMAL); return (CMD_RETURN_NORMAL);
/* What do we want to do? Neither -I or -O is -O. */
if (args_has(self->args, 'I')) {
in = 1;
out = args_has(self->args, 'O');
} else {
in = 0;
out = 1;
}
/* Open the new pipe. */ /* Open the new pipe. */
if (socketpair(AF_UNIX, SOCK_STREAM, PF_UNSPEC, pipe_fd) != 0) { if (socketpair(AF_UNIX, SOCK_STREAM, PF_UNSPEC, pipe_fd) != 0) {
cmdq_error(item, "socketpair error: %s", strerror(errno)); cmdq_error(item, "socketpair error: %s", strerror(errno));
@@ -95,59 +113,108 @@ cmd_pipe_pane_exec(struct cmd *self, struct cmdq_item *item)
format_free(ft); format_free(ft);
/* Fork the child. */ /* Fork the child. */
sigfillset(&set);
sigprocmask(SIG_BLOCK, &set, &oldset);
switch (fork()) { switch (fork()) {
case -1: case -1:
sigprocmask(SIG_SETMASK, &oldset, NULL);
cmdq_error(item, "fork error: %s", strerror(errno)); cmdq_error(item, "fork error: %s", strerror(errno));
free(cmd); free(cmd);
return (CMD_RETURN_ERROR); return (CMD_RETURN_ERROR);
case 0: case 0:
/* Child process. */ /* Child process. */
proc_clear_signals(server_proc, 1);
sigprocmask(SIG_SETMASK, &oldset, NULL);
close(pipe_fd[0]); close(pipe_fd[0]);
clear_signals(1);
if (dup2(pipe_fd[1], STDIN_FILENO) == -1)
_exit(1);
if (pipe_fd[1] != STDIN_FILENO)
close(pipe_fd[1]);
null_fd = open(_PATH_DEVNULL, O_WRONLY, 0); null_fd = open(_PATH_DEVNULL, O_WRONLY, 0);
if (dup2(null_fd, STDOUT_FILENO) == -1) if (out) {
_exit(1); if (dup2(pipe_fd[1], STDIN_FILENO) == -1)
_exit(1);
} else {
if (dup2(null_fd, STDIN_FILENO) == -1)
_exit(1);
}
if (in) {
if (dup2(pipe_fd[1], STDOUT_FILENO) == -1)
_exit(1);
if (pipe_fd[1] != STDOUT_FILENO)
close(pipe_fd[1]);
} else {
if (dup2(null_fd, STDOUT_FILENO) == -1)
_exit(1);
}
if (dup2(null_fd, STDERR_FILENO) == -1) if (dup2(null_fd, STDERR_FILENO) == -1)
_exit(1); _exit(1);
if (null_fd != STDOUT_FILENO && null_fd != STDERR_FILENO)
close(null_fd);
closefrom(STDERR_FILENO + 1); closefrom(STDERR_FILENO + 1);
execl(_PATH_BSHELL, "sh", "-c", cmd, (char *) NULL); execl(_PATH_BSHELL, "sh", "-c", cmd, (char *) NULL);
_exit(1); _exit(1);
default: default:
/* Parent process. */ /* Parent process. */
sigprocmask(SIG_SETMASK, &oldset, NULL);
close(pipe_fd[1]); close(pipe_fd[1]);
wp->pipe_fd = pipe_fd[0]; wp->pipe_fd = pipe_fd[0];
wp->pipe_off = EVBUFFER_LENGTH(wp->event->input); wp->pipe_off = EVBUFFER_LENGTH(wp->event->input);
wp->pipe_event = bufferevent_new(wp->pipe_fd,
NULL, NULL, cmd_pipe_pane_error_callback, wp);
bufferevent_enable(wp->pipe_event, EV_WRITE);
setblocking(wp->pipe_fd, 0); setblocking(wp->pipe_fd, 0);
wp->pipe_event = bufferevent_new(wp->pipe_fd,
cmd_pipe_pane_read_callback,
cmd_pipe_pane_write_callback,
cmd_pipe_pane_error_callback,
wp);
if (out)
bufferevent_enable(wp->pipe_event, EV_WRITE);
if (in)
bufferevent_enable(wp->pipe_event, EV_READ);
free(cmd); free(cmd);
return (CMD_RETURN_NORMAL); return (CMD_RETURN_NORMAL);
} }
} }
static void
cmd_pipe_pane_read_callback(__unused struct bufferevent *bufev, void *data)
{
struct window_pane *wp = data;
struct evbuffer *evb = wp->pipe_event->input;
size_t available;
available = EVBUFFER_LENGTH(evb);
log_debug("%%%u pipe read %zu", wp->id, available);
bufferevent_write(wp->event, EVBUFFER_DATA(evb), available);
evbuffer_drain(evb, available);
if (window_pane_destroy_ready(wp))
server_destroy_pane(wp, 1);
}
static void
cmd_pipe_pane_write_callback(__unused struct bufferevent *bufev, void *data)
{
struct window_pane *wp = data;
log_debug("%%%u pipe empty", wp->id);
if (window_pane_destroy_ready(wp))
server_destroy_pane(wp, 1);
}
static void static void
cmd_pipe_pane_error_callback(__unused struct bufferevent *bufev, cmd_pipe_pane_error_callback(__unused struct bufferevent *bufev,
__unused short what, void *data) __unused short what, void *data)
{ {
struct window_pane *wp = data; struct window_pane *wp = data;
log_debug("%%%u pipe error", wp->id);
bufferevent_free(wp->pipe_event); bufferevent_free(wp->pipe_event);
close(wp->pipe_fd); close(wp->pipe_fd);
wp->pipe_fd = -1; wp->pipe_fd = -1;
if (window_pane_destroy_ready(wp))
server_destroy_pane(wp, 1);
} }

View File

@@ -241,7 +241,7 @@ cmdq_fire_command(struct cmdq_item *item)
fsp = &item->target; fsp = &item->target;
else if (cmd_find_valid_state(&item->shared->current)) else if (cmd_find_valid_state(&item->shared->current))
fsp = &item->shared->current; fsp = &item->shared->current;
else if (cmd_find_from_client(&fs, item->client) == 0) else if (cmd_find_from_client(&fs, item->client, 0) == 0)
fsp = &fs; fsp = &fs;
else else
goto out; goto out;
@@ -428,7 +428,8 @@ cmdq_print(struct cmdq_item *item, const char *fmt, ...)
w = c->session->curw->window; w = c->session->curw->window;
if (w->active->mode != &window_copy_mode) { if (w->active->mode != &window_copy_mode) {
window_pane_reset_mode(w->active); window_pane_reset_mode(w->active);
window_pane_set_mode(w->active, &window_copy_mode); window_pane_set_mode(w->active, &window_copy_mode, NULL,
NULL);
window_copy_init_for_output(w->active); window_copy_init_for_output(w->active);
} }
window_copy_vadd(w->active, fmt, ap); window_copy_vadd(w->active, fmt, ap);
@@ -452,6 +453,8 @@ cmdq_error(struct cmdq_item *item, const char *fmt, ...)
msglen = xvasprintf(&msg, fmt, ap); msglen = xvasprintf(&msg, fmt, ap);
va_end(ap); va_end(ap);
log_debug("%s: %s", __func__, msg);
if (c == NULL) if (c == NULL)
cfg_add_cause("%s:%u: %s", cmd->file, cmd->line, msg); cfg_add_cause("%s:%u: %s", cmd->file, cmd->line, msg);
else if (c->session == NULL || (c->flags & CLIENT_CONTROL)) { else if (c->session == NULL || (c->flags & CLIENT_CONTROL)) {

View File

@@ -67,8 +67,9 @@ cmd_refresh_client_exec(struct cmd *self, struct cmdq_item *item)
cmdq_error(item, "not a control client"); cmdq_error(item, "not a control client");
return (CMD_RETURN_ERROR); return (CMD_RETURN_ERROR);
} }
if (tty_set_size(&c->tty, w, h)) tty_set_size(&c->tty, w, h);
recalculate_sizes(); c->flags |= CLIENT_SIZECHANGED;
recalculate_sizes();
} else if (args_has(args, 'S')) { } else if (args_has(args, 'S')) {
c->flags |= CLIENT_STATUSFORCE; c->flags |= CLIENT_STATUSFORCE;
server_status_client(c); server_status_client(c);

View File

@@ -46,26 +46,31 @@ const struct cmd_entry cmd_rename_session_entry = {
static enum cmd_retval static enum cmd_retval
cmd_rename_session_exec(struct cmd *self, struct cmdq_item *item) cmd_rename_session_exec(struct cmd *self, struct cmdq_item *item)
{ {
struct args *args = self->args; struct args *args = self->args;
struct session *s = item->target.s; struct client *c = cmd_find_client(item, NULL, 0);
const char *newname; struct session *s = item->target.s;
char *newname;
newname = args->argv[0]; newname = format_single(item, args->argv[0], c, s, NULL, NULL);
if (strcmp(newname, s->name) == 0) if (strcmp(newname, s->name) == 0) {
free(newname);
return (CMD_RETURN_NORMAL); return (CMD_RETURN_NORMAL);
}
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);
free(newname);
return (CMD_RETURN_ERROR); return (CMD_RETURN_ERROR);
} }
if (session_find(newname) != NULL) { if (session_find(newname) != NULL) {
cmdq_error(item, "duplicate session: %s", newname); cmdq_error(item, "duplicate session: %s", newname);
free(newname);
return (CMD_RETURN_ERROR); return (CMD_RETURN_ERROR);
} }
RB_REMOVE(sessions, &sessions, s); RB_REMOVE(sessions, &sessions, s);
free(s->name); free(s->name);
s->name = xstrdup(newname); s->name = newname;
RB_INSERT(sessions, &sessions, s); RB_INSERT(sessions, &sessions, s);
server_status_session(s); server_status_session(s);

View File

@@ -46,12 +46,17 @@ static enum cmd_retval
cmd_rename_window_exec(struct cmd *self, struct cmdq_item *item) cmd_rename_window_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 session *s = item->target.s;
struct winlink *wl = item->target.wl; struct winlink *wl = item->target.wl;
char *newname;
window_set_name(wl->window, args->argv[0]); newname = format_single(item, args->argv[0], c, s, wl, NULL);
window_set_name(wl->window, newname);
options_set_number(wl->window->options, "automatic-rename", 0); options_set_number(wl->window->options, "automatic-rename", 0);
server_status_window(wl->window); server_status_window(wl->window);
free(newname);
return (CMD_RETURN_NORMAL); return (CMD_RETURN_NORMAL);
} }

View File

@@ -129,9 +129,8 @@ static void
cmd_resize_pane_mouse_update(struct client *c, struct mouse_event *m) cmd_resize_pane_mouse_update(struct client *c, struct mouse_event *m)
{ {
struct winlink *wl; struct winlink *wl;
struct window_pane *wp; struct window_pane *loop, *wp_x, *wp_y;
int found; u_int y, ly, x, lx, sx, sy, ex, ey;
u_int y, ly;
wl = cmd_mouse_window(m, NULL); wl = cmd_mouse_window(m, NULL);
if (wl == NULL) { if (wl == NULL) {
@@ -139,37 +138,48 @@ cmd_resize_pane_mouse_update(struct client *c, struct mouse_event *m)
return; return;
} }
y = m->y; y = m->y; x = m->x;
if (m->statusat == 0 && y > 0) if (m->statusat == 0 && y > 0)
y--; y--;
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; ly = m->ly; lx = m->lx;
if (m->statusat == 0 && ly > 0) if (m->statusat == 0 && ly > 0)
ly--; ly--;
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;
found = 0; wp_x = wp_y = NULL;
TAILQ_FOREACH(wp, &wl->window->panes, entry) { TAILQ_FOREACH(loop, &wl->window->panes, entry) {
if (!window_pane_visible(wp)) if (!window_pane_visible(loop))
continue; continue;
if (wp->xoff + wp->sx == m->lx && sx = loop->xoff;
wp->yoff <= 1 + ly && if (sx != 0)
wp->yoff + wp->sy >= ly) { sx--;
layout_resize_pane(wp, LAYOUT_LEFTRIGHT, m->x - m->lx, 0); ex = loop->xoff + loop->sx;
found = 1;
} sy = loop->yoff;
if (wp->yoff + wp->sy == ly && if (sy != 0)
wp->xoff <= 1 + m->lx && sy--;
wp->xoff + wp->sx >= m->lx) { ey = loop->yoff + loop->sy;
layout_resize_pane(wp, LAYOUT_TOPBOTTOM, y - ly, 0);
found = 1; if ((lx == sx || lx == ex) &&
} (ly >= sy && ly <= ey) &&
(wp_x == NULL || loop->sy > wp_x->sy))
wp_x = loop;
if ((ly == sy || ly == ey) &&
(lx >= sx && lx <= ex) &&
(wp_y == NULL || loop->sx > wp_y->sx))
wp_y = loop;
} }
if (found) if (wp_x == NULL && wp_y == NULL) {
server_redraw_window(wl->window);
else
c->tty.mouse_drag_update = NULL; c->tty.mouse_drag_update = NULL;
return;
}
if (wp_x != NULL)
layout_resize_pane(wp_x, LAYOUT_LEFTRIGHT, x - lx, 0);
if (wp_y != NULL)
layout_resize_pane(wp_y, LAYOUT_TOPBOTTOM, y - ly, 0);
server_redraw_window(wl->window);
} }

View File

@@ -34,8 +34,9 @@ const struct cmd_entry cmd_respawn_pane_entry = {
.name = "respawn-pane", .name = "respawn-pane",
.alias = "respawnp", .alias = "respawnp",
.args = { "kt:", 0, -1 }, .args = { "c:kt:", 0, -1 },
.usage = "[-k] " CMD_TARGET_PANE_USAGE " [command]", .usage = "[-c start-directory] [-k] " CMD_TARGET_PANE_USAGE
" [command]",
.target = { 't', CMD_FIND_PANE, 0 }, .target = { 't', CMD_FIND_PANE, 0 },
@@ -50,10 +51,11 @@ cmd_respawn_pane_exec(struct cmd *self, struct cmdq_item *item)
struct winlink *wl = item->target.wl; struct winlink *wl = item->target.wl;
struct window *w = wl->window; struct window *w = wl->window;
struct window_pane *wp = item->target.wp; struct window_pane *wp = item->target.wp;
struct client *c = cmd_find_client(item, NULL, 1);
struct session *s = item->target.s; struct session *s = item->target.s;
struct environ *env; struct environ *env;
const char *path; const char *path = NULL, *cp;
char *cause; char *cause, *cwd = NULL;
u_int idx; u_int idx;
struct environ_entry *envent; struct environ_entry *envent;
@@ -69,7 +71,6 @@ cmd_respawn_pane_exec(struct cmd *self, struct cmdq_item *item)
screen_reinit(&wp->base); screen_reinit(&wp->base);
input_init(wp); input_init(wp);
path = NULL;
if (item->client != NULL && item->client->session == NULL) if (item->client != NULL && item->client->session == NULL)
envent = environ_find(item->client->environ, "PATH"); envent = environ_find(item->client->environ, "PATH");
else else
@@ -77,15 +78,20 @@ cmd_respawn_pane_exec(struct cmd *self, struct cmdq_item *item)
if (envent != NULL) if (envent != NULL)
path = envent->value; path = envent->value;
if ((cp = args_get(args, 'c')) != NULL)
cwd = format_single(item, cp, c, s, NULL, NULL);
env = environ_for_session(s, 0); env = environ_for_session(s, 0);
if (window_pane_spawn(wp, args->argc, args->argv, path, NULL, NULL, env, if (window_pane_spawn(wp, args->argc, args->argv, path, NULL, cwd, env,
s->tio, &cause) != 0) { s->tio, &cause) != 0) {
cmdq_error(item, "respawn pane failed: %s", cause); cmdq_error(item, "respawn pane failed: %s", cause);
free(cause); free(cause);
environ_free(env); environ_free(env);
free(cwd);
return (CMD_RETURN_ERROR); return (CMD_RETURN_ERROR);
} }
environ_free(env); environ_free(env);
free(cwd);
wp->flags |= PANE_REDRAW; wp->flags |= PANE_REDRAW;
server_status_window(w); server_status_window(w);

View File

@@ -34,8 +34,9 @@ const struct cmd_entry cmd_respawn_window_entry = {
.name = "respawn-window", .name = "respawn-window",
.alias = "respawnw", .alias = "respawnw",
.args = { "kt:", 0, -1 }, .args = { "c:kt:", 0, -1 },
.usage = "[-k] " CMD_TARGET_WINDOW_USAGE " [command]", .usage = "[-c start-directory] [-k] " CMD_TARGET_WINDOW_USAGE
" [command]",
.target = { 't', CMD_FIND_WINDOW, 0 }, .target = { 't', CMD_FIND_WINDOW, 0 },
@@ -51,9 +52,10 @@ cmd_respawn_window_exec(struct cmd *self, struct cmdq_item *item)
struct winlink *wl = item->target.wl; struct winlink *wl = item->target.wl;
struct window *w = wl->window; struct window *w = wl->window;
struct window_pane *wp; struct window_pane *wp;
struct client *c = cmd_find_client(item, NULL, 1);
struct environ *env; struct environ *env;
const char *path; const char *path = NULL, *cp;
char *cause; char *cause, *cwd = NULL;
struct environ_entry *envent; struct environ_entry *envent;
if (!args_has(self->args, 'k')) { if (!args_has(self->args, 'k')) {
@@ -73,7 +75,6 @@ cmd_respawn_window_exec(struct cmd *self, struct cmdq_item *item)
TAILQ_INSERT_HEAD(&w->panes, wp, entry); TAILQ_INSERT_HEAD(&w->panes, wp, entry);
window_pane_resize(wp, w->sx, w->sy); window_pane_resize(wp, w->sx, w->sy);
path = NULL;
if (item->client != NULL && item->client->session == NULL) if (item->client != NULL && item->client->session == NULL)
envent = environ_find(item->client->environ, "PATH"); envent = environ_find(item->client->environ, "PATH");
else else
@@ -81,16 +82,22 @@ cmd_respawn_window_exec(struct cmd *self, struct cmdq_item *item)
if (envent != NULL) if (envent != NULL)
path = envent->value; path = envent->value;
if ((cp = args_get(args, 'c')) != NULL)
cwd = format_single(item, cp, c, s, NULL, NULL);
env = environ_for_session(s, 0); env = environ_for_session(s, 0);
if (window_pane_spawn(wp, args->argc, args->argv, path, NULL, NULL, env, if (window_pane_spawn(wp, args->argc, args->argv, path, NULL, cwd, env,
s->tio, &cause) != 0) { s->tio, &cause) != 0) {
cmdq_error(item, "respawn window failed: %s", cause); cmdq_error(item, "respawn window failed: %s", cause);
free(cause); free(cause);
environ_free(env); environ_free(env);
free(cwd);
server_destroy_pane(wp, 0); server_destroy_pane(wp, 0);
return (CMD_RETURN_ERROR); return (CMD_RETURN_ERROR);
} }
environ_free(env); environ_free(env);
free(cwd);
layout_init(w, wp); layout_init(w, wp);
window_pane_reset_mode(wp); window_pane_reset_mode(wp);
screen_reinit(&wp->base); screen_reinit(&wp->base);

View File

@@ -78,7 +78,7 @@ 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); window_set_active_pane(w, wp);
cmd_find_from_winlink_pane(current, wl, wp); cmd_find_from_winlink_pane(current, wl, wp, 0);
server_redraw_window(w); server_redraw_window(w);
} else { } else {
wp = TAILQ_FIRST(&w->panes); wp = TAILQ_FIRST(&w->panes);
@@ -106,7 +106,7 @@ 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); window_set_active_pane(w, wp);
cmd_find_from_winlink_pane(current, wl, wp); cmd_find_from_winlink_pane(current, wl, wp, 0);
server_redraw_window(w); server_redraw_window(w);
} }

View File

@@ -68,14 +68,14 @@ cmd_run_shell_print(struct job *job, const char *msg)
cmdq_print(cdata->item, "%s", msg); cmdq_print(cdata->item, "%s", msg);
return; return;
} }
if (cmd_find_from_nothing(&fs) != 0) if (cmd_find_from_nothing(&fs, 0) != 0)
return; return;
wp = fs.wp; wp = fs.wp;
if (wp == NULL) if (wp == NULL)
return; return;
} }
if (window_pane_set_mode(wp, &window_copy_mode) == 0) if (window_pane_set_mode(wp, &window_copy_mode, NULL, NULL) == 0)
window_copy_init_for_output(wp); window_copy_init_for_output(wp);
if (wp->mode == &window_copy_mode) if (wp->mode == &window_copy_mode)
window_copy_add(wp, "%s", msg); window_copy_add(wp, "%s", msg);
@@ -111,7 +111,7 @@ cmd_run_shell_exec(struct cmd *self, struct cmdq_item *item)
cdata->item = item; cdata->item = item;
job_run(cdata->cmd, s, cwd, NULL, cmd_run_shell_callback, job_run(cdata->cmd, s, cwd, NULL, cmd_run_shell_callback,
cmd_run_shell_free, cdata); cmd_run_shell_free, cdata, 0);
if (args_has(args, 'b')) if (args_has(args, 'b'))
return (CMD_RETURN_NORMAL); return (CMD_RETURN_NORMAL);

View File

@@ -33,10 +33,10 @@ const struct cmd_entry cmd_select_layout_entry = {
.name = "select-layout", .name = "select-layout",
.alias = "selectl", .alias = "selectl",
.args = { "nopt:", 0, 1 }, .args = { "Enopt:", 0, 1 },
.usage = "[-nop] " CMD_TARGET_WINDOW_USAGE " [layout-name]", .usage = "[-Enop] " CMD_TARGET_PANE_USAGE " [layout-name]",
.target = { 't', CMD_FIND_WINDOW, 0 }, .target = { 't', CMD_FIND_PANE, 0 },
.flags = CMD_AFTERHOOK, .flags = CMD_AFTERHOOK,
.exec = cmd_select_layout_exec .exec = cmd_select_layout_exec
@@ -71,14 +71,14 @@ const struct cmd_entry cmd_previous_layout_entry = {
static enum cmd_retval static enum cmd_retval
cmd_select_layout_exec(struct cmd *self, struct cmdq_item *item) cmd_select_layout_exec(struct cmd *self, struct cmdq_item *item)
{ {
struct args *args = self->args; struct args *args = self->args;
struct winlink *wl = item->target.wl; struct winlink *wl = item->target.wl;
struct window *w; struct window *w = wl->window;
const char *layoutname; struct window_pane *wp = item->target.wp;
char *oldlayout; const char *layoutname;
int next, previous, layout; char *oldlayout;
int next, previous, layout;
w = wl->window;
server_unzoom_window(w); server_unzoom_window(w);
next = self->entry == &cmd_next_layout_entry; next = self->entry == &cmd_next_layout_entry;
@@ -99,6 +99,11 @@ cmd_select_layout_exec(struct cmd *self, struct cmdq_item *item)
goto changed; goto changed;
} }
if (args_has(args, 'E')) {
layout_spread_out(wp);
goto changed;
}
if (!args_has(args, 'o')) { if (!args_has(args, 'o')) {
if (args->argc == 0) if (args->argc == 0)
layout = w->lastlayout; layout = w->lastlayout;

View File

@@ -18,6 +18,8 @@
#include <sys/types.h> #include <sys/types.h>
#include <stdlib.h>
#include "tmux.h" #include "tmux.h"
/* /*
@@ -30,8 +32,8 @@ const struct cmd_entry cmd_select_pane_entry = {
.name = "select-pane", .name = "select-pane",
.alias = "selectp", .alias = "selectp",
.args = { "DdegLlMmP:Rt:U", 0, 0 }, .args = { "DdegLlMmP:RT:t:U", 0, 0 },
.usage = "[-DdegLlMmRU] [-P style] " CMD_TARGET_PANE_USAGE, .usage = "[-DdegLlMmRU] [-P style] [-T title] " CMD_TARGET_PANE_USAGE,
.target = { 't', CMD_FIND_PANE, 0 }, .target = { 't', CMD_FIND_PANE, 0 },
@@ -57,10 +59,12 @@ cmd_select_pane_exec(struct cmd *self, struct cmdq_item *item)
{ {
struct args *args = self->args; struct args *args = self->args;
struct cmd_find_state *current = &item->shared->current; struct cmd_find_state *current = &item->shared->current;
struct client *c = cmd_find_client(item, NULL, 1);
struct winlink *wl = item->target.wl; struct winlink *wl = item->target.wl;
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;
char *pane_title;
const char *style; const char *style;
if (self->entry == &cmd_last_pane_entry || args_has(args, 'l')) { if (self->entry == &cmd_last_pane_entry || args_has(args, 'l')) {
@@ -77,7 +81,7 @@ cmd_select_pane_exec(struct cmd *self, struct cmdq_item *item)
server_unzoom_window(w); server_unzoom_window(w);
window_redraw_active_switch(w, lastwp); window_redraw_active_switch(w, lastwp);
if (window_set_active_pane(w, lastwp)) { if (window_set_active_pane(w, lastwp)) {
cmd_find_from_winlink(current, wl); cmd_find_from_winlink(current, wl, 0);
server_status_window(w); server_status_window(w);
server_redraw_window_borders(w); server_redraw_window_borders(w);
} }
@@ -147,6 +151,14 @@ cmd_select_pane_exec(struct cmd *self, struct cmdq_item *item)
return (CMD_RETURN_NORMAL); return (CMD_RETURN_NORMAL);
} }
if (args_has(self->args, 'T')) {
pane_title = format_single(item, args_get(self->args, 'T'),
c, s, wl, wp);
screen_set_title(&wp->base, pane_title);
server_status_window(wp->window);
free(pane_title);
}
if (wp == w->active) if (wp == w->active)
return (CMD_RETURN_NORMAL); return (CMD_RETURN_NORMAL);
server_unzoom_window(wp->window); server_unzoom_window(wp->window);
@@ -156,7 +168,8 @@ cmd_select_pane_exec(struct cmd *self, struct cmdq_item *item)
} }
window_redraw_active_switch(w, wp); window_redraw_active_switch(w, wp);
if (window_set_active_pane(w, wp)) { if (window_set_active_pane(w, wp)) {
cmd_find_from_winlink(current, wl); cmd_find_from_winlink_pane(current, wl, wp, 0);
hooks_insert(s->hooks, item, current, "after-select-pane");
server_status_window(w); server_status_window(w);
server_redraw_window_borders(w); server_redraw_window_borders(w);
} }

View File

@@ -117,8 +117,9 @@ cmd_select_window_exec(struct cmd *self, struct cmdq_item *item)
return (CMD_RETURN_ERROR); return (CMD_RETURN_ERROR);
} }
} }
cmd_find_from_session(&item->shared->current, s); cmd_find_from_session(current, s, 0);
server_redraw_session(s); server_redraw_session(s);
hooks_insert(s->hooks, item, current, "after-select-window");
} else { } else {
/* /*
* If -T and select-window is invoked on same window as * If -T and select-window is invoked on same window as
@@ -130,12 +131,13 @@ cmd_select_window_exec(struct cmd *self, struct cmdq_item *item)
return (-1); return (-1);
} }
if (current->s == s) if (current->s == s)
cmd_find_from_session(current, s); cmd_find_from_session(current, s, 0);
server_redraw_session(s); server_redraw_session(s);
} else if (session_select(s, wl->idx) == 0) { } else if (session_select(s, wl->idx) == 0) {
cmd_find_from_session(current, s); cmd_find_from_session(current, s, 0);
server_redraw_session(s); server_redraw_session(s);
} }
hooks_insert(s->hooks, item, current, "after-select-window");
} }
recalculate_sizes(); recalculate_sizes();

View File

@@ -64,6 +64,8 @@ cmd_send_keys_inject(struct client *c, struct cmdq_item *item, key_code key)
struct key_binding *bd, bd_find; struct key_binding *bd, bd_find;
if (wp->mode == NULL || wp->mode->key_table == NULL) { if (wp->mode == NULL || wp->mode->key_table == NULL) {
if (options_get_number(wp->window->options, "xterm-keys"))
key |= KEYC_XTERM;
window_pane_key(wp, NULL, s, key, NULL); window_pane_key(wp, NULL, s, key, NULL);
return; return;
} }
@@ -73,7 +75,7 @@ cmd_send_keys_inject(struct client *c, struct cmdq_item *item, key_code key)
bd = RB_FIND(key_bindings, &table->key_bindings, &bd_find); bd = RB_FIND(key_bindings, &table->key_bindings, &bd_find);
if (bd != NULL) { if (bd != NULL) {
table->references++; table->references++;
key_bindings_dispatch(bd, c, NULL, &item->target); key_bindings_dispatch(bd, item, c, NULL, &item->target);
key_bindings_unref_table(table); key_bindings_unref_table(table);
} }
} }

View File

@@ -42,8 +42,8 @@ const struct cmd_entry cmd_set_option_entry = {
.name = "set-option", .name = "set-option",
.alias = "set", .alias = "set",
.args = { "agoqst:uw", 1, 2 }, .args = { "aFgoqst:uw", 1, 2 },
.usage = "[-agosquw] [-t target-window] option [value]", .usage = "[-aFgosquw] [-t target-window] option [value]",
.target = { 't', CMD_FIND_WINDOW, CMD_FIND_CANFAIL }, .target = { 't', CMD_FIND_WINDOW, CMD_FIND_CANFAIL },
@@ -55,8 +55,8 @@ const struct cmd_entry cmd_set_window_option_entry = {
.name = "set-window-option", .name = "set-window-option",
.alias = "setw", .alias = "setw",
.args = { "agoqt:u", 1, 2 }, .args = { "aFgoqt:u", 1, 2 },
.usage = "[-agoqu] " CMD_TARGET_WINDOW_USAGE " option [value]", .usage = "[-aFgoqu] " CMD_TARGET_WINDOW_USAGE " option [value]",
.target = { 't', CMD_FIND_WINDOW, CMD_FIND_CANFAIL }, .target = { 't', CMD_FIND_WINDOW, CMD_FIND_CANFAIL },
@@ -70,33 +70,38 @@ cmd_set_option_exec(struct cmd *self, struct cmdq_item *item)
struct args *args = self->args; struct args *args = self->args;
int append = args_has(args, 'a'); int append = args_has(args, 'a');
struct cmd_find_state *fs = &item->target; struct cmd_find_state *fs = &item->target;
struct client *c, *loop;
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;
struct client *c;
enum options_table_scope scope; enum options_table_scope scope;
struct options *oo; struct options *oo;
struct options_entry *parent, *o; struct options_entry *parent, *o;
char *name; char *name, *argument, *value = NULL, *cause;
const char *value, *target; const char *target;
int window, idx, already, error, ambiguous; int window, idx, already, error, ambiguous;
char *cause;
/* Expand argument. */
c = cmd_find_client(item, NULL, 1);
argument = format_single(item, args->argv[0], c, s, wl, NULL);
/* Parse option name and index. */ /* Parse option name and index. */
name = options_match(args->argv[0], &idx, &ambiguous); name = options_match(argument, &idx, &ambiguous);
if (name == NULL) { if (name == NULL) {
if (args_has(args, 'q')) if (args_has(args, 'q'))
return (CMD_RETURN_NORMAL); goto out;
if (ambiguous) if (ambiguous)
cmdq_error(item, "ambiguous option: %s", args->argv[0]); cmdq_error(item, "ambiguous option: %s", argument);
else else
cmdq_error(item, "invalid option: %s", args->argv[0]); cmdq_error(item, "invalid option: %s", argument);
return (CMD_RETURN_ERROR); goto fail;
} }
if (args->argc < 2) if (args->argc < 2)
value = NULL; value = NULL;
else if (args_has(args, 'F'))
value = format_single(item, args->argv[1], c, s, wl, NULL);
else else
value = args->argv[1]; value = xstrdup(args->argv[1]);
/* /*
* Figure out the scope: for user options it comes from the arguments, * Figure out the scope: for user options it comes from the arguments,
@@ -114,12 +119,12 @@ cmd_set_option_exec(struct cmd *self, struct cmdq_item *item)
scope = OPTIONS_TABLE_WINDOW; scope = OPTIONS_TABLE_WINDOW;
else { else {
scope = OPTIONS_TABLE_NONE; scope = OPTIONS_TABLE_NONE;
xasprintf(&cause, "unknown option: %s", args->argv[0]); 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'))
return (CMD_RETURN_NORMAL); goto out;
cmdq_error(item, "%s", cause); cmdq_error(item, "%s", cause);
free(cause); free(cause);
goto fail; goto fail;
@@ -159,7 +164,7 @@ cmd_set_option_exec(struct cmd *self, struct cmdq_item *item)
/* Check that array options and indexes match up. */ /* Check that array options and indexes match up. */
if (idx != -1) { if (idx != -1) {
if (*name == '@' || options_array_size(parent, NULL) == -1) { if (*name == '@' || options_array_size(parent, NULL) == -1) {
cmdq_error(item, "not an array: %s", args->argv[0]); cmdq_error(item, "not an array: %s", argument);
goto fail; goto fail;
} }
} }
@@ -176,8 +181,8 @@ cmd_set_option_exec(struct cmd *self, struct cmdq_item *item)
} }
if (already) { if (already) {
if (args_has(args, 'q')) if (args_has(args, 'q'))
return (CMD_RETURN_NORMAL); goto out;
cmdq_error(item, "already set: %s", args->argv[0]); cmdq_error(item, "already set: %s", argument);
goto fail; goto fail;
} }
} }
@@ -185,9 +190,11 @@ cmd_set_option_exec(struct cmd *self, struct cmdq_item *item)
/* Change the option. */ /* Change the option. */
if (args_has(args, 'u')) { if (args_has(args, 'u')) {
if (o == NULL) if (o == NULL)
goto fail; goto out;
if (idx == -1) { if (idx == -1) {
if (oo == global_options || if (*name == '@')
options_remove(o);
else if (oo == global_options ||
oo == global_s_options || oo == global_s_options ||
oo == global_w_options) oo == global_w_options)
options_default(oo, options_table_entry(o)); options_default(oo, options_table_entry(o));
@@ -217,7 +224,7 @@ cmd_set_option_exec(struct cmd *self, struct cmdq_item *item)
options_array_clear(o); options_array_clear(o);
options_array_assign(o, value); options_array_assign(o, value);
} else if (options_array_set(o, idx, value, append) != 0) { } else if (options_array_set(o, idx, value, append) != 0) {
cmdq_error(item, "invalid index: %s", args->argv[0]); cmdq_error(item, "invalid index: %s", argument);
goto fail; goto fail;
} }
} }
@@ -232,8 +239,14 @@ cmd_set_option_exec(struct cmd *self, struct cmdq_item *item)
} }
} }
if (strcmp(name, "key-table") == 0) { if (strcmp(name, "key-table") == 0) {
TAILQ_FOREACH(c, &clients, entry) TAILQ_FOREACH(loop, &clients, entry)
server_client_set_key_table(c, NULL); server_client_set_key_table(loop, NULL);
}
if (strcmp(name, "user-keys") == 0) {
TAILQ_FOREACH(loop, &clients, entry) {
if (loop->tty.flags & TTY_OPENED)
tty_keys_build(&loop->tty);
}
} }
if (strcmp(name, "status") == 0 || if (strcmp(name, "status") == 0 ||
strcmp(name, "status-interval") == 0) strcmp(name, "status-interval") == 0)
@@ -249,7 +262,7 @@ cmd_set_option_exec(struct cmd *self, struct cmdq_item *item)
RB_FOREACH(w, windows, &windows) RB_FOREACH(w, windows, &windows)
layout_fix_panes(w, w->sx, w->sy); layout_fix_panes(w, w->sx, w->sy);
} }
RB_FOREACH (s, sessions, &sessions) RB_FOREACH(s, sessions, &sessions)
status_update_saved(s); status_update_saved(s);
/* /*
@@ -257,15 +270,20 @@ cmd_set_option_exec(struct cmd *self, struct cmdq_item *item)
* anyway. * anyway.
*/ */
recalculate_sizes(); recalculate_sizes();
TAILQ_FOREACH(c, &clients, entry) { TAILQ_FOREACH(loop, &clients, entry) {
if (c->session != NULL) if (loop->session != NULL)
server_redraw_client(c); server_redraw_client(loop);
} }
out:
free(argument);
free(value);
free(name); free(name);
return (CMD_RETURN_NORMAL); return (CMD_RETURN_NORMAL);
fail: fail:
free(argument);
free(value);
free(name); free(name);
return (CMD_RETURN_ERROR); return (CMD_RETURN_ERROR);
} }

View File

@@ -127,25 +127,36 @@ cmd_show_options_one(struct cmd *self, struct cmdq_item *item,
struct options *oo) struct options *oo)
{ {
struct args *args = self->args; struct args *args = self->args;
struct client *c = cmd_find_client(item, NULL, 1);
struct session *s = item->target.s;
struct winlink *wl = item->target.wl;
struct options_entry *o; struct options_entry *o;
int idx, ambiguous; int idx, ambiguous;
const char *name = args->argv[0]; char *name;
name = format_single(item, args->argv[0], c, s, wl, NULL);
o = options_match_get(oo, name, &idx, 1, &ambiguous); o = options_match_get(oo, name, &idx, 1, &ambiguous);
if (o == NULL) { if (o == NULL) {
if (args_has(args, 'q')) if (args_has(args, 'q')) {
free(name);
return (CMD_RETURN_NORMAL); return (CMD_RETURN_NORMAL);
}
if (ambiguous) { if (ambiguous) {
cmdq_error(item, "ambiguous option: %s", name); cmdq_error(item, "ambiguous option: %s", name);
free(name);
return (CMD_RETURN_ERROR); return (CMD_RETURN_ERROR);
} }
if (*name != '@' && if (*name != '@' &&
options_match_get(oo, name, &idx, 0, &ambiguous) != NULL) options_match_get(oo, name, &idx, 0, &ambiguous) != NULL) {
free(name);
return (CMD_RETURN_NORMAL); return (CMD_RETURN_NORMAL);
}
cmdq_error(item, "unknown option: %s", name); cmdq_error(item, "unknown option: %s", name);
free(name);
return (CMD_RETURN_ERROR); return (CMD_RETURN_ERROR);
} }
cmd_show_options_print(self, item, o, idx); cmd_show_options_print(self, item, o, idx);
free(name);
return (CMD_RETURN_NORMAL); return (CMD_RETURN_NORMAL);
} }

View File

@@ -60,10 +60,10 @@ cmd_split_window_exec(struct cmd *self, struct cmdq_item *item)
struct window *w = wl->window; struct window *w = wl->window;
struct window_pane *wp = item->target.wp, *new_wp = NULL; struct window_pane *wp = item->target.wp, *new_wp = NULL;
struct environ *env; struct environ *env;
const char *cmd, *path, *shell, *template, *cwd, *to_free; const char *cmd, *path, *shell, *template, *tmp;
char **argv, *cause, *new_cause, *cp; char **argv, *cause, *new_cause, *cp, *cwd;
u_int hlimit; u_int hlimit;
int argc, size, percentage; int argc, size, percentage, before;
enum layout_type type; enum layout_type type;
struct layout_cell *lc; struct layout_cell *lc;
struct environ_entry *envent; struct environ_entry *envent;
@@ -85,18 +85,17 @@ cmd_split_window_exec(struct cmd *self, struct cmdq_item *item)
argv = args->argv; argv = args->argv;
} }
to_free = NULL; if ((tmp = args_get(args, 'c')) != NULL)
if (args_has(args, 'c')) { cwd = format_single(item, tmp, c, s, NULL, NULL);
cwd = args_get(args, 'c'); else if (item->client != NULL && item->client->session == NULL)
to_free = cwd = format_single(item, cwd, c, s, NULL, NULL); cwd = xstrdup(item->client->cwd);
} else if (item->client != NULL && item->client->session == NULL)
cwd = item->client->cwd;
else else
cwd = s->cwd; cwd = xstrdup(s->cwd);
type = LAYOUT_TOPBOTTOM; type = LAYOUT_TOPBOTTOM;
if (args_has(args, 'h')) if (args_has(args, 'h'))
type = LAYOUT_LEFTRIGHT; type = LAYOUT_LEFTRIGHT;
before = args_has(args, 'b');
size = -1; size = -1;
if (args_has(args, 'l')) { if (args_has(args, 'l')) {
@@ -126,14 +125,13 @@ cmd_split_window_exec(struct cmd *self, struct cmdq_item *item)
if (*shell == '\0' || areshell(shell)) if (*shell == '\0' || areshell(shell))
shell = _PATH_BSHELL; shell = _PATH_BSHELL;
lc = layout_split_pane(wp, type, size, args_has(args, 'b'), lc = layout_split_pane(wp, type, size, before, args_has(args, 'f'));
args_has(args, 'f'));
if (lc == NULL) { if (lc == NULL) {
cause = xstrdup("pane too small"); cause = xstrdup("pane too small");
goto error; goto error;
} }
new_wp = window_add_pane(w, wp, args_has(args, 'b'), hlimit); new_wp = window_add_pane(w, wp, before, args_has(args, 'f'), hlimit);
layout_assign_pane(lc, new_wp); layout_make_leaf(lc, new_wp);
path = NULL; path = NULL;
if (item->client != NULL && item->client->session == NULL) if (item->client != NULL && item->client->session == NULL)
@@ -151,12 +149,13 @@ cmd_split_window_exec(struct cmd *self, struct cmdq_item *item)
} }
environ_free(env); environ_free(env);
layout_fix_panes(w, w->sx, w->sy);
server_redraw_window(w); server_redraw_window(w);
if (!args_has(args, 'd')) { if (!args_has(args, 'd')) {
window_set_active_pane(w, new_wp); window_set_active_pane(w, new_wp);
session_select(s, wl->idx); session_select(s, wl->idx);
cmd_find_from_session(current, s); cmd_find_from_session(current, s, 0);
server_redraw_session(s); server_redraw_session(s);
} else } else
server_status_session(s); server_status_session(s);
@@ -170,12 +169,10 @@ cmd_split_window_exec(struct cmd *self, struct cmdq_item *item)
} }
notify_window("window-layout-changed", w); notify_window("window-layout-changed", w);
if (to_free != NULL) cmd_find_from_winlink_pane(&fs, wl, new_wp, 0);
free((void *)to_free);
cmd_find_from_winlink_pane(&fs, wl, new_wp);
hooks_insert(s->hooks, item, &fs, "after-split-window"); hooks_insert(s->hooks, item, &fs, "after-split-window");
free(cwd);
return (CMD_RETURN_NORMAL); return (CMD_RETURN_NORMAL);
error: error:
@@ -186,7 +183,6 @@ error:
cmdq_error(item, "create pane failed: %s", cause); cmdq_error(item, "create pane failed: %s", cause);
free(cause); free(cause);
if (to_free != NULL) free(cwd);
free((void *)to_free);
return (CMD_RETURN_ERROR); return (CMD_RETURN_ERROR);
} }

View File

@@ -159,21 +159,19 @@ cmd_string_parse(const char *s, const char *file, u_int line, char **cause)
char **argv; char **argv;
*cause = NULL; *cause = NULL;
if (cmd_string_split(s, &argc, &argv) != 0) if (cmd_string_split(s, &argc, &argv) != 0) {
goto error; xasprintf(cause, "invalid or unknown command: %s", s);
return (NULL);
}
if (argc != 0) { if (argc != 0) {
cmdlist = cmd_list_parse(argc, argv, file, line, cause); cmdlist = cmd_list_parse(argc, argv, file, line, cause);
if (cmdlist == NULL) { if (cmdlist == NULL) {
cmd_free_argv(argc, argv); cmd_free_argv(argc, argv);
goto error; return (NULL);
} }
} }
cmd_free_argv(argc, argv); cmd_free_argv(argc, argv);
return (cmdlist); return (cmdlist);
error:
xasprintf(cause, "invalid or unknown command: %s", s);
return (NULL);
} }
static void static void

View File

@@ -116,7 +116,7 @@ cmd_switch_client_exec(struct cmd *self, struct cmdq_item *item)
if (wp != NULL) if (wp != NULL)
window_set_active_pane(wp->window, wp); window_set_active_pane(wp->window, wp);
session_set_current(s, wl); session_set_current(s, wl);
cmd_find_from_session(&item->shared->current, s); cmd_find_from_session(&item->shared->current, s, 0);
} }
} }

4
cmd.c
View File

@@ -33,9 +33,7 @@ extern const struct cmd_entry cmd_break_pane_entry;
extern const struct cmd_entry cmd_capture_pane_entry; extern const struct cmd_entry cmd_capture_pane_entry;
extern const struct cmd_entry cmd_choose_buffer_entry; extern const struct cmd_entry cmd_choose_buffer_entry;
extern const struct cmd_entry cmd_choose_client_entry; extern const struct cmd_entry cmd_choose_client_entry;
extern const struct cmd_entry cmd_choose_session_entry;
extern const struct cmd_entry cmd_choose_tree_entry; extern const struct cmd_entry cmd_choose_tree_entry;
extern const struct cmd_entry cmd_choose_window_entry;
extern const struct cmd_entry cmd_clear_history_entry; extern const struct cmd_entry cmd_clear_history_entry;
extern const struct cmd_entry cmd_clock_mode_entry; extern const struct cmd_entry cmd_clock_mode_entry;
extern const struct cmd_entry cmd_command_prompt_entry; extern const struct cmd_entry cmd_command_prompt_entry;
@@ -122,9 +120,7 @@ const struct cmd_entry *cmd_table[] = {
&cmd_capture_pane_entry, &cmd_capture_pane_entry,
&cmd_choose_buffer_entry, &cmd_choose_buffer_entry,
&cmd_choose_client_entry, &cmd_choose_client_entry,
&cmd_choose_session_entry,
&cmd_choose_tree_entry, &cmd_choose_tree_entry,
&cmd_choose_window_entry,
&cmd_clear_history_entry, &cmd_clear_history_entry,
&cmd_clock_mode_entry, &cmd_clock_mode_entry,
&cmd_command_prompt_entry, &cmd_command_prompt_entry,

View File

@@ -101,17 +101,17 @@ void warnx(const char *, ...);
#include <paths.h> #include <paths.h>
#endif #endif
#ifdef HAVE_FORKPTY
#ifdef HAVE_LIBUTIL_H #ifdef HAVE_LIBUTIL_H
#include <libutil.h> #include <libutil.h>
#endif #endif
#ifdef HAVE_PTY_H #ifdef HAVE_PTY_H
#include <pty.h> #include <pty.h>
#endif #endif
#ifdef HAVE_UTIL_H #ifdef HAVE_UTIL_H
#include <util.h> #include <util.h>
#endif #endif
#endif
#ifdef HAVE_VIS #ifdef HAVE_VIS
#include <vis.h> #include <vis.h>
@@ -261,6 +261,11 @@ size_t strnlen(const char *, size_t);
char *strndup(const char *, size_t); char *strndup(const char *, size_t);
#endif #endif
#ifndef HAVE_MEMMEM
/* memmem.c */
void *memmem(const void *, size_t, const void *, size_t);
#endif
#ifndef HAVE_DAEMON #ifndef HAVE_DAEMON
/* daemon.c */ /* daemon.c */
int daemon(int, int); int daemon(int, int);

85
compat/daemon-darwin.c Normal file
View File

@@ -0,0 +1,85 @@
/*
* Copyright (c) 2017 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.
*/
/*
* Copyright (c) 2011-2013, Chris Johnsen <chris_johnsen@pobox.com>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above
* copyright notice, this list of conditions and the following
* disclaimer.
*
* 2. Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following
* disclaimer in the documentation and/or other materials
* provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
#include <sys/types.h>
#include <mach/mach.h>
#include <Availability.h>
#include <unistd.h>
void daemon_darwin(void);
#ifdef __MAC_10_10
extern kern_return_t bootstrap_look_up_per_user(mach_port_t, const char *,
uid_t, mach_port_t *);
extern kern_return_t bootstrap_get_root(mach_port_t, mach_port_t *);
void
daemon_darwin(void)
{
mach_port_t root = MACH_PORT_NULL;
mach_port_t s = MACH_PORT_NULL;
uid_t uid;
uid = getuid();
if (bootstrap_get_root(bootstrap_port, &root) == KERN_SUCCESS &&
bootstrap_look_up_per_user(root, NULL, uid, &s) == KERN_SUCCESS &&
task_set_bootstrap_port(mach_task_self(), s) == KERN_SUCCESS &&
mach_port_deallocate(mach_task_self(), bootstrap_port) == KERN_SUCCESS)
bootstrap_port = s;
}
#else
void
daemon_darwin(void)
{
}
#endif

View File

@@ -28,12 +28,18 @@
* SUCH DAMAGE. * SUCH DAMAGE.
*/ */
#include <sys/types.h>
#include <fcntl.h> #include <fcntl.h>
#include <unistd.h> #include <unistd.h>
#include <stdlib.h> #include <stdlib.h>
#include "compat.h" #include "compat.h"
#ifdef __APPLE__
extern void daemon_darwin(void);
#endif
int int
daemon(int nochdir, int noclose) daemon(int nochdir, int noclose)
{ {
@@ -61,5 +67,9 @@ daemon(int nochdir, int noclose)
if (fd > 2) if (fd > 2)
(void)close (fd); (void)close (fd);
} }
#ifdef __APPLE__
daemon_darwin();
#endif
return (0); return (0);
} }

View File

@@ -30,19 +30,12 @@ getdtablecount(void)
{ {
char path[PATH_MAX]; char path[PATH_MAX];
glob_t g; glob_t g;
int n; int n = 0;
if (snprintf(path, sizeof path, "/proc/%ld/fd/*", (long)getpid()) < 0) if (snprintf(path, sizeof path, "/proc/%ld/fd/*", (long)getpid()) < 0)
fatal("snprintf overflow"); fatal("snprintf overflow");
switch (glob(path, 0, NULL, &g)) { if (glob(path, 0, NULL, &g) == 0)
case GLOB_NOMATCH: n = g.gl_pathc;
return (0);
case 0:
break;
default:
fatal("glob(\"%s\") failed", path);
}
n = g.gl_pathc;
globfree(&g); globfree(&g);
return (n); return (n);
} }

65
compat/memmem.c Normal file
View File

@@ -0,0 +1,65 @@
/* $OpenBSD: memmem.c,v 1.4 2015/08/31 02:53:57 guenther Exp $ */
/*-
* Copyright (c) 2005 Pascal Gloor <pascal.gloor@spale.com>
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. The name of the author may not be used to endorse or promote
* products derived from this software without specific prior written
* permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#include <string.h>
#include "compat.h"
/*
* Find the first occurrence of the byte string s in byte string l.
*/
void *
memmem(const void *l, size_t l_len, const void *s, size_t s_len)
{
const char *cur, *last;
const char *cl = l;
const char *cs = s;
/* a zero length needle should just return the haystack */
if (s_len == 0)
return (void *)cl;
/* "s" must be smaller or equal to "l" */
if (l_len < s_len)
return NULL;
/* special case where s_len == 1 */
if (s_len == 1)
return memchr(l, *cs, l_len);
/* the last position where its possible to find "s" in "l" */
last = cl + l_len - s_len;
for (cur = cl; cur <= last; cur++)
if (cur[0] == cs[0] && memcmp(cur, cs, s_len) == 0)
return (void *)cur;
return NULL;
}

View File

@@ -1,6 +1,6 @@
# configure.ac # configure.ac
AC_INIT(tmux, 2.5) AC_INIT(tmux, 2.7-rc)
AC_PREREQ([2.60]) AC_PREREQ([2.60])
AC_CONFIG_AUX_DIR(etc) AC_CONFIG_AUX_DIR(etc)
@@ -104,10 +104,10 @@ AC_REPLACE_FUNCS([ \
closefrom \ closefrom \
explicit_bzero \ explicit_bzero \
fgetln \ fgetln \
fparseln \
freezero \ freezero \
getdtablecount \ getdtablecount \
getprogname \ getprogname \
memmem \
recallocarray \ recallocarray \
reallocarray \ reallocarray \
setenv \ setenv \
@@ -176,7 +176,8 @@ if test "x$found_ncurses" = xno; then
) )
fi fi
if test "x$found_ncurses" = xyes; then if test "x$found_ncurses" = xyes; then
CPPFLAGS="$LIBNCURSES_CFLAGS $LIBTINFO_CFLAGS $CPPFLAGS" AM_CFLAGS="$LIBNCURSES_CFLAGS $LIBTINFO_CFLAGS $AM_CFLAGS"
CFLAGS="$LIBNCURSES_CFLAGS $LIBTINFO_CFLAGS $CFLAGS"
LIBS="$LIBNCURSES_LIBS $LIBTINFO_LIBS $LIBS" LIBS="$LIBNCURSES_LIBS $LIBTINFO_LIBS $LIBS"
else else
# pkg-config didn't work, try ncurses. # pkg-config didn't work, try ncurses.
@@ -284,6 +285,7 @@ if test "x$found_b64_ntop" = xno; then
AC_MSG_RESULT(no) AC_MSG_RESULT(no)
AC_MSG_CHECKING(for b64_ntop with -lresolv) AC_MSG_CHECKING(for b64_ntop with -lresolv)
OLD_LIBS="$LIBS"
LIBS="$LIBS -lresolv" LIBS="$LIBS -lresolv"
AC_TRY_LINK( AC_TRY_LINK(
[ [
@@ -296,6 +298,7 @@ if test "x$found_b64_ntop" = xno; then
found_b64_ntop=no found_b64_ntop=no
) )
if test "x$found_b64_ntop" = xno; then if test "x$found_b64_ntop" = xno; then
LIBS="$OLD_LIBS"
AC_MSG_RESULT(no) AC_MSG_RESULT(no)
fi fi
fi fi
@@ -454,6 +457,14 @@ else
AC_LIBOBJ(getopt) AC_LIBOBJ(getopt)
fi fi
# Look for fparseln in libutil.
AC_SEARCH_LIBS(fparseln, util, found_fparseln=yes, found_fparseln=no)
if test "x$found_fparseln" = xyes; then
AC_DEFINE(HAVE_FPARSELN)
else
AC_LIBOBJ(fparseln)
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
@@ -562,8 +573,15 @@ case "$host_os" in
;; ;;
*darwin*) *darwin*)
AC_MSG_RESULT(darwin) AC_MSG_RESULT(darwin)
AC_DEFINE(BROKEN_CMSG_FIRSTHDR)
PLATFORM=darwin PLATFORM=darwin
#
# OS X CMSG_FIRSTHDR is broken, so redefine it with a working
# one. daemon works but has some stupid side effects, so use
# our internal version which has a workaround.
#
AC_DEFINE(BROKEN_CMSG_FIRSTHDR)
AC_LIBOBJ(daemon)
AC_LIBOBJ(daemon-darwin)
;; ;;
*dragonfly*) *dragonfly*)
AC_MSG_RESULT(dragonfly) AC_MSG_RESULT(dragonfly)
@@ -598,7 +616,7 @@ case "$host_os" in
AC_MSG_RESULT(hpux) AC_MSG_RESULT(hpux)
PLATFORM=hpux PLATFORM=hpux
;; ;;
*cygwin*) *cygwin*|*msys*)
AC_MSG_RESULT(cygwin) AC_MSG_RESULT(cygwin)
PLATFORM=cygwin PLATFORM=cygwin
;; ;;

View File

@@ -208,9 +208,15 @@ environ_push(struct environ *env)
/* Log the environment. */ /* Log the environment. */
void void
environ_log(struct environ *env, const char *prefix) environ_log(struct environ *env, const char *fmt, ...)
{ {
struct environ_entry *envent; struct environ_entry *envent;
va_list ap;
char *prefix;
va_start(ap, fmt);
vasprintf(&prefix, fmt, ap);
va_end(ap);
RB_FOREACH(envent, environ, env) { RB_FOREACH(envent, environ, env) {
if (envent->value != NULL && *envent->name != '\0') { if (envent->value != NULL && *envent->name != '\0') {
@@ -218,6 +224,8 @@ environ_log(struct environ *env, const char *prefix)
envent->value); envent->value);
} }
} }
free(prefix);
} }
/* Create initial environment for new child. */ /* Create initial environment for new child. */

200
format.c
View File

@@ -17,13 +17,11 @@
*/ */
#include <sys/types.h> #include <sys/types.h>
#include <sys/param.h>
#include <sys/wait.h> #include <sys/wait.h>
#include <ctype.h>
#include <errno.h> #include <errno.h>
#include <fnmatch.h>
#include <libgen.h> #include <libgen.h>
#include <netdb.h>
#include <stdarg.h> #include <stdarg.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
@@ -43,28 +41,6 @@ typedef void (*format_cb)(struct format_tree *, struct format_entry *);
static char *format_job_get(struct format_tree *, const char *); static char *format_job_get(struct format_tree *, const char *);
static void format_job_timer(int, short, void *); static void format_job_timer(int, short, void *);
static void format_cb_host(struct format_tree *, struct format_entry *);
static void format_cb_host_short(struct format_tree *,
struct format_entry *);
static void format_cb_pid(struct format_tree *, struct format_entry *);
static void format_cb_session_alerts(struct format_tree *,
struct format_entry *);
static void format_cb_window_layout(struct format_tree *,
struct format_entry *);
static void format_cb_window_visible_layout(struct format_tree *,
struct format_entry *);
static void format_cb_start_command(struct format_tree *,
struct format_entry *);
static void format_cb_current_command(struct format_tree *,
struct format_entry *);
static void format_cb_history_bytes(struct format_tree *,
struct format_entry *);
static void format_cb_pane_tabs(struct format_tree *,
struct format_entry *);
static void format_cb_current_path(struct format_tree *,
struct format_entry *);
static char *format_find(struct format_tree *, const char *, int); static char *format_find(struct format_tree *, const char *, int);
static void format_add_cb(struct format_tree *, const char *, format_cb); static void format_add_cb(struct format_tree *, const char *, format_cb);
static void format_add_tv(struct format_tree *, const char *, static void format_add_tv(struct format_tree *, const char *,
@@ -215,23 +191,27 @@ static void
format_job_update(struct job *job) format_job_update(struct job *job)
{ {
struct format_job *fj = job->data; struct format_job *fj = job->data;
char *line; struct evbuffer *evb = job->event->input;
char *line = NULL, *next;
time_t t; time_t t;
struct client *c;
if ((line = evbuffer_readline(job->event->input)) == NULL) while ((next = evbuffer_readline(evb)) != NULL) {
free(line);
line = next;
}
if (line == NULL)
return; return;
fj->updated = 1; fj->updated = 1;
free(fj->out); free(fj->out);
fj->out = line; fj->out = line;
log_debug("%s: %s: %s", __func__, fj->cmd, fj->out); log_debug("%s: %p %s: %s", __func__, fj, fj->cmd, fj->out);
t = time (NULL); t = time(NULL);
if (fj->status && fj->last != t) { if (fj->status && fj->last != t) {
TAILQ_FOREACH(c, &clients, entry) if (fj->client != NULL)
server_status_client(c); server_status_client(fj->client);
fj->last = t; fj->last = t;
} }
} }
@@ -256,10 +236,11 @@ format_job_complete(struct job *job)
} else } else
buf = line; buf = line;
log_debug("%s: %p %s: %s", __func__, fj, fj->cmd, buf);
if (*buf != '\0' || !fj->updated) { if (*buf != '\0' || !fj->updated) {
free(fj->out); free(fj->out);
fj->out = buf; fj->out = buf;
log_debug("%s: %s: %s", __func__, fj->cmd, fj->out);
} else } else
free(buf); free(buf);
@@ -314,7 +295,7 @@ format_job_get(struct format_tree *ft, const char *cmd)
t = time(NULL); t = time(NULL);
if (fj->job == NULL && (force || fj->last != t)) { if (fj->job == NULL && (force || fj->last != t)) {
fj->job = job_run(expanded, NULL, NULL, format_job_update, fj->job = job_run(expanded, NULL, NULL, format_job_update,
format_job_complete, NULL, fj); format_job_complete, NULL, fj, JOB_NOWAIT);
if (fj->job == NULL) { if (fj->job == NULL) {
free(fj->out); free(fj->out);
xasprintf(&fj->out, "<'%s' didn't start>", fj->cmd); xasprintf(&fj->out, "<'%s' didn't start>", fj->cmd);
@@ -551,7 +532,7 @@ format_cb_current_command(struct format_tree *ft, struct format_entry *fe)
} }
/* Callback for pane_current_path. */ /* Callback for pane_current_path. */
void static void
format_cb_current_path(struct format_tree *ft, struct format_entry *fe) format_cb_current_path(struct format_tree *ft, struct format_entry *fe)
{ {
struct window_pane *wp = ft->wp; struct window_pane *wp = ft->wp;
@@ -611,8 +592,35 @@ format_cb_pane_tabs(struct format_tree *ft, struct format_entry *fe)
evbuffer_add(buffer, ",", 1); evbuffer_add(buffer, ",", 1);
evbuffer_add_printf(buffer, "%u", i); evbuffer_add_printf(buffer, "%u", i);
} }
size = EVBUFFER_LENGTH(buffer); 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);
}
/* Callback for session_group_list. */
static void
format_cb_session_group_list(struct format_tree *ft, struct format_entry *fe)
{
struct session *s = ft->s;
struct session_group *sg;
struct session *loop;
struct evbuffer *buffer;
int size;
if (s == NULL)
return;
sg = session_group_contains(s);
if (sg == NULL)
return;
buffer = evbuffer_new();
TAILQ_FOREACH(loop, &sg->sessions, gentry) {
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); evbuffer_free(buffer);
} }
@@ -805,8 +813,11 @@ format_find(struct format_tree *ft, const char *key, int modifiers)
found = s; found = s;
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)
fe->value = xstrdup("");
}
found = fe->value; found = fe->value;
goto found; goto found;
} }
@@ -878,7 +889,7 @@ format_choose(char *s, char **left, char **right)
} }
/* Is this true? */ /* Is this true? */
static int int
format_true(const char *s) format_true(const char *s)
{ {
if (s != NULL && *s != '\0' && (s[0] != '0' || s[1] != '\0')) if (s != NULL && *s != '\0' && (s[0] != '0' || s[1] != '\0'))
@@ -886,19 +897,17 @@ format_true(const char *s)
return (0); return (0);
} }
/* /* Replace a key. */
* Replace a key/value pair in buffer. #{blah} is expanded directly,
* #{?blah,a,b} is replace with a if blah exists and is nonzero else b.
*/
static int 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)
{ {
char *copy, *copy0, *endptr, *ptr, *found, *new, *value; struct window_pane *wp = ft->wp;
char *from = NULL, *to = NULL, *left, *right; char *copy, *copy0, *endptr, *ptr, *found, *new;
size_t valuelen, newlen, fromlen, tolen, used; char *value, *from = NULL, *to = NULL, *left, *right;
long limit = 0; size_t valuelen, newlen, fromlen, tolen, used;
int modifiers = 0, compare = 0; long limit = 0;
int modifiers = 0, compare = 0, search = 0;
/* Make a copy of the key. */ /* Make a copy of the key. */
copy0 = copy = xmalloc(keylen + 1); copy0 = copy = xmalloc(keylen + 1);
@@ -907,6 +916,30 @@ format_replace(struct format_tree *ft, const char *key, size_t keylen,
/* Is there a length limit or whatnot? */ /* Is there a length limit or whatnot? */
switch (copy[0]) { switch (copy[0]) {
case 'm':
if (copy[1] != ':')
break;
compare = -2;
copy += 2;
break;
case 'C':
if (copy[1] != ':')
break;
search = 1;
copy += 2;
break;
case '|':
if (copy[1] != '|' || copy[2] != ':')
break;
compare = -3;
copy += 3;
break;
case '&':
if (copy[1] != '&' || copy[2] != ':')
break;
compare = -4;
copy += 3;
break;
case '!': case '!':
if (copy[1] == '=' && copy[2] == ':') { if (copy[1] == '=' && copy[2] == ':') {
compare = -1; compare = -1;
@@ -972,16 +1005,30 @@ format_replace(struct format_tree *ft, const char *key, size_t keylen,
} }
/* Is this a comparison or a conditional? */ /* Is this a comparison or a conditional? */
if (compare != 0) { if (search) {
/* Search in pane. */
if (wp == NULL)
value = xstrdup("0");
else
xasprintf(&value, "%u", window_pane_search(wp, copy));
} else if (compare != 0) {
/* Comparison: compare comma-separated left and right. */ /* Comparison: compare comma-separated left and right. */
if (format_choose(copy, &left, &right) != 0) if (format_choose(copy, &left, &right) != 0)
goto fail; goto fail;
left = format_expand(ft, left); left = format_expand(ft, left);
right = format_expand(ft, right); right = format_expand(ft, right);
if (compare == 1 && strcmp(left, right) == 0) if (compare == -3 &&
(format_true(left) || format_true(right)))
value = xstrdup("1");
else if (compare == -4 &&
(format_true(left) && format_true(right)))
value = xstrdup("1");
else if (compare == 1 && strcmp(left, right) == 0)
value = xstrdup("1"); value = xstrdup("1");
else if (compare == -1 && strcmp(left, right) != 0) else if (compare == -1 && strcmp(left, right) != 0)
value = xstrdup("1"); value = xstrdup("1");
else if (compare == -2 && fnmatch(left, right, 0) == 0)
value = xstrdup("1");
else else
value = xstrdup("0"); value = xstrdup("0");
free(right); free(right);
@@ -1090,7 +1137,7 @@ format_expand_time(struct format_tree *ft, const char *fmt, time_t t)
char * char *
format_expand(struct format_tree *ft, const char *fmt) format_expand(struct format_tree *ft, const char *fmt)
{ {
char *buf, *out; char *buf, *out, *name;
const char *ptr, *s, *saved = fmt; const char *ptr, *s, *saved = fmt;
size_t off, len, n, outlen; size_t off, len, n, outlen;
int ch, brackets; int ch, brackets;
@@ -1129,8 +1176,11 @@ format_expand(struct format_tree *ft, const char *fmt)
if (ft->flags & FORMAT_NOJOBS) if (ft->flags & FORMAT_NOJOBS)
out = xstrdup(""); out = xstrdup("");
else else {
out = format_job_get(ft, xstrndup(fmt, n)); name = xstrndup(fmt, n);
out = format_job_get(ft, name);
free(name);
}
outlen = strlen(out); outlen = strlen(out);
while (len - off < outlen + 1) { while (len - off < outlen + 1) {
@@ -1220,6 +1270,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)
{ {
format_add(ft, "session_format", "%d", s != NULL);
format_add(ft, "window_format", "%d", wl != NULL);
format_add(ft, "pane_format", "%d", wp != NULL);
if (s == NULL && c != NULL) if (s == NULL && c != NULL)
s = c->session; s = c->session;
if (wl == NULL && s != NULL) if (wl == NULL && s != NULL)
@@ -1253,8 +1307,13 @@ format_defaults_session(struct format_tree *ft, struct session *s)
sg = session_group_contains(s); sg = session_group_contains(s);
format_add(ft, "session_grouped", "%d", sg != NULL); format_add(ft, "session_grouped", "%d", sg != NULL);
if (sg != NULL) if (sg != NULL) {
format_add(ft, "session_group", "%s", sg->name); format_add(ft, "session_group", "%s", sg->name);
format_add(ft, "session_group_size", "%u",
session_group_count (sg));
format_add_cb(ft, "session_group_list",
format_cb_session_group_list);
}
format_add_tv(ft, "session_created", &s->creation_time); format_add_tv(ft, "session_created", &s->creation_time);
format_add_tv(ft, "session_last_attached", &s->last_attached_time); format_add_tv(ft, "session_last_attached", &s->last_attached_time);
@@ -1376,8 +1435,8 @@ void
format_defaults_pane(struct format_tree *ft, struct window_pane *wp) format_defaults_pane(struct format_tree *ft, struct window_pane *wp)
{ {
struct grid *gd = wp->base.grid; struct grid *gd = wp->base.grid;
int status = wp->status;
u_int idx; u_int idx;
int status, scroll_position;
if (ft->w == NULL) if (ft->w == NULL)
ft->w = wp->window; ft->w = wp->window;
@@ -1397,9 +1456,9 @@ format_defaults_pane(struct format_tree *ft, struct window_pane *wp)
format_add(ft, "pane_id", "%%%u", wp->id); format_add(ft, "pane_id", "%%%u", wp->id);
format_add(ft, "pane_active", "%d", wp == wp->window->active); format_add(ft, "pane_active", "%d", wp == wp->window->active);
format_add(ft, "pane_input_off", "%d", !!(wp->flags & PANE_INPUTOFF)); format_add(ft, "pane_input_off", "%d", !!(wp->flags & PANE_INPUTOFF));
format_add(ft, "pane_pipe", "%d", wp->pipe_fd != -1);
status = wp->status; if ((wp->flags & PANE_STATUSREADY) && WIFEXITED(status))
if (wp->fd == -1 && WIFEXITED(status))
format_add(ft, "pane_dead_status", "%d", WEXITSTATUS(status)); format_add(ft, "pane_dead_status", "%d", WEXITSTATUS(status));
format_add(ft, "pane_dead", "%d", wp->fd == -1); format_add(ft, "pane_dead", "%d", wp->fd == -1);
@@ -1408,6 +1467,12 @@ format_defaults_pane(struct format_tree *ft, struct window_pane *wp)
format_add(ft, "pane_top", "%u", wp->yoff); format_add(ft, "pane_top", "%u", wp->yoff);
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_top", "%d", wp->yoff == 0);
format_add(ft, "pane_at_right", "%d",
wp->xoff + wp->sx == wp->window->sx);
format_add(ft, "pane_at_bottom", "%d",
wp->yoff + wp->sy == wp->window->sy);
} }
format_add(ft, "pane_in_mode", "%d", wp->screen != &wp->base); format_add(ft, "pane_in_mode", "%d", wp->screen != &wp->base);
@@ -1416,8 +1481,8 @@ format_defaults_pane(struct format_tree *ft, struct window_pane *wp)
format_add(ft, "pane_synchronized", "%d", format_add(ft, "pane_synchronized", "%d",
!!options_get_number(wp->window->options, "synchronize-panes")); !!options_get_number(wp->window->options, "synchronize-panes"));
format_add(ft, "pane_search_string", "%s", if (wp->searchstr != NULL)
window_copy_search_string(wp)); format_add(ft, "pane_search_string", "%s", wp->searchstr);
format_add(ft, "pane_tty", "%s", wp->tty); format_add(ft, "pane_tty", "%s", wp->tty);
format_add(ft, "pane_pid", "%ld", (long) wp->pid); format_add(ft, "pane_pid", "%ld", (long) wp->pid);
@@ -1430,9 +1495,7 @@ format_defaults_pane(struct format_tree *ft, struct window_pane *wp)
format_add(ft, "scroll_region_upper", "%u", wp->base.rupper); format_add(ft, "scroll_region_upper", "%u", wp->base.rupper);
format_add(ft, "scroll_region_lower", "%u", wp->base.rlower); format_add(ft, "scroll_region_lower", "%u", wp->base.rlower);
scroll_position = window_copy_scroll_position(wp); window_copy_add_formats(wp, ft);
if (scroll_position != -1)
format_add(ft, "scroll_position", "%d", scroll_position);
format_add(ft, "alternate_on", "%d", wp->saved_grid ? 1 : 0); format_add(ft, "alternate_on", "%d", wp->saved_grid ? 1 : 0);
format_add(ft, "alternate_saved_x", "%u", wp->saved_cx); format_add(ft, "alternate_saved_x", "%u", wp->saved_cx);
@@ -1465,12 +1528,17 @@ format_defaults_pane(struct format_tree *ft, struct window_pane *wp)
void void
format_defaults_paste_buffer(struct format_tree *ft, struct paste_buffer *pb) format_defaults_paste_buffer(struct format_tree *ft, struct paste_buffer *pb)
{ {
size_t bufsize; struct timeval tv;
char *s; size_t size;
char *s;
paste_buffer_data(pb, &bufsize); timerclear(&tv);
format_add(ft, "buffer_size", "%zu", bufsize); tv.tv_sec = paste_buffer_created(pb);
paste_buffer_data(pb, &size);
format_add(ft, "buffer_size", "%zu", size);
format_add(ft, "buffer_name", "%s", paste_buffer_name(pb)); format_add(ft, "buffer_name", "%s", paste_buffer_name(pb));
format_add_tv(ft, "buffer_created", &tv);
s = paste_make_sample(pb); s = paste_make_sample(pb);
format_add(ft, "buffer_sample", "%s", s); format_add(ft, "buffer_sample", "%s", s);

View File

@@ -75,7 +75,7 @@ grid_view_clear_history(struct grid *gd, u_int bg)
/* Scroll the lines into the history. */ /* Scroll the lines into the history. */
for (yy = 0; yy < last; yy++) { for (yy = 0; yy < last; yy++) {
grid_collect_history(gd, bg); grid_collect_history(gd);
grid_scroll_history(gd, bg); grid_scroll_history(gd, bg);
} }
if (last < gd->sy) if (last < gd->sy)
@@ -96,32 +96,34 @@ grid_view_clear(struct grid *gd, u_int px, u_int py, u_int nx, u_int ny,
/* Scroll region up. */ /* Scroll region up. */
void void
grid_view_scroll_region_up(struct grid *gd, u_int rupper, u_int rlower) grid_view_scroll_region_up(struct grid *gd, u_int rupper, u_int rlower,
u_int bg)
{ {
if (gd->flags & GRID_HISTORY) { if (gd->flags & GRID_HISTORY) {
grid_collect_history(gd, 8); grid_collect_history(gd);
if (rupper == 0 && rlower == gd->sy - 1) if (rupper == 0 && rlower == gd->sy - 1)
grid_scroll_history(gd, 8); grid_scroll_history(gd, bg);
else { else {
rupper = grid_view_y(gd, rupper); rupper = grid_view_y(gd, rupper);
rlower = grid_view_y(gd, rlower); rlower = grid_view_y(gd, rlower);
grid_scroll_history_region(gd, rupper, rlower); grid_scroll_history_region(gd, rupper, rlower, bg);
} }
} else { } else {
rupper = grid_view_y(gd, rupper); rupper = grid_view_y(gd, rupper);
rlower = grid_view_y(gd, rlower); rlower = grid_view_y(gd, rlower);
grid_move_lines(gd, rupper, rupper + 1, rlower - rupper, 8); grid_move_lines(gd, rupper, rupper + 1, rlower - rupper, bg);
} }
} }
/* Scroll region down. */ /* Scroll region down. */
void void
grid_view_scroll_region_down(struct grid *gd, u_int rupper, u_int rlower) grid_view_scroll_region_down(struct grid *gd, u_int rupper, u_int rlower,
u_int bg)
{ {
rupper = grid_view_y(gd, rupper); rupper = grid_view_y(gd, rupper);
rlower = grid_view_y(gd, rlower); rlower = grid_view_y(gd, rlower);
grid_move_lines(gd, rupper + 1, rupper, rlower - rupper, 8); grid_move_lines(gd, rupper + 1, rupper, rlower - rupper, bg);
} }
/* Insert lines. */ /* Insert lines. */

646
grid.c
View File

@@ -43,22 +43,8 @@ static const struct grid_cell_entry grid_default_entry = {
0, { .data = { 0, 8, 8, ' ' } } 0, { .data = { 0, 8, 8, ' ' } }
}; };
static void grid_expand_line(struct grid *, u_int, u_int, u_int);
static void grid_empty_line(struct grid *, u_int, u_int); static void grid_empty_line(struct grid *, u_int, u_int);
static void grid_reflow_copy(struct grid_line *, u_int, struct grid_line *,
u_int, u_int);
static void grid_reflow_join(struct grid *, u_int *, struct grid_line *,
u_int);
static void grid_reflow_split(struct grid *, u_int *, struct grid_line *,
u_int, u_int);
static void grid_reflow_move(struct grid *, u_int *, struct grid_line *);
static size_t grid_string_cells_fg(const struct grid_cell *, int *);
static size_t grid_string_cells_bg(const struct grid_cell *, int *);
static void grid_string_cells_code(const struct grid_cell *,
const struct grid_cell *, char *, size_t, int);
/* Store cell in entry. */ /* Store cell in entry. */
static void static void
grid_store_cell(struct grid_cell_entry *gce, const struct grid_cell *gc, grid_store_cell(struct grid_cell_entry *gce, const struct grid_cell *gc,
@@ -94,6 +80,48 @@ grid_need_extended_cell(const struct grid_cell_entry *gce,
return (0); return (0);
} }
/* Free up unused extended cells. */
static void
grid_compact_line(struct grid_line *gl)
{
int new_extdsize = 0;
struct grid_cell *new_extddata;
struct grid_cell_entry *gce;
struct grid_cell *gc;
u_int px, idx;
if (gl->extdsize == 0)
return;
for (px = 0; px < gl->cellsize; px++) {
gce = &gl->celldata[px];
if (gce->flags & GRID_FLAG_EXTENDED)
new_extdsize++;
}
if (new_extdsize == 0) {
free(gl->extddata);
gl->extddata = NULL;
gl->extdsize = 0;
return;
}
new_extddata = xreallocarray(NULL, new_extdsize, sizeof *gl->extddata);
idx = 0;
for (px = 0; px < gl->cellsize; px++) {
gce = &gl->celldata[px];
if (gce->flags & GRID_FLAG_EXTENDED) {
gc = &gl->extddata[gce->offset];
memcpy(&new_extddata[idx], gc, sizeof *gc);
gce->offset = idx++;
}
}
free(gl->extddata);
gl->extddata = new_extddata;
gl->extdsize = new_extdsize;
}
/* Set cell as extended. */ /* Set cell as extended. */
static struct grid_cell * static struct grid_cell *
grid_extended_cell(struct grid_line *gl, struct grid_cell_entry *gce, grid_extended_cell(struct grid_line *gl, struct grid_cell_entry *gce,
@@ -140,7 +168,7 @@ grid_clear_cell(struct grid *gd, u_int px, u_int py, u_int bg)
static int static int
grid_check_y(struct grid *gd, u_int py) grid_check_y(struct grid *gd, u_int py)
{ {
if ((py) >= (gd)->hsize + (gd)->sy) { if (py >= gd->hsize + gd->sy) {
log_debug("y out of range: %u", py); log_debug("y out of range: %u", py);
return (-1); return (-1);
} }
@@ -162,6 +190,26 @@ grid_cells_equal(const struct grid_cell *gca, const struct grid_cell *gcb)
return (memcmp(gca->data.data, gcb->data.data, gca->data.size) == 0); return (memcmp(gca->data.data, gcb->data.data, gca->data.size) == 0);
} }
/* Free one line. */
static void
grid_free_line(struct grid *gd, u_int py)
{
free(gd->linedata[py].celldata);
gd->linedata[py].celldata = NULL;
free(gd->linedata[py].extddata);
gd->linedata[py].extddata = NULL;
}
/* Free several lines. */
static void
grid_free_lines(struct grid *gd, u_int py, u_int ny)
{
u_int yy;
for (yy = py; yy < py + ny; yy++)
grid_free_line(gd, yy);
}
/* Create a new grid. */ /* Create a new grid. */
struct grid * struct grid *
grid_create(u_int sx, u_int sy, u_int hlimit) grid_create(u_int sx, u_int sy, u_int hlimit)
@@ -178,7 +226,10 @@ grid_create(u_int sx, u_int sy, u_int hlimit)
gd->hsize = 0; gd->hsize = 0;
gd->hlimit = hlimit; gd->hlimit = hlimit;
gd->linedata = xcalloc(gd->sy, sizeof *gd->linedata); if (gd->sy != 0)
gd->linedata = xcalloc(gd->sy, sizeof *gd->linedata);
else
gd->linedata = NULL;
return (gd); return (gd);
} }
@@ -187,14 +238,7 @@ grid_create(u_int sx, u_int sy, u_int hlimit)
void void
grid_destroy(struct grid *gd) grid_destroy(struct grid *gd)
{ {
struct grid_line *gl; grid_free_lines(gd, 0, gd->hsize + gd->sy);
u_int yy;
for (yy = 0; yy < gd->hsize + gd->sy; yy++) {
gl = &gd->linedata[yy];
free(gl->celldata);
free(gl->extddata);
}
free(gd->linedata); free(gd->linedata);
@@ -233,19 +277,28 @@ grid_compare(struct grid *ga, struct grid *gb)
* and shift up. * and shift up.
*/ */
void void
grid_collect_history(struct grid *gd, u_int bg) grid_collect_history(struct grid *gd)
{ {
u_int yy; u_int ny;
if (gd->hsize < gd->hlimit) if (gd->hsize == 0 || gd->hsize < gd->hlimit)
return; return;
yy = gd->hlimit / 10; ny = gd->hlimit / 10;
if (yy < 1) if (ny < 1)
yy = 1; ny = 1;
if (ny > gd->hsize)
ny = gd->hsize;
grid_move_lines(gd, 0, yy, gd->hsize + gd->sy - yy, bg); /*
gd->hsize -= yy; * Free the lines from 0 to ny then move the remaining lines over
* them.
*/
grid_free_lines(gd, 0, ny);
memmove(&gd->linedata[0], &gd->linedata[ny],
(gd->hsize + gd->sy - ny) * (sizeof *gd->linedata));
gd->hsize -= ny;
if (gd->hscrolled > gd->hsize) if (gd->hscrolled > gd->hsize)
gd->hscrolled = gd->hsize; gd->hscrolled = gd->hsize;
} }
@@ -265,6 +318,7 @@ grid_scroll_history(struct grid *gd, u_int bg)
grid_empty_line(gd, yy, bg); grid_empty_line(gd, yy, bg);
gd->hscrolled++; gd->hscrolled++;
grid_compact_line(&gd->linedata[gd->hsize]);
gd->hsize++; gd->hsize++;
} }
@@ -272,8 +326,9 @@ grid_scroll_history(struct grid *gd, u_int bg)
void void
grid_clear_history(struct grid *gd) grid_clear_history(struct grid *gd)
{ {
grid_clear_lines(gd, 0, gd->hsize, 8); grid_free_lines(gd, 0, gd->hsize);
grid_move_lines(gd, 0, gd->hsize, gd->sy, 8); memmove(&gd->linedata[0], &gd->linedata[gd->hsize],
gd->sy * (sizeof *gd->linedata));
gd->hscrolled = 0; gd->hscrolled = 0;
gd->hsize = 0; gd->hsize = 0;
@@ -284,9 +339,9 @@ grid_clear_history(struct grid *gd)
/* Scroll a region up, moving the top line into the history. */ /* Scroll a region up, moving the top line into the history. */
void void
grid_scroll_history_region(struct grid *gd, u_int upper, u_int lower) grid_scroll_history_region(struct grid *gd, u_int upper, u_int lower, u_int bg)
{ {
struct grid_line *gl_history, *gl_upper, *gl_lower; struct grid_line *gl_history, *gl_upper;
u_int yy; u_int yy;
/* Create a space for a new line. */ /* Create a space for a new line. */
@@ -302,14 +357,13 @@ grid_scroll_history_region(struct grid *gd, u_int upper, u_int lower)
upper++; upper++;
gl_upper = &gd->linedata[upper]; gl_upper = &gd->linedata[upper];
lower++; lower++;
gl_lower = &gd->linedata[lower];
/* Move the line into the history. */ /* Move the line into the history. */
memcpy(gl_history, gl_upper, sizeof *gl_history); memcpy(gl_history, gl_upper, sizeof *gl_history);
/* Then move the region up and clear the bottom line. */ /* Then move the region up and clear the bottom line. */
memmove(gl_upper, gl_upper + 1, (lower - upper) * sizeof *gl_upper); memmove(gl_upper, gl_upper + 1, (lower - upper) * sizeof *gl_upper);
memset(gl_lower, 0, sizeof *gl_lower); grid_empty_line(gd, lower, bg);
/* Move the history offset down over the line. */ /* Move the history offset down over the line. */
gd->hscrolled++; gd->hscrolled++;
@@ -358,20 +412,11 @@ grid_peek_line(struct grid *gd, u_int py)
return (&gd->linedata[py]); return (&gd->linedata[py]);
} }
/* Get cell for reading. */ /* Get cell from line. */
void static void
grid_get_cell(struct grid *gd, u_int px, u_int py, struct grid_cell *gc) grid_get_cell1(struct grid_line *gl, u_int px, struct grid_cell *gc)
{ {
struct grid_line *gl; struct grid_cell_entry *gce = &gl->celldata[px];
struct grid_cell_entry *gce;
if (grid_check_y(gd, py) != 0 || px >= gd->linedata[py].cellsize) {
memcpy(gc, &grid_default_cell, sizeof *gc);
return;
}
gl = &gd->linedata[py];
gce = &gl->celldata[px];
if (gce->flags & GRID_FLAG_EXTENDED) { if (gce->flags & GRID_FLAG_EXTENDED) {
if (gce->offset >= gl->extdsize) if (gce->offset >= gl->extdsize)
@@ -392,6 +437,17 @@ grid_get_cell(struct grid *gd, u_int px, u_int py, struct grid_cell *gc)
utf8_set(&gc->data, gce->data.data); utf8_set(&gc->data, gce->data.data);
} }
/* Get cell for reading. */
void
grid_get_cell(struct grid *gd, u_int px, u_int py, struct grid_cell *gc)
{
if (grid_check_y(gd, py) != 0 || px >= gd->linedata[py].cellsize) {
memcpy(gc, &grid_default_cell, sizeof *gc);
return;
}
return (grid_get_cell1(&gd->linedata[py], px, gc));
}
/* Set cell at relative position. */ /* Set cell at relative position. */
void void
grid_set_cell(struct grid *gd, u_int px, u_int py, const struct grid_cell *gc) grid_set_cell(struct grid *gd, u_int px, u_int py, const struct grid_cell *gc)
@@ -482,8 +538,7 @@ grid_clear(struct grid *gd, u_int px, u_int py, u_int nx, u_int ny, u_int bg)
void void
grid_clear_lines(struct grid *gd, u_int py, u_int ny, u_int bg) grid_clear_lines(struct grid *gd, u_int py, u_int ny, u_int bg)
{ {
struct grid_line *gl; u_int yy;
u_int yy;
if (ny == 0) if (ny == 0)
return; return;
@@ -494,9 +549,7 @@ grid_clear_lines(struct grid *gd, u_int py, u_int ny, u_int bg)
return; return;
for (yy = py; yy < py + ny; yy++) { for (yy = py; yy < py + ny; yy++) {
gl = &gd->linedata[yy]; grid_free_line(gd, yy);
free(gl->celldata);
free(gl->extddata);
grid_empty_line(gd, yy, bg); grid_empty_line(gd, yy, bg);
} }
} }
@@ -523,13 +576,16 @@ grid_move_lines(struct grid *gd, u_int dy, u_int py, u_int ny, u_int bg)
for (yy = dy; yy < dy + ny; yy++) { for (yy = dy; yy < dy + ny; yy++) {
if (yy >= py && yy < py + ny) if (yy >= py && yy < py + ny)
continue; continue;
grid_clear_lines(gd, yy, 1, bg); grid_free_line(gd, yy);
} }
memmove(&gd->linedata[dy], &gd->linedata[py], memmove(&gd->linedata[dy], &gd->linedata[py],
ny * (sizeof *gd->linedata)); ny * (sizeof *gd->linedata));
/* Wipe any lines that have been moved (without freeing them). */ /*
* Wipe any lines that have been moved (without freeing them - they are
* still present).
*/
for (yy = py; yy < py + ny; yy++) { for (yy = py; yy < py + ny; yy++) {
if (yy < dy || yy >= dy + ny) if (yy < dy || yy >= dy + ny)
grid_empty_line(gd, yy, bg); grid_empty_line(gd, yy, bg);
@@ -666,8 +722,7 @@ grid_string_cells_bg(const struct grid_cell *gc, int *values)
/* /*
* Returns ANSI code to set particular attributes (colour, bold and so on) * Returns ANSI code to set particular attributes (colour, bold and so on)
* given a current state. The output buffer must be able to hold at least 57 * given a current state.
* bytes.
*/ */
static void static void
grid_string_cells_code(const struct grid_cell *lastgc, grid_string_cells_code(const struct grid_cell *lastgc,
@@ -847,9 +902,8 @@ grid_string_cells(struct grid *gd, u_int px, u_int py, u_int nx,
} }
/* /*
* Duplicate a set of lines between two grids. If there aren't enough lines in * Duplicate a set of lines between two grids. Both source and destination
* either source or destination, the number of lines is limited to the number * should be big enough.
* available.
*/ */
void void
grid_duplicate_lines(struct grid *dst, u_int dy, struct grid *src, u_int sy, grid_duplicate_lines(struct grid *dst, u_int dy, struct grid *src, u_int sy,
@@ -862,7 +916,7 @@ grid_duplicate_lines(struct grid *dst, u_int dy, struct grid *src, u_int sy,
ny = dst->hsize + dst->sy - dy; ny = dst->hsize + dst->sy - dy;
if (sy + ny > src->hsize + src->sy) if (sy + ny > src->hsize + src->sy)
ny = src->hsize + src->sy - sy; ny = src->hsize + src->sy - sy;
grid_clear_lines(dst, dy, ny, 8); grid_free_lines(dst, dy, ny);
for (yy = 0; yy < ny; yy++) { for (yy = 0; yy < ny; yy++) {
srcl = &src->linedata[sy]; srcl = &src->linedata[sy];
@@ -890,164 +944,326 @@ grid_duplicate_lines(struct grid *dst, u_int dy, struct grid *src, u_int sy,
} }
} }
/* Copy a section of a line. */ /* Mark line as dead. */
static void static void
grid_reflow_copy(struct grid_line *dst_gl, u_int to, struct grid_line *src_gl, grid_reflow_dead(struct grid_line *gl)
u_int from, u_int to_copy)
{ {
struct grid_cell_entry *gce; memset(gl, 0, sizeof *gl);
u_int i, was; gl->flags = GRID_LINE_DEAD;
}
memcpy(&dst_gl->celldata[to], &src_gl->celldata[from], /* Add lines, return the first new one. */
to_copy * sizeof *dst_gl->celldata); static struct grid_line *
grid_reflow_add(struct grid *gd, u_int n)
{
struct grid_line *gl;
u_int sy = gd->sy + n;
for (i = to; i < to + to_copy; i++) { gd->linedata = xreallocarray(gd->linedata, sy, sizeof *gd->linedata);
gce = &dst_gl->celldata[i]; gl = &gd->linedata[gd->sy];
if (~gce->flags & GRID_FLAG_EXTENDED) memset(gl, 0, n * (sizeof *gl));
gd->sy = sy;
return (gl);
}
/* Move a line across. */
static struct grid_line *
grid_reflow_move(struct grid *gd, struct grid_line *from)
{
struct grid_line *to;
to = grid_reflow_add(gd, 1);
memcpy(to, from, sizeof *to);
grid_reflow_dead(from);
return (to);
}
/* Join line below onto this one. */
static void
grid_reflow_join(struct grid *target, struct grid *gd, u_int sx, u_int yy,
u_int width, u_int *cy, int already)
{
struct grid_line *gl, *from;
struct grid_cell gc;
u_int lines, want, left, i, to, line;
u_int at;
int wrapped = 1;
/*
* Add a new target line.
*/
if (!already) {
to = target->sy;
gl = grid_reflow_move(target, &gd->linedata[yy]);
} else {
to = target->sy - 1;
gl = &target->linedata[to];
}
at = gl->cellused;
/*
* Loop until no more to consume or the target line is full.
*/
lines = 0;
for (;;) {
/*
* If this is now the last line, there is nothing more to be
* done.
*/
if (yy + lines == gd->hsize + gd->sy)
break;
line = yy + 1 + lines;
/* If the next line is empty, skip it. */
if (~gd->linedata[line].flags & GRID_LINE_WRAPPED)
wrapped = 0;
if (gd->linedata[line].cellused == 0) {
if (!wrapped)
break;
continue; continue;
was = gce->offset;
dst_gl->extddata = xreallocarray(dst_gl->extddata,
dst_gl->extdsize + 1, sizeof *dst_gl->extddata);
gce->offset = dst_gl->extdsize++;
memcpy(&dst_gl->extddata[gce->offset], &src_gl->extddata[was],
sizeof *dst_gl->extddata);
}
}
/* Join line data. */
static void
grid_reflow_join(struct grid *dst, u_int *py, struct grid_line *src_gl,
u_int new_x)
{
struct grid_line *dst_gl = &dst->linedata[(*py) - 1];
u_int left, to_copy, ox, nx;
/* How much is left on the old line? */
left = new_x - dst_gl->cellused;
/* Work out how much to append. */
to_copy = src_gl->cellused;
if (to_copy > left)
to_copy = left;
ox = dst_gl->cellused;
nx = ox + to_copy;
/* Resize the destination line. */
dst_gl->celldata = xreallocarray(dst_gl->celldata, nx,
sizeof *dst_gl->celldata);
dst_gl->cellsize = dst_gl->cellused = nx;
/* Append as much as possible. */
grid_reflow_copy(dst_gl, ox, src_gl, 0, to_copy);
/* If there is any left in the source, split it. */
if (src_gl->cellused > to_copy) {
dst_gl->flags |= GRID_LINE_WRAPPED;
src_gl->cellused -= to_copy;
grid_reflow_split(dst, py, src_gl, new_x, to_copy);
}
}
/* Split line data. */
static void
grid_reflow_split(struct grid *dst, u_int *py, struct grid_line *src_gl,
u_int new_x, u_int offset)
{
struct grid_line *dst_gl = NULL;
u_int to_copy;
/* Loop and copy sections of the source line. */
while (src_gl->cellused > 0) {
/* Create new line. */
if (*py >= dst->hsize + dst->sy)
grid_scroll_history(dst, 8);
dst_gl = &dst->linedata[*py];
(*py)++;
/* How much should we copy? */
to_copy = new_x;
if (to_copy > src_gl->cellused)
to_copy = src_gl->cellused;
/* Expand destination line. */
dst_gl->celldata = xreallocarray(NULL, to_copy,
sizeof *dst_gl->celldata);
dst_gl->cellsize = dst_gl->cellused = to_copy;
dst_gl->flags |= GRID_LINE_WRAPPED;
/* Copy the data. */
grid_reflow_copy(dst_gl, 0, src_gl, offset, to_copy);
/* Move offset and reduce old line size. */
offset += to_copy;
src_gl->cellused -= to_copy;
}
/* Last line is not wrapped. */
if (dst_gl != NULL)
dst_gl->flags &= ~GRID_LINE_WRAPPED;
}
/* Move line data. */
static void
grid_reflow_move(struct grid *dst, u_int *py, struct grid_line *src_gl)
{
struct grid_line *dst_gl;
/* Create new line. */
if (*py >= dst->hsize + dst->sy)
grid_scroll_history(dst, 8);
dst_gl = &dst->linedata[*py];
(*py)++;
/* Copy the old line. */
memcpy(dst_gl, src_gl, sizeof *dst_gl);
dst_gl->flags &= ~GRID_LINE_WRAPPED;
/* Clear old line. */
src_gl->celldata = NULL;
src_gl->extddata = NULL;
}
/*
* Reflow lines from src grid into dst grid of width new_x. Returns number of
* lines fewer in the visible area. The source grid is destroyed.
*/
u_int
grid_reflow(struct grid *dst, struct grid *src, u_int new_x)
{
u_int py, sy, line;
int previous_wrapped;
struct grid_line *src_gl;
py = 0;
sy = src->sy;
previous_wrapped = 0;
for (line = 0; line < sy + src->hsize; line++) {
src_gl = src->linedata + line;
if (!previous_wrapped) {
/* Wasn't wrapped. If smaller, move to destination. */
if (src_gl->cellused <= new_x)
grid_reflow_move(dst, &py, src_gl);
else
grid_reflow_split(dst, &py, src_gl, new_x, 0);
} else {
/* Previous was wrapped. Try to join. */
grid_reflow_join(dst, &py, src_gl, new_x);
} }
previous_wrapped = (src_gl->flags & GRID_LINE_WRAPPED);
/* This is where we started scrolling. */ /*
if (line == sy + src->hsize - src->hscrolled - 1) * Is the destination line now full? Copy the first character
dst->hscrolled = 0; * separately because we need to leave "from" set to the last
* line if this line is full.
*/
grid_get_cell1(&gd->linedata[line], 0, &gc);
if (width + gc.data.width > sx)
break;
width += gc.data.width;
grid_set_cell(target, at, to, &gc);
at++;
/* Join as much more as possible onto the current line. */
from = &gd->linedata[line];
for (want = 1; want < from->cellused; want++) {
grid_get_cell1(from, want, &gc);
if (width + gc.data.width > sx)
break;
width += gc.data.width;
grid_set_cell(target, at, to, &gc);
at++;
}
lines++;
/*
* If this line wasn't wrapped or we didn't consume the entire
* line, don't try to join any further lines.
*/
if (!wrapped || want != from->cellused || width == sx)
break;
}
if (lines == 0)
return;
/*
* If we didn't consume the entire final line, then remove what we did
* consume. If we consumed the entire line and it wasn't wrapped,
* remove the wrap flag from this line.
*/
left = from->cellused - want;
if (left != 0) {
grid_move_cells(gd, 0, want, yy + lines, left, 8);
from->cellsize = from->cellused = left;
lines--;
} else if (!wrapped)
gl->flags &= ~GRID_LINE_WRAPPED;
/* Remove the lines that were completely consumed. */
for (i = yy + 1; i < yy + 1 + lines; i++) {
free(gd->linedata[i].celldata);
free(gd->linedata[i].extddata);
grid_reflow_dead(&gd->linedata[i]);
} }
grid_destroy(src); /* Adjust cursor and scroll positions. */
if (*cy > to + lines)
if (py > sy) *cy -= lines;
return (0); else if (*cy > to)
return (sy - py); *cy = to;
if (gd->hscrolled > to + lines)
gd->hscrolled -= lines;
else if (gd->hscrolled > to)
gd->hscrolled = to;
}
/* Split this line into several new ones */
static void
grid_reflow_split(struct grid *target, struct grid *gd, u_int sx, u_int yy,
u_int at, u_int *cy)
{
struct grid_line *gl = &gd->linedata[yy], *first;
struct grid_cell gc;
u_int line, lines, width, i, xx;
u_int used = gl->cellused;
int flags = gl->flags;
/* How many lines do we need to insert? We know we need at least two. */
if (~gl->flags & GRID_LINE_EXTENDED)
lines = 1 + (gl->cellused - 1) / sx;
else {
lines = 2;
width = 0;
for (i = at; i < used; i++) {
grid_get_cell1(gl, i, &gc);
if (width + gc.data.width > sx) {
lines++;
width = 0;
}
width += gc.data.width;
}
}
/* Insert new lines. */
line = target->sy + 1;
first = grid_reflow_add(target, lines);
/* Copy sections from the original line. */
width = 0;
xx = 0;
for (i = at; i < used; i++) {
grid_get_cell1(gl, i, &gc);
if (width + gc.data.width > sx) {
target->linedata[line].flags |= GRID_LINE_WRAPPED;
line++;
width = 0;
xx = 0;
}
width += gc.data.width;
grid_set_cell(target, xx, line, &gc);
xx++;
}
if (flags & GRID_LINE_WRAPPED)
target->linedata[line].flags |= GRID_LINE_WRAPPED;
/* Move the remainder of the original line. */
gl->cellsize = gl->cellused = at;
gl->flags |= GRID_LINE_WRAPPED;
memcpy(first, gl, sizeof *first);
grid_reflow_dead(gl);
/* Adjust the cursor and scroll positions. */
if (yy <= *cy)
(*cy) += lines - 1;
if (yy <= gd->hscrolled)
gd->hscrolled += lines - 1;
/*
* If the original line had the wrapped flag and there is still space
* in the last new line, try to join with the next lines.
*/
if (width < sx && (flags & GRID_LINE_WRAPPED))
grid_reflow_join(target, gd, sx, yy, width, cy, 1);
}
/* Reflow lines on grid to new width. */
void
grid_reflow(struct grid *gd, u_int sx, u_int *cursor)
{
struct grid *target;
struct grid_line *gl;
struct grid_cell gc;
u_int yy, cy, width, i, at, first;
struct timeval start, tv;
gettimeofday(&start, NULL);
log_debug("%s: %u lines, new width %u", __func__, gd->hsize + gd->sy,
sx);
cy = gd->hsize + (*cursor);
/*
* Create a destination grid. This is just used as a container for the
* line data and may not be fully valid.
*/
target = grid_create(gd->sx, 0, 0);
/*
* Loop over each source line.
*/
for (yy = 0; yy < gd->hsize + gd->sy; yy++) {
gl = &gd->linedata[yy];
if (gl->flags & GRID_LINE_DEAD)
continue;
/*
* Work out the width of this line. first is the width of the
* first character, at is the point at which the available
* width is hit, and width is the full line width.
*/
first = at = width = 0;
if (~gl->flags & GRID_LINE_EXTENDED) {
first = 1;
width = gl->cellused;
if (width > sx)
at = sx;
else
at = width;
} else {
for (i = 0; i < gl->cellused; i++) {
grid_get_cell1(gl, i, &gc);
if (i == 0)
first = gc.data.width;
if (at == 0 && width + gc.data.width > sx)
at = i;
width += gc.data.width;
}
}
/*
* If the line is exactly right or the first character is wider
* than the targe width, just move it across unchanged.
*/
if (width == sx || first > sx) {
grid_reflow_move(target, gl);
continue;
}
/*
* If the line is too big, it needs to be split, whether or not
* it was previously wrapped.
*/
if (width > sx) {
grid_reflow_split(target, gd, sx, yy, at, &cy);
continue;
}
/*
* If the line was previously wrapped, join as much as possible
* of the next line.
*/
if (gl->flags & GRID_LINE_WRAPPED)
grid_reflow_join(target, gd, sx, yy, width, &cy, 0);
else
grid_reflow_move(target, gl);
}
/*
* Replace the old grid with the new.
*/
if (target->sy < gd->sy)
grid_reflow_add(target, gd->sy - target->sy);
gd->hsize = target->sy - gd->sy;
free(gd->linedata);
gd->linedata = target->linedata;
free(target);
/*
* Update scrolled and cursor positions.
*/
if (gd->hscrolled > gd->hsize)
gd->hscrolled = gd->hsize;
if (cy < gd->hsize)
*cursor = 0;
else
*cursor = cy - gd->hsize;
gettimeofday(&tv, NULL);
timersub(&tv, &start, &tv);
log_debug("%s: now %u lines (in %llu.%06u seconds)", __func__,
gd->hsize + gd->sy, (unsigned long long)tv.tv_sec,
(u_int)tv.tv_usec);
} }

27
hooks.c
View File

@@ -139,33 +139,6 @@ hooks_find(struct hooks *hooks, const char *name)
return (hook); return (hook);
} }
void
hooks_run(struct hooks *hooks, struct client *c, struct cmd_find_state *fs,
const char *fmt, ...)
{
struct hook *hook;
va_list ap;
char *name;
struct cmdq_item *new_item;
va_start(ap, fmt);
xvasprintf(&name, fmt, ap);
va_end(ap);
hook = hooks_find(hooks, name);
if (hook == NULL) {
free(name);
return;
}
log_debug("running hook %s", name);
new_item = cmdq_get_command(hook->cmdlist, fs, NULL, CMDQ_NOHOOKS);
cmdq_format(new_item, "hook", "%s", name);
cmdq_append(c, new_item);
free(name);
}
void void
hooks_insert(struct hooks *hooks, struct cmdq_item *item, hooks_insert(struct hooks *hooks, struct cmdq_item *item,
struct cmd_find_state *fs, const char *fmt, ...) struct cmd_find_state *fs, const char *fmt, ...)

View File

@@ -45,6 +45,10 @@ static const struct input_key_ent input_keys[] = {
/* Backspace key. */ /* Backspace key. */
{ KEYC_BSPACE, "\177", 0 }, { KEYC_BSPACE, "\177", 0 },
/* Paste keys. */
{ KEYC_PASTE_START, "\033[200~", 0 },
{ KEYC_PASTE_END, "\033[201~", 0 },
/* Function keys. */ /* Function keys. */
{ KEYC_F1, "\033OP", 0 }, { KEYC_F1, "\033OP", 0 },
{ KEYC_F2, "\033OQ", 0 }, { KEYC_F2, "\033OQ", 0 },
@@ -172,7 +176,7 @@ input_key(struct window_pane *wp, key_code key, struct mouse_event *m)
* If this is a normal 7-bit key, just send it, with a leading escape * If this is a normal 7-bit key, just send it, with a leading escape
* if necessary. If it is a UTF-8 key, split it and send it. * if necessary. If it is a UTF-8 key, split it and send it.
*/ */
justkey = (key & ~KEYC_ESCAPE); justkey = (key & ~(KEYC_XTERM|KEYC_ESCAPE));
if (justkey <= 0x7f) { if (justkey <= 0x7f) {
if (key & KEYC_ESCAPE) if (key & KEYC_ESCAPE)
bufferevent_write(wp->event, "\033", 1); bufferevent_write(wp->event, "\033", 1);

622
input.c

File diff suppressed because it is too large Load Diff

16
job.c
View File

@@ -43,13 +43,14 @@ struct joblist all_jobs = LIST_HEAD_INITIALIZER(all_jobs);
struct job * struct job *
job_run(const char *cmd, struct session *s, const char *cwd, job_run(const char *cmd, struct session *s, const char *cwd,
job_update_cb updatecb, job_complete_cb completecb, job_free_cb freecb, job_update_cb updatecb, job_complete_cb completecb, job_free_cb freecb,
void *data) void *data, int flags)
{ {
struct job *job; struct job *job;
struct environ *env; struct environ *env;
pid_t pid; pid_t pid;
int nullfd, out[2]; int nullfd, out[2];
const char *home; const char *home;
sigset_t set, oldset;
if (socketpair(AF_UNIX, SOCK_STREAM, PF_UNSPEC, out) != 0) if (socketpair(AF_UNIX, SOCK_STREAM, PF_UNSPEC, out) != 0)
return (NULL); return (NULL);
@@ -60,14 +61,18 @@ job_run(const char *cmd, struct session *s, const char *cwd,
*/ */
env = environ_for_session(s, !cfg_finished); env = environ_for_session(s, !cfg_finished);
sigfillset(&set);
sigprocmask(SIG_BLOCK, &set, &oldset);
switch (pid = fork()) { switch (pid = fork()) {
case -1: case -1:
sigprocmask(SIG_SETMASK, &oldset, NULL);
environ_free(env); environ_free(env);
close(out[0]); close(out[0]);
close(out[1]); close(out[1]);
return (NULL); return (NULL);
case 0: /* child */ case 0:
clear_signals(1); proc_clear_signals(server_proc, 1);
sigprocmask(SIG_SETMASK, &oldset, NULL);
if (cwd == NULL || chdir(cwd) != 0) { if (cwd == NULL || chdir(cwd) != 0) {
if ((home = find_home()) == NULL || chdir(home) != 0) if ((home = find_home()) == NULL || chdir(home) != 0)
@@ -99,12 +104,13 @@ job_run(const char *cmd, struct session *s, const char *cwd,
fatal("execl failed"); fatal("execl failed");
} }
/* parent */ sigprocmask(SIG_SETMASK, &oldset, NULL);
environ_free(env); environ_free(env);
close(out[1]); close(out[1]);
job = xmalloc(sizeof *job); job = xmalloc(sizeof *job);
job->state = JOB_RUNNING; job->state = JOB_RUNNING;
job->flags = flags;
job->cmd = xstrdup(cmd); job->cmd = xstrdup(cmd);
job->pid = pid; job->pid = pid;
@@ -157,7 +163,7 @@ job_read_callback(__unused struct bufferevent *bufev, void *data)
struct job *job = data; struct job *job = data;
if (job->updatecb != NULL) if (job->updatecb != NULL)
job->updatecb (job); job->updatecb(job);
} }
/* /*

View File

@@ -138,12 +138,17 @@ void
key_bindings_remove_table(const char *name) key_bindings_remove_table(const char *name)
{ {
struct key_table *table; struct key_table *table;
struct client *c;
table = key_bindings_get_table(name, 0); table = key_bindings_get_table(name, 0);
if (table != NULL) { if (table != NULL) {
RB_REMOVE(key_tables, &key_tables, table); RB_REMOVE(key_tables, &key_tables, table);
key_bindings_unref_table(table); key_bindings_unref_table(table);
} }
TAILQ_FOREACH(c, &clients, entry) {
if (c->keytable == table)
server_client_set_key_table(c, NULL);
}
} }
void void
@@ -157,13 +162,13 @@ key_bindings_init(void)
"bind ! break-pane", "bind ! break-pane",
"bind '\"' split-window", "bind '\"' split-window",
"bind '#' list-buffers", "bind '#' list-buffers",
"bind '$' command-prompt -I'#S' \"rename-session '%%'\"", "bind '$' command-prompt -I'#S' \"rename-session -- '%%'\"",
"bind % split-window -h", "bind % split-window -h",
"bind & confirm-before -p\"kill-window #W? (y/n)\" kill-window", "bind & confirm-before -p\"kill-window #W? (y/n)\" kill-window",
"bind \"'\" command-prompt -pindex \"select-window -t ':%%'\"", "bind \"'\" command-prompt -pindex \"select-window -t ':%%'\"",
"bind ( switch-client -p", "bind ( switch-client -p",
"bind ) switch-client -n", "bind ) switch-client -n",
"bind , command-prompt -I'#W' \"rename-window '%%'\"", "bind , command-prompt -I'#W' \"rename-window -- '%%'\"",
"bind - delete-buffer", "bind - delete-buffer",
"bind . command-prompt \"move-window -t '%%'\"", "bind . command-prompt \"move-window -t '%%'\"",
"bind 0 select-window -t:=0", "bind 0 select-window -t:=0",
@@ -178,16 +183,17 @@ key_bindings_init(void)
"bind 9 select-window -t:=9", "bind 9 select-window -t:=9",
"bind : command-prompt", "bind : command-prompt",
"bind \\; last-pane", "bind \\; last-pane",
"bind = choose-buffer", "bind = choose-buffer -Z",
"bind ? list-keys", "bind ? list-keys",
"bind D choose-client", "bind D choose-client -Z",
"bind E select-layout -E",
"bind L switch-client -l", "bind L switch-client -l",
"bind M select-pane -M", "bind M select-pane -M",
"bind [ copy-mode", "bind [ copy-mode",
"bind ] paste-buffer", "bind ] paste-buffer",
"bind c new-window", "bind c new-window",
"bind d detach-client", "bind d detach-client",
"bind f command-prompt \"find-window '%%'\"", "bind f command-prompt \"find-window -- '%%'\"",
"bind i display-message", "bind i display-message",
"bind l last-window", "bind l last-window",
"bind m select-pane -m", "bind m select-pane -m",
@@ -196,9 +202,9 @@ key_bindings_init(void)
"bind p previous-window", "bind p previous-window",
"bind q display-panes", "bind q display-panes",
"bind r refresh-client", "bind r refresh-client",
"bind s choose-tree", "bind s choose-tree -Zs",
"bind t clock-mode", "bind t clock-mode",
"bind w choose-window", "bind w choose-tree -Zw",
"bind x confirm-before -p\"kill-pane #P? (y/n)\" kill-pane", "bind x confirm-before -p\"kill-pane #P? (y/n)\" kill-pane",
"bind z resize-pane -Z", "bind z resize-pane -Z",
"bind { swap-pane -U", "bind { swap-pane -U",
@@ -244,23 +250,23 @@ key_bindings_init(void)
"bind -Tcopy-mode C-k send -X copy-end-of-line", "bind -Tcopy-mode C-k send -X copy-end-of-line",
"bind -Tcopy-mode C-n send -X cursor-down", "bind -Tcopy-mode C-n send -X cursor-down",
"bind -Tcopy-mode C-p send -X cursor-up", "bind -Tcopy-mode C-p send -X cursor-up",
"bind -Tcopy-mode C-r command-prompt -ip'search up' 'send -X search-backward-incremental \"%%%\"'", "bind -Tcopy-mode C-r command-prompt -ip'(search up)' -I'#{pane_search_string}' 'send -X search-backward-incremental \"%%%\"'",
"bind -Tcopy-mode C-s command-prompt -ip'search down' 'send -X search-forward-incremental \"%%%\"'", "bind -Tcopy-mode C-s command-prompt -ip'(search down)' -I'#{pane_search_string}' 'send -X search-forward-incremental \"%%%\"'",
"bind -Tcopy-mode C-v send -X page-down", "bind -Tcopy-mode C-v send -X page-down",
"bind -Tcopy-mode C-w send -X copy-selection-and-cancel", "bind -Tcopy-mode C-w send -X copy-selection-and-cancel",
"bind -Tcopy-mode Escape send -X cancel", "bind -Tcopy-mode Escape send -X cancel",
"bind -Tcopy-mode Space send -X page-down", "bind -Tcopy-mode Space send -X page-down",
"bind -Tcopy-mode , send -X jump-reverse", "bind -Tcopy-mode , send -X jump-reverse",
"bind -Tcopy-mode \\; send -X jump-again", "bind -Tcopy-mode \\; send -X jump-again",
"bind -Tcopy-mode F command-prompt -1p'jump backward' 'send -X jump-backward \"%%%\"'", "bind -Tcopy-mode F command-prompt -1p'(jump backward)' 'send -X jump-backward \"%%%\"'",
"bind -Tcopy-mode N send -X search-reverse", "bind -Tcopy-mode N send -X search-reverse",
"bind -Tcopy-mode R send -X rectangle-toggle", "bind -Tcopy-mode R send -X rectangle-toggle",
"bind -Tcopy-mode T command-prompt -1p'jump to backward' 'send -X jump-to-backward \"%%%\"'", "bind -Tcopy-mode T command-prompt -1p'(jump to backward)' 'send -X jump-to-backward \"%%%\"'",
"bind -Tcopy-mode f command-prompt -1p'jump forward' 'send -X jump-forward \"%%%\"'", "bind -Tcopy-mode f command-prompt -1p'(jump forward)' 'send -X jump-forward \"%%%\"'",
"bind -Tcopy-mode g command-prompt -p'goto line' 'send -X goto-line \"%%%\"'", "bind -Tcopy-mode g command-prompt -p'(goto line)' 'send -X goto-line \"%%%\"'",
"bind -Tcopy-mode n send -X search-again", "bind -Tcopy-mode n send -X search-again",
"bind -Tcopy-mode q send -X cancel", "bind -Tcopy-mode q send -X cancel",
"bind -Tcopy-mode t command-prompt -1p'jump to forward' 'send -X jump-to-forward \"%%%\"'", "bind -Tcopy-mode t command-prompt -1p'(jump to forward)' 'send -X jump-to-forward \"%%%\"'",
"bind -Tcopy-mode Home send -X start-of-line", "bind -Tcopy-mode Home send -X start-of-line",
"bind -Tcopy-mode End send -X end-of-line", "bind -Tcopy-mode End send -X end-of-line",
"bind -Tcopy-mode MouseDown1Pane select-pane", "bind -Tcopy-mode MouseDown1Pane select-pane",
@@ -276,15 +282,15 @@ key_bindings_init(void)
"bind -Tcopy-mode Down send -X cursor-down", "bind -Tcopy-mode Down send -X cursor-down",
"bind -Tcopy-mode Left send -X cursor-left", "bind -Tcopy-mode Left send -X cursor-left",
"bind -Tcopy-mode Right send -X cursor-right", "bind -Tcopy-mode Right send -X cursor-right",
"bind -Tcopy-mode M-1 command-prompt -Np'repeat' -I1 'send -N \"%%%\"'", "bind -Tcopy-mode M-1 command-prompt -Np'(repeat)' -I1 'send -N \"%%%\"'",
"bind -Tcopy-mode M-2 command-prompt -Np'repeat' -I2 'send -N \"%%%\"'", "bind -Tcopy-mode M-2 command-prompt -Np'(repeat)' -I2 'send -N \"%%%\"'",
"bind -Tcopy-mode M-3 command-prompt -Np'repeat' -I3 'send -N \"%%%\"'", "bind -Tcopy-mode M-3 command-prompt -Np'(repeat)' -I3 'send -N \"%%%\"'",
"bind -Tcopy-mode M-4 command-prompt -Np'repeat' -I4 'send -N \"%%%\"'", "bind -Tcopy-mode M-4 command-prompt -Np'(repeat)' -I4 'send -N \"%%%\"'",
"bind -Tcopy-mode M-5 command-prompt -Np'repeat' -I5 'send -N \"%%%\"'", "bind -Tcopy-mode M-5 command-prompt -Np'(repeat)' -I5 'send -N \"%%%\"'",
"bind -Tcopy-mode M-6 command-prompt -Np'repeat' -I6 'send -N \"%%%\"'", "bind -Tcopy-mode M-6 command-prompt -Np'(repeat)' -I6 'send -N \"%%%\"'",
"bind -Tcopy-mode M-7 command-prompt -Np'repeat' -I7 'send -N \"%%%\"'", "bind -Tcopy-mode M-7 command-prompt -Np'(repeat)' -I7 'send -N \"%%%\"'",
"bind -Tcopy-mode M-8 command-prompt -Np'repeat' -I8 'send -N \"%%%\"'", "bind -Tcopy-mode M-8 command-prompt -Np'(repeat)' -I8 'send -N \"%%%\"'",
"bind -Tcopy-mode M-9 command-prompt -Np'repeat' -I9 'send -N \"%%%\"'", "bind -Tcopy-mode M-9 command-prompt -Np'(repeat)' -I9 'send -N \"%%%\"'",
"bind -Tcopy-mode M-< send -X history-top", "bind -Tcopy-mode M-< send -X history-top",
"bind -Tcopy-mode M-> send -X history-bottom", "bind -Tcopy-mode M-> send -X history-bottom",
"bind -Tcopy-mode M-R send -X top-line", "bind -Tcopy-mode M-R send -X top-line",
@@ -316,25 +322,25 @@ key_bindings_init(void)
"bind -Tcopy-mode-vi Space send -X begin-selection", "bind -Tcopy-mode-vi Space send -X begin-selection",
"bind -Tcopy-mode-vi '$' send -X end-of-line", "bind -Tcopy-mode-vi '$' send -X end-of-line",
"bind -Tcopy-mode-vi , send -X jump-reverse", "bind -Tcopy-mode-vi , send -X jump-reverse",
"bind -Tcopy-mode-vi / command-prompt -p'search down' 'send -X search-forward \"%%%\"'", "bind -Tcopy-mode-vi / command-prompt -p'(search down)' 'send -X search-forward \"%%%\"'",
"bind -Tcopy-mode-vi 0 send -X start-of-line", "bind -Tcopy-mode-vi 0 send -X start-of-line",
"bind -Tcopy-mode-vi 1 command-prompt -Np'repeat' -I1 'send -N \"%%%\"'", "bind -Tcopy-mode-vi 1 command-prompt -Np'(repeat)' -I1 'send -N \"%%%\"'",
"bind -Tcopy-mode-vi 2 command-prompt -Np'repeat' -I2 'send -N \"%%%\"'", "bind -Tcopy-mode-vi 2 command-prompt -Np'(repeat)' -I2 'send -N \"%%%\"'",
"bind -Tcopy-mode-vi 3 command-prompt -Np'repeat' -I3 'send -N \"%%%\"'", "bind -Tcopy-mode-vi 3 command-prompt -Np'(repeat)' -I3 'send -N \"%%%\"'",
"bind -Tcopy-mode-vi 4 command-prompt -Np'repeat' -I4 'send -N \"%%%\"'", "bind -Tcopy-mode-vi 4 command-prompt -Np'(repeat)' -I4 'send -N \"%%%\"'",
"bind -Tcopy-mode-vi 5 command-prompt -Np'repeat' -I5 'send -N \"%%%\"'", "bind -Tcopy-mode-vi 5 command-prompt -Np'(repeat)' -I5 'send -N \"%%%\"'",
"bind -Tcopy-mode-vi 6 command-prompt -Np'repeat' -I6 'send -N \"%%%\"'", "bind -Tcopy-mode-vi 6 command-prompt -Np'(repeat)' -I6 'send -N \"%%%\"'",
"bind -Tcopy-mode-vi 7 command-prompt -Np'repeat' -I7 'send -N \"%%%\"'", "bind -Tcopy-mode-vi 7 command-prompt -Np'(repeat)' -I7 'send -N \"%%%\"'",
"bind -Tcopy-mode-vi 8 command-prompt -Np'repeat' -I8 'send -N \"%%%\"'", "bind -Tcopy-mode-vi 8 command-prompt -Np'(repeat)' -I8 'send -N \"%%%\"'",
"bind -Tcopy-mode-vi 9 command-prompt -Np'repeat' -I9 'send -N \"%%%\"'", "bind -Tcopy-mode-vi 9 command-prompt -Np'(repeat)' -I9 'send -N \"%%%\"'",
"bind -Tcopy-mode-vi : command-prompt -p'goto line' 'send -X goto-line \"%%%\"'", "bind -Tcopy-mode-vi : command-prompt -p'(goto line)' 'send -X goto-line \"%%%\"'",
"bind -Tcopy-mode-vi \\; send -X jump-again", "bind -Tcopy-mode-vi \\; send -X jump-again",
"bind -Tcopy-mode-vi ? command-prompt -p'search up' 'send -X search-backward \"%%%\"'", "bind -Tcopy-mode-vi ? command-prompt -p'(search up)' 'send -X search-backward \"%%%\"'",
"bind -Tcopy-mode-vi A send -X append-selection-and-cancel", "bind -Tcopy-mode-vi A send -X append-selection-and-cancel",
"bind -Tcopy-mode-vi B send -X previous-space", "bind -Tcopy-mode-vi B send -X previous-space",
"bind -Tcopy-mode-vi D send -X copy-end-of-line", "bind -Tcopy-mode-vi D send -X copy-end-of-line",
"bind -Tcopy-mode-vi E send -X next-space-end", "bind -Tcopy-mode-vi E send -X next-space-end",
"bind -Tcopy-mode-vi F command-prompt -1p'jump backward' 'send -X jump-backward \"%%%\"'", "bind -Tcopy-mode-vi F command-prompt -1p'(jump backward)' 'send -X jump-backward \"%%%\"'",
"bind -Tcopy-mode-vi G send -X history-bottom", "bind -Tcopy-mode-vi G send -X history-bottom",
"bind -Tcopy-mode-vi H send -X top-line", "bind -Tcopy-mode-vi H send -X top-line",
"bind -Tcopy-mode-vi J send -X scroll-down", "bind -Tcopy-mode-vi J send -X scroll-down",
@@ -342,13 +348,13 @@ key_bindings_init(void)
"bind -Tcopy-mode-vi L send -X bottom-line", "bind -Tcopy-mode-vi L send -X bottom-line",
"bind -Tcopy-mode-vi M send -X middle-line", "bind -Tcopy-mode-vi M send -X middle-line",
"bind -Tcopy-mode-vi N send -X search-reverse", "bind -Tcopy-mode-vi N send -X search-reverse",
"bind -Tcopy-mode-vi T command-prompt -1p'jump to backward' 'send -X jump-to-backward \"%%%\"'", "bind -Tcopy-mode-vi T command-prompt -1p'(jump to backward)' 'send -X jump-to-backward \"%%%\"'",
"bind -Tcopy-mode-vi V send -X select-line", "bind -Tcopy-mode-vi V send -X select-line",
"bind -Tcopy-mode-vi W send -X next-space", "bind -Tcopy-mode-vi W send -X next-space",
"bind -Tcopy-mode-vi ^ send -X back-to-indentation", "bind -Tcopy-mode-vi ^ send -X back-to-indentation",
"bind -Tcopy-mode-vi b send -X previous-word", "bind -Tcopy-mode-vi b send -X previous-word",
"bind -Tcopy-mode-vi e send -X next-word-end", "bind -Tcopy-mode-vi e send -X next-word-end",
"bind -Tcopy-mode-vi f command-prompt -1p'jump forward' 'send -X jump-forward \"%%%\"'", "bind -Tcopy-mode-vi f command-prompt -1p'(jump forward)' 'send -X jump-forward \"%%%\"'",
"bind -Tcopy-mode-vi g send -X history-top", "bind -Tcopy-mode-vi g send -X history-top",
"bind -Tcopy-mode-vi h send -X cursor-left", "bind -Tcopy-mode-vi h send -X cursor-left",
"bind -Tcopy-mode-vi j send -X cursor-down", "bind -Tcopy-mode-vi j send -X cursor-down",
@@ -357,7 +363,7 @@ key_bindings_init(void)
"bind -Tcopy-mode-vi n send -X search-again", "bind -Tcopy-mode-vi n send -X search-again",
"bind -Tcopy-mode-vi o send -X other-end", "bind -Tcopy-mode-vi o send -X other-end",
"bind -Tcopy-mode-vi q send -X cancel", "bind -Tcopy-mode-vi q send -X cancel",
"bind -Tcopy-mode-vi t command-prompt -1p'jump to forward' 'send -X jump-to-forward \"%%%\"'", "bind -Tcopy-mode-vi t command-prompt -1p'(jump to forward)' 'send -X jump-to-forward \"%%%\"'",
"bind -Tcopy-mode-vi v send -X rectangle-toggle", "bind -Tcopy-mode-vi v send -X rectangle-toggle",
"bind -Tcopy-mode-vi w send -X next-word", "bind -Tcopy-mode-vi w send -X next-word",
"bind -Tcopy-mode-vi { send -X previous-paragraph", "bind -Tcopy-mode-vi { send -X previous-paragraph",
@@ -400,11 +406,11 @@ key_bindings_read_only(struct cmdq_item *item, __unused void *data)
} }
void void
key_bindings_dispatch(struct key_binding *bd, struct client *c, key_bindings_dispatch(struct key_binding *bd, struct cmdq_item *item,
struct mouse_event *m, struct cmd_find_state *fs) struct client *c, struct mouse_event *m, struct cmd_find_state *fs)
{ {
struct cmd *cmd; struct cmd *cmd;
struct cmdq_item *item; struct cmdq_item *new_item;
int readonly; int readonly;
readonly = 1; readonly = 1;
@@ -413,11 +419,14 @@ key_bindings_dispatch(struct key_binding *bd, struct client *c,
readonly = 0; readonly = 0;
} }
if (!readonly && (c->flags & CLIENT_READONLY)) if (!readonly && (c->flags & CLIENT_READONLY))
cmdq_append(c, cmdq_get_callback(key_bindings_read_only, NULL)); new_item = cmdq_get_callback(key_bindings_read_only, NULL);
else { else {
item = cmdq_get_command(bd->cmdlist, fs, m, 0); new_item = cmdq_get_command(bd->cmdlist, fs, m, 0);
if (bd->flags & KEY_BINDING_REPEAT) if (bd->flags & KEY_BINDING_REPEAT)
item->shared->flags |= CMDQ_SHARED_REPEAT; new_item->shared->flags |= CMDQ_SHARED_REPEAT;
cmdq_append(c, item);
} }
if (item != NULL)
cmdq_insert_after(item, new_item);
else
cmdq_append(c, new_item);
} }

View File

@@ -110,12 +110,16 @@ static const struct {
static key_code static key_code
key_string_search_table(const char *string) key_string_search_table(const char *string)
{ {
u_int i; u_int i, user;
for (i = 0; i < nitems(key_string_table); i++) { for (i = 0; i < nitems(key_string_table); i++) {
if (strcasecmp(string, key_string_table[i].string) == 0) if (strcasecmp(string, key_string_table[i].string) == 0)
return (key_string_table[i].key); return (key_string_table[i].key);
} }
if (sscanf(string, "User%u", &user) == 1 && user < KEYC_NUSER)
return (KEYC_USER + user);
return (KEYC_UNKNOWN); return (KEYC_UNKNOWN);
} }
@@ -251,6 +255,10 @@ key_string_lookup_key(key_code key)
return ("FocusIn"); return ("FocusIn");
if (key == KEYC_FOCUS_OUT) if (key == KEYC_FOCUS_OUT)
return ("FocusOut"); return ("FocusOut");
if (key == KEYC_PASTE_START)
return ("PasteStart");
if (key == KEYC_PASTE_END)
return ("PasteEnd");
if (key == KEYC_MOUSE) if (key == KEYC_MOUSE)
return ("Mouse"); return ("Mouse");
if (key == KEYC_DRAGGING) if (key == KEYC_DRAGGING)
@@ -261,6 +269,10 @@ key_string_lookup_key(key_code key)
return ("MouseMoveStatus"); return ("MouseMoveStatus");
if (key == KEYC_MOUSEMOVE_BORDER) if (key == KEYC_MOUSEMOVE_BORDER)
return ("MouseMoveBorder"); return ("MouseMoveBorder");
if (key >= KEYC_USER && key < KEYC_USER + KEYC_NUSER) {
snprintf(out, sizeof out, "User%u", (u_int)(key - KEYC_USER));
return (out);
}
/* /*
* Special case: display C-@ as C-Space. Could do this below in * Special case: display C-@ as C-Space. Could do this below in

View File

@@ -115,11 +115,11 @@ layout_set_previous(struct window *w)
} }
static void static void
layout_set_even_h(struct window *w) layout_set_even(struct window *w, enum layout_type type)
{ {
struct window_pane *wp; struct window_pane *wp;
struct layout_cell *lc, *lcnew; struct layout_cell *lc, *lcnew;
u_int i, n, width, xoff; u_int n;
layout_print_cell(w->layout_root, __func__, 1); layout_print_cell(w->layout_root, __func__, 1);
@@ -128,36 +128,23 @@ layout_set_even_h(struct window *w)
if (n <= 1) if (n <= 1)
return; return;
/* How many can we fit? */
width = (w->sx - (n - 1)) / n;
if (width < PANE_MINIMUM)
width = PANE_MINIMUM;
/* Free the old root and construct a new. */ /* Free the old root and construct a new. */
layout_free(w); layout_free(w);
lc = w->layout_root = layout_create_cell(NULL); lc = w->layout_root = layout_create_cell(NULL);
layout_set_size(lc, w->sx, w->sy, 0, 0); layout_set_size(lc, w->sx, w->sy, 0, 0);
layout_make_node(lc, LAYOUT_LEFTRIGHT); layout_make_node(lc, type);
/* Build new leaf cells. */ /* Build new leaf cells. */
i = xoff = 0;
TAILQ_FOREACH(wp, &w->panes, entry) { TAILQ_FOREACH(wp, &w->panes, entry) {
/* Create child cell. */
lcnew = layout_create_cell(lc); lcnew = layout_create_cell(lc);
layout_set_size(lcnew, width, w->sy, xoff, 0);
layout_make_leaf(lcnew, wp); layout_make_leaf(lcnew, wp);
lcnew->sx = w->sx;
lcnew->sy = w->sy;
TAILQ_INSERT_TAIL(&lc->cells, lcnew, entry); TAILQ_INSERT_TAIL(&lc->cells, lcnew, entry);
i++;
xoff += width + 1;
} }
/* Allocate any remaining space. */ /* Spread out cells. */
if (w->sx > xoff - 1) { layout_spread_cell(w, lc);
lc = TAILQ_LAST(&lc->cells, layout_cells);
layout_resize_adjust(w, lc, LAYOUT_LEFTRIGHT,
w->sx - (xoff - 1));
}
/* Fix cell offsets. */ /* Fix cell offsets. */
layout_fix_offsets(lc); layout_fix_offsets(lc);
@@ -165,61 +152,20 @@ layout_set_even_h(struct window *w)
layout_print_cell(w->layout_root, __func__, 1); layout_print_cell(w->layout_root, __func__, 1);
notify_window("window-layout-changed", w);
server_redraw_window(w); server_redraw_window(w);
} }
static void
layout_set_even_h(struct window *w)
{
layout_set_even(w, LAYOUT_LEFTRIGHT);
}
static void static void
layout_set_even_v(struct window *w) layout_set_even_v(struct window *w)
{ {
struct window_pane *wp; layout_set_even(w, LAYOUT_TOPBOTTOM);
struct layout_cell *lc, *lcnew;
u_int i, n, height, yoff;
layout_print_cell(w->layout_root, __func__, 1);
/* Get number of panes. */
n = window_count_panes(w);
if (n <= 1)
return;
/* How many can we fit? */
height = (w->sy - (n - 1)) / n;
if (height < PANE_MINIMUM)
height = PANE_MINIMUM;
/* Free the old root and construct a new. */
layout_free(w);
lc = w->layout_root = layout_create_cell(NULL);
layout_set_size(lc, w->sx, w->sy, 0, 0);
layout_make_node(lc, LAYOUT_TOPBOTTOM);
/* Build new leaf cells. */
i = yoff = 0;
TAILQ_FOREACH(wp, &w->panes, entry) {
/* Create child cell. */
lcnew = layout_create_cell(lc);
layout_set_size(lcnew, w->sx, height, 0, yoff);
layout_make_leaf(lcnew, wp);
TAILQ_INSERT_TAIL(&lc->cells, lcnew, entry);
i++;
yoff += height + 1;
}
/* Allocate any remaining space. */
if (w->sy > yoff - 1) {
lc = TAILQ_LAST(&lc->cells, layout_cells);
layout_resize_adjust(w, lc, LAYOUT_TOPBOTTOM,
w->sy - (yoff - 1));
}
/* Fix cell offsets. */
layout_fix_offsets(lc);
layout_fix_panes(w, w->sx, w->sy);
layout_print_cell(w->layout_root, __func__, 1);
server_redraw_window(w);
} }
static void static void
@@ -342,6 +288,7 @@ layout_set_main_h(struct window *w)
layout_print_cell(w->layout_root, __func__, 1); layout_print_cell(w->layout_root, __func__, 1);
notify_window("window-layout-changed", w);
server_redraw_window(w); server_redraw_window(w);
} }
@@ -465,6 +412,7 @@ layout_set_main_v(struct window *w)
layout_print_cell(w->layout_root, __func__, 1); layout_print_cell(w->layout_root, __func__, 1);
notify_window("window-layout-changed", w);
server_redraw_window(w); server_redraw_window(w);
} }
@@ -567,5 +515,6 @@ layout_set_tiled(struct window *w)
layout_print_cell(w->layout_root, __func__, 1); layout_print_cell(w->layout_root, __func__, 1);
notify_window("window-layout-changed", w);
server_redraw_window(w); server_redraw_window(w);
} }

View File

@@ -97,9 +97,24 @@ void
layout_print_cell(struct layout_cell *lc, const char *hdr, u_int n) layout_print_cell(struct layout_cell *lc, const char *hdr, u_int n)
{ {
struct layout_cell *lcchild; struct layout_cell *lcchild;
const char *type;
log_debug("%s:%*s%p type %u [parent %p] wp=%p [%u,%u %ux%u]", hdr, n, switch (lc->type) {
" ", lc, lc->type, lc->parent, lc->wp, lc->xoff, lc->yoff, lc->sx, case LAYOUT_LEFTRIGHT:
type = "LEFTRIGHT";
break;
case LAYOUT_TOPBOTTOM:
type = "TOPBOTTOM";
break;
case LAYOUT_WINDOWPANE:
type = "WINDOWPANE";
break;
default:
type = "UNKNOWN";
break;
}
log_debug("%s:%*s%p type %s [parent %p] wp=%p [%u,%u %ux%u]", hdr, n,
" ", lc, type, lc->parent, lc->wp, lc->xoff, lc->yoff, lc->sx,
lc->sy); lc->sy);
switch (lc->type) { switch (lc->type) {
case LAYOUT_LEFTRIGHT: case LAYOUT_LEFTRIGHT:
@@ -983,3 +998,61 @@ layout_close_pane(struct window_pane *wp)
} }
notify_window("window-layout-changed", w); notify_window("window-layout-changed", w);
} }
int
layout_spread_cell(struct window *w, struct layout_cell *parent)
{
struct layout_cell *lc;
u_int number, each, size;
int change, changed;
number = 0;
TAILQ_FOREACH (lc, &parent->cells, entry)
number++;
if (number <= 1)
return (0);
if (parent->type == LAYOUT_LEFTRIGHT)
size = parent->sx;
else if (parent->type == LAYOUT_TOPBOTTOM)
size = parent->sy;
else
return (0);
each = (size - (number - 1)) / number;
changed = 0;
TAILQ_FOREACH (lc, &parent->cells, entry) {
if (TAILQ_NEXT(lc, entry) == NULL)
each = size - ((each + 1) * (number - 1));
change = 0;
if (parent->type == LAYOUT_LEFTRIGHT) {
change = each - (int)lc->sx;
layout_resize_adjust(w, lc, LAYOUT_LEFTRIGHT, change);
} else if (parent->type == LAYOUT_TOPBOTTOM) {
change = each - (int)lc->sy;
layout_resize_adjust(w, lc, LAYOUT_TOPBOTTOM, change);
}
if (change != 0)
changed = 1;
}
return (changed);
}
void
layout_spread_out(struct window_pane *wp)
{
struct layout_cell *parent;
struct window *w = wp->window;
parent = wp->layout_cell->parent;
if (parent == NULL)
return;
do {
if (layout_spread_cell(w, parent)) {
layout_fix_offsets(parent);
layout_fix_panes(w, w->sx, w->sy);
break;
}
} while ((parent = parent->parent) != NULL);
}

21
log.c
View File

@@ -61,12 +61,10 @@ log_open(const char *name)
if (log_level == 0) if (log_level == 0)
return; return;
log_close();
if (log_file != NULL)
fclose(log_file);
xasprintf(&path, "tmux-%s-%ld.log", name, (long)getpid()); xasprintf(&path, "tmux-%s-%ld.log", name, (long)getpid());
log_file = fopen(path, "w"); log_file = fopen(path, "a");
free(path); free(path);
if (log_file == NULL) if (log_file == NULL)
return; return;
@@ -75,6 +73,21 @@ log_open(const char *name)
event_set_log_callback(log_event_cb); event_set_log_callback(log_event_cb);
} }
/* Toggle logging. */
void
log_toggle(const char *name)
{
if (log_level == 0) {
log_level = 1;
log_open(name);
log_debug("log opened");
} else {
log_debug("log closed");
log_level = 0;
log_close();
}
}
/* Close logging. */ /* Close logging. */
void void
log_close(void) log_close(void)

975
mode-tree.c Normal file
View File

@@ -0,0 +1,975 @@
/* $OpenBSD$ */
/*
* Copyright (c) 2017 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 <ctype.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "tmux.h"
struct mode_tree_item;
TAILQ_HEAD(mode_tree_list, mode_tree_item);
struct mode_tree_data {
int dead;
u_int references;
int zoomed;
struct window_pane *wp;
void *modedata;
const char **sort_list;
u_int sort_size;
u_int sort_type;
mode_tree_build_cb buildcb;
mode_tree_draw_cb drawcb;
mode_tree_search_cb searchcb;
struct mode_tree_list children;
struct mode_tree_list saved;
struct mode_tree_line *line_list;
u_int line_size;
u_int depth;
u_int width;
u_int height;
u_int offset;
u_int current;
struct screen screen;
int preview;
char *search;
char *filter;
int no_matches;
};
struct mode_tree_item {
struct mode_tree_item *parent;
void *itemdata;
u_int line;
uint64_t tag;
const char *name;
const char *text;
int expanded;
int tagged;
struct mode_tree_list children;
TAILQ_ENTRY(mode_tree_item) entry;
};
struct mode_tree_line {
struct mode_tree_item *item;
u_int depth;
int last;
int flat;
};
static void mode_tree_free_items(struct mode_tree_list *);
static struct mode_tree_item *
mode_tree_find_item(struct mode_tree_list *mtl, uint64_t tag)
{
struct mode_tree_item *mti, *child;
TAILQ_FOREACH(mti, mtl, entry) {
if (mti->tag == tag)
return (mti);
child = mode_tree_find_item(&mti->children, tag);
if (child != NULL)
return (child);
}
return (NULL);
}
static void
mode_tree_free_item(struct mode_tree_item *mti)
{
mode_tree_free_items(&mti->children);
free((void *)mti->name);
free((void *)mti->text);
free(mti);
}
static void
mode_tree_free_items(struct mode_tree_list *mtl)
{
struct mode_tree_item *mti, *mti1;
TAILQ_FOREACH_SAFE(mti, mtl, entry, mti1) {
TAILQ_REMOVE(mtl, mti, entry);
mode_tree_free_item(mti);
}
}
static void
mode_tree_check_selected(struct mode_tree_data *mtd)
{
/*
* If the current line would now be off screen reset the offset to the
* last visible line.
*/
if (mtd->current > mtd->height - 1)
mtd->offset = mtd->current - mtd->height + 1;
}
static void
mode_tree_clear_lines(struct mode_tree_data *mtd)
{
free(mtd->line_list);
mtd->line_list = NULL;
mtd->line_size = 0;
}
static void
mode_tree_build_lines(struct mode_tree_data *mtd,
struct mode_tree_list *mtl, u_int depth)
{
struct mode_tree_item *mti;
struct mode_tree_line *line;
u_int i;
int flat = 1;
mtd->depth = depth;
TAILQ_FOREACH(mti, mtl, entry) {
mtd->line_list = xreallocarray(mtd->line_list,
mtd->line_size + 1, sizeof *mtd->line_list);
line = &mtd->line_list[mtd->line_size++];
line->item = mti;
line->depth = depth;
line->last = (mti == TAILQ_LAST(mtl, mode_tree_list));
mti->line = (mtd->line_size - 1);
if (!TAILQ_EMPTY(&mti->children))
flat = 0;
if (mti->expanded)
mode_tree_build_lines(mtd, &mti->children, depth + 1);
}
TAILQ_FOREACH(mti, mtl, entry) {
for (i = 0; i < mtd->line_size; i++) {
line = &mtd->line_list[i];
if (line->item == mti)
line->flat = flat;
}
}
}
static void
mode_tree_clear_tagged(struct mode_tree_list *mtl)
{
struct mode_tree_item *mti;
TAILQ_FOREACH(mti, mtl, entry) {
mti->tagged = 0;
mode_tree_clear_tagged(&mti->children);
}
}
void
mode_tree_up(struct mode_tree_data *mtd, int wrap)
{
if (mtd->current == 0) {
if (wrap) {
mtd->current = mtd->line_size - 1;
if (mtd->line_size >= mtd->height)
mtd->offset = mtd->line_size - mtd->height;
}
} else {
mtd->current--;
if (mtd->current < mtd->offset)
mtd->offset--;
}
}
void
mode_tree_down(struct mode_tree_data *mtd, int wrap)
{
if (mtd->current == mtd->line_size - 1) {
if (wrap) {
mtd->current = 0;
mtd->offset = 0;
}
} else {
mtd->current++;
if (mtd->current > mtd->offset + mtd->height - 1)
mtd->offset++;
}
}
void *
mode_tree_get_current(struct mode_tree_data *mtd)
{
return (mtd->line_list[mtd->current].item->itemdata);
}
void
mode_tree_expand_current(struct mode_tree_data *mtd)
{
if (!mtd->line_list[mtd->current].item->expanded) {
mtd->line_list[mtd->current].item->expanded = 1;
mode_tree_build(mtd);
}
}
void
mode_tree_set_current(struct mode_tree_data *mtd, uint64_t tag)
{
u_int i;
for (i = 0; i < mtd->line_size; i++) {
if (mtd->line_list[i].item->tag == tag)
break;
}
if (i != mtd->line_size) {
mtd->current = i;
if (mtd->current > mtd->height - 1)
mtd->offset = mtd->current - mtd->height + 1;
else
mtd->offset = 0;
} else {
mtd->current = 0;
mtd->offset = 0;
}
}
u_int
mode_tree_count_tagged(struct mode_tree_data *mtd)
{
struct mode_tree_item *mti;
u_int i, tagged;
tagged = 0;
for (i = 0; i < mtd->line_size; i++) {
mti = mtd->line_list[i].item;
if (mti->tagged)
tagged++;
}
return (tagged);
}
void
mode_tree_each_tagged(struct mode_tree_data *mtd, mode_tree_each_cb cb,
struct client *c, key_code key, int current)
{
struct mode_tree_item *mti;
u_int i;
int fired;
fired = 0;
for (i = 0; i < mtd->line_size; i++) {
mti = mtd->line_list[i].item;
if (mti->tagged) {
fired = 1;
cb(mtd->modedata, mti->itemdata, c, key);
}
}
if (!fired && current) {
mti = mtd->line_list[mtd->current].item;
cb(mtd->modedata, mti->itemdata, c, key);
}
}
struct mode_tree_data *
mode_tree_start(struct window_pane *wp, struct args *args,
mode_tree_build_cb buildcb, mode_tree_draw_cb drawcb,
mode_tree_search_cb searchcb, void *modedata, const char **sort_list,
u_int sort_size, struct screen **s)
{
struct mode_tree_data *mtd;
const char *sort;
u_int i;
mtd = xcalloc(1, sizeof *mtd);
mtd->references = 1;
mtd->wp = wp;
mtd->modedata = modedata;
mtd->sort_list = sort_list;
mtd->sort_size = sort_size;
mtd->sort_type = 0;
mtd->preview = !args_has(args, 'N');
sort = args_get(args, 'O');
if (sort != NULL) {
for (i = 0; i < sort_size; i++) {
if (strcasecmp(sort, sort_list[i]) == 0)
mtd->sort_type = i;
}
}
if (args_has(args, 'f'))
mtd->filter = xstrdup(args_get(args, 'f'));
else
mtd->filter = NULL;
mtd->buildcb = buildcb;
mtd->drawcb = drawcb;
mtd->searchcb = searchcb;
TAILQ_INIT(&mtd->children);
*s = &mtd->screen;
screen_init(*s, screen_size_x(&wp->base), screen_size_y(&wp->base), 0);
(*s)->mode &= ~MODE_CURSOR;
return (mtd);
}
void
mode_tree_zoom(struct mode_tree_data *mtd, struct args *args)
{
struct window_pane *wp = mtd->wp;
if (args_has(args, 'Z')) {
mtd->zoomed = (wp->window->flags & WINDOW_ZOOMED);
if (!mtd->zoomed && window_zoom(wp) == 0)
server_redraw_window(wp->window);
} else
mtd->zoomed = -1;
}
void
mode_tree_build(struct mode_tree_data *mtd)
{
struct screen *s = &mtd->screen;
uint64_t tag;
if (mtd->line_list != NULL)
tag = mtd->line_list[mtd->current].item->tag;
else
tag = 0;
TAILQ_CONCAT(&mtd->saved, &mtd->children, entry);
TAILQ_INIT(&mtd->children);
mtd->buildcb(mtd->modedata, mtd->sort_type, &tag, mtd->filter);
mtd->no_matches = TAILQ_EMPTY(&mtd->children);
if (mtd->no_matches)
mtd->buildcb(mtd->modedata, mtd->sort_type, &tag, NULL);
mode_tree_free_items(&mtd->saved);
TAILQ_INIT(&mtd->saved);
mode_tree_clear_lines(mtd);
mode_tree_build_lines(mtd, &mtd->children, 0);
mode_tree_set_current(mtd, tag);
mtd->width = screen_size_x(s);
if (mtd->preview) {
mtd->height = (screen_size_y(s) / 3) * 2;
if (mtd->height > mtd->line_size)
mtd->height = screen_size_y(s) / 2;
if (mtd->height < 10)
mtd->height = screen_size_y(s);
if (screen_size_y(s) - mtd->height < 2)
mtd->height = screen_size_y(s);
} else
mtd->height = screen_size_y(s);
mode_tree_check_selected(mtd);
}
static void
mode_tree_remove_ref(struct mode_tree_data *mtd)
{
if (--mtd->references == 0)
free(mtd);
}
void
mode_tree_free(struct mode_tree_data *mtd)
{
struct window_pane *wp = mtd->wp;
if (mtd->zoomed == 0)
server_unzoom_window(wp->window);
mode_tree_free_items(&mtd->children);
mode_tree_clear_lines(mtd);
screen_free(&mtd->screen);
free(mtd->search);
free(mtd->filter);
mtd->dead = 1;
mode_tree_remove_ref(mtd);
}
void
mode_tree_resize(struct mode_tree_data *mtd, u_int sx, u_int sy)
{
struct screen *s = &mtd->screen;
screen_resize(s, sx, sy, 0);
mode_tree_build(mtd);
mode_tree_draw(mtd);
mtd->wp->flags |= PANE_REDRAW;
}
struct mode_tree_item *
mode_tree_add(struct mode_tree_data *mtd, struct mode_tree_item *parent,
void *itemdata, uint64_t tag, const char *name, const char *text,
int expanded)
{
struct mode_tree_item *mti, *saved;
log_debug("%s: %llu, %s %s", __func__, (unsigned long long)tag,
name, text);
mti = xcalloc(1, sizeof *mti);
mti->parent = parent;
mti->itemdata = itemdata;
mti->tag = tag;
mti->name = xstrdup(name);
mti->text = xstrdup(text);
saved = mode_tree_find_item(&mtd->saved, tag);
if (saved != NULL) {
if (parent == NULL || (parent != NULL && parent->expanded))
mti->tagged = saved->tagged;
mti->expanded = saved->expanded;
} else if (expanded == -1)
mti->expanded = 1;
else
mti->expanded = expanded;
TAILQ_INIT(&mti->children);
if (parent != NULL)
TAILQ_INSERT_TAIL(&parent->children, mti, entry);
else
TAILQ_INSERT_TAIL(&mtd->children, mti, entry);
return (mti);
}
void
mode_tree_remove(struct mode_tree_data *mtd, struct mode_tree_item *mti)
{
struct mode_tree_item *parent = mti->parent;
if (parent != NULL)
TAILQ_REMOVE(&parent->children, mti, entry);
else
TAILQ_REMOVE(&mtd->children, mti, entry);
mode_tree_free_item(mti);
}
void
mode_tree_draw(struct mode_tree_data *mtd)
{
struct window_pane *wp = mtd->wp;
struct screen *s = &mtd->screen;
struct mode_tree_line *line;
struct mode_tree_item *mti;
struct options *oo = wp->window->options;
struct screen_write_ctx ctx;
struct grid_cell gc0, gc;
u_int w, h, i, j, sy, box_x, box_y;
char *text, *start, key[7];
const char *tag, *symbol;
size_t size, n;
int keylen;
if (mtd->line_size == 0)
return;
memcpy(&gc0, &grid_default_cell, sizeof gc0);
memcpy(&gc, &grid_default_cell, sizeof gc);
style_apply(&gc, oo, "mode-style");
w = mtd->width;
h = mtd->height;
screen_write_start(&ctx, NULL, s);
screen_write_clearscreen(&ctx, 8);
if (mtd->line_size > 10)
keylen = 6;
else
keylen = 4;
for (i = 0; i < mtd->line_size; i++) {
if (i < mtd->offset)
continue;
if (i > mtd->offset + h - 1)
break;
line = &mtd->line_list[i];
mti = line->item;
screen_write_cursormove(&ctx, 0, i - mtd->offset);
if (i < 10)
snprintf(key, sizeof key, "(%c) ", '0' + i);
else if (i < 36)
snprintf(key, sizeof key, "(M-%c)", 'a' + (i - 10));
else
*key = '\0';
if (line->flat)
symbol = "";
else if (TAILQ_EMPTY(&mti->children))
symbol = " ";
else if (mti->expanded)
symbol = "- ";
else
symbol = "+ ";
if (line->depth == 0)
start = xstrdup(symbol);
else {
size = (4 * line->depth) + 32;
start = xcalloc(1, size);
for (j = 1; j < line->depth; j++) {
if (mti->parent != NULL &&
mtd->line_list[mti->parent->line].last)
strlcat(start, " ", size);
else
strlcat(start, "\001x\001 ", size);
}
if (line->last)
strlcat(start, "\001mq\001> ", size);
else
strlcat(start, "\001tq\001> ", size);
strlcat(start, symbol, size);
}
if (mti->tagged)
tag = "*";
else
tag = "";
xasprintf(&text, "%-*s%s%s%s: %s", keylen, key, start,
mti->name, tag, mti->text);
free(start);
if (mti->tagged) {
gc.attr ^= GRID_ATTR_BRIGHT;
gc0.attr ^= GRID_ATTR_BRIGHT;
}
if (i != mtd->current) {
screen_write_nputs(&ctx, w, &gc0, "%s", text);
screen_write_clearendofline(&ctx, 8);
} else {
screen_write_nputs(&ctx, w, &gc, "%s", text);
screen_write_clearendofline(&ctx, gc.bg);
}
free(text);
if (mti->tagged) {
gc.attr ^= GRID_ATTR_BRIGHT;
gc0.attr ^= GRID_ATTR_BRIGHT;
}
}
sy = screen_size_y(s);
if (!mtd->preview || sy <= 4 || h <= 4 || sy - h <= 4 || w <= 4) {
screen_write_stop(&ctx);
return;
}
line = &mtd->line_list[mtd->current];
mti = line->item;
screen_write_cursormove(&ctx, 0, h);
screen_write_box(&ctx, w, sy - h);
xasprintf(&text, " %s (sort: %s)", mti->name,
mtd->sort_list[mtd->sort_type]);
if (w - 2 >= strlen(text)) {
screen_write_cursormove(&ctx, 1, h);
screen_write_puts(&ctx, &gc0, "%s", text);
if (mtd->no_matches)
n = (sizeof "no matches") - 1;
else
n = (sizeof "active") - 1;
if (mtd->filter != NULL && w - 2 >= strlen(text) + 10 + n + 2) {
screen_write_puts(&ctx, &gc0, " (filter: ");
if (mtd->no_matches)
screen_write_puts(&ctx, &gc, "no matches");
else
screen_write_puts(&ctx, &gc0, "active");
screen_write_puts(&ctx, &gc0, ") ");
}
}
free(text);
box_x = w - 4;
box_y = sy - h - 2;
if (box_x != 0 && box_y != 0) {
screen_write_cursormove(&ctx, 2, h + 1);
mtd->drawcb(mtd->modedata, mti->itemdata, &ctx, box_x, box_y);
}
screen_write_stop(&ctx);
}
static struct mode_tree_item *
mode_tree_search_for(struct mode_tree_data *mtd)
{
struct mode_tree_item *mti, *last, *next;
if (mtd->search == NULL)
return (NULL);
mti = last = mtd->line_list[mtd->current].item;
for (;;) {
if (!TAILQ_EMPTY(&mti->children))
mti = TAILQ_FIRST(&mti->children);
else if ((next = TAILQ_NEXT(mti, entry)) != NULL)
mti = next;
else {
for (;;) {
mti = mti->parent;
if (mti == NULL)
break;
if ((next = TAILQ_NEXT(mti, entry)) != NULL) {
mti = next;
break;
}
}
}
if (mti == NULL)
mti = TAILQ_FIRST(&mtd->children);
if (mti == last)
break;
if (mtd->searchcb == NULL) {
if (strstr(mti->name, mtd->search) != NULL)
return (mti);
continue;
}
if (mtd->searchcb(mtd->modedata, mti->itemdata, mtd->search))
return (mti);
}
return (NULL);
}
static void
mode_tree_search_set(struct mode_tree_data *mtd)
{
struct mode_tree_item *mti, *loop;
uint64_t tag;
mti = mode_tree_search_for(mtd);
if (mti == NULL)
return;
tag = mti->tag;
loop = mti->parent;
while (loop != NULL) {
loop->expanded = 1;
loop = loop->parent;
}
mode_tree_build(mtd);
mode_tree_set_current(mtd, tag);
mode_tree_draw(mtd);
mtd->wp->flags |= PANE_REDRAW;
}
static int
mode_tree_search_callback(__unused struct client *c, void *data, const char *s,
__unused int done)
{
struct mode_tree_data *mtd = data;
if (mtd->dead)
return (0);
free(mtd->search);
if (s == NULL || *s == '\0') {
mtd->search = NULL;
return (0);
}
mtd->search = xstrdup(s);
mode_tree_search_set(mtd);
return (0);
}
static void
mode_tree_search_free(void *data)
{
mode_tree_remove_ref(data);
}
static int
mode_tree_filter_callback(__unused struct client *c, void *data, const char *s,
__unused int done)
{
struct mode_tree_data *mtd = data;
if (mtd->dead)
return (0);
if (mtd->filter != NULL)
free(mtd->filter);
if (s == NULL || *s == '\0')
mtd->filter = NULL;
else
mtd->filter = xstrdup(s);
mode_tree_build(mtd);
mode_tree_draw(mtd);
mtd->wp->flags |= PANE_REDRAW;
return (0);
}
static void
mode_tree_filter_free(void *data)
{
mode_tree_remove_ref(data);
}
int
mode_tree_key(struct mode_tree_data *mtd, struct client *c, key_code *key,
struct mouse_event *m, u_int *xp, u_int *yp)
{
struct mode_tree_line *line;
struct mode_tree_item *current, *parent;
u_int i, x, y;
int choice;
key_code tmp;
if (KEYC_IS_MOUSE(*key)) {
if (cmd_mouse_at(mtd->wp, m, &x, &y, 0) != 0) {
*key = KEYC_NONE;
return (0);
}
if (xp != NULL)
*xp = x;
if (yp != NULL)
*yp = y;
if (x > mtd->width || y > mtd->height) {
if (!mtd->preview)
*key = KEYC_NONE;
return (0);
}
if (mtd->offset + y < mtd->line_size) {
if (*key == KEYC_MOUSEDOWN1_PANE ||
*key == KEYC_DOUBLECLICK1_PANE)
mtd->current = mtd->offset + y;
if (*key == KEYC_DOUBLECLICK1_PANE)
*key = '\r';
else
*key = KEYC_NONE;
} else
*key = KEYC_NONE;
return (0);
}
line = &mtd->line_list[mtd->current];
current = line->item;
choice = -1;
if (*key >= '0' && *key <= '9')
choice = (*key) - '0';
else if (((*key) & KEYC_MASK_MOD) == KEYC_ESCAPE) {
tmp = (*key) & KEYC_MASK_KEY;
if (tmp >= 'a' && tmp <= 'z')
choice = 10 + (tmp - 'a');
}
if (choice != -1) {
if ((u_int)choice > mtd->line_size - 1) {
*key = KEYC_NONE;
return (0);
}
mtd->current = choice;
*key = '\r';
return (0);
}
switch (*key) {
case 'q':
case '\033': /* Escape */
case '\007': /* C-g */
return (1);
case KEYC_UP:
case 'k':
case KEYC_WHEELUP_PANE:
case '\020': /* C-p */
mode_tree_up(mtd, 1);
break;
case KEYC_DOWN:
case 'j':
case KEYC_WHEELDOWN_PANE:
case '\016': /* C-n */
mode_tree_down(mtd, 1);
break;
case KEYC_PPAGE:
case '\002': /* C-b */
for (i = 0; i < mtd->height; i++) {
if (mtd->current == 0)
break;
mode_tree_up(mtd, 1);
}
break;
case KEYC_NPAGE:
case '\006': /* C-f */
for (i = 0; i < mtd->height; i++) {
if (mtd->current == mtd->line_size - 1)
break;
mode_tree_down(mtd, 1);
}
break;
case KEYC_HOME:
mtd->current = 0;
mtd->offset = 0;
break;
case KEYC_END:
mtd->current = mtd->line_size - 1;
if (mtd->current > mtd->height - 1)
mtd->offset = mtd->current - mtd->height + 1;
else
mtd->offset = 0;
break;
case 't':
/*
* Do not allow parents and children to both be tagged: untag
* all parents and children of current.
*/
if (!current->tagged) {
parent = current->parent;
while (parent != NULL) {
parent->tagged = 0;
parent = parent->parent;
}
mode_tree_clear_tagged(&current->children);
current->tagged = 1;
} else
current->tagged = 0;
mode_tree_down(mtd, 0);
break;
case 'T':
for (i = 0; i < mtd->line_size; i++)
mtd->line_list[i].item->tagged = 0;
break;
case '\024': /* C-t */
for (i = 0; i < mtd->line_size; i++) {
if (mtd->line_list[i].item->parent == NULL)
mtd->line_list[i].item->tagged = 1;
else
mtd->line_list[i].item->tagged = 0;
}
break;
case 'O':
mtd->sort_type++;
if (mtd->sort_type == mtd->sort_size)
mtd->sort_type = 0;
mode_tree_build(mtd);
break;
case KEYC_LEFT:
case 'h':
case '-':
if (line->flat || !current->expanded)
current = current->parent;
if (current == NULL)
mode_tree_up(mtd, 0);
else {
current->expanded = 0;
mtd->current = current->line;
mode_tree_build(mtd);
}
break;
case KEYC_RIGHT:
case 'l':
case '+':
if (line->flat || current->expanded)
mode_tree_down(mtd, 0);
else if (!line->flat) {
current->expanded = 1;
mode_tree_build(mtd);
}
break;
case '\023': /* C-s */
mtd->references++;
status_prompt_set(c, "(search) ", "",
mode_tree_search_callback, mode_tree_search_free, mtd,
PROMPT_NOFORMAT);
break;
case 'n':
mode_tree_search_set(mtd);
break;
case 'f':
mtd->references++;
status_prompt_set(c, "(filter) ", mtd->filter,
mode_tree_filter_callback, mode_tree_filter_free, mtd,
PROMPT_NOFORMAT);
break;
case 'v':
mtd->preview = !mtd->preview;
mode_tree_build(mtd);
if (mtd->preview)
mode_tree_check_selected(mtd);
break;
}
return (0);
}
void
mode_tree_run_command(struct client *c, struct cmd_find_state *fs,
const char *template, const char *name)
{
struct cmdq_item *new_item;
struct cmd_list *cmdlist;
char *command, *cause;
command = cmd_template_replace(template, name, 1);
if (command == NULL || *command == '\0') {
free(command);
return;
}
cmdlist = cmd_string_parse(command, NULL, 0, &cause);
if (cmdlist == NULL) {
if (cause != NULL && c != NULL) {
*cause = toupper((u_char)*cause);
status_message_set(c, "%s", cause);
}
free(cause);
} else {
new_item = cmdq_get_command(cmdlist, fs, NULL, 0);
cmdq_append(c, new_item);
cmd_list_free(cmdlist);
}
free(command);
}

View File

@@ -151,7 +151,9 @@ parse_window_name(const char *in)
if (*name != '\0') { if (*name != '\0') {
ptr = name + strlen(name) - 1; ptr = name + strlen(name) - 1;
while (ptr > name && !isalnum((u_char)*ptr)) while (ptr > name &&
!isalnum((u_char)*ptr) &&
!ispunct((u_char)*ptr))
*ptr-- = '\0'; *ptr-- = '\0';
} }

View File

@@ -45,7 +45,7 @@ notify_hook(struct cmdq_item *item, struct notify_entry *ne)
cmd_find_clear_state(&fs, 0); cmd_find_clear_state(&fs, 0);
if (cmd_find_empty_state(&ne->fs) || !cmd_find_valid_state(&ne->fs)) if (cmd_find_empty_state(&ne->fs) || !cmd_find_valid_state(&ne->fs))
cmd_find_from_nothing(&fs); cmd_find_from_nothing(&fs, 0);
else else
cmd_find_copy_state(&fs, &ne->fs); cmd_find_copy_state(&fs, &ne->fs);
@@ -169,7 +169,7 @@ notify_client(const char *name, struct client *c)
{ {
struct cmd_find_state fs; struct cmd_find_state fs;
cmd_find_from_client(&fs, c); cmd_find_from_client(&fs, c, 0);
notify_add(name, &fs, c, NULL, NULL, NULL); notify_add(name, &fs, c, NULL, NULL, NULL);
} }
@@ -179,9 +179,9 @@ notify_session(const char *name, struct session *s)
struct cmd_find_state fs; struct cmd_find_state fs;
if (session_alive(s)) if (session_alive(s))
cmd_find_from_session(&fs, s); cmd_find_from_session(&fs, s, 0);
else else
cmd_find_from_nothing(&fs); cmd_find_from_nothing(&fs, 0);
notify_add(name, &fs, NULL, s, NULL, NULL); notify_add(name, &fs, NULL, s, NULL, NULL);
} }
@@ -190,7 +190,7 @@ notify_winlink(const char *name, struct winlink *wl)
{ {
struct cmd_find_state fs; struct cmd_find_state fs;
cmd_find_from_winlink(&fs, wl); cmd_find_from_winlink(&fs, wl, 0);
notify_add(name, &fs, NULL, wl->session, wl->window, NULL); notify_add(name, &fs, NULL, wl->session, wl->window, NULL);
} }
@@ -199,7 +199,7 @@ notify_session_window(const char *name, struct session *s, struct window *w)
{ {
struct cmd_find_state fs; struct cmd_find_state fs;
cmd_find_from_session_window(&fs, s, w); cmd_find_from_session_window(&fs, s, w, 0);
notify_add(name, &fs, NULL, s, w, NULL); notify_add(name, &fs, NULL, s, w, NULL);
} }
@@ -208,7 +208,7 @@ notify_window(const char *name, struct window *w)
{ {
struct cmd_find_state fs; struct cmd_find_state fs;
cmd_find_from_window(&fs, w); cmd_find_from_window(&fs, w, 0);
notify_add(name, &fs, NULL, NULL, w, NULL); notify_add(name, &fs, NULL, NULL, w, NULL);
} }
@@ -217,6 +217,6 @@ notify_pane(const char *name, struct window_pane *wp)
{ {
struct cmd_find_state fs; struct cmd_find_state fs;
cmd_find_from_pane(&fs, wp); cmd_find_from_pane(&fs, wp, 0);
notify_add(name, &fs, NULL, NULL, NULL, wp); notify_add(name, &fs, NULL, NULL, NULL, wp);
} }

View File

@@ -50,9 +50,15 @@ static const char *options_table_status_position_list[] = {
static const char *options_table_bell_action_list[] = { static const char *options_table_bell_action_list[] = {
"none", "any", "current", "other", NULL "none", "any", "current", "other", NULL
}; };
static const char *options_table_visual_bell_list[] = {
"off", "on", "both", NULL
};
static const char *options_table_pane_status_list[] = { static const char *options_table_pane_status_list[] = {
"off", "top", "bottom", NULL "off", "top", "bottom", NULL
}; };
static const char *options_table_set_clipboard_list[] = {
"off", "external", "on", NULL
};
/* Top-level options. */ /* Top-level options. */
const struct options_table_entry options_table[] = { const struct options_table_entry options_table[] = {
@@ -61,7 +67,7 @@ const struct options_table_entry options_table[] = {
.scope = OPTIONS_TABLE_SERVER, .scope = OPTIONS_TABLE_SERVER,
.minimum = 1, .minimum = 1,
.maximum = INT_MAX, .maximum = INT_MAX,
.default_num = 20 .default_num = 50
}, },
{ .name = "command-alias", { .name = "command-alias",
@@ -70,7 +76,9 @@ const struct options_table_entry options_table[] = {
.default_str = "split-pane=split-window," .default_str = "split-pane=split-window,"
"splitp=split-window," "splitp=split-window,"
"server-info=show-messages -JT," "server-info=show-messages -JT,"
"info=show-messages -JT", "info=show-messages -JT,"
"choose-window=choose-tree -w,"
"choose-session=choose-tree -s",
.separator = "," .separator = ","
}, },
@@ -88,6 +96,12 @@ const struct options_table_entry options_table[] = {
.default_num = 500 .default_num = 500
}, },
{ .name = "exit-empty",
.type = OPTIONS_TABLE_FLAG,
.scope = OPTIONS_TABLE_SERVER,
.default_num = 1
},
{ .name = "exit-unattached", { .name = "exit-unattached",
.type = OPTIONS_TABLE_FLAG, .type = OPTIONS_TABLE_FLAG,
.scope = OPTIONS_TABLE_SERVER, .scope = OPTIONS_TABLE_SERVER,
@@ -115,8 +129,9 @@ const struct options_table_entry options_table[] = {
}, },
{ .name = "set-clipboard", { .name = "set-clipboard",
.type = OPTIONS_TABLE_FLAG, .type = OPTIONS_TABLE_CHOICE,
.scope = OPTIONS_TABLE_SERVER, .scope = OPTIONS_TABLE_SERVER,
.choices = options_table_set_clipboard_list,
.default_num = 1 .default_num = 1
}, },
@@ -124,11 +139,25 @@ const struct options_table_entry options_table[] = {
.type = OPTIONS_TABLE_ARRAY, .type = OPTIONS_TABLE_ARRAY,
.scope = OPTIONS_TABLE_SERVER, .scope = OPTIONS_TABLE_SERVER,
.default_str = "xterm*:XT:Ms=\\E]52;%p1%s;%p2%s\\007" .default_str = "xterm*:XT:Ms=\\E]52;%p1%s;%p2%s\\007"
":Cs=\\E]12;%p1%s\\007:Cr=\\E]112\\007" ":Cs=\\E]12;%p1%s\\007:Cr=\\E]112\\007"
":Ss=\\E[%p1%d q:Se=\\E[2 q,screen*:XT", ":Ss=\\E[%p1%d q:Se=\\E[2 q,screen*:XT",
.separator = "," .separator = ","
}, },
{ .name = "user-keys",
.type = OPTIONS_TABLE_ARRAY,
.scope = OPTIONS_TABLE_SERVER,
.default_str = "",
.separator = ","
},
{ .name = "activity-action",
.type = OPTIONS_TABLE_CHOICE,
.scope = OPTIONS_TABLE_SESSION,
.choices = options_table_bell_action_list,
.default_num = ALERT_OTHER
},
{ .name = "assume-paste-time", { .name = "assume-paste-time",
.type = OPTIONS_TABLE_NUMBER, .type = OPTIONS_TABLE_NUMBER,
.scope = OPTIONS_TABLE_SESSION, .scope = OPTIONS_TABLE_SESSION,
@@ -149,13 +178,7 @@ const struct options_table_entry options_table[] = {
.type = OPTIONS_TABLE_CHOICE, .type = OPTIONS_TABLE_CHOICE,
.scope = OPTIONS_TABLE_SESSION, .scope = OPTIONS_TABLE_SESSION,
.choices = options_table_bell_action_list, .choices = options_table_bell_action_list,
.default_num = BELL_ANY .default_num = ALERT_ANY
},
{ .name = "bell-on-alert",
.type = OPTIONS_TABLE_FLAG,
.scope = OPTIONS_TABLE_SESSION,
.default_num = 0
}, },
{ .name = "default-command", { .name = "default-command",
@@ -336,6 +359,13 @@ const struct options_table_entry options_table[] = {
.default_str = "#S:#I:#W - \"#T\" #{session_alerts}" .default_str = "#S:#I:#W - \"#T\" #{session_alerts}"
}, },
{ .name = "silence-action",
.type = OPTIONS_TABLE_CHOICE,
.scope = OPTIONS_TABLE_SESSION,
.choices = options_table_bell_action_list,
.default_num = ALERT_OTHER
},
{ .name = "status", { .name = "status",
.type = OPTIONS_TABLE_FLAG, .type = OPTIONS_TABLE_FLAG,
.scope = OPTIONS_TABLE_SESSION, .scope = OPTIONS_TABLE_SESSION,
@@ -484,25 +514,28 @@ const struct options_table_entry options_table[] = {
.type = OPTIONS_TABLE_ARRAY, .type = OPTIONS_TABLE_ARRAY,
.scope = OPTIONS_TABLE_SESSION, .scope = OPTIONS_TABLE_SESSION,
.default_str = "DISPLAY SSH_ASKPASS SSH_AUTH_SOCK SSH_AGENT_PID " .default_str = "DISPLAY SSH_ASKPASS SSH_AUTH_SOCK SSH_AGENT_PID "
"SSH_CONNECTION WINDOWID XAUTHORITY" "SSH_CONNECTION WINDOWID XAUTHORITY"
}, },
{ .name = "visual-activity", { .name = "visual-activity",
.type = OPTIONS_TABLE_FLAG, .type = OPTIONS_TABLE_CHOICE,
.scope = OPTIONS_TABLE_SESSION, .scope = OPTIONS_TABLE_SESSION,
.default_num = 0 .choices = options_table_visual_bell_list,
.default_num = VISUAL_OFF
}, },
{ .name = "visual-bell", { .name = "visual-bell",
.type = OPTIONS_TABLE_FLAG, .type = OPTIONS_TABLE_CHOICE,
.scope = OPTIONS_TABLE_SESSION, .scope = OPTIONS_TABLE_SESSION,
.default_num = 0 .choices = options_table_visual_bell_list,
.default_num = VISUAL_OFF
}, },
{ .name = "visual-silence", { .name = "visual-silence",
.type = OPTIONS_TABLE_FLAG, .type = OPTIONS_TABLE_CHOICE,
.scope = OPTIONS_TABLE_SESSION, .scope = OPTIONS_TABLE_SESSION,
.default_num = 0 .choices = options_table_visual_bell_list,
.default_num = VISUAL_OFF
}, },
{ .name = "word-separators", { .name = "word-separators",
@@ -520,7 +553,7 @@ 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,
.default_num = 1 .default_num = 0
}, },
{ .name = "alternate-screen", { .name = "alternate-screen",
@@ -539,7 +572,7 @@ const struct options_table_entry options_table[] = {
.type = OPTIONS_TABLE_STRING, .type = OPTIONS_TABLE_STRING,
.scope = OPTIONS_TABLE_WINDOW, .scope = OPTIONS_TABLE_WINDOW,
.default_str = "#{?pane_in_mode,[tmux],#{pane_current_command}}" .default_str = "#{?pane_in_mode,[tmux],#{pane_current_command}}"
"#{?pane_dead,[dead],}" "#{?pane_dead,[dead],}"
}, },
{ .name = "clock-mode-colour", { .name = "clock-mode-colour",
@@ -627,6 +660,12 @@ const struct options_table_entry options_table[] = {
.default_num = 0 .default_num = 0
}, },
{ .name = "monitor-bell",
.type = OPTIONS_TABLE_FLAG,
.scope = OPTIONS_TABLE_WINDOW,
.default_num = 1
},
{ .name = "monitor-silence", { .name = "monitor-silence",
.type = OPTIONS_TABLE_NUMBER, .type = OPTIONS_TABLE_NUMBER,
.scope = OPTIONS_TABLE_WINDOW, .scope = OPTIONS_TABLE_WINDOW,
@@ -697,7 +736,7 @@ const struct options_table_entry options_table[] = {
.type = OPTIONS_TABLE_STRING, .type = OPTIONS_TABLE_STRING,
.scope = OPTIONS_TABLE_WINDOW, .scope = OPTIONS_TABLE_WINDOW,
.default_str = "#{?pane_active,#[reverse],}#{pane_index}#[default] " .default_str = "#{?pane_active,#[reverse],}#{pane_index}#[default] "
"\"#{pane_title}\"" "\"#{pane_title}\""
}, },
{ .name = "pane-border-status", { .name = "pane-border-status",

View File

@@ -114,7 +114,7 @@ options_free(struct options *oo)
{ {
struct options_entry *o, *tmp; struct options_entry *o, *tmp;
RB_FOREACH_SAFE (o, options_tree, &oo->tree, tmp) RB_FOREACH_SAFE(o, options_tree, &oo->tree, tmp)
options_remove(o); options_remove(o);
free(oo); free(oo);
} }
@@ -478,7 +478,6 @@ options_match_get(struct options *oo, const char *s, int *idx, int only,
return (o); return (o);
} }
const char * const char *
options_get_string(struct options *oo, const char *name) options_get_string(struct options *oo, const char *name)
{ {

View File

@@ -88,11 +88,17 @@ osdep_get_cwd(int fd)
struct event_base * struct event_base *
osdep_event_init(void) osdep_event_init(void)
{ {
struct event_base *base;
/* /*
* On OS X, kqueue and poll are both completely broken and don't * On OS X, kqueue and poll are both completely broken and don't
* work on anything except socket file descriptors (yes, really). * work on anything except socket file descriptors (yes, really).
*/ */
setenv("EVENT_NOKQUEUE", "1", 1); setenv("EVENT_NOKQUEUE", "1", 1);
setenv("EVENT_NOPOLL", "1", 1); setenv("EVENT_NOPOLL", "1", 1);
return (event_init());
base = event_init();
unsetenv("EVENT_NOKQUEUE");
unsetenv("EVENT_NOPOLL");
return (base);
} }

View File

@@ -193,10 +193,15 @@ osdep_get_cwd(int fd)
struct event_base * struct event_base *
osdep_event_init(void) osdep_event_init(void)
{ {
struct event_base *base;
/* /*
* On some versions of FreeBSD, kqueue doesn't work properly on tty * On some versions of FreeBSD, kqueue doesn't work properly on tty
* file descriptors. This is fixed in recent FreeBSD versions. * file descriptors. This is fixed in recent FreeBSD versions.
*/ */
setenv("EVENT_NOKQUEUE", "1", 1); setenv("EVENT_NOKQUEUE", "1", 1);
return (event_init());
base = event_init();
unsetenv("EVENT_NOKQUEUE");
return (base);
} }

View File

@@ -92,7 +92,12 @@ osdep_get_cwd(int fd)
struct event_base * struct event_base *
osdep_event_init(void) osdep_event_init(void)
{ {
struct event_base *base;
/* On Linux, epoll doesn't work on /dev/null (yes, really). */ /* On Linux, epoll doesn't work on /dev/null (yes, really). */
setenv("EVENT_NOEPOLL", "1", 1); setenv("EVENT_NOEPOLL", "1", 1);
return (event_init());
base = event_init();
unsetenv("EVENT_NOEPOLL");
return (base);
} }

111
proc.c
View File

@@ -22,6 +22,7 @@
#include <errno.h> #include <errno.h>
#include <event.h> #include <event.h>
#include <signal.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#include <unistd.h> #include <unistd.h>
@@ -33,6 +34,14 @@ struct tmuxproc {
int exit; int exit;
void (*signalcb)(int); void (*signalcb)(int);
struct event ev_sighup;
struct event ev_sigchld;
struct event ev_sigcont;
struct event ev_sigterm;
struct event ev_sigusr1;
struct event ev_sigusr2;
struct event ev_sigwinch;
}; };
struct tmuxpeer { struct tmuxpeer {
@@ -45,7 +54,7 @@ struct tmuxpeer {
#define PEER_BAD 0x1 #define PEER_BAD 0x1
void (*dispatchcb)(struct imsg *, void *); void (*dispatchcb)(struct imsg *, void *);
void *arg; void *arg;
}; };
static int peer_check_version(struct tmuxpeer *, struct imsg *); static int peer_check_version(struct tmuxpeer *, struct imsg *);
@@ -159,36 +168,12 @@ proc_send(struct tmuxpeer *peer, enum msgtype type, int fd, const void *buf,
return (0); return (0);
} }
int
proc_send_s(struct tmuxpeer *peer, enum msgtype type, const char *s)
{
return (proc_send(peer, type, -1, s, strlen(s) + 1));
}
struct tmuxproc * struct tmuxproc *
proc_start(const char *name, struct event_base *base, int forkflag, proc_start(const char *name)
void (*signalcb)(int))
{ {
struct tmuxproc *tp; struct tmuxproc *tp;
struct utsname u; struct utsname u;
if (forkflag) {
switch (fork()) {
case -1:
fatal("fork failed");
case 0:
break;
default:
return (NULL);
}
if (daemon(1, 0) != 0)
fatal("daemon failed");
clear_signals(0);
if (event_reinit(base) != 0)
fatalx("event_reinit failed");
}
log_open(name); log_open(name);
setproctitle("%s (%s)", name, socket_path); setproctitle("%s (%s)", name, socket_path);
@@ -203,9 +188,6 @@ proc_start(const char *name, struct event_base *base, int forkflag,
tp = xcalloc(1, sizeof *tp); tp = xcalloc(1, sizeof *tp);
tp->name = xstrdup(name); tp->name = xstrdup(name);
tp->signalcb = signalcb;
set_signals(proc_signal_cb, tp);
return (tp); return (tp);
} }
@@ -225,6 +207,71 @@ proc_exit(struct tmuxproc *tp)
tp->exit = 1; tp->exit = 1;
} }
void
proc_set_signals(struct tmuxproc *tp, void (*signalcb)(int))
{
struct sigaction sa;
tp->signalcb = signalcb;
memset(&sa, 0, sizeof sa);
sigemptyset(&sa.sa_mask);
sa.sa_flags = SA_RESTART;
sa.sa_handler = SIG_IGN;
sigaction(SIGINT, &sa, NULL);
sigaction(SIGPIPE, &sa, NULL);
sigaction(SIGTSTP, &sa, NULL);
signal_set(&tp->ev_sighup, SIGHUP, proc_signal_cb, tp);
signal_add(&tp->ev_sighup, NULL);
signal_set(&tp->ev_sigchld, SIGCHLD, proc_signal_cb, tp);
signal_add(&tp->ev_sigchld, NULL);
signal_set(&tp->ev_sigcont, SIGCONT, proc_signal_cb, tp);
signal_add(&tp->ev_sigcont, NULL);
signal_set(&tp->ev_sigterm, SIGTERM, proc_signal_cb, tp);
signal_add(&tp->ev_sigterm, NULL);
signal_set(&tp->ev_sigusr1, SIGUSR1, proc_signal_cb, tp);
signal_add(&tp->ev_sigusr1, NULL);
signal_set(&tp->ev_sigusr2, SIGUSR2, proc_signal_cb, tp);
signal_add(&tp->ev_sigusr2, NULL);
signal_set(&tp->ev_sigwinch, SIGWINCH, proc_signal_cb, tp);
signal_add(&tp->ev_sigwinch, NULL);
}
void
proc_clear_signals(struct tmuxproc *tp, int defaults)
{
struct sigaction sa;
memset(&sa, 0, sizeof sa);
sigemptyset(&sa.sa_mask);
sa.sa_flags = SA_RESTART;
sa.sa_handler = SIG_DFL;
sigaction(SIGINT, &sa, NULL);
sigaction(SIGPIPE, &sa, NULL);
sigaction(SIGTSTP, &sa, NULL);
signal_del(&tp->ev_sighup);
signal_del(&tp->ev_sigchld);
signal_del(&tp->ev_sigcont);
signal_del(&tp->ev_sigterm);
signal_del(&tp->ev_sigusr1);
signal_del(&tp->ev_sigusr2);
signal_del(&tp->ev_sigwinch);
if (defaults) {
sigaction(SIGHUP, &sa, NULL);
sigaction(SIGCHLD, &sa, NULL);
sigaction(SIGCONT, &sa, NULL);
sigaction(SIGTERM, &sa, NULL);
sigaction(SIGUSR1, &sa, NULL);
sigaction(SIGUSR2, &sa, NULL);
sigaction(SIGWINCH, &sa, NULL);
}
}
struct tmuxpeer * struct tmuxpeer *
proc_add_peer(struct tmuxproc *tp, int fd, proc_add_peer(struct tmuxproc *tp, int fd,
void (*dispatchcb)(struct imsg *, void *), void *arg) void (*dispatchcb)(struct imsg *, void *), void *arg)
@@ -263,3 +310,9 @@ proc_kill_peer(struct tmuxpeer *peer)
{ {
peer->flags |= PEER_BAD; peer->flags |= PEER_BAD;
} }
void
proc_toggle_log(struct tmuxproc *tp)
{
log_toggle(tp->name);
}

View File

@@ -0,0 +1,49 @@
#!/bin/sh
# 947
# size in control mode should change after refresh-client -C, and -x and -y
# should work without -d for control clients
PATH=/bin:/usr/bin
TERM=screen
[ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux)
TMUX="$TEST_TMUX -Ltest"
$TMUX kill-server 2>/dev/null
TMP=$(mktemp)
OUT=$(mktemp)
#trap "rm -f $TMP $OUT" 0 1 15
$TMUX -f/dev/null new -d || exit 1
sleep 1
cat <<EOF|$TMUX -C a >$TMP
ls -F':#{session_width} #{session_height}'
refresh -C 100,50
ls -F':#{session_width} #{session_height}'
EOF
grep ^: $TMP >$OUT
printf ":80 24\n:100 50\n"|cmp -s $OUT - || exit 1
$TMUX kill-server 2>/dev/null
$TMUX -f/dev/null new -d || exit 1
sleep 1
cat <<EOF|$TMUX -C a >$TMP
ls -F':#{session_width} #{session_height}'
refresh -C 80,24
ls -F':#{session_width} #{session_height}'
EOF
grep ^: $TMP >$OUT
printf ":80 24\n:80 24\n"|cmp -s $OUT - || exit 1
$TMUX kill-server 2>/dev/null
cat <<EOF|$TMUX -C new -x 100 -y 50 >$TMP
ls -F':#{session_width} #{session_height}'
refresh -C 80,24
ls -F':#{session_width} #{session_height}'
EOF
grep ^: $TMP >$OUT
printf ":100 50\n:80 24\n"|cmp -s $OUT - || exit 1
$TMUX kill-server 2>/dev/null
exit 0

View File

@@ -0,0 +1,19 @@
#!/bin/sh
# 971
# has-session should return 1 on error
PATH=/bin:/usr/bin
TERM=screen
[ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux)
TMUX="$TEST_TMUX -Ltest"
$TMUX kill-server 2>/dev/null
$TMUX -f/dev/null has -tfoo </dev/null 2>/dev/null && exit 1
$TMUX -f/dev/null start\; has -tfoo </dev/null 2>/dev/null && exit 1
$TMUX -f/dev/null new -d\; has -tfoo </dev/null 2>/dev/null && exit 1
$TMUX -f/dev/null new -dsfoo\; has -tfoo </dev/null 2>/dev/null || exit 1
$TMUX kill-server 2>/dev/null
exit 0

View File

@@ -0,0 +1,27 @@
#!/bin/sh
# new-session without clients should be the right size
PATH=/bin:/usr/bin
TERM=screen
[ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux)
TMUX="$TEST_TMUX -Ltest"
$TMUX kill-server 2>/dev/null
TMP=$(mktemp)
trap "rm -f $TMP" 0 1 15
$TMUX -f/dev/null new -d </dev/null || exit 1
sleep 1
$TMUX ls -F "#{session_width} #{session_height}" >$TMP
printf "80 24\n"|cmp -s $TMP - || exit 1
$TMUX kill-server 2>/dev/null
$TMUX -f/dev/null new -d -x 100 -y 50 </dev/null || exit 1
sleep 1
$TMUX ls -F "#{session_width} #{session_height}" >$TMP
printf "100 50\n"|cmp -s $TMP - || exit 1
$TMUX kill-server 2>/dev/null
exit 0

View File

@@ -49,24 +49,31 @@ recalculate_sizes(void)
struct client *c; struct client *c;
struct window *w; struct window *w;
struct window_pane *wp; struct window_pane *wp;
u_int ssx, ssy, has, limit; u_int ssx, ssy, has, limit, lines;
int flag, has_status, is_zoomed, forced; int flag, is_zoomed, forced;
RB_FOREACH(s, sessions, &sessions) { RB_FOREACH(s, sessions, &sessions) {
has_status = options_get_number(s->options, "status"); lines = status_line_size(s);
s->attached = 0; s->attached = 0;
ssx = ssy = UINT_MAX; ssx = ssy = UINT_MAX;
TAILQ_FOREACH(c, &clients, entry) { TAILQ_FOREACH(c, &clients, entry) {
if (c->flags & CLIENT_SUSPENDED) if (c->flags & CLIENT_SUSPENDED)
continue; continue;
if ((c->flags & (CLIENT_CONTROL|CLIENT_SIZECHANGED)) ==
CLIENT_CONTROL)
continue;
if (c->session == s) { if (c->session == s) {
if (c->tty.sx < ssx) if (c->tty.sx < ssx)
ssx = c->tty.sx; ssx = c->tty.sx;
if (has_status && c->flags &= ~CLIENT_STATUSOFF;
if (lines != 0 && lines + PANE_MINIMUM > c->tty.sy)
c->flags |= CLIENT_STATUSOFF;
if ((~c->flags & CLIENT_STATUSOFF) &&
!(c->flags & CLIENT_CONTROL) && !(c->flags & CLIENT_CONTROL) &&
c->tty.sy > 1 && c->tty.sy - 1 < ssy) c->tty.sy > lines &&
ssy = c->tty.sy - 1; c->tty.sy - lines < ssy)
ssy = c->tty.sy - lines;
else if (c->tty.sy < ssy) else if (c->tty.sy < ssy)
ssy = c->tty.sy; ssy = c->tty.sy;
s->attached++; s->attached++;
@@ -78,8 +85,8 @@ recalculate_sizes(void)
} }
s->flags &= ~SESSION_UNATTACHED; s->flags &= ~SESSION_UNATTACHED;
if (has_status && ssy == 0) if (lines != 0 && ssy == 0)
ssy = 1; ssy = lines;
if (s->sx == ssx && s->sy == ssy) if (s->sx == ssx && s->sy == ssy)
continue; continue;
@@ -156,6 +163,8 @@ recalculate_sizes(void)
if (w->active == wp) if (w->active == wp)
break; break;
} }
if (w->active == w->last)
w->last = NULL;
server_redraw_window(w); server_redraw_window(w);
notify_window("window-layout-changed", w); notify_window("window-layout-changed", w);

View File

@@ -18,6 +18,7 @@
#include <sys/types.h> #include <sys/types.h>
#include <stdlib.h>
#include <string.h> #include <string.h>
#include "tmux.h" #include "tmux.h"
@@ -33,11 +34,11 @@ static int screen_redraw_make_pane_status(struct client *, struct window *,
struct window_pane *); struct window_pane *);
static void screen_redraw_draw_pane_status(struct client *, int); static void screen_redraw_draw_pane_status(struct client *, int);
static void screen_redraw_draw_borders(struct client *, int, int, u_int); static void screen_redraw_draw_borders(struct client *, int, u_int, u_int);
static void screen_redraw_draw_panes(struct client *, u_int); static void screen_redraw_draw_panes(struct client *, u_int, u_int);
static void screen_redraw_draw_status(struct client *, u_int); static void screen_redraw_draw_status(struct client *, u_int, u_int);
static void screen_redraw_draw_number(struct client *, struct window_pane *, static void screen_redraw_draw_number(struct client *, struct window_pane *,
u_int); u_int, u_int);
#define CELL_INSIDE 0 #define CELL_INSIDE 0
#define CELL_LEFTRIGHT 1 #define CELL_LEFTRIGHT 1
@@ -299,6 +300,7 @@ screen_redraw_make_pane_status(struct client *c, struct window *w,
screen_write_cnputs(&ctx, outlen, &gc, "%s", out); screen_write_cnputs(&ctx, outlen, &gc, "%s", out);
screen_write_stop(&ctx); screen_write_stop(&ctx);
free(out);
format_free(ft); format_free(ft);
wp->status_size = outlen; wp->status_size = outlen;
@@ -377,36 +379,38 @@ screen_redraw_screen(struct client *c, int draw_panes, int draw_status,
struct tty *tty = &c->tty; struct tty *tty = &c->tty;
struct window *w = c->session->curw->window; struct window *w = c->session->curw->window;
struct options *wo = w->options; struct options *wo = w->options;
u_int top; u_int top, lines;
int status, pane_status, spos; int position, pane_status;
/* Suspended clients should not be updated. */
if (c->flags & CLIENT_SUSPENDED) if (c->flags & CLIENT_SUSPENDED)
return; return;
/* Get status line, er, status. */ if (c->flags & CLIENT_STATUSOFF)
spos = options_get_number(oo, "status-position"); lines = 0;
if (c->message_string != NULL || c->prompt_string != NULL)
status = 1;
else else
status = options_get_number(oo, "status"); lines = status_line_size(c->session);
top = 0; if (c->message_string != NULL || c->prompt_string != NULL)
if (status && spos == 0) lines = (lines == 0) ? 1 : lines;
position = options_get_number(oo, "status-position");
if (lines != 0 && position == 0)
top = 1; top = 1;
if (!status) else
top = 0;
if (lines == 0)
draw_status = 0; draw_status = 0;
/* Draw the elements. */
if (draw_borders) { if (draw_borders) {
pane_status = options_get_number(wo, "pane-border-status"); pane_status = options_get_number(wo, "pane-border-status");
screen_redraw_draw_borders(c, status, pane_status, top); screen_redraw_draw_borders(c, pane_status, lines, top);
if (pane_status != CELL_STATUS_OFF) if (pane_status != CELL_STATUS_OFF)
screen_redraw_draw_pane_status(c, pane_status); screen_redraw_draw_pane_status(c, pane_status);
} }
if (draw_panes) if (draw_panes)
screen_redraw_draw_panes(c, top); screen_redraw_draw_panes(c, lines, top);
if (draw_status) if (draw_status)
screen_redraw_draw_status(c, top); screen_redraw_draw_status(c, lines, top);
tty_reset(tty); tty_reset(tty);
} }
@@ -421,7 +425,7 @@ screen_redraw_pane(struct client *c, struct window_pane *wp)
yoff = wp->yoff; yoff = wp->yoff;
if (status_at_line(c) == 0) if (status_at_line(c) == 0)
yoff++; yoff += status_line_size(c->session);
log_debug("%s: redraw pane %%%u (at %u,%u)", c->name, wp->id, log_debug("%s: redraw pane %%%u (at %u,%u)", c->name, wp->id,
wp->xoff, yoff); wp->xoff, yoff);
@@ -433,7 +437,7 @@ screen_redraw_pane(struct client *c, struct window_pane *wp)
/* Draw the borders. */ /* Draw the borders. */
static void static void
screen_redraw_draw_borders(struct client *c, int status, int pane_status, screen_redraw_draw_borders(struct client *c, int pane_status, u_int lines,
u_int top) u_int top)
{ {
struct session *s = c->session; struct session *s = c->session;
@@ -449,7 +453,7 @@ screen_redraw_draw_borders(struct client *c, int status, int pane_status,
const char *tmp; const char *tmp;
size_t msglen = 0; size_t msglen = 0;
small = (tty->sy - status + top > w->sy) || (tty->sx > w->sx); small = (tty->sy - lines + top > w->sy) || (tty->sx > w->sx);
if (small) { if (small) {
flags = w->flags & (WINDOW_FORCEWIDTH|WINDOW_FORCEHEIGHT); flags = w->flags & (WINDOW_FORCEWIDTH|WINDOW_FORCEHEIGHT);
if (flags == (WINDOW_FORCEWIDTH|WINDOW_FORCEHEIGHT)) if (flags == (WINDOW_FORCEWIDTH|WINDOW_FORCEHEIGHT))
@@ -458,18 +462,20 @@ screen_redraw_draw_borders(struct client *c, int status, int pane_status,
tmp = "force-width"; tmp = "force-width";
else if (flags == WINDOW_FORCEHEIGHT) else if (flags == WINDOW_FORCEHEIGHT)
tmp = "force-height"; tmp = "force-height";
else if (c->flags & CLIENT_STATUSOFF)
tmp = "status line";
else else
tmp = "a smaller client"; tmp = "a smaller client";
xsnprintf(msg, sizeof msg, "(size %ux%u from %s)", xsnprintf(msg, sizeof msg, "(size %ux%u from %s)",
w->sx, w->sy, tmp); w->sx, w->sy, tmp);
msglen = strlen(msg); msglen = strlen(msg);
if (tty->sy - 1 - status + top > w->sy && tty->sx >= msglen) { if (tty->sy - 1 - lines + top > w->sy && tty->sx >= msglen) {
msgx = tty->sx - msglen; msgx = tty->sx - msglen;
msgy = tty->sy - 1 - status + top; msgy = tty->sy - 1 - lines + top;
} else if (tty->sx - w->sx > msglen) { } else if (tty->sx - w->sx > msglen) {
msgx = tty->sx - msglen; msgx = tty->sx - msglen;
msgy = tty->sy - 1 - status + top; msgy = tty->sy - 1 - lines + top;
} else } else
small = 0; small = 0;
} }
@@ -483,7 +489,7 @@ screen_redraw_draw_borders(struct client *c, int status, int pane_status,
memcpy(&m_active_gc, &active_gc, sizeof m_active_gc); memcpy(&m_active_gc, &active_gc, sizeof m_active_gc);
m_active_gc.attr ^= GRID_ATTR_REVERSE; m_active_gc.attr ^= GRID_ATTR_REVERSE;
for (j = 0; j < tty->sy - status; j++) { for (j = 0; j < tty->sy - lines; j++) {
for (i = 0; i < tty->sx; i++) { for (i = 0; i < tty->sx; i++) {
type = screen_redraw_check_cell(c, i, j, pane_status, type = screen_redraw_check_cell(c, i, j, pane_status,
&wp); &wp);
@@ -505,7 +511,10 @@ screen_redraw_draw_borders(struct client *c, int status, int pane_status,
tty_attributes(tty, &active_gc, NULL); tty_attributes(tty, &active_gc, NULL);
else else
tty_attributes(tty, &other_gc, NULL); tty_attributes(tty, &other_gc, NULL);
tty_cursor(tty, i, top + j); if (top)
tty_cursor(tty, i, lines + j);
else
tty_cursor(tty, i, j);
tty_putc(tty, CELL_BORDERS[type]); tty_putc(tty, CELL_BORDERS[type]);
} }
} }
@@ -520,38 +529,47 @@ screen_redraw_draw_borders(struct client *c, int status, int pane_status,
/* Draw the panes. */ /* Draw the panes. */
static void static void
screen_redraw_draw_panes(struct client *c, u_int top) screen_redraw_draw_panes(struct client *c, u_int lines, u_int top)
{ {
struct window *w = c->session->curw->window; struct window *w = c->session->curw->window;
struct tty *tty = &c->tty; struct tty *tty = &c->tty;
struct window_pane *wp; struct window_pane *wp;
u_int i; u_int i, y;
if (top)
y = lines;
else
y = 0;
TAILQ_FOREACH(wp, &w->panes, entry) { TAILQ_FOREACH(wp, &w->panes, entry) {
if (!window_pane_visible(wp)) if (!window_pane_visible(wp))
continue; continue;
for (i = 0; i < wp->sy; i++) for (i = 0; i < wp->sy; i++)
tty_draw_pane(tty, wp, i, wp->xoff, top + wp->yoff); tty_draw_pane(tty, wp, i, wp->xoff, y + wp->yoff);
if (c->flags & CLIENT_IDENTIFY) if (c->flags & CLIENT_IDENTIFY)
screen_redraw_draw_number(c, wp, top); screen_redraw_draw_number(c, wp, lines, top);
} }
} }
/* Draw the status line. */ /* Draw the status line. */
static void static void
screen_redraw_draw_status(struct client *c, u_int top) screen_redraw_draw_status(struct client *c, u_int lines, u_int top)
{ {
struct tty *tty = &c->tty; struct tty *tty = &c->tty;
u_int i, y;
if (top) if (top)
tty_draw_line(tty, NULL, &c->status, 0, 0, 0); y = 0;
else else
tty_draw_line(tty, NULL, &c->status, 0, 0, tty->sy - 1); y = tty->sy - lines;
for (i = 0; i < lines; i++)
tty_draw_line(tty, NULL, &c->status.status, i, 0, y);
} }
/* Draw number on a pane. */ /* Draw number on a pane. */
static void static void
screen_redraw_draw_number(struct client *c, struct window_pane *wp, u_int top) screen_redraw_draw_number(struct client *c, struct window_pane *wp,
u_int lines, u_int top)
{ {
struct tty *tty = &c->tty; struct tty *tty = &c->tty;
struct session *s = c->session; struct session *s = c->session;
@@ -576,7 +594,7 @@ screen_redraw_draw_number(struct client *c, struct window_pane *wp, u_int top)
xoff = wp->xoff; yoff = wp->yoff; xoff = wp->xoff; yoff = wp->yoff;
if (top) if (top)
yoff++; yoff += lines;
if (wp->sx < len * 6 || wp->sy < 5) { if (wp->sx < len * 6 || wp->sy < 5) {
tty_cursor(tty, xoff + px - len / 2, yoff + py); tty_cursor(tty, xoff + px - len / 2, yoff + py);

View File

@@ -48,7 +48,7 @@ struct screen_write_collect_item {
struct grid_cell gc; struct grid_cell gc;
TAILQ_ENTRY (screen_write_collect_item) entry; TAILQ_ENTRY(screen_write_collect_item) entry;
}; };
struct screen_write_collect_line { struct screen_write_collect_line {
TAILQ_HEAD(, screen_write_collect_item) items; TAILQ_HEAD(, screen_write_collect_item) items;
@@ -75,6 +75,9 @@ screen_write_start(struct screen_write_ctx *ctx, struct window_pane *wp,
TAILQ_INIT(&ctx->list[y].items); TAILQ_INIT(&ctx->list[y].items);
ctx->item = xcalloc(1, sizeof *ctx->item); ctx->item = xcalloc(1, sizeof *ctx->item);
ctx->scrolled = 0;
ctx->bg = 8;
if (wp != NULL) if (wp != NULL)
snprintf(tmp, sizeof tmp, "pane %%%u", wp->id); snprintf(tmp, sizeof tmp, "pane %%%u", wp->id);
log_debug("%s: size %ux%u, %s", __func__, screen_size_x(ctx->s), log_debug("%s: size %ux%u, %s", __func__, screen_size_x(ctx->s),
@@ -348,39 +351,218 @@ screen_write_cnputs(struct screen_write_ctx *ctx, ssize_t maxlen,
free(msg); free(msg);
} }
/* Copy from another screen. */ /* Copy from another screen. Assumes target region is big enough. */
void void
screen_write_copy(struct screen_write_ctx *ctx, struct screen *src, u_int px, screen_write_copy(struct screen_write_ctx *ctx, struct screen *src, u_int px,
u_int py, u_int nx, u_int ny, bitstr_t *markbs, u_int py, u_int nx, u_int ny, bitstr_t *mbs, const struct grid_cell *mgc)
const struct grid_cell *markgc)
{ {
struct screen *s = ctx->s; struct screen *s = ctx->s;
struct grid *gd = src->grid; struct grid *gd = src->grid;
struct grid_cell gc; struct grid_cell gc;
u_int xx, yy, cx, cy, b; u_int xx, yy, cx, cy, b;
if (nx == 0 || ny == 0)
return;
cx = s->cx; cx = s->cx;
cy = s->cy; cy = s->cy;
for (yy = py; yy < py + ny; yy++) { for (yy = py; yy < py + ny; yy++) {
for (xx = px; xx < px + nx; xx++) { for (xx = px; xx < px + nx; xx++) {
grid_get_cell(gd, xx, yy, &gc); grid_get_cell(gd, xx, yy, &gc);
if (markbs != NULL) { if (mbs != NULL) {
b = (yy * screen_size_x(src)) + xx; b = (yy * screen_size_x(src)) + xx;
if (bit_test(markbs, b)) { if (bit_test(mbs, b)) {
gc.attr = markgc->attr; gc.attr = mgc->attr;
gc.fg = markgc->fg; gc.fg = mgc->fg;
gc.bg = markgc->bg; gc.bg = mgc->bg;
} }
} }
screen_write_cell(ctx, &gc); if (xx + gc.data.width <= px + nx)
screen_write_cell(ctx, &gc);
} }
cy++; cy++;
screen_write_cursormove(ctx, cx, cy); screen_write_cursormove(ctx, cx, cy);
} }
} }
/*
* Copy from another screen but without the selection stuff. Also assumes the
* target region is already big enough and already cleared.
*/
void
screen_write_fast_copy(struct screen_write_ctx *ctx, struct screen *src,
u_int px, u_int py, u_int nx, u_int ny)
{
struct screen *s = ctx->s;
struct grid *gd = src->grid;
struct grid_cell gc;
u_int xx, yy, cx, cy;
if (nx == 0 || ny == 0)
return;
cy = s->cy;
for (yy = py; yy < py + ny; yy++) {
if (yy >= gd->hsize + gd->sy)
break;
cx = s->cx;
for (xx = px; xx < px + nx; xx++) {
if (xx >= gd->linedata[yy].cellsize)
break;
grid_get_cell(gd, xx, yy, &gc);
if (xx + gc.data.width > px + nx)
break;
if (!grid_cells_equal(&gc, &grid_default_cell))
grid_view_set_cell(ctx->s->grid, cx, cy, &gc);
cx++;
}
cy++;
}
}
/* Draw a horizontal line on screen. */
void
screen_write_hline(struct screen_write_ctx *ctx, u_int nx, int left, int right)
{
struct screen *s = ctx->s;
struct grid_cell gc;
u_int cx, cy, i;
cx = s->cx;
cy = s->cy;
memcpy(&gc, &grid_default_cell, sizeof gc);
gc.attr |= GRID_ATTR_CHARSET;
screen_write_putc(ctx, &gc, left ? 't' : 'q');
for (i = 1; i < nx - 1; i++)
screen_write_putc(ctx, &gc, 'q');
screen_write_putc(ctx, &gc, right ? 'u' : 'q');
screen_write_cursormove(ctx, cx, cy);
}
/* Draw a horizontal line on screen. */
void
screen_write_vline(struct screen_write_ctx *ctx, u_int ny, int top, int bottom)
{
struct screen *s = ctx->s;
struct grid_cell gc;
u_int cx, cy, i;
cx = s->cx;
cy = s->cy;
memcpy(&gc, &grid_default_cell, sizeof gc);
gc.attr |= GRID_ATTR_CHARSET;
screen_write_putc(ctx, &gc, top ? 'w' : 'x');
for (i = 1; i < ny - 1; i++) {
screen_write_cursormove(ctx, cx, cy + i);
screen_write_putc(ctx, &gc, 'x');
}
screen_write_cursormove(ctx, cx, cy + ny - 1);
screen_write_putc(ctx, &gc, bottom ? 'v' : 'x');
screen_write_cursormove(ctx, cx, cy);
}
/* Draw a box on screen. */
void
screen_write_box(struct screen_write_ctx *ctx, u_int nx, u_int ny)
{
struct screen *s = ctx->s;
struct grid_cell gc;
u_int cx, cy, i;
cx = s->cx;
cy = s->cy;
memcpy(&gc, &grid_default_cell, sizeof gc);
gc.attr |= GRID_ATTR_CHARSET;
screen_write_putc(ctx, &gc, 'l');
for (i = 1; i < nx - 1; i++)
screen_write_putc(ctx, &gc, 'q');
screen_write_putc(ctx, &gc, 'k');
screen_write_cursormove(ctx, cx, cy + ny - 1);
screen_write_putc(ctx, &gc, 'm');
for (i = 1; i < nx - 1; i++)
screen_write_putc(ctx, &gc, 'q');
screen_write_putc(ctx, &gc, 'j');
for (i = 1; i < ny - 1; i++) {
screen_write_cursormove(ctx, cx, cy + i);
screen_write_putc(ctx, &gc, 'x');
}
for (i = 1; i < ny - 1; i++) {
screen_write_cursormove(ctx, cx + nx - 1, cy + i);
screen_write_putc(ctx, &gc, 'x');
}
screen_write_cursormove(ctx, cx, cy);
}
/*
* Write a preview version of a window. Assumes target area is big enough and
* already cleared.
*/
void
screen_write_preview(struct screen_write_ctx *ctx, struct screen *src, u_int nx,
u_int ny)
{
struct screen *s = ctx->s;
struct grid_cell gc;
u_int cx, cy, px, py;
cx = s->cx;
cy = s->cy;
/*
* If the cursor is on, pick the area around the cursor, otherwise use
* the top left.
*/
if (src->mode & MODE_CURSOR) {
px = src->cx;
if (px < nx / 3)
px = 0;
else
px = px - nx / 3;
if (px + nx > screen_size_x(src)) {
if (nx > screen_size_x(src))
px = 0;
else
px = screen_size_x(src) - nx;
}
py = src->cy;
if (py < ny / 3)
py = 0;
else
py = py - ny / 3;
if (py + ny > screen_size_y(src)) {
if (ny > screen_size_y(src))
py = 0;
else
py = screen_size_y(src) - ny;
}
} else {
px = 0;
py = 0;
}
screen_write_fast_copy(ctx, src, px, src->grid->hsize + py, nx, ny);
if (src->mode & MODE_CURSOR) {
grid_view_get_cell(src->grid, src->cx, src->cy, &gc);
gc.attr |= GRID_ATTR_REVERSE;
screen_write_cursormove(ctx, cx + (src->cx - px),
cy + (src->cy - py));
screen_write_cell(ctx, &gc);
}
}
/* Set up context for TTY command. */ /* Set up context for TTY command. */
static void static void
screen_write_initctx(struct screen_write_ctx *ctx, struct tty_ctx *ttyctx) screen_write_initctx(struct screen_write_ctx *ctx, struct tty_ctx *ttyctx)
@@ -812,15 +994,16 @@ screen_write_cursormove(struct screen_write_ctx *ctx, u_int px, u_int py)
/* Reverse index (up with scroll). */ /* Reverse index (up with scroll). */
void void
screen_write_reverseindex(struct screen_write_ctx *ctx) screen_write_reverseindex(struct screen_write_ctx *ctx, u_int bg)
{ {
struct screen *s = ctx->s; struct screen *s = ctx->s;
struct tty_ctx ttyctx; struct tty_ctx ttyctx;
screen_write_initctx(ctx, &ttyctx); screen_write_initctx(ctx, &ttyctx);
ttyctx.bg = bg;
if (s->cy == s->rupper) if (s->cy == s->rupper)
grid_view_scroll_region_down(s->grid, s->rupper, s->rlower); grid_view_scroll_region_down(s->grid, s->rupper, s->rlower, bg);
else if (s->cy > 0) else if (s->cy > 0)
s->cy--; s->cy--;
@@ -854,7 +1037,7 @@ screen_write_scrollregion(struct screen_write_ctx *ctx, u_int rupper,
/* Line feed. */ /* Line feed. */
void void
screen_write_linefeed(struct screen_write_ctx *ctx, int wrapped) screen_write_linefeed(struct screen_write_ctx *ctx, int wrapped, u_int bg)
{ {
struct screen *s = ctx->s; struct screen *s = ctx->s;
struct grid *gd = s->grid; struct grid *gd = s->grid;
@@ -869,8 +1052,13 @@ screen_write_linefeed(struct screen_write_ctx *ctx, int wrapped)
log_debug("%s: at %u,%u (region %u-%u)", __func__, s->cx, s->cy, log_debug("%s: at %u,%u (region %u-%u)", __func__, s->cx, s->cy,
s->rupper, s->rlower); s->rupper, s->rlower);
if (bg != ctx->bg) {
screen_write_collect_flush(ctx, 1);
ctx->bg = bg;
}
if (s->cy == s->rlower) { if (s->cy == s->rlower) {
grid_view_scroll_region_up(gd, s->rupper, s->rlower); grid_view_scroll_region_up(gd, s->rupper, s->rlower, bg);
screen_write_collect_scroll(ctx); screen_write_collect_scroll(ctx);
ctx->scrolled++; ctx->scrolled++;
} else if (s->cy < screen_size_y(s) - 1) } else if (s->cy < screen_size_y(s) - 1)
@@ -879,7 +1067,7 @@ screen_write_linefeed(struct screen_write_ctx *ctx, int wrapped)
/* Scroll up. */ /* Scroll up. */
void void
screen_write_scrollup(struct screen_write_ctx *ctx, u_int lines) screen_write_scrollup(struct screen_write_ctx *ctx, u_int lines, u_int bg)
{ {
struct screen *s = ctx->s; struct screen *s = ctx->s;
struct grid *gd = s->grid; struct grid *gd = s->grid;
@@ -890,8 +1078,13 @@ screen_write_scrollup(struct screen_write_ctx *ctx, u_int lines)
else if (lines > s->rlower - s->rupper + 1) else if (lines > s->rlower - s->rupper + 1)
lines = s->rlower - s->rupper + 1; lines = s->rlower - s->rupper + 1;
if (bg != ctx->bg) {
screen_write_collect_flush(ctx, 1);
ctx->bg = bg;
}
for (i = 0; i < lines; i++) { for (i = 0; i < lines; i++) {
grid_view_scroll_region_up(gd, s->rupper, s->rlower); grid_view_scroll_region_up(gd, s->rupper, s->rlower, bg);
screen_write_collect_scroll(ctx); screen_write_collect_scroll(ctx);
} }
ctx->scrolled += lines; ctx->scrolled += lines;
@@ -1046,9 +1239,12 @@ screen_write_collect_flush(struct screen_write_ctx *ctx, int scroll_only)
screen_write_initctx(ctx, &ttyctx); screen_write_initctx(ctx, &ttyctx);
ttyctx.num = ctx->scrolled; ttyctx.num = ctx->scrolled;
ttyctx.bg = ctx->bg;
tty_write(tty_cmd_scrollup, &ttyctx); tty_write(tty_cmd_scrollup, &ttyctx);
} }
ctx->scrolled = 0; ctx->scrolled = 0;
ctx->bg = 8;
if (scroll_only) if (scroll_only)
return; return;
@@ -1083,6 +1279,7 @@ screen_write_collect_end(struct screen_write_ctx *ctx)
struct screen *s = ctx->s; struct screen *s = ctx->s;
struct screen_write_collect_item *ci = ctx->item; struct screen_write_collect_item *ci = ctx->item;
struct grid_cell gc; struct grid_cell gc;
u_int xx;
if (ci->used == 0) if (ci->used == 0)
return; return;
@@ -1095,9 +1292,29 @@ screen_write_collect_end(struct screen_write_ctx *ctx)
log_debug("%s: %u %s (at %u,%u)", __func__, ci->used, ci->data, s->cx, log_debug("%s: %u %s (at %u,%u)", __func__, ci->used, ci->data, s->cx,
s->cy); s->cy);
if (s->cx != 0) {
for (xx = s->cx; xx > 0; xx--) {
grid_view_get_cell(s->grid, xx, s->cy, &gc);
if (~gc.flags & GRID_FLAG_PADDING)
break;
grid_view_set_cell(s->grid, xx, s->cy,
&grid_default_cell);
}
if (gc.data.width > 1)
grid_view_set_cell(s->grid, xx, s->cy,
&grid_default_cell);
}
memcpy(&gc, &ci->gc, sizeof gc); memcpy(&gc, &ci->gc, sizeof gc);
grid_view_set_cells(s->grid, s->cx, s->cy, &gc, ci->data, ci->used); grid_view_set_cells(s->grid, s->cx, s->cy, &gc, ci->data, ci->used);
s->cx += ci->used; s->cx += ci->used;
for (xx = s->cx; xx < screen_size_x(s); xx++) {
grid_view_get_cell(s->grid, xx, s->cy, &gc);
if (~gc.flags & GRID_FLAG_PADDING)
break;
grid_view_set_cell(s->grid, xx, s->cy, &grid_default_cell);
}
} }
/* Write cell data, collecting if necessary. */ /* Write cell data, collecting if necessary. */
@@ -1118,7 +1335,7 @@ screen_write_collect_add(struct screen_write_ctx *ctx,
*/ */
collect = 1; collect = 1;
if (gc->data.width != 1 || gc->data.size != 1) if (gc->data.width != 1 || gc->data.size != 1 || *gc->data.data >= 0x7f)
collect = 0; collect = 0;
else if (gc->attr & GRID_ATTR_CHARSET) else if (gc->attr & GRID_ATTR_CHARSET)
collect = 0; collect = 0;
@@ -1143,7 +1360,7 @@ screen_write_collect_add(struct screen_write_ctx *ctx,
if (s->cx > sx - 1) { if (s->cx > sx - 1) {
log_debug("%s: wrapped at %u,%u", __func__, s->cx, s->cy); log_debug("%s: wrapped at %u,%u", __func__, s->cx, s->cy);
ci->wrapped = 1; ci->wrapped = 1;
screen_write_linefeed(ctx, 1); screen_write_linefeed(ctx, 1, 8);
s->cx = 0; s->cx = 0;
} }
@@ -1204,8 +1421,10 @@ screen_write_cell(struct screen_write_ctx *ctx, const struct grid_cell *gc)
/* Check this will fit on the current line and wrap if not. */ /* Check this will fit on the current line and wrap if not. */
if ((s->mode & MODE_WRAP) && s->cx > sx - width) { if ((s->mode & MODE_WRAP) && s->cx > sx - width) {
screen_write_linefeed(ctx, 1); log_debug("%s: wrapped at %u,%u", __func__, s->cx, s->cy);
screen_write_linefeed(ctx, 1, 8);
s->cx = 0; s->cx = 0;
screen_write_collect_flush(ctx, 1);
} }
/* Sanity check cursor position. */ /* Sanity check cursor position. */
@@ -1226,6 +1445,7 @@ screen_write_cell(struct screen_write_ctx *ctx, const struct grid_cell *gc)
* already ensured there is enough room. * already ensured there is enough room.
*/ */
for (xx = s->cx + 1; xx < s->cx + width; xx++) { for (xx = s->cx + 1; xx < s->cx + width; xx++) {
log_debug("%s: new padding at %u,%u", __func__, xx, s->cy);
grid_view_set_cell(gd, xx, s->cy, &screen_write_pad_cell); grid_view_set_cell(gd, xx, s->cy, &screen_write_pad_cell);
skip = 0; skip = 0;
} }
@@ -1255,7 +1475,7 @@ screen_write_cell(struct screen_write_ctx *ctx, const struct grid_cell *gc)
} }
} }
/* Update the selection the flag and set the cell. */ /* Update the selected flag and set the cell. */
selected = screen_check_selection(s, s->cx, s->cy); selected = screen_check_selection(s, s->cx, s->cy);
if (selected && (~gc->flags & GRID_FLAG_SELECTED)) { if (selected && (~gc->flags & GRID_FLAG_SELECTED)) {
memcpy(&tmp_gc, gc, sizeof tmp_gc); memcpy(&tmp_gc, gc, sizeof tmp_gc);
@@ -1375,10 +1595,12 @@ 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: padding 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);
} }
/* Overwrite the character at the start of this padding. */ /* Overwrite the character at the start of this padding. */
log_debug("%s: character 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;
} }
@@ -1395,6 +1617,7 @@ 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);
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

@@ -24,17 +24,44 @@
#include "tmux.h" #include "tmux.h"
struct screen_title_entry {
char *text;
TAILQ_ENTRY(screen_title_entry) entry;
};
TAILQ_HEAD(screen_titles, screen_title_entry);
static void screen_resize_x(struct screen *, u_int); static void screen_resize_x(struct screen *, u_int);
static void screen_resize_y(struct screen *, u_int); static void screen_resize_y(struct screen *, u_int);
static void screen_reflow(struct screen *, u_int); static void screen_reflow(struct screen *, u_int);
/* Free titles stack. */
static void
screen_free_titles(struct screen *s)
{
struct screen_title_entry *title_entry;
if (s->titles == NULL)
return;
while ((title_entry = TAILQ_FIRST(s->titles)) != NULL) {
TAILQ_REMOVE(s->titles, title_entry, entry);
free(title_entry->text);
free(title_entry);
}
free(s->titles);
s->titles = NULL;
}
/* Create a new screen. */ /* Create a new screen. */
void void
screen_init(struct screen *s, u_int sx, u_int sy, u_int hlimit) screen_init(struct screen *s, u_int sx, u_int sy, u_int hlimit)
{ {
s->grid = grid_create(sx, sy, hlimit); s->grid = grid_create(sx, sy, hlimit);
s->title = xstrdup(""); s->title = xstrdup("");
s->titles = NULL;
s->cstyle = 0; s->cstyle = 0;
s->ccolour = xstrdup(""); s->ccolour = xstrdup("");
@@ -60,6 +87,7 @@ screen_reinit(struct screen *s)
grid_clear_lines(s->grid, s->grid->hsize, s->grid->sy, 8); grid_clear_lines(s->grid, s->grid->hsize, s->grid->sy, 8);
screen_clear_selection(s); screen_clear_selection(s);
screen_free_titles(s);
} }
/* Destroy a screen. */ /* Destroy a screen. */
@@ -69,7 +97,10 @@ screen_free(struct screen *s)
free(s->tabs); free(s->tabs);
free(s->title); free(s->title);
free(s->ccolour); free(s->ccolour);
grid_destroy(s->grid); grid_destroy(s->grid);
screen_free_titles(s);
} }
/* Reset tabs to default, eight spaces apart. */ /* Reset tabs to default, eight spaces apart. */
@@ -107,7 +138,44 @@ void
screen_set_title(struct screen *s, const char *title) screen_set_title(struct screen *s, const char *title)
{ {
free(s->title); free(s->title);
s->title = xstrdup(title); utf8_stravis(&s->title, title, VIS_OCTAL|VIS_CSTYLE|VIS_TAB|VIS_NL);
}
/* Push the current title onto the stack. */
void
screen_push_title(struct screen *s)
{
struct screen_title_entry *title_entry;
if (s->titles == NULL) {
s->titles = xmalloc(sizeof *s->titles);
TAILQ_INIT(s->titles);
}
title_entry = xmalloc(sizeof *title_entry);
title_entry->text = xstrdup(s->title);
TAILQ_INSERT_HEAD(s->titles, title_entry, entry);
}
/*
* Pop a title from the stack and set it as the screen title. If the stack is
* empty, do nothing.
*/
void
screen_pop_title(struct screen *s)
{
struct screen_title_entry *title_entry;
if (s->titles == NULL)
return;
title_entry = TAILQ_FIRST(s->titles);
if (title_entry != NULL) {
screen_set_title(s, title_entry->text);
TAILQ_REMOVE(s->titles, title_entry, entry);
free(title_entry->text);
free(title_entry);
}
} }
/* Resize screen. */ /* Resize screen. */
@@ -128,7 +196,8 @@ screen_resize(struct screen *s, u_int sx, u_int sy, int reflow)
* is simpler and more reliable so let's do that. * is simpler and more reliable so let's do that.
*/ */
screen_reset_tabs(s); screen_reset_tabs(s);
} } else
reflow = 0;
if (sy != screen_size_y(s)) if (sy != screen_size_y(s))
screen_resize_y(s, sy); screen_resize_y(s, sy);
@@ -400,14 +469,5 @@ screen_select_cell(struct screen *s, struct grid_cell *dst,
static void static void
screen_reflow(struct screen *s, u_int new_x) screen_reflow(struct screen *s, u_int new_x)
{ {
struct grid *old = s->grid; grid_reflow(s->grid, new_x, &s->cy);
u_int change;
s->grid = grid_create(old->sx, old->sy, old->hlimit);
change = grid_reflow(s->grid, old, new_x);
if (change < s->cy)
s->cy -= change;
else
s->cy = 0;
} }

View File

@@ -47,28 +47,43 @@ 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 *);
/* Number of attached clients. */
u_int
server_client_how_many(void)
{
struct client *c;
u_int n;
n = 0;
TAILQ_FOREACH(c, &clients, entry) {
if (c->session != NULL && (~c->flags & CLIENT_DETACHING))
n++;
}
return (n);
}
/* Identify mode callback. */ /* Identify mode callback. */
static void static void
server_client_callback_identify(__unused int fd, __unused short events, void *data) server_client_callback_identify(__unused int fd, __unused short events,
void *data)
{ {
server_client_clear_identify(data, NULL); server_client_clear_identify(data, NULL);
} }
/* Set identify mode on client. */ /* Set identify mode on client. */
void void
server_client_set_identify(struct client *c) server_client_set_identify(struct client *c, u_int delay)
{ {
struct timeval tv; struct timeval tv;
int delay;
delay = options_get_number(c->session->options, "display-panes-time");
tv.tv_sec = delay / 1000; tv.tv_sec = delay / 1000;
tv.tv_usec = (delay % 1000) * 1000L; tv.tv_usec = (delay % 1000) * 1000L;
if (event_initialized(&c->identify_timer)) if (event_initialized(&c->identify_timer))
evtimer_del(&c->identify_timer); evtimer_del(&c->identify_timer);
evtimer_set(&c->identify_timer, server_client_callback_identify, c); evtimer_set(&c->identify_timer, server_client_callback_identify, c);
evtimer_add(&c->identify_timer, &tv); if (delay != 0)
evtimer_add(&c->identify_timer, &tv);
c->flags |= CLIENT_IDENTIFY; c->flags |= CLIENT_IDENTIFY;
c->tty.flags |= (TTY_FREEZE|TTY_NOCURSOR); c->tty.flags |= (TTY_FREEZE|TTY_NOCURSOR);
@@ -144,7 +159,7 @@ server_client_is_default_key_table(struct client *c, struct key_table *table)
} }
/* Create a new client. */ /* Create a new client. */
void struct client *
server_client_create(int fd) server_client_create(int fd)
{ {
struct client *c; struct client *c;
@@ -178,7 +193,7 @@ server_client_create(int fd)
c->tty.sx = 80; c->tty.sx = 80;
c->tty.sy = 24; c->tty.sy = 24;
screen_init(&c->status, c->tty.sx, 1, 0); screen_init(&c->status.status, c->tty.sx, 1, 0);
c->message_string = NULL; c->message_string = NULL;
TAILQ_INIT(&c->message_log); TAILQ_INIT(&c->message_log);
@@ -197,6 +212,7 @@ server_client_create(int fd)
TAILQ_INSERT_TAIL(&clients, c, entry); TAILQ_INSERT_TAIL(&clients, c, entry);
log_debug("new client %p", c); log_debug("new client %p", c);
return (c);
} }
/* Open client terminal if needed. */ /* Open client terminal if needed. */
@@ -254,9 +270,13 @@ server_client_lost(struct client *c)
if (c->stderr_data != c->stdout_data) if (c->stderr_data != c->stdout_data)
evbuffer_free(c->stderr_data); evbuffer_free(c->stderr_data);
if (event_initialized(&c->status_timer)) if (event_initialized(&c->status.timer))
evtimer_del(&c->status_timer); evtimer_del(&c->status.timer);
screen_free(&c->status); screen_free(&c->status.status);
if (c->status.old_status != NULL) {
screen_free(c->status.old_status);
free(c->status.old_status);
}
free(c->title); free(c->title);
free((void *)c->cwd); free((void *)c->cwd);
@@ -328,7 +348,7 @@ server_client_free(__unused int fd, __unused short events, void *arg)
void void
server_client_suspend(struct client *c) server_client_suspend(struct client *c)
{ {
struct session *s = c->session; struct session *s = c->session;
if (s == NULL || (c->flags & CLIENT_DETACHING)) if (s == NULL || (c->flags & CLIENT_DETACHING))
return; return;
@@ -342,14 +362,14 @@ server_client_suspend(struct client *c)
void void
server_client_detach(struct client *c, enum msgtype msgtype) server_client_detach(struct client *c, enum msgtype msgtype)
{ {
struct session *s = c->session; struct session *s = c->session;
if (s == NULL || (c->flags & CLIENT_DETACHING)) if (s == NULL || (c->flags & CLIENT_DETACHING))
return; return;
c->flags |= CLIENT_DETACHING; c->flags |= CLIENT_DETACHING;
notify_client("client-detached", c); notify_client("client-detached", c);
proc_send_s(c->peer, msgtype, s->name); proc_send(c->peer, msgtype, -1, s->name, strlen(s->name) + 1);
} }
/* Execute command to replace a client. */ /* Execute command to replace a client. */
@@ -794,8 +814,9 @@ server_client_handle_key(struct client *c, key_code key)
struct timeval tv; struct timeval tv;
struct key_table *table, *first; struct key_table *table, *first;
struct key_binding bd_find, *bd; struct key_binding bd_find, *bd;
int xtimeout; int xtimeout, flags;
struct cmd_find_state fs; struct cmd_find_state fs;
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_DEAD|CLIENT_SUSPENDED)) != 0)
@@ -855,8 +876,8 @@ server_client_handle_key(struct client *c, key_code key)
m->valid = 0; m->valid = 0;
/* Find affected pane. */ /* Find affected pane. */
if (!KEYC_IS_MOUSE(key) || cmd_find_from_mouse(&fs, m) != 0) if (!KEYC_IS_MOUSE(key) || cmd_find_from_mouse(&fs, m, 0) != 0)
cmd_find_from_session(&fs, s); cmd_find_from_session(&fs, s, 0);
wp = fs.wp; wp = fs.wp;
/* Forward mouse keys if disabled. */ /* Forward mouse keys if disabled. */
@@ -884,15 +905,17 @@ server_client_handle_key(struct client *c, key_code key)
* The prefix always takes precedence and forces a switch to the prefix * The prefix always takes precedence and forces a switch to the prefix
* table, unless we are already there. * table, unless we are already there.
*/ */
if ((key == (key_code)options_get_number(s->options, "prefix") || retry:
key == (key_code)options_get_number(s->options, "prefix2")) && key0 = (key & ~KEYC_XTERM);
if ((key0 == (key_code)options_get_number(s->options, "prefix") ||
key0 == (key_code)options_get_number(s->options, "prefix2")) &&
strcmp(table->name, "prefix") != 0) { strcmp(table->name, "prefix") != 0) {
server_client_set_key_table(c, "prefix"); server_client_set_key_table(c, "prefix");
server_status_client(c); server_status_client(c);
return; return;
} }
flags = c->flags;
retry:
/* Log key table. */ /* Log key table. */
if (wp == NULL) if (wp == NULL)
log_debug("key table %s (no pane)", table->name); log_debug("key table %s (no pane)", table->name);
@@ -902,7 +925,7 @@ retry:
log_debug("currently repeating"); log_debug("currently repeating");
/* Try to see if there is a key binding in the current table. */ /* Try to see if there is a key binding in the current table. */
bd_find.key = (key & ~KEYC_XTERM); bd_find.key = key0;
bd = RB_FIND(key_bindings, &table->key_bindings, &bd_find); bd = RB_FIND(key_bindings, &table->key_bindings, &bd_find);
if (bd != NULL) { if (bd != NULL) {
/* /*
@@ -945,7 +968,7 @@ retry:
server_status_client(c); server_status_client(c);
/* Execute the key binding. */ /* Execute the key binding. */
key_bindings_dispatch(bd, c, m, &fs); key_bindings_dispatch(bd, NULL, c, m, &fs);
key_bindings_unref_table(table); key_bindings_unref_table(table);
return; return;
} }
@@ -968,7 +991,7 @@ retry:
* No match in the root table either. If this wasn't the first table * No match in the root table either. If this wasn't the first table
* tried, don't pass the key to the pane. * tried, don't pass the key to the pane.
*/ */
if (first != table) { if (first != table && (~flags & CLIENT_REPEAT)) {
server_client_set_key_table(c, NULL); server_client_set_key_table(c, NULL);
server_status_client(c); server_status_client(c);
return; return;
@@ -1016,6 +1039,47 @@ server_client_loop(void)
} }
} }
/* Check if we need to force a resize. */
static int
server_client_resize_force(struct window_pane *wp)
{
struct timeval tv = { .tv_usec = 100000 };
struct winsize ws;
/*
* If we are resizing to the same size as when we entered the loop
* (that is, to the same size the application currently thinks it is),
* tmux may have gone through several resizes internally and thrown
* away parts of the screen. So we need the application to actually
* redraw even though its final size has not changed.
*/
if (wp->flags & PANE_RESIZEFORCE) {
wp->flags &= ~PANE_RESIZEFORCE;
return (0);
}
if (wp->sx != wp->osx ||
wp->sy != wp->osy ||
wp->sx <= 1 ||
wp->sy <= 1)
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);
evtimer_add(&wp->resize_timer, &tv);
wp->flags |= PANE_RESIZEFORCE;
return (1);
}
/* Resize timer event. */ /* Resize timer event. */
static void static void
server_client_resize_event(__unused int fd, __unused short events, void *data) server_client_resize_event(__unused int fd, __unused short events, void *data)
@@ -1027,12 +1091,13 @@ server_client_resize_event(__unused int fd, __unused short events, void *data)
if (!(wp->flags & PANE_RESIZE)) if (!(wp->flags & PANE_RESIZE))
return; return;
if (server_client_resize_force(wp))
return;
memset(&ws, 0, sizeof ws); memset(&ws, 0, sizeof ws);
ws.ws_col = wp->sx; ws.ws_col = wp->sx;
ws.ws_row = wp->sy; ws.ws_row = wp->sy;
if (wp->fd != -1 && ioctl(wp->fd, TIOCSWINSZ, &ws) == -1)
if (ioctl(wp->fd, TIOCSWINSZ, &ws) == -1) {
#ifdef __sun #ifdef __sun
/* /*
* Some versions of Solaris apparently can return an error when * Some versions of Solaris apparently can return an error when
@@ -1043,9 +1108,12 @@ server_client_resize_event(__unused int fd, __unused short events, void *data)
if (errno != EINVAL && errno != ENXIO) if (errno != EINVAL && errno != ENXIO)
#endif #endif
fatal("ioctl failed"); fatal("ioctl failed");
} log_debug("%s: %%%u resize to %u,%u", __func__, wp->id, wp->sx, wp->sy);
wp->flags &= ~PANE_RESIZE; wp->flags &= ~PANE_RESIZE;
wp->osx = wp->sx;
wp->osy = wp->sy;
} }
/* Check if pane should be resized. */ /* Check if pane should be resized. */
@@ -1056,6 +1124,7 @@ server_client_check_resize(struct window_pane *wp)
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);
@@ -1143,7 +1212,7 @@ server_client_reset_state(struct client *c)
struct window_pane *wp = w->active, *loop; struct window_pane *wp = w->active, *loop;
struct screen *s = wp->screen; struct screen *s = wp->screen;
struct options *oo = c->session->options; struct options *oo = c->session->options;
int status, mode, o; int lines, mode;
if (c->flags & (CLIENT_CONTROL|CLIENT_SUSPENDED)) if (c->flags & (CLIENT_CONTROL|CLIENT_SUSPENDED))
return; return;
@@ -1151,13 +1220,14 @@ server_client_reset_state(struct client *c)
tty_region_off(&c->tty); tty_region_off(&c->tty);
tty_margin_off(&c->tty); tty_margin_off(&c->tty);
status = options_get_number(oo, "status"); if (status_at_line(c) != 0)
if (!window_pane_visible(wp) || wp->yoff + s->cy >= c->tty.sy - status) lines = 0;
else
lines = status_line_size(c->session);
if (!window_pane_visible(wp) || wp->yoff + s->cy >= c->tty.sy - lines)
tty_cursor(&c->tty, 0, 0); tty_cursor(&c->tty, 0, 0);
else { else
o = status && options_get_number(oo, "status-position") == 0; tty_cursor(&c->tty, wp->xoff + s->cx, lines + wp->yoff + s->cy);
tty_cursor(&c->tty, wp->xoff + s->cx, o + wp->yoff + s->cy);
}
/* /*
* Set mouse mode if requested. To support dragging, always use button * Set mouse mode if requested. To support dragging, always use button
@@ -1219,6 +1289,8 @@ server_client_check_exit(struct client *c)
if (EVBUFFER_LENGTH(c->stderr_data) != 0) if (EVBUFFER_LENGTH(c->stderr_data) != 0)
return; return;
if (c->flags & CLIENT_ATTACHED)
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_EXIT;
} }
@@ -1238,7 +1310,7 @@ server_client_check_redraw(struct client *c)
struct session *s = c->session; struct session *s = c->session;
struct tty *tty = &c->tty; struct tty *tty = &c->tty;
struct window_pane *wp; struct window_pane *wp;
int needed, flags, masked; int needed, flags, masked;
struct timeval tv = { .tv_usec = 1000 }; struct timeval tv = { .tv_usec = 1000 };
static struct event ev; static struct event ev;
size_t left; size_t left;
@@ -1262,28 +1334,23 @@ server_client_check_redraw(struct client *c)
} }
} }
} }
if (needed) { if (needed && (left = EVBUFFER_LENGTH(tty->out)) != 0) {
left = EVBUFFER_LENGTH(tty->out); log_debug("%s: redraw deferred (%zu left)", c->name, left);
if (left != 0) { if (!evtimer_initialized(&ev))
log_debug("%s: redraw deferred (%zu left)", c->name, left);
if (evtimer_initialized(&ev) && evtimer_pending(&ev, NULL))
return;
log_debug("redraw timer started");
evtimer_set(&ev, server_client_redraw_timer, NULL); evtimer_set(&ev, server_client_redraw_timer, NULL);
if (!evtimer_pending(&ev, NULL)) {
log_debug("redraw timer started");
evtimer_add(&ev, &tv); evtimer_add(&ev, &tv);
/*
* We may have got here for a single pane redraw, but
* force a full redraw next time in case other panes
* have been updated.
*/
c->flags |= CLIENT_REDRAW;
return;
} }
if (evtimer_initialized(&ev))
evtimer_del(&ev); /*
* We may have got here for a single pane redraw, but force a
* full redraw next time in case other panes have been updated.
*/
c->flags |= CLIENT_REDRAW;
return;
} else if (needed)
log_debug("%s: redraw needed", c->name); log_debug("%s: redraw needed", c->name);
}
if (c->flags & (CLIENT_REDRAW|CLIENT_STATUS)) { if (c->flags & (CLIENT_REDRAW|CLIENT_STATUS)) {
if (options_get_number(s->options, "set-titles")) if (options_get_number(s->options, "set-titles"))
@@ -1416,10 +1483,9 @@ server_client_dispatch(struct imsg *imsg, void *arg)
if (c->flags & CLIENT_CONTROL) if (c->flags & CLIENT_CONTROL)
break; break;
if (tty_resize(&c->tty)) { tty_resize(&c->tty);
recalculate_sizes(); recalculate_sizes();
server_redraw_client(c); server_redraw_client(c);
}
if (c->session != NULL) if (c->session != NULL)
notify_client("client-resized", c); notify_client("client-resized", c);
break; break;
@@ -1497,6 +1563,9 @@ server_client_dispatch_command(struct client *c, struct imsg *imsg)
int argc; int argc;
char **argv, *cause; char **argv, *cause;
if (c->flags & CLIENT_EXIT)
return;
if (imsg->hdr.len - IMSG_HEADER_SIZE < sizeof data) if (imsg->hdr.len - IMSG_HEADER_SIZE < sizeof data)
fatalx("bad MSG_COMMAND size"); fatalx("bad MSG_COMMAND size");
memcpy(&data, imsg->data, sizeof data); memcpy(&data, imsg->data, sizeof data);
@@ -1543,7 +1612,7 @@ static void
server_client_dispatch_identify(struct client *c, struct imsg *imsg) server_client_dispatch_identify(struct client *c, struct imsg *imsg)
{ {
const char *data, *home; const char *data, *home;
size_t datalen; size_t datalen;
int flags; int flags;
char *name; char *name;
@@ -1667,7 +1736,7 @@ server_client_dispatch_shell(struct client *c)
shell = options_get_string(global_s_options, "default-shell"); shell = options_get_string(global_s_options, "default-shell");
if (*shell == '\0' || areshell(shell)) if (*shell == '\0' || areshell(shell))
shell = _PATH_BSHELL; shell = _PATH_BSHELL;
proc_send_s(c->peer, MSG_SHELL, shell); proc_send(c->peer, MSG_SHELL, -1, shell, strlen(shell) + 1);
proc_kill_peer(c->peer); proc_kill_peer(c->peer);
} }
@@ -1688,7 +1757,7 @@ void
server_client_push_stdout(struct client *c) server_client_push_stdout(struct client *c)
{ {
struct msg_stdout_data data; struct msg_stdout_data data;
size_t sent, left; size_t sent, left;
left = EVBUFFER_LENGTH(c->stdout_data); left = EVBUFFER_LENGTH(c->stdout_data);
while (left != 0) { while (left != 0) {
@@ -1729,7 +1798,7 @@ void
server_client_push_stderr(struct client *c) server_client_push_stderr(struct client *c)
{ {
struct msg_stderr_data data; struct msg_stderr_data data;
size_t sent, left; size_t sent, left;
if (c->stderr_data == c->stdout_data) { if (c->stderr_data == c->stdout_data) {
server_client_push_stdout(c); server_client_push_stdout(c);

View File

@@ -17,6 +17,7 @@
*/ */
#include <sys/types.h> #include <sys/types.h>
#include <sys/wait.h>
#include <sys/uio.h> #include <sys/uio.h>
#include <stdlib.h> #include <stdlib.h>
@@ -162,7 +163,7 @@ server_lock_client(struct client *c)
return; return;
cmd = options_get_string(c->session->options, "lock-command"); cmd = options_get_string(c->session->options, "lock-command");
if (strlen(cmd) + 1 > MAX_IMSGSIZE - IMSG_HEADER_SIZE) if (*cmd == '\0' || strlen(cmd) + 1 > MAX_IMSGSIZE - IMSG_HEADER_SIZE)
return; return;
tty_stop_tty(&c->tty); tty_stop_tty(&c->tty);
@@ -171,7 +172,7 @@ server_lock_client(struct client *c)
tty_raw(&c->tty, tty_term_string(c->tty.term, TTYC_E3)); tty_raw(&c->tty, tty_term_string(c->tty.term, TTYC_E3));
c->flags |= CLIENT_SUSPENDED; c->flags |= CLIENT_SUSPENDED;
proc_send_s(c->peer, MSG_LOCK, cmd); proc_send(c->peer, MSG_LOCK, -1, cmd, strlen(cmd) + 1);
} }
void void
@@ -276,11 +277,11 @@ void
server_destroy_pane(struct window_pane *wp, int notify) server_destroy_pane(struct window_pane *wp, int notify)
{ {
struct window *w = wp->window; struct window *w = wp->window;
int old_fd;
struct screen_write_ctx ctx; struct screen_write_ctx ctx;
struct grid_cell gc; struct grid_cell gc;
time_t t;
char tim[26];
old_fd = wp->fd;
if (wp->fd != -1) { if (wp->fd != -1) {
#ifdef HAVE_UTEMPTER #ifdef HAVE_UTEMPTER
utempter_remove_record(wp->fd); utempter_remove_record(wp->fd);
@@ -291,22 +292,39 @@ server_destroy_pane(struct window_pane *wp, int notify)
} }
if (options_get_number(w->options, "remain-on-exit")) { if (options_get_number(w->options, "remain-on-exit")) {
if (old_fd == -1) if (~wp->flags & PANE_STATUSREADY)
return; return;
if (wp->flags & PANE_STATUSDRAWN)
return;
wp->flags |= PANE_STATUSDRAWN;
if (notify) if (notify)
notify_pane("pane-died", wp); notify_pane("pane-died", wp);
screen_write_start(&ctx, wp, &wp->base); screen_write_start(&ctx, wp, &wp->base);
screen_write_scrollregion(&ctx, 0, screen_size_y(ctx.s) - 1); screen_write_scrollregion(&ctx, 0, screen_size_y(ctx.s) - 1);
screen_write_cursormove(&ctx, 0, screen_size_y(ctx.s) - 1); screen_write_cursormove(&ctx, 0, screen_size_y(ctx.s) - 1);
screen_write_linefeed(&ctx, 1); screen_write_linefeed(&ctx, 1, 8);
memcpy(&gc, &grid_default_cell, sizeof gc); memcpy(&gc, &grid_default_cell, sizeof gc);
gc.attr |= GRID_ATTR_BRIGHT;
screen_write_puts(&ctx, &gc, "Pane is dead"); time(&t);
ctime_r(&t, tim);
if (WIFEXITED(wp->status)) {
screen_write_nputs(&ctx, -1, &gc,
"Pane is dead (status %d, %s)",
WEXITSTATUS(wp->status),
tim);
} else if (WIFSIGNALED(wp->status)) {
screen_write_nputs(&ctx, -1, &gc,
"Pane is dead (signal %d, %s)",
WTERMSIG(wp->status),
tim);
}
screen_write_stop(&ctx); screen_write_stop(&ctx);
wp->flags |= PANE_REDRAW; wp->flags |= PANE_REDRAW;
return; return;
} }
@@ -334,7 +352,7 @@ server_destroy_session_group(struct session *s)
else { else {
TAILQ_FOREACH_SAFE(s, &sg->sessions, gentry, s1) { TAILQ_FOREACH_SAFE(s, &sg->sessions, gentry, s1) {
server_destroy_session(s); server_destroy_session(s);
session_destroy(s); session_destroy(s, __func__);
} }
} }
} }
@@ -400,7 +418,7 @@ server_check_unattached(void)
if (!(s->flags & SESSION_UNATTACHED)) if (!(s->flags & SESSION_UNATTACHED))
continue; continue;
if (options_get_number (s->options, "destroy-unattached")) if (options_get_number (s->options, "destroy-unattached"))
session_destroy(s); session_destroy(s, __func__);
} }
} }
@@ -438,8 +456,6 @@ server_set_stdin_callback(struct client *c, void (*cb)(struct client *, int,
void void
server_unzoom_window(struct window *w) server_unzoom_window(struct window *w)
{ {
if (window_unzoom(w) == 0) { if (window_unzoom(w) == 0)
server_redraw_window(w); server_redraw_window(w);
server_status_window(w);
}
} }

109
server.c
View File

@@ -49,7 +49,6 @@ static struct event server_ev_accept;
struct cmd_find_state marked_pane; struct cmd_find_state marked_pane;
static int server_create_socket(void);
static int server_loop(void); static int server_loop(void);
static void server_send_exit(void); static void server_send_exit(void);
static void server_accept(int, short, void *); static void server_accept(int, short, void *);
@@ -98,59 +97,101 @@ server_check_marked(void)
/* Create server socket. */ /* Create server socket. */
static int static int
server_create_socket(void) server_create_socket(char **cause)
{ {
struct sockaddr_un sa; struct sockaddr_un sa;
size_t size; size_t size;
mode_t mask; mode_t mask;
int fd; int fd, saved_errno;
memset(&sa, 0, sizeof sa); memset(&sa, 0, sizeof sa);
sa.sun_family = AF_UNIX; sa.sun_family = AF_UNIX;
size = strlcpy(sa.sun_path, socket_path, sizeof sa.sun_path); size = strlcpy(sa.sun_path, socket_path, sizeof sa.sun_path);
if (size >= sizeof sa.sun_path) { if (size >= sizeof sa.sun_path) {
errno = ENAMETOOLONG; errno = ENAMETOOLONG;
return (-1); goto fail;
} }
unlink(sa.sun_path); unlink(sa.sun_path);
if ((fd = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) if ((fd = socket(AF_UNIX, SOCK_STREAM, 0)) == -1)
return (-1); goto fail;
mask = umask(S_IXUSR|S_IXGRP|S_IRWXO); mask = umask(S_IXUSR|S_IXGRP|S_IRWXO);
if (bind(fd, (struct sockaddr *) &sa, sizeof(sa)) == -1) { if (bind(fd, (struct sockaddr *)&sa, sizeof sa) == -1) {
saved_errno = errno;
close(fd); close(fd);
return (-1); errno = saved_errno;
goto fail;
} }
umask(mask); umask(mask);
if (listen(fd, 128) == -1) { if (listen(fd, 128) == -1) {
saved_errno = errno;
close(fd); close(fd);
return (-1); errno = saved_errno;
goto fail;
} }
setblocking(fd, 0); setblocking(fd, 0);
return (fd); return (fd);
fail:
if (cause != NULL) {
xasprintf(cause, "error creating %s (%s)", socket_path,
strerror(errno));
}
return (-1);
}
/* Server error callback. */
static enum cmd_retval
server_start_error(struct cmdq_item *item, void *data)
{
char *error = data;
cmdq_error(item, "%s", error);
free(error);
return (CMD_RETURN_NORMAL);
} }
/* Fork new server. */ /* Fork new server. */
int int
server_start(struct event_base *base, int lockfd, char *lockfile) server_start(struct tmuxproc *client, struct event_base *base, int lockfd,
char *lockfile)
{ {
int pair[2]; int pair[2];
struct job *job; struct job *job;
sigset_t set, oldset;
struct client *c;
char *cause = NULL;
if (socketpair(AF_UNIX, SOCK_STREAM, PF_UNSPEC, pair) != 0) if (socketpair(AF_UNIX, SOCK_STREAM, PF_UNSPEC, pair) != 0)
fatal("socketpair failed"); fatal("socketpair failed");
server_proc = proc_start("server", base, 1, server_signal); sigfillset(&set);
if (server_proc == NULL) { sigprocmask(SIG_BLOCK, &set, &oldset);
switch (fork()) {
case -1:
fatal("fork failed");
case 0:
break;
default:
sigprocmask(SIG_SETMASK, &oldset, NULL);
close(pair[1]); close(pair[1]);
return (pair[0]); return (pair[0]);
} }
close(pair[0]); close(pair[0]);
if (daemon(1, 0) != 0)
fatal("daemon failed");
proc_clear_signals(client, 0);
if (event_reinit(base) != 0)
fatalx("event_reinit failed");
server_proc = proc_start("server");
proc_set_signals(server_proc, server_signal);
sigprocmask(SIG_SETMASK, &oldset, NULL);
if (log_get_level() > 3) if (log_get_level() > 1)
tty_create_log(); tty_create_log();
if (pledge("stdio rpath wpath cpath fattr unix getpw recvfd proc exec " if (pledge("stdio rpath wpath cpath fattr unix getpw recvfd proc exec "
"tty ps", NULL) != 0) "tty ps", NULL) != 0)
@@ -165,11 +206,10 @@ server_start(struct event_base *base, int lockfd, char *lockfile)
gettimeofday(&start_time, NULL); gettimeofday(&start_time, NULL);
server_fd = server_create_socket(); server_fd = server_create_socket(&cause);
if (server_fd == -1) if (server_fd != -1)
fatal("couldn't create socket"); server_update_socket();
server_update_socket(); c = server_client_create(pair[1]);
server_client_create(pair[1]);
if (lockfd >= 0) { if (lockfd >= 0) {
unlink(lockfile); unlink(lockfile);
@@ -177,6 +217,11 @@ server_start(struct event_base *base, int lockfd, char *lockfile)
close(lockfd); close(lockfd);
} }
if (cause != NULL) {
cmdq_append(c, cmdq_get_callback(server_start_error, cause));
c->flags |= CLIENT_EXIT;
}
start_cfg(); start_cfg();
server_add_accept(0); server_add_accept(0);
@@ -198,6 +243,7 @@ server_loop(void)
{ {
struct client *c; struct client *c;
u_int items; u_int items;
struct job *job;
do { do {
items = cmdq_next(NULL); items = cmdq_next(NULL);
@@ -209,6 +255,9 @@ server_loop(void)
server_client_loop(); server_client_loop();
if (!options_get_number(global_options, "exit-empty") && !server_exit)
return (0);
if (!options_get_number(global_options, "exit-unattached")) { if (!options_get_number(global_options, "exit-unattached")) {
if (!RB_EMPTY(&sessions)) if (!RB_EMPTY(&sessions))
return (0); return (0);
@@ -227,6 +276,11 @@ server_loop(void)
if (!TAILQ_EMPTY(&clients)) if (!TAILQ_EMPTY(&clients))
return (0); return (0);
LIST_FOREACH(job, &all_jobs, entry) {
if ((~job->flags & JOB_NOWAIT) && job->state == JOB_RUNNING)
return (0);
}
return (1); return (1);
} }
@@ -242,13 +296,16 @@ server_send_exit(void)
TAILQ_FOREACH_SAFE(c, &clients, entry, c1) { TAILQ_FOREACH_SAFE(c, &clients, entry, c1) {
if (c->flags & CLIENT_SUSPENDED) if (c->flags & CLIENT_SUSPENDED)
server_client_lost(c); server_client_lost(c);
else else {
if (c->flags & CLIENT_ATTACHED)
notify_client("client-detached", c);
proc_send(c->peer, MSG_SHUTDOWN, -1, NULL, 0); proc_send(c->peer, MSG_SHUTDOWN, -1, NULL, 0);
}
c->session = NULL; c->session = NULL;
} }
RB_FOREACH_SAFE(s, sessions, &sessions, s1) RB_FOREACH_SAFE(s, sessions, &sessions, s1)
session_destroy(s); session_destroy(s, __func__);
} }
/* Update socket execute permissions based on whether sessions are attached. */ /* Update socket execute permissions based on whether sessions are attached. */
@@ -346,6 +403,7 @@ server_signal(int sig)
{ {
int fd; int fd;
log_debug("%s: %s", __func__, strsignal(sig));
switch (sig) { switch (sig) {
case SIGTERM: case SIGTERM:
server_exit = 1; server_exit = 1;
@@ -356,7 +414,7 @@ server_signal(int sig)
break; break;
case SIGUSR1: case SIGUSR1:
event_del(&server_ev_accept); event_del(&server_ev_accept);
fd = server_create_socket(); fd = server_create_socket(NULL);
if (fd != -1) { if (fd != -1) {
close(server_fd); close(server_fd);
server_fd = fd; server_fd = fd;
@@ -364,6 +422,9 @@ server_signal(int sig)
} }
server_add_accept(0); server_add_accept(0);
break; break;
case SIGUSR2:
proc_toggle_log(server_proc);
break;
} }
} }
@@ -402,7 +463,13 @@ server_child_exited(pid_t pid, int status)
TAILQ_FOREACH(wp, &w->panes, entry) { TAILQ_FOREACH(wp, &w->panes, entry) {
if (wp->pid == pid) { if (wp->pid == pid) {
wp->status = status; wp->status = status;
server_destroy_pane(wp, 1); wp->flags |= PANE_STATUSREADY;
log_debug("%%%u exited", wp->id);
wp->flags |= PANE_EXITED;
if (window_pane_destroy_ready(wp))
server_destroy_pane(wp, 1);
break; break;
} }
} }

View File

@@ -38,10 +38,6 @@ static struct winlink *session_next_alert(struct winlink *);
static struct winlink *session_previous_alert(struct winlink *); static struct winlink *session_previous_alert(struct winlink *);
static void session_group_remove(struct session *); static void session_group_remove(struct session *);
static u_int session_group_count(struct session_group *);
static void session_group_synchronize1(struct session *, struct session *);
static u_int session_group_count(struct session_group *);
static void session_group_synchronize1(struct session *, struct session *); static void session_group_synchronize1(struct session *, struct session *);
RB_GENERATE(sessions, session, entry, session_cmp); RB_GENERATE(sessions, session, entry, session_cmp);
@@ -177,7 +173,7 @@ session_create(const char *prefix, const char *name, int argc, char **argv,
if (argc >= 0) { if (argc >= 0) {
wl = session_new(s, NULL, argc, argv, path, cwd, idx, cause); wl = session_new(s, NULL, argc, argv, path, cwd, idx, cause);
if (wl == NULL) { if (wl == NULL) {
session_destroy(s); session_destroy(s, __func__);
return (NULL); return (NULL);
} }
session_select(s, RB_ROOT(&s->windows)->idx); session_select(s, RB_ROOT(&s->windows)->idx);
@@ -228,11 +224,11 @@ session_free(__unused int fd, __unused short events, void *arg)
/* Destroy a session. */ /* Destroy a session. */
void void
session_destroy(struct session *s) session_destroy(struct session *s, const char *from)
{ {
struct winlink *wl; struct winlink *wl;
log_debug("session %s destroyed", s->name); log_debug("session %s destroyed (%s)", s->name, from);
s->curw = NULL; s->curw = NULL;
RB_REMOVE(sessions, &sessions, s); RB_REMOVE(sessions, &sessions, s);
@@ -418,7 +414,7 @@ session_detach(struct session *s, struct winlink *wl)
session_group_synchronize_from(s); session_group_synchronize_from(s);
if (RB_EMPTY(&s->windows)) { if (RB_EMPTY(&s->windows)) {
session_destroy(s); session_destroy(s, __func__);
return (1); return (1);
} }
return (0); return (0);
@@ -623,7 +619,7 @@ session_group_remove(struct session *s)
} }
/* Count number of sessions in session group. */ /* Count number of sessions in session group. */
static u_int u_int
session_group_count(struct session_group *sg) session_group_count(struct session_group *sg)
{ {
struct session *s; struct session *s;

105
signal.c
View File

@@ -1,105 +0,0 @@
/* $OpenBSD$ */
/*
* Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>
* Copyright (c) 2010 Romain Francoise <rfrancoise@debian.org>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER
* IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
* OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include <sys/types.h>
#include <string.h>
#include <signal.h>
#include "tmux.h"
static struct event ev_sighup;
static struct event ev_sigchld;
static struct event ev_sigcont;
static struct event ev_sigterm;
static struct event ev_sigusr1;
static struct event ev_sigwinch;
void
set_signals(void (*handler)(int, short, void *), void *arg)
{
struct sigaction sigact;
memset(&sigact, 0, sizeof sigact);
sigemptyset(&sigact.sa_mask);
sigact.sa_flags = SA_RESTART;
sigact.sa_handler = SIG_IGN;
if (sigaction(SIGINT, &sigact, NULL) != 0)
fatal("sigaction failed");
if (sigaction(SIGPIPE, &sigact, NULL) != 0)
fatal("sigaction failed");
if (sigaction(SIGUSR2, &sigact, NULL) != 0)
fatal("sigaction failed");
if (sigaction(SIGTSTP, &sigact, NULL) != 0)
fatal("sigaction failed");
signal_set(&ev_sighup, SIGHUP, handler, arg);
signal_add(&ev_sighup, NULL);
signal_set(&ev_sigchld, SIGCHLD, handler, arg);
signal_add(&ev_sigchld, NULL);
signal_set(&ev_sigcont, SIGCONT, handler, arg);
signal_add(&ev_sigcont, NULL);
signal_set(&ev_sigterm, SIGTERM, handler, arg);
signal_add(&ev_sigterm, NULL);
signal_set(&ev_sigusr1, SIGUSR1, handler, arg);
signal_add(&ev_sigusr1, NULL);
signal_set(&ev_sigwinch, SIGWINCH, handler, arg);
signal_add(&ev_sigwinch, NULL);
}
void
clear_signals(int after_fork)
{
struct sigaction sigact;
memset(&sigact, 0, sizeof sigact);
sigemptyset(&sigact.sa_mask);
sigact.sa_flags = SA_RESTART;
sigact.sa_handler = SIG_DFL;
if (sigaction(SIGINT, &sigact, NULL) != 0)
fatal("sigaction failed");
if (sigaction(SIGPIPE, &sigact, NULL) != 0)
fatal("sigaction failed");
if (sigaction(SIGUSR2, &sigact, NULL) != 0)
fatal("sigaction failed");
if (sigaction(SIGTSTP, &sigact, NULL) != 0)
fatal("sigaction failed");
if (after_fork) {
if (sigaction(SIGHUP, &sigact, NULL) != 0)
fatal("sigaction failed");
if (sigaction(SIGCHLD, &sigact, NULL) != 0)
fatal("sigaction failed");
if (sigaction(SIGCONT, &sigact, NULL) != 0)
fatal("sigaction failed");
if (sigaction(SIGTERM, &sigact, NULL) != 0)
fatal("sigaction failed");
if (sigaction(SIGUSR1, &sigact, NULL) != 0)
fatal("sigaction failed");
if (sigaction(SIGWINCH, &sigact, NULL) != 0)
fatal("sigaction failed");
} else {
event_del(&ev_sighup);
event_del(&ev_sigchld);
event_del(&ev_sigcont);
event_del(&ev_sigterm);
event_del(&ev_sigusr1);
event_del(&ev_sigwinch);
}
}

154
status.c
View File

@@ -151,7 +151,7 @@ status_timer_callback(__unused int fd, __unused short events, void *arg)
struct session *s = c->session; struct session *s = c->session;
struct timeval tv; struct timeval tv;
evtimer_del(&c->status_timer); evtimer_del(&c->status.timer);
if (s == NULL) if (s == NULL)
return; return;
@@ -163,7 +163,7 @@ status_timer_callback(__unused int fd, __unused short events, void *arg)
tv.tv_sec = options_get_number(s->options, "status-interval"); tv.tv_sec = options_get_number(s->options, "status-interval");
if (tv.tv_sec != 0) if (tv.tv_sec != 0)
evtimer_add(&c->status_timer, &tv); evtimer_add(&c->status.timer, &tv);
log_debug("client %p, status interval %d", c, (int)tv.tv_sec); log_debug("client %p, status interval %d", c, (int)tv.tv_sec);
} }
@@ -173,10 +173,10 @@ status_timer_start(struct client *c)
{ {
struct session *s = c->session; struct session *s = c->session;
if (event_initialized(&c->status_timer)) if (event_initialized(&c->status.timer))
evtimer_del(&c->status_timer); evtimer_del(&c->status.timer);
else else
evtimer_set(&c->status_timer, status_timer_callback, c); evtimer_set(&c->status.timer, status_timer_callback, c);
if (s != NULL && options_get_number(s->options, "status")) if (s != NULL && options_get_number(s->options, "status"))
status_timer_callback(-1, 0, c); status_timer_callback(-1, 0, c);
@@ -210,9 +210,24 @@ status_at_line(struct client *c)
{ {
struct session *s = c->session; struct session *s = c->session;
if (c->flags & CLIENT_STATUSOFF)
return (-1);
if (s->statusat != 1) if (s->statusat != 1)
return (s->statusat); return (s->statusat);
return (c->tty.sy - 1); return (c->tty.sy - status_line_size(s));
}
/*
* Get size of status line for session. 0 means off. Note that status line may
* be forced off for an individual client if it is too small (the
* CLIENT_STATUSOFF flag is set for this).
*/
u_int
status_line_size(struct session *s)
{
if (s->statusat == -1)
return (0);
return (1);
} }
/* Retrieve options for left string. */ /* Retrieve options for left string. */
@@ -296,13 +311,21 @@ status_redraw(struct client *c)
time_t t; time_t t;
char *left, *right; char *left, *right;
const char *sep; const char *sep;
u_int offset, needed; u_int offset, needed, lines;
u_int wlstart, wlwidth, wlavailable, wloffset, wlsize; u_int wlstart, wlwidth, wlavailable, wloffset, wlsize;
size_t llen, rlen, seplen; size_t llen, rlen, seplen;
int larrow, rarrow; int larrow, rarrow;
/* Delete the saved status line, if any. */
if (c->status.old_status != NULL) {
screen_free(c->status.old_status);
free(c->status.old_status);
c->status.old_status = NULL;
}
/* No status line? */ /* No status line? */
if (c->tty.sy == 0 || !options_get_number(s->options, "status")) lines = status_line_size(s);
if (c->tty.sy == 0 || lines == 0)
return (1); return (1);
left = right = NULL; left = right = NULL;
larrow = rarrow = 0; larrow = rarrow = 0;
@@ -314,15 +337,15 @@ status_redraw(struct client *c)
style_apply(&stdgc, s->options, "status-style"); style_apply(&stdgc, s->options, "status-style");
/* Create the target screen. */ /* Create the target screen. */
memcpy(&old_status, &c->status, sizeof old_status); memcpy(&old_status, &c->status.status, sizeof old_status);
screen_init(&c->status, c->tty.sx, 1, 0); screen_init(&c->status.status, c->tty.sx, lines, 0);
screen_write_start(&ctx, NULL, &c->status); screen_write_start(&ctx, NULL, &c->status.status);
for (offset = 0; offset < c->tty.sx; offset++) for (offset = 0; offset < lines * c->tty.sx; offset++)
screen_write_putc(&ctx, &stdgc, ' '); screen_write_putc(&ctx, &stdgc, ' ');
screen_write_stop(&ctx); screen_write_stop(&ctx);
/* If the height is one line, blank status line. */ /* If the height is too small, blank status line. */
if (c->tty.sy <= 1) if (c->tty.sy < lines)
goto out; goto out;
/* Work out left and right strings. */ /* Work out left and right strings. */
@@ -439,7 +462,7 @@ status_redraw(struct client *c)
draw: draw:
/* Begin drawing. */ /* Begin drawing. */
screen_write_start(&ctx, NULL, &c->status); screen_write_start(&ctx, NULL, &c->status.status);
/* Draw the left string and arrow. */ /* Draw the left string and arrow. */
screen_write_cursormove(&ctx, 0, 0); screen_write_cursormove(&ctx, 0, 0);
@@ -485,8 +508,7 @@ draw:
/* Copy the window list. */ /* Copy the window list. */
c->wlmouse = -wloffset + wlstart; c->wlmouse = -wloffset + wlstart;
screen_write_cursormove(&ctx, wloffset, 0); screen_write_cursormove(&ctx, wloffset, 0);
screen_write_copy(&ctx, &window_list, wlstart, 0, wlwidth, 1, NULL, screen_write_fast_copy(&ctx, &window_list, wlstart, 0, wlwidth, 1);
NULL);
screen_free(&window_list); screen_free(&window_list);
screen_write_stop(&ctx); screen_write_stop(&ctx);
@@ -495,7 +517,7 @@ out:
free(left); free(left);
free(right); free(right);
if (grid_compare(c->status.grid, old_status.grid) == 0) { if (grid_compare(c->status.status.grid, old_status.grid) == 0) {
screen_free(&old_status); screen_free(&old_status);
return (0); return (0);
} }
@@ -568,6 +590,13 @@ status_message_set(struct client *c, const char *fmt, ...)
status_message_clear(c); status_message_clear(c);
if (c->status.old_status == NULL) {
c->status.old_status = xmalloc(sizeof *c->status.old_status);
memcpy(c->status.old_status, &c->status.status,
sizeof *c->status.old_status);
screen_init(&c->status.status, c->tty.sx, 1, 0);
}
va_start(ap, fmt); va_start(ap, fmt);
xvasprintf(&c->message_string, fmt, ap); xvasprintf(&c->message_string, fmt, ap);
va_end(ap); va_end(ap);
@@ -603,7 +632,7 @@ status_message_clear(struct client *c)
c->tty.flags &= ~(TTY_NOCURSOR|TTY_FREEZE); c->tty.flags &= ~(TTY_NOCURSOR|TTY_FREEZE);
c->flags |= CLIENT_REDRAW; /* screen was frozen and may have changed */ c->flags |= CLIENT_REDRAW; /* screen was frozen and may have changed */
screen_reinit(&c->status); screen_reinit(&c->status.status);
} }
/* Clear status line message after timer expires. */ /* Clear status line message after timer expires. */
@@ -624,11 +653,18 @@ status_message_redraw(struct client *c)
struct screen old_status; struct screen old_status;
size_t len; size_t len;
struct grid_cell gc; struct grid_cell gc;
u_int lines, offset;
if (c->tty.sx == 0 || c->tty.sy == 0) if (c->tty.sx == 0 || c->tty.sy == 0)
return (0); return (0);
memcpy(&old_status, &c->status, sizeof old_status); memcpy(&old_status, &c->status.status, sizeof old_status);
screen_init(&c->status, c->tty.sx, 1, 0);
lines = status_line_size(c->session);
if (lines <= 1) {
lines = 1;
screen_init(&c->status.status, c->tty.sx, 1, 0);
} else
screen_init(&c->status.status, c->tty.sx, lines, 0);
len = screen_write_strlen("%s", c->message_string); len = screen_write_strlen("%s", c->message_string);
if (len > c->tty.sx) if (len > c->tty.sx)
@@ -636,16 +672,15 @@ status_message_redraw(struct client *c)
style_apply(&gc, s->options, "message-style"); style_apply(&gc, s->options, "message-style");
screen_write_start(&ctx, NULL, &c->status); screen_write_start(&ctx, NULL, &c->status.status);
screen_write_cursormove(&ctx, 0, 0); screen_write_cursormove(&ctx, 0, 0);
screen_write_nputs(&ctx, len, &gc, "%s", c->message_string); for (offset = 0; offset < lines * c->tty.sx; offset++)
for (; len < c->tty.sx; len++)
screen_write_putc(&ctx, &gc, ' '); screen_write_putc(&ctx, &gc, ' ');
screen_write_cursormove(&ctx, 0, lines - 1);
screen_write_nputs(&ctx, len, &gc, "%s", c->message_string);
screen_write_stop(&ctx); screen_write_stop(&ctx);
if (grid_compare(c->status.grid, old_status.grid) == 0) { if (grid_compare(c->status.status.grid, old_status.grid) == 0) {
screen_free(&old_status); screen_free(&old_status);
return (0); return (0);
} }
@@ -656,8 +691,7 @@ status_message_redraw(struct client *c)
/* Enable status line prompt. */ /* Enable status line prompt. */
void void
status_prompt_set(struct client *c, const char *msg, const char *input, status_prompt_set(struct client *c, const char *msg, const char *input,
int (*callbackfn)(void *, const char *, int), void (*freefn)(void *), prompt_input_cb inputcb, prompt_free_cb freecb, void *data, int flags)
void *data, int flags)
{ {
struct format_tree *ft; struct format_tree *ft;
time_t t; time_t t;
@@ -665,20 +699,32 @@ status_prompt_set(struct client *c, const char *msg, const char *input,
ft = format_create(c, NULL, FORMAT_NONE, 0); ft = format_create(c, NULL, FORMAT_NONE, 0);
format_defaults(ft, c, NULL, NULL, NULL); format_defaults(ft, c, NULL, NULL, NULL);
t = time(NULL); t = time(NULL);
tmp = format_expand_time(ft, input, t);
if (input == NULL)
input = "";
if (flags & PROMPT_NOFORMAT)
tmp = xstrdup(input);
else
tmp = format_expand_time(ft, input, t);
status_message_clear(c); status_message_clear(c);
status_prompt_clear(c); status_prompt_clear(c);
if (c->status.old_status == NULL) {
c->status.old_status = xmalloc(sizeof *c->status.old_status);
memcpy(c->status.old_status, &c->status.status,
sizeof *c->status.old_status);
screen_init(&c->status.status, c->tty.sx, 1, 0);
}
c->prompt_string = format_expand_time(ft, msg, t); c->prompt_string = format_expand_time(ft, msg, t);
c->prompt_buffer = utf8_fromcstr(tmp); c->prompt_buffer = utf8_fromcstr(tmp);
c->prompt_index = utf8_strlen(c->prompt_buffer); c->prompt_index = utf8_strlen(c->prompt_buffer);
c->prompt_callbackfn = callbackfn; c->prompt_inputcb = inputcb;
c->prompt_freefn = freefn; c->prompt_freecb = freecb;
c->prompt_data = data; c->prompt_data = data;
c->prompt_hindex = 0; c->prompt_hindex = 0;
@@ -692,7 +738,7 @@ status_prompt_set(struct client *c, const char *msg, const char *input,
if ((flags & PROMPT_INCREMENTAL) && *tmp != '\0') { if ((flags & PROMPT_INCREMENTAL) && *tmp != '\0') {
xasprintf(&cp, "=%s", tmp); xasprintf(&cp, "=%s", tmp);
c->prompt_callbackfn(c->prompt_data, cp, 0); c->prompt_inputcb(c, c->prompt_data, cp, 0);
free(cp); free(cp);
} }
@@ -707,8 +753,8 @@ status_prompt_clear(struct client *c)
if (c->prompt_string == NULL) if (c->prompt_string == NULL)
return; return;
if (c->prompt_freefn != NULL && c->prompt_data != NULL) if (c->prompt_freecb != NULL && c->prompt_data != NULL)
c->prompt_freefn(c->prompt_data); c->prompt_freecb(c->prompt_data);
free(c->prompt_string); free(c->prompt_string);
c->prompt_string = NULL; c->prompt_string = NULL;
@@ -719,7 +765,7 @@ status_prompt_clear(struct client *c)
c->tty.flags &= ~(TTY_NOCURSOR|TTY_FREEZE); c->tty.flags &= ~(TTY_NOCURSOR|TTY_FREEZE);
c->flags |= CLIENT_REDRAW; /* screen was frozen and may have changed */ c->flags |= CLIENT_REDRAW; /* screen was frozen and may have changed */
screen_reinit(&c->status); screen_reinit(&c->status.status);
} }
/* Update status line prompt with a new prompt string. */ /* Update status line prompt with a new prompt string. */
@@ -759,12 +805,19 @@ status_prompt_redraw(struct client *c)
struct session *s = c->session; struct session *s = c->session;
struct screen old_status; struct screen old_status;
u_int i, offset, left, start, pcursor, pwidth, width; u_int i, offset, left, start, pcursor, pwidth, width;
u_int lines;
struct grid_cell gc, cursorgc; struct grid_cell gc, cursorgc;
if (c->tty.sx == 0 || c->tty.sy == 0) if (c->tty.sx == 0 || c->tty.sy == 0)
return (0); return (0);
memcpy(&old_status, &c->status, sizeof old_status); memcpy(&old_status, &c->status.status, sizeof old_status);
screen_init(&c->status, c->tty.sx, 1, 0);
lines = status_line_size(c->session);
if (lines <= 1) {
lines = 1;
screen_init(&c->status.status, c->tty.sx, 1, 0);
} else
screen_init(&c->status.status, c->tty.sx, lines, 0);
if (c->prompt_mode == PROMPT_COMMAND) if (c->prompt_mode == PROMPT_COMMAND)
style_apply(&gc, s->options, "message-command-style"); style_apply(&gc, s->options, "message-command-style");
@@ -778,11 +831,12 @@ status_prompt_redraw(struct client *c)
if (start > c->tty.sx) if (start > c->tty.sx)
start = c->tty.sx; start = c->tty.sx;
screen_write_start(&ctx, NULL, &c->status); screen_write_start(&ctx, NULL, &c->status.status);
screen_write_cursormove(&ctx, 0, 0);
for (offset = 0; offset < lines * c->tty.sx; offset++)
screen_write_putc(&ctx, &gc, ' ');
screen_write_cursormove(&ctx, 0, 0); screen_write_cursormove(&ctx, 0, 0);
screen_write_nputs(&ctx, start, &gc, "%s", c->prompt_string); screen_write_nputs(&ctx, start, &gc, "%s", c->prompt_string);
while (c->status.cx < screen_size_x(&c->status))
screen_write_putc(&ctx, &gc, ' ');
screen_write_cursormove(&ctx, start, 0); screen_write_cursormove(&ctx, start, 0);
left = c->tty.sx - start; left = c->tty.sx - start;
@@ -823,13 +877,14 @@ status_prompt_redraw(struct client *c)
screen_write_cell(&ctx, &cursorgc); screen_write_cell(&ctx, &cursorgc);
} }
} }
if (c->status.cx < screen_size_x(&c->status) && c->prompt_index >= i) if (c->status.status.cx < screen_size_x(&c->status.status) &&
c->prompt_index >= i)
screen_write_putc(&ctx, &cursorgc, ' '); screen_write_putc(&ctx, &cursorgc, ' ');
finished: finished:
screen_write_stop(&ctx); screen_write_stop(&ctx);
if (grid_compare(c->status.grid, old_status.grid) == 0) { if (grid_compare(c->status.status.grid, old_status.grid) == 0) {
screen_free(&old_status); screen_free(&old_status);
return (0); return (0);
} }
@@ -995,7 +1050,7 @@ status_prompt_key(struct client *c, key_code key)
if (key >= '0' && key <= '9') if (key >= '0' && key <= '9')
goto append_key; goto append_key;
s = utf8_tocstr(c->prompt_buffer); s = utf8_tocstr(c->prompt_buffer);
c->prompt_callbackfn(c->prompt_data, s, 1); c->prompt_inputcb(c, c->prompt_data, s, 1);
status_prompt_clear(c); status_prompt_clear(c);
free(s); free(s);
return (1); return (1);
@@ -1276,13 +1331,14 @@ process_key:
s = utf8_tocstr(c->prompt_buffer); s = utf8_tocstr(c->prompt_buffer);
if (*s != '\0') if (*s != '\0')
status_prompt_add_history(s); status_prompt_add_history(s);
if (c->prompt_callbackfn(c->prompt_data, s, 1) == 0) if (c->prompt_inputcb(c, c->prompt_data, s, 1) == 0)
status_prompt_clear(c); status_prompt_clear(c);
free(s); free(s);
break; break;
case '\033': /* Escape */ case '\033': /* Escape */
case '\003': /* C-c */ case '\003': /* C-c */
if (c->prompt_callbackfn(c->prompt_data, NULL, 1) == 0) case '\007': /* C-g */
if (c->prompt_inputcb(c, c->prompt_data, NULL, 1) == 0)
status_prompt_clear(c); status_prompt_clear(c);
break; break;
case '\022': /* C-r */ case '\022': /* C-r */
@@ -1330,7 +1386,7 @@ append_key:
s = utf8_tocstr(c->prompt_buffer); s = utf8_tocstr(c->prompt_buffer);
if (strlen(s) != 1) if (strlen(s) != 1)
status_prompt_clear(c); status_prompt_clear(c);
else if (c->prompt_callbackfn(c->prompt_data, s, 1) == 0) else if (c->prompt_inputcb(c, c->prompt_data, s, 1) == 0)
status_prompt_clear(c); status_prompt_clear(c);
free(s); free(s);
} }
@@ -1340,7 +1396,7 @@ changed:
if (c->prompt_flags & PROMPT_INCREMENTAL) { if (c->prompt_flags & PROMPT_INCREMENTAL) {
s = utf8_tocstr(c->prompt_buffer); s = utf8_tocstr(c->prompt_buffer);
xasprintf(&cp, "%c%s", prefix, s); xasprintf(&cp, "%c%s", prefix, s);
c->prompt_callbackfn(c->prompt_data, cp, 0); c->prompt_inputcb(c, c->prompt_data, cp, 0);
free(cp); free(cp);
free(s); free(s);
} }

607
tmux.1

File diff suppressed because it is too large Load Diff

56
tmux.c
View File

@@ -41,9 +41,10 @@ struct hooks *global_hooks;
struct timeval start_time; struct timeval start_time;
const char *socket_path; const char *socket_path;
int ptm_fd = -1; int ptm_fd = -1;
const char *shell_command;
static __dead void usage(void); static __dead void usage(void);
static char *make_label(const char *); static char *make_label(const char *, char **);
static const char *getshell(void); static const char *getshell(void);
static int checkshell(const char *); static int checkshell(const char *);
@@ -105,12 +106,13 @@ areshell(const char *shell)
} }
static char * static char *
make_label(const char *label) make_label(const char *label, char **cause)
{ {
char *base, resolved[PATH_MAX], *path, *s; char *base, resolved[PATH_MAX], *path, *s;
struct stat sb; struct stat sb;
uid_t uid; uid_t uid;
int saved_errno;
*cause = NULL;
if (label == NULL) if (label == NULL)
label = "default"; label = "default";
@@ -120,11 +122,16 @@ make_label(const char *label)
xasprintf(&base, "%s/tmux-%ld", s, (long)uid); xasprintf(&base, "%s/tmux-%ld", s, (long)uid);
else else
xasprintf(&base, "%s/tmux-%ld", _PATH_TMP, (long)uid); xasprintf(&base, "%s/tmux-%ld", _PATH_TMP, (long)uid);
if (realpath(base, resolved) == NULL &&
if (mkdir(base, S_IRWXU) != 0 && errno != EEXIST) strlcpy(resolved, base, sizeof resolved) >= sizeof resolved) {
errno = ERANGE;
free(base);
goto fail; goto fail;
}
if (lstat(base, &sb) != 0) if (mkdir(resolved, S_IRWXU) != 0 && errno != EEXIST)
goto fail;
if (lstat(resolved, &sb) != 0)
goto fail; goto fail;
if (!S_ISDIR(sb.st_mode)) { if (!S_ISDIR(sb.st_mode)) {
errno = ENOTDIR; errno = ENOTDIR;
@@ -134,18 +141,11 @@ make_label(const char *label)
errno = EACCES; errno = EACCES;
goto fail; goto fail;
} }
if (realpath(base, resolved) == NULL)
strlcpy(resolved, base, sizeof resolved);
xasprintf(&path, "%s/%s", resolved, label); xasprintf(&path, "%s/%s", resolved, label);
free(base);
return (path); return (path);
fail: fail:
saved_errno = errno; xasprintf(cause, "error creating %s (%s)", resolved, strerror(errno));
free(base);
errno = saved_errno;
return (NULL); return (NULL);
} }
@@ -187,13 +187,14 @@ find_home(void)
int int
main(int argc, char **argv) main(int argc, char **argv)
{ {
char *path, *label, tmp[PATH_MAX]; char *path, *label, *cause, **var;
char *shellcmd = NULL, **var; char tmp[PATH_MAX];
const char *s, *shell; const char *s, *shell, *cwd;
int opt, flags, keys; int opt, flags, keys;
const struct options_table_entry *oe; const struct options_table_entry *oe;
if (setlocale(LC_CTYPE, "en_US.UTF-8") == NULL) { if (setlocale(LC_CTYPE, "en_US.UTF-8") == NULL &&
setlocale(LC_CTYPE, "C.UTF-8") == NULL) {
if (setlocale(LC_CTYPE, "") == NULL) if (setlocale(LC_CTYPE, "") == NULL)
errx(1, "invalid LC_ALL, LC_CTYPE or LANG"); errx(1, "invalid LC_ALL, LC_CTYPE or LANG");
s = nl_langinfo(CODESET); s = nl_langinfo(CODESET);
@@ -216,8 +217,7 @@ main(int argc, char **argv)
flags |= CLIENT_256COLOURS; flags |= CLIENT_256COLOURS;
break; break;
case 'c': case 'c':
free(shellcmd); shell_command = optarg;
shellcmd = xstrdup(optarg);
break; break;
case 'C': case 'C':
if (flags & CLIENT_CONTROL) if (flags & CLIENT_CONTROL)
@@ -257,7 +257,7 @@ main(int argc, char **argv)
argc -= optind; argc -= optind;
argv += optind; argv += optind;
if (shellcmd != NULL && argc != 0) if (shell_command != NULL && argc != 0)
usage(); usage();
if ((ptm_fd = getptmfd()) == -1) if ((ptm_fd = getptmfd()) == -1)
@@ -293,8 +293,9 @@ main(int argc, char **argv)
global_environ = environ_create(); global_environ = environ_create();
for (var = environ; *var != NULL; var++) for (var = environ; *var != NULL; var++)
environ_put(global_environ, *var); environ_put(global_environ, *var);
if (getcwd(tmp, sizeof tmp) != NULL) if ((cwd = getenv("PWD")) == NULL &&
environ_set(global_environ, "PWD", "%s", tmp); (cwd = getcwd(tmp, sizeof tmp)) != NULL)
environ_set(global_environ, "PWD", "%s", cwd);
global_options = options_create(NULL); global_options = options_create(NULL);
global_s_options = options_create(NULL); global_s_options = options_create(NULL);
@@ -339,13 +340,16 @@ main(int argc, char **argv)
path[strcspn(path, ",")] = '\0'; path[strcspn(path, ",")] = '\0';
} }
} }
if (path == NULL && (path = make_label(label)) == NULL) { if (path == NULL && (path = make_label(label, &cause)) == NULL) {
fprintf(stderr, "can't create socket: %s\n", strerror(errno)); if (cause != NULL) {
fprintf(stderr, "%s\n", cause);
free(cause);
}
exit(1); exit(1);
} }
socket_path = path; socket_path = path;
free(label); free(label);
/* Pass control to the client. */ /* Pass control to the client. */
exit(client_main(osdep_event_init(), argc, argv, flags, shellcmd)); exit(client_main(osdep_event_init(), argc, argv, flags));
} }

405
tmux.h
View File

@@ -42,12 +42,13 @@ extern char **environ;
struct args; struct args;
struct client; struct client;
struct cmd_find_state;
struct cmdq_item; struct cmdq_item;
struct cmdq_list; struct cmdq_list;
struct environ; struct environ;
struct format_job_tree; struct format_job_tree;
struct input_ctx; struct input_ctx;
struct mode_key_cmdstr; struct mode_tree_data;
struct mouse_event; struct mouse_event;
struct options; struct options;
struct options_entry; struct options_entry;
@@ -80,21 +81,30 @@ struct tmuxproc;
#define nitems(_a) (sizeof((_a)) / sizeof((_a)[0])) #define nitems(_a) (sizeof((_a)) / sizeof((_a)[0]))
#endif #endif
/* Bell option values. */ /* Alert option values. */
#define BELL_NONE 0 #define ALERT_NONE 0
#define BELL_ANY 1 #define ALERT_ANY 1
#define BELL_CURRENT 2 #define ALERT_CURRENT 2
#define BELL_OTHER 3 #define ALERT_OTHER 3
/* Visual option values. */
#define VISUAL_OFF 0
#define VISUAL_ON 1
#define VISUAL_BOTH 2
/* Special key codes. */ /* Special key codes. */
#define KEYC_NONE 0xffff00000000ULL #define KEYC_NONE 0xffff00000000ULL
#define KEYC_UNKNOWN 0xfffe00000000ULL #define KEYC_UNKNOWN 0xfffe00000000ULL
#define KEYC_BASE 0x000010000000ULL #define KEYC_BASE 0x000010000000ULL
#define KEYC_USER 0x000020000000ULL
/* Available user keys. */
#define KEYC_NUSER 1000
/* Key modifier bits. */ /* Key modifier bits. */
#define KEYC_ESCAPE 0x200000000000ULL #define KEYC_ESCAPE 0x200000000000ULL
#define KEYC_CTRL 0x400000000000ULL #define KEYC_CTRL 0x400000000000ULL
#define KEYC_SHIFT 0x800000000000ULL #define KEYC_SHIFT 0x800000000000ULL
#define KEYC_XTERM 0x1000000000000ULL #define KEYC_XTERM 0x1000000000000ULL
/* Mask to obtain key w/o modifiers. */ /* Mask to obtain key w/o modifiers. */
@@ -130,6 +140,10 @@ enum {
KEYC_FOCUS_IN = KEYC_BASE, KEYC_FOCUS_IN = KEYC_BASE,
KEYC_FOCUS_OUT, KEYC_FOCUS_OUT,
/* Paste brackets. */
KEYC_PASTE_START,
KEYC_PASTE_END,
/* Mouse keys. */ /* Mouse keys. */
KEYC_MOUSE, /* unclassified mouse event */ KEYC_MOUSE, /* unclassified mouse event */
KEYC_DRAGGING, /* dragging in progress */ KEYC_DRAGGING, /* dragging in progress */
@@ -207,67 +221,67 @@ enum {
/* Termcap codes. */ /* Termcap codes. */
enum tty_code_code { enum tty_code_code {
TTYC_AX = 0, TTYC_AX = 0,
TTYC_ACSC, /* acs_chars, ac */ TTYC_ACSC,
TTYC_BCE, /* back_color_erase, ut */ TTYC_BCE,
TTYC_BEL, /* bell, bl */ TTYC_BEL,
TTYC_BLINK, /* enter_blink_mode, mb */ TTYC_BLINK,
TTYC_BOLD, /* enter_bold_mode, md */ TTYC_BOLD,
TTYC_CIVIS, /* cursor_invisible, vi */ TTYC_CIVIS,
TTYC_CLEAR, /* clear_screen, cl */ TTYC_CLEAR,
TTYC_CNORM, /* cursor_normal, ve */ TTYC_CNORM,
TTYC_COLORS, /* max_colors, Co */ TTYC_COLORS,
TTYC_CR, /* restore cursor colour, Cr */ TTYC_CR,
TTYC_CS, /* set cursor colour, Cs */ TTYC_CS,
TTYC_CSR, /* change_scroll_region, cs */ TTYC_CSR,
TTYC_CUB, /* parm_left_cursor, LE */ TTYC_CUB,
TTYC_CUB1, /* cursor_left, le */ TTYC_CUB1,
TTYC_CUD, /* parm_down_cursor, DO */ TTYC_CUD,
TTYC_CUD1, /* cursor_down, do */ TTYC_CUD1,
TTYC_CUF, /* parm_right_cursor, RI */ TTYC_CUF,
TTYC_CUF1, /* cursor_right, nd */ TTYC_CUF1,
TTYC_CUP, /* cursor_address, cm */ TTYC_CUP,
TTYC_CUU, /* parm_up_cursor, UP */ TTYC_CUU,
TTYC_CUU1, /* cursor_up, up */ TTYC_CUU1,
TTYC_CVVIS, /* cursor_visible, vs */ TTYC_CVVIS,
TTYC_DCH, /* parm_dch, DC */ TTYC_DCH,
TTYC_DCH1, /* delete_character, dc */ TTYC_DCH1,
TTYC_DIM, /* enter_dim_mode, mh */ TTYC_DIM,
TTYC_DL, /* parm_delete_line, DL */ TTYC_DL,
TTYC_DL1, /* delete_line, dl */ TTYC_DL1,
TTYC_E3, TTYC_E3,
TTYC_ECH, /* erase_chars, ec */ TTYC_ECH,
TTYC_ED, /* clr_eos, cd */ TTYC_ED,
TTYC_EL, /* clr_eol, ce */ TTYC_EL,
TTYC_EL1, /* clr_bol, cb */ TTYC_EL1,
TTYC_ENACS, /* ena_acs, eA */ TTYC_ENACS,
TTYC_FSL, /* from_status_line, fsl */ TTYC_FSL,
TTYC_HOME, /* cursor_home, ho */ TTYC_HOME,
TTYC_HPA, /* column_address, ch */ TTYC_HPA,
TTYC_ICH, /* parm_ich, IC */ TTYC_ICH,
TTYC_ICH1, /* insert_character, ic */ TTYC_ICH1,
TTYC_IL, /* parm_insert_line, IL */ TTYC_IL,
TTYC_IL1, /* insert_line, il */ TTYC_IL1,
TTYC_INDN, /* parm_index, indn */ TTYC_INDN,
TTYC_INVIS, /* enter_secure_mode, mk */ TTYC_INVIS,
TTYC_KCBT, /* key_btab, kB */ TTYC_KCBT,
TTYC_KCUB1, /* key_left, kl */ TTYC_KCUB1,
TTYC_KCUD1, /* key_down, kd */ TTYC_KCUD1,
TTYC_KCUF1, /* key_right, kr */ TTYC_KCUF1,
TTYC_KCUU1, /* key_up, ku */ TTYC_KCUU1,
TTYC_KDC2, TTYC_KDC2,
TTYC_KDC3, TTYC_KDC3,
TTYC_KDC4, TTYC_KDC4,
TTYC_KDC5, TTYC_KDC5,
TTYC_KDC6, TTYC_KDC6,
TTYC_KDC7, TTYC_KDC7,
TTYC_KDCH1, /* key_dc, kD */ TTYC_KDCH1,
TTYC_KDN2, TTYC_KDN2,
TTYC_KDN3, TTYC_KDN3,
TTYC_KDN4, TTYC_KDN4,
TTYC_KDN5, TTYC_KDN5,
TTYC_KDN6, TTYC_KDN6,
TTYC_KDN7, TTYC_KDN7,
TTYC_KEND, /* key_end, ke */ TTYC_KEND,
TTYC_KEND2, TTYC_KEND2,
TTYC_KEND3, TTYC_KEND3,
TTYC_KEND4, TTYC_KEND4,
@@ -343,35 +357,37 @@ enum tty_code_code {
TTYC_KHOM5, TTYC_KHOM5,
TTYC_KHOM6, TTYC_KHOM6,
TTYC_KHOM7, TTYC_KHOM7,
TTYC_KHOME, /* key_home, kh */ TTYC_KHOME,
TTYC_KIC2, TTYC_KIC2,
TTYC_KIC3, TTYC_KIC3,
TTYC_KIC4, TTYC_KIC4,
TTYC_KIC5, TTYC_KIC5,
TTYC_KIC6, TTYC_KIC6,
TTYC_KIC7, TTYC_KIC7,
TTYC_KICH1, /* key_ic, kI */ TTYC_KICH1,
TTYC_KIND,
TTYC_KLFT2, TTYC_KLFT2,
TTYC_KLFT3, TTYC_KLFT3,
TTYC_KLFT4, TTYC_KLFT4,
TTYC_KLFT5, TTYC_KLFT5,
TTYC_KLFT6, TTYC_KLFT6,
TTYC_KLFT7, TTYC_KLFT7,
TTYC_KMOUS, /* key_mouse, Km */ TTYC_KMOUS,
TTYC_KNP, /* key_npage, kN */ TTYC_KNP,
TTYC_KNXT2, TTYC_KNXT2,
TTYC_KNXT3, TTYC_KNXT3,
TTYC_KNXT4, TTYC_KNXT4,
TTYC_KNXT5, TTYC_KNXT5,
TTYC_KNXT6, TTYC_KNXT6,
TTYC_KNXT7, TTYC_KNXT7,
TTYC_KPP, /* key_ppage, kP */ TTYC_KPP,
TTYC_KPRV2, TTYC_KPRV2,
TTYC_KPRV3, TTYC_KPRV3,
TTYC_KPRV4, TTYC_KPRV4,
TTYC_KPRV5, TTYC_KPRV5,
TTYC_KPRV6, TTYC_KPRV6,
TTYC_KPRV7, TTYC_KPRV7,
TTYC_KRI,
TTYC_KRIT2, TTYC_KRIT2,
TTYC_KRIT3, TTYC_KRIT3,
TTYC_KRIT4, TTYC_KRIT4,
@@ -384,30 +400,34 @@ enum tty_code_code {
TTYC_KUP5, TTYC_KUP5,
TTYC_KUP6, TTYC_KUP6,
TTYC_KUP7, TTYC_KUP7,
TTYC_MS, /* modify xterm(1) selection */ TTYC_MS,
TTYC_OP, /* orig_pair, op */ TTYC_OP,
TTYC_REV, /* enter_reverse_mode, mr */ TTYC_REV,
TTYC_RI, /* scroll_reverse, sr */ TTYC_RGB,
TTYC_RMACS, /* exit_alt_charset_mode */ TTYC_RI,
TTYC_RMCUP, /* exit_ca_mode, te */ TTYC_RMACS,
TTYC_RMKX, /* keypad_local, ke */ TTYC_RMCUP,
TTYC_SE, /* reset cursor style, Se */ TTYC_RMKX,
TTYC_SETAB, /* set_a_background, AB */ TTYC_SE,
TTYC_SETAF, /* set_a_foreground, AF */ TTYC_SETAB,
TTYC_SGR0, /* exit_attribute_mode, me */ TTYC_SETAF,
TTYC_SITM, /* enter_italics_mode, it */ TTYC_SETRGBB,
TTYC_SMACS, /* enter_alt_charset_mode, as */ TTYC_SETRGBF,
TTYC_SMCUP, /* enter_ca_mode, ti */ TTYC_SGR0,
TTYC_SMKX, /* keypad_xmit, ks */ TTYC_SITM,
TTYC_SMSO, /* enter_standout_mode, so */ TTYC_SMACS,
TTYC_SMUL, /* enter_underline_mode, us */ TTYC_SMCUP,
TTYC_SMKX,
TTYC_SMSO,
TTYC_SMUL,
TTYC_SMXX, TTYC_SMXX,
TTYC_SS, /* set cursor style, Ss */ TTYC_SS,
TTYC_TC, /* 24-bit "true" colour, Tc */ TTYC_TC,
TTYC_TSL, /* to_status_line, tsl */ TTYC_TSL,
TTYC_VPA, /* row_address, cv */ TTYC_U8,
TTYC_XENL, /* eat_newline_glitch, xn */ TTYC_VPA,
TTYC_XT, /* xterm(1)-compatible title, XT */ TTYC_XENL,
TTYC_XT,
}; };
/* Message codes. */ /* Message codes. */
@@ -535,6 +555,7 @@ enum utf8_state {
/* Grid line flags. */ /* Grid line flags. */
#define GRID_LINE_WRAPPED 0x1 #define GRID_LINE_WRAPPED 0x1
#define GRID_LINE_EXTENDED 0x2 #define GRID_LINE_EXTENDED 0x2
#define GRID_LINE_DEAD 0x4
/* Grid cell data. */ /* Grid cell data. */
struct grid_cell { struct grid_cell {
@@ -605,6 +626,9 @@ struct job {
JOB_CLOSED JOB_CLOSED
} state; } state;
int flags;
#define JOB_NOWAIT 0x1
char *cmd; char *cmd;
pid_t pid; pid_t pid;
int status; int status;
@@ -617,7 +641,7 @@ struct job {
job_free_cb freecb; job_free_cb freecb;
void *data; void *data;
LIST_ENTRY(job) entry; LIST_ENTRY(job) entry;
}; };
LIST_HEAD(joblist, job); LIST_HEAD(joblist, job);
@@ -645,8 +669,10 @@ struct screen_sel {
}; };
/* Virtual screen. */ /* Virtual screen. */
struct screen_titles;
struct screen { struct screen {
char *title; char *title;
struct screen_titles *titles;
struct grid *grid; /* grid data */ struct grid *grid; /* grid data */
@@ -676,6 +702,7 @@ struct screen_write_ctx {
struct screen_write_collect_item *item; struct screen_write_collect_item *item;
struct screen_write_collect_line *list; struct screen_write_collect_line *list;
u_int scrolled; u_int scrolled;
u_int bg;
u_int cells; u_int cells;
u_int written; u_int written;
@@ -695,7 +722,8 @@ struct screen_write_ctx {
struct window_mode { struct window_mode {
const char *name; const char *name;
struct screen *(*init)(struct window_pane *); struct screen *(*init)(struct window_pane *, struct cmd_find_state *,
struct args *);
void (*free)(struct window_pane *); void (*free)(struct window_pane *);
void (*resize)(struct window_pane *, u_int, u_int); void (*resize)(struct window_pane *, u_int, u_int);
void (*key)(struct window_pane *, struct client *, void (*key)(struct window_pane *, struct client *,
@@ -743,6 +771,9 @@ struct window_pane {
u_int sx; u_int sx;
u_int sy; u_int sy;
u_int osx;
u_int osy;
u_int xoff; u_int xoff;
u_int yoff; u_int yoff;
@@ -751,9 +782,13 @@ struct window_pane {
#define PANE_DROP 0x2 #define PANE_DROP 0x2
#define PANE_FOCUSED 0x4 #define PANE_FOCUSED 0x4
#define PANE_RESIZE 0x8 #define PANE_RESIZE 0x8
#define PANE_FOCUSPUSH 0x10 #define PANE_RESIZEFORCE 0x10
#define PANE_INPUTOFF 0x20 #define PANE_FOCUSPUSH 0x20
#define PANE_CHANGED 0x40 #define PANE_INPUTOFF 0x40
#define PANE_CHANGED 0x80
#define PANE_EXITED 0x100
#define PANE_STATUSREADY 0x200
#define PANE_STATUSDRAWN 0x400
int argc; int argc;
char **argv; char **argv;
@@ -796,6 +831,7 @@ struct window_pane {
struct event modetimer; struct event modetimer;
time_t modelast; time_t modelast;
u_int modeprefix; u_int modeprefix;
char *searchstr;
TAILQ_ENTRY(window_pane) entry; TAILQ_ENTRY(window_pane) entry;
RB_ENTRY(window_pane) tree_entry; RB_ENTRY(window_pane) tree_entry;
@@ -1284,7 +1320,16 @@ struct cmd_entry {
enum cmd_retval (*exec)(struct cmd *, struct cmdq_item *); enum cmd_retval (*exec)(struct cmd *, struct cmdq_item *);
}; };
/* Status line. */
struct status_line {
struct event timer;
struct screen status;
struct screen *old_status;
};
/* Client connection. */ /* Client connection. */
typedef int (*prompt_input_cb)(struct client *, void *, const char *, int);
typedef void (*prompt_free_cb)(void *);
struct client { struct client {
const char *name; const char *name;
struct tmuxpeer *peer; struct tmuxpeer *peer;
@@ -1324,8 +1369,7 @@ struct client {
struct event click_timer; struct event click_timer;
u_int click_button; u_int click_button;
struct event status_timer; struct status_line status;
struct screen status;
#define CLIENT_TERMINAL 0x1 #define CLIENT_TERMINAL 0x1
#define CLIENT_LOGIN 0x2 #define CLIENT_LOGIN 0x2
@@ -1349,11 +1393,14 @@ struct client {
#define CLIENT_STATUSFORCE 0x80000 #define CLIENT_STATUSFORCE 0x80000
#define CLIENT_DOUBLECLICK 0x100000 #define CLIENT_DOUBLECLICK 0x100000
#define CLIENT_TRIPLECLICK 0x200000 #define CLIENT_TRIPLECLICK 0x200000
#define CLIENT_SIZECHANGED 0x400000
#define CLIENT_STATUSOFF 0x800000
int flags; int flags;
struct key_table *keytable; struct key_table *keytable;
struct event identify_timer; struct event identify_timer;
void (*identify_callback)(struct client *, struct window_pane *); void (*identify_callback)(struct client *,
struct window_pane *);
void *identify_callback_data; void *identify_callback_data;
char *message_string; char *message_string;
@@ -1364,8 +1411,8 @@ struct client {
char *prompt_string; char *prompt_string;
struct utf8_data *prompt_buffer; struct utf8_data *prompt_buffer;
size_t prompt_index; size_t prompt_index;
int (*prompt_callbackfn)(void *, const char *, int); prompt_input_cb prompt_inputcb;
void (*prompt_freefn)(void *); prompt_free_cb prompt_freecb;
void *prompt_data; void *prompt_data;
u_int prompt_hindex; u_int prompt_hindex;
enum { PROMPT_ENTRY, PROMPT_COMMAND } prompt_mode; enum { PROMPT_ENTRY, PROMPT_COMMAND } prompt_mode;
@@ -1373,6 +1420,7 @@ struct client {
#define PROMPT_SINGLE 0x1 #define PROMPT_SINGLE 0x1
#define PROMPT_NUMERIC 0x2 #define PROMPT_NUMERIC 0x2
#define PROMPT_INCREMENTAL 0x4 #define PROMPT_INCREMENTAL 0x4
#define PROMPT_NOFORMAT 0x8
int prompt_flags; int prompt_flags;
struct session *session; struct session *session;
@@ -1462,7 +1510,9 @@ extern struct options *global_w_options;
extern struct environ *global_environ; extern struct environ *global_environ;
extern struct timeval start_time; extern struct timeval start_time;
extern const char *socket_path; extern const char *socket_path;
extern const char *shell_command;
extern int ptm_fd; extern int ptm_fd;
extern const char *shell_command;
int areshell(const char *); int areshell(const char *);
void setblocking(int, int); void setblocking(int, int);
const char *find_home(void); const char *find_home(void);
@@ -1470,15 +1520,16 @@ const char *find_home(void);
/* proc.c */ /* proc.c */
struct imsg; struct imsg;
int proc_send(struct tmuxpeer *, enum msgtype, int, const void *, size_t); int proc_send(struct tmuxpeer *, enum msgtype, int, const void *, size_t);
int proc_send_s(struct tmuxpeer *, enum msgtype, const char *); struct tmuxproc *proc_start(const char *);
struct tmuxproc *proc_start(const char *, struct event_base *, int,
void (*)(int));
void proc_loop(struct tmuxproc *, int (*)(void)); void proc_loop(struct tmuxproc *, int (*)(void));
void proc_exit(struct tmuxproc *); void proc_exit(struct tmuxproc *);
void proc_set_signals(struct tmuxproc *, void(*)(int));
void proc_clear_signals(struct tmuxproc *, int);
struct tmuxpeer *proc_add_peer(struct tmuxproc *, int, struct tmuxpeer *proc_add_peer(struct tmuxproc *, int,
void (*)(struct imsg *, void *), void *); void (*)(struct imsg *, void *), void *);
void proc_remove_peer(struct tmuxpeer *); void proc_remove_peer(struct tmuxpeer *);
void proc_kill_peer(struct tmuxpeer *); void proc_kill_peer(struct tmuxpeer *);
void proc_toggle_log(struct tmuxproc *);
/* cfg.c */ /* cfg.c */
extern int cfg_finished; extern int cfg_finished;
@@ -1512,6 +1563,7 @@ char *paste_make_sample(struct paste_buffer *);
#define FORMAT_PANE 0x80000000U #define FORMAT_PANE 0x80000000U
#define FORMAT_WINDOW 0x40000000U #define FORMAT_WINDOW 0x40000000U
struct format_tree; struct format_tree;
int format_true(const char *);
struct format_tree *format_create(struct client *, struct cmdq_item *, int, struct format_tree *format_create(struct client *, struct cmdq_item *, int,
int); int);
void format_free(struct format_tree *); void format_free(struct format_tree *);
@@ -1542,8 +1594,6 @@ void hooks_add(struct hooks *, const char *, struct cmd_list *);
void hooks_copy(struct hooks *, struct hooks *); void hooks_copy(struct hooks *, struct hooks *);
void hooks_remove(struct hooks *, const char *); void hooks_remove(struct hooks *, const char *);
struct hook *hooks_find(struct hooks *, const char *); struct hook *hooks_find(struct hooks *, const char *);
void printflike(4, 5) hooks_run(struct hooks *, struct client *,
struct cmd_find_state *, const char *, ...);
void printflike(4, 5) hooks_insert(struct hooks *, struct cmdq_item *, void printflike(4, 5) hooks_insert(struct hooks *, struct cmdq_item *,
struct cmd_find_state *, const char *, ...); struct cmd_find_state *, const char *, ...);
@@ -1606,7 +1656,7 @@ extern const struct options_table_entry options_table[];
/* job.c */ /* job.c */
extern struct joblist all_jobs; extern struct joblist all_jobs;
struct job *job_run(const char *, struct session *, const char *, struct job *job_run(const char *, struct session *, const char *,
job_update_cb, job_complete_cb, job_free_cb, void *); job_update_cb, job_complete_cb, job_free_cb, void *, int);
void job_free(struct job *); void job_free(struct job *);
void job_died(struct job *, int); void job_died(struct job *, int);
@@ -1624,7 +1674,7 @@ void environ_put(struct environ *, const char *);
void environ_unset(struct environ *, const char *); void environ_unset(struct environ *, const char *);
void environ_update(struct options *, struct environ *, struct environ *); void environ_update(struct options *, struct environ *, struct environ *);
void environ_push(struct environ *); void environ_push(struct environ *);
void environ_log(struct environ *, const char *); void printflike(2, 3) environ_log(struct environ *, const char *, ...);
struct environ *environ_for_session(struct session *, int); struct environ *environ_for_session(struct session *, int);
/* tty.c */ /* tty.c */
@@ -1639,6 +1689,7 @@ void tty_cursor(struct tty *, u_int, u_int);
void tty_putcode(struct tty *, enum tty_code_code); void tty_putcode(struct tty *, enum tty_code_code);
void tty_putcode1(struct tty *, enum tty_code_code, int); void tty_putcode1(struct tty *, enum tty_code_code, int);
void tty_putcode2(struct tty *, enum tty_code_code, int, int); void tty_putcode2(struct tty *, enum tty_code_code, int, int);
void tty_putcode3(struct tty *, enum tty_code_code, int, int, int);
void tty_putcode_ptr1(struct tty *, enum tty_code_code, const void *); void tty_putcode_ptr1(struct tty *, enum tty_code_code, const void *);
void tty_putcode_ptr2(struct tty *, enum tty_code_code, const void *, void tty_putcode_ptr2(struct tty *, enum tty_code_code, const void *,
const void *); const void *);
@@ -1646,8 +1697,8 @@ void tty_puts(struct tty *, const char *);
void tty_putc(struct tty *, u_char); 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 *);
int tty_resize(struct tty *); void tty_resize(struct tty *);
int tty_set_size(struct tty *, u_int, u_int); void tty_set_size(struct tty *, 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 *);
@@ -1693,6 +1744,8 @@ const char *tty_term_string(struct tty_term *, enum tty_code_code);
const char *tty_term_string1(struct tty_term *, enum tty_code_code, int); const char *tty_term_string1(struct tty_term *, enum tty_code_code, int);
const char *tty_term_string2(struct tty_term *, enum tty_code_code, int, const char *tty_term_string2(struct tty_term *, enum tty_code_code, int,
int); int);
const char *tty_term_string3(struct tty_term *, enum tty_code_code, int,
int, int);
const char *tty_term_ptr1(struct tty_term *, enum tty_code_code, const char *tty_term_ptr1(struct tty_term *, enum tty_code_code,
const void *); const void *);
const char *tty_term_ptr2(struct tty_term *, enum tty_code_code, const char *tty_term_ptr2(struct tty_term *, enum tty_code_code,
@@ -1702,6 +1755,7 @@ int tty_term_flag(struct tty_term *, enum tty_code_code);
const char *tty_term_describe(struct tty_term *, enum tty_code_code); const char *tty_term_describe(struct tty_term *, enum tty_code_code);
/* tty-acs.c */ /* tty-acs.c */
int tty_acs_needed(struct tty *);
const char *tty_acs_get(struct tty *, u_char); const char *tty_acs_get(struct tty *, u_char);
/* tty-keys.c */ /* tty-keys.c */
@@ -1710,6 +1764,7 @@ void tty_keys_free(struct tty *);
key_code tty_keys_next(struct tty *); key_code tty_keys_next(struct tty *);
/* arguments.c */ /* arguments.c */
void args_set(struct args *, u_char, const char *);
struct args *args_parse(const char *, int, char **); struct args *args_parse(const char *, int, char **);
void args_free(struct args *); void args_free(struct args *);
char *args_print(struct args *); char *args_print(struct args *);
@@ -1729,20 +1784,22 @@ void cmd_find_copy_state(struct cmd_find_state *,
struct cmd_find_state *); struct cmd_find_state *);
void cmd_find_log_state(const char *, struct cmd_find_state *); void cmd_find_log_state(const char *, struct cmd_find_state *);
void cmd_find_from_session(struct cmd_find_state *, void cmd_find_from_session(struct cmd_find_state *,
struct session *); struct session *, int);
void cmd_find_from_winlink(struct cmd_find_state *, void cmd_find_from_winlink(struct cmd_find_state *,
struct winlink *); struct winlink *, int);
int cmd_find_from_session_window(struct cmd_find_state *, int cmd_find_from_session_window(struct cmd_find_state *,
struct session *, struct window *); struct session *, struct window *, int);
int cmd_find_from_window(struct cmd_find_state *, struct window *); int cmd_find_from_window(struct cmd_find_state *, struct window *,
int);
void cmd_find_from_winlink_pane(struct cmd_find_state *, void cmd_find_from_winlink_pane(struct cmd_find_state *,
struct winlink *, struct window_pane *); struct winlink *, struct window_pane *, int);
int cmd_find_from_pane(struct cmd_find_state *, int cmd_find_from_pane(struct cmd_find_state *,
struct window_pane *); struct window_pane *, int);
int cmd_find_from_client(struct cmd_find_state *, struct client *); int cmd_find_from_client(struct cmd_find_state *, struct client *,
int);
int cmd_find_from_mouse(struct cmd_find_state *, int cmd_find_from_mouse(struct cmd_find_state *,
struct mouse_event *); struct mouse_event *, int);
int cmd_find_from_nothing(struct cmd_find_state *); int cmd_find_from_nothing(struct cmd_find_state *, int);
/* cmd.c */ /* cmd.c */
int cmd_pack_argv(int, char **, char *, size_t); int cmd_pack_argv(int, char **, char *, size_t);
@@ -1791,7 +1848,7 @@ struct cmd_list *cmd_string_parse(const char *, const char *, u_int, char **);
void cmd_wait_for_flush(void); void cmd_wait_for_flush(void);
/* client.c */ /* client.c */
int client_main(struct event_base *, int, char **, int, const char *); int client_main(struct event_base *, int, char **, int);
/* key-bindings.c */ /* key-bindings.c */
RB_PROTOTYPE(key_bindings, key_binding, entry, key_bindings_cmp); RB_PROTOTYPE(key_bindings, key_binding, entry, key_bindings_cmp);
@@ -1805,8 +1862,8 @@ void key_bindings_add(const char *, key_code, 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);
void key_bindings_dispatch(struct key_binding *, struct client *, void key_bindings_dispatch(struct key_binding *, struct cmdq_item *,
struct mouse_event *, struct cmd_find_state *); struct client *, struct mouse_event *, struct cmd_find_state *);
/* key-string.c */ /* key-string.c */
key_code key_string_lookup_string(const char *); key_code key_string_lookup_string(const char *);
@@ -1827,18 +1884,19 @@ void server_clear_marked(void);
int server_is_marked(struct session *, struct winlink *, int server_is_marked(struct session *, struct winlink *,
struct window_pane *); struct window_pane *);
int server_check_marked(void); int server_check_marked(void);
int server_start(struct event_base *, int, char *); int server_start(struct tmuxproc *, struct event_base *, int, char *);
void server_update_socket(void); void server_update_socket(void);
void server_add_accept(int); void server_add_accept(int);
/* server-client.c */ /* server-client.c */
void server_client_set_identify(struct client *); u_int server_client_how_many(void);
void server_client_set_identify(struct client *, u_int);
void server_client_clear_identify(struct client *, struct window_pane *); void server_client_clear_identify(struct client *, struct window_pane *);
void server_client_set_key_table(struct client *, const char *); void server_client_set_key_table(struct client *, const char *);
const char *server_client_get_key_table(struct client *); const char *server_client_get_key_table(struct client *);
int server_client_check_nested(struct client *); int server_client_check_nested(struct client *);
void server_client_handle_key(struct client *, key_code); void server_client_handle_key(struct client *, key_code);
void server_client_create(int); struct client *server_client_create(int);
int server_client_open(struct client *, char **); int server_client_open(struct client *, char **);
void server_client_unref(struct client *); void server_client_unref(struct client *);
void server_client_lost(struct client *); void server_client_lost(struct client *);
@@ -1882,13 +1940,14 @@ void status_timer_start(struct client *);
void status_timer_start_all(void); void status_timer_start_all(void);
void status_update_saved(struct session *s); void status_update_saved(struct session *s);
int status_at_line(struct client *); int status_at_line(struct client *);
u_int status_line_size(struct session *);
struct window *status_get_window_at(struct client *, u_int); struct window *status_get_window_at(struct client *, u_int);
int status_redraw(struct client *); int status_redraw(struct client *);
void printflike(2, 3) status_message_set(struct client *, const char *, ...); void printflike(2, 3) status_message_set(struct client *, const char *, ...);
void status_message_clear(struct client *); void status_message_clear(struct client *);
int status_message_redraw(struct client *); int status_message_redraw(struct client *);
void status_prompt_set(struct client *, const char *, const char *, void status_prompt_set(struct client *, const char *, const char *,
int (*)(void *, const char *, int), void (*)(void *), void *, int); prompt_input_cb, prompt_free_cb, void *, int);
void status_prompt_clear(struct client *); void status_prompt_clear(struct client *);
int status_prompt_redraw(struct client *); int status_prompt_redraw(struct client *);
int status_prompt_key(struct client *, key_code); int status_prompt_key(struct client *, key_code);
@@ -1931,9 +1990,9 @@ int grid_cells_equal(const struct grid_cell *, const struct grid_cell *);
struct grid *grid_create(u_int, u_int, u_int); struct grid *grid_create(u_int, u_int, u_int);
void grid_destroy(struct grid *); void grid_destroy(struct grid *);
int grid_compare(struct grid *, struct grid *); int grid_compare(struct grid *, struct grid *);
void grid_collect_history(struct grid *, u_int); void grid_collect_history(struct grid *);
void grid_scroll_history(struct grid *, u_int); void grid_scroll_history(struct grid *, u_int);
void grid_scroll_history_region(struct grid *, u_int, u_int); void grid_scroll_history_region(struct grid *, u_int, u_int, u_int);
void grid_clear_history(struct grid *); void grid_clear_history(struct grid *);
const struct grid_line *grid_peek_line(struct grid *, u_int); const struct grid_line *grid_peek_line(struct grid *, u_int);
void grid_get_cell(struct grid *, u_int, u_int, struct grid_cell *); void grid_get_cell(struct grid *, u_int, u_int, struct grid_cell *);
@@ -1948,7 +2007,7 @@ char *grid_string_cells(struct grid *, u_int, u_int, u_int,
struct grid_cell **, int, int, int); struct grid_cell **, int, int, int);
void grid_duplicate_lines(struct grid *, u_int, struct grid *, u_int, void grid_duplicate_lines(struct grid *, u_int, struct grid *, u_int,
u_int); u_int);
u_int grid_reflow(struct grid *, struct grid *, u_int); void grid_reflow(struct grid *, u_int, u_int *);
/* grid-view.c */ /* grid-view.c */
void grid_view_get_cell(struct grid *, u_int, u_int, struct grid_cell *); void grid_view_get_cell(struct grid *, u_int, u_int, struct grid_cell *);
@@ -1958,8 +2017,8 @@ void grid_view_set_cells(struct grid *, u_int, u_int,
const struct grid_cell *, const char *, size_t); const struct grid_cell *, const char *, size_t);
void grid_view_clear_history(struct grid *, u_int); void grid_view_clear_history(struct grid *, u_int);
void grid_view_clear(struct grid *, u_int, u_int, u_int, u_int, u_int); void grid_view_clear(struct grid *, u_int, u_int, u_int, u_int, u_int);
void grid_view_scroll_region_up(struct grid *, u_int, u_int); void grid_view_scroll_region_up(struct grid *, u_int, u_int, u_int);
void grid_view_scroll_region_down(struct grid *, u_int, u_int); void grid_view_scroll_region_down(struct grid *, u_int, u_int, u_int);
void grid_view_insert_lines(struct grid *, u_int, u_int, u_int); void grid_view_insert_lines(struct grid *, u_int, u_int, u_int);
void grid_view_insert_lines_region(struct grid *, u_int, u_int, u_int, void grid_view_insert_lines_region(struct grid *, u_int, u_int, u_int,
u_int); u_int);
@@ -1989,6 +2048,13 @@ void screen_write_putc(struct screen_write_ctx *, const struct grid_cell *,
u_char); u_char);
void screen_write_copy(struct screen_write_ctx *, struct screen *, u_int, void screen_write_copy(struct screen_write_ctx *, struct screen *, u_int,
u_int, u_int, u_int, bitstr_t *, const struct grid_cell *); u_int, u_int, u_int, bitstr_t *, const struct grid_cell *);
void screen_write_fast_copy(struct screen_write_ctx *, struct screen *,
u_int, u_int, u_int, u_int);
void screen_write_hline(struct screen_write_ctx *, u_int, int, int);
void screen_write_vline(struct screen_write_ctx *, u_int, int, int);
void screen_write_box(struct screen_write_ctx *, u_int, u_int);
void screen_write_preview(struct screen_write_ctx *, struct screen *, u_int,
u_int);
void screen_write_backspace(struct screen_write_ctx *); void screen_write_backspace(struct screen_write_ctx *);
void screen_write_mode_set(struct screen_write_ctx *, int); void screen_write_mode_set(struct screen_write_ctx *, int);
void screen_write_mode_clear(struct screen_write_ctx *, int); void screen_write_mode_clear(struct screen_write_ctx *, int);
@@ -2006,10 +2072,10 @@ void screen_write_clearline(struct screen_write_ctx *, u_int);
void screen_write_clearendofline(struct screen_write_ctx *, u_int); void screen_write_clearendofline(struct screen_write_ctx *, u_int);
void screen_write_clearstartofline(struct screen_write_ctx *, u_int); void screen_write_clearstartofline(struct screen_write_ctx *, u_int);
void screen_write_cursormove(struct screen_write_ctx *, u_int, u_int); void screen_write_cursormove(struct screen_write_ctx *, u_int, u_int);
void screen_write_reverseindex(struct screen_write_ctx *); 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); void screen_write_linefeed(struct screen_write_ctx *, int, u_int);
void screen_write_scrollup(struct screen_write_ctx *, u_int); void screen_write_scrollup(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);
@@ -2035,6 +2101,8 @@ 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 *); void screen_set_title(struct screen *, const char *);
void screen_push_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);
void screen_set_selection(struct screen *, void screen_set_selection(struct screen *,
u_int, u_int, u_int, u_int, u_int, struct grid_cell *); u_int, u_int, u_int, u_int, u_int, struct grid_cell *);
@@ -2081,7 +2149,7 @@ int window_has_pane(struct window *, struct window_pane *);
int window_set_active_pane(struct window *, struct window_pane *); int window_set_active_pane(struct window *, struct window_pane *);
void window_redraw_active_switch(struct window *, 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 *, int,
int, u_int); int, u_int);
void window_resize(struct window *, u_int, u_int); void window_resize(struct window *, u_int, u_int);
int window_zoom(struct window_pane *); int window_zoom(struct window_pane *);
@@ -2098,6 +2166,7 @@ u_int window_count_panes(struct window *);
void window_destroy_panes(struct window *); void window_destroy_panes(struct window *);
struct window_pane *window_pane_find_by_id_str(const char *); struct window_pane *window_pane_find_by_id_str(const char *);
struct window_pane *window_pane_find_by_id(u_int); struct window_pane *window_pane_find_by_id(u_int);
int window_pane_destroy_ready(struct window_pane *);
int window_pane_spawn(struct window_pane *, int, char **, int window_pane_spawn(struct window_pane *, int, char **,
const char *, const char *, const char *, struct environ *, const char *, const char *, const char *, struct environ *,
struct termios *, char **); struct termios *, char **);
@@ -2111,14 +2180,13 @@ void window_pane_unset_palette(struct window_pane *, u_int);
void window_pane_reset_palette(struct window_pane *); void window_pane_reset_palette(struct window_pane *);
int window_pane_get_palette(const struct window_pane *, int); int window_pane_get_palette(const struct window_pane *, int);
int window_pane_set_mode(struct window_pane *, int window_pane_set_mode(struct window_pane *,
const struct window_mode *); const struct window_mode *, struct cmd_find_state *,
struct args *);
void window_pane_reset_mode(struct window_pane *); void window_pane_reset_mode(struct window_pane *);
void window_pane_key(struct window_pane *, struct client *, void window_pane_key(struct window_pane *, struct client *,
struct session *, key_code, struct mouse_event *); struct session *, key_code, struct mouse_event *);
int window_pane_outside(struct window_pane *);
int window_pane_visible(struct window_pane *); int window_pane_visible(struct window_pane *);
char *window_pane_search(struct window_pane *, const char *, u_int window_pane_search(struct window_pane *, const char *);
u_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 *);
@@ -2156,6 +2224,8 @@ void layout_assign_pane(struct layout_cell *, struct window_pane *);
struct layout_cell *layout_split_pane(struct window_pane *, enum layout_type, struct layout_cell *layout_split_pane(struct window_pane *, enum layout_type,
int, int, int); int, int, int);
void layout_close_pane(struct window_pane *); void layout_close_pane(struct window_pane *);
int layout_spread_cell(struct window *, struct layout_cell *);
void layout_spread_out(struct window_pane *);
/* layout-custom.c */ /* layout-custom.c */
char *layout_dump(struct layout_cell *); char *layout_dump(struct layout_cell *);
@@ -2167,10 +2237,50 @@ u_int layout_set_select(struct window *, u_int);
u_int layout_set_next(struct window *); u_int layout_set_next(struct window *);
u_int layout_set_previous(struct window *); u_int layout_set_previous(struct window *);
/* mode-tree.c */
typedef void (*mode_tree_build_cb)(void *, u_int, uint64_t *, const char *);
typedef void (*mode_tree_draw_cb)(void *, void *, struct screen_write_ctx *,
u_int, u_int);
typedef int (*mode_tree_search_cb)(void *, void *, const char *);
typedef void (*mode_tree_each_cb)(void *, void *, struct client *, key_code);
u_int mode_tree_count_tagged(struct mode_tree_data *);
void *mode_tree_get_current(struct mode_tree_data *);
void mode_tree_expand_current(struct mode_tree_data *);
void mode_tree_set_current(struct mode_tree_data *, uint64_t);
void mode_tree_each_tagged(struct mode_tree_data *, mode_tree_each_cb,
struct client *, key_code, int);
void mode_tree_up(struct mode_tree_data *, int);
void mode_tree_down(struct mode_tree_data *, int);
struct mode_tree_data *mode_tree_start(struct window_pane *, struct args *,
mode_tree_build_cb, mode_tree_draw_cb, mode_tree_search_cb,
void *, const char **, u_int, struct screen **);
void mode_tree_zoom(struct mode_tree_data *, struct args *);
void mode_tree_build(struct mode_tree_data *);
void mode_tree_free(struct mode_tree_data *);
void mode_tree_resize(struct mode_tree_data *, u_int, u_int);
struct mode_tree_item *mode_tree_add(struct mode_tree_data *,
struct mode_tree_item *, void *, uint64_t, const char *,
const char *, int);
void mode_tree_remove(struct mode_tree_data *, struct mode_tree_item *);
void mode_tree_draw(struct mode_tree_data *);
int mode_tree_key(struct mode_tree_data *, struct client *, key_code *,
struct mouse_event *, u_int *, u_int *);
void mode_tree_run_command(struct client *, struct cmd_find_state *,
const char *, const char *);
/* window-buffer.c */
extern const struct window_mode window_buffer_mode;
/* window-tree.c */
extern const struct window_mode window_tree_mode;
/* window-clock.c */ /* window-clock.c */
extern const struct window_mode window_clock_mode; extern const struct window_mode window_clock_mode;
extern const char window_clock_table[14][5][5]; extern const char window_clock_table[14][5][5];
/* window-client.c */
extern const struct window_mode window_client_mode;
/* window-copy.c */ /* window-copy.c */
extern const struct window_mode window_copy_mode; extern const struct window_mode window_copy_mode;
void window_copy_init_from_pane(struct window_pane *, int); void window_copy_init_from_pane(struct window_pane *, int);
@@ -2179,36 +2289,14 @@ void printflike(2, 3) window_copy_add(struct window_pane *, const char *, ...);
void window_copy_vadd(struct window_pane *, const char *, va_list); void window_copy_vadd(struct window_pane *, const char *, va_list);
void window_copy_pageup(struct window_pane *, int); void window_copy_pageup(struct window_pane *, int);
void window_copy_start_drag(struct client *, struct mouse_event *); void window_copy_start_drag(struct client *, struct mouse_event *);
int window_copy_scroll_position(struct window_pane *); void window_copy_add_formats(struct window_pane *,
const char *window_copy_search_string(struct window_pane *); struct format_tree *);
/* window-choose.c */
extern const struct window_mode window_choose_mode;
void window_choose_add(struct window_pane *,
struct window_choose_data *);
void window_choose_ready(struct window_pane *,
u_int, void (*)(struct window_choose_data *));
struct window_choose_data *window_choose_data_create (int,
struct client *, struct session *);
void window_choose_data_run(struct window_choose_data *);
struct window_choose_data *window_choose_add_window(struct window_pane *,
struct client *, struct session *, struct winlink *,
const char *, const char *, u_int);
struct window_choose_data *window_choose_add_session(struct window_pane *,
struct client *, struct session *, const char *,
const char *, u_int);
void window_choose_expand_all(struct window_pane *);
void window_choose_set_current(struct window_pane *, u_int);
/* names.c */ /* names.c */
void check_window_name(struct window *); void check_window_name(struct window *);
char *default_window_name(struct window *); char *default_window_name(struct window *);
char *parse_window_name(const char *); char *parse_window_name(const char *);
/* signal.c */
void set_signals(void(*)(int, short, void *), void *);
void clear_signals(int);
/* control.c */ /* control.c */
void control_callback(struct client *, int, void *); void control_callback(struct client *, int, void *);
void printflike(2, 3) control_write(struct client *, const char *, ...); void printflike(2, 3) control_write(struct client *, const char *, ...);
@@ -2243,7 +2331,7 @@ struct session *session_find_by_id(u_int);
struct session *session_create(const char *, const char *, int, char **, struct session *session_create(const char *, const char *, int, char **,
const char *, const char *, struct environ *, const char *, const char *, struct environ *,
struct termios *, int, u_int, u_int, char **); struct termios *, int, u_int, u_int, char **);
void session_destroy(struct session *); void session_destroy(struct session *, const char *);
void session_add_ref(struct session *, const char *); void session_add_ref(struct session *, const char *);
void session_remove_ref(struct session *, const char *); void session_remove_ref(struct session *, const char *);
int session_check_name(const char *); int session_check_name(const char *);
@@ -2268,6 +2356,7 @@ struct session_group *session_group_new(const char *);
void session_group_add(struct session_group *, struct session *); 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 *);
void session_renumber_windows(struct session *); void session_renumber_windows(struct session *);
/* utf8.c */ /* utf8.c */
@@ -2277,6 +2366,7 @@ enum utf8_state utf8_open(struct utf8_data *, u_char);
enum utf8_state utf8_append(struct utf8_data *, u_char); enum utf8_state utf8_append(struct utf8_data *, u_char);
enum utf8_state utf8_combine(const struct utf8_data *, wchar_t *); enum utf8_state utf8_combine(const struct utf8_data *, wchar_t *);
enum utf8_state utf8_split(wchar_t, struct utf8_data *); enum utf8_state utf8_split(wchar_t, struct utf8_data *);
int utf8_isvalid(const char *);
int utf8_strvis(char *, const char *, size_t, int); int utf8_strvis(char *, const char *, size_t, int);
int utf8_stravis(char **, const char *, int); int utf8_stravis(char **, const char *, int);
char *utf8_sanitize(const char *); char *utf8_sanitize(const char *);
@@ -2298,6 +2388,7 @@ struct event_base *osdep_event_init(void);
void log_add_level(void); void log_add_level(void);
int log_get_level(void); int log_get_level(void);
void log_open(const char *); void log_open(const char *);
void log_toggle(const char *);
void log_close(void); void log_close(void);
void printflike(1, 2) log_debug(const char *, ...); void printflike(1, 2) log_debug(const char *, ...);
__dead void printflike(1, 2) fatal(const char *, ...); __dead void printflike(1, 2) fatal(const char *, ...);

View File

@@ -20,7 +20,7 @@ then
else else
SEQ=seq SEQ=seq
fi fi
SEPARATOR=';' SEPARATOR=':'
setBackgroundColor() setBackgroundColor()
{ {

View File

@@ -4,7 +4,7 @@ rm diff.out
touch diff.out touch diff.out
for i in *.[ch]; do for i in *.[ch]; do
diff -u -I'\$OpenBSD' $i /usr/src/usr.bin/tmux/$i >diff.tmp diff -u -I'\$OpenBSD' $i ../../OpenBSD/tmux/$i >diff.tmp
set -- `wc -l diff.tmp` set -- `wc -l diff.tmp`
[ $1 -eq 8 ] && continue [ $1 -eq 8 ] && continue
echo $i echo $i

View File

@@ -74,23 +74,48 @@ tty_acs_cmp(const void *key, const void *value)
return (ch - entry->key); return (ch - entry->key);
} }
/* Should this terminal use ACS instead of UTF-8 line drawing? */
int
tty_acs_needed(struct tty *tty)
{
if (tty == NULL)
return (0);
/*
* If the U8 flag is present, it marks whether a terminal supports
* UTF-8 and ACS together.
*
* If it is present and zero, we force ACS - this gives users a way to
* turn off UTF-8 line drawing.
*
* If it is nonzero, we can fall through to the default and use UTF-8
* line drawing on UTF-8 terminals.
*/
if (tty_term_has(tty->term, TTYC_U8) &&
tty_term_number(tty->term, TTYC_U8) == 0)
return (1);
if (tty->flags & TTY_UTF8)
return (0);
return (1);
}
/* Retrieve ACS to output as a string. */ /* Retrieve ACS to output as a string. */
const char * const char *
tty_acs_get(struct tty *tty, u_char ch) tty_acs_get(struct tty *tty, u_char ch)
{ {
struct tty_acs_entry *entry; struct tty_acs_entry *entry;
/* If not a UTF-8 terminal, use the ACS set. */ /* Use the ACS set instead of UTF-8 if needed. */
if (tty != NULL && !(tty->flags & TTY_UTF8)) { if (tty_acs_needed(tty)) {
if (tty->term->acs[ch][0] == '\0') if (tty->term->acs[ch][0] == '\0')
return (NULL); return (NULL);
return (&tty->term->acs[ch][0]); return (&tty->term->acs[ch][0]);
} }
/* Otherwise look up the UTF-8 translation. */ /* Otherwise look up the UTF-8 translation. */
entry = bsearch(&ch, entry = bsearch(&ch, tty_acs_table, nitems(tty_acs_table),
tty_acs_table, nitems(tty_acs_table), sizeof tty_acs_table[0], sizeof tty_acs_table[0], tty_acs_cmp);
tty_acs_cmp);
if (entry == NULL) if (entry == NULL)
return (NULL); return (NULL);
return (entry->string); return (entry->string);

View File

@@ -165,6 +165,10 @@ static const struct tty_default_key_raw tty_default_raw_keys[] = {
/* Focus tracking. */ /* Focus tracking. */
{ "\033[I", KEYC_FOCUS_IN }, { "\033[I", KEYC_FOCUS_IN },
{ "\033[O", KEYC_FOCUS_OUT }, { "\033[O", KEYC_FOCUS_OUT },
/* Paste keys. */
{ "\033[200~", KEYC_PASTE_START },
{ "\033[201~", KEYC_PASTE_END },
}; };
/* Default terminfo(5) keys. */ /* Default terminfo(5) keys. */
@@ -267,6 +271,7 @@ static const struct tty_default_key_code tty_default_code_keys[] = {
{ TTYC_KDC5, KEYC_DC|KEYC_CTRL|KEYC_XTERM }, { TTYC_KDC5, KEYC_DC|KEYC_CTRL|KEYC_XTERM },
{ TTYC_KDC6, KEYC_DC|KEYC_SHIFT|KEYC_CTRL|KEYC_XTERM }, { TTYC_KDC6, KEYC_DC|KEYC_SHIFT|KEYC_CTRL|KEYC_XTERM },
{ TTYC_KDC7, KEYC_DC|KEYC_ESCAPE|KEYC_CTRL|KEYC_XTERM }, { TTYC_KDC7, KEYC_DC|KEYC_ESCAPE|KEYC_CTRL|KEYC_XTERM },
{ TTYC_KIND, KEYC_DOWN|KEYC_SHIFT|KEYC_XTERM },
{ TTYC_KDN2, KEYC_DOWN|KEYC_SHIFT|KEYC_XTERM }, { TTYC_KDN2, KEYC_DOWN|KEYC_SHIFT|KEYC_XTERM },
{ TTYC_KDN3, KEYC_DOWN|KEYC_ESCAPE|KEYC_XTERM }, { TTYC_KDN3, KEYC_DOWN|KEYC_ESCAPE|KEYC_XTERM },
{ TTYC_KDN4, KEYC_DOWN|KEYC_SHIFT|KEYC_ESCAPE|KEYC_XTERM }, { TTYC_KDN4, KEYC_DOWN|KEYC_SHIFT|KEYC_ESCAPE|KEYC_XTERM },
@@ -315,6 +320,7 @@ static const struct tty_default_key_code tty_default_code_keys[] = {
{ TTYC_KRIT5, KEYC_RIGHT|KEYC_CTRL|KEYC_XTERM }, { TTYC_KRIT5, KEYC_RIGHT|KEYC_CTRL|KEYC_XTERM },
{ TTYC_KRIT6, KEYC_RIGHT|KEYC_SHIFT|KEYC_CTRL|KEYC_XTERM }, { TTYC_KRIT6, KEYC_RIGHT|KEYC_SHIFT|KEYC_CTRL|KEYC_XTERM },
{ TTYC_KRIT7, KEYC_RIGHT|KEYC_ESCAPE|KEYC_CTRL|KEYC_XTERM }, { TTYC_KRIT7, KEYC_RIGHT|KEYC_ESCAPE|KEYC_CTRL|KEYC_XTERM },
{ TTYC_KRI, KEYC_UP|KEYC_SHIFT|KEYC_XTERM },
{ TTYC_KUP2, KEYC_UP|KEYC_SHIFT|KEYC_XTERM }, { TTYC_KUP2, KEYC_UP|KEYC_SHIFT|KEYC_XTERM },
{ TTYC_KUP3, KEYC_UP|KEYC_ESCAPE|KEYC_XTERM }, { TTYC_KUP3, KEYC_UP|KEYC_ESCAPE|KEYC_XTERM },
{ TTYC_KUP4, KEYC_UP|KEYC_SHIFT|KEYC_ESCAPE|KEYC_XTERM }, { TTYC_KUP4, KEYC_UP|KEYC_SHIFT|KEYC_ESCAPE|KEYC_XTERM },
@@ -385,8 +391,9 @@ tty_keys_build(struct tty *tty)
{ {
const struct tty_default_key_raw *tdkr; const struct tty_default_key_raw *tdkr;
const struct tty_default_key_code *tdkc; const struct tty_default_key_code *tdkc;
u_int i; u_int i, size;
const char *s; const char *s, *value;
struct options_entry *o;
if (tty->key_tree != NULL) if (tty->key_tree != NULL)
tty_keys_free(tty); tty_keys_free(tty);
@@ -407,6 +414,15 @@ tty_keys_build(struct tty *tty)
tty_keys_add(tty, s, tdkc->key); tty_keys_add(tty, s, tdkc->key);
} }
o = options_get(global_options, "user-keys");
if (o != NULL && options_array_size(o, &size) != -1) {
for (i = 0; i < size; i++) {
value = options_array_get(o, i);
if (value != NULL)
tty_keys_add(tty, value, KEYC_USER + i);
}
}
} }
/* Free the entire key tree. */ /* Free the entire key tree. */
@@ -579,7 +595,17 @@ tty_keys_next(struct tty *tty)
} }
first_key: first_key:
/* Handle keys starting with escape. */ /* Try to lookup complete key. */
n = tty_keys_next1(tty, buf, len, &key, &size, expired);
if (n == 0) /* found */
goto complete_key;
if (n == 1)
goto partial_key;
/*
* If not a complete key, look for key with an escape prefix (meta
* modifier).
*/
if (*buf == '\033') { if (*buf == '\033') {
/* Look for a key without the escape. */ /* Look for a key without the escape. */
n = tty_keys_next1(tty, buf + 1, len - 1, &key, &size, expired); n = tty_keys_next1(tty, buf + 1, len - 1, &key, &size, expired);
@@ -604,13 +630,6 @@ first_key:
goto partial_key; goto partial_key;
} }
/* Try to lookup key. */
n = tty_keys_next1(tty, buf, len, &key, &size, expired);
if (n == 0) /* found */
goto complete_key;
if (n == 1)
goto partial_key;
/* /*
* At this point, we know the key is not partial (with or without * At this point, we know the key is not partial (with or without
* escape). So pass it through even if the timer has not expired. * escape). So pass it through even if the timer has not expired.

View File

@@ -68,36 +68,36 @@ static const struct tty_term_code_entry tty_term_codes[] = {
[TTYC_CNORM] = { TTYCODE_STRING, "cnorm" }, [TTYC_CNORM] = { TTYCODE_STRING, "cnorm" },
[TTYC_COLORS] = { TTYCODE_NUMBER, "colors" }, [TTYC_COLORS] = { TTYCODE_NUMBER, "colors" },
[TTYC_CR] = { TTYCODE_STRING, "Cr" }, [TTYC_CR] = { TTYCODE_STRING, "Cr" },
[TTYC_CS] = { TTYCODE_STRING, "Cs" },
[TTYC_CSR] = { TTYCODE_STRING, "csr" }, [TTYC_CSR] = { TTYCODE_STRING, "csr" },
[TTYC_CUB] = { TTYCODE_STRING, "cub" }, [TTYC_CS] = { TTYCODE_STRING, "Cs" },
[TTYC_CUB1] = { TTYCODE_STRING, "cub1" }, [TTYC_CUB1] = { TTYCODE_STRING, "cub1" },
[TTYC_CUD] = { TTYCODE_STRING, "cud" }, [TTYC_CUB] = { TTYCODE_STRING, "cub" },
[TTYC_CUD1] = { TTYCODE_STRING, "cud1" }, [TTYC_CUD1] = { TTYCODE_STRING, "cud1" },
[TTYC_CUF] = { TTYCODE_STRING, "cuf" }, [TTYC_CUD] = { TTYCODE_STRING, "cud" },
[TTYC_CUF1] = { TTYCODE_STRING, "cuf1" }, [TTYC_CUF1] = { TTYCODE_STRING, "cuf1" },
[TTYC_CUF] = { TTYCODE_STRING, "cuf" },
[TTYC_CUP] = { TTYCODE_STRING, "cup" }, [TTYC_CUP] = { TTYCODE_STRING, "cup" },
[TTYC_CUU] = { TTYCODE_STRING, "cuu" },
[TTYC_CUU1] = { TTYCODE_STRING, "cuu1" }, [TTYC_CUU1] = { TTYCODE_STRING, "cuu1" },
[TTYC_CUU] = { TTYCODE_STRING, "cuu" },
[TTYC_CVVIS] = { TTYCODE_STRING, "cvvis" }, [TTYC_CVVIS] = { TTYCODE_STRING, "cvvis" },
[TTYC_DCH] = { TTYCODE_STRING, "dch" },
[TTYC_DCH1] = { TTYCODE_STRING, "dch1" }, [TTYC_DCH1] = { TTYCODE_STRING, "dch1" },
[TTYC_DCH] = { TTYCODE_STRING, "dch" },
[TTYC_DIM] = { TTYCODE_STRING, "dim" }, [TTYC_DIM] = { TTYCODE_STRING, "dim" },
[TTYC_DL] = { TTYCODE_STRING, "dl" },
[TTYC_DL1] = { TTYCODE_STRING, "dl1" }, [TTYC_DL1] = { TTYCODE_STRING, "dl1" },
[TTYC_DL] = { TTYCODE_STRING, "dl" },
[TTYC_E3] = { TTYCODE_STRING, "E3" }, [TTYC_E3] = { TTYCODE_STRING, "E3" },
[TTYC_ECH] = { TTYCODE_STRING, "ech" }, [TTYC_ECH] = { TTYCODE_STRING, "ech" },
[TTYC_ED] = { TTYCODE_STRING, "ed" }, [TTYC_ED] = { TTYCODE_STRING, "ed" },
[TTYC_EL] = { TTYCODE_STRING, "el" },
[TTYC_EL1] = { TTYCODE_STRING, "el1" }, [TTYC_EL1] = { TTYCODE_STRING, "el1" },
[TTYC_EL] = { TTYCODE_STRING, "el" },
[TTYC_ENACS] = { TTYCODE_STRING, "enacs" }, [TTYC_ENACS] = { TTYCODE_STRING, "enacs" },
[TTYC_FSL] = { TTYCODE_STRING, "fsl" }, [TTYC_FSL] = { TTYCODE_STRING, "fsl" },
[TTYC_HOME] = { TTYCODE_STRING, "home" }, [TTYC_HOME] = { TTYCODE_STRING, "home" },
[TTYC_HPA] = { TTYCODE_STRING, "hpa" }, [TTYC_HPA] = { TTYCODE_STRING, "hpa" },
[TTYC_ICH] = { TTYCODE_STRING, "ich" },
[TTYC_ICH1] = { TTYCODE_STRING, "ich1" }, [TTYC_ICH1] = { TTYCODE_STRING, "ich1" },
[TTYC_IL] = { TTYCODE_STRING, "il" }, [TTYC_ICH] = { TTYCODE_STRING, "ich" },
[TTYC_IL1] = { TTYCODE_STRING, "il1" }, [TTYC_IL1] = { TTYCODE_STRING, "il1" },
[TTYC_IL] = { TTYCODE_STRING, "il" },
[TTYC_INDN] = { TTYCODE_STRING, "indn" }, [TTYC_INDN] = { TTYCODE_STRING, "indn" },
[TTYC_INVIS] = { TTYCODE_STRING, "invis" }, [TTYC_INVIS] = { TTYCODE_STRING, "invis" },
[TTYC_KCBT] = { TTYCODE_STRING, "kcbt" }, [TTYC_KCBT] = { TTYCODE_STRING, "kcbt" },
@@ -112,20 +112,19 @@ static const struct tty_term_code_entry tty_term_codes[] = {
[TTYC_KDC6] = { TTYCODE_STRING, "kDC6" }, [TTYC_KDC6] = { TTYCODE_STRING, "kDC6" },
[TTYC_KDC7] = { TTYCODE_STRING, "kDC7" }, [TTYC_KDC7] = { TTYCODE_STRING, "kDC7" },
[TTYC_KDCH1] = { TTYCODE_STRING, "kdch1" }, [TTYC_KDCH1] = { TTYCODE_STRING, "kdch1" },
[TTYC_KDN2] = { TTYCODE_STRING, "kDN" }, [TTYC_KDN2] = { TTYCODE_STRING, "kDN" }, /* not kDN2 */
[TTYC_KDN3] = { TTYCODE_STRING, "kDN3" }, [TTYC_KDN3] = { TTYCODE_STRING, "kDN3" },
[TTYC_KDN4] = { TTYCODE_STRING, "kDN4" }, [TTYC_KDN4] = { TTYCODE_STRING, "kDN4" },
[TTYC_KDN5] = { TTYCODE_STRING, "kDN5" }, [TTYC_KDN5] = { TTYCODE_STRING, "kDN5" },
[TTYC_KDN6] = { TTYCODE_STRING, "kDN6" }, [TTYC_KDN6] = { TTYCODE_STRING, "kDN6" },
[TTYC_KDN7] = { TTYCODE_STRING, "kDN7" }, [TTYC_KDN7] = { TTYCODE_STRING, "kDN7" },
[TTYC_KEND] = { TTYCODE_STRING, "kend" },
[TTYC_KEND2] = { TTYCODE_STRING, "kEND" }, [TTYC_KEND2] = { TTYCODE_STRING, "kEND" },
[TTYC_KEND3] = { TTYCODE_STRING, "kEND3" }, [TTYC_KEND3] = { TTYCODE_STRING, "kEND3" },
[TTYC_KEND4] = { TTYCODE_STRING, "kEND4" }, [TTYC_KEND4] = { TTYCODE_STRING, "kEND4" },
[TTYC_KEND5] = { TTYCODE_STRING, "kEND5" }, [TTYC_KEND5] = { TTYCODE_STRING, "kEND5" },
[TTYC_KEND6] = { TTYCODE_STRING, "kEND6" }, [TTYC_KEND6] = { TTYCODE_STRING, "kEND6" },
[TTYC_KEND7] = { TTYCODE_STRING, "kEND7" }, [TTYC_KEND7] = { TTYCODE_STRING, "kEND7" },
[TTYC_KF1] = { TTYCODE_STRING, "kf1" }, [TTYC_KEND] = { TTYCODE_STRING, "kend" },
[TTYC_KF10] = { TTYCODE_STRING, "kf10" }, [TTYC_KF10] = { TTYCODE_STRING, "kf10" },
[TTYC_KF11] = { TTYCODE_STRING, "kf11" }, [TTYC_KF11] = { TTYCODE_STRING, "kf11" },
[TTYC_KF12] = { TTYCODE_STRING, "kf12" }, [TTYC_KF12] = { TTYCODE_STRING, "kf12" },
@@ -136,7 +135,7 @@ static const struct tty_term_code_entry tty_term_codes[] = {
[TTYC_KF17] = { TTYCODE_STRING, "kf17" }, [TTYC_KF17] = { TTYCODE_STRING, "kf17" },
[TTYC_KF18] = { TTYCODE_STRING, "kf18" }, [TTYC_KF18] = { TTYCODE_STRING, "kf18" },
[TTYC_KF19] = { TTYCODE_STRING, "kf19" }, [TTYC_KF19] = { TTYCODE_STRING, "kf19" },
[TTYC_KF2] = { TTYCODE_STRING, "kf2" }, [TTYC_KF1] = { TTYCODE_STRING, "kf1" },
[TTYC_KF20] = { TTYCODE_STRING, "kf20" }, [TTYC_KF20] = { TTYCODE_STRING, "kf20" },
[TTYC_KF21] = { TTYCODE_STRING, "kf21" }, [TTYC_KF21] = { TTYCODE_STRING, "kf21" },
[TTYC_KF22] = { TTYCODE_STRING, "kf22" }, [TTYC_KF22] = { TTYCODE_STRING, "kf22" },
@@ -147,7 +146,7 @@ static const struct tty_term_code_entry tty_term_codes[] = {
[TTYC_KF27] = { TTYCODE_STRING, "kf27" }, [TTYC_KF27] = { TTYCODE_STRING, "kf27" },
[TTYC_KF28] = { TTYCODE_STRING, "kf28" }, [TTYC_KF28] = { TTYCODE_STRING, "kf28" },
[TTYC_KF29] = { TTYCODE_STRING, "kf29" }, [TTYC_KF29] = { TTYCODE_STRING, "kf29" },
[TTYC_KF3] = { TTYCODE_STRING, "kf3" }, [TTYC_KF2] = { TTYCODE_STRING, "kf2" },
[TTYC_KF30] = { TTYCODE_STRING, "kf30" }, [TTYC_KF30] = { TTYCODE_STRING, "kf30" },
[TTYC_KF31] = { TTYCODE_STRING, "kf31" }, [TTYC_KF31] = { TTYCODE_STRING, "kf31" },
[TTYC_KF32] = { TTYCODE_STRING, "kf32" }, [TTYC_KF32] = { TTYCODE_STRING, "kf32" },
@@ -158,7 +157,7 @@ static const struct tty_term_code_entry tty_term_codes[] = {
[TTYC_KF37] = { TTYCODE_STRING, "kf37" }, [TTYC_KF37] = { TTYCODE_STRING, "kf37" },
[TTYC_KF38] = { TTYCODE_STRING, "kf38" }, [TTYC_KF38] = { TTYCODE_STRING, "kf38" },
[TTYC_KF39] = { TTYCODE_STRING, "kf39" }, [TTYC_KF39] = { TTYCODE_STRING, "kf39" },
[TTYC_KF4] = { TTYCODE_STRING, "kf4" }, [TTYC_KF3] = { TTYCODE_STRING, "kf3" },
[TTYC_KF40] = { TTYCODE_STRING, "kf40" }, [TTYC_KF40] = { TTYCODE_STRING, "kf40" },
[TTYC_KF41] = { TTYCODE_STRING, "kf41" }, [TTYC_KF41] = { TTYCODE_STRING, "kf41" },
[TTYC_KF42] = { TTYCODE_STRING, "kf42" }, [TTYC_KF42] = { TTYCODE_STRING, "kf42" },
@@ -169,7 +168,7 @@ static const struct tty_term_code_entry tty_term_codes[] = {
[TTYC_KF47] = { TTYCODE_STRING, "kf47" }, [TTYC_KF47] = { TTYCODE_STRING, "kf47" },
[TTYC_KF48] = { TTYCODE_STRING, "kf48" }, [TTYC_KF48] = { TTYCODE_STRING, "kf48" },
[TTYC_KF49] = { TTYCODE_STRING, "kf49" }, [TTYC_KF49] = { TTYCODE_STRING, "kf49" },
[TTYC_KF5] = { TTYCODE_STRING, "kf5" }, [TTYC_KF4] = { TTYCODE_STRING, "kf4" },
[TTYC_KF50] = { TTYCODE_STRING, "kf50" }, [TTYC_KF50] = { TTYCODE_STRING, "kf50" },
[TTYC_KF51] = { TTYCODE_STRING, "kf51" }, [TTYC_KF51] = { TTYCODE_STRING, "kf51" },
[TTYC_KF52] = { TTYCODE_STRING, "kf52" }, [TTYC_KF52] = { TTYCODE_STRING, "kf52" },
@@ -180,11 +179,12 @@ static const struct tty_term_code_entry tty_term_codes[] = {
[TTYC_KF57] = { TTYCODE_STRING, "kf57" }, [TTYC_KF57] = { TTYCODE_STRING, "kf57" },
[TTYC_KF58] = { TTYCODE_STRING, "kf58" }, [TTYC_KF58] = { TTYCODE_STRING, "kf58" },
[TTYC_KF59] = { TTYCODE_STRING, "kf59" }, [TTYC_KF59] = { TTYCODE_STRING, "kf59" },
[TTYC_KF6] = { TTYCODE_STRING, "kf6" }, [TTYC_KF5] = { TTYCODE_STRING, "kf5" },
[TTYC_KF60] = { TTYCODE_STRING, "kf60" }, [TTYC_KF60] = { TTYCODE_STRING, "kf60" },
[TTYC_KF61] = { TTYCODE_STRING, "kf61" }, [TTYC_KF61] = { TTYCODE_STRING, "kf61" },
[TTYC_KF62] = { TTYCODE_STRING, "kf62" }, [TTYC_KF62] = { TTYCODE_STRING, "kf62" },
[TTYC_KF63] = { TTYCODE_STRING, "kf63" }, [TTYC_KF63] = { TTYCODE_STRING, "kf63" },
[TTYC_KF6] = { TTYCODE_STRING, "kf6" },
[TTYC_KF7] = { TTYCODE_STRING, "kf7" }, [TTYC_KF7] = { TTYCODE_STRING, "kf7" },
[TTYC_KF8] = { TTYCODE_STRING, "kf8" }, [TTYC_KF8] = { TTYCODE_STRING, "kf8" },
[TTYC_KF9] = { TTYCODE_STRING, "kf9" }, [TTYC_KF9] = { TTYCODE_STRING, "kf9" },
@@ -202,6 +202,7 @@ static const struct tty_term_code_entry tty_term_codes[] = {
[TTYC_KIC6] = { TTYCODE_STRING, "kIC6" }, [TTYC_KIC6] = { TTYCODE_STRING, "kIC6" },
[TTYC_KIC7] = { TTYCODE_STRING, "kIC7" }, [TTYC_KIC7] = { TTYCODE_STRING, "kIC7" },
[TTYC_KICH1] = { TTYCODE_STRING, "kich1" }, [TTYC_KICH1] = { TTYCODE_STRING, "kich1" },
[TTYC_KIND] = { TTYCODE_STRING, "kind" },
[TTYC_KLFT2] = { TTYCODE_STRING, "kLFT" }, [TTYC_KLFT2] = { TTYCODE_STRING, "kLFT" },
[TTYC_KLFT3] = { TTYCODE_STRING, "kLFT3" }, [TTYC_KLFT3] = { TTYCODE_STRING, "kLFT3" },
[TTYC_KLFT4] = { TTYCODE_STRING, "kLFT4" }, [TTYC_KLFT4] = { TTYCODE_STRING, "kLFT4" },
@@ -229,7 +230,8 @@ static const struct tty_term_code_entry tty_term_codes[] = {
[TTYC_KRIT5] = { TTYCODE_STRING, "kRIT5" }, [TTYC_KRIT5] = { TTYCODE_STRING, "kRIT5" },
[TTYC_KRIT6] = { TTYCODE_STRING, "kRIT6" }, [TTYC_KRIT6] = { TTYCODE_STRING, "kRIT6" },
[TTYC_KRIT7] = { TTYCODE_STRING, "kRIT7" }, [TTYC_KRIT7] = { TTYCODE_STRING, "kRIT7" },
[TTYC_KUP2] = { TTYCODE_STRING, "kUP" }, [TTYC_KRI] = { TTYCODE_STRING, "kri" },
[TTYC_KUP2] = { TTYCODE_STRING, "kUP" }, /* not kUP2 */
[TTYC_KUP3] = { TTYCODE_STRING, "kUP3" }, [TTYC_KUP3] = { TTYCODE_STRING, "kUP3" },
[TTYC_KUP4] = { TTYCODE_STRING, "kUP4" }, [TTYC_KUP4] = { TTYCODE_STRING, "kUP4" },
[TTYC_KUP5] = { TTYCODE_STRING, "kUP5" }, [TTYC_KUP5] = { TTYCODE_STRING, "kUP5" },
@@ -238,13 +240,16 @@ static const struct tty_term_code_entry tty_term_codes[] = {
[TTYC_MS] = { TTYCODE_STRING, "Ms" }, [TTYC_MS] = { TTYCODE_STRING, "Ms" },
[TTYC_OP] = { TTYCODE_STRING, "op" }, [TTYC_OP] = { TTYCODE_STRING, "op" },
[TTYC_REV] = { TTYCODE_STRING, "rev" }, [TTYC_REV] = { TTYCODE_STRING, "rev" },
[TTYC_RGB] = { TTYCODE_FLAG, "RGB" },
[TTYC_RI] = { TTYCODE_STRING, "ri" }, [TTYC_RI] = { TTYCODE_STRING, "ri" },
[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" },
[TTYC_SE] = { TTYCODE_STRING, "Se" },
[TTYC_SETAB] = { TTYCODE_STRING, "setab" }, [TTYC_SETAB] = { TTYCODE_STRING, "setab" },
[TTYC_SETAF] = { TTYCODE_STRING, "setaf" }, [TTYC_SETAF] = { TTYCODE_STRING, "setaf" },
[TTYC_SETRGBB] = { TTYCODE_STRING, "setrgbb" },
[TTYC_SETRGBF] = { TTYCODE_STRING, "setrgbf" },
[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" },
[TTYC_SMACS] = { TTYCODE_STRING, "smacs" }, [TTYC_SMACS] = { TTYCODE_STRING, "smacs" },
@@ -256,6 +261,7 @@ static const struct tty_term_code_entry tty_term_codes[] = {
[TTYC_SS] = { TTYCODE_STRING, "Ss" }, [TTYC_SS] = { TTYCODE_STRING, "Ss" },
[TTYC_TC] = { TTYCODE_FLAG, "Tc" }, [TTYC_TC] = { TTYCODE_FLAG, "Tc" },
[TTYC_TSL] = { TTYCODE_STRING, "tsl" }, [TTYC_TSL] = { TTYCODE_STRING, "tsl" },
[TTYC_U8] = { TTYCODE_NUMBER, "U8" },
[TTYC_VPA] = { TTYCODE_STRING, "vpa" }, [TTYC_VPA] = { TTYCODE_STRING, "vpa" },
[TTYC_XENL] = { TTYCODE_FLAG, "xenl" }, [TTYC_XENL] = { TTYCODE_FLAG, "xenl" },
[TTYC_XT] = { TTYCODE_FLAG, "XT" }, [TTYC_XT] = { TTYCODE_FLAG, "XT" },
@@ -393,8 +399,8 @@ tty_term_find(char *name, int fd, char **cause)
return (term); return (term);
} }
} }
log_debug("new term: %s", name); log_debug("new term: %s", name);
term = xmalloc(sizeof *term); term = xmalloc(sizeof *term);
term->name = xstrdup(name); term->name = xstrdup(name);
term->references = 1; term->references = 1;
@@ -526,6 +532,25 @@ tty_term_find(char *name, int fd, char **cause)
code->type = TTYCODE_STRING; code->type = TTYCODE_STRING;
} }
/*
* 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++)
log_debug("%s%s", name, tty_term_describe(term, i));
return (term); return (term);
error: error:
@@ -581,6 +606,12 @@ tty_term_string2(struct tty_term *term, enum tty_code_code code, int a, int b)
return (tparm((char *) tty_term_string(term, code), a, b, 0, 0, 0, 0, 0, 0, 0)); return (tparm((char *) tty_term_string(term, code), a, b, 0, 0, 0, 0, 0, 0, 0));
} }
const char *
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));
}
const char * const char *
tty_term_ptr1(struct tty_term *term, enum tty_code_code code, const void *a) tty_term_ptr1(struct tty_term *term, enum tty_code_code code, const void *a)
{ {

541
tty.c
View File

@@ -71,8 +71,6 @@ static void tty_default_colours(struct grid_cell *,
static void tty_default_attributes(struct tty *, const struct window_pane *, static void tty_default_attributes(struct tty *, const struct window_pane *,
u_int); u_int);
#define tty_use_acs(tty) \
(tty_term_has((tty)->term, TTYC_ACSC) && !((tty)->flags & TTY_UTF8))
#define tty_use_margin(tty) \ #define tty_use_margin(tty) \
((tty)->term_type == TTY_VT420) ((tty)->term_type == TTY_VT420)
@@ -122,7 +120,7 @@ tty_init(struct tty *tty, struct client *c, int fd, char *term)
return (0); return (0);
} }
int void
tty_resize(struct tty *tty) tty_resize(struct tty *tty)
{ {
struct client *c = tty->client; struct client *c = tty->client;
@@ -141,21 +139,15 @@ tty_resize(struct tty *tty)
sy = 24; sy = 24;
} }
log_debug("%s: %s now %ux%u", __func__, c->name, sx, sy); log_debug("%s: %s now %ux%u", __func__, c->name, sx, sy);
tty_set_size(tty, sx, sy);
if (!tty_set_size(tty, sx, sy))
return (0);
tty_invalidate(tty); tty_invalidate(tty);
return (1);
} }
int 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)
{ {
if (sx == tty->sx && sy == tty->sy)
return (0);
tty->sx = sx; tty->sx = sx;
tty->sy = sy; tty->sy = sy;
return (1);
} }
static void static void
@@ -167,8 +159,11 @@ tty_read_callback(__unused int fd, __unused short events, void *data)
int nread; int nread;
nread = evbuffer_read(tty->in, tty->fd, -1); nread = evbuffer_read(tty->in, tty->fd, -1);
if (nread == -1) if (nread == 0 || nread == -1) {
event_del(&tty->event_in);
server_client_lost(tty->client);
return; return;
}
log_debug("%s: read %d bytes (already %zu)", c->name, nread, size); log_debug("%s: read %d bytes (already %zu)", c->name, nread, size);
while (tty_keys_next(tty)) while (tty_keys_next(tty))
@@ -278,7 +273,8 @@ tty_open(struct tty *tty, char **cause)
void void
tty_start_tty(struct tty *tty) tty_start_tty(struct tty *tty)
{ {
struct termios tio; struct client *c = tty->client;
struct termios tio;
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);
@@ -299,10 +295,14 @@ tty_start_tty(struct tty *tty)
tty_putcode(tty, TTYC_SMCUP); tty_putcode(tty, TTYC_SMCUP);
tty_putcode(tty, TTYC_SMKX); tty_putcode(tty, TTYC_SMKX);
if (tty_use_acs(tty))
tty_putcode(tty, TTYC_ENACS);
tty_putcode(tty, TTYC_CLEAR); tty_putcode(tty, TTYC_CLEAR);
if (tty_acs_needed(tty)) {
log_debug("%s: using capabilities for ACS", c->name);
tty_putcode(tty, TTYC_ENACS);
} else
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[?1006l\033[?1005l");
@@ -351,7 +351,7 @@ tty_stop_tty(struct tty *tty)
return; return;
tty_raw(tty, tty_term_string2(tty->term, TTYC_CSR, 0, ws.ws_row - 1)); tty_raw(tty, tty_term_string2(tty->term, TTYC_CSR, 0, ws.ws_row - 1));
if (tty_use_acs(tty)) if (tty_acs_needed(tty))
tty_raw(tty, tty_term_string(tty->term, TTYC_RMACS)); tty_raw(tty, tty_term_string(tty->term, TTYC_RMACS));
tty_raw(tty, tty_term_string(tty->term, TTYC_SGR0)); tty_raw(tty, tty_term_string(tty->term, TTYC_SGR0));
tty_raw(tty, tty_term_string(tty->term, TTYC_RMKX)); tty_raw(tty, tty_term_string(tty->term, TTYC_RMKX));
@@ -469,6 +469,14 @@ tty_putcode2(struct tty *tty, enum tty_code_code code, int a, int b)
tty_puts(tty, tty_term_string2(tty->term, code, a, b)); tty_puts(tty, tty_term_string2(tty->term, code, a, b));
} }
void
tty_putcode3(struct tty *tty, enum tty_code_code code, int a, int b, int c)
{
if (a < 0 || b < 0 || c < 0)
return;
tty_puts(tty, tty_term_string3(tty->term, code, a, b, c));
}
void void
tty_putcode_ptr1(struct tty *tty, enum tty_code_code code, const void *a) tty_putcode_ptr1(struct tty *tty, enum tty_code_code code, const void *a)
{ {
@@ -754,6 +762,115 @@ tty_redraw_region(struct tty *tty, const struct tty_ctx *ctx)
} }
} }
static void
tty_clear_line(struct tty *tty, const struct window_pane *wp, u_int py,
u_int px, u_int nx, u_int bg)
{
log_debug("%s: %u at %u,%u", __func__, nx, px, py);
/* Nothing to clear. */
if (nx == 0)
return;
/* If genuine BCE is available, can try escape sequences. */
if (!tty_fake_bce(tty, wp, bg)) {
/* Off the end of the line, use EL if available. */
if (px + nx >= tty->sx && tty_term_has(tty->term, TTYC_EL)) {
tty_cursor(tty, px, py);
tty_putcode(tty, TTYC_EL);
return;
}
/* At the start of the line. Use EL1. */
if (px == 0 && tty_term_has(tty->term, TTYC_EL1)) {
tty_cursor(tty, px + nx - 1, py);
tty_putcode(tty, TTYC_EL1);
return;
}
/* Section of line. Use ECH if possible. */
if (tty_term_has(tty->term, TTYC_ECH)) {
tty_cursor(tty, px, py);
tty_putcode1(tty, TTYC_ECH, nx);
return;
}
}
/* Couldn't use an escape sequence, use spaces. */
tty_cursor(tty, px, py);
tty_repeat_space(tty, nx);
}
static void
tty_clear_area(struct tty *tty, const struct window_pane *wp, u_int py,
u_int ny, u_int px, u_int nx, u_int bg)
{
u_int yy;
char tmp[64];
log_debug("%s: %u,%u at %u,%u", __func__, nx, ny, px, py);
/* Nothing to clear. */
if (nx == 0 || ny == 0)
return;
/* If genuine BCE is available, can try escape sequences. */
if (!tty_fake_bce(tty, wp, bg)) {
/* Use ED if clearing off the bottom of the terminal. */
if (px == 0 &&
px + nx >= tty->sx &&
py + ny >= tty->sy &&
tty_term_has(tty->term, TTYC_ED)) {
tty_cursor(tty, 0, py);
tty_putcode(tty, TTYC_ED);
return;
}
/*
* On VT420 compatible terminals we can use DECFRA if the
* background colour isn't default (because it doesn't work
* after SGR 0).
*/
if (tty->term_type == TTY_VT420 && bg != 8) {
xsnprintf(tmp, sizeof tmp, "\033[32;%u;%u;%u;%u$x",
py + 1, px + 1, py + ny, px + nx);
tty_puts(tty, tmp);
return;
}
/* Full lines can be scrolled away to clear them. */
if (px == 0 &&
px + nx >= tty->sx &&
ny > 2 &&
tty_term_has(tty->term, TTYC_CSR) &&
tty_term_has(tty->term, TTYC_INDN)) {
tty_region(tty, py, py + ny - 1);
tty_margin_off(tty);
tty_putcode1(tty, TTYC_INDN, ny);
return;
}
/*
* If margins are supported, can just scroll the area off to
* clear it.
*/
if (nx > 2 &&
ny > 2 &&
tty_term_has(tty->term, TTYC_CSR) &&
tty_use_margin(tty) &&
tty_term_has(tty->term, TTYC_INDN)) {
tty_region(tty, py, py + ny - 1);
tty_margin(tty, px, px + nx - 1);
tty_putcode1(tty, TTYC_INDN, ny);
return;
}
}
/* Couldn't use an escape sequence, loop over the lines. */
for (yy = py; yy < py + ny; yy++)
tty_clear_line(tty, wp, yy, px, nx, bg);
}
void void
tty_draw_pane(struct tty *tty, const struct window_pane *wp, u_int py, u_int ox, tty_draw_pane(struct tty *tty, const struct window_pane *wp, u_int py, u_int ox,
u_int oy) u_int oy)
@@ -761,15 +878,41 @@ tty_draw_pane(struct tty *tty, const struct window_pane *wp, u_int py, u_int ox,
tty_draw_line(tty, wp, wp->screen, py, ox, oy); tty_draw_line(tty, wp, wp->screen, py, ox, oy);
} }
static const struct grid_cell *
tty_check_codeset(struct tty *tty, const struct grid_cell *gc)
{
static struct grid_cell new;
u_int n;
/* Characters less than 0x7f are always fine, no matter what. */
if (gc->data.size == 1 && *gc->data.data < 0x7f)
return (gc);
/* UTF-8 terminal and a UTF-8 character - fine. */
if (tty->flags & TTY_UTF8)
return (gc);
/* Replace by the right number of underscores. */
n = gc->data.width;
if (n > UTF8_SIZE)
n = UTF8_SIZE;
memcpy(&new, gc, sizeof new);
new.data.size = n;
memset(new.data.data, '_', n);
return (&new);
}
void void
tty_draw_line(struct tty *tty, const struct window_pane *wp, tty_draw_line(struct tty *tty, const struct window_pane *wp,
struct screen *s, u_int py, u_int ox, u_int oy) struct screen *s, u_int py, u_int ox, u_int oy)
{ {
struct grid *gd = s->grid;
struct grid_cell gc, last; struct grid_cell gc, last;
u_int i, j, sx, width; const struct grid_cell *gcp;
u_int i, j, ux, sx, nx, width;
int flags, cleared = 0; int flags, cleared = 0;
char buf[512]; char buf[512];
size_t len; size_t len, old_len;
flags = (tty->flags & TTY_NOCURSOR); flags = (tty->flags & TTY_NOCURSOR);
tty->flags |= TTY_NOCURSOR; tty->flags |= TTY_NOCURSOR;
@@ -783,14 +926,15 @@ tty_draw_line(struct tty *tty, const struct window_pane *wp,
* there may be empty background cells after it (from BCE). * there may be empty background cells after it (from BCE).
*/ */
sx = screen_size_x(s); sx = screen_size_x(s);
if (sx > s->grid->linedata[s->grid->hsize + py].cellsize) if (sx > gd->linedata[gd->hsize + py].cellsize)
sx = s->grid->linedata[s->grid->hsize + py].cellsize; sx = gd->linedata[gd->hsize + py].cellsize;
if (sx > tty->sx) if (sx > tty->sx)
sx = tty->sx; sx = tty->sx;
ux = 0;
if (wp == NULL || if (wp == NULL ||
py == 0 || py == 0 ||
(~s->grid->linedata[s->grid->hsize + py - 1].flags & GRID_LINE_WRAPPED) || (~gd->linedata[gd->hsize + py - 1].flags & GRID_LINE_WRAPPED) ||
ox != 0 || ox != 0 ||
tty->cx < tty->sx || tty->cx < tty->sx ||
screen_size_x(s) < tty->sx) { screen_size_x(s) < tty->sx) {
@@ -814,63 +958,68 @@ tty_draw_line(struct tty *tty, const struct window_pane *wp,
width = 0; width = 0;
for (i = 0; i < sx; i++) { for (i = 0; i < sx; i++) {
grid_view_get_cell(s->grid, i, py, &gc); grid_view_get_cell(gd, i, py, &gc);
gcp = tty_check_codeset(tty, &gc);
if (len != 0 && if (len != 0 &&
(((~tty->flags & TTY_UTF8) && ((gcp->attr & GRID_ATTR_CHARSET) ||
(gc.data.size != 1 || gcp->flags != last.flags ||
*gc.data.data >= 0x7f || gcp->attr != last.attr ||
gc.data.width != 1)) || gcp->fg != last.fg ||
(gc.attr & GRID_ATTR_CHARSET) || gcp->bg != last.bg ||
gc.flags != last.flags || ux + width + gcp->data.width >= screen_size_x(s) ||
gc.attr != last.attr || (sizeof buf) - len < gcp->data.size)) {
gc.fg != last.fg ||
gc.bg != last.bg ||
(sizeof buf) - len < gc.data.size)) {
tty_attributes(tty, &last, wp); tty_attributes(tty, &last, wp);
tty_putn(tty, buf, len, width); tty_putn(tty, buf, len, width);
ux += width;
len = 0; len = 0;
width = 0; width = 0;
} }
if (gc.flags & GRID_FLAG_SELECTED) if (gcp->flags & GRID_FLAG_SELECTED)
screen_select_cell(s, &last, &gc); screen_select_cell(s, &last, gcp);
else else
memcpy(&last, &gc, sizeof last); memcpy(&last, gcp, sizeof last);
if (((~tty->flags & TTY_UTF8) && if (ux + gcp->data.width > screen_size_x(s)) {
(gc.data.size != 1 ||
*gc.data.data >= 0x7f ||
gc.data.width != 1)) ||
(gc.attr & GRID_ATTR_CHARSET)) {
tty_attributes(tty, &last, wp); tty_attributes(tty, &last, wp);
if (~tty->flags & TTY_UTF8) { for (j = 0; j < gcp->data.width; j++) {
for (j = 0; j < gc.data.width; j++) if (ux + j > screen_size_x(s))
tty_putc(tty, '_'); break;
} else { tty_putc(tty, ' ');
for (j = 0; j < gc.data.size; j++) ux++;
tty_putc(tty, gc.data.data[j]);
} }
} else if (gcp->attr & GRID_ATTR_CHARSET) {
tty_attributes(tty, &last, wp);
for (j = 0; j < gcp->data.size; j++)
tty_putc(tty, gcp->data.data[j]);
ux += gc.data.width;
} else { } else {
memcpy(buf + len, gc.data.data, gc.data.size); memcpy(buf + len, gcp->data.data, gcp->data.size);
len += gc.data.size; len += gcp->data.size;
width += gc.data.width; width += gcp->data.width;
} }
} }
if (len != 0) { if (len != 0) {
tty_attributes(tty, &last, wp); if (grid_cells_equal(&last, &grid_default_cell)) {
tty_putn(tty, buf, len, width); old_len = len;
while (len > 0 && buf[len - 1] == ' ') {
len--;
width--;
}
log_debug("%s: trimmed %zu spaces", __func__,
old_len - len);
}
if (len != 0) {
tty_attributes(tty, &last, wp);
tty_putn(tty, buf, len, width);
ux += width;
}
} }
if (!cleared && sx < tty->sx) { if (!cleared && ux < screen_size_x(s)) {
nx = screen_size_x(s) - ux;
tty_default_attributes(tty, wp, 8); tty_default_attributes(tty, wp, 8);
tty_cursor(tty, ox + sx, oy + py); tty_clear_line(tty, wp, oy + py, ox + ux, nx, 8);
if (sx != screen_size_x(s) &&
ox + screen_size_x(s) >= tty->sx &&
tty_term_has(tty->term, TTYC_EL) &&
!tty_fake_bce(tty, wp, 8))
tty_putcode(tty, TTYC_EL);
else
tty_repeat_space(tty, screen_size_x(s) - sx);
} }
tty->flags = (tty->flags & ~TTY_NOCURSOR) | flags; tty->flags = (tty->flags & ~TTY_NOCURSOR) | flags;
@@ -912,7 +1061,7 @@ tty_write(void (*cmdfn)(struct tty *, const struct tty_ctx *),
ctx->xoff = wp->xoff; ctx->xoff = wp->xoff;
ctx->yoff = wp->yoff; ctx->yoff = wp->yoff;
if (status_at_line(c) == 0) if (status_at_line(c) == 0)
ctx->yoff++; ctx->yoff += status_line_size(c->session);
cmdfn(&c->tty, ctx); cmdfn(&c->tty, ctx);
} }
@@ -1018,73 +1167,54 @@ void
tty_cmd_clearline(struct tty *tty, const struct tty_ctx *ctx) tty_cmd_clearline(struct tty *tty, const struct tty_ctx *ctx)
{ {
struct window_pane *wp = ctx->wp; struct window_pane *wp = ctx->wp;
struct screen *s = wp->screen; u_int nx, py = ctx->yoff + ctx->ocy;
u_int sx = screen_size_x(s);
tty_default_attributes(tty, wp, ctx->bg); tty_default_attributes(tty, wp, ctx->bg);
tty_cursor_pane(tty, ctx, 0, ctx->ocy); nx = screen_size_x(wp->screen);
tty_clear_line(tty, wp, py, ctx->xoff, nx, ctx->bg);
if (tty_pane_full_width(tty, ctx) &&
!tty_fake_bce(tty, wp, ctx->bg) &&
tty_term_has(tty->term, TTYC_EL))
tty_putcode(tty, TTYC_EL);
else
tty_repeat_space(tty, sx);
} }
void void
tty_cmd_clearendofline(struct tty *tty, const struct tty_ctx *ctx) tty_cmd_clearendofline(struct tty *tty, const struct tty_ctx *ctx)
{ {
struct window_pane *wp = ctx->wp; struct window_pane *wp = ctx->wp;
struct screen *s = wp->screen; u_int nx, py = ctx->yoff + ctx->ocy;
u_int sx = screen_size_x(s);
tty_default_attributes(tty, wp, ctx->bg); tty_default_attributes(tty, wp, ctx->bg);
tty_cursor_pane(tty, ctx, ctx->ocx, ctx->ocy); nx = screen_size_x(wp->screen) - ctx->ocx;
tty_clear_line(tty, wp, py, ctx->xoff + ctx->ocx, nx, ctx->bg);
if (tty_pane_full_width(tty, ctx) &&
tty_term_has(tty->term, TTYC_EL) &&
!tty_fake_bce(tty, wp, ctx->bg))
tty_putcode(tty, TTYC_EL);
else
tty_repeat_space(tty, sx - ctx->ocx);
} }
void void
tty_cmd_clearstartofline(struct tty *tty, const struct tty_ctx *ctx) tty_cmd_clearstartofline(struct tty *tty, const struct tty_ctx *ctx)
{ {
struct window_pane *wp = ctx->wp; struct window_pane *wp = ctx->wp;
u_int py = ctx->yoff + ctx->ocy;
tty_default_attributes(tty, wp, ctx->bg); tty_default_attributes(tty, wp, ctx->bg);
if (ctx->xoff == 0 && tty_clear_line(tty, wp, py, ctx->xoff, ctx->ocx + 1, ctx->bg);
tty_term_has(tty->term, TTYC_EL1) &&
!tty_fake_bce(tty, ctx->wp, ctx->bg)) {
tty_cursor_pane(tty, ctx, ctx->ocx, ctx->ocy);
tty_putcode(tty, TTYC_EL1);
} else {
tty_cursor_pane(tty, ctx, 0, ctx->ocy);
tty_repeat_space(tty, ctx->ocx + 1);
}
} }
void void
tty_cmd_reverseindex(struct tty *tty, const struct tty_ctx *ctx) tty_cmd_reverseindex(struct tty *tty, const struct tty_ctx *ctx)
{ {
struct window_pane *wp = ctx->wp;
if (ctx->ocy != ctx->orupper) if (ctx->ocy != ctx->orupper)
return; return;
if (!tty_pane_full_width(tty, ctx) || if (!tty_pane_full_width(tty, ctx) ||
tty_fake_bce(tty, ctx->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_redraw_region(tty, ctx); tty_redraw_region(tty, ctx);
return; return;
} }
tty_attributes(tty, &grid_default_cell, ctx->wp); 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_off(tty);
@@ -1108,7 +1238,7 @@ tty_cmd_linefeed(struct tty *tty, const struct tty_ctx *ctx)
return; return;
} }
tty_attributes(tty, &grid_default_cell, wp); 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_pane(tty, ctx); tty_margin_pane(tty, ctx);
@@ -1130,7 +1260,7 @@ void
tty_cmd_scrollup(struct tty *tty, const struct tty_ctx *ctx) tty_cmd_scrollup(struct tty *tty, const struct tty_ctx *ctx)
{ {
struct window_pane *wp = ctx->wp; struct window_pane *wp = ctx->wp;
u_int i, lines; u_int i;
if ((!tty_pane_full_width(tty, ctx) && !tty_use_margin(tty)) || if ((!tty_pane_full_width(tty, ctx) && !tty_use_margin(tty)) ||
tty_fake_bce(tty, wp, 8) || tty_fake_bce(tty, wp, 8) ||
@@ -1139,142 +1269,86 @@ tty_cmd_scrollup(struct tty *tty, const struct tty_ctx *ctx)
return; return;
} }
tty_attributes(tty, &grid_default_cell, wp); 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_pane(tty, ctx); tty_margin_pane(tty, ctx);
/* if (ctx->num == 1 || !tty_term_has(tty->term, TTYC_INDN)) {
* Konsole has a bug where it will ignore SU if the parameter is more
* than the height of the scroll region. Clamping the parameter doesn't
* hurt in any case.
*/
lines = tty->rlower - tty->rupper;
if (lines > ctx->num)
lines = ctx->num;
if (lines == 1 || !tty_term_has(tty->term, TTYC_INDN)) {
tty_cursor(tty, tty->rright, tty->rlower); tty_cursor(tty, tty->rright, tty->rlower);
for (i = 0; i < lines; i++) for (i = 0; i < ctx->num; i++)
tty_putc(tty, '\n'); tty_putc(tty, '\n');
} else } else
tty_putcode1(tty, TTYC_INDN, lines); tty_putcode1(tty, TTYC_INDN, ctx->num);
} }
void void
tty_cmd_clearendofscreen(struct tty *tty, const struct tty_ctx *ctx) tty_cmd_clearendofscreen(struct tty *tty, const struct tty_ctx *ctx)
{ {
struct window_pane *wp = ctx->wp; struct window_pane *wp = ctx->wp;
struct screen *s = wp->screen; u_int px, py, nx, ny;
u_int i, j;
u_int sx = screen_size_x(s), sy = screen_size_y(s);
tty_default_attributes(tty, wp, ctx->bg); tty_default_attributes(tty, wp, ctx->bg);
tty_region_pane(tty, ctx, 0, sy - 1); tty_region_pane(tty, ctx, 0, screen_size_y(wp->screen) - 1);
tty_margin_off(tty); tty_margin_off(tty);
if (tty_pane_full_width(tty, ctx) && px = ctx->xoff;
ctx->yoff + wp->sy >= tty->sy - 1 && nx = screen_size_x(wp->screen);
status_at_line(tty->client) <= 0 && py = ctx->yoff + ctx->ocy + 1;
tty_term_has(tty->term, TTYC_ED)) { ny = screen_size_y(wp->screen) - ctx->ocy - 1;
tty_cursor_pane(tty, ctx, ctx->ocx, ctx->ocy);
tty_putcode(tty, TTYC_ED); tty_clear_area(tty, wp, py, ny, px, nx, ctx->bg);
} else if (tty_pane_full_width(tty, ctx) &&
tty_term_has(tty->term, TTYC_EL) && px = ctx->xoff + ctx->ocx;
!tty_fake_bce(tty, wp, ctx->bg)) { nx = screen_size_x(wp->screen) - ctx->ocx;
tty_cursor_pane(tty, ctx, ctx->ocx, ctx->ocy); py = ctx->yoff + ctx->ocy;
tty_putcode(tty, TTYC_EL);
if (ctx->ocy != sy - 1) { tty_clear_line(tty, wp, py, px, nx, ctx->bg);
tty_cursor_pane(tty, ctx, 0, ctx->ocy + 1);
for (i = ctx->ocy + 1; i < sy; i++) {
tty_putcode(tty, TTYC_EL);
if (i == sy - 1)
continue;
tty_emulate_repeat(tty, TTYC_CUD, TTYC_CUD1, 1);
tty->cy++;
}
}
} else {
tty_cursor_pane(tty, ctx, ctx->ocx, ctx->ocy);
tty_repeat_space(tty, sx - ctx->ocx);
for (j = ctx->ocy + 1; j < sy; j++) {
tty_cursor_pane(tty, ctx, 0, j);
tty_repeat_space(tty, sx);
}
}
} }
void void
tty_cmd_clearstartofscreen(struct tty *tty, const struct tty_ctx *ctx) tty_cmd_clearstartofscreen(struct tty *tty, const struct tty_ctx *ctx)
{ {
struct window_pane *wp = ctx->wp; struct window_pane *wp = ctx->wp;
struct screen *s = wp->screen; u_int px, py, nx, ny;
u_int i, j;
u_int sx = screen_size_x(s), sy = screen_size_y(s);
tty_default_attributes(tty, wp, ctx->bg); tty_default_attributes(tty, wp, ctx->bg);
tty_region_pane(tty, ctx, 0, sy - 1); tty_region_pane(tty, ctx, 0, screen_size_y(wp->screen) - 1);
tty_margin_off(tty); tty_margin_off(tty);
if (tty_pane_full_width(tty, ctx) && px = ctx->xoff;
tty_term_has(tty->term, TTYC_EL) && nx = screen_size_x(wp->screen);
!tty_fake_bce(tty, wp, ctx->bg)) { py = ctx->yoff;
tty_cursor_pane(tty, ctx, 0, 0); ny = ctx->ocy - 1;
for (i = 0; i < ctx->ocy; i++) {
tty_putcode(tty, TTYC_EL); tty_clear_area(tty, wp, py, ny, px, nx, ctx->bg);
tty_emulate_repeat(tty, TTYC_CUD, TTYC_CUD1, 1);
tty->cy++; px = ctx->xoff;
} nx = ctx->ocx + 1;
} else { py = ctx->yoff + ctx->ocy;
tty_cursor_pane(tty, ctx, 0, 0);
for (j = 0; j < ctx->ocy; j++) { tty_clear_line(tty, wp, py, px, nx, ctx->bg);
tty_cursor_pane(tty, ctx, 0, j);
tty_repeat_space(tty, sx);
}
}
tty_cursor_pane(tty, ctx, 0, ctx->ocy);
tty_repeat_space(tty, ctx->ocx + 1);
} }
void void
tty_cmd_clearscreen(struct tty *tty, const struct tty_ctx *ctx) tty_cmd_clearscreen(struct tty *tty, const struct tty_ctx *ctx)
{ {
struct window_pane *wp = ctx->wp; struct window_pane *wp = ctx->wp;
struct screen *s = wp->screen; u_int px, py, nx, ny;
u_int i, j;
u_int sx = screen_size_x(s), sy = screen_size_y(s);
tty_default_attributes(tty, wp, ctx->bg); tty_default_attributes(tty, wp, ctx->bg);
tty_region_pane(tty, ctx, 0, sy - 1); tty_region_pane(tty, ctx, 0, screen_size_y(wp->screen) - 1);
tty_margin_off(tty); tty_margin_off(tty);
if (tty_pane_full_width(tty, ctx) && px = ctx->xoff;
ctx->yoff + wp->sy >= tty->sy - 1 && nx = screen_size_x(wp->screen);
status_at_line(tty->client) <= 0 && py = ctx->yoff;
tty_term_has(tty->term, TTYC_ED)) { ny = screen_size_y(wp->screen);
tty_cursor_pane(tty, ctx, 0, 0);
tty_putcode(tty, TTYC_ED); tty_clear_area(tty, wp, py, ny, px, nx, ctx->bg);
} else if (tty_pane_full_width(tty, ctx) &&
tty_term_has(tty->term, TTYC_EL) &&
!tty_fake_bce(tty, wp, ctx->bg)) {
tty_cursor_pane(tty, ctx, 0, 0);
for (i = 0; i < sy; i++) {
tty_putcode(tty, TTYC_EL);
if (i != sy - 1) {
tty_emulate_repeat(tty, TTYC_CUD, TTYC_CUD1, 1);
tty->cy++;
}
}
} else {
tty_cursor_pane(tty, ctx, 0, 0);
for (j = 0; j < sy; j++) {
tty_cursor_pane(tty, ctx, 0, j);
tty_repeat_space(tty, sx);
}
}
} }
void void
@@ -1349,7 +1423,7 @@ static void
tty_cell(struct tty *tty, const struct grid_cell *gc, tty_cell(struct tty *tty, const struct grid_cell *gc,
const struct window_pane *wp) const struct window_pane *wp)
{ {
u_int i; 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_EARLYWRAP) &&
@@ -1365,22 +1439,16 @@ tty_cell(struct tty *tty, const struct grid_cell *gc,
tty_attributes(tty, gc, wp); tty_attributes(tty, gc, wp);
/* Get the cell and if ASCII write with putc to do ACS translation. */ /* Get the cell and if ASCII write with putc to do ACS translation. */
if (gc->data.size == 1) { gcp = tty_check_codeset(tty, gc);
if (*gc->data.data < 0x20 || *gc->data.data == 0x7f) if (gcp->data.size == 1) {
if (*gcp->data.data < 0x20 || *gcp->data.data == 0x7f)
return; return;
tty_putc(tty, *gc->data.data); tty_putc(tty, *gcp->data.data);
return;
}
/* If not UTF-8, write _. */
if (!(tty->flags & TTY_UTF8)) {
for (i = 0; i < gc->data.width; i++)
tty_putc(tty, '_');
return; return;
} }
/* Write the data. */ /* Write the data. */
tty_putn(tty, gc->data.data, gc->data.size, gc->data.width); tty_putn(tty, gcp->data.data, gcp->data.size, gcp->data.width);
} }
void void
@@ -1389,7 +1457,7 @@ tty_reset(struct tty *tty)
struct grid_cell *gc = &tty->cell; struct grid_cell *gc = &tty->cell;
if (!grid_cells_equal(gc, &grid_default_cell)) { if (!grid_cells_equal(gc, &grid_default_cell)) {
if ((gc->attr & GRID_ATTR_CHARSET) && tty_use_acs(tty)) if ((gc->attr & GRID_ATTR_CHARSET) && tty_acs_needed(tty))
tty_putcode(tty, TTYC_RMACS); tty_putcode(tty, TTYC_RMACS);
tty_putcode(tty, TTYC_SGR0); tty_putcode(tty, TTYC_SGR0);
memcpy(gc, &grid_default_cell, sizeof *gc); memcpy(gc, &grid_default_cell, sizeof *gc);
@@ -1559,7 +1627,8 @@ tty_cursor(struct tty *tty, u_int cx, u_int cy)
} }
/* Zero on the next line. */ /* Zero on the next line. */
if (cx == 0 && cy == thisy + 1 && thisy != tty->rlower) { if (cx == 0 && cy == thisy + 1 && thisy != tty->rlower &&
(!tty_use_margin(tty) || tty->rleft == 0)) {
tty_putc(tty, '\r'); tty_putc(tty, '\r');
tty_putc(tty, '\n'); tty_putc(tty, '\n');
goto out; goto out;
@@ -1572,7 +1641,7 @@ tty_cursor(struct tty *tty, u_int cx, u_int cy)
*/ */
/* To left edge. */ /* To left edge. */
if (cx == 0) { if (cx == 0 && (!tty_use_margin(tty) || tty->rleft == 0)) {
tty_putc(tty, '\r'); tty_putc(tty, '\r');
goto out; goto out;
} }
@@ -1739,7 +1808,7 @@ tty_attributes(struct tty *tty, const struct grid_cell *gc,
tty_putcode(tty, TTYC_INVIS); tty_putcode(tty, TTYC_INVIS);
if (changed & GRID_ATTR_STRIKETHROUGH) if (changed & GRID_ATTR_STRIKETHROUGH)
tty_putcode(tty, TTYC_SMXX); tty_putcode(tty, TTYC_SMXX);
if ((changed & GRID_ATTR_CHARSET) && tty_use_acs(tty)) if ((changed & GRID_ATTR_CHARSET) && tty_acs_needed(tty))
tty_putcode(tty, TTYC_SMACS); tty_putcode(tty, TTYC_SMACS);
} }
@@ -1808,15 +1877,23 @@ tty_check_fg(struct tty *tty, const struct window_pane *wp,
u_int colours; u_int colours;
int c; int c;
/* Perform substitution if this pane has a palette */ /*
if ((~gc->flags & GRID_FLAG_NOPALETTE) && * Perform substitution if this pane has a palette. If the bright
(c = window_pane_get_palette(wp, gc->fg)) != -1) * attribute is set, use the bright entry in the palette by changing to
gc->fg = c; * the aixterm colour.
*/
if (~gc->flags & GRID_FLAG_NOPALETTE) {
c = gc->fg;
if (c < 8 && gc->attr & GRID_ATTR_BRIGHT)
c += 90;
if ((c = window_pane_get_palette(wp, c)) != -1)
gc->fg = c;
}
/* 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_flag(tty->term, TTYC_TC)) { if (!tty_term_has(tty->term, TTYC_SETRGBF)) {
colour_split_rgb(gc->fg, &r, &g, &b); colour_split_rgb(gc->fg, &r, &g, &b);
gc->fg = colour_find_rgb(r, g, b); gc->fg = colour_find_rgb(r, g, b);
} else } else
@@ -1861,15 +1938,16 @@ tty_check_bg(struct tty *tty, const struct window_pane *wp,
u_int colours; u_int colours;
int c; int c;
/* Perform substitution if this pane has a palette */ /* Perform substitution if this pane has a palette. */
if ((~gc->flags & GRID_FLAG_NOPALETTE) && if (~gc->flags & GRID_FLAG_NOPALETTE) {
(c = window_pane_get_palette(wp, gc->bg)) != -1) if ((c = window_pane_get_palette(wp, gc->bg)) != -1)
gc->bg = c; gc->bg = c;
}
/* 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_flag(tty->term, TTYC_TC)) { if (!tty_term_has(tty->term, TTYC_SETRGBB)) {
colour_split_rgb(gc->bg, &r, &g, &b); colour_split_rgb(gc->bg, &r, &g, &b);
gc->bg = colour_find_rgb(r, g, b); gc->bg = colour_find_rgb(r, g, b);
} else } else
@@ -1973,11 +2051,15 @@ tty_try_colour(struct tty *tty, int colour, const char *type)
if (colour & COLOUR_FLAG_256) { if (colour & COLOUR_FLAG_256) {
/* /*
* If the user has specified -2 to the client, setaf and setab * If the user has specified -2 to the client (meaning
* may not work (or they may not want to use them), so send the * TERM_256COLOURS is set), setaf and setab may not work (or
* usual sequence. * they may not want to use them), so send the usual sequence.
*
* Also if RGB is set, setaf and setab do not support the 256
* colour palette so use the sequences directly there too.
*/ */
if (tty->term_flags & TERM_256COLOURS) if ((tty->term_flags & TERM_256COLOURS) ||
tty_term_has(tty->term, TTYC_RGB))
goto fallback_256; goto fallback_256;
/* /*
@@ -2000,13 +2082,17 @@ tty_try_colour(struct tty *tty, int colour, const char *type)
} }
if (colour & COLOUR_FLAG_RGB) { if (colour & COLOUR_FLAG_RGB) {
if (!tty_term_flag(tty->term, TTYC_TC)) if (*type == '3') {
return (-1); if (!tty_term_has(tty->term, TTYC_SETRGBF))
return (-1);
colour_split_rgb(colour & 0xffffff, &r, &g, &b); colour_split_rgb(colour & 0xffffff, &r, &g, &b);
xsnprintf(s, sizeof s, "\033[%s;2;%hhu;%hhu;%hhum", type, tty_putcode3(tty, TTYC_SETRGBF, r, g, b);
r, g, b); } else {
tty_puts(tty, s); if (!tty_term_has(tty->term, TTYC_SETRGBB))
return (-1);
colour_split_rgb(colour & 0xffffff, &r, &g, &b);
tty_putcode3(tty, TTYC_SETRGBB, r, g, b);
}
return (0); return (0);
} }
@@ -2014,6 +2100,7 @@ tty_try_colour(struct tty *tty, int colour, const char *type)
fallback_256: fallback_256:
xsnprintf(s, sizeof s, "\033[%s;5;%dm", type, colour & 0xff); xsnprintf(s, sizeof s, "\033[%s;5;%dm", type, colour & 0xff);
log_debug("%s: 256 colour fallback: %s", tty->client->name, s);
tty_puts(tty, s); tty_puts(tty, s);
return (0); return (0);
} }

27
utf8.c
View File

@@ -232,6 +232,30 @@ utf8_stravis(char **dst, const char *src, int flag)
return (len); return (len);
} }
/* Does this string contain anything that isn't valid UTF-8? */
int
utf8_isvalid(const char *s)
{
struct utf8_data ud;
const char *end;
enum utf8_state more;
end = s + strlen(s);
while (s < end) {
if ((more = utf8_open(&ud, *s)) == UTF8_MORE) {
while (++s < end && more == UTF8_MORE)
more = utf8_append(&ud, *s);
if (more == UTF8_DONE)
continue;
return (0);
}
if (*s < 0x20 || *s > 0x7e)
return (0);
s++;
}
return (1);
}
/* /*
* Sanitize a string, changing any UTF-8 characters to '_'. Caller should free * Sanitize a string, changing any UTF-8 characters to '_'. Caller should free
* the returned string. Anything not valid printable ASCII or UTF-8 is * the returned string. Anything not valid printable ASCII or UTF-8 is
@@ -427,8 +451,7 @@ utf8_rtrimcstr(const char *s, u_int width)
next = end - 1; next = end - 1;
at = 0; at = 0;
for (;;) for (;;) {
{
if (at + next->width > width) { if (at + next->width > width) {
next++; next++;
break; break;

375
window-buffer.c Normal file
View File

@@ -0,0 +1,375 @@
/* $OpenBSD$ */
/*
* Copyright (c) 2017 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 <time.h>
#include "tmux.h"
static struct screen *window_buffer_init(struct window_pane *,
struct cmd_find_state *, struct args *);
static void window_buffer_free(struct window_pane *);
static void window_buffer_resize(struct window_pane *, u_int,
u_int);
static void window_buffer_key(struct window_pane *,
struct client *, struct session *, key_code,
struct mouse_event *);
#define WINDOW_BUFFER_DEFAULT_COMMAND "paste-buffer -b '%%'"
#define WINDOW_BUFFER_DEFAULT_FORMAT \
"#{buffer_size} bytes (#{t:buffer_created})"
const struct window_mode window_buffer_mode = {
.name = "buffer-mode",
.init = window_buffer_init,
.free = window_buffer_free,
.resize = window_buffer_resize,
.key = window_buffer_key,
};
enum window_buffer_sort_type {
WINDOW_BUFFER_BY_TIME,
WINDOW_BUFFER_BY_NAME,
WINDOW_BUFFER_BY_SIZE,
};
static const char *window_buffer_sort_list[] = {
"time",
"name",
"size"
};
struct window_buffer_itemdata {
const char *name;
u_int order;
size_t size;
};
struct window_buffer_modedata {
struct mode_tree_data *data;
char *command;
char *format;
struct window_buffer_itemdata **item_list;
u_int item_size;
};
static struct window_buffer_itemdata *
window_buffer_add_item(struct window_buffer_modedata *data)
{
struct window_buffer_itemdata *item;
data->item_list = xreallocarray(data->item_list, data->item_size + 1,
sizeof *data->item_list);
item = data->item_list[data->item_size++] = xcalloc(1, sizeof *item);
return (item);
}
static void
window_buffer_free_item(struct window_buffer_itemdata *item)
{
free((void *)item->name);
free(item);
}
static int
window_buffer_cmp_name(const void *a0, const void *b0)
{
const struct window_buffer_itemdata *const *a = a0;
const struct window_buffer_itemdata *const *b = b0;
return (strcmp((*a)->name, (*b)->name));
}
static int
window_buffer_cmp_time(const void *a0, const void *b0)
{
const struct window_buffer_itemdata *const *a = a0;
const struct window_buffer_itemdata *const *b = b0;
if ((*a)->order > (*b)->order)
return (-1);
if ((*a)->order < (*b)->order)
return (1);
return (strcmp((*a)->name, (*b)->name));
}
static int
window_buffer_cmp_size(const void *a0, const void *b0)
{
const struct window_buffer_itemdata *const *a = a0;
const struct window_buffer_itemdata *const *b = b0;
if ((*a)->size > (*b)->size)
return (-1);
if ((*a)->size < (*b)->size)
return (1);
return (strcmp((*a)->name, (*b)->name));
}
static void
window_buffer_build(void *modedata, u_int sort_type, __unused uint64_t *tag,
const char *filter)
{
struct window_buffer_modedata *data = modedata;
struct window_buffer_itemdata *item;
u_int i;
struct paste_buffer *pb;
char *text, *cp;
struct format_tree *ft;
for (i = 0; i < data->item_size; i++)
window_buffer_free_item(data->item_list[i]);
free(data->item_list);
data->item_list = NULL;
data->item_size = 0;
pb = NULL;
while ((pb = paste_walk(pb)) != NULL) {
item = window_buffer_add_item(data);
item->name = xstrdup(paste_buffer_name(pb));
paste_buffer_data(pb, &item->size);
item->order = paste_buffer_order(pb);
}
switch (sort_type) {
case WINDOW_BUFFER_BY_NAME:
qsort(data->item_list, data->item_size, sizeof *data->item_list,
window_buffer_cmp_name);
break;
case WINDOW_BUFFER_BY_TIME:
qsort(data->item_list, data->item_size, sizeof *data->item_list,
window_buffer_cmp_time);
break;
case WINDOW_BUFFER_BY_SIZE:
qsort(data->item_list, data->item_size, sizeof *data->item_list,
window_buffer_cmp_size);
break;
}
for (i = 0; i < data->item_size; i++) {
item = data->item_list[i];
pb = paste_get_name(item->name);
if (pb == NULL)
continue;
ft = format_create(NULL, NULL, FORMAT_NONE, 0);
format_defaults_paste_buffer(ft, pb);
if (filter != NULL) {
cp = format_expand(ft, filter);
if (!format_true(cp)) {
free(cp);
format_free(ft);
continue;
}
free(cp);
}
text = format_expand(ft, data->format);
mode_tree_add(data->data, NULL, item, item->order, item->name,
text, -1);
free(text);
format_free(ft);
}
}
static void
window_buffer_draw(__unused void *modedata, void *itemdata,
struct screen_write_ctx *ctx, u_int sx, u_int sy)
{
struct window_buffer_itemdata *item = itemdata;
struct paste_buffer *pb;
char line[1024];
const char *pdata, *end, *cp;
size_t psize, at;
u_int i, cx = ctx->s->cx, cy = ctx->s->cy;
pb = paste_get_name(item->name);
if (pb == NULL)
return;
pdata = end = paste_buffer_data(pb, &psize);
for (i = 0; i < sy; i++) {
at = 0;
while (end != pdata + psize && *end != '\n') {
if ((sizeof line) - at > 5) {
cp = vis(line + at, *end, VIS_TAB|VIS_OCTAL, 0);
at = cp - line;
}
end++;
}
if (at > sx)
at = sx;
line[at] = '\0';
if (*line != '\0') {
screen_write_cursormove(ctx, cx, cy + i);
screen_write_puts(ctx, &grid_default_cell, "%s", line);
}
if (end == pdata + psize)
break;
end++;
}
}
static int
window_buffer_search(__unused void *modedata, void *itemdata, const char *ss)
{
struct window_buffer_itemdata *item = itemdata;
struct paste_buffer *pb;
const char *bufdata;
size_t bufsize;
if ((pb = paste_get_name(item->name)) == NULL)
return (0);
if (strstr(item->name, ss) != NULL)
return (1);
bufdata = paste_buffer_data(pb, &bufsize);
return (memmem(bufdata, bufsize, ss, strlen(ss)) != NULL);
}
static struct screen *
window_buffer_init(struct window_pane *wp, __unused struct cmd_find_state *fs,
struct args *args)
{
struct window_buffer_modedata *data;
struct screen *s;
wp->modedata = data = xcalloc(1, sizeof *data);
if (args == NULL || !args_has(args, 'F'))
data->format = xstrdup(WINDOW_BUFFER_DEFAULT_FORMAT);
else
data->format = xstrdup(args_get(args, 'F'));
if (args == NULL || args->argc == 0)
data->command = xstrdup(WINDOW_BUFFER_DEFAULT_COMMAND);
else
data->command = xstrdup(args->argv[0]);
data->data = mode_tree_start(wp, args, window_buffer_build,
window_buffer_draw, window_buffer_search, data,
window_buffer_sort_list, nitems(window_buffer_sort_list), &s);
mode_tree_zoom(data->data, args);
mode_tree_build(data->data);
mode_tree_draw(data->data);
return (s);
}
static void
window_buffer_free(struct window_pane *wp)
{
struct window_buffer_modedata *data = wp->modedata;
u_int i;
if (data == NULL)
return;
mode_tree_free(data->data);
for (i = 0; i < data->item_size; i++)
window_buffer_free_item(data->item_list[i]);
free(data->item_list);
free(data->format);
free(data->command);
free(data);
}
static void
window_buffer_resize(struct window_pane *wp, u_int sx, u_int sy)
{
struct window_buffer_modedata *data = wp->modedata;
mode_tree_resize(data->data, sx, sy);
}
static void
window_buffer_do_delete(void* modedata, void *itemdata,
__unused struct client *c, __unused key_code key)
{
struct window_buffer_modedata *data = modedata;
struct window_buffer_itemdata *item = itemdata;
struct paste_buffer *pb;
if (item == mode_tree_get_current(data->data))
mode_tree_down(data->data, 0);
if ((pb = paste_get_name(item->name)) != NULL)
paste_free(pb);
}
static void
window_buffer_do_paste(void* modedata, void *itemdata, struct client *c,
__unused key_code key)
{
struct window_buffer_modedata *data = modedata;
struct window_buffer_itemdata *item = itemdata;
struct paste_buffer *pb;
if ((pb = paste_get_name(item->name)) != NULL)
mode_tree_run_command(c, NULL, data->command, item->name);
}
static void
window_buffer_key(struct window_pane *wp, struct client *c,
__unused struct session *s, key_code key, struct mouse_event *m)
{
struct window_buffer_modedata *data = wp->modedata;
struct mode_tree_data *mtd = data->data;
struct window_buffer_itemdata *item;
int finished;
finished = mode_tree_key(mtd, c, &key, m, NULL, NULL);
switch (key) {
case 'd':
item = mode_tree_get_current(mtd);
window_buffer_do_delete(data, item, c, key);
mode_tree_build(mtd);
break;
case 'D':
mode_tree_each_tagged(mtd, window_buffer_do_delete, c, key, 0);
mode_tree_build(mtd);
break;
case 'P':
mode_tree_each_tagged(mtd, window_buffer_do_paste, c, key, 0);
finished = 1;
break;
case 'p':
case '\r':
item = mode_tree_get_current(mtd);
window_buffer_do_paste(data, item, c, key);
finished = 1;
break;
}
if (finished || paste_get_top(NULL) == NULL)
window_pane_reset_mode(wp);
else {
mode_tree_draw(mtd);
wp->flags |= PANE_REDRAW;
}
}

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