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> * 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 *src_s = source->s;
struct session *dst_s = target->s; struct session *dst_s = target->s;
struct window_pane *wp = source->wp; struct window_pane *wp = source->wp;
struct window *w = wl->window; struct window *w = wl->window, *old_w = w;
char *cause, *cp; 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'); const char *template, *name = args_get(args, 'n');
if (args_has(args, 'W')) 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); wl = winlink_find_by_window(&dst_s->windows, w);
if (wl == NULL) if (wl == NULL)
return (CMD_RETURN_ERROR); return (CMD_RETURN_ERROR);
window_fire_pane_moved(wp, old_w, old_idx, w, wl->idx);
goto out; goto out;
} }
if (idx != -1 && winlink_find_by_index(&dst_s->windows, idx) != NULL) { 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"); idx = -1 - options_get_number(dst_s->options, "base-index");
wl = session_attach(dst_s, w, idx, &cause); /* can't fail */ wl = session_attach(dst_s, w, idx, &cause); /* can't fail */
window_remove_ref(w, __func__); 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')) { if (!args_has(args, 'd')) {
session_select(dst_s, wl->idx); session_select(dst_s, wl->idx);
cmd_find_from_session(current, dst_s, 0); 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> * 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); 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); lc = layout_get_tiled_cell(item, args, dst_w, dst_wp, flags, &cause);
if (cause != NULL) { if (cause != NULL) {
cmdq_error(item, "size or position %s", cause); cmdq_error(item, "size or position %s", cause);
@@ -498,6 +505,7 @@ cmd_join_pane_exec(struct cmd *self, struct cmdq_item *item)
} else } else
server_status_session(dst_s); 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) if (window_count_panes(src_w, 1) == 0)
server_kill_window(src_w, 1); server_kill_window(src_w, 1);
else 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> * 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] " .usage = "[-bdefhIklPvWZ] [-c start-directory] [-e environment] "
"[-F format] [-l size] [-m message] [-p percentage] " "[-F format] [-l size] [-m message] [-p percentage] "
"[-s style] [-S active-border-style] " "[-s style] [-S active-border-style] "
"[-R inactive-border-style] [-T title] " CMD_TARGET_PANE_USAGE " " "[-R inactive-border-style] [-T title] "
"[shell-command [argument ...]]", CMD_TARGET_PANE_USAGE " [shell-command [argument ...]]",
.target = { 't', CMD_FIND_PANE, 0 }, .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) if (cmd_get_entry(self) == &cmd_new_pane_entry)
is_floating = !args_has(args, 'L'); is_floating = !args_has(args, 'L');
else else {
is_floating = window_pane_is_floating(wp); 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')) if (args_has(args, 'b'))
flags |= SPAWN_BEFORE; flags |= SPAWN_BEFORE;
if (args_has(args, 'f')) if (args_has(args, 'f'))
flags |= SPAWN_FULLSIZE; flags |= SPAWN_FULLSIZE;
if (args_has(args, 'd'))
flags |= SPAWN_DETACHED;
if (args_has(args, 'Z'))
flags |= SPAWN_ZOOM;
input = args_has(args, 'I'); input = args_has(args, 'I');
if (input || (count == 1 && *args_string(args, 0) == '\0')) 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) if (flags & SPAWN_FLOATING) {
lc = layout_get_floating_cell(item, args, lines, w, wp, &cause); lc = layout_get_floating_cell(item, args, lines, w, wp, flags,
else &cause);
} else
lc = layout_get_tiled_cell(item, args, w, wp, flags, &cause); lc = layout_get_tiled_cell(item, args, w, wp, flags, &cause);
if (cause != NULL) { if (cause != NULL) {
cmdq_error(item, "%s", cause); cmdq_error(item, "%s", cause);
@@ -158,12 +168,7 @@ cmd_split_window_exec(struct cmd *self, struct cmdq_item *item)
sc.idx = -1; sc.idx = -1;
sc.cwd = args_get(args, 'c'); sc.cwd = args_get(args, 'c');
sc.flags = flags; 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) { if ((new_wp = spawn_pane(&sc, &cause)) == NULL) {
cmdq_error(item, "create pane failed: %s", cause); cmdq_error(item, "create pane failed: %s", cause);
@@ -239,10 +244,10 @@ cmd_split_window_exec(struct cmd *self, struct cmdq_item *item)
break; break;
} }
} }
if (!args_has(args, 'd')) if (~flags & SPAWN_DETACHED)
cmd_find_from_winlink_pane(current, wl, new_wp, 0); cmd_find_from_winlink_pane(current, wl, new_wp, 0);
if (!is_floating) { if (~flags & SPAWN_FLOATING) {
window_pop_zoom(wp->window); window_pop_zoom(wp->window);
server_redraw_window(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> * 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 window_pane *tmp_wp, *src_wp, *dst_wp;
struct layout_cell *src_lc, *dst_lc; struct layout_cell *src_lc, *dst_lc;
u_int sx, sy, xoff, yoff; u_int sx, sy, xoff, yoff;
int src_idx, dst_idx;
dst_w = target->wl->window; dst_w = target->wl->window;
dst_wp = target->wp; dst_wp = target->wp;
dst_idx = target->wl->idx;
src_w = source->wl->window; src_w = source->wl->window;
src_wp = source->wp; src_wp = source->wp;
src_idx = source->wl->idx;
if (window_push_zoom(dst_w, 0, args_has(args, 'Z'))) if (window_push_zoom(dst_w, 0, args_has(args, 'Z')))
server_redraw_window(dst_w); 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); redraw_invalidate_scene(dst_w);
server_redraw_window(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); events_fire_window("window-layout-changed", src_w);
if (src_w != dst_w) if (src_w != dst_w)
events_fire_window("window-layout-changed", 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> * 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++; ptr++;
slen = strlen(s); 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"); fatalx("argument too long");
buf = xrealloc(buf, len + (slen * 3) + 1); buf = xrealloc(buf, len + (slen * 3) + 1);
for (cp = s; *cp != '\0'; cp++) { 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> * Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>
@@ -1316,8 +1316,10 @@ input_c0_dispatch(struct input_ctx *ictx)
case '\000': /* NUL */ case '\000': /* NUL */
break; break;
case '\007': /* BEL */ case '\007': /* BEL */
if (wp != NULL) if (wp != NULL) {
events_fire_pane("pane-bell", wp);
alerts_queue(wp->window, WINDOW_BELL); alerts_queue(wp->window, WINDOW_BELL);
}
break; break;
case '\010': /* BS */ case '\010': /* BS */
screen_write_backspace(sctx); screen_write_backspace(sctx);
@@ -2199,7 +2201,7 @@ input_csi_dispatch_winops(struct input_ctx *ictx)
screen_pop_title(sctx->s); screen_pop_title(sctx->s);
if (wp == NULL) if (wp == NULL)
break; break;
events_fire_pane("pane-title-changed", wp); input_fire_pane_title_changed(wp, sctx->s->title);
server_redraw_window_borders(w); server_redraw_window_borders(w);
server_status_window(w); server_status_window(w);
break; 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> * Copyright (c) 2009 Nicholas Marriott <nicholas.marriott@gmail.com>
@@ -135,6 +135,21 @@ layout_set_first_tiled(struct window *w)
return (NULL); 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 static void
layout_set_even(struct window *w, enum layout_type type) 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); wp = TAILQ_NEXT(wp, entry);
TAILQ_INSERT_TAIL(&lcroot->cells, wp->layout_cell, entry); TAILQ_INSERT_TAIL(&lcroot->cells, wp->layout_cell, entry);
wp->layout_cell->parent = lcroot; wp->layout_cell->parent = lcroot;
layout_set_size(wp->layout_cell, sx, otherh, 0, 0);
layout_set_link_floating(w, lcroot);
} else { } else {
lcother = layout_create_cell(lcroot); lcother = layout_create_cell(lcroot);
layout_set_size(lcother, sx, otherh, 0, 0); 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); wp = TAILQ_NEXT(wp, entry);
TAILQ_INSERT_HEAD(&lcroot->cells, wp->layout_cell, entry); TAILQ_INSERT_HEAD(&lcroot->cells, wp->layout_cell, entry);
wp->layout_cell->parent = lcroot; wp->layout_cell->parent = lcroot;
layout_set_size(wp->layout_cell, sx, otherh, 0, 0);
layout_set_link_floating(w, lcroot);
} else { } else {
lcother = layout_create_cell(lcroot); lcother = layout_create_cell(lcroot);
layout_set_size(lcother, sx, otherh, 0, 0); layout_set_size(lcother, sx, otherh, 0, 0);
@@ -461,6 +480,8 @@ layout_set_main_v(struct window *w)
wp = TAILQ_NEXT(wp, entry); wp = TAILQ_NEXT(wp, entry);
TAILQ_INSERT_TAIL(&lcroot->cells, wp->layout_cell, entry); TAILQ_INSERT_TAIL(&lcroot->cells, wp->layout_cell, entry);
wp->layout_cell->parent = lcroot; wp->layout_cell->parent = lcroot;
layout_set_size(wp->layout_cell, otherw, sy, 0, 0);
layout_set_link_floating(w, lcroot);
} else { } else {
lcother = layout_create_cell(lcroot); lcother = layout_create_cell(lcroot);
layout_make_node(lcother, LAYOUT_TOPBOTTOM); layout_make_node(lcother, LAYOUT_TOPBOTTOM);
@@ -559,6 +580,8 @@ layout_set_main_v_mirrored(struct window *w)
wp = TAILQ_NEXT(wp, entry); wp = TAILQ_NEXT(wp, entry);
TAILQ_INSERT_HEAD(&lcroot->cells, wp->layout_cell, entry); TAILQ_INSERT_HEAD(&lcroot->cells, wp->layout_cell, entry);
wp->layout_cell->parent = lcroot; wp->layout_cell->parent = lcroot;
layout_set_size(wp->layout_cell, otherw, sy, 0, 0);
layout_set_link_floating(w, lcroot);
} else { } else {
lcother = layout_create_cell(lcroot); lcother = layout_create_cell(lcroot);
layout_make_node(lcother, LAYOUT_TOPBOTTOM); layout_make_node(lcother, LAYOUT_TOPBOTTOM);
@@ -700,6 +723,7 @@ layout_set_tiled(struct window *w)
w->sy - used); w->sy - used);
} }
layout_set_link_floating(w, lcroot);
layout_fix_offsets(w); layout_fix_offsets(w);
layout_fix_panes(w, NULL); 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> * 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 window *w, struct window_pane *wp, int flags, char **cause)
{ {
struct layout_cell *lc; struct layout_cell *lc;
enum layout_type type; enum layout_type type = LAYOUT_TOPBOTTOM;
u_int curval; u_int curval;
int size = -1; int size = -1;
char *error = NULL; char *error = NULL;
@@ -1608,12 +1608,11 @@ layout_get_tiled_cell(struct cmdq_item *item, struct args *args,
return (NULL); return (NULL);
} }
type = LAYOUT_TOPBOTTOM; if (flags & SPAWN_HORIZONTAL)
if (args_has(args, 'h'))
type = LAYOUT_LEFTRIGHT; type = LAYOUT_LEFTRIGHT;
if (args_has(args, 'l') || args_has(args, 'p')) { if (args_has(args, 'l') || args_has(args, 'p')) {
if (args_has(args, 'f')) { if (flags & SPAWN_FULLSIZE) {
if (type == LAYOUT_TOPBOTTOM) if (type == LAYOUT_TOPBOTTOM)
curval = w->sy; curval = w->sy;
else else
@@ -1641,12 +1640,7 @@ layout_get_tiled_cell(struct cmdq_item *item, struct args *args,
return (NULL); return (NULL);
} }
if (args_has(args, 'b')) window_push_zoom(wp->window, 1, (flags & SPAWN_ZOOM));
flags |= SPAWN_BEFORE;
if (args_has(args, 'f'))
flags |= SPAWN_FULLSIZE;
window_push_zoom(wp->window, 1, args_has(args, 'Z'));
lc = layout_split_pane(wp, type, size, flags); lc = layout_split_pane(wp, type, size, flags);
if (lc == NULL) if (lc == NULL)
*cause = xstrdup("no space for a new pane"); *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 * struct layout_cell *
layout_get_floating_cell(struct cmdq_item *item, struct args *args, 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) char **cause)
{ {
struct layout_cell *lcnew; struct layout_cell *lcnew, *lc = wp->layout_cell;
struct layout_geometry fg; struct layout_geometry fg;
layout_geometry_init(&fg); layout_geometry_init(&fg);
if (layout_floating_args_parse(item, args, lines, w, &fg, cause) != 0) if (flags & SPAWN_SPLIT) {
return (NULL); 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); lcnew = layout_floating_pane(w, wp, &fg);
return (lcnew); return (lcnew);
} }
@@ -1766,6 +1767,131 @@ layout_floating_args_parse(struct cmdq_item *item, struct args *args,
return (0); 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 * Removes a cell from the tiled layout by giving the cell's space to the
* nearest neighbour. * 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> * 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("alert-silence", ""),
OPTIONS_TABLE_HOOK("client-active", ""), OPTIONS_TABLE_HOOK("client-active", ""),
OPTIONS_TABLE_HOOK("client-attached", ""), OPTIONS_TABLE_HOOK("client-attached", ""),
OPTIONS_TABLE_HOOK("client-created", ""),
OPTIONS_TABLE_HOOK("client-closed", ""),
OPTIONS_TABLE_HOOK("client-detached", ""), OPTIONS_TABLE_HOOK("client-detached", ""),
OPTIONS_TABLE_HOOK("client-focus-in", ""), OPTIONS_TABLE_HOOK("client-focus-in", ""),
OPTIONS_TABLE_HOOK("client-focus-out", ""), 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("client-dark-theme", ""),
OPTIONS_TABLE_HOOK("command-error", ""), OPTIONS_TABLE_HOOK("command-error", ""),
OPTIONS_TABLE_HOOK("marked-pane-changed", ""), 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-finished", ""),
OPTIONS_TABLE_PANE_HOOK("pane-command-started", ""), OPTIONS_TABLE_PANE_HOOK("pane-command-started", ""),
OPTIONS_TABLE_PANE_HOOK("pane-created", ""), 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-changed", ""),
OPTIONS_TABLE_PANE_HOOK("pane-mode-entered", ""), OPTIONS_TABLE_PANE_HOOK("pane-mode-entered", ""),
OPTIONS_TABLE_PANE_HOOK("pane-mode-exited", ""), 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-closed", ""),
OPTIONS_TABLE_PANE_HOOK("pane-prompt-opened", ""), OPTIONS_TABLE_PANE_HOOK("pane-prompt-opened", ""),
OPTIONS_TABLE_PANE_HOOK("pane-resized", ""), 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_PANE_HOOK("pane-title-changed", ""),
OPTIONS_TABLE_HOOK("session-closed", ""), OPTIONS_TABLE_HOOK("session-closed", ""),
OPTIONS_TABLE_HOOK("session-created", ""), OPTIONS_TABLE_HOOK("session-created", ""),
OPTIONS_TABLE_HOOK("session-added-to-group", ""),
OPTIONS_TABLE_HOOK("session-renamed", ""), OPTIONS_TABLE_HOOK("session-renamed", ""),
OPTIONS_TABLE_HOOK("session-removed-from-group", ""),
OPTIONS_TABLE_HOOK("session-window-changed", ""), 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_WINDOW_HOOK("window-layout-changed", ""),
OPTIONS_TABLE_HOOK("window-linked", ""), OPTIONS_TABLE_HOOK("window-linked", ""),
OPTIONS_TABLE_WINDOW_HOOK("window-pane-changed", ""), 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> * Copyright (c) 2026 Nicholas Marriott <nicholas.marriott@gmail.com>
@@ -42,6 +42,8 @@ struct prompt {
char *word_separators; char *word_separators;
struct grid_cell style; struct grid_cell style;
struct grid_cell command_style; struct grid_cell command_style;
char *style_str;
char *command_style_str;
enum screen_cursor_style cstyle; enum screen_cursor_style cstyle;
enum screen_cursor_style command_cstyle; enum screen_cursor_style command_cstyle;
int ccolour; int ccolour;
@@ -61,11 +63,30 @@ struct prompt {
char *complete_display; 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 char *prompt_complete(struct prompt *, const char *, u_int);
static void prompt_clear_complete(struct prompt *); 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 int prompt_replace_complete(struct prompt *, const char *);
static u_int prompt_width(struct prompt *, u_int);
/* Get prompt flags as a string. */ /* Get prompt flags as a string. */
static const char * 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->style, oo, "message-style", NULL);
style_apply(&pd->command_style, oo, "message-command-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"); n = options_get_number(oo, "prompt-cursor-style");
screen_set_cursor_style(n, &pd->cstyle, &pd->cmode); screen_set_cursor_style(n, &pd->cstyle, &pd->cmode);
n = options_get_number(oo, "prompt-command-cursor-style"); 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->style, &pd->style, sizeof pr->style);
memcpy(&pr->command_style, &pd->command_style, memcpy(&pr->command_style, &pd->command_style,
sizeof pr->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->cstyle = pd->cstyle;
pr->command_cstyle = pd->command_cstyle; pr->command_cstyle = pd->command_cstyle;
pr->ccolour = pd->ccolour; pr->ccolour = pd->ccolour;
@@ -201,6 +226,8 @@ prompt_free(struct prompt *pr)
if (pr->freecb != NULL && pr->data != NULL) if (pr->freecb != NULL && pr->data != NULL)
pr->freecb(pr->data); pr->freecb(pr->data);
free(pr->message_format); free(pr->message_format);
free(pr->style_str);
free(pr->command_style_str);
free(pr->word_separators); free(pr->word_separators);
free(pr->last); free(pr->last);
free(pr->string); free(pr->string);
@@ -321,16 +348,17 @@ prompt_redraw_character(struct screen_write_ctx *ctx, u_int offset,
* 0 otherwise. * 0 otherwise.
*/ */
static int static int
prompt_redraw_quote(const struct prompt *pr, u_int pcursor, prompt_redraw_quote(const struct prompt *pr, u_int pcursor, u_int input_x,
struct screen_write_ctx *ctx, u_int offset, u_int pwidth, u_int *width, struct screen_write_ctx *ctx, u_int offset, u_int pw, u_int *w,
struct grid_cell *gc) struct grid_cell *gc)
{ {
struct utf8_data ud; 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, '^'); utf8_set(&ud, '^');
return (prompt_redraw_character(ctx, offset, pwidth, return (prompt_redraw_character(ctx, offset, pw, w, gc, &ud));
width, gc, &ud));
} }
return (1); return (1);
} }
@@ -368,12 +396,12 @@ prompt_draw_complete(struct prompt *pr, struct screen_write_ctx *ctx, u_int ax,
free(ud); free(ud);
} }
/* Expand prompt string using the current input. */ /* Create the prompt format tree using the current input. */
static char * static struct format_tree *
prompt_expand(struct prompt *pr) prompt_format_tree(struct prompt *pr)
{ {
struct format_tree *ft; struct format_tree *ft;
char *expanded, *prompt, *tmp; char *tmp;
if (cmd_find_valid_state(&pr->state)) if (cmd_find_valid_state(&pr->state))
ft = format_create_from_state(NULL, NULL, &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_flags", "%s", prompt_flags_to_string(pr->flags));
format_add(ft, "prompt_type", "%s", prompt_type_string(pr->type)); 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) if (pr->flags & PROMPT_COMMANDMODE)
format_add(ft, "command_prompt", "1"); format_add(ft, "command_prompt", "1");
else else
format_add(ft, "command_prompt", "0"); 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); expanded = format_expand_time(ft, pr->message_format);
free(prompt); free(prompt);
format_free(ft);
return (expanded); return (expanded);
} }
/* Work out the width used by the prompt string. */ /* Get the effective message style for the current prompt. */
static u_int static void
prompt_width(struct prompt *pr, u_int aw) prompt_effective_style(struct prompt *pr, struct style *sy,
struct format_tree *ft)
{ {
char *expanded; const char *s;
u_int start; struct grid_cell *gc;
char *expanded;
expanded = prompt_expand(pr); if (pr->flags & PROMPT_COMMANDMODE) {
start = format_width(expanded); s = pr->command_style_str;
if (start > aw) gc = &pr->command_style;
start = aw; } else {
free(expanded); s = pr->style_str;
return (start); 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. */ /* 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_write_ctx *ctx = pd->ctx;
struct screen *s = ctx->s; struct screen *s = ctx->s;
u_int ax = pd->area_x, py = pd->prompt_line; u_int ax = pd->area_x, py = pd->prompt_line, *cx;
u_int aw = pd->area_width, *cx = pd->cursor_x; u_int aw = pd->area_width;
struct grid_cell gc; struct grid_cell gc;
u_int i, offset, left, start, width; struct prompt_layout pl;
u_int pcursor, pwidth; struct style sy;
u_int i, width, pcursor;
char *expanded; char *expanded;
/* Choose the cursor colour and style for this prompt. */
if (pr->flags & PROMPT_COMMANDMODE) { if (pr->flags & PROMPT_COMMANDMODE) {
memcpy(&gc, &pr->command_style, sizeof gc);
s->default_cstyle = pr->command_cstyle; s->default_cstyle = pr->command_cstyle;
s->default_mode = pr->command_cmode; s->default_mode = pr->command_cmode;
s->default_ccolour = pr->command_ccolour; s->default_ccolour = pr->command_ccolour;
} else { } else {
memcpy(&gc, &pr->style, sizeof gc);
s->default_cstyle = pr->cstyle; s->default_cstyle = pr->cstyle;
s->default_mode = pr->cmode; s->default_mode = pr->cmode;
s->default_ccolour = pr->ccolour; s->default_ccolour = pr->ccolour;
} }
expanded = prompt_expand(pr); prompt_layout(pr, ax, aw, &pl, &expanded, &sy);
start = format_width(expanded); memcpy(&gc, &sy.gc, sizeof gc);
if (start > aw) cx = pd->cursor_x;
start = aw; *cx = pl.cursor_x;
*cx = ax + start;
screen_write_cursormove(ctx, ax, py, 0); screen_write_cursormove(ctx, ax, py, 0);
format_draw(ctx, &gc, aw, expanded, NULL, 0); if (sy.fill != 8)
screen_write_cursormove(ctx, ax + start, py, 0); screen_write_clearcharacter(ctx, aw, sy.fill);
free(expanded);
left = aw - start;
if (left == 0)
return;
pcursor = utf8_strwidth(pr->buffer, pr->index); 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; if (pl.content_width != 0) {
for (i = 0; pr->buffer[i].size != 0; i++) { screen_write_cursormove(ctx, pl.content_x, py, 0);
if (!prompt_redraw_quote(pr, pcursor, ctx, offset, pwidth, if (pl.label_width != 0)
&width, &gc)) format_draw(ctx, &gc, pl.label_width, expanded, NULL,
break; 0);
if (!prompt_redraw_character(ctx, offset, pwidth, &width, &gc,
&pr->buffer[i])) screen_write_cursormove(ctx, pl.input_x, py, 0);
break; 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); free(expanded);
prompt_draw_complete(pr, ctx, ax, aw, *cx, py, &gc);
} }
/* Move cursor in prompt from a mouse position. */ /* 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; struct utf8_data *ud;
enum prompt_key_result result; enum prompt_key_result result;
u_int cx, start, left, pcursor, pwidth, offset, width; struct prompt_layout pl;
u_int target; struct style sy;
char *expanded;
u_int pwidth, width, target;
size_t idx; size_t idx;
if (x < ax || x >= ax + aw) if (x < ax || x >= ax + aw)
return (PROMPT_KEY_NOT_HANDLED); return (PROMPT_KEY_NOT_HANDLED);
start = prompt_width(pr, aw); prompt_layout(pr, ax, aw, &pl, &expanded, &sy);
left = aw - start; free(expanded);
if (left == 0)
if (pl.input_width == 0)
return (PROMPT_KEY_HANDLED); return (PROMPT_KEY_HANDLED);
pcursor = utf8_strwidth(pr->buffer, pr->index);
pwidth = utf8_strwidth(pr->buffer, -1); pwidth = utf8_strwidth(pr->buffer, -1);
if (pr->flags & PROMPT_QUOTENEXT) if (pr->flags & PROMPT_QUOTENEXT)
pwidth++; pwidth++;
if (pcursor >= left)
offset = (pcursor - left) + 1;
else
offset = 0;
cx = ax + start + pcursor - offset; result = prompt_mouse_complete(pr, x, pl.cursor_x, pl.content_x,
result = prompt_mouse_complete(pr, x, cx, ax, aw, redraw); pl.content_width, redraw);
if (result != PROMPT_KEY_NOT_HANDLED) if (result != PROMPT_KEY_NOT_HANDLED)
return (result); return (result);
if (x <= ax + start) if (x <= pl.input_x)
target = offset; target = pl.input_offset;
else else
target = offset + x - (ax + start); target = pl.input_offset + x - pl.input_x;
if (target > pwidth) if (target > pwidth)
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> * Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>
@@ -22,9 +22,27 @@
#include "tmux.h" #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 void
resize_window(struct window *w, u_int sx, u_int sy, int xpixel, int ypixel) 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; int zoomed;
/* Check size limits. */ /* 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); tty_update_window_offset(w);
server_redraw_window(w); server_redraw_window(w);
events_fire_window("window-layout-changed", 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; 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> * 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. */ /* Set client session. */
void void
server_client_set_session(struct client *c, struct session *s) 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); alerts_check_session(s);
tty_update_client_offset(c); tty_update_client_offset(c);
status_timer_start(c); status_timer_start(c);
events_fire_client("client-session-changed", c); server_client_fire_session_changed(c, old);
server_redraw_client(c); server_redraw_client(c);
} }
@@ -466,6 +522,8 @@ server_client_lost(struct client *c)
server_client_attached_lost(c); server_client_attached_lost(c);
events_fire_client("client-detached", 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) if (c->flags & CLIENT_CONTROL)
control_stop(c); control_stop(c);
@@ -2421,6 +2479,7 @@ server_client_dispatch(struct imsg *imsg, void *arg)
struct client *c = arg; struct client *c = arg;
ssize_t datalen; ssize_t datalen;
struct session *s; struct session *s;
u_int old_sx, old_sy;
if (c->flags & CLIENT_DEAD) if (c->flags & CLIENT_DEAD)
return; return;
@@ -2459,6 +2518,8 @@ server_client_dispatch(struct imsg *imsg, void *arg)
if (c->flags & CLIENT_CONTROL) if (c->flags & CLIENT_CONTROL)
break; break;
server_client_update_latest(c); server_client_update_latest(c);
old_sx = c->tty.sx;
old_sy = c->tty.sy;
tty_resize(&c->tty); tty_resize(&c->tty);
tty_repeat_requests(&c->tty, 0); tty_repeat_requests(&c->tty, 0);
recalculate_sizes(); recalculate_sizes();
@@ -2468,7 +2529,7 @@ server_client_dispatch(struct imsg *imsg, void *arg)
c->overlay_resize(c, c->overlay_data); c->overlay_resize(c, c->overlay_data);
server_redraw_client(c); server_redraw_client(c);
if (c->session != NULL) if (c->session != NULL)
events_fire_client("client-resized", c); server_client_fire_resized(c, old_sx, old_sy);
break; break;
case MSG_EXITING: case MSG_EXITING:
if (datalen != 0) if (datalen != 0)
@@ -2775,6 +2836,8 @@ server_client_dispatch_identify(struct client *c, struct imsg *imsg)
close(c->out_fd); close(c->out_fd);
c->out_fd = -1; 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 pasting has taken too long, turn it off. */
if (c->flags & (CLIENT_BRACKETPASTING|CLIENT_ASSUMEPASTING) && 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> * Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>
@@ -471,6 +471,29 @@ session_last(struct session *s)
return (session_set_current(s, wl)); 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 .*/ /* Set current winlink to wl .*/
int int
session_set_current(struct session *s, struct winlink *wl) 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); winlink_clear_flags(wl);
window_update_activity(wl->window); window_update_activity(wl->window);
tty_update_window_offset(wl->window); tty_update_window_offset(wl->window);
events_fire_session("session-window-changed", s); session_fire_window_changed(s, wl, old);
return (0); return (0);
} }
@@ -540,12 +563,33 @@ session_group_new(const char *name)
return (sg); 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. */ /* Add a session to a session group. */
void void
session_group_add(struct session_group *sg, struct session *s) 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); 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. */ /* 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) if ((sg = session_group_contains(s)) == NULL)
return; return;
session_group_fire("session-removed-from-group", sg, s);
TAILQ_REMOVE(&sg->sessions, s, gentry); TAILQ_REMOVE(&sg->sessions, s, gentry);
if (TAILQ_EMPTY(&sg->sessions)) { if (TAILQ_EMPTY(&sg->sessions)) {
RB_REMOVE(session_groups, &session_groups, sg); 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> * 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); session_select(s, sc->wl->idx);
/* Fire notification if new window. */ /* 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); events_fire_winlink("window-linked", sc->wl);
}
session_group_synchronize_from(s); session_group_synchronize_from(s);
return (sc->wl); 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> .\" Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>
.\" .\"
@@ -14,7 +14,7 @@
.\" IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING .\" IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
.\" OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. .\" 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 .Dt TMUX 1
.Os .Os
.Sh NAME .Sh NAME
@@ -4077,6 +4077,21 @@ or full window width (with
.Fl v ) , .Fl v ) ,
instead of splitting the active pane. instead of splitting the active pane.
.Pp .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 .Fl k
keeps the pane open after the optional keeps the pane open after the optional
.Ar shell\-command .Ar shell\-command
@@ -6343,6 +6358,10 @@ See
Run when a client becomes the latest active client of its session. Run when a client becomes the latest active client of its session.
.It client\-attached .It client\-attached
Run when a client is 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 .It client\-detached
Run when a client is detached. Run when a client is detached.
.It client\-focus\-in .It client\-focus\-in
@@ -6361,6 +6380,8 @@ Run when a client switches to a dark theme.
Run when a command fails. Run when a command fails.
.It marked\-pane\-changed .It marked\-pane\-changed
Run when the marked pane is set or cleared. Run when the marked pane is set or cleared.
.It pane\-bell
Run when a pane receives a bell.
.It pane\-command\-finished .It pane\-command\-finished
Run when an OSC 133 command finishes in a pane. Run when an OSC 133 command finishes in a pane.
The event payload includes The event payload includes
@@ -6394,6 +6415,8 @@ Run when a pane changes mode.
Run when a pane enters a mode. Run when a pane enters a mode.
.It pane\-mode\-exited .It pane\-mode\-exited
Run when a pane exits a mode. Run when a pane exits a mode.
.It pane\-moved
Run when a pane is moved to another window.
.It pane\-prompt\-closed .It pane\-prompt\-closed
Run when a prompt in a pane is closed. Run when a prompt in a pane is closed.
.It pane\-prompt\-opened .It pane\-prompt\-opened
@@ -6412,10 +6435,18 @@ Run when a pane title is changed.
Run when a session is closed. Run when a session is closed.
.It session\-created .It session\-created
Run when a new session is 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 .It session\-renamed
Run when a session is renamed. Run when a session is renamed.
.It session\-window\-changed .It session\-window\-changed
Run when a session changes its active window. 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 .It window\-layout\-changed
Run when a window layout is changed. Run when a window layout is changed.
.It window\-linked .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> * Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>
@@ -2127,6 +2127,8 @@ struct prompt_create_data {
struct grid_cell style; struct grid_cell style;
struct grid_cell command_style; struct grid_cell command_style;
const char *style_str;
const char *command_style_str;
enum screen_cursor_style cstyle; enum screen_cursor_style cstyle;
enum screen_cursor_style command_cstyle; enum screen_cursor_style command_cstyle;
int ccolour; int ccolour;
@@ -2496,6 +2498,8 @@ struct spawn_context {
#define SPAWN_EMPTY 0x40 #define SPAWN_EMPTY 0x40
#define SPAWN_ZOOM 0x80 #define SPAWN_ZOOM 0x80
#define SPAWN_FLOATING 0x100 #define SPAWN_FLOATING 0x100
#define SPAWN_HORIZONTAL 0x200
#define SPAWN_SPLIT 0x400
}; };
/* Paste buffer. */ /* Paste buffer. */
@@ -3670,6 +3674,8 @@ int window_has_floating_panes(struct window *);
int window_has_pane(struct window *, struct window_pane *); int window_has_pane(struct window *, struct window_pane *);
int window_set_active_pane(struct window *, struct window_pane *, int window_set_active_pane(struct window *, struct window_pane *,
int); int);
void window_fire_pane_moved(struct window_pane *, struct window *,
int, struct window *, int);
void window_update_focus(struct window *); void window_update_focus(struct window *);
void window_pane_update_focus(struct window_pane *); void window_pane_update_focus(struct window_pane *);
void window_redraw_active_switch(struct window *, 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 window *, struct window_pane *, int, char **);
struct layout_cell *layout_get_floating_cell(struct cmdq_item *, struct args *, struct layout_cell *layout_get_floating_cell(struct cmdq_item *, struct args *,
enum pane_lines, struct window *, struct window_pane *, enum pane_lines, struct window *, struct window_pane *,
char **); int, char **);
int layout_floating_args_parse(struct cmdq_item *, struct args *, int layout_floating_args_parse(struct cmdq_item *, struct args *,
enum pane_lines, struct window *, struct layout_geometry *, enum pane_lines, struct window *, struct layout_geometry *,
char **); 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_remove_tile(struct window *, struct layout_cell *);
int layout_insert_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> * 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, static void window_copy_update_cursor(struct window_mode_entry *, u_int,
u_int); u_int);
static void window_copy_start_selection(struct window_mode_entry *); 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 *, static int window_copy_adjust_selection(struct window_mode_entry *,
u_int *, u_int *); 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_set_selection(struct window_mode_entry *, int, int);
static int window_copy_update_selection(struct window_mode_entry *, int, static int window_copy_update_selection(struct window_mode_entry *, int,
int); int);
@@ -862,7 +866,7 @@ window_copy_scroll1(struct window_mode_entry *wme, struct window_pane *wp,
if (data->searchmark != NULL && !data->timeout) if (data->searchmark != NULL && !data->timeout)
window_copy_search_marks(wme, NULL, data->searchregex, 1); 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_pane_scrollbar_show(wp, 1);
window_copy_redraw_screen(wme, 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; data->cy += delta;
} }
window_copy_update_selection(wme, 0, 0); window_copy_update_selection_view(wme, 0, 0);
return (WINDOW_COPY_CMD_REDRAW); 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_CANCEL);
return (WINDOW_COPY_CMD_NOTHING); return (WINDOW_COPY_CMD_NOTHING);
} }
for (; np != 0; np--) for (; np != 0; np--)
window_copy_cursor_down(wme, 1); window_copy_cursor_down(wme, 1);
if (data->scroll_exit && data->oy == 0 && data->screen.sel == NULL) 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_mode_entry *wme = cs->wme;
struct window_copy_mode_data *data = wme->data; struct window_copy_mode_data *data = wme->data;
u_int np = wme->prefix; 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--) for (; np != 0; np--)
window_copy_cursor_down(wme, 1); 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_mode_entry *wme = cs->wme;
struct window_copy_mode_data *data = wme->data; struct window_copy_mode_data *data = wme->data;
u_int np = wme->prefix; u_int np = wme->prefix;
int dragging;
/* /*
* If at the top, nothing can change, so return WINDOW_COPY_CMD_NOTHING * 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)) if (data->oy == screen_hsize(data->backing))
return (WINDOW_COPY_CMD_NOTHING); 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--) for (; np != 0; np--)
window_copy_cursor_up(wme, 1); window_copy_cursor_up(wme, 1);
return (WINDOW_COPY_CMD_MOVE); 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); data->cy = cy + (data->oy - oy);
break; break;
} }
window_copy_update_selection(wme, 0, 0); window_copy_update_selection_view(wme, 0, 0);
return (WINDOW_COPY_CMD_REDRAW); 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); 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 static int
window_copy_adjust_selection(struct window_mode_entry *wme, u_int *selx, window_copy_adjust_selection(struct window_mode_entry *wme, u_int *selx,
u_int *sely) 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)); 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 static int
window_copy_set_selection(struct window_mode_entry *wme, int may_redraw, window_copy_set_selection(struct window_mode_entry *wme, int may_redraw,
int no_reset) 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) if (data->searchmark != NULL && !data->timeout)
window_copy_search_marks(wme, NULL, data->searchregex, 1); 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_numbers_active(wme)) {
if (window_copy_line_number_mode(wme) != if (window_copy_line_number_mode(wme) !=
WINDOW_COPY_LINE_NUMBERS_ABSOLUTE) { 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) if (data->searchmark != NULL && !data->timeout)
window_copy_search_marks(wme, NULL, data->searchregex, 1); 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_numbers_active(wme)) {
if (window_copy_line_number_mode(wme) != if (window_copy_line_number_mode(wme) !=
WINDOW_COPY_LINE_NUMBERS_ABSOLUTE) { 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_mode_entry *wme;
struct window_copy_mode_data *data; struct window_copy_mode_data *data;
u_int x, y, yg; u_int x, y, yg;
int inside_selection, on_start, on_end;
if (c == NULL) if (c == NULL)
return; 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; c->tty.mouse_drag_release = window_copy_drag_release;
data = wme->data; 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)); x = window_copy_cursor_unoffset(wme, x, screen_size_x(&data->screen));
yg = screen_hsize(data->backing) + y - data->oy; 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; data->selflag = SEL_CHAR;
}
switch (data->selflag) { switch (data->selflag) {
case SEL_WORD: case SEL_WORD:
if (data->separators != NULL) { if (data->separators != NULL) {
@@ -6777,7 +6911,15 @@ window_copy_start_drag(struct client *c, struct mouse_event *m)
break; break;
case SEL_CHAR: case SEL_CHAR:
window_copy_update_cursor(wme, x, y); 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; break;
} }
@@ -6853,6 +6995,7 @@ window_copy_drag_release(struct client *c, struct mouse_event *m)
data = wme->data; data = wme->data;
if (window_copy_line_numbers_active(wme)) if (window_copy_line_numbers_active(wme))
window_copy_drag_update(c, m); window_copy_drag_update(c, m);
data->cursordrag = CURSORDRAG_NONE;
evtimer_del(&data->dragtimer); 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> * 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); 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 static void
window_fire_pane_mode_changed(const char *name, struct window_pane *wp, window_fire_pane_mode_changed(const char *name, struct window_pane *wp,
const char *previous, const char *current, int entered) const char *previous, const char *current, int entered)
@@ -488,6 +511,9 @@ window_add_ref(struct window *w, const char *from)
void void
window_remove_ref(struct window *w, const char *from) window_remove_ref(struct window *w, const char *from)
{ {
if (w->references == 1)
events_fire_window("window-closed", w);
w->references--; w->references--;
log_debug("%s: @%u %s, now %d", __func__, w->id, from, 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) { if (w->active != NULL) {
window_pane_stack_remove(&w->last_panes, w->active); window_pane_stack_remove(&w->last_panes, w->active);
w->active->flags |= PANE_CHANGED; w->active->flags |= PANE_CHANGED;
events_fire_window("window-pane-changed", w); window_fire_pane_changed(w, w->active, wp);
window_update_focus(w); window_update_focus(w);
} }
} }