mirror of
https://github.com/tmux/tmux.git
synced 2026-10-07 02:04:22 +00:00
Merge branch 'master' into local_windows
This commit is contained in:
169
format.c
169
format.c
@@ -1,4 +1,4 @@
|
||||
/* $OpenBSD: format.c,v 1.407 2026/07/20 07:42:13 nicm Exp $ */
|
||||
/* $OpenBSD: format.c,v 1.412 2026/08/05 07:31:08 nicm Exp $ */
|
||||
|
||||
/*
|
||||
* Copyright (c) 2011 Nicholas Marriott <nicholas.marriott@gmail.com>
|
||||
@@ -128,6 +128,7 @@ format_job_cmp(struct format_job *fj1, struct format_job *fj2)
|
||||
#define FORMAT_OPTIONS 0x40000000
|
||||
#define FORMAT_ENVIRON 0x80000000ULL
|
||||
#define FORMAT_DIFFERENCE 0x100000000ULL
|
||||
#define FORMAT_CYCLE 0x200000000ULL
|
||||
|
||||
/* Limit on recursion. */
|
||||
#define FORMAT_LOOP_LIMIT 100
|
||||
@@ -138,9 +139,13 @@ format_job_cmp(struct format_job *fj1, struct format_job *fj2)
|
||||
/* How often to check the time in long loops. */
|
||||
#define FORMAT_TIME_LOOP_CHECK 10000
|
||||
|
||||
/* Fixed animation period (ms): redraw interval and shortest frame step. */
|
||||
#define FORMAT_CYCLE_PERIOD 100
|
||||
|
||||
/* Format expand flags. */
|
||||
#define FORMAT_EXPAND_TIME 0x1
|
||||
#define FORMAT_EXPAND_NOJOBS 0x2
|
||||
#define FORMAT_EXPAND_NOCYCLE 0x4
|
||||
|
||||
/* Entry in format tree. */
|
||||
struct format_entry {
|
||||
@@ -413,7 +418,8 @@ format_job_get(struct format_expand_state *es, const char *cmd)
|
||||
RB_INSERT(format_job_tree, jobs, fj);
|
||||
}
|
||||
|
||||
format_copy_state(&next, es, FORMAT_EXPAND_NOJOBS);
|
||||
format_copy_state(&next, es, FORMAT_EXPAND_NOJOBS|
|
||||
FORMAT_EXPAND_NOCYCLE);
|
||||
next.flags &= ~FORMAT_EXPAND_TIME;
|
||||
|
||||
expanded = format_expand1(&next, cmd);
|
||||
@@ -2095,6 +2101,60 @@ format_cb_synchronized_output_flag(struct format_tree *ft)
|
||||
return (NULL);
|
||||
}
|
||||
|
||||
/* Callback for pane_private_modes. */
|
||||
static void *
|
||||
format_cb_pane_private_modes(struct format_tree *ft)
|
||||
{
|
||||
static const struct {
|
||||
int mode;
|
||||
int number;
|
||||
} table[] = {
|
||||
{ MODE_KCURSOR, 1 }, /* DECCKM */
|
||||
{ MODE_ORIGIN, 6 }, /* DECOM */
|
||||
{ MODE_WRAP, 7 }, /* DECAWM */
|
||||
{ MODE_CURSOR_BLINKING, 12 }, /* cursor blinking */
|
||||
{ MODE_CURSOR, 25 }, /* DECTCEM */
|
||||
{ MODE_MOUSE_STANDARD, 1000 }, /* mouse normal tracking */
|
||||
{ MODE_MOUSE_BUTTON, 1002 }, /* mouse button tracking */
|
||||
{ MODE_MOUSE_ALL, 1003 }, /* mouse any tracking */
|
||||
{ MODE_FOCUSON, 1004 }, /* focus reporting */
|
||||
{ MODE_MOUSE_UTF8, 1005 }, /* mouse: UTF-8 */
|
||||
{ MODE_MOUSE_SGR, 1006 }, /* mouse: SGR */
|
||||
{ MODE_BRACKETPASTE, 2004 }, /* bracketed paste */
|
||||
{ MODE_SYNC, 2026 }, /* synchronized output */
|
||||
{ MODE_THEME_UPDATES, 2031 }, /* theme update notifications */
|
||||
};
|
||||
int mode;
|
||||
char *value = NULL, *tmp;
|
||||
u_int i;
|
||||
|
||||
if (ft->wp == NULL)
|
||||
return (NULL);
|
||||
mode = ft->wp->base.mode;
|
||||
|
||||
for (i = 0; i < nitems(table); i++) {
|
||||
if (~mode & table[i].mode)
|
||||
continue;
|
||||
/*
|
||||
* Only report cursor blinking when set by the application, not
|
||||
* when it comes from the cursor-style option.
|
||||
*/
|
||||
if (table[i].mode == MODE_CURSOR_BLINKING &&
|
||||
(~mode & MODE_CURSOR_BLINKING_SET))
|
||||
continue;
|
||||
if (value == NULL)
|
||||
xasprintf(&value, "%d", table[i].number);
|
||||
else {
|
||||
xasprintf(&tmp, "%s,%d", value, table[i].number);
|
||||
free(value);
|
||||
value = tmp;
|
||||
}
|
||||
}
|
||||
if (value == NULL)
|
||||
return (xstrdup(""));
|
||||
return (value);
|
||||
}
|
||||
|
||||
/* Callback for pane_active. */
|
||||
static void *
|
||||
format_cb_pane_active(struct format_tree *ft)
|
||||
@@ -2622,20 +2682,23 @@ format_cb_pane_unzoomed_width(struct format_tree *ft)
|
||||
{
|
||||
struct window_pane *wp = ft->wp;
|
||||
struct layout_cell *lc;
|
||||
int sb_w, sb_pad;
|
||||
int saved, sb_w, sb_pad;
|
||||
u_int sx;
|
||||
|
||||
if (wp == NULL)
|
||||
return (NULL);
|
||||
|
||||
lc = wp->saved_layout_cell;
|
||||
saved = (lc != NULL);
|
||||
if (lc == NULL)
|
||||
lc = wp->layout_cell;
|
||||
if (lc == NULL)
|
||||
return (NULL);
|
||||
sx = lc->g.sx;
|
||||
|
||||
if (window_pane_scrollbar_reserve(wp)) {
|
||||
if ((saved && !SCREEN_IS_ALTERNATE(&wp->base) &&
|
||||
wp->window->sb == PANE_SCROLLBARS_ALWAYS) ||
|
||||
(!saved && window_pane_scrollbar_reserve(wp))) {
|
||||
sb_w = wp->scrollbar_style.width;
|
||||
sb_pad = wp->scrollbar_style.pad;
|
||||
if (sb_w < 1)
|
||||
@@ -3853,6 +3916,9 @@ static const struct format_table_entry format_table[] = {
|
||||
{ "pane_pipe_pid", FORMAT_TABLE_STRING,
|
||||
format_cb_pane_pipe_pid
|
||||
},
|
||||
{ "pane_private_modes", FORMAT_TABLE_STRING,
|
||||
format_cb_pane_private_modes
|
||||
},
|
||||
{ "pane_right", FORMAT_TABLE_STRING,
|
||||
format_cb_pane_right
|
||||
},
|
||||
@@ -4240,6 +4306,8 @@ format_log_debug_cb(const char *key, const char *value, void *arg)
|
||||
void
|
||||
format_log_debug(struct format_tree *ft, const char *prefix)
|
||||
{
|
||||
if (log_get_level() == 0)
|
||||
return;
|
||||
format_each(ft, format_log_debug_cb, (void *)prefix);
|
||||
}
|
||||
|
||||
@@ -4368,7 +4436,7 @@ format_quote_shell(const char *s)
|
||||
|
||||
at = out = xmalloc(strlen(s) * 2 + 1);
|
||||
for (cp = s; *cp != '\0'; cp++) {
|
||||
if (strchr("|&;<>()$`\\\"'*?[# =%", *cp) != NULL)
|
||||
if (strchr("|&;<>()$`\\\"'*?[# =%\n\t", *cp) != NULL)
|
||||
*at++ = '\\';
|
||||
*at++ = *cp;
|
||||
}
|
||||
@@ -4846,7 +4914,7 @@ format_build_modifiers(struct format_expand_state *es, const char **s,
|
||||
|
||||
/*
|
||||
* Modifiers are a ; separated list of the forms:
|
||||
* l,m,C,a,b,c,d,I,n,t,w,q,E,T,S,W,P,O,V,R,<,>
|
||||
* l,m,C,a,b,c,d,I,n,t,w,q,E,T,S,W,P,O,V,R,A,<,>
|
||||
* =a
|
||||
* =/a
|
||||
* =/a/
|
||||
@@ -4865,7 +4933,7 @@ format_build_modifiers(struct format_expand_state *es, const char **s,
|
||||
break;
|
||||
|
||||
/* Check single character modifiers with no arguments. */
|
||||
if (strchr("labdnwETSWPOVL!<>", cp[0]) != NULL &&
|
||||
if (strchr("labdnwETSWPOVL!<>A", cp[0]) != NULL &&
|
||||
format_is_end(cp[1])) {
|
||||
format_add_modifier(&list, count, cp, 1, NULL, 0);
|
||||
cp++;
|
||||
@@ -4887,7 +4955,7 @@ format_build_modifiers(struct format_expand_state *es, const char **s,
|
||||
}
|
||||
|
||||
/* Now try single character with arguments. */
|
||||
if (strchr("ImCLNPSOVst=pReqWc", cp[0]) == NULL)
|
||||
if (strchr("ImCLNPSOVst=pReqWcA", cp[0]) == NULL)
|
||||
break;
|
||||
c = cp[0];
|
||||
|
||||
@@ -5850,6 +5918,74 @@ fail:
|
||||
return (NULL);
|
||||
}
|
||||
|
||||
/* Callback for the cycle timer; redraw the status line. */
|
||||
static void
|
||||
format_cycle_callback(__unused int fd, __unused short events, void *arg)
|
||||
{
|
||||
struct client *c = arg;
|
||||
|
||||
if (c->message_string == NULL && c->prompt == NULL)
|
||||
c->flags |= CLIENT_REDRAWSTATUS;
|
||||
}
|
||||
|
||||
/* Arm the cycle timer to redraw the status line if it is not already. */
|
||||
static void
|
||||
format_cycle_start_timer(struct client *c)
|
||||
{
|
||||
struct timeval tv;
|
||||
|
||||
tv.tv_sec = FORMAT_CYCLE_PERIOD / 1000;
|
||||
tv.tv_usec = (FORMAT_CYCLE_PERIOD % 1000) * 1000L;
|
||||
|
||||
if (!event_initialized(&c->cycle_timer))
|
||||
evtimer_set(&c->cycle_timer, format_cycle_callback, c);
|
||||
if (!evtimer_pending(&c->cycle_timer, NULL))
|
||||
evtimer_add(&c->cycle_timer, &tv);
|
||||
}
|
||||
|
||||
/* Expand the "A" animation modifier; see the manual for the syntax. */
|
||||
static char *
|
||||
format_cycle(struct format_expand_state *es, const char *frames, u_int count)
|
||||
{
|
||||
struct format_tree *ft = es->ft;
|
||||
const char *start, *end, *cp;
|
||||
u_int n, index, i;
|
||||
|
||||
/*
|
||||
* A cycle is only expanded in a status format, and never in the
|
||||
* command or output of #() where it would change on every frame and
|
||||
* make the job run again.
|
||||
*/
|
||||
if (!(ft->flags & FORMAT_STATUS) || (es->flags & FORMAT_EXPAND_NOCYCLE))
|
||||
return (xstrdup(""));
|
||||
if (*frames == '\0')
|
||||
return (xstrdup(""));
|
||||
|
||||
/* Count the comma-separated frames (there is at least one). */
|
||||
n = 1;
|
||||
for (cp = frames; *cp != '\0'; cp++) {
|
||||
if (*cp == ',')
|
||||
n++;
|
||||
}
|
||||
index = (es->start_time / (count * FORMAT_CYCLE_PERIOD)) % n;
|
||||
|
||||
/*
|
||||
* Redraw the status line so the frames advance on their own; a
|
||||
* single frame never changes so there is nothing to redraw for.
|
||||
*/
|
||||
if (n > 1 && ft->client != NULL)
|
||||
format_cycle_start_timer(ft->client);
|
||||
|
||||
/* Walk to the chosen frame and return a copy of it. */
|
||||
start = frames;
|
||||
for (i = 0; i < index; i++)
|
||||
start = strchr(start, ',') + 1;
|
||||
end = strchr(start, ',');
|
||||
if (end == NULL)
|
||||
end = start + strlen(start);
|
||||
return (xstrndup(start, end - start));
|
||||
}
|
||||
|
||||
/* Replace a key. */
|
||||
static int
|
||||
format_replace(struct format_expand_state *es, const char *key, size_t keylen,
|
||||
@@ -5870,6 +6006,7 @@ format_replace(struct format_expand_state *es, const char *key, size_t keylen,
|
||||
struct format_modifier *list, *cmp = NULL, *search = NULL;
|
||||
struct format_modifier **sub = NULL, *mexp = NULL, *fm;
|
||||
struct format_modifier *bool_op_n = NULL;
|
||||
u_int cycle_count = 1;
|
||||
u_int i, count, nsub = 0, nrep, check = 0;
|
||||
const char *loop_flags = "";
|
||||
struct format_expand_state next;
|
||||
@@ -5930,6 +6067,15 @@ format_replace(struct format_expand_state *es, const char *key, size_t keylen,
|
||||
if (errstr != NULL)
|
||||
width = 0;
|
||||
break;
|
||||
case 'A':
|
||||
modifiers |= FORMAT_CYCLE;
|
||||
if (fm->argc < 1)
|
||||
break;
|
||||
cycle_count = strtonum(fm->argv[0], 1, 100,
|
||||
&errstr);
|
||||
if (errstr != NULL)
|
||||
cycle_count = 1;
|
||||
break;
|
||||
case 'w':
|
||||
modifiers |= FORMAT_WIDTH;
|
||||
break;
|
||||
@@ -6151,6 +6297,13 @@ format_replace(struct format_expand_state *es, const char *key, size_t keylen,
|
||||
goto done;
|
||||
}
|
||||
|
||||
/* Is this an animation cycle? */
|
||||
if (modifiers & FORMAT_CYCLE) {
|
||||
value = format_cycle(es, copy, cycle_count);
|
||||
format_log(es, "cycle '%s' is: %s", copy, value);
|
||||
goto done;
|
||||
}
|
||||
|
||||
/* Is this a literal string? */
|
||||
if (modifiers & FORMAT_LITERAL) {
|
||||
format_log(es, "literal string is '%s'", copy);
|
||||
|
||||
Reference in New Issue
Block a user