diff --git a/cmd-list-keys.c b/cmd-list-keys.c index 6c0fec67..4f2c9dd3 100644 --- a/cmd-list-keys.c +++ b/cmd-list-keys.c @@ -195,10 +195,6 @@ cmd_list_keys_exec(struct cmd *self, struct cmdq_item *item) } sort_crit.reversed = args_has(args, 'r'); - prefix = cmd_list_keys_get_prefix(args); - single = args_has(args, '1'); - notes_only = args_has(args, 'N'); - if ((tablename = args_get(args, 'T')) != NULL) { table = key_bindings_get_table(tablename, 0); if (table == NULL) { @@ -207,6 +203,10 @@ cmd_list_keys_exec(struct cmd *self, struct cmdq_item *item) } } + prefix = cmd_list_keys_get_prefix(args); + single = args_has(args, '1'); + notes_only = args_has(args, 'N'); + if ((template = args_get(args, 'F')) == NULL) template = LIST_KEYS_TEMPLATE; diff --git a/cmd-parse.y b/cmd-parse.y index 215943b1..542f37d1 100644 --- a/cmd-parse.y +++ b/cmd-parse.y @@ -1604,7 +1604,9 @@ yylex_token_tilde(char **buf, size_t *len) if (*name == '\0') { envent = environ_find(global_environ, "HOME"); - if (envent != NULL && *envent->value != '\0') + if (envent != NULL && + envent->value != NULL && + *envent->value != '\0') home = envent->value; else if ((pw = getpwuid(getuid())) != NULL) home = pw->pw_dir; diff --git a/control.c b/control.c index 35f960d2..0273227f 100644 --- a/control.c +++ b/control.c @@ -1045,6 +1045,9 @@ control_check_subs_timer(__unused int fd, __unused short events, void *data) log_debug("%s: timer fired", __func__); evtimer_add(&cs->subs_timer, &tv); + if (s == NULL) + return; + /* Find which subscription types are present. */ RB_FOREACH(csub, control_subs, &cs->subs) { switch (csub->type) { diff --git a/format.c b/format.c index 3da638af..398161e7 100644 --- a/format.c +++ b/format.c @@ -4508,6 +4508,37 @@ format_window_name(struct format_expand_state *es, const char *fmt) return (xstrdup("0")); } +/* Add neighbor window variables to the format tree. */ +static void +format_add_window_neighbor(struct format_tree *nft, struct winlink *wl, + struct session *s, const char *prefix) +{ + struct options_entry *o; + const char *oname; + char *key, *prefixed, *oval; + + xasprintf(&key, "%s_window_index", prefix); + format_add(nft, key, "%u", wl->idx); + free(key); + + xasprintf(&key, "%s_window_active", prefix); + format_add(nft, key, "%d", wl == s->curw); + free(key); + + o = options_first(wl->window->options); + while (o != NULL) { + oname = options_name(o); + if (*oname == '@') { + xasprintf(&prefixed, "%s_%s", prefix, oname); + oval = options_to_string(o, -1, 1); + format_add(nft, prefixed, "%s", oval); + free(oval); + free(prefixed); + } + o = options_next(o); + } +} + /* Loop over windows. */ static char * format_loop_windows(struct format_expand_state *es, const char *fmt) @@ -4551,6 +4582,17 @@ format_loop_windows(struct format_expand_state *es, const char *fmt) nft = format_create(c, item, FORMAT_WINDOW|w->id, ft->flags|last); format_defaults(nft, ft->c, ft->s, wl, NULL); + + /* Add neighbor window data to the format tree. */ + format_add(nft, "window_after_active", "%d", + i > 0 && l[i - 1] == ft->s->curw); + format_add(nft, "window_before_active", "%d", + i + 1 < n && l[i + 1] == ft->s->curw); + if (i + 1 < n) + format_add_window_neighbor(nft, l[i + 1], ft->s, "next"); + if (i > 0) + format_add_window_neighbor(nft, l[i - 1], ft->s, "prev"); + format_copy_state(&next, es, 0); next.ft = nft; expanded = format_expand1(&next, use); diff --git a/image-sixel.c b/image-sixel.c index 5c800461..f6eaade3 100644 --- a/image-sixel.c +++ b/image-sixel.c @@ -357,7 +357,7 @@ sixel_parse(const char *buf, size_t len, u_int p2, u_int xpixel, u_int ypixel) return (si); bad: - free(si); + sixel_free(si); return (NULL); } diff --git a/input.c b/input.c index 42bafa89..f8e95e19 100644 --- a/input.c +++ b/input.c @@ -949,7 +949,7 @@ input_set_state(struct input_ctx *ictx, const struct input_transition *itr) /* Parse data. */ static void -input_parse(struct input_ctx *ictx, u_char *buf, size_t len) +input_parse(struct input_ctx *ictx, const u_char *buf, size_t len) { struct screen_write_ctx *sctx = &ictx->ctx; const struct input_state *state = NULL; @@ -1020,7 +1020,7 @@ input_parse_pane(struct window_pane *wp) /* Parse given input. */ void -input_parse_buffer(struct window_pane *wp, u_char *buf, size_t len) +input_parse_buffer(struct window_pane *wp, const u_char *buf, size_t len) { struct input_ctx *ictx = wp->ictx; struct screen_write_ctx *sctx = &ictx->ctx; @@ -1051,7 +1051,7 @@ input_parse_buffer(struct window_pane *wp, u_char *buf, size_t len) /* Parse given input for screen. */ void input_parse_screen(struct input_ctx *ictx, struct screen *s, - screen_write_init_ctx_cb cb, void *arg, u_char *buf, size_t len) + screen_write_init_ctx_cb cb, void *arg, const u_char *buf, size_t len) { struct screen_write_ctx *sctx = &ictx->ctx; diff --git a/menu.c b/menu.c index 0df6fb47..9ae300ed 100644 --- a/menu.c +++ b/menu.c @@ -363,7 +363,7 @@ menu_key_cb(struct client *c, void *data, struct key_event *event) name = menu->items[i].name; if (name == NULL || *name == '-') continue; - if (event->key == menu->items[i].key) { + if ((event->key & ~KEYC_MASK_FLAGS) == menu->items[i].key) { md->choice = i; goto chosen; } diff --git a/mode-tree.c b/mode-tree.c index d76ff93d..0cd17e39 100644 --- a/mode-tree.c +++ b/mode-tree.c @@ -58,6 +58,7 @@ struct mode_tree_data { mode_tree_key_cb keycb; mode_tree_swap_cb swapcb; mode_tree_sort_cb sortcb; + mode_tree_help_cb helpcb; struct mode_tree_list children; struct mode_tree_list saved; @@ -132,6 +133,36 @@ static const struct menu_item mode_tree_menu_items[] = { { NULL, KEYC_NONE, NULL } }; +static const char* mode_tree_help_start[] = { + "\r\033[1m Up, k \033[0m\016x\017 \033[0mMove cursor up\n", + "\r\033[1m Down, j \033[0m\016x\017 \033[0mMove cursor down\n", + "\r\033[1m g \033[0m\016x\017 \033[0mGo to top\n", + "\r\033[1m G \033[0m\016x\017 \033[0mGo to bottom\n", + "\r\033[1m PPage, C-b \033[0m\016x\017 \033[0mPage up\n", + "\r\033[1m NPage, C-f \033[0m\016x\017 \033[0mPage down\n", + "\r\033[1m Left, h \033[0m\016x\017 \033[0mCollapse %1\n", + "\r\033[1m Right, l \033[0m\016x\017 \033[0mExpand %1\n", + "\r\033[1m M-- \033[0m\016x\017 \033[0mCollapse all %1s\n", + "\r\033[1m M-+ \033[0m\016x\017 \033[0mExpand all %1s\n", + "\r\033[1m t \033[0m\016x\017 \033[0mToggle %1 tag\n", + "\r\033[1m T \033[0m\016x\017 \033[0mUntag all %1s\n", + "\r\033[1m C-t \033[0m\016x\017 \033[0mTag all %1s\n", + "\r\033[1m C-s \033[0m\016x\017 \033[0mSearch forward\n", + "\r\033[1m C-r \033[0m\016x\017 \033[0mSearch backward\n", + "\r\033[1m n \033[0m\016x\017 \033[0mRepeat search forward\n", + "\r\033[1m N \033[0m\016x\017 \033[0mRepeat search backward\n", + "\r\033[1m f \033[0m\016x\017 \033[0mFilter %1s\n", + "\r\033[1m O \033[0m\016x\017 \033[0mChange sort order\n", + "\r\033[1m r \033[0m\016x\017 \033[0mReverse sort order\n", + "\r\033[1m v \033[0m\016x\017 \033[0mToggle preview\n", + NULL +}; +static const char* mode_tree_help_end[] = { + "\r\033[1m q, Escape \033[0m\016x\017 \033[0mExit mode\033[H", + NULL +}; +#define MODE_TREE_HELP_DEFAULT_WIDTH 39 + static int mode_tree_is_lowercase(const char *ptr) { @@ -453,8 +484,9 @@ mode_tree_start(struct window_pane *wp, struct args *args, mode_tree_build_cb buildcb, mode_tree_draw_cb drawcb, mode_tree_search_cb searchcb, mode_tree_menu_cb menucb, mode_tree_height_cb heightcb, mode_tree_key_cb keycb, - mode_tree_swap_cb swapcb, mode_tree_sort_cb sortcb, void *modedata, - const struct menu_item *menu, struct screen **s) + mode_tree_swap_cb swapcb, mode_tree_sort_cb sortcb, + mode_tree_help_cb helpcb, void *modedata, const struct menu_item *menu, + struct screen **s) { struct mode_tree_data *mtd; @@ -488,6 +520,7 @@ mode_tree_start(struct window_pane *wp, struct args *args, mtd->keycb = keycb; mtd->swapcb = swapcb; mtd->sortcb = sortcb; + mtd->helpcb = helpcb; TAILQ_INIT(&mtd->children); @@ -1123,6 +1156,57 @@ mode_tree_display_menu(struct mode_tree_data *mtd, struct client *c, u_int x, } } +static void +mode_tree_display_help(__unused struct mode_tree_data *mtd, struct client *c) +{ + struct session *s = c->session; + u_int px, py, w, h = 0; + const char **line, **lines = NULL, *item = "item"; + char *new_line; + + if (mtd->helpcb == NULL) + w = MODE_TREE_HELP_DEFAULT_WIDTH; + else { + lines = mtd->helpcb(&w, &item); + if (w < MODE_TREE_HELP_DEFAULT_WIDTH) + w = MODE_TREE_HELP_DEFAULT_WIDTH; + } + for (line = mode_tree_help_start; *line != NULL; line++) + h++; + for (line = lines; *line != NULL; line++) + h++; + for (line = mode_tree_help_end; *line != NULL; line++) + h++; + + if (c->tty.sx < w || c->tty.sy < h) + return; + px = (c->tty.sx - w) / 2; + py = (c->tty.sy - h) / 2; + + if (popup_display(POPUP_CLOSEANYKEY|POPUP_NOJOB, BOX_LINES_DEFAULT, + NULL, px, py, w, h, NULL, NULL, 0, NULL, NULL, NULL, c, s, NULL, + NULL, NULL, NULL) != 0) + return; + + popup_write(c, "\033[H\033[?25l\033[?7l\033)0", 17); + for (line = mode_tree_help_start; *line != NULL; line++) { + new_line = cmd_template_replace(*line, item, 1); + popup_write(c, new_line, strlen(new_line)); + free(new_line); + } + for (line = lines; *line != NULL; line++) { + new_line = cmd_template_replace(*line, item, 1); + popup_write(c, new_line, strlen(new_line)); + free(new_line); + } + for (line = mode_tree_help_end; *line != NULL; line++) { + new_line = cmd_template_replace(*line, item, 1); + popup_write(c, new_line, strlen(new_line)); + free(new_line); + } + popup_write(c, "\033[H", 3); +} + int mode_tree_key(struct mode_tree_data *mtd, struct client *c, key_code *key, struct mouse_event *m, u_int *xp, u_int *yp) @@ -1193,6 +1277,10 @@ mode_tree_key(struct mode_tree_data *mtd, struct client *c, key_code *key, case '\033': /* Escape */ case 'g'|KEYC_CTRL: return (1); + case KEYC_F1: + case 'h'|KEYC_CTRL: + mode_tree_display_help(mtd, c); + break; case KEYC_UP: case 'k': case KEYC_WHEELUP_PANE: diff --git a/options-table.c b/options-table.c index d9918193..dfe0c9e5 100644 --- a/options-table.c +++ b/options-table.c @@ -141,7 +141,7 @@ static const char *options_table_allow_passthrough_list[] = { "#{T:window-status-format}" \ "#[pop-default]" \ "#[norange default]" \ - "#{?loop_last_flag,,#{window-status-separator}}" \ + "#{?loop_last_flag,,#{E:window-status-separator}}" \ "," \ "#[range=window|#{window_index} list=focus " \ "#{?#{!=:#{E:window-status-current-style},default}," \ @@ -168,7 +168,7 @@ static const char *options_table_allow_passthrough_list[] = { "#{T:window-status-current-format}" \ "#[pop-default]" \ "#[norange list=on default]" \ - "#{?loop_last_flag,,#{window-status-separator}}" \ + "#{?loop_last_flag,,#{E:window-status-separator}}" \ "}" \ "#[nolist align=right range=right #{E:status-right-style}]" \ "#[push-default]" \ @@ -723,13 +723,24 @@ const struct options_table_entry options_table[] = { { .name = "message-command-style", .type = OPTIONS_TABLE_STRING, .scope = OPTIONS_TABLE_SESSION, - .default_str = "bg=black,fg=yellow", + .default_str = "bg=black,fg=yellow,fill=black", .flags = OPTIONS_TABLE_IS_STYLE, .separator = ",", .text = "Style of the command prompt when in command mode, if " "'mode-keys' is set to 'vi'." }, + { .name = "message-format", + .type = OPTIONS_TABLE_STRING, + .scope = OPTIONS_TABLE_SESSION, + .default_str = "#[#{?#{command_prompt}," + "#{E:message-command-style}," + "#{E:message-style}}]" + "#{message}", + .text = "Format string for the prompt and message area. " + "The '#{message}' placeholder is replaced with the content." + }, + { .name = "message-line", .type = OPTIONS_TABLE_CHOICE, .scope = OPTIONS_TABLE_SESSION, @@ -741,10 +752,13 @@ const struct options_table_entry options_table[] = { { .name = "message-style", .type = OPTIONS_TABLE_STRING, .scope = OPTIONS_TABLE_SESSION, - .default_str = "bg=yellow,fg=black", + .default_str = "bg=yellow,fg=black,fill=yellow", .flags = OPTIONS_TABLE_IS_STYLE, .separator = ",", - .text = "Style of messages and the command prompt." + .text = "Style of messages and the command prompt. " + "A 'fill' attribute controls background clearing and " + "a 'width' attribute (fixed or percentage) constrains " + "the prompt area width." }, { .name = "mouse", diff --git a/popup.c b/popup.c index d4100998..9bfa3880 100644 --- a/popup.c +++ b/popup.c @@ -847,16 +847,35 @@ popup_display(int flags, enum box_lines lines, struct cmdq_item *item, u_int px, pd->psx = sx; pd->psy = sy; - pd->job = job_run(shellcmd, argc, argv, env, s, cwd, - popup_job_update_cb, popup_job_complete_cb, NULL, pd, - JOB_NOWAIT|JOB_PTY|JOB_KEEPWRITE|JOB_DEFAULTSHELL, jx, jy); - pd->ictx = input_init(NULL, job_get_event(pd->job), &pd->palette, c); + if (flags & POPUP_NOJOB) + pd->ictx = input_init(NULL, NULL, &pd->palette, NULL); + else { + pd->job = job_run(shellcmd, argc, argv, env, s, cwd, + popup_job_update_cb, popup_job_complete_cb, NULL, pd, + JOB_NOWAIT|JOB_PTY|JOB_KEEPWRITE|JOB_DEFAULTSHELL, jx, jy); + pd->ictx = input_init(NULL, job_get_event(pd->job), + &pd->palette, c); + } server_client_set_overlay(c, 0, popup_check_cb, popup_mode_cb, popup_draw_cb, popup_key_cb, popup_free_cb, popup_resize_cb, pd); return (0); } +void +popup_write(struct client *c, const char *data, size_t size) +{ + struct popup_data *pd = c->overlay_data; + + if (!popup_present(c)) + return; + c->overlay_check = NULL; + c->overlay_data = NULL; + input_parse_screen(pd->ictx, &pd->s, popup_init_ctx_cb, pd, data, size); + c->overlay_check = popup_check_cb; + c->overlay_data = pd; +} + static void popup_editor_free(struct popup_editor *pe) { diff --git a/status.c b/status.c index 87d5e017..cd8c62be 100644 --- a/status.c +++ b/status.c @@ -551,6 +551,71 @@ status_message_callback(__unused int fd, __unused short event, void *data) status_message_clear(c); } +/* + * Calculate prompt/message area geometry from the style's width and align + * directives: x offset and available width within the status line. + */ +static void +status_prompt_area(struct client *c, u_int *area_x, u_int *area_w) +{ + struct session *s = c->session; + struct style *sy; + u_int w; + + /* Get width from message-style's width directive. */ + sy = options_string_to_style(s->options, "message-style", NULL); + if (sy != NULL && sy->width >= 0) { + if (sy->width_percentage) + w = (c->tty.sx * (u_int)sy->width) / 100; + else + w = (u_int)sy->width; + } else + w = c->tty.sx; + if (w == 0 || w > c->tty.sx) + w = c->tty.sx; + + /* Get horizontal position from message-style's align directive. */ + if (sy != NULL) { + switch (sy->align) { + case STYLE_ALIGN_CENTRE: + case STYLE_ALIGN_ABSOLUTE_CENTRE: + *area_x = (c->tty.sx - w) / 2; + break; + case STYLE_ALIGN_RIGHT: + *area_x = c->tty.sx - w; + break; + default: + *area_x = 0; + break; + } + } else + *area_x = 0; + + *area_w = w; +} + +/* Escape # characters in a string so format_draw treats them as literal. */ +static char * +status_prompt_escape(const char *s) +{ + const char *cp; + char *out, *p; + size_t n = 0; + + for (cp = s; *cp != '\0'; cp++) { + if (*cp == '#') + n++; + } + p = out = xmalloc(strlen(s) + n + 1); + for (cp = s; *cp != '\0'; cp++) { + if (*cp == '#') + *p++ = '#'; + *p++ = *cp; + } + *p = '\0'; + return (out); +} + /* Draw client message on status line of present else on last line. */ int status_message_redraw(struct client *c) @@ -559,10 +624,12 @@ status_message_redraw(struct client *c) struct screen_write_ctx ctx; struct session *s = c->session; struct screen old_screen; - size_t len; - u_int lines, offset, messageline; + u_int lines, messageline; + u_int ax, aw; struct grid_cell gc; struct format_tree *ft; + const char *msgfmt; + char *expanded, *msg; if (c->tty.sx == 0 || c->tty.sy == 0) return (0); @@ -577,26 +644,36 @@ status_message_redraw(struct client *c) if (messageline > lines - 1) messageline = lines - 1; - len = screen_write_strlen("%s", c->message_string); - if (len > c->tty.sx) - len = c->tty.sx; + status_prompt_area(c, &ax, &aw); ft = format_create_defaults(NULL, c, NULL, NULL, NULL); - style_apply(&gc, s->options, "message-style", ft); + memcpy(&gc, &grid_default_cell, sizeof gc); + + /* + * Set #{message} in the format tree. If styles should be ignored in + * the message content, escape # characters so format_draw treats them + * as literal text. + */ + if (c->message_ignore_styles) { + msg = status_prompt_escape(c->message_string); + format_add(ft, "message", "%s", msg); + free(msg); + } else + format_add(ft, "message", "%s", c->message_string); + format_add(ft, "command_prompt", "%d", 0); + + msgfmt = options_get_string(s->options, "message-format"); + expanded = format_expand_time(ft, msgfmt); format_free(ft); screen_write_start(&ctx, sl->active); screen_write_fast_copy(&ctx, &sl->screen, 0, 0, c->tty.sx, lines); - screen_write_cursormove(&ctx, 0, messageline, 0); - for (offset = 0; offset < c->tty.sx; offset++) - screen_write_putc(&ctx, &gc, ' '); - screen_write_cursormove(&ctx, 0, messageline, 0); - if (c->message_ignore_styles) - screen_write_nputs(&ctx, len, &gc, "%s", c->message_string); - else - format_draw(&ctx, &gc, c->tty.sx, c->message_string, NULL, 0); + screen_write_cursormove(&ctx, ax, messageline, 0); + format_draw(&ctx, &gc, aw, expanded, NULL, 0); screen_write_stop(&ctx); + free(expanded); + if (grid_compare(sl->active->grid, old_screen.grid) == 0) { screen_free(&old_screen); return (0); @@ -789,9 +866,10 @@ status_prompt_redraw(struct client *c) struct screen old_screen; u_int i, lines, offset, left, start, width, n; u_int pcursor, pwidth, promptline; + u_int ax, aw; struct grid_cell gc; - struct format_tree *ft = c->prompt_formats; - char *prompt, *tmp; + const char *msgfmt; + char *expanded, *prompt, *tmp; if (c->tty.sx == 0 || c->tty.sy == 0) return (0); @@ -816,29 +894,40 @@ status_prompt_redraw(struct client *c) promptline = lines - 1; if (c->prompt_mode == PROMPT_COMMAND) - style_apply(&gc, s->options, "message-command-style", ft); + style_apply(&gc, s->options, "message-command-style", NULL); else - style_apply(&gc, s->options, "message-style", ft); + style_apply(&gc, s->options, "message-style", NULL); + + status_prompt_area(c, &ax, &aw); tmp = utf8_tocstr(c->prompt_buffer); format_add(c->prompt_formats, "prompt-input", "%s", tmp); prompt = format_expand_time(c->prompt_formats, c->prompt_string); - free (tmp); + free(tmp); + + /* + * Set #{message} to the prompt string and expand message-format. + * format_draw handles fill, alignment, and decorations in one call. + */ + format_add(c->prompt_formats, "message", "%s", prompt); + format_add(c->prompt_formats, "command_prompt", "%d", + c->prompt_mode == PROMPT_COMMAND); + msgfmt = options_get_string(s->options, "message-format"); + expanded = format_expand_time(c->prompt_formats, msgfmt); start = format_width(prompt); - if (start > c->tty.sx) - start = c->tty.sx; + if (start > aw) + start = aw; screen_write_start(&ctx, sl->active); screen_write_fast_copy(&ctx, &sl->screen, 0, 0, c->tty.sx, lines); - screen_write_cursormove(&ctx, 0, promptline, 0); - for (offset = 0; offset < c->tty.sx; offset++) - screen_write_putc(&ctx, &gc, ' '); - screen_write_cursormove(&ctx, 0, promptline, 0); - format_draw(&ctx, &gc, start, prompt, NULL, 0); - screen_write_cursormove(&ctx, start, promptline, 0); + screen_write_cursormove(&ctx, ax, promptline, 0); + format_draw(&ctx, &gc, aw, expanded, NULL, 0); + screen_write_cursormove(&ctx, ax + start, promptline, 0); - left = c->tty.sx - start; + free(expanded); + + left = aw - start; if (left == 0) goto finished; @@ -857,7 +946,7 @@ status_prompt_redraw(struct client *c) offset = 0; if (pwidth > left) pwidth = left; - c->prompt_cursor = start + pcursor - offset; + c->prompt_cursor = ax + start + pcursor - offset; width = 0; for (i = 0; c->prompt_buffer[i].size != 0; i++) { @@ -1831,7 +1920,7 @@ status_prompt_complete_list_menu(struct client *c, char **list, u_int size, struct menu_item item; struct status_prompt_menu *spm; u_int lines = status_line_size(c), height, i; - u_int py; + u_int py, ax, aw; if (size <= 1) return (0); @@ -1859,11 +1948,13 @@ status_prompt_complete_list_menu(struct client *c, char **list, u_int size, menu_add_item(menu, &item, NULL, c, NULL); } + status_prompt_area(c, &ax, &aw); if (options_get_number(c->session->options, "status-position") == 0) py = lines; else py = c->tty.sy - 3 - height; offset += utf8_cstrwidth(c->prompt_string); + offset += ax; if (offset > 2) offset -= 2; else @@ -1890,7 +1981,7 @@ status_prompt_complete_window_menu(struct client *c, struct session *s, struct winlink *wl; char **list = NULL, *tmp; u_int lines = status_line_size(c), height; - u_int py, size = 0, i; + u_int py, size = 0, i, ax, aw; if (c->tty.sy - lines < 3) return (NULL); @@ -1955,11 +2046,13 @@ status_prompt_complete_window_menu(struct client *c, struct session *s, spm->size = size; spm->list = list; + status_prompt_area(c, &ax, &aw); if (options_get_number(c->session->options, "status-position") == 0) py = lines; else py = c->tty.sy - 3 - height; offset += utf8_cstrwidth(c->prompt_string); + offset += ax; if (offset > 2) offset -= 2; else diff --git a/style.c b/style.c index 4acc17dd..394eaf32 100644 --- a/style.c +++ b/style.c @@ -39,7 +39,7 @@ static struct style style_default = { STYLE_RANGE_NONE, 0, "", - STYLE_WIDTH_DEFAULT, STYLE_PAD_DEFAULT, + STYLE_WIDTH_DEFAULT, 0, STYLE_PAD_DEFAULT, STYLE_DEFAULT_BASE }; @@ -226,10 +226,20 @@ style_parse(struct style *sy, const struct grid_cell *base, const char *in) sy->gc.attr &= ~value; } } else if (end > 6 && strncasecmp(tmp, "width=", 6) == 0) { - n = strtonum(tmp + 6, 0, UINT_MAX, &errstr); - if (errstr != NULL) - goto error; - sy->width = (int)n; + if (end > 7 && tmp[end - 1] == '%') { + tmp[end - 1] = '\0'; + n = strtonum(tmp + 6, 0, 100, &errstr); + if (errstr != NULL) + goto error; + sy->width = (int)n; + sy->width_percentage = 1; + } else { + n = strtonum(tmp + 6, 0, UINT_MAX, &errstr); + if (errstr != NULL) + goto error; + sy->width = (int)n; + sy->width_percentage = 0; + } } else if (end > 4 && strncasecmp(tmp, "pad=", 4) == 0) { n = strtonum(tmp + 4, 0, UINT_MAX, &errstr); if (errstr != NULL) @@ -343,13 +353,17 @@ style_tostring(struct style *sy) comma = ","; } if (gc->attr != 0) { - xsnprintf(s + off, sizeof s - off, "%s%s", comma, + off += xsnprintf(s + off, sizeof s - off, "%s%s", comma, attributes_tostring(gc->attr)); comma = ","; } if (sy->width >= 0) { - xsnprintf(s + off, sizeof s - off, "%swidth=%u", comma, - sy->width); + if (sy->width_percentage) + off += xsnprintf(s + off, sizeof s - off, + "%swidth=%u%%", comma, sy->width); + else + off += xsnprintf(s + off, sizeof s - off, + "%swidth=%u", comma, sy->width); comma = ","; } if (sy->pad >= 0) { diff --git a/tmux.1 b/tmux.1 index 28d22ea4..2cdac4e4 100644 --- a/tmux.1 +++ b/tmux.1 @@ -2700,7 +2700,7 @@ zooms the pane. .Fl y disables any confirmation prompts. The following keys may be used in client mode: -.Bl -column "Key" "Function" -offset indent +.Bl -column "KeyXXXXXX" "Function" -offset indent .It Sy "Key" Ta Sy "Function" .It Li "Enter" Ta "Choose selected client" .It Li "Up" Ta "Select previous client" @@ -2721,6 +2721,7 @@ The following keys may be used in client mode: .It Li "O" Ta "Change sort order" .It Li "r" Ta "Reverse sort order" .It Li "v" Ta "Toggle preview" +.It Li "F1 or C-h" Ta "Display help" .It Li "q" Ta "Exit mode" .El .Pp @@ -2779,7 +2780,7 @@ zooms the pane. .Fl y disables any confirmation prompts. The following keys may be used in tree mode: -.Bl -column "Key" "Function" -offset indent +.Bl -column "KeyXXXXXX" "Function" -offset indent .It Sy "Key" Ta Sy "Function" .It Li "Enter" Ta "Choose selected item" .It Li "Up" Ta "Select previous item" @@ -2808,6 +2809,7 @@ The following keys may be used in tree mode: .It Li "O" Ta "Change sort order" .It Li "r" Ta "Reverse sort order" .It Li "v" Ta "Toggle preview" +.It Li "F1 or C-h" Ta "Display help" .It Li "q" Ta "Exit mode" .El .Pp @@ -2859,7 +2861,7 @@ Option values in the list are shown for the active pane in the current window. .Fl Z zooms the pane. The following keys may be used in customize mode: -.Bl -column "Key" "Function" -offset indent +.Bl -column "KeyXXXXXX" "Function" -offset indent .It Sy "Key" Ta Sy "Function" .It Li "Enter" Ta "Set pane, window, session or global option value" .It Li "Up" Ta "Select previous item" @@ -2883,6 +2885,7 @@ The following keys may be used in customize mode: .It Li "C-t" Ta "Tag all items" .It Li "f" Ta "Enter a format to filter items" .It Li "v" Ta "Toggle option information" +.It Li "F1 or C-h" Ta "Display help" .It Li "q" Ta "Exit mode" .El .Pp @@ -4747,6 +4750,23 @@ For how to specify see the .Sx STYLES section. +.It Ic message-format Ar string +Set the format string for the prompt and message area. +The special placeholder +.Ql #{message} +expands to the interactive prompt or message text and +.Ql #{command_prompt} +is set to 1 when the prompt is in command mode (vi). +Style directives like +.Ic fill , +.Ic align , +and +.Ic width +may be used in the format string. +The default uses a conditional to select between +.Ic message-style +and +.Ic message-command-style . .It Xo Ic message-line .Op Ic 0 | 1 | 2 | 3 | 4 .Xc @@ -4754,6 +4774,23 @@ Set line on which status line messages and the command prompt are shown. .It Ic message-style Ar style Set status line message style. This is used for messages and for the command prompt. +The message is drawn on top of the existing status line. +A +.Ic width +attribute (a fixed number of columns or a percentage such as +.Ql 50% ) +constrains the prompt area width and an +.Ic align +attribute +.Pq Ic left , centre , right +sets its horizontal position. +When the width is less than the full terminal width, the normal status +bar content remains visible around the prompt area. +The +.Ic fill +attribute is used by the default +.Ic message-format +to clear the background. For how to specify .Ar style , see the @@ -6127,6 +6164,21 @@ For example, to get a list of windows formatted like the status line: #{W:#{E:window-status-format} ,#{E:window-status-current-format} } .Ed .Pp +Within the +.Ql W:\& +loop, user options +.Po +.Ql @name +.Pc +from neighboring windows are available with a +.Ql next_ +or +.Ql prev_ +prefix, for example a user option +.Ql @color +on the next window is available as +.Ql next_@color . +.Pp .Ql N:\& checks if a window (without any suffix or with the .Ql w @@ -6295,6 +6347,8 @@ The following variables are available, where appropriate: .It Li "mouse_x" Ta "" Ta "Mouse X position, if any" .It Li "mouse_y" Ta "" Ta "Mouse Y position, if any" .It Li "next_session_id" Ta "" Ta "Unique session ID for next new session" +.It Li "next_window_active" Ta "" Ta "1 if next window in W: loop is active" +.It Li "next_window_index" Ta "" Ta "Index of next window in W: loop" .It Li "origin_flag" Ta "" Ta "Pane origin flag" .It Li "pane_active" Ta "" Ta "1 if active pane" .It Li "pane_at_bottom" Ta "" Ta "1 if pane is at the bottom of window" @@ -6339,6 +6393,8 @@ The following variables are available, where appropriate: .It Li "pane_unseen_changes" Ta "" Ta "1 if there were changes in pane while in mode" .It Li "pane_width" Ta "" Ta "Width of pane" .It Li "pid" Ta "" Ta "Server PID" +.It Li "prev_window_active" Ta "" Ta "1 if previous window in W: loop is active" +.It Li "prev_window_index" Ta "" Ta "Index of previous window in W: loop" .It Li "rectangle_toggle" Ta "" Ta "1 if rectangle selection is activated" .It Li "scroll_position" Ta "" Ta "Scroll position in copy mode" .It Li "scroll_region_lower" Ta "" Ta "Bottom of scroll region in pane" @@ -6506,6 +6562,11 @@ is the terminal alternate character set. Align text to the left, centre or right of the available space if appropriate. .It Ic fill=colour Fill the available space with a background colour if appropriate. +.It Ic width=N +Set the width of the styled area. +.Ar N +may be a column count or a percentage (for example +.Ql 50% ) . .It Xo Ic list=on , .Ic list=focus , .Ic list=left-marker , @@ -7336,7 +7397,7 @@ zooms the pane. .Fl y disables any confirmation prompts. The following keys may be used in buffer mode: -.Bl -column "Key" "Function" -offset indent +.Bl -column "KeyXXXXXX" "Function" -offset indent .It Sy "Key" Ta Sy "Function" .It Li "Enter" Ta "Paste selected buffer" .It Li "Up" Ta "Select previous buffer" @@ -7356,6 +7417,7 @@ The following keys may be used in buffer mode: .It Li "O" Ta "Change sort order" .It Li "r" Ta "Reverse sort order" .It Li "v" Ta "Toggle preview" +.It Li "F1 or C-h" Ta "Display help" .It Li "q" Ta "Exit mode" .El .Pp diff --git a/tmux.h b/tmux.h index de841a27..c9d3c04c 100644 --- a/tmux.h +++ b/tmux.h @@ -920,6 +920,7 @@ struct style { char range_string[16]; int width; + int width_percentage; int pad; enum style_default_type default_type; @@ -3084,9 +3085,9 @@ void input_free(struct input_ctx *); void input_reset(struct input_ctx *, int); struct evbuffer *input_pending(struct input_ctx *); void input_parse_pane(struct window_pane *); -void input_parse_buffer(struct window_pane *, u_char *, size_t); +void input_parse_buffer(struct window_pane *, const u_char *, size_t); void input_parse_screen(struct input_ctx *, struct screen *, - screen_write_init_ctx_cb, void *, u_char *, size_t); + screen_write_init_ctx_cb, void *, const u_char *, size_t); void input_reply_clipboard(struct bufferevent *, const char *, size_t, const char *, char); void input_set_buffer_size(size_t); @@ -3484,6 +3485,7 @@ typedef key_code (*mode_tree_key_cb)(void *, void *, u_int); typedef int (*mode_tree_swap_cb)(void *, void *, struct sort_criteria *); typedef void (*mode_tree_sort_cb)(struct sort_criteria *); typedef void (*mode_tree_each_cb)(void *, void *, struct client *, key_code); +typedef const char** (*mode_tree_help_cb)(u_int *, const char**); u_int mode_tree_count_tagged(struct mode_tree_data *); void *mode_tree_get_current(struct mode_tree_data *); const char *mode_tree_get_current_name(struct mode_tree_data *); @@ -3498,7 +3500,7 @@ int mode_tree_down(struct mode_tree_data *, int); struct mode_tree_data *mode_tree_start(struct window_pane *, struct args *, mode_tree_build_cb, mode_tree_draw_cb, mode_tree_search_cb, mode_tree_menu_cb, mode_tree_height_cb, mode_tree_key_cb, - mode_tree_swap_cb, mode_tree_sort_cb, void *, + mode_tree_swap_cb, mode_tree_sort_cb, mode_tree_help_cb, void *, const struct menu_item *, struct screen **); void mode_tree_zoom(struct mode_tree_data *, struct args *); void mode_tree_build(struct mode_tree_data *); @@ -3717,6 +3719,7 @@ int menu_key_cb(struct client *, void *, struct key_event *); #define POPUP_CLOSEEXITZERO 0x2 #define POPUP_INTERNAL 0x4 #define POPUP_CLOSEANYKEY 0x8 +#define POPUP_NOJOB 0x10 typedef void (*popup_close_cb)(int, void *); typedef void (*popup_finish_edit_cb)(char *, size_t, void *); int popup_display(int, enum box_lines, struct cmdq_item *, u_int, @@ -3724,6 +3727,7 @@ int popup_display(int, enum box_lines, struct cmdq_item *, u_int, char **, const char *, const char *, struct client *, struct session *, const char *, const char *, popup_close_cb, void *); +void popup_write(struct client *, const char *, size_t); int popup_editor(struct client *, const char *, size_t, popup_finish_edit_cb, void *); int popup_present(struct client *); diff --git a/window-buffer.c b/window-buffer.c index ca8d4630..ac604a34 100644 --- a/window-buffer.c +++ b/window-buffer.c @@ -329,6 +329,25 @@ window_buffer_sort(struct sort_criteria *sort_crit) sort_crit->order = sort_crit->order_seq[0]; } +static const char* window_buffer_help_lines[] = { + "\r\033[1m Enter \033[0m\016x\017 \033[0mPaste selected %1\n", + "\r\033[1m p \033[0m\016x\017 \033[0mPaste selected %1\n", + "\r\033[1m P \033[0m\016x\017 \033[0mPaste tagged %1s\n", + "\r\033[1m d \033[0m\016x\017 \033[0mDelete selected %1\n", + "\r\033[1m D \033[0m\016x\017 \033[0mDelete tagged %1s\n", + "\r\033[1m e \033[0m\016x\017 \033[0mOpen %1 in editor\n", + "\r\033[1m f \033[0m\016x\017 \033[0mEnter a filter\n", + NULL +}; + +static const char** +window_buffer_help(u_int *width, const char **item) +{ + *width = 0; + *item = "buffer"; + return (window_buffer_help_lines); +} + static struct screen * window_buffer_init(struct window_mode_entry *wme, struct cmd_find_state *fs, struct args *args) @@ -356,8 +375,8 @@ window_buffer_init(struct window_mode_entry *wme, struct cmd_find_state *fs, data->data = mode_tree_start(wp, args, window_buffer_build, window_buffer_draw, window_buffer_search, window_buffer_menu, NULL, - window_buffer_get_key, NULL, window_buffer_sort, data, - window_buffer_menu_items, &s); + window_buffer_get_key, NULL, window_buffer_sort, window_buffer_help, + data, window_buffer_menu_items, &s); mode_tree_zoom(data->data, args); mode_tree_build(data->data); diff --git a/window-client.c b/window-client.c index 9f2d5dce..be1fd5fa 100644 --- a/window-client.c +++ b/window-client.c @@ -240,6 +240,26 @@ window_client_sort(struct sort_criteria *sort_crit) sort_crit->order = sort_crit->order_seq[0]; } +static const char* window_client_help_lines[] = { + "\r\033[1m Enter \033[0m\016x\017 \033[0mChoose selected %1\n", + "\r\033[1m d \033[0m\016x\017 \033[0mDetach selected %1\n", + "\r\033[1m D \033[0m\016x\017 \033[0mDetach tagged %1s\n", + "\r\033[1m x \033[0m\016x\017 \033[0mDetach selected %1\n", + "\r\033[1m X \033[0m\016x\017 \033[0mDetach tagged %1s\n", + "\r\033[1m z \033[0m\016x\017 \033[0mSuspend selected %1\n", + "\r\033[1m Z \033[0m\016x\017 \033[0mSuspend tagged %1s\n", + "\r\033[1m f \033[0m\016x\017 \033[0mEnter a filter\n", + NULL +}; + +static const char** +window_client_help(u_int *width, const char **item) +{ + *width = 0; + *item = "client"; + return (window_client_help_lines); +} + static struct screen * window_client_init(struct window_mode_entry *wme, __unused struct cmd_find_state *fs, struct args *args) @@ -266,8 +286,8 @@ window_client_init(struct window_mode_entry *wme, data->data = mode_tree_start(wp, args, window_client_build, window_client_draw, NULL, window_client_menu, NULL, - window_client_get_key, NULL, window_client_sort, data, - window_client_menu_items, &s); + window_client_get_key, NULL, window_client_sort, + window_client_help, data, window_client_menu_items, &s); mode_tree_zoom(data->data, args); mode_tree_build(data->data); diff --git a/window-customize.c b/window-customize.c index 0887aa5c..3f78eca9 100644 --- a/window-customize.c +++ b/window-customize.c @@ -868,6 +868,27 @@ window_customize_height(__unused void *modedata, __unused u_int height) return (12); } +static const char* window_customize_help_lines[] = { + "\r\033[1m Enter, s \033[0m\016x\017 \033[0mSet %1 value\n", + "\r\033[1m S \033[0m\016x\017 \033[0mSet global %1 value\n", + "\r\033[1m w \033[0m\016x\017 \033[0mSet window %1 value\n", + "\r\033[1m d \033[0m\016x\017 \033[0mSet to default value\n", + "\r\033[1m D \033[0m\016x\017 \033[0mSet tagged %1s to default value\n", + "\r\033[1m u \033[0m\016x\017 \033[0mUnset an %1\n", + "\r\033[1m U \033[0m\016x\017 \033[0mUnset tagged %1s\n", + "\r\033[1m f \033[0m\016x\017 \033[0mEnter a filter\n", + "\r\033[1m v \033[0m\016x\017 \033[0mToggle information\n", + NULL +}; + +static const char** +window_customize_help(u_int *width, const char **item) +{ + *width = 52; + *item = "option"; + return (window_customize_help_lines); +} + static struct screen * window_customize_init(struct window_mode_entry *wme, struct cmd_find_state *fs, struct args *args) @@ -891,8 +912,8 @@ window_customize_init(struct window_mode_entry *wme, struct cmd_find_state *fs, data->data = mode_tree_start(wp, args, window_customize_build, window_customize_draw, NULL, window_customize_menu, - window_customize_height, NULL, NULL, NULL, data, - window_customize_menu_items, &s); + window_customize_height, NULL, NULL, NULL, window_customize_help, + data, window_customize_menu_items, &s); mode_tree_zoom(data->data, args); mode_tree_build(data->data); diff --git a/window-tree.c b/window-tree.c index 31bb6805..20877809 100644 --- a/window-tree.c +++ b/window-tree.c @@ -860,6 +860,30 @@ window_tree_sort(struct sort_criteria *sort_crit) sort_crit->order = sort_crit->order_seq[0]; } +static const char* window_tree_help_lines[] = { + "\r\033[1m Enter \033[0m\016x\017 \033[0mChoose selected item\n", + "\r\033[1m S-Up \033[0m\016x\017 \033[0mSwap current and previous window\n", + "\r\033[1m S-Down \033[0m\016x\017 \033[0mSwap current and next window\n", + "\r\033[1m x \033[0m\016x\017 \033[0mKill selected item\n", + "\r\033[1m X \033[0m\016x\017 \033[0mKill tagged items\n", + "\r\033[1m < \033[0m\016x\017 \033[0mScroll previews left\n", + "\r\033[1m > \033[0m\016x\017 \033[0mScroll previews right\n", + "\r\033[1m m \033[0m\016x\017 \033[0mSet the marked pane\n", + "\r\033[1m M \033[0m\016x\017 \033[0mClear the marked pane\n", + "\r\033[1m : \033[0m\016x\017 \033[0mRun a command for each tagged item\n", + "\r\033[1m f \033[0m\016x\017 \033[0mEnter a format\n", + "\r\033[1m H \033[0m\016x\017 \033[0mJump to the starting pane\n", + NULL +}; + +static const char** +window_tree_help(u_int *width, const char **item) +{ + *width = 51; + *item = "item"; + return (window_tree_help_lines); +} + static struct screen * window_tree_init(struct window_mode_entry *wme, struct cmd_find_state *fs, struct args *args) @@ -898,8 +922,8 @@ window_tree_init(struct window_mode_entry *wme, struct cmd_find_state *fs, data->data = mode_tree_start(wp, args, window_tree_build, window_tree_draw, window_tree_search, window_tree_menu, NULL, - window_tree_get_key, window_tree_swap, window_tree_sort, data, - window_tree_menu_items, &s); + window_tree_get_key, window_tree_swap, window_tree_sort, + window_tree_help, data, window_tree_menu_items, &s); mode_tree_zoom(data->data, args); mode_tree_build(data->data);