Merge branch 'master' into 3.0-rc

pull/1869/head
Nicholas Marriott 2019-07-29 10:51:30 +01:00
commit da552eb73b
73 changed files with 1841 additions and 937 deletions

View File

@ -2,13 +2,20 @@
Before opening an issue, please ensure that:
- Your problem is a specific problem or question or suggestion, not a general
complaint.
- `$TERM` inside tmux is screen, screen-256color, tmux or tmux-256color. Check
by running `echo $TERM` inside tmux.
- You can reproduce the problem with the latest tmux release, or a build from
Git master.
- Your question or issue is not covered in the manual (run man tmux).
- Your question or issue is not covered [in the
manual](https://man.openbsd.org/tmux.1) (run `man tmux`).
- Your problem is not mentioned in the [CHANGES
file](https://raw.githubusercontent.com/tmux/tmux/master/CHANGES) file.
- Nobody else has opened the same issue recently.

View File

@ -3,6 +3,9 @@
Please read https://github.com/tmux/tmux/blob/master/.github/CONTRIBUTING.md
before opening an issue.
If you have upgraded, make sure your issue is not covered in the CHANGES file
for your version: https://raw.githubusercontent.com/tmux/tmux/master/CHANGES
Describe the problem and the steps to reproduce. Add a minimal tmux config if
necessary. Screenshots can be helpful, but no more than one or two.

19
.github/README.md vendored
View File

@ -30,7 +30,8 @@ configure with `--enable-utempter` to enable this.
### From version control
To get and build the latest from version control:
To get and build the latest from version control - note that this requires
`autoconf`, `automake` and `pkg-config`:
~~~bash
git clone https://github.com/tmux/tmux.git
@ -39,10 +40,6 @@ sh autogen.sh
./configure && make
~~~
(Note that this requires at least a working C compiler, `make`, `autoconf`,
`automake`, `pkg-config` as well as `libevent` and `ncurses` libraries and
headers.)
## Contributing
Bug reports, feature suggestions and especially code contributions are most
@ -50,14 +47,12 @@ welcome. Please send by email to:
tmux-users@googlegroups.com
Or open a GitHub issue or pull request.
Or open a GitHub issue or pull request. **Please read [this
document](CONTRIBUTING.md) before opening an issue.**
There is [a TODO list](https://github.com/tmux/tmux/wiki/Contributing) which
explains some ideas for tmux not yet developed. Please feel free to ask for
clarifications on the mailing list if you're thinking of working on these or
need further information.
Please read the CONTRIBUTING file before opening an issue.
There is [a list of suggestions for contributions](https://github.com/tmux/tmux/wiki/Contributing).
Please feel free to ask on the mailing list if you're thinking of working on something or need
further information.
## Documentation

53
CHANGES
View File

@ -1,3 +1,56 @@
CHANGES FROM 3.0 to X.X
* Expand arguments to C and s format modifiers to match the m modifier.
* Add support for underscore colours (Setulc capability must be added with
terminal-overrides as described in tmux(1)).
* Add a "fill" style attribute for the fill colour of the drawing area (where
appropriate).
* New -H flag to send-keys to send literal keys.
* Format variables for pane mouse modes (mouse_utf8_flag and mouse_sgr_flag)
and for origin mode (origin_flag).
* Add -F to refresh-client for flags for control mode clients, only one flag
(no-output) supported at the moment.
* Add a few vi(1) keys for menus.
* Add pane options, set with set-option -p and displayed with show-options -p.
Pane options inherit from window options (so every pane option is also
a window option). The pane style is now configured by setting window-style
and window-active-style in the pane options; select-pane -P and -g now change
the option but are no longer documented.
* Do not document set-window-option and show-window-options. set-option -w and
show-options -w should be used instead.
* Add a -A flag to show-options to show parent options as well (they are marked
with a *).
* Resize panes lazily - do not resize unless they are in an attached, active
window.
* Add regular expression support for the format search, match and substitute
modifiers and make them able to ignore case. find-window now accepts -r to
use regular expressions.
* Do not use $TMUX to find the session because for windows in multiple sessions
it is wrong as often as it is right, and for windows in one session it is
pointless. Instead use TMUX_PANE if it is present.
* Do not always resize the window back to its original size after applying a
layout, keep it at the layout size until it must be resized (for example when
attached and window-size is not manual).
* Add new-session -X and attach-session -x to send SIGHUP to parent when
detaching (like detach-client -P).
* Support for octal escapes in strings (such as \007) and improve list-keys
output so it parses correctly if copied into a configuration file.
CHANGES FROM 2.9 to 3.0
* INCOMPATIBLE: Add a new {} syntax to the configuration file. This is a string

View File

@ -151,6 +151,7 @@ dist_tmux_SOURCES = \
options.c \
paste.c \
proc.c \
regsub.c \
resize.c \
screen-redraw.c \
screen-write.c \

6
README
View File

@ -26,16 +26,14 @@ To build and install tmux from a release tarball, use:
tmux can use the utempter library to update utmp(5), if it is installed - run
configure with --enable-utempter to enable this.
To get and build the latest from version control:
To get and build the latest from version control - note that this requires
autoconf, automake and pkg-config:
$ git clone https://github.com/tmux/tmux.git
$ cd tmux
$ sh autogen.sh
$ ./configure && make
(Note that this requires at least a working C compiler, make, autoconf,
automake, pkg-config as well as libevent and ncurses libraries and headers.)
* Contributing
Bug reports, feature suggestions and especially code contributions are most

View File

@ -37,6 +37,7 @@ TAILQ_HEAD(args_values, args_value);
struct args_entry {
u_char flag;
struct args_values values;
u_int count;
RB_ENTRY(args_entry) entry;
};
@ -173,6 +174,7 @@ args_print(struct args *args)
size_t len;
char *buf;
int i;
u_int j;
struct args_entry *entry;
struct args_value *value;
@ -186,7 +188,8 @@ args_print(struct args *args)
if (*buf == '\0')
args_print_add(&buf, &len, "-");
args_print_add(&buf, &len, "%c", entry->flag);
for (j = 0; j < entry->count; j++)
args_print_add(&buf, &len, "%c", entry->flag);
}
/* Then the flags with arguments. */
@ -212,12 +215,14 @@ args_escape(const char *s)
if (*s == '\0')
return (xstrdup(s));
if ((strchr(quoted, s[0]) != NULL || s[0] == '~') && s[1] == '\0') {
if (s[0] != ' ' &&
(strchr(quoted, s[0]) != NULL || s[0] == '~') &&
s[1] == '\0') {
xasprintf(&escaped, "\\%c", s[0]);
return (escaped);
}
flags = VIS_OCTAL|VIS_TAB|VIS_NL;
flags = VIS_OCTAL|VIS_CSTYLE|VIS_TAB|VIS_NL;
if (s[strcspn(s, quoted)] != '\0')
flags |= VIS_DQ;
utf8_stravis(&escaped, s, flags);
@ -241,7 +246,12 @@ args_escape(const char *s)
int
args_has(struct args *args, u_char ch)
{
return (args_find(args, ch) != NULL);
struct args_entry *entry;
entry = args_find(args, ch);
if (entry == NULL)
return (0);
return (entry->count);
}
/* Set argument value in the arguments tree. */
@ -255,9 +265,11 @@ args_set(struct args *args, u_char ch, const char *s)
if (entry == NULL) {
entry = xcalloc(1, sizeof *entry);
entry->flag = ch;
entry->count = 1;
TAILQ_INIT(&entry->values);
RB_INSERT(args_tree, &args->tree, entry);
}
} else
entry->count++;
if (s != NULL) {
value = xcalloc(1, sizeof *value);

10
cfg.c
View File

@ -52,7 +52,7 @@ cfg_done(__unused struct cmdq_item *item, __unused void *data)
cfg_show_causes(RB_MIN(sessions, &sessions));
if (cfg_item != NULL)
cfg_item->flags &= ~CMDQ_WAITING;
cmdq_continue(cfg_item);
status_prompt_load_history();
@ -90,14 +90,14 @@ start_cfg(void)
}
if (cfg_file == NULL)
load_cfg(TMUX_CONF, NULL, NULL, CMD_PARSE_QUIET, NULL);
load_cfg(TMUX_CONF, c, NULL, CMD_PARSE_QUIET, NULL);
if (cfg_file == NULL && (home = find_home()) != NULL) {
xasprintf(&cfg_file, "%s/.tmux.conf", home);
flags = CMD_PARSE_QUIET;
}
if (cfg_file != NULL)
load_cfg(cfg_file, NULL, NULL, flags, NULL);
load_cfg(cfg_file, c, NULL, flags, NULL);
cmdq_append(NULL, cmdq_get_callback(cfg_done, NULL));
}
@ -126,6 +126,8 @@ load_cfg(const char *path, struct client *c, struct cmdq_item *item, int flags,
pi.flags = flags;
pi.file = path;
pi.line = 1;
pi.item = item;
pi.c = c;
pr = cmd_parse_from_file(f, &pi);
fclose(f);
@ -145,7 +147,7 @@ load_cfg(const char *path, struct client *c, struct cmdq_item *item, int flags,
if (item != NULL)
cmdq_insert_after(item, new_item0);
else
cmdq_append(c, new_item0);
cmdq_append(NULL, new_item0);
cmd_list_free(pr->cmdlist);
if (new_item != NULL)

View File

@ -202,7 +202,7 @@ client_exit_message(void)
case CLIENT_EXIT_TERMINATED:
return ("terminated");
case CLIENT_EXIT_LOST_SERVER:
return ("lost server");
return ("server exited unexpectedly");
case CLIENT_EXIT_EXITED:
return ("exited");
case CLIENT_EXIT_SERVER_EXITED:
@ -436,7 +436,7 @@ client_stdin_callback(__unused int fd, __unused short events,
struct msg_stdin_data data;
data.size = read(STDIN_FILENO, data.data, sizeof data.data);
if (data.size < 0 && (errno == EINTR || errno == EAGAIN))
if (data.size == -1 && (errno == EINTR || errno == EAGAIN))
return;
proc_send(client_peer, MSG_STDIN, -1, &data, sizeof data);

View File

@ -37,8 +37,8 @@ const struct cmd_entry cmd_attach_session_entry = {
.name = "attach-session",
.alias = "attach",
.args = { "c:dErt:", 0, 0 },
.usage = "[-dEr] [-c working-directory] " CMD_TARGET_SESSION_USAGE,
.args = { "c:dErt:x", 0, 0 },
.usage = "[-dErx] [-c working-directory] " CMD_TARGET_SESSION_USAGE,
/* -t is special */
@ -48,7 +48,7 @@ const struct cmd_entry cmd_attach_session_entry = {
enum cmd_retval
cmd_attach_session(struct cmdq_item *item, const char *tflag, int dflag,
int rflag, const char *cflag, int Eflag)
int xflag, int rflag, const char *cflag, int Eflag)
{
struct cmd_find_state *current = &item->shared->current;
enum cmd_find_type type;
@ -58,6 +58,7 @@ cmd_attach_session(struct cmdq_item *item, const char *tflag, int dflag,
struct winlink *wl;
struct window_pane *wp;
char *cause;
enum msgtype msgtype;
if (RB_EMPTY(&sessions)) {
cmdq_error(item, "no sessions");
@ -102,11 +103,15 @@ cmd_attach_session(struct cmdq_item *item, const char *tflag, int dflag,
c->last_session = c->session;
if (c->session != NULL) {
if (dflag) {
if (dflag || xflag) {
if (xflag)
msgtype = MSG_DETACHKILL;
else
msgtype = MSG_DETACH;
TAILQ_FOREACH(c_loop, &clients, entry) {
if (c_loop->session != s || c == c_loop)
continue;
server_client_detach(c_loop, MSG_DETACH);
server_client_detach(c_loop, msgtype);
}
}
if (!Eflag)
@ -131,11 +136,15 @@ cmd_attach_session(struct cmdq_item *item, const char *tflag, int dflag,
if (rflag)
c->flags |= CLIENT_READONLY;
if (dflag) {
if (dflag || xflag) {
if (xflag)
msgtype = MSG_DETACHKILL;
else
msgtype = MSG_DETACH;
TAILQ_FOREACH(c_loop, &clients, entry) {
if (c_loop->session != s || c == c_loop)
continue;
server_client_detach(c_loop, MSG_DETACH);
server_client_detach(c_loop, msgtype);
}
}
if (!Eflag)
@ -169,6 +178,6 @@ cmd_attach_session_exec(struct cmd *self, struct cmdq_item *item)
struct args *args = self->args;
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')));
args_has(args, 'd'), args_has(args, 'x'), args_has(args, 'r'),
args_get(args, 'c'), args_has(args, 'E')));
}

View File

@ -77,6 +77,8 @@ cmd_break_pane_exec(struct cmd *self, struct cmdq_item *item)
layout_close_pane(wp);
w = wp->window = window_create(w->sx, w->sy);
options_set_parent(wp->options, w->options);
wp->flags |= PANE_STYLECHANGED;
TAILQ_INSERT_HEAD(&w->panes, wp, entry);
w->active = wp;

View File

@ -109,8 +109,7 @@ cmd_display_message_exec(struct cmd *self, struct cmdq_item *item)
format_defaults(ft, target_c, s, wl, wp);
if (args_has(args, 'a')) {
if (item != NULL)
format_each(ft, cmd_display_message_each, item);
format_each(ft, cmd_display_message_each, item);
return (CMD_RETURN_NORMAL);
}

View File

@ -188,7 +188,7 @@ cmd_display_panes_free(struct client *c)
struct cmd_display_panes_data *cdata = c->overlay_data;
if (cdata->item != NULL)
cdata->item->flags &= ~CMDQ_WAITING;
cmdq_continue(cdata->item);
free(cdata->command);
free(cdata);
}

View File

@ -32,8 +32,8 @@ const struct cmd_entry cmd_find_window_entry = {
.name = "find-window",
.alias = "findw",
.args = { "CNt:TZ", 1, 1 },
.usage = "[-CNTZ] " CMD_TARGET_PANE_USAGE " match-string",
.args = { "CNrt:TZ", 1, 1 },
.usage = "[-CNrTZ] " CMD_TARGET_PANE_USAGE " match-string",
.target = { 't', CMD_FIND_PANE, 0 },
@ -57,30 +57,59 @@ cmd_find_window_exec(struct cmd *self, struct cmdq_item *item)
if (!C && !N && !T)
C = N = T = 1;
if (C && N && T) {
xasprintf(&filter,
"#{||:"
"#{C:%s},#{||:#{m:*%s*,#{window_name}},"
"#{m:*%s*,#{pane_title}}}}",
s, s, s);
} else if (C && N) {
xasprintf(&filter,
"#{||:#{C:%s},#{m:*%s*,#{window_name}}}",
s, s);
} else if (C && T) {
xasprintf(&filter,
"#{||:#{C:%s},#{m:*%s*,#{pane_title}}}",
s, s);
} else if (N && T) {
xasprintf(&filter,
"#{||:#{m:*%s*,#{window_name}},#{m:*%s*,#{pane_title}}}",
s, s);
} else if (C)
xasprintf(&filter, "#{C:%s}", s);
else if (N)
xasprintf(&filter, "#{m:*%s*,#{window_name}}", s);
else
xasprintf(&filter, "#{m:*%s*,#{pane_title}}", s);
if (!args_has(args, 'r')) {
if (C && N && T) {
xasprintf(&filter,
"#{||:"
"#{C:%s},#{||:#{m:*%s*,#{window_name}},"
"#{m:*%s*,#{pane_title}}}}",
s, s, s);
} else if (C && N) {
xasprintf(&filter,
"#{||:#{C:%s},#{m:*%s*,#{window_name}}}",
s, s);
} else if (C && T) {
xasprintf(&filter,
"#{||:#{C:%s},#{m:*%s*,#{pane_title}}}",
s, s);
} else if (N && T) {
xasprintf(&filter,
"#{||:#{m:*%s*,#{window_name}},"
"#{m:*%s*,#{pane_title}}}",
s, s);
} else if (C)
xasprintf(&filter, "#{C:%s}", s);
else if (N)
xasprintf(&filter, "#{m:*%s*,#{window_name}}", s);
else
xasprintf(&filter, "#{m:*%s*,#{pane_title}}", s);
} else {
if (C && N && T) {
xasprintf(&filter,
"#{||:"
"#{C/r:%s},#{||:#{m/r:%s,#{window_name}},"
"#{m/r:%s,#{pane_title}}}}",
s, s, s);
} else if (C && N) {
xasprintf(&filter,
"#{||:#{C/r:%s},#{m/r:%s,#{window_name}}}",
s, s);
} else if (C && T) {
xasprintf(&filter,
"#{||:#{C/r:%s},#{m/r:%s,#{pane_title}}}",
s, s);
} else if (N && T) {
xasprintf(&filter,
"#{||:#{m/r:%s,#{window_name}},"
"#{m/r:%s,#{pane_title}}}",
s, s);
} else if (C)
xasprintf(&filter, "#{C/r:%s}", s);
else if (N)
xasprintf(&filter, "#{m/r:%s,#{window_name}}", s);
else
xasprintf(&filter, "#{m/r:%s,#{pane_title}}", s);
}
new_args = args_parse("", 1, &argv);
if (args_has(args, 'Z'))

View File

@ -75,38 +75,12 @@ static const char *cmd_find_pane_table[][2] = {
{ NULL, NULL }
};
/* Get session from TMUX if present. */
static struct session *
cmd_find_try_TMUX(struct client *c)
{
struct environ_entry *envent;
char tmp[256];
long long pid;
u_int session;
struct session *s;
envent = environ_find(c->environ, "TMUX");
if (envent == NULL)
return (NULL);
if (sscanf(envent->value, "%255[^,],%lld,%d", tmp, &pid, &session) != 3)
return (NULL);
if (pid != getpid())
return (NULL);
log_debug("%s: client %p TMUX %s (session $%u)", __func__, c,
envent->value, session);
s = session_find_by_id(session);
if (s != NULL)
log_debug("%s: session $%u still exists", __func__, s->id);
return (s);
}
/* Find pane containing client if any. */
static struct window_pane *
cmd_find_inside_pane(struct client *c)
{
struct window_pane *wp;
struct environ_entry *envent;
if (c == NULL)
return (NULL);
@ -115,6 +89,11 @@ cmd_find_inside_pane(struct client *c)
if (wp->fd != -1 && strcmp(wp->tty, c->ttyname) == 0)
break;
}
if (wp == NULL) {
envent = environ_find(c->environ, "TMUX_PANE");
if (envent != NULL)
wp = window_pane_find_by_id_str(envent->value);
}
if (wp != NULL)
log_debug("%s: got pane %%%u (%s)", __func__, wp->id, wp->tty);
return (wp);
@ -879,8 +858,6 @@ cmd_find_from_mouse(struct cmd_find_state *fs, struct mouse_event *m, int flags)
int
cmd_find_from_client(struct cmd_find_state *fs, struct client *c, int flags)
{
struct session *s;
struct winlink *wl;
struct window_pane *wp;
/* If no client, treat as from nothing. */
@ -902,30 +879,6 @@ cmd_find_from_client(struct cmd_find_state *fs, struct client *c, int flags)
if (wp == NULL)
goto unknown_pane;
/* If we have a session in TMUX, see if it has this pane. */
s = cmd_find_try_TMUX(c);
if (s != NULL) {
RB_FOREACH(wl, winlinks, &s->windows) {
if (window_has_pane(wl->window, wp))
break;
}
if (wl != NULL) {
log_debug("%s: session $%u has pane %%%u", __func__,
s->id, wp->id);
fs->s = s;
fs->wl = s->curw; /* use current session */
fs->w = fs->wl->window;
fs->wp = fs->w->active; /* use active pane */
cmd_find_log_state(__func__, fs);
return (0);
} else {
log_debug("%s: session $%u does not have pane %%%u",
__func__, s->id, wp->id);
}
}
/*
* Don't have a session, or it doesn't have this pane. Try all
* sessions.
@ -947,17 +900,7 @@ cmd_find_from_client(struct cmd_find_state *fs, struct client *c, int flags)
return (0);
unknown_pane:
/*
* We're not running in a known pane, but maybe this client has TMUX
* in the environment. That'd give us a session.
*/
s = cmd_find_try_TMUX(c);
if (s != NULL) {
cmd_find_from_session(fs, s, flags);
return (0);
}
/* Otherwise we need to guess. */
/* We can't find the pane so need to guess. */
return (cmd_find_from_nothing(fs, flags));
}
@ -1005,6 +948,8 @@ cmd_find_target(struct cmd_find_state *fs, struct cmdq_item *item,
strlcat(tmp, "CANFAIL,", sizeof tmp);
if (*tmp != '\0')
tmp[strlen(tmp) - 1] = '\0';
else
strlcat(tmp, "NONE", sizeof tmp);
log_debug("%s: target %s, type %s, item %p, flags %s", __func__,
target == NULL ? "none" : target, s, item, tmp);

View File

@ -67,10 +67,11 @@ cmd_if_shell_exec(struct cmd *self, struct cmdq_item *item)
struct cmd_if_shell_data *cdata;
char *shellcmd, *cmd;
struct cmdq_item *new_item;
struct cmd_find_state *fs = &item->target;
struct client *c = cmd_find_client(item, NULL, 1);
struct session *s = item->target.s;
struct winlink *wl = item->target.wl;
struct window_pane *wp = item->target.wp;
struct session *s = fs->s;
struct winlink *wl = fs->wl;
struct window_pane *wp = fs->wp;
struct cmd_parse_input pi;
struct cmd_parse_result *pr;
@ -92,7 +93,7 @@ cmd_if_shell_exec(struct cmd *self, struct cmdq_item *item)
pi.line = self->line;
pi.item = item;
pi.c = c;
cmd_find_copy_state(&pi.fs, &item->target);
cmd_find_copy_state(&pi.fs, fs);
pr = cmd_parse_from_string(cmd, &pi);
switch (pr->status) {
@ -103,7 +104,7 @@ cmd_if_shell_exec(struct cmd *self, struct cmdq_item *item)
free(pr->error);
return (CMD_RETURN_ERROR);
case CMD_PARSE_SUCCESS:
new_item = cmdq_get_command(pr->cmdlist, NULL, m, 0);
new_item = cmdq_get_command(pr->cmdlist, fs, m, 0);
cmdq_insert_after(item, new_item);
cmd_list_free(pr->cmdlist);
break;
@ -120,7 +121,10 @@ cmd_if_shell_exec(struct cmd *self, struct cmdq_item *item)
cdata->cmd_else = NULL;
memcpy(&cdata->mouse, m, sizeof cdata->mouse);
cdata->client = item->client;
if (!args_has(args, 'b'))
cdata->client = item->client;
else
cdata->client = c;
if (cdata->client != NULL)
cdata->client->references++;
@ -137,7 +141,7 @@ cmd_if_shell_exec(struct cmd *self, struct cmdq_item *item)
cdata->input.c = c;
if (cdata->input.c != NULL)
cdata->input.c->references++;
cmd_find_copy_state(&cdata->input.fs, &item->target);
cmd_find_copy_state(&cdata->input.fs, fs);
if (job_run(shellcmd, s, server_client_get_cwd(item->client, s), NULL,
cmd_if_shell_callback, cmd_if_shell_free, cdata, 0) == NULL) {
@ -195,7 +199,7 @@ cmd_if_shell_callback(struct job *job)
out:
if (cdata->item != NULL)
cdata->item->flags &= ~CMDQ_WAITING;
cmdq_continue(cdata->item);
}
static void

View File

@ -139,6 +139,8 @@ cmd_join_pane_exec(struct cmd *self, struct cmdq_item *item)
TAILQ_REMOVE(&src_w->panes, src_wp, entry);
src_wp->window = dst_w;
options_set_parent(src_wp->options, dst_w->options);
src_wp->flags |= PANE_STYLECHANGED;
TAILQ_INSERT_AFTER(&dst_w->panes, dst_wp, src_wp, entry);
layout_assign_pane(lc, src_wp);

View File

@ -176,7 +176,7 @@ cmd_load_buffer_callback(struct client *c, int closed, void *data)
free(cause);
}
out:
cdata->item->flags &= ~CMDQ_WAITING;
cmdq_continue(cdata->item);
free(cdata->bufname);
free(cdata);

View File

@ -39,8 +39,8 @@ const struct cmd_entry cmd_new_session_entry = {
.name = "new-session",
.alias = "new",
.args = { "Ac:dDEF:n:Ps:t:x:y:", 0, -1 },
.usage = "[-AdDEP] [-c start-directory] [-F format] [-n window-name] "
.args = { "Ac:dDEF:n:Ps:t:x:Xy:", 0, -1 },
.usage = "[-AdDEPX] [-c start-directory] [-F format] [-n window-name] "
"[-s session-name] " CMD_TARGET_SESSION_USAGE " [-x width] "
"[-y height] [command]",
@ -105,7 +105,8 @@ cmd_new_session_exec(struct cmd *self, struct cmdq_item *item)
if (args_has(args, 'A')) {
retval = cmd_attach_session(item,
newname, args_has(args, 'D'),
0, NULL, args_has(args, 'E'));
args_has(args, 'X'), 0, NULL,
args_has(args, 'E'));
free(newname);
return (retval);
}

View File

@ -77,6 +77,8 @@ static char *cmd_parse_get_error(const char *, u_int, const char *);
static void cmd_parse_free_command(struct cmd_parse_command *);
static struct cmd_parse_commands *cmd_parse_new_commands(void);
static void cmd_parse_free_commands(struct cmd_parse_commands *);
static void cmd_parse_print_commands(struct cmd_parse_input *, u_int,
struct cmd_list *);
%}
@ -507,6 +509,22 @@ cmd_parse_get_error(const char *file, u_int line, const char *error)
return (s);
}
static void
cmd_parse_print_commands(struct cmd_parse_input *pi, u_int line,
struct cmd_list *cmdlist)
{
char *s;
if (pi->item != NULL && (pi->flags & CMD_PARSE_VERBOSE)) {
s = cmd_list_print(cmdlist, 0);
if (pi->file != NULL)
cmdq_print(pi->item, "%s:%u: %s", pi->file, line, s);
else
cmdq_print(pi->item, "%u: %s", line, s);
free(s);
}
}
static void
cmd_parse_free_command(struct cmd_parse_command *cmd)
{
@ -663,6 +681,7 @@ cmd_parse_build_commands(struct cmd_parse_commands *cmds,
if (cmdlist == NULL || cmd->line != line) {
if (cmdlist != NULL) {
cmd_parse_print_commands(pi, line, cmdlist);
cmd_list_move(result, cmdlist);
cmd_list_free(cmdlist);
}
@ -682,6 +701,7 @@ cmd_parse_build_commands(struct cmd_parse_commands *cmds,
cmd_list_append(cmdlist, add);
}
if (cmdlist != NULL) {
cmd_parse_print_commands(pi, line, cmdlist);
cmd_list_move(result, cmdlist);
cmd_list_free(cmdlist);
}
@ -1124,17 +1144,54 @@ error:
static int
yylex_token_escape(char **buf, size_t *len)
{
int ch, type;
int ch, type, o2, o3;
u_int size, i, tmp;
char s[9];
struct utf8_data ud;
switch (ch = yylex_getc()) {
ch = yylex_getc();
if (ch >= '4' && ch <= '7') {
yyerror("invalid octal escape");
return (0);
}
if (ch >= '0' && ch <= '3') {
o2 = yylex_getc();
if (o2 >= '0' && o2 <= '7') {
o3 = yylex_getc();
if (o3 >= '0' && o3 <= '7') {
ch = 64 * (ch - '0') +
8 * (o2 - '0') +
(o3 - '0');
yylex_append1(buf, len, ch);
return (1);
}
}
yyerror("invalid octal escape");
return (0);
}
switch (ch) {
case EOF:
return (0);
case 'a':
ch = '\a';
break;
case 'b':
ch = '\b';
break;
case 'e':
ch = '\033';
break;
case 'f':
ch = '\f';
break;
case 's':
ch = ' ';
break;
case 'v':
ch = '\v';
break;
case 'r':
ch = '\r';
break;

View File

@ -156,6 +156,13 @@ cmdq_insert_hook(struct session *s, struct cmdq_item *item,
free(name);
}
/* Continue processing command queue. */
void
cmdq_continue(struct cmdq_item *item)
{
item->flags &= ~CMDQ_WAITING;
}
/* Remove an item. */
static void
cmdq_remove(struct cmdq_item *item)

View File

@ -19,6 +19,7 @@
#include <sys/types.h>
#include <stdlib.h>
#include <string.h>
#include "tmux.h"
@ -33,8 +34,9 @@ const struct cmd_entry cmd_refresh_client_entry = {
.name = "refresh-client",
.alias = "refresh",
.args = { "cC:DlLRSt:U", 0, 1 },
.usage = "[-cDlLRSU] [-C size] " CMD_TARGET_CLIENT_USAGE " [adjustment]",
.args = { "cC:DF:lLRSt:U", 0, 1 },
.usage = "[-cDlLRSU] [-C XxY] [-F flags] " CMD_TARGET_CLIENT_USAGE
" [adjustment]",
.flags = CMD_AFTERHOOK,
.exec = cmd_refresh_client_exec
@ -48,6 +50,7 @@ cmd_refresh_client_exec(struct cmd *self, struct cmdq_item *item)
struct tty *tty;
struct window *w;
const char *size, *errstr;
char *copy, *next, *s;
u_int x, y, adjust;
if ((c = cmd_find_client(item, args_get(args, 't'), 0)) == NULL)
@ -107,28 +110,43 @@ cmd_refresh_client_exec(struct cmd *self, struct cmdq_item *item)
if (args_has(args, 'l')) {
if (c->session != NULL)
tty_putcode_ptr2(&c->tty, TTYC_MS, "", "?");
} else if (args_has(args, 'C')) {
if ((size = args_get(args, 'C')) == NULL) {
cmdq_error(item, "missing size");
return (CMD_RETURN_ERROR);
return (CMD_RETURN_NORMAL);
}
if (args_has(args, 'C') || args_has(args, 'F')) {
if (args_has(args, 'C')) {
if (!(c->flags & CLIENT_CONTROL)) {
cmdq_error(item, "not a control client");
return (CMD_RETURN_ERROR);
}
size = args_get(args, 'C');
if (sscanf(size, "%u,%u", &x, &y) != 2 &&
sscanf(size, "%ux%u", &x, &y) != 2) {
cmdq_error(item, "bad size argument");
return (CMD_RETURN_ERROR);
}
if (x < WINDOW_MINIMUM || x > WINDOW_MAXIMUM ||
y < WINDOW_MINIMUM || y > WINDOW_MAXIMUM) {
cmdq_error(item, "size too small or too big");
return (CMD_RETURN_ERROR);
}
tty_set_size(&c->tty, x, y);
c->flags |= CLIENT_SIZECHANGED;
recalculate_sizes();
}
if (sscanf(size, "%u,%u", &x, &y) != 2 &&
sscanf(size, "%ux%u", &x, &y)) {
cmdq_error(item, "bad size argument");
return (CMD_RETURN_ERROR);
if (args_has(args, 'F')) {
if (!(c->flags & CLIENT_CONTROL)) {
cmdq_error(item, "not a control client");
return (CMD_RETURN_ERROR);
}
s = copy = xstrdup(args_get(args, 'F'));
while ((next = strsep(&s, ",")) != NULL) {
/* Unknown flags are ignored. */
if (strcmp(next, "no-output") == 0)
c->flags |= CLIENT_CONTROL_NOOUTPUT;
}
free(copy);
}
if (x < WINDOW_MINIMUM || x > WINDOW_MAXIMUM ||
y < WINDOW_MINIMUM || y > WINDOW_MAXIMUM) {
cmdq_error(item, "size too small or too big");
return (CMD_RETURN_ERROR);
}
if (!(c->flags & CLIENT_CONTROL)) {
cmdq_error(item, "not a control client");
return (CMD_RETURN_ERROR);
}
tty_set_size(&c->tty, x, y);
c->flags |= CLIENT_SIZECHANGED;
recalculate_sizes();
return (CMD_RETURN_NORMAL);
}

View File

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

View File

@ -155,7 +155,7 @@ cmd_run_shell_callback(struct job *job)
free(msg);
if (cdata->item != NULL)
cdata->item->flags &= ~CMDQ_WAITING;
cmdq_continue(cdata->item);
}
static void

View File

@ -33,8 +33,8 @@ const struct cmd_entry cmd_select_pane_entry = {
.name = "select-pane",
.alias = "selectp",
.args = { "DdegLlMmP:RT:t:U", 0, 0 },
.usage = "[-DdegLlMmRU] [-P style] [-T title] " CMD_TARGET_PANE_USAGE,
.args = { "DdegLlMmP:RT:t:U", 0, 0 }, /* -P and -g deprecated */
.usage = "[-DdeLlMmRU] [-T title] " CMD_TARGET_PANE_USAGE,
.target = { 't', CMD_FIND_PANE, 0 },
@ -90,9 +90,10 @@ cmd_select_pane_exec(struct cmd *self, struct cmdq_item *item)
struct window *w = wl->window;
struct session *s = item->target.s;
struct window_pane *wp = item->target.wp, *lastwp, *markedwp;
struct style *sy = &wp->style;
char *pane_title;
const char *style;
struct style *sy;
struct options_entry *o;
if (self->entry == &cmd_last_pane_entry || args_has(args, 'l')) {
lastwp = w->last;
@ -144,15 +145,18 @@ cmd_select_pane_exec(struct cmd *self, struct cmdq_item *item)
if (args_has(self->args, 'P') || args_has(self->args, 'g')) {
if ((style = args_get(args, 'P')) != NULL) {
style_set(sy, &grid_default_cell);
if (style_parse(sy, &grid_default_cell, style) == -1) {
o = options_set_style(wp->options, "window-style", 0,
style);
if (o == NULL) {
cmdq_error(item, "bad style: %s", style);
return (CMD_RETURN_ERROR);
}
wp->flags |= PANE_REDRAW;
wp->flags |= (PANE_REDRAW|PANE_STYLECHANGED);
}
if (args_has(self->args, 'g'))
if (args_has(self->args, 'g')) {
sy = options_get_style(wp->options, "window-style");
cmdq_print(item, "%s", style_tostring(sy));
}
return (CMD_RETURN_NORMAL);
}

View File

@ -33,8 +33,8 @@ const struct cmd_entry cmd_send_keys_entry = {
.name = "send-keys",
.alias = "send",
.args = { "lXRMN:t:", 0, -1 },
.usage = "[-lXRM] [-N repeat-count] " CMD_TARGET_PANE_USAGE " key ...",
.args = { "HlXRMN:t:", 0, -1 },
.usage = "[-HlXRM] [-N repeat-count] " CMD_TARGET_PANE_USAGE " key ...",
.target = { 't', CMD_FIND_PANE, 0 },
@ -56,7 +56,7 @@ const struct cmd_entry cmd_send_prefix_entry = {
};
static struct cmdq_item *
cmd_send_keys_inject(struct client *c, struct cmd_find_state *fs,
cmd_send_keys_inject_key(struct client *c, struct cmd_find_state *fs,
struct cmdq_item *item, key_code key)
{
struct window_mode_entry *wme;
@ -81,20 +81,56 @@ cmd_send_keys_inject(struct client *c, struct cmd_find_state *fs,
return (item);
}
static struct cmdq_item *
cmd_send_keys_inject_string(struct client *c, struct cmd_find_state *fs,
struct cmdq_item *item, struct args *args, int i)
{
const char *s = args->argv[i];
struct utf8_data *ud, *uc;
wchar_t wc;
key_code key;
char *endptr;
long n;
int literal;
if (args_has(args, 'H')) {
n = strtol(s, &endptr, 16);
if (*s =='\0' || n < 0 || n > 0xff || *endptr != '\0')
return (item);
return (cmd_send_keys_inject_key(c, fs, item, KEYC_LITERAL|n));
}
literal = args_has(args, 'l');
if (!literal) {
key = key_string_lookup_string(s);
if (key != KEYC_NONE && key != KEYC_UNKNOWN)
return (cmd_send_keys_inject_key(c, fs, item, key));
literal = 1;
}
if (literal) {
ud = utf8_fromcstr(s);
for (uc = ud; uc->size != 0; uc++) {
if (utf8_combine(uc, &wc) != UTF8_DONE)
continue;
item = cmd_send_keys_inject_key(c, fs, item, wc);
}
free(ud);
}
return (item);
}
static enum cmd_retval
cmd_send_keys_exec(struct cmd *self, struct cmdq_item *item)
{
struct args *args = self->args;
struct client *c = cmd_find_client(item, NULL, 1);
struct cmd_find_state *fs = &item->target;
struct window_pane *wp = item->target.wp;
struct session *s = item->target.s;
struct winlink *wl = item->target.wl;
struct mouse_event *m = &item->shared->mouse;
struct cmd_find_state *fs = &item->target;
struct window_mode_entry *wme = TAILQ_FIRST(&wp->modes);
struct utf8_data *ud, *uc;
wchar_t wc;
int i, literal;
int i;
key_code key;
u_int np = 1;
char *cause = NULL;
@ -141,7 +177,7 @@ cmd_send_keys_exec(struct cmd *self, struct cmdq_item *item)
key = options_get_number(s->options, "prefix2");
else
key = options_get_number(s->options, "prefix");
cmd_send_keys_inject(c, fs, item, key);
cmd_send_keys_inject_key(c, fs, item, key);
return (CMD_RETURN_NORMAL);
}
@ -151,28 +187,8 @@ cmd_send_keys_exec(struct cmd *self, struct cmdq_item *item)
}
for (; np != 0; np--) {
for (i = 0; i < args->argc; i++) {
literal = args_has(args, 'l');
if (!literal) {
key = key_string_lookup_string(args->argv[i]);
if (key != KEYC_NONE && key != KEYC_UNKNOWN) {
item = cmd_send_keys_inject(c, fs, item,
key);
} else
literal = 1;
}
if (literal) {
ud = utf8_fromcstr(args->argv[i]);
for (uc = ud; uc->size != 0; uc++) {
if (utf8_combine(uc, &wc) != UTF8_DONE)
continue;
item = cmd_send_keys_inject(c, fs, item,
wc);
}
free(ud);
}
}
for (i = 0; i < args->argc; i++)
item = cmd_send_keys_inject_string(c, fs, item, args, i);
}
return (CMD_RETURN_NORMAL);

View File

@ -43,10 +43,10 @@ const struct cmd_entry cmd_set_option_entry = {
.name = "set-option",
.alias = "set",
.args = { "aFgoqst:uw", 1, 2 },
.usage = "[-aFgosquw] [-t target-window] option [value]",
.args = { "aFgopqst:uw", 1, 2 },
.usage = "[-aFgopqsuw] " CMD_TARGET_PANE_USAGE " option [value]",
.target = { 't', CMD_FIND_WINDOW, CMD_FIND_CANFAIL },
.target = { 't', CMD_FIND_PANE, CMD_FIND_CANFAIL },
.flags = CMD_AFTERHOOK,
.exec = cmd_set_option_exec
@ -88,20 +88,24 @@ cmd_set_option_exec(struct cmd *self, struct cmdq_item *item)
struct session *s = fs->s;
struct winlink *wl = fs->wl;
struct window *w;
enum options_table_scope scope;
struct window_pane *wp;
struct options *oo;
struct options_entry *parent, *o;
char *name, *argument, *value = NULL, *cause;
const char *target;
int window, idx, already, error, ambiguous;
int scope;
struct style *sy;
window = (self->entry == &cmd_set_window_option_entry);
/* Expand argument. */
c = cmd_find_client(item, NULL, 1);
argument = format_single(item, args->argv[0], c, s, wl, NULL);
/* If set-hook -R, fire the hook straight away. */
if (self->entry == &cmd_set_hook_entry && args_has(args, 'R')) {
notify_hook(item, argument);
free(argument);
return (CMD_RETURN_NORMAL);
}
@ -123,25 +127,8 @@ cmd_set_option_exec(struct cmd *self, struct cmdq_item *item)
else
value = xstrdup(args->argv[1]);
/*
* Figure out the scope: for user options it comes from the arguments,
* otherwise from the option name.
*/
if (*name == '@') {
window = (self->entry == &cmd_set_window_option_entry);
scope = options_scope_from_flags(args, window, fs, &oo, &cause);
} else {
if (options_get_only(global_options, name) != NULL)
scope = OPTIONS_TABLE_SERVER;
else if (options_get_only(global_s_options, name) != NULL)
scope = OPTIONS_TABLE_SESSION;
else if (options_get_only(global_w_options, name) != NULL)
scope = OPTIONS_TABLE_WINDOW;
else {
scope = OPTIONS_TABLE_NONE;
xasprintf(&cause, "unknown option: %s", argument);
}
}
/* Get the scope and table for the option .*/
scope = options_scope_from_name(args, window, name, fs, &oo, &cause);
if (scope == OPTIONS_TABLE_NONE) {
if (args_has(args, 'q'))
goto out;
@ -149,35 +136,6 @@ cmd_set_option_exec(struct cmd *self, struct cmdq_item *item)
free(cause);
goto fail;
}
/* Which table should this option go into? */
if (scope == OPTIONS_TABLE_SERVER)
oo = global_options;
else if (scope == OPTIONS_TABLE_SESSION) {
if (args_has(self->args, 'g'))
oo = global_s_options;
else if (s == NULL) {
target = args_get(args, 't');
if (target != NULL)
cmdq_error(item, "no such session: %s", target);
else
cmdq_error(item, "no current session");
goto fail;
} else
oo = s->options;
} else if (scope == OPTIONS_TABLE_WINDOW) {
if (args_has(self->args, 'g'))
oo = global_w_options;
else if (wl == NULL) {
target = args_get(args, 't');
if (target != NULL)
cmdq_error(item, "no such window: %s", target);
else
cmdq_error(item, "no current window");
goto fail;
} else
oo = wl->window->options;
}
o = options_get_only(oo, name);
parent = options_get(oo, name);
@ -292,8 +250,8 @@ cmd_set_option_exec(struct cmd *self, struct cmdq_item *item)
alerts_reset_all();
if (strcmp(name, "window-style") == 0 ||
strcmp(name, "window-active-style") == 0) {
RB_FOREACH(w, windows, &windows)
w->flags |= WINDOW_STYLECHANGED;
RB_FOREACH(wp, window_pane_tree, &all_window_panes)
wp->flags |= PANE_STYLECHANGED;
}
if (strcmp(name, "pane-border-status") == 0) {
RB_FOREACH(w, windows, &windows)

View File

@ -30,18 +30,18 @@
static enum cmd_retval cmd_show_options_exec(struct cmd *, struct cmdq_item *);
static void cmd_show_options_print(struct cmd *, struct cmdq_item *,
struct options_entry *, int);
struct options_entry *, int, int);
static enum cmd_retval cmd_show_options_all(struct cmd *, struct cmdq_item *,
struct options *);
int, struct options *);
const struct cmd_entry cmd_show_options_entry = {
.name = "show-options",
.alias = "show",
.args = { "gHqst:vw", 0, 1 },
.usage = "[-gHqsvw] [-t target-session|target-window] [option]",
.args = { "AgHpqst:vw", 0, 1 },
.usage = "[-AgHpqsvw] " CMD_TARGET_PANE_USAGE " [option]",
.target = { 't', CMD_FIND_WINDOW, CMD_FIND_CANFAIL },
.target = { 't', CMD_FIND_PANE, CMD_FIND_CANFAIL },
.flags = CMD_AFTERHOOK,
.exec = cmd_show_options_exec
@ -82,13 +82,12 @@ cmd_show_options_exec(struct cmd *self, struct cmdq_item *item)
struct session *s = item->target.s;
struct winlink *wl = item->target.wl;
struct options *oo;
enum options_table_scope scope;
char *argument, *name = NULL, *cause;
const char *target;
int window, idx, ambiguous;
int window, idx, ambiguous, parent, scope;
struct options_entry *o;
window = (self->entry == &cmd_show_window_options_entry);
if (args->argc == 0) {
scope = options_scope_from_flags(args, window, fs, &oo, &cause);
if (scope == OPTIONS_TABLE_NONE) {
@ -98,7 +97,7 @@ cmd_show_options_exec(struct cmd *self, struct cmdq_item *item)
free(cause);
return (CMD_RETURN_ERROR);
}
return (cmd_show_options_all(self, item, oo));
return (cmd_show_options_all(self, item, scope, oo));
}
argument = format_single(item, args->argv[0], c, s, wl, NULL);
@ -112,49 +111,7 @@ cmd_show_options_exec(struct cmd *self, struct cmdq_item *item)
cmdq_error(item, "invalid option: %s", argument);
goto fail;
}
if (*name == '@')
scope = options_scope_from_flags(args, window, fs, &oo, &cause);
else {
if (options_get_only(global_options, name) != NULL)
scope = OPTIONS_TABLE_SERVER;
else if (options_get_only(global_s_options, name) != NULL)
scope = OPTIONS_TABLE_SESSION;
else if (options_get_only(global_w_options, name) != NULL)
scope = OPTIONS_TABLE_WINDOW;
else {
scope = OPTIONS_TABLE_NONE;
xasprintf(&cause, "unknown option: %s", argument);
}
if (scope == OPTIONS_TABLE_SERVER)
oo = global_options;
else if (scope == OPTIONS_TABLE_SESSION) {
if (args_has(self->args, 'g'))
oo = global_s_options;
else if (s == NULL) {
target = args_get(args, 't');
if (target != NULL) {
cmdq_error(item, "no such session: %s",
target);
} else
cmdq_error(item, "no current session");
goto fail;
} else
oo = s->options;
} else if (scope == OPTIONS_TABLE_WINDOW) {
if (args_has(self->args, 'g'))
oo = global_w_options;
else if (wl == NULL) {
target = args_get(args, 't');
if (target != NULL) {
cmdq_error(item, "no such window: %s",
target);
} else
cmdq_error(item, "no current window");
goto fail;
} else
oo = wl->window->options;
}
}
scope = options_scope_from_name(args, window, name, fs, &oo, &cause);
if (scope == OPTIONS_TABLE_NONE) {
if (args_has(args, 'q'))
goto fail;
@ -163,8 +120,13 @@ cmd_show_options_exec(struct cmd *self, struct cmdq_item *item)
goto fail;
}
o = options_get_only(oo, name);
if (args_has(args, 'A') && o == NULL) {
o = options_get(oo, name);
parent = 1;
} else
parent = 0;
if (o != NULL)
cmd_show_options_print(self, item, o, idx);
cmd_show_options_print(self, item, o, idx, parent);
free(name);
free(argument);
@ -178,7 +140,7 @@ fail:
static void
cmd_show_options_print(struct cmd *self, struct cmdq_item *item,
struct options_entry *o, int idx)
struct options_entry *o, int idx, int parent)
{
struct options_array_item *a;
const char *name = options_name(o);
@ -197,7 +159,8 @@ cmd_show_options_print(struct cmd *self, struct cmdq_item *item,
}
while (a != NULL) {
idx = options_array_item_index(a);
cmd_show_options_print(self, item, o, idx);
cmd_show_options_print(self, item, o, idx,
parent);
a = options_array_next(a);
}
return;
@ -209,50 +172,81 @@ cmd_show_options_print(struct cmd *self, struct cmdq_item *item,
cmdq_print(item, "%s", value);
else if (options_isstring(o)) {
escaped = args_escape(value);
cmdq_print(item, "%s %s", name, escaped);
if (parent)
cmdq_print(item, "%s* %s", name, escaped);
else
cmdq_print(item, "%s %s", name, escaped);
free(escaped);
} else
cmdq_print(item, "%s %s", name, value);
} else {
if (parent)
cmdq_print(item, "%s* %s", name, value);
else
cmdq_print(item, "%s %s", name, value);
}
free(value);
free(tmp);
}
static enum cmd_retval
cmd_show_options_all(struct cmd *self, struct cmdq_item *item,
cmd_show_options_all(struct cmd *self, struct cmdq_item *item, int scope,
struct options *oo)
{
const struct options_table_entry *oe;
struct options_entry *o;
struct options_array_item *a;
const char *name;
u_int idx;
const struct options_table_entry *oe;
int parent;
o = options_first(oo);
while (o != NULL) {
oe = options_table_entry(o);
if (options_table_entry(o) == NULL)
cmd_show_options_print(self, item, o, -1, 0);
o = options_next(o);
}
for (oe = options_table; oe->name != NULL; oe++) {
if (~oe->scope & scope)
continue;
if ((self->entry != &cmd_show_hooks_entry &&
!args_has(self->args, 'H') &&
oe != NULL &&
(oe->flags & OPTIONS_TABLE_IS_HOOK)) ||
(self->entry == &cmd_show_hooks_entry &&
(oe == NULL ||
(~oe->flags & OPTIONS_TABLE_IS_HOOK)))) {
o = options_next(o);
(~oe->flags & OPTIONS_TABLE_IS_HOOK))))
continue;
}
o = options_get_only(oo, oe->name);
if (o == NULL) {
if (!args_has(self->args, 'A'))
continue;
o = options_get(oo, oe->name);
if (o == NULL)
continue;
parent = 1;
} else
parent = 0;
if (!options_isarray(o))
cmd_show_options_print(self, item, o, -1);
cmd_show_options_print(self, item, o, -1, parent);
else if ((a = options_array_first(o)) == NULL) {
if (!args_has(self->args, 'v'))
cmdq_print(item, "%s", options_name(o));
if (!args_has(self->args, 'v')) {
name = options_name(o);
if (parent)
cmdq_print(item, "%s*", name);
else
cmdq_print(item, "%s", name);
}
} else {
while (a != NULL) {
idx = options_array_item_index(a);
cmd_show_options_print(self, item, o, idx);
cmd_show_options_print(self, item, o, idx,
parent);
a = options_array_next(a);
}
}
o = options_next(o);
}
return (CMD_RETURN_NORMAL);
}

View File

@ -37,8 +37,8 @@ const struct cmd_entry cmd_source_file_entry = {
.name = "source-file",
.alias = "source",
.args = { "nq", 1, -1 },
.usage = "[-nq] path ...",
.args = { "nqv", 1, -1 },
.usage = "[-nqv] path ...",
.flags = 0,
.exec = cmd_source_file_exec
@ -62,6 +62,8 @@ cmd_source_file_exec(struct cmd *self, struct cmdq_item *item)
flags |= CMD_PARSE_QUIET;
if (args_has(args, 'n'))
flags |= CMD_PARSE_PARSEONLY;
if (args_has(args, 'v'))
flags |= CMD_PARSE_VERBOSE;
utf8_stravis(&cwd, server_client_get_cwd(c, NULL), VIS_GLOB);
retval = CMD_RETURN_NORMAL;

View File

@ -90,7 +90,11 @@ cmd_swap_pane_exec(struct cmd *self, struct cmdq_item *item)
src_wp->layout_cell = dst_lc;
src_wp->window = dst_w;
options_set_parent(src_wp->options, dst_w->options);
src_wp->flags |= PANE_STYLECHANGED;
dst_wp->window = src_w;
options_set_parent(dst_wp->options, src_w->options);
dst_wp->flags |= PANE_STYLECHANGED;
sx = src_wp->sx; sy = src_wp->sy;
xoff = src_wp->xoff; yoff = src_wp->yoff;

View File

@ -153,7 +153,7 @@ cmd_wait_for_signal(__unused struct cmdq_item *item, const char *name,
log_debug("signal wait channel %s, with waiters", wc->name);
TAILQ_FOREACH_SAFE(wi, &wc->waiters, entry, wi1) {
wi->item->flags &= ~CMDQ_WAITING;
cmdq_continue(wi->item);
TAILQ_REMOVE(&wc->waiters, wi, entry);
free(wi);
@ -229,7 +229,7 @@ cmd_wait_for_unlock(struct cmdq_item *item, const char *name,
}
if ((wi = TAILQ_FIRST(&wc->lockers)) != NULL) {
wi->item->flags &= ~CMDQ_WAITING;
cmdq_continue(wi->item);
TAILQ_REMOVE(&wc->lockers, wi, entry);
free(wi);
} else {
@ -248,13 +248,13 @@ cmd_wait_for_flush(void)
RB_FOREACH_SAFE(wc, wait_channels, &wait_channels, wc1) {
TAILQ_FOREACH_SAFE(wi, &wc->waiters, entry, wi1) {
wi->item->flags &= ~CMDQ_WAITING;
cmdq_continue(wi->item);
TAILQ_REMOVE(&wc->waiters, wi, entry);
free(wi);
}
wc->woken = 1;
TAILQ_FOREACH_SAFE(wi, &wc->lockers, entry, wi1) {
wi->item->flags &= ~CMDQ_WAITING;
cmdq_continue(wi->item);
TAILQ_REMOVE(&wc->lockers, wi, entry);
free(wi);
}

4
cmd.c
View File

@ -596,8 +596,8 @@ cmd_mouse_at(struct window_pane *wp, struct mouse_event *m, u_int *xp,
}
log_debug("%s: x=%u, y=%u%s", __func__, x, y, last ? " (last)" : "");
if (m->statusat == 0 && y > 0)
y--;
if (m->statusat == 0 && y >= m->statuslines)
y -= m->statuslines;
if (x < wp->xoff || x >= wp->xoff + wp->sx)
return (-1);

View File

@ -230,11 +230,85 @@ colour_fromstring(const char *s)
return (-1);
}
/* Convert 256 colour palette to 16. */
u_char
colour_256to16(u_char c)
/* Convert 256 colour to RGB colour. */
int
colour_256toRGB(int c)
{
static const u_char table[256] = {
static const int table[256] = {
0x000000, 0x800000, 0x008000, 0x808000,
0x000080, 0x800080, 0x008080, 0xc0c0c0,
0x808080, 0xff0000, 0x00ff00, 0xffff00,
0x0000ff, 0xff00ff, 0x00ffff, 0xffffff,
0x000000, 0x00005f, 0x000087, 0x0000af,
0x0000d7, 0x0000ff, 0x005f00, 0x005f5f,
0x005f87, 0x005faf, 0x005fd7, 0x005fff,
0x008700, 0x00875f, 0x008787, 0x0087af,
0x0087d7, 0x0087ff, 0x00af00, 0x00af5f,
0x00af87, 0x00afaf, 0x00afd7, 0x00afff,
0x00d700, 0x00d75f, 0x00d787, 0x00d7af,
0x00d7d7, 0x00d7ff, 0x00ff00, 0x00ff5f,
0x00ff87, 0x00ffaf, 0x00ffd7, 0x00ffff,
0x5f0000, 0x5f005f, 0x5f0087, 0x5f00af,
0x5f00d7, 0x5f00ff, 0x5f5f00, 0x5f5f5f,
0x5f5f87, 0x5f5faf, 0x5f5fd7, 0x5f5fff,
0x5f8700, 0x5f875f, 0x5f8787, 0x5f87af,
0x5f87d7, 0x5f87ff, 0x5faf00, 0x5faf5f,
0x5faf87, 0x5fafaf, 0x5fafd7, 0x5fafff,
0x5fd700, 0x5fd75f, 0x5fd787, 0x5fd7af,
0x5fd7d7, 0x5fd7ff, 0x5fff00, 0x5fff5f,
0x5fff87, 0x5fffaf, 0x5fffd7, 0x5fffff,
0x870000, 0x87005f, 0x870087, 0x8700af,
0x8700d7, 0x8700ff, 0x875f00, 0x875f5f,
0x875f87, 0x875faf, 0x875fd7, 0x875fff,
0x878700, 0x87875f, 0x878787, 0x8787af,
0x8787d7, 0x8787ff, 0x87af00, 0x87af5f,
0x87af87, 0x87afaf, 0x87afd7, 0x87afff,
0x87d700, 0x87d75f, 0x87d787, 0x87d7af,
0x87d7d7, 0x87d7ff, 0x87ff00, 0x87ff5f,
0x87ff87, 0x87ffaf, 0x87ffd7, 0x87ffff,
0xaf0000, 0xaf005f, 0xaf0087, 0xaf00af,
0xaf00d7, 0xaf00ff, 0xaf5f00, 0xaf5f5f,
0xaf5f87, 0xaf5faf, 0xaf5fd7, 0xaf5fff,
0xaf8700, 0xaf875f, 0xaf8787, 0xaf87af,
0xaf87d7, 0xaf87ff, 0xafaf00, 0xafaf5f,
0xafaf87, 0xafafaf, 0xafafd7, 0xafafff,
0xafd700, 0xafd75f, 0xafd787, 0xafd7af,
0xafd7d7, 0xafd7ff, 0xafff00, 0xafff5f,
0xafff87, 0xafffaf, 0xafffd7, 0xafffff,
0xd70000, 0xd7005f, 0xd70087, 0xd700af,
0xd700d7, 0xd700ff, 0xd75f00, 0xd75f5f,
0xd75f87, 0xd75faf, 0xd75fd7, 0xd75fff,
0xd78700, 0xd7875f, 0xd78787, 0xd787af,
0xd787d7, 0xd787ff, 0xd7af00, 0xd7af5f,
0xd7af87, 0xd7afaf, 0xd7afd7, 0xd7afff,
0xd7d700, 0xd7d75f, 0xd7d787, 0xd7d7af,
0xd7d7d7, 0xd7d7ff, 0xd7ff00, 0xd7ff5f,
0xd7ff87, 0xd7ffaf, 0xd7ffd7, 0xd7ffff,
0xff0000, 0xff005f, 0xff0087, 0xff00af,
0xff00d7, 0xff00ff, 0xff5f00, 0xff5f5f,
0xff5f87, 0xff5faf, 0xff5fd7, 0xff5fff,
0xff8700, 0xff875f, 0xff8787, 0xff87af,
0xff87d7, 0xff87ff, 0xffaf00, 0xffaf5f,
0xffaf87, 0xffafaf, 0xffafd7, 0xffafff,
0xffd700, 0xffd75f, 0xffd787, 0xffd7af,
0xffd7d7, 0xffd7ff, 0xffff00, 0xffff5f,
0xffff87, 0xffffaf, 0xffffd7, 0xffffff,
0x080808, 0x121212, 0x1c1c1c, 0x262626,
0x303030, 0x3a3a3a, 0x444444, 0x4e4e4e,
0x585858, 0x626262, 0x6c6c6c, 0x767676,
0x808080, 0x8a8a8a, 0x949494, 0x9e9e9e,
0xa8a8a8, 0xb2b2b2, 0xbcbcbc, 0xc6c6c6,
0xd0d0d0, 0xdadada, 0xe4e4e4, 0xeeeeee
};
return (table[c & 0xff] | COLOUR_FLAG_RGB);
}
/* Convert 256 colour to 16 colour. */
int
colour_256to16(int c)
{
static const char table[256] = {
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
0, 4, 4, 4, 12, 12, 2, 6, 4, 4, 12, 12, 2, 2, 6, 4,
12, 12, 2, 2, 2, 6, 12, 12, 10, 10, 10, 10, 14, 12, 10, 10,
@ -253,5 +327,5 @@ colour_256to16(u_char c)
8, 8, 8, 8, 7, 7, 7, 7, 7, 7, 15, 15, 15, 15, 15, 15
};
return (table[c]);
return (table[c & 0xff]);
}

View File

@ -1,6 +1,6 @@
# configure.ac
AC_INIT([tmux], 3.0-rc3)
AC_INIT([tmux], next-3.1)
AC_PREREQ([2.60])
AC_CONFIG_AUX_DIR(etc)

View File

@ -36,6 +36,9 @@ control_notify_input(struct client *c, struct window_pane *wp,
if (c->session == NULL)
return;
if (c->flags & CLIENT_CONTROL_NOOUTPUT)
return;
/*
* Only write input if the window pane is linked to a window belonging
* to the client's session.

View File

@ -80,6 +80,7 @@ control_callback(struct client *c, int closed, __unused void *data)
if (line == NULL)
break;
if (*line == '\0') { /* empty line exit */
free(line);
c->flags |= CLIENT_EXIT;
break;
}

View File

@ -511,8 +511,9 @@ format_draw(struct screen_write_ctx *octx, const struct grid_cell *base,
u_int ocx = os->cx, ocy = os->cy, i, width[TOTAL];
u_int map[] = { LEFT, LEFT, CENTRE, RIGHT };
int focus_start = -1, focus_end = -1;
int list_state = -1;
int list_state = -1, fill = -1;
enum style_align list_align = STYLE_ALIGN_DEFAULT;
struct grid_cell gc;
struct style sy;
struct utf8_data *ud = &sy.gc.data;
const char *cp, *end;
@ -564,7 +565,7 @@ format_draw(struct screen_write_ctx *octx, const struct grid_cell *base,
cp++;
}
/* Draw the cell to th current screen. */
/* Draw the cell to the current screen. */
screen_write_cell(&ctx[current], &sy.gc);
width[current] += ud->width;
continue;
@ -590,6 +591,10 @@ format_draw(struct screen_write_ctx *octx, const struct grid_cell *base,
style_tostring(&sy));
free(tmp);
/* If this style has a fill colour, store it for later. */
if (sy.fill != 8)
fill = sy.fill;
/* Check the list state. */
switch (sy.list) {
case STYLE_LIST_ON:
@ -711,6 +716,14 @@ format_draw(struct screen_write_ctx *octx, const struct grid_cell *base,
fr->argument, names[fr->index], fr->start, fr->end);
}
/* Clear the available area. */
if (fill != -1) {
memcpy(&gc, &grid_default_cell, sizeof gc);
gc.bg = fill;
for (i = 0; i < available; i++)
screen_write_putc(octx, &gc, ' ');
}
/*
* Draw the screens. How they are arranged depends on where the list
* appearsq.

131
format.c
View File

@ -23,6 +23,7 @@
#include <errno.h>
#include <fnmatch.h>
#include <libgen.h>
#include <regex.h>
#include <stdarg.h>
#include <stdlib.h>
#include <string.h>
@ -1067,6 +1068,8 @@ format_find(struct format_tree *ft, const char *key, int modifiers)
if (~modifiers & FORMAT_TIMESTRING) {
o = options_parse_get(global_options, key, &idx, 0);
if (o == NULL && ft->wp != NULL)
o = options_parse_get(ft->wp->options, key, &idx, 0);
if (o == NULL && ft->w != NULL)
o = options_parse_get(ft->w->options, key, &idx, 0);
if (o == NULL)
@ -1263,7 +1266,7 @@ format_build_modifiers(struct format_tree *ft, const char **s, u_int *count)
cp++;
/* Check single character modifiers with no arguments. */
if (strchr("lmCbdtqETSWP<>", cp[0]) != NULL &&
if (strchr("lbdtqETSWP<>", cp[0]) != NULL &&
format_is_end(cp[1])) {
format_add_modifier(&list, count, cp, 1, NULL, 0);
cp++;
@ -1284,7 +1287,7 @@ format_build_modifiers(struct format_tree *ft, const char **s, u_int *count)
}
/* Now try single character with arguments. */
if (strchr("s=", cp[0]) == NULL)
if (strchr("mCs=", cp[0]) == NULL)
break;
c = cp[0];
@ -1345,39 +1348,67 @@ format_build_modifiers(struct format_tree *ft, const char **s, u_int *count)
return list;
}
/* Match against an fnmatch(3) pattern or regular expression. */
static char *
format_match(struct format_modifier *fm, const char *pattern, const char *text)
{
const char *s = "";
regex_t r;
int flags = 0;
if (fm->argc >= 1)
s = fm->argv[0];
if (strchr(s, 'r') == NULL) {
if (strchr(s, 'i') != NULL)
flags |= FNM_CASEFOLD;
if (fnmatch(pattern, text, flags) != 0)
return (xstrdup("0"));
} else {
flags = REG_EXTENDED|REG_NOSUB;
if (strchr(s, 'i') != NULL)
flags |= REG_ICASE;
if (regcomp(&r, pattern, flags) != 0)
return (xstrdup("0"));
if (regexec(&r, text, 0, NULL, 0) != 0) {
regfree(&r);
return (xstrdup("0"));
}
regfree(&r);
}
return (xstrdup("1"));
}
/* Perform substitution in string. */
static char *
format_substitute(const char *source, const char *from, const char *to)
format_sub(struct format_modifier *fm, const char *text, const char *pattern,
const char *with)
{
char *copy, *new;
const char *cp;
size_t fromlen, tolen, newlen, used;
char *value;
int flags = REG_EXTENDED;
fromlen = strlen(from);
tolen = strlen(to);
if (fm->argc >= 3 && strchr(fm->argv[2], 'i') != NULL)
flags |= REG_ICASE;
value = regsub(pattern, with, text, flags);
if (value == NULL)
return (xstrdup(text));
return (value);
}
newlen = strlen(source) + 1;
copy = new = xmalloc(newlen);
/* Search inside pane. */
static char *
format_search(struct format_modifier *fm, struct window_pane *wp, const char *s)
{
int ignore = 0, regex = 0;
char *value;
for (cp = source; *cp != '\0'; /* nothing */) {
if (strncmp(cp, from, fromlen) != 0) {
*new++ = *cp++;
continue;
}
used = new - copy;
newlen += tolen;
copy = xrealloc(copy, newlen);
new = copy + used;
memcpy(new, to, tolen);
new += tolen;
cp += fromlen;
if (fm->argc >= 1) {
if (strchr(fm->argv[0], 'i') != NULL)
ignore = 1;
if (strchr(fm->argv[0], 'r') != NULL)
regex = 1;
}
*new = '\0';
return (copy);
xasprintf(&value, "%u", window_pane_search(wp, s, regex, ignore));
return (value);
}
/* Loop over sessions. */
@ -1522,11 +1553,10 @@ format_replace(struct format_tree *ft, const char *key, size_t keylen,
char *copy0, *condition, *found, *new;
char *value, *left, *right;
size_t valuelen;
int modifiers = 0, limit = 0;
int modifiers = 0, limit = 0, j;
struct format_modifier *list, *fm, *cmp = NULL, *search = NULL;
struct format_modifier *sub = NULL;
u_int i, count;
int j;
/* Make a copy of the key. */
copy = copy0 = xstrndup(key, keylen);
@ -1553,18 +1583,18 @@ format_replace(struct format_tree *ft, const char *key, size_t keylen,
search = fm;
break;
case 's':
if (fm->argc != 2)
if (fm->argc < 2)
break;
sub = fm;
break;
case '=':
if (fm->argc != 1 && fm->argc != 2)
if (fm->argc < 1)
break;
limit = strtonum(fm->argv[0], INT_MIN, INT_MAX,
&errptr);
if (errptr != NULL)
limit = 0;
if (fm->argc == 2 && fm->argv[1] != NULL)
if (fm->argc >= 2 && fm->argv[1] != NULL)
marker = fm->argv[1];
break;
case 'l':
@ -1630,13 +1660,15 @@ format_replace(struct format_tree *ft, const char *key, size_t keylen,
goto fail;
} else if (search != NULL) {
/* Search in pane. */
new = format_expand(ft, copy);
if (wp == NULL) {
format_log(ft, "search '%s' but no pane", copy);
format_log(ft, "search '%s' but no pane", new);
value = xstrdup("0");
} else {
format_log(ft, "search '%s' pane %%%u", copy, wp->id);
xasprintf(&value, "%u", window_pane_search(wp, copy));
format_log(ft, "search '%s' pane %%%u", new, wp->id);
value = format_search(fm, wp, new);
}
free(new);
} else if (cmp != NULL) {
/* Comparison of left and right. */
if (format_choose(ft, copy, &left, &right, 1) != 0) {
@ -1687,12 +1719,8 @@ format_replace(struct format_tree *ft, const char *key, size_t keylen,
value = xstrdup("1");
else
value = xstrdup("0");
} else if (strcmp(cmp->modifier, "m") == 0) {
if (fnmatch(left, right, 0) == 0)
value = xstrdup("1");
else
value = xstrdup("0");
}
} else if (strcmp(cmp->modifier, "m") == 0)
value = format_match(cmp, left, right);
free(right);
free(left);
@ -1770,11 +1798,14 @@ done:
/* Perform substitution if any. */
if (sub != NULL) {
new = format_substitute(value, sub->argv[0], sub->argv[1]);
format_log(ft, "substituted '%s' to '%s: %s", sub->argv[0],
sub->argv[1], new);
left = format_expand(ft, sub->argv[0]);
right = format_expand(ft, sub->argv[1]);
new = format_sub(sub, value, left, right);
format_log(ft, "substitute '%s' to '%s': %s", left, right, new);
free(value);
value = new;
free(right);
free(left);
}
/* Truncate the value if needed. */
@ -1998,10 +2029,10 @@ void
format_defaults(struct format_tree *ft, struct client *c, struct session *s,
struct winlink *wl, struct window_pane *wp)
{
if (c != NULL)
if (c != NULL && c->name != NULL)
log_debug("%s: c=%s", __func__, c->name);
else
log_debug("%s: s=none", __func__);
log_debug("%s: c=none", __func__);
if (s != NULL)
log_debug("%s: s=$%u", __func__, s->id);
else
@ -2286,6 +2317,8 @@ format_defaults_pane(struct format_tree *ft, struct window_pane *wp)
!!(wp->base.mode & MODE_KKEYPAD));
format_add(ft, "wrap_flag", "%d",
!!(wp->base.mode & MODE_WRAP));
format_add(ft, "origin_flag", "%d",
!!(wp->base.mode & MODE_ORIGIN));
format_add(ft, "mouse_any_flag", "%d",
!!(wp->base.mode & ALL_MOUSE_MODES));
@ -2295,6 +2328,10 @@ format_defaults_pane(struct format_tree *ft, struct window_pane *wp)
!!(wp->base.mode & MODE_MOUSE_BUTTON));
format_add(ft, "mouse_all_flag", "%d",
!!(wp->base.mode & MODE_MOUSE_ALL));
format_add(ft, "mouse_utf8_flag", "%d",
!!(wp->base.mode & MODE_MOUSE_UTF8));
format_add(ft, "mouse_sgr_flag", "%d",
!!(wp->base.mode & MODE_MOUSE_SGR));
format_add_cb(ft, "pane_tabs", format_cb_pane_tabs);
}

View File

@ -214,7 +214,6 @@ grid_view_delete_cells(struct grid *gd, u_int px, u_int py, u_int nx, u_int bg)
sx = grid_view_x(gd, gd->sx);
grid_move_cells(gd, px, px + nx, py, sx - px - nx, bg);
grid_clear(gd, sx - nx, py, px + nx - (sx - nx), 1, bg);
}
/* Convert cells into a string. */

35
grid.c
View File

@ -37,12 +37,12 @@
/* Default grid cell data. */
const struct grid_cell grid_default_cell = {
0, 0, 8, 8, { { ' ' }, 0, 1, 1 }
{ { ' ' }, 0, 1, 1 }, 0, 0, 8, 8, 0
};
/* Cleared grid cell data. */
const struct grid_cell grid_cleared_cell = {
GRID_FLAG_CLEARED, 0, 8, 8, { { ' ' }, 0, 1, 1 }
{ { ' ' }, 0, 1, 1 }, 0, GRID_FLAG_CLEARED, 8, 8, 0
};
static const struct grid_cell_entry grid_cleared_entry = {
GRID_FLAG_CLEARED, { .data = { 0, 8, 8, ' ' } }
@ -82,6 +82,8 @@ grid_need_extended_cell(const struct grid_cell_entry *gce,
return (1);
if ((gc->fg & COLOUR_FLAG_RGB) || (gc->bg & COLOUR_FLAG_RGB))
return (1);
if (gc->us != 0) /* only supports 256 or RGB */
return (1);
return (0);
}
@ -473,6 +475,7 @@ grid_get_cell1(struct grid_line *gl, u_int px, struct grid_cell *gc)
gc->bg = gce->data.bg;
if (gce->flags & GRID_FLAG_BG256)
gc->bg |= COLOUR_FLAG_256;
gc->us = 0;
utf8_set(&gc->data, gce->data.data);
}
@ -544,7 +547,7 @@ void
grid_clear(struct grid *gd, u_int px, u_int py, u_int nx, u_int ny, u_int bg)
{
struct grid_line *gl;
u_int xx, yy;
u_int xx, yy, ox, sx;
if (nx == 0 || ny == 0)
return;
@ -561,16 +564,20 @@ grid_clear(struct grid *gd, u_int px, u_int py, u_int nx, u_int ny, u_int bg)
for (yy = py; yy < py + ny; yy++) {
gl = &gd->linedata[yy];
if (px + nx >= gd->sx && px < gl->cellused)
gl->cellused = px;
if (px > gl->cellsize && COLOUR_DEFAULT(bg))
continue;
if (px + nx >= gl->cellsize && COLOUR_DEFAULT(bg)) {
gl->cellsize = px;
continue;
sx = gd->sx;
if (sx > gl->cellsize)
sx = gl->cellsize;
ox = nx;
if (COLOUR_DEFAULT(bg)) {
if (px > sx)
continue;
if (px + nx > sx)
ox = sx - px;
}
grid_expand_line(gd, yy, px + nx, 8); /* default bg first */
for (xx = px; xx < px + nx; xx++)
grid_expand_line(gd, yy, px + ox, 8); /* default bg first */
for (xx = px; xx < px + ox; xx++)
grid_clear_cell(gd, xx, yy, bg);
}
}
@ -1213,6 +1220,10 @@ grid_reflow(struct grid *gd, u_int sx)
struct grid_cell gc;
u_int yy, width, i, at, first;
/* Do not reflow to the same size. */
if (sx == gd->sx)
return;
/*
* Create a destination grid. This is just used as a container for the
* line data and may not be fully valid.

View File

@ -172,6 +172,13 @@ input_key(struct window_pane *wp, key_code key, struct mouse_event *m)
return;
}
/* Literal keys go as themselves (can't be more than eight bits). */
if (key & KEYC_LITERAL) {
ud.data[0] = (u_char)key;
bufferevent_write(wp->event, &ud.data[0], 1);
return;
}
/*
* 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.

29
input.c
View File

@ -1829,6 +1829,8 @@ input_csi_dispatch_sgr_256_do(struct input_ctx *ictx, int fgbg, int c)
gc->fg = c | COLOUR_FLAG_256;
else if (fgbg == 48)
gc->bg = c | COLOUR_FLAG_256;
else if (fgbg == 58)
gc->us = c | COLOUR_FLAG_256;
}
return (1);
}
@ -1862,6 +1864,8 @@ input_csi_dispatch_sgr_rgb_do(struct input_ctx *ictx, int fgbg, int r, int g,
gc->fg = colour_join_rgb(r, g, b);
else if (fgbg == 48)
gc->bg = colour_join_rgb(r, g, b);
else if (fgbg == 58)
gc->us = colour_join_rgb(r, g, b);
return (1);
}
@ -1938,7 +1942,7 @@ input_csi_dispatch_sgr_colon(struct input_ctx *ictx, u_int i)
}
return;
}
if (n < 2 || (p[0] != 38 && p[0] != 48))
if (n < 2 || (p[0] != 38 && p[0] != 48 && p[0] != 58))
return;
switch (p[1]) {
case 2:
@ -1983,7 +1987,7 @@ input_csi_dispatch_sgr(struct input_ctx *ictx)
if (n == -1)
continue;
if (n == 38 || n == 48) {
if (n == 38 || n == 48 || n == 58) {
i++;
switch (input_get(ictx, i, 0, -1)) {
case 2:
@ -2078,6 +2082,9 @@ input_csi_dispatch_sgr(struct input_ctx *ictx)
case 55:
gc->attr &= ~GRID_ATTR_OVERLINE;
break;
case 59:
gc->us = 0;
break;
case 90:
case 91:
case 92:
@ -2259,7 +2266,7 @@ input_exit_rename(struct input_ctx *ictx)
{
if (ictx->flags & INPUT_DISCARD)
return;
if (!options_get_number(ictx->wp->window->options, "allow-rename"))
if (!options_get_number(ictx->wp->options, "allow-rename"))
return;
log_debug("%s: \"%s\"", __func__, ictx->input_buf);
@ -2347,12 +2354,14 @@ input_osc_10(struct input_ctx *ictx, const char *p)
{
struct window_pane *wp = ictx->wp;
u_int r, g, b;
char tmp[16];
if (sscanf(p, "rgb:%2x/%2x/%2x", &r, &g, &b) != 3)
goto bad;
wp->style.gc.fg = colour_join_rgb(r, g, b);
wp->flags |= PANE_REDRAW;
xsnprintf(tmp, sizeof tmp, "fg=#%02x%02x%02x", r, g, b);
options_set_style(wp->options, "window-style", 1, tmp);
options_set_style(wp->options, "window-active-style", 1, tmp);
wp->flags |= (PANE_REDRAW|PANE_STYLECHANGED);
return;
@ -2366,12 +2375,14 @@ input_osc_11(struct input_ctx *ictx, const char *p)
{
struct window_pane *wp = ictx->wp;
u_int r, g, b;
char tmp[16];
if (sscanf(p, "rgb:%2x/%2x/%2x", &r, &g, &b) != 3)
goto bad;
wp->style.gc.bg = colour_join_rgb(r, g, b);
wp->flags |= PANE_REDRAW;
xsnprintf(tmp, sizeof tmp, "bg=#%02x%02x%02x", r, g, b);
options_set_style(wp->options, "window-style", 1, tmp);
options_set_style(wp->options, "window-active-style", 1, tmp);
wp->flags |= (PANE_REDRAW|PANE_STYLECHANGED);
return;

2
job.c
View File

@ -117,7 +117,7 @@ job_run(const char *cmd, struct session *s, const char *cwd,
close(out[0]);
nullfd = open(_PATH_DEVNULL, O_RDWR, 0);
if (nullfd < 0)
if (nullfd == -1)
fatal("open failed");
if (dup2(nullfd, STDERR_FILENO) == -1)
fatal("dup2 failed");

View File

@ -284,6 +284,12 @@ key_string_lookup_key(key_code key)
return (out);
}
/* Literal keys are themselves. */
if (key & KEYC_LITERAL) {
snprintf(out, sizeof out, "%c", (int)(key & 0xff));
return (out);
}
/*
* Special case: display C-@ as C-Space. Could do this below in
* the (key >= 0 && key <= 32), but this way we let it be found

View File

@ -122,7 +122,7 @@ layout_parse(struct window *w, const char *layout)
{
struct layout_cell *lc, *lcchild;
struct window_pane *wp;
u_int npanes, ncells, sx, sy;
u_int npanes, ncells;
u_short csum;
/* Check validity. */
@ -153,8 +153,7 @@ layout_parse(struct window *w, const char *layout)
layout_destroy_cell(w, lcchild, &lc);
}
/* Save the old window size and resize to the layout size. */
sx = w->sx; sy = w->sy;
/* Resize to the layout size. */
window_resize(w, lc->sx, lc->sy);
/* Destroy the old layout and swap to the new. */
@ -166,12 +165,9 @@ layout_parse(struct window *w, const char *layout)
layout_assign(&wp, lc);
/* Update pane offsets and sizes. */
layout_fix_offsets(lc);
layout_fix_offsets(w);
layout_fix_panes(w);
/* Then resize the layout back to the original window size. */
layout_resize(w, sx, sy);
window_resize(w, sx, sy);
recalculate_sizes();
layout_print_cell(lc, __func__, 0);

View File

@ -158,7 +158,7 @@ layout_set_even(struct window *w, enum layout_type type)
layout_spread_cell(w, lc);
/* Fix cell offsets. */
layout_fix_offsets(lc);
layout_fix_offsets(w);
layout_fix_panes(w);
layout_print_cell(w->layout_root, __func__, 1);
@ -257,7 +257,7 @@ layout_set_main_h(struct window *w)
}
/* Fix cell offsets. */
layout_fix_offsets(lc);
layout_fix_offsets(w);
layout_fix_panes(w);
layout_print_cell(w->layout_root, __func__, 1);
@ -344,7 +344,7 @@ layout_set_main_v(struct window *w)
}
/* Fix cell offsets. */
layout_fix_offsets(lc);
layout_fix_offsets(w);
layout_fix_panes(w);
layout_print_cell(w->layout_root, __func__, 1);
@ -453,7 +453,7 @@ layout_set_tiled(struct window *w)
}
/* Fix cell offsets. */
layout_fix_offsets(lc);
layout_fix_offsets(w);
layout_fix_panes(w);
layout_print_cell(w->layout_root, __func__, 1);

141
layout.c
View File

@ -39,7 +39,6 @@ static int layout_resize_pane_grow(struct window *, struct layout_cell *,
enum layout_type, int, int);
static int layout_resize_pane_shrink(struct window *, struct layout_cell *,
enum layout_type, int);
static int layout_need_status(struct layout_cell *, int);
static u_int layout_new_pane_size(struct window *, u_int,
struct layout_cell *, enum layout_type, u_int, u_int,
u_int);
@ -199,9 +198,9 @@ layout_make_node(struct layout_cell *lc, enum layout_type type)
lc->wp = NULL;
}
/* Fix cell offsets based on their sizes. */
void
layout_fix_offsets(struct layout_cell *lc)
/* Fix cell offsets for a child cell. */
static void
layout_fix_offsets1(struct layout_cell *lc)
{
struct layout_cell *lcchild;
u_int xoff, yoff;
@ -212,7 +211,7 @@ layout_fix_offsets(struct layout_cell *lc)
lcchild->xoff = xoff;
lcchild->yoff = lc->yoff;
if (lcchild->type != LAYOUT_WINDOWPANE)
layout_fix_offsets(lcchild);
layout_fix_offsets1(lcchild);
xoff += lcchild->sx + 1;
}
} else {
@ -221,61 +220,92 @@ layout_fix_offsets(struct layout_cell *lc)
lcchild->xoff = lc->xoff;
lcchild->yoff = yoff;
if (lcchild->type != LAYOUT_WINDOWPANE)
layout_fix_offsets(lcchild);
layout_fix_offsets1(lcchild);
yoff += lcchild->sy + 1;
}
}
}
/*
* Returns 1 if we need to reserve space for the pane status line. This is the
* case for the most upper panes only.
*/
static int
layout_need_status(struct layout_cell *lc, int at_top)
/* Update cell offsets based on their sizes. */
void
layout_fix_offsets(struct window *w)
{
struct layout_cell *first_lc;
struct layout_cell *lc = w->layout_root;
if (lc->parent != NULL) {
if (lc->parent->type == LAYOUT_LEFTRIGHT)
return (layout_need_status(lc->parent, at_top));
lc->xoff = 0;
lc->yoff = 0;
if (at_top)
first_lc = TAILQ_FIRST(&lc->parent->cells);
else
first_lc = TAILQ_LAST(&lc->parent->cells,layout_cells);
if (lc == first_lc)
return (layout_need_status(lc->parent, at_top));
return (0);
layout_fix_offsets1(lc);
}
/* Is this a top cell? */
static int
layout_cell_is_top(struct window *w, struct layout_cell *lc)
{
struct layout_cell *next;
while (lc != w->layout_root) {
next = lc->parent;
if (next->type == LAYOUT_TOPBOTTOM &&
lc != TAILQ_FIRST(&next->cells))
return (0);
lc = next;
}
return (1);
}
/* Is this a bottom cell? */
static int
layout_cell_is_bottom(struct window *w, struct layout_cell *lc)
{
struct layout_cell *next;
while (lc != w->layout_root) {
next = lc->parent;
if (next->type == LAYOUT_TOPBOTTOM &&
lc != TAILQ_LAST(&next->cells, layout_cells))
return (0);
lc = next;
}
return (1);
}
/*
* Returns 1 if we need to add an extra line for the pane status line. This is
* the case for the most upper or lower panes only.
*/
static int
layout_add_border(struct window *w, struct layout_cell *lc, int status)
{
if (status == PANE_STATUS_TOP)
return (layout_cell_is_top(w, lc));
if (status == PANE_STATUS_BOTTOM)
return (layout_cell_is_bottom(w, lc));
return (0);
}
/* Update pane offsets and sizes based on their cells. */
void
layout_fix_panes(struct window *w)
{
struct window_pane *wp;
struct layout_cell *lc;
int shift, status;
int status;
status = options_get_number(w->options, "pane-border-status");
TAILQ_FOREACH(wp, &w->panes, entry) {
if ((lc = wp->layout_cell) == NULL)
continue;
if (status != 0)
shift = layout_need_status(lc, status == 1);
else
shift = 0;
wp->xoff = lc->xoff;
wp->yoff = lc->yoff;
if (shift && status == 1)
wp->yoff += 1;
window_pane_resize(wp, lc->sx, lc->sy - shift);
if (layout_add_border(w, lc, status)) {
if (status == PANE_STATUS_TOP)
wp->yoff++;
window_pane_resize(wp, lc->sx, lc->sy - 1);
} else
window_pane_resize(wp, lc->sx, lc->sy);
}
}
@ -312,13 +342,15 @@ layout_resize_check(struct window *w, struct layout_cell *lc,
status = options_get_number(w->options, "pane-border-status");
if (lc->type == LAYOUT_WINDOWPANE) {
/* Space available in this cell only. */
minimum = PANE_MINIMUM;
if (type == LAYOUT_LEFTRIGHT)
if (type == LAYOUT_LEFTRIGHT) {
available = lc->sx;
else {
minimum = PANE_MINIMUM;
} else {
available = lc->sy;
if (status != 0)
minimum += layout_need_status(lc, status == 1);
if (layout_add_border(w, lc, status))
minimum = PANE_MINIMUM + 1;
else
minimum = PANE_MINIMUM;
}
if (available > minimum)
available -= minimum;
@ -507,7 +539,7 @@ layout_resize(struct window *w, u_int sx, u_int sy)
layout_resize_adjust(w, lc, LAYOUT_TOPBOTTOM, ychange);
/* Fix cell offsets. */
layout_fix_offsets(lc);
layout_fix_offsets(w);
layout_fix_panes(w);
}
@ -567,7 +599,7 @@ layout_resize_layout(struct window *w, struct layout_cell *lc,
}
/* Fix cell offsets. */
layout_fix_offsets(w->layout_root);
layout_fix_offsets(w);
layout_fix_panes(w);
notify_window("window-layout-changed", w);
}
@ -861,9 +893,10 @@ layout_split_pane(struct window_pane *wp, enum layout_type type, int size,
return (NULL);
break;
case LAYOUT_TOPBOTTOM:
minimum = PANE_MINIMUM * 2 + 1;
if (status != 0)
minimum += layout_need_status(lc, status == 1);
if (layout_add_border(wp->window, lc, status))
minimum = PANE_MINIMUM * 2 + 2;
else
minimum = PANE_MINIMUM * 2 + 1;
if (sy < minimum)
return (NULL);
break;
@ -988,7 +1021,7 @@ layout_split_pane(struct window_pane *wp, enum layout_type type, int size,
if (full_size) {
if (!resize_first)
layout_resize_child_cells(wp->window, lc);
layout_fix_offsets(wp->window->layout_root);
layout_fix_offsets(wp->window);
} else
layout_make_leaf(lc, wp);
@ -1006,7 +1039,7 @@ layout_close_pane(struct window_pane *wp)
/* Fix pane offsets and sizes. */
if (w->layout_root != NULL) {
layout_fix_offsets(w->layout_root);
layout_fix_offsets(w);
layout_fix_panes(w);
}
notify_window("window-layout-changed", w);
@ -1021,7 +1054,7 @@ layout_spread_cell(struct window *w, struct layout_cell *parent)
number = 0;
TAILQ_FOREACH (lc, &parent->cells, entry)
number++;
number++;
if (number <= 1)
return (0);
status = options_get_number(w->options, "pane-border-status");
@ -1029,9 +1062,10 @@ layout_spread_cell(struct window *w, struct layout_cell *parent)
if (parent->type == LAYOUT_LEFTRIGHT)
size = parent->sx;
else if (parent->type == LAYOUT_TOPBOTTOM) {
size = parent->sy;
if (status != 0)
size -= layout_need_status(parent, status == 1);
if (layout_add_border(w, parent, status))
size = parent->sy - 1;
else
size = parent->sy;
} else
return (0);
if (size < number - 1)
@ -1049,9 +1083,10 @@ layout_spread_cell(struct window *w, struct layout_cell *parent)
change = each - (int)lc->sx;
layout_resize_adjust(w, lc, LAYOUT_LEFTRIGHT, change);
} else if (parent->type == LAYOUT_TOPBOTTOM) {
this = each;
if (status != 0)
this += layout_need_status(lc, status == 1);
if (layout_add_border(w, lc, status))
this = each + 1;
else
this = each;
change = this - (int)lc->sy;
layout_resize_adjust(w, lc, LAYOUT_TOPBOTTOM, change);
}
@ -1073,7 +1108,7 @@ layout_spread_out(struct window_pane *wp)
do {
if (layout_spread_cell(w, parent)) {
layout_fix_offsets(parent);
layout_fix_offsets(w);
layout_fix_panes(w);
break;
}

22
menu.c
View File

@ -161,7 +161,7 @@ menu_free_cb(struct client *c)
struct menu_data *md = c->overlay_data;
if (md->item != NULL)
md->item->flags &= ~CMDQ_WAITING;
cmdq_continue(md->item);
if (md->cb != NULL)
md->cb(md->menu, UINT_MAX, KEYC_NONE, md->data);
@ -206,8 +206,18 @@ menu_key_cb(struct client *c, struct key_event *event)
c->flags |= CLIENT_REDRAWOVERLAY;
return (0);
}
for (i = 0; i < (u_int)count; i++) {
name = menu->items[i].name;
if (name == NULL || *name == '-')
continue;
if (event->key == menu->items[i].key) {
md->choice = i;
goto chosen;
}
}
switch (event->key) {
case KEYC_UP:
case 'k':
if (old == -1)
old = 0;
do {
@ -220,6 +230,7 @@ menu_key_cb(struct client *c, struct key_event *event)
c->flags |= CLIENT_REDRAWOVERLAY;
return (0);
case KEYC_DOWN:
case 'j':
if (old == -1)
old = 0;
do {
@ -239,15 +250,6 @@ menu_key_cb(struct client *c, struct key_event *event)
case 'q':
return (1);
}
for (i = 0; i < (u_int)count; i++) {
name = menu->items[i].name;
if (name == NULL || *name == '-')
continue;
if (event->key == menu->items[i].key) {
md->choice = i;
goto chosen;
}
}
return (0);
chosen:

View File

@ -480,7 +480,7 @@ mode_tree_add(struct mode_tree_data *mtd, struct mode_tree_item *parent,
saved = mode_tree_find_item(&mtd->saved, tag);
if (saved != NULL) {
if (parent == NULL || (parent != NULL && parent->expanded))
if (parent == NULL || parent->expanded)
mti->tagged = saved->tagged;
mti->expanded = saved->expanded;
} else if (expanded == -1)
@ -933,6 +933,7 @@ mode_tree_key(struct mode_tree_data *mtd, struct client *c, key_code *key,
case '\016': /* C-n */
mode_tree_down(mtd, 1);
break;
case 'g':
case KEYC_PPAGE:
case '\002': /* C-b */
for (i = 0; i < mtd->height; i++) {
@ -941,6 +942,7 @@ mode_tree_key(struct mode_tree_data *mtd, struct client *c, key_code *key,
mode_tree_up(mtd, 1);
}
break;
case 'G':
case KEYC_NPAGE:
case '\006': /* C-f */
for (i = 0; i < mtd->height; i++) {
@ -1019,6 +1021,8 @@ mode_tree_key(struct mode_tree_data *mtd, struct client *c, key_code *key,
mode_tree_build(mtd);
}
break;
case '?':
case '/':
case '\023': /* C-s */
mtd->references++;
status_prompt_set(c, "(search) ", "",

View File

@ -562,13 +562,13 @@ const struct options_table_entry options_table[] = {
{ .name = "allow-rename",
.type = OPTIONS_TABLE_FLAG,
.scope = OPTIONS_TABLE_WINDOW,
.scope = OPTIONS_TABLE_WINDOW|OPTIONS_TABLE_PANE,
.default_num = 0
},
{ .name = "alternate-screen",
.type = OPTIONS_TABLE_FLAG,
.scope = OPTIONS_TABLE_WINDOW,
.scope = OPTIONS_TABLE_WINDOW|OPTIONS_TABLE_PANE,
.default_num = 1
},
@ -688,7 +688,7 @@ const struct options_table_entry options_table[] = {
.type = OPTIONS_TABLE_CHOICE,
.scope = OPTIONS_TABLE_WINDOW,
.choices = options_table_pane_status_list,
.default_num = 0
.default_num = PANE_STATUS_OFF
},
{ .name = "pane-border-style",
@ -699,7 +699,7 @@ const struct options_table_entry options_table[] = {
{ .name = "remain-on-exit",
.type = OPTIONS_TABLE_FLAG,
.scope = OPTIONS_TABLE_WINDOW,
.scope = OPTIONS_TABLE_WINDOW|OPTIONS_TABLE_PANE,
.default_num = 0
},
@ -711,7 +711,7 @@ const struct options_table_entry options_table[] = {
{ .name = "window-active-style",
.type = OPTIONS_TABLE_STYLE,
.scope = OPTIONS_TABLE_WINDOW,
.scope = OPTIONS_TABLE_WINDOW|OPTIONS_TABLE_PANE,
.default_str = "default"
},
@ -724,7 +724,7 @@ const struct options_table_entry options_table[] = {
{ .name = "window-style",
.type = OPTIONS_TABLE_STYLE,
.scope = OPTIONS_TABLE_WINDOW,
.scope = OPTIONS_TABLE_WINDOW|OPTIONS_TABLE_PANE,
.default_str = "default"
},

105
options.c
View File

@ -100,7 +100,7 @@ options_parent_table_entry(struct options *oo, const char *s)
if (oo->parent == NULL)
fatalx("no parent options for %s", s);
o = options_get_only(oo->parent, s);
o = options_get(oo->parent, s);
if (o == NULL)
fatalx("%s not in parent options", s);
return (o->tableentry);
@ -178,6 +178,12 @@ options_free(struct options *oo)
free(oo);
}
void
options_set_parent(struct options *oo, struct options *parent)
{
oo->parent = parent;
}
struct options_entry *
options_first(struct options *oo)
{
@ -365,7 +371,8 @@ options_array_set(struct options_entry *o, u_int idx, const char *value,
pr = cmd_parse_from_string(value, NULL);
switch (pr->status) {
case CMD_PARSE_EMPTY:
*cause = xstrdup("empty command");
if (cause != NULL)
*cause = xstrdup("empty command");
return (-1);
case CMD_PARSE_ERROR:
if (cause != NULL)
@ -544,7 +551,7 @@ options_parse_get(struct options *oo, const char *s, int *idx, int only)
}
char *
options_match(const char *s, int *idx, int* ambiguous)
options_match(const char *s, int *idx, int *ambiguous)
{
const struct options_table_entry *oe, *found;
char *name;
@ -724,20 +731,102 @@ options_set_style(struct options *oo, const char *name, int append,
return (o);
}
enum options_table_scope
int
options_scope_from_name(struct args *args, int window,
const char *name, struct cmd_find_state *fs, struct options **oo,
char **cause)
{
struct session *s = fs->s;
struct winlink *wl = fs->wl;
struct window_pane *wp = fs->wp;
const char *target = args_get(args, 't');
const struct options_table_entry *oe;
int scope = OPTIONS_TABLE_NONE;
if (*name == '@')
return (options_scope_from_flags(args, window, fs, oo, cause));
for (oe = options_table; oe->name != NULL; oe++) {
if (strcmp(oe->name, name) == 0)
break;
}
if (oe->name == NULL) {
xasprintf(cause, "unknown option: %s", name);
return (OPTIONS_TABLE_NONE);
}
switch (oe->scope) {
case OPTIONS_TABLE_SERVER:
*oo = global_options;
scope = OPTIONS_TABLE_SERVER;
break;
case OPTIONS_TABLE_SESSION:
if (args_has(args, 'g')) {
*oo = global_s_options;
scope = OPTIONS_TABLE_SESSION;
} else if (s == NULL && target != NULL)
xasprintf(cause, "no such session: %s", target);
else if (s == NULL)
xasprintf(cause, "no current session");
else {
*oo = s->options;
scope = OPTIONS_TABLE_SESSION;
}
break;
case OPTIONS_TABLE_WINDOW|OPTIONS_TABLE_PANE:
if (args_has(args, 'p')) {
if (wp == NULL && target != NULL)
xasprintf(cause, "no such pane: %s", target);
else if (wp == NULL)
xasprintf(cause, "no current pane");
else {
*oo = wp->options;
scope = OPTIONS_TABLE_PANE;
}
break;
}
/* FALLTHROUGH */
case OPTIONS_TABLE_WINDOW:
if (args_has(args, 'g')) {
*oo = global_w_options;
scope = OPTIONS_TABLE_WINDOW;
} else if (wl == NULL && target != NULL)
xasprintf(cause, "no such window: %s", target);
else if (wl == NULL)
xasprintf(cause, "no current window");
else {
*oo = wl->window->options;
scope = OPTIONS_TABLE_WINDOW;
}
break;
}
return (scope);
}
int
options_scope_from_flags(struct args *args, int window,
struct cmd_find_state *fs, struct options **oo, char **cause)
{
struct session *s = fs->s;
struct winlink *wl = fs->wl;
const char *target= args_get(args, 't');
struct session *s = fs->s;
struct winlink *wl = fs->wl;
struct window_pane *wp = fs->wp;
const char *target = args_get(args, 't');
if (args_has(args, 's')) {
*oo = global_options;
return (OPTIONS_TABLE_SERVER);
}
if (window || args_has(args, 'w')) {
if (args_has(args, 'p')) {
if (wp == NULL) {
if (target != NULL)
xasprintf(cause, "no such pane: %s", target);
else
xasprintf(cause, "no current pane");
return (OPTIONS_TABLE_NONE);
}
*oo = wp->options;
return (OPTIONS_TABLE_PANE);
} else if (window || args_has(args, 'w')) {
if (args_has(args, 'g')) {
*oo = global_w_options;
return (OPTIONS_TABLE_WINDOW);

View File

@ -133,13 +133,27 @@ char *
osdep_get_cwd(int fd)
{
static char target[PATH_MAX + 1];
char *path;
pid_t pgrp;
#ifdef KERN_PROC_CWD
int mib[4];
size_t len;
#else
char *path;
ssize_t n;
#endif
if ((pgrp = tcgetpgrp(fd)) == -1)
return (NULL);
#ifdef KERN_PROC_CWD
mib[0] = CTL_KERN;
mib[1] = KERN_PROC_ARGS;
mib[2] = pgrp;
mib[3] = KERN_PROC_CWD;
len = sizeof(target);
if (sysctl(mib, __arraycount(mib), target, &len, NULL, 0) == 0)
return (target);
#else
xasprintf(&path, "/proc/%lld/cwd", (long long) pgrp);
n = readlink(path, target, sizeof(target) - 1);
free(path);
@ -147,6 +161,7 @@ osdep_get_cwd(int fd)
target[n] = '\0';
return (target);
}
#endif
return (NULL);
}

View File

@ -1,7 +1,5 @@
#!/bin/sh
# new-session without clients should be the right size
PATH=/bin:/usr/bin
TERM=screen
@ -19,6 +17,7 @@ EOF
$TMUX -f$TMP start </dev/null || exit 1
sleep 1
$TMUX lsw -aF '#{session_name},#{window_name}'|sort >$TMP || exit 1
$TMUX kill-server 2>/dev/null
cat <<EOF|cmp -s $TMP - || exit 1
bar,bar0
bar,bar1
@ -27,7 +26,6 @@ foo,foo0
foo,foo1
foo,foo2
EOF
$TMUX kill-server 2>/dev/null
cat <<EOF >$TMP
new -sfoo -nfoo0
@ -40,6 +38,7 @@ EOF
$TMUX -f$TMP start </dev/null || exit 1
sleep 1
$TMUX lsw -aF '#{session_name},#{window_name}'|sort >$TMP || exit 1
$TMUX kill-server 2>/dev/null
cat <<EOF|cmp -s $TMP - || exit 1
bar,bar0
bar,bar1
@ -48,6 +47,5 @@ foo,foo0
foo,foo1
foo,foo2
EOF
$TMUX kill-server 2>/dev/null
exit 0

View File

@ -0,0 +1,8 @@
%if #{l:1}
set -g status-style fg=cyan,bg='#001040'
%elif #{l:1}
set -g status-style fg=white,bg='#400040'
%else
set -g status-style fg=white,bg='#800000'
%endif
bind ^X last-window

View File

@ -0,0 +1,93 @@
# -----------------------------------------------------------------------------
# This config is targeted for tmux 2.1+ and should be placed in $HOME.
#
# Read the "Plugin Manager" section (bottom) before trying to use this config!
# -----------------------------------------------------------------------------
# -----------------------------------------------------------------------------
# Global options
# -----------------------------------------------------------------------------
# Set a new prefix / leader key.
set -g prefix `
bind ` send-prefix
# Allow opening multiple terminals to view the same session at different sizes.
setw -g aggressive-resize on
# Remove delay when switching between Vim modes.
set -s escape-time 0
# Allow Vim's FocusGained to work when your terminal gains focus.
# Requires Vim plugin: https://github.com/tmux-plugins/vim-tmux-focus-events
set -g focus-events on
# Add a bit more scroll history in the buffer.
set -g history-limit 50000
# Enable color support inside of tmux.
set -g default-terminal "screen-256color"
# Ensure window titles get renamed automatically.
setw -g automatic-rename
# Start windows and panes index at 1, not 0.
set -g base-index 1
setw -g pane-base-index 1
# Enable full mouse support.
set -g mouse on
# Status bar optimized for Gruvbox.
set -g status-fg colour244
set -g status-bg default
set -g status-left ''
set -g status-right-length 0
#set -g status-right-length 20
#set -g status-right '%a %Y-%m-%d %H:%M'
set -g pane-border-fg default
set -g pane-border-bg default
set -g pane-active-border-fg colour250
set -g pane-active-border-bg default
set-window-option -g window-status-current-attr bold
set-window-option -g window-status-current-fg colour223
# -----------------------------------------------------------------------------
# Key bindings
# -----------------------------------------------------------------------------
# Unbind default keys
unbind C-b
unbind '"'
unbind %
# Reload the tmux config.
bind-key r source-file ~/.tmux.conf
# Split panes.
bind-key h split-window -v
bind-key v split-window -h
# Move around panes with ALT + arrow keys.
bind-key -n M-Up select-pane -U
bind-key -n M-Left select-pane -L
bind-key -n M-Down select-pane -D
bind-key -n M-Right select-pane -R
# -----------------------------------------------------------------------------
# Plugin Manager - https://github.com/tmux-plugins/tpm
# In order to use the plugins below you need to install TPM and the plugins.
# Step 1) git clone https://github.com/tmux-plugins/tpm ~/.tmux/plugins/tpm
# Step 2) Reload tmux if it's already started with `r
# Step 3) Launch tmux and hit `I (capital i) to fetch any plugins
# -----------------------------------------------------------------------------
# List of plugins.
set -g @plugin 'tmux-plugins/tpm'
set -g @plugin 'tmux-plugins/tmux-resurrect'
set -g @plugin 'tmux-plugins/tmux-yank'
# Initialize TPM (keep this line at the very bottom of your tmux.conf).
run -b '~/.tmux/plugins/tpm/tpm'

29
regress/xenl-terminal.sh Normal file
View File

@ -0,0 +1,29 @@
#!/bin/sh
PATH=/bin:/usr/bin
TERM=screen
[ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux)
TMUX="$TEST_TMUX -Ltest"
$TMUX kill-server 2>/dev/null
TMUX2="$TEST_TMUX -Ltest2"
$TMUX2 kill-server 2>/dev/null
TMP=$(mktemp)
trap "rm -f $TMP" 0 1 15
$TMUX2 -f/dev/null new -d || exit 1
$TMUX2 set -as terminal-overrides ',*:xenl@' || exit 1
$TMUX2 set -g status-right 'RRR' || exit 1
$TMUX2 set -g status-left 'LLL' || exit 1
$TMUX2 set -g window-status-current-format 'WWW' || exit 1
$TMUX -f/dev/null new -x20 -y2 -d "$TMUX2 attach" || exit 1
sleep 1
$TMUX capturep -p|tail -1 >$TMP || exit 1
$TMUX kill-server 2>/dev/null
$TMUX2 kill-server 2>/dev/null
cat <<EOF|cmp -s $TMP - || exit 1
LLLWWW RR
EOF
exit 0

115
regsub.c Normal file
View File

@ -0,0 +1,115 @@
/* $OpenBSD$ */
/*
* Copyright (c) 2019 Nicholas Marriott <nicholas.marriott@gmail.com>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER
* IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
* OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include <sys/types.h>
#include <regex.h>
#include <string.h>
#include "tmux.h"
static void
regsub_copy(char **buf, size_t *len, const char *text, size_t start,
size_t end)
{
size_t add = end - start;
*buf = xrealloc(*buf, (*len) + add + 1);
memcpy((*buf) + *len, text + start, add);
(*len) += add;
}
static void
regsub_expand(char **buf, size_t *len, const char *with, const char *text,
regmatch_t *m, u_int n)
{
const char *cp;
u_int i;
for (cp = with; *cp != '\0'; cp++) {
if (*cp == '\\') {
cp++;
if (*cp >= '0' && *cp <= '9') {
i = *cp - '0';
if (i < n && m[i].rm_so != m[i].rm_eo) {
regsub_copy(buf, len, text, m[i].rm_so,
m[i].rm_eo);
continue;
}
}
}
*buf = xrealloc(*buf, (*len) + 2);
(*buf)[(*len)++] = *cp;
}
}
char *
regsub(const char *pattern, const char *with, const char *text, int flags)
{
regex_t r;
regmatch_t m[10];
ssize_t start, end, last, len = 0;
int empty = 0;
char *buf = NULL;
if (*text == '\0')
return (xstrdup(""));
if (regcomp(&r, pattern, flags) != 0)
return (NULL);
start = 0;
last = 0;
end = strlen(text);
while (start <= end) {
m[0].rm_so = start;
m[0].rm_eo = end;
if (regexec(&r, text, nitems(m), m, REG_STARTEND) != 0) {
regsub_copy(&buf, &len, text, start, end);
break;
}
/*
* Append any text not part of this match (from the end of the
* last match).
*/
regsub_copy(&buf, &len, text, last, m[0].rm_so);
/*
* If the last match was empty and this one isn't (it is either
* later or has matched text), expand this match. If it is
* empty, move on one character and try again from there.
*/
if (empty || m[0].rm_so != last || m[0].rm_so != m[0].rm_eo) {
regsub_expand(&buf, &len, with, text, m, nitems(m));
last = m[0].rm_eo;
start = m[0].rm_eo;
empty = 0;
} else {
last = m[0].rm_eo;
start = m[0].rm_eo + 1;
empty = 1;
}
}
buf[len] = '\0';
regfree(&r);
return (buf);
}

View File

@ -45,10 +45,6 @@ static void screen_redraw_draw_pane(struct screen_redraw_ctx *,
#define CELL_BORDERS " xqlkmjwvtun~"
#define CELL_STATUS_OFF 0
#define CELL_STATUS_TOP 1
#define CELL_STATUS_BOTTOM 2
/* Check if cell is on the border of a particular pane. */
static int
screen_redraw_cell_border1(struct window_pane *wp, u_int px, u_int py)
@ -112,12 +108,12 @@ screen_redraw_check_cell(struct client *c, u_int px, u_int py, int pane_status,
if (px > w->sx || py > w->sy)
return (CELL_OUTSIDE);
if (pane_status != CELL_STATUS_OFF) {
if (pane_status != PANE_STATUS_OFF) {
TAILQ_FOREACH(wp, &w->panes, entry) {
if (!window_pane_visible(wp))
continue;
if (pane_status == CELL_STATUS_TOP)
if (pane_status == PANE_STATUS_TOP)
line = wp->yoff - 1;
else
line = wp->yoff + wp->sy;
@ -153,7 +149,7 @@ screen_redraw_check_cell(struct client *c, u_int px, u_int py, int pane_status,
borders |= 8;
if (px <= w->sx && screen_redraw_cell_border(c, px + 1, py))
borders |= 4;
if (pane_status == CELL_STATUS_TOP) {
if (pane_status == PANE_STATUS_TOP) {
if (py != 0 && screen_redraw_cell_border(c, px, py - 1))
borders |= 2;
} else {
@ -208,9 +204,9 @@ screen_redraw_check_is(u_int px, u_int py, int type, int pane_status,
border = screen_redraw_cell_border1(wantwp, px, py);
if (border == 0 || border == -1)
return (0);
if (pane_status == CELL_STATUS_TOP && border == 4)
if (pane_status == PANE_STATUS_TOP && border == 4)
return (0);
if (pane_status == CELL_STATUS_BOTTOM && border == 3)
if (pane_status == PANE_STATUS_BOTTOM && border == 3)
return (0);
/* If there are more than two panes, that's enough. */
@ -222,7 +218,7 @@ screen_redraw_check_is(u_int px, u_int py, int type, int pane_status,
return (1);
/* With status lines mark the entire line. */
if (pane_status != CELL_STATUS_OFF)
if (pane_status != PANE_STATUS_OFF)
return (1);
/* Check if the pane covers the whole width. */
@ -270,7 +266,7 @@ screen_redraw_make_pane_status(struct client *c, struct window *w,
fmt = options_get_string(w->options, "pane-border-format");
ft = format_create(c, NULL, FORMAT_PANE|wp->id, 0);
ft = format_create(c, NULL, FORMAT_PANE|wp->id, FORMAT_STATUS);
format_defaults(ft, c, NULL, NULL, wp);
expanded = format_expand_time(ft, fmt);
@ -324,7 +320,7 @@ screen_redraw_draw_pane_status(struct screen_redraw_ctx *ctx)
s = &wp->status_screen;
size = wp->status_size;
if (ctx->pane_status == CELL_STATUS_TOP)
if (ctx->pane_status == PANE_STATUS_TOP)
yoff = wp->yoff - 1;
else
yoff = wp->yoff + wp->sy;
@ -386,7 +382,7 @@ screen_redraw_update(struct client *c, int flags)
if (c->overlay_draw != NULL)
flags |= CLIENT_REDRAWOVERLAY;
if (options_get_number(wo, "pane-border-status") != CELL_STATUS_OFF) {
if (options_get_number(wo, "pane-border-status") != PANE_STATUS_OFF) {
redraw = 0;
TAILQ_FOREACH(wp, &w->panes, entry) {
if (screen_redraw_make_pane_status(c, w, wp))
@ -441,7 +437,7 @@ screen_redraw_screen(struct client *c)
screen_redraw_set_context(c, &ctx);
if (flags & (CLIENT_REDRAWWINDOW|CLIENT_REDRAWBORDERS)) {
if (ctx.pane_status != CELL_STATUS_OFF)
if (ctx.pane_status != PANE_STATUS_OFF)
screen_redraw_draw_pane_status(&ctx);
screen_redraw_draw_borders(&ctx);
}

View File

@ -36,7 +36,7 @@ static const struct grid_cell *screen_write_combine(struct screen_write_ctx *,
const struct utf8_data *, u_int *);
static const struct grid_cell screen_write_pad_cell = {
GRID_FLAG_PADDING, 0, 8, 8, { { 0 }, 0, 0, 0 }
{ { 0 }, 0, 0, 0 }, 0, GRID_FLAG_PADDING, 0, 8, 8
};
struct screen_write_collect_item {
@ -1169,11 +1169,7 @@ screen_write_clearscreen(struct screen_write_ctx *ctx, u_int bg)
void
screen_write_clearhistory(struct screen_write_ctx *ctx)
{
struct screen *s = ctx->s;
struct grid *gd = s->grid;
grid_move_lines(gd, 0, gd->hsize, gd->sy, 8);
gd->hscrolled = gd->hsize = 0;
grid_clear_history(ctx->s->grid);
}
/* Clear a collected line. */

View File

@ -522,9 +522,10 @@ have_event:
/* Is this on the status line? */
m->statusat = status_at_line(c);
m->statuslines = status_line_size(c);
if (m->statusat != -1 &&
y >= (u_int)m->statusat &&
y < m->statusat + status_line_size(c)) {
y < m->statusat + m->statuslines) {
sr = status_get_range(c, x, y - m->statusat);
if (sr == NULL) {
where = STATUS_DEFAULT;
@ -553,8 +554,8 @@ have_event:
/* Not on status line. Adjust position and check for border or pane. */
if (where == NOWHERE) {
px = x;
if (m->statusat == 0 && y > 0)
py = y - 1;
if (m->statusat == 0 && y >= m->statuslines)
py = y - m->statuslines;
else if (m->statusat > 0 && y >= (u_int)m->statusat)
py = m->statusat - 1;
else
@ -1024,16 +1025,6 @@ server_client_key_callback(struct cmdq_item *item, void *data)
fatal("gettimeofday failed");
session_update_activity(s, &c->activity_time);
/* Handle status line. */
if (~c->flags & CLIENT_READONLY)
status_message_clear(c);
if (c->prompt_string != NULL) {
if (c->flags & CLIENT_READONLY)
goto out;
if (status_prompt_key(c, key) == 0)
goto out;
}
/* Check for mouse keys. */
m->valid = 0;
if (key == KEYC_MOUSE) {
@ -1214,17 +1205,26 @@ server_client_handle_key(struct client *c, struct key_event *event)
return (0);
/*
* Key presses in overlay mode are a special case. The queue might be
* blocked so they need to be processed immediately rather than queued.
* Key presses in overlay mode and the command prompt are a special
* case. The queue might be blocked so they need to be processed
* immediately rather than queued.
*/
if ((~c->flags & CLIENT_READONLY) && c->overlay_key != NULL) {
switch (c->overlay_key(c, event)) {
case 0:
return (0);
case 1:
server_client_clear_overlay(c);
return (0);
if (~c->flags & CLIENT_READONLY) {
status_message_clear(c);
if (c->prompt_string != NULL) {
if (status_prompt_key(c, event->key) == 0)
return (0);
}
if (c->overlay_key != NULL) {
switch (c->overlay_key(c, event)) {
case 0:
return (0);
case 1:
server_client_clear_overlay(c);
return (0);
}
}
server_client_clear_overlay(c);
}
/*
@ -1243,6 +1243,8 @@ server_client_loop(void)
struct client *c;
struct window *w;
struct window_pane *wp;
struct winlink *wl;
struct session *s;
int focus;
TAILQ_FOREACH(c, &clients, entry) {
@ -1259,11 +1261,17 @@ server_client_loop(void)
*/
focus = options_get_number(global_options, "focus-events");
RB_FOREACH(w, windows, &windows) {
TAILQ_FOREACH(wl, &w->winlinks, wentry) {
s = wl->session;
if (s->attached != 0 && s->curw == wl)
break;
}
TAILQ_FOREACH(wp, &w->panes, entry) {
if (wp->fd != -1) {
if (focus)
server_client_check_focus(wp);
server_client_check_resize(wp);
if (wl != NULL)
server_client_check_resize(wp);
}
wp->flags &= ~PANE_REDRAW;
}
@ -1525,7 +1533,9 @@ server_client_click_timer(__unused int fd, __unused short events, void *data)
static void
server_client_check_exit(struct client *c)
{
if (!(c->flags & CLIENT_EXIT))
if (~c->flags & CLIENT_EXIT)
return;
if (c->flags & CLIENT_EXITED)
return;
if (EVBUFFER_LENGTH(c->stdin_data) != 0)
@ -1538,7 +1548,7 @@ server_client_check_exit(struct client *c)
if (c->flags & CLIENT_ATTACHED)
notify_client("client-detached", c);
proc_send(c->peer, MSG_EXIT, -1, &c->retval, sizeof c->retval);
c->flags &= ~CLIENT_EXIT;
c->flags |= CLIENT_EXITED;
}
/* Redraw timer callback. */
@ -1943,26 +1953,29 @@ server_client_dispatch_identify(struct client *c, struct imsg *imsg)
close(c->fd);
c->fd = -1;
return;
} else if (c->fd != -1) {
if (tty_init(&c->tty, c, c->fd, c->term) != 0) {
close(c->fd);
c->fd = -1;
} else {
if (c->flags & CLIENT_UTF8)
c->tty.flags |= TTY_UTF8;
if (c->flags & CLIENT_256COLOURS)
c->tty.term_flags |= TERM_256COLOURS;
tty_resize(&c->tty);
c->flags |= CLIENT_TERMINAL;
}
}
if (c->fd == -1)
return;
if (tty_init(&c->tty, c, c->fd, c->term) != 0) {
close(c->fd);
c->fd = -1;
return;
}
if (c->flags & CLIENT_UTF8)
c->tty.flags |= TTY_UTF8;
if (c->flags & CLIENT_256COLOURS)
c->tty.term_flags |= TERM_256COLOURS;
tty_resize(&c->tty);
if (!(c->flags & CLIENT_CONTROL))
c->flags |= CLIENT_TERMINAL;
/*
* If this is the first client that has finished identifying, load
* configuration files.
*/
if ((~c->flags & CLIENT_EXIT) &&
!cfg_finished &&
c == TAILQ_FIRST(&clients) &&
TAILQ_NEXT(c, entry) == NULL)
start_cfg();
}
/* Handle shell message. */

View File

@ -308,7 +308,7 @@ server_destroy_pane(struct window_pane *wp, int notify)
wp->fd = -1;
}
if (options_get_number(w->options, "remain-on-exit")) {
if (options_get_number(wp->options, "remain-on-exit")) {
if (~wp->flags & PANE_STATUSREADY)
return;

View File

@ -43,7 +43,7 @@
struct clients clients;
struct tmuxproc *server_proc;
static int server_fd;
static int server_fd = -1;
static int server_exit;
static struct event server_ev_accept;
@ -209,9 +209,7 @@ server_start(struct tmuxproc *client, struct event_base *base, int lockfd,
c->flags |= CLIENT_EXIT;
}
start_cfg();
server_add_accept(0);
proc_loop(server_proc, server_loop);
job_kill_all();
@ -363,6 +361,9 @@ server_add_accept(int timeout)
{
struct timeval tv = { timeout, 0 };
if (server_fd == -1)
return;
if (event_initialized(&server_ev_accept))
event_del(&server_ev_accept);

View File

@ -170,10 +170,8 @@ spawn_window(struct spawn_context *sc, char **cause)
/* Spawn the pane. */
wp = spawn_pane(sc, cause);
if (wp == NULL) {
if (~sc->flags & SPAWN_RESPAWN) {
window_destroy(w);
if (~sc->flags & SPAWN_RESPAWN)
winlink_remove(&s->windows, sc->wl);
}
return (NULL);
}

14
style.c
View File

@ -30,8 +30,9 @@
/* Default style. */
static struct style style_default = {
{ 0, 0, 8, 8, { { ' ' }, 0, 1, 1 } },
{ { { ' ' }, 0, 1, 1 }, 0, 0, 8, 8, 0 },
8,
STYLE_ALIGN_DEFAULT,
STYLE_LIST_OFF,
@ -127,6 +128,10 @@ style_parse(struct style *sy, const struct grid_cell *base, const char *in)
sy->align = STYLE_ALIGN_RIGHT;
else
goto error;
} else if (end > 5 && strncasecmp(tmp, "fill=", 5) == 0) {
if ((value = colour_fromstring(tmp + 5)) == -1)
goto error;
sy->fill = value;
} else if (end > 3 && strncasecmp(tmp + 1, "g=", 2) == 0) {
if ((value = colour_fromstring(tmp + 3)) == -1)
goto error;
@ -213,6 +218,11 @@ style_tostring(struct style *sy)
tmp);
comma = ",";
}
if (sy->fill != 8) {
off += xsnprintf(s + off, sizeof s - off, "%sfill=%s", comma,
colour_tostring(sy->fill));
comma = ",";
}
if (gc->fg != 8) {
off += xsnprintf(s + off, sizeof s - off, "%sfg=%s", comma,
colour_tostring(gc->fg));
@ -290,6 +300,8 @@ style_equal(struct style *sy1, struct style *sy2)
return (0);
if ((gc1->attr & STYLE_ATTR_MASK) != (gc2->attr & STYLE_ATTR_MASK))
return (0);
if (sy1->fill != sy2->fill)
return (0);
if (sy1->align != sy2->align)
return (0);
return (1);

511
tmux.1
View File

@ -461,7 +461,7 @@ Will execute
.Ic if-shell ,
the shell command
.Xr true 1 ,
.Ic new-window
.Ic split-window
and
.Ic kill-session
in that order.
@ -475,7 +475,7 @@ commands and their arguments.
This section describes the syntax of commands parsed by
.Nm ,
for example in a configuration file or at the command prompt.
Note the when commands are entered into the shell, they are parsed by the shell
Note that when commands are entered into the shell, they are parsed by the shell
- see for example
.Xr ksh 1
or
@ -520,7 +520,11 @@ the given four or eight digit hexadecimal number.
When preceded (escaped) by a \e, the following characters are replaced: \ee by
the escape character; \er by a carriage return; \en by a newline; and \et by a
tab.
.Pp
.It
\eooo is replaced by a character of the octal value ooo.
Three octal digits are required, for example \e001.
The largest valid character is \e377.
.It
Any other characters preceded by \e are replaced by themselves (that is, the \e
is removed) and are not treated as having any special meaning - so for example
\e; will not mark a command sequence and \e$ will not expand an environment
@ -610,7 +614,7 @@ Most commands accept the optional
.Fl s )
argument with one of
.Ar target-client ,
.Ar target-session
.Ar target-session ,
.Ar target-window ,
or
.Ar target-pane .
@ -777,7 +781,7 @@ may consist entirely of the token
.Ql {mouse}
(alternative form
.Ql = )
to specify the most recent mouse event
to specify the session, window or pane where the most recent mouse event occurred
(see the
.Sx MOUSE SUPPORT
section)
@ -877,7 +881,7 @@ refresh-client -t/dev/ttyp2
rename-session -tfirst newname
set-window-option -t:0 monitor-activity on
set-option -wt:0 monitor-activity on
new-window ; split-window -d
@ -919,7 +923,7 @@ section.
The following commands are available to manage clients and sessions:
.Bl -tag -width Ds
.It Xo Ic attach-session
.Op Fl dEr
.Op Fl dErx
.Op Fl c Ar working-directory
.Op Fl t Ar target-session
.Xc
@ -932,6 +936,10 @@ If used from inside, switch the current client.
If
.Fl d
is specified, any other clients attached to the session are detached.
If
.Fl x
is given, send SIGHUP to the parent process of the client as well as
detaching the client, typically causing it to exit.
.Fl r
signifies the client is read-only (only keys bound to the
.Ic detach-client
@ -1049,7 +1057,7 @@ command.
Lock all clients attached to
.Ar target-session .
.It Xo Ic new-session
.Op Fl AdDEP
.Op Fl AdDEPX
.Op Fl c Ar start-directory
.Op Fl F Ar format
.Op Fl n Ar window-name
@ -1106,6 +1114,12 @@ already exists; in this case,
behaves like
.Fl d
to
.Ic attach-session ,
and
.Fl X
behaves like
.Fl x
to
.Ic attach-session .
.Pp
If
@ -1153,7 +1167,8 @@ is used, the
option will not be applied.
.It Xo Ic refresh-client
.Op Fl cDlLRSU
.Op Fl C Ar width,height
.Op Fl C Ar XxY
.Op Fl F Ar flags
.Op Fl t Ar target-client
.Op Ar adjustment
.Xc
@ -1196,7 +1211,13 @@ window, changing the current window in the attached session will reset
it.
.Pp
.Fl C
sets the width and height of a control client.
sets the width and height of a control client and
.Fl F
sets a comma-separated list of flags.
Currently the only flag available is
.Ql no-output
to disable receiving pane output.
.Pp
.Fl l
requests the clipboard from the client using the
.Xr xterm 1
@ -1242,7 +1263,7 @@ and
.Fl T
show debugging information about jobs and terminals.
.It Xo Ic source-file
.Op Fl nq
.Op Fl nqv
.Ar path
.Ar ...
.Xc
@ -1260,6 +1281,8 @@ does not exist.
With
.Fl n ,
the file is parsed but no commands are executed.
.Fl v
shows the parsed commands and line numbers if possible.
.It Ic start-server
.D1 (alias: Ic start )
Start the
@ -1343,6 +1366,9 @@ It is also entered when a command that produces output, such as
.Ic list-keys ,
is executed from a key binding.
.Pp
In copy mode an indicator is displayed in the top-right corner of the pane with
the current position and the number of lines in the history.
.Pp
Commands are sent to copy mode using the
.Fl X
flag to the
@ -1396,6 +1422,7 @@ The following commands are supported in copy mode:
.It Li "jump-to-backward <to>" Ta "T" Ta ""
.It Li "jump-to-forward <to>" Ta "t" Ta ""
.It Li "middle-line" Ta "M" Ta "M-r"
.It Li "next-matching-bracket" Ta "%" Ta "M-C-f"
.It Li "next-paragraph" Ta "}" Ta "M-}"
.It Li "next-space" Ta "W" Ta ""
.It Li "next-space-end" Ta "E" Ta ""
@ -1405,6 +1432,7 @@ The following commands are supported in copy mode:
.It Li "page-down" Ta "C-f" Ta "PageDown"
.It Li "page-down-and-cancel" Ta "" Ta ""
.It Li "page-up" Ta "C-b" Ta "PageUp"
.It Li "previous-matching-bracket" Ta "" Ta "M-C-b"
.It Li "previous-paragraph" Ta "{" Ta "M-{"
.It Li "previous-space" Ta "B" Ta ""
.It Li "previous-word" Ta "b" Ta "M-b"
@ -1817,14 +1845,16 @@ With
.Fl b ,
other commands are not blocked from running until the indicator is closed.
.It Xo Ic find-window
.Op Fl CNTZ
.Op Fl rCNTZ
.Op Fl t Ar target-pane
.Ar match-string
.Xc
.D1 (alias: Ic findw )
Search for the
Search for a
.Xr fnmatch 3
pattern
pattern or, with
.Fl r ,
regular expression
.Ar match-string
in window names, titles, and visible content (but not history).
The flags control matching behavior:
@ -2312,8 +2342,7 @@ applies the last set layout if possible (undoes the most recent layout change).
.Fl E
spreads the current pane and any panes next to it out evenly.
.It Xo Ic select-pane
.Op Fl DdegLlMmRU
.Op Fl P Ar style
.Op Fl DdeLlMmRU
.Op Fl T Ar title
.Op Fl t Ar target-pane
.Xc
@ -2321,9 +2350,7 @@ spreads the current pane and any panes next to it out evenly.
Make pane
.Ar target-pane
the active pane in window
.Ar target-window ,
or set its style (with
.Fl P ) .
.Ar target-window .
If one of
.Fl D ,
.Fl L ,
@ -2340,6 +2367,8 @@ command.
enables or
.Fl d
disables input to the pane.
.Fl T
sets the pane title.
.Pp
.Fl m
and
@ -2354,25 +2383,6 @@ to
.Ic swap-pane
and
.Ic swap-window .
.Pp
Each pane has a style: by default the
.Ic window-style
and
.Ic window-active-style
options are used,
.Ic select-pane
.Fl P
sets the style for a single pane.
For example, to set the pane 1 background to red:
.Bd -literal -offset indent
select-pane -t:.1 -P 'bg=red'
.Ed
.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
@ -2629,7 +2639,7 @@ With
only
.Ar key-table .
.It Xo Ic send-keys
.Op Fl lMRX
.Op Fl HlMRX
.Op Fl N Ar repeat-count
.Op Fl t Ar target-pane
.Ar key Ar ...
@ -2644,10 +2654,16 @@ or
.Ql NPage )
to send; if the string is not recognised as a key, it is sent as a series of
characters.
All arguments are sent sequentially from first to last.
.Pp
The
.Fl l
flag disables key name lookup and sends the keys literally.
All arguments are sent sequentially from first to last.
flag disables key name lookup and processes the keys as literal UTF-8
characters.
The
.Fl H
flag expects each key to be a hexadecimal number for an ASCII character.
.Pp
The
.Fl R
flag causes the terminal state to be reset.
@ -2691,16 +2707,17 @@ is present, all key bindings are removed.
The appearance and behaviour of
.Nm
may be modified by changing the value of various options.
There are three types of option:
There are four types of option:
.Em server options ,
.Em session options
.Em window options
and
.Em window options .
.Em pane options .
.Pp
The
.Nm
server has a set of global options which do not apply to any particular
window or session.
window or session or pane.
These are altered with the
.Ic set-option
.Fl s
@ -2722,16 +2739,29 @@ The available server and session options are listed under the
.Ic set-option
command.
.Pp
Similarly, a set of window options is attached to each window, and there is
a set of global window options from which any unset options are inherited.
Window options are altered with the
.Ic set-window-option
command and can be listed with the
.Ic show-window-options
command.
All window options are documented with the
.Ic set-window-option
command.
Similarly, a set of window options is attached to each window and a set of pane
options to each pane.
Pane options inherit from window options.
This means any pane option may be set as a window option to apply the option to
all panes in the window without the option set, for example these commands will
set the background colour to red for all panes except pane 0:
.Bd -literal -offset indent
set -w window-style bg=red
set -pt:.0 window-style bg=blue
.Ed
.Pp
There is also a set of global window options from which any unset window or
pane options are inherited.
Window and pane options are altered with
.Ic set-option
.Fl w
and
.Fl p
commands and displayed with
.Ic show-option
.Fl w
and
.Fl p .
.Pp
.Nm
also supports user options which are prefixed with a
@ -2749,26 +2779,27 @@ abc123
Commands which set options are as follows:
.Bl -tag -width Ds
.It Xo Ic set-option
.Op Fl aFgoqsuw
.Op Fl t Ar target-session | Ar target-window
.Op Fl aFgopqsuw
.Op Fl t Ar target-pane
.Ar option Ar value
.Xc
.D1 (alias: Ic set )
Set a window option with
.Fl w
(equivalent to the
.Ic set-window-option
command),
Set a pane option with
.Fl p ,
a window option with
.Fl w ,
a server option with
.Fl s ,
otherwise a session option.
If the option is not a user option,
.Fl w
and
or
.Fl s
are unnecessary -
may be unnecessary -
.Nm
will infer the type from the option name.
will infer the type from the option name, assuming
.Fl w
for pane options.
If
.Fl g
is given, the global session or window option is set.
@ -2813,13 +2844,49 @@ blue foreground.
Without
.Fl a ,
the result would be the default background and a blue foreground.
.Pp
Available window options are listed under
.Ic set-window-option .
.Pp
.It Xo Ic show-options
.Op Fl AgHpqsvw
.Op Fl t Ar target-pane
.Op Ar option
.Xc
.D1 (alias: Ic show )
Show the pane options (or a single option if
.Ar option
is provided) with
.Fl p ,
the window options with
.Fl w ,
the server options with
.Fl s ,
otherwise the session options.
If the option is not a user option,
.Fl w
or
.Fl s
may be unnecessary -
.Nm
will infer the type from the option name, assuming
.Fl w
for pane options.
Global session or window options are listed if
.Fl g
is used.
.Fl v
shows only the option value, not the name.
If
.Fl q
is set, no error will be returned if
.Ar option
is unset.
.Fl H
includes hooks (omitted by default).
.Fl A
includes options inherited from a parent set of options, such options are
marked with an asterisk.
.Ar value
depends on the option and may be a number, a string, or a flag (on, off, or
omitted to toggle).
.El
.Pp
Available server options are:
.Bl -tag -width Ds
@ -2960,6 +3027,18 @@ for all terminal types matching
The terminal entry value is passed through
.Xr strunvis 3
before interpretation.
.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
.El
.Pp
Available session options are:
@ -3166,7 +3245,7 @@ the terminal appears to be
.Xr xterm 1 .
This option is off by default.
.It Ic set-titles-string Ar string
String used to set the window title if
String used to set the client terminal title if
.Ic set-titles
is on.
Formats are expanded, see the
@ -3300,18 +3379,6 @@ 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 | both
.Xc
@ -3346,26 +3413,8 @@ copy mode.
The default is
.Ql \ -_@ .
.El
.It Xo Ic set-window-option
.Op Fl aFgoqu
.Op Fl t Ar target-window
.Ar option Ar value
.Xc
.D1 (alias: Ic setw )
Set a window option.
The
.Fl a ,
.Fl F ,
.Fl g ,
.Fl o ,
.Fl q
and
.Fl u
flags work similarly to the
.Ic set-option
command.
.Pp
Supported window options are:
Available window options are:
.Pp
.Bl -tag -width Ds -compact
.It Xo Ic aggressive-resize
@ -3384,29 +3433,6 @@ session; this option is good for full-screen programs which support
.Dv SIGWINCH
and poor for interactive programs such as shells.
.Pp
.It Xo Ic allow-rename
.Op Ic on | off
.Xc
Allow programs to change the window name using a terminal escape
sequence (\eek...\ee\e\e).
The default is off.
.Pp
.It Xo Ic alternate-screen
.Op Ic on | off
.Xc
This option configures whether programs running inside
.Nm
may use the terminal alternate screen feature, which allows the
.Em smcup
and
.Em rmcup
.Xr terminfo 5
capabilities.
The alternate screen feature preserves the contents of the window when an
interactive application starts and restores it on exit, so that any output
visible before the application starts reappears unchanged after it exits.
The default is on.
.Pp
.It Xo Ic automatic-rename
.Op Ic on | off
.Xc
@ -3425,7 +3451,7 @@ or later with
or with a terminal escape sequence.
It may be switched off globally with:
.Bd -literal -offset indent
set-window-option -g automatic-rename off
set-option -wg automatic-rename off
.Ed
.Pp
.It Ic automatic-rename-format Ar format
@ -3542,29 +3568,12 @@ see the
section.
Attributes are ignored.
.Pp
.It Xo Ic remain-on-exit
.Op Ic on | off
.Xc
A window with this flag set is not destroyed when the program running in it
exits.
The window may be reactivated with the
.Ic respawn-window
command.
.Pp
.It Xo Ic synchronize-panes
.Op Ic on | off
.Xc
Duplicate input to any pane to all other panes in the same window (only
for panes that are not in any special mode).
.Pp
.It Ic window-active-style Ar style
Set the style for the window's active pane.
For how to specify
.Ar style ,
see the
.Sx STYLES
section.
.Pp
.It Ic window-status-activity-style Ar style
Set status line style for windows with an activity alert.
For how to specify
@ -3644,14 +3653,6 @@ command and the
.Ic aggressive-resize
option.
.Pp
.It Ic window-style Ar style
Set the default window style.
For how to specify
.Ar style ,
see the
.Sx STYLES
section.
.Pp
.It Xo Ic wrap-search
.Op Ic on | off
.Xc
@ -3668,54 +3669,54 @@ will generate
function key sequences; these have a number included to indicate modifiers such
as Shift, Alt or Ctrl.
.El
.It Xo Ic show-options
.Op Fl gHqsvw
.Op Fl t Ar target-session | Ar target-window
.Op Ar option
.Pp
Available pane options are:
.Pp
.Bl -tag -width Ds -compact
.It Xo Ic allow-rename
.Op Ic on | off
.Xc
.D1 (alias: Ic show )
Show the window options (or a single window option if given) with
.Fl w
(equivalent to
.Ic show-window-options ) ,
the server options with
.Fl s ,
otherwise the session options for
.Ar target session .
If
.Ar option
is given and is not a user option,
.Fl w
Allow programs in the pane to change the window name using a terminal escape
sequence (\eek...\ee\e\e).
.Pp
.It Xo Ic alternate-screen
.Op Ic on | off
.Xc
This option configures whether programs running inside the pane may use the
terminal alternate screen feature, which allows the
.Em smcup
and
.Fl s
are unnecessary -
.Nm
will infer the type from the option name.
Global session or window options are listed if
.Fl g
is used.
.Fl v
shows only the option value, not the name.
If
.Fl q
is set, no error will be returned if
.Ar option
is unset.
.Fl H
includes hooks (omitted by default).
.It Xo Ic show-window-options
.Op Fl gv
.Op Fl t Ar target-window
.Op Ar option
.Em rmcup
.Xr terminfo 5
capabilities.
The alternate screen feature preserves the contents of the window when an
interactive application starts and restores it on exit, so that any output
visible before the application starts reappears unchanged after it exits.
.Pp
.It Xo Ic remain-on-exit
.Op Ic on | off
.Xc
.D1 (alias: Ic showw )
List the window options or a single option for
.Ar target-window ,
or the global window options if
.Fl g
is used.
.Fl v
shows only the option value, not the name.
A pane with this flag set is not destroyed when the program running in it
exits.
The pane may be reactivated with the
.Ic respawn-pane
command.
.Pp
.It Ic window-active-style Ar style
Set the pane style when it is the active pane.
For how to specify
.Ar style ,
see the
.Sx STYLES
section.
.Pp
.It Ic window-style Ar style
Set the pane style.
For how to specify
.Ar style ,
see the
.Sx STYLES
section.
.El
.Sh HOOKS
.Nm
@ -3924,7 +3925,7 @@ flag with a
.Ar format
argument.
This is a string which controls the output format of the command.
Replacement variables are enclosed in
Format variables are enclosed in
.Ql #{
and
.Ql } ,
@ -3983,7 +3984,7 @@ For example:
#{?pane_in_mode,#[fg=white#,bg=red],#[fg=red#,bg=white]}#W .
.Ed
.Pp
Comparisons may be expressed by prefixing two comma-separated
String comparisons may be expressed by prefixing two comma-separated
alternatives by
.Ql == ,
.Ql != ,
@ -4001,25 +4002,45 @@ if running on
.Ql myhost ,
otherwise by
.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}} .
.Pp
An
.Ql m
specifies an
.Xr fnmatch 3
or regular expression comparison.
The first argument is the pattern and the second the string to compare.
An optional third argument specifies flags:
.Ql r
means the pattern is a regular expression instead of the default
.Xr fnmatch 3
pattern, and
.Ql i
means to ignore case.
For example:
.Ql #{m:*foo*,#{host}}
or
.Ql #{m/ri:^A,MYVAR} .
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.
pattern or regular expression in the pane content and evaluates to zero if not
found, or a line number if found.
Like
.Ql m ,
an
.Ql r
flag means search for a regular expression and
.Ql i
ignores case.
For example:
.Ql #{C/r:^Start}
.Pp
A limit may be placed on the length of the resultant string by prefixing it
by an
@ -4065,7 +4086,7 @@ will expand the format twice, for example
.Ql #{E:status-left}
is the result of expanding the content of the
.Ic status-left
option rather than the content itself.
option rather than the option itself.
.Ql T:
is like
.Ql E:
@ -4092,8 +4113,16 @@ will substitute
with
.Ql bar
throughout.
The first argument may be an extended regular expression and a final argument may be
.Ql i
to ignore case, for example
.Ql s/a(.)/\e1x/i:
would change
.Ql abABab
into
.Ql bxBxbx .
.Pp
In addition, the first line of a shell command's output may be inserted using
In addition, the last line of a shell command's output may be inserted using
.Ql #() .
For example,
.Ql #(uptime)
@ -4112,10 +4141,18 @@ global environment set (see the
.Sx GLOBAL AND SESSION ENVIRONMENT
section).
.Pp
An
.Ql l
specifies that a string should be interpreted literally and not expanded.
For example
.Ql #{l:#{?pane_in_mode,yes,no}}
will be replaced by
.Ql #{?pane_in_mode,yes,no} .
.Pp
The following variables are available, where appropriate:
.Bl -column "XXXXXXXXXXXXXXXXXXX" "XXXXX"
.It Sy "Variable name" Ta Sy "Alias" Ta Sy "Replaced with"
.It Li "alternate_on" Ta "" Ta "If pane is in alternate screen"
.It Li "alternate_on" Ta "" Ta "1 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"
@ -4166,11 +4203,14 @@ The following variables are available, where appropriate:
.It Li "mouse_all_flag" Ta "" Ta "Pane mouse all flag"
.It Li "mouse_any_flag" Ta "" Ta "Pane mouse any flag"
.It Li "mouse_button_flag" Ta "" Ta "Pane mouse button flag"
.It Li "mouse_line" Ta "" Ta "Line under mouse, if any"
.It Li "mouse_sgr_flag" Ta "" Ta "Pane mouse SGR flag"
.It Li "mouse_standard_flag" Ta "" Ta "Pane mouse standard flag"
.It Li "mouse_utf8_flag" Ta "" Ta "Pane mouse UTF-8 flag"
.It Li "mouse_word" Ta "" Ta "Word under mouse, if any"
.It Li "mouse_x" Ta "" Ta "Mouse X position, if any"
.It Li "mouse_y" Ta "" Ta "Mouse Y position, if any"
.It Li "mouse_word" Ta "" Ta "Word under mouse, if any"
.It Li "mouse_line" Ta "" Ta "Line under mouse, if any"
.It Li "origin_flag" Ta "" Ta "Pane origin 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"
@ -4184,19 +4224,19 @@ The following variables are available, where appropriate:
.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_in_mode" Ta "" Ta "1 if pane is in a mode"
.It Li "pane_index" Ta "#P" Ta "Index of pane"
.It Li "pane_input_off" Ta "" Ta "If input to pane is disabled"
.It Li "pane_input_off" Ta "" Ta "1 if input to pane is disabled"
.It Li "pane_left" Ta "" Ta "Left of pane"
.It Li "pane_marked" Ta "" Ta "1 if this is the marked pane"
.It Li "pane_marked_set" Ta "" Ta "1 if a marked pane is set"
.It Li "pane_mode" Ta "" Ta "Name of pane mode, if any."
.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_synchronized" Ta "" Ta "1 if pane is synchronized"
.It Li "pane_tabs" Ta "" Ta "Pane tab positions"
.It Li "pane_title" Ta "#T" Ta "Title of pane"
.It Li "pane_top" Ta "" Ta "Top of pane"
@ -4325,10 +4365,12 @@ to unset.
.Ic align=right
.Xc
Align text to the left, centre or right of the available space if appropriate.
.It Ic fill=colour
Fill the available space with a background colour if appropriate.
.It Xo Ic list=on ,
.Ic list=focus ,
.Ic list=left-marker ,
.Ic list=right=marker ,
.Ic list=right-marker ,
.Ic nolist
.Xc
Mark the position of the various window list components in the
@ -4560,7 +4602,7 @@ session option.
Commands related to the status line are as follows:
.Bl -tag -width Ds
.It Xo Ic command-prompt
.Op Fl 1i
.Op Fl 1Ni
.Op Fl I Ar inputs
.Op Fl p Ar prompts
.Op Fl t Ar target-client
@ -4610,6 +4652,8 @@ but any quotation marks are escaped.
.Fl 1
makes the prompt only accept one key press, in this case the resulting input
is a single character.
.Fl N
makes the prompt only accept numeric key presses.
.Fl i
executes the command every time the prompt input changes instead of when the
user exits the command prompt.
@ -4621,7 +4665,7 @@ option:
.Bl -column "FunctionXXXXXXXXXXXXXXXXXXXXXXXXX" "viXXXX" "emacsX" -offset indent
.It Sy "Function" Ta Sy "vi" Ta Sy "emacs"
.It Li "Cancel command prompt" Ta "Escape" Ta "Escape"
.It Li "Delete current word" Ta "" Ta "C-w"
.It Li "Delete from cursor to start of word" Ta "" Ta "C-w"
.It Li "Delete entire command" Ta "d" Ta "C-u"
.It Li "Delete from cursor to end" Ta "D" Ta "C-k"
.It Li "Execute command" Ta "Enter" Ta "Enter"
@ -4739,8 +4783,7 @@ section; information is taken from
.Ar target-pane
if
.Fl t
is given, otherwise the active pane for the session attached to
.Ar target-client .
is given, otherwise the active pane.
.Pp
.Fl v
prints verbose logging as the format is parsed and
@ -5032,11 +5075,33 @@ $ printf '\e033]12;red\e033\e\e'
.Ed
.It Em \&Smol
Enable the overline attribute.
The capability is usually SGR 53 and can be added to
.Ic terminal-overrides
as:
.Bd -literal -offset indent
Smol=\eE[53m
.Ed
.It Em \&Smulx
Set a styled underline.
The single parameter is one of: 0 for no underline, 1 for normal
underline, 2 for double underline, 3 for curly underline, 4 for dotted
underline and 5 for dashed underline.
Set a styled underscore.
The single parameter is one of: 0 for no underscore, 1 for normal
underscore, 2 for double underscore, 3 for curly underscore, 4 for dotted
underscore and 5 for dashed underscore.
The capability can typically be added to
.Ic terminal-overrides
as:
.Bd -literal -offset indent
Smulx=\eE[4::%p1%dm
.Ed
.It Em \&Setulc
Set the underscore colour.
The argument is (red * 65536) + (green * 256) + blue where each is between 0
and 255.
The capability can typically be added to
.Ic terminal-overrides
as:
.Bd -literal -offset indent
Setulc=\eE[58::2::%p1%{65536}%/%d::%p1%{256}%/%{255}%&%d::%p1%{255}%&%d%;m
.Ed
.It Em \&Ss , Se
Set or reset the cursor style.
If set, a sequence such as this may be used

6
tmux.c
View File

@ -321,11 +321,11 @@ main(int argc, char **argv)
global_s_options = options_create(NULL);
global_w_options = options_create(NULL);
for (oe = options_table; oe->name != NULL; oe++) {
if (oe->scope == OPTIONS_TABLE_SERVER)
if (oe->scope & OPTIONS_TABLE_SERVER)
options_default(global_options, oe);
if (oe->scope == OPTIONS_TABLE_SESSION)
if (oe->scope & OPTIONS_TABLE_SESSION)
options_default(global_s_options, oe);
if (oe->scope == OPTIONS_TABLE_WINDOW)
if (oe->scope & OPTIONS_TABLE_WINDOW)
options_default(global_w_options, oe);
}

64
tmux.h
View File

@ -113,9 +113,10 @@ struct winlink;
#define KEYC_CTRL 0x400000000000ULL
#define KEYC_SHIFT 0x800000000000ULL
#define KEYC_XTERM 0x1000000000000ULL
#define KEYC_LITERAL 0x2000000000000ULL
/* Mask to obtain key w/o modifiers. */
#define KEYC_MASK_MOD (KEYC_ESCAPE|KEYC_CTRL|KEYC_SHIFT|KEYC_XTERM)
#define KEYC_MASK_MOD (KEYC_ESCAPE|KEYC_CTRL|KEYC_SHIFT|KEYC_XTERM|KEYC_LITERAL)
#define KEYC_MASK_KEY (~KEYC_MASK_MOD)
/* Is this a mouse key? */
@ -429,6 +430,7 @@ enum tty_code_code {
TTYC_SETAF,
TTYC_SETRGBB,
TTYC_SETRGBF,
TTYC_SETULC,
TTYC_SGR0,
TTYC_SITM,
TTYC_SMACS,
@ -597,12 +599,13 @@ enum utf8_state {
/* Grid cell data. */
struct grid_cell {
u_char flags;
struct utf8_data data; /* 21 bytes */
u_short attr;
u_char flags;
int fg;
int bg;
struct utf8_data data;
};
int us;
} __packed;
struct grid_cell_entry {
u_char flags;
union {
@ -682,6 +685,7 @@ TAILQ_HEAD(style_ranges, style_range);
struct style {
struct grid_cell gc;
int fill;
enum style_align align;
enum style_list list;
@ -812,6 +816,7 @@ struct window_pane {
u_int active_point;
struct window *window;
struct options *options;
struct layout_cell *layout_cell;
struct layout_cell *saved_layout_cell;
@ -838,6 +843,7 @@ struct window_pane {
#define PANE_STATUSREADY 0x200
#define PANE_STATUSDRAWN 0x400
#define PANE_EMPTY 0x800
#define PANE_STYLECHANGED 0x1000
int argc;
char **argv;
@ -856,7 +862,8 @@ struct window_pane {
struct input_ctx *ictx;
struct style style;
struct style cached_style;
struct style cached_active_style;
int *palette;
int pipe_fd;
@ -916,7 +923,6 @@ struct window {
#define WINDOW_ACTIVITY 0x2
#define WINDOW_SILENCE 0x4
#define WINDOW_ZOOMED 0x8
#define WINDOW_STYLECHANGED 0x10
#define WINDOW_ALERTFLAGS (WINDOW_BELL|WINDOW_ACTIVITY|WINDOW_SILENCE)
int alerts_queued;
@ -924,9 +930,6 @@ struct window {
struct options *options;
struct style style;
struct style active_style;
u_int references;
TAILQ_HEAD(, winlink) winlinks;
@ -958,6 +961,11 @@ TAILQ_HEAD(winlink_stack, winlink);
#define WINDOW_SIZE_SMALLEST 1
#define WINDOW_SIZE_MANUAL 2
/* Pane border status option. */
#define PANE_STATUS_OFF 0
#define PANE_STATUS_TOP 1
#define PANE_STATUS_BOTTOM 2
/* Layout direction. */
enum layout_type {
LAYOUT_LEFTRIGHT,
@ -1326,6 +1334,7 @@ struct cmd_parse_input {
#define CMD_PARSE_QUIET 0x1
#define CMD_PARSE_PARSEONLY 0x2
#define CMD_PARSE_NOALIAS 0x4
#define CMD_PARSE_VERBOSE 0x8
const char *file;
u_int line;
@ -1491,7 +1500,7 @@ struct client {
#define CLIENT_REPEAT 0x20
#define CLIENT_SUSPENDED 0x40
#define CLIENT_ATTACHED 0x80
/* 0x100 unused */
#define CLIENT_EXITED 0x100
#define CLIENT_DEAD 0x200
#define CLIENT_REDRAWBORDERS 0x400
#define CLIENT_READONLY 0x800
@ -1509,6 +1518,7 @@ struct client {
#define CLIENT_STATUSOFF 0x800000
#define CLIENT_REDRAWSTATUSALWAYS 0x1000000
#define CLIENT_REDRAWOVERLAY 0x2000000
#define CLIENT_CONTROL_NOOUTPUT 0x4000000
#define CLIENT_ALLREDRAWFLAGS \
(CLIENT_REDRAWWINDOW| \
CLIENT_REDRAWSTATUS| \
@ -1606,12 +1616,11 @@ enum options_table_type {
OPTIONS_TABLE_COMMAND
};
enum options_table_scope {
OPTIONS_TABLE_NONE,
OPTIONS_TABLE_SERVER,
OPTIONS_TABLE_SESSION,
OPTIONS_TABLE_WINDOW
};
#define OPTIONS_TABLE_NONE 0
#define OPTIONS_TABLE_SERVER 0x1
#define OPTIONS_TABLE_SESSION 0x2
#define OPTIONS_TABLE_WINDOW 0x4
#define OPTIONS_TABLE_PANE 0x8
#define OPTIONS_TABLE_IS_ARRAY 0x1
#define OPTIONS_TABLE_IS_HOOK 0x2
@ -1619,7 +1628,7 @@ enum options_table_scope {
struct options_table_entry {
const char *name;
enum options_table_type type;
enum options_table_scope scope;
int scope;
int flags;
u_int minimum;
@ -1781,6 +1790,7 @@ void notify_pane(const char *, struct window_pane *);
/* options.c */
struct options *options_create(struct options *);
void options_free(struct options *);
void options_set_parent(struct options *, struct options *);
struct options_entry *options_first(struct options *);
struct options_entry *options_next(struct options_entry *);
struct options_entry *options_empty(struct options *,
@ -1820,7 +1830,10 @@ struct options_entry *options_set_number(struct options *, const char *,
long long);
struct options_entry *options_set_style(struct options *, const char *, int,
const char *);
enum options_table_scope options_scope_from_flags(struct args *, int,
int options_scope_from_name(struct args *, int,
const char *, struct cmd_find_state *, struct options **,
char **);
int options_scope_from_flags(struct args *, int,
struct cmd_find_state *, struct options **, char **);
/* options-table.c */
@ -2011,7 +2024,7 @@ extern const struct cmd_entry *cmd_table[];
/* cmd-attach-session.c */
enum cmd_retval cmd_attach_session(struct cmdq_item *, const char *, int, int,
const char *, int);
int, const char *, int);
/* cmd-parse.c */
void cmd_parse_empty(struct cmd_parse_input *);
@ -2038,6 +2051,7 @@ void cmdq_insert_after(struct cmdq_item *, struct cmdq_item *);
void cmdq_append(struct client *, struct cmdq_item *);
void cmdq_insert_hook(struct session *, struct cmdq_item *,
struct cmd_find_state *, const char *, ...);
void cmdq_continue(struct cmdq_item *);
void printflike(3, 4) cmdq_format(struct cmdq_item *, const char *,
const char *, ...);
u_int cmdq_next(struct client *);
@ -2187,7 +2201,8 @@ int colour_join_rgb(u_char, u_char, u_char);
void colour_split_rgb(int, u_char *, u_char *, u_char *);
const char *colour_tostring(int);
int colour_fromstring(const char *s);
u_char colour_256to16(u_char);
int colour_256toRGB(int);
int colour_256to16(int);
/* attributes.c */
const char *attributes_tostring(int);
@ -2352,7 +2367,6 @@ struct window *window_find_by_id_str(const char *);
struct window *window_find_by_id(u_int);
void window_update_activity(struct window *);
struct window *window_create(u_int, u_int);
void window_destroy(struct window *);
void window_pane_set_event(struct window_pane *);
struct window_pane *window_get_active_at(struct window *, u_int, u_int);
struct window_pane *window_find_string(struct window *, const char *);
@ -2397,7 +2411,8 @@ void window_pane_key(struct window_pane *, struct client *,
struct session *, struct winlink *, key_code,
struct mouse_event *);
int window_pane_visible(struct window_pane *);
u_int window_pane_search(struct window_pane *, const char *);
u_int window_pane_search(struct window_pane *, const char *, int,
int);
const char *window_printable_flags(struct winlink *);
struct window_pane *window_pane_find_up(struct window_pane *);
struct window_pane *window_pane_find_down(struct window_pane *);
@ -2425,7 +2440,7 @@ void layout_set_size(struct layout_cell *, u_int, u_int, u_int,
u_int);
void layout_make_leaf(struct layout_cell *, struct window_pane *);
void layout_make_node(struct layout_cell *, enum layout_type);
void layout_fix_offsets(struct layout_cell *);
void layout_fix_offsets(struct window *);
void layout_fix_panes(struct window *);
void layout_resize_adjust(struct window *, struct layout_cell *,
enum layout_type, int);
@ -2634,4 +2649,7 @@ int style_is_default(struct style *);
struct winlink *spawn_window(struct spawn_context *, char **);
struct window_pane *spawn_pane(struct spawn_context *, char **);
/* regsub.c */
char *regsub(const char *, const char *, const char *, int);
#endif /* TMUX_H */

View File

@ -249,6 +249,7 @@ static const struct tty_term_code_entry tty_term_codes[] = {
[TTYC_SETAF] = { TTYCODE_STRING, "setaf" },
[TTYC_SETRGBB] = { TTYCODE_STRING, "setrgbb" },
[TTYC_SETRGBF] = { TTYCODE_STRING, "setrgbf" },
[TTYC_SETULC] = { TTYCODE_STRING, "Setulc" },
[TTYC_SE] = { TTYCODE_STRING, "Se" },
[TTYC_SGR0] = { TTYCODE_STRING, "sgr0" },
[TTYC_SITM] = { TTYCODE_STRING, "sitm" },
@ -691,7 +692,7 @@ tty_term_describe(struct tty_term *term, enum tty_code_code code)
break;
case TTYCODE_STRING:
strnvis(out, term->codes[code].value.string, sizeof out,
VIS_OCTAL|VIS_TAB|VIS_NL);
VIS_OCTAL|VIS_CSTYLE|VIS_TAB|VIS_NL);
xsnprintf(s, sizeof s, "%4u: %s: (string) %s",
code, tty_term_codes[code].name,
out);

138
tty.c
View File

@ -49,8 +49,11 @@ static void tty_check_fg(struct tty *, struct window_pane *,
struct grid_cell *);
static void tty_check_bg(struct tty *, struct window_pane *,
struct grid_cell *);
static void tty_check_us(struct tty *, struct window_pane *,
struct grid_cell *);
static void tty_colours_fg(struct tty *, const struct grid_cell *);
static void tty_colours_bg(struct tty *, const struct grid_cell *);
static void tty_colours_us(struct tty *, const struct grid_cell *);
static void tty_region_pane(struct tty *, const struct tty_ctx *, u_int,
u_int);
@ -527,6 +530,12 @@ tty_putc(struct tty *tty, u_char ch)
{
const char *acs;
if ((tty->term->flags & TERM_EARLYWRAP) &&
ch >= 0x20 && ch != 0x7f &&
tty->cy == tty->sy - 1 &&
tty->cx + 1 >= tty->sx)
return;
if (tty->cell.attr & GRID_ATTR_CHARSET) {
acs = tty_acs_get(tty, ch);
if (acs != NULL)
@ -557,6 +566,11 @@ tty_putc(struct tty *tty, u_char ch)
void
tty_putn(struct tty *tty, const void *buf, size_t len, u_int width)
{
if ((tty->term->flags & TERM_EARLYWRAP) &&
tty->cy == tty->sy - 1 &&
tty->cx + len >= tty->sx)
len = tty->sx - tty->cx - 1;
tty_add(tty, buf, len);
if (tty->cx + width > tty->sx) {
tty->cx = (tty->cx + width) - tty->sx;
@ -1049,17 +1063,17 @@ tty_clamp_area(struct tty *tty, const struct tty_ctx *ctx, u_int px, u_int py,
*y = ctx->yoff + py - ctx->oy;
*ry = ny;
} else if (yoff < ctx->oy && yoff + ny > ctx->oy + ctx->sy) {
/* Both left and right not visible. */
/* Both top and bottom not visible. */
*j = ctx->oy;
*y = 0;
*ry = ctx->sy;
} else if (yoff < ctx->oy) {
/* Left not visible. */
/* Top not visible. */
*j = ctx->oy - (ctx->yoff + py);
*y = 0;
*ry = ny - *j;
} else {
/* Right not visible. */
/* Bottom not visible. */
*j = 0;
*y = (ctx->yoff + py) - ctx->oy;
*ry = ctx->sy - *y;
@ -1203,7 +1217,7 @@ tty_draw_line(struct tty *tty, struct window_pane *wp, struct screen *s,
const struct grid_cell *gcp;
struct grid_line *gl;
u_int i, j, ux, sx, width;
int flags, cleared = 0;
int flags, cleared = 0, wrapped = 0;
char buf[512];
size_t len;
u_int cellsize;
@ -1260,8 +1274,10 @@ tty_draw_line(struct tty *tty, struct window_pane *wp, struct screen *s,
tty_putcode(tty, TTYC_EL1);
cleared = 1;
}
} else
} else {
log_debug("%s: wrapped line %u", __func__, aty);
wrapped = 1;
}
memcpy(&last, &grid_default_cell, sizeof last);
len = 0;
@ -1276,6 +1292,7 @@ tty_draw_line(struct tty *tty, struct window_pane *wp, struct screen *s,
gcp->attr != last.attr ||
gcp->fg != last.fg ||
gcp->bg != last.bg ||
gcp->us != last.us ||
ux + width + gcp->data.width > nx ||
(sizeof buf) - len < gcp->data.size)) {
tty_attributes(tty, &last, wp);
@ -1284,13 +1301,15 @@ tty_draw_line(struct tty *tty, struct window_pane *wp, struct screen *s,
tty_clear_line(tty, wp, aty, atx + ux, width,
last.bg);
} else {
tty_cursor(tty, atx + ux, aty);
if (!wrapped || atx != 0 || ux != 0)
tty_cursor(tty, atx + ux, aty);
tty_putn(tty, buf, len, width);
}
ux += width;
len = 0;
width = 0;
wrapped = 0;
}
if (gcp->flags & GRID_FLAG_SELECTED)
@ -1324,7 +1343,8 @@ tty_draw_line(struct tty *tty, struct window_pane *wp, struct screen *s,
log_debug("%s: %zu cleared (end)", __func__, len);
tty_clear_line(tty, wp, aty, atx + ux, width, last.bg);
} else {
tty_cursor(tty, atx + ux, aty);
if (!wrapped || atx != 0 || ux != 0)
tty_cursor(tty, atx + ux, aty);
tty_putn(tty, buf, len, width);
}
ux += width;
@ -2121,10 +2141,11 @@ tty_attributes(struct tty *tty, const struct grid_cell *gc,
/* Ignore cell if it is the same as the last one. */
if (wp != NULL &&
(int)wp->id == tty->last_wp &&
~(wp->window->flags & WINDOW_STYLECHANGED) &&
~(wp->flags & PANE_STYLECHANGED) &&
gc->attr == tty->last_cell.attr &&
gc->fg == tty->last_cell.fg &&
gc->bg == tty->last_cell.bg)
gc->bg == tty->last_cell.bg &&
gc->us == tty->last_cell.us)
return;
tty->last_wp = (wp != NULL ? (int)wp->id : -1);
memcpy(&tty->last_cell, gc, sizeof tty->last_cell);
@ -2152,14 +2173,18 @@ tty_attributes(struct tty *tty, const struct grid_cell *gc,
/* Fix up the colours if necessary. */
tty_check_fg(tty, wp, &gc2);
tty_check_bg(tty, wp, &gc2);
tty_check_us(tty, wp, &gc2);
/* If any bits are being cleared, reset everything. */
if (tc->attr & ~gc2.attr)
/*
* If any bits are being cleared or the underline colour is now default,
* reset everything.
*/
if ((tc->attr & ~gc2.attr) || (tc->us != gc2.us && gc2.us == 0))
tty_reset(tty);
/*
* Set the colours. This may call tty_reset() (so it comes next) and
* may add to (NOT remove) the desired attributes by changing new_attr.
* may add to (NOT remove) the desired attributes.
*/
tty_colours(tty, &gc2);
@ -2212,7 +2237,7 @@ tty_colours(struct tty *tty, const struct grid_cell *gc)
int have_ax;
/* No changes? Nothing is necessary. */
if (gc->fg == tc->fg && gc->bg == tc->bg)
if (gc->fg == tc->fg && gc->bg == tc->bg && gc->us == tc->us)
return;
/*
@ -2260,6 +2285,10 @@ tty_colours(struct tty *tty, const struct grid_cell *gc)
*/
if (!COLOUR_DEFAULT(gc->bg) && gc->bg != tc->bg)
tty_colours_bg(tty, gc);
/* Set the underscore color. */
if (gc->us != tc->us)
tty_colours_us(tty, gc);
}
static void
@ -2374,6 +2403,22 @@ tty_check_bg(struct tty *tty, struct window_pane *wp, struct grid_cell *gc)
gc->bg -= 90;
}
static void
tty_check_us(__unused struct tty *tty, struct window_pane *wp, struct grid_cell *gc)
{
int c;
/* Perform substitution if this pane has a palette. */
if (~gc->flags & GRID_FLAG_NOPALETTE) {
if ((c = window_pane_get_palette(wp, gc->us)) != -1)
gc->us = c;
}
/* Underscore colour is set as RGB so convert a 256 colour to RGB. */
if (gc->us & COLOUR_FLAG_256)
gc->us = colour_256toRGB (gc->us);
}
static void
tty_colours_fg(struct tty *tty, const struct grid_cell *gc)
{
@ -2438,6 +2483,31 @@ save_bg:
tc->bg = gc->bg;
}
static void
tty_colours_us(struct tty *tty, const struct grid_cell *gc)
{
struct grid_cell *tc = &tty->cell;
u_int c;
u_char r, g, b;
/* Must be an RGB colour - this should never happen. */
if (~gc->us & COLOUR_FLAG_RGB)
return;
/*
* Setulc follows the ncurses(3) one argument "direct colour"
* capability format. Calculate the colour value.
*/
colour_split_rgb(gc->us, &r, &g, &b);
c = (65536 * r) + (256 * g) + b;
/* Write the colour. */
tty_putcode1(tty, TTYC_SETULC, c);
/* Save the new values in the terminal current cell. */
tc->us = gc->us;
}
static int
tty_try_colour(struct tty *tty, int colour, const char *type)
{
@ -2503,30 +2573,28 @@ fallback_256:
static void
tty_default_colours(struct grid_cell *gc, struct window_pane *wp)
{
struct window *w = wp->window;
struct options *oo = w->options;
struct style *active, *pane, *window;
int c;
struct options *oo = wp->options;
struct style *style, *active_style;
int c;
if (w->flags & WINDOW_STYLECHANGED) {
w->flags &= ~WINDOW_STYLECHANGED;
active = options_get_style(oo, "window-active-style");
style_copy(&w->active_style, active);
window = options_get_style(oo, "window-style");
style_copy(&w->style, window);
if (wp->flags & PANE_STYLECHANGED) {
wp->flags &= ~PANE_STYLECHANGED;
active_style = options_get_style(oo, "window-active-style");
style = options_get_style(oo, "window-style");
style_copy(&wp->cached_active_style, active_style);
style_copy(&wp->cached_style, style);
} else {
active = &w->active_style;
window = &w->style;
active_style = &wp->cached_active_style;
style = &wp->cached_style;
}
pane = &wp->style;
if (gc->fg == 8) {
if (pane->gc.fg != 8)
gc->fg = pane->gc.fg;
else if (wp == w->active && active->gc.fg != 8)
gc->fg = active->gc.fg;
if (wp == wp->window->active && active_style->gc.fg != 8)
gc->fg = active_style->gc.fg;
else
gc->fg = window->gc.fg;
gc->fg = style->gc.fg;
if (gc->fg != 8) {
c = window_pane_get_palette(wp, gc->fg);
@ -2536,12 +2604,10 @@ tty_default_colours(struct grid_cell *gc, struct window_pane *wp)
}
if (gc->bg == 8) {
if (pane->gc.bg != 8)
gc->bg = pane->gc.bg;
else if (wp == w->active && active->gc.bg != 8)
gc->bg = active->gc.bg;
if (wp == wp->window->active && active_style->gc.bg != 8)
gc->bg = active_style->gc.bg;
else
gc->bg = window->gc.bg;
gc->bg = style->gc.bg;
if (gc->bg != 8) {
c = window_pane_get_palette(wp, gc->bg);

View File

@ -245,7 +245,7 @@ window_buffer_draw(__unused void *modedata, void *itemdata,
at = 0;
while (end != pdata + psize && *end != '\n') {
if ((sizeof line) - at > 5) {
cp = vis(line + at, *end, VIS_TAB|VIS_OCTAL, 0);
cp = vis(line + at, *end, VIS_OCTAL|VIS_TAB, 0);
at = cp - line;
}
end++;

View File

@ -577,6 +577,7 @@ window_copy_resize(struct window_mode_entry *wme, u_int sx, u_int sy)
struct window_copy_mode_data *data = wme->data;
struct screen *s = &data->screen;
struct screen_write_ctx ctx;
int search;
screen_resize(s, sx, sy, 1);
if (data->backing != &wp->base)
@ -589,13 +590,15 @@ window_copy_resize(struct window_mode_entry *wme, u_int sx, u_int sy)
if (data->oy > screen_hsize(data->backing))
data->oy = screen_hsize(data->backing);
search = (data->searchmark != NULL);
window_copy_clear_selection(wme);
window_copy_clear_marks(wme);
screen_write_start(&ctx, NULL, s);
window_copy_write_lines(wme, &ctx, 0, screen_size_y(s) - 1);
screen_write_stop(&ctx);
if (data->searchmark != NULL)
if (search)
window_copy_search_marks(wme, NULL);
data->searchx = data->cx;
data->searchy = data->cy;
@ -2700,7 +2703,7 @@ window_copy_append_selection(struct window_mode_entry *wme)
struct window_pane *wp = wme->wp;
char *buf;
struct paste_buffer *pb;
const char *bufdata, *bufname;
const char *bufdata, *bufname = NULL;
size_t len, bufsize;
struct screen_write_ctx ctx;
@ -3027,8 +3030,8 @@ window_copy_cursor_up(struct window_mode_entry *wme, int scroll_only)
if (data->lineflag == LINE_SEL_LEFT_RIGHT && oy == data->sely)
window_copy_other_end(wme);
data->cx = data->lastcx;
if (scroll_only || data->cy == 0) {
data->cx = data->lastcx;
window_copy_scroll_down(wme, 1);
if (scroll_only) {
if (data->cy == screen_size_y(s) - 1)
@ -3037,7 +3040,7 @@ window_copy_cursor_up(struct window_mode_entry *wme, int scroll_only)
window_copy_redraw_lines(wme, data->cy, 2);
}
} else {
window_copy_update_cursor(wme, data->cx, data->cy - 1);
window_copy_update_cursor(wme, data->lastcx, data->cy - 1);
if (window_copy_update_selection(wme, 1)) {
if (data->cy == screen_size_y(s) - 1)
window_copy_redraw_lines(wme, data->cy, 1);
@ -3077,13 +3080,13 @@ window_copy_cursor_down(struct window_mode_entry *wme, int scroll_only)
if (data->lineflag == LINE_SEL_RIGHT_LEFT && oy == data->endsely)
window_copy_other_end(wme);
data->cx = data->lastcx;
if (scroll_only || data->cy == screen_size_y(s) - 1) {
data->cx = data->lastcx;
window_copy_scroll_up(wme, 1);
if (scroll_only && data->cy > 0)
window_copy_redraw_lines(wme, data->cy - 1, 2);
} else {
window_copy_update_cursor(wme, data->cx, data->cy + 1);
window_copy_update_cursor(wme, data->lastcx, data->cy + 1);
if (window_copy_update_selection(wme, 1))
window_copy_redraw_lines(wme, data->cy - 1, 2);
}

185
window.c
View File

@ -19,9 +19,11 @@
#include <sys/types.h>
#include <sys/ioctl.h>
#include <ctype.h>
#include <errno.h>
#include <fcntl.h>
#include <fnmatch.h>
#include <regex.h>
#include <signal.h>
#include <stdint.h>
#include <stdlib.h>
@ -310,7 +312,7 @@ window_create(u_int sx, u_int sy)
w = xcalloc(1, sizeof *w);
w->name = NULL;
w->flags = WINDOW_STYLECHANGED;
w->flags = 0;
TAILQ_INIT(&w->panes);
w->active = NULL;
@ -334,7 +336,7 @@ window_create(u_int sx, u_int sy)
return (w);
}
void
static void
window_destroy(struct window *w)
{
log_debug("window @%u destroyed (%d references)", w->id, w->references);
@ -408,6 +410,7 @@ window_set_name(struct window *w, const char *new_name)
void
window_resize(struct window *w, u_int sx, u_int sy)
{
log_debug("%s: @%u resize %ux%u", __func__, w->id, sx, sy);
w->sx = sx;
w->sy = sy;
}
@ -447,31 +450,37 @@ window_set_active_pane(struct window *w, struct window_pane *wp, int notify)
void
window_redraw_active_switch(struct window *w, struct window_pane *wp)
{
struct style *sy;
struct style *sy1, *sy2;
int c1, c2;
if (wp == w->active)
return;
/*
* If window-style and window-active-style are the same, we don't need
* to redraw panes when switching active panes.
*/
sy = options_get_style(w->options, "window-active-style");
if (style_equal(sy, options_get_style(w->options, "window-style")))
return;
/*
* If the now active or inactive pane do not have a custom style or if
* the palette is different, they need to be redrawn.
*/
if (window_pane_get_palette(w->active, w->active->style.gc.fg) != -1 ||
window_pane_get_palette(w->active, w->active->style.gc.bg) != -1 ||
style_is_default(&w->active->style))
w->active->flags |= PANE_REDRAW;
if (window_pane_get_palette(wp, wp->style.gc.fg) != -1 ||
window_pane_get_palette(wp, wp->style.gc.bg) != -1 ||
style_is_default(&wp->style))
wp->flags |= PANE_REDRAW;
for (;;) {
/*
* If the active and inactive styles or palettes are different,
* need to redraw the panes.
*/
sy1 = &wp->cached_style;
sy2 = &wp->cached_active_style;
if (!style_equal(sy1, sy2))
wp->flags |= PANE_REDRAW;
else {
c1 = window_pane_get_palette(wp, sy1->gc.fg);
c2 = window_pane_get_palette(wp, sy2->gc.fg);
if (c1 != c2)
wp->flags |= PANE_REDRAW;
else {
c1 = window_pane_get_palette(wp, sy1->gc.bg);
c2 = window_pane_get_palette(wp, sy2->gc.bg);
if (c1 != c2)
wp->flags |= PANE_REDRAW;
}
}
if (wp == w->active)
break;
wp = w->active;
}
}
struct window_pane *
@ -775,6 +784,8 @@ window_pane_create(struct window *w, u_int sx, u_int sy, u_int hlimit)
wp = xcalloc(1, sizeof *wp);
wp->window = w;
wp->options = options_create(w->options);
wp->flags = PANE_STYLECHANGED;
wp->id = next_window_pane_id++;
RB_INSERT(window_pane_tree, &all_window_panes, wp);
@ -802,8 +813,8 @@ window_pane_create(struct window *w, u_int sx, u_int sy, u_int hlimit)
wp->pipe_event = NULL;
wp->saved_grid = NULL;
style_set(&wp->style, &grid_default_cell);
wp->saved_cx = UINT_MAX;
wp->saved_cy = UINT_MAX;
screen_init(&wp->base, sx, sy, hlimit);
wp->screen = &wp->base;
@ -850,6 +861,7 @@ window_pane_destroy(struct window_pane *wp)
RB_REMOVE(window_pane_tree, &all_window_panes, wp);
options_free(wp->options);
free((void *)wp->cwd);
free(wp->shell);
cmd_free_argv(wp->argc, wp->argv);
@ -913,6 +925,7 @@ window_pane_resize(struct window_pane *wp, u_int sx, u_int sy)
wp->sx = sx;
wp->sy = sy;
log_debug("%s: %%%u resize %ux%u", __func__, wp->id, sx, sy);
screen_resize(&wp->base, sx, sy, wp->saved_grid == NULL);
wme = TAILQ_FIRST(&wp->modes);
@ -935,7 +948,7 @@ window_pane_alternate_on(struct window_pane *wp, struct grid_cell *gc,
if (wp->saved_grid != NULL)
return;
if (!options_get_number(wp->window->options, "alternate-screen"))
if (!options_get_number(wp->options, "alternate-screen"))
return;
sx = screen_size_x(s);
sy = screen_size_y(s);
@ -963,9 +976,24 @@ window_pane_alternate_off(struct window_pane *wp, struct grid_cell *gc,
struct screen *s = &wp->base;
u_int sx, sy;
if (wp->saved_grid == NULL)
if (!options_get_number(wp->options, "alternate-screen"))
return;
if (!options_get_number(wp->window->options, "alternate-screen"))
/*
* Restore the cursor position and cell. This happens even if not
* currently in the alternate screen.
*/
if (cursor && wp->saved_cx != UINT_MAX && wp->saved_cy != UINT_MAX) {
s->cx = wp->saved_cx;
if (s->cx > screen_size_x(s) - 1)
s->cx = screen_size_x(s) - 1;
s->cy = wp->saved_cy;
if (s->cy > screen_size_y(s) - 1)
s->cy = screen_size_y(s) - 1;
memcpy(gc, &wp->saved_cell, sizeof *gc);
}
if (wp->saved_grid == NULL)
return;
sx = screen_size_x(s);
sy = screen_size_y(s);
@ -977,17 +1005,8 @@ window_pane_alternate_off(struct window_pane *wp, struct grid_cell *gc,
if (sy > wp->saved_grid->sy)
screen_resize(s, sx, wp->saved_grid->sy, 1);
/* Restore the grid, cursor position and cell. */
/* Restore the saved grid. */
grid_duplicate_lines(s->grid, screen_hsize(s), wp->saved_grid, 0, sy);
if (cursor)
s->cx = wp->saved_cx;
if (s->cx > screen_size_x(s) - 1)
s->cx = screen_size_x(s) - 1;
if (cursor)
s->cy = wp->saved_cy;
if (s->cy > screen_size_y(s) - 1)
s->cy = screen_size_y(s) - 1;
memcpy(gc, &wp->saved_cell, sizeof *gc);
/*
* Turn history back on (so resize can use it) and then resize back to
@ -1137,7 +1156,7 @@ window_pane_reset_mode(struct window_pane *wp)
} else {
log_debug("%s: next mode is %s", __func__, next->mode->name);
wp->screen = next->screen;
if (next != NULL && next->mode->resize != NULL)
if (next->mode->resize != NULL)
next->mode->resize(next, wp->sx, wp->sy);
}
wp->flags |= (PANE_REDRAW|PANE_CHANGED);
@ -1199,24 +1218,48 @@ window_pane_visible(struct window_pane *wp)
}
u_int
window_pane_search(struct window_pane *wp, const char *searchstr)
window_pane_search(struct window_pane *wp, const char *term, int regex,
int ignore)
{
struct screen *s = &wp->base;
char *newsearchstr, *line;
regex_t r;
char *new = NULL, *line;
u_int i;
int flags = 0, found;
size_t n;
xasprintf(&newsearchstr, "*%s*", searchstr);
if (!regex) {
if (ignore)
flags |= FNM_CASEFOLD;
xasprintf(&new, "*%s*", term);
} else {
if (ignore)
flags |= REG_ICASE;
if (regcomp(&r, term, flags|REG_EXTENDED) != 0)
return (0);
}
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) {
free(line);
break;
for (n = strlen(line); n > 0; n--) {
if (!isspace((u_char)line[n - 1]))
break;
line[n - 1] = '\0';
}
log_debug("%s: %s", __func__, line);
if (!regex)
found = (fnmatch(new, line, 0) == 0);
else
found = (regexec(&r, line, 0, NULL, 0) == 0);
free(line);
if (found)
break;
}
if (!regex)
free(new);
else
regfree(&r);
free(newsearchstr);
if (i == screen_size_y(s))
return (0);
return (i + 1);
@ -1248,25 +1291,35 @@ window_pane_choose_best(struct window_pane **list, u_int size)
struct window_pane *
window_pane_find_up(struct window_pane *wp)
{
struct window *w;
struct window_pane *next, *best, **list;
u_int edge, left, right, end, size;
int status, found;
if (wp == NULL)
return (NULL);
status = options_get_number(wp->window->options, "pane-border-status");
w = wp->window;
status = options_get_number(w->options, "pane-border-status");
list = NULL;
size = 0;
edge = wp->yoff;
if (edge == (status == 1 ? 1 : 0))
edge = wp->window->sy + 1 - (status == 2 ? 1 : 0);
if (status == PANE_STATUS_TOP) {
if (edge == 1)
edge = w->sy + 1;
} else if (status == PANE_STATUS_BOTTOM) {
if (edge == 0)
edge = w->sy;
} else {
if (edge == 0)
edge = w->sy + 1;
}
left = wp->xoff;
right = wp->xoff + wp->sx;
TAILQ_FOREACH(next, &wp->window->panes, entry) {
TAILQ_FOREACH(next, &w->panes, entry) {
if (next == wp)
continue;
if (next->yoff + next->sy + 1 != edge)
@ -1295,25 +1348,35 @@ window_pane_find_up(struct window_pane *wp)
struct window_pane *
window_pane_find_down(struct window_pane *wp)
{
struct window *w;
struct window_pane *next, *best, **list;
u_int edge, left, right, end, size;
int status, found;
if (wp == NULL)
return (NULL);
status = options_get_number(wp->window->options, "pane-border-status");
w = wp->window;
status = options_get_number(w->options, "pane-border-status");
list = NULL;
size = 0;
edge = wp->yoff + wp->sy + 1;
if (edge >= wp->window->sy - (status == 2 ? 1 : 0))
edge = (status == 1 ? 1 : 0);
if (status == PANE_STATUS_TOP) {
if (edge >= w->sy)
edge = 1;
} else if (status == PANE_STATUS_BOTTOM) {
if (edge >= w->sy - 1)
edge = 0;
} else {
if (edge >= w->sy)
edge = 0;
}
left = wp->xoff;
right = wp->xoff + wp->sx;
TAILQ_FOREACH(next, &wp->window->panes, entry) {
TAILQ_FOREACH(next, &w->panes, entry) {
if (next == wp)
continue;
if (next->yoff != edge)
@ -1342,24 +1405,26 @@ window_pane_find_down(struct window_pane *wp)
struct window_pane *
window_pane_find_left(struct window_pane *wp)
{
struct window *w;
struct window_pane *next, *best, **list;
u_int edge, top, bottom, end, size;
int found;
if (wp == NULL)
return (NULL);
w = wp->window;
list = NULL;
size = 0;
edge = wp->xoff;
if (edge == 0)
edge = wp->window->sx + 1;
edge = w->sx + 1;
top = wp->yoff;
bottom = wp->yoff + wp->sy;
TAILQ_FOREACH(next, &wp->window->panes, entry) {
TAILQ_FOREACH(next, &w->panes, entry) {
if (next == wp)
continue;
if (next->xoff + next->sx + 1 != edge)
@ -1388,24 +1453,26 @@ window_pane_find_left(struct window_pane *wp)
struct window_pane *
window_pane_find_right(struct window_pane *wp)
{
struct window *w;
struct window_pane *next, *best, **list;
u_int edge, top, bottom, end, size;
int found;
if (wp == NULL)
return (NULL);
w = wp->window;
list = NULL;
size = 0;
edge = wp->xoff + wp->sx + 1;
if (edge >= wp->window->sx)
if (edge >= w->sx)
edge = 0;
top = wp->yoff;
bottom = wp->yoff + wp->sy;
TAILQ_FOREACH(next, &wp->window->panes, entry) {
TAILQ_FOREACH(next, &w->panes, entry) {
if (next == wp)
continue;
if (next->xoff != edge)
@ -1487,7 +1554,7 @@ window_pane_input_callback(struct client *c, int closed, void *data)
c->stdin_callback = NULL;
server_client_unref(c);
cdata->item->flags &= ~CMDQ_WAITING;
cmdq_continue(cdata->item);
free(cdata);
return;

View File

@ -111,7 +111,7 @@ xvasprintf(char **ret, const char *fmt, va_list ap)
i = vasprintf(ret, fmt, ap);
if (i < 0 || *ret == NULL)
if (i == -1)
fatalx("xasprintf: %s", strerror(errno));
return i;