Home | History | Annotate | Line # | Download | only in dist
      1 /* $OpenBSD$ */
      2 
      3 /*
      4  * Copyright (c) 2020 Nicholas Marriott <nicholas.marriott (at) gmail.com>
      5  *
      6  * Permission to use, copy, modify, and distribute this software for any
      7  * purpose with or without fee is hereby granted, provided that the above
      8  * copyright notice and this permission notice appear in all copies.
      9  *
     10  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
     11  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
     12  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
     13  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
     14  * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER
     15  * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
     16  * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
     17  */
     18 
     19 #include <sys/types.h>
     20 
     21 #include <ctype.h>
     22 #include <stdlib.h>
     23 #include <string.h>
     24 
     25 #include "tmux.h"
     26 
     27 static struct screen	*window_customize_init(struct window_mode_entry *,
     28 			     struct cmd_find_state *, struct args *);
     29 static void		 window_customize_free(struct window_mode_entry *);
     30 static void		 window_customize_resize(struct window_mode_entry *,
     31 			      u_int, u_int);
     32 static void		 window_customize_key(struct window_mode_entry *,
     33 			     struct client *, struct session *,
     34 			     struct winlink *, key_code, struct mouse_event *);
     35 
     36 #define WINDOW_CUSTOMIZE_DEFAULT_FORMAT \
     37 	"#{?is_option," \
     38 		"#{?option_is_global,,#[reverse](#{option_scope})#[default] }" \
     39 		"#[ignore]" \
     40 		"#{option_value}#{?option_unit, #{option_unit},}" \
     41 	"," \
     42 		"#{key}" \
     43 	"}"
     44 
     45 static const struct menu_item window_customize_menu_items[] = {
     46 	{ "Select", '\r', NULL },
     47 	{ "Expand", KEYC_RIGHT, NULL },
     48 	{ "", KEYC_NONE, NULL },
     49 	{ "Tag", 't', NULL },
     50 	{ "Tag All", '\024', NULL },
     51 	{ "Tag None", 'T', NULL },
     52 	{ "", KEYC_NONE, NULL },
     53 	{ "Cancel", 'q', NULL },
     54 
     55 	{ NULL, KEYC_NONE, NULL }
     56 };
     57 
     58 const struct window_mode window_customize_mode = {
     59 	.name = "options-mode",
     60 	.default_format = WINDOW_CUSTOMIZE_DEFAULT_FORMAT,
     61 
     62 	.init = window_customize_init,
     63 	.free = window_customize_free,
     64 	.resize = window_customize_resize,
     65 	.key = window_customize_key,
     66 };
     67 
     68 enum window_customize_scope {
     69 	WINDOW_CUSTOMIZE_NONE,
     70 	WINDOW_CUSTOMIZE_KEY,
     71 	WINDOW_CUSTOMIZE_SERVER,
     72 	WINDOW_CUSTOMIZE_GLOBAL_SESSION,
     73 	WINDOW_CUSTOMIZE_SESSION,
     74 	WINDOW_CUSTOMIZE_GLOBAL_WINDOW,
     75 	WINDOW_CUSTOMIZE_WINDOW,
     76 	WINDOW_CUSTOMIZE_PANE
     77 };
     78 
     79 enum window_customize_change {
     80 	WINDOW_CUSTOMIZE_UNSET,
     81 	WINDOW_CUSTOMIZE_RESET,
     82 };
     83 
     84 struct window_customize_itemdata {
     85 	struct window_customize_modedata	*data;
     86 	enum window_customize_scope		 scope;
     87 
     88 	char					*table;
     89 	key_code				 key;
     90 
     91 	struct options				*oo;
     92 	char					*name;
     93 	int					 idx;
     94 };
     95 
     96 struct window_customize_modedata {
     97 	struct window_pane			 *wp;
     98 	int					  dead;
     99 	int					  references;
    100 
    101 	struct mode_tree_data			 *data;
    102 	char					 *format;
    103 	int					  hide_global;
    104 	int					  prompt_flags;
    105 
    106 	struct window_customize_itemdata	**item_list;
    107 	u_int					  item_size;
    108 
    109 	struct cmd_find_state			  fs;
    110 	enum window_customize_change		  change;
    111 };
    112 
    113 static uint64_t
    114 window_customize_get_tag(struct options_entry *o, int idx,
    115     const struct options_table_entry *oe)
    116 {
    117 	uint64_t	offset;
    118 
    119 	if (oe == NULL)
    120 		return ((uintptr_t)o);
    121 	offset = ((const char *)oe - (const char *)options_table) / sizeof *options_table;
    122 	return ((2ULL << 62)|(offset << 32)|((idx + 1) << 1)|1);
    123 }
    124 
    125 static struct options *
    126 window_customize_get_tree(enum window_customize_scope scope,
    127     struct cmd_find_state *fs)
    128 {
    129 	switch (scope) {
    130 	case WINDOW_CUSTOMIZE_NONE:
    131 	case WINDOW_CUSTOMIZE_KEY:
    132 		return (NULL);
    133 	case WINDOW_CUSTOMIZE_SERVER:
    134 		return (global_options);
    135 	case WINDOW_CUSTOMIZE_GLOBAL_SESSION:
    136 		return (global_s_options);
    137 	case WINDOW_CUSTOMIZE_SESSION:
    138 		return (fs->s->options);
    139 	case WINDOW_CUSTOMIZE_GLOBAL_WINDOW:
    140 		return (global_w_options);
    141 	case WINDOW_CUSTOMIZE_WINDOW:
    142 		return (fs->w->options);
    143 	case WINDOW_CUSTOMIZE_PANE:
    144 		return (fs->wp->options);
    145 	}
    146 	return (NULL);
    147 }
    148 
    149 static int
    150 window_customize_check_item(struct window_customize_modedata *data,
    151     struct window_customize_itemdata *item, struct cmd_find_state *fsp)
    152 {
    153 	struct cmd_find_state	fs;
    154 
    155 	if (fsp == NULL)
    156 		fsp = &fs;
    157 
    158 	if (cmd_find_valid_state(&data->fs))
    159 		cmd_find_copy_state(fsp, &data->fs);
    160 	else
    161 		cmd_find_from_pane(fsp, data->wp, 0);
    162 	return (item->oo == window_customize_get_tree(item->scope, fsp));
    163 }
    164 
    165 static int
    166 window_customize_get_key(struct window_customize_itemdata *item,
    167     struct key_table **ktp, struct key_binding **bdp)
    168 {
    169 	struct key_table	*kt;
    170 	struct key_binding	*bd;
    171 
    172 	kt = key_bindings_get_table(item->table, 0);
    173 	if (kt == NULL)
    174 		return (0);
    175 	bd = key_bindings_get(kt, item->key);
    176 	if (bd == NULL)
    177 		return (0);
    178 
    179 	if (ktp != NULL)
    180 		*ktp = kt;
    181 	if (bdp != NULL)
    182 		*bdp = bd;
    183 	return (1);
    184 }
    185 
    186 static char *
    187 window_customize_scope_text(enum window_customize_scope scope,
    188     struct cmd_find_state *fs)
    189 {
    190 	char	*s;
    191 	u_int	 idx;
    192 
    193 	switch (scope) {
    194 	case WINDOW_CUSTOMIZE_PANE:
    195 		window_pane_index(fs->wp, &idx);
    196 		xasprintf(&s, "pane %u", idx);
    197 		break;
    198 	case WINDOW_CUSTOMIZE_SESSION:
    199 		xasprintf(&s, "session %s", fs->s->name);
    200 		break;
    201 	case WINDOW_CUSTOMIZE_WINDOW:
    202 		xasprintf(&s, "window %u", fs->wl->idx);
    203 		break;
    204 	default:
    205 		s = xstrdup("");
    206 		break;
    207 	}
    208 	return (s);
    209 }
    210 
    211 static struct window_customize_itemdata *
    212 window_customize_add_item(struct window_customize_modedata *data)
    213 {
    214 	struct window_customize_itemdata	*item;
    215 
    216 	data->item_list = xreallocarray(data->item_list, data->item_size + 1,
    217 	    sizeof *data->item_list);
    218 	item = data->item_list[data->item_size++] = xcalloc(1, sizeof *item);
    219 	return (item);
    220 }
    221 
    222 static void
    223 window_customize_free_item(struct window_customize_itemdata *item)
    224 {
    225 	free(item->table);
    226 	free(item->name);
    227 	free(item);
    228 }
    229 
    230 static void
    231 window_customize_build_array(struct window_customize_modedata *data,
    232     struct mode_tree_item *top, enum window_customize_scope scope,
    233     struct options_entry *o, struct format_tree *ft)
    234 {
    235 	const struct options_table_entry	*oe = options_table_entry(o);
    236 	struct options				*oo = options_owner(o);
    237 	struct window_customize_itemdata	*item;
    238 	struct options_array_item		*ai;
    239 	char					*name, *value, *text;
    240 	u_int					 idx;
    241 	uint64_t				 tag;
    242 
    243 	ai = options_array_first(o);
    244 	while (ai != NULL) {
    245 		idx = options_array_item_index(ai);
    246 
    247 		xasprintf(&name, "%s[%u]", options_name(o), idx);
    248 		format_add(ft, "option_name", "%s", name);
    249 		value = options_to_string(o, idx, 0);
    250 		format_add(ft, "option_value", "%s", value);
    251 
    252 		item = window_customize_add_item(data);
    253 		item->scope = scope;
    254 		item->oo = oo;
    255 		item->name = xstrdup(options_name(o));
    256 		item->idx = idx;
    257 
    258 		text = format_expand(ft, data->format);
    259 		tag = window_customize_get_tag(o, idx, oe);
    260 		mode_tree_add(data->data, top, item, tag, name, text, -1);
    261 		free(text);
    262 
    263 		free(name);
    264 		free(value);
    265 
    266 		ai = options_array_next(ai);
    267 	}
    268 }
    269 
    270 static void
    271 window_customize_build_option(struct window_customize_modedata *data,
    272     struct mode_tree_item *top, enum window_customize_scope scope,
    273     struct options_entry *o, struct format_tree *ft,
    274     const char *filter, struct cmd_find_state *fs)
    275 {
    276 	const struct options_table_entry	*oe = options_table_entry(o);
    277 	struct options				*oo = options_owner(o);
    278 	const char				*name = options_name(o);
    279 	struct window_customize_itemdata	*item;
    280 	char					*text, *expanded, *value;
    281 	int					 global = 0, array = 0;
    282 	uint64_t				 tag;
    283 
    284 	if (oe != NULL && (oe->flags & OPTIONS_TABLE_IS_HOOK))
    285 		return;
    286 	if (oe != NULL && (oe->flags & OPTIONS_TABLE_IS_ARRAY))
    287 		array = 1;
    288 
    289 	if (scope == WINDOW_CUSTOMIZE_SERVER ||
    290 	    scope == WINDOW_CUSTOMIZE_GLOBAL_SESSION ||
    291 	    scope == WINDOW_CUSTOMIZE_GLOBAL_WINDOW)
    292 		global = 1;
    293 	if (data->hide_global && global)
    294 		return;
    295 
    296 	format_add(ft, "option_name", "%s", name);
    297 	format_add(ft, "option_is_global", "%d", global);
    298 	format_add(ft, "option_is_array", "%d", array);
    299 
    300 	text = window_customize_scope_text(scope, fs);
    301 	format_add(ft, "option_scope", "%s", text);
    302 	free(text);
    303 
    304 	if (oe != NULL && oe->unit != NULL)
    305 		format_add(ft, "option_unit", "%s", oe->unit);
    306 	else
    307 		format_add(ft, "option_unit", "%s", "");
    308 
    309 	if (!array) {
    310 		value = options_to_string(o, -1, 0);
    311 		format_add(ft, "option_value", "%s", value);
    312 		free(value);
    313 	}
    314 
    315 	if (filter != NULL) {
    316 		expanded = format_expand(ft, filter);
    317 		if (!format_true(expanded)) {
    318 			free(expanded);
    319 			return;
    320 		}
    321 		free(expanded);
    322 	}
    323 	item = window_customize_add_item(data);
    324 	item->oo = oo;
    325 	item->scope = scope;
    326 	item->name = xstrdup(name);
    327 	item->idx = -1;
    328 
    329 	if (array)
    330 		text = NULL;
    331 	else
    332 		text = format_expand(ft, data->format);
    333 	tag = window_customize_get_tag(o, -1, oe);
    334 	top = mode_tree_add(data->data, top, item, tag, name, text, 0);
    335 	free(text);
    336 
    337 	if (array)
    338 		window_customize_build_array(data, top, scope, o, ft);
    339 }
    340 
    341 static void
    342 window_customize_find_user_options(struct options *oo, const char ***list,
    343     u_int *size)
    344 {
    345 	struct options_entry	*o;
    346 	const char		*name;
    347 	u_int			 i;
    348 
    349 	o = options_first(oo);
    350 	while (o != NULL) {
    351 		name = options_name(o);
    352 		if (*name != '@') {
    353 			o = options_next(o);
    354 			continue;
    355 		}
    356 		for (i = 0; i < *size; i++) {
    357 			if (strcmp((*list)[i], name) == 0)
    358 				break;
    359 		}
    360 		if (i != *size) {
    361 			o = options_next(o);
    362 			continue;
    363 		}
    364 		*list = xreallocarray(*list, (*size) + 1, sizeof **list);
    365 		(*list)[(*size)++] = name;
    366 
    367 		o = options_next(o);
    368 	}
    369 }
    370 
    371 static void
    372 window_customize_build_options(struct window_customize_modedata *data,
    373     const char *title, uint64_t tag,
    374     enum window_customize_scope scope0, struct options *oo0,
    375     enum window_customize_scope scope1, struct options *oo1,
    376     enum window_customize_scope scope2, struct options *oo2,
    377     struct format_tree *ft, const char *filter, struct cmd_find_state *fs)
    378 {
    379 	struct mode_tree_item		 *top;
    380 	struct options_entry		 *o = NULL, *loop;
    381 	const char			**list = NULL, *name;
    382 	u_int				  size = 0, i;
    383 	enum window_customize_scope	  scope;
    384 
    385 	top = mode_tree_add(data->data, NULL, NULL, tag, title, NULL, 0);
    386 	mode_tree_no_tag(top);
    387 
    388 	/*
    389 	 * We get the options from the first tree, but build it using the
    390 	 * values from the other two. Any tree can have user options so we need
    391 	 * to build a separate list of them.
    392 	 */
    393 
    394 	window_customize_find_user_options(oo0, &list, &size);
    395 	if (oo1 != NULL)
    396 		window_customize_find_user_options(oo1, &list, &size);
    397 	if (oo2 != NULL)
    398 		window_customize_find_user_options(oo2, &list, &size);
    399 
    400 	for (i = 0; i < size; i++) {
    401 		if (oo2 != NULL)
    402 			o = options_get(oo2, list[i]);
    403 		if (o == NULL && oo1 != NULL)
    404 			o = options_get(oo1, list[i]);
    405 		if (o == NULL)
    406 			o = options_get(oo0, list[i]);
    407 		if (options_owner(o) == oo2)
    408 			scope = scope2;
    409 		else if (options_owner(o) == oo1)
    410 			scope = scope1;
    411 		else
    412 			scope = scope0;
    413 		window_customize_build_option(data, top, scope, o, ft, filter,
    414 		    fs);
    415 	}
    416 	free(list);
    417 
    418 	loop = options_first(oo0);
    419 	while (loop != NULL) {
    420 		name = options_name(loop);
    421 		if (*name == '@') {
    422 			loop = options_next(loop);
    423 			continue;
    424 		}
    425 		if (oo2 != NULL)
    426 			o = options_get(oo2, name);
    427 		else if (oo1 != NULL)
    428 			o = options_get(oo1, name);
    429 		else
    430 			o = loop;
    431 		if (options_owner(o) == oo2)
    432 			scope = scope2;
    433 		else if (options_owner(o) == oo1)
    434 			scope = scope1;
    435 		else
    436 			scope = scope0;
    437 		window_customize_build_option(data, top, scope, o, ft, filter,
    438 		    fs);
    439 		loop = options_next(loop);
    440 	}
    441 }
    442 
    443 static void
    444 window_customize_build_keys(struct window_customize_modedata *data,
    445     struct key_table *kt, struct format_tree *ft, const char *filter,
    446     struct cmd_find_state *fs, u_int number)
    447 {
    448 	struct mode_tree_item			*top, *child, *mti;
    449 	struct window_customize_itemdata	*item;
    450 	struct key_binding			*bd;
    451 	char					*title, *text, *tmp, *expanded;
    452 	const char				*flag;
    453 	uint64_t				 tag;
    454 
    455 	tag = (1ULL << 62)|((uint64_t)number << 54)|1;
    456 
    457 	xasprintf(&title, "Key Table - %s", kt->name);
    458 	top = mode_tree_add(data->data, NULL, NULL, tag, title, NULL, 0);
    459 	mode_tree_no_tag(top);
    460 	free(title);
    461 
    462 	ft = format_create_from_state(NULL, NULL, fs);
    463 	format_add(ft, "is_option", "0");
    464 	format_add(ft, "is_key", "1");
    465 
    466 	bd = key_bindings_first(kt);
    467 	while (bd != NULL) {
    468 		format_add(ft, "key", "%s", key_string_lookup_key(bd->key, 0));
    469 		if (bd->note != NULL)
    470 			format_add(ft, "key_note", "%s", bd->note);
    471 		if (filter != NULL) {
    472 			expanded = format_expand(ft, filter);
    473 			if (!format_true(expanded)) {
    474 				free(expanded);
    475 				continue;
    476 			}
    477 			free(expanded);
    478 		}
    479 
    480 		item = window_customize_add_item(data);
    481 		item->scope = WINDOW_CUSTOMIZE_KEY;
    482 		item->table = xstrdup(kt->name);
    483 		item->key = bd->key;
    484 		item->name = xstrdup(key_string_lookup_key(item->key, 0));
    485 		item->idx = -1;
    486 
    487 		expanded = format_expand(ft, data->format);
    488 		child = mode_tree_add(data->data, top, item, (uintptr_t)bd,
    489 		    expanded, NULL, 0);
    490 		free(expanded);
    491 
    492 		tmp = cmd_list_print(bd->cmdlist, 0);
    493 		xasprintf(&text, "#[ignore]%s", tmp);
    494 		free(tmp);
    495 		mti = mode_tree_add(data->data, child, item,
    496 		    tag|(bd->key << 3)|(0 << 1)|1, "Command", text, -1);
    497 		mode_tree_draw_as_parent(mti);
    498 		mode_tree_no_tag(mti);
    499 		free(text);
    500 
    501 		if (bd->note != NULL)
    502 			xasprintf(&text, "#[ignore]%s", bd->note);
    503 		else
    504 			text = xstrdup("");
    505 		mti = mode_tree_add(data->data, child, item,
    506 		    tag|(bd->key << 3)|(1 << 1)|1, "Note", text, -1);
    507 		mode_tree_draw_as_parent(mti);
    508 		mode_tree_no_tag(mti);
    509 		free(text);
    510 
    511 		if (bd->flags & KEY_BINDING_REPEAT)
    512 			flag = "on";
    513 		else
    514 			flag = "off";
    515 		mti = mode_tree_add(data->data, child, item,
    516 		    tag|(bd->key << 3)|(2 << 1)|1, "Repeat", flag, -1);
    517 		mode_tree_draw_as_parent(mti);
    518 		mode_tree_no_tag(mti);
    519 
    520 		bd = key_bindings_next(kt, bd);
    521 	}
    522 
    523 	format_free(ft);
    524 }
    525 
    526 static void
    527 window_customize_build(void *modedata,
    528     __unused struct mode_tree_sort_criteria *sort_crit, __unused uint64_t *tag,
    529     const char *filter)
    530 {
    531 	struct window_customize_modedata	*data = modedata;
    532 	struct cmd_find_state			 fs;
    533 	struct format_tree			*ft;
    534 	u_int					 i;
    535 	struct key_table			*kt;
    536 
    537 	for (i = 0; i < data->item_size; i++)
    538 		window_customize_free_item(data->item_list[i]);
    539 	free(data->item_list);
    540 	data->item_list = NULL;
    541 	data->item_size = 0;
    542 
    543 	if (cmd_find_valid_state(&data->fs))
    544 		cmd_find_copy_state(&fs, &data->fs);
    545 	else
    546 		cmd_find_from_pane(&fs, data->wp, 0);
    547 
    548 	ft = format_create_from_state(NULL, NULL, &fs);
    549 	format_add(ft, "is_option", "1");
    550 	format_add(ft, "is_key", "0");
    551 
    552 	window_customize_build_options(data, "Server Options",
    553 	    (3ULL << 62)|(OPTIONS_TABLE_SERVER << 1)|1,
    554 	    WINDOW_CUSTOMIZE_SERVER, global_options,
    555 	    WINDOW_CUSTOMIZE_NONE, NULL,
    556 	    WINDOW_CUSTOMIZE_NONE, NULL,
    557 	    ft, filter, &fs);
    558 	window_customize_build_options(data, "Session Options",
    559 	    (3ULL << 62)|(OPTIONS_TABLE_SESSION << 1)|1,
    560 	    WINDOW_CUSTOMIZE_GLOBAL_SESSION, global_s_options,
    561 	    WINDOW_CUSTOMIZE_SESSION, fs.s->options,
    562 	    WINDOW_CUSTOMIZE_NONE, NULL,
    563 	    ft, filter, &fs);
    564 	window_customize_build_options(data, "Window & Pane Options",
    565 	    (3ULL << 62)|(OPTIONS_TABLE_WINDOW << 1)|1,
    566 	    WINDOW_CUSTOMIZE_GLOBAL_WINDOW, global_w_options,
    567 	    WINDOW_CUSTOMIZE_WINDOW, fs.w->options,
    568 	    WINDOW_CUSTOMIZE_PANE, fs.wp->options,
    569 	    ft, filter, &fs);
    570 
    571 	format_free(ft);
    572 	ft = format_create_from_state(NULL, NULL, &fs);
    573 
    574 	i = 0;
    575 	kt = key_bindings_first_table();
    576 	while (kt != NULL) {
    577 		if (!RB_EMPTY(&kt->key_bindings)) {
    578 			window_customize_build_keys(data, kt, ft, filter, &fs,
    579 			    i);
    580 			if (++i == 256)
    581 				break;
    582 		}
    583 		kt = key_bindings_next_table(kt);
    584 	}
    585 
    586 	format_free(ft);
    587 }
    588 
    589 static void
    590 window_customize_draw_key(__unused struct window_customize_modedata *data,
    591     struct window_customize_itemdata *item, struct screen_write_ctx *ctx,
    592     u_int sx, u_int sy)
    593 {
    594 	struct screen		*s = ctx->s;
    595 	u_int			 cx = s->cx, cy = s->cy;
    596 	struct key_table	*kt;
    597 	struct key_binding	*bd, *default_bd;
    598 	const char		*note, *period = "";
    599 	char			*cmd, *default_cmd;
    600 
    601 	if (item == NULL || !window_customize_get_key(item, &kt, &bd))
    602 		return;
    603 
    604 	note = bd->note;
    605 	if (note == NULL)
    606 		note = "There is no note for this key.";
    607 	if (*note != '\0' && note[strlen (note) - 1] != '.')
    608 		period = ".";
    609 	if (!screen_write_text(ctx, cx, sx, sy, 0, &grid_default_cell, "%s%s",
    610 	    note, period))
    611 		return;
    612 	screen_write_cursormove(ctx, cx, s->cy + 1, 0); /* skip line */
    613 	if (s->cy >= cy + sy - 1)
    614 		return;
    615 
    616 	if (!screen_write_text(ctx, cx, sx, sy - (s->cy - cy), 0,
    617 	    &grid_default_cell, "This key is in the %s table.", kt->name))
    618 		return;
    619 	if (!screen_write_text(ctx, cx, sx, sy - (s->cy - cy), 0,
    620 	    &grid_default_cell, "This key %s repeat.",
    621 	    (bd->flags & KEY_BINDING_REPEAT) ? "does" : "does not"))
    622 		return;
    623 	screen_write_cursormove(ctx, cx, s->cy + 1, 0); /* skip line */
    624 	if (s->cy >= cy + sy - 1)
    625 		return;
    626 
    627 	cmd = cmd_list_print(bd->cmdlist, 0);
    628 	if (!screen_write_text(ctx, cx, sx, sy - (s->cy - cy), 0,
    629 	    &grid_default_cell, "Command: %s", cmd)) {
    630 		free(cmd);
    631 		return;
    632 	}
    633 	default_bd = key_bindings_get_default(kt, bd->key);
    634 	if (default_bd != NULL) {
    635 		default_cmd = cmd_list_print(default_bd->cmdlist, 0);
    636 		if (strcmp(cmd, default_cmd) != 0 &&
    637 		    !screen_write_text(ctx, cx, sx, sy - (s->cy - cy), 0,
    638 		    &grid_default_cell, "The default is: %s", default_cmd)) {
    639 			free(default_cmd);
    640 			free(cmd);
    641 			return;
    642 		}
    643 		free(default_cmd);
    644 	}
    645 	free(cmd);
    646 }
    647 
    648 static void
    649 window_customize_draw_option(struct window_customize_modedata *data,
    650     struct window_customize_itemdata *item, struct screen_write_ctx *ctx,
    651     u_int sx, u_int sy)
    652 {
    653 	struct screen				 *s = ctx->s;
    654 	u_int					  cx = s->cx, cy = s->cy;
    655 	int					  idx;
    656 	struct options_entry			 *o, *parent;
    657 	struct options				 *go, *wo;
    658 	const struct options_table_entry	 *oe;
    659 	struct grid_cell			  gc;
    660 	const char				**choice, *text, *name;
    661 	const char				 *space = "", *unit = "";
    662 	char					 *value = NULL, *expanded;
    663 	char					 *default_value = NULL;
    664 	char					  choices[256] = "";
    665 	struct cmd_find_state			  fs;
    666 	struct format_tree			 *ft;
    667 
    668 	if (!window_customize_check_item(data, item, &fs))
    669 		return;
    670 	name = item->name;
    671 	idx = item->idx;
    672 
    673 	o = options_get(item->oo, name);
    674 	if (o == NULL)
    675 		return;
    676 	oe = options_table_entry(o);
    677 
    678 	if (oe != NULL && oe->unit != NULL) {
    679 		space = " ";
    680 		unit = oe->unit;
    681 	}
    682 	ft = format_create_from_state(NULL, NULL, &fs);
    683 
    684 	if (oe == NULL || oe->text == NULL)
    685 		text = "This option doesn't have a description.";
    686 	else
    687 		text = oe->text;
    688 	if (!screen_write_text(ctx, cx, sx, sy, 0, &grid_default_cell, "%s",
    689 	    text))
    690 		goto out;
    691 	screen_write_cursormove(ctx, cx, s->cy + 1, 0); /* skip line */
    692 	if (s->cy >= cy + sy - 1)
    693 		goto out;
    694 
    695 	if (oe == NULL)
    696 		text = "user";
    697 	else if ((oe->scope & (OPTIONS_TABLE_WINDOW|OPTIONS_TABLE_PANE)) ==
    698 	    (OPTIONS_TABLE_WINDOW|OPTIONS_TABLE_PANE))
    699 		text = "window and pane";
    700 	else if (oe->scope & OPTIONS_TABLE_WINDOW)
    701 		text = "window";
    702 	else if (oe->scope & OPTIONS_TABLE_SESSION)
    703 		text = "session";
    704 	else
    705 		text = "server";
    706 	if (!screen_write_text(ctx, cx, sx, sy - (s->cy - cy), 0,
    707 	    &grid_default_cell, "This is a %s option.", text))
    708 		goto out;
    709 	if (oe != NULL && (oe->flags & OPTIONS_TABLE_IS_ARRAY)) {
    710 		if (idx != -1) {
    711 			if (!screen_write_text(ctx, cx, sx, sy - (s->cy - cy),
    712 			    0, &grid_default_cell,
    713 			    "This is an array option, index %u.", idx))
    714 				goto out;
    715 		} else {
    716 			if (!screen_write_text(ctx, cx, sx, sy - (s->cy - cy),
    717 			    0, &grid_default_cell, "This is an array option."))
    718 				goto out;
    719 		}
    720 		if (idx == -1)
    721 			goto out;
    722 	}
    723 	screen_write_cursormove(ctx, cx, s->cy + 1, 0); /* skip line */
    724 	if (s->cy >= cy + sy - 1)
    725 		goto out;
    726 
    727 	value = options_to_string(o, idx, 0);
    728 	if (oe != NULL && idx == -1) {
    729 		default_value = options_default_to_string(oe);
    730 		if (strcmp(default_value, value) == 0) {
    731 			free(default_value);
    732 			default_value = NULL;
    733 		}
    734 	}
    735 	if (!screen_write_text(ctx, cx, sx, sy - (s->cy - cy), 0,
    736 	    &grid_default_cell, "Option value: %s%s%s", value, space, unit))
    737 		goto out;
    738 	if (oe == NULL || oe->type == OPTIONS_TABLE_STRING) {
    739 		expanded = format_expand(ft, value);
    740 		if (strcmp(expanded, value) != 0) {
    741 			if (!screen_write_text(ctx, cx, sx, sy - (s->cy - cy),
    742 			    0, &grid_default_cell, "This expands to: %s",
    743 			    expanded))
    744 				goto out;
    745 		}
    746 		free(expanded);
    747 	}
    748 	if (oe != NULL && oe->type == OPTIONS_TABLE_CHOICE) {
    749 		for (choice = oe->choices; *choice != NULL; choice++) {
    750 			strlcat(choices, *choice, sizeof choices);
    751 			strlcat(choices, ", ", sizeof choices);
    752 		}
    753 		choices[strlen(choices) - 2] = '\0';
    754 		if (!screen_write_text(ctx, cx, sx, sy - (s->cy - cy), 0,
    755 		    &grid_default_cell, "Available values are: %s",
    756 		    choices))
    757 			goto out;
    758 	}
    759 	if (oe != NULL && oe->type == OPTIONS_TABLE_COLOUR) {
    760 		if (!screen_write_text(ctx, cx, sx, sy - (s->cy - cy), 1,
    761 		    &grid_default_cell, "This is a colour option: "))
    762 			goto out;
    763 		memcpy(&gc, &grid_default_cell, sizeof gc);
    764 		gc.fg = options_get_number(item->oo, name);
    765 		if (!screen_write_text(ctx, cx, sx, sy - (s->cy - cy), 0, &gc,
    766 		    "EXAMPLE"))
    767 			goto out;
    768 	}
    769 	if (oe != NULL && (oe->flags & OPTIONS_TABLE_IS_STYLE)) {
    770 		if (!screen_write_text(ctx, cx, sx, sy - (s->cy - cy), 1,
    771 		    &grid_default_cell, "This is a style option: "))
    772 			goto out;
    773 		style_apply(&gc, item->oo, name, ft);
    774 		if (!screen_write_text(ctx, cx, sx, sy - (s->cy - cy), 0, &gc,
    775 		    "EXAMPLE"))
    776 			goto out;
    777 	}
    778 	if (default_value != NULL) {
    779 		if (!screen_write_text(ctx, cx, sx, sy - (s->cy - cy), 0,
    780 		    &grid_default_cell, "The default is: %s%s%s", default_value,
    781 		    space, unit))
    782 			goto out;
    783 	}
    784 
    785 	screen_write_cursormove(ctx, cx, s->cy + 1, 0); /* skip line */
    786 	if (s->cy > cy + sy - 1)
    787 		goto out;
    788 	if (oe != NULL && (oe->flags & OPTIONS_TABLE_IS_ARRAY)) {
    789 		wo = NULL;
    790 		go = NULL;
    791 	} else {
    792 		switch (item->scope) {
    793 		case WINDOW_CUSTOMIZE_PANE:
    794 			wo = options_get_parent(item->oo);
    795 			go = options_get_parent(wo);
    796 			break;
    797 		case WINDOW_CUSTOMIZE_WINDOW:
    798 		case WINDOW_CUSTOMIZE_SESSION:
    799 			wo = NULL;
    800 			go = options_get_parent(item->oo);
    801 			break;
    802 		default:
    803 			wo = NULL;
    804 			go = NULL;
    805 			break;
    806 		}
    807 	}
    808 	if (wo != NULL && options_owner(o) != wo) {
    809 		parent = options_get_only(wo, name);
    810 		if (parent != NULL) {
    811 			value = options_to_string(parent, -1 , 0);
    812 			if (!screen_write_text(ctx, s->cx, sx,
    813 			    sy - (s->cy - cy), 0, &grid_default_cell,
    814 			    "Window value (from window %u): %s%s%s", fs.wl->idx,
    815 			    value, space, unit))
    816 				goto out;
    817 		}
    818 	}
    819 	if (go != NULL && options_owner(o) != go) {
    820 		parent = options_get_only(go, name);
    821 		if (parent != NULL) {
    822 			value = options_to_string(parent, -1 , 0);
    823 			if (!screen_write_text(ctx, s->cx, sx,
    824 			    sy - (s->cy - cy), 0, &grid_default_cell,
    825 			    "Global value: %s%s%s", value, space, unit))
    826 				goto out;
    827 		}
    828 	}
    829 
    830 out:
    831 	free(value);
    832 	free(default_value);
    833 	format_free(ft);
    834 }
    835 
    836 static void
    837 window_customize_draw(void *modedata, void *itemdata,
    838     struct screen_write_ctx *ctx, u_int sx, u_int sy)
    839 {
    840 	struct window_customize_modedata	*data = modedata;
    841 	struct window_customize_itemdata	*item = itemdata;
    842 
    843 	if (item == NULL)
    844 		return;
    845 
    846 	if (item->scope == WINDOW_CUSTOMIZE_KEY)
    847 		window_customize_draw_key(data, item, ctx, sx, sy);
    848 	else
    849 		window_customize_draw_option(data, item, ctx, sx, sy);
    850 }
    851 
    852 static void
    853 window_customize_menu(void *modedata, struct client *c, key_code key)
    854 {
    855 	struct window_customize_modedata	*data = modedata;
    856 	struct window_pane			*wp = data->wp;
    857 	struct window_mode_entry		*wme;
    858 
    859 	wme = TAILQ_FIRST(&wp->modes);
    860 	if (wme == NULL || wme->data != modedata)
    861 		return;
    862 	window_customize_key(wme, c, NULL, NULL, key, NULL);
    863 }
    864 
    865 static u_int
    866 window_customize_height(__unused void *modedata, __unused u_int height)
    867 {
    868 	return (12);
    869 }
    870 
    871 static struct screen *
    872 window_customize_init(struct window_mode_entry *wme, struct cmd_find_state *fs,
    873     struct args *args)
    874 {
    875 	struct window_pane			*wp = wme->wp;
    876 	struct window_customize_modedata	*data;
    877 	struct screen				*s;
    878 
    879 	wme->data = data = xcalloc(1, sizeof *data);
    880 	data->wp = wp;
    881 	data->references = 1;
    882 
    883 	memcpy(&data->fs, fs, sizeof data->fs);
    884 
    885 	if (args == NULL || !args_has(args, 'F'))
    886 		data->format = xstrdup(WINDOW_CUSTOMIZE_DEFAULT_FORMAT);
    887 	else
    888 		data->format = xstrdup(args_get(args, 'F'));
    889 	if (args_has(args, 'y'))
    890 		data->prompt_flags = PROMPT_ACCEPT;
    891 
    892 	data->data = mode_tree_start(wp, args, window_customize_build,
    893 	    window_customize_draw, NULL, window_customize_menu,
    894 	    window_customize_height, NULL, NULL, data,
    895 	    window_customize_menu_items, NULL, 0, &s);
    896 	mode_tree_zoom(data->data, args);
    897 
    898 	mode_tree_build(data->data);
    899 	mode_tree_draw(data->data);
    900 
    901 	return (s);
    902 }
    903 
    904 static void
    905 window_customize_destroy(struct window_customize_modedata *data)
    906 {
    907 	u_int	i;
    908 
    909 	if (--data->references != 0)
    910 		return;
    911 
    912 	for (i = 0; i < data->item_size; i++)
    913 		window_customize_free_item(data->item_list[i]);
    914 	free(data->item_list);
    915 
    916 	free(data->format);
    917 
    918 	free(data);
    919 }
    920 
    921 static void
    922 window_customize_free(struct window_mode_entry *wme)
    923 {
    924 	struct window_customize_modedata *data = wme->data;
    925 
    926 	if (data == NULL)
    927 		return;
    928 
    929 	data->dead = 1;
    930 	mode_tree_free(data->data);
    931 	window_customize_destroy(data);
    932 }
    933 
    934 static void
    935 window_customize_resize(struct window_mode_entry *wme, u_int sx, u_int sy)
    936 {
    937 	struct window_customize_modedata	*data = wme->data;
    938 
    939 	mode_tree_resize(data->data, sx, sy);
    940 }
    941 
    942 static void
    943 window_customize_free_callback(void *modedata)
    944 {
    945 	window_customize_destroy(modedata);
    946 }
    947 
    948 static void
    949 window_customize_free_item_callback(void *itemdata)
    950 {
    951 	struct window_customize_itemdata	*item = itemdata;
    952 	struct window_customize_modedata	*data = item->data;
    953 
    954 	window_customize_free_item(item);
    955 	window_customize_destroy(data);
    956 }
    957 
    958 static int
    959 window_customize_set_option_callback(struct client *c, void *itemdata,
    960     const char *s, __unused int done)
    961 {
    962 	struct window_customize_itemdata	*item = itemdata;
    963 	struct window_customize_modedata	*data = item->data;
    964 	struct options_entry			*o;
    965 	const struct options_table_entry	*oe;
    966 	struct options				*oo = item->oo;
    967 	const char				*name = item->name;
    968 	char					*cause;
    969 	int					 idx = item->idx;
    970 
    971 	if (s == NULL || *s == '\0' || data->dead)
    972 		return (0);
    973 	if (item == NULL || !window_customize_check_item(data, item, NULL))
    974 		return (0);
    975 	o = options_get(oo, name);
    976 	if (o == NULL)
    977 		return (0);
    978 	oe = options_table_entry(o);
    979 
    980 	if (oe != NULL && (oe->flags & OPTIONS_TABLE_IS_ARRAY)) {
    981 		if (idx == -1) {
    982 			for (idx = 0; idx < INT_MAX; idx++) {
    983 				if (options_array_get(o, idx) == NULL)
    984 					break;
    985 			}
    986 		}
    987 		if (options_array_set(o, idx, s, 0, &cause) != 0)
    988 			goto fail;
    989 	} else {
    990 		if (options_from_string(oo, oe, name, s, 0, &cause) != 0)
    991 			goto fail;
    992 	}
    993 
    994 	options_push_changes(item->name);
    995 	mode_tree_build(data->data);
    996 	mode_tree_draw(data->data);
    997 	data->wp->flags |= PANE_REDRAW;
    998 
    999 	return (0);
   1000 
   1001 fail:
   1002 	*cause = toupper((u_char)*cause);
   1003 	status_message_set(c, -1, 1, 0, 0, "%s", cause);
   1004 	free(cause);
   1005 	return (0);
   1006 }
   1007 
   1008 static void
   1009 window_customize_set_option(struct client *c,
   1010     struct window_customize_modedata *data,
   1011     struct window_customize_itemdata *item, int global, int pane)
   1012 {
   1013 	struct options_entry			*o;
   1014 	const struct options_table_entry	*oe;
   1015 	struct options				*oo;
   1016 	struct window_customize_itemdata	*new_item;
   1017 	int					 flag, idx = item->idx;
   1018 	enum window_customize_scope		 scope = WINDOW_CUSTOMIZE_NONE;
   1019 	u_int					 choice;
   1020 	const char				*name = item->name, *space = "";
   1021 	char					*prompt, *value, *text;
   1022 	struct cmd_find_state			 fs;
   1023 
   1024 	if (item == NULL || !window_customize_check_item(data, item, &fs))
   1025 		return;
   1026 	o = options_get(item->oo, name);
   1027 	if (o == NULL)
   1028 		return;
   1029 
   1030 	oe = options_table_entry(o);
   1031 	if (oe != NULL && ~oe->scope & OPTIONS_TABLE_PANE)
   1032 		pane = 0;
   1033 	if (oe != NULL && (oe->flags & OPTIONS_TABLE_IS_ARRAY)) {
   1034 		scope = item->scope;
   1035 		oo = item->oo;
   1036 	} else {
   1037 		if (global) {
   1038 			switch (item->scope) {
   1039 			case WINDOW_CUSTOMIZE_NONE:
   1040 			case WINDOW_CUSTOMIZE_KEY:
   1041 			case WINDOW_CUSTOMIZE_SERVER:
   1042 			case WINDOW_CUSTOMIZE_GLOBAL_SESSION:
   1043 			case WINDOW_CUSTOMIZE_GLOBAL_WINDOW:
   1044 				scope = item->scope;
   1045 				break;
   1046 			case WINDOW_CUSTOMIZE_SESSION:
   1047 				scope = WINDOW_CUSTOMIZE_GLOBAL_SESSION;
   1048 				break;
   1049 			case WINDOW_CUSTOMIZE_WINDOW:
   1050 			case WINDOW_CUSTOMIZE_PANE:
   1051 				scope = WINDOW_CUSTOMIZE_GLOBAL_WINDOW;
   1052 				break;
   1053 			}
   1054 		} else {
   1055 			switch (item->scope) {
   1056 			case WINDOW_CUSTOMIZE_NONE:
   1057 			case WINDOW_CUSTOMIZE_KEY:
   1058 			case WINDOW_CUSTOMIZE_SERVER:
   1059 			case WINDOW_CUSTOMIZE_SESSION:
   1060 				scope = item->scope;
   1061 				break;
   1062 			case WINDOW_CUSTOMIZE_WINDOW:
   1063 			case WINDOW_CUSTOMIZE_PANE:
   1064 				if (pane)
   1065 					scope = WINDOW_CUSTOMIZE_PANE;
   1066 				else
   1067 					scope = WINDOW_CUSTOMIZE_WINDOW;
   1068 				break;
   1069 			case WINDOW_CUSTOMIZE_GLOBAL_SESSION:
   1070 				scope = WINDOW_CUSTOMIZE_SESSION;
   1071 				break;
   1072 			case WINDOW_CUSTOMIZE_GLOBAL_WINDOW:
   1073 				if (pane)
   1074 					scope = WINDOW_CUSTOMIZE_PANE;
   1075 				else
   1076 					scope = WINDOW_CUSTOMIZE_WINDOW;
   1077 				break;
   1078 			}
   1079 		}
   1080 		if (scope == item->scope)
   1081 			oo = item->oo;
   1082 		else
   1083 			oo = window_customize_get_tree(scope, &fs);
   1084 	}
   1085 
   1086 	if (oe != NULL && oe->type == OPTIONS_TABLE_FLAG) {
   1087 		flag = options_get_number(oo, name);
   1088 		options_set_number(oo, name, !flag);
   1089 	} else if (oe != NULL && oe->type == OPTIONS_TABLE_CHOICE) {
   1090 		choice = options_get_number(oo, name);
   1091 		if (oe->choices[choice + 1] == NULL)
   1092 			choice = 0;
   1093 		else
   1094 			choice++;
   1095 		options_set_number(oo, name, choice);
   1096 	} else {
   1097 		text = window_customize_scope_text(scope, &fs);
   1098 		if (*text != '\0')
   1099 			space = ", for ";
   1100 		else if (scope != WINDOW_CUSTOMIZE_SERVER)
   1101 			space = ", global";
   1102 		if (oe != NULL && (oe->flags & OPTIONS_TABLE_IS_ARRAY)) {
   1103 			if (idx == -1) {
   1104 				xasprintf(&prompt, "(%s[+]%s%s) ", name, space,
   1105 				    text);
   1106 			} else {
   1107 				xasprintf(&prompt, "(%s[%d]%s%s) ", name, idx,
   1108 				    space, text);
   1109 			}
   1110 		} else
   1111 			xasprintf(&prompt, "(%s%s%s) ", name, space, text);
   1112 		free(text);
   1113 
   1114 		value = options_to_string(o, idx, 0);
   1115 
   1116 		new_item = xcalloc(1, sizeof *new_item);
   1117 		new_item->data = data;
   1118 		new_item->scope = scope;
   1119 		new_item->oo = oo;
   1120 		new_item->name = xstrdup(name);
   1121 		new_item->idx = idx;
   1122 
   1123 		data->references++;
   1124 		status_prompt_set(c, NULL, prompt, value,
   1125 		    window_customize_set_option_callback,
   1126 		    window_customize_free_item_callback, new_item,
   1127 		    PROMPT_NOFORMAT, PROMPT_TYPE_COMMAND);
   1128 
   1129 		free(prompt);
   1130 		free(value);
   1131 	}
   1132 }
   1133 
   1134 static void
   1135 window_customize_unset_option(struct window_customize_modedata *data,
   1136     struct window_customize_itemdata *item)
   1137 {
   1138 	struct options_entry	*o;
   1139 
   1140 	if (item == NULL || !window_customize_check_item(data, item, NULL))
   1141 		return;
   1142 
   1143 	o = options_get(item->oo, item->name);
   1144 	if (o == NULL)
   1145 		return;
   1146 	if (item->idx != -1 && item == mode_tree_get_current(data->data))
   1147 		mode_tree_up(data->data, 0);
   1148 	options_remove_or_default(o, item->idx, NULL);
   1149 }
   1150 
   1151 static void
   1152 window_customize_reset_option(struct window_customize_modedata *data,
   1153     struct window_customize_itemdata *item)
   1154 {
   1155 	struct options		*oo;
   1156 	struct options_entry	*o;
   1157 
   1158 	if (item == NULL || !window_customize_check_item(data, item, NULL))
   1159 		return;
   1160 	if (item->idx != -1)
   1161 		return;
   1162 
   1163 	oo = item->oo;
   1164 	while (oo != NULL) {
   1165 		o = options_get_only(item->oo, item->name);
   1166 		if (o != NULL)
   1167 			options_remove_or_default(o, -1, NULL);
   1168 		oo = options_get_parent(oo);
   1169 	}
   1170 }
   1171 
   1172 static int
   1173 window_customize_set_command_callback(struct client *c, void *itemdata,
   1174     const char *s, __unused int done)
   1175 {
   1176 	struct window_customize_itemdata	*item = itemdata;
   1177 	struct window_customize_modedata	*data = item->data;
   1178 	struct key_binding			*bd;
   1179 	struct cmd_parse_result			*pr;
   1180 	char					*error;
   1181 
   1182 	if (s == NULL || *s == '\0' || data->dead)
   1183 		return (0);
   1184 	if (item == NULL || !window_customize_get_key(item, NULL, &bd))
   1185 		return (0);
   1186 
   1187 	pr = cmd_parse_from_string(s, NULL);
   1188 	switch (pr->status) {
   1189 	case CMD_PARSE_ERROR:
   1190 		error = pr->error;
   1191 		goto fail;
   1192 	case CMD_PARSE_SUCCESS:
   1193 		break;
   1194 	}
   1195 	cmd_list_free(bd->cmdlist);
   1196 	bd->cmdlist = pr->cmdlist;
   1197 
   1198 	mode_tree_build(data->data);
   1199 	mode_tree_draw(data->data);
   1200 	data->wp->flags |= PANE_REDRAW;
   1201 
   1202 	return (0);
   1203 
   1204 fail:
   1205 	*error = toupper((u_char)*error);
   1206 	status_message_set(c, -1, 1, 0, 0, "%s", error);
   1207 	free(error);
   1208 	return (0);
   1209 }
   1210 
   1211 static int
   1212 window_customize_set_note_callback(__unused struct client *c, void *itemdata,
   1213     const char *s, __unused int done)
   1214 {
   1215 	struct window_customize_itemdata	*item = itemdata;
   1216 	struct window_customize_modedata	*data = item->data;
   1217 	struct key_binding			*bd;
   1218 
   1219 	if (s == NULL || *s == '\0' || data->dead)
   1220 		return (0);
   1221 	if (item == NULL || !window_customize_get_key(item, NULL, &bd))
   1222 		return (0);
   1223 
   1224 	free(__UNCONST(bd->note));
   1225 	bd->note = xstrdup(s);
   1226 
   1227 	mode_tree_build(data->data);
   1228 	mode_tree_draw(data->data);
   1229 	data->wp->flags |= PANE_REDRAW;
   1230 
   1231 	return (0);
   1232 }
   1233 
   1234 static void
   1235 window_customize_set_key(struct client *c,
   1236     struct window_customize_modedata *data,
   1237     struct window_customize_itemdata *item)
   1238 {
   1239 	key_code				 key = item->key;
   1240 	struct key_binding			*bd;
   1241 	const char				*s;
   1242 	char					*prompt, *value;
   1243 	struct window_customize_itemdata	*new_item;
   1244 
   1245 	if (item == NULL || !window_customize_get_key(item, NULL, &bd))
   1246 		return;
   1247 
   1248 	s = mode_tree_get_current_name(data->data);
   1249 	if (strcmp(s, "Repeat") == 0)
   1250 		bd->flags ^= KEY_BINDING_REPEAT;
   1251 	else if (strcmp(s, "Command") == 0) {
   1252 		xasprintf(&prompt, "(%s) ", key_string_lookup_key(key, 0));
   1253 		value = cmd_list_print(bd->cmdlist, 0);
   1254 
   1255 		new_item = xcalloc(1, sizeof *new_item);
   1256 		new_item->data = data;
   1257 		new_item->scope = item->scope;
   1258 		new_item->table = xstrdup(item->table);
   1259 		new_item->key = key;
   1260 
   1261 		data->references++;
   1262 		status_prompt_set(c, NULL, prompt, value,
   1263 		    window_customize_set_command_callback,
   1264 		    window_customize_free_item_callback, new_item,
   1265 		    PROMPT_NOFORMAT, PROMPT_TYPE_COMMAND);
   1266 		free(prompt);
   1267 		free(value);
   1268 	} else if (strcmp(s, "Note") == 0) {
   1269 		xasprintf(&prompt, "(%s) ", key_string_lookup_key(key, 0));
   1270 
   1271 		new_item = xcalloc(1, sizeof *new_item);
   1272 		new_item->data = data;
   1273 		new_item->scope = item->scope;
   1274 		new_item->table = xstrdup(item->table);
   1275 		new_item->key = key;
   1276 
   1277 		data->references++;
   1278 		status_prompt_set(c, NULL, prompt,
   1279 		    (bd->note == NULL ? "" : bd->note),
   1280 		    window_customize_set_note_callback,
   1281 		    window_customize_free_item_callback, new_item,
   1282 		    PROMPT_NOFORMAT, PROMPT_TYPE_COMMAND);
   1283 		free(prompt);
   1284 	}
   1285 }
   1286 
   1287 static void
   1288 window_customize_unset_key(struct window_customize_modedata *data,
   1289     struct window_customize_itemdata *item)
   1290 {
   1291 	struct key_table	*kt;
   1292 	struct key_binding	*bd;
   1293 
   1294 	if (item == NULL || !window_customize_get_key(item, &kt, &bd))
   1295 		return;
   1296 
   1297 	if (item == mode_tree_get_current(data->data)) {
   1298 		mode_tree_collapse_current(data->data);
   1299 		mode_tree_up(data->data, 0);
   1300 	}
   1301 	key_bindings_remove(kt->name, bd->key);
   1302 }
   1303 
   1304 static void
   1305 window_customize_reset_key(struct window_customize_modedata *data,
   1306     struct window_customize_itemdata *item)
   1307 {
   1308 	struct key_table	*kt;
   1309 	struct key_binding	*dd, *bd;
   1310 
   1311 	if (item == NULL || !window_customize_get_key(item, &kt, &bd))
   1312 		return;
   1313 
   1314 	dd = key_bindings_get_default(kt, bd->key);
   1315 	if (dd != NULL && bd->cmdlist == dd->cmdlist)
   1316 		return;
   1317 	if (dd == NULL && item == mode_tree_get_current(data->data)) {
   1318 		mode_tree_collapse_current(data->data);
   1319 		mode_tree_up(data->data, 0);
   1320 	}
   1321 	key_bindings_reset(kt->name, bd->key);
   1322 }
   1323 
   1324 static void
   1325 window_customize_change_each(void *modedata, void *itemdata,
   1326     __unused struct client *c, __unused key_code key)
   1327 {
   1328 	struct window_customize_modedata	*data = modedata;
   1329 	struct window_customize_itemdata	*item = itemdata;
   1330 
   1331 	switch (data->change) {
   1332 	case WINDOW_CUSTOMIZE_UNSET:
   1333 		if (item->scope == WINDOW_CUSTOMIZE_KEY)
   1334 			window_customize_unset_key(data, item);
   1335 		else
   1336 			window_customize_unset_option(data, item);
   1337 		break;
   1338 	case WINDOW_CUSTOMIZE_RESET:
   1339 		if (item->scope == WINDOW_CUSTOMIZE_KEY)
   1340 			window_customize_reset_key(data, item);
   1341 		else
   1342 			window_customize_reset_option(data, item);
   1343 		break;
   1344 	}
   1345 	if (item->scope != WINDOW_CUSTOMIZE_KEY)
   1346 		options_push_changes(item->name);
   1347 }
   1348 
   1349 static int
   1350 window_customize_change_current_callback(__unused struct client *c,
   1351     void *modedata, const char *s, __unused int done)
   1352 {
   1353 	struct window_customize_modedata	*data = modedata;
   1354 	struct window_customize_itemdata	*item;
   1355 
   1356 	if (s == NULL || *s == '\0' || data->dead)
   1357 		return (0);
   1358 	if (tolower((u_char) s[0]) != 'y' || s[1] != '\0')
   1359 		return (0);
   1360 
   1361 	item = mode_tree_get_current(data->data);
   1362 	switch (data->change) {
   1363 	case WINDOW_CUSTOMIZE_UNSET:
   1364 		if (item->scope == WINDOW_CUSTOMIZE_KEY)
   1365 			window_customize_unset_key(data, item);
   1366 		else
   1367 			window_customize_unset_option(data, item);
   1368 		break;
   1369 	case WINDOW_CUSTOMIZE_RESET:
   1370 		if (item->scope == WINDOW_CUSTOMIZE_KEY)
   1371 			window_customize_reset_key(data, item);
   1372 		else
   1373 			window_customize_reset_option(data, item);
   1374 		break;
   1375 	}
   1376 	if (item->scope != WINDOW_CUSTOMIZE_KEY)
   1377 		options_push_changes(item->name);
   1378 	mode_tree_build(data->data);
   1379 	mode_tree_draw(data->data);
   1380 	data->wp->flags |= PANE_REDRAW;
   1381 
   1382 	return (0);
   1383 }
   1384 
   1385 static int
   1386 window_customize_change_tagged_callback(struct client *c, void *modedata,
   1387     const char *s, __unused int done)
   1388 {
   1389 	struct window_customize_modedata	*data = modedata;
   1390 
   1391 	if (s == NULL || *s == '\0' || data->dead)
   1392 		return (0);
   1393 	if (tolower((u_char) s[0]) != 'y' || s[1] != '\0')
   1394 		return (0);
   1395 
   1396 	mode_tree_each_tagged(data->data, window_customize_change_each, c,
   1397 	    KEYC_NONE, 0);
   1398 	mode_tree_build(data->data);
   1399 	mode_tree_draw(data->data);
   1400 	data->wp->flags |= PANE_REDRAW;
   1401 
   1402 	return (0);
   1403 }
   1404 
   1405 static void
   1406 window_customize_key(struct window_mode_entry *wme, struct client *c,
   1407     __unused struct session *s, __unused struct winlink *wl, key_code key,
   1408     struct mouse_event *m)
   1409 {
   1410 	struct window_pane			*wp = wme->wp;
   1411 	struct window_customize_modedata	*data = wme->data;
   1412 	struct window_customize_itemdata	*item, *new_item;
   1413 	int					 finished, idx;
   1414 	char					*prompt;
   1415 	u_int					 tagged;
   1416 
   1417 	item = mode_tree_get_current(data->data);
   1418 	finished = mode_tree_key(data->data, c, &key, m, NULL, NULL);
   1419 	if (item != (new_item = mode_tree_get_current(data->data)))
   1420 		item = new_item;
   1421 
   1422 	switch (key) {
   1423 	case '\r':
   1424 	case 's':
   1425 		if (item == NULL)
   1426 			break;
   1427 		if (item->scope == WINDOW_CUSTOMIZE_KEY)
   1428 			window_customize_set_key(c, data, item);
   1429 		else {
   1430 			window_customize_set_option(c, data, item, 0, 1);
   1431 			options_push_changes(item->name);
   1432 		}
   1433 		mode_tree_build(data->data);
   1434 		break;
   1435 	case 'w':
   1436 		if (item == NULL || item->scope == WINDOW_CUSTOMIZE_KEY)
   1437 			break;
   1438 		window_customize_set_option(c, data, item, 0, 0);
   1439 		options_push_changes(item->name);
   1440 		mode_tree_build(data->data);
   1441 		break;
   1442 	case 'S':
   1443 	case 'W':
   1444 		if (item == NULL || item->scope == WINDOW_CUSTOMIZE_KEY)
   1445 			break;
   1446 		window_customize_set_option(c, data, item, 1, 0);
   1447 		options_push_changes(item->name);
   1448 		mode_tree_build(data->data);
   1449 		break;
   1450 	case 'd':
   1451 		if (item == NULL || item->idx != -1)
   1452 			break;
   1453 		xasprintf(&prompt, "Reset %s to default? ", item->name);
   1454 		data->references++;
   1455 		data->change = WINDOW_CUSTOMIZE_RESET;
   1456 		status_prompt_set(c, NULL, prompt, "",
   1457 		    window_customize_change_current_callback,
   1458 		    window_customize_free_callback, data,
   1459 		    PROMPT_SINGLE|PROMPT_NOFORMAT|data->prompt_flags,
   1460 		    PROMPT_TYPE_COMMAND);
   1461 		free(prompt);
   1462 		break;
   1463 	case 'D':
   1464 		tagged = mode_tree_count_tagged(data->data);
   1465 		if (tagged == 0)
   1466 			break;
   1467 		xasprintf(&prompt, "Reset %u tagged to default? ", tagged);
   1468 		data->references++;
   1469 		data->change = WINDOW_CUSTOMIZE_RESET;
   1470 		status_prompt_set(c, NULL, prompt, "",
   1471 		    window_customize_change_tagged_callback,
   1472 		    window_customize_free_callback, data,
   1473 		    PROMPT_SINGLE|PROMPT_NOFORMAT|data->prompt_flags,
   1474 		    PROMPT_TYPE_COMMAND);
   1475 		free(prompt);
   1476 		break;
   1477 	case 'u':
   1478 		if (item == NULL)
   1479 			break;
   1480 		idx = item->idx;
   1481 		if (idx != -1)
   1482 			xasprintf(&prompt, "Unset %s[%d]? ", item->name, idx);
   1483 		else
   1484 			xasprintf(&prompt, "Unset %s? ", item->name);
   1485 		data->references++;
   1486 		data->change = WINDOW_CUSTOMIZE_UNSET;
   1487 		status_prompt_set(c, NULL, prompt, "",
   1488 		    window_customize_change_current_callback,
   1489 		    window_customize_free_callback, data,
   1490 		    PROMPT_SINGLE|PROMPT_NOFORMAT|data->prompt_flags,
   1491 		    PROMPT_TYPE_COMMAND);
   1492 		free(prompt);
   1493 		break;
   1494 	case 'U':
   1495 		tagged = mode_tree_count_tagged(data->data);
   1496 		if (tagged == 0)
   1497 			break;
   1498 		xasprintf(&prompt, "Unset %u tagged? ", tagged);
   1499 		data->references++;
   1500 		data->change = WINDOW_CUSTOMIZE_UNSET;
   1501 		status_prompt_set(c, NULL, prompt, "",
   1502 		    window_customize_change_tagged_callback,
   1503 		    window_customize_free_callback, data,
   1504 		    PROMPT_SINGLE|PROMPT_NOFORMAT|data->prompt_flags,
   1505 		    PROMPT_TYPE_COMMAND);
   1506 		free(prompt);
   1507 		break;
   1508 	case 'H':
   1509 		data->hide_global = !data->hide_global;
   1510 		mode_tree_build(data->data);
   1511 		break;
   1512 	}
   1513 	if (finished)
   1514 		window_pane_reset_mode(wp);
   1515 	else {
   1516 		mode_tree_draw(data->data);
   1517 		wp->flags |= PANE_REDRAW;
   1518 	}
   1519 }
   1520