merge conflicts

This commit is contained in:
christos 2017-10-12 22:17:34 +00:00
parent c9ad075b4f
commit fe99a11758
50 changed files with 2878 additions and 3382 deletions

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

@ -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,
__UNCONST(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

@ -40,20 +40,23 @@ const struct cmd_entry cmd_attach_session_entry = {
.args = { "c:dErt:", 0, 0 },
.usage = "[-dEr] [-c working-directory] " CMD_TARGET_SESSION_USAGE,
.tflag = CMD_SESSION_WITHPANE,
/* -t is special */
.flags = CMD_STARTSERVER,
.exec = cmd_attach_session_exec
};
enum cmd_retval
cmd_attach_session(struct cmdq_item *item, int dflag, int rflag,
const char *cflag, int Eflag)
cmd_attach_session(struct cmdq_item *item, const char *tflag, int dflag,
int rflag, const char *cflag, int Eflag)
{
struct session *s = item->state.tflag.s;
struct cmd_find_state *current = &item->shared->current;
enum cmd_find_type type;
int flags;
struct client *c = item->client, *c_loop;
struct winlink *wl = item->state.tflag.wl;
struct window_pane *wp = item->state.tflag.wp;
struct session *s;
struct winlink *wl;
struct window_pane *wp;
char *cause;
if (RB_EMPTY(&sessions)) {
@ -69,10 +72,27 @@ cmd_attach_session(struct cmdq_item *item, int dflag, int rflag,
return (CMD_RETURN_ERROR);
}
if (tflag != NULL && tflag[strcspn(tflag, ":.")] != '\0') {
type = CMD_FIND_PANE;
flags = 0;
} else {
type = CMD_FIND_SESSION;
flags = CMD_FIND_PREFER_UNATTACHED;
}
if (cmd_find_target(&item->target, item, tflag, type, flags) != 0)
return (CMD_RETURN_ERROR);
s = item->target.s;
wl = item->target.wl;
wp = item->target.wp;
if (wl != NULL) {
if (wp != NULL)
window_set_active_pane(wp->window, wp);
session_set_current(s, wl);
if (wp != NULL)
cmd_find_from_winlink_pane(current, wl, wp, 0);
else
cmd_find_from_winlink(current, wl, 0);
}
if (cflag != NULL) {
@ -92,7 +112,7 @@ cmd_attach_session(struct cmdq_item *item, int dflag, int rflag,
environ_update(s->options, c->environ, s->environ);
c->session = s;
if (!item->repeat)
if (~item->shared->flags & CMDQ_SHARED_REPEAT)
server_client_set_key_table(c, NULL);
status_timer_start(c);
notify_client("client-session-changed", c);
@ -145,6 +165,7 @@ cmd_attach_session_exec(struct cmd *self, struct cmdq_item *item)
{
struct args *args = self->args;
return (cmd_attach_session(item, args_has(args, 'd'),
args_has(args, 'r'), args_get(args, 'c'), args_has(args, 'E')));
return (cmd_attach_session(item, args_get(args, 't'),
args_has(args, 'd'), args_has(args, 'r'), args_get(args, 'c'),
args_has(args, 'E')));
}

View File

@ -43,7 +43,7 @@ const struct cmd_entry cmd_capture_pane_entry = {
.usage = "[-aCeJpPq] " CMD_BUFFER_USAGE " [-E end-line] "
"[-S start-line]" CMD_TARGET_PANE_USAGE,
.tflag = CMD_PANE,
.target = { 't', CMD_FIND_PANE, 0 },
.flags = CMD_AFTERHOOK,
.exec = cmd_capture_pane_exec
@ -56,7 +56,7 @@ const struct cmd_entry cmd_clear_history_entry = {
.args = { "t:", 0, 0 },
.usage = CMD_TARGET_PANE_USAGE,
.tflag = CMD_PANE,
.target = { 't', CMD_FIND_PANE, 0 },
.flags = CMD_AFTERHOOK,
.exec = cmd_capture_pane_exec
@ -193,7 +193,7 @@ cmd_capture_pane_exec(struct cmd *self, struct cmdq_item *item)
{
struct args *args = self->args;
struct client *c;
struct window_pane *wp = item->state.tflag.wp;
struct window_pane *wp = item->target.wp;
char *buf, *cause;
const char *bufname;
size_t len;

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]",
.tflag = CMD_WINDOW,
.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 = item->state.c;
struct winlink *wl = item->state.tflag.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]",
.tflag = CMD_WINDOW,
.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 = item->state.c;
struct client *c1;
struct window_choose_data *cdata;
struct winlink *wl = item->state.tflag.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

@ -43,8 +43,7 @@ const struct cmd_entry cmd_display_message_entry = {
.usage = "[-p] [-c target-client] [-F format] "
CMD_TARGET_PANE_USAGE " [message]",
.cflag = CMD_CLIENT_CANFAIL,
.tflag = CMD_PANE,
.target = { 't', CMD_FIND_PANE, 0 },
.flags = CMD_AFTERHOOK,
.exec = cmd_display_message_exec
@ -54,10 +53,10 @@ static enum cmd_retval
cmd_display_message_exec(struct cmd *self, struct cmdq_item *item)
{
struct args *args = self->args;
struct client *c = item->state.c;
struct session *s = item->state.tflag.s;
struct winlink *wl = item->state.tflag.wl;
struct window_pane *wp = item->state.tflag.wp;
struct client *c;
struct session *s = item->target.s;
struct winlink *wl = item->target.wl;
struct window_pane *wp = item->target.wp;
const char *template;
char *msg;
struct format_tree *ft;
@ -66,6 +65,7 @@ cmd_display_message_exec(struct cmd *self, struct cmdq_item *item)
cmdq_error(item, "only one of -F or argument must be given");
return (CMD_RETURN_ERROR);
}
c = cmd_find_client(item, args_get(args, 'c'), 1);
template = args_get(args, 'F');
if (args->argc != 0)
@ -73,7 +73,7 @@ cmd_display_message_exec(struct cmd *self, struct cmdq_item *item)
if (template == NULL)
template = DISPLAY_MESSAGE_TEMPLATE;
ft = format_create(item, FORMAT_NONE, 0);
ft = format_create(item->client, item, FORMAT_NONE, 0);
format_defaults(ft, c, s, wl, wp);
msg = format_expand_time(ft, template, time(NULL));

View File

@ -31,7 +31,6 @@
static enum cmd_retval cmd_if_shell_exec(struct cmd *, struct cmdq_item *);
static enum cmd_retval cmd_if_shell_error(struct cmdq_item *, void *);
static void cmd_if_shell_callback(struct job *);
static void cmd_if_shell_free(void *);
@ -43,7 +42,7 @@ const struct cmd_entry cmd_if_shell_entry = {
.usage = "[-bF] " CMD_TARGET_PANE_USAGE " shell-command command "
"[command]",
.tflag = CMD_PANE_CANFAIL,
.target = { 't', CMD_FIND_PANE, CMD_FIND_CANFAIL },
.flags = 0,
.exec = cmd_if_shell_exec
@ -65,14 +64,15 @@ static enum cmd_retval
cmd_if_shell_exec(struct cmd *self, struct cmdq_item *item)
{
struct args *args = self->args;
struct cmdq_shared *shared = item->shared;
struct cmd_if_shell_data *cdata;
char *shellcmd, *cmd, *cause;
struct cmd_list *cmdlist;
struct cmdq_item *new_item;
struct client *c = item->state.c;
struct session *s = item->state.tflag.s;
struct winlink *wl = item->state.tflag.wl;
struct window_pane *wp = item->state.tflag.wp;
struct client *c = cmd_find_client(item, NULL, 1);
struct session *s = item->target.s;
struct winlink *wl = item->target.wl;
struct window_pane *wp = item->target.wp;
const char *cwd;
if (item->client != NULL && item->client->session == NULL)
@ -100,7 +100,7 @@ cmd_if_shell_exec(struct cmd *self, struct cmdq_item *item)
}
return (CMD_RETURN_ERROR);
}
new_item = cmdq_get_command(cmdlist, NULL, &item->mouse, 0);
new_item = cmdq_get_command(cmdlist, NULL, &shared->mouse, 0);
cmdq_insert_after(item, new_item);
cmd_list_free(cmdlist);
return (CMD_RETURN_NORMAL);
@ -119,16 +119,17 @@ cmd_if_shell_exec(struct cmd *self, struct cmdq_item *item)
cdata->cmd_else = NULL;
cdata->client = item->client;
cdata->client->references++;
if (cdata->client != NULL)
cdata->client->references++;
if (!args_has(args, 'b'))
cdata->item = item;
else
cdata->item = NULL;
memcpy(&cdata->mouse, &item->mouse, sizeof cdata->mouse);
memcpy(&cdata->mouse, &shared->mouse, sizeof cdata->mouse);
job_run(shellcmd, s, cwd, cmd_if_shell_callback, cmd_if_shell_free,
cdata);
job_run(shellcmd, s, cwd, NULL, cmd_if_shell_callback,
cmd_if_shell_free, cdata);
free(shellcmd);
if (args_has(args, 'b'))
@ -136,17 +137,6 @@ cmd_if_shell_exec(struct cmd *self, struct cmdq_item *item)
return (CMD_RETURN_WAIT);
}
static enum cmd_retval
cmd_if_shell_error(struct cmdq_item *item, void *data)
{
char *error = data;
cmdq_error(item, "%s", error);
free(error);
return (CMD_RETURN_NORMAL);
}
static void
cmd_if_shell_callback(struct job *job)
{
@ -166,10 +156,10 @@ cmd_if_shell_callback(struct job *job)
cmdlist = cmd_string_parse(cmd, file, line, &cause);
if (cmdlist == NULL) {
if (cause != NULL)
new_item = cmdq_get_callback(cmd_if_shell_error, cause);
else
new_item = NULL;
if (cause != NULL && cdata->item != NULL)
cmdq_error(cdata->item, "%s", cause);
free(cause);
new_item = NULL;
} else {
new_item = cmdq_get_command(cmdlist, NULL, &cdata->mouse, 0);
cmd_list_free(cmdlist);
@ -192,7 +182,8 @@ cmd_if_shell_free(void *data)
{
struct cmd_if_shell_data *cdata = data;
server_client_unref(cdata->client);
if (cdata->client != NULL)
server_client_unref(cdata->client);
free(cdata->cmd_else);
free(cdata->cmd_if);

View File

@ -127,6 +127,7 @@ error:
free(pdata);
if (f != NULL)
fclose(f);
free(file);
return (CMD_RETURN_ERROR);
}

View File

@ -44,7 +44,7 @@ const struct cmd_entry cmd_new_session_entry = {
"[-s session-name] " CMD_TARGET_SESSION_USAGE " [-x width] "
"[-y height] [command]",
.tflag = CMD_SESSION_CANFAIL,
.target = { 't', CMD_FIND_SESSION, CMD_FIND_CANFAIL },
.flags = CMD_STARTSERVER,
.exec = cmd_new_session_exec
@ -57,7 +57,7 @@ const struct cmd_entry cmd_has_session_entry = {
.args = { "t:", 0, 0 },
.usage = CMD_TARGET_SESSION_USAGE,
.tflag = CMD_SESSION,
.target = { 't', CMD_FIND_SESSION, 0 },
.flags = 0,
.exec = cmd_new_session_exec
@ -74,17 +74,18 @@ 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;
if (self->entry == &cmd_has_session_entry) {
/*
* cmd_prepare() will fail if the session cannot be found,
* hence always return success here.
* cmd_find_target() will fail if the session cannot be found,
* so always return success here.
*/
return (CMD_RETURN_NORMAL);
}
@ -102,16 +103,9 @@ cmd_new_session_exec(struct cmd *self, struct cmdq_item *item)
}
if ((as = session_find(newname)) != NULL) {
if (args_has(args, 'A')) {
/*
* This item is now destined for
* attach-session. Because attach-session will
* have already been prepared, copy this
* session into its tflag so it can be used.
*/
cmd_find_from_session(&item->state.tflag, as);
return (cmd_attach_session(item,
args_has(args, 'D'), 0, NULL,
args_has(args, 'E')));
newname, args_has(args, 'D'),
0, NULL, args_has(args, 'E')));
}
cmdq_error(item, "duplicate session: %s", newname);
return (CMD_RETURN_ERROR);
@ -121,7 +115,7 @@ cmd_new_session_exec(struct cmd *self, struct cmdq_item *item)
/* Is this going to be part of a session group? */
group = args_get(args, 't');
if (group != NULL) {
groupwith = item->state.tflag.s;
groupwith = item->target.s;
if (groupwith == NULL) {
if (!session_check_name(group)) {
cmdq_error(item, "bad group name: %s", group);
@ -146,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;
@ -155,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
@ -192,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)
@ -297,7 +296,7 @@ cmd_new_session_exec(struct cmd *self, struct cmdq_item *item)
} else if (c->session != NULL)
c->last_session = c->session;
c->session = s;
if (!item->repeat)
if (~item->shared->flags & CMDQ_SHARED_REPEAT)
server_client_set_key_table(c, NULL);
status_timer_start(c);
notify_client("client-session-changed", c);
@ -324,19 +323,18 @@ cmd_new_session_exec(struct cmd *self, struct cmdq_item *item)
free(cp);
}
if (!detached)
if (!detached) {
c->flags |= CLIENT_ATTACHED;
cmd_find_from_session(&item->shared->current, s, 0);
}
if (to_free != NULL)
free(__UNCONST(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(__UNCONST(to_free));
free(to_free);
return (CMD_RETURN_ERROR);
}

View File

@ -42,7 +42,7 @@ const struct cmd_entry cmd_new_window_entry = {
.usage = "[-adkP] [-c start-directory] [-F format] [-n window-name] "
CMD_TARGET_WINDOW_USAGE " [command]",
.tflag = CMD_WINDOW_INDEX,
.target = { 't', CMD_FIND_WINDOW, CMD_FIND_WINDOW_INDEX },
.flags = 0,
.exec = cmd_new_window_exec
@ -52,12 +52,13 @@ static enum cmd_retval
cmd_new_window_exec(struct cmd *self, struct cmdq_item *item)
{
struct args *args = self->args;
struct session *s = item->state.tflag.s;
struct winlink *wl = item->state.tflag.wl;
struct client *c = item->state.c;
int idx = item->state.tflag.idx;
const char *cmd, *path, *template, *cwd, *to_free;
char **argv, *cause, *cp;
struct cmd_find_state *current = &item->shared->current;
struct session *s = item->target.s;
struct winlink *wl = item->target.wl;
struct client *c = cmd_find_client(item, NULL, 1);
int idx = item->target.idx;
const char *cmd, *path, *template, *cwd;
char **argv, *cause, *cp, *to_free = NULL;
int argc, detached;
struct environ_entry *envent;
struct cmd_find_state fs;
@ -92,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
@ -132,6 +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, 0);
server_redraw_session_group(s);
} else
server_status_session_group(s);
@ -144,16 +146,13 @@ cmd_new_window_exec(struct cmd *self, struct cmdq_item *item)
free(cp);
}
if (to_free != NULL)
free(__UNCONST(to_free));
cmd_find_from_winlink(&fs, s, 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(__UNCONST(to_free));
free(to_free);
return (CMD_RETURN_ERROR);
}

View File

@ -37,7 +37,7 @@ const struct cmd_entry cmd_paste_buffer_entry = {
.usage = "[-dpr] [-s separator] " CMD_BUFFER_USAGE " "
CMD_TARGET_PANE_USAGE,
.tflag = CMD_PANE,
.target = { 't', CMD_FIND_PANE, 0 },
.flags = CMD_AFTERHOOK,
.exec = cmd_paste_buffer_exec
@ -47,7 +47,7 @@ static enum cmd_retval
cmd_paste_buffer_exec(struct cmd *self, struct cmdq_item *item)
{
struct args *args = self->args;
struct window_pane *wp = item->state.tflag.wp;
struct window_pane *wp = item->target.wp;
struct paste_buffer *pb;
const char *sepstr, *bufname, *bufdata, *bufend, *line;
size_t seplen, bufsize;

View File

@ -102,8 +102,11 @@ cmdq_insert_after(struct cmdq_item *after, struct cmdq_item *item)
static void
cmdq_remove(struct cmdq_item *item)
{
if (item->formats != NULL)
format_free(item->formats);
if (item->shared != NULL && --item->shared->references == 0) {
if (item->shared->formats != NULL)
format_free(item->shared->formats);
free(item->shared);
}
if (item->client != NULL)
server_client_unref(item->client);
@ -150,6 +153,15 @@ cmdq_get_command(struct cmd_list *cmdlist, struct cmd_find_state *current,
struct cmd *cmd;
u_int group = cmdq_next_group();
char *tmp;
struct cmdq_shared *shared;
shared = xcalloc(1, sizeof *shared);
if (current != NULL)
cmd_find_copy_state(&shared->current, current);
else
cmd_find_clear_state(&shared->current, 0);
if (m != NULL)
memcpy(&shared->mouse, m, sizeof shared->mouse);
TAILQ_FOREACH(cmd, &cmdlist->list, qentry) {
xasprintf(&tmp, "command[%s]", cmd->entry->name);
@ -161,13 +173,11 @@ cmdq_get_command(struct cmd_list *cmdlist, struct cmd_find_state *current,
item->group = group;
item->flags = flags;
item->shared = shared;
item->cmdlist = cmdlist;
item->cmd = cmd;
if (current != NULL)
cmd_find_copy_state(&item->current, current);
if (m != NULL)
memcpy(&item->mouse, m, sizeof item->mouse);
shared->references++;
cmdlist->references++;
if (first == NULL)
@ -179,41 +189,63 @@ cmdq_get_command(struct cmd_list *cmdlist, struct cmd_find_state *current,
return (first);
}
/* Fill in flag for a command. */
static enum cmd_retval
cmdq_find_flag(struct cmdq_item *item, struct cmd_find_state *fs,
const struct cmd_entry_flag *flag)
{
const char *value;
if (flag->flag == 0) {
cmd_find_clear_state(fs, 0);
return (CMD_RETURN_NORMAL);
}
value = args_get(item->cmd->args, flag->flag);
if (cmd_find_target(fs, item, value, flag->type, flag->flags) != 0) {
cmd_find_clear_state(fs, 0);
return (CMD_RETURN_ERROR);
}
return (CMD_RETURN_NORMAL);
}
/* Fire command on command queue. */
static enum cmd_retval
cmdq_fire_command(struct cmdq_item *item)
{
struct client *c = item->client;
struct cmd *cmd = item->cmd;
const struct cmd_entry *entry = cmd->entry;
enum cmd_retval retval;
const char *name;
struct cmd_find_state *fsp, fs;
int flags;
flags = !!(cmd->flags & CMD_CONTROL);
cmdq_guard(item, "begin", flags);
if (cmd_prepare_state(cmd, item) != 0) {
retval = CMD_RETURN_ERROR;
goto out;
}
if (item->client == NULL)
item->client = cmd_find_client(item, NULL, CMD_FIND_QUIET);
retval = cmd->entry->exec(cmd, item);
item->client = cmd_find_client(item, NULL, 1);
retval = cmdq_find_flag(item, &item->source, &entry->source);
if (retval == CMD_RETURN_ERROR)
goto out;
retval = cmdq_find_flag(item, &item->target, &entry->target);
if (retval == CMD_RETURN_ERROR)
goto out;
if (cmd->entry->flags & CMD_AFTERHOOK) {
name = cmd->entry->name;
if (cmd_find_valid_state(&item->state.tflag))
fsp = &item->state.tflag;
else {
if (cmd_find_current(&fs, item, CMD_FIND_QUIET) != 0)
goto out;
retval = entry->exec(cmd, item);
if (retval == CMD_RETURN_ERROR)
goto out;
if (entry->flags & CMD_AFTERHOOK) {
if (cmd_find_valid_state(&item->target))
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) == 0)
fsp = &fs;
}
hooks_insert(fsp->s->hooks, item, fsp, "after-%s", name);
else
goto out;
hooks_insert(fsp->s->hooks, item, fsp, "after-%s", entry->name);
}
out:
@ -258,19 +290,17 @@ cmdq_fire_callback(struct cmdq_item *item)
void
cmdq_format(struct cmdq_item *item, const char *key, const char *fmt, ...)
{
struct cmdq_shared *shared = item->shared;
va_list ap;
struct cmdq_item *loop;
char *value;
va_start(ap, fmt);
xvasprintf(&value, fmt, ap);
va_end(ap);
for (loop = item; loop != NULL; loop = item->next) {
if (loop->formats == NULL)
loop->formats = format_create(NULL, FORMAT_NONE, 0);
format_add(loop->formats, key, "%s", value);
}
if (shared->formats == NULL)
shared->formats = format_create(NULL, NULL, FORMAT_NONE, 0);
format_add(shared->formats, key, "%s", value);
free(value);
}
@ -319,8 +349,7 @@ cmdq_next(struct client *c)
item->time = time(NULL);
item->number = ++number;
switch (item->type)
{
switch (item->type) {
case CMDQ_COMMAND:
retval = cmdq_fire_command(item);
@ -399,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);
@ -423,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

@ -36,7 +36,7 @@ const struct cmd_entry cmd_send_keys_entry = {
.args = { "lXRMN:t:", 0, -1 },
.usage = "[-lXRM] [-N repeat-count] " CMD_TARGET_PANE_USAGE " key ...",
.tflag = CMD_PANE,
.target = { 't', CMD_FIND_PANE, 0 },
.flags = CMD_AFTERHOOK,
.exec = cmd_send_keys_exec
@ -49,20 +49,45 @@ const struct cmd_entry cmd_send_prefix_entry = {
.args = { "2t:", 0, 0 },
.usage = "[-2] " CMD_TARGET_PANE_USAGE,
.tflag = CMD_PANE,
.target = { 't', CMD_FIND_PANE, 0 },
.flags = CMD_AFTERHOOK,
.exec = cmd_send_keys_exec
};
static void
cmd_send_keys_inject(struct client *c, struct cmdq_item *item, key_code key)
{
struct window_pane *wp = item->target.wp;
struct session *s = item->target.s;
struct key_table *table;
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;
}
table = key_bindings_get_table(wp->mode->key_table(wp), 1);
bd_find.key = (key & ~KEYC_XTERM);
bd = RB_FIND(key_bindings, &table->key_bindings, &bd_find);
if (bd != NULL) {
table->references++;
key_bindings_dispatch(bd, item, c, NULL, &item->target);
key_bindings_unref_table(table);
}
}
static enum cmd_retval
cmd_send_keys_exec(struct cmd *self, struct cmdq_item *item)
{
struct args *args = self->args;
struct client *c = item->state.c;
struct window_pane *wp = item->state.tflag.wp;
struct session *s = item->state.tflag.s;
struct mouse_event *m = &item->mouse;
struct client *c = cmd_find_client(item, NULL, 1);
struct window_pane *wp = item->target.wp;
struct session *s = item->target.s;
struct mouse_event *m = &item->shared->mouse;
struct utf8_data *ud, *uc;
wchar_t wc;
int i, literal;
@ -108,7 +133,7 @@ cmd_send_keys_exec(struct cmd *self, struct cmdq_item *item)
key = options_get_number(s->options, "prefix2");
else
key = options_get_number(s->options, "prefix");
window_pane_key(wp, NULL, s, key, NULL);
cmd_send_keys_inject(c, item, key);
return (CMD_RETURN_NORMAL);
}
@ -123,7 +148,7 @@ cmd_send_keys_exec(struct cmd *self, struct cmdq_item *item)
if (!literal) {
key = key_string_lookup_string(args->argv[i]);
if (key != KEYC_NONE && key != KEYC_UNKNOWN)
window_pane_key(wp, NULL, s, key, NULL);
cmd_send_keys_inject(c, item, key);
else
literal = 1;
}
@ -132,7 +157,7 @@ cmd_send_keys_exec(struct cmd *self, struct cmdq_item *item)
for (uc = ud; uc->size != 0; uc++) {
if (utf8_combine(uc, &wc) != UTF8_DONE)
continue;
window_pane_key(wp, NULL, s, wc, NULL);
cmd_send_keys_inject(c, item, wc);
}
free(ud);
}

View File

@ -38,8 +38,6 @@ const struct cmd_entry cmd_show_messages_entry = {
.args = { "JTt:", 0, 0 },
.usage = "[-JT] " CMD_TARGET_CLIENT_USAGE,
.tflag = CMD_CLIENT,
.flags = CMD_AFTERHOOK,
.exec = cmd_show_messages_exec
};
@ -75,7 +73,7 @@ cmd_show_messages_jobs(struct cmdq_item *item, int blank)
u_int n;
n = 0;
LIST_FOREACH(job, &all_jobs, lentry) {
LIST_FOREACH(job, &all_jobs, entry) {
if (blank) {
cmdq_print(item, "%s", "");
blank = 0;
@ -91,11 +89,14 @@ static enum cmd_retval
cmd_show_messages_exec(struct cmd *self, struct cmdq_item *item)
{
struct args *args = self->args;
struct client *c = item->state.c;
struct client *c;
struct message_entry *msg;
char *tim;
int done, blank;
if ((c = cmd_find_client(item, args_get(args, 't'), 0)) == NULL)
return (CMD_RETURN_ERROR);
done = blank = 0;
if (args_has(args, 'T')) {
blank = cmd_show_messages_terminals(item, blank);

View File

@ -43,7 +43,7 @@ const struct cmd_entry cmd_split_window_entry = {
.usage = "[-bdfhvP] [-c start-directory] [-F format] "
"[-p percentage|-l size] " CMD_TARGET_PANE_USAGE " [command]",
.tflag = CMD_PANE,
.target = { 't', CMD_FIND_PANE, 0 },
.flags = 0,
.exec = cmd_split_window_exec
@ -52,15 +52,16 @@ const struct cmd_entry cmd_split_window_entry = {
static enum cmd_retval
cmd_split_window_exec(struct cmd *self, struct cmdq_item *item)
{
struct cmd_find_state *current = &item->shared->current;
struct args *args = self->args;
struct client *c = item->state.c;
struct session *s = item->state.tflag.s;
struct winlink *wl = item->state.tflag.wl;
struct client *c = cmd_find_client(item, NULL, 1);
struct session *s = item->target.s;
struct winlink *wl = item->target.wl;
struct window *w = wl->window;
struct window_pane *wp = item->state.tflag.wp, *new_wp = NULL;
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;
@ -84,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
@ -132,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)
@ -142,7 +143,7 @@ cmd_split_window_exec(struct cmd *self, struct cmdq_item *item)
if (envent != NULL)
path = envent->value;
env = environ_for_session(s);
env = environ_for_session(s, 0);
if (window_pane_spawn(new_wp, argc, argv, path, shell, cwd, env,
s->tio, &cause) != 0) {
environ_free(env);
@ -150,11 +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, 0);
server_redraw_session(s);
} else
server_status_session(s);
@ -168,17 +171,10 @@ cmd_split_window_exec(struct cmd *self, struct cmdq_item *item)
}
notify_window("window-layout-changed", w);
if (to_free != NULL)
free(__UNCONST(to_free));
cmd_find_clear_state(&fs, NULL, 0);
fs.s = s;
fs.wl = wl;
fs.w = w;
fs.wp = new_wp;
cmd_find_log_state(__func__, &fs);
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:
@ -189,7 +185,6 @@ error:
cmdq_error(item, "create pane failed: %s", cause);
free(cause);
if (to_free != NULL)
free(__UNCONST(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

@ -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,11 +224,13 @@ environ_log(struct environ *env, const char *prefix)
envent->value);
}
}
free(prefix);
}
/* Create initial environment for new child. */
struct environ *
environ_for_session(struct session *s)
environ_for_session(struct session *s, int no_TERM)
{
struct environ *env;
const char *value;
@ -233,8 +241,10 @@ environ_for_session(struct session *s)
if (s != NULL)
environ_copy(s->environ, env);
value = options_get_string(global_options, "default-terminal");
environ_set(env, "TERM", "%s", value);
if (!no_TERM) {
value = options_get_string(global_options, "default-terminal");
environ_set(env, "TERM", "%s", value);
}
if (s != NULL)
idx = s->id;

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>
@ -40,32 +38,9 @@
struct format_entry;
typedef void (*format_cb)(struct format_tree *, struct format_entry *);
static void format_job_callback(struct job *);
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 *,
@ -76,17 +51,19 @@ static int format_replace(struct format_tree *, const char *, size_t,
static void format_defaults_session(struct format_tree *,
struct session *);
static void format_defaults_client(struct format_tree *, struct client *);
static void format_defaults_winlink(struct format_tree *, struct session *,
static void format_defaults_winlink(struct format_tree *,
struct winlink *);
/* Entry in format job tree. */
struct format_job {
struct client *client;
u_int tag;
const char *cmd;
const char *expanded;
time_t last;
char *out;
int updated;
struct job *job;
int status;
@ -129,9 +106,11 @@ struct format_entry {
/* Format entry tree. */
struct format_tree {
struct window *w;
struct winlink *wl;
struct session *s;
struct window_pane *wp;
struct client *client;
u_int tag;
int flags;
@ -207,17 +186,40 @@ static const char *format_lower[] = {
NULL /* z */
};
/* Format job callback. */
/* Format job update callback. */
static void
format_job_callback(struct job *job)
format_job_update(struct job *job)
{
struct format_job *fj = job->data;
char *line;
time_t t;
if ((line = evbuffer_readline(job->event->input)) == NULL)
return;
fj->updated = 1;
free(fj->out);
fj->out = line;
log_debug("%s: %p %s: %s", __func__, fj, fj->cmd, fj->out);
t = time(NULL);
if (fj->status && fj->last != t) {
if (fj->client != NULL)
server_status_client(fj->client);
fj->last = t;
}
}
/* Format job complete callback. */
static void
format_job_complete(struct job *job)
{
struct format_job *fj = job->data;
char *line, *buf;
size_t len;
struct client *c;
fj->job = NULL;
free(fj->out);
buf = NULL;
if ((line = evbuffer_readline(job->event->input)) == NULL) {
@ -228,37 +230,53 @@ format_job_callback(struct job *job)
buf[len] = '\0';
} else
buf = line;
fj->out = buf;
log_debug("%s: %p %s: %s", __func__, fj, fj->cmd, buf);
if (*buf != '\0' || !fj->updated) {
free(fj->out);
fj->out = buf;
} else
free(buf);
if (fj->status) {
TAILQ_FOREACH(c, &clients, entry)
server_status_client(c);
if (fj->client != NULL)
server_status_client(fj->client);
fj->status = 0;
}
log_debug("%s: %s: %s", __func__, fj->cmd, fj->out);
}
/* Find a job. */
static char *
format_job_get(struct format_tree *ft, const char *cmd)
{
struct format_job_tree *jobs;
struct format_job fj0, *fj;
time_t t;
char *expanded;
int force;
if (ft->client == NULL)
jobs = &format_jobs;
else if (ft->client->jobs != NULL)
jobs = ft->client->jobs;
else {
jobs = ft->client->jobs = xmalloc(sizeof *ft->client->jobs);
RB_INIT(jobs);
}
fj0.tag = ft->tag;
fj0.cmd = cmd;
if ((fj = RB_FIND(format_job_tree, &format_jobs, &fj0)) == NULL) {
if ((fj = RB_FIND(format_job_tree, jobs, &fj0)) == NULL) {
fj = xcalloc(1, sizeof *fj);
fj->client = ft->client;
fj->tag = ft->tag;
fj->cmd = xstrdup(cmd);
fj->expanded = NULL;
xasprintf(&fj->out, "<'%s' not ready>", fj->cmd);
RB_INSERT(format_job_tree, &format_jobs, fj);
RB_INSERT(format_job_tree, jobs, fj);
}
expanded = format_expand(ft, cmd);
@ -271,13 +289,14 @@ format_job_get(struct format_tree *ft, const char *cmd)
t = time(NULL);
if (fj->job == NULL && (force || fj->last != t)) {
fj->job = job_run(expanded, NULL, NULL, format_job_callback,
NULL, fj);
fj->job = job_run(expanded, NULL, NULL, format_job_update,
format_job_complete, NULL, fj);
if (fj->job == NULL) {
free(fj->out);
xasprintf(&fj->out, "<'%s' didn't start>", fj->cmd);
}
fj->last = t;
fj->updated = 0;
}
if (ft->flags & FORMAT_STATUS)
@ -289,17 +308,16 @@ format_job_get(struct format_tree *ft, const char *cmd)
/* Remove old jobs. */
static void
format_job_timer(__unused int fd, __unused short events, __unused void *arg)
format_job_tidy(struct format_job_tree *jobs, int force)
{
struct format_job *fj, *fj1;
time_t now;
struct timeval tv = { .tv_sec = 60 };
now = time(NULL);
RB_FOREACH_SAFE(fj, format_job_tree, &format_jobs, fj1) {
if (fj->last > now || now - fj->last < 3600)
RB_FOREACH_SAFE(fj, format_job_tree, jobs, fj1) {
if (!force && (fj->last > now || now - fj->last < 3600))
continue;
RB_REMOVE(format_job_tree, &format_jobs, fj);
RB_REMOVE(format_job_tree, jobs, fj);
log_debug("%s: %s", __func__, fj->cmd);
@ -312,6 +330,29 @@ format_job_timer(__unused int fd, __unused short events, __unused void *arg)
free(fj);
}
}
/* Remove old jobs for client. */
void
format_lost_client(struct client *c)
{
if (c->jobs != NULL)
format_job_tidy(c->jobs, 1);
free(c->jobs);
}
/* Remove old jobs periodically. */
static void
format_job_timer(__unused int fd, __unused short events, __unused void *arg)
{
struct client *c;
struct timeval tv = { .tv_sec = 60 };
format_job_tidy(&format_jobs, 0);
TAILQ_FOREACH(c, &clients, entry) {
if (c->jobs != NULL)
format_job_tidy(c->jobs, 0);
}
evtimer_del(&format_job_event);
evtimer_add(&format_job_event, &tv);
@ -357,7 +398,7 @@ format_cb_session_alerts(struct format_tree *ft, struct format_entry *fe)
{
struct session *s = ft->s;
struct winlink *wl;
char alerts[256], tmp[16];
char alerts[1024], tmp[16];
if (s == NULL)
return;
@ -381,6 +422,48 @@ format_cb_session_alerts(struct format_tree *ft, struct format_entry *fe)
fe->value = xstrdup(alerts);
}
/* Callback for session_stack. */
static void
format_cb_session_stack(struct format_tree *ft, struct format_entry *fe)
{
struct session *s = ft->s;
struct winlink *wl;
char result[1024], tmp[16];
if (s == NULL)
return;
xsnprintf(result, sizeof result, "%u", s->curw->idx);
TAILQ_FOREACH(wl, &s->lastw, sentry) {
xsnprintf(tmp, sizeof tmp, "%u", wl->idx);
if (*result != '\0')
strlcat(result, ",", sizeof result);
strlcat(result, tmp, sizeof result);
}
fe->value = xstrdup(result);
}
/* Callback for window_stack_index. */
static void
format_cb_window_stack_index(struct format_tree *ft, struct format_entry *fe)
{
struct session *s = ft->wl->session;
struct winlink *wl;
u_int idx;
idx = 0;
TAILQ_FOREACH(wl, &s->lastw, sentry) {
idx++;
if (wl == ft->wl)
break;
}
if (wl != NULL)
xasprintf(&fe->value, "%u", idx);
else
fe->value = xstrdup("0");
}
/* Callback for window_layout. */
static void
format_cb_window_layout(struct format_tree *ft, struct format_entry *fe)
@ -444,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;
@ -523,7 +606,7 @@ format_merge(struct format_tree *ft, struct format_tree *from)
/* Create a new tree. */
struct format_tree *
format_create(struct cmdq_item *item, int tag, int flags)
format_create(struct client *c, struct cmdq_item *item, int tag, int flags)
{
struct format_tree *ft;
@ -535,6 +618,11 @@ format_create(struct cmdq_item *item, int tag, int flags)
ft = xcalloc(1, sizeof *ft);
RB_INIT(&ft->tree);
if (c != NULL) {
ft->client = c;
ft->client->references++;
}
ft->tag = tag;
ft->flags = flags;
@ -545,10 +633,12 @@ format_create(struct cmdq_item *item, int tag, int flags)
format_add(ft, "socket_path", "%s", socket_path);
format_add_tv(ft, "start_time", &start_time);
if (item != NULL && item->cmd != NULL)
format_add(ft, "command", "%s", item->cmd->entry->name);
if (item != NULL && item->formats != NULL)
format_merge(ft, item->formats);
if (item != NULL) {
if (item->cmd != NULL)
format_add(ft, "command", "%s", item->cmd->entry->name);
if (item->shared != NULL && item->shared->formats != NULL)
format_merge(ft, item->shared->formats);
}
return (ft);
}
@ -566,6 +656,8 @@ format_free(struct format_tree *ft)
free(fe);
}
if (ft->client != NULL)
server_client_unref(ft->client);
free(ft);
}
@ -762,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'))
@ -770,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);
@ -791,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;
@ -856,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);
@ -1088,7 +1216,10 @@ format_single(struct cmdq_item *item, const char *fmt, struct client *c,
struct format_tree *ft;
char *expanded;
ft = format_create(item, FORMAT_NONE, 0);
if (item != NULL)
ft = format_create(item->client, item, FORMAT_NONE, 0);
else
ft = format_create(NULL, item, FORMAT_NONE, 0);
format_defaults(ft, c, s, wl, wp);
expanded = format_expand(ft, fmt);
@ -1101,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)
@ -1112,8 +1247,8 @@ format_defaults(struct format_tree *ft, struct client *c, struct session *s,
format_defaults_client(ft, c);
if (s != NULL)
format_defaults_session(ft, s);
if (s != NULL && wl != NULL)
format_defaults_winlink(ft, s, wl);
if (wl != NULL)
format_defaults_winlink(ft, wl);
if (wp != NULL)
format_defaults_pane(ft, wp);
}
@ -1145,6 +1280,7 @@ format_defaults_session(struct format_tree *ft, struct session *s)
format_add(ft, "session_many_attached", "%d", s->attached > 1);
format_add_cb(ft, "session_alerts", format_cb_session_alerts);
format_add_cb(ft, "session_stack", format_cb_session_stack);
}
/* Set default format keys for a client. */
@ -1174,7 +1310,9 @@ format_defaults_client(struct format_tree *ft, struct client *c)
format_add_tv(ft, "client_created", &c->creation_time);
format_add_tv(ft, "client_activity", &c->activity_time);
format_add(ft, "client_written", "%zu", tty->written);
format_add(ft, "client_written", "%zu", c->written);
format_add(ft, "client_discarded", "%zu", c->discarded);
name = server_client_get_key_table(c);
if (strcmp(c->keytable->name, name) == 0)
@ -1222,21 +1360,20 @@ format_defaults_window(struct format_tree *ft, struct window *w)
/* Set default format keys for a winlink. */
static void
format_defaults_winlink(struct format_tree *ft, struct session *s,
struct winlink *wl)
format_defaults_winlink(struct format_tree *ft, struct winlink *wl)
{
struct session *s = wl->session;
struct window *w = wl->window;
char *flags;
if (ft->w == NULL)
ft->w = wl->window;
flags = window_printable_flags(s, wl);
ft->wl = wl;
format_defaults_window(ft, w);
format_add(ft, "window_index", "%d", wl->idx);
format_add(ft, "window_flags", "%s", flags);
format_add_cb(ft, "window_stack_index", format_cb_window_stack_index);
format_add(ft, "window_flags", "%s", window_printable_flags(wl));
format_add(ft, "window_active", "%d", wl == s->curw);
format_add(ft, "window_bell_flag", "%d",
@ -1248,8 +1385,6 @@ format_defaults_winlink(struct format_tree *ft, struct session *s,
format_add(ft, "window_last_flag", "%d",
!!(wl == TAILQ_FIRST(&s->lastw)));
format_add(ft, "window_linked", "%d", session_is_linked(s, wl->window));
free(flags);
}
/* Set default format keys for a window pane. */
@ -1258,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;
@ -1278,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))
@ -1289,11 +1425,20 @@ 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);
if (wp->mode != NULL)
format_add(ft, "pane_mode", "%s", wp->mode->name);
format_add(ft, "pane_synchronized", "%d",
!!options_get_number(wp->window->options, "synchronize-panes"));
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);
@ -1306,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);
@ -1341,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

@ -89,11 +89,53 @@ grid_need_extended_cell(const struct grid_cell_entry *gce,
return (1);
if (gc->data.size != 1 || gc->data.width != 1)
return (1);
if ((gc->fg & COLOUR_FLAG_RGB) ||(gc->bg & COLOUR_FLAG_RGB))
if ((gc->fg & COLOUR_FLAG_RGB) || (gc->bg & COLOUR_FLAG_RGB))
return (1);
return (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++;
@ -472,7 +537,7 @@ grid_clear(struct grid *gd, u_int px, u_int py, u_int nx, u_int ny, u_int bg)
gd->linedata[yy].cellsize = px;
continue;
}
grid_expand_line(gd, yy, px + nx, bg);
grid_expand_line(gd, yy, px + nx, 8); /* default bg first */
for (xx = px; xx < px + nx; xx++)
grid_clear_cell(gd, xx, yy, bg);
}
@ -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,
@ -675,8 +739,7 @@ grid_string_cells_code(const struct grid_cell *lastgc,
{
int oldc[64], newc[64], s[128];
size_t noldc, nnewc, n, i;
u_int attr = gc->attr;
u_int lastattr = lastgc->attr;
u_int attr = gc->attr, lastattr = lastgc->attr;
char tmp[64];
struct {
@ -708,23 +771,7 @@ grid_string_cells_code(const struct grid_cell *lastgc,
s[n++] = attrs[i].code;
}
/* If the foreground colour changed, append its parameters. */
nnewc = grid_string_cells_fg(gc, newc);
noldc = grid_string_cells_fg(lastgc, oldc);
if (nnewc != noldc || memcmp(newc, oldc, nnewc * sizeof newc[0]) != 0) {
for (i = 0; i < nnewc; i++)
s[n++] = newc[i];
}
/* If the background colour changed, append its parameters. */
nnewc = grid_string_cells_bg(gc, newc);
noldc = grid_string_cells_bg(lastgc, oldc);
if (nnewc != noldc || memcmp(newc, oldc, nnewc * sizeof newc[0]) != 0) {
for (i = 0; i < nnewc; i++)
s[n++] = newc[i];
}
/* If there are any parameters, append an SGR code. */
/* Write the attributes. */
*buf = '\0';
if (n > 0) {
if (escape_c0)
@ -741,16 +788,56 @@ grid_string_cells_code(const struct grid_cell *lastgc,
strlcat(buf, "m", len);
}
/* If the foreground colour changed, write its parameters. */
nnewc = grid_string_cells_fg(gc, newc);
noldc = grid_string_cells_fg(lastgc, oldc);
if (nnewc != noldc ||
memcmp(newc, oldc, nnewc * sizeof newc[0]) != 0 ||
(n != 0 && s[0] == 0)) {
if (escape_c0)
strlcat(buf, "\\033[", len);
else
strlcat(buf, "\033[", len);
for (i = 0; i < nnewc; i++) {
if (i + 1 < nnewc)
xsnprintf(tmp, sizeof tmp, "%d;", newc[i]);
else
xsnprintf(tmp, sizeof tmp, "%d", newc[i]);
strlcat(buf, tmp, len);
}
strlcat(buf, "m", len);
}
/* If the background colour changed, append its parameters. */
nnewc = grid_string_cells_bg(gc, newc);
noldc = grid_string_cells_bg(lastgc, oldc);
if (nnewc != noldc ||
memcmp(newc, oldc, nnewc * sizeof newc[0]) != 0 ||
(n != 0 && s[0] == 0)) {
if (escape_c0)
strlcat(buf, "\\033[", len);
else
strlcat(buf, "\033[", len);
for (i = 0; i < nnewc; i++) {
if (i + 1 < nnewc)
xsnprintf(tmp, sizeof tmp, "%d;", newc[i]);
else
xsnprintf(tmp, sizeof tmp, "%d", newc[i]);
strlcat(buf, tmp, len);
}
strlcat(buf, "m", len);
}
/* Append shift in/shift out if needed. */
if ((attr & GRID_ATTR_CHARSET) && !(lastattr & GRID_ATTR_CHARSET)) {
if (escape_c0)
strlcat(buf, "\\016", len); /* SO */
strlcat(buf, "\\016", len); /* SO */
else
strlcat(buf, "\016", len); /* SO */
}
if (!(attr & GRID_ATTR_CHARSET) && (lastattr & GRID_ATTR_CHARSET)) {
if (escape_c0)
strlcat(buf, "\\017", len); /* SI */
strlcat(buf, "\\017", len); /* SI */
else
strlcat(buf, "\017", len); /* SI */
}
@ -824,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,
@ -839,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);
@ -200,6 +204,7 @@ input_key(struct window_pane *wp, key_code key, struct mouse_event *m)
return;
}
}
key &= ~KEYC_XTERM;
/* Otherwise look the key up in the table. */
for (i = 0; i < nitems(input_keys); i++) {

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((const char *)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)
@ -1308,7 +1345,8 @@ input_csi_dispatch(struct input_ctx *ictx)
}
break;
case INPUT_CSI_ECH:
screen_write_clearcharacter(sctx, input_get(ictx, 0, 1, 1));
screen_write_clearcharacter(sctx, input_get(ictx, 0, 1, 1),
ictx->cell.cell.bg);
break;
case INPUT_CSI_DCH:
screen_write_deletecharacter(sctx, input_get(ictx, 0, 1, 1),
@ -1391,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);
@ -1416,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)) {
@ -1442,6 +1490,7 @@ input_csi_dispatch(struct input_ctx *ictx)
break;
}
ictx->last = -1;
return (0);
}
@ -1838,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)
@ -1867,13 +1927,15 @@ 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. */
static void
input_exit_osc(struct input_ctx *ictx)
{
u_char *p = ictx->input_buf;
char *p = (char *)ictx->input_buf;
u_int option;
if (ictx->flags & INPUT_DISCARD)
@ -1892,18 +1954,26 @@ input_exit_osc(struct input_ctx *ictx)
switch (option) {
case 0:
case 2:
screen_set_title(ictx->ctx.s, (const char *)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, (char *)p);
break;
case 52:
input_osc_52(ictx->wp, (char *)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 */
screen_set_cursor_colour(ictx->ctx.s, (const char *)p);
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, (char *)p);
@ -1925,17 +1995,22 @@ 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. */
static void
input_exit_apc(struct input_ctx *ictx)
{
char *p = (char *)ictx->input_buf;
if (ictx->flags & INPUT_DISCARD)
return;
log_debug("%s: \"%s\"", __func__, ictx->input_buf);
log_debug("%s: \"%s\"", __func__, p);
screen_set_title(ictx->ctx.s, (const char *)ictx->input_buf);
if (!utf8_isvalid(p))
return;
screen_set_title(ictx->ctx.s, p);
server_status_window(ictx->wp->window);
}
@ -1946,21 +2021,25 @@ 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. */
static void
input_exit_rename(struct input_ctx *ictx)
{
char *p = (char *)ictx->input_buf;
if (ictx->flags & INPUT_DISCARD)
return;
if (!options_get_number(ictx->wp->window->options, "allow-rename"))
return;
log_debug("%s: \"%s\"", __func__, ictx->input_buf);
log_debug("%s: \"%s\"", __func__, p);
window_set_name(ictx->wp->window, (const char *)ictx->input_buf);
if (!utf8_isvalid(p))
return;
window_set_name(ictx->wp->window, p);
options_set_number(ictx->wp->window->options, "automatic-rename", 0);
server_status_window(ictx->wp->window);
}
@ -1974,6 +2053,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);
}
@ -2011,7 +2091,7 @@ input_utf8_close(struct input_ctx *ictx)
(int)ud->size, ud->data, ud->width);
utf8_copy(&ictx->cell.cell.data, ud);
screen_write_cell(&ictx->ctx, &ictx->cell.cell);
screen_write_collect_add(&ictx->ctx, &ictx->cell.cell);
return (0);
}
@ -2050,6 +2130,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)
@ -2057,9 +2173,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++;
@ -2076,11 +2196,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((char *)out, outlen);
}

View File

@ -32,8 +32,9 @@
* output.
*/
static void job_callback(struct bufferevent *, short, void *);
static void job_read_callback(struct bufferevent *, void *);
static void job_write_callback(struct bufferevent *, void *);
static void job_error_callback(struct bufferevent *, short, void *);
/* All jobs list. */
struct joblist all_jobs = LIST_HEAD_INITIALIZER(all_jobs);
@ -41,26 +42,37 @@ struct joblist all_jobs = LIST_HEAD_INITIALIZER(all_jobs);
/* Start a job running, if it isn't already. */
struct job *
job_run(const char *cmd, struct session *s, const char *cwd,
void (*callbackfn)(struct job *), void (*freefn)(void *), void *data)
job_update_cb updatecb, job_complete_cb completecb, job_free_cb freecb,
void *data)
{
struct job *job;
struct environ *env;
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);
env = environ_for_session(s);
/*
* Do not set TERM during .tmux.conf, it is nice to be able to use
* if-shell to decide on default-terminal based on outside TERM.
*/
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)
@ -92,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]);
@ -103,17 +115,18 @@ job_run(const char *cmd, struct session *s, const char *cwd,
job->pid = pid;
job->status = 0;
LIST_INSERT_HEAD(&all_jobs, job, lentry);
LIST_INSERT_HEAD(&all_jobs, job, entry);
job->callbackfn = callbackfn;
job->freefn = freefn;
job->updatecb = updatecb;
job->completecb = completecb;
job->freecb = freecb;
job->data = data;
job->fd = out[0];
setblocking(job->fd, 0);
job->event = bufferevent_new(job->fd, NULL, job_write_callback,
job_callback, job);
job->event = bufferevent_new(job->fd, job_read_callback,
job_write_callback, job_error_callback, job);
bufferevent_enable(job->event, EV_READ|EV_WRITE);
log_debug("run job %p: %s, pid %ld", job, job->cmd, (long) job->pid);
@ -126,11 +139,11 @@ job_free(struct job *job)
{
log_debug("free job %p: %s", job, job->cmd);
LIST_REMOVE(job, lentry);
LIST_REMOVE(job, entry);
free(job->cmd);
if (job->freefn != NULL && job->data != NULL)
job->freefn(job->data);
if (job->freecb != NULL && job->data != NULL)
job->freecb(job->data);
if (job->pid != -1)
kill(job->pid, SIGTERM);
@ -142,7 +155,21 @@ job_free(struct job *job)
free(job);
}
/* Called when output buffer falls below low watermark (default is 0). */
/* Job buffer read callback. */
static void
job_read_callback(__unused struct bufferevent *bufev, void *data)
{
struct job *job = data;
if (job->updatecb != NULL)
job->updatecb(job);
}
/*
* Job buffer write callback. Fired when the buffer falls below watermark
* (default is empty). If all the data has been written, disable the write
* event.
*/
static void
job_write_callback(__unused struct bufferevent *bufev, void *data)
{
@ -160,7 +187,7 @@ job_write_callback(__unused struct bufferevent *bufev, void *data)
/* Job buffer error callback. */
static void
job_callback(__unused struct bufferevent *bufev, __unused short events,
job_error_callback(__unused struct bufferevent *bufev, __unused short events,
void *data)
{
struct job *job = data;
@ -168,8 +195,8 @@ job_callback(__unused struct bufferevent *bufev, __unused short events,
log_debug("job error %p: %s, pid %ld", job, job->cmd, (long) job->pid);
if (job->state == JOB_DEAD) {
if (job->callbackfn != NULL)
job->callbackfn(job);
if (job->completecb != NULL)
job->completecb(job);
job_free(job);
} else {
bufferevent_disable(job->event, EV_READ);
@ -186,8 +213,8 @@ job_died(struct job *job, int status)
job->status = status;
if (job->state == JOB_CLOSED) {
if (job->callbackfn != NULL)
job->callbackfn(job);
if (job->completecb != NULL)
job->completecb(job);
job_free(job);
} else {
job->pid = -1;

View File

@ -84,7 +84,7 @@ key_bindings_unref_table(struct key_table *table)
}
void
key_bindings_add(const char *name, key_code key, int can_repeat,
key_bindings_add(const char *name, key_code key, int repeat,
struct cmd_list *cmdlist)
{
struct key_table *table;
@ -92,7 +92,7 @@ key_bindings_add(const char *name, key_code key, int can_repeat,
table = key_bindings_get_table(name, 1);
bd_find.key = key;
bd_find.key = (key & ~KEYC_XTERM);
bd = RB_FIND(key_bindings, &table->key_bindings, &bd_find);
if (bd != NULL) {
RB_REMOVE(key_bindings, &table->key_bindings, bd);
@ -100,11 +100,12 @@ key_bindings_add(const char *name, key_code key, int can_repeat,
free(bd);
}
bd = xmalloc(sizeof *bd);
bd = xcalloc(1, sizeof *bd);
bd->key = key;
RB_INSERT(key_bindings, &table->key_bindings, bd);
bd->can_repeat = can_repeat;
if (repeat)
bd->flags |= KEY_BINDING_REPEAT;
bd->cmdlist = cmdlist;
}
@ -118,7 +119,7 @@ key_bindings_remove(const char *name, key_code key)
if (table == NULL)
return;
bd_find.key = key;
bd_find.key = (key & ~KEYC_XTERM);
bd = RB_FIND(key_bindings, &table->key_bindings, &bd_find);
if (bd == NULL)
return;
@ -137,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
@ -195,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",
@ -243,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",
@ -275,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",
@ -315,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",
@ -341,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",
@ -356,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",
@ -399,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;
@ -412,10 +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);
item->repeat = bd->can_repeat;
cmdq_append(c, item);
new_item = cmdq_get_command(bd->cmdlist, fs, m, 0);
if (bd->flags & KEY_BINDING_REPEAT)
new_item->shared->flags |= CMDQ_SHARED_REPEAT;
}
if (item != NULL)
cmdq_insert_after(item, new_item);
else
cmdq_append(c, new_item);
}

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)

View File

@ -110,8 +110,8 @@ mode_tree_free_item(struct mode_tree_item *mti)
{
mode_tree_free_items(&mti->children);
free((void *)mti->name);
free((void *)mti->text);
free(__UNCONST(mti->name));
free(__UNCONST(mti->text));
free(mti);
}

View File

@ -43,9 +43,9 @@ notify_hook(struct cmdq_item *item, struct notify_entry *ne)
struct session *s = ne->session;
struct window *w = ne->window;
cmd_find_clear_state(&fs, NULL, 0);
cmd_find_clear_state(&fs, 0);
if (cmd_find_empty_state(&ne->fs) || !cmd_find_valid_state(&ne->fs))
cmd_find_current(&fs, item, CMD_FIND_QUIET);
cmd_find_from_nothing(&fs, 0);
else
cmd_find_copy_state(&fs, &ne->fs);
@ -78,8 +78,12 @@ notify_callback(struct cmdq_item *item, void *data)
log_debug("%s: %s", __func__, ne->name);
if (strcmp(ne->name, "pane-mode-changed") == 0)
control_notify_pane_mode_changed(ne->pane);
if (strcmp(ne->name, "window-layout-changed") == 0)
control_notify_window_layout_changed(ne->window);
if (strcmp(ne->name, "window-pane-changed") == 0)
control_notify_window_pane_changed(ne->window);
if (strcmp(ne->name, "window-unlinked") == 0)
control_notify_window_unlinked(ne->session, ne->window);
if (strcmp(ne->name, "window-linked") == 0)
@ -94,18 +98,20 @@ notify_callback(struct cmdq_item *item, void *data)
control_notify_session_created(ne->session);
if (strcmp(ne->name, "session-closed") == 0)
control_notify_session_closed(ne->session);
if (strcmp(ne->name, "session-window-changed") == 0)
control_notify_session_window_changed(ne->session);
notify_hook(item, ne);
if (ne->client != NULL)
server_client_unref(ne->client);
if (ne->session != NULL)
session_unref(ne->session);
session_remove_ref(ne->session, __func__);
if (ne->window != NULL)
window_remove_ref(ne->window);
window_remove_ref(ne->window, __func__);
if (ne->fs.s != NULL)
session_unref(ne->fs.s);
session_remove_ref(ne->fs.s, __func__);
free(__UNCONST(ne->name));
free(ne);
@ -135,13 +141,13 @@ notify_add(const char *name, struct cmd_find_state *fs, struct client *c,
if (c != NULL)
c->references++;
if (s != NULL)
s->references++;
session_add_ref(s, __func__);
if (w != NULL)
w->references++;
window_add_ref(w, __func__);
cmd_find_copy_state(&ne->fs, fs);
if (ne->fs.s != NULL)
ne->fs.s->references++; /* cmd_find_valid_state need session */
if (ne->fs.s != NULL) /* cmd_find_valid_state needs session */
session_add_ref(ne->fs.s, __func__);
new_item = cmdq_get_callback(notify_callback, ne);
cmdq_append(NULL, new_item);
@ -163,10 +169,7 @@ notify_client(const char *name, struct client *c)
{
struct cmd_find_state fs;
if (c->session != NULL)
cmd_find_from_session(&fs, c->session);
else
cmd_find_current(&fs, NULL, CMD_FIND_QUIET);
cmd_find_from_client(&fs, c, 0);
notify_add(name, &fs, c, NULL, NULL, NULL);
}
@ -176,19 +179,19 @@ 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_current(&fs, NULL, CMD_FIND_QUIET);
cmd_find_from_nothing(&fs, 0);
notify_add(name, &fs, NULL, s, NULL, NULL);
}
void
notify_winlink(const char *name, struct session *s, struct winlink *wl)
notify_winlink(const char *name, struct winlink *wl)
{
struct cmd_find_state fs;
cmd_find_from_winlink(&fs, s, wl);
notify_add(name, &fs, NULL, s, wl->window, NULL);
cmd_find_from_winlink(&fs, wl, 0);
notify_add(name, &fs, NULL, wl->session, wl->window, NULL);
}
void
@ -196,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);
}
@ -205,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);
}
@ -214,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

@ -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);
}
@ -433,7 +433,7 @@ options_match(const char *s, int *idx, int* ambiguous)
if (*name == '@') {
*ambiguous = 0;
return (xstrdup(name));
return (name);
}
found = NULL;
@ -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)
{

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

@ -1,88 +0,0 @@
/* $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/ioctl.h>
#include <sys/time.h>
#ifdef __OpenBSD__
#include <sys/tty.h>
#endif
#include <fcntl.h>
#include <string.h>
#include <termios.h>
#include <unistd.h>
#include "compat.h"
int pty_open(int *);
pid_t pty_fork(int, int *, char *, size_t, struct winsize *);
#ifdef __OpenBSD__
int
pty_open(int *fd)
{
*fd = open(PATH_PTMDEV, O_RDWR|O_CLOEXEC);
if (*fd < 0)
return (-1);
return (0);
}
#else
int
pty_open(__unused int *fd)
{
*fd = -1;
return (0);
}
#endif
#ifdef __OpenBSD__
pid_t
pty_fork(int ptmfd, int *fd, char *name, size_t namelen, struct winsize *ws)
{
struct ptmget ptm;
pid_t pid;
if (ioctl(ptmfd, PTMGET, &ptm) == -1)
return (-1);
strlcpy(name, ptm.sn, namelen);
ioctl(ptm.sfd, TIOCSWINSZ, ws);
switch (pid = fork()) {
case -1:
close(ptm.cfd);
close(ptm.sfd);
return (-1);
case 0:
close(ptm.cfd);
login_tty(ptm.sfd);
return (0);
}
*fd = ptm.cfd;
close(ptm.sfd);
return (pid);
}
#else
pid_t
pty_fork(__unused int ptmfd, int *fd, char *name, __unused size_t namelen,
struct winsize *ws)
{
return (forkpty(fd, name, NULL, ws));
}
#endif

View File

@ -41,13 +41,14 @@ static const struct grid_cell screen_write_pad_cell = {
struct screen_write_collect_item {
u_int x;
int wrapped;
u_int used;
char data[256];
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;
@ -74,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),
@ -358,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;
@ -380,12 +387,154 @@ 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)
{
struct screen *s = ctx->s;
memset(ttyctx, 0, sizeof *ttyctx);
ttyctx->wp = ctx->wp;
ttyctx->ocx = s->cx;
@ -605,7 +754,7 @@ screen_write_deletecharacter(struct screen_write_ctx *ctx, u_int nx, u_int bg)
/* Clear nx characters. */
void
screen_write_clearcharacter(struct screen_write_ctx *ctx, u_int nx)
screen_write_clearcharacter(struct screen_write_ctx *ctx, u_int nx, u_int bg)
{
struct screen *s = ctx->s;
struct tty_ctx ttyctx;
@ -622,8 +771,9 @@ screen_write_clearcharacter(struct screen_write_ctx *ctx, u_int nx)
return;
screen_write_initctx(ctx, &ttyctx);
ttyctx.bg = bg;
grid_view_clear(s->grid, s->cx, s->cy, nx, 1, 8);
grid_view_clear(s->grid, s->cx, s->cy, nx, 1, bg);
screen_write_collect_flush(ctx, 0);
ttyctx.num = nx;
@ -808,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--;
@ -850,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;
@ -865,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)
@ -875,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;
@ -886,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;
@ -942,9 +1103,9 @@ screen_write_clearstartofscreen(struct screen_write_ctx *ctx, u_int bg)
if (s->cy > 0)
grid_view_clear(s->grid, 0, 0, sx, s->cy, bg);
if (s->cx > sx - 1)
grid_view_clear(s->grid, 0, s->cy, sx, 1, 8);
grid_view_clear(s->grid, 0, s->cy, sx, 1, bg);
else
grid_view_clear(s->grid, 0, s->cy, s->cx + 1, 1, 8);
grid_view_clear(s->grid, 0, s->cy, s->cx + 1, 1, bg);
screen_write_collect_clear(ctx, 0, s->cy);
screen_write_collect_flush(ctx, 0);
@ -1042,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;
@ -1054,6 +1218,7 @@ screen_write_collect_flush(struct screen_write_ctx *ctx, int scroll_only)
screen_write_cursormove(ctx, ci->x, y);
screen_write_initctx(ctx, &ttyctx);
ttyctx.cell = &ci->gc;
ttyctx.wrapped = ci->wrapped;
ttyctx.ptr = ci->data;
ttyctx.num = ci->used;
tty_write(tty_cmd_cells, &ttyctx);
@ -1113,7 +1278,7 @@ screen_write_collect_add(struct screen_write_ctx *ctx,
*/
collect = 1;
if (gc->data.width != 1)
if (gc->data.width != 1 || gc->data.size != 1)
collect = 0;
else if (gc->attr & GRID_ATTR_CHARSET)
collect = 0;
@ -1133,13 +1298,15 @@ screen_write_collect_add(struct screen_write_ctx *ctx,
if (s->cx > sx - 1 || ctx->item->used > sx - 1 - s->cx)
screen_write_collect_end(ctx);
ci = ctx->item; /* may have changed */
if (s->cx > sx - 1) {
log_debug("%s: wrapped at %u,%u", __func__, s->cx, s->cy);
screen_write_linefeed(ctx, 1);
ci->wrapped = 1;
screen_write_linefeed(ctx, 1, 8);
s->cx = 0;
}
ci = ctx->item; /* may have changed */
if (ci->used == 0)
memcpy(&ci->gc, gc, sizeof ci->gc);
ci->data[ci->used++] = gc->data.data[0];
@ -1197,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. */
@ -1248,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);
@ -1312,12 +1481,12 @@ screen_write_combine(struct screen_write_ctx *ctx, const struct utf8_data *ud,
fatalx("UTF-8 data empty");
/* Retrieve the previous cell. */
for (n = 1; n < s->cx; n++) {
for (n = 1; n <= s->cx; n++) {
grid_view_get_cell(gd, s->cx - n, s->cy, &gc);
if (~gc.flags & GRID_FLAG_PADDING)
break;
}
if (n == s->cx)
if (n > s->cx)
return (NULL);
*xx = s->cx - n;

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 *);
/* Idenfity mode callback. */
/* 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);
@ -136,11 +151,11 @@ server_client_get_key_table(struct client *c)
return (name);
}
/* Is this client using the default key table? */
int
server_client_is_default_key_table(struct client *c)
/* Is this table the default key table? */
static int
server_client_is_default_key_table(struct client *c, struct key_table *table)
{
return (strcmp(c->keytable->name, server_client_get_key_table(c)) == 0);
return (strcmp(table->name, server_client_get_key_table(c)) == 0);
}
/* Create a new client. */
@ -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(__UNCONST(c->cwd));
@ -281,6 +300,7 @@ server_client_lost(struct client *c)
free(c->prompt_string);
free(c->prompt_buffer);
format_lost_client(c);
environ_free(c->environ);
proc_remove_peer(c->peer);
@ -323,17 +343,32 @@ server_client_free(__unused int fd, __unused short events, void *arg)
}
}
/* Suspend a client. */
void
server_client_suspend(struct client *c)
{
struct session *s = c->session;
if (s == NULL || (c->flags & CLIENT_DETACHING))
return;
tty_stop_tty(&c->tty);
c->flags |= CLIENT_SUSPENDED;
proc_send(c->peer, MSG_SUSPEND, -1, NULL, 0);
}
/* Detach a client. */
void
server_client_detach(struct client *c, enum msgtype msgtype)
{
struct session *s = c->session;
if (s == NULL)
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. */
@ -776,11 +811,11 @@ server_client_handle_key(struct client *c, key_code key)
struct window *w;
struct window_pane *wp;
struct timeval tv;
const char *name;
struct key_table *table;
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)
@ -840,10 +875,9 @@ server_client_handle_key(struct client *c, key_code key)
m->valid = 0;
/* Find affected pane. */
if (KEYC_IS_MOUSE(key) && m->valid)
wp = cmd_mouse_pane(m, NULL, NULL);
else
wp = w->active;
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. */
if (KEYC_IS_MOUSE(key) && !options_get_number(s->options, "mouse"))
@ -853,37 +887,44 @@ server_client_handle_key(struct client *c, key_code key)
if (!KEYC_IS_MOUSE(key) && server_client_assume_paste(s))
goto forward;
retry:
/*
* Work out the current key table. If the pane is in a mode, use
* the mode table instead of the default key table.
*/
name = NULL;
if (wp != NULL && wp->mode != NULL && wp->mode->key_table != NULL)
name = wp->mode->key_table(wp);
if (name == NULL || !server_client_is_default_key_table(c))
if (server_client_is_default_key_table(c, c->keytable) &&
wp != NULL &&
wp->mode != NULL &&
wp->mode->key_table != NULL)
table = key_bindings_get_table(wp->mode->key_table(wp), 1);
else
table = c->keytable;
else
table = key_bindings_get_table(name, 1);
if (wp == NULL)
log_debug("key table %s (no pane)", table->name);
else
log_debug("key table %s (pane %%%u)", table->name, wp->id);
first = table;
/*
* 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. */
if (wp == NULL)
log_debug("key table %s (no pane)", table->name);
else
log_debug("key table %s (pane %%%u)", table->name, wp->id);
if (c->flags & CLIENT_REPEAT)
log_debug("currently repeating");
/* Try to see if there is a key binding in the current table. */
bd_find.key = key;
bd_find.key = key0;
bd = RB_FIND(key_bindings, &table->key_bindings, &bd_find);
if (bd != NULL) {
/*
@ -891,12 +932,15 @@ retry:
* non-repeating binding was found, stop repeating and try
* again in the root table.
*/
if ((c->flags & CLIENT_REPEAT) && !bd->can_repeat) {
if ((c->flags & CLIENT_REPEAT) &&
(~bd->flags & KEY_BINDING_REPEAT)) {
server_client_set_key_table(c, NULL);
c->flags &= ~CLIENT_REPEAT;
server_status_client(c);
table = c->keytable;
goto retry;
}
log_debug("found in key table %s", table->name);
/*
* Take a reference to this table to make sure the key binding
@ -909,7 +953,7 @@ retry:
* the client back to the root table.
*/
xtimeout = options_get_number(s->options, "repeat-time");
if (xtimeout != 0 && bd->can_repeat) {
if (xtimeout != 0 && (bd->flags & KEY_BINDING_REPEAT)) {
c->flags |= CLIENT_REPEAT;
tv.tv_sec = xtimeout / 1000;
@ -922,39 +966,31 @@ retry:
}
server_status_client(c);
/* Find default state if the pane is known. */
cmd_find_clear_state(&fs, NULL, 0);
if (wp != NULL) {
fs.s = s;
fs.wl = fs.s->curw;
fs.w = fs.wl->window;
fs.wp = wp;
cmd_find_log_state(__func__, &fs);
if (!cmd_find_valid_state(&fs))
fatalx("invalid key state");
}
/* Dispatch the key binding. */
key_bindings_dispatch(bd, c, m, &fs);
/* Execute the key binding. */
key_bindings_dispatch(bd, NULL, c, m, &fs);
key_bindings_unref_table(table);
return;
}
/*
* No match in this table. If repeating, switch the client back to the
* root table and try again.
* No match in this table. If not in the root table or if repeating,
* switch the client back to the root table and try again.
*/
if (c->flags & CLIENT_REPEAT) {
log_debug("not found in key table %s", table->name);
if (!server_client_is_default_key_table(c, table) ||
(c->flags & CLIENT_REPEAT)) {
server_client_set_key_table(c, NULL);
c->flags &= ~CLIENT_REPEAT;
server_status_client(c);
table = c->keytable;
goto retry;
}
/* If no match and we're not in the root table, that's it. */
if (name == NULL && !server_client_is_default_key_table(c)) {
log_debug("no key in key table %s", table->name);
/*
* 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 && (~flags & CLIENT_REPEAT)) {
server_client_set_key_table(c, NULL);
server_status_client(c);
return;
@ -1002,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)
@ -1013,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
@ -1029,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. */
@ -1042,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);
@ -1209,6 +1291,14 @@ server_client_check_exit(struct client *c)
c->flags &= ~CLIENT_EXIT;
}
/* Redraw timer callback. */
static void
server_client_redraw_timer(__unused int fd, __unused short events,
__unused void* data)
{
log_debug("redraw timer fired");
}
/* Check for client redraws. */
static void
server_client_check_redraw(struct client *c)
@ -1216,19 +1306,56 @@ server_client_check_redraw(struct client *c)
struct session *s = c->session;
struct tty *tty = &c->tty;
struct window_pane *wp;
int flags, masked;
int needed, flags, masked;
struct timeval tv = { .tv_usec = 1000 };
static struct event ev;
size_t left;
if (c->flags & (CLIENT_CONTROL|CLIENT_SUSPENDED))
return;
/*
* If there is outstanding data, defer the redraw until it has been
* consumed. We can just add a timer to get out of the event loop and
* end up back here.
*/
needed = 0;
if (c->flags & CLIENT_REDRAW)
needed = 1;
else {
TAILQ_FOREACH(wp, &c->session->curw->window->panes, entry) {
if (wp->flags & PANE_REDRAW) {
needed = 1;
break;
}
}
}
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;
} 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"))
server_client_set_title(c);
screen_redraw_update(c); /* will adjust flags */
}
flags = tty->flags & (TTY_FREEZE|TTY_NOCURSOR);
tty->flags = (tty->flags & ~TTY_FREEZE) | TTY_NOCURSOR;
flags = tty->flags & (TTY_BLOCK|TTY_FREEZE|TTY_NOCURSOR);
tty->flags = (tty->flags & ~(TTY_BLOCK|TTY_FREEZE)) | TTY_NOCURSOR;
if (c->flags & CLIENT_REDRAW) {
tty_update_mode(tty, tty->mode, NULL);
@ -1258,6 +1385,16 @@ server_client_check_redraw(struct client *c)
c->flags &= ~(CLIENT_REDRAW|CLIENT_BORDERS|CLIENT_STATUS|
CLIENT_STATUSFORCE);
if (needed) {
/*
* We would have deferred the redraw unless the output buffer
* was empty, so we can record how many bytes the redraw
* generated.
*/
c->redraw = EVBUFFER_LENGTH(tty->out);
log_debug("%s: redraw added %zu bytes", c->name, c->redraw);
}
}
/* Set client title. */
@ -1271,7 +1408,7 @@ server_client_set_title(struct client *c)
template = options_get_string(s->options, "set-titles-string");
ft = format_create(NULL, FORMAT_NONE, 0);
ft = format_create(c, NULL, FORMAT_NONE, 0);
format_defaults(ft, c, NULL, NULL, NULL);
title = format_expand_time(ft, template, time(NULL));
@ -1342,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;
@ -1469,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;
@ -1593,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);
}
@ -1614,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) {
@ -1655,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

@ -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);
@ -188,13 +188,21 @@ session_create(const char *prefix, const char *name, int argc, char **argv,
return (s);
}
/* Add a reference to a session. */
void
session_add_ref(struct session *s, const char *from)
{
s->references++;
log_debug("%s: %s %s, now %d", __func__, s->name, from, s->references);
}
/* Remove a reference from a session. */
void
session_unref(struct session *s)
session_remove_ref(struct session *s, const char *from)
{
log_debug("session %s has %d references", s->name, s->references);
s->references--;
log_debug("%s: %s %s, now %d", __func__, s->name, from, s->references);
if (s->references == 0)
event_once(-1, EV_TIMEOUT, session_free, s, NULL);
}
@ -220,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);
@ -247,7 +255,7 @@ session_destroy(struct session *s)
free(__UNCONST(s->cwd));
session_unref(s);
session_remove_ref(s, __func__);
}
/* Check a session name is valid: not empty and no colons or periods. */
@ -359,7 +367,7 @@ session_new(struct session *s, const char *name, int argc, char **argv,
shell = _PATH_BSHELL;
hlimit = options_get_number(s->options, "history-limit");
env = environ_for_session(s);
env = environ_for_session(s, 0);
w = window_create_spawn(name, argc, argv, path, shell, cwd, env, s->tio,
s->sx, s->sy, hlimit, cause);
if (w == NULL) {
@ -410,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);
@ -544,6 +552,7 @@ session_set_current(struct session *s, struct winlink *wl)
s->curw = wl;
winlink_clear_flags(wl);
window_update_activity(wl->window);
notify_session("session-window-changed", s);
return (0);
}

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);
@ -519,9 +526,9 @@ status_replace(struct client *c, struct winlink *wl, const char *fmt, time_t t)
else
tag = FORMAT_NONE;
if (c->flags & CLIENT_STATUSFORCE)
ft = format_create(NULL, tag, FORMAT_STATUS|FORMAT_FORCE);
ft = format_create(c, NULL, tag, FORMAT_STATUS|FORMAT_FORCE);
else
ft = format_create(NULL, tag, FORMAT_STATUS);
ft = format_create(c, NULL, tag, FORMAT_STATUS);
format_defaults(ft, c, NULL, wl, NULL);
expanded = format_expand_time(ft, fmt, t);
@ -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,29 +669,39 @@ 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;
char *tmp;
char *tmp, *cp;
ft = format_create(NULL, FORMAT_NONE, 0);
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;
@ -690,6 +713,12 @@ status_prompt_set(struct client *c, const char *msg, const char *input,
c->tty.flags |= (TTY_NOCURSOR|TTY_FREEZE);
c->flags |= CLIENT_STATUS;
if ((flags & PROMPT_INCREMENTAL) && *tmp != '\0') {
xasprintf(&cp, "=%s", tmp);
c->prompt_inputcb(c, c->prompt_data, cp, 0);
free(cp);
}
free(tmp);
format_free(ft);
}
@ -701,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;
@ -724,7 +753,7 @@ status_prompt_update(struct client *c, const char *msg, const char *input)
time_t t;
char *tmp;
ft = format_create(NULL, FORMAT_NONE, 0);
ft = format_create(c, NULL, FORMAT_NONE, 0);
format_defaults(ft, c, NULL, NULL, NULL);
t = time(NULL);
@ -989,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);
@ -1270,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 */
@ -1324,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);
}
@ -1334,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);
}
@ -1497,6 +1526,7 @@ status_prompt_complete(struct session *session, const char *s)
out = status_prompt_complete_prefix(list, size);
if (out != NULL) {
xasprintf(&tmp, "-%c%s%s", copy[1], out, colon);
free(out);
out = tmp;
goto found;
}

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
@ -3199,7 +3274,6 @@ will generate
.Xr xterm 1 -style
function key sequences; these have a number included to indicate modifiers such
as Shift, Alt or Ctrl.
The default is off.
.El
.It Xo Ic show-options
.Op Fl gqsvw
@ -3263,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
@ -3288,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
@ -3432,20 +3512,39 @@ 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
.Ql !=
and a colon.
For example
.Ql #{==,#{host},myhost}
.Ql #{==:#{host},myhost}
will be replaced by
.Ql 1
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
@ -3494,6 +3593,8 @@ does not wait for
.Ql #()
commands to finish; instead, the previous result from running the same command is used,
or a placeholder if the command has not been run before.
If the command hasn't exited, the most recent line of output will be used, but the status
line will not be updated more than once a second.
Commands are executed with the
.Nm
global environment set (see the
@ -3506,12 +3607,14 @@ 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"
.It Li "client_key_table" Ta "" Ta "Current key table"
.It Li "client_last_session" Ta "" Ta "Name of the client's last session"
@ -3553,19 +3656,27 @@ 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"
.It Li "pane_input_off" Ta "" Ta "If input to pane is disabled"
.It Li "pane_index" Ta "#P" Ta "Index of pane"
.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"
.It Li "pane_synchronized" Ta "" Ta "If pane is synchronized"
.It Li "pane_tabs" Ta "" Ta "Pane tab positions"
@ -3577,28 +3688,31 @@ 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"
.It Li "session_id" Ta "" Ta "Unique session ID"
.It Li "session_many_attached" Ta "" Ta "1 if multiple clients attached"
.It Li "session_name" Ta "#S" Ta "Name of session"
.It Li "session_stack" Ta "" Ta "Window indexes in most recent order"
.It Li "session_width" Ta "" Ta "Width of session"
.It Li "session_windows" Ta "" Ta "Number of windows in session"
.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"
@ -3608,6 +3722,7 @@ The following variables are available, where appropriate:
.It Li "window_name" Ta "#W" Ta "Name of window"
.It Li "window_panes" Ta "" Ta "Number of panes in window"
.It Li "window_silence_flag" Ta "" Ta "1 if window has silence alert"
.It Li "window_stack_index" Ta "" Ta "Index in session most recent stack"
.It Li "window_visible_layout" Ta "" Ta "Window layout description, respecting zoomed window panes"
.It Li "window_width" Ta "" Ta "Width of window"
.It Li "window_zoomed_flag" Ta "" Ta "1 if window is zoomed"
@ -3621,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
@ -3669,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
@ -3763,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."
@ -3960,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
@ -3974,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 )
@ -4234,6 +4383,11 @@ A notification will never occur inside an output block.
.Pp
The following notifications are defined:
.Bl -tag -width Ds
.It Ic %client-session-changed Ar client Ar session-id Ar name
The client is now attached to the session with ID
.Ar session-id ,
which is named
.Ar name .
.It Ic %exit Op Ar reason
The
.Nm
@ -4256,6 +4410,10 @@ and the window flags are
A window pane produced output.
.Ar value
escapes non-printable characters and backslash as octal \\xxx.
.It Ic %pane-mode-changed Ar pane-id
The pane with ID
.Ar pane-id
has changed mode.
.It Ic %session-changed Ar session-id Ar name
The client is now attached to the session with ID
.Ar session-id ,
@ -4264,6 +4422,11 @@ which is named
.It Ic %session-renamed Ar name
The current session was renamed to
.Ar name .
.It Ic %session-window-changed Ar session-id Ar window-id
The session with ID
.Ar session-id
changed its active window to the window with ID
.Ar window-id .
.It Ic %sessions-changed
A session was created or destroyed.
.It Ic %unlinked-window-add Ar window-id
@ -4278,6 +4441,11 @@ was linked to the current session.
The window with ID
.Ar window-id
closed.
.It Ic %window-pane-changed Ar window-id Ar pane-id
The active pane in the window with ID
.Ar window-id
changed to the pane with ID
.Ar pane-id .
.It Ic %window-renamed Ar window-id Ar name
The window with ID
.Ar window-id

View File

@ -22,7 +22,6 @@
#include <errno.h>
#include <event.h>
#include <fcntl.h>
#include <getopt.h>
#include <langinfo.h>
#include <locale.h>
#include <pwd.h>
@ -42,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 *);
@ -118,7 +118,7 @@ make_label(const char *label)
uid = getuid();
if ((s = getenv("TMUX_TMPDIR")) != NULL && *s != '\0')
xasprintf(&base, "%s/tmux-%u", s, uid);
xasprintf(&base, "%s/tmux-%ld", s, (long)uid);
else
xasprintf(&base, "%s/tmux-%ld", _PATH_TMP, (long)uid);
@ -188,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);
@ -217,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)
@ -258,11 +258,11 @@ main(int argc, char **argv)
argc -= optind;
argv += optind;
if (shellcmd != NULL && argc != 0)
if (shell_command != NULL && argc != 0)
usage();
if (pty_open(&ptm_fd) != 0)
errx(1, "open(\"/dev/ptm\"");
if ((ptm_fd = getptmfd()) == -1)
err(1, "getptmfd");
if (pledge("stdio rpath wpath cpath flock fattr unix getpw sendfd "
"recvfd proc exec tty ps", NULL) != 0)
err(1, "pledge");
@ -348,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));
}

File diff suppressed because it is too large Load Diff

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. */
@ -257,67 +261,72 @@ static const struct tty_default_key_code tty_default_code_keys[] = {
{ TTYC_KCUB1, KEYC_LEFT },
{ TTYC_KCUF1, KEYC_RIGHT },
/* Key and modifier capabilities. */
{ TTYC_KDC2, KEYC_DC|KEYC_SHIFT },
{ TTYC_KDC3, KEYC_DC|KEYC_ESCAPE },
{ TTYC_KDC4, KEYC_DC|KEYC_SHIFT|KEYC_ESCAPE },
{ TTYC_KDC5, KEYC_DC|KEYC_CTRL },
{ TTYC_KDC6, KEYC_DC|KEYC_SHIFT|KEYC_CTRL },
{ TTYC_KDC7, KEYC_DC|KEYC_ESCAPE|KEYC_CTRL },
{ TTYC_KDN2, KEYC_DOWN|KEYC_SHIFT },
{ TTYC_KDN3, KEYC_DOWN|KEYC_ESCAPE },
{ TTYC_KDN4, KEYC_DOWN|KEYC_SHIFT|KEYC_ESCAPE },
{ TTYC_KDN5, KEYC_DOWN|KEYC_CTRL },
{ TTYC_KDN6, KEYC_DOWN|KEYC_SHIFT|KEYC_CTRL },
{ TTYC_KDN7, KEYC_DOWN|KEYC_ESCAPE|KEYC_CTRL },
{ TTYC_KEND2, KEYC_END|KEYC_SHIFT },
{ TTYC_KEND3, KEYC_END|KEYC_ESCAPE },
{ TTYC_KEND4, KEYC_END|KEYC_SHIFT|KEYC_ESCAPE },
{ TTYC_KEND5, KEYC_END|KEYC_CTRL },
{ TTYC_KEND6, KEYC_END|KEYC_SHIFT|KEYC_CTRL },
{ TTYC_KEND7, KEYC_END|KEYC_ESCAPE|KEYC_CTRL },
{ TTYC_KHOM2, KEYC_HOME|KEYC_SHIFT },
{ TTYC_KHOM3, KEYC_HOME|KEYC_ESCAPE },
{ TTYC_KHOM4, KEYC_HOME|KEYC_SHIFT|KEYC_ESCAPE },
{ TTYC_KHOM5, KEYC_HOME|KEYC_CTRL },
{ TTYC_KHOM6, KEYC_HOME|KEYC_SHIFT|KEYC_CTRL },
{ TTYC_KHOM7, KEYC_HOME|KEYC_ESCAPE|KEYC_CTRL },
{ TTYC_KIC2, KEYC_IC|KEYC_SHIFT },
{ TTYC_KIC3, KEYC_IC|KEYC_ESCAPE },
{ TTYC_KIC4, KEYC_IC|KEYC_SHIFT|KEYC_ESCAPE },
{ TTYC_KIC5, KEYC_IC|KEYC_CTRL },
{ TTYC_KIC6, KEYC_IC|KEYC_SHIFT|KEYC_CTRL },
{ TTYC_KIC7, KEYC_IC|KEYC_ESCAPE|KEYC_CTRL },
{ TTYC_KLFT2, KEYC_LEFT|KEYC_SHIFT },
{ TTYC_KLFT3, KEYC_LEFT|KEYC_ESCAPE },
{ TTYC_KLFT4, KEYC_LEFT|KEYC_SHIFT|KEYC_ESCAPE },
{ TTYC_KLFT5, KEYC_LEFT|KEYC_CTRL },
{ TTYC_KLFT6, KEYC_LEFT|KEYC_SHIFT|KEYC_CTRL },
{ TTYC_KLFT7, KEYC_LEFT|KEYC_ESCAPE|KEYC_CTRL },
{ TTYC_KNXT2, KEYC_NPAGE|KEYC_SHIFT },
{ TTYC_KNXT3, KEYC_NPAGE|KEYC_ESCAPE },
{ TTYC_KNXT4, KEYC_NPAGE|KEYC_SHIFT|KEYC_ESCAPE },
{ TTYC_KNXT5, KEYC_NPAGE|KEYC_CTRL },
{ TTYC_KNXT6, KEYC_NPAGE|KEYC_SHIFT|KEYC_CTRL },
{ TTYC_KNXT7, KEYC_NPAGE|KEYC_ESCAPE|KEYC_CTRL },
{ TTYC_KPRV2, KEYC_PPAGE|KEYC_SHIFT },
{ TTYC_KPRV3, KEYC_PPAGE|KEYC_ESCAPE },
{ TTYC_KPRV4, KEYC_PPAGE|KEYC_SHIFT|KEYC_ESCAPE },
{ TTYC_KPRV5, KEYC_PPAGE|KEYC_CTRL },
{ TTYC_KPRV6, KEYC_PPAGE|KEYC_SHIFT|KEYC_CTRL },
{ TTYC_KPRV7, KEYC_PPAGE|KEYC_ESCAPE|KEYC_CTRL },
{ TTYC_KRIT2, KEYC_RIGHT|KEYC_SHIFT },
{ TTYC_KRIT3, KEYC_RIGHT|KEYC_ESCAPE },
{ TTYC_KRIT4, KEYC_RIGHT|KEYC_SHIFT|KEYC_ESCAPE },
{ TTYC_KRIT5, KEYC_RIGHT|KEYC_CTRL },
{ TTYC_KRIT6, KEYC_RIGHT|KEYC_SHIFT|KEYC_CTRL },
{ TTYC_KRIT7, KEYC_RIGHT|KEYC_ESCAPE|KEYC_CTRL },
{ TTYC_KUP2, KEYC_UP|KEYC_SHIFT },
{ TTYC_KUP3, KEYC_UP|KEYC_ESCAPE },
{ TTYC_KUP4, KEYC_UP|KEYC_SHIFT|KEYC_ESCAPE },
{ TTYC_KUP5, KEYC_UP|KEYC_CTRL },
{ TTYC_KUP6, KEYC_UP|KEYC_SHIFT|KEYC_CTRL },
{ TTYC_KUP7, KEYC_UP|KEYC_ESCAPE|KEYC_CTRL },
/*
* Key and modifier capabilities. We set the xterm flag to mark that
* any leading escape means an escape key press and not the modifier.
*/
{ TTYC_KDC2, KEYC_DC|KEYC_SHIFT|KEYC_XTERM },
{ TTYC_KDC3, KEYC_DC|KEYC_ESCAPE|KEYC_XTERM },
{ TTYC_KDC4, KEYC_DC|KEYC_SHIFT|KEYC_ESCAPE|KEYC_XTERM },
{ 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 },
{ TTYC_KDN5, KEYC_DOWN|KEYC_CTRL|KEYC_XTERM },
{ TTYC_KDN6, KEYC_DOWN|KEYC_SHIFT|KEYC_CTRL|KEYC_XTERM },
{ TTYC_KDN7, KEYC_DOWN|KEYC_ESCAPE|KEYC_CTRL|KEYC_XTERM },
{ TTYC_KEND2, KEYC_END|KEYC_SHIFT|KEYC_XTERM },
{ TTYC_KEND3, KEYC_END|KEYC_ESCAPE|KEYC_XTERM },
{ TTYC_KEND4, KEYC_END|KEYC_SHIFT|KEYC_ESCAPE|KEYC_XTERM },
{ TTYC_KEND5, KEYC_END|KEYC_CTRL|KEYC_XTERM },
{ TTYC_KEND6, KEYC_END|KEYC_SHIFT|KEYC_CTRL|KEYC_XTERM },
{ TTYC_KEND7, KEYC_END|KEYC_ESCAPE|KEYC_CTRL|KEYC_XTERM },
{ TTYC_KHOM2, KEYC_HOME|KEYC_SHIFT|KEYC_XTERM },
{ TTYC_KHOM3, KEYC_HOME|KEYC_ESCAPE|KEYC_XTERM },
{ TTYC_KHOM4, KEYC_HOME|KEYC_SHIFT|KEYC_ESCAPE|KEYC_XTERM },
{ TTYC_KHOM5, KEYC_HOME|KEYC_CTRL|KEYC_XTERM },
{ TTYC_KHOM6, KEYC_HOME|KEYC_SHIFT|KEYC_CTRL|KEYC_XTERM },
{ TTYC_KHOM7, KEYC_HOME|KEYC_ESCAPE|KEYC_CTRL|KEYC_XTERM },
{ TTYC_KIC2, KEYC_IC|KEYC_SHIFT|KEYC_XTERM },
{ TTYC_KIC3, KEYC_IC|KEYC_ESCAPE|KEYC_XTERM },
{ TTYC_KIC4, KEYC_IC|KEYC_SHIFT|KEYC_ESCAPE|KEYC_XTERM },
{ TTYC_KIC5, KEYC_IC|KEYC_CTRL|KEYC_XTERM },
{ TTYC_KIC6, KEYC_IC|KEYC_SHIFT|KEYC_CTRL|KEYC_XTERM },
{ TTYC_KIC7, KEYC_IC|KEYC_ESCAPE|KEYC_CTRL|KEYC_XTERM },
{ TTYC_KLFT2, KEYC_LEFT|KEYC_SHIFT|KEYC_XTERM },
{ TTYC_KLFT3, KEYC_LEFT|KEYC_ESCAPE|KEYC_XTERM },
{ TTYC_KLFT4, KEYC_LEFT|KEYC_SHIFT|KEYC_ESCAPE|KEYC_XTERM },
{ TTYC_KLFT5, KEYC_LEFT|KEYC_CTRL|KEYC_XTERM },
{ TTYC_KLFT6, KEYC_LEFT|KEYC_SHIFT|KEYC_CTRL|KEYC_XTERM },
{ TTYC_KLFT7, KEYC_LEFT|KEYC_ESCAPE|KEYC_CTRL|KEYC_XTERM },
{ TTYC_KNXT2, KEYC_NPAGE|KEYC_SHIFT|KEYC_XTERM },
{ TTYC_KNXT3, KEYC_NPAGE|KEYC_ESCAPE|KEYC_XTERM },
{ TTYC_KNXT4, KEYC_NPAGE|KEYC_SHIFT|KEYC_ESCAPE|KEYC_XTERM },
{ TTYC_KNXT5, KEYC_NPAGE|KEYC_CTRL|KEYC_XTERM },
{ TTYC_KNXT6, KEYC_NPAGE|KEYC_SHIFT|KEYC_CTRL|KEYC_XTERM },
{ TTYC_KNXT7, KEYC_NPAGE|KEYC_ESCAPE|KEYC_CTRL|KEYC_XTERM },
{ TTYC_KPRV2, KEYC_PPAGE|KEYC_SHIFT|KEYC_XTERM },
{ TTYC_KPRV3, KEYC_PPAGE|KEYC_ESCAPE|KEYC_XTERM },
{ TTYC_KPRV4, KEYC_PPAGE|KEYC_SHIFT|KEYC_ESCAPE|KEYC_XTERM },
{ TTYC_KPRV5, KEYC_PPAGE|KEYC_CTRL|KEYC_XTERM },
{ TTYC_KPRV6, KEYC_PPAGE|KEYC_SHIFT|KEYC_CTRL|KEYC_XTERM },
{ TTYC_KPRV7, KEYC_PPAGE|KEYC_ESCAPE|KEYC_CTRL|KEYC_XTERM },
{ TTYC_KRIT2, KEYC_RIGHT|KEYC_SHIFT|KEYC_XTERM },
{ TTYC_KRIT3, KEYC_RIGHT|KEYC_ESCAPE|KEYC_XTERM },
{ TTYC_KRIT4, KEYC_RIGHT|KEYC_SHIFT|KEYC_ESCAPE|KEYC_XTERM },
{ 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 },
{ TTYC_KUP5, KEYC_UP|KEYC_CTRL|KEYC_XTERM },
{ TTYC_KUP6, KEYC_UP|KEYC_SHIFT|KEYC_CTRL|KEYC_XTERM },
{ TTYC_KUP7, KEYC_UP|KEYC_ESCAPE|KEYC_CTRL|KEYC_XTERM },
};
/* Add key to tree. */
@ -382,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);
@ -404,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. */
@ -476,6 +495,7 @@ tty_keys_next1(struct tty *tty, const char *buf, size_t len, key_code *key,
enum utf8_state more;
u_int i;
wchar_t wc;
int n;
log_debug("%s: next key is %zu (%.*s) (expired=%d)", c->name, len,
(int)len, buf, expired);
@ -493,6 +513,13 @@ tty_keys_next1(struct tty *tty, const char *buf, size_t len, key_code *key,
return (0);
}
/* Is this an an xterm(1) key? */
n = xterm_keys_find(buf, len, size, key);
if (n == 0)
return (0);
if (n == 1 && !expired)
return (1);
/* Is this valid UTF-8? */
more = utf8_open(&ud, (u_char)*buf);
if (more == UTF8_MORE) {
@ -568,11 +595,33 @@ 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);
if (n == 0) { /* found */
if (key & KEYC_XTERM) {
/*
* We want the escape key as well as the xterm
* key, because the xterm sequence implicitly
* includes the escape (so if we see
* \033\033[1;3D we know it is an Escape
* followed by M-Left, not just M-Left).
*/
key = '\033';
size = 1;
goto complete_key;
}
key |= KEYC_ESCAPE;
size++;
goto complete_key;
@ -581,20 +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;
/* Is this an an xterm(1) key? */
n = xterm_keys_find(buf, len, &size, &key);
if (n == 0)
goto complete_key;
if (n == 1 && !expired)
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(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(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)
{

View File

@ -71,14 +71,16 @@ 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)
#define tty_pane_full_width(tty, ctx) \
#define tty_pane_full_width(tty, ctx) \
((ctx)->xoff == 0 && screen_size_x((ctx)->wp->screen) >= (tty)->sx)
#define TTY_BLOCK_INTERVAL (100000 /* 100 milliseconds */)
#define TTY_BLOCK_START(tty) (1 + ((tty)->sx * (tty)->sy) * 8)
#define TTY_BLOCK_STOP(tty) (1 + ((tty)->sx * (tty)->sy) / 8)
void
tty_create_log(void)
{
@ -118,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;
@ -137,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
@ -163,14 +159,62 @@ 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))
;
}
static void
tty_timer_callback(__unused int fd, __unused short events, void *data)
{
struct tty *tty = data;
struct client *c = tty->client;
struct timeval tv = { .tv_usec = TTY_BLOCK_INTERVAL };
log_debug("%s: %zu discarded", c->name, tty->discarded);
c->flags |= CLIENT_REDRAW;
c->discarded += tty->discarded;
if (tty->discarded < TTY_BLOCK_STOP(tty)) {
tty->flags &= ~TTY_BLOCK;
tty_invalidate(tty);
return;
}
tty->discarded = 0;
evtimer_add(&tty->timer, &tv);
}
static int
tty_block_maybe(struct tty *tty)
{
struct client *c = tty->client;
size_t size = EVBUFFER_LENGTH(tty->out);
struct timeval tv = { .tv_usec = TTY_BLOCK_INTERVAL };
if (size < TTY_BLOCK_START(tty))
return (0);
if (tty->flags & TTY_BLOCK)
return (1);
tty->flags |= TTY_BLOCK;
log_debug("%s: can't keep up, %zu discarded", c->name, size);
evbuffer_drain(tty->out, size);
c->discarded += size;
tty->discarded = 0;
evtimer_add(&tty->timer, &tv);
return (1);
}
static void
tty_write_callback(__unused int fd, __unused short events, void *data)
{
@ -184,6 +228,16 @@ tty_write_callback(__unused int fd, __unused short events, void *data)
return;
log_debug("%s: wrote %d bytes (of %zu)", c->name, nwrite, size);
if (c->redraw > 0) {
if ((size_t)nwrite >= c->redraw)
c->redraw = 0;
else
c->redraw -= nwrite;
log_debug("%s: waiting for redraw, %zu bytes left", c->name,
c->redraw);
} else if (tty_block_maybe(tty))
return;
if (EVBUFFER_LENGTH(tty->out) != 0)
event_add(&tty->event_out, NULL);
}
@ -198,7 +252,7 @@ tty_open(struct tty *tty, char **cause)
}
tty->flags |= TTY_OPENED;
tty->flags &= ~(TTY_NOCURSOR|TTY_FREEZE);
tty->flags &= ~(TTY_NOCURSOR|TTY_FREEZE|TTY_BLOCK|TTY_TIMER);
event_set(&tty->event_in, tty->fd, EV_PERSIST|EV_READ,
tty_read_callback, tty);
@ -207,6 +261,8 @@ tty_open(struct tty *tty, char **cause)
event_set(&tty->event_out, tty->fd, EV_WRITE, tty_write_callback, tty);
tty->out = evbuffer_new();
evtimer_set(&tty->timer, tty_timer_callback, tty);
tty_start_tty(tty);
tty_keys_build(tty);
@ -217,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);
@ -238,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");
@ -273,6 +334,9 @@ tty_stop_tty(struct tty *tty)
return;
tty->flags &= ~TTY_STARTED;
event_del(&tty->timer);
tty->flags &= ~TTY_BLOCK;
event_del(&tty->event_in);
event_del(&tty->event_out);
@ -287,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));
@ -405,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)
{
@ -425,9 +497,14 @@ tty_add(struct tty *tty, const char *buf, size_t len)
{
struct client *c = tty->client;
if (tty->flags & TTY_BLOCK) {
tty->discarded += len;
return;
}
evbuffer_add(tty->out, buf, len);
log_debug("%s: %.*s", c->name, (int)len, (const char *)buf);
tty->written += len;
log_debug("%s: %.*s", c->name, (int)len, buf);
c->written += len;
if (tty_log_fd != -1)
write(tty_log_fd, buf, len);
@ -478,9 +555,13 @@ void
tty_putn(struct tty *tty, const void *buf, size_t len, u_int width)
{
tty_add(tty, buf, len);
if (tty->cx + width > tty->sx)
tty->cx = tty->cy = UINT_MAX;
else
if (tty->cx + width > tty->sx) {
tty->cx = (tty->cx + width) - tty->sx;
if (tty->cx <= tty->sx)
tty->cy++;
else
tty->cx = tty->cy = UINT_MAX;
} else
tty->cx += width;
}
@ -681,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)
@ -693,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;
@ -705,24 +895,37 @@ tty_draw_line(struct tty *tty, const struct window_pane *wp,
tty_region_off(tty);
tty_margin_off(tty);
/*
* Clamp the width to cellsize - note this is not cellused, because
* there may be empty background cells after it (from BCE).
*/
sx = screen_size_x(s);
if (sx > s->grid->linedata[s->grid->hsize + py].cellused)
sx = s->grid->linedata[s->grid->hsize + py].cellused;
if (sx > s->grid->linedata[s->grid->hsize + py].cellsize)
sx = s->grid->linedata[s->grid->hsize + py].cellsize;
if (sx > tty->sx)
sx = tty->sx;
ux = 0;
if (screen_size_x(s) < tty->sx &&
ox == 0 &&
sx != screen_size_x(s) &&
tty_term_has(tty->term, TTYC_EL1) &&
!tty_fake_bce(tty, wp, 8)) {
tty_default_attributes(tty, wp, 8);
tty_cursor(tty, screen_size_x(s) - 1, oy + py);
tty_putcode(tty, TTYC_EL1);
cleared = 1;
}
if (sx != 0)
tty_cursor(tty, ox, oy + py);
if (wp == NULL ||
py == 0 ||
(~s->grid->linedata[s->grid->hsize + py - 1].flags & GRID_LINE_WRAPPED) ||
ox != 0 ||
tty->cx < tty->sx ||
screen_size_x(s) < tty->sx) {
if (screen_size_x(s) < tty->sx &&
ox == 0 &&
sx != screen_size_x(s) &&
tty_term_has(tty->term, TTYC_EL1) &&
!tty_fake_bce(tty, wp, 8)) {
tty_default_attributes(tty, wp, 8);
tty_cursor(tty, screen_size_x(s) - 1, oy + py);
tty_putcode(tty, TTYC_EL1);
cleared = 1;
}
if (sx != 0)
tty_cursor(tty, ox, oy + py);
} else
log_debug("%s: wrapped line %u", __func__, oy + py);
memcpy(&last, &grid_default_cell, sizeof last);
len = 0;
@ -743,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;
@ -765,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;
@ -772,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;
@ -876,7 +1087,7 @@ tty_cmd_deletecharacter(struct tty *tty, const struct tty_ctx *ctx)
void
tty_cmd_clearcharacter(struct tty *tty, const struct tty_ctx *ctx)
{
tty_attributes(tty, &grid_default_cell, ctx->wp);
tty_default_attributes(tty, ctx->wp, ctx->bg);
tty_cursor_pane(tty, ctx, ctx->ocx, ctx->ocy);
@ -905,6 +1116,7 @@ tty_cmd_insertline(struct tty *tty, const struct tty_ctx *ctx)
tty_cursor_pane(tty, ctx, ctx->ocx, ctx->ocy);
tty_emulate_repeat(tty, TTYC_IL, TTYC_IL1, ctx->num);
tty->cx = tty->cy = UINT_MAX;
}
void
@ -925,79 +1137,61 @@ tty_cmd_deleteline(struct tty *tty, const struct tty_ctx *ctx)
tty_cursor_pane(tty, ctx, ctx->ocx, ctx->ocy);
tty_emulate_repeat(tty, TTYC_DL, TTYC_DL1, ctx->num);
tty->cx = tty->cy = UINT_MAX;
}
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);
@ -1021,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);
@ -1052,7 +1246,7 @@ 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);
@ -1069,115 +1263,69 @@ 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) &&
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
@ -1292,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);
@ -1414,7 +1562,8 @@ static void
tty_cursor_pane_unless_wrap(struct tty *tty, const struct tty_ctx *ctx,
u_int cx, u_int cy)
{
if (!tty_pane_full_width(tty, ctx) ||
if (!ctx->wrapped ||
!tty_pane_full_width(tty, ctx) ||
(tty->term->flags & TERM_EARLYWRAP) ||
ctx->xoff + cx != 0 ||
ctx->yoff + cy != tty->cy + 1 ||
@ -1461,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;
@ -1474,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;
}
@ -1641,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);
}
@ -1710,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
@ -1763,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
@ -1902,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);
}

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;

View File

@ -87,7 +87,7 @@ window_buffer_add_item(struct window_buffer_modedata *data)
static void
window_buffer_free_item(struct window_buffer_itemdata *item)
{
free((void *)item->name);
free(__UNCONST(item->name));
free(item);
}

File diff suppressed because it is too large Load Diff

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 *);
@ -105,6 +106,8 @@ static void window_copy_move_mouse(struct mouse_event *);
static void window_copy_drag_update(struct client *, struct mouse_event *);
const struct window_mode window_copy_mode = {
.name = "copy-mode",
.init = window_copy_init,
.free = window_copy_free,
.resize = window_copy_resize,
@ -185,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;
@ -206,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;
@ -314,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;
@ -992,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;
@ -1132,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;
@ -1448,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);
}
@ -1496,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);
@ -1617,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)
@ -1641,7 +1661,7 @@ window_copy_copy_pipe(struct window_pane *wp, struct session *s,
return;
expanded = format_single(NULL, arg, NULL, s, NULL, wp);
job = job_run(expanded, s, NULL, NULL, NULL, NULL);
job = job_run(expanded, s, NULL, NULL, NULL, NULL, NULL);
bufferevent_write(job->event, buf, len);
free(expanded);
@ -1674,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, (u_char *)buf, len);
screen_write_stop(&ctx);
notify_pane("pane-set-clipboard", wp);
}
if (bufname == NULL || *bufname == '\0')
@ -2393,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
@ -2430,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);

View File

@ -186,11 +186,11 @@ winlink_set_window(struct winlink *wl, struct window *w)
{
if (wl->window != NULL) {
TAILQ_REMOVE(&wl->window->winlinks, wl, wentry);
window_remove_ref(w);
window_remove_ref(wl->window, __func__);
}
TAILQ_INSERT_TAIL(&w->winlinks, wl, wentry);
wl->window = w;
w->references++;
window_add_ref(w, __func__);
}
void
@ -200,7 +200,7 @@ winlink_remove(struct winlinks *wwl, struct winlink *wl)
if (w != NULL) {
TAILQ_REMOVE(&w->winlinks, wl, wentry);
window_remove_ref(w);
window_remove_ref(w, __func__);
}
RB_REMOVE(winlinks, wwl, wl);
@ -355,14 +355,15 @@ window_create_spawn(const char *name, int argc, char **argv, const char *path,
} else
w->name = default_window_name(w);
notify_window("window-pane-changed", w);
return (w);
}
static void
window_destroy(struct window *w)
{
if (!TAILQ_EMPTY(&w->winlinks))
fatalx("window destroyed with winlinks");
log_debug("window @%u destroyed (%d references)", w->id, w->references);
RB_REMOVE(windows, &windows, w);
@ -386,12 +387,36 @@ window_destroy(struct window *w)
free(w);
}
void
window_remove_ref(struct window *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)
{
w->references++;
log_debug("%s: @%u %s, now %d", __func__, w->id, from, w->references);
}
void
window_remove_ref(struct window *w, const char *from)
{
if (w->references == 0)
fatal("bad reference count");
w->references--;
log_debug("%s: @%u %s, now %d", __func__, w->id, from, w->references);
if (w->references == 0)
window_destroy(w);
}
@ -400,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);
}
@ -435,11 +460,14 @@ window_set_active_pane(struct window *w, struct window_pane *wp)
w->active = TAILQ_PREV(w->active, window_panes, entry);
if (w->active == NULL)
w->active = TAILQ_LAST(&w->panes, window_panes);
if (w->active == wp)
if (w->active == wp) {
notify_window("window-pane-changed", w);
return (1);
}
}
w->active->active_point = next_active_point++;
w->active->flags |= PANE_CHANGED;
notify_window("window-pane-changed", w);
return (1);
}
@ -604,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();
@ -615,8 +645,10 @@ window_lost_pane(struct window *w, struct window_pane *wp)
if (w->active == NULL)
w->active = TAILQ_NEXT(wp, entry);
}
if (w->active != NULL)
if (w->active != NULL) {
w->active->flags |= PANE_CHANGED;
notify_window("window-pane-changed", w);
}
} else if (wp == w->last)
w->last = NULL;
}
@ -709,12 +741,12 @@ window_destroy_panes(struct window *w)
}
}
/* Retuns the printable flags on a window, empty string if no flags set. */
char *
window_printable_flags(struct session *s, struct winlink *wl)
const char *
window_printable_flags(struct winlink *wl)
{
char flags[32];
int pos;
struct session *s = wl->session;
static char flags[32];
int pos;
pos = 0;
if (wl->flags & WINLINK_ACTIVITY)
@ -732,7 +764,7 @@ window_printable_flags(struct session *s, struct winlink *wl)
if (wl->window->flags & WINDOW_ZOOMED)
flags[pos++] = 'Z';
flags[pos] = '\0';
return (xstrdup(flags));
return (flags);
}
struct window_pane *
@ -787,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;
@ -815,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
@ -860,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);
@ -889,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 = pty_fork(ptm_fd, &wp->fd, wp->tty, sizeof wp->tty, &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("/");
@ -913,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)
@ -920,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, '/');
@ -961,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,
@ -1001,7 +1041,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
@ -1173,7 +1217,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 };
@ -1186,11 +1231,12 @@ 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);
server_status_window(wp->window);
notify_pane("pane-mode-changed", wp);
return (0);
}
@ -1210,6 +1256,7 @@ window_pane_reset_mode(struct window_pane *wp)
wp->flags |= (PANE_REDRAW|PANE_CHANGED);
server_status_window(wp->window);
notify_pane("pane-mode-changed", wp);
}
void
@ -1224,7 +1271,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;
}
@ -1248,49 +1295,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. */

View File

@ -1,4 +1,4 @@
# $NetBSD: Makefile,v 1.19 2017/04/23 18:22:36 christos Exp $
# $NetBSD: Makefile,v 1.20 2017/10/12 22:17:35 christos Exp $
.include <bsd.own.mk>
@ -22,8 +22,6 @@ cmd-attach-session.c \
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 \
@ -100,6 +98,7 @@ layout-custom.c \
layout-set.c \
layout.c \
log.c \
mode-tree.c \
names.c \
notify.c \
options-table.c \
@ -107,7 +106,6 @@ options.c \
osdep-netbsd.c \
paste.c \
proc.c \
pty.c \
resize.c \
screen-redraw.c \
screen-write.c \
@ -116,7 +114,6 @@ server-client.c \
server-fn.c \
server.c \
session.c \
signal.c \
status.c \
style.c \
tmux.c \
@ -125,9 +122,11 @@ tty-keys.c \
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 \
xterm-keys.c
@ -138,6 +137,7 @@ CPPFLAGS+=-DSUPPORT_UTMP -DSUPPORT_UTMPX
# Files in compat/
SRCS+= imsg-buffer.c
SRCS+= imsg.c
SRCS+= fdforkpty.c
SRCS+= freezero.c
SRCS+= explicit_bzero.c
SRCS+= recallocarray.c
@ -155,10 +155,13 @@ CPPFLAGS+= -I${SRCDIR} -I${.CURDIR}
CPPFLAGS+= \
-DHAVE_ASPRINTF=1 \
-DHAVE_BITSTRING_H=1 \
-DHAVE_B64_NTOP=1 \
-DHAVE_BITSTRING_H=1 \
-DHAVE_CFMAKERAW=1 \
-DHAVE_CLOSEFROM=1 \
-DHAVE_CURSES_H=1 \
-DHAVE_DAEMON=1 \
-DHAVE_DECL_OPTARG=1 \
-DHAVE_DECL_OPTIND=1 \
-DHAVE_DECL_OPTRESET=1 \
-DHAVE_DIRENT_H=1 \
@ -172,6 +175,7 @@ CPPFLAGS+= \
-DHAVE_GETPROGNAME=1 \
-DHAVE_INTTYPES_H=1 \
-DHAVE_INTTYPES_H=1 \
-DHAVE_MEMMEM=1 \
-DHAVE_MEMORY_H=1 \
-DHAVE_PATHS_H=1 \
-DHAVE_PROC_PID=1 \
@ -186,7 +190,10 @@ CPPFLAGS+= \
-DHAVE_STRINGS_H=1 \
-DHAVE_STRING_H=1 \
-DHAVE_STRLCAT=1 \
-DHAVE_STRLCPY= \
-DHAVE_STRLCPY=1 \
-DHAVE_STRNDUP=1 \
-DHAVE_STRSEP=1 \
-DHAVE_STRTONUM=1 \
-DHAVE_SYSCONF=1 \
-DHAVE_SYS_DIR_H=1 \
-DHAVE_SYS_STAT_H=1 \
@ -201,13 +208,13 @@ CPPFLAGS+= \
-DPACKAGE=\"tmux\" \
-DPACKAGE_BUGREPORT=\"\" \
-DPACKAGE_NAME=\"tmux\" \
-DPACKAGE_STRING=\"tmux\ 2.4\" \
-DPACKAGE_STRING=\"tmux\ 2.6\" \
-DPACKAGE_TARNAME=\"tmux\" \
-DPACKAGE_URL=\"\" \
-DPACKAGE_VERSION=\"2.4\" \
-DPACKAGE_VERSION=\"2.6\" \
-DSTDC_HEADERS=1 \
-DTMUX_CONF="\"/etc/tmux.conf\"" \
-DVERSION=\"2.4\" \
-DVERSION=\"2.6\" \
-D_ALL_SOURCE=1 \
-D_GNU_SOURCE=1 \
-D_OPENBSD_SOURCE \