[BACK]Return to window-customize.c CVS log [TXT][DIR] Up to [local] / src / usr.bin / tmux

Annotation of src/usr.bin/tmux/window-customize.c, Revision 1.1

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