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
= format_width(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 format_draw(&ctx
, &gc
, start
, c
->prompt_string
, NULL
, 0);
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;
751 c
->prompt_cursor
= start
+ c
->prompt_index
- offset
;
754 for (i
= 0; c
->prompt_buffer
[i
].size
!= 0; i
++) {
755 if (width
< offset
) {
756 width
+= c
->prompt_buffer
[i
].width
;
759 if (width
>= offset
+ pwidth
)
761 width
+= c
->prompt_buffer
[i
].width
;
762 if (width
> offset
+ pwidth
)
765 if (i
!= c
->prompt_index
) {
766 utf8_copy(&gc
.data
, &c
->prompt_buffer
[i
]);
767 screen_write_cell(&ctx
, &gc
);
769 utf8_copy(&cursorgc
.data
, &c
->prompt_buffer
[i
]);
770 screen_write_cell(&ctx
, &cursorgc
);
773 if (sl
->active
->cx
< screen_size_x(sl
->active
) && c
->prompt_index
>= i
)
774 screen_write_putc(&ctx
, &cursorgc
, ' ');
777 screen_write_stop(&ctx
);
779 if (grid_compare(sl
->active
->grid
, old_screen
.grid
) == 0) {
780 screen_free(&old_screen
);
783 screen_free(&old_screen
);
787 /* Is this a separator? */
789 status_prompt_in_list(const char *ws
, const struct utf8_data
*ud
)
791 if (ud
->size
!= 1 || ud
->width
!= 1)
793 return (strchr(ws
, *ud
->data
) != NULL
);
796 /* Is this a space? */
798 status_prompt_space(const struct utf8_data
*ud
)
800 if (ud
->size
!= 1 || ud
->width
!= 1)
802 return (*ud
->data
== ' ');
806 * Translate key from vi to emacs. Return 0 to drop key, 1 to process the key
807 * as an emacs key; return 2 to append to the buffer.
810 status_prompt_translate_key(struct client
*c
, key_code key
, key_code
*new_key
)
812 if (c
->prompt_mode
== PROMPT_ENTRY
) {
814 case '\001': /* C-a */
815 case '\003': /* C-c */
816 case '\005': /* C-e */
817 case '\007': /* C-g */
818 case '\010': /* C-h */
819 case '\011': /* Tab */
820 case '\013': /* C-k */
821 case '\016': /* C-n */
822 case '\020': /* C-p */
823 case '\024': /* C-t */
824 case '\025': /* C-u */
825 case '\027': /* C-w */
826 case '\031': /* C-y */
829 case KEYC_LEFT
|KEYC_CTRL
:
830 case KEYC_RIGHT
|KEYC_CTRL
:
841 case '\033': /* Escape */
842 c
->prompt_mode
= PROMPT_COMMAND
;
843 c
->flags
|= CLIENT_REDRAWSTATUS
;
852 *new_key
= KEYC_LEFT
;
859 c
->prompt_mode
= PROMPT_ENTRY
;
860 c
->flags
|= CLIENT_REDRAWSTATUS
;
861 break; /* switch mode and... */
863 c
->prompt_mode
= PROMPT_ENTRY
;
864 c
->flags
|= CLIENT_REDRAWSTATUS
;
865 *new_key
= '\025'; /* C-u */
868 case '\033': /* Escape */
869 c
->prompt_mode
= PROMPT_ENTRY
;
870 c
->flags
|= CLIENT_REDRAWSTATUS
;
882 *new_key
= KEYC_HOME
;
886 *new_key
= '\013'; /* C-k */
890 *new_key
= KEYC_BSPACE
;
893 *new_key
= 'b'|KEYC_META
;
896 *new_key
= 'B'|KEYC_VI
;
899 *new_key
= '\025'; /* C-u */
902 *new_key
= 'e'|KEYC_VI
;
905 *new_key
= 'E'|KEYC_VI
;
908 *new_key
= 'w'|KEYC_VI
;
911 *new_key
= 'W'|KEYC_VI
;
914 *new_key
= '\031'; /* C-y */
917 *new_key
= '\003'; /* C-c */
926 *new_key
= KEYC_DOWN
;
930 *new_key
= KEYC_LEFT
;
935 *new_key
= KEYC_RIGHT
;
941 case '\010' /* C-h */:
942 case '\003' /* C-c */:
950 /* Paste into prompt. */
952 status_prompt_paste(struct client
*c
)
954 struct paste_buffer
*pb
;
956 size_t size
, n
, bufsize
;
958 struct utf8_data
*ud
, *udp
;
959 enum utf8_state more
;
961 size
= utf8_strlen(c
->prompt_buffer
);
962 if (c
->prompt_saved
!= NULL
) {
963 ud
= c
->prompt_saved
;
964 n
= utf8_strlen(c
->prompt_saved
);
966 if ((pb
= paste_get_top(NULL
)) == NULL
)
968 bufdata
= paste_buffer_data(pb
, &bufsize
);
969 ud
= xreallocarray(NULL
, bufsize
+ 1, sizeof *ud
);
971 for (i
= 0; i
!= bufsize
; /* nothing */) {
972 more
= utf8_open(udp
, bufdata
[i
]);
973 if (more
== UTF8_MORE
) {
974 while (++i
!= bufsize
&& more
== UTF8_MORE
)
975 more
= utf8_append(udp
, bufdata
[i
]);
976 if (more
== UTF8_DONE
) {
982 if (bufdata
[i
] <= 31 || bufdata
[i
] >= 127)
984 utf8_set(udp
, bufdata
[i
]);
994 c
->prompt_buffer
= xreallocarray(c
->prompt_buffer
, size
+ n
+ 1,
995 sizeof *c
->prompt_buffer
);
996 if (c
->prompt_index
== size
) {
997 memcpy(c
->prompt_buffer
+ c
->prompt_index
, ud
,
998 n
* sizeof *c
->prompt_buffer
);
999 c
->prompt_index
+= n
;
1000 c
->prompt_buffer
[c
->prompt_index
].size
= 0;
1002 memmove(c
->prompt_buffer
+ c
->prompt_index
+ n
,
1003 c
->prompt_buffer
+ c
->prompt_index
,
1004 (size
+ 1 - c
->prompt_index
) * sizeof *c
->prompt_buffer
);
1005 memcpy(c
->prompt_buffer
+ c
->prompt_index
, ud
,
1006 n
* sizeof *c
->prompt_buffer
);
1007 c
->prompt_index
+= n
;
1010 if (ud
!= c
->prompt_saved
)
1015 /* Finish completion. */
1017 status_prompt_replace_complete(struct client
*c
, const char *s
)
1019 char word
[64], *allocated
= NULL
;
1020 size_t size
, n
, off
, idx
, used
;
1021 struct utf8_data
*first
, *last
, *ud
;
1023 /* Work out where the cursor currently is. */
1024 idx
= c
->prompt_index
;
1027 size
= utf8_strlen(c
->prompt_buffer
);
1029 /* Find the word we are in. */
1030 first
= &c
->prompt_buffer
[idx
];
1031 while (first
> c
->prompt_buffer
&& !status_prompt_space(first
))
1033 while (first
->size
!= 0 && status_prompt_space(first
))
1035 last
= &c
->prompt_buffer
[idx
];
1036 while (last
->size
!= 0 && !status_prompt_space(last
))
1038 while (last
> c
->prompt_buffer
&& status_prompt_space(last
))
1040 if (last
->size
!= 0)
1046 for (ud
= first
; ud
< last
; ud
++) {
1047 if (used
+ ud
->size
>= sizeof word
)
1049 memcpy(word
+ used
, ud
->data
, ud
->size
);
1057 /* Try to complete it. */
1059 allocated
= status_prompt_complete(c
, word
,
1060 first
- c
->prompt_buffer
);
1061 if (allocated
== NULL
)
1066 /* Trim out word. */
1067 n
= size
- (last
- c
->prompt_buffer
) + 1; /* with \0 */
1068 memmove(first
, last
, n
* sizeof *c
->prompt_buffer
);
1069 size
-= last
- first
;
1071 /* Insert the new word. */
1073 off
= first
- c
->prompt_buffer
;
1074 c
->prompt_buffer
= xreallocarray(c
->prompt_buffer
, size
+ 1,
1075 sizeof *c
->prompt_buffer
);
1076 first
= c
->prompt_buffer
+ off
;
1077 memmove(first
+ strlen(s
), first
, n
* sizeof *c
->prompt_buffer
);
1078 for (idx
= 0; idx
< strlen(s
); idx
++)
1079 utf8_set(&first
[idx
], s
[idx
]);
1080 c
->prompt_index
= (first
- c
->prompt_buffer
) + strlen(s
);
1086 /* Prompt forward to the next beginning of a word. */
1088 status_prompt_forward_word(struct client
*c
, size_t size
, int vi
,
1089 const char *separators
)
1091 size_t idx
= c
->prompt_index
;
1092 int word_is_separators
;
1094 /* In emacs mode, skip until the first non-whitespace character. */
1096 while (idx
!= size
&&
1097 status_prompt_space(&c
->prompt_buffer
[idx
]))
1100 /* Can't move forward if we're already at the end. */
1102 c
->prompt_index
= idx
;
1106 /* Determine the current character class (separators or not). */
1107 word_is_separators
= status_prompt_in_list(separators
,
1108 &c
->prompt_buffer
[idx
]) &&
1109 !status_prompt_space(&c
->prompt_buffer
[idx
]);
1111 /* Skip ahead until the first space or opposite character class. */
1114 if (status_prompt_space(&c
->prompt_buffer
[idx
])) {
1115 /* In vi mode, go to the start of the next word. */
1117 while (idx
!= size
&&
1118 status_prompt_space(&c
->prompt_buffer
[idx
]))
1122 } while (idx
!= size
&& word_is_separators
== status_prompt_in_list(
1123 separators
, &c
->prompt_buffer
[idx
]));
1125 c
->prompt_index
= idx
;
1128 /* Prompt forward to the next end of a word. */
1130 status_prompt_end_word(struct client
*c
, size_t size
, const char *separators
)
1132 size_t idx
= c
->prompt_index
;
1133 int word_is_separators
;
1135 /* Can't move forward if we're already at the end. */
1139 /* Find the next word. */
1143 c
->prompt_index
= idx
;
1146 } while (status_prompt_space(&c
->prompt_buffer
[idx
]));
1148 /* Determine the character class (separators or not). */
1149 word_is_separators
= status_prompt_in_list(separators
,
1150 &c
->prompt_buffer
[idx
]);
1152 /* Skip ahead until the next space or opposite character class. */
1157 } while (!status_prompt_space(&c
->prompt_buffer
[idx
]) &&
1158 word_is_separators
== status_prompt_in_list(separators
,
1159 &c
->prompt_buffer
[idx
]));
1161 /* Back up to the previous character to stop at the end of the word. */
1162 c
->prompt_index
= idx
- 1;
1165 /* Prompt backward to the previous beginning of a word. */
1167 status_prompt_backward_word(struct client
*c
, const char *separators
)
1169 size_t idx
= c
->prompt_index
;
1170 int word_is_separators
;
1172 /* Find non-whitespace. */
1175 if (!status_prompt_space(&c
->prompt_buffer
[idx
]))
1178 word_is_separators
= status_prompt_in_list(separators
,
1179 &c
->prompt_buffer
[idx
]);
1181 /* Find the character before the beginning of the word. */
1184 if (status_prompt_space(&c
->prompt_buffer
[idx
]) ||
1185 word_is_separators
!= status_prompt_in_list(separators
,
1186 &c
->prompt_buffer
[idx
])) {
1187 /* Go back to the word. */
1192 c
->prompt_index
= idx
;
1195 /* Handle keys in prompt. */
1197 status_prompt_key(struct client
*c
, key_code key
)
1199 struct options
*oo
= c
->session
->options
;
1200 char *s
, *cp
, prefix
= '=';
1201 const char *histstr
, *separators
= NULL
, *keystring
;
1203 struct utf8_data tmp
;
1204 int keys
, word_is_separators
;
1206 if (c
->prompt_flags
& PROMPT_KEY
) {
1207 keystring
= key_string_lookup_key(key
, 0);
1208 c
->prompt_inputcb(c
, c
->prompt_data
, keystring
, 1);
1209 status_prompt_clear(c
);
1212 size
= utf8_strlen(c
->prompt_buffer
);
1214 if (c
->prompt_flags
& PROMPT_NUMERIC
) {
1215 if (key
>= '0' && key
<= '9')
1217 s
= utf8_tocstr(c
->prompt_buffer
);
1218 c
->prompt_inputcb(c
, c
->prompt_data
, s
, 1);
1219 status_prompt_clear(c
);
1223 key
&= ~KEYC_MASK_FLAGS
;
1225 keys
= options_get_number(c
->session
->options
, "status-keys");
1226 if (keys
== MODEKEY_VI
) {
1227 switch (status_prompt_translate_key(c
, key
, &key
)) {
1240 case '\002': /* C-b */
1241 if (c
->prompt_index
> 0) {
1247 case '\006': /* C-f */
1248 if (c
->prompt_index
< size
) {
1254 case '\001': /* C-a */
1255 if (c
->prompt_index
!= 0) {
1256 c
->prompt_index
= 0;
1261 case '\005': /* C-e */
1262 if (c
->prompt_index
!= size
) {
1263 c
->prompt_index
= size
;
1267 case '\011': /* Tab */
1268 if (status_prompt_replace_complete(c
, NULL
))
1272 case '\010': /* C-h */
1273 if (c
->prompt_index
!= 0) {
1274 if (c
->prompt_index
== size
)
1275 c
->prompt_buffer
[--c
->prompt_index
].size
= 0;
1277 memmove(c
->prompt_buffer
+ c
->prompt_index
- 1,
1278 c
->prompt_buffer
+ c
->prompt_index
,
1279 (size
+ 1 - c
->prompt_index
) *
1280 sizeof *c
->prompt_buffer
);
1287 case '\004': /* C-d */
1288 if (c
->prompt_index
!= size
) {
1289 memmove(c
->prompt_buffer
+ c
->prompt_index
,
1290 c
->prompt_buffer
+ c
->prompt_index
+ 1,
1291 (size
+ 1 - c
->prompt_index
) *
1292 sizeof *c
->prompt_buffer
);
1296 case '\025': /* C-u */
1297 c
->prompt_buffer
[0].size
= 0;
1298 c
->prompt_index
= 0;
1300 case '\013': /* C-k */
1301 if (c
->prompt_index
< size
) {
1302 c
->prompt_buffer
[c
->prompt_index
].size
= 0;
1306 case '\027': /* C-w */
1307 separators
= options_get_string(oo
, "word-separators");
1308 idx
= c
->prompt_index
;
1310 /* Find non-whitespace. */
1313 if (!status_prompt_space(&c
->prompt_buffer
[idx
]))
1316 word_is_separators
= status_prompt_in_list(separators
,
1317 &c
->prompt_buffer
[idx
]);
1319 /* Find the character before the beginning of the word. */
1322 if (status_prompt_space(&c
->prompt_buffer
[idx
]) ||
1323 word_is_separators
!= status_prompt_in_list(
1324 separators
, &c
->prompt_buffer
[idx
])) {
1325 /* Go back to the word. */
1331 free(c
->prompt_saved
);
1332 c
->prompt_saved
= xcalloc(sizeof *c
->prompt_buffer
,
1333 (c
->prompt_index
- idx
) + 1);
1334 memcpy(c
->prompt_saved
, c
->prompt_buffer
+ idx
,
1335 (c
->prompt_index
- idx
) * sizeof *c
->prompt_buffer
);
1337 memmove(c
->prompt_buffer
+ idx
,
1338 c
->prompt_buffer
+ c
->prompt_index
,
1339 (size
+ 1 - c
->prompt_index
) *
1340 sizeof *c
->prompt_buffer
);
1341 memset(c
->prompt_buffer
+ size
- (c
->prompt_index
- idx
),
1342 '\0', (c
->prompt_index
- idx
) * sizeof *c
->prompt_buffer
);
1343 c
->prompt_index
= idx
;
1346 case KEYC_RIGHT
|KEYC_CTRL
:
1348 separators
= options_get_string(oo
, "word-separators");
1349 status_prompt_forward_word(c
, size
, 0, separators
);
1352 status_prompt_end_word(c
, size
, "");
1355 separators
= options_get_string(oo
, "word-separators");
1356 status_prompt_end_word(c
, size
, separators
);
1359 status_prompt_forward_word(c
, size
, 1, "");
1362 separators
= options_get_string(oo
, "word-separators");
1363 status_prompt_forward_word(c
, size
, 1, separators
);
1366 status_prompt_backward_word(c
, "");
1368 case KEYC_LEFT
|KEYC_CTRL
:
1370 separators
= options_get_string(oo
, "word-separators");
1371 status_prompt_backward_word(c
, separators
);
1374 case '\020': /* C-p */
1375 histstr
= status_prompt_up_history(c
->prompt_hindex
,
1377 if (histstr
== NULL
)
1379 free(c
->prompt_buffer
);
1380 c
->prompt_buffer
= utf8_fromcstr(histstr
);
1381 c
->prompt_index
= utf8_strlen(c
->prompt_buffer
);
1384 case '\016': /* C-n */
1385 histstr
= status_prompt_down_history(c
->prompt_hindex
,
1387 if (histstr
== NULL
)
1389 free(c
->prompt_buffer
);
1390 c
->prompt_buffer
= utf8_fromcstr(histstr
);
1391 c
->prompt_index
= utf8_strlen(c
->prompt_buffer
);
1393 case '\031': /* C-y */
1394 if (status_prompt_paste(c
))
1397 case '\024': /* C-t */
1398 idx
= c
->prompt_index
;
1402 utf8_copy(&tmp
, &c
->prompt_buffer
[idx
- 2]);
1403 utf8_copy(&c
->prompt_buffer
[idx
- 2],
1404 &c
->prompt_buffer
[idx
- 1]);
1405 utf8_copy(&c
->prompt_buffer
[idx
- 1], &tmp
);
1406 c
->prompt_index
= idx
;
1412 s
= utf8_tocstr(c
->prompt_buffer
);
1414 status_prompt_add_history(s
, c
->prompt_type
);
1415 if (c
->prompt_inputcb(c
, c
->prompt_data
, s
, 1) == 0)
1416 status_prompt_clear(c
);
1419 case '\033': /* Escape */
1420 case '\003': /* C-c */
1421 case '\007': /* C-g */
1422 if (c
->prompt_inputcb(c
, c
->prompt_data
, NULL
, 1) == 0)
1423 status_prompt_clear(c
);
1425 case '\022': /* C-r */
1426 if (~c
->prompt_flags
& PROMPT_INCREMENTAL
)
1428 if (c
->prompt_buffer
[0].size
== 0) {
1430 free(c
->prompt_buffer
);
1431 c
->prompt_buffer
= utf8_fromcstr(c
->prompt_last
);
1432 c
->prompt_index
= utf8_strlen(c
->prompt_buffer
);
1436 case '\023': /* C-s */
1437 if (~c
->prompt_flags
& PROMPT_INCREMENTAL
)
1439 if (c
->prompt_buffer
[0].size
== 0) {
1441 free(c
->prompt_buffer
);
1442 c
->prompt_buffer
= utf8_fromcstr(c
->prompt_last
);
1443 c
->prompt_index
= utf8_strlen(c
->prompt_buffer
);
1451 c
->flags
|= CLIENT_REDRAWSTATUS
;
1455 if (key
<= 0x1f || (key
>= KEYC_BASE
&& key
< KEYC_BASE_END
))
1458 utf8_set(&tmp
, key
);
1459 else if (KEYC_IS_UNICODE(key
))
1460 utf8_to_data(key
, &tmp
);
1464 c
->prompt_buffer
= xreallocarray(c
->prompt_buffer
, size
+ 2,
1465 sizeof *c
->prompt_buffer
);
1467 if (c
->prompt_index
== size
) {
1468 utf8_copy(&c
->prompt_buffer
[c
->prompt_index
], &tmp
);
1470 c
->prompt_buffer
[c
->prompt_index
].size
= 0;
1472 memmove(c
->prompt_buffer
+ c
->prompt_index
+ 1,
1473 c
->prompt_buffer
+ c
->prompt_index
,
1474 (size
+ 1 - c
->prompt_index
) *
1475 sizeof *c
->prompt_buffer
);
1476 utf8_copy(&c
->prompt_buffer
[c
->prompt_index
], &tmp
);
1480 if (c
->prompt_flags
& PROMPT_SINGLE
) {
1481 if (utf8_strlen(c
->prompt_buffer
) != 1)
1482 status_prompt_clear(c
);
1484 s
= utf8_tocstr(c
->prompt_buffer
);
1485 if (c
->prompt_inputcb(c
, c
->prompt_data
, s
, 1) == 0)
1486 status_prompt_clear(c
);
1492 c
->flags
|= CLIENT_REDRAWSTATUS
;
1493 if (c
->prompt_flags
& PROMPT_INCREMENTAL
) {
1494 s
= utf8_tocstr(c
->prompt_buffer
);
1495 xasprintf(&cp
, "%c%s", prefix
, s
);
1496 c
->prompt_inputcb(c
, c
->prompt_data
, cp
, 0);
1503 /* Get previous line from the history. */
1505 status_prompt_up_history(u_int
*idx
, u_int type
)
1508 * History runs from 0 to size - 1. Index is from 0 to size. Zero is
1512 if (status_prompt_hsize
[type
] == 0 ||
1513 idx
[type
] == status_prompt_hsize
[type
])
1516 return (status_prompt_hlist
[type
][status_prompt_hsize
[type
] - idx
[type
]]);
1519 /* Get next line from the history. */
1521 status_prompt_down_history(u_int
*idx
, u_int type
)
1523 if (status_prompt_hsize
[type
] == 0 || idx
[type
] == 0)
1528 return (status_prompt_hlist
[type
][status_prompt_hsize
[type
] - idx
[type
]]);
1531 /* Add line to the history. */
1533 status_prompt_add_history(const char *line
, u_int type
)
1535 u_int i
, oldsize
, newsize
, freecount
, hlimit
, new = 1;
1538 oldsize
= status_prompt_hsize
[type
];
1540 strcmp(status_prompt_hlist
[type
][oldsize
- 1], line
) == 0)
1543 hlimit
= options_get_number(global_options
, "prompt-history-limit");
1544 if (hlimit
> oldsize
) {
1547 newsize
= oldsize
+ new;
1550 freecount
= oldsize
+ new - newsize
;
1551 if (freecount
> oldsize
)
1552 freecount
= oldsize
;
1555 for (i
= 0; i
< freecount
; i
++)
1556 free(status_prompt_hlist
[type
][i
]);
1557 movesize
= (oldsize
- freecount
) *
1558 sizeof *status_prompt_hlist
[type
];
1560 memmove(&status_prompt_hlist
[type
][0],
1561 &status_prompt_hlist
[type
][freecount
], movesize
);
1566 free(status_prompt_hlist
[type
]);
1567 status_prompt_hlist
[type
] = NULL
;
1568 } else if (newsize
!= oldsize
) {
1569 status_prompt_hlist
[type
] =
1570 xreallocarray(status_prompt_hlist
[type
], newsize
,
1571 sizeof *status_prompt_hlist
[type
]);
1574 if (new == 1 && newsize
> 0)
1575 status_prompt_hlist
[type
][newsize
- 1] = xstrdup(line
);
1576 status_prompt_hsize
[type
] = newsize
;
1579 /* Add to completion list. */
1581 status_prompt_add_list(char ***list
, u_int
*size
, const char *s
)
1585 for (i
= 0; i
< *size
; i
++) {
1586 if (strcmp((*list
)[i
], s
) == 0)
1589 *list
= xreallocarray(*list
, (*size
) + 1, sizeof **list
);
1590 (*list
)[(*size
)++] = xstrdup(s
);
1593 /* Build completion list. */
1595 status_prompt_complete_list(u_int
*size
, const char *s
, int at_start
)
1597 char **list
= NULL
, *tmp
;
1598 const char **layout
, *value
, *cp
;
1599 const struct cmd_entry
**cmdent
;
1600 const struct options_table_entry
*oe
;
1601 size_t slen
= strlen(s
), valuelen
;
1602 struct options_entry
*o
;
1603 struct options_array_item
*a
;
1604 const char *layouts
[] = {
1605 "even-horizontal", "even-vertical", "main-horizontal",
1606 "main-vertical", "tiled", NULL
1610 for (cmdent
= cmd_table
; *cmdent
!= NULL
; cmdent
++) {
1611 if (strncmp((*cmdent
)->name
, s
, slen
) == 0)
1612 status_prompt_add_list(&list
, size
, (*cmdent
)->name
);
1613 if ((*cmdent
)->alias
!= NULL
&&
1614 strncmp((*cmdent
)->alias
, s
, slen
) == 0)
1615 status_prompt_add_list(&list
, size
, (*cmdent
)->alias
);
1617 o
= options_get_only(global_options
, "command-alias");
1619 a
= options_array_first(o
);
1621 value
= options_array_item_value(a
)->string
;
1622 if ((cp
= strchr(value
, '=')) == NULL
)
1624 valuelen
= cp
- value
;
1625 if (slen
> valuelen
|| strncmp(value
, s
, slen
) != 0)
1628 xasprintf(&tmp
, "%.*s", (int)valuelen
, value
);
1629 status_prompt_add_list(&list
, size
, tmp
);
1633 a
= options_array_next(a
);
1638 for (oe
= options_table
; oe
->name
!= NULL
; oe
++) {
1639 if (strncmp(oe
->name
, s
, slen
) == 0)
1640 status_prompt_add_list(&list
, size
, oe
->name
);
1642 for (layout
= layouts
; *layout
!= NULL
; layout
++) {
1643 if (strncmp(*layout
, s
, slen
) == 0)
1644 status_prompt_add_list(&list
, size
, *layout
);
1649 /* Find longest prefix. */
1651 status_prompt_complete_prefix(char **list
, u_int size
)
1657 if (list
== NULL
|| size
== 0)
1659 out
= xstrdup(list
[0]);
1660 for (i
= 1; i
< size
; i
++) {
1661 j
= strlen(list
[i
]);
1662 if (j
> strlen(out
))
1664 for (; j
> 0; j
--) {
1665 if (out
[j
- 1] != list
[i
][j
- 1])
1672 /* Complete word menu callback. */
1674 status_prompt_menu_callback(__unused
struct menu
*menu
, u_int idx
, key_code key
,
1677 struct status_prompt_menu
*spm
= data
;
1678 struct client
*c
= spm
->c
;
1682 if (key
!= KEYC_NONE
) {
1684 if (spm
->flag
== '\0')
1685 s
= xstrdup(spm
->list
[idx
]);
1687 xasprintf(&s
, "-%c%s", spm
->flag
, spm
->list
[idx
]);
1688 if (c
->prompt_type
== PROMPT_TYPE_WINDOW_TARGET
) {
1689 free(c
->prompt_buffer
);
1690 c
->prompt_buffer
= utf8_fromcstr(s
);
1691 c
->prompt_index
= utf8_strlen(c
->prompt_buffer
);
1692 c
->flags
|= CLIENT_REDRAWSTATUS
;
1693 } else if (status_prompt_replace_complete(c
, s
))
1694 c
->flags
|= CLIENT_REDRAWSTATUS
;
1698 for (i
= 0; i
< spm
->size
; i
++)
1703 /* Show complete word menu. */
1705 status_prompt_complete_list_menu(struct client
*c
, char **list
, u_int size
,
1706 u_int offset
, char flag
)
1709 struct menu_item item
;
1710 struct status_prompt_menu
*spm
;
1711 u_int lines
= status_line_size(c
), height
, i
;
1716 if (c
->tty
.sy
- lines
< 3)
1719 spm
= xmalloc(sizeof *spm
);
1725 height
= c
->tty
.sy
- lines
- 2;
1730 spm
->start
= size
- height
;
1732 menu
= menu_create("");
1733 for (i
= spm
->start
; i
< size
; i
++) {
1734 item
.name
= list
[i
];
1735 item
.key
= '0' + (i
- spm
->start
);
1736 item
.command
= NULL
;
1737 menu_add_item(menu
, &item
, NULL
, c
, NULL
);
1740 if (options_get_number(c
->session
->options
, "status-position") == 0)
1743 py
= c
->tty
.sy
- 3 - height
;
1744 offset
+= utf8_cstrwidth(c
->prompt_string
);
1750 if (menu_display(menu
, MENU_NOMOUSE
|MENU_TAB
, NULL
, offset
,
1751 py
, c
, NULL
, status_prompt_menu_callback
, spm
) != 0) {
1759 /* Show complete word menu. */
1761 status_prompt_complete_window_menu(struct client
*c
, struct session
*s
,
1762 const char *word
, u_int offset
, char flag
)
1765 struct menu_item item
;
1766 struct status_prompt_menu
*spm
;
1768 char **list
= NULL
, *tmp
;
1769 u_int lines
= status_line_size(c
), height
;
1772 if (c
->tty
.sy
- lines
< 3)
1775 spm
= xmalloc(sizeof *spm
);
1779 height
= c
->tty
.sy
- lines
- 2;
1784 menu
= menu_create("");
1785 RB_FOREACH(wl
, winlinks
, &s
->windows
) {
1786 if (word
!= NULL
&& *word
!= '\0') {
1787 xasprintf(&tmp
, "%d", wl
->idx
);
1788 if (strncmp(tmp
, word
, strlen(word
)) != 0) {
1795 list
= xreallocarray(list
, size
+ 1, sizeof *list
);
1796 if (c
->prompt_type
== PROMPT_TYPE_WINDOW_TARGET
) {
1797 xasprintf(&tmp
, "%d (%s)", wl
->idx
, wl
->window
->name
);
1798 xasprintf(&list
[size
++], "%d", wl
->idx
);
1800 xasprintf(&tmp
, "%s:%d (%s)", s
->name
, wl
->idx
,
1802 xasprintf(&list
[size
++], "%s:%d", s
->name
, wl
->idx
);
1805 item
.key
= '0' + size
- 1;
1806 item
.command
= NULL
;
1807 menu_add_item(menu
, &item
, NULL
, c
, NULL
);
1820 xasprintf(&tmp
, "-%c%s", flag
, list
[0]);
1833 if (options_get_number(c
->session
->options
, "status-position") == 0)
1836 py
= c
->tty
.sy
- 3 - height
;
1837 offset
+= utf8_cstrwidth(c
->prompt_string
);
1843 if (menu_display(menu
, MENU_NOMOUSE
|MENU_TAB
, NULL
, offset
,
1844 py
, c
, NULL
, status_prompt_menu_callback
, spm
) != 0) {
1852 /* Sort complete list. */
1854 status_prompt_complete_sort(const void *a
, const void *b
)
1856 const char **aa
= (const char **)a
, **bb
= (const char **)b
;
1858 return (strcmp(*aa
, *bb
));
1861 /* Complete a session. */
1863 status_prompt_complete_session(char ***list
, u_int
*size
, const char *s
,
1866 struct session
*loop
;
1867 char *out
, *tmp
, n
[11];
1869 RB_FOREACH(loop
, sessions
, &sessions
) {
1870 if (*s
== '\0' || strncmp(loop
->name
, s
, strlen(s
)) == 0) {
1871 *list
= xreallocarray(*list
, (*size
) + 2,
1873 xasprintf(&(*list
)[(*size
)++], "%s:", loop
->name
);
1874 } else if (*s
== '$') {
1875 xsnprintf(n
, sizeof n
, "%u", loop
->id
);
1877 strncmp(n
, s
+ 1, strlen(s
) - 1) == 0) {
1878 *list
= xreallocarray(*list
, (*size
) + 2,
1880 xasprintf(&(*list
)[(*size
)++], "$%s:", n
);
1884 out
= status_prompt_complete_prefix(*list
, *size
);
1885 if (out
!= NULL
&& flag
!= '\0') {
1886 xasprintf(&tmp
, "-%c%s", flag
, out
);
1893 /* Complete word. */
1895 status_prompt_complete(struct client
*c
, const char *word
, u_int offset
)
1897 struct session
*session
;
1898 const char *s
, *colon
;
1899 char **list
= NULL
, *copy
= NULL
, *out
= NULL
;
1903 if (*word
== '\0' &&
1904 c
->prompt_type
!= PROMPT_TYPE_TARGET
&&
1905 c
->prompt_type
!= PROMPT_TYPE_WINDOW_TARGET
)
1908 if (c
->prompt_type
!= PROMPT_TYPE_TARGET
&&
1909 c
->prompt_type
!= PROMPT_TYPE_WINDOW_TARGET
&&
1910 strncmp(word
, "-t", 2) != 0 &&
1911 strncmp(word
, "-s", 2) != 0) {
1912 list
= status_prompt_complete_list(&size
, word
, offset
== 0);
1916 xasprintf(&out
, "%s ", list
[0]);
1918 out
= status_prompt_complete_prefix(list
, size
);
1922 if (c
->prompt_type
== PROMPT_TYPE_TARGET
||
1923 c
->prompt_type
== PROMPT_TYPE_WINDOW_TARGET
) {
1932 /* If this is a window completion, open the window menu. */
1933 if (c
->prompt_type
== PROMPT_TYPE_WINDOW_TARGET
) {
1934 out
= status_prompt_complete_window_menu(c
, c
->session
, s
,
1938 colon
= strchr(s
, ':');
1940 /* If there is no colon, complete as a session. */
1941 if (colon
== NULL
) {
1942 out
= status_prompt_complete_session(&list
, &size
, s
, flag
);
1946 /* If there is a colon but no period, find session and show a menu. */
1947 if (strchr(colon
+ 1, '.') == NULL
) {
1949 session
= c
->session
;
1952 *strchr(copy
, ':') = '\0';
1953 session
= session_find(copy
);
1955 if (session
== NULL
)
1958 out
= status_prompt_complete_window_menu(c
, session
, colon
+ 1,
1966 qsort(list
, size
, sizeof *list
, status_prompt_complete_sort
);
1967 for (i
= 0; i
< size
; i
++)
1968 log_debug("complete %u: %s", i
, list
[i
]);
1971 if (out
!= NULL
&& strcmp(word
, out
) == 0) {
1976 !status_prompt_complete_list_menu(c
, list
, size
, offset
, flag
)) {
1977 for (i
= 0; i
< size
; i
++)
1984 /* Return the type of the prompt as an enum. */
1986 status_prompt_type(const char *type
)
1990 for (i
= 0; i
< PROMPT_NTYPES
; i
++) {
1991 if (strcmp(type
, status_prompt_type_string(i
)) == 0)
1994 return (PROMPT_TYPE_INVALID
);
1997 /* Accessor for prompt_type_strings. */
1999 status_prompt_type_string(u_int type
)
2001 if (type
>= PROMPT_NTYPES
)
2003 return (prompt_type_strings
[type
]);