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
);
445 sle
->expanded
= expanded
;
448 screen_write_stop(&ctx
);
450 /* Free the format tree. */
453 /* Return if the status line has changed. */
454 log_debug("%s exit: force=%d, changed=%d", __func__
, force
, changed
);
455 return (force
|| changed
);
458 /* Set a status line message. */
460 status_message_set(struct client
*c
, int delay
, int ignore_styles
,
461 int ignore_keys
, const char *fmt
, ...)
466 status_message_clear(c
);
467 status_push_screen(c
);
470 xvasprintf(&c
->message_string
, fmt
, ap
);
473 server_add_message("%s message: %s", c
->name
, c
->message_string
);
476 * With delay -1, the display-time option is used; zero means wait for
477 * key press; more than zero is the actual delay time in milliseconds.
480 delay
= options_get_number(c
->session
->options
, "display-time");
482 tv
.tv_sec
= delay
/ 1000;
483 tv
.tv_usec
= (delay
% 1000) * 1000L;
485 if (event_initialized(&c
->message_timer
))
486 evtimer_del(&c
->message_timer
);
487 evtimer_set(&c
->message_timer
, status_message_callback
, c
);
489 evtimer_add(&c
->message_timer
, &tv
);
493 c
->message_ignore_keys
= ignore_keys
;
494 c
->message_ignore_styles
= ignore_styles
;
496 c
->tty
.flags
|= (TTY_NOCURSOR
|TTY_FREEZE
);
497 c
->flags
|= CLIENT_REDRAWSTATUS
;
500 /* Clear status line message. */
502 status_message_clear(struct client
*c
)
504 if (c
->message_string
== NULL
)
507 free(c
->message_string
);
508 c
->message_string
= NULL
;
510 if (c
->prompt_string
== NULL
)
511 c
->tty
.flags
&= ~(TTY_NOCURSOR
|TTY_FREEZE
);
512 c
->flags
|= CLIENT_ALLREDRAWFLAGS
; /* was frozen and may have changed */
514 status_pop_screen(c
);
517 /* Clear status line message after timer expires. */
519 status_message_callback(__unused
int fd
, __unused
short event
, void *data
)
521 struct client
*c
= data
;
523 status_message_clear(c
);
526 /* Draw client message on status line of present else on last line. */
528 status_message_redraw(struct client
*c
)
530 struct status_line
*sl
= &c
->status
;
531 struct screen_write_ctx ctx
;
532 struct session
*s
= c
->session
;
533 struct screen old_screen
;
537 struct format_tree
*ft
;
539 if (c
->tty
.sx
== 0 || c
->tty
.sy
== 0)
541 memcpy(&old_screen
, sl
->active
, sizeof old_screen
);
543 lines
= status_line_size(c
);
546 screen_init(sl
->active
, c
->tty
.sx
, lines
, 0);
548 len
= screen_write_strlen("%s", c
->message_string
);
552 ft
= format_create_defaults(NULL
, c
, NULL
, NULL
, NULL
);
553 style_apply(&gc
, s
->options
, "message-style", ft
);
556 screen_write_start(&ctx
, sl
->active
);
557 screen_write_fast_copy(&ctx
, &sl
->screen
, 0, 0, c
->tty
.sx
, lines
- 1);
558 screen_write_cursormove(&ctx
, 0, lines
- 1, 0);
559 for (offset
= 0; offset
< c
->tty
.sx
; offset
++)
560 screen_write_putc(&ctx
, &gc
, ' ');
561 screen_write_cursormove(&ctx
, 0, lines
- 1, 0);
562 if (c
->message_ignore_styles
)
563 screen_write_nputs(&ctx
, len
, &gc
, "%s", c
->message_string
);
565 format_draw(&ctx
, &gc
, c
->tty
.sx
, c
->message_string
, NULL
);
566 screen_write_stop(&ctx
);
568 if (grid_compare(sl
->active
->grid
, old_screen
.grid
) == 0) {
569 screen_free(&old_screen
);
572 screen_free(&old_screen
);
576 /* Enable status line prompt. */
578 status_prompt_set(struct client
*c
, struct cmd_find_state
*fs
,
579 const char *msg
, const char *input
, prompt_input_cb inputcb
,
580 prompt_free_cb freecb
, void *data
, int flags
, enum prompt_type prompt_type
)
582 struct format_tree
*ft
;
586 ft
= format_create_from_state(NULL
, c
, fs
);
588 ft
= format_create_defaults(NULL
, c
, NULL
, NULL
, NULL
);
592 if (flags
& PROMPT_NOFORMAT
)
593 tmp
= xstrdup(input
);
595 tmp
= format_expand_time(ft
, input
);
597 status_message_clear(c
);
598 status_prompt_clear(c
);
599 status_push_screen(c
);
601 c
->prompt_string
= format_expand_time(ft
, msg
);
603 if (flags
& PROMPT_INCREMENTAL
) {
604 c
->prompt_last
= xstrdup(tmp
);
605 c
->prompt_buffer
= utf8_fromcstr("");
607 c
->prompt_last
= NULL
;
608 c
->prompt_buffer
= utf8_fromcstr(tmp
);
610 c
->prompt_index
= utf8_strlen(c
->prompt_buffer
);
612 c
->prompt_inputcb
= inputcb
;
613 c
->prompt_freecb
= freecb
;
614 c
->prompt_data
= data
;
616 memset(c
->prompt_hindex
, 0, sizeof c
->prompt_hindex
);
618 c
->prompt_flags
= flags
;
619 c
->prompt_type
= prompt_type
;
620 c
->prompt_mode
= PROMPT_ENTRY
;
622 if (~flags
& PROMPT_INCREMENTAL
)
623 c
->tty
.flags
|= (TTY_NOCURSOR
|TTY_FREEZE
);
624 c
->flags
|= CLIENT_REDRAWSTATUS
;
626 if (flags
& PROMPT_INCREMENTAL
)
627 c
->prompt_inputcb(c
, c
->prompt_data
, "=", 0);
633 /* Remove status line prompt. */
635 status_prompt_clear(struct client
*c
)
637 if (c
->prompt_string
== NULL
)
640 if (c
->prompt_freecb
!= NULL
&& c
->prompt_data
!= NULL
)
641 c
->prompt_freecb(c
->prompt_data
);
643 free(c
->prompt_last
);
644 c
->prompt_last
= NULL
;
646 free(c
->prompt_string
);
647 c
->prompt_string
= NULL
;
649 free(c
->prompt_buffer
);
650 c
->prompt_buffer
= NULL
;
652 free(c
->prompt_saved
);
653 c
->prompt_saved
= NULL
;
655 c
->tty
.flags
&= ~(TTY_NOCURSOR
|TTY_FREEZE
);
656 c
->flags
|= CLIENT_ALLREDRAWFLAGS
; /* was frozen and may have changed */
658 status_pop_screen(c
);
661 /* Update status line prompt with a new prompt string. */
663 status_prompt_update(struct client
*c
, const char *msg
, const char *input
)
665 struct format_tree
*ft
;
668 ft
= format_create(c
, NULL
, FORMAT_NONE
, 0);
669 format_defaults(ft
, c
, NULL
, NULL
, NULL
);
671 tmp
= format_expand_time(ft
, input
);
673 free(c
->prompt_string
);
674 c
->prompt_string
= format_expand_time(ft
, msg
);
676 free(c
->prompt_buffer
);
677 c
->prompt_buffer
= utf8_fromcstr(tmp
);
678 c
->prompt_index
= utf8_strlen(c
->prompt_buffer
);
680 memset(c
->prompt_hindex
, 0, sizeof c
->prompt_hindex
);
682 c
->flags
|= CLIENT_REDRAWSTATUS
;
688 /* Draw client prompt on status line of present else on last line. */
690 status_prompt_redraw(struct client
*c
)
692 struct status_line
*sl
= &c
->status
;
693 struct screen_write_ctx ctx
;
694 struct session
*s
= c
->session
;
695 struct screen old_screen
;
696 u_int i
, lines
, offset
, left
, start
, width
;
697 u_int pcursor
, pwidth
;
698 struct grid_cell gc
, cursorgc
;
699 struct format_tree
*ft
;
701 if (c
->tty
.sx
== 0 || c
->tty
.sy
== 0)
703 memcpy(&old_screen
, sl
->active
, sizeof old_screen
);
705 lines
= status_line_size(c
);
708 screen_init(sl
->active
, c
->tty
.sx
, lines
, 0);
710 ft
= format_create_defaults(NULL
, c
, NULL
, NULL
, NULL
);
711 if (c
->prompt_mode
== PROMPT_COMMAND
)
712 style_apply(&gc
, s
->options
, "message-command-style", ft
);
714 style_apply(&gc
, s
->options
, "message-style", ft
);
717 memcpy(&cursorgc
, &gc
, sizeof cursorgc
);
718 cursorgc
.attr
^= GRID_ATTR_REVERSE
;
720 start
= screen_write_strlen("%s", c
->prompt_string
);
721 if (start
> c
->tty
.sx
)
724 screen_write_start(&ctx
, sl
->active
);
725 screen_write_fast_copy(&ctx
, &sl
->screen
, 0, 0, c
->tty
.sx
, lines
- 1);
726 screen_write_cursormove(&ctx
, 0, lines
- 1, 0);
727 for (offset
= 0; offset
< c
->tty
.sx
; offset
++)
728 screen_write_putc(&ctx
, &gc
, ' ');
729 screen_write_cursormove(&ctx
, 0, lines
- 1, 0);
730 screen_write_nputs(&ctx
, start
, &gc
, "%s", c
->prompt_string
);
731 screen_write_cursormove(&ctx
, start
, lines
- 1, 0);
733 left
= c
->tty
.sx
- start
;
737 pcursor
= utf8_strwidth(c
->prompt_buffer
, c
->prompt_index
);
738 pwidth
= utf8_strwidth(c
->prompt_buffer
, -1);
739 if (pcursor
>= left
) {
741 * The cursor would be outside the screen so start drawing
742 * with it on the right.
744 offset
= (pcursor
- left
) + 1;
752 for (i
= 0; c
->prompt_buffer
[i
].size
!= 0; i
++) {
753 if (width
< offset
) {
754 width
+= c
->prompt_buffer
[i
].width
;
757 if (width
>= offset
+ pwidth
)
759 width
+= c
->prompt_buffer
[i
].width
;
760 if (width
> offset
+ pwidth
)
763 if (i
!= c
->prompt_index
) {
764 utf8_copy(&gc
.data
, &c
->prompt_buffer
[i
]);
765 screen_write_cell(&ctx
, &gc
);
767 utf8_copy(&cursorgc
.data
, &c
->prompt_buffer
[i
]);
768 screen_write_cell(&ctx
, &cursorgc
);
771 if (sl
->active
->cx
< screen_size_x(sl
->active
) && c
->prompt_index
>= i
)
772 screen_write_putc(&ctx
, &cursorgc
, ' ');
775 screen_write_stop(&ctx
);
777 if (grid_compare(sl
->active
->grid
, old_screen
.grid
) == 0) {
778 screen_free(&old_screen
);
781 screen_free(&old_screen
);
785 /* Is this a separator? */
787 status_prompt_in_list(const char *ws
, const struct utf8_data
*ud
)
789 if (ud
->size
!= 1 || ud
->width
!= 1)
791 return (strchr(ws
, *ud
->data
) != NULL
);
794 /* Is this a space? */
796 status_prompt_space(const struct utf8_data
*ud
)
798 if (ud
->size
!= 1 || ud
->width
!= 1)
800 return (*ud
->data
== ' ');
804 * Translate key from vi to emacs. Return 0 to drop key, 1 to process the key
805 * as an emacs key; return 2 to append to the buffer.
808 status_prompt_translate_key(struct client
*c
, key_code key
, key_code
*new_key
)
810 if (c
->prompt_mode
== PROMPT_ENTRY
) {
812 case '\003': /* C-c */
813 case '\007': /* C-g */
814 case '\010': /* C-h */
815 case '\011': /* Tab */
816 case '\025': /* C-u */
817 case '\027': /* C-w */
830 case '\033': /* Escape */
831 c
->prompt_mode
= PROMPT_COMMAND
;
832 c
->flags
|= CLIENT_REDRAWSTATUS
;
845 c
->prompt_mode
= PROMPT_ENTRY
;
846 c
->flags
|= CLIENT_REDRAWSTATUS
;
847 break; /* switch mode and... */
849 c
->prompt_mode
= PROMPT_ENTRY
;
850 c
->flags
|= CLIENT_REDRAWSTATUS
;
851 *new_key
= '\025'; /* C-u */
854 case '\033': /* Escape */
855 c
->prompt_mode
= PROMPT_ENTRY
;
856 c
->flags
|= CLIENT_REDRAWSTATUS
;
868 *new_key
= KEYC_HOME
;
872 *new_key
= '\013'; /* C-k */
876 *new_key
= KEYC_BSPACE
;
879 *new_key
= 'b'|KEYC_META
;
882 *new_key
= 'B'|KEYC_VI
;
888 *new_key
= 'e'|KEYC_VI
;
891 *new_key
= 'E'|KEYC_VI
;
894 *new_key
= 'w'|KEYC_VI
;
897 *new_key
= 'W'|KEYC_VI
;
900 *new_key
= '\031'; /* C-y */
903 *new_key
= '\003'; /* C-c */
912 *new_key
= KEYC_DOWN
;
916 *new_key
= KEYC_LEFT
;
921 *new_key
= KEYC_RIGHT
;
927 case '\010' /* C-h */:
928 case '\003' /* C-c */:
936 /* Paste into prompt. */
938 status_prompt_paste(struct client
*c
)
940 struct paste_buffer
*pb
;
942 size_t size
, n
, bufsize
;
944 struct utf8_data
*ud
, *udp
;
945 enum utf8_state more
;
947 size
= utf8_strlen(c
->prompt_buffer
);
948 if (c
->prompt_saved
!= NULL
) {
949 ud
= c
->prompt_saved
;
950 n
= utf8_strlen(c
->prompt_saved
);
952 if ((pb
= paste_get_top(NULL
)) == NULL
)
954 bufdata
= paste_buffer_data(pb
, &bufsize
);
955 ud
= xreallocarray(NULL
, bufsize
+ 1, sizeof *ud
);
957 for (i
= 0; i
!= bufsize
; /* nothing */) {
958 more
= utf8_open(udp
, bufdata
[i
]);
959 if (more
== UTF8_MORE
) {
960 while (++i
!= bufsize
&& more
== UTF8_MORE
)
961 more
= utf8_append(udp
, bufdata
[i
]);
962 if (more
== UTF8_DONE
) {
968 if (bufdata
[i
] <= 31 || bufdata
[i
] >= 127)
970 utf8_set(udp
, bufdata
[i
]);
980 c
->prompt_buffer
= xreallocarray(c
->prompt_buffer
, size
+ n
+ 1,
981 sizeof *c
->prompt_buffer
);
982 if (c
->prompt_index
== size
) {
983 memcpy(c
->prompt_buffer
+ c
->prompt_index
, ud
,
984 n
* sizeof *c
->prompt_buffer
);
985 c
->prompt_index
+= n
;
986 c
->prompt_buffer
[c
->prompt_index
].size
= 0;
988 memmove(c
->prompt_buffer
+ c
->prompt_index
+ n
,
989 c
->prompt_buffer
+ c
->prompt_index
,
990 (size
+ 1 - c
->prompt_index
) * sizeof *c
->prompt_buffer
);
991 memcpy(c
->prompt_buffer
+ c
->prompt_index
, ud
,
992 n
* sizeof *c
->prompt_buffer
);
993 c
->prompt_index
+= n
;
996 if (ud
!= c
->prompt_saved
)
1001 /* Finish completion. */
1003 status_prompt_replace_complete(struct client
*c
, const char *s
)
1005 char word
[64], *allocated
= NULL
;
1006 size_t size
, n
, off
, idx
, used
;
1007 struct utf8_data
*first
, *last
, *ud
;
1009 /* Work out where the cursor currently is. */
1010 idx
= c
->prompt_index
;
1013 size
= utf8_strlen(c
->prompt_buffer
);
1015 /* Find the word we are in. */
1016 first
= &c
->prompt_buffer
[idx
];
1017 while (first
> c
->prompt_buffer
&& !status_prompt_space(first
))
1019 while (first
->size
!= 0 && status_prompt_space(first
))
1021 last
= &c
->prompt_buffer
[idx
];
1022 while (last
->size
!= 0 && !status_prompt_space(last
))
1024 while (last
> c
->prompt_buffer
&& status_prompt_space(last
))
1026 if (last
->size
!= 0)
1032 for (ud
= first
; ud
< last
; ud
++) {
1033 if (used
+ ud
->size
>= sizeof word
)
1035 memcpy(word
+ used
, ud
->data
, ud
->size
);
1043 /* Try to complete it. */
1045 allocated
= status_prompt_complete(c
, word
,
1046 first
- c
->prompt_buffer
);
1047 if (allocated
== NULL
)
1052 /* Trim out word. */
1053 n
= size
- (last
- c
->prompt_buffer
) + 1; /* with \0 */
1054 memmove(first
, last
, n
* sizeof *c
->prompt_buffer
);
1055 size
-= last
- first
;
1057 /* Insert the new word. */
1059 off
= first
- c
->prompt_buffer
;
1060 c
->prompt_buffer
= xreallocarray(c
->prompt_buffer
, size
+ 1,
1061 sizeof *c
->prompt_buffer
);
1062 first
= c
->prompt_buffer
+ off
;
1063 memmove(first
+ strlen(s
), first
, n
* sizeof *c
->prompt_buffer
);
1064 for (idx
= 0; idx
< strlen(s
); idx
++)
1065 utf8_set(&first
[idx
], s
[idx
]);
1066 c
->prompt_index
= (first
- c
->prompt_buffer
) + strlen(s
);
1072 /* Prompt forward to the next beginning of a word. */
1074 status_prompt_forward_word(struct client
*c
, size_t size
, int vi
,
1075 const char *separators
)
1077 size_t idx
= c
->prompt_index
;
1078 int word_is_separators
;
1080 /* In emacs mode, skip until the first non-whitespace character. */
1082 while (idx
!= size
&&
1083 status_prompt_space(&c
->prompt_buffer
[idx
]))
1086 /* Can't move forward if we're already at the end. */
1088 c
->prompt_index
= idx
;
1092 /* Determine the current character class (separators or not). */
1093 word_is_separators
= status_prompt_in_list(separators
,
1094 &c
->prompt_buffer
[idx
]) &&
1095 !status_prompt_space(&c
->prompt_buffer
[idx
]);
1097 /* Skip ahead until the first space or opposite character class. */
1100 if (status_prompt_space(&c
->prompt_buffer
[idx
])) {
1101 /* In vi mode, go to the start of the next word. */
1103 while (idx
!= size
&&
1104 status_prompt_space(&c
->prompt_buffer
[idx
]))
1108 } while (idx
!= size
&& word_is_separators
== status_prompt_in_list(
1109 separators
, &c
->prompt_buffer
[idx
]));
1111 c
->prompt_index
= idx
;
1114 /* Prompt forward to the next end of a word. */
1116 status_prompt_end_word(struct client
*c
, size_t size
, const char *separators
)
1118 size_t idx
= c
->prompt_index
;
1119 int word_is_separators
;
1121 /* Can't move forward if we're already at the end. */
1125 /* Find the next word. */
1129 c
->prompt_index
= idx
;
1132 } while (status_prompt_space(&c
->prompt_buffer
[idx
]));
1134 /* Determine the character class (separators or not). */
1135 word_is_separators
= status_prompt_in_list(separators
,
1136 &c
->prompt_buffer
[idx
]);
1138 /* Skip ahead until the next space or opposite character class. */
1143 } while (!status_prompt_space(&c
->prompt_buffer
[idx
]) &&
1144 word_is_separators
== status_prompt_in_list(separators
,
1145 &c
->prompt_buffer
[idx
]));
1147 /* Back up to the previous character to stop at the end of the word. */
1148 c
->prompt_index
= idx
- 1;
1151 /* Prompt backward to the previous beginning of a word. */
1153 status_prompt_backward_word(struct client
*c
, const char *separators
)
1155 size_t idx
= c
->prompt_index
;
1156 int word_is_separators
;
1158 /* Find non-whitespace. */
1161 if (!status_prompt_space(&c
->prompt_buffer
[idx
]))
1164 word_is_separators
= status_prompt_in_list(separators
,
1165 &c
->prompt_buffer
[idx
]);
1167 /* Find the character before the beginning of the word. */
1170 if (status_prompt_space(&c
->prompt_buffer
[idx
]) ||
1171 word_is_separators
!= status_prompt_in_list(separators
,
1172 &c
->prompt_buffer
[idx
])) {
1173 /* Go back to the word. */
1178 c
->prompt_index
= idx
;
1181 /* Handle keys in prompt. */
1183 status_prompt_key(struct client
*c
, key_code key
)
1185 struct options
*oo
= c
->session
->options
;
1186 char *s
, *cp
, prefix
= '=';
1187 const char *histstr
, *separators
= NULL
, *keystring
;
1189 struct utf8_data tmp
;
1190 int keys
, word_is_separators
;
1192 if (c
->prompt_flags
& PROMPT_KEY
) {
1193 keystring
= key_string_lookup_key(key
, 0);
1194 c
->prompt_inputcb(c
, c
->prompt_data
, keystring
, 1);
1195 status_prompt_clear(c
);
1198 size
= utf8_strlen(c
->prompt_buffer
);
1200 if (c
->prompt_flags
& PROMPT_NUMERIC
) {
1201 if (key
>= '0' && key
<= '9')
1203 s
= utf8_tocstr(c
->prompt_buffer
);
1204 c
->prompt_inputcb(c
, c
->prompt_data
, s
, 1);
1205 status_prompt_clear(c
);
1209 key
&= ~KEYC_MASK_FLAGS
;
1211 keys
= options_get_number(c
->session
->options
, "status-keys");
1212 if (keys
== MODEKEY_VI
) {
1213 switch (status_prompt_translate_key(c
, key
, &key
)) {
1226 case '\002': /* C-b */
1227 if (c
->prompt_index
> 0) {
1233 case '\006': /* C-f */
1234 if (c
->prompt_index
< size
) {
1240 case '\001': /* C-a */
1241 if (c
->prompt_index
!= 0) {
1242 c
->prompt_index
= 0;
1247 case '\005': /* C-e */
1248 if (c
->prompt_index
!= size
) {
1249 c
->prompt_index
= size
;
1253 case '\011': /* Tab */
1254 if (status_prompt_replace_complete(c
, NULL
))
1258 case '\010': /* C-h */
1259 if (c
->prompt_index
!= 0) {
1260 if (c
->prompt_index
== size
)
1261 c
->prompt_buffer
[--c
->prompt_index
].size
= 0;
1263 memmove(c
->prompt_buffer
+ c
->prompt_index
- 1,
1264 c
->prompt_buffer
+ c
->prompt_index
,
1265 (size
+ 1 - c
->prompt_index
) *
1266 sizeof *c
->prompt_buffer
);
1273 case '\004': /* C-d */
1274 if (c
->prompt_index
!= size
) {
1275 memmove(c
->prompt_buffer
+ c
->prompt_index
,
1276 c
->prompt_buffer
+ c
->prompt_index
+ 1,
1277 (size
+ 1 - c
->prompt_index
) *
1278 sizeof *c
->prompt_buffer
);
1282 case '\025': /* C-u */
1283 c
->prompt_buffer
[0].size
= 0;
1284 c
->prompt_index
= 0;
1286 case '\013': /* C-k */
1287 if (c
->prompt_index
< size
) {
1288 c
->prompt_buffer
[c
->prompt_index
].size
= 0;
1292 case '\027': /* C-w */
1293 separators
= options_get_string(oo
, "word-separators");
1294 idx
= c
->prompt_index
;
1296 /* Find non-whitespace. */
1299 if (!status_prompt_space(&c
->prompt_buffer
[idx
]))
1302 word_is_separators
= status_prompt_in_list(separators
,
1303 &c
->prompt_buffer
[idx
]);
1305 /* Find the character before the beginning of the word. */
1308 if (status_prompt_space(&c
->prompt_buffer
[idx
]) ||
1309 word_is_separators
!= status_prompt_in_list(
1310 separators
, &c
->prompt_buffer
[idx
])) {
1311 /* Go back to the word. */
1317 free(c
->prompt_saved
);
1318 c
->prompt_saved
= xcalloc(sizeof *c
->prompt_buffer
,
1319 (c
->prompt_index
- idx
) + 1);
1320 memcpy(c
->prompt_saved
, c
->prompt_buffer
+ idx
,
1321 (c
->prompt_index
- idx
) * sizeof *c
->prompt_buffer
);
1323 memmove(c
->prompt_buffer
+ idx
,
1324 c
->prompt_buffer
+ c
->prompt_index
,
1325 (size
+ 1 - c
->prompt_index
) *
1326 sizeof *c
->prompt_buffer
);
1327 memset(c
->prompt_buffer
+ size
- (c
->prompt_index
- idx
),
1328 '\0', (c
->prompt_index
- idx
) * sizeof *c
->prompt_buffer
);
1329 c
->prompt_index
= idx
;
1332 case KEYC_RIGHT
|KEYC_CTRL
:
1334 separators
= options_get_string(oo
, "word-separators");
1335 status_prompt_forward_word(c
, size
, 0, separators
);
1338 status_prompt_end_word(c
, size
, "");
1341 separators
= options_get_string(oo
, "word-separators");
1342 status_prompt_end_word(c
, size
, separators
);
1345 status_prompt_forward_word(c
, size
, 1, "");
1348 separators
= options_get_string(oo
, "word-separators");
1349 status_prompt_forward_word(c
, size
, 1, separators
);
1352 status_prompt_backward_word(c
, "");
1354 case KEYC_LEFT
|KEYC_CTRL
:
1356 separators
= options_get_string(oo
, "word-separators");
1357 status_prompt_backward_word(c
, separators
);
1360 case '\020': /* C-p */
1361 histstr
= status_prompt_up_history(c
->prompt_hindex
,
1363 if (histstr
== NULL
)
1365 free(c
->prompt_buffer
);
1366 c
->prompt_buffer
= utf8_fromcstr(histstr
);
1367 c
->prompt_index
= utf8_strlen(c
->prompt_buffer
);
1370 case '\016': /* C-n */
1371 histstr
= status_prompt_down_history(c
->prompt_hindex
,
1373 if (histstr
== NULL
)
1375 free(c
->prompt_buffer
);
1376 c
->prompt_buffer
= utf8_fromcstr(histstr
);
1377 c
->prompt_index
= utf8_strlen(c
->prompt_buffer
);
1379 case '\031': /* C-y */
1380 if (status_prompt_paste(c
))
1383 case '\024': /* C-t */
1384 idx
= c
->prompt_index
;
1388 utf8_copy(&tmp
, &c
->prompt_buffer
[idx
- 2]);
1389 utf8_copy(&c
->prompt_buffer
[idx
- 2],
1390 &c
->prompt_buffer
[idx
- 1]);
1391 utf8_copy(&c
->prompt_buffer
[idx
- 1], &tmp
);
1392 c
->prompt_index
= idx
;
1398 s
= utf8_tocstr(c
->prompt_buffer
);
1400 status_prompt_add_history(s
, c
->prompt_type
);
1401 if (c
->prompt_inputcb(c
, c
->prompt_data
, s
, 1) == 0)
1402 status_prompt_clear(c
);
1405 case '\033': /* Escape */
1406 case '\003': /* C-c */
1407 case '\007': /* C-g */
1408 if (c
->prompt_inputcb(c
, c
->prompt_data
, NULL
, 1) == 0)
1409 status_prompt_clear(c
);
1411 case '\022': /* C-r */
1412 if (~c
->prompt_flags
& PROMPT_INCREMENTAL
)
1414 if (c
->prompt_buffer
[0].size
== 0) {
1416 free(c
->prompt_buffer
);
1417 c
->prompt_buffer
= utf8_fromcstr(c
->prompt_last
);
1418 c
->prompt_index
= utf8_strlen(c
->prompt_buffer
);
1422 case '\023': /* C-s */
1423 if (~c
->prompt_flags
& PROMPT_INCREMENTAL
)
1425 if (c
->prompt_buffer
[0].size
== 0) {
1427 free(c
->prompt_buffer
);
1428 c
->prompt_buffer
= utf8_fromcstr(c
->prompt_last
);
1429 c
->prompt_index
= utf8_strlen(c
->prompt_buffer
);
1437 c
->flags
|= CLIENT_REDRAWSTATUS
;
1441 if (key
<= 0x1f || (key
>= KEYC_BASE
&& key
< KEYC_BASE_END
))
1444 utf8_set(&tmp
, key
);
1445 else if (KEYC_IS_UNICODE(key
))
1446 utf8_to_data(key
, &tmp
);
1450 c
->prompt_buffer
= xreallocarray(c
->prompt_buffer
, size
+ 2,
1451 sizeof *c
->prompt_buffer
);
1453 if (c
->prompt_index
== size
) {
1454 utf8_copy(&c
->prompt_buffer
[c
->prompt_index
], &tmp
);
1456 c
->prompt_buffer
[c
->prompt_index
].size
= 0;
1458 memmove(c
->prompt_buffer
+ c
->prompt_index
+ 1,
1459 c
->prompt_buffer
+ c
->prompt_index
,
1460 (size
+ 1 - c
->prompt_index
) *
1461 sizeof *c
->prompt_buffer
);
1462 utf8_copy(&c
->prompt_buffer
[c
->prompt_index
], &tmp
);
1466 if (c
->prompt_flags
& PROMPT_SINGLE
) {
1467 if (utf8_strlen(c
->prompt_buffer
) != 1)
1468 status_prompt_clear(c
);
1470 s
= utf8_tocstr(c
->prompt_buffer
);
1471 if (c
->prompt_inputcb(c
, c
->prompt_data
, s
, 1) == 0)
1472 status_prompt_clear(c
);
1478 c
->flags
|= CLIENT_REDRAWSTATUS
;
1479 if (c
->prompt_flags
& PROMPT_INCREMENTAL
) {
1480 s
= utf8_tocstr(c
->prompt_buffer
);
1481 xasprintf(&cp
, "%c%s", prefix
, s
);
1482 c
->prompt_inputcb(c
, c
->prompt_data
, cp
, 0);
1489 /* Get previous line from the history. */
1491 status_prompt_up_history(u_int
*idx
, u_int type
)
1494 * History runs from 0 to size - 1. Index is from 0 to size. Zero is
1498 if (status_prompt_hsize
[type
] == 0 ||
1499 idx
[type
] == status_prompt_hsize
[type
])
1502 return (status_prompt_hlist
[type
][status_prompt_hsize
[type
] - idx
[type
]]);
1505 /* Get next line from the history. */
1507 status_prompt_down_history(u_int
*idx
, u_int type
)
1509 if (status_prompt_hsize
[type
] == 0 || idx
[type
] == 0)
1514 return (status_prompt_hlist
[type
][status_prompt_hsize
[type
] - idx
[type
]]);
1517 /* Add line to the history. */
1519 status_prompt_add_history(const char *line
, u_int type
)
1521 u_int i
, oldsize
, newsize
, freecount
, hlimit
, new = 1;
1524 oldsize
= status_prompt_hsize
[type
];
1526 strcmp(status_prompt_hlist
[type
][oldsize
- 1], line
) == 0)
1529 hlimit
= options_get_number(global_options
, "prompt-history-limit");
1530 if (hlimit
> oldsize
) {
1533 newsize
= oldsize
+ new;
1536 freecount
= oldsize
+ new - newsize
;
1537 if (freecount
> oldsize
)
1538 freecount
= oldsize
;
1541 for (i
= 0; i
< freecount
; i
++)
1542 free(status_prompt_hlist
[type
][i
]);
1543 movesize
= (oldsize
- freecount
) *
1544 sizeof *status_prompt_hlist
[type
];
1546 memmove(&status_prompt_hlist
[type
][0],
1547 &status_prompt_hlist
[type
][freecount
], movesize
);
1552 free(status_prompt_hlist
[type
]);
1553 status_prompt_hlist
[type
] = NULL
;
1554 } else if (newsize
!= oldsize
) {
1555 status_prompt_hlist
[type
] =
1556 xreallocarray(status_prompt_hlist
[type
], newsize
,
1557 sizeof *status_prompt_hlist
[type
]);
1560 if (new == 1 && newsize
> 0)
1561 status_prompt_hlist
[type
][newsize
- 1] = xstrdup(line
);
1562 status_prompt_hsize
[type
] = newsize
;
1565 /* Build completion list. */
1567 status_prompt_complete_list(u_int
*size
, const char *s
, int at_start
)
1570 const char **layout
, *value
, *cp
;
1571 const struct cmd_entry
**cmdent
;
1572 const struct options_table_entry
*oe
;
1573 size_t slen
= strlen(s
), valuelen
;
1574 struct options_entry
*o
;
1575 struct options_array_item
*a
;
1576 const char *layouts
[] = {
1577 "even-horizontal", "even-vertical", "main-horizontal",
1578 "main-vertical", "tiled", NULL
1582 for (cmdent
= cmd_table
; *cmdent
!= NULL
; cmdent
++) {
1583 if (strncmp((*cmdent
)->name
, s
, slen
) == 0) {
1584 list
= xreallocarray(list
, (*size
) + 1, sizeof *list
);
1585 list
[(*size
)++] = xstrdup((*cmdent
)->name
);
1587 if ((*cmdent
)->alias
!= NULL
&&
1588 strncmp((*cmdent
)->alias
, s
, slen
) == 0) {
1589 list
= xreallocarray(list
, (*size
) + 1, sizeof *list
);
1590 list
[(*size
)++] = xstrdup((*cmdent
)->alias
);
1593 o
= options_get_only(global_options
, "command-alias");
1595 a
= options_array_first(o
);
1597 value
= options_array_item_value(a
)->string
;
1598 if ((cp
= strchr(value
, '=')) == NULL
)
1600 valuelen
= cp
- value
;
1601 if (slen
> valuelen
|| strncmp(value
, s
, slen
) != 0)
1604 list
= xreallocarray(list
, (*size
) + 1, sizeof *list
);
1605 list
[(*size
)++] = xstrndup(value
, valuelen
);
1608 a
= options_array_next(a
);
1614 for (oe
= options_table
; oe
->name
!= NULL
; oe
++) {
1615 if (strncmp(oe
->name
, s
, slen
) == 0) {
1616 list
= xreallocarray(list
, (*size
) + 1, sizeof *list
);
1617 list
[(*size
)++] = xstrdup(oe
->name
);
1620 for (layout
= layouts
; *layout
!= NULL
; layout
++) {
1621 if (strncmp(*layout
, s
, slen
) == 0) {
1622 list
= xreallocarray(list
, (*size
) + 1, sizeof *list
);
1623 list
[(*size
)++] = xstrdup(*layout
);
1629 /* Find longest prefix. */
1631 status_prompt_complete_prefix(char **list
, u_int size
)
1637 if (list
== NULL
|| size
== 0)
1639 out
= xstrdup(list
[0]);
1640 for (i
= 1; i
< size
; i
++) {
1641 j
= strlen(list
[i
]);
1642 if (j
> strlen(out
))
1644 for (; j
> 0; j
--) {
1645 if (out
[j
- 1] != list
[i
][j
- 1])
1652 /* Complete word menu callback. */
1654 status_prompt_menu_callback(__unused
struct menu
*menu
, u_int idx
, key_code key
,
1657 struct status_prompt_menu
*spm
= data
;
1658 struct client
*c
= spm
->c
;
1662 if (key
!= KEYC_NONE
) {
1664 if (spm
->flag
== '\0')
1665 s
= xstrdup(spm
->list
[idx
]);
1667 xasprintf(&s
, "-%c%s", spm
->flag
, spm
->list
[idx
]);
1668 if (c
->prompt_type
== PROMPT_TYPE_WINDOW_TARGET
) {
1669 free(c
->prompt_buffer
);
1670 c
->prompt_buffer
= utf8_fromcstr(s
);
1671 c
->prompt_index
= utf8_strlen(c
->prompt_buffer
);
1672 c
->flags
|= CLIENT_REDRAWSTATUS
;
1673 } else if (status_prompt_replace_complete(c
, s
))
1674 c
->flags
|= CLIENT_REDRAWSTATUS
;
1678 for (i
= 0; i
< spm
->size
; i
++)
1683 /* Show complete word menu. */
1685 status_prompt_complete_list_menu(struct client
*c
, char **list
, u_int size
,
1686 u_int offset
, char flag
)
1689 struct menu_item item
;
1690 struct status_prompt_menu
*spm
;
1691 u_int lines
= status_line_size(c
), height
, i
;
1696 if (c
->tty
.sy
- lines
< 3)
1699 spm
= xmalloc(sizeof *spm
);
1705 height
= c
->tty
.sy
- lines
- 2;
1710 spm
->start
= size
- height
;
1712 menu
= menu_create("");
1713 for (i
= spm
->start
; i
< size
; i
++) {
1714 item
.name
= list
[i
];
1715 item
.key
= '0' + (i
- spm
->start
);
1716 item
.command
= NULL
;
1717 menu_add_item(menu
, &item
, NULL
, NULL
, NULL
);
1720 if (options_get_number(c
->session
->options
, "status-position") == 0)
1723 py
= c
->tty
.sy
- 3 - height
;
1724 offset
+= utf8_cstrwidth(c
->prompt_string
);
1730 if (menu_display(menu
, MENU_NOMOUSE
|MENU_TAB
, NULL
, offset
,
1731 py
, c
, NULL
, status_prompt_menu_callback
, spm
) != 0) {
1739 /* Show complete word menu. */
1741 status_prompt_complete_window_menu(struct client
*c
, struct session
*s
,
1742 const char *word
, u_int offset
, char flag
)
1745 struct menu_item item
;
1746 struct status_prompt_menu
*spm
;
1748 char **list
= NULL
, *tmp
;
1749 u_int lines
= status_line_size(c
), height
;
1752 if (c
->tty
.sy
- lines
< 3)
1755 spm
= xmalloc(sizeof *spm
);
1759 height
= c
->tty
.sy
- lines
- 2;
1764 menu
= menu_create("");
1765 RB_FOREACH(wl
, winlinks
, &s
->windows
) {
1766 if (word
!= NULL
&& *word
!= '\0') {
1767 xasprintf(&tmp
, "%d", wl
->idx
);
1768 if (strncmp(tmp
, word
, strlen(word
)) != 0) {
1775 list
= xreallocarray(list
, size
+ 1, sizeof *list
);
1776 if (c
->prompt_type
== PROMPT_TYPE_WINDOW_TARGET
) {
1777 xasprintf(&tmp
, "%d (%s)", wl
->idx
, wl
->window
->name
);
1778 xasprintf(&list
[size
++], "%d", wl
->idx
);
1780 xasprintf(&tmp
, "%s:%d (%s)", s
->name
, wl
->idx
,
1782 xasprintf(&list
[size
++], "%s:%d", s
->name
, wl
->idx
);
1785 item
.key
= '0' + size
- 1;
1786 item
.command
= NULL
;
1787 menu_add_item(menu
, &item
, NULL
, NULL
, NULL
);
1800 xasprintf(&tmp
, "-%c%s", flag
, list
[0]);
1813 if (options_get_number(c
->session
->options
, "status-position") == 0)
1816 py
= c
->tty
.sy
- 3 - height
;
1817 offset
+= utf8_cstrwidth(c
->prompt_string
);
1823 if (menu_display(menu
, MENU_NOMOUSE
|MENU_TAB
, NULL
, offset
,
1824 py
, c
, NULL
, status_prompt_menu_callback
, spm
) != 0) {
1832 /* Sort complete list. */
1834 status_prompt_complete_sort(const void *a
, const void *b
)
1836 const char **aa
= (const char **)a
, **bb
= (const char **)b
;
1838 return (strcmp(*aa
, *bb
));
1841 /* Complete a session. */
1843 status_prompt_complete_session(char ***list
, u_int
*size
, const char *s
,
1846 struct session
*loop
;
1847 char *out
, *tmp
, n
[11];
1849 RB_FOREACH(loop
, sessions
, &sessions
) {
1850 if (*s
== '\0' || strncmp(loop
->name
, s
, strlen(s
)) == 0) {
1851 *list
= xreallocarray(*list
, (*size
) + 2,
1853 xasprintf(&(*list
)[(*size
)++], "%s:", loop
->name
);
1854 } else if (*s
== '$') {
1855 xsnprintf(n
, sizeof n
, "%u", loop
->id
);
1857 strncmp(n
, s
+ 1, strlen(s
) - 1) == 0) {
1858 *list
= xreallocarray(*list
, (*size
) + 2,
1860 xasprintf(&(*list
)[(*size
)++], "$%s:", n
);
1864 out
= status_prompt_complete_prefix(*list
, *size
);
1865 if (out
!= NULL
&& flag
!= '\0') {
1866 xasprintf(&tmp
, "-%c%s", flag
, out
);
1873 /* Complete word. */
1875 status_prompt_complete(struct client
*c
, const char *word
, u_int offset
)
1877 struct session
*session
;
1878 const char *s
, *colon
;
1879 char **list
= NULL
, *copy
= NULL
, *out
= NULL
;
1883 if (*word
== '\0' &&
1884 c
->prompt_type
!= PROMPT_TYPE_TARGET
&&
1885 c
->prompt_type
!= PROMPT_TYPE_WINDOW_TARGET
)
1888 if (c
->prompt_type
!= PROMPT_TYPE_TARGET
&&
1889 c
->prompt_type
!= PROMPT_TYPE_WINDOW_TARGET
&&
1890 strncmp(word
, "-t", 2) != 0 &&
1891 strncmp(word
, "-s", 2) != 0) {
1892 list
= status_prompt_complete_list(&size
, word
, offset
== 0);
1896 xasprintf(&out
, "%s ", list
[0]);
1898 out
= status_prompt_complete_prefix(list
, size
);
1902 if (c
->prompt_type
== PROMPT_TYPE_TARGET
||
1903 c
->prompt_type
== PROMPT_TYPE_WINDOW_TARGET
) {
1912 /* If this is a window completion, open the window menu. */
1913 if (c
->prompt_type
== PROMPT_TYPE_WINDOW_TARGET
) {
1914 out
= status_prompt_complete_window_menu(c
, c
->session
, s
,
1918 colon
= strchr(s
, ':');
1920 /* If there is no colon, complete as a session. */
1921 if (colon
== NULL
) {
1922 out
= status_prompt_complete_session(&list
, &size
, s
, flag
);
1926 /* If there is a colon but no period, find session and show a menu. */
1927 if (strchr(colon
+ 1, '.') == NULL
) {
1929 session
= c
->session
;
1932 *strchr(copy
, ':') = '\0';
1933 session
= session_find(copy
);
1935 if (session
== NULL
)
1938 out
= status_prompt_complete_window_menu(c
, session
, colon
+ 1,
1946 qsort(list
, size
, sizeof *list
, status_prompt_complete_sort
);
1947 for (i
= 0; i
< size
; i
++)
1948 log_debug("complete %u: %s", i
, list
[i
]);
1951 if (out
!= NULL
&& strcmp(word
, out
) == 0) {
1956 !status_prompt_complete_list_menu(c
, list
, size
, offset
, flag
)) {
1957 for (i
= 0; i
< size
; i
++)
1964 /* Return the type of the prompt as an enum. */
1966 status_prompt_type(const char *type
)
1970 for (i
= 0; i
< PROMPT_NTYPES
; i
++) {
1971 if (strcmp(type
, status_prompt_type_string(i
)) == 0)
1974 return (PROMPT_TYPE_INVALID
);
1977 /* Accessor for prompt_type_strings. */
1979 status_prompt_type_string(u_int type
)
1981 if (type
>= PROMPT_NTYPES
)
1983 return (prompt_type_strings
[type
]);