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 window at window list position. */
268 status_get_range(struct client
*c
, u_int x
, u_int y
)
270 struct status_line
*sl
= &c
->status
;
271 struct style_range
*sr
;
273 if (y
>= nitems(sl
->entries
))
275 TAILQ_FOREACH(sr
, &sl
->entries
[y
].ranges
, entry
) {
276 if (x
>= sr
->start
&& x
< sr
->end
)
282 /* Free all ranges. */
284 status_free_ranges(struct style_ranges
*srs
)
286 struct style_range
*sr
, *sr1
;
288 TAILQ_FOREACH_SAFE(sr
, srs
, entry
, sr1
) {
289 TAILQ_REMOVE(srs
, sr
, entry
);
294 /* Save old status line. */
296 status_push_screen(struct client
*c
)
298 struct status_line
*sl
= &c
->status
;
300 if (sl
->active
== &sl
->screen
) {
301 sl
->active
= xmalloc(sizeof *sl
->active
);
302 screen_init(sl
->active
, c
->tty
.sx
, status_line_size(c
), 0);
307 /* Restore old status line. */
309 status_pop_screen(struct client
*c
)
311 struct status_line
*sl
= &c
->status
;
313 if (--sl
->references
== 0) {
314 screen_free(sl
->active
);
316 sl
->active
= &sl
->screen
;
320 /* Initialize status line. */
322 status_init(struct client
*c
)
324 struct status_line
*sl
= &c
->status
;
327 for (i
= 0; i
< nitems(sl
->entries
); i
++)
328 TAILQ_INIT(&sl
->entries
[i
].ranges
);
330 screen_init(&sl
->screen
, c
->tty
.sx
, 1, 0);
331 sl
->active
= &sl
->screen
;
334 /* Free status line. */
336 status_free(struct client
*c
)
338 struct status_line
*sl
= &c
->status
;
341 for (i
= 0; i
< nitems(sl
->entries
); i
++) {
342 status_free_ranges(&sl
->entries
[i
].ranges
);
343 free((void *)sl
->entries
[i
].expanded
);
346 if (event_initialized(&sl
->timer
))
347 evtimer_del(&sl
->timer
);
349 if (sl
->active
!= &sl
->screen
) {
350 screen_free(sl
->active
);
353 screen_free(&sl
->screen
);
356 /* Draw status line for client. */
358 status_redraw(struct client
*c
)
360 struct status_line
*sl
= &c
->status
;
361 struct status_line_entry
*sle
;
362 struct session
*s
= c
->session
;
363 struct screen_write_ctx ctx
;
365 u_int lines
, i
, n
, width
= c
->tty
.sx
;
366 int flags
, force
= 0, changed
= 0, fg
, bg
;
367 struct options_entry
*o
;
368 union options_value
*ov
;
369 struct format_tree
*ft
;
372 log_debug("%s enter", __func__
);
374 /* Shouldn't get here if not the active screen. */
375 if (sl
->active
!= &sl
->screen
)
376 fatalx("not the active screen");
378 /* No status line? */
379 lines
= status_line_size(c
);
380 if (c
->tty
.sy
== 0 || lines
== 0)
383 /* Create format tree. */
384 flags
= FORMAT_STATUS
;
385 if (c
->flags
& CLIENT_STATUSFORCE
)
386 flags
|= FORMAT_FORCE
;
387 ft
= format_create(c
, NULL
, FORMAT_NONE
, flags
);
388 format_defaults(ft
, c
, NULL
, NULL
, NULL
);
390 /* Set up default colour. */
391 style_apply(&gc
, s
->options
, "status-style", ft
);
392 fg
= options_get_number(s
->options
, "status-fg");
393 if (!COLOUR_DEFAULT(fg
))
395 bg
= options_get_number(s
->options
, "status-bg");
396 if (!COLOUR_DEFAULT(bg
))
398 if (!grid_cells_equal(&gc
, &sl
->style
)) {
400 memcpy(&sl
->style
, &gc
, sizeof sl
->style
);
403 /* Resize the target screen. */
404 if (screen_size_x(&sl
->screen
) != width
||
405 screen_size_y(&sl
->screen
) != lines
) {
406 screen_resize(&sl
->screen
, width
, lines
, 0);
409 screen_write_start(&ctx
, &sl
->screen
);
411 /* Write the status lines. */
412 o
= options_get(s
->options
, "status-format");
414 for (n
= 0; n
< width
* lines
; n
++)
415 screen_write_putc(&ctx
, &gc
, ' ');
417 for (i
= 0; i
< lines
; i
++) {
418 screen_write_cursormove(&ctx
, 0, i
, 0);
420 ov
= options_array_get(o
, i
);
422 for (n
= 0; n
< width
; n
++)
423 screen_write_putc(&ctx
, &gc
, ' ');
426 sle
= &sl
->entries
[i
];
428 expanded
= format_expand_time(ft
, ov
->string
);
430 sle
->expanded
!= NULL
&&
431 strcmp(expanded
, sle
->expanded
) == 0) {
437 for (n
= 0; n
< width
; n
++)
438 screen_write_putc(&ctx
, &gc
, ' ');
439 screen_write_cursormove(&ctx
, 0, i
, 0);
441 status_free_ranges(&sle
->ranges
);
442 format_draw(&ctx
, &gc
, width
, expanded
, &sle
->ranges
,
446 sle
->expanded
= expanded
;
449 screen_write_stop(&ctx
);
451 /* Free the format tree. */
454 /* Return if the status line has changed. */
455 log_debug("%s exit: force=%d, changed=%d", __func__
, force
, changed
);
456 return (force
|| changed
);
459 /* Set a status line message. */
461 status_message_set(struct client
*c
, int delay
, int ignore_styles
,
462 int ignore_keys
, const char *fmt
, ...)
467 status_message_clear(c
);
468 status_push_screen(c
);
471 xvasprintf(&c
->message_string
, fmt
, ap
);
474 server_add_message("%s message: %s", c
->name
, c
->message_string
);
477 * With delay -1, the display-time option is used; zero means wait for
478 * key press; more than zero is the actual delay time in milliseconds.
481 delay
= options_get_number(c
->session
->options
, "display-time");
483 tv
.tv_sec
= delay
/ 1000;
484 tv
.tv_usec
= (delay
% 1000) * 1000L;
486 if (event_initialized(&c
->message_timer
))
487 evtimer_del(&c
->message_timer
);
488 evtimer_set(&c
->message_timer
, status_message_callback
, c
);
490 evtimer_add(&c
->message_timer
, &tv
);
494 c
->message_ignore_keys
= ignore_keys
;
495 c
->message_ignore_styles
= ignore_styles
;
497 c
->tty
.flags
|= (TTY_NOCURSOR
|TTY_FREEZE
);
498 c
->flags
|= CLIENT_REDRAWSTATUS
;
501 /* Clear status line message. */
503 status_message_clear(struct client
*c
)
505 if (c
->message_string
== NULL
)
508 free(c
->message_string
);
509 c
->message_string
= NULL
;
511 if (c
->prompt_string
== NULL
)
512 c
->tty
.flags
&= ~(TTY_NOCURSOR
|TTY_FREEZE
);
513 c
->flags
|= CLIENT_ALLREDRAWFLAGS
; /* was frozen and may have changed */
515 status_pop_screen(c
);
518 /* Clear status line message after timer expires. */
520 status_message_callback(__unused
int fd
, __unused
short event
, void *data
)
522 struct client
*c
= data
;
524 status_message_clear(c
);
527 /* Draw client message on status line of present else on last line. */
529 status_message_redraw(struct client
*c
)
531 struct status_line
*sl
= &c
->status
;
532 struct screen_write_ctx ctx
;
533 struct session
*s
= c
->session
;
534 struct screen old_screen
;
538 struct format_tree
*ft
;
540 if (c
->tty
.sx
== 0 || c
->tty
.sy
== 0)
542 memcpy(&old_screen
, sl
->active
, sizeof old_screen
);
544 lines
= status_line_size(c
);
547 screen_init(sl
->active
, c
->tty
.sx
, lines
, 0);
549 len
= screen_write_strlen("%s", c
->message_string
);
553 ft
= format_create_defaults(NULL
, c
, NULL
, NULL
, NULL
);
554 style_apply(&gc
, s
->options
, "message-style", ft
);
557 screen_write_start(&ctx
, sl
->active
);
558 screen_write_fast_copy(&ctx
, &sl
->screen
, 0, 0, c
->tty
.sx
, lines
- 1);
559 screen_write_cursormove(&ctx
, 0, lines
- 1, 0);
560 for (offset
= 0; offset
< c
->tty
.sx
; offset
++)
561 screen_write_putc(&ctx
, &gc
, ' ');
562 screen_write_cursormove(&ctx
, 0, lines
- 1, 0);
563 if (c
->message_ignore_styles
)
564 screen_write_nputs(&ctx
, len
, &gc
, "%s", c
->message_string
);
566 format_draw(&ctx
, &gc
, c
->tty
.sx
, c
->message_string
, NULL
, 0);
567 screen_write_stop(&ctx
);
569 if (grid_compare(sl
->active
->grid
, old_screen
.grid
) == 0) {
570 screen_free(&old_screen
);
573 screen_free(&old_screen
);
577 /* Enable status line prompt. */
579 status_prompt_set(struct client
*c
, struct cmd_find_state
*fs
,
580 const char *msg
, const char *input
, prompt_input_cb inputcb
,
581 prompt_free_cb freecb
, void *data
, int flags
, enum prompt_type prompt_type
)
583 struct format_tree
*ft
;
587 ft
= format_create_from_state(NULL
, c
, fs
);
589 ft
= format_create_defaults(NULL
, c
, NULL
, NULL
, NULL
);
593 if (flags
& PROMPT_NOFORMAT
)
594 tmp
= xstrdup(input
);
596 tmp
= format_expand_time(ft
, input
);
598 status_message_clear(c
);
599 status_prompt_clear(c
);
600 status_push_screen(c
);
602 c
->prompt_string
= format_expand_time(ft
, msg
);
604 if (flags
& PROMPT_INCREMENTAL
) {
605 c
->prompt_last
= xstrdup(tmp
);
606 c
->prompt_buffer
= utf8_fromcstr("");
608 c
->prompt_last
= NULL
;
609 c
->prompt_buffer
= utf8_fromcstr(tmp
);
611 c
->prompt_index
= utf8_strlen(c
->prompt_buffer
);
613 c
->prompt_inputcb
= inputcb
;
614 c
->prompt_freecb
= freecb
;
615 c
->prompt_data
= data
;
617 memset(c
->prompt_hindex
, 0, sizeof c
->prompt_hindex
);
619 c
->prompt_flags
= flags
;
620 c
->prompt_type
= prompt_type
;
621 c
->prompt_mode
= PROMPT_ENTRY
;
623 if (~flags
& PROMPT_INCREMENTAL
)
624 c
->tty
.flags
|= (TTY_NOCURSOR
|TTY_FREEZE
);
625 c
->flags
|= CLIENT_REDRAWSTATUS
;
627 if (flags
& PROMPT_INCREMENTAL
)
628 c
->prompt_inputcb(c
, c
->prompt_data
, "=", 0);
634 /* Remove status line prompt. */
636 status_prompt_clear(struct client
*c
)
638 if (c
->prompt_string
== NULL
)
641 if (c
->prompt_freecb
!= NULL
&& c
->prompt_data
!= NULL
)
642 c
->prompt_freecb(c
->prompt_data
);
644 free(c
->prompt_last
);
645 c
->prompt_last
= NULL
;
647 free(c
->prompt_string
);
648 c
->prompt_string
= NULL
;
650 free(c
->prompt_buffer
);
651 c
->prompt_buffer
= NULL
;
653 free(c
->prompt_saved
);
654 c
->prompt_saved
= NULL
;
656 c
->tty
.flags
&= ~(TTY_NOCURSOR
|TTY_FREEZE
);
657 c
->flags
|= CLIENT_ALLREDRAWFLAGS
; /* was frozen and may have changed */
659 status_pop_screen(c
);
662 /* Update status line prompt with a new prompt string. */
664 status_prompt_update(struct client
*c
, const char *msg
, const char *input
)
666 struct format_tree
*ft
;
669 ft
= format_create(c
, NULL
, FORMAT_NONE
, 0);
670 format_defaults(ft
, c
, NULL
, NULL
, NULL
);
672 tmp
= format_expand_time(ft
, input
);
674 free(c
->prompt_string
);
675 c
->prompt_string
= format_expand_time(ft
, msg
);
677 free(c
->prompt_buffer
);
678 c
->prompt_buffer
= utf8_fromcstr(tmp
);
679 c
->prompt_index
= utf8_strlen(c
->prompt_buffer
);
681 memset(c
->prompt_hindex
, 0, sizeof c
->prompt_hindex
);
683 c
->flags
|= CLIENT_REDRAWSTATUS
;
689 /* Draw client prompt on status line of present else on last line. */
691 status_prompt_redraw(struct client
*c
)
693 struct status_line
*sl
= &c
->status
;
694 struct screen_write_ctx ctx
;
695 struct session
*s
= c
->session
;
696 struct screen old_screen
;
697 u_int i
, lines
, offset
, left
, start
, width
;
698 u_int pcursor
, pwidth
;
699 struct grid_cell gc
, cursorgc
;
700 struct format_tree
*ft
;
702 if (c
->tty
.sx
== 0 || c
->tty
.sy
== 0)
704 memcpy(&old_screen
, sl
->active
, sizeof old_screen
);
706 lines
= status_line_size(c
);
709 screen_init(sl
->active
, c
->tty
.sx
, lines
, 0);
711 ft
= format_create_defaults(NULL
, c
, NULL
, NULL
, NULL
);
712 if (c
->prompt_mode
== PROMPT_COMMAND
)
713 style_apply(&gc
, s
->options
, "message-command-style", ft
);
715 style_apply(&gc
, s
->options
, "message-style", ft
);
718 memcpy(&cursorgc
, &gc
, sizeof cursorgc
);
719 cursorgc
.attr
^= GRID_ATTR_REVERSE
;
721 start
= screen_write_strlen("%s", c
->prompt_string
);
722 if (start
> c
->tty
.sx
)
725 screen_write_start(&ctx
, sl
->active
);
726 screen_write_fast_copy(&ctx
, &sl
->screen
, 0, 0, c
->tty
.sx
, lines
- 1);
727 screen_write_cursormove(&ctx
, 0, lines
- 1, 0);
728 for (offset
= 0; offset
< c
->tty
.sx
; offset
++)
729 screen_write_putc(&ctx
, &gc
, ' ');
730 screen_write_cursormove(&ctx
, 0, lines
- 1, 0);
731 screen_write_nputs(&ctx
, start
, &gc
, "%s", c
->prompt_string
);
732 screen_write_cursormove(&ctx
, start
, lines
- 1, 0);
734 left
= c
->tty
.sx
- start
;
738 pcursor
= utf8_strwidth(c
->prompt_buffer
, c
->prompt_index
);
739 pwidth
= utf8_strwidth(c
->prompt_buffer
, -1);
740 if (pcursor
>= left
) {
742 * The cursor would be outside the screen so start drawing
743 * with it on the right.
745 offset
= (pcursor
- left
) + 1;
753 for (i
= 0; c
->prompt_buffer
[i
].size
!= 0; i
++) {
754 if (width
< offset
) {
755 width
+= c
->prompt_buffer
[i
].width
;
758 if (width
>= offset
+ pwidth
)
760 width
+= c
->prompt_buffer
[i
].width
;
761 if (width
> offset
+ pwidth
)
764 if (i
!= c
->prompt_index
) {
765 utf8_copy(&gc
.data
, &c
->prompt_buffer
[i
]);
766 screen_write_cell(&ctx
, &gc
);
768 utf8_copy(&cursorgc
.data
, &c
->prompt_buffer
[i
]);
769 screen_write_cell(&ctx
, &cursorgc
);
772 if (sl
->active
->cx
< screen_size_x(sl
->active
) && c
->prompt_index
>= i
)
773 screen_write_putc(&ctx
, &cursorgc
, ' ');
776 screen_write_stop(&ctx
);
778 if (grid_compare(sl
->active
->grid
, old_screen
.grid
) == 0) {
779 screen_free(&old_screen
);
782 screen_free(&old_screen
);
786 /* Is this a separator? */
788 status_prompt_in_list(const char *ws
, const struct utf8_data
*ud
)
790 if (ud
->size
!= 1 || ud
->width
!= 1)
792 return (strchr(ws
, *ud
->data
) != NULL
);
795 /* Is this a space? */
797 status_prompt_space(const struct utf8_data
*ud
)
799 if (ud
->size
!= 1 || ud
->width
!= 1)
801 return (*ud
->data
== ' ');
805 * Translate key from vi to emacs. Return 0 to drop key, 1 to process the key
806 * as an emacs key; return 2 to append to the buffer.
809 status_prompt_translate_key(struct client
*c
, key_code key
, key_code
*new_key
)
811 if (c
->prompt_mode
== PROMPT_ENTRY
) {
813 case '\001': /* C-a */
814 case '\003': /* C-c */
815 case '\005': /* C-e */
816 case '\007': /* C-g */
817 case '\010': /* C-h */
818 case '\011': /* Tab */
819 case '\013': /* C-k */
820 case '\016': /* C-n */
821 case '\020': /* C-p */
822 case '\024': /* C-t */
823 case '\025': /* C-u */
824 case '\027': /* C-w */
825 case '\031': /* C-y */
828 case KEYC_LEFT
|KEYC_CTRL
:
829 case KEYC_RIGHT
|KEYC_CTRL
:
840 case '\033': /* Escape */
841 c
->prompt_mode
= PROMPT_COMMAND
;
842 c
->flags
|= CLIENT_REDRAWSTATUS
;
851 *new_key
= KEYC_LEFT
;
858 c
->prompt_mode
= PROMPT_ENTRY
;
859 c
->flags
|= CLIENT_REDRAWSTATUS
;
860 break; /* switch mode and... */
862 c
->prompt_mode
= PROMPT_ENTRY
;
863 c
->flags
|= CLIENT_REDRAWSTATUS
;
864 *new_key
= '\025'; /* C-u */
867 case '\033': /* Escape */
868 c
->prompt_mode
= PROMPT_ENTRY
;
869 c
->flags
|= CLIENT_REDRAWSTATUS
;
881 *new_key
= KEYC_HOME
;
885 *new_key
= '\013'; /* C-k */
889 *new_key
= KEYC_BSPACE
;
892 *new_key
= 'b'|KEYC_META
;
895 *new_key
= 'B'|KEYC_VI
;
898 *new_key
= '\025'; /* C-u */
901 *new_key
= 'e'|KEYC_VI
;
904 *new_key
= 'E'|KEYC_VI
;
907 *new_key
= 'w'|KEYC_VI
;
910 *new_key
= 'W'|KEYC_VI
;
913 *new_key
= '\031'; /* C-y */
916 *new_key
= '\003'; /* C-c */
925 *new_key
= KEYC_DOWN
;
929 *new_key
= KEYC_LEFT
;
934 *new_key
= KEYC_RIGHT
;
940 case '\010' /* C-h */:
941 case '\003' /* C-c */:
949 /* Paste into prompt. */
951 status_prompt_paste(struct client
*c
)
953 struct paste_buffer
*pb
;
955 size_t size
, n
, bufsize
;
957 struct utf8_data
*ud
, *udp
;
958 enum utf8_state more
;
960 size
= utf8_strlen(c
->prompt_buffer
);
961 if (c
->prompt_saved
!= NULL
) {
962 ud
= c
->prompt_saved
;
963 n
= utf8_strlen(c
->prompt_saved
);
965 if ((pb
= paste_get_top(NULL
)) == NULL
)
967 bufdata
= paste_buffer_data(pb
, &bufsize
);
968 ud
= xreallocarray(NULL
, bufsize
+ 1, sizeof *ud
);
970 for (i
= 0; i
!= bufsize
; /* nothing */) {
971 more
= utf8_open(udp
, bufdata
[i
]);
972 if (more
== UTF8_MORE
) {
973 while (++i
!= bufsize
&& more
== UTF8_MORE
)
974 more
= utf8_append(udp
, bufdata
[i
]);
975 if (more
== UTF8_DONE
) {
981 if (bufdata
[i
] <= 31 || bufdata
[i
] >= 127)
983 utf8_set(udp
, bufdata
[i
]);
993 c
->prompt_buffer
= xreallocarray(c
->prompt_buffer
, size
+ n
+ 1,
994 sizeof *c
->prompt_buffer
);
995 if (c
->prompt_index
== size
) {
996 memcpy(c
->prompt_buffer
+ c
->prompt_index
, ud
,
997 n
* sizeof *c
->prompt_buffer
);
998 c
->prompt_index
+= n
;
999 c
->prompt_buffer
[c
->prompt_index
].size
= 0;
1001 memmove(c
->prompt_buffer
+ c
->prompt_index
+ n
,
1002 c
->prompt_buffer
+ c
->prompt_index
,
1003 (size
+ 1 - c
->prompt_index
) * sizeof *c
->prompt_buffer
);
1004 memcpy(c
->prompt_buffer
+ c
->prompt_index
, ud
,
1005 n
* sizeof *c
->prompt_buffer
);
1006 c
->prompt_index
+= n
;
1009 if (ud
!= c
->prompt_saved
)
1014 /* Finish completion. */
1016 status_prompt_replace_complete(struct client
*c
, const char *s
)
1018 char word
[64], *allocated
= NULL
;
1019 size_t size
, n
, off
, idx
, used
;
1020 struct utf8_data
*first
, *last
, *ud
;
1022 /* Work out where the cursor currently is. */
1023 idx
= c
->prompt_index
;
1026 size
= utf8_strlen(c
->prompt_buffer
);
1028 /* Find the word we are in. */
1029 first
= &c
->prompt_buffer
[idx
];
1030 while (first
> c
->prompt_buffer
&& !status_prompt_space(first
))
1032 while (first
->size
!= 0 && status_prompt_space(first
))
1034 last
= &c
->prompt_buffer
[idx
];
1035 while (last
->size
!= 0 && !status_prompt_space(last
))
1037 while (last
> c
->prompt_buffer
&& status_prompt_space(last
))
1039 if (last
->size
!= 0)
1045 for (ud
= first
; ud
< last
; ud
++) {
1046 if (used
+ ud
->size
>= sizeof word
)
1048 memcpy(word
+ used
, ud
->data
, ud
->size
);
1056 /* Try to complete it. */
1058 allocated
= status_prompt_complete(c
, word
,
1059 first
- c
->prompt_buffer
);
1060 if (allocated
== NULL
)
1065 /* Trim out word. */
1066 n
= size
- (last
- c
->prompt_buffer
) + 1; /* with \0 */
1067 memmove(first
, last
, n
* sizeof *c
->prompt_buffer
);
1068 size
-= last
- first
;
1070 /* Insert the new word. */
1072 off
= first
- c
->prompt_buffer
;
1073 c
->prompt_buffer
= xreallocarray(c
->prompt_buffer
, size
+ 1,
1074 sizeof *c
->prompt_buffer
);
1075 first
= c
->prompt_buffer
+ off
;
1076 memmove(first
+ strlen(s
), first
, n
* sizeof *c
->prompt_buffer
);
1077 for (idx
= 0; idx
< strlen(s
); idx
++)
1078 utf8_set(&first
[idx
], s
[idx
]);
1079 c
->prompt_index
= (first
- c
->prompt_buffer
) + strlen(s
);
1085 /* Prompt forward to the next beginning of a word. */
1087 status_prompt_forward_word(struct client
*c
, size_t size
, int vi
,
1088 const char *separators
)
1090 size_t idx
= c
->prompt_index
;
1091 int word_is_separators
;
1093 /* In emacs mode, skip until the first non-whitespace character. */
1095 while (idx
!= size
&&
1096 status_prompt_space(&c
->prompt_buffer
[idx
]))
1099 /* Can't move forward if we're already at the end. */
1101 c
->prompt_index
= idx
;
1105 /* Determine the current character class (separators or not). */
1106 word_is_separators
= status_prompt_in_list(separators
,
1107 &c
->prompt_buffer
[idx
]) &&
1108 !status_prompt_space(&c
->prompt_buffer
[idx
]);
1110 /* Skip ahead until the first space or opposite character class. */
1113 if (status_prompt_space(&c
->prompt_buffer
[idx
])) {
1114 /* In vi mode, go to the start of the next word. */
1116 while (idx
!= size
&&
1117 status_prompt_space(&c
->prompt_buffer
[idx
]))
1121 } while (idx
!= size
&& word_is_separators
== status_prompt_in_list(
1122 separators
, &c
->prompt_buffer
[idx
]));
1124 c
->prompt_index
= idx
;
1127 /* Prompt forward to the next end of a word. */
1129 status_prompt_end_word(struct client
*c
, size_t size
, const char *separators
)
1131 size_t idx
= c
->prompt_index
;
1132 int word_is_separators
;
1134 /* Can't move forward if we're already at the end. */
1138 /* Find the next word. */
1142 c
->prompt_index
= idx
;
1145 } while (status_prompt_space(&c
->prompt_buffer
[idx
]));
1147 /* Determine the character class (separators or not). */
1148 word_is_separators
= status_prompt_in_list(separators
,
1149 &c
->prompt_buffer
[idx
]);
1151 /* Skip ahead until the next space or opposite character class. */
1156 } while (!status_prompt_space(&c
->prompt_buffer
[idx
]) &&
1157 word_is_separators
== status_prompt_in_list(separators
,
1158 &c
->prompt_buffer
[idx
]));
1160 /* Back up to the previous character to stop at the end of the word. */
1161 c
->prompt_index
= idx
- 1;
1164 /* Prompt backward to the previous beginning of a word. */
1166 status_prompt_backward_word(struct client
*c
, const char *separators
)
1168 size_t idx
= c
->prompt_index
;
1169 int word_is_separators
;
1171 /* Find non-whitespace. */
1174 if (!status_prompt_space(&c
->prompt_buffer
[idx
]))
1177 word_is_separators
= status_prompt_in_list(separators
,
1178 &c
->prompt_buffer
[idx
]);
1180 /* Find the character before the beginning of the word. */
1183 if (status_prompt_space(&c
->prompt_buffer
[idx
]) ||
1184 word_is_separators
!= status_prompt_in_list(separators
,
1185 &c
->prompt_buffer
[idx
])) {
1186 /* Go back to the word. */
1191 c
->prompt_index
= idx
;
1194 /* Handle keys in prompt. */
1196 status_prompt_key(struct client
*c
, key_code key
)
1198 struct options
*oo
= c
->session
->options
;
1199 char *s
, *cp
, prefix
= '=';
1200 const char *histstr
, *separators
= NULL
, *keystring
;
1202 struct utf8_data tmp
;
1203 int keys
, word_is_separators
;
1205 if (c
->prompt_flags
& PROMPT_KEY
) {
1206 keystring
= key_string_lookup_key(key
, 0);
1207 c
->prompt_inputcb(c
, c
->prompt_data
, keystring
, 1);
1208 status_prompt_clear(c
);
1211 size
= utf8_strlen(c
->prompt_buffer
);
1213 if (c
->prompt_flags
& PROMPT_NUMERIC
) {
1214 if (key
>= '0' && key
<= '9')
1216 s
= utf8_tocstr(c
->prompt_buffer
);
1217 c
->prompt_inputcb(c
, c
->prompt_data
, s
, 1);
1218 status_prompt_clear(c
);
1222 key
&= ~KEYC_MASK_FLAGS
;
1224 keys
= options_get_number(c
->session
->options
, "status-keys");
1225 if (keys
== MODEKEY_VI
) {
1226 switch (status_prompt_translate_key(c
, key
, &key
)) {
1239 case '\002': /* C-b */
1240 if (c
->prompt_index
> 0) {
1246 case '\006': /* C-f */
1247 if (c
->prompt_index
< size
) {
1253 case '\001': /* C-a */
1254 if (c
->prompt_index
!= 0) {
1255 c
->prompt_index
= 0;
1260 case '\005': /* C-e */
1261 if (c
->prompt_index
!= size
) {
1262 c
->prompt_index
= size
;
1266 case '\011': /* Tab */
1267 if (status_prompt_replace_complete(c
, NULL
))
1271 case '\010': /* C-h */
1272 if (c
->prompt_index
!= 0) {
1273 if (c
->prompt_index
== size
)
1274 c
->prompt_buffer
[--c
->prompt_index
].size
= 0;
1276 memmove(c
->prompt_buffer
+ c
->prompt_index
- 1,
1277 c
->prompt_buffer
+ c
->prompt_index
,
1278 (size
+ 1 - c
->prompt_index
) *
1279 sizeof *c
->prompt_buffer
);
1286 case '\004': /* C-d */
1287 if (c
->prompt_index
!= size
) {
1288 memmove(c
->prompt_buffer
+ c
->prompt_index
,
1289 c
->prompt_buffer
+ c
->prompt_index
+ 1,
1290 (size
+ 1 - c
->prompt_index
) *
1291 sizeof *c
->prompt_buffer
);
1295 case '\025': /* C-u */
1296 c
->prompt_buffer
[0].size
= 0;
1297 c
->prompt_index
= 0;
1299 case '\013': /* C-k */
1300 if (c
->prompt_index
< size
) {
1301 c
->prompt_buffer
[c
->prompt_index
].size
= 0;
1305 case '\027': /* C-w */
1306 separators
= options_get_string(oo
, "word-separators");
1307 idx
= c
->prompt_index
;
1309 /* Find non-whitespace. */
1312 if (!status_prompt_space(&c
->prompt_buffer
[idx
]))
1315 word_is_separators
= status_prompt_in_list(separators
,
1316 &c
->prompt_buffer
[idx
]);
1318 /* Find the character before the beginning of the word. */
1321 if (status_prompt_space(&c
->prompt_buffer
[idx
]) ||
1322 word_is_separators
!= status_prompt_in_list(
1323 separators
, &c
->prompt_buffer
[idx
])) {
1324 /* Go back to the word. */
1330 free(c
->prompt_saved
);
1331 c
->prompt_saved
= xcalloc(sizeof *c
->prompt_buffer
,
1332 (c
->prompt_index
- idx
) + 1);
1333 memcpy(c
->prompt_saved
, c
->prompt_buffer
+ idx
,
1334 (c
->prompt_index
- idx
) * sizeof *c
->prompt_buffer
);
1336 memmove(c
->prompt_buffer
+ idx
,
1337 c
->prompt_buffer
+ c
->prompt_index
,
1338 (size
+ 1 - c
->prompt_index
) *
1339 sizeof *c
->prompt_buffer
);
1340 memset(c
->prompt_buffer
+ size
- (c
->prompt_index
- idx
),
1341 '\0', (c
->prompt_index
- idx
) * sizeof *c
->prompt_buffer
);
1342 c
->prompt_index
= idx
;
1345 case KEYC_RIGHT
|KEYC_CTRL
:
1347 separators
= options_get_string(oo
, "word-separators");
1348 status_prompt_forward_word(c
, size
, 0, separators
);
1351 status_prompt_end_word(c
, size
, "");
1354 separators
= options_get_string(oo
, "word-separators");
1355 status_prompt_end_word(c
, size
, separators
);
1358 status_prompt_forward_word(c
, size
, 1, "");
1361 separators
= options_get_string(oo
, "word-separators");
1362 status_prompt_forward_word(c
, size
, 1, separators
);
1365 status_prompt_backward_word(c
, "");
1367 case KEYC_LEFT
|KEYC_CTRL
:
1369 separators
= options_get_string(oo
, "word-separators");
1370 status_prompt_backward_word(c
, separators
);
1373 case '\020': /* C-p */
1374 histstr
= status_prompt_up_history(c
->prompt_hindex
,
1376 if (histstr
== NULL
)
1378 free(c
->prompt_buffer
);
1379 c
->prompt_buffer
= utf8_fromcstr(histstr
);
1380 c
->prompt_index
= utf8_strlen(c
->prompt_buffer
);
1383 case '\016': /* C-n */
1384 histstr
= status_prompt_down_history(c
->prompt_hindex
,
1386 if (histstr
== NULL
)
1388 free(c
->prompt_buffer
);
1389 c
->prompt_buffer
= utf8_fromcstr(histstr
);
1390 c
->prompt_index
= utf8_strlen(c
->prompt_buffer
);
1392 case '\031': /* C-y */
1393 if (status_prompt_paste(c
))
1396 case '\024': /* C-t */
1397 idx
= c
->prompt_index
;
1401 utf8_copy(&tmp
, &c
->prompt_buffer
[idx
- 2]);
1402 utf8_copy(&c
->prompt_buffer
[idx
- 2],
1403 &c
->prompt_buffer
[idx
- 1]);
1404 utf8_copy(&c
->prompt_buffer
[idx
- 1], &tmp
);
1405 c
->prompt_index
= idx
;
1411 s
= utf8_tocstr(c
->prompt_buffer
);
1413 status_prompt_add_history(s
, c
->prompt_type
);
1414 if (c
->prompt_inputcb(c
, c
->prompt_data
, s
, 1) == 0)
1415 status_prompt_clear(c
);
1418 case '\033': /* Escape */
1419 case '\003': /* C-c */
1420 case '\007': /* C-g */
1421 if (c
->prompt_inputcb(c
, c
->prompt_data
, NULL
, 1) == 0)
1422 status_prompt_clear(c
);
1424 case '\022': /* C-r */
1425 if (~c
->prompt_flags
& PROMPT_INCREMENTAL
)
1427 if (c
->prompt_buffer
[0].size
== 0) {
1429 free(c
->prompt_buffer
);
1430 c
->prompt_buffer
= utf8_fromcstr(c
->prompt_last
);
1431 c
->prompt_index
= utf8_strlen(c
->prompt_buffer
);
1435 case '\023': /* C-s */
1436 if (~c
->prompt_flags
& PROMPT_INCREMENTAL
)
1438 if (c
->prompt_buffer
[0].size
== 0) {
1440 free(c
->prompt_buffer
);
1441 c
->prompt_buffer
= utf8_fromcstr(c
->prompt_last
);
1442 c
->prompt_index
= utf8_strlen(c
->prompt_buffer
);
1450 c
->flags
|= CLIENT_REDRAWSTATUS
;
1454 if (key
<= 0x1f || (key
>= KEYC_BASE
&& key
< KEYC_BASE_END
))
1457 utf8_set(&tmp
, key
);
1458 else if (KEYC_IS_UNICODE(key
))
1459 utf8_to_data(key
, &tmp
);
1463 c
->prompt_buffer
= xreallocarray(c
->prompt_buffer
, size
+ 2,
1464 sizeof *c
->prompt_buffer
);
1466 if (c
->prompt_index
== size
) {
1467 utf8_copy(&c
->prompt_buffer
[c
->prompt_index
], &tmp
);
1469 c
->prompt_buffer
[c
->prompt_index
].size
= 0;
1471 memmove(c
->prompt_buffer
+ c
->prompt_index
+ 1,
1472 c
->prompt_buffer
+ c
->prompt_index
,
1473 (size
+ 1 - c
->prompt_index
) *
1474 sizeof *c
->prompt_buffer
);
1475 utf8_copy(&c
->prompt_buffer
[c
->prompt_index
], &tmp
);
1479 if (c
->prompt_flags
& PROMPT_SINGLE
) {
1480 if (utf8_strlen(c
->prompt_buffer
) != 1)
1481 status_prompt_clear(c
);
1483 s
= utf8_tocstr(c
->prompt_buffer
);
1484 if (c
->prompt_inputcb(c
, c
->prompt_data
, s
, 1) == 0)
1485 status_prompt_clear(c
);
1491 c
->flags
|= CLIENT_REDRAWSTATUS
;
1492 if (c
->prompt_flags
& PROMPT_INCREMENTAL
) {
1493 s
= utf8_tocstr(c
->prompt_buffer
);
1494 xasprintf(&cp
, "%c%s", prefix
, s
);
1495 c
->prompt_inputcb(c
, c
->prompt_data
, cp
, 0);
1502 /* Get previous line from the history. */
1504 status_prompt_up_history(u_int
*idx
, u_int type
)
1507 * History runs from 0 to size - 1. Index is from 0 to size. Zero is
1511 if (status_prompt_hsize
[type
] == 0 ||
1512 idx
[type
] == status_prompt_hsize
[type
])
1515 return (status_prompt_hlist
[type
][status_prompt_hsize
[type
] - idx
[type
]]);
1518 /* Get next line from the history. */
1520 status_prompt_down_history(u_int
*idx
, u_int type
)
1522 if (status_prompt_hsize
[type
] == 0 || idx
[type
] == 0)
1527 return (status_prompt_hlist
[type
][status_prompt_hsize
[type
] - idx
[type
]]);
1530 /* Add line to the history. */
1532 status_prompt_add_history(const char *line
, u_int type
)
1534 u_int i
, oldsize
, newsize
, freecount
, hlimit
, new = 1;
1537 oldsize
= status_prompt_hsize
[type
];
1539 strcmp(status_prompt_hlist
[type
][oldsize
- 1], line
) == 0)
1542 hlimit
= options_get_number(global_options
, "prompt-history-limit");
1543 if (hlimit
> oldsize
) {
1546 newsize
= oldsize
+ new;
1549 freecount
= oldsize
+ new - newsize
;
1550 if (freecount
> oldsize
)
1551 freecount
= oldsize
;
1554 for (i
= 0; i
< freecount
; i
++)
1555 free(status_prompt_hlist
[type
][i
]);
1556 movesize
= (oldsize
- freecount
) *
1557 sizeof *status_prompt_hlist
[type
];
1559 memmove(&status_prompt_hlist
[type
][0],
1560 &status_prompt_hlist
[type
][freecount
], movesize
);
1565 free(status_prompt_hlist
[type
]);
1566 status_prompt_hlist
[type
] = NULL
;
1567 } else if (newsize
!= oldsize
) {
1568 status_prompt_hlist
[type
] =
1569 xreallocarray(status_prompt_hlist
[type
], newsize
,
1570 sizeof *status_prompt_hlist
[type
]);
1573 if (new == 1 && newsize
> 0)
1574 status_prompt_hlist
[type
][newsize
- 1] = xstrdup(line
);
1575 status_prompt_hsize
[type
] = newsize
;
1578 /* Build completion list. */
1580 status_prompt_complete_list(u_int
*size
, const char *s
, int at_start
)
1583 const char **layout
, *value
, *cp
;
1584 const struct cmd_entry
**cmdent
;
1585 const struct options_table_entry
*oe
;
1586 size_t slen
= strlen(s
), valuelen
;
1587 struct options_entry
*o
;
1588 struct options_array_item
*a
;
1589 const char *layouts
[] = {
1590 "even-horizontal", "even-vertical", "main-horizontal",
1591 "main-vertical", "tiled", NULL
1595 for (cmdent
= cmd_table
; *cmdent
!= NULL
; cmdent
++) {
1596 if (strncmp((*cmdent
)->name
, s
, slen
) == 0) {
1597 list
= xreallocarray(list
, (*size
) + 1, sizeof *list
);
1598 list
[(*size
)++] = xstrdup((*cmdent
)->name
);
1600 if ((*cmdent
)->alias
!= NULL
&&
1601 strncmp((*cmdent
)->alias
, s
, slen
) == 0) {
1602 list
= xreallocarray(list
, (*size
) + 1, sizeof *list
);
1603 list
[(*size
)++] = xstrdup((*cmdent
)->alias
);
1606 o
= options_get_only(global_options
, "command-alias");
1608 a
= options_array_first(o
);
1610 value
= options_array_item_value(a
)->string
;
1611 if ((cp
= strchr(value
, '=')) == NULL
)
1613 valuelen
= cp
- value
;
1614 if (slen
> valuelen
|| strncmp(value
, s
, slen
) != 0)
1617 list
= xreallocarray(list
, (*size
) + 1, sizeof *list
);
1618 list
[(*size
)++] = xstrndup(value
, valuelen
);
1621 a
= options_array_next(a
);
1627 for (oe
= options_table
; oe
->name
!= NULL
; oe
++) {
1628 if (strncmp(oe
->name
, s
, slen
) == 0) {
1629 list
= xreallocarray(list
, (*size
) + 1, sizeof *list
);
1630 list
[(*size
)++] = xstrdup(oe
->name
);
1633 for (layout
= layouts
; *layout
!= NULL
; layout
++) {
1634 if (strncmp(*layout
, s
, slen
) == 0) {
1635 list
= xreallocarray(list
, (*size
) + 1, sizeof *list
);
1636 list
[(*size
)++] = xstrdup(*layout
);
1642 /* Find longest prefix. */
1644 status_prompt_complete_prefix(char **list
, u_int size
)
1650 if (list
== NULL
|| size
== 0)
1652 out
= xstrdup(list
[0]);
1653 for (i
= 1; i
< size
; i
++) {
1654 j
= strlen(list
[i
]);
1655 if (j
> strlen(out
))
1657 for (; j
> 0; j
--) {
1658 if (out
[j
- 1] != list
[i
][j
- 1])
1665 /* Complete word menu callback. */
1667 status_prompt_menu_callback(__unused
struct menu
*menu
, u_int idx
, key_code key
,
1670 struct status_prompt_menu
*spm
= data
;
1671 struct client
*c
= spm
->c
;
1675 if (key
!= KEYC_NONE
) {
1677 if (spm
->flag
== '\0')
1678 s
= xstrdup(spm
->list
[idx
]);
1680 xasprintf(&s
, "-%c%s", spm
->flag
, spm
->list
[idx
]);
1681 if (c
->prompt_type
== PROMPT_TYPE_WINDOW_TARGET
) {
1682 free(c
->prompt_buffer
);
1683 c
->prompt_buffer
= utf8_fromcstr(s
);
1684 c
->prompt_index
= utf8_strlen(c
->prompt_buffer
);
1685 c
->flags
|= CLIENT_REDRAWSTATUS
;
1686 } else if (status_prompt_replace_complete(c
, s
))
1687 c
->flags
|= CLIENT_REDRAWSTATUS
;
1691 for (i
= 0; i
< spm
->size
; i
++)
1696 /* Show complete word menu. */
1698 status_prompt_complete_list_menu(struct client
*c
, char **list
, u_int size
,
1699 u_int offset
, char flag
)
1702 struct menu_item item
;
1703 struct status_prompt_menu
*spm
;
1704 u_int lines
= status_line_size(c
), height
, i
;
1709 if (c
->tty
.sy
- lines
< 3)
1712 spm
= xmalloc(sizeof *spm
);
1718 height
= c
->tty
.sy
- lines
- 2;
1723 spm
->start
= size
- height
;
1725 menu
= menu_create("");
1726 for (i
= spm
->start
; i
< size
; i
++) {
1727 item
.name
= list
[i
];
1728 item
.key
= '0' + (i
- spm
->start
);
1729 item
.command
= NULL
;
1730 menu_add_item(menu
, &item
, NULL
, NULL
, NULL
);
1733 if (options_get_number(c
->session
->options
, "status-position") == 0)
1736 py
= c
->tty
.sy
- 3 - height
;
1737 offset
+= utf8_cstrwidth(c
->prompt_string
);
1743 if (menu_display(menu
, MENU_NOMOUSE
|MENU_TAB
, NULL
, offset
,
1744 py
, c
, NULL
, status_prompt_menu_callback
, spm
) != 0) {
1752 /* Show complete word menu. */
1754 status_prompt_complete_window_menu(struct client
*c
, struct session
*s
,
1755 const char *word
, u_int offset
, char flag
)
1758 struct menu_item item
;
1759 struct status_prompt_menu
*spm
;
1761 char **list
= NULL
, *tmp
;
1762 u_int lines
= status_line_size(c
), height
;
1765 if (c
->tty
.sy
- lines
< 3)
1768 spm
= xmalloc(sizeof *spm
);
1772 height
= c
->tty
.sy
- lines
- 2;
1777 menu
= menu_create("");
1778 RB_FOREACH(wl
, winlinks
, &s
->windows
) {
1779 if (word
!= NULL
&& *word
!= '\0') {
1780 xasprintf(&tmp
, "%d", wl
->idx
);
1781 if (strncmp(tmp
, word
, strlen(word
)) != 0) {
1788 list
= xreallocarray(list
, size
+ 1, sizeof *list
);
1789 if (c
->prompt_type
== PROMPT_TYPE_WINDOW_TARGET
) {
1790 xasprintf(&tmp
, "%d (%s)", wl
->idx
, wl
->window
->name
);
1791 xasprintf(&list
[size
++], "%d", wl
->idx
);
1793 xasprintf(&tmp
, "%s:%d (%s)", s
->name
, wl
->idx
,
1795 xasprintf(&list
[size
++], "%s:%d", s
->name
, wl
->idx
);
1798 item
.key
= '0' + size
- 1;
1799 item
.command
= NULL
;
1800 menu_add_item(menu
, &item
, NULL
, NULL
, NULL
);
1813 xasprintf(&tmp
, "-%c%s", flag
, list
[0]);
1826 if (options_get_number(c
->session
->options
, "status-position") == 0)
1829 py
= c
->tty
.sy
- 3 - height
;
1830 offset
+= utf8_cstrwidth(c
->prompt_string
);
1836 if (menu_display(menu
, MENU_NOMOUSE
|MENU_TAB
, NULL
, offset
,
1837 py
, c
, NULL
, status_prompt_menu_callback
, spm
) != 0) {
1845 /* Sort complete list. */
1847 status_prompt_complete_sort(const void *a
, const void *b
)
1849 const char **aa
= (const char **)a
, **bb
= (const char **)b
;
1851 return (strcmp(*aa
, *bb
));
1854 /* Complete a session. */
1856 status_prompt_complete_session(char ***list
, u_int
*size
, const char *s
,
1859 struct session
*loop
;
1860 char *out
, *tmp
, n
[11];
1862 RB_FOREACH(loop
, sessions
, &sessions
) {
1863 if (*s
== '\0' || strncmp(loop
->name
, s
, strlen(s
)) == 0) {
1864 *list
= xreallocarray(*list
, (*size
) + 2,
1866 xasprintf(&(*list
)[(*size
)++], "%s:", loop
->name
);
1867 } else if (*s
== '$') {
1868 xsnprintf(n
, sizeof n
, "%u", loop
->id
);
1870 strncmp(n
, s
+ 1, strlen(s
) - 1) == 0) {
1871 *list
= xreallocarray(*list
, (*size
) + 2,
1873 xasprintf(&(*list
)[(*size
)++], "$%s:", n
);
1877 out
= status_prompt_complete_prefix(*list
, *size
);
1878 if (out
!= NULL
&& flag
!= '\0') {
1879 xasprintf(&tmp
, "-%c%s", flag
, out
);
1886 /* Complete word. */
1888 status_prompt_complete(struct client
*c
, const char *word
, u_int offset
)
1890 struct session
*session
;
1891 const char *s
, *colon
;
1892 char **list
= NULL
, *copy
= NULL
, *out
= NULL
;
1896 if (*word
== '\0' &&
1897 c
->prompt_type
!= PROMPT_TYPE_TARGET
&&
1898 c
->prompt_type
!= PROMPT_TYPE_WINDOW_TARGET
)
1901 if (c
->prompt_type
!= PROMPT_TYPE_TARGET
&&
1902 c
->prompt_type
!= PROMPT_TYPE_WINDOW_TARGET
&&
1903 strncmp(word
, "-t", 2) != 0 &&
1904 strncmp(word
, "-s", 2) != 0) {
1905 list
= status_prompt_complete_list(&size
, word
, offset
== 0);
1909 xasprintf(&out
, "%s ", list
[0]);
1911 out
= status_prompt_complete_prefix(list
, size
);
1915 if (c
->prompt_type
== PROMPT_TYPE_TARGET
||
1916 c
->prompt_type
== PROMPT_TYPE_WINDOW_TARGET
) {
1925 /* If this is a window completion, open the window menu. */
1926 if (c
->prompt_type
== PROMPT_TYPE_WINDOW_TARGET
) {
1927 out
= status_prompt_complete_window_menu(c
, c
->session
, s
,
1931 colon
= strchr(s
, ':');
1933 /* If there is no colon, complete as a session. */
1934 if (colon
== NULL
) {
1935 out
= status_prompt_complete_session(&list
, &size
, s
, flag
);
1939 /* If there is a colon but no period, find session and show a menu. */
1940 if (strchr(colon
+ 1, '.') == NULL
) {
1942 session
= c
->session
;
1945 *strchr(copy
, ':') = '\0';
1946 session
= session_find(copy
);
1948 if (session
== NULL
)
1951 out
= status_prompt_complete_window_menu(c
, session
, colon
+ 1,
1959 qsort(list
, size
, sizeof *list
, status_prompt_complete_sort
);
1960 for (i
= 0; i
< size
; i
++)
1961 log_debug("complete %u: %s", i
, list
[i
]);
1964 if (out
!= NULL
&& strcmp(word
, out
) == 0) {
1969 !status_prompt_complete_list_menu(c
, list
, size
, offset
, flag
)) {
1970 for (i
= 0; i
< size
; i
++)
1977 /* Return the type of the prompt as an enum. */
1979 status_prompt_type(const char *type
)
1983 for (i
= 0; i
< PROMPT_NTYPES
; i
++) {
1984 if (strcmp(type
, status_prompt_type_string(i
)) == 0)
1987 return (PROMPT_TYPE_INVALID
);
1990 /* Accessor for prompt_type_strings. */
1992 status_prompt_type_string(u_int type
)
1994 if (type
>= PROMPT_NTYPES
)
1996 return (prompt_type_strings
[type
]);