mirror of
https://github.com/tmux/tmux.git
synced 2025-01-12 03:08:46 +00:00
Merge branch 'obsd-master'
This commit is contained in:
commit
aa4f3a9b3d
@ -34,27 +34,62 @@ const struct cmd_entry cmd_refresh_client_entry = {
|
|||||||
.name = "refresh-client",
|
.name = "refresh-client",
|
||||||
.alias = "refresh",
|
.alias = "refresh",
|
||||||
|
|
||||||
.args = { "A:cC:Df:F:lLRSt:U", 0, 1 },
|
.args = { "A:B:cC:Df:F:lLRSt:U", 0, 1 },
|
||||||
.usage = "[-cDlLRSU] [-A pane:state] [-C XxY] [-f flags] "
|
.usage = "[-cDlLRSU] [-A pane:state] [-B name:what:format] "
|
||||||
CMD_TARGET_CLIENT_USAGE " [adjustment]",
|
"[-C XxY] [-f flags] " CMD_TARGET_CLIENT_USAGE " [adjustment]",
|
||||||
|
|
||||||
.flags = CMD_AFTERHOOK|CMD_CLIENT_TFLAG,
|
.flags = CMD_AFTERHOOK|CMD_CLIENT_TFLAG,
|
||||||
.exec = cmd_refresh_client_exec
|
.exec = cmd_refresh_client_exec
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static void
|
||||||
|
cmd_refresh_client_update_subscription(struct client *tc, const char *value)
|
||||||
|
{
|
||||||
|
char *copy, *split, *name, *what;
|
||||||
|
enum control_sub_type subtype;
|
||||||
|
int subid = -1;
|
||||||
|
|
||||||
|
copy = name = xstrdup(value);
|
||||||
|
if ((split = strchr(copy, ':')) == NULL) {
|
||||||
|
control_remove_sub(tc, copy);
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
*split++ = '\0';
|
||||||
|
|
||||||
|
what = split;
|
||||||
|
if ((split = strchr(what, ':')) == NULL)
|
||||||
|
goto out;
|
||||||
|
*split++ = '\0';
|
||||||
|
|
||||||
|
if (strcmp(what, "%*") == 0)
|
||||||
|
subtype = CONTROL_SUB_ALL_PANES;
|
||||||
|
else if (sscanf(what, "%%%d", &subid) == 1 && subid >= 0)
|
||||||
|
subtype = CONTROL_SUB_PANE;
|
||||||
|
else if (strcmp(what, "@*") == 0)
|
||||||
|
subtype = CONTROL_SUB_ALL_WINDOWS;
|
||||||
|
else if (sscanf(what, "@%d", &subid) == 1 && subid >= 0)
|
||||||
|
subtype = CONTROL_SUB_WINDOW;
|
||||||
|
else
|
||||||
|
subtype = CONTROL_SUB_SESSION;
|
||||||
|
control_add_sub(tc, name, subtype, subid, split);
|
||||||
|
|
||||||
|
out:
|
||||||
|
free(copy);
|
||||||
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
cmd_refresh_client_update_offset(struct client *tc, const char *value)
|
cmd_refresh_client_update_offset(struct client *tc, const char *value)
|
||||||
{
|
{
|
||||||
struct window_pane *wp;
|
struct window_pane *wp;
|
||||||
char *copy, *colon;
|
char *copy, *split;
|
||||||
u_int pane;
|
u_int pane;
|
||||||
|
|
||||||
if (*value != '%')
|
if (*value != '%')
|
||||||
return;
|
return;
|
||||||
copy = xstrdup(value);
|
copy = xstrdup(value);
|
||||||
if ((colon = strchr(copy, ':')) == NULL)
|
if ((split = strchr(copy, ':')) == NULL)
|
||||||
goto out;
|
goto out;
|
||||||
*colon++ = '\0';
|
*split++ = '\0';
|
||||||
|
|
||||||
if (sscanf(copy, "%%%u", &pane) != 1)
|
if (sscanf(copy, "%%%u", &pane) != 1)
|
||||||
goto out;
|
goto out;
|
||||||
@ -62,13 +97,13 @@ cmd_refresh_client_update_offset(struct client *tc, const char *value)
|
|||||||
if (wp == NULL)
|
if (wp == NULL)
|
||||||
goto out;
|
goto out;
|
||||||
|
|
||||||
if (strcmp(colon, "on") == 0)
|
if (strcmp(split, "on") == 0)
|
||||||
control_set_pane_on(tc, wp);
|
control_set_pane_on(tc, wp);
|
||||||
else if (strcmp(colon, "off") == 0)
|
else if (strcmp(split, "off") == 0)
|
||||||
control_set_pane_off(tc, wp);
|
control_set_pane_off(tc, wp);
|
||||||
else if (strcmp(colon, "continue") == 0)
|
else if (strcmp(split, "continue") == 0)
|
||||||
control_continue_pane(tc, wp);
|
control_continue_pane(tc, wp);
|
||||||
else if (strcmp(colon, "pause") == 0)
|
else if (strcmp(split, "pause") == 0)
|
||||||
control_pause_pane(tc, wp);
|
control_pause_pane(tc, wp);
|
||||||
|
|
||||||
out:
|
out:
|
||||||
@ -156,6 +191,16 @@ cmd_refresh_client_exec(struct cmd *self, struct cmdq_item *item)
|
|||||||
}
|
}
|
||||||
return (CMD_RETURN_NORMAL);
|
return (CMD_RETURN_NORMAL);
|
||||||
}
|
}
|
||||||
|
if (args_has(args, 'B')) {
|
||||||
|
if (~tc->flags & CLIENT_CONTROL)
|
||||||
|
goto not_control_client;
|
||||||
|
value = args_first_value(args, 'B', &av);
|
||||||
|
while (value != NULL) {
|
||||||
|
cmd_refresh_client_update_subscription(tc, value);
|
||||||
|
value = args_next_value(&av);
|
||||||
|
}
|
||||||
|
return (CMD_RETURN_NORMAL);
|
||||||
|
}
|
||||||
if (args_has(args, 'C')) {
|
if (args_has(args, 'C')) {
|
||||||
if (~tc->flags & CLIENT_CONTROL)
|
if (~tc->flags & CLIENT_CONTROL)
|
||||||
goto not_control_client;
|
goto not_control_client;
|
||||||
|
391
control.c
391
control.c
@ -76,6 +76,42 @@ struct control_pane {
|
|||||||
};
|
};
|
||||||
RB_HEAD(control_panes, 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. */
|
/* Control client state. */
|
||||||
struct control_state {
|
struct control_state {
|
||||||
struct control_panes panes;
|
struct control_panes panes;
|
||||||
@ -87,6 +123,9 @@ struct control_state {
|
|||||||
|
|
||||||
struct bufferevent *read_event;
|
struct bufferevent *read_event;
|
||||||
struct bufferevent *write_event;
|
struct bufferevent *write_event;
|
||||||
|
|
||||||
|
struct control_subs subs;
|
||||||
|
struct event subs_timer;
|
||||||
};
|
};
|
||||||
|
|
||||||
/* Low and high watermarks. */
|
/* Low and high watermarks. */
|
||||||
@ -116,6 +155,73 @@ control_pane_cmp(struct control_pane *cp1, struct control_pane *cp2)
|
|||||||
}
|
}
|
||||||
RB_GENERATE_STATIC(control_panes, control_pane, entry, control_pane_cmp);
|
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);
|
||||||
|
}
|
||||||
|
RB_FOREACH_SAFE(csw, control_sub_windows, &csub->windows, csw1) {
|
||||||
|
RB_REMOVE(control_sub_windows, &csub->windows, csw);
|
||||||
|
free(csw);
|
||||||
|
}
|
||||||
|
free(csub->last);
|
||||||
|
|
||||||
|
RB_REMOVE(control_subs, &cs->subs, csub);
|
||||||
|
free(csub->name);
|
||||||
|
free(csub->format);
|
||||||
|
free(csub);
|
||||||
|
}
|
||||||
|
|
||||||
/* Free a block. */
|
/* Free a block. */
|
||||||
static void
|
static void
|
||||||
control_free_block(struct control_state *cs, struct control_block *cb)
|
control_free_block(struct control_state *cs, struct control_block *cb)
|
||||||
@ -666,6 +772,7 @@ control_start(struct client *c)
|
|||||||
RB_INIT(&cs->panes);
|
RB_INIT(&cs->panes);
|
||||||
TAILQ_INIT(&cs->pending_list);
|
TAILQ_INIT(&cs->pending_list);
|
||||||
TAILQ_INIT(&cs->all_blocks);
|
TAILQ_INIT(&cs->all_blocks);
|
||||||
|
RB_INIT(&cs->subs);
|
||||||
|
|
||||||
cs->read_event = bufferevent_new(c->fd, control_read_callback,
|
cs->read_event = bufferevent_new(c->fd, control_read_callback,
|
||||||
control_write_callback, control_error_callback, c);
|
control_write_callback, control_error_callback, c);
|
||||||
@ -704,14 +811,298 @@ control_stop(struct client *c)
|
|||||||
{
|
{
|
||||||
struct control_state *cs = c->control_state;
|
struct control_state *cs = c->control_state;
|
||||||
struct control_block *cb, *cb1;
|
struct control_block *cb, *cb1;
|
||||||
|
struct control_sub *csub, *csub1;
|
||||||
|
|
||||||
if (~c->flags & CLIENT_CONTROLCONTROL)
|
if (~c->flags & CLIENT_CONTROLCONTROL)
|
||||||
bufferevent_free(cs->write_event);
|
bufferevent_free(cs->write_event);
|
||||||
bufferevent_free(cs->read_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);
|
||||||
|
|
||||||
TAILQ_FOREACH_SAFE(cb, &cs->all_blocks, all_entry, cb1)
|
TAILQ_FOREACH_SAFE(cb, &cs->all_blocks, all_entry, cb1)
|
||||||
control_free_block(cs, cb);
|
control_free_block(cs, cb);
|
||||||
control_reset_offsets(c);
|
control_reset_offsets(c);
|
||||||
|
|
||||||
free(cs);
|
free(cs);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Check session subscription. */
|
||||||
|
static void
|
||||||
|
control_check_subs_session(struct client *c, struct control_sub *csub)
|
||||||
|
{
|
||||||
|
struct session *s = c->session;
|
||||||
|
struct format_tree *ft;
|
||||||
|
char *value;
|
||||||
|
|
||||||
|
ft = format_create_defaults(NULL, c, s, NULL, NULL);
|
||||||
|
value = format_expand(ft, csub->format);
|
||||||
|
format_free(ft);
|
||||||
|
|
||||||
|
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)
|
||||||
|
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. */
|
||||||
|
static void
|
||||||
|
control_check_subs_all_panes(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;
|
||||||
|
|
||||||
|
RB_FOREACH(wl, winlinks, &s->windows) {
|
||||||
|
w = wl->window;
|
||||||
|
TAILQ_FOREACH(wp, &w->panes, entry) {
|
||||||
|
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 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. */
|
||||||
|
static void
|
||||||
|
control_check_subs_all_windows(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;
|
||||||
|
|
||||||
|
RB_FOREACH(wl, winlinks, &s->windows) {
|
||||||
|
w = wl->window;
|
||||||
|
|
||||||
|
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 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 timeval tv = { .tv_sec = 1 };
|
||||||
|
|
||||||
|
log_debug("%s: timer fired", __func__);
|
||||||
|
evtimer_add(&cs->subs_timer, &tv);
|
||||||
|
|
||||||
|
RB_FOREACH_SAFE(csub, control_subs, &cs->subs, csub1) {
|
||||||
|
switch (csub->type) {
|
||||||
|
case CONTROL_SUB_SESSION:
|
||||||
|
control_check_subs_session(c, csub);
|
||||||
|
break;
|
||||||
|
case CONTROL_SUB_PANE:
|
||||||
|
control_check_subs_pane(c, csub);
|
||||||
|
break;
|
||||||
|
case CONTROL_SUB_ALL_PANES:
|
||||||
|
control_check_subs_all_panes(c, csub);
|
||||||
|
break;
|
||||||
|
case CONTROL_SUB_WINDOW:
|
||||||
|
control_check_subs_window(c, csub);
|
||||||
|
break;
|
||||||
|
case CONTROL_SUB_ALL_WINDOWS:
|
||||||
|
control_check_subs_all_windows(c, csub);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Add a subscription. */
|
||||||
|
void
|
||||||
|
control_add_sub(struct client *c, const char *name, enum control_sub_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);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Remove a subscription. */
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
@ -25,10 +25,10 @@
|
|||||||
|
|
||||||
static void screen_write_collect_clear(struct screen_write_ctx *, u_int,
|
static void screen_write_collect_clear(struct screen_write_ctx *, u_int,
|
||||||
u_int);
|
u_int);
|
||||||
static int screen_write_collect_clear_end(struct screen_write_ctx *, u_int,
|
static void screen_write_collect_clear_end(struct screen_write_ctx *, u_int,
|
||||||
|
u_int);
|
||||||
|
static void screen_write_collect_clear_start(struct screen_write_ctx *,
|
||||||
u_int, u_int);
|
u_int, u_int);
|
||||||
static int screen_write_collect_clear_start(struct screen_write_ctx *,
|
|
||||||
u_int, u_int, u_int);
|
|
||||||
static void screen_write_collect_scroll(struct screen_write_ctx *);
|
static void screen_write_collect_scroll(struct screen_write_ctx *);
|
||||||
static void screen_write_collect_flush(struct screen_write_ctx *, int,
|
static void screen_write_collect_flush(struct screen_write_ctx *, int,
|
||||||
const char *);
|
const char *);
|
||||||
@ -1127,13 +1127,12 @@ screen_write_clearendofline(struct screen_write_ctx *ctx, u_int bg)
|
|||||||
|
|
||||||
grid_view_clear(s->grid, s->cx, s->cy, sx - s->cx, 1, bg);
|
grid_view_clear(s->grid, s->cx, s->cy, sx - s->cx, 1, bg);
|
||||||
|
|
||||||
if (!screen_write_collect_clear_end(ctx, s->cy, s->cx, bg)) {
|
screen_write_collect_clear_end(ctx, s->cy, s->cx);
|
||||||
ci->x = s->cx;
|
ci->x = s->cx;
|
||||||
ci->type = CLEAR_END;
|
ci->type = CLEAR_END;
|
||||||
ci->bg = bg;
|
ci->bg = bg;
|
||||||
TAILQ_INSERT_TAIL(&ctx->s->write_list[s->cy].items, ci, entry);
|
TAILQ_INSERT_TAIL(&ctx->s->write_list[s->cy].items, ci, entry);
|
||||||
ctx->item = xcalloc(1, sizeof *ctx->item);
|
ctx->item = xcalloc(1, sizeof *ctx->item);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Clear to start of line from cursor. */
|
/* Clear to start of line from cursor. */
|
||||||
@ -1154,13 +1153,12 @@ screen_write_clearstartofline(struct screen_write_ctx *ctx, u_int bg)
|
|||||||
else
|
else
|
||||||
grid_view_clear(s->grid, 0, s->cy, s->cx + 1, 1, bg);
|
grid_view_clear(s->grid, 0, s->cy, s->cx + 1, 1, bg);
|
||||||
|
|
||||||
if (!screen_write_collect_clear_start(ctx, s->cy, s->cx, bg)) {
|
screen_write_collect_clear_start(ctx, s->cy, s->cx);
|
||||||
ci->x = s->cx;
|
ci->x = s->cx;
|
||||||
ci->type = CLEAR_START;
|
ci->type = CLEAR_START;
|
||||||
ci->bg = bg;
|
ci->bg = bg;
|
||||||
TAILQ_INSERT_TAIL(&ctx->s->write_list[s->cy].items, ci, entry);
|
TAILQ_INSERT_TAIL(&ctx->s->write_list[s->cy].items, ci, entry);
|
||||||
ctx->item = xcalloc(1, sizeof *ctx->item);
|
ctx->item = xcalloc(1, sizeof *ctx->item);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Move cursor to px,py. */
|
/* Move cursor to px,py. */
|
||||||
@ -1393,25 +1391,18 @@ screen_write_clearhistory(struct screen_write_ctx *ctx)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Clear to start of a collected line. */
|
/* Clear to start of a collected line. */
|
||||||
static int
|
static void
|
||||||
screen_write_collect_clear_start(struct screen_write_ctx *ctx, u_int y, u_int x,
|
screen_write_collect_clear_start(struct screen_write_ctx *ctx, u_int y, u_int x)
|
||||||
u_int bg)
|
|
||||||
{
|
{
|
||||||
struct screen_write_collect_item *ci, *tmp;
|
struct screen_write_collect_item *ci, *tmp;
|
||||||
size_t size = 0;
|
size_t size = 0;
|
||||||
u_int items = 0;
|
u_int items = 0;
|
||||||
int redundant = 0;
|
|
||||||
|
|
||||||
if (TAILQ_EMPTY(&ctx->s->write_list[y].items))
|
if (TAILQ_EMPTY(&ctx->s->write_list[y].items))
|
||||||
return (0);
|
return;
|
||||||
TAILQ_FOREACH_SAFE(ci, &ctx->s->write_list[y].items, entry, tmp) {
|
TAILQ_FOREACH_SAFE(ci, &ctx->s->write_list[y].items, entry, tmp) {
|
||||||
switch (ci->type) {
|
switch (ci->type) {
|
||||||
case CLEAR_START:
|
case CLEAR_START:
|
||||||
if (ci->x >= x) {
|
|
||||||
if (ci->bg == bg)
|
|
||||||
redundant = 1;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
case CLEAR_END:
|
case CLEAR_END:
|
||||||
if (ci->x <= x)
|
if (ci->x <= x)
|
||||||
@ -1430,21 +1421,18 @@ screen_write_collect_clear_start(struct screen_write_ctx *ctx, u_int y, u_int x,
|
|||||||
ctx->skipped += size;
|
ctx->skipped += size;
|
||||||
log_debug("%s: dropped %u items (%zu bytes) (line %u)", __func__, items,
|
log_debug("%s: dropped %u items (%zu bytes) (line %u)", __func__, items,
|
||||||
size, y);
|
size, y);
|
||||||
return (redundant);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Clear to end of a collected line. */
|
/* Clear to end of a collected line. */
|
||||||
static int
|
static void
|
||||||
screen_write_collect_clear_end(struct screen_write_ctx *ctx, u_int y, u_int x,
|
screen_write_collect_clear_end(struct screen_write_ctx *ctx, u_int y, u_int x)
|
||||||
u_int bg)
|
|
||||||
{
|
{
|
||||||
struct screen_write_collect_item *ci, *tmp;
|
struct screen_write_collect_item *ci, *tmp;
|
||||||
size_t size = 0;
|
size_t size = 0;
|
||||||
int redundant = 0;
|
|
||||||
u_int items = 0;
|
u_int items = 0;
|
||||||
|
|
||||||
if (TAILQ_EMPTY(&ctx->s->write_list[y].items))
|
if (TAILQ_EMPTY(&ctx->s->write_list[y].items))
|
||||||
return (0);
|
return;
|
||||||
TAILQ_FOREACH_SAFE(ci, &ctx->s->write_list[y].items, entry, tmp) {
|
TAILQ_FOREACH_SAFE(ci, &ctx->s->write_list[y].items, entry, tmp) {
|
||||||
switch (ci->type) {
|
switch (ci->type) {
|
||||||
case CLEAR_START:
|
case CLEAR_START:
|
||||||
@ -1452,11 +1440,6 @@ screen_write_collect_clear_end(struct screen_write_ctx *ctx, u_int y, u_int x,
|
|||||||
ci->x = x;
|
ci->x = x;
|
||||||
continue;
|
continue;
|
||||||
case CLEAR_END:
|
case CLEAR_END:
|
||||||
if (ci->x <= x) {
|
|
||||||
if (ci->bg == bg)
|
|
||||||
redundant = 1;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
case TEXT:
|
case TEXT:
|
||||||
if (ci->x < x)
|
if (ci->x < x)
|
||||||
@ -1471,7 +1454,6 @@ screen_write_collect_clear_end(struct screen_write_ctx *ctx, u_int y, u_int x,
|
|||||||
ctx->skipped += size;
|
ctx->skipped += size;
|
||||||
log_debug("%s: dropped %u items (%zu bytes) (line %u)", __func__, items,
|
log_debug("%s: dropped %u items (%zu bytes) (line %u)", __func__, items,
|
||||||
size, y);
|
size, y);
|
||||||
return (redundant);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Clear collected lines. */
|
/* Clear collected lines. */
|
||||||
@ -1566,6 +1548,7 @@ screen_write_collect_flush(struct screen_write_ctx *ctx, int scroll_only,
|
|||||||
TAILQ_FOREACH_SAFE(ci, &cl->items, entry, tmp) {
|
TAILQ_FOREACH_SAFE(ci, &cl->items, entry, tmp) {
|
||||||
screen_write_set_cursor(ctx, ci->x, y);
|
screen_write_set_cursor(ctx, ci->x, y);
|
||||||
if (ci->type == CLEAR_END) {
|
if (ci->type == CLEAR_END) {
|
||||||
|
log_debug("XXX %u %u", ci->x, ci->bg);
|
||||||
screen_write_initctx(ctx, &ttyctx, 1);
|
screen_write_initctx(ctx, &ttyctx, 1);
|
||||||
ttyctx.bg = ci->bg;
|
ttyctx.bg = ci->bg;
|
||||||
tty_write(tty_cmd_clearendofline, &ttyctx);
|
tty_write(tty_cmd_clearendofline, &ttyctx);
|
||||||
|
@ -1472,11 +1472,13 @@ server_client_check_pane_resize(struct window_pane *wp)
|
|||||||
* Otherwise resize to the force size and start the timer.
|
* Otherwise resize to the force size and start the timer.
|
||||||
*/
|
*/
|
||||||
if (wp->flags & PANE_RESIZENOW) {
|
if (wp->flags & PANE_RESIZENOW) {
|
||||||
log_debug("%s: resizing %%%u after forced resize", __func__, wp->id);
|
log_debug("%s: resizing %%%u after forced resize",
|
||||||
|
__func__, wp->id);
|
||||||
window_pane_send_resize(wp, 0);
|
window_pane_send_resize(wp, 0);
|
||||||
wp->flags &= ~(PANE_RESIZE|PANE_RESIZEFORCE|PANE_RESIZENOW);
|
wp->flags &= ~(PANE_RESIZE|PANE_RESIZEFORCE|PANE_RESIZENOW);
|
||||||
} else if (!evtimer_pending(&wp->force_timer, NULL)) {
|
} else if (!evtimer_pending(&wp->force_timer, NULL)) {
|
||||||
log_debug("%s: forcing resize of %%%u", __func__, wp->id);
|
log_debug("%s: forcing resize of %%%u", __func__,
|
||||||
|
wp->id);
|
||||||
window_pane_send_resize(wp, 1);
|
window_pane_send_resize(wp, 1);
|
||||||
server_client_start_force_timer(wp);
|
server_client_start_force_timer(wp);
|
||||||
}
|
}
|
||||||
|
48
tmux.1
48
tmux.1
@ -1255,6 +1255,7 @@ specified multiple times.
|
|||||||
.It Xo Ic refresh-client
|
.It Xo Ic refresh-client
|
||||||
.Op Fl cDlLRSU
|
.Op Fl cDlLRSU
|
||||||
.Op Fl A Ar pane:state
|
.Op Fl A Ar pane:state
|
||||||
|
.Op Fl B Ar name:what:format
|
||||||
.Op Fl C Ar XxY
|
.Op Fl C Ar XxY
|
||||||
.Op Fl f Ar flags
|
.Op Fl f Ar flags
|
||||||
.Op Fl t Ar target-client
|
.Op Fl t Ar target-client
|
||||||
@ -1328,6 +1329,31 @@ will pause the pane.
|
|||||||
.Fl A
|
.Fl A
|
||||||
may be given multiple times for different panes.
|
may be given multiple times for different panes.
|
||||||
.Pp
|
.Pp
|
||||||
|
.Fl B
|
||||||
|
sets a subscription to a format for a control mode client.
|
||||||
|
The argument is split into three items by colons:
|
||||||
|
.Ar name
|
||||||
|
is a name for the subscription;
|
||||||
|
.Ar what
|
||||||
|
is a type of item to subscribe to;
|
||||||
|
.Ar format
|
||||||
|
is the format.
|
||||||
|
After a subscription is added, changes to the format are reported with the
|
||||||
|
.Ic %subscription-changed
|
||||||
|
notification, at most once a second.
|
||||||
|
If only the name is given, the subscription is removed.
|
||||||
|
.Ar what
|
||||||
|
may be empty to check the format only for the attached session, or one of:
|
||||||
|
a pane ID such as
|
||||||
|
.Ql %0 ;
|
||||||
|
.Ql %*
|
||||||
|
for all panes in the attached session;
|
||||||
|
an window ID such as
|
||||||
|
.Ql @0 ;
|
||||||
|
or
|
||||||
|
.Ql @*
|
||||||
|
for all windows in the attached session.
|
||||||
|
.Pp
|
||||||
.Fl f
|
.Fl f
|
||||||
sets a comma-separated list of client flags, see
|
sets a comma-separated list of client flags, see
|
||||||
.Ic attach-session .
|
.Ic attach-session .
|
||||||
@ -5932,7 +5958,7 @@ or an error occurred.
|
|||||||
If present,
|
If present,
|
||||||
.Ar reason
|
.Ar reason
|
||||||
describes why the client exited.
|
describes why the client exited.
|
||||||
.It Ic %extended-output Ar pane-id Ar age Ar ... : Ar value
|
.It Ic %extended-output Ar pane-id Ar age Ar ... \& : Ar value
|
||||||
New form of
|
New form of
|
||||||
.Ic %output
|
.Ic %output
|
||||||
sent when the
|
sent when the
|
||||||
@ -5980,6 +6006,26 @@ changed its active window to the window with ID
|
|||||||
.Ar window-id .
|
.Ar window-id .
|
||||||
.It Ic %sessions-changed
|
.It Ic %sessions-changed
|
||||||
A session was created or destroyed.
|
A session was created or destroyed.
|
||||||
|
.It Xo Ic %subscription-changed
|
||||||
|
.Ar name
|
||||||
|
.Ar session-id
|
||||||
|
.Ar window-id
|
||||||
|
.Ar window-index
|
||||||
|
.Ar pane-id ... \& :
|
||||||
|
.Ar value
|
||||||
|
.Xc
|
||||||
|
The value of the format associated with subscription
|
||||||
|
.Ar name
|
||||||
|
has changed to
|
||||||
|
.Ar value .
|
||||||
|
See
|
||||||
|
.Ic refresh-client
|
||||||
|
.Fl B .
|
||||||
|
Any arguments after
|
||||||
|
.Ar pane-id
|
||||||
|
up until a single
|
||||||
|
.Ql \&:
|
||||||
|
are for future use and should be ignored.
|
||||||
.It Ic %unlinked-window-add Ar window-id
|
.It Ic %unlinked-window-add Ar window-id
|
||||||
The window with ID
|
The window with ID
|
||||||
.Ar window-id
|
.Ar window-id
|
||||||
|
12
tmux.h
12
tmux.h
@ -1724,6 +1724,15 @@ struct client {
|
|||||||
};
|
};
|
||||||
TAILQ_HEAD(clients, 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
|
||||||
|
};
|
||||||
|
|
||||||
/* Key binding and key table. */
|
/* Key binding and key table. */
|
||||||
struct key_binding {
|
struct key_binding {
|
||||||
key_code key;
|
key_code key;
|
||||||
@ -2864,6 +2873,9 @@ void control_reset_offsets(struct client *);
|
|||||||
void printflike(2, 3) control_write(struct client *, const char *, ...);
|
void printflike(2, 3) control_write(struct client *, const char *, ...);
|
||||||
void control_write_output(struct client *, struct window_pane *);
|
void control_write_output(struct client *, struct window_pane *);
|
||||||
int control_all_done(struct client *);
|
int control_all_done(struct client *);
|
||||||
|
void control_add_sub(struct client *, const char *, enum control_sub_type,
|
||||||
|
int, const char *);
|
||||||
|
void control_remove_sub(struct client *, const char *);
|
||||||
|
|
||||||
/* control-notify.c */
|
/* control-notify.c */
|
||||||
void control_notify_input(struct client *, struct window_pane *,
|
void control_notify_input(struct client *, struct window_pane *,
|
||||||
|
Loading…
Reference in New Issue
Block a user