1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375 |
- /* $OpenBSD$ */
- /*
- * Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>
- *
- * Permission to use, copy, modify, and distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER
- * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
- * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
- #include <sys/types.h>
- #include <sys/time.h>
- #include <errno.h>
- #include <limits.h>
- #include <stdarg.h>
- #include <stdlib.h>
- #include <string.h>
- #include <time.h>
- #include <unistd.h>
- #include "tmux.h"
- #include "tmate.h"
- char *status_redraw_get_left(struct client *, time_t, struct grid_cell *,
- size_t *);
- char *status_redraw_get_right(struct client *, time_t, struct grid_cell *,
- size_t *);
- char *status_print(struct client *, struct winlink *, time_t,
- struct grid_cell *);
- char *status_replace(struct client *, struct winlink *, const char *, time_t);
- void status_message_callback(int, short, void *);
- void status_timer_callback(int, short, void *);
- const char *status_prompt_up_history(u_int *);
- const char *status_prompt_down_history(u_int *);
- void status_prompt_add_history(const char *);
- const char **status_prompt_complete_list(u_int *, const char *);
- char *status_prompt_complete_prefix(const char **, u_int);
- char *status_prompt_complete(struct session *, const char *);
- char *status_prompt_find_history_file(void);
- /* Status prompt history. */
- #define PROMPT_HISTORY 100
- char **status_prompt_hlist;
- u_int status_prompt_hsize;
- /* Find the history file to load/save from/to. */
- char *
- status_prompt_find_history_file(void)
- {
- const char *home, *history_file;
- char *path;
- history_file = options_get_string(global_options, "history-file");
- if (*history_file == '\0')
- return (NULL);
- if (*history_file == '/')
- return (xstrdup(history_file));
- if (history_file[0] != '~' || history_file[1] != '/')
- return (NULL);
- if ((home = find_home()) == NULL)
- return (NULL);
- xasprintf(&path, "%s%s", home, history_file + 1);
- return (path);
- }
- /* Load status prompt history from file. */
- void
- status_prompt_load_history(void)
- {
- FILE *f;
- char *history_file, *line, *tmp;
- size_t length;
- if ((history_file = status_prompt_find_history_file()) == NULL)
- return;
- log_debug("loading history from %s", history_file);
- f = fopen(history_file, "r");
- if (f == NULL) {
- log_debug("%s: %s", history_file, strerror(errno));
- free(history_file);
- return;
- }
- free(history_file);
- for (;;) {
- if ((line = fgetln(f, &length)) == NULL)
- break;
- if (length > 0) {
- if (line[length - 1] == '\n') {
- line[length - 1] = '\0';
- status_prompt_add_history(line);
- } else {
- tmp = xmalloc(length + 1);
- memcpy(tmp, line, length);
- tmp[length] = '\0';
- status_prompt_add_history(tmp);
- free(tmp);
- }
- }
- }
- fclose(f);
- }
- /* Save status prompt history to file. */
- void
- status_prompt_save_history(void)
- {
- FILE *f;
- u_int i;
- char *history_file;
- if ((history_file = status_prompt_find_history_file()) == NULL)
- return;
- log_debug("saving history to %s", history_file);
- f = fopen(history_file, "w");
- if (f == NULL) {
- log_debug("%s: %s", history_file, strerror(errno));
- free(history_file);
- return;
- }
- free(history_file);
- for (i = 0; i < status_prompt_hsize; i++) {
- fputs(status_prompt_hlist[i], f);
- fputc('\n', f);
- }
- fclose(f);
- }
- /* Status timer callback. */
- void
- status_timer_callback(__unused int fd, __unused short events, void *arg)
- {
- struct client *c = arg;
- struct session *s = c->session;
- struct timeval tv;
- evtimer_del(&c->status_timer);
- if (s == NULL)
- return;
- if (c->message_string == NULL && c->prompt_string == NULL)
- c->flags |= CLIENT_STATUS;
- timerclear(&tv);
- tv.tv_sec = options_get_number(s->options, "status-interval");
- if (tv.tv_sec != 0)
- evtimer_add(&c->status_timer, &tv);
- log_debug("client %p, status interval %d", c, (int)tv.tv_sec);
- }
- /* Start status timer for client. */
- void
- status_timer_start(struct client *c)
- {
- struct session *s = c->session;
- if (event_initialized(&c->status_timer))
- evtimer_del(&c->status_timer);
- else
- evtimer_set(&c->status_timer, status_timer_callback, c);
- if (s != NULL && options_get_number(s->options, "status"))
- status_timer_callback(-1, 0, c);
- }
- /* Start status timer for all clients. */
- void
- status_timer_start_all(void)
- {
- struct client *c;
- TAILQ_FOREACH(c, &clients, entry)
- status_timer_start(c);
- }
- /* Get screen line of status line. -1 means off. */
- int
- status_at_line(struct client *c)
- {
- struct session *s = c->session;
- if (!options_get_number(s->options, "status"))
- return (-1);
- if (options_get_number(s->options, "status-position") == 0)
- return (0);
- return (c->tty.sy - 1);
- }
- /* Retrieve options for left string. */
- char *
- status_redraw_get_left(struct client *c, time_t t, struct grid_cell *gc,
- size_t *size)
- {
- struct session *s = c->session;
- const char *template;
- char *left;
- size_t leftlen;
- style_apply_update(gc, s->options, "status-left-style");
- template = options_get_string(s->options, "status-left");
- left = status_replace(c, NULL, template, t);
- *size = options_get_number(s->options, "status-left-length");
- leftlen = screen_write_cstrlen("%s", left);
- if (leftlen < *size)
- *size = leftlen;
- return (left);
- }
- /* Retrieve options for right string. */
- char *
- status_redraw_get_right(struct client *c, time_t t, struct grid_cell *gc,
- size_t *size)
- {
- struct session *s = c->session;
- const char *template;
- char *right;
- size_t rightlen;
- style_apply_update(gc, s->options, "status-right-style");
- template = options_get_string(s->options, "status-right");
- right = status_replace(c, NULL, template, t);
- *size = options_get_number(s->options, "status-right-length");
- rightlen = screen_write_cstrlen("%s", right);
- if (rightlen < *size)
- *size = rightlen;
- return (right);
- }
- /* Get window at window list position. */
- struct window *
- status_get_window_at(struct client *c, u_int x)
- {
- struct session *s = c->session;
- struct winlink *wl;
- struct options *oo;
- size_t len;
- x += c->wlmouse;
- RB_FOREACH(wl, winlinks, &s->windows) {
- oo = wl->window->options;
- len = strlen(options_get_string(oo, "window-status-separator"));
- if (x < wl->status_width)
- return (wl->window);
- x -= wl->status_width + len;
- }
- return (NULL);
- }
- /* Draw status for client on the last lines of given context. */
- int
- status_redraw(struct client *c)
- {
- struct screen_write_ctx ctx;
- struct session *s = c->session;
- struct winlink *wl;
- struct screen old_status, window_list;
- struct grid_cell stdgc, lgc, rgc, gc;
- struct options *oo;
- time_t t;
- char *left, *right, *sep;
- u_int offset, needed;
- u_int wlstart, wlwidth, wlavailable, wloffset, wlsize;
- size_t llen, rlen, seplen;
- int larrow, rarrow;
- /* No status line? */
- if (c->tty.sy == 0 || !options_get_number(s->options, "status"))
- #ifdef TMATE
- if (c->tty.sy == 0 || !(c->flags & CLIENT_FORCE_STATUS))
- #endif
- return (1);
- left = right = NULL;
- larrow = rarrow = 0;
- /* Store current time. */
- t = time(NULL);
- /* Set up default colour. */
- style_apply(&stdgc, s->options, "status-style");
- /* Create the target screen. */
- memcpy(&old_status, &c->status, sizeof old_status);
- screen_init(&c->status, c->tty.sx, 1, 0);
- screen_write_start(&ctx, NULL, &c->status);
- for (offset = 0; offset < c->tty.sx; offset++)
- screen_write_putc(&ctx, &stdgc, ' ');
- screen_write_stop(&ctx);
- /* If the height is one line, blank status line. */
- if (c->tty.sy <= 1)
- goto out;
- /* Work out left and right strings. */
- memcpy(&lgc, &stdgc, sizeof lgc);
- left = status_redraw_get_left(c, t, &lgc, &llen);
- memcpy(&rgc, &stdgc, sizeof rgc);
- right = status_redraw_get_right(c, t, &rgc, &rlen);
- #ifdef TMATE
- tmate_status(left, right);
- #endif
- /*
- * Figure out how much space we have for the window list. If there
- * isn't enough space, just show a blank status line.
- */
- needed = 0;
- if (llen != 0)
- needed += llen;
- if (rlen != 0)
- needed += rlen;
- if (c->tty.sx == 0 || c->tty.sx <= needed)
- goto out;
- wlavailable = c->tty.sx - needed;
- /* Calculate the total size needed for the window list. */
- wlstart = wloffset = wlwidth = 0;
- RB_FOREACH(wl, winlinks, &s->windows) {
- free(wl->status_text);
- memcpy(&wl->status_cell, &stdgc, sizeof wl->status_cell);
- wl->status_text = status_print(c, wl, t, &wl->status_cell);
- wl->status_width = screen_write_cstrlen("%s", wl->status_text);
- if (wl == s->curw)
- wloffset = wlwidth;
- oo = wl->window->options;
- sep = options_get_string(oo, "window-status-separator");
- seplen = screen_write_strlen("%s", sep);
- wlwidth += wl->status_width + seplen;
- }
- /* Create a new screen for the window list. */
- screen_init(&window_list, wlwidth, 1, 0);
- /* And draw the window list into it. */
- screen_write_start(&ctx, NULL, &window_list);
- RB_FOREACH(wl, winlinks, &s->windows) {
- screen_write_cnputs(&ctx, -1, &wl->status_cell, "%s",
- wl->status_text);
- oo = wl->window->options;
- sep = options_get_string(oo, "window-status-separator");
- screen_write_nputs(&ctx, -1, &stdgc, "%s", sep);
- }
- screen_write_stop(&ctx);
- /* If there is enough space for the total width, skip to draw now. */
- if (wlwidth <= wlavailable)
- goto draw;
- /* Find size of current window text. */
- wlsize = s->curw->status_width;
- /*
- * If the current window is already on screen, good to draw from the
- * start and just leave off the end.
- */
- if (wloffset + wlsize < wlavailable) {
- if (wlavailable > 0) {
- rarrow = 1;
- wlavailable--;
- }
- wlwidth = wlavailable;
- } else {
- /*
- * Work out how many characters we need to omit from the
- * start. There are wlavailable characters to fill, and
- * wloffset + wlsize must be the last. So, the start character
- * is wloffset + wlsize - wlavailable.
- */
- if (wlavailable > 0) {
- larrow = 1;
- wlavailable--;
- }
- wlstart = wloffset + wlsize - wlavailable;
- if (wlavailable > 0 && wlwidth > wlstart + wlavailable + 1) {
- rarrow = 1;
- wlstart++;
- wlavailable--;
- }
- wlwidth = wlavailable;
- }
- /* Bail if anything is now too small too. */
- if (wlwidth == 0 || wlavailable == 0) {
- screen_free(&window_list);
- goto out;
- }
- /*
- * Now the start position is known, work out the state of the left and
- * right arrows.
- */
- offset = 0;
- RB_FOREACH(wl, winlinks, &s->windows) {
- if (wl->flags & WINLINK_ALERTFLAGS &&
- larrow == 1 && offset < wlstart)
- larrow = -1;
- offset += wl->status_width;
- if (wl->flags & WINLINK_ALERTFLAGS &&
- rarrow == 1 && offset > wlstart + wlwidth)
- rarrow = -1;
- }
- draw:
- /* Begin drawing. */
- screen_write_start(&ctx, NULL, &c->status);
- /* Draw the left string and arrow. */
- screen_write_cursormove(&ctx, 0, 0);
- if (llen != 0)
- screen_write_cnputs(&ctx, llen, &lgc, "%s", left);
- if (larrow != 0) {
- memcpy(&gc, &stdgc, sizeof gc);
- if (larrow == -1)
- gc.attr ^= GRID_ATTR_REVERSE;
- screen_write_putc(&ctx, &gc, '<');
- }
- /* Draw the right string and arrow. */
- if (rarrow != 0) {
- screen_write_cursormove(&ctx, c->tty.sx - rlen - 1, 0);
- memcpy(&gc, &stdgc, sizeof gc);
- if (rarrow == -1)
- gc.attr ^= GRID_ATTR_REVERSE;
- screen_write_putc(&ctx, &gc, '>');
- } else
- screen_write_cursormove(&ctx, c->tty.sx - rlen, 0);
- if (rlen != 0)
- screen_write_cnputs(&ctx, rlen, &rgc, "%s", right);
- /* Figure out the offset for the window list. */
- if (llen != 0)
- wloffset = llen;
- else
- wloffset = 0;
- if (wlwidth < wlavailable) {
- switch (options_get_number(s->options, "status-justify")) {
- case 1: /* centred */
- wloffset += (wlavailable - wlwidth) / 2;
- break;
- case 2: /* right */
- wloffset += (wlavailable - wlwidth);
- break;
- }
- }
- if (larrow != 0)
- wloffset++;
- /* Copy the window list. */
- c->wlmouse = -wloffset + wlstart;
- screen_write_cursormove(&ctx, wloffset, 0);
- screen_write_copy(&ctx, &window_list, wlstart, 0, wlwidth, 1);
- screen_free(&window_list);
- screen_write_stop(&ctx);
- out:
- free(left);
- free(right);
- if (grid_compare(c->status.grid, old_status.grid) == 0) {
- screen_free(&old_status);
- return (0);
- }
- screen_free(&old_status);
- return (1);
- }
- /* Replace special sequences in fmt. */
- char *
- status_replace(struct client *c, struct winlink *wl, const char *fmt, time_t t)
- {
- struct format_tree *ft;
- char *expanded;
- if (fmt == NULL)
- return (xstrdup(""));
- if (c->flags & CLIENT_STATUSFORCE)
- ft = format_create(NULL, FORMAT_STATUS|FORMAT_FORCE);
- else
- ft = format_create(NULL, FORMAT_STATUS);
- format_defaults(ft, c, NULL, wl, NULL);
- expanded = format_expand_time(ft, fmt, t);
- format_free(ft);
- return (expanded);
- }
- /* Return winlink status line entry and adjust gc as necessary. */
- char *
- status_print(struct client *c, struct winlink *wl, time_t t,
- struct grid_cell *gc)
- {
- struct options *oo = wl->window->options;
- struct session *s = c->session;
- const char *fmt;
- char *text;
- style_apply_update(gc, oo, "window-status-style");
- fmt = options_get_string(oo, "window-status-format");
- if (wl == s->curw) {
- style_apply_update(gc, oo, "window-status-current-style");
- fmt = options_get_string(oo, "window-status-current-format");
- }
- if (wl == TAILQ_FIRST(&s->lastw))
- style_apply_update(gc, oo, "window-status-last-style");
- if (wl->flags & WINLINK_BELL)
- style_apply_update(gc, oo, "window-status-bell-style");
- else if (wl->flags & (WINLINK_ACTIVITY|WINLINK_SILENCE))
- style_apply_update(gc, oo, "window-status-activity-style");
- text = status_replace(c, wl, fmt, t);
- return (text);
- }
- /* Set a status line message. */
- void
- status_message_set(struct client *c, const char *fmt, ...)
- {
- struct timeval tv;
- struct message_entry *msg, *msg1;
- va_list ap;
- int delay;
- u_int limit;
- limit = options_get_number(global_options, "message-limit");
- status_prompt_clear(c);
- status_message_clear(c);
- va_start(ap, fmt);
- xvasprintf(&c->message_string, fmt, ap);
- va_end(ap);
- msg = xcalloc(1, sizeof *msg);
- msg->msg_time = time(NULL);
- msg->msg_num = c->message_next++;
- msg->msg = xstrdup(c->message_string);
- TAILQ_INSERT_TAIL(&c->message_log, msg, entry);
- TAILQ_FOREACH_SAFE(msg, &c->message_log, entry, msg1) {
- if (msg->msg_num + limit >= c->message_next)
- break;
- free(msg->msg);
- TAILQ_REMOVE(&c->message_log, msg, entry);
- free(msg);
- }
- delay = options_get_number(c->session->options, "display-time");
- if (delay > 0) {
- tv.tv_sec = delay / 1000;
- tv.tv_usec = (delay % 1000) * 1000L;
- if (event_initialized(&c->message_timer))
- evtimer_del(&c->message_timer);
- evtimer_set(&c->message_timer, status_message_callback, c);
- evtimer_add(&c->message_timer, &tv);
- }
- c->tty.flags |= (TTY_NOCURSOR|TTY_FREEZE);
- c->flags |= CLIENT_STATUS;
- }
- /* Clear status line message. */
- void
- status_message_clear(struct client *c)
- {
- if (c->message_string == NULL)
- return;
- free(c->message_string);
- c->message_string = NULL;
- c->tty.flags &= ~(TTY_NOCURSOR|TTY_FREEZE);
- #ifdef TMATE
- if (c->flags & CLIENT_FORCE_STATUS) {
- c->flags &= ~CLIENT_FORCE_STATUS;
- recalculate_sizes();
- }
- #endif
- c->flags |= CLIENT_REDRAW; /* screen was frozen and may have changed */
- screen_reinit(&c->status);
- }
- /* Clear status line message after timer expires. */
- void
- status_message_callback(__unused int fd, __unused short event, void *data)
- {
- struct client *c = data;
- status_message_clear(c);
- }
- /* Draw client message on status line of present else on last line. */
- int
- status_message_redraw(struct client *c)
- {
- struct screen_write_ctx ctx;
- struct session *s = c->session;
- struct screen old_status;
- size_t len;
- struct grid_cell gc;
- if (c->tty.sx == 0 || c->tty.sy == 0)
- return (0);
- memcpy(&old_status, &c->status, sizeof old_status);
- screen_init(&c->status, c->tty.sx, 1, 0);
- len = screen_write_strlen("%s", c->message_string);
- if (len > c->tty.sx)
- len = c->tty.sx;
- style_apply(&gc, s->options, "message-style");
- screen_write_start(&ctx, NULL, &c->status);
- screen_write_cursormove(&ctx, 0, 0);
- screen_write_nputs(&ctx, len, &gc, "%s", c->message_string);
- for (; len < c->tty.sx; len++)
- screen_write_putc(&ctx, &gc, ' ');
- screen_write_stop(&ctx);
- if (grid_compare(c->status.grid, old_status.grid) == 0) {
- screen_free(&old_status);
- return (0);
- }
- screen_free(&old_status);
- return (1);
- }
- /* Enable status line prompt. */
- void
- status_prompt_set(struct client *c, const char *msg, const char *input,
- int (*callbackfn)(void *, const char *), void (*freefn)(void *),
- void *data, int flags)
- {
- struct format_tree *ft;
- int keys;
- time_t t;
- ft = format_create(NULL, 0);
- format_defaults(ft, c, NULL, NULL, NULL);
- t = time(NULL);
- status_message_clear(c);
- status_prompt_clear(c);
- c->prompt_string = format_expand_time(ft, msg, t);
- c->prompt_buffer = format_expand_time(ft, input, t);
- c->prompt_index = strlen(c->prompt_buffer);
- c->prompt_callbackfn = callbackfn;
- c->prompt_freefn = freefn;
- c->prompt_data = data;
- c->prompt_hindex = 0;
- c->prompt_flags = flags;
- keys = options_get_number(c->session->options, "status-keys");
- if (keys == MODEKEY_EMACS)
- mode_key_init(&c->prompt_mdata, &mode_key_tree_emacs_edit);
- else
- mode_key_init(&c->prompt_mdata, &mode_key_tree_vi_edit);
- c->tty.flags |= (TTY_NOCURSOR|TTY_FREEZE);
- c->flags |= CLIENT_STATUS;
- format_free(ft);
- }
- /* Remove status line prompt. */
- void
- status_prompt_clear(struct client *c)
- {
- if (c->prompt_string == NULL)
- return;
- if (c->prompt_freefn != NULL && c->prompt_data != NULL)
- c->prompt_freefn(c->prompt_data);
- free(c->prompt_string);
- c->prompt_string = NULL;
- free(c->prompt_buffer);
- c->prompt_buffer = NULL;
- c->tty.flags &= ~(TTY_NOCURSOR|TTY_FREEZE);
- c->flags |= CLIENT_REDRAW; /* screen was frozen and may have changed */
- screen_reinit(&c->status);
- }
- /* Update status line prompt with a new prompt string. */
- void
- status_prompt_update(struct client *c, const char *msg, const char *input)
- {
- struct format_tree *ft;
- time_t t;
- ft = format_create(NULL, 0);
- format_defaults(ft, c, NULL, NULL, NULL);
- t = time(NULL);
- free(c->prompt_string);
- c->prompt_string = format_expand_time(ft, msg, t);
- free(c->prompt_buffer);
- c->prompt_buffer = format_expand_time(ft, input, t);
- c->prompt_index = strlen(c->prompt_buffer);
- c->prompt_hindex = 0;
- c->flags |= CLIENT_STATUS;
- format_free(ft);
- }
- /* Draw client prompt on status line of present else on last line. */
- int
- status_prompt_redraw(struct client *c)
- {
- struct screen_write_ctx ctx;
- struct session *s = c->session;
- struct screen old_status;
- size_t i, size, left, len, off;
- struct grid_cell gc;
- if (c->tty.sx == 0 || c->tty.sy == 0)
- return (0);
- memcpy(&old_status, &c->status, sizeof old_status);
- screen_init(&c->status, c->tty.sx, 1, 0);
- len = screen_write_strlen("%s", c->prompt_string);
- if (len > c->tty.sx)
- len = c->tty.sx;
- off = 0;
- /* Change colours for command mode. */
- if (c->prompt_mdata.mode == 1)
- style_apply(&gc, s->options, "message-command-style");
- else
- style_apply(&gc, s->options, "message-style");
- screen_write_start(&ctx, NULL, &c->status);
- screen_write_cursormove(&ctx, 0, 0);
- screen_write_nputs(&ctx, len, &gc, "%s", c->prompt_string);
- left = c->tty.sx - len;
- if (left != 0) {
- size = screen_write_strlen("%s", c->prompt_buffer);
- if (c->prompt_index >= left) {
- off = c->prompt_index - left + 1;
- if (c->prompt_index == size)
- left--;
- size = left;
- }
- screen_write_nputs(&ctx, left, &gc, "%s", c->prompt_buffer +
- off);
- for (i = len + size; i < c->tty.sx; i++)
- screen_write_putc(&ctx, &gc, ' ');
- }
- screen_write_stop(&ctx);
- /* Apply fake cursor. */
- off = len + c->prompt_index - off;
- grid_view_get_cell(c->status.grid, off, 0, &gc);
- gc.attr ^= GRID_ATTR_REVERSE;
- grid_view_set_cell(c->status.grid, off, 0, &gc);
- if (grid_compare(c->status.grid, old_status.grid) == 0) {
- screen_free(&old_status);
- return (0);
- }
- screen_free(&old_status);
- return (1);
- }
- /* Handle keys in prompt. */
- void
- status_prompt_key(struct client *c, key_code key)
- {
- struct session *sess = c->session;
- struct options *oo = sess->options;
- struct paste_buffer *pb;
- char *s, *first, *last, word[64], swapc;
- const char *histstr, *bufdata, *wsep = NULL;
- u_char ch;
- size_t size, n, off, idx, bufsize;
- size = strlen(c->prompt_buffer);
- switch (mode_key_lookup(&c->prompt_mdata, key, NULL)) {
- case MODEKEYEDIT_CURSORLEFT:
- if (c->prompt_index > 0) {
- c->prompt_index--;
- c->flags |= CLIENT_STATUS;
- }
- break;
- case MODEKEYEDIT_SWITCHMODE:
- c->flags |= CLIENT_STATUS;
- break;
- case MODEKEYEDIT_SWITCHMODEAPPEND:
- c->flags |= CLIENT_STATUS;
- /* FALLTHROUGH */
- case MODEKEYEDIT_CURSORRIGHT:
- if (c->prompt_index < size) {
- c->prompt_index++;
- c->flags |= CLIENT_STATUS;
- }
- break;
- case MODEKEYEDIT_SWITCHMODEBEGINLINE:
- c->flags |= CLIENT_STATUS;
- /* FALLTHROUGH */
- case MODEKEYEDIT_STARTOFLINE:
- if (c->prompt_index != 0) {
- c->prompt_index = 0;
- c->flags |= CLIENT_STATUS;
- }
- break;
- case MODEKEYEDIT_SWITCHMODEAPPENDLINE:
- c->flags |= CLIENT_STATUS;
- /* FALLTHROUGH */
- case MODEKEYEDIT_ENDOFLINE:
- if (c->prompt_index != size) {
- c->prompt_index = size;
- c->flags |= CLIENT_STATUS;
- }
- break;
- case MODEKEYEDIT_COMPLETE:
- if (*c->prompt_buffer == '\0')
- break;
- idx = c->prompt_index;
- if (idx != 0)
- idx--;
- /* Find the word we are in. */
- first = c->prompt_buffer + idx;
- while (first > c->prompt_buffer && *first != ' ')
- first--;
- while (*first == ' ')
- first++;
- last = c->prompt_buffer + idx;
- while (*last != '\0' && *last != ' ')
- last++;
- while (*last == ' ')
- last--;
- if (*last != '\0')
- last++;
- if (last <= first ||
- ((size_t) (last - first)) > (sizeof word) - 1)
- break;
- memcpy(word, first, last - first);
- word[last - first] = '\0';
- /* And try to complete it. */
- if ((s = status_prompt_complete(sess, word)) == NULL)
- break;
- /* Trim out word. */
- n = size - (last - c->prompt_buffer) + 1; /* with \0 */
- memmove(first, last, n);
- size -= last - first;
- /* Insert the new word. */
- size += strlen(s);
- off = first - c->prompt_buffer;
- c->prompt_buffer = xrealloc(c->prompt_buffer, size + 1);
- first = c->prompt_buffer + off;
- memmove(first + strlen(s), first, n);
- memcpy(first, s, strlen(s));
- c->prompt_index = (first - c->prompt_buffer) + strlen(s);
- free(s);
- c->flags |= CLIENT_STATUS;
- break;
- case MODEKEYEDIT_BACKSPACE:
- if (c->prompt_index != 0) {
- if (c->prompt_index == size)
- c->prompt_buffer[--c->prompt_index] = '\0';
- else {
- memmove(c->prompt_buffer + c->prompt_index - 1,
- c->prompt_buffer + c->prompt_index,
- size + 1 - c->prompt_index);
- c->prompt_index--;
- }
- c->flags |= CLIENT_STATUS;
- }
- break;
- case MODEKEYEDIT_DELETE:
- case MODEKEYEDIT_SWITCHMODESUBSTITUTE:
- if (c->prompt_index != size) {
- memmove(c->prompt_buffer + c->prompt_index,
- c->prompt_buffer + c->prompt_index + 1,
- size + 1 - c->prompt_index);
- c->flags |= CLIENT_STATUS;
- }
- break;
- case MODEKEYEDIT_DELETELINE:
- case MODEKEYEDIT_SWITCHMODESUBSTITUTELINE:
- *c->prompt_buffer = '\0';
- c->prompt_index = 0;
- c->flags |= CLIENT_STATUS;
- break;
- case MODEKEYEDIT_DELETETOENDOFLINE:
- case MODEKEYEDIT_SWITCHMODECHANGELINE:
- if (c->prompt_index < size) {
- c->prompt_buffer[c->prompt_index] = '\0';
- c->flags |= CLIENT_STATUS;
- }
- break;
- case MODEKEYEDIT_DELETEWORD:
- wsep = options_get_string(oo, "word-separators");
- idx = c->prompt_index;
- /* Find a non-separator. */
- while (idx != 0) {
- idx--;
- if (!strchr(wsep, c->prompt_buffer[idx]))
- break;
- }
- /* Find the separator at the beginning of the word. */
- while (idx != 0) {
- idx--;
- if (strchr(wsep, c->prompt_buffer[idx])) {
- /* Go back to the word. */
- idx++;
- break;
- }
- }
- memmove(c->prompt_buffer + idx,
- c->prompt_buffer + c->prompt_index,
- size + 1 - c->prompt_index);
- memset(c->prompt_buffer + size - (c->prompt_index - idx),
- '\0', c->prompt_index - idx);
- c->prompt_index = idx;
- c->flags |= CLIENT_STATUS;
- break;
- case MODEKEYEDIT_NEXTSPACE:
- wsep = " ";
- /* FALLTHROUGH */
- case MODEKEYEDIT_NEXTWORD:
- if (wsep == NULL)
- wsep = options_get_string(oo, "word-separators");
- /* Find a separator. */
- while (c->prompt_index != size) {
- c->prompt_index++;
- if (strchr(wsep, c->prompt_buffer[c->prompt_index]))
- break;
- }
- /* Find the word right after the separation. */
- while (c->prompt_index != size) {
- c->prompt_index++;
- if (!strchr(wsep, c->prompt_buffer[c->prompt_index]))
- break;
- }
- c->flags |= CLIENT_STATUS;
- break;
- case MODEKEYEDIT_NEXTSPACEEND:
- wsep = " ";
- /* FALLTHROUGH */
- case MODEKEYEDIT_NEXTWORDEND:
- if (wsep == NULL)
- wsep = options_get_string(oo, "word-separators");
- /* Find a word. */
- while (c->prompt_index != size) {
- c->prompt_index++;
- if (!strchr(wsep, c->prompt_buffer[c->prompt_index]))
- break;
- }
- /* Find the separator at the end of the word. */
- while (c->prompt_index != size) {
- c->prompt_index++;
- if (strchr(wsep, c->prompt_buffer[c->prompt_index]))
- break;
- }
- /* Back up to the end-of-word like vi. */
- if (options_get_number(oo, "status-keys") == MODEKEY_VI &&
- c->prompt_index != 0)
- c->prompt_index--;
- c->flags |= CLIENT_STATUS;
- break;
- case MODEKEYEDIT_PREVIOUSSPACE:
- wsep = " ";
- /* FALLTHROUGH */
- case MODEKEYEDIT_PREVIOUSWORD:
- if (wsep == NULL)
- wsep = options_get_string(oo, "word-separators");
- /* Find a non-separator. */
- while (c->prompt_index != 0) {
- c->prompt_index--;
- if (!strchr(wsep, c->prompt_buffer[c->prompt_index]))
- break;
- }
- /* Find the separator at the beginning of the word. */
- while (c->prompt_index != 0) {
- c->prompt_index--;
- if (strchr(wsep, c->prompt_buffer[c->prompt_index])) {
- /* Go back to the word. */
- c->prompt_index++;
- break;
- }
- }
- c->flags |= CLIENT_STATUS;
- break;
- case MODEKEYEDIT_HISTORYUP:
- histstr = status_prompt_up_history(&c->prompt_hindex);
- if (histstr == NULL)
- break;
- free(c->prompt_buffer);
- c->prompt_buffer = xstrdup(histstr);
- c->prompt_index = strlen(c->prompt_buffer);
- c->flags |= CLIENT_STATUS;
- break;
- case MODEKEYEDIT_HISTORYDOWN:
- histstr = status_prompt_down_history(&c->prompt_hindex);
- if (histstr == NULL)
- break;
- free(c->prompt_buffer);
- c->prompt_buffer = xstrdup(histstr);
- c->prompt_index = strlen(c->prompt_buffer);
- c->flags |= CLIENT_STATUS;
- break;
- case MODEKEYEDIT_PASTE:
- if ((pb = paste_get_top(NULL)) == NULL)
- break;
- bufdata = paste_buffer_data(pb, &bufsize);
- for (n = 0; n < bufsize; n++) {
- ch = (u_char)bufdata[n];
- if (ch < 32 || ch == 127)
- break;
- }
- c->prompt_buffer = xrealloc(c->prompt_buffer, size + n + 1);
- if (c->prompt_index == size) {
- memcpy(c->prompt_buffer + c->prompt_index, bufdata, n);
- c->prompt_index += n;
- c->prompt_buffer[c->prompt_index] = '\0';
- } else {
- memmove(c->prompt_buffer + c->prompt_index + n,
- c->prompt_buffer + c->prompt_index,
- size + 1 - c->prompt_index);
- memcpy(c->prompt_buffer + c->prompt_index, bufdata, n);
- c->prompt_index += n;
- }
- c->flags |= CLIENT_STATUS;
- break;
- case MODEKEYEDIT_TRANSPOSECHARS:
- idx = c->prompt_index;
- if (idx < size)
- idx++;
- if (idx >= 2) {
- swapc = c->prompt_buffer[idx - 2];
- c->prompt_buffer[idx - 2] = c->prompt_buffer[idx - 1];
- c->prompt_buffer[idx - 1] = swapc;
- c->prompt_index = idx;
- c->flags |= CLIENT_STATUS;
- }
- break;
- case MODEKEYEDIT_ENTER:
- if (*c->prompt_buffer != '\0')
- status_prompt_add_history(c->prompt_buffer);
- if (c->prompt_callbackfn(c->prompt_data, c->prompt_buffer) == 0)
- status_prompt_clear(c);
- break;
- case MODEKEYEDIT_CANCEL:
- if (c->prompt_callbackfn(c->prompt_data, NULL) == 0)
- status_prompt_clear(c);
- break;
- case MODEKEY_OTHER:
- if (key <= 0x1f || key >= 0x7f)
- break;
- c->prompt_buffer = xrealloc(c->prompt_buffer, size + 2);
- if (c->prompt_index == size) {
- c->prompt_buffer[c->prompt_index++] = key;
- c->prompt_buffer[c->prompt_index] = '\0';
- } else {
- memmove(c->prompt_buffer + c->prompt_index + 1,
- c->prompt_buffer + c->prompt_index,
- size + 1 - c->prompt_index);
- c->prompt_buffer[c->prompt_index++] = key;
- }
- if (c->prompt_flags & PROMPT_SINGLE) {
- if (c->prompt_callbackfn(c->prompt_data,
- c->prompt_buffer) == 0)
- status_prompt_clear(c);
- }
- c->flags |= CLIENT_STATUS;
- break;
- default:
- break;
- }
- }
- /* Get previous line from the history. */
- const char *
- status_prompt_up_history(u_int *idx)
- {
- /*
- * History runs from 0 to size - 1. Index is from 0 to size. Zero is
- * empty.
- */
- if (status_prompt_hsize == 0 || *idx == status_prompt_hsize)
- return (NULL);
- (*idx)++;
- return (status_prompt_hlist[status_prompt_hsize - *idx]);
- }
- /* Get next line from the history. */
- const char *
- status_prompt_down_history(u_int *idx)
- {
- if (status_prompt_hsize == 0 || *idx == 0)
- return ("");
- (*idx)--;
- if (*idx == 0)
- return ("");
- return (status_prompt_hlist[status_prompt_hsize - *idx]);
- }
- /* Add line to the history. */
- void
- status_prompt_add_history(const char *line)
- {
- size_t size;
- if (status_prompt_hsize > 0 &&
- strcmp(status_prompt_hlist[status_prompt_hsize - 1], line) == 0)
- return;
- if (status_prompt_hsize == PROMPT_HISTORY) {
- free(status_prompt_hlist[0]);
- size = (PROMPT_HISTORY - 1) * sizeof *status_prompt_hlist;
- memmove(&status_prompt_hlist[0], &status_prompt_hlist[1], size);
- status_prompt_hlist[status_prompt_hsize - 1] = xstrdup(line);
- return;
- }
- status_prompt_hlist = xreallocarray(status_prompt_hlist,
- status_prompt_hsize + 1, sizeof *status_prompt_hlist);
- status_prompt_hlist[status_prompt_hsize++] = xstrdup(line);
- }
- /* Build completion list. */
- const char **
- status_prompt_complete_list(u_int *size, const char *s)
- {
- const char **list = NULL, **layout;
- const struct cmd_entry **cmdent;
- const struct options_table_entry *oe;
- const char *layouts[] = {
- "even-horizontal", "even-vertical", "main-horizontal",
- "main-vertical", "tiled", NULL
- };
- *size = 0;
- for (cmdent = cmd_table; *cmdent != NULL; cmdent++) {
- if (strncmp((*cmdent)->name, s, strlen(s)) == 0) {
- list = xreallocarray(list, (*size) + 1, sizeof *list);
- list[(*size)++] = (*cmdent)->name;
- }
- }
- for (oe = options_table; oe->name != NULL; oe++) {
- if (strncmp(oe->name, s, strlen(s)) == 0) {
- list = xreallocarray(list, (*size) + 1, sizeof *list);
- list[(*size)++] = oe->name;
- }
- }
- for (layout = layouts; *layout != NULL; layout++) {
- if (strncmp(*layout, s, strlen(s)) == 0) {
- list = xreallocarray(list, (*size) + 1, sizeof *list);
- list[(*size)++] = *layout;
- }
- }
- return (list);
- }
- /* Find longest prefix. */
- char *
- status_prompt_complete_prefix(const char **list, u_int size)
- {
- char *out;
- u_int i;
- size_t j;
- out = xstrdup(list[0]);
- for (i = 1; i < size; i++) {
- j = strlen(list[i]);
- if (j > strlen(out))
- j = strlen(out);
- for (; j > 0; j--) {
- if (out[j - 1] != list[i][j - 1])
- out[j - 1] = '\0';
- }
- }
- return (out);
- }
- /* Complete word. */
- char *
- status_prompt_complete(struct session *sess, const char *s)
- {
- const char **list = NULL, *colon;
- u_int size = 0, i;
- struct session *s_loop;
- struct winlink *wl;
- struct window *w;
- char *copy, *out, *tmp;
- if (*s == '\0')
- return (NULL);
- out = NULL;
- if (strncmp(s, "-t", 2) != 0 && strncmp(s, "-s", 2) != 0) {
- list = status_prompt_complete_list(&size, s);
- if (size == 0)
- out = NULL;
- else if (size == 1)
- xasprintf(&out, "%s ", list[0]);
- else
- out = status_prompt_complete_prefix(list, size);
- free(list);
- return (out);
- }
- copy = xstrdup(s);
- colon = ":";
- if (copy[strlen(copy) - 1] == ':')
- copy[strlen(copy) - 1] = '\0';
- else
- colon = "";
- s = copy + 2;
- RB_FOREACH(s_loop, sessions, &sessions) {
- if (strncmp(s_loop->name, s, strlen(s)) == 0) {
- list = xreallocarray(list, size + 2, sizeof *list);
- list[size++] = s_loop->name;
- }
- }
- if (size == 1) {
- out = xstrdup(list[0]);
- if (session_find(list[0]) != NULL)
- colon = ":";
- } else if (size != 0)
- out = status_prompt_complete_prefix(list, size);
- if (out != NULL) {
- xasprintf(&tmp, "-%c%s%s", copy[1], out, colon);
- out = tmp;
- goto found;
- }
- colon = "";
- if (*s == ':') {
- RB_FOREACH(wl, winlinks, &sess->windows) {
- xasprintf(&tmp, ":%s", wl->window->name);
- if (strncmp(tmp, s, strlen(s)) == 0){
- list = xreallocarray(list, size + 1,
- sizeof *list);
- list[size++] = tmp;
- continue;
- }
- free(tmp);
- xasprintf(&tmp, ":%d", wl->idx);
- if (strncmp(tmp, s, strlen(s)) == 0) {
- list = xreallocarray(list, size + 1,
- sizeof *list);
- list[size++] = tmp;
- continue;
- }
- free(tmp);
- }
- } else {
- RB_FOREACH(s_loop, sessions, &sessions) {
- RB_FOREACH(wl, winlinks, &s_loop->windows) {
- w = wl->window;
- xasprintf(&tmp, "%s:%s", s_loop->name, w->name);
- if (strncmp(tmp, s, strlen(s)) == 0) {
- list = xreallocarray(list, size + 1,
- sizeof *list);
- list[size++] = tmp;
- continue;
- }
- free(tmp);
- xasprintf(&tmp, "%s:%d", s_loop->name, wl->idx);
- if (strncmp(tmp, s, strlen(s)) == 0) {
- list = xreallocarray(list, size + 1,
- sizeof *list);
- list[size++] = tmp;
- continue;
- }
- free(tmp);
- }
- }
- }
- if (size == 1) {
- out = xstrdup(list[0]);
- colon = " ";
- } else if (size != 0)
- out = status_prompt_complete_prefix(list, size);
- if (out != NULL) {
- xasprintf(&tmp, "-%c%s%s", copy[1], out, colon);
- out = tmp;
- }
- for (i = 0; i < size; i++)
- free((void *)list[i]);
- found:
- free(copy);
- free(list);
- return (out);
- }
|