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 /* Enable status line prompt. */
603 status_prompt_set(struct client
*c
, struct cmd_find_state
*fs
,
604 const char *msg
, const char *input
, prompt_input_cb inputcb
,
605 prompt_free_cb freecb
, void *data
, int flags
, enum prompt_type prompt_type
)
607 struct format_tree
*ft
;
611 ft
= format_create_from_state(NULL
, c
, fs
);
613 ft
= format_create_defaults(NULL
, c
, NULL
, NULL
, NULL
);
617 if (flags
& PROMPT_NOFORMAT
)
618 tmp
= xstrdup(input
);
620 tmp
= format_expand_time(ft
, input
);
622 status_message_clear(c
);
623 status_prompt_clear(c
);
624 status_push_screen(c
);
626 c
->prompt_string
= format_expand_time(ft
, msg
);
628 if (flags
& PROMPT_INCREMENTAL
) {
629 c
->prompt_last
= xstrdup(tmp
);
630 c
->prompt_buffer
= utf8_fromcstr("");
632 c
->prompt_last
= NULL
;
633 c
->prompt_buffer
= utf8_fromcstr(tmp
);
635 c
->prompt_index
= utf8_strlen(c
->prompt_buffer
);
637 c
->prompt_inputcb
= inputcb
;
638 c
->prompt_freecb
= freecb
;
639 c
->prompt_data
= data
;
641 memset(c
->prompt_hindex
, 0, sizeof c
->prompt_hindex
);
643 c
->prompt_flags
= flags
;
644 c
->prompt_type
= prompt_type
;
645 c
->prompt_mode
= PROMPT_ENTRY
;
647 if (~flags
& PROMPT_INCREMENTAL
)
648 c
->tty
.flags
|= (TTY_NOCURSOR
|TTY_FREEZE
);
649 c
->flags
|= CLIENT_REDRAWSTATUS
;
651 if (flags
& PROMPT_INCREMENTAL
)
652 c
->prompt_inputcb(c
, c
->prompt_data
, "=", 0);
658 /* Remove status line prompt. */
660 status_prompt_clear(struct client
*c
)
662 if (c
->prompt_string
== NULL
)
665 if (c
->prompt_freecb
!= NULL
&& c
->prompt_data
!= NULL
)
666 c
->prompt_freecb(c
->prompt_data
);
668 free(c
->prompt_last
);
669 c
->prompt_last
= NULL
;
671 free(c
->prompt_string
);
672 c
->prompt_string
= NULL
;
674 free(c
->prompt_buffer
);
675 c
->prompt_buffer
= NULL
;
677 free(c
->prompt_saved
);
678 c
->prompt_saved
= NULL
;
680 c
->tty
.flags
&= ~(TTY_NOCURSOR
|TTY_FREEZE
);
681 c
->flags
|= CLIENT_ALLREDRAWFLAGS
; /* was frozen and may have changed */
683 status_pop_screen(c
);
686 /* Update status line prompt with a new prompt string. */
688 status_prompt_update(struct client
*c
, const char *msg
, const char *input
)
690 struct format_tree
*ft
;
693 ft
= format_create(c
, NULL
, FORMAT_NONE
, 0);
694 format_defaults(ft
, c
, NULL
, NULL
, NULL
);
696 tmp
= format_expand_time(ft
, input
);
698 free(c
->prompt_string
);
699 c
->prompt_string
= format_expand_time(ft
, msg
);
701 free(c
->prompt_buffer
);
702 c
->prompt_buffer
= utf8_fromcstr(tmp
);
703 c
->prompt_index
= utf8_strlen(c
->prompt_buffer
);
705 memset(c
->prompt_hindex
, 0, sizeof c
->prompt_hindex
);
707 c
->flags
|= CLIENT_REDRAWSTATUS
;
713 /* Draw client prompt on status line of present else on last line. */
715 status_prompt_redraw(struct client
*c
)
717 struct status_line
*sl
= &c
->status
;
718 struct screen_write_ctx ctx
;
719 struct session
*s
= c
->session
;
720 struct screen old_screen
;
721 u_int i
, lines
, offset
, left
, start
, width
;
722 u_int pcursor
, pwidth
, promptline
;
723 struct grid_cell gc
, cursorgc
;
724 struct format_tree
*ft
;
726 if (c
->tty
.sx
== 0 || c
->tty
.sy
== 0)
728 memcpy(&old_screen
, sl
->active
, sizeof old_screen
);
730 lines
= status_line_size(c
);
733 screen_init(sl
->active
, c
->tty
.sx
, lines
, 0);
735 promptline
= status_prompt_line_at(c
);
736 if (promptline
> lines
- 1)
737 promptline
= lines
- 1;
739 ft
= format_create_defaults(NULL
, c
, NULL
, NULL
, NULL
);
740 if (c
->prompt_mode
== PROMPT_COMMAND
)
741 style_apply(&gc
, s
->options
, "message-command-style", ft
);
743 style_apply(&gc
, s
->options
, "message-style", ft
);
746 memcpy(&cursorgc
, &gc
, sizeof cursorgc
);
747 cursorgc
.attr
^= GRID_ATTR_REVERSE
;
749 start
= format_width(c
->prompt_string
);
750 if (start
> c
->tty
.sx
)
753 screen_write_start(&ctx
, sl
->active
);
754 screen_write_fast_copy(&ctx
, &sl
->screen
, 0, 0, c
->tty
.sx
, lines
);
755 screen_write_cursormove(&ctx
, 0, promptline
, 0);
756 for (offset
= 0; offset
< c
->tty
.sx
; offset
++)
757 screen_write_putc(&ctx
, &gc
, ' ');
758 screen_write_cursormove(&ctx
, 0, promptline
, 0);
759 format_draw(&ctx
, &gc
, start
, c
->prompt_string
, NULL
, 0);
760 screen_write_cursormove(&ctx
, start
, promptline
, 0);
762 left
= c
->tty
.sx
- start
;
766 pcursor
= utf8_strwidth(c
->prompt_buffer
, c
->prompt_index
);
767 pwidth
= utf8_strwidth(c
->prompt_buffer
, -1);
768 if (pcursor
>= left
) {
770 * The cursor would be outside the screen so start drawing
771 * with it on the right.
773 offset
= (pcursor
- left
) + 1;
779 c
->prompt_cursor
= start
+ c
->prompt_index
- offset
;
782 for (i
= 0; c
->prompt_buffer
[i
].size
!= 0; i
++) {
783 if (width
< offset
) {
784 width
+= c
->prompt_buffer
[i
].width
;
787 if (width
>= offset
+ pwidth
)
789 width
+= c
->prompt_buffer
[i
].width
;
790 if (width
> offset
+ pwidth
)
793 if (i
!= c
->prompt_index
) {
794 utf8_copy(&gc
.data
, &c
->prompt_buffer
[i
]);
795 screen_write_cell(&ctx
, &gc
);
797 utf8_copy(&cursorgc
.data
, &c
->prompt_buffer
[i
]);
798 screen_write_cell(&ctx
, &cursorgc
);
801 if (sl
->active
->cx
< screen_size_x(sl
->active
) && c
->prompt_index
>= i
)
802 screen_write_putc(&ctx
, &cursorgc
, ' ');
805 screen_write_stop(&ctx
);
807 if (grid_compare(sl
->active
->grid
, old_screen
.grid
) == 0) {
808 screen_free(&old_screen
);
811 screen_free(&old_screen
);
815 /* Is this a separator? */
817 status_prompt_in_list(const char *ws
, const struct utf8_data
*ud
)
819 if (ud
->size
!= 1 || ud
->width
!= 1)
821 return (strchr(ws
, *ud
->data
) != NULL
);
824 /* Is this a space? */
826 status_prompt_space(const struct utf8_data
*ud
)
828 if (ud
->size
!= 1 || ud
->width
!= 1)
830 return (*ud
->data
== ' ');
834 * Translate key from vi to emacs. Return 0 to drop key, 1 to process the key
835 * as an emacs key; return 2 to append to the buffer.
838 status_prompt_translate_key(struct client
*c
, key_code key
, key_code
*new_key
)
840 if (c
->prompt_mode
== PROMPT_ENTRY
) {
842 case '\001': /* C-a */
843 case '\003': /* C-c */
844 case '\005': /* C-e */
845 case '\007': /* C-g */
846 case '\010': /* C-h */
847 case '\011': /* Tab */
848 case '\013': /* C-k */
849 case '\016': /* C-n */
850 case '\020': /* C-p */
851 case '\024': /* C-t */
852 case '\025': /* C-u */
853 case '\027': /* C-w */
854 case '\031': /* C-y */
857 case KEYC_LEFT
|KEYC_CTRL
:
858 case KEYC_RIGHT
|KEYC_CTRL
:
869 case '\033': /* Escape */
870 c
->prompt_mode
= PROMPT_COMMAND
;
871 c
->flags
|= CLIENT_REDRAWSTATUS
;
880 *new_key
= KEYC_LEFT
;
887 c
->prompt_mode
= PROMPT_ENTRY
;
888 c
->flags
|= CLIENT_REDRAWSTATUS
;
889 break; /* switch mode and... */
891 c
->prompt_mode
= PROMPT_ENTRY
;
892 c
->flags
|= CLIENT_REDRAWSTATUS
;
893 *new_key
= '\025'; /* C-u */
896 case '\033': /* Escape */
897 c
->prompt_mode
= PROMPT_ENTRY
;
898 c
->flags
|= CLIENT_REDRAWSTATUS
;
910 *new_key
= KEYC_HOME
;
914 *new_key
= '\013'; /* C-k */
918 *new_key
= KEYC_BSPACE
;
921 *new_key
= 'b'|KEYC_META
;
924 *new_key
= 'B'|KEYC_VI
;
927 *new_key
= '\025'; /* C-u */
930 *new_key
= 'e'|KEYC_VI
;
933 *new_key
= 'E'|KEYC_VI
;
936 *new_key
= 'w'|KEYC_VI
;
939 *new_key
= 'W'|KEYC_VI
;
942 *new_key
= '\031'; /* C-y */
945 *new_key
= '\003'; /* C-c */
954 *new_key
= KEYC_DOWN
;
958 *new_key
= KEYC_LEFT
;
963 *new_key
= KEYC_RIGHT
;
969 case '\010' /* C-h */:
970 case '\003' /* C-c */:
978 /* Paste into prompt. */
980 status_prompt_paste(struct client
*c
)
982 struct paste_buffer
*pb
;
984 size_t size
, n
, bufsize
;
986 struct utf8_data
*ud
, *udp
;
987 enum utf8_state more
;
989 size
= utf8_strlen(c
->prompt_buffer
);
990 if (c
->prompt_saved
!= NULL
) {
991 ud
= c
->prompt_saved
;
992 n
= utf8_strlen(c
->prompt_saved
);
994 if ((pb
= paste_get_top(NULL
)) == NULL
)
996 bufdata
= paste_buffer_data(pb
, &bufsize
);
997 ud
= xreallocarray(NULL
, bufsize
+ 1, sizeof *ud
);
999 for (i
= 0; i
!= bufsize
; /* nothing */) {
1000 more
= utf8_open(udp
, bufdata
[i
]);
1001 if (more
== UTF8_MORE
) {
1002 while (++i
!= bufsize
&& more
== UTF8_MORE
)
1003 more
= utf8_append(udp
, bufdata
[i
]);
1004 if (more
== UTF8_DONE
) {
1010 if (bufdata
[i
] <= 31 || bufdata
[i
] >= 127)
1012 utf8_set(udp
, bufdata
[i
]);
1022 c
->prompt_buffer
= xreallocarray(c
->prompt_buffer
, size
+ n
+ 1,
1023 sizeof *c
->prompt_buffer
);
1024 if (c
->prompt_index
== size
) {
1025 memcpy(c
->prompt_buffer
+ c
->prompt_index
, ud
,
1026 n
* sizeof *c
->prompt_buffer
);
1027 c
->prompt_index
+= n
;
1028 c
->prompt_buffer
[c
->prompt_index
].size
= 0;
1030 memmove(c
->prompt_buffer
+ c
->prompt_index
+ n
,
1031 c
->prompt_buffer
+ c
->prompt_index
,
1032 (size
+ 1 - c
->prompt_index
) * sizeof *c
->prompt_buffer
);
1033 memcpy(c
->prompt_buffer
+ c
->prompt_index
, ud
,
1034 n
* sizeof *c
->prompt_buffer
);
1035 c
->prompt_index
+= n
;
1038 if (ud
!= c
->prompt_saved
)
1043 /* Finish completion. */
1045 status_prompt_replace_complete(struct client
*c
, const char *s
)
1047 char word
[64], *allocated
= NULL
;
1048 size_t size
, n
, off
, idx
, used
;
1049 struct utf8_data
*first
, *last
, *ud
;
1051 /* Work out where the cursor currently is. */
1052 idx
= c
->prompt_index
;
1055 size
= utf8_strlen(c
->prompt_buffer
);
1057 /* Find the word we are in. */
1058 first
= &c
->prompt_buffer
[idx
];
1059 while (first
> c
->prompt_buffer
&& !status_prompt_space(first
))
1061 while (first
->size
!= 0 && status_prompt_space(first
))
1063 last
= &c
->prompt_buffer
[idx
];
1064 while (last
->size
!= 0 && !status_prompt_space(last
))
1066 while (last
> c
->prompt_buffer
&& status_prompt_space(last
))
1068 if (last
->size
!= 0)
1074 for (ud
= first
; ud
< last
; ud
++) {
1075 if (used
+ ud
->size
>= sizeof word
)
1077 memcpy(word
+ used
, ud
->data
, ud
->size
);
1085 /* Try to complete it. */
1087 allocated
= status_prompt_complete(c
, word
,
1088 first
- c
->prompt_buffer
);
1089 if (allocated
== NULL
)
1094 /* Trim out word. */
1095 n
= size
- (last
- c
->prompt_buffer
) + 1; /* with \0 */
1096 memmove(first
, last
, n
* sizeof *c
->prompt_buffer
);
1097 size
-= last
- first
;
1099 /* Insert the new word. */
1101 off
= first
- c
->prompt_buffer
;
1102 c
->prompt_buffer
= xreallocarray(c
->prompt_buffer
, size
+ 1,
1103 sizeof *c
->prompt_buffer
);
1104 first
= c
->prompt_buffer
+ off
;
1105 memmove(first
+ strlen(s
), first
, n
* sizeof *c
->prompt_buffer
);
1106 for (idx
= 0; idx
< strlen(s
); idx
++)
1107 utf8_set(&first
[idx
], s
[idx
]);
1108 c
->prompt_index
= (first
- c
->prompt_buffer
) + strlen(s
);
1114 /* Prompt forward to the next beginning of a word. */
1116 status_prompt_forward_word(struct client
*c
, size_t size
, int vi
,
1117 const char *separators
)
1119 size_t idx
= c
->prompt_index
;
1120 int word_is_separators
;
1122 /* In emacs mode, skip until the first non-whitespace character. */
1124 while (idx
!= size
&&
1125 status_prompt_space(&c
->prompt_buffer
[idx
]))
1128 /* Can't move forward if we're already at the end. */
1130 c
->prompt_index
= idx
;
1134 /* Determine the current character class (separators or not). */
1135 word_is_separators
= status_prompt_in_list(separators
,
1136 &c
->prompt_buffer
[idx
]) &&
1137 !status_prompt_space(&c
->prompt_buffer
[idx
]);
1139 /* Skip ahead until the first space or opposite character class. */
1142 if (status_prompt_space(&c
->prompt_buffer
[idx
])) {
1143 /* In vi mode, go to the start of the next word. */
1145 while (idx
!= size
&&
1146 status_prompt_space(&c
->prompt_buffer
[idx
]))
1150 } while (idx
!= size
&& word_is_separators
== status_prompt_in_list(
1151 separators
, &c
->prompt_buffer
[idx
]));
1153 c
->prompt_index
= idx
;
1156 /* Prompt forward to the next end of a word. */
1158 status_prompt_end_word(struct client
*c
, size_t size
, const char *separators
)
1160 size_t idx
= c
->prompt_index
;
1161 int word_is_separators
;
1163 /* Can't move forward if we're already at the end. */
1167 /* Find the next word. */
1171 c
->prompt_index
= idx
;
1174 } while (status_prompt_space(&c
->prompt_buffer
[idx
]));
1176 /* Determine the character class (separators or not). */
1177 word_is_separators
= status_prompt_in_list(separators
,
1178 &c
->prompt_buffer
[idx
]);
1180 /* Skip ahead until the next space or opposite character class. */
1185 } while (!status_prompt_space(&c
->prompt_buffer
[idx
]) &&
1186 word_is_separators
== status_prompt_in_list(separators
,
1187 &c
->prompt_buffer
[idx
]));
1189 /* Back up to the previous character to stop at the end of the word. */
1190 c
->prompt_index
= idx
- 1;
1193 /* Prompt backward to the previous beginning of a word. */
1195 status_prompt_backward_word(struct client
*c
, const char *separators
)
1197 size_t idx
= c
->prompt_index
;
1198 int word_is_separators
;
1200 /* Find non-whitespace. */
1203 if (!status_prompt_space(&c
->prompt_buffer
[idx
]))
1206 word_is_separators
= status_prompt_in_list(separators
,
1207 &c
->prompt_buffer
[idx
]);
1209 /* Find the character before the beginning of the word. */
1212 if (status_prompt_space(&c
->prompt_buffer
[idx
]) ||
1213 word_is_separators
!= status_prompt_in_list(separators
,
1214 &c
->prompt_buffer
[idx
])) {
1215 /* Go back to the word. */
1220 c
->prompt_index
= idx
;
1223 /* Handle keys in prompt. */
1225 status_prompt_key(struct client
*c
, key_code key
)
1227 struct options
*oo
= c
->session
->options
;
1228 char *s
, *cp
, prefix
= '=';
1229 const char *histstr
, *separators
= NULL
, *keystring
;
1231 struct utf8_data tmp
;
1232 int keys
, word_is_separators
;
1234 if (c
->prompt_flags
& PROMPT_KEY
) {
1235 keystring
= key_string_lookup_key(key
, 0);
1236 c
->prompt_inputcb(c
, c
->prompt_data
, keystring
, 1);
1237 status_prompt_clear(c
);
1240 size
= utf8_strlen(c
->prompt_buffer
);
1242 if (c
->prompt_flags
& PROMPT_NUMERIC
) {
1243 if (key
>= '0' && key
<= '9')
1245 s
= utf8_tocstr(c
->prompt_buffer
);
1246 c
->prompt_inputcb(c
, c
->prompt_data
, s
, 1);
1247 status_prompt_clear(c
);
1251 key
&= ~KEYC_MASK_FLAGS
;
1253 keys
= options_get_number(c
->session
->options
, "status-keys");
1254 if (keys
== MODEKEY_VI
) {
1255 switch (status_prompt_translate_key(c
, key
, &key
)) {
1268 case '\002': /* C-b */
1269 if (c
->prompt_index
> 0) {
1275 case '\006': /* C-f */
1276 if (c
->prompt_index
< size
) {
1282 case '\001': /* C-a */
1283 if (c
->prompt_index
!= 0) {
1284 c
->prompt_index
= 0;
1289 case '\005': /* C-e */
1290 if (c
->prompt_index
!= size
) {
1291 c
->prompt_index
= size
;
1295 case '\011': /* Tab */
1296 if (status_prompt_replace_complete(c
, NULL
))
1300 case '\010': /* C-h */
1301 if (c
->prompt_index
!= 0) {
1302 if (c
->prompt_index
== size
)
1303 c
->prompt_buffer
[--c
->prompt_index
].size
= 0;
1305 memmove(c
->prompt_buffer
+ c
->prompt_index
- 1,
1306 c
->prompt_buffer
+ c
->prompt_index
,
1307 (size
+ 1 - c
->prompt_index
) *
1308 sizeof *c
->prompt_buffer
);
1315 case '\004': /* C-d */
1316 if (c
->prompt_index
!= size
) {
1317 memmove(c
->prompt_buffer
+ c
->prompt_index
,
1318 c
->prompt_buffer
+ c
->prompt_index
+ 1,
1319 (size
+ 1 - c
->prompt_index
) *
1320 sizeof *c
->prompt_buffer
);
1324 case '\025': /* C-u */
1325 c
->prompt_buffer
[0].size
= 0;
1326 c
->prompt_index
= 0;
1328 case '\013': /* C-k */
1329 if (c
->prompt_index
< size
) {
1330 c
->prompt_buffer
[c
->prompt_index
].size
= 0;
1334 case '\027': /* C-w */
1335 separators
= options_get_string(oo
, "word-separators");
1336 idx
= c
->prompt_index
;
1338 /* Find non-whitespace. */
1341 if (!status_prompt_space(&c
->prompt_buffer
[idx
]))
1344 word_is_separators
= status_prompt_in_list(separators
,
1345 &c
->prompt_buffer
[idx
]);
1347 /* Find the character before the beginning of the word. */
1350 if (status_prompt_space(&c
->prompt_buffer
[idx
]) ||
1351 word_is_separators
!= status_prompt_in_list(
1352 separators
, &c
->prompt_buffer
[idx
])) {
1353 /* Go back to the word. */
1359 free(c
->prompt_saved
);
1360 c
->prompt_saved
= xcalloc(sizeof *c
->prompt_buffer
,
1361 (c
->prompt_index
- idx
) + 1);
1362 memcpy(c
->prompt_saved
, c
->prompt_buffer
+ idx
,
1363 (c
->prompt_index
- idx
) * sizeof *c
->prompt_buffer
);
1365 memmove(c
->prompt_buffer
+ idx
,
1366 c
->prompt_buffer
+ c
->prompt_index
,
1367 (size
+ 1 - c
->prompt_index
) *
1368 sizeof *c
->prompt_buffer
);
1369 memset(c
->prompt_buffer
+ size
- (c
->prompt_index
- idx
),
1370 '\0', (c
->prompt_index
- idx
) * sizeof *c
->prompt_buffer
);
1371 c
->prompt_index
= idx
;
1374 case KEYC_RIGHT
|KEYC_CTRL
:
1376 separators
= options_get_string(oo
, "word-separators");
1377 status_prompt_forward_word(c
, size
, 0, separators
);
1380 status_prompt_end_word(c
, size
, "");
1383 separators
= options_get_string(oo
, "word-separators");
1384 status_prompt_end_word(c
, size
, separators
);
1387 status_prompt_forward_word(c
, size
, 1, "");
1390 separators
= options_get_string(oo
, "word-separators");
1391 status_prompt_forward_word(c
, size
, 1, separators
);
1394 status_prompt_backward_word(c
, "");
1396 case KEYC_LEFT
|KEYC_CTRL
:
1398 separators
= options_get_string(oo
, "word-separators");
1399 status_prompt_backward_word(c
, separators
);
1402 case '\020': /* C-p */
1403 histstr
= status_prompt_up_history(c
->prompt_hindex
,
1405 if (histstr
== NULL
)
1407 free(c
->prompt_buffer
);
1408 c
->prompt_buffer
= utf8_fromcstr(histstr
);
1409 c
->prompt_index
= utf8_strlen(c
->prompt_buffer
);
1412 case '\016': /* C-n */
1413 histstr
= status_prompt_down_history(c
->prompt_hindex
,
1415 if (histstr
== NULL
)
1417 free(c
->prompt_buffer
);
1418 c
->prompt_buffer
= utf8_fromcstr(histstr
);
1419 c
->prompt_index
= utf8_strlen(c
->prompt_buffer
);
1421 case '\031': /* C-y */
1422 if (status_prompt_paste(c
))
1425 case '\024': /* C-t */
1426 idx
= c
->prompt_index
;
1430 utf8_copy(&tmp
, &c
->prompt_buffer
[idx
- 2]);
1431 utf8_copy(&c
->prompt_buffer
[idx
- 2],
1432 &c
->prompt_buffer
[idx
- 1]);
1433 utf8_copy(&c
->prompt_buffer
[idx
- 1], &tmp
);
1434 c
->prompt_index
= idx
;
1440 s
= utf8_tocstr(c
->prompt_buffer
);
1442 status_prompt_add_history(s
, c
->prompt_type
);
1443 if (c
->prompt_inputcb(c
, c
->prompt_data
, s
, 1) == 0)
1444 status_prompt_clear(c
);
1447 case '\033': /* Escape */
1448 case '\003': /* C-c */
1449 case '\007': /* C-g */
1450 if (c
->prompt_inputcb(c
, c
->prompt_data
, NULL
, 1) == 0)
1451 status_prompt_clear(c
);
1453 case '\022': /* C-r */
1454 if (~c
->prompt_flags
& PROMPT_INCREMENTAL
)
1456 if (c
->prompt_buffer
[0].size
== 0) {
1458 free(c
->prompt_buffer
);
1459 c
->prompt_buffer
= utf8_fromcstr(c
->prompt_last
);
1460 c
->prompt_index
= utf8_strlen(c
->prompt_buffer
);
1464 case '\023': /* C-s */
1465 if (~c
->prompt_flags
& PROMPT_INCREMENTAL
)
1467 if (c
->prompt_buffer
[0].size
== 0) {
1469 free(c
->prompt_buffer
);
1470 c
->prompt_buffer
= utf8_fromcstr(c
->prompt_last
);
1471 c
->prompt_index
= utf8_strlen(c
->prompt_buffer
);
1479 c
->flags
|= CLIENT_REDRAWSTATUS
;
1484 utf8_set(&tmp
, key
);
1485 else if (KEYC_IS_UNICODE(key
))
1486 utf8_to_data(key
, &tmp
);
1490 c
->prompt_buffer
= xreallocarray(c
->prompt_buffer
, size
+ 2,
1491 sizeof *c
->prompt_buffer
);
1493 if (c
->prompt_index
== size
) {
1494 utf8_copy(&c
->prompt_buffer
[c
->prompt_index
], &tmp
);
1496 c
->prompt_buffer
[c
->prompt_index
].size
= 0;
1498 memmove(c
->prompt_buffer
+ c
->prompt_index
+ 1,
1499 c
->prompt_buffer
+ c
->prompt_index
,
1500 (size
+ 1 - c
->prompt_index
) *
1501 sizeof *c
->prompt_buffer
);
1502 utf8_copy(&c
->prompt_buffer
[c
->prompt_index
], &tmp
);
1506 if (c
->prompt_flags
& PROMPT_SINGLE
) {
1507 if (utf8_strlen(c
->prompt_buffer
) != 1)
1508 status_prompt_clear(c
);
1510 s
= utf8_tocstr(c
->prompt_buffer
);
1511 if (c
->prompt_inputcb(c
, c
->prompt_data
, s
, 1) == 0)
1512 status_prompt_clear(c
);
1518 c
->flags
|= CLIENT_REDRAWSTATUS
;
1519 if (c
->prompt_flags
& PROMPT_INCREMENTAL
) {
1520 s
= utf8_tocstr(c
->prompt_buffer
);
1521 xasprintf(&cp
, "%c%s", prefix
, s
);
1522 c
->prompt_inputcb(c
, c
->prompt_data
, cp
, 0);
1529 /* Get previous line from the history. */
1531 status_prompt_up_history(u_int
*idx
, u_int type
)
1534 * History runs from 0 to size - 1. Index is from 0 to size. Zero is
1538 if (status_prompt_hsize
[type
] == 0 ||
1539 idx
[type
] == status_prompt_hsize
[type
])
1542 return (status_prompt_hlist
[type
][status_prompt_hsize
[type
] - idx
[type
]]);
1545 /* Get next line from the history. */
1547 status_prompt_down_history(u_int
*idx
, u_int type
)
1549 if (status_prompt_hsize
[type
] == 0 || idx
[type
] == 0)
1554 return (status_prompt_hlist
[type
][status_prompt_hsize
[type
] - idx
[type
]]);
1557 /* Add line to the history. */
1559 status_prompt_add_history(const char *line
, u_int type
)
1561 u_int i
, oldsize
, newsize
, freecount
, hlimit
, new = 1;
1564 oldsize
= status_prompt_hsize
[type
];
1566 strcmp(status_prompt_hlist
[type
][oldsize
- 1], line
) == 0)
1569 hlimit
= options_get_number(global_options
, "prompt-history-limit");
1570 if (hlimit
> oldsize
) {
1573 newsize
= oldsize
+ new;
1576 freecount
= oldsize
+ new - newsize
;
1577 if (freecount
> oldsize
)
1578 freecount
= oldsize
;
1581 for (i
= 0; i
< freecount
; i
++)
1582 free(status_prompt_hlist
[type
][i
]);
1583 movesize
= (oldsize
- freecount
) *
1584 sizeof *status_prompt_hlist
[type
];
1586 memmove(&status_prompt_hlist
[type
][0],
1587 &status_prompt_hlist
[type
][freecount
], movesize
);
1592 free(status_prompt_hlist
[type
]);
1593 status_prompt_hlist
[type
] = NULL
;
1594 } else if (newsize
!= oldsize
) {
1595 status_prompt_hlist
[type
] =
1596 xreallocarray(status_prompt_hlist
[type
], newsize
,
1597 sizeof *status_prompt_hlist
[type
]);
1600 if (new == 1 && newsize
> 0)
1601 status_prompt_hlist
[type
][newsize
- 1] = xstrdup(line
);
1602 status_prompt_hsize
[type
] = newsize
;
1605 /* Add to completion list. */
1607 status_prompt_add_list(char ***list
, u_int
*size
, const char *s
)
1611 for (i
= 0; i
< *size
; i
++) {
1612 if (strcmp((*list
)[i
], s
) == 0)
1615 *list
= xreallocarray(*list
, (*size
) + 1, sizeof **list
);
1616 (*list
)[(*size
)++] = xstrdup(s
);
1619 /* Build completion list. */
1621 status_prompt_complete_list(u_int
*size
, const char *s
, int at_start
)
1623 char **list
= NULL
, *tmp
;
1624 const char **layout
, *value
, *cp
;
1625 const struct cmd_entry
**cmdent
;
1626 const struct options_table_entry
*oe
;
1627 size_t slen
= strlen(s
), valuelen
;
1628 struct options_entry
*o
;
1629 struct options_array_item
*a
;
1630 const char *layouts
[] = {
1631 "even-horizontal", "even-vertical", "main-horizontal",
1632 "main-vertical", "tiled", NULL
1636 for (cmdent
= cmd_table
; *cmdent
!= NULL
; cmdent
++) {
1637 if (strncmp((*cmdent
)->name
, s
, slen
) == 0)
1638 status_prompt_add_list(&list
, size
, (*cmdent
)->name
);
1639 if ((*cmdent
)->alias
!= NULL
&&
1640 strncmp((*cmdent
)->alias
, s
, slen
) == 0)
1641 status_prompt_add_list(&list
, size
, (*cmdent
)->alias
);
1643 o
= options_get_only(global_options
, "command-alias");
1645 a
= options_array_first(o
);
1647 value
= options_array_item_value(a
)->string
;
1648 if ((cp
= strchr(value
, '=')) == NULL
)
1650 valuelen
= cp
- value
;
1651 if (slen
> valuelen
|| strncmp(value
, s
, slen
) != 0)
1654 xasprintf(&tmp
, "%.*s", (int)valuelen
, value
);
1655 status_prompt_add_list(&list
, size
, tmp
);
1659 a
= options_array_next(a
);
1664 for (oe
= options_table
; oe
->name
!= NULL
; oe
++) {
1665 if (strncmp(oe
->name
, s
, slen
) == 0)
1666 status_prompt_add_list(&list
, size
, oe
->name
);
1668 for (layout
= layouts
; *layout
!= NULL
; layout
++) {
1669 if (strncmp(*layout
, s
, slen
) == 0)
1670 status_prompt_add_list(&list
, size
, *layout
);
1675 /* Find longest prefix. */
1677 status_prompt_complete_prefix(char **list
, u_int size
)
1683 if (list
== NULL
|| size
== 0)
1685 out
= xstrdup(list
[0]);
1686 for (i
= 1; i
< size
; i
++) {
1687 j
= strlen(list
[i
]);
1688 if (j
> strlen(out
))
1690 for (; j
> 0; j
--) {
1691 if (out
[j
- 1] != list
[i
][j
- 1])
1698 /* Complete word menu callback. */
1700 status_prompt_menu_callback(__unused
struct menu
*menu
, u_int idx
, key_code key
,
1703 struct status_prompt_menu
*spm
= data
;
1704 struct client
*c
= spm
->c
;
1708 if (key
!= KEYC_NONE
) {
1710 if (spm
->flag
== '\0')
1711 s
= xstrdup(spm
->list
[idx
]);
1713 xasprintf(&s
, "-%c%s", spm
->flag
, spm
->list
[idx
]);
1714 if (c
->prompt_type
== PROMPT_TYPE_WINDOW_TARGET
) {
1715 free(c
->prompt_buffer
);
1716 c
->prompt_buffer
= utf8_fromcstr(s
);
1717 c
->prompt_index
= utf8_strlen(c
->prompt_buffer
);
1718 c
->flags
|= CLIENT_REDRAWSTATUS
;
1719 } else if (status_prompt_replace_complete(c
, s
))
1720 c
->flags
|= CLIENT_REDRAWSTATUS
;
1724 for (i
= 0; i
< spm
->size
; i
++)
1729 /* Show complete word menu. */
1731 status_prompt_complete_list_menu(struct client
*c
, char **list
, u_int size
,
1732 u_int offset
, char flag
)
1735 struct menu_item item
;
1736 struct status_prompt_menu
*spm
;
1737 u_int lines
= status_line_size(c
), height
, i
;
1742 if (c
->tty
.sy
- lines
< 3)
1745 spm
= xmalloc(sizeof *spm
);
1751 height
= c
->tty
.sy
- lines
- 2;
1756 spm
->start
= size
- height
;
1758 menu
= menu_create("");
1759 for (i
= spm
->start
; i
< size
; i
++) {
1760 item
.name
= list
[i
];
1761 item
.key
= '0' + (i
- spm
->start
);
1762 item
.command
= NULL
;
1763 menu_add_item(menu
, &item
, NULL
, c
, NULL
);
1766 if (options_get_number(c
->session
->options
, "status-position") == 0)
1769 py
= c
->tty
.sy
- 3 - height
;
1770 offset
+= utf8_cstrwidth(c
->prompt_string
);
1776 if (menu_display(menu
, MENU_NOMOUSE
|MENU_TAB
, 0, NULL
, offset
, py
, c
,
1777 BOX_LINES_DEFAULT
, NULL
, NULL
, NULL
, NULL
,
1778 status_prompt_menu_callback
, spm
) != 0) {
1786 /* Show complete word menu. */
1788 status_prompt_complete_window_menu(struct client
*c
, struct session
*s
,
1789 const char *word
, u_int offset
, char flag
)
1792 struct menu_item item
;
1793 struct status_prompt_menu
*spm
;
1795 char **list
= NULL
, *tmp
;
1796 u_int lines
= status_line_size(c
), height
;
1799 if (c
->tty
.sy
- lines
< 3)
1802 spm
= xmalloc(sizeof *spm
);
1806 height
= c
->tty
.sy
- lines
- 2;
1811 menu
= menu_create("");
1812 RB_FOREACH(wl
, winlinks
, &s
->windows
) {
1813 if (word
!= NULL
&& *word
!= '\0') {
1814 xasprintf(&tmp
, "%d", wl
->idx
);
1815 if (strncmp(tmp
, word
, strlen(word
)) != 0) {
1822 list
= xreallocarray(list
, size
+ 1, sizeof *list
);
1823 if (c
->prompt_type
== PROMPT_TYPE_WINDOW_TARGET
) {
1824 xasprintf(&tmp
, "%d (%s)", wl
->idx
, wl
->window
->name
);
1825 xasprintf(&list
[size
++], "%d", wl
->idx
);
1827 xasprintf(&tmp
, "%s:%d (%s)", s
->name
, wl
->idx
,
1829 xasprintf(&list
[size
++], "%s:%d", s
->name
, wl
->idx
);
1832 item
.key
= '0' + size
- 1;
1833 item
.command
= NULL
;
1834 menu_add_item(menu
, &item
, NULL
, c
, NULL
);
1847 xasprintf(&tmp
, "-%c%s", flag
, list
[0]);
1860 if (options_get_number(c
->session
->options
, "status-position") == 0)
1863 py
= c
->tty
.sy
- 3 - height
;
1864 offset
+= utf8_cstrwidth(c
->prompt_string
);
1870 if (menu_display(menu
, MENU_NOMOUSE
|MENU_TAB
, 0, NULL
, offset
, py
, c
,
1871 BOX_LINES_DEFAULT
, NULL
, NULL
, NULL
, NULL
,
1872 status_prompt_menu_callback
, spm
) != 0) {
1880 /* Sort complete list. */
1882 status_prompt_complete_sort(const void *a
, const void *b
)
1884 const char **aa
= (const char **)a
, **bb
= (const char **)b
;
1886 return (strcmp(*aa
, *bb
));
1889 /* Complete a session. */
1891 status_prompt_complete_session(char ***list
, u_int
*size
, const char *s
,
1894 struct session
*loop
;
1895 char *out
, *tmp
, n
[11];
1897 RB_FOREACH(loop
, sessions
, &sessions
) {
1898 if (*s
== '\0' || strncmp(loop
->name
, s
, strlen(s
)) == 0) {
1899 *list
= xreallocarray(*list
, (*size
) + 2,
1901 xasprintf(&(*list
)[(*size
)++], "%s:", loop
->name
);
1902 } else if (*s
== '$') {
1903 xsnprintf(n
, sizeof n
, "%u", loop
->id
);
1905 strncmp(n
, s
+ 1, strlen(s
) - 1) == 0) {
1906 *list
= xreallocarray(*list
, (*size
) + 2,
1908 xasprintf(&(*list
)[(*size
)++], "$%s:", n
);
1912 out
= status_prompt_complete_prefix(*list
, *size
);
1913 if (out
!= NULL
&& flag
!= '\0') {
1914 xasprintf(&tmp
, "-%c%s", flag
, out
);
1921 /* Complete word. */
1923 status_prompt_complete(struct client
*c
, const char *word
, u_int offset
)
1925 struct session
*session
;
1926 const char *s
, *colon
;
1927 char **list
= NULL
, *copy
= NULL
, *out
= NULL
;
1931 if (*word
== '\0' &&
1932 c
->prompt_type
!= PROMPT_TYPE_TARGET
&&
1933 c
->prompt_type
!= PROMPT_TYPE_WINDOW_TARGET
)
1936 if (c
->prompt_type
!= PROMPT_TYPE_TARGET
&&
1937 c
->prompt_type
!= PROMPT_TYPE_WINDOW_TARGET
&&
1938 strncmp(word
, "-t", 2) != 0 &&
1939 strncmp(word
, "-s", 2) != 0) {
1940 list
= status_prompt_complete_list(&size
, word
, offset
== 0);
1944 xasprintf(&out
, "%s ", list
[0]);
1946 out
= status_prompt_complete_prefix(list
, size
);
1950 if (c
->prompt_type
== PROMPT_TYPE_TARGET
||
1951 c
->prompt_type
== PROMPT_TYPE_WINDOW_TARGET
) {
1960 /* If this is a window completion, open the window menu. */
1961 if (c
->prompt_type
== PROMPT_TYPE_WINDOW_TARGET
) {
1962 out
= status_prompt_complete_window_menu(c
, c
->session
, s
,
1966 colon
= strchr(s
, ':');
1968 /* If there is no colon, complete as a session. */
1969 if (colon
== NULL
) {
1970 out
= status_prompt_complete_session(&list
, &size
, s
, flag
);
1974 /* If there is a colon but no period, find session and show a menu. */
1975 if (strchr(colon
+ 1, '.') == NULL
) {
1977 session
= c
->session
;
1980 *strchr(copy
, ':') = '\0';
1981 session
= session_find(copy
);
1983 if (session
== NULL
)
1986 out
= status_prompt_complete_window_menu(c
, session
, colon
+ 1,
1994 qsort(list
, size
, sizeof *list
, status_prompt_complete_sort
);
1995 for (i
= 0; i
< size
; i
++)
1996 log_debug("complete %u: %s", i
, list
[i
]);
1999 if (out
!= NULL
&& strcmp(word
, out
) == 0) {
2004 !status_prompt_complete_list_menu(c
, list
, size
, offset
, flag
)) {
2005 for (i
= 0; i
< size
; i
++)
2012 /* Return the type of the prompt as an enum. */
2014 status_prompt_type(const char *type
)
2018 for (i
= 0; i
< PROMPT_NTYPES
; i
++) {
2019 if (strcmp(type
, status_prompt_type_string(i
)) == 0)
2022 return (PROMPT_TYPE_INVALID
);
2025 /* Accessor for prompt_type_strings. */
2027 status_prompt_type_string(u_int type
)
2029 if (type
>= PROMPT_NTYPES
)
2031 return (prompt_type_strings
[type
]);