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
, ...)
478 status_message_clear(c
);
479 status_push_screen(c
);
482 xvasprintf(&c
->message_string
, fmt
, ap
);
485 server_add_message("%s message: %s", c
->name
, c
->message_string
);
488 * With delay -1, the display-time option is used; zero means wait for
489 * key press; more than zero is the actual delay time in milliseconds.
492 delay
= options_get_number(c
->session
->options
, "display-time");
494 tv
.tv_sec
= delay
/ 1000;
495 tv
.tv_usec
= (delay
% 1000) * 1000L;
497 if (event_initialized(&c
->message_timer
))
498 evtimer_del(&c
->message_timer
);
499 evtimer_set(&c
->message_timer
, status_message_callback
, c
);
501 evtimer_add(&c
->message_timer
, &tv
);
505 c
->message_ignore_keys
= ignore_keys
;
506 c
->message_ignore_styles
= ignore_styles
;
508 c
->tty
.flags
|= (TTY_NOCURSOR
|TTY_FREEZE
);
509 c
->flags
|= CLIENT_REDRAWSTATUS
;
512 /* Clear status line message. */
514 status_message_clear(struct client
*c
)
516 if (c
->message_string
== NULL
)
519 free(c
->message_string
);
520 c
->message_string
= NULL
;
522 if (c
->prompt_string
== NULL
)
523 c
->tty
.flags
&= ~(TTY_NOCURSOR
|TTY_FREEZE
);
524 c
->flags
|= CLIENT_ALLREDRAWFLAGS
; /* was frozen and may have changed */
526 status_pop_screen(c
);
529 /* Clear status line message after timer expires. */
531 status_message_callback(__unused
int fd
, __unused
short event
, void *data
)
533 struct client
*c
= data
;
535 status_message_clear(c
);
538 /* Draw client message on status line of present else on last line. */
540 status_message_redraw(struct client
*c
)
542 struct status_line
*sl
= &c
->status
;
543 struct screen_write_ctx ctx
;
544 struct session
*s
= c
->session
;
545 struct screen old_screen
;
547 u_int lines
, offset
, messageline
;
549 struct format_tree
*ft
;
551 if (c
->tty
.sx
== 0 || c
->tty
.sy
== 0)
553 memcpy(&old_screen
, sl
->active
, sizeof old_screen
);
555 lines
= status_line_size(c
);
558 screen_init(sl
->active
, c
->tty
.sx
, lines
, 0);
560 messageline
= status_prompt_line_at(c
);
561 if (messageline
> lines
- 1)
562 messageline
= lines
- 1;
564 len
= screen_write_strlen("%s", c
->message_string
);
568 ft
= format_create_defaults(NULL
, c
, NULL
, NULL
, NULL
);
569 style_apply(&gc
, s
->options
, "message-style", ft
);
572 screen_write_start(&ctx
, sl
->active
);
573 screen_write_fast_copy(&ctx
, &sl
->screen
, 0, 0, c
->tty
.sx
, lines
);
574 screen_write_cursormove(&ctx
, 0, messageline
, 0);
575 for (offset
= 0; offset
< c
->tty
.sx
; offset
++)
576 screen_write_putc(&ctx
, &gc
, ' ');
577 screen_write_cursormove(&ctx
, 0, messageline
, 0);
578 if (c
->message_ignore_styles
)
579 screen_write_nputs(&ctx
, len
, &gc
, "%s", c
->message_string
);
581 format_draw(&ctx
, &gc
, c
->tty
.sx
, c
->message_string
, NULL
, 0);
582 screen_write_stop(&ctx
);
584 if (grid_compare(sl
->active
->grid
, old_screen
.grid
) == 0) {
585 screen_free(&old_screen
);
588 screen_free(&old_screen
);
592 /* Enable status line prompt. */
594 status_prompt_set(struct client
*c
, struct cmd_find_state
*fs
,
595 const char *msg
, const char *input
, prompt_input_cb inputcb
,
596 prompt_free_cb freecb
, void *data
, int flags
, enum prompt_type prompt_type
)
598 struct format_tree
*ft
;
602 ft
= format_create_from_state(NULL
, c
, fs
);
604 ft
= format_create_defaults(NULL
, c
, NULL
, NULL
, NULL
);
608 if (flags
& PROMPT_NOFORMAT
)
609 tmp
= xstrdup(input
);
611 tmp
= format_expand_time(ft
, input
);
613 status_message_clear(c
);
614 status_prompt_clear(c
);
615 status_push_screen(c
);
617 c
->prompt_string
= format_expand_time(ft
, msg
);
619 if (flags
& PROMPT_INCREMENTAL
) {
620 c
->prompt_last
= xstrdup(tmp
);
621 c
->prompt_buffer
= utf8_fromcstr("");
623 c
->prompt_last
= NULL
;
624 c
->prompt_buffer
= utf8_fromcstr(tmp
);
626 c
->prompt_index
= utf8_strlen(c
->prompt_buffer
);
628 c
->prompt_inputcb
= inputcb
;
629 c
->prompt_freecb
= freecb
;
630 c
->prompt_data
= data
;
632 memset(c
->prompt_hindex
, 0, sizeof c
->prompt_hindex
);
634 c
->prompt_flags
= flags
;
635 c
->prompt_type
= prompt_type
;
636 c
->prompt_mode
= PROMPT_ENTRY
;
638 if (~flags
& PROMPT_INCREMENTAL
)
639 c
->tty
.flags
|= (TTY_NOCURSOR
|TTY_FREEZE
);
640 c
->flags
|= CLIENT_REDRAWSTATUS
;
642 if (flags
& PROMPT_INCREMENTAL
)
643 c
->prompt_inputcb(c
, c
->prompt_data
, "=", 0);
649 /* Remove status line prompt. */
651 status_prompt_clear(struct client
*c
)
653 if (c
->prompt_string
== NULL
)
656 if (c
->prompt_freecb
!= NULL
&& c
->prompt_data
!= NULL
)
657 c
->prompt_freecb(c
->prompt_data
);
659 free(c
->prompt_last
);
660 c
->prompt_last
= NULL
;
662 free(c
->prompt_string
);
663 c
->prompt_string
= NULL
;
665 free(c
->prompt_buffer
);
666 c
->prompt_buffer
= NULL
;
668 free(c
->prompt_saved
);
669 c
->prompt_saved
= NULL
;
671 c
->tty
.flags
&= ~(TTY_NOCURSOR
|TTY_FREEZE
);
672 c
->flags
|= CLIENT_ALLREDRAWFLAGS
; /* was frozen and may have changed */
674 status_pop_screen(c
);
677 /* Update status line prompt with a new prompt string. */
679 status_prompt_update(struct client
*c
, const char *msg
, const char *input
)
681 struct format_tree
*ft
;
684 ft
= format_create(c
, NULL
, FORMAT_NONE
, 0);
685 format_defaults(ft
, c
, NULL
, NULL
, NULL
);
687 tmp
= format_expand_time(ft
, input
);
689 free(c
->prompt_string
);
690 c
->prompt_string
= format_expand_time(ft
, msg
);
692 free(c
->prompt_buffer
);
693 c
->prompt_buffer
= utf8_fromcstr(tmp
);
694 c
->prompt_index
= utf8_strlen(c
->prompt_buffer
);
696 memset(c
->prompt_hindex
, 0, sizeof c
->prompt_hindex
);
698 c
->flags
|= CLIENT_REDRAWSTATUS
;
704 /* Draw client prompt on status line of present else on last line. */
706 status_prompt_redraw(struct client
*c
)
708 struct status_line
*sl
= &c
->status
;
709 struct screen_write_ctx ctx
;
710 struct session
*s
= c
->session
;
711 struct screen old_screen
;
712 u_int i
, lines
, offset
, left
, start
, width
;
713 u_int pcursor
, pwidth
, promptline
;
714 struct grid_cell gc
, cursorgc
;
715 struct format_tree
*ft
;
717 if (c
->tty
.sx
== 0 || c
->tty
.sy
== 0)
719 memcpy(&old_screen
, sl
->active
, sizeof old_screen
);
721 lines
= status_line_size(c
);
724 screen_init(sl
->active
, c
->tty
.sx
, lines
, 0);
726 promptline
= status_prompt_line_at(c
);
727 if (promptline
> lines
- 1)
728 promptline
= lines
- 1;
730 ft
= format_create_defaults(NULL
, c
, NULL
, NULL
, NULL
);
731 if (c
->prompt_mode
== PROMPT_COMMAND
)
732 style_apply(&gc
, s
->options
, "message-command-style", ft
);
734 style_apply(&gc
, s
->options
, "message-style", ft
);
737 memcpy(&cursorgc
, &gc
, sizeof cursorgc
);
738 cursorgc
.attr
^= GRID_ATTR_REVERSE
;
740 start
= format_width(c
->prompt_string
);
741 if (start
> c
->tty
.sx
)
744 screen_write_start(&ctx
, sl
->active
);
745 screen_write_fast_copy(&ctx
, &sl
->screen
, 0, 0, c
->tty
.sx
, lines
);
746 screen_write_cursormove(&ctx
, 0, promptline
, 0);
747 for (offset
= 0; offset
< c
->tty
.sx
; offset
++)
748 screen_write_putc(&ctx
, &gc
, ' ');
749 screen_write_cursormove(&ctx
, 0, promptline
, 0);
750 format_draw(&ctx
, &gc
, start
, c
->prompt_string
, NULL
, 0);
751 screen_write_cursormove(&ctx
, start
, promptline
, 0);
753 left
= c
->tty
.sx
- start
;
757 pcursor
= utf8_strwidth(c
->prompt_buffer
, c
->prompt_index
);
758 pwidth
= utf8_strwidth(c
->prompt_buffer
, -1);
759 if (pcursor
>= left
) {
761 * The cursor would be outside the screen so start drawing
762 * with it on the right.
764 offset
= (pcursor
- left
) + 1;
770 c
->prompt_cursor
= start
+ c
->prompt_index
- offset
;
773 for (i
= 0; c
->prompt_buffer
[i
].size
!= 0; i
++) {
774 if (width
< offset
) {
775 width
+= c
->prompt_buffer
[i
].width
;
778 if (width
>= offset
+ pwidth
)
780 width
+= c
->prompt_buffer
[i
].width
;
781 if (width
> offset
+ pwidth
)
784 if (i
!= c
->prompt_index
) {
785 utf8_copy(&gc
.data
, &c
->prompt_buffer
[i
]);
786 screen_write_cell(&ctx
, &gc
);
788 utf8_copy(&cursorgc
.data
, &c
->prompt_buffer
[i
]);
789 screen_write_cell(&ctx
, &cursorgc
);
792 if (sl
->active
->cx
< screen_size_x(sl
->active
) && c
->prompt_index
>= i
)
793 screen_write_putc(&ctx
, &cursorgc
, ' ');
796 screen_write_stop(&ctx
);
798 if (grid_compare(sl
->active
->grid
, old_screen
.grid
) == 0) {
799 screen_free(&old_screen
);
802 screen_free(&old_screen
);
806 /* Is this a separator? */
808 status_prompt_in_list(const char *ws
, const struct utf8_data
*ud
)
810 if (ud
->size
!= 1 || ud
->width
!= 1)
812 return (strchr(ws
, *ud
->data
) != NULL
);
815 /* Is this a space? */
817 status_prompt_space(const struct utf8_data
*ud
)
819 if (ud
->size
!= 1 || ud
->width
!= 1)
821 return (*ud
->data
== ' ');
825 * Translate key from vi to emacs. Return 0 to drop key, 1 to process the key
826 * as an emacs key; return 2 to append to the buffer.
829 status_prompt_translate_key(struct client
*c
, key_code key
, key_code
*new_key
)
831 if (c
->prompt_mode
== PROMPT_ENTRY
) {
833 case '\001': /* C-a */
834 case '\003': /* C-c */
835 case '\005': /* C-e */
836 case '\007': /* C-g */
837 case '\010': /* C-h */
838 case '\011': /* Tab */
839 case '\013': /* C-k */
840 case '\016': /* C-n */
841 case '\020': /* C-p */
842 case '\024': /* C-t */
843 case '\025': /* C-u */
844 case '\027': /* C-w */
845 case '\031': /* C-y */
848 case KEYC_LEFT
|KEYC_CTRL
:
849 case KEYC_RIGHT
|KEYC_CTRL
:
860 case '\033': /* Escape */
861 c
->prompt_mode
= PROMPT_COMMAND
;
862 c
->flags
|= CLIENT_REDRAWSTATUS
;
871 *new_key
= KEYC_LEFT
;
878 c
->prompt_mode
= PROMPT_ENTRY
;
879 c
->flags
|= CLIENT_REDRAWSTATUS
;
880 break; /* switch mode and... */
882 c
->prompt_mode
= PROMPT_ENTRY
;
883 c
->flags
|= CLIENT_REDRAWSTATUS
;
884 *new_key
= '\025'; /* C-u */
887 case '\033': /* Escape */
888 c
->prompt_mode
= PROMPT_ENTRY
;
889 c
->flags
|= CLIENT_REDRAWSTATUS
;
901 *new_key
= KEYC_HOME
;
905 *new_key
= '\013'; /* C-k */
909 *new_key
= KEYC_BSPACE
;
912 *new_key
= 'b'|KEYC_META
;
915 *new_key
= 'B'|KEYC_VI
;
918 *new_key
= '\025'; /* C-u */
921 *new_key
= 'e'|KEYC_VI
;
924 *new_key
= 'E'|KEYC_VI
;
927 *new_key
= 'w'|KEYC_VI
;
930 *new_key
= 'W'|KEYC_VI
;
933 *new_key
= '\031'; /* C-y */
936 *new_key
= '\003'; /* C-c */
945 *new_key
= KEYC_DOWN
;
949 *new_key
= KEYC_LEFT
;
954 *new_key
= KEYC_RIGHT
;
960 case '\010' /* C-h */:
961 case '\003' /* C-c */:
969 /* Paste into prompt. */
971 status_prompt_paste(struct client
*c
)
973 struct paste_buffer
*pb
;
975 size_t size
, n
, bufsize
;
977 struct utf8_data
*ud
, *udp
;
978 enum utf8_state more
;
980 size
= utf8_strlen(c
->prompt_buffer
);
981 if (c
->prompt_saved
!= NULL
) {
982 ud
= c
->prompt_saved
;
983 n
= utf8_strlen(c
->prompt_saved
);
985 if ((pb
= paste_get_top(NULL
)) == NULL
)
987 bufdata
= paste_buffer_data(pb
, &bufsize
);
988 ud
= xreallocarray(NULL
, bufsize
+ 1, sizeof *ud
);
990 for (i
= 0; i
!= bufsize
; /* nothing */) {
991 more
= utf8_open(udp
, bufdata
[i
]);
992 if (more
== UTF8_MORE
) {
993 while (++i
!= bufsize
&& more
== UTF8_MORE
)
994 more
= utf8_append(udp
, bufdata
[i
]);
995 if (more
== UTF8_DONE
) {
1001 if (bufdata
[i
] <= 31 || bufdata
[i
] >= 127)
1003 utf8_set(udp
, bufdata
[i
]);
1013 c
->prompt_buffer
= xreallocarray(c
->prompt_buffer
, size
+ n
+ 1,
1014 sizeof *c
->prompt_buffer
);
1015 if (c
->prompt_index
== size
) {
1016 memcpy(c
->prompt_buffer
+ c
->prompt_index
, ud
,
1017 n
* sizeof *c
->prompt_buffer
);
1018 c
->prompt_index
+= n
;
1019 c
->prompt_buffer
[c
->prompt_index
].size
= 0;
1021 memmove(c
->prompt_buffer
+ c
->prompt_index
+ n
,
1022 c
->prompt_buffer
+ c
->prompt_index
,
1023 (size
+ 1 - c
->prompt_index
) * sizeof *c
->prompt_buffer
);
1024 memcpy(c
->prompt_buffer
+ c
->prompt_index
, ud
,
1025 n
* sizeof *c
->prompt_buffer
);
1026 c
->prompt_index
+= n
;
1029 if (ud
!= c
->prompt_saved
)
1034 /* Finish completion. */
1036 status_prompt_replace_complete(struct client
*c
, const char *s
)
1038 char word
[64], *allocated
= NULL
;
1039 size_t size
, n
, off
, idx
, used
;
1040 struct utf8_data
*first
, *last
, *ud
;
1042 /* Work out where the cursor currently is. */
1043 idx
= c
->prompt_index
;
1046 size
= utf8_strlen(c
->prompt_buffer
);
1048 /* Find the word we are in. */
1049 first
= &c
->prompt_buffer
[idx
];
1050 while (first
> c
->prompt_buffer
&& !status_prompt_space(first
))
1052 while (first
->size
!= 0 && status_prompt_space(first
))
1054 last
= &c
->prompt_buffer
[idx
];
1055 while (last
->size
!= 0 && !status_prompt_space(last
))
1057 while (last
> c
->prompt_buffer
&& status_prompt_space(last
))
1059 if (last
->size
!= 0)
1065 for (ud
= first
; ud
< last
; ud
++) {
1066 if (used
+ ud
->size
>= sizeof word
)
1068 memcpy(word
+ used
, ud
->data
, ud
->size
);
1076 /* Try to complete it. */
1078 allocated
= status_prompt_complete(c
, word
,
1079 first
- c
->prompt_buffer
);
1080 if (allocated
== NULL
)
1085 /* Trim out word. */
1086 n
= size
- (last
- c
->prompt_buffer
) + 1; /* with \0 */
1087 memmove(first
, last
, n
* sizeof *c
->prompt_buffer
);
1088 size
-= last
- first
;
1090 /* Insert the new word. */
1092 off
= first
- c
->prompt_buffer
;
1093 c
->prompt_buffer
= xreallocarray(c
->prompt_buffer
, size
+ 1,
1094 sizeof *c
->prompt_buffer
);
1095 first
= c
->prompt_buffer
+ off
;
1096 memmove(first
+ strlen(s
), first
, n
* sizeof *c
->prompt_buffer
);
1097 for (idx
= 0; idx
< strlen(s
); idx
++)
1098 utf8_set(&first
[idx
], s
[idx
]);
1099 c
->prompt_index
= (first
- c
->prompt_buffer
) + strlen(s
);
1105 /* Prompt forward to the next beginning of a word. */
1107 status_prompt_forward_word(struct client
*c
, size_t size
, int vi
,
1108 const char *separators
)
1110 size_t idx
= c
->prompt_index
;
1111 int word_is_separators
;
1113 /* In emacs mode, skip until the first non-whitespace character. */
1115 while (idx
!= size
&&
1116 status_prompt_space(&c
->prompt_buffer
[idx
]))
1119 /* Can't move forward if we're already at the end. */
1121 c
->prompt_index
= idx
;
1125 /* Determine the current character class (separators or not). */
1126 word_is_separators
= status_prompt_in_list(separators
,
1127 &c
->prompt_buffer
[idx
]) &&
1128 !status_prompt_space(&c
->prompt_buffer
[idx
]);
1130 /* Skip ahead until the first space or opposite character class. */
1133 if (status_prompt_space(&c
->prompt_buffer
[idx
])) {
1134 /* In vi mode, go to the start of the next word. */
1136 while (idx
!= size
&&
1137 status_prompt_space(&c
->prompt_buffer
[idx
]))
1141 } while (idx
!= size
&& word_is_separators
== status_prompt_in_list(
1142 separators
, &c
->prompt_buffer
[idx
]));
1144 c
->prompt_index
= idx
;
1147 /* Prompt forward to the next end of a word. */
1149 status_prompt_end_word(struct client
*c
, size_t size
, const char *separators
)
1151 size_t idx
= c
->prompt_index
;
1152 int word_is_separators
;
1154 /* Can't move forward if we're already at the end. */
1158 /* Find the next word. */
1162 c
->prompt_index
= idx
;
1165 } while (status_prompt_space(&c
->prompt_buffer
[idx
]));
1167 /* Determine the character class (separators or not). */
1168 word_is_separators
= status_prompt_in_list(separators
,
1169 &c
->prompt_buffer
[idx
]);
1171 /* Skip ahead until the next space or opposite character class. */
1176 } while (!status_prompt_space(&c
->prompt_buffer
[idx
]) &&
1177 word_is_separators
== status_prompt_in_list(separators
,
1178 &c
->prompt_buffer
[idx
]));
1180 /* Back up to the previous character to stop at the end of the word. */
1181 c
->prompt_index
= idx
- 1;
1184 /* Prompt backward to the previous beginning of a word. */
1186 status_prompt_backward_word(struct client
*c
, const char *separators
)
1188 size_t idx
= c
->prompt_index
;
1189 int word_is_separators
;
1191 /* Find non-whitespace. */
1194 if (!status_prompt_space(&c
->prompt_buffer
[idx
]))
1197 word_is_separators
= status_prompt_in_list(separators
,
1198 &c
->prompt_buffer
[idx
]);
1200 /* Find the character before the beginning of the word. */
1203 if (status_prompt_space(&c
->prompt_buffer
[idx
]) ||
1204 word_is_separators
!= status_prompt_in_list(separators
,
1205 &c
->prompt_buffer
[idx
])) {
1206 /* Go back to the word. */
1211 c
->prompt_index
= idx
;
1214 /* Handle keys in prompt. */
1216 status_prompt_key(struct client
*c
, key_code key
)
1218 struct options
*oo
= c
->session
->options
;
1219 char *s
, *cp
, prefix
= '=';
1220 const char *histstr
, *separators
= NULL
, *keystring
;
1222 struct utf8_data tmp
;
1223 int keys
, word_is_separators
;
1225 if (c
->prompt_flags
& PROMPT_KEY
) {
1226 keystring
= key_string_lookup_key(key
, 0);
1227 c
->prompt_inputcb(c
, c
->prompt_data
, keystring
, 1);
1228 status_prompt_clear(c
);
1231 size
= utf8_strlen(c
->prompt_buffer
);
1233 if (c
->prompt_flags
& PROMPT_NUMERIC
) {
1234 if (key
>= '0' && key
<= '9')
1236 s
= utf8_tocstr(c
->prompt_buffer
);
1237 c
->prompt_inputcb(c
, c
->prompt_data
, s
, 1);
1238 status_prompt_clear(c
);
1242 key
&= ~KEYC_MASK_FLAGS
;
1244 keys
= options_get_number(c
->session
->options
, "status-keys");
1245 if (keys
== MODEKEY_VI
) {
1246 switch (status_prompt_translate_key(c
, key
, &key
)) {
1259 case '\002': /* C-b */
1260 if (c
->prompt_index
> 0) {
1266 case '\006': /* C-f */
1267 if (c
->prompt_index
< size
) {
1273 case '\001': /* C-a */
1274 if (c
->prompt_index
!= 0) {
1275 c
->prompt_index
= 0;
1280 case '\005': /* C-e */
1281 if (c
->prompt_index
!= size
) {
1282 c
->prompt_index
= size
;
1286 case '\011': /* Tab */
1287 if (status_prompt_replace_complete(c
, NULL
))
1291 case '\010': /* C-h */
1292 if (c
->prompt_index
!= 0) {
1293 if (c
->prompt_index
== size
)
1294 c
->prompt_buffer
[--c
->prompt_index
].size
= 0;
1296 memmove(c
->prompt_buffer
+ c
->prompt_index
- 1,
1297 c
->prompt_buffer
+ c
->prompt_index
,
1298 (size
+ 1 - c
->prompt_index
) *
1299 sizeof *c
->prompt_buffer
);
1306 case '\004': /* C-d */
1307 if (c
->prompt_index
!= size
) {
1308 memmove(c
->prompt_buffer
+ c
->prompt_index
,
1309 c
->prompt_buffer
+ c
->prompt_index
+ 1,
1310 (size
+ 1 - c
->prompt_index
) *
1311 sizeof *c
->prompt_buffer
);
1315 case '\025': /* C-u */
1316 c
->prompt_buffer
[0].size
= 0;
1317 c
->prompt_index
= 0;
1319 case '\013': /* C-k */
1320 if (c
->prompt_index
< size
) {
1321 c
->prompt_buffer
[c
->prompt_index
].size
= 0;
1325 case '\027': /* C-w */
1326 separators
= options_get_string(oo
, "word-separators");
1327 idx
= c
->prompt_index
;
1329 /* Find non-whitespace. */
1332 if (!status_prompt_space(&c
->prompt_buffer
[idx
]))
1335 word_is_separators
= status_prompt_in_list(separators
,
1336 &c
->prompt_buffer
[idx
]);
1338 /* Find the character before the beginning of the word. */
1341 if (status_prompt_space(&c
->prompt_buffer
[idx
]) ||
1342 word_is_separators
!= status_prompt_in_list(
1343 separators
, &c
->prompt_buffer
[idx
])) {
1344 /* Go back to the word. */
1350 free(c
->prompt_saved
);
1351 c
->prompt_saved
= xcalloc(sizeof *c
->prompt_buffer
,
1352 (c
->prompt_index
- idx
) + 1);
1353 memcpy(c
->prompt_saved
, c
->prompt_buffer
+ idx
,
1354 (c
->prompt_index
- idx
) * sizeof *c
->prompt_buffer
);
1356 memmove(c
->prompt_buffer
+ idx
,
1357 c
->prompt_buffer
+ c
->prompt_index
,
1358 (size
+ 1 - c
->prompt_index
) *
1359 sizeof *c
->prompt_buffer
);
1360 memset(c
->prompt_buffer
+ size
- (c
->prompt_index
- idx
),
1361 '\0', (c
->prompt_index
- idx
) * sizeof *c
->prompt_buffer
);
1362 c
->prompt_index
= idx
;
1365 case KEYC_RIGHT
|KEYC_CTRL
:
1367 separators
= options_get_string(oo
, "word-separators");
1368 status_prompt_forward_word(c
, size
, 0, separators
);
1371 status_prompt_end_word(c
, size
, "");
1374 separators
= options_get_string(oo
, "word-separators");
1375 status_prompt_end_word(c
, size
, separators
);
1378 status_prompt_forward_word(c
, size
, 1, "");
1381 separators
= options_get_string(oo
, "word-separators");
1382 status_prompt_forward_word(c
, size
, 1, separators
);
1385 status_prompt_backward_word(c
, "");
1387 case KEYC_LEFT
|KEYC_CTRL
:
1389 separators
= options_get_string(oo
, "word-separators");
1390 status_prompt_backward_word(c
, separators
);
1393 case '\020': /* C-p */
1394 histstr
= status_prompt_up_history(c
->prompt_hindex
,
1396 if (histstr
== NULL
)
1398 free(c
->prompt_buffer
);
1399 c
->prompt_buffer
= utf8_fromcstr(histstr
);
1400 c
->prompt_index
= utf8_strlen(c
->prompt_buffer
);
1403 case '\016': /* C-n */
1404 histstr
= status_prompt_down_history(c
->prompt_hindex
,
1406 if (histstr
== NULL
)
1408 free(c
->prompt_buffer
);
1409 c
->prompt_buffer
= utf8_fromcstr(histstr
);
1410 c
->prompt_index
= utf8_strlen(c
->prompt_buffer
);
1412 case '\031': /* C-y */
1413 if (status_prompt_paste(c
))
1416 case '\024': /* C-t */
1417 idx
= c
->prompt_index
;
1421 utf8_copy(&tmp
, &c
->prompt_buffer
[idx
- 2]);
1422 utf8_copy(&c
->prompt_buffer
[idx
- 2],
1423 &c
->prompt_buffer
[idx
- 1]);
1424 utf8_copy(&c
->prompt_buffer
[idx
- 1], &tmp
);
1425 c
->prompt_index
= idx
;
1431 s
= utf8_tocstr(c
->prompt_buffer
);
1433 status_prompt_add_history(s
, c
->prompt_type
);
1434 if (c
->prompt_inputcb(c
, c
->prompt_data
, s
, 1) == 0)
1435 status_prompt_clear(c
);
1438 case '\033': /* Escape */
1439 case '\003': /* C-c */
1440 case '\007': /* C-g */
1441 if (c
->prompt_inputcb(c
, c
->prompt_data
, NULL
, 1) == 0)
1442 status_prompt_clear(c
);
1444 case '\022': /* C-r */
1445 if (~c
->prompt_flags
& PROMPT_INCREMENTAL
)
1447 if (c
->prompt_buffer
[0].size
== 0) {
1449 free(c
->prompt_buffer
);
1450 c
->prompt_buffer
= utf8_fromcstr(c
->prompt_last
);
1451 c
->prompt_index
= utf8_strlen(c
->prompt_buffer
);
1455 case '\023': /* C-s */
1456 if (~c
->prompt_flags
& PROMPT_INCREMENTAL
)
1458 if (c
->prompt_buffer
[0].size
== 0) {
1460 free(c
->prompt_buffer
);
1461 c
->prompt_buffer
= utf8_fromcstr(c
->prompt_last
);
1462 c
->prompt_index
= utf8_strlen(c
->prompt_buffer
);
1470 c
->flags
|= CLIENT_REDRAWSTATUS
;
1474 if (key
<= 0x1f || (key
>= KEYC_BASE
&& key
< KEYC_BASE_END
))
1477 utf8_set(&tmp
, key
);
1478 else if (KEYC_IS_UNICODE(key
))
1479 utf8_to_data(key
, &tmp
);
1483 c
->prompt_buffer
= xreallocarray(c
->prompt_buffer
, size
+ 2,
1484 sizeof *c
->prompt_buffer
);
1486 if (c
->prompt_index
== size
) {
1487 utf8_copy(&c
->prompt_buffer
[c
->prompt_index
], &tmp
);
1489 c
->prompt_buffer
[c
->prompt_index
].size
= 0;
1491 memmove(c
->prompt_buffer
+ c
->prompt_index
+ 1,
1492 c
->prompt_buffer
+ c
->prompt_index
,
1493 (size
+ 1 - c
->prompt_index
) *
1494 sizeof *c
->prompt_buffer
);
1495 utf8_copy(&c
->prompt_buffer
[c
->prompt_index
], &tmp
);
1499 if (c
->prompt_flags
& PROMPT_SINGLE
) {
1500 if (utf8_strlen(c
->prompt_buffer
) != 1)
1501 status_prompt_clear(c
);
1503 s
= utf8_tocstr(c
->prompt_buffer
);
1504 if (c
->prompt_inputcb(c
, c
->prompt_data
, s
, 1) == 0)
1505 status_prompt_clear(c
);
1511 c
->flags
|= CLIENT_REDRAWSTATUS
;
1512 if (c
->prompt_flags
& PROMPT_INCREMENTAL
) {
1513 s
= utf8_tocstr(c
->prompt_buffer
);
1514 xasprintf(&cp
, "%c%s", prefix
, s
);
1515 c
->prompt_inputcb(c
, c
->prompt_data
, cp
, 0);
1522 /* Get previous line from the history. */
1524 status_prompt_up_history(u_int
*idx
, u_int type
)
1527 * History runs from 0 to size - 1. Index is from 0 to size. Zero is
1531 if (status_prompt_hsize
[type
] == 0 ||
1532 idx
[type
] == status_prompt_hsize
[type
])
1535 return (status_prompt_hlist
[type
][status_prompt_hsize
[type
] - idx
[type
]]);
1538 /* Get next line from the history. */
1540 status_prompt_down_history(u_int
*idx
, u_int type
)
1542 if (status_prompt_hsize
[type
] == 0 || idx
[type
] == 0)
1547 return (status_prompt_hlist
[type
][status_prompt_hsize
[type
] - idx
[type
]]);
1550 /* Add line to the history. */
1552 status_prompt_add_history(const char *line
, u_int type
)
1554 u_int i
, oldsize
, newsize
, freecount
, hlimit
, new = 1;
1557 oldsize
= status_prompt_hsize
[type
];
1559 strcmp(status_prompt_hlist
[type
][oldsize
- 1], line
) == 0)
1562 hlimit
= options_get_number(global_options
, "prompt-history-limit");
1563 if (hlimit
> oldsize
) {
1566 newsize
= oldsize
+ new;
1569 freecount
= oldsize
+ new - newsize
;
1570 if (freecount
> oldsize
)
1571 freecount
= oldsize
;
1574 for (i
= 0; i
< freecount
; i
++)
1575 free(status_prompt_hlist
[type
][i
]);
1576 movesize
= (oldsize
- freecount
) *
1577 sizeof *status_prompt_hlist
[type
];
1579 memmove(&status_prompt_hlist
[type
][0],
1580 &status_prompt_hlist
[type
][freecount
], movesize
);
1585 free(status_prompt_hlist
[type
]);
1586 status_prompt_hlist
[type
] = NULL
;
1587 } else if (newsize
!= oldsize
) {
1588 status_prompt_hlist
[type
] =
1589 xreallocarray(status_prompt_hlist
[type
], newsize
,
1590 sizeof *status_prompt_hlist
[type
]);
1593 if (new == 1 && newsize
> 0)
1594 status_prompt_hlist
[type
][newsize
- 1] = xstrdup(line
);
1595 status_prompt_hsize
[type
] = newsize
;
1598 /* Add to completion list. */
1600 status_prompt_add_list(char ***list
, u_int
*size
, const char *s
)
1604 for (i
= 0; i
< *size
; i
++) {
1605 if (strcmp((*list
)[i
], s
) == 0)
1608 *list
= xreallocarray(*list
, (*size
) + 1, sizeof **list
);
1609 (*list
)[(*size
)++] = xstrdup(s
);
1612 /* Build completion list. */
1614 status_prompt_complete_list(u_int
*size
, const char *s
, int at_start
)
1616 char **list
= NULL
, *tmp
;
1617 const char **layout
, *value
, *cp
;
1618 const struct cmd_entry
**cmdent
;
1619 const struct options_table_entry
*oe
;
1620 size_t slen
= strlen(s
), valuelen
;
1621 struct options_entry
*o
;
1622 struct options_array_item
*a
;
1623 const char *layouts
[] = {
1624 "even-horizontal", "even-vertical", "main-horizontal",
1625 "main-vertical", "tiled", NULL
1629 for (cmdent
= cmd_table
; *cmdent
!= NULL
; cmdent
++) {
1630 if (strncmp((*cmdent
)->name
, s
, slen
) == 0)
1631 status_prompt_add_list(&list
, size
, (*cmdent
)->name
);
1632 if ((*cmdent
)->alias
!= NULL
&&
1633 strncmp((*cmdent
)->alias
, s
, slen
) == 0)
1634 status_prompt_add_list(&list
, size
, (*cmdent
)->alias
);
1636 o
= options_get_only(global_options
, "command-alias");
1638 a
= options_array_first(o
);
1640 value
= options_array_item_value(a
)->string
;
1641 if ((cp
= strchr(value
, '=')) == NULL
)
1643 valuelen
= cp
- value
;
1644 if (slen
> valuelen
|| strncmp(value
, s
, slen
) != 0)
1647 xasprintf(&tmp
, "%.*s", (int)valuelen
, value
);
1648 status_prompt_add_list(&list
, size
, tmp
);
1652 a
= options_array_next(a
);
1657 for (oe
= options_table
; oe
->name
!= NULL
; oe
++) {
1658 if (strncmp(oe
->name
, s
, slen
) == 0)
1659 status_prompt_add_list(&list
, size
, oe
->name
);
1661 for (layout
= layouts
; *layout
!= NULL
; layout
++) {
1662 if (strncmp(*layout
, s
, slen
) == 0)
1663 status_prompt_add_list(&list
, size
, *layout
);
1668 /* Find longest prefix. */
1670 status_prompt_complete_prefix(char **list
, u_int size
)
1676 if (list
== NULL
|| size
== 0)
1678 out
= xstrdup(list
[0]);
1679 for (i
= 1; i
< size
; i
++) {
1680 j
= strlen(list
[i
]);
1681 if (j
> strlen(out
))
1683 for (; j
> 0; j
--) {
1684 if (out
[j
- 1] != list
[i
][j
- 1])
1691 /* Complete word menu callback. */
1693 status_prompt_menu_callback(__unused
struct menu
*menu
, u_int idx
, key_code key
,
1696 struct status_prompt_menu
*spm
= data
;
1697 struct client
*c
= spm
->c
;
1701 if (key
!= KEYC_NONE
) {
1703 if (spm
->flag
== '\0')
1704 s
= xstrdup(spm
->list
[idx
]);
1706 xasprintf(&s
, "-%c%s", spm
->flag
, spm
->list
[idx
]);
1707 if (c
->prompt_type
== PROMPT_TYPE_WINDOW_TARGET
) {
1708 free(c
->prompt_buffer
);
1709 c
->prompt_buffer
= utf8_fromcstr(s
);
1710 c
->prompt_index
= utf8_strlen(c
->prompt_buffer
);
1711 c
->flags
|= CLIENT_REDRAWSTATUS
;
1712 } else if (status_prompt_replace_complete(c
, s
))
1713 c
->flags
|= CLIENT_REDRAWSTATUS
;
1717 for (i
= 0; i
< spm
->size
; i
++)
1722 /* Show complete word menu. */
1724 status_prompt_complete_list_menu(struct client
*c
, char **list
, u_int size
,
1725 u_int offset
, char flag
)
1728 struct menu_item item
;
1729 struct status_prompt_menu
*spm
;
1730 u_int lines
= status_line_size(c
), height
, i
;
1735 if (c
->tty
.sy
- lines
< 3)
1738 spm
= xmalloc(sizeof *spm
);
1744 height
= c
->tty
.sy
- lines
- 2;
1749 spm
->start
= size
- height
;
1751 menu
= menu_create("");
1752 for (i
= spm
->start
; i
< size
; i
++) {
1753 item
.name
= list
[i
];
1754 item
.key
= '0' + (i
- spm
->start
);
1755 item
.command
= NULL
;
1756 menu_add_item(menu
, &item
, NULL
, c
, NULL
);
1759 if (options_get_number(c
->session
->options
, "status-position") == 0)
1762 py
= c
->tty
.sy
- 3 - height
;
1763 offset
+= utf8_cstrwidth(c
->prompt_string
);
1769 if (menu_display(menu
, MENU_NOMOUSE
|MENU_TAB
, 0, NULL
, offset
,
1770 py
, c
, NULL
, status_prompt_menu_callback
, spm
) != 0) {
1778 /* Show complete word menu. */
1780 status_prompt_complete_window_menu(struct client
*c
, struct session
*s
,
1781 const char *word
, u_int offset
, char flag
)
1784 struct menu_item item
;
1785 struct status_prompt_menu
*spm
;
1787 char **list
= NULL
, *tmp
;
1788 u_int lines
= status_line_size(c
), height
;
1791 if (c
->tty
.sy
- lines
< 3)
1794 spm
= xmalloc(sizeof *spm
);
1798 height
= c
->tty
.sy
- lines
- 2;
1803 menu
= menu_create("");
1804 RB_FOREACH(wl
, winlinks
, &s
->windows
) {
1805 if (word
!= NULL
&& *word
!= '\0') {
1806 xasprintf(&tmp
, "%d", wl
->idx
);
1807 if (strncmp(tmp
, word
, strlen(word
)) != 0) {
1814 list
= xreallocarray(list
, size
+ 1, sizeof *list
);
1815 if (c
->prompt_type
== PROMPT_TYPE_WINDOW_TARGET
) {
1816 xasprintf(&tmp
, "%d (%s)", wl
->idx
, wl
->window
->name
);
1817 xasprintf(&list
[size
++], "%d", wl
->idx
);
1819 xasprintf(&tmp
, "%s:%d (%s)", s
->name
, wl
->idx
,
1821 xasprintf(&list
[size
++], "%s:%d", s
->name
, wl
->idx
);
1824 item
.key
= '0' + size
- 1;
1825 item
.command
= NULL
;
1826 menu_add_item(menu
, &item
, NULL
, c
, NULL
);
1839 xasprintf(&tmp
, "-%c%s", flag
, list
[0]);
1852 if (options_get_number(c
->session
->options
, "status-position") == 0)
1855 py
= c
->tty
.sy
- 3 - height
;
1856 offset
+= utf8_cstrwidth(c
->prompt_string
);
1862 if (menu_display(menu
, MENU_NOMOUSE
|MENU_TAB
, 0, NULL
, offset
,
1863 py
, c
, NULL
, status_prompt_menu_callback
, spm
) != 0) {
1871 /* Sort complete list. */
1873 status_prompt_complete_sort(const void *a
, const void *b
)
1875 const char **aa
= (const char **)a
, **bb
= (const char **)b
;
1877 return (strcmp(*aa
, *bb
));
1880 /* Complete a session. */
1882 status_prompt_complete_session(char ***list
, u_int
*size
, const char *s
,
1885 struct session
*loop
;
1886 char *out
, *tmp
, n
[11];
1888 RB_FOREACH(loop
, sessions
, &sessions
) {
1889 if (*s
== '\0' || strncmp(loop
->name
, s
, strlen(s
)) == 0) {
1890 *list
= xreallocarray(*list
, (*size
) + 2,
1892 xasprintf(&(*list
)[(*size
)++], "%s:", loop
->name
);
1893 } else if (*s
== '$') {
1894 xsnprintf(n
, sizeof n
, "%u", loop
->id
);
1896 strncmp(n
, s
+ 1, strlen(s
) - 1) == 0) {
1897 *list
= xreallocarray(*list
, (*size
) + 2,
1899 xasprintf(&(*list
)[(*size
)++], "$%s:", n
);
1903 out
= status_prompt_complete_prefix(*list
, *size
);
1904 if (out
!= NULL
&& flag
!= '\0') {
1905 xasprintf(&tmp
, "-%c%s", flag
, out
);
1912 /* Complete word. */
1914 status_prompt_complete(struct client
*c
, const char *word
, u_int offset
)
1916 struct session
*session
;
1917 const char *s
, *colon
;
1918 char **list
= NULL
, *copy
= NULL
, *out
= NULL
;
1922 if (*word
== '\0' &&
1923 c
->prompt_type
!= PROMPT_TYPE_TARGET
&&
1924 c
->prompt_type
!= PROMPT_TYPE_WINDOW_TARGET
)
1927 if (c
->prompt_type
!= PROMPT_TYPE_TARGET
&&
1928 c
->prompt_type
!= PROMPT_TYPE_WINDOW_TARGET
&&
1929 strncmp(word
, "-t", 2) != 0 &&
1930 strncmp(word
, "-s", 2) != 0) {
1931 list
= status_prompt_complete_list(&size
, word
, offset
== 0);
1935 xasprintf(&out
, "%s ", list
[0]);
1937 out
= status_prompt_complete_prefix(list
, size
);
1941 if (c
->prompt_type
== PROMPT_TYPE_TARGET
||
1942 c
->prompt_type
== PROMPT_TYPE_WINDOW_TARGET
) {
1951 /* If this is a window completion, open the window menu. */
1952 if (c
->prompt_type
== PROMPT_TYPE_WINDOW_TARGET
) {
1953 out
= status_prompt_complete_window_menu(c
, c
->session
, s
,
1957 colon
= strchr(s
, ':');
1959 /* If there is no colon, complete as a session. */
1960 if (colon
== NULL
) {
1961 out
= status_prompt_complete_session(&list
, &size
, s
, flag
);
1965 /* If there is a colon but no period, find session and show a menu. */
1966 if (strchr(colon
+ 1, '.') == NULL
) {
1968 session
= c
->session
;
1971 *strchr(copy
, ':') = '\0';
1972 session
= session_find(copy
);
1974 if (session
== NULL
)
1977 out
= status_prompt_complete_window_menu(c
, session
, colon
+ 1,
1985 qsort(list
, size
, sizeof *list
, status_prompt_complete_sort
);
1986 for (i
= 0; i
< size
; i
++)
1987 log_debug("complete %u: %s", i
, list
[i
]);
1990 if (out
!= NULL
&& strcmp(word
, out
) == 0) {
1995 !status_prompt_complete_list_menu(c
, list
, size
, offset
, flag
)) {
1996 for (i
= 0; i
< size
; i
++)
2003 /* Return the type of the prompt as an enum. */
2005 status_prompt_type(const char *type
)
2009 for (i
= 0; i
< PROMPT_NTYPES
; i
++) {
2010 if (strcmp(type
, status_prompt_type_string(i
)) == 0)
2013 return (PROMPT_TYPE_INVALID
);
2016 /* Accessor for prompt_type_strings. */
2018 status_prompt_type_string(u_int type
)
2020 if (type
>= PROMPT_NTYPES
)
2022 return (prompt_type_strings
[type
]);