Merge remote-tracking branch 'origin/master' into command_parser

# Conflicts:
#	cmd-queue.c
#	notify.c
#	options.c
This commit is contained in:
Nicholas Marriott
2026-07-13 11:12:46 +01:00
247 changed files with 12071 additions and 2193 deletions

View File

@@ -25,12 +25,15 @@ jobs:
- name: ubuntu-24.04-x64
runner: ubuntu-24.04
make: make
# - name: ubuntu-24.04-arm64
# runner: ubuntu-24.04-arm
# make: make
# - name: macos-26-arm64
# runner: macos-26
# make: gmake
configure: --enable-utf8proc --enable-asan
#- name: ubuntu-24.04-arm64
# runner: ubuntu-24.04-arm
# make: make
# configure: --enable-utf8proc --enable-asan
- name: macos-26-arm64
runner: macos-26
make: gmake
configure: --enable-utf8proc --enable-asan
steps:
- name: checkout
@@ -65,11 +68,19 @@ jobs:
- name: build
run: |
sh autogen.sh
./configure --enable-utf8proc
sh autogen.sh
./configure ${{ matrix.configure }}
${{ matrix.make }} -j"$(getconf _NPROCESSORS_ONLN)"
- name: test
run: |
cd regress
${{ matrix.make }}
- name: logs
if: failure()
uses: actions/upload-artifact@v4
with:
name: regress-logs-${{ matrix.name }}
path: regress/logs/*.log
if-no-files-found: ignore

1
.gitignore vendored
View File

@@ -21,6 +21,7 @@ configure
core
etc/
fuzz/*-fuzzer
regress/logs/
tags
tmux
tmux.1.*

View File

@@ -13,7 +13,8 @@ AM_CPPFLAGS += @XOPEN_DEFINES@ \
-DTMUX_VERSION='"@VERSION@"' \
-DTMUX_CONF='"$(sysconfdir)/tmux.conf:~/.tmux.conf:$$XDG_CONFIG_HOME/tmux/tmux.conf:~/.config/tmux/tmux.conf"' \
-DTMUX_LOCK_CMD='"@DEFAULT_LOCK_CMD@"' \
-DTMUX_TERM='"@DEFAULT_TERM@"'
-DTMUX_TERM='"@DEFAULT_TERM@"' \
-DTMUX_MOUSE=1
# Additional object files.
LDADD = $(LIBOBJS)
@@ -42,7 +43,8 @@ AM_CPPFLAGS += -DDEBUG
endif
AM_CPPFLAGS += -iquote.
if IS_ASAN
AM_CFLAGS += -fsanitize=address
AM_CPPFLAGS += -DASAN
AM_CFLAGS += -fsanitize=address -fno-omit-frame-pointer
AM_LDFLAGS += -fsanitize=address
endif
endif
@@ -160,13 +162,16 @@ dist_tmux_SOURCES = \
control-notify.c \
control.c \
environ.c \
events-payload.c \
events.c \
file.c \
format.c \
format-draw.c \
format.c \
fuzzy.c \
grid-reader.c \
grid-view.c \
grid.c \
hooks.c \
hyperlinks.c \
input-keys.c \
input.c \
@@ -179,15 +184,15 @@ dist_tmux_SOURCES = \
log.c \
menu.c \
mode-tree.c \
monitor.c \
names.c \
notify.c \
options-table.c \
options.c \
paste.c \
popup.c \
proc.c \
prompt.c \
prompt-history.c \
prompt.c \
regsub.c \
resize.c \
screen-redraw.c \
@@ -202,9 +207,9 @@ dist_tmux_SOURCES = \
spawn.c \
status.c \
style.c \
tmux-protocol.h \
tmux.c \
tmux.h \
tmux-protocol.h \
tty-acs.c \
tty-draw.c \
tty-features.c \

1
SECURITY.md Normal file
View File

@@ -0,0 +1 @@
Please report security issues to nicholas.marriott@gmail.com.

View File

@@ -1,4 +1,4 @@
/* $OpenBSD$ */
/* $OpenBSD: alerts.c,v 1.35 2026/07/10 13:38:45 nicm Exp $ */
/*
* Copyright (c) 2015 Nicholas Marriott <nicholas.marriott@gmail.com>
@@ -205,7 +205,7 @@ alerts_check_bell(struct window *w)
}
if (!alerts_action_applies(wl, "bell-action"))
continue;
notify_winlink("alert-bell", wl);
events_fire_winlink("alert-bell", wl);
if (s->flags & SESSION_ALERTED)
continue;
@@ -241,7 +241,7 @@ alerts_check_activity(struct window *w)
}
if (!alerts_action_applies(wl, "activity-action"))
continue;
notify_winlink("alert-activity", wl);
events_fire_winlink("alert-activity", wl);
if (s->flags & SESSION_ALERTED)
continue;
@@ -277,7 +277,7 @@ alerts_check_silence(struct window *w)
}
if (!alerts_action_applies(wl, "silence-action"))
continue;
notify_winlink("alert-silence", wl);
events_fire_winlink("alert-silence", wl);
if (s->flags & SESSION_ALERTED)
continue;

View File

@@ -1,4 +1,4 @@
/* $OpenBSD$ */
/* $OpenBSD: arguments.c,v 1.66 2026/06/26 09:54:56 nicm Exp $ */
/*
* Copyright (c) 2010 Nicholas Marriott <nicholas.marriott@gmail.com>

View File

@@ -1,4 +1,4 @@
/* $OpenBSD$ */
/* $OpenBSD: attributes.c,v 1.13 2026/07/02 08:51:05 nicm Exp $ */
/*
* Copyright (c) 2009 Joshua Elsasser <josh@elsasser.org>

2
cfg.c
View File

@@ -1,4 +1,4 @@
/* $OpenBSD$ */
/* $OpenBSD: cfg.c,v 1.89 2026/06/25 11:39:11 nicm Exp $ */
/*
* Copyright (c) 2008 Nicholas Marriott <nicholas.marriott@gmail.com>

View File

@@ -1,4 +1,4 @@
/* $OpenBSD$ */
/* $OpenBSD: client.c,v 1.166 2026/07/10 15:45:11 nicm Exp $ */
/*
* Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>
@@ -257,9 +257,8 @@ client_main(struct event_base *base, int argc, char **argv, uint64_t flags,
pid_t ppid;
enum msgtype msg;
struct termios tio, saved_tio;
size_t size, linesize = 0;
ssize_t linelen;
char *line = NULL, **caps = NULL, *cause;
size_t size;
char **caps = NULL, *cause;
u_int ncaps = 0;
/* Set up the initial command. */
@@ -410,11 +409,6 @@ client_main(struct event_base *base, int argc, char **argv, uint64_t flags,
client_exec(client_execshell, client_execcmd);
}
/* Restore streams to blocking. */
setblocking(STDIN_FILENO, 1);
setblocking(STDOUT_FILENO, 1);
setblocking(STDERR_FILENO, 1);
/* Print the exit message, if any, and exit. */
if (client_attached) {
if (client_exitreason != CLIENT_EXIT_NONE)
@@ -429,15 +423,8 @@ client_main(struct event_base *base, int argc, char **argv, uint64_t flags,
else
printf("%%exit\n");
fflush(stdout);
if (client_flags & CLIENT_CONTROL_WAITEXIT) {
setvbuf(stdin, NULL, _IOLBF, 0);
for (;;) {
linelen = getline(&line, &linesize, stdin);
if (linelen <= 1)
break;
}
free(line);
}
if (client_flags & CLIENT_CONTROL_WAITEXIT)
control_wait_exit(STDIN_FILENO);
if (client_flags & CLIENT_CONTROLCONTROL) {
printf("\033\\");
fflush(stdout);
@@ -445,6 +432,12 @@ client_main(struct event_base *base, int argc, char **argv, uint64_t flags,
}
} else if (client_exitreason != CLIENT_EXIT_NONE)
fprintf(stderr, "%s\n", client_exit_message());
/* Restore the streams to blocking. */
setblocking(STDIN_FILENO, 1);
setblocking(STDOUT_FILENO, 1);
setblocking(STDERR_FILENO, 1);
return (client_exitval);
}

View File

@@ -1,4 +1,4 @@
/* $OpenBSD$ */
/* $OpenBSD: cmd-attach-session.c,v 1.91 2026/07/10 13:38:45 nicm Exp $ */
/*
* Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>
@@ -163,7 +163,7 @@ cmd_attach_session(struct cmdq_item *item, const char *tflag, int dflag,
if (~c->flags & CLIENT_CONTROL)
proc_send(c->peer, MSG_READY, -1, NULL, 0);
notify_client("client-attached", c);
events_fire_client("client-attached", c);
c->flags |= CLIENT_ATTACHED;
}

View File

@@ -1,4 +1,4 @@
/* $OpenBSD$ */
/* $OpenBSD: cmd-bind-key.c,v 1.47 2025/04/09 07:03:04 nicm Exp $ */
/*
* Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>

View File

@@ -1,4 +1,4 @@
/* $OpenBSD$ */
/* $OpenBSD: cmd-break-pane.c,v 1.72 2026/07/10 13:38:45 nicm Exp $ */
/*
* Copyright (c) 2009 Nicholas Marriott <nicholas.marriott@gmail.com>
@@ -51,10 +51,9 @@ cmd_break_pane_float(struct cmdq_item *item, struct args *args,
struct window *w, struct window_pane *wp)
{
struct layout_cell *lc = wp->layout_cell;
u_int sx = lc->saved_sx, sy = lc->saved_sy;
int ox = lc->saved_xoff, oy = lc->saved_yoff;
char *cause = NULL;
enum pane_lines lines = window_get_pane_lines(w);
struct layout_geometry *fg = &lc->fg;
if (window_pane_is_floating(wp)) {
cmdq_error(item, "pane is already floating");
@@ -65,14 +64,13 @@ cmd_break_pane_float(struct cmdq_item *item, struct args *args,
return (CMD_RETURN_ERROR);
}
if (layout_floating_args_parse(item, args, lines, w, &sx, &sy, &ox, &oy,
&cause) != 0) {
if (layout_floating_args_parse(item, args, lines, w, fg, &cause) != 0) {
cmdq_error(item, "failed to float pane: %s", cause);
free(cause);
return (CMD_RETURN_ERROR);
}
layout_remove_tile(w, lc);
layout_set_size(lc, sx, sy, ox, oy);
layout_set_size(lc, fg->sx, fg->sy, fg->xoff, fg->yoff);
lc->flags |= LAYOUT_CELL_FLOATING;
TAILQ_REMOVE(&w->z_index, wp, zentry);
@@ -82,7 +80,7 @@ cmd_break_pane_float(struct cmdq_item *item, struct args *args,
window_set_active_pane(w, wp, 1);
layout_fix_offsets(w);
layout_fix_panes(w, NULL);
notify_window("window-layout-changed", w);
events_fire_window("window-layout-changed", w);
server_redraw_window(w);
return (CMD_RETURN_NORMAL);
@@ -101,7 +99,7 @@ cmd_break_pane_exec(struct cmd *self, struct cmdq_item *item)
struct session *dst_s = target->s;
struct window_pane *wp = source->wp;
struct window *w = wl->window;
char *newname, *cause, *cp;
char *cause, *cp;
int idx = target->idx, before;
const char *template, *name = args_get(args, 'n');
@@ -153,6 +151,7 @@ cmd_break_pane_exec(struct cmd *self, struct cmdq_item *item)
layout_close_pane(wp);
w = wp->window = window_create(w->sx, w->sy, w->xpixel, w->ypixel);
window_add_ref(w, __func__);
options_set_parent(wp->options, w->options);
wp->flags |= (PANE_STYLECHANGED|PANE_THEMECHANGED);
TAILQ_INSERT_HEAD(&w->panes, wp, entry);
@@ -160,12 +159,11 @@ cmd_break_pane_exec(struct cmd *self, struct cmdq_item *item)
w->active = wp;
w->latest = tc;
if (name == NULL) {
newname = default_window_name(w);
window_set_name(w, newname, 0);
free(newname);
} else {
window_set_name(w, name, 0);
free(w->name);
if (name == NULL)
w->name = default_window_name(w);
else {
w->name = clean_name(name, 0);
options_set_number(w->options, "automatic-rename", 0);
}
@@ -176,6 +174,7 @@ cmd_break_pane_exec(struct cmd *self, struct cmdq_item *item)
if (idx == -1)
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__);
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$ */
/* $OpenBSD: cmd-capture-pane.c,v 1.67 2026/07/02 10:34:14 nicm Exp $ */
/*
* Copyright (c) 2009 Jonathan Alvarado <radobobo@users.sourceforge.net>

View File

@@ -1,4 +1,4 @@
/* $OpenBSD$ */
/* $OpenBSD: cmd-choose-tree.c,v 1.57 2026/06/26 14:40:30 nicm Exp $ */
/*
* Copyright (c) 2012 Thomas Adam <thomas@xteddy.org>

View File

@@ -1,4 +1,4 @@
/* $OpenBSD$ */
/* $OpenBSD: cmd-command-prompt.c,v 1.75 2026/06/25 11:39:11 nicm Exp $ */
/*
* Copyright (c) 2008 Nicholas Marriott <nicholas.marriott@gmail.com>

View File

@@ -1,4 +1,4 @@
/* $OpenBSD$ */
/* $OpenBSD: cmd-confirm-before.c,v 1.61 2026/06/24 10:55:39 nicm Exp $ */
/*
* Copyright (c) 2009 Tiago Cunha <me@tiagocunha.org>

View File

@@ -1,4 +1,4 @@
/* $OpenBSD$ */
/* $OpenBSD: cmd-copy-mode.c,v 1.53 2026/07/08 08:07:42 nicm Exp $ */
/*
* Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>
@@ -30,8 +30,8 @@ const struct cmd_entry cmd_copy_mode_entry = {
.name = "copy-mode",
.alias = NULL,
.args = { "deHMqSs:t:u", 0, 0, NULL },
.usage = "[-deHMqSu] [-s src-pane] " CMD_TARGET_PANE_USAGE,
.args = { "dekHMqSs:t:u", 0, 0, NULL },
.usage = "[-dekHMqSu] [-s src-pane] " CMD_TARGET_PANE_USAGE,
.source = { 's', CMD_FIND_PANE, 0 },
.target = { 't', CMD_FIND_PANE, 0 },

View File

@@ -1,4 +1,4 @@
/* $OpenBSD$ */
/* $OpenBSD: cmd-detach-client.c,v 1.39 2026/05/22 15:22:43 nicm Exp $ */
/*
* Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>

View File

@@ -1,4 +1,4 @@
/* $OpenBSD$ */
/* $OpenBSD: cmd-display-menu.c,v 1.51 2026/07/07 09:45:09 nicm Exp $ */
/*
* Copyright (c) 2019 Nicholas Marriott <nicholas.marriott@gmail.com>
@@ -126,6 +126,10 @@ cmd_display_menu_get_pos(struct client *tc, struct cmdq_item *item,
format_add(ft, "popup_mouse_y", "%u", event->m.y);
}
/* Position of the previous menu, for -x/-y L. */
format_add(ft, "popup_last_x", "%u", tc->menu_last_px);
format_add(ft, "popup_last_y", "%u", tc->menu_last_py + h);
/*
* If there are any status lines, add this window position and the
* status line position.
@@ -238,6 +242,8 @@ cmd_display_menu_get_pos(struct client *tc, struct cmdq_item *item,
xp = "#{popup_pane_left}";
else if (strcmp(xp, "M") == 0)
xp = "#{popup_mouse_centre_x}";
else if (strcmp(xp, "L") == 0)
xp = "#{popup_last_x}";
else if (strcmp(xp, "W") == 0)
xp = "#{popup_window_status_line_x}";
p = format_expand(ft, xp);
@@ -258,6 +264,8 @@ cmd_display_menu_get_pos(struct client *tc, struct cmdq_item *item,
yp = "#{popup_pane_bottom}";
else if (strcmp(yp, "M") == 0)
yp = "#{popup_mouse_top}";
else if (strcmp(yp, "L") == 0)
yp = "#{popup_last_y}";
else if (strcmp(yp, "S") == 0)
yp = "#{popup_status_line_y}";
else if (strcmp(yp, "W") == 0)

View File

@@ -1,4 +1,4 @@
/* $OpenBSD$ */
/* $OpenBSD: cmd-display-message.c,v 1.65 2026/02/23 08:46:57 nicm Exp $ */
/*
* Copyright (c) 2009 Tiago Cunha <me@tiagocunha.org>

View File

@@ -1,4 +1,4 @@
/* $OpenBSD$ */
/* $OpenBSD: cmd-display-panes.c,v 1.56 2026/06/25 16:32:42 nicm Exp $ */
/*
* Copyright (c) 2009 Nicholas Marriott <nicholas.marriott@gmail.com>

View File

@@ -1,4 +1,4 @@
/* $OpenBSD$ */
/* $OpenBSD: cmd-find-window.c,v 1.56 2023/12/27 20:42:01 nicm Exp $ */
/*
* Copyright (c) 2009 Nicholas Marriott <nicholas.marriott@gmail.com>

View File

@@ -1,4 +1,4 @@
/* $OpenBSD$ */
/* $OpenBSD: cmd-find.c,v 1.86 2026/07/02 08:47:25 nicm Exp $ */
/*
* Copyright (c) 2015 Nicholas Marriott <nicholas.marriott@gmail.com>

View File

@@ -1,4 +1,4 @@
/* $OpenBSD$ */
/* $OpenBSD: cmd-if-shell.c,v 1.86 2025/08/01 09:05:51 nicm Exp $ */
/*
* Copyright (c) 2009 Tiago Cunha <me@tiagocunha.org>

View File

@@ -1,4 +1,4 @@
/* $OpenBSD$ */
/* $OpenBSD: cmd-join-pane.c,v 1.69 2026/07/10 13:38:45 nicm Exp $ */
/*
* Copyright (c) 2011 George Nachman <tmux@georgester.com>
@@ -71,8 +71,9 @@ cmd_join_pane_place(struct cmdq_item *item, struct winlink *wl,
struct window *w = wl->window;
struct layout_cell *lc = wp->layout_cell;
struct window_pane *owp;
int wx = w->sx, wy = w->sy, px = lc->sx;
int py = lc->sy, xoff = lc->xoff, yoff = lc->yoff;
int wx = w->sx, wy = w->sy;
int px = lc->g.sx, py = lc->g.sy;
int xoff = lc->g.xoff, yoff = lc->g.yoff;
int border = 1;
if (window_pane_get_pane_lines(wp) == PANE_LINES_NONE)
@@ -180,12 +181,12 @@ cmd_join_pane_place(struct cmdq_item *item, struct winlink *wl,
return (CMD_RETURN_ERROR);
}
if (xoff != lc->xoff || yoff != lc->yoff) {
lc->xoff = xoff;
lc->yoff = yoff;
if (xoff != lc->g.xoff || yoff != lc->g.yoff) {
lc->g.xoff = xoff;
lc->g.yoff = yoff;
layout_fix_panes(w, NULL);
}
notify_window("window-layout-changed", w);
events_fire_window("window-layout-changed", w);
server_redraw_window(w);
return (CMD_RETURN_NORMAL);
@@ -200,7 +201,7 @@ cmd_join_pane_move(struct cmdq_item *item, struct args *args,
const char *errstr, *argval;
const char flags[] = { 'U', 'D', 'L', 'R' };
char *cause = NULL, flag;
int xoff = lc->xoff, yoff = lc->yoff, adjust;
int xoff = lc->g.xoff, yoff = lc->g.yoff, adjust;
u_int i;
enum pane_lines lines = window_pane_get_pane_lines(wp);
@@ -251,11 +252,11 @@ cmd_join_pane_move(struct cmdq_item *item, struct args *args,
xoff += adjust;
}
if (xoff != lc->xoff || yoff != lc->yoff) {
lc->xoff = xoff;
lc->yoff = yoff;
if (xoff != lc->g.xoff || yoff != lc->g.yoff) {
lc->g.xoff = xoff;
lc->g.yoff = yoff;
layout_fix_panes(w, NULL);
notify_window("window-layout-changed", w);
events_fire_window("window-layout-changed", w);
server_redraw_window(w);
}
@@ -319,8 +320,8 @@ cmd_join_pane_mouse_move(struct client *c, struct mouse_event *m)
ly = m->statusat - 1;
if (x != lx || y != ly) {
lc->xoff += x - lx;
lc->yoff += y - ly;
lc->g.xoff += x - lx;
lc->g.yoff += y - ly;
layout_fix_panes(w, NULL);
server_redraw_window(w);
server_redraw_window_borders(w);
@@ -357,7 +358,46 @@ cmd_join_pane_zindex(struct cmdq_item *item, struct winlink *wl,
else
TAILQ_INSERT_TAIL(&w->z_index, wp, zentry);
notify_window("window-layout-changed", w);
events_fire_window("window-layout-changed", w);
server_redraw_window(w);
return (CMD_RETURN_NORMAL);
}
static enum cmd_retval
cmd_join_pane_tile(struct cmdq_item *item, struct args *args, struct window *w,
struct window_pane *wp)
{
struct layout_cell *lc = wp->layout_cell;
if (!window_pane_is_floating(wp)) {
cmdq_error(item, "pane is not floating");
return (CMD_RETURN_ERROR);
}
if (w->flags & WINDOW_ZOOMED) {
cmdq_error(item, "can't tile a pane while window is zoomed");
return (CMD_RETURN_ERROR);
}
lc->fg.sx = lc->g.sx;
lc->fg.sy = lc->g.sy;
lc->fg.xoff = lc->g.xoff;
lc->fg.yoff = lc->g.yoff;
if (layout_insert_tile(w, lc) != 0) {
cmdq_error(item, "no space for a new pane");
return (CMD_RETURN_ERROR);
}
lc->flags &= ~LAYOUT_CELL_FLOATING;
TAILQ_REMOVE(&w->z_index, wp, zentry);
TAILQ_INSERT_TAIL(&w->z_index, wp, zentry);
if (!args_has(args, 'd'))
window_set_active_pane(w, wp, 1);
layout_fix_offsets(w);
layout_fix_panes(w, NULL);
events_fire_window("window-layout-changed", w);
server_redraw_window(w);
return (CMD_RETURN_NORMAL);
@@ -412,6 +452,8 @@ cmd_join_pane_exec(struct cmd *self, struct cmdq_item *item)
server_unzoom_window(src_w);
if (src_wp == dst_wp) {
if (window_pane_is_floating(src_wp))
return (cmd_join_pane_tile(item, args, src_w, src_wp));
cmdq_error(item, "source and target panes must be different");
return (CMD_RETURN_ERROR);
}
@@ -459,8 +501,8 @@ cmd_join_pane_exec(struct cmd *self, struct cmdq_item *item)
if (window_count_panes(src_w, 1) == 0)
server_kill_window(src_w, 1);
else
notify_window("window-layout-changed", src_w);
notify_window("window-layout-changed", dst_w);
events_fire_window("window-layout-changed", src_w);
events_fire_window("window-layout-changed", dst_w);
return (CMD_RETURN_NORMAL);
}

View File

@@ -1,4 +1,4 @@
/* $OpenBSD$ */
/* $OpenBSD: cmd-kill-pane.c,v 1.34 2026/06/09 21:22:22 nicm Exp $ */
/*
* Copyright (c) 2009 Nicholas Marriott <nicholas.marriott@gmail.com>

View File

@@ -1,4 +1,4 @@
/* $OpenBSD$ */
/* $OpenBSD: cmd-kill-server.c,v 1.20 2021/08/21 10:22:39 nicm Exp $ */
/*
* Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>

View File

@@ -1,4 +1,4 @@
/* $OpenBSD$ */
/* $OpenBSD: cmd-kill-session.c,v 1.31 2026/06/09 12:57:40 nicm Exp $ */
/*
* Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>

View File

@@ -1,4 +1,4 @@
/* $OpenBSD$ */
/* $OpenBSD: cmd-kill-window.c,v 1.30 2026/06/09 12:57:40 nicm Exp $ */
/*
* Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>

View File

@@ -1,4 +1,4 @@
/* $OpenBSD$ */
/* $OpenBSD: cmd-list-buffers.c,v 1.41 2026/02/27 08:25:12 nicm Exp $ */
/*
* Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>

View File

@@ -1,4 +1,4 @@
/* $OpenBSD$ */
/* $OpenBSD: cmd-list-clients.c,v 1.42 2026/02/27 08:25:12 nicm Exp $ */
/*
* Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>

View File

@@ -1,4 +1,4 @@
/* $OpenBSD$ */
/* $OpenBSD: cmd-list-commands.c,v 1.15 2026/02/24 08:22:13 nicm Exp $ */
/*
* Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>

View File

@@ -1,4 +1,4 @@
/* $OpenBSD$ */
/* $OpenBSD: cmd-list-keys.c,v 1.78 2026/07/01 13:12:17 nicm Exp $ */
/*
* Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>

View File

@@ -1,4 +1,4 @@
/* $OpenBSD$ */
/* $OpenBSD: cmd-list-panes.c,v 1.40 2026/06/01 14:01:09 nicm Exp $ */
/*
* Copyright (c) 2009 Nicholas Marriott <nicholas.marriott@gmail.com>

View File

@@ -1,4 +1,4 @@
/* $OpenBSD$ */
/* $OpenBSD: cmd-list-sessions.c,v 1.36 2026/02/27 08:25:12 nicm Exp $ */
/*
* Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>

View File

@@ -1,4 +1,4 @@
/* $OpenBSD$ */
/* $OpenBSD: cmd-list-windows.c,v 1.50 2026/02/27 08:25:12 nicm Exp $ */
/*
* Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>

View File

@@ -1,4 +1,4 @@
/* $OpenBSD$ */
/* $OpenBSD: cmd-load-buffer.c,v 1.66 2025/10/28 07:32:26 nicm Exp $ */
/*
* Copyright (c) 2009 Tiago Cunha <me@tiagocunha.org>

View File

@@ -1,4 +1,4 @@
/* $OpenBSD$ */
/* $OpenBSD: cmd-lock-server.c,v 1.31 2021/08/21 10:22:39 nicm Exp $ */
/*
* Copyright (c) 2008 Nicholas Marriott <nicholas.marriott@gmail.com>

View File

@@ -1,4 +1,4 @@
/* $OpenBSD$ */
/* $OpenBSD: cmd-move-window.c,v 1.34 2021/08/21 10:22:39 nicm Exp $ */
/*
* Copyright (c) 2008 Nicholas Marriott <nicholas.marriott@gmail.com>

View File

@@ -1,4 +1,4 @@
/* $OpenBSD$ */
/* $OpenBSD: cmd-new-session.c,v 1.152 2026/07/10 13:38:45 nicm Exp $ */
/*
* Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>
@@ -325,7 +325,7 @@ cmd_new_session_exec(struct cmd *self, struct cmdq_item *item)
session_group_synchronize_to(s);
session_select(s, RB_MIN(winlinks, &s->windows)->idx);
}
notify_session("session-created", s);
events_fire_session("session-created", s);
/*
* Set the client to the new session. If a command client exists, it is

View File

@@ -1,4 +1,4 @@
/* $OpenBSD$ */
/* $OpenBSD: cmd-new-window.c,v 1.103 2026/07/08 08:07:42 nicm Exp $ */
/*
* Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>
@@ -38,8 +38,8 @@ const struct cmd_entry cmd_new_window_entry = {
.name = "new-window",
.alias = "neww",
.args = { "abc:de:F:kn:PSt:", 0, -1, NULL },
.usage = "[-abdkPS] [-c start-directory] [-e environment] [-F format] "
.args = { "abc:de:EF:kn:PSt:", 0, -1, NULL },
.usage = "[-abdEkPS] [-c start-directory] [-e environment] [-F format] "
"[-n window-name] " CMD_TARGET_WINDOW_USAGE
" [shell-command [argument ...]]",
@@ -60,12 +60,19 @@ cmd_new_window_exec(struct cmd *self, struct cmdq_item *item)
struct client *tc = cmdq_get_target_client(item);
struct session *s = target->s;
struct winlink *wl = target->wl, *new_wl = NULL;
int idx = target->idx, before;
int idx = target->idx, before, count = args_count(args);
char *cause = NULL, *cp, *expanded, *wname = NULL;
const char *template, *name;
struct cmd_find_state fs;
struct args_value *av;
if (args_has(args, 'E') &&
count != 0 &&
(count != 1 || *args_string(args, 0) != '\0')) {
cmdq_error(item, "command cannot be given for empty pane");
return (CMD_RETURN_ERROR);
}
/*
* If -S and -n are given and -t is not and a single window with this
* name already exists, select it.
@@ -134,6 +141,8 @@ cmd_new_window_exec(struct cmd *self, struct cmdq_item *item)
sc.cwd = args_get(args, 'c');
sc.flags = 0;
if (args_has(args, 'E') || (count == 1 && *args_string(args, 0) == '\0'))
sc.flags |= SPAWN_EMPTY;
if (args_has(args, 'd'))
sc.flags |= SPAWN_DETACHED;
if (args_has(args, 'k'))

View File

@@ -1,4 +1,4 @@
/* $OpenBSD$ */
/* $OpenBSD: cmd-parse.y,v 1.58 2026/04/27 12:31:11 nicm Exp $ */
/*
* Copyright (c) 2019 Nicholas Marriott <nicholas.marriott@gmail.com>

View File

@@ -1,4 +1,4 @@
/* $OpenBSD$ */
/* $OpenBSD: cmd-paste-buffer.c,v 1.44 2026/03/04 07:19:32 tb Exp $ */
/*
* Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>

View File

@@ -1,4 +1,4 @@
/* $OpenBSD$ */
/* $OpenBSD: cmd-pipe-pane.c,v 1.63 2026/04/28 08:47:55 nicm Exp $ */
/*
* Copyright (c) 2009 Nicholas Marriott <nicholas.marriott@gmail.com>

View File

@@ -1,4 +1,4 @@
/* $OpenBSD$ */
/* $OpenBSD: cmd-queue.c,v 1.120 2026/07/10 13:38:45 nicm Exp $ */
/*
* Copyright (c) 2013 Nicholas Marriott <nicholas.marriott@gmail.com>
@@ -350,74 +350,53 @@ cmdq_insert_after(struct cmdq_item *after, struct cmdq_item *item)
/* Insert a hook. */
void
cmdq_insert_hook(struct session *s, struct cmdq_item *item,
cmdq_insert_hook(__unused struct session *s, struct cmdq_item *item,
struct cmd_find_state *current, const char *fmt, ...)
{
struct cmdq_state *state = item->state;
struct cmd *cmd = item->cmd;
struct args *args = cmd_get_args(cmd);
struct args_entry *ae;
struct args_value *av;
struct options *oo;
struct event_payload *ep;
va_list ap;
char *name, tmp[32], flag, *arguments;
u_int i;
const char *value;
struct cmdq_item *new_item;
struct cmdq_state *new_state;
struct options_entry *o;
struct options_array_item *a;
struct cmd_parse_tree *tree;
if (item->state->flags & CMDQ_STATE_NOHOOKS)
return;
if (s == NULL)
oo = global_s_options;
else
oo = s->options;
va_start(ap, fmt);
xvasprintf(&name, fmt, ap);
va_end(ap);
o = options_get(oo, name);
if (o == NULL) {
free(name);
return;
}
log_debug("running hook %s (parent %p)", name, item);
/*
* The hooks get a new state because they should not update the current
* target or formats for any subsequent commands.
*/
new_state = cmdq_new_state(current, &state->event, CMDQ_STATE_NOHOOKS);
cmdq_add_format(new_state, "hook", "%s", name);
ep = event_payload_create();
if (current != NULL)
event_payload_set_target(ep, current);
event_payload_set_pointer(ep, "_cmdq_item", item, NULL, NULL);
arguments = args_print(args);
cmdq_add_format(new_state, "hook_arguments", "%s", arguments);
event_payload_set_string(ep, "arguments", "%s", arguments);
free(arguments);
for (i = 0; i < args_count(args); i++) {
xsnprintf(tmp, sizeof tmp, "hook_argument_%d", i);
cmdq_add_format(new_state, tmp, "%s", args_string(args, i));
xsnprintf(tmp, sizeof tmp, "argument_%u", i);
event_payload_set_string(ep, tmp, "%s", args_string(args, i));
}
flag = args_first(args, &ae);
while (flag != 0) {
value = args_get(args, flag);
if (value == NULL) {
xsnprintf(tmp, sizeof tmp, "hook_flag_%c", flag);
cmdq_add_format(new_state, tmp, "1");
} else {
xsnprintf(tmp, sizeof tmp, "hook_flag_%c", flag);
cmdq_add_format(new_state, tmp, "%s", value);
}
xsnprintf(tmp, sizeof tmp, "flag_%c", flag);
if (value == NULL)
event_payload_set_string(ep, tmp, "1");
else
event_payload_set_string(ep, tmp, "%s", value);
i = 0;
av = args_first_value(args, flag);
while (av != NULL) {
xsnprintf(tmp, sizeof tmp, "hook_flag_%c_%d", flag, i);
cmdq_add_format(new_state, tmp, "%s", av->string);
xsnprintf(tmp, sizeof tmp, "flag_%c_%u", flag, i);
event_payload_set_string(ep, tmp, "%s", av->string);
i++;
av = args_next_value(av);
}
@@ -425,20 +404,7 @@ cmdq_insert_hook(struct session *s, struct cmdq_item *item,
flag = args_next(&ae);
}
a = options_array_first(o);
while (a != NULL) {
tree = options_array_item_value(a)->cmd;
if (tree != NULL) {
new_item = cmd_invoke_get(tree, new_state, NULL);
if (item != NULL)
item = cmdq_insert_after(item, new_item);
else
item = cmdq_append(NULL, new_item);
}
a = options_array_next(a);
}
cmdq_free_state(new_state);
events_fire(name, ep);
free(name);
}

View File

@@ -1,4 +1,4 @@
/* $OpenBSD$ */
/* $OpenBSD: cmd-refresh-client.c,v 1.54 2026/07/05 08:24:00 nicm Exp $ */
/*
* Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>
@@ -46,36 +46,17 @@ const struct cmd_entry cmd_refresh_client_entry = {
static void
cmd_refresh_client_update_subscription(struct client *tc, const char *value)
{
char *copy, *split, *name, *what;
enum control_sub_type subtype;
int subid = -1;
char *name, *format;
enum monitor_type type;
int id;
copy = name = xstrdup(value);
if ((split = strchr(copy, ':')) == NULL) {
control_remove_sub(tc, copy);
goto out;
if (monitor_parse(value, &name, &type, &id, &format) != 0) {
control_remove_sub(tc, value);
return;
}
*split++ = '\0';
what = split;
if ((split = strchr(what, ':')) == NULL)
goto out;
*split++ = '\0';
if (strcmp(what, "%*") == 0)
subtype = CONTROL_SUB_ALL_PANES;
else if (sscanf(what, "%%%d", &subid) == 1 && subid >= 0)
subtype = CONTROL_SUB_PANE;
else if (strcmp(what, "@*") == 0)
subtype = CONTROL_SUB_ALL_WINDOWS;
else if (sscanf(what, "@%d", &subid) == 1 && subid >= 0)
subtype = CONTROL_SUB_WINDOW;
else
subtype = CONTROL_SUB_SESSION;
control_add_sub(tc, name, subtype, subid, split);
out:
free(copy);
control_add_sub(tc, name, type, id, format);
free(name);
free(format);
}
static enum cmd_retval

View File

@@ -1,4 +1,4 @@
/* $OpenBSD$ */
/* $OpenBSD: cmd-rename-session.c,v 1.39 2026/07/10 13:38:45 nicm Exp $ */
/*
* Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>
@@ -49,6 +49,8 @@ cmd_rename_session_exec(struct cmd *self, struct cmdq_item *item)
struct args *args = cmd_get_args(self);
struct cmd_find_state *target = cmdq_get_target(item);
struct session *s = target->s;
struct event_payload *ep;
struct cmd_find_state fs;
char *newname, *tmp;
tmp = format_single_from_target(item, args_string(args, 0));
@@ -69,13 +71,20 @@ cmd_rename_session_exec(struct cmd *self, struct cmdq_item *item)
return (CMD_RETURN_ERROR);
}
ep = event_payload_create();
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, "old_name", "%s", s->name);
event_payload_set_string(ep, "new_name", "%s", newname);
RB_REMOVE(sessions, &sessions, s);
free(s->name);
s->name = newname;
RB_INSERT(sessions, &sessions, s);
server_status_session(s);
notify_session("session-renamed", s);
events_fire("session-renamed", ep);
return (CMD_RETURN_NORMAL);
}

View File

@@ -1,4 +1,4 @@
/* $OpenBSD$ */
/* $OpenBSD: cmd-rename-window.c,v 1.30 2026/06/29 18:17:28 nicm Exp $ */
/*
* Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>

View File

@@ -1,4 +1,4 @@
/* $OpenBSD$ */
/* $OpenBSD: cmd-resize-pane.c,v 1.67 2026/07/10 13:38:45 nicm Exp $ */
/*
* Copyright (c) 2009 Nicholas Marriott <nicholas.marriott@gmail.com>
@@ -182,7 +182,7 @@ cmd_resize_pane_exec(struct cmd *self, struct cmdq_item *item)
if (lc->parent != NULL)
layout_fix_offsets(w);
layout_fix_panes(w, NULL);
notify_window("window-layout-changed", w);
events_fire_window("window-layout-changed", w);
server_redraw_window(w);
return (CMD_RETURN_NORMAL);
@@ -270,10 +270,10 @@ cmd_resize_pane_mouse_resize_move_floating(struct client *c,
if ((lx == left || lx == left + 1) && ly == wp->yoff - 1) {
/* Top left corner. */
new_sx = lc->sx + (lx - x);
new_sx = lc->g.sx + (lx - x);
if (new_sx < PANE_MINIMUM)
new_sx = PANE_MINIMUM;
new_sy = lc->sy + (ly - y);
new_sy = lc->g.sy + (ly - y);
if (new_sy < PANE_MINIMUM)
new_sy = PANE_MINIMUM;
new_xoff = x + 1; /* because mouse is on border at xoff - 1 */
@@ -283,65 +283,65 @@ cmd_resize_pane_mouse_resize_move_floating(struct client *c,
} else if ((lx == right + 1 || lx == right) &&
ly == wp->yoff - 1) {
/* Top right corner. */
new_sx = x - lc->xoff;
new_sx = x - lc->g.xoff;
if (new_sx < PANE_MINIMUM)
new_sx = PANE_MINIMUM;
new_sy = lc->sy + (ly - y);
new_sy = lc->g.sy + (ly - y);
if (new_sy < PANE_MINIMUM)
new_sy = PANE_MINIMUM;
new_yoff = y + 1;
layout_set_size(lc, new_sx, new_sy, lc->xoff, new_yoff);
layout_set_size(lc, new_sx, new_sy, lc->g.xoff, new_yoff);
resizes++;
} else if ((lx == left || lx == left + 1) &&
ly == wp->yoff + sy) {
/* Bottom left corner. */
new_sx = lc->sx + (lx - x);
new_sx = lc->g.sx + (lx - x);
if (new_sx < PANE_MINIMUM)
new_sx = PANE_MINIMUM;
new_sy = y - lc->yoff;
new_sy = y - lc->g.yoff;
if (new_sy < PANE_MINIMUM)
return;
new_xoff = x + 1;
layout_set_size(lc, new_sx, new_sy, new_xoff, lc->yoff);
layout_set_size(lc, new_sx, new_sy, new_xoff, lc->g.yoff);
resizes++;
} else if ((lx == right + 1 || lx == right) &&
ly == wp->yoff + sy) {
/* Bottom right corner. */
new_sx = x - lc->xoff;
new_sx = x - lc->g.xoff;
if (new_sx < PANE_MINIMUM)
new_sx = PANE_MINIMUM;
new_sy = y - lc->yoff;
new_sy = y - lc->g.yoff;
if (new_sy < PANE_MINIMUM)
new_sy = PANE_MINIMUM;
layout_set_size(lc, new_sx, new_sy, lc->xoff, lc->yoff);
layout_set_size(lc, new_sx, new_sy, lc->g.xoff, lc->g.yoff);
resizes++;
} else if (lx == right) {
/* Right border. */
new_sx = x - lc->xoff;
new_sx = x - lc->g.xoff;
if (new_sx < PANE_MINIMUM)
return;
layout_set_size(lc, new_sx, lc->sy, lc->xoff, lc->yoff);
layout_set_size(lc, new_sx, lc->g.sy, lc->g.xoff, lc->g.yoff);
resizes++;
} else if (lx == left) {
/* Left border. */
new_sx = lc->sx + (lx - x);
new_sx = lc->g.sx + (lx - x);
if (new_sx < PANE_MINIMUM)
return;
new_xoff = x + 1;
layout_set_size(lc, new_sx, lc->sy, new_xoff, lc->yoff);
layout_set_size(lc, new_sx, lc->g.sy, new_xoff, lc->g.yoff);
resizes++;
} else if (ly == wp->yoff + sy) {
/* Bottom border. */
new_sy = y - lc->yoff;
new_sy = y - lc->g.yoff;
if (new_sy < PANE_MINIMUM)
return;
layout_set_size(lc, lc->sx, new_sy, lc->xoff, lc->yoff);
layout_set_size(lc, lc->g.sx, new_sy, lc->g.xoff, lc->g.yoff);
resizes++;
} else if (ly == wp->yoff - 1) {
/* Top border (move instead of resize). */
new_xoff = lc->xoff + (x - lx);
new_xoff = lc->g.xoff + (x - lx);
new_yoff = y + 1;
layout_set_size(lc, lc->sx, lc->sy, new_xoff, new_yoff);
layout_set_size(lc, lc->g.sx, lc->g.sy, new_xoff, new_yoff);
resizes++;
}
if (resizes != 0) {

View File

@@ -1,4 +1,4 @@
/* $OpenBSD$ */
/* $OpenBSD: cmd-resize-window.c,v 1.10 2023/06/30 13:19:32 nicm Exp $ */
/*
* Copyright (c) 2018 Nicholas Marriott <nicholas.marriott@gmail.com>

View File

@@ -1,4 +1,4 @@
/* $OpenBSD$ */
/* $OpenBSD: cmd-respawn-pane.c,v 1.40 2026/07/03 16:09:49 nicm Exp $ */
/*
* Copyright (c) 2008 Nicholas Marriott <nicholas.marriott@gmail.com>
@@ -34,8 +34,8 @@ const struct cmd_entry cmd_respawn_pane_entry = {
.name = "respawn-pane",
.alias = "respawnp",
.args = { "c:e:kt:", 0, -1, NULL },
.usage = "[-k] [-c start-directory] [-e environment] "
.args = { "c:e:Ekt:", 0, -1, NULL },
.usage = "[-Ek] [-c start-directory] [-e environment] "
CMD_TARGET_PANE_USAGE " [shell-command [argument ...]]",
.target = { 't', CMD_FIND_PANE, 0 },
@@ -75,6 +75,8 @@ cmd_respawn_pane_exec(struct cmd *self, struct cmdq_item *item)
sc.cwd = args_get(args, 'c');
sc.flags = SPAWN_RESPAWN;
if (args_has(args, 'E'))
sc.flags |= SPAWN_EMPTY;
if (args_has(args, 'k'))
sc.flags |= SPAWN_KILL;

View File

@@ -1,4 +1,4 @@
/* $OpenBSD$ */
/* $OpenBSD: cmd-respawn-window.c,v 1.51 2026/07/03 16:09:49 nicm Exp $ */
/*
* Copyright (c) 2008 Nicholas Marriott <nicholas.marriott@gmail.com>
@@ -34,8 +34,8 @@ const struct cmd_entry cmd_respawn_window_entry = {
.name = "respawn-window",
.alias = "respawnw",
.args = { "c:e:kt:", 0, -1, NULL },
.usage = "[-k] [-c start-directory] [-e environment] "
.args = { "c:e:Ekt:", 0, -1, NULL },
.usage = "[-Ek] [-c start-directory] [-e environment] "
CMD_TARGET_WINDOW_USAGE " [shell-command [argument ...]]",
.target = { 't', CMD_FIND_WINDOW, 0 },
@@ -74,6 +74,8 @@ cmd_respawn_window_exec(struct cmd *self, struct cmdq_item *item)
sc.cwd = args_get(args, 'c');
sc.flags = SPAWN_RESPAWN;
if (args_has(args, 'E'))
sc.flags |= SPAWN_EMPTY;
if (args_has(args, 'k'))
sc.flags |= SPAWN_KILL;

View File

@@ -1,4 +1,4 @@
/* $OpenBSD$ */
/* $OpenBSD: cmd-rotate-window.c,v 1.34 2026/06/22 08:47:45 nicm Exp $ */
/*
* Copyright (c) 2009 Nicholas Marriott <nicholas.marriott@gmail.com>

View File

@@ -1,4 +1,4 @@
/* $OpenBSD$ */
/* $OpenBSD: cmd-run-shell.c,v 1.91 2026/06/01 08:27:37 nicm Exp $ */
/*
* Copyright (c) 2009 Tiago Cunha <me@tiagocunha.org>

View File

@@ -1,4 +1,4 @@
/* $OpenBSD$ */
/* $OpenBSD: cmd-save-buffer.c,v 1.57 2025/10/28 07:32:26 nicm Exp $ */
/*
* Copyright (c) 2009 Tiago Cunha <me@tiagocunha.org>

View File

@@ -1,4 +1,4 @@
/* $OpenBSD$ */
/* $OpenBSD: cmd-select-layout.c,v 1.43 2026/07/10 13:38:45 nicm Exp $ */
/*
* Copyright (c) 2009 Nicholas Marriott <nicholas.marriott@gmail.com>
@@ -139,7 +139,7 @@ changed:
free(oldlayout);
recalculate_sizes();
server_redraw_window(w);
notify_window("window-layout-changed", w);
events_fire_window("window-layout-changed", w);
return (CMD_RETURN_NORMAL);
error:

View File

@@ -1,4 +1,4 @@
/* $OpenBSD$ */
/* $OpenBSD: cmd-select-pane.c,v 1.76 2026/07/10 13:38:45 nicm Exp $ */
/*
* Copyright (c) 2009 Nicholas Marriott <nicholas.marriott@gmail.com>
@@ -80,6 +80,69 @@ cmd_select_pane_redraw(struct window *w)
}
}
static enum cmd_retval
cmd_select_pane_marked_pane(struct cmd *self, struct cmdq_item *item)
{
struct args *args = cmd_get_args(self);
struct cmd_find_state *target = cmdq_get_target(item);
struct event_payload *ep;
struct cmd_find_state fs;
struct winlink *wl = target->wl;
struct window_pane *wp = target->wp, *lwp = NULL, *mwp;
struct session *s = target->s;
if (args_has(args, 'm') && !window_pane_is_visible(wp))
return (CMD_RETURN_NORMAL);
if (server_check_marked())
lwp = marked_pane.wp;
if (args_has(args, 'M') || server_is_marked(s, wl, wp))
server_clear_marked();
else
server_set_marked(s, wl, wp);
mwp = marked_pane.wp;
ep = event_payload_create();
if (mwp != NULL)
cmd_find_from_pane(&fs, mwp, 0);
else if (lwp != NULL)
cmd_find_from_pane(&fs, lwp, 0);
else
cmd_find_from_pane(&fs, wp, 0);
event_payload_set_target(ep, &fs);
if (mwp != NULL) {
event_payload_set_pane(ep, "pane", mwp);
event_payload_set_pane(ep, "new_pane", mwp);
event_payload_set_window(ep, "window", mwp->window);
} else if (lwp != NULL) {
event_payload_set_pane(ep, "pane", lwp);
event_payload_set_window(ep, "window", lwp->window);
} else {
event_payload_set_pane(ep, "pane", wp);
event_payload_set_window(ep, "window", wp->window);
}
if (lwp != NULL)
event_payload_set_pane(ep, "old_pane", lwp);
event_payload_set_int(ep, "marked", mwp != NULL);
events_fire("marked-pane-changed", ep);
if (lwp != NULL) {
lwp->flags |= (PANE_REDRAW|PANE_STYLECHANGED|PANE_THEMECHANGED);
server_redraw_window_borders(lwp->window);
server_status_window(lwp->window);
}
if (mwp != NULL) {
mwp->flags |= (PANE_REDRAW|PANE_STYLECHANGED|PANE_THEMECHANGED);
server_redraw_window_borders(mwp->window);
server_status_window(mwp->window);
}
if (window_pane_is_floating(wp)) {
window_redraw_active_switch(wp->window, wp);
window_set_active_pane(wp->window, wp, 1);
}
return (CMD_RETURN_NORMAL);
}
static enum cmd_retval
cmd_select_pane_exec(struct cmd *self, struct cmdq_item *item)
{
@@ -88,10 +151,12 @@ cmd_select_pane_exec(struct cmd *self, struct cmdq_item *item)
struct cmd_find_state *current = cmdq_get_current(item);
struct cmd_find_state *target = cmdq_get_target(item);
struct client *c = cmdq_get_client(item);
struct event_payload *ep;
struct cmd_find_state fs;
struct winlink *wl = target->wl;
struct window *w = wl->window;
struct session *s = target->s;
struct window_pane *wp = target->wp, *activewp, *lastwp, *markedwp;
struct window_pane *wp = target->wp, *activewp, *lastwp;
struct options *oo = wp->options;
char *title;
const char *style;
@@ -134,38 +199,8 @@ cmd_select_pane_exec(struct cmd *self, struct cmdq_item *item)
return (CMD_RETURN_NORMAL);
}
if (args_has(args, 'm') || args_has(args, 'M')) {
if (args_has(args, 'm') && !window_pane_is_visible(wp))
return (CMD_RETURN_NORMAL);
if (server_check_marked())
lastwp = marked_pane.wp;
else
lastwp = NULL;
if (args_has(args, 'M') || server_is_marked(s, wl, wp))
server_clear_marked();
else
server_set_marked(s, wl, wp);
markedwp = marked_pane.wp;
if (lastwp != NULL) {
lastwp->flags |= (PANE_REDRAW|PANE_STYLECHANGED|
PANE_THEMECHANGED);
server_redraw_window_borders(lastwp->window);
server_status_window(lastwp->window);
}
if (markedwp != NULL) {
markedwp->flags |= (PANE_REDRAW|PANE_STYLECHANGED|
PANE_THEMECHANGED);
server_redraw_window_borders(markedwp->window);
server_status_window(markedwp->window);
}
if (window_pane_is_floating(wp)) {
window_redraw_active_switch(w, wp);
window_set_active_pane(w, wp, 1);
}
return (CMD_RETURN_NORMAL);
}
if (args_has(args, 'm') || args_has(args, 'M'))
return (cmd_select_pane_marked_pane(self, item));
style = args_get(args, 'P');
if (style != NULL) {
@@ -218,7 +253,13 @@ cmd_select_pane_exec(struct cmd *self, struct cmdq_item *item)
if (args_has(args, 'T')) {
title = format_single_from_target(item, args_get(args, 'T'));
if (screen_set_title(&wp->base, title, 0)) {
notify_pane("pane-title-changed", wp);
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", wp->window);
event_payload_set_string(ep, "new_title", "%s", title);
events_fire("pane-title-changed", ep);
server_redraw_window_borders(wp->window);
server_status_window(wp->window);
}

View File

@@ -1,4 +1,4 @@
/* $OpenBSD$ */
/* $OpenBSD: cmd-select-window.c,v 1.30 2021/08/21 10:22:39 nicm Exp $ */
/*
* Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>

View File

@@ -1,4 +1,4 @@
/* $OpenBSD$ */
/* $OpenBSD: cmd-send-keys.c,v 1.81 2026/06/11 19:13:34 nicm Exp $ */
/*
* Copyright (c) 2008 Nicholas Marriott <nicholas.marriott@gmail.com>

View File

@@ -1,4 +1,4 @@
/* $OpenBSD$ */
/* $OpenBSD: cmd-server-access.c,v 1.6 2026/06/09 12:58:40 nicm Exp $ */
/*
* Copyright (c) 2021 Dallas Lyons <dallasdlyons@gmail.com>

View File

@@ -1,4 +1,4 @@
/* $OpenBSD$ */
/* $OpenBSD: cmd-set-buffer.c,v 1.37 2026/02/15 17:43:26 nicm Exp $ */
/*
* Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>

View File

@@ -1,4 +1,4 @@
/* $OpenBSD$ */
/* $OpenBSD: cmd-set-environment.c,v 1.29 2025/04/09 06:27:43 nicm Exp $ */
/*
* Copyright (c) 2009 Nicholas Marriott <nicholas.marriott@gmail.com>

View File

@@ -1,4 +1,4 @@
/* $OpenBSD$ */
/* $OpenBSD: cmd-set-option.c,v 1.146 2026/07/10 15:20:06 nicm Exp $ */
/*
* Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>
@@ -31,6 +31,10 @@ static enum args_parse_type cmd_set_option_args_parse(struct args *,
u_int, char **);
static enum cmd_retval cmd_set_option_exec(struct cmd *,
struct cmdq_item *);
static enum cmd_retval cmd_set_hook_event_exec(struct cmd *,
struct cmdq_item *);
static enum cmd_retval cmd_set_hook_monitor_exec(struct cmdq_item *,
struct args *, int);
const struct cmd_entry cmd_set_option_entry = {
.name = "set-option",
@@ -62,8 +66,9 @@ const struct cmd_entry cmd_set_hook_entry = {
.name = "set-hook",
.alias = NULL,
.args = { "agpRt:uw", 1, 2, cmd_set_option_args_parse },
.usage = "[-agpRuw] " CMD_TARGET_PANE_USAGE " hook [command]",
.args = { "agpERTt:uB:w", 0, 2, cmd_set_option_args_parse },
.usage = "[-agpERTuw] [-B name:what:format] " CMD_TARGET_PANE_USAGE " "
"[hook] [command]",
.target = { 't', CMD_FIND_PANE, CMD_FIND_CANFAIL },
@@ -72,14 +77,152 @@ const struct cmd_entry cmd_set_hook_entry = {
};
static enum args_parse_type
cmd_set_option_args_parse(__unused struct args *args, u_int idx,
cmd_set_option_args_parse(struct args *args, u_int idx,
__unused char **cause)
{
if (args_has(args, 'B'))
return (ARGS_PARSE_COMMANDS_OR_STRING);
if (idx == 1)
return (ARGS_PARSE_COMMANDS_OR_STRING);
return (ARGS_PARSE_STRING);
}
static enum cmd_retval
cmd_set_hook_event_exec(struct cmd *self, struct cmdq_item *item)
{
struct args *args = cmd_get_args(self);
struct cmd_find_state *target = cmdq_get_target(item);
struct event_payload *ep;
struct client *c;
char *argument;
if (args_count(args) == 0) {
cmdq_error(item, "missing argument");
return (CMD_RETURN_ERROR);
}
if (args_count(args) != 1) {
cmdq_error(item, "too many arguments");
return (CMD_RETURN_ERROR);
}
argument = format_single_from_target(item, args_string(args, 0));
if (*argument != '@') {
cmdq_error(item, "event name must start with @");
free(argument);
return (CMD_RETURN_ERROR);
}
ep = event_payload_create();
event_payload_set_target(ep, target);
c = cmdq_get_client(item);
if (c != NULL)
event_payload_set_client(ep, "client", c);
if (target->s != NULL)
event_payload_set_session(ep, "session", target->s);
if (target->w != NULL)
event_payload_set_window(ep, "window", target->w);
if (target->wl != NULL)
event_payload_set_int(ep, "window_index", target->wl->idx);
else if (target->idx != -1)
event_payload_set_int(ep, "window_index", target->idx);
if (target->wp != NULL)
event_payload_set_pane(ep, "pane", target->wp);
events_fire(argument, ep);
free(argument);
return (CMD_RETURN_NORMAL);
}
static enum cmd_retval
cmd_set_hook_monitor_exec(struct cmdq_item *item, struct args *args, int window)
{
struct cmd_find_state *target = cmdq_get_target(item), fs;
struct options *oo;
struct options_entry *o;
struct session *s = NULL;
char *cause = NULL, *name = NULL, *format = NULL;
char *expanded = NULL, *newvalue = NULL;
const char *value, *old;
enum monitor_type type;
int id, scope, flags = 0;
if (args_count(args) > 1) {
cmdq_error(item, "too many arguments");
return (CMD_RETURN_ERROR);
}
value = args_get(args, 'B');
if (args_has(args, 'u')) {
if (monitor_parse(value, &name, &type, &id, &format) != 0)
name = xstrdup(value);
free(format);
format = NULL;
} else {
if (monitor_parse(value, &name, &type, &id, &format) != 0) {
cmdq_error(item, "invalid subscription: %s", value);
return (CMD_RETURN_ERROR);
}
}
if (*name != '@') {
cmdq_error(item, "monitor hook name must start with @");
goto fail;
}
scope = options_scope_from_name(args, window, name, target, &oo,
&cause);
if (scope == OPTIONS_TABLE_NONE) {
cmdq_error(item, "%s", cause);
free(cause);
goto fail;
}
cmd_find_copy_state(&fs, target);
if (args_has(args, 'u')) {
hooks_monitor_remove(oo, name);
goto out;
}
if (args_count(args) != 0) {
value = args_string(args, 0);
if (args_has(args, 'F')) {
expanded = format_single_from_target(item, value);
value = expanded;
}
o = options_get_only(oo, name);
if (!args_has(args, 'o') || o == NULL) {
if (args_has(args, 'a') && o != NULL) {
old = options_get_string(oo, name);
xasprintf(&newvalue, "%s%s", old, value);
value = newvalue;
}
options_set_string(oo, name, 0, "%s", value);
options_push_changes(name);
}
}
if (oo != global_options &&
oo != global_s_options &&
oo != global_w_options)
s = target->s;
if (args_has(args, 'T'))
flags |= MONITOR_NOTIFY_TRUE;
hooks_monitor_add(item, oo, name, type, id, format, flags, &fs, s);
out:
free(newvalue);
free(expanded);
free(name);
free(format);
return (CMD_RETURN_NORMAL);
fail:
free(newvalue);
free(expanded);
free(name);
free(format);
return (CMD_RETURN_ERROR);
}
static enum cmd_retval
cmd_set_option_exec(struct cmd *self, struct cmdq_item *item)
{
@@ -89,26 +232,34 @@ cmd_set_option_exec(struct cmd *self, struct cmdq_item *item)
struct window_pane *loop;
struct options *oo;
struct options_entry *parent, *o, *po;
char *name, *argument, *expanded = NULL;
char *cause;
char *name, *argument, *cause;
char *expanded = NULL, *array_key = NULL;
const char *value;
int window, idx, already, error, ambiguous;
int window, already, error, ambiguous;
int scope;
window = (cmd_get_entry(self) == &cmd_set_window_option_entry);
if (cmd_get_entry(self) == &cmd_set_hook_entry && args_has(args, 'E'))
return (cmd_set_hook_event_exec(self, item));
if (cmd_get_entry(self) == &cmd_set_hook_entry && args_has(args, 'B'))
return (cmd_set_hook_monitor_exec(item, args, window));
if (args_count(args) == 0) {
cmdq_error(item, "missing argument");
return (CMD_RETURN_ERROR);
}
/* Expand argument. */
argument = format_single_from_target(item, args_string(args, 0));
/* If set-hook -R, fire the hook straight away. */
if (cmd_get_entry(self) == &cmd_set_hook_entry && args_has(args, 'R')) {
notify_hook(item, argument);
hooks_run(item, argument);
free(argument);
return (CMD_RETURN_NORMAL);
}
/* Parse option name and index. */
name = options_match(argument, &idx, &ambiguous);
/* Parse option name and array key. */
name = options_match(argument, &array_key, &ambiguous);
if (name == NULL) {
if (args_has(args, 'q'))
goto out;
@@ -140,21 +291,23 @@ cmd_set_option_exec(struct cmd *self, struct cmdq_item *item)
o = options_get_only(oo, name);
parent = options_get(oo, name);
/* Check that array options and indexes match up. */
if (idx != -1 && (*name == '@' || !options_is_array(parent))) {
/* Check that array options and keys match up. */
if (array_key != NULL && (*name == '@' || !options_is_array(parent))) {
cmdq_error(item, "not an array: %s", argument);
goto fail;
}
/* With -o, check this option is not already set. */
if (!args_has(args, 'u') && args_has(args, 'o')) {
if (idx == -1)
if (array_key == NULL)
already = (o != NULL);
else {
if (o == NULL)
already = 0;
else if (options_array_get(o, array_key) != NULL)
already = 1;
else
already = (options_array_get(o, idx) != NULL);
already = 0;
}
if (already) {
if (args_has(args, 'q'))
@@ -170,7 +323,8 @@ cmd_set_option_exec(struct cmd *self, struct cmdq_item *item)
po = options_get_only(loop->options, name);
if (po == NULL)
continue;
if (options_remove_or_default(po, idx, &cause) != 0) {
if (options_remove_or_default(po, array_key,
&cause) != 0) {
cmdq_error(item, "%s", cause);
free(cause);
goto fail;
@@ -180,7 +334,7 @@ cmd_set_option_exec(struct cmd *self, struct cmdq_item *item)
if (args_has(args, 'u') || args_has(args, 'U')) {
if (o == NULL)
goto out;
if (options_remove_or_default(o, idx, &cause) != 0) {
if (options_remove_or_default(o, array_key, &cause) != 0) {
cmdq_error(item, "%s", cause);
free(cause);
goto fail;
@@ -191,7 +345,9 @@ cmd_set_option_exec(struct cmd *self, struct cmdq_item *item)
goto fail;
}
options_set_string(oo, name, append, "%s", value);
} else if (idx == -1 && !options_is_array(parent)) {
if (cmd_get_entry(self) == &cmd_set_hook_entry)
hooks_add_event(name);
} else if (array_key == NULL && !options_is_array(parent)) {
error = options_from_string(oo, options_table_entry(parent),
options_table_entry(parent)->name, value,
args_has(args, 'a'), &cause);
@@ -207,7 +363,7 @@ cmd_set_option_exec(struct cmd *self, struct cmdq_item *item)
}
if (o == NULL)
o = options_empty(oo, options_table_entry(parent));
if (idx == -1) {
if (array_key == NULL) {
if (!append)
options_array_clear(o);
if (options_array_assign(o, value, &cause) != 0) {
@@ -215,7 +371,7 @@ cmd_set_option_exec(struct cmd *self, struct cmdq_item *item)
free(cause);
goto fail;
}
} else if (options_array_set(o, idx, value, append,
} else if (options_array_set(o, array_key, value, append,
&cause) != 0) {
cmdq_error(item, "%s", cause);
free(cause);
@@ -229,11 +385,13 @@ out:
free(argument);
free(expanded);
free(name);
free(array_key);
return (CMD_RETURN_NORMAL);
fail:
free(argument);
free(expanded);
free(name);
free(array_key);
return (CMD_RETURN_ERROR);
}

View File

@@ -1,4 +1,4 @@
/* $OpenBSD$ */
/* $OpenBSD: cmd-show-environment.c,v 1.29 2025/04/09 06:27:43 nicm Exp $ */
/*
* Copyright (c) 2009 Nicholas Marriott <nicholas.marriott@gmail.com>

View File

@@ -1,4 +1,4 @@
/* $OpenBSD$ */
/* $OpenBSD: cmd-show-messages.c,v 1.37 2025/11/18 08:37:54 nicm Exp $ */
/*
* Copyright (c) 2009 Nicholas Marriott <nicholas.marriott@gmail.com>

View File

@@ -1,4 +1,4 @@
/* $OpenBSD$ */
/* $OpenBSD: cmd-show-options.c,v 1.73 2026/07/10 13:38:45 nicm Exp $ */
/*
* Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>
@@ -30,7 +30,11 @@
static enum cmd_retval cmd_show_options_exec(struct cmd *, struct cmdq_item *);
static void cmd_show_options_print(struct cmd *, struct cmdq_item *,
struct options_entry *, int, int);
struct options_entry *, const char *, int);
static void cmd_show_hooks_print_monitor(struct cmdq_item *,
struct options_entry *);
static enum cmd_retval cmd_show_hooks_monitor(struct cmd *, struct cmdq_item *,
int, struct options *);
static enum cmd_retval cmd_show_options_all(struct cmd *, struct cmdq_item *,
int, struct options *);
@@ -64,8 +68,8 @@ const struct cmd_entry cmd_show_hooks_entry = {
.name = "show-hooks",
.alias = NULL,
.args = { "gpt:w", 0, 1, NULL },
.usage = "[-gpw] " CMD_TARGET_PANE_USAGE " [hook]",
.args = { "Bgpt:w", 0, 1, NULL },
.usage = "[-Bgpw] " CMD_TARGET_PANE_USAGE " [hook]",
.target = { 't', CMD_FIND_PANE, CMD_FIND_CANFAIL },
@@ -80,7 +84,8 @@ cmd_show_options_exec(struct cmd *self, struct cmdq_item *item)
struct cmd_find_state *target = cmdq_get_target(item);
struct options *oo;
char *argument, *name = NULL, *cause;
int window, idx, ambiguous, parent, scope;
char *array_key = NULL;
int window, ambiguous, parent, scope;
struct options_entry *o;
window = (cmd_get_entry(self) == &cmd_show_window_options_entry);
@@ -95,11 +100,14 @@ cmd_show_options_exec(struct cmd *self, struct cmdq_item *item)
free(cause);
return (CMD_RETURN_ERROR);
}
if (cmd_get_entry(self) == &cmd_show_hooks_entry &&
args_has(args, 'B'))
return (cmd_show_hooks_monitor(self, item, scope, oo));
return (cmd_show_options_all(self, item, scope, oo));
}
argument = format_single_from_target(item, args_string(args, 0));
name = options_match(argument, &idx, &ambiguous);
name = options_match(argument, &array_key, &ambiguous);
if (name == NULL) {
if (args_has(args, 'q'))
goto out;
@@ -124,8 +132,13 @@ cmd_show_options_exec(struct cmd *self, struct cmdq_item *item)
parent = 1;
} else
parent = 0;
if (o != NULL)
cmd_show_options_print(self, item, o, idx, parent);
if (o != NULL) {
if (cmd_get_entry(self) == &cmd_show_hooks_entry &&
args_has(args, 'B'))
cmd_show_hooks_print_monitor(item, o);
else
cmd_show_options_print(self, item, o, array_key, parent);
}
else if (*name == '@') {
if (args_has(args, 'q'))
goto out;
@@ -135,26 +148,28 @@ cmd_show_options_exec(struct cmd *self, struct cmdq_item *item)
out:
free(name);
free(array_key);
free(argument);
return (CMD_RETURN_NORMAL);
fail:
free(name);
free(array_key);
free(argument);
return (CMD_RETURN_ERROR);
}
static void
cmd_show_options_print(struct cmd *self, struct cmdq_item *item,
struct options_entry *o, int idx, int parent)
struct options_entry *o, const char *array_key, int parent)
{
struct args *args = cmd_get_args(self);
struct options_array_item *a;
const char *name = options_name(o);
char *value, *tmp = NULL, *escaped;
if (idx != -1) {
xasprintf(&tmp, "%s[%d]", name, idx);
if (array_key != NULL) {
xasprintf(&tmp, "%s[%s]", name, array_key);
name = tmp;
} else {
if (options_is_array(o)) {
@@ -165,8 +180,8 @@ cmd_show_options_print(struct cmd *self, struct cmdq_item *item,
return;
}
while (a != NULL) {
idx = options_array_item_index(a);
cmd_show_options_print(self, item, o, idx,
array_key = options_array_item_key(a);
cmd_show_options_print(self, item, o, array_key,
parent);
a = options_array_next(a);
}
@@ -174,7 +189,7 @@ cmd_show_options_print(struct cmd *self, struct cmdq_item *item,
}
}
value = options_to_string(o, idx, 0);
value = options_to_string(o, array_key, 0);
if (args_has(args, 'v'))
cmdq_print(item, "%s", value);
else if (options_is_string(o)) {
@@ -195,6 +210,33 @@ cmd_show_options_print(struct cmd *self, struct cmdq_item *item,
free(tmp);
}
static void
cmd_show_hooks_print_monitor(struct cmdq_item *item, struct options_entry *o)
{
char *value;
value = hooks_monitor_to_string(o);
if (value == NULL)
return;
cmdq_print(item, "%s", value);
free(value);
}
/* Show all hook monitors. */
static enum cmd_retval
cmd_show_hooks_monitor(__unused struct cmd *self, struct cmdq_item *item,
__unused int scope, struct options *oo)
{
struct options_entry *o;
o = options_first(oo);
while (o != NULL) {
cmd_show_hooks_print_monitor(item, o);
o = options_next(o);
}
return (CMD_RETURN_NORMAL);
}
static enum cmd_retval
cmd_show_options_all(struct cmd *self, struct cmdq_item *item, int scope,
struct options *oo)
@@ -203,15 +245,14 @@ cmd_show_options_all(struct cmd *self, struct cmdq_item *item, int scope,
const struct options_table_entry *oe;
struct options_entry *o;
struct options_array_item *a;
const char *name;
u_int idx;
const char *name, *array_key;
int parent;
if (cmd_get_entry(self) != &cmd_show_hooks_entry) {
o = options_first(oo);
while (o != NULL) {
if (options_table_entry(o) == NULL)
cmd_show_options_print(self, item, o, -1, 0);
cmd_show_options_print(self, item, o, NULL, 0);
o = options_next(o);
}
}
@@ -238,7 +279,7 @@ cmd_show_options_all(struct cmd *self, struct cmdq_item *item, int scope,
parent = 0;
if (!options_is_array(o))
cmd_show_options_print(self, item, o, -1, parent);
cmd_show_options_print(self, item, o, NULL, parent);
else if ((a = options_array_first(o)) == NULL) {
if (!args_has(args, 'v')) {
name = options_name(o);
@@ -249,8 +290,8 @@ cmd_show_options_all(struct cmd *self, struct cmdq_item *item, int scope,
}
} else {
while (a != NULL) {
idx = options_array_item_index(a);
cmd_show_options_print(self, item, o, idx,
array_key = options_array_item_key(a);
cmd_show_options_print(self, item, o, array_key,
parent);
a = options_array_next(a);
}

View File

@@ -1,4 +1,4 @@
/* $OpenBSD$ */
/* $OpenBSD: cmd-show-prompt-history.c,v 1.5 2026/06/25 11:39:11 nicm Exp $ */
/*
* Copyright (c) 2021 Anindya Mukherjee <anindya49@hotmail.com>

View File

@@ -1,4 +1,4 @@
/* $OpenBSD$ */
/* $OpenBSD: cmd-source-file.c,v 1.62 2025/11/18 08:42:09 nicm Exp $ */
/*
* Copyright (c) 2008 Tiago Cunha <me@tiagocunha.org>

View File

@@ -1,4 +1,4 @@
/* $OpenBSD$ */
/* $OpenBSD: cmd-split-window.c,v 1.141 2026/07/10 13:38:45 nicm Exp $ */
/*
* Copyright (c) 2009 Nicholas Marriott <nicholas.marriott@gmail.com>
@@ -81,8 +81,9 @@ cmd_split_window_exec(struct cmd *self, struct cmdq_item *item)
struct session *s = target->s;
struct winlink *wl = target->wl;
struct window *w = wl->window;
struct window_pane *wp = target->wp, *new_wp;
struct window_pane *wp = target->wp, *new_wp = NULL;
struct layout_cell *lc = NULL;
struct event_payload *ep;
struct cmd_find_state fs;
int input, empty, is_floating, flags = 0;
const char *template, *style, *value;
@@ -104,7 +105,7 @@ cmd_split_window_exec(struct cmd *self, struct cmdq_item *item)
flags |= SPAWN_FULLSIZE;
input = args_has(args, 'I');
if (input)
if (input || (count == 1 && *args_string(args, 0) == '\0'))
empty = 1;
else
empty = args_has(args, 'E');
@@ -167,6 +168,11 @@ cmd_split_window_exec(struct cmd *self, struct cmdq_item *item)
if ((new_wp = spawn_pane(&sc, &cause)) == NULL) {
cmdq_error(item, "create pane failed: %s", cause);
free(cause);
/*
* spawn_pane has already torn the half-built pane down (its
* fork-failure path removes the pane and destroys the layout
* cell), so new_wp is NULL and there is nothing for fail to do.
*/
goto fail;
}
@@ -212,17 +218,19 @@ cmd_split_window_exec(struct cmd *self, struct cmdq_item *item)
if (args_has(args, 'T')) {
title = format_single_from_target(item, args_get(args, 'T'));
screen_set_title(&new_wp->base, title, 0);
notify_pane("pane-title-changed", new_wp);
ep = event_payload_create();
cmd_find_from_pane(&fs, new_wp, 0);
event_payload_set_target(ep, &fs);
event_payload_set_pane(ep, "pane", new_wp);
event_payload_set_window(ep, "window", new_wp->window);
event_payload_set_string(ep, "new_title", "%s", title);
events_fire("pane-title-changed", ep);
free(title);
}
if (input) {
switch (window_pane_start_input(new_wp, item, &cause)) {
case -1:
server_client_remove_pane(new_wp);
if (!is_floating)
layout_close_pane(new_wp);
window_remove_pane(wp->window, new_wp);
cmdq_error(item, "%s", cause);
free(cause);
goto fail;
@@ -269,10 +277,20 @@ cmd_split_window_exec(struct cmd *self, struct cmdq_item *item)
return (CMD_RETURN_NORMAL);
fail:
/*
* If the pane was spawned before we failed, tear it down here; this
* also destroys its layout cell. spawn_pane's own failure path has
* already done this, so new_wp is NULL in that case.
*/
if (new_wp != NULL) {
server_client_remove_pane(new_wp);
if (!is_floating)
layout_close_pane(new_wp);
window_remove_pane(wp->window, new_wp);
}
if (sc.argv != NULL)
cmd_free_argv(sc.argc, sc.argv);
environ_free(sc.environ);
layout_destroy_cell(w, lc, &w->layout_root);
return (CMD_RETURN_ERROR);

View File

@@ -1,4 +1,4 @@
/* $OpenBSD$ */
/* $OpenBSD: cmd-swap-pane.c,v 1.53 2026/07/10 13:38:45 nicm Exp $ */
/*
* Copyright (c) 2009 Nicholas Marriott <nicholas.marriott@gmail.com>
@@ -42,6 +42,22 @@ const struct cmd_entry cmd_swap_pane_entry = {
.exec = cmd_swap_pane_exec
};
static struct window_pane *
cmd_swap_pane_next_tiled_pane(struct window_pane *wp)
{
while (wp != NULL && !layout_cell_is_tiled(wp->layout_cell))
wp = TAILQ_NEXT(wp, entry);
return (wp);
}
static struct window_pane *
cmd_swap_pane_prev_tiled_pane(struct window_pane *wp)
{
while (wp != NULL && !layout_cell_is_tiled(wp->layout_cell))
wp = TAILQ_PREV(wp, window_panes, entry);
return (wp);
}
static enum cmd_retval
cmd_swap_pane_exec(struct cmd *self, struct cmdq_item *item)
{
@@ -62,15 +78,29 @@ cmd_swap_pane_exec(struct cmd *self, struct cmdq_item *item)
server_redraw_window(dst_w);
if (args_has(args, 'D')) {
if (window_pane_is_floating(dst_wp)) {
cmdq_error(item, "cannot swap down on floating pane");
return (CMD_RETURN_ERROR);
}
src_w = dst_w;
src_wp = TAILQ_NEXT(dst_wp, entry);
if (src_wp == NULL)
src_wp = cmd_swap_pane_next_tiled_pane(src_wp);
if (src_wp == NULL) {
src_wp = TAILQ_FIRST(&dst_w->panes);
src_wp = cmd_swap_pane_next_tiled_pane(src_wp);
}
} else if (args_has(args, 'U')) {
if (window_pane_is_floating(dst_wp)) {
cmdq_error(item, "cannot swap up on floating pane");
return (CMD_RETURN_ERROR);
}
src_w = dst_w;
src_wp = TAILQ_PREV(dst_wp, window_panes, entry);
if (src_wp == NULL)
src_wp = cmd_swap_pane_prev_tiled_pane(src_wp);
if (src_wp == NULL) {
src_wp = TAILQ_LAST(&dst_w->panes, window_panes);
src_wp = cmd_swap_pane_prev_tiled_pane(src_wp);
}
}
if (src_w != dst_w && window_push_zoom(src_w, 0, args_has(args, 'Z')))
@@ -79,12 +109,6 @@ cmd_swap_pane_exec(struct cmd *self, struct cmdq_item *item)
if (src_wp == dst_wp)
goto out;
if (window_pane_is_floating(src_wp) ||
window_pane_is_floating(dst_wp)) {
cmdq_error(item, "cannot swap floating panes");
return (CMD_RETURN_ERROR);
}
server_client_remove_pane(src_wp);
server_client_remove_pane(dst_wp);
@@ -156,9 +180,9 @@ cmd_swap_pane_exec(struct cmd *self, struct cmdq_item *item)
redraw_invalidate_scene(dst_w);
server_redraw_window(dst_w);
notify_window("window-layout-changed", src_w);
events_fire_window("window-layout-changed", src_w);
if (src_w != dst_w)
notify_window("window-layout-changed", dst_w);
events_fire_window("window-layout-changed", dst_w);
out:
if (window_pop_zoom(src_w))

View File

@@ -1,4 +1,4 @@
/* $OpenBSD$ */
/* $OpenBSD: cmd-swap-window.c,v 1.29 2025/10/30 13:52:08 nicm Exp $ */
/*
* Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>

View File

@@ -1,4 +1,4 @@
/* $OpenBSD$ */
/* $OpenBSD: cmd-switch-client.c,v 1.74 2026/05/22 15:22:43 nicm Exp $ */
/*
* Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>

View File

@@ -1,4 +1,4 @@
/* $OpenBSD$ */
/* $OpenBSD: cmd-unbind-key.c,v 1.34 2021/08/21 10:22:39 nicm Exp $ */
/*
* Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>

View File

@@ -1,4 +1,4 @@
/* $OpenBSD$ */
/* $OpenBSD: cmd-wait-for.c,v 1.23 2026/07/10 13:38:45 nicm Exp $ */
/*
* Copyright (c) 2013 Nicholas Marriott <nicholas.marriott@gmail.com>
@@ -34,8 +34,8 @@ const struct cmd_entry cmd_wait_for_entry = {
.name = "wait-for",
.alias = "wait",
.args = { "LSU", 1, 1, NULL },
.usage = "[-L|-S|-U] channel",
.args = { "EF:LSUlvw:", 1, 1, NULL },
.usage = "[-ELSUlv] [-F format] [-w waiter] name",
.flags = 0,
.exec = cmd_wait_for_exec
@@ -46,6 +46,17 @@ struct wait_item {
TAILQ_ENTRY(wait_item) entry;
};
struct wait_event_item {
struct cmdq_item *item;
struct events_sink *sink;
char *name;
char *filter;
int verbose;
TAILQ_ENTRY(wait_event_item) entry;
};
static TAILQ_HEAD(, wait_event_item) wait_event_items =
TAILQ_HEAD_INITIALIZER(wait_event_items);
struct wait_channel {
const char *name;
int locked;
@@ -76,9 +87,28 @@ static enum cmd_retval cmd_wait_for_lock(struct cmdq_item *, const char *,
struct wait_channel *);
static enum cmd_retval cmd_wait_for_unlock(struct cmdq_item *, const char *,
struct wait_channel *);
static enum cmd_retval cmd_wait_for_event(struct cmdq_item *, const char *,
struct args *);
static void cmd_wait_for_event_cb(const char *,
struct event_payload *, void *);
static void cmd_wait_for_event_free(struct wait_event_item *);
static enum cmd_retval cmd_wait_for_event_list(struct cmdq_item *,
const char *);
static enum cmd_retval cmd_wait_for_event_wake(struct cmdq_item *,
const char *, struct args *);
static enum cmd_retval cmd_wait_for_list(struct cmdq_item *,
struct wait_channel *);
static enum cmd_retval cmd_wait_for_wake(struct cmdq_item *, const char *,
struct args *, struct wait_channel *);
static struct wait_channel *cmd_wait_for_add(const char *);
static void cmd_wait_for_remove(struct wait_channel *);
static void cmd_wait_for_remove_empty(
struct wait_channel *);
static const char *cmd_wait_for_item_client_name(
struct cmdq_item *);
static const char *cmd_wait_for_client_name(
struct wait_event_item *);
static struct wait_channel *
cmd_wait_for_add(const char *name)
@@ -117,16 +147,53 @@ cmd_wait_for_remove(struct wait_channel *wc)
free(wc);
}
static void
cmd_wait_for_remove_empty(struct wait_channel *wc)
{
if (wc->locked || wc->woken)
return;
if (!TAILQ_EMPTY(&wc->waiters) || !TAILQ_EMPTY(&wc->lockers))
return;
log_debug("remove empty wait channel %s", wc->name);
RB_REMOVE(wait_channels, &wait_channels, wc);
free((void *)wc->name);
free(wc);
}
static const char *
cmd_wait_for_item_client_name(struct cmdq_item *item)
{
struct client *c = cmdq_get_client(item);
if (c == NULL || c->name == NULL)
return ("");
return (c->name);
}
static const char *
cmd_wait_for_client_name(struct wait_event_item *wei)
{
return (cmd_wait_for_item_client_name(wei->item));
}
static enum cmd_retval
cmd_wait_for_exec(struct cmd *self, struct cmdq_item *item)
{
struct args *args = cmd_get_args(self);
struct args *args = cmd_get_args(self);
const char *name = args_string(args, 0);
struct wait_channel *wc, find;
struct wait_channel *wc, find = { .name = name };
if (args_has(args, 'E'))
return (cmd_wait_for_event(item, name, args));
find.name = name;
wc = RB_FIND(wait_channels, &wait_channels, &find);
if (args_has(args, 'l'))
return (cmd_wait_for_list(item, wc));
if (args_has(args, 'w'))
return (cmd_wait_for_wake(item, name, args, wc));
if (args_has(args, 'S'))
return (cmd_wait_for_signal(item, name, wc));
if (args_has(args, 'L'))
@@ -136,6 +203,179 @@ cmd_wait_for_exec(struct cmd *self, struct cmdq_item *item)
return (cmd_wait_for_wait(item, name, wc));
}
static void
cmd_wait_for_event_print(struct wait_event_item *wei, struct event_payload *ep)
{
struct event_payload_item *epi;
const char *key;
char *value;
epi = event_payload_first(ep);
while (epi != NULL) {
key = event_payload_item_name(epi);
if (*key != '_') {
value = event_payload_item_print(epi);
cmdq_print(wei->item, "%s=%s", key, value);
free(value);
}
epi = event_payload_next(epi);
}
}
static void
cmd_wait_for_event_cb(__unused const char *name, struct event_payload *ep,
void *item_data)
{
struct wait_event_item *wei = item_data;
struct format_tree *ft;
char *expanded;
int flag;
if (wei->verbose)
cmd_wait_for_event_print(wei, ep);
if (wei->filter != NULL) {
ft = format_create(cmdq_get_client(wei->item), wei->item,
FORMAT_NONE, FORMAT_NOJOBS);
event_payload_add_formats(ep, ft, NULL);
expanded = format_expand(ft, wei->filter);
flag = format_true(expanded);
free(expanded);
format_free(ft);
if (!flag)
return;
}
TAILQ_REMOVE(&wait_event_items, wei, entry);
cmdq_continue(wei->item);
cmd_wait_for_event_free(wei);
}
static void
cmd_wait_for_event_free(struct wait_event_item *wei)
{
events_remove_sink(wei->sink);
free(wei->name);
free(wei->filter);
free(wei);
}
static enum cmd_retval
cmd_wait_for_event(struct cmdq_item *item, const char *name, struct args *args)
{
struct wait_event_item *wei;
const char *filter = args_get(args, 'F');
if (!hooks_valid_event_name(name)) {
cmdq_error(item, "invalid event: %s", name);
return (CMD_RETURN_ERROR);
}
if (args_has(args, 'l'))
return (cmd_wait_for_event_list(item, name));
if (args_has(args, 'w'))
return (cmd_wait_for_event_wake(item, name, args));
if (cmdq_get_client(item) == NULL) {
cmdq_error(item, "not able to wait");
return (CMD_RETURN_ERROR);
}
wei = xcalloc(1, sizeof *wei);
wei->item = item;
wei->name = xstrdup(name);
wei->filter = (filter != NULL ? xstrdup(filter) : NULL);
wei->verbose = args_has(args, 'v');
wei->sink = events_add_sink(name, cmd_wait_for_event_cb, wei);
TAILQ_INSERT_TAIL(&wait_event_items, wei, entry);
return (CMD_RETURN_WAIT);
}
static enum cmd_retval
cmd_wait_for_event_list(struct cmdq_item *item, const char *name)
{
struct wait_event_item *wei;
TAILQ_FOREACH(wei, &wait_event_items, entry) {
if (strcmp(wei->name, name) == 0)
cmdq_print(item, "%s", cmd_wait_for_client_name(wei));
}
return (CMD_RETURN_NORMAL);
}
static enum cmd_retval
cmd_wait_for_event_wake(struct cmdq_item *item, const char *name,
struct args *args)
{
struct wait_event_item *wei, *wei1;
const char *client_name = args_get(args, 'w');
TAILQ_FOREACH_SAFE(wei, &wait_event_items, entry, wei1) {
if (strcmp(wei->name, name) != 0)
continue;
if (strcmp(cmd_wait_for_client_name(wei), client_name) != 0)
continue;
TAILQ_REMOVE(&wait_event_items, wei, entry);
cmdq_continue(wei->item);
cmd_wait_for_event_free(wei);
return (CMD_RETURN_NORMAL);
}
cmdq_error(item, "waiter %s not found", client_name);
return (CMD_RETURN_ERROR);
}
static enum cmd_retval
cmd_wait_for_list(struct cmdq_item *item, struct wait_channel *wc)
{
struct wait_item *wi;
if (wc == NULL)
return (CMD_RETURN_NORMAL);
TAILQ_FOREACH(wi, &wc->waiters, entry)
cmdq_print(item, "%s", cmd_wait_for_item_client_name(wi->item));
TAILQ_FOREACH(wi, &wc->lockers, entry)
cmdq_print(item, "%s", cmd_wait_for_item_client_name(wi->item));
return (CMD_RETURN_NORMAL);
}
static enum cmd_retval
cmd_wait_for_wake(__unused struct cmdq_item *item, const char *name,
struct args *args, struct wait_channel *wc)
{
struct wait_item *wi, *wi1;
const char *client_name = args_get(args, 'w');
if (wc != NULL) {
TAILQ_FOREACH_SAFE(wi, &wc->waiters, entry, wi1) {
name = cmd_wait_for_item_client_name(wi->item);
if (strcmp(name, client_name) != 0)
continue;
cmdq_continue(wi->item);
TAILQ_REMOVE(&wc->waiters, wi, entry);
free(wi);
cmd_wait_for_remove_empty(wc);
return (CMD_RETURN_NORMAL);
}
TAILQ_FOREACH_SAFE(wi, &wc->lockers, entry, wi1) {
name = cmd_wait_for_item_client_name(wi->item);
if (strcmp(name, client_name) != 0)
continue;
cmdq_continue(wi->item);
TAILQ_REMOVE(&wc->lockers, wi, entry);
free(wi);
cmd_wait_for_remove_empty(wc);
return (CMD_RETURN_NORMAL);
}
}
return (CMD_RETURN_NORMAL);
}
static enum cmd_retval
cmd_wait_for_signal(__unused struct cmdq_item *item, const char *name,
struct wait_channel *wc)
@@ -245,6 +485,13 @@ cmd_wait_for_flush(void)
{
struct wait_channel *wc, *wc1;
struct wait_item *wi, *wi1;
struct wait_event_item *wei, *wei1;
TAILQ_FOREACH_SAFE(wei, &wait_event_items, entry, wei1) {
TAILQ_REMOVE(&wait_event_items, wei, entry);
cmdq_continue(wei->item);
cmd_wait_for_event_free(wei);
}
RB_FOREACH_SAFE(wc, wait_channels, &wait_channels, wc1) {
TAILQ_FOREACH_SAFE(wi, &wc->waiters, entry, wi1) {

11
cmd.c
View File

@@ -1,4 +1,4 @@
/* $OpenBSD$ */
/* $OpenBSD: cmd.c,v 1.186 2026/07/12 20:35:52 nicm Exp $ */
/*
* Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>
@@ -646,7 +646,7 @@ cmd_template_replace(const char *template, const char *s, int idx)
char ch, *buf;
const char *ptr, *cp, quote[] = "\"\\$;~";
int replaced, quoted;
size_t len;
size_t len, slen;
if (strchr(template, '%') == NULL)
return (xstrdup(template));
@@ -671,7 +671,10 @@ cmd_template_replace(const char *template, const char *s, int idx)
if (quoted)
ptr++;
buf = xrealloc(buf, len + (strlen(s) * 3) + 1);
slen = strlen(s);
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++) {
if (quoted && strchr(quote, *cp) != NULL)
buf[len++] = '\\';
@@ -680,6 +683,8 @@ cmd_template_replace(const char *template, const char *s, int idx)
buf[len] = '\0';
continue;
}
if (len > SIZE_MAX - 2)
fatalx("argument too long");
buf = xrealloc(buf, len + 2);
buf[len++] = ch;
buf[len] = '\0';

View File

@@ -1,4 +1,4 @@
/* $OpenBSD$ */
/* $OpenBSD: colour.c,v 1.35 2026/07/06 14:29:10 nicm Exp $ */
/*
* Copyright (c) 2008 Nicholas Marriott <nicholas.marriott@gmail.com>
@@ -1294,7 +1294,8 @@ colour_palette_from_option(struct colour_palette *p, struct options *oo)
{
struct options_entry *o;
struct options_array_item *a;
u_int i, n;
union options_value *ov;
u_int i;
int c;
if (p == NULL)
@@ -1312,12 +1313,11 @@ colour_palette_from_option(struct colour_palette *p, struct options *oo)
p->default_palette = xcalloc(256, sizeof *p->default_palette);
for (i = 0; i < 256; i++)
p->default_palette[i] = -1;
while (a != NULL) {
n = options_array_item_index(a);
if (n < 256) {
c = options_array_item_value(a)->number;
p->default_palette[n] = c;
for (i = 0; i < 256; i++) {
ov = options_array_getv(o, "%u", i);
if (ov != NULL) {
c = ov->number;
p->default_palette[i] = c;
}
a = options_array_next(a);
}
}

View File

@@ -74,8 +74,14 @@ getdelim(char **buf, size_t *bufsiz, int delimiter, FILE *fp)
}
if (ptr + 2 >= eptr) {
char *nbuf;
size_t nbufsiz = *bufsiz * 2;
size_t nbufsiz;
ssize_t d = ptr - *buf;
if (*bufsiz > SIZE_MAX / 2) {
errno = EOVERFLOW;
return -1;
}
nbufsiz = *bufsiz * 2;
if ((nbuf = realloc(*buf, nbufsiz)) == NULL)
return -1;
*buf = nbuf;

View File

@@ -1,6 +1,6 @@
# configure.ac
AC_INIT([tmux], next-3.7)
AC_INIT([tmux], next-3.8)
AC_PREREQ([2.60])
AC_CONFIG_AUX_DIR(etc)
@@ -78,7 +78,7 @@ AC_ARG_ENABLE(
asan,
AS_HELP_STRING(--enable-asan, enable ASAN build flags),
,
enable_asan=no
[case "x$host_os" in *darwin*) enable_asan=yes;; esac]
)
AM_CONDITIONAL(IS_ASAN, test "x$enable_asan" = xyes)

View File

@@ -1,4 +1,4 @@
/* $OpenBSD$ */
/* $OpenBSD: control-notify.c,v 1.37 2026/07/10 13:38:45 nicm Exp $ */
/*
* Copyright (c) 2012 Nicholas Marriott <nicholas.marriott@gmail.com>
@@ -23,31 +23,56 @@
#include "tmux.h"
/* Should this client be sent events? */
#define CONTROL_SHOULD_NOTIFY_CLIENT(c) \
((c) != NULL && ((c)->flags & CLIENT_CONTROL) && \
((c) != NULL && \
((c)->flags & CLIENT_CONTROL) && \
(~(c)->flags & CLIENT_EXIT) && \
(c)->control_state != NULL)
void
control_notify_pane_mode_changed(int pane)
/* Notify control clients that pane mode changed. */
static void
control_pane_mode_changed_cb(__unused const char *name,
struct event_payload *ep, __unused void *sink_data)
{
struct client *c;
struct window_pane *wp;
struct client *c;
char *value;
TAILQ_FOREACH(c, &clients, entry) {
if (!CONTROL_SHOULD_NOTIFY_CLIENT(c))
continue;
control_write(c, "%%pane-mode-changed %%%u", pane);
wp = event_payload_get_pane(ep, "pane");
if (wp != NULL) {
TAILQ_FOREACH(c, &clients, entry) {
if (!CONTROL_SHOULD_NOTIFY_CLIENT(c))
continue;
control_write(c, "%%pane-mode-changed %%%u", wp->id);
}
return;
}
value = event_payload_print(ep, "pane");
if (value == NULL)
return;
TAILQ_FOREACH(c, &clients, entry) {
if (CONTROL_SHOULD_NOTIFY_CLIENT(c))
control_write(c, "%%pane-mode-changed %s", value);
}
free(value);
}
void
control_notify_window_layout_changed(struct window *w)
/* Notify control clients that window layout changed. */
static void
control_window_layout_changed_cb(__unused const char *name,
struct event_payload *ep, __unused void *sink_data)
{
struct client *c;
struct session *s;
struct winlink *wl;
const char *template;
char *cp;
struct client *c;
struct session *s;
struct winlink *wl;
struct window *w = event_payload_get_window(ep, "window");
const char *template;
char *cp;
if (w == NULL)
return;
template = "%layout-change #{window_id} #{window_layout} "
"#{window_visible_layout} #{window_raw_flags}";
@@ -72,12 +97,15 @@ control_notify_window_layout_changed(struct window *w)
free(cp);
}
void
control_notify_window_pane_changed(struct window *w)
/* Notify control clients that window pane changed. */
static void
control_window_pane_changed_cb(__unused const char *name,
struct event_payload *ep, __unused void *sink_data)
{
struct client *c;
struct window *w = event_payload_get_window(ep, "window");
if (w->active == NULL)
if (w == NULL || w->active == NULL)
return;
TAILQ_FOREACH(c, &clients, entry) {
if (!CONTROL_SHOULD_NOTIFY_CLIENT(c))
@@ -88,12 +116,17 @@ control_notify_window_pane_changed(struct window *w)
}
}
void
control_notify_window_unlinked(__unused struct session *s, struct window *w)
/* Notify control clients that a window was unlinked. */
static void
control_window_unlinked_cb(__unused const char *name, struct event_payload *ep,
__unused void *sink_data)
{
struct client *c;
struct session *cs;
struct client *c;
struct session *cs;
struct window *w = event_payload_get_window(ep, "window");
if (w == NULL)
return;
TAILQ_FOREACH(c, &clients, entry) {
if (!CONTROL_SHOULD_NOTIFY_CLIENT(c) || c->session == NULL)
continue;
@@ -106,12 +139,17 @@ control_notify_window_unlinked(__unused struct session *s, struct window *w)
}
}
void
control_notify_window_linked(__unused struct session *s, struct window *w)
/* Notify control clients that a window was linked. */
static void
control_window_linked_cb(__unused const char *name, struct event_payload *ep,
__unused void *sink_data)
{
struct client *c;
struct session *cs;
struct client *c;
struct session *cs;
struct window *w = event_payload_get_window(ep, "window");
if (w == NULL)
return;
TAILQ_FOREACH(c, &clients, entry) {
if (!CONTROL_SHOULD_NOTIFY_CLIENT(c) || c->session == NULL)
continue;
@@ -124,12 +162,17 @@ control_notify_window_linked(__unused struct session *s, struct window *w)
}
}
void
control_notify_window_renamed(struct window *w)
/* Notify control clients that a window was renamed. */
static void
control_window_renamed_cb(__unused const char *name, struct event_payload *ep,
__unused void *sink_data)
{
struct client *c;
struct session *cs;
struct client *c;
struct session *cs;
struct window *w = event_payload_get_window(ep, "window");
if (w == NULL)
return;
TAILQ_FOREACH(c, &clients, entry) {
if (!CONTROL_SHOULD_NOTIFY_CLIENT(c) || c->session == NULL)
continue;
@@ -145,13 +188,16 @@ control_notify_window_renamed(struct window *w)
}
}
void
control_notify_client_session_changed(struct client *cc)
/* Notify control clients that a client changed session. */
static void
control_client_session_changed_cb(__unused const char *name,
struct event_payload *ep, __unused void *sink_data)
{
struct client *c;
struct session *s;
struct client *cc = event_payload_get_client(ep, "client");
struct client *c;
struct session *s;
if (cc->session == NULL)
if (cc == NULL || cc->session == NULL)
return;
s = cc->session;
@@ -169,22 +215,32 @@ control_notify_client_session_changed(struct client *cc)
}
}
void
control_notify_client_detached(struct client *cc)
/* Notify control clients that a client detached. */
static void
control_client_detached_cb(__unused const char *name, struct event_payload *ep,
__unused void *sink_data)
{
struct client *c;
struct client *cc = event_payload_get_client(ep, "client");
struct client *c;
if (cc == NULL)
return;
TAILQ_FOREACH(c, &clients, entry) {
if (CONTROL_SHOULD_NOTIFY_CLIENT(c))
control_write(c, "%%client-detached %s", cc->name);
}
}
void
control_notify_session_renamed(struct session *s)
/* Notify control clients that a session was renamed. */
static void
control_session_renamed_cb(__unused const char *name, struct event_payload *ep,
__unused void *sink_data)
{
struct client *c;
struct session *s = event_payload_get_session(ep, "session");
struct client *c;
if (s == NULL)
return;
TAILQ_FOREACH(c, &clients, entry) {
if (!CONTROL_SHOULD_NOTIFY_CLIENT(c))
continue;
@@ -193,8 +249,10 @@ control_notify_session_renamed(struct session *s)
}
}
void
control_notify_session_created(__unused struct session *s)
/* Notify control clients that sessions changed. */
static void
control_session_created_cb(__unused const char *name,
__unused struct event_payload *ep, __unused void *sink_data)
{
struct client *c;
@@ -206,8 +264,10 @@ control_notify_session_created(__unused struct session *s)
}
}
void
control_notify_session_closed(__unused struct session *s)
/* Notify control clients that sessions changed. */
static void
control_session_closed_cb(__unused const char *name,
__unused struct event_payload *ep, __unused void *sink_data)
{
struct client *c;
@@ -219,9 +279,12 @@ control_notify_session_closed(__unused struct session *s)
}
}
void
control_notify_session_window_changed(struct session *s)
/* Notify control clients that the current window changed. */
static void
control_session_window_changed_cb(__unused const char *name,
struct event_payload *ep, __unused void *sink_data)
{
struct session *s = event_payload_get_session(ep, "session");
struct client *c;
/*
@@ -229,7 +292,7 @@ control_notify_session_window_changed(struct session *s)
* session has been destroyed (which sets curw to NULL) but is kept
* alive by the notification's reference. Skip the notification.
*/
if (s->curw == NULL)
if (s == NULL || s->curw == NULL)
return;
TAILQ_FOREACH(c, &clients, entry) {
@@ -241,28 +304,68 @@ control_notify_session_window_changed(struct session *s)
}
}
void
control_notify_paste_buffer_changed(const char *name)
/* Notify control clients that a paste buffer changed. */
static void
control_paste_buffer_changed_cb(__unused const char *name,
struct event_payload *ep, __unused void *sink_data)
{
const char *pbname = event_payload_get_string(ep, "paste_buffer");
struct client *c;
if (pbname == NULL)
return;
TAILQ_FOREACH(c, &clients, entry) {
if (!CONTROL_SHOULD_NOTIFY_CLIENT(c))
continue;
control_write(c, "%%paste-buffer-changed %s", name);
control_write(c, "%%paste-buffer-changed %s", pbname);
}
}
void
control_notify_paste_buffer_deleted(const char *name)
/* Notify control clients that a paste buffer was deleted. */
static void
control_paste_buffer_deleted_cb(__unused const char *name,
struct event_payload *ep, __unused void *sink_data)
{
const char *pbname = event_payload_get_string(ep, "paste_buffer");
struct client *c;
if (pbname == NULL)
return;
TAILQ_FOREACH(c, &clients, entry) {
if (!CONTROL_SHOULD_NOTIFY_CLIENT(c))
continue;
control_write(c, "%%paste-buffer-deleted %s", name);
control_write(c, "%%paste-buffer-deleted %s", pbname);
}
}
/* Build control event sinks. */
void
control_build_events(void)
{
/* Control event sink callbacks. */
static struct {
const char *name;
events_cb cb;
} events[] = {
{ "pane-mode-changed", control_pane_mode_changed_cb },
{ "window-layout-changed", control_window_layout_changed_cb },
{ "window-pane-changed", control_window_pane_changed_cb },
{ "window-unlinked", control_window_unlinked_cb },
{ "window-linked", control_window_linked_cb },
{ "window-renamed", control_window_renamed_cb },
{ "client-session-changed", control_client_session_changed_cb },
{ "client-detached", control_client_detached_cb },
{ "session-renamed", control_session_renamed_cb },
{ "session-created", control_session_created_cb },
{ "session-closed", control_session_closed_cb },
{ "session-window-changed", control_session_window_changed_cb },
{ "paste-buffer-changed", control_paste_buffer_changed_cb },
{ "paste-buffer-deleted", control_paste_buffer_deleted_cb }
};
u_int i;
for (i = 0; i < nitems(events); i++)
events_add_sink(events[i].name, events[i].cb, NULL);
}

506
control.c
View File

@@ -1,4 +1,4 @@
/* $OpenBSD$ */
/* $OpenBSD: control.c,v 1.61 2026/07/10 15:45:11 nicm Exp $ */
/*
* Copyright (c) 2012 Nicholas Marriott <nicholas.marriott@gmail.com>
@@ -19,6 +19,9 @@
#include <sys/types.h>
#include <errno.h>
#include <event.h>
#include <poll.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
@@ -75,42 +78,6 @@ struct control_pane {
};
RB_HEAD(control_panes, control_pane);
/* Subscription pane. */
struct control_sub_pane {
u_int pane;
u_int idx;
char *last;
RB_ENTRY(control_sub_pane) entry;
};
RB_HEAD(control_sub_panes, control_sub_pane);
/* Subscription window. */
struct control_sub_window {
u_int window;
u_int idx;
char *last;
RB_ENTRY(control_sub_window) entry;
};
RB_HEAD(control_sub_windows, control_sub_window);
/* Control client subscription. */
struct control_sub {
char *name;
char *format;
enum control_sub_type type;
u_int id;
char *last;
struct control_sub_panes panes;
struct control_sub_windows windows;
RB_ENTRY(control_sub) entry;
};
RB_HEAD(control_subs, control_sub);
/* Control client state. */
struct control_state {
struct control_panes panes;
@@ -123,8 +90,7 @@ struct control_state {
struct bufferevent *read_event;
struct bufferevent *write_event;
struct control_subs subs;
struct event subs_timer;
struct monitor_set *subs;
};
/* Low and high watermarks. */
@@ -154,75 +120,6 @@ control_pane_cmp(struct control_pane *cp1, struct control_pane *cp2)
}
RB_GENERATE_STATIC(control_panes, control_pane, entry, control_pane_cmp);
/* Compare client subs. */
static int
control_sub_cmp(struct control_sub *csub1, struct control_sub *csub2)
{
return (strcmp(csub1->name, csub2->name));
}
RB_GENERATE_STATIC(control_subs, control_sub, entry, control_sub_cmp);
/* Compare client subscription panes. */
static int
control_sub_pane_cmp(struct control_sub_pane *csp1,
struct control_sub_pane *csp2)
{
if (csp1->pane < csp2->pane)
return (-1);
if (csp1->pane > csp2->pane)
return (1);
if (csp1->idx < csp2->idx)
return (-1);
if (csp1->idx > csp2->idx)
return (1);
return (0);
}
RB_GENERATE_STATIC(control_sub_panes, control_sub_pane, entry,
control_sub_pane_cmp);
/* Compare client subscription windows. */
static int
control_sub_window_cmp(struct control_sub_window *csw1,
struct control_sub_window *csw2)
{
if (csw1->window < csw2->window)
return (-1);
if (csw1->window > csw2->window)
return (1);
if (csw1->idx < csw2->idx)
return (-1);
if (csw1->idx > csw2->idx)
return (1);
return (0);
}
RB_GENERATE_STATIC(control_sub_windows, control_sub_window, entry,
control_sub_window_cmp);
/* Free a subscription. */
static void
control_free_sub(struct control_state *cs, struct control_sub *csub)
{
struct control_sub_pane *csp, *csp1;
struct control_sub_window *csw, *csw1;
RB_FOREACH_SAFE(csp, control_sub_panes, &csub->panes, csp1) {
RB_REMOVE(control_sub_panes, &csub->panes, csp);
free(csp->last);
free(csp);
}
RB_FOREACH_SAFE(csw, control_sub_windows, &csub->windows, csw1) {
RB_REMOVE(control_sub_windows, &csub->windows, csw);
free(csw->last);
free(csw);
}
free(csub->last);
RB_REMOVE(control_subs, &cs->subs, csub);
free(csub->name);
free(csub->format);
free(csub);
}
/* Free a block. */
static void
control_free_block(struct control_state *cs, struct control_block *cb)
@@ -481,7 +378,7 @@ control_write_output(struct client *c, struct window_pane *wp)
if (winlink_find_by_window(&c->session->windows, wp->window) == NULL)
return;
if (c->flags & CONTROL_IGNORE_FLAGS) {
if (c->flags & (CONTROL_IGNORE_FLAGS|CLIENT_EXIT)) {
cp = control_get_pane(c, wp);
if (cp != NULL)
goto ignore;
@@ -597,6 +494,53 @@ control_all_done(struct client *c)
return (EVBUFFER_LENGTH(cs->write_event->output) == 0);
}
/*
* Wait for the terminal to send an empty line or close, used by a control
* client after printing %exit so a wrapping terminal (such as iTerm2) can
* finish reading.
*/
void
control_wait_exit(int fd)
{
struct pollfd pfd;
struct evbuffer *evb;
char *line;
int n;
evb = evbuffer_new();
if (evb == NULL)
fatalx("out of memory");
for (;;) {
line = evbuffer_readln(evb, NULL, EVBUFFER_EOL_LF);
if (line != NULL) {
if (*line == '\0') { /* empty line, stop */
free(line);
break;
}
free(line);
continue; /* drain buffered lines first */
}
memset(&pfd, 0, sizeof pfd);
pfd.fd = fd;
pfd.events = POLLIN;
if (poll(&pfd, 1, INFTIM) == -1) {
if (errno == EINTR)
continue;
break;
}
n = evbuffer_read(evb, fd, -1);
if (n == 0)
break;
if (n == -1 && errno != EAGAIN && errno != EINTR)
break;
}
evbuffer_free(evb);
}
/* Flush all blocks until output. */
static void
control_flush_all_blocks(struct client *c)
@@ -772,6 +716,30 @@ control_write_callback(__unused struct bufferevent *bufev, void *data)
bufferevent_disable(cs->write_event, EV_WRITE);
}
/* Write a subscription change. */
static void
control_sub_change(struct monitor_change *change, __unused void *data)
{
struct client *c = change->c;
struct session *s = change->s;
struct winlink *wl = change->wl;
struct window_pane *wp = change->wp;
struct window *w;
if (wp != NULL) {
w = wp->window;
control_write(c, "%%subscription-changed %s $%u @%u %u %%%u : %s",
change->name, s->id, w->id, wl->idx, wp->id, change->value);
} else if (wl != NULL) {
w = wl->window;
control_write(c, "%%subscription-changed %s $%u @%u %u - : %s",
change->name, s->id, w->id, wl->idx, change->value);
} else {
control_write(c, "%%subscription-changed %s $%u - - - : %s",
change->name, s->id, change->value);
}
}
/* Initialize for control mode. */
void
control_start(struct client *c)
@@ -789,7 +757,7 @@ control_start(struct client *c)
RB_INIT(&cs->panes);
TAILQ_INIT(&cs->pending_list);
TAILQ_INIT(&cs->all_blocks);
RB_INIT(&cs->subs);
cs->subs = monitor_create_client(c, control_sub_change, NULL);
cs->read_event = bufferevent_new(c->fd, control_read_callback,
control_write_callback, control_error_callback, c);
@@ -838,20 +806,16 @@ control_stop(struct client *c)
{
struct control_state *cs = c->control_state;
struct control_block *cb, *cb1;
struct control_sub *csub, *csub1;
if (cs == NULL)
return;
monitor_destroy(cs->subs);
if (~c->flags & CLIENT_CONTROLCONTROL)
bufferevent_free(cs->write_event);
bufferevent_free(cs->read_event);
RB_FOREACH_SAFE(csub, control_subs, &cs->subs, csub1)
control_free_sub(cs, csub);
if (evtimer_initialized(&cs->subs_timer))
evtimer_del(&cs->subs_timer);
control_reset_offsets(c);
TAILQ_FOREACH_SAFE(cb, &cs->all_blocks, all_entry, cb1)
control_free_block(cs, cb);
@@ -860,313 +824,14 @@ control_stop(struct client *c)
free(cs);
}
/* Check session subscription. */
static void
control_check_subs_session(struct client *c, struct control_sub *csub,
struct format_tree *ft)
{
struct session *s = c->session;
char *value;
value = format_expand(ft, csub->format);
if (csub->last != NULL && strcmp(value, csub->last) == 0) {
free(value);
return;
}
control_write(c,
"%%subscription-changed %s $%u - - - : %s",
csub->name, s->id, value);
free(csub->last);
csub->last = value;
}
/* Check pane subscription. */
static void
control_check_subs_pane(struct client *c, struct control_sub *csub)
{
struct session *s = c->session;
struct window_pane *wp;
struct window *w;
struct winlink *wl;
struct format_tree *ft;
char *value;
struct control_sub_pane *csp, find;
wp = window_pane_find_by_id(csub->id);
if (wp == NULL || wp->fd == -1)
return;
w = wp->window;
TAILQ_FOREACH(wl, &w->winlinks, wentry) {
if (wl->session != s)
continue;
ft = format_create_defaults(NULL, c, s, wl, wp);
value = format_expand(ft, csub->format);
format_free(ft);
find.pane = wp->id;
find.idx = wl->idx;
csp = RB_FIND(control_sub_panes, &csub->panes, &find);
if (csp == NULL) {
csp = xcalloc(1, sizeof *csp);
csp->pane = wp->id;
csp->idx = wl->idx;
RB_INSERT(control_sub_panes, &csub->panes, csp);
}
if (csp->last != NULL && strcmp(value, csp->last) == 0) {
free(value);
continue;
}
control_write(c,
"%%subscription-changed %s $%u @%u %u %%%u : %s",
csub->name, s->id, w->id, wl->idx, wp->id, value);
free(csp->last);
csp->last = value;
}
}
/* Check all-panes subscription for a pane. */
static void
control_check_subs_all_panes_one(struct client *c, struct control_sub *csub,
struct format_tree *ft, struct winlink *wl, struct window_pane *wp)
{
struct session *s = c->session;
struct window *w = wl->window;
char *value;
struct control_sub_pane *csp, find;
value = format_expand(ft, csub->format);
find.pane = wp->id;
find.idx = wl->idx;
csp = RB_FIND(control_sub_panes, &csub->panes, &find);
if (csp == NULL) {
csp = xcalloc(1, sizeof *csp);
csp->pane = wp->id;
csp->idx = wl->idx;
RB_INSERT(control_sub_panes, &csub->panes, csp);
}
if (csp->last != NULL && strcmp(value, csp->last) == 0) {
free(value);
return;
}
control_write(c,
"%%subscription-changed %s $%u @%u %u %%%u : %s",
csub->name, s->id, w->id, wl->idx, wp->id, value);
free(csp->last);
csp->last = value;
}
/* Check window subscription. */
static void
control_check_subs_window(struct client *c, struct control_sub *csub)
{
struct session *s = c->session;
struct window *w;
struct winlink *wl;
struct format_tree *ft;
char *value;
struct control_sub_window *csw, find;
w = window_find_by_id(csub->id);
if (w == NULL)
return;
TAILQ_FOREACH(wl, &w->winlinks, wentry) {
if (wl->session != s)
continue;
ft = format_create_defaults(NULL, c, s, wl, NULL);
value = format_expand(ft, csub->format);
format_free(ft);
find.window = w->id;
find.idx = wl->idx;
csw = RB_FIND(control_sub_windows, &csub->windows, &find);
if (csw == NULL) {
csw = xcalloc(1, sizeof *csw);
csw->window = w->id;
csw->idx = wl->idx;
RB_INSERT(control_sub_windows, &csub->windows, csw);
}
if (csw->last != NULL && strcmp(value, csw->last) == 0) {
free(value);
continue;
}
control_write(c,
"%%subscription-changed %s $%u @%u %u - : %s",
csub->name, s->id, w->id, wl->idx, value);
free(csw->last);
csw->last = value;
}
}
/* Check all-windows subscription for a window. */
static void
control_check_subs_all_windows_one(struct client *c, struct control_sub *csub,
struct format_tree *ft, struct winlink *wl)
{
struct session *s = c->session;
struct window *w = wl->window;
char *value;
struct control_sub_window *csw, find;
value = format_expand(ft, csub->format);
find.window = w->id;
find.idx = wl->idx;
csw = RB_FIND(control_sub_windows, &csub->windows, &find);
if (csw == NULL) {
csw = xcalloc(1, sizeof *csw);
csw->window = w->id;
csw->idx = wl->idx;
RB_INSERT(control_sub_windows, &csub->windows, csw);
}
if (csw->last != NULL && strcmp(value, csw->last) == 0) {
free(value);
return;
}
control_write(c,
"%%subscription-changed %s $%u @%u %u - : %s",
csub->name, s->id, w->id, wl->idx, value);
free(csw->last);
csw->last = value;
}
/* Check subscriptions timer. */
static void
control_check_subs_timer(__unused int fd, __unused short events, void *data)
{
struct client *c = data;
struct control_state *cs = c->control_state;
struct control_sub *csub, *csub1;
struct session *s = c->session;
struct format_tree *ft;
struct winlink *wl;
struct window_pane *wp;
struct timeval tv = { .tv_sec = 1 };
int have_session = 0, have_all_panes = 0;
int have_all_windows = 0;
log_debug("%s: timer fired", __func__);
evtimer_add(&cs->subs_timer, &tv);
if (s == NULL)
return;
/* Find which subscription types are present. */
RB_FOREACH(csub, control_subs, &cs->subs) {
switch (csub->type) {
case CONTROL_SUB_SESSION:
have_session = 1;
break;
case CONTROL_SUB_ALL_PANES:
have_all_panes = 1;
break;
case CONTROL_SUB_ALL_WINDOWS:
have_all_windows = 1;
break;
default:
break;
}
}
/* Check session subscriptions. */
if (have_session) {
ft = format_create_defaults(NULL, c, s, NULL, NULL);
RB_FOREACH_SAFE(csub, control_subs, &cs->subs, csub1) {
if (csub->type == CONTROL_SUB_SESSION)
control_check_subs_session(c, csub, ft);
}
format_free(ft);
}
/* Check pane and window subscriptions. */
RB_FOREACH_SAFE(csub, control_subs, &cs->subs, csub1) {
switch (csub->type) {
case CONTROL_SUB_PANE:
control_check_subs_pane(c, csub);
break;
case CONTROL_SUB_WINDOW:
control_check_subs_window(c, csub);
break;
case CONTROL_SUB_SESSION:
case CONTROL_SUB_ALL_PANES:
case CONTROL_SUB_ALL_WINDOWS:
break;
}
}
/* Check all-panes subscriptions. */
if (have_all_panes) {
RB_FOREACH(wl, winlinks, &s->windows) {
TAILQ_FOREACH(wp, &wl->window->panes, entry) {
ft = format_create_defaults(NULL, c, s, wl, wp);
RB_FOREACH_SAFE(csub, control_subs, &cs->subs,
csub1) {
if (csub->type != CONTROL_SUB_ALL_PANES)
continue;
control_check_subs_all_panes_one(c,
csub, ft, wl, wp);
}
format_free(ft);
}
}
}
/* Check all-windows subscriptions. */
if (have_all_windows) {
RB_FOREACH(wl, winlinks, &s->windows) {
ft = format_create_defaults(NULL, c, s, wl, NULL);
RB_FOREACH_SAFE(csub, control_subs, &cs->subs,
csub1) {
if (csub->type != CONTROL_SUB_ALL_WINDOWS)
continue;
control_check_subs_all_windows_one(c, csub, ft,
wl);
}
format_free(ft);
}
}
}
/* Add a subscription. */
void
control_add_sub(struct client *c, const char *name, enum control_sub_type type,
control_add_sub(struct client *c, const char *name, enum monitor_type type,
int id, const char *format)
{
struct control_state *cs = c->control_state;
struct control_sub *csub, find;
struct timeval tv = { .tv_sec = 1 };
find.name = (char *)name;
if ((csub = RB_FIND(control_subs, &cs->subs, &find)) != NULL)
control_free_sub(cs, csub);
csub = xcalloc(1, sizeof *csub);
csub->name = xstrdup(name);
csub->type = type;
csub->id = id;
csub->format = xstrdup(format);
RB_INSERT(control_subs, &cs->subs, csub);
RB_INIT(&csub->panes);
RB_INIT(&csub->windows);
if (!evtimer_initialized(&cs->subs_timer))
evtimer_set(&cs->subs_timer, control_check_subs_timer, c);
if (!evtimer_pending(&cs->subs_timer, NULL))
evtimer_add(&cs->subs_timer, &tv);
monitor_add(cs->subs, name, type, id, format, MONITOR_NOTIFY_INITIAL);
}
/* Remove a subscription. */
@@ -1174,11 +839,6 @@ void
control_remove_sub(struct client *c, const char *name)
{
struct control_state *cs = c->control_state;
struct control_sub *csub, find;
find.name = (char *)name;
if ((csub = RB_FIND(control_subs, &cs->subs, &find)) != NULL)
control_free_sub(cs, csub);
if (RB_EMPTY(&cs->subs))
evtimer_del(&cs->subs_timer);
monitor_remove(cs->subs, name);
}

View File

@@ -1,4 +1,4 @@
/* $OpenBSD$ */
/* $OpenBSD: environ.c,v 1.31 2026/06/13 20:07:30 nicm Exp $ */
/*
* Copyright (c) 2009 Nicholas Marriott <nicholas.marriott@gmail.com>

672
events-payload.c Normal file
View File

@@ -0,0 +1,672 @@
/* $OpenBSD: events-payload.c,v 1.1 2026/07/10 13:38:45 nicm Exp $ */
/*
* Copyright (c) 2026 Nicholas Marriott <nicholas.marriott@gmail.com>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER
* IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
* OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include <sys/types.h>
#include <event.h>
#include <stdarg.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include "tmux.h"
/* Event payload item. */
struct event_payload_item {
char *name;
enum event_payload_type type;
union {
char *string;
time_t time;
int number;
u_int unsigned_number;
struct client *client;
struct session *session;
struct window *window;
struct window_pane *pane;
struct {
void *ptr;
event_payload_free_cb free_cb;
event_payload_print_cb print_cb;
} pointer;
};
RB_ENTRY(event_payload_item) entry;
};
RB_HEAD(event_payload_tree, event_payload_item);
struct event_payload {
struct event_payload_tree items;
struct cmd_find_state target;
};
static int
event_payload_cmp(struct event_payload_item *epi1,
struct event_payload_item *epi2)
{
return (strcmp(epi1->name, epi2->name));
}
RB_GENERATE_STATIC(event_payload_tree, event_payload_item, entry,
event_payload_cmp);
/* Find an item. */
static struct event_payload_item *
event_payload_find(struct event_payload *ep, const char *name)
{
struct event_payload_item find = { .name = (char *)name };
return (RB_FIND(event_payload_tree, &ep->items, &find));
}
/* Free the target in a payload. */
static void
event_payload_free_target(struct event_payload *ep)
{
struct cmd_find_state *target = &ep->target;
if (target->s != NULL)
session_remove_ref(target->s, __func__);
if (target->w != NULL)
window_remove_ref(target->w, __func__);
if (target->wp != NULL)
window_pane_remove_ref(target->wp, __func__);
cmd_find_clear_state(target, 0);
}
/* Free the value in an item. */
static void
event_payload_free_value(struct event_payload_item *epi)
{
switch (epi->type) {
case EVENT_PAYLOAD_STRING:
free(epi->string);
break;
case EVENT_PAYLOAD_CLIENT:
server_client_unref(epi->client);
break;
case EVENT_PAYLOAD_SESSION:
session_remove_ref(epi->session, __func__);
break;
case EVENT_PAYLOAD_WINDOW:
window_remove_ref(epi->window, __func__);
break;
case EVENT_PAYLOAD_PANE:
window_pane_remove_ref(epi->pane, __func__);
break;
case EVENT_PAYLOAD_POINTER:
if (epi->pointer.free_cb != NULL)
epi->pointer.free_cb(epi->pointer.ptr);
break;
case EVENT_PAYLOAD_INT:
case EVENT_PAYLOAD_UINT:
case EVENT_PAYLOAD_TIME:
break;
}
}
/* Set an item. */
static void
event_payload_set_item(struct event_payload *ep, const char *name,
struct event_payload_item *new)
{
struct event_payload_item *old;
new->name = xstrdup(name);
old = RB_INSERT(event_payload_tree, &ep->items, new);
if (old != NULL) {
RB_REMOVE(event_payload_tree, &ep->items, old);
event_payload_free_value(old);
free(old->name);
free(old);
RB_INSERT(event_payload_tree, &ep->items, new);
}
}
/* Create an event payload. */
struct event_payload *
event_payload_create(void)
{
struct event_payload *ep;
ep = xcalloc(1, sizeof *ep);
RB_INIT(&ep->items);
cmd_find_clear_state(&ep->target, 0);
return (ep);
}
/* Free an event payload. */
void
event_payload_free(struct event_payload *ep)
{
struct event_payload_item *epi, *epi1;
if (ep != NULL) {
RB_FOREACH_SAFE(epi, event_payload_tree, &ep->items, epi1) {
RB_REMOVE(event_payload_tree, &ep->items, epi);
event_payload_free_value(epi);
free(epi->name);
free(epi);
}
event_payload_free_target(ep);
free(ep);
}
}
/* Set the target. */
void
event_payload_set_target(struct event_payload *ep, struct cmd_find_state *fs)
{
struct cmd_find_state *target = &ep->target;
event_payload_free_target(ep);
if (fs->s != NULL) {
session_add_ref(fs->s, __func__);
target->s = fs->s;
}
if (fs->wl != NULL) {
target->idx = fs->wl->idx;
if (target->s == NULL) {
session_add_ref(fs->wl->session, __func__);
target->s = fs->wl->session;
}
} else
target->idx = -1;
if (fs->w != NULL) {
window_add_ref(fs->w, __func__);
target->w = fs->w;
} else if (fs->wl != NULL) {
window_add_ref(fs->wl->window, __func__);
target->w = fs->wl->window;
}
if (fs->wp != NULL) {
window_pane_add_ref(fs->wp, __func__);
target->wp = fs->wp;
}
}
/* Get the target. */
int
event_payload_get_target(struct event_payload *ep, struct cmd_find_state *fs)
{
struct cmd_find_state *t = &ep->target;
struct winlink *wl = NULL;
int flags = fs->flags;
if (t->idx != -1 &&
t->s != NULL &&
t->w != NULL &&
session_alive(t->s)) {
wl = winlink_find_by_index(&t->s->windows, t->idx);
if (wl != NULL && wl->window != t->w)
wl = NULL;
}
cmd_find_clear_state(fs, flags);
fs->s = t->s;
fs->w = t->w;
fs->wp = t->wp;
fs->wl = wl;
fs->idx = (wl != NULL ? wl->idx : -1);
if (cmd_find_valid_state(fs))
return (1);
if (wl != NULL &&
t->wp != NULL &&
window_has_pane(wl->window, t->wp)) {
cmd_find_from_winlink_pane(fs, wl, t->wp, flags);
if (cmd_find_valid_state(fs))
return (1);
}
if (t->wp != NULL &&
cmd_find_from_pane(fs, t->wp, flags) == 0 &&
cmd_find_valid_state(fs))
return (1);
if (wl != NULL) {
cmd_find_from_winlink(fs, wl, flags);
if (cmd_find_valid_state(fs))
return (1);
}
if (t->s != NULL &&
t->w != NULL &&
session_alive(t->s) &&
cmd_find_from_session_window(fs, t->s, t->w, flags) == 0 &&
cmd_find_valid_state(fs))
return (1);
if (t->s != NULL && session_alive(t->s)) {
cmd_find_from_session(fs, t->s, flags);
if (cmd_find_valid_state(fs))
return (1);
}
if (cmd_find_from_nothing(fs, flags) == 0)
return (1);
cmd_find_clear_state(fs, flags);
return (0);
}
/* Set a string item. */
void
event_payload_set_string(struct event_payload *ep, const char *name,
const char *fmt, ...)
{
struct event_payload_item *epi;
va_list ap;
va_start(ap, fmt);
epi = xcalloc(1, sizeof *epi);
epi->type = EVENT_PAYLOAD_STRING;
xvasprintf(&epi->string, fmt, ap);
event_payload_set_item(ep, name, epi);
va_end(ap);
}
/* Set a time item. */
void
event_payload_set_time(struct event_payload *ep, const char *name,
time_t value)
{
struct event_payload_item *epi;
epi = xcalloc(1, sizeof *epi);
epi->type = EVENT_PAYLOAD_TIME;
epi->time = value;
event_payload_set_item(ep, name, epi);
}
/* Set a number item. */
void
event_payload_set_int(struct event_payload *ep, const char *name, int value)
{
struct event_payload_item *epi;
epi = xcalloc(1, sizeof *epi);
epi->type = EVENT_PAYLOAD_INT;
epi->number = value;
event_payload_set_item(ep, name, epi);
}
/* Set an unsigned number item. */
void
event_payload_set_uint(struct event_payload *ep, const char *name, u_int value)
{
struct event_payload_item *epi;
epi = xcalloc(1, sizeof *epi);
epi->type = EVENT_PAYLOAD_UINT;
epi->unsigned_number = value;
event_payload_set_item(ep, name, epi);
}
/* Set a client item. */
void
event_payload_set_client(struct event_payload *ep, const char *name,
struct client *c)
{
struct event_payload_item *epi;
c->references++;
epi = xcalloc(1, sizeof *epi);
epi->type = EVENT_PAYLOAD_CLIENT;
epi->client = c;
event_payload_set_item(ep, name, epi);
}
/* Set a session item. */
void
event_payload_set_session(struct event_payload *ep, const char *name,
struct session *s)
{
struct event_payload_item *epi;
session_add_ref(s, __func__);
epi = xcalloc(1, sizeof *epi);
epi->type = EVENT_PAYLOAD_SESSION;
epi->session = s;
event_payload_set_item(ep, name, epi);
}
/* Set a window item. */
void
event_payload_set_window(struct event_payload *ep, const char *name,
struct window *w)
{
struct event_payload_item *epi;
window_add_ref(w, __func__);
epi = xcalloc(1, sizeof *epi);
epi->type = EVENT_PAYLOAD_WINDOW;
epi->window = w;
event_payload_set_item(ep, name, epi);
}
/* Set a pane item. */
void
event_payload_set_pane(struct event_payload *ep, const char *name,
struct window_pane *wp)
{
struct event_payload_item *epi;
window_pane_add_ref(wp, __func__);
epi = xcalloc(1, sizeof *epi);
epi->type = EVENT_PAYLOAD_PANE;
epi->pane = wp;
event_payload_set_item(ep, name, epi);
}
/* Set a pointer item. */
void
event_payload_set_pointer(struct event_payload *ep, const char *name,
void *ptr, event_payload_free_cb free_cb, event_payload_print_cb print_cb)
{
struct event_payload_item *epi;
epi = xcalloc(1, sizeof *epi);
epi->type = EVENT_PAYLOAD_POINTER;
epi->pointer.ptr = ptr;
epi->pointer.free_cb = free_cb;
epi->pointer.print_cb = print_cb;
event_payload_set_item(ep, name, epi);
}
/* Get a string item. */
const char *
event_payload_get_string(struct event_payload *ep, const char *name)
{
struct event_payload_item *epi;
epi = event_payload_find(ep, name);
if (epi == NULL || epi->type != EVENT_PAYLOAD_STRING)
return (NULL);
return (epi->string);
}
/* Print a payload item. */
static void
event_payload_add_item(struct event_payload_item *epi, struct evbuffer *evb)
{
switch (epi->type) {
case EVENT_PAYLOAD_STRING:
evbuffer_add_printf(evb, "%s", epi->string);
break;
case EVENT_PAYLOAD_TIME:
evbuffer_add_printf(evb, "%lld", (long long)epi->time);
break;
case EVENT_PAYLOAD_INT:
evbuffer_add_printf(evb, "%d", epi->number);
break;
case EVENT_PAYLOAD_UINT:
evbuffer_add_printf(evb, "%u", epi->unsigned_number);
break;
case EVENT_PAYLOAD_CLIENT:
evbuffer_add_printf(evb, "%s", epi->client->name);
break;
case EVENT_PAYLOAD_SESSION:
evbuffer_add_printf(evb, "$%u", epi->session->id);
break;
case EVENT_PAYLOAD_WINDOW:
evbuffer_add_printf(evb, "@%u", epi->window->id);
break;
case EVENT_PAYLOAD_PANE:
evbuffer_add_printf(evb, "%%%u", epi->pane->id);
break;
case EVENT_PAYLOAD_POINTER:
if (epi->pointer.print_cb != NULL)
epi->pointer.print_cb(epi->pointer.ptr, evb);
else
evbuffer_add_printf(evb, "%p", epi->pointer.ptr);
break;
}
}
/* Print a payload item. */
char *
event_payload_item_print(struct event_payload_item *epi)
{
struct evbuffer *evb;
char *value = NULL;
size_t size;
evb = evbuffer_new();
if (evb == NULL)
fatalx("out of memory");
event_payload_add_item(epi, evb);
if ((size = EVBUFFER_LENGTH(evb)) != 0)
value = xmemdup(EVBUFFER_DATA(evb), size);
else
value = xstrdup("");
evbuffer_free(evb);
return (value);
}
/* Print an item value. */
char *
event_payload_print(struct event_payload *ep, const char *name)
{
struct event_payload_item *epi;
epi = event_payload_find(ep, name);
if (epi == NULL)
return (NULL);
return (event_payload_item_print(epi));
}
/* Add payload items as formats. */
void
event_payload_add_formats(struct event_payload *ep, struct format_tree *ft,
const char *prefix)
{
struct event_payload_item *epi;
char *name, *value;
const char *key;
if (prefix == NULL)
prefix = "";
RB_FOREACH(epi, event_payload_tree, &ep->items) {
key = epi->name;
if (*key == '_')
continue;
value = event_payload_item_print(epi);
xasprintf(&name, "%s%s", prefix, key);
format_add(ft, name, "%s", value);
free(name);
free(value);
if (epi->type == EVENT_PAYLOAD_SESSION) {
xasprintf(&name, "%s%s_name", prefix, key);
format_add(ft, name, "%s", epi->session->name);
free(name);
} else if (epi->type == EVENT_PAYLOAD_WINDOW) {
xasprintf(&name, "%s%s_name", prefix, key);
format_add(ft, name, "%s", epi->window->name);
free(name);
}
}
}
/* Get the first payload item. */
struct event_payload_item *
event_payload_first(struct event_payload *ep)
{
return (RB_MIN(event_payload_tree, &ep->items));
}
/* Get the next payload item. */
struct event_payload_item *
event_payload_next(struct event_payload_item *epi)
{
return (RB_NEXT(event_payload_tree, , epi));
}
/* Get a payload item name. */
const char *
event_payload_item_name(struct event_payload_item *epi)
{
return (epi->name);
}
/* Get a payload item type. */
enum event_payload_type
event_payload_item_type(struct event_payload_item *epi)
{
return (epi->type);
}
/* Log a payload. */
void
event_payload_log(struct event_payload *ep, const char *fmt, ...)
{
struct event_payload_item *epi;
struct evbuffer *evb;
va_list ap;
char *prefix;
va_start(ap, fmt);
xvasprintf(&prefix, fmt, ap);
va_end(ap);
evb = evbuffer_new();
if (evb == NULL)
fatalx("out of memory");
if (ep != NULL) {
RB_FOREACH(epi, event_payload_tree, &ep->items) {
if (EVBUFFER_LENGTH(evb) != 0)
evbuffer_add_printf(evb, ", ");
evbuffer_add_printf(evb, "%s=", epi->name);
event_payload_add_item(epi, evb);
}
}
log_debug("%s%.*s", prefix, (int)EVBUFFER_LENGTH(evb),
(char *)EVBUFFER_DATA(evb));
evbuffer_free(evb);
free(prefix);
}
/* Get a time item. */
time_t
event_payload_get_time(struct event_payload *ep, const char *name)
{
struct event_payload_item *epi;
epi = event_payload_find(ep, name);
if (epi == NULL || epi->type != EVENT_PAYLOAD_TIME)
return (0);
return (epi->time);
}
/* Get a number item. */
int
event_payload_get_int(struct event_payload *ep, const char *name, int *value)
{
struct event_payload_item *epi;
epi = event_payload_find(ep, name);
if (epi == NULL || epi->type != EVENT_PAYLOAD_INT)
return (-1);
*value = epi->number;
return (0);
}
/* Get an unsigned number item. */
int
event_payload_get_uint(struct event_payload *ep, const char *name, u_int *value)
{
struct event_payload_item *epi;
epi = event_payload_find(ep, name);
if (epi == NULL || epi->type != EVENT_PAYLOAD_UINT)
return (-1);
*value = epi->unsigned_number;
return (0);
}
/* Get a client item. */
struct client *
event_payload_get_client(struct event_payload *ep, const char *name)
{
struct event_payload_item *epi;
epi = event_payload_find(ep, name);
if (epi == NULL || epi->type != EVENT_PAYLOAD_CLIENT)
return (NULL);
return (epi->client);
}
/* Get a session item. */
struct session *
event_payload_get_session(struct event_payload *ep, const char *name)
{
struct event_payload_item *epi;
epi = event_payload_find(ep, name);
if (epi == NULL || epi->type != EVENT_PAYLOAD_SESSION)
return (NULL);
return (epi->session);
}
/* Get a window item. */
struct window *
event_payload_get_window(struct event_payload *ep, const char *name)
{
struct event_payload_item *epi;
epi = event_payload_find(ep, name);
if (epi == NULL || epi->type != EVENT_PAYLOAD_WINDOW)
return (NULL);
return (epi->window);
}
/* Get a pane item. */
struct window_pane *
event_payload_get_pane(struct event_payload *ep, const char *name)
{
struct event_payload_item *epi;
epi = event_payload_find(ep, name);
if (epi == NULL || epi->type != EVENT_PAYLOAD_PANE)
return (NULL);
return (epi->pane);
}
/* Get a pointer item. */
void *
event_payload_get_pointer(struct event_payload *ep, const char *name)
{
struct event_payload_item *epi;
epi = event_payload_find(ep, name);
if (epi == NULL || epi->type != EVENT_PAYLOAD_POINTER)
return (NULL);
return (epi->pointer.ptr);
}

199
events.c Normal file
View File

@@ -0,0 +1,199 @@
/* $OpenBSD: events.c,v 1.1 2026/07/10 13:38:45 nicm Exp $ */
/*
* Copyright (c) 2026 Nicholas Marriott <nicholas.marriott@gmail.com>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include <sys/types.h>
#include <stdlib.h>
#include <string.h>
#include "tmux.h"
/* Event sink. */
struct events_sink {
char *name;
events_cb cb;
void *data;
int dead;
u_int generation;
TAILQ_ENTRY(events_sink) entry;
};
TAILQ_HEAD(events_sinks, events_sink);
static struct events_sinks events_sinks = TAILQ_HEAD_INITIALIZER(events_sinks);
static u_int events_dispatching;
static u_int events_generation;
/* Free an event sink. */
static void
events_free_sink(struct events_sink *es)
{
TAILQ_REMOVE(&events_sinks, es, entry);
free(es->name);
free(es);
}
/* Free dead event sinks. */
static void
events_free_dead(void)
{
struct events_sink *es, *es1;
TAILQ_FOREACH_SAFE(es, &events_sinks, entry, es1) {
if (es->dead)
events_free_sink(es);
}
}
/* Add an event sink. */
struct events_sink *
events_add_sink(const char *name, events_cb cb, void *data)
{
struct events_sink *es;
es = xcalloc(1, sizeof *es);
es->name = xstrdup(name);
es->cb = cb;
es->data = data;
es->generation = ++events_generation;
TAILQ_INSERT_TAIL(&events_sinks, es, entry);
return (es);
}
/* Remove an event sink. */
void
events_remove_sink(struct events_sink *es)
{
if (es != NULL && !es->dead) {
if (events_dispatching != 0)
es->dead = 1;
else
events_free_sink(es);
}
}
/* Fire an event. */
void
events_fire(const char *name, struct event_payload *ep)
{
struct events_sink *es;
u_int generation = events_generation;
event_payload_set_string(ep, "event", "%s", name);
if (log_get_level() != 0)
event_payload_log(ep, "%s: %s: ", __func__, name);
events_dispatching++;
TAILQ_FOREACH(es, &events_sinks, entry) {
if (es->dead || es->generation > generation)
continue;
if (strcmp(es->name, name) == 0)
es->cb(name, ep, es->data);
}
if (--events_dispatching == 0)
events_free_dead();
event_payload_free(ep);
}
/* Fire a client event. */
void
events_fire_client(const char *name, struct client *c)
{
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);
events_fire(name, ep);
}
/* Fire a session event. */
void
events_fire_session(const char *name, 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);
events_fire(name, ep);
}
/* Fire a window event. */
void
events_fire_window(const char *name, struct window *w)
{
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);
events_fire(name, ep);
}
/* Fire a pane event. */
void
events_fire_pane(const char *name, struct window_pane *wp)
{
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", wp->window);
events_fire(name, ep);
}
/* Fire a winlink event. */
void
events_fire_winlink(const char *name, struct winlink *wl)
{
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", wl->session);
event_payload_set_window(ep, "window", wl->window);
event_payload_set_int(ep, "window_index", wl->idx);
events_fire(name, ep);
}

2
file.c
View File

@@ -1,4 +1,4 @@
/* $OpenBSD$ */
/* $OpenBSD: file.c,v 1.20 2026/05/17 10:54:01 nicm Exp $ */
/*
* Copyright (c) 2019 Nicholas Marriott <nicholas.marriott@gmail.com>

View File

@@ -1,4 +1,4 @@
/* $OpenBSD$ */
/* $OpenBSD: format-draw.c,v 1.33 2026/06/29 17:08:52 nicm Exp $ */
/*
* Copyright (c) 2019 Nicholas Marriott <nicholas.marriott@gmail.com>

634
format.c
View File

@@ -1,4 +1,4 @@
/* $OpenBSD$ */
/* $OpenBSD: format.c,v 1.403 2026/07/10 15:20:06 nicm Exp $ */
/*
* Copyright (c) 2011 Nicholas Marriott <nicholas.marriott@gmail.com>
@@ -42,6 +42,7 @@
struct format_expand_state;
static char *format_job_get(struct format_expand_state *, const char *);
static char *format_quote_shell_single(const char *);
static char *format_expand1(struct format_expand_state *, const char *);
static int format_replace(struct format_expand_state *, const char *,
size_t, char **, size_t *, size_t *);
@@ -123,6 +124,10 @@ format_job_cmp(struct format_job *fj1, struct format_job *fj2)
#define FORMAT_CLIENT_ENVIRON 0x4000000
#define FORMAT_COLOUR_ESC_FG 0x8000000
#define FORMAT_COLOUR_ESC_BG 0x10000000
#define FORMAT_QUOTE_SHELL_SQ 0x20000000
#define FORMAT_OPTIONS 0x40000000
#define FORMAT_ENVIRON 0x80000000ULL
#define FORMAT_DIFFERENCE 0x100000000ULL
/* Limit on recursion. */
#define FORMAT_LOOP_LIMIT 100
@@ -878,6 +883,37 @@ format_cb_start_command(struct format_tree *ft)
return (cmd_stringify_argv(wp->argc, wp->argv));
}
/* Callback for pane_start_command_list. */
static void *
format_cb_start_command_list(struct format_tree *ft)
{
struct window_pane *wp = ft->wp;
char *buf = NULL, *s;
size_t len = 0;
int i;
if (wp == NULL)
return (NULL);
if (wp->argc == 0)
return (xstrdup(""));
for (i = 0; i < wp->argc; i++) {
s = format_quote_shell_single(wp->argv[i]);
len += strlen(s) + 1;
buf = xrealloc(buf, len);
if (i == 0)
*buf = '\0';
else
strlcat(buf, " ", len);
strlcat(buf, s, len);
free(s);
}
return (buf);
}
/* Callback for pane_start_path. */
static void *
format_cb_start_path(struct format_tree *ft)
@@ -2151,6 +2187,106 @@ format_cb_pane_dead_time(struct format_tree *ft)
return (NULL);
}
/* Callback for pane_last_output_time. */
static void *
format_cb_pane_last_output_time(struct format_tree *ft)
{
struct window_pane *wp = ft->wp;
static struct timeval tv;
if (wp != NULL && wp->last_output_time != 0) {
tv.tv_sec = wp->last_output_time;
tv.tv_usec = 0;
return (&tv);
}
return (NULL);
}
/* Callback for pane_last_prompt_time. */
static void *
format_cb_pane_last_prompt_time(struct format_tree *ft)
{
struct window_pane *wp = ft->wp;
static struct timeval tv;
if (wp != NULL && wp->last_prompt_time != 0) {
tv.tv_sec = wp->last_prompt_time;
tv.tv_usec = 0;
return (&tv);
}
return (NULL);
}
/* Callback for pane_command_start_time. */
static void *
format_cb_pane_command_start_time(struct format_tree *ft)
{
struct window_pane *wp = ft->wp;
static struct timeval tv;
if (wp != NULL && wp->cmd_start_time != 0) {
tv.tv_sec = wp->cmd_start_time;
tv.tv_usec = 0;
return (&tv);
}
return (NULL);
}
/* Callback for pane_command_end_time. */
static void *
format_cb_pane_command_end_time(struct format_tree *ft)
{
struct window_pane *wp = ft->wp;
static struct timeval tv;
if (wp != NULL && wp->cmd_end_time != 0) {
tv.tv_sec = wp->cmd_end_time;
tv.tv_usec = 0;
return (&tv);
}
return (NULL);
}
/* Callback for pane_command_running. */
static void *
format_cb_pane_command_running(struct format_tree *ft)
{
struct window_pane *wp = ft->wp;
if (wp != NULL)
return (format_printf("%d", !!(wp->flags & PANE_CMDRUNNING)));
return (NULL);
}
/* Callback for pane_command_duration. */
static void *
format_cb_pane_command_duration(struct format_tree *ft)
{
struct window_pane *wp = ft->wp;
time_t end;
if (wp == NULL || wp->cmd_start_time == 0)
return (NULL);
if (wp->flags & PANE_CMDRUNNING)
end = time(NULL);
else
end = wp->cmd_end_time;
if (end < wp->cmd_start_time)
end = wp->cmd_start_time;
return (format_printf("%lld", (long long)(end - wp->cmd_start_time)));
}
/* Callback for pane_command_status. */
static void *
format_cb_pane_command_status(struct format_tree *ft)
{
struct window_pane *wp = ft->wp;
if (wp != NULL && wp->cmd_status != -1)
return (format_printf("%d", wp->cmd_status));
return (NULL);
}
/* Callback for pane_format. */
static void *
format_cb_pane_format(struct format_tree *ft)
@@ -2306,7 +2442,7 @@ format_cb_pane_path(struct format_tree *ft)
static void *
format_cb_pane_pid(struct format_tree *ft)
{
if (ft->wp != NULL)
if (ft->wp != NULL && ft->wp->fd != -1)
return (format_printf("%ld", (long)ft->wp->pid));
return (NULL);
}
@@ -2861,6 +2997,19 @@ format_cb_window_height(struct format_tree *ft)
return (NULL);
}
/* Callback for window_manual_height. */
static void *
format_cb_window_manual_height(struct format_tree *ft)
{
struct window *w = ft->w;
if (w == NULL)
return (NULL);
if (options_get_number(w->options, "window-size") != WINDOW_SIZE_MANUAL)
return (xstrdup(""));
return (format_printf("%u", w->manual_sy));
}
/* Callback for window_id. */
static void *
format_cb_window_id(struct format_tree *ft)
@@ -3041,6 +3190,19 @@ format_cb_window_width(struct format_tree *ft)
return (NULL);
}
/* Callback for window_manual_width. */
static void *
format_cb_window_manual_width(struct format_tree *ft)
{
struct window *w = ft->w;
if (w == NULL)
return (NULL);
if (options_get_number(w->options, "window-size") != WINDOW_SIZE_MANUAL)
return (xstrdup(""));
return (format_printf("%u", w->manual_sx));
}
/* Callback for window_zoomed_flag. */
static void *
format_cb_window_zoomed_flag(struct format_tree *ft)
@@ -3441,6 +3603,21 @@ static const struct format_table_entry format_table[] = {
{ "pane_bottom", FORMAT_TABLE_STRING,
format_cb_pane_bottom
},
{ "pane_command_duration", FORMAT_TABLE_STRING,
format_cb_pane_command_duration
},
{ "pane_command_end_time", FORMAT_TABLE_TIME,
format_cb_pane_command_end_time
},
{ "pane_command_running", FORMAT_TABLE_STRING,
format_cb_pane_command_running
},
{ "pane_command_start_time", FORMAT_TABLE_TIME,
format_cb_pane_command_start_time
},
{ "pane_command_status", FORMAT_TABLE_STRING,
format_cb_pane_command_status
},
{ "pane_current_command", FORMAT_TABLE_STRING,
format_cb_current_command
},
@@ -3492,6 +3669,12 @@ static const struct format_table_entry format_table[] = {
{ "pane_last", FORMAT_TABLE_STRING,
format_cb_pane_last
},
{ "pane_last_output_time", FORMAT_TABLE_TIME,
format_cb_pane_last_output_time
},
{ "pane_last_prompt_time", FORMAT_TABLE_TIME,
format_cb_pane_last_prompt_time
},
{ "pane_left", FORMAT_TABLE_STRING,
format_cb_pane_left
},
@@ -3531,6 +3714,9 @@ static const struct format_table_entry format_table[] = {
{ "pane_start_command", FORMAT_TABLE_STRING,
format_cb_start_command
},
{ "pane_start_command_list", FORMAT_TABLE_STRING,
format_cb_start_command_list
},
{ "pane_start_path", FORMAT_TABLE_STRING,
format_cb_start_path
},
@@ -3747,6 +3933,12 @@ static const struct format_table_entry format_table[] = {
{ "window_linked_sessions_list", FORMAT_TABLE_STRING,
format_cb_window_linked_sessions_list
},
{ "window_manual_height", FORMAT_TABLE_STRING,
format_cb_window_manual_height
},
{ "window_manual_width", FORMAT_TABLE_STRING,
format_cb_window_manual_width
},
{ "window_marked_flag", FORMAT_TABLE_STRING,
format_cb_window_marked_flag
},
@@ -4027,6 +4219,29 @@ format_quote_shell(const char *s)
return (out);
}
/* Quote string with POSIX shell single quotes. */
static char *
format_quote_shell_single(const char *s)
{
const char *cp;
char *out, *at;
at = out = xmalloc(strlen(s) * 4 + 3);
*at++ = '\'';
for (cp = s; *cp != '\0'; cp++) {
if (*cp == '\'') {
*at++ = '\'';
*at++ = '\\';
*at++ = '\'';
*at++ = '\'';
} else
*at++ = *cp;
}
*at++ = '\'';
*at = '\0';
return (out);
}
/* Quote #s in string. */
static char *
format_quote_style(const char *s)
@@ -4128,9 +4343,20 @@ format_relative_time(time_t t)
return (xstrdup(out));
}
/* Make a time difference in seconds. */
static char *
format_time_difference(time_t t)
{
time_t now = time(NULL);
char *out;
xasprintf(&out, "%ld", (long)now - (long)t);
return (out);
}
/* Find a format entry. */
static char *
format_find(struct format_tree *ft, const char *key, int modifiers,
format_find(struct format_tree *ft, const char *key, uint64_t modifiers,
const char *time_format)
{
const struct format_table_entry *fte;
@@ -4138,25 +4364,26 @@ format_find(struct format_tree *ft, const char *key, int modifiers,
struct format_entry *fe, fe_find;
struct environ_entry *envent;
struct options_entry *o;
int idx;
char *found = NULL, *saved, s[512];
char *array_key = NULL;
const char *errstr;
time_t t = 0;
struct tm tm;
o = options_parse_get(global_options, key, &idx, 0);
o = options_parse_get(global_options, key, &array_key, 0);
if (o == NULL && ft->wp != NULL)
o = options_parse_get(ft->wp->options, key, &idx, 0);
o = options_parse_get(ft->wp->options, key, &array_key, 0);
if (o == NULL && ft->w != NULL)
o = options_parse_get(ft->w->options, key, &idx, 0);
o = options_parse_get(ft->w->options, key, &array_key, 0);
if (o == NULL)
o = options_parse_get(global_w_options, key, &idx, 0);
o = options_parse_get(global_w_options, key, &array_key, 0);
if (o == NULL && ft->s != NULL)
o = options_parse_get(ft->s->options, key, &idx, 0);
o = options_parse_get(ft->s->options, key, &array_key, 0);
if (o == NULL)
o = options_parse_get(global_s_options, key, &idx, 0);
o = options_parse_get(global_s_options, key, &array_key, 0);
if (o != NULL) {
found = options_to_string(o, idx, 1);
found = options_to_string(o, array_key, 1);
free(array_key);
goto found;
}
@@ -4211,6 +4438,8 @@ found:
return (NULL);
if (modifiers & FORMAT_RELATIVE)
found = format_relative_time(t);
else if (modifiers & FORMAT_DIFFERENCE)
found = format_time_difference(t);
else if (modifiers & FORMAT_PRETTY)
found = format_pretty_time(t, 0);
else {
@@ -4245,6 +4474,11 @@ found:
found = format_quote_shell(saved);
free(saved);
}
if (modifiers & FORMAT_QUOTE_SHELL_SQ) {
saved = found;
found = format_quote_shell_single(saved);
free(saved);
}
if (modifiers & FORMAT_QUOTE_STYLE) {
saved = found;
found = format_quote_style(saved);
@@ -4455,7 +4689,7 @@ format_build_modifiers(struct format_expand_state *es, const char **s,
/*
* Modifiers are a ; separated list of the forms:
* l,m,C,a,b,c,d,I,n,t,w,q,E,T,S,W,P,R,<,>
* l,m,C,a,b,c,d,I,n,t,w,q,E,T,S,W,P,O,V,R,<,>
* =a
* =/a
* =/a/
@@ -4474,7 +4708,7 @@ format_build_modifiers(struct format_expand_state *es, const char **s,
break;
/* Check single character modifiers with no arguments. */
if (strchr("labdnwETSWPL!<>", cp[0]) != NULL &&
if (strchr("labdnwETSWPOVL!<>", cp[0]) != NULL &&
format_is_end(cp[1])) {
format_add_modifier(&list, count, cp, 1, NULL, 0);
cp++;
@@ -4496,7 +4730,7 @@ format_build_modifiers(struct format_expand_state *es, const char **s,
}
/* Now try single character with arguments. */
if (strchr("ImCLNPSst=pReqWc", cp[0]) == NULL)
if (strchr("ImCLNPSOVst=pReqWc", cp[0]) == NULL)
break;
c = cp[0];
@@ -4773,11 +5007,14 @@ format_loop_sessions(struct format_expand_state *es, const char *fmt)
else
use = all;
nft = format_create(c, item, FORMAT_NONE, ft->flags);
format_add(nft, "loop_index", "%d", i);
format_add(nft, "loop_last_flag", "%d", i == n - 1);
format_defaults(nft, ft->c, s, NULL, NULL);
format_copy_state(&next, es, 0);
next.ft = nft;
expanded = format_expand1(&next, use);
format_free(next.ft);
@@ -4820,9 +5057,9 @@ format_window_name(struct format_expand_state *es, const char *fmt)
return (xstrdup("0"));
}
/* Add neighbor window variables to the format tree. */
/* Add neighbour window variables to the format tree. */
static void
format_add_window_neighbor(struct format_tree *nft, struct winlink *wl,
format_add_window_neighbour(struct format_tree *nft, struct winlink *wl,
struct session *s, const char *prefix)
{
struct options_entry *o;
@@ -4842,7 +5079,7 @@ format_add_window_neighbor(struct format_tree *nft, struct winlink *wl,
oname = options_name(o);
if (*oname == '@') {
xasprintf(&prefixed, "%s_%s", prefix, oname);
oval = options_to_string(o, -1, 1);
oval = options_to_string(o, NULL, 1);
format_add(nft, prefixed, "%s", oval);
free(oval);
free(prefixed);
@@ -4855,20 +5092,21 @@ format_add_window_neighbor(struct format_tree *nft, struct winlink *wl,
static char *
format_loop_windows(struct format_expand_state *es, const char *fmt)
{
struct sort_criteria *sc = &sort_crit;
struct format_tree *ft = es->ft;
struct client *c = ft->client;
struct cmdq_item *item = ft->item;
struct format_tree *nft;
struct format_expand_state next;
char *all, *active, *use, *expanded, *value;
struct evbuffer *buffer;
size_t size;
struct winlink *wl, **l;
struct window *w;
int i, n;
struct sort_criteria *sc = &sort_crit;
struct format_tree *ft = es->ft;
struct client *c = ft->client;
struct session *s = ft->s;
struct cmdq_item *item = ft->item;
struct format_tree *nft;
struct format_expand_state next;
char *all, *active, *use, *expanded, *value;
struct evbuffer *buffer;
size_t size;
struct winlink *wl, **l;
struct window *w;
int i, n;
if (ft->s == NULL) {
if (s == NULL) {
format_log(es, "window loop but no session");
return (NULL);
}
@@ -4882,33 +5120,38 @@ format_loop_windows(struct format_expand_state *es, const char *fmt)
if (buffer == NULL)
fatalx("out of memory");
l = sort_get_winlinks_session(ft->s, &n, sc);
l = sort_get_winlinks_session(s, &n, sc);
for (i = 0; i < n; i++) {
wl = l[i];
w = wl->window;
format_log(es, "window loop: %u @%u", wl->idx, w->id);
if (active != NULL && wl == ft->s->curw)
if (active != NULL && wl == s->curw)
use = active;
else
use = all;
nft = format_create(c, item, FORMAT_WINDOW|w->id,
ft->flags);
nft = format_create(c, item, FORMAT_WINDOW|w->id, ft->flags);
format_add(nft, "loop_index", "%d", i);
format_add(nft, "loop_last_flag", "%d", i == n - 1);
format_defaults(nft, ft->c, ft->s, wl, NULL);
/* Add neighbor window data to the format tree. */
format_add(nft, "window_after_active", "%d",
i > 0 && l[i - 1] == ft->s->curw);
format_add(nft, "window_before_active", "%d",
i + 1 < n && l[i + 1] == ft->s->curw);
/* Add neighbour window data to the format tree. */
if (i > 0 && l[i - 1] == s->curw)
format_add(nft, "window_after_active", "1");
else
format_add(nft, "window_after_active", "0");
if (i + 1 < n && l[i + 1] == s->curw)
format_add(nft, "window_before_active", "1");
else
format_add(nft, "window_before_active", "0");
if (i + 1 < n)
format_add_window_neighbor(nft, l[i + 1], ft->s, "next");
format_add_window_neighbour(nft, l[i + 1], s, "next");
if (i > 0)
format_add_window_neighbor(nft, l[i - 1], ft->s, "prev");
format_add_window_neighbour(nft, l[i - 1], s, "prev");
format_defaults(nft, ft->c, s, wl, NULL);
format_copy_state(&next, es, 0);
next.ft = nft;
expanded = format_expand1(&next, use);
format_free(nft);
@@ -4965,13 +5208,15 @@ format_loop_panes(struct format_expand_state *es, const char *fmt)
use = active;
else
use = all;
nft = format_create(c, item, FORMAT_PANE|wp->id,
ft->flags);
nft = format_create(c, item, FORMAT_PANE|wp->id, ft->flags);
format_add(nft, "loop_index", "%d", i);
format_add(nft, "loop_last_flag", "%d", i == n - 1);
format_defaults(nft, ft->c, ft->s, ft->wl, wp);
format_copy_state(&next, es, 0);
next.ft = nft;
expanded = format_expand1(&next, use);
format_free(nft);
@@ -4990,6 +5235,271 @@ format_loop_panes(struct format_expand_state *es, const char *fmt)
return (value);
}
/* Add an option to an options loop. */
static void
format_loop_add_option(struct format_expand_state *es, const char *fmt,
struct evbuffer *buffer, struct options_entry *o, u_int n, u_int i)
{
struct format_tree *ft = es->ft, *nft;
struct format_expand_state next;
const struct options_table_entry *oe = options_table_entry(o);
const char *name = options_name(o);
char *expanded, *s;
int is_array = options_is_array(o);
format_log(es, "option loop: %s", name);
nft = format_create(ft->client, ft->item, FORMAT_NONE, ft->flags);
format_add(nft, "option_name", "%s", name);
s = options_to_string(o, NULL, 0);
format_add(nft, "option_value", "%s", s);
free(s);
format_add(nft, "option_is_array", "%d", is_array);
format_add(nft, "option_array_key", "%s", "");
format_add(nft, "option_array_index", "%s", "");
format_add(nft, "option_array_first", "%d", is_array);
format_add(nft, "option_array_last", "%d", is_array);
format_add(nft, "option_array_count", "%u", n);
if (oe != NULL && (oe->flags & OPTIONS_TABLE_IS_HOOK))
format_add(nft, "option_is_hook", "1");
else
format_add(nft, "option_is_hook", "0");
format_add(nft, "option_is_user", "%d", oe == NULL);
if (options_next(o) == NULL)
format_add(nft, "loop_last_flag", "1");
else
format_add(nft, "loop_last_flag", "0");
format_add(nft, "loop_index", "%u", i);
format_defaults(nft, ft->c, ft->s, ft->wl, ft->wp);
format_copy_state(&next, es, 0);
next.ft = nft;
expanded = format_expand1(&next, fmt);
format_free(nft);
evbuffer_add(buffer, expanded, strlen(expanded));
free(expanded);
}
/* Add an array option item to an options loop. */
static void
format_loop_add_array_item(struct format_expand_state *es, const char *fmt,
struct evbuffer *buffer, struct options_entry *o,
struct options_array_item *a, int n, u_int i)
{
struct format_tree *ft = es->ft, *nft;
struct format_expand_state next;
const struct options_table_entry *oe = options_table_entry(o);
const char *name = options_name(o);
const char *array_key;
char *expanded, *s;
array_key = options_array_item_key(a);
format_log(es, "option loop: %s[%s]", name, array_key);
nft = format_create(ft->client, ft->item, FORMAT_NONE, ft->flags);
format_add(nft, "option_name", "%s", name);
s = options_to_string(o, array_key, 0);
format_add(nft, "option_value", "%s", s);
free(s);
format_add(nft, "option_is_array", "1");
format_add(nft, "option_array_key", "%s", array_key);
format_add(nft, "option_array_index", "%s", array_key);
if (a == options_array_first(o))
format_add(nft, "option_array_first", "1");
else
format_add(nft, "option_array_first", "0");
if (options_array_next(a) == NULL)
format_add(nft, "option_array_last", "1");
else
format_add(nft, "option_array_last", "0");
format_add(nft, "option_array_count", "%u", n);
if (oe != NULL && (oe->flags & OPTIONS_TABLE_IS_HOOK))
format_add(nft, "option_is_hook", "1");
else
format_add(nft, "option_is_hook", "0");
format_add(nft, "option_is_user", "%d", oe == NULL);
if (options_array_next(a) == NULL && options_next(o) == NULL)
format_add(nft, "loop_last_flag", "1");
else
format_add(nft, "loop_last_flag", "0");
format_add(nft, "loop_index", "%u", i);
format_defaults(nft, ft->c, ft->s, ft->wl, ft->wp);
format_copy_state(&next, es, 0);
next.ft = nft;
expanded = format_expand1(&next, fmt);
format_free(nft);
evbuffer_add(buffer, expanded, strlen(expanded));
free(expanded);
}
/* Loop over options. */
static char *
format_loop_options(struct format_expand_state *es, const char *fmt,
const char *flags)
{
struct format_tree *ft = es->ft;
struct options *oo = NULL;
struct options_entry *o;
struct options_array_item *a;
char *value;
struct evbuffer *buffer;
size_t size;
u_int i = 0, n;
int global = 0;
if (flags == NULL || *flags == '\0')
flags = "s";
if (strchr(flags, 'v') != NULL)
oo = global_options;
else {
if (strchr(flags, 'g') != NULL)
global = 1;
if (strchr(flags, 'w') != NULL) {
if (global)
oo = global_w_options;
else if (ft->w != NULL)
oo = ft->w->options;
} else if (strchr(flags, 's') != NULL) {
if (global)
oo = global_s_options;
else if (ft->s != NULL)
oo = ft->s->options;
} else if (strchr(flags, 'p') != NULL) {
if (global)
/* invalid */;
else if (ft->wp != NULL)
oo = ft->wp->options;
} else if (global)
oo = global_s_options;
}
if (oo == NULL)
return (xstrdup(""));
buffer = evbuffer_new();
if (buffer == NULL)
fatalx("out of memory");
o = options_first(oo);
while (o != NULL) {
n = 0;
if (options_is_array(o)) {
a = options_array_first(o);
while (a != NULL) {
n++;
a = options_array_next(a);
}
}
if (!options_is_array(o) || n == 0) {
format_loop_add_option(es, fmt, buffer, o, n, i);
i++;
o = options_next(o);
continue;
}
a = options_array_first(o);
while (a != NULL) {
format_loop_add_array_item(es, fmt, buffer, o, a, n, i);
i++;
a = options_array_next(a);
}
o = options_next(o);
}
if ((size = EVBUFFER_LENGTH(buffer)) != 0)
value = xmemdup(EVBUFFER_DATA(buffer), size);
else
value = xstrdup("");
evbuffer_free(buffer);
return (value);
}
/* Loop over an environment. */
static char *
format_loop_environ(struct format_expand_state *es, const char *fmt,
const char *flags)
{
struct format_tree *ft = es->ft, *nft;
struct client *c = ft->client;
struct cmdq_item *item = ft->item;
struct format_expand_state next;
struct environ *env = NULL;
struct environ_entry *envent;
char *expanded, *value;
struct evbuffer *buffer;
size_t size;
u_int i = 0;
if (flags == NULL || *flags == '\0' || strcmp(flags, "s") == 0) {
if (ft->s != NULL)
env = ft->s->environ;
} else if (strcmp(flags, "g") == 0)
env = global_environ;
else if (strcmp(flags, "c") == 0) {
if (ft->client != NULL)
env = ft->client->environ;
}
if (env == NULL)
return (xstrdup(""));
buffer = evbuffer_new();
if (buffer == NULL)
fatalx("out of memory");
envent = environ_first(env);
while (envent != NULL) {
format_log(es, "environment loop: %s", envent->name);
nft = format_create(c, item, FORMAT_NONE, ft->flags);
format_add(nft, "environ_name", "%s", envent->name);
if (envent->value == NULL)
format_add(nft, "environ_value", "%s", "");
else
format_add(nft, "environ_value", "%s", envent->value);
if (envent->flags & ENVIRON_HIDDEN)
format_add(nft, "environ_hidden", "1");
else
format_add(nft, "environ_hidden", "0");
format_add(nft, "environ_removed", "%d", envent->value == NULL);
if (environ_next(envent) == NULL)
format_add(nft, "loop_last_flag", "1");
else
format_add(nft, "loop_last_flag", "0");
format_add(nft, "loop_index", "%u", i);
format_defaults(nft, ft->c, ft->s, ft->wl, ft->wp);
format_copy_state(&next, es, 0);
next.ft = nft;
expanded = format_expand1(&next, fmt);
format_free(nft);
evbuffer_add(buffer, expanded, strlen(expanded));
free(expanded);
i++;
envent = environ_next(envent);
}
if ((size = EVBUFFER_LENGTH(buffer)) != 0)
value = xmemdup(EVBUFFER_DATA(buffer), size);
else
value = xstrdup("");
evbuffer_free(buffer);
return (value);
}
/* Loop over clients. */
static char *
format_loop_clients(struct format_expand_state *es, const char *fmt)
@@ -5014,14 +5524,16 @@ format_loop_clients(struct format_expand_state *es, const char *fmt)
c = l[i];
format_log(es, "client loop: %s", c->name);
nft = format_create(c, item, 0, ft->flags);
format_add(nft, "loop_index", "%d", i);
format_add(nft, "loop_last_flag", "%d", i == n - 1);
format_defaults(nft, c, ft->s, ft->wl, ft->wp);
format_copy_state(&next, es, 0);
next.ft = nft;
expanded = format_expand1(&next, fmt);
format_free(nft);
evbuffer_add(buffer, expanded, strlen(expanded));
free(expanded);
}
@@ -5191,12 +5703,14 @@ format_replace(struct format_expand_state *es, const char *key, size_t keylen,
char *copy0, *condition, *found, *new;
char *value, *left, *right;
size_t valuelen;
int modifiers = 0, limit = 0, width = 0;
uint64_t modifiers = 0;
int limit = 0, width = 0;
int j, c;
struct format_modifier *list, *cmp = NULL, *search = NULL;
struct format_modifier **sub = NULL, *mexp = NULL, *fm;
struct format_modifier *bool_op_n = NULL;
u_int i, count, nsub = 0, nrep, check = 0;
const char *loop_flags = "";
struct format_expand_state next;
struct environ_entry *envent;
@@ -5305,6 +5819,8 @@ format_replace(struct format_expand_state *es, const char *key, size_t keylen,
modifiers |= FORMAT_PRETTY;
else if (strchr(fm->argv[0], 'r') != NULL)
modifiers |= FORMAT_RELATIVE;
else if (strchr(fm->argv[0], 'd') != NULL)
modifiers |= FORMAT_DIFFERENCE;
else if (fm->argc >= 2 &&
strchr(fm->argv[0], 'f') != NULL) {
free(time_format);
@@ -5315,6 +5831,8 @@ format_replace(struct format_expand_state *es, const char *key, size_t keylen,
case 'q':
if (fm->argc < 1)
modifiers |= FORMAT_QUOTE_SHELL;
else if (strchr(fm->argv[0], 's') != NULL)
modifiers |= FORMAT_QUOTE_SHELL_SQ;
else if (strchr(fm->argv[0], 'e') != NULL ||
strchr(fm->argv[0], 'h') != NULL)
modifiers |= FORMAT_QUOTE_STYLE;
@@ -5381,11 +5899,27 @@ format_replace(struct format_expand_state *es, const char *key, size_t keylen,
sc->reversed = 0;
break;
}
if (strchr(fm->argv[0], 'i') != NULL)
sc->order = SORT_INDEX;
else if (strchr(fm->argv[0], 'z') != NULL)
sc->order = SORT_Z;
else
sc->order = SORT_CREATION;
if (strchr(fm->argv[0], 'r') != NULL)
sc->reversed = 1;
else
sc->reversed = 0;
break;
case 'O':
modifiers |= FORMAT_OPTIONS;
if (fm->argc == 1)
loop_flags = fm->argv[0];
break;
case 'V':
modifiers |= FORMAT_ENVIRON;
if (fm->argc == 1)
loop_flags = fm->argv[0];
break;
case 'L':
modifiers |= FORMAT_CLIENTS;
if (fm->argc < 1) {
@@ -5521,6 +6055,14 @@ format_replace(struct format_expand_state *es, const char *key, size_t keylen,
value = format_loop_clients(es, copy);
if (value == NULL)
goto fail;
} else if (modifiers & FORMAT_OPTIONS) {
value = format_loop_options(es, copy, loop_flags);
if (value == NULL)
goto fail;
} else if (modifiers & FORMAT_ENVIRON) {
value = format_loop_environ(es, copy, loop_flags);
if (value == NULL)
goto fail;
} else if (modifiers & FORMAT_WINDOW_NAME) {
value = format_window_name(es, copy);
if (value == NULL)

View File

@@ -1,4 +1,4 @@
/* $OpenBSD$ */
/* $OpenBSD: fuzzy.c,v 1.1 2026/06/26 14:40:30 nicm Exp $ */
/*
* Copyright (c) 2026 Nicholas Marriott <nicholas.marriott@gmail.com>

View File

@@ -1,4 +1,4 @@
/* $OpenBSD$ */
/* $OpenBSD: grid-reader.c,v 1.10 2026/05/17 13:12:21 nicm Exp $ */
/*
* Copyright (c) 2020 Anindya Mukherjee <anindya49@hotmail.com>

View File

@@ -1,4 +1,4 @@
/* $OpenBSD$ */
/* $OpenBSD: grid-view.c,v 1.38 2026/01/22 08:55:01 nicm Exp $ */
/*
* Copyright (c) 2008 Nicholas Marriott <nicholas.marriott@gmail.com>

35
grid.c
View File

@@ -1,4 +1,4 @@
/* $OpenBSD$ */
/* $OpenBSD: grid.c,v 1.153 2026/07/02 08:51:05 nicm Exp $ */
/*
* Copyright (c) 2008 Nicholas Marriott <nicholas.marriott@gmail.com>
@@ -60,21 +60,6 @@ static const struct grid_cell_entry grid_cleared_entry = {
};
#ifdef __APPLE__
static void
grid_check_lines(struct grid *gd)
{
u_int i, j;
for (i = 0; i < gd->hsize + gd->sy; i++) {
for (j = i + 1; j < gd->hsize + gd->sy; j++) {
if (gd->linedata[i].celldata != NULL)
assert(gd->linedata[i].celldata != gd->linedata[j].celldata);
if (gd->linedata[i].extddata != NULL)
assert(gd->linedata[i].extddata != gd->linedata[j].extddata);
}
}
}
void
grid_check_is_clear(struct grid *gd)
{
@@ -104,11 +89,6 @@ grid_check_is_clear(struct grid *gd)
}
}
#else
static void
grid_check_lines(__unused struct grid *gd)
{
}
void
grid_check_is_clear(__unused struct grid *gd)
{
@@ -386,6 +366,9 @@ grid_create(u_int sx, u_int sy, u_int hlimit)
if (gd->sy != 0)
gd->linedata = xcalloc(gd->sy, sizeof *gd->linedata);
#ifdef __APPLE__
assert(gd->hsize == 0);
#endif
grid_check_is_clear(gd);
return (gd);
}
@@ -506,8 +489,6 @@ grid_scroll_history(struct grid *gd, u_int bg)
gd->linedata[gd->hsize].time = current_time;
gd->hsize++;
gd->scroll_added++;
grid_check_lines(gd);
}
/* Clear the history. */
@@ -557,8 +538,6 @@ grid_scroll_history_region(struct grid *gd, u_int upper, u_int lower, u_int bg)
gd->hscrolled++;
gd->hsize++;
gd->scroll_added++;
grid_check_lines(gd);
}
/* Expand line to fit to cell. */
@@ -820,8 +799,6 @@ grid_move_lines(struct grid *gd, u_int dy, u_int py, u_int ny, u_int bg)
}
if (py != 0 && (py < dy || py >= dy + ny))
gd->linedata[py - 1].flags &= ~GRID_LINE_WRAPPED;
grid_check_lines(gd);
}
/* Move a group of cells. */
@@ -1311,8 +1288,6 @@ grid_duplicate_lines(struct grid *dst, u_int dy, struct grid *src, u_int sy,
sy++;
dy++;
}
grid_check_lines(dst);
}
/* Mark line as dead. */
@@ -1609,8 +1584,6 @@ grid_reflow(struct grid *gd, u_int sx)
gd->linedata = target->linedata;
free(target);
gd->scroll_generation++;
grid_check_lines(gd);
}
/* Convert to position based on wrapped lines. */

458
hooks.c Normal file
View File

@@ -0,0 +1,458 @@
/* $OpenBSD: hooks.c,v 1.13 2026/07/10 15:20:06 nicm Exp $ */
/*
* Copyright (c) 2026 Nicholas Marriott <nicholas.marriott@gmail.com>
* Copyright (c) 2012 George Nachman <tmux@georgester.com>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER
* IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
* OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include <sys/types.h>
#include <stdlib.h>
#include <string.h>
#include "tmux.h"
/* Hook monitor state owned by an option entry. */
struct hook_monitor {
struct options *oo;
struct monitor_set *set;
struct events_sink *sink;
struct cmd_find_state fs;
enum monitor_type type;
int id;
char *format;
};
/* Hook command data built from an event payload. */
struct hooks_data {
const char *name;
struct cmd_find_state fs;
struct format_tree *formats;
struct options *oo;
struct client *client;
int expand;
};
/* Hook event sink registered for a notify event name. */
struct hooks_event {
char *name;
struct events_sink *sink;
TAILQ_ENTRY(hooks_event) entry;
};
TAILQ_HEAD(hooks_events, hooks_event);
static struct hooks_events hooks_events = TAILQ_HEAD_INITIALIZER(hooks_events);
/* Insert one hook command list. */
static struct cmdq_item *
hooks_insert_one(struct cmdq_item *item, struct hooks_data *hd,
struct cmd_parse_tree *tree, struct cmdq_state *state)
{
struct cmdq_item *new_item;
char *s;
if (tree == NULL)
return (item);
if (log_get_level() != 0) {
s = cmd_parse_print(tree, 0);
log_debug("%s: hook %s is: %s", __func__, hd->name, s);
free(s);
}
new_item = cmd_invoke_get(tree, state, NULL);
if (item != NULL)
return (cmdq_insert_after(item, new_item));
return (cmdq_append(NULL, new_item));
}
/* Parse a hook command. */
static struct cmd_parse_tree *
hooks_parse(struct hooks_data *hd, struct cmd_find_state *fs,
const char *value, char **cause)
{
struct cmd_parse_tree *tree;
struct format_tree *ft;
char *expanded;
if (!hd->expand)
return (cmd_parse_from_string(value, NULL, cause));
ft = format_create_defaults(NULL, hd->client, fs->s, fs->wl, fs->wp);
if (hd->formats != NULL)
format_merge(ft, hd->formats);
expanded = format_expand(ft, value);
format_free(ft);
tree = cmd_parse_from_string(expanded, NULL, cause);
free(expanded);
return (tree);
}
/* Insert commands for a hook. */
static void
hooks_insert(struct cmdq_item *item, struct hooks_data *hd)
{
struct cmd_find_state fs;
struct options *oo;
struct cmdq_state *state;
struct options_entry *o;
struct options_array_item *a;
struct cmd_parse_tree *tree;
const char *value;
char *cause = NULL;
log_debug("%s: inserting hook %s", __func__, hd->name);
cmd_find_clear_state(&fs, 0);
if (cmd_find_empty_state(&hd->fs) || !cmd_find_valid_state(&hd->fs))
cmd_find_from_nothing(&fs, 0);
else
cmd_find_copy_state(&fs, &hd->fs);
if (hd->oo != NULL) {
oo = hd->oo;
o = options_get_only(oo, hd->name);
} else {
if (fs.s == NULL)
oo = global_s_options;
else
oo = fs.s->options;
o = options_get(oo, hd->name);
if (o == NULL && fs.wp != NULL) {
oo = fs.wp->options;
o = options_get(oo, hd->name);
}
if (o == NULL && fs.wl != NULL) {
oo = fs.wl->window->options;
o = options_get(oo, hd->name);
}
}
if (o == NULL) {
log_debug("%s: hook %s not found", __func__, hd->name);
return;
}
if (item == NULL)
state = cmdq_new_state(&fs, NULL, CMDQ_STATE_NOHOOKS);
else {
state = cmdq_new_state(&fs, cmdq_get_event(item),
CMDQ_STATE_NOHOOKS);
}
cmdq_add_formats(state, hd->formats);
if (*hd->name == '@') {
value = options_get_string(oo, hd->name);
cause = NULL;
tree = hooks_parse(hd, &fs, value, &cause);
if (tree == NULL) {
log_debug("%s: can't parse hook %s: %s", __func__,
hd->name, cause);
free(cause);
} else {
hooks_insert_one(item, hd, tree, state);
cmd_parse_free(tree);
}
} else {
a = options_array_first(o);
while (a != NULL) {
if (hd->expand) {
value = options_array_item_value(a)->string;
cause = NULL;
tree = hooks_parse(hd, &fs, value, &cause);
if (tree == NULL) {
if (cause != NULL)
cmdq_error(item, "%s", cause);
free(cause);
} else {
item = hooks_insert_one(item, hd, tree,
state);
cmd_parse_free(tree);
}
} else {
tree = options_array_item_value(a)->cmd;
item = hooks_insert_one(item, hd, tree,
state);
}
a = options_array_next(a);
}
}
cmdq_free_state(state);
}
/* Insert commands for a hook event. */
static void
hooks_insert_event(struct cmdq_item *item, const char *name,
struct event_payload *ep, struct options *oo, int expand)
{
struct hooks_data hd;
struct format_tree *ft;
struct client *c;
if (item != NULL && (cmdq_get_flags(item) & CMDQ_STATE_NOHOOKS))
return;
c = event_payload_get_client(ep, "client");
ft = format_create(c, item, FORMAT_NONE, FORMAT_NOJOBS);
event_payload_add_formats(ep, ft, "hook_");
format_add(ft, "hook", "%s", name);
format_log_debug(ft, __func__);
memset(&hd, 0, sizeof hd);
hd.name = name;
cmd_find_clear_state(&hd.fs, 0);
event_payload_get_target(ep, &hd.fs);
hd.formats = ft;
hd.oo = oo;
hd.client = c;
hd.expand = expand;
hooks_insert(item, &hd);
format_free(ft);
}
/* Handle an event for hooks. */
static void
hooks_event_cb(const char *name, struct event_payload *ep,
__unused void *sink_data)
{
struct cmdq_item *item;
if (event_payload_get_pointer(ep, "_hook_monitor") != NULL)
return;
item = event_payload_get_pointer(ep, "_cmdq_item");
if (item != NULL) {
hooks_insert_event(item, name, ep, NULL, 0);
return;
}
item = cmdq_running(NULL);
if (item == NULL || (~cmdq_get_flags(item) & CMDQ_STATE_NOHOOKS))
hooks_insert_event(NULL, name, ep, NULL, 0);
}
/* Add a hook event sink. */
void
hooks_add_event(const char *name)
{
struct hooks_event *he;
TAILQ_FOREACH(he, &hooks_events, entry) {
if (strcmp(he->name, name) == 0)
return;
}
he = xcalloc(1, sizeof *he);
he->name = xstrdup(name);
he->sink = events_add_sink(name, hooks_event_cb, NULL);
TAILQ_INSERT_TAIL(&hooks_events, he, entry);
}
/* Return if an event name can be fired through the hooks path. */
int
hooks_valid_event_name(const char *name)
{
const struct options_table_entry *oe;
if (*name == '@')
return (1);
oe = options_search(name);
return (oe != NULL && (oe->flags & OPTIONS_TABLE_IS_HOOK));
}
/* Add hook event sinks for all built-in hooks. */
void
hooks_build_events(void)
{
const struct options_table_entry *oe;
for (oe = options_table; oe->name != NULL; oe++) {
if (oe->flags & OPTIONS_TABLE_IS_HOOK)
hooks_add_event(oe->name);
}
}
/* Run a hook immediately. */
void
hooks_run(struct cmdq_item *item, const char *name)
{
struct cmd_find_state *target = cmdq_get_target(item);
struct hooks_data hd = { 0 };
hd.name = name;
cmd_find_copy_state(&hd.fs, target);
hd.client = cmdq_get_client(item);
hd.formats = format_create(NULL, NULL, 0, FORMAT_NOJOBS);
format_add(hd.formats, "hook", "%s", name);
format_log_debug(hd.formats, __func__);
hooks_insert(item, &hd);
format_free(hd.formats);
}
/* Free a hook monitor. */
void
hooks_monitor_free(void *data)
{
struct hook_monitor *hm = data;
events_remove_sink(hm->sink);
monitor_destroy(hm->set);
free(hm->format);
free(hm);
}
/* Remove a hook monitor. */
void
hooks_monitor_remove(struct options *oo, const char *name)
{
struct options_entry *o;
struct hook_monitor *hm;
o = options_get_only(oo, name);
if (o == NULL)
return;
hm = options_get_monitor_data(o);
if (hm != NULL) {
options_set_monitor_data(o, NULL);
hooks_monitor_free(hm);
}
}
/* Handle a hook monitor event. */
static void
hooks_monitor_hook_cb(const char *name, struct event_payload *ep,
void *sink_data)
{
struct hook_monitor *hm = sink_data;
if (event_payload_get_pointer(ep, "_hook_monitor") == hm)
hooks_insert_event(cmdq_running(NULL), name, ep, hm->oo, 1);
}
/* Fire a hook monitor event. */
static void
hooks_monitor_cb(struct monitor_change *change, void *data)
{
struct hook_monitor *hm = data;
struct event_payload *ep;
struct winlink *wl = change->wl;
struct window_pane *wp = change->wp;
struct cmd_find_state fs;
ep = event_payload_create();
event_payload_set_pointer(ep, "_hook_monitor", data, NULL, NULL);
cmd_find_clear_state(&fs, 0);
if (wl != NULL && wp != NULL && wp->window == wl->window)
cmd_find_from_winlink_pane(&fs, wl, wp, 0);
else if (wl != NULL)
cmd_find_from_winlink(&fs, wl, 0);
else if (wp != NULL)
cmd_find_from_pane(&fs, wp, 0);
else if (change->s != NULL)
cmd_find_from_session(&fs, change->s, 0);
else
cmd_find_copy_state(&fs, &hm->fs);
event_payload_set_target(ep, &fs);
if (change->value != NULL)
event_payload_set_string(ep, "value", "%s", change->value);
else
event_payload_set_string(ep, "value", "%s", "");
if (change->last != NULL)
event_payload_set_string(ep, "last", "%s", change->last);
else
event_payload_set_string(ep, "last", "%s", "");
if (change->c != NULL)
event_payload_set_client(ep, "client", change->c);
if (change->s != NULL)
event_payload_set_session(ep, "session", change->s);
if (wl != NULL) {
if (change->s == NULL)
event_payload_set_session(ep, "session",
wl->session);
event_payload_set_window(ep, "window", wl->window);
event_payload_set_int(ep, "window_index", wl->idx);
}
if (wp != NULL) {
event_payload_set_pane(ep, "pane", wp);
if (wl == NULL)
event_payload_set_window(ep, "window", wp->window);
}
events_fire(change->name, ep);
}
/* Add a hook monitor. */
void
hooks_monitor_add(__unused struct cmdq_item *item, struct options *oo,
const char *name, enum monitor_type type, int id, const char *format,
int flags, struct cmd_find_state *fs, struct session *s)
{
struct options_entry *o;
struct hook_monitor *hm;
hooks_monitor_remove(oo, name);
o = options_get_only(oo, name);
if (o == NULL)
o = options_set_string(oo, name, 0, "%s", "");
hm = xcalloc(1, sizeof *hm);
hm->oo = oo;
cmd_find_copy_state(&hm->fs, fs);
hm->type = type;
hm->id = id;
hm->format = xstrdup(format);
hm->set = monitor_create_session(s, hooks_monitor_cb, hm);
hm->sink = events_add_sink(name, hooks_monitor_hook_cb, hm);
options_set_monitor_data(o, hm);
monitor_add(hm->set, name, type, id, format, flags);
}
/* Convert a hook monitor to its value. */
char *
hooks_monitor_to_string(struct options_entry *o)
{
struct hook_monitor *hm = options_get_monitor_data(o);
const char *name = options_name(o);
char *s;
if (hm == NULL)
return (NULL);
switch (hm->type) {
case MONITOR_SESSION:
xasprintf(&s, "%s::%s", name, hm->format);
break;
case MONITOR_PANE:
xasprintf(&s, "%s:%%%d:%s", name, hm->id, hm->format);
break;
case MONITOR_ALL_PANES:
xasprintf(&s, "%s:%%*:%s", name, hm->format);
break;
case MONITOR_WINDOW:
xasprintf(&s, "%s:@%d:%s", name, hm->id, hm->format);
break;
case MONITOR_ALL_WINDOWS:
xasprintf(&s, "%s:@*:%s", name, hm->format);
break;
}
return (s);
}

View File

@@ -1,4 +1,4 @@
/* $OpenBSD$ */
/* $OpenBSD: hyperlinks.c,v 1.5 2026/06/29 16:44:06 nicm Exp $ */
/*
* Copyright (c) 2021 Will <author@will.party>

View File

@@ -1,4 +1,4 @@
/* $OpenBSD$ */
/* $OpenBSD: input-keys.c,v 1.114 2026/06/15 21:47:01 nicm Exp $ */
/*
* Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>

114
input.c
View File

@@ -1,4 +1,4 @@
/* $OpenBSD$ */
/* $OpenBSD: input.c,v 1.266 2026/07/10 15:20:06 nicm Exp $ */
/*
* Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>
@@ -174,6 +174,8 @@ static void input_osc_110(struct input_ctx *, const char *);
static void input_osc_111(struct input_ctx *, const char *);
static void input_osc_112(struct input_ctx *, const char *);
static void input_osc_133(struct input_ctx *, const char *);
static void input_fire_pane_title_changed(struct window_pane *,
const char *);
/* Transition entry/exit handlers. */
static void input_clear(struct input_ctx *);
@@ -210,6 +212,21 @@ static int input_end_bel(struct input_ctx *);
/* Command table comparison function. */
static int input_table_compare(const void *, const void *);
static void
input_fire_pane_title_changed(struct window_pane *wp, const char *title)
{
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", wp->window);
event_payload_set_string(ep, "new_title", "%s", title);
events_fire("pane-title-changed", ep);
}
/* Command table entry. */
struct input_table_entry {
int ch;
@@ -1017,6 +1034,8 @@ input_parse_pane(struct window_pane *wp)
size_t new_size;
new_data = window_pane_get_new_data(wp, &wp->offset, &new_size);
if (new_size != 0)
wp->last_output_time = time(NULL);
input_parse_buffer(wp, new_data, new_size);
window_pane_update_used_data(wp, &wp->offset, new_size);
}
@@ -2180,7 +2199,7 @@ input_csi_dispatch_winops(struct input_ctx *ictx)
screen_pop_title(sctx->s);
if (wp == NULL)
break;
notify_pane("pane-title-changed", wp);
events_fire_pane("pane-title-changed", wp);
server_redraw_window_borders(w);
server_status_window(w);
break;
@@ -2700,7 +2719,7 @@ input_exit_osc(struct input_ctx *ictx)
if (wp != NULL &&
options_get_number(wp->options, "allow-set-title") &&
screen_set_title(sctx->s, p, 1)) {
notify_pane("pane-title-changed", wp);
input_fire_pane_title_changed(wp, p);
server_redraw_window_borders(wp->window);
server_status_window(wp->window);
}
@@ -2778,7 +2797,7 @@ input_exit_apc(struct input_ctx *ictx)
if (wp != NULL &&
options_get_number(wp->options, "allow-set-title") &&
screen_set_title(sctx->s, ictx->input_buf, 1)) {
notify_pane("pane-title-changed", wp);
input_fire_pane_title_changed(wp, ictx->input_buf);
server_redraw_window_borders(wp->window);
server_status_window(wp->window);
}
@@ -2818,7 +2837,7 @@ input_exit_rename(struct input_ctx *ictx)
if (ictx->input_len == 0) {
o = options_get_only(w->options, "automatic-rename");
if (o != NULL)
options_remove_or_default(o, -1, NULL);
options_remove_or_default(o, NULL, NULL);
if (!options_get_number(w->options, "automatic-rename"))
window_set_name(w, "", 1);
} else {
@@ -3146,7 +3165,8 @@ input_osc_12(struct input_ctx *ictx, const char *p)
c = ictx->ctx.s->ccolour;
if (c == -1)
c = ictx->ctx.s->default_ccolour;
input_osc_colour_reply(ictx, 1, 12, 0, c, ictx->input_end);
input_osc_colour_reply(ictx, 1, 12, 0, c,
ictx->input_end);
}
return;
}
@@ -3166,24 +3186,92 @@ input_osc_112(struct input_ctx *ictx, const char *p)
screen_set_cursor_colour(ictx->ctx.s, -1);
}
/* Fire an OSC 133 command event. */
static void
input_fire_command_event(struct window_pane *wp, const char *name)
{
struct event_payload *ep;
struct cmd_find_state fs;
time_t tstart = wp->cmd_start_time, end;
time_t tend = wp->cmd_end_time;
ep = event_payload_create();
cmd_find_from_pane(&fs, wp, 0);
event_payload_set_target(ep, &fs);
if (fs.s != NULL)
event_payload_set_session(ep, "session", fs.s);
if (fs.wl != NULL)
event_payload_set_int(ep, "window_index", fs.wl->idx);
event_payload_set_window(ep, "window", wp->window);
event_payload_set_pane(ep, "pane", wp);
if (wp->cmd_status != -1)
event_payload_set_int(ep, "command_status", wp->cmd_status);
if (tstart != 0)
event_payload_set_time(ep, "command_start_time", tstart);
if (tend != 0)
event_payload_set_time(ep, "command_end_time", tend);
if (tstart != 0) {
if (wp->flags & PANE_CMDRUNNING)
end = time(NULL);
else
end = tend;
if (end < tstart)
end = tstart;
end -= tstart;
event_payload_set_uint(ep, "command_duration", end);
}
events_fire(name, ep);
}
/* Handle the OSC 133 sequence. */
static void
input_osc_133(struct input_ctx *ictx, const char *p)
{
struct window_pane *wp = ictx->wp;
struct grid *gd = ictx->ctx.s->grid;
u_int line = ictx->ctx.s->cy + gd->hsize;
struct grid_line *gl;
struct grid_line *gl = NULL;
const char *errstr;
int status;
if (line > gd->hsize + gd->sy - 1)
return;
gl = grid_get_line(gd, line);
if (line <= gd->hsize + gd->sy - 1)
gl = grid_get_line(gd, line);
switch (*p) {
case 'A':
gl->flags |= GRID_LINE_START_PROMPT;
if (gl != NULL)
gl->flags |= GRID_LINE_START_PROMPT;
if (wp != NULL) {
wp->last_prompt_time = time(NULL);
events_fire_pane("pane-shell-prompt", wp);
}
break;
case 'C':
gl->flags |= GRID_LINE_START_OUTPUT;
if (gl != NULL)
gl->flags |= GRID_LINE_START_OUTPUT;
if (wp != NULL) {
wp->cmd_start_time = time(NULL);
wp->cmd_end_time = 0;
wp->flags |= PANE_CMDRUNNING;
wp->cmd_status = -1;
input_fire_command_event(wp, "pane-command-started");
}
break;
case 'D':
if (wp != NULL) {
wp->cmd_end_time = time(NULL);
wp->flags &= ~PANE_CMDRUNNING;
wp->cmd_status = -1;
if (p[1] == ';' && p[2] != '\0') {
status = strtonum(p + 2, 0, INT_MAX, &errstr);
if (errstr == NULL)
wp->cmd_status = status;
}
input_fire_command_event(wp, "pane-command-finished");
}
break;
}
}
@@ -3289,7 +3377,7 @@ input_osc_52(struct input_ctx *ictx, const char *p)
screen_write_start_pane(&ctx, wp, NULL);
screen_write_setselection(&ctx, clip, out, outlen);
screen_write_stop(&ctx);
notify_pane("pane-set-clipboard", wp);
events_fire_pane("pane-set-clipboard", wp);
paste_add(NULL, out, outlen);
}
}

18
job.c
View File

@@ -1,4 +1,4 @@
/* $OpenBSD$ */
/* $OpenBSD: job.c,v 1.75 2026/07/12 20:35:52 nicm Exp $ */
/*
* Copyright (c) 2009 Nicholas Marriott <nicholas.marriott@gmail.com>
@@ -147,7 +147,7 @@ job_run(const char *cmd, int argc, char **argv, struct environ *e,
else if (chdir("/") == 0)
environ_set(env, "PWD", 0, "/");
else
fatal("chdir failed");
_exit(1);
}
environ_push(env);
@@ -155,21 +155,21 @@ job_run(const char *cmd, int argc, char **argv, struct environ *e,
if (~flags & JOB_PTY) {
if (dup2(out[1], STDIN_FILENO) == -1)
fatal("dup2 failed");
_exit(1);
do_close = do_close && out[1] != STDIN_FILENO;
if (dup2(out[1], STDOUT_FILENO) == -1)
fatal("dup2 failed");
_exit(1);
do_close = do_close && out[1] != STDOUT_FILENO;
if (flags & JOB_SHOWSTDERR) {
if (dup2(out[1], STDERR_FILENO) == -1)
fatal("dup2 failed");
_exit(1);
do_close = do_close && out[1] != STDERR_FILENO;
} else {
nullfd = open(_PATH_DEVNULL, O_RDWR);
if (nullfd == -1)
fatal("open failed");
_exit(1);
if (dup2(nullfd, STDERR_FILENO) == -1)
fatal("dup2 failed");
_exit(1);
if (nullfd != STDERR_FILENO)
close(nullfd);
}
@@ -183,11 +183,11 @@ job_run(const char *cmd, int argc, char **argv, struct environ *e,
if (flags & JOB_DEFAULTSHELL)
setenv("SHELL", shell, 1);
execl(shell, argv0, "-c", cmd, (char *)NULL);
fatal("execl failed");
_exit(1);
} else {
argvp = cmd_copy_argv(argc, argv);
execvp(argvp[0], argvp);
fatal("execvp failed");
_exit(1);
}
}

View File

@@ -1,4 +1,4 @@
/* $OpenBSD$ */
/* $OpenBSD: key-bindings.c,v 1.186 2026/07/07 12:30:36 nicm Exp $ */
/*
* Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>
@@ -59,17 +59,45 @@
" '#{?mouse_hyperlink,Type #[underscore]#{=/9/...:mouse_hyperlink},}' 'C-h' {copy-mode -q; send-keys -l -- \"#{q:mouse_hyperlink}\"}" \
" '#{?mouse_hyperlink,Copy #[underscore]#{=/9/...:mouse_hyperlink},}' 'h' {copy-mode -q; set-buffer -- \"#{q:mouse_hyperlink}\"}" \
" ''" \
" '#{?#{#{pane_floating_flag}},Move,}' '' {display-menu -xL -yL -T '#[align=centre]Move' " DEFAULT_MOVE_MENU " }" \
" '#{?#{#{pane_floating_flag}},Move & Resize,}' '' {display-menu -xL -yL -T '#[align=centre]Move & Resize' " DEFAULT_MOVE_RESIZE_MENU " }" \
" '#{?#{#{pane_floating_flag}},Tile,}' 't' { join-pane }" \
" '#{?#{!:#{pane_floating_flag}},Float,}' 'f' { break-pane -W }" \
" '#{?#{!:#{pane_floating_flag}},Horizontal Split,}' 'h' {split-window -h}" \
" '#{?#{!:#{pane_floating_flag}},Vertical Split,}' 'v' {split-window -v}" \
" ''" \
" '#{?#{&&:#{!:#{pane_floating_flag}},#{>:#{window_panes},1}},Swap Up,}' 'u' {swap-pane -U}" \
" '#{?#{&&:#{!:#{pane_floating_flag}},#{>:#{window_panes},1}},Swap Down,}' 'd' {swap-pane -D}" \
" '#{?#{!:#{pane_floating_flag}},#{?pane_marked_set,,-}Swap Marked,}' 's' {swap-pane}" \
" '#{?pane_marked_set,,-}Swap Marked' 's' {swap-pane}" \
" ''" \
" 'Kill' 'X' {kill-pane}" \
" 'Respawn' 'R' {respawn-pane -k}" \
" '#{?pane_marked,Unmark,Mark}' 'm' {select-pane -m}" \
" '#{?#{>:#{window_panes},1},,-}#{?window_zoomed_flag,Unzoom,Zoom}' 'z' {resize-pane -Z}"
#define DEFAULT_MOVE_MENU \
" 'Centre' 'c' {move-pane -P centre}" \
" ''" \
" 'Top Left' '1' {move-pane -P top-left}" \
" 'Top Right' '2' {move-pane -P top-right}" \
" 'Bottom Left' '3' {move-pane -P bottom-left}" \
" 'Bottom Right' '4' {move-pane -P bottom-right}" \
" ''" \
" 'Top' 't' {move-pane -P top-centre}" \
" 'Bottom' 'b' {move-pane -P bottom-centre}" \
" 'Left' 'l' {move-pane -P centre-left}" \
" 'Right' 'r' {move-pane -P centre-right}"
#define DEFAULT_MOVE_RESIZE_MENU \
" 'Fill' '0' {resize-pane -x100% -y100%; move-pane -P top-left}" \
" ''" \
" 'Top Left' '1' {resize-pane -x50% -y50%; move-pane -P top-left}" \
" 'Top Right' '2' {resize-pane -x50% -y50%; move-pane -P top-right}" \
" 'Bottom Left' '3' {resize-pane -x50% -y50%; move-pane -P bottom-left}" \
" 'Bottom Right' '4' {resize-pane -x50% -y50%; move-pane -P bottom-right}" \
" ''" \
" 'Top' 't' {resize-pane -x100% -y50%; move-pane -P top-centre}" \
" 'Bottom' 'b' {resize-pane -x100% -y50%; move-pane -P bottom-centre}" \
" 'Left' 'l' {resize-pane -x50% -y100%; move-pane -P centre-left}" \
" 'Right' 'r' {resize-pane -x50% -y100%; move-pane -P centre-right}"
static int key_bindings_cmp(struct key_binding *, struct key_binding *);
RB_GENERATE_STATIC(key_bindings, key_binding, entry, key_bindings_cmp);
@@ -358,13 +386,14 @@ key_bindings_init(void)
"bind -N 'Rename current session' '$' { command-prompt -I'#S' { rename-session -- '%%' } }",
"bind -N 'Split window horizontally' % { split-window -h }",
"bind -N 'Kill current window' & { confirm-before -p\"kill-window #W? (y/n)\" kill-window }",
"bind -N 'Prompt for window index to select' \"'\" { command-prompt -T window-target -pindex { select-window -t ':%%' } }",
"bind -N 'Prompt for window index to select' \"'\" { command-prompt -pindex { select-window -t ':%%' } }",
"bind -N 'New floating pane' * { new-pane }",
"bind -N 'Toggle pane between floating and tiled' @ { if -F '#{pane_floating_flag}' { join-pane } { break-pane -W } }",
"bind -N 'Switch to previous client' ( { switch-client -p }",
"bind -N 'Switch to next client' ) { switch-client -n }",
"bind -N 'Rename current window' , { command-prompt -I'#W' { rename-window -- '%%' } }",
"bind -N 'Delete the most recent paste buffer' - { delete-buffer }",
"bind -N 'Move the current window' . { command-prompt -T target { move-window -t '%%' } }",
"bind -N 'Move the current window' . { command-prompt { move-window -t '%%' } }",
"bind -N 'Describe key binding' '/' { command-prompt -kpkey { list-keys -1N '%%' } }",
"bind -N 'Select window 0' 0 { select-window -t:=0 }",
"bind -N 'Select window 1' 1 { select-window -t:=1 }",
@@ -405,10 +434,8 @@ key_bindings_init(void)
"bind -N 'Choose a window from a list' w { choose-tree -Zw }",
"bind -N 'Kill the active pane' x { confirm-before -p\"kill-pane #P? (y/n)\" kill-pane }",
"bind -N 'Zoom the active pane' z { resize-pane -Z }",
"bind -N 'Move pane to top-left corner' '{' { resize-pane -x50% -y50%; move-pane -P top-left }",
"bind -N 'Move pane to top-right corner' '}' { resize-pane -x50% -y50%; move-pane -P top-right }",
"bind -N 'Move pane to bottom-left corner' 'M-{' { resize-pane -x50% -y50%; move-pane -P bottom-left }",
"bind -N 'Move pane to bottom-right corner' 'M-}' { resize-pane -x50% -y50%; move-pane -P bottom-right }",
"bind -N 'Swap the active pane with the pane above' '{' { swap-pane -U }",
"bind -N 'Swap the active pane with the pane below' '}' { swap-pane -D }",
"bind -N 'Show messages' '~' { show-messages }",
"bind -N 'Enter copy mode and scroll up' PPage { copy-mode -u }",
"bind -N 'Select the pane above the active pane' -r Up { select-pane -U }",
@@ -439,6 +466,28 @@ key_bindings_init(void)
"bind -N 'Resize the pane left' -r C-Left if -F '#{?floating_pane_flag}' { resizep -R-1 } { resize-pane -L }",
"bind -N 'Resize the pane right' -r C-Right { resize-pane -R }",
/* Floating pane movement. */
"bind -N 'Move a floating pane' g { switch-client -Tmove }",
"bind -Tmove -N 'Move pane to top-left corner' 1 { move-pane -P top-left }",
"bind -Tmove -N 'Move pane to top-right corner' 2 { move-pane -P top-right }",
"bind -Tmove -N 'Move pane to bottom-left corner' 3 { move-pane -P bottom-left }",
"bind -Tmove -N 'Move pane to bottom-right corner' 4 { move-pane -P bottom-right }",
"bind -Tmove -N 'Move pane to top-left corner and resize' M-1 { resize-pane -x50% -y50%; move-pane -P top-left }",
"bind -Tmove -N 'Move pane to top-right corner and resize' M-2 { resize-pane -x50% -y50%; move-pane -P top-right }",
"bind -Tmove -N 'Move pane to bottom-left corner and resize' M-3 { resize-pane -x50% -y50%; move-pane -P bottom-left }",
"bind -Tmove -N 'Move pane to bottom-right corner and resize' M-4 { resize-pane -x50% -y50%; move-pane -P bottom-right }",
"bind -Tmove -N 'Move pane to top' 'Up' { move-pane -P top-centre }",
"bind -Tmove -N 'Move pane to bottom' 'Down' { move-pane -P bottom-centre }",
"bind -Tmove -N 'Move pane to left' 'Left' { move-pane -P centre-left }",
"bind -Tmove -N 'Move pane to right' 'Right' { move-pane -P centre-right }",
"bind -Tmove -N 'Move pane to top and resize' 'M-Up' { resizep -x100% -y50%; move-pane -P top-centre }",
"bind -Tmove -N 'Move pane to bottom and resize' 'M-Down' { resizep -x100% -y50%; move-pane -P bottom-centre }",
"bind -Tmove -N 'Move pane to left and resize' 'M-Left' { resizep -x50% -y100%; move-pane -P centre-left }",
"bind -Tmove -N 'Move pane to right and resize' 'M-Right' { resizep -x50% -y100%; move-pane -P centre-right }",
"bind -Tmove -N 'Move pane to fill the window' 0 { resize-pane -x100% -y100%; move-pane -P top-left }",
"bind -Tmove -N 'Display move menu' , { if -F '#{pane_floating_flag}' { display-menu -xP -yP -T '#[align=centre]Move' " DEFAULT_MOVE_MENU " } }",
"bind -Tmove -N 'Display move and resize menu' . { if -F '#{pane_floating_flag}' { display-menu -xP -yP -T '#[align=centre]Move & Resize' " DEFAULT_MOVE_RESIZE_MENU " } }",
/* Menu keys */
"bind -N 'Display window menu' < { display-menu -xW -yW -T '#[align=centre]#{window_index}:#{window_name}' " DEFAULT_WINDOW_MENU " }",
"bind -N 'Display pane menu' > { display-menu -xP -yP -T '#[align=centre]#{pane_index} (#{pane_id})' " DEFAULT_PANE_MENU " }",
@@ -477,6 +526,7 @@ key_bindings_init(void)
/* Mouse button 1 down on default pane-border-format */
"bind -n MouseDown1Control9 { display-menu -t= -xM -yM -O -T 'Kill pane #{pane_index}?' 'Yes' 'y' { kill-pane -t= } 'No' 'n' {}}",
"bind -n MouseDown1Control8 { resize-pane -Z }",
"bind -n MouseDown1Control7 { if -Ft= '#{pane_floating_flag}' { join-pane } { break-pane -W } }",
/* Mouse wheel down on status line. */
"bind -n WheelDownStatus { next-window }",

View File

@@ -1,4 +1,4 @@
/* $OpenBSD$ */
/* $OpenBSD: key-string.c,v 1.77 2026/03/31 11:46:43 nicm Exp $ */
/*
* Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>

View File

@@ -1,4 +1,4 @@
/* $OpenBSD$ */
/* $OpenBSD: layout-custom.c,v 1.37 2026/07/10 13:38:45 nicm Exp $ */
/*
* Copyright (c) 2010 Nicholas Marriott <nicholas.marriott@gmail.com>
@@ -101,10 +101,10 @@ layout_append(struct layout_cell *lc, char *buf, size_t len)
return (0);
if (lc->wp != NULL) {
tmplen = xsnprintf(tmp, sizeof tmp, "%ux%u,%d,%d,%u",
lc->sx, lc->sy, lc->xoff, lc->yoff, lc->wp->id);
lc->g.sx, lc->g.sy, lc->g.xoff, lc->g.yoff, lc->wp->id);
} else {
tmplen = xsnprintf(tmp, sizeof tmp, "%ux%u,%d,%d",
lc->sx, lc->sy, lc->xoff, lc->yoff);
lc->g.sx, lc->g.sy, lc->g.xoff, lc->g.yoff);
}
if (tmplen > (sizeof tmp) - 1)
return (-1);
@@ -145,24 +145,24 @@ layout_check(struct layout_cell *lc)
break;
case LAYOUT_LEFTRIGHT:
TAILQ_FOREACH(lcchild, &lc->cells, entry) {
if (lcchild->sy != lc->sy)
if (lcchild->g.sy != lc->g.sy)
return (0);
if (!layout_check(lcchild))
return (0);
n += lcchild->sx + 1;
n += lcchild->g.sx + 1;
}
if (n - 1 != lc->sx)
if (n - 1 != lc->g.sx)
return (0);
break;
case LAYOUT_TOPBOTTOM:
TAILQ_FOREACH(lcchild, &lc->cells, entry) {
if (lcchild->sx != lc->sx)
if (lcchild->g.sx != lc->g.sx)
return (0);
if (!layout_check(lcchild))
return (0);
n += lcchild->sy + 1;
n += lcchild->g.sy + 1;
}
if (n - 1 != lc->sy)
if (n - 1 != lc->g.sy)
return (0);
break;
}
@@ -237,21 +237,21 @@ layout_parse(struct window *w, const char *layout, char **cause)
break;
case LAYOUT_LEFTRIGHT:
TAILQ_FOREACH(lcchild, &tiled_lc->cells, entry) {
sy = lcchild->sy + 1;
sx += lcchild->sx + 1;
sy = lcchild->g.sy + 1;
sx += lcchild->g.sx + 1;
}
break;
case LAYOUT_TOPBOTTOM:
TAILQ_FOREACH(lcchild, &tiled_lc->cells, entry) {
sx = lcchild->sx + 1;
sy += lcchild->sy + 1;
sx = lcchild->g.sx + 1;
sy += lcchild->g.sy + 1;
}
break;
}
if (tiled_lc->type != LAYOUT_WINDOWPANE &&
(tiled_lc->sx != sx || tiled_lc->sy != sy)) {
(tiled_lc->g.sx != sx || tiled_lc->g.sy != sy)) {
layout_print_cell(tiled_lc, __func__, 0);
tiled_lc->sx = sx - 1; tiled_lc->sy = sy - 1;
tiled_lc->g.sx = sx - 1; tiled_lc->g.sy = sy - 1;
}
/* Check the new layout. */
@@ -262,7 +262,7 @@ layout_parse(struct window *w, const char *layout, char **cause)
/* Resize window to the layout size. */
if (sx != 0 && sy != 0)
window_resize(w, tiled_lc->sx, tiled_lc->sy, -1, -1);
window_resize(w, tiled_lc->g.sx, tiled_lc->g.sy, -1, -1);
/* Destroy the old layout and swap to the new. */
layout_free_cell(w->layout_root, 0);
@@ -286,7 +286,7 @@ layout_parse(struct window *w, const char *layout, char **cause)
recalculate_sizes();
layout_print_cell(tiled_lc, __func__, 0);
notify_window("window-layout-changed", w);
events_fire_window("window-layout-changed", w);
return (0);
@@ -358,10 +358,10 @@ layout_construct_cell(struct layout_cell *lcparent, const char **layout)
}
lc = layout_create_cell(lcparent);
lc->sx = sx;
lc->sy = sy;
lc->xoff = xoff;
lc->yoff = yoff;
lc->g.sx = sx;
lc->g.sy = sy;
lc->g.xoff = xoff;
lc->g.yoff = yoff;
return (lc);
}

View File

@@ -1,4 +1,4 @@
/* $OpenBSD$ */
/* $OpenBSD: layout-set.c,v 1.39 2026/07/10 13:38:45 nicm Exp $ */
/*
* Copyright (c) 2009 Nicholas Marriott <nicholas.marriott@gmail.com>
@@ -170,8 +170,8 @@ layout_set_even(struct window *w, enum layout_type type)
TAILQ_INSERT_TAIL(&lcroot->cells, lcchild, entry);
lcchild->parent = lcroot;
if (layout_cell_is_tiled(lcchild)) {
lcchild->sx = w->sx;
lcchild->sy = w->sy;
lcchild->g.sx = w->sx;
lcchild->g.sy = w->sy;
}
}
@@ -182,8 +182,8 @@ layout_set_even(struct window *w, enum layout_type type)
layout_print_cell(w->layout_root, __func__, 1);
window_resize(w, lcroot->sx, lcroot->sy, -1, -1);
notify_window("window-layout-changed", w);
window_resize(w, lcroot->g.sx, lcroot->g.sy, -1, -1);
events_fire_window("window-layout-changed", w);
server_redraw_window(w);
}
@@ -291,8 +291,8 @@ layout_set_main_h(struct window *w)
layout_print_cell(w->layout_root, __func__, 1);
window_resize(w, lcroot->sx, lcroot->sy, -1, -1);
notify_window("window-layout-changed", w);
window_resize(w, lcroot->g.sx, lcroot->g.sy, -1, -1);
events_fire_window("window-layout-changed", w);
server_redraw_window(w);
}
@@ -388,8 +388,8 @@ layout_set_main_h_mirrored(struct window *w)
layout_print_cell(w->layout_root, __func__, 1);
window_resize(w, lcroot->sx, lcroot->sy, -1, -1);
notify_window("window-layout-changed", w);
window_resize(w, lcroot->g.sx, lcroot->g.sy, -1, -1);
events_fire_window("window-layout-changed", w);
server_redraw_window(w);
}
@@ -485,8 +485,8 @@ layout_set_main_v(struct window *w)
layout_print_cell(w->layout_root, __func__, 1);
window_resize(w, lcroot->sx, lcroot->sy, -1, -1);
notify_window("window-layout-changed", w);
window_resize(w, lcroot->g.sx, lcroot->g.sy, -1, -1);
events_fire_window("window-layout-changed", w);
server_redraw_window(w);
}
@@ -583,8 +583,8 @@ layout_set_main_v_mirrored(struct window *w)
layout_print_cell(w->layout_root, __func__, 1);
window_resize(w, lcroot->sx, lcroot->sy, -1, -1);
notify_window("window-layout-changed", w);
window_resize(w, lcroot->g.sx, lcroot->g.sy, -1, -1);
events_fire_window("window-layout-changed", w);
server_redraw_window(w);
}
@@ -705,7 +705,7 @@ layout_set_tiled(struct window *w)
layout_print_cell(w->layout_root, __func__, 1);
window_resize(w, lcroot->sx, lcroot->sy, -1, -1);
notify_window("window-layout-changed", w);
window_resize(w, lcroot->g.sx, lcroot->g.sy, -1, -1);
events_fire_window("window-layout-changed", w);
server_redraw_window(w);
}

318
layout.c
View File

@@ -1,4 +1,4 @@
/* $OpenBSD$ */
/* $OpenBSD: layout.c,v 1.93 2026/07/10 13:38:45 nicm Exp $ */
/*
* Copyright (c) 2009 Nicholas Marriott <nicholas.marriott@gmail.com>
@@ -56,32 +56,29 @@ static int layout_set_size_check(struct window *, struct layout_cell *,
static void layout_resize_child_cells(struct window *,
struct layout_cell *);
/* Initializes cell geometry to sentinel values. */
static void
layout_geometry_init(struct layout_geometry *lg)
{
lg->sx = UINT_MAX;
lg->sy = UINT_MAX;
lg->xoff = INT_MAX;
lg->yoff = INT_MAX;
}
/* Create a new layout cell. */
struct layout_cell *
layout_create_cell(struct layout_cell *lcparent)
{
struct layout_cell *lc;
lc = xmalloc(sizeof *lc);
lc = xcalloc(1, sizeof *lc);
lc->type = LAYOUT_WINDOWPANE;
lc->flags = 0;
lc->parent = lcparent;
TAILQ_INIT(&lc->cells);
lc->sx = UINT_MAX;
lc->sy = UINT_MAX;
lc->xoff = INT_MAX;
lc->yoff = INT_MAX;
lc->saved_sx = UINT_MAX;
lc->saved_sy = UINT_MAX;
lc->saved_xoff = INT_MAX;
lc->saved_yoff = INT_MAX;
lc->wp = NULL;
layout_geometry_init(&lc->g);
layout_geometry_init(&lc->fg);
return (lc);
}
@@ -144,8 +141,8 @@ layout_print_cell(struct layout_cell *lc, const char *hdr, u_int n)
break;
}
log_debug("%s:%*s%p type %s [parent %p] wp=%p [%d,%d %ux%u]", hdr, n,
" ", lc, type, lc->parent, lc->wp, lc->xoff, lc->yoff, lc->sx,
lc->sy);
" ", lc, type, lc->parent, lc->wp, lc->g.xoff, lc->g.yoff, lc->g.sx,
lc->g.sy);
switch (lc->type) {
case LAYOUT_LEFTRIGHT:
case LAYOUT_TOPBOTTOM:
@@ -164,10 +161,10 @@ layout_search_by_border(struct layout_cell *lc, u_int x, u_int y)
struct layout_cell *lcchild, *last = NULL;
TAILQ_FOREACH(lcchild, &lc->cells, entry) {
if ((int)x >= lcchild->xoff &&
(int)x < lcchild->xoff + (int)lcchild->sx &&
(int)y >= lcchild->yoff &&
(int)y < lcchild->yoff + (int)lcchild->sy) {
if ((int)x >= lcchild->g.xoff &&
(int)x < lcchild->g.xoff + (int)lcchild->g.sx &&
(int)y >= lcchild->g.yoff &&
(int)y < lcchild->g.yoff + (int)lcchild->g.sy) {
/* Inside the cell - recurse. */
return (layout_search_by_border(lcchild, x, y));
}
@@ -179,13 +176,13 @@ layout_search_by_border(struct layout_cell *lc, u_int x, u_int y)
switch (lc->type) {
case LAYOUT_LEFTRIGHT:
if ((int)x < lcchild->xoff &&
(int)x >= last->xoff + (int)last->sx)
if ((int)x < lcchild->g.xoff &&
(int)x >= last->g.xoff + (int)last->g.sx)
return (last);
break;
case LAYOUT_TOPBOTTOM:
if ((int)y < lcchild->yoff &&
(int)y >= last->yoff + (int)last->sy)
if ((int)y < lcchild->g.yoff &&
(int)y >= last->g.yoff + (int)last->g.sy)
return (last);
break;
case LAYOUT_WINDOWPANE:
@@ -202,11 +199,11 @@ layout_search_by_border(struct layout_cell *lc, u_int x, u_int y)
void
layout_set_size(struct layout_cell *lc, u_int sx, u_int sy, int xoff, int yoff)
{
lc->sx = sx;
lc->sy = sy;
lc->g.sx = sx;
lc->g.sy = sy;
lc->xoff = xoff;
lc->yoff = yoff;
lc->g.xoff = xoff;
lc->g.yoff = yoff;
}
/* Make a cell a leaf cell. */
@@ -331,28 +328,28 @@ layout_fix_offsets1(struct layout_cell *lc)
int xoff, yoff;
if (lc->type == LAYOUT_LEFTRIGHT) {
xoff = lc->xoff;
xoff = lc->g.xoff;
TAILQ_FOREACH(lcchild, &lc->cells, entry) {
if (!layout_cell_is_tiled(lcchild) &&
!layout_cell_has_tiled_child(lcchild))
continue;
lcchild->xoff = xoff;
lcchild->yoff = lc->yoff;
lcchild->g.xoff = xoff;
lcchild->g.yoff = lc->g.yoff;
if (lcchild->type != LAYOUT_WINDOWPANE)
layout_fix_offsets1(lcchild);
xoff += lcchild->sx + 1;
xoff += lcchild->g.sx + 1;
}
} else {
yoff = lc->yoff;
yoff = lc->g.yoff;
TAILQ_FOREACH(lcchild, &lc->cells, entry) {
if (!layout_cell_is_tiled(lcchild) &&
!layout_cell_has_tiled_child(lcchild))
continue;
lcchild->xoff = lc->xoff;
lcchild->yoff = yoff;
lcchild->g.xoff = lc->g.xoff;
lcchild->g.yoff = yoff;
if (lcchild->type != LAYOUT_WINDOWPANE)
layout_fix_offsets1(lcchild);
yoff += lcchild->sy + 1;
yoff += lcchild->g.sy + 1;
}
}
}
@@ -367,8 +364,8 @@ layout_fix_offsets(struct window *w)
if (lc->flags & LAYOUT_CELL_FLOATING)
return;
lc->xoff = 0;
lc->yoff = 0;
lc->g.xoff = 0;
lc->g.yoff = 0;
layout_fix_offsets1(lc);
}
@@ -452,16 +449,17 @@ layout_fix_panes(struct window *w, struct window_pane *skip)
old_sx = wp->sx;
old_sy = wp->sy;
wp->xoff = lc->xoff;
wp->yoff = lc->yoff;
sx = lc->sx;
sy = lc->sy;
wp->xoff = lc->g.xoff;
wp->yoff = lc->g.yoff;
sx = lc->g.sx;
sy = lc->g.sy;
if (!window_pane_is_floating(wp) &&
layout_add_horizontal_border(w, lc, status)) {
if (status == PANE_STATUS_TOP)
wp->yoff++;
sy--;
if (sy > 1)
sy--;
}
if (window_pane_scrollbar_reserve(wp)) {
@@ -535,14 +533,14 @@ layout_resize_check(struct window *w, struct layout_cell *lc,
if (lc->type == LAYOUT_WINDOWPANE) {
/* Space available in this cell only. */
if (type == LAYOUT_LEFTRIGHT) {
available = lc->sx;
available = lc->g.sx;
if (w->sb == PANE_SCROLLBARS_ALWAYS)
minimum = PANE_MINIMUM + sb_style->width +
sb_style->pad;
else
minimum = PANE_MINIMUM;
} else {
available = lc->sy;
available = lc->g.sy;
if (layout_add_horizontal_border(w, lc, status))
minimum = PANE_MINIMUM + 1;
else
@@ -580,12 +578,13 @@ layout_resize_adjust(struct window *w, struct layout_cell *lc,
enum layout_type type, int change)
{
struct layout_cell *lcchild;
int changed;
/* Adjust the cell size. */
if (type == LAYOUT_LEFTRIGHT)
lc->sx += change;
lc->g.sx += change;
else
lc->sy += change;
lc->g.sy += change;
/* If this is a leaf cell, that is all that is necessary. */
if (type == LAYOUT_WINDOWPANE)
@@ -613,6 +612,7 @@ layout_resize_adjust(struct window *w, struct layout_cell *lc,
* until no further change is possible.
*/
while (change != 0) {
changed = 0;
TAILQ_FOREACH(lcchild, &lc->cells, entry) {
if (change == 0)
break;
@@ -622,13 +622,17 @@ layout_resize_adjust(struct window *w, struct layout_cell *lc,
if (change > 0) {
layout_resize_adjust(w, lcchild, type, 1);
change--;
changed = 1;
continue;
}
if (layout_resize_check(w, lcchild, type) > 0) {
layout_resize_adjust(w, lcchild, type, -1);
change++;
changed = 1;
}
}
if (!changed)
break;
}
}
@@ -640,9 +644,9 @@ layout_resize_set_size(struct window *w, struct layout_cell *lc,
int change;
if (type == LAYOUT_LEFTRIGHT)
change = size - lc->sx;
change = size - lc->g.sx;
else
change = size - lc->sy;
change = size - lc->g.sy;
layout_resize_adjust(w, lc, type, change);
}
@@ -716,9 +720,9 @@ layout_destroy_cell(struct window *w, struct layout_cell *lc,
lcother = layout_cell_get_neighbour(lc);
if (lcother != NULL) {
if (lcparent->type == LAYOUT_LEFTRIGHT)
change = lc->sx + 1;
change = lc->g.sx + 1;
else
change = lc->sy + 1;
change = lc->g.sy + 1;
layout_resize_adjust(w, lcother, lcparent->type, change);
} else
layout_remove_tile(w, lcparent);
@@ -739,8 +743,8 @@ out:
lc->parent = lcparent->parent;
if (lc->parent == NULL) {
if (layout_cell_is_tiled(lc)) {
lc->xoff = 0;
lc->yoff = 0;
lc->g.xoff = 0;
lc->g.yoff = 0;
}
*lcroot = lc;
} else
@@ -791,29 +795,29 @@ layout_resize(struct window *w, u_int sx, u_int sy)
*/
if (lc->type == LAYOUT_WINDOWPANE && (lc->flags & LAYOUT_CELL_FLOATING))
return;
xchange = sx - lc->sx;
xchange = sx - lc->g.sx;
xlimit = layout_resize_check(w, lc, LAYOUT_LEFTRIGHT);
if (xchange < 0 && xchange < -xlimit)
xchange = -xlimit;
if (xlimit == 0) {
if (sx <= lc->sx) /* lc->sx is minimum possible */
if (sx <= lc->g.sx) /* lc->g.sx is minimum possible */
xchange = 0;
else
xchange = sx - lc->sx;
xchange = sx - lc->g.sx;
}
if (xchange != 0)
layout_resize_adjust(w, lc, LAYOUT_LEFTRIGHT, xchange);
/* Adjust vertically in a similar fashion. */
ychange = sy - lc->sy;
ychange = sy - lc->g.sy;
ylimit = layout_resize_check(w, lc, LAYOUT_TOPBOTTOM);
if (ychange < 0 && ychange < -ylimit)
ychange = -ylimit;
if (ylimit == 0) {
if (sy <= lc->sy) /* lc->sy is minimum possible */
if (sy <= lc->g.sy) /* lc->g.sy is minimum possible */
ychange = 0;
else
ychange = sy - lc->sy;
ychange = sy - lc->g.sy;
}
if (ychange != 0)
layout_resize_adjust(w, lc, LAYOUT_TOPBOTTOM, ychange);
@@ -844,9 +848,9 @@ layout_resize_pane_to(struct window_pane *wp, enum layout_type type,
/* Work out the size adjustment. */
if (type == LAYOUT_LEFTRIGHT)
size = lc->sx;
size = lc->g.sx;
else
size = lc->sy;
size = lc->g.sy;
if (lc == TAILQ_LAST(&lcparent->cells, layout_cells))
change = size - new_size;
else
@@ -877,13 +881,13 @@ layout_resize_floating_pane_to(struct window_pane *wp, enum layout_type type,
}
if (type == LAYOUT_TOPBOTTOM) {
if (lc->sy == size)
if (lc->g.sy == size)
return (0);
lc->sy = size;
lc->g.sy = size;
} else {
if (lc->sx == size)
if (lc->g.sx == size)
return (0);
lc->sx = size;
lc->g.sx = size;
}
redraw_invalidate_scene(wp->window);
return (0);
@@ -905,23 +909,23 @@ layout_resize_floating_pane(struct window_pane *wp, enum layout_type type,
return (0);
if (type == LAYOUT_TOPBOTTOM) {
size = lc->sy + change;
size = lc->g.sy + change;
if (size < PANE_MINIMUM || size > PANE_MAXIMUM) {
*cause = xstrdup("change is too big or too small");
return (-1);
}
lc->sy = size;
lc->g.sy = size;
if (opposite)
lc->yoff -= change;
lc->g.yoff -= change;
} else {
size = lc->sx + change;
size = lc->g.sx + change;
if (size < PANE_MINIMUM || size > PANE_MAXIMUM) {
*cause = xstrdup("change is too big or too small");
return (-1);
}
lc->sx = size;
lc->g.sx = size;
if (opposite)
lc->xoff -= change;
lc->g.xoff -= change;
}
redraw_invalidate_scene(wp->window);
return (0);
@@ -953,7 +957,7 @@ layout_resize_layout(struct window *w, struct layout_cell *lc,
/* Fix cell offsets. */
layout_fix_offsets(w);
layout_fix_panes(w, NULL);
notify_window("window-layout-changed", w);
events_fire_window("window-layout-changed", w);
}
/* Resize a single pane within the layout. */
@@ -1087,13 +1091,13 @@ layout_new_pane_size(struct window *w, u_int previous, struct layout_cell *lc,
*/
min = (PANE_MINIMUM + 1) * (count_left - 1);
if (type == LAYOUT_LEFTRIGHT) {
if (lc->sx - available > min)
min = lc->sx - available;
new_size = (lc->sx * size) / previous;
if (lc->g.sx - available > min)
min = lc->g.sx - available;
new_size = (lc->g.sx * size) / previous;
} else {
if (lc->sy - available > min)
min = lc->sy - available;
new_size = (lc->sy * size) / previous;
if (lc->g.sy - available > min)
min = lc->g.sy - available;
new_size = (lc->g.sy * size) / previous;
}
/* Check against the maximum and minimum size. */
@@ -1129,9 +1133,9 @@ layout_set_size_check(struct window *w, struct layout_cell *lc,
return (0);
if (type == LAYOUT_LEFTRIGHT)
previous = lc->sx;
previous = lc->g.sx;
else
previous = lc->sy;
previous = lc->g.sy;
idx = 0;
TAILQ_FOREACH(lcchild, &lc->cells, entry) {
@@ -1167,32 +1171,32 @@ static void
layout_resize_child_cells(struct window *w, struct layout_cell *lc)
{
struct layout_cell *lcchild;
u_int previous, available, count, idx;
u_int prev, available, count, idx;
if (lc->type == LAYOUT_WINDOWPANE)
return;
/* What is the current size used? */
count = 0;
previous = 0;
prev = 0;
TAILQ_FOREACH(lcchild, &lc->cells, entry) {
if (!layout_cell_is_tiled(lcchild) &&
!layout_cell_has_tiled_child(lcchild))
continue;
count++;
if (lc->type == LAYOUT_LEFTRIGHT)
previous += lcchild->sx;
prev += lcchild->g.sx;
else if (lc->type == LAYOUT_TOPBOTTOM)
previous += lcchild->sy;
prev += lcchild->g.sy;
}
previous += (count - 1);
prev += (count - 1);
/* And how much is available? */
available = 0;
if (lc->type == LAYOUT_LEFTRIGHT)
available = lc->sx;
available = lc->g.sx;
else if (lc->type == LAYOUT_TOPBOTTOM)
available = lc->sy;
available = lc->g.sy;
/* Resize children into the new size. */
idx = 0;
@@ -1201,20 +1205,20 @@ layout_resize_child_cells(struct window *w, struct layout_cell *lc)
!layout_cell_has_tiled_child(lcchild))
continue;
if (lc->type == LAYOUT_TOPBOTTOM) {
lcchild->sx = lc->sx;
lcchild->xoff = lc->xoff;
lcchild->g.sx = lc->g.sx;
lcchild->g.xoff = lc->g.xoff;
} else {
lcchild->sx = layout_new_pane_size(w, previous, lcchild,
lc->type, lc->sx, count - idx, available);
available -= (lcchild->sx + 1);
lcchild->g.sx = layout_new_pane_size(w, prev, lcchild,
lc->type, lc->g.sx, count - idx, available);
available -= (lcchild->g.sx + 1);
}
if (lc->type == LAYOUT_LEFTRIGHT) {
lcchild->sy = lc->sy;
lcchild->yoff = lc->yoff;
lcchild->g.sy = lc->g.sy;
lcchild->g.yoff = lc->g.yoff;
} else {
lcchild->sy = layout_new_pane_size(w, previous, lcchild,
lc->type, lc->sy, count - idx, available);
available -= (lcchild->sy + 1);
lcchild->g.sy = layout_new_pane_size(w, prev, lcchild,
lc->type, lc->g.sy, count - idx, available);
available -= (lcchild->g.sy + 1);
}
layout_resize_child_cells(w, lcchild);
idx++;
@@ -1234,7 +1238,7 @@ layout_replace_with_node(struct window *w, struct layout_cell *lc,
lcparent = layout_create_cell(lc->parent);
layout_make_node(lcparent, type);
layout_set_size(lcparent, lc->sx, lc->sy, lc->xoff, lc->yoff);
layout_set_size(lcparent, lc->g.sx, lc->g.sy, lc->g.xoff, lc->g.yoff);
if (lc->parent == NULL)
w->layout_root = lcparent;
else
@@ -1253,7 +1257,7 @@ layout_split_check_space(struct window_pane *wp, struct layout_cell *lc,
enum layout_type type)
{
struct style *sb_style = &wp->scrollbar_style;
u_int minimum, sx = lc->sx, sy = lc->sy;
u_int minimum, sx = lc->g.sx, sy = lc->g.sy;
int status;
if (lc->flags & LAYOUT_CELL_FLOATING)
@@ -1292,7 +1296,7 @@ layout_split_sizes(struct layout_cell *lc, int size, int before,
enum layout_type type, u_int *size1, u_int *size2, u_int *saved_size)
{
u_int s1, s2, ss;
u_int sx = lc->sx, sy = lc->sy;
u_int sx = lc->g.sx, sy = lc->g.sy;
if (type == LAYOUT_LEFTRIGHT)
ss = sx;
@@ -1306,7 +1310,7 @@ layout_split_sizes(struct layout_cell *lc, int size, int before,
s2 = size;
if (s2 < PANE_MINIMUM)
s2 = PANE_MINIMUM;
else if (s2 > sx - 2)
else if (s2 > ss - 2)
s2 = ss - 2;
s1 = ss - 1 - s2;
@@ -1339,10 +1343,10 @@ layout_split_pane(struct window_pane *wp, enum layout_type type, int size,
lc = wp->layout_cell;
/* Copy the old cell size. */
sx = lc->sx;
sy = lc->sy;
xoff = lc->xoff;
yoff = lc->yoff;
sx = lc->g.sx;
sy = lc->g.sy;
xoff = lc->g.xoff;
yoff = lc->g.yoff;
/* Check there is enough space for the two new panes. */
if (!layout_split_check_space(wp, lc, type))
@@ -1383,13 +1387,13 @@ layout_split_pane(struct window_pane *wp, enum layout_type type, int size,
* must be resized before inserting the new cell.
*/
if (lc->type == LAYOUT_LEFTRIGHT) {
lc->sx = new_size;
lc->g.sx = new_size;
layout_resize_child_cells(wp->window, lc);
lc->sx = saved_size;
lc->g.sx = saved_size;
} else if (lc->type == LAYOUT_TOPBOTTOM) {
lc->sy = new_size;
lc->g.sy = new_size;
layout_resize_child_cells(wp->window, lc);
lc->sy = saved_size;
lc->g.sy = saved_size;
}
resize_first = 1;
@@ -1433,10 +1437,10 @@ layout_split_pane(struct window_pane *wp, enum layout_type type, int size,
*/
if (!resize_first && type == LAYOUT_LEFTRIGHT) {
layout_set_size(lc1, size1, sy, xoff, yoff);
layout_set_size(lc2, size2, sy, xoff + lc1->sx + 1, yoff);
layout_set_size(lc2, size2, sy, xoff + lc1->g.sx + 1, yoff);
} else if (!resize_first && type == LAYOUT_TOPBOTTOM) {
layout_set_size(lc1, sx, size1, xoff, yoff);
layout_set_size(lc2, sx, size2, xoff, yoff + lc1->sy + 1);
layout_set_size(lc2, sx, size2, xoff, yoff + lc1->g.sy + 1);
}
if (full_size) {
if (!resize_first)
@@ -1453,8 +1457,8 @@ layout_split_pane(struct window_pane *wp, enum layout_type type, int size,
* layout_assign_pane before much else happens!
*/
struct layout_cell *
layout_floating_pane(struct window *w, struct window_pane *wp, u_int sx,
u_int sy, int ox, int oy)
layout_floating_pane(struct window *w, struct window_pane *wp,
struct layout_geometry *lg)
{
struct layout_cell *lc, *lcnew, *lcparent;
@@ -1466,16 +1470,16 @@ layout_floating_pane(struct window *w, struct window_pane *wp, u_int sx,
if (lcparent == NULL) {
/*
* Adding a pane to a root that isn't node. Must create and
* insert a new root.
*/
* Adding a pane to a root that isn't a node. Must create and
* insert a new root.
*/
lcparent = layout_replace_with_node(w, lc, LAYOUT_TOPBOTTOM);
}
lcnew = layout_create_cell(lcparent);
TAILQ_INSERT_AFTER(&lcparent->cells, lc, lcnew, entry);
lcnew->flags |= LAYOUT_CELL_FLOATING;
layout_set_size(lcnew, sx, sy, ox, oy);
layout_set_size(lcnew, lg->sx, lg->sy, lg->xoff, lg->yoff);
return (lcnew);
}
@@ -1498,7 +1502,7 @@ layout_close_pane(struct window_pane *wp)
layout_fix_offsets(w);
layout_fix_panes(w, NULL);
}
notify_window("window-layout-changed", w);
events_fire_window("window-layout-changed", w);
}
/* Spread out cells inside a parent cell. */
@@ -1518,12 +1522,12 @@ layout_spread_cell(struct window *w, struct layout_cell *parent)
status = window_get_pane_status(w);
if (parent->type == LAYOUT_LEFTRIGHT)
size = parent->sx;
size = parent->g.sx;
else if (parent->type == LAYOUT_TOPBOTTOM) {
if (layout_add_horizontal_border(w, parent, status))
size = parent->sy - 1;
size = parent->g.sy - 1;
else
size = parent->sy;
size = parent->g.sy;
} else
return (0);
if (size < number - 1)
@@ -1544,7 +1548,7 @@ layout_spread_cell(struct window *w, struct layout_cell *parent)
continue;
change = 0;
if (parent->type == LAYOUT_LEFTRIGHT) {
change = each - (int)lc->sx;
change = each - (int)lc->g.sx;
if (remainder > 0) {
change++;
remainder--;
@@ -1559,7 +1563,7 @@ layout_spread_cell(struct window *w, struct layout_cell *parent)
this++;
remainder--;
}
change = this - (int)lc->sy;
change = this - (int)lc->g.sy;
layout_resize_adjust(w, lc, LAYOUT_TOPBOTTOM, change);
}
if (change != 0)
@@ -1656,29 +1660,29 @@ layout_get_floating_cell(struct cmdq_item *item, struct args *args,
char **cause)
{
struct layout_cell *lcnew;
u_int sx = UINT_MAX, sy = UINT_MAX;
int ox = INT_MAX, oy = INT_MAX;
struct layout_geometry fg;
if (layout_floating_args_parse(item, args, lines, w, &sx, &sy, &ox, &oy,
cause) != 0)
layout_geometry_init(&fg);
if (layout_floating_args_parse(item, args, lines, w, &fg, cause) != 0)
return (NULL);
lcnew = layout_floating_pane(w, wp, sx, sy, ox, oy);
window_push_zoom(wp->window, 1, args_has(args, 'Z'));
lcnew = layout_floating_pane(w, wp, &fg);
return (lcnew);
}
int
layout_floating_args_parse(struct cmdq_item *item, struct args *args,
enum pane_lines lines, struct window *w, u_int *sxp, u_int *syp, int *oxp,
int *oyp, char **cause)
enum pane_lines lines, struct window *w, struct layout_geometry *lg,
char **cause)
{
int sx, sy, ox, oy;
char *error = NULL;
sx = *sxp == UINT_MAX ? w->sx / 2 : *sxp;
sy = *syp == UINT_MAX ? w->sy / 4 : *syp;
ox = *oxp == INT_MAX ? INT_MAX : *oxp;
oy = *oyp == INT_MAX ? INT_MAX : *oyp;
sx = lg->sx == UINT_MAX ? w->sx / 2 : lg->sx;
sy = lg->sy == UINT_MAX ? w->sy / 4 : lg->sy;
ox = lg->xoff;
oy = lg->yoff;
if (args_has(args, 'x')) {
sx = args_percentage_and_expand(args, 'x', 0, PANE_MAXIMUM,
@@ -1730,7 +1734,7 @@ layout_floating_args_parse(struct cmdq_item *item, struct args *args,
ox = 4;
}
w->last_new_pane_x = ox;
} else
} else if (args_has(args, 'X'))
if (lines != PANE_LINES_NONE)
ox += 1;
if (oy == INT_MAX) {
@@ -1742,7 +1746,7 @@ layout_floating_args_parse(struct cmdq_item *item, struct args *args,
oy = 2;
}
w->last_new_pane_y = oy;
} else
} else if (args_has(args, 'Y'))
if (lines != PANE_LINES_NONE)
oy += 1;
@@ -1755,10 +1759,10 @@ layout_floating_args_parse(struct cmdq_item *item, struct args *args,
return (-1);
}
*sxp = sx;
*syp = sy;
*oxp = ox;
*oyp = oy;
lg->sx = sx;
lg->sy = sy;
lg->xoff = ox;
lg->yoff = oy;
return (0);
}
@@ -1774,7 +1778,7 @@ layout_remove_tile(struct window *w, struct layout_cell *lc)
int change;
if (lc->flags & LAYOUT_CELL_FLOATING)
return (0);
return (-1);
lcneighbour = layout_cell_get_neighbour(lc);
if (lcneighbour == NULL) {
@@ -1787,9 +1791,9 @@ layout_remove_tile(struct window *w, struct layout_cell *lc)
* neighbour.
*/
if (type == LAYOUT_TOPBOTTOM)
change = lc->sy + 1;
change = lc->g.sy + 1;
else
change = lc->sx + 1;
change = lc->g.sx + 1;
layout_resize_adjust(w, lcneighbour, type, change);
}
@@ -1799,7 +1803,7 @@ layout_remove_tile(struct window *w, struct layout_cell *lc)
*/
if (lc->parent != NULL)
layout_set_size(lc, 0, 0, 0, 0);
return (1);
return (0);
}
/*
@@ -1816,14 +1820,14 @@ layout_insert_tile(struct window *w, struct layout_cell *lc)
if (lc == NULL)
fatalx("layout cell cannot be null when tiling");
lcparent = lc->parent;
if (lc->flags & LAYOUT_CELL_FLOATING)
return (1);
if (layout_cell_is_tiled(lc))
return (-1);
lcparent = lc->parent;
if (lcparent == NULL) {
/* Only pane in the layout. */
layout_set_size(lc, w->sx, w->sy, 0, 0);
return (1);
return (0);
}
type = lcparent->type;
@@ -1835,9 +1839,9 @@ layout_insert_tile(struct window *w, struct layout_cell *lc)
*/
layout_insert_tile(w, lcparent);
if (type == LAYOUT_LEFTRIGHT)
size1 = lcparent->sx;
size1 = lcparent->g.sx;
else
size1 = lcparent->sy;
size1 = lcparent->g.sy;
layout_resize_set_size(w, lc, type, size1);
} else {
/*
@@ -1846,7 +1850,7 @@ layout_insert_tile(struct window *w, struct layout_cell *lc)
*/
lctiled = layout_cell_get_first_tiled(lcneighbour);
if (!layout_split_check_space(lctiled->wp, lcneighbour, type))
return (0);
return (-1);
layout_split_sizes(lcneighbour, -1, 0, type, &size1, &size2,
&saved_size);
layout_resize_set_size(w, lc, type, size1);
@@ -1855,13 +1859,13 @@ layout_insert_tile(struct window *w, struct layout_cell *lc)
/* Setting opposite of the 'split' size to that of the parent. */
if (lcparent->type == LAYOUT_LEFTRIGHT) {
size1 = lcparent->sy;
size1 = lcparent->g.sy;
type = LAYOUT_TOPBOTTOM;
} else {
size1 = lcparent->sx;
size1 = lcparent->g.sx;
type = LAYOUT_LEFTRIGHT;
}
layout_resize_set_size(w, lc, type, size1);
return (1);
return (0);
}

Some files were not shown because too many files have changed in this diff Show More