4 * Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>
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.
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.
19 #include <sys/types.h>
32 static void status_message_callback(int, short, void *);
33 static void status_timer_callback(int, short, void *);
35 static char *status_prompt_find_history_file(void);
36 static const char *status_prompt_up_history(u_int
*, u_int
);
37 static const char *status_prompt_down_history(u_int
*, u_int
);
38 static void status_prompt_add_history(const char *, u_int
);
40 static char *status_prompt_complete(struct client
*, const char *, u_int
);
41 static char *status_prompt_complete_window_menu(struct client
*,
42 struct session
*, const char *, u_int
, char);
44 struct status_prompt_menu
{
52 static const char *prompt_type_strings
[] = {
59 /* Status prompt history. */
60 char **status_prompt_hlist
[PROMPT_NTYPES
];
61 u_int status_prompt_hsize
[PROMPT_NTYPES
];
63 /* Find the history file to load/save from/to. */
65 status_prompt_find_history_file(void)
67 const char *home
, *history_file
;
70 history_file
= options_get_string(global_options
, "history-file");
71 if (*history_file
== '\0')
73 if (*history_file
== '/')
74 return (xstrdup(history_file
));
76 if (history_file
[0] != '~' || history_file
[1] != '/')
78 if ((home
= find_home()) == NULL
)
80 xasprintf(&path
, "%s%s", home
, history_file
+ 1);
84 /* Add loaded history item to the appropriate list. */
86 status_prompt_add_typed_history(char *line
)
89 enum prompt_type type
= PROMPT_TYPE_INVALID
;
91 typestr
= strsep(&line
, ":");
93 type
= status_prompt_type(typestr
);
94 if (type
== PROMPT_TYPE_INVALID
) {
96 * Invalid types are not expected, but this provides backward
97 * compatibility with old history files.
101 status_prompt_add_history(typestr
, PROMPT_TYPE_COMMAND
);
103 status_prompt_add_history(line
, type
);
106 /* Load status prompt history from file. */
108 status_prompt_load_history(void)
111 char *history_file
, *line
, *tmp
;
114 if ((history_file
= status_prompt_find_history_file()) == NULL
)
116 log_debug("loading history from %s", history_file
);
118 f
= fopen(history_file
, "r");
120 log_debug("%s: %s", history_file
, strerror(errno
));
127 if ((line
= fgetln(f
, &length
)) == NULL
)
131 if (line
[length
- 1] == '\n') {
132 line
[length
- 1] = '\0';
133 status_prompt_add_typed_history(line
);
135 tmp
= xmalloc(length
+ 1);
136 memcpy(tmp
, line
, length
);
138 status_prompt_add_typed_history(tmp
);
146 /* Save status prompt history to file. */
148 status_prompt_save_history(void)
154 if ((history_file
= status_prompt_find_history_file()) == NULL
)
156 log_debug("saving history to %s", history_file
);
158 f
= fopen(history_file
, "w");
160 log_debug("%s: %s", history_file
, strerror(errno
));
166 for (type
= 0; type
< PROMPT_NTYPES
; type
++) {
167 for (i
= 0; i
< status_prompt_hsize
[type
]; i
++) {
168 fputs(prompt_type_strings
[type
], f
);
170 fputs(status_prompt_hlist
[type
][i
], f
);
178 /* Status timer callback. */
180 status_timer_callback(__unused
int fd
, __unused
short events
, void *arg
)
182 struct client
*c
= arg
;
183 struct session
*s
= c
->session
;
186 evtimer_del(&c
->status
.timer
);
191 if (c
->message_string
== NULL
&& c
->prompt_string
== NULL
)
192 c
->flags
|= CLIENT_REDRAWSTATUS
;
195 tv
.tv_sec
= options_get_number(s
->options
, "status-interval");
198 evtimer_add(&c
->status
.timer
, &tv
);
199 log_debug("client %p, status interval %d", c
, (int)tv
.tv_sec
);
202 /* Start status timer for client. */
204 status_timer_start(struct client
*c
)
206 struct session
*s
= c
->session
;
208 if (event_initialized(&c
->status
.timer
))
209 evtimer_del(&c
->status
.timer
);
211 evtimer_set(&c
->status
.timer
, status_timer_callback
, c
);
213 if (s
!= NULL
&& options_get_number(s
->options
, "status"))
214 status_timer_callback(-1, 0, c
);
217 /* Start status timer for all clients. */
219 status_timer_start_all(void)
223 TAILQ_FOREACH(c
, &clients
, entry
)
224 status_timer_start(c
);
227 /* Update status cache. */
229 status_update_cache(struct session
*s
)
231 s
->statuslines
= options_get_number(s
->options
, "status");
232 if (s
->statuslines
== 0)
234 else if (options_get_number(s
->options
, "status-position") == 0)
240 /* Get screen line of status line. -1 means off. */
242 status_at_line(struct client
*c
)
244 struct session
*s
= c
->session
;
246 if (c
->flags
& (CLIENT_STATUSOFF
|CLIENT_CONTROL
))
248 if (s
->statusat
!= 1)
249 return (s
->statusat
);
250 return (c
->tty
.sy
- status_line_size(c
));
253 /* Get size of status line for client's session. 0 means off. */
255 status_line_size(struct client
*c
)
257 struct session
*s
= c
->session
;
259 if (c
->flags
& (CLIENT_STATUSOFF
|CLIENT_CONTROL
))
262 return (options_get_number(global_s_options
, "status"));
263 return (s
->statuslines
);
266 /* Get the prompt line number for client's session. 1 means at the bottom. */
268 status_prompt_line_at(struct client
*c
)
270 struct session
*s
= c
->session
;
272 if (c
->flags
& (CLIENT_STATUSOFF
|CLIENT_CONTROL
))
274 return (options_get_number(s
->options
, "message-line"));
277 /* Get window at window list position. */
279 status_get_range(struct client
*c
, u_int x
, u_int y
)
281 struct status_line
*sl
= &c
->status
;
282 struct style_range
*sr
;
284 if (y
>= nitems(sl
->entries
))
286 TAILQ_FOREACH(sr
, &sl
->entries
[y
].ranges
, entry
) {
287 if (x
>= sr
->start
&& x
< sr
->end
)
293 /* Free all ranges. */
295 status_free_ranges(struct style_ranges
*srs
)
297 struct style_range
*sr
, *sr1
;
299 TAILQ_FOREACH_SAFE(sr
, srs
, entry
, sr1
) {
300 TAILQ_REMOVE(srs
, sr
, entry
);
305 /* Save old status line. */
307 status_push_screen(struct client
*c
)
309 struct status_line
*sl
= &c
->status
;
311 if (sl
->active
== &sl
->screen
) {
312 sl
->active
= xmalloc(sizeof *sl
->active
);
313 screen_init(sl
->active
, c
->tty
.sx
, status_line_size(c
), 0);
318 /* Restore old status line. */
320 status_pop_screen(struct client
*c
)
322 struct status_line
*sl
= &c
->status
;
324 if (--sl
->references
== 0) {
325 screen_free(sl
->active
);
327 sl
->active
= &sl
->screen
;
331 /* Initialize status line. */
333 status_init(struct client
*c
)
335 struct status_line
*sl
= &c
->status
;
338 for (i
= 0; i
< nitems(sl
->entries
); i
++)
339 TAILQ_INIT(&sl
->entries
[i
].ranges
);
341 screen_init(&sl
->screen
, c
->tty
.sx
, 1, 0);
342 sl
->active
= &sl
->screen
;
345 /* Free status line. */
347 status_free(struct client
*c
)
349 struct status_line
*sl
= &c
->status
;
352 for (i
= 0; i
< nitems(sl
->entries
); i
++) {
353 status_free_ranges(&sl
->entries
[i
].ranges
);
354 free((void *)sl
->entries
[i
].expanded
);
357 if (event_initialized(&sl
->timer
))
358 evtimer_del(&sl
->timer
);
360 if (sl
->active
!= &sl
->screen
) {
361 screen_free(sl
->active
);
364 screen_free(&sl
->screen
);
367 /* Draw status line for client. */
369 status_redraw(struct client
*c
)
371 struct status_line
*sl
= &c
->status
;
372 struct status_line_entry
*sle
;
373 struct session
*s
= c
->session
;
374 struct screen_write_ctx ctx
;
376 u_int lines
, i
, n
, width
= c
->tty
.sx
;
377 int flags
, force
= 0, changed
= 0, fg
, bg
;
378 struct options_entry
*o
;
379 union options_value
*ov
;
380 struct format_tree
*ft
;
383 log_debug("%s enter", __func__
);
385 /* Shouldn't get here if not the active screen. */
386 if (sl
->active
!= &sl
->screen
)
387 fatalx("not the active screen");
389 /* No status line? */
390 lines
= status_line_size(c
);
391 if (c
->tty
.sy
== 0 || lines
== 0)
394 /* Create format tree. */
395 flags
= FORMAT_STATUS
;
396 if (c
->flags
& CLIENT_STATUSFORCE
)
397 flags
|= FORMAT_FORCE
;
398 ft
= format_create(c
, NULL
, FORMAT_NONE
, flags
);
399 format_defaults(ft
, c
, NULL
, NULL
, NULL
);
401 /* Set up default colour. */
402 style_apply(&gc
, s
->options
, "status-style", ft
);
403 fg
= options_get_number(s
->options
, "status-fg");
404 if (!COLOUR_DEFAULT(fg
))
406 bg
= options_get_number(s
->options
, "status-bg");
407 if (!COLOUR_DEFAULT(bg
))
409 if (!grid_cells_equal(&gc
, &sl
->style
)) {
411 memcpy(&sl
->style
, &gc
, sizeof sl
->style
);
414 /* Resize the target screen. */
415 if (screen_size_x(&sl
->screen
) != width
||
416 screen_size_y(&sl
->screen
) != lines
) {
417 screen_resize(&sl
->screen
, width
, lines
, 0);
420 screen_write_start(&ctx
, &sl
->screen
);
422 /* Write the status lines. */
423 o
= options_get(s
->options
, "status-format");
425 for (n
= 0; n
< width
* lines
; n
++)
426 screen_write_putc(&ctx
, &gc
, ' ');
428 for (i
= 0; i
< lines
; i
++) {
429 screen_write_cursormove(&ctx
, 0, i
, 0);
431 ov
= options_array_get(o
, i
);
433 for (n
= 0; n
< width
; n
++)
434 screen_write_putc(&ctx
, &gc
, ' ');
437 sle
= &sl
->entries
[i
];
439 expanded
= format_expand_time(ft
, ov
->string
);
441 sle
->expanded
!= NULL
&&
442 strcmp(expanded
, sle
->expanded
) == 0) {
448 for (n
= 0; n
< width
; n
++)
449 screen_write_putc(&ctx
, &gc
, ' ');
450 screen_write_cursormove(&ctx
, 0, i
, 0);
452 status_free_ranges(&sle
->ranges
);
453 format_draw(&ctx
, &gc
, width
, expanded
, &sle
->ranges
,
457 sle
->expanded
= expanded
;
460 screen_write_stop(&ctx
);
462 /* Free the format tree. */
465 /* Return if the status line has changed. */
466 log_debug("%s exit: force=%d, changed=%d", __func__
, force
, changed
);
467 return (force
|| changed
);
470 /* Set a status line message. */
472 status_message_set(struct client
*c
, int delay
, int ignore_styles
,
473 int ignore_keys
, const char *fmt
, ...)
480 xvasprintf(&s
, fmt
, ap
);
483 log_debug("%s: %s", __func__
, s
);
486 server_add_message("message: %s", s
);
491 status_message_clear(c
);
492 status_push_screen(c
);
493 c
->message_string
= s
;
494 server_add_message("%s message: %s", c
->name
, s
);
497 * With delay -1, the display-time option is used; zero means wait for
498 * key press; more than zero is the actual delay time in milliseconds.
501 delay
= options_get_number(c
->session
->options
, "display-time");
503 tv
.tv_sec
= delay
/ 1000;
504 tv
.tv_usec
= (delay
% 1000) * 1000L;
506 if (event_initialized(&c
->message_timer
))
507 evtimer_del(&c
->message_timer
);
508 evtimer_set(&c
->message_timer
, status_message_callback
, c
);
510 evtimer_add(&c
->message_timer
, &tv
);
514 c
->message_ignore_keys
= ignore_keys
;
515 c
->message_ignore_styles
= ignore_styles
;
517 c
->tty
.flags
|= (TTY_NOCURSOR
|TTY_FREEZE
);
518 c
->flags
|= CLIENT_REDRAWSTATUS
;
521 /* Clear status line message. */
523 status_message_clear(struct client
*c
)
525 if (c
->message_string
== NULL
)
528 free(c
->message_string
);
529 c
->message_string
= NULL
;
531 if (c
->prompt_string
== NULL
)
532 c
->tty
.flags
&= ~(TTY_NOCURSOR
|TTY_FREEZE
);
533 c
->flags
|= CLIENT_ALLREDRAWFLAGS
; /* was frozen and may have changed */
535 status_pop_screen(c
);
538 /* Clear status line message after timer expires. */
540 status_message_callback(__unused
int fd
, __unused
short event
, void *data
)
542 struct client
*c
= data
;
544 status_message_clear(c
);
547 /* Draw client message on status line of present else on last line. */
549 status_message_redraw(struct client
*c
)
551 struct status_line
*sl
= &c
->status
;
552 struct screen_write_ctx ctx
;
553 struct session
*s
= c
->session
;
554 struct screen old_screen
;
556 u_int lines
, offset
, messageline
;
558 struct format_tree
*ft
;
560 if (c
->tty
.sx
== 0 || c
->tty
.sy
== 0)
562 memcpy(&old_screen
, sl
->active
, sizeof old_screen
);
564 lines
= status_line_size(c
);
567 screen_init(sl
->active
, c
->tty
.sx
, lines
, 0);
569 messageline
= status_prompt_line_at(c
);
570 if (messageline
> lines
- 1)
571 messageline
= lines
- 1;
573 len
= screen_write_strlen("%s", c
->message_string
);
577 ft
= format_create_defaults(NULL
, c
, NULL
, NULL
, NULL
);
578 style_apply(&gc
, s
->options
, "message-style", ft
);
581 screen_write_start(&ctx
, sl
->active
);
582 screen_write_fast_copy(&ctx
, &sl
->screen
, 0, 0, c
->tty
.sx
, lines
);
583 screen_write_cursormove(&ctx
, 0, messageline
, 0);
584 for (offset
= 0; offset
< c
->tty
.sx
; offset
++)
585 screen_write_putc(&ctx
, &gc
, ' ');
586 screen_write_cursormove(&ctx
, 0, messageline
, 0);
587 if (c
->message_ignore_styles
)
588 screen_write_nputs(&ctx
, len
, &gc
, "%s", c
->message_string
);
590 format_draw(&ctx
, &gc
, c
->tty
.sx
, c
->message_string
, NULL
, 0);
591 screen_write_stop(&ctx
);
593 if (grid_compare(sl
->active
->grid
, old_screen
.grid
) == 0) {
594 screen_free(&old_screen
);
597 screen_free(&old_screen
);
601 /* Accept prompt immediately. */
602 static enum cmd_retval
603 status_prompt_accept(__unused
struct cmdq_item
*item
, void *data
)
605 struct client
*c
= data
;
607 if (c
->prompt_string
!= NULL
) {
608 c
->prompt_inputcb(c
, c
->prompt_data
, "y", 1);
609 status_prompt_clear(c
);
611 return (CMD_RETURN_NORMAL
);
614 /* Enable status line prompt. */
616 status_prompt_set(struct client
*c
, struct cmd_find_state
*fs
,
617 const char *msg
, const char *input
, prompt_input_cb inputcb
,
618 prompt_free_cb freecb
, void *data
, int flags
, enum prompt_type prompt_type
)
620 struct format_tree
*ft
;
623 server_client_clear_overlay(c
);
626 ft
= format_create_from_state(NULL
, c
, fs
);
628 ft
= format_create_defaults(NULL
, c
, NULL
, NULL
, NULL
);
632 if (flags
& PROMPT_NOFORMAT
)
633 tmp
= xstrdup(input
);
635 tmp
= format_expand_time(ft
, input
);
637 status_message_clear(c
);
638 status_prompt_clear(c
);
639 status_push_screen(c
);
641 c
->prompt_string
= format_expand_time(ft
, msg
);
643 if (flags
& PROMPT_INCREMENTAL
) {
644 c
->prompt_last
= xstrdup(tmp
);
645 c
->prompt_buffer
= utf8_fromcstr("");
647 c
->prompt_last
= NULL
;
648 c
->prompt_buffer
= utf8_fromcstr(tmp
);
650 c
->prompt_index
= utf8_strlen(c
->prompt_buffer
);
652 c
->prompt_inputcb
= inputcb
;
653 c
->prompt_freecb
= freecb
;
654 c
->prompt_data
= data
;
656 memset(c
->prompt_hindex
, 0, sizeof c
->prompt_hindex
);
658 c
->prompt_flags
= flags
;
659 c
->prompt_type
= prompt_type
;
660 c
->prompt_mode
= PROMPT_ENTRY
;
662 if (~flags
& PROMPT_INCREMENTAL
)
663 c
->tty
.flags
|= TTY_FREEZE
;
664 c
->flags
|= CLIENT_REDRAWSTATUS
;
666 if (flags
& PROMPT_INCREMENTAL
)
667 c
->prompt_inputcb(c
, c
->prompt_data
, "=", 0);
672 if ((flags
& PROMPT_SINGLE
) && (flags
& PROMPT_ACCEPT
))
673 cmdq_append(c
, cmdq_get_callback(status_prompt_accept
, c
));
676 /* Remove status line prompt. */
678 status_prompt_clear(struct client
*c
)
680 if (c
->prompt_string
== NULL
)
683 if (c
->prompt_freecb
!= NULL
&& c
->prompt_data
!= NULL
)
684 c
->prompt_freecb(c
->prompt_data
);
686 free(c
->prompt_last
);
687 c
->prompt_last
= NULL
;
689 free(c
->prompt_string
);
690 c
->prompt_string
= NULL
;
692 free(c
->prompt_buffer
);
693 c
->prompt_buffer
= NULL
;
695 free(c
->prompt_saved
);
696 c
->prompt_saved
= NULL
;
698 c
->tty
.flags
&= ~(TTY_NOCURSOR
|TTY_FREEZE
);
699 c
->flags
|= CLIENT_ALLREDRAWFLAGS
; /* was frozen and may have changed */
701 status_pop_screen(c
);
704 /* Update status line prompt with a new prompt string. */
706 status_prompt_update(struct client
*c
, const char *msg
, const char *input
)
708 struct format_tree
*ft
;
711 ft
= format_create(c
, NULL
, FORMAT_NONE
, 0);
712 format_defaults(ft
, c
, NULL
, NULL
, NULL
);
714 tmp
= format_expand_time(ft
, input
);
716 free(c
->prompt_string
);
717 c
->prompt_string
= format_expand_time(ft
, msg
);
719 free(c
->prompt_buffer
);
720 c
->prompt_buffer
= utf8_fromcstr(tmp
);
721 c
->prompt_index
= utf8_strlen(c
->prompt_buffer
);
723 memset(c
->prompt_hindex
, 0, sizeof c
->prompt_hindex
);
725 c
->flags
|= CLIENT_REDRAWSTATUS
;
731 /* Redraw character. Return 1 if can continue redrawing, 0 otherwise. */
733 status_prompt_redraw_character(struct screen_write_ctx
*ctx
, u_int offset
,
734 u_int pwidth
, u_int
*width
, struct grid_cell
*gc
,
735 const struct utf8_data
*ud
)
739 if (*width
< offset
) {
743 if (*width
>= offset
+ pwidth
)
746 if (*width
> offset
+ pwidth
)
750 if (ud
->size
== 1 && (ch
<= 0x1f || ch
== 0x7f)) {
751 gc
->data
.data
[0] = '^';
752 gc
->data
.data
[1] = (ch
== 0x7f) ? '?' : ch
|0x40;
753 gc
->data
.size
= gc
->data
.have
= 2;
756 utf8_copy(&gc
->data
, ud
);
757 screen_write_cell(ctx
, gc
);
762 * Redraw quote indicator '^' if necessary. Return 1 if can continue redrawing,
766 status_prompt_redraw_quote(const struct client
*c
, u_int pcursor
,
767 struct screen_write_ctx
*ctx
, u_int offset
, u_int pwidth
, u_int
*width
,
768 struct grid_cell
*gc
)
772 if (c
->prompt_flags
& PROMPT_QUOTENEXT
&& ctx
->s
->cx
== pcursor
+ 1) {
774 return (status_prompt_redraw_character(ctx
, offset
, pwidth
,
780 /* Draw client prompt on status line of present else on last line. */
782 status_prompt_redraw(struct client
*c
)
784 struct status_line
*sl
= &c
->status
;
785 struct screen_write_ctx ctx
;
786 struct session
*s
= c
->session
;
787 struct screen old_screen
;
788 u_int i
, lines
, offset
, left
, start
, width
, n
;
789 u_int pcursor
, pwidth
, promptline
;
791 struct format_tree
*ft
;
793 if (c
->tty
.sx
== 0 || c
->tty
.sy
== 0)
795 memcpy(&old_screen
, sl
->active
, sizeof old_screen
);
797 lines
= status_line_size(c
);
800 screen_init(sl
->active
, c
->tty
.sx
, lines
, 0);
802 n
= options_get_number(s
->options
, "prompt-cursor-colour");
803 sl
->active
->default_ccolour
= n
;
804 n
= options_get_number(s
->options
, "prompt-cursor-style");
805 screen_set_cursor_style(n
, &sl
->active
->default_cstyle
,
806 &sl
->active
->default_mode
);
808 promptline
= status_prompt_line_at(c
);
809 if (promptline
> lines
- 1)
810 promptline
= lines
- 1;
812 ft
= format_create_defaults(NULL
, c
, NULL
, NULL
, NULL
);
813 if (c
->prompt_mode
== PROMPT_COMMAND
)
814 style_apply(&gc
, s
->options
, "message-command-style", ft
);
816 style_apply(&gc
, s
->options
, "message-style", ft
);
819 start
= format_width(c
->prompt_string
);
820 if (start
> c
->tty
.sx
)
823 screen_write_start(&ctx
, sl
->active
);
824 screen_write_fast_copy(&ctx
, &sl
->screen
, 0, 0, c
->tty
.sx
, lines
);
825 screen_write_cursormove(&ctx
, 0, promptline
, 0);
826 for (offset
= 0; offset
< c
->tty
.sx
; offset
++)
827 screen_write_putc(&ctx
, &gc
, ' ');
828 screen_write_cursormove(&ctx
, 0, promptline
, 0);
829 format_draw(&ctx
, &gc
, start
, c
->prompt_string
, NULL
, 0);
830 screen_write_cursormove(&ctx
, start
, promptline
, 0);
832 left
= c
->tty
.sx
- start
;
836 pcursor
= utf8_strwidth(c
->prompt_buffer
, c
->prompt_index
);
837 pwidth
= utf8_strwidth(c
->prompt_buffer
, -1);
838 if (c
->prompt_flags
& PROMPT_QUOTENEXT
)
840 if (pcursor
>= left
) {
842 * The cursor would be outside the screen so start drawing
843 * with it on the right.
845 offset
= (pcursor
- left
) + 1;
851 c
->prompt_cursor
= start
+ pcursor
- offset
;
854 for (i
= 0; c
->prompt_buffer
[i
].size
!= 0; i
++) {
855 if (!status_prompt_redraw_quote(c
, pcursor
, &ctx
, offset
,
856 pwidth
, &width
, &gc
))
858 if (!status_prompt_redraw_character(&ctx
, offset
, pwidth
,
859 &width
, &gc
, &c
->prompt_buffer
[i
]))
862 status_prompt_redraw_quote(c
, pcursor
, &ctx
, offset
, pwidth
, &width
,
866 screen_write_stop(&ctx
);
868 if (grid_compare(sl
->active
->grid
, old_screen
.grid
) == 0) {
869 screen_free(&old_screen
);
872 screen_free(&old_screen
);
876 /* Is this a separator? */
878 status_prompt_in_list(const char *ws
, const struct utf8_data
*ud
)
880 if (ud
->size
!= 1 || ud
->width
!= 1)
882 return (strchr(ws
, *ud
->data
) != NULL
);
885 /* Is this a space? */
887 status_prompt_space(const struct utf8_data
*ud
)
889 if (ud
->size
!= 1 || ud
->width
!= 1)
891 return (*ud
->data
== ' ');
895 * Translate key from vi to emacs. Return 0 to drop key, 1 to process the key
896 * as an emacs key; return 2 to append to the buffer.
899 status_prompt_translate_key(struct client
*c
, key_code key
, key_code
*new_key
)
901 if (c
->prompt_mode
== PROMPT_ENTRY
) {
908 case '\011': /* Tab */
919 case KEYC_LEFT
|KEYC_CTRL
:
920 case KEYC_RIGHT
|KEYC_CTRL
:
931 case '\033': /* Escape */
932 c
->prompt_mode
= PROMPT_COMMAND
;
933 c
->flags
|= CLIENT_REDRAWSTATUS
;
942 *new_key
= KEYC_LEFT
;
949 c
->prompt_mode
= PROMPT_ENTRY
;
950 c
->flags
|= CLIENT_REDRAWSTATUS
;
951 break; /* switch mode and... */
953 c
->prompt_mode
= PROMPT_ENTRY
;
954 c
->flags
|= CLIENT_REDRAWSTATUS
;
955 *new_key
= 'u'|KEYC_CTRL
;
958 case '\033': /* Escape */
959 c
->prompt_mode
= PROMPT_ENTRY
;
960 c
->flags
|= CLIENT_REDRAWSTATUS
;
972 *new_key
= KEYC_HOME
;
976 *new_key
= 'k'|KEYC_CTRL
;
980 *new_key
= KEYC_BSPACE
;
983 *new_key
= 'b'|KEYC_META
;
986 *new_key
= 'B'|KEYC_VI
;
989 *new_key
= 'u'|KEYC_CTRL
;
992 *new_key
= 'e'|KEYC_VI
;
995 *new_key
= 'E'|KEYC_VI
;
998 *new_key
= 'w'|KEYC_VI
;
1001 *new_key
= 'W'|KEYC_VI
;
1004 *new_key
= 'y'|KEYC_CTRL
;
1007 *new_key
= 'c'|KEYC_CTRL
;
1016 *new_key
= KEYC_DOWN
;
1020 *new_key
= KEYC_LEFT
;
1025 *new_key
= KEYC_RIGHT
;
1040 /* Paste into prompt. */
1042 status_prompt_paste(struct client
*c
)
1044 struct paste_buffer
*pb
;
1045 const char *bufdata
;
1046 size_t size
, n
, bufsize
;
1048 struct utf8_data
*ud
, *udp
;
1049 enum utf8_state more
;
1051 size
= utf8_strlen(c
->prompt_buffer
);
1052 if (c
->prompt_saved
!= NULL
) {
1053 ud
= c
->prompt_saved
;
1054 n
= utf8_strlen(c
->prompt_saved
);
1056 if ((pb
= paste_get_top(NULL
)) == NULL
)
1058 bufdata
= paste_buffer_data(pb
, &bufsize
);
1059 ud
= udp
= xreallocarray(NULL
, bufsize
+ 1, sizeof *ud
);
1060 for (i
= 0; i
!= bufsize
; /* nothing */) {
1061 more
= utf8_open(udp
, bufdata
[i
]);
1062 if (more
== UTF8_MORE
) {
1063 while (++i
!= bufsize
&& more
== UTF8_MORE
)
1064 more
= utf8_append(udp
, bufdata
[i
]);
1065 if (more
== UTF8_DONE
) {
1071 if (bufdata
[i
] <= 31 || bufdata
[i
] >= 127)
1073 utf8_set(udp
, bufdata
[i
]);
1081 c
->prompt_buffer
= xreallocarray(c
->prompt_buffer
, size
+ n
+ 1,
1082 sizeof *c
->prompt_buffer
);
1083 if (c
->prompt_index
== size
) {
1084 memcpy(c
->prompt_buffer
+ c
->prompt_index
, ud
,
1085 n
* sizeof *c
->prompt_buffer
);
1086 c
->prompt_index
+= n
;
1087 c
->prompt_buffer
[c
->prompt_index
].size
= 0;
1089 memmove(c
->prompt_buffer
+ c
->prompt_index
+ n
,
1090 c
->prompt_buffer
+ c
->prompt_index
,
1091 (size
+ 1 - c
->prompt_index
) *
1092 sizeof *c
->prompt_buffer
);
1093 memcpy(c
->prompt_buffer
+ c
->prompt_index
, ud
,
1094 n
* sizeof *c
->prompt_buffer
);
1095 c
->prompt_index
+= n
;
1098 if (ud
!= c
->prompt_saved
)
1103 /* Finish completion. */
1105 status_prompt_replace_complete(struct client
*c
, const char *s
)
1107 char word
[64], *allocated
= NULL
;
1108 size_t size
, n
, off
, idx
, used
;
1109 struct utf8_data
*first
, *last
, *ud
;
1111 /* Work out where the cursor currently is. */
1112 idx
= c
->prompt_index
;
1115 size
= utf8_strlen(c
->prompt_buffer
);
1117 /* Find the word we are in. */
1118 first
= &c
->prompt_buffer
[idx
];
1119 while (first
> c
->prompt_buffer
&& !status_prompt_space(first
))
1121 while (first
->size
!= 0 && status_prompt_space(first
))
1123 last
= &c
->prompt_buffer
[idx
];
1124 while (last
->size
!= 0 && !status_prompt_space(last
))
1126 while (last
> c
->prompt_buffer
&& status_prompt_space(last
))
1128 if (last
->size
!= 0)
1134 for (ud
= first
; ud
< last
; ud
++) {
1135 if (used
+ ud
->size
>= sizeof word
)
1137 memcpy(word
+ used
, ud
->data
, ud
->size
);
1145 /* Try to complete it. */
1147 allocated
= status_prompt_complete(c
, word
,
1148 first
- c
->prompt_buffer
);
1149 if (allocated
== NULL
)
1154 /* Trim out word. */
1155 n
= size
- (last
- c
->prompt_buffer
) + 1; /* with \0 */
1156 memmove(first
, last
, n
* sizeof *c
->prompt_buffer
);
1157 size
-= last
- first
;
1159 /* Insert the new word. */
1161 off
= first
- c
->prompt_buffer
;
1162 c
->prompt_buffer
= xreallocarray(c
->prompt_buffer
, size
+ 1,
1163 sizeof *c
->prompt_buffer
);
1164 first
= c
->prompt_buffer
+ off
;
1165 memmove(first
+ strlen(s
), first
, n
* sizeof *c
->prompt_buffer
);
1166 for (idx
= 0; idx
< strlen(s
); idx
++)
1167 utf8_set(&first
[idx
], s
[idx
]);
1168 c
->prompt_index
= (first
- c
->prompt_buffer
) + strlen(s
);
1174 /* Prompt forward to the next beginning of a word. */
1176 status_prompt_forward_word(struct client
*c
, size_t size
, int vi
,
1177 const char *separators
)
1179 size_t idx
= c
->prompt_index
;
1180 int word_is_separators
;
1182 /* In emacs mode, skip until the first non-whitespace character. */
1184 while (idx
!= size
&&
1185 status_prompt_space(&c
->prompt_buffer
[idx
]))
1188 /* Can't move forward if we're already at the end. */
1190 c
->prompt_index
= idx
;
1194 /* Determine the current character class (separators or not). */
1195 word_is_separators
= status_prompt_in_list(separators
,
1196 &c
->prompt_buffer
[idx
]) &&
1197 !status_prompt_space(&c
->prompt_buffer
[idx
]);
1199 /* Skip ahead until the first space or opposite character class. */
1202 if (status_prompt_space(&c
->prompt_buffer
[idx
])) {
1203 /* In vi mode, go to the start of the next word. */
1205 while (idx
!= size
&&
1206 status_prompt_space(&c
->prompt_buffer
[idx
]))
1210 } while (idx
!= size
&& word_is_separators
== status_prompt_in_list(
1211 separators
, &c
->prompt_buffer
[idx
]));
1213 c
->prompt_index
= idx
;
1216 /* Prompt forward to the next end of a word. */
1218 status_prompt_end_word(struct client
*c
, size_t size
, const char *separators
)
1220 size_t idx
= c
->prompt_index
;
1221 int word_is_separators
;
1223 /* Can't move forward if we're already at the end. */
1227 /* Find the next word. */
1231 c
->prompt_index
= idx
;
1234 } while (status_prompt_space(&c
->prompt_buffer
[idx
]));
1236 /* Determine the character class (separators or not). */
1237 word_is_separators
= status_prompt_in_list(separators
,
1238 &c
->prompt_buffer
[idx
]);
1240 /* Skip ahead until the next space or opposite character class. */
1245 } while (!status_prompt_space(&c
->prompt_buffer
[idx
]) &&
1246 word_is_separators
== status_prompt_in_list(separators
,
1247 &c
->prompt_buffer
[idx
]));
1249 /* Back up to the previous character to stop at the end of the word. */
1250 c
->prompt_index
= idx
- 1;
1253 /* Prompt backward to the previous beginning of a word. */
1255 status_prompt_backward_word(struct client
*c
, const char *separators
)
1257 size_t idx
= c
->prompt_index
;
1258 int word_is_separators
;
1260 /* Find non-whitespace. */
1263 if (!status_prompt_space(&c
->prompt_buffer
[idx
]))
1266 word_is_separators
= status_prompt_in_list(separators
,
1267 &c
->prompt_buffer
[idx
]);
1269 /* Find the character before the beginning of the word. */
1272 if (status_prompt_space(&c
->prompt_buffer
[idx
]) ||
1273 word_is_separators
!= status_prompt_in_list(separators
,
1274 &c
->prompt_buffer
[idx
])) {
1275 /* Go back to the word. */
1280 c
->prompt_index
= idx
;
1283 /* Handle keys in prompt. */
1285 status_prompt_key(struct client
*c
, key_code key
)
1287 struct options
*oo
= c
->session
->options
;
1288 char *s
, *cp
, prefix
= '=';
1289 const char *histstr
, *separators
= NULL
, *keystring
;
1291 struct utf8_data tmp
;
1292 int keys
, word_is_separators
;
1294 if (c
->prompt_flags
& PROMPT_KEY
) {
1295 keystring
= key_string_lookup_key(key
, 0);
1296 c
->prompt_inputcb(c
, c
->prompt_data
, keystring
, 1);
1297 status_prompt_clear(c
);
1300 size
= utf8_strlen(c
->prompt_buffer
);
1302 if (c
->prompt_flags
& PROMPT_NUMERIC
) {
1303 if (key
>= '0' && key
<= '9')
1305 s
= utf8_tocstr(c
->prompt_buffer
);
1306 c
->prompt_inputcb(c
, c
->prompt_data
, s
, 1);
1307 status_prompt_clear(c
);
1311 key
&= ~KEYC_MASK_FLAGS
;
1313 if (c
->prompt_flags
& (PROMPT_SINGLE
|PROMPT_QUOTENEXT
)) {
1314 if ((key
& KEYC_MASK_KEY
) == KEYC_BSPACE
)
1316 else if ((key
& KEYC_MASK_KEY
) > 0x7f) {
1317 if (!KEYC_IS_UNICODE(key
))
1319 key
&= KEYC_MASK_KEY
;
1321 key
&= (key
& KEYC_CTRL
) ? 0x1f : KEYC_MASK_KEY
;
1322 c
->prompt_flags
&= ~PROMPT_QUOTENEXT
;
1326 keys
= options_get_number(c
->session
->options
, "status-keys");
1327 if (keys
== MODEKEY_VI
) {
1328 switch (status_prompt_translate_key(c
, key
, &key
)) {
1342 if (c
->prompt_index
> 0) {
1349 if (c
->prompt_index
< size
) {
1356 if (c
->prompt_index
!= 0) {
1357 c
->prompt_index
= 0;
1363 if (c
->prompt_index
!= size
) {
1364 c
->prompt_index
= size
;
1368 case '\011': /* Tab */
1369 if (status_prompt_replace_complete(c
, NULL
))
1374 if (c
->prompt_index
!= 0) {
1375 if (c
->prompt_index
== size
)
1376 c
->prompt_buffer
[--c
->prompt_index
].size
= 0;
1378 memmove(c
->prompt_buffer
+ c
->prompt_index
- 1,
1379 c
->prompt_buffer
+ c
->prompt_index
,
1380 (size
+ 1 - c
->prompt_index
) *
1381 sizeof *c
->prompt_buffer
);
1389 if (c
->prompt_index
!= size
) {
1390 memmove(c
->prompt_buffer
+ c
->prompt_index
,
1391 c
->prompt_buffer
+ c
->prompt_index
+ 1,
1392 (size
+ 1 - c
->prompt_index
) *
1393 sizeof *c
->prompt_buffer
);
1398 c
->prompt_buffer
[0].size
= 0;
1399 c
->prompt_index
= 0;
1402 if (c
->prompt_index
< size
) {
1403 c
->prompt_buffer
[c
->prompt_index
].size
= 0;
1408 separators
= options_get_string(oo
, "word-separators");
1409 idx
= c
->prompt_index
;
1411 /* Find non-whitespace. */
1414 if (!status_prompt_space(&c
->prompt_buffer
[idx
]))
1417 word_is_separators
= status_prompt_in_list(separators
,
1418 &c
->prompt_buffer
[idx
]);
1420 /* Find the character before the beginning of the word. */
1423 if (status_prompt_space(&c
->prompt_buffer
[idx
]) ||
1424 word_is_separators
!= status_prompt_in_list(
1425 separators
, &c
->prompt_buffer
[idx
])) {
1426 /* Go back to the word. */
1432 free(c
->prompt_saved
);
1433 c
->prompt_saved
= xcalloc(sizeof *c
->prompt_buffer
,
1434 (c
->prompt_index
- idx
) + 1);
1435 memcpy(c
->prompt_saved
, c
->prompt_buffer
+ idx
,
1436 (c
->prompt_index
- idx
) * sizeof *c
->prompt_buffer
);
1438 memmove(c
->prompt_buffer
+ idx
,
1439 c
->prompt_buffer
+ c
->prompt_index
,
1440 (size
+ 1 - c
->prompt_index
) *
1441 sizeof *c
->prompt_buffer
);
1442 memset(c
->prompt_buffer
+ size
- (c
->prompt_index
- idx
),
1443 '\0', (c
->prompt_index
- idx
) * sizeof *c
->prompt_buffer
);
1444 c
->prompt_index
= idx
;
1447 case KEYC_RIGHT
|KEYC_CTRL
:
1449 separators
= options_get_string(oo
, "word-separators");
1450 status_prompt_forward_word(c
, size
, 0, separators
);
1453 status_prompt_end_word(c
, size
, "");
1456 separators
= options_get_string(oo
, "word-separators");
1457 status_prompt_end_word(c
, size
, separators
);
1460 status_prompt_forward_word(c
, size
, 1, "");
1463 separators
= options_get_string(oo
, "word-separators");
1464 status_prompt_forward_word(c
, size
, 1, separators
);
1467 status_prompt_backward_word(c
, "");
1469 case KEYC_LEFT
|KEYC_CTRL
:
1471 separators
= options_get_string(oo
, "word-separators");
1472 status_prompt_backward_word(c
, separators
);
1476 histstr
= status_prompt_up_history(c
->prompt_hindex
,
1478 if (histstr
== NULL
)
1480 free(c
->prompt_buffer
);
1481 c
->prompt_buffer
= utf8_fromcstr(histstr
);
1482 c
->prompt_index
= utf8_strlen(c
->prompt_buffer
);
1486 histstr
= status_prompt_down_history(c
->prompt_hindex
,
1488 if (histstr
== NULL
)
1490 free(c
->prompt_buffer
);
1491 c
->prompt_buffer
= utf8_fromcstr(histstr
);
1492 c
->prompt_index
= utf8_strlen(c
->prompt_buffer
);
1495 if (status_prompt_paste(c
))
1499 idx
= c
->prompt_index
;
1503 utf8_copy(&tmp
, &c
->prompt_buffer
[idx
- 2]);
1504 utf8_copy(&c
->prompt_buffer
[idx
- 2],
1505 &c
->prompt_buffer
[idx
- 1]);
1506 utf8_copy(&c
->prompt_buffer
[idx
- 1], &tmp
);
1507 c
->prompt_index
= idx
;
1513 s
= utf8_tocstr(c
->prompt_buffer
);
1515 status_prompt_add_history(s
, c
->prompt_type
);
1516 if (c
->prompt_inputcb(c
, c
->prompt_data
, s
, 1) == 0)
1517 status_prompt_clear(c
);
1520 case '\033': /* Escape */
1523 if (c
->prompt_inputcb(c
, c
->prompt_data
, NULL
, 1) == 0)
1524 status_prompt_clear(c
);
1527 if (~c
->prompt_flags
& PROMPT_INCREMENTAL
)
1529 if (c
->prompt_buffer
[0].size
== 0) {
1531 free(c
->prompt_buffer
);
1532 c
->prompt_buffer
= utf8_fromcstr(c
->prompt_last
);
1533 c
->prompt_index
= utf8_strlen(c
->prompt_buffer
);
1538 if (~c
->prompt_flags
& PROMPT_INCREMENTAL
)
1540 if (c
->prompt_buffer
[0].size
== 0) {
1542 free(c
->prompt_buffer
);
1543 c
->prompt_buffer
= utf8_fromcstr(c
->prompt_last
);
1544 c
->prompt_index
= utf8_strlen(c
->prompt_buffer
);
1549 c
->prompt_flags
|= PROMPT_QUOTENEXT
;
1555 c
->flags
|= CLIENT_REDRAWSTATUS
;
1560 utf8_set(&tmp
, key
);
1561 if (key
<= 0x1f || key
== 0x7f)
1563 } else if (KEYC_IS_UNICODE(key
))
1564 utf8_to_data(key
, &tmp
);
1568 c
->prompt_buffer
= xreallocarray(c
->prompt_buffer
, size
+ 2,
1569 sizeof *c
->prompt_buffer
);
1571 if (c
->prompt_index
== size
) {
1572 utf8_copy(&c
->prompt_buffer
[c
->prompt_index
], &tmp
);
1574 c
->prompt_buffer
[c
->prompt_index
].size
= 0;
1576 memmove(c
->prompt_buffer
+ c
->prompt_index
+ 1,
1577 c
->prompt_buffer
+ c
->prompt_index
,
1578 (size
+ 1 - c
->prompt_index
) *
1579 sizeof *c
->prompt_buffer
);
1580 utf8_copy(&c
->prompt_buffer
[c
->prompt_index
], &tmp
);
1584 if (c
->prompt_flags
& PROMPT_SINGLE
) {
1585 if (utf8_strlen(c
->prompt_buffer
) != 1)
1586 status_prompt_clear(c
);
1588 s
= utf8_tocstr(c
->prompt_buffer
);
1589 if (c
->prompt_inputcb(c
, c
->prompt_data
, s
, 1) == 0)
1590 status_prompt_clear(c
);
1596 c
->flags
|= CLIENT_REDRAWSTATUS
;
1597 if (c
->prompt_flags
& PROMPT_INCREMENTAL
) {
1598 s
= utf8_tocstr(c
->prompt_buffer
);
1599 xasprintf(&cp
, "%c%s", prefix
, s
);
1600 c
->prompt_inputcb(c
, c
->prompt_data
, cp
, 0);
1607 /* Get previous line from the history. */
1609 status_prompt_up_history(u_int
*idx
, u_int type
)
1612 * History runs from 0 to size - 1. Index is from 0 to size. Zero is
1616 if (status_prompt_hsize
[type
] == 0 ||
1617 idx
[type
] == status_prompt_hsize
[type
])
1620 return (status_prompt_hlist
[type
][status_prompt_hsize
[type
] - idx
[type
]]);
1623 /* Get next line from the history. */
1625 status_prompt_down_history(u_int
*idx
, u_int type
)
1627 if (status_prompt_hsize
[type
] == 0 || idx
[type
] == 0)
1632 return (status_prompt_hlist
[type
][status_prompt_hsize
[type
] - idx
[type
]]);
1635 /* Add line to the history. */
1637 status_prompt_add_history(const char *line
, u_int type
)
1639 u_int i
, oldsize
, newsize
, freecount
, hlimit
, new = 1;
1642 oldsize
= status_prompt_hsize
[type
];
1644 strcmp(status_prompt_hlist
[type
][oldsize
- 1], line
) == 0)
1647 hlimit
= options_get_number(global_options
, "prompt-history-limit");
1648 if (hlimit
> oldsize
) {
1651 newsize
= oldsize
+ new;
1654 freecount
= oldsize
+ new - newsize
;
1655 if (freecount
> oldsize
)
1656 freecount
= oldsize
;
1659 for (i
= 0; i
< freecount
; i
++)
1660 free(status_prompt_hlist
[type
][i
]);
1661 movesize
= (oldsize
- freecount
) *
1662 sizeof *status_prompt_hlist
[type
];
1664 memmove(&status_prompt_hlist
[type
][0],
1665 &status_prompt_hlist
[type
][freecount
], movesize
);
1670 free(status_prompt_hlist
[type
]);
1671 status_prompt_hlist
[type
] = NULL
;
1672 } else if (newsize
!= oldsize
) {
1673 status_prompt_hlist
[type
] =
1674 xreallocarray(status_prompt_hlist
[type
], newsize
,
1675 sizeof *status_prompt_hlist
[type
]);
1678 if (new == 1 && newsize
> 0)
1679 status_prompt_hlist
[type
][newsize
- 1] = xstrdup(line
);
1680 status_prompt_hsize
[type
] = newsize
;
1683 /* Add to completion list. */
1685 status_prompt_add_list(char ***list
, u_int
*size
, const char *s
)
1689 for (i
= 0; i
< *size
; i
++) {
1690 if (strcmp((*list
)[i
], s
) == 0)
1693 *list
= xreallocarray(*list
, (*size
) + 1, sizeof **list
);
1694 (*list
)[(*size
)++] = xstrdup(s
);
1697 /* Build completion list. */
1699 status_prompt_complete_list(u_int
*size
, const char *s
, int at_start
)
1701 char **list
= NULL
, *tmp
;
1702 const char **layout
, *value
, *cp
;
1703 const struct cmd_entry
**cmdent
;
1704 const struct options_table_entry
*oe
;
1705 size_t slen
= strlen(s
), valuelen
;
1706 struct options_entry
*o
;
1707 struct options_array_item
*a
;
1708 const char *layouts
[] = {
1709 "even-horizontal", "even-vertical",
1710 "main-horizontal", "main-horizontal-mirrored",
1711 "main-vertical", "main-vertical-mirrored", "tiled", NULL
1715 for (cmdent
= cmd_table
; *cmdent
!= NULL
; cmdent
++) {
1716 if (strncmp((*cmdent
)->name
, s
, slen
) == 0)
1717 status_prompt_add_list(&list
, size
, (*cmdent
)->name
);
1718 if ((*cmdent
)->alias
!= NULL
&&
1719 strncmp((*cmdent
)->alias
, s
, slen
) == 0)
1720 status_prompt_add_list(&list
, size
, (*cmdent
)->alias
);
1722 o
= options_get_only(global_options
, "command-alias");
1724 a
= options_array_first(o
);
1726 value
= options_array_item_value(a
)->string
;
1727 if ((cp
= strchr(value
, '=')) == NULL
)
1729 valuelen
= cp
- value
;
1730 if (slen
> valuelen
|| strncmp(value
, s
, slen
) != 0)
1733 xasprintf(&tmp
, "%.*s", (int)valuelen
, value
);
1734 status_prompt_add_list(&list
, size
, tmp
);
1738 a
= options_array_next(a
);
1743 for (oe
= options_table
; oe
->name
!= NULL
; oe
++) {
1744 if (strncmp(oe
->name
, s
, slen
) == 0)
1745 status_prompt_add_list(&list
, size
, oe
->name
);
1747 for (layout
= layouts
; *layout
!= NULL
; layout
++) {
1748 if (strncmp(*layout
, s
, slen
) == 0)
1749 status_prompt_add_list(&list
, size
, *layout
);
1754 /* Find longest prefix. */
1756 status_prompt_complete_prefix(char **list
, u_int size
)
1762 if (list
== NULL
|| size
== 0)
1764 out
= xstrdup(list
[0]);
1765 for (i
= 1; i
< size
; i
++) {
1766 j
= strlen(list
[i
]);
1767 if (j
> strlen(out
))
1769 for (; j
> 0; j
--) {
1770 if (out
[j
- 1] != list
[i
][j
- 1])
1777 /* Complete word menu callback. */
1779 status_prompt_menu_callback(__unused
struct menu
*menu
, u_int idx
, key_code key
,
1782 struct status_prompt_menu
*spm
= data
;
1783 struct client
*c
= spm
->c
;
1787 if (key
!= KEYC_NONE
) {
1789 if (spm
->flag
== '\0')
1790 s
= xstrdup(spm
->list
[idx
]);
1792 xasprintf(&s
, "-%c%s", spm
->flag
, spm
->list
[idx
]);
1793 if (c
->prompt_type
== PROMPT_TYPE_WINDOW_TARGET
) {
1794 free(c
->prompt_buffer
);
1795 c
->prompt_buffer
= utf8_fromcstr(s
);
1796 c
->prompt_index
= utf8_strlen(c
->prompt_buffer
);
1797 c
->flags
|= CLIENT_REDRAWSTATUS
;
1798 } else if (status_prompt_replace_complete(c
, s
))
1799 c
->flags
|= CLIENT_REDRAWSTATUS
;
1803 for (i
= 0; i
< spm
->size
; i
++)
1808 /* Show complete word menu. */
1810 status_prompt_complete_list_menu(struct client
*c
, char **list
, u_int size
,
1811 u_int offset
, char flag
)
1814 struct menu_item item
;
1815 struct status_prompt_menu
*spm
;
1816 u_int lines
= status_line_size(c
), height
, i
;
1821 if (c
->tty
.sy
- lines
< 3)
1824 spm
= xmalloc(sizeof *spm
);
1830 height
= c
->tty
.sy
- lines
- 2;
1835 spm
->start
= size
- height
;
1837 menu
= menu_create("");
1838 for (i
= spm
->start
; i
< size
; i
++) {
1839 item
.name
= list
[i
];
1840 item
.key
= '0' + (i
- spm
->start
);
1841 item
.command
= NULL
;
1842 menu_add_item(menu
, &item
, NULL
, c
, NULL
);
1845 if (options_get_number(c
->session
->options
, "status-position") == 0)
1848 py
= c
->tty
.sy
- 3 - height
;
1849 offset
+= utf8_cstrwidth(c
->prompt_string
);
1855 if (menu_display(menu
, MENU_NOMOUSE
|MENU_TAB
, 0, NULL
, offset
, py
, c
,
1856 BOX_LINES_DEFAULT
, NULL
, NULL
, NULL
, NULL
,
1857 status_prompt_menu_callback
, spm
) != 0) {
1865 /* Show complete word menu. */
1867 status_prompt_complete_window_menu(struct client
*c
, struct session
*s
,
1868 const char *word
, u_int offset
, char flag
)
1871 struct menu_item item
;
1872 struct status_prompt_menu
*spm
;
1874 char **list
= NULL
, *tmp
;
1875 u_int lines
= status_line_size(c
), height
;
1878 if (c
->tty
.sy
- lines
< 3)
1881 spm
= xmalloc(sizeof *spm
);
1885 height
= c
->tty
.sy
- lines
- 2;
1890 menu
= menu_create("");
1891 RB_FOREACH(wl
, winlinks
, &s
->windows
) {
1892 if (word
!= NULL
&& *word
!= '\0') {
1893 xasprintf(&tmp
, "%d", wl
->idx
);
1894 if (strncmp(tmp
, word
, strlen(word
)) != 0) {
1901 list
= xreallocarray(list
, size
+ 1, sizeof *list
);
1902 if (c
->prompt_type
== PROMPT_TYPE_WINDOW_TARGET
) {
1903 xasprintf(&tmp
, "%d (%s)", wl
->idx
, wl
->window
->name
);
1904 xasprintf(&list
[size
++], "%d", wl
->idx
);
1906 xasprintf(&tmp
, "%s:%d (%s)", s
->name
, wl
->idx
,
1908 xasprintf(&list
[size
++], "%s:%d", s
->name
, wl
->idx
);
1911 item
.key
= '0' + size
- 1;
1912 item
.command
= NULL
;
1913 menu_add_item(menu
, &item
, NULL
, c
, NULL
);
1927 xasprintf(&tmp
, "-%c%s", flag
, list
[0]);
1941 if (options_get_number(c
->session
->options
, "status-position") == 0)
1944 py
= c
->tty
.sy
- 3 - height
;
1945 offset
+= utf8_cstrwidth(c
->prompt_string
);
1951 if (menu_display(menu
, MENU_NOMOUSE
|MENU_TAB
, 0, NULL
, offset
, py
, c
,
1952 BOX_LINES_DEFAULT
, NULL
, NULL
, NULL
, NULL
,
1953 status_prompt_menu_callback
, spm
) != 0) {
1961 /* Sort complete list. */
1963 status_prompt_complete_sort(const void *a
, const void *b
)
1965 const char **aa
= (const char **)a
, **bb
= (const char **)b
;
1967 return (strcmp(*aa
, *bb
));
1970 /* Complete a session. */
1972 status_prompt_complete_session(char ***list
, u_int
*size
, const char *s
,
1975 struct session
*loop
;
1976 char *out
, *tmp
, n
[11];
1978 RB_FOREACH(loop
, sessions
, &sessions
) {
1979 if (*s
== '\0' || strncmp(loop
->name
, s
, strlen(s
)) == 0) {
1980 *list
= xreallocarray(*list
, (*size
) + 2,
1982 xasprintf(&(*list
)[(*size
)++], "%s:", loop
->name
);
1983 } else if (*s
== '$') {
1984 xsnprintf(n
, sizeof n
, "%u", loop
->id
);
1986 strncmp(n
, s
+ 1, strlen(s
) - 1) == 0) {
1987 *list
= xreallocarray(*list
, (*size
) + 2,
1989 xasprintf(&(*list
)[(*size
)++], "$%s:", n
);
1993 out
= status_prompt_complete_prefix(*list
, *size
);
1994 if (out
!= NULL
&& flag
!= '\0') {
1995 xasprintf(&tmp
, "-%c%s", flag
, out
);
2002 /* Complete word. */
2004 status_prompt_complete(struct client
*c
, const char *word
, u_int offset
)
2006 struct session
*session
;
2007 const char *s
, *colon
;
2008 char **list
= NULL
, *copy
= NULL
, *out
= NULL
;
2012 if (*word
== '\0' &&
2013 c
->prompt_type
!= PROMPT_TYPE_TARGET
&&
2014 c
->prompt_type
!= PROMPT_TYPE_WINDOW_TARGET
)
2017 if (c
->prompt_type
!= PROMPT_TYPE_TARGET
&&
2018 c
->prompt_type
!= PROMPT_TYPE_WINDOW_TARGET
&&
2019 strncmp(word
, "-t", 2) != 0 &&
2020 strncmp(word
, "-s", 2) != 0) {
2021 list
= status_prompt_complete_list(&size
, word
, offset
== 0);
2025 xasprintf(&out
, "%s ", list
[0]);
2027 out
= status_prompt_complete_prefix(list
, size
);
2031 if (c
->prompt_type
== PROMPT_TYPE_TARGET
||
2032 c
->prompt_type
== PROMPT_TYPE_WINDOW_TARGET
) {
2041 /* If this is a window completion, open the window menu. */
2042 if (c
->prompt_type
== PROMPT_TYPE_WINDOW_TARGET
) {
2043 out
= status_prompt_complete_window_menu(c
, c
->session
, s
,
2047 colon
= strchr(s
, ':');
2049 /* If there is no colon, complete as a session. */
2050 if (colon
== NULL
) {
2051 out
= status_prompt_complete_session(&list
, &size
, s
, flag
);
2055 /* If there is a colon but no period, find session and show a menu. */
2056 if (strchr(colon
+ 1, '.') == NULL
) {
2058 session
= c
->session
;
2061 *strchr(copy
, ':') = '\0';
2062 session
= session_find(copy
);
2064 if (session
== NULL
)
2067 out
= status_prompt_complete_window_menu(c
, session
, colon
+ 1,
2075 qsort(list
, size
, sizeof *list
, status_prompt_complete_sort
);
2076 for (i
= 0; i
< size
; i
++)
2077 log_debug("complete %u: %s", i
, list
[i
]);
2080 if (out
!= NULL
&& strcmp(word
, out
) == 0) {
2085 !status_prompt_complete_list_menu(c
, list
, size
, offset
, flag
)) {
2086 for (i
= 0; i
< size
; i
++)
2093 /* Return the type of the prompt as an enum. */
2095 status_prompt_type(const char *type
)
2099 for (i
= 0; i
< PROMPT_NTYPES
; i
++) {
2100 if (strcmp(type
, status_prompt_type_string(i
)) == 0)
2103 return (PROMPT_TYPE_INVALID
);
2106 /* Accessor for prompt_type_strings. */
2108 status_prompt_type_string(u_int type
)
2110 if (type
>= PROMPT_NTYPES
)
2112 return (prompt_type_strings
[type
]);