mirror of
https://github.com/tmux/tmux.git
synced 2026-07-08 18:39:30 +00:00
Merge branch 'obsd-master'
This commit is contained in:
@@ -47,7 +47,7 @@ static void
|
||||
cmd_refresh_client_update_subscription(struct client *tc, const char *value)
|
||||
{
|
||||
char *copy, *split, *name, *what;
|
||||
enum control_sub_type subtype;
|
||||
enum monitor_type subtype;
|
||||
int subid = -1;
|
||||
|
||||
copy = name = xstrdup(value);
|
||||
@@ -63,15 +63,15 @@ cmd_refresh_client_update_subscription(struct client *tc, const char *value)
|
||||
*split++ = '\0';
|
||||
|
||||
if (strcmp(what, "%*") == 0)
|
||||
subtype = CONTROL_SUB_ALL_PANES;
|
||||
subtype = MONITOR_ALL_PANES;
|
||||
else if (sscanf(what, "%%%d", &subid) == 1 && subid >= 0)
|
||||
subtype = CONTROL_SUB_PANE;
|
||||
subtype = MONITOR_PANE;
|
||||
else if (strcmp(what, "@*") == 0)
|
||||
subtype = CONTROL_SUB_ALL_WINDOWS;
|
||||
subtype = MONITOR_ALL_WINDOWS;
|
||||
else if (sscanf(what, "@%d", &subid) == 1 && subid >= 0)
|
||||
subtype = CONTROL_SUB_WINDOW;
|
||||
subtype = MONITOR_WINDOW;
|
||||
else
|
||||
subtype = CONTROL_SUB_SESSION;
|
||||
subtype = MONITOR_SESSION;
|
||||
control_add_sub(tc, name, subtype, subid, split);
|
||||
|
||||
out:
|
||||
|
||||
452
control.c
452
control.c
@@ -75,42 +75,6 @@ struct control_pane {
|
||||
};
|
||||
RB_HEAD(control_panes, control_pane);
|
||||
|
||||
/* Subscription pane. */
|
||||
struct control_sub_pane {
|
||||
u_int pane;
|
||||
u_int idx;
|
||||
char *last;
|
||||
|
||||
RB_ENTRY(control_sub_pane) entry;
|
||||
};
|
||||
RB_HEAD(control_sub_panes, control_sub_pane);
|
||||
|
||||
/* Subscription window. */
|
||||
struct control_sub_window {
|
||||
u_int window;
|
||||
u_int idx;
|
||||
char *last;
|
||||
|
||||
RB_ENTRY(control_sub_window) entry;
|
||||
};
|
||||
RB_HEAD(control_sub_windows, control_sub_window);
|
||||
|
||||
/* Control client subscription. */
|
||||
struct control_sub {
|
||||
char *name;
|
||||
char *format;
|
||||
|
||||
enum control_sub_type type;
|
||||
u_int id;
|
||||
|
||||
char *last;
|
||||
struct control_sub_panes panes;
|
||||
struct control_sub_windows windows;
|
||||
|
||||
RB_ENTRY(control_sub) entry;
|
||||
};
|
||||
RB_HEAD(control_subs, control_sub);
|
||||
|
||||
/* Control client state. */
|
||||
struct control_state {
|
||||
struct control_panes panes;
|
||||
@@ -123,8 +87,7 @@ struct control_state {
|
||||
struct bufferevent *read_event;
|
||||
struct bufferevent *write_event;
|
||||
|
||||
struct control_subs subs;
|
||||
struct event subs_timer;
|
||||
struct monitor_set *subs;
|
||||
};
|
||||
|
||||
/* Low and high watermarks. */
|
||||
@@ -154,75 +117,6 @@ control_pane_cmp(struct control_pane *cp1, struct control_pane *cp2)
|
||||
}
|
||||
RB_GENERATE_STATIC(control_panes, control_pane, entry, control_pane_cmp);
|
||||
|
||||
/* Compare client subs. */
|
||||
static int
|
||||
control_sub_cmp(struct control_sub *csub1, struct control_sub *csub2)
|
||||
{
|
||||
return (strcmp(csub1->name, csub2->name));
|
||||
}
|
||||
RB_GENERATE_STATIC(control_subs, control_sub, entry, control_sub_cmp);
|
||||
|
||||
/* Compare client subscription panes. */
|
||||
static int
|
||||
control_sub_pane_cmp(struct control_sub_pane *csp1,
|
||||
struct control_sub_pane *csp2)
|
||||
{
|
||||
if (csp1->pane < csp2->pane)
|
||||
return (-1);
|
||||
if (csp1->pane > csp2->pane)
|
||||
return (1);
|
||||
if (csp1->idx < csp2->idx)
|
||||
return (-1);
|
||||
if (csp1->idx > csp2->idx)
|
||||
return (1);
|
||||
return (0);
|
||||
}
|
||||
RB_GENERATE_STATIC(control_sub_panes, control_sub_pane, entry,
|
||||
control_sub_pane_cmp);
|
||||
|
||||
/* Compare client subscription windows. */
|
||||
static int
|
||||
control_sub_window_cmp(struct control_sub_window *csw1,
|
||||
struct control_sub_window *csw2)
|
||||
{
|
||||
if (csw1->window < csw2->window)
|
||||
return (-1);
|
||||
if (csw1->window > csw2->window)
|
||||
return (1);
|
||||
if (csw1->idx < csw2->idx)
|
||||
return (-1);
|
||||
if (csw1->idx > csw2->idx)
|
||||
return (1);
|
||||
return (0);
|
||||
}
|
||||
RB_GENERATE_STATIC(control_sub_windows, control_sub_window, entry,
|
||||
control_sub_window_cmp);
|
||||
|
||||
/* Free a subscription. */
|
||||
static void
|
||||
control_free_sub(struct control_state *cs, struct control_sub *csub)
|
||||
{
|
||||
struct control_sub_pane *csp, *csp1;
|
||||
struct control_sub_window *csw, *csw1;
|
||||
|
||||
RB_FOREACH_SAFE(csp, control_sub_panes, &csub->panes, csp1) {
|
||||
RB_REMOVE(control_sub_panes, &csub->panes, csp);
|
||||
free(csp->last);
|
||||
free(csp);
|
||||
}
|
||||
RB_FOREACH_SAFE(csw, control_sub_windows, &csub->windows, csw1) {
|
||||
RB_REMOVE(control_sub_windows, &csub->windows, csw);
|
||||
free(csw->last);
|
||||
free(csw);
|
||||
}
|
||||
free(csub->last);
|
||||
|
||||
RB_REMOVE(control_subs, &cs->subs, csub);
|
||||
free(csub->name);
|
||||
free(csub->format);
|
||||
free(csub);
|
||||
}
|
||||
|
||||
/* Free a block. */
|
||||
static void
|
||||
control_free_block(struct control_state *cs, struct control_block *cb)
|
||||
@@ -766,6 +660,30 @@ control_write_callback(__unused struct bufferevent *bufev, void *data)
|
||||
bufferevent_disable(cs->write_event, EV_WRITE);
|
||||
}
|
||||
|
||||
/* Write a subscription change. */
|
||||
static void
|
||||
control_sub_change(struct monitor_change *change, __unused void *data)
|
||||
{
|
||||
struct client *c = change->c;
|
||||
struct session *s = change->s;
|
||||
struct winlink *wl = change->wl;
|
||||
struct window_pane *wp = change->wp;
|
||||
struct window *w;
|
||||
|
||||
if (wp != NULL) {
|
||||
w = wp->window;
|
||||
control_write(c, "%%subscription-changed %s $%u @%u %u %%%u : %s",
|
||||
change->name, s->id, w->id, wl->idx, wp->id, change->value);
|
||||
} else if (wl != NULL) {
|
||||
w = wl->window;
|
||||
control_write(c, "%%subscription-changed %s $%u @%u %u - : %s",
|
||||
change->name, s->id, w->id, wl->idx, change->value);
|
||||
} else {
|
||||
control_write(c, "%%subscription-changed %s $%u - - - : %s",
|
||||
change->name, s->id, change->value);
|
||||
}
|
||||
}
|
||||
|
||||
/* Initialize for control mode. */
|
||||
void
|
||||
control_start(struct client *c)
|
||||
@@ -783,7 +701,7 @@ control_start(struct client *c)
|
||||
RB_INIT(&cs->panes);
|
||||
TAILQ_INIT(&cs->pending_list);
|
||||
TAILQ_INIT(&cs->all_blocks);
|
||||
RB_INIT(&cs->subs);
|
||||
cs->subs = monitor_create(c, control_sub_change, NULL);
|
||||
|
||||
cs->read_event = bufferevent_new(c->fd, control_read_callback,
|
||||
control_write_callback, control_error_callback, c);
|
||||
@@ -832,20 +750,16 @@ control_stop(struct client *c)
|
||||
{
|
||||
struct control_state *cs = c->control_state;
|
||||
struct control_block *cb, *cb1;
|
||||
struct control_sub *csub, *csub1;
|
||||
|
||||
if (cs == NULL)
|
||||
return;
|
||||
|
||||
monitor_destroy(cs->subs);
|
||||
|
||||
if (~c->flags & CLIENT_CONTROLCONTROL)
|
||||
bufferevent_free(cs->write_event);
|
||||
bufferevent_free(cs->read_event);
|
||||
|
||||
RB_FOREACH_SAFE(csub, control_subs, &cs->subs, csub1)
|
||||
control_free_sub(cs, csub);
|
||||
if (evtimer_initialized(&cs->subs_timer))
|
||||
evtimer_del(&cs->subs_timer);
|
||||
|
||||
control_reset_offsets(c);
|
||||
TAILQ_FOREACH_SAFE(cb, &cs->all_blocks, all_entry, cb1)
|
||||
control_free_block(cs, cb);
|
||||
@@ -854,313 +768,14 @@ control_stop(struct client *c)
|
||||
free(cs);
|
||||
}
|
||||
|
||||
/* Check session subscription. */
|
||||
static void
|
||||
control_check_subs_session(struct client *c, struct control_sub *csub,
|
||||
struct format_tree *ft)
|
||||
{
|
||||
struct session *s = c->session;
|
||||
char *value;
|
||||
|
||||
value = format_expand(ft, csub->format);
|
||||
|
||||
if (csub->last != NULL && strcmp(value, csub->last) == 0) {
|
||||
free(value);
|
||||
return;
|
||||
}
|
||||
control_write(c,
|
||||
"%%subscription-changed %s $%u - - - : %s",
|
||||
csub->name, s->id, value);
|
||||
free(csub->last);
|
||||
csub->last = value;
|
||||
}
|
||||
|
||||
/* Check pane subscription. */
|
||||
static void
|
||||
control_check_subs_pane(struct client *c, struct control_sub *csub)
|
||||
{
|
||||
struct session *s = c->session;
|
||||
struct window_pane *wp;
|
||||
struct window *w;
|
||||
struct winlink *wl;
|
||||
struct format_tree *ft;
|
||||
char *value;
|
||||
struct control_sub_pane *csp, find;
|
||||
|
||||
wp = window_pane_find_by_id(csub->id);
|
||||
if (wp == NULL || wp->fd == -1)
|
||||
return;
|
||||
w = wp->window;
|
||||
|
||||
TAILQ_FOREACH(wl, &w->winlinks, wentry) {
|
||||
if (wl->session != s)
|
||||
continue;
|
||||
|
||||
ft = format_create_defaults(NULL, c, s, wl, wp);
|
||||
value = format_expand(ft, csub->format);
|
||||
format_free(ft);
|
||||
|
||||
find.pane = wp->id;
|
||||
find.idx = wl->idx;
|
||||
|
||||
csp = RB_FIND(control_sub_panes, &csub->panes, &find);
|
||||
if (csp == NULL) {
|
||||
csp = xcalloc(1, sizeof *csp);
|
||||
csp->pane = wp->id;
|
||||
csp->idx = wl->idx;
|
||||
RB_INSERT(control_sub_panes, &csub->panes, csp);
|
||||
}
|
||||
|
||||
if (csp->last != NULL && strcmp(value, csp->last) == 0) {
|
||||
free(value);
|
||||
continue;
|
||||
}
|
||||
control_write(c,
|
||||
"%%subscription-changed %s $%u @%u %u %%%u : %s",
|
||||
csub->name, s->id, w->id, wl->idx, wp->id, value);
|
||||
free(csp->last);
|
||||
csp->last = value;
|
||||
}
|
||||
}
|
||||
|
||||
/* Check all-panes subscription for a pane. */
|
||||
static void
|
||||
control_check_subs_all_panes_one(struct client *c, struct control_sub *csub,
|
||||
struct format_tree *ft, struct winlink *wl, struct window_pane *wp)
|
||||
{
|
||||
struct session *s = c->session;
|
||||
struct window *w = wl->window;
|
||||
char *value;
|
||||
struct control_sub_pane *csp, find;
|
||||
|
||||
value = format_expand(ft, csub->format);
|
||||
|
||||
find.pane = wp->id;
|
||||
find.idx = wl->idx;
|
||||
|
||||
csp = RB_FIND(control_sub_panes, &csub->panes, &find);
|
||||
if (csp == NULL) {
|
||||
csp = xcalloc(1, sizeof *csp);
|
||||
csp->pane = wp->id;
|
||||
csp->idx = wl->idx;
|
||||
RB_INSERT(control_sub_panes, &csub->panes, csp);
|
||||
}
|
||||
|
||||
if (csp->last != NULL && strcmp(value, csp->last) == 0) {
|
||||
free(value);
|
||||
return;
|
||||
}
|
||||
control_write(c,
|
||||
"%%subscription-changed %s $%u @%u %u %%%u : %s",
|
||||
csub->name, s->id, w->id, wl->idx, wp->id, value);
|
||||
free(csp->last);
|
||||
csp->last = value;
|
||||
}
|
||||
|
||||
/* Check window subscription. */
|
||||
static void
|
||||
control_check_subs_window(struct client *c, struct control_sub *csub)
|
||||
{
|
||||
struct session *s = c->session;
|
||||
struct window *w;
|
||||
struct winlink *wl;
|
||||
struct format_tree *ft;
|
||||
char *value;
|
||||
struct control_sub_window *csw, find;
|
||||
|
||||
w = window_find_by_id(csub->id);
|
||||
if (w == NULL)
|
||||
return;
|
||||
|
||||
TAILQ_FOREACH(wl, &w->winlinks, wentry) {
|
||||
if (wl->session != s)
|
||||
continue;
|
||||
|
||||
ft = format_create_defaults(NULL, c, s, wl, NULL);
|
||||
value = format_expand(ft, csub->format);
|
||||
format_free(ft);
|
||||
|
||||
find.window = w->id;
|
||||
find.idx = wl->idx;
|
||||
|
||||
csw = RB_FIND(control_sub_windows, &csub->windows, &find);
|
||||
if (csw == NULL) {
|
||||
csw = xcalloc(1, sizeof *csw);
|
||||
csw->window = w->id;
|
||||
csw->idx = wl->idx;
|
||||
RB_INSERT(control_sub_windows, &csub->windows, csw);
|
||||
}
|
||||
|
||||
if (csw->last != NULL && strcmp(value, csw->last) == 0) {
|
||||
free(value);
|
||||
continue;
|
||||
}
|
||||
control_write(c,
|
||||
"%%subscription-changed %s $%u @%u %u - : %s",
|
||||
csub->name, s->id, w->id, wl->idx, value);
|
||||
free(csw->last);
|
||||
csw->last = value;
|
||||
}
|
||||
}
|
||||
|
||||
/* Check all-windows subscription for a window. */
|
||||
static void
|
||||
control_check_subs_all_windows_one(struct client *c, struct control_sub *csub,
|
||||
struct format_tree *ft, struct winlink *wl)
|
||||
{
|
||||
struct session *s = c->session;
|
||||
struct window *w = wl->window;
|
||||
char *value;
|
||||
struct control_sub_window *csw, find;
|
||||
|
||||
value = format_expand(ft, csub->format);
|
||||
|
||||
find.window = w->id;
|
||||
find.idx = wl->idx;
|
||||
|
||||
csw = RB_FIND(control_sub_windows, &csub->windows, &find);
|
||||
if (csw == NULL) {
|
||||
csw = xcalloc(1, sizeof *csw);
|
||||
csw->window = w->id;
|
||||
csw->idx = wl->idx;
|
||||
RB_INSERT(control_sub_windows, &csub->windows, csw);
|
||||
}
|
||||
|
||||
if (csw->last != NULL && strcmp(value, csw->last) == 0) {
|
||||
free(value);
|
||||
return;
|
||||
}
|
||||
control_write(c,
|
||||
"%%subscription-changed %s $%u @%u %u - : %s",
|
||||
csub->name, s->id, w->id, wl->idx, value);
|
||||
free(csw->last);
|
||||
csw->last = value;
|
||||
}
|
||||
|
||||
/* Check subscriptions timer. */
|
||||
static void
|
||||
control_check_subs_timer(__unused int fd, __unused short events, void *data)
|
||||
{
|
||||
struct client *c = data;
|
||||
struct control_state *cs = c->control_state;
|
||||
struct control_sub *csub, *csub1;
|
||||
struct session *s = c->session;
|
||||
struct format_tree *ft;
|
||||
struct winlink *wl;
|
||||
struct window_pane *wp;
|
||||
struct timeval tv = { .tv_sec = 1 };
|
||||
int have_session = 0, have_all_panes = 0;
|
||||
int have_all_windows = 0;
|
||||
|
||||
log_debug("%s: timer fired", __func__);
|
||||
evtimer_add(&cs->subs_timer, &tv);
|
||||
|
||||
if (s == NULL)
|
||||
return;
|
||||
|
||||
/* Find which subscription types are present. */
|
||||
RB_FOREACH(csub, control_subs, &cs->subs) {
|
||||
switch (csub->type) {
|
||||
case CONTROL_SUB_SESSION:
|
||||
have_session = 1;
|
||||
break;
|
||||
case CONTROL_SUB_ALL_PANES:
|
||||
have_all_panes = 1;
|
||||
break;
|
||||
case CONTROL_SUB_ALL_WINDOWS:
|
||||
have_all_windows = 1;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* Check session subscriptions. */
|
||||
if (have_session) {
|
||||
ft = format_create_defaults(NULL, c, s, NULL, NULL);
|
||||
RB_FOREACH_SAFE(csub, control_subs, &cs->subs, csub1) {
|
||||
if (csub->type == CONTROL_SUB_SESSION)
|
||||
control_check_subs_session(c, csub, ft);
|
||||
}
|
||||
format_free(ft);
|
||||
}
|
||||
|
||||
/* Check pane and window subscriptions. */
|
||||
RB_FOREACH_SAFE(csub, control_subs, &cs->subs, csub1) {
|
||||
switch (csub->type) {
|
||||
case CONTROL_SUB_PANE:
|
||||
control_check_subs_pane(c, csub);
|
||||
break;
|
||||
case CONTROL_SUB_WINDOW:
|
||||
control_check_subs_window(c, csub);
|
||||
break;
|
||||
case CONTROL_SUB_SESSION:
|
||||
case CONTROL_SUB_ALL_PANES:
|
||||
case CONTROL_SUB_ALL_WINDOWS:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* Check all-panes subscriptions. */
|
||||
if (have_all_panes) {
|
||||
RB_FOREACH(wl, winlinks, &s->windows) {
|
||||
TAILQ_FOREACH(wp, &wl->window->panes, entry) {
|
||||
ft = format_create_defaults(NULL, c, s, wl, wp);
|
||||
RB_FOREACH_SAFE(csub, control_subs, &cs->subs,
|
||||
csub1) {
|
||||
if (csub->type != CONTROL_SUB_ALL_PANES)
|
||||
continue;
|
||||
control_check_subs_all_panes_one(c,
|
||||
csub, ft, wl, wp);
|
||||
}
|
||||
format_free(ft);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Check all-windows subscriptions. */
|
||||
if (have_all_windows) {
|
||||
RB_FOREACH(wl, winlinks, &s->windows) {
|
||||
ft = format_create_defaults(NULL, c, s, wl, NULL);
|
||||
RB_FOREACH_SAFE(csub, control_subs, &cs->subs,
|
||||
csub1) {
|
||||
if (csub->type != CONTROL_SUB_ALL_WINDOWS)
|
||||
continue;
|
||||
control_check_subs_all_windows_one(c, csub, ft,
|
||||
wl);
|
||||
}
|
||||
format_free(ft);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Add a subscription. */
|
||||
void
|
||||
control_add_sub(struct client *c, const char *name, enum control_sub_type type,
|
||||
control_add_sub(struct client *c, const char *name, enum monitor_type type,
|
||||
int id, const char *format)
|
||||
{
|
||||
struct control_state *cs = c->control_state;
|
||||
struct control_sub *csub, find;
|
||||
struct timeval tv = { .tv_sec = 1 };
|
||||
|
||||
find.name = (char *)name;
|
||||
if ((csub = RB_FIND(control_subs, &cs->subs, &find)) != NULL)
|
||||
control_free_sub(cs, csub);
|
||||
|
||||
csub = xcalloc(1, sizeof *csub);
|
||||
csub->name = xstrdup(name);
|
||||
csub->type = type;
|
||||
csub->id = id;
|
||||
csub->format = xstrdup(format);
|
||||
RB_INSERT(control_subs, &cs->subs, csub);
|
||||
|
||||
RB_INIT(&csub->panes);
|
||||
RB_INIT(&csub->windows);
|
||||
|
||||
if (!evtimer_initialized(&cs->subs_timer))
|
||||
evtimer_set(&cs->subs_timer, control_check_subs_timer, c);
|
||||
if (!evtimer_pending(&cs->subs_timer, NULL))
|
||||
evtimer_add(&cs->subs_timer, &tv);
|
||||
monitor_add(cs->subs, name, type, id, format);
|
||||
}
|
||||
|
||||
/* Remove a subscription. */
|
||||
@@ -1168,11 +783,6 @@ void
|
||||
control_remove_sub(struct client *c, const char *name)
|
||||
{
|
||||
struct control_state *cs = c->control_state;
|
||||
struct control_sub *csub, find;
|
||||
|
||||
find.name = (char *)name;
|
||||
if ((csub = RB_FIND(control_subs, &cs->subs, &find)) != NULL)
|
||||
control_free_sub(cs, csub);
|
||||
if (RB_EMPTY(&cs->subs))
|
||||
evtimer_del(&cs->subs_timer);
|
||||
monitor_remove(cs->subs, name);
|
||||
}
|
||||
|
||||
526
monitor.c
Normal file
526
monitor.c
Normal file
@@ -0,0 +1,526 @@
|
||||
/* $OpenBSD$ */
|
||||
|
||||
/*
|
||||
* Copyright (c) 2026 Nicholas Marriott <nicm@users.sourceforge.net>
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER
|
||||
* IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
|
||||
* OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <sys/types.h>
|
||||
|
||||
#include <event.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "tmux.h"
|
||||
|
||||
/* Subscription pane. */
|
||||
struct monitor_pane {
|
||||
u_int pane;
|
||||
u_int idx;
|
||||
char *last;
|
||||
|
||||
RB_ENTRY(monitor_pane) entry;
|
||||
};
|
||||
RB_HEAD(monitor_panes, monitor_pane);
|
||||
|
||||
/* Subscription window. */
|
||||
struct monitor_window {
|
||||
u_int window;
|
||||
u_int idx;
|
||||
char *last;
|
||||
|
||||
RB_ENTRY(monitor_window) entry;
|
||||
};
|
||||
RB_HEAD(monitor_windows, monitor_window);
|
||||
|
||||
/* Subscription. */
|
||||
struct monitor_item {
|
||||
char *name;
|
||||
char *format;
|
||||
|
||||
enum monitor_type type;
|
||||
u_int id;
|
||||
|
||||
char *last;
|
||||
struct monitor_panes panes;
|
||||
struct monitor_windows windows;
|
||||
|
||||
RB_ENTRY(monitor_item) entry;
|
||||
};
|
||||
RB_HEAD(monitor_items, monitor_item);
|
||||
|
||||
/* Monitored subscription set. */
|
||||
struct monitor_set {
|
||||
struct client *client;
|
||||
monitor_cb cb;
|
||||
void *data;
|
||||
|
||||
struct monitor_items items;
|
||||
struct event timer;
|
||||
};
|
||||
|
||||
static void monitor_timer(__unused int, __unused short, void *);
|
||||
|
||||
/* Compare subscriptions. */
|
||||
static int
|
||||
monitor_item_cmp(struct monitor_item *m1, struct monitor_item *m2)
|
||||
{
|
||||
return (strcmp(m1->name, m2->name));
|
||||
}
|
||||
RB_GENERATE_STATIC(monitor_items, monitor_item, entry, monitor_item_cmp);
|
||||
|
||||
/* Compare subscription panes. */
|
||||
static int
|
||||
monitor_pane_cmp(struct monitor_pane *mp1, struct monitor_pane *mp2)
|
||||
{
|
||||
if (mp1->pane < mp2->pane)
|
||||
return (-1);
|
||||
if (mp1->pane > mp2->pane)
|
||||
return (1);
|
||||
if (mp1->idx < mp2->idx)
|
||||
return (-1);
|
||||
if (mp1->idx > mp2->idx)
|
||||
return (1);
|
||||
return (0);
|
||||
}
|
||||
RB_GENERATE_STATIC(monitor_panes, monitor_pane, entry, monitor_pane_cmp);
|
||||
|
||||
/* Compare subscription windows. */
|
||||
static int
|
||||
monitor_window_cmp(struct monitor_window *mw1, struct monitor_window *mw2)
|
||||
{
|
||||
if (mw1->window < mw2->window)
|
||||
return (-1);
|
||||
if (mw1->window > mw2->window)
|
||||
return (1);
|
||||
if (mw1->idx < mw2->idx)
|
||||
return (-1);
|
||||
if (mw1->idx > mw2->idx)
|
||||
return (1);
|
||||
return (0);
|
||||
}
|
||||
RB_GENERATE_STATIC(monitor_windows, monitor_window, entry, monitor_window_cmp);
|
||||
|
||||
/* Free a subscription. */
|
||||
static void
|
||||
monitor_free_item(struct monitor_set *ms, struct monitor_item *me)
|
||||
{
|
||||
struct monitor_pane *mp, *mp1;
|
||||
struct monitor_window *mw, *mw1;
|
||||
|
||||
RB_FOREACH_SAFE(mp, monitor_panes, &me->panes, mp1) {
|
||||
RB_REMOVE(monitor_panes, &me->panes, mp);
|
||||
free(mp->last);
|
||||
free(mp);
|
||||
}
|
||||
RB_FOREACH_SAFE(mw, monitor_windows, &me->windows, mw1) {
|
||||
RB_REMOVE(monitor_windows, &me->windows, mw);
|
||||
free(mw->last);
|
||||
free(mw);
|
||||
}
|
||||
free(me->last);
|
||||
|
||||
RB_REMOVE(monitor_items, &ms->items, me);
|
||||
free(me->name);
|
||||
free(me->format);
|
||||
free(me);
|
||||
}
|
||||
|
||||
/* Report a changed value. */
|
||||
static void
|
||||
monitor_report(struct monitor_set *ms, struct monitor_item *me,
|
||||
struct session *s, struct winlink *wl, struct window_pane *wp,
|
||||
const char *value)
|
||||
{
|
||||
struct monitor_change change;
|
||||
|
||||
change.name = me->name;
|
||||
change.value = value;
|
||||
change.c = ms->client;
|
||||
change.s = s;
|
||||
change.wl = wl;
|
||||
change.wp = wp;
|
||||
log_debug("%s: %s changed to %s", __func__, me->name, value);
|
||||
ms->cb(&change, ms->data);
|
||||
}
|
||||
|
||||
/* Check session subscription. */
|
||||
static void
|
||||
monitor_check_session(struct monitor_set *ms, struct monitor_item *me,
|
||||
struct format_tree *ft)
|
||||
{
|
||||
struct session *s = ms->client->session;
|
||||
char *value;
|
||||
|
||||
value = format_expand(ft, me->format);
|
||||
|
||||
if (me->last != NULL && strcmp(value, me->last) == 0) {
|
||||
free(value);
|
||||
return;
|
||||
}
|
||||
|
||||
monitor_report(ms, me, s, NULL, NULL, value);
|
||||
free(me->last);
|
||||
me->last = value;
|
||||
}
|
||||
|
||||
/* Check pane subscription. */
|
||||
static void
|
||||
monitor_check_pane(struct monitor_set *ms, struct monitor_item *me)
|
||||
{
|
||||
struct client *c = ms->client;
|
||||
struct session *s = c->session;
|
||||
struct window_pane *wp;
|
||||
struct window *w;
|
||||
struct winlink *wl;
|
||||
struct format_tree *ft;
|
||||
char *value;
|
||||
struct monitor_pane *mp, find;
|
||||
|
||||
wp = window_pane_find_by_id(me->id);
|
||||
if (wp == NULL || wp->fd == -1)
|
||||
return;
|
||||
w = wp->window;
|
||||
|
||||
TAILQ_FOREACH(wl, &w->winlinks, wentry) {
|
||||
if (wl->session != s)
|
||||
continue;
|
||||
|
||||
ft = format_create_defaults(NULL, c, s, wl, wp);
|
||||
value = format_expand(ft, me->format);
|
||||
format_free(ft);
|
||||
|
||||
find.pane = wp->id;
|
||||
find.idx = wl->idx;
|
||||
mp = RB_FIND(monitor_panes, &me->panes, &find);
|
||||
if (mp == NULL) {
|
||||
mp = xcalloc(1, sizeof *mp);
|
||||
mp->pane = wp->id;
|
||||
mp->idx = wl->idx;
|
||||
RB_INSERT(monitor_panes, &me->panes, mp);
|
||||
}
|
||||
|
||||
if (mp->last != NULL && strcmp(value, mp->last) == 0) {
|
||||
free(value);
|
||||
continue;
|
||||
}
|
||||
|
||||
monitor_report(ms, me, s, wl, wp, value);
|
||||
free(mp->last);
|
||||
mp->last = value;
|
||||
}
|
||||
}
|
||||
|
||||
/* Check one all-panes subscription. */
|
||||
static void
|
||||
monitor_check_all_panes_one(struct monitor_set *ms, struct monitor_item *me,
|
||||
struct format_tree *ft, struct winlink *wl, struct window_pane *wp)
|
||||
{
|
||||
struct session *s = ms->client->session;
|
||||
char *value;
|
||||
struct monitor_pane *mp, find;
|
||||
|
||||
value = format_expand(ft, me->format);
|
||||
|
||||
find.pane = wp->id;
|
||||
find.idx = wl->idx;
|
||||
mp = RB_FIND(monitor_panes, &me->panes, &find);
|
||||
if (mp == NULL) {
|
||||
mp = xcalloc(1, sizeof *mp);
|
||||
mp->pane = wp->id;
|
||||
mp->idx = wl->idx;
|
||||
RB_INSERT(monitor_panes, &me->panes, mp);
|
||||
}
|
||||
|
||||
if (mp->last != NULL && strcmp(value, mp->last) == 0) {
|
||||
free(value);
|
||||
return;
|
||||
}
|
||||
|
||||
monitor_report(ms, me, s, wl, wp, value);
|
||||
free(mp->last);
|
||||
mp->last = value;
|
||||
}
|
||||
|
||||
/* Check window subscription. */
|
||||
static void
|
||||
monitor_check_window(struct monitor_set *ms, struct monitor_item *me)
|
||||
{
|
||||
struct client *c = ms->client;
|
||||
struct session *s = c->session;
|
||||
struct window *w;
|
||||
struct winlink *wl;
|
||||
struct format_tree *ft;
|
||||
char *value;
|
||||
struct monitor_window *mw, find;
|
||||
|
||||
w = window_find_by_id(me->id);
|
||||
if (w == NULL)
|
||||
return;
|
||||
|
||||
TAILQ_FOREACH(wl, &w->winlinks, wentry) {
|
||||
if (wl->session != s)
|
||||
continue;
|
||||
|
||||
ft = format_create_defaults(NULL, c, s, wl, NULL);
|
||||
value = format_expand(ft, me->format);
|
||||
format_free(ft);
|
||||
|
||||
find.window = w->id;
|
||||
find.idx = wl->idx;
|
||||
mw = RB_FIND(monitor_windows, &me->windows, &find);
|
||||
if (mw == NULL) {
|
||||
mw = xcalloc(1, sizeof *mw);
|
||||
mw->window = w->id;
|
||||
mw->idx = wl->idx;
|
||||
RB_INSERT(monitor_windows, &me->windows, mw);
|
||||
}
|
||||
|
||||
if (mw->last != NULL && strcmp(value, mw->last) == 0) {
|
||||
free(value);
|
||||
continue;
|
||||
}
|
||||
|
||||
monitor_report(ms, me, s, wl, NULL, value);
|
||||
free(mw->last);
|
||||
mw->last = value;
|
||||
}
|
||||
}
|
||||
|
||||
/* Check one all-windows subscription. */
|
||||
static void
|
||||
monitor_check_all_windows_one(struct monitor_set *ms, struct monitor_item *me,
|
||||
struct format_tree *ft, struct winlink *wl)
|
||||
{
|
||||
struct session *s = ms->client->session;
|
||||
struct window *w = wl->window;
|
||||
char *value;
|
||||
struct monitor_window *mw, find;
|
||||
|
||||
value = format_expand(ft, me->format);
|
||||
|
||||
find.window = w->id;
|
||||
find.idx = wl->idx;
|
||||
mw = RB_FIND(monitor_windows, &me->windows, &find);
|
||||
if (mw == NULL) {
|
||||
mw = xcalloc(1, sizeof *mw);
|
||||
mw->window = w->id;
|
||||
mw->idx = wl->idx;
|
||||
RB_INSERT(monitor_windows, &me->windows, mw);
|
||||
}
|
||||
|
||||
if (mw->last != NULL && strcmp(value, mw->last) == 0) {
|
||||
free(value);
|
||||
return;
|
||||
}
|
||||
|
||||
monitor_report(ms, me, s, wl, NULL, value);
|
||||
free(mw->last);
|
||||
mw->last = value;
|
||||
}
|
||||
|
||||
/* Check session subscriptions. */
|
||||
static void
|
||||
monitor_check_sessions(struct monitor_set *ms)
|
||||
{
|
||||
struct client *c = ms->client;
|
||||
struct session *s = c->session;
|
||||
struct monitor_item *me, *me1;
|
||||
struct format_tree *ft;
|
||||
|
||||
ft = format_create_defaults(NULL, c, s, NULL, NULL);
|
||||
RB_FOREACH_SAFE(me, monitor_items, &ms->items, me1) {
|
||||
if (me->type == MONITOR_SESSION)
|
||||
monitor_check_session(ms, me, ft);
|
||||
}
|
||||
format_free(ft);
|
||||
}
|
||||
|
||||
/* Check pane and window subscriptions. */
|
||||
static void
|
||||
monitor_check_panes_windows(struct monitor_set *ms)
|
||||
{
|
||||
struct monitor_item *me, *me1;
|
||||
|
||||
RB_FOREACH_SAFE(me, monitor_items, &ms->items, me1) {
|
||||
switch (me->type) {
|
||||
case MONITOR_PANE:
|
||||
monitor_check_pane(ms, me);
|
||||
break;
|
||||
case MONITOR_WINDOW:
|
||||
monitor_check_window(ms, me);
|
||||
break;
|
||||
case MONITOR_SESSION:
|
||||
case MONITOR_ALL_PANES:
|
||||
case MONITOR_ALL_WINDOWS:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Check all-panes subscriptions. */
|
||||
static void
|
||||
monitor_check_all_panes(struct monitor_set *ms)
|
||||
{
|
||||
struct client *c = ms->client;
|
||||
struct session *s = c->session;
|
||||
struct monitor_item *me, *me1;
|
||||
struct window_pane *wp;
|
||||
struct format_tree *ft;
|
||||
struct winlink *wl;
|
||||
|
||||
RB_FOREACH(wl, winlinks, &s->windows) {
|
||||
TAILQ_FOREACH(wp, &wl->window->panes, entry) {
|
||||
ft = format_create_defaults(NULL, c, s, wl, wp);
|
||||
RB_FOREACH_SAFE(me, monitor_items, &ms->items, me1) {
|
||||
if (me->type != MONITOR_ALL_PANES)
|
||||
continue;
|
||||
monitor_check_all_panes_one(ms, me, ft, wl, wp);
|
||||
}
|
||||
format_free(ft);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Check all-windows subscriptions. */
|
||||
static void
|
||||
monitor_check_all_windows(struct monitor_set *ms)
|
||||
{
|
||||
struct client *c = ms->client;
|
||||
struct session *s = c->session;
|
||||
struct monitor_item *me, *me1;
|
||||
struct format_tree *ft;
|
||||
struct winlink *wl;
|
||||
|
||||
RB_FOREACH(wl, winlinks, &s->windows) {
|
||||
ft = format_create_defaults(NULL, c, s, wl, NULL);
|
||||
RB_FOREACH_SAFE(me, monitor_items, &ms->items, me1) {
|
||||
if (me->type != MONITOR_ALL_WINDOWS)
|
||||
continue;
|
||||
monitor_check_all_windows_one(ms, me, ft, wl);
|
||||
}
|
||||
format_free(ft);
|
||||
}
|
||||
}
|
||||
|
||||
/* Check subscriptions. */
|
||||
static void
|
||||
monitor_timer(__unused int fd, __unused short events, void *data)
|
||||
{
|
||||
struct monitor_set *ms = data;
|
||||
struct client *c = ms->client;
|
||||
struct monitor_item *me;
|
||||
struct timeval tv = { .tv_sec = 1 };
|
||||
int have_session = 0, have_all_panes = 0;
|
||||
int have_all_windows = 0;
|
||||
|
||||
log_debug("%s: timer fired", __func__);
|
||||
evtimer_add(&ms->timer, &tv);
|
||||
|
||||
if (c->session == NULL)
|
||||
return;
|
||||
|
||||
RB_FOREACH(me, monitor_items, &ms->items) {
|
||||
switch (me->type) {
|
||||
case MONITOR_SESSION:
|
||||
have_session = 1;
|
||||
break;
|
||||
case MONITOR_ALL_PANES:
|
||||
have_all_panes = 1;
|
||||
break;
|
||||
case MONITOR_ALL_WINDOWS:
|
||||
have_all_windows = 1;
|
||||
break;
|
||||
case MONITOR_PANE:
|
||||
case MONITOR_WINDOW:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (have_session)
|
||||
monitor_check_sessions(ms);
|
||||
monitor_check_panes_windows(ms);
|
||||
if (have_all_panes)
|
||||
monitor_check_all_panes(ms);
|
||||
if (have_all_windows)
|
||||
monitor_check_all_windows(ms);
|
||||
}
|
||||
|
||||
/* Create a monitor set. */
|
||||
struct monitor_set *
|
||||
monitor_create(struct client *c, monitor_cb cb, void *data)
|
||||
{
|
||||
struct monitor_set *ms;
|
||||
|
||||
ms = xcalloc(1, sizeof *ms);
|
||||
ms->client = c;
|
||||
ms->cb = cb;
|
||||
ms->data = data;
|
||||
RB_INIT(&ms->items);
|
||||
return (ms);
|
||||
}
|
||||
|
||||
/* Destroy a monitor set. */
|
||||
void
|
||||
monitor_destroy(struct monitor_set *ms)
|
||||
{
|
||||
struct monitor_item *me, *me1;
|
||||
|
||||
if (ms == NULL)
|
||||
return;
|
||||
|
||||
if (evtimer_initialized(&ms->timer))
|
||||
evtimer_del(&ms->timer);
|
||||
RB_FOREACH_SAFE(me, monitor_items, &ms->items, me1)
|
||||
monitor_free_item(ms, me);
|
||||
free(ms);
|
||||
}
|
||||
|
||||
/* Add a subscription. */
|
||||
void
|
||||
monitor_add(struct monitor_set *ms, const char *name, enum monitor_type type,
|
||||
int id, const char *format)
|
||||
{
|
||||
struct monitor_item *me, find = { .name = (char *)name };
|
||||
struct timeval tv = { .tv_sec = 1 };
|
||||
|
||||
if ((me = RB_FIND(monitor_items, &ms->items, &find)) != NULL)
|
||||
monitor_free_item(ms, me);
|
||||
|
||||
me = xcalloc(1, sizeof *me);
|
||||
me->name = xstrdup(name);
|
||||
me->format = xstrdup(format);
|
||||
me->type = type;
|
||||
me->id = id;
|
||||
RB_INIT(&me->panes);
|
||||
RB_INIT(&me->windows);
|
||||
RB_INSERT(monitor_items, &ms->items, me);
|
||||
|
||||
if (!evtimer_initialized(&ms->timer))
|
||||
evtimer_set(&ms->timer, monitor_timer, ms);
|
||||
if (!evtimer_pending(&ms->timer, NULL))
|
||||
evtimer_add(&ms->timer, &tv);
|
||||
}
|
||||
|
||||
/* Remove a subscription. */
|
||||
void
|
||||
monitor_remove(struct monitor_set *ms, const char *name)
|
||||
{
|
||||
struct monitor_item *me, find = { .name = (char *)name };
|
||||
|
||||
if ((me = RB_FIND(monitor_items, &ms->items, &find)) != NULL)
|
||||
monitor_free_item(ms, me);
|
||||
if (RB_EMPTY(&ms->items) && evtimer_initialized(&ms->timer))
|
||||
evtimer_del(&ms->timer);
|
||||
}
|
||||
@@ -2079,7 +2079,7 @@ screen_write_clearscreen(struct screen_write_ctx *ctx, u_int bg)
|
||||
|
||||
screen_write_collect_clear(ctx, 0, sy);
|
||||
|
||||
if (!screen_write_should_draw_lines(ctx, s->cy, sy - s->cy))
|
||||
if (!screen_write_should_draw_lines(ctx, 0, sy))
|
||||
return;
|
||||
if (~ttyctx.flags & TTY_CTX_PANE_OBSCURED) {
|
||||
tty_write(tty_cmd_clearscreen, &ttyctx);
|
||||
|
||||
35
tmux.h
35
tmux.h
@@ -2315,14 +2315,24 @@ struct client {
|
||||
};
|
||||
TAILQ_HEAD(clients, client);
|
||||
|
||||
/* Control mode subscription type. */
|
||||
enum control_sub_type {
|
||||
CONTROL_SUB_SESSION,
|
||||
CONTROL_SUB_PANE,
|
||||
CONTROL_SUB_ALL_PANES,
|
||||
CONTROL_SUB_WINDOW,
|
||||
CONTROL_SUB_ALL_WINDOWS
|
||||
/* Monitor. */
|
||||
enum monitor_type {
|
||||
MONITOR_SESSION,
|
||||
MONITOR_PANE,
|
||||
MONITOR_ALL_PANES,
|
||||
MONITOR_WINDOW,
|
||||
MONITOR_ALL_WINDOWS
|
||||
};
|
||||
struct monitor_change {
|
||||
const char *name;
|
||||
const char *value;
|
||||
|
||||
struct client *c;
|
||||
struct session *s;
|
||||
struct winlink *wl;
|
||||
struct window_pane *wp;
|
||||
};
|
||||
typedef void (*monitor_cb)(struct monitor_change *, void *);
|
||||
|
||||
/* Key binding and key table. */
|
||||
struct key_binding {
|
||||
@@ -3844,6 +3854,13 @@ void check_window_name(struct window *);
|
||||
char *default_window_name(struct window *);
|
||||
char *parse_window_name(const char *);
|
||||
|
||||
/* monitor.c */
|
||||
struct monitor_set *monitor_create(struct client *, monitor_cb, void *);
|
||||
void monitor_destroy(struct monitor_set *);
|
||||
void monitor_add(struct monitor_set *, const char *, enum monitor_type, int,
|
||||
const char *);
|
||||
void monitor_remove(struct monitor_set *, const char *);
|
||||
|
||||
/* control.c */
|
||||
void control_discard(struct client *);
|
||||
void control_start(struct client *);
|
||||
@@ -3859,8 +3876,8 @@ void control_reset_offsets(struct client *);
|
||||
void printflike(2, 3) control_write(struct client *, const char *, ...);
|
||||
void control_write_output(struct client *, struct window_pane *);
|
||||
int control_all_done(struct client *);
|
||||
void control_add_sub(struct client *, const char *, enum control_sub_type,
|
||||
int, const char *);
|
||||
void control_add_sub(struct client *, const char *, enum monitor_type, int,
|
||||
const char *);
|
||||
void control_remove_sub(struct client *, const char *);
|
||||
|
||||
/* control-notify.c */
|
||||
|
||||
Reference in New Issue
Block a user