308 Commits
2.5 ... 2.6

Author SHA1 Message Date
Nicholas Marriott
bd71cbbe27 2.6. 2017-10-05 14:31:23 +01:00
nicm
71ec616e4d Initialize alerts timer event where it is used, avoids crash with new windows. 2017-09-22 17:58:30 +01:00
Nicholas Marriott
a8b84b7cfa 2.6-rc3. 2017-09-11 10:08:28 +01:00
Nicholas Marriott
495e2ed17f Merge branch 'master' into 2.6-rc 2017-09-11 10:08:15 +01:00
Thomas Adam
d8c397d1b7 Merge branch 'obsd-master' 2017-09-11 10:01:11 +01:00
nicm
d8d6c2746e Mention that filter is a format. 2017-09-11 06:53:06 +00:00
nicm
6fdaaa0637 Do not free more lines than are available in the history. 2017-09-11 06:40:46 +00:00
Nicholas Marriott
034b19b734 2.6-rc2. 2017-09-10 16:08:22 +01:00
Nicholas Marriott
cb8eba1530 Merge branch 'master' into 2.6-rc 2017-09-10 16:07:44 +01:00
Thomas Adam
7aa8b8a25c Merge branch 'obsd-master' 2017-09-10 16:01:14 +01:00
Nicholas Marriott
abcbfcb0e8 Merge branch 'master' into 2.6-rc 2017-09-10 15:38:02 +01:00
nicm
70bc07a358 Previously, extended cell data was never reduced in size even when the
cell was overwritten. With a large history this can be a substantial
amount of memory. To reduce this, compact each extended cell list to
only cells in use as it is scrolled off the visible screen into the
history. From Dan Aloni in GitHub issue 1062.
2017-09-10 14:36:12 +00:00
Thomas Adam
7f83b53027 Merge branch 'obsd-master'
Conflicts:
	server-client.c
2017-09-10 11:39:45 +01:00
nicm
8405fcdd9b Apply timeout to CAN and RS which also wait for ST. 2017-09-10 08:01:23 +00:00
nicm
f56f09ea38 Fix a few errors in how the selected line is chosen after resize,
reported by Felix Rosencrantz in GitHub issue 1059.
2017-09-08 16:28:41 +00:00
Nicholas Marriott
c62cfe64c8 Add to CHANGES. 2017-09-08 14:22:34 +01:00
nicm
78cf3c14ca When removing a key table clear it out of clients, fixes issue with
unbind -a reported by Thomas Sattler.
2017-09-08 08:45:27 +00:00
nicm
89e057dc4a Do not fail if unset an option that is already unset, reported by Thomas
Sattler.
2017-09-07 13:18:44 +00:00
nicm
466066c3a1 Do not attempt to use TIOCSWINSZ on a -1 file descriptor (possible if
the pane has already died).
2017-09-06 07:12:41 +00:00
Thomas Adam
ff3d05d92f Merge branch 'obsd-master' 2017-09-04 12:01:11 +01:00
nicm
eadd79acec Move to current mouse position not last when clcking in copy mode; fixes
GitHub issue 1055. Also a man page fix from jmc.
2017-09-04 09:18:51 +00:00
Thomas Adam
d019821281 Merge branch 'obsd-master' 2017-09-02 20:01:18 +01:00
nicm
f4848b437f Add selectp -T to set pane title. 2017-09-02 17:51:54 +00:00
Nicholas Marriott
e941e532fa Mention GitHub for code. 2017-08-30 21:49:31 +01:00
Nicholas Marriott
a1986c5973 Add to CHANGES. 2017-08-30 21:24:16 +01:00
Nicholas Marriott
6e2b3f435a Add to CHANGES. 2017-08-30 21:23:26 +01:00
Nicholas Marriott
07d3c4d882 Merge branch 'master' into 2.6-rc 2017-08-30 20:04:37 +01:00
Thomas Adam
f81e87f1e2 Merge branch 'obsd-master' 2017-08-30 20:01:11 +01:00
nicm
6abfd9b8ff Instead of overloading the line clear function to mean free if
background is default (8), introduce an explicit free function and use
it where a free alone is needed. Likewise, use memmove directly rather
than grid_move_lines where it makes sense. Based on a memory leak fix by
Dan Aloni in GitHub issue 1051.
2017-08-30 18:13:47 +00:00
Nicholas Marriott
07c679b52d Merge branch 'master' into 2.6-rc 2017-08-30 12:04:09 +01:00
Nicholas Marriott
b4c633cc40 Merge branch 'master' of github.com:tmux/tmux 2017-08-30 12:03:59 +01:00
Thomas Adam
54c5070767 Merge branch 'obsd-master' 2017-08-30 12:01:10 +01:00
nicm
17cf1b21c6 Pass flags into cmd_find_from_* to fix prefer-unattached, reported by
Thomas Sattler.
2017-08-30 10:33:57 +00:00
Nicholas Marriott
8f364053ca Add to TODO. 2017-08-30 11:21:20 +01:00
Nicholas Marriott
2e4e521629 2.6-rc version. 2017-08-30 09:34:27 +01:00
Nicholas Marriott
fa20f19494 Fix position of -v, pointed out by Thomas Sattler. 2017-08-30 09:33:53 +01:00
Nicholas Marriott
c1d8b0f74e Back to master. 2017-08-29 22:19:46 +01:00
Nicholas Marriott
3815e4f05e This is not true now... 2017-08-29 22:19:27 +01:00
Nicholas Marriott
72488b526b Merge branch 'master' into 2.6-rc 2017-08-29 22:06:22 +01:00
Nicholas Marriott
5fec6c598e Merge branch 'master' of github.com:tmux/tmux 2017-08-29 22:06:06 +01:00
Thomas Adam
dee6bb5a31 Merge branch 'obsd-master' 2017-08-29 22:01:11 +01:00
Nicholas Marriott
566b9623b3 Merge branch 'master' into 2.6-rc 2017-08-29 21:42:15 +01:00
Nicholas Marriott
3f3fb43850 More style. 2017-08-29 21:42:05 +01:00
Nicholas Marriott
2248b886fe 2.6-rc version. 2017-08-29 21:37:51 +01:00
Nicholas Marriott
82b30f2322 Style of headings. 2017-08-29 21:34:56 +01:00
Nicholas Marriott
6b841a036a Fix example from Adam Spiers. 2017-08-29 21:32:09 +01:00
nicm
a7d1ee5433 Redraw rectangle selections properly when cursor at end, GitHub issue 992. 2017-08-29 20:26:25 +00:00
Thomas Adam
0f7160eb2f Merge branch 'obsd-master' 2017-08-29 12:01:25 +01:00
Nicholas Marriott
91d6bff8b8 Merge branch 'master' of github.com:tmux/tmux 2017-08-29 11:13:54 +01:00
Nicholas Marriott
5cdccf78a1 Update CHANGES. 2017-08-29 11:13:35 +01:00
nicm
5fc0be5045 Support REP escape sequence (\033[b). 2017-08-29 09:28:45 +00:00
nicm
9852bd743c Check for complete keys before escape prefix, allows keys to be defined
with a leading escape. GitHub issue 1048.
2017-08-29 09:18:48 +00:00
Thomas Adam
7d3bf6453e Merge branch 'obsd-master' 2017-08-28 14:01:17 +01:00
nicm
fe4467ad2b Do not forbid targets to specify non-visible panes - the checks for
visibility are better where the target is used. GitHub issue 1049.
2017-08-28 12:36:38 +00:00
Thomas Adam
b2322b3893 Merge branch 'obsd-master' 2017-08-27 11:48:44 +01:00
nicm
fccfc4e4be Do not allow the current line of screen when the preview is toggled,
from Thomas Adam.
2017-08-27 09:08:36 +00:00
Thomas Adam
e65cc09276 Merge branch 'obsd-master' 2017-08-27 10:01:15 +01:00
nicm
25cf126de8 Use kind and kri for S-Up/Down as well as kUP and kDN. 2017-08-27 08:33:55 +00:00
Thomas Adam
1492c9d7d9 Merge branch 'obsd-master' 2017-08-24 12:01:10 +01:00
nicm
3c63ad4a9c When tty is error or closed, remove client. Reported by Thomas Sattler. 2017-08-24 08:48:37 +00:00
Thomas Adam
3b40f8e42c Merge branch 'obsd-master' 2017-08-23 12:01:13 +01:00
nicm
08b125194e Key (v) and flag (-N) to toggle preview in choose modes. 2017-08-23 09:39:11 +00:00
nicm
1d60dd5872 Fix searching when match is at end of line, from Brad Town. 2017-08-23 09:18:22 +00:00
nicm
f0ce29c341 Allow multiple bells even if there is an existing bell (but not activity
or silence), from Brad Town.
2017-08-23 09:16:39 +00:00
nicm
e1b3dc89d2 Run alert hooks based on the options rather than unconditionally, from
Brad Town.
2017-08-23 09:14:21 +00:00
Nicholas Marriott
0f708dd6e2 Add to TODO. 2017-08-22 13:02:20 +01:00
Thomas Adam
730312e60f Merge branch 'obsd-master' 2017-08-22 00:01:10 +01:00
nicm
bbe9da063e Same as previous for \r alone. 2017-08-21 21:02:58 +00:00
nicm
7ec2a2b9ce Do not emit \r\n to move to column 0 if there are margins, because it
will instead move to the margin left.
2017-08-21 21:01:21 +00:00
Thomas Adam
ccdc369025 Merge branch 'obsd-master' 2017-08-20 00:01:22 +01:00
nicm
768740ae98 Fix example for user-keys. 2017-08-19 20:40:16 +00:00
Thomas Adam
07a13697e1 Merge branch 'obsd-master' 2017-08-17 12:01:17 +01:00
nicm
8daa1d5f54 Add monitor-bell window option to match the activity and silence
options, from Brad Town.
2017-08-17 08:37:38 +00:00
Nicholas Marriott
de86bf1856 Add to CHANGES. 2017-08-16 16:23:58 +01:00
Thomas Adam
2103a09430 Merge branch 'obsd-master' 2017-08-16 14:01:15 +01:00
nicm
c6a8ad23a1 Add -d flag to display-panes to specify timeout, and make 0 mean no
timeout. From Laurens Post.
2017-08-16 12:12:54 +00:00
nicm
c1ec28a34b Rename BELL_* values to ALERT_* now they are used by more than bells,
based on a diff from Brad Town.
2017-08-16 11:46:08 +00:00
Thomas Adam
0824850bbc Merge branch 'obsd-master' 2017-08-09 16:01:10 +01:00
Thomas Adam
27c3852103 Merge branch 'obsd-master'
Conflicts:
	tmux.1
2017-08-09 15:07:18 +01:00
nicm
ac2ba0961b Fix filtering so it works after the change to only show windows if they
have multiple panes.
2017-08-09 13:44:36 +00:00
Nicholas Marriott
237b7a50f4 Update CHANGES and TODO. 2017-08-09 12:48:54 +01:00
nicm
5dd5543fe4 Add -F to choose-tree, choose-client, choose-buffer to specify the
format of each line, as well as adding a couple of formats needed for
the default display.
2017-08-09 11:43:45 +00:00
Thomas Adam
4bb5bb9450 Merge branch 'obsd-master' 2017-08-08 12:01:15 +01:00
nicm
31b06571aa Hooks for after-select-pane and after-select-window. 2017-08-08 09:21:20 +00:00
Thomas Adam
e7b1e05bbd Merge branch 'obsd-master' 2017-08-02 14:01:10 +01:00
nicm
6f9b9655d7 Add selection_present format so commands in copy mode can use it, GitHub
issue 1028.
2017-08-02 11:10:48 +00:00
Nicholas Marriott
45ee118b26 Merge branch 'master' of github.com:tmux/tmux 2017-07-28 15:14:35 +01:00
Nicholas Marriott
0d6fc7eb1e I already mentioned these... revert previous. 2017-07-28 15:14:01 +01:00
Thomas Adam
ed8ddf2449 Merge branch 'obsd-master' 2017-07-28 14:01:13 +01:00
nicm
b4c9f6edba Show pane title in window list for windows with only one pane. 2017-07-28 10:59:58 +00:00
Nicholas Marriott
a704f57971 This is a big meaningless. 2017-07-27 23:25:39 +01:00
Nicholas Marriott
6b60a5c6d6 Update CHANGES. 2017-07-27 23:22:54 +01:00
Thomas Adam
147740ed40 Merge branch 'obsd-master' 2017-07-27 14:01:13 +01:00
nicm
3df7c91f1a Add pane_at_left/right/top/bottom formats, from Amos Bird. 2017-07-27 10:42:05 +00:00
Thomas Adam
58744de3eb Merge branch 'obsd-master' 2017-07-26 18:01:16 +01:00
nicm
b1bd0c7fc1 Always reset the alerts timer so it works even if activity and silence
are enabled on the same window.
2017-07-26 16:16:25 +00:00
nicm
76887b1d27 Make bell, activity and silence alerting more consistent:
- remove the bell-on-alert option;

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

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

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

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

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

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

Tested by Enrico Ghirardi.

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

138
CHANGES
View File

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

View File

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

462
FAQ
View File

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

View File

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

9
README
View File

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

15
SYNCING
View File

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

25
TODO
View File

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

155
alerts.c
View File

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

View File

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

3
cfg.c
View File

@@ -23,7 +23,6 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include "tmux.h"
@@ -233,7 +232,7 @@ cfg_show_causes(struct session *s)
return;
wp = s->curw->window->active;
window_pane_set_mode(wp, &window_copy_mode);
window_pane_set_mode(wp, &window_copy_mode, NULL, NULL);
window_copy_init_for_output(wp);
for (i = 0; i < cfg_ncauses; i++) {
window_copy_add(wp, "%s", cfg_causes[i]);

View File

@@ -63,7 +63,7 @@ static void client_write(int, const char *, size_t);
static void client_signal(int);
static void client_dispatch(struct imsg *, void *);
static void client_dispatch_attached(struct imsg *);
static void client_dispatch_wait(struct imsg *, const char *);
static void client_dispatch_wait(struct imsg *);
static const char *client_exit_message(void);
/*
@@ -156,7 +156,7 @@ retry:
close(lockfd);
return (-1);
}
fd = server_start(base, lockfd, lockfile);
fd = server_start(client_proc, base, lockfd, lockfile);
}
if (locked && lockfd >= 0) {
@@ -214,8 +214,7 @@ client_exit_message(void)
/* Client main loop. */
int
client_main(struct event_base *base, int argc, char **argv, int flags,
const char *shellcmd)
client_main(struct event_base *base, int argc, char **argv, int flags)
{
struct cmd *cmd;
struct cmd_list *cmdlist;
@@ -236,7 +235,7 @@ client_main(struct event_base *base, int argc, char **argv, int flags,
/* Set up the initial command. */
cmdflags = 0;
if (shellcmd != NULL) {
if (shell_command != NULL) {
msg = MSG_SHELL;
cmdflags = CMD_STARTSERVER;
} else if (argc == 0) {
@@ -261,7 +260,8 @@ client_main(struct event_base *base, int argc, char **argv, int flags,
}
/* Create client process structure (starts logging). */
client_proc = proc_start("client", base, 0, client_signal);
client_proc = proc_start("client");
proc_set_signals(client_proc, client_signal);
/* Initialize the client socket and start the server. */
fd = client_connect(base, socket_path, cmdflags & CMD_STARTSERVER);
@@ -275,8 +275,7 @@ client_main(struct event_base *base, int argc, char **argv, int flags,
}
return (1);
}
client_peer = proc_add_peer(client_proc, fd, client_dispatch,
(void *)shellcmd);
client_peer = proc_add_peer(client_proc, fd, client_dispatch, NULL);
/* Save these before pledge(). */
if ((cwd = getcwd(path, sizeof path)) == NULL) {
@@ -367,7 +366,6 @@ client_main(struct event_base *base, int argc, char **argv, int flags,
if (client_exittype == MSG_EXEC) {
if (client_flags & CLIENT_CONTROLCONTROL)
tcsetattr(STDOUT_FILENO, TCSAFLUSH, &saved_tio);
clear_signals(0);
client_exec(client_execshell, client_execcmd);
}
@@ -483,6 +481,8 @@ client_exec(const char *shell, const char *shellcmd)
xasprintf(&argv0, "%s", name);
setenv("SHELL", shell, 1);
proc_clear_signals(client_proc, 1);
setblocking(STDIN_FILENO, 1);
setblocking(STDOUT_FILENO, 1);
setblocking(STDERR_FILENO, 1);
@@ -534,7 +534,7 @@ client_signal(int sig)
/* Callback for client read events. */
static void
client_dispatch(struct imsg *imsg, void *arg)
client_dispatch(struct imsg *imsg, __unused void *arg)
{
if (imsg == NULL) {
client_exitreason = CLIENT_EXIT_LOST_SERVER;
@@ -546,12 +546,12 @@ client_dispatch(struct imsg *imsg, void *arg)
if (client_attached)
client_dispatch_attached(imsg);
else
client_dispatch_wait(imsg, arg);
client_dispatch_wait(imsg);
}
/* Dispatch imsgs when in wait state (before MSG_READY). */
static void
client_dispatch_wait(struct imsg *imsg, const char *shellcmd)
client_dispatch_wait(struct imsg *imsg)
{
char *data;
ssize_t datalen;
@@ -630,8 +630,7 @@ client_dispatch_wait(struct imsg *imsg, const char *shellcmd)
if (datalen == 0 || data[datalen - 1] != '\0')
fatalx("bad MSG_SHELL string");
clear_signals(0);
client_exec(data, shellcmd);
client_exec(data, shell_command);
/* NOTREACHED */
case MSG_DETACH:
case MSG_DETACHKILL:

View File

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

View File

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

View File

@@ -1,101 +0,0 @@
/* $OpenBSD$ */
/*
* Copyright (c) 2010 Nicholas Marriott <nicholas.marriott@gmail.com>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER
* IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
* OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include <sys/types.h>
#include <ctype.h>
#include <stdlib.h>
#include "tmux.h"
/*
* Enter choice mode to choose a buffer.
*/
#define CHOOSE_BUFFER_TEMPLATE \
"#{buffer_name}: #{buffer_size} bytes: #{buffer_sample}"
static enum cmd_retval cmd_choose_buffer_exec(struct cmd *,
struct cmdq_item *);
const struct cmd_entry cmd_choose_buffer_entry = {
.name = "choose-buffer",
.alias = NULL,
.args = { "F:t:", 0, 1 },
.usage = CMD_TARGET_WINDOW_USAGE " [-F format] [template]",
.target = { 't', CMD_FIND_WINDOW, 0 },
.flags = 0,
.exec = cmd_choose_buffer_exec
};
static enum cmd_retval
cmd_choose_buffer_exec(struct cmd *self, struct cmdq_item *item)
{
struct args *args = self->args;
struct client *c = cmd_find_client(item, NULL, 1);
struct winlink *wl = item->target.wl;
struct window_choose_data *cdata;
struct paste_buffer *pb;
char *action, *action_data;
const char *template;
u_int idx;
if (c == NULL) {
cmdq_error(item, "no client available");
return (CMD_RETURN_ERROR);
}
if ((template = args_get(args, 'F')) == NULL)
template = CHOOSE_BUFFER_TEMPLATE;
if (paste_get_top(NULL) == NULL)
return (CMD_RETURN_NORMAL);
if (window_pane_set_mode(wl->window->active, &window_choose_mode) != 0)
return (CMD_RETURN_NORMAL);
if (args->argc != 0)
action = xstrdup(args->argv[0]);
else
action = xstrdup("paste-buffer -b '%%'");
idx = 0;
pb = NULL;
while ((pb = paste_walk(pb)) != NULL) {
cdata = window_choose_data_create(TREE_OTHER, c, c->session);
cdata->idx = idx;
cdata->ft_template = xstrdup(template);
format_defaults_paste_buffer(cdata->ft, pb);
xasprintf(&action_data, "%s", paste_buffer_name(pb));
cdata->command = cmd_template_replace(action, action_data, 1);
free(action_data);
window_choose_add(wl->window->active, cdata);
idx++;
}
free(action);
window_choose_ready(wl->window->active, 0, NULL);
return (CMD_RETURN_NORMAL);
}

View File

@@ -1,135 +0,0 @@
/* $OpenBSD$ */
/*
* Copyright (c) 2009 Nicholas Marriott <nicholas.marriott@gmail.com>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER
* IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
* OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include <sys/types.h>
#include <ctype.h>
#include <stdlib.h>
#include "tmux.h"
/*
* Enter choice mode to choose a client.
*/
#define CHOOSE_CLIENT_TEMPLATE \
"#{client_name}: #{session_name} " \
"[#{client_width}x#{client_height} #{client_termname}]" \
"#{?client_utf8, (utf8),}#{?client_readonly, (ro),} " \
"(last used #{t:client_activity})"
static enum cmd_retval cmd_choose_client_exec(struct cmd *,
struct cmdq_item *);
static void cmd_choose_client_callback(struct window_choose_data *);
const struct cmd_entry cmd_choose_client_entry = {
.name = "choose-client",
.alias = NULL,
.args = { "F:t:", 0, 1 },
.usage = CMD_TARGET_WINDOW_USAGE " [-F format] [template]",
.target = { 't', CMD_FIND_WINDOW, 0 },
.flags = 0,
.exec = cmd_choose_client_exec
};
struct cmd_choose_client_data {
struct client *client;
};
static enum cmd_retval
cmd_choose_client_exec(struct cmd *self, struct cmdq_item *item)
{
struct args *args = self->args;
struct client *c = cmd_find_client(item, NULL, 1);
struct client *c1;
struct window_choose_data *cdata;
struct winlink *wl = item->target.wl;
const char *template;
char *action;
u_int idx, cur;
if (c == NULL) {
cmdq_error(item, "no client available");
return (CMD_RETURN_ERROR);
}
if (window_pane_set_mode(wl->window->active, &window_choose_mode) != 0)
return (CMD_RETURN_NORMAL);
if ((template = args_get(args, 'F')) == NULL)
template = CHOOSE_CLIENT_TEMPLATE;
if (args->argc != 0)
action = xstrdup(args->argv[0]);
else
action = xstrdup("detach-client -t '%%'");
cur = idx = 0;
TAILQ_FOREACH(c1, &clients, entry) {
if (c1->session == NULL)
continue;
if (c1 == item->client)
cur = idx;
cdata = window_choose_data_create(TREE_OTHER, c, c->session);
cdata->idx = idx;
cdata->ft_template = xstrdup(template);
format_add(cdata->ft, "line", "%u", idx);
format_defaults(cdata->ft, c1, NULL, NULL, NULL);
cdata->command = cmd_template_replace(action, c1->name, 1);
window_choose_add(wl->window->active, cdata);
idx++;
}
free(action);
window_choose_ready(wl->window->active, cur,
cmd_choose_client_callback);
return (CMD_RETURN_NORMAL);
}
static void
cmd_choose_client_callback(struct window_choose_data *cdata)
{
struct client *c;
u_int idx;
if (cdata == NULL)
return;
if (cdata->start_client->flags & CLIENT_DEAD)
return;
idx = 0;
TAILQ_FOREACH(c, &clients, entry) {
if (idx == cdata->idx)
break;
idx++;
}
if (c == NULL || c->session == NULL)
return;
window_choose_data_run(cdata);
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -95,6 +95,22 @@ cmd_find_try_TMUX(struct client *c)
return (session_find_by_id(session));
}
/* Find pane containing client if any. */
static struct window_pane *
cmd_find_inside_pane(struct client *c)
{
struct window_pane *wp;
if (c == NULL)
return (NULL);
RB_FOREACH(wp, window_pane_tree, &all_window_panes) {
if (strcmp(wp->tty, c->ttyname) == 0)
break;
}
return (wp);
}
/* Is this client better? */
static int
cmd_find_client_better(struct client *c, struct client *than)
@@ -493,7 +509,7 @@ cmd_find_get_pane(struct cmd_find_state *fs, const char *pane, int only)
/* Check for pane ids starting with %. */
if (*pane == '%') {
fs->wp = window_pane_find_by_id_str(pane);
if (fs->wp == NULL || window_pane_outside(fs->wp))
if (fs->wp == NULL)
return (-1);
fs->w = fs->wp->window;
return (cmd_find_best_session_with_window(fs));
@@ -530,7 +546,7 @@ cmd_find_get_pane_with_session(struct cmd_find_state *fs, const char *pane)
/* Check for pane ids starting with %. */
if (*pane == '%') {
fs->wp = window_pane_find_by_id_str(pane);
if (fs->wp == NULL || window_pane_outside(fs->wp))
if (fs->wp == NULL)
return (-1);
fs->w = fs->wp->window;
return (cmd_find_best_winlink_with_window(fs));
@@ -562,7 +578,7 @@ cmd_find_get_pane_with_window(struct cmd_find_state *fs, const char *pane)
/* Check for pane ids starting with %. */
if (*pane == '%') {
fs->wp = window_pane_find_by_id_str(pane);
if (fs->wp == NULL || window_pane_outside(fs->wp))
if (fs->wp == NULL)
return (-1);
if (fs->wp->window != fs->w)
return (-1);
@@ -574,27 +590,27 @@ cmd_find_get_pane_with_window(struct cmd_find_state *fs, const char *pane)
if (fs->w->last == NULL)
return (-1);
fs->wp = fs->w->last;
if (fs->wp == NULL || window_pane_outside(fs->wp))
if (fs->wp == NULL)
return (-1);
return (0);
} else if (strcmp(pane, "{up-of}") == 0) {
fs->wp = window_pane_find_up(fs->w->active);
if (fs->wp == NULL || window_pane_outside(fs->wp))
if (fs->wp == NULL)
return (-1);
return (0);
} else if (strcmp(pane, "{down-of}") == 0) {
fs->wp = window_pane_find_down(fs->w->active);
if (fs->wp == NULL || window_pane_outside(fs->wp))
if (fs->wp == NULL)
return (-1);
return (0);
} else if (strcmp(pane, "{left-of}") == 0) {
fs->wp = window_pane_find_left(fs->w->active);
if (fs->wp == NULL || window_pane_outside(fs->wp))
if (fs->wp == NULL)
return (-1);
return (0);
} else if (strcmp(pane, "{right-of}") == 0) {
fs->wp = window_pane_find_right(fs->w->active);
if (fs->wp == NULL || window_pane_outside(fs->wp))
if (fs->wp == NULL)
return (-1);
return (0);
}
@@ -610,7 +626,7 @@ cmd_find_get_pane_with_window(struct cmd_find_state *fs, const char *pane)
fs->wp = window_pane_next_by_number(fs->w, wp, n);
else
fs->wp = window_pane_previous_by_number(fs->w, wp, n);
if (fs->wp != NULL && !window_pane_outside(fs->wp))
if (fs->wp != NULL)
return (0);
}
@@ -618,13 +634,13 @@ cmd_find_get_pane_with_window(struct cmd_find_state *fs, const char *pane)
idx = strtonum(pane, 0, INT_MAX, &errstr);
if (errstr == NULL) {
fs->wp = window_pane_at_index(fs->w, idx);
if (fs->wp != NULL && !window_pane_outside(fs->wp))
if (fs->wp != NULL)
return (0);
}
/* Try as a description. */
fs->wp = window_find_string(fs->w, pane);
if (fs->wp != NULL && !window_pane_outside(fs->wp))
if (fs->wp != NULL)
return (0);
return (-1);
@@ -672,9 +688,7 @@ cmd_find_valid_state(struct cmd_find_state *fs)
if (fs->w != fs->wl->window)
return (0);
if (!window_has_pane(fs->w, fs->wp))
return (0);
return (!window_pane_outside(fs->wp));
return (window_has_pane(fs->w, fs->wp));
}
/* Copy a state. */
@@ -713,9 +727,9 @@ cmd_find_log_state(const char *prefix, struct cmd_find_state *fs)
/* Find state from a session. */
void
cmd_find_from_session(struct cmd_find_state *fs, struct session *s)
cmd_find_from_session(struct cmd_find_state *fs, struct session *s, int flags)
{
cmd_find_clear_state(fs, 0);
cmd_find_clear_state(fs, flags);
fs->s = s;
fs->wl = fs->s->curw;
@@ -727,9 +741,9 @@ cmd_find_from_session(struct cmd_find_state *fs, struct session *s)
/* Find state from a winlink. */
void
cmd_find_from_winlink(struct cmd_find_state *fs, struct winlink *wl)
cmd_find_from_winlink(struct cmd_find_state *fs, struct winlink *wl, int flags)
{
cmd_find_clear_state(fs, 0);
cmd_find_clear_state(fs, flags);
fs->s = wl->session;
fs->wl = wl;
@@ -742,14 +756,14 @@ cmd_find_from_winlink(struct cmd_find_state *fs, struct winlink *wl)
/* Find state from a session and window. */
int
cmd_find_from_session_window(struct cmd_find_state *fs, struct session *s,
struct window *w)
struct window *w, int flags)
{
cmd_find_clear_state(fs, 0);
cmd_find_clear_state(fs, flags);
fs->s = s;
fs->w = w;
if (cmd_find_best_winlink_with_window(fs) != 0) {
cmd_find_clear_state(fs, 0);
cmd_find_clear_state(fs, flags);
return (-1);
}
fs->wp = fs->w->active;
@@ -760,17 +774,17 @@ cmd_find_from_session_window(struct cmd_find_state *fs, struct session *s,
/* Find state from a window. */
int
cmd_find_from_window(struct cmd_find_state *fs, struct window *w)
cmd_find_from_window(struct cmd_find_state *fs, struct window *w, int flags)
{
cmd_find_clear_state(fs, 0);
cmd_find_clear_state(fs, flags);
fs->w = w;
if (cmd_find_best_session_with_window(fs) != 0) {
cmd_find_clear_state(fs, 0);
cmd_find_clear_state(fs, flags);
return (-1);
}
if (cmd_find_best_winlink_with_window(fs) != 0) {
cmd_find_clear_state(fs, 0);
cmd_find_clear_state(fs, flags);
return (-1);
}
fs->wp = fs->w->active;
@@ -782,9 +796,9 @@ cmd_find_from_window(struct cmd_find_state *fs, struct window *w)
/* Find state from a winlink and pane. */
void
cmd_find_from_winlink_pane(struct cmd_find_state *fs, struct winlink *wl,
struct window_pane *wp)
struct window_pane *wp, int flags)
{
cmd_find_clear_state(fs, 0);
cmd_find_clear_state(fs, flags);
fs->s = wl->session;
fs->wl = wl;
@@ -797,14 +811,10 @@ cmd_find_from_winlink_pane(struct cmd_find_state *fs, struct winlink *wl,
/* Find state from a pane. */
int
cmd_find_from_pane(struct cmd_find_state *fs, struct window_pane *wp)
cmd_find_from_pane(struct cmd_find_state *fs, struct window_pane *wp, int flags)
{
if (cmd_find_from_window(fs, wp->window) != 0)
if (cmd_find_from_window(fs, wp->window, flags) != 0)
return (-1);
if (window_pane_outside(wp)) {
cmd_find_clear_state(fs, 0);
return (-1);
}
fs->wp = wp;
cmd_find_log_state(__func__, fs);
@@ -813,13 +823,13 @@ cmd_find_from_pane(struct cmd_find_state *fs, struct window_pane *wp)
/* Find state from nothing. */
int
cmd_find_from_nothing(struct cmd_find_state *fs)
cmd_find_from_nothing(struct cmd_find_state *fs, int flags)
{
cmd_find_clear_state(fs, 0);
cmd_find_clear_state(fs, flags);
fs->s = cmd_find_best_session(NULL, 0, fs->flags);
fs->s = cmd_find_best_session(NULL, 0, flags);
if (fs->s == NULL) {
cmd_find_clear_state(fs, 0);
cmd_find_clear_state(fs, flags);
return (-1);
}
fs->wl = fs->s->curw;
@@ -833,16 +843,16 @@ cmd_find_from_nothing(struct cmd_find_state *fs)
/* Find state from mouse. */
int
cmd_find_from_mouse(struct cmd_find_state *fs, struct mouse_event *m)
cmd_find_from_mouse(struct cmd_find_state *fs, struct mouse_event *m, int flags)
{
cmd_find_clear_state(fs, 0);
cmd_find_clear_state(fs, flags);
if (!m->valid)
return (-1);
fs->wp = cmd_mouse_pane(m, &fs->s, &fs->wl);
if (fs->wp == NULL) {
cmd_find_clear_state(fs, 0);
cmd_find_clear_state(fs, flags);
return (-1);
}
fs->w = fs->wl->window;
@@ -853,7 +863,7 @@ cmd_find_from_mouse(struct cmd_find_state *fs, struct mouse_event *m)
/* Find state from client. */
int
cmd_find_from_client(struct cmd_find_state *fs, struct client *c)
cmd_find_from_client(struct cmd_find_state *fs, struct client *c, int flags)
{
struct session *s;
struct winlink *wl;
@@ -861,23 +871,20 @@ cmd_find_from_client(struct cmd_find_state *fs, struct client *c)
/* If no client, treat as from nothing. */
if (c == NULL)
return (cmd_find_from_nothing(fs));
return (cmd_find_from_nothing(fs, flags));
/* If this is an attached client, all done. */
if (c->session != NULL) {
cmd_find_from_session(fs, c->session);
cmd_find_from_session(fs, c->session, flags);
return (0);
}
cmd_find_clear_state(fs, 0);
cmd_find_clear_state(fs, flags);
/*
* If this is an unattached client running in a pane, we can use that
* to limit the list of sessions to those containing that pane.
*/
RB_FOREACH(wp, window_pane_tree, &all_window_panes) {
if (strcmp(wp->tty, c->ttyname) == 0)
break;
}
wp = cmd_find_inside_pane(c);
if (wp == NULL)
goto unknown_pane;
@@ -930,12 +937,12 @@ unknown_pane:
*/
s = cmd_find_try_TMUX(c);
if (s != NULL) {
cmd_find_from_session(fs, s);
cmd_find_from_session(fs, s, flags);
return (0);
}
/* Otherwise we need to guess. */
return (cmd_find_from_nothing(fs));
return (cmd_find_from_nothing(fs, flags));
}
/*
@@ -949,7 +956,7 @@ cmd_find_target(struct cmd_find_state *fs, struct cmdq_item *item,
struct mouse_event *m;
struct cmd_find_state current;
char *colon, *period, *copy = NULL;
const char *session, *window, *pane;
const char *session, *window, *pane, *s;
int window_only = 0, pane_only = 0;
/* Can fail flag implies quiet. */
@@ -957,10 +964,18 @@ cmd_find_target(struct cmd_find_state *fs, struct cmdq_item *item,
flags |= CMD_FIND_QUIET;
/* Log the arguments. */
if (target == NULL)
log_debug("%s: target none, type %d", __func__, type);
if (type == CMD_FIND_PANE)
s = "pane";
else if (type == CMD_FIND_WINDOW)
s = "window";
else if (type == CMD_FIND_SESSION)
s = "session";
else
log_debug("%s: target %s, type %d", __func__, target, type);
s = "unknown";
if (target == NULL)
log_debug("%s: target none, type %s", __func__, s);
else
log_debug("%s: target %s, type %s", __func__, target, s);
log_debug("%s: item %p, flags %#x", __func__, item, flags);
/* Clear new state. */
@@ -973,11 +988,14 @@ cmd_find_target(struct cmd_find_state *fs, struct cmdq_item *item,
} else if (cmd_find_valid_state(&item->shared->current)) {
fs->current = &item->shared->current;
log_debug("%s: current is from queue", __func__);
} else if (cmd_find_from_client(&current, item->client) == 0) {
} else if (cmd_find_from_client(&current, item->client, flags) == 0) {
fs->current = &current;
log_debug("%s: current is from client", __func__);
} else
} else {
if (~flags & CMD_FIND_QUIET)
cmdq_error(item, "no current target");
goto error;
}
if (!cmd_find_valid_state(fs->current))
fatalx("invalid current find state");
@@ -991,7 +1009,7 @@ 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 && !window_pane_outside(fs->wp))
if (fs->wp != NULL)
fs->w = fs->wl->window;
break;
case CMD_FIND_WINDOW:
@@ -1220,30 +1238,46 @@ no_pane:
goto error;
}
/* Find the current client. */
static struct client *
cmd_find_current_client(struct cmdq_item *item, int quiet)
{
struct client *c;
struct session *s;
struct window_pane *wp;
struct cmd_find_state fs;
if (item->client != NULL && item->client->session != NULL)
return (item->client);
c = NULL;
if ((wp = cmd_find_inside_pane(item->client)) != NULL) {
cmd_find_clear_state(&fs, CMD_FIND_QUIET);
fs.w = wp->window;
if (cmd_find_best_session_with_window(&fs) == 0)
c = cmd_find_best_client(fs.s);
} else {
s = cmd_find_best_session(NULL, 0, CMD_FIND_QUIET);
if (s != NULL)
c = cmd_find_best_client(s);
}
if (c == NULL && !quiet)
cmdq_error(item, "no current client");
log_debug("%s: no target, return %p", __func__, c);
return (c);
}
/* Find the target client or report an error and return NULL. */
struct client *
cmd_find_client(struct cmdq_item *item, const char *target, int quiet)
{
struct client *c;
struct session *s;
char *copy;
size_t size;
/* A NULL argument means the current client. */
if (target == NULL) {
c = NULL;
if (item->client != NULL && item->client->session != NULL)
c = item->client;
else {
s = cmd_find_best_session(NULL, 0, CMD_FIND_QUIET);
if (s != NULL)
c = cmd_find_best_client(s);
}
if (c == NULL && !quiet)
cmdq_error(item, "no current client");
log_debug("%s: no target, return %p", __func__, c);
return (c);
}
if (target == NULL)
return (cmd_find_current_client(item, quiet));
copy = xstrdup(target);
/* Trim a single trailing colon if any. */

View File

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

View File

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

View File

@@ -74,9 +74,10 @@ cmd_new_session_exec(struct cmd *self, struct cmdq_item *item)
struct termios tio, *tiop;
struct session_group *sg;
const char *newname, *errstr, *template, *group, *prefix;
const char *path, *cmd, *cwd, *to_free = NULL;
char **argv, *cause, *cp;
const char *path, *cmd, *cwd;
char **argv, *cause, *cp, *to_free = NULL;
int detached, already_attached, idx, argc;
int is_control = 0;
u_int sx, sy;
struct environ_entry *envent;
struct cmd_find_state fs;
@@ -139,6 +140,8 @@ cmd_new_session_exec(struct cmd *self, struct cmdq_item *item)
detached = args_has(args, 'd');
if (c == NULL)
detached = 1;
else if (c->flags & CLIENT_CONTROL)
is_control = 1;
/* Is this client already attached? */
already_attached = 0;
@@ -148,7 +151,8 @@ cmd_new_session_exec(struct cmd *self, struct cmdq_item *item)
/* Get the new session working directory. */
if (args_has(args, 'c')) {
cwd = args_get(args, 'c');
to_free = cwd = format_single(item, cwd, c, NULL, NULL, NULL);
to_free = format_single(item, cwd, c, NULL, NULL, NULL);
cwd = to_free;
} else if (c != NULL && c->session == NULL && c->cwd != NULL)
cwd = c->cwd;
else
@@ -185,29 +189,31 @@ cmd_new_session_exec(struct cmd *self, struct cmdq_item *item)
}
/* Find new session size. */
if (c != NULL) {
if (!detached) {
sx = c->tty.sx;
sy = c->tty.sy;
if (!is_control &&
sy > 0 &&
options_get_number(global_s_options, "status"))
sy--;
} else {
sx = 80;
sy = 24;
}
if (detached && args_has(args, 'x')) {
if ((is_control || detached) && args_has(args, 'x')) {
sx = strtonum(args_get(args, 'x'), 1, USHRT_MAX, &errstr);
if (errstr != NULL) {
cmdq_error(item, "width %s", errstr);
goto error;
}
}
if (detached && args_has(args, 'y')) {
if ((is_control || detached) && args_has(args, 'y')) {
sy = strtonum(args_get(args, 'y'), 1, USHRT_MAX, &errstr);
if (errstr != NULL) {
cmdq_error(item, "height %s", errstr);
goto error;
}
}
if (sy > 0 && options_get_number(global_s_options, "status"))
sy--;
if (sx == 0)
sx = 1;
if (sy == 0)
@@ -319,19 +325,16 @@ cmd_new_session_exec(struct cmd *self, struct cmdq_item *item)
if (!detached) {
c->flags |= CLIENT_ATTACHED;
cmd_find_from_session(&item->shared->current, s);
cmd_find_from_session(&item->shared->current, s, 0);
}
if (to_free != NULL)
free((void *)to_free);
cmd_find_from_session(&fs, s);
cmd_find_from_session(&fs, s, 0);
hooks_insert(s->hooks, item, &fs, "after-new-session");
free(to_free);
return (CMD_RETURN_NORMAL);
error:
if (to_free != NULL)
free((void *)to_free);
free(to_free);
return (CMD_RETURN_ERROR);
}

View File

@@ -57,8 +57,8 @@ cmd_new_window_exec(struct cmd *self, struct cmdq_item *item)
struct winlink *wl = item->target.wl;
struct client *c = cmd_find_client(item, NULL, 1);
int idx = item->target.idx;
const char *cmd, *path, *template, *cwd, *to_free;
char **argv, *cause, *cp;
const char *cmd, *path, *template, *cwd;
char **argv, *cause, *cp, *to_free = NULL;
int argc, detached;
struct environ_entry *envent;
struct cmd_find_state fs;
@@ -93,10 +93,10 @@ cmd_new_window_exec(struct cmd *self, struct cmdq_item *item)
if (envent != NULL)
path = envent->value;
to_free = NULL;
if (args_has(args, 'c')) {
cwd = args_get(args, 'c');
to_free = cwd = format_single(item, cwd, c, s, NULL, NULL);
to_free = format_single(item, cwd, c, s, NULL, NULL);
cwd = to_free;
} else if (item->client != NULL && item->client->session == NULL)
cwd = item->client->cwd;
else
@@ -133,7 +133,7 @@ cmd_new_window_exec(struct cmd *self, struct cmdq_item *item)
}
if (!detached) {
session_select(s, wl->idx);
cmd_find_from_winlink(current, wl);
cmd_find_from_winlink(current, wl, 0);
server_redraw_session_group(s);
} else
server_status_session_group(s);
@@ -146,16 +146,13 @@ cmd_new_window_exec(struct cmd *self, struct cmdq_item *item)
free(cp);
}
if (to_free != NULL)
free((void *)to_free);
cmd_find_from_winlink(&fs, wl);
cmd_find_from_winlink(&fs, wl, 0);
hooks_insert(s->hooks, item, &fs, "after-new-window");
free(to_free);
return (CMD_RETURN_NORMAL);
error:
if (to_free != NULL)
free((void *)to_free);
free(to_free);
return (CMD_RETURN_ERROR);
}

View File

@@ -21,6 +21,7 @@
#include <errno.h>
#include <fcntl.h>
#include <signal.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
@@ -34,6 +35,7 @@
static enum cmd_retval cmd_pipe_pane_exec(struct cmd *, struct cmdq_item *);
static void cmd_pipe_pane_write_callback(struct bufferevent *, void *);
static void cmd_pipe_pane_error_callback(struct bufferevent *, short, void *);
const struct cmd_entry cmd_pipe_pane_entry = {
@@ -60,6 +62,7 @@ cmd_pipe_pane_exec(struct cmd *self, struct cmdq_item *item)
char *cmd;
int old_fd, pipe_fd[2], null_fd;
struct format_tree *ft;
sigset_t set, oldset;
/* Destroy the old pipe. */
old_fd = wp->pipe_fd;
@@ -67,6 +70,11 @@ cmd_pipe_pane_exec(struct cmd *self, struct cmdq_item *item)
bufferevent_free(wp->pipe_event);
close(wp->pipe_fd);
wp->pipe_fd = -1;
if (window_pane_destroy_ready(wp)) {
server_destroy_pane(wp, 1);
return (CMD_RETURN_NORMAL);
}
}
/* If no pipe command, that is enough. */
@@ -95,16 +103,20 @@ cmd_pipe_pane_exec(struct cmd *self, struct cmdq_item *item)
format_free(ft);
/* Fork the child. */
sigfillset(&set);
sigprocmask(SIG_BLOCK, &set, &oldset);
switch (fork()) {
case -1:
sigprocmask(SIG_SETMASK, &oldset, NULL);
cmdq_error(item, "fork error: %s", strerror(errno));
free(cmd);
return (CMD_RETURN_ERROR);
case 0:
/* Child process. */
proc_clear_signals(server_proc, 1);
sigprocmask(SIG_SETMASK, &oldset, NULL);
close(pipe_fd[0]);
clear_signals(1);
if (dup2(pipe_fd[1], STDIN_FILENO) == -1)
_exit(1);
@@ -125,13 +137,15 @@ cmd_pipe_pane_exec(struct cmd *self, struct cmdq_item *item)
_exit(1);
default:
/* Parent process. */
sigprocmask(SIG_SETMASK, &oldset, NULL);
close(pipe_fd[1]);
wp->pipe_fd = pipe_fd[0];
wp->pipe_off = EVBUFFER_LENGTH(wp->event->input);
wp->pipe_event = bufferevent_new(wp->pipe_fd,
NULL, NULL, cmd_pipe_pane_error_callback, wp);
wp->pipe_event = bufferevent_new(wp->pipe_fd, NULL,
cmd_pipe_pane_write_callback, cmd_pipe_pane_error_callback,
wp);
bufferevent_enable(wp->pipe_event, EV_WRITE);
setblocking(wp->pipe_fd, 0);
@@ -141,13 +155,28 @@ cmd_pipe_pane_exec(struct cmd *self, struct cmdq_item *item)
}
}
static void
cmd_pipe_pane_write_callback(__unused struct bufferevent *bufev, void *data)
{
struct window_pane *wp = data;
log_debug("%%%u pipe empty", wp->id);
if (window_pane_destroy_ready(wp))
server_destroy_pane(wp, 1);
}
static void
cmd_pipe_pane_error_callback(__unused struct bufferevent *bufev,
__unused short what, void *data)
{
struct window_pane *wp = data;
log_debug("%%%u pipe error", wp->id);
bufferevent_free(wp->pipe_event);
close(wp->pipe_fd);
wp->pipe_fd = -1;
if (window_pane_destroy_ready(wp))
server_destroy_pane(wp, 1);
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -68,14 +68,14 @@ cmd_run_shell_print(struct job *job, const char *msg)
cmdq_print(cdata->item, "%s", msg);
return;
}
if (cmd_find_from_nothing(&fs) != 0)
if (cmd_find_from_nothing(&fs, 0) != 0)
return;
wp = fs.wp;
if (wp == NULL)
return;
}
if (window_pane_set_mode(wp, &window_copy_mode) == 0)
if (window_pane_set_mode(wp, &window_copy_mode, NULL, NULL) == 0)
window_copy_init_for_output(wp);
if (wp->mode == &window_copy_mode)
window_copy_add(wp, "%s", msg);

View File

@@ -30,8 +30,8 @@ const struct cmd_entry cmd_select_pane_entry = {
.name = "select-pane",
.alias = "selectp",
.args = { "DdegLlMmP:Rt:U", 0, 0 },
.usage = "[-DdegLlMmRU] [-P style] " CMD_TARGET_PANE_USAGE,
.args = { "DdegLlMmP:RT:t:U", 0, 0 },
.usage = "[-DdegLlMmRU] [-P style] [-T title] " CMD_TARGET_PANE_USAGE,
.target = { 't', CMD_FIND_PANE, 0 },
@@ -77,7 +77,7 @@ cmd_select_pane_exec(struct cmd *self, struct cmdq_item *item)
server_unzoom_window(w);
window_redraw_active_switch(w, lastwp);
if (window_set_active_pane(w, lastwp)) {
cmd_find_from_winlink(current, wl);
cmd_find_from_winlink(current, wl, 0);
server_status_window(w);
server_redraw_window_borders(w);
}
@@ -147,6 +147,11 @@ cmd_select_pane_exec(struct cmd *self, struct cmdq_item *item)
return (CMD_RETURN_NORMAL);
}
if (args_has(self->args, 'T')) {
screen_set_title(&wp->base, args_get(self->args, 'T'));
server_status_window(wp->window);
}
if (wp == w->active)
return (CMD_RETURN_NORMAL);
server_unzoom_window(wp->window);
@@ -156,7 +161,8 @@ cmd_select_pane_exec(struct cmd *self, struct cmdq_item *item)
}
window_redraw_active_switch(w, wp);
if (window_set_active_pane(w, wp)) {
cmd_find_from_winlink(current, wl);
cmd_find_from_winlink_pane(current, wl, wp, 0);
hooks_insert(s->hooks, item, current, "after-select-pane");
server_status_window(w);
server_redraw_window_borders(w);
}

View File

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

View File

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

View File

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

View File

@@ -127,25 +127,36 @@ cmd_show_options_one(struct cmd *self, struct cmdq_item *item,
struct options *oo)
{
struct 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;
const char *name = args->argv[0];
char *name;
name = format_single(item, args->argv[0], c, s, wl, NULL);
o = options_match_get(oo, name, &idx, 1, &ambiguous);
if (o == NULL) {
if (args_has(args, 'q'))
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)
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);
}

View File

@@ -60,8 +60,8 @@ cmd_split_window_exec(struct cmd *self, struct cmdq_item *item)
struct window *w = wl->window;
struct window_pane *wp = item->target.wp, *new_wp = NULL;
struct environ *env;
const char *cmd, *path, *shell, *template, *cwd, *to_free;
char **argv, *cause, *new_cause, *cp;
const char *cmd, *path, *shell, *template, *cwd;
char **argv, *cause, *new_cause, *cp, *to_free = NULL;
u_int hlimit;
int argc, size, percentage;
enum layout_type type;
@@ -85,10 +85,10 @@ cmd_split_window_exec(struct cmd *self, struct cmdq_item *item)
argv = args->argv;
}
to_free = NULL;
if (args_has(args, 'c')) {
cwd = args_get(args, 'c');
to_free = cwd = format_single(item, cwd, c, s, NULL, NULL);
to_free = format_single(item, cwd, c, s, NULL, NULL);
cwd = to_free;
} else if (item->client != NULL && item->client->session == NULL)
cwd = item->client->cwd;
else
@@ -133,7 +133,7 @@ cmd_split_window_exec(struct cmd *self, struct cmdq_item *item)
goto error;
}
new_wp = window_add_pane(w, wp, args_has(args, 'b'), hlimit);
layout_assign_pane(lc, new_wp);
layout_make_leaf(lc, new_wp);
path = NULL;
if (item->client != NULL && item->client->session == NULL)
@@ -151,12 +151,13 @@ cmd_split_window_exec(struct cmd *self, struct cmdq_item *item)
}
environ_free(env);
layout_fix_panes(w, w->sx, w->sy);
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);
cmd_find_from_session(current, s, 0);
server_redraw_session(s);
} else
server_status_session(s);
@@ -170,12 +171,10 @@ cmd_split_window_exec(struct cmd *self, struct cmdq_item *item)
}
notify_window("window-layout-changed", w);
if (to_free != NULL)
free((void *)to_free);
cmd_find_from_winlink_pane(&fs, wl, new_wp);
cmd_find_from_winlink_pane(&fs, wl, new_wp, 0);
hooks_insert(s->hooks, item, &fs, "after-split-window");
free(to_free);
return (CMD_RETURN_NORMAL);
error:
@@ -186,7 +185,6 @@ error:
cmdq_error(item, "create pane failed: %s", cause);
free(cause);
if (to_free != NULL)
free((void *)to_free);
free(to_free);
return (CMD_RETURN_ERROR);
}

View File

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

View File

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

4
cmd.c
View File

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

View File

@@ -261,6 +261,11 @@ size_t strnlen(const char *, size_t);
char *strndup(const char *, size_t);
#endif
#ifndef HAVE_MEMMEM
/* memmem.c */
void *memmem(const void *, size_t, const void *, size_t);
#endif
#ifndef HAVE_DAEMON
/* daemon.c */
int daemon(int, int);

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

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

View File

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

65
compat/memmem.c Normal file
View File

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

View File

@@ -1,6 +1,6 @@
# configure.ac
AC_INIT(tmux, 2.5)
AC_INIT(tmux, 2.6)
AC_PREREQ([2.60])
AC_CONFIG_AUX_DIR(etc)
@@ -104,10 +104,10 @@ AC_REPLACE_FUNCS([ \
closefrom \
explicit_bzero \
fgetln \
fparseln \
freezero \
getdtablecount \
getprogname \
memmem \
recallocarray \
reallocarray \
setenv \
@@ -176,7 +176,8 @@ if test "x$found_ncurses" = xno; then
)
fi
if test "x$found_ncurses" = xyes; then
CPPFLAGS="$LIBNCURSES_CFLAGS $LIBTINFO_CFLAGS $CPPFLAGS"
AM_CFLAGS="$LIBNCURSES_CFLAGS $LIBTINFO_CFLAGS $AM_CFLAGS"
CFLAGS="$LIBNCURSES_CFLAGS $LIBTINFO_CFLAGS $CFLAGS"
LIBS="$LIBNCURSES_LIBS $LIBTINFO_LIBS $LIBS"
else
# pkg-config didn't work, try ncurses.
@@ -454,6 +455,14 @@ 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
@@ -562,8 +571,15 @@ case "$host_os" in
;;
*darwin*)
AC_MSG_RESULT(darwin)
AC_DEFINE(BROKEN_CMSG_FIRSTHDR)
PLATFORM=darwin
#
# OS X CMSG_FIRSTHDR is broken, so redefine it with a working
# one. daemon works but has some stupid side effects, so use
# our internal version which has a workaround.
#
AC_DEFINE(BROKEN_CMSG_FIRSTHDR)
AC_LIBOBJ(daemon)
AC_LIBOBJ(daemon-darwin)
;;
*dragonfly*)
AC_MSG_RESULT(dragonfly)

View File

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

132
format.c
View File

@@ -17,13 +17,11 @@
*/
#include <sys/types.h>
#include <sys/param.h>
#include <sys/wait.h>
#include <ctype.h>
#include <errno.h>
#include <fnmatch.h>
#include <libgen.h>
#include <netdb.h>
#include <stdarg.h>
#include <stdlib.h>
#include <string.h>
@@ -43,28 +41,6 @@ typedef void (*format_cb)(struct format_tree *, struct format_entry *);
static char *format_job_get(struct format_tree *, const char *);
static void format_job_timer(int, short, void *);
static void format_cb_host(struct format_tree *, struct format_entry *);
static void format_cb_host_short(struct format_tree *,
struct format_entry *);
static void format_cb_pid(struct format_tree *, struct format_entry *);
static void format_cb_session_alerts(struct format_tree *,
struct format_entry *);
static void format_cb_window_layout(struct format_tree *,
struct format_entry *);
static void format_cb_window_visible_layout(struct format_tree *,
struct format_entry *);
static void format_cb_start_command(struct format_tree *,
struct format_entry *);
static void format_cb_current_command(struct format_tree *,
struct format_entry *);
static void format_cb_history_bytes(struct format_tree *,
struct format_entry *);
static void format_cb_pane_tabs(struct format_tree *,
struct format_entry *);
static void format_cb_current_path(struct format_tree *,
struct format_entry *);
static char *format_find(struct format_tree *, const char *, int);
static void format_add_cb(struct format_tree *, const char *, format_cb);
static void format_add_tv(struct format_tree *, const char *,
@@ -217,7 +193,6 @@ format_job_update(struct job *job)
struct format_job *fj = job->data;
char *line;
time_t t;
struct client *c;
if ((line = evbuffer_readline(job->event->input)) == NULL)
return;
@@ -226,12 +201,12 @@ format_job_update(struct job *job)
free(fj->out);
fj->out = line;
log_debug("%s: %s: %s", __func__, fj->cmd, fj->out);
log_debug("%s: %p %s: %s", __func__, fj, fj->cmd, fj->out);
t = time (NULL);
t = time(NULL);
if (fj->status && fj->last != t) {
TAILQ_FOREACH(c, &clients, entry)
server_status_client(c);
if (fj->client != NULL)
server_status_client(fj->client);
fj->last = t;
}
}
@@ -256,10 +231,11 @@ format_job_complete(struct job *job)
} else
buf = line;
log_debug("%s: %p %s: %s", __func__, fj, fj->cmd, buf);
if (*buf != '\0' || !fj->updated) {
free(fj->out);
fj->out = buf;
log_debug("%s: %s: %s", __func__, fj->cmd, fj->out);
} else
free(buf);
@@ -551,7 +527,7 @@ format_cb_current_command(struct format_tree *ft, struct format_entry *fe)
}
/* Callback for pane_current_path. */
void
static void
format_cb_current_path(struct format_tree *ft, struct format_entry *fe)
{
struct window_pane *wp = ft->wp;
@@ -878,7 +854,7 @@ format_choose(char *s, char **left, char **right)
}
/* Is this true? */
static int
int
format_true(const char *s)
{
if (s != NULL && *s != '\0' && (s[0] != '0' || s[1] != '\0'))
@@ -886,19 +862,17 @@ format_true(const char *s)
return (0);
}
/*
* Replace a key/value pair in buffer. #{blah} is expanded directly,
* #{?blah,a,b} is replace with a if blah exists and is nonzero else b.
*/
/* Replace a key. */
static int
format_replace(struct format_tree *ft, const char *key, size_t keylen,
char **buf, size_t *len, size_t *off)
{
char *copy, *copy0, *endptr, *ptr, *found, *new, *value;
char *from = NULL, *to = NULL, *left, *right;
size_t valuelen, newlen, fromlen, tolen, used;
long limit = 0;
int modifiers = 0, compare = 0;
struct window_pane *wp = ft->wp;
char *copy, *copy0, *endptr, *ptr, *found, *new;
char *value, *from = NULL, *to = NULL, *left, *right;
size_t valuelen, newlen, fromlen, tolen, used;
long limit = 0;
int modifiers = 0, compare = 0, search = 0;
/* Make a copy of the key. */
copy0 = copy = xmalloc(keylen + 1);
@@ -907,6 +881,30 @@ format_replace(struct format_tree *ft, const char *key, size_t keylen,
/* Is there a length limit or whatnot? */
switch (copy[0]) {
case 'm':
if (copy[1] != ':')
break;
compare = -2;
copy += 2;
break;
case 'C':
if (copy[1] != ':')
break;
search = 1;
copy += 2;
break;
case '|':
if (copy[1] != '|' || copy[2] != ':')
break;
compare = -3;
copy += 3;
break;
case '&':
if (copy[1] != '&' || copy[2] != ':')
break;
compare = -4;
copy += 3;
break;
case '!':
if (copy[1] == '=' && copy[2] == ':') {
compare = -1;
@@ -972,16 +970,30 @@ format_replace(struct format_tree *ft, const char *key, size_t keylen,
}
/* Is this a comparison or a conditional? */
if (compare != 0) {
if (search) {
/* Search in pane. */
if (wp == NULL)
value = xstrdup("0");
else
xasprintf(&value, "%u", window_pane_search(wp, copy));
} else if (compare != 0) {
/* Comparison: compare comma-separated left and right. */
if (format_choose(copy, &left, &right) != 0)
goto fail;
left = format_expand(ft, left);
right = format_expand(ft, right);
if (compare == 1 && strcmp(left, right) == 0)
if (compare == -3 &&
(format_true(left) || format_true(right)))
value = xstrdup("1");
else if (compare == -4 &&
(format_true(left) && format_true(right)))
value = xstrdup("1");
else if (compare == 1 && strcmp(left, right) == 0)
value = xstrdup("1");
else if (compare == -1 && strcmp(left, right) != 0)
value = xstrdup("1");
else if (compare == -2 && fnmatch(left, right, 0) == 0)
value = xstrdup("1");
else
value = xstrdup("0");
free(right);
@@ -1220,6 +1232,10 @@ void
format_defaults(struct format_tree *ft, struct client *c, struct session *s,
struct winlink *wl, struct window_pane *wp)
{
format_add(ft, "session_format", "%d", s != NULL);
format_add(ft, "window_format", "%d", wl != NULL);
format_add(ft, "pane_format", "%d", wp != NULL);
if (s == NULL && c != NULL)
s = c->session;
if (wl == NULL && s != NULL)
@@ -1377,7 +1393,7 @@ format_defaults_pane(struct format_tree *ft, struct window_pane *wp)
{
struct grid *gd = wp->base.grid;
u_int idx;
int status, scroll_position;
int status;
if (ft->w == NULL)
ft->w = wp->window;
@@ -1397,6 +1413,7 @@ format_defaults_pane(struct format_tree *ft, struct window_pane *wp)
format_add(ft, "pane_id", "%%%u", wp->id);
format_add(ft, "pane_active", "%d", wp == wp->window->active);
format_add(ft, "pane_input_off", "%d", !!(wp->flags & PANE_INPUTOFF));
format_add(ft, "pane_pipe", "%d", wp->pipe_fd != -1);
status = wp->status;
if (wp->fd == -1 && WIFEXITED(status))
@@ -1408,6 +1425,10 @@ format_defaults_pane(struct format_tree *ft, struct window_pane *wp)
format_add(ft, "pane_top", "%u", wp->yoff);
format_add(ft, "pane_right", "%u", wp->xoff + wp->sx - 1);
format_add(ft, "pane_bottom", "%u", wp->yoff + wp->sy - 1);
format_add(ft, "pane_at_left", "%d", wp->xoff == 0);
format_add(ft, "pane_at_top", "%d", wp->yoff == 0);
format_add(ft, "pane_at_right", "%d", wp->xoff + wp->sx == wp->window->sx);
format_add(ft, "pane_at_bottom", "%d", wp->yoff + wp->sy == wp->window->sy);
}
format_add(ft, "pane_in_mode", "%d", wp->screen != &wp->base);
@@ -1416,8 +1437,8 @@ format_defaults_pane(struct format_tree *ft, struct window_pane *wp)
format_add(ft, "pane_synchronized", "%d",
!!options_get_number(wp->window->options, "synchronize-panes"));
format_add(ft, "pane_search_string", "%s",
window_copy_search_string(wp));
if (wp->searchstr != NULL)
format_add(ft, "pane_search_string", "%s", wp->searchstr);
format_add(ft, "pane_tty", "%s", wp->tty);
format_add(ft, "pane_pid", "%ld", (long) wp->pid);
@@ -1430,9 +1451,7 @@ format_defaults_pane(struct format_tree *ft, struct window_pane *wp)
format_add(ft, "scroll_region_upper", "%u", wp->base.rupper);
format_add(ft, "scroll_region_lower", "%u", wp->base.rlower);
scroll_position = window_copy_scroll_position(wp);
if (scroll_position != -1)
format_add(ft, "scroll_position", "%d", scroll_position);
window_copy_add_formats(wp, ft);
format_add(ft, "alternate_on", "%d", wp->saved_grid ? 1 : 0);
format_add(ft, "alternate_saved_x", "%u", wp->saved_cx);
@@ -1465,12 +1484,17 @@ format_defaults_pane(struct format_tree *ft, struct window_pane *wp)
void
format_defaults_paste_buffer(struct format_tree *ft, struct paste_buffer *pb)
{
size_t bufsize;
char *s;
struct timeval tv;
size_t size;
char *s;
paste_buffer_data(pb, &bufsize);
format_add(ft, "buffer_size", "%zu", bufsize);
timerclear(&tv);
tv.tv_sec = paste_buffer_created(pb);
paste_buffer_data(pb, &size);
format_add(ft, "buffer_size", "%zu", size);
format_add(ft, "buffer_name", "%s", paste_buffer_name(pb));
format_add_tv(ft, "buffer_created", &tv);
s = paste_make_sample(pb);
format_add(ft, "buffer_sample", "%s", s);

View File

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

133
grid.c
View File

@@ -94,6 +94,48 @@ grid_need_extended_cell(const struct grid_cell_entry *gce,
return (0);
}
/* Free up unused extended cells. */
static void
grid_compact_line(struct grid_line *gl)
{
int new_extdsize = 0;
struct grid_cell *new_extddata;
struct grid_cell_entry *gce;
struct grid_cell *gc;
u_int px, idx;
if (gl->extdsize == 0)
return;
for (px = 0; px < gl->cellsize; px++) {
gce = &gl->celldata[px];
if (gce->flags & GRID_FLAG_EXTENDED)
new_extdsize++;
}
if (new_extdsize == 0) {
free(gl->extddata);
gl->extddata = NULL;
gl->extdsize = 0;
return;
}
new_extddata = xreallocarray(NULL, new_extdsize, sizeof *gl->extddata);
idx = 0;
for (px = 0; px < gl->cellsize; px++) {
gce = &gl->celldata[px];
if (gce->flags & GRID_FLAG_EXTENDED) {
gc = &gl->extddata[gce->offset];
memcpy(&new_extddata[idx], gc, sizeof *gc);
gce->offset = idx++;
}
}
free(gl->extddata);
gl->extddata = new_extddata;
gl->extdsize = new_extdsize;
}
/* Set cell as extended. */
static struct grid_cell *
grid_extended_cell(struct grid_line *gl, struct grid_cell_entry *gce,
@@ -162,6 +204,26 @@ grid_cells_equal(const struct grid_cell *gca, const struct grid_cell *gcb)
return (memcmp(gca->data.data, gcb->data.data, gca->data.size) == 0);
}
/* Free one line. */
static void
grid_free_line(struct grid *gd, u_int py)
{
free(gd->linedata[py].celldata);
gd->linedata[py].celldata = NULL;
free(gd->linedata[py].extddata);
gd->linedata[py].extddata = NULL;
}
/* Free several lines. */
static void
grid_free_lines(struct grid *gd, u_int py, u_int ny)
{
u_int yy;
for (yy = py; yy < py + ny; yy++)
grid_free_line(gd, yy);
}
/* Create a new grid. */
struct grid *
grid_create(u_int sx, u_int sy, u_int hlimit)
@@ -187,14 +249,7 @@ grid_create(u_int sx, u_int sy, u_int hlimit)
void
grid_destroy(struct grid *gd)
{
struct grid_line *gl;
u_int yy;
for (yy = 0; yy < gd->hsize + gd->sy; yy++) {
gl = &gd->linedata[yy];
free(gl->celldata);
free(gl->extddata);
}
grid_free_lines(gd, 0, gd->hsize + gd->sy);
free(gd->linedata);
@@ -233,19 +288,28 @@ grid_compare(struct grid *ga, struct grid *gb)
* and shift up.
*/
void
grid_collect_history(struct grid *gd, u_int bg)
grid_collect_history(struct grid *gd)
{
u_int yy;
u_int ny;
if (gd->hsize < gd->hlimit)
if (gd->hsize == 0 || gd->hsize < gd->hlimit)
return;
yy = gd->hlimit / 10;
if (yy < 1)
yy = 1;
ny = gd->hlimit / 10;
if (ny < 1)
ny = 1;
if (ny > gd->hsize)
ny = gd->hsize;
grid_move_lines(gd, 0, yy, gd->hsize + gd->sy - yy, bg);
gd->hsize -= yy;
/*
* Free the lines from 0 to ny then move the remaining lines over
* them.
*/
grid_free_lines(gd, 0, ny);
memmove(&gd->linedata[0], &gd->linedata[ny],
(gd->hsize + gd->sy - ny) * (sizeof *gd->linedata));
gd->hsize -= ny;
if (gd->hscrolled > gd->hsize)
gd->hscrolled = gd->hsize;
}
@@ -265,6 +329,7 @@ grid_scroll_history(struct grid *gd, u_int bg)
grid_empty_line(gd, yy, bg);
gd->hscrolled++;
grid_compact_line(&gd->linedata[gd->hsize]);
gd->hsize++;
}
@@ -272,8 +337,9 @@ grid_scroll_history(struct grid *gd, u_int bg)
void
grid_clear_history(struct grid *gd)
{
grid_clear_lines(gd, 0, gd->hsize, 8);
grid_move_lines(gd, 0, gd->hsize, gd->sy, 8);
grid_free_lines(gd, 0, gd->hsize);
memmove(&gd->linedata[0], &gd->linedata[gd->hsize],
gd->sy * (sizeof *gd->linedata));
gd->hscrolled = 0;
gd->hsize = 0;
@@ -284,9 +350,9 @@ grid_clear_history(struct grid *gd)
/* Scroll a region up, moving the top line into the history. */
void
grid_scroll_history_region(struct grid *gd, u_int upper, u_int lower)
grid_scroll_history_region(struct grid *gd, u_int upper, u_int lower, u_int bg)
{
struct grid_line *gl_history, *gl_upper, *gl_lower;
struct grid_line *gl_history, *gl_upper;
u_int yy;
/* Create a space for a new line. */
@@ -302,14 +368,13 @@ grid_scroll_history_region(struct grid *gd, u_int upper, u_int lower)
upper++;
gl_upper = &gd->linedata[upper];
lower++;
gl_lower = &gd->linedata[lower];
/* Move the line into the history. */
memcpy(gl_history, gl_upper, sizeof *gl_history);
/* Then move the region up and clear the bottom line. */
memmove(gl_upper, gl_upper + 1, (lower - upper) * sizeof *gl_upper);
memset(gl_lower, 0, sizeof *gl_lower);
grid_empty_line(gd, lower, bg);
/* Move the history offset down over the line. */
gd->hscrolled++;
@@ -482,8 +547,7 @@ grid_clear(struct grid *gd, u_int px, u_int py, u_int nx, u_int ny, u_int bg)
void
grid_clear_lines(struct grid *gd, u_int py, u_int ny, u_int bg)
{
struct grid_line *gl;
u_int yy;
u_int yy;
if (ny == 0)
return;
@@ -494,9 +558,7 @@ grid_clear_lines(struct grid *gd, u_int py, u_int ny, u_int bg)
return;
for (yy = py; yy < py + ny; yy++) {
gl = &gd->linedata[yy];
free(gl->celldata);
free(gl->extddata);
grid_free_line(gd, yy);
grid_empty_line(gd, yy, bg);
}
}
@@ -523,13 +585,16 @@ grid_move_lines(struct grid *gd, u_int dy, u_int py, u_int ny, u_int bg)
for (yy = dy; yy < dy + ny; yy++) {
if (yy >= py && yy < py + ny)
continue;
grid_clear_lines(gd, yy, 1, bg);
grid_free_line(gd, yy);
}
memmove(&gd->linedata[dy], &gd->linedata[py],
ny * (sizeof *gd->linedata));
/* Wipe any lines that have been moved (without freeing them). */
/*
* Wipe any lines that have been moved (without freeing them - they are
* still present).
*/
for (yy = py; yy < py + ny; yy++) {
if (yy < dy || yy >= dy + ny)
grid_empty_line(gd, yy, bg);
@@ -666,8 +731,7 @@ grid_string_cells_bg(const struct grid_cell *gc, int *values)
/*
* Returns ANSI code to set particular attributes (colour, bold and so on)
* given a current state. The output buffer must be able to hold at least 57
* bytes.
* given a current state.
*/
static void
grid_string_cells_code(const struct grid_cell *lastgc,
@@ -847,9 +911,8 @@ grid_string_cells(struct grid *gd, u_int px, u_int py, u_int nx,
}
/*
* Duplicate a set of lines between two grids. If there aren't enough lines in
* either source or destination, the number of lines is limited to the number
* available.
* Duplicate a set of lines between two grids. Both source and destination
* should be big enough.
*/
void
grid_duplicate_lines(struct grid *dst, u_int dy, struct grid *src, u_int sy,
@@ -862,7 +925,7 @@ grid_duplicate_lines(struct grid *dst, u_int dy, struct grid *src, u_int sy,
ny = dst->hsize + dst->sy - dy;
if (sy + ny > src->hsize + src->sy)
ny = src->hsize + src->sy - sy;
grid_clear_lines(dst, dy, ny, 8);
grid_free_lines(dst, dy, ny);
for (yy = 0; yy < ny; yy++) {
srcl = &src->linedata[sy];

View File

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

171
input.c
View File

@@ -87,12 +87,15 @@ struct input_ctx {
struct utf8_data utf8data;
int ch;
int last;
int flags;
#define INPUT_DISCARD 0x1
const struct input_state *state;
struct event timer;
/*
* All input received since we were last in the ground state. Sent to
* control clients on connection.
@@ -110,12 +113,15 @@ static void input_set_state(struct window_pane *,
static void input_reset_cell(struct input_ctx *);
static void input_osc_4(struct window_pane *, const char *);
static void input_osc_10(struct window_pane *, const char *);
static void input_osc_11(struct window_pane *, const char *);
static void input_osc_52(struct window_pane *, const char *);
static void input_osc_104(struct window_pane *, const char *);
/* Transition entry/exit handlers. */
static void input_clear(struct input_ctx *);
static void input_ground(struct input_ctx *);
static void input_enter_dcs(struct input_ctx *);
static void input_enter_osc(struct input_ctx *);
static void input_exit_osc(struct input_ctx *);
static void input_enter_apc(struct input_ctx *);
@@ -216,6 +222,7 @@ enum input_csi_type {
INPUT_CSI_ICH,
INPUT_CSI_IL,
INPUT_CSI_RCP,
INPUT_CSI_REP,
INPUT_CSI_RM,
INPUT_CSI_RM_PRIVATE,
INPUT_CSI_SCP,
@@ -247,6 +254,7 @@ static const struct input_table_entry input_csi_table[] = {
{ 'S', "", INPUT_CSI_SU },
{ 'X', "", INPUT_CSI_ECH },
{ 'Z', "", INPUT_CSI_CBT },
{ 'b', "", INPUT_CSI_REP },
{ 'c', "", INPUT_CSI_DA },
{ 'c', ">", INPUT_CSI_DA_TWO },
{ 'd', "", INPUT_CSI_VPA },
@@ -362,7 +370,7 @@ static const struct input_state input_state_csi_ignore = {
/* dcs_enter state definition. */
static const struct input_state input_state_dcs_enter = {
"dcs_enter",
input_clear, NULL,
input_enter_dcs, NULL,
input_state_dcs_enter_table
};
@@ -425,7 +433,7 @@ static const struct input_state input_state_rename_string = {
/* consume_st state definition. */
static const struct input_state input_state_consume_st = {
"consume_st",
NULL, NULL,
input_enter_rename, NULL, /* rename also waits for ST */
input_state_consume_st_table
};
@@ -754,6 +762,30 @@ input_table_compare(const void *key, const void *value)
return (strcmp(ictx->interm_buf, entry->interm));
}
/*
* Timer - if this expires then have been waiting for a terminator for too
* long, so reset to ground.
*/
static void
input_timer_callback(__unused int fd, __unused short events, void *arg)
{
struct input_ctx *ictx = arg;
struct window_pane *wp = ictx->wp;
log_debug("%s: %%%u %s expired" , __func__, wp->id, ictx->state->name);
input_reset(wp, 0);
}
/* Start the timer. */
static void
input_start_timer(struct input_ctx *ictx)
{
struct timeval tv = { .tv_usec = 100000 };
event_del(&ictx->timer);
event_add(&ictx->timer, &tv);
}
/* Reset cell state to default. */
static void
input_reset_cell(struct input_ctx *ictx)
@@ -780,6 +812,8 @@ input_init(struct window_pane *wp)
ictx->since_ground = evbuffer_new();
evtimer_set(&ictx->timer, input_timer_callback, ictx);
input_reset(wp, 0);
}
@@ -789,6 +823,8 @@ input_free(struct window_pane *wp)
{
struct input_ctx *ictx = wp->ictx;
event_del(&ictx->timer);
free(ictx->input_buf);
evbuffer_free(ictx->since_ground);
@@ -813,14 +849,9 @@ input_reset(struct window_pane *wp, int clear)
screen_write_stop(&ictx->ctx);
}
*ictx->interm_buf = '\0';
ictx->interm_len = 0;
input_clear(ictx);
*ictx->param_buf = '\0';
ictx->param_len = 0;
*ictx->input_buf = '\0';
ictx->input_len = 0;
ictx->last = -1;
ictx->state = &input_state_ground;
ictx->flags = 0;
@@ -995,6 +1026,8 @@ input_reply(struct input_ctx *ictx, const char *fmt, ...)
static void
input_clear(struct input_ctx *ictx)
{
event_del(&ictx->timer);
*ictx->interm_buf = '\0';
ictx->interm_len = 0;
@@ -1011,6 +1044,7 @@ input_clear(struct input_ctx *ictx)
static void
input_ground(struct input_ctx *ictx)
{
event_del(&ictx->timer);
evbuffer_drain(ictx->since_ground, EVBUFFER_LENGTH(ictx->since_ground));
if (ictx->input_space > INPUT_BUF_START) {
@@ -1033,6 +1067,7 @@ input_print(struct input_ctx *ictx)
utf8_set(&ictx->cell.cell.data, ictx->ch);
screen_write_collect_add(&ictx->ctx, &ictx->cell.cell);
ictx->last = ictx->ch;
ictx->cell.cell.attr &= ~GRID_ATTR_CHARSET;
@@ -1123,7 +1158,7 @@ input_c0_dispatch(struct input_ctx *ictx)
case '\012': /* LF */
case '\013': /* VT */
case '\014': /* FF */
screen_write_linefeed(sctx, 0);
screen_write_linefeed(sctx, 0, ictx->cell.cell.bg);
break;
case '\015': /* CR */
screen_write_carriagereturn(sctx);
@@ -1139,6 +1174,7 @@ input_c0_dispatch(struct input_ctx *ictx)
break;
}
ictx->last = -1;
return (0);
}
@@ -1168,18 +1204,18 @@ input_esc_dispatch(struct input_ctx *ictx)
screen_write_reset(sctx);
break;
case INPUT_ESC_IND:
screen_write_linefeed(sctx, 0);
screen_write_linefeed(sctx, 0, ictx->cell.cell.bg);
break;
case INPUT_ESC_NEL:
screen_write_carriagereturn(sctx);
screen_write_linefeed(sctx, 0);
screen_write_linefeed(sctx, 0, ictx->cell.cell.bg);
break;
case INPUT_ESC_HTS:
if (s->cx < screen_size_x(s))
bit_set(s->tabs, s->cx);
break;
case INPUT_ESC_RI:
screen_write_reverseindex(sctx);
screen_write_reverseindex(sctx, ictx->cell.cell.bg);
break;
case INPUT_ESC_DECKPAM:
screen_write_mode_set(sctx, MODE_KKEYPAD);
@@ -1216,6 +1252,7 @@ input_esc_dispatch(struct input_ctx *ictx)
break;
}
ictx->last = -1;
return (0);
}
@@ -1226,7 +1263,7 @@ input_csi_dispatch(struct input_ctx *ictx)
struct screen_write_ctx *sctx = &ictx->ctx;
struct screen *s = sctx->s;
struct input_table_entry *entry;
int n, m;
int i, n, m;
u_int cx;
if (ictx->flags & INPUT_DISCARD)
@@ -1392,6 +1429,15 @@ input_csi_dispatch(struct input_ctx *ictx)
screen_write_insertline(sctx, input_get(ictx, 0, 1, 1),
ictx->cell.cell.bg);
break;
case INPUT_CSI_REP:
if (ictx->last == -1)
break;
ictx->ch = ictx->last;
n = input_get(ictx, 0, 1, 1);
for (i = 0; i < n; i++)
input_print(ictx);
break;
case INPUT_CSI_RCP:
memcpy(&ictx->cell, &ictx->old_cell, sizeof ictx->cell);
screen_write_cursormove(sctx, ictx->old_cx, ictx->old_cy);
@@ -1417,7 +1463,8 @@ input_csi_dispatch(struct input_ctx *ictx)
input_csi_dispatch_sm_private(ictx);
break;
case INPUT_CSI_SU:
screen_write_scrollup(sctx, input_get(ictx, 0, 1, 1));
screen_write_scrollup(sctx, input_get(ictx, 0, 1, 1),
ictx->cell.cell.bg);
break;
case INPUT_CSI_TBC:
switch (input_get(ictx, 0, 0, 0)) {
@@ -1443,6 +1490,7 @@ input_csi_dispatch(struct input_ctx *ictx)
break;
}
ictx->last = -1;
return (0);
}
@@ -1839,6 +1887,17 @@ input_csi_dispatch_sgr(struct input_ctx *ictx)
}
}
/* DCS string started. */
static void
input_enter_dcs(struct input_ctx *ictx)
{
log_debug("%s", __func__);
input_clear(ictx);
input_start_timer(ictx);
ictx->last = -1;
}
/* DCS terminator (ST) received. */
static int
input_dcs_dispatch(struct input_ctx *ictx)
@@ -1868,6 +1927,8 @@ input_enter_osc(struct input_ctx *ictx)
log_debug("%s", __func__);
input_clear(ictx);
input_start_timer(ictx);
ictx->last = -1;
}
/* OSC terminator (ST) received. */
@@ -1893,19 +1954,27 @@ input_exit_osc(struct input_ctx *ictx)
switch (option) {
case 0:
case 2:
screen_set_title(ictx->ctx.s, p);
server_status_window(ictx->wp->window);
if (utf8_isvalid(p)) {
screen_set_title(ictx->ctx.s, p);
server_status_window(ictx->wp->window);
}
break;
case 4:
input_osc_4(ictx->wp, p);
break;
case 52:
input_osc_52(ictx->wp, p);
case 10:
input_osc_10(ictx->wp, p);
break;
case 11:
input_osc_11(ictx->wp, p);
break;
case 12:
if (*p != '?') /* ? is colour request */
if (utf8_isvalid(p) && *p != '?') /* ? is colour request */
screen_set_cursor_colour(ictx->ctx.s, p);
break;
case 52:
input_osc_52(ictx->wp, p);
break;
case 104:
input_osc_104(ictx->wp, p);
break;
@@ -1926,6 +1995,8 @@ input_enter_apc(struct input_ctx *ictx)
log_debug("%s", __func__);
input_clear(ictx);
input_start_timer(ictx);
ictx->last = -1;
}
/* APC terminator (ST) received. */
@@ -1936,6 +2007,8 @@ input_exit_apc(struct input_ctx *ictx)
return;
log_debug("%s: \"%s\"", __func__, ictx->input_buf);
if (!utf8_isvalid(ictx->input_buf))
return;
screen_set_title(ictx->ctx.s, ictx->input_buf);
server_status_window(ictx->wp->window);
}
@@ -1947,6 +2020,8 @@ input_enter_rename(struct input_ctx *ictx)
log_debug("%s", __func__);
input_clear(ictx);
input_start_timer(ictx);
ictx->last = -1;
}
/* Rename terminator (ST) received. */
@@ -1959,9 +2034,10 @@ input_exit_rename(struct input_ctx *ictx)
return;
log_debug("%s: \"%s\"", __func__, ictx->input_buf);
if (!utf8_isvalid(ictx->input_buf))
return;
window_set_name(ictx->wp->window, ictx->input_buf);
options_set_number(ictx->wp->window->options, "automatic-rename", 0);
server_status_window(ictx->wp->window);
}
@@ -1975,6 +2051,7 @@ input_utf8_open(struct input_ctx *ictx)
fatalx("UTF-8 open invalid %#x", ictx->ch);
log_debug("%s %hhu", __func__, ud->size);
ictx->last = -1;
return (0);
}
@@ -2051,6 +2128,42 @@ bad:
free(copy);
}
/* Handle the OSC 10 sequence for setting background colour. */
static void
input_osc_10(struct window_pane *wp, const char *p)
{
u_int r, g, b;
if (sscanf(p, "rgb:%2x/%2x/%2x", &r, &g, &b) != 3)
goto bad;
wp->colgc.fg = colour_join_rgb(r, g, b);
wp->flags |= PANE_REDRAW;
return;
bad:
log_debug("bad OSC 10: %s", p);
}
/* Handle the OSC 11 sequence for setting background colour. */
static void
input_osc_11(struct window_pane *wp, const char *p)
{
u_int r, g, b;
if (sscanf(p, "rgb:%2x/%2x/%2x", &r, &g, &b) != 3)
goto bad;
wp->colgc.bg = colour_join_rgb(r, g, b);
wp->flags |= PANE_REDRAW;
return;
bad:
log_debug("bad OSC 11: %s", p);
}
/* Handle the OSC 52 sequence for setting the clipboard. */
static void
input_osc_52(struct window_pane *wp, const char *p)
@@ -2058,9 +2171,13 @@ input_osc_52(struct window_pane *wp, const char *p)
char *end;
size_t len;
u_char *out;
int outlen;
int outlen, state;
struct screen_write_ctx ctx;
state = options_get_number(global_options, "set-clipboard");
if (state != 2)
return;
if ((end = strchr(p, ';')) == NULL)
return;
end++;
@@ -2077,11 +2194,11 @@ input_osc_52(struct window_pane *wp, const char *p)
return;
}
if (options_get_number(global_options, "set-clipboard")) {
screen_write_start(&ctx, wp, NULL);
screen_write_setselection(&ctx, out, outlen);
screen_write_stop(&ctx);
}
screen_write_start(&ctx, wp, NULL);
screen_write_setselection(&ctx, out, outlen);
screen_write_stop(&ctx);
notify_pane("pane-set-clipboard", wp);
paste_add(out, outlen);
}

13
job.c
View File

@@ -50,6 +50,7 @@ job_run(const char *cmd, struct session *s, const char *cwd,
pid_t pid;
int nullfd, out[2];
const char *home;
sigset_t set, oldset;
if (socketpair(AF_UNIX, SOCK_STREAM, PF_UNSPEC, out) != 0)
return (NULL);
@@ -60,14 +61,18 @@ job_run(const char *cmd, struct session *s, const char *cwd,
*/
env = environ_for_session(s, !cfg_finished);
sigfillset(&set);
sigprocmask(SIG_BLOCK, &set, &oldset);
switch (pid = fork()) {
case -1:
sigprocmask(SIG_SETMASK, &oldset, NULL);
environ_free(env);
close(out[0]);
close(out[1]);
return (NULL);
case 0: /* child */
clear_signals(1);
case 0:
proc_clear_signals(server_proc, 1);
sigprocmask(SIG_SETMASK, &oldset, NULL);
if (cwd == NULL || chdir(cwd) != 0) {
if ((home = find_home()) == NULL || chdir(home) != 0)
@@ -99,7 +104,7 @@ job_run(const char *cmd, struct session *s, const char *cwd,
fatal("execl failed");
}
/* parent */
sigprocmask(SIG_SETMASK, &oldset, NULL);
environ_free(env);
close(out[1]);
@@ -157,7 +162,7 @@ job_read_callback(__unused struct bufferevent *bufev, void *data)
struct job *job = data;
if (job->updatecb != NULL)
job->updatecb (job);
job->updatecb(job);
}
/*

View File

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

View File

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

View File

@@ -165,6 +165,7 @@ layout_set_even_h(struct window *w)
layout_print_cell(w->layout_root, __func__, 1);
notify_window("window-layout-changed", w);
server_redraw_window(w);
}
@@ -219,6 +220,7 @@ layout_set_even_v(struct window *w)
layout_print_cell(w->layout_root, __func__, 1);
notify_window("window-layout-changed", w);
server_redraw_window(w);
}
@@ -342,6 +344,7 @@ layout_set_main_h(struct window *w)
layout_print_cell(w->layout_root, __func__, 1);
notify_window("window-layout-changed", w);
server_redraw_window(w);
}
@@ -465,6 +468,7 @@ layout_set_main_v(struct window *w)
layout_print_cell(w->layout_root, __func__, 1);
notify_window("window-layout-changed", w);
server_redraw_window(w);
}
@@ -567,5 +571,6 @@ layout_set_tiled(struct window *w)
layout_print_cell(w->layout_root, __func__, 1);
notify_window("window-layout-changed", w);
server_redraw_window(w);
}

21
log.c
View File

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

920
mode-tree.c Normal file
View File

@@ -0,0 +1,920 @@
/* $OpenBSD$ */
/*
* Copyright (c) 2017 Nicholas Marriott <nicholas.marriott@gmail.com>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER
* IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
* OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include <sys/types.h>
#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "tmux.h"
struct mode_tree_item;
TAILQ_HEAD(mode_tree_list, mode_tree_item);
struct mode_tree_data {
int dead;
u_int references;
struct window_pane *wp;
void *modedata;
const char **sort_list;
u_int sort_size;
u_int sort_type;
void (*buildcb)(void *, u_int, uint64_t *,
const char *);
struct screen *(*drawcb)(void *, void *, u_int, u_int);
int (*searchcb)(void*, void *, const char *);
struct mode_tree_list children;
struct mode_tree_list saved;
struct mode_tree_line *line_list;
u_int line_size;
u_int depth;
u_int width;
u_int height;
u_int offset;
u_int current;
struct screen screen;
int preview;
char *search;
char *filter;
};
struct mode_tree_item {
struct mode_tree_item *parent;
void *itemdata;
u_int line;
uint64_t tag;
const char *name;
const char *text;
int expanded;
int tagged;
struct mode_tree_list children;
TAILQ_ENTRY(mode_tree_item) entry;
};
struct mode_tree_line {
struct mode_tree_item *item;
u_int depth;
int last;
int flat;
};
static void mode_tree_free_items(struct mode_tree_list *);
static struct mode_tree_item *
mode_tree_find_item(struct mode_tree_list *mtl, uint64_t tag)
{
struct mode_tree_item *mti, *child;
TAILQ_FOREACH(mti, mtl, entry) {
if (mti->tag == tag)
return (mti);
child = mode_tree_find_item(&mti->children, tag);
if (child != NULL)
return (child);
}
return (NULL);
}
static void
mode_tree_free_item(struct mode_tree_item *mti)
{
mode_tree_free_items(&mti->children);
free((void *)mti->name);
free((void *)mti->text);
free(mti);
}
static void
mode_tree_free_items(struct mode_tree_list *mtl)
{
struct mode_tree_item *mti, *mti1;
TAILQ_FOREACH_SAFE(mti, mtl, entry, mti1) {
TAILQ_REMOVE(mtl, mti, entry);
mode_tree_free_item(mti);
}
}
static void
mode_tree_check_selected(struct mode_tree_data *mtd)
{
/*
* If the current line would now be off screen reset the offset to the
* last visible line.
*/
if (mtd->current > mtd->height - 1)
mtd->offset = mtd->current - mtd->height + 1;
}
static void
mode_tree_clear_lines(struct mode_tree_data *mtd)
{
free(mtd->line_list);
mtd->line_list = NULL;
mtd->line_size = 0;
}
static void
mode_tree_build_lines(struct mode_tree_data *mtd,
struct mode_tree_list *mtl, u_int depth)
{
struct mode_tree_item *mti;
struct mode_tree_line *line;
u_int i;
int flat = 1;
mtd->depth = depth;
TAILQ_FOREACH(mti, mtl, entry) {
mtd->line_list = xreallocarray(mtd->line_list,
mtd->line_size + 1, sizeof *mtd->line_list);
line = &mtd->line_list[mtd->line_size++];
line->item = mti;
line->depth = depth;
line->last = (mti == TAILQ_LAST(mtl, mode_tree_list));
mti->line = (mtd->line_size - 1);
if (!TAILQ_EMPTY(&mti->children))
flat = 0;
if (mti->expanded)
mode_tree_build_lines(mtd, &mti->children, depth + 1);
}
TAILQ_FOREACH(mti, mtl, entry) {
for (i = 0; i < mtd->line_size; i++) {
line = &mtd->line_list[i];
if (line->item == mti)
line->flat = flat;
}
}
}
static void
mode_tree_clear_tagged(struct mode_tree_list *mtl)
{
struct mode_tree_item *mti;
TAILQ_FOREACH(mti, mtl, entry) {
mti->tagged = 0;
mode_tree_clear_tagged(&mti->children);
}
}
static void
mode_tree_set_current(struct mode_tree_data *mtd, uint64_t tag)
{
u_int i;
for (i = 0; i < mtd->line_size; i++) {
if (mtd->line_list[i].item->tag == tag)
break;
}
if (i != mtd->line_size) {
mtd->current = i;
if (mtd->current > mtd->height - 1)
mtd->offset = mtd->current - mtd->height + 1;
else
mtd->offset = 0;
} else {
mtd->current = 0;
mtd->offset = 0;
}
}
void
mode_tree_up(struct mode_tree_data *mtd, int wrap)
{
if (mtd->current == 0) {
if (wrap) {
mtd->current = mtd->line_size - 1;
if (mtd->line_size >= mtd->height)
mtd->offset = mtd->line_size - mtd->height;
}
} else {
mtd->current--;
if (mtd->current < mtd->offset)
mtd->offset--;
}
}
void
mode_tree_down(struct mode_tree_data *mtd, int wrap)
{
if (mtd->current == mtd->line_size - 1) {
if (wrap) {
mtd->current = 0;
mtd->offset = 0;
}
} else {
mtd->current++;
if (mtd->current > mtd->offset + mtd->height - 1)
mtd->offset++;
}
}
void *
mode_tree_get_current(struct mode_tree_data *mtd)
{
return (mtd->line_list[mtd->current].item->itemdata);
}
u_int
mode_tree_count_tagged(struct mode_tree_data *mtd)
{
struct mode_tree_item *mti;
u_int i, tagged;
tagged = 0;
for (i = 0; i < mtd->line_size; i++) {
mti = mtd->line_list[i].item;
if (mti->tagged)
tagged++;
}
return (tagged);
}
void
mode_tree_each_tagged(struct mode_tree_data *mtd, void (*cb)(void *, void *,
key_code), key_code key, int current)
{
struct mode_tree_item *mti;
u_int i;
int fired;
fired = 0;
for (i = 0; i < mtd->line_size; i++) {
mti = mtd->line_list[i].item;
if (mti->tagged) {
fired = 1;
cb(mtd->modedata, mti->itemdata, key);
}
}
if (!fired && current) {
mti = mtd->line_list[mtd->current].item;
cb(mtd->modedata, mti->itemdata, key);
}
}
struct mode_tree_data *
mode_tree_start(struct window_pane *wp, struct args *args,
void (*buildcb)(void *, u_int, uint64_t *, const char *),
struct screen *(*drawcb)(void *, void *, u_int, u_int),
int (*searchcb)(void *, void *, const char *), void *modedata,
const char **sort_list, u_int sort_size, struct screen **s)
{
struct mode_tree_data *mtd;
const char *sort;
u_int i;
mtd = xcalloc(1, sizeof *mtd);
mtd->references = 1;
mtd->wp = wp;
mtd->modedata = modedata;
mtd->sort_list = sort_list;
mtd->sort_size = sort_size;
mtd->sort_type = 0;
mtd->preview = !args_has(args, 'N');
sort = args_get(args, 'O');
if (sort != NULL) {
for (i = 0; i < sort_size; i++) {
if (strcasecmp(sort, sort_list[i]) == 0)
mtd->sort_type = i;
}
}
if (args_has(args, 'f'))
mtd->filter = xstrdup(args_get(args, 'f'));
else
mtd->filter = NULL;
mtd->buildcb = buildcb;
mtd->drawcb = drawcb;
mtd->searchcb = searchcb;
TAILQ_INIT(&mtd->children);
*s = &mtd->screen;
screen_init(*s, screen_size_x(&wp->base), screen_size_y(&wp->base), 0);
(*s)->mode &= ~MODE_CURSOR;
return (mtd);
}
void
mode_tree_build(struct mode_tree_data *mtd)
{
struct screen *s = &mtd->screen;
uint64_t tag;
if (mtd->line_list != NULL)
tag = mtd->line_list[mtd->current].item->tag;
else
tag = 0;
TAILQ_CONCAT(&mtd->saved, &mtd->children, entry);
TAILQ_INIT(&mtd->children);
mtd->buildcb(mtd->modedata, mtd->sort_type, &tag, mtd->filter);
if (TAILQ_EMPTY(&mtd->children))
mtd->buildcb(mtd->modedata, mtd->sort_type, &tag, NULL);
mode_tree_free_items(&mtd->saved);
TAILQ_INIT(&mtd->saved);
mode_tree_clear_lines(mtd);
mode_tree_build_lines(mtd, &mtd->children, 0);
mode_tree_set_current(mtd, tag);
mtd->width = screen_size_x(s);
if (mtd->preview) {
mtd->height = (screen_size_y(s) / 3) * 2;
if (mtd->height > mtd->line_size)
mtd->height = screen_size_y(s) / 2;
if (mtd->height < 10)
mtd->height = screen_size_y(s);
if (screen_size_y(s) - mtd->height < 2)
mtd->height = screen_size_y(s);
} else
mtd->height = screen_size_y(s);
mode_tree_check_selected(mtd);
}
static void
mode_tree_remove_ref(struct mode_tree_data *mtd)
{
if (--mtd->references == 0)
free(mtd);
}
void
mode_tree_free(struct mode_tree_data *mtd)
{
mode_tree_free_items(&mtd->children);
mode_tree_clear_lines(mtd);
screen_free(&mtd->screen);
free(mtd->search);
free(mtd->filter);
mtd->dead = 1;
mode_tree_remove_ref(mtd);
}
void
mode_tree_resize(struct mode_tree_data *mtd, u_int sx, u_int sy)
{
struct screen *s = &mtd->screen;
screen_resize(s, sx, sy, 0);
mode_tree_build(mtd);
mode_tree_draw(mtd);
mtd->wp->flags |= PANE_REDRAW;
}
struct mode_tree_item *
mode_tree_add(struct mode_tree_data *mtd, struct mode_tree_item *parent,
void *itemdata, uint64_t tag, const char *name, const char *text,
int expanded)
{
struct mode_tree_item *mti, *saved;
log_debug("%s: %llu, %s %s", __func__, (unsigned long long)tag,
name, text);
mti = xcalloc(1, sizeof *mti);
mti->parent = parent;
mti->itemdata = itemdata;
mti->tag = tag;
mti->name = xstrdup(name);
mti->text = xstrdup(text);
saved = mode_tree_find_item(&mtd->saved, tag);
if (saved != NULL) {
if (parent == NULL || (parent != NULL && parent->expanded))
mti->tagged = saved->tagged;
mti->expanded = saved->expanded;
} else if (expanded == -1)
mti->expanded = 1;
else
mti->expanded = expanded;
TAILQ_INIT(&mti->children);
if (parent != NULL)
TAILQ_INSERT_TAIL(&parent->children, mti, entry);
else
TAILQ_INSERT_TAIL(&mtd->children, mti, entry);
return (mti);
}
void
mode_tree_remove(struct mode_tree_data *mtd, struct mode_tree_item *mti)
{
struct mode_tree_item *parent = mti->parent;
if (parent != NULL)
TAILQ_REMOVE(&parent->children, mti, entry);
else
TAILQ_REMOVE(&mtd->children, mti, entry);
mode_tree_free_item(mti);
}
void
mode_tree_draw(struct mode_tree_data *mtd)
{
struct window_pane *wp = mtd->wp;
struct screen *s = &mtd->screen, *box = NULL;
struct mode_tree_line *line;
struct mode_tree_item *mti;
struct options *oo = wp->window->options;
struct screen_write_ctx ctx;
struct grid_cell gc0, gc;
u_int w, h, i, j, sy, box_x, box_y;
char *text, *start, key[7];
const char *tag, *symbol;
size_t size;
int keylen;
if (mtd->line_size == 0)
return;
memcpy(&gc0, &grid_default_cell, sizeof gc0);
memcpy(&gc, &grid_default_cell, sizeof gc);
style_apply(&gc, oo, "mode-style");
w = mtd->width;
h = mtd->height;
screen_write_start(&ctx, NULL, s);
screen_write_clearscreen(&ctx, 8);
if (mtd->line_size > 10)
keylen = 6;
else
keylen = 4;
for (i = 0; i < mtd->line_size; i++) {
if (i < mtd->offset)
continue;
if (i > mtd->offset + h - 1)
break;
line = &mtd->line_list[i];
mti = line->item;
screen_write_cursormove(&ctx, 0, i - mtd->offset);
if (i < 10)
snprintf(key, sizeof key, "(%c) ", '0' + i);
else if (i < 36)
snprintf(key, sizeof key, "(M-%c)", 'a' + (i - 10));
else
*key = '\0';
if (line->flat)
symbol = "";
else if (TAILQ_EMPTY(&mti->children))
symbol = " ";
else if (mti->expanded)
symbol = "- ";
else
symbol = "+ ";
if (line->depth == 0)
start = xstrdup(symbol);
else {
size = (4 * line->depth) + 32;
start = xcalloc(1, size);
for (j = 1; j < line->depth; j++) {
if (mti->parent != NULL &&
mtd->line_list[mti->parent->line].last)
strlcat(start, " ", size);
else
strlcat(start, "\001x\001 ", size);
}
if (line->last)
strlcat(start, "\001mq\001> ", size);
else
strlcat(start, "\001tq\001> ", size);
strlcat(start, symbol, size);
}
if (mti->tagged)
tag = "*";
else
tag = "";
xasprintf(&text, "%-*s%s%s%s: %s", keylen, key, start,
mti->name, tag, mti->text);
free(start);
if (mti->tagged) {
gc.attr ^= GRID_ATTR_BRIGHT;
gc0.attr ^= GRID_ATTR_BRIGHT;
}
if (i != mtd->current) {
screen_write_puts(&ctx, &gc0, "%.*s", w, text);
screen_write_clearendofline(&ctx, 8);
} else
screen_write_puts(&ctx, &gc, "%-*.*s", w, w, text);
free(text);
if (mti->tagged) {
gc.attr ^= GRID_ATTR_BRIGHT;
gc0.attr ^= GRID_ATTR_BRIGHT;
}
}
sy = screen_size_y(s);
if (!mtd->preview || sy <= 4 || h <= 4 || sy - h <= 4 || w <= 4) {
screen_write_stop(&ctx);
return;
}
line = &mtd->line_list[mtd->current];
mti = line->item;
screen_write_cursormove(&ctx, 0, h);
screen_write_box(&ctx, w, sy - h);
xasprintf(&text, " %s (sort: %s) ", mti->name,
mtd->sort_list[mtd->sort_type]);
if (w - 2 >= strlen(text)) {
screen_write_cursormove(&ctx, 1, h);
screen_write_puts(&ctx, &gc0, "%s", text);
}
free(text);
box_x = w - 4;
box_y = sy - h - 2;
if (box_x != 0 && box_y != 0)
box = mtd->drawcb(mtd->modedata, mti->itemdata, box_x, box_y);
if (box != NULL) {
screen_write_cursormove(&ctx, 2, h + 1);
screen_write_copy(&ctx, box, 0, 0, box_x, box_y, NULL, NULL);
screen_free(box);
}
screen_write_stop(&ctx);
}
static struct mode_tree_item *
mode_tree_search_for(struct mode_tree_data *mtd)
{
struct mode_tree_item *mti, *last, *next;
if (mtd->search == NULL)
return (NULL);
mti = last = mtd->line_list[mtd->current].item;
for (;;) {
if (!TAILQ_EMPTY(&mti->children))
mti = TAILQ_FIRST(&mti->children);
else if ((next = TAILQ_NEXT(mti, entry)) != NULL)
mti = next;
else {
for (;;) {
mti = mti->parent;
if (mti == NULL)
break;
if ((next = TAILQ_NEXT(mti, entry)) != NULL) {
mti = next;
break;
}
}
}
if (mti == NULL)
mti = TAILQ_FIRST(&mtd->children);
if (mti == last)
break;
if (mtd->searchcb == NULL) {
if (strstr(mti->name, mtd->search) != NULL)
return (mti);
continue;
}
if (mtd->searchcb(mtd->modedata, mti->itemdata, mtd->search))
return (mti);
}
return (NULL);
}
static void
mode_tree_search_set(struct mode_tree_data *mtd)
{
struct mode_tree_item *mti, *loop;
uint64_t tag;
mti = mode_tree_search_for(mtd);
if (mti == NULL)
return;
tag = mti->tag;
loop = mti->parent;
while (loop != NULL) {
loop->expanded = 1;
loop = loop->parent;
}
mode_tree_build(mtd);
mode_tree_set_current(mtd, tag);
mode_tree_draw(mtd);
mtd->wp->flags |= PANE_REDRAW;
}
static int
mode_tree_search_callback(__unused struct client *c, void *data, const char *s,
__unused int done)
{
struct mode_tree_data *mtd = data;
if (mtd->dead)
return (0);
free(mtd->search);
if (s == NULL || *s == '\0') {
mtd->search = NULL;
return (0);
}
mtd->search = xstrdup(s);
mode_tree_search_set(mtd);
return (0);
}
static void
mode_tree_search_free(void *data)
{
mode_tree_remove_ref(data);
}
static int
mode_tree_filter_callback(__unused struct client *c, void *data, const char *s,
__unused int done)
{
struct mode_tree_data *mtd = data;
if (mtd->dead)
return (0);
if (mtd->filter != NULL)
free(mtd->filter);
if (s == NULL || *s == '\0')
mtd->filter = NULL;
else
mtd->filter = xstrdup(s);
mode_tree_build(mtd);
mode_tree_draw(mtd);
mtd->wp->flags |= PANE_REDRAW;
return (0);
}
static void
mode_tree_filter_free(void *data)
{
mode_tree_remove_ref(data);
}
int
mode_tree_key(struct mode_tree_data *mtd, struct client *c, key_code *key,
struct mouse_event *m)
{
struct mode_tree_line *line;
struct mode_tree_item *current, *parent;
u_int i, x, y;
int choice;
key_code tmp;
if (*key == KEYC_MOUSEDOWN1_PANE) {
if (cmd_mouse_at(mtd->wp, m, &x, &y, 0) != 0) {
*key = KEYC_NONE;
return (0);
}
if (x > mtd->width || y > mtd->height) {
*key = KEYC_NONE;
return (0);
}
if (mtd->offset + y < mtd->line_size) {
mtd->current = mtd->offset + y;
*key = '\r';
return (0);
}
}
line = &mtd->line_list[mtd->current];
current = line->item;
choice = -1;
if (*key >= '0' && *key <= '9')
choice = (*key) - '0';
else if (((*key) & KEYC_MASK_MOD) == KEYC_ESCAPE) {
tmp = (*key) & KEYC_MASK_KEY;
if (tmp >= 'a' && tmp <= 'z')
choice = 10 + (tmp - 'a');
}
if (choice != -1) {
if ((u_int)choice > mtd->line_size - 1) {
*key = KEYC_NONE;
return (0);
}
mtd->current = choice;
*key = '\r';
return (0);
}
switch (*key) {
case 'q':
case '\033': /* Escape */
return (1);
case KEYC_UP:
case 'k':
case KEYC_WHEELUP_PANE:
mode_tree_up(mtd, 1);
break;
case KEYC_DOWN:
case 'j':
case KEYC_WHEELDOWN_PANE:
mode_tree_down(mtd, 1);
break;
case KEYC_PPAGE:
case '\002': /* C-b */
for (i = 0; i < mtd->height; i++) {
if (mtd->current == 0)
break;
mode_tree_up(mtd, 1);
}
break;
case KEYC_NPAGE:
case '\006': /* C-f */
for (i = 0; i < mtd->height; i++) {
if (mtd->current == mtd->line_size - 1)
break;
mode_tree_down(mtd, 1);
}
break;
case KEYC_HOME:
mtd->current = 0;
mtd->offset = 0;
break;
case KEYC_END:
mtd->current = mtd->line_size - 1;
if (mtd->current > mtd->height - 1)
mtd->offset = mtd->current - mtd->height + 1;
else
mtd->offset = 0;
break;
case 't':
/*
* Do not allow parents and children to both be tagged: untag
* all parents and children of current.
*/
if (!current->tagged) {
parent = current->parent;
while (parent != NULL) {
parent->tagged = 0;
parent = parent->parent;
}
mode_tree_clear_tagged(&current->children);
current->tagged = 1;
} else
current->tagged = 0;
mode_tree_down(mtd, 0);
break;
case 'T':
for (i = 0; i < mtd->line_size; i++)
mtd->line_list[i].item->tagged = 0;
break;
case '\024': /* C-t */
for (i = 0; i < mtd->line_size; i++) {
if (mtd->line_list[i].item->parent == NULL)
mtd->line_list[i].item->tagged = 1;
else
mtd->line_list[i].item->tagged = 0;
}
break;
case 'O':
mtd->sort_type++;
if (mtd->sort_type == mtd->sort_size)
mtd->sort_type = 0;
mode_tree_build(mtd);
break;
case KEYC_LEFT:
case '-':
if (line->flat || !current->expanded)
current = current->parent;
if (current == NULL)
mode_tree_up(mtd, 0);
else {
current->expanded = 0;
mtd->current = current->line;
mode_tree_build(mtd);
}
break;
case KEYC_RIGHT:
case '+':
if (line->flat || current->expanded)
mode_tree_down(mtd, 0);
else if (!line->flat) {
current->expanded = 1;
mode_tree_build(mtd);
}
break;
case '\023': /* C-s */
mtd->references++;
status_prompt_set(c, "(search) ", "",
mode_tree_search_callback, mode_tree_search_free, mtd,
PROMPT_NOFORMAT);
break;
case 'n':
mode_tree_search_set(mtd);
break;
case 'f':
mtd->references++;
status_prompt_set(c, "(filter) ", mtd->filter,
mode_tree_filter_callback, mode_tree_filter_free, mtd,
PROMPT_NOFORMAT);
break;
case 'v':
mtd->preview = !mtd->preview;
mode_tree_build(mtd);
if (mtd->preview)
mode_tree_check_selected(mtd);
break;
}
return (0);
}
void
mode_tree_run_command(struct client *c, struct cmd_find_state *fs,
const char *template, const char *name)
{
struct cmdq_item *new_item;
struct cmd_list *cmdlist;
char *command, *cause;
command = cmd_template_replace(template, name, 1);
if (command == NULL || *command == '\0') {
free(command);
return;
}
cmdlist = cmd_string_parse(command, NULL, 0, &cause);
if (cmdlist == NULL) {
if (cause != NULL && c != NULL) {
*cause = toupper((u_char)*cause);
status_message_set(c, "%s", cause);
}
free(cause);
} else {
new_item = cmdq_get_command(cmdlist, fs, NULL, 0);
cmdq_append(c, new_item);
cmd_list_free(cmdlist);
}
free(command);
}

View File

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

View File

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

View File

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

View File

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

111
proc.c
View File

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

View File

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

View File

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

View File

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

View File

@@ -60,6 +60,9 @@ recalculate_sizes(void)
TAILQ_FOREACH(c, &clients, entry) {
if (c->flags & CLIENT_SUSPENDED)
continue;
if ((c->flags & (CLIENT_CONTROL|CLIENT_SIZECHANGED)) ==
CLIENT_CONTROL)
continue;
if (c->session == s) {
if (c->tty.sx < ssx)
ssx = c->tty.sx;
@@ -156,6 +159,8 @@ recalculate_sizes(void)
if (w->active == wp)
break;
}
if (w->active == w->last)
w->last = NULL;
server_redraw_window(w);
notify_window("window-layout-changed", w);

View File

@@ -48,7 +48,7 @@ struct screen_write_collect_item {
struct grid_cell gc;
TAILQ_ENTRY (screen_write_collect_item) entry;
TAILQ_ENTRY(screen_write_collect_item) entry;
};
struct screen_write_collect_line {
TAILQ_HEAD(, screen_write_collect_item) items;
@@ -75,6 +75,9 @@ screen_write_start(struct screen_write_ctx *ctx, struct window_pane *wp,
TAILQ_INIT(&ctx->list[y].items);
ctx->item = xcalloc(1, sizeof *ctx->item);
ctx->scrolled = 0;
ctx->bg = 8;
if (wp != NULL)
snprintf(tmp, sizeof tmp, "pane %%%u", wp->id);
log_debug("%s: size %ux%u, %s", __func__, screen_size_x(ctx->s),
@@ -359,6 +362,9 @@ screen_write_copy(struct screen_write_ctx *ctx, struct screen *src, u_int px,
struct grid_cell gc;
u_int xx, yy, cx, cy, b;
if (nx == 0 || ny == 0)
return;
cx = s->cx;
cy = s->cy;
@@ -381,6 +387,146 @@ screen_write_copy(struct screen_write_ctx *ctx, struct screen *src, u_int px,
}
}
/* Draw a horizontal line on screen. */
void
screen_write_hline(struct screen_write_ctx *ctx, u_int nx, int left, int right)
{
struct screen *s = ctx->s;
struct grid_cell gc;
u_int cx, cy, i;
cx = s->cx;
cy = s->cy;
memcpy(&gc, &grid_default_cell, sizeof gc);
gc.attr |= GRID_ATTR_CHARSET;
screen_write_putc(ctx, &gc, left ? 't' : 'q');
for (i = 1; i < nx - 1; i++)
screen_write_putc(ctx, &gc, 'q');
screen_write_putc(ctx, &gc, right ? 'u' : 'q');
screen_write_cursormove(ctx, cx, cy);
}
/* Draw a horizontal line on screen. */
void
screen_write_vline(struct screen_write_ctx *ctx, u_int ny, int top, int bottom)
{
struct screen *s = ctx->s;
struct grid_cell gc;
u_int cx, cy, i;
cx = s->cx;
cy = s->cy;
memcpy(&gc, &grid_default_cell, sizeof gc);
gc.attr |= GRID_ATTR_CHARSET;
screen_write_putc(ctx, &gc, top ? 'w' : 'x');
for (i = 1; i < ny - 1; i++) {
screen_write_cursormove(ctx, cx, cy + i);
screen_write_putc(ctx, &gc, 'x');
}
screen_write_cursormove(ctx, cx, cy + ny);
screen_write_putc(ctx, &gc, bottom ? 'v' : 'x');
screen_write_cursormove(ctx, cx, cy);
}
/* Draw a box on screen. */
void
screen_write_box(struct screen_write_ctx *ctx, u_int nx, u_int ny)
{
struct screen *s = ctx->s;
struct grid_cell gc;
u_int cx, cy, i;
cx = s->cx;
cy = s->cy;
memcpy(&gc, &grid_default_cell, sizeof gc);
gc.attr |= GRID_ATTR_CHARSET;
screen_write_putc(ctx, &gc, 'l');
for (i = 1; i < nx - 1; i++)
screen_write_putc(ctx, &gc, 'q');
screen_write_putc(ctx, &gc, 'k');
screen_write_cursormove(ctx, cx, cy + ny - 1);
screen_write_putc(ctx, &gc, 'm');
for (i = 1; i < nx - 1; i++)
screen_write_putc(ctx, &gc, 'q');
screen_write_putc(ctx, &gc, 'j');
for (i = 1; i < ny - 1; i++) {
screen_write_cursormove(ctx, cx, cy + i);
screen_write_putc(ctx, &gc, 'x');
}
for (i = 1; i < ny - 1; i++) {
screen_write_cursormove(ctx, cx + nx - 1, cy + i);
screen_write_putc(ctx, &gc, 'x');
}
screen_write_cursormove(ctx, cx, cy);
}
/* Write a preview version of a window. */
void
screen_write_preview(struct screen_write_ctx *ctx, struct screen *src, u_int nx,
u_int ny)
{
struct screen *s = ctx->s;
struct grid_cell gc;
u_int cx, cy, px, py;
cx = s->cx;
cy = s->cy;
/*
* If the cursor is on, pick the area around the cursor, otherwise use
* the top left.
*/
if (src->mode & MODE_CURSOR) {
px = src->cx;
if (px < nx / 3)
px = 0;
else
px = px - nx / 3;
if (px + nx > screen_size_x(src)) {
if (nx > screen_size_x(src))
px = 0;
else
px = screen_size_x(src) - nx;
}
py = src->cy;
if (py < ny / 3)
py = 0;
else
py = py - ny / 3;
if (py + ny > screen_size_y(src)) {
if (ny > screen_size_y(src))
py = 0;
else
py = screen_size_y(src) - ny;
}
} else {
px = 0;
py = 0;
}
screen_write_copy(ctx, src, px, src->grid->hsize + py, nx, ny, NULL,
NULL);
if (src->mode & MODE_CURSOR) {
grid_view_get_cell(src->grid, src->cx, src->cy, &gc);
gc.attr |= GRID_ATTR_REVERSE;
screen_write_cursormove(ctx, cx + (src->cx - px),
cy + (src->cy - py));
screen_write_cell(ctx, &gc);
}
}
/* Set up context for TTY command. */
static void
screen_write_initctx(struct screen_write_ctx *ctx, struct tty_ctx *ttyctx)
@@ -812,15 +958,16 @@ screen_write_cursormove(struct screen_write_ctx *ctx, u_int px, u_int py)
/* Reverse index (up with scroll). */
void
screen_write_reverseindex(struct screen_write_ctx *ctx)
screen_write_reverseindex(struct screen_write_ctx *ctx, u_int bg)
{
struct screen *s = ctx->s;
struct tty_ctx ttyctx;
screen_write_initctx(ctx, &ttyctx);
ttyctx.bg = bg;
if (s->cy == s->rupper)
grid_view_scroll_region_down(s->grid, s->rupper, s->rlower);
grid_view_scroll_region_down(s->grid, s->rupper, s->rlower, bg);
else if (s->cy > 0)
s->cy--;
@@ -854,7 +1001,7 @@ screen_write_scrollregion(struct screen_write_ctx *ctx, u_int rupper,
/* Line feed. */
void
screen_write_linefeed(struct screen_write_ctx *ctx, int wrapped)
screen_write_linefeed(struct screen_write_ctx *ctx, int wrapped, u_int bg)
{
struct screen *s = ctx->s;
struct grid *gd = s->grid;
@@ -869,8 +1016,13 @@ screen_write_linefeed(struct screen_write_ctx *ctx, int wrapped)
log_debug("%s: at %u,%u (region %u-%u)", __func__, s->cx, s->cy,
s->rupper, s->rlower);
if (bg != ctx->bg) {
screen_write_collect_flush(ctx, 1);
ctx->bg = bg;
}
if (s->cy == s->rlower) {
grid_view_scroll_region_up(gd, s->rupper, s->rlower);
grid_view_scroll_region_up(gd, s->rupper, s->rlower, bg);
screen_write_collect_scroll(ctx);
ctx->scrolled++;
} else if (s->cy < screen_size_y(s) - 1)
@@ -879,7 +1031,7 @@ screen_write_linefeed(struct screen_write_ctx *ctx, int wrapped)
/* Scroll up. */
void
screen_write_scrollup(struct screen_write_ctx *ctx, u_int lines)
screen_write_scrollup(struct screen_write_ctx *ctx, u_int lines, u_int bg)
{
struct screen *s = ctx->s;
struct grid *gd = s->grid;
@@ -890,8 +1042,13 @@ screen_write_scrollup(struct screen_write_ctx *ctx, u_int lines)
else if (lines > s->rlower - s->rupper + 1)
lines = s->rlower - s->rupper + 1;
if (bg != ctx->bg) {
screen_write_collect_flush(ctx, 1);
ctx->bg = bg;
}
for (i = 0; i < lines; i++) {
grid_view_scroll_region_up(gd, s->rupper, s->rlower);
grid_view_scroll_region_up(gd, s->rupper, s->rlower, bg);
screen_write_collect_scroll(ctx);
}
ctx->scrolled += lines;
@@ -1046,9 +1203,12 @@ screen_write_collect_flush(struct screen_write_ctx *ctx, int scroll_only)
screen_write_initctx(ctx, &ttyctx);
ttyctx.num = ctx->scrolled;
ttyctx.bg = ctx->bg;
tty_write(tty_cmd_scrollup, &ttyctx);
}
ctx->scrolled = 0;
ctx->bg = 8;
if (scroll_only)
return;
@@ -1143,7 +1303,7 @@ screen_write_collect_add(struct screen_write_ctx *ctx,
if (s->cx > sx - 1) {
log_debug("%s: wrapped at %u,%u", __func__, s->cx, s->cy);
ci->wrapped = 1;
screen_write_linefeed(ctx, 1);
screen_write_linefeed(ctx, 1, 8);
s->cx = 0;
}
@@ -1204,8 +1364,10 @@ screen_write_cell(struct screen_write_ctx *ctx, const struct grid_cell *gc)
/* Check this will fit on the current line and wrap if not. */
if ((s->mode & MODE_WRAP) && s->cx > sx - width) {
screen_write_linefeed(ctx, 1);
log_debug("%s: wrapped at %u,%u", __func__, s->cx, s->cy);
screen_write_linefeed(ctx, 1, 8);
s->cx = 0;
screen_write_collect_flush(ctx, 1);
}
/* Sanity check cursor position. */
@@ -1255,7 +1417,7 @@ screen_write_cell(struct screen_write_ctx *ctx, const struct grid_cell *gc)
}
}
/* Update the selection the flag and set the cell. */
/* Update the selected flag and set the cell. */
selected = screen_check_selection(s, s->cx, s->cy);
if (selected && (~gc->flags & GRID_FLAG_SELECTED)) {
memcpy(&tmp_gc, gc, sizeof tmp_gc);

View File

@@ -107,7 +107,7 @@ void
screen_set_title(struct screen *s, const char *title)
{
free(s->title);
s->title = xstrdup(title);
utf8_stravis(&s->title, title, VIS_OCTAL|VIS_CSTYLE|VIS_TAB|VIS_NL);
}
/* Resize screen. */

View File

@@ -47,28 +47,43 @@ static void server_client_dispatch_command(struct client *, struct imsg *);
static void server_client_dispatch_identify(struct client *, struct imsg *);
static void server_client_dispatch_shell(struct client *);
/* Number of attached clients. */
u_int
server_client_how_many(void)
{
struct client *c;
u_int n;
n = 0;
TAILQ_FOREACH(c, &clients, entry) {
if (c->session != NULL && (~c->flags & CLIENT_DETACHING))
n++;
}
return (n);
}
/* Identify mode callback. */
static void
server_client_callback_identify(__unused int fd, __unused short events, void *data)
server_client_callback_identify(__unused int fd, __unused short events,
void *data)
{
server_client_clear_identify(data, NULL);
}
/* Set identify mode on client. */
void
server_client_set_identify(struct client *c)
server_client_set_identify(struct client *c, u_int delay)
{
struct timeval tv;
int delay;
delay = options_get_number(c->session->options, "display-panes-time");
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);
evtimer_add(&c->identify_timer, &tv);
if (delay != 0)
evtimer_add(&c->identify_timer, &tv);
c->flags |= CLIENT_IDENTIFY;
c->tty.flags |= (TTY_FREEZE|TTY_NOCURSOR);
@@ -257,6 +272,10 @@ server_client_lost(struct client *c)
if (event_initialized(&c->status_timer))
evtimer_del(&c->status_timer);
screen_free(&c->status);
if (c->old_status != NULL) {
screen_free(c->old_status);
free(c->old_status);
}
free(c->title);
free((void *)c->cwd);
@@ -328,7 +347,7 @@ server_client_free(__unused int fd, __unused short events, void *arg)
void
server_client_suspend(struct client *c)
{
struct session *s = c->session;
struct session *s = c->session;
if (s == NULL || (c->flags & CLIENT_DETACHING))
return;
@@ -342,14 +361,14 @@ server_client_suspend(struct client *c)
void
server_client_detach(struct client *c, enum msgtype msgtype)
{
struct session *s = c->session;
struct session *s = c->session;
if (s == NULL || (c->flags & CLIENT_DETACHING))
return;
c->flags |= CLIENT_DETACHING;
notify_client("client-detached", c);
proc_send_s(c->peer, msgtype, s->name);
proc_send(c->peer, msgtype, -1, s->name, strlen(s->name) + 1);
}
/* Execute command to replace a client. */
@@ -794,8 +813,9 @@ server_client_handle_key(struct client *c, key_code key)
struct timeval tv;
struct key_table *table, *first;
struct key_binding bd_find, *bd;
int xtimeout;
int xtimeout, flags;
struct cmd_find_state fs;
key_code key0;
/* Check the client is good to accept input. */
if (s == NULL || (c->flags & (CLIENT_DEAD|CLIENT_SUSPENDED)) != 0)
@@ -855,8 +875,8 @@ server_client_handle_key(struct client *c, key_code key)
m->valid = 0;
/* Find affected pane. */
if (!KEYC_IS_MOUSE(key) || cmd_find_from_mouse(&fs, m) != 0)
cmd_find_from_session(&fs, s);
if (!KEYC_IS_MOUSE(key) || cmd_find_from_mouse(&fs, m, 0) != 0)
cmd_find_from_session(&fs, s, 0);
wp = fs.wp;
/* Forward mouse keys if disabled. */
@@ -884,13 +904,15 @@ server_client_handle_key(struct client *c, key_code key)
* The prefix always takes precedence and forces a switch to the prefix
* table, unless we are already there.
*/
if ((key == (key_code)options_get_number(s->options, "prefix") ||
key == (key_code)options_get_number(s->options, "prefix2")) &&
key0 = (key & ~KEYC_XTERM);
if ((key0 == (key_code)options_get_number(s->options, "prefix") ||
key0 == (key_code)options_get_number(s->options, "prefix2")) &&
strcmp(table->name, "prefix") != 0) {
server_client_set_key_table(c, "prefix");
server_status_client(c);
return;
}
flags = c->flags;
retry:
/* Log key table. */
@@ -902,7 +924,7 @@ retry:
log_debug("currently repeating");
/* Try to see if there is a key binding in the current table. */
bd_find.key = (key & ~KEYC_XTERM);
bd_find.key = key0;
bd = RB_FIND(key_bindings, &table->key_bindings, &bd_find);
if (bd != NULL) {
/*
@@ -945,7 +967,7 @@ retry:
server_status_client(c);
/* Execute the key binding. */
key_bindings_dispatch(bd, c, m, &fs);
key_bindings_dispatch(bd, NULL, c, m, &fs);
key_bindings_unref_table(table);
return;
}
@@ -968,7 +990,7 @@ retry:
* No match in the root table either. If this wasn't the first table
* tried, don't pass the key to the pane.
*/
if (first != table) {
if (first != table && (~flags & CLIENT_REPEAT)) {
server_client_set_key_table(c, NULL);
server_status_client(c);
return;
@@ -1016,6 +1038,47 @@ server_client_loop(void)
}
}
/* Check if we need to force a resize. */
static int
server_client_resize_force(struct window_pane *wp)
{
struct timeval tv = { .tv_usec = 100000 };
struct winsize ws;
/*
* If we are resizing to the same size as when we entered the loop
* (that is, to the same size the application currently thinks it is),
* tmux may have gone through several resizes internally and thrown
* away parts of the screen. So we need the application to actually
* redraw even though its final size has not changed.
*/
if (wp->flags & PANE_RESIZEFORCE) {
wp->flags &= ~PANE_RESIZEFORCE;
return (0);
}
if (wp->sx != wp->osx ||
wp->sy != wp->osy ||
wp->sx <= 1 ||
wp->sy <= 1)
return (0);
memset(&ws, 0, sizeof ws);
ws.ws_col = wp->sx;
ws.ws_row = wp->sy - 1;
if (ioctl(wp->fd, TIOCSWINSZ, &ws) == -1)
#ifdef __sun
if (errno != EINVAL && errno != ENXIO)
#endif
fatal("ioctl failed");
log_debug("%s: %%%u forcing resize", __func__, wp->id);
evtimer_add(&wp->resize_timer, &tv);
wp->flags |= PANE_RESIZEFORCE;
return (1);
}
/* Resize timer event. */
static void
server_client_resize_event(__unused int fd, __unused short events, void *data)
@@ -1027,12 +1090,13 @@ server_client_resize_event(__unused int fd, __unused short events, void *data)
if (!(wp->flags & PANE_RESIZE))
return;
if (server_client_resize_force(wp))
return;
memset(&ws, 0, sizeof ws);
ws.ws_col = wp->sx;
ws.ws_row = wp->sy;
if (ioctl(wp->fd, TIOCSWINSZ, &ws) == -1) {
if (ioctl(wp->fd, TIOCSWINSZ, &ws) == -1)
#ifdef __sun
/*
* Some versions of Solaris apparently can return an error when
@@ -1043,9 +1107,12 @@ server_client_resize_event(__unused int fd, __unused short events, void *data)
if (errno != EINVAL && errno != ENXIO)
#endif
fatal("ioctl failed");
}
log_debug("%s: %%%u resize to %u,%u", __func__, wp->id, wp->sx, wp->sy);
wp->flags &= ~PANE_RESIZE;
wp->osx = wp->sx;
wp->osy = wp->sy;
}
/* Check if pane should be resized. */
@@ -1056,6 +1123,7 @@ server_client_check_resize(struct window_pane *wp)
if (!(wp->flags & PANE_RESIZE))
return;
log_debug("%s: %%%u resize to %u,%u", __func__, wp->id, wp->sx, wp->sy);
if (!event_initialized(&wp->resize_timer))
evtimer_set(&wp->resize_timer, server_client_resize_event, wp);
@@ -1238,7 +1306,7 @@ server_client_check_redraw(struct client *c)
struct session *s = c->session;
struct tty *tty = &c->tty;
struct window_pane *wp;
int needed, flags, masked;
int needed, flags, masked;
struct timeval tv = { .tv_usec = 1000 };
static struct event ev;
size_t left;
@@ -1262,28 +1330,23 @@ server_client_check_redraw(struct client *c)
}
}
}
if (needed) {
left = EVBUFFER_LENGTH(tty->out);
if (left != 0) {
log_debug("%s: redraw deferred (%zu left)", c->name, left);
if (evtimer_initialized(&ev) && evtimer_pending(&ev, NULL))
return;
log_debug("redraw timer started");
if (needed && (left = EVBUFFER_LENGTH(tty->out)) != 0) {
log_debug("%s: redraw deferred (%zu left)", c->name, left);
if (!evtimer_initialized(&ev))
evtimer_set(&ev, server_client_redraw_timer, NULL);
if (!evtimer_pending(&ev, NULL)) {
log_debug("redraw timer started");
evtimer_add(&ev, &tv);
/*
* We may have got here for a single pane redraw, but
* force a full redraw next time in case other panes
* have been updated.
*/
c->flags |= CLIENT_REDRAW;
return;
}
if (evtimer_initialized(&ev))
evtimer_del(&ev);
/*
* We may have got here for a single pane redraw, but force a
* full redraw next time in case other panes have been updated.
*/
c->flags |= CLIENT_REDRAW;
return;
} else if (needed)
log_debug("%s: redraw needed", c->name);
}
if (c->flags & (CLIENT_REDRAW|CLIENT_STATUS)) {
if (options_get_number(s->options, "set-titles"))
@@ -1416,10 +1479,9 @@ server_client_dispatch(struct imsg *imsg, void *arg)
if (c->flags & CLIENT_CONTROL)
break;
if (tty_resize(&c->tty)) {
recalculate_sizes();
server_redraw_client(c);
}
tty_resize(&c->tty);
recalculate_sizes();
server_redraw_client(c);
if (c->session != NULL)
notify_client("client-resized", c);
break;
@@ -1543,7 +1605,7 @@ static void
server_client_dispatch_identify(struct client *c, struct imsg *imsg)
{
const char *data, *home;
size_t datalen;
size_t datalen;
int flags;
char *name;
@@ -1667,7 +1729,7 @@ server_client_dispatch_shell(struct client *c)
shell = options_get_string(global_s_options, "default-shell");
if (*shell == '\0' || areshell(shell))
shell = _PATH_BSHELL;
proc_send_s(c->peer, MSG_SHELL, shell);
proc_send(c->peer, MSG_SHELL, -1, shell, strlen(shell) + 1);
proc_kill_peer(c->peer);
}
@@ -1688,7 +1750,7 @@ void
server_client_push_stdout(struct client *c)
{
struct msg_stdout_data data;
size_t sent, left;
size_t sent, left;
left = EVBUFFER_LENGTH(c->stdout_data);
while (left != 0) {
@@ -1729,7 +1791,7 @@ void
server_client_push_stderr(struct client *c)
{
struct msg_stderr_data data;
size_t sent, left;
size_t sent, left;
if (c->stderr_data == c->stdout_data) {
server_client_push_stdout(c);

View File

@@ -162,7 +162,7 @@ server_lock_client(struct client *c)
return;
cmd = options_get_string(c->session->options, "lock-command");
if (strlen(cmd) + 1 > MAX_IMSGSIZE - IMSG_HEADER_SIZE)
if (*cmd == '\0' || strlen(cmd) + 1 > MAX_IMSGSIZE - IMSG_HEADER_SIZE)
return;
tty_stop_tty(&c->tty);
@@ -171,7 +171,7 @@ server_lock_client(struct client *c)
tty_raw(&c->tty, tty_term_string(c->tty.term, TTYC_E3));
c->flags |= CLIENT_SUSPENDED;
proc_send_s(c->peer, MSG_LOCK, cmd);
proc_send(c->peer, MSG_LOCK, -1, cmd, strlen(cmd) + 1);
}
void
@@ -300,7 +300,7 @@ server_destroy_pane(struct window_pane *wp, int notify)
screen_write_start(&ctx, wp, &wp->base);
screen_write_scrollregion(&ctx, 0, screen_size_y(ctx.s) - 1);
screen_write_cursormove(&ctx, 0, screen_size_y(ctx.s) - 1);
screen_write_linefeed(&ctx, 1);
screen_write_linefeed(&ctx, 1, 8);
memcpy(&gc, &grid_default_cell, sizeof gc);
gc.attr |= GRID_ATTR_BRIGHT;
screen_write_puts(&ctx, &gc, "Pane is dead");
@@ -334,7 +334,7 @@ server_destroy_session_group(struct session *s)
else {
TAILQ_FOREACH_SAFE(s, &sg->sessions, gentry, s1) {
server_destroy_session(s);
session_destroy(s);
session_destroy(s, __func__);
}
}
}
@@ -400,7 +400,7 @@ server_check_unattached(void)
if (!(s->flags & SESSION_UNATTACHED))
continue;
if (options_get_number (s->options, "destroy-unattached"))
session_destroy(s);
session_destroy(s, __func__);
}
}

View File

@@ -135,22 +135,39 @@ server_create_socket(void)
/* Fork new server. */
int
server_start(struct event_base *base, int lockfd, char *lockfile)
server_start(struct tmuxproc *client, struct event_base *base, int lockfd,
char *lockfile)
{
int pair[2];
struct job *job;
sigset_t set, oldset;
if (socketpair(AF_UNIX, SOCK_STREAM, PF_UNSPEC, pair) != 0)
fatal("socketpair failed");
server_proc = proc_start("server", base, 1, server_signal);
if (server_proc == NULL) {
sigfillset(&set);
sigprocmask(SIG_BLOCK, &set, &oldset);
switch (fork()) {
case -1:
fatal("fork failed");
case 0:
break;
default:
sigprocmask(SIG_SETMASK, &oldset, NULL);
close(pair[1]);
return (pair[0]);
}
close(pair[0]);
if (daemon(1, 0) != 0)
fatal("daemon failed");
proc_clear_signals(client, 0);
if (event_reinit(base) != 0)
fatalx("event_reinit failed");
server_proc = proc_start("server");
proc_set_signals(server_proc, server_signal);
sigprocmask(SIG_SETMASK, &oldset, NULL);
if (log_get_level() > 3)
if (log_get_level() > 1)
tty_create_log();
if (pledge("stdio rpath wpath cpath fattr unix getpw recvfd proc exec "
"tty ps", NULL) != 0)
@@ -248,7 +265,7 @@ server_send_exit(void)
}
RB_FOREACH_SAFE(s, sessions, &sessions, s1)
session_destroy(s);
session_destroy(s, __func__);
}
/* Update socket execute permissions based on whether sessions are attached. */
@@ -346,6 +363,7 @@ server_signal(int sig)
{
int fd;
log_debug("%s: %s", __func__, strsignal(sig));
switch (sig) {
case SIGTERM:
server_exit = 1;
@@ -364,6 +382,9 @@ server_signal(int sig)
}
server_add_accept(0);
break;
case SIGUSR2:
proc_toggle_log(server_proc);
break;
}
}
@@ -402,7 +423,12 @@ server_child_exited(pid_t pid, int status)
TAILQ_FOREACH(wp, &w->panes, entry) {
if (wp->pid == pid) {
wp->status = status;
server_destroy_pane(wp, 1);
log_debug("%%%u exited", wp->id);
wp->flags |= PANE_EXITED;
if (window_pane_destroy_ready(wp))
server_destroy_pane(wp, 1);
break;
}
}

View File

@@ -177,7 +177,7 @@ session_create(const char *prefix, const char *name, int argc, char **argv,
if (argc >= 0) {
wl = session_new(s, NULL, argc, argv, path, cwd, idx, cause);
if (wl == NULL) {
session_destroy(s);
session_destroy(s, __func__);
return (NULL);
}
session_select(s, RB_ROOT(&s->windows)->idx);
@@ -228,11 +228,11 @@ session_free(__unused int fd, __unused short events, void *arg)
/* Destroy a session. */
void
session_destroy(struct session *s)
session_destroy(struct session *s, const char *from)
{
struct winlink *wl;
log_debug("session %s destroyed", s->name);
log_debug("session %s destroyed (%s)", s->name, from);
s->curw = NULL;
RB_REMOVE(sessions, &sessions, s);
@@ -418,7 +418,7 @@ session_detach(struct session *s, struct winlink *wl)
session_group_synchronize_from(s);
if (RB_EMPTY(&s->windows)) {
session_destroy(s);
session_destroy(s, __func__);
return (1);
}
return (0);

105
signal.c
View File

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

View File

@@ -301,6 +301,13 @@ status_redraw(struct client *c)
size_t llen, rlen, seplen;
int larrow, rarrow;
/* Delete the saved status line, if any. */
if (c->old_status != NULL) {
screen_free(c->old_status);
free(c->old_status);
c->old_status = NULL;
}
/* No status line? */
if (c->tty.sy == 0 || !options_get_number(s->options, "status"))
return (1);
@@ -568,6 +575,12 @@ status_message_set(struct client *c, const char *fmt, ...)
status_message_clear(c);
if (c->old_status == NULL) {
c->old_status = xmalloc(sizeof *c->old_status);
memcpy(c->old_status, &c->status, sizeof *c->old_status);
screen_init(&c->status, c->tty.sx, 1, 0);
}
va_start(ap, fmt);
xvasprintf(&c->message_string, fmt, ap);
va_end(ap);
@@ -656,8 +669,7 @@ status_message_redraw(struct client *c)
/* Enable status line prompt. */
void
status_prompt_set(struct client *c, const char *msg, const char *input,
int (*callbackfn)(void *, const char *, int), void (*freefn)(void *),
void *data, int flags)
prompt_input_cb inputcb, prompt_free_cb freecb, void *data, int flags)
{
struct format_tree *ft;
time_t t;
@@ -665,20 +677,31 @@ status_prompt_set(struct client *c, const char *msg, const char *input,
ft = format_create(c, NULL, FORMAT_NONE, 0);
format_defaults(ft, c, NULL, NULL, NULL);
t = time(NULL);
tmp = format_expand_time(ft, input, t);
if (input == NULL)
input = "";
if (flags & PROMPT_NOFORMAT)
tmp = xstrdup(input);
else
tmp = format_expand_time(ft, input, t);
status_message_clear(c);
status_prompt_clear(c);
if (c->old_status == NULL) {
c->old_status = xmalloc(sizeof *c->old_status);
memcpy(c->old_status, &c->status, sizeof *c->old_status);
screen_init(&c->status, c->tty.sx, 1, 0);
}
c->prompt_string = format_expand_time(ft, msg, t);
c->prompt_buffer = utf8_fromcstr(tmp);
c->prompt_index = utf8_strlen(c->prompt_buffer);
c->prompt_callbackfn = callbackfn;
c->prompt_freefn = freefn;
c->prompt_inputcb = inputcb;
c->prompt_freecb = freecb;
c->prompt_data = data;
c->prompt_hindex = 0;
@@ -692,7 +715,7 @@ status_prompt_set(struct client *c, const char *msg, const char *input,
if ((flags & PROMPT_INCREMENTAL) && *tmp != '\0') {
xasprintf(&cp, "=%s", tmp);
c->prompt_callbackfn(c->prompt_data, cp, 0);
c->prompt_inputcb(c, c->prompt_data, cp, 0);
free(cp);
}
@@ -707,8 +730,8 @@ status_prompt_clear(struct client *c)
if (c->prompt_string == NULL)
return;
if (c->prompt_freefn != NULL && c->prompt_data != NULL)
c->prompt_freefn(c->prompt_data);
if (c->prompt_freecb != NULL && c->prompt_data != NULL)
c->prompt_freecb(c->prompt_data);
free(c->prompt_string);
c->prompt_string = NULL;
@@ -995,7 +1018,7 @@ status_prompt_key(struct client *c, key_code key)
if (key >= '0' && key <= '9')
goto append_key;
s = utf8_tocstr(c->prompt_buffer);
c->prompt_callbackfn(c->prompt_data, s, 1);
c->prompt_inputcb(c, c->prompt_data, s, 1);
status_prompt_clear(c);
free(s);
return (1);
@@ -1276,13 +1299,13 @@ process_key:
s = utf8_tocstr(c->prompt_buffer);
if (*s != '\0')
status_prompt_add_history(s);
if (c->prompt_callbackfn(c->prompt_data, s, 1) == 0)
if (c->prompt_inputcb(c, c->prompt_data, s, 1) == 0)
status_prompt_clear(c);
free(s);
break;
case '\033': /* Escape */
case '\003': /* C-c */
if (c->prompt_callbackfn(c->prompt_data, NULL, 1) == 0)
if (c->prompt_inputcb(c, c->prompt_data, NULL, 1) == 0)
status_prompt_clear(c);
break;
case '\022': /* C-r */
@@ -1330,7 +1353,7 @@ append_key:
s = utf8_tocstr(c->prompt_buffer);
if (strlen(s) != 1)
status_prompt_clear(c);
else if (c->prompt_callbackfn(c->prompt_data, s, 1) == 0)
else if (c->prompt_inputcb(c, c->prompt_data, s, 1) == 0)
status_prompt_clear(c);
free(s);
}
@@ -1340,7 +1363,7 @@ changed:
if (c->prompt_flags & PROMPT_INCREMENTAL) {
s = utf8_tocstr(c->prompt_buffer);
xasprintf(&cp, "%c%s", prefix, s);
c->prompt_callbackfn(c->prompt_data, cp, 0);
c->prompt_inputcb(c, c->prompt_data, cp, 0);
free(cp);
free(s);
}

505
tmux.1
View File

@@ -199,7 +199,6 @@ characters to the terminal it is running (if not, they are replaced by
.Ql _ ) .
.It Fl v
Request verbose logging.
This option may be specified multiple times for increasing verbosity.
Log messages will be saved into
.Pa tmux-client-PID.log
and
@@ -207,10 +206,26 @@ and
files in the current directory, where
.Em PID
is the PID of the server or client process.
If
.Fl v
is specified twice, an additional
.Pa tmux-out-PID.log
file is generated with a copy of everything
.Nm
writes to the terminal.
.Pp
The
.Dv SIGUSR2
signal may be sent to the
.Nm
server process to toggle logging between on (as if
.Fl v
was given) and off.
.It Fl V
Report the
.Nm
version.
.Pp
.It Ar command Op Ar flags
This specifies one of a set of commands used to control
.Nm ,
@@ -833,13 +848,13 @@ is given.
and
.Ar shell-command
are the name of and shell command to execute in the initial window.
If
.Fl d
is used,
With
.Fl d ,
the initial size is 80 x 24;
.Fl x
and
.Fl y
specify the size of the initial window.
can be used to specify a different size.
.Pp
If run from a terminal, any
.Xr termios 4
@@ -1343,139 +1358,125 @@ the end of the visible pane.
The default is to capture only the visible contents of the pane.
.It Xo
.Ic choose-client
.Op Fl N
.Op Fl F Ar format
.Op Fl t Ar target-window
.Op Fl f Ar filter
.Op Fl O Ar sort-order
.Op Fl t Ar target-pane
.Op Ar template
.Xc
Put a window into client choice mode, allowing a client to be selected
interactively from a list.
Put a pane into client mode, allowing a client to be selected interactively from
a list.
The following keys may be used in client mode:
.Bl -column "Key" "Function" -offset indent
.It Sy "Key" Ta Sy "Function"
.It Li "Enter" Ta "Choose selected client"
.It Li "Up" Ta "Select previous client"
.It Li "Down" Ta "Select next client"
.It Li "C-s" Ta "Search by name"
.It Li "n" Ta "Repeat last search"
.It Li "t" Ta "Toggle if client is tagged"
.It Li "T" Ta "Tag no clients"
.It Li "C-t" Ta "Tag all clients"
.It Li "d" Ta "Detach selected client"
.It Li "D" Ta "Detach tagged clients"
.It Li "x" Ta "Detach and HUP selected client"
.It Li "X" Ta "Detach and HUP tagged clients"
.It Li "z" Ta "Suspend selected client"
.It Li "Z" Ta "Suspend tagged clients"
.It Li "f" Ta "Enter a format to filter items"
.It Li "O" Ta "Change sort order"
.It Li "v" Ta "Toggle preview"
.It Li "q" Ta "Exit mode"
.El
.Pp
After a client is chosen,
.Ql %%
is replaced by the client
.Xr pty 4
path in
is replaced by the client name in
.Ar template
and the result executed as a command.
If
.Ar template
is not given, "detach-client -t '%%'" is used.
For the meaning of the
.Pp
.Fl O
specifies the initial sort order: one of
.Ql name ,
.Ql size ,
.Ql creation ,
or
.Ql activity .
.Fl f
specifies an initial filter: the filter is a format - if it evaluates to zero,
the item in the list is not shown, otherwise it is shown.
If a filter would lead to an empty list, it is ignored.
.Fl F
flag, see the
.Sx FORMATS
section.
specifies the format for each item in the list.
.Fl N
starts without the preview.
This command works only if at least one client is attached.
.It Xo
.Ic choose-session
.Ic choose-tree
.Op Fl Nsw
.Op Fl F Ar format
.Op Fl t Ar target-window
.Op Fl f Ar filter
.Op Fl O Ar sort-order
.Op Fl t Ar target-pane
.Op Ar template
.Xc
Put a window into session choice mode, where a session may be selected
Put a pane into tree mode, where a session, window or pane may be chosen
interactively from a list.
When one is chosen,
.Fl s
starts with sessions collapsed and
.Fl w
with windows collapsed.
The following keys may be used in tree mode:
.Bl -column "Key" "Function" -offset indent
.It Sy "Key" Ta Sy "Function"
.It Li "Enter" Ta "Choose selected item"
.It Li "Up" Ta "Select previous item"
.It Li "Down" Ta "Select next item"
.It Li "<" Ta "Scroll list of previews left"
.It Li ">" Ta "Scroll list of previews right"
.It Li "C-s" Ta "Search by name"
.It Li "n" Ta "Repeat last search"
.It Li "t" Ta "Toggle if item is tagged"
.It Li "T" Ta "Tag no items"
.It Li "C-t" Ta "Tag all items"
.It Li "\&:" Ta "Run a command for each tagged item"
.It Li "f" Ta "Enter a format to filter items"
.It Li "O" Ta "Change sort order"
.It Li "v" Ta "Toggle preview"
.It Li "q" Ta "Exit mode"
.El
.Pp
After a session, window or pane is chosen,
.Ql %%
is replaced by the session name in
is replaced by the target in
.Ar template
and the result executed as a command.
If
.Ar template
is not given, "switch-client -t '%%'" is used.
For the meaning of the
.Pp
.Fl O
specifies the initial sort order: one of
.Ql index ,
.Ql name ,
or
.Ql time .
.Fl f
specifies an initial filter: the filter is a format - if it evaluates to zero,
the item in the list is not shown, otherwise it is shown.
If a filter would lead to an empty list, it is ignored.
.Fl F
flag, see the
.Sx FORMATS
section.
This command works only if at least one client is attached.
.It Xo
.Ic choose-tree
.Op Fl suw
.Op Fl b Ar session-template
.Op Fl c Ar window-template
.Op Fl S Ar format
.Op Fl W Ar format
.Op Fl t Ar target-window
.Xc
Put a window into tree choice mode, where either sessions or windows may be
selected interactively from a list.
By default, windows belonging to a session are indented to show their
relationship to a session.
.Pp
Note that the
.Ic choose-window
and
.Ic choose-session
commands are wrappers around
.Ic choose-tree .
.Pp
If
.Fl s
is given, will show sessions.
If
.Fl w
is given, will show windows.
.Pp
By default, the tree is collapsed and sessions must be expanded to windows
with the right arrow key.
The
.Fl u
option will start with all sessions expanded instead.
.Pp
If
.Fl b
is given, will override the default session command.
Note that
.Ql %%
can be used and will be replaced with the session name.
The default option if not specified is "switch-client -t '%%'".
If
.Fl c
is given, will override the default window command.
Like
.Fl b ,
.Ql %%
can be used and will be replaced with the session name and window index.
When a window is chosen from the list, the session command is run before the
window command.
.Pp
.Fl S
uses
.Ar format
instead of the default session
format and
.Fl W
instead of the default window format.
For the meaning of
.Ar format ,
see the
.Sx FORMATS
section.
.Pp
This command works only if at least one client is attached.
.It Xo
.Ic choose-window
.Op Fl F Ar format
.Op Fl t Ar target-window
.Op Ar template
.Xc
Put a window into window choice mode, where a window may be chosen
interactively from a list.
After a window is selected,
.Ql %%
is replaced by the session name and window index in
.Ar template
and the result executed as a command.
If
.Ar template
is not given, "select-window -t '%%'" is used.
For the meaning of the
.Fl F
flag, see the
.Sx FORMATS
section.
specifies the format for each item in the tree.
.Fl N
starts without the preview.
This command works only if at least one client is attached.
.It Xo
.Ic display-panes
.Op Fl d Ar duration
.Op Fl t Ar target-client
.Op Ar template
.Xc
@@ -1483,11 +1484,19 @@ This command works only if at least one client is attached.
Display a visible indicator of each pane shown by
.Ar target-client .
See the
.Ic display-panes-time ,
.Ic display-panes-colour ,
.Ic display-panes-colour
and
.Ic display-panes-active-colour
session options.
The indicator is closed when a key is pressed or
.Ar duration
milliseconds have passed.
If
.Fl d
is not given,
.Ic display-panes-time
is used.
A duration of zero means the indicator stays until a key is pressed.
While the indicator is on screen, a pane may be chosen with the
.Ql 0
to
@@ -1502,8 +1511,7 @@ The default
is "select-pane -t '%%'".
.It Xo Ic find-window
.Op Fl CNT
.Op Fl F Ar format
.Op Fl t Ar target-window
.Op Fl t Ar target-pane
.Ar match-string
.Xc
.D1 (alias: Ic findw )
@@ -1521,13 +1529,7 @@ matches only the window name and
matches only the window title.
The default is
.Fl CNT .
If only one window is matched, it'll be automatically selected,
otherwise a choice list is shown.
For the meaning of the
.Fl F
flag, see the
.Sx FORMATS
section.
.Pp
This command works only if at least one client is attached.
.It Xo Ic join-pane
.Op Fl bdhv
@@ -1862,6 +1864,7 @@ and unzoomed (its normal position in the layout).
begins mouse resizing (only valid if bound to a mouse key binding, see
.Sx MOUSE SUPPORT ) .
.It Xo Ic respawn-pane
.Op Fl c Ar start-directory
.Op Fl k
.Op Fl t Ar target-pane
.Op Ar shell-command
@@ -1876,7 +1879,10 @@ is not given, the command used when the pane was created is executed.
The pane must be already inactive, unless
.Fl k
is given, in which case any existing command is killed.
.Fl c
specifies a new working directory for the pane.
.It Xo Ic respawn-window
.Op Fl c Ar start-directory
.Op Fl k
.Op Fl t Ar target-window
.Op Ar shell-command
@@ -1891,6 +1897,8 @@ is not given, the command used when the window was created is executed.
The window must be already inactive, unless
.Fl k
is given, in which case any existing command is killed.
.Fl c
specifies a new working directory for the window.
.It Xo Ic rotate-window
.Op Fl DU
.Op Fl t Ar target-window
@@ -1923,6 +1931,7 @@ applies the last set layout if possible (undoes the most recent layout change).
.It Xo Ic select-pane
.Op Fl DdegLlMmRU
.Op Fl P Ar style
.Op Fl T Ar title
.Op Fl t Ar target-pane
.Xc
.D1 (alias: Ic selectp )
@@ -1978,6 +1987,9 @@ select-pane -t:.1 -P 'bg=red'
.Pp
.Fl g
shows the current pane style.
.Pp
.Fl T
sets the pane title.
.It Xo Ic select-window
.Op Fl lnpT
.Op Fl t Ar target-window
@@ -2335,7 +2347,7 @@ abc123
Commands which set options are as follows:
.Bl -tag -width Ds
.It Xo Ic set-option
.Op Fl agoqsuw
.Op Fl aFgoqsuw
.Op Fl t Ar target-session | Ar target-window
.Ar option Ar value
.Xc
@@ -2351,6 +2363,8 @@ otherwise a session option.
If
.Fl g
is given, the global session or window option is set.
.Fl F
expands formats in the option value.
The
.Fl u
flag unsets an option, so a session inherits the option from the global
@@ -2413,7 +2427,7 @@ it is replaced with
.Ar value .
For example, after:
.Pp
.Dl set -s command-alias[2] zoom='resize-pane -Z'
.Dl set -s command-alias[100] zoom='resize-pane -Z'
.Pp
Using:
.Pp
@@ -2467,17 +2481,36 @@ Set the number of error or information messages to save in the message log for
each client.
The default is 100.
.It Xo Ic set-clipboard
.Op Ic on | off
.Op Ic on | external | off
.Xc
Attempt to set the terminal clipboard content using the
\ee]52;...\e007
.Xr xterm 1
escape sequences.
This option is on by default if there is an
escape sequence, if there is an
.Em \&Ms
entry in the
.Xr terminfo 5
description for the client terminal.
description (see the
.Sx TERMINFO EXTENSIONS
section).
.Pp
If set to
.Ic on ,
.Nm
will both accept the escape sequence to create a buffer and attempt to set
the terminal clipboard.
If set to
.Ic external ,
.Nm
will attempt to set the terminal clipboard but ignore attempts
by applications to set
.Nm
buffers.
If
.Ic off ,
.Nm
will neither accept the clipboard escape sequence nor attempt to set the
clipboard.
.Pp
Note that this feature needs to be enabled in
.Xr xterm 1
by setting the resource:
@@ -2516,6 +2549,25 @@ before interpretation.
.Pp
Available session options are:
.Bl -tag -width Ds
.It Xo Ic activity-action
.Op Ic any | none | current | other
.Xc
Set action on window activity when
.Ic monitor-activity
is on.
.Ic any
means activity in any window linked to a session causes a bell or message
(depending on
.Ic visual-activity )
in the current window of that session,
.Ic none
means all activity is ignored (equivalent to
.Ic monitor-activity
being off),
.Ic current
means only activity in windows other than the current window are ignored and
.Ic other
means activity in the current window is ignored but not those in other windows.
.It Ic assume-paste-time Ar milliseconds
If keys are entered faster than one in
.Ar milliseconds ,
@@ -2530,21 +2582,11 @@ The default is zero.
.It Xo Ic bell-action
.Op Ic any | none | current | other
.Xc
Set action on window bell.
.Ic any
means a bell in any window linked to a session causes a bell in the current
window of that session,
.Ic none
means all bells are ignored,
.Ic current
means only bells in windows other than the current window are ignored and
.Ic other
means bells in the current window are ignored but not those in other windows.
.It Xo Ic bell-on-alert
.Op Ic on | off
.Xc
If on, ring the terminal bell when an alert
occurs.
Set action on a bell in a window when
.Ic monitor-bell
is on.
The values are the same as those for
.Ic activity-action .
.It Ic default-command Ar shell-command
Set the command used for new windows (if not specified when the window is
created) to
@@ -2760,6 +2802,14 @@ is on.
Formats are expanded, see the
.Sx FORMATS
section.
.It Xo Ic silence-action
.Op Ic any | none | current | other
.Xc
Set action on window silence when
.Ic monitor-silence
is on.
The values are the same as those for
.Ic activity-action .
.It Xo Ic status
.Op Ic on | off
.Xc
@@ -2836,7 +2886,7 @@ Set the position of the status line.
Display
.Ar string
to the right of the status line.
By default, the current window title in double quotes, the date and the time
By default, the current pane title in double quotes, the date and the time
are shown.
As with
.Ic status-left ,
@@ -2872,27 +2922,45 @@ removed from the session environment (as if
was given to the
.Ic set-environment
command).
.It Ic user-keys[] Ar key
Set list of user-defined key escape sequences.
Each item is associated with a key named
.Ql User0 ,
.Ql User1 ,
and so on.
.Pp
For example:
.Bd -literal -offset indent
set -s user-keys[0] "\ee[5;30012~"
bind User0 resize-pane -L 3
.Ed
.It Xo Ic visual-activity
.Op Ic on | off
.Op Ic on | off | both
.Xc
If on, display a status line message when activity occurs in a window
for which the
If on, display a message instead of sending a bell when activity occurs in a
window for which the
.Ic monitor-activity
window option is enabled.
If set to both, a bell and a message are produced.
.It Xo Ic visual-bell
.Op Ic on | off
.Op Ic on | off | both
.Xc
If this option is on, a message is shown on a bell instead of it being passed
through to the terminal (which normally makes a sound).
If on, a message is shown on a bell in a window for which the
.Ic monitor-bell
window option is enabled instead of it being passed through to the
terminal (which normally makes a sound).
If set to both, a bell and a message are produced.
Also see the
.Ic bell-action
option.
.It Xo Ic visual-silence
.Op Ic on | off
.Op Ic on | off | both
.Xc
If
.Ic monitor-silence
is enabled, prints a message after the interval has expired on a given window.
is enabled, prints a message after the interval has expired on a given window
instead of sending a bell.
If set to both, a bell and a message are produced.
.It Ic word-separators Ar string
Sets the session's conception of what characters are considered word
separators, for the purposes of the next and previous word commands in
@@ -2901,7 +2969,7 @@ The default is
.Ql \ -_@ .
.El
.It Xo Ic set-window-option
.Op Fl agoqu
.Op Fl aFgoqu
.Op Fl t Ar target-window
.Ar option Ar value
.Xc
@@ -2909,6 +2977,7 @@ The default is
Set a window option.
The
.Fl a ,
.Fl F ,
.Fl g ,
.Fl o ,
.Fl q
@@ -3036,6 +3105,12 @@ option.
Monitor for activity in the window.
Windows with activity are highlighted in the status line.
.Pp
.It Xo Ic monitor-bell
.Op Ic on | off
.Xc
Monitor for a bell in the window.
Windows with a bell are highlighted in the status line.
.Pp
.It Xo Ic monitor-silence
.Op Ic interval
.Xc
@@ -3262,13 +3337,15 @@ set-hook after-split-window "selectl even-vertical"
.Ed
.Pp
In addition, the following hooks are available:
.Bl -tag -width "XXXXXXXXXXXXXXXX"
.Bl -tag -width "XXXXXXXXXXXXXXXXXXXXXX"
.It alert-activity
Run when a window has activity.
See
.Ic monitor-activity .
.It alert-bell
Run when a window has received a bell.
See
.Ic monitor-bell .
.It alert-silence
Run when a window has been silent.
See
@@ -3287,6 +3364,10 @@ Run when the program running in a pane exits, but
is on so the pane has not closed.
.It pane-exited
Run when the program running in a pane exits.
.It pane-set-clipboard
Run when the terminal clipboard is set using the
.Xr xterm 1
escape sequence.
.It session-created
Run when a new session created.
.It session-closed
@@ -3431,7 +3512,7 @@ is enabled, or
.Ql no
if not.
.Pp
Simple comparisons may be expressed by prefixing two comma-separated
Comparisons may be expressed by prefixing two comma-separated
alternatives by
.Ql ==
or
@@ -3444,7 +3525,26 @@ will be replaced by
if running on
.Ql myhost ,
otherwise by
.Ql 0.
.Ql 0 .
An
.Ql m
specifies an
.Xr fnmatch 3
comparison where the first argument is the pattern and the second the string to
compare, for example
.Ql #{m:*foo*,#{host}} .
.Ql ||
and
.Ql &&
evaluate to true if either or both of two comma-separated alternatives are
true, for example
.Ql #{||,#{pane_in_mode},#{alternate_on}} .
A
.Ql C
performs a search for an
.Xr fnmatch 3
pattern in the pane content and evaluates to zero if not found, or a line
number if found.
.Pp
A limit may be placed on the length of the resultant string by prefixing it
by an
@@ -3507,11 +3607,12 @@ The following variables are available, where appropriate:
.It Li "alternate_on" Ta "" Ta "If pane is in alternate screen"
.It Li "alternate_saved_x" Ta "" Ta "Saved cursor X in alternate screen"
.It Li "alternate_saved_y" Ta "" Ta "Saved cursor Y in alternate screen"
.It Li "buffer_created" Ta "" Ta "Time buffer created"
.It Li "buffer_name" Ta "" Ta "Name of buffer"
.It Li "buffer_sample" Ta "" Ta "Sample of start of buffer"
.It Li "buffer_size" Ta "" Ta "Size of the specified buffer in bytes"
.It Li "client_activity" Ta "" Ta "Integer time client last had activity"
.It Li "client_created" Ta "" Ta "Integer time client created"
.It Li "client_activity" Ta "" Ta "Time client last had activity"
.It Li "client_created" Ta "" Ta "Time client created"
.It Li "client_control_mode" Ta "" Ta "1 if client is in control mode"
.It Li "client_discarded" Ta "" Ta "Bytes discarded when client behind"
.It Li "client_height" Ta "" Ta "Height of client"
@@ -3555,11 +3656,16 @@ The following variables are available, where appropriate:
.It Li "mouse_standard_flag" Ta "" Ta "Pane mouse standard flag"
.It Li "mouse_all_flag" Ta "" Ta "Pane mouse all flag"
.It Li "pane_active" Ta "" Ta "1 if active pane"
.It Li "pane_at_bottom" Ta "" Ta "1 if pane is at the bottom of window"
.It Li "pane_at_left" Ta "" Ta "1 if pane is at the left of window"
.It Li "pane_at_right" Ta "" Ta "1 if pane is at the right of window"
.It Li "pane_at_top" Ta "" Ta "1 if pane is at the top of window"
.It Li "pane_bottom" Ta "" Ta "Bottom of pane"
.It Li "pane_current_command" Ta "" Ta "Current command if available"
.It Li "pane_current_path" Ta "" Ta "Current path if available"
.It Li "pane_dead" Ta "" Ta "1 if pane is dead"
.It Li "pane_dead_status" Ta "" Ta "Exit status of process in dead pane"
.It Li "pane_format" Ta "" Ta "1 if format is for a pane (not assuming the current)"
.It Li "pane_height" Ta "" Ta "Height of pane"
.It Li "pane_id" Ta "#D" Ta "Unique pane ID"
.It Li "pane_in_mode" Ta "" Ta "If pane is in a mode"
@@ -3568,6 +3674,7 @@ The following variables are available, where appropriate:
.It Li "pane_left" Ta "" Ta "Left of pane"
.It Li "pane_mode" Ta "" Ta "Name of pane mode, if any."
.It Li "pane_pid" Ta "" Ta "PID of first process in pane"
.It Li "pane_pipe" Ta "" Ta "1 if pane is being piped"
.It Li "pane_right" Ta "" Ta "Right of pane"
.It Li "pane_search_string" Ta "" Ta "Last search string in copy mode"
.It Li "pane_start_command" Ta "" Ta "Command pane started with"
@@ -3581,11 +3688,13 @@ The following variables are available, where appropriate:
.It Li "scroll_region_lower" Ta "" Ta "Bottom of scroll region in pane"
.It Li "scroll_region_upper" Ta "" Ta "Top of scroll region in pane"
.It Li "scroll_position" Ta "" Ta "Scroll position in copy mode"
.It Li "selection_present" Ta "" Ta "1 if selection started in copy mode"
.It Li "session_alerts" Ta "" Ta "List of window indexes with alerts"
.It Li "session_attached" Ta "" Ta "Number of clients session is attached to"
.It Li "session_activity" Ta "" Ta "Integer time of session last activity"
.It Li "session_created" Ta "" Ta "Integer time session created"
.It Li "session_last_attached" Ta "" Ta "Integer time session last attached"
.It Li "session_activity" Ta "" Ta "Time of session last activity"
.It Li "session_created" Ta "" Ta "Time session created"
.It Li "session_format" Ta "" Ta "1 if format is for a session (not assuming the current)"
.It Li "session_last_attached" Ta "" Ta "Time session last attached"
.It Li "session_group" Ta "" Ta "Name of session group"
.It Li "session_grouped" Ta "" Ta "1 if session in a group"
.It Li "session_height" Ta "" Ta "Height of session"
@@ -3598,12 +3707,12 @@ The following variables are available, where appropriate:
.It Li "socket_path" Ta "" Ta "Server socket path"
.It Li "start_time" Ta "" Ta "Server start time"
.It Li "version" Ta "" Ta "Server version"
.It Li "window_activity" Ta "" Ta "Integer time of window last activity"
.It Li "window_activity" Ta "" Ta "Time of window last activity"
.It Li "window_activity_flag" Ta "" Ta "1 if window has activity"
.It Li "window_active" Ta "" Ta "1 if window active"
.It Li "window_bell_flag" Ta "" Ta "1 if window has bell"
.It Li "window_find_matches" Ta "" Ta "Matched data from the find-window"
.It Li "window_flags" Ta "#F" Ta "Window flags"
.It Li "window_format" Ta "" Ta "1 if format is for a window (not assuming the current)"
.It Li "window_height" Ta "" Ta "Height of window"
.It Li "window_id" Ta "" Ta "Unique window ID"
.It Li "window_index" Ta "#I" Ta "Index of window"
@@ -3627,14 +3736,11 @@ and are displayed in the status line and various lists: the name is the
.Nm
identifier for a window or session.
Only panes have titles.
A pane's title is typically set by the program running inside the pane and
is not modified by
.Nm .
It is the same mechanism used to set for example the
A pane's title is typically set by the program running inside the pane using
an escape sequence (like it would set the
.Xr xterm 1
window title in an
.Xr X 7
window manager.
window title in
.Xr X 7 ) .
Windows themselves do not have titles - a window's title is the title of its
active pane.
.Nm
@@ -3675,6 +3781,11 @@ A pane's title can be set via the OSC title setting sequence, for example:
.Bd -literal -offset indent
$ printf '\e033]2;My Title\e033\e\e'
.Ed
.Pp
It can also be modified with the
.Ic select-pane
.Fl T
command.
.Sh ENVIRONMENT
When the server is started,
.Nm
@@ -3769,8 +3880,8 @@ The flag is one of the following symbols appended to the window name:
.It Sy "Symbol" Ta Sy "Meaning"
.It Li "*" Ta "Denotes the current window."
.It Li "-" Ta "Marks the last window (previously selected)."
.It Li "#" Ta "Window is monitored and activity has been detected."
.It Li "!" Ta "A bell has occurred in the window."
.It Li "#" Ta "Window activity is monitored and activity has been detected."
.It Li "\&!" Ta "Window bells are monitored and a bell has occurred in the window."
.It Li "~" Ta "The window has been silent for the monitor-silence interval."
.It Li "M" Ta "The window contains the marked pane."
.It Li "Z" Ta "The window's active pane is zoomed."
@@ -3966,13 +4077,35 @@ The buffer commands are as follows:
.Bl -tag -width Ds
.It Xo
.Ic choose-buffer
.Op Fl N
.Op Fl F Ar format
.Op Fl t Ar target-window
.Op Fl f Ar filter
.Op Fl O Ar sort-order
.Op Fl t Ar target-pane
.Op Ar template
.Xc
Put a window into buffer choice mode, where a buffer may be chosen
interactively from a list.
After a buffer is selected,
Put a pane into buffer mode, where a buffer may be chosen interactively from
a list.
The following keys may be used in buffer mode:
.Bl -column "Key" "Function" -offset indent
.It Sy "Key" Ta Sy "Function"
.It Li "Enter" Ta "Choose selected buffer"
.It Li "Up" Ta "Select previous buffer"
.It Li "Down" Ta "Select next buffer"
.It Li "C-s" Ta "Search by name or content"
.It Li "n" Ta "Repeat last search"
.It Li "t" Ta "Toggle if buffer is tagged"
.It Li "T" Ta "Tag no buffers"
.It Li "C-t" Ta "Tag all buffers"
.It Li "d" Ta "Delete selected buffer"
.It Li "D" Ta "Delete tagged buffers"
.It Li "f" Ta "Enter a format to filter items"
.It Li "O" Ta "Change sort order"
.It Li "v" Ta "Toggle preview"
.It Li "q" Ta "Exit mode"
.El
.Pp
After a buffer is chosen,
.Ql %%
is replaced by the buffer name in
.Ar template
@@ -3980,11 +4113,21 @@ and the result executed as a command.
If
.Ar template
is not given, "paste-buffer -b '%%'" is used.
For the meaning of the
.Pp
.Fl O
specifies the initial sort order: one of
.Ql time ,
.Ql name
or
.Ql size .
.Fl f
specifies an initial filter: the filter is a format - if it evaluates to zero,
the item in the list is not shown, otherwise it is shown.
If a filter would lead to an empty list, it is ignored.
.Fl F
flag, see the
.Sx FORMATS
section.
specifies the format for each item in the list.
.Fl N
starts without the preview.
This command works only if at least one client is attached.
.It Ic clear-history Op Fl t Ar target-pane
.D1 (alias: Ic clearhist )

15
tmux.c
View File

@@ -41,6 +41,7 @@ struct hooks *global_hooks;
struct timeval start_time;
const char *socket_path;
int ptm_fd = -1;
const char *shell_command;
static __dead void usage(void);
static char *make_label(const char *);
@@ -187,13 +188,14 @@ find_home(void)
int
main(int argc, char **argv)
{
char *path, *label, tmp[PATH_MAX];
char *shellcmd = NULL, **var;
char *path, *label, **var;
char tmp[PATH_MAX];
const char *s, *shell;
int opt, flags, keys;
const struct options_table_entry *oe;
if (setlocale(LC_CTYPE, "en_US.UTF-8") == NULL) {
if (setlocale(LC_CTYPE, "en_US.UTF-8") == NULL &&
setlocale(LC_CTYPE, "C.UTF-8") == NULL) {
if (setlocale(LC_CTYPE, "") == NULL)
errx(1, "invalid LC_ALL, LC_CTYPE or LANG");
s = nl_langinfo(CODESET);
@@ -216,8 +218,7 @@ main(int argc, char **argv)
flags |= CLIENT_256COLOURS;
break;
case 'c':
free(shellcmd);
shellcmd = xstrdup(optarg);
shell_command = optarg;
break;
case 'C':
if (flags & CLIENT_CONTROL)
@@ -257,7 +258,7 @@ main(int argc, char **argv)
argc -= optind;
argv += optind;
if (shellcmd != NULL && argc != 0)
if (shell_command != NULL && argc != 0)
usage();
if ((ptm_fd = getptmfd()) == -1)
@@ -347,5 +348,5 @@ main(int argc, char **argv)
free(label);
/* Pass control to the client. */
exit(client_main(osdep_event_init(), argc, argv, flags, shellcmd));
exit(client_main(osdep_event_init(), argc, argv, flags));
}

363
tmux.h
View File

@@ -42,12 +42,13 @@ extern char **environ;
struct args;
struct client;
struct cmd_find_state;
struct cmdq_item;
struct cmdq_list;
struct environ;
struct format_job_tree;
struct input_ctx;
struct mode_key_cmdstr;
struct mode_tree_data;
struct mouse_event;
struct options;
struct options_entry;
@@ -80,21 +81,30 @@ struct tmuxproc;
#define nitems(_a) (sizeof((_a)) / sizeof((_a)[0]))
#endif
/* Bell option values. */
#define BELL_NONE 0
#define BELL_ANY 1
#define BELL_CURRENT 2
#define BELL_OTHER 3
/* Alert option values. */
#define ALERT_NONE 0
#define ALERT_ANY 1
#define ALERT_CURRENT 2
#define ALERT_OTHER 3
/* Visual option values. */
#define VISUAL_OFF 0
#define VISUAL_ON 1
#define VISUAL_BOTH 2
/* Special key codes. */
#define KEYC_NONE 0xffff00000000ULL
#define KEYC_UNKNOWN 0xfffe00000000ULL
#define KEYC_BASE 0x000010000000ULL
#define KEYC_USER 0x000020000000ULL
/* Available user keys. */
#define KEYC_NUSER 1000
/* Key modifier bits. */
#define KEYC_ESCAPE 0x200000000000ULL
#define KEYC_CTRL 0x400000000000ULL
#define KEYC_SHIFT 0x800000000000ULL
#define KEYC_CTRL 0x400000000000ULL
#define KEYC_SHIFT 0x800000000000ULL
#define KEYC_XTERM 0x1000000000000ULL
/* Mask to obtain key w/o modifiers. */
@@ -130,6 +140,10 @@ enum {
KEYC_FOCUS_IN = KEYC_BASE,
KEYC_FOCUS_OUT,
/* Paste brackets. */
KEYC_PASTE_START,
KEYC_PASTE_END,
/* Mouse keys. */
KEYC_MOUSE, /* unclassified mouse event */
KEYC_DRAGGING, /* dragging in progress */
@@ -207,67 +221,67 @@ enum {
/* Termcap codes. */
enum tty_code_code {
TTYC_AX = 0,
TTYC_ACSC, /* acs_chars, ac */
TTYC_BCE, /* back_color_erase, ut */
TTYC_BEL, /* bell, bl */
TTYC_BLINK, /* enter_blink_mode, mb */
TTYC_BOLD, /* enter_bold_mode, md */
TTYC_CIVIS, /* cursor_invisible, vi */
TTYC_CLEAR, /* clear_screen, cl */
TTYC_CNORM, /* cursor_normal, ve */
TTYC_COLORS, /* max_colors, Co */
TTYC_CR, /* restore cursor colour, Cr */
TTYC_CS, /* set cursor colour, Cs */
TTYC_CSR, /* change_scroll_region, cs */
TTYC_CUB, /* parm_left_cursor, LE */
TTYC_CUB1, /* cursor_left, le */
TTYC_CUD, /* parm_down_cursor, DO */
TTYC_CUD1, /* cursor_down, do */
TTYC_CUF, /* parm_right_cursor, RI */
TTYC_CUF1, /* cursor_right, nd */
TTYC_CUP, /* cursor_address, cm */
TTYC_CUU, /* parm_up_cursor, UP */
TTYC_CUU1, /* cursor_up, up */
TTYC_CVVIS, /* cursor_visible, vs */
TTYC_DCH, /* parm_dch, DC */
TTYC_DCH1, /* delete_character, dc */
TTYC_DIM, /* enter_dim_mode, mh */
TTYC_DL, /* parm_delete_line, DL */
TTYC_DL1, /* delete_line, dl */
TTYC_ACSC,
TTYC_BCE,
TTYC_BEL,
TTYC_BLINK,
TTYC_BOLD,
TTYC_CIVIS,
TTYC_CLEAR,
TTYC_CNORM,
TTYC_COLORS,
TTYC_CR,
TTYC_CS,
TTYC_CSR,
TTYC_CUB,
TTYC_CUB1,
TTYC_CUD,
TTYC_CUD1,
TTYC_CUF,
TTYC_CUF1,
TTYC_CUP,
TTYC_CUU,
TTYC_CUU1,
TTYC_CVVIS,
TTYC_DCH,
TTYC_DCH1,
TTYC_DIM,
TTYC_DL,
TTYC_DL1,
TTYC_E3,
TTYC_ECH, /* erase_chars, ec */
TTYC_ED, /* clr_eos, cd */
TTYC_EL, /* clr_eol, ce */
TTYC_EL1, /* clr_bol, cb */
TTYC_ENACS, /* ena_acs, eA */
TTYC_FSL, /* from_status_line, fsl */
TTYC_HOME, /* cursor_home, ho */
TTYC_HPA, /* column_address, ch */
TTYC_ICH, /* parm_ich, IC */
TTYC_ICH1, /* insert_character, ic */
TTYC_IL, /* parm_insert_line, IL */
TTYC_IL1, /* insert_line, il */
TTYC_INDN, /* parm_index, indn */
TTYC_INVIS, /* enter_secure_mode, mk */
TTYC_KCBT, /* key_btab, kB */
TTYC_KCUB1, /* key_left, kl */
TTYC_KCUD1, /* key_down, kd */
TTYC_KCUF1, /* key_right, kr */
TTYC_KCUU1, /* key_up, ku */
TTYC_ECH,
TTYC_ED,
TTYC_EL,
TTYC_EL1,
TTYC_ENACS,
TTYC_FSL,
TTYC_HOME,
TTYC_HPA,
TTYC_ICH,
TTYC_ICH1,
TTYC_IL,
TTYC_IL1,
TTYC_INDN,
TTYC_INVIS,
TTYC_KCBT,
TTYC_KCUB1,
TTYC_KCUD1,
TTYC_KCUF1,
TTYC_KCUU1,
TTYC_KDC2,
TTYC_KDC3,
TTYC_KDC4,
TTYC_KDC5,
TTYC_KDC6,
TTYC_KDC7,
TTYC_KDCH1, /* key_dc, kD */
TTYC_KDCH1,
TTYC_KDN2,
TTYC_KDN3,
TTYC_KDN4,
TTYC_KDN5,
TTYC_KDN6,
TTYC_KDN7,
TTYC_KEND, /* key_end, ke */
TTYC_KEND,
TTYC_KEND2,
TTYC_KEND3,
TTYC_KEND4,
@@ -343,35 +357,37 @@ enum tty_code_code {
TTYC_KHOM5,
TTYC_KHOM6,
TTYC_KHOM7,
TTYC_KHOME, /* key_home, kh */
TTYC_KHOME,
TTYC_KIC2,
TTYC_KIC3,
TTYC_KIC4,
TTYC_KIC5,
TTYC_KIC6,
TTYC_KIC7,
TTYC_KICH1, /* key_ic, kI */
TTYC_KICH1,
TTYC_KIND,
TTYC_KLFT2,
TTYC_KLFT3,
TTYC_KLFT4,
TTYC_KLFT5,
TTYC_KLFT6,
TTYC_KLFT7,
TTYC_KMOUS, /* key_mouse, Km */
TTYC_KNP, /* key_npage, kN */
TTYC_KMOUS,
TTYC_KNP,
TTYC_KNXT2,
TTYC_KNXT3,
TTYC_KNXT4,
TTYC_KNXT5,
TTYC_KNXT6,
TTYC_KNXT7,
TTYC_KPP, /* key_ppage, kP */
TTYC_KPP,
TTYC_KPRV2,
TTYC_KPRV3,
TTYC_KPRV4,
TTYC_KPRV5,
TTYC_KPRV6,
TTYC_KPRV7,
TTYC_KRI,
TTYC_KRIT2,
TTYC_KRIT3,
TTYC_KRIT4,
@@ -384,30 +400,33 @@ enum tty_code_code {
TTYC_KUP5,
TTYC_KUP6,
TTYC_KUP7,
TTYC_MS, /* modify xterm(1) selection */
TTYC_OP, /* orig_pair, op */
TTYC_REV, /* enter_reverse_mode, mr */
TTYC_RI, /* scroll_reverse, sr */
TTYC_RMACS, /* exit_alt_charset_mode */
TTYC_RMCUP, /* exit_ca_mode, te */
TTYC_RMKX, /* keypad_local, ke */
TTYC_SE, /* reset cursor style, Se */
TTYC_SETAB, /* set_a_background, AB */
TTYC_SETAF, /* set_a_foreground, AF */
TTYC_SGR0, /* exit_attribute_mode, me */
TTYC_SITM, /* enter_italics_mode, it */
TTYC_SMACS, /* enter_alt_charset_mode, as */
TTYC_SMCUP, /* enter_ca_mode, ti */
TTYC_SMKX, /* keypad_xmit, ks */
TTYC_SMSO, /* enter_standout_mode, so */
TTYC_SMUL, /* enter_underline_mode, us */
TTYC_MS,
TTYC_OP,
TTYC_REV,
TTYC_RI,
TTYC_RMACS,
TTYC_RMCUP,
TTYC_RMKX,
TTYC_SE,
TTYC_SETAB,
TTYC_SETAF,
TTYC_SETRGBB,
TTYC_SETRGBF,
TTYC_SGR0,
TTYC_SITM,
TTYC_SMACS,
TTYC_SMCUP,
TTYC_SMKX,
TTYC_SMSO,
TTYC_SMUL,
TTYC_SMXX,
TTYC_SS, /* set cursor style, Ss */
TTYC_TC, /* 24-bit "true" colour, Tc */
TTYC_TSL, /* to_status_line, tsl */
TTYC_VPA, /* row_address, cv */
TTYC_XENL, /* eat_newline_glitch, xn */
TTYC_XT, /* xterm(1)-compatible title, XT */
TTYC_SS,
TTYC_TC,
TTYC_TSL,
TTYC_U8,
TTYC_VPA,
TTYC_XENL,
TTYC_XT,
};
/* Message codes. */
@@ -617,7 +636,7 @@ struct job {
job_free_cb freecb;
void *data;
LIST_ENTRY(job) entry;
LIST_ENTRY(job) entry;
};
LIST_HEAD(joblist, job);
@@ -676,6 +695,7 @@ struct screen_write_ctx {
struct screen_write_collect_item *item;
struct screen_write_collect_line *list;
u_int scrolled;
u_int bg;
u_int cells;
u_int written;
@@ -695,7 +715,8 @@ struct screen_write_ctx {
struct window_mode {
const char *name;
struct screen *(*init)(struct window_pane *);
struct screen *(*init)(struct window_pane *, struct cmd_find_state *,
struct args *);
void (*free)(struct window_pane *);
void (*resize)(struct window_pane *, u_int, u_int);
void (*key)(struct window_pane *, struct client *,
@@ -743,6 +764,9 @@ struct window_pane {
u_int sx;
u_int sy;
u_int osx;
u_int osy;
u_int xoff;
u_int yoff;
@@ -751,9 +775,11 @@ struct window_pane {
#define PANE_DROP 0x2
#define PANE_FOCUSED 0x4
#define PANE_RESIZE 0x8
#define PANE_FOCUSPUSH 0x10
#define PANE_INPUTOFF 0x20
#define PANE_CHANGED 0x40
#define PANE_RESIZEFORCE 0x10
#define PANE_FOCUSPUSH 0x20
#define PANE_INPUTOFF 0x40
#define PANE_CHANGED 0x80
#define PANE_EXITED 0x100
int argc;
char **argv;
@@ -796,6 +822,7 @@ struct window_pane {
struct event modetimer;
time_t modelast;
u_int modeprefix;
char *searchstr;
TAILQ_ENTRY(window_pane) entry;
RB_ENTRY(window_pane) tree_entry;
@@ -1285,6 +1312,8 @@ struct cmd_entry {
};
/* Client connection. */
typedef int (*prompt_input_cb)(struct client *, void *, const char *, int);
typedef void (*prompt_free_cb)(void *);
struct client {
const char *name;
struct tmuxpeer *peer;
@@ -1327,6 +1356,8 @@ struct client {
struct event status_timer;
struct screen status;
struct screen *old_status;
#define CLIENT_TERMINAL 0x1
#define CLIENT_LOGIN 0x2
#define CLIENT_EXIT 0x4
@@ -1349,11 +1380,13 @@ struct client {
#define CLIENT_STATUSFORCE 0x80000
#define CLIENT_DOUBLECLICK 0x100000
#define CLIENT_TRIPLECLICK 0x200000
#define CLIENT_SIZECHANGED 0x400000
int flags;
struct key_table *keytable;
struct event identify_timer;
void (*identify_callback)(struct client *, struct window_pane *);
void (*identify_callback)(struct client *,
struct window_pane *);
void *identify_callback_data;
char *message_string;
@@ -1364,8 +1397,8 @@ struct client {
char *prompt_string;
struct utf8_data *prompt_buffer;
size_t prompt_index;
int (*prompt_callbackfn)(void *, const char *, int);
void (*prompt_freefn)(void *);
prompt_input_cb prompt_inputcb;
prompt_free_cb prompt_freecb;
void *prompt_data;
u_int prompt_hindex;
enum { PROMPT_ENTRY, PROMPT_COMMAND } prompt_mode;
@@ -1373,6 +1406,7 @@ struct client {
#define PROMPT_SINGLE 0x1
#define PROMPT_NUMERIC 0x2
#define PROMPT_INCREMENTAL 0x4
#define PROMPT_NOFORMAT 0x8
int prompt_flags;
struct session *session;
@@ -1462,7 +1496,9 @@ extern struct options *global_w_options;
extern struct environ *global_environ;
extern struct timeval start_time;
extern const char *socket_path;
extern const char *shell_command;
extern int ptm_fd;
extern const char *shell_command;
int areshell(const char *);
void setblocking(int, int);
const char *find_home(void);
@@ -1470,15 +1506,16 @@ const char *find_home(void);
/* proc.c */
struct imsg;
int proc_send(struct tmuxpeer *, enum msgtype, int, const void *, size_t);
int proc_send_s(struct tmuxpeer *, enum msgtype, const char *);
struct tmuxproc *proc_start(const char *, struct event_base *, int,
void (*)(int));
struct tmuxproc *proc_start(const char *);
void proc_loop(struct tmuxproc *, int (*)(void));
void proc_exit(struct tmuxproc *);
void proc_set_signals(struct tmuxproc *, void(*)(int));
void proc_clear_signals(struct tmuxproc *, int);
struct tmuxpeer *proc_add_peer(struct tmuxproc *, int,
void (*)(struct imsg *, void *), void *);
void proc_remove_peer(struct tmuxpeer *);
void proc_kill_peer(struct tmuxpeer *);
void proc_toggle_log(struct tmuxproc *);
/* cfg.c */
extern int cfg_finished;
@@ -1512,6 +1549,7 @@ char *paste_make_sample(struct paste_buffer *);
#define FORMAT_PANE 0x80000000U
#define FORMAT_WINDOW 0x40000000U
struct format_tree;
int format_true(const char *);
struct format_tree *format_create(struct client *, struct cmdq_item *, int,
int);
void format_free(struct format_tree *);
@@ -1624,7 +1662,7 @@ void environ_put(struct environ *, const char *);
void environ_unset(struct environ *, const char *);
void environ_update(struct options *, struct environ *, struct environ *);
void environ_push(struct environ *);
void environ_log(struct environ *, const char *);
void printflike(2, 3) environ_log(struct environ *, const char *, ...);
struct environ *environ_for_session(struct session *, int);
/* tty.c */
@@ -1639,6 +1677,7 @@ void tty_cursor(struct tty *, u_int, u_int);
void tty_putcode(struct tty *, enum tty_code_code);
void tty_putcode1(struct tty *, enum tty_code_code, int);
void tty_putcode2(struct tty *, enum tty_code_code, int, int);
void tty_putcode3(struct tty *, enum tty_code_code, int, int, int);
void tty_putcode_ptr1(struct tty *, enum tty_code_code, const void *);
void tty_putcode_ptr2(struct tty *, enum tty_code_code, const void *,
const void *);
@@ -1646,8 +1685,8 @@ void tty_puts(struct tty *, const char *);
void tty_putc(struct tty *, u_char);
void tty_putn(struct tty *, const void *, size_t, u_int);
int tty_init(struct tty *, struct client *, int, char *);
int tty_resize(struct tty *);
int tty_set_size(struct tty *, u_int, u_int);
void tty_resize(struct tty *);
void tty_set_size(struct tty *, u_int, u_int);
void tty_start_tty(struct tty *);
void tty_stop_tty(struct tty *);
void tty_set_title(struct tty *, const char *);
@@ -1693,6 +1732,8 @@ const char *tty_term_string(struct tty_term *, enum tty_code_code);
const char *tty_term_string1(struct tty_term *, enum tty_code_code, int);
const char *tty_term_string2(struct tty_term *, enum tty_code_code, int,
int);
const char *tty_term_string3(struct tty_term *, enum tty_code_code, int,
int, int);
const char *tty_term_ptr1(struct tty_term *, enum tty_code_code,
const void *);
const char *tty_term_ptr2(struct tty_term *, enum tty_code_code,
@@ -1702,6 +1743,7 @@ int tty_term_flag(struct tty_term *, enum tty_code_code);
const char *tty_term_describe(struct tty_term *, enum tty_code_code);
/* tty-acs.c */
int tty_acs_needed(struct tty *);
const char *tty_acs_get(struct tty *, u_char);
/* tty-keys.c */
@@ -1710,6 +1752,7 @@ void tty_keys_free(struct tty *);
key_code tty_keys_next(struct tty *);
/* arguments.c */
void args_set(struct args *, u_char, const char *);
struct args *args_parse(const char *, int, char **);
void args_free(struct args *);
char *args_print(struct args *);
@@ -1729,20 +1772,22 @@ void cmd_find_copy_state(struct cmd_find_state *,
struct cmd_find_state *);
void cmd_find_log_state(const char *, struct cmd_find_state *);
void cmd_find_from_session(struct cmd_find_state *,
struct session *);
struct session *, int);
void cmd_find_from_winlink(struct cmd_find_state *,
struct winlink *);
struct winlink *, int);
int cmd_find_from_session_window(struct cmd_find_state *,
struct session *, struct window *);
int cmd_find_from_window(struct cmd_find_state *, struct window *);
struct session *, struct window *, int);
int cmd_find_from_window(struct cmd_find_state *, struct window *,
int);
void cmd_find_from_winlink_pane(struct cmd_find_state *,
struct winlink *, struct window_pane *);
struct winlink *, struct window_pane *, int);
int cmd_find_from_pane(struct cmd_find_state *,
struct window_pane *);
int cmd_find_from_client(struct cmd_find_state *, struct client *);
struct window_pane *, int);
int cmd_find_from_client(struct cmd_find_state *, struct client *,
int);
int cmd_find_from_mouse(struct cmd_find_state *,
struct mouse_event *);
int cmd_find_from_nothing(struct cmd_find_state *);
struct mouse_event *, int);
int cmd_find_from_nothing(struct cmd_find_state *, int);
/* cmd.c */
int cmd_pack_argv(int, char **, char *, size_t);
@@ -1791,7 +1836,7 @@ struct cmd_list *cmd_string_parse(const char *, const char *, u_int, char **);
void cmd_wait_for_flush(void);
/* client.c */
int client_main(struct event_base *, int, char **, int, const char *);
int client_main(struct event_base *, int, char **, int);
/* key-bindings.c */
RB_PROTOTYPE(key_bindings, key_binding, entry, key_bindings_cmp);
@@ -1805,8 +1850,8 @@ void key_bindings_add(const char *, key_code, int, struct cmd_list *);
void key_bindings_remove(const char *, key_code);
void key_bindings_remove_table(const char *);
void key_bindings_init(void);
void key_bindings_dispatch(struct key_binding *, struct client *,
struct mouse_event *, struct cmd_find_state *);
void key_bindings_dispatch(struct key_binding *, struct cmdq_item *,
struct client *, struct mouse_event *, struct cmd_find_state *);
/* key-string.c */
key_code key_string_lookup_string(const char *);
@@ -1827,12 +1872,13 @@ void server_clear_marked(void);
int server_is_marked(struct session *, struct winlink *,
struct window_pane *);
int server_check_marked(void);
int server_start(struct event_base *, int, char *);
int server_start(struct tmuxproc *, struct event_base *, int, char *);
void server_update_socket(void);
void server_add_accept(int);
/* server-client.c */
void server_client_set_identify(struct client *);
u_int server_client_how_many(void);
void server_client_set_identify(struct client *, u_int);
void server_client_clear_identify(struct client *, struct window_pane *);
void server_client_set_key_table(struct client *, const char *);
const char *server_client_get_key_table(struct client *);
@@ -1888,7 +1934,7 @@ void printflike(2, 3) status_message_set(struct client *, const char *, ...);
void status_message_clear(struct client *);
int status_message_redraw(struct client *);
void status_prompt_set(struct client *, const char *, const char *,
int (*)(void *, const char *, int), void (*)(void *), void *, int);
prompt_input_cb, prompt_free_cb, void *, int);
void status_prompt_clear(struct client *);
int status_prompt_redraw(struct client *);
int status_prompt_key(struct client *, key_code);
@@ -1931,9 +1977,9 @@ int grid_cells_equal(const struct grid_cell *, const struct grid_cell *);
struct grid *grid_create(u_int, u_int, u_int);
void grid_destroy(struct grid *);
int grid_compare(struct grid *, struct grid *);
void grid_collect_history(struct grid *, u_int);
void grid_collect_history(struct grid *);
void grid_scroll_history(struct grid *, u_int);
void grid_scroll_history_region(struct grid *, u_int, u_int);
void grid_scroll_history_region(struct grid *, u_int, u_int, u_int);
void grid_clear_history(struct grid *);
const struct grid_line *grid_peek_line(struct grid *, u_int);
void grid_get_cell(struct grid *, u_int, u_int, struct grid_cell *);
@@ -1958,8 +2004,8 @@ void grid_view_set_cells(struct grid *, u_int, u_int,
const struct grid_cell *, const char *, size_t);
void grid_view_clear_history(struct grid *, u_int);
void grid_view_clear(struct grid *, u_int, u_int, u_int, u_int, u_int);
void grid_view_scroll_region_up(struct grid *, u_int, u_int);
void grid_view_scroll_region_down(struct grid *, u_int, u_int);
void grid_view_scroll_region_up(struct grid *, u_int, u_int, u_int);
void grid_view_scroll_region_down(struct grid *, u_int, u_int, u_int);
void grid_view_insert_lines(struct grid *, u_int, u_int, u_int);
void grid_view_insert_lines_region(struct grid *, u_int, u_int, u_int,
u_int);
@@ -1989,6 +2035,11 @@ void screen_write_putc(struct screen_write_ctx *, const struct grid_cell *,
u_char);
void screen_write_copy(struct screen_write_ctx *, struct screen *, u_int,
u_int, u_int, u_int, bitstr_t *, const struct grid_cell *);
void screen_write_hline(struct screen_write_ctx *, u_int, int, int);
void screen_write_vline(struct screen_write_ctx *, u_int, int, int);
void screen_write_box(struct screen_write_ctx *, u_int, u_int);
void screen_write_preview(struct screen_write_ctx *, struct screen *, u_int,
u_int);
void screen_write_backspace(struct screen_write_ctx *);
void screen_write_mode_set(struct screen_write_ctx *, int);
void screen_write_mode_clear(struct screen_write_ctx *, int);
@@ -2006,10 +2057,10 @@ void screen_write_clearline(struct screen_write_ctx *, u_int);
void screen_write_clearendofline(struct screen_write_ctx *, u_int);
void screen_write_clearstartofline(struct screen_write_ctx *, u_int);
void screen_write_cursormove(struct screen_write_ctx *, u_int, u_int);
void screen_write_reverseindex(struct screen_write_ctx *);
void screen_write_reverseindex(struct screen_write_ctx *, u_int);
void screen_write_scrollregion(struct screen_write_ctx *, u_int, u_int);
void screen_write_linefeed(struct screen_write_ctx *, int);
void screen_write_scrollup(struct screen_write_ctx *, u_int);
void screen_write_linefeed(struct screen_write_ctx *, int, u_int);
void screen_write_scrollup(struct screen_write_ctx *, u_int, u_int);
void screen_write_carriagereturn(struct screen_write_ctx *);
void screen_write_clearendofscreen(struct screen_write_ctx *, u_int);
void screen_write_clearstartofscreen(struct screen_write_ctx *, u_int);
@@ -2098,6 +2149,7 @@ u_int window_count_panes(struct window *);
void window_destroy_panes(struct window *);
struct window_pane *window_pane_find_by_id_str(const char *);
struct window_pane *window_pane_find_by_id(u_int);
int window_pane_destroy_ready(struct window_pane *);
int window_pane_spawn(struct window_pane *, int, char **,
const char *, const char *, const char *, struct environ *,
struct termios *, char **);
@@ -2111,14 +2163,13 @@ void window_pane_unset_palette(struct window_pane *, u_int);
void window_pane_reset_palette(struct window_pane *);
int window_pane_get_palette(const struct window_pane *, int);
int window_pane_set_mode(struct window_pane *,
const struct window_mode *);
const struct window_mode *, struct cmd_find_state *,
struct args *);
void window_pane_reset_mode(struct window_pane *);
void window_pane_key(struct window_pane *, struct client *,
struct session *, key_code, struct mouse_event *);
int window_pane_outside(struct window_pane *);
int window_pane_visible(struct window_pane *);
char *window_pane_search(struct window_pane *, const char *,
u_int *);
u_int window_pane_search(struct window_pane *, const char *);
const char *window_printable_flags(struct winlink *);
struct window_pane *window_pane_find_up(struct window_pane *);
struct window_pane *window_pane_find_down(struct window_pane *);
@@ -2167,10 +2218,44 @@ u_int layout_set_select(struct window *, u_int);
u_int layout_set_next(struct window *);
u_int layout_set_previous(struct window *);
/* mode-tree.c */
u_int mode_tree_count_tagged(struct mode_tree_data *);
void *mode_tree_get_current(struct mode_tree_data *);
void mode_tree_each_tagged(struct mode_tree_data *, void (*)(void *, void *,
key_code), key_code, int);
void mode_tree_up(struct mode_tree_data *, int);
void mode_tree_down(struct mode_tree_data *, int);
struct mode_tree_data *mode_tree_start(struct window_pane *, struct args *,
void (*)(void *, u_int, uint64_t *, const char *),
struct screen *(*)(void *, void *, u_int, u_int),
int (*)(void *, void *, const char *), void *, const char **,
u_int, struct screen **);
void mode_tree_build(struct mode_tree_data *);
void mode_tree_free(struct mode_tree_data *);
void mode_tree_resize(struct mode_tree_data *, u_int, u_int);
struct mode_tree_item *mode_tree_add(struct mode_tree_data *,
struct mode_tree_item *, void *, uint64_t, const char *,
const char *, int);
void mode_tree_remove(struct mode_tree_data *, struct mode_tree_item *);
void mode_tree_draw(struct mode_tree_data *);
int mode_tree_key(struct mode_tree_data *, struct client *, key_code *,
struct mouse_event *);
void mode_tree_run_command(struct client *, struct cmd_find_state *,
const char *, const char *);
/* window-buffer.c */
extern const struct window_mode window_buffer_mode;
/* window-tree.c */
extern const struct window_mode window_tree_mode;
/* window-clock.c */
extern const struct window_mode window_clock_mode;
extern const char window_clock_table[14][5][5];
/* window-client.c */
extern const struct window_mode window_client_mode;
/* window-copy.c */
extern const struct window_mode window_copy_mode;
void window_copy_init_from_pane(struct window_pane *, int);
@@ -2179,36 +2264,14 @@ void printflike(2, 3) window_copy_add(struct window_pane *, const char *, ...);
void window_copy_vadd(struct window_pane *, const char *, va_list);
void window_copy_pageup(struct window_pane *, int);
void window_copy_start_drag(struct client *, struct mouse_event *);
int window_copy_scroll_position(struct window_pane *);
const char *window_copy_search_string(struct window_pane *);
/* window-choose.c */
extern const struct window_mode window_choose_mode;
void window_choose_add(struct window_pane *,
struct window_choose_data *);
void window_choose_ready(struct window_pane *,
u_int, void (*)(struct window_choose_data *));
struct window_choose_data *window_choose_data_create (int,
struct client *, struct session *);
void window_choose_data_run(struct window_choose_data *);
struct window_choose_data *window_choose_add_window(struct window_pane *,
struct client *, struct session *, struct winlink *,
const char *, const char *, u_int);
struct window_choose_data *window_choose_add_session(struct window_pane *,
struct client *, struct session *, const char *,
const char *, u_int);
void window_choose_expand_all(struct window_pane *);
void window_choose_set_current(struct window_pane *, u_int);
void window_copy_add_formats(struct window_pane *,
struct format_tree *);
/* names.c */
void check_window_name(struct window *);
char *default_window_name(struct window *);
char *parse_window_name(const char *);
/* signal.c */
void set_signals(void(*)(int, short, void *), void *);
void clear_signals(int);
/* control.c */
void control_callback(struct client *, int, void *);
void printflike(2, 3) control_write(struct client *, const char *, ...);
@@ -2243,7 +2306,7 @@ struct session *session_find_by_id(u_int);
struct session *session_create(const char *, const char *, int, char **,
const char *, const char *, struct environ *,
struct termios *, int, u_int, u_int, char **);
void session_destroy(struct session *);
void session_destroy(struct session *, const char *);
void session_add_ref(struct session *, const char *);
void session_remove_ref(struct session *, const char *);
int session_check_name(const char *);
@@ -2277,6 +2340,7 @@ enum utf8_state utf8_open(struct utf8_data *, u_char);
enum utf8_state utf8_append(struct utf8_data *, u_char);
enum utf8_state utf8_combine(const struct utf8_data *, wchar_t *);
enum utf8_state utf8_split(wchar_t, struct utf8_data *);
int utf8_isvalid(const char *);
int utf8_strvis(char *, const char *, size_t, int);
int utf8_stravis(char **, const char *, int);
char *utf8_sanitize(const char *);
@@ -2298,6 +2362,7 @@ struct event_base *osdep_event_init(void);
void log_add_level(void);
int log_get_level(void);
void log_open(const char *);
void log_toggle(const char *);
void log_close(void);
void printflike(1, 2) log_debug(const char *, ...);
__dead void printflike(1, 2) fatal(const char *, ...);

View File

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

View File

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

View File

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

View File

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

423
tty.c
View File

@@ -71,8 +71,6 @@ static void tty_default_colours(struct grid_cell *,
static void tty_default_attributes(struct tty *, const struct window_pane *,
u_int);
#define tty_use_acs(tty) \
(tty_term_has((tty)->term, TTYC_ACSC) && !((tty)->flags & TTY_UTF8))
#define tty_use_margin(tty) \
((tty)->term_type == TTY_VT420)
@@ -122,7 +120,7 @@ tty_init(struct tty *tty, struct client *c, int fd, char *term)
return (0);
}
int
void
tty_resize(struct tty *tty)
{
struct client *c = tty->client;
@@ -141,21 +139,15 @@ tty_resize(struct tty *tty)
sy = 24;
}
log_debug("%s: %s now %ux%u", __func__, c->name, sx, sy);
if (!tty_set_size(tty, sx, sy))
return (0);
tty_set_size(tty, sx, sy);
tty_invalidate(tty);
return (1);
}
int
void
tty_set_size(struct tty *tty, u_int sx, u_int sy)
{
if (sx == tty->sx && sy == tty->sy)
return (0);
tty->sx = sx;
tty->sy = sy;
return (1);
}
static void
@@ -167,8 +159,11 @@ tty_read_callback(__unused int fd, __unused short events, void *data)
int nread;
nread = evbuffer_read(tty->in, tty->fd, -1);
if (nread == -1)
if (nread == 0 || nread == -1) {
event_del(&tty->event_in);
server_client_lost(tty->client);
return;
}
log_debug("%s: read %d bytes (already %zu)", c->name, nread, size);
while (tty_keys_next(tty))
@@ -278,7 +273,8 @@ tty_open(struct tty *tty, char **cause)
void
tty_start_tty(struct tty *tty)
{
struct termios tio;
struct client *c = tty->client;
struct termios tio;
if (tty->fd != -1 && tcgetattr(tty->fd, &tty->tio) == 0) {
setblocking(tty->fd, 0);
@@ -299,10 +295,14 @@ tty_start_tty(struct tty *tty)
tty_putcode(tty, TTYC_SMCUP);
tty_putcode(tty, TTYC_SMKX);
if (tty_use_acs(tty))
tty_putcode(tty, TTYC_ENACS);
tty_putcode(tty, TTYC_CLEAR);
if (tty_acs_needed(tty)) {
log_debug("%s: using capabilities for ACS", c->name);
tty_putcode(tty, TTYC_ENACS);
} else
log_debug("%s: using UTF-8 for ACS", c->name);
tty_putcode(tty, TTYC_CNORM);
if (tty_term_has(tty->term, TTYC_KMOUS))
tty_puts(tty, "\033[?1000l\033[?1002l\033[?1006l\033[?1005l");
@@ -351,7 +351,7 @@ tty_stop_tty(struct tty *tty)
return;
tty_raw(tty, tty_term_string2(tty->term, TTYC_CSR, 0, ws.ws_row - 1));
if (tty_use_acs(tty))
if (tty_acs_needed(tty))
tty_raw(tty, tty_term_string(tty->term, TTYC_RMACS));
tty_raw(tty, tty_term_string(tty->term, TTYC_SGR0));
tty_raw(tty, tty_term_string(tty->term, TTYC_RMKX));
@@ -469,6 +469,14 @@ tty_putcode2(struct tty *tty, enum tty_code_code code, int a, int b)
tty_puts(tty, tty_term_string2(tty->term, code, a, b));
}
void
tty_putcode3(struct tty *tty, enum tty_code_code code, int a, int b, int c)
{
if (a < 0 || b < 0 || c < 0)
return;
tty_puts(tty, tty_term_string3(tty->term, code, a, b, c));
}
void
tty_putcode_ptr1(struct tty *tty, enum tty_code_code code, const void *a)
{
@@ -754,6 +762,115 @@ tty_redraw_region(struct tty *tty, const struct tty_ctx *ctx)
}
}
static void
tty_clear_line(struct tty *tty, const struct window_pane *wp, u_int py,
u_int px, u_int nx, u_int bg)
{
log_debug("%s: %u at %u,%u", __func__, nx, px, py);
/* Nothing to clear. */
if (nx == 0)
return;
/* If genuine BCE is available, can try escape sequences. */
if (!tty_fake_bce(tty, wp, bg)) {
/* Off the end of the line, use EL if available. */
if (px + nx >= tty->sx && tty_term_has(tty->term, TTYC_EL)) {
tty_cursor(tty, px, py);
tty_putcode(tty, TTYC_EL);
return;
}
/* At the start of the line. Use EL1. */
if (px == 0 && tty_term_has(tty->term, TTYC_EL1)) {
tty_cursor(tty, px + nx - 1, py);
tty_putcode(tty, TTYC_EL1);
return;
}
/* Section of line. Use ECH if possible. */
if (tty_term_has(tty->term, TTYC_ECH)) {
tty_cursor(tty, px, py);
tty_putcode1(tty, TTYC_ECH, nx);
return;
}
}
/* Couldn't use an escape sequence, use spaces. */
tty_cursor(tty, px, py);
tty_repeat_space(tty, nx);
}
static void
tty_clear_area(struct tty *tty, const struct window_pane *wp, u_int py,
u_int ny, u_int px, u_int nx, u_int bg)
{
u_int yy;
char tmp[64];
log_debug("%s: %u,%u at %u,%u", __func__, nx, ny, px, py);
/* Nothing to clear. */
if (nx == 0 || ny == 0)
return;
/* If genuine BCE is available, can try escape sequences. */
if (!tty_fake_bce(tty, wp, bg)) {
/* Use ED if clearing off the bottom of the terminal. */
if (px == 0 &&
px + nx >= tty->sx &&
py + ny >= tty->sy &&
tty_term_has(tty->term, TTYC_ED)) {
tty_cursor(tty, 0, py);
tty_putcode(tty, TTYC_ED);
return;
}
/*
* On VT420 compatible terminals we can use DECFRA if the
* background colour isn't default (because it doesn't work
* after SGR 0).
*/
if (tty->term_type == TTY_VT420 && bg != 8) {
xsnprintf(tmp, sizeof tmp, "\033[32;%u;%u;%u;%u$x",
py + 1, px + 1, py + ny, px + nx);
tty_puts(tty, tmp);
return;
}
/* Full lines can be scrolled away to clear them. */
if (px == 0 &&
px + nx >= tty->sx &&
ny > 2 &&
tty_term_has(tty->term, TTYC_CSR) &&
tty_term_has(tty->term, TTYC_INDN)) {
tty_region(tty, py, py + ny - 1);
tty_margin_off(tty);
tty_putcode1(tty, TTYC_INDN, ny);
return;
}
/*
* If margins are supported, can just scroll the area off to
* clear it.
*/
if (nx > 2 &&
ny > 2 &&
tty_term_has(tty->term, TTYC_CSR) &&
tty_use_margin(tty) &&
tty_term_has(tty->term, TTYC_INDN)) {
tty_region(tty, py, py + ny - 1);
tty_margin(tty, px, px + nx - 1);
tty_putcode1(tty, TTYC_INDN, ny);
return;
}
}
/* Couldn't use an escape sequence, loop over the lines. */
for (yy = py; yy < py + ny; yy++)
tty_clear_line(tty, wp, yy, px, nx, bg);
}
void
tty_draw_pane(struct tty *tty, const struct window_pane *wp, u_int py, u_int ox,
u_int oy)
@@ -766,10 +883,10 @@ tty_draw_line(struct tty *tty, const struct window_pane *wp,
struct screen *s, u_int py, u_int ox, u_int oy)
{
struct grid_cell gc, last;
u_int i, j, sx, width;
u_int i, j, ux, sx, nx, width;
int flags, cleared = 0;
char buf[512];
size_t len;
size_t len, old_len;
flags = (tty->flags & TTY_NOCURSOR);
tty->flags |= TTY_NOCURSOR;
@@ -787,6 +904,7 @@ tty_draw_line(struct tty *tty, const struct window_pane *wp,
sx = s->grid->linedata[s->grid->hsize + py].cellsize;
if (sx > tty->sx)
sx = tty->sx;
ux = 0;
if (wp == NULL ||
py == 0 ||
@@ -828,6 +946,7 @@ tty_draw_line(struct tty *tty, const struct window_pane *wp,
(sizeof buf) - len < gc.data.size)) {
tty_attributes(tty, &last, wp);
tty_putn(tty, buf, len, width);
ux += width;
len = 0;
width = 0;
@@ -850,6 +969,7 @@ tty_draw_line(struct tty *tty, const struct window_pane *wp,
for (j = 0; j < gc.data.size; j++)
tty_putc(tty, gc.data.data[j]);
}
ux += gc.data.width;
} else {
memcpy(buf + len, gc.data.data, gc.data.size);
len += gc.data.size;
@@ -857,20 +977,26 @@ tty_draw_line(struct tty *tty, const struct window_pane *wp,
}
}
if (len != 0) {
tty_attributes(tty, &last, wp);
tty_putn(tty, buf, len, width);
if (grid_cells_equal(&last, &grid_default_cell)) {
old_len = len;
while (len > 0 && buf[len - 1] == ' ') {
len--;
width--;
}
log_debug("%s: trimmed %zu spaces", __func__,
old_len - len);
}
if (len != 0) {
tty_attributes(tty, &last, wp);
tty_putn(tty, buf, len, width);
ux += width;
}
}
if (!cleared && sx < tty->sx) {
nx = screen_size_x(s) - ux;
if (!cleared && ux < tty->sx && nx != 0) {
tty_default_attributes(tty, wp, 8);
tty_cursor(tty, ox + sx, oy + py);
if (sx != screen_size_x(s) &&
ox + screen_size_x(s) >= tty->sx &&
tty_term_has(tty->term, TTYC_EL) &&
!tty_fake_bce(tty, wp, 8))
tty_putcode(tty, TTYC_EL);
else
tty_repeat_space(tty, screen_size_x(s) - sx);
tty_clear_line(tty, wp, oy + py, ox + ux, nx, 8);
}
tty->flags = (tty->flags & ~TTY_NOCURSOR) | flags;
@@ -1018,73 +1144,54 @@ void
tty_cmd_clearline(struct tty *tty, const struct tty_ctx *ctx)
{
struct window_pane *wp = ctx->wp;
struct screen *s = wp->screen;
u_int sx = screen_size_x(s);
u_int nx, py = ctx->yoff + ctx->ocy;
tty_default_attributes(tty, wp, ctx->bg);
tty_cursor_pane(tty, ctx, 0, ctx->ocy);
if (tty_pane_full_width(tty, ctx) &&
!tty_fake_bce(tty, wp, ctx->bg) &&
tty_term_has(tty->term, TTYC_EL))
tty_putcode(tty, TTYC_EL);
else
tty_repeat_space(tty, sx);
nx = screen_size_x(wp->screen);
tty_clear_line(tty, wp, py, ctx->xoff, nx, ctx->bg);
}
void
tty_cmd_clearendofline(struct tty *tty, const struct tty_ctx *ctx)
{
struct window_pane *wp = ctx->wp;
struct screen *s = wp->screen;
u_int sx = screen_size_x(s);
u_int nx, py = ctx->yoff + ctx->ocy;
tty_default_attributes(tty, wp, ctx->bg);
tty_cursor_pane(tty, ctx, ctx->ocx, ctx->ocy);
if (tty_pane_full_width(tty, ctx) &&
tty_term_has(tty->term, TTYC_EL) &&
!tty_fake_bce(tty, wp, ctx->bg))
tty_putcode(tty, TTYC_EL);
else
tty_repeat_space(tty, sx - ctx->ocx);
nx = screen_size_x(wp->screen) - ctx->ocx;
tty_clear_line(tty, wp, py, ctx->xoff + ctx->ocx, nx, ctx->bg);
}
void
tty_cmd_clearstartofline(struct tty *tty, const struct tty_ctx *ctx)
{
struct window_pane *wp = ctx->wp;
u_int py = ctx->yoff + ctx->ocy;
tty_default_attributes(tty, wp, ctx->bg);
if (ctx->xoff == 0 &&
tty_term_has(tty->term, TTYC_EL1) &&
!tty_fake_bce(tty, ctx->wp, ctx->bg)) {
tty_cursor_pane(tty, ctx, ctx->ocx, ctx->ocy);
tty_putcode(tty, TTYC_EL1);
} else {
tty_cursor_pane(tty, ctx, 0, ctx->ocy);
tty_repeat_space(tty, ctx->ocx + 1);
}
tty_clear_line(tty, wp, py, ctx->xoff, ctx->ocx + 1, ctx->bg);
}
void
tty_cmd_reverseindex(struct tty *tty, const struct tty_ctx *ctx)
{
struct window_pane *wp = ctx->wp;
if (ctx->ocy != ctx->orupper)
return;
if (!tty_pane_full_width(tty, ctx) ||
tty_fake_bce(tty, ctx->wp, 8) ||
tty_fake_bce(tty, wp, 8) ||
!tty_term_has(tty->term, TTYC_CSR) ||
!tty_term_has(tty->term, TTYC_RI)) {
tty_redraw_region(tty, ctx);
return;
}
tty_attributes(tty, &grid_default_cell, ctx->wp);
tty_default_attributes(tty, wp, ctx->bg);
tty_region_pane(tty, ctx, ctx->orupper, ctx->orlower);
tty_margin_off(tty);
@@ -1108,7 +1215,7 @@ tty_cmd_linefeed(struct tty *tty, const struct tty_ctx *ctx)
return;
}
tty_attributes(tty, &grid_default_cell, wp);
tty_default_attributes(tty, wp, ctx->bg);
tty_region_pane(tty, ctx, ctx->orupper, ctx->orlower);
tty_margin_pane(tty, ctx);
@@ -1130,7 +1237,7 @@ void
tty_cmd_scrollup(struct tty *tty, const struct tty_ctx *ctx)
{
struct window_pane *wp = ctx->wp;
u_int i, lines;
u_int i;
if ((!tty_pane_full_width(tty, ctx) && !tty_use_margin(tty)) ||
tty_fake_bce(tty, wp, 8) ||
@@ -1139,142 +1246,86 @@ tty_cmd_scrollup(struct tty *tty, const struct tty_ctx *ctx)
return;
}
tty_attributes(tty, &grid_default_cell, wp);
tty_default_attributes(tty, wp, ctx->bg);
tty_region_pane(tty, ctx, ctx->orupper, ctx->orlower);
tty_margin_pane(tty, ctx);
/*
* Konsole has a bug where it will ignore SU if the parameter is more
* than the height of the scroll region. Clamping the parameter doesn't
* hurt in any case.
*/
lines = tty->rlower - tty->rupper;
if (lines > ctx->num)
lines = ctx->num;
if (lines == 1 || !tty_term_has(tty->term, TTYC_INDN)) {
if (ctx->num == 1 || !tty_term_has(tty->term, TTYC_INDN)) {
tty_cursor(tty, tty->rright, tty->rlower);
for (i = 0; i < lines; i++)
for (i = 0; i < ctx->num; i++)
tty_putc(tty, '\n');
} else
tty_putcode1(tty, TTYC_INDN, lines);
tty_putcode1(tty, TTYC_INDN, ctx->num);
}
void
tty_cmd_clearendofscreen(struct tty *tty, const struct tty_ctx *ctx)
{
struct window_pane *wp = ctx->wp;
struct screen *s = wp->screen;
u_int i, j;
u_int sx = screen_size_x(s), sy = screen_size_y(s);
u_int px, py, nx, ny;
tty_default_attributes(tty, wp, ctx->bg);
tty_region_pane(tty, ctx, 0, sy - 1);
tty_region_pane(tty, ctx, 0, screen_size_y(wp->screen) - 1);
tty_margin_off(tty);
if (tty_pane_full_width(tty, ctx) &&
ctx->yoff + wp->sy >= tty->sy - 1 &&
status_at_line(tty->client) <= 0 &&
tty_term_has(tty->term, TTYC_ED)) {
tty_cursor_pane(tty, ctx, ctx->ocx, ctx->ocy);
tty_putcode(tty, TTYC_ED);
} else if (tty_pane_full_width(tty, ctx) &&
tty_term_has(tty->term, TTYC_EL) &&
!tty_fake_bce(tty, wp, ctx->bg)) {
tty_cursor_pane(tty, ctx, ctx->ocx, ctx->ocy);
tty_putcode(tty, TTYC_EL);
if (ctx->ocy != sy - 1) {
tty_cursor_pane(tty, ctx, 0, ctx->ocy + 1);
for (i = ctx->ocy + 1; i < sy; i++) {
tty_putcode(tty, TTYC_EL);
if (i == sy - 1)
continue;
tty_emulate_repeat(tty, TTYC_CUD, TTYC_CUD1, 1);
tty->cy++;
}
}
} else {
tty_cursor_pane(tty, ctx, ctx->ocx, ctx->ocy);
tty_repeat_space(tty, sx - ctx->ocx);
for (j = ctx->ocy + 1; j < sy; j++) {
tty_cursor_pane(tty, ctx, 0, j);
tty_repeat_space(tty, sx);
}
}
px = ctx->xoff;
nx = screen_size_x(wp->screen);
py = ctx->yoff + ctx->ocy + 1;
ny = screen_size_y(wp->screen) - ctx->ocy - 1;
tty_clear_area(tty, wp, py, ny, px, nx, ctx->bg);
px = ctx->xoff + ctx->ocx;
nx = screen_size_x(wp->screen) - ctx->ocx;
py = ctx->yoff + ctx->ocy;
tty_clear_line(tty, wp, py, px, nx, ctx->bg);
}
void
tty_cmd_clearstartofscreen(struct tty *tty, const struct tty_ctx *ctx)
{
struct window_pane *wp = ctx->wp;
struct screen *s = wp->screen;
u_int i, j;
u_int sx = screen_size_x(s), sy = screen_size_y(s);
u_int px, py, nx, ny;
tty_default_attributes(tty, wp, ctx->bg);
tty_region_pane(tty, ctx, 0, sy - 1);
tty_region_pane(tty, ctx, 0, screen_size_y(wp->screen) - 1);
tty_margin_off(tty);
if (tty_pane_full_width(tty, ctx) &&
tty_term_has(tty->term, TTYC_EL) &&
!tty_fake_bce(tty, wp, ctx->bg)) {
tty_cursor_pane(tty, ctx, 0, 0);
for (i = 0; i < ctx->ocy; i++) {
tty_putcode(tty, TTYC_EL);
tty_emulate_repeat(tty, TTYC_CUD, TTYC_CUD1, 1);
tty->cy++;
}
} else {
tty_cursor_pane(tty, ctx, 0, 0);
for (j = 0; j < ctx->ocy; j++) {
tty_cursor_pane(tty, ctx, 0, j);
tty_repeat_space(tty, sx);
}
}
tty_cursor_pane(tty, ctx, 0, ctx->ocy);
tty_repeat_space(tty, ctx->ocx + 1);
px = ctx->xoff;
nx = screen_size_x(wp->screen);
py = ctx->yoff;
ny = ctx->ocy - 1;
tty_clear_area(tty, wp, py, ny, px, nx, ctx->bg);
px = ctx->xoff;
nx = ctx->ocx + 1;
py = ctx->yoff + ctx->ocy;
tty_clear_line(tty, wp, py, px, nx, ctx->bg);
}
void
tty_cmd_clearscreen(struct tty *tty, const struct tty_ctx *ctx)
{
struct window_pane *wp = ctx->wp;
struct screen *s = wp->screen;
u_int i, j;
u_int sx = screen_size_x(s), sy = screen_size_y(s);
u_int px, py, nx, ny;
tty_default_attributes(tty, wp, ctx->bg);
tty_region_pane(tty, ctx, 0, sy - 1);
tty_region_pane(tty, ctx, 0, screen_size_y(wp->screen) - 1);
tty_margin_off(tty);
if (tty_pane_full_width(tty, ctx) &&
ctx->yoff + wp->sy >= tty->sy - 1 &&
status_at_line(tty->client) <= 0 &&
tty_term_has(tty->term, TTYC_ED)) {
tty_cursor_pane(tty, ctx, 0, 0);
tty_putcode(tty, TTYC_ED);
} else if (tty_pane_full_width(tty, ctx) &&
tty_term_has(tty->term, TTYC_EL) &&
!tty_fake_bce(tty, wp, ctx->bg)) {
tty_cursor_pane(tty, ctx, 0, 0);
for (i = 0; i < sy; i++) {
tty_putcode(tty, TTYC_EL);
if (i != sy - 1) {
tty_emulate_repeat(tty, TTYC_CUD, TTYC_CUD1, 1);
tty->cy++;
}
}
} else {
tty_cursor_pane(tty, ctx, 0, 0);
for (j = 0; j < sy; j++) {
tty_cursor_pane(tty, ctx, 0, j);
tty_repeat_space(tty, sx);
}
}
px = ctx->xoff;
nx = screen_size_x(wp->screen);
py = ctx->yoff;
ny = screen_size_y(wp->screen);
tty_clear_area(tty, wp, py, ny, px, nx, ctx->bg);
}
void
@@ -1389,7 +1440,7 @@ tty_reset(struct tty *tty)
struct grid_cell *gc = &tty->cell;
if (!grid_cells_equal(gc, &grid_default_cell)) {
if ((gc->attr & GRID_ATTR_CHARSET) && tty_use_acs(tty))
if ((gc->attr & GRID_ATTR_CHARSET) && tty_acs_needed(tty))
tty_putcode(tty, TTYC_RMACS);
tty_putcode(tty, TTYC_SGR0);
memcpy(gc, &grid_default_cell, sizeof *gc);
@@ -1559,7 +1610,8 @@ tty_cursor(struct tty *tty, u_int cx, u_int cy)
}
/* Zero on the next line. */
if (cx == 0 && cy == thisy + 1 && thisy != tty->rlower) {
if (cx == 0 && cy == thisy + 1 && thisy != tty->rlower &&
(!tty_use_margin(tty) || tty->rleft == 0)) {
tty_putc(tty, '\r');
tty_putc(tty, '\n');
goto out;
@@ -1572,7 +1624,7 @@ tty_cursor(struct tty *tty, u_int cx, u_int cy)
*/
/* To left edge. */
if (cx == 0) {
if (cx == 0 && (!tty_use_margin(tty) || tty->rleft == 0)) {
tty_putc(tty, '\r');
goto out;
}
@@ -1739,7 +1791,7 @@ tty_attributes(struct tty *tty, const struct grid_cell *gc,
tty_putcode(tty, TTYC_INVIS);
if (changed & GRID_ATTR_STRIKETHROUGH)
tty_putcode(tty, TTYC_SMXX);
if ((changed & GRID_ATTR_CHARSET) && tty_use_acs(tty))
if ((changed & GRID_ATTR_CHARSET) && tty_acs_needed(tty))
tty_putcode(tty, TTYC_SMACS);
}
@@ -1808,15 +1860,23 @@ tty_check_fg(struct tty *tty, const struct window_pane *wp,
u_int colours;
int c;
/* Perform substitution if this pane has a palette */
if ((~gc->flags & GRID_FLAG_NOPALETTE) &&
(c = window_pane_get_palette(wp, gc->fg)) != -1)
gc->fg = c;
/*
* Perform substitution if this pane has a palette. If the bright
* attribute is set, use the bright entry in the palette by changing to
* the aixterm colour.
*/
if (~gc->flags & GRID_FLAG_NOPALETTE) {
c = gc->fg;
if (c < 8 && gc->attr & GRID_ATTR_BRIGHT)
c += 90;
if ((c = window_pane_get_palette(wp, c)) != -1)
gc->fg = c;
}
/* Is this a 24-bit colour? */
if (gc->fg & COLOUR_FLAG_RGB) {
/* Not a 24-bit terminal? Translate to 256-colour palette. */
if (!tty_term_flag(tty->term, TTYC_TC)) {
if (!tty_term_has(tty->term, TTYC_SETRGBF)) {
colour_split_rgb(gc->fg, &r, &g, &b);
gc->fg = colour_find_rgb(r, g, b);
} else
@@ -1861,15 +1921,16 @@ tty_check_bg(struct tty *tty, const struct window_pane *wp,
u_int colours;
int c;
/* Perform substitution if this pane has a palette */
if ((~gc->flags & GRID_FLAG_NOPALETTE) &&
(c = window_pane_get_palette(wp, gc->bg)) != -1)
gc->bg = c;
/* Perform substitution if this pane has a palette. */
if (~gc->flags & GRID_FLAG_NOPALETTE) {
if ((c = window_pane_get_palette(wp, gc->bg)) != -1)
gc->bg = c;
}
/* Is this a 24-bit colour? */
if (gc->bg & COLOUR_FLAG_RGB) {
/* Not a 24-bit terminal? Translate to 256-colour palette. */
if (!tty_term_flag(tty->term, TTYC_TC)) {
if (!tty_term_has(tty->term, TTYC_SETRGBB)) {
colour_split_rgb(gc->bg, &r, &g, &b);
gc->bg = colour_find_rgb(r, g, b);
} else
@@ -2000,13 +2061,17 @@ tty_try_colour(struct tty *tty, int colour, const char *type)
}
if (colour & COLOUR_FLAG_RGB) {
if (!tty_term_flag(tty->term, TTYC_TC))
return (-1);
colour_split_rgb(colour & 0xffffff, &r, &g, &b);
xsnprintf(s, sizeof s, "\033[%s;2;%hhu;%hhu;%hhum", type,
r, g, b);
tty_puts(tty, s);
if (*type == '3') {
if (!tty_term_has(tty->term, TTYC_SETRGBF))
return (-1);
colour_split_rgb(colour & 0xffffff, &r, &g, &b);
tty_putcode3(tty, TTYC_SETRGBF, r, g, b);
} else {
if (!tty_term_has(tty->term, TTYC_SETRGBB))
return (-1);
colour_split_rgb(colour & 0xffffff, &r, &g, &b);
tty_putcode3(tty, TTYC_SETRGBB, r, g, b);
}
return (0);
}

27
utf8.c
View File

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

382
window-buffer.c Normal file
View File

@@ -0,0 +1,382 @@
/* $OpenBSD$ */
/*
* Copyright (c) 2017 Nicholas Marriott <nicholas.marriott@gmail.com>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER
* IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
* OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include <sys/types.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include "tmux.h"
static struct screen *window_buffer_init(struct window_pane *,
struct cmd_find_state *, struct args *);
static void window_buffer_free(struct window_pane *);
static void window_buffer_resize(struct window_pane *, u_int,
u_int);
static void window_buffer_key(struct window_pane *,
struct client *, struct session *, key_code,
struct mouse_event *);
#define WINDOW_BUFFER_DEFAULT_COMMAND "paste-buffer -b '%%'"
#define WINDOW_BUFFER_DEFAULT_FORMAT \
"#{buffer_size} bytes (#{t:buffer_created})"
const struct window_mode window_buffer_mode = {
.name = "buffer-mode",
.init = window_buffer_init,
.free = window_buffer_free,
.resize = window_buffer_resize,
.key = window_buffer_key,
};
enum window_buffer_sort_type {
WINDOW_BUFFER_BY_TIME,
WINDOW_BUFFER_BY_NAME,
WINDOW_BUFFER_BY_SIZE,
};
static const char *window_buffer_sort_list[] = {
"time",
"name",
"size"
};
struct window_buffer_itemdata {
const char *name;
u_int order;
size_t size;
};
struct window_buffer_modedata {
struct mode_tree_data *data;
char *command;
char *format;
struct window_buffer_itemdata **item_list;
u_int item_size;
};
static struct window_buffer_itemdata *
window_buffer_add_item(struct window_buffer_modedata *data)
{
struct window_buffer_itemdata *item;
data->item_list = xreallocarray(data->item_list, data->item_size + 1,
sizeof *data->item_list);
item = data->item_list[data->item_size++] = xcalloc(1, sizeof *item);
return (item);
}
static void
window_buffer_free_item(struct window_buffer_itemdata *item)
{
free((void *)item->name);
free(item);
}
static int
window_buffer_cmp_name(const void *a0, const void *b0)
{
const struct window_buffer_itemdata *const *a = a0;
const struct window_buffer_itemdata *const *b = b0;
return (strcmp((*a)->name, (*b)->name));
}
static int
window_buffer_cmp_time(const void *a0, const void *b0)
{
const struct window_buffer_itemdata *const *a = a0;
const struct window_buffer_itemdata *const *b = b0;
if ((*a)->order > (*b)->order)
return (-1);
if ((*a)->order < (*b)->order)
return (1);
return (strcmp((*a)->name, (*b)->name));
}
static int
window_buffer_cmp_size(const void *a0, const void *b0)
{
const struct window_buffer_itemdata *const *a = a0;
const struct window_buffer_itemdata *const *b = b0;
if ((*a)->size > (*b)->size)
return (-1);
if ((*a)->size < (*b)->size)
return (1);
return (strcmp((*a)->name, (*b)->name));
}
static void
window_buffer_build(void *modedata, u_int sort_type, __unused uint64_t *tag,
const char *filter)
{
struct window_buffer_modedata *data = modedata;
struct window_buffer_itemdata *item;
u_int i;
struct paste_buffer *pb;
char *text, *cp;
struct format_tree *ft;
for (i = 0; i < data->item_size; i++)
window_buffer_free_item(data->item_list[i]);
free(data->item_list);
data->item_list = NULL;
data->item_size = 0;
pb = NULL;
while ((pb = paste_walk(pb)) != NULL) {
item = window_buffer_add_item(data);
item->name = xstrdup(paste_buffer_name(pb));
paste_buffer_data(pb, &item->size);
item->order = paste_buffer_order(pb);
}
switch (sort_type) {
case WINDOW_BUFFER_BY_NAME:
qsort(data->item_list, data->item_size, sizeof *data->item_list,
window_buffer_cmp_name);
break;
case WINDOW_BUFFER_BY_TIME:
qsort(data->item_list, data->item_size, sizeof *data->item_list,
window_buffer_cmp_time);
break;
case WINDOW_BUFFER_BY_SIZE:
qsort(data->item_list, data->item_size, sizeof *data->item_list,
window_buffer_cmp_size);
break;
}
for (i = 0; i < data->item_size; i++) {
item = data->item_list[i];
pb = paste_get_name(item->name);
if (pb == NULL)
continue;
ft = format_create(NULL, NULL, FORMAT_NONE, 0);
format_defaults_paste_buffer(ft, pb);
if (filter != NULL) {
cp = format_expand(ft, filter);
if (!format_true(cp)) {
free(cp);
format_free(ft);
continue;
}
free(cp);
}
text = format_expand(ft, data->format);
mode_tree_add(data->data, NULL, item, item->order, item->name,
text, -1);
free(text);
format_free(ft);
}
}
static struct screen *
window_buffer_draw(__unused void *modedata, void *itemdata, u_int sx, u_int sy)
{
struct window_buffer_itemdata *item = itemdata;
struct paste_buffer *pb;
static struct screen s;
struct screen_write_ctx ctx;
char line[1024];
const char *pdata, *end, *cp;
size_t psize, at;
u_int i;
pb = paste_get_name(item->name);
if (pb == NULL)
return (NULL);
screen_init(&s, sx, sy, 0);
screen_write_start(&ctx, NULL, &s);
screen_write_clearscreen(&ctx, 8);
pdata = end = paste_buffer_data(pb, &psize);
for (i = 0; i < sy; i++) {
at = 0;
while (end != pdata + psize && *end != '\n') {
if ((sizeof line) - at > 5) {
cp = vis(line + at, *end, VIS_TAB|VIS_OCTAL, 0);
at = cp - line;
}
end++;
}
if (at > sx)
at = sx;
line[at] = '\0';
if (*line != '\0') {
screen_write_cursormove(&ctx, 0, i);
screen_write_puts(&ctx, &grid_default_cell, "%s", line);
}
if (end == pdata + psize)
break;
end++;
}
screen_write_stop(&ctx);
return (&s);
}
static int
window_buffer_search(__unused void *modedata, void *itemdata, const char *ss)
{
struct window_buffer_itemdata *item = itemdata;
struct paste_buffer *pb;
const char *bufdata;
size_t bufsize;
if ((pb = paste_get_name(item->name)) == NULL)
return (0);
if (strstr(item->name, ss) != NULL)
return (1);
bufdata = paste_buffer_data(pb, &bufsize);
return (memmem(bufdata, bufsize, ss, strlen(ss)) != NULL);
}
static struct screen *
window_buffer_init(struct window_pane *wp, __unused struct cmd_find_state *fs,
struct args *args)
{
struct window_buffer_modedata *data;
struct screen *s;
wp->modedata = data = xcalloc(1, sizeof *data);
if (args == NULL || !args_has(args, 'F'))
data->format = xstrdup(WINDOW_BUFFER_DEFAULT_FORMAT);
else
data->format = xstrdup(args_get(args, 'F'));
if (args == NULL || args->argc == 0)
data->command = xstrdup(WINDOW_BUFFER_DEFAULT_COMMAND);
else
data->command = xstrdup(args->argv[0]);
data->data = mode_tree_start(wp, args, window_buffer_build,
window_buffer_draw, window_buffer_search, data,
window_buffer_sort_list, nitems(window_buffer_sort_list), &s);
mode_tree_build(data->data);
mode_tree_draw(data->data);
return (s);
}
static void
window_buffer_free(struct window_pane *wp)
{
struct window_buffer_modedata *data = wp->modedata;
u_int i;
if (data == NULL)
return;
mode_tree_free(data->data);
for (i = 0; i < data->item_size; i++)
window_buffer_free_item(data->item_list[i]);
free(data->item_list);
free(data->format);
free(data->command);
free(data);
}
static void
window_buffer_resize(struct window_pane *wp, u_int sx, u_int sy)
{
struct window_buffer_modedata *data = wp->modedata;
mode_tree_resize(data->data, sx, sy);
}
static void
window_buffer_do_delete(void* modedata, void *itemdata, __unused key_code key)
{
struct window_buffer_modedata *data = modedata;
struct window_buffer_itemdata *item = itemdata;
struct paste_buffer *pb;
if (item == mode_tree_get_current(data->data))
mode_tree_down(data->data, 0);
if ((pb = paste_get_name(item->name)) != NULL)
paste_free(pb);
}
static void
window_buffer_key(struct window_pane *wp, struct client *c,
__unused struct session *s, key_code key, struct mouse_event *m)
{
struct window_buffer_modedata *data = wp->modedata;
struct window_buffer_itemdata *item;
char *command, *name;
int finished;
/*
* t = toggle tag
* T = tag none
* C-t = tag all
* q = exit
* O = change sort order
*
* d = delete buffer
* D = delete tagged buffers
* Enter = paste buffer
*/
finished = mode_tree_key(data->data, c, &key, m);
switch (key) {
case 'd':
item = mode_tree_get_current(data->data);
window_buffer_do_delete(data, item, key);
mode_tree_build(data->data);
break;
case 'D':
mode_tree_each_tagged(data->data, window_buffer_do_delete, key,
0);
mode_tree_build(data->data);
break;
case '\r':
item = mode_tree_get_current(data->data);
command = xstrdup(data->command);
name = xstrdup(item->name);
window_pane_reset_mode(wp);
mode_tree_run_command(c, NULL, command, name);
free(name);
free(command);
return;
}
if (finished || paste_get_top(NULL) == NULL)
window_pane_reset_mode(wp);
else {
mode_tree_draw(data->data);
wp->flags |= PANE_REDRAW;
}
}

File diff suppressed because it is too large Load Diff

376
window-client.c Normal file
View File

@@ -0,0 +1,376 @@
/* $OpenBSD$ */
/*
* Copyright (c) 2017 Nicholas Marriott <nicholas.marriott@gmail.com>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER
* IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
* OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include <sys/types.h>
#include <sys/time.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include "tmux.h"
static struct screen *window_client_init(struct window_pane *,
struct cmd_find_state *, struct args *);
static void window_client_free(struct window_pane *);
static void window_client_resize(struct window_pane *, u_int,
u_int);
static void window_client_key(struct window_pane *,
struct client *, struct session *, key_code,
struct mouse_event *);
#define WINDOW_CLIENT_DEFAULT_COMMAND "detach-client -t '%%'"
#define WINDOW_CLIENT_DEFAULT_FORMAT \
"session #{session_name} " \
"(#{client_width}x#{client_height}, #{t:client_activity})"
const struct window_mode window_client_mode = {
.name = "client-mode",
.init = window_client_init,
.free = window_client_free,
.resize = window_client_resize,
.key = window_client_key,
};
enum window_client_sort_type {
WINDOW_CLIENT_BY_NAME,
WINDOW_CLIENT_BY_SIZE,
WINDOW_CLIENT_BY_CREATION_TIME,
WINDOW_CLIENT_BY_ACTIVITY_TIME,
};
static const char *window_client_sort_list[] = {
"name",
"size",
"creation",
"activity"
};
struct window_client_itemdata {
struct client *c;
};
struct window_client_modedata {
struct mode_tree_data *data;
char *format;
char *command;
struct window_client_itemdata **item_list;
u_int item_size;
};
static struct window_client_itemdata *
window_client_add_item(struct window_client_modedata *data)
{
struct window_client_itemdata *item;
data->item_list = xreallocarray(data->item_list, data->item_size + 1,
sizeof *data->item_list);
item = data->item_list[data->item_size++] = xcalloc(1, sizeof *item);
return (item);
}
static void
window_client_free_item(struct window_client_itemdata *item)
{
server_client_unref(item->c);
free(item);
}
static int
window_client_cmp_name(const void *a0, const void *b0)
{
const struct window_client_itemdata *const *a = a0;
const struct window_client_itemdata *const *b = b0;
return (strcmp((*a)->c->name, (*b)->c->name));
}
static int
window_client_cmp_size(const void *a0, const void *b0)
{
const struct window_client_itemdata *const *a = a0;
const struct window_client_itemdata *const *b = b0;
if ((*a)->c->tty.sx < (*b)->c->tty.sx)
return (-1);
if ((*a)->c->tty.sx > (*b)->c->tty.sx)
return (1);
if ((*a)->c->tty.sy < (*b)->c->tty.sy)
return (-1);
if ((*a)->c->tty.sy > (*b)->c->tty.sy)
return (1);
return (strcmp((*a)->c->name, (*b)->c->name));
}
static int
window_client_cmp_creation_time(const void *a0, const void *b0)
{
const struct window_client_itemdata *const *a = a0;
const struct window_client_itemdata *const *b = b0;
if (timercmp(&(*a)->c->creation_time, &(*b)->c->creation_time, >))
return (-1);
if (timercmp(&(*a)->c->creation_time, &(*b)->c->creation_time, <))
return (1);
return (strcmp((*a)->c->name, (*b)->c->name));
}
static int
window_client_cmp_activity_time(const void *a0, const void *b0)
{
const struct window_client_itemdata *const *a = a0;
const struct window_client_itemdata *const *b = b0;
if (timercmp(&(*a)->c->activity_time, &(*b)->c->activity_time, >))
return (-1);
if (timercmp(&(*a)->c->activity_time, &(*b)->c->activity_time, <))
return (1);
return (strcmp((*a)->c->name, (*b)->c->name));
}
static void
window_client_build(void *modedata, u_int sort_type, __unused uint64_t *tag,
const char *filter)
{
struct window_client_modedata *data = modedata;
struct window_client_itemdata *item;
u_int i;
struct client *c;
char *text, *cp;
for (i = 0; i < data->item_size; i++)
window_client_free_item(data->item_list[i]);
free(data->item_list);
data->item_list = NULL;
data->item_size = 0;
TAILQ_FOREACH(c, &clients, entry) {
if (c->session == NULL || (c->flags & (CLIENT_DETACHING)))
continue;
item = window_client_add_item(data);
item->c = c;
c->references++;
}
switch (sort_type) {
case WINDOW_CLIENT_BY_NAME:
qsort(data->item_list, data->item_size, sizeof *data->item_list,
window_client_cmp_name);
break;
case WINDOW_CLIENT_BY_SIZE:
qsort(data->item_list, data->item_size, sizeof *data->item_list,
window_client_cmp_size);
break;
case WINDOW_CLIENT_BY_CREATION_TIME:
qsort(data->item_list, data->item_size, sizeof *data->item_list,
window_client_cmp_creation_time);
break;
case WINDOW_CLIENT_BY_ACTIVITY_TIME:
qsort(data->item_list, data->item_size, sizeof *data->item_list,
window_client_cmp_activity_time);
break;
}
for (i = 0; i < data->item_size; i++) {
item = data->item_list[i];
c = item->c;
if (filter != NULL) {
cp = format_single(NULL, filter, c, NULL, NULL, NULL);
if (!format_true(cp)) {
free(cp);
continue;
}
free(cp);
}
text = format_single(NULL, data->format, c, NULL, NULL, NULL);
mode_tree_add(data->data, NULL, item, (uint64_t)c, c->name,
text, -1);
free(text);
}
}
static struct screen *
window_client_draw(__unused void *modedata, void *itemdata, u_int sx, u_int sy)
{
struct window_client_itemdata *item = itemdata;
struct client *c = item->c;
struct window_pane *wp;
static struct screen s;
struct screen_write_ctx ctx;
if (c->session == NULL || (c->flags & (CLIENT_DEAD|CLIENT_DETACHING)))
return (NULL);
wp = c->session->curw->window->active;
screen_init(&s, sx, sy, 0);
screen_write_start(&ctx, NULL, &s);
screen_write_clearscreen(&ctx, 8);
screen_write_preview(&ctx, &wp->base, sx, sy - 3);
screen_write_cursormove(&ctx, 0, sy - 2);
screen_write_hline(&ctx, sx, 0, 0);
screen_write_cursormove(&ctx, 0, sy - 1);
if (c->old_status != NULL)
screen_write_copy(&ctx, c->old_status, 0, 0, sx, 1, NULL, NULL);
else
screen_write_copy(&ctx, &c->status, 0, 0, sx, 1, NULL, NULL);
screen_write_stop(&ctx);
return (&s);
}
static struct screen *
window_client_init(struct window_pane *wp, __unused struct cmd_find_state *fs,
struct args *args)
{
struct window_client_modedata *data;
struct screen *s;
wp->modedata = data = xcalloc(1, sizeof *data);
if (args == NULL || !args_has(args, 'F'))
data->format = xstrdup(WINDOW_CLIENT_DEFAULT_FORMAT);
else
data->format = xstrdup(args_get(args, 'F'));
if (args == NULL || args->argc == 0)
data->command = xstrdup(WINDOW_CLIENT_DEFAULT_COMMAND);
else
data->command = xstrdup(args->argv[0]);
data->data = mode_tree_start(wp, args, window_client_build,
window_client_draw, NULL, data, window_client_sort_list,
nitems(window_client_sort_list), &s);
mode_tree_build(data->data);
mode_tree_draw(data->data);
return (s);
}
static void
window_client_free(struct window_pane *wp)
{
struct window_client_modedata *data = wp->modedata;
u_int i;
if (data == NULL)
return;
mode_tree_free(data->data);
for (i = 0; i < data->item_size; i++)
window_client_free_item(data->item_list[i]);
free(data->item_list);
free(data->format);
free(data->command);
free(data);
}
static void
window_client_resize(struct window_pane *wp, u_int sx, u_int sy)
{
struct window_client_modedata *data = wp->modedata;
mode_tree_resize(data->data, sx, sy);
}
static void
window_client_do_detach(void* modedata, void *itemdata, key_code key)
{
struct window_client_modedata *data = modedata;
struct window_client_itemdata *item = itemdata;
if (item == mode_tree_get_current(data->data))
mode_tree_down(data->data, 0);
if (key == 'd' || key == 'D')
server_client_detach(item->c, MSG_DETACH);
else if (key == 'x' || key == 'X')
server_client_detach(item->c, MSG_DETACHKILL);
else if (key == 'z' || key == 'Z')
server_client_suspend(item->c);
}
static void
window_client_key(struct window_pane *wp, struct client *c,
__unused struct session *s, key_code key, struct mouse_event *m)
{
struct window_client_modedata *data = wp->modedata;
struct window_client_itemdata *item;
char *command, *name;
int finished;
/*
* t = toggle tag
* T = tag none
* C-t = tag all
* q = exit
* O = change sort order
*
* d = detach client
* D = detach tagged clients
* x = detach and kill client
* X = detach and kill tagged clients
* z = suspend client
* Z = suspend tagged clients
* Enter = detach client
*/
finished = mode_tree_key(data->data, c, &key, m);
switch (key) {
case 'd':
case 'x':
case 'z':
item = mode_tree_get_current(data->data);
window_client_do_detach(data, item, key);
mode_tree_build(data->data);
break;
case 'D':
case 'X':
case 'Z':
mode_tree_each_tagged(data->data, window_client_do_detach, key,
0);
mode_tree_build(data->data);
break;
case '\r':
item = mode_tree_get_current(data->data);
command = xstrdup(data->command);
name = xstrdup(item->c->ttyname);
window_pane_reset_mode(wp);
mode_tree_run_command(c, NULL, command, name);
free(name);
free(command);
return;
}
if (finished || server_client_how_many() == 0)
window_pane_reset_mode(wp);
else {
mode_tree_draw(data->data);
wp->flags |= PANE_REDRAW;
}
}

View File

@@ -24,7 +24,8 @@
#include "tmux.h"
static struct screen *window_clock_init(struct window_pane *);
static struct screen *window_clock_init(struct window_pane *,
struct cmd_find_state *, struct args *);
static void window_clock_free(struct window_pane *);
static void window_clock_resize(struct window_pane *, u_int, u_int);
static void window_clock_key(struct window_pane *, struct client *,
@@ -145,7 +146,8 @@ window_clock_timer_callback(__unused int fd, __unused short events, void *arg)
}
static struct screen *
window_clock_init(struct window_pane *wp)
window_clock_init(struct window_pane *wp, __unused struct cmd_find_state *fs,
__unused struct args *args)
{
struct window_clock_mode_data *data;
struct screen *s;

View File

@@ -27,7 +27,8 @@
static const char *window_copy_key_table(struct window_pane *);
static void window_copy_command(struct window_pane *, struct client *,
struct session *, struct args *, struct mouse_event *);
static struct screen *window_copy_init(struct window_pane *);
static struct screen *window_copy_init(struct window_pane *,
struct cmd_find_state *, struct args *);
static void window_copy_free(struct window_pane *);
static int window_copy_pagedown(struct window_pane *, int);
static void window_copy_next_paragraph(struct window_pane *);
@@ -187,7 +188,8 @@ struct window_copy_mode_data {
};
static struct screen *
window_copy_init(struct window_pane *wp)
window_copy_init(struct window_pane *wp, __unused struct cmd_find_state *fs,
__unused struct args *args)
{
struct window_copy_mode_data *data;
struct screen *s;
@@ -208,8 +210,13 @@ window_copy_init(struct window_pane *wp)
data->rectflag = 0;
data->scroll_exit = 0;
data->searchtype = WINDOW_COPY_OFF;
data->searchstr = NULL;
if (wp->searchstr != NULL) {
data->searchtype = WINDOW_COPY_SEARCHUP;
data->searchstr = xstrdup(wp->searchstr);
} else {
data->searchtype = WINDOW_COPY_OFF;
data->searchstr = NULL;
}
data->searchmark = NULL;
data->searchx = data->searchy = data->searcho = -1;
@@ -316,7 +323,7 @@ window_copy_vadd(struct window_pane *wp, const char *fmt, va_list ap)
* (so it's on a new line).
*/
screen_write_carriagereturn(&back_ctx);
screen_write_linefeed(&back_ctx, 0);
screen_write_linefeed(&back_ctx, 0, 8);
} else
data->backing_written = 1;
old_cy = backing->cy;
@@ -994,7 +1001,7 @@ window_copy_search_lr(struct grid *gd,
int matched;
for (ax = first; ax < last; ax++) {
if (ax + sgd->sx >= gd->sx)
if (ax + sgd->sx > gd->sx)
break;
for (bx = 0; bx < sgd->sx; bx++) {
px = ax + bx;
@@ -1134,6 +1141,9 @@ window_copy_search(struct window_pane *wp, int direction, int moveflag)
u_int fx, fy, endline;
int wrapflag, cis, found;
free(wp->searchstr);
wp->searchstr = xstrdup(data->searchstr);
fx = data->cx;
fy = screen_hsize(data->backing) - data->oy + data->cy;
@@ -1450,7 +1460,7 @@ window_copy_adjust_selection(struct window_pane *wp, u_int *selx, u_int *sely)
}
*selx = sx;
*sely = screen_hsize(s) + sy;
*sely = sy;
return (relpos);
}
@@ -1498,10 +1508,17 @@ window_copy_update_selection(struct window_pane *wp, int may_redraw)
* of lines, and redraw just past that in both directions
*/
cy = data->cy;
if (sy < cy)
window_copy_redraw_lines(wp, sy, cy - sy + 1);
else
window_copy_redraw_lines(wp, cy, sy - cy + 1);
if (data->cursordrag == CURSORDRAG_ENDSEL) {
if (sy < cy)
window_copy_redraw_lines(wp, sy, cy - sy + 1);
else
window_copy_redraw_lines(wp, cy, sy - cy + 1);
} else {
if (endsy < cy)
window_copy_redraw_lines(wp, endsy, cy - endsy + 1);
else
window_copy_redraw_lines(wp, cy, endsy - cy + 1);
}
}
return (1);
@@ -1619,10 +1636,11 @@ window_copy_copy_buffer(struct window_pane *wp, const char *bufname, void *buf,
{
struct screen_write_ctx ctx;
if (options_get_number(global_options, "set-clipboard")) {
if (options_get_number(global_options, "set-clipboard") != 0) {
screen_write_start(&ctx, wp, NULL);
screen_write_setselection(&ctx, buf, len);
screen_write_stop(&ctx);
notify_pane("pane-set-clipboard", wp);
}
if (paste_set(buf, len, bufname, NULL) != 0)
@@ -1676,10 +1694,11 @@ window_copy_append_selection(struct window_pane *wp, const char *bufname)
if (buf == NULL)
return;
if (options_get_number(global_options, "set-clipboard")) {
if (options_get_number(global_options, "set-clipboard") != 0) {
screen_write_start(&ctx, wp, NULL);
screen_write_setselection(&ctx, buf, len);
screen_write_stop(&ctx);
notify_pane("pane-set-clipboard", wp);
}
if (bufname == NULL || *bufname == '\0')
@@ -2395,14 +2414,17 @@ window_copy_scroll_down(struct window_pane *wp, u_int ny)
screen_write_stop(&ctx);
}
int
window_copy_scroll_position(struct window_pane *wp)
void
window_copy_add_formats(struct window_pane *wp, struct format_tree *ft)
{
struct window_copy_mode_data *data = wp->modedata;
struct screen *s = &data->screen;
if (wp->mode != &window_copy_mode)
return (-1);
return (data->oy);
return;
format_add(ft, "selection_present", "%d", s->sel.flag);
format_add(ft, "scroll_position", "%d", data->oy);
}
static void
@@ -2432,7 +2454,7 @@ window_copy_move_mouse(struct mouse_event *m)
if (wp == NULL || wp->mode != &window_copy_mode)
return;
if (cmd_mouse_at(wp, m, &x, &y, 1) != 0)
if (cmd_mouse_at(wp, m, &x, &y, 0) != 0)
return;
window_copy_update_cursor(wp, x, y);
@@ -2482,16 +2504,3 @@ window_copy_drag_update(__unused struct client *c, struct mouse_event *m)
if (window_copy_update_selection(wp, 1))
window_copy_redraw_selection(wp, old_cy);
}
const char *
window_copy_search_string(struct window_pane *wp)
{
struct window_copy_mode_data *data;
if (wp->mode != &window_copy_mode)
return ("");
data = wp->modedata;
if (data->searchtype == WINDOW_COPY_OFF || data->searchstr == NULL)
return ("");
return (data->searchstr);
}

991
window-tree.c Normal file
View File

@@ -0,0 +1,991 @@
/* $OpenBSD$ */
/*
* Copyright (c) 2017 Nicholas Marriott <nicholas.marriott@gmail.com>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER
* IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
* OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include <sys/types.h>
#include <stdlib.h>
#include <string.h>
#include "tmux.h"
static struct screen *window_tree_init(struct window_pane *,
struct cmd_find_state *, struct args *);
static void window_tree_free(struct window_pane *);
static void window_tree_resize(struct window_pane *, u_int, u_int);
static void window_tree_key(struct window_pane *,
struct client *, struct session *, key_code,
struct mouse_event *);
#define WINDOW_TREE_DEFAULT_COMMAND "switch-client -t '%%'"
#define WINDOW_TREE_DEFAULT_FORMAT \
"#{?pane_format," \
"#{pane_current_command} \"#{pane_title}\"" \
"," \
"#{?window_format," \
"#{window_name}#{window_flags} " \
"(#{window_panes} panes)" \
"#{?#{==:#{window_panes},1}, \"#{pane_title}\",}" \
"," \
"#{session_windows} windows" \
"#{?session_grouped, (group ,}" \
"#{session_group}#{?session_grouped,),}" \
"#{?session_attached, (attached),}" \
"}" \
"}"
const struct window_mode window_tree_mode = {
.name = "tree-mode",
.init = window_tree_init,
.free = window_tree_free,
.resize = window_tree_resize,
.key = window_tree_key,
};
enum window_tree_sort_type {
WINDOW_TREE_BY_INDEX,
WINDOW_TREE_BY_NAME,
WINDOW_TREE_BY_TIME,
};
static const char *window_tree_sort_list[] = {
"index",
"name",
"time"
};
enum window_tree_type {
WINDOW_TREE_NONE,
WINDOW_TREE_SESSION,
WINDOW_TREE_WINDOW,
WINDOW_TREE_PANE,
};
struct window_tree_itemdata {
enum window_tree_type type;
int session;
int winlink;
int pane;
};
struct window_tree_modedata {
struct window_pane *wp;
int dead;
int references;
struct mode_tree_data *data;
char *format;
char *command;
struct window_tree_itemdata **item_list;
u_int item_size;
struct client *client;
const char *entered;
struct cmd_find_state fs;
enum window_tree_type type;
int offset;
};
static void
window_tree_pull_item(struct window_tree_itemdata *item, struct session **sp,
struct winlink **wlp, struct window_pane **wp)
{
*wp = NULL;
*wlp = NULL;
*sp = session_find_by_id(item->session);
if (*sp == NULL)
return;
if (item->type == WINDOW_TREE_SESSION) {
*wlp = (*sp)->curw;
*wp = (*wlp)->window->active;
return;
}
*wlp = winlink_find_by_index(&(*sp)->windows, item->winlink);
if (*wlp == NULL) {
*sp = NULL;
return;
}
if (item->type == WINDOW_TREE_WINDOW) {
*wp = (*wlp)->window->active;
return;
}
*wp = window_pane_find_by_id(item->pane);
if (!window_has_pane((*wlp)->window, *wp))
*wp = NULL;
if (*wp == NULL) {
*sp = NULL;
*wlp = NULL;
return;
}
}
static struct window_tree_itemdata *
window_tree_add_item(struct window_tree_modedata *data)
{
struct window_tree_itemdata *item;
data->item_list = xreallocarray(data->item_list, data->item_size + 1,
sizeof *data->item_list);
item = data->item_list[data->item_size++] = xcalloc(1, sizeof *item);
return (item);
}
static void
window_tree_free_item(struct window_tree_itemdata *item)
{
free(item);
}
static int
window_tree_cmp_session_name(const void *a0, const void *b0)
{
const struct session *const *a = a0;
const struct session *const *b = b0;
return (strcmp((*a)->name, (*b)->name));
}
static int
window_tree_cmp_session_time(const void *a0, const void *b0)
{
const struct session *const *a = a0;
const struct session *const *b = b0;
if (timercmp(&(*a)->activity_time, &(*b)->activity_time, >))
return (-1);
if (timercmp(&(*a)->activity_time, &(*b)->activity_time, <))
return (1);
return (strcmp((*a)->name, (*b)->name));
}
static int
window_tree_cmp_window_name(const void *a0, const void *b0)
{
const struct winlink *const *a = a0;
const struct winlink *const *b = b0;
return (strcmp((*a)->window->name, (*b)->window->name));
}
static int
window_tree_cmp_window_time(const void *a0, const void *b0)
{
const struct winlink *const *a = a0;
const struct winlink *const *b = b0;
if (timercmp(&(*a)->window->activity_time,
&(*b)->window->activity_time, >))
return (-1);
if (timercmp(&(*a)->window->activity_time,
&(*b)->window->activity_time, <))
return (1);
return (strcmp((*a)->window->name, (*b)->window->name));
}
static int
window_tree_cmp_pane_time(const void *a0, const void *b0)
{
const struct window_pane *const *a = a0;
const struct window_pane *const *b = b0;
if ((*a)->active_point < (*b)->active_point)
return (-1);
if ((*a)->active_point > (*b)->active_point)
return (1);
return (0);
}
static void
window_tree_build_pane(struct session *s, struct winlink *wl,
struct window_pane *wp, void *modedata, struct mode_tree_item *parent)
{
struct window_tree_modedata *data = modedata;
struct window_tree_itemdata *item;
char *name, *text;
u_int idx;
window_pane_index(wp, &idx);
item = window_tree_add_item(data);
item->type = WINDOW_TREE_PANE;
item->session = s->id;
item->winlink = wl->idx;
item->pane = wp->id;
text = format_single(NULL, data->format, NULL, s, wl, wp);
xasprintf(&name, "%u", idx);
mode_tree_add(data->data, parent, item, (uint64_t)wp, name, text, -1);
free(text);
free(name);
}
static int
window_tree_filter_pane(struct session *s, struct winlink *wl,
struct window_pane *wp, const char *filter)
{
char *cp;
int result;
if (filter == NULL)
return (1);
cp = format_single(NULL, filter, NULL, s, wl, wp);
result = format_true(cp);
free(cp);
return (result);
}
static int
window_tree_build_window(struct session *s, struct winlink *wl, void* modedata,
u_int sort_type, struct mode_tree_item *parent, const char *filter)
{
struct window_tree_modedata *data = modedata;
struct window_tree_itemdata *item;
struct mode_tree_item *mti;
char *name, *text;
struct window_pane *wp, **l;
u_int n, i;
int expanded;
item = window_tree_add_item(data);
item->type = WINDOW_TREE_WINDOW;
item->session = s->id;
item->winlink = wl->idx;
item->pane = -1;
text = format_single(NULL, data->format, NULL, s, wl, NULL);
xasprintf(&name, "%u", wl->idx);
if (data->type == WINDOW_TREE_SESSION ||
data->type == WINDOW_TREE_WINDOW)
expanded = 0;
else
expanded = 1;
mti = mode_tree_add(data->data, parent, item, (uint64_t)wl, name, text,
expanded);
free(text);
free(name);
wp = TAILQ_FIRST(&wl->window->panes);
if (TAILQ_NEXT(wp, entry) == NULL) {
if (!window_tree_filter_pane(s, wl, wp, filter))
goto empty;
return (1);
}
l = NULL;
n = 0;
TAILQ_FOREACH(wp, &wl->window->panes, entry) {
if (!window_tree_filter_pane(s, wl, wp, filter))
continue;
l = xreallocarray(l, n + 1, sizeof *l);
l[n++] = wp;
}
if (n == 0)
goto empty;
switch (sort_type) {
case WINDOW_TREE_BY_INDEX:
break;
case WINDOW_TREE_BY_NAME:
/* Panes don't have names, so leave in number order. */
break;
case WINDOW_TREE_BY_TIME:
qsort(l, n, sizeof *l, window_tree_cmp_pane_time);
break;
}
for (i = 0; i < n; i++)
window_tree_build_pane(s, wl, l[i], modedata, mti);
free(l);
return (1);
empty:
window_tree_free_item(item);
data->item_size--;
mode_tree_remove(data->data, mti);
return (0);
}
static void
window_tree_build_session(struct session *s, void* modedata,
u_int sort_type, const char *filter)
{
struct window_tree_modedata *data = modedata;
struct window_tree_itemdata *item;
struct mode_tree_item *mti;
char *text;
struct winlink *wl, **l;
u_int n, i, empty;
int expanded;
item = window_tree_add_item(data);
item->type = WINDOW_TREE_SESSION;
item->session = s->id;
item->winlink = -1;
item->pane = -1;
text = format_single(NULL, data->format, NULL, s, NULL, NULL);
if (data->type == WINDOW_TREE_SESSION)
expanded = 0;
else
expanded = 1;
mti = mode_tree_add(data->data, NULL, item, (uint64_t)s, s->name, text,
expanded);
free(text);
l = NULL;
n = 0;
RB_FOREACH(wl, winlinks, &s->windows) {
l = xreallocarray(l, n + 1, sizeof *l);
l[n++] = wl;
}
switch (sort_type) {
case WINDOW_TREE_BY_INDEX:
break;
case WINDOW_TREE_BY_NAME:
qsort(l, n, sizeof *l, window_tree_cmp_window_name);
break;
case WINDOW_TREE_BY_TIME:
qsort(l, n, sizeof *l, window_tree_cmp_window_time);
break;
}
empty = 0;
for (i = 0; i < n; i++) {
if (!window_tree_build_window(s, l[i], modedata, sort_type, mti,
filter))
empty++;
}
if (empty == n) {
window_tree_free_item(item);
data->item_size--;
mode_tree_remove(data->data, mti);
}
free(l);
}
static void
window_tree_build(void *modedata, u_int sort_type, uint64_t *tag,
const char *filter)
{
struct window_tree_modedata *data = modedata;
struct session *s, **l;
u_int n, i;
for (i = 0; i < data->item_size; i++)
window_tree_free_item(data->item_list[i]);
free(data->item_list);
data->item_list = NULL;
data->item_size = 0;
l = NULL;
n = 0;
RB_FOREACH(s, sessions, &sessions) {
l = xreallocarray(l, n + 1, sizeof *l);
l[n++] = s;
}
switch (sort_type) {
case WINDOW_TREE_BY_INDEX:
break;
case WINDOW_TREE_BY_NAME:
qsort(l, n, sizeof *l, window_tree_cmp_session_name);
break;
case WINDOW_TREE_BY_TIME:
qsort(l, n, sizeof *l, window_tree_cmp_session_time);
break;
}
for (i = 0; i < n; i++)
window_tree_build_session(l[i], modedata, sort_type, filter);
free(l);
switch (data->type) {
case WINDOW_TREE_NONE:
break;
case WINDOW_TREE_SESSION:
*tag = (uint64_t)data->fs.s;
break;
case WINDOW_TREE_WINDOW:
*tag = (uint64_t)data->fs.wl;
break;
case WINDOW_TREE_PANE:
*tag = (uint64_t)data->fs.wp;
break;
}
}
static void
window_tree_draw_session(struct window_tree_modedata *data, struct session *s,
struct screen_write_ctx *ctx, u_int sx, u_int sy)
{
struct options *oo = s->options;
struct winlink *wl;
struct window *w;
u_int loop, total, visible, each, width, offset;
u_int current, start, end, remaining, i;
struct grid_cell gc;
int colour, active_colour, left, right;
char *label;
size_t len;
total = winlink_count(&s->windows);
memcpy(&gc, &grid_default_cell, sizeof gc);
colour = options_get_number(oo, "display-panes-colour");
active_colour = options_get_number(oo, "display-panes-active-colour");
if (sx / total < 24) {
visible = sx / 24;
if (visible == 0)
visible = 1;
} else
visible = total;
current = 0;
RB_FOREACH(wl, winlinks, &s->windows) {
if (wl == s->curw)
break;
current++;
}
if (current < visible) {
start = 0;
end = visible;
} else if (current >= total - visible) {
start = total - visible;
end = total;
} else {
start = current - (visible / 2);
end = start + visible;
}
if (data->offset < -(int)start)
data->offset = -(int)start;
if (data->offset > (int)(total - end))
data->offset = (int)(total - end);
start += data->offset;
end += data->offset;
left = (start != 0);
right = (end != total);
if (((left && right) && sx <= 6) || ((left || right) && sx <= 3))
left = right = 0;
if (left && right) {
each = (sx - 6) / visible;
remaining = (sx - 6) - (visible * each);
} else if (left || right) {
each = (sx - 3) / visible;
remaining = (sx - 3) - (visible * each);
} else {
each = sx / visible;
remaining = sx - (visible * each);
}
if (each == 0)
return;
if (left) {
screen_write_cursormove(ctx, 2, 0);
screen_write_vline(ctx, sy, 0, 0);
screen_write_cursormove(ctx, 0, sy / 2);
screen_write_puts(ctx, &grid_default_cell, "<");
}
if (right) {
screen_write_cursormove(ctx, sx - 3, 0);
screen_write_vline(ctx, sy, 0, 0);
screen_write_cursormove(ctx, sx - 1, sy / 2);
screen_write_puts(ctx, &grid_default_cell, ">");
}
i = loop = 0;
RB_FOREACH(wl, winlinks, &s->windows) {
if (loop == end)
break;
if (loop < start) {
loop++;
continue;
}
w = wl->window;
if (wl == s->curw)
gc.fg = active_colour;
else
gc.fg = colour;
if (left)
offset = 3 + (i * each);
else
offset = (i * each);
if (loop == end - 1)
width = each + remaining;
else
width = each - 1;
screen_write_cursormove(ctx, offset, 0);
screen_write_preview(ctx, &w->active->base, width, sy);
xasprintf(&label, " %u:%s ", wl->idx, w->name);
if (strlen(label) > width)
xasprintf(&label, " %u ", wl->idx);
len = strlen(label) / 2;
screen_write_cursormove(ctx, offset + (each / 2) - len, sy / 2);
if (len < width)
screen_write_puts(ctx, &gc, "%s", label);
free(label);
if (loop != end - 1) {
screen_write_cursormove(ctx, offset + width, 0);
screen_write_vline(ctx, sy, 0, 0);
}
loop++;
i++;
}
}
static void
window_tree_draw_window(struct window_tree_modedata *data, struct session *s,
struct window *w, struct screen_write_ctx *ctx, u_int sx, u_int sy)
{
struct options *oo = s->options;
struct window_pane *wp;
u_int loop, total, visible, each, width, offset;
u_int current, start, end, remaining, i;
struct grid_cell gc;
int colour, active_colour, left, right;
char *label;
size_t len;
total = window_count_panes(w);
memcpy(&gc, &grid_default_cell, sizeof gc);
colour = options_get_number(oo, "display-panes-colour");
active_colour = options_get_number(oo, "display-panes-active-colour");
if (sx / total < 24) {
visible = sx / 24;
if (visible == 0)
visible = 1;
} else
visible = total;
current = 0;
TAILQ_FOREACH(wp, &w->panes, entry) {
if (wp == w->active)
break;
current++;
}
if (current < visible) {
start = 0;
end = visible;
} else if (current >= total - visible) {
start = total - visible;
end = total;
} else {
start = current - (visible / 2);
end = start + visible;
}
if (data->offset < -(int)start)
data->offset = -(int)start;
if (data->offset > (int)(total - end))
data->offset = (int)(total - end);
start += data->offset;
end += data->offset;
left = (start != 0);
right = (end != total);
if (((left && right) && sx <= 6) || ((left || right) && sx <= 3))
left = right = 0;
if (left && right) {
each = (sx - 6) / visible;
remaining = (sx - 6) - (visible * each);
} else if (left || right) {
each = (sx - 3) / visible;
remaining = (sx - 3) - (visible * each);
} else {
each = sx / visible;
remaining = sx - (visible * each);
}
if (each == 0)
return;
if (left) {
screen_write_cursormove(ctx, 2, 0);
screen_write_vline(ctx, sy, 0, 0);
screen_write_cursormove(ctx, 0, sy / 2);
screen_write_puts(ctx, &grid_default_cell, "<");
}
if (right) {
screen_write_cursormove(ctx, sx - 3, 0);
screen_write_vline(ctx, sy, 0, 0);
screen_write_cursormove(ctx, sx - 1, sy / 2);
screen_write_puts(ctx, &grid_default_cell, ">");
}
i = loop = 0;
TAILQ_FOREACH(wp, &w->panes, entry) {
if (loop == end)
break;
if (loop < start) {
loop++;
continue;
}
if (wp == w->active)
gc.fg = active_colour;
else
gc.fg = colour;
if (left)
offset = 3 + (i * each);
else
offset = (i * each);
if (loop == end - 1)
width = each + remaining;
else
width = each - 1;
screen_write_cursormove(ctx, offset, 0);
screen_write_preview(ctx, &wp->base, width, sy);
xasprintf(&label, " %u ", loop);
len = strlen(label) / 2;
screen_write_cursormove(ctx, offset + (each / 2) - len, sy / 2);
if (len < width)
screen_write_puts(ctx, &gc, "%s", label);
free(label);
if (loop != end - 1) {
screen_write_cursormove(ctx, offset + width, 0);
screen_write_vline(ctx, sy, 0, 0);
}
loop++;
i++;
}
}
static struct screen *
window_tree_draw(void *modedata, void *itemdata, u_int sx, u_int sy)
{
struct window_tree_itemdata *item = itemdata;
struct session *sp;
struct winlink *wlp;
struct window_pane *wp;
static struct screen s;
struct screen_write_ctx ctx;
window_tree_pull_item(item, &sp, &wlp, &wp);
if (wp == NULL)
return (NULL);
screen_init(&s, sx, sy, 0);
screen_write_start(&ctx, NULL, &s);
switch (item->type) {
case WINDOW_TREE_NONE:
return (0);
case WINDOW_TREE_SESSION:
window_tree_draw_session(modedata, sp, &ctx, sx, sy);
break;
case WINDOW_TREE_WINDOW:
window_tree_draw_window(modedata, sp, wlp->window, &ctx, sx, sy);
break;
case WINDOW_TREE_PANE:
screen_write_preview(&ctx, &wp->base, sx, sy);
break;
}
screen_write_stop(&ctx);
return (&s);
}
static int
window_tree_search(__unused void *modedata, void *itemdata, const char *ss)
{
struct window_tree_itemdata *item = itemdata;
struct session *s;
struct winlink *wl;
struct window_pane *wp;
const char *cmd;
window_tree_pull_item(item, &s, &wl, &wp);
switch (item->type) {
case WINDOW_TREE_NONE:
return (0);
case WINDOW_TREE_SESSION:
if (s == NULL)
return (0);
return (strstr(s->name, ss) != NULL);
case WINDOW_TREE_WINDOW:
if (s == NULL || wl == NULL)
return (0);
return (strstr(wl->window->name, ss) != NULL);
case WINDOW_TREE_PANE:
if (s == NULL || wl == NULL || wp == NULL)
break;
cmd = osdep_get_name(wp->fd, wp->tty);
if (cmd == NULL || *cmd == '\0')
return (0);
return (strstr(cmd, ss) != NULL);
}
return (0);
}
static struct screen *
window_tree_init(struct window_pane *wp, struct cmd_find_state *fs,
struct args *args)
{
struct window_tree_modedata *data;
struct screen *s;
wp->modedata = data = xcalloc(1, sizeof *data);
if (args_has(args, 's'))
data->type = WINDOW_TREE_SESSION;
else if (args_has(args, 'w'))
data->type = WINDOW_TREE_WINDOW;
else
data->type = WINDOW_TREE_PANE;
memcpy(&data->fs, fs, sizeof data->fs);
data->wp = wp;
data->references = 1;
if (args == NULL || !args_has(args, 'F'))
data->format = xstrdup(WINDOW_TREE_DEFAULT_FORMAT);
else
data->format = xstrdup(args_get(args, 'F'));
if (args == NULL || args->argc == 0)
data->command = xstrdup(WINDOW_TREE_DEFAULT_COMMAND);
else
data->command = xstrdup(args->argv[0]);
data->data = mode_tree_start(wp, args, window_tree_build,
window_tree_draw, window_tree_search, data, window_tree_sort_list,
nitems(window_tree_sort_list), &s);
mode_tree_build(data->data);
mode_tree_draw(data->data);
data->type = WINDOW_TREE_NONE;
return (s);
}
static void
window_tree_destroy(struct window_tree_modedata *data)
{
u_int i;
if (--data->references != 0)
return;
mode_tree_free(data->data);
for (i = 0; i < data->item_size; i++)
window_tree_free_item(data->item_list[i]);
free(data->item_list);
free(data->format);
free(data->command);
free(data);
}
static void
window_tree_free(struct window_pane *wp)
{
struct window_tree_modedata *data = wp->modedata;
if (data == NULL)
return;
data->dead = 1;
window_tree_destroy(data);
}
static void
window_tree_resize(struct window_pane *wp, u_int sx, u_int sy)
{
struct window_tree_modedata *data = wp->modedata;
mode_tree_resize(data->data, sx, sy);
}
static char *
window_tree_get_target(struct window_tree_itemdata *item,
struct cmd_find_state *fs)
{
struct session *s;
struct winlink *wl;
struct window_pane *wp;
char *target;
window_tree_pull_item(item, &s, &wl, &wp);
target = NULL;
switch (item->type) {
case WINDOW_TREE_NONE:
break;
case WINDOW_TREE_SESSION:
if (s == NULL)
break;
xasprintf(&target, "=%s:", s->name);
break;
case WINDOW_TREE_WINDOW:
if (s == NULL || wl == NULL)
break;
xasprintf(&target, "=%s:%u.", s->name, wl->idx);
break;
case WINDOW_TREE_PANE:
if (s == NULL || wl == NULL || wp == NULL)
break;
xasprintf(&target, "=%s:%u.%%%u", s->name, wl->idx, wp->id);
break;
}
if (target == NULL)
cmd_find_clear_state(fs, 0);
else
cmd_find_from_winlink_pane(fs, wl, wp, 0);
return (target);
}
static void
window_tree_command_each(void* modedata, void* itemdata, __unused key_code key)
{
struct window_tree_modedata *data = modedata;
struct window_tree_itemdata *item = itemdata;
char *name;
struct cmd_find_state fs;
name = window_tree_get_target(item, &fs);
if (name != NULL)
mode_tree_run_command(data->client, &fs, data->entered, name);
free(name);
}
static enum cmd_retval
window_tree_command_done(__unused struct cmdq_item *item, void *modedata)
{
struct window_tree_modedata *data = modedata;
if (!data->dead) {
mode_tree_build(data->data);
mode_tree_draw(data->data);
data->wp->flags |= PANE_REDRAW;
}
window_tree_destroy(data);
return (CMD_RETURN_NORMAL);
}
static int
window_tree_command_callback(struct client *c, void *modedata, const char *s,
__unused int done)
{
struct window_tree_modedata *data = modedata;
if (data->dead)
return (0);
data->client = c;
data->entered = s;
mode_tree_each_tagged(data->data, window_tree_command_each, KEYC_NONE,
1);
data->client = NULL;
data->entered = NULL;
data->references++;
cmdq_append(c, cmdq_get_callback(window_tree_command_done, data));
return (0);
}
static void
window_tree_command_free(void *modedata)
{
struct window_tree_modedata *data = modedata;
window_tree_destroy(data);
}
static void
window_tree_key(struct window_pane *wp, struct client *c,
__unused struct session *s, key_code key, struct mouse_event *m)
{
struct window_tree_modedata *data = wp->modedata;
struct window_tree_itemdata *item;
char *command, *name, *prompt;
struct cmd_find_state fs;
int finished;
u_int tagged;
item = mode_tree_get_current(data->data);
finished = mode_tree_key(data->data, c, &key, m);
if (item != mode_tree_get_current(data->data))
data->offset = 0;
switch (key) {
case '<':
data->offset--;
break;
case '>':
data->offset++;
break;
case ':':
tagged = mode_tree_count_tagged(data->data);
if (tagged != 0)
xasprintf(&prompt, "(%u tagged) ", tagged);
else
xasprintf(&prompt, "(current) ");
data->references++;
status_prompt_set(c, prompt, "", window_tree_command_callback,
window_tree_command_free, data, PROMPT_NOFORMAT);
free(prompt);
break;
case '\r':
item = mode_tree_get_current(data->data);
command = xstrdup(data->command);
name = window_tree_get_target(item, &fs);
window_pane_reset_mode(wp);
if (name != NULL)
mode_tree_run_command(c, NULL, command, name);
free(name);
free(command);
return;
}
if (finished)
window_pane_reset_mode(wp);
else {
mode_tree_draw(data->data);
wp->flags |= PANE_REDRAW;
}
}

View File

@@ -387,6 +387,23 @@ window_destroy(struct window *w)
free(w);
}
int
window_pane_destroy_ready(struct window_pane *wp)
{
int n;
if (wp->pipe_fd != -1) {
if (EVBUFFER_LENGTH(wp->pipe_event->output) != 0)
return (0);
if (ioctl(wp->fd, FIONREAD, &n) != -1 && n > 0)
return (0);
}
if (~wp->flags & PANE_EXITED)
return (0);
return (1);
}
void
window_add_ref(struct window *w, const char *from)
{
@@ -408,7 +425,7 @@ void
window_set_name(struct window *w, const char *new_name)
{
free(w->name);
w->name = xstrdup(new_name);
utf8_stravis(&w->name, new_name, VIS_OCTAL|VIS_CSTYLE|VIS_TAB|VIS_NL);
notify_window("window-renamed", w);
}
@@ -615,6 +632,8 @@ window_add_pane(struct window *w, struct window_pane *other, int before,
void
window_lost_pane(struct window *w, struct window_pane *wp)
{
log_debug("%s: @%u pane %%%u", __func__, w->id, wp->id);
if (wp == marked_pane.wp)
server_clear_marked();
@@ -800,8 +819,8 @@ window_pane_create(struct window *w, u_int sx, u_int sy, u_int hlimit)
wp->xoff = 0;
wp->yoff = 0;
wp->sx = sx;
wp->sy = sy;
wp->sx = wp->osx = sx;
wp->sy = wp->osx = sy;
wp->pipe_fd = -1;
wp->pipe_off = 0;
@@ -828,6 +847,7 @@ static void
window_pane_destroy(struct window_pane *wp)
{
window_pane_reset_mode(wp);
free(wp->searchstr);
if (wp->fd != -1) {
#ifdef HAVE_UTEMPTER
@@ -873,6 +893,7 @@ window_pane_spawn(struct window_pane *wp, int argc, char **argv,
char s[32];
#endif
int i;
sigset_t set, oldset;
if (wp->fd != -1) {
bufferevent_free(wp->event);
@@ -902,14 +923,21 @@ window_pane_spawn(struct window_pane *wp, int argc, char **argv,
ws.ws_col = screen_size_x(&wp->base);
ws.ws_row = screen_size_y(&wp->base);
wp->pid = fdforkpty(ptm_fd, &wp->fd, wp->tty, NULL, &ws);
switch (wp->pid) {
sigfillset(&set);
sigprocmask(SIG_BLOCK, &set, &oldset);
switch (wp->pid = fdforkpty(ptm_fd, &wp->fd, wp->tty, NULL, &ws)) {
case -1:
wp->fd = -1;
xasprintf(cause, "%s: %s", cmd, strerror(errno));
free(cmd);
sigprocmask(SIG_SETMASK, &oldset, NULL);
return (-1);
case 0:
proc_clear_signals(server_proc, 1);
sigprocmask(SIG_SETMASK, &oldset, NULL);
if (chdir(wp->cwd) != 0) {
if ((home = find_home()) == NULL || chdir(home) != 0)
chdir("/");
@@ -926,6 +954,7 @@ window_pane_spawn(struct window_pane *wp, int argc, char **argv,
if (tcsetattr(STDIN_FILENO, TCSANOW, &tio2) != 0)
fatal("tcgetattr failed");
log_close();
closefrom(STDERR_FILENO + 1);
if (path != NULL)
@@ -933,9 +962,6 @@ window_pane_spawn(struct window_pane *wp, int argc, char **argv,
environ_set(env, "TMUX_PANE", "%%%u", wp->id);
environ_push(env);
clear_signals(1);
log_close();
setenv("SHELL", wp->shell, 1);
ptr = strrchr(wp->shell, '/');
@@ -974,6 +1000,7 @@ window_pane_spawn(struct window_pane *wp, int argc, char **argv,
kill(getpid(), SIGCHLD);
#endif
sigprocmask(SIG_SETMASK, &oldset, NULL);
setblocking(wp->fd, 0);
wp->event = bufferevent_new(wp->fd, window_pane_read_callback, NULL,
@@ -1013,7 +1040,11 @@ window_pane_error_callback(__unused struct bufferevent *bufev,
{
struct window_pane *wp = data;
server_destroy_pane(wp, 1);
log_debug("%%%u error", wp->id);
wp->flags |= PANE_EXITED;
if (window_pane_destroy_ready(wp))
server_destroy_pane(wp, 1);
}
void
@@ -1185,7 +1216,8 @@ window_pane_mode_timer(__unused int fd, __unused short events, void *arg)
}
int
window_pane_set_mode(struct window_pane *wp, const struct window_mode *mode)
window_pane_set_mode(struct window_pane *wp, const struct window_mode *mode,
struct cmd_find_state *fs, struct args *args)
{
struct screen *s;
struct timeval tv = { .tv_sec = 10 };
@@ -1198,7 +1230,7 @@ window_pane_set_mode(struct window_pane *wp, const struct window_mode *mode)
evtimer_set(&wp->modetimer, window_pane_mode_timer, wp);
evtimer_add(&wp->modetimer, &tv);
if ((s = wp->mode->init(wp)) != NULL)
if ((s = wp->mode->init(wp, fs, args)) != NULL)
wp->screen = s;
wp->flags |= (PANE_REDRAW|PANE_CHANGED);
@@ -1238,7 +1270,7 @@ window_pane_key(struct window_pane *wp, struct client *c, struct session *s,
if (wp->mode != NULL) {
wp->modelast = time(NULL);
if (wp->mode->key != NULL)
wp->mode->key(wp, c, s, key, m);
wp->mode->key(wp, c, s, (key & ~KEYC_XTERM), m);
return;
}
@@ -1262,49 +1294,42 @@ window_pane_key(struct window_pane *wp, struct client *c, struct session *s,
}
int
window_pane_outside(struct window_pane *wp)
window_pane_visible(struct window_pane *wp)
{
struct window *w = wp->window;
if (wp->xoff >= w->sx || wp->yoff >= w->sy)
return (1);
if (wp->xoff + wp->sx > w->sx || wp->yoff + wp->sy > w->sy)
return (1);
return (0);
}
int
window_pane_visible(struct window_pane *wp)
{
if (wp->layout_cell == NULL)
return (0);
return (!window_pane_outside(wp));
if (wp->xoff >= w->sx || wp->yoff >= w->sy)
return (0);
if (wp->xoff + wp->sx > w->sx || wp->yoff + wp->sy > w->sy)
return (0);
return (1);
}
char *
window_pane_search(struct window_pane *wp, const char *searchstr,
u_int *lineno)
u_int
window_pane_search(struct window_pane *wp, const char *searchstr)
{
struct screen *s = &wp->base;
char *newsearchstr, *line, *msg;
char *newsearchstr, *line;
u_int i;
msg = NULL;
xasprintf(&newsearchstr, "*%s*", searchstr);
for (i = 0; i < screen_size_y(s); i++) {
line = grid_view_string_cells(s->grid, 0, i, screen_size_x(s));
if (fnmatch(newsearchstr, line, 0) == 0) {
msg = line;
if (lineno != NULL)
*lineno = i;
free(line);
break;
}
free(line);
}
free(newsearchstr);
return (msg);
if (i == screen_size_y(s))
return (0);
return (i + 1);
}
/* Get MRU pane from a list. */