Add a flag to make a client wait for an empty line before exiting in control mode to avoid stray commands ending up in the shell.

<|original_code|>
		return (s->cwd);
	if (c != NULL && (s = c->session) != NULL && s->cwd != NULL)
		return (s->cwd);
	if ((home = find_home()) != NULL)
		return (home);
	return ("/");
}

/* Get control client flags. */
static uint64_t
server_client_control_flags(struct client *c, const char *next)
{
	if (strcmp(next, "pause-after") == 0) {
		c->pause_age = 0;
		return (CLIENT_CONTROL_PAUSEAFTER);
	}
	if (sscanf(next, "pause-after=%u", &c->pause_age) == 1) {
		c->pause_age *= 1000;
		return (CLIENT_CONTROL_PAUSEAFTER);
	}
	if (strcmp(next, "no-output") == 0)
		return (CLIENT_CONTROL_NOOUTPUT);
	return (0);
}

/* Set client flags. */
void
server_client_set_flags(struct client *c, const char *flags)
{
	char	*s, *copy, *next;
	uint64_t flag;
	int	 not;

	s = copy = xstrdup (flags);
	while ((next = strsep(&s, ",")) != NULL) {
		not = (*next == '!');
		if (not)
			next++;

		if (c->flags & CLIENT_CONTROL)
			flag = server_client_control_flags(c, next);
		else
			flag = 0;
		if (strcmp(next, "read-only") == 0)
			flag = CLIENT_READONLY;
		else if (strcmp(next, "ignore-size") == 0)
			flag = CLIENT_IGNORESIZE;
		else if (strcmp(next, "active-pane") == 0)
			flag = CLIENT_ACTIVEPANE;
		if (flag == 0)
			continue;

		log_debug("client %s set flag %s", c->name, next);
		if (not)
			c->flags &= ~flag;
		else
			c->flags |= flag;
		if (flag == CLIENT_CONTROL_NOOUTPUT)
			control_reset_offsets(c);
	}
	free(copy);
}

/* Get client flags. This is only flags useful to show to users. */
const char *
server_client_get_flags(struct client *c)
{
	static char	s[256];
	char	 	tmp[32];

	*s = '\0';
	if (c->flags & CLIENT_ATTACHED)
		strlcat(s, "attached,", sizeof s);
	if (c->flags & CLIENT_CONTROL)
		strlcat(s, "control-mode,", sizeof s);
	if (c->flags & CLIENT_IGNORESIZE)
		strlcat(s, "ignore-size,", sizeof s);
	if (c->flags & CLIENT_CONTROL_NOOUTPUT)
		strlcat(s, "no-output,", sizeof s);
	if (c->flags & CLIENT_CONTROL_PAUSEAFTER) {
		xsnprintf(tmp, sizeof tmp, "pause-after=%u,",
		    c->pause_age / 1000);
		strlcat(s, tmp, sizeof s);
	}
	if (c->flags & CLIENT_READONLY)
		strlcat(s, "read-only,", sizeof s);
	if (c->flags & CLIENT_ACTIVEPANE)
		strlcat(s, "active-pane,", sizeof s);
	if (c->flags & CLIENT_SUSPENDED)
		strlcat(s, "suspended,", sizeof s);
	if (c->flags & CLIENT_UTF8)
		strlcat(s, "UTF-8,", sizeof s);
	if (*s != '\0')
		s[strlen(s) - 1] = '\0';
	return (s);
}

/* Get client window. */
static struct client_window *
server_client_get_client_window(struct client *c, u_int id)
{
	struct client_window	cw = { .window = id };

<|edits_diff|>
--- server-client.c
+++ server-client.c
@@ -20,6 +20,8 @@
 	}
 	if (strcmp(next, "no-output") == 0)
 		return (CLIENT_CONTROL_NOOUTPUT);
+	if (strcmp(next, "wait-exit") == 0)
+		return (CLIENT_CONTROL_WAITEXIT);
 	return (0);
 }

@@ -59,6 +61,7 @@
 			control_reset_offsets(c);
 	}
 	free(copy);
+	proc_send(c->peer, MSG_FLAGS, -1, &c->flags, sizeof c->flags);
 }

 /* Get client flags. This is only flags useful to show to users. */
<|current_version|>
		return (s->cwd);
	if (c != NULL && (s = c->session) != NULL && s->cwd != NULL)
		return (s->cwd);
	if ((home = find_home()) != NULL)
		return (home);
	return ("/");
}

/* Get control client flags. */
static uint64_t
server_client_control_flags(struct client *c, const char *next)
{
	if (strcmp(next, "pause-after") == 0) {
		c->pause_age = 0;
		return (CLIENT_CONTROL_PAUSEAFTER);
	}
	if (sscanf(next, "pause-after=%u", &c->pause_age) == 1) {
		c->pause_age *= 1000;
		return (CLIENT_CONTROL_PAUSEAFTER);
	}
	if (strcmp(next, "no-output") == 0)
		return (CLIENT_CONTROL_NOOUTPUT);
	if (strcmp(next, "wait-exit") == 0)
		return (CLIENT_CONTROL_WAITEXIT);
	return (0);
}

/* Set client flags. */
void
server_client_set_flags(struct client *c, const char *flags)
{
	char	*s, *copy, *next;
	uint64_t flag;
	int	 not;

	s = copy = xstrdup (flags);
	while ((next = strsep(&s, ",")) != NULL) {
		not = (*next == '!');
		if (not)
			next++;

		if (c->flags & CLIENT_CONTROL)
			flag = server_client_control_flags(c, next);
		else
			flag = 0;
		if (strcmp(next, "read-only") == 0)
			flag = CLIENT_READONLY;
		else if (strcmp(next, "ignore-size") == 0)
			flag = CLIENT_IGNORESIZE;
		else if (strcmp(next, "active-pane") == 0)
			flag = CLIENT_ACTIVEPANE;
		if (flag == 0)
			continue;

		log_debug("client %s set flag %s", c->name, next);
		if (not)
			c->flags &= ~flag;
		else
			c->flags |= flag;
		if (flag == CLIENT_CONTROL_NOOUTPUT)
			control_reset_offsets(c);
	}
	free(copy);
	proc_send(c->peer, MSG_FLAGS, -1, &c->flags, sizeof c->flags);
}

/* Get client flags. This is only flags useful to show to users. */
const char *
server_client_get_flags(struct client *c)
{
	static char	s[256];
	char	 	tmp[32];

	*s = '\0';
	if (c->flags & CLIENT_ATTACHED)
		strlcat(s, "attached,", sizeof s);
	if (c->flags & CLIENT_CONTROL)
		strlcat(s, "control-mode,", sizeof s);
	if (c->flags & CLIENT_IGNORESIZE)
		strlcat(s, "ignore-size,", sizeof s);
	if (c->flags & CLIENT_CONTROL_NOOUTPUT)
		strlcat(s, "no-output,", sizeof s);
	if (c->flags & CLIENT_CONTROL_PAUSEAFTER) {
		xsnprintf(tmp, sizeof tmp, "pause-after=%u,",
		    c->pause_age / 1000);
		strlcat(s, tmp, sizeof s);
	}
	if (c->flags & CLIENT_READONLY)
		strlcat(s, "read-only,", sizeof s);
	if (c->flags & CLIENT_ACTIVEPANE)
		strlcat(s, "active-pane,", sizeof s);
	if (c->flags & CLIENT_SUSPENDED)
		strlcat(s, "suspended,", sizeof s);
	if (c->flags & CLIENT_UTF8)
		strlcat(s, "UTF-8,", sizeof s);
	if (*s != '\0')
		s[strlen(s) - 1] = '\0';
	return (s);
}

/* Get client window. */
static struct client_window *
server_client_get_client_window(struct client *c, u_int id)
{
	struct client_window	cw = { .window = id };

<|next_version|>
		return (s->cwd);
	if (c != NULL && (s = c->session) != NULL && s->cwd != NULL)
		return (s->cwd);
	if ((home = find_home()) != NULL)
		return (home);
	return ("/");
}

/* Get control client flags. */
static uint64_t
server_client_control_flags(struct client *c, const char *next)
{
	if (strcmp(next, "pause-after") == 0) {
		c->pause_age = 0;
		return (CLIENT_CONTROL_PAUSEAFTER);
	}
	if (sscanf(next, "pause-after=%u", &c->pause_age) == 1) {
		c->pause_age *= 1000;
		return (CLIENT_CONTROL_PAUSEAFTER);
	}
	if (strcmp(next, "no-output") == 0)
		return (CLIENT_CONTROL_NOOUTPUT);
	if (strcmp(next, "wait-exit") == 0)
		return (CLIENT_CONTROL_WAITEXIT);
	return (0);
}

/* Set client flags. */
void
server_client_set_flags(struct client *c, const char *flags)
{
	char	*s, *copy, *next;
	uint64_t flag;
	int	 not;

	s = copy = xstrdup (flags);
	while ((next = strsep(&s, ",")) != NULL) {
		not = (*next == '!');
		if (not)
			next++;

		if (c->flags & CLIENT_CONTROL)
			flag = server_client_control_flags(c, next);
		else
			flag = 0;
		if (strcmp(next, "read-only") == 0)
			flag = CLIENT_READONLY;
		else if (strcmp(next, "ignore-size") == 0)
			flag = CLIENT_IGNORESIZE;
		else if (strcmp(next, "active-pane") == 0)
			flag = CLIENT_ACTIVEPANE;
		if (flag == 0)
			continue;

		log_debug("client %s set flag %s", c->name, next);
		if (not)
			c->flags &= ~flag;
		else
			c->flags |= flag;
		if (flag == CLIENT_CONTROL_NOOUTPUT)
			control_reset_offsets(c);
	}
	free(copy);
	proc_send(c->peer, MSG_FLAGS, -1, &c->flags, sizeof c->flags);
}

/* Get client flags. This is only flags useful to show to users. */
const char *
server_client_get_flags(struct client *c)
{
	static char	s[256];
	char	 	tmp[32];

	*s = '\0';
	if (c->flags & CLIENT_ATTACHED)
		strlcat(s, "attached,", sizeof s);
	if (c->flags & CLIENT_CONTROL)
		strlcat(s, "control-mode,", sizeof s);
	if (c->flags & CLIENT_IGNORESIZE)
		strlcat(s, "ignore-size,", sizeof s);
	if (c->flags & CLIENT_CONTROL_NOOUTPUT)
		strlcat(s, "no-output,", sizeof s);
	if (c->flags & CLIENT_CONTROL_WAITEXIT)
		strlcat(s, "wait-exit,", sizeof s);
	if (c->flags & CLIENT_CONTROL_PAUSEAFTER) {
		xsnprintf(tmp, sizeof tmp, "pause-after=%u,",
		    c->pause_age / 1000);
		strlcat(s, tmp, sizeof s);
	}
	if (c->flags & CLIENT_READONLY)
		strlcat(s, "read-only,", sizeof s);
	if (c->flags & CLIENT_ACTIVEPANE)
		strlcat(s, "active-pane,", sizeof s);
	if (c->flags & CLIENT_SUSPENDED)
		strlcat(s, "suspended,", sizeof s);
	if (c->flags & CLIENT_UTF8)
		strlcat(s, "UTF-8,", sizeof s);
	if (*s != '\0')
		s[strlen(s) - 1] = '\0';
	return (s);
}

/* Get client window. */
static struct client_window *
server_client_get_client_window(struct client *c, u_int id)
{
	struct client_window	cw = { .window = id };


Add support for the iTerm2 sychronized updates escape sequence which drastically reduces flickering.

<|original_code|>

	ctx->pane_status = options_get_number(wo, "pane-border-status");

	tty_window_offset(&c->tty, &ctx->ox, &ctx->oy, &ctx->sx, &ctx->sy);

	log_debug("%s: %s @%u ox=%u oy=%u sx=%u sy=%u %u/%d", __func__, c->name,
	    w->id, ctx->ox, ctx->oy, ctx->sx, ctx->sy, ctx->statuslines,
	    ctx->statustop);
}

/* Redraw entire screen. */
void
screen_redraw_screen(struct client *c)
{
	struct screen_redraw_ctx	ctx;
	int				flags;

	if (c->flags & CLIENT_SUSPENDED)
		return;

	flags = screen_redraw_update(c, c->flags);
	screen_redraw_set_context(c, &ctx);

	if (flags & (CLIENT_REDRAWWINDOW|CLIENT_REDRAWBORDERS)) {
		if (ctx.pane_status != PANE_STATUS_OFF)
			screen_redraw_draw_pane_status(&ctx);
		screen_redraw_draw_borders(&ctx);
	}
	if (flags & CLIENT_REDRAWWINDOW)
		screen_redraw_draw_panes(&ctx);
	if (ctx.statuslines != 0 &&
	    (flags & (CLIENT_REDRAWSTATUS|CLIENT_REDRAWSTATUSALWAYS)))
		screen_redraw_draw_status(&ctx);
	if (c->overlay_draw != NULL && (flags & CLIENT_REDRAWOVERLAY))
		c->overlay_draw(c, &ctx);
	tty_reset(&c->tty);
}

/* Redraw a single pane. */
void
screen_redraw_pane(struct client *c, struct window_pane *wp)
{
	struct screen_redraw_ctx	 ctx;

	if (c->overlay_draw != NULL || !window_pane_visible(wp))
		return;

	screen_redraw_set_context(c, &ctx);

	screen_redraw_draw_pane(&ctx, wp);
	tty_reset(&c->tty);
}

/* Draw a border cell. */
static void
screen_redraw_draw_borders_cell(struct screen_redraw_ctx *ctx, u_int i, u_int j,
    struct grid_cell *m_active_gc, struct grid_cell *active_gc,
    struct grid_cell *m_other_gc, struct grid_cell *other_gc)
{
	struct client		*c = ctx->c;
	struct session		*s = c->session;
	struct window		*w = s->curw->window;
	struct tty		*tty = &c->tty;
	struct window_pane	*wp;
	struct window_pane	*active = w->active;
	struct window_pane	*marked = marked_pane.wp;
	u_int			 type, x = ctx->ox + i, y = ctx->oy + j;
	int			 flag, pane_status = ctx->pane_status;

	if (c->overlay_check != NULL && !c->overlay_check(c, x, y))
		return;
	type = screen_redraw_check_cell(c, x, y, pane_status, &wp);
	if (type == CELL_INSIDE)
		return;
	flag = screen_redraw_check_is(x, y, type, pane_status, w, active, wp);
<|edits_diff|>
--- screen-redraw.c
+++ screen-redraw.c
@@ -20,6 +20,7 @@

 	flags = screen_redraw_update(c, c->flags);
 	screen_redraw_set_context(c, &ctx);
+	tty_sync_start(&c->tty);

 	if (flags & (CLIENT_REDRAWWINDOW|CLIENT_REDRAWBORDERS)) {
 		if (ctx.pane_status != PANE_STATUS_OFF)
@@ -33,7 +34,9 @@
 		screen_redraw_draw_status(&ctx);
 	if (c->overlay_draw != NULL && (flags & CLIENT_REDRAWOVERLAY))
 		c->overlay_draw(c, &ctx);
+
 	tty_reset(&c->tty);
+	tty_sync_end(&c->tty);
 }

 /* Redraw a single pane. */
@@ -46,8 +49,10 @@
 		return;

 	screen_redraw_set_context(c, &ctx);
+	tty_sync_start(&c->tty);

 	screen_redraw_draw_pane(&ctx, wp);
+
 	tty_reset(&c->tty);
 }

<|current_version|>

	ctx->pane_status = options_get_number(wo, "pane-border-status");

	tty_window_offset(&c->tty, &ctx->ox, &ctx->oy, &ctx->sx, &ctx->sy);

	log_debug("%s: %s @%u ox=%u oy=%u sx=%u sy=%u %u/%d", __func__, c->name,
	    w->id, ctx->ox, ctx->oy, ctx->sx, ctx->sy, ctx->statuslines,
	    ctx->statustop);
}

/* Redraw entire screen. */
void
screen_redraw_screen(struct client *c)
{
	struct screen_redraw_ctx	ctx;
	int				flags;

	if (c->flags & CLIENT_SUSPENDED)
		return;

	flags = screen_redraw_update(c, c->flags);
	screen_redraw_set_context(c, &ctx);
	tty_sync_start(&c->tty);

	if (flags & (CLIENT_REDRAWWINDOW|CLIENT_REDRAWBORDERS)) {
		if (ctx.pane_status != PANE_STATUS_OFF)
			screen_redraw_draw_pane_status(&ctx);
		screen_redraw_draw_borders(&ctx);
	}
	if (flags & CLIENT_REDRAWWINDOW)
		screen_redraw_draw_panes(&ctx);
	if (ctx.statuslines != 0 &&
	    (flags & (CLIENT_REDRAWSTATUS|CLIENT_REDRAWSTATUSALWAYS)))
		screen_redraw_draw_status(&ctx);
	if (c->overlay_draw != NULL && (flags & CLIENT_REDRAWOVERLAY))
		c->overlay_draw(c, &ctx);

	tty_reset(&c->tty);
	tty_sync_end(&c->tty);
}

/* Redraw a single pane. */
void
screen_redraw_pane(struct client *c, struct window_pane *wp)
{
	struct screen_redraw_ctx	 ctx;

	if (c->overlay_draw != NULL || !window_pane_visible(wp))
		return;

	screen_redraw_set_context(c, &ctx);
	tty_sync_start(&c->tty);

	screen_redraw_draw_pane(&ctx, wp);

	tty_reset(&c->tty);
}

/* Draw a border cell. */
static void
screen_redraw_draw_borders_cell(struct screen_redraw_ctx *ctx, u_int i, u_int j,
    struct grid_cell *m_active_gc, struct grid_cell *active_gc,
    struct grid_cell *m_other_gc, struct grid_cell *other_gc)
{
	struct client		*c = ctx->c;
	struct session		*s = c->session;
	struct window		*w = s->curw->window;
	struct tty		*tty = &c->tty;
	struct window_pane	*wp;
	struct window_pane	*active = w->active;
	struct window_pane	*marked = marked_pane.wp;
	u_int			 type, x = ctx->ox + i, y = ctx->oy + j;
	int			 flag, pane_status = ctx->pane_status;

	if (c->overlay_check != NULL && !c->overlay_check(c, x, y))
		return;
	type = screen_redraw_check_cell(c, x, y, pane_status, &wp);
	if (type == CELL_INSIDE)
		return;
	flag = screen_redraw_check_is(x, y, type, pane_status, w, active, wp);
<|next_version|>

	ctx->pane_status = options_get_number(wo, "pane-border-status");

	tty_window_offset(&c->tty, &ctx->ox, &ctx->oy, &ctx->sx, &ctx->sy);

	log_debug("%s: %s @%u ox=%u oy=%u sx=%u sy=%u %u/%d", __func__, c->name,
	    w->id, ctx->ox, ctx->oy, ctx->sx, ctx->sy, ctx->statuslines,
	    ctx->statustop);
}

/* Redraw entire screen. */
void
screen_redraw_screen(struct client *c)
{
	struct screen_redraw_ctx	ctx;
	int				flags;

	if (c->flags & CLIENT_SUSPENDED)
		return;

	flags = screen_redraw_update(c, c->flags);
	screen_redraw_set_context(c, &ctx);
	tty_sync_start(&c->tty);

	if (flags & (CLIENT_REDRAWWINDOW|CLIENT_REDRAWBORDERS)) {
		if (ctx.pane_status != PANE_STATUS_OFF)
			screen_redraw_draw_pane_status(&ctx);
		screen_redraw_draw_borders(&ctx);
	}
	if (flags & CLIENT_REDRAWWINDOW)
		screen_redraw_draw_panes(&ctx);
	if (ctx.statuslines != 0 &&
	    (flags & (CLIENT_REDRAWSTATUS|CLIENT_REDRAWSTATUSALWAYS)))
		screen_redraw_draw_status(&ctx);
	if (c->overlay_draw != NULL && (flags & CLIENT_REDRAWOVERLAY))
		c->overlay_draw(c, &ctx);

	tty_reset(&c->tty);
	tty_sync_end(&c->tty);
}

/* Redraw a single pane. */
void
screen_redraw_pane(struct client *c, struct window_pane *wp)
{
	struct screen_redraw_ctx	 ctx;

	if (c->overlay_draw != NULL || !window_pane_visible(wp))
		return;

	screen_redraw_set_context(c, &ctx);
	tty_sync_start(&c->tty);

	screen_redraw_draw_pane(&ctx, wp);

	tty_reset(&c->tty);
	tty_sync_end(&c->tty);
}

/* Draw a border cell. */
static void
screen_redraw_draw_borders_cell(struct screen_redraw_ctx *ctx, u_int i, u_int j,
    struct grid_cell *m_active_gc, struct grid_cell *active_gc,
    struct grid_cell *m_other_gc, struct grid_cell *other_gc)
{
	struct client		*c = ctx->c;
	struct session		*s = c->session;
	struct window		*w = s->curw->window;
	struct tty		*tty = &c->tty;
	struct window_pane	*wp;
	struct window_pane	*active = w->active;
	struct window_pane	*marked = marked_pane.wp;
	u_int			 type, x = ctx->ox + i, y = ctx->oy + j;
	int			 flag, pane_status = ctx->pane_status;

	if (c->overlay_check != NULL && !c->overlay_check(c, x, y))
		return;
	type = screen_redraw_check_cell(c, x, y, pane_status, &wp);
	if (type == CELL_INSIDE)
		return;
	flag = screen_redraw_check_is(x, y, type, pane_status, w, active, wp);


Add a per-pane option set. Pane options inherit from window options (so there should be no change to existing behaviour) and are set and shown with set-option -p and show-options -p.

<|original_code|>

	if (src_wp == dst_wp)
		return (CMD_RETURN_NORMAL);

	tmp_wp = TAILQ_PREV(dst_wp, window_panes, entry);
	TAILQ_REMOVE(&dst_w->panes, dst_wp, entry);
	TAILQ_REPLACE(&src_w->panes, src_wp, dst_wp, entry);
	if (tmp_wp == src_wp)
		tmp_wp = dst_wp;
	if (tmp_wp == NULL)
		TAILQ_INSERT_HEAD(&dst_w->panes, src_wp, entry);
	else
		TAILQ_INSERT_AFTER(&dst_w->panes, tmp_wp, src_wp, entry);

	src_lc = src_wp->layout_cell;
	dst_lc = dst_wp->layout_cell;
	src_lc->wp = dst_wp;
	dst_wp->layout_cell = src_lc;
	dst_lc->wp = src_wp;
	src_wp->layout_cell = dst_lc;

	src_wp->window = dst_w;
	dst_wp->window = src_w;

	sx = src_wp->sx; sy = src_wp->sy;
	xoff = src_wp->xoff; yoff = src_wp->yoff;
	src_wp->xoff = dst_wp->xoff; src_wp->yoff = dst_wp->yoff;
	window_pane_resize(src_wp, dst_wp->sx, dst_wp->sy);
	dst_wp->xoff = xoff; dst_wp->yoff = yoff;
	window_pane_resize(dst_wp, sx, sy);

	if (!args_has(self->args, 'd')) {
		if (src_w != dst_w) {
			window_set_active_pane(src_w, dst_wp, 1);
			window_set_active_pane(dst_w, src_wp, 1);
		} else {
			tmp_wp = dst_wp;
			window_set_active_pane(src_w, tmp_wp, 1);
		}
	} else {
		if (src_w->active == src_wp)
			window_set_active_pane(src_w, dst_wp, 1);
		if (dst_w->active == dst_wp)
			window_set_active_pane(dst_w, src_wp, 1);
	}
	if (src_w != dst_w) {
		if (src_w->last == src_wp)
<|edits_diff|>
--- cmd-swap-pane.c
+++ cmd-swap-pane.c
@@ -20,6 +20,8 @@
 	src_wp->layout_cell = dst_lc;

 	src_wp->window = dst_w;
+	options_set_parent(src_wp->options, dst_w->options);
+	src_wp->flags |= PANE_STYLECHANGED;
 	dst_wp->window = src_w;

 	sx = src_wp->sx; sy = src_wp->sy;
<|current_version|>

	if (src_wp == dst_wp)
		return (CMD_RETURN_NORMAL);

	tmp_wp = TAILQ_PREV(dst_wp, window_panes, entry);
	TAILQ_REMOVE(&dst_w->panes, dst_wp, entry);
	TAILQ_REPLACE(&src_w->panes, src_wp, dst_wp, entry);
	if (tmp_wp == src_wp)
		tmp_wp = dst_wp;
	if (tmp_wp == NULL)
		TAILQ_INSERT_HEAD(&dst_w->panes, src_wp, entry);
	else
		TAILQ_INSERT_AFTER(&dst_w->panes, tmp_wp, src_wp, entry);

	src_lc = src_wp->layout_cell;
	dst_lc = dst_wp->layout_cell;
	src_lc->wp = dst_wp;
	dst_wp->layout_cell = src_lc;
	dst_lc->wp = src_wp;
	src_wp->layout_cell = dst_lc;

	src_wp->window = dst_w;
	options_set_parent(src_wp->options, dst_w->options);
	src_wp->flags |= PANE_STYLECHANGED;
	dst_wp->window = src_w;

	sx = src_wp->sx; sy = src_wp->sy;
	xoff = src_wp->xoff; yoff = src_wp->yoff;
	src_wp->xoff = dst_wp->xoff; src_wp->yoff = dst_wp->yoff;
	window_pane_resize(src_wp, dst_wp->sx, dst_wp->sy);
	dst_wp->xoff = xoff; dst_wp->yoff = yoff;
	window_pane_resize(dst_wp, sx, sy);

	if (!args_has(self->args, 'd')) {
		if (src_w != dst_w) {
			window_set_active_pane(src_w, dst_wp, 1);
			window_set_active_pane(dst_w, src_wp, 1);
		} else {
			tmp_wp = dst_wp;
			window_set_active_pane(src_w, tmp_wp, 1);
		}
	} else {
		if (src_w->active == src_wp)
			window_set_active_pane(src_w, dst_wp, 1);
		if (dst_w->active == dst_wp)
			window_set_active_pane(dst_w, src_wp, 1);
	}
	if (src_w != dst_w) {
		if (src_w->last == src_wp)
<|next_version|>

	if (src_wp == dst_wp)
		return (CMD_RETURN_NORMAL);

	tmp_wp = TAILQ_PREV(dst_wp, window_panes, entry);
	TAILQ_REMOVE(&dst_w->panes, dst_wp, entry);
	TAILQ_REPLACE(&src_w->panes, src_wp, dst_wp, entry);
	if (tmp_wp == src_wp)
		tmp_wp = dst_wp;
	if (tmp_wp == NULL)
		TAILQ_INSERT_HEAD(&dst_w->panes, src_wp, entry);
	else
		TAILQ_INSERT_AFTER(&dst_w->panes, tmp_wp, src_wp, entry);

	src_lc = src_wp->layout_cell;
	dst_lc = dst_wp->layout_cell;
	src_lc->wp = dst_wp;
	dst_wp->layout_cell = src_lc;
	dst_lc->wp = src_wp;
	src_wp->layout_cell = dst_lc;

	src_wp->window = dst_w;
	options_set_parent(src_wp->options, dst_w->options);
	src_wp->flags |= PANE_STYLECHANGED;
	dst_wp->window = src_w;
	options_set_parent(dst_wp->options, src_w->options);
	dst_wp->flags |= PANE_STYLECHANGED;

	sx = src_wp->sx; sy = src_wp->sy;
	xoff = src_wp->xoff; yoff = src_wp->yoff;
	src_wp->xoff = dst_wp->xoff; src_wp->yoff = dst_wp->yoff;
	window_pane_resize(src_wp, dst_wp->sx, dst_wp->sy);
	dst_wp->xoff = xoff; dst_wp->yoff = yoff;
	window_pane_resize(dst_wp, sx, sy);

	if (!args_has(self->args, 'd')) {
		if (src_w != dst_w) {
			window_set_active_pane(src_w, dst_wp, 1);
			window_set_active_pane(dst_w, src_wp, 1);
		} else {
			tmp_wp = dst_wp;
			window_set_active_pane(src_w, tmp_wp, 1);
		}
	} else {
		if (src_w->active == src_wp)
			window_set_active_pane(src_w, dst_wp, 1);
		if (dst_w->active == dst_wp)
			window_set_active_pane(dst_w, src_wp, 1);
	}
	if (src_w != dst_w) {
		if (src_w->last == src_wp)


Free old strings after they have been expanded in format_choose.

<|original_code|>
	}
	if (*s == '\0')
		return (NULL);
	return (s);
}

/* Return left and right alternatives separated by commas. */
static int
format_choose(struct format_tree *ft, const char *s, char **left, char **right,
    int expand)
{
	const char	*cp;
	char		*left0, *right0;

	cp = format_skip(s, ",");
	if (cp == NULL)
		return (-1);
	left0 = xstrndup(s, cp - s);
	right0 = xstrdup(cp + 1);

	if (expand) {
		*left = format_expand(ft, left0);
		*right = format_expand(ft, right0);
	} else {
		*left = left0;
		*right = right0;
	}
	return (0);
}

/* Is this true? */
int
format_true(const char *s)
{
	if (s != NULL && *s != '\0' && (s[0] != '0' || s[1] != '\0'))
		return (1);
	return (0);
}

/* Check if modifier end. */
static int
format_is_end(char c)
{
	return (c == ';' || c == ':');
}

/* Add to modifier list. */
<|edits_diff|>
--- format.c
+++ format.c
@@ -20,6 +20,7 @@

 	if (expand) {
 		*left = format_expand(ft, left0);
+		free(left0);
 		*right = format_expand(ft, right0);
 	} else {
 		*left = left0;
<|current_version|>
	}
	if (*s == '\0')
		return (NULL);
	return (s);
}

/* Return left and right alternatives separated by commas. */
static int
format_choose(struct format_tree *ft, const char *s, char **left, char **right,
    int expand)
{
	const char	*cp;
	char		*left0, *right0;

	cp = format_skip(s, ",");
	if (cp == NULL)
		return (-1);
	left0 = xstrndup(s, cp - s);
	right0 = xstrdup(cp + 1);

	if (expand) {
		*left = format_expand(ft, left0);
		free(left0);
		*right = format_expand(ft, right0);
	} else {
		*left = left0;
		*right = right0;
	}
	return (0);
}

/* Is this true? */
int
format_true(const char *s)
{
	if (s != NULL && *s != '\0' && (s[0] != '0' || s[1] != '\0'))
		return (1);
	return (0);
}

/* Check if modifier end. */
static int
format_is_end(char c)
{
	return (c == ';' || c == ':');
}

/* Add to modifier list. */
<|next_version|>
	}
	if (*s == '\0')
		return (NULL);
	return (s);
}

/* Return left and right alternatives separated by commas. */
static int
format_choose(struct format_tree *ft, const char *s, char **left, char **right,
    int expand)
{
	const char	*cp;
	char		*left0, *right0;

	cp = format_skip(s, ",");
	if (cp == NULL)
		return (-1);
	left0 = xstrndup(s, cp - s);
	right0 = xstrdup(cp + 1);

	if (expand) {
		*left = format_expand(ft, left0);
		free(left0);
		*right = format_expand(ft, right0);
		free(right0);
	} else {
		*left = left0;
		*right = right0;
	}
	return (0);
}

/* Is this true? */
int
format_true(const char *s)
{
	if (s != NULL && *s != '\0' && (s[0] != '0' || s[1] != '\0'))
		return (1);
	return (0);
}

/* Check if modifier end. */
static int
format_is_end(char c)
{
	return (c == ';' || c == ':');
}

/* Add to modifier list. */


evbuffer_new and bufferevent_new can both fail (when malloc fails) and return NULL. GitHub issue 1547.

<|original_code|>
server_client_create(int fd)
{
	struct client	*c;

	setblocking(fd, 0);

	c = xcalloc(1, sizeof *c);
	c->references = 1;
	c->peer = proc_add_peer(server_proc, fd, server_client_dispatch, c);

	if (gettimeofday(&c->creation_time, NULL) != 0)
		fatal("gettimeofday failed");
	memcpy(&c->activity_time, &c->creation_time, sizeof c->activity_time);

	c->environ = environ_create();

	c->fd = -1;
	c->cwd = NULL;

	TAILQ_INIT(&c->queue);

	c->stdin_data = evbuffer_new();
	c->stdout_data = evbuffer_new();
	c->stderr_data = evbuffer_new();

	c->tty.fd = -1;
	c->title = NULL;

	c->session = NULL;
	c->last_session = NULL;

	c->tty.sx = 80;
	c->tty.sy = 24;

	screen_init(&c->status.status, c->tty.sx, 1, 0);

	c->message_string = NULL;
	TAILQ_INIT(&c->message_log);

	c->prompt_string = NULL;
	c->prompt_buffer = NULL;
	c->prompt_index = 0;

	c->flags |= CLIENT_FOCUSED;

	c->keytable = key_bindings_get_table("root", 1);
	c->keytable->references++;

<|edits_diff|>
--- server-client.c
+++ server-client.c
@@ -20,7 +20,11 @@
 	TAILQ_INIT(&c->queue);

 	c->stdin_data = evbuffer_new();
+	if (c->stdin_data == NULL)
+		fatalx("out of memory");
 	c->stdout_data = evbuffer_new();
+	if (c->stdout_data == NULL)
+		fatalx("out of memory");
 	c->stderr_data = evbuffer_new();

 	c->tty.fd = -1;
<|current_version|>
server_client_create(int fd)
{
	struct client	*c;

	setblocking(fd, 0);

	c = xcalloc(1, sizeof *c);
	c->references = 1;
	c->peer = proc_add_peer(server_proc, fd, server_client_dispatch, c);

	if (gettimeofday(&c->creation_time, NULL) != 0)
		fatal("gettimeofday failed");
	memcpy(&c->activity_time, &c->creation_time, sizeof c->activity_time);

	c->environ = environ_create();

	c->fd = -1;
	c->cwd = NULL;

	TAILQ_INIT(&c->queue);

	c->stdin_data = evbuffer_new();
	if (c->stdin_data == NULL)
		fatalx("out of memory");
	c->stdout_data = evbuffer_new();
	if (c->stdout_data == NULL)
		fatalx("out of memory");
	c->stderr_data = evbuffer_new();

	c->tty.fd = -1;
	c->title = NULL;

	c->session = NULL;
	c->last_session = NULL;

	c->tty.sx = 80;
	c->tty.sy = 24;

	screen_init(&c->status.status, c->tty.sx, 1, 0);

	c->message_string = NULL;
	TAILQ_INIT(&c->message_log);

	c->prompt_string = NULL;
	c->prompt_buffer = NULL;
	c->prompt_index = 0;

	c->flags |= CLIENT_FOCUSED;

	c->keytable = key_bindings_get_table("root", 1);
	c->keytable->references++;

<|next_version|>
server_client_create(int fd)
{
	struct client	*c;

	setblocking(fd, 0);

	c = xcalloc(1, sizeof *c);
	c->references = 1;
	c->peer = proc_add_peer(server_proc, fd, server_client_dispatch, c);

	if (gettimeofday(&c->creation_time, NULL) != 0)
		fatal("gettimeofday failed");
	memcpy(&c->activity_time, &c->creation_time, sizeof c->activity_time);

	c->environ = environ_create();

	c->fd = -1;
	c->cwd = NULL;

	TAILQ_INIT(&c->queue);

	c->stdin_data = evbuffer_new();
	if (c->stdin_data == NULL)
		fatalx("out of memory");
	c->stdout_data = evbuffer_new();
	if (c->stdout_data == NULL)
		fatalx("out of memory");
	c->stderr_data = evbuffer_new();
	if (c->stderr_data == NULL)
		fatalx("out of memory");

	c->tty.fd = -1;
	c->title = NULL;

	c->session = NULL;
	c->last_session = NULL;

	c->tty.sx = 80;
	c->tty.sy = 24;

	screen_init(&c->status.status, c->tty.sx, 1, 0);

	c->message_string = NULL;
	TAILQ_INIT(&c->message_log);

	c->prompt_string = NULL;
	c->prompt_buffer = NULL;
	c->prompt_index = 0;

	c->flags |= CLIENT_FOCUSED;

	c->keytable = key_bindings_get_table("root", 1);
	c->keytable->references++;


Missing va_end, from Anton Lindqvist.

<|original_code|>
/* Log a debug message. */
void
log_debug(const char *msg, ...)
{
	va_list	ap;

	va_start(ap, msg);
	log_vwrite(msg, ap);
	va_end(ap);
}

/* Log a critical error with error string and die. */
__dead void
fatal(const char *msg, ...)
{
	char	*fmt;
	va_list	 ap;

	va_start(ap, msg);
	if (asprintf(&fmt, "fatal: %s: %s", msg, strerror(errno)) == -1)
		exit(1);
	log_vwrite(fmt, ap);
	exit(1);
}

/* Log a critical error and die. */
__dead void
fatalx(const char *msg, ...)
{
	char	*fmt;
	va_list	 ap;

	va_start(ap, msg);
	if (asprintf(&fmt, "fatal: %s", msg) == -1)
		exit(1);
	log_vwrite(fmt, ap);
	exit(1);
}

<|edits_diff|>
--- log.c
+++ log.c
@@ -20,6 +20,7 @@
 	if (asprintf(&fmt, "fatal: %s: %s", msg, strerror(errno)) == -1)
 		exit(1);
 	log_vwrite(fmt, ap);
+	va_end(ap);
 	exit(1);
 }

<|current_version|>
/* Log a debug message. */
void
log_debug(const char *msg, ...)
{
	va_list	ap;

	va_start(ap, msg);
	log_vwrite(msg, ap);
	va_end(ap);
}

/* Log a critical error with error string and die. */
__dead void
fatal(const char *msg, ...)
{
	char	*fmt;
	va_list	 ap;

	va_start(ap, msg);
	if (asprintf(&fmt, "fatal: %s: %s", msg, strerror(errno)) == -1)
		exit(1);
	log_vwrite(fmt, ap);
	va_end(ap);
	exit(1);
}

/* Log a critical error and die. */
__dead void
fatalx(const char *msg, ...)
{
	char	*fmt;
	va_list	 ap;

	va_start(ap, msg);
	if (asprintf(&fmt, "fatal: %s", msg) == -1)
		exit(1);
	log_vwrite(fmt, ap);
	exit(1);
}

<|next_version|>
/* Log a debug message. */
void
log_debug(const char *msg, ...)
{
	va_list	ap;

	va_start(ap, msg);
	log_vwrite(msg, ap);
	va_end(ap);
}

/* Log a critical error with error string and die. */
__dead void
fatal(const char *msg, ...)
{
	char	*fmt;
	va_list	 ap;

	va_start(ap, msg);
	if (asprintf(&fmt, "fatal: %s: %s", msg, strerror(errno)) == -1)
		exit(1);
	log_vwrite(fmt, ap);
	va_end(ap);
	exit(1);
}

/* Log a critical error and die. */
__dead void
fatalx(const char *msg, ...)
{
	char	*fmt;
	va_list	 ap;

	va_start(ap, msg);
	if (asprintf(&fmt, "fatal: %s", msg) == -1)
		exit(1);
	log_vwrite(fmt, ap);
	va_end(ap);
	exit(1);
}


Move the cursor back into the last column on CUU/CUD to match xterm behaviour. From George Nachman.

<|original_code|>

	s->mode &= ~mode;
}

/* Cursor up by ny. */
void
screen_write_cursorup(struct screen_write_ctx *ctx, u_int ny)
{
	struct screen	*s = ctx->s;

	if (ny == 0)
		ny = 1;

	if (s->cy < s->rupper) {
		/* Above region. */
		if (ny > s->cy)
			ny = s->cy;
	} else {
		/* Below region. */
		if (ny > s->cy - s->rupper)
			ny = s->cy - s->rupper;
	}
	if (ny == 0)
		return;

	s->cy -= ny;
}

/* Cursor down by ny. */
void
screen_write_cursordown(struct screen_write_ctx *ctx, u_int ny)
{
	struct screen	*s = ctx->s;

	if (ny == 0)
		ny = 1;

	if (s->cy > s->rlower) {
		/* Below region. */
		if (ny > screen_size_y(s) - 1 - s->cy)
			ny = screen_size_y(s) - 1 - s->cy;
	} else {
		/* Above region. */
		if (ny > s->rlower - s->cy)
			ny = s->rlower - s->cy;
	}
	if (ny == 0)
		return;

	s->cy += ny;
}

/* Cursor right by nx.  */
void
screen_write_cursorright(struct screen_write_ctx *ctx, u_int nx)
{
	struct screen	*s = ctx->s;

	if (nx == 0)
		nx = 1;

	if (nx > screen_size_x(s) - 1 - s->cx)
		nx = screen_size_x(s) - 1 - s->cx;
	if (nx == 0)
		return;

	s->cx += nx;
}

/* Cursor left by nx. */
<|edits_diff|>
--- screen-write.c
+++ screen-write.c
@@ -20,6 +20,8 @@
 		if (ny > s->cy - s->rupper)
 			ny = s->cy - s->rupper;
 	}
+	if (s->cx == screen_size_x(s))
+	    s->cx--;
 	if (ny == 0)
 		return;

<|current_version|>

	s->mode &= ~mode;
}

/* Cursor up by ny. */
void
screen_write_cursorup(struct screen_write_ctx *ctx, u_int ny)
{
	struct screen	*s = ctx->s;

	if (ny == 0)
		ny = 1;

	if (s->cy < s->rupper) {
		/* Above region. */
		if (ny > s->cy)
			ny = s->cy;
	} else {
		/* Below region. */
		if (ny > s->cy - s->rupper)
			ny = s->cy - s->rupper;
	}
	if (s->cx == screen_size_x(s))
	    s->cx--;
	if (ny == 0)
		return;

	s->cy -= ny;
}

/* Cursor down by ny. */
void
screen_write_cursordown(struct screen_write_ctx *ctx, u_int ny)
{
	struct screen	*s = ctx->s;

	if (ny == 0)
		ny = 1;

	if (s->cy > s->rlower) {
		/* Below region. */
		if (ny > screen_size_y(s) - 1 - s->cy)
			ny = screen_size_y(s) - 1 - s->cy;
	} else {
		/* Above region. */
		if (ny > s->rlower - s->cy)
			ny = s->rlower - s->cy;
	}
	if (ny == 0)
		return;

	s->cy += ny;
}

/* Cursor right by nx.  */
void
screen_write_cursorright(struct screen_write_ctx *ctx, u_int nx)
{
	struct screen	*s = ctx->s;

	if (nx == 0)
		nx = 1;

	if (nx > screen_size_x(s) - 1 - s->cx)
		nx = screen_size_x(s) - 1 - s->cx;
	if (nx == 0)
		return;

	s->cx += nx;
}

/* Cursor left by nx. */
<|next_version|>

	s->mode &= ~mode;
}

/* Cursor up by ny. */
void
screen_write_cursorup(struct screen_write_ctx *ctx, u_int ny)
{
	struct screen	*s = ctx->s;

	if (ny == 0)
		ny = 1;

	if (s->cy < s->rupper) {
		/* Above region. */
		if (ny > s->cy)
			ny = s->cy;
	} else {
		/* Below region. */
		if (ny > s->cy - s->rupper)
			ny = s->cy - s->rupper;
	}
	if (s->cx == screen_size_x(s))
	    s->cx--;
	if (ny == 0)
		return;

	s->cy -= ny;
}

/* Cursor down by ny. */
void
screen_write_cursordown(struct screen_write_ctx *ctx, u_int ny)
{
	struct screen	*s = ctx->s;

	if (ny == 0)
		ny = 1;

	if (s->cy > s->rlower) {
		/* Below region. */
		if (ny > screen_size_y(s) - 1 - s->cy)
			ny = screen_size_y(s) - 1 - s->cy;
	} else {
		/* Above region. */
		if (ny > s->rlower - s->cy)
			ny = s->rlower - s->cy;
	}
	if (s->cx == screen_size_x(s))
	    s->cx--;
	if (ny == 0)
		return;

	s->cy += ny;
}

/* Cursor right by nx.  */
void
screen_write_cursorright(struct screen_write_ctx *ctx, u_int nx)
{
	struct screen	*s = ctx->s;

	if (nx == 0)
		nx = 1;

	if (nx > screen_size_x(s) - 1 - s->cx)
		nx = screen_size_x(s) - 1 - s->cx;
	if (nx == 0)
		return;

	s->cx += nx;
}

/* Cursor left by nx. */


Add resize-pane -Z to temporary zoom the active pane to occupy the full window or unzoom (restored to the normal layout) if it already zoomed, bound to C-b z by default. The pane is unzoomed on pretty much any excuse whatsoever.

<|original_code|>
{
	return (join_pane(self, cmdq, self->entry == &cmd_join_pane_entry));
}

enum cmd_retval
join_pane(struct cmd *self, struct cmd_q *cmdq, int not_same_window)
{
	struct args		*args = self->args;
	struct session		*dst_s;
	struct winlink		*src_wl, *dst_wl;
	struct window		*src_w, *dst_w;
	struct window_pane	*src_wp, *dst_wp;
	char			*cause;
	int			 size, percentage, dst_idx;
	enum layout_type	 type;
	struct layout_cell	*lc;

	dst_wl = cmd_find_pane(cmdq, args_get(args, 't'), &dst_s, &dst_wp);
	if (dst_wl == NULL)
		return (CMD_RETURN_ERROR);
	dst_w = dst_wl->window;
	dst_idx = dst_wl->idx;

	src_wl = cmd_find_pane(cmdq, args_get(args, 's'), NULL, &src_wp);
	if (src_wl == NULL)
		return (CMD_RETURN_ERROR);
	src_w = src_wl->window;

	if (not_same_window && src_w == dst_w) {
		cmdq_error(cmdq, "can't join a pane to its own window");
		return (CMD_RETURN_ERROR);
	}
	if (!not_same_window && src_wp == dst_wp) {
		cmdq_error(cmdq, "source and target panes must be different");
		return (CMD_RETURN_ERROR);
	}

	type = LAYOUT_TOPBOTTOM;
	if (args_has(args, 'h'))
		type = LAYOUT_LEFTRIGHT;

	size = -1;
	if (args_has(args, 'l')) {
		size = args_strtonum(args, 'l', 0, INT_MAX, &cause);
		if (cause != NULL) {
			cmdq_error(cmdq, "size %s", cause);
			free(cause);
			return (CMD_RETURN_ERROR);
		}
	} else if (args_has(args, 'p')) {
		percentage = args_strtonum(args, 'p', 0, 100, &cause);
<|edits_diff|>
--- cmd-join-pane.c
+++ cmd-join-pane.c
@@ -20,6 +20,7 @@
 		return (CMD_RETURN_ERROR);
 	dst_w = dst_wl->window;
 	dst_idx = dst_wl->idx;
+	server_unzoom_window(dst_w);

 	src_wl = cmd_find_pane(cmdq, args_get(args, 's'), NULL, &src_wp);
 	if (src_wl == NULL)
<|current_version|>
{
	return (join_pane(self, cmdq, self->entry == &cmd_join_pane_entry));
}

enum cmd_retval
join_pane(struct cmd *self, struct cmd_q *cmdq, int not_same_window)
{
	struct args		*args = self->args;
	struct session		*dst_s;
	struct winlink		*src_wl, *dst_wl;
	struct window		*src_w, *dst_w;
	struct window_pane	*src_wp, *dst_wp;
	char			*cause;
	int			 size, percentage, dst_idx;
	enum layout_type	 type;
	struct layout_cell	*lc;

	dst_wl = cmd_find_pane(cmdq, args_get(args, 't'), &dst_s, &dst_wp);
	if (dst_wl == NULL)
		return (CMD_RETURN_ERROR);
	dst_w = dst_wl->window;
	dst_idx = dst_wl->idx;
	server_unzoom_window(dst_w);

	src_wl = cmd_find_pane(cmdq, args_get(args, 's'), NULL, &src_wp);
	if (src_wl == NULL)
		return (CMD_RETURN_ERROR);
	src_w = src_wl->window;

	if (not_same_window && src_w == dst_w) {
		cmdq_error(cmdq, "can't join a pane to its own window");
		return (CMD_RETURN_ERROR);
	}
	if (!not_same_window && src_wp == dst_wp) {
		cmdq_error(cmdq, "source and target panes must be different");
		return (CMD_RETURN_ERROR);
	}

	type = LAYOUT_TOPBOTTOM;
	if (args_has(args, 'h'))
		type = LAYOUT_LEFTRIGHT;

	size = -1;
	if (args_has(args, 'l')) {
		size = args_strtonum(args, 'l', 0, INT_MAX, &cause);
		if (cause != NULL) {
			cmdq_error(cmdq, "size %s", cause);
			free(cause);
			return (CMD_RETURN_ERROR);
		}
	} else if (args_has(args, 'p')) {
		percentage = args_strtonum(args, 'p', 0, 100, &cause);
<|next_version|>
{
	return (join_pane(self, cmdq, self->entry == &cmd_join_pane_entry));
}

enum cmd_retval
join_pane(struct cmd *self, struct cmd_q *cmdq, int not_same_window)
{
	struct args		*args = self->args;
	struct session		*dst_s;
	struct winlink		*src_wl, *dst_wl;
	struct window		*src_w, *dst_w;
	struct window_pane	*src_wp, *dst_wp;
	char			*cause;
	int			 size, percentage, dst_idx;
	enum layout_type	 type;
	struct layout_cell	*lc;

	dst_wl = cmd_find_pane(cmdq, args_get(args, 't'), &dst_s, &dst_wp);
	if (dst_wl == NULL)
		return (CMD_RETURN_ERROR);
	dst_w = dst_wl->window;
	dst_idx = dst_wl->idx;
	server_unzoom_window(dst_w);

	src_wl = cmd_find_pane(cmdq, args_get(args, 's'), NULL, &src_wp);
	if (src_wl == NULL)
		return (CMD_RETURN_ERROR);
	src_w = src_wl->window;
	server_unzoom_window(src_w);

	if (not_same_window && src_w == dst_w) {
		cmdq_error(cmdq, "can't join a pane to its own window");
		return (CMD_RETURN_ERROR);
	}
	if (!not_same_window && src_wp == dst_wp) {
		cmdq_error(cmdq, "source and target panes must be different");
		return (CMD_RETURN_ERROR);
	}

	type = LAYOUT_TOPBOTTOM;
	if (args_has(args, 'h'))
		type = LAYOUT_LEFTRIGHT;

	size = -1;
	if (args_has(args, 'l')) {
		size = args_strtonum(args, 'l', 0, INT_MAX, &cause);
		if (cause != NULL) {
			cmdq_error(cmdq, "size %s", cause);
			free(cause);
			return (CMD_RETURN_ERROR);
		}
	} else if (args_has(args, 'p')) {
		percentage = args_strtonum(args, 'p', 0, 100, &cause);


Sync OpenBSD patchset 1069:

<|original_code|>
		return (-1);

	if (ctx->cmdclient == NULL && ctx->curclient == NULL)
		return (0);

	if (ctx->cmdclient == NULL) {
		if (args_has(self->args, 'd')) {
			/*
			 * Can't use server_write_session in case attaching to
			 * the same session as currently attached to.
			 */
			for (i = 0; i < ARRAY_LENGTH(&clients); i++) {
				c = ARRAY_ITEM(&clients, i);
				if (c == NULL || c->session != s)
					continue;
				if (c == ctx->curclient)
					continue;
				server_write_client(c, MSG_DETACH, NULL, 0);
			}
		}

		ctx->curclient->session = s;
		session_update_activity(s);
		server_redraw_client(ctx->curclient);
		s->curw->flags &= ~WINLINK_ALERTFLAGS;
	} else {
		if (!(ctx->cmdclient->flags & CLIENT_TERMINAL)) {
			ctx->error(ctx, "not a terminal");
			return (-1);
		}

		overrides =
		    options_get_string(&s->options, "terminal-overrides");
		if (tty_open(&ctx->cmdclient->tty, overrides, &cause) != 0) {
			ctx->error(ctx, "terminal open failed: %s", cause);
			xfree(cause);
			return (-1);
		}

		if (args_has(self->args, 'r'))
			ctx->cmdclient->flags |= CLIENT_READONLY;

		if (args_has(self->args, 'd'))
			server_write_session(s, MSG_DETACH, NULL, 0);

		ctx->cmdclient->session = s;
		session_update_activity(s);
		server_write_client(ctx->cmdclient, MSG_READY, NULL, 0);

		update = options_get_string(&s->options, "update-environment");
		environ_update(update, &ctx->cmdclient->environ, &s->environ);

		server_redraw_client(ctx->cmdclient);
		s->curw->flags &= ~WINLINK_ALERTFLAGS;
	}
	recalculate_sizes();
	server_update_socket();

	return (1);	/* 1 means don't tell command client to exit */
}

<|edits_diff|>
--- cmd-attach-session.c
+++ cmd-attach-session.c
@@ -20,6 +20,7 @@
 		}

 		ctx->curclient->session = s;
+		notify_attached_session_changed(ctx->curclient);
 		session_update_activity(s);
 		server_redraw_client(ctx->curclient);
 		s->curw->flags &= ~WINLINK_ALERTFLAGS;
<|current_version|>
		return (-1);

	if (ctx->cmdclient == NULL && ctx->curclient == NULL)
		return (0);

	if (ctx->cmdclient == NULL) {
		if (args_has(self->args, 'd')) {
			/*
			 * Can't use server_write_session in case attaching to
			 * the same session as currently attached to.
			 */
			for (i = 0; i < ARRAY_LENGTH(&clients); i++) {
				c = ARRAY_ITEM(&clients, i);
				if (c == NULL || c->session != s)
					continue;
				if (c == ctx->curclient)
					continue;
				server_write_client(c, MSG_DETACH, NULL, 0);
			}
		}

		ctx->curclient->session = s;
		notify_attached_session_changed(ctx->curclient);
		session_update_activity(s);
		server_redraw_client(ctx->curclient);
		s->curw->flags &= ~WINLINK_ALERTFLAGS;
	} else {
		if (!(ctx->cmdclient->flags & CLIENT_TERMINAL)) {
			ctx->error(ctx, "not a terminal");
			return (-1);
		}

		overrides =
		    options_get_string(&s->options, "terminal-overrides");
		if (tty_open(&ctx->cmdclient->tty, overrides, &cause) != 0) {
			ctx->error(ctx, "terminal open failed: %s", cause);
			xfree(cause);
			return (-1);
		}

		if (args_has(self->args, 'r'))
			ctx->cmdclient->flags |= CLIENT_READONLY;

		if (args_has(self->args, 'd'))
			server_write_session(s, MSG_DETACH, NULL, 0);

		ctx->cmdclient->session = s;
		session_update_activity(s);
		server_write_client(ctx->cmdclient, MSG_READY, NULL, 0);

		update = options_get_string(&s->options, "update-environment");
		environ_update(update, &ctx->cmdclient->environ, &s->environ);

		server_redraw_client(ctx->cmdclient);
		s->curw->flags &= ~WINLINK_ALERTFLAGS;
	}
	recalculate_sizes();
	server_update_socket();

	return (1);	/* 1 means don't tell command client to exit */
}

<|next_version|>
		return (-1);

	if (ctx->cmdclient == NULL && ctx->curclient == NULL)
		return (0);

	if (ctx->cmdclient == NULL) {
		if (args_has(self->args, 'd')) {
			/*
			 * Can't use server_write_session in case attaching to
			 * the same session as currently attached to.
			 */
			for (i = 0; i < ARRAY_LENGTH(&clients); i++) {
				c = ARRAY_ITEM(&clients, i);
				if (c == NULL || c->session != s)
					continue;
				if (c == ctx->curclient)
					continue;
				server_write_client(c, MSG_DETACH, NULL, 0);
			}
		}

		ctx->curclient->session = s;
		notify_attached_session_changed(ctx->curclient);
		session_update_activity(s);
		server_redraw_client(ctx->curclient);
		s->curw->flags &= ~WINLINK_ALERTFLAGS;
	} else {
		if (!(ctx->cmdclient->flags & CLIENT_TERMINAL)) {
			ctx->error(ctx, "not a terminal");
			return (-1);
		}

		overrides =
		    options_get_string(&s->options, "terminal-overrides");
		if (tty_open(&ctx->cmdclient->tty, overrides, &cause) != 0) {
			ctx->error(ctx, "terminal open failed: %s", cause);
			xfree(cause);
			return (-1);
		}

		if (args_has(self->args, 'r'))
			ctx->cmdclient->flags |= CLIENT_READONLY;

		if (args_has(self->args, 'd'))
			server_write_session(s, MSG_DETACH, NULL, 0);

		ctx->cmdclient->session = s;
		notify_attached_session_changed(ctx->cmdclient);
		session_update_activity(s);
		server_write_client(ctx->cmdclient, MSG_READY, NULL, 0);

		update = options_get_string(&s->options, "update-environment");
		environ_update(update, &ctx->cmdclient->environ, &s->environ);

		server_redraw_client(ctx->cmdclient);
		s->curw->flags &= ~WINLINK_ALERTFLAGS;
	}
	recalculate_sizes();
	server_update_socket();

	return (1);	/* 1 means don't tell command client to exit */
}


Need to call recalculate_sizes() when changing window with the mouse, from marcel partap.

<|original_code|>
			status_update_jobs(c);
			c->flags |= CLIENT_STATUS;
		}
	}
}

/* Check for mouse keys. */
void
server_client_check_mouse(
    struct client *c, struct window_pane *wp, struct mouse_event *mouse)
{
	struct session	*s = c->session;
	struct options	*oo = &s->options;
	int		 statusat;

	statusat = status_at_line(c);

	/* Is this a window selection click on the status line? */
	if (statusat != -1 && mouse->y == (u_int)statusat &&
	    options_get_number(oo, "mouse-select-window")) {
		if (mouse->b == MOUSE_UP && c->last_mouse.b != MOUSE_UP) {
			status_set_window_at(c, mouse->x);
			return;
		}
		if (mouse->b & MOUSE_45) {
			if ((mouse->b & MOUSE_BUTTON) == MOUSE_1) {
				session_previous(c->session, 0);
				server_redraw_session(s);
			}
			if ((mouse->b & MOUSE_BUTTON) == MOUSE_2) {
				session_next(c->session, 0);
				server_redraw_session(s);
			}
			return;
		}
		memcpy(&c->last_mouse, mouse, sizeof c->last_mouse);
		return;
	}

	/*
	 * Not on status line - adjust mouse position if status line is at the
	 * top and limit if at the bottom. From here on a struct mouse
	 * represents the offset onto the window itself.
	 */
	if (statusat == 0 &&mouse->y > 0)
		mouse->y--;
	else if (statusat > 0 && mouse->y >= (u_int)statusat)
		mouse->y = statusat - 1;

	/* Is this a pane selection? Allow down only in copy mode. */
	if (options_get_number(oo, "mouse-select-pane") &&
	    ((!(mouse->b & MOUSE_DRAG) && mouse->b != MOUSE_UP) ||
	    wp->mode != &window_copy_mode)) {
		window_set_active_at(wp->window, mouse->x, mouse->y);
		server_redraw_window_borders(wp->window);
		wp = wp->window->active; /* may have changed */
<|edits_diff|>
--- server-client.c
+++ server-client.c
@@ -20,12 +20,14 @@
 	    options_get_number(oo, "mouse-select-window")) {
 		if (mouse->b == MOUSE_UP && c->last_mouse.b != MOUSE_UP) {
 			status_set_window_at(c, mouse->x);
+			recalculate_sizes();
 			return;
 		}
 		if (mouse->b & MOUSE_45) {
 			if ((mouse->b & MOUSE_BUTTON) == MOUSE_1) {
 				session_previous(c->session, 0);
 				server_redraw_session(s);
+				recalculate_sizes();
 			}
 			if ((mouse->b & MOUSE_BUTTON) == MOUSE_2) {
 				session_next(c->session, 0);
<|current_version|>
			status_update_jobs(c);
			c->flags |= CLIENT_STATUS;
		}
	}
}

/* Check for mouse keys. */
void
server_client_check_mouse(
    struct client *c, struct window_pane *wp, struct mouse_event *mouse)
{
	struct session	*s = c->session;
	struct options	*oo = &s->options;
	int		 statusat;

	statusat = status_at_line(c);

	/* Is this a window selection click on the status line? */
	if (statusat != -1 && mouse->y == (u_int)statusat &&
	    options_get_number(oo, "mouse-select-window")) {
		if (mouse->b == MOUSE_UP && c->last_mouse.b != MOUSE_UP) {
			status_set_window_at(c, mouse->x);
			recalculate_sizes();
			return;
		}
		if (mouse->b & MOUSE_45) {
			if ((mouse->b & MOUSE_BUTTON) == MOUSE_1) {
				session_previous(c->session, 0);
				server_redraw_session(s);
				recalculate_sizes();
			}
			if ((mouse->b & MOUSE_BUTTON) == MOUSE_2) {
				session_next(c->session, 0);
				server_redraw_session(s);
			}
			return;
		}
		memcpy(&c->last_mouse, mouse, sizeof c->last_mouse);
		return;
	}

	/*
	 * Not on status line - adjust mouse position if status line is at the
	 * top and limit if at the bottom. From here on a struct mouse
	 * represents the offset onto the window itself.
	 */
	if (statusat == 0 &&mouse->y > 0)
		mouse->y--;
	else if (statusat > 0 && mouse->y >= (u_int)statusat)
		mouse->y = statusat - 1;

	/* Is this a pane selection? Allow down only in copy mode. */
	if (options_get_number(oo, "mouse-select-pane") &&
	    ((!(mouse->b & MOUSE_DRAG) && mouse->b != MOUSE_UP) ||
	    wp->mode != &window_copy_mode)) {
		window_set_active_at(wp->window, mouse->x, mouse->y);
		server_redraw_window_borders(wp->window);
		wp = wp->window->active; /* may have changed */
<|next_version|>
			status_update_jobs(c);
			c->flags |= CLIENT_STATUS;
		}
	}
}

/* Check for mouse keys. */
void
server_client_check_mouse(
    struct client *c, struct window_pane *wp, struct mouse_event *mouse)
{
	struct session	*s = c->session;
	struct options	*oo = &s->options;
	int		 statusat;

	statusat = status_at_line(c);

	/* Is this a window selection click on the status line? */
	if (statusat != -1 && mouse->y == (u_int)statusat &&
	    options_get_number(oo, "mouse-select-window")) {
		if (mouse->b == MOUSE_UP && c->last_mouse.b != MOUSE_UP) {
			status_set_window_at(c, mouse->x);
			recalculate_sizes();
			return;
		}
		if (mouse->b & MOUSE_45) {
			if ((mouse->b & MOUSE_BUTTON) == MOUSE_1) {
				session_previous(c->session, 0);
				server_redraw_session(s);
				recalculate_sizes();
			}
			if ((mouse->b & MOUSE_BUTTON) == MOUSE_2) {
				session_next(c->session, 0);
				server_redraw_session(s);
				recalculate_sizes();
			}
			return;
		}
		memcpy(&c->last_mouse, mouse, sizeof c->last_mouse);
		return;
	}

	/*
	 * Not on status line - adjust mouse position if status line is at the
	 * top and limit if at the bottom. From here on a struct mouse
	 * represents the offset onto the window itself.
	 */
	if (statusat == 0 &&mouse->y > 0)
		mouse->y--;
	else if (statusat > 0 && mouse->y >= (u_int)statusat)
		mouse->y = statusat - 1;

	/* Is this a pane selection? Allow down only in copy mode. */
	if (options_get_number(oo, "mouse-select-pane") &&
	    ((!(mouse->b & MOUSE_DRAG) && mouse->b != MOUSE_UP) ||
	    wp->mode != &window_copy_mode)) {
		window_set_active_at(wp->window, mouse->x, mouse->y);
		server_redraw_window_borders(wp->window);
		wp = wp->window->active; /* may have changed */


Track the last session for a client and add a flag to switch-client and a key binding (L) to move a client back to its last session.

<|original_code|>
		w->name = xstrdup(data->winname);

		options_set_number(&w->options, "automatic-rename", 0);
	}

	/*
	 * If a target session is given, this is to be part of a session group,
	 * so add it to the group and synchronize.
	 */
	if (groupwith != NULL) {
		session_group_add(groupwith, s);
		session_group_synchronize_to(s);
		session_select(s, RB_ROOT(&s->windows)->idx);
	}

	/*
	 * Set the client to the new session. If a command client exists, it is
	 * taking this session and needs to get MSG_READY and stay around.
	 */
	if (!detached) {
		if (ctx->cmdclient != NULL) {
			server_write_client(ctx->cmdclient, MSG_READY, NULL, 0);
			ctx->cmdclient->session = s;
			server_redraw_client(ctx->cmdclient);
		} else {
			ctx->curclient->session = s;
			server_redraw_client(ctx->curclient);
		}
	}
	recalculate_sizes();
	server_update_socket();

	/*
	 * If there are still configuration file errors to display, put the new
	 * session's current window into more mode and display them now.
	 */
	if (cfg_finished && !ARRAY_EMPTY(&cfg_causes)) {
		wp = s->curw->window->active;
		window_pane_set_mode(wp, &window_copy_mode);
		window_copy_init_for_output(wp);
		for (i = 0; i < ARRAY_LENGTH(&cfg_causes); i++) {
			cause = ARRAY_ITEM(&cfg_causes, i);
			window_copy_add(wp, "%s", cause);
			xfree(cause);
		}
		ARRAY_FREE(&cfg_causes);
	}

	return (!detached);	/* 1 means don't tell command client to exit */
<|edits_diff|>
--- cmd-new-session.c
+++ cmd-new-session.c
@@ -20,6 +20,10 @@
 	if (!detached) {
 		if (ctx->cmdclient != NULL) {
 			server_write_client(ctx->cmdclient, MSG_READY, NULL, 0);
+			if (ctx->cmdclient->session != NULL) {
+				session_index(ctx->cmdclient->session,
+				    &ctx->cmdclient->last_session);
+			}
 			ctx->cmdclient->session = s;
 			server_redraw_client(ctx->cmdclient);
 		} else {
<|current_version|>
		w->name = xstrdup(data->winname);

		options_set_number(&w->options, "automatic-rename", 0);
	}

	/*
	 * If a target session is given, this is to be part of a session group,
	 * so add it to the group and synchronize.
	 */
	if (groupwith != NULL) {
		session_group_add(groupwith, s);
		session_group_synchronize_to(s);
		session_select(s, RB_ROOT(&s->windows)->idx);
	}

	/*
	 * Set the client to the new session. If a command client exists, it is
	 * taking this session and needs to get MSG_READY and stay around.
	 */
	if (!detached) {
		if (ctx->cmdclient != NULL) {
			server_write_client(ctx->cmdclient, MSG_READY, NULL, 0);
			if (ctx->cmdclient->session != NULL) {
				session_index(ctx->cmdclient->session,
				    &ctx->cmdclient->last_session);
			}
			ctx->cmdclient->session = s;
			server_redraw_client(ctx->cmdclient);
		} else {
			ctx->curclient->session = s;
			server_redraw_client(ctx->curclient);
		}
	}
	recalculate_sizes();
	server_update_socket();

	/*
	 * If there are still configuration file errors to display, put the new
	 * session's current window into more mode and display them now.
	 */
	if (cfg_finished && !ARRAY_EMPTY(&cfg_causes)) {
		wp = s->curw->window->active;
		window_pane_set_mode(wp, &window_copy_mode);
		window_copy_init_for_output(wp);
		for (i = 0; i < ARRAY_LENGTH(&cfg_causes); i++) {
			cause = ARRAY_ITEM(&cfg_causes, i);
			window_copy_add(wp, "%s", cause);
			xfree(cause);
		}
		ARRAY_FREE(&cfg_causes);
	}

	return (!detached);	/* 1 means don't tell command client to exit */
<|next_version|>
		w->name = xstrdup(data->winname);

		options_set_number(&w->options, "automatic-rename", 0);
	}

	/*
	 * If a target session is given, this is to be part of a session group,
	 * so add it to the group and synchronize.
	 */
	if (groupwith != NULL) {
		session_group_add(groupwith, s);
		session_group_synchronize_to(s);
		session_select(s, RB_ROOT(&s->windows)->idx);
	}

	/*
	 * Set the client to the new session. If a command client exists, it is
	 * taking this session and needs to get MSG_READY and stay around.
	 */
	if (!detached) {
		if (ctx->cmdclient != NULL) {
			server_write_client(ctx->cmdclient, MSG_READY, NULL, 0);
			if (ctx->cmdclient->session != NULL) {
				session_index(ctx->cmdclient->session,
				    &ctx->cmdclient->last_session);
			}
			ctx->cmdclient->session = s;
			server_redraw_client(ctx->cmdclient);
		} else {
			if (ctx->curclient->session != NULL) {
				session_index(ctx->curclient->session,
				    &ctx->curclient->last_session);
			}
			ctx->curclient->session = s;
			server_redraw_client(ctx->curclient);
		}
	}
	recalculate_sizes();
	server_update_socket();

	/*
	 * If there are still configuration file errors to display, put the new
	 * session's current window into more mode and display them now.
	 */
	if (cfg_finished && !ARRAY_EMPTY(&cfg_causes)) {
		wp = s->curw->window->active;
		window_pane_set_mode(wp, &window_copy_mode);
		window_copy_init_for_output(wp);
		for (i = 0; i < ARRAY_LENGTH(&cfg_causes); i++) {
			cause = ARRAY_ITEM(&cfg_causes, i);
			window_copy_add(wp, "%s", cause);
			xfree(cause);
		}
		ARRAY_FREE(&cfg_causes);
	}

	return (!detached);	/* 1 means don't tell command client to exit */


copy mode uses the real screen as backing and if it is updated while copying, strange things can happen. So, freeze reading from the pty while in copy mode.

<|original_code|>
	struct screen			*s;
	struct screen_write_ctx	 	 ctx;
	u_int				 i;
	int				 keys;

	wp->modedata = data = xmalloc(sizeof *data);
	data->oy = 0;
	data->cx = wp->base.cx;
	data->cy = wp->base.cy;

	data->lastcx = 0;
	data->lastsx = 0;

	data->rectflag = 0;

	data->inputtype = WINDOW_COPY_OFF;
	data->inputprompt = NULL;
	data->inputstr = xstrdup("");

	data->searchtype = WINDOW_COPY_OFF;
	data->searchstr = NULL;

	s = &data->screen;
	screen_init(s, screen_size_x(&wp->base), screen_size_y(&wp->base), 0);
	if (options_get_number(&wp->window->options, "mode-mouse"))
		s->mode |= MODE_MOUSE;

	keys = options_get_number(&wp->window->options, "mode-keys");
	if (keys == MODEKEY_EMACS)
		mode_key_init(&data->mdata, &mode_key_tree_emacs_copy);
	else
		mode_key_init(&data->mdata, &mode_key_tree_vi_copy);

	s->cx = data->cx;
	s->cy = data->cy;

	screen_write_start(&ctx, NULL, s);
	for (i = 0; i < screen_size_y(s); i++)
		window_copy_write_line(wp, &ctx, i);
	screen_write_cursormove(&ctx, data->cx, data->cy);
	screen_write_stop(&ctx);

	return (s);
}

void
window_copy_free(struct window_pane *wp)
{
	struct window_copy_mode_data	*data = wp->modedata;

	if (data->searchstr != NULL)
		xfree(data->searchstr);
	xfree(data->inputstr);

	screen_free(&data->screen);

	xfree(data);
}

void
window_copy_pageup(struct window_pane *wp)
{
	struct window_copy_mode_data	*data = wp->modedata;
	struct screen			*s = &data->screen;
	u_int				 n;

	n = 1;
	if (screen_size_y(s) > 2)
		n = screen_size_y(s) - 2;
	if (data->oy + n > screen_hsize(&wp->base))
		data->oy = screen_hsize(&wp->base);
	else
		data->oy += n;
<|edits_diff|>
--- window-copy.c
+++ window-copy.c
@@ -19,6 +19,9 @@

 	data->searchtype = WINDOW_COPY_OFF;
 	data->searchstr = NULL;
+
+	wp->flags |= PANE_FREEZE;
+	bufferevent_disable(wp->event, EV_READ|EV_WRITE);

 	s = &data->screen;
 	screen_init(s, screen_size_x(&wp->base), screen_size_y(&wp->base), 0);
<|current_version|>
	struct screen			*s;
	struct screen_write_ctx	 	 ctx;
	u_int				 i;
	int				 keys;

	wp->modedata = data = xmalloc(sizeof *data);
	data->oy = 0;
	data->cx = wp->base.cx;
	data->cy = wp->base.cy;

	data->lastcx = 0;
	data->lastsx = 0;

	data->rectflag = 0;

	data->inputtype = WINDOW_COPY_OFF;
	data->inputprompt = NULL;
	data->inputstr = xstrdup("");

	data->searchtype = WINDOW_COPY_OFF;
	data->searchstr = NULL;

	wp->flags |= PANE_FREEZE;
	bufferevent_disable(wp->event, EV_READ|EV_WRITE);

	s = &data->screen;
	screen_init(s, screen_size_x(&wp->base), screen_size_y(&wp->base), 0);
	if (options_get_number(&wp->window->options, "mode-mouse"))
		s->mode |= MODE_MOUSE;

	keys = options_get_number(&wp->window->options, "mode-keys");
	if (keys == MODEKEY_EMACS)
		mode_key_init(&data->mdata, &mode_key_tree_emacs_copy);
	else
		mode_key_init(&data->mdata, &mode_key_tree_vi_copy);

	s->cx = data->cx;
	s->cy = data->cy;

	screen_write_start(&ctx, NULL, s);
	for (i = 0; i < screen_size_y(s); i++)
		window_copy_write_line(wp, &ctx, i);
	screen_write_cursormove(&ctx, data->cx, data->cy);
	screen_write_stop(&ctx);

	return (s);
}

void
window_copy_free(struct window_pane *wp)
{
	struct window_copy_mode_data	*data = wp->modedata;

	if (data->searchstr != NULL)
		xfree(data->searchstr);
	xfree(data->inputstr);

	screen_free(&data->screen);

	xfree(data);
}

void
window_copy_pageup(struct window_pane *wp)
{
	struct window_copy_mode_data	*data = wp->modedata;
	struct screen			*s = &data->screen;
	u_int				 n;

	n = 1;
	if (screen_size_y(s) > 2)
		n = screen_size_y(s) - 2;
	if (data->oy + n > screen_hsize(&wp->base))
		data->oy = screen_hsize(&wp->base);
	else
		data->oy += n;
<|next_version|>
	struct screen			*s;
	struct screen_write_ctx	 	 ctx;
	u_int				 i;
	int				 keys;

	wp->modedata = data = xmalloc(sizeof *data);
	data->oy = 0;
	data->cx = wp->base.cx;
	data->cy = wp->base.cy;

	data->lastcx = 0;
	data->lastsx = 0;

	data->rectflag = 0;

	data->inputtype = WINDOW_COPY_OFF;
	data->inputprompt = NULL;
	data->inputstr = xstrdup("");

	data->searchtype = WINDOW_COPY_OFF;
	data->searchstr = NULL;

	wp->flags |= PANE_FREEZE;
	bufferevent_disable(wp->event, EV_READ|EV_WRITE);

	s = &data->screen;
	screen_init(s, screen_size_x(&wp->base), screen_size_y(&wp->base), 0);
	if (options_get_number(&wp->window->options, "mode-mouse"))
		s->mode |= MODE_MOUSE;

	keys = options_get_number(&wp->window->options, "mode-keys");
	if (keys == MODEKEY_EMACS)
		mode_key_init(&data->mdata, &mode_key_tree_emacs_copy);
	else
		mode_key_init(&data->mdata, &mode_key_tree_vi_copy);

	s->cx = data->cx;
	s->cy = data->cy;

	screen_write_start(&ctx, NULL, s);
	for (i = 0; i < screen_size_y(s); i++)
		window_copy_write_line(wp, &ctx, i);
	screen_write_cursormove(&ctx, data->cx, data->cy);
	screen_write_stop(&ctx);

	return (s);
}

void
window_copy_free(struct window_pane *wp)
{
	struct window_copy_mode_data	*data = wp->modedata;

	wp->flags &= ~PANE_FREEZE;
	bufferevent_enable(wp->event, EV_READ|EV_WRITE);

	if (data->searchstr != NULL)
		xfree(data->searchstr);
	xfree(data->inputstr);

	screen_free(&data->screen);

	xfree(data);
}

void
window_copy_pageup(struct window_pane *wp)
{
	struct window_copy_mode_data	*data = wp->modedata;
	struct screen			*s = &data->screen;
	u_int				 n;

	n = 1;
	if (screen_size_y(s) > 2)
		n = screen_size_y(s) - 2;
	if (data->oy + n > screen_hsize(&wp->base))
		data->oy = screen_hsize(&wp->base);
	else
		data->oy += n;


Add icon override setting for newTabMenu entries (#18116)

<|original_code|>
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.

#include "pch.h"
#include "ActionEntry.h"
#include "JsonUtils.h"

#include "ActionEntry.g.cpp"

using namespace Microsoft::Terminal::Settings::Model;
using namespace winrt::Microsoft::Terminal::Settings::Model::implementation;

static constexpr std::string_view ActionIdKey{ "id" };

ActionEntry::ActionEntry() noexcept :
    ActionEntryT<ActionEntry, NewTabMenuEntry>(NewTabMenuEntryType::Action)
{
}

Json::Value ActionEntry::ToJson() const
{
    auto json = NewTabMenuEntry::ToJson();

    JsonUtils::SetValueForKey(json, ActionIdKey, _ActionId);

    return json;
}

winrt::com_ptr<NewTabMenuEntry> ActionEntry::FromJson(const Json::Value& json)
{
    auto entry = winrt::make_self<ActionEntry>();

    JsonUtils::GetValueForKey(json, ActionIdKey, entry->_ActionId);

    return entry;
}

<|edits_diff|>
--- src/cascadia/TerminalSettingsModel/ActionEntry.cpp
+++ src/cascadia/TerminalSettingsModel/ActionEntry.cpp
@@ -11,6 +11,7 @@
 using namespace winrt::Microsoft::Terminal::Settings::Model::implementation;

 static constexpr std::string_view ActionIdKey{ "id" };
+static constexpr std::string_view IconKey{ "icon" };

 ActionEntry::ActionEntry() noexcept :
     ActionEntryT<ActionEntry, NewTabMenuEntry>(NewTabMenuEntryType::Action)
@@ -22,6 +23,7 @@
     auto json = NewTabMenuEntry::ToJson();

     JsonUtils::SetValueForKey(json, ActionIdKey, _ActionId);
+    JsonUtils::SetValueForKey(json, IconKey, _Icon);

     return json;
 }
<|current_version|>
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.

#include "pch.h"
#include "ActionEntry.h"
#include "JsonUtils.h"

#include "ActionEntry.g.cpp"

using namespace Microsoft::Terminal::Settings::Model;
using namespace winrt::Microsoft::Terminal::Settings::Model::implementation;

static constexpr std::string_view ActionIdKey{ "id" };
static constexpr std::string_view IconKey{ "icon" };

ActionEntry::ActionEntry() noexcept :
    ActionEntryT<ActionEntry, NewTabMenuEntry>(NewTabMenuEntryType::Action)
{
}

Json::Value ActionEntry::ToJson() const
{
    auto json = NewTabMenuEntry::ToJson();

    JsonUtils::SetValueForKey(json, ActionIdKey, _ActionId);
    JsonUtils::SetValueForKey(json, IconKey, _Icon);

    return json;
}

winrt::com_ptr<NewTabMenuEntry> ActionEntry::FromJson(const Json::Value& json)
{
    auto entry = winrt::make_self<ActionEntry>();

    JsonUtils::GetValueForKey(json, ActionIdKey, entry->_ActionId);

    return entry;
}

<|next_version|>
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.

#include "pch.h"
#include "ActionEntry.h"
#include "JsonUtils.h"

#include "ActionEntry.g.cpp"

using namespace Microsoft::Terminal::Settings::Model;
using namespace winrt::Microsoft::Terminal::Settings::Model::implementation;

static constexpr std::string_view ActionIdKey{ "id" };
static constexpr std::string_view IconKey{ "icon" };

ActionEntry::ActionEntry() noexcept :
    ActionEntryT<ActionEntry, NewTabMenuEntry>(NewTabMenuEntryType::Action)
{
}

Json::Value ActionEntry::ToJson() const
{
    auto json = NewTabMenuEntry::ToJson();

    JsonUtils::SetValueForKey(json, ActionIdKey, _ActionId);
    JsonUtils::SetValueForKey(json, IconKey, _Icon);

    return json;
}

winrt::com_ptr<NewTabMenuEntry> ActionEntry::FromJson(const Json::Value& json)
{
    auto entry = winrt::make_self<ActionEntry>();

    JsonUtils::GetValueForKey(json, ActionIdKey, entry->_ActionId);
    JsonUtils::GetValueForKey(json, IconKey, entry->_Icon);

    return entry;
}


Add a Compatibility and Terminal page to the Settings UI (#17895)

<|original_code|>
#include "Profiles_Base.h"
#include "Profiles_Base.g.cpp"
#include "ProfileViewModel.h"

#include <LibraryResources.h>
#include "..\WinRTUtils\inc\Utils.h"

using namespace winrt::Windows::UI::Xaml;
using namespace winrt::Windows::UI::Xaml::Controls;
using namespace winrt::Windows::UI::Xaml::Navigation;

namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
{
    Profiles_Base::Profiles_Base()
    {
        InitializeComponent();

        const auto startingDirCheckboxTooltip{ ToolTipService::GetToolTip(StartingDirectoryUseParentCheckbox()) };
        Automation::AutomationProperties::SetFullDescription(StartingDirectoryUseParentCheckbox(), unbox_value<hstring>(startingDirCheckboxTooltip));

        Automation::AutomationProperties::SetName(DeleteButton(), RS_(L"Profile_DeleteButton/Text"));
        AppearanceNavigator().Content(box_value(RS_(L"Profile_Appearance/Header")));
        AdvancedNavigator().Content(box_value(RS_(L"Profile_Advanced/Header")));
    }

    void Profiles_Base::OnNavigatedTo(const NavigationEventArgs& e)
    {
        const auto args = e.Parameter().as<Editor::NavigateToProfileArgs>();
        _Profile = args.Profile();
        _windowRoot = args.WindowRoot();

        // Check the use parent directory box if the starting directory is empty
        if (_Profile.StartingDirectory().empty())
        {
            StartingDirectoryUseParentCheckbox().IsChecked(true);
        }

        _layoutUpdatedRevoker = LayoutUpdated(winrt::auto_revoke, [this](auto /*s*/, auto /*e*/) {
            // This event fires every time the layout changes, but it is always the last one to fire
            // in any layout change chain. That gives us great flexibility in finding the right point
            // at which to initialize our renderer (and our terminal).
            // Any earlier than the last layout update and we may not know the terminal's starting size.

            // Only let this succeed once.
            _layoutUpdatedRevoker.revoke();

            if (_Profile.FocusDeleteButton())
            {
                DeleteButton().Focus(FocusState::Programmatic);
                _Profile.FocusDeleteButton(false);
            }
        });
    }

    void Profiles_Base::OnNavigatedFrom(const NavigationEventArgs& /*e*/)
    {
        _ViewModelChangedRevoker.revoke();
    }

    void Profiles_Base::Appearance_Click(const IInspectable& /*sender*/, const RoutedEventArgs& /*args*/)
    {
        _Profile.CurrentPage(ProfileSubPage::Appearance);
    }

    void Profiles_Base::Advanced_Click(const IInspectable& /*sender*/, const RoutedEventArgs& /*args*/)
    {
        _Profile.CurrentPage(ProfileSubPage::Advanced);
    }

    void Profiles_Base::DeleteConfirmation_Click(const IInspectable& /*sender*/, const RoutedEventArgs& /*e*/)
    {
        winrt::get_self<ProfileViewModel>(_Profile)->DeleteProfile();
    }

    safe_void_coroutine Profiles_Base::Commandline_Click(const IInspectable&, const RoutedEventArgs&)
    {
        auto lifetime = get_strong();

        static constexpr COMDLG_FILTERSPEC supportedFileTypes[] = {
            { L"Executable Files (*.exe, *.cmd, *.bat)", L"*.exe;*.cmd;*.bat" },
            { L"All Files (*.*)", L"*.*" }
        };

        static constexpr winrt::guid clientGuidExecutables{ 0x2E7E4331, 0x0800, 0x48E6, { 0xB0, 0x17, 0xA1, 0x4C, 0xD8, 0x73, 0xDD, 0x58 } };
        const auto parentHwnd{ reinterpret_cast<HWND>(_windowRoot.GetHostingWindow()) };
        auto path = co_await OpenFilePicker(parentHwnd, [](auto&& dialog) {
<|edits_diff|>
--- src/cascadia/TerminalSettingsEditor/Profiles_Base.cpp
+++ src/cascadia/TerminalSettingsEditor/Profiles_Base.cpp
@@ -20,6 +20,7 @@

         Automation::AutomationProperties::SetName(DeleteButton(), RS_(L"Profile_DeleteButton/Text"));
         AppearanceNavigator().Content(box_value(RS_(L"Profile_Appearance/Header")));
+        TerminalNavigator().Content(box_value(RS_(L"Profile_Terminal/Header")));
         AdvancedNavigator().Content(box_value(RS_(L"Profile_Advanced/Header")));
     }

<|current_version|>
#include "Profiles_Base.h"
#include "Profiles_Base.g.cpp"
#include "ProfileViewModel.h"

#include <LibraryResources.h>
#include "..\WinRTUtils\inc\Utils.h"

using namespace winrt::Windows::UI::Xaml;
using namespace winrt::Windows::UI::Xaml::Controls;
using namespace winrt::Windows::UI::Xaml::Navigation;

namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
{
    Profiles_Base::Profiles_Base()
    {
        InitializeComponent();

        const auto startingDirCheckboxTooltip{ ToolTipService::GetToolTip(StartingDirectoryUseParentCheckbox()) };
        Automation::AutomationProperties::SetFullDescription(StartingDirectoryUseParentCheckbox(), unbox_value<hstring>(startingDirCheckboxTooltip));

        Automation::AutomationProperties::SetName(DeleteButton(), RS_(L"Profile_DeleteButton/Text"));
        AppearanceNavigator().Content(box_value(RS_(L"Profile_Appearance/Header")));
        TerminalNavigator().Content(box_value(RS_(L"Profile_Terminal/Header")));
        AdvancedNavigator().Content(box_value(RS_(L"Profile_Advanced/Header")));
    }

    void Profiles_Base::OnNavigatedTo(const NavigationEventArgs& e)
    {
        const auto args = e.Parameter().as<Editor::NavigateToProfileArgs>();
        _Profile = args.Profile();
        _windowRoot = args.WindowRoot();

        // Check the use parent directory box if the starting directory is empty
        if (_Profile.StartingDirectory().empty())
        {
            StartingDirectoryUseParentCheckbox().IsChecked(true);
        }

        _layoutUpdatedRevoker = LayoutUpdated(winrt::auto_revoke, [this](auto /*s*/, auto /*e*/) {
            // This event fires every time the layout changes, but it is always the last one to fire
            // in any layout change chain. That gives us great flexibility in finding the right point
            // at which to initialize our renderer (and our terminal).
            // Any earlier than the last layout update and we may not know the terminal's starting size.

            // Only let this succeed once.
            _layoutUpdatedRevoker.revoke();

            if (_Profile.FocusDeleteButton())
            {
                DeleteButton().Focus(FocusState::Programmatic);
                _Profile.FocusDeleteButton(false);
            }
        });
    }

    void Profiles_Base::OnNavigatedFrom(const NavigationEventArgs& /*e*/)
    {
        _ViewModelChangedRevoker.revoke();
    }

    void Profiles_Base::Appearance_Click(const IInspectable& /*sender*/, const RoutedEventArgs& /*args*/)
    {
        _Profile.CurrentPage(ProfileSubPage::Appearance);
    }

    void Profiles_Base::Advanced_Click(const IInspectable& /*sender*/, const RoutedEventArgs& /*args*/)
    {
        _Profile.CurrentPage(ProfileSubPage::Advanced);
    }

    void Profiles_Base::DeleteConfirmation_Click(const IInspectable& /*sender*/, const RoutedEventArgs& /*e*/)
    {
        winrt::get_self<ProfileViewModel>(_Profile)->DeleteProfile();
    }

    safe_void_coroutine Profiles_Base::Commandline_Click(const IInspectable&, const RoutedEventArgs&)
    {
        auto lifetime = get_strong();

        static constexpr COMDLG_FILTERSPEC supportedFileTypes[] = {
            { L"Executable Files (*.exe, *.cmd, *.bat)", L"*.exe;*.cmd;*.bat" },
            { L"All Files (*.*)", L"*.*" }
        };

        static constexpr winrt::guid clientGuidExecutables{ 0x2E7E4331, 0x0800, 0x48E6, { 0xB0, 0x17, 0xA1, 0x4C, 0xD8, 0x73, 0xDD, 0x58 } };
        const auto parentHwnd{ reinterpret_cast<HWND>(_windowRoot.GetHostingWindow()) };
        auto path = co_await OpenFilePicker(parentHwnd, [](auto&& dialog) {
<|next_version|>
#include "Profiles_Base.h"
#include "Profiles_Base.g.cpp"
#include "ProfileViewModel.h"

#include <LibraryResources.h>
#include "..\WinRTUtils\inc\Utils.h"

using namespace winrt::Windows::UI::Xaml;
using namespace winrt::Windows::UI::Xaml::Controls;
using namespace winrt::Windows::UI::Xaml::Navigation;

namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
{
    Profiles_Base::Profiles_Base()
    {
        InitializeComponent();

        const auto startingDirCheckboxTooltip{ ToolTipService::GetToolTip(StartingDirectoryUseParentCheckbox()) };
        Automation::AutomationProperties::SetFullDescription(StartingDirectoryUseParentCheckbox(), unbox_value<hstring>(startingDirCheckboxTooltip));

        Automation::AutomationProperties::SetName(DeleteButton(), RS_(L"Profile_DeleteButton/Text"));
        AppearanceNavigator().Content(box_value(RS_(L"Profile_Appearance/Header")));
        TerminalNavigator().Content(box_value(RS_(L"Profile_Terminal/Header")));
        AdvancedNavigator().Content(box_value(RS_(L"Profile_Advanced/Header")));
    }

    void Profiles_Base::OnNavigatedTo(const NavigationEventArgs& e)
    {
        const auto args = e.Parameter().as<Editor::NavigateToProfileArgs>();
        _Profile = args.Profile();
        _windowRoot = args.WindowRoot();

        // Check the use parent directory box if the starting directory is empty
        if (_Profile.StartingDirectory().empty())
        {
            StartingDirectoryUseParentCheckbox().IsChecked(true);
        }

        _layoutUpdatedRevoker = LayoutUpdated(winrt::auto_revoke, [this](auto /*s*/, auto /*e*/) {
            // This event fires every time the layout changes, but it is always the last one to fire
            // in any layout change chain. That gives us great flexibility in finding the right point
            // at which to initialize our renderer (and our terminal).
            // Any earlier than the last layout update and we may not know the terminal's starting size.

            // Only let this succeed once.
            _layoutUpdatedRevoker.revoke();

            if (_Profile.FocusDeleteButton())
            {
                DeleteButton().Focus(FocusState::Programmatic);
                _Profile.FocusDeleteButton(false);
            }
        });
    }

    void Profiles_Base::OnNavigatedFrom(const NavigationEventArgs& /*e*/)
    {
        _ViewModelChangedRevoker.revoke();
    }

    void Profiles_Base::Appearance_Click(const IInspectable& /*sender*/, const RoutedEventArgs& /*args*/)
    {
        _Profile.CurrentPage(ProfileSubPage::Appearance);
    }

    void Profiles_Base::Terminal_Click(const IInspectable& /*sender*/, const RoutedEventArgs& /*args*/)
    {
        _Profile.CurrentPage(ProfileSubPage::Terminal);
    }

    void Profiles_Base::Advanced_Click(const IInspectable& /*sender*/, const RoutedEventArgs& /*args*/)
    {
        _Profile.CurrentPage(ProfileSubPage::Advanced);
    }

    void Profiles_Base::DeleteConfirmation_Click(const IInspectable& /*sender*/, const RoutedEventArgs& /*e*/)
    {
        winrt::get_self<ProfileViewModel>(_Profile)->DeleteProfile();
    }

    safe_void_coroutine Profiles_Base::Commandline_Click(const IInspectable&, const RoutedEventArgs&)
    {
        auto lifetime = get_strong();

        static constexpr COMDLG_FILTERSPEC supportedFileTypes[] = {
            { L"Executable Files (*.exe, *.cmd, *.bat)", L"*.exe;*.cmd;*.bat" },
            { L"All Files (*.*)", L"*.*" }
        };

        static constexpr winrt::guid clientGuidExecutables{ 0x2E7E4331, 0x0800, 0x48E6, { 0xB0, 0x17, 0xA1, 0x4C, 0xD8, 0x73, 0xDD, 0x58 } };
        const auto parentHwnd{ reinterpret_cast<HWND>(_windowRoot.GetHostingWindow()) };
        auto path = co_await OpenFilePicker(parentHwnd, [](auto&& dialog) {


Add tooltips to context menus (#14058)

<|original_code|>
        _AppendCloseMenuItems(contextMenuFlyout);
        TabViewItem().ContextFlyout(contextMenuFlyout);
    }

    // Method Description:
    // - Append the close menu items to the context menu flyout
    // Arguments:
    // - flyout - the menu flyout to which the close items must be appended
    // Return Value:
    // - <none>
    void TabBase::_AppendCloseMenuItems(winrt::Windows::UI::Xaml::Controls::MenuFlyout flyout)
    {
        auto weakThis{ get_weak() };

        // Close tabs after
        _closeTabsAfterMenuItem.Click([weakThis](auto&&, auto&&) {
            if (auto tab{ weakThis.get() })
            {
                tab->_CloseTabsAfter();
            }
        });
        _closeTabsAfterMenuItem.Text(RS_(L"TabCloseAfter"));

        // Close other tabs
        _closeOtherTabsMenuItem.Click([weakThis](auto&&, auto&&) {
            if (auto tab{ weakThis.get() })
            {
                tab->_CloseOtherTabs();
            }
        });
        _closeOtherTabsMenuItem.Text(RS_(L"TabCloseOther"));

        // Close
        Controls::MenuFlyoutItem closeTabMenuItem;
        Controls::FontIcon closeSymbol;
        closeSymbol.FontFamily(Media::FontFamily{ L"Segoe Fluent Icons, Segoe MDL2 Assets" });
        closeSymbol.Glyph(L"\xE711");

        closeTabMenuItem.Click([weakThis](auto&&, auto&&) {
            if (auto tab{ weakThis.get() })
            {
                tab->_CloseRequestedHandlers(nullptr, nullptr);
            }
        });
        closeTabMenuItem.Text(RS_(L"TabClose"));
        closeTabMenuItem.Icon(closeSymbol);

        // GH#8238 append the close menu items to the flyout itself until crash in XAML is fixed
        //Controls::MenuFlyoutSubItem closeSubMenu;
        //closeSubMenu.Text(RS_(L"TabCloseSubMenu"));
        //closeSubMenu.Items().Append(_closeTabsAfterMenuItem);
        //closeSubMenu.Items().Append(_closeOtherTabsMenuItem);
        //flyout.Items().Append(closeSubMenu);
        flyout.Items().Append(_closeTabsAfterMenuItem);
        flyout.Items().Append(_closeOtherTabsMenuItem);
        flyout.Items().Append(closeTabMenuItem);
    }

    // Method Description:
    // - Enable the Close menu items based on tab index and total number of tabs
    // Arguments:
    // - <none>
    // Return Value:
    // - <none>
    void TabBase::_EnableCloseMenuItems()
    {
        // close other tabs is enabled only if there are other tabs
        _closeOtherTabsMenuItem.IsEnabled(TabViewNumTabs() > 1);
        // close tabs after is enabled only if there are other tabs on the right
        _closeTabsAfterMenuItem.IsEnabled(TabViewIndex() < TabViewNumTabs() - 1);
<|edits_diff|>
--- src/cascadia/TerminalApp/TabBase.cpp
+++ src/cascadia/TerminalApp/TabBase.cpp
@@ -20,6 +20,10 @@
             }
         });
         _closeTabsAfterMenuItem.Text(RS_(L"TabCloseAfter"));
+        const auto closeTabsAfterToolTip = RS_(L"TabCloseAfterToolTip");
+
+        WUX::Controls::ToolTipService::SetToolTip(_closeTabsAfterMenuItem, box_value(closeTabsAfterToolTip));
+        Automation::AutomationProperties::SetHelpText(_closeTabsAfterMenuItem, closeTabsAfterToolTip);

         // Close other tabs
         _closeOtherTabsMenuItem.Click([weakThis](auto&&, auto&&) {
@@ -29,6 +33,10 @@
             }
         });
         _closeOtherTabsMenuItem.Text(RS_(L"TabCloseOther"));
+        const auto closeOtherTabsToolTip = RS_(L"TabCloseOtherToolTip");
+
+        WUX::Controls::ToolTipService::SetToolTip(_closeOtherTabsMenuItem, box_value(closeOtherTabsToolTip));
+        Automation::AutomationProperties::SetHelpText(_closeOtherTabsMenuItem, closeOtherTabsToolTip);

         // Close
         Controls::MenuFlyoutItem closeTabMenuItem;
<|current_version|>
        _AppendCloseMenuItems(contextMenuFlyout);
        TabViewItem().ContextFlyout(contextMenuFlyout);
    }

    // Method Description:
    // - Append the close menu items to the context menu flyout
    // Arguments:
    // - flyout - the menu flyout to which the close items must be appended
    // Return Value:
    // - <none>
    void TabBase::_AppendCloseMenuItems(winrt::Windows::UI::Xaml::Controls::MenuFlyout flyout)
    {
        auto weakThis{ get_weak() };

        // Close tabs after
        _closeTabsAfterMenuItem.Click([weakThis](auto&&, auto&&) {
            if (auto tab{ weakThis.get() })
            {
                tab->_CloseTabsAfter();
            }
        });
        _closeTabsAfterMenuItem.Text(RS_(L"TabCloseAfter"));
        const auto closeTabsAfterToolTip = RS_(L"TabCloseAfterToolTip");

        WUX::Controls::ToolTipService::SetToolTip(_closeTabsAfterMenuItem, box_value(closeTabsAfterToolTip));
        Automation::AutomationProperties::SetHelpText(_closeTabsAfterMenuItem, closeTabsAfterToolTip);

        // Close other tabs
        _closeOtherTabsMenuItem.Click([weakThis](auto&&, auto&&) {
            if (auto tab{ weakThis.get() })
            {
                tab->_CloseOtherTabs();
            }
        });
        _closeOtherTabsMenuItem.Text(RS_(L"TabCloseOther"));
        const auto closeOtherTabsToolTip = RS_(L"TabCloseOtherToolTip");

        WUX::Controls::ToolTipService::SetToolTip(_closeOtherTabsMenuItem, box_value(closeOtherTabsToolTip));
        Automation::AutomationProperties::SetHelpText(_closeOtherTabsMenuItem, closeOtherTabsToolTip);

        // Close
        Controls::MenuFlyoutItem closeTabMenuItem;
        Controls::FontIcon closeSymbol;
        closeSymbol.FontFamily(Media::FontFamily{ L"Segoe Fluent Icons, Segoe MDL2 Assets" });
        closeSymbol.Glyph(L"\xE711");

        closeTabMenuItem.Click([weakThis](auto&&, auto&&) {
            if (auto tab{ weakThis.get() })
            {
                tab->_CloseRequestedHandlers(nullptr, nullptr);
            }
        });
        closeTabMenuItem.Text(RS_(L"TabClose"));
        closeTabMenuItem.Icon(closeSymbol);

        // GH#8238 append the close menu items to the flyout itself until crash in XAML is fixed
        //Controls::MenuFlyoutSubItem closeSubMenu;
        //closeSubMenu.Text(RS_(L"TabCloseSubMenu"));
        //closeSubMenu.Items().Append(_closeTabsAfterMenuItem);
        //closeSubMenu.Items().Append(_closeOtherTabsMenuItem);
        //flyout.Items().Append(closeSubMenu);
        flyout.Items().Append(_closeTabsAfterMenuItem);
        flyout.Items().Append(_closeOtherTabsMenuItem);
        flyout.Items().Append(closeTabMenuItem);
    }

    // Method Description:
    // - Enable the Close menu items based on tab index and total number of tabs
    // Arguments:
    // - <none>
    // Return Value:
    // - <none>
    void TabBase::_EnableCloseMenuItems()
    {
        // close other tabs is enabled only if there are other tabs
        _closeOtherTabsMenuItem.IsEnabled(TabViewNumTabs() > 1);
        // close tabs after is enabled only if there are other tabs on the right
        _closeTabsAfterMenuItem.IsEnabled(TabViewIndex() < TabViewNumTabs() - 1);
<|next_version|>
        _AppendCloseMenuItems(contextMenuFlyout);
        TabViewItem().ContextFlyout(contextMenuFlyout);
    }

    // Method Description:
    // - Append the close menu items to the context menu flyout
    // Arguments:
    // - flyout - the menu flyout to which the close items must be appended
    // Return Value:
    // - <none>
    void TabBase::_AppendCloseMenuItems(winrt::Windows::UI::Xaml::Controls::MenuFlyout flyout)
    {
        auto weakThis{ get_weak() };

        // Close tabs after
        _closeTabsAfterMenuItem.Click([weakThis](auto&&, auto&&) {
            if (auto tab{ weakThis.get() })
            {
                tab->_CloseTabsAfter();
            }
        });
        _closeTabsAfterMenuItem.Text(RS_(L"TabCloseAfter"));
        const auto closeTabsAfterToolTip = RS_(L"TabCloseAfterToolTip");

        WUX::Controls::ToolTipService::SetToolTip(_closeTabsAfterMenuItem, box_value(closeTabsAfterToolTip));
        Automation::AutomationProperties::SetHelpText(_closeTabsAfterMenuItem, closeTabsAfterToolTip);

        // Close other tabs
        _closeOtherTabsMenuItem.Click([weakThis](auto&&, auto&&) {
            if (auto tab{ weakThis.get() })
            {
                tab->_CloseOtherTabs();
            }
        });
        _closeOtherTabsMenuItem.Text(RS_(L"TabCloseOther"));
        const auto closeOtherTabsToolTip = RS_(L"TabCloseOtherToolTip");

        WUX::Controls::ToolTipService::SetToolTip(_closeOtherTabsMenuItem, box_value(closeOtherTabsToolTip));
        Automation::AutomationProperties::SetHelpText(_closeOtherTabsMenuItem, closeOtherTabsToolTip);

        // Close
        Controls::MenuFlyoutItem closeTabMenuItem;
        Controls::FontIcon closeSymbol;
        closeSymbol.FontFamily(Media::FontFamily{ L"Segoe Fluent Icons, Segoe MDL2 Assets" });
        closeSymbol.Glyph(L"\xE711");

        closeTabMenuItem.Click([weakThis](auto&&, auto&&) {
            if (auto tab{ weakThis.get() })
            {
                tab->_CloseRequestedHandlers(nullptr, nullptr);
            }
        });
        closeTabMenuItem.Text(RS_(L"TabClose"));
        closeTabMenuItem.Icon(closeSymbol);
        const auto closeTabToolTip = RS_(L"TabCloseToolTip");

        WUX::Controls::ToolTipService::SetToolTip(closeTabMenuItem, box_value(closeTabToolTip));
        Automation::AutomationProperties::SetHelpText(closeTabMenuItem, closeTabToolTip);

        // GH#8238 append the close menu items to the flyout itself until crash in XAML is fixed
        //Controls::MenuFlyoutSubItem closeSubMenu;
        //closeSubMenu.Text(RS_(L"TabCloseSubMenu"));
        //closeSubMenu.Items().Append(_closeTabsAfterMenuItem);
        //closeSubMenu.Items().Append(_closeOtherTabsMenuItem);
        //flyout.Items().Append(closeSubMenu);
        flyout.Items().Append(_closeTabsAfterMenuItem);
        flyout.Items().Append(_closeOtherTabsMenuItem);
        flyout.Items().Append(closeTabMenuItem);
    }

    // Method Description:
    // - Enable the Close menu items based on tab index and total number of tabs
    // Arguments:
    // - <none>
    // Return Value:
    // - <none>
    void TabBase::_EnableCloseMenuItems()
    {
        // close other tabs is enabled only if there are other tabs
        _closeOtherTabsMenuItem.IsEnabled(TabViewNumTabs() > 1);
        // close tabs after is enabled only if there are other tabs on the right
        _closeTabsAfterMenuItem.IsEnabled(TabViewIndex() < TabViewNumTabs() - 1);


Add tooltips to context menus (#14058)

<|original_code|>

        // add menu separator
        auto separatorItem = WUX::Controls::MenuFlyoutSeparator{};
        newTabFlyout.Items().Append(separatorItem);

        // add static items
        {
            // GH#2455 - Make sure to try/catch calls to Application::Current,
            // because that _won't_ be an instance of TerminalApp::App in the
            // LocalTests
            auto isUwp = false;
            try
            {
                isUwp = ::winrt::Windows::UI::Xaml::Application::Current().as<::winrt::TerminalApp::App>().Logic().IsUwp();
            }
            CATCH_LOG();

            if (!isUwp)
            {
                // Create the settings button.
                auto settingsItem = WUX::Controls::MenuFlyoutItem{};
                settingsItem.Text(RS_(L"SettingsMenuItem"));

                WUX::Controls::SymbolIcon ico{};
                ico.Symbol(WUX::Controls::Symbol::Setting);
                settingsItem.Icon(ico);

                settingsItem.Click({ this, &TerminalPage::_SettingsButtonOnClick });
                newTabFlyout.Items().Append(settingsItem);

                const auto settingsKeyChord{ actionMap.GetKeyBindingForAction(ShortcutAction::OpenSettings, OpenSettingsArgs{ SettingsTarget::SettingsUI }) };
                if (settingsKeyChord)
                {
                    _SetAcceleratorForMenuItem(settingsItem, settingsKeyChord);
                }

                // Create the command palette button.
                auto commandPaletteFlyout = WUX::Controls::MenuFlyoutItem{};
                commandPaletteFlyout.Text(RS_(L"CommandPaletteMenuItem"));

                WUX::Controls::FontIcon commandPaletteIcon{};
                commandPaletteIcon.Glyph(L"\xE945");
                commandPaletteIcon.FontFamily(Media::FontFamily{ L"Segoe Fluent Icons, Segoe MDL2 Assets" });
                commandPaletteFlyout.Icon(commandPaletteIcon);

                commandPaletteFlyout.Click({ this, &TerminalPage::_CommandPaletteButtonOnClick });
                newTabFlyout.Items().Append(commandPaletteFlyout);

                const auto commandPaletteKeyChord{ actionMap.GetKeyBindingForAction(ShortcutAction::ToggleCommandPalette) };
                if (commandPaletteKeyChord)
                {
                    _SetAcceleratorForMenuItem(commandPaletteFlyout, commandPaletteKeyChord);
                }
            }

            // Create the about button.
            auto aboutFlyout = WUX::Controls::MenuFlyoutItem{};
            aboutFlyout.Text(RS_(L"AboutMenuItem"));

            WUX::Controls::SymbolIcon aboutIcon{};
            aboutIcon.Symbol(WUX::Controls::Symbol::Help);
            aboutFlyout.Icon(aboutIcon);

            aboutFlyout.Click({ this, &TerminalPage::_AboutButtonOnClick });
            newTabFlyout.Items().Append(aboutFlyout);
        }

        // Before opening the fly-out set focus on the current tab
        // so no matter how fly-out is closed later on the focus will return to some tab.
        // We cannot do it on closing because if the window loses focus (alt+tab)
        // the closing event is not fired.
        // It is important to set the focus on the tab
        // Since the previous focus location might be discarded in the background,
        // e.g., the command palette will be dismissed by the menu,
        // and then closing the fly-out will move the focus to wrong location.
        newTabFlyout.Opening([this](auto&&, auto&&) {
            _FocusCurrentTab(true);
        });
        _newTabButton.Flyout(newTabFlyout);
    }

    // Function Description:
<|edits_diff|>
--- src/cascadia/TerminalApp/TerminalPage.cpp
+++ src/cascadia/TerminalApp/TerminalPage.cpp
@@ -20,6 +20,10 @@
                 // Create the settings button.
                 auto settingsItem = WUX::Controls::MenuFlyoutItem{};
                 settingsItem.Text(RS_(L"SettingsMenuItem"));
+                const auto settingsToolTip = RS_(L"SettingsToolTip");
+
+                WUX::Controls::ToolTipService::SetToolTip(settingsItem, box_value(settingsToolTip));
+                Automation::AutomationProperties::SetHelpText(settingsItem, settingsToolTip);

                 WUX::Controls::SymbolIcon ico{};
                 ico.Symbol(WUX::Controls::Symbol::Setting);
@@ -37,6 +41,10 @@
                 // Create the command palette button.
                 auto commandPaletteFlyout = WUX::Controls::MenuFlyoutItem{};
                 commandPaletteFlyout.Text(RS_(L"CommandPaletteMenuItem"));
+                const auto commandPaletteToolTip = RS_(L"CommandPaletteToolTip");
+
+                WUX::Controls::ToolTipService::SetToolTip(commandPaletteFlyout, box_value(commandPaletteToolTip));
+                Automation::AutomationProperties::SetHelpText(commandPaletteFlyout, commandPaletteToolTip);

                 WUX::Controls::FontIcon commandPaletteIcon{};
                 commandPaletteIcon.Glyph(L"\xE945");
<|current_version|>

        // add menu separator
        auto separatorItem = WUX::Controls::MenuFlyoutSeparator{};
        newTabFlyout.Items().Append(separatorItem);

        // add static items
        {
            // GH#2455 - Make sure to try/catch calls to Application::Current,
            // because that _won't_ be an instance of TerminalApp::App in the
            // LocalTests
            auto isUwp = false;
            try
            {
                isUwp = ::winrt::Windows::UI::Xaml::Application::Current().as<::winrt::TerminalApp::App>().Logic().IsUwp();
            }
            CATCH_LOG();

            if (!isUwp)
            {
                // Create the settings button.
                auto settingsItem = WUX::Controls::MenuFlyoutItem{};
                settingsItem.Text(RS_(L"SettingsMenuItem"));
                const auto settingsToolTip = RS_(L"SettingsToolTip");

                WUX::Controls::ToolTipService::SetToolTip(settingsItem, box_value(settingsToolTip));
                Automation::AutomationProperties::SetHelpText(settingsItem, settingsToolTip);

                WUX::Controls::SymbolIcon ico{};
                ico.Symbol(WUX::Controls::Symbol::Setting);
                settingsItem.Icon(ico);

                settingsItem.Click({ this, &TerminalPage::_SettingsButtonOnClick });
                newTabFlyout.Items().Append(settingsItem);

                const auto settingsKeyChord{ actionMap.GetKeyBindingForAction(ShortcutAction::OpenSettings, OpenSettingsArgs{ SettingsTarget::SettingsUI }) };
                if (settingsKeyChord)
                {
                    _SetAcceleratorForMenuItem(settingsItem, settingsKeyChord);
                }

                // Create the command palette button.
                auto commandPaletteFlyout = WUX::Controls::MenuFlyoutItem{};
                commandPaletteFlyout.Text(RS_(L"CommandPaletteMenuItem"));
                const auto commandPaletteToolTip = RS_(L"CommandPaletteToolTip");

                WUX::Controls::ToolTipService::SetToolTip(commandPaletteFlyout, box_value(commandPaletteToolTip));
                Automation::AutomationProperties::SetHelpText(commandPaletteFlyout, commandPaletteToolTip);

                WUX::Controls::FontIcon commandPaletteIcon{};
                commandPaletteIcon.Glyph(L"\xE945");
                commandPaletteIcon.FontFamily(Media::FontFamily{ L"Segoe Fluent Icons, Segoe MDL2 Assets" });
                commandPaletteFlyout.Icon(commandPaletteIcon);

                commandPaletteFlyout.Click({ this, &TerminalPage::_CommandPaletteButtonOnClick });
                newTabFlyout.Items().Append(commandPaletteFlyout);

                const auto commandPaletteKeyChord{ actionMap.GetKeyBindingForAction(ShortcutAction::ToggleCommandPalette) };
                if (commandPaletteKeyChord)
                {
                    _SetAcceleratorForMenuItem(commandPaletteFlyout, commandPaletteKeyChord);
                }
            }

            // Create the about button.
            auto aboutFlyout = WUX::Controls::MenuFlyoutItem{};
            aboutFlyout.Text(RS_(L"AboutMenuItem"));

            WUX::Controls::SymbolIcon aboutIcon{};
            aboutIcon.Symbol(WUX::Controls::Symbol::Help);
            aboutFlyout.Icon(aboutIcon);

            aboutFlyout.Click({ this, &TerminalPage::_AboutButtonOnClick });
            newTabFlyout.Items().Append(aboutFlyout);
        }

        // Before opening the fly-out set focus on the current tab
        // so no matter how fly-out is closed later on the focus will return to some tab.
        // We cannot do it on closing because if the window loses focus (alt+tab)
        // the closing event is not fired.
        // It is important to set the focus on the tab
        // Since the previous focus location might be discarded in the background,
        // e.g., the command palette will be dismissed by the menu,
        // and then closing the fly-out will move the focus to wrong location.
        newTabFlyout.Opening([this](auto&&, auto&&) {
            _FocusCurrentTab(true);
        });
        _newTabButton.Flyout(newTabFlyout);
    }

    // Function Description:
<|next_version|>

        // add menu separator
        auto separatorItem = WUX::Controls::MenuFlyoutSeparator{};
        newTabFlyout.Items().Append(separatorItem);

        // add static items
        {
            // GH#2455 - Make sure to try/catch calls to Application::Current,
            // because that _won't_ be an instance of TerminalApp::App in the
            // LocalTests
            auto isUwp = false;
            try
            {
                isUwp = ::winrt::Windows::UI::Xaml::Application::Current().as<::winrt::TerminalApp::App>().Logic().IsUwp();
            }
            CATCH_LOG();

            if (!isUwp)
            {
                // Create the settings button.
                auto settingsItem = WUX::Controls::MenuFlyoutItem{};
                settingsItem.Text(RS_(L"SettingsMenuItem"));
                const auto settingsToolTip = RS_(L"SettingsToolTip");

                WUX::Controls::ToolTipService::SetToolTip(settingsItem, box_value(settingsToolTip));
                Automation::AutomationProperties::SetHelpText(settingsItem, settingsToolTip);

                WUX::Controls::SymbolIcon ico{};
                ico.Symbol(WUX::Controls::Symbol::Setting);
                settingsItem.Icon(ico);

                settingsItem.Click({ this, &TerminalPage::_SettingsButtonOnClick });
                newTabFlyout.Items().Append(settingsItem);

                const auto settingsKeyChord{ actionMap.GetKeyBindingForAction(ShortcutAction::OpenSettings, OpenSettingsArgs{ SettingsTarget::SettingsUI }) };
                if (settingsKeyChord)
                {
                    _SetAcceleratorForMenuItem(settingsItem, settingsKeyChord);
                }

                // Create the command palette button.
                auto commandPaletteFlyout = WUX::Controls::MenuFlyoutItem{};
                commandPaletteFlyout.Text(RS_(L"CommandPaletteMenuItem"));
                const auto commandPaletteToolTip = RS_(L"CommandPaletteToolTip");

                WUX::Controls::ToolTipService::SetToolTip(commandPaletteFlyout, box_value(commandPaletteToolTip));
                Automation::AutomationProperties::SetHelpText(commandPaletteFlyout, commandPaletteToolTip);

                WUX::Controls::FontIcon commandPaletteIcon{};
                commandPaletteIcon.Glyph(L"\xE945");
                commandPaletteIcon.FontFamily(Media::FontFamily{ L"Segoe Fluent Icons, Segoe MDL2 Assets" });
                commandPaletteFlyout.Icon(commandPaletteIcon);

                commandPaletteFlyout.Click({ this, &TerminalPage::_CommandPaletteButtonOnClick });
                newTabFlyout.Items().Append(commandPaletteFlyout);

                const auto commandPaletteKeyChord{ actionMap.GetKeyBindingForAction(ShortcutAction::ToggleCommandPalette) };
                if (commandPaletteKeyChord)
                {
                    _SetAcceleratorForMenuItem(commandPaletteFlyout, commandPaletteKeyChord);
                }
            }

            // Create the about button.
            auto aboutFlyout = WUX::Controls::MenuFlyoutItem{};
            aboutFlyout.Text(RS_(L"AboutMenuItem"));
            const auto aboutToolTip = RS_(L"AboutToolTip");

            WUX::Controls::ToolTipService::SetToolTip(aboutFlyout, box_value(aboutToolTip));
            Automation::AutomationProperties::SetHelpText(aboutFlyout, aboutToolTip);

            WUX::Controls::SymbolIcon aboutIcon{};
            aboutIcon.Symbol(WUX::Controls::Symbol::Help);
            aboutFlyout.Icon(aboutIcon);

            aboutFlyout.Click({ this, &TerminalPage::_AboutButtonOnClick });
            newTabFlyout.Items().Append(aboutFlyout);
        }

        // Before opening the fly-out set focus on the current tab
        // so no matter how fly-out is closed later on the focus will return to some tab.
        // We cannot do it on closing because if the window loses focus (alt+tab)
        // the closing event is not fired.
        // It is important to set the focus on the tab
        // Since the previous focus location might be discarded in the background,
        // e.g., the command palette will be dismissed by the menu,
        // and then closing the fly-out will move the focus to wrong location.
        newTabFlyout.Opening([this](auto&&, auto&&) {
            _FocusCurrentTab(true);
        });
        _newTabButton.Flyout(newTabFlyout);
    }

    // Function Description: