Merge branch 'master' into 3.2-rc

This commit is contained in:
Nicholas Marriott 2020-11-03 08:35:52 +00:00
commit ba9962b568
51 changed files with 1276 additions and 687 deletions

11
.github/README.md vendored
View File

@ -19,6 +19,12 @@ suitable yacc (yacc or bison) are needed.
## Installation
### Binary packages
Some platforms provide binary packages for tmux, although these are sometimes
out of date. Examples are listed on
[this page](https://github.com/tmux/tmux/wiki/Installing).
### From release tarball
To build and install tmux from a release tarball, use:
@ -31,6 +37,9 @@ sudo make install
tmux can use the utempter library to update utmp(5), if it is installed - run
configure with `--enable-utempter` to enable this.
For more detailed instructions on building and installing tmux, see
[this page](https://github.com/tmux/tmux/wiki/Installing).
### From version control
To get and build the latest from version control - note that this requires
@ -72,7 +81,7 @@ And a bash(1) completion file at:
https://github.com/imomaliev/tmux-bash-completion
For debugging, run tmux with `-v` or `-vv` to generate server and client log
For debugging, run tmux with `-v` or `-vv` to generate server and client log
files in the current directory.
## Support

36
CHANGES
View File

@ -1,4 +1,33 @@
CHANGES FROM 3.1b TO 3.2
CHANGES FROM 3.2 TO 3.3
* Fire focus events even when the pane is in a mode.
* Add -O flag to display-menu to not automatically close when all mouse buttons
are released.
* Allow fnmatch(3) wildcards in update-environment.
* Disable nested job expansion so that the result of #() is not expanded again.
* Use the setal capability as well as (tmux's) Setulc.
* Add -q flag to unbind-key to hide errors.
* Allow -N without a command to change or add a note to an existing key.
* Add a -w flag to set- and load-buffer to send to clipboard using OSC 52.
* Add -F to set-environment and source-file.
* Allow colour to be spelt as color in various places.
* Add n: modifier to get length of a format.
* Respond to OSC colour requests if a colour is available.
* Add a -d option to display-message to set delay.
CHANGES FROM 3.1c TO 3.2
* Add a way for control mode clients to subscribe to a format and be notified
of changes rather than having to poll.
@ -265,6 +294,11 @@ CHANGES FROM 3.1b TO 3.2
* Add number operators for formats (+, -, *, / and m),
CHANGED FROM 3.1b TO 3.1c
* Do not write after the end of the array and overwrite the stack when
colon-separated SGR sequences contain empty arguments.
CHANGES FROM 3.1a TO 3.1b
* Fix build on systems without sys/queue.h.

View File

@ -200,7 +200,7 @@ alerts_check_bell(struct window *w)
* not check WINLINK_BELL).
*/
s = wl->session;
if (s->curw != wl) {
if (s->curw != wl || s->attached == 0) {
wl->flags |= WINLINK_BELL;
server_status_session(s);
}
@ -236,7 +236,7 @@ alerts_check_activity(struct window *w)
if (wl->flags & WINLINK_ACTIVITY)
continue;
s = wl->session;
if (s->curw != wl) {
if (s->curw != wl || s->attached == 0) {
wl->flags |= WINLINK_ACTIVITY;
server_status_session(s);
}
@ -272,7 +272,7 @@ alerts_check_silence(struct window *w)
if (wl->flags & WINLINK_SILENCE)
continue;
s = wl->session;
if (s->curw != wl) {
if (s->curw != wl || s->attached == 0) {
wl->flags |= WINLINK_SILENCE;
server_status_session(s);
}
@ -316,9 +316,9 @@ alerts_set_message(struct winlink *wl, const char *type, const char *option)
if (visual == VISUAL_OFF)
continue;
if (c->session->curw == wl)
status_message_set(c, 1, "%s in current window", type);
status_message_set(c, -1, 1, "%s in current window", type);
else {
status_message_set(c, 1, "%s in window %d", type,
status_message_set(c, -1, 1, "%s in window %d", type,
wl->idx);
}
}

View File

@ -36,6 +36,7 @@
static struct tmuxproc *client_proc;
static struct tmuxpeer *client_peer;
static uint64_t client_flags;
static int client_suspended;
static enum {
CLIENT_EXIT_NONE,
CLIENT_EXIT_DETACHED,
@ -59,7 +60,8 @@ static struct client_files client_files = RB_INITIALIZER(&client_files);
static __dead void client_exec(const char *,const char *);
static int client_get_lock(char *);
static int client_connect(struct event_base *, const char *, int);
static int client_connect(struct event_base *, const char *,
uint64_t);
static void client_send_identify(const char *, const char *, int);
static void client_signal(int);
static void client_dispatch(struct imsg *, void *);
@ -100,7 +102,7 @@ client_get_lock(char *lockfile)
/* Connect client to server. */
static int
client_connect(struct event_base *base, const char *path, int flags)
client_connect(struct event_base *base, const char *path, uint64_t flags)
{
struct sockaddr_un sa;
size_t size;
@ -220,7 +222,7 @@ static void
client_exit(void)
{
struct client_file *cf;
size_t left;
size_t left;
int waiting = 0;
RB_FOREACH (cf, client_files, &client_files) {
@ -238,7 +240,8 @@ client_exit(void)
/* Client main loop. */
int
client_main(struct event_base *base, int argc, char **argv, int flags, int feat)
client_main(struct event_base *base, int argc, char **argv, uint64_t flags,
int feat)
{
struct cmd_parse_result *pr;
struct msg_command *data;
@ -284,7 +287,7 @@ client_main(struct event_base *base, int argc, char **argv, int flags, int feat)
/* Save the flags. */
client_flags = flags;
log_debug("flags are %#llx", client_flags);
log_debug("flags are %#llx", (unsigned long long)client_flags);
/* Initialize the client socket and start the server. */
fd = client_connect(base, socket_path, client_flags);
@ -442,6 +445,8 @@ client_send_identify(const char *ttynam, const char *cwd, int feat)
pid_t pid;
proc_send(client_peer, MSG_IDENTIFY_FLAGS, -1, &flags, sizeof flags);
proc_send(client_peer, MSG_IDENTIFY_LONGFLAGS, -1, &client_flags,
sizeof client_flags);
if ((s = getenv("TERM")) == NULL)
s = "";
@ -761,6 +766,7 @@ client_signal(int sig)
struct sigaction sigact;
int status;
log_debug("%s: %s", __func__, strsignal(sig));
if (sig == SIGCHLD)
waitpid(WAIT_ANY, &status, WNOHANG);
else if (!client_attached) {
@ -774,7 +780,8 @@ client_signal(int sig)
proc_send(client_peer, MSG_EXITING, -1, NULL, 0);
break;
case SIGTERM:
client_exitreason = CLIENT_EXIT_TERMINATED;
if (!client_suspended)
client_exitreason = CLIENT_EXIT_TERMINATED;
client_exitval = 1;
proc_send(client_peer, MSG_EXITING, -1, NULL, 0);
break;
@ -789,6 +796,7 @@ client_signal(int sig)
if (sigaction(SIGTSTP, &sigact, NULL) != 0)
fatal("sigaction failed");
proc_send(client_peer, MSG_WAKEUP, -1, NULL, 0);
client_suspended = 0;
break;
}
}
@ -891,7 +899,8 @@ client_dispatch_wait(struct imsg *imsg)
fatalx("bad MSG_FLAGS string");
memcpy(&client_flags, data, sizeof client_flags);
log_debug("new flags are %#llx", client_flags);
log_debug("new flags are %#llx",
(unsigned long long)client_flags);
break;
case MSG_SHELL:
if (datalen == 0 || data[datalen - 1] != '\0')
@ -944,7 +953,8 @@ client_dispatch_attached(struct imsg *imsg)
fatalx("bad MSG_FLAGS string");
memcpy(&client_flags, data, sizeof client_flags);
log_debug("new flags are %#llx", client_flags);
log_debug("new flags are %#llx",
(unsigned long long)client_flags);
break;
case MSG_DETACH:
case MSG_DETACHKILL:
@ -999,6 +1009,7 @@ client_dispatch_attached(struct imsg *imsg)
sigact.sa_handler = SIG_DFL;
if (sigaction(SIGTSTP, &sigact, NULL) != 0)
fatal("sigaction failed");
client_suspended = 1;
kill(getpid(), SIGTSTP);
break;
case MSG_LOCK:

View File

@ -59,7 +59,7 @@ cmd_attach_session(struct cmdq_item *item, const char *tflag, int dflag,
struct session *s;
struct winlink *wl;
struct window_pane *wp;
char *cause;
char *cwd, *cause;
enum msgtype msgtype;
if (RB_EMPTY(&sessions)) {
@ -99,8 +99,9 @@ cmd_attach_session(struct cmdq_item *item, const char *tflag, int dflag,
}
if (cflag != NULL) {
cwd = format_single(item, cflag, c, s, wl, wp);
free((void *)s->cwd);
s->cwd = format_single(item, cflag, c, s, wl, wp);
s->cwd = cwd;
}
if (fflag)
server_client_set_flags(c, fflag);

View File

@ -33,9 +33,9 @@ const struct cmd_entry cmd_bind_key_entry = {
.name = "bind-key",
.alias = "bind",
.args = { "nrN:T:", 2, -1 },
.args = { "nrN:T:", 1, -1 },
.usage = "[-nr] [-T key-table] [-N note] key "
"command [arguments]",
"[command [arguments]]",
.flags = CMD_AFTERHOOK,
.exec = cmd_bind_key_exec
@ -46,7 +46,7 @@ cmd_bind_key_exec(struct cmd *self, struct cmdq_item *item)
{
struct args *args = cmd_get_args(self);
key_code key;
const char *tablename, *note;
const char *tablename, *note = args_get(args, 'N');
struct cmd_parse_result *pr;
char **argv = args->argv;
int argc = args->argc, repeat;
@ -65,22 +65,24 @@ cmd_bind_key_exec(struct cmd *self, struct cmdq_item *item)
tablename = "prefix";
repeat = args_has(args, 'r');
if (argc == 2)
pr = cmd_parse_from_string(argv[1], NULL);
else
pr = cmd_parse_from_arguments(argc - 1, argv + 1, NULL);
switch (pr->status) {
case CMD_PARSE_EMPTY:
cmdq_error(item, "empty command");
return (CMD_RETURN_ERROR);
case CMD_PARSE_ERROR:
cmdq_error(item, "%s", pr->error);
free(pr->error);
return (CMD_RETURN_ERROR);
case CMD_PARSE_SUCCESS:
break;
}
note = args_get(args, 'N');
key_bindings_add(tablename, key, note, repeat, pr->cmdlist);
if (argc != 1) {
if (argc == 2)
pr = cmd_parse_from_string(argv[1], NULL);
else
pr = cmd_parse_from_arguments(argc - 1, argv + 1, NULL);
switch (pr->status) {
case CMD_PARSE_EMPTY:
cmdq_error(item, "empty command");
return (CMD_RETURN_ERROR);
case CMD_PARSE_ERROR:
cmdq_error(item, "%s", pr->error);
free(pr->error);
return (CMD_RETURN_ERROR);
case CMD_PARSE_SUCCESS:
break;
}
key_bindings_add(tablename, key, note, repeat, pr->cmdlist);
} else
key_bindings_add(tablename, key, note, repeat, NULL);
return (CMD_RETURN_NORMAL);
}

View File

@ -36,8 +36,8 @@ const struct cmd_entry cmd_display_menu_entry = {
.name = "display-menu",
.alias = "menu",
.args = { "c:t:T:x:y:", 1, -1 },
.usage = "[-c target-client] " CMD_TARGET_PANE_USAGE " [-T title] "
.args = { "c:t:OT:x:y:", 1, -1 },
.usage = "[-O] [-c target-client] " CMD_TARGET_PANE_USAGE " [-T title] "
"[-x position] [-y position] name key command ...",
.target = { 't', CMD_FIND_PANE, 0 },
@ -229,6 +229,8 @@ cmd_display_menu_exec(struct cmd *self, struct cmdq_item *item)
cmd_display_menu_get_position(tc, item, args, &px, &py, menu->width + 4,
menu->count + 2);
if (args_has(args, 'O'))
flags |= MENU_STAYOPEN;
if (!event->m.valid)
flags |= MENU_NOMOUSE;
if (menu_display(menu, flags, item, px, py, tc, target, NULL,

View File

@ -39,8 +39,8 @@ const struct cmd_entry cmd_display_message_entry = {
.name = "display-message",
.alias = "display",
.args = { "ac:Ipt:F:v", 0, 1 },
.usage = "[-aIpv] [-c target-client] [-F format] "
.args = { "acd:Ipt:F:v", 0, 1 },
.usage = "[-aIpv] [-c target-client] [-d delay] [-F format] "
CMD_TARGET_PANE_USAGE " [message]",
.target = { 't', CMD_FIND_PANE, 0 },
@ -68,6 +68,7 @@ cmd_display_message_exec(struct cmd *self, struct cmdq_item *item)
struct window_pane *wp = target->wp;
const char *template;
char *msg, *cause;
int delay = -1;
struct format_tree *ft;
int flags;
@ -85,6 +86,15 @@ cmd_display_message_exec(struct cmd *self, struct cmdq_item *item)
return (CMD_RETURN_ERROR);
}
if (args_has(args, 'd')) {
delay = args_strtonum(args, 'd', 0, UINT_MAX, &cause);
if (cause != NULL) {
cmdq_error(item, "delay %s", cause);
free(cause);
return (CMD_RETURN_ERROR);
}
}
template = args_get(args, 'F');
if (args->argc != 0)
template = args->argv[0];
@ -117,7 +127,7 @@ cmd_display_message_exec(struct cmd *self, struct cmdq_item *item)
if (args_has(args, 'p'))
cmdq_print(item, "%s", msg);
else if (tc != NULL)
status_message_set(tc, 0, "%s", msg);
status_message_set(tc, delay, 0, "%s", msg);
free(msg);
format_free(ft);

View File

@ -55,11 +55,11 @@ cmd_display_panes_draw_pane(struct screen_redraw_ctx *ctx,
struct session *s = c->session;
struct options *oo = s->options;
struct window *w = wp->window;
struct grid_cell gc;
u_int idx, px, py, i, j, xoff, yoff, sx, sy;
struct grid_cell fgc, bgc;
u_int pane, idx, px, py, i, j, xoff, yoff, sx, sy;
int colour, active_colour;
char buf[16], *ptr;
size_t len;
char buf[16], lbuf[16], rbuf[16], *ptr;
size_t len, llen, rlen;
if (wp->xoff + wp->sx <= ctx->ox ||
wp->xoff >= ctx->ox + ctx->sx ||
@ -109,29 +109,50 @@ cmd_display_panes_draw_pane(struct screen_redraw_ctx *ctx,
px = sx / 2;
py = sy / 2;
if (window_pane_index(wp, &idx) != 0)
if (window_pane_index(wp, &pane) != 0)
fatalx("index not found");
len = xsnprintf(buf, sizeof buf, "%u", idx);
len = xsnprintf(buf, sizeof buf, "%u", pane);
if (sx < len)
return;
colour = options_get_number(oo, "display-panes-colour");
active_colour = options_get_number(oo, "display-panes-active-colour");
memcpy(&fgc, &grid_default_cell, sizeof fgc);
memcpy(&bgc, &grid_default_cell, sizeof bgc);
if (w->active == wp) {
fgc.fg = active_colour;
bgc.bg = active_colour;
} else {
fgc.fg = colour;
bgc.bg = colour;
}
rlen = xsnprintf(rbuf, sizeof rbuf, "%ux%u", wp->sx, wp->sy);
if (pane > 9 && pane < 35)
llen = xsnprintf(lbuf, sizeof lbuf, "%c", 'a' + (pane - 10));
else
llen = 0;
if (sx < len * 6 || sy < 5) {
tty_cursor(tty, xoff + px - len / 2, yoff + py);
goto draw_text;
tty_attributes(tty, &fgc, &grid_default_cell, NULL);
if (sx >= len + llen + 1) {
len += llen + 1;
tty_cursor(tty, xoff + px - len / 2, yoff + py);
tty_putn(tty, buf, len, len);
tty_putn(tty, " ", 1, 1);
tty_putn(tty, lbuf, llen, llen);
} else {
tty_cursor(tty, xoff + px - len / 2, yoff + py);
tty_putn(tty, buf, len, len);
}
goto out;
}
px -= len * 3;
py -= 2;
memcpy(&gc, &grid_default_cell, sizeof gc);
if (w->active == wp)
gc.bg = active_colour;
else
gc.bg = colour;
tty_attributes(tty, &gc, &grid_default_cell, NULL);
tty_attributes(tty, &bgc, &grid_default_cell, NULL);
for (ptr = buf; *ptr != '\0'; ptr++) {
if (*ptr < '0' || *ptr > '9')
continue;
@ -147,20 +168,20 @@ cmd_display_panes_draw_pane(struct screen_redraw_ctx *ctx,
px += 6;
}
len = xsnprintf(buf, sizeof buf, "%ux%u", wp->sx, wp->sy);
if (sx < len || sy < 6)
return;
tty_cursor(tty, xoff + sx - len, yoff);
draw_text:
memcpy(&gc, &grid_default_cell, sizeof gc);
if (w->active == wp)
gc.fg = active_colour;
else
gc.fg = colour;
tty_attributes(tty, &gc, &grid_default_cell, NULL);
tty_puts(tty, buf);
if (sy <= 6)
goto out;
tty_attributes(tty, &fgc, &grid_default_cell, NULL);
if (rlen != 0 && sx >= rlen) {
tty_cursor(tty, xoff + sx - rlen, yoff);
tty_putn(tty, rbuf, rlen, rlen);
}
if (llen != 0) {
tty_cursor(tty, xoff + sx / 2 + len * 3 - llen - 1,
yoff + py + 5);
tty_putn(tty, lbuf, llen, llen);
}
out:
tty_cursor(tty, 0, 0);
}
@ -197,11 +218,21 @@ cmd_display_panes_key(struct client *c, struct key_event *event)
struct window *w = c->session->curw->window;
struct window_pane *wp;
enum cmd_parse_status status;
u_int index;
key_code key;
if (event->key < '0' || event->key > '9')
if (event->key >= '0' && event->key <= '9')
index = event->key - '0';
else if ((event->key & KEYC_MASK_MODIFIERS) == 0) {
key = (event->key & KEYC_MASK_KEY);
if (key >= 'a' && key <= 'z')
index = 10 + (key - 'a');
else
return (-1);
} else
return (-1);
wp = window_pane_at_index(w, event->key - '0');
wp = window_pane_at_index(w, index);
if (wp == NULL)
return (1);
window_unzoom(w);

View File

@ -114,7 +114,7 @@ cmd_list_keys_print_notes(struct cmdq_item *item, struct args *args,
note = xstrdup(bd->note);
tmp = utf8_padcstr(key, keywidth + 1);
if (args_has(args, '1') && tc != NULL)
status_message_set(tc, 1, "%s%s%s", prefix, tmp, note);
status_message_set(tc, -1, 1, "%s%s%s", prefix, tmp, note);
else
cmdq_print(item, "%s%s%s", prefix, tmp, note);
free(tmp);

View File

@ -37,14 +37,15 @@ const struct cmd_entry cmd_load_buffer_entry = {
.name = "load-buffer",
.alias = "loadb",
.args = { "b:", 1, 1 },
.usage = CMD_BUFFER_USAGE " path",
.args = { "b:t:w", 1, 1 },
.usage = CMD_BUFFER_USAGE " " CMD_TARGET_CLIENT_USAGE " path",
.flags = CMD_AFTERHOOK,
.flags = CMD_AFTERHOOK|CMD_CLIENT_TFLAG|CMD_CLIENT_CANFAIL,
.exec = cmd_load_buffer_exec
};
struct cmd_load_buffer_data {
struct client *client;
struct cmdq_item *item;
char *name;
};
@ -54,6 +55,7 @@ cmd_load_buffer_done(__unused struct client *c, const char *path, int error,
int closed, struct evbuffer *buffer, void *data)
{
struct cmd_load_buffer_data *cdata = data;
struct client *tc = cdata->client;
struct cmdq_item *item = cdata->item;
void *bdata = EVBUFFER_DATA(buffer);
size_t bsize = EVBUFFER_LENGTH(buffer);
@ -72,7 +74,12 @@ cmd_load_buffer_done(__unused struct client *c, const char *path, int error,
cmdq_error(item, "%s", cause);
free(cause);
free(copy);
}
} else if (tc != NULL &&
tc->session != NULL &&
(~tc->flags & CLIENT_DEAD))
tty_set_selection(&tc->tty, copy, bsize);
if (tc != NULL)
server_client_unref(tc);
}
cmdq_continue(item);
@ -84,16 +91,19 @@ static enum cmd_retval
cmd_load_buffer_exec(struct cmd *self, struct cmdq_item *item)
{
struct args *args = cmd_get_args(self);
struct client *tc = cmdq_get_target_client(item);
struct cmd_load_buffer_data *cdata;
const char *bufname = args_get(args, 'b');
char *path;
cdata = xmalloc(sizeof *cdata);
cdata = xcalloc(1, sizeof *cdata);
cdata->item = item;
if (bufname != NULL)
cdata->name = xstrdup(bufname);
else
cdata->name = NULL;
if (args_has(args, 'w') && tc != NULL) {
cdata->client = tc;
cdata->client->references++;
}
path = format_single_from_target(item, args->argv[0]);
file_read(cmdq_get_client(item), path, cmd_load_buffer_done, cdata);

View File

@ -858,7 +858,7 @@ cmdq_error(struct cmdq_item *item, const char *fmt, ...)
c->retval = 1;
} else {
*msg = toupper((u_char) *msg);
status_message_set(c, 1, "%s", msg);
status_message_set(c, -1, 1, "%s", msg);
}
free(msg);

View File

@ -198,6 +198,7 @@ cmd_select_pane_exec(struct cmd *self, struct cmdq_item *item)
if (args_has(args, 'T')) {
title = format_single_from_target(item, args_get(args, 'T'));
if (screen_set_title(&wp->base, title)) {
notify_pane("pane-title-changed", wp);
server_redraw_window_borders(wp->window);
server_status_window(wp->window);
}

View File

@ -33,10 +33,11 @@ const struct cmd_entry cmd_set_buffer_entry = {
.name = "set-buffer",
.alias = "setb",
.args = { "ab:n:", 0, 1 },
.usage = "[-a] " CMD_BUFFER_USAGE " [-n new-buffer-name] data",
.args = { "ab:t:n:w", 0, 1 },
.usage = "[-aw] " CMD_BUFFER_USAGE " [-n new-buffer-name] "
CMD_TARGET_CLIENT_USAGE " data",
.flags = CMD_AFTERHOOK,
.flags = CMD_AFTERHOOK|CMD_CLIENT_TFLAG|CMD_CLIENT_CANFAIL,
.exec = cmd_set_buffer_exec
};
@ -55,6 +56,7 @@ static enum cmd_retval
cmd_set_buffer_exec(struct cmd *self, struct cmdq_item *item)
{
struct args *args = cmd_get_args(self);
struct client *tc = cmdq_get_target_client(item);
struct paste_buffer *pb;
char *bufdata, *cause;
const char *bufname, *olddata;
@ -118,6 +120,8 @@ cmd_set_buffer_exec(struct cmd *self, struct cmdq_item *item)
free(cause);
return (CMD_RETURN_ERROR);
}
if (args_has(args, 'w') && tc != NULL)
tty_set_selection(&tc->tty, bufdata, bufsize);
return (CMD_RETURN_NORMAL);
}

View File

@ -34,8 +34,8 @@ const struct cmd_entry cmd_set_environment_entry = {
.name = "set-environment",
.alias = "setenv",
.args = { "hgrt:u", 1, 2 },
.usage = "[-hgru] " CMD_TARGET_SESSION_USAGE " name [value]",
.args = { "Fhgrt:u", 1, 2 },
.usage = "[-Fhgru] " CMD_TARGET_SESSION_USAGE " name [value]",
.target = { 't', CMD_FIND_SESSION, CMD_FIND_CANFAIL },
@ -50,6 +50,8 @@ cmd_set_environment_exec(struct cmd *self, struct cmdq_item *item)
struct cmd_find_state *target = cmdq_get_target(item);
struct environ *env;
const char *name, *value, *tflag;
char *expand = NULL;
enum cmd_retval retval = CMD_RETURN_NORMAL;
name = args->argv[0];
if (*name == '\0') {
@ -63,6 +65,8 @@ cmd_set_environment_exec(struct cmd *self, struct cmdq_item *item)
if (args->argc < 2)
value = NULL;
else if (args_has(args, 'F'))
value = expand = format_single_from_target(item, args->argv[1]);
else
value = args->argv[1];
@ -75,7 +79,8 @@ cmd_set_environment_exec(struct cmd *self, struct cmdq_item *item)
cmdq_error(item, "no such session: %s", tflag);
else
cmdq_error(item, "no current session");
return (CMD_RETURN_ERROR);
retval = CMD_RETURN_ERROR;
goto out;
}
env = target->s->environ;
}
@ -83,25 +88,31 @@ cmd_set_environment_exec(struct cmd *self, struct cmdq_item *item)
if (args_has(args, 'u')) {
if (value != NULL) {
cmdq_error(item, "can't specify a value with -u");
return (CMD_RETURN_ERROR);
retval = CMD_RETURN_ERROR;
goto out;
}
environ_unset(env, name);
} else if (args_has(args, 'r')) {
if (value != NULL) {
cmdq_error(item, "can't specify a value with -r");
return (CMD_RETURN_ERROR);
retval = CMD_RETURN_ERROR;
goto out;
}
environ_clear(env, name);
} else {
if (value == NULL) {
cmdq_error(item, "no value specified");
return (CMD_RETURN_ERROR);
retval = CMD_RETURN_ERROR;
goto out;
}
if (args_has(args, 'h'))
environ_set(env, name, ENVIRON_HIDDEN, "%s", value);
else
environ_set(env, name, 0, "%s", value);
}
return (CMD_RETURN_NORMAL);
out:
free(expand);
return (retval);
}

View File

@ -35,8 +35,8 @@ const struct cmd_entry cmd_source_file_entry = {
.name = "source-file",
.alias = "source",
.args = { "nqv", 1, -1 },
.usage = "[-nqv] path ...",
.args = { "Fnqv", 1, -1 },
.usage = "[-Fnqv] path ...",
.flags = 0,
.exec = cmd_source_file_exec
@ -126,7 +126,7 @@ cmd_source_file_exec(struct cmd *self, struct cmdq_item *item)
struct cmd_source_file_data *cdata;
struct client *c = cmdq_get_client(item);
enum cmd_retval retval = CMD_RETURN_NORMAL;
char *pattern, *cwd;
char *pattern, *cwd, *expand = NULL;
const char *path, *error;
glob_t g;
int i, result;
@ -145,7 +145,12 @@ cmd_source_file_exec(struct cmd *self, struct cmdq_item *item)
utf8_stravis(&cwd, server_client_get_cwd(c, NULL), VIS_GLOB);
for (i = 0; i < args->argc; i++) {
path = args->argv[i];
if (args_has(args, 'F')) {
free(expand);
expand = format_single_from_target(item, args->argv[i]);
path = expand;
} else
path = args->argv[i];
if (strcmp(path, "-") == 0) {
cmd_source_file_add(cdata, "-");
continue;
@ -172,6 +177,7 @@ cmd_source_file_exec(struct cmd *self, struct cmdq_item *item)
free(pattern);
continue;
}
free(expand);
free(pattern);
for (j = 0; j < g.gl_pathc; j++)

View File

@ -32,8 +32,8 @@ const struct cmd_entry cmd_unbind_key_entry = {
.name = "unbind-key",
.alias = "unbind",
.args = { "anT:", 0, 1 },
.usage = "[-an] [-T key-table] key",
.args = { "anqT:", 0, 1 },
.usage = "[-anq] [-T key-table] key",
.flags = CMD_AFTERHOOK,
.exec = cmd_unbind_key_exec
@ -45,44 +45,54 @@ cmd_unbind_key_exec(struct cmd *self, struct cmdq_item *item)
struct args *args = cmd_get_args(self);
key_code key;
const char *tablename;
int quiet = args_has(args, 'q');
if (!args_has(args, 'a')) {
if (args->argc != 1) {
cmdq_error(item, "missing key");
return (CMD_RETURN_ERROR);
}
key = key_string_lookup_string(args->argv[0]);
if (key == KEYC_NONE || key == KEYC_UNKNOWN) {
cmdq_error(item, "unknown key: %s", args->argv[0]);
return (CMD_RETURN_ERROR);
}
} else {
if (args_has(args, 'a')) {
if (args->argc != 0) {
cmdq_error(item, "key given with -a");
if (!quiet)
cmdq_error(item, "key given with -a");
return (CMD_RETURN_ERROR);
}
key = KEYC_UNKNOWN;
}
if (key == KEYC_UNKNOWN) {
tablename = args_get(args, 'T');
if (tablename == NULL) {
key_bindings_remove_table("root");
key_bindings_remove_table("prefix");
return (CMD_RETURN_NORMAL);
if (args_has(args, 'n'))
tablename = "root";
else
tablename = "prefix";
}
if (key_bindings_get_table(tablename, 0) == NULL) {
cmdq_error(item, "table %s doesn't exist", tablename);
if (!quiet) {
cmdq_error(item, "table %s doesn't exist" ,
tablename);
}
return (CMD_RETURN_ERROR);
}
key_bindings_remove_table(tablename);
return (CMD_RETURN_NORMAL);
}
if (args->argc != 1) {
if (!quiet)
cmdq_error(item, "missing key");
return (CMD_RETURN_ERROR);
}
key = key_string_lookup_string(args->argv[0]);
if (key == KEYC_NONE || key == KEYC_UNKNOWN) {
if (!quiet)
cmdq_error(item, "unknown key: %s", args->argv[0]);
return (CMD_RETURN_ERROR);
}
if (args_has(args, 'T')) {
tablename = args_get(args, 'T');
if (key_bindings_get_table(tablename, 0) == NULL) {
cmdq_error(item, "table %s doesn't exist", tablename);
if (!quiet) {
cmdq_error(item, "table %s doesn't exist" ,
tablename);
}
return (CMD_RETURN_ERROR);
}
} else if (args_has(args, 'n'))

View File

@ -189,6 +189,12 @@ colour_fromstring(const char *s)
return (-1);
return (n | COLOUR_FLAG_256);
}
if (strncasecmp(s, "color", (sizeof "color") - 1) == 0) {
n = strtonum(s + (sizeof "color") - 1, 0, 255, &errstr);
if (errstr != NULL)
return (-1);
return (n | COLOUR_FLAG_256);
}
if (strcasecmp(s, "default") == 0)
return (8);

View File

@ -27,6 +27,10 @@
#include <termios.h>
#include <wchar.h>
#ifdef HAVE_MALLOC_TRIM
#include <malloc.h>
#endif
#ifdef HAVE_UTF8PROC
#include <utf8proc.h>
#endif
@ -35,6 +39,10 @@
#define __attribute__(a)
#endif
#ifdef BROKEN___DEAD
#undef __dead
#endif
#ifndef __unused
#define __unused __attribute__ ((__unused__))
#endif

29
compat/getdtablesize.c Normal file
View File

@ -0,0 +1,29 @@
/*
* Copyright (c) 2020 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 <unistd.h>
#include "compat.h"
#ifdef HAVE_SYSCONF
int
getdtablesize(void)
{
return (sysconf(_SC_OPEN_MAX));
}
#endif

View File

@ -1,4 +1,4 @@
/* $OpenBSD: imsg-buffer.c,v 1.11 2017/12/14 09:27:44 kettenis Exp $ */
/* $OpenBSD: imsg-buffer.c,v 1.12 2019/01/20 02:50:03 bcook Exp $ */
/*
* Copyright (c) 2003, 2004 Henning Brauer <henning@openbsd.org>
@ -70,7 +70,7 @@ ibuf_dynamic(size_t len, size_t max)
static int
ibuf_realloc(struct ibuf *buf, size_t len)
{
u_char *b;
unsigned char *b;
/* on static buffers max is eq size and so the following fails */
if (buf->wpos + len > buf->max) {

View File

@ -1,4 +1,4 @@
/* $OpenBSD: imsg.h,v 1.4 2017/03/24 09:34:12 nicm Exp $ */
/* $OpenBSD: imsg.h,v 1.5 2019/01/20 02:50:03 bcook Exp $ */
/*
* Copyright (c) 2006, 2007 Pierre-Yves Ritschard <pyr@openbsd.org>
@ -21,13 +21,15 @@
#ifndef _IMSG_H_
#define _IMSG_H_
#include <stdint.h>
#define IBUF_READ_SIZE 65535
#define IMSG_HEADER_SIZE sizeof(struct imsg_hdr)
#define MAX_IMSGSIZE 16384
struct ibuf {
TAILQ_ENTRY(ibuf) entry;
u_char *buf;
unsigned char *buf;
size_t size;
size_t max;
size_t wpos;
@ -42,8 +44,8 @@ struct msgbuf {
};
struct ibuf_read {
u_char buf[IBUF_READ_SIZE];
u_char *rptr;
unsigned char buf[IBUF_READ_SIZE];
unsigned char *rptr;
size_t wpos;
};

View File

@ -1,6 +1,6 @@
# configure.ac
AC_INIT([tmux], 3.2-rc2)
AC_INIT([tmux], next-3.3)
AC_PREREQ([2.60])
AC_CONFIG_AUX_DIR(etc)
@ -113,6 +113,7 @@ AC_REPLACE_FUNCS([ \
fgetln \
freezero \
getdtablecount \
getdtablesize \
getline \
getprogname \
memmem \
@ -328,6 +329,31 @@ AC_SEARCH_LIBS(inet_ntoa, nsl)
AC_SEARCH_LIBS(socket, socket)
AC_CHECK_LIB(xnet, socket)
# Check if using glibc and have malloc_trim(3). The glibc free(3) is pretty bad
# about returning memory to the kernel unless the application tells it when to
# with malloc_trim(3).
AC_MSG_CHECKING(if free doesn't work very well)
AC_LINK_IFELSE([AC_LANG_SOURCE(
[
#include <stdlib.h>
#ifdef __GLIBC__
#include <malloc.h>
int main(void) {
malloc_trim (0);
exit(0);
}
#else
no
#endif
])],
found_malloc_trim=yes,
found_malloc_trim=no
)
AC_MSG_RESULT($found_malloc_trim)
if test "x$found_malloc_trim" = xyes; then
AC_DEFINE(HAVE_MALLOC_TRIM)
fi
# Check for CMSG_DATA. On some platforms like HP-UX this requires UNIX 95
# (_XOPEN_SOURCE and _XOPEN_SOURCE_EXTENDED) (see xopen_networking(7)). On
# others, UNIX 03 (_XOPEN_SOURCE 600, see standards(7) on Solaris).
@ -570,6 +596,12 @@ case "$host_os" in
AC_MSG_RESULT(darwin)
PLATFORM=darwin
#
# OS X uses __dead2 instead of __dead, like FreeBSD. But it
# defines __dead away so it needs to be removed before we can
# replace it.
#
AC_DEFINE(BROKEN___DEAD)
#
# OS X CMSG_FIRSTHDR is broken, so redefine it with a working
# one. daemon works but has some stupid side effects, so use
# our internal version which has a workaround.

View File

@ -688,8 +688,8 @@ control_write_pending(struct client *c, struct control_pane *cp, size_t limit)
else
age = 0;
log_debug("%s: %s: output block %zu (age %llu) for %%%u "
"(used %zu/%zu)", __func__, c->name, cb->size, age,
cp->pane, used, limit);
"(used %zu/%zu)", __func__, c->name, cb->size,
(unsigned long long)age, cp->pane, used, limit);
size = cb->size;
if (size > limit - used)

View File

@ -18,6 +18,7 @@
#include <sys/types.h>
#include <fnmatch.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
@ -191,7 +192,11 @@ environ_update(struct options *oo, struct environ *src, struct environ *dst)
a = options_array_first(o);
while (a != NULL) {
ov = options_array_item_value(a);
if ((envent = environ_find(src, ov->string)) == NULL)
RB_FOREACH(envent, environ, src) {
if (fnmatch(ov->string, envent->name, 0) == 0)
break;
}
if (envent == NULL)
environ_clear(dst, ov->string);
else
environ_set(dst, envent->name, 0, "%s", envent->value);

469
format.c
View File

@ -38,15 +38,18 @@
* string.
*/
static char *format_job_get(struct format_tree *, const char *);
static void format_job_timer(int, short, void *);
struct format_expand_state;
static int format_replace(struct format_tree *, const char *, size_t,
char **, size_t *, size_t *);
static char *format_job_get(struct format_expand_state *, const char *);
static void format_job_timer(int, short, void *);
static char *format_expand1(struct format_expand_state *, const char *);
static int format_replace(struct format_expand_state *, const char *,
size_t, char **, size_t *, size_t *);
static void format_defaults_session(struct format_tree *,
struct session *);
static void format_defaults_client(struct format_tree *, struct client *);
static void format_defaults_winlink(struct format_tree *, struct winlink *);
static void format_defaults_winlink(struct format_tree *,
struct winlink *);
/* Entry in format job tree. */
struct format_job {
@ -94,15 +97,20 @@ format_job_cmp(struct format_job *fj1, struct format_job *fj2)
#define FORMAT_WINDOWS 0x100
#define FORMAT_PANES 0x200
#define FORMAT_PRETTY 0x400
#define FORMAT_LENGTH 0x800
/* Limit on recursion. */
#define FORMAT_LOOP_LIMIT 10
/* Format expand flags. */
#define FORMAT_EXPAND_TIME 0x1
#define FORMAT_EXPAND_NOJOBS 0x2
/* Entry in format tree. */
struct format_entry {
char *key;
char *value;
time_t t;
time_t time;
format_cb cb;
RB_ENTRY(format_entry) entry;
};
@ -119,8 +127,6 @@ struct format_tree {
struct client *client;
int flags;
u_int tag;
time_t time;
u_int loop;
struct mouse_event m;
@ -129,6 +135,14 @@ struct format_tree {
static int format_entry_cmp(struct format_entry *, struct format_entry *);
RB_GENERATE_STATIC(format_entry_tree, format_entry, entry, format_entry_cmp);
/* Format expand state. */
struct format_expand_state {
struct format_tree *ft;
u_int loop;
time_t time;
int flags;
};
/* Format modifier. */
struct format_modifier {
char modifier[3];
@ -214,8 +228,10 @@ format_logging(struct format_tree *ft)
/* Log a message if verbose. */
static void printflike(3, 4)
format_log1(struct format_tree *ft, const char *from, const char *fmt, ...)
format_log1(struct format_expand_state *es, const char *from, const char *fmt,
...)
{
struct format_tree *ft = es->ft;
va_list ap;
char *s;
static const char spaces[] = " ";
@ -229,11 +245,22 @@ format_log1(struct format_tree *ft, const char *from, const char *fmt, ...)
log_debug("%s: %s", from, s);
if (ft->item != NULL && (ft->flags & FORMAT_VERBOSE))
cmdq_print(ft->item, "#%.*s%s", ft->loop, spaces, s);
cmdq_print(ft->item, "#%.*s%s", es->loop, spaces, s);
free(s);
}
#define format_log(ft, fmt, ...) format_log1(ft, __func__, fmt, ##__VA_ARGS__)
#define format_log(es, fmt, ...) format_log1(es, __func__, fmt, ##__VA_ARGS__)
/* Copy expand state. */
static void
format_copy_state(struct format_expand_state *to,
struct format_expand_state *from, int flags)
{
to->ft = from->ft;
to->loop = from->loop;
to->time = from->time;
to->flags = from->flags|flags;
}
/* Format job update callback. */
static void
@ -303,13 +330,15 @@ format_job_complete(struct job *job)
/* Find a job. */
static char *
format_job_get(struct format_tree *ft, const char *cmd)
format_job_get(struct format_expand_state *es, const char *cmd)
{
struct format_job_tree *jobs;
struct format_job fj0, *fj;
time_t t;
char *expanded;
int force;
struct format_tree *ft = es->ft;
struct format_job_tree *jobs;
struct format_job fj0, *fj;
time_t t;
char *expanded;
int force;
struct format_expand_state next;
if (ft->client == NULL)
jobs = &format_jobs;
@ -334,7 +363,7 @@ format_job_get(struct format_tree *ft, const char *cmd)
RB_INSERT(format_job_tree, jobs, fj);
}
expanded = format_expand(ft, cmd);
expanded = format_expand1(es, cmd);
if (fj->expanded == NULL || strcmp(expanded, fj->expanded) != 0) {
free((void *)fj->expanded);
fj->expanded = xstrdup(expanded);
@ -356,12 +385,12 @@ format_job_get(struct format_tree *ft, const char *cmd)
fj->last = t;
fj->updated = 0;
}
free(expanded);
if (ft->flags & FORMAT_STATUS)
fj->status = 1;
free(expanded);
return (format_expand(ft, fj->out));
format_copy_state(&next, es, FORMAT_EXPAND_NOJOBS);
return (format_expand1(&next, fj->out));
}
/* Remove old jobs. */
@ -1210,7 +1239,6 @@ format_create(struct client *c, struct cmdq_item *item, int tag, int flags)
ft->tag = tag;
ft->flags = flags;
ft->time = time(NULL);
format_add(ft, "version", "%s", getversion());
format_add_cb(ft, "host", format_cb_host);
@ -1260,8 +1288,8 @@ format_each(struct format_tree *ft, void (*cb)(const char *, const char *,
char s[64];
RB_FOREACH(fe, format_entry_tree, &ft->tree) {
if (fe->t != 0) {
xsnprintf(s, sizeof s, "%lld", (long long)fe->t);
if (fe->time != 0) {
xsnprintf(s, sizeof s, "%lld", (long long)fe->time);
cb(fe->key, s, arg);
} else {
if (fe->value == NULL && fe->cb != NULL) {
@ -1294,7 +1322,7 @@ format_add(struct format_tree *ft, const char *key, const char *fmt, ...)
}
fe->cb = NULL;
fe->t = 0;
fe->time = 0;
va_start(ap, fmt);
xvasprintf(&fe->value, fmt, ap);
@ -1319,7 +1347,7 @@ format_add_tv(struct format_tree *ft, const char *key, struct timeval *tv)
}
fe->cb = NULL;
fe->t = tv->tv_sec;
fe->time = tv->tv_sec;
fe->value = NULL;
}
@ -1343,7 +1371,7 @@ format_add_cb(struct format_tree *ft, const char *key, format_cb cb)
}
fe->cb = cb;
fe->t = 0;
fe->time = 0;
fe->value = NULL;
}
@ -1439,8 +1467,8 @@ format_find(struct format_tree *ft, const char *key, int modifiers,
fe_find.key = (char *)key;
fe = RB_FIND(format_entry_tree, &ft->tree, &fe_find);
if (fe != NULL) {
if (fe->t != 0) {
t = fe->t;
if (fe->time != 0) {
t = fe->time;
goto found;
}
if (fe->value == NULL && fe->cb != NULL) {
@ -1562,8 +1590,8 @@ format_skip(const char *s, const char *end)
/* Return left and right alternatives separated by commas. */
static int
format_choose(struct format_tree *ft, const char *s, char **left, char **right,
int expand)
format_choose(struct format_expand_state *es, const char *s, char **left,
char **right, int expand)
{
const char *cp;
char *left0, *right0;
@ -1575,9 +1603,9 @@ format_choose(struct format_tree *ft, const char *s, char **left, char **right,
right0 = xstrdup(cp + 1);
if (expand) {
*left = format_expand(ft, left0);
*left = format_expand1(es, left0);
free(left0);
*right = format_expand(ft, right0);
*right = format_expand1(es, right0);
free(right0);
} else {
*left = left0;
@ -1633,7 +1661,8 @@ format_free_modifiers(struct format_modifier *list, u_int count)
/* Build modifier list. */
static struct format_modifier *
format_build_modifiers(struct format_tree *ft, const char **s, u_int *count)
format_build_modifiers(struct format_expand_state *es, const char **s,
u_int *count)
{
const char *cp = *s, *end;
struct format_modifier *list = NULL;
@ -1642,7 +1671,7 @@ format_build_modifiers(struct format_tree *ft, const char **s, u_int *count)
/*
* Modifiers are a ; separated list of the forms:
* l,m,C,b,d,t,q,E,T,S,W,P,<,>
* l,m,C,b,d,n,t,q,E,T,S,W,P,<,>
* =a
* =/a
* =/a/
@ -1659,7 +1688,7 @@ format_build_modifiers(struct format_tree *ft, const char **s, u_int *count)
cp++;
/* Check single character modifiers with no arguments. */
if (strchr("lbdqETSWP<>", cp[0]) != NULL &&
if (strchr("lbdnqETSWP<>", cp[0]) != NULL &&
format_is_end(cp[1])) {
format_add_modifier(&list, count, cp, 1, NULL, 0);
cp++;
@ -1701,7 +1730,7 @@ format_build_modifiers(struct format_tree *ft, const char **s, u_int *count)
argv = xcalloc(1, sizeof *argv);
value = xstrndup(cp + 1, end - (cp + 1));
argv[0] = format_expand(ft, value);
argv[0] = format_expand1(es, value);
free(value);
argc = 1;
@ -1725,7 +1754,7 @@ format_build_modifiers(struct format_tree *ft, const char **s, u_int *count)
argv = xreallocarray (argv, argc + 1, sizeof *argv);
value = xstrndup(cp, end - cp);
argv[argc++] = format_expand(ft, value);
argv[argc++] = format_expand1(es, value);
free(value);
cp = end;
@ -1806,25 +1835,28 @@ format_search(struct format_modifier *fm, struct window_pane *wp, const char *s)
/* Loop over sessions. */
static char *
format_loop_sessions(struct format_tree *ft, const char *fmt)
format_loop_sessions(struct format_expand_state *es, const char *fmt)
{
struct client *c = ft->client;
struct cmdq_item *item = ft->item;
struct format_tree *nft;
char *expanded, *value;
size_t valuelen;
struct session *s;
struct format_tree *ft = es->ft;
struct client *c = ft->client;
struct cmdq_item *item = ft->item;
struct format_tree *nft;
struct format_expand_state next;
char *expanded, *value;
size_t valuelen;
struct session *s;
value = xcalloc(1, 1);
valuelen = 1;
RB_FOREACH(s, sessions, &sessions) {
format_log(ft, "session loop: $%u", s->id);
format_log(es, "session loop: $%u", s->id);
nft = format_create(c, item, FORMAT_NONE, ft->flags);
nft->loop = ft->loop;
format_defaults(nft, ft->c, s, NULL, NULL);
expanded = format_expand(nft, fmt);
format_free(nft);
format_defaults(next.ft, ft->c, s, NULL, NULL);
format_copy_state(&next, es, 0);
next.ft = nft;
expanded = format_expand1(&next, fmt);
format_free(next.ft);
valuelen += strlen(expanded);
value = xrealloc(value, valuelen);
@ -1838,22 +1870,24 @@ format_loop_sessions(struct format_tree *ft, const char *fmt)
/* Loop over windows. */
static char *
format_loop_windows(struct format_tree *ft, const char *fmt)
format_loop_windows(struct format_expand_state *es, const char *fmt)
{
struct client *c = ft->client;
struct cmdq_item *item = ft->item;
struct format_tree *nft;
char *all, *active, *use, *expanded, *value;
size_t valuelen;
struct winlink *wl;
struct window *w;
struct format_tree *ft = es->ft;
struct client *c = ft->client;
struct cmdq_item *item = ft->item;
struct format_tree *nft;
struct format_expand_state next;
char *all, *active, *use, *expanded, *value;
size_t valuelen;
struct winlink *wl;
struct window *w;
if (ft->s == NULL) {
format_log(ft, "window loop but no session");
format_log(es, "window loop but no session");
return (NULL);
}
if (format_choose(ft, fmt, &all, &active, 0) != 0) {
if (format_choose(es, fmt, &all, &active, 0) != 0) {
all = xstrdup(fmt);
active = NULL;
}
@ -1863,15 +1897,16 @@ format_loop_windows(struct format_tree *ft, const char *fmt)
RB_FOREACH(wl, winlinks, &ft->s->windows) {
w = wl->window;
format_log(ft, "window loop: %u @%u", wl->idx, w->id);
format_log(es, "window loop: %u @%u", wl->idx, w->id);
if (active != NULL && wl == ft->s->curw)
use = active;
else
use = all;
nft = format_create(c, item, FORMAT_WINDOW|w->id, ft->flags);
nft->loop = ft->loop;
format_defaults(nft, ft->c, ft->s, wl, NULL);
expanded = format_expand(nft, use);
format_copy_state(&next, es, 0);
next.ft = nft;
expanded = format_expand1(&next, use);
format_free(nft);
valuelen += strlen(expanded);
@ -1889,21 +1924,23 @@ format_loop_windows(struct format_tree *ft, const char *fmt)
/* Loop over panes. */
static char *
format_loop_panes(struct format_tree *ft, const char *fmt)
format_loop_panes(struct format_expand_state *es, const char *fmt)
{
struct client *c = ft->client;
struct cmdq_item *item = ft->item;
struct format_tree *nft;
char *all, *active, *use, *expanded, *value;
size_t valuelen;
struct window_pane *wp;
struct format_tree *ft = es->ft;
struct client *c = ft->client;
struct cmdq_item *item = ft->item;
struct format_tree *nft;
struct format_expand_state next;
char *all, *active, *use, *expanded, *value;
size_t valuelen;
struct window_pane *wp;
if (ft->w == NULL) {
format_log(ft, "pane loop but no window");
format_log(es, "pane loop but no window");
return (NULL);
}
if (format_choose(ft, fmt, &all, &active, 0) != 0) {
if (format_choose(es, fmt, &all, &active, 0) != 0) {
all = xstrdup(fmt);
active = NULL;
}
@ -1912,15 +1949,16 @@ format_loop_panes(struct format_tree *ft, const char *fmt)
valuelen = 1;
TAILQ_FOREACH(wp, &ft->w->panes, entry) {
format_log(ft, "pane loop: %%%u", wp->id);
format_log(es, "pane loop: %%%u", wp->id);
if (active != NULL && wp == ft->w->active)
use = active;
else
use = all;
nft = format_create(c, item, FORMAT_PANE|wp->id, ft->flags);
nft->loop = ft->loop;
format_defaults(nft, ft->c, ft->s, ft->wl, wp);
expanded = format_expand(nft, use);
format_copy_state(&next, es, 0);
next.ft = nft;
expanded = format_expand1(&next, use);
format_free(nft);
valuelen += strlen(expanded);
@ -1937,16 +1975,26 @@ format_loop_panes(struct format_tree *ft, const char *fmt)
}
static char *
format_replace_expression(struct format_modifier *mexp, struct format_tree *ft,
const char *copy)
format_replace_expression(struct format_modifier *mexp,
struct format_expand_state *es, const char *copy)
{
int argc = mexp->argc;
const char *errstr;
char *endch, *value, *left = NULL, *right = NULL;
int use_fp = 0;
u_int prec = 0;
double mleft, mright, result;
enum { ADD, SUBTRACT, MULTIPLY, DIVIDE, MODULUS } operator;
int argc = mexp->argc;
const char *errstr;
char *endch, *value, *left = NULL, *right = NULL;
int use_fp = 0;
u_int prec = 0;
double mleft, mright, result;
enum { ADD,
SUBTRACT,
MULTIPLY,
DIVIDE,
MODULUS,
EQUAL,
NOT_EQUAL,
GREATER_THAN,
GREATER_THAN_EQUAL,
LESS_THAN,
LESS_THAN_EQUAL } operator;
if (strcmp(mexp->argv[0], "+") == 0)
operator = ADD;
@ -1959,8 +2007,20 @@ format_replace_expression(struct format_modifier *mexp, struct format_tree *ft,
else if (strcmp(mexp->argv[0], "%") == 0 ||
strcmp(mexp->argv[0], "m") == 0)
operator = MODULUS;
else if (strcmp(mexp->argv[0], "==") == 0)
operator = EQUAL;
else if (strcmp(mexp->argv[0], "!=") == 0)
operator = NOT_EQUAL;
else if (strcmp(mexp->argv[0], ">") == 0)
operator = GREATER_THAN;
else if (strcmp(mexp->argv[0], "<") == 0)
operator = LESS_THAN;
else if (strcmp(mexp->argv[0], ">=") == 0)
operator = GREATER_THAN_EQUAL;
else if (strcmp(mexp->argv[0], "<=") == 0)
operator = LESS_THAN_EQUAL;
else {
format_log(ft, "expression has no valid operator: '%s'",
format_log(es, "expression has no valid operator: '%s'",
mexp->argv[0]);
goto fail;
}
@ -1975,26 +2035,26 @@ format_replace_expression(struct format_modifier *mexp, struct format_tree *ft,
if (argc >= 3) {
prec = strtonum(mexp->argv[2], INT_MIN, INT_MAX, &errstr);
if (errstr != NULL) {
format_log (ft, "expression precision %s: %s", errstr,
format_log(es, "expression precision %s: %s", errstr,
mexp->argv[2]);
goto fail;
}
}
if (format_choose(ft, copy, &left, &right, 1) != 0) {
format_log(ft, "expression syntax error");
if (format_choose(es, copy, &left, &right, 1) != 0) {
format_log(es, "expression syntax error");
goto fail;
}
mleft = strtod(left, &endch);
if (*endch != '\0') {
format_log(ft, "expression left side is invalid: %s", left);
format_log(es, "expression left side is invalid: %s", left);
goto fail;
}
mright = strtod(right, &endch);
if (*endch != '\0') {
format_log(ft, "expression right side is invalid: %s", right);
format_log(es, "expression right side is invalid: %s", right);
goto fail;
}
@ -2002,8 +2062,8 @@ format_replace_expression(struct format_modifier *mexp, struct format_tree *ft,
mleft = (long long)mleft;
mright = (long long)mright;
}
format_log(ft, "expression left side is: %.*f", prec, mleft);
format_log(ft, "expression right side is: %.*f", prec, mright);
format_log(es, "expression left side is: %.*f", prec, mleft);
format_log(es, "expression right side is: %.*f", prec, mright);
switch (operator) {
case ADD:
@ -2021,12 +2081,30 @@ format_replace_expression(struct format_modifier *mexp, struct format_tree *ft,
case MODULUS:
result = fmod(mleft, mright);
break;
case EQUAL:
result = fabs(mleft - mright) < 1e-9;
break;
case NOT_EQUAL:
result = fabs(mleft - mright) > 1e-9;
break;
case GREATER_THAN:
result = (mleft > mright);
break;
case GREATER_THAN_EQUAL:
result = (mleft >= mright);
break;
case LESS_THAN:
result = (mleft < mright);
break;
case LESS_THAN_EQUAL:
result = (mleft > mright);
break;
}
if (use_fp)
xasprintf(&value, "%.*f", prec, result);
else
xasprintf(&value, "%.*f", prec, (double)(long long)result);
format_log(ft, "expression result is %s", value);
format_log(es, "expression result is %s", value);
free(right);
free(left);
@ -2040,31 +2118,34 @@ fail:
/* Replace a key. */
static int
format_replace(struct format_tree *ft, const char *key, size_t keylen,
format_replace(struct format_expand_state *es, const char *key, size_t keylen,
char **buf, size_t *len, size_t *off)
{
struct window_pane *wp = ft->wp;
const char *errptr, *copy, *cp, *marker = NULL;
const char *time_format = NULL;
char *copy0, *condition, *found, *new;
char *value, *left, *right;
size_t valuelen;
int modifiers = 0, limit = 0, width = 0, j;
struct format_modifier *list, *fm, *cmp = NULL, *search = NULL;
struct format_modifier **sub = NULL, *mexp = NULL;
u_int i, count, nsub = 0;
struct format_tree *ft = es->ft;
struct window_pane *wp = ft->wp;
const char *errptr, *copy, *cp, *marker = NULL;
const char *time_format = NULL;
char *copy0, *condition, *found, *new;
char *value, *left, *right;
size_t valuelen;
int modifiers = 0, limit = 0, width = 0;
int j;
struct format_modifier *list, *cmp = NULL, *search = NULL;
struct format_modifier **sub = NULL, *mexp = NULL, *fm;
u_int i, count, nsub = 0;
struct format_expand_state next;
/* Make a copy of the key. */
copy = copy0 = xstrndup(key, keylen);
/* Process modifier list. */
list = format_build_modifiers(ft, &copy, &count);
list = format_build_modifiers(es, &copy, &count);
for (i = 0; i < count; i++) {
fm = &list[i];
if (format_logging(ft)) {
format_log(ft, "modifier %u is %s", i, fm->modifier);
format_log(es, "modifier %u is %s", i, fm->modifier);
for (j = 0; j < fm->argc; j++) {
format_log(ft, "modifier %u argument %d: %s", i,
format_log(es, "modifier %u argument %d: %s", i,
j, fm->argv[j]);
}
}
@ -2117,6 +2198,9 @@ format_replace(struct format_tree *ft, const char *key, size_t keylen,
case 'd':
modifiers |= FORMAT_DIRNAME;
break;
case 'n':
modifiers |= FORMAT_LENGTH;
break;
case 't':
modifiers |= FORMAT_TIMESTRING;
if (fm->argc < 1)
@ -2165,37 +2249,37 @@ format_replace(struct format_tree *ft, const char *key, size_t keylen,
/* Is this a loop, comparison or condition? */
if (modifiers & FORMAT_SESSIONS) {
value = format_loop_sessions(ft, copy);
value = format_loop_sessions(es, copy);
if (value == NULL)
goto fail;
} else if (modifiers & FORMAT_WINDOWS) {
value = format_loop_windows(ft, copy);
value = format_loop_windows(es, copy);
if (value == NULL)
goto fail;
} else if (modifiers & FORMAT_PANES) {
value = format_loop_panes(ft, copy);
value = format_loop_panes(es, copy);
if (value == NULL)
goto fail;
} else if (search != NULL) {
/* Search in pane. */
new = format_expand(ft, copy);
new = format_expand1(es, copy);
if (wp == NULL) {
format_log(ft, "search '%s' but no pane", new);
format_log(es, "search '%s' but no pane", new);
value = xstrdup("0");
} else {
format_log(ft, "search '%s' pane %%%u", new, wp->id);
format_log(es, "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) {
format_log(ft, "compare %s syntax error: %s",
if (format_choose(es, copy, &left, &right, 1) != 0) {
format_log(es, "compare %s syntax error: %s",
cmp->modifier, copy);
goto fail;
}
format_log(ft, "compare %s left is: %s", cmp->modifier, left);
format_log(ft, "compare %s right is: %s", cmp->modifier, right);
format_log(es, "compare %s left is: %s", cmp->modifier, left);
format_log(es, "compare %s right is: %s", cmp->modifier, right);
if (strcmp(cmp->modifier, "||") == 0) {
if (format_true(left) || format_true(right))
@ -2246,11 +2330,11 @@ format_replace(struct format_tree *ft, const char *key, size_t keylen,
/* Conditional: check first and choose second or third. */
cp = format_skip(copy + 1, ",");
if (cp == NULL) {
format_log(ft, "condition syntax error: %s", copy + 1);
format_log(es, "condition syntax error: %s", copy + 1);
goto fail;
}
condition = xstrndup(copy + 1, cp - (copy + 1));
format_log(ft, "condition is: %s", condition);
format_log(es, "condition is: %s", condition);
found = format_find(ft, condition, modifiers, time_format);
if (found == NULL) {
@ -2259,32 +2343,32 @@ format_replace(struct format_tree *ft, const char *key, size_t keylen,
* the expansion doesn't have any effect, then assume
* false.
*/
found = format_expand(ft, condition);
found = format_expand1(es, condition);
if (strcmp(found, condition) == 0) {
free(found);
found = xstrdup("");
format_log(ft, "condition '%s' found: %s",
format_log(es, "condition '%s' found: %s",
condition, found);
} else {
format_log(ft,
format_log(es,
"condition '%s' not found; assuming false",
condition);
}
} else
format_log(ft, "condition '%s' found", condition);
format_log(es, "condition '%s' found", condition);
if (format_choose(ft, cp + 1, &left, &right, 0) != 0) {
format_log(ft, "condition '%s' syntax error: %s",
if (format_choose(es, cp + 1, &left, &right, 0) != 0) {
format_log(es, "condition '%s' syntax error: %s",
condition, cp + 1);
free(found);
goto fail;
}
if (format_true(found)) {
format_log(ft, "condition '%s' is true", condition);
value = format_expand(ft, left);
format_log(es, "condition '%s' is true", condition);
value = format_expand1(es, left);
} else {
format_log(ft, "condition '%s' is false", condition);
value = format_expand(ft, right);
format_log(es, "condition '%s' is false", condition);
value = format_expand1(es, right);
}
free(right);
free(left);
@ -2292,38 +2376,44 @@ format_replace(struct format_tree *ft, const char *key, size_t keylen,
free(condition);
free(found);
} else if (mexp != NULL) {
value = format_replace_expression(mexp, ft, copy);
value = format_replace_expression(mexp, es, copy);
if (value == NULL)
value = xstrdup("");
} else {
/* Neither: look up directly. */
value = format_find(ft, copy, modifiers, time_format);
if (value == NULL) {
format_log(ft, "format '%s' not found", copy);
value = xstrdup("");
} else
format_log(ft, "format '%s' found: %s", copy, value);
if (strstr(copy, "#{") != 0) {
format_log(es, "expanding inner format '%s'", copy);
value = format_expand1(es, copy);
} else {
value = format_find(ft, copy, modifiers, time_format);
if (value == NULL) {
format_log(es, "format '%s' not found", copy);
value = xstrdup("");
} else {
format_log(es, "format '%s' found: %s", copy,
value);
}
}
}
done:
/* Expand again if required. */
if (modifiers & FORMAT_EXPAND) {
new = format_expand(ft, value);
new = format_expand1(es, value);
free(value);
value = new;
}
else if (modifiers & FORMAT_EXPANDTIME) {
new = format_expand_time(ft, value);
} else if (modifiers & FORMAT_EXPANDTIME) {
format_copy_state(&next, es, FORMAT_EXPAND_TIME);
new = format_expand1(&next, value);
free(value);
value = new;
}
/* Perform substitution if any. */
for (i = 0; i < nsub; i++) {
left = format_expand(ft, sub[i]->argv[0]);
right = format_expand(ft, sub[i]->argv[1]);
left = format_expand1(es, sub[i]->argv[0]);
right = format_expand1(es, sub[i]->argv[1]);
new = format_sub(sub[i], value, left, right);
format_log(ft, "substitute '%s' to '%s': %s", left, right, new);
format_log(es, "substitute '%s' to '%s': %s", left, right, new);
free(value);
value = new;
free(right);
@ -2340,7 +2430,7 @@ done:
free(value);
value = new;
}
format_log(ft, "applied length limit %d: %s", limit, value);
format_log(es, "applied length limit %d: %s", limit, value);
} else if (limit < 0) {
new = format_trim_right(value, -limit);
if (marker != NULL && strcmp(new, value) != 0) {
@ -2350,7 +2440,7 @@ done:
free(value);
value = new;
}
format_log(ft, "applied length limit %d: %s", limit, value);
format_log(es, "applied length limit %d: %s", limit, value);
}
/* Pad the value if needed. */
@ -2358,12 +2448,20 @@ done:
new = utf8_padcstr(value, width);
free(value);
value = new;
format_log(ft, "applied padding width %d: %s", width, value);
format_log(es, "applied padding width %d: %s", width, value);
} else if (width < 0) {
new = utf8_rpadcstr(value, -width);
free(value);
value = new;
format_log(ft, "applied padding width %d: %s", width, value);
format_log(es, "applied padding width %d: %s", width, value);
}
/* Replace with the length if needed. */
if (modifiers & FORMAT_LENGTH) {
xasprintf(&new, "%zu", strlen(value));
free(value);
value = new;
format_log(es, "replacing with length: %s", new);
}
/* Expand the buffer and copy in the value. */
@ -2375,7 +2473,7 @@ done:
memcpy(*buf + *off, value, valuelen);
*off += valuelen;
format_log(ft, "replaced '%s' with '%s'", copy0, value);
format_log(es, "replaced '%s' with '%s'", copy0, value);
free(value);
free(sub);
@ -2384,7 +2482,7 @@ done:
return (0);
fail:
format_log(ft, "failed %s", copy0);
format_log(es, "failed %s", copy0);
free(sub);
format_free_modifiers(list, count);
@ -2394,32 +2492,35 @@ fail:
/* Expand keys in a template. */
static char *
format_expand1(struct format_tree *ft, const char *fmt, int time)
format_expand1(struct format_expand_state *es, const char *fmt)
{
char *buf, *out, *name;
const char *ptr, *s;
size_t off, len, n, outlen;
int ch, brackets;
struct tm *tm;
char expanded[8192];
struct format_tree *ft = es->ft;
char *buf, *out, *name;
const char *ptr, *s;
size_t off, len, n, outlen;
int ch, brackets;
struct tm *tm;
char expanded[8192];
if (fmt == NULL || *fmt == '\0')
return (xstrdup(""));
if (ft->loop == FORMAT_LOOP_LIMIT)
if (es->loop == FORMAT_LOOP_LIMIT)
return (xstrdup(""));
ft->loop++;
es->loop++;
format_log(ft, "expanding format: %s", fmt);
format_log(es, "expanding format: %s", fmt);
if (time) {
tm = localtime(&ft->time);
if (es->flags & FORMAT_EXPAND_TIME) {
if (es->time == 0)
es->time = time(NULL);
tm = localtime(&es->time);
if (strftime(expanded, sizeof expanded, fmt, tm) == 0) {
format_log(ft, "format is too long");
format_log(es, "format is too long");
return (xstrdup(""));
}
if (format_logging(ft) && strcmp(expanded, fmt) != 0)
format_log(ft, "after time expanded: %s", expanded);
format_log(es, "after time expanded: %s", expanded);
fmt = expanded;
}
@ -2453,14 +2554,15 @@ format_expand1(struct format_tree *ft, const char *fmt, int time)
n = ptr - fmt;
name = xstrndup(fmt, n);
format_log(ft, "found #(): %s", name);
format_log(es, "found #(): %s", name);
if (ft->flags & FORMAT_NOJOBS) {
if ((ft->flags & FORMAT_NOJOBS) ||
(es->flags & FORMAT_EXPAND_NOJOBS)) {
out = xstrdup("");
format_log(ft, "#() is disabled");
format_log(es, "#() is disabled");
} else {
out = format_job_get(ft, name);
format_log(ft, "#() result: %s", out);
out = format_job_get(es, name);
format_log(es, "#() result: %s", out);
}
free(name);
@ -2482,15 +2584,15 @@ format_expand1(struct format_tree *ft, const char *fmt, int time)
break;
n = ptr - fmt;
format_log(ft, "found #{}: %.*s", (int)n, fmt);
if (format_replace(ft, fmt, n, &buf, &len, &off) != 0)
format_log(es, "found #{}: %.*s", (int)n, fmt);
if (format_replace(es, fmt, n, &buf, &len, &off) != 0)
break;
fmt += n + 1;
continue;
case '}':
case '#':
case ',':
format_log(ft, "found #%c", ch);
format_log(es, "found #%c", ch);
while (len - off < 2) {
buf = xreallocarray(buf, 2, len);
len *= 2;
@ -2513,8 +2615,8 @@ format_expand1(struct format_tree *ft, const char *fmt, int time)
continue;
}
n = strlen(s);
format_log(ft, "found #%c: %s", ch, s);
if (format_replace(ft, s, n, &buf, &len, &off) != 0)
format_log(es, "found #%c: %s", ch, s);
if (format_replace(es, s, n, &buf, &len, &off) != 0)
break;
continue;
}
@ -2523,8 +2625,8 @@ format_expand1(struct format_tree *ft, const char *fmt, int time)
}
buf[off] = '\0';
format_log(ft, "result is: %s", buf);
ft->loop--;
format_log(es, "result is: %s", buf);
es->loop--;
return (buf);
}
@ -2533,14 +2635,24 @@ format_expand1(struct format_tree *ft, const char *fmt, int time)
char *
format_expand_time(struct format_tree *ft, const char *fmt)
{
return (format_expand1(ft, fmt, 1));
struct format_expand_state es;
memset(&es, 0, sizeof es);
es.ft = ft;
es.flags = FORMAT_EXPAND_TIME;
return (format_expand1(&es, fmt));
}
/* Expand keys in a template. */
char *
format_expand(struct format_tree *ft, const char *fmt)
{
return (format_expand1(ft, fmt, 0));
struct format_expand_state es;
memset(&es, 0, sizeof es);
es.ft = ft;
es.flags = 0;
return (format_expand1(&es, fmt));
}
/* Expand a single string. */
@ -2889,6 +3001,7 @@ format_defaults_pane(struct format_tree *ft, struct window_pane *wp)
format_add(ft, "pane_dead", "%d", wp->fd == -1);
else
format_add(ft, "pane_dead", "0");
format_add(ft, "pane_last", "%d", wp == w->last);
if (server_check_marked() && marked_pane.wp == wp)
format_add(ft, "pane_marked", "1");

24
grid.c
View File

@ -265,6 +265,9 @@ grid_free_lines(struct grid *gd, u_int py, u_int ny)
for (yy = py; yy < py + ny; yy++)
grid_free_line(gd, yy);
#ifdef HAVE_MALLOC_TRIM
malloc_trim(0);
#endif
}
/* Create a new grid. */
@ -463,7 +466,7 @@ grid_expand_line(struct grid *gd, u_int py, u_int sx, u_int bg)
sx = gd->sx / 4;
else if (sx < gd->sx / 2)
sx = gd->sx / 2;
else
else if (gd->sx > sx)
sx = gd->sx;
gl->celldata = xreallocarray(gl->celldata, sx, sizeof *gl->celldata);
@ -1277,7 +1280,7 @@ grid_reflow(struct grid *gd, u_int sx)
struct grid *target;
struct grid_line *gl;
struct grid_cell gc;
u_int yy, width, i, at, first;
u_int yy, width, i, at;
/*
* Create a destination grid. This is just used as a container for the
@ -1294,13 +1297,12 @@ grid_reflow(struct grid *gd, u_int sx)
continue;
/*
* Work out the width of this line. first is the width of the
* first character, at is the point at which the available
* width is hit, and width is the full line width.
* Work out the width of this line. at is the point at which
* the available width is hit, and width is the full line
* width.
*/
first = at = width = 0;
at = width = 0;
if (~gl->flags & GRID_LINE_EXTENDED) {
first = 1;
width = gl->cellused;
if (width > sx)
at = sx;
@ -1309,8 +1311,6 @@ grid_reflow(struct grid *gd, u_int sx)
} else {
for (i = 0; i < gl->cellused; i++) {
grid_get_cell1(gl, i, &gc);
if (i == 0)
first = gc.data.width;
if (at == 0 && width + gc.data.width > sx)
at = i;
width += gc.data.width;
@ -1318,10 +1318,10 @@ grid_reflow(struct grid *gd, u_int sx)
}
/*
* If the line is exactly right or the first character is wider
* than the target width, just move it across unchanged.
* If the line is exactly right, just move it across
* unchanged.
*/
if (width == sx || first > sx) {
if (width == sx) {
grid_reflow_move(target, gl);
continue;
}

View File

@ -555,6 +555,8 @@ input_key(struct screen *s, struct bufferevent *bev, key_code key)
case KEYC_SHIFT|KEYC_META|KEYC_CTRL:
modifier = '8';
break;
default:
fatalx("invalid key modifiers: %llx", key);
}
xsnprintf(tmp, sizeof tmp, "\033[%llu;%cu", outkey, modifier);
bufferevent_write(bev, tmp, strlen(tmp));

51
input.c
View File

@ -65,7 +65,7 @@ struct input_param {
INPUT_MISSING,
INPUT_NUMBER,
INPUT_STRING
} type;
} type;
union {
int num;
char *str;
@ -81,7 +81,7 @@ struct input_ctx {
struct input_cell cell;
struct input_cell old_cell;
u_int old_cx;
u_int old_cx;
u_int old_cy;
int old_mode;
@ -121,7 +121,7 @@ struct input_ctx {
* All input received since we were last in the ground state. Sent to
* control clients on connection.
*/
struct evbuffer *since_ground;
struct evbuffer *since_ground;
};
/* Helper functions. */
@ -1545,6 +1545,10 @@ input_csi_dispatch(struct input_ctx *ictx)
if (n == -1)
break;
m = screen_size_x(s) - s->cx;
if (n > m)
n = m;
if (ictx->last == -1)
break;
ictx->ch = ictx->last;
@ -1867,6 +1871,7 @@ input_csi_dispatch_winops(struct input_ctx *ictx)
case 2:
screen_pop_title(sctx->s);
if (wp != NULL) {
notify_pane("pane-title-changed", wp);
server_redraw_window_borders(wp->window);
server_status_window(wp->window);
}
@ -2266,6 +2271,7 @@ input_exit_osc(struct input_ctx *ictx)
case 0:
case 2:
if (screen_set_title(sctx->s, p) && wp != NULL) {
notify_pane("pane-title-changed", wp);
server_redraw_window_borders(wp->window);
server_status_window(wp->window);
}
@ -2331,6 +2337,7 @@ input_exit_apc(struct input_ctx *ictx)
log_debug("%s: \"%s\"", __func__, ictx->input_buf);
if (screen_set_title(sctx->s, ictx->input_buf) && wp != NULL) {
notify_pane("pane-title-changed", wp);
server_redraw_window_borders(wp->window);
server_status_window(wp->window);
}
@ -2461,13 +2468,31 @@ input_osc_parse_colour(const char *p, u_int *r, u_int *g, u_int *b)
return (1);
}
/* Reply to a colour request. */
static void
input_osc_colour_reply(struct input_ctx *ictx, u_int n, int c)
{
u_char r, g, b;
const char *end;
if (c == 8 || (~c & COLOUR_FLAG_RGB))
return;
colour_split_rgb(c, &r, &g, &b);
if (ictx->input_end == INPUT_END_BEL)
end = "\007";
else
end = "\033\\";
input_reply(ictx, "\033]%u;rgb:%02hhx/%02hhx/%02hhx%s", n, r, g, b, end);
}
/* Handle the OSC 4 sequence for setting (multiple) palette entries. */
static void
input_osc_4(struct input_ctx *ictx, const char *p)
{
struct window_pane *wp = ictx->wp;
char *copy, *s, *next = NULL;
long idx;
long idx;
u_int r, g, b;
if (wp == NULL)
@ -2499,17 +2524,22 @@ bad:
free(copy);
}
/* Handle the OSC 10 sequence for setting foreground colour. */
/* Handle the OSC 10 sequence for setting and querying foreground colour. */
static void
input_osc_10(struct input_ctx *ictx, const char *p)
{
struct window_pane *wp = ictx->wp;
struct grid_cell defaults;
u_int r, g, b;
if (wp == NULL)
return;
if (strcmp(p, "?") == 0)
if (strcmp(p, "?") == 0) {
tty_default_colours(&defaults, wp);
input_osc_colour_reply(ictx, 10, defaults.fg);
return;
}
if (!input_osc_parse_colour(p, &r, &g, &b))
goto bad;
@ -2522,17 +2552,22 @@ bad:
log_debug("bad OSC 10: %s", p);
}
/* Handle the OSC 11 sequence for setting background colour. */
/* Handle the OSC 11 sequence for setting and querying background colour. */
static void
input_osc_11(struct input_ctx *ictx, const char *p)
{
struct window_pane *wp = ictx->wp;
struct grid_cell defaults;
u_int r, g, b;
if (wp == NULL)
return;
if (strcmp(p, "?") == 0)
if (strcmp(p, "?") == 0) {
tty_default_colours(&defaults, wp);
input_osc_colour_reply(ictx, 11, defaults.bg);
return;
}
if (!input_osc_parse_colour(p, &r, &g, &b))
goto bad;

View File

@ -191,6 +191,16 @@ key_bindings_add(const char *name, key_code key, const char *note, int repeat,
table = key_bindings_get_table(name, 1);
bd = key_bindings_get(table, key & ~KEYC_MASK_FLAGS);
if (cmdlist == NULL) {
if (bd != NULL) {
free((void *)bd->note);
if (note != NULL)
bd->note = xstrdup(note);
else
bd->note = NULL;
}
return;
}
if (bd != NULL) {
RB_REMOVE(key_bindings, &table->key_bindings, bd);
key_bindings_free(bd);

25
menu.c
View File

@ -203,16 +203,28 @@ menu_key_cb(struct client *c, struct key_event *event)
m->x > md->px + 4 + menu->width ||
m->y < md->py + 1 ||
m->y > md->py + 1 + count - 1) {
if (MOUSE_RELEASE(m->b))
return (1);
if (~md->flags & MENU_STAYOPEN) {
if (MOUSE_RELEASE(m->b))
return (1);
} else {
if (!MOUSE_RELEASE(m->b) &&
MOUSE_WHEEL(m->b) == 0 &&
!MOUSE_DRAG(m->b))
return (1);
}
if (md->choice != -1) {
md->choice = -1;
c->flags |= CLIENT_REDRAWOVERLAY;
}
return (0);
}
if (MOUSE_RELEASE(m->b))
goto chosen;
if (~md->flags & MENU_STAYOPEN) {
if (MOUSE_RELEASE(m->b))
goto chosen;
} else {
if (MOUSE_WHEEL(m->b) == 0 && !MOUSE_DRAG(m->b))
goto chosen;
}
md->choice = m->y - (md->py + 1);
if (md->choice != old)
c->flags |= CLIENT_REDRAWOVERLAY;
@ -303,8 +315,11 @@ chosen:
if (md->choice == -1)
return (1);
item = &menu->items[md->choice];
if (item->name == NULL || *item->name == '-')
if (item->name == NULL || *item->name == '-') {
if (md->flags & MENU_STAYOPEN)
return (0);
return (1);
}
if (md->cb != NULL) {
md->cb(md->menu, md->choice, item->key, md->data);
md->cb = NULL;

View File

@ -1176,7 +1176,7 @@ mode_tree_run_command(struct client *c, struct cmd_find_state *fs,
if (status == CMD_PARSE_ERROR) {
if (c != NULL) {
*error = toupper((u_char)*error);
status_message_set(c, 1, "%s", error);
status_message_set(c, -1, 1, "%s", error);
}
free(error);
}

View File

@ -170,6 +170,14 @@ static const char *options_table_status_format_default[] = {
.separator = "" \
}
/* Map of name conversions. */
const struct options_name_map options_other_names[] = {
{ "display-panes-color", "display-panes-colour" },
{ "display-panes-active-color", "display-panes-active-colour" },
{ "clock-mode-color", "clock-mode-colour" },
{ NULL, NULL }
};
/* Top-level options. */
const struct options_table_entry options_table[] = {
/* Server options. */
@ -1120,6 +1128,7 @@ const struct options_table_entry options_table[] = {
OPTIONS_TABLE_PANE_HOOK("pane-focus-out", ""),
OPTIONS_TABLE_PANE_HOOK("pane-mode-changed", ""),
OPTIONS_TABLE_PANE_HOOK("pane-set-clipboard", ""),
OPTIONS_TABLE_PANE_HOOK("pane-title-changed", ""),
OPTIONS_TABLE_HOOK("session-closed", ""),
OPTIONS_TABLE_HOOK("session-created", ""),
OPTIONS_TABLE_HOOK("session-renamed", ""),

View File

@ -95,6 +95,18 @@ options_cmp(struct options_entry *lhs, struct options_entry *rhs)
return (strcmp(lhs->name, rhs->name));
}
static const char *
options_map_name(const char *name)
{
const struct options_name_map *map;
for (map = options_other_names; map->from != NULL; map++) {
if (strcmp(map->from, name) == 0)
return (map->to);
}
return (name);
}
static const struct options_table_entry *
options_parent_table_entry(struct options *oo, const char *s)
{
@ -204,10 +216,14 @@ options_next(struct options_entry *o)
struct options_entry *
options_get_only(struct options *oo, const char *name)
{
struct options_entry o;
struct options_entry o = { .name = name }, *found;
o.name = name;
return (RB_FIND(options_tree, &oo->tree, &o));
found = RB_FIND(options_tree, &oo->tree, &o);
if (found == NULL) {
o.name = options_map_name(name);
return (RB_FIND(options_tree, &oo->tree, &o));
}
return (found);
}
struct options_entry *
@ -608,19 +624,21 @@ char *
options_match(const char *s, int *idx, int *ambiguous)
{
const struct options_table_entry *oe, *found;
char *name;
char *parsed;
const char *name;
size_t namelen;
name = options_parse(s, idx);
if (name == NULL)
parsed = options_parse(s, idx);
if (parsed == NULL)
return (NULL);
namelen = strlen(name);
if (*name == '@') {
if (*parsed == '@') {
*ambiguous = 0;
return (name);
return (parsed);
}
name = options_map_name(parsed);
namelen = strlen(name);
found = NULL;
for (oe = options_table; oe->name != NULL; oe++) {
if (strcmp(oe->name, name) == 0) {
@ -630,13 +648,13 @@ options_match(const char *s, int *idx, int *ambiguous)
if (strncmp(oe->name, name, namelen) == 0) {
if (found != NULL) {
*ambiguous = 1;
free(name);
free(parsed);
return (NULL);
}
found = oe;
}
}
free(name);
free(parsed);
if (found == NULL) {
*ambiguous = 0;
return (NULL);

View File

@ -261,7 +261,7 @@ popup_handle_drag(struct client *c, struct popup_data *pd,
pd->sx = m->x - pd->px;
pd->sy = m->y - pd->py;
screen_resize(&pd->s, pd->sx, pd->sy, 0);
screen_resize(&pd->s, pd->sx - 2, pd->sy - 2, 0);
if (pd->ictx == NULL)
popup_write_screen(c, pd);
else if (pd->job != NULL)

1
proc.c
View File

@ -239,6 +239,7 @@ proc_set_signals(struct tmuxproc *tp, void (*signalcb)(int))
sigaction(SIGTSTP, &sa, NULL);
sigaction(SIGTTIN, &sa, NULL);
sigaction(SIGTTOU, &sa, NULL);
sigaction(SIGQUIT, &sa, NULL);
signal_set(&tp->ev_sigint, SIGINT, proc_signal_cb, tp);
signal_add(&tp->ev_sigint, NULL);

93
regress/style-trim.sh Normal file
View File

@ -0,0 +1,93 @@
#!/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
$TMUX2 -f/dev/null new -d "$TMUX -f/dev/null new"
sleep 2
$TMUX set -g status-style fg=default,bg=default
check() {
v=$($TMUX display -p "$1")
$TMUX set -g status-format[0] "$1"
sleep 1
r=$($TMUX2 capturep -Cep|tail -1|sed 's|\\033\[||g')
#printf "$1 = [$v = $2] [$r = $3]"
if [ "$v" = "$2" -a "$r" = "$3" ]; then
: #printf " good\n"
else
#printf " \033[31mbad\033[0m\n"
exit 1
fi
}
# drawn as #0
$TMUX setenv -g V '#0'
check '#{V} #{w:V}' '#0 2' '#0 2'
check '#{=3:V}' '#0' '#0'
check '#{=-3:V}' '#0' '#0'
# drawn as #0
$TMUX setenv -g V '###[bg=yellow]0'
check '#{V} #{w:V}' '###[bg=yellow]0 2' '#43m0 249m'
check '#{=3:V}' '###[bg=yellow]0' '#43m049m'
check '#{=-3:V}' '###[bg=yellow]0' '#43m049m'
# drawn as #0123456
$TMUX setenv -g V '#0123456'
check '#{V} #{w:V}' '#0123456 8' '#0123456 8'
check '#{=3:V}' '#01' '#01'
check '#{=-3:V}' '456' '456'
# drawn as ##0123456
$TMUX setenv -g V '##0123456'
check '#{V} #{w:V}' '##0123456 9' '##0123456 9'
check '#{=3:V}' '##0' '##0'
check '#{=-3:V}' '456' '456'
# drawn as ###0123456
$TMUX setenv -g V '###0123456'
check '#{V} #{w:V}' '###0123456 10' '###0123456 10'
check '#{=3:V}' '###' '###'
check '#{=-3:V}' '456' '456'
# drawn as 0123456
$TMUX setenv -g V '#[bg=yellow]0123456'
check '#{V} #{w:V}' '#[bg=yellow]0123456 7' '43m0123456 749m'
check '#{=3:V}' '#[bg=yellow]012' '43m01249m'
check '#{=-3:V}' '#[bg=yellow]456' '43m45649m'
# drawn as #[bg=yellow]0123456
$TMUX setenv -g V '##[bg=yellow]0123456'
check '#{V} #{w:V}' '##[bg=yellow]0123456 19' '#[bg=yellow]0123456 19'
check '#{=3:V}' '##[b' '#[b'
check '#{=-3:V}' '456' '456'
# drawn as #0123456
$TMUX setenv -g V '###[bg=yellow]0123456'
check '#{V} #{w:V}' '###[bg=yellow]0123456 8' '#43m0123456 849m'
check '#{=3:V}' '###[bg=yellow]01' '#43m0149m'
check '#{=-3:V}' '#[bg=yellow]456' '43m45649m'
# drawn as ##[bg=yellow]0123456
$TMUX setenv -g V '####[bg=yellow]0123456'
check '#{V} #{w:V}' '####[bg=yellow]0123456 20' '##[bg=yellow]0123456 20'
check '#{=3:V}' '####[' '##['
check '#{=-3:V}' '456' '456'
# drawn as ###0123456
$TMUX setenv -g V '#####[bg=yellow]0123456'
check '#{V} #{w:V}' '#####[bg=yellow]0123456 9' '##43m0123456 949m'
check '#{=3:V}' '#####[bg=yellow]0' '##43m049m'
check '#{=-3:V}' '#[bg=yellow]456' '43m45649m'
$TMUX kill-server 2>/dev/null
$TMUX2 kill-server 2>/dev/null
exit 0

375
resize.c
View File

@ -92,130 +92,156 @@ ignore_client_size(struct client *c)
return (0);
}
void
default_window_size(struct client *c, struct session *s, struct window *w,
u_int *sx, u_int *sy, u_int *xpixel, u_int *ypixel, int type)
static u_int
clients_with_window(struct window *w)
{
struct client *loop;
u_int cx, cy, n;
const char *value;
u_int n = 0;
if (type == -1)
type = options_get_number(global_w_options, "window-size");
switch (type) {
case WINDOW_SIZE_LARGEST:
TAILQ_FOREACH(loop, &clients, entry) {
if (ignore_client_size(loop) || !session_has(loop->session, w))
continue;
if (++n > 1)
break;
}
return (n);
}
static int
clients_calculate_size(int type, int current, struct session *s,
struct window *w, int (*skip_client)(struct client *, int, int,
struct session *, struct window *), u_int *sx, u_int *sy, u_int *xpixel,
u_int *ypixel)
{
struct client *loop;
u_int cx, cy, n = 0;
/* Manual windows do not have their size changed based on a client. */
if (type == WINDOW_SIZE_MANUAL)
return (0);
/*
* Start comparing with 0 for largest and UINT_MAX for smallest or
* latest.
*/
if (type == WINDOW_SIZE_LARGEST)
*sx = *sy = 0;
*xpixel = *ypixel = 0;
TAILQ_FOREACH(loop, &clients, entry) {
if (ignore_client_size(loop))
continue;
if (w != NULL && !session_has(loop->session, w))
continue;
if (w == NULL && loop->session != s)
continue;
else
*sx = *sy = UINT_MAX;
*xpixel = *ypixel = 0;
cx = loop->tty.sx;
cy = loop->tty.sy - status_line_size(loop);
/*
* For latest, count the number of clients with this window. We only
* care if there is more than one.
*/
if (type == WINDOW_SIZE_LATEST)
n = clients_with_window(w);
/* Loop over the clients and work out the size. */
TAILQ_FOREACH(loop, &clients, entry) {
if (ignore_client_size(loop))
continue;
if (skip_client(loop, type, current, s, w))
continue;
/*
* If there are multiple clients attached, only accept the
* latest client; otherwise let the only client be chosen as
* for smallest.
*/
if (type == WINDOW_SIZE_LATEST && n > 1 && loop != w->latest)
continue;
/* Work out this client's size. */
cx = loop->tty.sx;
cy = loop->tty.sy - status_line_size(loop);
/*
* If it is larger or smaller than the best so far, update the
* new size.
*/
if (type == WINDOW_SIZE_LARGEST) {
if (cx > *sx)
*sx = cx;
if (cy > *sy)
*sy = cy;
if (loop->tty.xpixel > *xpixel &&
loop->tty.ypixel > *ypixel) {
*xpixel = loop->tty.xpixel;
*ypixel = loop->tty.ypixel;
}
}
if (*sx == 0 || *sy == 0)
goto manual;
break;
case WINDOW_SIZE_SMALLEST:
*sx = *sy = UINT_MAX;
*xpixel = *ypixel = 0;
TAILQ_FOREACH(loop, &clients, entry) {
if (ignore_client_size(loop))
continue;
if (w != NULL && !session_has(loop->session, w))
continue;
if (w == NULL && loop->session != s)
continue;
cx = loop->tty.sx;
cy = loop->tty.sy - status_line_size(loop);
} else {
if (cx < *sx)
*sx = cx;
if (cy < *sy)
*sy = cy;
if (loop->tty.xpixel > *xpixel &&
loop->tty.ypixel > *ypixel) {
*xpixel = loop->tty.xpixel;
*ypixel = loop->tty.ypixel;
}
}
if (*sx == UINT_MAX || *sy == UINT_MAX)
goto manual;
break;
case WINDOW_SIZE_LATEST:
if (loop->tty.xpixel > *xpixel && loop->tty.ypixel > *ypixel) {
*xpixel = loop->tty.xpixel;
*ypixel = loop->tty.ypixel;
}
}
/* Return whether a suitable size was found. */
if (type == WINDOW_SIZE_LARGEST)
return (*sx != 0 && *sy != 0);
return (*sx != UINT_MAX && *sy != UINT_MAX);
}
static int
default_window_size_skip_client (struct client *loop, int type,
__unused int current, struct session *s, struct window *w)
{
/*
* Latest checks separately, so do not check here. Otherwise only
* include clients where the session contains the window or where the
* session is the given session.
*/
if (type == WINDOW_SIZE_LATEST)
return (0);
if (w != NULL && !session_has(loop->session, w))
return (1);
if (w == NULL && loop->session != s)
return (1);
return (0);
}
void
default_window_size(struct client *c, struct session *s, struct window *w,
u_int *sx, u_int *sy, u_int *xpixel, u_int *ypixel, int type)
{
const char *value;
/* Get type if not provided. */
if (type == -1)
type = options_get_number(global_w_options, "window-size");
/*
* Latest clients can use the given client if suitable. If there is no
* client and no window, use the default size as for manual type.
*/
if (type == WINDOW_SIZE_LATEST) {
if (c != NULL && !ignore_client_size(c)) {
*sx = c->tty.sx;
*sy = c->tty.sy - status_line_size(c);
*xpixel = c->tty.xpixel;
*ypixel = c->tty.ypixel;
} else {
if (w == NULL)
goto manual;
n = 0;
TAILQ_FOREACH(loop, &clients, entry) {
if (!ignore_client_size(loop) &&
session_has(loop->session, w)) {
if (++n > 1)
break;
}
}
*sx = *sy = UINT_MAX;
*xpixel = *ypixel = 0;
TAILQ_FOREACH(loop, &clients, entry) {
if (ignore_client_size(loop))
continue;
if (n > 1 && loop != w->latest)
continue;
s = loop->session;
cx = loop->tty.sx;
cy = loop->tty.sy - status_line_size(loop);
if (cx < *sx)
*sx = cx;
if (cy < *sy)
*sy = cy;
if (loop->tty.xpixel > *xpixel &&
loop->tty.ypixel > *ypixel) {
*xpixel = loop->tty.xpixel;
*ypixel = loop->tty.ypixel;
}
}
if (*sx == UINT_MAX || *sy == UINT_MAX)
goto manual;
*ypixel = c->tty.ypixel;
goto done;
}
break;
case WINDOW_SIZE_MANUAL:
goto manual;
if (w == NULL)
type = WINDOW_SIZE_MANUAL;
}
goto done;
manual:
value = options_get_string(s->options, "default-size");
if (sscanf(value, "%ux%u", sx, sy) != 2) {
*sx = 80;
*sy = 24;
/*
* Look for a client to base the size on. If none exists (or the type
* is manual), use the default-size option.
*/
if (!clients_calculate_size(type, 0, s, w,
default_window_size_skip_client, sx, sy, xpixel, ypixel)) {
value = options_get_string(s->options, "default-size");
if (sscanf(value, "%ux%u", sx, sy) != 2) {
*sx = 80;
*sy = 24;
}
}
done:
/* Make sure the limits are enforced. */
if (*sx < WINDOW_MINIMUM)
*sx = WINDOW_MINIMUM;
if (*sx > WINDOW_MAXIMUM)
@ -226,127 +252,50 @@ done:
*sy = WINDOW_MAXIMUM;
}
static int
recalculate_size_skip_client(struct client *loop, __unused int type,
int current, __unused struct session *s, struct window *w)
{
/*
* If the current flag is set, then skip any client where this window
* is not the current window - this is used for aggressive-resize.
* Otherwise skip any session that doesn't contain the window.
*/
if (current)
return (loop->session->curw->window != w);
return (session_has(loop->session, w) == 0);
}
void
recalculate_size(struct window *w, int now)
{
struct session *s;
struct client *c;
u_int sx, sy, cx, cy, xpixel = 0, ypixel = 0, n;
int type, current, has, changed;
u_int sx, sy, xpixel = 0, ypixel = 0;
int type, current, changed;
/*
* Do not attempt to resize windows which have no pane, they must be on
* the way to destruction.
*/
if (w->active == NULL)
return;
log_debug("%s: @%u is %u,%u", __func__, w->id, w->sx, w->sy);
/*
* Type is manual, smallest, largest, latest. Current is the
* aggressive-resize option (do not resize based on clients where the
* window is not the current window).
*/
type = options_get_number(w->options, "window-size");
current = options_get_number(w->options, "aggressive-resize");
changed = 1;
switch (type) {
case WINDOW_SIZE_LARGEST:
sx = sy = 0;
TAILQ_FOREACH(c, &clients, entry) {
if (ignore_client_size(c))
continue;
s = c->session;
/* Look for a suitable client and get the new size. */
changed = clients_calculate_size(type, current, NULL, w,
recalculate_size_skip_client, &sx, &sy, &xpixel, &ypixel);
if (current)
has = (s->curw->window == w);
else
has = session_has(s, w);
if (!has)
continue;
cx = c->tty.sx;
cy = c->tty.sy - status_line_size(c);
if (cx > sx)
sx = cx;
if (cy > sy)
sy = cy;
if (c->tty.xpixel > xpixel && c->tty.ypixel > ypixel) {
xpixel = c->tty.xpixel;
ypixel = c->tty.ypixel;
}
}
if (sx == 0 || sy == 0)
changed = 0;
break;
case WINDOW_SIZE_SMALLEST:
sx = sy = UINT_MAX;
TAILQ_FOREACH(c, &clients, entry) {
if (ignore_client_size(c))
continue;
s = c->session;
if (current)
has = (s->curw->window == w);
else
has = session_has(s, w);
if (!has)
continue;
cx = c->tty.sx;
cy = c->tty.sy - status_line_size(c);
if (cx < sx)
sx = cx;
if (cy < sy)
sy = cy;
if (c->tty.xpixel > xpixel && c->tty.ypixel > ypixel) {
xpixel = c->tty.xpixel;
ypixel = c->tty.ypixel;
}
}
if (sx == UINT_MAX || sy == UINT_MAX)
changed = 0;
break;
case WINDOW_SIZE_LATEST:
n = 0;
TAILQ_FOREACH(c, &clients, entry) {
if (!ignore_client_size(c) &&
session_has(c->session, w)) {
if (++n > 1)
break;
}
}
sx = sy = UINT_MAX;
TAILQ_FOREACH(c, &clients, entry) {
if (ignore_client_size(c))
continue;
if (n > 1 && c != w->latest)
continue;
s = c->session;
if (current)
has = (s->curw->window == w);
else
has = session_has(s, w);
if (!has)
continue;
cx = c->tty.sx;
cy = c->tty.sy - status_line_size(c);
if (cx < sx)
sx = cx;
if (cy < sy)
sy = cy;
if (c->tty.xpixel > xpixel && c->tty.ypixel > ypixel) {
xpixel = c->tty.xpixel;
ypixel = c->tty.ypixel;
}
}
if (sx == UINT_MAX || sy == UINT_MAX)
changed = 0;
break;
case WINDOW_SIZE_MANUAL:
changed = 0;
break;
}
/*
* Make sure the size has actually changed. If the window has already
* got a resize scheduled, then use the new size; otherwise the old.
*/
if (w->flags & WINDOW_RESIZE) {
if (!now && changed && w->new_sx == sx && w->new_sy == sy)
changed = 0;
@ -355,10 +304,20 @@ recalculate_size(struct window *w, int now)
changed = 0;
}
/*
* If the size hasn't changed, update the window offset but not the
* size.
*/
if (!changed) {
tty_update_window_offset(w);
return;
}
/*
* If the now flag is set or if the window is sized manually, change
* the size immediately. Otherwise set the flag and it will be done
* later.
*/
log_debug("%s: @%u new size %u,%u", __func__, w->id, sx, sy);
if (now || type == WINDOW_SIZE_MANUAL)
resize_window(w, sx, sy, xpixel, ypixel);

View File

@ -78,6 +78,7 @@ screen_init(struct screen *s, u_int sx, u_int sy, u_int hlimit)
s->title = xstrdup("");
s->titles = NULL;
s->path = NULL;
s->cstyle = 0;
s->ccolour = xstrdup("");
@ -120,6 +121,7 @@ screen_free(struct screen *s)
{
free(s->sel);
free(s->tabs);
free(s->path);
free(s->title);
free(s->ccolour);

View File

@ -1588,10 +1588,6 @@ server_client_check_pane_focus(struct window_pane *wp)
if (wp->window->active != wp)
goto not_focused;
/* If we're in a mode, we're not focused. */
if (wp->screen != &wp->base)
goto not_focused;
/*
* If our window is the current window in any focused clients with an
* attached session, we're focused.
@ -1660,8 +1656,6 @@ server_client_reset_state(struct client *c)
s = wp->screen;
if (s != NULL)
mode = s->mode;
if (c->prompt_string != NULL || c->message_string != NULL)
mode &= ~MODE_CURSOR;
log_debug("%s: client %s mode %x", __func__, c->name, mode);
/* Reset region and margin. */
@ -1693,8 +1687,8 @@ server_client_reset_state(struct client *c)
* mode.
*/
if (options_get_number(oo, "mouse")) {
mode &= ~ALL_MOUSE_MODES;
if (c->overlay_draw == NULL) {
mode &= ~ALL_MOUSE_MODES;
TAILQ_FOREACH(loop, &w->panes, entry) {
if (loop->screen->mode & MODE_MOUSE_ALL)
mode |= MODE_MOUSE_ALL;
@ -1985,6 +1979,7 @@ server_client_dispatch(struct imsg *imsg, void *arg)
switch (imsg->hdr.type) {
case MSG_IDENTIFY_FEATURES:
case MSG_IDENTIFY_FLAGS:
case MSG_IDENTIFY_LONGFLAGS:
case MSG_IDENTIFY_TERM:
case MSG_IDENTIFY_TTYNAME:
case MSG_IDENTIFY_CWD:
@ -2028,7 +2023,7 @@ server_client_dispatch(struct imsg *imsg, void *arg)
break;
c->flags &= ~CLIENT_SUSPENDED;
if (c->fd == -1) /* exited in the meantime */
if (c->fd == -1 || c->session == NULL) /* exited already */
break;
s = c->session;
@ -2143,6 +2138,7 @@ server_client_dispatch_identify(struct client *c, struct imsg *imsg)
const char *data, *home;
size_t datalen;
int flags, feat;
uint64_t longflags;
char *name;
if (c->flags & CLIENT_IDENTIFIED)
@ -2167,6 +2163,14 @@ server_client_dispatch_identify(struct client *c, struct imsg *imsg)
c->flags |= flags;
log_debug("client %p IDENTIFY_FLAGS %#x", c, flags);
break;
case MSG_IDENTIFY_LONGFLAGS:
if (datalen != sizeof longflags)
fatalx("bad MSG_IDENTIFY_LONGFLAGS size");
memcpy(&longflags, data, sizeof longflags);
c->flags |= longflags;
log_debug("client %p IDENTIFY_LONGFLAGS %#llx", c,
(unsigned long long)longflags);
break;
case MSG_IDENTIFY_TERM:
if (datalen == 0 || data[datalen - 1] != '\0')
fatalx("bad MSG_IDENTIFY_TERM string");

View File

@ -342,6 +342,7 @@ server_destroy_pane(struct window_pane *wp, int notify)
time(&t);
ctime_r(&t, tim);
tim[strcspn(tim, "\n")] = '\0';
if (WIFEXITED(wp->status)) {
screen_write_nputs(&ctx, -1, &gc,

View File

@ -157,7 +157,7 @@ server_start(struct tmuxproc *client, int flags, struct event_base *base,
{
int pair[2];
sigset_t set, oldset;
struct client *c;
struct client *c = NULL;
char *cause = NULL;
sigfillset(&set);
@ -223,9 +223,11 @@ server_start(struct tmuxproc *client, int flags, struct event_base *base,
}
if (cause != NULL) {
cmdq_append(c, cmdq_get_error(cause));
if (c != NULL) {
cmdq_append(c, cmdq_get_error(cause));
c->flags |= CLIENT_EXIT;
}
free(cause);
c->flags |= CLIENT_EXIT;
}
server_add_accept(0);

View File

@ -423,11 +423,11 @@ status_redraw(struct client *c)
/* Set a status line message. */
void
status_message_set(struct client *c, int ignore_styles, const char *fmt, ...)
status_message_set(struct client *c, int delay, int ignore_styles,
const char *fmt, ...)
{
struct timeval tv;
va_list ap;
int delay;
status_message_clear(c);
status_push_screen(c);
@ -439,7 +439,12 @@ status_message_set(struct client *c, int ignore_styles, const char *fmt, ...)
server_add_message("%s message: %s", c->name, c->message_string);
delay = options_get_number(c->session->options, "display-time");
/*
* With delay -1, the display-time option is used; zero means wait for
* key press; more than zero is the actual delay time in milliseconds.
*/
if (delay == -1)
delay = options_get_number(c->session->options, "display-time");
if (delay > 0) {
tv.tv_sec = delay / 1000;
tv.tv_usec = (delay % 1000) * 1000L;
@ -447,6 +452,7 @@ status_message_set(struct client *c, int ignore_styles, const char *fmt, ...)
if (event_initialized(&c->message_timer))
evtimer_del(&c->message_timer);
evtimer_set(&c->message_timer, status_message_callback, c);
evtimer_add(&c->message_timer, &tv);
}

89
tmux.1
View File

@ -1003,7 +1003,7 @@ wait for an empty line input before exiting in control mode
.El
.Pp
A leading
.Ql !
.Ql \&!
turns a flag off if the client is already attached.
.Fl r
is an alias for
@ -1407,7 +1407,7 @@ and
.Fl T
show debugging information about jobs and terminals.
.It Xo Ic source-file
.Op Fl nqv
.Op Fl Fnqv
.Ar path
.Ar ...
.Xc
@ -1418,6 +1418,11 @@ Execute commands from one or more files specified by
.Xr glob 7
patterns).
If
.Fl F
is present, then
.Ar path
is expanded as a format.
If
.Fl q
is given, no error will be returned if
.Ar path
@ -1853,7 +1858,7 @@ The
.Fl P
option prints information about the new window after it has been created.
By default, it uses the format
.Ql #{session_name}:#{window_index}
.Ql #{session_name}:#{window_index}.#{pane_index}
but a different format may be specified with
.Fl F .
.It Xo Ic capture-pane
@ -3034,7 +3039,7 @@ Send the prefix key, or with
.Fl 2
the secondary prefix key, to a window as if it was pressed.
.It Xo Ic unbind-key
.Op Fl an
.Op Fl anq
.Op Fl T Ar key-table
.Ar key
.Xc
@ -3049,6 +3054,9 @@ are the same as for
If
.Fl a
is present, all key bindings are removed.
The
.Fl q
option prevents errors being returned.
.El
.Sh OPTIONS
The appearance and behaviour of
@ -3159,6 +3167,9 @@ flag unsets an option, so a session inherits the option from the global
options (or with
.Fl g ,
restores a global option to the default).
.Ar value
depends on the option and may be a number, a string, or a flag (on, off, or
omitted to toggle).
.Pp
The
.Fl o
@ -3230,9 +3241,6 @@ 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:
@ -4536,7 +4544,7 @@ multiplication
.Ql * ,
division
.Ql / ,
and modulus
modulus
.Ql m
or
.Ql %
@ -4545,7 +4553,15 @@ or
must be escaped as
.Ql %%
in formats which are also expanded by
.Xr strftime 3 ) .
.Xr strftime 3 )
and numeric comparison operators
.Ql == ,
.Ql != ,
.Ql < ,
.Ql <= ,
.Ql >
and
.Ql >= .
For example,
.Ql #{e|*|f|4:5.5,3}
multiplies 5.5 by 3 for a result with four decimal places and
@ -4574,6 +4590,9 @@ pads the string to a given width, for example
.Ql #{p10:pane_title}
will result in a width of at least 10 characters.
A positive width pads on the left, a negative on the right.
.Ql n
expands to the length of the variable, for example
.Ql #{n:window_name} .
.Pp
Prefixing a time variable with
.Ql t:\&
@ -4770,6 +4789,7 @@ The following variables are available, where appropriate:
.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 "1 if input to pane is disabled"
.It Li "pane_last" Ta "" Ta "1 if last pane"
.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"
@ -5098,7 +5118,7 @@ section).
Commands to alter and view the environment are:
.Bl -tag -width Ds
.It Xo Ic set-environment
.Op Fl hgru
.Op Fl Fhgru
.Op Fl t Ar target-session
.Ar name Op Ar value
.Xc
@ -5109,6 +5129,11 @@ If
is used, the change is made in the global environment; otherwise, it is applied
to the session environment for
.Ar target-session .
If
.Fl F
is present, then
.Ar value
is expanded as a format.
The
.Fl u
flag unsets a variable.
@ -5314,6 +5339,7 @@ option.
This command works only from inside
.Nm .
.It Xo Ic display-menu
.Op Fl O
.Op Fl c Ar target-client
.Op Fl t Ar target-pane
.Op Fl T Ar title
@ -5365,8 +5391,14 @@ Both may be a row or column number, or one of the following special values:
Each menu consists of items followed by a key shortcut shown in brackets.
If the menu is too large to fit on the terminal, it is not displayed.
Pressing the key shortcut chooses the corresponding item.
If the mouse is enabled and the menu is opened from a mouse key binding, releasing
the mouse button with an item selected will choose that item.
If the mouse is enabled and the menu is opened from a mouse key binding,
releasing the mouse button with an item selected chooses that item and
releasing the mouse button without an item selected closes the menu.
.Fl O
changes this behaviour so that the menu does not close when the mouse button is
released without an item selected the menu is not closed and a mouse button
must be clicked to choose an item.
.Pp
The following keys are also available:
.Bl -column "Key" "Function" -offset indent
.It Sy "Key" Ta Sy "Function"
@ -5378,6 +5410,7 @@ The following keys are also available:
.It Xo Ic display-message
.Op Fl aIpv
.Op Fl c Ar target-client
.Op Fl d Ar delay
.Op Fl t Ar target-pane
.Op Ar message
.Xc
@ -5387,7 +5420,14 @@ If
.Fl p
is given, the output is printed to stdout, otherwise it is displayed in the
.Ar target-client
status line.
status line for up to
.Ar delay
milliseconds.
If
.Ar delay
is not given, the
.Ic message-time
option is used; a delay of zero waits for a key press.
The format of
.Ar message
is described in the
@ -5629,12 +5669,21 @@ See the
.Sx FORMATS
section.
.It Xo Ic load-buffer
.Op Fl w
.Op Fl b Ar buffer-name
.Op Fl t Ar target-client
.Ar path
.Xc
.D1 (alias: Ic loadb )
Load the contents of the specified paste buffer from
.Ar path .
If
.Fl w
is given, the buffer is also sent to the clipboard for
.Ar target-client
using the
.Xr xterm 1
escape sequence, if possible.
.It Xo Ic paste-buffer
.Op Fl dpr
.Op Fl b Ar buffer-name
@ -5671,14 +5720,22 @@ The
.Fl a
option appends to rather than overwriting the file.
.It Xo Ic set-buffer
.Op Fl a
.Op Fl aw
.Op Fl b Ar buffer-name
.Op Fl t Ar target-client
.Op Fl n Ar new-buffer-name
.Ar data
.Xc
.D1 (alias: Ic setb )
Set the contents of the specified buffer to
.Ar data .
If
.Fl w
is given, the buffer is also sent to the clipboard for
.Ar target-client
using the
.Xr xterm 1
escape sequence, if possible.
The
.Fl a
option appends to rather than overwriting the buffer.
@ -5855,8 +5912,8 @@ 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.
.It Em \&Setulc
Set the underscore colour.
.It Em \&Setulc , \&ol
Set the underscore colour or reset to the default.
The argument is (red * 65536) + (green * 256) + blue where each is between 0
and 255.
.It Em \&Ss , Se

4
tmux.c
View File

@ -322,8 +322,8 @@ main(int argc, char **argv)
char *path = NULL, *label = NULL;
char *cause, **var;
const char *s, *shell, *cwd;
int opt, flags = 0, keys;
int feat = 0;
int opt, keys, feat = 0;
uint64_t flags = 0;
const struct options_table_entry *oe;
if (setlocale(LC_CTYPE, "en_US.UTF-8") == NULL &&

19
tmux.h
View File

@ -450,6 +450,7 @@ enum tty_code_code {
TTYC_KUP6,
TTYC_KUP7,
TTYC_MS,
TTYC_OL,
TTYC_OP,
TTYC_REV,
TTYC_RGB,
@ -461,6 +462,7 @@ enum tty_code_code {
TTYC_SE,
TTYC_SETAB,
TTYC_SETAF,
TTYC_SETAL,
TTYC_SETRGBB,
TTYC_SETRGBF,
TTYC_SETULC,
@ -498,6 +500,7 @@ enum msgtype {
MSG_IDENTIFY_CWD,
MSG_IDENTIFY_FEATURES,
MSG_IDENTIFY_STDOUT,
MSG_IDENTIFY_LONGFLAGS,
MSG_COMMAND = 200,
MSG_DETACH,
@ -1790,6 +1793,7 @@ enum options_table_type {
struct options_table_entry {
const char *name;
const char *alternative_name;
enum options_table_type type;
int scope;
int flags;
@ -1809,6 +1813,11 @@ struct options_table_entry {
const char *unit;
};
struct options_name_map {
const char *from;
const char *to;
};
/* Common command usages. */
#define CMD_TARGET_PANE_USAGE "[-t target-pane]"
#define CMD_TARGET_WINDOW_USAGE "[-t target-window]"
@ -2041,7 +2050,8 @@ int options_remove_or_default(struct options_entry *, int,
char **);
/* options-table.c */
extern const struct options_table_entry options_table[];
extern const struct options_table_entry options_table[];
extern const struct options_name_map options_other_names[];
/* job.c */
typedef void (*job_update_cb) (struct job *);
@ -2121,6 +2131,7 @@ int tty_open(struct tty *, char **);
void tty_close(struct tty *);
void tty_free(struct tty *);
void tty_update_features(struct tty *);
void tty_set_selection(struct tty *, const char *, size_t);
void tty_write(void (*)(struct tty *, const struct tty_ctx *),
struct tty_ctx *);
void tty_cmd_alignmenttest(struct tty *, const struct tty_ctx *);
@ -2325,7 +2336,7 @@ void printflike(2, 3) cmdq_error(struct cmdq_item *, const char *, ...);
void cmd_wait_for_flush(void);
/* client.c */
int client_main(struct event_base *, int, char **, int, int);
int client_main(struct event_base *, int, char **, uint64_t, int);
/* key-bindings.c */
struct key_table *key_bindings_get_table(const char *, int);
@ -2452,8 +2463,7 @@ struct style_range *status_get_range(struct client *, u_int, u_int);
void status_init(struct client *);
void status_free(struct client *);
int status_redraw(struct client *);
void printflike(3, 4) status_message_set(struct client *, int, const char *,
...);
void status_message_set(struct client *, int, int, const char *, ...);
void status_message_clear(struct client *);
int status_message_redraw(struct client *);
void status_prompt_set(struct client *, struct cmd_find_state *,
@ -2971,6 +2981,7 @@ __dead void printflike(1, 2) fatalx(const char *, ...);
/* menu.c */
#define MENU_NOMOUSE 0x1
#define MENU_TAB 0x2
#define MENU_STAYOPEN 0x4
struct menu *menu_create(const char *);
void menu_add_items(struct menu *, const struct menu_item *,
struct cmdq_item *, struct client *,

View File

@ -112,6 +112,7 @@ static const struct tty_feature tty_feature_overline = {
static const char *tty_feature_usstyle_capabilities[] = {
"Smulx=\\E[4::%p1%dm",
"Setulc=\\E[58::2::%p1%{65536}%/%d::%p1%{256}%/%{255}%&%d::%p1%{255}%&%d%;m",
"ol=\\E[59m",
NULL
};
static const struct tty_feature tty_feature_usstyle = {
@ -336,7 +337,7 @@ tty_default_features(int *feat, const char *name, u_int version)
"256,RGB,bpaste,clipboard,strikethrough,title"
{ .name = "mintty",
.features = TTY_FEATURES_BASE_MODERN_XTERM
",ccolour,cstyle,extkeys,margins,overline"
",ccolour,cstyle,extkeys,margins,overline,usstyle"
},
{ .name = "tmux",
.features = TTY_FEATURES_BASE_MODERN_XTERM

View File

@ -248,6 +248,7 @@ static const struct tty_term_code_entry tty_term_codes[] = {
[TTYC_KUP6] = { TTYCODE_STRING, "kUP6" },
[TTYC_KUP7] = { TTYCODE_STRING, "kUP7" },
[TTYC_MS] = { TTYCODE_STRING, "Ms" },
[TTYC_OL] = { TTYCODE_STRING, "ol" },
[TTYC_OP] = { TTYCODE_STRING, "op" },
[TTYC_REV] = { TTYCODE_STRING, "rev" },
[TTYC_RGB] = { TTYCODE_FLAG, "RGB" },
@ -258,6 +259,7 @@ static const struct tty_term_code_entry tty_term_codes[] = {
[TTYC_RMKX] = { TTYCODE_STRING, "rmkx" },
[TTYC_SETAB] = { TTYCODE_STRING, "setab" },
[TTYC_SETAF] = { TTYCODE_STRING, "setaf" },
[TTYC_SETAL] = { TTYCODE_STRING, "setal" },
[TTYC_SETRGBB] = { TTYCODE_STRING, "setrgbb" },
[TTYC_SETRGBF] = { TTYCODE_STRING, "setrgbf" },
[TTYC_SETULC] = { TTYCODE_STRING, "Setulc" },
@ -305,6 +307,8 @@ tty_term_strip(const char *s)
ptr++;
if (*ptr == '>')
ptr++;
if (*ptr == '\0')
break;
}
buf[len++] = *ptr;

54
tty.c
View File

@ -1896,19 +1896,27 @@ tty_cmd_cells(struct tty *tty, const struct tty_ctx *ctx)
void
tty_cmd_setselection(struct tty *tty, const struct tty_ctx *ctx)
{
char *buf;
size_t off;
tty_set_selection(tty, ctx->ptr, ctx->num);
}
void
tty_set_selection(struct tty *tty, const char *buf, size_t len)
{
char *encoded;
size_t size;
if (~tty->flags & TTY_STARTED)
return;
if (!tty_term_has(tty->term, TTYC_MS))
return;
off = 4 * ((ctx->num + 2) / 3) + 1; /* storage for base64 */
buf = xmalloc(off);
size = 4 * ((len + 2) / 3) + 1; /* storage for base64 */
encoded = xmalloc(size);
b64_ntop(ctx->ptr, ctx->num, buf, off);
tty_putcode_ptr2(tty, TTYC_MS, "", buf);
b64_ntop(buf, len, encoded, size);
tty_putcode_ptr2(tty, TTYC_MS, "", encoded);
free(buf);
free(encoded);
}
void
@ -2535,7 +2543,7 @@ tty_colours_fg(struct tty *tty, const struct grid_cell *gc)
/* Is this a 24-bit or 256-colour colour? */
if (gc->fg & COLOUR_FLAG_RGB || gc->fg & COLOUR_FLAG_256) {
if (tty_try_colour(tty, gc->fg, "38") == 0)
goto save_fg;
goto save;
/* Should not get here, already converted in tty_check_fg. */
return;
}
@ -2547,13 +2555,13 @@ tty_colours_fg(struct tty *tty, const struct grid_cell *gc)
tty_puts(tty, s);
} else
tty_putcode1(tty, TTYC_SETAF, gc->fg - 90 + 8);
goto save_fg;
goto save;
}
/* Otherwise set the foreground colour. */
tty_putcode1(tty, TTYC_SETAF, gc->fg);
save_fg:
save:
/* Save the new values in the terminal current cell. */
tc->fg = gc->fg;
}
@ -2567,7 +2575,7 @@ tty_colours_bg(struct tty *tty, const struct grid_cell *gc)
/* Is this a 24-bit or 256-colour colour? */
if (gc->bg & COLOUR_FLAG_RGB || gc->bg & COLOUR_FLAG_256) {
if (tty_try_colour(tty, gc->bg, "48") == 0)
goto save_bg;
goto save;
/* Should not get here, already converted in tty_check_bg. */
return;
}
@ -2579,13 +2587,13 @@ tty_colours_bg(struct tty *tty, const struct grid_cell *gc)
tty_puts(tty, s);
} else
tty_putcode1(tty, TTYC_SETAB, gc->bg - 90 + 8);
goto save_bg;
goto save;
}
/* Otherwise set the background colour. */
tty_putcode1(tty, TTYC_SETAB, gc->bg);
save_bg:
save:
/* Save the new values in the terminal current cell. */
tc->bg = gc->bg;
}
@ -2597,20 +2605,34 @@ tty_colours_us(struct tty *tty, const struct grid_cell *gc)
u_int c;
u_char r, g, b;
/* Clear underline colour. */
if (gc->us == 0) {
tty_putcode(tty, TTYC_OL);
goto save;
}
/* 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"
* Setulc and setal 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);
/*
* Write the colour. Only use setal if the RGB flag is set because the
* non-RGB version may be wrong.
*/
if (tty_term_has(tty->term, TTYC_SETULC))
tty_putcode1(tty, TTYC_SETULC, c);
else if (tty_term_has(tty->term, TTYC_SETAL) &&
tty_term_has(tty->term, TTYC_RGB))
tty_putcode1(tty, TTYC_SETAL, c);
save:
/* Save the new values in the terminal current cell. */
tc->us = gc->us;
}

View File

@ -72,14 +72,13 @@ static int window_copy_search_marks(struct window_mode_entry *,
struct screen *, int, int);
static void window_copy_clear_marks(struct window_mode_entry *);
static void window_copy_move_left(struct screen *, u_int *, u_int *, int);
static void window_copy_move_right(struct screen *, u_int *, u_int *, int);
static int window_copy_is_lowercase(const char *);
static int window_copy_search_jump(struct window_mode_entry *,
struct grid *, struct grid *, u_int, u_int, u_int, int, int,
int, int);
static int window_copy_search(struct window_mode_entry *, int, int);
static int window_copy_search_up(struct window_mode_entry *, int);
static int window_copy_search_down(struct window_mode_entry *, int);
int, int, u_int *);
static int window_copy_search(struct window_mode_entry *, int, int, int);
static int window_copy_search_up(struct window_mode_entry *, int, int);
static int window_copy_search_down(struct window_mode_entry *, int, int);
static void window_copy_goto_line(struct window_mode_entry *, const char *);
static void window_copy_update_cursor(struct window_mode_entry *, u_int,
u_int);
@ -110,7 +109,7 @@ static void window_copy_cursor_back_to_indentation(
static void window_copy_cursor_end_of_line(struct window_mode_entry *);
static void window_copy_other_end(struct window_mode_entry *);
static void window_copy_cursor_left(struct window_mode_entry *);
static void window_copy_cursor_right(struct window_mode_entry *);
static void window_copy_cursor_right(struct window_mode_entry *, int);
static void window_copy_cursor_up(struct window_mode_entry *, int);
static void window_copy_cursor_down(struct window_mode_entry *, int);
static void window_copy_cursor_jump(struct window_mode_entry *);
@ -1094,7 +1093,7 @@ window_copy_cmd_cursor_right(struct window_copy_cmd_state *cs)
u_int np = wme->prefix;
for (; np != 0; np--)
window_copy_cursor_right(wme);
window_copy_cursor_right(wme, 0);
return (WINDOW_COPY_CMD_NOTHING);
}
@ -1685,10 +1684,10 @@ window_copy_cmd_search_again(struct window_copy_cmd_state *cs)
if (data->searchtype == WINDOW_COPY_SEARCHUP) {
for (; np != 0; np--)
window_copy_search_up(wme, data->searchregex);
window_copy_search_up(wme, data->searchregex, 1);
} else if (data->searchtype == WINDOW_COPY_SEARCHDOWN) {
for (; np != 0; np--)
window_copy_search_down(wme, data->searchregex);
window_copy_search_down(wme, data->searchregex, 1);
}
return (WINDOW_COPY_CMD_NOTHING);
}
@ -1702,10 +1701,10 @@ window_copy_cmd_search_reverse(struct window_copy_cmd_state *cs)
if (data->searchtype == WINDOW_COPY_SEARCHUP) {
for (; np != 0; np--)
window_copy_search_down(wme, data->searchregex);
window_copy_search_down(wme, data->searchregex, 1);
} else if (data->searchtype == WINDOW_COPY_SEARCHDOWN) {
for (; np != 0; np--)
window_copy_search_up(wme, data->searchregex);
window_copy_search_up(wme, data->searchregex, 1);
}
return (WINDOW_COPY_CMD_NOTHING);
}
@ -1953,7 +1952,7 @@ window_copy_cmd_search_backward(struct window_copy_cmd_state *cs)
data->searchregex = 1;
data->timeout = 0;
for (; np != 0; np--)
window_copy_search_up(wme, 1);
window_copy_search_up(wme, 1, 0);
}
return (WINDOW_COPY_CMD_NOTHING);
}
@ -1973,7 +1972,7 @@ window_copy_cmd_search_backward_text(struct window_copy_cmd_state *cs)
data->searchregex = 0;
data->timeout = 0;
for (; np != 0; np--)
window_copy_search_up(wme, 0);
window_copy_search_up(wme, 0, 0);
}
return (WINDOW_COPY_CMD_NOTHING);
}
@ -1993,7 +1992,7 @@ window_copy_cmd_search_forward(struct window_copy_cmd_state *cs)
data->searchregex = 1;
data->timeout = 0;
for (; np != 0; np--)
window_copy_search_down(wme, 1);
window_copy_search_down(wme, 1, 0);
}
return (WINDOW_COPY_CMD_NOTHING);
}
@ -2013,7 +2012,7 @@ window_copy_cmd_search_forward_text(struct window_copy_cmd_state *cs)
data->searchregex = 0;
data->timeout = 0;
for (; np != 0; np--)
window_copy_search_down(wme, 0);
window_copy_search_down(wme, 0, 0);
}
return (WINDOW_COPY_CMD_NOTHING);
}
@ -2052,7 +2051,7 @@ window_copy_cmd_search_backward_incremental(struct window_copy_cmd_state *cs)
data->searchregex = 0;
free(data->searchstr);
data->searchstr = xstrdup(argument);
if (!window_copy_search_up(wme, 0)) {
if (!window_copy_search_up(wme, 0, 1)) {
window_copy_clear_marks(wme);
return (WINDOW_COPY_CMD_REDRAW);
}
@ -2062,7 +2061,7 @@ window_copy_cmd_search_backward_incremental(struct window_copy_cmd_state *cs)
data->searchregex = 0;
free(data->searchstr);
data->searchstr = xstrdup(argument);
if (!window_copy_search_down(wme, 0)) {
if (!window_copy_search_down(wme, 0, 0)) {
window_copy_clear_marks(wme);
return (WINDOW_COPY_CMD_REDRAW);
}
@ -2105,7 +2104,7 @@ window_copy_cmd_search_forward_incremental(struct window_copy_cmd_state *cs)
data->searchregex = 0;
free(data->searchstr);
data->searchstr = xstrdup(argument);
if (!window_copy_search_down(wme, 0)) {
if (!window_copy_search_down(wme, 0, 1)) {
window_copy_clear_marks(wme);
return (WINDOW_COPY_CMD_REDRAW);
}
@ -2115,7 +2114,7 @@ window_copy_cmd_search_forward_incremental(struct window_copy_cmd_state *cs)
data->searchregex = 0;
free(data->searchstr);
data->searchstr = xstrdup(argument);
if (!window_copy_search_up(wme, 0)) {
if (!window_copy_search_up(wme, 0, 1)) {
window_copy_clear_marks(wme);
return (WINDOW_COPY_CMD_REDRAW);
}
@ -2406,8 +2405,8 @@ window_copy_search_compare(struct grid *gd, u_int px, u_int py,
}
static int
window_copy_search_lr(struct grid *gd,
struct grid *sgd, u_int *ppx, u_int py, u_int first, u_int last, int cis)
window_copy_search_lr(struct grid *gd, struct grid *sgd, u_int *ppx, u_int py,
u_int first, u_int last, int cis)
{
u_int ax, bx, px, pywrap, endline;
int matched;
@ -2817,23 +2816,6 @@ window_copy_move_left(struct screen *s, u_int *fx, u_int *fy, int wrapflag)
*fx = *fx - 1;
}
static void
window_copy_move_right(struct screen *s, u_int *fx, u_int *fy, int wrapflag)
{
if (*fx == screen_size_x(s) - 1) { /* right */
if (*fy == screen_hsize(s) + screen_size_y(s) - 1) { /* bottom */
if (wrapflag) {
*fx = 0;
*fy = 0;
}
return;
}
*fx = 0;
*fy = *fy + 1;
} else
*fx = *fx + 1;
}
static int
window_copy_is_lowercase(const char *ptr)
{
@ -2854,7 +2836,7 @@ window_copy_is_lowercase(const char *ptr)
static int
window_copy_search_jump(struct window_mode_entry *wme, struct grid *gd,
struct grid *sgd, u_int fx, u_int fy, u_int endline, int cis, int wrap,
int direction, int regex)
int direction, int regex, u_int *foundlen)
{
u_int i, px, sx, ssize = 1;
int found = 0, cflags = REG_EXTENDED;
@ -2871,6 +2853,7 @@ window_copy_search_jump(struct window_mode_entry *wme, struct grid *gd,
free(sbuf);
return (0);
}
free(sbuf);
}
if (direction) {
@ -2878,15 +2861,20 @@ window_copy_search_jump(struct window_mode_entry *wme, struct grid *gd,
if (regex) {
found = window_copy_search_lr_regex(gd,
&px, &sx, i, fx, gd->sx, &reg);
if (found)
*foundlen = sx;
} else {
found = window_copy_search_lr(gd, sgd,
&px, i, fx, gd->sx, cis);
if (found)
*foundlen = sgd->sx;
}
if (found)
break;
fx = 0;
}
} else {
*foundlen = 0;
for (i = fy + 1; endline < i; i--) {
if (regex) {
found = window_copy_search_rl_regex(gd,
@ -2902,10 +2890,8 @@ window_copy_search_jump(struct window_mode_entry *wme, struct grid *gd,
fx = gd->sx - 1;
}
}
if (regex) {
free(sbuf);
if (regex)
regfree(&reg);
}
if (found) {
window_copy_scroll_to(wme, px, i, 1);
@ -2915,7 +2901,7 @@ window_copy_search_jump(struct window_mode_entry *wme, struct grid *gd,
return (window_copy_search_jump(wme, gd, sgd,
direction ? 0 : gd->sx - 1,
direction ? 0 : gd->hsize + gd->sy - 1, fy, cis, 0,
direction, regex));
direction, regex, foundlen));
}
return (0);
}
@ -2925,7 +2911,8 @@ window_copy_search_jump(struct window_mode_entry *wme, struct grid *gd,
* down.
*/
static int
window_copy_search(struct window_mode_entry *wme, int direction, int regex)
window_copy_search(struct window_mode_entry *wme, int direction, int regex,
int again)
{
struct window_pane *wp = wme->wp;
struct window_copy_mode_data *data = wme->data;
@ -2933,7 +2920,7 @@ window_copy_search(struct window_mode_entry *wme, int direction, int regex)
struct screen_write_ctx ctx;
struct grid *gd = s->grid;
const char *str = data->searchstr;
u_int fx, fy, endline;
u_int fx, fy, endline, i, foundlen;
int wrapflag, cis, found, visible_only;
if (regex && str[strcspn(str, "^$*+()?[].\\")] == '\0')
@ -2961,18 +2948,23 @@ window_copy_search(struct window_mode_entry *wme, int direction, int regex)
wrapflag = options_get_number(wp->window->options, "wrap-search");
cis = window_copy_is_lowercase(str);
if (direction) {
window_copy_move_right(s, &fx, &fy, wrapflag);
if (direction)
endline = gd->hsize + gd->sy - 1;
} else {
window_copy_move_left(s, &fx, &fy, wrapflag);
else {
if (again)
window_copy_move_left(s, &fx, &fy, wrapflag);
endline = 0;
}
found = window_copy_search_jump(wme, gd, ss.grid, fx, fy, endline, cis,
wrapflag, direction, regex);
if (found)
wrapflag, direction, regex, &foundlen);
if (found) {
window_copy_search_marks(wme, &ss, regex, visible_only);
if (foundlen != 0) {
for (i = 0; i < foundlen; i++)
window_copy_cursor_right(wme, 1);
}
}
window_copy_redraw_screen(wme);
screen_free(&ss);
@ -2995,8 +2987,8 @@ window_copy_visible_lines(struct window_copy_mode_data *data, u_int *start,
}
static int
window_copy_search_mark_at(struct window_copy_mode_data *data, u_int px, u_int py,
u_int *at)
window_copy_search_mark_at(struct window_copy_mode_data *data, u_int px,
u_int py, u_int *at)
{
struct screen *s = data->backing;
struct grid *gd = s->grid;
@ -3049,6 +3041,7 @@ window_copy_search_marks(struct window_mode_entry *wme, struct screen *ssp,
free(sbuf);
return (0);
}
free(sbuf);
}
tstart = get_timer();
@ -3096,7 +3089,7 @@ again:
data->searchgen++;
}
px++;
px += width;
}
t = get_timer();
@ -3146,10 +3139,8 @@ again:
out:
if (ssp == &ss)
screen_free(&ss);
if (regex) {
free(sbuf);
if (regex)
regfree(&reg);
}
return (1);
}
@ -3163,15 +3154,15 @@ window_copy_clear_marks(struct window_mode_entry *wme)
}
static int
window_copy_search_up(struct window_mode_entry *wme, int regex)
window_copy_search_up(struct window_mode_entry *wme, int regex, int again)
{
return (window_copy_search(wme, 0, regex));
return (window_copy_search(wme, 0, regex, again));
}
static int
window_copy_search_down(struct window_mode_entry *wme, int regex)
window_copy_search_down(struct window_mode_entry *wme, int regex, int again)
{
return (window_copy_search(wme, 1, regex));
return (window_copy_search(wme, 1, regex, again));
}
static void
@ -3256,7 +3247,7 @@ window_copy_update_style(struct window_mode_entry *wme, u_int fx, u_int fy,
{
struct window_copy_mode_data *data = wme->data;
u_int mark, start, end, cy, cursor, current;
int inv = 0;
int inv = 0, found = 0;
if (data->showmark && fy == data->my) {
gc->attr = mkgc->attr;
@ -3282,20 +3273,28 @@ window_copy_update_style(struct window_mode_entry *wme, u_int fx, u_int fy,
return;
cy = screen_hsize(data->backing) - data->oy + data->cy;
if (window_copy_search_mark_at(data, data->cx, cy, &cursor) == 0 &&
data->searchmark[cursor] == mark) {
window_copy_match_start_end(data, cursor, &start, &end);
if (current >= start && current <= end) {
gc->attr = cgc->attr;
if (inv) {
gc->fg = cgc->bg;
gc->bg = cgc->fg;
if (window_copy_search_mark_at(data, data->cx, cy, &cursor) == 0) {
if (data->searchmark[cursor] == mark)
found = 1;
else if (cursor != 0) {
cursor--;
if (data->searchmark[cursor] == mark)
found = 1;
}
if (found) {
window_copy_match_start_end(data, cursor, &start, &end);
if (current >= start && current <= end) {
gc->attr = cgc->attr;
if (inv) {
gc->fg = cgc->bg;
gc->bg = cgc->fg;
}
else {
gc->fg = cgc->fg;
gc->bg = cgc->bg;
}
return;
}
else {
gc->fg = cgc->fg;
gc->bg = cgc->bg;
}
return;
}
}
@ -3370,7 +3369,8 @@ window_copy_write_line(struct window_mode_entry *wme,
} else if (data->searchthis == -1) {
size = xsnprintf(hdr, sizeof hdr,
"(%d%s results) [%u/%u]", data->searchcount,
data->searchmore ? "+" : "", data->oy, hsize);
data->searchmore ? "+" : "", data->oy,
hsize);
} else {
size = xsnprintf(hdr, sizeof hdr,
"(%d/%d results) [%u/%u]", data->searchthis,
@ -4142,7 +4142,7 @@ window_copy_cursor_left(struct window_mode_entry *wme)
}
static void
window_copy_cursor_right(struct window_mode_entry *wme)
window_copy_cursor_right(struct window_mode_entry *wme, int all)
{
struct window_copy_mode_data *data = wme->data;
u_int px, py, yy, cx, cy;
@ -4150,7 +4150,7 @@ window_copy_cursor_right(struct window_mode_entry *wme)
py = screen_hsize(data->backing) + data->cy - data->oy;
yy = screen_hsize(data->backing) + screen_size_y(data->backing) - 1;
if (data->screen.sel != NULL && data->rectflag)
if (all || (data->screen.sel != NULL && data->rectflag))
px = screen_size_x(&data->screen);
else
px = window_copy_find_length(wme, py);

View File

@ -380,7 +380,7 @@ window_customize_build_options(struct window_customize_modedata *data,
struct format_tree *ft, const char *filter, struct cmd_find_state *fs)
{
struct mode_tree_item *top;
struct options_entry *o, *loop;
struct options_entry *o = NULL, *loop;
const char **list = NULL, *name;
u_int size = 0, i;
enum window_customize_scope scope;
@ -1003,7 +1003,7 @@ window_customize_set_option_callback(struct client *c, void *itemdata,
fail:
*cause = toupper((u_char)*cause);
status_message_set(c, 1, "%s", cause);
status_message_set(c, -1, 1, "%s", cause);
free(cause);
return (0);
}
@ -1018,7 +1018,7 @@ window_customize_set_option(struct client *c,
struct options *oo;
struct window_customize_itemdata *new_item;
int flag, idx = item->idx;
enum window_customize_scope scope;
enum window_customize_scope scope = WINDOW_CUSTOMIZE_NONE;
u_int choice;
const char *name = item->name, *space = "";
char *prompt, *value, *text;
@ -1031,7 +1031,7 @@ window_customize_set_option(struct client *c,
return;
oe = options_table_entry(o);
if (~oe->scope & OPTIONS_TABLE_PANE)
if (oe != NULL && ~oe->scope & OPTIONS_TABLE_PANE)
pane = 0;
if (oe != NULL && (oe->flags & OPTIONS_TABLE_IS_ARRAY)) {
scope = item->scope;
@ -1209,7 +1209,7 @@ window_customize_set_command_callback(struct client *c, void *itemdata,
fail:
*error = toupper((u_char)*error);
status_message_set(c, 1, "%s", error);
status_message_set(c, -1, 1, "%s", error);
free(error);
return (0);
}