309 Commits
2.9a ... 3.0

Author SHA1 Message Date
Nicholas Marriott
c22470bd14 3.0-rc3. 2019-06-14 16:17:34 +01:00
Nicholas Marriott
31cb95893e #{ is no longer special. 2019-06-14 16:10:27 +01:00
nicm
52b8274285 Do not crash if the environment variable is present but empty. 2019-06-14 16:06:12 +01:00
nicm
a924694820 Use the right client when working out where to save or load the buffer,
reported by kn@.
2019-06-14 16:04:57 +01:00
nicm
0a94dbe051 A couple of minor parser changes around conditions: 1) only treat #{
specially after a condition, otherwise as a comment (which is more as
most people expect) 2) allow formats to be quoted after a condition.
2019-06-14 16:04:52 +01:00
Nicholas Marriott
ebc9dcb3bb Add a bit to {}. 2019-06-14 12:40:35 +01:00
Nicholas Marriott
f31847db62 Improve description of #{. 2019-06-14 10:33:55 +01:00
nicm
e337c1ba7d Do not try to parse command when unsetting, GitHub issue 1788. 2019-06-11 15:51:30 +01:00
nicm
db5a7c1740 Need to increment the argument to skip the prefix earlier, fixes
repeated incremental search in copy mode, reported by Kaushal Modi in
GitHub issue 1780.
2019-06-05 21:06:06 +01:00
Nicholas Marriott
53914e50b9 RC2. 2019-06-05 15:09:42 +01:00
Nicholas Marriott
e252984993 If only one of -x or -y is given, use the calculated size for the
other. Also fix some warnings. Pointed out by Ben Boeckel.
2019-06-05 06:44:08 +01:00
nicm
17bc11bd15 yacc(1) copies its union so it is not a good place to store
TAILQ_HEADs. Allocate them instead. Found from a problem reported by
sthen@.
2019-06-02 10:52:32 +01:00
nicm
1a3a973bd0 Allow % strings that are all numbers or %s, and fix a double free. Both
reported by George Nachman, GitHub issues 1765 and 1766.
2019-05-31 15:18:25 +01:00
nicm
e4eec92852 No longer need to reduce line number by one. 2019-05-30 13:12:35 +01:00
nicm
75d112c484 The line number needs to be updated only after the \n is processed by
the parser, so store a flag and update it next time around. Also each
new line needs its own shared data.
2019-05-29 23:04:22 +01:00
Nicholas Marriott
a05c3a7aa6 Add command order test. 2019-05-29 20:43:11 +01:00
Nicholas Marriott
47795d1695 Update .travis.yml from master. 2019-05-29 14:40:41 +01:00
Nicholas Marriott
7cdb1cfc8d as -> and. 2019-05-29 12:13:46 +01:00
Nicholas Marriott
8827b7f9a6 Tweak {} text. 2019-05-29 11:25:07 +01:00
nicm
7eeb479705 Do not read past the end of the argument string if it is empty. 2019-05-28 21:04:41 +01:00
nicm
8d137233a9 Redraw status line if size changes, GitHub issue 1762. Also fix length
of target buffer when pasting into status line.
2019-05-28 21:04:35 +01:00
Nicholas Marriott
05d07413ff 3.0-rc. 2019-05-28 15:46:17 +01:00
Thomas Adam
b8360504f3 Merge branch 'obsd-master' 2019-05-28 15:02:26 +01:00
Nicholas Marriott
a062650d4b Tweak menu option. 2019-05-28 14:49:50 +01:00
Nicholas Marriott
f012db9be9 Add regress for conf files. 2019-05-28 13:21:19 +01:00
nicm
299d4f3aaa Exit 1 correctly if source-file fails. 2019-05-28 12:20:28 +00:00
Thomas Adam
eba6cf61c9 Merge branch 'obsd-master' 2019-05-28 13:02:27 +01:00
nicm
0ec410689c Allow source-file to take multiple arguments. 2019-05-28 11:46:30 +00:00
nicm
e0fd295054 Change the default right click pane to open the menu if not in a mode
and no application mouse.
2019-05-28 10:27:11 +00:00
nicm
99a8469ee4 Add key bindings to open the window and pane menus (C-m and M-m for now). 2019-05-28 10:05:24 +00:00
nicm
12255411f2 Allow menu items to be disabled by putting a - at the start of their
name, rather than just including #[dim] which still allowed them to be
chosen.
2019-05-28 09:50:54 +00:00
Thomas Adam
c0116b2c5b Merge branch 'obsd-master' 2019-05-28 09:02:26 +01:00
nicm
799a154b91 Change display-menu from taking a single string to a set of arguments,
which is much easier to work with. Based on a diff from Avi Halachmi.
2019-05-28 07:18:42 +00:00
Thomas Adam
793f4d89d6 Merge branch 'obsd-master' 2019-05-27 19:02:25 +01:00
nicm
90cd045cf3 Clarify newlines inside {} a little. 2019-05-27 16:22:32 +00:00
nicm
94f6488f0e Go less crazy with horizontal separators on default menus. 2019-05-27 15:29:46 +00:00
Thomas Adam
d4bf4bd7c7 Merge branch 'obsd-master' 2019-05-27 15:02:28 +01:00
Nicholas Marriott
522d1bd309 Add. 2019-05-27 14:39:44 +01:00
Nicholas Marriott
a21de4c483 Mention <><=>=. 2019-05-27 14:39:06 +01:00
Nicholas Marriott
714311a696 Add {}. 2019-05-27 14:37:47 +01:00
Nicholas Marriott
9f0904ce6f Remove unused fparseln compat code. 2019-05-27 14:28:05 +01:00
nicm
ae3eba6e08 Fix crash when killing the current window, reported by Jesus Rafael
Sanchez in GitHub issue 1760.
2019-05-27 12:48:52 +00:00
nicm
6b332127ca Add an additional {} syntax for defining strings in the configuration
file, making it much tidier to define commands that contain other tmux
or shell commands (like if-shell). Also tweak bind-key to expect a
string if it is only given one argument, so {} can be used with it as
well. From Avi Halachmi.
2019-05-27 12:16:27 +00:00
Thomas Adam
bd40d704e2 Merge branch 'obsd-master' 2019-05-27 09:02:26 +01:00
nicm
65e5e14561 Fix the intended ordering of items in buffer mode - it should not always
be tag 0 when the tree is empty. GitHub issue 1759.
2019-05-27 06:50:04 +00:00
Thomas Adam
103e44d936 Merge branch 'obsd-master' 2019-05-26 21:02:26 +01:00
nicm
097973e3d5 Add keys for new menu items. 2019-05-26 18:43:43 +00:00
nicm
5fef946df4 Always redraw overlay if it is on (so status line doesn't redraw over it). 2019-05-26 18:27:52 +00:00
nicm
023c2c5392 Do not accept choice unless mouse has actually moved before. 2019-05-26 18:19:52 +00:00
nicm
e90d4a6021 Add formats for word and line under the mouse and use them to add some
items to the pane menu.
2019-05-26 17:34:45 +00:00
Thomas Adam
2e84d1cf03 Merge branch 'obsd-master' 2019-05-26 15:02:25 +01:00
nicm
6431005169 Add a way to append or prepend to a format if the length has been limited. 2019-05-26 12:02:42 +00:00
Thomas Adam
9beb3eb593 Merge branch 'obsd-master' 2019-05-26 13:02:26 +01:00
nicm
6dee409981 Some other platforms doesn't support fmemopen(3) (not unexpectedly), so
don't use it - since we only use getc/ungetc on the file anyway it is
easy not to.
2019-05-26 10:08:50 +00:00
Thomas Adam
f3fc81b178 Merge branch 'master' of github.com:ThomasAdam/tmux 2019-05-25 18:50:05 +01:00
Thomas Adam
463bd8abb9 Merge branch 'obsd-master' 2019-05-25 18:41:51 +01:00
nicm
a65a6d62d1 Add <, >, <=, >= for formats, GitHub issue 1747. 2019-05-25 16:51:10 +00:00
nicm
207789dc2d Client name can actually be NULL, so use address in that case. 2019-05-25 10:46:55 +00:00
nicm
d7586d3d65 Use client name when logging command queue. 2019-05-25 10:44:09 +00:00
nicm
f8d3d247d8 Merge cmd_list_parse into cmd-parse.y so it can use the new alias
processing code.
2019-05-25 07:18:20 +00:00
nicm
6b0fa14470 Fix error handling in if-shell. 2019-05-25 07:15:53 +00:00
Nicholas Marriott
a69211aff5 Fix up regress test. 2019-05-25 08:09:23 +01:00
nicm
930245d7ff Make cmd_log_argv take a printf-like format for the prefix. 2019-05-25 06:58:10 +00:00
espie
0dc8b7d5d8 unbreak build, okay nicm@ 2019-05-23 21:36:42 +00:00
Thomas Adam
19a3a9ee20 Merge branch 'obsd-master' 2019-05-23 21:02:30 +01:00
nicm
f3e01ecc42 Fix line numbers - commands are added after the line ends so they need to
get line - 1.
2019-05-23 18:39:00 +00:00
nicm
6c260af56d Use the same argument escaping code for options as well. 2019-05-23 18:33:53 +00:00
Nicholas Marriott
e817821104 Mention \. 2019-05-23 19:27:41 +01:00
nicm
f006116bac Environment variables can start with { also. 2019-05-23 18:22:13 +00:00
Thomas Adam
43431e7e84 Merge branch 'obsd-master' 2019-05-23 17:02:25 +01:00
nicm
eb8b51effc Fix drawing of status-right when it is aligned to the centre, GitHub
issue 1754.
2019-05-23 14:44:33 +00:00
Nicholas Marriott
389cf63cbc Tweak text. 2019-05-23 15:18:20 +01:00
Thomas Adam
2148fe33cd CHANGES: remove note to packagers 2019-05-23 15:16:10 +01:00
Thomas Adam
1a6540fea0 CHANGES: remove note to packagers 2019-05-23 15:15:42 +01:00
nicm
7d702f3cef Don't remove group items for group 0 (no group). 2019-05-23 15:15:42 +01:00
nicm
d0c462f718 Fix length calculation for pasting UTF-8 characters in the status line,
GitHub issue 1753.
2019-05-23 15:15:42 +01:00
Thomas Adam
238d2aa870 CHANGES: mention yacc 2019-05-23 15:12:24 +01:00
Thomas Adam
6bb505eb73 CHANGES: mention yacc 2019-05-23 15:11:25 +01:00
Nicholas Marriott
83b9807370 Remove duplicate. 2019-05-23 15:11:25 +01:00
Nicholas Marriott
3e6e533779 Add yacc(1) bits. 2019-05-23 15:11:25 +01:00
nicm
27bfb56ad5 Break the argument escaping code into a separate function and use it to
escape key bindings in list-keys. Also escape ~ and ; and $ properly.
2019-05-23 14:03:44 +00:00
Thomas Adam
c49f2a0365 Merge branch 'obsd-master' 2019-05-23 15:02:28 +01:00
nicm
3e3eb1dd0f Don't remove group items for group 0 (no group). 2019-05-23 13:08:43 +00:00
nicm
a4fe7e81c8 Fix length calculation for pasting UTF-8 characters in the status line,
GitHub issue 1753.
2019-05-23 12:47:52 +00:00
Nicholas Marriott
4ce26b0393 Remove duplicate. 2019-05-23 13:32:18 +01:00
Thomas Adam
82bf0f4d48 configure.ac: add AC_PROC_YACC 2019-05-23 13:12:03 +01:00
Thomas Adam
8590ee65e6 TRAVIS: add bison to build deps 2019-05-23 13:12:03 +01:00
Thomas Adam
d67b99c7e4 configure.ac: add AC_PROC_YACC 2019-05-23 13:09:34 +01:00
Thomas Adam
092c7bfeb8 TRAVIS: add bison to build deps 2019-05-23 13:06:47 +01:00
Nicholas Marriott
bf2cf33fc6 Add yacc(1) bits. 2019-05-23 13:04:41 +01:00
Thomas Adam
75aeb733f2 Merge branch 'obsd-master' 2019-05-23 13:02:27 +01:00
nicm
723010ba72 Replace the split parser code (cfg.c and cmd-string.c) with a single
parser using yacc(1). This is a major change but is clearer and simpler
and allows some edge cases to be made more consistent, as well as
tidying up how aliases are handled. It will also allow some further
improvements later.

Entirely the same parser is now used for parsing the configuration file
and for string commands. This means that constructs previously only
available in .tmux.conf, such as %if, can now be used in string commands
(for example, those given to if-shell - not commands invoked from the
shell, they are still parsed by the shell itself).

The only syntax change I am aware of is that #{} outside quotes or a
comment is now considered a format and not a comment, so #{ is now a
syntax error (notably, if it is at the start of a line).

This also adds two new sections to the man page documenting the syntax
and outlining how parsing and command execution works.

Thanks to everyone who sent me test configs (they still all parse
without errors - but this doesn't mean they still work as intended!).

Thanks to Avi Halachmi for testing and man page improvements, also to
jmc@ for reviewing the man page changes.
2019-05-23 11:13:30 +00:00
Nicholas Marriott
7ca2e2fe88 Add a config. 2019-05-23 11:57:04 +01:00
Thomas Adam
9d450cc6d0 Merge branch 'obsd-master' 2019-05-22 21:02:28 +01:00
nicm
5571d7a21c Fix crash if window doesn't exist, GitHub issue 1751. 2019-05-22 18:58:31 +00:00
Nicholas Marriott
e353d0cab3 Another config. 2019-05-22 19:49:48 +01:00
Nicholas Marriott
282f7fbd37 Add a test config. 2019-05-22 13:31:20 +01:00
Nicholas Marriott
b68fc7f104 Two more configs. 2019-05-22 07:12:38 +01:00
Nicholas Marriott
54dfe36340 Another. 2019-05-21 17:07:08 +01:00
Thomas Adam
c4f4904f9b More configs 2019-05-21 16:43:34 +01:00
Nicholas Marriott
d769fec8d6 More configs. 2019-05-21 14:41:07 +01:00
Nicholas Marriott
f1ce611345 Add another one. 2019-05-21 11:28:12 +01:00
Thomas Adam
3856116069 Merge branch 'obsd-master' 2019-05-21 09:02:37 +01:00
Nicholas Marriott
afe231c94c Add a few test configs thanks to various people. 2019-05-21 08:02:17 +01:00
nicm
ae4cccb4f1 Fix a couple of Ta from Alphonse Mariya. 2019-05-21 07:01:14 +00:00
Thomas Adam
98ee93bde3 Merge branch 'obsd-master' 2019-05-20 15:02:40 +01:00
nicm
87d82170a6 Fix ordering of source-file with multiple files and add flags to load_cfg. 2019-05-20 13:23:32 +00:00
Thomas Adam
3a7e15511b Merge branch 'obsd-master' 2019-05-20 13:02:36 +01:00
nicm
e128c7fcd8 Replace the various identical error callbacks with a single one in cmd-queue.c. 2019-05-20 11:46:06 +00:00
nicm
8db89f8efb Add a helper to allocate a cmd_list. 2019-05-20 11:34:37 +00:00
Thomas Adam
c271cb9ff8 Merge branch 'obsd-master' 2019-05-20 07:02:36 +01:00
nicm
6e0c663a89 Fix the color space parameter in RGB SGR, from Brad Town. 2019-05-20 05:35:46 +00:00
Thomas Adam
56246c2936 README.md: mention TODO list on the tmux wiki 2019-05-19 13:31:09 +01:00
Thomas Adam
54da493476 Merge branch 'obsd-master' 2019-05-18 23:02:36 +01:00
nicm
82ebd98c5f Move the single command flag (CMD_CONTROL) into the shared flags. 2019-05-18 21:14:10 +00:00
Nicholas Marriott
831c67c2d8 Remove TODO, moving onto website. 2019-05-18 14:44:31 +01:00
Nicholas Marriott
d6f7be6345 Add to CHANGES. 2019-05-18 13:29:38 +01:00
Thomas Adam
eb064e8a62 Merge branch 'obsd-master' 2019-05-17 09:02:36 +01:00
nicm
9b83b1daa6 Change a couple of ACS characters to be more sensible and add a few
missing ones, reported by Ricardo Banffy.
2019-05-17 05:48:25 +00:00
nicm
4acd345c6a Initialize default size variables, from Thomas Adam. 2019-05-17 05:47:31 +00:00
Thomas Adam
d9ac0e7576 Merge branch 'obsd-master' 2019-05-15 22:50:42 +01:00
Nicholas Marriott
dcf0bc2cc9 Mention CONTRIBUTING. 2019-05-15 21:42:43 +01:00
nicm
cf4566b47b Fix dragging when in view mode rather than copy mode, GitHub issue 1740
from Brad Town.
2019-05-15 19:25:53 +00:00
Nicholas Marriott
50e77536fe Less headings. 2019-05-15 20:21:38 +01:00
Nicholas Marriott
cd4e467751 Kill tmux for logs. 2019-05-15 20:21:07 +01:00
Thomas Adam
9228fead48 Merge branch 'obsd-master' 2019-05-14 09:02:28 +01:00
nicm
38b8a198ba Fix sizing of main-vertical and main-horizontal layouts, GitHub issue 1736. 2019-05-14 07:37:50 +00:00
Thomas Adam
1eefbd28e8 Merge branch 'obsd-master' 2019-05-13 23:02:26 +01:00
nicm
1ee944a19d Add support for overline (SGR 53), from Ricardo Banffy. 2019-05-13 20:10:23 +00:00
nicm
1b0512aa7e Always include Lock in the menu. 2019-05-13 20:07:02 +00:00
Thomas Adam
b5b5d35eee Merge branch 'obsd-master' 2019-05-13 11:02:31 +01:00
nicm
c5f660e33a Fix column width for copy mode commands. 2019-05-13 08:56:07 +00:00
Nicholas Marriott
e1e520d741 Clarify a little. 2019-05-13 09:51:51 +01:00
Thomas Adam
699d9d2fac Merge branch 'obsd-master' 2019-05-12 21:02:27 +01:00
nicm
c3c3927c2b Oops, removed too much in last change. 2019-05-12 18:18:30 +00:00
nicm
00f19b7f91 Fix some indentation and dead assignments. 2019-05-12 18:16:33 +00:00
Thomas Adam
42da951edf README.md: avaailable -> available
Correct typo.
2019-05-12 18:37:32 +01:00
Thomas Adam
180bbab1fc Merge branch 'obsd-master' 2019-05-12 11:02:25 +01:00
Nicholas Marriott
69c59c52b6 Spelling error. 2019-05-12 10:06:42 +01:00
Nicholas Marriott
19370631ea Tweak text of menu CHANGES entry. 2019-05-12 10:05:10 +01:00
Nicholas Marriott
99c1853792 Scatter some `. 2019-05-12 10:00:31 +01:00
nicm
a131655235 Add simple menus to tree, client, buffer modes. 2019-05-12 08:58:09 +00:00
Thomas Adam
677bb168a9 Merge branch 'obsd-master' 2019-05-12 09:02:28 +01:00
nicm
c91323e4d6 Remove menu_create_from_items, I thought I would use it for some later
work but I don't need it.
2019-05-12 07:27:08 +00:00
Thomas Adam
50d1d04913 Merge branch 'obsd-master' 2019-05-11 09:02:29 +01:00
Nicholas Marriott
67e2f5869a Mention -vv. 2019-05-11 08:34:08 +01:00
Nicholas Marriott
3a9c199ae7 New ISSUE_TEMPLATE from Nicolas CARPi. 2019-05-11 08:23:14 +01:00
Nicholas Marriott
bd6d0b3101 Formatted README.md, from Nicolas CARPi. 2019-05-11 08:19:14 +01:00
Nicholas Marriott
f1dd65cbdf Another tweak. 2019-05-11 08:09:49 +01:00
Nicholas Marriott
c18d7c5fcb Update CONTRIBUTING.md, mostly from Nicolas CARPi. 2019-05-11 08:07:35 +01:00
Nicholas Marriott
0d64531f66 Tweaks to README, some from me, some from Nicolas CARPi. 2019-05-11 07:55:28 +01:00
Nicholas Marriott
f44dafd224 Add to CHANGES. 2019-05-11 07:43:18 +01:00
nicm
198b0a23a2 Don't use arguments with It and -enum, pointed out by jmc. 2019-05-11 06:40:01 +00:00
nicm
ad27b7decd Do not reduce window height by status line height for control mode
clients, from George Nachman.
2019-05-11 06:34:56 +00:00
Thomas Adam
fc00839adc Update CHANGES 2019-05-10 22:27:33 +01:00
Thomas Adam
aa13bd4016 Merge branch 'obsd-master' 2019-05-10 22:10:38 +01:00
nicm
d62fd78655 Fix a typo in previous (, -> :). 2019-05-10 18:09:51 +00:00
nicm
6dcca5fda4 Add support for simple menus usable with mouse or keyboard. New command
display-menu shows a menu (bound to the mouse on status line by default)
and a couple of extra formats for the default menus.
2019-05-10 18:04:06 +00:00
Thomas Adam
c5f6ea5c0d Merge branch 'obsd-master' 2019-05-10 17:02:27 +01:00
nicm
004a9b52f0 Add a function to draw a simple menu onto a screen. 2019-05-10 14:12:47 +00:00
Thomas Adam
e5f06d2cf6 Merge branch 'obsd-master' 2019-05-09 17:02:25 +01:00
nicm
cb10bfb8ef Save mouse buttons as well as position. 2019-05-09 14:09:32 +00:00
Thomas Adam
2e00d775e4 Merge branch 'obsd-master' 2019-05-09 15:02:26 +01:00
nicm
21d9750450 send-keys also needs to insert key commands in the right order. 2019-05-09 13:12:59 +00:00
Thomas Adam
f431e20f3d Merge branch 'obsd-master' 2019-05-09 11:02:25 +01:00
nicm
3c68e51609 Change swap-window -d to be the other way round (stay with src window),
so it works like swap-pane.
2019-05-09 08:39:09 +00:00
nicm
299b7289ea Add formats to show if pane is the marked pane and if any marked pane is set. 2019-05-09 08:38:13 +00:00
Thomas Adam
4bc45fc95a Merge branch 'obsd-master' 2019-05-08 21:02:25 +01:00
nicm
f9682d2e55 Add a flag to redraw only the overlay, and remove the overlay on resize. 2019-05-08 18:07:12 +00:00
nicm
a384245c5a Adjust how mouse targets are found so they always have a session, window
and pane.
2019-05-08 18:05:03 +00:00
Thomas Adam
b24d7d9c95 Merge branch 'obsd-master' 2019-05-07 23:02:30 +01:00
Thomas Adam
453a62c672 Merge branch 'obsd-master' 2019-05-07 21:02:25 +01:00
nicm
89db309e10 Move around the display-panes identify code to make it a bit more
generic and hide the display-panes specific bits into
cmd-display-panes.c.
2019-05-07 20:01:41 +00:00
nicm
d53d3bce59 Adjust the same bit to adjust the selection for history-top and
history-bottom as for cursor-up and cursor-down. GitHub issue 1723.
2019-05-07 19:46:17 +00:00
Thomas Adam
3439c02e9d Merge branch 'obsd-master' 2019-05-07 17:02:25 +01:00
schwarze
fcd0e3082b Rename the ENVIRONMENT section which was squatting on the standard
manual page section and create a new ENVIRONMENT with the expected
content.  Move some information that was misplaced below the -u
flag into that new section.
Feedback and OK nicm@ jmc@ tb@
2019-05-07 14:01:39 +00:00
Thomas Adam
d9767b8112 Merge branch 'obsd-master' 2019-05-07 13:02:27 +01:00
nicm
85a9c2f52b Treat keys in identify mode (display-panes) specially and handle them
immediately rather than queuing them (the command can block the queue
which means they were not being seen until it finished which was too
late). Reported by denis@ and solene@, ok solene@.
2019-05-07 11:24:03 +00:00
nicm
69440d19b7 Do not use evbuffer_add_buffer because it is destructive and doesn't
work in newer libevent.
2019-05-07 10:25:15 +00:00
Nicholas Marriott
eac30a86d7 -attr -> -style. 2019-05-06 21:57:32 +01:00
Thomas Adam
d4177e954c Merge branch 'obsd-master' 2019-05-03 23:02:28 +01:00
nicm
45ae9a8e35 Fix order of insertion in load_cfg. 2019-05-03 21:21:00 +00:00
nicm
9f75635596 Allow panes to be empty (no command), output can be piped to them with
split-window or display-message -I.
2019-05-03 20:44:24 +00:00
Thomas Adam
4d505574dc Merge branch 'obsd-master' 2019-05-03 21:02:26 +01:00
nicm
e8e4f4ec3e Insert after the right element on queue. 2019-05-03 18:59:58 +00:00
nicm
4097257bef Do not store the mouse position we calculate as the start of a drag back
into the mouse event that later code uses, it has been adjusted and they
should use the original position. GitHub issue 1710.
2019-05-03 18:42:40 +00:00
Thomas Adam
a14512e23e Merge branch 'obsd-master' 2019-05-03 19:02:27 +01:00
nicm
84e4652513 Use the right index for user-keys. 2019-05-03 18:00:19 +00:00
nicm
fc3d85e34b Fix mouse positioning when the pane is not entirely visible. 2019-05-03 16:51:29 +00:00
Nicholas Marriott
43656d4ea7 Sync up some tmux.1 bits. 2019-05-03 17:12:30 +01:00
Thomas Adam
fff85d854e Merge branch 'obsd-master' 2019-05-03 17:04:11 +01:00
nicm
cf6075fb29 Correct ordering when adding after an existing item. 2019-05-03 15:43:01 +00:00
nicm
33298d6df6 Instead of processing keys all together, put them up on the client
command queue so they are ordered correctly with the commands that they
execute.
2019-05-03 14:51:30 +00:00
nicm
4bb48998e0 Fix reverse attribute in status line, GitHub issue 1709. 2019-05-03 10:00:48 +00:00
nicm
5d9e591ae8 Fix up some bits about window-size that seem to have got lost. 2019-05-02 20:12:40 +00:00
Thomas Adam
4d16df9312 Merge branch 'obsd-master' 2019-05-01 09:02:27 +01:00
Nicholas Marriott
18e554aa61 Merge tag '2.9a'
2.9a
2019-05-01 07:29:23 +01:00
nicm
c176361788 Remove unused variable from Thomas Adam. 2019-05-01 06:07:14 +00:00
Thomas Adam
dc7e53897a Merge branch 'obsd-master' 2019-04-30 21:02:26 +01:00
nicm
429c4bc51b Fix user options after show-hooks merge, GitHub issue 1704. 2019-04-30 18:02:03 +00:00
Nicholas Marriott
1156d91cf8 Simple -C sanity test. 2019-04-30 18:39:07 +01:00
Thomas Adam
4c5b0fbbcc Merge branch 'obsd-master' 2019-04-30 09:02:29 +01:00
nicm
5a288b1efe Don't redraw control clients, from George Nachman. 2019-04-30 06:21:30 +00:00
nicm
82c789ee58 Fix memory leak in window tree search, from Amos Bird. 2019-04-30 06:19:51 +00:00
Nicholas Marriott
7e0f9ab3eb Don't think I will do this. 2019-04-29 21:48:15 +01:00
Nicholas Marriott
87bd8965c6 Add to CHANGES & remove from TODO. 2019-04-29 20:18:07 +01:00
Thomas Adam
2aa517c805 Merge branch 'obsd-master' 2019-04-29 09:02:25 +01:00
nicm
ec81bd2399 Add support for keys to jump between matching brackets - C-M-f and C-M-b
in emacs, % in vi. Suggested by and help from Chris Barber in GitHub
issue 1666.
2019-04-29 06:55:21 +00:00
Thomas Adam
3ab229da70 Merge branch 'obsd-master' 2019-04-28 23:02:30 +01:00
Nicholas Marriott
2cecabd75e Fix gseq use in RGB colour script. 2019-04-28 22:16:34 +01:00
nicm
c4b0da5513 Support multiple occurances of the same argument. Use this for a new
flag -e to new-window, split-window, respawn-window, respawn-pane to
pass environment variables into the newly created process. From Steffen
Christgau in GitHub issue 1697.
2019-04-28 20:05:50 +00:00
Thomas Adam
5489796737 Merge branch 'obsd-master' 2019-04-27 20:09:07 +01:00
Thomas Adam
85f09f9a4c Merge branch 'obsd-master' 2019-04-26 13:02:37 +01:00
Nicholas Marriott
293fd0d258 Update CHANGES and TODO. 2019-04-26 12:44:25 +01:00
nicm
dfb7bb6830 Merge hooks into options and make each one an array option. This allows
multiple commands to be easily bound to one hook. set-hook and
show-hooks remain but they are now variants of set-option and
show-options. show-options now has a -H flag to show hooks (by default
they are not shown).
2019-04-26 11:38:51 +00:00
nicm
f1e14f86c4 Destroy panes before options to avoid crash when forced into a mode by a
hook.
2019-04-26 10:24:26 +00:00
nicm
6644d209d2 Unbreak main-vertical and main-horizontal layouts. 2019-04-26 10:15:40 +00:00
Thomas Adam
7c4a2253e8 Merge branch 'obsd-master' 2019-04-25 21:02:43 +01:00
nicm
2d65bbd941 options_array_item_value cannot return NULL. 2019-04-25 19:36:59 +00:00
nicm
a609e6361a Need a fallback for -2 for aixterm colours. 2019-04-25 19:03:43 +00:00
nicm
32a81e197b Make options_tostring allocate its result instead of using a stack
buffer (needed for something in the future).
2019-04-25 18:18:55 +00:00
Thomas Adam
f2c0605d6d Merge branch 'obsd-master' 2019-04-25 17:02:37 +01:00
nicm
1677bb0dea Need to escape ]. 2019-04-25 15:35:07 +00:00
Nicholas Marriott
dddc544b8f Update CHANGES. 2019-04-25 13:00:32 +01:00
Thomas Adam
643eecde86 Merge branch 'obsd-master' 2019-04-25 09:02:36 +01:00
nicm
567d3e27ab Automatically scroll if dragging to create a selection with the mouse
and the cursor reaches the top or bottom line.
2019-04-25 06:34:57 +00:00
Nicholas Marriott
a2e08b587a Merge branch '2.9-rc' 2019-04-25 07:29:02 +01:00
Nicholas Marriott
cb75ec25c8 2.9 now. 2019-04-24 22:35:23 +01:00
nicm
7d06216289 Do not loop forever if there is a nonprintable character in the format. 2019-04-24 22:34:56 +01:00
Thomas Adam
4ab208ecc8 Merge branch 'obsd-master' 2019-04-24 22:09:57 +01:00
nicm
6aa0bedad2 Use bg not fg when adjusting for aixterm, from Ailin Nemui. 2019-04-24 20:32:31 +00:00
nicm
c6e39976c6 Do not loop forever if there is a nonprintable character in the format. 2019-04-24 20:27:52 +00:00
Thomas Adam
c869366133 Merge branch 'obsd-master' 2019-04-23 23:02:42 +01:00
nicm
b9022e33ea Somehow missed these bits in last commit. 2019-04-23 20:40:03 +00:00
nicm
772b3b7a06 Indicate an array option with a flag rather than a special type so that
in future will not have to be strings.
2019-04-23 20:36:55 +00:00
Thomas Adam
b0d7623b7e Merge branch 'obsd-master' 2019-04-23 13:02:37 +01:00
Thomas Adam
4a96f599f6 Merge branch 'obsd-master' 2019-04-23 11:02:36 +01:00
nicm
564e44adc6 Add -no-clear variants of copy-selection and copy-pipe which do not
clear the selection after copying. Make copy-pipe clear the selection by
default to be consistent with copy-selection. From Avi Halachmi.
2019-04-23 09:39:07 +00:00
nicm
6752f41c2a Do not try to resize if the parent cell is NULL, problem reported by
Sunil Nimmagadda.
2019-04-23 09:15:24 +00:00
Nicholas Marriott
51c09bf2b4 Merge branch '2.9-rc' 2019-04-18 22:14:55 +01:00
Nicholas Marriott
8d752f5aaa Update CHANGES. 2019-04-18 22:11:46 +01:00
Nicholas Marriott
e7827f8bc2 Remove duplicate entry. 2019-04-18 14:16:19 +01:00
Thomas Adam
f980e868dd Merge branch 'obsd-master' 2019-04-18 14:11:22 +01:00
Thomas Adam
3c1f0cfc34 Merge branch 'obsd-master' 2019-04-18 14:08:13 +01:00
nicm
5b9211d827 Copy the code to infer the option type to show-options and document it. 2019-04-18 12:22:07 +00:00
nicm
3f189945d8 Pass target client and session to load_cfg from source-file so formats
work. Reported by Thomas Sattler.
2019-04-18 11:07:28 +00:00
nicm
f3ab05e7cd Update session activity on focus event, from tafryn at gmail dot com. 2019-04-18 10:11:52 +00:00
jmc
2219f7cc73 mark up punctuation-as-macro-args properly; 2019-04-17 16:34:35 +00:00
nicm
1fd3b9ec1c Fix minimum size check on split and size of first cell on spread out
with a pane status line.
2019-04-17 14:44:33 +00:00
nicm
da31eddadc Rewrite main-vertical and horizontal to use the common spread out code
and to handle the case where the panes won't fit into the existing
window size.
2019-04-17 14:44:14 +00:00
nicm
c660e46149 Set the window size as well as the layout size when using the preset
layouts.
2019-04-17 14:43:49 +00:00
nicm
e3b1358bbc Do not let the size of the pane status screen go negative. 2019-04-17 14:41:08 +00:00
nicm
5943cd1907 Document that switch-client can change all of session,window,pane and
check for % in the target as well as ":.".
2019-04-17 14:39:37 +00:00
nicm
78287e27c8 Break new window and pane creation common code from various commands and
window.c into a separate file spawn.c.
2019-04-17 14:37:48 +00:00
Nicholas Marriott
82bc2c87a9 Add to TODO. 2019-04-17 09:10:23 +01:00
Nicholas Marriott
e6ee3e9504 Warning fixes from Carlo Marcelo Arenas Belón. 2019-04-10 19:27:09 +01:00
Nicholas Marriott
1c1ec84aa3 Fix minimum size check on split and size of first cell on spread out with a
pane status line.
2019-04-10 14:54:02 +01:00
Nicholas Marriott
f3fcf977e4 Use the right winlink pointer in new-window. 2019-04-10 11:40:59 +01:00
Nicholas Marriott
4a3ac3bd26 Remove unused variable. 2019-04-10 07:27:21 +01:00
Nicholas Marriott
3c5d3a3780 Add PATH_DEFPATH to compat.h from Carlo Marcelo Arenas Belón. 2019-04-09 21:31:50 +01:00
Nicholas Marriott
fe44f90e5b Do not let the size of the pane status screen go negative. 2019-04-09 21:30:03 +01:00
Nicholas Marriott
031e9bc854 Rewrite main-vertical and horizontal to use the common spread out code and to
handle the case where the panes won't fit into the existing window size.
2019-04-09 21:15:00 +01:00
Nicholas Marriott
0cbccc90ab Set the window size as well as the layout size when using the preset layouts
and calculate the sizes correctly.
2019-04-09 20:38:43 +01:00
Nicholas Marriott
71d90c11dd Solaris has no paths.h. 2019-04-09 07:52:54 +01:00
Thomas Adam
2546572e8e Merge branch 'obsd-master' 2019-04-08 21:02:37 +01:00
nicm
835ccbac46 select-word was missing from the command list, from pawel-slowik. 2019-04-08 17:55:51 +00:00
Nicholas Marriott
b5b67c5386 Document that switch-client can change all of session,window,pane and check for
% in the target as well as ":.".
2019-04-08 10:38:50 +01:00
Thomas Adam
fbe488e4de Merge branch 'obsd-master' 2019-04-07 23:02:37 +01:00
nicm
bb629f3be7 Current window style also needs to be tested for default. 2019-04-07 20:18:20 +00:00
Thomas Adam
883a428e27 Linux: remove include for util.h
Linux doesn't require this header.
2019-04-07 15:39:58 +01:00
Nicholas Marriott
e44e2c48dd Do not log NULL command. 2019-04-07 13:16:55 +01:00
Nicholas Marriott
ece737274e Forgot to add the file. 2019-04-07 13:11:55 +01:00
Nicholas Marriott
5ece386cdf Break new window and pane creation common code from various commands and
window.c into a separate file spawn.c.
2019-04-07 13:01:03 +01:00
Nicholas Marriott
7653328ce7 Remove from TODO one thing that I don't think is necessary and one that is
done.
2019-04-07 12:18:40 +01:00
Nicholas Marriott
fc111d2b12 Fix a duplicate entry in CHANGES and remove an item from TODO. 2019-04-07 12:07:26 +01:00
Thomas Adam
04402db616 Merge branch 'obsd-master' 2019-04-05 23:02:36 +01:00
nicm
1b5a8a0f09 Fix some warnings, from Thomas Adam. 2019-04-05 20:32:31 +00:00
Nicholas Marriott
e33b623f21 Merge branch '2.9-rc' 2019-04-04 18:31:54 +01:00
Thomas Adam
5a97af7961 Merge branch 'obsd-master' 2019-04-04 13:02:37 +01:00
nicm
f4aefb738e Fix size check for splitw -f and top level pane size for tiled layout,
problems reported by Thomas Sattler.
2019-04-04 10:25:35 +00:00
Thomas Adam
cb039b986e Merge branch 'obsd-master' 2019-04-03 09:02:41 +01:00
nicm
f6c54f3f03 Do not load /etc/tmux.conf if given -f. 2019-04-03 06:43:19 +00:00
nicm
06d58b3b7b screen_write_fast_copy can no longer assume the target screen is default
(it isn't for the pane status lines).
2019-04-03 06:43:04 +00:00
Thomas Adam
c9ef144dca Merge branch 'obsd-master' 2019-04-02 21:02:37 +01:00
nicm
09f71ed1b2 Silence flag should use the same option as activity, reported by Thomas
Sattler.
2019-04-02 18:41:24 +00:00
Nicholas Marriott
f0ddc301b7 Add CHANGES. 2019-04-02 17:02:09 +01:00
Thomas Adam
bfc1f0ca62 Merge branch 'obsd-master' 2019-04-02 11:02:44 +01:00
nicm
7bcc0d16f2 Add an argument to copy commands to set the prefix for the buffer name,
allows buffers for different sessions to be named separately.
2019-04-02 09:03:39 +00:00
Nicholas Marriott
f03776c262 Don't use a config file in tests. 2019-04-02 09:53:02 +01:00
nicm
ffa4d48967 Store and restore cursor across reflow by working out a position based
on unwrapped lines, rather than a grid offset. Fixes problems reported
by Thomas Sattler and Paul de Weerd.
2019-04-02 08:45:32 +00:00
Nicholas Marriott
79c3fd4f39 Add cursor regression tests. 2019-04-02 09:44:28 +01:00
Thomas Adam
294accea1b Merge branch 'obsd-master' 2019-04-01 21:02:37 +01:00
nicm
792fcb1dbf Restore a check to stop scrolled lines becoming larger than total lines,
fixes a crash reported by Thomas Sattler.
2019-04-01 19:33:38 +00:00
Nicholas Marriott
da359269cb Merge branch '2.9-rc' 2019-03-29 11:20:15 +00:00
Thomas Adam
ecc5cea09b Merge branch 'obsd-master' 2019-03-29 10:02:36 +00:00
nicm
d68a17a1df Free old strings after they have been expanded in format_choose. 2019-03-29 09:33:24 +00:00
Nicholas Marriott
52cb7a6382 Merge branch '2.9-rc' 2019-03-29 07:05:50 +00:00
Thomas Adam
405bb8435c Merge branch 'obsd-master' 2019-03-29 00:02:36 +00:00
nicm
c6c4960b35 Fix offset of list ranges. 2019-03-28 22:18:46 +00:00
Thomas Adam
2c4f2393ec Merge branch 'obsd-master' 2019-03-28 22:02:37 +00:00
nicm
08bc226527 Expand session and window formats for buffer filters. 2019-03-28 21:05:15 +00:00
Thomas Adam
6b38334b28 Merge branch 'obsd-master' 2019-03-27 14:02:36 +00:00
nicm
c1f0918f8a Fix stop-selection, from Avi Halachmi. 2019-03-27 13:25:11 +00:00
Thomas Adam
6861045d38 Merge branch 'obsd-master' 2019-03-26 22:02:36 +00:00
nicm
9f8d193b11 Break copy mode commands into individual functions instead of a big load
of if statements.
2019-03-26 21:01:19 +00:00
Nicholas Marriott
a13e57f701 Update TODO. 2019-03-26 17:04:47 +00:00
Nicholas Marriott
e722ba38e3 There is no examples directory anymore. 2019-03-26 15:10:05 +00:00
Nicholas Marriott
b1dc2b5353 Merge branch '2.9-rc' 2019-03-26 15:05:41 +00:00
Nicholas Marriott
0a913b09cf For 3.0. 2019-03-26 14:38:20 +00:00
119 changed files with 10264 additions and 3959 deletions

71
.github/CONTRIBUTING.md vendored Normal file
View File

@@ -0,0 +1,71 @@
## What should I do before opening an issue?
Before opening an issue, please ensure that:
- `$TERM` inside tmux is screen, screen-256color, tmux or tmux-256color. Check
by running `echo $TERM` inside tmux.
- You can reproduce the problem with the latest tmux release, or a build from
Git master.
- Your question or issue is not covered in the manual (run man tmux).
- Nobody else has opened the same issue recently.
## What should I include in an issue?
Please include the output of:
~~~bash
uname -sp && tmux -V && echo $TERM
~~~
Also include:
- Your platform (Linux, OS X, or whatever).
- A brief description of the problem with steps to reproduce.
- A minimal tmux config, if you can't reproduce without a config.
- Your terminal, and `$TERM` inside and outside of tmux.
- Logs from tmux (see below).
- At most one or two screenshots, if helpful.
## How do I test without a .tmux.conf?
Run a separate tmux server with `-f/dev/null` to skip loading `.tmux.conf`:
~~~bash
tmux -Ltest kill-server
tmux -Ltest -f/dev/null new
~~~
## How do I get logs from tmux?
Add `-vv` to tmux to create three log files in the current directory. If you can
reproduce without a configuration file:
~~~bash
tmux -Ltest kill-server
tmux -vv -Ltest -f/dev/null new
~~~
Or if you need your configuration:
~~~base
tmux kill-server
tmux -vv new
~~~
The log files are:
- `tmux-server*.log`: server log file.
- `tmux-client*.log`: client log file.
- `tmux-out*.log`: output log file.
Please attach the log files to your issue.

19
.github/ISSUE_TEMPLATE.md vendored Normal file
View File

@@ -0,0 +1,19 @@
### Issue description
Please read https://github.com/tmux/tmux/blob/master/.github/CONTRIBUTING.md
before opening an issue.
Describe the problem and the steps to reproduce. Add a minimal tmux config if
necessary. Screenshots can be helpful, but no more than one or two.
Do not report bugs (crashes, incorrect behaviour) without reproducing on a tmux
built from the latest code in Git.
### Required information
Please provide the following information:
* tmux version (`tmux -V`).
* Platform (`uname -sp`).
* $TERM inside and outside of tmux (`echo $TERM`).
* Logs from tmux (`tmux kill-server; tmux -vv new`).

88
.github/README.md vendored Normal file
View File

@@ -0,0 +1,88 @@
# Welcome to tmux!
tmux is a terminal multiplexer: it enables a number of terminals to be created,
accessed, and controlled from a single screen. tmux may be detached from a
screen and continue running in the background, then later reattached.
This release runs on OpenBSD, FreeBSD, NetBSD, Linux, OS X and Solaris.
## Dependencies
tmux depends on [libevent](https://libevent.org) 2.x, available from [this
page](https://github.com/libevent/libevent/releases/latest).
It also depends on [ncurses](https://www.gnu.org/software/ncurses/), available
from [this page](https://invisible-mirror.net/archives/ncurses/).
## Installation
### From release tarball
To build and install tmux from a release tarball, use:
~~~bash
./configure && make
sudo make install
~~~
tmux can use the utempter library to update utmp(5), if it is installed - run
configure with `--enable-utempter` to enable this.
### From version control
To get and build the latest from version control:
~~~bash
git clone https://github.com/tmux/tmux.git
cd tmux
sh autogen.sh
./configure && make
~~~
(Note that this requires at least a working C compiler, `make`, `autoconf`,
`automake`, `pkg-config` as well as `libevent` and `ncurses` libraries and
headers.)
## Contributing
Bug reports, feature suggestions and especially code contributions are most
welcome. Please send by email to:
tmux-users@googlegroups.com
Or open a GitHub issue or pull request.
There is [a TODO list](https://github.com/tmux/tmux/wiki/Contributing) which
explains some ideas for tmux not yet developed. Please feel free to ask for
clarifications on the mailing list if you're thinking of working on these or
need further information.
Please read the CONTRIBUTING file before opening an issue.
## Documentation
For documentation on using tmux, see the tmux.1 manpage. View it from the
source tree with:
~~~bash
nroff -mdoc tmux.1|less
~~~
A small example configuration is in `example_tmux.conf`.
And a bash(1) completion file at:
https://github.com/imomaliev/tmux-bash-completion
For debugging, run tmux with `-v` or `-vv` to generate server and client log
files in the current directory.
## Support
The tmux mailing list for general discussion and bug reports is:
https://groups.google.com/forum/#!forum/tmux-users
Subscribe by sending an email to:
tmux-users+subscribe@googlegroups.com

1
.gitignore vendored
View File

@@ -18,3 +18,4 @@ Makefile.in
configure
tmux.1.*
*.dSYM
cmd-parse.c

View File

@@ -1,10 +1,16 @@
language: c
matrix:
include:
- compiler: gcc
- compiler: clang
env: CFLAGS="-g -O2"
os:
- linux
- osx
compiler:
- gcc
- clang
before_install:
- sudo apt-get update -qq
- sudo apt-get -y install debhelper autotools-dev dh-autoreconf file libncurses5-dev libevent-dev pkg-config libutempter-dev build-essential
script: (CFLAGS= ./autogen.sh) && ./configure --enable-debug && make
- if [ "$TRAVIS_OS_NAME" = "linux" ]; then sudo apt-get update -qq; fi
- if [ "$TRAVIS_OS_NAME" = "linux" ]; then sudo apt-get -y install bison autotools-dev libncurses5-dev libevent-dev pkg-config libutempter-dev build-essential; fi
script:
- ./autogen.sh && ./configure && make

66
CHANGES
View File

@@ -1,3 +1,69 @@
CHANGES FROM 2.9 to 3.0
* INCOMPATIBLE: Add a new {} syntax to the configuration file. This is a string
similar to single quotes but also includes newlines and allows commands that
take other commands as string arguments to be expressed more clearly and
without additional escaping.
A literal { and } or a string containing { or } must now be escaped or
quoted, for example '{' and '}' instead of { or }, or 'X#{foo}' instead of
X#{foo}.
* New <, >, <= and >= comparison operators for formats.
* Improve escaping of special characters in list-keys output.
* INCOMPATIBLE: tmux's configuration parsing has changed to use yacc(1). There
is one incompatible change: a \ on its own must be escaped or quoted as
either \\ or '\' (the latter works on older tmux versions).
Entirely the same parser is now used for parsing the configuration file
and for string commands. This means that constructs previously only
available in .tmux.conf, such as %if, can now be used in string commands
(for example, those given to if-shell - not commands invoked from the
shell, they are still parsed by the shell itself).
* Add support for the overline attribute (SGR 53). The Smol capability is
needed in terminal-overrides.
* Add the ability to create simple menus. Introduces new command
display-menu. Default menus are bound to MouseDown3 on the status line;
MouseDown3 or M-MouseDown3 on panes; MouseDown3 in tree, client and
buffer modes; and C-b C-m and C-b M-m.
* Allow panes to be empty (no command). They can be created either by piping to
split-window -I, or by passing an empty command ('') to split-window. Output
can be sent to an existing empty window with display-message -I.
* Add keys to jump between matching brackets (emacs C-M-f and C-M-b, vi %).
* Add a -e flag to new-window, split-window, respawn-window, respawn-pane to
pass environment variables into the newly created process.
* Hooks are now stored in the options tree as array options, allowing them to
have multiple separate commands. set-hook and show-hooks remain but
set-option and show-options can now also be used (show-options will only show
hooks if given the -H flag). Hooks with multiple commands are run in index
order.
* Automatically scroll if dragging to create a selection with the mouse and the
cursor reaches the top or bottom line.
* Add -no-clear variants of copy-selection and copy-pipe which do not clear the
selection after copying. Make copy-pipe clear the selection by default to be
consistent with copy-selection.
* Add an argument to copy commands to set the prefix for the buffer name, this
(for example) allows buffers for different sessions to be named separately.
* Update session activity on focus event.
* Pass target from source-file into the config file parser so formats in %if
and %endif have access to more useful variables.
* Add the ability to infer an option type (server, session, window) from its
name to show-options (it was already present in set-option).
CHANGES FROM 2.9 to 2.9a
* Fix bugs in select-pane and the main-horizontal and main-vertical layouts.

View File

@@ -1,33 +0,0 @@
When reporting issues:
YOU MUST INCLUDE THE TMUX VERSION
DO NOT OPEN AN ISSUE THAT DOES NOT MENTION THE TMUX VERSION
Please also include:
- your platform (Linux, OS X, or whatever);
- a brief description of the problem with steps to reproduce;
- a minimal tmux config, if you can't reproduce without a config;
- your terminal, and $TERM inside and outside of tmux;
- logs from tmux (see below);
- at most one or two screenshots, if helpful.
This should include at least the output of:
$ uname -sp && tmux -V && echo $TERM
Please do not report bugs (crashes, incorrect behaviour) without reproducing on
a tmux built from the latest code in Git.
Note that TERM inside tmux must be a variant of screen or tmux (for example:
screen or screen-256color, tmux or tmux-256color). Please ensure this is the
case before opening an issue.
To run tmux without a config and get logs, run:
tmux -Ltest kill-server
tmux -vv -Ltest -f/dev/null new
Then reproduce the problem, exit tmux, and attach the tmux-server-*.log file
from the current directory to the issue.

View File

@@ -1,10 +1,7 @@
THIS IS FOR INFORMATION ONLY, CODE IS UNDER THE LICENCE AT THE TOP OF ITS FILE.
The README, CHANGES, FAQ and TODO files are licensed under the ISC
license. Files under examples/ remain copyright their authors unless otherwise
stated in the file but permission has been received to distribute them with
tmux. All other files have a license and copyright notice at their start,
typically:
The README, CHANGES, FAQ and TODO files are licensed under the ISC license. All
other files have a license and copyright notice at their start, typically:
Copyright (c) <author>

View File

@@ -1 +0,0 @@
Please read https://raw.githubusercontent.com/tmux/tmux/master/CONTRIBUTING

View File

@@ -2,7 +2,7 @@
# Obvious program stuff.
bin_PROGRAMS = tmux
CLEANFILES = tmux.1.mdoc tmux.1.man
CLEANFILES = tmux.1.mdoc tmux.1.man cmd-parse.c
# Distribution tarball options.
EXTRA_DIST = \
@@ -71,6 +71,7 @@ dist_tmux_SOURCES = \
cmd-confirm-before.c \
cmd-copy-mode.c \
cmd-detach-client.c \
cmd-display-menu.c \
cmd-display-message.c \
cmd-display-panes.c \
cmd-find-window.c \
@@ -93,6 +94,7 @@ dist_tmux_SOURCES = \
cmd-move-window.c \
cmd-new-session.c \
cmd-new-window.c \
cmd-parse.y \
cmd-paste-buffer.c \
cmd-pipe-pane.c \
cmd-queue.c \
@@ -112,14 +114,12 @@ dist_tmux_SOURCES = \
cmd-send-keys.c \
cmd-set-buffer.c \
cmd-set-environment.c \
cmd-set-hook.c \
cmd-set-option.c \
cmd-show-environment.c \
cmd-show-messages.c \
cmd-show-options.c \
cmd-source-file.c \
cmd-split-window.c \
cmd-string.c \
cmd-swap-pane.c \
cmd-swap-window.c \
cmd-switch-client.c \
@@ -135,7 +135,6 @@ dist_tmux_SOURCES = \
format-draw.c \
grid-view.c \
grid.c \
hooks.c \
input-keys.c \
input.c \
job.c \
@@ -145,6 +144,7 @@ dist_tmux_SOURCES = \
layout-set.c \
layout.c \
log.c \
menu.c \
mode-tree.c \
names.c \
notify.c \
@@ -160,6 +160,7 @@ dist_tmux_SOURCES = \
server-fn.c \
server.c \
session.c \
spawn.c \
status.c \
style.c \
tmux.c \

72
README
View File

@@ -6,13 +6,17 @@ screen and continue running in the background, then later reattached.
This release runs on OpenBSD, FreeBSD, NetBSD, Linux, OS X and Solaris.
tmux depends on libevent 2.x. Download it from:
* Dependencies
http://libevent.org
tmux depends on libevent 2.x, available from:
https://github.com/libevent/libevent/releases/latest
It also depends on ncurses, available from:
http://invisible-island.net/ncurses/
https://invisible-mirror.net/archives/ncurses/
* Installation
To build and install tmux from a release tarball, use:
@@ -32,40 +36,42 @@ To get and build the latest from version control:
(Note that this requires at least a working C compiler, make, autoconf,
automake, pkg-config as well as libevent and ncurses libraries and headers.)
For more information see http://git-scm.com. Patches should be sent by email to
the mailing list at tmux-users@googlegroups.com or submitted through GitHub at
https://github.com/tmux/tmux/issues.
For documentation on using tmux, see the tmux.1 manpage. It can be viewed from
the source tree with:
$ nroff -mdoc tmux.1|less
A small example configuration in example_tmux.conf.
And a bash(1) completion file at:
https://github.com/imomaliev/tmux-bash-completion
For debugging, running tmux with -v or -vv will generate server and client log
files in the current directory.
tmux mailing lists are available. For general discussion and bug reports:
https://groups.google.com/forum/#!forum/tmux-users
And for Git commit emails:
https://groups.google.com/forum/#!forum/tmux-git
Subscribe by sending an email to <tmux-users+subscribe@googlegroups.com>.
* Contributing
Bug reports, feature suggestions and especially code contributions are most
welcome. Please send by email to:
tmux-users@googlegroups.com
This file and the CHANGES, FAQ, SYNCING and TODO files are licensed under the
ISC license. All other files have a license and copyright notice at their start.
Or open a GitHub issue or pull request.
-- Nicholas Marriott <nicholas.marriott@gmail.com>
* Documentation
For documentation on using tmux, see the tmux.1 manpage. View it from the
source tree with:
$ nroff -mdoc tmux.1|less
A small example configuration is in example_tmux.conf.
A bash(1) completion file is at:
https://github.com/imomaliev/tmux-bash-completion
For debugging, run tmux with -v and -vv to generate server and client log files
in the current directory.
* Support
The tmux mailing list for general discussion and bug reports is:
https://groups.google.com/forum/#!forum/tmux-users
Subscribe by sending an email to:
tmux-users+subscribe@googlegroups.com
* License
This file and the CHANGES files are licensed under the ISC license. All other
files have a license and copyright notice at their start.

161
TODO
View File

@@ -1,161 +0,0 @@
- command bits and pieces:
* allow multiple targets: fnmatch for -t/-c, for example detach all
clients with -t*
* ' and " should be parsed the same (eg "\e" vs '\e') in config
and command prompt
* last-pane across sessions
* resize-pane -p to match split-window -p
* flag to wait-for to have a timeout and/or to stop waiting when the
client gets a signal
- make command sequences more usable
* don't require space after ;
* options for error handling: && and ||?
- options bits and pieces:
* way to set socket path from config file
- format improvements:
* some way to pad # stuff with spaces
* formats to show if a window is linked into multiple sessions, into
multiple attached sessions, and is the active window in multiple
attached sessions?
* comparison operators like < and > (for #{version}?)
- improve monitor-*:
* straighten out rules for multiple clients
* think about what happens across sessions
* monitor changes within a region
* perhaps monitor /all/ panes in the window not just one
- improve mouse support:
* bind commands to mouse in different areas?
* commands executed when clicking on a pattern (URL)
- warts on current naming:
* display-time but message-fg/bg/attr
* list-* vs show-*
- copy/paste improvements:
* paste w/o trailing whitespace
* command to toggle selection not to move it in copy-mode
* regex searching
* searching in copy mode should unwrap lines, so if you search for "foobar"
then it should be found even if it is now "foo\nbar" (if the WRAP flag
is set on the line)
* capture-pane option to preserve spaces but not join lines
* improve word and line selection in copy mode (for example when
dragging it should select by word. compare how xterm works. GitHub
issue 682)
* key to search for word under cursor (GitHub issue 1240)
* when entering copy mode, should copy grid so that input does not
need to be suspended
* allow the prefix for automatic buffers to be specified as part of the
key binding to allow session buffers or similar (GitHub issue 1501)
* copy-pipe should be synchronous (GitHub issue 1517)
- layout stuff
* way to tag a layout as a number/name
* maybe keep last layout + size around and if size reverts just put it
back
* revamp layouts: they are too complicated, should be more closely
integrated, should support hints, layout sets should just be a
special case of custom layouts, and we should support panes that are
not attached to a cell at all. this could be the time to introduce
panelink to replace layout_cell
* way to set hints/limits about pane size for resizing
* a mode where one application can cross two panes (ie x|y, width =
COLUMNS/2 but height = ROWS * 2)
* separate active panes for different clients
* way to choose where the freed space goes when a pane is killed:
option to kill-pane? GitHub issue 918
- code cleanup
* instead of separate window and session options, just one master
options list with each option having a type (window or session), then
options on window, on session, and global. for window options we look
window->session->global, and for session we look session->global.
problem: what about windows in multiple sessions? there are contexts
where we do not know which session, or where multiple choices makes
no sense... could at least have one global list for all types of
global options and keep separate window,session lists
* the way pane, window, session destroy is handled is too complicated
and the distinction between session.c, window.c and server-fn.c
functions is not clear. could we just have kill_pane(),
kill_window(), unlink_window(), kill_session() that fix up all data
structures (flagging sessions as dead) and return a value to say
whether clients need to be checked for dead sessions? sort of like
session_detach now but more so. or some other scheme to make it
simpler and clearer? also would be nice to remove/rename server-fn.c
* more readable way to work out the various things commands need to
know about the client, notably:
- is this the config file? (cmdq->c == NULL)
- is this a command client? (cmdq->c != NULL &&
cmdq->c->session == NULL)
- is this a control client?
- can i do stdin or stdout to this client?
or even guarantee that cmdq->c != NULL and provide a better way to
tell when in the config file - then we use cmdq->c if we need a
client w/o a session else cmd_current_client
- miscellaneous
* link panes into multiple windows
* live update: server started with -U connects to server, requests
sessions and windows, receives file descriptors
* there are inconsistencies in what we get from old shell and what
comes from config for new sessions and windows. likewise, panes and
jobs and run-shell and lock command all start with slightly different
environments
* multiline status line? separate command prompt and status line?
* automatic pane logging
* marks in history, automatically add (move?) one when pane is changed
* this doesn't work, need pane reference count probably:
bind -n DoubleClick3Status confirm-before -p "kill-window #I? (y/n)" kill-window
* marker lines in history (GitHub issue 1042)
* tree mode stuff: make command prompt (:) common code so all modes get it,
predefined filters, tag-all key, ...
* drag panes and windows around to move/swap them in choose mode
* flag to specify environment to new-window, split-window,
new-session (issue 1498)
* multiple column panes (issue 1503)
* support for ZERO WIDTH JOINER U+200D
* individual pane synchronize-panes (with pane options?); issue 1638
- hooks
* more hooks for various things
* finish after hooks for special commands. these do not have a hook at
the moment:
attach-session detach-client kill-server respawn-window
swap-window break-pane find-window kill-session rotate-window
switch-client choose-tree if-shell kill-window run-shell
wait-for command-prompt join-pane move-window source-file
confirm-before kill-pane respawn-pane swap-pane
at the moment AFTERHOOK uses current only if target is not valid,
but target is ALWAYS valid - it should use current if no -t flag?
then select-* could use AFTERHOOK
* multiple hooks with the same name?
* finish hooks for notifys
* for session_closed, if no sessions at all, perhaps fake up a
temporary one
- pan
* tty_window_offset should try to keep as much off active pane
visible as possible
* rather than centering cursor it might be better if only
moved offset when it gets close to an edge?
----
TODO soonish maybe:
- Store hooks as options, issue 1619.
- Support buffer prefixes, issue 1501.
- copy-pipe should be synchronous, issue 1517.
- -E flag to pass environment to new-*, issue 1498.
- Copy mode searching is slow when there is a big history, issue 1545.
- Grid "block" stuff, issue 1269. Can be used potentially for compression of
history (bit silly really though), reflow performance (can reflow blocks on
demand). It would possibly be good if history-limit could be global and
collected LRU.
- Command aliases should be able to override builtin commands in order to add
default flags (some mechanism needed to avoid recursion). GitHub issue 1630.

View File

@@ -28,9 +28,15 @@
* Manipulate command arguments.
*/
struct args_value {
char *value;
TAILQ_ENTRY(args_value) entry;
};
TAILQ_HEAD(args_values, args_value);
struct args_entry {
u_char flag;
char *value;
struct args_values values;
RB_ENTRY(args_entry) entry;
};
@@ -92,12 +98,18 @@ args_free(struct args *args)
{
struct args_entry *entry;
struct args_entry *entry1;
struct args_value *value;
struct args_value *value1;
cmd_free_argv(args->argc, args->argv);
RB_FOREACH_SAFE(entry, args_tree, &args->tree, entry1) {
RB_REMOVE(args_tree, &args->tree, entry);
free(entry->value);
TAILQ_FOREACH_SAFE(value, &entry->values, entry, value1) {
TAILQ_REMOVE(&entry->values, value, entry);
free(value->value);
free(value);
}
free(entry);
}
@@ -123,22 +135,53 @@ args_print_add(char **buf, size_t *len, const char *fmt, ...)
free(s);
}
/* Add value to string. */
static void
args_print_add_value(char **buf, size_t *len, struct args_entry *entry,
struct args_value *value)
{
char *escaped;
if (**buf != '\0')
args_print_add(buf, len, " -%c ", entry->flag);
else
args_print_add(buf, len, "-%c ", entry->flag);
escaped = args_escape(value->value);
args_print_add(buf, len, "%s", escaped);
free(escaped);
}
/* Add argument to string. */
static void
args_print_add_argument(char **buf, size_t *len, const char *argument)
{
char *escaped;
if (**buf != '\0')
args_print_add(buf, len, " ");
escaped = args_escape(argument);
args_print_add(buf, len, "%s", escaped);
free(escaped);
}
/* Print a set of arguments. */
char *
args_print(struct args *args)
{
size_t len;
char *buf, *escaped;
int i, flags;
char *buf;
int i;
struct args_entry *entry;
static const char quoted[] = " #\"';$";
struct args_value *value;
len = 1;
buf = xcalloc(1, len);
/* Process the flags first. */
RB_FOREACH(entry, args_tree, &args->tree) {
if (entry->value != NULL)
if (!TAILQ_EMPTY(&entry->values))
continue;
if (*buf == '\0')
@@ -148,44 +191,52 @@ args_print(struct args *args)
/* Then the flags with arguments. */
RB_FOREACH(entry, args_tree, &args->tree) {
if (entry->value == NULL)
continue;
if (*buf != '\0')
args_print_add(&buf, &len, " -%c ", entry->flag);
else
args_print_add(&buf, &len, "-%c ", entry->flag);
flags = VIS_OCTAL|VIS_TAB|VIS_NL;
if (entry->value[strcspn(entry->value, quoted)] != '\0')
flags |= VIS_DQ;
utf8_stravis(&escaped, entry->value, flags);
if (flags & VIS_DQ)
args_print_add(&buf, &len, "\"%s\"", escaped);
else
args_print_add(&buf, &len, "%s", escaped);
free(escaped);
TAILQ_FOREACH(value, &entry->values, entry)
args_print_add_value(&buf, &len, entry, value);
}
/* And finally the argument vector. */
for (i = 0; i < args->argc; i++) {
if (*buf != '\0')
args_print_add(&buf, &len, " ");
flags = VIS_OCTAL|VIS_TAB|VIS_NL;
if (args->argv[i][strcspn(args->argv[i], quoted)] != '\0')
flags |= VIS_DQ;
utf8_stravis(&escaped, args->argv[i], flags);
if (flags & VIS_DQ)
args_print_add(&buf, &len, "\"%s\"", escaped);
else
args_print_add(&buf, &len, "%s", escaped);
free(escaped);
}
for (i = 0; i < args->argc; i++)
args_print_add_argument(&buf, &len, args->argv[i]);
return (buf);
}
/* Escape an argument. */
char *
args_escape(const char *s)
{
static const char quoted[] = " #\"';${}";
char *escaped, *result;
int flags;
if (*s == '\0')
return (xstrdup(s));
if ((strchr(quoted, s[0]) != NULL || s[0] == '~') && s[1] == '\0') {
xasprintf(&escaped, "\\%c", s[0]);
return (escaped);
}
flags = VIS_OCTAL|VIS_TAB|VIS_NL;
if (s[strcspn(s, quoted)] != '\0')
flags |= VIS_DQ;
utf8_stravis(&escaped, s, flags);
if (flags & VIS_DQ) {
if (*escaped == '~')
xasprintf(&result, "\"\\%s\"", escaped);
else
xasprintf(&result, "\"%s\"", escaped);
} else {
if (*escaped == '~')
xasprintf(&result, "\\%s", escaped);
else
result = xstrdup(escaped);
}
free(escaped);
return (result);
}
/* Return if an argument is present. */
int
args_has(struct args *args, u_char ch)
@@ -195,22 +246,24 @@ args_has(struct args *args, u_char ch)
/* Set argument value in the arguments tree. */
void
args_set(struct args *args, u_char ch, const char *value)
args_set(struct args *args, u_char ch, const char *s)
{
struct args_entry *entry;
struct args_value *value;
/* Replace existing argument. */
if ((entry = args_find(args, ch)) != NULL) {
free(entry->value);
entry->value = NULL;
} else {
entry = args_find(args, ch);
if (entry == NULL) {
entry = xcalloc(1, sizeof *entry);
entry->flag = ch;
TAILQ_INIT(&entry->values);
RB_INSERT(args_tree, &args->tree, entry);
}
if (value != NULL)
entry->value = xstrdup(value);
if (s != NULL) {
value = xcalloc(1, sizeof *value);
value->value = xstrdup(s);
TAILQ_INSERT_TAIL(&entry->values, value, entry);
}
}
/* Get argument value. Will be NULL if it isn't present. */
@@ -221,7 +274,34 @@ args_get(struct args *args, u_char ch)
if ((entry = args_find(args, ch)) == NULL)
return (NULL);
return (entry->value);
return (TAILQ_LAST(&entry->values, args_values)->value);
}
/* Get first value in argument. */
const char *
args_first_value(struct args *args, u_char ch, struct args_value **value)
{
struct args_entry *entry;
if ((entry = args_find(args, ch)) == NULL)
return (NULL);
*value = TAILQ_FIRST(&entry->values);
if (*value == NULL)
return (NULL);
return ((*value)->value);
}
/* Get next value in argument. */
const char *
args_next_value(struct args_value **value)
{
if (*value == NULL)
return (NULL);
*value = TAILQ_NEXT(*value, entry);
if (*value == NULL)
return (NULL);
return ((*value)->value);
}
/* Convert an argument value to a number. */
@@ -232,13 +312,15 @@ args_strtonum(struct args *args, u_char ch, long long minval, long long maxval,
const char *errstr;
long long ll;
struct args_entry *entry;
struct args_value *value;
if ((entry = args_find(args, ch)) == NULL) {
*cause = xstrdup("missing");
return (0);
}
value = TAILQ_LAST(&entry->values, args_values);
ll = strtonum(entry->value, minval, maxval, &errstr);
ll = strtonum(value->value, minval, maxval, &errstr);
if (errstr != NULL) {
*cause = xstrdup(errstr);
return (0);

View File

@@ -31,7 +31,7 @@ attributes_tostring(int attr)
if (attr == 0)
return ("none");
len = xsnprintf(buf, sizeof buf, "%s%s%s%s%s%s%s%s%s%s%s%s",
len = xsnprintf(buf, sizeof buf, "%s%s%s%s%s%s%s%s%s%s%s%s%s",
(attr & GRID_ATTR_BRIGHT) ? "bright," : "",
(attr & GRID_ATTR_DIM) ? "dim," : "",
(attr & GRID_ATTR_UNDERSCORE) ? "underscore," : "",
@@ -43,7 +43,8 @@ attributes_tostring(int attr)
(attr & GRID_ATTR_UNDERSCORE_2) ? "double-underscore," : "",
(attr & GRID_ATTR_UNDERSCORE_3) ? "curly-underscore," : "",
(attr & GRID_ATTR_UNDERSCORE_4) ? "dotted-underscore," : "",
(attr & GRID_ATTR_UNDERSCORE_5) ? "dashed-underscore," : "");
(attr & GRID_ATTR_UNDERSCORE_5) ? "dashed-underscore," : "",
(attr & GRID_ATTR_OVERLINE) ? "overline," : "");
if (len > 0)
buf[len - 1] = '\0';
@@ -73,7 +74,8 @@ attributes_fromstring(const char *str)
{ "double-underscore", GRID_ATTR_UNDERSCORE_2 },
{ "curly-underscore", GRID_ATTR_UNDERSCORE_3 },
{ "dotted-underscore", GRID_ATTR_UNDERSCORE_4 },
{ "dashed-underscore", GRID_ATTR_UNDERSCORE_5 }
{ "dashed-underscore", GRID_ATTR_UNDERSCORE_5 },
{ "overline", GRID_ATTR_OVERLINE }
};
if (*str == '\0' || strcspn(str, delimiters) == 0)

234
cfg.c
View File

@@ -26,17 +26,6 @@
#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);
struct client *cfg_client;
static char *cfg_file;
int cfg_finished;
@@ -81,13 +70,12 @@ void
start_cfg(void)
{
const char *home;
int quiet = 0;
int flags = 0;
struct client *c;
/*
* Configuration files are loaded without a client, so NULL is passed
* into load_cfg() and commands run in the global queue with
* item->client NULL.
* Configuration files are loaded without a client, so commands are run
* in the global queue with item->client NULL.
*
* However, we must block the initial client (but just the initial
* client) so that its command runs after the configuration is loaded.
@@ -101,210 +89,68 @@ start_cfg(void)
cmdq_append(c, cfg_item);
}
load_cfg(TMUX_CONF, NULL, NULL, 1);
if (cfg_file == NULL)
load_cfg(TMUX_CONF, NULL, NULL, CMD_PARSE_QUIET, NULL);
if (cfg_file == NULL && (home = find_home()) != NULL) {
xasprintf(&cfg_file, "%s/.tmux.conf", home);
quiet = 1;
flags = CMD_PARSE_QUIET;
}
if (cfg_file != NULL)
load_cfg(cfg_file, NULL, NULL, quiet);
load_cfg(cfg_file, NULL, NULL, flags, 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
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 flags,
struct cmdq_item **new_item)
{
FILE *f;
const char delim[3] = { '\\', '\\', '\0' };
u_int found = 0;
size_t line = 0;
char *buf, *cause1, *p, *q;
struct cmd_list *cmdlist;
struct cmdq_item *new_item;
struct cfg_cond *cond, *cond1;
struct cfg_conds conds;
struct cmd_parse_input pi;
struct cmd_parse_result *pr;
struct cmdq_item *new_item0;
TAILQ_INIT(&conds);
if (new_item != NULL)
*new_item = NULL;
log_debug("loading %s", path);
if ((f = fopen(path, "rb")) == NULL) {
if (errno == ENOENT && quiet)
if (errno == ENOENT && (flags & CMD_PARSE_QUIET))
return (0);
cfg_add_cause("%s: %s", path, strerror(errno));
return (-1);
}
while ((buf = fparseln(f, NULL, &line, delim, 0)) != NULL) {
log_debug("%s: %s", path, buf);
memset(&pi, 0, sizeof pi);
pi.flags = flags;
pi.file = path;
pi.line = 1;
p = buf;
while (isspace((u_char)*p))
p++;
if (*p == '\0') {
free(buf);
continue;
}
q = p + strlen(p) - 1;
while (q != p && isspace((u_char)*q))
*q-- = '\0';
if (*p == '%') {
cfg_handle_directive(p, path, line, &conds);
continue;
}
cond = TAILQ_FIRST(&conds);
if (cond != NULL && !cond->met)
continue;
cmdlist = cmd_string_parse(p, path, line, &cause1);
if (cmdlist == NULL) {
free(buf);
if (cause1 == NULL)
continue;
cfg_add_cause("%s:%zu: %s", path, line, cause1);
free(cause1);
continue;
}
free(buf);
new_item = cmdq_get_command(cmdlist, NULL, NULL, 0);
if (item != NULL)
cmdq_insert_after(item, new_item);
else
cmdq_append(c, new_item);
cmd_list_free(cmdlist);
found++;
}
pr = cmd_parse_from_file(f, &pi);
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);
if (pr->status == CMD_PARSE_EMPTY)
return (0);
if (pr->status == CMD_PARSE_ERROR) {
cfg_add_cause("%s", pr->error);
free(pr->error);
return (-1);
}
if (flags & CMD_PARSE_PARSEONLY) {
cmd_list_free(pr->cmdlist);
return (0);
}
return (found);
new_item0 = cmdq_get_command(pr->cmdlist, NULL, NULL, 0);
if (item != NULL)
cmdq_insert_after(item, new_item0);
else
cmdq_append(c, new_item0);
cmd_list_free(pr->cmdlist);
if (new_item != NULL)
*new_item = new_item0;
return (0);
}
void

View File

@@ -215,14 +215,13 @@ client_exit_message(void)
int
client_main(struct event_base *base, int argc, char **argv, int flags)
{
struct cmd_parse_result *pr;
struct cmd *cmd;
struct cmd_list *cmdlist;
struct msg_command_data *data;
int cmdflags, fd, i;
const char *ttynam, *cwd;
pid_t ppid;
enum msgtype msg;
char *cause;
struct termios tio, saved_tio;
size_t size;
@@ -248,14 +247,15 @@ client_main(struct event_base *base, int argc, char **argv, int flags)
* later in server) but it is necessary to get the start server
* flag.
*/
cmdlist = cmd_list_parse(argc, argv, NULL, 0, &cause);
if (cmdlist != NULL) {
TAILQ_FOREACH(cmd, &cmdlist->list, qentry) {
pr = cmd_parse_from_arguments(argc, argv, NULL);
if (pr->status == CMD_PARSE_SUCCESS) {
TAILQ_FOREACH(cmd, &pr->cmdlist->list, qentry) {
if (cmd->entry->flags & CMD_STARTSERVER)
cmdflags |= CMD_STARTSERVER;
}
cmd_list_free(cmdlist);
}
cmd_list_free(pr->cmdlist);
} else
free(pr->error);
}
/* Create client process structure (starts logging). */

View File

@@ -87,7 +87,7 @@ cmd_attach_session(struct cmdq_item *item, const char *tflag, int dflag,
if (wl != NULL) {
if (wp != NULL)
window_set_active_pane(wp->window, wp);
window_set_active_pane(wp->window, wp, 1);
session_set_current(s, wl);
if (wp != NULL)
cmd_find_from_winlink_pane(current, wl, wp, 0);

View File

@@ -44,15 +44,16 @@ const struct cmd_entry cmd_bind_key_entry = {
static enum cmd_retval
cmd_bind_key_exec(struct cmd *self, struct cmdq_item *item)
{
struct args *args = self->args;
char *cause;
struct cmd_list *cmdlist;
key_code key;
const char *tablename;
struct args *args = self->args;
key_code key;
const char *tablename;
struct cmd_parse_result *pr;
char **argv = args->argv;
int argc = args->argc;
key = key_string_lookup_string(args->argv[0]);
key = key_string_lookup_string(argv[0]);
if (key == KEYC_NONE || key == KEYC_UNKNOWN) {
cmdq_error(item, "unknown key: %s", args->argv[0]);
cmdq_error(item, "unknown key: %s", argv[0]);
return (CMD_RETURN_ERROR);
}
@@ -63,14 +64,21 @@ cmd_bind_key_exec(struct cmd *self, struct cmdq_item *item)
else
tablename = "prefix";
cmdlist = cmd_list_parse(args->argc - 1, args->argv + 1, NULL, 0,
&cause);
if (cmdlist == NULL) {
cmdq_error(item, "%s", cause);
free(cause);
if (argc == 2)
pr = cmd_parse_from_string(argv[1], NULL);
else
pr = cmd_parse_from_arguments(argc - 1, argv + 1, NULL);
switch (pr->status) {
case CMD_PARSE_EMPTY:
cmdq_error(item, "empty command");
return (CMD_RETURN_ERROR);
case CMD_PARSE_ERROR:
cmdq_error(item, "%s", pr->error);
free(pr->error);
return (CMD_RETURN_ERROR);
case CMD_PARSE_SUCCESS:
break;
}
key_bindings_add(tablename, key, args_has(args, 'r'), cmdlist);
key_bindings_add(tablename, key, args_has(args, 'r'), pr->cmdlist);
return (CMD_RETURN_NORMAL);
}

View File

@@ -129,26 +129,15 @@ cmd_command_prompt_exec(struct cmd *self, struct cmdq_item *item)
return (CMD_RETURN_NORMAL);
}
static enum cmd_retval
cmd_command_prompt_error(struct cmdq_item *item, void *data)
{
char *error = data;
cmdq_error(item, "%s", error);
free(error);
return (CMD_RETURN_NORMAL);
}
static int
cmd_command_prompt_callback(struct client *c, void *data, const char *s,
int done)
{
struct cmd_command_prompt_cdata *cdata = data;
struct cmd_list *cmdlist;
struct cmdq_item *new_item;
char *cause, *new_template, *prompt, *ptr;
char *new_template, *prompt, *ptr;
char *input = NULL;
struct cmd_parse_result *pr;
if (s == NULL)
return (0);
@@ -175,20 +164,22 @@ cmd_command_prompt_callback(struct client *c, void *data, const char *s,
return (1);
}
cmdlist = cmd_string_parse(new_template, NULL, 0, &cause);
if (cmdlist == NULL) {
if (cause != NULL) {
new_item = cmdq_get_callback(cmd_command_prompt_error,
cause);
} else
new_item = NULL;
} else {
new_item = cmdq_get_command(cmdlist, NULL, NULL, 0);
cmd_list_free(cmdlist);
}
if (new_item != NULL)
pr = cmd_parse_from_string(new_template, NULL);
switch (pr->status) {
case CMD_PARSE_EMPTY:
new_item = NULL;
break;
case CMD_PARSE_ERROR:
new_item = cmdq_get_error(pr->error);
free(pr->error);
cmdq_append(c, new_item);
break;
case CMD_PARSE_SUCCESS:
new_item = cmdq_get_command(pr->cmdlist, NULL, NULL, 0);
cmd_list_free(pr->cmdlist);
cmdq_append(c, new_item);
break;
}
if (!done)
free(new_template);

View File

@@ -82,48 +82,38 @@ cmd_confirm_before_exec(struct cmd *self, struct cmdq_item *item)
return (CMD_RETURN_NORMAL);
}
static enum cmd_retval
cmd_confirm_before_error(struct cmdq_item *item, void *data)
{
char *error = data;
cmdq_error(item, "%s", error);
free(error);
return (CMD_RETURN_NORMAL);
}
static int
cmd_confirm_before_callback(struct client *c, void *data, const char *s,
__unused int done)
{
struct cmd_confirm_before_data *cdata = data;
struct cmd_list *cmdlist;
struct cmdq_item *new_item;
char *cause;
struct cmd_parse_result *pr;
if (c->flags & CLIENT_DEAD)
return (0);
if (s == NULL || *s == '\0')
return (0);
if (tolower((u_char) s[0]) != 'y' || s[1] != '\0')
if (tolower((u_char)s[0]) != 'y' || s[1] != '\0')
return (0);
cmdlist = cmd_string_parse(cdata->cmd, NULL, 0, &cause);
if (cmdlist == NULL) {
if (cause != NULL) {
new_item = cmdq_get_callback(cmd_confirm_before_error,
cause);
} else
new_item = NULL;
} else {
new_item = cmdq_get_command(cmdlist, NULL, NULL, 0);
cmd_list_free(cmdlist);
}
if (new_item != NULL)
pr = cmd_parse_from_string(cdata->cmd, NULL);
switch (pr->status) {
case CMD_PARSE_EMPTY:
new_item = NULL;
break;
case CMD_PARSE_ERROR:
new_item = cmdq_get_error(pr->error);
free(pr->error);
cmdq_append(c, new_item);
break;
case CMD_PARSE_SUCCESS:
new_item = cmdq_get_command(pr->cmdlist, NULL, NULL, 0);
cmd_list_free(pr->cmdlist);
cmdq_append(c, new_item);
break;
}
return (0);
}

View File

@@ -73,10 +73,10 @@ cmd_copy_mode_exec(struct cmd *self, struct cmdq_item *item)
return (CMD_RETURN_NORMAL);
}
if (window_pane_set_mode(wp, &window_copy_mode, NULL, args) != 0)
return (CMD_RETURN_NORMAL);
if (args_has(args, 'M'))
window_copy_start_drag(c, &shared->mouse);
if (!window_pane_set_mode(wp, &window_copy_mode, NULL, args)) {
if (args_has(args, 'M'))
window_copy_start_drag(c, &shared->mouse);
}
if (args_has(self->args, 'u'))
window_copy_pageup(wp, 0);

178
cmd-display-menu.c Normal file
View File

@@ -0,0 +1,178 @@
/* $OpenBSD$ */
/*
* Copyright (c) 2019 Nicholas Marriott <nicholas.marriott@gmail.com>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER
* IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
* OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include <sys/types.h>
#include <stdlib.h>
#include <string.h>
#include "tmux.h"
/*
* Display a menu on a client.
*/
static enum cmd_retval cmd_display_menu_exec(struct cmd *,
struct cmdq_item *);
const struct cmd_entry cmd_display_menu_entry = {
.name = "display-menu",
.alias = "menu",
.args = { "c:t:T:x:y:", 1, -1 },
.usage = "[-c target-client] " CMD_TARGET_PANE_USAGE " [-T title] "
"[-x position] [-y position] name key command ...",
.target = { 't', CMD_FIND_PANE, 0 },
.flags = CMD_AFTERHOOK,
.exec = cmd_display_menu_exec
};
static enum cmd_retval
cmd_display_menu_exec(struct cmd *self, struct cmdq_item *item)
{
struct args *args = self->args;
struct client *c;
struct session *s = item->target.s;
struct winlink *wl = item->target.wl;
struct window_pane *wp = item->target.wp;
struct cmd_find_state *fs = &item->target;
struct menu *menu = NULL;
struct style_range *sr;
struct menu_item menu_item;
const char *xp, *yp, *key;
char *title, *name;
int at, flags, i;
u_int px, py, ox, oy, sx, sy;
if ((c = cmd_find_client(item, args_get(args, 'c'), 0)) == NULL)
return (CMD_RETURN_ERROR);
if (c->overlay_draw != NULL)
return (CMD_RETURN_NORMAL);
at = status_at_line(c);
if (args_has(args, 'T'))
title = format_single(NULL, args_get(args, 'T'), c, s, wl, wp);
else
title = xstrdup("");
menu = menu_create(title);
for (i = 0; i != args->argc; /* nothing */) {
name = args->argv[i++];
if (*name == '\0') {
menu_add_item(menu, NULL, item, c, fs);
continue;
}
if (args->argc - i < 2) {
cmdq_error(item, "not enough arguments");
free(title);
menu_free(menu);
return (CMD_RETURN_ERROR);
}
key = args->argv[i++];
menu_item.name = name;
menu_item.key = key_string_lookup_string(key);
menu_item.command = args->argv[i++];
menu_add_item(menu, &menu_item, item, c, fs);
}
free(title);
if (menu == NULL) {
cmdq_error(item, "invalid menu arguments");
return (CMD_RETURN_ERROR);
}
if (menu->count == 0) {
menu_free(menu);
return (CMD_RETURN_NORMAL);
}
xp = args_get(args, 'x');
if (xp == NULL)
px = 0;
else if (strcmp(xp, "R") == 0)
px = c->tty.sx - 1;
else if (strcmp(xp, "P") == 0) {
tty_window_offset(&c->tty, &ox, &oy, &sx, &sy);
if (wp->xoff >= ox)
px = wp->xoff - ox;
else
px = 0;
} else if (strcmp(xp, "M") == 0 && item->shared->mouse.valid) {
if (item->shared->mouse.x > (menu->width + 4) / 2)
px = item->shared->mouse.x - (menu->width + 4) / 2;
else
px = 0;
}
else if (strcmp(xp, "W") == 0) {
if (at == -1)
px = 0;
else {
TAILQ_FOREACH(sr, &c->status.entries[0].ranges, entry) {
if (sr->type != STYLE_RANGE_WINDOW)
continue;
if (sr->argument == (u_int)wl->idx)
break;
}
if (sr != NULL)
px = sr->start;
else
px = 0;
}
} else
px = strtoul(xp, NULL, 10);
if (px + menu->width + 4 >= c->tty.sx)
px = c->tty.sx - menu->width - 4;
yp = args_get(args, 'y');
if (yp == NULL)
py = 0;
else if (strcmp(yp, "P") == 0) {
tty_window_offset(&c->tty, &ox, &oy, &sx, &sy);
if (wp->yoff + wp->sy >= oy)
py = wp->yoff + wp->sy - oy;
else
py = 0;
} else if (strcmp(yp, "M") == 0 && item->shared->mouse.valid)
py = item->shared->mouse.y + menu->count + 2;
else if (strcmp(yp, "S") == 0) {
if (at == -1)
py = c->tty.sy;
else if (at == 0)
py = status_line_size(c) + menu->count + 2;
else
py = at;
} else
py = strtoul(yp, NULL, 10);
if (py < menu->count + 2)
py = 0;
else
py -= menu->count + 2;
if (py + menu->count + 2 >= c->tty.sy)
py = c->tty.sy - menu->count - 2;
flags = 0;
if (!item->shared->mouse.valid)
flags |= MENU_NOMOUSE;
if (menu_display(menu, flags, item, px, py, c, fs, NULL, NULL) != 0)
return (CMD_RETURN_NORMAL);
return (CMD_RETURN_WAIT);
}

View File

@@ -39,8 +39,8 @@ const struct cmd_entry cmd_display_message_entry = {
.name = "display-message",
.alias = "display",
.args = { "ac:pt:F:v", 0, 1 },
.usage = "[-apv] [-c target-client] [-F format] "
.args = { "ac:Ipt:F:v", 0, 1 },
.usage = "[-aIpv] [-c target-client] [-F format] "
CMD_TARGET_PANE_USAGE " [message]",
.target = { 't', CMD_FIND_PANE, 0 },
@@ -66,10 +66,19 @@ cmd_display_message_exec(struct cmd *self, struct cmdq_item *item)
struct winlink *wl = item->target.wl;
struct window_pane *wp = item->target.wp;
const char *template;
char *msg;
char *msg, *cause;
struct format_tree *ft;
int flags;
if (args_has(args, 'I')) {
if (window_pane_start_input(wp, item, &cause) != 0) {
cmdq_error(item, "%s", cause);
free(cause);
return (CMD_RETURN_ERROR);
}
return (CMD_RETURN_WAIT);
}
if (args_has(args, 'F') && args->argc != 0) {
cmdq_error(item, "only one of -F or argument must be given");
return (CMD_RETURN_ERROR);

View File

@@ -18,8 +18,8 @@
#include <sys/types.h>
#include <ctype.h>
#include <stdlib.h>
#include <string.h>
#include "tmux.h"
@@ -30,9 +30,6 @@
static enum cmd_retval cmd_display_panes_exec(struct cmd *,
struct cmdq_item *);
static void cmd_display_panes_callback(struct client *,
struct window_pane *);
const struct cmd_entry cmd_display_panes_entry = {
.name = "display-panes",
.alias = "displayp",
@@ -44,32 +41,218 @@ const struct cmd_entry cmd_display_panes_entry = {
.exec = cmd_display_panes_exec
};
struct cmd_display_panes_data {
struct cmdq_item *item;
char *command;
};
static void
cmd_display_panes_draw_pane(struct screen_redraw_ctx *ctx,
struct window_pane *wp)
{
struct client *c = ctx->c;
struct tty *tty = &c->tty;
struct session *s = c->session;
struct options *oo = s->options;
struct window *w = wp->window;
struct grid_cell gc;
u_int idx, px, py, i, j, xoff, yoff, sx, sy;
int colour, active_colour;
char buf[16], *ptr;
size_t len;
if (wp->xoff + wp->sx <= ctx->ox ||
wp->xoff >= ctx->ox + ctx->sx ||
wp->yoff + wp->sy <= ctx->oy ||
wp->yoff >= ctx->oy + ctx->sy)
return;
if (wp->xoff >= ctx->ox && wp->xoff + wp->sx <= ctx->ox + ctx->sx) {
/* All visible. */
xoff = wp->xoff - ctx->ox;
sx = wp->sx;
} else if (wp->xoff < ctx->ox &&
wp->xoff + wp->sx > ctx->ox + ctx->sx) {
/* Both left and right not visible. */
xoff = 0;
sx = ctx->sx;
} else if (wp->xoff < ctx->ox) {
/* Left not visible. */
xoff = 0;
sx = wp->sx - (ctx->ox - wp->xoff);
} else {
/* Right not visible. */
xoff = wp->xoff - ctx->ox;
sx = wp->sx - xoff;
}
if (wp->yoff >= ctx->oy && wp->yoff + wp->sy <= ctx->oy + ctx->sy) {
/* All visible. */
yoff = wp->yoff - ctx->oy;
sy = wp->sy;
} else if (wp->yoff < ctx->oy &&
wp->yoff + wp->sy > ctx->oy + ctx->sy) {
/* Both top and bottom not visible. */
yoff = 0;
sy = ctx->sy;
} else if (wp->yoff < ctx->oy) {
/* Top not visible. */
yoff = 0;
sy = wp->sy - (ctx->oy - wp->yoff);
} else {
/* Bottom not visible. */
yoff = wp->yoff - ctx->oy;
sy = wp->sy - yoff;
}
if (ctx->statustop)
yoff += ctx->statuslines;
px = sx / 2;
py = sy / 2;
if (window_pane_index(wp, &idx) != 0)
fatalx("index not found");
len = xsnprintf(buf, sizeof buf, "%u", idx);
if (sx < len)
return;
colour = options_get_number(oo, "display-panes-colour");
active_colour = options_get_number(oo, "display-panes-active-colour");
if (sx < len * 6 || sy < 5) {
tty_cursor(tty, xoff + px - len / 2, yoff + py);
goto draw_text;
}
px -= len * 3;
py -= 2;
memcpy(&gc, &grid_default_cell, sizeof gc);
if (w->active == wp)
gc.bg = active_colour;
else
gc.bg = colour;
gc.flags |= GRID_FLAG_NOPALETTE;
tty_attributes(tty, &gc, wp);
for (ptr = buf; *ptr != '\0'; ptr++) {
if (*ptr < '0' || *ptr > '9')
continue;
idx = *ptr - '0';
for (j = 0; j < 5; j++) {
for (i = px; i < px + 5; i++) {
tty_cursor(tty, xoff + i, yoff + py + j);
if (window_clock_table[idx][j][i - px])
tty_putc(tty, ' ');
}
}
px += 6;
}
len = xsnprintf(buf, sizeof buf, "%ux%u", wp->sx, wp->sy);
if (sx < len || sy < 6)
return;
tty_cursor(tty, xoff + sx - len, yoff);
draw_text:
memcpy(&gc, &grid_default_cell, sizeof gc);
if (w->active == wp)
gc.fg = active_colour;
else
gc.fg = colour;
gc.flags |= GRID_FLAG_NOPALETTE;
tty_attributes(tty, &gc, wp);
tty_puts(tty, buf);
tty_cursor(tty, 0, 0);
}
static void
cmd_display_panes_draw(struct client *c, struct screen_redraw_ctx *ctx)
{
struct window *w = c->session->curw->window;
struct window_pane *wp;
log_debug("%s: %s @%u", __func__, c->name, w->id);
TAILQ_FOREACH(wp, &w->panes, entry) {
if (window_pane_visible(wp))
cmd_display_panes_draw_pane(ctx, wp);
}
}
static void
cmd_display_panes_free(struct client *c)
{
struct cmd_display_panes_data *cdata = c->overlay_data;
if (cdata->item != NULL)
cdata->item->flags &= ~CMDQ_WAITING;
free(cdata->command);
free(cdata);
}
static int
cmd_display_panes_key(struct client *c, struct key_event *event)
{
struct cmd_display_panes_data *cdata = c->overlay_data;
struct cmdq_item *new_item;
char *cmd, *expanded;
struct window *w = c->session->curw->window;
struct window_pane *wp;
struct cmd_parse_result *pr;
if (event->key < '0' || event->key > '9')
return (1);
wp = window_pane_at_index(w, event->key - '0');
if (wp == NULL)
return (1);
window_unzoom(w);
xasprintf(&expanded, "%%%u", wp->id);
cmd = cmd_template_replace(cdata->command, expanded, 1);
pr = cmd_parse_from_string(cmd, NULL);
switch (pr->status) {
case CMD_PARSE_EMPTY:
new_item = NULL;
break;
case CMD_PARSE_ERROR:
new_item = cmdq_get_error(pr->error);
free(pr->error);
cmdq_append(c, new_item);
break;
case CMD_PARSE_SUCCESS:
new_item = cmdq_get_command(pr->cmdlist, NULL, NULL, 0);
cmd_list_free(pr->cmdlist);
cmdq_append(c, new_item);
break;
}
free(cmd);
free(expanded);
return (1);
}
static enum cmd_retval
cmd_display_panes_exec(struct cmd *self, struct cmdq_item *item)
{
struct args *args = self->args;
struct client *c;
struct session *s;
u_int delay;
char *cause;
struct args *args = self->args;
struct client *c;
struct session *s;
u_int delay;
char *cause;
struct cmd_display_panes_data *cdata;
if ((c = cmd_find_client(item, args_get(args, 't'), 0)) == NULL)
return (CMD_RETURN_ERROR);
s = c->session;
if (c->identify_callback != NULL)
if (c->overlay_draw != NULL)
return (CMD_RETURN_NORMAL);
c->identify_callback = cmd_display_panes_callback;
if (args->argc != 0)
c->identify_callback_data = xstrdup(args->argv[0]);
else
c->identify_callback_data = xstrdup("select-pane -t '%%'");
if (args_has(args, 'b'))
c->identify_callback_item = NULL;
else
c->identify_callback_item = item;
if (args_has(args, 'd')) {
delay = args_strtonum(args, 'd', 0, UINT_MAX, &cause);
if (cause != NULL) {
@@ -79,65 +262,21 @@ cmd_display_panes_exec(struct cmd *self, struct cmdq_item *item)
}
} else
delay = options_get_number(s->options, "display-panes-time");
server_client_set_identify(c, delay);
cdata = xmalloc(sizeof *cdata);
if (args->argc != 0)
cdata->command = xstrdup(args->argv[0]);
else
cdata->command = xstrdup("select-pane -t '%%'");
if (args_has(args, 'b'))
cdata->item = NULL;
else
cdata->item = item;
server_client_set_overlay(c, delay, cmd_display_panes_draw,
cmd_display_panes_key, cmd_display_panes_free, cdata);
if (args_has(args, 'b'))
return (CMD_RETURN_NORMAL);
return (CMD_RETURN_WAIT);
}
static enum cmd_retval
cmd_display_panes_error(struct cmdq_item *item, void *data)
{
char *error = data;
cmdq_error(item, "%s", error);
free(error);
return (CMD_RETURN_NORMAL);
}
static void
cmd_display_panes_callback(struct client *c, struct window_pane *wp)
{
struct cmd_list *cmdlist;
struct cmdq_item *new_item;
char *cmd, *expanded, *cause;
if (wp == NULL)
goto out;
xasprintf(&expanded, "%%%u", wp->id);
cmd = cmd_template_replace(c->identify_callback_data, expanded, 1);
cmdlist = cmd_string_parse(cmd, NULL, 0, &cause);
if (cmdlist == NULL && cause != NULL)
new_item = cmdq_get_callback(cmd_display_panes_error, cause);
else if (cmdlist == NULL)
new_item = NULL;
else {
new_item = cmdq_get_command(cmdlist, NULL, NULL, 0);
cmd_list_free(cmdlist);
}
if (new_item != NULL) {
if (c->identify_callback_item != NULL)
cmdq_insert_after(c->identify_callback_item, new_item);
else
cmdq_append(c, new_item);
}
free(cmd);
free(expanded);
out:
if (c->identify_callback_item != NULL) {
c->identify_callback_item->flags &= ~CMDQ_WAITING;
c->identify_callback_item = NULL;
}
free(c->identify_callback_data);
c->identify_callback_data = NULL;
c->identify_callback = NULL;
}

View File

@@ -1039,12 +1039,16 @@ cmd_find_target(struct cmd_find_state *fs, struct cmdq_item *item,
switch (type) {
case CMD_FIND_PANE:
fs->wp = cmd_mouse_pane(m, &fs->s, &fs->wl);
if (fs->wp != NULL)
if (fs->wp != NULL) {
fs->w = fs->wl->window;
break;
break;
}
/* FALLTHROUGH */
case CMD_FIND_WINDOW:
case CMD_FIND_SESSION:
fs->wl = cmd_mouse_window(m, &fs->s);
if (fs->wl == NULL && fs->s != NULL)
fs->wl = fs->s->curw;
if (fs->wl != NULL) {
fs->w = fs->wl->window;
fs->wp = fs->w->active;

View File

@@ -49,8 +49,7 @@ const struct cmd_entry cmd_if_shell_entry = {
};
struct cmd_if_shell_data {
char *file;
u_int line;
struct cmd_parse_input input;
char *cmd_if;
char *cmd_else;
@@ -64,51 +63,62 @@ static enum cmd_retval
cmd_if_shell_exec(struct cmd *self, struct cmdq_item *item)
{
struct args *args = self->args;
struct cmdq_shared *shared = item->shared;
struct mouse_event *m = &item->shared->mouse;
struct cmd_if_shell_data *cdata;
char *shellcmd, *cmd, *cause;
struct cmd_list *cmdlist;
char *shellcmd, *cmd;
struct cmdq_item *new_item;
struct client *c = cmd_find_client(item, NULL, 1);
struct session *s = item->target.s;
struct winlink *wl = item->target.wl;
struct window_pane *wp = item->target.wp;
struct cmd_parse_input pi;
struct cmd_parse_result *pr;
shellcmd = format_single(item, args->argv[0], c, s, wl, wp);
if (args_has(args, 'F')) {
cmd = NULL;
if (*shellcmd != '0' && *shellcmd != '\0')
cmd = args->argv[1];
else if (args->argc == 3)
cmd = args->argv[2];
else
cmd = NULL;
free(shellcmd);
if (cmd == NULL)
return (CMD_RETURN_NORMAL);
cmdlist = cmd_string_parse(cmd, NULL, 0, &cause);
if (cmdlist == NULL) {
if (cause != NULL) {
cmdq_error(item, "%s", cause);
free(cause);
}
memset(&pi, 0, sizeof pi);
if (self->file != NULL)
pi.file = self->file;
pi.line = self->line;
pi.item = item;
pi.c = c;
cmd_find_copy_state(&pi.fs, &item->target);
pr = cmd_parse_from_string(cmd, &pi);
switch (pr->status) {
case CMD_PARSE_EMPTY:
break;
case CMD_PARSE_ERROR:
cmdq_error(item, "%s", pr->error);
free(pr->error);
return (CMD_RETURN_ERROR);
case CMD_PARSE_SUCCESS:
new_item = cmdq_get_command(pr->cmdlist, NULL, m, 0);
cmdq_insert_after(item, new_item);
cmd_list_free(pr->cmdlist);
break;
}
new_item = cmdq_get_command(cmdlist, NULL, &shared->mouse, 0);
cmdq_insert_after(item, new_item);
cmd_list_free(cmdlist);
return (CMD_RETURN_NORMAL);
}
cdata = xcalloc(1, sizeof *cdata);
if (self->file != NULL) {
cdata->file = xstrdup(self->file);
cdata->line = self->line;
}
cdata->cmd_if = xstrdup(args->argv[1]);
if (args->argc == 3)
cdata->cmd_else = xstrdup(args->argv[2]);
else
cdata->cmd_else = NULL;
memcpy(&cdata->mouse, m, sizeof cdata->mouse);
cdata->client = item->client;
if (cdata->client != NULL)
@@ -118,7 +128,16 @@ cmd_if_shell_exec(struct cmd *self, struct cmdq_item *item)
cdata->item = item;
else
cdata->item = NULL;
memcpy(&cdata->mouse, &shared->mouse, sizeof cdata->mouse);
memset(&cdata->input, 0, sizeof cdata->input);
if (self->file != NULL)
cdata->input.file = xstrdup(self->file);
cdata->input.line = self->line;
cdata->input.item = cdata->item;
cdata->input.c = c;
if (cdata->input.c != NULL)
cdata->input.c->references++;
cmd_find_copy_state(&cdata->input.fs, &item->target);
if (job_run(shellcmd, s, server_client_get_cwd(item->client, s), NULL,
cmd_if_shell_callback, cmd_if_shell_free, cdata, 0) == NULL) {
@@ -139,11 +158,11 @@ cmd_if_shell_callback(struct job *job)
{
struct cmd_if_shell_data *cdata = job_get_data(job);
struct client *c = cdata->client;
struct cmd_list *cmdlist;
struct cmdq_item *new_item;
char *cause, *cmd, *file = cdata->file;
u_int line = cdata->line;
struct mouse_event *m = &cdata->mouse;
struct cmdq_item *new_item = NULL;
char *cmd;
int status;
struct cmd_parse_result *pr;
status = job_get_status(job);
if (!WIFEXITED(status) || WEXITSTATUS(status) != 0)
@@ -153,17 +172,20 @@ cmd_if_shell_callback(struct job *job)
if (cmd == NULL)
goto out;
cmdlist = cmd_string_parse(cmd, file, line, &cause);
if (cmdlist == NULL) {
if (cause != NULL && cdata->item != NULL)
cmdq_error(cdata->item, "%s", cause);
free(cause);
new_item = NULL;
} else {
new_item = cmdq_get_command(cmdlist, NULL, &cdata->mouse, 0);
cmd_list_free(cmdlist);
pr = cmd_parse_from_string(cmd, &cdata->input);
switch (pr->status) {
case CMD_PARSE_EMPTY:
break;
case CMD_PARSE_ERROR:
if (cdata->item != NULL)
cmdq_error(cdata->item, "%s", pr->error);
free(pr->error);
break;
case CMD_PARSE_SUCCESS:
new_item = cmdq_get_command(pr->cmdlist, NULL, m, 0);
cmd_list_free(pr->cmdlist);
break;
}
if (new_item != NULL) {
if (cdata->item == NULL)
cmdq_append(c, new_item);
@@ -187,6 +209,9 @@ cmd_if_shell_free(void *data)
free(cdata->cmd_else);
free(cdata->cmd_if);
free(cdata->file);
if (cdata->input.c != NULL)
server_client_unref(cdata->input.c);
free((void *)cdata->input.file);
free(cdata);
}

View File

@@ -71,7 +71,7 @@ cmd_join_pane_exec(struct cmd *self, struct cmdq_item *item)
int size, percentage, dst_idx;
enum layout_type type;
struct layout_cell *lc;
int not_same_window;
int not_same_window, flags;
if (self->entry == &cmd_join_pane_entry)
not_same_window = 1;
@@ -123,7 +123,11 @@ cmd_join_pane_exec(struct cmd *self, struct cmdq_item *item)
else
size = (dst_wp->sx * percentage) / 100;
}
lc = layout_split_pane(dst_wp, type, size, args_has(args, 'b'), 0);
if (args_has(args, 'b'))
flags = SPAWN_BEFORE;
else
flags = 0;
lc = layout_split_pane(dst_wp, type, size, flags);
if (lc == NULL) {
cmdq_error(item, "create pane failed: pane too small");
return (CMD_RETURN_ERROR);
@@ -144,7 +148,7 @@ cmd_join_pane_exec(struct cmd *self, struct cmdq_item *item)
server_redraw_window(dst_w);
if (!args_has(args, 'd')) {
window_set_active_pane(dst_w, src_wp);
window_set_active_pane(dst_w, src_wp, 1);
session_select(dst_s, dst_idx);
cmd_find_from_session(current, dst_s, 0);
server_redraw_session(dst_s);

View File

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

View File

@@ -60,8 +60,8 @@ cmd_list_keys_exec(struct cmd *self, struct cmdq_item *item)
struct args *args = self->args;
struct key_table *table;
struct key_binding *bd;
const char *key, *tablename, *r;
char *cp, tmp[BUFSIZ];
const char *tablename, *r;
char *key, *cp, tmp[BUFSIZ];
int repeat, width, tablewidth, keywidth;
if (self->entry == &cmd_list_commands_entry)
@@ -83,7 +83,7 @@ cmd_list_keys_exec(struct cmd *self, struct cmdq_item *item)
}
bd = key_bindings_first(table);
while (bd != NULL) {
key = key_string_lookup_key(bd->key);
key = args_escape(key_string_lookup_key(bd->key));
if (bd->flags & KEY_BINDING_REPEAT)
repeat = 1;
@@ -95,6 +95,7 @@ cmd_list_keys_exec(struct cmd *self, struct cmdq_item *item)
if (width > keywidth)
keywidth = width;
free(key);
bd = key_bindings_next(table, bd);
}
table = key_bindings_next_table(table);
@@ -108,7 +109,7 @@ cmd_list_keys_exec(struct cmd *self, struct cmdq_item *item)
}
bd = key_bindings_first(table);
while (bd != NULL) {
key = key_string_lookup_key(bd->key);
key = args_escape(key_string_lookup_key(bd->key));
if (!repeat)
r = "";
@@ -128,11 +129,13 @@ cmd_list_keys_exec(struct cmd *self, struct cmdq_item *item)
strlcat(tmp, " ", sizeof tmp);
free(cp);
cp = cmd_list_print(bd->cmdlist);
cp = cmd_list_print(bd->cmdlist, 1);
strlcat(tmp, cp, sizeof tmp);
free(cp);
cmdq_print(item, "bind-key %s", tmp);
free(key);
bd = key_bindings_next(table, bd);
}
table = key_bindings_next_table(table);

View File

@@ -23,62 +23,37 @@
#include "tmux.h"
static u_int cmd_list_next_group = 1;
struct cmd_list *
cmd_list_parse(int argc, char **argv, const char *file, u_int line,
char **cause)
cmd_list_new(void)
{
struct cmd_list *cmdlist;
struct cmd *cmd;
int i, lastsplit;
size_t arglen, new_argc;
char **copy_argv, **new_argv;
copy_argv = cmd_copy_argv(argc, argv);
cmdlist = xcalloc(1, sizeof *cmdlist);
cmdlist->references = 1;
cmdlist->group = cmd_list_next_group++;
TAILQ_INIT(&cmdlist->list);
lastsplit = 0;
for (i = 0; i < argc; i++) {
arglen = strlen(copy_argv[i]);
if (arglen == 0 || copy_argv[i][arglen - 1] != ';')
continue;
copy_argv[i][arglen - 1] = '\0';
if (arglen > 1 && copy_argv[i][arglen - 2] == '\\') {
copy_argv[i][arglen - 2] = ';';
continue;
}
new_argc = i - lastsplit;
new_argv = copy_argv + lastsplit;
if (arglen != 1)
new_argc++;
cmd = cmd_parse(new_argc, new_argv, file, line, cause);
if (cmd == NULL)
goto bad;
TAILQ_INSERT_TAIL(&cmdlist->list, cmd, qentry);
lastsplit = i + 1;
}
if (lastsplit != argc) {
cmd = cmd_parse(argc - lastsplit, copy_argv + lastsplit,
file, line, cause);
if (cmd == NULL)
goto bad;
TAILQ_INSERT_TAIL(&cmdlist->list, cmd, qentry);
}
cmd_free_argv(argc, copy_argv);
return (cmdlist);
}
bad:
cmd_list_free(cmdlist);
cmd_free_argv(argc, copy_argv);
return (NULL);
void
cmd_list_append(struct cmd_list *cmdlist, struct cmd *cmd)
{
cmd->group = cmdlist->group;
TAILQ_INSERT_TAIL(&cmdlist->list, cmd, qentry);
}
void
cmd_list_move(struct cmd_list *cmdlist, struct cmd_list *from)
{
struct cmd *cmd, *cmd1;
TAILQ_FOREACH_SAFE(cmd, &from->list, qentry, cmd1) {
TAILQ_REMOVE(&from->list, cmd, qentry);
TAILQ_INSERT_TAIL(&cmdlist->list, cmd, qentry);
}
cmdlist->group = cmd_list_next_group++;
}
void
@@ -91,16 +66,14 @@ cmd_list_free(struct cmd_list *cmdlist)
TAILQ_FOREACH_SAFE(cmd, &cmdlist->list, qentry, cmd1) {
TAILQ_REMOVE(&cmdlist->list, cmd, qentry);
args_free(cmd->args);
free(cmd->file);
free(cmd);
cmd_free(cmd);
}
free(cmdlist);
}
char *
cmd_list_print(struct cmd_list *cmdlist)
cmd_list_print(struct cmd_list *cmdlist, int escaped)
{
struct cmd *cmd;
char *buf, *this;
@@ -112,12 +85,16 @@ cmd_list_print(struct cmd_list *cmdlist)
TAILQ_FOREACH(cmd, &cmdlist->list, qentry) {
this = cmd_print(cmd);
len += strlen(this) + 3;
len += strlen(this) + 4;
buf = xrealloc(buf, len);
strlcat(buf, this, len);
if (TAILQ_NEXT(cmd, qentry) != NULL)
strlcat(buf, " ; ", len);
if (TAILQ_NEXT(cmd, qentry) != NULL) {
if (escaped)
strlcat(buf, " \\; ", len);
else
strlcat(buf, " ; ", len);
}
free(this);
}

View File

@@ -93,7 +93,7 @@ cmd_load_buffer_exec(struct cmd *self, struct cmdq_item *item)
return (CMD_RETURN_WAIT);
}
file = server_client_get_path(c, path);
file = server_client_get_path(item->client, path);
free(path);
f = fopen(file, "rb");

View File

@@ -69,20 +69,17 @@ cmd_new_session_exec(struct cmd *self, struct cmdq_item *item)
struct args *args = self->args;
struct client *c = item->client;
struct session *s, *as, *groupwith;
struct window *w;
struct environ *env;
struct options *oo;
struct termios tio, *tiop;
struct session_group *sg;
const char *errstr, *template, *group, *prefix;
const char *path, *cmd, *tmp, *value;
char **argv, *cause, *cp, *newname, *cwd = NULL;
int detached, already_attached, idx, argc;
int is_control = 0;
u_int sx, sy, dsx = 80, dsy = 24;
struct environ_entry *envent;
struct cmd_find_state fs;
const char *errstr, *template, *group, *prefix, *tmp;
char *cause, *cwd = NULL, *cp, *newname = NULL;
int detached, already_attached, is_control = 0;
u_int sx, sy, dsx, dsy;
struct spawn_context sc;
enum cmd_retval retval;
struct cmd_find_state fs;
if (self->entry == &cmd_has_session_entry) {
/*
@@ -97,13 +94,12 @@ cmd_new_session_exec(struct cmd *self, struct cmdq_item *item)
return (CMD_RETURN_ERROR);
}
newname = NULL;
if (args_has(args, 's')) {
newname = format_single(item, args_get(args, 's'), c, NULL,
NULL, NULL);
if (!session_check_name(newname)) {
cmdq_error(item, "bad session name: %s", newname);
goto error;
goto fail;
}
if ((as = session_find(newname)) != NULL) {
if (args_has(args, 'A')) {
@@ -114,7 +110,7 @@ cmd_new_session_exec(struct cmd *self, struct cmdq_item *item)
return (retval);
}
cmdq_error(item, "duplicate session: %s", newname);
goto error;
goto fail;
}
}
@@ -125,7 +121,7 @@ cmd_new_session_exec(struct cmd *self, struct cmdq_item *item)
if (groupwith == NULL) {
if (!session_check_name(group)) {
cmdq_error(item, "bad group name: %s", group);
goto error;
goto fail;
}
sg = session_group_find(group);
} else
@@ -173,7 +169,7 @@ cmd_new_session_exec(struct cmd *self, struct cmdq_item *item)
if (server_client_check_nested(item->client)) {
cmdq_error(item, "sessions should be nested with care, "
"unset $TMUX to force");
return (CMD_RETURN_ERROR);
goto fail;
}
if (tcgetattr(c->tty.fd, &tio) != 0)
fatal("tcgetattr failed");
@@ -186,7 +182,7 @@ cmd_new_session_exec(struct cmd *self, struct cmdq_item *item)
if (server_client_open(c, &cause) != 0) {
cmdq_error(item, "open terminal failed: %s", cause);
free(cause);
goto error;
goto fail;
}
}
@@ -196,11 +192,13 @@ cmd_new_session_exec(struct cmd *self, struct cmdq_item *item)
if (strcmp(tmp, "-") == 0) {
if (c != NULL)
dsx = c->tty.sx;
else
dsx = 80;
} else {
dsx = strtonum(tmp, 1, USHRT_MAX, &errstr);
if (errstr != NULL) {
cmdq_error(item, "width %s", errstr);
goto error;
goto fail;
}
}
}
@@ -209,11 +207,13 @@ cmd_new_session_exec(struct cmd *self, struct cmdq_item *item)
if (strcmp(tmp, "-") == 0) {
if (c != NULL)
dsy = c->tty.sy;
else
dsy = 24;
} else {
dsy = strtonum(tmp, 1, USHRT_MAX, &errstr);
if (errstr != NULL) {
cmdq_error(item, "height %s", errstr);
goto error;
goto fail;
}
}
}
@@ -225,8 +225,8 @@ cmd_new_session_exec(struct cmd *self, struct cmdq_item *item)
if (sy > 0 && options_get_number(global_s_options, "status"))
sy--;
} else {
value = options_get_string(global_s_options, "default-size");
if (sscanf(value, "%ux%u", &sx, &sy) != 2) {
tmp = options_get_string(global_s_options, "default-size");
if (sscanf(tmp, "%ux%u", &sx, &sy) != 2) {
sx = 80;
sy = 24;
}
@@ -240,59 +240,39 @@ cmd_new_session_exec(struct cmd *self, struct cmdq_item *item)
if (sy == 0)
sy = 1;
/* Figure out the command for the new window. */
argc = -1;
argv = NULL;
if (!args_has(args, 't') && args->argc != 0) {
argc = args->argc;
argv = args->argv;
} else if (sg == NULL && groupwith == NULL) {
cmd = options_get_string(global_s_options, "default-command");
if (cmd != NULL && *cmd != '\0') {
argc = 1;
argv = (char **)&cmd;
} else {
argc = 0;
argv = NULL;
}
/* Create the new session. */
oo = options_create(global_s_options);
if (args_has(args, 'x') || args_has(args, 'y')) {
if (!args_has(args, 'x'))
dsx = sx;
if (!args_has(args, 'y'))
dsy = sy;
options_set_string(oo, "default-size", 0, "%ux%u", dsx, dsy);
}
path = NULL;
if (c != NULL && c->session == NULL)
envent = environ_find(c->environ, "PATH");
else
envent = environ_find(global_environ, "PATH");
if (envent != NULL)
path = envent->value;
/* Construct the environment. */
env = environ_create();
if (c != NULL && !args_has(args, 'E'))
environ_update(global_s_options, c->environ, env);
s = session_create(prefix, newname, cwd, env, oo, tiop);
/* Set up the options. */
oo = options_create(global_s_options);
if (args_has(args, 'x') || args_has(args, 'y'))
options_set_string(oo, "default-size", 0, "%ux%u", dsx, dsy);
/* Spawn the initial window. */
memset(&sc, 0, sizeof sc);
sc.item = item;
sc.s = s;
/* Create the new session. */
idx = -1 - options_get_number(global_s_options, "base-index");
s = session_create(prefix, newname, argc, argv, path, cwd, env, oo,
tiop, idx, &cause);
environ_free(env);
if (s == NULL) {
cmdq_error(item, "create session failed: %s", cause);
sc.name = args_get(args, 'n');
sc.argc = args->argc;
sc.argv = args->argv;
sc.idx = -1;
sc.cwd = args_get(args, 'c');
sc.flags = 0;
if (spawn_window(&sc, &cause) == NULL) {
session_destroy(s, 0, __func__);
cmdq_error(item, "create window failed: %s", cause);
free(cause);
goto error;
}
/* Set the initial window name if one given. */
if (argc >= 0 && (tmp = args_get(args, 'n')) != NULL) {
cp = format_single(item, tmp, c, s, NULL, NULL);
w = s->curw->window;
window_set_name(w, cp);
options_set_number(w->options, "automatic-rename", 0);
free(cp);
goto fail;
}
/*
@@ -358,13 +338,13 @@ cmd_new_session_exec(struct cmd *self, struct cmdq_item *item)
}
cmd_find_from_session(&fs, s, 0);
hooks_insert(s->hooks, item, &fs, "after-new-session");
cmdq_insert_hook(s, item, &fs, "after-new-session");
free(cwd);
free(newname);
return (CMD_RETURN_NORMAL);
error:
fail:
free(cwd);
free(newname);
return (CMD_RETURN_ERROR);

View File

@@ -38,9 +38,9 @@ const struct cmd_entry cmd_new_window_entry = {
.name = "new-window",
.alias = "neww",
.args = { "ac:dF:kn:Pt:", 0, -1 },
.usage = "[-adkP] [-c start-directory] [-F format] [-n window-name] "
CMD_TARGET_WINDOW_USAGE " [command]",
.args = { "ac:de:F:kn:Pt:", 0, -1 },
.usage = "[-adkP] [-c start-directory] [-e environment] [-F format] "
"[-n window-name] " CMD_TARGET_WINDOW_USAGE " [command]",
.target = { 't', CMD_FIND_WINDOW, CMD_FIND_WINDOW_INDEX },
@@ -53,87 +53,53 @@ cmd_new_window_exec(struct cmd *self, struct cmdq_item *item)
{
struct args *args = self->args;
struct cmd_find_state *current = &item->shared->current;
struct spawn_context sc;
struct client *c = cmd_find_client(item, NULL, 1);
struct session *s = item->target.s;
struct winlink *wl = item->target.wl;
struct client *c = cmd_find_client(item, NULL, 1);
int idx = item->target.idx;
const char *cmd, *path, *template, *tmp;
char **argv, *cause, *cp, *cwd, *name;
int argc, detached;
struct environ_entry *envent;
struct winlink *new_wl;
char *cause = NULL, *cp;
const char *template, *add;
struct cmd_find_state fs;
struct args_value *value;
if (args_has(args, 'a') && wl != NULL) {
if ((idx = winlink_shuffle_up(s, wl)) == -1) {
cmdq_error(item, "no free window indexes");
return (CMD_RETURN_ERROR);
}
}
detached = args_has(args, 'd');
if (args->argc == 0) {
cmd = options_get_string(s->options, "default-command");
if (cmd != NULL && *cmd != '\0') {
argc = 1;
argv = (char **)&cmd;
} else {
argc = 0;
argv = NULL;
}
} else {
argc = args->argc;
argv = args->argv;
if (args_has(args, 'a') && (idx = winlink_shuffle_up(s, wl)) == -1) {
cmdq_error(item, "couldn't get a window index");
return (CMD_RETURN_ERROR);
}
path = NULL;
if (item->client != NULL && item->client->session == NULL)
envent = environ_find(item->client->environ, "PATH");
else
envent = environ_find(s->environ, "PATH");
if (envent != NULL)
path = envent->value;
memset(&sc, 0, sizeof sc);
sc.item = item;
sc.s = s;
if ((tmp = args_get(args, 'c')) != NULL)
cwd = format_single(item, tmp, c, s, NULL, NULL);
else
cwd = xstrdup(server_client_get_cwd(item->client, s));
sc.name = args_get(args, 'n');
sc.argc = args->argc;
sc.argv = args->argv;
sc.environ = environ_create();
if ((tmp = args_get(args, 'n')) != NULL)
name = format_single(item, tmp, c, s, NULL, NULL);
else
name = NULL;
if (idx != -1)
wl = winlink_find_by_index(&s->windows, idx);
if (wl != NULL && args_has(args, 'k')) {
/*
* Can't use session_detach as it will destroy session if this
* makes it empty.
*/
notify_session_window("window-unlinked", s, wl->window);
wl->flags &= ~WINLINK_ALERTFLAGS;
winlink_stack_remove(&s->lastw, wl);
winlink_remove(&s->windows, wl);
/* Force select/redraw if current. */
if (wl == s->curw) {
detached = 0;
s->curw = NULL;
}
add = args_first_value(args, 'e', &value);
while (add != NULL) {
environ_put(sc.environ, add);
add = args_next_value(&value);
}
if (idx == -1)
idx = -1 - options_get_number(s->options, "base-index");
wl = session_new(s, name, argc, argv, path, cwd, idx,
&cause);
if (wl == NULL) {
sc.idx = idx;
sc.cwd = args_get(args, 'c');
sc.flags = 0;
if (args_has(args, 'd'))
sc.flags |= SPAWN_DETACHED;
if (args_has(args, 'k'))
sc.flags |= SPAWN_KILL;
if ((new_wl = spawn_window(&sc, &cause)) == NULL) {
cmdq_error(item, "create window failed: %s", cause);
free(cause);
goto error;
return (CMD_RETURN_ERROR);
}
if (!detached) {
session_select(s, wl->idx);
cmd_find_from_winlink(current, wl, 0);
if (!args_has(args, 'd') || new_wl == s->curw) {
cmd_find_from_winlink(current, new_wl, 0);
server_redraw_session_group(s);
} else
server_status_session_group(s);
@@ -141,20 +107,14 @@ cmd_new_window_exec(struct cmd *self, struct cmdq_item *item)
if (args_has(args, 'P')) {
if ((template = args_get(args, 'F')) == NULL)
template = NEW_WINDOW_TEMPLATE;
cp = format_single(item, template, c, s, wl, NULL);
cp = format_single(item, template, c, s, new_wl, NULL);
cmdq_print(item, "%s", cp);
free(cp);
}
cmd_find_from_winlink(&fs, wl, 0);
hooks_insert(s->hooks, item, &fs, "after-new-window");
cmd_find_from_winlink(&fs, new_wl, 0);
cmdq_insert_hook(s, item, &fs, "after-new-window");
free(name);
free(cwd);
environ_free(sc.environ);
return (CMD_RETURN_NORMAL);
error:
free(name);
free(cwd);
return (CMD_RETURN_ERROR);
}

1471
cmd-parse.y Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -32,11 +32,14 @@ static struct cmdq_list global_queue = TAILQ_HEAD_INITIALIZER(global_queue);
static const char *
cmdq_name(struct client *c)
{
static char s[32];
static char s[256];
if (c == NULL)
return ("<global>");
xsnprintf(s, sizeof s, "<%p>", c);
if (c->name != NULL)
xsnprintf(s, sizeof s, "<%s>", c->name);
else
xsnprintf(s, sizeof s, "<%p>", c);
return (s);
}
@@ -66,6 +69,7 @@ cmdq_append(struct client *c, struct cmdq_item *item)
item->queue = queue;
TAILQ_INSERT_TAIL(queue, item, entry);
log_debug("%s %s: %s", __func__, cmdq_name(c), item->name);
item = next;
} while (item != NULL);
@@ -81,23 +85,77 @@ cmdq_insert_after(struct cmdq_item *after, struct cmdq_item *item)
do {
next = item->next;
item->next = NULL;
item->next = after->next;
after->next = item;
if (c != NULL)
c->references++;
item->client = c;
item->queue = queue;
if (after->next != NULL)
TAILQ_INSERT_AFTER(queue, after->next, item, entry);
else
TAILQ_INSERT_AFTER(queue, after, item, entry);
after->next = item;
TAILQ_INSERT_AFTER(queue, after, item, entry);
log_debug("%s %s: %s after %s", __func__, cmdq_name(c),
item->name, after->name);
after = item;
item = next;
} while (item != NULL);
}
/* Insert a hook. */
void
cmdq_insert_hook(struct session *s, struct cmdq_item *item,
struct cmd_find_state *fs, const char *fmt, ...)
{
struct options *oo;
va_list ap;
char *name;
struct cmdq_item *new_item;
struct options_entry *o;
struct options_array_item *a;
struct cmd_list *cmdlist;
if (item->flags & CMDQ_NOHOOKS)
return;
if (s == NULL)
oo = global_s_options;
else
oo = s->options;
va_start(ap, fmt);
xvasprintf(&name, fmt, ap);
va_end(ap);
o = options_get(oo, name);
if (o == NULL) {
free(name);
return;
}
log_debug("running hook %s (parent %p)", name, item);
a = options_array_first(o);
while (a != NULL) {
cmdlist = options_array_item_value(a)->cmdlist;
if (cmdlist == NULL) {
a = options_array_next(a);
continue;
}
new_item = cmdq_get_command(cmdlist, fs, NULL, CMDQ_NOHOOKS);
cmdq_format(new_item, "hook", "%s", name);
if (item != NULL) {
cmdq_insert_after(item, new_item);
item = new_item;
} else
cmdq_append(NULL, new_item);
a = options_array_next(a);
}
free(name);
}
/* Remove an item. */
static void
cmdq_remove(struct cmdq_item *item)
@@ -111,30 +169,23 @@ cmdq_remove(struct cmdq_item *item)
if (item->client != NULL)
server_client_unref(item->client);
if (item->type == CMDQ_COMMAND)
if (item->cmdlist != NULL)
cmd_list_free(item->cmdlist);
TAILQ_REMOVE(item->queue, item, entry);
free((void *)item->name);
free(item->name);
free(item);
}
/* Set command group. */
static u_int
cmdq_next_group(void)
{
static u_int group;
return (++group);
}
/* Remove all subsequent items that match this item's group. */
static void
cmdq_remove_group(struct cmdq_item *item)
{
struct cmdq_item *this, *next;
if (item->group == 0)
return;
this = TAILQ_NEXT(item, entry);
while (this != NULL) {
next = TAILQ_NEXT(this, entry);
@@ -151,32 +202,34 @@ cmdq_get_command(struct cmd_list *cmdlist, struct cmd_find_state *current,
{
struct cmdq_item *item, *first = NULL, *last = NULL;
struct cmd *cmd;
u_int group = cmdq_next_group();
char *tmp;
struct cmdq_shared *shared;
shared = xcalloc(1, sizeof *shared);
if (current != NULL)
cmd_find_copy_state(&shared->current, current);
else
cmd_find_clear_state(&shared->current, 0);
if (m != NULL)
memcpy(&shared->mouse, m, sizeof shared->mouse);
struct cmdq_shared *shared = NULL;
u_int group = 0;
TAILQ_FOREACH(cmd, &cmdlist->list, qentry) {
xasprintf(&tmp, "command[%s]", cmd->entry->name);
if (cmd->group != group) {
shared = xcalloc(1, sizeof *shared);
if (current != NULL)
cmd_find_copy_state(&shared->current, current);
else
cmd_find_clear_state(&shared->current, 0);
if (m != NULL)
memcpy(&shared->mouse, m, sizeof shared->mouse);
group = cmd->group;
}
item = xcalloc(1, sizeof *item);
item->name = tmp;
xasprintf(&item->name, "[%s/%p]", cmd->entry->name, item);
item->type = CMDQ_COMMAND;
item->group = group;
item->group = cmd->group;
item->flags = flags;
item->shared = shared;
item->cmdlist = cmdlist;
item->cmd = cmd;
log_debug("%s: %s group %u", __func__, item->name, item->group);
shared->references++;
cmdlist->references++;
@@ -214,13 +267,22 @@ static enum cmd_retval
cmdq_fire_command(struct cmdq_item *item)
{
struct client *c = item->client;
const char *name = cmdq_name(c);
struct cmdq_shared *shared = item->shared;
struct cmd *cmd = item->cmd;
const struct cmd_entry *entry = cmd->entry;
enum cmd_retval retval;
struct cmd_find_state *fsp, fs;
int flags;
char *tmp;
flags = !!(cmd->flags & CMD_CONTROL);
if (log_get_level() > 1) {
tmp = cmd_print(cmd);
log_debug("%s %s: (%u) %s", __func__, name, item->group, tmp);
free(tmp);
}
flags = !!(shared->flags & CMDQ_SHARED_CONTROL);
cmdq_guard(item, "begin", flags);
if (item->client == NULL)
@@ -245,7 +307,7 @@ cmdq_fire_command(struct cmdq_item *item)
fsp = &fs;
else
goto out;
hooks_insert(fsp->s->hooks, item, fsp, "after-%s", entry->name);
cmdq_insert_hook(fsp->s, item, fsp, "after-%s", entry->name);
}
out:
@@ -262,12 +324,9 @@ struct cmdq_item *
cmdq_get_callback1(const char *name, cmdq_cb cb, void *data)
{
struct cmdq_item *item;
char *tmp;
xasprintf(&tmp, "callback[%s]", name);
item = xcalloc(1, sizeof *item);
item->name = tmp;
xasprintf(&item->name, "[%s/%p]", name, item);
item->type = CMDQ_CALLBACK;
item->group = 0;
@@ -279,6 +338,25 @@ cmdq_get_callback1(const char *name, cmdq_cb cb, void *data)
return (item);
}
/* Generic error callback. */
static enum cmd_retval
cmdq_error_callback(struct cmdq_item *item, void *data)
{
char *error = data;
cmdq_error(item, "%s", error);
free(error);
return (CMD_RETURN_NORMAL);
}
/* Get an error callback for the command queue. */
struct cmdq_item *
cmdq_get_error(const char *error)
{
return (cmdq_get_callback(cmdq_error_callback, xstrdup(error)));
}
/* Fire callback on callback queue. */
static enum cmd_retval
cmdq_fire_callback(struct cmdq_item *item)

View File

@@ -143,12 +143,12 @@ cmd_resize_pane_mouse_update(struct client *c, struct mouse_event *m)
}
w = wl->window;
y = m->y; x = m->x;
y = m->y + m->oy; x = m->x + m->ox;
if (m->statusat == 0 && y > 0)
y--;
else if (m->statusat > 0 && y >= (u_int)m->statusat)
y = m->statusat - 1;
ly = m->ly; lx = m->lx;
ly = m->ly + m->oy; lx = m->lx + m->ox;
if (m->statusat == 0 && ly > 0)
ly--;
else if (m->statusat > 0 && ly >= (u_int)m->statusat)

View File

@@ -20,7 +20,7 @@
#include <sys/types.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include "tmux.h"
@@ -34,9 +34,9 @@ const struct cmd_entry cmd_respawn_pane_entry = {
.name = "respawn-pane",
.alias = "respawnp",
.args = { "c:kt:", 0, -1 },
.usage = "[-c start-directory] [-k] " CMD_TARGET_PANE_USAGE
" [command]",
.args = { "c:e:kt:", 0, -1 },
.usage = "[-k] [-c start-directory] [-e environment] "
CMD_TARGET_PANE_USAGE " [command]",
.target = { 't', CMD_FIND_PANE, 0 },
@@ -48,53 +48,49 @@ static enum cmd_retval
cmd_respawn_pane_exec(struct cmd *self, struct cmdq_item *item)
{
struct args *args = self->args;
struct winlink *wl = item->target.wl;
struct window *w = wl->window;
struct window_pane *wp = item->target.wp;
struct client *c = cmd_find_client(item, NULL, 1);
struct spawn_context sc;
struct session *s = item->target.s;
struct environ *env;
const char *path = NULL, *cp;
char *cause, *cwd = NULL;
u_int idx;
struct environ_entry *envent;
struct winlink *wl = item->target.wl;
struct window_pane *wp = item->target.wp;
char *cause = NULL;
const char *add;
struct args_value *value;
if (!args_has(self->args, 'k') && wp->fd != -1) {
if (window_pane_index(wp, &idx) != 0)
fatalx("index not found");
cmdq_error(item, "pane still active: %s:%d.%u",
s->name, wl->idx, idx);
return (CMD_RETURN_ERROR);
memset(&sc, 0, sizeof sc);
sc.item = item;
sc.s = s;
sc.wl = wl;
sc.wp0 = wp;
sc.lc = NULL;
sc.name = NULL;
sc.argc = args->argc;
sc.argv = args->argv;
sc.environ = environ_create();
add = args_first_value(args, 'e', &value);
while (add != NULL) {
environ_put(sc.environ, add);
add = args_next_value(&value);
}
window_pane_reset_mode_all(wp);
screen_reinit(&wp->base);
input_init(wp);
sc.idx = -1;
sc.cwd = args_get(args, 'c');
if (item->client != NULL && item->client->session == NULL)
envent = environ_find(item->client->environ, "PATH");
else
envent = environ_find(s->environ, "PATH");
if (envent != NULL)
path = envent->value;
sc.flags = SPAWN_RESPAWN;
if (args_has(args, 'k'))
sc.flags |= SPAWN_KILL;
if ((cp = args_get(args, 'c')) != NULL)
cwd = format_single(item, cp, c, s, NULL, NULL);
env = environ_for_session(s, 0);
if (window_pane_spawn(wp, args->argc, args->argv, path, NULL, cwd, env,
s->tio, &cause) != 0) {
if (spawn_pane(&sc, &cause) == NULL) {
cmdq_error(item, "respawn pane failed: %s", cause);
free(cause);
environ_free(env);
free(cwd);
return (CMD_RETURN_ERROR);
}
environ_free(env);
free(cwd);
wp->flags |= PANE_REDRAW;
server_status_window(w);
server_status_window(wp->window);
environ_free(sc.environ);
return (CMD_RETURN_NORMAL);
}

View File

@@ -19,7 +19,7 @@
#include <sys/types.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include "tmux.h"
@@ -34,9 +34,9 @@ const struct cmd_entry cmd_respawn_window_entry = {
.name = "respawn-window",
.alias = "respawnw",
.args = { "c:kt:", 0, -1 },
.usage = "[-c start-directory] [-k] " CMD_TARGET_WINDOW_USAGE
" [command]",
.args = { "c:e:kt:", 0, -1 },
.usage = "[-k] [-c start-directory] [-e environment] "
CMD_TARGET_WINDOW_USAGE " [command]",
.target = { 't', CMD_FIND_WINDOW, 0 },
@@ -48,64 +48,44 @@ static enum cmd_retval
cmd_respawn_window_exec(struct cmd *self, struct cmdq_item *item)
{
struct args *args = self->args;
struct spawn_context sc;
struct session *s = item->target.s;
struct winlink *wl = item->target.wl;
struct window *w = wl->window;
struct window_pane *wp;
struct client *c = cmd_find_client(item, NULL, 1);
struct environ *env;
const char *path = NULL, *cp;
char *cause, *cwd = NULL;
struct environ_entry *envent;
char *cause = NULL;
const char *add;
struct args_value *value;
if (!args_has(self->args, 'k')) {
TAILQ_FOREACH(wp, &w->panes, entry) {
if (wp->fd == -1)
continue;
cmdq_error(item, "window still active: %s:%d", s->name,
wl->idx);
return (CMD_RETURN_ERROR);
}
memset(&sc, 0, sizeof sc);
sc.item = item;
sc.s = s;
sc.wl = wl;
sc.name = NULL;
sc.argc = args->argc;
sc.argv = args->argv;
sc.environ = environ_create();
add = args_first_value(args, 'e', &value);
while (add != NULL) {
environ_put(sc.environ, add);
add = args_next_value(&value);
}
wp = TAILQ_FIRST(&w->panes);
TAILQ_REMOVE(&w->panes, wp, entry);
layout_free(w);
window_destroy_panes(w);
TAILQ_INSERT_HEAD(&w->panes, wp, entry);
window_pane_resize(wp, w->sx, w->sy);
sc.idx = -1;
sc.cwd = args_get(args, 'c');
if (item->client != NULL && item->client->session == NULL)
envent = environ_find(item->client->environ, "PATH");
else
envent = environ_find(s->environ, "PATH");
if (envent != NULL)
path = envent->value;
sc.flags = SPAWN_RESPAWN;
if (args_has(args, 'k'))
sc.flags |= SPAWN_KILL;
if ((cp = args_get(args, 'c')) != NULL)
cwd = format_single(item, cp, c, s, NULL, NULL);
env = environ_for_session(s, 0);
if (window_pane_spawn(wp, args->argc, args->argv, path, NULL, cwd, env,
s->tio, &cause) != 0) {
if (spawn_window(&sc, &cause) == NULL) {
cmdq_error(item, "respawn window failed: %s", cause);
free(cause);
environ_free(env);
free(cwd);
server_destroy_pane(wp, 0);
return (CMD_RETURN_ERROR);
}
environ_free(env);
free(cwd);
layout_init(w, wp);
window_pane_reset_mode_all(wp);
screen_reinit(&wp->base);
input_init(wp);
window_set_active_pane(w, wp);
recalculate_sizes();
server_redraw_window(w);
server_redraw_window(wl->window);
environ_free(sc.environ);
return (CMD_RETURN_NORMAL);
}

View File

@@ -77,7 +77,7 @@ cmd_rotate_window_exec(struct cmd *self, struct cmdq_item *item)
if ((wp = TAILQ_PREV(w->active, window_panes, entry)) == NULL)
wp = TAILQ_LAST(&w->panes, window_panes);
window_set_active_pane(w, wp);
window_set_active_pane(w, wp, 1);
cmd_find_from_winlink_pane(current, wl, wp, 0);
server_redraw_window(w);
} else {
@@ -105,7 +105,7 @@ cmd_rotate_window_exec(struct cmd *self, struct cmdq_item *item)
if ((wp = TAILQ_NEXT(w->active, entry)) == NULL)
wp = TAILQ_FIRST(&w->panes);
window_set_active_pane(w, wp);
window_set_active_pane(w, wp, 1);
cmd_find_from_winlink_pane(current, wl, wp, 0);
server_redraw_window(w);
}

View File

@@ -104,7 +104,7 @@ cmd_save_buffer_exec(struct cmd *self, struct cmdq_item *item)
if (args_has(self->args, 'a'))
flags = "ab";
file = server_client_get_path(c, path);
file = server_client_get_path(item->client, path);
free(path);
f = fopen(file, flags);

View File

@@ -112,7 +112,7 @@ cmd_select_pane_exec(struct cmd *self, struct cmdq_item *item)
else {
server_unzoom_window(w);
window_redraw_active_switch(w, lastwp);
if (window_set_active_pane(w, lastwp)) {
if (window_set_active_pane(w, lastwp, 1)) {
cmd_find_from_winlink(current, wl, 0);
cmd_select_pane_redraw(w);
}
@@ -194,9 +194,9 @@ cmd_select_pane_exec(struct cmd *self, struct cmdq_item *item)
return (CMD_RETURN_NORMAL);
server_unzoom_window(wp->window);
window_redraw_active_switch(w, wp);
if (window_set_active_pane(w, wp)) {
if (window_set_active_pane(w, wp, 1)) {
cmd_find_from_winlink_pane(current, wl, wp, 0);
hooks_insert(s->hooks, item, current, "after-select-pane");
cmdq_insert_hook(s, item, current, "after-select-pane");
cmd_select_pane_redraw(w);
}

View File

@@ -119,7 +119,7 @@ cmd_select_window_exec(struct cmd *self, struct cmdq_item *item)
}
cmd_find_from_session(current, s, 0);
server_redraw_session(s);
hooks_insert(s->hooks, item, current, "after-select-window");
cmdq_insert_hook(s, item, current, "after-select-window");
} else {
/*
* If -T and select-window is invoked on same window as
@@ -137,7 +137,7 @@ cmd_select_window_exec(struct cmd *self, struct cmdq_item *item)
cmd_find_from_session(current, s, 0);
server_redraw_session(s);
}
hooks_insert(s->hooks, item, current, "after-select-window");
cmdq_insert_hook(s, item, current, "after-select-window");
}
recalculate_sizes();

View File

@@ -55,31 +55,30 @@ const struct cmd_entry cmd_send_prefix_entry = {
.exec = cmd_send_keys_exec
};
static void
cmd_send_keys_inject(struct client *c, struct cmdq_item *item, key_code key)
static struct cmdq_item *
cmd_send_keys_inject(struct client *c, struct cmd_find_state *fs,
struct cmdq_item *item, key_code key)
{
struct window_pane *wp = item->target.wp;
struct session *s = item->target.s;
struct winlink *wl = item->target.wl;
struct window_mode_entry *wme;
struct key_table *table;
struct key_binding *bd;
wme = TAILQ_FIRST(&wp->modes);
wme = TAILQ_FIRST(&fs->wp->modes);
if (wme == NULL || wme->mode->key_table == NULL) {
if (options_get_number(wp->window->options, "xterm-keys"))
if (options_get_number(fs->wp->window->options, "xterm-keys"))
key |= KEYC_XTERM;
window_pane_key(wp, NULL, s, wl, key, NULL);
return;
window_pane_key(fs->wp, item->client, fs->s, fs->wl, key, NULL);
return (item);
}
table = key_bindings_get_table(wme->mode->key_table(wme), 1);
bd = key_bindings_get(table, key & ~KEYC_XTERM);
if (bd != NULL) {
table->references++;
key_bindings_dispatch(bd, item, c, NULL, &item->target);
item = key_bindings_dispatch(bd, item, c, NULL, &item->target);
key_bindings_unref_table(table);
}
return (item);
}
static enum cmd_retval
@@ -91,6 +90,7 @@ cmd_send_keys_exec(struct cmd *self, struct cmdq_item *item)
struct session *s = item->target.s;
struct winlink *wl = item->target.wl;
struct mouse_event *m = &item->shared->mouse;
struct cmd_find_state *fs = &item->target;
struct window_mode_entry *wme = TAILQ_FIRST(&wp->modes);
struct utf8_data *ud, *uc;
wchar_t wc;
@@ -132,7 +132,7 @@ cmd_send_keys_exec(struct cmd *self, struct cmdq_item *item)
cmdq_error(item, "no mouse target");
return (CMD_RETURN_ERROR);
}
window_pane_key(wp, NULL, s, wl, m->key, m);
window_pane_key(wp, item->client, s, wl, m->key, m);
return (CMD_RETURN_NORMAL);
}
@@ -141,7 +141,7 @@ cmd_send_keys_exec(struct cmd *self, struct cmdq_item *item)
key = options_get_number(s->options, "prefix2");
else
key = options_get_number(s->options, "prefix");
cmd_send_keys_inject(c, item, key);
cmd_send_keys_inject(c, fs, item, key);
return (CMD_RETURN_NORMAL);
}
@@ -155,9 +155,10 @@ cmd_send_keys_exec(struct cmd *self, struct cmdq_item *item)
literal = args_has(args, 'l');
if (!literal) {
key = key_string_lookup_string(args->argv[i]);
if (key != KEYC_NONE && key != KEYC_UNKNOWN)
cmd_send_keys_inject(c, item, key);
else
if (key != KEYC_NONE && key != KEYC_UNKNOWN) {
item = cmd_send_keys_inject(c, fs, item,
key);
} else
literal = 1;
}
if (literal) {
@@ -165,7 +166,8 @@ cmd_send_keys_exec(struct cmd *self, struct cmdq_item *item)
for (uc = ud; uc->size != 0; uc++) {
if (utf8_combine(uc, &wc) != UTF8_DONE)
continue;
cmd_send_keys_inject(c, item, wc);
item = cmd_send_keys_inject(c, fs, item,
wc);
}
free(ud);
}

View File

@@ -1,133 +0,0 @@
/* $OpenBSD$ */
/*
* Copyright (c) 2012 Thomas Adam <thomas@xteddy.org>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER
* IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
* OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include <sys/types.h>
#include <stdlib.h>
#include <string.h>
#include "tmux.h"
/*
* Set or show global or session hooks.
*/
static enum cmd_retval cmd_set_hook_exec(struct cmd *, struct cmdq_item *);
const struct cmd_entry cmd_set_hook_entry = {
.name = "set-hook",
.alias = NULL,
.args = { "gRt:u", 1, 2 },
.usage = "[-gRu] " CMD_TARGET_SESSION_USAGE " hook-name [command]",
.target = { 't', CMD_FIND_SESSION, CMD_FIND_CANFAIL },
.flags = CMD_AFTERHOOK,
.exec = cmd_set_hook_exec
};
const struct cmd_entry cmd_show_hooks_entry = {
.name = "show-hooks",
.alias = NULL,
.args = { "gt:", 0, 1 },
.usage = "[-g] " CMD_TARGET_SESSION_USAGE,
.target = { 't', CMD_FIND_SESSION, 0 },
.flags = CMD_AFTERHOOK,
.exec = cmd_set_hook_exec
};
static enum cmd_retval
cmd_set_hook_exec(struct cmd *self, struct cmdq_item *item)
{
struct args *args = self->args;
struct cmd_list *cmdlist;
struct hooks *hooks;
struct hook *hook;
char *cause, *tmp;
const char *name, *cmd, *target;
if (args_has(args, 'g'))
hooks = global_hooks;
else {
if (item->target.s == NULL) {
target = args_get(args, 't');
if (target != NULL)
cmdq_error(item, "no such session: %s", target);
else
cmdq_error(item, "no current session");
return (CMD_RETURN_ERROR);
}
hooks = item->target.s->hooks;
}
if (self->entry == &cmd_show_hooks_entry) {
hook = hooks_first(hooks);
while (hook != NULL) {
tmp = cmd_list_print(hook->cmdlist);
cmdq_print(item, "%s -> %s", hook->name, tmp);
free(tmp);
hook = hooks_next(hook);
}
return (CMD_RETURN_NORMAL);
}
name = args->argv[0];
if (*name == '\0') {
cmdq_error(item, "invalid hook name");
return (CMD_RETURN_ERROR);
}
if (args->argc < 2)
cmd = NULL;
else
cmd = args->argv[1];
if (cmd != NULL && (args_has(args, 'R') || args_has(args, 'u'))) {
cmdq_error(item, "no command allowed");
return (CMD_RETURN_ERROR);
}
if (args_has(args, 'R')) {
notify_hook(item, name);
return (CMD_RETURN_NORMAL);
}
if (args_has(args, 'u')) {
hooks_remove(hooks, name);
return (CMD_RETURN_NORMAL);
}
if (cmd == NULL) {
cmdq_error(item, "no command given");
return (CMD_RETURN_ERROR);
}
cmdlist = cmd_string_parse(cmd, NULL, 0, &cause);
if (cmdlist == NULL) {
if (cause != NULL) {
cmdq_error(item, "%s", cause);
free(cause);
}
return (CMD_RETURN_ERROR);
}
hooks_add(hooks, name, cmdlist);
cmd_list_free(cmdlist);
return (CMD_RETURN_NORMAL);
}

View File

@@ -65,6 +65,19 @@ const struct cmd_entry cmd_set_window_option_entry = {
.exec = cmd_set_option_exec
};
const struct cmd_entry cmd_set_hook_entry = {
.name = "set-hook",
.alias = NULL,
.args = { "agRt:u", 1, 2 },
.usage = "[-agRu] " CMD_TARGET_SESSION_USAGE " hook [command]",
.target = { 't', CMD_FIND_SESSION, CMD_FIND_CANFAIL },
.flags = CMD_AFTERHOOK,
.exec = cmd_set_option_exec
};
static enum cmd_retval
cmd_set_option_exec(struct cmd *self, struct cmdq_item *item)
{
@@ -87,6 +100,11 @@ cmd_set_option_exec(struct cmd *self, struct cmdq_item *item)
c = cmd_find_client(item, NULL, 1);
argument = format_single(item, args->argv[0], c, s, wl, NULL);
if (self->entry == &cmd_set_hook_entry && args_has(args, 'R')) {
notify_hook(item, argument);
return (CMD_RETURN_NORMAL);
}
/* Parse option name and index. */
name = options_match(argument, &idx, &ambiguous);
if (name == NULL) {
@@ -200,8 +218,11 @@ cmd_set_option_exec(struct cmd *self, struct cmdq_item *item)
options_default(oo, options_table_entry(o));
else
options_remove(o);
} else
options_array_set(o, idx, NULL, 0);
} else if (options_array_set(o, idx, NULL, 0, &cause) != 0) {
cmdq_error(item, "%s", cause);
free(cause);
goto fail;
}
} else if (*name == '@') {
if (value == NULL) {
cmdq_error(item, "empty value");
@@ -222,9 +243,15 @@ cmd_set_option_exec(struct cmd *self, struct cmdq_item *item)
if (idx == -1) {
if (!append)
options_array_clear(o);
options_array_assign(o, value);
} else if (options_array_set(o, idx, value, append) != 0) {
cmdq_error(item, "invalid index: %s", argument);
if (options_array_assign(o, value, &cause) != 0) {
cmdq_error(item, "%s", cause);
free(cause);
goto fail;
}
} else if (options_array_set(o, idx, value, append,
&cause) != 0) {
cmdq_error(item, "%s", cause);
free(cause);
goto fail;
}
}
@@ -366,7 +393,7 @@ cmd_set_option_set(struct cmd *self, struct cmdq_item *item, struct options *oo,
return (-1);
}
return (0);
case OPTIONS_TABLE_ARRAY:
case OPTIONS_TABLE_COMMAND:
break;
}
return (-1);

View File

@@ -29,8 +29,8 @@
static enum cmd_retval cmd_show_options_exec(struct cmd *, struct cmdq_item *);
static enum cmd_retval cmd_show_options_one(struct cmd *, struct cmdq_item *,
struct options *);
static void cmd_show_options_print(struct cmd *, struct cmdq_item *,
struct options_entry *, int);
static enum cmd_retval cmd_show_options_all(struct cmd *, struct cmdq_item *,
struct options *);
@@ -38,8 +38,8 @@ const struct cmd_entry cmd_show_options_entry = {
.name = "show-options",
.alias = "show",
.args = { "gqst:vw", 0, 1 },
.usage = "[-gqsvw] [-t target-session|target-window] [option]",
.args = { "gHqst:vw", 0, 1 },
.usage = "[-gHqsvw] [-t target-session|target-window] [option]",
.target = { 't', CMD_FIND_WINDOW, CMD_FIND_CANFAIL },
@@ -60,28 +60,120 @@ const struct cmd_entry cmd_show_window_options_entry = {
.exec = cmd_show_options_exec
};
const struct cmd_entry cmd_show_hooks_entry = {
.name = "show-hooks",
.alias = NULL,
.args = { "gt:", 0, 1 },
.usage = "[-g] " CMD_TARGET_SESSION_USAGE,
.target = { 't', CMD_FIND_SESSION, 0 },
.flags = CMD_AFTERHOOK,
.exec = cmd_show_options_exec
};
static enum cmd_retval
cmd_show_options_exec(struct cmd *self, struct cmdq_item *item)
{
struct args *args = self->args;
struct cmd_find_state *fs = &item->target;
struct client *c = cmd_find_client(item, NULL, 1);
struct session *s = item->target.s;
struct winlink *wl = item->target.wl;
struct options *oo;
enum options_table_scope scope;
char *cause;
int window;
char *argument, *name = NULL, *cause;
const char *target;
int window, idx, ambiguous;
struct options_entry *o;
window = (self->entry == &cmd_show_window_options_entry);
scope = options_scope_from_flags(args, window, fs, &oo, &cause);
if (args->argc == 0) {
scope = options_scope_from_flags(args, window, fs, &oo, &cause);
if (scope == OPTIONS_TABLE_NONE) {
if (args_has(args, 'q'))
return (CMD_RETURN_NORMAL);
cmdq_error(item, "%s", cause);
free(cause);
return (CMD_RETURN_ERROR);
}
return (cmd_show_options_all(self, item, oo));
}
argument = format_single(item, args->argv[0], c, s, wl, NULL);
name = options_match(argument, &idx, &ambiguous);
if (name == NULL) {
if (args_has(args, 'q'))
goto fail;
if (ambiguous)
cmdq_error(item, "ambiguous option: %s", argument);
else
cmdq_error(item, "invalid option: %s", argument);
goto fail;
}
if (*name == '@')
scope = options_scope_from_flags(args, window, fs, &oo, &cause);
else {
if (options_get_only(global_options, name) != NULL)
scope = OPTIONS_TABLE_SERVER;
else if (options_get_only(global_s_options, name) != NULL)
scope = OPTIONS_TABLE_SESSION;
else if (options_get_only(global_w_options, name) != NULL)
scope = OPTIONS_TABLE_WINDOW;
else {
scope = OPTIONS_TABLE_NONE;
xasprintf(&cause, "unknown option: %s", argument);
}
if (scope == OPTIONS_TABLE_SERVER)
oo = global_options;
else if (scope == OPTIONS_TABLE_SESSION) {
if (args_has(self->args, 'g'))
oo = global_s_options;
else if (s == NULL) {
target = args_get(args, 't');
if (target != NULL) {
cmdq_error(item, "no such session: %s",
target);
} else
cmdq_error(item, "no current session");
goto fail;
} else
oo = s->options;
} else if (scope == OPTIONS_TABLE_WINDOW) {
if (args_has(self->args, 'g'))
oo = global_w_options;
else if (wl == NULL) {
target = args_get(args, 't');
if (target != NULL) {
cmdq_error(item, "no such window: %s",
target);
} else
cmdq_error(item, "no current window");
goto fail;
} else
oo = wl->window->options;
}
}
if (scope == OPTIONS_TABLE_NONE) {
if (args_has(args, 'q'))
goto fail;
cmdq_error(item, "%s", cause);
free(cause);
return (CMD_RETURN_ERROR);
goto fail;
}
o = options_get_only(oo, name);
if (o != NULL)
cmd_show_options_print(self, item, o, idx);
if (args->argc == 0)
return (cmd_show_options_all(self, item, oo));
else
return (cmd_show_options_one(self, item, oo));
free(name);
free(argument);
return (CMD_RETURN_NORMAL);
fail:
free(name);
free(argument);
return (CMD_RETURN_ERROR);
}
static void
@@ -89,15 +181,20 @@ cmd_show_options_print(struct cmd *self, struct cmdq_item *item,
struct options_entry *o, int idx)
{
struct options_array_item *a;
const char *name, *value;
char *tmp, *escaped;
const char *name = options_name(o);
char *value, *tmp = NULL, *escaped;
if (idx != -1) {
xasprintf(&tmp, "%s[%d]", options_name(o), idx);
xasprintf(&tmp, "%s[%d]", name, idx);
name = tmp;
} else {
if (options_isarray(o)) {
a = options_array_first(o);
if (a == NULL) {
if (!args_has(self->args, 'v'))
cmdq_print(item, "%s", name);
return;
}
while (a != NULL) {
idx = options_array_item_index(a);
cmd_show_options_print(self, item, o, idx);
@@ -105,77 +202,50 @@ cmd_show_options_print(struct cmd *self, struct cmdq_item *item,
}
return;
}
tmp = NULL;
name = options_name(o);
}
value = options_tostring(o, idx, 0);
if (args_has(self->args, 'v'))
cmdq_print(item, "%s", value);
else if (options_isstring(o)) {
utf8_stravis(&escaped, value, VIS_OCTAL|VIS_TAB|VIS_NL|VIS_DQ);
cmdq_print(item, "%s \"%s\"", name, escaped);
escaped = args_escape(value);
cmdq_print(item, "%s %s", name, escaped);
free(escaped);
} else
cmdq_print(item, "%s %s", name, value);
free(value);
free(tmp);
}
static enum cmd_retval
cmd_show_options_one(struct cmd *self, struct cmdq_item *item,
struct options *oo)
{
struct args *args = self->args;
struct client *c = cmd_find_client(item, NULL, 1);
struct session *s = item->target.s;
struct winlink *wl = item->target.wl;
struct options_entry *o;
int idx, ambiguous;
char *name;
name = format_single(item, args->argv[0], c, s, wl, NULL);
o = options_match_get(oo, name, &idx, 1, &ambiguous);
if (o == NULL) {
if (args_has(args, 'q')) {
free(name);
return (CMD_RETURN_NORMAL);
}
if (ambiguous) {
cmdq_error(item, "ambiguous option: %s", name);
free(name);
return (CMD_RETURN_ERROR);
}
if (*name != '@' &&
options_match_get(oo, name, &idx, 0, &ambiguous) != NULL) {
free(name);
return (CMD_RETURN_NORMAL);
}
cmdq_error(item, "unknown option: %s", name);
free(name);
return (CMD_RETURN_ERROR);
}
cmd_show_options_print(self, item, o, idx);
free(name);
return (CMD_RETURN_NORMAL);
}
static enum cmd_retval
cmd_show_options_all(struct cmd *self, struct cmdq_item *item,
struct options *oo)
{
struct options_entry *o;
const struct options_table_entry *oe;
struct options_array_item *a;
u_int idx;
const struct options_table_entry *oe;
o = options_first(oo);
while (o != NULL) {
oe = options_table_entry(o);
if ((self->entry != &cmd_show_hooks_entry &&
!args_has(self->args, 'H') &&
oe != NULL &&
(oe->flags & OPTIONS_TABLE_IS_HOOK)) ||
(self->entry == &cmd_show_hooks_entry &&
(oe == NULL ||
(~oe->flags & OPTIONS_TABLE_IS_HOOK)))) {
o = options_next(o);
continue;
}
if (!options_isarray(o))
cmd_show_options_print(self, item, o, -1);
else {
a = options_array_first(o);
else if ((a = options_array_first(o)) == NULL) {
if (!args_has(self->args, 'v'))
cmdq_print(item, "%s", options_name(o));
} else {
while (a != NULL) {
idx = options_array_item_index(a);
cmd_show_options_print(self, item, o, idx);

View File

@@ -37,8 +37,8 @@ const struct cmd_entry cmd_source_file_entry = {
.name = "source-file",
.alias = "source",
.args = { "q", 1, 1 },
.usage = "[-q] path",
.args = { "nq", 1, -1 },
.usage = "[-nq] path ...",
.flags = 0,
.exec = cmd_source_file_exec
@@ -48,45 +48,60 @@ static enum cmd_retval
cmd_source_file_exec(struct cmd *self, struct cmdq_item *item)
{
struct args *args = self->args;
int quiet = args_has(args, 'q');
int flags = 0;
struct client *c = item->client;
struct cmdq_item *new_item;
struct cmdq_item *new_item, *after;
enum cmd_retval retval;
char *pattern, *tmp;
const char *path = args->argv[0];
char *pattern, *cwd;
const char *path, *error;
glob_t g;
u_int i;
int i;
u_int j;
if (*path == '/')
pattern = xstrdup(path);
else {
utf8_stravis(&tmp, server_client_get_cwd(c, NULL), VIS_GLOB);
xasprintf(&pattern, "%s/%s", tmp, path);
free(tmp);
}
log_debug("%s: %s", __func__, pattern);
if (args_has(args, 'q'))
flags |= CMD_PARSE_QUIET;
if (args_has(args, 'n'))
flags |= CMD_PARSE_PARSEONLY;
utf8_stravis(&cwd, server_client_get_cwd(c, NULL), VIS_GLOB);
retval = CMD_RETURN_NORMAL;
if (glob(pattern, 0, NULL, &g) != 0) {
if (!quiet || errno != ENOENT) {
cmdq_error(item, "%s: %s", path, strerror(errno));
retval = CMD_RETURN_ERROR;
for (i = 0; i < args->argc; i++) {
path = args->argv[i];
if (*path == '/')
pattern = xstrdup(path);
else
xasprintf(&pattern, "%s/%s", cwd, path);
log_debug("%s: %s", __func__, pattern);
if (glob(pattern, 0, NULL, &g) != 0) {
error = strerror(errno);
if (errno != ENOENT || (~flags & CMD_PARSE_QUIET)) {
cmdq_error(item, "%s: %s", path, error);
retval = CMD_RETURN_ERROR;
}
free(pattern);
continue;
}
free(pattern);
return (retval);
}
free(pattern);
for (i = 0; i < (u_int)g.gl_pathc; i++) {
if (load_cfg(g.gl_pathv[i], c, item, quiet) < 0)
retval = CMD_RETURN_ERROR;
after = item;
for (j = 0; j < g.gl_pathc; j++) {
path = g.gl_pathv[j];
if (load_cfg(path, c, after, flags, &new_item) < 0)
retval = CMD_RETURN_ERROR;
else if (new_item != NULL)
after = new_item;
}
globfree(&g);
}
if (cfg_finished) {
if (retval == CMD_RETURN_ERROR && c->session == NULL)
c->retval = 1;
new_item = cmdq_get_callback(cmd_source_file_done, NULL);
cmdq_insert_after(item, new_item);
}
globfree(&g);
free(cwd);
return (retval);
}

View File

@@ -39,9 +39,10 @@ const struct cmd_entry cmd_split_window_entry = {
.name = "split-window",
.alias = "splitw",
.args = { "bc:dfF:l:hp:Pt:v", 0, -1 },
.usage = "[-bdfhvP] [-c start-directory] [-F format] "
"[-p percentage|-l size] " CMD_TARGET_PANE_USAGE " [command]",
.args = { "bc:de:fF:hIl:p:Pt:v", 0, -1 },
.usage = "[-bdefhIPv] [-c start-directory] [-e environment] "
"[-F format] [-p percentage|-l size] " CMD_TARGET_PANE_USAGE
" [command]",
.target = { 't', CMD_FIND_PANE, 0 },
@@ -52,111 +53,106 @@ const struct cmd_entry cmd_split_window_entry = {
static enum cmd_retval
cmd_split_window_exec(struct cmd *self, struct cmdq_item *item)
{
struct cmd_find_state *current = &item->shared->current;
struct args *args = self->args;
struct cmd_find_state *current = &item->shared->current;
struct spawn_context sc;
struct client *c = cmd_find_client(item, NULL, 1);
struct session *s = item->target.s;
struct winlink *wl = item->target.wl;
struct window *w = wl->window;
struct window_pane *wp = item->target.wp, *new_wp = NULL;
struct environ *env;
const char *cmd, *path, *shell, *template, *tmp;
char **argv, *cause, *new_cause, *cp, *cwd;
u_int hlimit;
int argc, size, percentage, before;
struct window_pane *wp = item->target.wp, *new_wp;
enum layout_type type;
struct layout_cell *lc;
struct environ_entry *envent;
struct cmd_find_state fs;
struct cmd_find_state fs;
int size, percentage, flags, input;
const char *template, *add;
char *cause, *cp;
struct args_value *value;
server_unzoom_window(w);
if (args->argc == 0) {
cmd = options_get_string(s->options, "default-command");
if (cmd != NULL && *cmd != '\0') {
argc = 1;
argv = (char **)&cmd;
} else {
argc = 0;
argv = NULL;
}
} else {
argc = args->argc;
argv = args->argv;
}
if ((tmp = args_get(args, 'c')) != NULL)
cwd = format_single(item, tmp, c, s, NULL, NULL);
else
cwd = xstrdup(server_client_get_cwd(item->client, s));
type = LAYOUT_TOPBOTTOM;
if (args_has(args, 'h'))
type = LAYOUT_LEFTRIGHT;
before = args_has(args, 'b');
size = -1;
else
type = LAYOUT_TOPBOTTOM;
if (args_has(args, 'l')) {
size = args_strtonum(args, 'l', 0, INT_MAX, &cause);
if (cause != NULL) {
xasprintf(&new_cause, "size %s", cause);
cmdq_error(item, "create pane failed: -l %s", cause);
free(cause);
cause = new_cause;
goto error;
return (CMD_RETURN_ERROR);
}
} else if (args_has(args, 'p')) {
percentage = args_strtonum(args, 'p', 0, INT_MAX, &cause);
if (cause != NULL) {
xasprintf(&new_cause, "percentage %s", cause);
cmdq_error(item, "create pane failed: -p %s", cause);
free(cause);
cause = new_cause;
goto error;
return (CMD_RETURN_ERROR);
}
if (type == LAYOUT_TOPBOTTOM)
size = (wp->sy * percentage) / 100;
else
size = (wp->sx * percentage) / 100;
}
hlimit = options_get_number(s->options, "history-limit");
shell = options_get_string(s->options, "default-shell");
if (*shell == '\0' || areshell(shell))
shell = _PATH_BSHELL;
lc = layout_split_pane(wp, type, size, before, args_has(args, 'f'));
if (lc == NULL) {
cause = xstrdup("pane too small");
goto error;
}
new_wp = window_add_pane(w, wp, before, args_has(args, 'f'), hlimit);
layout_make_leaf(lc, new_wp);
path = NULL;
if (item->client != NULL && item->client->session == NULL)
envent = environ_find(item->client->environ, "PATH");
else
envent = environ_find(s->environ, "PATH");
if (envent != NULL)
path = envent->value;
env = environ_for_session(s, 0);
if (window_pane_spawn(new_wp, argc, argv, path, shell, cwd, env,
s->tio, &cause) != 0) {
environ_free(env);
goto error;
}
environ_free(env);
layout_fix_panes(w);
server_redraw_window(w);
if (!args_has(args, 'd')) {
window_set_active_pane(w, new_wp);
session_select(s, wl->idx);
cmd_find_from_session(current, s, 0);
server_redraw_session(s);
} else
server_status_session(s);
size = -1;
server_unzoom_window(wp->window);
input = (args_has(args, 'I') && args->argc == 0);
flags = 0;
if (args_has(args, 'b'))
flags |= SPAWN_BEFORE;
if (args_has(args, 'f'))
flags |= SPAWN_FULLSIZE;
if (input || (args->argc == 1 && *args->argv[0] == '\0'))
flags |= SPAWN_EMPTY;
lc = layout_split_pane(wp, type, size, flags);
if (lc == NULL) {
cmdq_error(item, "no space for new pane");
return (CMD_RETURN_ERROR);
}
memset(&sc, 0, sizeof sc);
sc.item = item;
sc.s = s;
sc.wl = wl;
sc.wp0 = wp;
sc.lc = lc;
sc.name = NULL;
sc.argc = args->argc;
sc.argv = args->argv;
sc.environ = environ_create();
add = args_first_value(args, 'e', &value);
while (add != NULL) {
environ_put(sc.environ, add);
add = args_next_value(&value);
}
sc.idx = -1;
sc.cwd = args_get(args, 'c');
sc.flags = flags;
if (args_has(args, 'd'))
sc.flags |= SPAWN_DETACHED;
if ((new_wp = spawn_pane(&sc, &cause)) == NULL) {
layout_close_pane(new_wp);
cmdq_error(item, "create pane failed: %s", cause);
free(cause);
return (CMD_RETURN_ERROR);
}
if (input && window_pane_start_input(new_wp, item, &cause) != 0) {
layout_close_pane(new_wp);
window_remove_pane(wp->window, new_wp);
cmdq_error(item, "%s", cause);
free(cause);
return (CMD_RETURN_ERROR);
}
if (!args_has(args, 'd'))
cmd_find_from_winlink_pane(current, wl, new_wp, 0);
server_redraw_window(wp->window);
server_status_session(s);
if (args_has(args, 'P')) {
if ((template = args_get(args, 'F')) == NULL)
@@ -165,22 +161,12 @@ cmd_split_window_exec(struct cmd *self, struct cmdq_item *item)
cmdq_print(item, "%s", cp);
free(cp);
}
notify_window("window-layout-changed", w);
cmd_find_from_winlink_pane(&fs, wl, new_wp, 0);
hooks_insert(s->hooks, item, &fs, "after-split-window");
cmdq_insert_hook(s, item, &fs, "after-split-window");
free(cwd);
environ_free(sc.environ);
if (input)
return (CMD_RETURN_WAIT);
return (CMD_RETURN_NORMAL);
error:
if (new_wp != NULL) {
layout_close_pane(new_wp);
window_remove_pane(w, new_wp);
}
cmdq_error(item, "create pane failed: %s", cause);
free(cause);
free(cwd);
return (CMD_RETURN_ERROR);
}

View File

@@ -1,390 +0,0 @@
/* $OpenBSD$ */
/*
* Copyright (c) 2008 Nicholas Marriott <nicholas.marriott@gmail.com>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER
* IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
* OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include <sys/types.h>
#include <errno.h>
#include <pwd.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include "tmux.h"
/*
* Parse a command from a string.
*/
static int cmd_string_getc(const char *, size_t *);
static void cmd_string_ungetc(size_t *);
static void cmd_string_copy(char **, char *, size_t *);
static char *cmd_string_string(const char *, size_t *, char, int);
static char *cmd_string_variable(const char *, size_t *);
static char *cmd_string_expand_tilde(const char *, size_t *);
static int
cmd_string_getc(const char *s, size_t *p)
{
const u_char *ucs = s;
if (ucs[*p] == '\0')
return (EOF);
return (ucs[(*p)++]);
}
static void
cmd_string_ungetc(size_t *p)
{
(*p)--;
}
static int
cmd_string_unicode(wchar_t *wc, const char *s, size_t *p, char ch)
{
int size = (ch == 'u') ? 4 : 8;
u_int tmp;
if (size == 4 && sscanf(s + *p, "%4x", &tmp) != 1)
return (-1);
if (size == 8 && sscanf(s + *p, "%8x", &tmp) != 1)
return (-1);
*p += size;
*wc = (wchar_t)tmp;
return (0);
}
int
cmd_string_split(const char *s, int *rargc, char ***rargv)
{
size_t p = 0;
int ch, argc = 0, append = 0;
char **argv = NULL, *buf = NULL, *t;
const char *whitespace, *equals;
size_t len = 0;
for (;;) {
ch = cmd_string_getc(s, &p);
switch (ch) {
case '\'':
if ((t = cmd_string_string(s, &p, '\'', 0)) == NULL)
goto error;
cmd_string_copy(&buf, t, &len);
break;
case '"':
if ((t = cmd_string_string(s, &p, '"', 1)) == NULL)
goto error;
cmd_string_copy(&buf, t, &len);
break;
case '$':
if ((t = cmd_string_variable(s, &p)) == NULL)
goto error;
cmd_string_copy(&buf, t, &len);
break;
case '#':
/* Comment: discard rest of line. */
while ((ch = cmd_string_getc(s, &p)) != EOF)
;
/* FALLTHROUGH */
case EOF:
case ' ':
case '\t':
if (buf != NULL) {
buf = xrealloc(buf, len + 1);
buf[len] = '\0';
argv = xreallocarray(argv, argc + 1,
sizeof *argv);
argv[argc++] = buf;
buf = NULL;
len = 0;
}
if (ch != EOF)
break;
while (argc != 0) {
equals = strchr(argv[0], '=');
whitespace = argv[0] + strcspn(argv[0], " \t");
if (equals == NULL || equals > whitespace)
break;
environ_put(global_environ, argv[0]);
argc--;
memmove(argv, argv + 1, argc * (sizeof *argv));
}
goto done;
case '~':
if (buf != NULL) {
append = 1;
break;
}
t = cmd_string_expand_tilde(s, &p);
if (t == NULL)
goto error;
cmd_string_copy(&buf, t, &len);
break;
default:
append = 1;
break;
}
if (append) {
if (len >= SIZE_MAX - 2)
goto error;
buf = xrealloc(buf, len + 1);
buf[len++] = ch;
}
append = 0;
}
done:
*rargc = argc;
*rargv = argv;
free(buf);
return (0);
error:
if (argv != NULL)
cmd_free_argv(argc, argv);
free(buf);
return (-1);
}
struct cmd_list *
cmd_string_parse(const char *s, const char *file, u_int line, char **cause)
{
struct cmd_list *cmdlist = NULL;
int argc;
char **argv;
*cause = NULL;
if (cmd_string_split(s, &argc, &argv) != 0) {
xasprintf(cause, "invalid or unknown command: %s", s);
return (NULL);
}
if (argc != 0) {
cmdlist = cmd_list_parse(argc, argv, file, line, cause);
if (cmdlist == NULL) {
cmd_free_argv(argc, argv);
return (NULL);
}
}
cmd_free_argv(argc, argv);
return (cmdlist);
}
static void
cmd_string_copy(char **dst, char *src, size_t *len)
{
size_t srclen;
srclen = strlen(src);
*dst = xrealloc(*dst, *len + srclen + 1);
strlcpy(*dst + *len, src, srclen + 1);
*len += srclen;
free(src);
}
static char *
cmd_string_string(const char *s, size_t *p, char endch, int esc)
{
int ch;
wchar_t wc;
struct utf8_data ud;
char *buf = NULL, *t;
size_t len = 0;
while ((ch = cmd_string_getc(s, p)) != endch) {
switch (ch) {
case EOF:
goto error;
case '\\':
if (!esc)
break;
switch (ch = cmd_string_getc(s, p)) {
case EOF:
goto error;
case 'e':
ch = '\033';
break;
case 'r':
ch = '\r';
break;
case 'n':
ch = '\n';
break;
case 't':
ch = '\t';
break;
case 'u':
case 'U':
if (cmd_string_unicode(&wc, s, p, ch) != 0)
goto error;
if (utf8_split(wc, &ud) != UTF8_DONE)
goto error;
if (len >= SIZE_MAX - ud.size - 1)
goto error;
buf = xrealloc(buf, len + ud.size);
memcpy(buf + len, ud.data, ud.size);
len += ud.size;
continue;
}
break;
case '$':
if (!esc)
break;
if ((t = cmd_string_variable(s, p)) == NULL)
goto error;
cmd_string_copy(&buf, t, &len);
continue;
}
if (len >= SIZE_MAX - 2)
goto error;
buf = xrealloc(buf, len + 1);
buf[len++] = ch;
}
buf = xrealloc(buf, len + 1);
buf[len] = '\0';
return (buf);
error:
free(buf);
return (NULL);
}
static char *
cmd_string_variable(const char *s, size_t *p)
{
int ch, fch;
char *buf, *t;
size_t len;
struct environ_entry *envent;
#define cmd_string_first(ch) ((ch) == '_' || \
((ch) >= 'a' && (ch) <= 'z') || ((ch) >= 'A' && (ch) <= 'Z'))
#define cmd_string_other(ch) ((ch) == '_' || \
((ch) >= 'a' && (ch) <= 'z') || ((ch) >= 'A' && (ch) <= 'Z') || \
((ch) >= '0' && (ch) <= '9'))
buf = NULL;
len = 0;
fch = EOF;
switch (ch = cmd_string_getc(s, p)) {
case EOF:
goto error;
case '{':
fch = '{';
ch = cmd_string_getc(s, p);
if (!cmd_string_first(ch))
goto error;
/* FALLTHROUGH */
default:
if (!cmd_string_first(ch)) {
xasprintf(&t, "$%c", ch);
return (t);
}
buf = xrealloc(buf, len + 1);
buf[len++] = ch;
for (;;) {
ch = cmd_string_getc(s, p);
if (ch == EOF || !cmd_string_other(ch))
break;
else {
if (len >= SIZE_MAX - 3)
goto error;
buf = xrealloc(buf, len + 1);
buf[len++] = ch;
}
}
}
if (fch == '{' && ch != '}')
goto error;
if (ch != EOF && fch != '{')
cmd_string_ungetc(p); /* ch */
buf = xrealloc(buf, len + 1);
buf[len] = '\0';
envent = environ_find(global_environ, buf);
free(buf);
if (envent == NULL)
return (xstrdup(""));
return (xstrdup(envent->value));
error:
free(buf);
return (NULL);
}
static char *
cmd_string_expand_tilde(const char *s, size_t *p)
{
struct passwd *pw;
struct environ_entry *envent;
char *home, *path, *user, *cp;
int last;
home = NULL;
last = cmd_string_getc(s, p);
if (last == EOF || last == '/' || last == ' '|| last == '\t') {
envent = environ_find(global_environ, "HOME");
if (envent != NULL && *envent->value != '\0')
home = envent->value;
else if ((pw = getpwuid(getuid())) != NULL)
home = pw->pw_dir;
} else {
cmd_string_ungetc(p);
cp = user = xmalloc(strlen(s));
for (;;) {
last = cmd_string_getc(s, p);
if (last == EOF ||
last == '/' ||
last == ' '||
last == '\t')
break;
*cp++ = last;
}
*cp = '\0';
if ((pw = getpwnam(user)) != NULL)
home = pw->pw_dir;
free(user);
}
if (home == NULL)
return (NULL);
if (last != EOF)
xasprintf(&path, "%s%c", home, last);
else
xasprintf(&path, "%s", home);
return (path);
}

View File

@@ -101,17 +101,17 @@ cmd_swap_pane_exec(struct cmd *self, struct cmdq_item *item)
if (!args_has(self->args, 'd')) {
if (src_w != dst_w) {
window_set_active_pane(src_w, dst_wp);
window_set_active_pane(dst_w, src_wp);
window_set_active_pane(src_w, dst_wp, 1);
window_set_active_pane(dst_w, src_wp, 1);
} else {
tmp_wp = dst_wp;
window_set_active_pane(src_w, tmp_wp);
window_set_active_pane(src_w, tmp_wp, 1);
}
} else {
if (src_w->active == src_wp)
window_set_active_pane(src_w, dst_wp);
window_set_active_pane(src_w, dst_wp, 1);
if (dst_w->active == dst_wp)
window_set_active_pane(dst_w, src_wp);
window_set_active_pane(dst_w, src_wp, 1);
}
if (src_w != dst_w) {
if (src_w->last == src_wp)

View File

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

View File

@@ -61,7 +61,7 @@ cmd_switch_client_exec(struct cmd *self, struct cmdq_item *item)
if ((c = cmd_find_client(item, args_get(args, 'c'), 0)) == NULL)
return (CMD_RETURN_ERROR);
if (tflag != NULL && tflag[strcspn(tflag, ":.")] != '\0') {
if (tflag != NULL && tflag[strcspn(tflag, ":.%")] != '\0') {
type = CMD_FIND_PANE;
flags = 0;
} else {
@@ -116,7 +116,7 @@ cmd_switch_client_exec(struct cmd *self, struct cmdq_item *item)
server_unzoom_window(wl->window);
if (wp != NULL) {
window_redraw_active_switch(wp->window, wp);
window_set_active_pane(wp->window, wp);
window_set_active_pane(wp->window, wp, 1);
}
session_set_current(s, wl);
cmd_find_from_session(&item->shared->current, s, 0);

238
cmd.c
View File

@@ -41,6 +41,7 @@ extern const struct cmd_entry cmd_confirm_before_entry;
extern const struct cmd_entry cmd_copy_mode_entry;
extern const struct cmd_entry cmd_delete_buffer_entry;
extern const struct cmd_entry cmd_detach_client_entry;
extern const struct cmd_entry cmd_display_menu_entry;
extern const struct cmd_entry cmd_display_message_entry;
extern const struct cmd_entry cmd_display_panes_entry;
extern const struct cmd_entry cmd_down_pane_entry;
@@ -129,6 +130,7 @@ const struct cmd_entry *cmd_table[] = {
&cmd_copy_mode_entry,
&cmd_delete_buffer_entry,
&cmd_detach_client_entry,
&cmd_display_menu_entry,
&cmd_display_message_entry,
&cmd_display_panes_entry,
&cmd_find_window_entry,
@@ -202,13 +204,43 @@ const struct cmd_entry *cmd_table[] = {
NULL
};
void
cmd_log_argv(int argc, char **argv, const char *prefix)
void printflike(3, 4)
cmd_log_argv(int argc, char **argv, const char *fmt, ...)
{
int i;
char *prefix;
va_list ap;
int i;
va_start(ap, fmt);
xvasprintf(&prefix, fmt, ap);
va_end(ap);
for (i = 0; i < argc; i++)
log_debug("%s: argv[%d]=%s", prefix, i, argv[i]);
free(prefix);
}
void
cmd_prepend_argv(int *argc, char ***argv, char *arg)
{
char **new_argv;
int i;
new_argv = xreallocarray(NULL, (*argc) + 1, sizeof *new_argv);
new_argv[0] = xstrdup(arg);
for (i = 0; i < *argc; i++)
new_argv[1 + i] = (*argv)[i];
free(*argv);
*argv = new_argv;
(*argc)++;
}
void
cmd_append_argv(int *argc, char ***argv, char *arg)
{
*argv = xreallocarray(*argv, (*argc) + 1, sizeof **argv);
(*argv)[(*argc)++] = xstrdup(arg);
}
int
@@ -219,7 +251,7 @@ cmd_pack_argv(int argc, char **argv, char *buf, size_t len)
if (argc == 0)
return (0);
cmd_log_argv(argc, argv, __func__);
cmd_log_argv(argc, argv, "%s", __func__);
*buf = '\0';
for (i = 0; i < argc; i++) {
@@ -256,7 +288,7 @@ cmd_unpack_argv(char *buf, size_t len, int argc, char ***argv)
buf += arglen;
len -= arglen;
}
cmd_log_argv(argc, *argv, __func__);
cmd_log_argv(argc, *argv, "%s", __func__);
return (0);
}
@@ -315,107 +347,103 @@ cmd_stringify_argv(int argc, char **argv)
return (buf);
}
static int
cmd_try_alias(int *argc, char ***argv)
char *
cmd_get_alias(const char *name)
{
struct options_entry *o;
struct options_array_item *a;
int old_argc = *argc, new_argc, i;
char **old_argv = *argv, **new_argv;
size_t wanted;
const char *s, *cp = NULL;
struct options_entry *o;
struct options_array_item *a;
union options_value *ov;
size_t wanted, n;
const char *equals;
o = options_get_only(global_options, "command-alias");
if (o == NULL)
return (-1);
wanted = strlen(old_argv[0]);
return (NULL);
wanted = strlen(name);
a = options_array_first(o);
while (a != NULL) {
s = options_array_item_value(a);
if (s != NULL) {
cp = strchr(s, '=');
if (cp != NULL &&
(size_t)(cp - s) == wanted &&
strncmp(old_argv[0], s, wanted) == 0)
break;
ov = options_array_item_value(a);
equals = strchr(ov->string, '=');
if (equals != NULL) {
n = equals - ov->string;
if (n == wanted && strncmp(name, ov->string, n) == 0)
return (xstrdup(equals + 1));
}
a = options_array_next(a);
}
if (a == NULL)
return (-1);
return (NULL);
}
if (cmd_string_split(cp + 1, &new_argc, &new_argv) != 0)
return (-1);
static const struct cmd_entry *
cmd_find(const char *name, char **cause)
{
const struct cmd_entry **loop, *entry, *found = NULL;
int ambiguous;
char s[BUFSIZ];
*argc = new_argc + old_argc - 1;
*argv = xcalloc((*argc) + 1, sizeof **argv);
ambiguous = 0;
for (loop = cmd_table; *loop != NULL; loop++) {
entry = *loop;
if (entry->alias != NULL && strcmp(entry->alias, name) == 0) {
ambiguous = 0;
found = entry;
break;
}
for (i = 0; i < new_argc; i++)
(*argv)[i] = xstrdup(new_argv[i]);
for (i = 1; i < old_argc; i++)
(*argv)[new_argc + i - 1] = xstrdup(old_argv[i]);
if (strncmp(entry->name, name, strlen(name)) != 0)
continue;
if (found != NULL)
ambiguous = 1;
found = entry;
log_debug("alias: %s=%s", old_argv[0], cp + 1);
for (i = 0; i < *argc; i++)
log_debug("alias: argv[%d] = %s", i, (*argv)[i]);
if (strcmp(entry->name, name) == 0)
break;
}
if (ambiguous)
goto ambiguous;
if (found == NULL) {
xasprintf(cause, "unknown command: %s", name);
return (NULL);
}
return (found);
cmd_free_argv(new_argc, new_argv);
return (0);
ambiguous:
*s = '\0';
for (loop = cmd_table; *loop != NULL; loop++) {
entry = *loop;
if (strncmp(entry->name, name, strlen(name)) != 0)
continue;
if (strlcat(s, entry->name, sizeof s) >= sizeof s)
break;
if (strlcat(s, ", ", sizeof s) >= sizeof s)
break;
}
s[strlen(s) - 2] = '\0';
xasprintf(cause, "ambiguous command: %s, could be: %s", name, s);
return (NULL);
}
struct cmd *
cmd_parse(int argc, char **argv, const char *file, u_int line, char **cause)
{
const struct cmd_entry *entry;
const char *name;
const struct cmd_entry **entryp, *entry;
struct cmd *cmd;
struct args *args;
char s[BUFSIZ];
int ambiguous, allocated = 0;
*cause = NULL;
if (argc == 0) {
xasprintf(cause, "no command");
return (NULL);
}
name = argv[0];
retry:
ambiguous = 0;
entry = NULL;
for (entryp = cmd_table; *entryp != NULL; entryp++) {
if ((*entryp)->alias != NULL &&
strcmp((*entryp)->alias, argv[0]) == 0) {
ambiguous = 0;
entry = *entryp;
break;
}
if (strncmp((*entryp)->name, argv[0], strlen(argv[0])) != 0)
continue;
if (entry != NULL)
ambiguous = 1;
entry = *entryp;
/* Bail now if an exact match. */
if (strcmp(entry->name, argv[0]) == 0)
break;
}
if ((ambiguous || entry == NULL) &&
server_proc != NULL &&
!allocated &&
cmd_try_alias(&argc, &argv) == 0) {
allocated = 1;
goto retry;
}
if (ambiguous)
goto ambiguous;
if (entry == NULL) {
xasprintf(cause, "unknown command: %s", name);
entry = cmd_find(name, cause);
if (entry == NULL)
return (NULL);
}
cmd_log_argv(argc, argv, entry->name);
cmd_log_argv(argc, argv, "%s: %s", __func__, entry->name);
args = args_parse(entry->args.template, argc, argv);
if (args == NULL)
@@ -433,23 +461,11 @@ retry:
cmd->file = xstrdup(file);
cmd->line = line;
if (allocated)
cmd_free_argv(argc, argv);
return (cmd);
cmd->alias = NULL;
cmd->argc = argc;
cmd->argv = cmd_copy_argv(argc, argv);
ambiguous:
*s = '\0';
for (entryp = cmd_table; *entryp != NULL; entryp++) {
if (strncmp((*entryp)->name, argv[0], strlen(argv[0])) != 0)
continue;
if (strlcat(s, (*entryp)->name, sizeof s) >= sizeof s)
break;
if (strlcat(s, ", ", sizeof s) >= sizeof s)
break;
}
s[strlen(s) - 2] = '\0';
xasprintf(cause, "ambiguous command: %s, could be: %s", name, s);
return (NULL);
return (cmd);
usage:
if (args != NULL)
@@ -458,6 +474,18 @@ usage:
return (NULL);
}
void
cmd_free(struct cmd *cmd)
{
free(cmd->alias);
cmd_free_argv(cmd->argc, cmd->argv);
free(cmd->file);
args_free(cmd->args);
free(cmd);
}
char *
cmd_print(struct cmd *cmd)
{
@@ -481,17 +509,16 @@ cmd_mouse_at(struct window_pane *wp, struct mouse_event *m, u_int *xp,
u_int x, y;
if (last) {
x = m->lx;
y = m->ly;
x = m->lx + m->ox;
y = m->ly + m->oy;
} else {
x = m->x;
y = m->y;
x = m->x + m->ox;
y = m->y + m->oy;
}
log_debug("%s: x=%u, y=%u%s", __func__, x, y, last ? " (last)" : "");
if (m->statusat == 0 && y > 0)
y--;
else if (m->statusat > 0 && y >= (u_int)m->statusat)
y = m->statusat - 1;
if (x < wp->xoff || x >= wp->xoff + wp->sx)
return (-1);
@@ -511,17 +538,22 @@ cmd_mouse_window(struct mouse_event *m, struct session **sp)
{
struct session *s;
struct window *w;
struct winlink *wl;
if (!m->valid || m->s == -1 || m->w == -1)
if (!m->valid)
return (NULL);
if ((s = session_find_by_id(m->s)) == NULL)
if (m->s == -1 || (s = session_find_by_id(m->s)) == NULL)
return (NULL);
if ((w = window_find_by_id(m->w)) == NULL)
return (NULL);
if (m->w == -1)
wl = s->curw;
else {
if ((w = window_find_by_id(m->w)) == NULL)
return (NULL);
wl = winlink_find_by_window(&s->windows, w);
}
if (sp != NULL)
*sp = s;
return (winlink_find_by_window(&s->windows, w));
return (wl);
}
/* Get current mouse pane if any. */

View File

@@ -67,6 +67,7 @@ void warnx(const char *, ...);
#define _PATH_DEVNULL "/dev/null"
#define _PATH_TTY "/dev/tty"
#define _PATH_DEV "/dev/"
#define _PATH_DEFPATH "/usr/bin:/bin"
#endif
#ifndef __OpenBSD__
@@ -312,10 +313,6 @@ int vasprintf(char **, const char *, va_list);
char *fgetln(FILE *, size_t *);
#endif
#ifndef HAVE_FPARSELN
char *fparseln(FILE *, size_t *, size_t *, const char *, int);
#endif
#ifndef HAVE_SETENV
/* setenv.c */
int setenv(const char *, const char *, int);

View File

@@ -1,221 +0,0 @@
/* $OpenBSD: fparseln.c,v 1.6 2005/08/02 21:46:23 espie Exp $ */
/* $NetBSD: fparseln.c,v 1.7 1999/07/02 15:49:12 simonb Exp $ */
/*
* Copyright (c) 1997 Christos Zoulas. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by Christos Zoulas.
* 4. The name of the author may not be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/* OPENBSD ORIGINAL: lib/libutil/fparseln.c */
#include <sys/types.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include "compat.h"
/*
* fparseln() specific operation flags.
*/
#define FPARSELN_UNESCESC 0x01
#define FPARSELN_UNESCCONT 0x02
#define FPARSELN_UNESCCOMM 0x04
#define FPARSELN_UNESCREST 0x08
#define FPARSELN_UNESCALL 0x0f
static int isescaped(const char *, const char *, int);
/* isescaped():
* Return true if the character in *p that belongs to a string
* that starts in *sp, is escaped by the escape character esc.
*/
static int
isescaped(const char *sp, const char *p, int esc)
{
const char *cp;
size_t ne;
/* No escape character */
if (esc == '\0')
return 1;
/* Count the number of escape characters that precede ours */
for (ne = 0, cp = p; --cp >= sp && *cp == esc; ne++)
continue;
/* Return true if odd number of escape characters */
return (ne & 1) != 0;
}
/* fparseln():
* Read a line from a file parsing continuations ending in \
* and eliminating trailing newlines, or comments starting with
* the comment char.
*/
char *
fparseln(FILE *fp, size_t *size, size_t *lineno, const char str[3],
int flags)
{
static const char dstr[3] = { '\\', '\\', '#' };
char *buf = NULL, *ptr, *cp, esc, con, nl, com;
size_t s, len = 0;
int cnt = 1;
if (str == NULL)
str = dstr;
esc = str[0];
con = str[1];
com = str[2];
/*
* XXX: it would be cool to be able to specify the newline character,
* but unfortunately, fgetln does not let us
*/
nl = '\n';
while (cnt) {
cnt = 0;
if (lineno)
(*lineno)++;
if ((ptr = fgetln(fp, &s)) == NULL)
break;
if (s && com) { /* Check and eliminate comments */
for (cp = ptr; cp < ptr + s; cp++)
if (*cp == com && !isescaped(ptr, cp, esc)) {
s = cp - ptr;
cnt = s == 0 && buf == NULL;
break;
}
}
if (s && nl) { /* Check and eliminate newlines */
cp = &ptr[s - 1];
if (*cp == nl)
s--; /* forget newline */
}
if (s && con) { /* Check and eliminate continuations */
cp = &ptr[s - 1];
if (*cp == con && !isescaped(ptr, cp, esc)) {
s--; /* forget escape */
cnt = 1;
}
}
if (s == 0 && buf != NULL)
continue;
if ((cp = realloc(buf, len + s + 1)) == NULL) {
free(buf);
return NULL;
}
buf = cp;
(void) memcpy(buf + len, ptr, s);
len += s;
buf[len] = '\0';
}
if ((flags & FPARSELN_UNESCALL) != 0 && esc && buf != NULL &&
strchr(buf, esc) != NULL) {
ptr = cp = buf;
while (cp[0] != '\0') {
int skipesc;
while (cp[0] != '\0' && cp[0] != esc)
*ptr++ = *cp++;
if (cp[0] == '\0' || cp[1] == '\0')
break;
skipesc = 0;
if (cp[1] == com)
skipesc += (flags & FPARSELN_UNESCCOMM);
if (cp[1] == con)
skipesc += (flags & FPARSELN_UNESCCONT);
if (cp[1] == esc)
skipesc += (flags & FPARSELN_UNESCESC);
if (cp[1] != com && cp[1] != con && cp[1] != esc)
skipesc = (flags & FPARSELN_UNESCREST);
if (skipesc)
cp++;
else
*ptr++ = *cp++;
*ptr++ = *cp++;
}
*ptr = '\0';
len = strlen(buf);
}
if (size)
*size = len;
return buf;
}
#ifdef TEST
int main(int, char **);
int
main(argc, argv)
int argc;
char **argv;
{
char *ptr;
size_t size, line;
line = 0;
while ((ptr = fparseln(stdin, &size, &line, NULL,
FPARSELN_UNESCALL)) != NULL)
printf("line %d (%d) |%s|\n", line, size, ptr);
return 0;
}
/*
# This is a test
line 1
line 2 \
line 3 # Comment
line 4 \# Not comment \\\\
# And a comment \
line 5 \\\
line 6
*/
#endif /* TEST */

View File

@@ -1,6 +1,6 @@
# configure.ac
AC_INIT([tmux], 2.9a)
AC_INIT([tmux], 3.0-rc3)
AC_PREREQ([2.60])
AC_CONFIG_AUX_DIR(etc)
@@ -28,6 +28,7 @@ AC_PROG_CC_C99
AC_PROG_CPP
AC_PROG_EGREP
AC_PROG_INSTALL
AC_PROG_YACC
PKG_PROG_PKG_CONFIG
AC_USE_SYSTEM_EXTENSIONS
@@ -457,14 +458,6 @@ else
AC_LIBOBJ(getopt)
fi
# Look for fparseln in libutil.
AC_SEARCH_LIBS(fparseln, util, found_fparseln=yes, found_fparseln=no)
if test "x$found_fparseln" = xyes; then
AC_DEFINE(HAVE_FPARSELN)
else
AC_LIBOBJ(fparseln)
fi
# Look for fdforkpty and forkpty in libutil.
AC_SEARCH_LIBS(fdforkpty, util, found_fdforkpty=yes, found_fdforkpty=no)
if test "x$found_fdforkpty" = xyes; then

View File

@@ -28,19 +28,14 @@
void
control_notify_input(struct client *c, struct window_pane *wp,
struct evbuffer *input)
const u_char *buf, size_t len)
{
u_char *buf;
size_t len;
struct evbuffer *message;
u_int i;
if (c->session == NULL)
return;
buf = EVBUFFER_DATA(input);
len = EVBUFFER_LENGTH(input);
/*
* Only write input if the window pane is linked to a window belonging
* to the client's session.

View File

@@ -68,10 +68,9 @@ control_error(struct cmdq_item *item, void *data)
void
control_callback(struct client *c, int closed, __unused void *data)
{
char *line, *cause;
struct cmd_list *cmdlist;
struct cmd *cmd;
char *line;
struct cmdq_item *item;
struct cmd_parse_result *pr;
if (closed)
c->flags |= CLIENT_EXIT;
@@ -85,16 +84,20 @@ control_callback(struct client *c, int closed, __unused void *data)
break;
}
cmdlist = cmd_string_parse(line, NULL, 0, &cause);
if (cmdlist == NULL) {
item = cmdq_get_callback(control_error, cause);
pr = cmd_parse_from_string(line, NULL);
switch (pr->status) {
case CMD_PARSE_EMPTY:
break;
case CMD_PARSE_ERROR:
item = cmdq_get_callback(control_error, pr->error);
cmdq_append(c, item);
} else {
TAILQ_FOREACH(cmd, &cmdlist->list, qentry)
cmd->flags |= CMD_CONTROL;
item = cmdq_get_command(cmdlist, NULL, NULL, 0);
break;
case CMD_PARSE_SUCCESS:
item = cmdq_get_command(pr->cmdlist, NULL, NULL, 0);
item->shared->flags |= CMDQ_SHARED_CONTROL;
cmdq_append(c, item);
cmd_list_free(cmdlist);
cmd_list_free(pr->cmdlist);
break;
}
free(line);

View File

@@ -177,20 +177,16 @@ environ_update(struct options *oo, struct environ *src, struct environ *dst)
struct environ_entry *envent;
struct options_entry *o;
struct options_array_item *a;
const char *value;
union options_value *ov;
o = options_get(oo, "update-environment");
if (o == NULL)
return;
a = options_array_first(o);
while (a != NULL) {
value = options_array_item_value(a);
if (value == NULL) {
a = options_array_next(a);
continue;
}
if ((envent = environ_find(src, value)) == NULL)
environ_clear(dst, value);
ov = options_array_item_value(a);
if ((envent = environ_find(src, ov->string)) == NULL)
environ_clear(dst, ov->string);
else
environ_set(dst, envent->name, "%s", envent->value);
a = options_array_next(a);

View File

@@ -6,7 +6,7 @@
# Some tweaks to the status line
set -g status-right "%H:%M"
set -g window-status-current-attr "underscore"
set -g window-status-current-style "underscore"
# If running inside tmux ($TMUX is set), then change the status line to red
%if #{TMUX}

View File

@@ -345,12 +345,6 @@ format_draw_centre(struct screen_write_ctx *octx, u_int available, u_int ocx,
/* Write left at 0. */
format_draw_put(octx, ocx, ocy, left, frs, 0, 0, width_left);
/* Write after at available - width_after. */
format_draw_put(octx, ocx, ocy, after, frs,
available - width_after,
after->cx - width_after,
width_after);
/* Write right at available - width_right. */
format_draw_put(octx, ocx, ocy, right, frs,
available - width_right,
@@ -374,10 +368,10 @@ format_draw_centre(struct screen_write_ctx *octx, u_int available, u_int ocx,
/*
* Write after at
* middle + width_list / 2 - width_centre.
* middle - width_list / 2 + width_list
*/
format_draw_put(octx, ocx, ocy, after, frs,
middle + width_list / 2,
middle - width_list / 2 + width_list,
0,
width_after);

278
format.c
View File

@@ -127,6 +127,8 @@ struct format_tree {
time_t time;
u_int loop;
struct mouse_event m;
RB_HEAD(format_entry_tree, format_entry) tree;
};
static int format_entry_cmp(struct format_entry *, struct format_entry *);
@@ -715,6 +717,121 @@ format_cb_cursor_character(struct format_tree *ft, struct format_entry *fe)
xasprintf(&fe->value, "%.*s", (int)gc.data.size, gc.data.data);
}
/* Callback for mouse_word. */
static void
format_cb_mouse_word(struct format_tree *ft, struct format_entry *fe)
{
struct window_pane *wp;
u_int x, y, end;
struct grid *gd;
struct grid_line *gl;
struct grid_cell gc;
const char *ws;
struct utf8_data *ud = NULL;
size_t size = 0;
int found = 0;
if (!ft->m.valid)
return;
wp = cmd_mouse_pane(&ft->m, NULL, NULL);
if (wp == NULL)
return;
if (cmd_mouse_at(wp, &ft->m, &x, &y, 0) != 0)
return;
gd = wp->base.grid;
ws = options_get_string(global_s_options, "word-separators");
y = gd->hsize + y;
for (;;) {
grid_get_cell(gd, x, y, &gc);
if (gc.flags & GRID_FLAG_PADDING)
break;
if (utf8_cstrhas(ws, &gc.data)) {
found = 1;
break;
}
if (x == 0) {
if (y == 0)
break;
gl = &gd->linedata[y - 1];
if (~gl->flags & GRID_LINE_WRAPPED)
break;
y--;
x = grid_line_length(gd, y);
if (x == 0)
break;
}
x--;
}
for (;;) {
if (found) {
end = grid_line_length(gd, y);
if (end == 0 || x == end - 1) {
if (y == gd->hsize + gd->sy - 1)
break;
gl = &gd->linedata[y];
if (~gl->flags & GRID_LINE_WRAPPED)
break;
y++;
x = 0;
} else
x++;
}
found = 1;
grid_get_cell(gd, x, y, &gc);
if (gc.flags & GRID_FLAG_PADDING)
break;
if (utf8_cstrhas(ws, &gc.data))
break;
ud = xreallocarray(ud, size + 2, sizeof *ud);
memcpy(&ud[size++], &gc.data, sizeof *ud);
}
if (size != 0) {
ud[size].size = 0;
fe->value = utf8_tocstr(ud);
free(ud);
}
}
/* Callback for mouse_line. */
static void
format_cb_mouse_line(struct format_tree *ft, struct format_entry *fe)
{
struct window_pane *wp;
u_int x, y;
struct grid *gd;
struct grid_cell gc;
struct utf8_data *ud = NULL;
size_t size = 0;
if (!ft->m.valid)
return;
wp = cmd_mouse_pane(&ft->m, NULL, NULL);
if (wp == NULL)
return;
if (cmd_mouse_at(wp, &ft->m, &x, &y, 0) != 0)
return;
gd = wp->base.grid;
y = gd->hsize + y;
for (x = 0; x < grid_line_length(gd, y); x++) {
grid_get_cell(gd, x, y, &gc);
if (gc.flags & GRID_FLAG_PADDING)
break;
ud = xreallocarray(ud, size + 2, sizeof *ud);
memcpy(&ud[size++], &gc.data, sizeof *ud);
}
if (size != 0) {
ud[size].size = 0;
fe->value = utf8_tocstr(ud);
free(ud);
}
}
/* Merge a format tree. */
static void
format_merge(struct format_tree *ft, struct format_tree *from)
@@ -727,6 +844,35 @@ format_merge(struct format_tree *ft, struct format_tree *from)
}
}
/* Add item bits to tree. */
static void
format_create_add_item(struct format_tree *ft, struct cmdq_item *item)
{
struct mouse_event *m;
struct window_pane *wp;
u_int x, y;
if (item->cmd != NULL)
format_add(ft, "command", "%s", item->cmd->entry->name);
if (item->shared == NULL)
return;
if (item->shared->formats != NULL)
format_merge(ft, item->shared->formats);
m = &item->shared->mouse;
if (m->valid && ((wp = cmd_mouse_pane(m, NULL, NULL)) != NULL)) {
format_add(ft, "mouse_pane", "%%%u", wp->id);
if (cmd_mouse_at(wp, m, &x, &y, 0) == 0) {
format_add(ft, "mouse_x", "%u", x);
format_add(ft, "mouse_y", "%u", y);
format_add_cb(ft, "mouse_word", format_cb_mouse_word);
format_add_cb(ft, "mouse_line", format_cb_mouse_line);
}
}
memcpy(&ft->m, m, sizeof ft->m);
}
/* Create a new tree. */
struct format_tree *
format_create(struct client *c, struct cmdq_item *item, int tag, int flags)
@@ -768,12 +914,8 @@ format_create(struct client *c, struct cmdq_item *item, int tag, int flags)
}
}
if (item != NULL) {
if (item->cmd != NULL)
format_add(ft, "command", "%s", item->cmd->entry->name);
if (item->shared != NULL && item->shared->formats != NULL)
format_merge(ft, item->shared->formats);
}
if (item != NULL)
format_create_add_item(ft, item);
return (ft);
}
@@ -920,9 +1062,8 @@ format_find(struct format_tree *ft, const char *key, int modifiers)
struct environ_entry *envent;
static char s[64];
struct options_entry *o;
const char *found;
int idx;
char *copy, *saved;
char *found, *saved;
if (~modifiers & FORMAT_TIMESTRING) {
o = options_parse_get(global_options, key, &idx, 0);
@@ -949,12 +1090,11 @@ format_find(struct format_tree *ft, const char *key, int modifiers)
return (NULL);
ctime_r(&fe->t, s);
s[strcspn(s, "\n")] = '\0';
found = s;
found = xstrdup(s);
goto found;
}
if (fe->t != 0) {
xsnprintf(s, sizeof s, "%lld", (long long)fe->t);
found = s;
xasprintf(&found, "%lld", (long long)fe->t);
goto found;
}
if (fe->value == NULL && fe->cb != NULL) {
@@ -962,7 +1102,7 @@ format_find(struct format_tree *ft, const char *key, int modifiers)
if (fe->value == NULL)
fe->value = xstrdup("");
}
found = fe->value;
found = xstrdup(fe->value);
goto found;
}
@@ -972,8 +1112,8 @@ format_find(struct format_tree *ft, const char *key, int modifiers)
envent = environ_find(ft->s->environ, key);
if (envent == NULL)
envent = environ_find(global_environ, key);
if (envent != NULL) {
found = envent->value;
if (envent != NULL && envent->value != NULL) {
found = xstrdup(envent->value);
goto found;
}
}
@@ -983,23 +1123,22 @@ format_find(struct format_tree *ft, const char *key, int modifiers)
found:
if (found == NULL)
return (NULL);
copy = xstrdup(found);
if (modifiers & FORMAT_BASENAME) {
saved = copy;
copy = xstrdup(basename(saved));
saved = found;
found = xstrdup(basename(saved));
free(saved);
}
if (modifiers & FORMAT_DIRNAME) {
saved = copy;
copy = xstrdup(dirname(saved));
saved = found;
found = xstrdup(dirname(saved));
free(saved);
}
if (modifiers & FORMAT_QUOTE) {
saved = copy;
copy = xstrdup(format_quote(saved));
saved = found;
found = xstrdup(format_quote(saved));
free(saved);
}
return (copy);
return (found);
}
/* Skip until end. */
@@ -1107,13 +1246,13 @@ format_build_modifiers(struct format_tree *ft, const char **s, u_int *count)
/*
* Modifiers are a ; separated list of the forms:
* l,m,C,b,d,t,q,E,T,S,W,P
* l,m,C,b,d,t,q,E,T,S,W,P,<,>
* =a
* =/a
* =/a/
* s/a/b/
* s/a/b
* ||,&&,!=,==
* ||,&&,!=,==,<=,>=
*/
*count = 0;
@@ -1124,7 +1263,7 @@ format_build_modifiers(struct format_tree *ft, const char **s, u_int *count)
cp++;
/* Check single character modifiers with no arguments. */
if (strchr("lmCbdtqETSWP", cp[0]) != NULL &&
if (strchr("lmCbdtqETSWP<>", cp[0]) != NULL &&
format_is_end(cp[1])) {
format_add_modifier(&list, count, cp, 1, NULL, 0);
cp++;
@@ -1135,7 +1274,9 @@ format_build_modifiers(struct format_tree *ft, const char **s, u_int *count)
if ((memcmp("||", cp, 2) == 0 ||
memcmp("&&", cp, 2) == 0 ||
memcmp("!=", cp, 2) == 0 ||
memcmp("==", cp, 2) == 0) &&
memcmp("==", cp, 2) == 0 ||
memcmp("<=", cp, 2) == 0 ||
memcmp(">=", cp, 2) == 0) &&
format_is_end(cp[2])) {
format_add_modifier(&list, count, cp, 2, NULL, 0);
cp += 2;
@@ -1377,7 +1518,7 @@ format_replace(struct format_tree *ft, const char *key, size_t keylen,
char **buf, size_t *len, size_t *off)
{
struct window_pane *wp = ft->wp;
const char *errptr, *copy, *cp;
const char *errptr, *copy, *cp, *marker = NULL;
char *copy0, *condition, *found, *new;
char *value, *left, *right;
size_t valuelen;
@@ -1404,6 +1545,8 @@ format_replace(struct format_tree *ft, const char *key, size_t keylen,
if (fm->size == 1) {
switch (fm->modifier[0]) {
case 'm':
case '<':
case '>':
cmp = fm;
break;
case 'C':
@@ -1415,12 +1558,14 @@ format_replace(struct format_tree *ft, const char *key, size_t keylen,
sub = fm;
break;
case '=':
if (fm->argc != 1)
if (fm->argc != 1 && fm->argc != 2)
break;
limit = strtonum(fm->argv[0], INT_MIN, INT_MAX,
&errptr);
if (errptr != NULL)
limit = 0;
if (fm->argc == 2 && fm->argv[1] != NULL)
marker = fm->argv[1];
break;
case 'l':
modifiers |= FORMAT_LITERAL;
@@ -1457,7 +1602,9 @@ format_replace(struct format_tree *ft, const char *key, size_t keylen,
if (strcmp(fm->modifier, "||") == 0 ||
strcmp(fm->modifier, "&&") == 0 ||
strcmp(fm->modifier, "==") == 0 ||
strcmp(fm->modifier, "!=") == 0)
strcmp(fm->modifier, "!=") == 0 ||
strcmp(fm->modifier, ">=") == 0 ||
strcmp(fm->modifier, "<=") == 0)
cmp = fm;
}
}
@@ -1520,8 +1667,27 @@ format_replace(struct format_tree *ft, const char *key, size_t keylen,
value = xstrdup("1");
else
value = xstrdup("0");
}
else if (strcmp(cmp->modifier, "m") == 0) {
} else if (strcmp(cmp->modifier, "<") == 0) {
if (strcmp(left, right) < 0)
value = xstrdup("1");
else
value = xstrdup("0");
} else if (strcmp(cmp->modifier, ">") == 0) {
if (strcmp(left, right) > 0)
value = xstrdup("1");
else
value = xstrdup("0");
} else if (strcmp(cmp->modifier, "<=") == 0) {
if (strcmp(left, right) <= 0)
value = xstrdup("1");
else
value = xstrdup("0");
} else if (strcmp(cmp->modifier, ">=") == 0) {
if (strcmp(left, right) >= 0)
value = xstrdup("1");
else
value = xstrdup("0");
} else if (strcmp(cmp->modifier, "m") == 0) {
if (fnmatch(left, right, 0) == 0)
value = xstrdup("1");
else
@@ -1614,14 +1780,24 @@ done:
/* Truncate the value if needed. */
if (limit > 0) {
new = format_trim_left(value, limit);
format_log(ft, "applied length limit %d: %s", limit, new);
free(value);
value = new;
if (marker != NULL && strcmp(new, value) != 0) {
free(value);
xasprintf(&value, "%s%s", new, marker);
} else {
free(value);
value = new;
}
format_log(ft, "applied length limit %d: %s", limit, value);
} else if (limit < 0) {
new = format_trim_right(value, -limit);
format_log(ft, "applied length limit %d: %s", limit, new);
free(value);
value = new;
if (marker != NULL && strcmp(new, value) != 0) {
free(value);
xasprintf(&value, "%s%s", marker, new);
} else {
free(value);
value = new;
}
format_log(ft, "applied length limit %d: %s", limit, value);
}
/* Expand the buffer and copy in the value. */
@@ -1822,6 +1998,23 @@ void
format_defaults(struct format_tree *ft, struct client *c, struct session *s,
struct winlink *wl, struct window_pane *wp)
{
if (c != NULL)
log_debug("%s: c=%s", __func__, c->name);
else
log_debug("%s: s=none", __func__);
if (s != NULL)
log_debug("%s: s=$%u", __func__, s->id);
else
log_debug("%s: s=none", __func__);
if (wl != NULL)
log_debug("%s: wl=%u w=@%u", __func__, wl->idx, wl->window->id);
else
log_debug("%s: wl=none", __func__);
if (wp != NULL)
log_debug("%s: wp=%%%u", __func__, wp->id);
else
log_debug("%s: wp=none", __func__);
if (c != NULL && s != NULL && c->session != s)
log_debug("%s: session does not match", __func__);
@@ -2033,7 +2226,16 @@ format_defaults_pane(struct format_tree *ft, struct window_pane *wp)
if ((wp->flags & PANE_STATUSREADY) && WIFEXITED(status))
format_add(ft, "pane_dead_status", "%d", WEXITSTATUS(status));
format_add(ft, "pane_dead", "%d", wp->fd == -1);
if (~wp->flags & PANE_EMPTY)
format_add(ft, "pane_dead", "%d", wp->fd == -1);
else
format_add(ft, "pane_dead", "0");
if (server_check_marked() && marked_pane.wp == wp)
format_add(ft, "pane_marked", "1");
else
format_add(ft, "pane_marked", "0");
format_add(ft, "pane_marked_set", "%d", server_check_marked());
format_add(ft, "pane_left", "%u", wp->xoff);
format_add(ft, "pane_top", "%u", wp->yoff);

20
grid.c
View File

@@ -790,6 +790,7 @@ grid_string_cells_code(const struct grid_cell *lastgc,
{ GRID_ATTR_UNDERSCORE_3, 43 },
{ GRID_ATTR_UNDERSCORE_4, 44 },
{ GRID_ATTR_UNDERSCORE_5, 45 },
{ GRID_ATTR_OVERLINE, 53 },
};
n = 0;
@@ -1349,3 +1350,22 @@ grid_unwrap_position(struct grid *gd, u_int *px, u_int *py, u_int wx, u_int wy)
*px = wx;
*py = yy;
}
/* Get length of line. */
u_int
grid_line_length(struct grid *gd, u_int py)
{
struct grid_cell gc;
u_int px;
px = grid_get_line(gd, py)->cellsize;
if (px > gd->sx)
px = gd->sx;
while (px > 0) {
grid_get_cell(gd, px - 1, py, &gc);
if (gc.data.size != 1 || *gc.data.data != ' ')
break;
px--;
}
return (px);
}

173
hooks.c
View File

@@ -1,173 +0,0 @@
/* $OpenBSD$ */
/*
* Copyright (c) 2012 Thomas Adam <thomas@xteddy.org>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER
* IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
* OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include <sys/types.h>
#include <stdlib.h>
#include <string.h>
#include "tmux.h"
struct hooks {
RB_HEAD(hooks_tree, hook) tree;
struct hooks *parent;
};
static int hooks_cmp(struct hook *, struct hook *);
RB_GENERATE_STATIC(hooks_tree, hook, entry, hooks_cmp);
static struct hook *hooks_find1(struct hooks *, const char *);
static void hooks_free1(struct hooks *, struct hook *);
static int
hooks_cmp(struct hook *hook1, struct hook *hook2)
{
return (strcmp(hook1->name, hook2->name));
}
struct hooks *
hooks_get(struct session *s)
{
if (s != NULL)
return (s->hooks);
return (global_hooks);
}
struct hooks *
hooks_create(struct hooks *parent)
{
struct hooks *hooks;
hooks = xcalloc(1, sizeof *hooks);
RB_INIT(&hooks->tree);
hooks->parent = parent;
return (hooks);
}
static void
hooks_free1(struct hooks *hooks, struct hook *hook)
{
RB_REMOVE(hooks_tree, &hooks->tree, hook);
cmd_list_free(hook->cmdlist);
free((char *)hook->name);
free(hook);
}
void
hooks_free(struct hooks *hooks)
{
struct hook *hook, *hook1;
RB_FOREACH_SAFE(hook, hooks_tree, &hooks->tree, hook1)
hooks_free1(hooks, hook);
free(hooks);
}
struct hook *
hooks_first(struct hooks *hooks)
{
return (RB_MIN(hooks_tree, &hooks->tree));
}
struct hook *
hooks_next(struct hook *hook)
{
return (RB_NEXT(hooks_tree, &hooks->tree, hook));
}
void
hooks_add(struct hooks *hooks, const char *name, struct cmd_list *cmdlist)
{
struct hook *hook;
if ((hook = hooks_find1(hooks, name)) != NULL)
hooks_free1(hooks, hook);
hook = xcalloc(1, sizeof *hook);
hook->name = xstrdup(name);
hook->cmdlist = cmdlist;
hook->cmdlist->references++;
RB_INSERT(hooks_tree, &hooks->tree, hook);
}
void
hooks_remove(struct hooks *hooks, const char *name)
{
struct hook *hook;
if ((hook = hooks_find1(hooks, name)) != NULL)
hooks_free1(hooks, hook);
}
static struct hook *
hooks_find1(struct hooks *hooks, const char *name)
{
struct hook hook;
hook.name = name;
return (RB_FIND(hooks_tree, &hooks->tree, &hook));
}
struct hook *
hooks_find(struct hooks *hooks, const char *name)
{
struct hook hook0, *hook;
hook0.name = name;
hook = RB_FIND(hooks_tree, &hooks->tree, &hook0);
while (hook == NULL) {
hooks = hooks->parent;
if (hooks == NULL)
break;
hook = RB_FIND(hooks_tree, &hooks->tree, &hook0);
}
return (hook);
}
void
hooks_insert(struct hooks *hooks, struct cmdq_item *item,
struct cmd_find_state *fs, const char *fmt, ...)
{
struct hook *hook;
va_list ap;
char *name;
struct cmdq_item *new_item;
if (item->flags & CMDQ_NOHOOKS)
return;
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 (parent %p)", name, item);
new_item = cmdq_get_command(hook->cmdlist, fs, NULL, CMDQ_NOHOOKS);
cmdq_format(new_item, "hook", "%s", name);
if (item != NULL)
cmdq_insert_after(item, new_item);
else
cmdq_append(NULL, new_item);
free(name);
}

59
input.c
View File

@@ -873,19 +873,28 @@ input_set_state(struct window_pane *wp, const struct input_transition *itr)
/* Parse input. */
void
input_parse(struct window_pane *wp)
{
struct evbuffer *evb = wp->event->input;
input_parse_buffer(wp, EVBUFFER_DATA(evb), EVBUFFER_LENGTH(evb));
evbuffer_drain(evb, EVBUFFER_LENGTH(evb));
}
/* Parse given input. */
void
input_parse_buffer(struct window_pane *wp, u_char *buf, size_t len)
{
struct input_ctx *ictx = wp->ictx;
struct screen_write_ctx *sctx = &ictx->ctx;
const struct input_transition *itr;
struct evbuffer *evb = wp->event->input;
u_char *buf;
size_t len, off;
size_t off = 0;
if (EVBUFFER_LENGTH(evb) == 0)
if (len == 0)
return;
window_update_activity(wp->window);
wp->flags |= PANE_CHANGED;
notify_input(wp, buf, len);
/*
* Open the screen. Use NULL wp if there is a mode set as don't want to
@@ -897,12 +906,6 @@ input_parse(struct window_pane *wp)
screen_write_start(sctx, NULL, &wp->base);
ictx->wp = wp;
buf = EVBUFFER_DATA(evb);
len = EVBUFFER_LENGTH(evb);
off = 0;
notify_input(wp, evb);
log_debug("%s: %%%u %s, %zu bytes: %.*s", __func__, wp->id,
ictx->state->name, len, (int)len, buf);
@@ -950,8 +953,6 @@ input_parse(struct window_pane *wp)
/* Close the screen. */
screen_write_stop(sctx);
evbuffer_drain(evb, len);
}
/* Split the parameter list (if any). */
@@ -1185,6 +1186,8 @@ input_c0_dispatch(struct input_ctx *ictx)
case '\013': /* VT */
case '\014': /* FF */
screen_write_linefeed(sctx, 0, ictx->cell.cell.bg);
if (s->mode & MODE_CRLF)
screen_write_carriagereturn(sctx);
break;
case '\015': /* CR */
screen_write_carriagereturn(sctx);
@@ -1935,23 +1938,25 @@ input_csi_dispatch_sgr_colon(struct input_ctx *ictx, u_int i)
}
return;
}
if (p[0] != 38 && p[0] != 48)
if (n < 2 || (p[0] != 38 && p[0] != 48))
return;
if (p[1] == -1)
i = 2;
else
i = 1;
switch (p[i]) {
switch (p[1]) {
case 2:
if (n < i + 4)
if (n < 3)
break;
input_csi_dispatch_sgr_rgb_do(ictx, p[0], p[i + 1], p[i + 2],
p[i + 3]);
if (n == 5)
i = 2;
else
i = 3;
if (n < i + 3)
break;
input_csi_dispatch_sgr_rgb_do(ictx, p[0], p[i], p[i + 1],
p[i + 2]);
break;
case 5:
if (n < i + 2)
if (n < 3)
break;
input_csi_dispatch_sgr_256_do(ictx, p[0], p[i + 1]);
input_csi_dispatch_sgr_256_do(ictx, p[0], p[2]);
break;
}
}
@@ -2067,6 +2072,12 @@ input_csi_dispatch_sgr(struct input_ctx *ictx)
case 49:
gc->bg = 8;
break;
case 53:
gc->attr |= GRID_ATTR_OVERLINE;
break;
case 55:
gc->attr &= ~GRID_ATTR_OVERLINE;
break;
case 90:
case 91:
case 92:
@@ -2432,7 +2443,7 @@ input_osc_52(struct input_ctx *ictx, const char *p)
screen_write_stop(&ctx);
notify_pane("pane-set-clipboard", wp);
paste_add(out, outlen);
paste_add(NULL, out, outlen);
}
/* Handle the OSC 104 sequence for unsetting (multiple) palette entries. */

View File

@@ -24,6 +24,52 @@
#include "tmux.h"
#define DEFAULT_CLIENT_MENU \
" 'Detach' 'd' {detach-client}" \
" 'Detach & Kill' 'X' {detach-client -P}" \
" 'Detach Others' 'o' {detach-client -a}" \
" ''" \
" 'Lock' 'l' {lock-client}"
#define DEFAULT_SESSION_MENU \
" 'Next' 'n' {switch-client -n}" \
" 'Previous' 'p' {switch-client -p}" \
" ''" \
" 'Renumber' 'N' {move-window -r}" \
" 'Rename' 'n' {command-prompt -I \"#S\" \"rename-session -- '%%'\"}" \
" ''" \
" 'New Session' 's' {new-session}" \
" 'New Window' 'w' {new-window}"
#define DEFAULT_WINDOW_MENU \
" 'Swap Left' 'l' {swap-window -t:-1}" \
" 'Swap Right' 'r' {swap-window -t:+1}" \
" '#{?pane_marked_set,,-}Swap Marked' 's' {swap-window}" \
" ''" \
" 'Kill' 'X' {kill-window}" \
" 'Respawn' 'R' {respawn-window -k}" \
" '#{?pane_marked,Unmark,Mark}' 'm' {select-pane -m}" \
" 'Rename' 'n' {command-prompt -I \"#W\" \"rename-window -- '%%'\"}" \
" ''" \
" 'New After' 'w' {new-window -a}" \
" 'New At End' 'W' {new-window}"
#define DEFAULT_PANE_MENU \
" '#{?mouse_word,Search For #[underscore]#{=/9/...:mouse_word},}' 'C-r' {copy-mode -t=; send -Xt= search-backward \"#{q:mouse_word}\"}" \
" '#{?mouse_word,Type #[underscore]#{=/9/...:mouse_word},}' 'C-y' {send-keys -l -- \"#{q:mouse_word}\"}" \
" '#{?mouse_word,Copy #[underscore]#{=/9/...:mouse_word},}' 'c' {set-buffer -- \"#{q:mouse_word}\"}" \
" '#{?mouse_line,Copy Line,}' 'l' {set-buffer -- \"#{q:mouse_line}\"}" \
" ''" \
" 'Horizontal Split' 'h' {split-window -h}" \
" 'Vertical Split' 'v' {split-window -v}" \
" ''" \
" 'Swap Up' 'u' {swap-pane -U}" \
" 'Swap Down' 'd' {swap-pane -D}" \
" '#{?pane_marked_set,,-}Swap Marked' 's' {swap-pane}" \
" ''" \
" 'Kill' 'X' {kill-pane}" \
" 'Respawn' 'R' {respawn-pane -k}" \
" '#{?pane_marked,Unmark,Mark}' 'm' {select-pane -m}" \
" '#{?window_zoomed_flag,Unzoom,Zoom}' 'z' {resize-pane -Z}"
static int key_bindings_cmp(struct key_binding *, struct key_binding *);
RB_GENERATE_STATIC(key_bindings, key_binding, entry, key_bindings_cmp);
static int key_table_cmp(struct key_table *, struct key_table *);
@@ -242,8 +288,8 @@ key_bindings_init(void)
"bind w choose-tree -Zw",
"bind x confirm-before -p\"kill-pane #P? (y/n)\" kill-pane",
"bind z resize-pane -Z",
"bind { swap-pane -U",
"bind } swap-pane -D",
"bind '{' swap-pane -U",
"bind '}' swap-pane -D",
"bind '~' show-messages",
"bind PPage copy-mode -u",
"bind -r Up select-pane -U",
@@ -271,14 +317,22 @@ key_bindings_init(void)
"bind -r C-Down resize-pane -D",
"bind -r C-Left resize-pane -L",
"bind -r C-Right resize-pane -R",
"bind -n MouseDown1Pane select-pane -t=\\; send-keys -M",
"bind -n MouseDrag1Border resize-pane -M",
"bind -n MouseDown1Status select-window -t=",
"bind -n WheelDownStatus next-window",
"bind -n WheelUpStatus previous-window",
"bind -n MouseDrag1Pane if -Ft= '#{mouse_any_flag}' 'if -Ft= \"#{pane_in_mode}\" \"copy-mode -M\" \"send-keys -M\"' 'copy-mode -M'",
"bind -n MouseDown3Pane if-shell -Ft= '#{mouse_any_flag}' 'select-pane -t=; send-keys -M' 'select-pane -mt='",
"bind -n WheelUpPane if-shell -Ft= '#{mouse_any_flag}' 'send-keys -M' 'if -Ft= \"#{pane_in_mode}\" \"send-keys -M\" \"copy-mode -et=\"'",
"bind -n WheelUpPane if -Ft= '#{mouse_any_flag}' 'send-keys -M' 'if -Ft= \"#{pane_in_mode}\" \"send-keys -M\" \"copy-mode -et=\"'",
"bind -n MouseDown3StatusRight display-menu -t= -xM -yS -T \"#[align=centre]#{client_name}\" " DEFAULT_CLIENT_MENU,
"bind -n MouseDown3StatusLeft display-menu -t= -xM -yS -T \"#[align=centre]#{session_name}\" " DEFAULT_SESSION_MENU,
"bind -n MouseDown3Status display-menu -t= -xW -yS -T \"#[align=centre]#{window_index}:#{window_name}\" " DEFAULT_WINDOW_MENU,
"bind C-m display-menu -xW -yS -T \"#[align=centre]#{window_index}:#{window_name}\" " DEFAULT_WINDOW_MENU,
"bind -n MouseDown3Pane if -Ft= '#{||:#{mouse_any_flag},#{pane_in_mode}}' 'select-pane -t=; send-keys -M' {display-menu -t= -xM -yM -T \"#[align=centre]#{pane_index} (#{pane_id})\" " DEFAULT_PANE_MENU "}",
"bind -n M-MouseDown3Pane display-menu -t= -xM -yM -T \"#[align=centre]#{pane_index} (#{pane_id})\" " DEFAULT_PANE_MENU,
"bind M-m display-menu -xP -yP -T \"#[align=centre]#{pane_index} (#{pane_id})\" " DEFAULT_PANE_MENU,
"bind -Tcopy-mode C-Space send -X begin-selection",
"bind -Tcopy-mode C-a send -X start-of-line",
@@ -335,13 +389,15 @@ key_bindings_init(void)
"bind -Tcopy-mode M-> send -X history-bottom",
"bind -Tcopy-mode M-R send -X top-line",
"bind -Tcopy-mode M-b send -X previous-word",
"bind -Tcopy-mode C-M-b send -X previous-matching-bracket",
"bind -Tcopy-mode M-f send -X next-word-end",
"bind -Tcopy-mode C-M-f send -X next-matching-bracket",
"bind -Tcopy-mode M-m send -X back-to-indentation",
"bind -Tcopy-mode M-r send -X middle-line",
"bind -Tcopy-mode M-v send -X page-up",
"bind -Tcopy-mode M-w send -X copy-selection-and-cancel",
"bind -Tcopy-mode M-{ send -X previous-paragraph",
"bind -Tcopy-mode M-} send -X next-paragraph",
"bind -Tcopy-mode 'M-{' send -X previous-paragraph",
"bind -Tcopy-mode 'M-}' send -X next-paragraph",
"bind -Tcopy-mode M-Up send -X halfpage-up",
"bind -Tcopy-mode M-Down send -X halfpage-down",
"bind -Tcopy-mode C-Up send -X scroll-up",
@@ -406,8 +462,9 @@ key_bindings_init(void)
"bind -Tcopy-mode-vi t command-prompt -1p'(jump to forward)' 'send -X jump-to-forward \"%%%\"'",
"bind -Tcopy-mode-vi v send -X rectangle-toggle",
"bind -Tcopy-mode-vi w send -X next-word",
"bind -Tcopy-mode-vi { send -X previous-paragraph",
"bind -Tcopy-mode-vi } send -X next-paragraph",
"bind -Tcopy-mode-vi '{' send -X previous-paragraph",
"bind -Tcopy-mode-vi '}' send -X next-paragraph",
"bind -Tcopy-mode-vi % send -X next-matching-bracket",
"bind -Tcopy-mode-vi MouseDown1Pane select-pane",
"bind -Tcopy-mode-vi MouseDrag1Pane select-pane\\; send -X begin-selection",
"bind -Tcopy-mode-vi MouseDragEnd1Pane send -X copy-selection-and-cancel",
@@ -425,16 +482,15 @@ key_bindings_init(void)
"bind -Tcopy-mode-vi C-Up send -X scroll-up",
"bind -Tcopy-mode-vi C-Down send -X scroll-down",
};
u_int i;
struct cmd_list *cmdlist;
char *cause;
u_int i;
struct cmd_parse_result *pr;
for (i = 0; i < nitems(defaults); i++) {
cmdlist = cmd_string_parse(defaults[i], "<default>", i, &cause);
if (cmdlist == NULL)
pr = cmd_parse_from_string(defaults[i], NULL);
if (pr->status != CMD_PARSE_SUCCESS)
fatalx("bad default key: %s", defaults[i]);
cmdq_append(NULL, cmdq_get_command(cmdlist, NULL, NULL, 0));
cmd_list_free(cmdlist);
cmdq_append(NULL, cmdq_get_command(pr->cmdlist, NULL, NULL, 0));
cmd_list_free(pr->cmdlist);
}
}
@@ -445,7 +501,7 @@ key_bindings_read_only(struct cmdq_item *item, __unused void *data)
return (CMD_RETURN_ERROR);
}
void
struct cmdq_item *
key_bindings_dispatch(struct key_binding *bd, struct cmdq_item *item,
struct client *c, struct mouse_event *m, struct cmd_find_state *fs)
{
@@ -469,4 +525,5 @@ key_bindings_dispatch(struct key_binding *bd, struct cmdq_item *item,
cmdq_insert_after(item, new_item);
else
cmdq_append(c, new_item);
return (new_item);
}

View File

@@ -185,7 +185,7 @@ layout_set_main_h(struct window *w)
{
struct window_pane *wp;
struct layout_cell *lc, *lcmain, *lcother, *lcchild;
u_int n, mainh, otherh, sx;
u_int n, mainh, otherh, sx, sy;
layout_print_cell(w->layout_root, __func__, 1);
@@ -195,22 +195,25 @@ layout_set_main_h(struct window *w)
return;
n--; /* take off main pane */
/* Find available height - take off one line for the border. */
sy = w->sy - 1;
/* Get the main pane height and work out the other pane height. */
mainh = options_get_number(w->options, "main-pane-height");
if (mainh + PANE_MINIMUM + 1 >= w->sy) {
if (w->sy <= PANE_MINIMUM + 1 + PANE_MINIMUM)
if (mainh + PANE_MINIMUM >= sy) {
if (sy <= PANE_MINIMUM + PANE_MINIMUM)
mainh = PANE_MINIMUM;
else
mainh = w->sy - (PANE_MINIMUM + 1);
mainh = sy - PANE_MINIMUM;
otherh = PANE_MINIMUM;
} else {
otherh = options_get_number(w->options, "other-pane-height");
if (otherh == 0)
otherh = w->sy - mainh;
else if (otherh > w->sy || w->sy - otherh < mainh)
otherh = w->sy - mainh;
otherh = sy - mainh;
else if (otherh > sy || sy - otherh < mainh)
otherh = sy - mainh;
else
mainh = w->sy - otherh;
mainh = sy - otherh;
}
/* Work out what width is needed. */
@@ -221,7 +224,7 @@ layout_set_main_h(struct window *w)
/* Free old tree and create a new root. */
layout_free(w);
lc = w->layout_root = layout_create_cell(NULL);
layout_set_size(lc, sx, mainh + otherh, 0, 0);
layout_set_size(lc, sx, mainh + otherh + 1, 0, 0);
layout_make_node(lc, LAYOUT_TOPBOTTOM);
/* Create the main pane. */
@@ -269,7 +272,7 @@ layout_set_main_v(struct window *w)
{
struct window_pane *wp;
struct layout_cell *lc, *lcmain, *lcother, *lcchild;
u_int n, mainw, otherw, sy;
u_int n, mainw, otherw, sx, sy;
layout_print_cell(w->layout_root, __func__, 1);
@@ -279,22 +282,25 @@ layout_set_main_v(struct window *w)
return;
n--; /* take off main pane */
/* Find available width - take off one line for the border. */
sx = w->sx - 1;
/* Get the main pane width and work out the other pane width. */
mainw = options_get_number(w->options, "main-pane-width");
if (mainw + PANE_MINIMUM + 1 >= w->sx) {
if (w->sx <= PANE_MINIMUM + 1 + PANE_MINIMUM)
if (mainw + PANE_MINIMUM >= sx) {
if (sx <= PANE_MINIMUM + PANE_MINIMUM)
mainw = PANE_MINIMUM;
else
mainw = w->sx - (PANE_MINIMUM + 1);
mainw = sx - PANE_MINIMUM;
otherw = PANE_MINIMUM;
} else {
otherw = options_get_number(w->options, "other-pane-width");
if (otherw == 0)
otherw = w->sx - mainw;
else if (otherw > w->sx || w->sx - otherw < mainw)
otherw = w->sx - mainw;
otherw = sx - mainw;
else if (otherw > sx || sx - otherw < mainw)
otherw = sx - mainw;
else
mainw = w->sx - otherw;
mainw = sx - otherw;
}
/* Work out what height is needed. */
@@ -305,7 +311,7 @@ layout_set_main_v(struct window *w)
/* Free old tree and create a new root. */
layout_free(w);
lc = w->layout_root = layout_create_cell(NULL);
layout_set_size(lc, mainw + otherw, sy, 0, 0);
layout_set_size(lc, mainw + otherw + 1, sy, 0, 0);
layout_make_node(lc, LAYOUT_LEFTRIGHT);
/* Create the main pane. */

View File

@@ -414,9 +414,9 @@ layout_destroy_cell(struct window *w, struct layout_cell *lc,
lcother = TAILQ_NEXT(lc, entry);
else
lcother = TAILQ_PREV(lc, layout_cells, entry);
if (lcparent->type == LAYOUT_LEFTRIGHT)
if (lcother != NULL && lcparent->type == LAYOUT_LEFTRIGHT)
layout_resize_adjust(w, lcother, lcparent->type, lc->sx + 1);
else
else if (lcother != NULL)
layout_resize_adjust(w, lcother, lcparent->type, lc->sy + 1);
/* Remove this from the parent's list. */
@@ -831,12 +831,12 @@ layout_resize_child_cells(struct window *w, struct layout_cell *lc)
*/
struct layout_cell *
layout_split_pane(struct window_pane *wp, enum layout_type type, int size,
int insert_before, int full_size)
int flags)
{
struct layout_cell *lc, *lcparent, *lcnew, *lc1, *lc2;
u_int sx, sy, xoff, yoff, size1, size2, minimum;
u_int new_size, saved_size, resize_first = 0;
int status;
int full_size = (flags & SPAWN_FULLSIZE), status;
/*
* If full_size is specified, add a new cell at the top of the window
@@ -881,7 +881,7 @@ layout_split_pane(struct window_pane *wp, enum layout_type type, int size,
saved_size = sy;
if (size < 0)
size2 = ((saved_size + 1) / 2) - 1;
else if (insert_before)
else if (flags & SPAWN_BEFORE)
size2 = saved_size - size - 1;
else
size2 = size;
@@ -892,7 +892,7 @@ layout_split_pane(struct window_pane *wp, enum layout_type type, int size,
size1 = saved_size - 1 - size2;
/* Which size are we using? */
if (insert_before)
if (flags & SPAWN_BEFORE)
new_size = size2;
else
new_size = size1;
@@ -908,7 +908,7 @@ layout_split_pane(struct window_pane *wp, enum layout_type type, int size,
*/
lcparent = lc->parent;
lcnew = layout_create_cell(lcparent);
if (insert_before)
if (flags & SPAWN_BEFORE)
TAILQ_INSERT_BEFORE(lc, lcnew, entry);
else
TAILQ_INSERT_AFTER(&lcparent->cells, lc, lcnew, entry);
@@ -937,7 +937,7 @@ layout_split_pane(struct window_pane *wp, enum layout_type type, int size,
layout_set_size(lcnew, size, sy, 0, 0);
else if (lc->type == LAYOUT_TOPBOTTOM)
layout_set_size(lcnew, sx, size, 0, 0);
if (insert_before)
if (flags & SPAWN_BEFORE)
TAILQ_INSERT_HEAD(&lc->cells, lcnew, entry);
else
TAILQ_INSERT_TAIL(&lc->cells, lcnew, entry);
@@ -961,12 +961,12 @@ layout_split_pane(struct window_pane *wp, enum layout_type type, int size,
/* Create the new child cell. */
lcnew = layout_create_cell(lcparent);
if (insert_before)
if (flags & SPAWN_BEFORE)
TAILQ_INSERT_HEAD(&lcparent->cells, lcnew, entry);
else
TAILQ_INSERT_TAIL(&lcparent->cells, lcnew, entry);
}
if (insert_before) {
if (flags & SPAWN_BEFORE) {
lc1 = lcnew;
lc2 = lc;
} else {

318
menu.c Normal file
View File

@@ -0,0 +1,318 @@
/* $OpenBSD$ */
/*
* Copyright (c) 2019 Nicholas Marriott <nicholas.marriott@gmail.com>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER
* IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
* OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include <sys/types.h>
#include <stdlib.h>
#include <string.h>
#include "tmux.h"
struct menu_data {
struct cmdq_item *item;
int flags;
struct cmd_find_state fs;
struct screen s;
u_int px;
u_int py;
struct menu *menu;
int choice;
menu_choice_cb cb;
void *data;
};
void
menu_add_items(struct menu *menu, const struct menu_item *items,
struct cmdq_item *qitem, struct client *c, struct cmd_find_state *fs)
{
const struct menu_item *loop;
for (loop = items; loop->name != NULL; loop++)
menu_add_item(menu, loop, qitem, c, fs);
}
void
menu_add_item(struct menu *menu, const struct menu_item *item,
struct cmdq_item *qitem, struct client *c, struct cmd_find_state *fs)
{
struct menu_item *new_item;
const char *key, *cmd;
char *s, *name;
u_int width;
int line;
line = (item == NULL || item->name == NULL || *item->name == '\0');
if (line && menu->count == 0)
return;
menu->items = xreallocarray(menu->items, menu->count + 1,
sizeof *menu->items);
new_item = &menu->items[menu->count++];
memset(new_item, 0, sizeof *new_item);
if (line)
return;
if (fs != NULL)
s = format_single(qitem, item->name, c, fs->s, fs->wl, fs->wp);
else
s = format_single(qitem, item->name, c, NULL, NULL, NULL);
if (*s == '\0') { /* no item if empty after format expanded */
menu->count--;
return;
}
if (*s != '-' && item->key != KEYC_UNKNOWN && item->key != KEYC_NONE) {
key = key_string_lookup_key(item->key);
xasprintf(&name, "%s#[default] #[align=right](%s)", s, key);
} else
xasprintf(&name, "%s", s);
new_item->name = name;
free(s);
cmd = item->command;
if (cmd != NULL) {
if (fs != NULL)
s = format_single(qitem, cmd, c, fs->s, fs->wl, fs->wp);
else
s = format_single(qitem, cmd, c, NULL, NULL, NULL);
} else
s = NULL;
new_item->command = s;
new_item->key = item->key;
width = format_width(new_item->name);
if (width > menu->width)
menu->width = width;
}
struct menu *
menu_create(const char *title)
{
struct menu *menu;
menu = xcalloc(1, sizeof *menu);
menu->title = xstrdup(title);
return (menu);
}
void
menu_free(struct menu *menu)
{
u_int i;
for (i = 0; i < menu->count; i++) {
free((void *)menu->items[i].name);
free((void *)menu->items[i].command);
}
free(menu->items);
free((void *)menu->title);
free(menu);
}
static void
menu_draw_cb(struct client *c, __unused struct screen_redraw_ctx *ctx0)
{
struct menu_data *md = c->overlay_data;
struct tty *tty = &c->tty;
struct screen *s = &md->s;
struct menu *menu = md->menu;
struct screen_write_ctx ctx;
u_int i, px, py;
screen_write_start(&ctx, NULL, s);
screen_write_clearscreen(&ctx, 8);
screen_write_menu(&ctx, menu, md->choice);
screen_write_stop(&ctx);
px = md->px;
py = md->py;
for (i = 0; i < screen_size_y(&md->s); i++)
tty_draw_line(tty, NULL, s, 0, i, menu->width + 4, px, py + i);
if (~md->flags & MENU_NOMOUSE)
tty_update_mode(tty, MODE_MOUSE_ALL, NULL);
}
static void
menu_free_cb(struct client *c)
{
struct menu_data *md = c->overlay_data;
if (md->item != NULL)
md->item->flags &= ~CMDQ_WAITING;
if (md->cb != NULL)
md->cb(md->menu, UINT_MAX, KEYC_NONE, md->data);
screen_free(&md->s);
menu_free(md->menu);
free(md);
}
static int
menu_key_cb(struct client *c, struct key_event *event)
{
struct menu_data *md = c->overlay_data;
struct menu *menu = md->menu;
struct mouse_event *m = &event->m;
u_int i;
int count = menu->count, old = md->choice;
const struct menu_item *item;
struct cmdq_item *new_item;
struct cmd_parse_result *pr;
const char *name;
if (KEYC_IS_MOUSE(event->key)) {
if (md->flags & MENU_NOMOUSE)
return (0);
if (m->x < md->px ||
m->x > md->px + 4 + menu->width ||
m->y < md->py + 1 ||
m->y > md->py + 1 + count - 1) {
if (MOUSE_RELEASE(m->b))
return (1);
if (md->choice != -1) {
md->choice = -1;
c->flags |= CLIENT_REDRAWOVERLAY;
}
return (0);
}
if (MOUSE_RELEASE(m->b))
goto chosen;
md->choice = m->y - (md->py + 1);
if (md->choice != old)
c->flags |= CLIENT_REDRAWOVERLAY;
return (0);
}
switch (event->key) {
case KEYC_UP:
if (old == -1)
old = 0;
do {
if (md->choice == -1 || md->choice == 0)
md->choice = count - 1;
else
md->choice--;
name = menu->items[md->choice].name;
} while ((name == NULL || *name == '-') && md->choice != old);
c->flags |= CLIENT_REDRAWOVERLAY;
return (0);
case KEYC_DOWN:
if (old == -1)
old = 0;
do {
if (md->choice == -1 || md->choice == count - 1)
md->choice = 0;
else
md->choice++;
name = menu->items[md->choice].name;
} while ((name == NULL || *name == '-') && md->choice != old);
c->flags |= CLIENT_REDRAWOVERLAY;
return (0);
case '\r':
goto chosen;
case '\033': /* Escape */
case '\003': /* C-c */
case '\007': /* C-g */
case 'q':
return (1);
}
for (i = 0; i < (u_int)count; i++) {
name = menu->items[i].name;
if (name == NULL || *name == '-')
continue;
if (event->key == menu->items[i].key) {
md->choice = i;
goto chosen;
}
}
return (0);
chosen:
if (md->choice == -1)
return (1);
item = &menu->items[md->choice];
if (item->name == NULL || *item->name == '-')
return (1);
if (md->cb != NULL) {
md->cb(md->menu, md->choice, item->key, md->data);
md->cb = NULL;
return (1);
}
pr = cmd_parse_from_string(item->command, NULL);
switch (pr->status) {
case CMD_PARSE_EMPTY:
new_item = NULL;
break;
case CMD_PARSE_ERROR:
new_item = cmdq_get_error(pr->error);
free(pr->error);
cmdq_append(c, new_item);
break;
case CMD_PARSE_SUCCESS:
if (md->item != NULL)
m = &md->item->shared->mouse;
else
m = NULL;
new_item = cmdq_get_command(pr->cmdlist, &md->fs, m, 0);
cmd_list_free(pr->cmdlist);
cmdq_append(c, new_item);
break;
}
return (1);
}
int
menu_display(struct menu *menu, int flags, struct cmdq_item *item, u_int px,
u_int py, struct client *c, struct cmd_find_state *fs, menu_choice_cb cb,
void *data)
{
struct menu_data *md;
if (c->tty.sx < menu->width + 4 || c->tty.sy < menu->count + 2)
return (-1);
md = xcalloc(1, sizeof *md);
md->item = item;
md->flags = flags;
if (fs != NULL)
cmd_find_copy_state(&md->fs, fs);
screen_init(&md->s, menu->width + 4, menu->count + 2, 0);
md->px = px;
md->py = py;
md->menu = menu;
md->choice = -1;
md->cb = cb;
md->data = data;
server_client_set_overlay(c, 0, menu_draw_cb, menu_key_cb, menu_free_cb,
md);
return (0);
}

View File

@@ -35,6 +35,7 @@ struct mode_tree_data {
struct window_pane *wp;
void *modedata;
const struct menu_item *menu;
const char **sort_list;
u_int sort_size;
@@ -43,6 +44,7 @@ struct mode_tree_data {
mode_tree_build_cb buildcb;
mode_tree_draw_cb drawcb;
mode_tree_search_cb searchcb;
mode_tree_menu_cb menucb;
struct mode_tree_list children;
struct mode_tree_list saved;
@@ -89,8 +91,24 @@ struct mode_tree_line {
int flat;
};
struct mode_tree_menu {
struct mode_tree_data *data;
struct client *c;
u_int line;
void *itemdata;
};
static void mode_tree_free_items(struct mode_tree_list *);
static const struct menu_item mode_tree_menu_items[] = {
{ "Scroll Left", '<', NULL },
{ "Scroll Right", '>', NULL },
{ "", KEYC_NONE, NULL },
{ "Cancel", 'q', NULL },
{ NULL, KEYC_NONE, NULL }
};
static struct mode_tree_item *
mode_tree_find_item(struct mode_tree_list *mtl, uint64_t tag)
{
@@ -299,8 +317,9 @@ mode_tree_each_tagged(struct mode_tree_data *mtd, mode_tree_each_cb cb,
struct mode_tree_data *
mode_tree_start(struct window_pane *wp, struct args *args,
mode_tree_build_cb buildcb, mode_tree_draw_cb drawcb,
mode_tree_search_cb searchcb, void *modedata, const char **sort_list,
u_int sort_size, struct screen **s)
mode_tree_search_cb searchcb, mode_tree_menu_cb menucb, void *modedata,
const struct menu_item *menu, const char **sort_list, u_int sort_size,
struct screen **s)
{
struct mode_tree_data *mtd;
const char *sort;
@@ -311,6 +330,7 @@ mode_tree_start(struct window_pane *wp, struct args *args,
mtd->wp = wp;
mtd->modedata = modedata;
mtd->menu = menu;
mtd->sort_list = sort_list;
mtd->sort_size = sort_size;
@@ -334,6 +354,7 @@ mode_tree_start(struct window_pane *wp, struct args *args,
mtd->buildcb = buildcb;
mtd->drawcb = drawcb;
mtd->searchcb = searchcb;
mtd->menucb = menucb;
TAILQ_INIT(&mtd->children);
@@ -366,7 +387,7 @@ mode_tree_build(struct mode_tree_data *mtd)
if (mtd->line_list != NULL)
tag = mtd->line_list[mtd->current].item->tag;
else
tag = 0;
tag = UINT64_MAX;
TAILQ_CONCAT(&mtd->saved, &mtd->children, entry);
TAILQ_INIT(&mtd->children);
@@ -382,6 +403,8 @@ mode_tree_build(struct mode_tree_data *mtd)
mode_tree_clear_lines(mtd);
mode_tree_build_lines(mtd, &mtd->children, 0);
if (tag == UINT64_MAX)
tag = mtd->line_list[mtd->current].item->tag;
mode_tree_set_current(mtd, tag);
mtd->width = screen_size_x(s);
@@ -762,6 +785,70 @@ mode_tree_filter_free(void *data)
mode_tree_remove_ref(data);
}
static void
mode_tree_menu_callback(__unused struct menu *menu, __unused u_int idx,
key_code key, void *data)
{
struct mode_tree_menu *mtm = data;
struct mode_tree_data *mtd = mtm->data;
struct mode_tree_item *mti;
if (mtd->dead || key == KEYC_NONE)
goto out;
if (mtm->line >= mtd->line_size)
goto out;
mti = mtd->line_list[mtm->line].item;
if (mti->itemdata != mtm->itemdata)
goto out;
mtd->current = mtm->line;
mtd->menucb (mtd->modedata, mtm->c, key);
out:
mode_tree_remove_ref(mtd);
free(mtm);
}
static void
mode_tree_display_menu(struct mode_tree_data *mtd, struct client *c, u_int x,
u_int y, int outside)
{
struct mode_tree_item *mti;
struct menu *menu;
const struct menu_item *items;
struct mode_tree_menu *mtm;
char *title;
u_int line;
if (mtd->offset + y > mtd->line_size - 1)
line = mtd->current;
else
line = mtd->offset + y;
mti = mtd->line_list[line].item;
if (!outside) {
items = mtd->menu;
xasprintf(&title, "#[align=centre]%s", mti->name);
} else {
items = mode_tree_menu_items;
title = xstrdup("");
}
menu = menu_create(title);
menu_add_items(menu, items, NULL, NULL, NULL);
free(title);
mtm = xmalloc(sizeof *mtm);
mtm->data = mtd;
mtm->c = c;
mtm->line = line;
mtm->itemdata = mti->itemdata;
mtd->references++;
if (menu_display(menu, 0, NULL, x, y, c, NULL, mode_tree_menu_callback,
mtm) != 0)
menu_free(menu);
}
int
mode_tree_key(struct mode_tree_data *mtd, struct client *c, key_code *key,
struct mouse_event *m, u_int *xp, u_int *yp)
@@ -772,7 +859,7 @@ mode_tree_key(struct mode_tree_data *mtd, struct client *c, key_code *key,
int choice;
key_code tmp;
if (KEYC_IS_MOUSE(*key)) {
if (KEYC_IS_MOUSE(*key) && m != NULL) {
if (cmd_mouse_at(mtd->wp, m, &x, &y, 0) != 0) {
*key = KEYC_NONE;
return (0);
@@ -782,20 +869,29 @@ mode_tree_key(struct mode_tree_data *mtd, struct client *c, key_code *key,
if (yp != NULL)
*yp = y;
if (x > mtd->width || y > mtd->height) {
if (*key == KEYC_MOUSEDOWN3_PANE)
mode_tree_display_menu(mtd, c, x, y, 1);
if (!mtd->preview)
*key = KEYC_NONE;
return (0);
}
if (mtd->offset + y < mtd->line_size) {
if (*key == KEYC_MOUSEDOWN1_PANE ||
*key == KEYC_MOUSEDOWN3_PANE ||
*key == KEYC_DOUBLECLICK1_PANE)
mtd->current = mtd->offset + y;
if (*key == KEYC_DOUBLECLICK1_PANE)
*key = '\r';
else
else {
if (*key == KEYC_MOUSEDOWN3_PANE)
mode_tree_display_menu(mtd, c, x, y, 0);
*key = KEYC_NONE;
} else
}
} else {
if (*key == KEYC_MOUSEDOWN3_PANE)
mode_tree_display_menu(mtd, c, x, y, 0);
*key = KEYC_NONE;
}
return (0);
}
@@ -879,7 +975,8 @@ mode_tree_key(struct mode_tree_data *mtd, struct client *c, key_code *key,
current->tagged = 1;
} else
current->tagged = 0;
mode_tree_down(mtd, 0);
if (m != NULL)
mode_tree_down(mtd, 0);
break;
case 'T':
for (i = 0; i < mtd->line_size; i++)
@@ -952,8 +1049,8 @@ mode_tree_run_command(struct client *c, struct cmd_find_state *fs,
const char *template, const char *name)
{
struct cmdq_item *new_item;
struct cmd_list *cmdlist;
char *command, *cause;
char *command;
struct cmd_parse_result *pr;
command = cmd_template_replace(template, name, 1);
if (command == NULL || *command == '\0') {
@@ -961,17 +1058,22 @@ mode_tree_run_command(struct client *c, struct cmd_find_state *fs,
return;
}
cmdlist = cmd_string_parse(command, NULL, 0, &cause);
if (cmdlist == NULL) {
if (cause != NULL && c != NULL) {
*cause = toupper((u_char)*cause);
status_message_set(c, "%s", cause);
pr = cmd_parse_from_string(command, NULL);
switch (pr->status) {
case CMD_PARSE_EMPTY:
break;
case CMD_PARSE_ERROR:
if (c != NULL) {
*pr->error = toupper((u_char)*pr->error);
status_message_set(c, "%s", pr->error);
}
free(cause);
} else {
new_item = cmdq_get_command(cmdlist, fs, NULL, 0);
free(pr->error);
break;
case CMD_PARSE_SUCCESS:
new_item = cmdq_get_command(pr->cmdlist, fs, NULL, 0);
cmdq_append(c, new_item);
cmd_list_free(cmdlist);
cmd_list_free(pr->cmdlist);
break;
}
free(command);

View File

@@ -35,13 +35,34 @@ struct notify_entry {
};
static void
notify_hook1(struct cmdq_item *item, struct notify_entry *ne)
notify_hook_formats(struct cmdq_item *item, struct session *s, struct window *w,
int pane)
{
struct cmd_find_state fs;
struct hook *hook;
struct cmdq_item *new_item;
struct session *s = ne->session;
struct window *w = ne->window;
if (s != NULL) {
cmdq_format(item, "hook_session", "$%u", s->id);
cmdq_format(item, "hook_session_name", "%s", s->name);
}
if (w != NULL) {
cmdq_format(item, "hook_window", "@%u", w->id);
cmdq_format(item, "hook_window_name", "%s", w->name);
}
if (pane != -1)
cmdq_format(item, "hook_pane", "%%%d", pane);
}
static void
notify_insert_hook(struct cmdq_item *item, struct notify_entry *ne)
{
struct cmd_find_state fs;
struct options *oo;
struct cmdq_item *new_item;
struct session *s = ne->session;
struct window *w = ne->window;
struct options_entry *o;
struct options_array_item *a;
struct cmd_list *cmdlist;
log_debug("%s: %s", __func__, ne->name);
cmd_find_clear_state(&fs, 0);
if (cmd_find_empty_state(&ne->fs) || !cmd_find_valid_state(&ne->fs))
@@ -49,26 +70,31 @@ notify_hook1(struct cmdq_item *item, struct notify_entry *ne)
else
cmd_find_copy_state(&fs, &ne->fs);
hook = hooks_find(hooks_get(fs.s), ne->name);
if (hook == NULL)
if (fs.s == NULL)
oo = global_s_options;
else
oo = fs.s->options;
o = options_get(oo, ne->name);
if (o == NULL)
return;
log_debug("notify hook %s", ne->name);
new_item = cmdq_get_command(hook->cmdlist, &fs, NULL, CMDQ_NOHOOKS);
cmdq_format(new_item, "hook", "%s", ne->name);
a = options_array_first(o);
while (a != NULL) {
cmdlist = options_array_item_value(a)->cmdlist;
if (cmdlist == NULL) {
a = options_array_next(a);
continue;
}
if (s != NULL) {
cmdq_format(new_item, "hook_session", "$%u", s->id);
cmdq_format(new_item, "hook_session_name", "%s", s->name);
new_item = cmdq_get_command(cmdlist, &fs, NULL, CMDQ_NOHOOKS);
cmdq_format(new_item, "hook", "%s", ne->name);
notify_hook_formats(new_item, s, w, ne->pane);
cmdq_insert_after(item, new_item);
item = new_item;
a = options_array_next(a);
}
if (w != NULL) {
cmdq_format(new_item, "hook_window", "@%u", w->id);
cmdq_format(new_item, "hook_window_name", "%s", w->name);
}
if (ne->pane != -1)
cmdq_format(new_item, "hook_pane", "%%%d", ne->pane);
cmdq_insert_after(item, new_item);
}
static enum cmd_retval
@@ -101,7 +127,7 @@ notify_callback(struct cmdq_item *item, void *data)
if (strcmp(ne->name, "session-window-changed") == 0)
control_notify_session_window_changed(ne->session);
notify_hook1(item, ne);
notify_insert_hook(item, ne);
if (ne->client != NULL)
server_client_unref(ne->client);
@@ -168,17 +194,17 @@ notify_hook(struct cmdq_item *item, const char *name)
ne.window = item->target.w;
ne.pane = item->target.wp->id;
notify_hook1(item, &ne);
notify_insert_hook(item, &ne);
}
void
notify_input(struct window_pane *wp, struct evbuffer *input)
notify_input(struct window_pane *wp, const u_char *buf, size_t len)
{
struct client *c;
TAILQ_FOREACH(c, &clients, entry) {
if (c->flags & CLIENT_CONTROL)
control_notify_input(c, wp, input);
control_notify_input(c, wp, buf, len);
}
}

View File

@@ -129,8 +129,19 @@ static const char *options_table_status_format_default[] = {
OPTIONS_TABLE_STATUS_FORMAT1, OPTIONS_TABLE_STATUS_FORMAT2, NULL
};
/* Helper for hook options. */
#define OPTIONS_TABLE_HOOK(hook_name, default_value) \
{ .name = hook_name, \
.type = OPTIONS_TABLE_COMMAND, \
.scope = OPTIONS_TABLE_SESSION, \
.flags = OPTIONS_TABLE_IS_ARRAY|OPTIONS_TABLE_IS_HOOK, \
.default_str = default_value, \
.separator = "" \
}
/* Top-level options. */
const struct options_table_entry options_table[] = {
/* Server options. */
{ .name = "buffer-limit",
.type = OPTIONS_TABLE_NUMBER,
.scope = OPTIONS_TABLE_SERVER,
@@ -140,8 +151,9 @@ const struct options_table_entry options_table[] = {
},
{ .name = "command-alias",
.type = OPTIONS_TABLE_ARRAY,
.type = OPTIONS_TABLE_STRING,
.scope = OPTIONS_TABLE_SERVER,
.flags = OPTIONS_TABLE_IS_ARRAY,
.default_str = "split-pane=split-window,"
"splitp=split-window,"
"server-info=show-messages -JT,"
@@ -205,8 +217,9 @@ const struct options_table_entry options_table[] = {
},
{ .name = "terminal-overrides",
.type = OPTIONS_TABLE_ARRAY,
.type = OPTIONS_TABLE_STRING,
.scope = OPTIONS_TABLE_SERVER,
.flags = OPTIONS_TABLE_IS_ARRAY,
.default_str = "xterm*:XT:Ms=\\E]52;%p1%s;%p2%s\\007"
":Cs=\\E]12;%p1%s\\007:Cr=\\E]112\\007"
":Ss=\\E[%p1%d q:Se=\\E[2 q,screen*:XT",
@@ -214,12 +227,14 @@ const struct options_table_entry options_table[] = {
},
{ .name = "user-keys",
.type = OPTIONS_TABLE_ARRAY,
.type = OPTIONS_TABLE_STRING,
.scope = OPTIONS_TABLE_SERVER,
.flags = OPTIONS_TABLE_IS_ARRAY,
.default_str = "",
.separator = ","
},
/* Session options. */
{ .name = "activity-action",
.type = OPTIONS_TABLE_CHOICE,
.scope = OPTIONS_TABLE_SESSION,
@@ -420,8 +435,9 @@ const struct options_table_entry options_table[] = {
},
{ .name = "status-format",
.type = OPTIONS_TABLE_ARRAY,
.type = OPTIONS_TABLE_STRING,
.scope = OPTIONS_TABLE_SESSION,
.flags = OPTIONS_TABLE_IS_ARRAY,
.default_arr = options_table_status_format_default,
},
@@ -503,8 +519,9 @@ const struct options_table_entry options_table[] = {
},
{ .name = "update-environment",
.type = OPTIONS_TABLE_ARRAY,
.type = OPTIONS_TABLE_STRING,
.scope = OPTIONS_TABLE_SESSION,
.flags = OPTIONS_TABLE_IS_ARRAY,
.default_str = "DISPLAY KRB5CCNAME SSH_ASKPASS SSH_AUTH_SOCK "
"SSH_AGENT_PID SSH_CONNECTION WINDOWID XAUTHORITY"
},
@@ -533,9 +550,10 @@ const struct options_table_entry options_table[] = {
{ .name = "word-separators",
.type = OPTIONS_TABLE_STRING,
.scope = OPTIONS_TABLE_SESSION,
.default_str = " -_@"
.default_str = " "
},
/* Window options. */
{ .name = "aggressive-resize",
.type = OPTIONS_TABLE_FLAG,
.scope = OPTIONS_TABLE_WINDOW,
@@ -770,5 +788,66 @@ const struct options_table_entry options_table[] = {
.default_num = 1
},
/* Hook options. */
OPTIONS_TABLE_HOOK("after-bind-key", ""),
OPTIONS_TABLE_HOOK("after-capture-pane", ""),
OPTIONS_TABLE_HOOK("after-copy-mode", ""),
OPTIONS_TABLE_HOOK("after-display-message", ""),
OPTIONS_TABLE_HOOK("after-display-panes", ""),
OPTIONS_TABLE_HOOK("after-list-buffers", ""),
OPTIONS_TABLE_HOOK("after-list-clients", ""),
OPTIONS_TABLE_HOOK("after-list-keys", ""),
OPTIONS_TABLE_HOOK("after-list-panes", ""),
OPTIONS_TABLE_HOOK("after-list-sessions", ""),
OPTIONS_TABLE_HOOK("after-list-windows", ""),
OPTIONS_TABLE_HOOK("after-load-buffer", ""),
OPTIONS_TABLE_HOOK("after-lock-server", ""),
OPTIONS_TABLE_HOOK("after-new-session", ""),
OPTIONS_TABLE_HOOK("after-new-window", ""),
OPTIONS_TABLE_HOOK("after-paste-buffer", ""),
OPTIONS_TABLE_HOOK("after-pipe-pane", ""),
OPTIONS_TABLE_HOOK("after-queue", ""),
OPTIONS_TABLE_HOOK("after-refresh-client", ""),
OPTIONS_TABLE_HOOK("after-rename-session", ""),
OPTIONS_TABLE_HOOK("after-rename-window", ""),
OPTIONS_TABLE_HOOK("after-resize-pane", ""),
OPTIONS_TABLE_HOOK("after-resize-window", ""),
OPTIONS_TABLE_HOOK("after-save-buffer", ""),
OPTIONS_TABLE_HOOK("after-select-layout", ""),
OPTIONS_TABLE_HOOK("after-select-pane", ""),
OPTIONS_TABLE_HOOK("after-select-window", ""),
OPTIONS_TABLE_HOOK("after-send-keys", ""),
OPTIONS_TABLE_HOOK("after-set-buffer", ""),
OPTIONS_TABLE_HOOK("after-set-environment", ""),
OPTIONS_TABLE_HOOK("after-set-hook", ""),
OPTIONS_TABLE_HOOK("after-set-option", ""),
OPTIONS_TABLE_HOOK("after-show-environment", ""),
OPTIONS_TABLE_HOOK("after-show-messages", ""),
OPTIONS_TABLE_HOOK("after-show-options", ""),
OPTIONS_TABLE_HOOK("after-split-window", ""),
OPTIONS_TABLE_HOOK("after-unbind-key", ""),
OPTIONS_TABLE_HOOK("alert-activity", ""),
OPTIONS_TABLE_HOOK("alert-bell", ""),
OPTIONS_TABLE_HOOK("alert-silence", ""),
OPTIONS_TABLE_HOOK("client-attached", ""),
OPTIONS_TABLE_HOOK("client-detached", ""),
OPTIONS_TABLE_HOOK("client-resized", ""),
OPTIONS_TABLE_HOOK("client-session-changed", ""),
OPTIONS_TABLE_HOOK("pane-died", ""),
OPTIONS_TABLE_HOOK("pane-exited", ""),
OPTIONS_TABLE_HOOK("pane-focus-in", ""),
OPTIONS_TABLE_HOOK("pane-focus-out", ""),
OPTIONS_TABLE_HOOK("pane-mode-changed", ""),
OPTIONS_TABLE_HOOK("pane-set-clipboard", ""),
OPTIONS_TABLE_HOOK("session-closed", ""),
OPTIONS_TABLE_HOOK("session-created", ""),
OPTIONS_TABLE_HOOK("session-renamed", ""),
OPTIONS_TABLE_HOOK("session-window-changed", ""),
OPTIONS_TABLE_HOOK("window-layout-changed", ""),
OPTIONS_TABLE_HOOK("window-linked", ""),
OPTIONS_TABLE_HOOK("window-pane-changed", ""),
OPTIONS_TABLE_HOOK("window-renamed", ""),
OPTIONS_TABLE_HOOK("window-unlinked", ""),
{ .name = NULL }
};

287
options.c
View File

@@ -32,10 +32,9 @@
struct options_array_item {
u_int index;
char *value;
union options_value value;
RB_ENTRY(options_array_item) entry;
};
RB_HEAD(options_array, options_array_item);
static int
options_array_cmp(struct options_array_item *a1, struct options_array_item *a2)
{
@@ -48,19 +47,13 @@ options_array_cmp(struct options_array_item *a1, struct options_array_item *a2)
RB_GENERATE_STATIC(options_array, options_array_item, entry, options_array_cmp);
struct options_entry {
struct options *owner;
struct options *owner;
const char *name;
const struct options_table_entry *tableentry;
const char *name;
const struct options_table_entry *tableentry;
union options_value value;
union {
char *string;
long long number;
struct style style;
struct options_array array;
};
RB_ENTRY(options_entry) entry;
RB_ENTRY(options_entry) entry;
};
struct options {
@@ -83,9 +76,13 @@ static struct options_entry *options_add(struct options *, const char *);
#define OPTIONS_IS_STYLE(o) \
((o)->tableentry != NULL && \
(o)->tableentry->type == OPTIONS_TABLE_STYLE)
#define OPTIONS_IS_ARRAY(o) \
#define OPTIONS_IS_COMMAND(o) \
((o)->tableentry != NULL && \
(o)->tableentry->type == OPTIONS_TABLE_ARRAY)
(o)->tableentry->type == OPTIONS_TABLE_COMMAND)
#define OPTIONS_IS_ARRAY(o) \
((o)->tableentry != NULL && \
((o)->tableentry->flags & OPTIONS_TABLE_IS_ARRAY))
static int options_cmp(struct options_entry *, struct options_entry *);
RB_GENERATE_STATIC(options_tree, options_entry, entry, options_cmp);
@@ -109,6 +106,57 @@ options_parent_table_entry(struct options *oo, const char *s)
return (o->tableentry);
}
static void
options_value_free(struct options_entry *o, union options_value *ov)
{
if (OPTIONS_IS_STRING(o))
free(ov->string);
if (OPTIONS_IS_COMMAND(o) && ov->cmdlist != NULL)
cmd_list_free(ov->cmdlist);
}
static char *
options_value_tostring(struct options_entry *o, union options_value *ov,
int numeric)
{
char *s;
if (OPTIONS_IS_COMMAND(o))
return (cmd_list_print(ov->cmdlist, 0));
if (OPTIONS_IS_STYLE(o))
return (xstrdup(style_tostring(&ov->style)));
if (OPTIONS_IS_NUMBER(o)) {
switch (o->tableentry->type) {
case OPTIONS_TABLE_NUMBER:
xasprintf(&s, "%lld", ov->number);
break;
case OPTIONS_TABLE_KEY:
s = xstrdup(key_string_lookup_key(ov->number));
break;
case OPTIONS_TABLE_COLOUR:
s = xstrdup(colour_tostring(ov->number));
break;
case OPTIONS_TABLE_FLAG:
if (numeric)
xasprintf(&s, "%lld", ov->number);
else
s = xstrdup(ov->number ? "on" : "off");
break;
case OPTIONS_TABLE_CHOICE:
s = xstrdup(o->tableentry->choices[ov->number]);
break;
case OPTIONS_TABLE_STRING:
case OPTIONS_TABLE_STYLE:
case OPTIONS_TABLE_COMMAND:
fatalx("not a number option type");
}
return (s);
}
if (OPTIONS_IS_STRING(o))
return (xstrdup(ov->string));
return (xstrdup(""));
}
struct options *
options_create(struct options *parent)
{
@@ -174,8 +222,8 @@ options_empty(struct options *oo, const struct options_table_entry *oe)
o = options_add(oo, oe->name);
o->tableentry = oe;
if (oe->type == OPTIONS_TABLE_ARRAY)
RB_INIT(&o->array);
if (oe->flags & OPTIONS_TABLE_IS_ARRAY)
RB_INIT(&o->value.array);
return (o);
}
@@ -183,23 +231,35 @@ options_empty(struct options *oo, const struct options_table_entry *oe)
struct options_entry *
options_default(struct options *oo, const struct options_table_entry *oe)
{
struct options_entry *o;
u_int i;
struct options_entry *o;
union options_value *ov;
u_int i;
o = options_empty(oo, oe);
if (oe->type == OPTIONS_TABLE_ARRAY) {
if (oe->default_arr != NULL) {
for (i = 0; oe->default_arr[i] != NULL; i++)
options_array_set(o, i, oe->default_arr[i], 0);
} else
options_array_assign(o, oe->default_str);
} else if (oe->type == OPTIONS_TABLE_STRING)
o->string = xstrdup(oe->default_str);
else if (oe->type == OPTIONS_TABLE_STYLE) {
style_set(&o->style, &grid_default_cell);
style_parse(&o->style, &grid_default_cell, oe->default_str);
} else
o->number = oe->default_num;
ov = &o->value;
if (oe->flags & OPTIONS_TABLE_IS_ARRAY) {
if (oe->default_arr == NULL) {
options_array_assign(o, oe->default_str, NULL);
return (o);
}
for (i = 0; oe->default_arr[i] != NULL; i++)
options_array_set(o, i, oe->default_arr[i], 0, NULL);
return (o);
}
switch (oe->type) {
case OPTIONS_TABLE_STRING:
ov->string = xstrdup(oe->default_str);
break;
case OPTIONS_TABLE_STYLE:
style_set(&ov->style, &grid_default_cell);
style_parse(&ov->style, &grid_default_cell, oe->default_str);
break;
default:
ov->number = oe->default_num;
break;
}
return (o);
}
@@ -225,11 +285,10 @@ options_remove(struct options_entry *o)
{
struct options *oo = o->owner;
if (OPTIONS_IS_STRING(o))
free(o->string);
else if (OPTIONS_IS_ARRAY(o))
if (OPTIONS_IS_ARRAY(o))
options_array_clear(o);
else
options_value_free(o, &o->value);
RB_REMOVE(options_tree, &oo->tree, o);
free(o);
}
@@ -252,14 +311,14 @@ options_array_item(struct options_entry *o, u_int idx)
struct options_array_item a;
a.index = idx;
return (RB_FIND(options_array, &o->array, &a));
return (RB_FIND(options_array, &o->value.array, &a));
}
static void
options_array_free(struct options_entry *o, struct options_array_item *a)
{
free(a->value);
RB_REMOVE(options_array, &o->array, a);
options_value_free(o, &a->value);
RB_REMOVE(options_array, &o->value.array, a);
free(a);
}
@@ -271,11 +330,11 @@ options_array_clear(struct options_entry *o)
if (!OPTIONS_IS_ARRAY(o))
return;
RB_FOREACH_SAFE(a, options_array, &o->array, a1)
RB_FOREACH_SAFE(a, options_array, &o->value.array, a1)
options_array_free(o, a);
}
const char *
union options_value *
options_array_get(struct options_entry *o, u_int idx)
{
struct options_array_item *a;
@@ -285,18 +344,39 @@ options_array_get(struct options_entry *o, u_int idx)
a = options_array_item(o, idx);
if (a == NULL)
return (NULL);
return (a->value);
return (&a->value);
}
int
options_array_set(struct options_entry *o, u_int idx, const char *value,
int append)
int append, char **cause)
{
struct options_array_item *a;
char *new;
struct cmd_parse_result *pr;
if (!OPTIONS_IS_ARRAY(o))
if (!OPTIONS_IS_ARRAY(o)) {
if (cause != NULL)
*cause = xstrdup("not an array");
return (-1);
}
if (OPTIONS_IS_COMMAND(o) && value != NULL) {
pr = cmd_parse_from_string(value, NULL);
switch (pr->status) {
case CMD_PARSE_EMPTY:
*cause = xstrdup("empty command");
return (-1);
case CMD_PARSE_ERROR:
if (cause != NULL)
*cause = pr->error;
else
free(pr->error);
return (-1);
case CMD_PARSE_SUCCESS:
break;
}
}
a = options_array_item(o, idx);
if (value == NULL) {
@@ -305,25 +385,29 @@ options_array_set(struct options_entry *o, u_int idx, const char *value,
return (0);
}
if (OPTIONS_IS_STRING(o)) {
if (a != NULL && append)
xasprintf(&new, "%s%s", a->value.string, value);
else
new = xstrdup(value);
}
if (a == NULL) {
a = xcalloc(1, sizeof *a);
a->index = idx;
a->value = xstrdup(value);
RB_INSERT(options_array, &o->array, a);
} else {
free(a->value);
if (a != NULL && append)
xasprintf(&new, "%s%s", a->value, value);
else
new = xstrdup(value);
a->value = new;
}
RB_INSERT(options_array, &o->value.array, a);
} else
options_value_free(o, &a->value);
if (OPTIONS_IS_STRING(o))
a->value.string = new;
else if (OPTIONS_IS_COMMAND(o))
a->value.cmdlist = pr->cmdlist;
return (0);
}
void
options_array_assign(struct options_entry *o, const char *s)
int
options_array_assign(struct options_entry *o, const char *s, char **cause)
{
const char *separator;
char *copy, *next, *string;
@@ -332,7 +416,18 @@ options_array_assign(struct options_entry *o, const char *s)
separator = o->tableentry->separator;
if (separator == NULL)
separator = " ,";
if (*separator == '\0') {
if (*s == '\0')
return (0);
for (i = 0; i < UINT_MAX; i++) {
if (options_array_item(o, i) == NULL)
break;
}
return (options_array_set(o, i, s, 0, cause));
}
if (*s == '\0')
return (0);
copy = string = xstrdup(s);
while ((next = strsep(&string, separator)) != NULL) {
if (*next == '\0')
@@ -343,9 +438,13 @@ options_array_assign(struct options_entry *o, const char *s)
}
if (i == UINT_MAX)
break;
options_array_set(o, i, next, 0);
if (options_array_set(o, i, next, 0, cause) != 0) {
free(copy);
return (-1);
}
}
free(copy);
return (0);
}
struct options_array_item *
@@ -353,13 +452,13 @@ options_array_first(struct options_entry *o)
{
if (!OPTIONS_IS_ARRAY(o))
return (NULL);
return (RB_MIN(options_array, &o->array));
return (RB_MIN(options_array, &o->value.array));
}
struct options_array_item *
options_array_next(struct options_array_item *a)
{
return (RB_NEXT(options_array, &o->array, a));
return (RB_NEXT(options_array, &o->value.array, a));
}
u_int
@@ -368,10 +467,10 @@ options_array_item_index(struct options_array_item *a)
return (a->index);
}
const char *
union options_value *
options_array_item_value(struct options_array_item *a)
{
return (a->value);
return (&a->value);
}
int
@@ -383,59 +482,23 @@ options_isarray(struct options_entry *o)
int
options_isstring(struct options_entry *o)
{
return (OPTIONS_IS_STRING(o) || OPTIONS_IS_ARRAY(o));
return (OPTIONS_IS_STRING(o));
}
const char *
char *
options_tostring(struct options_entry *o, int idx, int numeric)
{
static char s[1024];
const char *tmp;
struct options_array_item *a;
if (OPTIONS_IS_ARRAY(o)) {
if (idx == -1)
return (NULL);
return (xstrdup(""));
a = options_array_item(o, idx);
if (a == NULL)
return ("");
return (a->value);
return (xstrdup(""));
return (options_value_tostring(o, &a->value, numeric));
}
if (OPTIONS_IS_STYLE(o))
return (style_tostring(&o->style));
if (OPTIONS_IS_NUMBER(o)) {
tmp = NULL;
switch (o->tableentry->type) {
case OPTIONS_TABLE_NUMBER:
xsnprintf(s, sizeof s, "%lld", o->number);
break;
case OPTIONS_TABLE_KEY:
tmp = key_string_lookup_key(o->number);
break;
case OPTIONS_TABLE_COLOUR:
tmp = colour_tostring(o->number);
break;
case OPTIONS_TABLE_FLAG:
if (numeric)
xsnprintf(s, sizeof s, "%lld", o->number);
else
tmp = (o->number ? "on" : "off");
break;
case OPTIONS_TABLE_CHOICE:
tmp = o->tableentry->choices[o->number];
break;
case OPTIONS_TABLE_STRING:
case OPTIONS_TABLE_STYLE:
case OPTIONS_TABLE_ARRAY:
break;
}
if (tmp != NULL)
xsnprintf(s, sizeof s, "%s", tmp);
return (s);
}
if (OPTIONS_IS_STRING(o))
return (o->string);
return (NULL);
return (options_value_tostring(o, &o->value, numeric));
}
char *
@@ -549,7 +612,7 @@ options_get_string(struct options *oo, const char *name)
fatalx("missing option %s", name);
if (!OPTIONS_IS_STRING(o))
fatalx("option %s is not a string", name);
return (o->string);
return (o->value.string);
}
long long
@@ -562,7 +625,7 @@ options_get_number(struct options *oo, const char *name)
fatalx("missing option %s", name);
if (!OPTIONS_IS_NUMBER(o))
fatalx("option %s is not a number", name);
return (o->number);
return (o->value.number);
}
struct style *
@@ -575,7 +638,7 @@ options_get_style(struct options *oo, const char *name)
fatalx("missing option %s", name);
if (!OPTIONS_IS_STYLE(o))
fatalx("option %s is not a style", name);
return (&o->style);
return (&o->value.style);
}
struct options_entry *
@@ -592,7 +655,7 @@ options_set_string(struct options *oo, const char *name, int append,
o = options_get_only(oo, name);
if (o != NULL && append && OPTIONS_IS_STRING(o)) {
xasprintf(&value, "%s%s", o->string, s);
xasprintf(&value, "%s%s", o->value.string, s);
free(s);
} else
value = s;
@@ -606,8 +669,8 @@ options_set_string(struct options *oo, const char *name, int append,
if (!OPTIONS_IS_STRING(o))
fatalx("option %s is not a string", name);
free(o->string);
o->string = value;
free(o->value.string);
o->value.string = value;
return (o);
}
@@ -628,7 +691,7 @@ options_set_number(struct options *oo, const char *name, long long value)
if (!OPTIONS_IS_NUMBER(o))
fatalx("option %s is not a number", name);
o->number = value;
o->value.number = value;
return (o);
}
@@ -644,7 +707,7 @@ options_set_style(struct options *oo, const char *name, int append,
o = options_get_only(oo, name);
if (o != NULL && append && OPTIONS_IS_STYLE(o))
style_copy(&sy, &o->style);
style_copy(&sy, &o->value.style);
else
style_set(&sy, &grid_default_cell);
if (style_parse(&sy, &grid_default_cell, value) == -1)
@@ -657,7 +720,7 @@ options_set_style(struct options *oo, const char *name, int append,
if (!OPTIONS_IS_STYLE(o))
fatalx("option %s is not a style", name);
style_copy(&o->style, &sy);
style_copy(&o->value.style, &sy);
return (o);
}

View File

@@ -30,7 +30,9 @@ char *osdep_get_name(int, char *);
char *osdep_get_cwd(int);
struct event_base *osdep_event_init(void);
#ifndef __unused
#define __unused __attribute__ ((__unused__))
#endif
char *
osdep_get_name(int fd, __unused char *tty)
@@ -47,6 +49,7 @@ osdep_get_name(int fd, __unused char *tty)
&bsdinfo, sizeof bsdinfo);
if (ret == sizeof bsdinfo && *bsdinfo.pbsi_comm != '\0')
return (strdup(bsdinfo.pbsi_comm));
return (NULL);
#else
int mib[4] = { CTL_KERN, KERN_PROC, KERN_PROC_PID, 0 };
size_t size;

View File

@@ -157,11 +157,14 @@ paste_free(struct paste_buffer *pb)
* that the caller is responsible for allocating data.
*/
void
paste_add(char *data, size_t size)
paste_add(const char *prefix, char *data, size_t size)
{
struct paste_buffer *pb, *pb1;
u_int limit;
if (prefix == NULL)
prefix = "buffer";
if (size == 0) {
free(data);
return;
@@ -180,7 +183,7 @@ paste_add(char *data, size_t size)
pb->name = NULL;
do {
free(pb->name);
xasprintf(&pb->name, "buffer%04u", paste_next_index);
xasprintf(&pb->name, "%s%u", prefix, paste_next_index);
paste_next_index++;
} while (paste_get_name(pb->name) != NULL);
@@ -262,7 +265,7 @@ paste_set(char *data, size_t size, const char *name, char **cause)
return (0);
}
if (name == NULL) {
paste_add(data, size);
paste_add(NULL, data, size);
return (0);
}

53
regress/command-order.sh Normal file
View File

@@ -0,0 +1,53 @@
#!/bin/sh
# new-session without clients should be the right size
PATH=/bin:/usr/bin
TERM=screen
[ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux)
TMUX="$TEST_TMUX -Ltest"
$TMUX kill-server 2>/dev/null
TMP=$(mktemp)
trap "rm -f $TMP" 0 1 15
cat <<EOF >$TMP
new -sfoo -nfoo0; neww -nfoo1; neww -nfoo2
new -sbar -nbar0; neww -nbar1; neww -nbar2
EOF
$TMUX -f$TMP start </dev/null || exit 1
sleep 1
$TMUX lsw -aF '#{session_name},#{window_name}'|sort >$TMP || exit 1
cat <<EOF|cmp -s $TMP - || exit 1
bar,bar0
bar,bar1
bar,bar2
foo,foo0
foo,foo1
foo,foo2
EOF
$TMUX kill-server 2>/dev/null
cat <<EOF >$TMP
new -sfoo -nfoo0
neww -nfoo1
neww -nfoo2
new -sbar -nbar0
neww -nbar1
neww -nbar2
EOF
$TMUX -f$TMP start </dev/null || exit 1
sleep 1
$TMUX lsw -aF '#{session_name},#{window_name}'|sort >$TMP || exit 1
cat <<EOF|cmp -s $TMP - || exit 1
bar,bar0
bar,bar1
bar,bar2
foo,foo0
foo,foo1
foo,foo2
EOF
$TMUX kill-server 2>/dev/null
exit 0

14
regress/conf-syntax.sh Normal file
View File

@@ -0,0 +1,14 @@
#!/bin/sh
PATH=/bin:/usr/bin
TERM=screen
[ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux)
TMUX="$TEST_TMUX -Ltest"
$TMUX kill-server 2>/dev/null
for i in conf/*.conf; do
$TMUX -f/dev/null start \; source -n $i || exit 1
done
exit 0

View File

@@ -0,0 +1,58 @@
set -g prefix C-g
# needed for e.g. mutt
bind C-g send-prefix
set -g set-titles on
set -g status-position top
set -g status-keys vi
set -g mode-keys vi
set -g base-index 1
set -g pane-base-index 1
set -g focus-events on
set history-file ~/.tmux_SSH_history
set focus-events on
set -g history-limit 100000
set -s set-clipboard on
set -g display-time 3000
set -g display-panes-time 3000
set -g pane-border-status top
setw -g window-status-current-style bg=colour240,fg=colour250
setw -g window-status-separator "|"
set -g status-bg colour235
set -g status-fg colour245
set -g window-status-format " #I #{=+15:pane_title} #{=-2:?window_flags, #{window_flags}, }"
set -g window-status-current-format " #I #{=+15:pane_title} #{=-2:?window_flags, #{window_flags}, }"
set -g pane-border-format " #P: #{s/ //:pane_title} "
set -g renumber-windows on
set -g status-right-length 0
##############################################################
# I prefer not to have a status for my tabbed term
set -g status-right ""
set -g status-right-length 0
set -g status-left-length 0
set -g status-left ""
# some settings for "navigation"
bind -n C-PageUp copy-mode -u
unbind -n C-Left
unbind -n C-Right
bind -n C-Left select-window -t :-
bind -n C-Right select-window -t :+
# I prefer a tiled layout and easy joining of current active pane via windows'
# index
bind F1 join-pane -s 1.\; select-layout tiled
bind F2 join-pane -s 2.\; select-layout tiled
bind F3 join-pane -s 3.\; select-layout tiled
bind F4 join-pane -s 4.\; select-layout tiled
bind F5 join-pane -s 5.\; select-layout tiled
bind F6 join-pane -s 6.\; select-layout tiled
bind F7 join-pane -s 7.\; select-layout tiled
bind F8 join-pane -s 8.\; select-layout tiled
bind F9 join-pane -s 9.\; select-layout tiled

View File

@@ -0,0 +1,56 @@
# 256 colors for vim
set -g default-terminal "screen-256color"
# Set default shell to zsh
set-option -g default-shell /bin/zsh
# Start window numbering at 1
set-option -g base-index 1
set-window-option -g pane-base-index 1
# Cycle panes with C-b C-b
unbind ^B
bind ^B select-pane -t :.+
# Reload config wtih a key
bind-key r source-file ~/.tmux.conf \; display "Config reloaded!"
# Mouse works as expected
# set -g mode-mouse on
# set -g mouse-select-pane on
# set -g mouse-resize-pane on
# set -g mouse-select-window on
# Scrolling works as expected
set -g terminal-overrides 'xterm*:smcup@:rmcup@'
# Use the system clipboard
# set-option -g default-command "reattach-to-user-namespace -l zsh"
# Clear the pane and its history
bind -n C-k send-keys C-l \; clear-history
# smart pane switching with awareness of vim splits
bind -n C-h run "(tmux display-message -p '#{pane_current_command}' | grep -iq vim && tmux send-keys C-h) || tmux select-pane -L"
bind -n C-j run "(tmux display-message -p '#{pane_current_command}' | grep -iq vim && tmux send-keys C-j) || tmux select-pane -D"
bind -n C-k run "(tmux display-message -p '#{pane_current_command}' | grep -iq vim && tmux send-keys C-k) || tmux select-pane -U"
bind -n C-l run "(tmux display-message -p '#{pane_current_command}' | grep -iq vim && tmux send-keys C-l) || tmux select-pane -R"
bind -n C-\ run "(tmux display-message -p '#{pane_current_command}' | grep -iq vim && tmux send-keys 'C-\\') || tmux select-pane -l"
# C-l is taken oer by vim style pane navigation
bind C-l send-keys 'C-l'
# Use vim keybindings in copy mode
setw -g mode-keys vi
# Setup 'v' to begin selection as in Vim
# bind-key -t vi-copy v begin-selection
# bind-key -t vi-copy y copy-pipe "reattach-to-user-namespace pbcopy"
# Update default binding of `Enter` to also use copy-pipe
# unbind -t vi-copy Enter
# bind-key -t vi-copy Enter copy-pipe "reattach-to-user-namespace pbcopy"
# Powerline
run-shell "powerline-daemon -q"
source "/Users/adamcooper/Library/Python/3.7/lib/python/site-packages/powerline/bindings/tmux/powerline.conf"

View File

@@ -0,0 +1,30 @@
set -sg escape-time 10
set -g default-terminal tmux-256color
set -g prefix ^X
set -g history-limit 10000
setw -g mode-keys vi
setw -g xterm-keys off
# black, red, green, yellow, blue, magenta, cyan, white, default.
setw -g message-command-style fg=yellow,bg=black
setw -g message-style fg=black,bg=yellow
%if #{m:*mydomain*,#{host}}
set -g status-style fg=cyan,bg='#001040'
setw -g window-status-current-style fg='#f0f0f0',bg='#001040'
%elif #{||:#{m:*somedomain*,#{host}},#{m:*otherdomain*,#{host}}}
set -g status-style fg=white,bg='#400040'
setw -g window-status-current-style fg=yellow,bg='#400040',bright
%else
set -g status-style fg=white,bg='#800000'
setw -g window-status-current-style fg=brightwhite,bg='#800000'
%endif
unbind ^B
bind ^X last-window
bind x send-prefix
bind ^C new-window
bind ^D detach-client
bind ^N next-window
bind ^P previous-window

View File

@@ -0,0 +1,118 @@
#
# Tureba's tmux.conf
#
# To use it, either:
# a) link ~/.tmux.conf to it; or
# b) create a ~/.tmux.conf that sources it.
#
# who: Arthur Nascimento <tureba@gmail.com>
# where: github.com/tureba/myconfigfiles
#
# defaults
set -g default-shell /bin/zsh
set -g default-command zsh
# tmux sets screen/screen-256, but has no codes for italics
set -g default-terminal tmux-256color
# linux terminal doesn't need this, but xterm does
set -g terminal-overrides 'xterm*:smcup@:rmcup@,*256col*:colors=256,xterm*:XT'
# xterm-style function key sequences
setw -g xterm-keys on
# 1, 2 and 3 are closer together than 0, 1 and 2
set -g base-index 1
set -g pane-base-index 1
# easier to type than C-b
set -g prefix C-a
set -g prefix2 C-b
unbind C-b
bind C-a send-prefix
# for repeatable keys
set -g repeat-time 170
# status bar
set -g status-style fg=green,bg=colour234
set -g status-right-style bg=colour236
set -g status-right "#[bold,fg=blue][#[fg=default]#T#[fg=blue]]#[nobold,fg=default] | #[fg=yellow]%F %R"
set -g status-right-length 120
set -g status-left-style bg=colour236,bright
set -g status-left "#[fg=blue][#[fg=default]#h#[fg=cyan]:#[fg=default]#S#[fg=blue]]"
set -g status-left-length 30
setw -g window-status-style fg=green
setw -g window-status-format " #I#[nobold]:#W "
setw -g window-status-current-style fg=green,bright
setw -g window-status-current-format "#[fg=red][#[fg=default]#I:#W#[fg=red]]"
setw -g window-status-separator "|"
setw -g window-status-activity-style blink
setw -g window-status-bell-style blink
setw -g window-status-last-style bright
# enable wm window titles
set -g set-titles on
# auto window rename
setw -g automatic-rename on
# auto window resize
setw -g aggressive-resize on
# mouse settings
set -g mouse on
# var|bind \ cmd | vim | less | copy | zsh
# pane_in_mode | 0 | 0 | 1 | 0
# mouse_any_flag | 1 | 0 | 0 | 0
# alternate_on | 1 | 1 | 0 | 0
# WheelUpPane | send -M | send Up | * | send Up (** or copy-mode -e)
# WheelDownPane | send -M | send Down | * | send Down
# * panes in copy mode have scroll handled by different bindings
# ** cycle over shell history
#bind -T root WheelUpPane if -Ft= '#{mouse_any_flag}' 'send -Mt=' 'send -t= Up'
# ** enter copy mode
bind -T root WheelUpPane if -Ft= '#{mouse_any_flag}' 'send -Mt=' 'if -Ft= "#{alternate_on}" "send -t= Up" "copy-mode -et="'
bind -T root WheelDownPane if -Ft= '#{mouse_any_flag}' 'send -Mt=' 'send -t= Down'
# sensible v/h splits
unbind %
unbind '"'
bind | split-window -h
bind - split-window -v
# hjkl pane traversal
bind -r h select-pane -L
bind -r j select-pane -D
bind -r k select-pane -U
bind -r l select-pane -R
# window navigation
unbind p
bind -r [ previous-window
unbind n
bind -r ] next-window
# Vi copypaste mode
setw -g mode-keys vi
bind C-c copy-mode
bind p paste-buffer
bind -T copy-mode-vi v send-keys -X begin-selection
bind -T copy-mode-vi y send-keys -X copy-selection
bind -T copy-mode-vi V send-keys -X rectangle-toggle
# toggle window activity monitoring
bind m setw monitor-activity
# reload the configuration
bind r source-file ~/.tmux.conf
# toggle synchronize-panes
bind S setw synchronize-panes
# create a new window with exactly this command
bind C command-prompt "new-window 'exec %%'"
# (toggle) mark this pane for easier joins and swaps
bind . select-pane -m

View File

@@ -0,0 +1,30 @@
###
if-shell " \
tmux -V \
| awk '{print $2}' \
| awk -F - '{print $1}' \
| awk '{ \
if ($1 ~ /^[[:digit:].]+$/) { \
exit !($1 >= 2.6) \
} else { \
exit !($1 == \"master\" || $1 == \"next\") \
} \
}'" \
"source-file ~/.tmux/v2rc" \
"source-file ~/.tmux/v1rc" \
;
###
set-option -qg status-left \
"[#[fg=yellow]#{session_name}#[default]] #[fg=colour060]#{host_short}#[default]:#[fg=colour151]#{b:pane_current_path} #[fg=colour099]#(git -C #{pane_current_path} symbolic-ref --short HEAD) #[fg=green]#(git -C #{pane_current_path} status --porcelain --untracked-files=no | cut -b 1-1 | sort | uniq | awk '/^[^[:space:]]/ {printf\(\"%%s\", $0\)}')#[fg=red]#(git -C #{pane_current_path} status --porcelain --untracked-files=no | cut -b 2-2 | sort | uniq | awk '/^[^[:space:]]/ {printf\(\"%%s\", $0\)}')#[fg=colour113]#(git -C #{pane_current_path} stash list 2>/dev/null | wc -l | tr -d '\n' | sed s,^0\$,,) #[default]"
set-option -qg status-right \
"#[default] ┊ #[fg=colour065]#(grep ^MemFree /proc/meminfo | awk '{print rshift\($2, 10\)}')#[fg=colour071]m #[default]┊ #[fg=colour101]#(echo \"\(`awk '{print \$1}' /proc/loadavg` / `grep ^processor /proc/cpuinfo | wc -l`\) \* 100\" | bc -ql | sed 's,\\..*,,' | awk '{printf\(\"%%2u\", $0\)}')#[fg=colour102]%% "
set-option -qwg window-status-current-format \
"#[fg=colour208]»#[fg=colour190]#{window_name}#[fg=colour037]·#{?window_flags,#[fg=colour058]#{window_flags}#[default], #[default]}"
set-option -qwg window-status-format \
"#[default]»#[fg=colour066]#{window_name}#[fg=colour037]·#{?window_flags,#[fg=colour058]#{window_flags}#[default], #[default]}"

View File

@@ -0,0 +1,432 @@
# Dynamic configuration file generated by ~/Makefile from /home/sunny/.tmux.conf.erb
#
# DO NOT EDIT THIS FILE BY HAND --
# YOUR CHANGES WILL BE OVERWRITTEN
#
bind-key R source ~/.tmux.conf \; display-message 'config reloaded!'
#-----------------------------------------------------------------------------
# terminal
#-----------------------------------------------------------------------------
# enable mouse support for general selection and control
set-option -g mouse on
# auto-set terminal title to current window pane's title
set-option -g set-titles on
# enable 256-color support for pretty colorschemes in Vim
set-option -g default-terminal 'screen-256color'
# allow Vim to receive focus events from terminal window
set-option -g focus-events on
# allow Vim to recieve modifier keys: Shift, Control, Alt
set-window-option -g xterm-keys on
# prevent tmux from catching modifier keys meant for Vim
set-option -s escape-time 0
# enable 24-bit true color RGB escape sequences under st
# https://sunaku.github.io/tmux-24bit-color.html
set-option -ga terminal-overrides ',st-256color:Tc'
set-option -ga terminal-overrides ',xterm-256color:Tc' # hterm (ChromeOS)
# allow set-titles to change the window title under XTerm
# http://opennomad.com/content/goodbye-screen-hello-tmux
set-option -ga terminal-overrides ',xterm*:XT'
# allow set-titles to change the window title under XTerm
# http://opennomad.com/content/goodbye-screen-hello-tmux
# http://stackoverflow.com/questions/15195624
set-option -ga terminal-overrides ',st-256color:smkx=\E='
# yank to system clipboard rather than primary selection
# http://invisible-island.net/xterm/terminfo-contents.html#tic-xterm_tmux
set-option -ga terminal-overrides ',xterm*:Ms=\E]52;c;%p2%s\007'
# KiTTY always appends to clipboard; must clear it first
# https://sw.kovidgoyal.net/kitty/protocol-extensions.html#pasting-to-clipboard
set-option -ga terminal-overrides ',xterm-kitty:Ms=\E]52;c;!\007\E]52;c;%p2%s\007'
# prevent standout from appearing as italics under URxvt
# http://comments.gmane.org/gmane.comp.terminal-emulators.tmux.user/1927
set-option -ga terminal-overrides ',rxvt-unicode*:sitm@'
#-----------------------------------------------------------------------------
# appearance
#-----------------------------------------------------------------------------
# Colors from the "lucius" and "gruvbox" themes in the vim-airline plugin:
# https://github.com/bling/vim-airline/blob/master/autoload/airline/themes/lucius.vim
# https://github.com/morhetz/gruvbox/blob/master/autoload/airline/themes/gruvbox.vim
set-option -g status-style fg=colour246,bg=colour237
set-window-option -g window-status-current-style fg=colour214,bg=colour239
set-option -g pane-border-style fg=colour239
set-option -g pane-active-border-style fg=colour208
set-option -g message-style fg=colour214,bg=colour239
set-window-option -g mode-style fg=colour214,bg=colour239,bold,reverse
# Common UI interaction cues from Blueprint CSS:
# http://blueprintcss.org/tests/parts/forms.html
set-window-option -g window-status-bell-style 'bg=#205791,fg=#d5edf8' # info (blue)
set-window-option -g window-status-activity-style 'bg=#8a1f11,fg=#fbe3e4' # error (red)
#-----------------------------------------------------------------------------
# status bar
#-----------------------------------------------------------------------------
# toggle status bar visibility
bind-key -n M-` set-option -g status
# toggle status bar position
bind-key -n M-~ \
if-shell 'tmux show-option -g status-position | grep -q top$' \
'set-option -g status-position bottom' \
'set-option -g status-position top'
# put status bar at the top of the screen
set-option -g status-position top
# list windows on left side of status bar
set-option -g status-left-length 0
# make window list easier to scan
set-window-option -g window-status-format ' #[bold]#I#F#[nobold]#W '
set-window-option -g window-status-current-format ' #[bold]#I#F#[nobold]#W '
set-window-option -g window-status-separator ''
# show pane title, pane identifier, and hostname on right side of status bar
set-option -g status-right-length 64
set-option -g status-right '#{=32:pane_title} \
#[fg=colour214,bg=colour239] #S:#I.#P \
#(test -n "$SSH_TTY" && echo "#[fg=colour214,bg=colour239,bold,reverse] #H ")'
#-----------------------------------------------------------------------------
# windows
#-----------------------------------------------------------------------------
# create window
bind-key -n M-e new-window
# rename window
bind-key -n M-E command-prompt -I '#W' 'rename-window "%%%"'
set-window-option -g automatic-rename off
# break off pane to a new window
bind-key -n M-x \
command-prompt -p 'break-pane:' -I '#W' \
'break-pane ; rename-window "%%%"'
bind-key -n M-X break-pane
# focus window
bind-key -n M-, previous-window
bind-key -n M-. next-window
bind-key -n M-o last-window
# focus by number
set-option -g base-index 1
set-window-option -g pane-base-index 1
set-option -g renumber-windows on
bind-key -n M-0 choose-window
bind-key -n M-1 select-window -t :1
bind-key -n M-2 select-window -t :2
bind-key -n M-3 select-window -t :3
bind-key -n M-4 select-window -t :4
bind-key -n M-5 select-window -t :5
bind-key -n M-6 select-window -t :6
bind-key -n M-7 select-window -t :7
bind-key -n M-8 select-window -t :8
bind-key -n M-9 select-window -t :1 \; select-window -t :-1
# swap window
bind-key -n M-< swap-window -t :-1
bind-key -n M-> swap-window -t :+1
# monitor window
set-option -g visual-activity on
set-option -g visual-silence on
bind-key -n M-k \
set-window-option monitor-activity \;\
display-message 'monitor-activity #{?monitor-activity,on,off}'
bind-key -n M-K \
if-shell 'tmux show-window-option -g monitor-activity | grep -q off$' \
'set-window-option -g monitor-activity on' \
'set-window-option -g monitor-activity off' \;\
display-message 'monitor-activity #{?monitor-activity,on,off} (global)'
bind-key -n M-j \
command-prompt -p 'monitor-silence (seconds):' -I '#{monitor-silence}' \
'set-window-option monitor-silence %% ;\
display-message "monitor-silence #{?monitor-silence,on,off}"'
#-----------------------------------------------------------------------------
# panes
#-----------------------------------------------------------------------------
# send input to all panes in window (toggle)
bind-key C-a \
set-option synchronize-panes \;\
display-message 'synchronize-panes #{?synchronize-panes,on,off}'
# clear the screen in all panes in window
bind-key C-l \
set-option synchronize-panes on \;\
send-keys C-l \;\
set-option synchronize-panes off
# create pane (below, above, left, right)
bind-key -n M-c split-window -c '#{pane_current_path}'
bind-key -n M-C split-window -c '#{pane_current_path}' -b
bind-key -n M-R split-window -c '#{pane_current_path}' -b -h
bind-key -n M-r split-window -c '#{pane_current_path}' -h
# join pane (above, left, below, right)
bind-key -n M-g move-pane -t .-1 -s . # join pane at bottom of prev pane
bind-key -n M-l move-pane -t .-1 -s . -h # join pane at right of prev pane
bind-key -n M-G move-pane -d -s .+1 -t . # join next pane at bottom
bind-key -n M-L move-pane -d -s .+1 -t . -h # join next pane at right
# Intelligently navigate tmux panes and Vim splits using the same keys.
# See https://sunaku.github.io/tmux-select-pane.html for documentation.
#
# +-------------+------------+-----------------------------+
# | inside Vim? | is Zoomed? | Action taken by key binding |
# +-------------+------------+-----------------------------+
# | No | No | Focus directional tmux pane |
# | No | Yes | Nothing: ignore key binding |
# | Yes | No | Seamlessly focus Vim / tmux |
# | Yes | Yes | Focus directional Vim split |
# +-------------+------------+-----------------------------+
#
vim_navigation_timeout=0.05 # number of seconds we give Vim to navigate
navigate=' \
pane_is_zoomed() { \
test #{window_zoomed_flag} -eq 1; \
}; \
pane_title_changed() { \
test "#{pane_title}" != "$(tmux display -p "##{pane_title}")"; \
}; \
command_is_vim() { \
case "${1%% *}" in \
(vi|?vi|vim*|?vim*|view|?view|vi??*) true ;; \
(*) false ;; \
esac; \
}; \
pane_contains_vim() { \
case "#{=3:pane_current_command}" in \
(git|ssh|sh) command_is_vim "#{=5:pane_title}" ;; \
(*) command_is_vim "#{=5:pane_current_command}" ;; \
esac; \
}; \
pane_contains_neovim_terminal() { \
test "#{=12:pane_title}" = "nvim term://"; \
}; \
navigate() { \
tmux_navigation_command=$1; \
vim_navigation_command=$2; \
vim_navigation_only_if=${3:-true}; \
if pane_contains_vim && eval "$vim_navigation_only_if"; then \
if pane_contains_neovim_terminal; then \
tmux send-keys C-\\ C-n; \
fi; \
eval "$vim_navigation_command"; \
if ! pane_is_zoomed; then \
sleep $vim_navigation_timeout; : wait for Vim to change title; \
if ! pane_title_changed; then \
eval "$tmux_navigation_command"; \
fi; \
fi; \
elif ! pane_is_zoomed; then \
eval "$tmux_navigation_command"; \
fi; \
}; \
navigate '
navigate_left=" $navigate 'tmux select-pane -L' 'tmux send-keys C-w h'"
navigate_down=" $navigate 'tmux select-pane -D' 'tmux send-keys C-w j'"
navigate_up=" $navigate 'tmux select-pane -U' 'tmux send-keys C-w k'"
navigate_right="$navigate 'tmux select-pane -R' 'tmux send-keys C-w l'"
navigate_back=" $navigate 'tmux select-pane -l || tmux select-pane -t1'\
'tmux send-keys C-w p' \
'pane_is_zoomed' "
## QWERTY keys - comment these out if you don't use QWERTY layout!
#bind-key -n M-h run-shell -b "$navigate_left"
#bind-key -n M-j run-shell -b "$navigate_down"
#bind-key -n M-k run-shell -b "$navigate_up"
#bind-key -n M-l run-shell -b "$navigate_right"
#bind-key -n M-\ run-shell -b "$navigate_back"
# Dvorak keys - comment these out if you don't use Dvorak layout!
bind-key -n M-d run-shell -b "$navigate_back"
bind-key -n M-h run-shell -b "$navigate_left"
bind-key -n M-t run-shell -b "$navigate_up"
bind-key -n M-n run-shell -b "$navigate_down"
bind-key -n M-s run-shell -b "$navigate_right"
# resize pane
bind-key -r H resize-pane -L 5
bind-key -r T resize-pane -U 5
bind-key -r N resize-pane -D 5
bind-key -r S resize-pane -R 5
# zoom pane
bind-key -n M-m resize-pane -Z
# swap pane
bind-key -n M-- swap-pane -D
bind-key -n M-_ swap-pane -U
bind-key -n M-D run-shell 'tmux select-pane -l \; swap-pane -d -s #D'
bind-key -n M-H run-shell 'tmux select-pane -L \; swap-pane -d -s #D'
bind-key -n M-T run-shell 'tmux select-pane -U \; swap-pane -d -s #D'
bind-key -n M-N run-shell 'tmux select-pane -D \; swap-pane -d -s #D'
bind-key -n M-S run-shell 'tmux select-pane -R \; swap-pane -d -s #D'
# attach by number
bind-key -n 'M-!' join-pane -t :1
bind-key -n 'M-@' join-pane -t :2
bind-key -n 'M-#' join-pane -t :3
bind-key -n 'M-$' join-pane -t :4
bind-key -n 'M-%' join-pane -t :5
bind-key -n 'M-^' join-pane -t :6
bind-key -n 'M-&' join-pane -t :7
bind-key -n 'M-*' join-pane -t :8
bind-key -n 'M-(' run-shell 'tmux select-window -t :1 \;\
select-window -t :-1 \;\
join-pane -s "#{pane_id}"'
bind-key -n 'M-)' choose-window 'join-pane -t "%%%"'
# promote pane (toggle)
bind-key -n M-Enter \
if-shell 'test #P -ne 1' \
'select-pane -t 1' \
'last-pane; swap-pane -s 1'
# rotate panes
bind-key -n M-a rotate-window -D
bind-key -n M-A rotate-window -U
#-----------------------------------------------------------------------------
# layouts
#-----------------------------------------------------------------------------
bind-key M-w select-layout main-horizontal
bind-key M-W select-layout even-vertical
bind-key M-v select-layout main-vertical
bind-key M-V select-layout even-horizontal
bind-key M-z select-layout tiled
# half-screen tiling layouts (horizontal, vertical)
# https://sunaku.github.io/tmux-half-screen-tiling-layouts.html
bind-key -n M-w select-layout main-horizontal \;\
run-shell 'tmux resize-pane -t 1 -y $(( #{window_height} / 2 ))'
bind-key -n M-v select-layout main-vertical \;\
run-shell 'tmux resize-pane -t 1 -x $(( #{window_width} / 2 ))'
# binary space partitioned layouts (dwindle, spiral)
# https://sunaku.github.io/tmux-layout-dwindle.html
bind-key -n M-w run-shell 'tmux-layout-dwindle brhc && tmux-redraw-vim'
bind-key -n M-W run-shell 'tmux-layout-dwindle trhc && tmux-redraw-vim'
bind-key -n M-v run-shell 'tmux-layout-dwindle brvc && tmux-redraw-vim'
bind-key -n M-V run-shell 'tmux-layout-dwindle blvc && tmux-redraw-vim'
bind-key -n M-z select-layout tiled
#-----------------------------------------------------------------------------
# scrollback buffer
#-----------------------------------------------------------------------------
# buffer length
set-option -g history-limit 32767
# search buffer using copy mode
bind-key -n M-/ copy-mode \;\
command-prompt -p 'search-backward (press up):' \
-i 'send-keys -X search-backward-incremental "%%%"'
# search buffer using Vim or less
bind-key -n M-| \
capture-pane -J -S - \; \
new-window -n '#S:#I.#P' -a ' \
tmux save-buffer - \; delete-buffer | { \
if command -v vim; \
then vim -R -c "set nofen is hls ic" -; \
else less; \
fi; \
}; \
' \; \
run-shell 'tmux send-keys G \?'
# search colored buffer using less
bind-key -n M-? \
capture-pane -e -J -S - \; \
new-window -n '#S:#I.#P' -a ' \
tmux save-buffer - \; delete-buffer | \
less -R \
' \; \
run-shell 'tmux send-keys G \?'
# scroll buffer
# NOTE: set "URxvt.saveLines: 0" in ~/.Xdefaults to make Shift+PageUp bindable
# NOTE: see http://aperiodic.net/screen/interface for doing the same in XTerm
bind-key -n S-PPage copy-mode -u
# copy text from buffer
bind-key -n M-u copy-mode
set-window-option -g mode-keys vi
bind-key -T copy-mode-vi v send-keys -X begin-selection
bind-key -T copy-mode-vi y send-keys -X copy-selection
bind-key -T copy-mode-vi - send-keys -X jump-again
bind-key -T copy-mode-vi _ send-keys -X jump-reverse
bind-key -T copy-mode-vi ? command-prompt -p 'search-backward:' -I '#{pane_search_string}' -i 'send-keys -X search-backward-incremental "%%%"'
bind-key -T copy-mode-vi / command-prompt -p 'search-forward:' -I '#{pane_search_string}' -i 'send-keys -X search-forward-incremental "%%%"'
# transfer copied text to attached terminal with yank:
# https://github.com/sunaku/home/blob/master/bin/yank
bind-key -T copy-mode-vi Y send-keys -X copy-pipe 'yank > #{pane_tty}'
# open the visual selection with xdg-open(1)
bind-key -T copy-mode-vi O send-keys -X copy-pipe 'xargs -r xdg-open'
# paste most-recently copied text
bind-key -n M-i paste-buffer
# paste previously copied text (chosen from a menu)
bind-key -n M-I choose-buffer
# transfer most-recently copied text to attached terminal with yank:
# https://github.com/sunaku/home/blob/master/bin/yank
bind-key -n M-y run-shell 'tmux save-buffer - | yank > #{pane_tty}'
# transfer previously copied text (chosen from a menu) to attached terminal:
# https://github.com/sunaku/home/blob/master/bin/yank
bind-key -n M-Y choose-buffer 'run-shell "tmux save-buffer -b \"%%%\" - | yank > #{pane_tty}"'
#-----------------------------------------------------------------------------
# TMUX plugin manager https://github.com/tmux-plugins/tpm
#-----------------------------------------------------------------------------
set -g @plugin 'tmux-plugins/tmux-resurrect'
set -g @resurrect-capture-pane-contents on
set -g @plugin 'Morantron/tmux-fingers'
set -g @fingers-key '-n M-U'
set -g @fingers-compact-hints 1
set -g @fingers-hint-format '#[fg=yellow,bold,reverse]%s'
set -g @fingers-hint-labels ' \
a o e u i d h t n s \
p y f g c r l \
q j k x b m w v z \
A O E U I D H T N S \
P Y F G C R L \
Q J K X B M W V Z \
'
run-shell ~/.tmux/plugins/tpm/tpm

View File

@@ -0,0 +1,80 @@
set -g default-command "if [ \"$(uname)\" = 'Darwin' ]; then exec reattach-to-user-namespace $SHELL; else exec $SHELL; fi"
set -g history-limit 32000
set -g update-environment "DISPLAY WINDOWID SSH_ASKPASS SSH_AUTH_SOCK SSH_AGENT_PID SSH_CONNECTION SSH_CLIENT SSH_TTY KRB5CCNAME Apple_PubSub_Socket_Render Apple_Ubiquity_Message"
# Reset SHLVL (otherwise it is 2 inside tmux)
setenv -g SHLVL 0
# Send esc faster so that neovim won't get so laggy
# https://github.com/neovim/neovim/issues/2093
set -g escape-time 100
# Disable paste detection
set -g assume-paste-time 0
# Titles and window names
set -g set-titles on
set -g set-titles-string "#T"
# Make it not so annoying/sticky to switch windows
set -g repeat-time 170
# Don't deattach me when a session ends
set -g detach-on-destroy off
# Make shift+keys work
setw -g xterm-keys on
# Prefix
set -g prefix ^A
unbind ^B
bind ^A send-prefix
bind a send-prefix
# Last window
bind ^a last
# Next & prev
bind ' ' next
bind '^ ' next
bind ^p prev
# Status
set -g status off
# Need more (cow)bells!
set -g bell-action any
set -g bell-on-alert on
# Detach
bind ^d detach
# Control the a tmux in a tmux
bind A send-prefix \; send-prefix
bind C send-prefix \; send-keys c
bind n send-prefix \; send-keys ' '
bind bspace send-prefix \; send-keys p
bind '#' send-prefix \; send-keys '"'
# Other key bindings.
bind ^r command-prompt "find-window '%%'"
bind '"' choose-tree -w
bind w split-window
bind W split-window -c "#{pane_current_path}"
bind ^w split-window
bind I list-windows
bind i list-windows
bind D neww 'if who | grep -q "$USER.* via mosh"; then tmux lsc -F "#{client_activity} #{client_tty}" | sort | head -n -1 | awk "{print \$2}" | xargs -n1 tmux detach -t; else for i in $(tmux lsc | cut -d: -f1 | grep -v "^$SSH_TTY$"); do tmux detach -t $i; done; fi'
bind S neww -t 999 'window=`tmux display -p "#{pane_title}"`; i=0; tmux list-windows | cut -d: -f1 | while read j; do if [ $j != $i ]; then tmux move-window -s $j -t $i; fi; i=$(($i+1)); done' # ; tmux find-window -T "$window"
bind ^s command-prompt "rename-session '%%'"
# Make the default HOME always ~
bind c neww -c ~
bind ^c new -c ~
bind escape copy-mode
# Copy to the OS clipboard
bind -T copy-mode-vi y send -X copy-pipe-and-cancel "if [ \"$(uname)\" = 'Darwin' ]; then reattach-to-user-namespace pbcopy; else xclip; fi"
bind j command-prompt "join-pane -s '%%'"
bind ! break-pane -d
bind - command-prompt "move-pane -t '%%'"
# Makes `tmux a` work even when there isn't a session going on
new-session -A -c ~

View File

@@ -0,0 +1,65 @@
set-option -g allow-rename on
set-option -g automatic-rename off
set-option -g base-index 1
set-option -g default-command "$SHELL"
set-option -g default-terminal "tmux-256color"
set-option -g history-limit 25000
set-option -g mode-keys vi
set-option -g prefix C-f
set-option -g renumber-windows yes
set-option -g set-titles on
set-option -g set-titles-string "#T"
set-option -g xterm-keys on
set-option -g status-interval 1
set-option -g status-left "#(tmux-status-left)"
set-option -g status-left-length 40
set-option -g status-right ""
set-option -g window-status-current-attr bold
set-option -g window-status-current-format "[#I#F#{?window_zoomed_flag, ,}#{=40:pane_title}]"
set-option -g window-status-format "#I#{?window_zoomed_flag, ,}#F#{?window_flags,, }#{?window_zoomed_flag, ,}#{=20:pane_title}"
set-option -g pane-active-border-fg colour247
set-option -g pane-border-fg colour235
set-option -g status-bg colour7
set-option -g status-fg colour16
set-option -g status-left-bg colour4
set-option -g status-left-fg colour15
set-option -g window-status-current-bg colour15
set-option -g window-status-current-fg colour16
set-option -g update-environment "DBUS_SESSION_BUS_ADDRESS DISPLAY KRB5CCNAME \
SESSION_MANAGER SSH_AGENT_PID SSH_ASKPASS SSH_AUTH_SOCK SSH_CONNECTION \
WINDOWID XAUTHORITY SSH_TTY"
bind-key w break-pane -d
bind-key l clear-history \; display "Pane history cleared."
bind-key C-f if-shell "test #{window_panes} -eq 1" last-window last-pane
bind-key N new-session
bind-key t new-window
bind-key z resize-pane -Z
bind-key C-r rotate-window -D
bind-key -n C-t run-shell "metamux new-shell-in-pane #{window_panes}"
bind-key n run-shell "metamux rotate-pane next"
bind-key p run-shell "metamux rotate-pane prev"
bind-key q run-shell "metamux pane-buster"
bind-key S run-shell "metamux join-hidden-pane -v"
bind-key u run-shell "metamux open-last-url-printed"
bind-key | run-shell "metamux join-hidden-pane -h"
bind-key f send-prefix
bind-key r source "$HOME/.tmux.conf" \; display "Configuration reloaded."
# When the current window is split, Ctrl+Tab and Ctrl+Shift+Tab should rotate
# between the split windows. If there is only one pane in the current window,
# Ctrl+Tab and Ctrl+Shift+Tab will cycle between windows as though they were
# tabs in modern desktop UIs.
bind-key -n C-Tab if-shell "test #{window_panes} -eq 1" next-window "select-pane -t :.+"
bind-key -n C-S-Tab if-shell "test #{window_panes} -eq 1" previous-window "select-pane -t :.-"
# Binding to mark and swap panes; if no pane is marked, the shortcut will mark
# the active pane, but if a pane is already marked, active pane will be swapped
# with the marked pane.
bind-key m if-shell 'test -z "$PANE_IS_MARKED"' \
"select-pane -m; set-env PANE_IS_MARKED 1" \
"swap-pane; select-pane -M; set-env -u PANE_IS_MARKED"

View File

@@ -0,0 +1,580 @@
# Time-stamp: <2018-05-31 17:10:05 kmodi>
# https://github.com/tmux/tmux
# Hi-lock: (("\\(^\\s< \\**\\)\\(\\* *.*\\)" (1 'org-hide prepend) (2 '(:inherit org-level-1 :height 1.3 :weight bold :overline t :underline t) prepend)))
# Hi-Lock: end
# Running tmux built from master branch on tcsh in uxterm
# tmux version 2.5-RC+ dev
# Contents:
#
# PREFIX
# Source config
# Pane Management
# Window <-join/split-> Pane
# Select Panes
# Resize Panes
# Dynamic Split
# Window Management
# Window Navigation
# Swap Windows
# Split Window
# Layout
# Session Management
# Mouse
# Drag pane border to resize
# Left click on pane
# Middle click on pane
# Right click on pane
# Wheel scroll in pane
# Wheel scroll in pane WHILE in copy-mode
# Left click on status
# Middle click on status
# Other mouse settings
# Window Title
# Status Bar
# Left Status
# Right Status
# Pane Status
# Colors
# Status Bar Colors
# Message Colors
# Window Status Colors
# Pane Colors
# Mode Info Colors
# Activity
# Command Prompt
# Audible and Visual Bells
# Copy & Paste
# Synchronize commands to panes/windows/sessions
# Terminal Setting
# Other Options
# Server Options
# Session Options
# Window Options
# Notes
# * PREFIX
set -g prefix C-z
unbind C-b # unbind the default binding to send prefix key to the application
# Often you'll run a tmux inside another tmux and need a command sequence to
# send things to the inner session. With below binding that can be accomplished
# using "PREFIX Z <command>"
bind Z send-prefix
# * Source config
unbind r # unbind default binding to force redraw of attached client
bind r source-file ~/.tmux.conf \; display "Finished sourcing ~/.tmux.conf ."
# * Pane Management
set -g pane-base-index 1 # start pane indices at 1
set -g main-pane-width 100 # used by selectl main-vertical
bind z resize-pane -Z # zoom/unzoom the current pane
# If the window has >1 panes kill them without confirming. But confirm before kill
# the last pane (along with its window) in a window
bind x if "tmux display -p \"#{window_panes}\" | grep ^1\$" \
"confirm-before -p \"Kill the only pane in window? It will kill this window too! (y/n)\" kill-pane" \
"kill-pane"
bind C clear-history \; display "Cleared history of the current pane."
unbind C-p
bind C-p run -b "tmux display -p -F '#{pane_current_path}' | xclip -i -sel pri" \; display "Copied current path '#{pane_current_path}' to the primary selection."
# Hooks need tmux 2.3+
# set-hook -g -u after-kill-pane # Remove after hook for kill-pane
set-hook -g after-kill-pane "selectl main-vertical"
# If -g options is used when setting the hook, it has to be used when
# removing (-u option) the hook too.
# ** Window <-join/split-> Pane
# Join a pane *from* a different window (of same or different session) into the CURRENT window
# Binding mnemonic: F for (F)etch/pull (as in git) from a different window
bind F command-prompt -p "Join pane from [sess:]win#[.pane#] (ex: kmodi:3.1) into current window:" "join-pane -s '%%'"
# Join CURRENT pane *to* a different window
# Binding mnemonic: P for (P)ush (as in git) to a different window
bind P command-prompt -p "Send CURRENT pane to [sess:]win# (ex: kmodi:3):" "join-pane -t '%%'"
# PREFIX ! : break-pane, convert the current pane to a window
# ** Select Panes
bind o select-pane -t :.+ # cycle to the next pane number
bind O select-pane -t :.- # cycle to the previous pane number
# PREFIX ; : last-pane or select-pane -l, switch to the last active pane
# PREFIX ← : select-pane -L, switch to the pane on the left
# PREFIX → : select-pane -R, switch to the pane on the right
# PREFIX ↑ : select-pane -U, switch to the pane on the top
# PREFIX ↓ : select-pane -D, switch to the pane on the bottom
# PREFIX { : swap-pane -U, swap current pane with the pane above (not literally above)
# PREFIX } : swap-pane -D, swap current pane with the pane below (not literally below)
# ** Resize Panes
bind -r h resize-pane -L 2
bind -r C-h resize-pane -L 2
bind -r j resize-pane -D 2
bind -r C-j resize-pane -D 2
bind -r k resize-pane -U 2
bind -r C-k resize-pane -U 2
unbind l # unbind default binding for `last-window`
bind -r l resize-pane -R 2
bind -r C-l resize-pane -R 2
# ** Dynamic Split
# Key-chaining example, analogous to prefix maps in emacs
bind / switch-client -Tlauncher
# Run below -Tlauncher commands using "PREFIX / <binding>"
# Open calendar in a split window "PREFIX / c"
# FIXME: Below does not work; cal pane quits as soon as it launches (before "&& sleep .."
# was added). To make better of the situation, I now auto-close that pane after 3 seconds.
# bind -Tlauncher c split-window -h 'cal && sleep 3'
bind -Tlauncher c run "/home/kmodi/scripts/tcsh/tmux/dynamic_split.csh 'cal && sleep 3'"
# Start emacsclient in terminal mode in a split window "PREFIX / e"
# Use the emacs binding "C-x 5 0" to quit from that pane gracefully.
bind -Tlauncher e run "/home/kmodi/scripts/tcsh/tmux/dynamic_split.csh 'emacsclient -a \"\" -t'"
# Open man page "PREFIX / m"
# PREFIX / m will bring up the tmux command prompt. Enter the command for which
# you want to see the man page, example: ls. That man page will open in a split
# pane. When you are done reviewing the man page, hit q and the split pane
# closes by itself. Beautiful!
bind -Tlauncher m command-prompt -p "man" "run \"/home/kmodi/scripts/tcsh/tmux/dynamic_split.csh 'man %1'\""
# Open python interpreter in a split window for quick calculations "PREFIX / p"
# Ctrl-D in python quits python and thus closes the split window too.
bind -Tlauncher p run "/home/kmodi/scripts/tcsh/tmux/dynamic_split.csh 'ipython --profile=default --no-confirm-exit'"
# PREFIX Up, Down, Right, Left : Move cursor from one pane to another
# PREFIX Space : Cycle through different pane layouts
# PREFIX C-o : rotate-window, rotate panes in the current window
# * Window Management
set -g base-index 1 # start window indices at 1
# automatically renumber the windows
# http://unix.stackexchange.com/questions/21742/renumbering-windows-in-tmux
set -g renumber-windows on
bind C-f command-prompt -p "New window:" "new-window -c '#{pane_current_path}' -n %1"
bind C-r command-prompt -p "New name for this window:" "rename-window '%%'"
unbind L # unbind default binding for `switch-client -l`
bind L list-windows -F '#{window_index}:#{window_name}: #{?pane_dead, (dead), (not dead)}'
unbind & # unbind default binding for `kill-window`
bind C-c confirm-before -p "Kill this window? (y/n)" kill-window
# Move the current window to another window index in the same or any other session
bind m command-prompt -p "Move window to sess or sess:win# or win# (ex: kmodi or kmodi:3 or 2(of current session)):" "move-window -t '%%'"
# Move or bring a window from a different session to the current one
bind M command-prompt -p "Move the window from sess:win# (ex: kmodi:3):" "move-window -s '%%'"
# ** Window Navigation
bind C-z last-window # switch to last active window
# Allow repeats for next/previous-window
bind -r p previous-window
bind -r n next-window
# switch to another window by name
bind W split-window "tmux lsw | peco --initial-index `tmux lsw | awk '/active.$/ {print NR-1}'` | cut -d':' -f 1 | xargs tmux select-window -t"
# PREFIX <N> : switches to window with index=N
# ** Swap Windows
bind N move-window -r # renumber the windows
unbind , # unbind default binding for `rename-window`
bind -r , swap-window -t -1 # move window one position to the left
bind -r < swap-window -t -1 # move window one position to the left
unbind . # unbind default binding to move window to user provided index
bind -r . swap-window -t +1 # move window one position to the right
bind -r > swap-window -t +1 # move window one position to the right
unbind t # unbind default binding to show time
bind t swap-window -t 1 # swap the current window's position with window # 1, move it to the top
# ** Split Window
unbind & # unbind default binding for `split-window -h`
bind - split-window -v -c '#{pane_current_path}' # vertical split
bind _ split-window -v -c '#{pane_current_path}' -f # full vertical split (v2.3+)
bind \ split-window -h -c '#{pane_current_path}' # horizontal split
bind | split-window -h -c '#{pane_current_path}' -f # full horizontal split (v2.3+)
# https://www.reddit.com/r/tmux/comments/3paqoi/tmux_21_has_been_released/cw5wy00
bind w switch-client -Tsplit_wind
bind -Tsplit_wind v split-window -v -c '#{pane_current_path}'
bind -Tsplit_wind V split-window -v -c '#{pane_current_path}'\; swap-pane -U
bind -Tsplit_wind h split-window -h -c '#{pane_current_path}'
bind -Tsplit_wind H split-window -h -c '#{pane_current_path}'\; swap-pane -U
# ** Layout
bind Space next-layout
bind C-Space select-layout -o # undo only the last layout change #v2.1
# * Session Management
bind C-t command-prompt -p "New name for this session:" "rename-session '%%'"
bind b switch-client -l # switch to previously selected session
# switch to another session by name
bind S split-window "tmux ls | peco --initial-index `tmux ls | awk '/attached.$/ {print NR-1}'` | cut -d':' -f 1 | xargs tmux switch-client -t"
# switch to ANY window in ANY session by name
bind s split-window "tmux ls | cut -d: -f1 | xargs -I SESSION tmux lsw -F 'SESSION:#{window_name}' -t SESSION | peco --initial-index `tmux ls | cut -d: -f1 | xargs -I SESSION tmux lsw -F '___#{session_attached}#{window_active}___' -t SESSION | awk '/___11___/ {print NR-1}'` | xargs tmux switch-client -t"
# tmux kill-session -t NAME/SESSIONNUMBER # Kill session
# * Mouse
# setw -g mode-mouse on # incompatible in tmux 2.1+
set -g mouse on
# ** Drag pane border to resize
# set -g mouse-resize-pane off # incompatible in tmux 2.1+
bind -T root MouseDrag1Border resize-pane -M # default
# unbind -T root MouseDrag1Border # disable drag pane border to resize
bind -T root MouseDrag1Pane if -Ft= '#{mouse_any_flag}' 'if -Ft= "#{pane_in_mode}" "copy-mode -M" "send-keys -M"' 'copy-mode -M' # default
# ** Left click on pane
# set -g mouse-select-pane on # incompatible in tmux 2.1+
# Left click on a pane selects it
# bind -T root MouseDown1Pane select-pane -t=\; send-keys -M # default
bind -T root MouseDown1Pane select-pane -t=
# Sun Feb 19 11:31:34 EST 2017 - kmodi
# Below break in tmux 2.4
# # Fri Aug 26 18:35:21 EDT 2016 - kmodi
# # FIXME Need to remember why I unbound the below 2 bindings
# unbind -temacs-copy MouseDown1Pane
# unbind -temacs-copy MouseUp1Pane
# #
# https://groups.google.com/forum/#!topic/tmux-users/mHhdx7Au0Ds
# Fri Aug 26 18:30:15 EDT 2016 - kmodi
# Do not do the below!! That will update the primary selection with the top-most
# tmux buffer each time you left click on a pane.
# bind -T root MouseUp1Pane run -b "tmux show-buffer | xclip -i -sel pri"
#
# Left click in the pane *followed after a region selection* copies that to the
# secondary selection
bind -T root MouseUp1Pane run -b "tmux show-buffer | xclip -i -sel sec"
# Fri Aug 26 19:03:57 EDT 2016 - kmodi
# FIXME: As of today it needs to be figured out how to best paste the content
# from secondary selection
# ** Middle click on pane
# Middle click in a pane to paste from the primary selection
bind -T root MouseDown2Pane run -b "xclip -o -sel pri | tmux load-buffer - && tmux paste-buffer -s ' '"
# ** Right click on pane
# Right click on a pane selects and marks it *if not in copy-mode*; else
# passes on the mode keys
# bind -T root MouseDown3Pane select-pane -t= -m # default
bind -T root MouseDown3Pane if -Ft= '#{pane_in_mode}' 'send-keys -M' 'select-pane -t= -m'
# Sun Feb 19 11:32:00 EST 2017 - kmodi
# Below breaks in tmux 2.4
# # Right click *release* on a pane *in copy-mode* quits copy-mode
# bind -temacs-copy MouseUp3Pane cancel
# ** Wheel scroll in pane
unbind -T root WheelUpPane
unbind -T root WheelDownPane
# Do mouse wheel-up to enter copy mode and do page-up
# https://groups.google.com/d/msg/tmux-users/XTrSVUR15Zk/3iyJLMyQ7PwJ
# Below binding did not work
# bind -T root WheelUpPane if -Ft= '#{mouse_any_flag}' 'if -Ft= "#{pane_in_mode}" "copy-mode -u" "send-keys -M"' 'copy-mode -u'
# Below works and allows the WheelUpPane binding in emacs-copy table to be effective
bind -T root WheelUpPane if -Ft= '#{mouse_any_flag}' 'send-keys -M' 'if -Ft= "#{pane_in_mode}" "send-keys -M" "copy-mode -u"'
# |---------------------+-----------------------------------------+--------------------------------|
# | using mouse? AND .. | #{pane_in_mode} (already in copy-mode?) | action |
# |---------------------+-----------------------------------------+--------------------------------|
# | Yes | Don't care | Send the mode keys |
# | No | Yes | Send the mode keys |
# | No | No | Enable copy-mode and do PageUp |
# |---------------------+-----------------------------------------+--------------------------------|
# *** Wheel scroll in pane WHILE in copy-mode
# Sun Feb 19 11:32:16 EST 2017 - kmodi
# Below breaks in tmux 2.4
# # Once in copy-mode, mouse wheel scrolls scrolls by half pages
# bind -temacs-copy WheelUpPane halfpage-up
# bind -temacs-copy WheelDownPane halfpage-down
# ** Left click on status
# set -g mouse-select-window on # incompatible in tmux 2.1+
# Left click on a window name in status bar to select it
bind -T root MouseDown1Status select-window -t= # default
# ** Middle click on status
# Middle click on a window name in status bar to kill it
bind -T root MouseDown2Status kill-window
# ** Other mouse settings
# The special token {mouse} or = may be used as target-window or target-pane in
# commands bound to mouse key bindings. Example: -t =
# * Window Title
set -g set-titles on
set -g set-titles-string '#h :: #S:W#I(#W).P#P'
# * Status Bar
set -g status-interval 5 # default = 15 seconds
set -g status-justify centre
# ** Left Status
set -g status-left-length 20
# Change the left status when prefix is pressed.
# https://www.reddit.com/r/tmux/comments/5cm2ca/post_you_favourite_tmux_tricks_here/d9ziuy9/
set -g status-left "#{?client_prefix,#[fg=yellow]prefix pressed ..,[#S]}"
# ** Right Status
set -g status-right-length 20
set -g status-right "%l:%M %b %d %a "
# ** Pane Status
setw -g pane-border-status "bottom"
setw -g pane-border-format " #P #T "
# # tmux-powerline
# # https://github.com/erikw/tmux-powerline
# set -g status-left-length 30
# set -g status-right-length 30
# set -g status-left "#(~/usr_local/scripts/tmux-powerline/powerline.sh left)"
# set -g status-right "#(~/usr_local/scripts/tmux-powerline/powerline.sh right)"
# * Colors
# ** Status Bar Colors
set -g status-style fg=colour246,bg=colour233 # default for whole status line
set -g status-left-style fg=white,bold,bg=colour233
set -g status-right-style fg=colour75,none,bg=colour233
# ** Message Colors
set -g message-style fg=colour2,bold,bg=default
# ** Window Status Colors
setw -g window-status-style default # default for all window statuses
setw -g window-status-last-style fg=default,bg=colour235
setw -g window-status-current-style fg=white,bold,bg=colour63
setw -g window-status-bell-style default
setw -g window-status-activity-style fg=white,none,bg=colour196
# setw -g window-status-content-style fg=black,none,bg=green # incompatible with tmux 2.0+
# ** Pane Colors
setw -g pane-active-border-style fg=colour63,bg=default
setw -g pane-border-style fg=colour235,bg=default
setw -g window-active-style 'bg=#330000' # bg color of active pane
setw -g window-style 'bg=black' # bg color of inactive pane(s)
# ** Mode Info Colors
# Color of display shown on top-right in copy-mode, highlighting
setw -g mode-style fg=black,bg=colour244
# * Activity
# Notify when a window has activity
# This quick snippet will have tmux notify you in the status area when a
# window has activity:
setw -g monitor-activity on
set -g visual-activity off # Display message telling that an activity happened (on/off)
# It lets me know that there is activity in a non-active window
# To try this, enter `sleep 10 && echo “Hi”` in a window and switch to
# another window.
# # Notify when a window has a content alert
# setw -g monitor-content "--[A-Za-z][A-Za-z]sim Done--" # This string appears when a sim finishes, alert then # incompatible with tmux 2.0+
# # setw -g monitor-content "" # Disable monitor-content
# set -g visual-content on # Display message telling that a content alert was triggered (on/off) # incompatible with tmux 2.0+
# * Command Prompt
# Move focus to command prompt. tmux commands can be entered there directly
# without using the `tmux` prefix and it also supports auto-complete.
bind C-x command-prompt # default command-prompt binding "PREFIX :" also works
# * Audible and Visual Bells
set -g bell-action any
set -g bell-on-alert off
set -g visual-bell on
# * Copy & Paste
set -g set-clipboard off # default is on
# Copy tmux buffer to primary and clipboard selections
# run -b runs a shell command in background
# http://grota.github.io/blog/2012/05/08/tmux-clipboard-integration/
bind C-w run -b "tmux show-buffer | xclip -i -sel pri && tmux show-buffer | xclip -i -sel cli"
# Fri Aug 26 18:41:30 EDT 2016 - kmodi
# Below binding was suggested by Nicholas Marriott
# But the my older binding works fine so I am commenting out below for now.
# bind C-w run "tmux saveb - | xclip -i -sel pri; tmux saveb - | xclip -i -sel cli"
# Paste into tmux; also replace LF characters with
# space as separator characters (-s) when pasting.
# Yank from primary
bind C-y run -b "xclip -o -sel pri | tmux load-buffer - && tmux paste-buffer -s ' '"
# Yank from clipboard
bind M-y run -b "xclip -o -sel cli | tmux load-buffer - && tmux paste-buffer -s ' '"
# Open the file/dir path that was copied by selection in existing emacs client
# Usage: Highlight a file name in ls output and press "PREFIX e"
bind e run -b "tmux show-buffer | xclip -i -sel pri; (emacsclient -a '' `tmux display -p '#{pane_current_path}'`/`xclip -o -sel pri `&)"
# * Synchronize commands to panes/windows/sessions
# Send the same command to all panes in the same window
bind C-a command-prompt -p "Command to all panes in this window:" \
"run \"tmux list-panes -F '##{pane_index}' | xargs -I PANE \
tmux send-keys -t PANE '%1' Enter\""
# Alternative to using the above "C-a" binding is to enable pane synchronization,
# type the command you want to execute in all panes in the same window and disable
# pane synchronization
# Also turn the pane borders red while pane synchronization is enabled.
# - https://www.reddit.com/r/tmux/comments/5cm2ca/post_you_favourite_tmux_tricks_here/d9y6jzu/
bind C-s if -F '#{pane_synchronized}' \
'setw synchronize-panes off; \
setw pane-active-border-style fg=colour63,bg=default; \
setw pane-border-format " #P #T "' \
'setw synchronize-panes on; \
setw pane-active-border-style fg=red; \
setw pane-border-format " #P - Pane Synchronization ON "'
# So it would be: C-s <type the command RET> C-s
# https://scripter.co/command-to-every-pane-window-session-in-tmux/
# Send the same command to all panes/windows in the current session
bind C-e command-prompt -p "Command:" \
"run \"tmux list-panes -s -F '##{session_name}:##{window_index}.##{pane_index}' \
| xargs -I PANE tmux send-keys -t PANE '%1' Enter\""
# Send the same command to all panes/windows/sessions
bind E command-prompt -p "Command:" \
"run \"tmux list-panes -a -F '##{session_name}:##{window_index}.##{pane_index}' \
| xargs -I PANE tmux send-keys -t PANE '%1' Enter\""
# * Terminal Setting
# From `man tmux', about `default-terminal'
# Set the default terminal for new windows created in this session - the default
# value of the TERM environment variable. For tmux to work correctly, this must
# be set to screen, tmux or a derivative of them.
# set -g default-terminal "screen"
set -g default-terminal "screen-256color"
# Mon May 22 11:43:56 EDT 2017 - kmodi
# Blinking text (useful to show broken symlinks in ls) does not work when using tmux-24bits.
# set -g default-terminal "tmux-24bits"
# tmux-24bits is a custom terminfo profile created using the steps explained
# on https://github.com/ThomasAdam/tmux/blob/master/FAQ to support italics and
# 256 colors.
# Enable 24-bit color
# https://sunaku.github.io/tmux-24bit-color.html
set -ga terminal-overrides ",screen-256color:Tc"
# set -ga terminal-overrides ",tmux-24bits:Tc"
# Thu May 31 17:10:04 EDT 2018 - kmodi
# TODO: Try the 24-bit emacs+tmux config for ST
# https://www.reddit.com/r/emacs/comments/8ndm2x/gnu_emacs_261_24bit_colors_suckless_st_terminal/dzwh4vv/
# set -g default-terminal "tmux-256color"
# set -ga terminal-overrides ",*256col*:Tc"
#
setw -g xterm-keys on
# Uncomment below when using st (by suckless.org)
# set -g default-terminal "st-256color"
# # https://sunaku.github.io/tmux-24bit-color.html
# # st supports 24-bit color, so enable support for that in tmux
# set -ga terminal-overrides ",st-256color:Tc"
# setw -g xterm-keys off
bind R refresh-client
# bind R refresh-client \; display "Refreshed the client."
# * Other Options
# ** Server Options
set -s escape-time 0 # Allows for faster key repetition
# ** Session Options
# Set the default shell to /bin/sh. If the default is tcsh, doing a split-window takes a long
# time as my tcsh init is loaded first (which takes really long).
set -g default-shell /bin/sh
# If I am doing a new-window or split-window without a specified command, start the tcsh
# shell by default.
set -g default-command tcsh
set -g history-limit 100000
set -g display-time 1000 # Duration of tmux display messages in milliseconds
# ** Window Options
# When a smaller terminal connects to a tmux client, it shrinks to fit it. The
# clients attached with bigger displays see this constrained view.
# aggressive-resize makes it such that the window is only resized if the smaller
# client is actively looking at it.
setw -g aggressive-resize on
setw -g mode-keys emacs # Use emacs keybindings in copy mode
setw -g status-keys emacs
# * Notes
# |-------------------+------------|
# | tmux command | short form |
# |-------------------+------------|
# | set-option | set |
# | set-window-option | setw |
# | bind-key | bind |
# | unbind-key | unbind |
# | display-message | display |
# | run-shell | run |
# | if-shell | if |
# |-------------------+------------|
# Colo'u'r table
# http://guns.github.io/xterm-color-table.vim/images/xterm-color-table.png
# CHARACTER PAIR REPLACED WITH
# #(command) First line of commands output
# #[attributes] Colour or attribute change
# #H Hostname of local host
# #I Current window index
# #P Current pane index
# #S Session name
# #T Current window title
# #W Current window name
# ## A literal #
# Variables used in time format
# Source: http://docs.splunk.com/Documentation/Splunk/5.0.2/SearchReference/Commontimeformatvariables
# %y = year in numbers (2-digit)
# %Y = year in numbers (4-digit)
# %m = month in number (eg: 12)
# %B = full month name (eg: December)sho
# %b = short month name (eg: Dec)
# %d = day in numbers, with leading zeros (eg: 08)
# %e = day in numbers, no leading zeros (eg: 8)
# %A = full weekday name (eg: Sunday)
# %a = short weekday name (eg: Sun)
# %H = hours in 24-clock, with leading zeros
# %k = hours in 24-clock, no leading zeros
# %l = hours in 12-clock, with leading zeros
# %p = am/pm
# %T = time in 24-hour notation (%H:%M:%S)
# PREFIX ? : list-keys, display key bindings
# In command-prompt: show-options -g shows the global options
# In command-prompt: show-window-options -g shows the global windows options
# How do I know which tmux version I am running?
# tmux -V
# How to set bindings that don't need the prefix?
# bind -n .. or
# bind -T root ..
# Changelog: https://github.com/tmux/tmux/blob/master/CHANGES
# style colors: default, black, red, green, yellow, blue, magenta, cyan, white,
# colour0-colour255, hexdecimal RGB string '#ffffff'
# Use $SCRIPTS/bash/256-colors.sh to figure out the color number you want
# style attributes: none, bold/bright, dim, underscore, blink, reverse, hidden,
# or italics
# https://www.reddit.com/r/tmux/comments/3paqoi/tmux_21_has_been_released/cw552qd
# tmux buffers
# PREFIX # : List all paste buffers
# PREFIX - : Delete the most recently copied buffer of text
# PREFIX = : Choose which buffer to paste interactively from a list
# PREFIX ] : Paste the most recently copied buffer of text
# How to start a temporary tmux server in addition to an existing running one?
# > tmux -L temp
# In a shell environment in a terminal in tmux, the env var $TMUX will be
# defined to something like "/tmp/tmux-23273/default,31101,0". Outside tmux,
# $TMUX will be undefined.
# Notation to address a specific pane
# SESSION_NAME:WINDOW_INDEX.PANE_NUMBER (Example: foo:2.1 i.e. Pane 1 in Window 2 of Session foo)
# To print a message containing tmux variable values to stdout use '-p' option in display-message
# tmux display-message -p '#{session_name}:#{window_name}.#{pane_index}', or
# tmux display -p '#{session_name}:#{window_name}.#{pane_index}'

View File

@@ -0,0 +1,84 @@
# none of these attempts worked, to bind keys, except sometimes during the sesssion. Oh well.
# I thought maybe that was because F1 is handled differently in a console than in X, but
# even just C-1 didnt work. Using just "a" or "x" as the key did, but not yet sure why not "C-".
#bind-key -T root C-1 attach-session -t$0
#But this one works now, only picks the wrong one? Mbe need2understand what "$1" or $0 mean, better,
#but with the stub maybe this doesn't matter:
bind-key "`" switch-client -t$1
new-session #$0, stub, for keystroke convenience
#$1 for root
new-session #a stub I guess, where keyboard convenience is concerned
new-window
send-keys -l pgup
send-keys Enter
send-keys -l "less /root/.tmux.conf &"
send-keys -l "echo; echo; echo Put something here for ssa or just run manly?"
send-keys Enter
new-window
new-window
new-window sul #for lcall, like man pages
send-keys -l "man tmux&"
send-keys Enter
select-window -t :=1
#$2 for om, so, can do C-b C-s 2 to get to the session, then C-b <#s> to get ~"tabs"
new-session sula ; send-keys -l q #0
send-keys Enter Escape Escape
new-window sula ; send-keys -l q #1
send-keys Enter Escape Escape
new-window sula ; send-keys -l q #2
send-keys Enter Escape Enter Enter
new-window sula ; send-keys q #3, to start:
send-keys Enter Escape
# %%need a sleep here & .. ?
send-keys Enter Enter Enter Enter
new-window sula ; send-keys -l q #4
send-keys Enter Escape Escape
new-window sula
new-window sula
new-window sula
new-window sula
new-window sula
select-window -t :=2
select-window -t :=3
#$3 for email (mutt)
new-session sula
new-window sula ; send-keys mutt Enter
#nah, probly betr not?:
#send-keys -l z
#send-keys -l "thepassifdecide"
#send-keys Enter
new-window sula ; send-keys mutt Enter
send-keys -l "c!=sent"
send-keys Enter
new-window sula ; send-keys -l "cd mail/config; less mailsig.txt&"
send-keys Enter
send-keys "less macros&"
send-keys Enter
select-window -t :=1
#$4 for lacall-net: links etc
new-session suln
new-window suln
#send-keys -l "lkslfx" #; et; links ksl.com"
#send-keys asdafdfadfadfadfadf
#%%does opening links break subsequent cmds? With this Enter, the switch-client etc dont work:
#send-keys Enter
#send-keys Space Space Space
new-window suln
new-window suln
select-window -t :=1
#send-keys Space Space Space
#$5 for lacall-secnet, links?:
#new-session sulsn
# then, where to start:
#%%need a sleep here, or ck a debug thing?
switch-client -t"$0"
send-keys -l "sleep 2"
send-keys Enter
switch-client -t$2

View File

@@ -0,0 +1,108 @@
# Put the status bar on top
#set -g status-position "top"
# Basic colours, safer for dumb terminals.
#set -g status-style "bg=white,fg=black"
#set -g status-right-style "bg=green,fg=black"
#set -g window-status-current-style "bg=yellow,fg=black"
#set -g message-style "bg=white,fg=black"
#set -g window-status-activity-style "fg=blue"
#set -g window-status-bell-style "fg=red"
## Moar colours! Not recommended if attaching from dumber terminals with 8 or 16 colours.
#set -g default-terminal "tmux-256color"
# A more compatible XTERM var.
set -g default-terminal "screen-256color"
set -g message-style "bg=#485548 fg=#ffffff"
set -g pane-border-style "fg=#424954"
set -g pane-active-border-style "fg=#ffffff"
set -g status-style "bg=#424954 fg=#ffffff"
set -g status-right-style "bg=#303338 fg=colour87"
set -g window-status-current-style "bg=#303338"
set -g window-status-last-style "bg=#364146"
set -g window-status-format ' #I:#W#[fg=colour201]#F '
set -g window-status-current-format ' #[fg=colour226]#I#[fg=#ffffff]:#[fg=colour119]#W#[fg=colour202]#F '
set -g window-status-separator ""
# Uncomment and reload settings for sanity in a console with 8 colours.
#set -g status-style "bg=white,fg=black"
#set -g window-status-last-style "bg=white"
# Might help when graphical characters used for vertical and horizontal lines are drawn as x and q.
#set-option -ga terminal-overrides ',*:enacs@:smacs@:rmacs@:acsc@'
# Count panes starting from 1.
set -g base-index 1
# With this you set the window name in the status line.
# Beware of outrageous prompts, such as the default one in RHEL 7.
set -g set-titles on
# Let status right consists of only the pane title (removes date and time).
# Usually shows current path.
set -g status-right ' #T '
# Increase the default length of 40.
set -g status-right-length 80
# Scroll up with the mouse.
set -g mouse
# Clipboard integration, use this in tandem with the recommended xterm settings.
set -g set-clipboard on
# Pass through modifier keys, xterm style. You'll want this in vim.
set -g xterm-keys on
# Reduce time to wait for Escape key. You'll want this for neovim.
set-option escape-time 40
# Leave ESC alone...
#set-option -s escape-time 0
# New-style mouse scroll (>2.1)
bind -n WheelUpPane select-pane -t= \; copy-mode -e \; send-keys -M
bind -n WheelDownPane select-pane -t= \; send-keys -M
# This is for scrolling up with the terminal using keys, but has issues...
#set -ga terminal-overrides ',xterm*:smcup@:rmcup@'
# 10x more history.
set -g history-limit 20000
# Swap the default Control-b with Control-s which usually stops the output in a shell.
unbind C-b
set-option -g terminal-overrides "xterm-rightclick:krightclick=^[[29~"
set -g prefix C-s
bind C-s send-prefix
# For renumbering windows when you get gaps in numbering.
bind R \
move-window -r\; \
display-message "Windows reordered..."
# My shortcuts.
#bind-key -n C-S-t new-window # Doesn't work :-/
bind-key -n C-t new-window
bind-key -n C-PgUp prev
bind-key -n C-PgDn next
#bind-key -n C-S-PgUp swap-window -t -1 # Doesn't work :-/
#bind-key -n C-S-PgDn swap-window -t +1 # Doesn't work :-/
bind-key -n C-S-Left swap-window -t -1
bind-key -n C-S-Right swap-window -t +1
bind-key -n M-` select-window -t 0
bind-key -n M-1 select-window -t 1
bind-key -n M-2 select-window -t 2
bind-key -n M-3 select-window -t 3
bind-key -n M-4 select-window -t 4
bind-key -n M-5 select-window -t 5
bind-key -n M-6 select-window -t 6
bind-key -n M-7 select-window -t 7
bind-key -n M-8 select-window -t 8
bind-key -n M-9 select-window -t 9
bind-key -n M-0 select-window -t 10
# switch panes without prefix using Alt-arrow
bind -n M-Left select-pane -L
bind -n M-Right select-pane -R
bind -n M-Up select-pane -U
bind -n M-Down select-pane -D
# join pane from inputted window (horizontally or vertically)
#bind-key @ command-prompt -p "join pane from:" "join-pane -s ':%%' -h"
bind-key @ command-prompt -p "join pane from:" "join-pane -s ':%%' -v"

View File

@@ -0,0 +1,198 @@
### GENERAL
set-option -g prefix C-a # Set prefix to <C-a>
bind a send-prefix # Send <C-a> with <C-a>a
bind R source-file ~/.tmux.conf \; display "~/.tmux.conf reloaded"
bind -n M-R source-file ~/.tmux.conf \; display "~/.tmux.conf reloaded"
set -g history-limit 10000 # lines to keep in hisoty
set-option -g display-panes-time 3000 # Timeout for pane-numbering in ms
bind -n M-q display-panes
set-option -sg escape-time 0 # speed up commands
set -g mouse on # enable mouse (tmux 2.1+)
set -g base-index 1 # start window numbering at 1
set -g pane-base-index 1 # start pane numbering at 1
set -g renumber-windows on # renumber windows automatically
setw -g automatic-rename on # rename window after process
# Clear window name before renaming
bind , rename-window "" \; command-prompt "rename-window '%%'"
#### APPEARANCE
set -g default-terminal "screen-256color" # use 256 colors
setw -g aggressive-resize on # resize window to smallest client
set -g pane-border-style fg=colour238 # border color for inactive panes
set -g pane-active-border-style fg=colour247 # border color for active panes
# Status bar colors and format
setw -g window-status-format ' #[fg=#999999]#I #[fg=$666666]#W '
setw -g window-status-current-format '#[fg=#ffffff] #I #W#[fg=#ffffff] '
setw -g window-status-separator '#[fg=#292929]|#[fg=default]'
set -g status-bg default # background color for status bar
set -g status-position bottom # put status bar on top or bottom
set -g status-interval 2 # interval in s to update status
set -g status-justify left # horizontal alignment
set -g message-style fg=white,bg=black # appearance of status messages
set -g message-command-style fg=white # appearance of status message cmds
# Left section of status bar
set -g status-left ""
# Status bar visibility
set -g status off
bind -r -n M-t set status on
bind -r -n M-T set status off
bind t set status on
bind T set status off
# Right section of status bar
if-shell 'uname | grep -qi Darwin' "set -g status-right \"#[fg=#81a2be]#(/usr/local/bin/mpc | head -n 1 | sed 's/volume.*$//') #[fg=cyan]#(~/bin/battery-osx) #(~/bin/mailstatus.sh) #[fg=yellow]#(uptime|sed 's/.* //') #[fg=#666666]%F #[fg=#bababa]%R\""
if-shell 'uname | grep -qi Linux' "set -g status-right \"#[fg=cyan]#(~/bin/battery-linux) #(~/bin/mailstatus.sh) #[fg=yellow]#(cat /proc/loadavg|awk '{print $1;}') #[fg=#666666]%F #[fg=#bababa]%R\""
# Scaling of status-bar sections
set -g status-right-length 40
#### NAVIGATION
# With C-a prefix
bind h select-pane -L # navigate left with <C-a>h
bind j select-pane -D # navigate down with <C-a>j
bind k select-pane -U # navigate up with <C-a>k
bind l select-pane -R # navigate right with <C-a>l
bind -r H resize-pane -L 5 # resize pane left with <C-a>H
bind -r J resize-pane -D 5 # resize pane down with <C-a>J
bind -r K resize-pane -U 5 # resize pane up with <C-a>K
bind -r L resize-pane -R 5 # resize pane right with <C-a>L
# Navigate panes with Meta (alt) modifier + hjkl
bind -r -n M-h select-pane -L # navigate left with M-h
bind -r -n M-j select-pane -D # navigate down with M-j
bind -r -n M-k select-pane -U # navigate up with M-k
bind -r -n M-l select-pane -R # navigate right with M-l
bind -r -n M-H resize-pane -L 5 # resize pane left with M-H
bind -r -n M-J resize-pane -D 5 # resize pane down with M-J
bind -r -n M-K resize-pane -U 5 # resize pane up with M-K
bind -r -n M-L resize-pane -R 5 # resize pane right with M-L
# Navigate windows with Meta (alt) modifier + number keys
bind -n M-1 select-window -t :=1
bind -n M-2 select-window -t :=2
bind -n M-3 select-window -t :=3
bind -n M-4 select-window -t :=4
bind -n M-5 select-window -t :=5
bind -n M-6 select-window -t :=6
bind -n M-7 select-window -t :=7
bind -n M-8 select-window -t :=8
bind -n M-9 select-window -t :=9
bind -n M-0 select-window -t :=10
bind C-s last-window # go to last window with <C-a><C-s>
bind C-a last-pane # go to last pane with <C-a><C-a>
bind -n M-s last-window # go to last pane with M-s
bind -n M-a last-pane # go to last pane with M-a
bind -r n next-window # next window with <C-a>n
bind -r b previous-window # next window with <C-a>p
bind -n -r M-n next-window # next window with <M-n>
bind -n -r M-b previous-window # previous window with <M-n>
#bind -n M-, run-shell "tmux list-panes -as -F \"##I.##P ##{pane_current_command} . #{pane_current_path} (#W)#F\" | fzf-tmux | cut -d \" \" -f 1 | xargs tmux select-pane -t"
bind -n M-, run-shell "tmux list-windows -F \"##I:##W\" | fzf-tmux | cut -d \":\" -f 1 | xargs tmux select-window -t"
bind -n M-. run-shell "tmux list-sessions -F \"##S\" | fzf-tmux | xargs tmux switch -t"
#### LAYOUT CHANGING BINDINGS
# create panes in same directory
bind '"' split-window -c "#{pane_current_path}"
bind '%' split-window -h -c "#{pane_current_path}"
bind -r z resize-pane -Z # toggle pane zoom with <C-a>z
bind -r y next-layout # cycle to next pane layout with <C-a>y
bind -r Y previous-layout # cycle to previous pane layout with <C-a>Y
bind -r r rotate-window # rotate panes with <C-a>r
bind -n M-z resize-pane -Z # toggle pane zoom with <M-z>
bind -n -r M-y next-layout # cycle to next pane layout with <M-y>
bind -n -r M-Y previous-layout # cycle to previous pane layout with <M-Y>
bind -n -r M-r rotate-window # rotate panes with <M-r>
bind -r Left swap-window -t -1 # Swap window left
bind -r Right swap-window -t +1 # Swap window right
bind -r B swap-window -t -1 # Swap window left
bind -r N swap-window -t +1 # Swap window right
bind -n -r M-B swap-window -t -1 # Swap window left
bind -n -r M-N swap-window -t +1 # Swap window right
#### CLIPBOARD
# enable reattach-to-user-namespace which fixes pasteboard access and launchctl
bind Space copy-mode # enter copy mode with <C-a><Space>
bind -n M-u copy-mode # enter copy mode with M-u
bind -T copy-mode-vi M-u send -X halfpage-up # scroll up with M-u
bind -T copy-mode-vi M-d send -X halfpage-down # scroll down with M-d
bind -T copy-mode-vi v send -X begin-selection # start "visual" with v
# Copy (yank) with y
if-shell 'uname | grep -qi Linux && which xclip > /dev/null' 'bind -T copy-mode-vi y send-keys -X copy-pipe-and-cancel "DISPLAY=:0 xclip -i -sel clipboard"'
if-shell 'uname | grep -qi Darwin' 'bind -T copy-mode-vi y send-keys -X copy-pipe-and-cancel "pbcopy"'
if-shell 'uname | grep -qi Cygwin' 'bind -T copy-mode-vi y send-keys -X copy-pipe-and-cancel "cat > /dev/clipboard"'
# Paste with C-a p or M-p
if-shell 'uname | grep -qi Linux && which xclip > /dev/null' 'bind p run "DISPLAY=:0 xclip -o | tmux load-buffer - ; tmux paste-buffer"'
if-shell 'uname | grep -qi Darwin && which reattach-to-user-namespace > /dev/null' 'bind p run "pbpaste | tmux load-buffer - ; tmux paste-buffer"'
if-shell 'uname | grep -qi Darwin' 'bind -n M-p run "pbpaste | tmux load-buffer - ; tmux paste-buffer"'
if-shell 'uname | grep -qi Cygwin' 'bind p run "cat /dev/clipboard | tmux load-buffer - ; tmux paste-buffer"'
if-shell 'uname | grep -qi Cygwin' 'bind -n M-p run "cat /dev/clipboard | tmux load-buffer - ; tmux paste-buffer"'
#### LAUNCH PROCESSES
# use urlview to follow URLs in current pane
bind u capture-pane -J \; \
save-buffer "/tmp/active_tmux_buffer" \; \
delete-buffer \; \
split-window -l 10 "urlview '/tmp/active_tmux_buffer' && rm /tmp/active_tmux_buffer"
# Launch offlineimap in inactive splits
bind o split-window -p 25 '$SHELL -c "offlineimap -qf INBOX"' \; select-pane -l
bind O split-window -p 25 '$SHELL -c "offlineimap"' \; select-pane -l
# Use nested bindings (<C-a>l) for grouping process launch bindings
bind -n C-M-v new-window -n vim "/usr/local/bin/vim"
bind -n C-M-w new-window -n weather \
"curl 'wttr.in/?m'; echo -e '\nPress <enter> to quit'; read -n 1 -s"
# Open new window and resize status accordingly (should be a hook instead)
bind Enter new-window -c "#{pane_current_path}" "$SHELL"
bind -n M-Enter new-window \
"tmux set status-right-length `echo $(tput cols)/2|bc|tr -d '\n'`; zsh"
# Use nested bindings (<C-a>m) for grouping music-control bindings
bind m switchc -Tmpd
bind -n M-m switchc -Tmpd
bind -Tmpd v new-window -n vimpc "vimpc"
bind -Tmpd p display "#(mpc toggle | tr '\n' ' ')"
bind -Tmpd s display "#(mpc stop | tr '\n' ' ')"
bind -Tmpd n display "#(mpc next | tr '\n' ' ')"
bind -Tmpd b display "#(mpc prev | tr '\n' ' ')"
bind -Tmpd r display "#(mpc clear && mpc ls | mpc add && mpc random on && mpc play | tr '\n' ' ')"
#bind -Tmpd r new-window -n mpc "mpc clear && mpc ls | mpc add && mpc shuffle && mpc play"
bind -n C-M-p display "#(mpc toggle | tr '\n' ' ')"
bind -n C-M-s display "#(mpc stop | tr '\n' ' ')"
bind -n C-M-n display "#(mpc next | tr '\n' ' ')"
bind -n C-M-b display "#(mpc prev | tr '\n' ' ')"
#bind -n C-M-r display "#(mpc clear && mpc ls | mpc add && mpc random on && mpc play | tr '\n' ' ')"
bind -n C-M-a split-window -p 50 "source ~/code/fzf-mpd/fzf-mpd.zsh && fm"
# fzf-locate from entire file system and insert result in current pane (Alt-`)
bind -n 'M-`' run "tmux split-window -p 40 'tmux send-keys -t #{pane_id} \"$(locate / | fzf -m | paste -sd\\ -)\"'"
# Change to the previous pane, repeat the last command, change back
bind -n M-! last-pane \; send-keys C-p C-m \; last-pane

View File

@@ -0,0 +1,148 @@
set-option -g prefix C-a
unbind-key C-b
bind-key C-a send-prefix
set-option -s set-clipboard on
set -sg escape-time 0
set -g bell-action other
set -g lock-after-time 1800
set -g lock-command 'tput civis && read -s -n1'
set -g history-limit 10000
set -g default-terminal "screen-256color"
set -g pane-border-style fg=white,bg=default
set -g pane-active-border-style fg=red,bg=default
set -g repeat-time 100
set -g terminal-overrides "xterm*:kLFT5=\eOD:kRIT5=\eOC:kUP5=\eOA:kDN5=\eOB:smkx@:rmkx@:Tc"
#set -g terminal-overrides '*88col*:colors=88,*256col*:colors=256,rxvt-uni*:Tc:XT:Ms=\E]52;%p1%s;%p2%s\007:Cc=\E]12;%p1%s\007:Cr=\E]12;green\007:Cs=\E]777;Cs;%p1%d\007'
set -g mouse on
set -g status-style bg=blue,fg=cyan
set-hook -g alert-bell 'run -b "notify-send \"Bell in session #{session_name}:#{window_index}:#{window_name}\""'
unbind-key /
unbind-key c
unbind-key d
unbind-key f
unbind-key i
unbind-key l
unbind-key n
unbind-key o
unbind-key p
unbind-key r
unbind-key s
unbind-key t
unbind-key w
unbind-key x
unbind-key |
unbind-key -
unbind-key A
unbind-key S
unbind-key .
unbind-key "'"
unbind-key '#'
unbind-key ' '
unbind-key z
unbind-key ^z
bind a send-prefix
bind c new-window -a -c '#{pane_current_path}'
bind d detach-client
bind "/" command-prompt "find-window '%%'"
bind i display-message
bind a last-window
bind n next-window
bind o select-pane -D
bind p previous-window
bind r respawn-window
bind s choose-tree -Z
bind t clock-mode
bind w choose-window
bind k confirm-before kill-pane
bind x set lock-command '/usr/bin/vlock' \; lock-client \; set lock-command 'tput civis && read -s -n1'
bind "|" split-window -v -c '#{pane_current_path}'
bind "-" split-window -h -c '#{pane_current_path}'
bind l command-prompt "rename-window '%%'"
bind S command-prompt "rename-session '%%'"
bind . display-panes
bind "'" command-prompt -p "SSH: " "new-window -n %1 'ssh %1'"
bind ' ' choose-window
bind z resize-pane -Z
bind ^a last-window
bind ^c new-window -a -c '#{pane_current_path}'
bind ^d detach-client
bind ^i display-message
bind a last-window
bind ^n next-window
bind ^o select-pane -D
bind ^p previous-window
bind ^r respawn-window
bind ^s choose-session
bind ^t clock-mode
bind ^w choose-window
bind ^k confirm-before kill-pane
bind ^x lock-client
bind ^S command-prompt "rename-session '%%'"
bind ^z resize-pane -Z
bind -n C-Left previous-window
bind -n C-Right next-window
bind -n C-s set status
bind -r C-Left swapw -t:-
bind -r C-Right swapw -t:+
# Status stuff.
set -g status-left-style "fg=white, bg=magenta"
set -g status-left-length 30
set -g status-left "#S "
set -g status-right-length 30
set -g status-right-fg white
set -g status-right-bg blue
set -g status-right "#{?client_prefix,#[reverse][^a]#[noreverse],}[%a %d/%m %H:%M]"
set -g display-panes-time 4000
set -g window-status-bell-style reverse
#setw -g window-status-current-fg white
#setw -g window-status-current-bg colour34
setw -g mode-keys vi
setw -g window-status-separator "| "
#setw -g window-status-format "#[bg=blue]#I:#W:#{window_flags}#[bg=default]"
#setw -g window-status-current-format "#[fg=black,bg=green]#I:#W:#{window_flags}"
setw -g window-status-format "#[bg=blue]#I:#W:#{?window_linked,+#{window_flags},#{window_flags} }#[bg=default]"
setw -g window-status-current-format "#[fg=black,bg=green]#I:#W:#{?window_linked,+#{window_flags},#{window_flags}}"
set-window-option -g clock-mode-colour green
# Sessions
new -d -sspecial
new -d -swork -d -nmutt 'exec neomutt'
neww -d
neww -d
neww -d
neww -d
# FIXME -- the entire block below is required for taskwarrior.
#new -d -stask -ntask -x237 -y 79
#selectl -ttask tiled
#set -ttask status off
#splitw -ttask:task
#splitw -ttask:task
#splitw -ttask:task
#splitw -ttask:task
#splitw -ttask:task
#selectl -ttask:task 4ada,237x79,0,0[237x67,0,0{156x67,0,0,5,80x67,157,0[80x27,157,0,19,80x22,157,28,20,80x16,157,51,21]},237x11,0,68,22]
#send -ttask:task.0 'cyclenext list' 'C-m'
#send -ttask:task.1 'clear ; tasksh' 'C-m'
#send -ttask:task.2 'cyclenext summary' 'C-m'
#send -ttask:task.3 'cyclenext burndown.daily' 'C-m'
#send -ttask:task.4 'cyclenext ghistory.monthly' 'C-m'
#selectp -ttask:task.1
#linkw -stask:task -twork
#set -t task:task remain-on-exit on
set -t work:irc remain-on-exit on
set -t work:mutt remain-on-exit on

View File

@@ -0,0 +1,52 @@
set -as terminal-overrides '\e\r\n\t\u12ab\U000012ab'
set -as terminal-overrides "\e\r\n\t\u12ab\U000012ab"
# format #{abc #{def}}
# abc
set -g status-left \
"\u007c \
abc"
%if #{TMUX}
set -g status-bg red
%endif
X=1
Y=2 set -g status-bg blue; Z=3 set -g status-bg magenta
set -g status-left "~/abcdef"$HOME # abcdef
%if #{l:1} set -g status-bg red %endif
%if #{l:0}
X=1
%elif #{l:1}
Y=1
%if #{l:0}
Y=2
%else
Y=3
%endif
%endif
bind x display-message \"hello\"
bind c neww -c ~
bind ';' lsk
set -g status-left "a""b"
set -g status-left ~
set -g status-left 'a $HOME b ~ c \e\e\e'
set -g status-left "a $HOME b ~ c \e\e\e"
set -s command-alias[99] "foo=lsk;neww"
bind-key -n C-s if-shell 'true' 'display-message hello'
set -g status-left-style \
bg=red
set -g status-left \\\
abc
set -g status-left 'xyz' ; %if #{l:1} set -g status-bg red %endif ; bind x lsk

View File

@@ -0,0 +1,78 @@
# Scott Rochford's tmux configuration
#
# change the prefix to the GNU screen default (avoids clash with page up in vi)
set -g prefix C-a
unbind-key C-b
bind-key C-a send-prefix
# toggle sending input to all panes
bind-key b set-window-option synchronize-panes
# alternative to ',' which doesn't pre-fill the prompt with the existing name
bind-key < command-prompt "rename-window '%%'"
# Disabled all of these in favour of changing 'default-command' below.
#bind-key C-p pipe-pane -o 'cat >>~/tmux_logs/output.$(echo #I-#P-#W-#T | sed "s/[^[:alnum:].-]/_/g")' \; display-message 'Toggled logging'
# From http://unix.stackexchange.com/questions/5832/is-there-an-equivalent-of-gnu-screens-log-command-in-tmux
# bind-key H pipe-pane -o "exec cat >>$HOME/'#W-tmux.log'" \; display-message 'Toggled logging to $HOME/#W-tmux.log'
#bind-key H pipe-pane "exec cat >>$HOME/'#W-tmux.log'" \; display-message 'Started logging to $HOME/#W-tmux.log'
#bind-key h pipe-pane \; display-message 'Ended logging to $HOME/#W-tmux.log'
#set -g utf8 on
set-option -g history-limit 32768
# no longer available in 2.2
#set-option -g mouse-select-pane on
#set-option -g mouse-select-window on
set-option -g mouse on
# increase the amount of time status bar messages are displayed for (default 1000 I think)
set-option -g display-time 1500
# unfortunately this seems to have no effect in putty :-(
set-option -g set-clipboard on
set-option -g default-command 'tmux pipe-pane -o "cat >>~/tmux_logs/output-`date +%Y%m%d-%H%M%S-$$`" ; /bin/ksh -l'
#
# allow yank into system clipboard
# from http://stackoverflow.com/questions/17255031/how-to-copy-from-tmux-running-in-putty-to-windows-clipbard
#
# for some reason this is wrapping at 80 cols, using save- instead of show- helps
# -b for background is needed because xclip continues to run to service the clipboard paste reqeusts until the
# clipboard buffer is replaced with some new contents
#bind C-y run-shell -b "tmux save-buffer - | DISPLAY=$(<~/.xdisplay) xclip -selection clipboard -in && tmux display-message 'xclipped successfully'"
bind C-y save-buffer ~/etc/clipboard.pipe
#
# this was just for testing, but interestingly for some reason tmux-show-buffer >/tmp/t never terminates, writing to a pipe works fine??
#bind C-z run-shell "tmux show-buffer | cat >/tmp/t"
# move x clipboard into tmux paste buffer
#bind C-p run-shell -b "xclip -o -selection clipboard | tmux load-buffer - ; tmux paste-buffer"
bind C-p run-shell "DISPLAY=$(<~/.xdisplay) xclip -o -selection clipboard | tmux load-buffer - ; tmux paste-buffer"
# switch to last-but-one window (like prefix-l but last, last)
# only works on tmux-2.4 + with Nicholas Marriott's patch from my feature request, unless it reached mainline....
#bind k run-shell "tmux select-window -t $(tmux list-windows -F '#{session_stack}' | awk -F, '{print $3;exit}END{print $1}')"
bind k run-shell "tmux select-window -t $(echo #{session_stack} | awk -F, '{w=$1}NF>=3{w=$3;exit}END{print w}')"
# switch to oldest window (for clean-up), not sure why brackets are required around (NF) here...
bind K run-shell "tmux select-window -t $(echo #{session_stack} | awk -F, '{print $(NF)}')"
# prompt for hosts to connect to, open a new synchronized window with horizontally split panes for each host, supports brace expansion
bind N command-prompt -p hosts: 'run-shell -b "bash -c \"~/lbin/nw %% >/dev/null\""'
# seems to cause unexpected resizes when focussing on putty :-(
#set-option mouse-resize-pane on
#05:59 < Celti> annihilannic: I believe the #{pane_in_mode} format does what you want
#05:59 < Celti> put it in your statusline
#05:59 < Celti> annihilannic: No, my mistake, I should have read farther down, you want #{pane_synchronized}
# only works in tmux 2.0?, higher than 1.6.3 anyawy
set-option -g window-status-format ' #I:#W#F#{?pane_synchronized,S,}'
#set-option -g window-status-current-format ' #I:#W#{?pane_synchronized,[sync],}#F'
# to highlight in red when sync is on... not sure why I did this with set-window-option instead of set-option, perhaps
# both work?
set-window-option -g window-status-current-format "#{?pane_synchronized,#[bg=red],}#{?window_zoomed_flag,#[bg=yellow],} #I:#W#F#{?pane_synchronized,S,}"
#
# also only in 2.0? if I use this, don't need #F in window-status-*-format? - actually, nah,
# still useful for showing [Z]oomed, or - last active, etc.
set-option -g window-status-current-style bg=blue
# Toggle input on a pane (from Thomas Sattler)
bind-key R if -F '#{pane_input_off}' "select-pane -e; select-pane -P fg=default" "select-pane -d; select-pane -P fg=yellow"

View File

@@ -0,0 +1,283 @@
#---------------------------------------------------------------------------#
# .tmux.conf
# Helmut K. C. Tessarek, Last update 2018-10-16
#---------------------------------------------------------------------------#
#---------------------------------------------------------------------------#
# set prefix key to ctrl+a / ctrl-b is used in vi for going back one page
#---------------------------------------------------------------------------#
unbind C-b
set -g prefix C-a
#---------------------------------------------------------------------------#
# send the prefix to client inside window (nested sessions)
#---------------------------------------------------------------------------#
bind-key a send-prefix
#---------------------------------------------------------------------------#
# toggle last window like screen
#---------------------------------------------------------------------------#
bind-key C-a last-window
#---------------------------------------------------------------------------#
# start window indexing at one instead of zero
#---------------------------------------------------------------------------#
#set -g base-index 1
#---------------------------------------------------------------------------#
# default terminal - we want 256 colors !!!
#---------------------------------------------------------------------------#
set -g default-terminal "screen-256color"
#---------------------------------------------------------------------------#
# on-screen time for status messages in ms
#---------------------------------------------------------------------------#
set -g display-time 2000
#---------------------------------------------------------------------------#
# on-screen time for display-panes in ms
#---------------------------------------------------------------------------#
set -g display-panes-time 2000
#---------------------------------------------------------------------------#
# color for display pane indicator
#---------------------------------------------------------------------------#
set -g display-panes-colour "cyan"
#set -g display-panes-active-colour "#0087ff"
#set -g display-panes-active-colour "red"
#---------------------------------------------------------------------------#
# open a man page in new window
#---------------------------------------------------------------------------#
unbind m
bind m command-prompt "split-window 'exec man %%'"
#---------------------------------------------------------------------------#
# quick view of processes
#---------------------------------------------------------------------------#
#bind '~' split-window "exec htop"
#---------------------------------------------------------------------------#
# scrollback buffer n lines
#---------------------------------------------------------------------------#
set -g history-limit 5000
#---------------------------------------------------------------------------#
# toggle status bar
#---------------------------------------------------------------------------#
unbind b
bind-key b set-option status
#---------------------------------------------------------------------------#
# resize panes like vim
# feel free to change the "1" to however many lines you want to resize by,
# only one at a time can be slow
#---------------------------------------------------------------------------#
unbind <
unbind >
unbind -
unbind +
bind -r < resize-pane -L 1
bind -r > resize-pane -R 1
bind -r - resize-pane -D 1
bind -r + resize-pane -U 1
#---------------------------------------------------------------------------#
# toggle mouse helpers
#---------------------------------------------------------------------------#
unbind Enter
unbind C-m
bind C-m set-option mouse \; display-message 'mouse -> #{?mouse,on,off}'
#---------------------------------------------------------------------------#
# Reload config file
#---------------------------------------------------------------------------#
unbind R
bind-key R source-file ~/.tmux.conf \; display-message "Reloading configuration done"
#---------------------------------------------------------------------------#
# start ssh session in new window
#---------------------------------------------------------------------------#
unbind S
bind-key S command-prompt "new-window -n %1 'ssh %1'"
#---------------------------------------------------------------------------#
# start new session
#---------------------------------------------------------------------------#
unbind C
bind-key C command-prompt "new-session -s %1"
#---------------------------------------------------------------------------#
# Keys to switch session
#---------------------------------------------------------------------------#
bind Q switchc -t0
bind W switchc -t compile
bind E switchc -t config
#---------------------------------------------------------------------------#
# break pane in background
#---------------------------------------------------------------------------#
unbind '@'
bind '@' break-pane -d
#---------------------------------------------------------------------------#
# join pane with target window
#---------------------------------------------------------------------------#
unbind ^
bind ^ command-prompt "join-pane -t %1"
#---------------------------------------------------------------------------#
# move around panes with hjkl, as one would in vim after pressing ctrl-w
#---------------------------------------------------------------------------#
#bind h select-pane -L
#bind j select-pane -D
#bind k select-pane -U
#bind l select-pane -R
#---------------------------------------------------------------------------#
# bind : to command-prompt like vim
# this is the default in tmux already
#---------------------------------------------------------------------------#
bind : command-prompt
#---------------------------------------------------------------------------#
# Remain on exit
#---------------------------------------------------------------------------#
#setw -g remain-on-exit on
#---------------------------------------------------------------------------#
# vi-style controls for copy mode
#---------------------------------------------------------------------------#
setw -g mode-keys vi
#---------------------------------------------------------------------------#
# Make mouse useful in copy mode
#---------------------------------------------------------------------------#
#setw -g mode-mouse on
#---------------------------------------------------------------------------#
# More straight forward key bindings for splitting
#---------------------------------------------------------------------------#
unbind %
unbind v
#bind | split-window -h
bind v split-window -h
unbind '"'
unbind h
#bind - split-window -v
bind h split-window -v
#---------------------------------------------------------------------------#
# Synchronize panes
#---------------------------------------------------------------------------#
unbind y
bind y set-window-option synchronize-panes \; display-message 'synchronize-panes -> #{?synchronize-panes,on,off}'
#---------------------------------------------------------------------------#
# Other key codes: Tab, BTab, Escape
#---------------------------------------------------------------------------#
#---------------------------------------------------------------------------#
# Clock
#---------------------------------------------------------------------------#
setw -g clock-mode-colour green
setw -g clock-mode-style 24
#---------------------------------------------------------------------------#
# Terminal emulator window title
#---------------------------------------------------------------------------#
set -g set-titles on
set -g set-titles-string '#S:#I.#P #W'
#---------------------------------------------------------------------------#
# Status Bar
#---------------------------------------------------------------------------#
set -g status-bg black
set -g status-fg white
set -g status-interval 1
set -g status-left-length 30
set -g status-left '#[fg=green]#h#[default] '
#set -g status-right '#[fg=yellow]#(cut -d " " -f 1-4 /proc/loadavg)#[default] #[fg=cyan,bold]%Y-%m-%d %H:%M:%S#[default]'
#set -g status-right '#[fg=yellow,bold]%Y-%m-%d %H:%M#[default]'
set -g status-right '#[fg=yellow]%Y-%m-%d %H:%M %Z#[default]'
#set -g status-justify center
#set -g status-keys vi
set -g allow-rename off
setw -g automatic-rename on
#---------------------------------------------------------------------------#
# Highlighting the active window in status bar
#---------------------------------------------------------------------------#
#setw -g window-status-current-bg red
set-option -g window-status-format "#I:#W#F#{?pane_synchronized,S,}"
set-window-option -g window-status-current-format "#{?pane_synchronized,#[bg=red],}#{?window_zoomed_flag,#[bg=colour130],}#I:#W#F#{?pane_synchronized,S,}"
set-option -g window-status-current-style bg=blue
#---------------------------------------------------------------------------#
# global update environment
#---------------------------------------------------------------------------#
set -g update-environment "DISPLAY SSH_ASKPASS SSH_AUTH_SOCK SSH_AGENT_PID SSH_CONNECTION WINDOWID XAUTHORITY TZ"
#---------------------------------------------------------------------------#
# settings for AIX
# terminal overrides to enable colors
# set default terminal to vt100 or xterm (screen does not exist on AIX)
#---------------------------------------------------------------------------#
if-shell "uname|grep AIX" 'set -g terminal-overrides "xterm*:XT,xterm*:setab=\\E[4%p1%dm,xterm*:setaf=\\E[3%p1%dm"'
#if-shell "uname|grep AIX" "set -g default-terminal vt100"
if-shell "uname|grep AIX" "set -g default-terminal xterm"
#---------------------------------------------------------------------------#
# settings for macOS
#---------------------------------------------------------------------------#
if-shell "uname|grep Darwin" 'set -g default-command "/bin/bash -l"'
#---------------------------------------------------------------------------#
# Pane coloring
# set inactive/active window styles
#---------------------------------------------------------------------------#
set -g window-style "fg=colour247,bg=colour234"
set -g window-active-style "fg=colour250,bg=black"
set -g @TPCS "1"
#---------------------------------------------------------------------------#
# pane border - different style / use cyan
#---------------------------------------------------------------------------#
#set -g pane-border-bg colour235
#set -g pane-border-fg colour238
#set -g pane-active-border-bg colour234
#set -g pane-active-border-fg colour51
#---------------------------------------------------------------------------#
# toggle pane coloring on/off
#---------------------------------------------------------------------------#
unbind C-b
bind C-b if -F '#{@TPCS}' \
'set -g window-style "fg=default,bg=default" ; set -g window-active-style "fg=default,bg=default" ; set -g @TPCS "0"; display-message "Pane coloring -> off"' \
'set -g window-style "fg=colour247,bg=colour234" ; set -g window-active-style "fg=colour250,bg=black" ; set -g @TPCS "1"; display-message "Pane coloring -> on"'
#---------------------------------------------------------------------------#
# List of plugins
#---------------------------------------------------------------------------#
set -g @plugin 'tmux-plugins/tpm'
#set -g @plugin 'tmux-plugins/tmux-sensible'
set -g @plugin 'tmux-plugins/tmux-resurrect'
set -g @plugin 'tmux-plugins/tmux-logging'
set -g @resurrect-capture-pane-contents 'on'
set -g @resurrect-save-bash-history 'on'
set -g @logging-path $HOME
set -g @screen-capture-path $HOME
set -g @save-complete-history-path $HOME
# Other examples:
# set -g @plugin 'github_username/plugin_name'
# set -g @plugin 'git@github.com/user/plugin'
# set -g @plugin 'git@bitbucket.com/user/plugin'
#---------------------------------------------------------------------------#
# Initialize TMUX plugin manager (keep this line at the very bottom of tmux.conf)
#---------------------------------------------------------------------------#
run '~/.tmux/plugins/tpm/tpm'

View File

@@ -0,0 +1,42 @@
#!/bin/sh
PATH=/bin:/usr/bin
TERM=screen
[ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux)
TMUX="$TEST_TMUX -Ltest"
$TMUX kill-server 2>/dev/null
TMP=$(mktemp)
OUT=$(mktemp)
trap "rm -f $TMP $OUT" 0 1 15
$TMUX -f/dev/null new -d -x200 -y200 || exit 1
$TMUX -f/dev/null splitw || exit 1
sleep 1
cat <<EOF|$TMUX -C a >$TMP
selectp -t%0
splitw
neww
splitw
selectp -t%0
killp -t%1
swapp -t%2 -s%3
neww
splitw
splitw
selectl tiled
killw
EOF
sleep 1
$TMUX has || exit 1
$TMUX lsp -aF '#{pane_id} #{window_layout}' >$TMP || exit 1
cat <<EOF|cmp -s $TMP - || exit 1
%0 f5ab,200x200,0,0[200x50,0,0,0,200x149,0,51,3]
%3 f5ab,200x200,0,0[200x50,0,0,0,200x149,0,51,3]
%2 dcbd,200x200,0,0[200x100,0,0,2,200x99,0,101,4]
%4 dcbd,200x200,0,0[200x100,0,0,2,200x99,0,101,4]
EOF
$TMUX kill-server 2>/dev/null
exit 0

View File

@@ -23,7 +23,7 @@ refresh -C 100,50
ls -F':#{window_width} #{window_height}'
EOF
grep ^: $TMP >$OUT
printf ":80 24\n:100 49\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 -f/dev/null new -d || exit 1
@@ -34,7 +34,7 @@ refresh -C 80,24
ls -F':#{window_width} #{window_height}'
EOF
grep ^: $TMP >$OUT
printf ":80 24\n:80 23\n"|cmp -s $OUT - || exit 1
printf ":80 24\n:80 24\n"|cmp -s $OUT - || exit 1
$TMUX kill-server 2>/dev/null
cat <<EOF|$TMUX -f/dev/null -C new -x 100 -y 50 >$TMP
@@ -43,7 +43,7 @@ refresh -C 80,24
ls -F':#{window_width} #{window_height}'
EOF
grep ^: $TMP >$OUT
printf ":100 50\n:80 23\n"|cmp -s $OUT - || exit 1
printf ":100 50\n:80 24\n"|cmp -s $OUT - || exit 1
$TMUX kill-server 2>/dev/null
exit 0

View File

@@ -174,11 +174,12 @@ recalculate_sizes(void)
TAILQ_FOREACH(c, &clients, entry) {
if (ignore_client_size(c))
continue;
if (c->tty.sy <= status_line_size(c))
s = c->session;
if (c->tty.sy <= s->statuslines || (c->flags & CLIENT_CONTROL))
c->flags |= CLIENT_STATUSOFF;
else
c->flags &= ~CLIENT_STATUSOFF;
c->session->attached++;
s->attached++;
}
/* Walk each window and adjust the size. */

View File

@@ -23,27 +23,11 @@
#include "tmux.h"
struct screen_redraw_ctx {
struct client *c;
u_int lines;
int top;
int pane_status;
u_int sx;
u_int sy;
u_int ox;
u_int oy;
};
static void screen_redraw_draw_borders(struct screen_redraw_ctx *);
static void screen_redraw_draw_panes(struct screen_redraw_ctx *);
static void screen_redraw_draw_status(struct screen_redraw_ctx *);
static void screen_redraw_draw_pane(struct screen_redraw_ctx *,
struct window_pane *);
static void screen_redraw_draw_number(struct screen_redraw_ctx *,
struct window_pane *);
#define CELL_INSIDE 0
#define CELL_LEFTRIGHT 1
@@ -374,8 +358,8 @@ screen_redraw_draw_pane_status(struct screen_redraw_ctx *ctx)
width = size - x;
}
if (ctx->top)
yoff += ctx->lines;
if (ctx->statustop)
yoff += ctx->statuslines;
tty_draw_line(tty, NULL, s, i, 0, width, x, yoff - ctx->oy);
}
tty_cursor(tty, 0, 0);
@@ -399,6 +383,9 @@ screen_redraw_update(struct client *c, int flags)
if (!redraw && (~flags & CLIENT_REDRAWSTATUSALWAYS))
flags &= ~CLIENT_REDRAWSTATUS;
if (c->overlay_draw != NULL)
flags |= CLIENT_REDRAWOVERLAY;
if (options_get_number(wo, "pane-border-status") != CELL_STATUS_OFF) {
redraw = 0;
TAILQ_FOREACH(wp, &w->panes, entry) {
@@ -419,21 +406,25 @@ screen_redraw_set_context(struct client *c, struct screen_redraw_ctx *ctx)
struct options *oo = s->options;
struct window *w = s->curw->window;
struct options *wo = w->options;
u_int lines;
memset(ctx, 0, sizeof *ctx);
ctx->c = c;
ctx->lines = status_line_size(c);
lines = status_line_size(c);
if (c->message_string != NULL || c->prompt_string != NULL)
ctx->lines = (ctx->lines == 0) ? 1 : ctx->lines;
if (ctx->lines != 0 && options_get_number(oo, "status-position") == 0)
ctx->top = 1;
lines = (lines == 0) ? 1 : lines;
if (lines != 0 && options_get_number(oo, "status-position") == 0)
ctx->statustop = 1;
ctx->statuslines = lines;
ctx->pane_status = options_get_number(wo, "pane-border-status");
tty_window_offset(&c->tty, &ctx->ox, &ctx->oy, &ctx->sx, &ctx->sy);
log_debug("%s: %s @%u ox=%u oy=%u sx=%u sy=%u %u/%d", __func__, c->name,
w->id, ctx->ox, ctx->oy, ctx->sx, ctx->sy, ctx->lines, ctx->top);
w->id, ctx->ox, ctx->oy, ctx->sx, ctx->sy, ctx->statuslines,
ctx->statustop);
}
/* Redraw entire screen. */
@@ -456,9 +447,11 @@ screen_redraw_screen(struct client *c)
}
if (flags & CLIENT_REDRAWWINDOW)
screen_redraw_draw_panes(&ctx);
if (ctx.lines != 0 &&
if (ctx.statuslines != 0 &&
(flags & (CLIENT_REDRAWSTATUS|CLIENT_REDRAWSTATUSALWAYS)))
screen_redraw_draw_status(&ctx);
if (c->overlay_draw != NULL && (flags & CLIENT_REDRAWOVERLAY))
c->overlay_draw(c, &ctx);
tty_reset(&c->tty);
}
@@ -468,7 +461,7 @@ screen_redraw_pane(struct client *c, struct window_pane *wp)
{
struct screen_redraw_ctx ctx;
if (!window_pane_visible(wp))
if (c->overlay_draw != NULL || !window_pane_visible(wp))
return;
screen_redraw_set_context(c, &ctx);
@@ -508,8 +501,8 @@ screen_redraw_draw_borders_cell(struct screen_redraw_ctx *ctx, u_int i, u_int j,
tty_attributes(tty, active_gc, NULL);
else
tty_attributes(tty, other_gc, NULL);
if (ctx->top)
tty_cursor(tty, i, ctx->lines + j);
if (ctx->statustop)
tty_cursor(tty, i, ctx->statuslines + j);
else
tty_cursor(tty, i, j);
tty_putc(tty, CELL_BORDERS[type]);
@@ -538,7 +531,7 @@ screen_redraw_draw_borders(struct screen_redraw_ctx *ctx)
memcpy(&m_active_gc, &active_gc, sizeof m_active_gc);
m_active_gc.attr ^= GRID_ATTR_REVERSE;
for (j = 0; j < tty->sy - ctx->lines; j++) {
for (j = 0; j < tty->sy - ctx->statuslines; j++) {
for (i = 0; i < tty->sx; i++) {
screen_redraw_draw_borders_cell(ctx, i, j,
&m_active_gc, &active_gc, &m_other_gc, &other_gc);
@@ -557,11 +550,8 @@ screen_redraw_draw_panes(struct screen_redraw_ctx *ctx)
log_debug("%s: %s @%u", __func__, c->name, w->id);
TAILQ_FOREACH(wp, &w->panes, entry) {
if (!window_pane_visible(wp))
continue;
screen_redraw_draw_pane(ctx, wp);
if (c->flags & CLIENT_IDENTIFY)
screen_redraw_draw_number(ctx, wp);
if (window_pane_visible(wp))
screen_redraw_draw_pane(ctx, wp);
}
}
@@ -577,11 +567,11 @@ screen_redraw_draw_status(struct screen_redraw_ctx *ctx)
log_debug("%s: %s @%u", __func__, c->name, w->id);
if (ctx->top)
if (ctx->statustop)
y = 0;
else
y = c->tty.sy - ctx->lines;
for (i = 0; i < ctx->lines; i++)
y = c->tty.sy - ctx->statuslines;
for (i = 0; i < ctx->statuslines; i++)
tty_draw_line(tty, NULL, s, 0, i, UINT_MAX, 0, y + i);
}
@@ -599,8 +589,8 @@ screen_redraw_draw_pane(struct screen_redraw_ctx *ctx, struct window_pane *wp)
if (wp->xoff + wp->sx <= ctx->ox || wp->xoff >= ctx->ox + ctx->sx)
return;
if (ctx->top)
top = ctx->lines;
if (ctx->statustop)
top = ctx->statuslines;
else
top = 0;
@@ -639,125 +629,3 @@ screen_redraw_draw_pane(struct screen_redraw_ctx *ctx, struct window_pane *wp)
tty_draw_line(tty, wp, s, i, j, width, x, y);
}
}
/* Draw number on a pane. */
static void
screen_redraw_draw_number(struct screen_redraw_ctx *ctx, struct window_pane *wp)
{
struct client *c = ctx->c;
struct tty *tty = &c->tty;
struct session *s = c->session;
struct options *oo = s->options;
struct window *w = wp->window;
struct grid_cell gc;
u_int idx, px, py, i, j, xoff, yoff, sx, sy;
int colour, active_colour;
char buf[16], *ptr;
size_t len;
if (wp->xoff + wp->sx <= ctx->ox ||
wp->xoff >= ctx->ox + ctx->sx ||
wp->yoff + wp->sy <= ctx->oy ||
wp->yoff >= ctx->oy + ctx->sy)
return;
if (wp->xoff >= ctx->ox && wp->xoff + wp->sx <= ctx->ox + ctx->sx) {
/* All visible. */
xoff = wp->xoff - ctx->ox;
sx = wp->sx;
} else if (wp->xoff < ctx->ox &&
wp->xoff + wp->sx > ctx->ox + ctx->sx) {
/* Both left and right not visible. */
xoff = 0;
sx = ctx->sx;
} else if (wp->xoff < ctx->ox) {
/* Left not visible. */
xoff = 0;
sx = wp->sx - (ctx->ox - wp->xoff);
} else {
/* Right not visible. */
xoff = wp->xoff - ctx->ox;
sx = wp->sx - xoff;
}
if (wp->yoff >= ctx->oy && wp->yoff + wp->sy <= ctx->oy + ctx->sy) {
/* All visible. */
yoff = wp->yoff - ctx->oy;
sy = wp->sy;
} else if (wp->yoff < ctx->oy &&
wp->yoff + wp->sy > ctx->oy + ctx->sy) {
/* Both top and bottom not visible. */
yoff = 0;
sy = ctx->sy;
} else if (wp->yoff < ctx->oy) {
/* Top not visible. */
yoff = 0;
sy = wp->sy - (ctx->oy - wp->yoff);
} else {
/* Bottom not visible. */
yoff = wp->yoff - ctx->oy;
sy = wp->sy - yoff;
}
if (ctx->top)
yoff += ctx->lines;
px = sx / 2;
py = sy / 2;
if (window_pane_index(wp, &idx) != 0)
fatalx("index not found");
len = xsnprintf(buf, sizeof buf, "%u", idx);
if (sx < len)
return;
colour = options_get_number(oo, "display-panes-colour");
active_colour = options_get_number(oo, "display-panes-active-colour");
if (sx < len * 6 || sy < 5) {
tty_cursor(tty, xoff + px - len / 2, yoff + py);
goto draw_text;
}
px -= len * 3;
py -= 2;
memcpy(&gc, &grid_default_cell, sizeof gc);
if (w->active == wp)
gc.bg = active_colour;
else
gc.bg = colour;
gc.flags |= GRID_FLAG_NOPALETTE;
tty_attributes(tty, &gc, wp);
for (ptr = buf; *ptr != '\0'; ptr++) {
if (*ptr < '0' || *ptr > '9')
continue;
idx = *ptr - '0';
for (j = 0; j < 5; j++) {
for (i = px; i < px + 5; i++) {
tty_cursor(tty, xoff + i, yoff + py + j);
if (window_clock_table[idx][j][i - px])
tty_putc(tty, ' ');
}
}
px += 6;
}
len = xsnprintf(buf, sizeof buf, "%ux%u", wp->sx, wp->sy);
if (sx < len || sy < 6)
return;
tty_cursor(tty, xoff + sx - len, yoff);
draw_text:
memcpy(&gc, &grid_default_cell, sizeof gc);
if (w->active == wp)
gc.fg = active_colour;
else
gc.fg = colour;
gc.flags |= GRID_FLAG_NOPALETTE;
tty_attributes(tty, &gc, wp);
tty_puts(tty, buf);
tty_cursor(tty, 0, 0);
}

View File

@@ -403,6 +403,51 @@ screen_write_vline(struct screen_write_ctx *ctx, u_int ny, int top, int bottom)
screen_write_set_cursor(ctx, cx, cy);
}
/* Draw a menu on screen. */
void
screen_write_menu(struct screen_write_ctx *ctx, struct menu *menu, int choice)
{
struct screen *s = ctx->s;
struct grid_cell gc;
u_int cx, cy, i, j;
const char *name;
cx = s->cx;
cy = s->cy;
memcpy(&gc, &grid_default_cell, sizeof gc);
screen_write_box(ctx, menu->width + 4, menu->count + 2);
screen_write_cursormove(ctx, cx + 2, cy, 0);
format_draw(ctx, &gc, menu->width, menu->title, NULL);
for (i = 0; i < menu->count; i++) {
name = menu->items[i].name;
if (name == NULL) {
screen_write_cursormove(ctx, cx, cy + 1 + i, 0);
screen_write_hline(ctx, menu->width + 4, 1, 1);
} else {
if (choice >= 0 && i == (u_int)choice && *name != '-')
gc.attr |= GRID_ATTR_REVERSE;
screen_write_cursormove(ctx, cx + 2, cy + 1 + i, 0);
for (j = 0; j < menu->width; j++)
screen_write_putc(ctx, &gc, ' ');
screen_write_cursormove(ctx, cx + 2, cy + 1 + i, 0);
if (*name == '-') {
name++;
gc.attr |= GRID_ATTR_DIM;
format_draw(ctx, &gc, menu->width, name, NULL);
gc.attr &= ~GRID_ATTR_DIM;
} else
format_draw(ctx, &gc, menu->width, name, NULL);
if (choice >= 0 && i == (u_int)choice)
gc.attr &= ~GRID_ATTR_REVERSE;
}
}
screen_write_set_cursor(ctx, cx, cy);
}
/* Draw a box on screen. */
void
screen_write_box(struct screen_write_ctx *ctx, u_int nx, u_int ny)
@@ -1139,7 +1184,7 @@ screen_write_collect_clear(struct screen_write_ctx *ctx, u_int y, u_int n)
u_int i;
size_t size;
for (i = y ; i < y + n; i++) {
for (i = y; i < y + n; i++) {
if (TAILQ_EMPTY(&ctx->list[i].items))
continue;
size = 0;

View File

@@ -33,7 +33,7 @@
static void server_client_free(int, short, void *);
static void server_client_check_focus(struct window_pane *);
static void server_client_check_resize(struct window_pane *);
static key_code server_client_check_mouse(struct client *);
static key_code server_client_check_mouse(struct client *, struct key_event *);
static void server_client_repeat_timer(int, short, void *);
static void server_client_click_timer(int, short, void *);
static void server_client_check_exit(struct client *);
@@ -41,8 +41,7 @@ static void server_client_check_redraw(struct client *);
static void server_client_set_title(struct client *);
static void server_client_reset_state(struct client *);
static int server_client_assume_paste(struct session *);
static void server_client_clear_identify(struct client *,
struct window_pane *);
static void server_client_clear_overlay(struct client *);
static void server_client_dispatch(struct imsg *, void *);
static void server_client_dispatch_command(struct client *, struct imsg *);
@@ -64,44 +63,56 @@ server_client_how_many(void)
return (n);
}
/* Identify mode callback. */
/* Overlay timer callback. */
static void
server_client_callback_identify(__unused int fd, __unused short events,
void *data)
server_client_overlay_timer(__unused int fd, __unused short events, void *data)
{
server_client_clear_identify(data, NULL);
server_client_clear_overlay(data);
}
/* Set identify mode on client. */
/* Set an overlay on client. */
void
server_client_set_identify(struct client *c, u_int delay)
server_client_set_overlay(struct client *c, u_int delay, overlay_draw_cb drawcb,
overlay_key_cb keycb, overlay_free_cb freecb, void *data)
{
struct timeval tv;
if (c->overlay_draw != NULL)
server_client_clear_overlay(c);
tv.tv_sec = delay / 1000;
tv.tv_usec = (delay % 1000) * 1000L;
if (event_initialized(&c->identify_timer))
evtimer_del(&c->identify_timer);
evtimer_set(&c->identify_timer, server_client_callback_identify, c);
if (event_initialized(&c->overlay_timer))
evtimer_del(&c->overlay_timer);
evtimer_set(&c->overlay_timer, server_client_overlay_timer, c);
if (delay != 0)
evtimer_add(&c->identify_timer, &tv);
evtimer_add(&c->overlay_timer, &tv);
c->overlay_draw = drawcb;
c->overlay_key = keycb;
c->overlay_free = freecb;
c->overlay_data = data;
c->flags |= CLIENT_IDENTIFY;
c->tty.flags |= (TTY_FREEZE|TTY_NOCURSOR);
server_redraw_client(c);
}
/* Clear identify mode on client. */
/* Clear overlay mode on client. */
static void
server_client_clear_identify(struct client *c, struct window_pane *wp)
server_client_clear_overlay(struct client *c)
{
if (~c->flags & CLIENT_IDENTIFY)
if (c->overlay_draw == NULL)
return;
c->flags &= ~CLIENT_IDENTIFY;
if (c->identify_callback != NULL)
c->identify_callback(c, wp);
if (event_initialized(&c->overlay_timer))
evtimer_del(&c->overlay_timer);
if (c->overlay_free != NULL)
c->overlay_free(c);
c->overlay_draw = NULL;
c->overlay_key = NULL;
c->tty.flags &= ~(TTY_FREEZE|TTY_NOCURSOR);
server_redraw_client(c);
@@ -255,7 +266,7 @@ server_client_lost(struct client *c)
c->flags |= CLIENT_DEAD;
server_client_clear_identify(c, NULL);
server_client_clear_overlay(c);
status_prompt_clear(c);
status_message_clear(c);
@@ -289,9 +300,6 @@ server_client_lost(struct client *c)
key_bindings_unref_table(c->keytable);
if (event_initialized(&c->identify_timer))
evtimer_del(&c->identify_timer);
free(c->message_string);
if (event_initialized(&c->message_timer))
evtimer_del(&c->message_timer);
@@ -405,10 +413,10 @@ server_client_exec(struct client *c, const char *cmd)
/* Check for mouse keys. */
static key_code
server_client_check_mouse(struct client *c)
server_client_check_mouse(struct client *c, struct key_event *event)
{
struct mouse_event *m = &event->m;
struct session *s = c->session;
struct mouse_event *m = &c->tty.mouse;
struct winlink *wl;
struct window_pane *wp;
u_int x, y, b, sx, sy, px, py;
@@ -416,11 +424,21 @@ server_client_check_mouse(struct client *c)
key_code key;
struct timeval tv;
struct style_range *sr;
enum { NOTYPE, MOVE, DOWN, UP, DRAG, WHEEL, DOUBLE, TRIPLE } type;
enum { NOWHERE, PANE, STATUS, STATUS_LEFT, STATUS_RIGHT, STATUS_DEFAULT, BORDER } where;
type = NOTYPE;
where = NOWHERE;
enum { NOTYPE,
MOVE,
DOWN,
UP,
DRAG,
WHEEL,
DOUBLE,
TRIPLE } type = NOTYPE;
enum { NOWHERE,
PANE,
STATUS,
STATUS_LEFT,
STATUS_RIGHT,
STATUS_DEFAULT,
BORDER } where = NOWHERE;
log_debug("%s mouse %02x at %u,%u (last %u,%u) (%d)", c->name, m->b,
m->x, m->y, m->lx, m->ly, c->tty.mouse_drag_flag);
@@ -440,9 +458,11 @@ server_client_check_mouse(struct client *c)
type = DRAG;
if (c->tty.mouse_drag_flag) {
x = m->x, y = m->y, b = m->b;
if (x == m->lx && y == m->ly)
return (KEYC_UNKNOWN);
log_debug("drag update at %u,%u", x, y);
} else {
x = m->lx - m->ox, y = m->ly - m->oy, b = m->lb;
x = m->lx, y = m->ly, b = m->lb;
log_debug("drag start at %u,%u", x, y);
}
} else if (MOUSE_WHEEL(m->b)) {
@@ -547,8 +567,6 @@ have_event:
return (KEYC_UNKNOWN);
px = px + m->ox;
py = py + m->oy;
m->x = x + m->ox;
m->y = y + m->oy;
/* Try the pane borders if not zoomed. */
if (~s->curw->window->flags & WINDOW_ZOOMED) {
@@ -974,14 +992,19 @@ server_client_assume_paste(struct session *s)
return (0);
}
/* Handle data key input from client. */
void
server_client_handle_key(struct client *c, key_code key)
/*
* Handle data key input from client. This owns and can modify the key event it
* is given and is responsible for freeing it.
*/
static enum cmd_retval
server_client_key_callback(struct cmdq_item *item, void *data)
{
struct mouse_event *m = &c->tty.mouse;
struct client *c = item->client;
struct key_event *event = data;
key_code key = event->key;
struct mouse_event *m = &event->m;
struct session *s = c->session;
struct winlink *wl;
struct window *w;
struct window_pane *wp;
struct window_mode_entry *wme;
struct timeval tv;
@@ -993,45 +1016,32 @@ server_client_handle_key(struct client *c, key_code key)
/* Check the client is good to accept input. */
if (s == NULL || (c->flags & (CLIENT_DEAD|CLIENT_SUSPENDED)) != 0)
return;
goto out;
wl = s->curw;
w = wl->window;
/* Update the activity timer. */
if (gettimeofday(&c->activity_time, NULL) != 0)
fatal("gettimeofday failed");
session_update_activity(s, &c->activity_time);
/* Number keys jump to pane in identify mode. */
if (c->flags & CLIENT_IDENTIFY && key >= '0' && key <= '9') {
if (c->flags & CLIENT_READONLY)
return;
window_unzoom(w);
wp = window_pane_at_index(w, key - '0');
server_client_clear_identify(c, wp);
return;
}
/* Handle status line. */
if (!(c->flags & CLIENT_READONLY)) {
if (~c->flags & CLIENT_READONLY)
status_message_clear(c);
server_client_clear_identify(c, NULL);
}
if (c->prompt_string != NULL) {
if (c->flags & CLIENT_READONLY)
return;
goto out;
if (status_prompt_key(c, key) == 0)
return;
goto out;
}
/* Check for mouse keys. */
m->valid = 0;
if (key == KEYC_MOUSE) {
if (c->flags & CLIENT_READONLY)
return;
key = server_client_check_mouse(c);
goto out;
key = server_client_check_mouse(c, event);
if (key == KEYC_UNKNOWN)
return;
goto out;
m->valid = 1;
m->key = key;
@@ -1042,10 +1052,9 @@ server_client_handle_key(struct client *c, key_code key)
*/
if (key == KEYC_DRAGGING) {
c->tty.mouse_drag_update(c, m);
return;
goto out;
}
} else
m->valid = 0;
}
/* Find affected pane. */
if (!KEYC_IS_MOUSE(key) || cmd_find_from_mouse(&fs, m, 0) != 0)
@@ -1084,7 +1093,7 @@ table_changed:
strcmp(table->name, "prefix") != 0) {
server_client_set_key_table(c, "prefix");
server_status_client(c);
return;
goto out;
}
flags = c->flags;
@@ -1142,9 +1151,9 @@ try_again:
server_status_client(c);
/* Execute the key binding. */
key_bindings_dispatch(bd, NULL, c, m, &fs);
key_bindings_dispatch(bd, item, c, m, &fs);
key_bindings_unref_table(table);
return;
goto out;
}
/*
@@ -1179,14 +1188,48 @@ try_again:
if (first != table && (~flags & CLIENT_REPEAT)) {
server_client_set_key_table(c, NULL);
server_status_client(c);
return;
goto out;
}
forward_key:
if (c->flags & CLIENT_READONLY)
return;
goto out;
if (wp != NULL)
window_pane_key(wp, c, s, wl, key, m);
out:
free(event);
return (CMD_RETURN_NORMAL);
}
/* Handle a key event. */
int
server_client_handle_key(struct client *c, struct key_event *event)
{
struct session *s = c->session;
struct cmdq_item *item;
/* Check the client is good to accept input. */
if (s == NULL || (c->flags & (CLIENT_DEAD|CLIENT_SUSPENDED)) != 0)
return (0);
/*
* Key presses in overlay mode are a special case. The queue might be
* blocked so they need to be processed immediately rather than queued.
*/
if ((~c->flags & CLIENT_READONLY) && c->overlay_key != NULL) {
if (c->overlay_key(c, event) != 0)
server_client_clear_overlay(c);
return (0);
}
/*
* Add the key to the queue so it happens after any commands queued by
* previous keys.
*/
item = cmdq_get_callback(server_client_key_callback, event);
cmdq_append(c, item);
return (1);
}
/* Client functions that need to happen every loop. */
@@ -1379,6 +1422,7 @@ focused:
if (wp->base.mode & MODE_FOCUSON)
bufferevent_write(wp->event, "\033[I", 3);
notify_pane("pane-focus-in", wp);
session_update_activity(c->session, NULL);
}
wp->flags |= PANE_FOCUSED;
}
@@ -1404,6 +1448,8 @@ server_client_reset_state(struct client *c)
if (c->flags & (CLIENT_CONTROL|CLIENT_SUSPENDED))
return;
if (c->overlay_draw != NULL)
return;
mode = s->mode;
tty_region_off(&c->tty);
@@ -1514,10 +1560,11 @@ server_client_check_redraw(struct client *c)
if (c->flags & (CLIENT_CONTROL|CLIENT_SUSPENDED))
return;
if (c->flags & CLIENT_ALLREDRAWFLAGS) {
log_debug("%s: redraw%s%s%s", c->name,
log_debug("%s: redraw%s%s%s%s", c->name,
(c->flags & CLIENT_REDRAWWINDOW) ? " window" : "",
(c->flags & CLIENT_REDRAWSTATUS) ? " status" : "",
(c->flags & CLIENT_REDRAWBORDERS) ? " borders" : "");
(c->flags & CLIENT_REDRAWBORDERS) ? " borders" : "",
(c->flags & CLIENT_REDRAWOVERLAY) ? " overlay" : "");
}
/*
@@ -1665,8 +1712,7 @@ server_client_dispatch(struct imsg *imsg, void *arg)
evbuffer_add(c->stdin_data, stdindata.data,
stdindata.size);
}
c->stdin_callback(c, c->stdin_closed,
c->stdin_callback_data);
c->stdin_callback(c, c->stdin_closed, c->stdin_callback_data);
break;
case MSG_RESIZE:
if (datalen != 0)
@@ -1674,6 +1720,7 @@ server_client_dispatch(struct imsg *imsg, void *arg)
if (c->flags & CLIENT_CONTROL)
break;
server_client_clear_overlay(c);
tty_resize(&c->tty);
recalculate_sizes();
server_redraw_client(c);
@@ -1731,18 +1778,6 @@ server_client_command_done(struct cmdq_item *item, __unused void *data)
return (CMD_RETURN_NORMAL);
}
/* Show an error message. */
static enum cmd_retval
server_client_command_error(struct cmdq_item *item, void *data)
{
char *error = data;
cmdq_error(item, "%s", error);
free(error);
return (CMD_RETURN_NORMAL);
}
/* Handle command message. */
static void
server_client_dispatch_command(struct client *c, struct imsg *imsg)
@@ -1750,9 +1785,9 @@ server_client_dispatch_command(struct client *c, struct imsg *imsg)
struct msg_command_data data;
char *buf;
size_t len;
struct cmd_list *cmdlist = NULL;
int argc;
char **argv, *cause;
struct cmd_parse_result *pr;
if (c->flags & CLIENT_EXIT)
return;
@@ -1778,22 +1813,30 @@ server_client_dispatch_command(struct client *c, struct imsg *imsg)
*argv = xstrdup("new-session");
}
if ((cmdlist = cmd_list_parse(argc, argv, NULL, 0, &cause)) == NULL) {
cmd_free_argv(argc, argv);
pr = cmd_parse_from_arguments(argc, argv, NULL);
switch (pr->status) {
case CMD_PARSE_EMPTY:
cause = xstrdup("empty command");
goto error;
case CMD_PARSE_ERROR:
cause = pr->error;
goto error;
case CMD_PARSE_SUCCESS:
break;
}
cmd_free_argv(argc, argv);
cmdq_append(c, cmdq_get_command(cmdlist, NULL, NULL, 0));
cmdq_append(c, cmdq_get_command(pr->cmdlist, NULL, NULL, 0));
cmdq_append(c, cmdq_get_callback(server_client_command_done, NULL));
cmd_list_free(cmdlist);
cmd_list_free(pr->cmdlist);
return;
error:
cmdq_append(c, cmdq_get_callback(server_client_command_error, cause));
cmd_free_argv(argc, argv);
if (cmdlist != NULL)
cmd_list_free(cmdlist);
cmdq_append(c, cmdq_get_error(cause));
free(cause);
c->flags |= CLIENT_EXIT;
}

View File

@@ -369,7 +369,7 @@ server_destroy_session_group(struct session *s)
else {
TAILQ_FOREACH_SAFE(s, &sg->sessions, gentry, s1) {
server_destroy_session(s);
session_destroy(s, __func__);
session_destroy(s, 1, __func__);
}
}
}
@@ -436,11 +436,10 @@ server_check_unattached(void)
if (s->attached != 0)
continue;
if (options_get_number (s->options, "destroy-unattached"))
session_destroy(s, __func__);
session_destroy(s, 1, __func__);
}
}
/* Set stdin callback. */
int
server_set_stdin_callback(struct client *c, void (*cb)(struct client *, int,
void *), void *cb_data, char **cause)
@@ -454,7 +453,7 @@ server_set_stdin_callback(struct client *c, void (*cb)(struct client *, int,
return (-1);
}
if (c->stdin_callback != NULL) {
*cause = xstrdup("stdin in use");
*cause = xstrdup("stdin is in use");
return (-1);
}

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