Merge remote-tracking branch 'refs/remotes/tmux-openbsd/master'

* refs/remotes/tmux-openbsd/master:
  Add some additional simple events.
  fix window_copy_mouse_in_selection() indentation; ok nicm@
  Work out a single layout for the entire prompt so it can all be drawn together, fixes centered prompts. GitHub issue 5328.
  Allow drag on a selection to modify it, from Michael Grant.
  Make split-window inside a floating pane work more nicely and not immediately create an overlapping pane, from Dane Jensen.
  Set other cell when creating layout set, from Dane Jensen.
  Size comparison should be >= not >, from Yayo Razo.
  Remove worflow from master
This commit is contained in:
tmux update bot
2026-07-13 14:35:35 +00:00
18 changed files with 802 additions and 153 deletions

View File

@@ -1,4 +1,4 @@
/* $OpenBSD: cmd-break-pane.c,v 1.72 2026/07/10 13:38:45 nicm Exp $ */
/* $OpenBSD: cmd-break-pane.c,v 1.73 2026/07/13 13:01:14 nicm Exp $ */
/*
* Copyright (c) 2009 Nicholas Marriott <nicholas.marriott@gmail.com>
@@ -98,9 +98,9 @@ cmd_break_pane_exec(struct cmd *self, struct cmdq_item *item)
struct session *src_s = source->s;
struct session *dst_s = target->s;
struct window_pane *wp = source->wp;
struct window *w = wl->window;
struct window *w = wl->window, *old_w = w;
char *cause, *cp;
int idx = target->idx, before;
int idx = target->idx, before, old_idx = wl->idx;
const char *template, *name = args_get(args, 'n');
if (args_has(args, 'W'))
@@ -137,6 +137,7 @@ cmd_break_pane_exec(struct cmd *self, struct cmdq_item *item)
wl = winlink_find_by_window(&dst_s->windows, w);
if (wl == NULL)
return (CMD_RETURN_ERROR);
window_fire_pane_moved(wp, old_w, old_idx, w, wl->idx);
goto out;
}
if (idx != -1 && winlink_find_by_index(&dst_s->windows, idx) != NULL) {
@@ -175,6 +176,8 @@ cmd_break_pane_exec(struct cmd *self, struct cmdq_item *item)
idx = -1 - options_get_number(dst_s->options, "base-index");
wl = session_attach(dst_s, w, idx, &cause); /* can't fail */
window_remove_ref(w, __func__);
events_fire_window("window-created", w);
window_fire_pane_moved(wp, old_w, old_idx, w, wl->idx);
if (!args_has(args, 'd')) {
session_select(dst_s, wl->idx);
cmd_find_from_session(current, dst_s, 0);

View File

@@ -1,4 +1,4 @@
/* $OpenBSD: cmd-join-pane.c,v 1.69 2026/07/10 13:38:45 nicm Exp $ */
/* $OpenBSD: cmd-join-pane.c,v 1.71 2026/07/13 13:01:14 nicm Exp $ */
/*
* Copyright (c) 2011 George Nachman <tmux@georgester.com>
@@ -458,6 +458,13 @@ cmd_join_pane_exec(struct cmd *self, struct cmdq_item *item)
return (CMD_RETURN_ERROR);
}
if (args_has(args, 'h'))
flags |= SPAWN_HORIZONTAL;
if (args_has(args, 'b'))
flags |= SPAWN_BEFORE;
if (args_has(args, 'f'))
flags |= SPAWN_FULLSIZE;
lc = layout_get_tiled_cell(item, args, dst_w, dst_wp, flags, &cause);
if (cause != NULL) {
cmdq_error(item, "size or position %s", cause);
@@ -498,6 +505,7 @@ cmd_join_pane_exec(struct cmd *self, struct cmdq_item *item)
} else
server_status_session(dst_s);
window_fire_pane_moved(src_wp, src_w, src_wl->idx, dst_w, dst_idx);
if (window_count_panes(src_w, 1) == 0)
server_kill_window(src_w, 1);
else

View File

@@ -1,4 +1,4 @@
/* $OpenBSD: cmd-split-window.c,v 1.141 2026/07/10 13:38:45 nicm Exp $ */
/* $OpenBSD: cmd-split-window.c,v 1.142 2026/07/13 10:03:27 nicm Exp $ */
/*
* Copyright (c) 2009 Nicholas Marriott <nicholas.marriott@gmail.com>
@@ -61,8 +61,8 @@ const struct cmd_entry cmd_split_window_entry = {
.usage = "[-bdefhIklPvWZ] [-c start-directory] [-e environment] "
"[-F format] [-l size] [-m message] [-p percentage] "
"[-s style] [-S active-border-style] "
"[-R inactive-border-style] [-T title] " CMD_TARGET_PANE_USAGE " "
"[shell-command [argument ...]]",
"[-R inactive-border-style] [-T title] "
CMD_TARGET_PANE_USAGE " [shell-command [argument ...]]",
.target = { 't', CMD_FIND_PANE, 0 },
@@ -95,14 +95,23 @@ cmd_split_window_exec(struct cmd *self, struct cmdq_item *item)
if (cmd_get_entry(self) == &cmd_new_pane_entry)
is_floating = !args_has(args, 'L');
else
else {
is_floating = window_pane_is_floating(wp);
flags |= SPAWN_SPLIT;
}
flags = is_floating ? SPAWN_FLOATING : 0;
if (is_floating)
flags |= SPAWN_FLOATING;
if (args_has(args, 'h'))
flags |= SPAWN_HORIZONTAL;
if (args_has(args, 'b'))
flags |= SPAWN_BEFORE;
if (args_has(args, 'f'))
flags |= SPAWN_FULLSIZE;
if (args_has(args, 'd'))
flags |= SPAWN_DETACHED;
if (args_has(args, 'Z'))
flags |= SPAWN_ZOOM;
input = args_has(args, 'I');
if (input || (count == 1 && *args_string(args, 0) == '\0'))
@@ -130,9 +139,10 @@ cmd_split_window_exec(struct cmd *self, struct cmdq_item *item)
}
}
if (is_floating)
lc = layout_get_floating_cell(item, args, lines, w, wp, &cause);
else
if (flags & SPAWN_FLOATING) {
lc = layout_get_floating_cell(item, args, lines, w, wp, flags,
&cause);
} else
lc = layout_get_tiled_cell(item, args, w, wp, flags, &cause);
if (cause != NULL) {
cmdq_error(item, "%s", cause);
@@ -158,12 +168,7 @@ cmd_split_window_exec(struct cmd *self, struct cmdq_item *item)
sc.idx = -1;
sc.cwd = args_get(args, 'c');
sc.flags = flags;
if (args_has(args, 'd'))
sc.flags |= SPAWN_DETACHED;
if (args_has(args, 'Z'))
sc.flags |= SPAWN_ZOOM;
if ((new_wp = spawn_pane(&sc, &cause)) == NULL) {
cmdq_error(item, "create pane failed: %s", cause);
@@ -239,10 +244,10 @@ cmd_split_window_exec(struct cmd *self, struct cmdq_item *item)
break;
}
}
if (!args_has(args, 'd'))
if (~flags & SPAWN_DETACHED)
cmd_find_from_winlink_pane(current, wl, new_wp, 0);
if (!is_floating) {
if (~flags & SPAWN_FLOATING) {
window_pop_zoom(wp->window);
server_redraw_window(wp->window);
}

View File

@@ -1,4 +1,4 @@
/* $OpenBSD: cmd-swap-pane.c,v 1.53 2026/07/10 13:38:45 nicm Exp $ */
/* $OpenBSD: cmd-swap-pane.c,v 1.54 2026/07/13 13:01:14 nicm Exp $ */
/*
* Copyright (c) 2009 Nicholas Marriott <nicholas.marriott@gmail.com>
@@ -68,11 +68,14 @@ cmd_swap_pane_exec(struct cmd *self, struct cmdq_item *item)
struct window_pane *tmp_wp, *src_wp, *dst_wp;
struct layout_cell *src_lc, *dst_lc;
u_int sx, sy, xoff, yoff;
int src_idx, dst_idx;
dst_w = target->wl->window;
dst_wp = target->wp;
dst_idx = target->wl->idx;
src_w = source->wl->window;
src_wp = source->wp;
src_idx = source->wl->idx;
if (window_push_zoom(dst_w, 0, args_has(args, 'Z')))
server_redraw_window(dst_w);
@@ -180,6 +183,10 @@ cmd_swap_pane_exec(struct cmd *self, struct cmdq_item *item)
redraw_invalidate_scene(dst_w);
server_redraw_window(dst_w);
if (src_w != dst_w) {
window_fire_pane_moved(src_wp, src_w, src_idx, dst_w, dst_idx);
window_fire_pane_moved(dst_wp, dst_w, dst_idx, src_w, src_idx);
}
events_fire_window("window-layout-changed", src_w);
if (src_w != dst_w)
events_fire_window("window-layout-changed", dst_w);

4
cmd.c
View File

@@ -1,4 +1,4 @@
/* $OpenBSD: cmd.c,v 1.186 2026/07/12 20:35:52 nicm Exp $ */
/* $OpenBSD: cmd.c,v 1.187 2026/07/13 09:37:39 nicm Exp $ */
/*
* Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>
@@ -871,7 +871,7 @@ cmd_template_replace(const char *template, const char *s, int idx)
ptr++;
slen = strlen(s);
if (slen > SIZE_MAX / 3 || len > SIZE_MAX - (slen * 3) - 1)
if (slen >= SIZE_MAX / 3 || len > SIZE_MAX - (slen * 3) - 1)
fatalx("argument too long");
buf = xrealloc(buf, len + (slen * 3) + 1);
for (cp = s; *cp != '\0'; cp++) {

View File

@@ -1,4 +1,4 @@
/* $OpenBSD: input.c,v 1.266 2026/07/10 15:20:06 nicm Exp $ */
/* $OpenBSD: input.c,v 1.267 2026/07/13 13:01:14 nicm Exp $ */
/*
* Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>
@@ -1316,8 +1316,10 @@ input_c0_dispatch(struct input_ctx *ictx)
case '\000': /* NUL */
break;
case '\007': /* BEL */
if (wp != NULL)
if (wp != NULL) {
events_fire_pane("pane-bell", wp);
alerts_queue(wp->window, WINDOW_BELL);
}
break;
case '\010': /* BS */
screen_write_backspace(sctx);
@@ -2199,7 +2201,7 @@ input_csi_dispatch_winops(struct input_ctx *ictx)
screen_pop_title(sctx->s);
if (wp == NULL)
break;
events_fire_pane("pane-title-changed", wp);
input_fire_pane_title_changed(wp, sctx->s->title);
server_redraw_window_borders(w);
server_status_window(w);
break;

View File

@@ -1,4 +1,4 @@
/* $OpenBSD: layout-set.c,v 1.39 2026/07/10 13:38:45 nicm Exp $ */
/* $OpenBSD: layout-set.c,v 1.40 2026/07/13 09:42:12 nicm Exp $ */
/*
* Copyright (c) 2009 Nicholas Marriott <nicholas.marriott@gmail.com>
@@ -135,6 +135,21 @@ layout_set_first_tiled(struct window *w)
return (NULL);
}
static void
layout_set_link_floating(struct window *w, struct layout_cell *lcroot)
{
struct window_pane *wp;
struct layout_cell *lc;
TAILQ_FOREACH(wp, &w->panes, entry) {
lc = wp->layout_cell;
if (!layout_cell_is_tiled(lc)) {
TAILQ_INSERT_TAIL(&lcroot->cells, lc, entry);
lc->parent = lcroot;
}
}
}
static void
layout_set_even(struct window *w, enum layout_type type)
{
@@ -267,6 +282,8 @@ layout_set_main_h(struct window *w)
wp = TAILQ_NEXT(wp, entry);
TAILQ_INSERT_TAIL(&lcroot->cells, wp->layout_cell, entry);
wp->layout_cell->parent = lcroot;
layout_set_size(wp->layout_cell, sx, otherh, 0, 0);
layout_set_link_floating(w, lcroot);
} else {
lcother = layout_create_cell(lcroot);
layout_set_size(lcother, sx, otherh, 0, 0);
@@ -364,6 +381,8 @@ layout_set_main_h_mirrored(struct window *w)
wp = TAILQ_NEXT(wp, entry);
TAILQ_INSERT_HEAD(&lcroot->cells, wp->layout_cell, entry);
wp->layout_cell->parent = lcroot;
layout_set_size(wp->layout_cell, sx, otherh, 0, 0);
layout_set_link_floating(w, lcroot);
} else {
lcother = layout_create_cell(lcroot);
layout_set_size(lcother, sx, otherh, 0, 0);
@@ -461,6 +480,8 @@ layout_set_main_v(struct window *w)
wp = TAILQ_NEXT(wp, entry);
TAILQ_INSERT_TAIL(&lcroot->cells, wp->layout_cell, entry);
wp->layout_cell->parent = lcroot;
layout_set_size(wp->layout_cell, otherw, sy, 0, 0);
layout_set_link_floating(w, lcroot);
} else {
lcother = layout_create_cell(lcroot);
layout_make_node(lcother, LAYOUT_TOPBOTTOM);
@@ -559,6 +580,8 @@ layout_set_main_v_mirrored(struct window *w)
wp = TAILQ_NEXT(wp, entry);
TAILQ_INSERT_HEAD(&lcroot->cells, wp->layout_cell, entry);
wp->layout_cell->parent = lcroot;
layout_set_size(wp->layout_cell, otherw, sy, 0, 0);
layout_set_link_floating(w, lcroot);
} else {
lcother = layout_create_cell(lcroot);
layout_make_node(lcother, LAYOUT_TOPBOTTOM);
@@ -700,6 +723,7 @@ layout_set_tiled(struct window *w)
w->sy - used);
}
layout_set_link_floating(w, lcroot);
layout_fix_offsets(w);
layout_fix_panes(w, NULL);

158
layout.c
View File

@@ -1,4 +1,4 @@
/* $OpenBSD: layout.c,v 1.93 2026/07/10 13:38:45 nicm Exp $ */
/* $OpenBSD: layout.c,v 1.94 2026/07/13 10:03:27 nicm Exp $ */
/*
* Copyright (c) 2009 Nicholas Marriott <nicholas.marriott@gmail.com>
@@ -1598,7 +1598,7 @@ layout_get_tiled_cell(struct cmdq_item *item, struct args *args,
struct window *w, struct window_pane *wp, int flags, char **cause)
{
struct layout_cell *lc;
enum layout_type type;
enum layout_type type = LAYOUT_TOPBOTTOM;
u_int curval;
int size = -1;
char *error = NULL;
@@ -1608,12 +1608,11 @@ layout_get_tiled_cell(struct cmdq_item *item, struct args *args,
return (NULL);
}
type = LAYOUT_TOPBOTTOM;
if (args_has(args, 'h'))
if (flags & SPAWN_HORIZONTAL)
type = LAYOUT_LEFTRIGHT;
if (args_has(args, 'l') || args_has(args, 'p')) {
if (args_has(args, 'f')) {
if (flags & SPAWN_FULLSIZE) {
if (type == LAYOUT_TOPBOTTOM)
curval = w->sy;
else
@@ -1641,12 +1640,7 @@ layout_get_tiled_cell(struct cmdq_item *item, struct args *args,
return (NULL);
}
if (args_has(args, 'b'))
flags |= SPAWN_BEFORE;
if (args_has(args, 'f'))
flags |= SPAWN_FULLSIZE;
window_push_zoom(wp->window, 1, args_has(args, 'Z'));
window_push_zoom(wp->window, 1, (flags & SPAWN_ZOOM));
lc = layout_split_pane(wp, type, size, flags);
if (lc == NULL)
*cause = xstrdup("no space for a new pane");
@@ -1656,17 +1650,24 @@ layout_get_tiled_cell(struct cmdq_item *item, struct args *args,
struct layout_cell *
layout_get_floating_cell(struct cmdq_item *item, struct args *args,
enum pane_lines lines, struct window *w, struct window_pane *wp,
enum pane_lines lines, struct window *w, struct window_pane *wp, int flags,
char **cause)
{
struct layout_cell *lcnew;
struct layout_cell *lcnew, *lc = wp->layout_cell;
struct layout_geometry fg;
layout_geometry_init(&fg);
if (layout_floating_args_parse(item, args, lines, w, &fg, cause) != 0)
return (NULL);
if (flags & SPAWN_SPLIT) {
if (layout_split_floating_cell(lc, w, &fg, lines, flags, cause)
!= 0)
return (NULL);
} else {
if (layout_floating_args_parse(item, args, lines, w, &fg, cause)
!= 0)
return (NULL);
}
window_push_zoom(wp->window, 1, args_has(args, 'Z'));
window_push_zoom(wp->window, 1, (flags & SPAWN_ZOOM));
lcnew = layout_floating_pane(w, wp, &fg);
return (lcnew);
}
@@ -1766,6 +1767,131 @@ layout_floating_args_parse(struct cmdq_item *item, struct args *args,
return (0);
}
int
layout_split_floating_cell(struct layout_cell *lc, struct window *w,
struct layout_geometry *out, enum pane_lines lines, int flags,
char **cause)
{
struct layout_geometry old, new;
int tborder = 1, bborder = w->sy - 1;
int lborder = 3, rborder = w->sx - 3;
int border = lines != PANE_LINES_NONE ? 1 : 0;
int size, space;
/* First, move the target cell in-bounds. */
memcpy(&old, &lc->g, sizeof old);
if (lborder > old.xoff - border)
old.xoff = lborder + border;
if (rborder < old.xoff + (int)old.sx + border)
old.xoff = rborder - (int)old.sx - border;
if (tborder > old.yoff - border)
old.yoff = tborder + border;
if (bborder < old.yoff + (int)old.sy + border)
old.yoff = bborder - (int)old.sy - border;
/* Move the new cell to its ideal position. */
memcpy(&new, &old, sizeof new);
if (flags & SPAWN_HORIZONTAL) {
if (flags & SPAWN_BEFORE)
new.xoff -= old.sx + 2 * border;
else
new.xoff += old.sx + 2 * border;
} else {
if (flags & SPAWN_BEFORE)
new.yoff -= old.sy + 2 * border;
else
new.yoff += old.sy + 2 * border;
}
/*
* The position of the new cell is checked to see if it is in bounds.
* If it isn't, the availible space is split and equally given to both
* cells. Only one border is check because the target cell is in bounds
* already.
*/
if (lborder > new.xoff - border) {
/*
* The space for both panes is calculated. Since the offsets are
* associated to where pane contents start, we remove pane
* borders from the space. '1' is added in case the space is
* odd.
*/
space = old.xoff + old.sx - lborder - 3 * border + 1;
size = space / 2;
new.sx = size;
old.sx = size;
new.xoff = lborder + border;
old.xoff = new.xoff + new.sx + 2 * border;
/*
* If the original space was to be odd (now even), subtract 1
* from the rightmost cell
*/
if (space % 2 == 0)
old.sx -= 1;
} else if (rborder < new.xoff + (int)new.sx + border) {
space = rborder - old.xoff - 3 * border + 1;
size = space / 2;
new.sx = size;
old.sx = size;
new.xoff = old.xoff + old.sx + 2 * border;
if (space % 2 == 0)
new.sx -= 1;
} else if (tborder > new.yoff - border) {
space = old.sy + old.yoff - tborder - 3 * border + 1;
size = space / 2;
new.sy = size;
old.sy = size;
new.yoff = tborder + border;
old.yoff = new.yoff + new.sy + 2 * border;
if (space % 2 == 0)
old.sy -= 1;
} else if (bborder < new.yoff + (int)new.sy + border) {
space = bborder - old.yoff - 3 * border + 1;
size = space / 2;
new.sy = size;
old.sy = size;
new.yoff = old.yoff + old.sy + 2 * border;
if (space % 2 == 0)
new.sy -= 1;
}
/*
* Expand the cell to occupy the whole availible space where it was
* spawned.
*/
if (flags & SPAWN_FULLSIZE) {
if (flags & SPAWN_HORIZONTAL) {
new.yoff = tborder + border;
new.sy = bborder - tborder - 2 * border;
if (flags & SPAWN_BEFORE) {
new.xoff = lborder + border;
new.sx = old.xoff - new.xoff - 2 * border;
} else {
new.sx = rborder - new.xoff - border;
}
} else {
new.xoff = lborder + border;
new.sx = rborder - lborder - 2 * border;
if (flags & SPAWN_BEFORE) {
new.yoff = tborder + border;
new.sy = old.yoff - new.yoff - 2 * border;
} else {
new.sy = bborder - new.yoff - border;
}
}
}
if (new.sx < PANE_MINIMUM || new.sy < PANE_MINIMUM ||
old.sx < PANE_MINIMUM || old.sy < PANE_MINIMUM) {
*cause = xstrdup("no space for a new pane");
return (-1);
}
layout_set_size(lc, old.sx, old.sy, old.xoff, old.yoff);
memcpy(out, &new, sizeof *out);
return (0);
}
/*
* Removes a cell from the tiled layout by giving the cell's space to the
* nearest neighbour.

View File

@@ -1,4 +1,4 @@
/* $OpenBSD: options-table.c,v 1.233 2026/07/10 15:20:06 nicm Exp $ */
/* $OpenBSD: options-table.c,v 1.234 2026/07/13 13:01:14 nicm Exp $ */
/*
* Copyright (c) 2011 Nicholas Marriott <nicholas.marriott@gmail.com>
@@ -1924,6 +1924,8 @@ const struct options_table_entry options_table[] = {
OPTIONS_TABLE_HOOK("alert-silence", ""),
OPTIONS_TABLE_HOOK("client-active", ""),
OPTIONS_TABLE_HOOK("client-attached", ""),
OPTIONS_TABLE_HOOK("client-created", ""),
OPTIONS_TABLE_HOOK("client-closed", ""),
OPTIONS_TABLE_HOOK("client-detached", ""),
OPTIONS_TABLE_HOOK("client-focus-in", ""),
OPTIONS_TABLE_HOOK("client-focus-out", ""),
@@ -1933,6 +1935,7 @@ const struct options_table_entry options_table[] = {
OPTIONS_TABLE_HOOK("client-dark-theme", ""),
OPTIONS_TABLE_HOOK("command-error", ""),
OPTIONS_TABLE_HOOK("marked-pane-changed", ""),
OPTIONS_TABLE_PANE_HOOK("pane-bell", ""),
OPTIONS_TABLE_PANE_HOOK("pane-command-finished", ""),
OPTIONS_TABLE_PANE_HOOK("pane-command-started", ""),
OPTIONS_TABLE_PANE_HOOK("pane-created", ""),
@@ -1943,6 +1946,7 @@ const struct options_table_entry options_table[] = {
OPTIONS_TABLE_PANE_HOOK("pane-mode-changed", ""),
OPTIONS_TABLE_PANE_HOOK("pane-mode-entered", ""),
OPTIONS_TABLE_PANE_HOOK("pane-mode-exited", ""),
OPTIONS_TABLE_PANE_HOOK("pane-moved", ""),
OPTIONS_TABLE_PANE_HOOK("pane-prompt-closed", ""),
OPTIONS_TABLE_PANE_HOOK("pane-prompt-opened", ""),
OPTIONS_TABLE_PANE_HOOK("pane-resized", ""),
@@ -1951,8 +1955,12 @@ const struct options_table_entry options_table[] = {
OPTIONS_TABLE_PANE_HOOK("pane-title-changed", ""),
OPTIONS_TABLE_HOOK("session-closed", ""),
OPTIONS_TABLE_HOOK("session-created", ""),
OPTIONS_TABLE_HOOK("session-added-to-group", ""),
OPTIONS_TABLE_HOOK("session-renamed", ""),
OPTIONS_TABLE_HOOK("session-removed-from-group", ""),
OPTIONS_TABLE_HOOK("session-window-changed", ""),
OPTIONS_TABLE_WINDOW_HOOK("window-created", ""),
OPTIONS_TABLE_WINDOW_HOOK("window-closed", ""),
OPTIONS_TABLE_WINDOW_HOOK("window-layout-changed", ""),
OPTIONS_TABLE_HOOK("window-linked", ""),
OPTIONS_TABLE_WINDOW_HOOK("window-pane-changed", ""),

301
prompt.c
View File

@@ -1,4 +1,4 @@
/* $OpenBSD: prompt.c,v 1.4 2026/06/26 14:40:30 nicm Exp $ */
/* $OpenBSD: prompt.c,v 1.5 2026/07/13 10:29:17 nicm Exp $ */
/*
* Copyright (c) 2026 Nicholas Marriott <nicholas.marriott@gmail.com>
@@ -42,6 +42,8 @@ struct prompt {
char *word_separators;
struct grid_cell style;
struct grid_cell command_style;
char *style_str;
char *command_style_str;
enum screen_cursor_style cstyle;
enum screen_cursor_style command_cstyle;
int ccolour;
@@ -61,11 +63,30 @@ struct prompt {
char *complete_display;
};
struct prompt_layout {
u_int area_x;
u_int area_width;
u_int content_x;
u_int content_width;
u_int label_width;
u_int input_x;
u_int cursor_x;
u_int input_offset;
u_int input_width;
};
static char *prompt_complete(struct prompt *, const char *, u_int);
static void prompt_clear_complete(struct prompt *);
static char *prompt_expand(struct prompt *);
static struct format_tree *prompt_format_tree(struct prompt *);
static char *prompt_expand1(struct prompt *, struct format_tree *);
static void prompt_effective_style(struct prompt *, struct style *,
struct format_tree *);
static void prompt_layout(struct prompt *, u_int, u_int,
struct prompt_layout *, char **, struct style *);
static int prompt_replace_complete(struct prompt *, const char *);
static u_int prompt_width(struct prompt *, u_int);
/* Get prompt flags as a string. */
static const char *
@@ -120,6 +141,8 @@ prompt_set_options(struct prompt_create_data *pd, struct session *s)
style_apply(&pd->style, oo, "message-style", NULL);
style_apply(&pd->command_style, oo, "message-command-style", NULL);
pd->style_str = options_get_string(oo, "message-style");
pd->command_style_str = options_get_string(oo, "message-command-style");
n = options_get_number(oo, "prompt-cursor-style");
screen_set_cursor_style(n, &pd->cstyle, &pd->cmode);
n = options_get_number(oo, "prompt-command-cursor-style");
@@ -179,6 +202,8 @@ prompt_create(const struct prompt_create_data *pd)
memcpy(&pr->style, &pd->style, sizeof pr->style);
memcpy(&pr->command_style, &pd->command_style,
sizeof pr->command_style);
pr->style_str = xstrdup(pd->style_str);
pr->command_style_str = xstrdup(pd->command_style_str);
pr->cstyle = pd->cstyle;
pr->command_cstyle = pd->command_cstyle;
pr->ccolour = pd->ccolour;
@@ -201,6 +226,8 @@ prompt_free(struct prompt *pr)
if (pr->freecb != NULL && pr->data != NULL)
pr->freecb(pr->data);
free(pr->message_format);
free(pr->style_str);
free(pr->command_style_str);
free(pr->word_separators);
free(pr->last);
free(pr->string);
@@ -321,16 +348,17 @@ prompt_redraw_character(struct screen_write_ctx *ctx, u_int offset,
* 0 otherwise.
*/
static int
prompt_redraw_quote(const struct prompt *pr, u_int pcursor,
struct screen_write_ctx *ctx, u_int offset, u_int pwidth, u_int *width,
prompt_redraw_quote(const struct prompt *pr, u_int pcursor, u_int input_x,
struct screen_write_ctx *ctx, u_int offset, u_int pw, u_int *w,
struct grid_cell *gc)
{
struct utf8_data ud;
if (pr->flags & PROMPT_QUOTENEXT && ctx->s->cx == pcursor + 1) {
if (pr->flags & PROMPT_QUOTENEXT &&
pcursor >= offset &&
ctx->s->cx == input_x + pcursor - offset) {
utf8_set(&ud, '^');
return (prompt_redraw_character(ctx, offset, pwidth,
width, gc, &ud));
return (prompt_redraw_character(ctx, offset, pw, w, gc, &ud));
}
return (1);
}
@@ -368,12 +396,12 @@ prompt_draw_complete(struct prompt *pr, struct screen_write_ctx *ctx, u_int ax,
free(ud);
}
/* Expand prompt string using the current input. */
static char *
prompt_expand(struct prompt *pr)
/* Create the prompt format tree using the current input. */
static struct format_tree *
prompt_format_tree(struct prompt *pr)
{
struct format_tree *ft;
char *expanded, *prompt, *tmp;
char *tmp;
if (cmd_find_valid_state(&pr->state))
ft = format_create_from_state(NULL, NULL, &pr->state);
@@ -385,31 +413,145 @@ prompt_expand(struct prompt *pr)
format_add(ft, "prompt_flags", "%s", prompt_flags_to_string(pr->flags));
format_add(ft, "prompt_type", "%s", prompt_type_string(pr->type));
prompt = format_expand_time(ft, pr->string);
format_add(ft, "message", "%s", prompt);
if (pr->flags & PROMPT_COMMANDMODE)
format_add(ft, "command_prompt", "1");
else
format_add(ft, "command_prompt", "0");
return (ft);
}
/* Expand prompt string using the current input. */
static char *
prompt_expand1(struct prompt *pr, struct format_tree *ft)
{
char *expanded, *prompt;
prompt = format_expand_time(ft, pr->string);
format_add(ft, "message", "%s", prompt);
expanded = format_expand_time(ft, pr->message_format);
free(prompt);
format_free(ft);
return (expanded);
}
/* Work out the width used by the prompt string. */
static u_int
prompt_width(struct prompt *pr, u_int aw)
/* Get the effective message style for the current prompt. */
static void
prompt_effective_style(struct prompt *pr, struct style *sy,
struct format_tree *ft)
{
char *expanded;
u_int start;
const char *s;
struct grid_cell *gc;
char *expanded;
expanded = prompt_expand(pr);
start = format_width(expanded);
if (start > aw)
start = aw;
free(expanded);
return (start);
if (pr->flags & PROMPT_COMMANDMODE) {
s = pr->command_style_str;
gc = &pr->command_style;
} else {
s = pr->style_str;
gc = &pr->style;
}
style_set(sy, gc);
if (s != NULL) {
expanded = format_expand_time(ft, s);
if (style_parse(sy, &grid_default_cell, expanded) != 0)
style_set(sy, gc);
free(expanded);
}
}
/* Work out where the editable prompt content appears. */
static void
prompt_layout(struct prompt *pr, u_int ax, u_int aw, struct prompt_layout *pl,
char **expanded, struct style *sy)
{
char *local = NULL;
struct format_tree *ft;
u_int pcursor, pwidth, end, width, offset, avail;
memset(pl, 0, sizeof *pl);
pl->area_x = ax;
pl->area_width = aw;
ft = prompt_format_tree(pr);
if (sy != NULL)
prompt_effective_style(pr, sy, ft);
if (expanded != NULL)
*expanded = prompt_expand1(pr, ft);
else {
local = prompt_expand1(pr, ft);
expanded = &local;
}
format_free(ft);
if (aw == 0) {
free(local);
return;
}
pl->label_width = format_width(*expanded);
if (pl->label_width > aw)
pl->label_width = aw;
pcursor = utf8_strwidth(pr->buffer, pr->index);
pwidth = utf8_strwidth(pr->buffer, -1);
if (pr->flags & PROMPT_QUOTENEXT)
pwidth++;
avail = aw - pl->label_width;
if (avail == 0) {
pl->input_offset = 0;
pl->input_width = 0;
pl->cursor_x = pl->label_width;
} else {
if (pcursor >= avail) {
offset = (pcursor - avail) + 1;
width = avail;
} else {
offset = 0;
width = pwidth;
}
if (width > avail)
width = avail;
pl->input_offset = offset;
pl->input_width = width;
pl->cursor_x = pl->label_width + pcursor - offset;
}
pl->content_width = pl->label_width + pl->input_width;
if (pr->complete_display != NULL &&
pr->index == utf8_strlen(pr->buffer) &&
pl->cursor_x < aw) {
avail = aw - pl->cursor_x;
width = utf8_cstrwidth(pr->complete_display);
if (width > avail)
width = avail;
end = pl->cursor_x + width;
if (end > pl->content_width)
pl->content_width = end;
}
if (pl->content_width > aw)
pl->content_width = aw;
if (sy != NULL) {
switch (sy->align) {
case STYLE_ALIGN_CENTRE:
case STYLE_ALIGN_ABSOLUTE_CENTRE:
pl->content_x = ax + (aw - pl->content_width) / 2;
break;
case STYLE_ALIGN_RIGHT:
pl->content_x = ax + aw - pl->content_width;
break;
default:
pl->content_x = ax;
break;
}
} else
pl->content_x = ax;
pl->input_x = pl->content_x + pl->label_width;
pl->cursor_x += pl->content_x;
free(local);
}
/* Choose a completion from a mouse position. */
@@ -460,70 +602,59 @@ prompt_draw(struct prompt *pr, struct prompt_draw_data *pd)
{
struct screen_write_ctx *ctx = pd->ctx;
struct screen *s = ctx->s;
u_int ax = pd->area_x, py = pd->prompt_line;
u_int aw = pd->area_width, *cx = pd->cursor_x;
u_int ax = pd->area_x, py = pd->prompt_line, *cx;
u_int aw = pd->area_width;
struct grid_cell gc;
u_int i, offset, left, start, width;
u_int pcursor, pwidth;
struct prompt_layout pl;
struct style sy;
u_int i, width, pcursor;
char *expanded;
/* Choose the cursor colour and style for this prompt. */
if (pr->flags & PROMPT_COMMANDMODE) {
memcpy(&gc, &pr->command_style, sizeof gc);
s->default_cstyle = pr->command_cstyle;
s->default_mode = pr->command_cmode;
s->default_ccolour = pr->command_ccolour;
} else {
memcpy(&gc, &pr->style, sizeof gc);
s->default_cstyle = pr->cstyle;
s->default_mode = pr->cmode;
s->default_ccolour = pr->ccolour;
}
expanded = prompt_expand(pr);
start = format_width(expanded);
if (start > aw)
start = aw;
*cx = ax + start;
prompt_layout(pr, ax, aw, &pl, &expanded, &sy);
memcpy(&gc, &sy.gc, sizeof gc);
cx = pd->cursor_x;
*cx = pl.cursor_x;
screen_write_cursormove(ctx, ax, py, 0);
format_draw(ctx, &gc, aw, expanded, NULL, 0);
screen_write_cursormove(ctx, ax + start, py, 0);
free(expanded);
left = aw - start;
if (left == 0)
return;
if (sy.fill != 8)
screen_write_clearcharacter(ctx, aw, sy.fill);
pcursor = utf8_strwidth(pr->buffer, pr->index);
pwidth = utf8_strwidth(pr->buffer, -1);
if (pr->flags & PROMPT_QUOTENEXT)
pwidth++;
if (pcursor >= left) {
/*
* The cursor would be outside the screen so start drawing
* with it on the right.
*/
offset = (pcursor - left) + 1;
pwidth = left;
} else
offset = 0;
if (pwidth > left)
pwidth = left;
*cx = ax + start + pcursor - offset;
width = 0;
for (i = 0; pr->buffer[i].size != 0; i++) {
if (!prompt_redraw_quote(pr, pcursor, ctx, offset, pwidth,
&width, &gc))
break;
if (!prompt_redraw_character(ctx, offset, pwidth, &width, &gc,
&pr->buffer[i]))
break;
if (pl.content_width != 0) {
screen_write_cursormove(ctx, pl.content_x, py, 0);
if (pl.label_width != 0)
format_draw(ctx, &gc, pl.label_width, expanded, NULL,
0);
screen_write_cursormove(ctx, pl.input_x, py, 0);
width = 0;
for (i = 0; pr->buffer[i].size != 0; i++) {
if (!prompt_redraw_quote(pr, pcursor, pl.input_x,
ctx, pl.input_offset, pl.input_width, &width,
&gc))
break;
if (!prompt_redraw_character(ctx, pl.input_offset,
pl.input_width, &width, &gc, &pr->buffer[i]))
break;
}
prompt_redraw_quote(pr, pcursor, pl.input_x, ctx,
pl.input_offset, pl.input_width, &width, &gc);
prompt_draw_complete(pr, ctx, pl.content_x, pl.content_width,
pl.cursor_x, py, &gc);
}
prompt_redraw_quote(pr, pcursor, ctx, offset, pwidth, &width, &gc);
prompt_draw_complete(pr, ctx, ax, aw, *cx, py, &gc);
free(expanded);
}
/* Move cursor in prompt from a mouse position. */
@@ -532,36 +663,34 @@ prompt_mouse(struct prompt *pr, u_int x, u_int ax, u_int aw, int *redraw)
{
struct utf8_data *ud;
enum prompt_key_result result;
u_int cx, start, left, pcursor, pwidth, offset, width;
u_int target;
struct prompt_layout pl;
struct style sy;
char *expanded;
u_int pwidth, width, target;
size_t idx;
if (x < ax || x >= ax + aw)
return (PROMPT_KEY_NOT_HANDLED);
start = prompt_width(pr, aw);
left = aw - start;
if (left == 0)
prompt_layout(pr, ax, aw, &pl, &expanded, &sy);
free(expanded);
if (pl.input_width == 0)
return (PROMPT_KEY_HANDLED);
pcursor = utf8_strwidth(pr->buffer, pr->index);
pwidth = utf8_strwidth(pr->buffer, -1);
if (pr->flags & PROMPT_QUOTENEXT)
pwidth++;
if (pcursor >= left)
offset = (pcursor - left) + 1;
else
offset = 0;
cx = ax + start + pcursor - offset;
result = prompt_mouse_complete(pr, x, cx, ax, aw, redraw);
result = prompt_mouse_complete(pr, x, pl.cursor_x, pl.content_x,
pl.content_width, redraw);
if (result != PROMPT_KEY_NOT_HANDLED)
return (result);
if (x <= ax + start)
target = offset;
if (x <= pl.input_x)
target = pl.input_offset;
else
target = offset + x - (ax + start);
target = pl.input_offset + x - pl.input_x;
if (target > pwidth)
target = pwidth;

View File

@@ -1,4 +1,4 @@
/* $OpenBSD: resize.c,v 1.56 2026/07/10 13:38:45 nicm Exp $ */
/* $OpenBSD: resize.c,v 1.57 2026/07/13 13:01:14 nicm Exp $ */
/*
* Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>
@@ -22,9 +22,27 @@
#include "tmux.h"
static void
resize_fire_window_resized(struct window *w, u_int old_sx, u_int old_sy)
{
struct event_payload *ep;
struct cmd_find_state fs;
ep = event_payload_create();
cmd_find_from_window(&fs, w, 0);
event_payload_set_target(ep, &fs);
event_payload_set_window(ep, "window", w);
event_payload_set_uint(ep, "width", w->sx);
event_payload_set_uint(ep, "height", w->sy);
event_payload_set_uint(ep, "old_width", old_sx);
event_payload_set_uint(ep, "old_height", old_sy);
events_fire("window-resized", ep);
}
void
resize_window(struct window *w, u_int sx, u_int sy, int xpixel, int ypixel)
{
u_int old_sx = w->sx, old_sy = w->sy;
int zoomed;
/* Check size limits. */
@@ -61,7 +79,7 @@ resize_window(struct window *w, u_int sx, u_int sy, int xpixel, int ypixel)
tty_update_window_offset(w);
server_redraw_window(w);
events_fire_window("window-layout-changed", w);
events_fire_window("window-resized", w);
resize_fire_window_resized(w, old_sx, old_sy);
w->flags &= ~WINDOW_RESIZE;
}

View File

@@ -1,4 +1,4 @@
/* $OpenBSD: server-client.c,v 1.486 2026/07/10 13:38:45 nicm Exp $ */
/* $OpenBSD: server-client.c,v 1.487 2026/07/13 13:01:14 nicm Exp $ */
/*
* Copyright (c) 2009 Nicholas Marriott <nicholas.marriott@gmail.com>
@@ -403,6 +403,62 @@ server_client_attached_lost(struct client *c)
}
}
/* Fire client session changed. */
static void
server_client_fire_session_changed(struct client *c, struct session *old)
{
struct event_payload *ep;
struct cmd_find_state fs;
ep = event_payload_create();
cmd_find_from_client(&fs, c, 0);
event_payload_set_target(ep, &fs);
event_payload_set_client(ep, "client", c);
if (fs.s != NULL) {
event_payload_set_session(ep, "session", fs.s);
event_payload_set_session(ep, "new_session", fs.s);
}
if (old != NULL)
event_payload_set_session(ep, "old_session", old);
if (fs.w != NULL)
event_payload_set_window(ep, "window", fs.w);
if (fs.wl != NULL)
event_payload_set_int(ep, "window_index", fs.wl->idx);
else if (fs.idx != -1)
event_payload_set_int(ep, "window_index", fs.idx);
if (fs.wp != NULL)
event_payload_set_pane(ep, "pane", fs.wp);
events_fire("client-session-changed", ep);
}
/* Fire client resized. */
static void
server_client_fire_resized(struct client *c, u_int old_sx, u_int old_sy)
{
struct event_payload *ep;
struct cmd_find_state fs;
ep = event_payload_create();
cmd_find_from_client(&fs, c, 0);
event_payload_set_target(ep, &fs);
event_payload_set_client(ep, "client", c);
if (fs.s != NULL)
event_payload_set_session(ep, "session", fs.s);
if (fs.w != NULL)
event_payload_set_window(ep, "window", fs.w);
if (fs.wl != NULL)
event_payload_set_int(ep, "window_index", fs.wl->idx);
else if (fs.idx != -1)
event_payload_set_int(ep, "window_index", fs.idx);
if (fs.wp != NULL)
event_payload_set_pane(ep, "pane", fs.wp);
event_payload_set_uint(ep, "width", c->tty.sx);
event_payload_set_uint(ep, "height", c->tty.sy);
event_payload_set_uint(ep, "old_width", old_sx);
event_payload_set_uint(ep, "old_height", old_sy);
events_fire("client-resized", ep);
}
/* Set client session. */
void
server_client_set_session(struct client *c, struct session *s)
@@ -429,7 +485,7 @@ server_client_set_session(struct client *c, struct session *s)
alerts_check_session(s);
tty_update_client_offset(c);
status_timer_start(c);
events_fire_client("client-session-changed", c);
server_client_fire_session_changed(c, old);
server_redraw_client(c);
}
@@ -466,6 +522,8 @@ server_client_lost(struct client *c)
server_client_attached_lost(c);
events_fire_client("client-detached", c);
}
if (c->name != NULL && (c->flags & (CLIENT_CONTROL|CLIENT_TERMINAL)))
events_fire_client("client-closed", c);
if (c->flags & CLIENT_CONTROL)
control_stop(c);
@@ -2421,6 +2479,7 @@ server_client_dispatch(struct imsg *imsg, void *arg)
struct client *c = arg;
ssize_t datalen;
struct session *s;
u_int old_sx, old_sy;
if (c->flags & CLIENT_DEAD)
return;
@@ -2459,6 +2518,8 @@ server_client_dispatch(struct imsg *imsg, void *arg)
if (c->flags & CLIENT_CONTROL)
break;
server_client_update_latest(c);
old_sx = c->tty.sx;
old_sy = c->tty.sy;
tty_resize(&c->tty);
tty_repeat_requests(&c->tty, 0);
recalculate_sizes();
@@ -2468,7 +2529,7 @@ server_client_dispatch(struct imsg *imsg, void *arg)
c->overlay_resize(c, c->overlay_data);
server_redraw_client(c);
if (c->session != NULL)
events_fire_client("client-resized", c);
server_client_fire_resized(c, old_sx, old_sy);
break;
case MSG_EXITING:
if (datalen != 0)
@@ -2775,6 +2836,8 @@ server_client_dispatch_identify(struct client *c, struct imsg *imsg)
close(c->out_fd);
c->out_fd = -1;
}
if (c->flags & (CLIENT_CONTROL|CLIENT_TERMINAL))
events_fire_client("client-created", c);
/* If pasting has taken too long, turn it off. */
if (c->flags & (CLIENT_BRACKETPASTING|CLIENT_ASSUMEPASTING) &&

View File

@@ -1,4 +1,4 @@
/* $OpenBSD: session.c,v 1.105 2026/07/10 13:38:45 nicm Exp $ */
/* $OpenBSD: session.c,v 1.106 2026/07/13 13:01:14 nicm Exp $ */
/*
* Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>
@@ -471,6 +471,29 @@ session_last(struct session *s)
return (session_set_current(s, wl));
}
/* Fire session window changed. */
static void
session_fire_window_changed(struct session *s, struct winlink *wl,
struct winlink *old)
{
struct event_payload *ep;
struct cmd_find_state fs;
ep = event_payload_create();
cmd_find_from_winlink(&fs, wl, 0);
event_payload_set_target(ep, &fs);
event_payload_set_session(ep, "session", s);
event_payload_set_window(ep, "window", wl->window);
event_payload_set_window(ep, "new_window", wl->window);
event_payload_set_int(ep, "window_index", wl->idx);
event_payload_set_int(ep, "new_window_index", wl->idx);
if (old != NULL) {
event_payload_set_window(ep, "old_window", old->window);
event_payload_set_int(ep, "old_window_index", old->idx);
}
events_fire("session-window-changed", ep);
}
/* Set current winlink to wl .*/
int
session_set_current(struct session *s, struct winlink *wl)
@@ -493,7 +516,7 @@ session_set_current(struct session *s, struct winlink *wl)
winlink_clear_flags(wl);
window_update_activity(wl->window);
tty_update_window_offset(wl->window);
events_fire_session("session-window-changed", s);
session_fire_window_changed(s, wl, old);
return (0);
}
@@ -540,12 +563,33 @@ session_group_new(const char *name)
return (sg);
}
/* Fire session group changed. */
static void
session_group_fire(const char *name, struct session_group *sg,
struct session *s)
{
struct event_payload *ep;
struct cmd_find_state fs;
ep = event_payload_create();
if (session_alive(s)) {
cmd_find_from_session(&fs, s, 0);
event_payload_set_target(ep, &fs);
}
event_payload_set_session(ep, "session", s);
event_payload_set_string(ep, "group", "%s", sg->name);
event_payload_set_uint(ep, "group_size", session_group_count(sg));
events_fire(name, ep);
}
/* Add a session to a session group. */
void
session_group_add(struct session_group *sg, struct session *s)
{
if (session_group_contains(s) == NULL)
if (session_group_contains(s) == NULL) {
TAILQ_INSERT_TAIL(&sg->sessions, s, gentry);
session_group_fire("session-added-to-group", sg, s);
}
}
/* Remove a session from its group and destroy the group if empty. */
@@ -556,6 +600,7 @@ session_group_remove(struct session *s)
if ((sg = session_group_contains(s)) == NULL)
return;
session_group_fire("session-removed-from-group", sg, s);
TAILQ_REMOVE(&sg->sessions, s, gentry);
if (TAILQ_EMPTY(&sg->sessions)) {
RB_REMOVE(session_groups, &session_groups, sg);

View File

@@ -1,4 +1,4 @@
/* $OpenBSD: spawn.c,v 1.47 2026/07/10 13:38:45 nicm Exp $ */
/* $OpenBSD: spawn.c,v 1.48 2026/07/13 13:01:14 nicm Exp $ */
/*
* Copyright (c) 2019 Nicholas Marriott <nicholas.marriott@gmail.com>
@@ -229,8 +229,10 @@ spawn_window(struct spawn_context *sc, char **cause)
session_select(s, sc->wl->idx);
/* Fire notification if new window. */
if (~sc->flags & SPAWN_RESPAWN)
if (~sc->flags & SPAWN_RESPAWN) {
events_fire_window("window-created", w);
events_fire_winlink("window-linked", sc->wl);
}
session_group_synchronize_from(s);
return (sc->wl);

35
tmux.1
View File

@@ -1,4 +1,4 @@
.\" $OpenBSD: tmux.1,v 1.1132 2026/07/10 15:20:06 nicm Exp $
.\" $OpenBSD: tmux.1,v 1.1134 2026/07/13 13:01:14 nicm Exp $
.\"
.\" Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>
.\"
@@ -14,7 +14,7 @@
.\" IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
.\" OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
.\"
.Dd $Mdocdate: July 10 2026 $
.Dd $Mdocdate: July 13 2026 $
.Dt TMUX 1
.Os
.Sh NAME
@@ -4077,6 +4077,21 @@ or full window width (with
.Fl v ) ,
instead of splitting the active pane.
.Pp
If
.Ar target\-pane
is floating, a new floating pane is created adjacent to
.Ar target\-pane
with equal sizes.
The position is determined by the
.Fl h ,
.Fl v ,
and
.Fl b
flags.
If any of the new pane would not be visible on the screen,
.Ar target\-pane
and the new pane are resized to fit.
.Pp
.Fl k
keeps the pane open after the optional
.Ar shell\-command
@@ -6343,6 +6358,10 @@ See
Run when a client becomes the latest active client of its session.
.It client\-attached
Run when a client is attached.
.It client\-closed
Run when a client is closed.
.It client\-created
Run when a client is created.
.It client\-detached
Run when a client is detached.
.It client\-focus\-in
@@ -6361,6 +6380,8 @@ Run when a client switches to a dark theme.
Run when a command fails.
.It marked\-pane\-changed
Run when the marked pane is set or cleared.
.It pane\-bell
Run when a pane receives a bell.
.It pane\-command\-finished
Run when an OSC 133 command finishes in a pane.
The event payload includes
@@ -6394,6 +6415,8 @@ Run when a pane changes mode.
Run when a pane enters a mode.
.It pane\-mode\-exited
Run when a pane exits a mode.
.It pane\-moved
Run when a pane is moved to another window.
.It pane\-prompt\-closed
Run when a prompt in a pane is closed.
.It pane\-prompt\-opened
@@ -6412,10 +6435,18 @@ Run when a pane title is changed.
Run when a session is closed.
.It session\-created
Run when a new session is created.
.It session\-added\-to\-group
Run when a session is added to a session group.
.It session\-removed\-from\-group
Run when a session is removed from a session group.
.It session\-renamed
Run when a session is renamed.
.It session\-window\-changed
Run when a session changes its active window.
.It window\-closed
Run when a window is closed.
.It window\-created
Run when a window is created.
.It window\-layout\-changed
Run when a window layout is changed.
.It window\-linked

13
tmux.h
View File

@@ -1,4 +1,4 @@
/* $OpenBSD: tmux.h,v 1.1396 2026/07/10 15:45:11 nicm Exp $ */
/* $OpenBSD: tmux.h,v 1.1399 2026/07/13 13:01:14 nicm Exp $ */
/*
* Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>
@@ -2127,6 +2127,8 @@ struct prompt_create_data {
struct grid_cell style;
struct grid_cell command_style;
const char *style_str;
const char *command_style_str;
enum screen_cursor_style cstyle;
enum screen_cursor_style command_cstyle;
int ccolour;
@@ -2496,6 +2498,8 @@ struct spawn_context {
#define SPAWN_EMPTY 0x40
#define SPAWN_ZOOM 0x80
#define SPAWN_FLOATING 0x100
#define SPAWN_HORIZONTAL 0x200
#define SPAWN_SPLIT 0x400
};
/* Paste buffer. */
@@ -3670,6 +3674,8 @@ int window_has_floating_panes(struct window *);
int window_has_pane(struct window *, struct window_pane *);
int window_set_active_pane(struct window *, struct window_pane *,
int);
void window_fire_pane_moved(struct window_pane *, struct window *,
int, struct window *, int);
void window_update_focus(struct window *);
void window_pane_update_focus(struct window_pane *);
void window_redraw_active_switch(struct window *,
@@ -3840,10 +3846,13 @@ struct layout_cell *layout_get_tiled_cell(struct cmdq_item *, struct args *,
struct window *, struct window_pane *, int, char **);
struct layout_cell *layout_get_floating_cell(struct cmdq_item *, struct args *,
enum pane_lines, struct window *, struct window_pane *,
char **);
int, char **);
int layout_floating_args_parse(struct cmdq_item *, struct args *,
enum pane_lines, struct window *, struct layout_geometry *,
char **);
int layout_split_floating_cell(struct layout_cell *,
struct window *, struct layout_geometry *, enum pane_lines,
int, char **);
int layout_remove_tile(struct window *, struct layout_cell *);
int layout_insert_tile(struct window *, struct layout_cell *);

View File

@@ -1,4 +1,4 @@
/* $OpenBSD: window-copy.c,v 1.413 2026/07/10 13:38:45 nicm Exp $ */
/* $OpenBSD: window-copy.c,v 1.415 2026/07/13 12:53:56 jsg Exp $ */
/*
* Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>
@@ -103,8 +103,12 @@ static void window_copy_goto_line(struct window_mode_entry *, const char *);
static void window_copy_update_cursor(struct window_mode_entry *, u_int,
u_int);
static void window_copy_start_selection(struct window_mode_entry *);
static int window_copy_mouse_in_selection(struct window_mode_entry *,
u_int, u_int, int *, int *);
static int window_copy_adjust_selection(struct window_mode_entry *,
u_int *, u_int *);
static int window_copy_update_selection_view(struct window_mode_entry *,
int, int);
static int window_copy_set_selection(struct window_mode_entry *, int, int);
static int window_copy_update_selection(struct window_mode_entry *, int,
int);
@@ -862,7 +866,7 @@ window_copy_scroll1(struct window_mode_entry *wme, struct window_pane *wp,
if (data->searchmark != NULL && !data->timeout)
window_copy_search_marks(wme, NULL, data->searchregex, 1);
window_copy_update_selection(wme, 1, 0);
window_copy_update_selection_view(wme, 1, 0);
window_pane_scrollbar_show(wp, 1);
window_copy_redraw_screen(wme, 1);
}
@@ -1648,7 +1652,7 @@ window_copy_cmd_scroll_to(struct window_copy_cmd_state *cs, u_int to)
data->cy += delta;
}
window_copy_update_selection(wme, 0, 0);
window_copy_update_selection_view(wme, 0, 0);
return (WINDOW_COPY_CMD_REDRAW);
}
@@ -2361,6 +2365,7 @@ window_copy_cmd_scroll_down(struct window_copy_cmd_state *cs)
return (WINDOW_COPY_CMD_CANCEL);
return (WINDOW_COPY_CMD_NOTHING);
}
for (; np != 0; np--)
window_copy_cursor_down(wme, 1);
if (data->scroll_exit && data->oy == 0 && data->screen.sel == NULL)
@@ -2374,6 +2379,17 @@ window_copy_cmd_scroll_down_and_cancel(struct window_copy_cmd_state *cs)
struct window_mode_entry *wme = cs->wme;
struct window_copy_mode_data *data = wme->data;
u_int np = wme->prefix;
int dragging;
/* With a selection but no active drag, only scroll the view. */
dragging = (cs->c != NULL && cs->c->tty.mouse_drag_flag != 0);
if (data->screen.sel != NULL && !dragging) {
data->cursordrag = CURSORDRAG_NONE;
data->lineflag = LINE_SEL_NONE;
/* Move down in the history. */
window_copy_scroll_up(wme, np);
return (WINDOW_COPY_CMD_NOTHING);
}
for (; np != 0; np--)
window_copy_cursor_down(wme, 1);
@@ -2388,6 +2404,7 @@ window_copy_cmd_scroll_up(struct window_copy_cmd_state *cs)
struct window_mode_entry *wme = cs->wme;
struct window_copy_mode_data *data = wme->data;
u_int np = wme->prefix;
int dragging;
/*
* If at the top, nothing can change, so return WINDOW_COPY_CMD_NOTHING
@@ -2395,6 +2412,17 @@ window_copy_cmd_scroll_up(struct window_copy_cmd_state *cs)
*/
if (data->oy == screen_hsize(data->backing))
return (WINDOW_COPY_CMD_NOTHING);
/* With a selection but no active drag, only scroll the view. */
dragging = (cs->c != NULL && cs->c->tty.mouse_drag_flag != 0);
if (data->screen.sel != NULL && !dragging) {
data->cursordrag = CURSORDRAG_NONE;
data->lineflag = LINE_SEL_NONE;
/* Move up in the history. */
window_copy_scroll_down(wme, np);
return (WINDOW_COPY_CMD_NOTHING);
}
for (; np != 0; np--)
window_copy_cursor_up(wme, 1);
return (WINDOW_COPY_CMD_MOVE);
@@ -3117,7 +3145,7 @@ window_copy_cmd_recentre_top_bottom(struct window_copy_cmd_state *cs)
data->cy = cy + (data->oy - oy);
break;
}
window_copy_update_selection(wme, 0, 0);
window_copy_update_selection_view(wme, 0, 0);
return (WINDOW_COPY_CMD_REDRAW);
}
@@ -5504,6 +5532,76 @@ window_copy_start_selection(struct window_mode_entry *wme)
window_copy_set_selection(wme, 1, 0);
}
/*
* Return whether x,y is inside the selection and optionally identify the
* endpoint.
*/
static int
window_copy_mouse_in_selection(struct window_mode_entry *wme, u_int x, u_int y,
int *on_start, int *on_end)
{
struct window_copy_mode_data *data = wme->data;
u_int sx = screen_size_x(&data->screen);
u_int sy = screen_size_y(&data->screen);
u_int hsize, screeny;
u_int mx, my, selx, sely, endselx, endsely;
u_int cursorx, cursory;
long long mpos, spos, epos, dstart, dend;
if (on_start != NULL)
*on_start = 0;
if (on_end != NULL)
*on_end = 0;
if (data->screen.sel == NULL)
return (0);
hsize = screen_hsize(data->backing);
screeny = hsize - data->oy;
selx = window_copy_cursor_offset(wme, data->selx, sx);
sely = data->sely - screeny;
if (data->sely >= screeny && sely < sy && x == selx && y == sely) {
if (on_start != NULL)
*on_start = 1;
return (1);
}
endselx = window_copy_cursor_offset(wme, data->endselx, sx);
endsely = data->endsely - screeny;
if (data->endsely >= screeny &&
endsely < sy &&
x == endselx && y == endsely) {
if (on_end != NULL)
*on_end = 1;
return (1);
}
cursorx = window_copy_cursor_offset(wme, data->cx, sx);
cursory = data->cy;
if (x != cursorx || y != cursory) {
if (!screen_check_selection(&data->screen, x, y))
return (0);
}
if (on_start != NULL || on_end != NULL) {
mx = window_copy_cursor_unoffset(wme, x, sx);
my = screeny + y;
mpos = (long long)my * (sx + 1) + mx;
spos = (long long)data->sely * (sx + 1) + data->selx;
epos = (long long)data->endsely * (sx + 1) + data->endselx;
dstart = llabs(mpos - spos);
dend = llabs(mpos - epos);
if (dstart <= dend) {
if (on_start != NULL)
*on_start = 1;
} else {
if (on_end != NULL)
*on_end = 1;
}
}
return (1);
}
static int
window_copy_adjust_selection(struct window_mode_entry *wme, u_int *selx,
u_int *sely)
@@ -5549,6 +5647,35 @@ window_copy_update_selection(struct window_mode_entry *wme, int may_redraw,
return (window_copy_set_selection(wme, may_redraw, no_reset));
}
/*
* Update the visible selection after the view has changed. If the mouse is
* not dragging, keep the selection endpoints anchored to the same text.
*/
static int
window_copy_update_selection_view(struct window_mode_entry *wme, int may_redraw,
int no_reset)
{
struct window_copy_mode_data *data = wme->data;
u_int selx, sely, endselx, endsely;
int changed;
if (data->cursordrag != CURSORDRAG_NONE)
return (window_copy_update_selection(wme, may_redraw, no_reset));
selx = data->selx;
sely = data->sely;
endselx = data->endselx;
endsely = data->endsely;
changed = window_copy_update_selection(wme, may_redraw, 1);
data->selx = selx;
data->sely = sely;
data->endselx = endselx;
data->endsely = endsely;
return (changed);
}
static int
window_copy_set_selection(struct window_mode_entry *wme, int may_redraw,
int no_reset)
@@ -6578,7 +6705,7 @@ window_copy_scroll_up(struct window_mode_entry *wme, u_int ny)
if (data->searchmark != NULL && !data->timeout)
window_copy_search_marks(wme, NULL, data->searchregex, 1);
window_copy_update_selection(wme, 0, 0);
window_copy_update_selection_view(wme, 0, 0);
if (window_copy_line_numbers_active(wme)) {
if (window_copy_line_number_mode(wme) !=
WINDOW_COPY_LINE_NUMBERS_ABSOLUTE) {
@@ -6649,7 +6776,7 @@ window_copy_scroll_down(struct window_mode_entry *wme, u_int ny)
if (data->searchmark != NULL && !data->timeout)
window_copy_search_marks(wme, NULL, data->searchregex, 1);
window_copy_update_selection(wme, 0, 0);
window_copy_update_selection_view(wme, 0, 0);
if (window_copy_line_numbers_active(wme)) {
if (window_copy_line_number_mode(wme) !=
WINDOW_COPY_LINE_NUMBERS_ABSOLUTE) {
@@ -6738,6 +6865,7 @@ window_copy_start_drag(struct client *c, struct mouse_event *m)
struct window_mode_entry *wme;
struct window_copy_mode_data *data;
u_int x, y, yg;
int inside_selection, on_start, on_end;
if (c == NULL)
return;
@@ -6758,10 +6886,16 @@ window_copy_start_drag(struct client *c, struct mouse_event *m)
c->tty.mouse_drag_release = window_copy_drag_release;
data = wme->data;
on_start = on_end = 0;
inside_selection = window_copy_mouse_in_selection(wme, x, y,
&on_start, &on_end);
x = window_copy_cursor_unoffset(wme, x, screen_size_x(&data->screen));
yg = screen_hsize(data->backing) + y - data->oy;
if (x < data->selrx || x > data->endselrx || yg != data->selry)
if (on_start || on_end || !inside_selection ||
x < data->selrx || x > data->endselrx || yg != data->selry) {
data->lineflag = LINE_SEL_NONE;
data->selflag = SEL_CHAR;
}
switch (data->selflag) {
case SEL_WORD:
if (data->separators != NULL) {
@@ -6777,7 +6911,15 @@ window_copy_start_drag(struct client *c, struct mouse_event *m)
break;
case SEL_CHAR:
window_copy_update_cursor(wme, x, y);
window_copy_start_selection(wme);
if (!inside_selection)
window_copy_start_selection(wme);
else {
if (on_start)
data->cursordrag = CURSORDRAG_SEL;
else if (on_end)
data->cursordrag = CURSORDRAG_ENDSEL;
window_copy_update_selection(wme, 1, 0);
}
break;
}
@@ -6853,6 +6995,7 @@ window_copy_drag_release(struct client *c, struct mouse_event *m)
data = wme->data;
if (window_copy_line_numbers_active(wme))
window_copy_drag_update(c, m);
data->cursordrag = CURSORDRAG_NONE;
evtimer_del(&data->dragtimer);
}

View File

@@ -1,4 +1,4 @@
/* $OpenBSD: window.c,v 1.360 2026/07/10 15:20:06 nicm Exp $ */
/* $OpenBSD: window.c,v 1.361 2026/07/13 13:01:14 nicm Exp $ */
/*
* Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>
@@ -128,6 +128,29 @@ window_fire_pane_changed(struct window *w, struct window_pane *wp,
events_fire("window-pane-changed", ep);
}
void
window_fire_pane_moved(struct window_pane *wp, struct window *old_w,
int old_idx, struct window *new_w, int new_idx)
{
struct event_payload *ep;
struct cmd_find_state fs;
ep = event_payload_create();
cmd_find_from_pane(&fs, wp, 0);
event_payload_set_target(ep, &fs);
event_payload_set_pane(ep, "pane", wp);
event_payload_set_window(ep, "window", new_w);
event_payload_set_window(ep, "old_window", old_w);
event_payload_set_window(ep, "new_window", new_w);
if (old_idx != -1)
event_payload_set_int(ep, "old_window_index", old_idx);
if (new_idx != -1) {
event_payload_set_int(ep, "window_index", new_idx);
event_payload_set_int(ep, "new_window_index", new_idx);
}
events_fire("pane-moved", ep);
}
static void
window_fire_pane_mode_changed(const char *name, struct window_pane *wp,
const char *previous, const char *current, int entered)
@@ -488,6 +511,9 @@ window_add_ref(struct window *w, const char *from)
void
window_remove_ref(struct window *w, const char *from)
{
if (w->references == 1)
events_fire_window("window-closed", w);
w->references--;
log_debug("%s: @%u %s, now %d", __func__, w->id, from, w->references);
@@ -979,7 +1005,7 @@ window_lost_pane(struct window *w, struct window_pane *wp)
if (w->active != NULL) {
window_pane_stack_remove(&w->last_panes, w->active);
w->active->flags |= PANE_CHANGED;
events_fire_window("window-pane-changed", w);
window_fire_pane_changed(w, w->active, wp);
window_update_focus(w);
}
}