1
0
mirror of https://github.com/tmux/tmux.git synced 2025-04-10 11:08:49 +00:00

Merge branch 'master' into 3.2-rc

This commit is contained in:
Nicholas Marriott 2021-04-13 06:25:59 +01:00
commit c2048c5c65
27 changed files with 667 additions and 219 deletions

View File

@ -314,10 +314,11 @@ alerts_set_message(struct winlink *wl, const char *type, const char *option)
tty_putcode(&c->tty, TTYC_BEL);
if (visual == VISUAL_OFF)
continue;
if (c->session->curw == wl)
status_message_set(c, -1, 1, "%s in current window", type);
else {
status_message_set(c, -1, 1, "%s in window %d", type,
if (c->session->curw == wl) {
status_message_set(c, -1, 1, 0, "%s in current window",
type);
} else {
status_message_set(c, -1, 1, 0, "%s in window %d", type,
wl->idx);
}
}

20
cfg.c
View File

@ -102,6 +102,7 @@ load_cfg(const char *path, struct client *c, struct cmdq_item *item, int flags,
struct cmd_parse_input pi;
struct cmd_parse_result *pr;
struct cmdq_item *new_item0;
struct cmdq_state *state;
if (new_item != NULL)
*new_item = NULL;
@ -135,12 +136,19 @@ load_cfg(const char *path, struct client *c, struct cmdq_item *item, int flags,
return (0);
}
new_item0 = cmdq_get_command(pr->cmdlist, NULL);
if (item != NULL)
state = cmdq_copy_state(cmdq_get_state(item));
else
state = cmdq_new_state(NULL, NULL, 0);
cmdq_add_format(state, "current_file", "%s", pi.file);
new_item0 = cmdq_get_command(pr->cmdlist, state);
if (item != NULL)
new_item0 = cmdq_insert_after(item, new_item0);
else
new_item0 = cmdq_append(NULL, new_item0);
cmd_list_free(pr->cmdlist);
cmdq_free_state(state);
if (new_item != NULL)
*new_item = new_item0;
@ -155,6 +163,7 @@ load_cfg_from_buffer(const void *buf, size_t len, const char *path,
struct cmd_parse_input pi;
struct cmd_parse_result *pr;
struct cmdq_item *new_item0;
struct cmdq_state *state;
if (new_item != NULL)
*new_item = NULL;
@ -181,12 +190,19 @@ load_cfg_from_buffer(const void *buf, size_t len, const char *path,
return (0);
}
new_item0 = cmdq_get_command(pr->cmdlist, NULL);
if (item != NULL)
state = cmdq_copy_state(cmdq_get_state(item));
else
state = cmdq_new_state(NULL, NULL, 0);
cmdq_add_format(state, "current_file", "%s", pi.file);
new_item0 = cmdq_get_command(pr->cmdlist, state);
if (item != NULL)
new_item0 = cmdq_insert_after(item, new_item0);
else
new_item0 = cmdq_append(NULL, new_item0);
cmd_list_free(pr->cmdlist);
cmdq_free_state(state);
if (new_item != NULL)
*new_item = new_item0;

View File

@ -30,9 +30,9 @@ const struct cmd_entry cmd_choose_tree_entry = {
.name = "choose-tree",
.alias = NULL,
.args = { "F:Gf:NO:rst:wZ", 0, 1 },
.usage = "[-GNrswZ] [-F format] [-f filter] [-O sort-order] "
CMD_TARGET_PANE_USAGE " [template]",
.args = { "F:f:GK:NO:rst:wZ", 0, 1 },
.usage = "[-GNrswZ] [-F format] [-f filter] [-K key-format] "
"[-O sort-order] " CMD_TARGET_PANE_USAGE " [template]",
.target = { 't', CMD_FIND_PANE, 0 },
@ -44,9 +44,9 @@ const struct cmd_entry cmd_choose_client_entry = {
.name = "choose-client",
.alias = NULL,
.args = { "F:f:NO:rt:Z", 0, 1 },
.usage = "[-NrZ] [-F format] [-f filter] [-O sort-order] "
CMD_TARGET_PANE_USAGE " [template]",
.args = { "F:f:K:NO:rt:Z", 0, 1 },
.usage = "[-NrZ] [-F format] [-f filter] [-K key-format] "
"[-O sort-order] " CMD_TARGET_PANE_USAGE " [template]",
.target = { 't', CMD_FIND_PANE, 0 },
@ -58,9 +58,9 @@ const struct cmd_entry cmd_choose_buffer_entry = {
.name = "choose-buffer",
.alias = NULL,
.args = { "F:f:NO:rt:Z", 0, 1 },
.usage = "[-NrZ] [-F format] [-f filter] [-O sort-order] "
CMD_TARGET_PANE_USAGE " [template]",
.args = { "F:f:K:NO:rt:Z", 0, 1 },
.usage = "[-NrZ] [-F format] [-f filter] [-K key-format] "
"[-O sort-order] " CMD_TARGET_PANE_USAGE " [template]",
.target = { 't', CMD_FIND_PANE, 0 },

View File

@ -39,11 +39,11 @@ const struct cmd_entry cmd_display_message_entry = {
.name = "display-message",
.alias = "display",
.args = { "acd:Ipt:F:v", 0, 1 },
.usage = "[-aIpv] [-c target-client] [-d delay] [-F format] "
.args = { "acd:INpt:F:v", 0, 1 },
.usage = "[-aINpv] [-c target-client] [-d delay] [-F format] "
CMD_TARGET_PANE_USAGE " [message]",
.target = { 't', CMD_FIND_PANE, 0 },
.target = { 't', CMD_FIND_PANE, CMD_FIND_CANFAIL },
.flags = CMD_AFTERHOOK|CMD_CLIENT_CFLAG|CMD_CLIENT_CANFAIL,
.exec = cmd_display_message_exec
@ -73,6 +73,8 @@ cmd_display_message_exec(struct cmd *self, struct cmdq_item *item)
int flags;
if (args_has(args, 'I')) {
if (wp == NULL)
return (CMD_RETURN_NORMAL);
if (window_pane_start_input(wp, item, &cause) != 0) {
cmdq_error(item, "%s", cause);
free(cause);
@ -109,8 +111,10 @@ cmd_display_message_exec(struct cmd *self, struct cmdq_item *item)
*/
if (tc != NULL && tc->session == s)
c = tc;
else
else if (s != NULL)
c = cmd_find_best_client(s);
else
c = NULL;
if (args_has(args, 'v'))
flags = FORMAT_VERBOSE;
else
@ -124,10 +128,14 @@ cmd_display_message_exec(struct cmd *self, struct cmdq_item *item)
}
msg = format_expand_time(ft, template);
if (args_has(args, 'p'))
if (cmdq_get_client(item) == NULL)
cmdq_error(item, "%s", msg);
else if (args_has(args, 'p'))
cmdq_print(item, "%s", msg);
else if (tc != NULL)
status_message_set(tc, delay, 0, "%s", msg);
else if (tc != NULL) {
status_message_set(tc, delay, 0, args_has(args, 'N'), "%s",
msg);
}
free(msg);
format_free(ft);

View File

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

View File

@ -276,7 +276,7 @@ cmdq_merge_formats(struct cmdq_item *item, struct format_tree *ft)
const struct cmd_entry *entry;
if (item->cmd != NULL) {
entry = cmd_get_entry (item->cmd);
entry = cmd_get_entry(item->cmd);
format_add(ft, "command", "%s", entry->name);
}
if (item->state->formats != NULL)
@ -862,7 +862,7 @@ cmdq_error(struct cmdq_item *item, const char *fmt, ...)
c->retval = 1;
} else {
*msg = toupper((u_char) *msg);
status_message_set(c, -1, 1, "%s", msg);
status_message_set(c, -1, 1, 0, "%s", msg);
}
free(msg);

View File

@ -20,6 +20,7 @@
#include <sys/types.h>
#include <sys/wait.h>
#include <ctype.h>
#include <stdlib.h>
#include <string.h>
@ -190,8 +191,12 @@ cmd_run_shell_timer(__unused int fd, __unused short events, void* arg)
&error);
}
if (status == CMD_PARSE_ERROR) {
cmdq_error(cdata->item, "%s", error);
free(error);
if (cdata->item == NULL) {
*error = toupper((u_char)*error);
status_message_set(c, -1, 1, 0, "%s", error);
} else
cmdq_error(cdata->item, "%s", error);
free(error);
}
}

View File

@ -171,6 +171,17 @@ control_notify_client_session_changed(struct client *cc)
}
}
void
control_notify_client_detached(struct client *cc)
{
struct client *c;
TAILQ_FOREACH(c, &clients, entry) {
if (CONTROL_SHOULD_NOTIFY_CLIENT(c))
control_write(c, "%%client-detached %s", cc->name);
}
}
void
control_notify_session_renamed(struct session *s)
{

View File

@ -100,6 +100,7 @@ format_job_cmp(struct format_job *fj1, struct format_job *fj2)
#define FORMAT_QUOTE_STYLE 0x2000
#define FORMAT_WINDOW_NAME 0x4000
#define FORMAT_SESSION_NAME 0x8000
#define FORMAT_CHARACTER 0x10000
/* Limit on recursion. */
#define FORMAT_LOOP_LIMIT 10
@ -3522,7 +3523,7 @@ format_build_modifiers(struct format_expand_state *es, const char **s,
/*
* Modifiers are a ; separated list of the forms:
* l,m,C,b,d,n,t,w,q,E,T,S,W,P,<,>
* l,m,C,a,b,d,n,t,w,q,E,T,S,W,P,<,>
* =a
* =/a
* =/a/
@ -3539,7 +3540,7 @@ format_build_modifiers(struct format_expand_state *es, const char **s,
cp++;
/* Check single character modifiers with no arguments. */
if (strchr("lbdnwETSWP<>", cp[0]) != NULL &&
if (strchr("labdnwETSWP<>", cp[0]) != NULL &&
format_is_end(cp[1])) {
format_add_modifier(&list, count, cp, 1, NULL, 0);
cp++;
@ -3956,7 +3957,7 @@ format_replace_expression(struct format_modifier *mexp,
mright = (long long)mright;
}
format_log(es, "expression left side is: %.*f", prec, mleft);
format_log(es, "expression right side is: %.*f", prec, mright);
format_log(es, "expression right side is: %.*f", prec, mright);
switch (operator) {
case ADD:
@ -4016,10 +4017,10 @@ format_replace(struct format_expand_state *es, const char *key, size_t keylen,
{
struct format_tree *ft = es->ft;
struct window_pane *wp = ft->wp;
const char *errptr, *copy, *cp, *marker = NULL;
const char *errstr, *copy, *cp, *marker = NULL;
const char *time_format = NULL;
char *copy0, *condition, *found, *new;
char *value, *left, *right;
char *value, *left, *right, c;
size_t valuelen;
int modifiers = 0, limit = 0, width = 0;
int j;
@ -4063,8 +4064,8 @@ format_replace(struct format_expand_state *es, const char *key, size_t keylen,
if (fm->argc < 1)
break;
limit = strtonum(fm->argv[0], INT_MIN, INT_MAX,
&errptr);
if (errptr != NULL)
&errstr);
if (errstr != NULL)
limit = 0;
if (fm->argc >= 2 && fm->argv[1] != NULL)
marker = fm->argv[1];
@ -4073,8 +4074,8 @@ format_replace(struct format_expand_state *es, const char *key, size_t keylen,
if (fm->argc < 1)
break;
width = strtonum(fm->argv[0], INT_MIN, INT_MAX,
&errptr);
if (errptr != NULL)
&errstr);
if (errstr != NULL)
width = 0;
break;
case 'w':
@ -4088,6 +4089,9 @@ format_replace(struct format_expand_state *es, const char *key, size_t keylen,
case 'l':
modifiers |= FORMAT_LITERAL;
break;
case 'a':
modifiers |= FORMAT_CHARACTER;
break;
case 'b':
modifiers |= FORMAT_BASENAME;
break;
@ -4154,6 +4158,18 @@ format_replace(struct format_expand_state *es, const char *key, size_t keylen,
goto done;
}
/* Is this a character? */
if (modifiers & FORMAT_CHARACTER) {
new = format_expand1(es, copy);
c = strtonum(new, 32, 126, &errstr);
if (errstr != NULL)
value = xstrdup("");
else
xasprintf(&value, "%c", c);
free (new);
goto done;
}
/* Is this a loop, comparison or condition? */
if (modifiers & FORMAT_SESSIONS) {
value = format_loop_sessions(es, copy);

View File

@ -71,7 +71,7 @@ grid_reader_cursor_right(struct grid_reader *gr, int wrap, int all)
/* Move cursor back one position. */
void
grid_reader_cursor_left(struct grid_reader *gr)
grid_reader_cursor_left(struct grid_reader *gr, int wrap)
{
struct grid_cell gc;
@ -81,7 +81,9 @@ grid_reader_cursor_left(struct grid_reader *gr)
break;
gr->cx--;
}
if (gr->cx == 0 && gr->cy > 0) {
if (gr->cx == 0 && gr->cy > 0 &&
(wrap ||
grid_get_line(gr->gd, gr->cy - 1)->flags & GRID_LINE_WRAPPED)) {
grid_reader_cursor_up(gr);
grid_reader_cursor_end_of_line(gr, 0, 0);
} else if (gr->cx > 0)
@ -363,3 +365,25 @@ grid_reader_cursor_jump_back(struct grid_reader *gr, const struct utf8_data *jc)
}
return 0;
}
/* Jump back to the first non-blank character of the line. */
void
grid_reader_cursor_back_to_indentation(struct grid_reader *gr)
{
struct grid_cell gc;
u_int px, py, xx, yy;
yy = gr->gd->hsize + gr->gd->sy - 1;
grid_reader_cursor_start_of_line(gr, 1);
for (py = gr->cy; py <= yy; py++) {
xx = grid_line_length(gr->gd, py);
for (px = 0; px < xx; px++) {
grid_get_cell(gr->gd, px, py, &gc);
if (gc.data.size != 1 || *gc.data.data != ' ')
break;
}
if (~grid_get_line(gr->gd, py)->flags & GRID_LINE_WRAPPED)
break;
}
}

View File

@ -428,6 +428,14 @@ input_key_pane(struct window_pane *wp, key_code key, struct mouse_event *m)
return (input_key(wp->screen, wp->event, key));
}
static void
input_key_write(const char *from, struct bufferevent *bev, const char *data,
size_t size)
{
log_debug("%s: %.*s", from, (int)size, data);
bufferevent_write(bev, data, size);
}
/* Translate a key code into an output key sequence. */
int
input_key(struct screen *s, struct bufferevent *bev, key_code key)
@ -444,7 +452,7 @@ input_key(struct screen *s, struct bufferevent *bev, key_code key)
/* Literal keys go as themselves (can't be more than eight bits). */
if (key & KEYC_LITERAL) {
ud.data[0] = (u_char)key;
bufferevent_write(bev, &ud.data[0], 1);
input_key_write(__func__, bev, &ud.data[0], 1);
return (0);
}
@ -463,16 +471,16 @@ input_key(struct screen *s, struct bufferevent *bev, key_code key)
justkey = (key & ~(KEYC_META|KEYC_IMPLIED_META));
if (justkey <= 0x7f) {
if (key & KEYC_META)
bufferevent_write(bev, "\033", 1);
input_key_write(__func__, bev, "\033", 1);
ud.data[0] = justkey;
bufferevent_write(bev, &ud.data[0], 1);
input_key_write(__func__, bev, &ud.data[0], 1);
return (0);
}
if (justkey > 0x7f && justkey < KEYC_BASE) {
if (key & KEYC_META)
bufferevent_write(bev, "\033", 1);
input_key_write(__func__, bev, "\033", 1);
utf8_to_data(justkey, &ud);
bufferevent_write(bev, ud.data, ud.size);
input_key_write(__func__, bev, ud.data, ud.size);
return (0);
}
@ -494,8 +502,8 @@ input_key(struct screen *s, struct bufferevent *bev, key_code key)
if (ike != NULL) {
log_debug("found key 0x%llx: \"%s\"", key, ike->data);
if ((key & KEYC_META) && (~key & KEYC_IMPLIED_META))
bufferevent_write(bev, "\033", 1);
bufferevent_write(bev, ike->data, strlen(ike->data));
input_key_write(__func__, bev, "\033", 1);
input_key_write(__func__, bev, ike->data, strlen(ike->data));
return (0);
}
@ -560,7 +568,7 @@ input_key(struct screen *s, struct bufferevent *bev, key_code key)
goto missing;
}
xsnprintf(tmp, sizeof tmp, "\033[%llu;%cu", outkey, modifier);
bufferevent_write(bev, tmp, strlen(tmp));
input_key_write(__func__, bev, tmp, strlen(tmp));
return (0);
missing:
@ -656,5 +664,5 @@ input_key_mouse(struct window_pane *wp, struct mouse_event *m)
if (!input_key_get_mouse(s, m, x, y, &buf, &len))
return;
log_debug("writing mouse %.*s to %%%u", (int)len, buf, wp->id);
bufferevent_write(wp->event, buf, len);
input_key_write(__func__, wp->event, buf, len);
}

View File

@ -45,9 +45,9 @@ static const struct {
{ "F11", KEYC_F11|KEYC_IMPLIED_META },
{ "F12", KEYC_F12|KEYC_IMPLIED_META },
{ "IC", KEYC_IC|KEYC_IMPLIED_META },
{ "Insert", KEYC_IC|KEYC_IMPLIED_META },
{ "Insert", KEYC_IC|KEYC_IMPLIED_META },
{ "DC", KEYC_DC|KEYC_IMPLIED_META },
{ "Delete", KEYC_DC|KEYC_IMPLIED_META },
{ "Delete", KEYC_DC|KEYC_IMPLIED_META },
{ "Home", KEYC_HOME|KEYC_IMPLIED_META },
{ "End", KEYC_END|KEYC_IMPLIED_META },
{ "NPage", KEYC_NPAGE|KEYC_IMPLIED_META },
@ -70,7 +70,7 @@ static const struct {
{ "Right", KEYC_RIGHT|KEYC_CURSOR|KEYC_IMPLIED_META },
/* Numeric keypad. */
{ "KP/", KEYC_KP_SLASH|KEYC_KEYPAD },
{ "KP/", KEYC_KP_SLASH|KEYC_KEYPAD },
{ "KP*", KEYC_KP_STAR|KEYC_KEYPAD },
{ "KP-", KEYC_KP_MINUS|KEYC_KEYPAD },
{ "KP7", KEYC_KP_SEVEN|KEYC_KEYPAD },
@ -164,7 +164,7 @@ key_string_get_modifiers(const char **string)
key_code
key_string_lookup_string(const char *string)
{
static const char *other = "!#()+,-.0123456789:;<=>'\r\t";
static const char *other = "!#()+,-.0123456789:;<=>'\r\t\177";
key_code key, modifiers;
u_int u, i;
struct utf8_data ud, *udp;
@ -181,8 +181,8 @@ key_string_lookup_string(const char *string)
/* Is this a hexadecimal value? */
if (string[0] == '0' && string[1] == 'x') {
if (sscanf(string + 2, "%x", &u) != 1)
return (KEYC_UNKNOWN);
if (sscanf(string + 2, "%x", &u) != 1)
return (KEYC_UNKNOWN);
mlen = wctomb(m, u);
if (mlen <= 0 || mlen > MB_LEN_MAX)
return (KEYC_UNKNOWN);
@ -238,11 +238,12 @@ key_string_lookup_string(const char *string)
}
/* Convert the standard control keys. */
if (key < KEYC_BASE && (modifiers & KEYC_CTRL) && !strchr(other, key)) {
if (key < KEYC_BASE && (modifiers & KEYC_CTRL) &&
strchr(other, key) == NULL) {
if (key >= 97 && key <= 122)
key -= 96;
else if (key >= 64 && key <= 95)
key -= 64;
key -= 64;
else if (key == 32)
key = 0;
else if (key == 63)

13
log.c
View File

@ -110,15 +110,16 @@ log_vwrite(const char *msg, va_list ap)
return;
if (vasprintf(&fmt, msg, ap) == -1)
exit(1);
if (stravis(&out, fmt, VIS_OCTAL|VIS_CSTYLE|VIS_TAB|VIS_NL) == -1)
exit(1);
return;
if (stravis(&out, fmt, VIS_OCTAL|VIS_CSTYLE|VIS_TAB|VIS_NL) == -1) {
free(fmt);
return;
}
gettimeofday(&tv, NULL);
if (fprintf(log_file, "%lld.%06d %s\n", (long long)tv.tv_sec,
(int)tv.tv_usec, out) == -1)
exit(1);
fflush(log_file);
(int)tv.tv_usec, out) != -1)
fflush(log_file);
free(out);
free(fmt);

View File

@ -46,6 +46,7 @@ struct mode_tree_data {
mode_tree_search_cb searchcb;
mode_tree_menu_cb menucb;
mode_tree_height_cb heightcb;
mode_tree_key_cb keycb;
struct mode_tree_list children;
struct mode_tree_list saved;
@ -74,6 +75,10 @@ struct mode_tree_item {
void *itemdata;
u_int line;
key_code key;
const char *keystr;
size_t keylen;
uint64_t tag;
const char *name;
const char *text;
@ -135,6 +140,7 @@ mode_tree_free_item(struct mode_tree_item *mti)
free((void *)mti->name);
free((void *)mti->text);
free((void *)mti->keystr);
free(mti);
}
@ -193,6 +199,26 @@ mode_tree_build_lines(struct mode_tree_data *mtd,
flat = 0;
if (mti->expanded)
mode_tree_build_lines(mtd, &mti->children, depth + 1);
if (mtd->keycb != NULL) {
mti->key = mtd->keycb(mtd->modedata, mti->itemdata,
mti->line);
if (mti->key == KEYC_UNKNOWN)
mti->key = KEYC_NONE;
} else if (mti->line < 10)
mti->key = '0' + mti->line;
else if (mti->line < 36)
mti->key = KEYC_META|('a' + mti->line - 10);
else
mti->key = KEYC_NONE;
if (mti->key != KEYC_NONE) {
mti->keystr = xstrdup(key_string_lookup_key(mti->key,
0));
mti->keylen = strlen(mti->keystr);
} else {
mti->keystr = NULL;
mti->keylen = 0;
}
}
TAILQ_FOREACH(mti, mtl, entry) {
for (i = 0; i < mtd->line_size; i++) {
@ -363,7 +389,7 @@ struct mode_tree_data *
mode_tree_start(struct window_pane *wp, struct args *args,
mode_tree_build_cb buildcb, mode_tree_draw_cb drawcb,
mode_tree_search_cb searchcb, mode_tree_menu_cb menucb,
mode_tree_height_cb heightcb, void *modedata,
mode_tree_height_cb heightcb, mode_tree_key_cb keycb, void *modedata,
const struct menu_item *menu, const char **sort_list, u_int sort_size,
struct screen **s)
{
@ -402,6 +428,7 @@ mode_tree_start(struct window_pane *wp, struct args *args,
mtd->searchcb = searchcb;
mtd->menucb = menucb;
mtd->heightcb = heightcb;
mtd->keycb = keycb;
TAILQ_INIT(&mtd->children);
@ -596,10 +623,10 @@ mode_tree_draw(struct mode_tree_data *mtd)
struct screen_write_ctx ctx;
struct grid_cell gc0, gc;
u_int w, h, i, j, sy, box_x, box_y, width;
char *text, *start, key[7];
char *text, *start, *key;
const char *tag, *symbol;
size_t size, n;
int keylen;
int keylen, pad;
if (mtd->line_size == 0)
return;
@ -614,28 +641,30 @@ mode_tree_draw(struct mode_tree_data *mtd)
screen_write_start(&ctx, s);
screen_write_clearscreen(&ctx, 8);
if (mtd->line_size > 10)
keylen = 6;
else
keylen = 4;
keylen = 0;
for (i = 0; i < mtd->line_size; i++) {
mti = mtd->line_list[i].item;
if (mti->key == KEYC_NONE)
continue;
if ((int)mti->keylen + 3 > keylen)
keylen = mti->keylen + 3;
}
for (i = 0; i < mtd->line_size; i++) {
if (i < mtd->offset)
continue;
if (i > mtd->offset + h - 1)
break;
line = &mtd->line_list[i];
mti = line->item;
screen_write_cursormove(&ctx, 0, i - mtd->offset, 0);
if (i < 10)
snprintf(key, sizeof key, "(%c) ", '0' + i);
else if (i < 36)
snprintf(key, sizeof key, "(M-%c)", 'a' + (i - 10));
pad = keylen - 2 - mti->keylen;
if (mti->key != KEYC_NONE)
xasprintf(&key, "(%s)%*s", mti->keystr, pad, "");
else
*key = '\0';
key = xstrdup("");
if (line->flat)
symbol = "";
@ -698,6 +727,7 @@ mode_tree_draw(struct mode_tree_data *mtd)
}
}
free(text);
free(key);
if (mti->tagged) {
gc.attr ^= GRID_ATTR_BRIGHT;
@ -951,7 +981,6 @@ mode_tree_key(struct mode_tree_data *mtd, struct client *c, key_code *key,
struct mode_tree_item *current, *parent, *mti;
u_int i, x, y;
int choice;
key_code tmp;
if (KEYC_IS_MOUSE(*key) && m != NULL) {
if (cmd_mouse_at(mtd->wp, m, &x, &y, 0) != 0) {
@ -993,12 +1022,11 @@ mode_tree_key(struct mode_tree_data *mtd, struct client *c, key_code *key,
current = line->item;
choice = -1;
if (*key >= '0' && *key <= '9')
choice = (*key) - '0';
else if (((*key) & KEYC_MASK_MODIFIERS) == KEYC_META) {
tmp = (*key) & KEYC_MASK_KEY;
if (tmp >= 'a' && tmp <= 'z')
choice = 10 + (tmp - 'a');
for (i = 0; i < mtd->line_size; i++) {
if (*key == mtd->line_list[i].item->key) {
choice = i;
break;
}
}
if (choice != -1) {
if ((u_int)choice > mtd->line_size - 1) {
@ -1176,7 +1204,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, 1, "%s", error);
status_message_set(c, -1, 1, 0, "%s", error);
}
free(error);
}

View File

@ -125,6 +125,8 @@ notify_callback(struct cmdq_item *item, void *data)
control_notify_window_renamed(ne->window);
if (strcmp(ne->name, "client-session-changed") == 0)
control_notify_client_session_changed(ne->client);
if (strcmp(ne->name, "client-detached") == 0)
control_notify_client_detached(ne->client);
if (strcmp(ne->name, "session-renamed") == 0)
control_notify_session_renamed(ne->session);
if (strcmp(ne->name, "session-created") == 0)

View File

@ -153,8 +153,10 @@ screen_reset_tabs(struct screen *s)
void
screen_set_cursor_style(struct screen *s, u_int style)
{
if (style <= 6)
if (style <= 6) {
s->cstyle = style;
s->mode &= ~MODE_BLINKING;
}
}
/* Set screen cursor colour. */

View File

@ -296,6 +296,9 @@ server_client_lost(struct client *c)
TAILQ_REMOVE(&clients, c, entry);
log_debug("lost client %p", c);
if (c->flags & CLIENT_ATTACHED)
notify_client("client-detached", c);
if (c->flags & CLIENT_CONTROL)
control_stop(c);
if (c->flags & CLIENT_TERMINAL)
@ -1305,7 +1308,11 @@ server_client_handle_key(struct client *c, struct key_event *event)
* immediately rather than queued.
*/
if (~c->flags & CLIENT_READONLY) {
status_message_clear(c);
if (c->message_string != NULL) {
if (c->message_ignore_keys)
return (0);
status_message_clear(c);
}
if (c->overlay_key != NULL) {
switch (c->overlay_key(c, event)) {
case 0:
@ -1766,9 +1773,6 @@ server_client_check_exit(struct client *c)
if (EVBUFFER_LENGTH(cf->buffer) != 0)
return;
}
if (c->flags & CLIENT_ATTACHED)
notify_client("client-detached", c);
c->flags |= CLIENT_EXITED;
switch (c->exit_type) {

View File

@ -424,7 +424,7 @@ status_redraw(struct client *c)
/* Set a status line message. */
void
status_message_set(struct client *c, int delay, int ignore_styles,
const char *fmt, ...)
int ignore_keys, const char *fmt, ...)
{
struct timeval tv;
va_list ap;
@ -433,7 +433,6 @@ status_message_set(struct client *c, int delay, int ignore_styles,
status_push_screen(c);
va_start(ap, fmt);
c->message_ignore_styles = ignore_styles;
xvasprintf(&c->message_string, fmt, ap);
va_end(ap);
@ -456,6 +455,10 @@ status_message_set(struct client *c, int delay, int ignore_styles,
evtimer_add(&c->message_timer, &tv);
}
if (delay != 0)
c->message_ignore_keys = ignore_keys;
c->message_ignore_styles = ignore_styles;
c->tty.flags |= (TTY_NOCURSOR|TTY_FREEZE);
c->flags |= CLIENT_REDRAWSTATUS;
}

45
tmux.1
View File

@ -1985,12 +1985,17 @@ The default is to capture only the visible contents of the pane.
.Op Fl NrZ
.Op Fl F Ar format
.Op Fl f Ar filter
.Op Fl K Ar key-format
.Op Fl O Ar sort-order
.Op Fl t Ar target-pane
.Op Ar template
.Xc
Put a pane into client mode, allowing a client to be selected interactively from
a list.
Each client is shown on one line.
A shortcut key is shown on the left in brackets allowing for immediate choice,
or the list may be navigated and an item chosen or otherwise manipulated using
the keys below.
.Fl Z
zooms the pane.
The following keys may be used in client mode:
@ -2040,7 +2045,9 @@ specifies an initial filter: the filter is a format - if it evaluates to zero,
the item in the list is not shown, otherwise it is shown.
If a filter would lead to an empty list, it is ignored.
.Fl F
specifies the format for each item in the list.
specifies the format for each item in the list and
.Fl K
a format for each shortcut key; both are evaluated once for each line.
.Fl N
starts without the preview.
This command works only if at least one client is attached.
@ -2049,12 +2056,17 @@ This command works only if at least one client is attached.
.Op Fl GNrswZ
.Op Fl F Ar format
.Op Fl f Ar filter
.Op Fl K Ar key-format
.Op Fl O Ar sort-order
.Op Fl t Ar target-pane
.Op Ar template
.Xc
Put a pane into tree mode, where a session, window or pane may be chosen
interactively from a list.
interactively from a tree.
Each session, window or pane is shown on one line.
A shortcut key is shown on the left in brackets allowing for immediate choice,
or the tree may be navigated and an item chosen or otherwise manipulated using
the keys below.
.Fl s
starts with sessions collapsed and
.Fl w
@ -2113,7 +2125,9 @@ specifies an initial filter: the filter is a format - if it evaluates to zero,
the item in the list is not shown, otherwise it is shown.
If a filter would lead to an empty list, it is ignored.
.Fl F
specifies the format for each item in the tree.
specifies the format for each item in the tree and
.Fl K
a format for each shortcut key; both are evaluated once for each line.
.Fl N
starts without the preview.
.Fl G
@ -3149,8 +3163,8 @@ The appearance and behaviour of
may be modified by changing the value of various options.
There are four types of option:
.Em server options ,
.Em session options
.Em window options
.Em session options ,
.Em window options ,
and
.Em pane options .
.Pp
@ -4663,6 +4677,11 @@ For example,
multiplies 5.5 by 3 for a result with four decimal places and
.Ql #{e|%%:7,3}
returns the modulus of 7 and 3.
.Ql a
replaces a numeric argument by its ASCII equivalent, so
.Ql #{a:98}
results in
.Ql b .
.Pp
A limit may be placed on the length of the resultant string by prefixing it
by an
@ -4859,6 +4878,7 @@ The following variables are available, where appropriate:
.It Li "copy_cursor_word" Ta "" Ta "Word under cursor in copy mode"
.It Li "copy_cursor_x" Ta "" Ta "Cursor X position in copy mode"
.It Li "copy_cursor_y" Ta "" Ta "Cursor Y position in copy mode"
.It Li "current_file" Ta "" Ta "Current configuration file"
.It Li "cursor_character" Ta "" Ta "Character at cursor in pane"
.It Li "cursor_flag" Ta "" Ta "Pane cursor flag"
.It Li "cursor_x" Ta "" Ta "Cursor X position in pane"
@ -5545,7 +5565,7 @@ The following keys are also available:
.It Li "q" Ta "Exit menu"
.El
.It Xo Ic display-message
.Op Fl aIpv
.Op Fl aINpv
.Op Fl c Ar target-client
.Op Fl d Ar delay
.Op Fl t Ar target-pane
@ -5565,6 +5585,8 @@ If
is not given, the
.Ic message-time
option is used; a delay of zero waits for a key press.
.Ql N
ignores key presses and closes only after the delay expires.
The format of
.Ar message
is described in the
@ -5680,12 +5702,17 @@ The buffer commands are as follows:
.Op Fl NZr
.Op Fl F Ar format
.Op Fl f Ar filter
.Op Fl K Ar key-format
.Op Fl O Ar sort-order
.Op Fl t Ar target-pane
.Op Ar template
.Xc
Put a pane into buffer mode, where a buffer may be chosen interactively from
a list.
Each buffer is shown on one line.
A shortcut key is shown on the left in brackets allowing for immediate choice,
or the list may be navigated and an item chosen or otherwise manipulated using
the keys below.
.Fl Z
zooms the pane.
The following keys may be used in buffer mode:
@ -5733,7 +5760,9 @@ specifies an initial filter: the filter is a format - if it evaluates to zero,
the item in the list is not shown, otherwise it is shown.
If a filter would lead to an empty list, it is ignored.
.Fl F
specifies the format for each item in the list.
specifies the format for each item in the list and
.Fl K
a format for each shortcut key; both are evaluated once for each line.
.Fl N
starts without the preview.
This command works only if at least one client is attached.
@ -6107,6 +6136,8 @@ A notification will never occur inside an output block.
.Pp
The following notifications are defined:
.Bl -tag -width Ds
.It Ic %client-detached Ar client
The client has detached.
.It Ic %client-session-changed Ar client session-id name
The client is now attached to the session with ID
.Ar session-id ,

10
tmux.h
View File

@ -1688,6 +1688,7 @@ struct client {
uint64_t redraw_panes;
int message_ignore_keys;
int message_ignore_styles;
char *message_string;
struct event message_timer;
@ -2491,7 +2492,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 status_message_set(struct client *, int, int, const char *, ...);
void status_message_set(struct client *, int, 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 *,
@ -2584,7 +2585,7 @@ void grid_reader_get_cursor(struct grid_reader *, u_int *, u_int *);
u_int grid_reader_line_length(struct grid_reader *);
int grid_reader_in_set(struct grid_reader *, const char *);
void grid_reader_cursor_right(struct grid_reader *, int, int);
void grid_reader_cursor_left(struct grid_reader *);
void grid_reader_cursor_left(struct grid_reader *, int);
void grid_reader_cursor_down(struct grid_reader *);
void grid_reader_cursor_up(struct grid_reader *);
void grid_reader_cursor_start_of_line(struct grid_reader *, int);
@ -2597,6 +2598,7 @@ int grid_reader_cursor_jump(struct grid_reader *,
const struct utf8_data *);
int grid_reader_cursor_jump_back(struct grid_reader *,
const struct utf8_data *);
void grid_reader_cursor_back_to_indentation(struct grid_reader *);
/* grid-view.c */
void grid_view_get_cell(struct grid *, u_int, u_int, struct grid_cell *);
@ -2855,6 +2857,7 @@ typedef void (*mode_tree_draw_cb)(void *, void *, struct screen_write_ctx *,
typedef int (*mode_tree_search_cb)(void *, void *, const char *);
typedef void (*mode_tree_menu_cb)(void *, struct client *, key_code);
typedef u_int (*mode_tree_height_cb)(void *, u_int);
typedef key_code (*mode_tree_key_cb)(void *, void *, u_int);
typedef void (*mode_tree_each_cb)(void *, void *, struct client *, key_code);
u_int mode_tree_count_tagged(struct mode_tree_data *);
void *mode_tree_get_current(struct mode_tree_data *);
@ -2869,7 +2872,7 @@ void mode_tree_up(struct mode_tree_data *, int);
void mode_tree_down(struct mode_tree_data *, int);
struct mode_tree_data *mode_tree_start(struct window_pane *, struct args *,
mode_tree_build_cb, mode_tree_draw_cb, mode_tree_search_cb,
mode_tree_menu_cb, mode_tree_height_cb, void *,
mode_tree_menu_cb, mode_tree_height_cb, mode_tree_key_cb, void *,
const struct menu_item *, const char **, u_int, struct screen **);
void mode_tree_zoom(struct mode_tree_data *, struct args *);
void mode_tree_build(struct mode_tree_data *);
@ -2946,6 +2949,7 @@ void control_notify_window_unlinked(struct session *, struct window *);
void control_notify_window_linked(struct session *, struct window *);
void control_notify_window_renamed(struct window *);
void control_notify_client_session_changed(struct client *);
void control_notify_client_detached(struct client *);
void control_notify_session_renamed(struct session *);
void control_notify_session_created(struct session *);
void control_notify_session_closed(struct session *);

View File

@ -61,7 +61,7 @@ static int tty_keys_extended_device_attributes(struct tty *, const char *,
/* Default raw keys. */
struct tty_default_key_raw {
const char *string;
key_code key;
key_code key;
};
static const struct tty_default_key_raw tty_default_raw_keys[] = {
/* Application escape. */
@ -262,7 +262,7 @@ static const key_code tty_default_xterm_modifiers[] = {
*/
struct tty_default_key_code {
enum tty_code_code code;
key_code key;
key_code key;
};
static const struct tty_default_key_code tty_default_code_keys[] = {
/* Function keys. */
@ -420,7 +420,7 @@ tty_keys_add(struct tty *tty, const char *s, key_code key)
{
struct tty_key *tk;
size_t size;
const char *keystr;
const char *keystr;
keystr = key_string_lookup_key(key, 1);
if ((tk = tty_keys_find(tty, s, strlen(s), &size)) == NULL) {
@ -477,7 +477,7 @@ tty_keys_build(struct tty *tty)
const struct tty_default_key_raw *tdkr;
const struct tty_default_key_xterm *tdkx;
const struct tty_default_key_code *tdkc;
u_int i, j;
u_int i, j;
const char *s;
struct options_entry *o;
struct options_array_item *a;
@ -869,6 +869,9 @@ tty_keys_extended_key(struct tty *tty, const char *buf, size_t len,
size_t end;
u_int number, modifiers;
char tmp[64];
cc_t bspace;
key_code nkey;
key_code onlykey;
*size = 0;
@ -911,38 +914,68 @@ tty_keys_extended_key(struct tty *tty, const char *buf, size_t len,
}
*size = end + 1;
/* Store the key and modifiers. */
*key = number;
/* Store the key. */
bspace = tty->tio.c_cc[VERASE];
if (bspace != _POSIX_VDISABLE && number == bspace)
nkey = KEYC_BSPACE;
else
nkey = number;
/* Update the modifiers. */
switch (modifiers) {
case 2:
(*key) |= KEYC_SHIFT;
nkey |= KEYC_SHIFT;
break;
case 3:
(*key) |= (KEYC_META|KEYC_IMPLIED_META);
nkey |= (KEYC_META|KEYC_IMPLIED_META);
break;
case 4:
(*key) |= (KEYC_SHIFT|KEYC_META|KEYC_IMPLIED_META);
nkey |= (KEYC_SHIFT|KEYC_META|KEYC_IMPLIED_META);
break;
case 5:
(*key) |= KEYC_CTRL;
nkey |= KEYC_CTRL;
break;
case 6:
(*key) |= (KEYC_SHIFT|KEYC_CTRL);
nkey |= (KEYC_SHIFT|KEYC_CTRL);
break;
case 7:
(*key) |= (KEYC_META|KEYC_CTRL);
nkey |= (KEYC_META|KEYC_CTRL);
break;
case 8:
(*key) |= (KEYC_SHIFT|KEYC_META|KEYC_IMPLIED_META|KEYC_CTRL);
nkey |= (KEYC_SHIFT|KEYC_META|KEYC_IMPLIED_META|KEYC_CTRL);
break;
default:
*key = KEYC_NONE;
break;
}
/*
* Don't allow both KEYC_CTRL and as an implied modifier. Also convert
* C-X into C-x and so on.
*/
if (nkey & KEYC_CTRL){
onlykey = (nkey & KEYC_MASK_KEY);
if (onlykey < 32)
onlykey = (nkey & ~KEYC_CTRL);
else {
if (onlykey >= 97 && onlykey <= 122)
onlykey -= 96;
else if (onlykey >= 64 && onlykey <= 95)
onlykey -= 64;
else if (onlykey == 32)
onlykey = 0;
else if (onlykey == 63)
onlykey = 127;
onlykey |= ((nkey & KEYC_MASK_MODIFIERS) & ~KEYC_CTRL);
}
nkey = onlykey;
}
if (log_get_level() != 0) {
log_debug("%s: extended key %.*s is %llx (%s)", c->name,
(int)*size, buf, *key, key_string_lookup_key(*key, 1));
(int)*size, buf, nkey, key_string_lookup_key(nkey, 1));
}
*key = nkey;
return (0);
}

38
tty.c
View File

@ -670,19 +670,10 @@ tty_update_mode(struct tty *tty, int mode, struct screen *s)
if (changed != 0)
log_debug("%s: update mode %x to %x", c->name, tty->mode, mode);
if (changed & MODE_BLINKING) {
if (tty_term_has(tty->term, TTYC_CVVIS))
tty_putcode(tty, TTYC_CVVIS);
else
tty_putcode(tty, TTYC_CNORM);
changed |= MODE_CURSOR;
}
if (changed & MODE_CURSOR) {
if (mode & MODE_CURSOR)
tty_putcode(tty, TTYC_CNORM);
else
tty_putcode(tty, TTYC_CIVIS);
}
/*
* The cursor blinking flag can be reset by setting the cursor style, so
* set the style first.
*/
if (s != NULL && tty->cstyle != s->cstyle) {
if (tty_term_has(tty->term, TTYC_SS)) {
if (s->cstyle == 0 && tty_term_has(tty->term, TTYC_SE))
@ -691,7 +682,28 @@ tty_update_mode(struct tty *tty, int mode, struct screen *s)
tty_putcode1(tty, TTYC_SS, s->cstyle);
}
tty->cstyle = s->cstyle;
changed |= (MODE_CURSOR|MODE_BLINKING);
}
/*
* Cursor invisible (RM ?25) overrides cursor blinking (SM ?12 or RM
* 34), and we need to be careful not send cnorm after cvvis since it
* can undo it.
*/
if (changed & (MODE_CURSOR|MODE_BLINKING)) {
log_debug("%s: cursor %s, %sblinking", __func__,
(mode & MODE_CURSOR) ? "on" : "off",
(mode & MODE_BLINKING) ? "" : "not ");
if (~mode & MODE_CURSOR)
tty_putcode(tty, TTYC_CIVIS);
else if (mode & MODE_BLINKING) {
tty_putcode(tty, TTYC_CNORM);
if (tty_term_has(tty->term, TTYC_CVVIS))
tty_putcode(tty, TTYC_CVVIS);
} else
tty_putcode(tty, TTYC_CNORM);
}
if ((changed & ALL_MOUSE_MODES) &&
tty_term_has(tty->term, TTYC_KMOUS)) {
/*

View File

@ -41,6 +41,17 @@ static void window_buffer_key(struct window_mode_entry *,
#define WINDOW_BUFFER_DEFAULT_FORMAT \
"#{t/p:buffer_created}: #{buffer_sample}"
#define WINDOW_BUFFER_DEFAULT_KEY_FORMAT \
"#{?#{e|<:#{line},10}," \
"#{line}" \
"," \
"#{?#{e|<:#{line},36}," \
"M-#{a:#{e|+:97,#{e|-:#{line},10}}}" \
"," \
"" \
"}" \
"}"
static const struct menu_item window_buffer_menu_items[] = {
{ "Paste", 'p', NULL },
{ "Paste Tagged", 'P', NULL },
@ -93,6 +104,7 @@ struct window_buffer_modedata {
struct mode_tree_data *data;
char *command;
char *format;
char *key_format;
struct window_buffer_itemdata **item_list;
u_int item_size;
@ -232,7 +244,8 @@ window_buffer_draw(__unused void *modedata, void *itemdata,
while (end != pdata + psize && *end != '\n')
end++;
buf = xreallocarray(buf, 4, end - start + 1);
utf8_strvis(buf, start, end - start, VIS_OCTAL|VIS_CSTYLE|VIS_TAB);
utf8_strvis(buf, start, end - start,
VIS_OCTAL|VIS_CSTYLE|VIS_TAB);
if (*buf != '\0') {
screen_write_cursormove(ctx, cx, cy + i, 0);
screen_write_nputs(ctx, sx, &grid_default_cell, "%s",
@ -275,6 +288,41 @@ window_buffer_menu(void *modedata, struct client *c, key_code key)
window_buffer_key(wme, c, NULL, NULL, key, NULL);
}
static key_code
window_buffer_get_key(void *modedata, void *itemdata, u_int line)
{
struct window_buffer_modedata *data = modedata;
struct window_buffer_itemdata *item = itemdata;
struct format_tree *ft;
struct session *s;
struct winlink *wl;
struct window_pane *wp;
struct paste_buffer *pb;
char *expanded;
key_code key;
if (cmd_find_valid_state(&data->fs)) {
s = data->fs.s;
wl = data->fs.wl;
wp = data->fs.wp;
}
pb = paste_get_name(item->name);
if (pb == NULL)
return KEYC_NONE;
ft = format_create(NULL, NULL, FORMAT_NONE, 0);
format_defaults(ft, NULL, NULL, 0, NULL);
format_defaults(ft, NULL, s, wl, wp);
format_defaults_paste_buffer(ft, pb);
format_add(ft, "line", "%u", line);
expanded = format_expand(ft, data->key_format);
key = key_string_lookup_string(expanded);
free(expanded);
format_free(ft);
return key;
}
static struct screen *
window_buffer_init(struct window_mode_entry *wme, struct cmd_find_state *fs,
struct args *args)
@ -291,6 +339,10 @@ window_buffer_init(struct window_mode_entry *wme, struct cmd_find_state *fs,
data->format = xstrdup(WINDOW_BUFFER_DEFAULT_FORMAT);
else
data->format = xstrdup(args_get(args, 'F'));
if (args == NULL || !args_has(args, 'K'))
data->key_format = xstrdup(WINDOW_BUFFER_DEFAULT_KEY_FORMAT);
else
data->key_format = xstrdup(args_get(args, 'K'));
if (args == NULL || args->argc == 0)
data->command = xstrdup(WINDOW_BUFFER_DEFAULT_COMMAND);
else
@ -298,8 +350,8 @@ window_buffer_init(struct window_mode_entry *wme, struct cmd_find_state *fs,
data->data = mode_tree_start(wp, args, window_buffer_build,
window_buffer_draw, window_buffer_search, window_buffer_menu, NULL,
data, window_buffer_menu_items, window_buffer_sort_list,
nitems(window_buffer_sort_list), &s);
window_buffer_get_key, data, window_buffer_menu_items,
window_buffer_sort_list, nitems(window_buffer_sort_list), &s);
mode_tree_zoom(data->data, args);
mode_tree_build(data->data);
@ -324,6 +376,7 @@ window_buffer_free(struct window_mode_entry *wme)
free(data->item_list);
free(data->format);
free(data->key_format);
free(data->command);
free(data);

View File

@ -40,6 +40,17 @@ static void window_client_key(struct window_mode_entry *,
#define WINDOW_CLIENT_DEFAULT_FORMAT \
"#{t/p:client_activity}: session #{session_name}"
#define WINDOW_CLIENT_DEFAULT_KEY_FORMAT \
"#{?#{e|<:#{line},10}," \
"#{line}" \
"," \
"#{?#{e|<:#{line},36}," \
"M-#{a:#{e|+:97,#{e|-:#{line},10}}}" \
"," \
"" \
"}" \
"}"
static const struct menu_item window_client_menu_items[] = {
{ "Detach", 'd', NULL },
{ "Detach Tagged", 'D', NULL },
@ -87,6 +98,7 @@ struct window_client_modedata {
struct mode_tree_data *data;
char *format;
char *key_format;
char *command;
struct window_client_itemdata **item_list;
@ -252,6 +264,26 @@ window_client_menu(void *modedata, struct client *c, key_code key)
window_client_key(wme, c, NULL, NULL, key, NULL);
}
static key_code
window_client_get_key(void *modedata, void *itemdata, u_int line)
{
struct window_client_modedata *data = modedata;
struct window_client_itemdata *item = itemdata;
struct format_tree *ft;
char *expanded;
key_code key;
ft = format_create(NULL, NULL, FORMAT_NONE, 0);
format_defaults(ft, item->c, NULL, 0, NULL);
format_add(ft, "line", "%u", line);
expanded = format_expand(ft, data->key_format);
key = key_string_lookup_string(expanded);
free(expanded);
format_free(ft);
return key;
}
static struct screen *
window_client_init(struct window_mode_entry *wme,
__unused struct cmd_find_state *fs, struct args *args)
@ -267,15 +299,19 @@ window_client_init(struct window_mode_entry *wme,
data->format = xstrdup(WINDOW_CLIENT_DEFAULT_FORMAT);
else
data->format = xstrdup(args_get(args, 'F'));
if (args == NULL || !args_has(args, 'K'))
data->key_format = xstrdup(WINDOW_CLIENT_DEFAULT_KEY_FORMAT);
else
data->key_format = xstrdup(args_get(args, 'K'));
if (args == NULL || args->argc == 0)
data->command = xstrdup(WINDOW_CLIENT_DEFAULT_COMMAND);
else
data->command = xstrdup(args->argv[0]);
data->data = mode_tree_start(wp, args, window_client_build,
window_client_draw, NULL, window_client_menu, NULL, data,
window_client_menu_items, window_client_sort_list,
nitems(window_client_sort_list), &s);
window_client_draw, NULL, window_client_menu, NULL,
window_client_get_key, data, window_client_menu_items,
window_client_sort_list, nitems(window_client_sort_list), &s);
mode_tree_zoom(data->data, args);
mode_tree_build(data->data);
@ -300,6 +336,7 @@ window_client_free(struct window_mode_entry *wme)
free(data->item_list);
free(data->format);
free(data->key_format);
free(data->command);
free(data);

View File

@ -64,6 +64,8 @@ static int window_copy_search_rl(struct grid *, struct grid *, u_int *,
static int window_copy_last_regex(struct grid *, u_int, u_int, u_int,
u_int, u_int *, u_int *, const char *, const regex_t *,
int);
static int window_copy_search_mark_at(struct window_copy_mode_data *,
u_int, u_int, u_int *);
static char *window_copy_stringify(struct grid *, u_int, u_int, u_int,
char *, u_int *);
static void window_copy_cstrtocellpos(struct grid *, u_int, u_int *,
@ -71,16 +73,15 @@ static void window_copy_cstrtocellpos(struct grid *, u_int, u_int *,
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 int window_copy_is_lowercase(const char *);
static void window_copy_search_back_overlap(struct grid *, regex_t *,
u_int *, u_int *, u_int *, u_int);
static int window_copy_search_jump(struct window_mode_entry *,
struct grid *, struct grid *, u_int, u_int, u_int, int, 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);
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);
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);
@ -275,6 +276,7 @@ struct window_copy_mode_data {
int showmark;
int searchtype;
int searchdirection;
int searchregex;
char *searchstr;
u_char *searchmark;
@ -1718,10 +1720,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, 1);
window_copy_search_up(wme, data->searchregex);
} else if (data->searchtype == WINDOW_COPY_SEARCHDOWN) {
for (; np != 0; np--)
window_copy_search_down(wme, data->searchregex, 1);
window_copy_search_down(wme, data->searchregex);
}
return (WINDOW_COPY_CMD_NOTHING);
}
@ -1735,10 +1737,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, 1);
window_copy_search_down(wme, data->searchregex);
} else if (data->searchtype == WINDOW_COPY_SEARCHDOWN) {
for (; np != 0; np--)
window_copy_search_up(wme, data->searchregex, 1);
window_copy_search_up(wme, data->searchregex);
}
return (WINDOW_COPY_CMD_NOTHING);
}
@ -2042,7 +2044,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, 0);
window_copy_search_up(wme, 1);
}
return (WINDOW_COPY_CMD_NOTHING);
}
@ -2062,7 +2064,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, 0);
window_copy_search_up(wme, 0);
}
return (WINDOW_COPY_CMD_NOTHING);
}
@ -2082,7 +2084,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, 0);
window_copy_search_down(wme, 1);
}
return (WINDOW_COPY_CMD_NOTHING);
}
@ -2102,7 +2104,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, 0);
window_copy_search_down(wme, 0);
}
return (WINDOW_COPY_CMD_NOTHING);
}
@ -2143,7 +2145,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, 1)) {
if (!window_copy_search_up(wme, 0)) {
window_copy_clear_marks(wme);
return (WINDOW_COPY_CMD_REDRAW);
}
@ -2153,7 +2155,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, 0)) {
if (!window_copy_search_down(wme, 0)) {
window_copy_clear_marks(wme);
return (WINDOW_COPY_CMD_REDRAW);
}
@ -2198,7 +2200,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, 1)) {
if (!window_copy_search_down(wme, 0)) {
window_copy_clear_marks(wme);
return (WINDOW_COPY_CMD_REDRAW);
}
@ -2208,7 +2210,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, 1)) {
if (!window_copy_search_up(wme, 0)) {
window_copy_clear_marks(wme);
return (WINDOW_COPY_CMD_REDRAW);
}
@ -2917,6 +2919,23 @@ 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)
{
@ -2979,7 +2998,7 @@ window_copy_search_back_overlap(struct grid *gd, regex_t *preg, u_int *ppx,
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, u_int *foundlen)
int direction, int regex)
{
u_int i, px, sx, ssize = 1;
int found = 0, cflags = REG_EXTENDED;
@ -3004,20 +3023,15 @@ 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,
@ -3048,18 +3062,40 @@ 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, foundlen));
direction, regex));
}
return (0);
}
static void
window_copy_move_after_search_mark(struct window_copy_mode_data *data,
u_int *fx, u_int *fy, int wrapflag)
{
struct screen *s = data->backing;
u_int at, start;
if (window_copy_search_mark_at(data, *fx, *fy, &start) == 0 &&
data->searchmark[start] != 0) {
while (window_copy_search_mark_at(data, *fx, *fy, &at) == 0) {
if (data->searchmark[at] != data->searchmark[start])
break;
/* Stop if not wrapping and at the end of the grid. */
if (!wrapflag &&
*fx == screen_size_x(s) - 1 &&
*fy == screen_hsize(s) + screen_size_y(s) - 1)
break;
window_copy_move_right(s, fx, fy, wrapflag);
}
}
}
/*
* Search in for text searchstr. If direction is 0 then search up, otherwise
* down.
*/
static int
window_copy_search(struct window_mode_entry *wme, int direction, int regex,
int again)
window_copy_search(struct window_mode_entry *wme, int direction, int regex)
{
struct window_pane *wp = wme->wp;
struct window_copy_mode_data *data = wme->data;
@ -3067,12 +3103,15 @@ 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, i, foundlen;
int wrapflag, cis, found, visible_only;
u_int at, endline, fx, fy, start;
int cis, found, keys, visible_only;
int wrapflag;
if (regex && str[strcspn(str, "^$*+()?[].\\")] == '\0')
regex = 0;
data->searchdirection = direction;
if (data->timeout)
return (0);
@ -3097,27 +3136,94 @@ 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)
keys = options_get_number(wp->window->options, "mode-keys");
if (direction) {
/*
* Behave according to mode-keys. If it is emacs, search forward
* leaves the cursor after the match. If it is vi, the cursor
* remains at the beginning of the match, regardless of
* direction, which means that we need to start the next search
* after the term the cursor is currently on when searching
* forward.
*/
if (keys == MODEKEY_VI) {
if (data->searchmark != NULL)
window_copy_move_after_search_mark(data, &fx,
&fy, wrapflag);
else {
/*
* When there are no search marks, start the
* search after the current cursor position.
*/
window_copy_move_right(s, &fx, &fy, wrapflag);
}
}
endline = gd->hsize + gd->sy - 1;
}
else {
if (again)
window_copy_move_left(s, &fx, &fy, wrapflag);
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, &foundlen);
wrapflag, direction, regex);
if (found) {
window_copy_search_marks(wme, &ss, regex, visible_only);
if (foundlen != 0) {
/* Adjust for wrapped lines eating one right. */
i = data->cx + foundlen;
while (i > gd->sx - 1) {
i -= gd->sx;
window_copy_cursor_right(wme, 1);
fx = data->cx;
fy = screen_hsize(data->backing) - data->oy + data->cy;
/*
* When searching forward, if the cursor is not at the beginning
* of the mark, search again.
*/
if (direction &&
window_copy_search_mark_at(data, fx, fy, &at) == 0 &&
at > 0 &&
data->searchmark[at] == data->searchmark[at - 1]) {
window_copy_move_after_search_mark(data, &fx, &fy,
wrapflag);
window_copy_search_jump(wme, gd, ss.grid, fx,
fy, endline, cis, wrapflag, direction,
regex);
fx = data->cx;
fy = screen_hsize(data->backing) - data->oy + data->cy;
}
if (direction) {
/*
* When in Emacs mode, position the cursor just after
* the mark.
*/
if (keys == MODEKEY_EMACS) {
window_copy_move_after_search_mark(data, &fx,
&fy, wrapflag);
data->cx = fx;
data->cy = fy - screen_hsize(data->backing) +
data-> oy;
}
}
else {
/*
* When searching backward, position the cursor at the
* beginning of the mark.
*/
if (window_copy_search_mark_at(data, fx, fy,
&start) == 0) {
while (window_copy_search_mark_at(data, fx, fy,
&at) == 0 &&
data->searchmark[at] ==
data->searchmark[start]) {
data->cx = fx;
data->cy = fy -
screen_hsize(data->backing) +
data-> oy;
if (at == 0)
break;
window_copy_move_left(s, &fx, &fy, 0);
}
}
for (i = 0; i < foundlen; i++)
window_copy_cursor_right(wme, 1);
}
}
window_copy_redraw_screen(wme);
@ -3302,15 +3408,15 @@ window_copy_clear_marks(struct window_mode_entry *wme)
}
static int
window_copy_search_up(struct window_mode_entry *wme, int regex, int again)
window_copy_search_up(struct window_mode_entry *wme, int regex)
{
return (window_copy_search(wme, 0, regex, again));
return (window_copy_search(wme, 0, regex));
}
static int
window_copy_search_down(struct window_mode_entry *wme, int regex, int again)
window_copy_search_down(struct window_mode_entry *wme, int regex)
{
return (window_copy_search(wme, 1, regex, again));
return (window_copy_search(wme, 1, regex));
}
static void
@ -3396,9 +3502,11 @@ window_copy_update_style(struct window_mode_entry *wme, u_int fx, u_int fy,
struct grid_cell *gc, const struct grid_cell *mgc,
const struct grid_cell *cgc, const struct grid_cell *mkgc)
{
struct window_pane *wp = wme->wp;
struct window_copy_mode_data *data = wme->data;
u_int mark, start, end, cy, cursor, current;
int inv = 0, found = 0;
int keys;
if (data->showmark && fy == data->my) {
gc->attr = mkgc->attr;
@ -3425,13 +3533,16 @@ window_copy_update_style(struct window_mode_entry *wme, u_int fx, u_int fy,
cy = screen_hsize(data->backing) - data->oy + data->cy;
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)
keys = options_get_number(wp->window->options, "mode-keys");
if (cursor != 0 &&
keys == MODEKEY_EMACS &&
data->searchdirection) {
if (data->searchmark[cursor - 1] == mark) {
cursor--;
found = 1;
}
}
} else if (data->searchmark[cursor] == mark)
found = 1;
if (found) {
window_copy_match_start_end(data, cursor, &start, &end);
if (current >= start && current <= end) {
@ -4180,23 +4291,19 @@ static void
window_copy_cursor_back_to_indentation(struct window_mode_entry *wme)
{
struct window_copy_mode_data *data = wme->data;
u_int px, py, xx;
struct grid_cell gc;
struct screen *back_s = data->backing;
struct grid_reader gr;
u_int px, py, oldy, hsize;
px = 0;
py = screen_hsize(data->backing) + data->cy - data->oy;
xx = window_copy_find_length(wme, py);
px = data->cx;
hsize = screen_hsize(back_s);
py = hsize + data->cy - data->oy;
oldy = data->cy;
while (px < xx) {
grid_get_cell(data->backing->grid, px, py, &gc);
if (gc.data.size != 1 || *gc.data.data != ' ')
break;
px++;
}
window_copy_update_cursor(wme, px, data->cy);
if (window_copy_update_selection(wme, 1, 0))
window_copy_redraw_lines(wme, data->cy, 1);
grid_reader_start(&gr, back_s->grid, px, py);
grid_reader_cursor_back_to_indentation(&gr);
grid_reader_get_cursor(&gr, &px, &py);
window_copy_acquire_cursor_up(wme, hsize, data->oy, oldy, px, py);
}
static void
@ -4287,7 +4394,7 @@ window_copy_cursor_left(struct window_mode_entry *wme)
oldy = data->cy;
grid_reader_start(&gr, back_s->grid, px, py);
grid_reader_cursor_left(&gr);
grid_reader_cursor_left(&gr, 1);
grid_reader_get_cursor(&gr, &px, &py);
window_copy_acquire_cursor_up(wme, hsize, data->oy, oldy, px, py);
}
@ -4472,10 +4579,8 @@ window_copy_cursor_jump_back(struct window_mode_entry *wme)
py = hsize + data->cy - data->oy;
oldy = data->cy;
if (px > 0)
px--;
grid_reader_start(&gr, back_s->grid, px, py);
grid_reader_cursor_left(&gr, 0);
if (grid_reader_cursor_jump_back(&gr, data->jumpchar)) {
grid_reader_get_cursor(&gr, &px, &py);
window_copy_acquire_cursor_up(wme, hsize, data->oy, oldy, px,
@ -4498,7 +4603,7 @@ window_copy_cursor_jump_to(struct window_mode_entry *wme)
grid_reader_start(&gr, back_s->grid, px, py);
if (grid_reader_cursor_jump(&gr, data->jumpchar)) {
grid_reader_cursor_left(&gr);
grid_reader_cursor_left(&gr, 1);
grid_reader_get_cursor(&gr, &px, &py);
window_copy_acquire_cursor_down(wme, hsize,
screen_size_y(back_s), data->oy, oldy, px, py, 0);
@ -4518,13 +4623,9 @@ window_copy_cursor_jump_to_back(struct window_mode_entry *wme)
py = hsize + data->cy - data->oy;
oldy = data->cy;
if (px > 0)
px--;
if (px > 0)
px--;
grid_reader_start(&gr, back_s->grid, px, py);
grid_reader_cursor_left(&gr, 0);
grid_reader_cursor_left(&gr, 0);
if (grid_reader_cursor_jump_back(&gr, data->jumpchar)) {
grid_reader_cursor_right(&gr, 1, 0);
grid_reader_get_cursor(&gr, &px, &py);
@ -4577,7 +4678,7 @@ window_copy_cursor_next_word_end_pos(struct window_mode_entry *wme,
grid_reader_cursor_right(&gr, 0, 0);
grid_reader_cursor_next_word_end(&gr, separators);
if (keys == MODEKEY_VI)
grid_reader_cursor_left(&gr);
grid_reader_cursor_left(&gr, 1);
grid_reader_get_cursor(&gr, &px, &py);
*ppx = px;
*ppy = py;
@ -4607,7 +4708,7 @@ window_copy_cursor_next_word_end(struct window_mode_entry *wme,
grid_reader_cursor_right(&gr, 0, 0);
grid_reader_cursor_next_word_end(&gr, separators);
if (keys == MODEKEY_VI)
grid_reader_cursor_left(&gr);
grid_reader_cursor_left(&gr, 1);
grid_reader_get_cursor(&gr, &px, &py);
window_copy_acquire_cursor_down(wme, hsize, screen_size_y(back_s),
data->oy, oldy, px, py, no_reset);

View File

@ -890,8 +890,8 @@ window_customize_init(struct window_mode_entry *wme, struct cmd_find_state *fs,
data->data = mode_tree_start(wp, args, window_customize_build,
window_customize_draw, NULL, window_customize_menu,
window_customize_height, data, window_customize_menu_items, NULL, 0,
&s);
window_customize_height, NULL, data, window_customize_menu_items,
NULL, 0, &s);
mode_tree_zoom(data->data, args);
mode_tree_build(data->data);
@ -999,7 +999,7 @@ window_customize_set_option_callback(struct client *c, void *itemdata,
fail:
*cause = toupper((u_char)*cause);
status_message_set(c, -1, 1, "%s", cause);
status_message_set(c, -1, 1, 0, "%s", cause);
free(cause);
return (0);
}
@ -1205,7 +1205,7 @@ window_customize_set_command_callback(struct client *c, void *itemdata,
fail:
*error = toupper((u_char)*error);
status_message_set(c, -1, 1, "%s", error);
status_message_set(c, -1, 1, 0, "%s", error);
free(error);
return (0);
}

View File

@ -56,6 +56,17 @@ static void window_tree_key(struct window_mode_entry *,
"}" \
"}"
#define WINDOW_TREE_DEFAULT_KEY_FORMAT \
"#{?#{e|<:#{line},10}," \
"#{line}" \
"," \
"#{?#{e|<:#{line},36}," \
"M-#{a:#{e|+:97,#{e|-:#{line},10}}}" \
"," \
"" \
"}" \
"}"
static const struct menu_item window_tree_menu_items[] = {
{ "Select", '\r', NULL },
{ "Expand", KEYC_RIGHT, NULL },
@ -117,6 +128,7 @@ struct window_tree_modedata {
struct mode_tree_data *data;
char *format;
char *key_format;
char *command;
int squash_groups;
@ -856,6 +868,35 @@ window_tree_menu(void *modedata, struct client *c, key_code key)
window_tree_key(wme, c, NULL, NULL, key, NULL);
}
static key_code
window_tree_get_key(void *modedata, void *itemdata, u_int line)
{
struct window_tree_modedata *data = modedata;
struct window_tree_itemdata *item = itemdata;
struct format_tree *ft;
struct session *s;
struct winlink *wl;
struct window_pane *wp;
char *expanded;
key_code key;
ft = format_create(NULL, NULL, FORMAT_NONE, 0);
window_tree_pull_item(item, &s, &wl, &wp);
if (item->type == WINDOW_TREE_SESSION)
format_defaults(ft, NULL, s, NULL, NULL);
else if (item->type == WINDOW_TREE_WINDOW)
format_defaults(ft, NULL, s, wl, NULL);
else
format_defaults(ft, NULL, s, wl, wp);
format_add(ft, "line", "%u", line);
expanded = format_expand(ft, data->key_format);
key = key_string_lookup_string(expanded);
free(expanded);
format_free(ft);
return key;
}
static struct screen *
window_tree_init(struct window_mode_entry *wme, struct cmd_find_state *fs,
struct args *args)
@ -880,6 +921,10 @@ window_tree_init(struct window_mode_entry *wme, struct cmd_find_state *fs,
data->format = xstrdup(WINDOW_TREE_DEFAULT_FORMAT);
else
data->format = xstrdup(args_get(args, 'F'));
if (args == NULL || !args_has(args, 'K'))
data->key_format = xstrdup(WINDOW_TREE_DEFAULT_KEY_FORMAT);
else
data->key_format = xstrdup(args_get(args, 'K'));
if (args == NULL || args->argc == 0)
data->command = xstrdup(WINDOW_TREE_DEFAULT_COMMAND);
else
@ -887,9 +932,9 @@ window_tree_init(struct window_mode_entry *wme, struct cmd_find_state *fs,
data->squash_groups = !args_has(args, 'G');
data->data = mode_tree_start(wp, args, window_tree_build,
window_tree_draw, window_tree_search, window_tree_menu, NULL, data,
window_tree_menu_items, window_tree_sort_list,
nitems(window_tree_sort_list), &s);
window_tree_draw, window_tree_search, window_tree_menu, NULL,
window_tree_get_key, data, window_tree_menu_items,
window_tree_sort_list, nitems(window_tree_sort_list), &s);
mode_tree_zoom(data->data, args);
mode_tree_build(data->data);
@ -913,6 +958,7 @@ window_tree_destroy(struct window_tree_modedata *data)
free(data->item_list);
free(data->format);
free(data->key_format);
free(data->command);
free(data);