154 Commits
2.6 ... 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
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
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
59 changed files with 2299 additions and 1225 deletions

62
CHANGES
View File

@@ -1,3 +1,65 @@
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 CHANGES FROM 2.5 TO 2.6, 05 October 2017
* Add select-pane -T to set pane title. * Add select-pane -T to set pane title.

5
README
View File

@@ -43,11 +43,6 @@ the source tree with:
A small example configuration in example_tmux.conf. A small 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:
https://github.com/imomaliev/tmux-bash-completion https://github.com/imomaliev/tmux-bash-completion

15
TODO
View File

@@ -49,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
@@ -110,21 +111,23 @@
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) * marker lines in history (GitHub issue 1042)
* tree mode stuff: predefined filters, tag-all key, ... * 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. these do not have a hook at * finish after hooks for special commands. these do not have a hook at
the moment: the moment:
attach-session detach-client kill-server respawn-window attach-session detach-client kill-server respawn-window
swap-window break-pane find-window kill-session rotate-window swap-window break-pane find-window kill-session rotate-window
switch-client choose-tree if-shell kill-window run-shell switch-client choose-tree if-shell kill-window run-shell
wait-for command-prompt join-pane move-window source-file wait-for command-prompt join-pane move-window source-file
confirm-before kill-pane respawn-pane swap-pane confirm-before kill-pane respawn-pane swap-pane
at the moment AFTERHOOK uses current only if target is not valid, 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? but target is ALWAYS valid - it should use current if no -t flag?
then select-* could use AFTERHOOK 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 * for session_closed, if no sessions at all, perhaps fake up a
temporary one temporary one

View File

@@ -200,8 +200,10 @@ alerts_check_bell(struct window *w)
* not check WINLINK_BELL). * 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;
server_status_session(s);
}
if (!alerts_action_applies(wl, "bell-action")) if (!alerts_action_applies(wl, "bell-action"))
continue; continue;
notify_winlink("alert-bell", wl); notify_winlink("alert-bell", wl);
@@ -234,8 +236,10 @@ 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; wl->flags |= WINLINK_ACTIVITY;
server_status_session(s);
}
if (!alerts_action_applies(wl, "activity-action")) if (!alerts_action_applies(wl, "activity-action"))
continue; continue;
notify_winlink("alert-activity", wl); notify_winlink("alert-activity", wl);
@@ -268,8 +272,10 @@ 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; wl->flags |= WINLINK_SILENCE;
server_status_session(s);
}
if (!alerts_action_applies(wl, "silence-action")) if (!alerts_action_applies(wl, "silence-action"))
continue; continue;
notify_winlink("alert-silence", wl); notify_winlink("alert-silence", wl);

174
cfg.c
View File

@@ -26,6 +26,17 @@
#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;
@@ -101,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)
{ {
@@ -108,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) {
@@ -136,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);
@@ -176,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);
@@ -189,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);
} }

View File

@@ -278,10 +278,10 @@ client_main(struct event_base *base, int argc, char **argv, int flags)
client_peer = proc_add_peer(client_proc, fd, client_dispatch, NULL); client_peer = proc_add_peer(client_proc, fd, client_dispatch, NULL);
/* 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 = "";
@@ -338,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. */
@@ -449,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) {

View File

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

View File

@@ -389,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;
} }
@@ -587,8 +587,6 @@ 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) if (fs->wp == NULL)
return (-1); return (-1);
@@ -912,16 +910,12 @@ cmd_find_from_client(struct cmd_find_state *fs, struct client *c, int flags)
*/ */
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;

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

@@ -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,14 +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; const char *path, *cmd, *tmp;
char **argv, *cause, *cp, *to_free = NULL; char **argv, *cause, *cp, *newname, *cwd = NULL;
int detached, already_attached, idx, argc; int detached, already_attached, idx, argc;
int is_control = 0; 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) {
/* /*
@@ -95,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;
} }
} }
@@ -149,14 +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 = format_single(item, cwd, c, NULL, NULL, NULL); else if (c != NULL && c->session == NULL && c->cwd != NULL)
cwd = to_free; cwd = xstrdup(c->cwd);
} else if (c != NULL && c->session == NULL && c->cwd != NULL)
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
@@ -261,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);
} }
/* /*
@@ -331,10 +336,12 @@ cmd_new_session_exec(struct cmd *self, struct cmdq_item *item)
cmd_find_from_session(&fs, s, 0); cmd_find_from_session(&fs, s, 0);
hooks_insert(s->hooks, item, &fs, "after-new-session"); hooks_insert(s->hooks, item, &fs, "after-new-session");
free(to_free); free(cwd);
free(newname);
return (CMD_RETURN_NORMAL); return (CMD_RETURN_NORMAL);
error: error:
free(to_free); free(cwd);
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; const char *cmd, *path, *template, *tmp;
char **argv, *cause, *cp, *to_free = NULL; 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;
if (args_has(args, 'c')) { if ((tmp = args_get(args, 'c')) != NULL)
cwd = args_get(args, 'c'); cwd = format_single(item, tmp, c, s, NULL, NULL);
to_free = format_single(item, cwd, c, s, NULL, NULL); else if (item->client != NULL && item->client->session == NULL)
cwd = to_free; 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);
@@ -149,10 +152,12 @@ cmd_new_window_exec(struct cmd *self, struct cmdq_item *item)
cmd_find_from_winlink(&fs, wl, 0); cmd_find_from_winlink(&fs, wl, 0);
hooks_insert(s->hooks, item, &fs, "after-new-window"); hooks_insert(s->hooks, item, &fs, "after-new-window");
free(to_free); free(name);
free(cwd);
return (CMD_RETURN_NORMAL); return (CMD_RETURN_NORMAL);
error: error:
free(to_free); free(name);
free(cwd);
return (CMD_RETURN_ERROR); return (CMD_RETURN_ERROR);
} }

View File

@@ -35,6 +35,7 @@
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_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 *);
@@ -42,8 +43,8 @@ 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 },
@@ -60,7 +61,7 @@ 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; sigset_t set, oldset;
@@ -90,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));
@@ -118,19 +128,25 @@ cmd_pipe_pane_exec(struct cmd *self, struct cmdq_item *item)
sigprocmask(SIG_SETMASK, &oldset, NULL); sigprocmask(SIG_SETMASK, &oldset, NULL);
close(pipe_fd[0]); close(pipe_fd[0]);
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);
@@ -143,24 +159,46 @@ cmd_pipe_pane_exec(struct cmd *self, struct cmdq_item *item)
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,
cmd_pipe_pane_write_callback, 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 static void
cmd_pipe_pane_write_callback(__unused struct bufferevent *bufev, void *data) cmd_pipe_pane_write_callback(__unused struct bufferevent *bufev, void *data)
{ {
struct window_pane *wp = data; struct window_pane *wp = data;
log_debug("%%%u pipe empty", wp->id); log_debug("%%%u pipe empty", wp->id);
if (window_pane_destroy_ready(wp)) if (window_pane_destroy_ready(wp))
server_destroy_pane(wp, 1); server_destroy_pane(wp, 1);
} }

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

@@ -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"
/* /*
@@ -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')) {
@@ -148,8 +152,11 @@ cmd_select_pane_exec(struct cmd *self, struct cmdq_item *item)
} }
if (args_has(self->args, 'T')) { if (args_has(self->args, 'T')) {
screen_set_title(&wp->base, args_get(self->args, 'T')); pane_title = format_single(item, args_get(self->args, 'T'),
server_status_window(wp->window); 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)

View File

@@ -192,7 +192,9 @@ cmd_set_option_exec(struct cmd *self, struct cmdq_item *item)
if (o == NULL) if (o == NULL)
goto out; 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));

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; const char *cmd, *path, *shell, *template, *tmp;
char **argv, *cause, *new_cause, *cp, *to_free = NULL; 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;
} }
if (args_has(args, 'c')) { if ((tmp = args_get(args, 'c')) != NULL)
cwd = args_get(args, 'c'); cwd = format_single(item, tmp, c, s, NULL, NULL);
to_free = format_single(item, cwd, c, s, NULL, NULL); else if (item->client != NULL && item->client->session == NULL)
cwd = to_free; 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,13 +125,12 @@ 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_make_leaf(lc, new_wp); layout_make_leaf(lc, new_wp);
path = NULL; path = NULL;
@@ -174,7 +172,7 @@ cmd_split_window_exec(struct cmd *self, struct cmdq_item *item)
cmd_find_from_winlink_pane(&fs, wl, new_wp, 0); cmd_find_from_winlink_pane(&fs, wl, new_wp, 0);
hooks_insert(s->hooks, item, &fs, "after-split-window"); hooks_insert(s->hooks, item, &fs, "after-split-window");
free(to_free); free(cwd);
return (CMD_RETURN_NORMAL); return (CMD_RETURN_NORMAL);
error: error:
@@ -185,6 +183,6 @@ error:
cmdq_error(item, "create pane failed: %s", cause); cmdq_error(item, "create pane failed: %s", cause);
free(cause); free(cause);
free(to_free); free(cwd);
return (CMD_RETURN_ERROR); return (CMD_RETURN_ERROR);
} }

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>

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

View File

@@ -1,6 +1,6 @@
# configure.ac # configure.ac
AC_INIT(tmux, 2.6) AC_INIT(tmux, 2.7-rc)
AC_PREREQ([2.60]) AC_PREREQ([2.60])
AC_CONFIG_AUX_DIR(etc) AC_CONFIG_AUX_DIR(etc)
@@ -285,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(
[ [
@@ -297,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
@@ -614,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

@@ -191,10 +191,15 @@ 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;
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;
@@ -290,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);
@@ -587,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);
} }
@@ -781,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;
} }
@@ -1102,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;
@@ -1141,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) {
@@ -1269,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);
@@ -1392,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;
if (ft->w == NULL) if (ft->w == NULL)
ft->w = wp->window; ft->w = wp->window;
@@ -1415,8 +1458,7 @@ format_defaults_pane(struct format_tree *ft, struct window_pane *wp)
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); 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);
@@ -1427,8 +1469,10 @@ format_defaults_pane(struct format_tree *ft, struct window_pane *wp)
format_add(ft, "pane_bottom", "%u", wp->yoff + wp->sy - 1); format_add(ft, "pane_bottom", "%u", wp->yoff + wp->sy - 1);
format_add(ft, "pane_at_left", "%d", wp->xoff == 0); format_add(ft, "pane_at_left", "%d", wp->xoff == 0);
format_add(ft, "pane_at_top", "%d", wp->yoff == 0); format_add(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_right", "%d",
format_add(ft, "pane_at_bottom", "%d", wp->yoff + wp->sy == wp->window->sy); 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);

513
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,
@@ -182,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);
} }
@@ -240,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);
} }
@@ -423,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)
@@ -457,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)
@@ -953,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, ...)

457
input.c
View File

@@ -58,6 +58,19 @@ struct input_cell {
int g1set; /* 1 if ACS */ int g1set; /* 1 if ACS */
}; };
/* Input parser argument. */
struct input_param {
enum {
INPUT_MISSING,
INPUT_NUMBER,
INPUT_STRING
} type;
union {
int num;
char *str;
};
};
/* Input parser context. */ /* Input parser context. */
struct input_ctx { struct input_ctx {
struct window_pane *wp; struct window_pane *wp;
@@ -81,10 +94,11 @@ struct input_ctx {
size_t input_len; size_t input_len;
size_t input_space; size_t input_space;
int param_list[24]; /* -1 not present */ struct input_param param_list[24];
u_int param_list_len; u_int param_list_len;
struct utf8_data utf8data; struct utf8_data utf8data;
int utf8started;
int ch; int ch;
int last; int last;
@@ -146,9 +160,7 @@ static void input_csi_dispatch_sgr_256(struct input_ctx *, int, u_int *);
static void input_csi_dispatch_sgr_rgb(struct input_ctx *, int, u_int *); static void input_csi_dispatch_sgr_rgb(struct input_ctx *, int, u_int *);
static void input_csi_dispatch_sgr(struct input_ctx *); static void input_csi_dispatch_sgr(struct input_ctx *);
static int input_dcs_dispatch(struct input_ctx *); static int input_dcs_dispatch(struct input_ctx *);
static int input_utf8_open(struct input_ctx *); static int input_top_bit_set(struct input_ctx *);
static int input_utf8_add(struct input_ctx *);
static int input_utf8_close(struct input_ctx *);
/* Command table comparison function. */ /* Command table comparison function. */
static int input_table_compare(const void *, const void *); static int input_table_compare(const void *, const void *);
@@ -314,9 +326,6 @@ static const struct input_transition input_state_osc_string_table[];
static const struct input_transition input_state_apc_string_table[]; static const struct input_transition input_state_apc_string_table[];
static const struct input_transition input_state_rename_string_table[]; static const struct input_transition input_state_rename_string_table[];
static const struct input_transition input_state_consume_st_table[]; static const struct input_transition input_state_consume_st_table[];
static const struct input_transition input_state_utf8_three_table[];
static const struct input_transition input_state_utf8_two_table[];
static const struct input_transition input_state_utf8_one_table[];
/* ground state definition. */ /* ground state definition. */
static const struct input_state input_state_ground = { static const struct input_state input_state_ground = {
@@ -437,27 +446,6 @@ static const struct input_state input_state_consume_st = {
input_state_consume_st_table input_state_consume_st_table
}; };
/* utf8_three state definition. */
static const struct input_state input_state_utf8_three = {
"utf8_three",
NULL, NULL,
input_state_utf8_three_table
};
/* utf8_two state definition. */
static const struct input_state input_state_utf8_two = {
"utf8_two",
NULL, NULL,
input_state_utf8_two_table
};
/* utf8_one state definition. */
static const struct input_state input_state_utf8_one = {
"utf8_one",
NULL, NULL,
input_state_utf8_one_table
};
/* ground state table. */ /* ground state table. */
static const struct input_transition input_state_ground_table[] = { static const struct input_transition input_state_ground_table[] = {
INPUT_STATE_ANYWHERE, INPUT_STATE_ANYWHERE,
@@ -467,11 +455,7 @@ static const struct input_transition input_state_ground_table[] = {
{ 0x1c, 0x1f, input_c0_dispatch, NULL }, { 0x1c, 0x1f, input_c0_dispatch, NULL },
{ 0x20, 0x7e, input_print, NULL }, { 0x20, 0x7e, input_print, NULL },
{ 0x7f, 0x7f, NULL, NULL }, { 0x7f, 0x7f, NULL, NULL },
{ 0x80, 0xc1, NULL, NULL }, { 0x80, 0xff, input_top_bit_set, NULL },
{ 0xc2, 0xdf, input_utf8_open, &input_state_utf8_one },
{ 0xe0, 0xef, input_utf8_open, &input_state_utf8_two },
{ 0xf0, 0xf4, input_utf8_open, &input_state_utf8_three },
{ 0xf5, 0xff, NULL, NULL },
{ -1, -1, NULL, NULL } { -1, -1, NULL, NULL }
}; };
@@ -526,7 +510,7 @@ static const struct input_transition input_state_csi_enter_table[] = {
{ 0x1c, 0x1f, input_c0_dispatch, NULL }, { 0x1c, 0x1f, input_c0_dispatch, NULL },
{ 0x20, 0x2f, input_intermediate, &input_state_csi_intermediate }, { 0x20, 0x2f, input_intermediate, &input_state_csi_intermediate },
{ 0x30, 0x39, input_parameter, &input_state_csi_parameter }, { 0x30, 0x39, input_parameter, &input_state_csi_parameter },
{ 0x3a, 0x3a, NULL, &input_state_csi_ignore }, { 0x3a, 0x3a, input_parameter, &input_state_csi_parameter },
{ 0x3b, 0x3b, input_parameter, &input_state_csi_parameter }, { 0x3b, 0x3b, input_parameter, &input_state_csi_parameter },
{ 0x3c, 0x3f, input_intermediate, &input_state_csi_parameter }, { 0x3c, 0x3f, input_intermediate, &input_state_csi_parameter },
{ 0x40, 0x7e, input_csi_dispatch, &input_state_ground }, { 0x40, 0x7e, input_csi_dispatch, &input_state_ground },
@@ -544,7 +528,7 @@ static const struct input_transition input_state_csi_parameter_table[] = {
{ 0x1c, 0x1f, input_c0_dispatch, NULL }, { 0x1c, 0x1f, input_c0_dispatch, NULL },
{ 0x20, 0x2f, input_intermediate, &input_state_csi_intermediate }, { 0x20, 0x2f, input_intermediate, &input_state_csi_intermediate },
{ 0x30, 0x39, input_parameter, NULL }, { 0x30, 0x39, input_parameter, NULL },
{ 0x3a, 0x3a, NULL, &input_state_csi_ignore }, { 0x3a, 0x3a, input_parameter, NULL },
{ 0x3b, 0x3b, input_parameter, NULL }, { 0x3b, 0x3b, input_parameter, NULL },
{ 0x3c, 0x3f, NULL, &input_state_csi_ignore }, { 0x3c, 0x3f, NULL, &input_state_csi_ignore },
{ 0x40, 0x7e, input_csi_dispatch, &input_state_ground }, { 0x40, 0x7e, input_csi_dispatch, &input_state_ground },
@@ -717,39 +701,6 @@ static const struct input_transition input_state_consume_st_table[] = {
{ -1, -1, NULL, NULL } { -1, -1, NULL, NULL }
}; };
/* utf8_three state table. */
static const struct input_transition input_state_utf8_three_table[] = {
/* No INPUT_STATE_ANYWHERE */
{ 0x00, 0x7f, NULL, &input_state_ground },
{ 0x80, 0xbf, input_utf8_add, &input_state_utf8_two },
{ 0xc0, 0xff, NULL, &input_state_ground },
{ -1, -1, NULL, NULL }
};
/* utf8_two state table. */
static const struct input_transition input_state_utf8_two_table[] = {
/* No INPUT_STATE_ANYWHERE */
{ 0x00, 0x7f, NULL, &input_state_ground },
{ 0x80, 0xbf, input_utf8_add, &input_state_utf8_one },
{ 0xc0, 0xff, NULL, &input_state_ground },
{ -1, -1, NULL, NULL }
};
/* utf8_one state table. */
static const struct input_transition input_state_utf8_one_table[] = {
/* No INPUT_STATE_ANYWHERE */
{ 0x00, 0x7f, NULL, &input_state_ground },
{ 0x80, 0xbf, input_utf8_close, &input_state_ground },
{ 0xc0, 0xff, NULL, &input_state_ground },
{ -1, -1, NULL, NULL }
};
/* Input table compare. */ /* Input table compare. */
static int static int
input_table_compare(const void *key, const void *value) input_table_compare(const void *key, const void *value)
@@ -822,6 +773,12 @@ void
input_free(struct window_pane *wp) input_free(struct window_pane *wp)
{ {
struct input_ctx *ictx = wp->ictx; struct input_ctx *ictx = wp->ictx;
u_int i;
for (i = 0; i < ictx->param_list_len; i++) {
if (ictx->param_list[i].type == INPUT_STRING)
free(ictx->param_list[i].str);
}
event_del(&ictx->timer); event_del(&ictx->timer);
@@ -964,29 +921,51 @@ input_parse(struct window_pane *wp)
static int static int
input_split(struct input_ctx *ictx) input_split(struct input_ctx *ictx)
{ {
const char *errstr; const char *errstr;
char *ptr, *out; char *ptr, *out;
int n; struct input_param *ip;
u_int i;
for (i = 0; i < ictx->param_list_len; i++) {
if (ictx->param_list[i].type == INPUT_STRING)
free(ictx->param_list[i].str);
}
ictx->param_list_len = 0; ictx->param_list_len = 0;
if (ictx->param_len == 0) if (ictx->param_len == 0)
return (0); return (0);
ip = &ictx->param_list[0];
ptr = ictx->param_buf; ptr = ictx->param_buf;
while ((out = strsep(&ptr, ";")) != NULL) { while ((out = strsep(&ptr, ";")) != NULL) {
if (*out == '\0') if (*out == '\0')
n = -1; ip->type = INPUT_MISSING;
else { else {
n = strtonum(out, 0, INT_MAX, &errstr); if (strchr(out, ':') != NULL) {
if (errstr != NULL) ip->type = INPUT_STRING;
return (-1); ip->str = xstrdup(out);
} else {
ip->type = INPUT_NUMBER;
ip->num = strtonum(out, 0, INT_MAX, &errstr);
if (errstr != NULL)
return (-1);
}
} }
ip = &ictx->param_list[++ictx->param_list_len];
ictx->param_list[ictx->param_list_len++] = n;
if (ictx->param_list_len == nitems(ictx->param_list)) if (ictx->param_list_len == nitems(ictx->param_list))
return (-1); return (-1);
} }
for (i = 0; i < ictx->param_list_len; i++) {
ip = &ictx->param_list[i];
if (ip->type == INPUT_MISSING)
log_debug("parameter %u: missing", i);
else if (ip->type == INPUT_STRING)
log_debug("parameter %u: string %s", i, ip->str);
else if (ip->type == INPUT_NUMBER)
log_debug("parameter %u: number %d", i, ip->num);
}
return (0); return (0);
} }
@@ -994,14 +973,17 @@ input_split(struct input_ctx *ictx)
static int static int
input_get(struct input_ctx *ictx, u_int validx, int minval, int defval) input_get(struct input_ctx *ictx, u_int validx, int minval, int defval)
{ {
int retval; struct input_param *ip;
int retval;
if (validx >= ictx->param_list_len) if (validx >= ictx->param_list_len)
return (defval); return (defval);
ip = &ictx->param_list[validx];
retval = ictx->param_list[validx]; if (ip->type == INPUT_MISSING)
if (retval == -1)
return (defval); return (defval);
if (ip->type == INPUT_STRING)
return (-1);
retval = ip->num;
if (retval < minval) if (retval < minval)
return (minval); return (minval);
return (retval); return (retval);
@@ -1059,6 +1041,8 @@ input_print(struct input_ctx *ictx)
{ {
int set; int set;
ictx->utf8started = 0; /* can't be valid UTF-8 */
set = ictx->cell.set == 0 ? ictx->cell.g0set : ictx->cell.g1set; set = ictx->cell.set == 0 ? ictx->cell.g0set : ictx->cell.g1set;
if (set == 1) if (set == 1)
ictx->cell.cell.attr |= GRID_ATTR_CHARSET; ictx->cell.cell.attr |= GRID_ATTR_CHARSET;
@@ -1132,6 +1116,8 @@ input_c0_dispatch(struct input_ctx *ictx)
struct window_pane *wp = ictx->wp; struct window_pane *wp = ictx->wp;
struct screen *s = sctx->s; struct screen *s = sctx->s;
ictx->utf8started = 0; /* can't be valid UTF-8 */
log_debug("%s: '%c'", __func__, ictx->ch); log_debug("%s: '%c'", __func__, ictx->ch);
switch (ictx->ch) { switch (ictx->ch) {
@@ -1264,7 +1250,7 @@ input_csi_dispatch(struct input_ctx *ictx)
struct screen *s = sctx->s; struct screen *s = sctx->s;
struct input_table_entry *entry; struct input_table_entry *entry;
int i, n, m; int i, n, m;
u_int cx; u_int cx, bg = ictx->cell.cell.bg;
if (ictx->flags & INPUT_DISCARD) if (ictx->flags & INPUT_DISCARD)
return (0); return (0);
@@ -1289,6 +1275,8 @@ input_csi_dispatch(struct input_ctx *ictx)
if (cx > screen_size_x(s) - 1) if (cx > screen_size_x(s) - 1)
cx = screen_size_x(s) - 1; cx = screen_size_x(s) - 1;
n = input_get(ictx, 0, 1, 1); n = input_get(ictx, 0, 1, 1);
if (n == -1)
break;
while (cx > 0 && n-- > 0) { while (cx > 0 && n-- > 0) {
do do
cx--; cx--;
@@ -1297,35 +1285,52 @@ input_csi_dispatch(struct input_ctx *ictx)
s->cx = cx; s->cx = cx;
break; break;
case INPUT_CSI_CUB: case INPUT_CSI_CUB:
screen_write_cursorleft(sctx, input_get(ictx, 0, 1, 1)); n = input_get(ictx, 0, 1, 1);
if (n != -1)
screen_write_cursorleft(sctx, n);
break; break;
case INPUT_CSI_CUD: case INPUT_CSI_CUD:
screen_write_cursordown(sctx, input_get(ictx, 0, 1, 1)); n = input_get(ictx, 0, 1, 1);
if (n != -1)
screen_write_cursordown(sctx, n);
break; break;
case INPUT_CSI_CUF: case INPUT_CSI_CUF:
screen_write_cursorright(sctx, input_get(ictx, 0, 1, 1)); n = input_get(ictx, 0, 1, 1);
if (n != -1)
screen_write_cursorright(sctx, n);
break; break;
case INPUT_CSI_CUP: case INPUT_CSI_CUP:
n = input_get(ictx, 0, 1, 1); n = input_get(ictx, 0, 1, 1);
m = input_get(ictx, 1, 1, 1); m = input_get(ictx, 1, 1, 1);
screen_write_cursormove(sctx, m - 1, n - 1); if (n != -1 && m != -1)
screen_write_cursormove(sctx, m - 1, n - 1);
break; break;
case INPUT_CSI_WINOPS: case INPUT_CSI_WINOPS:
input_csi_dispatch_winops(ictx); input_csi_dispatch_winops(ictx);
break; break;
case INPUT_CSI_CUU: case INPUT_CSI_CUU:
screen_write_cursorup(sctx, input_get(ictx, 0, 1, 1)); n = input_get(ictx, 0, 1, 1);
if (n != -1)
screen_write_cursorup(sctx, n);
break; break;
case INPUT_CSI_CNL: case INPUT_CSI_CNL:
screen_write_carriagereturn(sctx); n = input_get(ictx, 0, 1, 1);
screen_write_cursordown(sctx, input_get(ictx, 0, 1, 1)); if (n != -1) {
screen_write_carriagereturn(sctx);
screen_write_cursordown(sctx, n);
}
break; break;
case INPUT_CSI_CPL: case INPUT_CSI_CPL:
screen_write_carriagereturn(sctx); n = input_get(ictx, 0, 1, 1);
screen_write_cursorup(sctx, input_get(ictx, 0, 1, 1)); if (n != -1) {
screen_write_carriagereturn(sctx);
screen_write_cursorup(sctx, n);
}
break; break;
case INPUT_CSI_DA: case INPUT_CSI_DA:
switch (input_get(ictx, 0, 0, 0)) { switch (input_get(ictx, 0, 0, 0)) {
case -1:
break;
case 0: case 0:
input_reply(ictx, "\033[?1;2c"); input_reply(ictx, "\033[?1;2c");
break; break;
@@ -1336,6 +1341,8 @@ input_csi_dispatch(struct input_ctx *ictx)
break; break;
case INPUT_CSI_DA_TWO: case INPUT_CSI_DA_TWO:
switch (input_get(ictx, 0, 0, 0)) { switch (input_get(ictx, 0, 0, 0)) {
case -1:
break;
case 0: case 0:
input_reply(ictx, "\033[>84;0;0c"); input_reply(ictx, "\033[>84;0;0c");
break; break;
@@ -1345,24 +1352,30 @@ input_csi_dispatch(struct input_ctx *ictx)
} }
break; break;
case INPUT_CSI_ECH: case INPUT_CSI_ECH:
screen_write_clearcharacter(sctx, input_get(ictx, 0, 1, 1), n = input_get(ictx, 0, 1, 1);
ictx->cell.cell.bg); if (n != -1)
screen_write_clearcharacter(sctx, n, bg);
break; break;
case INPUT_CSI_DCH: case INPUT_CSI_DCH:
screen_write_deletecharacter(sctx, input_get(ictx, 0, 1, 1), n = input_get(ictx, 0, 1, 1);
ictx->cell.cell.bg); if (n != -1)
screen_write_deletecharacter(sctx, n, bg);
break; break;
case INPUT_CSI_DECSTBM: case INPUT_CSI_DECSTBM:
n = input_get(ictx, 0, 1, 1); n = input_get(ictx, 0, 1, 1);
m = input_get(ictx, 1, 1, screen_size_y(s)); m = input_get(ictx, 1, 1, screen_size_y(s));
screen_write_scrollregion(sctx, n - 1, m - 1); if (n != -1 && m != -1)
screen_write_scrollregion(sctx, n - 1, m - 1);
break; break;
case INPUT_CSI_DL: case INPUT_CSI_DL:
screen_write_deleteline(sctx, input_get(ictx, 0, 1, 1), n = input_get(ictx, 0, 1, 1);
ictx->cell.cell.bg); if (n != -1)
screen_write_deleteline(sctx, n, bg);
break; break;
case INPUT_CSI_DSR: case INPUT_CSI_DSR:
switch (input_get(ictx, 0, 0, 0)) { switch (input_get(ictx, 0, 0, 0)) {
case -1:
break;
case 5: case 5:
input_reply(ictx, "\033[0n"); input_reply(ictx, "\033[0n");
break; break;
@@ -1376,24 +1389,24 @@ input_csi_dispatch(struct input_ctx *ictx)
break; break;
case INPUT_CSI_ED: case INPUT_CSI_ED:
switch (input_get(ictx, 0, 0, 0)) { switch (input_get(ictx, 0, 0, 0)) {
case -1:
break;
case 0: case 0:
screen_write_clearendofscreen(sctx, ictx->cell.cell.bg); screen_write_clearendofscreen(sctx, bg);
break; break;
case 1: case 1:
screen_write_clearstartofscreen(sctx, ictx->cell.cell.bg); screen_write_clearstartofscreen(sctx, bg);
break; break;
case 2: case 2:
screen_write_clearscreen(sctx, ictx->cell.cell.bg); screen_write_clearscreen(sctx, bg);
break; break;
case 3: case 3:
switch (input_get(ictx, 1, 0, 0)) { if (input_get(ictx, 1, 0, 0) == 0) {
case 0:
/* /*
* Linux console extension to clear history * Linux console extension to clear history
* (for example before locking the screen). * (for example before locking the screen).
*/ */
screen_write_clearhistory(sctx); screen_write_clearhistory(sctx);
break;
} }
break; break;
default: default:
@@ -1403,14 +1416,16 @@ input_csi_dispatch(struct input_ctx *ictx)
break; break;
case INPUT_CSI_EL: case INPUT_CSI_EL:
switch (input_get(ictx, 0, 0, 0)) { switch (input_get(ictx, 0, 0, 0)) {
case -1:
break;
case 0: case 0:
screen_write_clearendofline(sctx, ictx->cell.cell.bg); screen_write_clearendofline(sctx, bg);
break; break;
case 1: case 1:
screen_write_clearstartofline(sctx, ictx->cell.cell.bg); screen_write_clearstartofline(sctx, bg);
break; break;
case 2: case 2:
screen_write_clearline(sctx, ictx->cell.cell.bg); screen_write_clearline(sctx, bg);
break; break;
default: default:
log_debug("%s: unknown '%c'", __func__, ictx->ch); log_debug("%s: unknown '%c'", __func__, ictx->ch);
@@ -1419,22 +1434,28 @@ input_csi_dispatch(struct input_ctx *ictx)
break; break;
case INPUT_CSI_HPA: case INPUT_CSI_HPA:
n = input_get(ictx, 0, 1, 1); n = input_get(ictx, 0, 1, 1);
screen_write_cursormove(sctx, n - 1, s->cy); if (n != -1)
screen_write_cursormove(sctx, n - 1, s->cy);
break; break;
case INPUT_CSI_ICH: case INPUT_CSI_ICH:
screen_write_insertcharacter(sctx, input_get(ictx, 0, 1, 1), n = input_get(ictx, 0, 1, 1);
ictx->cell.cell.bg); if (n != -1)
screen_write_insertcharacter(sctx, n, bg);
break; break;
case INPUT_CSI_IL: case INPUT_CSI_IL:
screen_write_insertline(sctx, input_get(ictx, 0, 1, 1), n = input_get(ictx, 0, 1, 1);
ictx->cell.cell.bg); if (n != -1)
screen_write_insertline(sctx, n, bg);
break; break;
case INPUT_CSI_REP: case INPUT_CSI_REP:
n = input_get(ictx, 0, 1, 1);
if (n == -1)
break;
if (ictx->last == -1) if (ictx->last == -1)
break; break;
ictx->ch = ictx->last; ictx->ch = ictx->last;
n = input_get(ictx, 0, 1, 1);
for (i = 0; i < n; i++) for (i = 0; i < n; i++)
input_print(ictx); input_print(ictx);
break; break;
@@ -1463,11 +1484,14 @@ input_csi_dispatch(struct input_ctx *ictx)
input_csi_dispatch_sm_private(ictx); input_csi_dispatch_sm_private(ictx);
break; break;
case INPUT_CSI_SU: case INPUT_CSI_SU:
screen_write_scrollup(sctx, input_get(ictx, 0, 1, 1), n = input_get(ictx, 0, 1, 1);
ictx->cell.cell.bg); if (n != -1)
screen_write_scrollup(sctx, n, bg);
break; break;
case INPUT_CSI_TBC: case INPUT_CSI_TBC:
switch (input_get(ictx, 0, 0, 0)) { switch (input_get(ictx, 0, 0, 0)) {
case -1:
break;
case 0: case 0:
if (s->cx < screen_size_x(s)) if (s->cx < screen_size_x(s))
bit_clear(s->tabs, s->cx); bit_clear(s->tabs, s->cx);
@@ -1482,11 +1506,13 @@ input_csi_dispatch(struct input_ctx *ictx)
break; break;
case INPUT_CSI_VPA: case INPUT_CSI_VPA:
n = input_get(ictx, 0, 1, 1); n = input_get(ictx, 0, 1, 1);
screen_write_cursormove(sctx, s->cx, n - 1); if (n != -1)
screen_write_cursormove(sctx, s->cx, n - 1);
break; break;
case INPUT_CSI_DECSCUSR: case INPUT_CSI_DECSCUSR:
n = input_get(ictx, 0, 0, 0); n = input_get(ictx, 0, 0, 0);
screen_set_cursor_style(s, n); if (n != -1)
screen_set_cursor_style(s, n);
break; break;
} }
@@ -1502,6 +1528,8 @@ input_csi_dispatch_rm(struct input_ctx *ictx)
for (i = 0; i < ictx->param_list_len; i++) { for (i = 0; i < ictx->param_list_len; i++) {
switch (input_get(ictx, i, 0, -1)) { switch (input_get(ictx, i, 0, -1)) {
case -1:
break;
case 4: /* IRM */ case 4: /* IRM */
screen_write_mode_clear(&ictx->ctx, MODE_INSERT); screen_write_mode_clear(&ictx->ctx, MODE_INSERT);
break; break;
@@ -1524,6 +1552,8 @@ input_csi_dispatch_rm_private(struct input_ctx *ictx)
for (i = 0; i < ictx->param_list_len; i++) { for (i = 0; i < ictx->param_list_len; i++) {
switch (input_get(ictx, i, 0, -1)) { switch (input_get(ictx, i, 0, -1)) {
case -1:
break;
case 1: /* DECCKM */ case 1: /* DECCKM */
screen_write_mode_clear(&ictx->ctx, MODE_KCURSOR); screen_write_mode_clear(&ictx->ctx, MODE_KCURSOR);
break; break;
@@ -1581,6 +1611,8 @@ input_csi_dispatch_sm(struct input_ctx *ictx)
for (i = 0; i < ictx->param_list_len; i++) { for (i = 0; i < ictx->param_list_len; i++) {
switch (input_get(ictx, i, 0, -1)) { switch (input_get(ictx, i, 0, -1)) {
case -1:
break;
case 4: /* IRM */ case 4: /* IRM */
screen_write_mode_set(&ictx->ctx, MODE_INSERT); screen_write_mode_set(&ictx->ctx, MODE_INSERT);
break; break;
@@ -1603,6 +1635,8 @@ input_csi_dispatch_sm_private(struct input_ctx *ictx)
for (i = 0; i < ictx->param_list_len; i++) { for (i = 0; i < ictx->param_list_len; i++) {
switch (input_get(ictx, i, 0, -1)) { switch (input_get(ictx, i, 0, -1)) {
case -1:
break;
case 1: /* DECCKM */ case 1: /* DECCKM */
screen_write_mode_set(&ictx->ctx, MODE_KCURSOR); screen_write_mode_set(&ictx->ctx, MODE_KCURSOR);
break; break;
@@ -1693,12 +1727,33 @@ input_csi_dispatch_winops(struct input_ctx *ictx)
/* FALLTHROUGH */ /* FALLTHROUGH */
case 9: case 9:
case 10: case 10:
case 22:
case 23:
m++; m++;
if (input_get(ictx, m, 0, -1) == -1) if (input_get(ictx, m, 0, -1) == -1)
return; return;
break; break;
case 22:
m++;
switch (input_get(ictx, m, 0, -1)) {
case -1:
return;
case 0:
case 2:
screen_push_title(ictx->ctx.s);
break;
}
break;
case 23:
m++;
switch (input_get(ictx, m, 0, -1)) {
case -1:
return;
case 0:
case 2:
screen_pop_title(ictx->ctx.s);
server_status_window(ictx->wp->window);
break;
}
break;
case 18: case 18:
input_reply(ictx, "\033[8;%u;%ut", wp->sy, wp->sx); input_reply(ictx, "\033[8;%u;%ut", wp->sy, wp->sx);
break; break;
@@ -1710,16 +1765,13 @@ input_csi_dispatch_winops(struct input_ctx *ictx)
} }
} }
/* Handle CSI SGR for 256 colours. */ /* Helper for 256 colour SGR. */
static void static int
input_csi_dispatch_sgr_256(struct input_ctx *ictx, int fgbg, u_int *i) input_csi_dispatch_sgr_256_do(struct input_ctx *ictx, int fgbg, int c)
{ {
struct grid_cell *gc = &ictx->cell.cell; struct grid_cell *gc = &ictx->cell.cell;
int c;
(*i)++; if (c == -1 || c > 255) {
c = input_get(ictx, *i, 0, -1);
if (c == -1) {
if (fgbg == 38) if (fgbg == 38)
gc->fg = 8; gc->fg = 8;
else if (fgbg == 48) else if (fgbg == 48)
@@ -1730,32 +1782,92 @@ input_csi_dispatch_sgr_256(struct input_ctx *ictx, int fgbg, u_int *i)
else if (fgbg == 48) else if (fgbg == 48)
gc->bg = c | COLOUR_FLAG_256; gc->bg = c | COLOUR_FLAG_256;
} }
return (1);
}
/* Handle CSI SGR for 256 colours. */
static void
input_csi_dispatch_sgr_256(struct input_ctx *ictx, int fgbg, u_int *i)
{
int c;
c = input_get(ictx, (*i) + 1, 0, -1);
if (input_csi_dispatch_sgr_256_do(ictx, fgbg, c))
(*i)++;
}
/* Helper for RGB colour SGR. */
static int
input_csi_dispatch_sgr_rgb_do(struct input_ctx *ictx, int fgbg, int r, int g,
int b)
{
struct grid_cell *gc = &ictx->cell.cell;
if (r == -1 || r > 255)
return (0);
if (g == -1 || g > 255)
return (0);
if (b == -1 || b > 255)
return (0);
if (fgbg == 38)
gc->fg = colour_join_rgb(r, g, b);
else if (fgbg == 48)
gc->bg = colour_join_rgb(r, g, b);
return (1);
} }
/* Handle CSI SGR for RGB colours. */ /* Handle CSI SGR for RGB colours. */
static void static void
input_csi_dispatch_sgr_rgb(struct input_ctx *ictx, int fgbg, u_int *i) input_csi_dispatch_sgr_rgb(struct input_ctx *ictx, int fgbg, u_int *i)
{ {
struct grid_cell *gc = &ictx->cell.cell; int r, g, b;
int r, g, b;
(*i)++; r = input_get(ictx, (*i) + 1, 0, -1);
r = input_get(ictx, *i, 0, -1); g = input_get(ictx, (*i) + 2, 0, -1);
if (r == -1 || r > 255) b = input_get(ictx, (*i) + 3, 0, -1);
return; if (input_csi_dispatch_sgr_rgb_do(ictx, fgbg, r, g, b))
(*i)++; (*i) += 3;
g = input_get(ictx, *i, 0, -1); }
if (g == -1 || g > 255)
return;
(*i)++;
b = input_get(ictx, *i, 0, -1);
if (b == -1 || b > 255)
return;
if (fgbg == 38) /* Handle CSI SGR with a ISO parameter. */
gc->fg = colour_join_rgb(r, g, b); static void
else if (fgbg == 48) input_csi_dispatch_sgr_colon(struct input_ctx *ictx, u_int i)
gc->bg = colour_join_rgb(r, g, b); {
char *s = ictx->param_list[i].str, *copy, *ptr, *out;
int p[8];
u_int n;
const char *errstr;
for (n = 0; n < nitems(p); n++)
p[n] = -1;
n = 0;
ptr = copy = xstrdup(s);
while ((out = strsep(&ptr, ":")) != NULL) {
p[n++] = strtonum(out, 0, INT_MAX, &errstr);
if (errstr != NULL || n == nitems(p)) {
free(copy);
return;
}
log_debug("%s: %u = %d", __func__, n - 1, p[n - 1]);
}
free(copy);
if (n == 0 || (p[0] != 38 && p[0] != 48))
return;
switch (p[1]) {
case 2:
if (n != 5)
break;
input_csi_dispatch_sgr_rgb_do(ictx, p[0], p[2], p[3], p[4]);
break;
case 5:
if (n != 3)
break;
input_csi_dispatch_sgr_256_do(ictx, p[0], p[2]);
break;
}
} }
/* Handle CSI SGR. */ /* Handle CSI SGR. */
@@ -1772,7 +1884,13 @@ input_csi_dispatch_sgr(struct input_ctx *ictx)
} }
for (i = 0; i < ictx->param_list_len; i++) { for (i = 0; i < ictx->param_list_len; i++) {
if (ictx->param_list[i].type == INPUT_STRING) {
input_csi_dispatch_sgr_colon(ictx, i);
continue;
}
n = input_get(ictx, i, 0, 0); n = input_get(ictx, i, 0, 0);
if (n == -1)
continue;
if (n == 38 || n == 48) { if (n == 38 || n == 48) {
i++; i++;
@@ -1789,7 +1907,6 @@ input_csi_dispatch_sgr(struct input_ctx *ictx)
switch (n) { switch (n) {
case 0: case 0:
case 10:
memcpy(gc, &grid_default_cell, sizeof *gc); memcpy(gc, &grid_default_cell, sizeof *gc);
break; break;
case 1: case 1:
@@ -2043,48 +2160,30 @@ input_exit_rename(struct input_ctx *ictx)
/* Open UTF-8 character. */ /* Open UTF-8 character. */
static int static int
input_utf8_open(struct input_ctx *ictx) input_top_bit_set(struct input_ctx *ictx)
{ {
struct utf8_data *ud = &ictx->utf8data; struct utf8_data *ud = &ictx->utf8data;
if (utf8_open(ud, ictx->ch) != UTF8_MORE)
fatalx("UTF-8 open invalid %#x", ictx->ch);
log_debug("%s %hhu", __func__, ud->size);
ictx->last = -1; ictx->last = -1;
return (0); if (!ictx->utf8started) {
} if (utf8_open(ud, ictx->ch) != UTF8_MORE)
return (0);
/* Append to UTF-8 character. */ ictx->utf8started = 1;
static int
input_utf8_add(struct input_ctx *ictx)
{
struct utf8_data *ud = &ictx->utf8data;
if (utf8_append(ud, ictx->ch) != UTF8_MORE)
fatalx("UTF-8 add invalid %#x", ictx->ch);
log_debug("%s", __func__);
return (0);
}
/* Close UTF-8 string. */
static int
input_utf8_close(struct input_ctx *ictx)
{
struct utf8_data *ud = &ictx->utf8data;
if (utf8_append(ud, ictx->ch) != UTF8_DONE) {
/*
* An error here could be invalid UTF-8 or it could be a
* nonprintable character for which we can't get the
* width. Drop it.
*/
return (0); return (0);
} }
switch (utf8_append(ud, ictx->ch)) {
case UTF8_MORE:
return (0);
case UTF8_ERROR:
ictx->utf8started = 0;
return (0);
case UTF8_DONE:
break;
}
ictx->utf8started = 0;
log_debug("%s %hhu '%*s' (width %hhu)", __func__, ud->size, log_debug("%s %hhu '%*s' (width %hhu)", __func__, ud->size,
(int)ud->size, ud->data, ud->width); (int)ud->size, ud->data, ud->width);

3
job.c
View File

@@ -43,7 +43,7 @@ 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;
@@ -110,6 +110,7 @@ job_run(const char *cmd, struct session *s, const char *cwd,
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;

View File

@@ -162,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",
@@ -183,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",
@@ -201,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 -s", "bind s choose-tree -Zs",
"bind t clock-mode", "bind t clock-mode",
"bind w choose-tree -w", "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",

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);
@@ -169,59 +156,16 @@ layout_set_even_h(struct window *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);
notify_window("window-layout-changed", w);
server_redraw_window(w);
} }
static void static void

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

View File

@@ -31,6 +31,7 @@ TAILQ_HEAD(mode_tree_list, mode_tree_item);
struct mode_tree_data { struct mode_tree_data {
int dead; int dead;
u_int references; u_int references;
int zoomed;
struct window_pane *wp; struct window_pane *wp;
void *modedata; void *modedata;
@@ -39,10 +40,9 @@ struct mode_tree_data {
u_int sort_size; u_int sort_size;
u_int sort_type; u_int sort_type;
void (*buildcb)(void *, u_int, uint64_t *, mode_tree_build_cb buildcb;
const char *); mode_tree_draw_cb drawcb;
struct screen *(*drawcb)(void *, void *, u_int, u_int); mode_tree_search_cb searchcb;
int (*searchcb)(void*, void *, const char *);
struct mode_tree_list children; struct mode_tree_list children;
struct mode_tree_list saved; struct mode_tree_list saved;
@@ -63,6 +63,7 @@ struct mode_tree_data {
int preview; int preview;
char *search; char *search;
char *filter; char *filter;
int no_matches;
}; };
struct mode_tree_item { struct mode_tree_item {
@@ -191,27 +192,6 @@ mode_tree_clear_tagged(struct mode_tree_list *mtl)
} }
} }
static 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;
}
}
void void
mode_tree_up(struct mode_tree_data *mtd, int wrap) mode_tree_up(struct mode_tree_data *mtd, int wrap)
{ {
@@ -249,6 +229,36 @@ mode_tree_get_current(struct mode_tree_data *mtd)
return (mtd->line_list[mtd->current].item->itemdata); 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 u_int
mode_tree_count_tagged(struct mode_tree_data *mtd) mode_tree_count_tagged(struct mode_tree_data *mtd)
{ {
@@ -265,8 +275,8 @@ mode_tree_count_tagged(struct mode_tree_data *mtd)
} }
void void
mode_tree_each_tagged(struct mode_tree_data *mtd, void (*cb)(void *, void *, mode_tree_each_tagged(struct mode_tree_data *mtd, mode_tree_each_cb cb,
key_code), key_code key, int current) struct client *c, key_code key, int current)
{ {
struct mode_tree_item *mti; struct mode_tree_item *mti;
u_int i; u_int i;
@@ -277,21 +287,20 @@ mode_tree_each_tagged(struct mode_tree_data *mtd, void (*cb)(void *, void *,
mti = mtd->line_list[i].item; mti = mtd->line_list[i].item;
if (mti->tagged) { if (mti->tagged) {
fired = 1; fired = 1;
cb(mtd->modedata, mti->itemdata, key); cb(mtd->modedata, mti->itemdata, c, key);
} }
} }
if (!fired && current) { if (!fired && current) {
mti = mtd->line_list[mtd->current].item; mti = mtd->line_list[mtd->current].item;
cb(mtd->modedata, mti->itemdata, key); cb(mtd->modedata, mti->itemdata, c, key);
} }
} }
struct mode_tree_data * struct mode_tree_data *
mode_tree_start(struct window_pane *wp, struct args *args, mode_tree_start(struct window_pane *wp, struct args *args,
void (*buildcb)(void *, u_int, uint64_t *, const char *), mode_tree_build_cb buildcb, mode_tree_draw_cb drawcb,
struct screen *(*drawcb)(void *, void *, u_int, u_int), mode_tree_search_cb searchcb, void *modedata, const char **sort_list,
int (*searchcb)(void *, void *, const char *), void *modedata, u_int sort_size, struct screen **s)
const char **sort_list, u_int sort_size, struct screen **s)
{ {
struct mode_tree_data *mtd; struct mode_tree_data *mtd;
const char *sort; const char *sort;
@@ -335,6 +344,19 @@ mode_tree_start(struct window_pane *wp, struct args *args,
return (mtd); 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 void
mode_tree_build(struct mode_tree_data *mtd) mode_tree_build(struct mode_tree_data *mtd)
{ {
@@ -350,7 +372,8 @@ mode_tree_build(struct mode_tree_data *mtd)
TAILQ_INIT(&mtd->children); TAILQ_INIT(&mtd->children);
mtd->buildcb(mtd->modedata, mtd->sort_type, &tag, mtd->filter); mtd->buildcb(mtd->modedata, mtd->sort_type, &tag, mtd->filter);
if (TAILQ_EMPTY(&mtd->children)) mtd->no_matches = TAILQ_EMPTY(&mtd->children);
if (mtd->no_matches)
mtd->buildcb(mtd->modedata, mtd->sort_type, &tag, NULL); mtd->buildcb(mtd->modedata, mtd->sort_type, &tag, NULL);
mode_tree_free_items(&mtd->saved); mode_tree_free_items(&mtd->saved);
@@ -385,6 +408,11 @@ mode_tree_remove_ref(struct mode_tree_data *mtd)
void void
mode_tree_free(struct mode_tree_data *mtd) 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_free_items(&mtd->children);
mode_tree_clear_lines(mtd); mode_tree_clear_lines(mtd);
screen_free(&mtd->screen); screen_free(&mtd->screen);
@@ -463,7 +491,7 @@ void
mode_tree_draw(struct mode_tree_data *mtd) mode_tree_draw(struct mode_tree_data *mtd)
{ {
struct window_pane *wp = mtd->wp; struct window_pane *wp = mtd->wp;
struct screen *s = &mtd->screen, *box = NULL; struct screen *s = &mtd->screen;
struct mode_tree_line *line; struct mode_tree_line *line;
struct mode_tree_item *mti; struct mode_tree_item *mti;
struct options *oo = wp->window->options; struct options *oo = wp->window->options;
@@ -472,7 +500,7 @@ mode_tree_draw(struct mode_tree_data *mtd)
u_int w, h, i, j, sy, box_x, box_y; u_int w, h, i, j, sy, box_x, box_y;
char *text, *start, key[7]; char *text, *start, key[7];
const char *tag, *symbol; const char *tag, *symbol;
size_t size; size_t size, n;
int keylen; int keylen;
if (mtd->line_size == 0) if (mtd->line_size == 0)
@@ -554,10 +582,12 @@ mode_tree_draw(struct mode_tree_data *mtd)
} }
if (i != mtd->current) { if (i != mtd->current) {
screen_write_puts(&ctx, &gc0, "%.*s", w, text); screen_write_nputs(&ctx, w, &gc0, "%s", text);
screen_write_clearendofline(&ctx, 8); screen_write_clearendofline(&ctx, 8);
} else } else {
screen_write_puts(&ctx, &gc, "%-*.*s", w, w, text); screen_write_nputs(&ctx, w, &gc, "%s", text);
screen_write_clearendofline(&ctx, gc.bg);
}
free(text); free(text);
if (mti->tagged) { if (mti->tagged) {
@@ -578,24 +608,33 @@ mode_tree_draw(struct mode_tree_data *mtd)
screen_write_cursormove(&ctx, 0, h); screen_write_cursormove(&ctx, 0, h);
screen_write_box(&ctx, w, sy - h); screen_write_box(&ctx, w, sy - h);
xasprintf(&text, " %s (sort: %s) ", mti->name, xasprintf(&text, " %s (sort: %s)", mti->name,
mtd->sort_list[mtd->sort_type]); mtd->sort_list[mtd->sort_type]);
if (w - 2 >= strlen(text)) { if (w - 2 >= strlen(text)) {
screen_write_cursormove(&ctx, 1, h); screen_write_cursormove(&ctx, 1, h);
screen_write_puts(&ctx, &gc0, "%s", text); 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); free(text);
box_x = w - 4; box_x = w - 4;
box_y = sy - h - 2; box_y = sy - h - 2;
if (box_x != 0 && box_y != 0) if (box_x != 0 && box_y != 0) {
box = mtd->drawcb(mtd->modedata, mti->itemdata, box_x, box_y);
if (box != NULL) {
screen_write_cursormove(&ctx, 2, h + 1); screen_write_cursormove(&ctx, 2, h + 1);
screen_write_copy(&ctx, box, 0, 0, box_x, box_y, NULL, NULL); mtd->drawcb(mtd->modedata, mti->itemdata, &ctx, box_x, box_y);
screen_free(box);
} }
screen_write_stop(&ctx); screen_write_stop(&ctx);
@@ -722,7 +761,7 @@ mode_tree_filter_free(void *data)
int int
mode_tree_key(struct mode_tree_data *mtd, struct client *c, key_code *key, mode_tree_key(struct mode_tree_data *mtd, struct client *c, key_code *key,
struct mouse_event *m) struct mouse_event *m, u_int *xp, u_int *yp)
{ {
struct mode_tree_line *line; struct mode_tree_line *line;
struct mode_tree_item *current, *parent; struct mode_tree_item *current, *parent;
@@ -730,20 +769,31 @@ mode_tree_key(struct mode_tree_data *mtd, struct client *c, key_code *key,
int choice; int choice;
key_code tmp; key_code tmp;
if (*key == KEYC_MOUSEDOWN1_PANE) { if (KEYC_IS_MOUSE(*key)) {
if (cmd_mouse_at(mtd->wp, m, &x, &y, 0) != 0) { if (cmd_mouse_at(mtd->wp, m, &x, &y, 0) != 0) {
*key = KEYC_NONE; *key = KEYC_NONE;
return (0); return (0);
} }
if (xp != NULL)
*xp = x;
if (yp != NULL)
*yp = y;
if (x > mtd->width || y > mtd->height) { if (x > mtd->width || y > mtd->height) {
*key = KEYC_NONE; if (!mtd->preview)
*key = KEYC_NONE;
return (0); return (0);
} }
if (mtd->offset + y < mtd->line_size) { if (mtd->offset + y < mtd->line_size) {
mtd->current = mtd->offset + y; if (*key == KEYC_MOUSEDOWN1_PANE ||
*key = '\r'; *key == KEYC_DOUBLECLICK1_PANE)
return (0); 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]; line = &mtd->line_list[mtd->current];
@@ -770,15 +820,18 @@ mode_tree_key(struct mode_tree_data *mtd, struct client *c, key_code *key,
switch (*key) { switch (*key) {
case 'q': case 'q':
case '\033': /* Escape */ case '\033': /* Escape */
case '\007': /* C-g */
return (1); return (1);
case KEYC_UP: case KEYC_UP:
case 'k': case 'k':
case KEYC_WHEELUP_PANE: case KEYC_WHEELUP_PANE:
case '\020': /* C-p */
mode_tree_up(mtd, 1); mode_tree_up(mtd, 1);
break; break;
case KEYC_DOWN: case KEYC_DOWN:
case 'j': case 'j':
case KEYC_WHEELDOWN_PANE: case KEYC_WHEELDOWN_PANE:
case '\016': /* C-n */
mode_tree_down(mtd, 1); mode_tree_down(mtd, 1);
break; break;
case KEYC_PPAGE: case KEYC_PPAGE:
@@ -844,6 +897,7 @@ mode_tree_key(struct mode_tree_data *mtd, struct client *c, key_code *key,
mode_tree_build(mtd); mode_tree_build(mtd);
break; break;
case KEYC_LEFT: case KEYC_LEFT:
case 'h':
case '-': case '-':
if (line->flat || !current->expanded) if (line->flat || !current->expanded)
current = current->parent; current = current->parent;
@@ -856,6 +910,7 @@ mode_tree_key(struct mode_tree_data *mtd, struct client *c, key_code *key,
} }
break; break;
case KEYC_RIGHT: case KEYC_RIGHT:
case 'l':
case '+': case '+':
if (line->flat || current->expanded) if (line->flat || current->expanded)
mode_tree_down(mtd, 0); mode_tree_down(mtd, 0);

View File

@@ -96,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,
@@ -547,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",

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

View File

@@ -13,7 +13,7 @@ $TMUX kill-server 2>/dev/null
TMP=$(mktemp) TMP=$(mktemp)
OUT=$(mktemp) OUT=$(mktemp)
trap "rm -f $TMP $OUT" 0 1 15 #trap "rm -f $TMP $OUT" 0 1 15
$TMUX -f/dev/null new -d || exit 1 $TMUX -f/dev/null new -d || exit 1
sleep 1 sleep 1
@@ -23,7 +23,7 @@ refresh -C 100,50
ls -F':#{session_width} #{session_height}' ls -F':#{session_width} #{session_height}'
EOF EOF
grep ^: $TMP >$OUT grep ^: $TMP >$OUT
printf ":80 24\n:100 50\n"|cmp -s $OUT || exit 1 printf ":80 24\n:100 50\n"|cmp -s $OUT - || exit 1
$TMUX kill-server 2>/dev/null $TMUX kill-server 2>/dev/null
$TMUX -f/dev/null new -d || exit 1 $TMUX -f/dev/null new -d || exit 1
@@ -34,7 +34,7 @@ refresh -C 80,24
ls -F':#{session_width} #{session_height}' ls -F':#{session_width} #{session_height}'
EOF EOF
grep ^: $TMP >$OUT grep ^: $TMP >$OUT
printf ":80 24\n:80 24\n"|cmp -s $OUT || exit 1 printf ":80 24\n:80 24\n"|cmp -s $OUT - || exit 1
$TMUX kill-server 2>/dev/null $TMUX kill-server 2>/dev/null
cat <<EOF|$TMUX -C new -x 100 -y 50 >$TMP cat <<EOF|$TMUX -C new -x 100 -y 50 >$TMP
@@ -43,7 +43,7 @@ refresh -C 80,24
ls -F':#{session_width} #{session_height}' ls -F':#{session_width} #{session_height}'
EOF EOF
grep ^: $TMP >$OUT grep ^: $TMP >$OUT
printf ":100 50\n:80 24\n"|cmp -s $OUT || exit 1 printf ":100 50\n:80 24\n"|cmp -s $OUT - || exit 1
$TMUX kill-server 2>/dev/null $TMUX kill-server 2>/dev/null
exit 0 exit 0

View File

@@ -15,13 +15,13 @@ trap "rm -f $TMP" 0 1 15
$TMUX -f/dev/null new -d </dev/null || exit 1 $TMUX -f/dev/null new -d </dev/null || exit 1
sleep 1 sleep 1
$TMUX ls -F "#{session_width} #{session_height}" >$TMP $TMUX ls -F "#{session_width} #{session_height}" >$TMP
printf "80 24\n"|cmp -s $TMP || exit 1 printf "80 24\n"|cmp -s $TMP - || exit 1
$TMUX kill-server 2>/dev/null $TMUX kill-server 2>/dev/null
$TMUX -f/dev/null new -d -x 100 -y 50 </dev/null || exit 1 $TMUX -f/dev/null new -d -x 100 -y 50 </dev/null || exit 1
sleep 1 sleep 1
$TMUX ls -F "#{session_width} #{session_height}" >$TMP $TMUX ls -F "#{session_width} #{session_height}" >$TMP
printf "100 50\n"|cmp -s $TMP || exit 1 printf "100 50\n"|cmp -s $TMP - || exit 1
$TMUX kill-server 2>/dev/null $TMUX kill-server 2>/dev/null
exit 0 exit 0

View File

@@ -49,11 +49,11 @@ 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;
@@ -66,10 +66,14 @@ recalculate_sizes(void)
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++;
@@ -81,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;

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

@@ -351,11 +351,10 @@ 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;
@@ -371,22 +370,57 @@ screen_write_copy(struct screen_write_ctx *ctx, struct screen *src, u_int px,
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. */ /* Draw a horizontal line on screen. */
void void
screen_write_hline(struct screen_write_ctx *ctx, u_int nx, int left, int right) screen_write_hline(struct screen_write_ctx *ctx, u_int nx, int left, int right)
@@ -428,7 +462,7 @@ screen_write_vline(struct screen_write_ctx *ctx, u_int ny, int top, int bottom)
screen_write_cursormove(ctx, cx, cy + i); screen_write_cursormove(ctx, cx, cy + i);
screen_write_putc(ctx, &gc, 'x'); screen_write_putc(ctx, &gc, 'x');
} }
screen_write_cursormove(ctx, cx, cy + ny); screen_write_cursormove(ctx, cx, cy + ny - 1);
screen_write_putc(ctx, &gc, bottom ? 'v' : 'x'); screen_write_putc(ctx, &gc, bottom ? 'v' : 'x');
screen_write_cursormove(ctx, cx, cy); screen_write_cursormove(ctx, cx, cy);
@@ -471,7 +505,10 @@ screen_write_box(struct screen_write_ctx *ctx, u_int nx, u_int ny)
screen_write_cursormove(ctx, cx, cy); screen_write_cursormove(ctx, cx, cy);
} }
/* Write a preview version of a window. */ /*
* Write a preview version of a window. Assumes target area is big enough and
* already cleared.
*/
void void
screen_write_preview(struct screen_write_ctx *ctx, struct screen *src, u_int nx, screen_write_preview(struct screen_write_ctx *ctx, struct screen *src, u_int nx,
u_int ny) u_int ny)
@@ -515,8 +552,7 @@ screen_write_preview(struct screen_write_ctx *ctx, struct screen *src, u_int nx,
py = 0; py = 0;
} }
screen_write_copy(ctx, src, px, src->grid->hsize + py, nx, ny, NULL, screen_write_fast_copy(ctx, src, px, src->grid->hsize + py, nx, ny);
NULL);
if (src->mode & MODE_CURSOR) { if (src->mode & MODE_CURSOR) {
grid_view_get_cell(src->grid, src->cx, src->cy, &gc); grid_view_get_cell(src->grid, src->cx, src->cy, &gc);
@@ -1243,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;
@@ -1255,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. */
@@ -1278,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;
@@ -1388,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;
} }
@@ -1537,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;
} }
@@ -1557,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. */
@@ -110,6 +141,43 @@ screen_set_title(struct screen *s, const char *title)
utf8_stravis(&s->title, title, VIS_OCTAL|VIS_CSTYLE|VIS_TAB|VIS_NL); 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. */
void void
screen_resize(struct screen *s, u_int sx, u_int sy, int reflow) screen_resize(struct screen *s, u_int sx, u_int sy, int reflow)
@@ -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

@@ -159,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;
@@ -193,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);
@@ -212,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. */
@@ -269,12 +270,12 @@ 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->old_status != NULL) { if (c->status.old_status != NULL) {
screen_free(c->old_status); screen_free(c->status.old_status);
free(c->old_status); free(c->status.old_status);
} }
free(c->title); free(c->title);
@@ -904,6 +905,7 @@ 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.
*/ */
retry:
key0 = (key & ~KEYC_XTERM); key0 = (key & ~KEYC_XTERM);
if ((key0 == (key_code)options_get_number(s->options, "prefix") || if ((key0 == (key_code)options_get_number(s->options, "prefix") ||
key0 == (key_code)options_get_number(s->options, "prefix2")) && key0 == (key_code)options_get_number(s->options, "prefix2")) &&
@@ -914,7 +916,6 @@ server_client_handle_key(struct client *c, key_code key)
} }
flags = c->flags; 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);
@@ -1067,7 +1068,7 @@ server_client_resize_force(struct window_pane *wp)
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 - 1; ws.ws_row = wp->sy - 1;
if (ioctl(wp->fd, TIOCSWINSZ, &ws) == -1) if (wp->fd != -1 && ioctl(wp->fd, TIOCSWINSZ, &ws) == -1)
#ifdef __sun #ifdef __sun
if (errno != EINVAL && errno != ENXIO) if (errno != EINVAL && errno != ENXIO)
#endif #endif
@@ -1096,7 +1097,7 @@ server_client_resize_event(__unused int fd, __unused short events, void *data)
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 (ioctl(wp->fd, TIOCSWINSZ, &ws) == -1) if (wp->fd != -1 && 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
@@ -1211,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;
@@ -1219,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
@@ -1287,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;
} }
@@ -1559,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);

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>
@@ -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,9 +292,13 @@ 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);
@@ -302,11 +307,24 @@ server_destroy_pane(struct window_pane *wp, int notify)
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, 8); 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;
} }
@@ -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);
}
} }

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,39 +97,62 @@ 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. */
@@ -141,6 +163,8 @@ server_start(struct tmuxproc *client, struct event_base *base, int lockfd,
int pair[2]; int pair[2];
struct job *job; struct job *job;
sigset_t set, oldset; 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");
@@ -182,11 +206,10 @@ server_start(struct tmuxproc *client, struct event_base *base, int lockfd,
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);
@@ -194,6 +217,11 @@ server_start(struct tmuxproc *client, struct event_base *base, int lockfd,
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);
@@ -215,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);
@@ -226,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);
@@ -244,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);
} }
@@ -259,8 +296,11 @@ 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;
} }
@@ -374,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;
@@ -423,6 +463,7 @@ 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;
wp->flags |= PANE_STATUSREADY;
log_debug("%%%u exited", wp->id); log_debug("%%%u exited", wp->id);
wp->flags |= PANE_EXITED; wp->flags |= PANE_EXITED;

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

127
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,20 +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. */ /* Delete the saved status line, if any. */
if (c->old_status != NULL) { if (c->status.old_status != NULL) {
screen_free(c->old_status); screen_free(c->status.old_status);
free(c->old_status); free(c->status.old_status);
c->old_status = NULL; 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;
@@ -321,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. */
@@ -446,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);
@@ -492,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);
@@ -502,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);
} }
@@ -575,10 +590,11 @@ status_message_set(struct client *c, const char *fmt, ...)
status_message_clear(c); status_message_clear(c);
if (c->old_status == NULL) { if (c->status.old_status == NULL) {
c->old_status = xmalloc(sizeof *c->old_status); c->status.old_status = xmalloc(sizeof *c->status.old_status);
memcpy(c->old_status, &c->status, sizeof *c->old_status); memcpy(c->status.old_status, &c->status.status,
screen_init(&c->status, c->tty.sx, 1, 0); sizeof *c->status.old_status);
screen_init(&c->status.status, c->tty.sx, 1, 0);
} }
va_start(ap, fmt); va_start(ap, fmt);
@@ -616,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. */
@@ -637,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)
@@ -649,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);
} }
@@ -689,10 +711,11 @@ status_prompt_set(struct client *c, const char *msg, const char *input,
status_message_clear(c); status_message_clear(c);
status_prompt_clear(c); status_prompt_clear(c);
if (c->old_status == NULL) { if (c->status.old_status == NULL) {
c->old_status = xmalloc(sizeof *c->old_status); c->status.old_status = xmalloc(sizeof *c->status.old_status);
memcpy(c->old_status, &c->status, sizeof *c->old_status); memcpy(c->status.old_status, &c->status.status,
screen_init(&c->status, c->tty.sx, 1, 0); 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);
@@ -742,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. */
@@ -782,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");
@@ -801,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;
@@ -846,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);
} }
@@ -1305,6 +1337,7 @@ process_key:
break; break;
case '\033': /* Escape */ case '\033': /* Escape */
case '\003': /* C-c */ case '\003': /* C-c */
case '\007': /* C-g */
if (c->prompt_inputcb(c, c->prompt_data, NULL, 1) == 0) if (c->prompt_inputcb(c, c->prompt_data, NULL, 1) == 0)
status_prompt_clear(c); status_prompt_clear(c);
break; break;

110
tmux.1
View File

@@ -980,22 +980,37 @@ them with
and and
.Em %endif .Em %endif
lines. lines.
Additional
.Em %elif
and
.Em %else
lines may also be used.
The argument to The argument to
.Em %if .Em %if
is expanded as a format and if it evaluates to false and
(zero or empty), subsequent lines are ignored until .Em %elif
is expanded as a format and if it evaluates to false (zero or empty),
subsequent lines are ignored until the next
.Em %elif ,
.Em %else
or
.Em %endif . .Em %endif .
For example: For example:
.Bd -literal -offset indent .Bd -literal -offset indent
%if #{==:#{host},myhost} %if #{==:#{host},myhost}
set -g status-style bg=red set -g status-style bg=red
%elif #{==:#{host},myotherhost}
set -g status-style bg=green
%else
set -g status-style bg=blue
%endif %endif
.Ed .Ed
.Pp .Pp
Will change the status line to red if running on Will change the status line to red if running on
.Ql myhost . .Ql myhost ,
.Em %if green if running on
may not be nested. .Ql myotherhost ,
or blue if running on another host.
.It Ic start-server .It Ic start-server
.D1 (alias: Ic start ) .D1 (alias: Ic start )
Start the Start the
@@ -1111,9 +1126,10 @@ The following commands are supported in copy mode:
.It Li "end-of-line" Ta "$" Ta "C-e" .It Li "end-of-line" Ta "$" Ta "C-e"
.It Li "goto-line <line>" Ta ":" Ta "g" .It Li "goto-line <line>" Ta ":" Ta "g"
.It Li "halfpage-down" Ta "C-d" Ta "M-Down" .It Li "halfpage-down" Ta "C-d" Ta "M-Down"
.It Li "halfpage-down-and-cancel" Ta "" Ta ""
.It Li "halfpage-up" Ta "C-u" Ta "M-Up" .It Li "halfpage-up" Ta "C-u" Ta "M-Up"
.It Li "history-bottom" Ta "G" Ta "M-<" .It Li "history-bottom" Ta "G" Ta "M->"
.It Li "history-top" Ta "g" Ta "M->" .It Li "history-top" Ta "g" Ta "M-<"
.It Li "jump-again" Ta ";" Ta ";" .It Li "jump-again" Ta ";" Ta ";"
.It Li "jump-backward <to>" Ta "F" Ta "F" .It Li "jump-backward <to>" Ta "F" Ta "F"
.It Li "jump-forward <to>" Ta "f" Ta "f" .It Li "jump-forward <to>" Ta "f" Ta "f"
@@ -1128,12 +1144,14 @@ The following commands are supported in copy mode:
.It Li "next-word-end" Ta "e" Ta "M-f" .It Li "next-word-end" Ta "e" Ta "M-f"
.It Li "other-end" Ta "o" Ta "" .It Li "other-end" Ta "o" Ta ""
.It Li "page-down" Ta "C-f" Ta "PageDown" .It Li "page-down" Ta "C-f" Ta "PageDown"
.It Li "page-down-and-cancel" Ta "" Ta ""
.It Li "page-up" Ta "C-b" Ta "PageUp" .It Li "page-up" Ta "C-b" Ta "PageUp"
.It Li "previous-paragraph" Ta "{" Ta "M-{" .It Li "previous-paragraph" Ta "{" Ta "M-{"
.It Li "previous-space" Ta "B" Ta "" .It Li "previous-space" Ta "B" Ta ""
.It Li "previous-word" Ta "b" Ta "M-b" .It Li "previous-word" Ta "b" Ta "M-b"
.It Li "rectangle-toggle" Ta "v" Ta "R" .It Li "rectangle-toggle" Ta "v" Ta "R"
.It Li "scroll-down" Ta "C-e" Ta "C-Down" .It Li "scroll-down" Ta "C-e" Ta "C-Down"
.It Li "scroll-down-and-cancel" Ta "" Ta ""
.It Li "scroll-up" Ta "C-y" Ta "C-Up" .It Li "scroll-up" Ta "C-y" Ta "C-Up"
.It Li "search-again" Ta "n" Ta "n" .It Li "search-again" Ta "n" Ta "n"
.It Li "search-backward <for>" Ta "?" Ta "" .It Li "search-backward <for>" Ta "?" Ta ""
@@ -1147,6 +1165,11 @@ The following commands are supported in copy mode:
.It Li "top-line" Ta "H" Ta "M-R" .It Li "top-line" Ta "H" Ta "M-R"
.El .El
.Pp .Pp
The
.Ql -and-cancel
variants of some commands exit copy mode after they have completed (for copy
commands) or when the cursor reaches the bottom (for scrolling commands).
.Pp
The next and previous word keys use space and the The next and previous word keys use space and the
.Ql - , .Ql - ,
.Ql _ .Ql _
@@ -1358,7 +1381,7 @@ the end of the visible pane.
The default is to capture only the visible contents of the pane. The default is to capture only the visible contents of the pane.
.It Xo .It Xo
.Ic choose-client .Ic choose-client
.Op Fl N .Op Fl NZ
.Op Fl F Ar format .Op Fl F Ar format
.Op Fl f Ar filter .Op Fl f Ar filter
.Op Fl O Ar sort-order .Op Fl O Ar sort-order
@@ -1367,6 +1390,8 @@ The default is to capture only the visible contents of the pane.
.Xc .Xc
Put a pane into client mode, allowing a client to be selected interactively from Put a pane into client mode, allowing a client to be selected interactively from
a list. a list.
.Fl Z
zooms the pane.
The following keys may be used in client mode: The following keys may be used in client mode:
.Bl -column "Key" "Function" -offset indent .Bl -column "Key" "Function" -offset indent
.It Sy "Key" Ta Sy "Function" .It Sy "Key" Ta Sy "Function"
@@ -1417,7 +1442,7 @@ starts without the preview.
This command works only if at least one client is attached. This command works only if at least one client is attached.
.It Xo .It Xo
.Ic choose-tree .Ic choose-tree
.Op Fl Nsw .Op Fl GNswZ
.Op Fl F Ar format .Op Fl F Ar format
.Op Fl f Ar filter .Op Fl f Ar filter
.Op Fl O Ar sort-order .Op Fl O Ar sort-order
@@ -1430,6 +1455,8 @@ interactively from a list.
starts with sessions collapsed and starts with sessions collapsed and
.Fl w .Fl w
with windows collapsed. with windows collapsed.
.Fl Z
zooms the pane.
The following keys may be used in tree mode: The following keys may be used in tree mode:
.Bl -column "Key" "Function" -offset indent .Bl -column "Key" "Function" -offset indent
.It Sy "Key" Ta Sy "Function" .It Sy "Key" Ta Sy "Function"
@@ -1473,6 +1500,9 @@ If a filter would lead to an empty list, it is ignored.
specifies the format for each item in the tree. specifies the format for each item in the tree.
.Fl N .Fl N
starts without the preview. starts without the preview.
.Fl G
includes all sessions in any session groups in the tree rather than only the
first.
This command works only if at least one client is attached. This command works only if at least one client is attached.
.It Xo .It Xo
.Ic display-panes .Ic display-panes
@@ -1778,15 +1808,15 @@ If
.Fl a .Fl a
is used, move to the next window with an alert. is used, move to the next window with an alert.
.It Xo Ic pipe-pane .It Xo Ic pipe-pane
.Op Fl o .Op Fl IOo
.Op Fl t Ar target-pane .Op Fl t Ar target-pane
.Op Ar shell-command .Op Ar shell-command
.Xc .Xc
.D1 (alias: Ic pipep ) .D1 (alias: Ic pipep )
Pipe any output sent by the program in Pipe output sent by the program in
.Ar target-pane .Ar target-pane
to a shell command. to a shell command or vice versa.
A pane may only be piped to one command at a time, any existing pipe is A pane may only be connected to one command at a time, any existing pipe is
closed before closed before
.Ar shell-command .Ar shell-command
is executed. is executed.
@@ -1799,6 +1829,25 @@ If no
.Ar shell-command .Ar shell-command
is given, the current pipe (if any) is closed. is given, the current pipe (if any) is closed.
.Pp .Pp
.Fl I
and
.Fl O
specify which of the
.Ar shell-command
output streams are connected to the pane:
with
.Fl I
stdout is connected (so anything
.Ar shell-command
prints is written to the pane as if it were typed);
with
.Fl O
stdin is connected (so any output in the pane is piped to
.Ar shell-command ) .
Both may be used together and if neither are specified,
.Fl O
is used.
.Pp
The The
.Fl o .Fl o
option only opens a new pipe if no previous pipe exists, allowing a pipe to option only opens a new pipe if no previous pipe exists, allowing a pipe to
@@ -1909,8 +1958,8 @@ lower) with
.Fl U .Fl U
or downward (numerically higher). or downward (numerically higher).
.It Xo Ic select-layout .It Xo Ic select-layout
.Op Fl nop .Op Fl Enop
.Op Fl t Ar target-window .Op Fl t Ar target-pane
.Op Ar layout-name .Op Ar layout-name
.Xc .Xc
.D1 (alias: Ic selectl ) .D1 (alias: Ic selectl )
@@ -1928,6 +1977,8 @@ and
commands. commands.
.Fl o .Fl o
applies the last set layout if possible (undoes the most recent layout change). applies the last set layout if possible (undoes the most recent layout change).
.Fl E
spreads the current pane and any panes next to it out evenly.
.It Xo Ic select-pane .It Xo Ic select-pane
.Op Fl DdegLlMmRU .Op Fl DdegLlMmRU
.Op Fl P Ar style .Op Fl P Ar style
@@ -2460,6 +2511,11 @@ Set the time in milliseconds for which
waits after an escape is input to determine if it is part of a function or meta waits after an escape is input to determine if it is part of a function or meta
key sequences. key sequences.
The default is 500 milliseconds. The default is 500 milliseconds.
.It Xo Ic exit-empty
.Op Ic on | off
.Xc
If enabled (the default), the server will exit when there are no active
sessions.
.It Xo Ic exit-unattached .It Xo Ic exit-unattached
.Op Ic on | off .Op Ic on | off
.Xc .Xc
@@ -3008,7 +3064,7 @@ and poor for interactive programs such as shells.
.Xc .Xc
Allow programs to change the window name using a terminal escape Allow programs to change the window name using a terminal escape
sequence (\eek...\ee\e\e). sequence (\eek...\ee\e\e).
The default is on. The default is off.
.Pp .Pp
.It Xo Ic alternate-screen .It Xo Ic alternate-screen
.Op Ic on | off .Op Ic on | off
@@ -3336,7 +3392,11 @@ layout after every
set-hook after-split-window "selectl even-vertical" set-hook after-split-window "selectl even-vertical"
.Ed .Ed
.Pp .Pp
In addition, the following hooks are available: All the notifications listed in the
.Sx CONTROL MODE
section are hooks (without any arguments), except
.Ic %exit .
The following additional hooks are available:
.Bl -tag -width "XXXXXXXXXXXXXXXXXXXXXX" .Bl -tag -width "XXXXXXXXXXXXXXXXXXXXXX"
.It alert-activity .It alert-activity
Run when a window has activity. Run when a window has activity.
@@ -3435,8 +3495,8 @@ The following mouse events are available:
.It Li "MouseDown1" Ta "MouseUp1" Ta "MouseDrag1" Ta "MouseDragEnd1" .It Li "MouseDown1" Ta "MouseUp1" Ta "MouseDrag1" Ta "MouseDragEnd1"
.It Li "MouseDown2" Ta "MouseUp2" Ta "MouseDrag2" Ta "MouseDragEnd2" .It Li "MouseDown2" Ta "MouseUp2" Ta "MouseDrag2" Ta "MouseDragEnd2"
.It Li "MouseDown3" Ta "MouseUp3" Ta "MouseDrag3" Ta "MouseDragEnd3" .It Li "MouseDown3" Ta "MouseUp3" Ta "MouseDrag3" Ta "MouseDragEnd3"
.It Li "DoubleClick1" Ta "DoubleClick2" Ta "DoubleClick3" Ta "WheelUp" .It Li "DoubleClick1" Ta "DoubleClick2" Ta "DoubleClick3"
.It Li "TripleClick1" Ta "TripleClick2" Ta "TripleClick3" Ta "WheelDown" .It Li "TripleClick1" Ta "TripleClick2" Ta "TripleClick3"
.El .El
.Pp .Pp
Each should be suffixed with a location, for example Each should be suffixed with a location, for example
@@ -3638,7 +3698,7 @@ The following variables are available, where appropriate:
.It Li "cursor_y" Ta "" Ta "Cursor Y position in pane" .It Li "cursor_y" Ta "" Ta "Cursor Y position in pane"
.It Li "history_bytes" Ta "" Ta "Number of bytes in window history" .It Li "history_bytes" Ta "" Ta "Number of bytes in window history"
.It Li "history_limit" Ta "" Ta "Maximum window history lines" .It Li "history_limit" Ta "" Ta "Maximum window history lines"
.It Li "history_size" Ta "" Ta "Size of history in bytes" .It Li "history_size" Ta "" Ta "Size of history in lines"
.It Li "hook" Ta "" Ta "Name of running hook, if any" .It Li "hook" Ta "" Ta "Name of running hook, if any"
.It Li "hook_pane" Ta "" Ta "ID of pane where hook was run, if any" .It Li "hook_pane" Ta "" Ta "ID of pane where hook was run, if any"
.It Li "hook_session" Ta "" Ta "ID of session where hook was run, if any" .It Li "hook_session" Ta "" Ta "ID of session where hook was run, if any"
@@ -3696,6 +3756,8 @@ The following variables are available, where appropriate:
.It Li "session_format" Ta "" Ta "1 if format is for a session (not assuming the current)" .It Li "session_format" Ta "" Ta "1 if format is for a session (not assuming the current)"
.It Li "session_last_attached" Ta "" Ta "Time session last attached" .It Li "session_last_attached" Ta "" Ta "Time session last attached"
.It Li "session_group" Ta "" Ta "Name of session group" .It Li "session_group" Ta "" Ta "Name of session group"
.It Li "session_group_size" Ta "" Ta "Size of session group"
.It Li "session_group_list" Ta "" Ta "List of sessions in group"
.It Li "session_grouped" Ta "" Ta "1 if session in a group" .It Li "session_grouped" Ta "" Ta "1 if session in a group"
.It Li "session_height" Ta "" Ta "Height of session" .It Li "session_height" Ta "" Ta "Height of session"
.It Li "session_id" Ta "" Ta "Unique session ID" .It Li "session_id" Ta "" Ta "Unique session ID"
@@ -4077,7 +4139,7 @@ The buffer commands are as follows:
.Bl -tag -width Ds .Bl -tag -width Ds
.It Xo .It Xo
.Ic choose-buffer .Ic choose-buffer
.Op Fl N .Op Fl NZ
.Op Fl F Ar format .Op Fl F Ar format
.Op Fl f Ar filter .Op Fl f Ar filter
.Op Fl O Ar sort-order .Op Fl O Ar sort-order
@@ -4086,10 +4148,12 @@ The buffer commands are as follows:
.Xc .Xc
Put a pane into buffer mode, where a buffer may be chosen interactively from Put a pane into buffer mode, where a buffer may be chosen interactively from
a list. a list.
.Fl Z
zooms the pane.
The following keys may be used in buffer mode: The following keys may be used in buffer mode:
.Bl -column "Key" "Function" -offset indent .Bl -column "Key" "Function" -offset indent
.It Sy "Key" Ta Sy "Function" .It Sy "Key" Ta Sy "Function"
.It Li "Enter" Ta "Choose selected buffer" .It Li "Enter" Ta "Paste selected buffer"
.It Li "Up" Ta "Select previous buffer" .It Li "Up" Ta "Select previous buffer"
.It Li "Down" Ta "Select next buffer" .It Li "Down" Ta "Select next buffer"
.It Li "C-s" Ta "Search by name or content" .It Li "C-s" Ta "Search by name or content"
@@ -4097,6 +4161,8 @@ The following keys may be used in buffer mode:
.It Li "t" Ta "Toggle if buffer is tagged" .It Li "t" Ta "Toggle if buffer is tagged"
.It Li "T" Ta "Tag no buffers" .It Li "T" Ta "Tag no buffers"
.It Li "C-t" Ta "Tag all buffers" .It Li "C-t" Ta "Tag all buffers"
.It Li "p" Ta "Paste selected buffer"
.It Li "P" Ta "Paste tagged buffers"
.It Li "d" Ta "Delete selected buffer" .It Li "d" Ta "Delete selected buffer"
.It Li "D" Ta "Delete tagged buffers" .It Li "D" Ta "Delete tagged buffers"
.It Li "f" Ta "Enter a format to filter items" .It Li "f" Ta "Enter a format to filter items"

43
tmux.c
View File

@@ -44,7 +44,7 @@ int ptm_fd = -1;
const char *shell_command; 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 *);
@@ -106,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";
@@ -121,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;
@@ -135,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);
} }
@@ -188,9 +187,9 @@ find_home(void)
int int
main(int argc, char **argv) main(int argc, char **argv)
{ {
char *path, *label, **var; char *path, *label, *cause, **var;
char tmp[PATH_MAX]; 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;
@@ -294,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);
@@ -340,8 +340,11 @@ 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;

60
tmux.h
View File

@@ -403,6 +403,7 @@ enum tty_code_code {
TTYC_MS, TTYC_MS,
TTYC_OP, TTYC_OP,
TTYC_REV, TTYC_REV,
TTYC_RGB,
TTYC_RI, TTYC_RI,
TTYC_RMACS, TTYC_RMACS,
TTYC_RMCUP, TTYC_RMCUP,
@@ -554,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 {
@@ -624,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;
@@ -664,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 */
@@ -780,6 +787,8 @@ struct window_pane {
#define PANE_INPUTOFF 0x40 #define PANE_INPUTOFF 0x40
#define PANE_CHANGED 0x80 #define PANE_CHANGED 0x80
#define PANE_EXITED 0x100 #define PANE_EXITED 0x100
#define PANE_STATUSREADY 0x200
#define PANE_STATUSDRAWN 0x400
int argc; int argc;
char **argv; char **argv;
@@ -1311,6 +1320,13 @@ 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 int (*prompt_input_cb)(struct client *, void *, const char *, int);
typedef void (*prompt_free_cb)(void *); typedef void (*prompt_free_cb)(void *);
@@ -1353,10 +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;
struct screen *old_status;
#define CLIENT_TERMINAL 0x1 #define CLIENT_TERMINAL 0x1
#define CLIENT_LOGIN 0x2 #define CLIENT_LOGIN 0x2
@@ -1381,6 +1394,7 @@ struct client {
#define CLIENT_DOUBLECLICK 0x100000 #define CLIENT_DOUBLECLICK 0x100000
#define CLIENT_TRIPLECLICK 0x200000 #define CLIENT_TRIPLECLICK 0x200000
#define CLIENT_SIZECHANGED 0x400000 #define CLIENT_SIZECHANGED 0x400000
#define CLIENT_STATUSOFF 0x800000
int flags; int flags;
struct key_table *keytable; struct key_table *keytable;
@@ -1580,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 *, ...);
@@ -1644,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);
@@ -1884,7 +1896,7 @@ 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 *);
@@ -1928,6 +1940,7 @@ 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 *, ...);
@@ -1994,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 *);
@@ -2035,6 +2048,8 @@ 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_hline(struct screen_write_ctx *, u_int, int, int);
void screen_write_vline(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_box(struct screen_write_ctx *, u_int, u_int);
@@ -2086,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 *);
@@ -2132,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 *);
@@ -2207,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 *);
@@ -2219,17 +2238,23 @@ u_int layout_set_next(struct window *);
u_int layout_set_previous(struct window *); u_int layout_set_previous(struct window *);
/* mode-tree.c */ /* mode-tree.c */
typedef void (*mode_tree_build_cb)(void *, u_int, uint64_t *, const char *);
typedef void (*mode_tree_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 *); u_int mode_tree_count_tagged(struct mode_tree_data *);
void *mode_tree_get_current(struct mode_tree_data *); void *mode_tree_get_current(struct mode_tree_data *);
void mode_tree_each_tagged(struct mode_tree_data *, void (*)(void *, void *, void mode_tree_expand_current(struct mode_tree_data *);
key_code), key_code, int); 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_up(struct mode_tree_data *, int);
void mode_tree_down(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 *, struct mode_tree_data *mode_tree_start(struct window_pane *, struct args *,
void (*)(void *, u_int, uint64_t *, const char *), mode_tree_build_cb, mode_tree_draw_cb, mode_tree_search_cb,
struct screen *(*)(void *, void *, u_int, u_int), void *, const char **, u_int, struct screen **);
int (*)(void *, void *, const char *), void *, const char **, void mode_tree_zoom(struct mode_tree_data *, struct args *);
u_int, struct screen **);
void mode_tree_build(struct mode_tree_data *); void mode_tree_build(struct mode_tree_data *);
void mode_tree_free(struct mode_tree_data *); void mode_tree_free(struct mode_tree_data *);
void mode_tree_resize(struct mode_tree_data *, u_int, u_int); void mode_tree_resize(struct mode_tree_data *, u_int, u_int);
@@ -2239,7 +2264,7 @@ struct mode_tree_item *mode_tree_add(struct mode_tree_data *,
void mode_tree_remove(struct mode_tree_data *, struct mode_tree_item *); void mode_tree_remove(struct mode_tree_data *, struct mode_tree_item *);
void mode_tree_draw(struct mode_tree_data *); void mode_tree_draw(struct mode_tree_data *);
int mode_tree_key(struct mode_tree_data *, struct client *, key_code *, int mode_tree_key(struct mode_tree_data *, struct client *, key_code *,
struct mouse_event *); struct mouse_event *, u_int *, u_int *);
void mode_tree_run_command(struct client *, struct cmd_find_state *, void mode_tree_run_command(struct client *, struct cmd_find_state *,
const char *, const char *); const char *, const char *);
@@ -2331,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 */

View File

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

View File

@@ -271,7 +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_UP|KEYC_SHIFT|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 },

View File

@@ -240,6 +240,7 @@ 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" },
@@ -531,8 +532,11 @@ tty_term_find(char *name, int fd, char **cause)
code->type = TTYCODE_STRING; code->type = TTYCODE_STRING;
} }
/* On terminals with RGB colour (TC), fill in setrgbf and setrgbb. */ /*
if (tty_term_flag(term, TTYC_TC) && * 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_SETRGBF) &&
!tty_term_has(term, TTYC_SETRGBB)) { !tty_term_has(term, TTYC_SETRGBB)) {
code = &term->codes[TTYC_SETRGBF]; code = &term->codes[TTYC_SETRGBF];

122
tty.c
View File

@@ -878,11 +878,37 @@ 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;
const struct grid_cell *gcp;
u_int i, j, ux, sx, nx, width; u_int i, j, ux, sx, nx, width;
int flags, cleared = 0; int flags, cleared = 0;
char buf[512]; char buf[512];
@@ -900,15 +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; 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) {
@@ -932,18 +958,16 @@ 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; ux += width;
@@ -952,28 +976,27 @@ tty_draw_line(struct tty *tty, const struct window_pane *wp,
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; 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) {
@@ -993,8 +1016,8 @@ tty_draw_line(struct tty *tty, const struct window_pane *wp,
} }
} }
nx = screen_size_x(s) - ux; if (!cleared && ux < screen_size_x(s)) {
if (!cleared && ux < tty->sx && nx != 0) { nx = screen_size_x(s) - ux;
tty_default_attributes(tty, wp, 8); tty_default_attributes(tty, wp, 8);
tty_clear_line(tty, wp, oy + py, ox + ux, nx, 8); tty_clear_line(tty, wp, oy + py, ox + ux, nx, 8);
} }
@@ -1038,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);
} }
@@ -1400,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) &&
@@ -1416,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
@@ -2034,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;
/* /*
@@ -2079,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);
} }

View File

@@ -195,26 +195,20 @@ window_buffer_build(void *modedata, u_int sort_type, __unused uint64_t *tag,
} }
static struct screen * static void
window_buffer_draw(__unused void *modedata, void *itemdata, u_int sx, u_int sy) 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 window_buffer_itemdata *item = itemdata;
struct paste_buffer *pb; struct paste_buffer *pb;
static struct screen s;
struct screen_write_ctx ctx;
char line[1024]; char line[1024];
const char *pdata, *end, *cp; const char *pdata, *end, *cp;
size_t psize, at; size_t psize, at;
u_int i; u_int i, cx = ctx->s->cx, cy = ctx->s->cy;
pb = paste_get_name(item->name); pb = paste_get_name(item->name);
if (pb == NULL) if (pb == NULL)
return (NULL); return;
screen_init(&s, sx, sy, 0);
screen_write_start(&ctx, NULL, &s);
screen_write_clearscreen(&ctx, 8);
pdata = end = paste_buffer_data(pb, &psize); pdata = end = paste_buffer_data(pb, &psize);
for (i = 0; i < sy; i++) { for (i = 0; i < sy; i++) {
@@ -231,17 +225,14 @@ window_buffer_draw(__unused void *modedata, void *itemdata, u_int sx, u_int sy)
line[at] = '\0'; line[at] = '\0';
if (*line != '\0') { if (*line != '\0') {
screen_write_cursormove(&ctx, 0, i); screen_write_cursormove(ctx, cx, cy + i);
screen_write_puts(&ctx, &grid_default_cell, "%s", line); screen_write_puts(ctx, &grid_default_cell, "%s", line);
} }
if (end == pdata + psize) if (end == pdata + psize)
break; break;
end++; end++;
} }
screen_write_stop(&ctx);
return (&s);
} }
static int static int
@@ -281,6 +272,7 @@ window_buffer_init(struct window_pane *wp, __unused struct cmd_find_state *fs,
data->data = mode_tree_start(wp, args, window_buffer_build, data->data = mode_tree_start(wp, args, window_buffer_build,
window_buffer_draw, window_buffer_search, data, window_buffer_draw, window_buffer_search, data,
window_buffer_sort_list, nitems(window_buffer_sort_list), &s); window_buffer_sort_list, nitems(window_buffer_sort_list), &s);
mode_tree_zoom(data->data, args);
mode_tree_build(data->data); mode_tree_build(data->data);
mode_tree_draw(data->data); mode_tree_draw(data->data);
@@ -318,7 +310,8 @@ window_buffer_resize(struct window_pane *wp, u_int sx, u_int sy)
} }
static void static void
window_buffer_do_delete(void* modedata, void *itemdata, __unused key_code key) 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_modedata *data = modedata;
struct window_buffer_itemdata *item = itemdata; struct window_buffer_itemdata *item = itemdata;
@@ -330,53 +323,53 @@ window_buffer_do_delete(void* modedata, void *itemdata, __unused key_code key)
paste_free(pb); 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 static void
window_buffer_key(struct window_pane *wp, struct client *c, window_buffer_key(struct window_pane *wp, struct client *c,
__unused struct session *s, key_code key, struct mouse_event *m) __unused struct session *s, key_code key, struct mouse_event *m)
{ {
struct window_buffer_modedata *data = wp->modedata; struct window_buffer_modedata *data = wp->modedata;
struct mode_tree_data *mtd = data->data;
struct window_buffer_itemdata *item; struct window_buffer_itemdata *item;
char *command, *name;
int finished; int finished;
/* finished = mode_tree_key(mtd, c, &key, m, NULL, NULL);
* t = toggle tag
* T = tag none
* C-t = tag all
* q = exit
* O = change sort order
*
* d = delete buffer
* D = delete tagged buffers
* Enter = paste buffer
*/
finished = mode_tree_key(data->data, c, &key, m);
switch (key) { switch (key) {
case 'd': case 'd':
item = mode_tree_get_current(data->data); item = mode_tree_get_current(mtd);
window_buffer_do_delete(data, item, key); window_buffer_do_delete(data, item, c, key);
mode_tree_build(data->data); mode_tree_build(mtd);
break; break;
case 'D': case 'D':
mode_tree_each_tagged(data->data, window_buffer_do_delete, key, mode_tree_each_tagged(mtd, window_buffer_do_delete, c, key, 0);
0); mode_tree_build(mtd);
mode_tree_build(data->data);
break; break;
case 'P':
mode_tree_each_tagged(mtd, window_buffer_do_paste, c, key, 0);
finished = 1;
break;
case 'p':
case '\r': case '\r':
item = mode_tree_get_current(data->data); item = mode_tree_get_current(mtd);
command = xstrdup(data->command); window_buffer_do_paste(data, item, c, key);
name = xstrdup(item->name); finished = 1;
window_pane_reset_mode(wp); break;
mode_tree_run_command(c, NULL, command, name);
free(name);
free(command);
return;
} }
if (finished || paste_get_top(NULL) == NULL) if (finished || paste_get_top(NULL) == NULL)
window_pane_reset_mode(wp); window_pane_reset_mode(wp);
else { else {
mode_tree_draw(data->data); mode_tree_draw(mtd);
wp->flags |= PANE_REDRAW; wp->flags |= PANE_REDRAW;
} }
} }

View File

@@ -210,37 +210,29 @@ window_client_build(void *modedata, u_int sort_type, __unused uint64_t *tag,
} }
} }
static struct screen * static void
window_client_draw(__unused void *modedata, void *itemdata, u_int sx, u_int sy) window_client_draw(__unused void *modedata, void *itemdata,
struct screen_write_ctx *ctx, u_int sx, u_int sy)
{ {
struct window_client_itemdata *item = itemdata; struct window_client_itemdata *item = itemdata;
struct client *c = item->c; struct client *c = item->c;
struct window_pane *wp; struct window_pane *wp;
static struct screen s; u_int cx = ctx->s->cx, cy = ctx->s->cy;
struct screen_write_ctx ctx;
if (c->session == NULL || (c->flags & (CLIENT_DEAD|CLIENT_DETACHING))) if (c->session == NULL || (c->flags & (CLIENT_DEAD|CLIENT_DETACHING)))
return (NULL); return;
wp = c->session->curw->window->active; wp = c->session->curw->window->active;
screen_init(&s, sx, sy, 0); screen_write_preview(ctx, &wp->base, sx, sy - 3);
screen_write_start(&ctx, NULL, &s); screen_write_cursormove(ctx, cx, cy + sy - 2);
screen_write_clearscreen(&ctx, 8); screen_write_hline(ctx, sx, 0, 0);
screen_write_preview(&ctx, &wp->base, sx, sy - 3); screen_write_cursormove(ctx, cx, cy + sy - 1);
if (c->status.old_status != NULL)
screen_write_cursormove(&ctx, 0, sy - 2); screen_write_fast_copy(ctx, c->status.old_status, 0, 0, sx, 1);
screen_write_hline(&ctx, sx, 0, 0);
screen_write_cursormove(&ctx, 0, sy - 1);
if (c->old_status != NULL)
screen_write_copy(&ctx, c->old_status, 0, 0, sx, 1, NULL, NULL);
else else
screen_write_copy(&ctx, &c->status, 0, 0, sx, 1, NULL, NULL); screen_write_fast_copy(ctx, &c->status.status, 0, 0, sx, 1);
screen_write_stop(&ctx);
return (&s);
} }
static struct screen * static struct screen *
@@ -264,6 +256,7 @@ window_client_init(struct window_pane *wp, __unused struct cmd_find_state *fs,
data->data = mode_tree_start(wp, args, window_client_build, data->data = mode_tree_start(wp, args, window_client_build,
window_client_draw, NULL, data, window_client_sort_list, window_client_draw, NULL, data, window_client_sort_list,
nitems(window_client_sort_list), &s); nitems(window_client_sort_list), &s);
mode_tree_zoom(data->data, args);
mode_tree_build(data->data); mode_tree_build(data->data);
mode_tree_draw(data->data); mode_tree_draw(data->data);
@@ -301,7 +294,8 @@ window_client_resize(struct window_pane *wp, u_int sx, u_int sy)
} }
static void static void
window_client_do_detach(void* modedata, void *itemdata, key_code key) window_client_do_detach(void* modedata, void *itemdata,
__unused struct client *c, key_code key)
{ {
struct window_client_modedata *data = modedata; struct window_client_modedata *data = modedata;
struct window_client_itemdata *item = itemdata; struct window_client_itemdata *item = itemdata;
@@ -321,56 +315,35 @@ window_client_key(struct window_pane *wp, struct client *c,
__unused struct session *s, key_code key, struct mouse_event *m) __unused struct session *s, key_code key, struct mouse_event *m)
{ {
struct window_client_modedata *data = wp->modedata; struct window_client_modedata *data = wp->modedata;
struct mode_tree_data *mtd = data->data;
struct window_client_itemdata *item; struct window_client_itemdata *item;
char *command, *name;
int finished; int finished;
/* finished = mode_tree_key(mtd, c, &key, m, NULL, NULL);
* t = toggle tag
* T = tag none
* C-t = tag all
* q = exit
* O = change sort order
*
* d = detach client
* D = detach tagged clients
* x = detach and kill client
* X = detach and kill tagged clients
* z = suspend client
* Z = suspend tagged clients
* Enter = detach client
*/
finished = mode_tree_key(data->data, c, &key, m);
switch (key) { switch (key) {
case 'd': case 'd':
case 'x': case 'x':
case 'z': case 'z':
item = mode_tree_get_current(data->data); item = mode_tree_get_current(mtd);
window_client_do_detach(data, item, key); window_client_do_detach(data, item, c, key);
mode_tree_build(data->data); mode_tree_build(mtd);
break; break;
case 'D': case 'D':
case 'X': case 'X':
case 'Z': case 'Z':
mode_tree_each_tagged(data->data, window_client_do_detach, key, mode_tree_each_tagged(mtd, window_client_do_detach, c, key, 0);
0); mode_tree_build(mtd);
mode_tree_build(data->data);
break; break;
case '\r': case '\r':
item = mode_tree_get_current(data->data); item = mode_tree_get_current(mtd);
command = xstrdup(data->command); mode_tree_run_command(c, NULL, data->command, item->c->ttyname);
name = xstrdup(item->c->ttyname); finished = 1;
window_pane_reset_mode(wp); break;
mode_tree_run_command(c, NULL, command, name);
free(name);
free(command);
return;
} }
if (finished || server_client_how_many() == 0) if (finished || server_client_how_many() == 0)
window_pane_reset_mode(wp); window_pane_reset_mode(wp);
else { else {
mode_tree_draw(data->data); mode_tree_draw(mtd);
wp->flags |= PANE_REDRAW; wp->flags |= PANE_REDRAW;
} }
} }

View File

@@ -30,7 +30,7 @@ static void window_copy_command(struct window_pane *, struct client *,
static struct screen *window_copy_init(struct window_pane *, static struct screen *window_copy_init(struct window_pane *,
struct cmd_find_state *, struct args *); struct cmd_find_state *, struct args *);
static void window_copy_free(struct window_pane *); static void window_copy_free(struct window_pane *);
static int window_copy_pagedown(struct window_pane *, int); static int window_copy_pagedown(struct window_pane *, int, int);
static void window_copy_next_paragraph(struct window_pane *); static void window_copy_next_paragraph(struct window_pane *);
static void window_copy_previous_paragraph(struct window_pane *); static void window_copy_previous_paragraph(struct window_pane *);
static void window_copy_resize(struct window_pane *, u_int, u_int); static void window_copy_resize(struct window_pane *, u_int, u_int);
@@ -57,9 +57,9 @@ static void window_copy_move_right(struct screen *, u_int *, u_int *);
static int window_copy_is_lowercase(const char *); static int window_copy_is_lowercase(const char *);
static int window_copy_search_jump(struct window_pane *, struct grid *, static int window_copy_search_jump(struct window_pane *, struct grid *,
struct grid *, u_int, u_int, u_int, int, int, int); struct grid *, u_int, u_int, u_int, int, int, int);
static int window_copy_search(struct window_pane *, int, int); static int window_copy_search(struct window_pane *, int);
static int window_copy_search_up(struct window_pane *, int); static int window_copy_search_up(struct window_pane *);
static int window_copy_search_down(struct window_pane *, int); static int window_copy_search_down(struct window_pane *);
static void window_copy_goto_line(struct window_pane *, const char *); static void window_copy_goto_line(struct window_pane *, const char *);
static void window_copy_update_cursor(struct window_pane *, u_int, u_int); static void window_copy_update_cursor(struct window_pane *, u_int, u_int);
static void window_copy_start_selection(struct window_pane *); static void window_copy_start_selection(struct window_pane *);
@@ -91,8 +91,8 @@ static void window_copy_cursor_up(struct window_pane *, int);
static void window_copy_cursor_down(struct window_pane *, int); static void window_copy_cursor_down(struct window_pane *, int);
static void window_copy_cursor_jump(struct window_pane *); static void window_copy_cursor_jump(struct window_pane *);
static void window_copy_cursor_jump_back(struct window_pane *); static void window_copy_cursor_jump_back(struct window_pane *);
static void window_copy_cursor_jump_to(struct window_pane *, int); static void window_copy_cursor_jump_to(struct window_pane *);
static void window_copy_cursor_jump_to_back(struct window_pane *, int); static void window_copy_cursor_jump_to_back(struct window_pane *);
static void window_copy_cursor_next_word(struct window_pane *, static void window_copy_cursor_next_word(struct window_pane *,
const char *); const char *);
static void window_copy_cursor_next_word_end(struct window_pane *, static void window_copy_cursor_next_word_end(struct window_pane *,
@@ -392,7 +392,7 @@ window_copy_pageup(struct window_pane *wp, int half_page)
} }
static int static int
window_copy_pagedown(struct window_pane *wp, int half_page) window_copy_pagedown(struct window_pane *wp, int half_page, int scroll_exit)
{ {
struct window_copy_mode_data *data = wp->modedata; struct window_copy_mode_data *data = wp->modedata;
struct screen *s = &data->screen; struct screen *s = &data->screen;
@@ -431,7 +431,7 @@ window_copy_pagedown(struct window_pane *wp, int half_page)
window_copy_cursor_end_of_line(wp); window_copy_cursor_end_of_line(wp);
} }
if (data->scroll_exit && data->oy == 0) if (scroll_exit && data->oy == 0)
return (1); return (1);
window_copy_update_selection(wp, 1); window_copy_update_selection(wp, 1);
window_copy_redraw_screen(wp); window_copy_redraw_screen(wp);
@@ -524,7 +524,7 @@ window_copy_command(struct window_pane *wp, struct client *c, struct session *s,
struct screen *sn = &data->screen; struct screen *sn = &data->screen;
const char *command, *argument, *ws; const char *command, *argument, *ws;
u_int np = wp->modeprefix; u_int np = wp->modeprefix;
int cancel = 0, redraw = 0; int cancel = 0, redraw = 0, scroll_exit;
char prefix; char prefix;
if (args->argc == 0) if (args->argc == 0)
@@ -629,9 +629,14 @@ window_copy_command(struct window_pane *wp, struct client *c, struct session *s,
} }
if (strcmp(command, "end-of-line") == 0) if (strcmp(command, "end-of-line") == 0)
window_copy_cursor_end_of_line(wp); window_copy_cursor_end_of_line(wp);
if (strcmp(command, "halfpage-down") == 0) { if (strcmp(command, "halfpage-down") == 0 ||
strcmp(command, "halfpage-down-and-cancel") == 0) {
if (strcmp(command, "halfpage-down-and-cancel") == 0)
scroll_exit = 1;
else
scroll_exit = data->scroll_exit;
for (; np != 0; np--) { for (; np != 0; np--) {
if (window_copy_pagedown(wp, 1)) { if (window_copy_pagedown(wp, 1, scroll_exit)) {
cancel = 1; cancel = 1;
break; break;
} }
@@ -667,11 +672,11 @@ window_copy_command(struct window_pane *wp, struct client *c, struct session *s,
break; break;
case WINDOW_COPY_JUMPTOFORWARD: case WINDOW_COPY_JUMPTOFORWARD:
for (; np != 0; np--) for (; np != 0; np--)
window_copy_cursor_jump_to(wp, 1); window_copy_cursor_jump_to(wp);
break; break;
case WINDOW_COPY_JUMPTOBACKWARD: case WINDOW_COPY_JUMPTOBACKWARD:
for (; np != 0; np--) for (; np != 0; np--)
window_copy_cursor_jump_to_back(wp, 1); window_copy_cursor_jump_to_back(wp);
break; break;
} }
} }
@@ -687,11 +692,11 @@ window_copy_command(struct window_pane *wp, struct client *c, struct session *s,
break; break;
case WINDOW_COPY_JUMPTOFORWARD: case WINDOW_COPY_JUMPTOFORWARD:
for (; np != 0; np--) for (; np != 0; np--)
window_copy_cursor_jump_to_back(wp, 1); window_copy_cursor_jump_to_back(wp);
break; break;
case WINDOW_COPY_JUMPTOBACKWARD: case WINDOW_COPY_JUMPTOBACKWARD:
for (; np != 0; np--) for (; np != 0; np--)
window_copy_cursor_jump_to(wp, 1); window_copy_cursor_jump_to(wp);
break; break;
} }
} }
@@ -727,9 +732,14 @@ window_copy_command(struct window_pane *wp, struct client *c, struct session *s,
if ((np % 2) != 0) if ((np % 2) != 0)
window_copy_other_end(wp); window_copy_other_end(wp);
} }
if (strcmp(command, "page-down") == 0) { if (strcmp(command, "page-down") == 0 ||
strcmp(command, "page-down-and-cancel") == 0) {
if (strcmp(command, "page-down-and-cancel") == 0)
scroll_exit = 1;
else
scroll_exit = data->scroll_exit;
for (; np != 0; np--) { for (; np != 0; np--) {
if (window_copy_pagedown(wp, 0)) { if (window_copy_pagedown(wp, 0, scroll_exit)) {
cancel = 1; cancel = 1;
break; break;
} }
@@ -756,10 +766,15 @@ window_copy_command(struct window_pane *wp, struct client *c, struct session *s,
sn->sel.lineflag = LINE_SEL_NONE; sn->sel.lineflag = LINE_SEL_NONE;
window_copy_rectangle_toggle(wp); window_copy_rectangle_toggle(wp);
} }
if (strcmp(command, "scroll-down") == 0) { if (strcmp(command, "scroll-down") == 0 ||
strcmp(command, "scroll-down-and-cancel") == 0) {
if (strcmp(command, "scroll-down-and-cancel") == 0)
scroll_exit = 1;
else
scroll_exit = data->scroll_exit;
for (; np != 0; np--) for (; np != 0; np--)
window_copy_cursor_down(wp, 1); window_copy_cursor_down(wp, 1);
if (data->scroll_exit && data->oy == 0) if (scroll_exit && data->oy == 0)
cancel = 1; cancel = 1;
} }
if (strcmp(command, "scroll-up") == 0) { if (strcmp(command, "scroll-up") == 0) {
@@ -769,19 +784,19 @@ window_copy_command(struct window_pane *wp, struct client *c, struct session *s,
if (strcmp(command, "search-again") == 0) { if (strcmp(command, "search-again") == 0) {
if (data->searchtype == WINDOW_COPY_SEARCHUP) { if (data->searchtype == WINDOW_COPY_SEARCHUP) {
for (; np != 0; np--) for (; np != 0; np--)
window_copy_search_up(wp, 1); window_copy_search_up(wp);
} else if (data->searchtype == WINDOW_COPY_SEARCHDOWN) { } else if (data->searchtype == WINDOW_COPY_SEARCHDOWN) {
for (; np != 0; np--) for (; np != 0; np--)
window_copy_search_down(wp, 1); window_copy_search_down(wp);
} }
} }
if (strcmp(command, "search-reverse") == 0) { if (strcmp(command, "search-reverse") == 0) {
if (data->searchtype == WINDOW_COPY_SEARCHUP) { if (data->searchtype == WINDOW_COPY_SEARCHUP) {
for (; np != 0; np--) for (; np != 0; np--)
window_copy_search_down(wp, 1); window_copy_search_down(wp);
} else if (data->searchtype == WINDOW_COPY_SEARCHDOWN) { } else if (data->searchtype == WINDOW_COPY_SEARCHDOWN) {
for (; np != 0; np--) for (; np != 0; np--)
window_copy_search_up(wp, 1); window_copy_search_up(wp);
} }
} }
if (strcmp(command, "select-line") == 0) { if (strcmp(command, "select-line") == 0) {
@@ -841,27 +856,27 @@ window_copy_command(struct window_pane *wp, struct client *c, struct session *s,
data->jumptype = WINDOW_COPY_JUMPTOBACKWARD; data->jumptype = WINDOW_COPY_JUMPTOBACKWARD;
data->jumpchar = *argument; data->jumpchar = *argument;
for (; np != 0; np--) for (; np != 0; np--)
window_copy_cursor_jump_to_back(wp, 1); window_copy_cursor_jump_to_back(wp);
} }
if (strcmp(command, "jump-to-forward") == 0) { if (strcmp(command, "jump-to-forward") == 0) {
data->jumptype = WINDOW_COPY_JUMPTOFORWARD; data->jumptype = WINDOW_COPY_JUMPTOFORWARD;
data->jumpchar = *argument; data->jumpchar = *argument;
for (; np != 0; np--) for (; np != 0; np--)
window_copy_cursor_jump_to(wp, 1); window_copy_cursor_jump_to(wp);
} }
if (strcmp(command, "search-backward") == 0) { if (strcmp(command, "search-backward") == 0) {
data->searchtype = WINDOW_COPY_SEARCHUP; data->searchtype = WINDOW_COPY_SEARCHUP;
free(data->searchstr); free(data->searchstr);
data->searchstr = xstrdup(argument); data->searchstr = xstrdup(argument);
for (; np != 0; np--) for (; np != 0; np--)
window_copy_search_up(wp, 1); window_copy_search_up(wp);
} }
if (strcmp(command, "search-forward") == 0) { if (strcmp(command, "search-forward") == 0) {
data->searchtype = WINDOW_COPY_SEARCHDOWN; data->searchtype = WINDOW_COPY_SEARCHDOWN;
free(data->searchstr); free(data->searchstr);
data->searchstr = xstrdup(argument); data->searchstr = xstrdup(argument);
for (; np != 0; np--) for (; np != 0; np--)
window_copy_search_down(wp, 1); window_copy_search_down(wp);
} }
if (strcmp(command, "search-backward-incremental") == 0) { if (strcmp(command, "search-backward-incremental") == 0) {
prefix = *argument++; prefix = *argument++;
@@ -883,7 +898,7 @@ window_copy_command(struct window_pane *wp, struct client *c, struct session *s,
data->searchtype = WINDOW_COPY_SEARCHUP; data->searchtype = WINDOW_COPY_SEARCHUP;
free(data->searchstr); free(data->searchstr);
data->searchstr = xstrdup(argument); data->searchstr = xstrdup(argument);
if (!window_copy_search_up(wp, 1)) { if (!window_copy_search_up(wp)) {
window_copy_clear_marks(wp); window_copy_clear_marks(wp);
redraw = 1; redraw = 1;
} }
@@ -891,7 +906,7 @@ window_copy_command(struct window_pane *wp, struct client *c, struct session *s,
data->searchtype = WINDOW_COPY_SEARCHDOWN; data->searchtype = WINDOW_COPY_SEARCHDOWN;
free(data->searchstr); free(data->searchstr);
data->searchstr = xstrdup(argument); data->searchstr = xstrdup(argument);
if (!window_copy_search_down(wp, 1)) { if (!window_copy_search_down(wp)) {
window_copy_clear_marks(wp); window_copy_clear_marks(wp);
redraw = 1; redraw = 1;
} }
@@ -917,7 +932,7 @@ window_copy_command(struct window_pane *wp, struct client *c, struct session *s,
data->searchtype = WINDOW_COPY_SEARCHDOWN; data->searchtype = WINDOW_COPY_SEARCHDOWN;
free(data->searchstr); free(data->searchstr);
data->searchstr = xstrdup(argument); data->searchstr = xstrdup(argument);
if (!window_copy_search_down(wp, 1)) { if (!window_copy_search_down(wp)) {
window_copy_clear_marks(wp); window_copy_clear_marks(wp);
redraw = 1; redraw = 1;
} }
@@ -925,7 +940,7 @@ window_copy_command(struct window_pane *wp, struct client *c, struct session *s,
data->searchtype = WINDOW_COPY_SEARCHUP; data->searchtype = WINDOW_COPY_SEARCHUP;
free(data->searchstr); free(data->searchstr);
data->searchstr = xstrdup(argument); data->searchstr = xstrdup(argument);
if (!window_copy_search_up(wp, 1)) { if (!window_copy_search_up(wp)) {
window_copy_clear_marks(wp); window_copy_clear_marks(wp);
redraw = 1; redraw = 1;
} }
@@ -955,18 +970,22 @@ window_copy_scroll_to(struct window_pane *wp, u_int px, u_int py)
data->cx = px; data->cx = px;
gap = gd->sy / 4; if (py >= gd->hsize - data->oy && py < gd->hsize - data->oy + gd->sy)
if (py < gd->sy) { data->cy = py - (gd->hsize - data->oy);
offset = 0; else {
data->cy = py; gap = gd->sy / 4;
} else if (py > gd->hsize + gd->sy - gap) { if (py < gd->sy) {
offset = gd->hsize; offset = 0;
data->cy = py - gd->hsize; data->cy = py;
} else { } else if (py > gd->hsize + gd->sy - gap) {
offset = py + gap - gd->sy; offset = gd->hsize;
data->cy = py - offset; data->cy = py - gd->hsize;
} else {
offset = py + gap - gd->sy;
data->cy = py - offset;
}
data->oy = gd->hsize - offset;
} }
data->oy = gd->hsize - offset;
window_copy_update_selection(wp, 1); window_copy_update_selection(wp, 1);
window_copy_redraw_screen(wp); window_copy_redraw_screen(wp);
@@ -1128,11 +1147,10 @@ window_copy_search_jump(struct window_pane *wp, struct grid *gd,
/* /*
* Search in for text searchstr. If direction is 0 then search up, otherwise * Search in for text searchstr. If direction is 0 then search up, otherwise
* down. If moveflag is 0 then look for string at the current cursor position * down.
* as well.
*/ */
static int static int
window_copy_search(struct window_pane *wp, int direction, int moveflag) window_copy_search(struct window_pane *wp, int direction)
{ {
struct window_copy_mode_data *data = wp->modedata; struct window_copy_mode_data *data = wp->modedata;
struct screen *s = data->backing, ss; struct screen *s = data->backing, ss;
@@ -1152,12 +1170,10 @@ window_copy_search(struct window_pane *wp, int direction, int moveflag)
screen_write_nputs(&ctx, -1, &grid_default_cell, "%s", data->searchstr); screen_write_nputs(&ctx, -1, &grid_default_cell, "%s", data->searchstr);
screen_write_stop(&ctx); screen_write_stop(&ctx);
if (moveflag) { if (direction)
if (direction) window_copy_move_right(s, &fx, &fy);
window_copy_move_right(s, &fx, &fy); else
else window_copy_move_left(s, &fx, &fy);
window_copy_move_left(s, &fx, &fy);
}
window_copy_clear_selection(wp); window_copy_clear_selection(wp);
wrapflag = options_get_number(wp->window->options, "wrap-search"); wrapflag = options_get_number(wp->window->options, "wrap-search");
@@ -1243,15 +1259,15 @@ window_copy_clear_marks(struct window_pane *wp)
} }
static int static int
window_copy_search_up(struct window_pane *wp, int moveflag) window_copy_search_up(struct window_pane *wp)
{ {
return (window_copy_search(wp, 0, moveflag)); return (window_copy_search(wp, 0));
} }
static int static int
window_copy_search_down(struct window_pane *wp, int moveflag) window_copy_search_down(struct window_pane *wp)
{ {
return (window_copy_search(wp, 1, moveflag)); return (window_copy_search(wp, 1));
} }
static void static void
@@ -1661,7 +1677,7 @@ window_copy_copy_pipe(struct window_pane *wp, struct session *s,
return; return;
expanded = format_single(NULL, arg, NULL, s, NULL, wp); expanded = format_single(NULL, arg, NULL, s, NULL, wp);
job = job_run(expanded, s, NULL, NULL, NULL, NULL, NULL); job = job_run(expanded, s, NULL, NULL, NULL, NULL, NULL, JOB_NOWAIT);
bufferevent_write(job->event, buf, len); bufferevent_write(job->event, buf, len);
free(expanded); free(expanded);
@@ -2166,14 +2182,14 @@ window_copy_cursor_jump_back(struct window_pane *wp)
} }
static void static void
window_copy_cursor_jump_to(struct window_pane *wp, int jump_again) window_copy_cursor_jump_to(struct window_pane *wp)
{ {
struct window_copy_mode_data *data = wp->modedata; struct window_copy_mode_data *data = wp->modedata;
struct screen *back_s = data->backing; struct screen *back_s = data->backing;
struct grid_cell gc; struct grid_cell gc;
u_int px, py, xx; u_int px, py, xx;
px = data->cx + 1 + jump_again; px = data->cx + 2;
py = screen_hsize(back_s) + data->cy - data->oy; py = screen_hsize(back_s) + data->cy - data->oy;
xx = window_copy_find_length(wp, py); xx = window_copy_find_length(wp, py);
@@ -2191,7 +2207,7 @@ window_copy_cursor_jump_to(struct window_pane *wp, int jump_again)
} }
static void static void
window_copy_cursor_jump_to_back(struct window_pane *wp, int jump_again) window_copy_cursor_jump_to_back(struct window_pane *wp)
{ {
struct window_copy_mode_data *data = wp->modedata; struct window_copy_mode_data *data = wp->modedata;
struct screen *back_s = data->backing; struct screen *back_s = data->backing;
@@ -2204,7 +2220,7 @@ window_copy_cursor_jump_to_back(struct window_pane *wp, int jump_again)
if (px > 0) if (px > 0)
px--; px--;
if (jump_again && px > 0) if (px > 0)
px--; px--;
for (;;) { for (;;) {

View File

@@ -43,8 +43,10 @@ static void window_tree_key(struct window_pane *,
"#{?#{==:#{window_panes},1}, \"#{pane_title}\",}" \ "#{?#{==:#{window_panes},1}, \"#{pane_title}\",}" \
"," \ "," \
"#{session_windows} windows" \ "#{session_windows} windows" \
"#{?session_grouped, (group ,}" \ "#{?session_grouped, " \
"#{session_group}#{?session_grouped,),}" \ "(group #{session_group}: " \
"#{session_group_list})," \
"}" \
"#{?session_attached, (attached),}" \ "#{?session_attached, (attached),}" \
"}" \ "}" \
"}" "}"
@@ -91,17 +93,23 @@ struct window_tree_modedata {
struct mode_tree_data *data; struct mode_tree_data *data;
char *format; char *format;
char *command; char *command;
int squash_groups;
struct window_tree_itemdata **item_list; struct window_tree_itemdata **item_list;
u_int item_size; u_int item_size;
struct client *client;
const char *entered; const char *entered;
struct cmd_find_state fs; struct cmd_find_state fs;
enum window_tree_type type; enum window_tree_type type;
int offset; int offset;
int left;
int right;
u_int start;
u_int end;
u_int each;
}; };
static void static void
@@ -288,7 +296,8 @@ window_tree_build_window(struct session *s, struct winlink *wl, void* modedata,
free(text); free(text);
free(name); free(name);
wp = TAILQ_FIRST(&wl->window->panes); if ((wp = TAILQ_FIRST(&wl->window->panes)) == NULL)
goto empty;
if (TAILQ_NEXT(wp, entry) == NULL) { if (TAILQ_NEXT(wp, entry) == NULL) {
if (!window_tree_filter_pane(s, wl, wp, filter)) if (!window_tree_filter_pane(s, wl, wp, filter))
goto empty; goto empty;
@@ -395,8 +404,11 @@ window_tree_build(void *modedata, u_int sort_type, uint64_t *tag,
{ {
struct window_tree_modedata *data = modedata; struct window_tree_modedata *data = modedata;
struct session *s, **l; struct session *s, **l;
struct session_group *sg, *current;
u_int n, i; u_int n, i;
current = session_group_contains(data->fs.s);
for (i = 0; i < data->item_size; i++) for (i = 0; i < data->item_size; i++)
window_tree_free_item(data->item_list[i]); window_tree_free_item(data->item_list[i]);
free(data->item_list); free(data->item_list);
@@ -406,6 +418,12 @@ window_tree_build(void *modedata, u_int sort_type, uint64_t *tag,
l = NULL; l = NULL;
n = 0; n = 0;
RB_FOREACH(s, sessions, &sessions) { RB_FOREACH(s, sessions, &sessions) {
if (data->squash_groups &&
(sg = session_group_contains(s)) != NULL) {
if ((sg == current && s != data->fs.s) ||
(sg != current && s != TAILQ_FIRST(&sg->sessions)))
continue;
}
l = xreallocarray(l, n + 1, sizeof *l); l = xreallocarray(l, n + 1, sizeof *l);
l[n++] = s; l[n++] = s;
} }
@@ -434,11 +452,36 @@ window_tree_build(void *modedata, u_int sort_type, uint64_t *tag,
*tag = (uint64_t)data->fs.wl; *tag = (uint64_t)data->fs.wl;
break; break;
case WINDOW_TREE_PANE: case WINDOW_TREE_PANE:
*tag = (uint64_t)data->fs.wp; if (window_count_panes(data->fs.wl->window) == 1)
*tag = (uint64_t)data->fs.wl;
else
*tag = (uint64_t)data->fs.wp;
break; break;
} }
} }
static void
window_tree_draw_label(struct screen_write_ctx *ctx, u_int px, u_int py,
u_int sx, u_int sy, const struct grid_cell *gc, const char *label)
{
size_t len;
u_int ox, oy;
len = strlen(label);
if (sx == 0 || sy == 1 || len > sx)
return;
ox = (sx - len + 1) / 2;
oy = (sy + 1) / 2;
if (ox > 1 && ox + len < sx - 1 && sy >= 3) {
screen_write_cursormove(ctx, px + ox - 1, py + oy - 1);
screen_write_box(ctx, len + 2, 3);
}
screen_write_cursormove(ctx, px + ox, py + oy);
screen_write_puts(ctx, gc, "%s", label);
}
static void static void
window_tree_draw_session(struct window_tree_modedata *data, struct session *s, window_tree_draw_session(struct window_tree_modedata *data, struct session *s,
struct screen_write_ctx *ctx, u_int sx, u_int sy) struct screen_write_ctx *ctx, u_int sx, u_int sy)
@@ -446,12 +489,12 @@ window_tree_draw_session(struct window_tree_modedata *data, struct session *s,
struct options *oo = s->options; struct options *oo = s->options;
struct winlink *wl; struct winlink *wl;
struct window *w; struct window *w;
u_int cx = ctx->s->cx, cy = ctx->s->cy;
u_int loop, total, visible, each, width, offset; u_int loop, total, visible, each, width, offset;
u_int current, start, end, remaining, i; u_int current, start, end, remaining, i;
struct grid_cell gc; struct grid_cell gc;
int colour, active_colour, left, right; int colour, active_colour, left, right;
char *label; char *label;
size_t len;
total = winlink_count(&s->windows); total = winlink_count(&s->windows);
@@ -509,17 +552,25 @@ window_tree_draw_session(struct window_tree_modedata *data, struct session *s,
return; return;
if (left) { if (left) {
screen_write_cursormove(ctx, 2, 0); data->left = cx + 2;
screen_write_cursormove(ctx, cx + 2, cy);
screen_write_vline(ctx, sy, 0, 0); screen_write_vline(ctx, sy, 0, 0);
screen_write_cursormove(ctx, 0, sy / 2); screen_write_cursormove(ctx, cx, cy + sy / 2);
screen_write_puts(ctx, &grid_default_cell, "<"); screen_write_puts(ctx, &grid_default_cell, "<");
} } else
data->left = -1;
if (right) { if (right) {
screen_write_cursormove(ctx, sx - 3, 0); data->right = cx + sx - 3;
screen_write_cursormove(ctx, cx + sx - 3, cy);
screen_write_vline(ctx, sy, 0, 0); screen_write_vline(ctx, sy, 0, 0);
screen_write_cursormove(ctx, sx - 1, sy / 2); screen_write_cursormove(ctx, cx + sx - 1, cy + sy / 2);
screen_write_puts(ctx, &grid_default_cell, ">"); screen_write_puts(ctx, &grid_default_cell, ">");
} } else
data->right = -1;
data->start = start;
data->end = end;
data->each = each;
i = loop = 0; i = loop = 0;
RB_FOREACH(wl, winlinks, &s->windows) { RB_FOREACH(wl, winlinks, &s->windows) {
@@ -545,20 +596,18 @@ window_tree_draw_session(struct window_tree_modedata *data, struct session *s,
else else
width = each - 1; width = each - 1;
screen_write_cursormove(ctx, offset, 0); screen_write_cursormove(ctx, cx + offset, cy);
screen_write_preview(ctx, &w->active->base, width, sy); screen_write_preview(ctx, &w->active->base, width, sy);
xasprintf(&label, " %u:%s ", wl->idx, w->name); xasprintf(&label, " %u:%s ", wl->idx, w->name);
if (strlen(label) > width) if (strlen(label) > width)
xasprintf(&label, " %u ", wl->idx); xasprintf(&label, " %u ", wl->idx);
len = strlen(label) / 2; window_tree_draw_label(ctx, cx + offset, cy, width, sy, &gc,
screen_write_cursormove(ctx, offset + (each / 2) - len, sy / 2); label);
if (len < width)
screen_write_puts(ctx, &gc, "%s", label);
free(label); free(label);
if (loop != end - 1) { if (loop != end - 1) {
screen_write_cursormove(ctx, offset + width, 0); screen_write_cursormove(ctx, cx + offset + width, cy);
screen_write_vline(ctx, sy, 0, 0); screen_write_vline(ctx, sy, 0, 0);
} }
loop++; loop++;
@@ -573,12 +622,12 @@ window_tree_draw_window(struct window_tree_modedata *data, struct session *s,
{ {
struct options *oo = s->options; struct options *oo = s->options;
struct window_pane *wp; struct window_pane *wp;
u_int cx = ctx->s->cx, cy = ctx->s->cy;
u_int loop, total, visible, each, width, offset; u_int loop, total, visible, each, width, offset;
u_int current, start, end, remaining, i; u_int current, start, end, remaining, i;
struct grid_cell gc; struct grid_cell gc;
int colour, active_colour, left, right; int colour, active_colour, left, right, pane_idx;
char *label; char *label;
size_t len;
total = window_count_panes(w); total = window_count_panes(w);
@@ -636,17 +685,25 @@ window_tree_draw_window(struct window_tree_modedata *data, struct session *s,
return; return;
if (left) { if (left) {
screen_write_cursormove(ctx, 2, 0); data->left = cx + 2;
screen_write_cursormove(ctx, cx + 2, cy);
screen_write_vline(ctx, sy, 0, 0); screen_write_vline(ctx, sy, 0, 0);
screen_write_cursormove(ctx, 0, sy / 2); screen_write_cursormove(ctx, cx, cy + sy / 2);
screen_write_puts(ctx, &grid_default_cell, "<"); screen_write_puts(ctx, &grid_default_cell, "<");
} } else
data->left = -1;
if (right) { if (right) {
screen_write_cursormove(ctx, sx - 3, 0); data->right = cx + sx - 3;
screen_write_cursormove(ctx, cx + sx - 3, cy);
screen_write_vline(ctx, sy, 0, 0); screen_write_vline(ctx, sy, 0, 0);
screen_write_cursormove(ctx, sx - 1, sy / 2); screen_write_cursormove(ctx, cx + sx - 1, cy + sy / 2);
screen_write_puts(ctx, &grid_default_cell, ">"); screen_write_puts(ctx, &grid_default_cell, ">");
} } else
data->right = -1;
data->start = start;
data->end = end;
data->each = each;
i = loop = 0; i = loop = 0;
TAILQ_FOREACH(wp, &w->panes, entry) { TAILQ_FOREACH(wp, &w->panes, entry) {
@@ -671,18 +728,18 @@ window_tree_draw_window(struct window_tree_modedata *data, struct session *s,
else else
width = each - 1; width = each - 1;
screen_write_cursormove(ctx, offset, 0); screen_write_cursormove(ctx, cx + offset, cy);
screen_write_preview(ctx, &wp->base, width, sy); screen_write_preview(ctx, &wp->base, width, sy);
xasprintf(&label, " %u ", loop); if (window_pane_index(wp, &pane_idx) != 0)
len = strlen(label) / 2; pane_idx = loop;
screen_write_cursormove(ctx, offset + (each / 2) - len, sy / 2); xasprintf(&label, " %u ", pane_idx);
if (len < width) window_tree_draw_label(ctx, cx + offset, cy, each, sy, &gc,
screen_write_puts(ctx, &gc, "%s", label); label);
free(label); free(label);
if (loop != end - 1) { if (loop != end - 1) {
screen_write_cursormove(ctx, offset + width, 0); screen_write_cursormove(ctx, cx + offset + width, cy);
screen_write_vline(ctx, sy, 0, 0); screen_write_vline(ctx, sy, 0, 0);
} }
loop++; loop++;
@@ -691,39 +748,32 @@ window_tree_draw_window(struct window_tree_modedata *data, struct session *s,
} }
} }
static struct screen * static void
window_tree_draw(void *modedata, void *itemdata, u_int sx, u_int sy) window_tree_draw(void *modedata, void *itemdata, struct screen_write_ctx *ctx,
u_int sx, u_int sy)
{ {
struct window_tree_itemdata *item = itemdata; struct window_tree_itemdata *item = itemdata;
struct session *sp; struct session *sp;
struct winlink *wlp; struct winlink *wlp;
struct window_pane *wp; struct window_pane *wp;
static struct screen s;
struct screen_write_ctx ctx;
window_tree_pull_item(item, &sp, &wlp, &wp); window_tree_pull_item(item, &sp, &wlp, &wp);
if (wp == NULL) if (wp == NULL)
return (NULL); return;
screen_init(&s, sx, sy, 0);
screen_write_start(&ctx, NULL, &s);
switch (item->type) { switch (item->type) {
case WINDOW_TREE_NONE: case WINDOW_TREE_NONE:
return (0); break;
case WINDOW_TREE_SESSION: case WINDOW_TREE_SESSION:
window_tree_draw_session(modedata, sp, &ctx, sx, sy); window_tree_draw_session(modedata, sp, ctx, sx, sy);
break; break;
case WINDOW_TREE_WINDOW: case WINDOW_TREE_WINDOW:
window_tree_draw_window(modedata, sp, wlp->window, &ctx, sx, sy); window_tree_draw_window(modedata, sp, wlp->window, ctx, sx, sy);
break; break;
case WINDOW_TREE_PANE: case WINDOW_TREE_PANE:
screen_write_preview(&ctx, &wp->base, sx, sy); screen_write_preview(ctx, &wp->base, sx, sy);
break; break;
} }
screen_write_stop(&ctx);
return (&s);
} }
static int static int
@@ -787,10 +837,12 @@ window_tree_init(struct window_pane *wp, struct cmd_find_state *fs,
data->command = xstrdup(WINDOW_TREE_DEFAULT_COMMAND); data->command = xstrdup(WINDOW_TREE_DEFAULT_COMMAND);
else else
data->command = xstrdup(args->argv[0]); data->command = xstrdup(args->argv[0]);
data->squash_groups = !args_has(args, 'G');
data->data = mode_tree_start(wp, args, window_tree_build, data->data = mode_tree_start(wp, args, window_tree_build,
window_tree_draw, window_tree_search, data, window_tree_sort_list, window_tree_draw, window_tree_search, data, window_tree_sort_list,
nitems(window_tree_sort_list), &s); nitems(window_tree_sort_list), &s);
mode_tree_zoom(data->data, args);
mode_tree_build(data->data); mode_tree_build(data->data);
mode_tree_draw(data->data); mode_tree_draw(data->data);
@@ -879,7 +931,8 @@ window_tree_get_target(struct window_tree_itemdata *item,
} }
static void static void
window_tree_command_each(void* modedata, void* itemdata, __unused key_code key) window_tree_command_each(void* modedata, void* itemdata, struct client *c,
__unused key_code key)
{ {
struct window_tree_modedata *data = modedata; struct window_tree_modedata *data = modedata;
struct window_tree_itemdata *item = itemdata; struct window_tree_itemdata *item = itemdata;
@@ -888,7 +941,7 @@ window_tree_command_each(void* modedata, void* itemdata, __unused key_code key)
name = window_tree_get_target(item, &fs); name = window_tree_get_target(item, &fs);
if (name != NULL) if (name != NULL)
mode_tree_run_command(data->client, &fs, data->entered, name); mode_tree_run_command(c, &fs, data->entered, name);
free(name); free(name);
} }
@@ -912,16 +965,12 @@ window_tree_command_callback(struct client *c, void *modedata, const char *s,
{ {
struct window_tree_modedata *data = modedata; struct window_tree_modedata *data = modedata;
if (data->dead) if (s == NULL || data->dead)
return (0); return (0);
data->client = c;
data->entered = s; data->entered = s;
mode_tree_each_tagged(data->data, window_tree_command_each, c,
mode_tree_each_tagged(data->data, window_tree_command_each, KEYC_NONE, KEYC_NONE, 1);
1);
data->client = NULL;
data->entered = NULL; data->entered = NULL;
data->references++; data->references++;
@@ -938,21 +987,86 @@ window_tree_command_free(void *modedata)
window_tree_destroy(data); window_tree_destroy(data);
} }
static key_code
window_tree_mouse(struct window_tree_modedata *data, key_code key, u_int x,
struct window_tree_itemdata *item)
{
struct session *s;
struct winlink *wl;
struct window_pane *wp;
u_int loop;
if (key != KEYC_MOUSEDOWN1_PANE)
return (KEYC_NONE);
if (data->left != -1 && x <= (u_int)data->left)
return ('<');
if (data->right != -1 && x >= (u_int)data->right)
return ('>');
if (data->left != -1)
x -= data->left;
else if (x != 0)
x--;
if (x == 0 || data->end == 0)
x = 0;
else {
x = x / data->each;
if (data->start + x >= data->end)
x = data->end - 1;
}
window_tree_pull_item(item, &s, &wl, &wp);
if (item->type == WINDOW_TREE_SESSION) {
if (s == NULL)
return (KEYC_NONE);
mode_tree_expand_current(data->data);
loop = 0;
RB_FOREACH(wl, winlinks, &s->windows) {
if (loop == data->start + x)
break;
loop++;
}
if (wl != NULL)
mode_tree_set_current(data->data, (uint64_t)wl);
return ('\r');
}
if (item->type == WINDOW_TREE_WINDOW) {
if (wl == NULL)
return (KEYC_NONE);
mode_tree_expand_current(data->data);
loop = 0;
TAILQ_FOREACH(wp, &wl->window->panes, entry) {
if (loop == data->start + x)
break;
loop++;
}
if (wp != NULL)
mode_tree_set_current(data->data, (uint64_t)wp);
return ('\r');
}
return (KEYC_NONE);
}
static void static void
window_tree_key(struct window_pane *wp, struct client *c, window_tree_key(struct window_pane *wp, struct client *c,
__unused struct session *s, key_code key, struct mouse_event *m) __unused struct session *s, key_code key, struct mouse_event *m)
{ {
struct window_tree_modedata *data = wp->modedata; struct window_tree_modedata *data = wp->modedata;
struct window_tree_itemdata *item; struct window_tree_itemdata *item, *new_item;
char *command, *name, *prompt; char *name, *prompt;
struct cmd_find_state fs; struct cmd_find_state fs;
int finished; int finished;
u_int tagged; u_int tagged, x, y;
item = mode_tree_get_current(data->data); item = mode_tree_get_current(data->data);
finished = mode_tree_key(data->data, c, &key, m); finished = mode_tree_key(data->data, c, &key, m, &x, &y);
if (item != mode_tree_get_current(data->data)) if (item != (new_item = mode_tree_get_current(data->data))) {
item = new_item;
data->offset = 0; data->offset = 0;
}
if (KEYC_IS_MOUSE(key))
key = window_tree_mouse(data, key, x, item);
switch (key) { switch (key) {
case '<': case '<':
data->offset--; data->offset--;
@@ -973,14 +1087,12 @@ window_tree_key(struct window_pane *wp, struct client *c,
break; break;
case '\r': case '\r':
item = mode_tree_get_current(data->data); item = mode_tree_get_current(data->data);
command = xstrdup(data->command);
name = window_tree_get_target(item, &fs); name = window_tree_get_target(item, &fs);
window_pane_reset_mode(wp);
if (name != NULL) if (name != NULL)
mode_tree_run_command(c, NULL, command, name); mode_tree_run_command(c, NULL, data->command, name);
finished = 1;
free(name); free(name);
free(command); break;
return;
} }
if (finished) if (finished)
window_pane_reset_mode(wp); window_pane_reset_mode(wp);

View File

@@ -339,7 +339,7 @@ window_create_spawn(const char *name, int argc, char **argv, const char *path,
struct window_pane *wp; struct window_pane *wp;
w = window_create(sx, sy); w = window_create(sx, sy);
wp = window_add_pane(w, NULL, 0, hlimit); wp = window_add_pane(w, NULL, 0, 0, hlimit);
layout_init(w, wp); layout_init(w, wp);
if (window_pane_spawn(wp, argc, argv, path, shell, cwd, if (window_pane_spawn(wp, argc, argv, path, shell, cwd,
@@ -608,7 +608,7 @@ window_unzoom(struct window *w)
struct window_pane * struct window_pane *
window_add_pane(struct window *w, struct window_pane *other, int before, window_add_pane(struct window *w, struct window_pane *other, int before,
u_int hlimit) int full_size, u_int hlimit)
{ {
struct window_pane *wp; struct window_pane *wp;
@@ -621,10 +621,16 @@ window_add_pane(struct window *w, struct window_pane *other, int before,
TAILQ_INSERT_HEAD(&w->panes, wp, entry); TAILQ_INSERT_HEAD(&w->panes, wp, entry);
} else if (before) { } else if (before) {
log_debug("%s: @%u before %%%u", __func__, w->id, wp->id); log_debug("%s: @%u before %%%u", __func__, w->id, wp->id);
TAILQ_INSERT_BEFORE(other, wp, entry); if (full_size)
TAILQ_INSERT_HEAD(&w->panes, wp, entry);
else
TAILQ_INSERT_BEFORE(other, wp, entry);
} else { } else {
log_debug("%s: @%u after %%%u", __func__, w->id, wp->id); log_debug("%s: @%u after %%%u", __func__, w->id, wp->id);
TAILQ_INSERT_AFTER(&w->panes, other, wp, entry); if (full_size)
TAILQ_INSERT_TAIL(&w->panes, wp, entry);
else
TAILQ_INSERT_AFTER(&w->panes, other, wp, entry);
} }
return (wp); return (wp);
} }
@@ -912,6 +918,7 @@ window_pane_spawn(struct window_pane *wp, int argc, char **argv,
free((void *)wp->cwd); free((void *)wp->cwd);
wp->cwd = xstrdup(cwd); wp->cwd = xstrdup(cwd);
} }
wp->flags &= ~(PANE_STATUSREADY|PANE_STATUSDRAWN);
cmd = cmd_stringify_argv(wp->argc, wp->argv); cmd = cmd_stringify_argv(wp->argc, wp->argv);
log_debug("spawn: %s -- %s", wp->shell, cmd); log_debug("spawn: %s -- %s", wp->shell, cmd);
@@ -938,10 +945,13 @@ window_pane_spawn(struct window_pane *wp, int argc, char **argv,
proc_clear_signals(server_proc, 1); proc_clear_signals(server_proc, 1);
sigprocmask(SIG_SETMASK, &oldset, NULL); sigprocmask(SIG_SETMASK, &oldset, NULL);
if (chdir(wp->cwd) != 0) { cwd = NULL;
if ((home = find_home()) == NULL || chdir(home) != 0) if (chdir(wp->cwd) == 0)
chdir("/"); cwd = wp->cwd;
} else if ((home = find_home()) != NULL && chdir(home) == 0)
cwd = home;
else
chdir("/");
if (tcgetattr(STDIN_FILENO, &tio2) != 0) if (tcgetattr(STDIN_FILENO, &tio2) != 0)
fatal("tcgetattr failed"); fatal("tcgetattr failed");
@@ -959,6 +969,8 @@ window_pane_spawn(struct window_pane *wp, int argc, char **argv,
if (path != NULL) if (path != NULL)
environ_set(env, "PATH", "%s", path); environ_set(env, "PATH", "%s", path);
if (cwd != NULL)
environ_set(env, "PWD", "%s", cwd);
environ_set(env, "TMUX_PANE", "%%%u", wp->id); environ_set(env, "TMUX_PANE", "%%%u", wp->id);
environ_push(env); environ_push(env);