mirror of
https://github.com/tmux/tmux.git
synced 2026-08-03 14:08:59 +00:00
Break up notify.c so control and hooks are separate.
This commit is contained in:
@@ -168,6 +168,7 @@ dist_tmux_SOURCES = \
|
||||
grid-view.c \
|
||||
grid.c \
|
||||
hyperlinks.c \
|
||||
hooks.c \
|
||||
input-keys.c \
|
||||
input.c \
|
||||
job.c \
|
||||
|
||||
@@ -130,7 +130,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 +152,7 @@ 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);
|
||||
hooks_monitor_add(item, oo, name, type, id, format, &fs, target->s);
|
||||
|
||||
out:
|
||||
free(newvalue);
|
||||
@@ -197,7 +197,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);
|
||||
}
|
||||
|
||||
@@ -212,7 +212,7 @@ cmd_show_hooks_print_monitor(struct cmdq_item *item, struct options_entry *o)
|
||||
{
|
||||
char *value;
|
||||
|
||||
value = notify_monitor_to_string(o);
|
||||
value = hooks_monitor_to_string(o);
|
||||
if (value == NULL)
|
||||
return;
|
||||
cmdq_print(item, "%s", value);
|
||||
|
||||
177
control-notify.c
177
control-notify.c
@@ -23,31 +23,40 @@
|
||||
|
||||
#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)->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, void *data,
|
||||
__unused struct events_type *type, __unused void *sink_data)
|
||||
{
|
||||
struct client *c;
|
||||
struct notify_entry *ne = data;
|
||||
struct client *c;
|
||||
|
||||
TAILQ_FOREACH(c, &clients, entry) {
|
||||
if (!CONTROL_SHOULD_NOTIFY_CLIENT(c))
|
||||
continue;
|
||||
|
||||
control_write(c, "%%pane-mode-changed %%%u", pane);
|
||||
control_write(c, "%%pane-mode-changed %%%u", ne->pane);
|
||||
}
|
||||
}
|
||||
|
||||
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, void *data,
|
||||
__unused struct events_type *type, __unused void *sink_data)
|
||||
{
|
||||
struct client *c;
|
||||
struct session *s;
|
||||
struct winlink *wl;
|
||||
const char *template;
|
||||
char *cp;
|
||||
struct notify_entry *ne = data;
|
||||
struct client *c;
|
||||
struct session *s;
|
||||
struct winlink *wl;
|
||||
struct window *w = ne->window;
|
||||
const char *template;
|
||||
char *cp;
|
||||
|
||||
template = "%layout-change #{window_id} #{window_layout} "
|
||||
"#{window_visible_layout} #{window_raw_flags}";
|
||||
@@ -72,10 +81,14 @@ 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, void *data,
|
||||
__unused struct events_type *type, __unused void *sink_data)
|
||||
{
|
||||
struct notify_entry *ne = data;
|
||||
struct client *c;
|
||||
struct window *w = ne->window;
|
||||
|
||||
if (w->active == NULL)
|
||||
return;
|
||||
@@ -88,11 +101,15 @@ 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, void *data,
|
||||
__unused struct events_type *type, __unused void *sink_data)
|
||||
{
|
||||
struct client *c;
|
||||
struct session *cs;
|
||||
struct notify_entry *ne = data;
|
||||
struct client *c;
|
||||
struct session *cs;
|
||||
struct window *w = ne->window;
|
||||
|
||||
TAILQ_FOREACH(c, &clients, entry) {
|
||||
if (!CONTROL_SHOULD_NOTIFY_CLIENT(c) || c->session == NULL)
|
||||
@@ -106,11 +123,15 @@ 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, void *data,
|
||||
__unused struct events_type *type, __unused void *sink_data)
|
||||
{
|
||||
struct client *c;
|
||||
struct session *cs;
|
||||
struct notify_entry *ne = data;
|
||||
struct client *c;
|
||||
struct session *cs;
|
||||
struct window *w = ne->window;
|
||||
|
||||
TAILQ_FOREACH(c, &clients, entry) {
|
||||
if (!CONTROL_SHOULD_NOTIFY_CLIENT(c) || c->session == NULL)
|
||||
@@ -124,11 +145,15 @@ 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, void *data,
|
||||
__unused struct events_type *type, __unused void *sink_data)
|
||||
{
|
||||
struct client *c;
|
||||
struct session *cs;
|
||||
struct notify_entry *ne = data;
|
||||
struct client *c;
|
||||
struct session *cs;
|
||||
struct window *w = ne->window;
|
||||
|
||||
TAILQ_FOREACH(c, &clients, entry) {
|
||||
if (!CONTROL_SHOULD_NOTIFY_CLIENT(c) || c->session == NULL)
|
||||
@@ -145,11 +170,15 @@ 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, void *data,
|
||||
__unused struct events_type *type, __unused void *sink_data)
|
||||
{
|
||||
struct client *c;
|
||||
struct session *s;
|
||||
struct notify_entry *ne = data;
|
||||
struct client *cc = ne->client;
|
||||
struct client *c;
|
||||
struct session *s;
|
||||
|
||||
if (cc->session == NULL)
|
||||
return;
|
||||
@@ -169,10 +198,14 @@ 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, void *data,
|
||||
__unused struct events_type *type, __unused void *sink_data)
|
||||
{
|
||||
struct client *c;
|
||||
struct notify_entry *ne = data;
|
||||
struct client *cc = ne->client;
|
||||
struct client *c;
|
||||
|
||||
TAILQ_FOREACH(c, &clients, entry) {
|
||||
if (CONTROL_SHOULD_NOTIFY_CLIENT(c))
|
||||
@@ -180,10 +213,14 @@ control_notify_client_detached(struct client *cc)
|
||||
}
|
||||
}
|
||||
|
||||
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, void *data,
|
||||
__unused struct events_type *type, __unused void *sink_data)
|
||||
{
|
||||
struct client *c;
|
||||
struct notify_entry *ne = data;
|
||||
struct session *s = ne->session;
|
||||
struct client *c;
|
||||
|
||||
TAILQ_FOREACH(c, &clients, entry) {
|
||||
if (!CONTROL_SHOULD_NOTIFY_CLIENT(c))
|
||||
@@ -193,8 +230,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 void *data,
|
||||
__unused struct events_type *type, __unused void *sink_data)
|
||||
{
|
||||
struct client *c;
|
||||
|
||||
@@ -206,8 +245,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 void *data,
|
||||
__unused struct events_type *type, __unused void *sink_data)
|
||||
{
|
||||
struct client *c;
|
||||
|
||||
@@ -219,9 +260,13 @@ 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, void *data,
|
||||
__unused struct events_type *type, __unused void *sink_data)
|
||||
{
|
||||
struct notify_entry *ne = data;
|
||||
struct session *s = ne->session;
|
||||
struct client *c;
|
||||
|
||||
/*
|
||||
@@ -241,28 +286,64 @@ 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, void *data,
|
||||
__unused struct events_type *type, __unused void *sink_data)
|
||||
{
|
||||
struct notify_entry *ne = data;
|
||||
struct client *c;
|
||||
|
||||
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", ne->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, void *data,
|
||||
__unused struct events_type *type, __unused void *sink_data)
|
||||
{
|
||||
struct notify_entry *ne = data;
|
||||
struct client *c;
|
||||
|
||||
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", ne->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);
|
||||
}
|
||||
|
||||
2
events.c
2
events.c
@@ -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
|
||||
|
||||
439
hooks.c
Normal file
439
hooks.c
Normal file
@@ -0,0 +1,439 @@
|
||||
/* $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 monitor event data passed through events_fire. */
|
||||
struct hook_monitor_event {
|
||||
struct hook_monitor *hm;
|
||||
struct monitor_change *change;
|
||||
};
|
||||
|
||||
/* Hook event sink registered for a notify event name. */
|
||||
struct hooks_event {
|
||||
char *name;
|
||||
struct events_sink *sink;
|
||||
TAILQ_ENTRY(hooks_event) entry;
|
||||
};
|
||||
static TAILQ_HEAD(, hooks_event) hooks_events =
|
||||
TAILQ_HEAD_INITIALIZER(hooks_events);
|
||||
|
||||
/* Insert one hook command list. */
|
||||
static struct cmdq_item *
|
||||
hooks_insert_one(struct cmdq_item *item, struct notify_entry *ne,
|
||||
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__, ne->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 notify_entry *ne, struct cmd_find_state *fs,
|
||||
const char *value)
|
||||
{
|
||||
struct cmd_parse_result *pr;
|
||||
struct format_tree *ft;
|
||||
char *expanded;
|
||||
|
||||
if (!ne->expand)
|
||||
return (cmd_parse_from_string(value, NULL));
|
||||
|
||||
ft = format_create_defaults(NULL, ne->client, fs->s, fs->wl, fs->wp);
|
||||
if (ne->formats != NULL)
|
||||
format_merge(ft, ne->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 notify_entry *ne)
|
||||
{
|
||||
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__, ne->name);
|
||||
|
||||
cmd_find_clear_state(&fs, 0);
|
||||
if (cmd_find_empty_state(&ne->fs) || !cmd_find_valid_state(&ne->fs))
|
||||
cmd_find_from_nothing(&fs, 0);
|
||||
else
|
||||
cmd_find_copy_state(&fs, &ne->fs);
|
||||
|
||||
if (ne->oo != NULL) {
|
||||
oo = ne->oo;
|
||||
o = options_get_only(oo, ne->name);
|
||||
} else {
|
||||
if (fs.s == NULL)
|
||||
oo = global_s_options;
|
||||
else
|
||||
oo = fs.s->options;
|
||||
o = options_get(oo, ne->name);
|
||||
if (o == NULL && fs.wp != NULL) {
|
||||
oo = fs.wp->options;
|
||||
o = options_get(oo, ne->name);
|
||||
}
|
||||
if (o == NULL && fs.wl != NULL) {
|
||||
oo = fs.wl->window->options;
|
||||
o = options_get(oo, ne->name);
|
||||
}
|
||||
}
|
||||
if (o == NULL) {
|
||||
log_debug("%s: hook %s not found", __func__, ne->name);
|
||||
return;
|
||||
}
|
||||
|
||||
state = cmdq_new_state(&fs, NULL, CMDQ_STATE_NOHOOKS);
|
||||
cmdq_add_formats(state, ne->formats);
|
||||
|
||||
if (*ne->name == '@') {
|
||||
value = options_get_string(oo, ne->name);
|
||||
pr = hooks_parse(ne, &fs, value);
|
||||
switch (pr->status) {
|
||||
case CMD_PARSE_ERROR:
|
||||
log_debug("%s: can't parse hook %s: %s", __func__,
|
||||
ne->name, pr->error);
|
||||
free(pr->error);
|
||||
break;
|
||||
case CMD_PARSE_SUCCESS:
|
||||
hooks_insert_one(item, ne, pr->cmdlist, state);
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
a = options_array_first(o);
|
||||
while (a != NULL) {
|
||||
if (ne->expand) {
|
||||
value = options_array_item_value(a)->string;
|
||||
pr = hooks_parse(ne, &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, ne,
|
||||
pr->cmdlist, state);
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
cmdlist = options_array_item_value(a)->cmdlist;
|
||||
item = hooks_insert_one(item, ne, 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 events_type *type, void *data, struct options *oo, int expand)
|
||||
{
|
||||
struct cmd_find_state fs;
|
||||
struct notify_entry ne;
|
||||
struct format_tree *ft;
|
||||
struct client *c;
|
||||
|
||||
if (item != NULL && (cmdq_get_flags(item) & CMDQ_STATE_NOHOOKS))
|
||||
return;
|
||||
|
||||
cmd_find_clear_state(&fs, 0);
|
||||
if (!events_find_state(type, data, &fs) ||
|
||||
cmd_find_empty_state(&fs) || !cmd_find_valid_state(&fs))
|
||||
cmd_find_from_nothing(&fs, 0);
|
||||
|
||||
c = events_get_client(type, data);
|
||||
ft = format_create(c, item, FORMAT_NONE, FORMAT_NOJOBS);
|
||||
events_add_formats(type, data, ft);
|
||||
|
||||
memset(&ne, 0, sizeof ne);
|
||||
ne.name = name;
|
||||
cmd_find_copy_state(&ne.fs, &fs);
|
||||
ne.formats = ft;
|
||||
ne.oo = oo;
|
||||
ne.client = c;
|
||||
ne.expand = expand;
|
||||
|
||||
hooks_insert(item, &ne);
|
||||
format_free(ft);
|
||||
}
|
||||
|
||||
/* Handle an event for hooks. */
|
||||
static void
|
||||
hooks_event_cb(const char *name, void *data, struct events_type *type,
|
||||
__unused void *sink_data)
|
||||
{
|
||||
hooks_insert_event(cmdq_running(NULL), name, type, data, 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);
|
||||
}
|
||||
|
||||
/* Run a hook immediately. */
|
||||
void
|
||||
hooks_run(struct cmdq_item *item, const char *name)
|
||||
{
|
||||
struct cmd_find_state *target = cmdq_get_target(item);
|
||||
struct notify_entry ne;
|
||||
|
||||
memset(&ne, 0, sizeof ne);
|
||||
|
||||
ne.name = name;
|
||||
cmd_find_copy_state(&ne.fs, target);
|
||||
|
||||
ne.client = cmdq_get_client(item);
|
||||
ne.session = target->s;
|
||||
ne.window = target->w;
|
||||
ne.pane = (target->wp != NULL ? (int)target->wp->id : -1);
|
||||
|
||||
ne.formats = format_create(NULL, NULL, 0, FORMAT_NOJOBS);
|
||||
format_add(ne.formats, "hook", "%s", name);
|
||||
format_log_debug(ne.formats, __func__);
|
||||
|
||||
hooks_insert(item, &ne);
|
||||
format_free(ne.formats);
|
||||
}
|
||||
|
||||
/* Add formats for a hook monitor event. */
|
||||
static void
|
||||
hooks_monitor_event_add_formats(void *data, struct format_tree *ft)
|
||||
{
|
||||
struct hook_monitor_event *hme = data;
|
||||
struct monitor_change *change = hme->change;
|
||||
struct window *w;
|
||||
|
||||
format_add(ft, "hook", "%s", change->name);
|
||||
format_add(ft, "hook_value", "%s",
|
||||
change->value == NULL ? "" : change->value);
|
||||
format_add(ft, "hook_last", "%s",
|
||||
change->last == NULL ? "" : change->last);
|
||||
if (change->s != NULL) {
|
||||
format_add(ft, "hook_session", "$%u", change->s->id);
|
||||
format_add(ft, "hook_session_name", "%s", change->s->name);
|
||||
}
|
||||
if (change->wl != NULL) {
|
||||
w = change->wl->window;
|
||||
format_add(ft, "hook_window", "@%u", w->id);
|
||||
format_add(ft, "hook_window_name", "%s", w->name);
|
||||
format_add(ft, "hook_window_index", "%d", change->wl->idx);
|
||||
}
|
||||
if (change->wp != NULL)
|
||||
format_add(ft, "hook_pane", "%%%u", change->wp->id);
|
||||
}
|
||||
|
||||
/* Find state for a hook monitor event. */
|
||||
static int
|
||||
hooks_monitor_event_find_state(void *data, struct cmd_find_state *fs)
|
||||
{
|
||||
struct hook_monitor_event *hme = data;
|
||||
struct hook_monitor *hm = hme->hm;
|
||||
struct monitor_change *change = hme->change;
|
||||
|
||||
if (change->wp != NULL && change->wl != NULL)
|
||||
cmd_find_from_winlink_pane(fs, change->wl, change->wp, 0);
|
||||
else if (change->wl != NULL)
|
||||
cmd_find_from_winlink(fs, change->wl, 0);
|
||||
else if (change->s != NULL)
|
||||
cmd_find_from_session(fs, change->s, 0);
|
||||
else
|
||||
cmd_find_copy_state(fs, &hm->fs);
|
||||
return (1);
|
||||
}
|
||||
|
||||
/* Get client for a hook monitor event. */
|
||||
static struct client *
|
||||
hooks_monitor_event_get_client(void *data)
|
||||
{
|
||||
struct hook_monitor_event *hme = data;
|
||||
struct monitor_change *change = hme->change;
|
||||
|
||||
return (change->c);
|
||||
}
|
||||
|
||||
/* 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, void *data, struct events_type *type,
|
||||
void *sink_data)
|
||||
{
|
||||
struct hook_monitor_event *hme = data;
|
||||
struct hook_monitor *hm = sink_data;
|
||||
|
||||
if (hme->hm != hm)
|
||||
return;
|
||||
|
||||
hooks_insert_event(cmdq_running(NULL), name, type, data, hm->oo, 1);
|
||||
}
|
||||
|
||||
/* Fire a hook monitor event. */
|
||||
static void
|
||||
hooks_monitor_cb(struct monitor_change *change, void *data)
|
||||
{
|
||||
struct hook_monitor_event hme;
|
||||
|
||||
hme.hm = data;
|
||||
hme.change = change;
|
||||
events_fire(change->name, &hme);
|
||||
}
|
||||
|
||||
/* 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,
|
||||
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);
|
||||
events_add_event(name, hooks_monitor_event_add_formats,
|
||||
hooks_monitor_event_find_state, hooks_monitor_event_get_client);
|
||||
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, 0);
|
||||
}
|
||||
|
||||
/* 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);
|
||||
}
|
||||
@@ -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
|
||||
|
||||
570
notify.c
570
notify.c
@@ -23,212 +23,9 @@
|
||||
|
||||
#include "tmux.h"
|
||||
|
||||
struct notify_entry {
|
||||
const char *name;
|
||||
struct cmd_find_state fs;
|
||||
struct format_tree *formats;
|
||||
struct options *oo;
|
||||
|
||||
struct client *client;
|
||||
struct session *session;
|
||||
struct window *window;
|
||||
int pane;
|
||||
const char *pbname;
|
||||
int expand;
|
||||
};
|
||||
|
||||
struct notify_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;
|
||||
};
|
||||
|
||||
struct notify_monitor_event {
|
||||
struct notify_monitor *nhm;
|
||||
struct monitor_change *change;
|
||||
};
|
||||
|
||||
struct notify_event_entry {
|
||||
const char *name;
|
||||
void (*control_cb)(struct notify_entry *);
|
||||
};
|
||||
|
||||
static void notify_event_hook_cb(const char *, void *, struct events_type *,
|
||||
void *);
|
||||
static void notify_event_control_cb(const char *, void *,
|
||||
struct events_type *, void *);
|
||||
static void notify_register_events(void);
|
||||
|
||||
static struct cmdq_item *
|
||||
notify_insert_one_hook(struct cmdq_item *item, struct notify_entry *ne,
|
||||
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__, ne->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));
|
||||
}
|
||||
|
||||
static struct cmd_parse_result *
|
||||
notify_parse_hook(struct notify_entry *ne, struct cmd_find_state *fs,
|
||||
const char *value)
|
||||
{
|
||||
struct cmd_parse_result *pr;
|
||||
struct format_tree *ft;
|
||||
char *expanded;
|
||||
|
||||
if (!ne->expand)
|
||||
return (cmd_parse_from_string(value, NULL));
|
||||
|
||||
ft = format_create_defaults(NULL, ne->client, fs->s, fs->wl, fs->wp);
|
||||
if (ne->formats != NULL)
|
||||
format_merge(ft, ne->formats);
|
||||
expanded = format_expand(ft, value);
|
||||
format_free(ft);
|
||||
|
||||
pr = cmd_parse_from_string(expanded, NULL);
|
||||
free(expanded);
|
||||
return (pr);
|
||||
}
|
||||
|
||||
/* Add formats for a notify event. */
|
||||
static void
|
||||
notify_insert_hook(struct cmdq_item *item, struct notify_entry *ne)
|
||||
{
|
||||
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__, ne->name);
|
||||
|
||||
cmd_find_clear_state(&fs, 0);
|
||||
if (cmd_find_empty_state(&ne->fs) || !cmd_find_valid_state(&ne->fs))
|
||||
cmd_find_from_nothing(&fs, 0);
|
||||
else
|
||||
cmd_find_copy_state(&fs, &ne->fs);
|
||||
|
||||
if (ne->oo != NULL) {
|
||||
oo = ne->oo;
|
||||
o = options_get_only(oo, ne->name);
|
||||
} else {
|
||||
if (fs.s == NULL)
|
||||
oo = global_s_options;
|
||||
else
|
||||
oo = fs.s->options;
|
||||
o = options_get(oo, ne->name);
|
||||
if (o == NULL && fs.wp != NULL) {
|
||||
oo = fs.wp->options;
|
||||
o = options_get(oo, ne->name);
|
||||
}
|
||||
if (o == NULL && fs.wl != NULL) {
|
||||
oo = fs.wl->window->options;
|
||||
o = options_get(oo, ne->name);
|
||||
}
|
||||
}
|
||||
if (o == NULL) {
|
||||
log_debug("%s: hook %s not found", __func__, ne->name);
|
||||
return;
|
||||
}
|
||||
|
||||
state = cmdq_new_state(&fs, NULL, CMDQ_STATE_NOHOOKS);
|
||||
cmdq_add_formats(state, ne->formats);
|
||||
|
||||
if (*ne->name == '@') {
|
||||
value = options_get_string(oo, ne->name);
|
||||
pr = notify_parse_hook(ne, &fs, value);
|
||||
switch (pr->status) {
|
||||
case CMD_PARSE_ERROR:
|
||||
log_debug("%s: can't parse hook %s: %s", __func__,
|
||||
ne->name, pr->error);
|
||||
free(pr->error);
|
||||
break;
|
||||
case CMD_PARSE_SUCCESS:
|
||||
notify_insert_one_hook(item, ne, pr->cmdlist, state);
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
a = options_array_first(o);
|
||||
while (a != NULL) {
|
||||
if (ne->expand) {
|
||||
value = options_array_item_value(a)->string;
|
||||
pr = notify_parse_hook(ne, &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 = notify_insert_one_hook(item, ne,
|
||||
pr->cmdlist, state);
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
cmdlist = options_array_item_value(a)->cmdlist;
|
||||
item = notify_insert_one_hook(item, ne, cmdlist,
|
||||
state);
|
||||
}
|
||||
a = options_array_next(a);
|
||||
}
|
||||
}
|
||||
|
||||
cmdq_free_state(state);
|
||||
}
|
||||
|
||||
static void
|
||||
notify_event_insert_hook(struct cmdq_item *item, const char *name,
|
||||
struct events_type *type, void *data, struct options *oo, int expand)
|
||||
{
|
||||
struct cmd_find_state fs;
|
||||
struct notify_entry ne;
|
||||
struct format_tree *ft;
|
||||
struct client *c;
|
||||
|
||||
if (item != NULL && (cmdq_get_flags(item) & CMDQ_STATE_NOHOOKS))
|
||||
return;
|
||||
|
||||
cmd_find_clear_state(&fs, 0);
|
||||
if (!events_find_state(type, data, &fs) ||
|
||||
cmd_find_empty_state(&fs) || !cmd_find_valid_state(&fs))
|
||||
cmd_find_from_nothing(&fs, 0);
|
||||
|
||||
c = events_get_client(type, data);
|
||||
ft = format_create(c, item, FORMAT_NONE, FORMAT_NOJOBS);
|
||||
events_add_formats(type, data, ft);
|
||||
|
||||
memset(&ne, 0, sizeof ne);
|
||||
ne.name = name;
|
||||
cmd_find_copy_state(&ne.fs, &fs);
|
||||
ne.formats = ft;
|
||||
ne.oo = oo;
|
||||
ne.client = c;
|
||||
ne.expand = expand;
|
||||
|
||||
notify_insert_hook(item, &ne);
|
||||
format_free(ft);
|
||||
}
|
||||
|
||||
static void
|
||||
notify_event_add_formats(void *data, struct format_tree *ft)
|
||||
notify_add_formats(void *data, struct format_tree *ft)
|
||||
{
|
||||
struct notify_entry *ne = data;
|
||||
|
||||
@@ -236,8 +33,9 @@ notify_event_add_formats(void *data, struct format_tree *ft)
|
||||
format_merge(ft, ne->formats);
|
||||
}
|
||||
|
||||
/* Find state for a notify event. */
|
||||
static int
|
||||
notify_event_find_state(void *data, struct cmd_find_state *fs)
|
||||
notify_find_state(void *data, struct cmd_find_state *fs)
|
||||
{
|
||||
struct notify_entry *ne = data;
|
||||
|
||||
@@ -247,223 +45,25 @@ notify_event_find_state(void *data, struct cmd_find_state *fs)
|
||||
return (1);
|
||||
}
|
||||
|
||||
/* Get client for a notify event. */
|
||||
static struct client *
|
||||
notify_event_get_client(void *data)
|
||||
notify_get_client(void *data)
|
||||
{
|
||||
struct notify_entry *ne = data;
|
||||
|
||||
return (ne->client);
|
||||
}
|
||||
|
||||
/* Build a notify event type. */
|
||||
static void
|
||||
notify_monitor_event_add_formats(void *data, struct format_tree *ft)
|
||||
notify_build_event(const char *name)
|
||||
{
|
||||
struct notify_monitor_event *nme = data;
|
||||
struct monitor_change *change = nme->change;
|
||||
struct window *w;
|
||||
|
||||
format_add(ft, "hook", "%s", change->name);
|
||||
format_add(ft, "hook_value", "%s",
|
||||
change->value == NULL ? "" : change->value);
|
||||
format_add(ft, "hook_last", "%s",
|
||||
change->last == NULL ? "" : change->last);
|
||||
if (change->s != NULL) {
|
||||
format_add(ft, "hook_session", "$%u", change->s->id);
|
||||
format_add(ft, "hook_session_name", "%s", change->s->name);
|
||||
}
|
||||
if (change->wl != NULL) {
|
||||
w = change->wl->window;
|
||||
format_add(ft, "hook_window", "@%u", w->id);
|
||||
format_add(ft, "hook_window_name", "%s", w->name);
|
||||
format_add(ft, "hook_window_index", "%d", change->wl->idx);
|
||||
}
|
||||
if (change->wp != NULL)
|
||||
format_add(ft, "hook_pane", "%%%u", change->wp->id);
|
||||
}
|
||||
|
||||
static int
|
||||
notify_monitor_event_find_state(void *data, struct cmd_find_state *fs)
|
||||
{
|
||||
struct notify_monitor_event *nme = data;
|
||||
struct notify_monitor *nhm = nme->nhm;
|
||||
struct monitor_change *change = nme->change;
|
||||
|
||||
if (change->wp != NULL && change->wl != NULL)
|
||||
cmd_find_from_winlink_pane(fs, change->wl, change->wp, 0);
|
||||
else if (change->wl != NULL)
|
||||
cmd_find_from_winlink(fs, change->wl, 0);
|
||||
else if (change->s != NULL)
|
||||
cmd_find_from_session(fs, change->s, 0);
|
||||
else
|
||||
cmd_find_copy_state(fs, &nhm->fs);
|
||||
return (1);
|
||||
}
|
||||
|
||||
static struct client *
|
||||
notify_monitor_event_get_client(void *data)
|
||||
{
|
||||
struct notify_monitor_event *nme = data;
|
||||
struct monitor_change *change = nme->change;
|
||||
|
||||
return (change->c);
|
||||
}
|
||||
|
||||
static void
|
||||
notify_control_pane_mode_changed(struct notify_entry *ne)
|
||||
{
|
||||
control_notify_pane_mode_changed(ne->pane);
|
||||
}
|
||||
|
||||
static void
|
||||
notify_control_window_layout_changed(struct notify_entry *ne)
|
||||
{
|
||||
control_notify_window_layout_changed(ne->window);
|
||||
}
|
||||
|
||||
static void
|
||||
notify_control_window_pane_changed(struct notify_entry *ne)
|
||||
{
|
||||
control_notify_window_pane_changed(ne->window);
|
||||
}
|
||||
|
||||
static void
|
||||
notify_control_window_unlinked(struct notify_entry *ne)
|
||||
{
|
||||
control_notify_window_unlinked(ne->session, ne->window);
|
||||
}
|
||||
|
||||
static void
|
||||
notify_control_window_linked(struct notify_entry *ne)
|
||||
{
|
||||
control_notify_window_linked(ne->session, ne->window);
|
||||
}
|
||||
|
||||
static void
|
||||
notify_control_window_renamed(struct notify_entry *ne)
|
||||
{
|
||||
control_notify_window_renamed(ne->window);
|
||||
}
|
||||
|
||||
static void
|
||||
notify_control_client_session_changed(struct notify_entry *ne)
|
||||
{
|
||||
control_notify_client_session_changed(ne->client);
|
||||
}
|
||||
|
||||
static void
|
||||
notify_control_client_detached(struct notify_entry *ne)
|
||||
{
|
||||
control_notify_client_detached(ne->client);
|
||||
}
|
||||
|
||||
static void
|
||||
notify_control_session_renamed(struct notify_entry *ne)
|
||||
{
|
||||
control_notify_session_renamed(ne->session);
|
||||
}
|
||||
|
||||
static void
|
||||
notify_control_session_created(struct notify_entry *ne)
|
||||
{
|
||||
control_notify_session_created(ne->session);
|
||||
}
|
||||
|
||||
static void
|
||||
notify_control_session_closed(struct notify_entry *ne)
|
||||
{
|
||||
control_notify_session_closed(ne->session);
|
||||
}
|
||||
|
||||
static void
|
||||
notify_control_session_window_changed(struct notify_entry *ne)
|
||||
{
|
||||
control_notify_session_window_changed(ne->session);
|
||||
}
|
||||
|
||||
static void
|
||||
notify_control_paste_buffer_changed(struct notify_entry *ne)
|
||||
{
|
||||
control_notify_paste_buffer_changed(ne->pbname);
|
||||
}
|
||||
|
||||
static void
|
||||
notify_control_paste_buffer_deleted(struct notify_entry *ne)
|
||||
{
|
||||
control_notify_paste_buffer_deleted(ne->pbname);
|
||||
}
|
||||
|
||||
static struct notify_event_entry notify_events[] = {
|
||||
{ "alert-activity", NULL },
|
||||
{ "alert-bell", NULL },
|
||||
{ "alert-silence", NULL },
|
||||
{ "client-active", NULL },
|
||||
{ "client-attached", NULL },
|
||||
{ "client-dark-theme", NULL },
|
||||
{ "client-focus-in", NULL },
|
||||
{ "client-focus-out", NULL },
|
||||
{ "client-light-theme", NULL },
|
||||
{ "client-resized", NULL },
|
||||
{ "pane-mode-changed", notify_control_pane_mode_changed },
|
||||
{ "pane-died", NULL },
|
||||
{ "pane-exited", NULL },
|
||||
{ "pane-focus-in", NULL },
|
||||
{ "pane-focus-out", NULL },
|
||||
{ "pane-set-clipboard", NULL },
|
||||
{ "pane-title-changed", NULL },
|
||||
{ "window-layout-changed", notify_control_window_layout_changed },
|
||||
{ "window-pane-changed", notify_control_window_pane_changed },
|
||||
{ "window-resized", NULL },
|
||||
{ "window-unlinked", notify_control_window_unlinked },
|
||||
{ "window-linked", notify_control_window_linked },
|
||||
{ "window-renamed", notify_control_window_renamed },
|
||||
{ "client-session-changed", notify_control_client_session_changed },
|
||||
{ "client-detached", notify_control_client_detached },
|
||||
{ "session-renamed", notify_control_session_renamed },
|
||||
{ "session-created", notify_control_session_created },
|
||||
{ "session-closed", notify_control_session_closed },
|
||||
{ "session-window-changed", notify_control_session_window_changed },
|
||||
{ "paste-buffer-changed", notify_control_paste_buffer_changed },
|
||||
{ "paste-buffer-deleted", notify_control_paste_buffer_deleted }
|
||||
};
|
||||
|
||||
static void
|
||||
notify_event_hook_cb(const char *name, void *data, struct events_type *type,
|
||||
__unused void *sink_data)
|
||||
{
|
||||
notify_event_insert_hook(cmdq_running(NULL), name, type, data, NULL, 0);
|
||||
}
|
||||
|
||||
static void
|
||||
notify_event_control_cb(__unused const char *name, void *data,
|
||||
__unused struct events_type *type, void *sink_data)
|
||||
{
|
||||
struct notify_event_entry *nee = sink_data;
|
||||
|
||||
nee->control_cb(data);
|
||||
}
|
||||
|
||||
static void
|
||||
notify_register_events(void)
|
||||
{
|
||||
static int done;
|
||||
u_int i;
|
||||
|
||||
if (done)
|
||||
return;
|
||||
done = 1;
|
||||
|
||||
for (i = 0; i < nitems(notify_events); i++) {
|
||||
events_add_event(notify_events[i].name, notify_event_add_formats,
|
||||
notify_event_find_state, notify_event_get_client);
|
||||
events_add_sink(notify_events[i].name, notify_event_hook_cb,
|
||||
NULL);
|
||||
if (notify_events[i].control_cb != NULL) {
|
||||
events_add_sink(notify_events[i].name,
|
||||
notify_event_control_cb, ¬ify_events[i]);
|
||||
}
|
||||
}
|
||||
events_add_event(name, notify_add_formats, notify_find_state,
|
||||
notify_get_client);
|
||||
hooks_add_event(name);
|
||||
}
|
||||
|
||||
/* Fire a queued notify event. */
|
||||
static enum cmd_retval
|
||||
notify_callback(__unused struct cmdq_item *item, void *data)
|
||||
{
|
||||
@@ -471,7 +71,7 @@ notify_callback(__unused struct cmdq_item *item, void *data)
|
||||
|
||||
log_debug("%s: %s", __func__, ne->name);
|
||||
|
||||
notify_register_events();
|
||||
notify_build_event(ne->name);
|
||||
events_fire(ne->name, ne);
|
||||
|
||||
if (ne->client != NULL)
|
||||
@@ -492,115 +92,7 @@ notify_callback(__unused struct cmdq_item *item, void *data)
|
||||
return (CMD_RETURN_NORMAL);
|
||||
}
|
||||
|
||||
void
|
||||
notify_monitor_free(void *data)
|
||||
{
|
||||
struct notify_monitor *nhm = data;
|
||||
|
||||
events_remove_sink(nhm->sink);
|
||||
monitor_destroy(nhm->set);
|
||||
free(nhm->format);
|
||||
free(nhm);
|
||||
}
|
||||
|
||||
void
|
||||
notify_monitor_remove(struct options *oo, const char *name)
|
||||
{
|
||||
struct options_entry *o;
|
||||
struct notify_monitor *nhm;
|
||||
|
||||
o = options_get_only(oo, name);
|
||||
if (o == NULL)
|
||||
return;
|
||||
|
||||
nhm = options_get_monitor_data(o);
|
||||
if (nhm != NULL) {
|
||||
options_set_monitor_data(o, NULL);
|
||||
notify_monitor_free(nhm);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
notify_monitor_hook_cb(const char *name, void *data, struct events_type *type,
|
||||
void *sink_data)
|
||||
{
|
||||
struct notify_monitor_event *nme = data;
|
||||
struct notify_monitor *nhm = sink_data;
|
||||
|
||||
if (nme->nhm != nhm)
|
||||
return;
|
||||
|
||||
notify_event_insert_hook(cmdq_running(NULL), name, type, data, nhm->oo, 1);
|
||||
}
|
||||
|
||||
static void
|
||||
notify_monitor_cb(struct monitor_change *change, void *data)
|
||||
{
|
||||
struct notify_monitor_event nme;
|
||||
|
||||
nme.nhm = data;
|
||||
nme.change = change;
|
||||
events_fire(change->name, &nme);
|
||||
}
|
||||
|
||||
void
|
||||
notify_monitor_add(__unused struct cmdq_item *item, struct options *oo,
|
||||
const char *name, enum monitor_type type, int id, const char *format,
|
||||
struct cmd_find_state *fs, struct session *s)
|
||||
{
|
||||
struct options_entry *o;
|
||||
struct notify_monitor *nhm;
|
||||
|
||||
notify_monitor_remove(oo, name);
|
||||
o = options_get_only(oo, name);
|
||||
if (o == NULL)
|
||||
o = options_set_string(oo, name, 0, "%s", "");
|
||||
|
||||
nhm = xcalloc(1, sizeof *nhm);
|
||||
nhm->oo = oo;
|
||||
cmd_find_copy_state(&nhm->fs, fs);
|
||||
nhm->type = type;
|
||||
nhm->id = id;
|
||||
nhm->format = xstrdup(format);
|
||||
nhm->set = monitor_create_session(s, notify_monitor_cb, nhm);
|
||||
events_add_event(name, notify_monitor_event_add_formats,
|
||||
notify_monitor_event_find_state, notify_monitor_event_get_client);
|
||||
nhm->sink = events_add_sink(name, notify_monitor_hook_cb, nhm);
|
||||
options_set_monitor_data(o, nhm);
|
||||
monitor_add(nhm->set, name, type, id, format, 0);
|
||||
}
|
||||
|
||||
/* Convert a hook monitor to its value. */
|
||||
char *
|
||||
notify_monitor_to_string(struct options_entry *o)
|
||||
{
|
||||
struct notify_monitor *nhm = options_get_monitor_data(o);
|
||||
const char *name = options_name(o);
|
||||
char *s;
|
||||
|
||||
if (nhm == NULL)
|
||||
return (NULL);
|
||||
|
||||
switch (nhm->type) {
|
||||
case MONITOR_SESSION:
|
||||
xasprintf(&s, "%s::%s", name, nhm->format);
|
||||
break;
|
||||
case MONITOR_PANE:
|
||||
xasprintf(&s, "%s:%%%d:%s", name, nhm->id, nhm->format);
|
||||
break;
|
||||
case MONITOR_ALL_PANES:
|
||||
xasprintf(&s, "%s:%%*:%s", name, nhm->format);
|
||||
break;
|
||||
case MONITOR_WINDOW:
|
||||
xasprintf(&s, "%s:@%d:%s", name, nhm->id, nhm->format);
|
||||
break;
|
||||
case MONITOR_ALL_WINDOWS:
|
||||
xasprintf(&s, "%s:@*:%s", name, nhm->format);
|
||||
break;
|
||||
}
|
||||
return (s);
|
||||
}
|
||||
|
||||
/* Queue a notify event. */
|
||||
static void
|
||||
notify_add(const char *name, struct cmd_find_state *fs, struct client *c,
|
||||
struct session *s, struct window *w, struct window_pane *wp,
|
||||
@@ -656,30 +148,6 @@ notify_add(const char *name, struct cmd_find_state *fs, struct client *c,
|
||||
cmdq_append(NULL, cmdq_get_callback(notify_callback, ne));
|
||||
}
|
||||
|
||||
void
|
||||
notify_hook(struct cmdq_item *item, const char *name)
|
||||
{
|
||||
struct cmd_find_state *target = cmdq_get_target(item);
|
||||
struct notify_entry ne;
|
||||
|
||||
memset(&ne, 0, sizeof ne);
|
||||
|
||||
ne.name = name;
|
||||
cmd_find_copy_state(&ne.fs, target);
|
||||
|
||||
ne.client = cmdq_get_client(item);
|
||||
ne.session = target->s;
|
||||
ne.window = target->w;
|
||||
ne.pane = (target->wp != NULL ? (int)target->wp->id : -1);
|
||||
|
||||
ne.formats = format_create(NULL, NULL, 0, FORMAT_NOJOBS);
|
||||
format_add(ne.formats, "hook", "%s", name);
|
||||
format_log_debug(ne.formats, __func__);
|
||||
|
||||
notify_insert_hook(item, &ne);
|
||||
format_free(ne.formats);
|
||||
}
|
||||
|
||||
void
|
||||
notify_client(const char *name, struct client *c)
|
||||
{
|
||||
@@ -740,14 +208,14 @@ notify_pane(const char *name, struct window_pane *wp)
|
||||
void
|
||||
notify_paste_buffer(const char *pbname, int deleted)
|
||||
{
|
||||
struct cmd_find_state fs;
|
||||
struct cmd_find_state fs;
|
||||
|
||||
cmd_find_clear_state(&fs, 0);
|
||||
if (deleted) {
|
||||
notify_add("paste-buffer-deleted", &fs, NULL, NULL, NULL, NULL,
|
||||
pbname);
|
||||
notify_add("paste-buffer-deleted", &fs, NULL, NULL, NULL,
|
||||
NULL, pbname);
|
||||
} else {
|
||||
notify_add("paste-buffer-changed", &fs, NULL, NULL, NULL, NULL,
|
||||
pbname);
|
||||
notify_add("paste-buffer-changed", &fs, NULL, NULL, NULL,
|
||||
NULL, pbname);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -356,7 +356,7 @@ options_remove(struct options_entry *o)
|
||||
else
|
||||
options_value_free(o, &o->value);
|
||||
if (o->monitor_data != NULL)
|
||||
notify_monitor_free(o->monitor_data);
|
||||
hooks_monitor_free(o->monitor_data);
|
||||
RB_REMOVE(options_tree, &oo->tree, o);
|
||||
free((void *)o->name);
|
||||
free(o);
|
||||
|
||||
0
regress/set-hook-B.sh
Executable file → Normal file
0
regress/set-hook-B.sh
Executable file → Normal file
1
server.c
1
server.c
@@ -214,6 +214,7 @@ server_start(struct tmuxproc *client, uint64_t flags, struct event_base *base,
|
||||
TAILQ_INIT(&clients);
|
||||
RB_INIT(&sessions);
|
||||
key_bindings_init();
|
||||
control_build_events();
|
||||
TAILQ_INIT(&message_log);
|
||||
gettimeofday(&start_time, NULL);
|
||||
|
||||
|
||||
45
tmux.h
45
tmux.h
@@ -2344,6 +2344,21 @@ typedef void (*events_add_formats_cb)(void *, struct format_tree *);
|
||||
typedef int (*events_find_state_cb)(void *, struct cmd_find_state *);
|
||||
typedef struct client *(*events_get_client_cb)(void *);
|
||||
|
||||
/* Data passed by notify producers to event sinks. */
|
||||
struct notify_entry {
|
||||
const char *name;
|
||||
struct cmd_find_state fs;
|
||||
struct format_tree *formats;
|
||||
struct options *oo;
|
||||
|
||||
struct client *client;
|
||||
struct session *session;
|
||||
struct window *window;
|
||||
int pane;
|
||||
const char *pbname;
|
||||
int expand;
|
||||
};
|
||||
|
||||
/* Key binding and key table. */
|
||||
struct key_binding {
|
||||
key_code key;
|
||||
@@ -2673,14 +2688,17 @@ u_int format_width(const char *);
|
||||
char *format_trim_left(const char *, u_int);
|
||||
char *format_trim_right(const char *, u_int);
|
||||
|
||||
/* notify.c */
|
||||
void notify_hook(struct cmdq_item *, const char *);
|
||||
void notify_monitor_add(struct cmdq_item *, struct options *,
|
||||
/* hooks.c */
|
||||
void hooks_add_event(const char *);
|
||||
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 hooks_monitor_remove(struct options *, const char *);
|
||||
void hooks_monitor_free(void *);
|
||||
char *hooks_monitor_to_string(struct options_entry *);
|
||||
|
||||
/* notify.c */
|
||||
void notify_client(const char *, struct client *);
|
||||
void notify_session(const char *, struct session *);
|
||||
void notify_winlink(const char *, struct winlink *);
|
||||
@@ -3914,20 +3932,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;
|
||||
|
||||
Reference in New Issue
Block a user