Merge branch 'obsd-master'

This commit is contained in:
Thomas Adam
2026-04-23 11:58:17 +01:00
22 changed files with 225 additions and 78 deletions

View File

@@ -102,7 +102,7 @@ cmd_new_session_exec(struct cmd *self, struct cmdq_item *item)
tmp = args_get(args, 's');
if (tmp != NULL) {
name = format_single(item, tmp, c, NULL, NULL, NULL);
newname = session_check_name(name);
newname = clean_name(name, "#:.");
if (newname == NULL) {
cmdq_error(item, "invalid session: %s", name);
free(name);
@@ -142,7 +142,7 @@ cmd_new_session_exec(struct cmd *self, struct cmdq_item *item)
else if (groupwith != NULL)
prefix = xstrdup(groupwith->name);
else {
prefix = session_check_name(group);
prefix = clean_name(group, "#:.");
if (prefix == NULL) {
cmdq_error(item, "invalid session group: %s",
group);

View File

@@ -52,7 +52,7 @@ cmd_rename_session_exec(struct cmd *self, struct cmdq_item *item)
char *newname, *tmp;
tmp = format_single_from_target(item, args_string(args, 0));
newname = session_check_name(tmp);
newname = clean_name(tmp, "#:.");
if (newname == NULL) {
cmdq_error(item, "invalid session: %s", tmp);
free(tmp);

View File

@@ -120,6 +120,9 @@ format_job_cmp(struct format_job *fj1, struct format_job *fj2)
/* Limit on recursion. */
#define FORMAT_LOOP_LIMIT 100
/* Limit on time taken (milliseconds). */
#define FORMAT_TIME_LIMIT 100
/* Format expand flags. */
#define FORMAT_EXPAND_TIME 0x1
#define FORMAT_EXPAND_NOJOBS 0x2
@@ -169,9 +172,11 @@ RB_GENERATE_STATIC(format_entry_tree, format_entry, entry, format_entry_cmp);
struct format_expand_state {
struct format_tree *ft;
u_int loop;
uint64_t start_time;
int flags;
time_t time;
struct tm tm;
int flags;
};
/* Format modifier. */
@@ -292,6 +297,7 @@ format_copy_state(struct format_expand_state *to,
to->time = from->time;
memcpy(&to->tm, &from->tm, sizeof to->tm);
to->flags = from->flags|flags;
to->start_time = from->start_time;
}
/* Format job update callback. */
@@ -5247,7 +5253,7 @@ format_replace(struct format_expand_state *es, const char *key, size_t keylen,
value = format_search(search, wp, new);
}
free(new);
} else if (modifiers & FORMAT_REPEAT) {
} else if (modifiers & FORMAT_REPEAT) {
/* Repeat multiple times. */
if (format_choose(es, copy, &left, &right, 1) != 0) {
format_log(es, "repeat syntax error: %s", copy);
@@ -5266,7 +5272,7 @@ format_replace(struct format_expand_state *es, const char *key, size_t keylen,
}
free(right);
free(left);
} else if (modifiers & FORMAT_NOT) {
} else if (modifiers & FORMAT_NOT) {
value = format_bool_op_1(es, copy, 1);
} else if (modifiers & FORMAT_NOT_NOT) {
value = format_bool_op_1(es, copy, 0);
@@ -5532,10 +5538,16 @@ format_expand1(struct format_expand_state *es, const char *fmt)
size_t off, len, n, outlen;
int ch, brackets;
char expanded[8192];
uint64_t t = get_timer();
if (fmt == NULL || *fmt == '\0')
return (xstrdup(""));
if (t - es->start_time >= FORMAT_TIME_LIMIT) {
format_log(es, "reached time limit (%llu)",
(unsigned long long)(t - es->start_time));
return (xstrdup(""));
}
if (es->loop == FORMAT_LOOP_LIMIT) {
format_log(es, "reached loop limit (%u)", FORMAT_LOOP_LIMIT);
return (xstrdup(""));
@@ -5702,6 +5714,7 @@ format_expand_time(struct format_tree *ft, const char *fmt)
memset(&es, 0, sizeof es);
es.ft = ft;
es.flags = FORMAT_EXPAND_TIME;
es.start_time = get_timer();
return (format_expand1(&es, fmt));
}
@@ -5714,6 +5727,7 @@ format_expand(struct format_tree *ft, const char *fmt)
memset(&es, 0, sizeof es);
es.ft = ft;
es.flags = 0;
es.start_time = get_timer();
return (format_expand1(&es, fmt));
}

View File

@@ -2663,12 +2663,9 @@ input_exit_osc(struct input_ctx *ictx)
input_osc_4(ictx, p);
break;
case 7:
if (utf8_isvalid(p)) {
screen_set_path(sctx->s, p);
if (wp != NULL) {
server_redraw_window_borders(wp->window);
server_status_window(wp->window);
}
if (screen_set_path(sctx->s, p) && wp != NULL) {
server_redraw_window_borders(wp->window);
server_status_window(wp->window);
}
break;
case 8:

View File

@@ -467,6 +467,10 @@ key_bindings_init(void)
"bind -n MouseDown1Status { switch-client -t= }",
"bind -n C-MouseDown1Status { swap-window -t@ }",
/* Mouse button 1 down on default pane-border-format */
"bind -n MouseDown1Control9 { display-menu -t= -xM -yM -O -T 'Kill pane #{pane_index}?' 'Yes' 'y' { kill-pane -t= } 'No' 'n' {}}",
"bind -n MouseDown1Control8 { resize-pane -Z }",
/* Mouse wheel down on status line. */
"bind -n WheelDownStatus { next-window }",

View File

@@ -166,7 +166,9 @@ parse_window_name(const char *in)
if (*name == '/')
name = basename(name);
name = xstrdup(name);
name = clean_name(name, "#");
free(copy);
if (name == NULL)
return (xstrdup(""));
return (name);
}

View File

@@ -1304,7 +1304,14 @@ const struct options_table_entry options_table[] = {
.type = OPTIONS_TABLE_STRING,
.scope = OPTIONS_TABLE_WINDOW|OPTIONS_TABLE_PANE,
.default_str = "#{?pane_active,#[reverse],}#{pane_index}#[default] "
"\"#{pane_title}\"",
"\"#{pane_title}\""
"#{?#{mouse},"
"#[align=right]"
"#[range=control|8]["
"#{?#{window_zoomed_flag},u,z}"
"]#[norange]"
"#[range=control|9][x]#[norange]"
",}",
.text = "Format of text in the pane status lines."
},

View File

@@ -245,19 +245,28 @@ screen_set_cursor_colour(struct screen *s, int colour)
int
screen_set_title(struct screen *s, const char *title)
{
if (!utf8_isvalid(title))
char *new_title;
new_title = clean_name(title, "#");
if (new_title == NULL)
return (0);
free(s->title);
s->title = xstrdup(title);
s->title = new_title;
return (1);
}
/* Set screen path. */
void
int
screen_set_path(struct screen *s, const char *path)
{
char *new_path;
new_path = clean_name(path, "#");
if (new_path == NULL)
return (0);
free(s->path);
utf8_stravis(&s->path, path, VIS_OCTAL|VIS_CSTYLE|VIS_TAB|VIS_NL);
s->path = new_path;
return (1);
}
/* Push the current title onto the stack. */

View File

@@ -41,6 +41,7 @@ static void server_client_check_redraw(struct client *);
static void server_client_check_modes(struct client *);
static void server_client_set_title(struct client *);
static void server_client_set_path(struct client *);
static void server_client_set_progress_bar(struct client *);
static void server_client_reset_state(struct client *);
static void server_client_update_latest(struct client *);
static void server_client_dispatch(struct imsg *, void *);
@@ -2056,6 +2057,7 @@ server_client_check_redraw(struct client *c)
server_client_set_title(c);
server_client_set_path(c);
}
server_client_set_progress_bar(c);
screen_redraw_screen(c);
}
@@ -2122,6 +2124,23 @@ server_client_set_path(struct client *c)
}
}
/* Set client progress bar. */
static void
server_client_set_progress_bar(struct client *c)
{
struct session *s = c->session;
struct progress_bar *pane_pb;
if (s->curw == NULL)
return;
pane_pb = &s->curw->window->active->base.progress_bar;
if (pane_pb->state == c->progress_bar.state &&
pane_pb->progress == c->progress_bar.progress)
return;
memcpy(&c->progress_bar, pane_pb, sizeof c->progress_bar);
tty_set_progress_bar(&c->tty, &c->progress_bar);
}
/* Dispatch message from client. */
static void
server_client_dispatch(struct imsg *imsg, void *arg)

View File

@@ -229,24 +229,6 @@ session_destroy(struct session *s, int notify, const char *from)
session_remove_ref(s, __func__);
}
/* Sanitize session name. */
char *
session_check_name(const char *name)
{
char *copy, *cp, *new_name;
if (*name == '\0')
return (NULL);
copy = xstrdup(name);
for (cp = copy; *cp != '\0'; cp++) {
if (*cp == ':' || *cp == '.')
*cp = '_';
}
utf8_stravis(&new_name, copy, VIS_OCTAL|VIS_CSTYLE|VIS_TAB|VIS_NL);
free(copy);
return (new_name);
}
/* Lock session if it has timed out. */
static void
session_lock_timer(__unused int fd, __unused short events, void *arg)

View File

@@ -82,6 +82,7 @@ spawn_window(struct spawn_context *sc, char **cause)
struct winlink *wl;
int idx = sc->idx;
u_int sx, sy, xpixel, ypixel;
char *name;
spawn_log(__func__, sc);
@@ -180,8 +181,11 @@ spawn_window(struct spawn_context *sc, char **cause)
if (~sc->flags & SPAWN_RESPAWN) {
free(w->name);
if (sc->name != NULL) {
w->name = format_single(item, sc->name, c, s, NULL,
NULL);
name = format_single(item, sc->name, c, s, NULL, NULL);
w->name = clean_name(name, "#");
free(name);
if (w->name == NULL)
w->name = xstrdup("");
options_set_number(w->options, "automatic-rename", 0);
} else
w->name = default_window_name(w);

View File

@@ -972,6 +972,49 @@ status_prompt_space(const struct utf8_data *ud)
return (*ud->data == ' ');
}
static key_code
status_prompt_keypad_key(key_code key)
{
if (key & KEYC_MASK_MODIFIERS)
return (key);
switch (key) {
case KEYC_KP_SLASH:
return ('/');
case KEYC_KP_STAR:
return ('*');
case KEYC_KP_MINUS:
return ('-');
case KEYC_KP_SEVEN:
return ('7');
case KEYC_KP_EIGHT:
return ('8');
case KEYC_KP_NINE:
return ('9');
case KEYC_KP_PLUS:
return ('+');
case KEYC_KP_FOUR:
return ('4');
case KEYC_KP_FIVE:
return ('5');
case KEYC_KP_SIX:
return ('6');
case KEYC_KP_ONE:
return ('1');
case KEYC_KP_TWO:
return ('2');
case KEYC_KP_THREE:
return ('3');
case KEYC_KP_ENTER:
return ('\r');
case KEYC_KP_ZERO:
return ('0');
case KEYC_KP_PERIOD:
return ('.');
}
return (key);
}
/*
* Translate key from vi to emacs. Return 0 to drop key, 1 to process the key
* as an emacs key; return 2 to append to the buffer.
@@ -1383,6 +1426,9 @@ status_prompt_key(struct client *c, key_code key)
}
size = utf8_strlen(c->prompt_buffer);
key &= ~KEYC_MASK_FLAGS;
key = status_prompt_keypad_key(key);
if (c->prompt_flags & PROMPT_NUMERIC) {
if (key >= '0' && key <= '9')
goto append_key;
@@ -1392,7 +1438,6 @@ status_prompt_key(struct client *c, key_code key)
free(s);
return (1);
}
key &= ~KEYC_MASK_FLAGS;
if (c->prompt_flags & (PROMPT_SINGLE|PROMPT_QUOTENEXT)) {
if ((key & KEYC_MASK_KEY) == KEYC_BSPACE)

5
tmux.1
View File

@@ -4489,6 +4489,8 @@ mouse sequences.
Supports the OSC 7 working directory extension.
.It overline
Supports the overline SGR attribute.
.It progressbar
Supports the OSC 9;4 progress bar extension.
.It rectfill
Supports the DECFRA rectangle fill escape sequence.
.It RGB
@@ -7267,7 +7269,6 @@ Display a popup running
when omitted) on
.Ar target\-client .
A popup is a rectangular box drawn over the top of any panes.
Panes are not updated while a popup is present.
If the command is run inside an existing popup, that popup is modified.
Only the
.Fl b ,
@@ -7855,6 +7856,8 @@ $ printf \[aq]\e033[4 q\[aq]
If
.Em Se
is not set, \&Ss with argument 0 will be used to reset the cursor style instead.
.It Em \&Spb
Set the state and progress for the OSC9;4 progress bar.
.It Em \&Swd
Set the opening sequence for the working directory notification.
The sequence is terminated using the standard

17
tmux.c
View File

@@ -281,6 +281,23 @@ get_timer(void)
return ((ts.tv_sec * 1000ULL) + (ts.tv_nsec / 1000000ULL));
}
char *
clean_name(const char *name, const char* forbid)
{
char *copy, *cp, *new_name;
if (*name == '\0' || !utf8_isvalid(name))
return (NULL);
copy = xstrdup(name);
for (cp = copy; *cp != '\0'; cp++) {
if (strchr(forbid, *cp) != NULL)
*cp = '_';
}
utf8_stravis(&new_name, copy, VIS_OCTAL|VIS_CSTYLE|VIS_TAB|VIS_NL);
free(copy);
return (new_name);
}
const char *
sig2name(int signo)
{

7
tmux.h
View File

@@ -638,6 +638,7 @@ enum tty_code_code {
TTYC_SMUL,
TTYC_SMULX,
TTYC_SMXX,
TTYC_SPB,
TTYC_SXL,
TTYC_SS,
TTYC_SWD,
@@ -2017,6 +2018,7 @@ struct client {
char *title;
char *path;
const char *cwd;
struct progress_bar progress_bar;
char *term_name;
int term_features;
@@ -2361,6 +2363,7 @@ int checkshell(const char *);
void setblocking(int, int);
char *shell_argv0(const char *, int);
uint64_t get_timer(void);
char *clean_name(const char *, const char *);
const char *sig2name(int);
const char *find_cwd(void);
const char *find_home(void);
@@ -2655,6 +2658,7 @@ void tty_repeat_requests(struct tty *, int);
void tty_stop_tty(struct tty *);
void tty_set_title(struct tty *, const char *);
void tty_set_path(struct tty *, const char *);
void tty_set_progress_bar(struct tty *, struct progress_bar *);
void tty_default_attributes(struct tty *, const struct grid_cell *,
struct colour_palette *, u_int, struct hyperlinks *);
void tty_update_mode(struct tty *, int, struct screen *);
@@ -3330,7 +3334,7 @@ void screen_set_default_cursor(struct screen *, struct options *);
void screen_set_cursor_style(u_int, enum screen_cursor_style *, int *);
void screen_set_cursor_colour(struct screen *, int);
int screen_set_title(struct screen *, const char *);
void screen_set_path(struct screen *, const char *);
int screen_set_path(struct screen *, const char *);
void screen_push_title(struct screen *);
void screen_pop_title(struct screen *);
void screen_set_progress_bar(struct screen *, enum progress_bar_state, int);
@@ -3636,7 +3640,6 @@ struct session *session_create(const char *, const char *, const char *,
void session_destroy(struct session *, int, const char *);
void session_add_ref(struct session *, const char *);
void session_remove_ref(struct session *, const char *);
char *session_check_name(const char *);
void session_update_activity(struct session *, struct timeval *);
struct session *session_next_session(struct session *, struct sort_criteria *);
struct session *session_previous_session(struct session *,

View File

@@ -357,6 +357,17 @@ static const struct tty_feature tty_feature_sixel = {
TERM_SIXEL
};
/* Terminal supports the OSC 9;4 progress bar. */
static const char *const tty_feature_progressbar_capabilities[] = {
"Spb=\\E]9;4;%p1%d;%p2%d\\E\\\\",
NULL
};
static const struct tty_feature tty_feature_progressbar = {
"progressbar",
tty_feature_progressbar_capabilities,
0
};
/* Available terminal features. */
static const struct tty_feature *const tty_features[] = {
&tty_feature_256,
@@ -372,6 +383,7 @@ static const struct tty_feature *const tty_features[] = {
&tty_feature_mouse,
&tty_feature_osc7,
&tty_feature_overline,
&tty_feature_progressbar,
&tty_feature_rectfill,
&tty_feature_rgb,
&tty_feature_sixel,
@@ -475,45 +487,56 @@ tty_default_features(int *feat, const char *name, u_int version)
"256,RGB,bpaste,clipboard,mouse,strikethrough,title"
{ .name = "mintty",
.features = TTY_FEATURES_BASE_MODERN_XTERM ","
"ccolour,"
"cstyle,"
"extkeys,"
"margins,"
"overline,"
"usstyle"
"ccolour,"
"cstyle,"
"extkeys,"
"margins,"
"overline,"
"usstyle"
},
{ .name = "tmux",
.features = TTY_FEATURES_BASE_MODERN_XTERM ","
"ccolour,"
"cstyle,"
"extkeys,"
"focus,"
"overline,"
"usstyle,"
"hyperlinks"
"ccolour,"
"cstyle,"
"extkeys,"
"focus,"
"overline,"
"usstyle,"
"hyperlinks,"
"progressbar"
},
{ .name = "rxvt-unicode",
.features = "256,"
"bpaste,"
"ccolour,"
"cstyle,"
"mouse,"
"title,"
"ignorefkeys"
"bpaste,"
"ccolour,"
"cstyle,"
"mouse,"
"title,"
"ignorefkeys"
},
{ .name = "iTerm2",
.features = TTY_FEATURES_BASE_MODERN_XTERM ","
"cstyle,"
"extkeys,"
"margins,"
"usstyle,"
"sync,"
"osc7,hyperlinks"
"cstyle,"
"extkeys,"
"margins,"
"usstyle,"
"sync,"
"osc7,"
"hyperlinks,"
"progressbar"
},
{ .name = "foot",
.features = TTY_FEATURES_BASE_MODERN_XTERM ","
"cstyle,"
"extkeys"
"cstyle,"
"extkeys"
},
{ .name = "WezTerm",
.features = TTY_FEATURES_BASE_MODERN_XTERM ","
"ccolour,"
"cstyle,"
"extkeys,"
"focus,"
"usstyle"
},
{ .name = "XTerm",
/*
@@ -522,10 +545,10 @@ tty_default_features(int *feat, const char *name, u_int version)
* secondary DA shows VT420.
*/
.features = TTY_FEATURES_BASE_MODERN_XTERM ","
"ccolour,"
"cstyle,"
"extkeys,"
"focus"
"ccolour,"
"cstyle,"
"extkeys,"
"focus"
}
};
u_int i;

View File

@@ -1640,6 +1640,8 @@ tty_keys_extended_device_attributes(struct tty *tty, const char *buf,
tty_default_features(features, "mintty", 0);
else if (strncmp(tmp, "foot(", 5) == 0)
tty_default_features(features, "foot", 0);
else if (strncmp(tmp, "WezTerm ", 7) == 0)
tty_default_features(features, "WezTerm", 0);
log_debug("%s: received extended DA %.*s", c->name, (int)*size, buf);
free(c->term_type);

View File

@@ -280,6 +280,7 @@ static const struct tty_term_code_entry tty_term_codes[] = {
[TTYC_SMULX] = { TTYCODE_STRING, "Smulx" },
[TTYC_SMUL] = { TTYCODE_STRING, "smul" },
[TTYC_SMXX] = { TTYCODE_STRING, "smxx" },
[TTYC_SPB] = { TTYCODE_STRING, "Spb" },
[TTYC_SS] = { TTYCODE_STRING, "Ss" },
[TTYC_SWD] = { TTYCODE_STRING, "Swd" },
[TTYC_SYNC] = { TTYCODE_STRING, "Sync" },

7
tty.c
View File

@@ -3053,3 +3053,10 @@ tty_clipboard_query(struct tty *tty)
evtimer_add(&tty->clipboard_timer, &tv);
}
}
void
tty_set_progress_bar(struct tty *tty, struct progress_bar *pb)
{
if (tty_term_has(tty->term, TTYC_SPB))
tty_putcode_ii(tty, TTYC_SPB, pb->state, pb->progress);
}

View File

@@ -279,6 +279,7 @@ window_clock_draw_screen(struct window_mode_entry *wme)
memcpy(&gc, &grid_default_cell, sizeof gc);
gc.flags |= GRID_FLAG_NOPALETTE;
gc.bg = colour;
gc.fg = colour;
for (ptr = tim; *ptr != '\0'; ptr++) {
if (*ptr >= '0' && *ptr <= '9')
idx = *ptr - '0';
@@ -299,7 +300,7 @@ window_clock_draw_screen(struct window_mode_entry *wme)
for (i = 0; i < 5; i++) {
screen_write_cursormove(&ctx, x + i, y + j, 0);
if (window_clock_table[idx][j][i])
screen_write_putc(&ctx, &gc, ' ');
screen_write_putc(&ctx, &gc, '#');
}
}
x += 6;

View File

@@ -463,8 +463,10 @@ window_copy_init(struct window_mode_entry *wme,
data->scroll_exit = args_has(args, 'e');
data->hide_position = args_has(args, 'H');
if (base->hyperlinks != NULL)
if (base->hyperlinks != NULL) {
hyperlinks_free(data->screen.hyperlinks);
data->screen.hyperlinks = hyperlinks_copy(base->hyperlinks);
}
data->screen.cx = data->cx;
data->screen.cy = data->cy;
data->mx = data->cx;

View File

@@ -406,9 +406,14 @@ window_remove_ref(struct window *w, const char *from)
void
window_set_name(struct window *w, const char *new_name)
{
free(w->name);
utf8_stravis(&w->name, new_name, VIS_OCTAL|VIS_CSTYLE|VIS_TAB|VIS_NL);
notify_window("window-renamed", w);
char *name;
name = clean_name(new_name, "#");
if (name != NULL) {
free(w->name);
utf8_stravis(&w->name, new_name, VIS_OCTAL|VIS_CSTYLE|VIS_TAB|VIS_NL);
notify_window("window-renamed", w);
}
}
void