Merge branch 'obsd-master'

This commit is contained in:
Thomas Adam
2026-07-10 16:30:07 +01:00
9 changed files with 1108 additions and 94 deletions

View File

@@ -31,6 +31,8 @@ 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);
@@ -64,8 +66,8 @@ const struct cmd_entry cmd_set_hook_entry = {
.name = "set-hook",
.alias = NULL,
.args = { "agpRt:uB:w", 0, 2, cmd_set_option_args_parse },
.usage = "[-agpRuw] [-B name:what:format] " CMD_TARGET_PANE_USAGE " "
.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 },
@@ -85,17 +87,63 @@ cmd_set_option_args_parse(struct args *args, u_int idx,
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;
int id, scope, flags = 0;
if (args_count(args) > 1) {
cmdq_error(item, "too many arguments");
@@ -130,7 +178,7 @@ cmd_set_hook_monitor_exec(struct cmdq_item *item, struct args *args, int window)
cmd_find_copy_state(&fs, target);
if (args_has(args, 'u')) {
notify_monitor_remove(oo, name);
hooks_monitor_remove(oo, name);
goto out;
}
@@ -152,7 +200,13 @@ cmd_set_hook_monitor_exec(struct cmdq_item *item, struct args *args, int window)
}
}
notify_monitor_add(item, oo, name, type, id, format, &fs, target->s);
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);
@@ -185,6 +239,8 @@ cmd_set_option_exec(struct cmd *self, struct cmdq_item *item)
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) {
@@ -197,7 +253,7 @@ cmd_set_option_exec(struct cmd *self, struct cmdq_item *item)
/* 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);
}
@@ -289,6 +345,8 @@ cmd_set_option_exec(struct cmd *self, struct cmdq_item *item)
goto fail;
}
options_set_string(oo, name, append, "%s", value);
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,

121
format.c
View File

@@ -2187,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)
@@ -3503,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
},
@@ -3554,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
},

461
hooks.c Normal file
View File

@@ -0,0 +1,461 @@
/* $OpenBSD$ */
/*
* 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_list *cmdlist, struct cmdq_state *state)
{
struct cmdq_item *new_item;
char *s;
if (cmdlist == NULL)
return (item);
if (log_get_level() != 0) {
s = cmd_list_print(cmdlist, 0);
log_debug("%s: hook %s is: %s", __func__, hd->name, s);
free(s);
}
new_item = cmdq_get_command(cmdlist, state);
if (item != NULL)
return (cmdq_insert_after(item, new_item));
return (cmdq_append(NULL, new_item));
}
/* Parse a hook command. */
static struct cmd_parse_result *
hooks_parse(struct hooks_data *hd, struct cmd_find_state *fs,
const char *value)
{
struct cmd_parse_result *pr;
struct format_tree *ft;
char *expanded;
if (!hd->expand)
return (cmd_parse_from_string(value, NULL));
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);
pr = cmd_parse_from_string(expanded, NULL);
free(expanded);
return (pr);
}
/* 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_list *cmdlist;
const char *value;
struct cmd_parse_result *pr;
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);
pr = hooks_parse(hd, &fs, value);
switch (pr->status) {
case CMD_PARSE_ERROR:
log_debug("%s: can't parse hook %s: %s", __func__,
hd->name, pr->error);
free(pr->error);
break;
case CMD_PARSE_SUCCESS:
hooks_insert_one(item, hd, pr->cmdlist, state);
break;
}
} else {
a = options_array_first(o);
while (a != NULL) {
if (hd->expand) {
value = options_array_item_value(a)->string;
pr = hooks_parse(hd, &fs, value);
switch (pr->status) {
case CMD_PARSE_ERROR:
if (pr->error != NULL) {
cmdq_error(item, "%s",
pr->error);
}
break;
case CMD_PARSE_SUCCESS:
item = hooks_insert_one(item, hd,
pr->cmdlist, state);
break;
}
} else {
cmdlist = options_array_item_value(a)->cmdlist;
item = hooks_insert_one(item, hd, cmdlist,
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);
}

110
input.c
View File

@@ -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);
}
@@ -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);
}
}

View File

@@ -1,7 +1,7 @@
/* $OpenBSD$ */
/*
* Copyright (c) 2026 Nicholas Marriott <nicm@users.sourceforge.net>
* 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
@@ -53,7 +53,7 @@ struct monitor_item {
enum monitor_type type;
u_int id;
u_int flags;
int flags;
char *last;
struct monitor_panes panes;
@@ -198,7 +198,9 @@ monitor_check_value(struct monitor_set *ms, struct monitor_item *me,
{
if (*last == NULL) {
*last = value;
if (me->flags & MONITOR_NOTIFY_INITIAL)
if ((me->flags & MONITOR_NOTIFY_INITIAL) &&
((~me->flags & MONITOR_NOTIFY_TRUE) ||
format_true(value)))
monitor_report(ms, me, s, wl, wp, value, NULL);
return;
}
@@ -208,7 +210,8 @@ monitor_check_value(struct monitor_set *ms, struct monitor_item *me,
return;
}
monitor_report(ms, me, s, wl, wp, value, *last);
if ((~me->flags & MONITOR_NOTIFY_TRUE) || format_true(value))
monitor_report(ms, me, s, wl, wp, value, *last);
free(*last);
*last = value;
}
@@ -621,7 +624,7 @@ fail:
/* Add a subscription. */
void
monitor_add(struct monitor_set *ms, const char *name, enum monitor_type type,
int id, const char *format, u_int flags)
int id, const char *format, int flags)
{
struct monitor_item *me, find = { .name = (char *)name };
struct timeval tv = { .tv_sec = 1 };

View File

@@ -1932,12 +1932,22 @@ const struct options_table_entry options_table[] = {
OPTIONS_TABLE_HOOK("client-light-theme", ""),
OPTIONS_TABLE_HOOK("client-dark-theme", ""),
OPTIONS_TABLE_HOOK("command-error", ""),
OPTIONS_TABLE_HOOK("marked-pane-changed", ""),
OPTIONS_TABLE_PANE_HOOK("pane-command-finished", ""),
OPTIONS_TABLE_PANE_HOOK("pane-command-started", ""),
OPTIONS_TABLE_PANE_HOOK("pane-created", ""),
OPTIONS_TABLE_PANE_HOOK("pane-died", ""),
OPTIONS_TABLE_PANE_HOOK("pane-exited", ""),
OPTIONS_TABLE_PANE_HOOK("pane-focus-in", ""),
OPTIONS_TABLE_PANE_HOOK("pane-focus-out", ""),
OPTIONS_TABLE_PANE_HOOK("pane-mode-changed", ""),
OPTIONS_TABLE_PANE_HOOK("pane-mode-entered", ""),
OPTIONS_TABLE_PANE_HOOK("pane-mode-exited", ""),
OPTIONS_TABLE_PANE_HOOK("pane-prompt-closed", ""),
OPTIONS_TABLE_PANE_HOOK("pane-prompt-opened", ""),
OPTIONS_TABLE_PANE_HOOK("pane-resized", ""),
OPTIONS_TABLE_PANE_HOOK("pane-set-clipboard", ""),
OPTIONS_TABLE_PANE_HOOK("pane-shell-prompt", ""),
OPTIONS_TABLE_PANE_HOOK("pane-title-changed", ""),
OPTIONS_TABLE_HOOK("session-closed", ""),
OPTIONS_TABLE_HOOK("session-created", ""),
@@ -1948,6 +1958,8 @@ const struct options_table_entry options_table[] = {
OPTIONS_TABLE_WINDOW_HOOK("window-pane-changed", ""),
OPTIONS_TABLE_WINDOW_HOOK("window-renamed", ""),
OPTIONS_TABLE_WINDOW_HOOK("window-resized", ""),
OPTIONS_TABLE_WINDOW_HOOK("window-unzoomed", ""),
OPTIONS_TABLE_WINDOW_HOOK("window-zoomed", ""),
OPTIONS_TABLE_HOOK("window-unlinked", ""),
{ .name = NULL }

122
tmux.1
View File

@@ -6325,11 +6325,7 @@ For example, this could be used to write to a log file:
set\-hook \-g command\-error "run\-shell \\"echo 'a tmux command failed' >>/tmp/log\\""
.Ed
.Pp
All the notifications listed in the
.Sx CONTROL MODE
section are hooks (without any arguments), except
.Ic %exit .
The following additional hooks are available:
The following hooks are available in addition to after hooks:
.Bl -tag -width "XXXXXXXXXXXXXXXXXXXXXX"
.It alert\-activity
Run when a window has activity.
@@ -6348,11 +6344,11 @@ Run when a client becomes the latest active client of its session.
.It client\-attached
Run when a client is attached.
.It client\-detached
Run when a client is detached
Run when a client is detached.
.It client\-focus\-in
Run when focus enters a client
Run when focus enters a client.
.It client\-focus\-out
Run when focus exits a client
Run when focus exits a client.
.It client\-resized
Run when a client is resized.
.It client\-session\-changed
@@ -6363,6 +6359,21 @@ Run when a client switches to a light theme.
Run when a client switches to a dark theme.
.It command\-error
Run when a command fails.
.It marked\-pane\-changed
Run when the marked pane is set or cleared.
.It pane\-command\-finished
Run when an OSC 133 command finishes in a pane.
The event payload includes
.Ql hook_command_start_time ,
.Ql hook_command_end_time ,
.Ql hook_command_duration
and
.Ql hook_command_status
if present.
.It pane\-command\-started
Run when an OSC 133 command starts in a pane.
.It pane\-created
Run when a pane is created or respawned.
.It pane\-died
Run when the program running in a pane exits, but
.Ic remain\-on\-exit
@@ -6377,20 +6388,40 @@ option is on.
Run when the focus exits a pane, if the
.Ic focus\-events
option is on.
.It pane\-mode\-changed
Run when a pane changes mode.
.It pane\-mode\-entered
Run when a pane enters a mode.
.It pane\-mode\-exited
Run when a pane exits a mode.
.It pane\-prompt\-closed
Run when a prompt in a pane is closed.
.It pane\-prompt\-opened
Run when a prompt is opened in a pane.
.It pane\-resized
Run when a pane is resized.
.It pane\-set\-clipboard
Run when the terminal clipboard is set using the
.Xr xterm 1
escape sequence.
.It session\-created
Run when a new session created.
.It pane\-shell\-prompt
Run when an OSC 133 shell prompt starts in a pane.
.It pane\-title\-changed
Run when a pane title is changed.
.It session\-closed
Run when a session closed.
Run when a session is closed.
.It session\-created
Run when a new session is created.
.It session\-renamed
Run when a session is renamed.
.It session\-window\-changed
Run when a session changes its active window.
.It window\-layout\-changed
Run when a window layout is changed.
.It window\-linked
Run when a window is linked into a session.
.It window\-pane\-changed
Run when a window changes its active pane.
.It window\-renamed
Run when a window is renamed.
.It window\-resized
@@ -6398,6 +6429,10 @@ Run when a window is resized.
This may be after the
.Ar client\-resized
hook is run.
.It window\-unzoomed
Run when a window is unzoomed.
.It window\-zoomed
Run when a window is zoomed.
.It window\-unlinked
Run when a window is unlinked from a session.
.El
@@ -6405,13 +6440,15 @@ Run when a window is unlinked from a session.
Hooks are managed with these commands:
.Bl -tag -width Ds
.It Xo Ic set\-hook
.Op Fl agpRuw
.Op Fl agpERTuw
.Op Fl B Ar name:what:format
.Op Fl t Ar target\-pane
.Ar hook\-name
.Op Ar command
.Xc
Without
.Fl E
or
.Fl R ,
sets (or with
.Fl u
@@ -6434,6 +6471,11 @@ is the hook to run,
selects the session, pane, all panes, window, or all windows, and
.Ar format
is expanded once a second.
With
.Fl T ,
the hook is run only when
.Ar format
is true.
For monitor hooks,
.Ar name
must begin with
@@ -6452,6 +6494,14 @@ the subscription named by
is removed.
.Pp
With
.Fl E ,
fire the user event named
.Ar hook\-name .
.Ar hook\-name
must begin with
.Ql @ .
.Pp
With
.Fl R ,
run
.Ar hook\-name
@@ -7161,6 +7211,11 @@ The following variables are available, where appropriate:
.It Li "pane_at_top" Ta "" Ta "1 if pane is at the top of window"
.It Li "pane_bg" Ta "" Ta "Pane background colour"
.It Li "pane_bottom" Ta "" Ta "Bottom of pane"
.It Li "pane_command_duration" Ta "" Ta "Current or most recent OSC 133 command duration in seconds"
.It Li "pane_command_end_time" Ta "" Ta "Time most recent OSC 133 command ended"
.It Li "pane_command_running" Ta "" Ta "1 if an OSC 133 command is running"
.It Li "pane_command_start_time" Ta "" Ta "Time current or most recent OSC 133 command started"
.It Li "pane_command_status" Ta "" Ta "Exit status from most recent OSC 133 command"
.It Li "pane_current_command" Ta "" Ta "Current command if available"
.It Li "pane_current_path" Ta "" Ta "Current path if available"
.It Li "pane_dead" Ta "" Ta "1 if pane is dead"
@@ -7178,6 +7233,8 @@ The following variables are available, where appropriate:
.It Li "pane_input_off" Ta "" Ta "1 if input to pane is disabled"
.It Li "pane_key_mode" Ta "" Ta "Extended key reporting mode in this pane"
.It Li "pane_last" Ta "" Ta "1 if last pane"
.It Li "pane_last_output_time" Ta "" Ta "Time pane last produced output"
.It Li "pane_last_prompt_time" Ta "" Ta "Time most recent OSC 133 prompt began"
.It Li "pane_left" Ta "" Ta "Left of pane"
.It Li "pane_marked" Ta "" Ta "1 if this is the marked pane"
.It Li "pane_marked_set" Ta "" Ta "1 if a marked pane is set"
@@ -8562,8 +8619,10 @@ or the current pane if omitted) after the command finishes.
If the command fails, the exit status is also displayed.
.Tg wait
.It Xo Ic wait\-for
.Op Fl L | S | U
.Ar channel
.Op Fl ELSUlv
.Op Fl F Ar format
.Op Fl w Ar waiter
.Ar name
.Xc
.D1 Pq alias: Ic wait
When used without options, prevents the client from exiting until woken using
@@ -8576,6 +8635,41 @@ is used, the channel is locked and any clients that try to lock the same
channel are made to wait until the channel is unlocked with
.Ic wait\-for
.Fl U .
.Pp
With
.Fl E ,
.Nm
waits for the next event with
.Ar name .
Events include hook and notification names, and user
.Ql @
events generated by
.Ic set-hook
.Fl E
or
.Ic set-hook
.Fl B .
If
.Fl F
is given,
.Ar format
must also be true.
If
.Fl v
is given, event payload keys are printed (whether or not
.Ar format
is true).
.Pp
.Fl l
list the waiters for
.Ar name
and
.Fl w
wakes
.Ar waiter
on
.Ar name
immediately.
.El
.Sh EXIT MESSAGES
When a

136
tmux.h
View File

@@ -49,6 +49,9 @@ struct cmdq_state;
struct cmds;
struct control_state;
struct environ;
struct event_payload;
struct event_payload_item;
struct events_sink;
struct format_job_tree;
struct format_tree;
struct hyperlinks_uri;
@@ -1298,6 +1301,7 @@ struct window_pane {
#define PANE_UNSEENCHANGES 0x4000
#define PANE_REDRAWSCROLLBAR 0x8000
#define PANE_DESTROYED 0x10000
#define PANE_CMDRUNNING 0x20000
bitstr_t *sync_dirty;
u_int sync_dirty_size;
@@ -1320,6 +1324,12 @@ struct window_pane {
struct cmdq_item *wait_item; /* new-pane -W: waiting for pane exit */
struct spawn_editor_state *editor;
time_t last_output_time;
time_t last_prompt_time;
time_t cmd_start_time;
time_t cmd_end_time;
int cmd_status;
int fd;
struct bufferevent *event;
@@ -2331,6 +2341,7 @@ enum monitor_type {
MONITOR_ALL_WINDOWS
};
#define MONITOR_NOTIFY_INITIAL 0x1
#define MONITOR_NOTIFY_TRUE 0x2
struct monitor_change {
const char *name;
const char *value;
@@ -2343,6 +2354,23 @@ struct monitor_change {
};
typedef void (*monitor_cb)(struct monitor_change *, void *);
/* Event payload type. */
enum event_payload_type {
EVENT_PAYLOAD_STRING,
EVENT_PAYLOAD_TIME,
EVENT_PAYLOAD_INT,
EVENT_PAYLOAD_UINT,
EVENT_PAYLOAD_CLIENT,
EVENT_PAYLOAD_SESSION,
EVENT_PAYLOAD_WINDOW,
EVENT_PAYLOAD_PANE,
EVENT_PAYLOAD_POINTER
};
/* Event payload callbacks. */
typedef void (*event_payload_free_cb)(void *);
typedef void (*event_payload_print_cb)(void *, struct evbuffer *);
/* Key binding and key table. */
struct key_binding {
key_code key;
@@ -2652,29 +2680,78 @@ char *format_grid_hyperlink(struct grid *, u_int, u_int,
struct screen *);
char *format_grid_line(struct grid *, u_int);
/* format-draw.c */
void format_draw(struct screen_write_ctx *,
const struct grid_cell *, u_int, const char *,
struct style_ranges *, int);
u_int format_width(const char *);
char *format_trim_left(const char *, u_int);
char *format_trim_right(const char *, u_int);
/* events-payload.c */
struct event_payload *event_payload_create(void);
void event_payload_free(struct event_payload *);
void printflike(2, 3) event_payload_log(struct event_payload *, const char *,
...);
char *event_payload_item_print(struct event_payload_item *);
void event_payload_set_target(struct event_payload *,
struct cmd_find_state *);
int event_payload_get_target(struct event_payload *,
struct cmd_find_state *);
void printflike(3, 4) event_payload_set_string(struct event_payload *,
const char *, const char *, ...);
void event_payload_set_time(struct event_payload *, const char *, time_t);
void event_payload_set_int(struct event_payload *, const char *, int);
void event_payload_set_uint(struct event_payload *, const char *, u_int);
void event_payload_set_client(struct event_payload *, const char *,
struct client *);
void event_payload_set_session(struct event_payload *, const char *,
struct session *);
void event_payload_set_window(struct event_payload *, const char *,
struct window *);
void event_payload_set_pane(struct event_payload *, const char *,
struct window_pane *);
void event_payload_set_pointer(struct event_payload *, const char *, void *,
event_payload_free_cb, event_payload_print_cb);
const char *event_payload_get_string(struct event_payload *, const char *);
char *event_payload_print(struct event_payload *, const char *);
void event_payload_add_formats(struct event_payload *,
struct format_tree *, const char *);
struct event_payload_item *event_payload_first(struct event_payload *);
struct event_payload_item *event_payload_next(struct event_payload_item *);
const char *event_payload_item_name(struct event_payload_item *);
enum event_payload_type event_payload_item_type(struct event_payload_item *);
time_t event_payload_get_time(struct event_payload *, const char *);
int event_payload_get_int(struct event_payload *, const char *, int *);
int event_payload_get_uint(struct event_payload *, const char *, u_int *);
struct client *event_payload_get_client(struct event_payload *, const char *);
struct session *event_payload_get_session(struct event_payload *, const char *);
struct window *event_payload_get_window(struct event_payload *, const char *);
struct window_pane *event_payload_get_pane(struct event_payload *,
const char *);
void *event_payload_get_pointer(struct event_payload *, const char *);
/* notify.c */
void notify_hook(struct cmdq_item *, const char *);
void notify_monitor_add(struct cmdq_item *, struct options *,
/* events.c */
typedef void (*events_cb)(const char *, struct event_payload *, void *);
struct events_sink *events_add_sink(const char *, events_cb, void *);
void events_remove_sink(struct events_sink *);
void events_fire(const char *, struct event_payload *);
void events_fire_client(const char *, struct client *);
void events_fire_session(const char *, struct session *);
void events_fire_window(const char *, struct window *);
void events_fire_pane(const char *, struct window_pane *);
void events_fire_winlink(const char *, struct winlink *);
/* format-draw.c */
void format_draw(struct screen_write_ctx *, const struct grid_cell *,
u_int, const char *, struct style_ranges *, int);
u_int format_width(const char *);
char *format_trim_left(const char *, u_int);
char *format_trim_right(const char *, u_int);
/* hooks.c */
void hooks_add_event(const char *);
int hooks_valid_event_name(const char *);
void hooks_build_events(void);
void hooks_run(struct cmdq_item *, const char *);
void hooks_monitor_add(struct cmdq_item *, struct options *,
const char *, enum monitor_type, int, const char *,
struct cmd_find_state *, struct session *);
void notify_monitor_remove(struct options *, const char *);
void notify_monitor_free(void *);
char *notify_monitor_to_string(struct options_entry *);
void notify_client(const char *, struct client *);
void notify_session(const char *, struct session *);
void notify_winlink(const char *, struct winlink *);
void notify_session_window(const char *, struct session *, struct window *);
void notify_window(const char *, struct window *);
void notify_pane(const char *, struct window_pane *);
void notify_paste_buffer(const char *, int);
int, struct cmd_find_state *, struct session *);
void hooks_monitor_remove(struct options *, const char *);
void hooks_monitor_free(void *);
char *hooks_monitor_to_string(struct options_entry *);
/* options.c */
struct options *options_create(struct options *);
@@ -3882,7 +3959,7 @@ void monitor_destroy(struct monitor_set *);
int monitor_parse(const char *, char **, enum monitor_type *, int *,
char **);
void monitor_add(struct monitor_set *, const char *, enum monitor_type, int,
const char *, u_int);
const char *, int);
void monitor_remove(struct monitor_set *, const char *);
/* control.c */
@@ -3905,20 +3982,7 @@ void control_add_sub(struct client *, const char *, enum monitor_type, int,
void control_remove_sub(struct client *, const char *);
/* control-notify.c */
void control_notify_pane_mode_changed(int);
void control_notify_window_layout_changed(struct window *);
void control_notify_window_pane_changed(struct window *);
void control_notify_window_unlinked(struct session *, struct window *);
void control_notify_window_linked(struct session *, struct window *);
void control_notify_window_renamed(struct window *);
void control_notify_client_session_changed(struct client *);
void control_notify_client_detached(struct client *);
void control_notify_session_renamed(struct session *);
void control_notify_session_created(struct session *);
void control_notify_session_closed(struct session *);
void control_notify_session_window_changed(struct session *);
void control_notify_paste_buffer_changed(const char *);
void control_notify_paste_buffer_deleted(const char *);
void control_build_events(void);
/* session.c */
extern struct sessions sessions;

157
window.c
View File

@@ -73,8 +73,8 @@ static struct window_pane *window_pane_create(struct window *, u_int, u_int,
static void window_pane_destroy(struct window_pane *);
static void window_pane_free(struct window_pane *);
static void window_pane_scrollbar_timer(int, short, void *);
static void window_pane_full_size_offset(struct window_pane *wp,
int *xoff, int *yoff, u_int *sx, u_int *sy);
static void window_pane_full_size_offset(struct window_pane *, int *, int *,
u_int *, u_int *);
RB_GENERATE(windows, window, entry, window_cmp);
RB_GENERATE(winlinks, winlink, entry, winlink_cmp);
@@ -86,6 +86,7 @@ struct window_pane_prompt {
status_prompt_input_cb inputcb;
prompt_free_cb freecb;
void *data;
enum prompt_type type;
};
int
@@ -94,6 +95,78 @@ window_cmp(struct window *w1, struct window *w2)
return (w1->id - w2->id);
}
static void
window_fire_renamed(struct window *w, const char *old_name)
{
struct event_payload *ep;
struct cmd_find_state fs;
ep = event_payload_create();
cmd_find_from_window(&fs, w, 0);
event_payload_set_target(ep, &fs);
event_payload_set_window(ep, "window", w);
event_payload_set_string(ep, "old_name", "%s", old_name);
event_payload_set_string(ep, "new_name", "%s", w->name);
events_fire("window-renamed", ep);
}
static void
window_fire_pane_changed(struct window *w, struct window_pane *wp,
struct window_pane *lastwp)
{
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_window(ep, "window", w);
event_payload_set_pane(ep, "pane", wp);
event_payload_set_pane(ep, "new_pane", wp);
if (lastwp != NULL)
event_payload_set_pane(ep, "old_pane", lastwp);
events_fire("window-pane-changed", ep);
}
static void
window_fire_pane_mode_changed(const char *name, struct window_pane *wp,
const char *previous, const char *current, int entered)
{
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);
if (current != NULL)
event_payload_set_string(ep, "current_mode", "%s", current);
if (previous != NULL)
event_payload_set_string(ep, "previous_mode", "%s", previous);
event_payload_set_int(ep, "mode_entered", entered);
events_fire(name, ep);
}
static void
window_fire_pane_prompt(const char *name, struct window_pane *wp,
enum prompt_type type)
{
struct event_payload *ep;
struct cmd_find_state fs;
const char *type_string = prompt_type_string(type);
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, "prompt_type", "%s", type_string);
events_fire(name, ep);
}
int
winlink_cmp(struct winlink *wl1, struct winlink *wl2)
{
@@ -444,13 +517,15 @@ window_pane_remove_ref(struct window_pane *wp, const char *from)
void
window_set_name(struct window *w, const char *new_name, int untrusted)
{
char *name;
char *last, *name;
name = clean_name(new_name, untrusted);
if (name != NULL) {
last = xstrdup(w->name);
free(w->name);
w->name = name;
notify_window("window-renamed", w);
window_fire_renamed(w, last);
free(last);
}
}
@@ -560,13 +635,13 @@ window_pane_update_focus(struct window_pane *wp)
log_debug("%s: %%%u focus out", __func__, wp->id);
if (wp->base.mode & MODE_FOCUSON)
bufferevent_write(wp->event, "\033[O", 3);
notify_pane("pane-focus-out", wp);
events_fire_pane("pane-focus-out", wp);
wp->flags &= ~PANE_FOCUSED;
} else if (focused && (~wp->flags & PANE_FOCUSED)) {
log_debug("%s: %%%u focus in", __func__, wp->id);
if (wp->base.mode & MODE_FOCUSON)
bufferevent_write(wp->event, "\033[I", 3);
notify_pane("pane-focus-in", wp);
events_fire_pane("pane-focus-in", wp);
wp->flags |= PANE_FOCUSED;
} else
log_debug("%s: %%%u focus unchanged", __func__, wp->id);
@@ -602,7 +677,7 @@ window_set_active_pane(struct window *w, struct window_pane *wp, int notify)
server_redraw_window(w);
if (notify)
notify_window("window-pane-changed", w);
window_fire_pane_changed(w, w->active, lastwp);
return (1);
}
@@ -792,7 +867,8 @@ window_zoom(struct window_pane *wp)
w->saved_layout_root = w->layout_root;
layout_init(w, wp);
w->flags |= WINDOW_ZOOMED;
notify_window("window-layout-changed", w);
events_fire_window("window-zoomed", w);
events_fire_window("window-layout-changed", w);
redraw_invalidate_scene(w);
return (0);
@@ -818,8 +894,10 @@ window_unzoom(struct window *w, int notify)
}
layout_fix_panes(w, NULL);
if (notify)
notify_window("window-layout-changed", w);
if (notify) {
events_fire_window("window-unzoomed", w);
events_fire_window("window-layout-changed", w);
}
redraw_invalidate_scene(w);
return (0);
@@ -901,7 +979,7 @@ window_lost_pane(struct window *w, struct window_pane *wp)
if (w->active != NULL) {
window_pane_stack_remove(&w->last_panes, w->active);
w->active->flags |= PANE_CHANGED;
notify_window("window-pane-changed", w);
events_fire_window("window-pane-changed", w);
window_update_focus(w);
}
}
@@ -1106,6 +1184,7 @@ window_pane_create(struct window *w, u_int sx, u_int sy, u_int hlimit)
wp->window = w;
wp->options = options_create(w->options);
wp->flags = PANE_STYLECHANGED;
wp->cmd_status = -1;
wp->id = next_window_pane_id++;
RB_INSERT(window_pane_tree, &all_window_panes, wp);
@@ -1230,9 +1309,9 @@ window_pane_destroy(struct window_pane *wp)
window_pane_wait_finish(wp);
spawn_editor_finish(wp);
window_pane_clear_prompt(wp);
RB_REMOVE(window_pane_tree, &all_window_panes, wp);
wp->flags |= PANE_DESTROYED;
window_pane_clear_prompt(wp);
window_pane_free_modes(wp);
screen_write_clear_dirty(wp);
@@ -1363,6 +1442,8 @@ window_pane_resize(struct window_pane *wp, u_int sx, u_int sy)
{
struct window_mode_entry *wme;
struct window_pane_resize *r;
struct event_payload *ep;
struct cmd_find_state fs;
if (sx == wp->sx && sy == wp->sy)
return;
@@ -1385,6 +1466,17 @@ window_pane_resize(struct window_pane *wp, u_int sx, u_int sy)
wme = TAILQ_FIRST(&wp->modes);
if (wme != NULL && wme->mode->resize != NULL)
wme->mode->resize(wme, sx, sy);
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_uint(ep, "width", sx);
event_payload_set_uint(ep, "height", sy);
event_payload_set_uint(ep, "old_width", r->osx);
event_payload_set_uint(ep, "old_height", r->osy);
events_fire("pane-resized", ep);
}
int
@@ -1394,9 +1486,13 @@ window_pane_set_mode(struct window_pane *wp, struct window_pane *swp,
{
struct window_mode_entry *wme;
struct window *w = wp->window;
const char *name = mode->name, *p = NULL;
if (!TAILQ_EMPTY(&wp->modes) && TAILQ_FIRST(&wp->modes)->mode == mode)
return (1);
if (!TAILQ_EMPTY(&wp->modes)) {
if (TAILQ_FIRST(&wp->modes)->mode == mode)
return (1);
p = TAILQ_FIRST(&wp->modes)->mode->name;
}
TAILQ_FOREACH(wme, &wp->modes, entry) {
if (wme->mode == mode)
@@ -1422,7 +1518,9 @@ window_pane_set_mode(struct window_pane *wp, struct window_pane *swp,
server_redraw_window_borders(wp->window);
server_status_window(wp->window);
notify_pane("pane-mode-changed", wp);
window_fire_pane_mode_changed("pane-mode-entered", wp, p, name, 1);
window_fire_pane_mode_changed("pane-mode-changed", wp, p, name, 1);
return (0);
}
@@ -1433,11 +1531,13 @@ window_pane_reset_mode(struct window_pane *wp)
struct window_mode_entry *wme, *next;
struct window *w = wp->window;
int kill;
const char *name, *p;
if (TAILQ_EMPTY(&wp->modes))
return;
wme = TAILQ_FIRST(&wp->modes);
p = wme->mode->name;
kill = wme->kill;
TAILQ_REMOVE(&wp->modes, wme, entry);
wme->mode->free(wme);
@@ -1454,13 +1554,16 @@ window_pane_reset_mode(struct window_pane *wp)
if (next->mode->resize != NULL)
next->mode->resize(next, wp->sx, wp->sy);
}
name = (next == NULL ? NULL : next->mode->name);
wp->flags |= (PANE_REDRAW|PANE_REDRAWSCROLLBAR|PANE_CHANGED);
layout_fix_panes(w, NULL);
server_redraw_window_borders(wp->window);
server_status_window(wp->window);
notify_pane("pane-mode-changed", wp);
window_fire_pane_mode_changed("pane-mode-exited", wp, p, name, 0);
window_fire_pane_mode_changed("pane-mode-changed", wp, p, name, 0);
if (kill)
server_kill_pane(wp);
@@ -1523,6 +1626,7 @@ window_pane_set_prompt(struct window_pane *wp, struct client *c,
wpp->inputcb = inputcb;
wpp->freecb = freecb;
wpp->data = data;
wpp->type = type;
memset(&pd, 0, sizeof pd);
prompt_set_options(&pd, s);
@@ -1540,19 +1644,28 @@ window_pane_set_prompt(struct window_pane *wp, struct client *c,
wp->flags |= PANE_REDRAW;
prompt_incremental_start(wp->prompt);
window_fire_pane_prompt("pane-prompt-opened", wp, type);
}
/* Close a pane prompt. */
void
window_pane_clear_prompt(struct window_pane *wp)
{
struct prompt *prompt = wp->prompt;
struct prompt *prompt = wp->prompt;
struct window_pane_prompt *wpp = wp->prompt_data;
enum prompt_type type = PROMPT_TYPE_INVALID;
if (prompt == NULL)
return;
wp->prompt = NULL;
prompt_free(prompt);
wp->flags |= PANE_REDRAW;
if (prompt != NULL) {
if (wpp != NULL)
type = wpp->type;
wp->prompt = NULL;
prompt_free(prompt);
wp->flags |= PANE_REDRAW;
if (~wp->flags & PANE_DESTROYED)
window_fire_pane_prompt("pane-prompt-closed", wp, type);
}
}
/* Does this pane have an open prompt? */