mirror of
				https://github.com/tmux/tmux.git
				synced 2025-11-04 00:56:10 +00:00 
			
		
		
		
	Merge branch 'obsd-master'
This commit is contained in:
		@@ -34,27 +34,62 @@ const struct cmd_entry cmd_refresh_client_entry = {
 | 
			
		||||
	.name = "refresh-client",
 | 
			
		||||
	.alias = "refresh",
 | 
			
		||||
 | 
			
		||||
	.args = { "A:cC:Df:F:lLRSt:U", 0, 1 },
 | 
			
		||||
	.usage = "[-cDlLRSU] [-A pane:state] [-C XxY] [-f flags] "
 | 
			
		||||
		 CMD_TARGET_CLIENT_USAGE " [adjustment]",
 | 
			
		||||
	.args = { "A:B:cC:Df:F:lLRSt:U", 0, 1 },
 | 
			
		||||
	.usage = "[-cDlLRSU] [-A pane:state] [-B name:what:format] "
 | 
			
		||||
		 "[-C XxY] [-f flags] " CMD_TARGET_CLIENT_USAGE " [adjustment]",
 | 
			
		||||
 | 
			
		||||
	.flags = CMD_AFTERHOOK|CMD_CLIENT_TFLAG,
 | 
			
		||||
	.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
 | 
			
		||||
cmd_refresh_client_update_offset(struct client *tc, const char *value)
 | 
			
		||||
{
 | 
			
		||||
	struct window_pane	*wp;
 | 
			
		||||
	char			*copy, *colon;
 | 
			
		||||
	char			*copy, *split;
 | 
			
		||||
	u_int			 pane;
 | 
			
		||||
 | 
			
		||||
	if (*value != '%')
 | 
			
		||||
		return;
 | 
			
		||||
	copy = xstrdup(value);
 | 
			
		||||
	if ((colon = strchr(copy, ':')) == NULL)
 | 
			
		||||
	if ((split = strchr(copy, ':')) == NULL)
 | 
			
		||||
		goto out;
 | 
			
		||||
	*colon++ = '\0';
 | 
			
		||||
	*split++ = '\0';
 | 
			
		||||
 | 
			
		||||
	if (sscanf(copy, "%%%u", &pane) != 1)
 | 
			
		||||
		goto out;
 | 
			
		||||
@@ -62,13 +97,13 @@ cmd_refresh_client_update_offset(struct client *tc, const char *value)
 | 
			
		||||
	if (wp == NULL)
 | 
			
		||||
		goto out;
 | 
			
		||||
 | 
			
		||||
	if (strcmp(colon, "on") == 0)
 | 
			
		||||
	if (strcmp(split, "on") == 0)
 | 
			
		||||
		control_set_pane_on(tc, wp);
 | 
			
		||||
	else if (strcmp(colon, "off") == 0)
 | 
			
		||||
	else if (strcmp(split, "off") == 0)
 | 
			
		||||
		control_set_pane_off(tc, wp);
 | 
			
		||||
	else if (strcmp(colon, "continue") == 0)
 | 
			
		||||
	else if (strcmp(split, "continue") == 0)
 | 
			
		||||
		control_continue_pane(tc, wp);
 | 
			
		||||
	else if (strcmp(colon, "pause") == 0)
 | 
			
		||||
	else if (strcmp(split, "pause") == 0)
 | 
			
		||||
		control_pause_pane(tc, wp);
 | 
			
		||||
 | 
			
		||||
out:
 | 
			
		||||
@@ -156,6 +191,16 @@ cmd_refresh_client_exec(struct cmd *self, struct cmdq_item *item)
 | 
			
		||||
		}
 | 
			
		||||
		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 (~tc->flags & CLIENT_CONTROL)
 | 
			
		||||
			goto not_control_client;
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										391
									
								
								control.c
									
									
									
									
									
								
							
							
						
						
									
										391
									
								
								control.c
									
									
									
									
									
								
							@@ -76,6 +76,42 @@ 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;
 | 
			
		||||
@@ -87,6 +123,9 @@ struct control_state {
 | 
			
		||||
 | 
			
		||||
	struct bufferevent		*read_event;
 | 
			
		||||
	struct bufferevent		*write_event;
 | 
			
		||||
 | 
			
		||||
	struct control_subs		 subs;
 | 
			
		||||
	struct event			 subs_timer;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/* 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);
 | 
			
		||||
 | 
			
		||||
/* 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. */
 | 
			
		||||
static void
 | 
			
		||||
control_free_block(struct control_state *cs, struct control_block *cb)
 | 
			
		||||
@@ -666,6 +772,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->read_event = bufferevent_new(c->fd, control_read_callback,
 | 
			
		||||
	    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_block	*cb, *cb1;
 | 
			
		||||
	struct control_sub	*csub, *csub1;
 | 
			
		||||
 | 
			
		||||
	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);
 | 
			
		||||
 | 
			
		||||
	TAILQ_FOREACH_SAFE(cb, &cs->all_blocks, all_entry, cb1)
 | 
			
		||||
		control_free_block(cs, cb);
 | 
			
		||||
	control_reset_offsets(c);
 | 
			
		||||
 | 
			
		||||
	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,
 | 
			
		||||
		    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);
 | 
			
		||||
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_flush(struct screen_write_ctx *, int,
 | 
			
		||||
		    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);
 | 
			
		||||
 | 
			
		||||
	if (!screen_write_collect_clear_end(ctx, s->cy, s->cx, bg)) {
 | 
			
		||||
		ci->x = s->cx;
 | 
			
		||||
		ci->type = CLEAR_END;
 | 
			
		||||
		ci->bg = bg;
 | 
			
		||||
		TAILQ_INSERT_TAIL(&ctx->s->write_list[s->cy].items, ci, entry);
 | 
			
		||||
		ctx->item = xcalloc(1, sizeof *ctx->item);
 | 
			
		||||
	}
 | 
			
		||||
 	screen_write_collect_clear_end(ctx, s->cy, s->cx);
 | 
			
		||||
	ci->x = s->cx;
 | 
			
		||||
	ci->type = CLEAR_END;
 | 
			
		||||
	ci->bg = bg;
 | 
			
		||||
	TAILQ_INSERT_TAIL(&ctx->s->write_list[s->cy].items, ci, entry);
 | 
			
		||||
	ctx->item = xcalloc(1, sizeof *ctx->item);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Clear to start of line from cursor. */
 | 
			
		||||
@@ -1154,13 +1153,12 @@ screen_write_clearstartofline(struct screen_write_ctx *ctx, u_int bg)
 | 
			
		||||
	else
 | 
			
		||||
		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)) {
 | 
			
		||||
		ci->x = s->cx;
 | 
			
		||||
		ci->type = CLEAR_START;
 | 
			
		||||
		ci->bg = bg;
 | 
			
		||||
		TAILQ_INSERT_TAIL(&ctx->s->write_list[s->cy].items, ci, entry);
 | 
			
		||||
		ctx->item = xcalloc(1, sizeof *ctx->item);
 | 
			
		||||
	}
 | 
			
		||||
	screen_write_collect_clear_start(ctx, s->cy, s->cx);
 | 
			
		||||
	ci->x = s->cx;
 | 
			
		||||
	ci->type = CLEAR_START;
 | 
			
		||||
	ci->bg = bg;
 | 
			
		||||
	TAILQ_INSERT_TAIL(&ctx->s->write_list[s->cy].items, ci, entry);
 | 
			
		||||
	ctx->item = xcalloc(1, sizeof *ctx->item);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Move cursor to px,py. */
 | 
			
		||||
@@ -1393,25 +1391,18 @@ screen_write_clearhistory(struct screen_write_ctx *ctx)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Clear to start of a collected line. */
 | 
			
		||||
static int
 | 
			
		||||
screen_write_collect_clear_start(struct screen_write_ctx *ctx, u_int y, u_int x,
 | 
			
		||||
    u_int bg)
 | 
			
		||||
static void
 | 
			
		||||
screen_write_collect_clear_start(struct screen_write_ctx *ctx, u_int y, u_int x)
 | 
			
		||||
{
 | 
			
		||||
	struct screen_write_collect_item	*ci, *tmp;
 | 
			
		||||
	size_t					 size = 0;
 | 
			
		||||
	u_int					 items = 0;
 | 
			
		||||
	int					 redundant = 0;
 | 
			
		||||
 | 
			
		||||
	if (TAILQ_EMPTY(&ctx->s->write_list[y].items))
 | 
			
		||||
		return (0);
 | 
			
		||||
		return;
 | 
			
		||||
	TAILQ_FOREACH_SAFE(ci, &ctx->s->write_list[y].items, entry, tmp) {
 | 
			
		||||
		switch (ci->type) {
 | 
			
		||||
		case CLEAR_START:
 | 
			
		||||
			if (ci->x >= x) {
 | 
			
		||||
				if (ci->bg == bg)
 | 
			
		||||
					redundant = 1;
 | 
			
		||||
				continue;
 | 
			
		||||
			}
 | 
			
		||||
			break;
 | 
			
		||||
		case CLEAR_END:
 | 
			
		||||
			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;
 | 
			
		||||
	log_debug("%s: dropped %u items (%zu bytes) (line %u)", __func__, items,
 | 
			
		||||
	    size, y);
 | 
			
		||||
	return (redundant);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Clear to end of a collected line. */
 | 
			
		||||
static int
 | 
			
		||||
screen_write_collect_clear_end(struct screen_write_ctx *ctx, u_int y, u_int x,
 | 
			
		||||
    u_int bg)
 | 
			
		||||
static void
 | 
			
		||||
screen_write_collect_clear_end(struct screen_write_ctx *ctx, u_int y, u_int x)
 | 
			
		||||
{
 | 
			
		||||
	struct screen_write_collect_item	*ci, *tmp;
 | 
			
		||||
	size_t					 size = 0;
 | 
			
		||||
	int					 redundant = 0;
 | 
			
		||||
	u_int					 items = 0;
 | 
			
		||||
 | 
			
		||||
	if (TAILQ_EMPTY(&ctx->s->write_list[y].items))
 | 
			
		||||
		return (0);
 | 
			
		||||
		return;
 | 
			
		||||
	TAILQ_FOREACH_SAFE(ci, &ctx->s->write_list[y].items, entry, tmp) {
 | 
			
		||||
		switch (ci->type) {
 | 
			
		||||
		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;
 | 
			
		||||
			continue;
 | 
			
		||||
		case CLEAR_END:
 | 
			
		||||
			if (ci->x <= x) {
 | 
			
		||||
				if (ci->bg == bg)
 | 
			
		||||
					redundant = 1;
 | 
			
		||||
				continue;
 | 
			
		||||
			}
 | 
			
		||||
			break;
 | 
			
		||||
		case TEXT:
 | 
			
		||||
			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;
 | 
			
		||||
	log_debug("%s: dropped %u items (%zu bytes) (line %u)", __func__, items,
 | 
			
		||||
	    size, y);
 | 
			
		||||
	return (redundant);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* 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) {
 | 
			
		||||
			screen_write_set_cursor(ctx, ci->x, y);
 | 
			
		||||
			if (ci->type == CLEAR_END) {
 | 
			
		||||
				log_debug("XXX %u %u", ci->x, ci->bg);
 | 
			
		||||
				screen_write_initctx(ctx, &ttyctx, 1);
 | 
			
		||||
				ttyctx.bg = ci->bg;
 | 
			
		||||
				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.
 | 
			
		||||
		 */
 | 
			
		||||
		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);
 | 
			
		||||
			wp->flags &= ~(PANE_RESIZE|PANE_RESIZEFORCE|PANE_RESIZENOW);
 | 
			
		||||
		} 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);
 | 
			
		||||
			server_client_start_force_timer(wp);
 | 
			
		||||
		}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										48
									
								
								tmux.1
									
									
									
									
									
								
							
							
						
						
									
										48
									
								
								tmux.1
									
									
									
									
									
								
							@@ -1255,6 +1255,7 @@ specified multiple times.
 | 
			
		||||
.It Xo Ic refresh-client
 | 
			
		||||
.Op Fl cDlLRSU
 | 
			
		||||
.Op Fl A Ar pane:state
 | 
			
		||||
.Op Fl B Ar name:what:format
 | 
			
		||||
.Op Fl C Ar XxY
 | 
			
		||||
.Op Fl f Ar flags
 | 
			
		||||
.Op Fl t Ar target-client
 | 
			
		||||
@@ -1328,6 +1329,31 @@ will pause the pane.
 | 
			
		||||
.Fl A
 | 
			
		||||
may be given multiple times for different panes.
 | 
			
		||||
.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
 | 
			
		||||
sets a comma-separated list of client flags, see
 | 
			
		||||
.Ic attach-session .
 | 
			
		||||
@@ -5932,7 +5958,7 @@ or an error occurred.
 | 
			
		||||
If present,
 | 
			
		||||
.Ar reason
 | 
			
		||||
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
 | 
			
		||||
.Ic %output
 | 
			
		||||
sent when the
 | 
			
		||||
@@ -5980,6 +6006,26 @@ changed its active window to the window with ID
 | 
			
		||||
.Ar window-id .
 | 
			
		||||
.It Ic %sessions-changed
 | 
			
		||||
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
 | 
			
		||||
The window with ID
 | 
			
		||||
.Ar window-id
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										12
									
								
								tmux.h
									
									
									
									
									
								
							
							
						
						
									
										12
									
								
								tmux.h
									
									
									
									
									
								
							@@ -1724,6 +1724,15 @@ 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
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/* Key binding and key table. */
 | 
			
		||||
struct key_binding {
 | 
			
		||||
	key_code		 key;
 | 
			
		||||
@@ -2864,6 +2873,9 @@ 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_remove_sub(struct client *, const char *);
 | 
			
		||||
 | 
			
		||||
/* control-notify.c */
 | 
			
		||||
void	control_notify_input(struct client *, struct window_pane *,
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user