From f98c66ece81953c777cd332c6bd29d707b1685e5 Mon Sep 17 00:00:00 2001
From: nicm <nicm>
Date: Thu, 7 Mar 2019 20:24:21 +0000
Subject: [PATCH] Add a separate mode struct for the active window mode if any.

---
 cmd-capture-pane.c |   2 +-
 cmd-copy-mode.c    |   6 +-
 cmd-send-keys.c    |  51 +--
 format.c           |   6 +-
 server-client.c    |   7 +-
 tmux.h             |  32 +-
 window-buffer.c    |  28 +-
 window-client.c    |  28 +-
 window-clock.c     |  47 +--
 window-copy.c      | 936 ++++++++++++++++++++++++---------------------
 window-tree.c      |  27 +-
 window.c           |  26 +-
 12 files changed, 638 insertions(+), 558 deletions(-)

diff --git a/cmd-capture-pane.c b/cmd-capture-pane.c
index d3d7e8f8..cb01f502 100644
--- a/cmd-capture-pane.c
+++ b/cmd-capture-pane.c
@@ -199,7 +199,7 @@ cmd_capture_pane_exec(struct cmd *self, struct cmdq_item *item)
 	size_t			 len;
 
 	if (self->entry == &cmd_clear_history_entry) {
-		if (wp->mode == &window_copy_mode)
+		if (wp->mode != NULL && wp->mode->mode == &window_copy_mode)
 			window_pane_reset_mode(wp);
 		grid_clear_history(wp->base.grid);
 		return (CMD_RETURN_NORMAL);
diff --git a/cmd-copy-mode.c b/cmd-copy-mode.c
index 699a868d..1668029c 100644
--- a/cmd-copy-mode.c
+++ b/cmd-copy-mode.c
@@ -74,18 +74,18 @@ cmd_copy_mode_exec(struct cmd *self, struct cmdq_item *item)
 		return (CMD_RETURN_NORMAL);
 	}
 
-	if (wp->mode != &window_copy_mode) {
+	if (wp->mode == NULL || wp->mode->mode != &window_copy_mode) {
 		flag = window_pane_set_mode(wp, &window_copy_mode, NULL, NULL);
 		if (flag != 0)
 			return (CMD_RETURN_NORMAL);
 		window_copy_init_from_pane(wp, args_has(self->args, 'e'));
 	}
 	if (args_has(args, 'M')) {
-		if (wp->mode != NULL && wp->mode != &window_copy_mode)
+		if (wp->mode != NULL && wp->mode->mode != &window_copy_mode)
 			return (CMD_RETURN_NORMAL);
 		window_copy_start_drag(c, &shared->mouse);
 	}
-	if (wp->mode == &window_copy_mode && args_has(self->args, 'u'))
+	if (args_has(self->args, 'u'))
 		window_copy_pageup(wp, 0);
 
 	return (CMD_RETURN_NORMAL);
diff --git a/cmd-send-keys.c b/cmd-send-keys.c
index 8fc00023..80799c54 100644
--- a/cmd-send-keys.c
+++ b/cmd-send-keys.c
@@ -58,19 +58,20 @@ const struct cmd_entry cmd_send_prefix_entry = {
 static void
 cmd_send_keys_inject(struct client *c, struct cmdq_item *item, key_code key)
 {
-	struct window_pane	*wp = item->target.wp;
-	struct session		*s = item->target.s;
-	struct winlink		*wl = item->target.wl;
-	struct key_table	*table;
-	struct key_binding	*bd;
+	struct window_pane		*wp = item->target.wp;
+	struct session			*s = item->target.s;
+	struct winlink			*wl = item->target.wl;
+	struct window_mode_entry	*wme = wp->mode;
+	struct key_table		*table;
+	struct key_binding		*bd;
 
-	if (wp->mode == NULL || wp->mode->key_table == NULL) {
+	if (wme == NULL || wme->mode->key_table == NULL) {
 		if (options_get_number(wp->window->options, "xterm-keys"))
 			key |= KEYC_XTERM;
 		window_pane_key(wp, NULL, s, wl, key, NULL);
 		return;
 	}
-	table = key_bindings_get_table(wp->mode->key_table(wp), 1);
+	table = key_bindings_get_table(wme->mode->key_table(wme), 1);
 
 	bd = key_bindings_get(table, key & ~KEYC_XTERM);
 	if (bd != NULL) {
@@ -83,18 +84,19 @@ cmd_send_keys_inject(struct client *c, struct cmdq_item *item, key_code key)
 static enum cmd_retval
 cmd_send_keys_exec(struct cmd *self, struct cmdq_item *item)
 {
-	struct args		*args = self->args;
-	struct client		*c = cmd_find_client(item, NULL, 1);
-	struct window_pane	*wp = item->target.wp;
-	struct session		*s = item->target.s;
-	struct winlink		*wl = item->target.wl;
-	struct mouse_event	*m = &item->shared->mouse;
-	struct utf8_data	*ud, *uc;
-	wchar_t			 wc;
-	int			 i, literal;
-	key_code		 key;
-	u_int			 np = 1;
-	char			*cause = NULL;
+	struct args			*args = self->args;
+	struct client			*c = cmd_find_client(item, NULL, 1);
+	struct window_pane		*wp = item->target.wp;
+	struct session			*s = item->target.s;
+	struct winlink			*wl = item->target.wl;
+	struct mouse_event		*m = &item->shared->mouse;
+	struct window_mode_entry	*wme = wp->mode;
+	struct utf8_data		*ud, *uc;
+	wchar_t				 wc;
+	int				 i, literal;
+	key_code			 key;
+	u_int				 np = 1;
+	char				*cause = NULL;
 
 	if (args_has(args, 'N')) {
 		np = args_strtonum(args, 'N', 1, UINT_MAX, &cause);
@@ -103,19 +105,18 @@ cmd_send_keys_exec(struct cmd *self, struct cmdq_item *item)
 			free(cause);
 			return (CMD_RETURN_ERROR);
 		}
-		if (args_has(args, 'X') || args->argc == 0)
-			wp->modeprefix = np;
+		if (wme != NULL && (args_has(args, 'X') || args->argc == 0))
+			wme->prefix = np;
 	}
 
 	if (args_has(args, 'X')) {
-		if (wp->mode == NULL || wp->mode->command == NULL) {
+		if (wme == NULL || wme->mode->command == NULL) {
 			cmdq_error(item, "not in a mode");
 			return (CMD_RETURN_ERROR);
 		}
 		if (!m->valid)
-			wp->mode->command(wp, c, s, wl, args, NULL);
-		else
-			wp->mode->command(wp, c, s, wl, args, m);
+			m = NULL;
+		wme->mode->command(wme, c, s, wl, args, m);
 		return (CMD_RETURN_NORMAL);
 	}
 
diff --git a/format.c b/format.c
index f60a4019..166b18a6 100644
--- a/format.c
+++ b/format.c
@@ -1535,7 +1535,7 @@ format_defaults_pane(struct format_tree *ft, struct window_pane *wp)
 
 	format_add(ft, "pane_in_mode", "%d", wp->screen != &wp->base);
 	if (wp->mode != NULL)
-		format_add(ft, "pane_mode", "%s", wp->mode->name);
+		format_add(ft, "pane_mode", "%s", wp->mode->mode->name);
 
 	format_add(ft, "pane_synchronized", "%d",
 	    !!options_get_number(w->options, "synchronize-panes"));
@@ -1552,8 +1552,8 @@ format_defaults_pane(struct format_tree *ft, struct window_pane *wp)
 	format_add(ft, "scroll_region_upper", "%u", wp->base.rupper);
 	format_add(ft, "scroll_region_lower", "%u", wp->base.rlower);
 
-	if (wp->mode != NULL && wp->mode->formats != NULL)
-		wp->mode->formats(wp, ft);
+	if (wp->mode != NULL && wp->mode->mode->formats != NULL)
+		wp->mode->mode->formats(wp->mode, ft);
 
 	format_add(ft, "alternate_on", "%d", wp->saved_grid ? 1 : 0);
 	format_add(ft, "alternate_saved_x", "%u", wp->saved_cx);
diff --git a/server-client.c b/server-client.c
index bf69a981..a311376c 100644
--- a/server-client.c
+++ b/server-client.c
@@ -928,6 +928,7 @@ server_client_handle_key(struct client *c, key_code key)
 	struct window_pane	*wp;
 	struct timeval		 tv;
 	struct key_table	*table, *first;
+	const char		*tablename;
 	struct key_binding	*bd;
 	int			 xtimeout, flags;
 	struct cmd_find_state	 fs;
@@ -1009,8 +1010,10 @@ server_client_handle_key(struct client *c, key_code key)
 	if (server_client_is_default_key_table(c, c->keytable) &&
 	    wp != NULL &&
 	    wp->mode != NULL &&
-	    wp->mode->key_table != NULL)
-		table = key_bindings_get_table(wp->mode->key_table(wp), 1);
+	    wp->mode->mode->key_table != NULL) {
+		tablename = wp->mode->mode->key_table(wp->mode);
+		table = key_bindings_get_table(tablename, 1);
+	}
 	else
 		table = c->keytable;
 	first = table;
diff --git a/tmux.h b/tmux.h
index f0ee838e..69935f17 100644
--- a/tmux.h
+++ b/tmux.h
@@ -693,25 +693,37 @@ struct screen_write_ctx {
  * Window mode. Windows can be in several modes and this is used to call the
  * right function to handle input and output.
  */
+struct window_mode_entry;
 struct window_mode {
 	const char	*name;
 
-	struct screen	*(*init)(struct window_pane *, struct cmd_find_state *,
-			     struct args *);
-	void		 (*free)(struct window_pane *);
-	void		 (*resize)(struct window_pane *, u_int, u_int);
-	void		 (*key)(struct window_pane *, struct client *,
+	struct screen	*(*init)(struct window_mode_entry *,
+			     struct cmd_find_state *, struct args *);
+	void		 (*free)(struct window_mode_entry *);
+	void		 (*resize)(struct window_mode_entry *, u_int, u_int);
+	void		 (*key)(struct window_mode_entry *, struct client *,
 			     struct session *, struct winlink *, key_code,
 			     struct mouse_event *);
 
-	const char	*(*key_table)(struct window_pane *);
-	void		 (*command)(struct window_pane *, struct client *,
+	const char	*(*key_table)(struct window_mode_entry *);
+	void		 (*command)(struct window_mode_entry *, struct client *,
 			     struct session *, struct winlink *, struct args *,
 			     struct mouse_event *);
-	void		 (*formats)(struct window_pane *, struct format_tree *);
+	void		 (*formats)(struct window_mode_entry *,
+			     struct format_tree *);
 };
 #define WINDOW_MODE_TIMEOUT 180
 
+/* Active window mode. */
+struct window_mode_entry {
+	struct window_pane		*wp;
+
+	const struct window_mode	*mode;
+	void				*data;
+
+	u_int				 prefix;
+};
+
 /* Child window structure. */
 struct window_pane {
 	u_int		 id;
@@ -780,11 +792,9 @@ struct window_pane {
 	struct grid	*saved_grid;
 	struct grid_cell saved_cell;
 
-	const struct window_mode *mode;
-	void		*modedata;
+	struct window_mode_entry *mode;
 	struct event	 modetimer;
 	time_t		 modelast;
-	u_int		 modeprefix;
 	char		*searchstr;
 
 	TAILQ_ENTRY(window_pane) entry;
diff --git a/window-buffer.c b/window-buffer.c
index c67fa008..e1008e6b 100644
--- a/window-buffer.c
+++ b/window-buffer.c
@@ -25,12 +25,12 @@
 
 #include "tmux.h"
 
-static struct screen	*window_buffer_init(struct window_pane *,
+static struct screen	*window_buffer_init(struct window_mode_entry *,
 			     struct cmd_find_state *, struct args *);
-static void		 window_buffer_free(struct window_pane *);
-static void		 window_buffer_resize(struct window_pane *, u_int,
+static void		 window_buffer_free(struct window_mode_entry *);
+static void		 window_buffer_resize(struct window_mode_entry *, u_int,
 			     u_int);
-static void		 window_buffer_key(struct window_pane *,
+static void		 window_buffer_key(struct window_mode_entry *,
 			     struct client *, struct session *,
 			     struct winlink *, key_code, struct mouse_event *);
 
@@ -253,13 +253,14 @@ window_buffer_search(__unused void *modedata, void *itemdata, const char *ss)
 }
 
 static struct screen *
-window_buffer_init(struct window_pane *wp, __unused struct cmd_find_state *fs,
-    struct args *args)
+window_buffer_init(struct window_mode_entry *wme,
+    __unused struct cmd_find_state *fs, struct args *args)
 {
+	struct window_pane		*wp = wme->wp;
 	struct window_buffer_modedata	*data;
 	struct screen			*s;
 
-	wp->modedata = data = xcalloc(1, sizeof *data);
+	wme->data = data = xcalloc(1, sizeof *data);
 
 	if (args == NULL || !args_has(args, 'F'))
 		data->format = xstrdup(WINDOW_BUFFER_DEFAULT_FORMAT);
@@ -282,9 +283,9 @@ window_buffer_init(struct window_pane *wp, __unused struct cmd_find_state *fs,
 }
 
 static void
-window_buffer_free(struct window_pane *wp)
+window_buffer_free(struct window_mode_entry *wme)
 {
-	struct window_buffer_modedata	*data = wp->modedata;
+	struct window_buffer_modedata	*data = wme->data;
 	u_int				 i;
 
 	if (data == NULL)
@@ -303,9 +304,9 @@ window_buffer_free(struct window_pane *wp)
 }
 
 static void
-window_buffer_resize(struct window_pane *wp, u_int sx, u_int sy)
+window_buffer_resize(struct window_mode_entry *wme, u_int sx, u_int sy)
 {
-	struct window_buffer_modedata	*data = wp->modedata;
+	struct window_buffer_modedata	*data = wme->data;
 
 	mode_tree_resize(data->data, sx, sy);
 }
@@ -337,11 +338,12 @@ window_buffer_do_paste(void* modedata, void *itemdata, struct client *c,
 }
 
 static void
-window_buffer_key(struct window_pane *wp, struct client *c,
+window_buffer_key(struct window_mode_entry *wme, struct client *c,
     __unused struct session *s, __unused struct winlink *wl, key_code key,
     struct mouse_event *m)
 {
-	struct window_buffer_modedata	*data = wp->modedata;
+	struct window_pane		*wp = wme->wp;
+	struct window_buffer_modedata	*data = wme->data;
 	struct mode_tree_data		*mtd = data->data;
 	struct window_buffer_itemdata	*item;
 	int				 finished;
diff --git a/window-client.c b/window-client.c
index 7ac13b0b..f0c81cf0 100644
--- a/window-client.c
+++ b/window-client.c
@@ -25,12 +25,12 @@
 
 #include "tmux.h"
 
-static struct screen	*window_client_init(struct window_pane *,
+static struct screen	*window_client_init(struct window_mode_entry *,
 			     struct cmd_find_state *, struct args *);
-static void		 window_client_free(struct window_pane *);
-static void		 window_client_resize(struct window_pane *, u_int,
+static void		 window_client_free(struct window_mode_entry *);
+static void		 window_client_resize(struct window_mode_entry *, u_int,
 			     u_int);
-static void		 window_client_key(struct window_pane *,
+static void		 window_client_key(struct window_mode_entry *,
 			     struct client *, struct session *,
 			     struct winlink *, key_code, struct mouse_event *);
 
@@ -236,13 +236,14 @@ window_client_draw(__unused void *modedata, void *itemdata,
 }
 
 static struct screen *
-window_client_init(struct window_pane *wp, __unused struct cmd_find_state *fs,
-    struct args *args)
+window_client_init(struct window_mode_entry *wme,
+    __unused struct cmd_find_state *fs, struct args *args)
 {
+	struct window_pane		*wp = wme->wp;
 	struct window_client_modedata	*data;
 	struct screen			*s;
 
-	wp->modedata = data = xcalloc(1, sizeof *data);
+	wme->data = data = xcalloc(1, sizeof *data);
 
 	if (args == NULL || !args_has(args, 'F'))
 		data->format = xstrdup(WINDOW_CLIENT_DEFAULT_FORMAT);
@@ -265,9 +266,9 @@ window_client_init(struct window_pane *wp, __unused struct cmd_find_state *fs,
 }
 
 static void
-window_client_free(struct window_pane *wp)
+window_client_free(struct window_mode_entry *wme)
 {
-	struct window_client_modedata	*data = wp->modedata;
+	struct window_client_modedata	*data = wme->data;
 	u_int				 i;
 
 	if (data == NULL)
@@ -286,9 +287,9 @@ window_client_free(struct window_pane *wp)
 }
 
 static void
-window_client_resize(struct window_pane *wp, u_int sx, u_int sy)
+window_client_resize(struct window_mode_entry *wme, u_int sx, u_int sy)
 {
-	struct window_client_modedata	*data = wp->modedata;
+	struct window_client_modedata	*data = wme->data;
 
 	mode_tree_resize(data->data, sx, sy);
 }
@@ -311,11 +312,12 @@ window_client_do_detach(void* modedata, void *itemdata,
 }
 
 static void
-window_client_key(struct window_pane *wp, struct client *c,
+window_client_key(struct window_mode_entry *wme, struct client *c,
     __unused struct session *s, __unused struct winlink *wl, key_code key,
     struct mouse_event *m)
 {
-	struct window_client_modedata	*data = wp->modedata;
+	struct window_pane		*wp = wme->wp;
+	struct window_client_modedata	*data = wme->data;
 	struct mode_tree_data		*mtd = data->data;
 	struct window_client_itemdata	*item;
 	int				 finished;
diff --git a/window-clock.c b/window-clock.c
index 9642a141..c7074c0d 100644
--- a/window-clock.c
+++ b/window-clock.c
@@ -24,16 +24,16 @@
 
 #include "tmux.h"
 
-static struct screen *window_clock_init(struct window_pane *,
+static struct screen *window_clock_init(struct window_mode_entry *,
 		    struct cmd_find_state *, struct args *);
-static void	window_clock_free(struct window_pane *);
-static void	window_clock_resize(struct window_pane *, u_int, u_int);
-static void	window_clock_key(struct window_pane *, struct client *,
+static void	window_clock_free(struct window_mode_entry *);
+static void	window_clock_resize(struct window_mode_entry *, u_int, u_int);
+static void	window_clock_key(struct window_mode_entry *, struct client *,
 		     struct session *, struct winlink *, key_code,
 		     struct mouse_event *);
 
 static void	window_clock_timer_callback(int, short, void *);
-static void	window_clock_draw_screen(struct window_pane *);
+static void	window_clock_draw_screen(struct window_mode_entry *);
 
 const struct window_mode window_clock_mode = {
 	.name = "clock-mode",
@@ -126,8 +126,9 @@ const char window_clock_table[14][5][5] = {
 static void
 window_clock_timer_callback(__unused int fd, __unused short events, void *arg)
 {
-	struct window_pane		*wp = arg;
-	struct window_clock_mode_data	*data = wp->modedata;
+	struct window_mode_entry	*wme = arg;
+	struct window_pane		*wp = wme->wp;
+	struct window_clock_mode_data	*data = wme->data;
 	struct tm			 now, then;
 	time_t				 t;
 	struct timeval			 tv = { .tv_sec = 1 };
@@ -142,37 +143,38 @@ window_clock_timer_callback(__unused int fd, __unused short events, void *arg)
 		return;
 	data->tim = t;
 
-	window_clock_draw_screen(wp);
+	window_clock_draw_screen(wme);
 	server_redraw_window(wp->window);
 }
 
 static struct screen *
-window_clock_init(struct window_pane *wp, __unused struct cmd_find_state *fs,
-    __unused struct args *args)
+window_clock_init(struct window_mode_entry *wme,
+    __unused struct cmd_find_state *fs, __unused struct args *args)
 {
+	struct window_pane		*wp = wme->wp;
 	struct window_clock_mode_data	*data;
 	struct screen			*s;
 	struct timeval			 tv = { .tv_sec = 1 };
 
-	wp->modedata = data = xmalloc(sizeof *data);
+	wme->data = data = xmalloc(sizeof *data);
 	data->tim = time(NULL);
 
-	evtimer_set(&data->timer, window_clock_timer_callback, wp);
+	evtimer_set(&data->timer, window_clock_timer_callback, wme);
 	evtimer_add(&data->timer, &tv);
 
 	s = &data->screen;
 	screen_init(s, screen_size_x(&wp->base), screen_size_y(&wp->base), 0);
 	s->mode &= ~MODE_CURSOR;
 
-	window_clock_draw_screen(wp);
+	window_clock_draw_screen(wme);
 
 	return (s);
 }
 
 static void
-window_clock_free(struct window_pane *wp)
+window_clock_free(struct window_mode_entry *wme)
 {
-	struct window_clock_mode_data	*data = wp->modedata;
+	struct window_clock_mode_data	*data = wme->data;
 
 	evtimer_del(&data->timer);
 	screen_free(&data->screen);
@@ -180,27 +182,28 @@ window_clock_free(struct window_pane *wp)
 }
 
 static void
-window_clock_resize(struct window_pane *wp, u_int sx, u_int sy)
+window_clock_resize(struct window_mode_entry *wme, u_int sx, u_int sy)
 {
-	struct window_clock_mode_data	*data = wp->modedata;
+	struct window_clock_mode_data	*data = wme->data;
 	struct screen			*s = &data->screen;
 
 	screen_resize(s, sx, sy, 0);
-	window_clock_draw_screen(wp);
+	window_clock_draw_screen(wme);
 }
 
 static void
-window_clock_key(struct window_pane *wp, __unused struct client *c,
+window_clock_key(struct window_mode_entry *wme, __unused struct client *c,
     __unused struct session *s, __unused struct winlink *wl,
     __unused key_code key, __unused struct mouse_event *m)
 {
-	window_pane_reset_mode(wp);
+	window_pane_reset_mode(wme->wp);
 }
 
 static void
-window_clock_draw_screen(struct window_pane *wp)
+window_clock_draw_screen(struct window_mode_entry *wme)
 {
-	struct window_clock_mode_data	*data = wp->modedata;
+	struct window_pane		*wp = wme->wp;
+	struct window_clock_mode_data	*data = wme->data;
 	struct screen_write_ctx	 	 ctx;
 	int				 colour, style;
 	struct screen			*s = &data->screen;
diff --git a/window-copy.c b/window-copy.c
index a79e3bf4..9efc6a46 100644
--- a/window-copy.c
+++ b/window-copy.c
@@ -24,85 +24,93 @@
 
 #include "tmux.h"
 
-static const char *window_copy_key_table(struct window_pane *);
-static void	window_copy_command(struct window_pane *, struct client *,
+static const char *window_copy_key_table(struct window_mode_entry *);
+static void	window_copy_command(struct window_mode_entry *, struct client *,
 		    struct session *, struct winlink *, struct args *,
 		    struct mouse_event *);
-static struct screen *window_copy_init(struct window_pane *,
+static struct screen *window_copy_init(struct window_mode_entry *,
 		    struct cmd_find_state *, struct args *);
-static void	window_copy_free(struct window_pane *);
-static void	window_copy_resize(struct window_pane *, u_int, u_int);
-static void	window_copy_formats(struct window_pane *, struct format_tree *);
-static int	window_copy_pagedown(struct window_pane *, int, int);
-static void	window_copy_next_paragraph(struct window_pane *);
-static void	window_copy_previous_paragraph(struct window_pane *);
+static void	window_copy_free(struct window_mode_entry *);
+static void	window_copy_resize(struct window_mode_entry *, u_int, u_int);
+static void	window_copy_formats(struct window_mode_entry *,
+		    struct format_tree *);
+static void	window_copy_pageup1(struct window_mode_entry *, int);
+static int	window_copy_pagedown(struct window_mode_entry *, int, int);
+static void	window_copy_next_paragraph(struct window_mode_entry *);
+static void	window_copy_previous_paragraph(struct window_mode_entry *);
 
-static void	window_copy_redraw_selection(struct window_pane *, u_int);
-static void	window_copy_redraw_lines(struct window_pane *, u_int, u_int);
-static void	window_copy_redraw_screen(struct window_pane *);
-static void	window_copy_write_line(struct window_pane *,
+static void	window_copy_redraw_selection(struct window_mode_entry *, u_int);
+static void	window_copy_redraw_lines(struct window_mode_entry *, u_int,
+		    u_int);
+static void	window_copy_redraw_screen(struct window_mode_entry *);
+static void	window_copy_write_line(struct window_mode_entry *,
 		    struct screen_write_ctx *, u_int);
-static void	window_copy_write_lines(struct window_pane *,
+static void	window_copy_write_lines(struct window_mode_entry *,
 		    struct screen_write_ctx *, u_int, u_int);
 
-static void	window_copy_scroll_to(struct window_pane *, u_int, u_int);
+static void	window_copy_scroll_to(struct window_mode_entry *, u_int, u_int);
 static int	window_copy_search_compare(struct grid *, u_int, u_int,
 		    struct grid *, u_int, int);
 static int	window_copy_search_lr(struct grid *, struct grid *, u_int *,
 		    u_int, u_int, u_int, int);
 static int	window_copy_search_rl(struct grid *, struct grid *, u_int *,
 		    u_int, u_int, u_int, int);
-static int	window_copy_search_marks(struct window_pane *, struct screen *);
-static void	window_copy_clear_marks(struct window_pane *);
+static int	window_copy_search_marks(struct window_mode_entry *,
+		    struct screen *);
+static void	window_copy_clear_marks(struct window_mode_entry *);
 static void	window_copy_move_left(struct screen *, u_int *, u_int *);
 static void	window_copy_move_right(struct screen *, u_int *, u_int *);
 static int	window_copy_is_lowercase(const char *);
-static int	window_copy_search_jump(struct window_pane *, struct grid *,
-		    struct grid *, u_int, u_int, u_int, int, int, int);
-static int	window_copy_search(struct window_pane *, int);
-static int	window_copy_search_up(struct window_pane *);
-static int	window_copy_search_down(struct window_pane *);
-static void	window_copy_goto_line(struct window_pane *, const char *);
-static void	window_copy_update_cursor(struct window_pane *, u_int, u_int);
-static void	window_copy_start_selection(struct window_pane *);
-static int	window_copy_adjust_selection(struct window_pane *, u_int *,
-		    u_int *);
-static int	window_copy_set_selection(struct window_pane *, int);
-static int	window_copy_update_selection(struct window_pane *, int);
-static void	window_copy_synchronize_cursor(struct window_pane *);
-static void    *window_copy_get_selection(struct window_pane *, size_t *);
-static void	window_copy_copy_buffer(struct window_pane *, void *, size_t);
-static void	window_copy_copy_pipe(struct window_pane *, struct session *,
+static int	window_copy_search_jump(struct window_mode_entry *,
+		    struct grid *, struct grid *, u_int, u_int, u_int, int, int,
+		    int);
+static int	window_copy_search(struct window_mode_entry *, int);
+static int	window_copy_search_up(struct window_mode_entry *);
+static int	window_copy_search_down(struct window_mode_entry *);
+static void	window_copy_goto_line(struct window_mode_entry *, const char *);
+static void	window_copy_update_cursor(struct window_mode_entry *, u_int,
+		    u_int);
+static void	window_copy_start_selection(struct window_mode_entry *);
+static int	window_copy_adjust_selection(struct window_mode_entry *,
+		    u_int *, u_int *);
+static int	window_copy_set_selection(struct window_mode_entry *, int);
+static int	window_copy_update_selection(struct window_mode_entry *, int);
+static void	window_copy_synchronize_cursor(struct window_mode_entry *);
+static void    *window_copy_get_selection(struct window_mode_entry *, size_t *);
+static void	window_copy_copy_buffer(struct window_mode_entry *, void *,
+		    size_t);
+static void	window_copy_copy_pipe(struct window_mode_entry *,
+		    struct session *, const char *);
+static void	window_copy_copy_selection(struct window_mode_entry *);
+static void	window_copy_append_selection(struct window_mode_entry *);
+static void	window_copy_clear_selection(struct window_mode_entry *);
+static void	window_copy_copy_line(struct window_mode_entry *, char **,
+		    size_t *, u_int, u_int, u_int);
+static int	window_copy_in_set(struct window_mode_entry *, u_int, u_int,
 		    const char *);
-static void	window_copy_copy_selection(struct window_pane *);
-static void	window_copy_append_selection(struct window_pane *);
-static void	window_copy_clear_selection(struct window_pane *);
-static void	window_copy_copy_line(struct window_pane *, char **, size_t *,
-		    u_int, u_int, u_int);
-static int	window_copy_in_set(struct window_pane *, u_int, u_int,
+static u_int	window_copy_find_length(struct window_mode_entry *, u_int);
+static void	window_copy_cursor_start_of_line(struct window_mode_entry *);
+static void	window_copy_cursor_back_to_indentation(
+		    struct window_mode_entry *);
+static void	window_copy_cursor_end_of_line(struct window_mode_entry *);
+static void	window_copy_other_end(struct window_mode_entry *);
+static void	window_copy_cursor_left(struct window_mode_entry *);
+static void	window_copy_cursor_right(struct window_mode_entry *);
+static void	window_copy_cursor_up(struct window_mode_entry *, int);
+static void	window_copy_cursor_down(struct window_mode_entry *, int);
+static void	window_copy_cursor_jump(struct window_mode_entry *);
+static void	window_copy_cursor_jump_back(struct window_mode_entry *);
+static void	window_copy_cursor_jump_to(struct window_mode_entry *);
+static void	window_copy_cursor_jump_to_back(struct window_mode_entry *);
+static void	window_copy_cursor_next_word(struct window_mode_entry *,
 		    const char *);
-static u_int	window_copy_find_length(struct window_pane *, u_int);
-static void	window_copy_cursor_start_of_line(struct window_pane *);
-static void	window_copy_cursor_back_to_indentation(struct window_pane *);
-static void	window_copy_cursor_end_of_line(struct window_pane *);
-static void	window_copy_other_end(struct window_pane *);
-static void	window_copy_cursor_left(struct window_pane *);
-static void	window_copy_cursor_right(struct window_pane *);
-static void	window_copy_cursor_up(struct window_pane *, int);
-static void	window_copy_cursor_down(struct window_pane *, int);
-static void	window_copy_cursor_jump(struct window_pane *);
-static void	window_copy_cursor_jump_back(struct window_pane *);
-static void	window_copy_cursor_jump_to(struct window_pane *);
-static void	window_copy_cursor_jump_to_back(struct window_pane *);
-static void	window_copy_cursor_next_word(struct window_pane *,
+static void	window_copy_cursor_next_word_end(struct window_mode_entry *,
 		    const char *);
-static void	window_copy_cursor_next_word_end(struct window_pane *,
+static void	window_copy_cursor_previous_word(struct window_mode_entry *,
 		    const char *);
-static void	window_copy_cursor_previous_word(struct window_pane *,
-		    const char *);
-static void	window_copy_scroll_up(struct window_pane *, u_int);
-static void	window_copy_scroll_down(struct window_pane *, u_int);
-static void	window_copy_rectangle_toggle(struct window_pane *);
+static void	window_copy_scroll_up(struct window_mode_entry *, u_int);
+static void	window_copy_scroll_down(struct window_mode_entry *, u_int);
+static void	window_copy_rectangle_toggle(struct window_mode_entry *);
 static void	window_copy_move_mouse(struct mouse_event *);
 static void	window_copy_drag_update(struct client *, struct mouse_event *);
 
@@ -196,13 +204,14 @@ struct window_copy_mode_data {
 };
 
 static struct screen *
-window_copy_init(struct window_pane *wp, __unused struct cmd_find_state *fs,
-    __unused struct args *args)
+window_copy_init(struct window_mode_entry *wme,
+    __unused struct cmd_find_state *fs, __unused struct args *args)
 {
+	struct window_pane		*wp = wme->wp;
 	struct window_copy_mode_data	*data;
 	struct screen			*s;
 
-	wp->modedata = data = xcalloc(1, sizeof *data);
+	wme->data = data = xcalloc(1, sizeof *data);
 
 	data->cursordrag = CURSORDRAG_NONE;
 	data->lineflag = LINE_SEL_NONE;
@@ -235,12 +244,13 @@ window_copy_init(struct window_pane *wp, __unused struct cmd_find_state *fs,
 void
 window_copy_init_from_pane(struct window_pane *wp, int scroll_exit)
 {
-	struct window_copy_mode_data	*data = wp->modedata;
+	struct window_mode_entry	*wme = wp->mode;
+	struct window_copy_mode_data	*data = wme->data;
 	struct screen			*s = &data->screen;
 	struct screen_write_ctx	 	 ctx;
 	u_int				 i;
 
-	if (wp->mode != &window_copy_mode)
+	if (wme == NULL || wme->mode != &window_copy_mode)
 		fatalx("not in copy mode");
 
 	data->backing = &wp->base;
@@ -253,7 +263,7 @@ window_copy_init_from_pane(struct window_pane *wp, int scroll_exit)
 
 	screen_write_start(&ctx, NULL, s);
 	for (i = 0; i < screen_size_y(s); i++)
-		window_copy_write_line(wp, &ctx, i);
+		window_copy_write_line(wme, &ctx, i);
 	screen_write_cursormove(&ctx, data->cx, data->cy);
 	screen_write_stop(&ctx);
 }
@@ -261,23 +271,28 @@ window_copy_init_from_pane(struct window_pane *wp, int scroll_exit)
 void
 window_copy_init_for_output(struct window_pane *wp)
 {
-	struct window_copy_mode_data	*data = wp->modedata;
+	struct window_copy_mode_data	*data;
 
-	if (wp->mode == &window_copy_mode && data->backing != &wp->base)
-		return;
+	if (wp->mode != NULL && wp->mode->mode == &window_copy_mode) {
+		data = wp->mode->data;
+		if (data->backing != &wp->base)
+			return;
+	}
 	window_pane_reset_mode(wp);
-	window_pane_set_mode(wp, &window_copy_mode, NULL, NULL);
 
-	data = wp->modedata;
+	window_pane_set_mode(wp, &window_copy_mode, NULL, NULL);
+	data = wp->mode->data;
+
 	data->backing = xmalloc(sizeof *data->backing);
 	screen_init(data->backing, screen_size_x(&wp->base),
 	    screen_size_y(&wp->base), UINT_MAX);
 }
 
 static void
-window_copy_free(struct window_pane *wp)
+window_copy_free(struct window_mode_entry *wme)
 {
-	struct window_copy_mode_data	*data = wp->modedata;
+	struct window_pane		*wp = wme->wp;
+	struct window_copy_mode_data	*data = wme->data;
 
 	if (wp->fd != -1)
 		bufferevent_enable(wp->event, EV_READ|EV_WRITE);
@@ -307,7 +322,8 @@ window_copy_add(struct window_pane *wp, const char *fmt, ...)
 void
 window_copy_vadd(struct window_pane *wp, const char *fmt, va_list ap)
 {
-	struct window_copy_mode_data	*data = wp->modedata;
+	struct window_mode_entry	*wme = wp->mode;
+	struct window_copy_mode_data	*data = wme->data;
 	struct screen			*backing = data->backing;
 	struct screen_write_ctx	 	 back_ctx, ctx;
 	struct grid_cell		 gc;
@@ -342,10 +358,10 @@ window_copy_vadd(struct window_pane *wp, const char *fmt, va_list ap)
 	 * (If there's any history at all, it has changed.)
 	 */
 	if (screen_hsize(data->backing))
-		window_copy_redraw_lines(wp, 0, 1);
+		window_copy_redraw_lines(wme, 0, 1);
 
 	/* Write the new lines. */
-	window_copy_redraw_lines(wp, old_cy, backing->cy - old_cy + 1);
+	window_copy_redraw_lines(wme, old_cy, backing->cy - old_cy + 1);
 
 	screen_write_stop(&ctx);
 }
@@ -353,12 +369,18 @@ window_copy_vadd(struct window_pane *wp, const char *fmt, va_list ap)
 void
 window_copy_pageup(struct window_pane *wp, int half_page)
 {
-	struct window_copy_mode_data	*data = wp->modedata;
+	window_copy_pageup1(wp->mode, half_page);
+}
+
+static void
+window_copy_pageup1(struct window_mode_entry *wme, int half_page)
+{
+	struct window_copy_mode_data	*data = wme->data;
 	struct screen			*s = &data->screen;
 	u_int				 n, ox, oy, px, py;
 
 	oy = screen_hsize(data->backing) + data->cy - data->oy;
-	ox = window_copy_find_length(wp, oy);
+	ox = window_copy_find_length(wme, oy);
 
 	if (data->cx != ox) {
 		data->lastcx = data->cx;
@@ -385,25 +407,26 @@ window_copy_pageup(struct window_pane *wp, int half_page)
 
 	if (data->screen.sel == NULL || !data->rectflag) {
 		py = screen_hsize(data->backing) + data->cy - data->oy;
-		px = window_copy_find_length(wp, py);
+		px = window_copy_find_length(wme, py);
 		if ((data->cx >= data->lastsx && data->cx != px) ||
 		    data->cx > px)
-			window_copy_cursor_end_of_line(wp);
+			window_copy_cursor_end_of_line(wme);
 	}
 
-	window_copy_update_selection(wp, 1);
-	window_copy_redraw_screen(wp);
+	window_copy_update_selection(wme, 1);
+	window_copy_redraw_screen(wme);
 }
 
 static int
-window_copy_pagedown(struct window_pane *wp, int half_page, int scroll_exit)
+window_copy_pagedown(struct window_mode_entry *wme, int half_page,
+    int scroll_exit)
 {
-	struct window_copy_mode_data	*data = wp->modedata;
+	struct window_copy_mode_data	*data = wme->data;
 	struct screen			*s = &data->screen;
 	u_int				 n, ox, oy, px, py;
 
 	oy = screen_hsize(data->backing) + data->cy - data->oy;
-	ox = window_copy_find_length(wp, oy);
+	ox = window_copy_find_length(wme, oy);
 
 	if (data->cx != ox) {
 		data->lastcx = data->cx;
@@ -430,60 +453,60 @@ window_copy_pagedown(struct window_pane *wp, int half_page, int scroll_exit)
 
 	if (data->screen.sel == NULL || !data->rectflag) {
 		py = screen_hsize(data->backing) + data->cy - data->oy;
-		px = window_copy_find_length(wp, py);
+		px = window_copy_find_length(wme, py);
 		if ((data->cx >= data->lastsx && data->cx != px) ||
 		    data->cx > px)
-			window_copy_cursor_end_of_line(wp);
+			window_copy_cursor_end_of_line(wme);
 	}
 
 	if (scroll_exit && data->oy == 0)
 		return (1);
-	window_copy_update_selection(wp, 1);
-	window_copy_redraw_screen(wp);
+	window_copy_update_selection(wme, 1);
+	window_copy_redraw_screen(wme);
 	return (0);
 }
 
 static void
-window_copy_previous_paragraph(struct window_pane *wp)
+window_copy_previous_paragraph(struct window_mode_entry *wme)
 {
-	struct window_copy_mode_data	*data = wp->modedata;
+	struct window_copy_mode_data	*data = wme->data;
 	u_int				 oy;
 
 	oy = screen_hsize(data->backing) + data->cy - data->oy;
 
-	while (oy > 0 && window_copy_find_length(wp, oy) == 0)
+	while (oy > 0 && window_copy_find_length(wme, oy) == 0)
 		oy--;
 
-	while (oy > 0 && window_copy_find_length(wp, oy) > 0)
+	while (oy > 0 && window_copy_find_length(wme, oy) > 0)
 		oy--;
 
-	window_copy_scroll_to(wp, 0, oy);
+	window_copy_scroll_to(wme, 0, oy);
 }
 
 static void
-window_copy_next_paragraph(struct window_pane *wp)
+window_copy_next_paragraph(struct window_mode_entry *wme)
 {
-	struct window_copy_mode_data	*data = wp->modedata;
+	struct window_copy_mode_data	*data = wme->data;
 	struct screen			*s = &data->screen;
 	u_int				 maxy, ox, oy;
 
 	oy = screen_hsize(data->backing) + data->cy - data->oy;
 	maxy = screen_hsize(data->backing) + screen_size_y(s) - 1;
 
-	while (oy < maxy && window_copy_find_length(wp, oy) == 0)
+	while (oy < maxy && window_copy_find_length(wme, oy) == 0)
 		oy++;
 
-	while (oy < maxy && window_copy_find_length(wp, oy) > 0)
+	while (oy < maxy && window_copy_find_length(wme, oy) > 0)
 		oy++;
 
-	ox = window_copy_find_length(wp, oy);
-	window_copy_scroll_to(wp, ox, oy);
+	ox = window_copy_find_length(wme, oy);
+	window_copy_scroll_to(wme, ox, oy);
 }
 
 static void
-window_copy_formats(struct window_pane *wp, struct format_tree *ft)
+window_copy_formats(struct window_mode_entry *wme, struct format_tree *ft)
 {
-	struct window_copy_mode_data	*data = wp->modedata;
+	struct window_copy_mode_data	*data = wme->data;
 
 	format_add(ft, "selection_present", "%d", data->screen.sel != NULL);
 	format_add(ft, "scroll_position", "%d", data->oy);
@@ -491,9 +514,10 @@ window_copy_formats(struct window_pane *wp, struct format_tree *ft)
 }
 
 static void
-window_copy_resize(struct window_pane *wp, u_int sx, u_int sy)
+window_copy_resize(struct window_mode_entry *wme, u_int sx, u_int sy)
 {
-	struct window_copy_mode_data	*data = wp->modedata;
+	struct window_pane		*wp = wme->wp;
+	struct window_copy_mode_data	*data = wme->data;
 	struct screen			*s = &data->screen;
 	struct screen_write_ctx	 	 ctx;
 
@@ -508,37 +532,41 @@ window_copy_resize(struct window_pane *wp, u_int sx, u_int sy)
 	if (data->oy > screen_hsize(data->backing))
 		data->oy = screen_hsize(data->backing);
 
-	window_copy_clear_selection(wp);
+	window_copy_clear_selection(wme);
 
 	screen_write_start(&ctx, NULL, s);
-	window_copy_write_lines(wp, &ctx, 0, screen_size_y(s) - 1);
+	window_copy_write_lines(wme, &ctx, 0, screen_size_y(s) - 1);
 	screen_write_stop(&ctx);
 
 	if (data->searchmark != NULL)
-		window_copy_search_marks(wp, NULL);
+		window_copy_search_marks(wme, NULL);
 	data->searchx = data->cx;
 	data->searchy = data->cy;
 	data->searcho = data->oy;
 
-	window_copy_redraw_screen(wp);
+	window_copy_redraw_screen(wme);
 }
 
 static const char *
-window_copy_key_table(struct window_pane *wp)
+window_copy_key_table(struct window_mode_entry *wme)
 {
+	struct window_pane	*wp = wme->wp;
+
 	if (options_get_number(wp->window->options, "mode-keys") == MODEKEY_VI)
 		return ("copy-mode-vi");
 	return ("copy-mode");
 }
 
 static void
-window_copy_command(struct window_pane *wp, struct client *c, struct session *s,
-    __unused struct winlink *wl, struct args *args, struct mouse_event *m)
+window_copy_command(struct window_mode_entry *wme, struct client *c,
+    struct session *s, __unused struct winlink *wl, struct args *args,
+    struct mouse_event *m)
 {
-	struct window_copy_mode_data	*data = wp->modedata;
+	struct window_pane		*wp = wme->wp;
+	struct window_copy_mode_data	*data = wme->data;
 	struct screen			*sn = &data->screen;
 	const char			*command, *argument, *ws;
-	u_int				 np = wp->modeprefix;
+	u_int				 np = wme->prefix;
 	int				 cancel = 0, redraw = 0, scroll_exit;
 	char				 prefix;
 
@@ -552,25 +580,25 @@ window_copy_command(struct window_pane *wp, struct client *c, struct session *s,
 	if (args->argc == 1) {
 		if (strcmp(command, "append-selection") == 0) {
 			if (s != NULL)
-				window_copy_append_selection(wp);
-			window_copy_clear_selection(wp);
+				window_copy_append_selection(wme);
+			window_copy_clear_selection(wme);
 			redraw = 1;
 		}
 		if (strcmp(command, "append-selection-and-cancel") == 0) {
 			if (s != NULL)
-				window_copy_append_selection(wp);
-			window_copy_clear_selection(wp);
+				window_copy_append_selection(wme);
+			window_copy_clear_selection(wme);
 			redraw = 1;
 			cancel = 1;
 		}
 		if (strcmp(command, "back-to-indentation") == 0)
-			window_copy_cursor_back_to_indentation(wp);
+			window_copy_cursor_back_to_indentation(wme);
 		if (strcmp(command, "begin-selection") == 0) {
 			if (m != NULL)
 				window_copy_start_drag(c, m);
 			else {
 				data->lineflag = LINE_SEL_NONE;
-				window_copy_start_selection(wp);
+				window_copy_start_selection(wme);
 				redraw = 1;
 			}
 		}
@@ -579,71 +607,71 @@ window_copy_command(struct window_pane *wp, struct client *c, struct session *s,
 		if (strcmp(command, "bottom-line") == 0) {
 			data->cx = 0;
 			data->cy = screen_size_y(sn) - 1;
-			window_copy_update_selection(wp, 1);
+			window_copy_update_selection(wme, 1);
 			redraw = 1;
 		}
 		if (strcmp(command, "cancel") == 0)
 			cancel = 1;
 		if (strcmp(command, "clear-selection") == 0) {
-			window_copy_clear_selection(wp);
+			window_copy_clear_selection(wme);
 			redraw = 1;
 		}
 		if (strcmp(command, "copy-end-of-line") == 0) {
-			window_copy_start_selection(wp);
+			window_copy_start_selection(wme);
 			for (; np > 1; np--)
-				window_copy_cursor_down(wp, 0);
-			window_copy_cursor_end_of_line(wp);
+				window_copy_cursor_down(wme, 0);
+			window_copy_cursor_end_of_line(wme);
 			redraw = 1;
 
 			if (s != NULL) {
-				window_copy_copy_selection(wp);
+				window_copy_copy_selection(wme);
 				cancel = 1;
 			}
 		}
 		if (strcmp(command, "copy-line") == 0) {
-			window_copy_cursor_start_of_line(wp);
-			window_copy_start_selection(wp);
+			window_copy_cursor_start_of_line(wme);
+			window_copy_start_selection(wme);
 			for (; np > 1; np--)
-				window_copy_cursor_down(wp, 0);
-			window_copy_cursor_end_of_line(wp);
+				window_copy_cursor_down(wme, 0);
+			window_copy_cursor_end_of_line(wme);
 			redraw = 1;
 
 			if (s != NULL) {
-				window_copy_copy_selection(wp);
+				window_copy_copy_selection(wme);
 				cancel = 1;
 			}
 		}
 		if (strcmp(command, "copy-selection") == 0) {
 			if (s != NULL)
-				window_copy_copy_selection(wp);
-			window_copy_clear_selection(wp);
+				window_copy_copy_selection(wme);
+			window_copy_clear_selection(wme);
 			redraw = 1;
 		}
 		if (strcmp(command, "copy-selection-and-cancel") == 0) {
 			if (s != NULL)
-				window_copy_copy_selection(wp);
-			window_copy_clear_selection(wp);
+				window_copy_copy_selection(wme);
+			window_copy_clear_selection(wme);
 			redraw = 1;
 			cancel = 1;
 		}
 		if (strcmp(command, "cursor-down") == 0) {
 			for (; np != 0; np--)
-				window_copy_cursor_down(wp, 0);
+				window_copy_cursor_down(wme, 0);
 		}
 		if (strcmp(command, "cursor-left") == 0) {
 			for (; np != 0; np--)
-				window_copy_cursor_left(wp);
+				window_copy_cursor_left(wme);
 		}
 		if (strcmp(command, "cursor-right") == 0) {
 			for (; np != 0; np--)
-				window_copy_cursor_right(wp);
+				window_copy_cursor_right(wme);
 		}
 		if (strcmp(command, "cursor-up") == 0) {
 			for (; np != 0; np--)
-				window_copy_cursor_up(wp, 0);
+				window_copy_cursor_up(wme, 0);
 		}
 		if (strcmp(command, "end-of-line") == 0)
-			window_copy_cursor_end_of_line(wp);
+			window_copy_cursor_end_of_line(wme);
 		if (strcmp(command, "halfpage-down") == 0 ||
 		    strcmp(command, "halfpage-down-and-cancel") == 0) {
 			if (strcmp(command, "halfpage-down-and-cancel") == 0)
@@ -651,7 +679,7 @@ window_copy_command(struct window_pane *wp, struct client *c, struct session *s,
 			else
 				scroll_exit = data->scroll_exit;
 			for (; np != 0; np--) {
-				if (window_copy_pagedown(wp, 1, scroll_exit)) {
+				if (window_copy_pagedown(wme, 1, scroll_exit)) {
 					cancel = 1;
 					break;
 				}
@@ -659,39 +687,39 @@ window_copy_command(struct window_pane *wp, struct client *c, struct session *s,
 		}
 		if (strcmp(command, "halfpage-up") == 0) {
 			for (; np != 0; np--)
-				window_copy_pageup(wp, 1);
+				window_copy_pageup1(wme, 1);
 		}
 		if (strcmp(command, "history-bottom") == 0) {
 			data->cx = 0;
 			data->cy = screen_size_y(sn) - 1;
 			data->oy = 0;
-			window_copy_update_selection(wp, 1);
+			window_copy_update_selection(wme, 1);
 			redraw = 1;
 		}
 		if (strcmp(command, "history-top") == 0) {
 			data->cx = 0;
 			data->cy = 0;
 			data->oy = screen_hsize(data->backing);
-			window_copy_update_selection(wp, 1);
+			window_copy_update_selection(wme, 1);
 			redraw = 1;
 		}
 		if (strcmp(command, "jump-again") == 0) {
 			switch (data->jumptype) {
 			case WINDOW_COPY_JUMPFORWARD:
 				for (; np != 0; np--)
-					window_copy_cursor_jump(wp);
+					window_copy_cursor_jump(wme);
 				break;
 			case WINDOW_COPY_JUMPBACKWARD:
 				for (; np != 0; np--)
-					window_copy_cursor_jump_back(wp);
+					window_copy_cursor_jump_back(wme);
 				break;
 			case WINDOW_COPY_JUMPTOFORWARD:
 				for (; np != 0; np--)
-					window_copy_cursor_jump_to(wp);
+					window_copy_cursor_jump_to(wme);
 				break;
 			case WINDOW_COPY_JUMPTOBACKWARD:
 				for (; np != 0; np--)
-					window_copy_cursor_jump_to_back(wp);
+					window_copy_cursor_jump_to_back(wme);
 				break;
 			}
 		}
@@ -699,53 +727,53 @@ window_copy_command(struct window_pane *wp, struct client *c, struct session *s,
 			switch (data->jumptype) {
 			case WINDOW_COPY_JUMPFORWARD:
 				for (; np != 0; np--)
-					window_copy_cursor_jump_back(wp);
+					window_copy_cursor_jump_back(wme);
 				break;
 			case WINDOW_COPY_JUMPBACKWARD:
 				for (; np != 0; np--)
-					window_copy_cursor_jump(wp);
+					window_copy_cursor_jump(wme);
 				break;
 			case WINDOW_COPY_JUMPTOFORWARD:
 				for (; np != 0; np--)
-					window_copy_cursor_jump_to_back(wp);
+					window_copy_cursor_jump_to_back(wme);
 				break;
 			case WINDOW_COPY_JUMPTOBACKWARD:
 				for (; np != 0; np--)
-					window_copy_cursor_jump_to(wp);
+					window_copy_cursor_jump_to(wme);
 				break;
 			}
 		}
 		if (strcmp(command, "middle-line") == 0) {
 			data->cx = 0;
 			data->cy = (screen_size_y(sn) - 1) / 2;
-			window_copy_update_selection(wp, 1);
+			window_copy_update_selection(wme, 1);
 			redraw = 1;
 		}
 		if (strcmp(command, "next-paragraph") == 0) {
 			for (; np != 0; np--)
-				window_copy_next_paragraph(wp);
+				window_copy_next_paragraph(wme);
 		}
 		if (strcmp(command, "next-space") == 0) {
 			for (; np != 0; np--)
-				window_copy_cursor_next_word(wp, " ");
+				window_copy_cursor_next_word(wme, " ");
 		}
 		if (strcmp(command, "next-space-end") == 0) {
 			for (; np != 0; np--)
-				window_copy_cursor_next_word_end(wp, " ");
+				window_copy_cursor_next_word_end(wme, " ");
 		}
 		if (strcmp(command, "next-word") == 0) {
 			ws = options_get_string(s->options, "word-separators");
 			for (; np != 0; np--)
-				window_copy_cursor_next_word(wp, ws);
+				window_copy_cursor_next_word(wme, ws);
 		}
 		if (strcmp(command, "next-word-end") == 0) {
 			ws = options_get_string(s->options, "word-separators");
 			for (; np != 0; np--)
-				window_copy_cursor_next_word_end(wp, ws);
+				window_copy_cursor_next_word_end(wme, ws);
 		}
 		if (strcmp(command, "other-end") == 0) {
 			if ((np % 2) != 0)
-				window_copy_other_end(wp);
+				window_copy_other_end(wme);
 		}
 		if (strcmp(command, "page-down") == 0 ||
 		    strcmp(command, "page-down-and-cancel") == 0) {
@@ -754,7 +782,7 @@ window_copy_command(struct window_pane *wp, struct client *c, struct session *s,
 			else
 				scroll_exit = data->scroll_exit;
 			for (; np != 0; np--) {
-				if (window_copy_pagedown(wp, 0, scroll_exit)) {
+				if (window_copy_pagedown(wme, 0, scroll_exit)) {
 					cancel = 1;
 					break;
 				}
@@ -762,24 +790,24 @@ window_copy_command(struct window_pane *wp, struct client *c, struct session *s,
 		}
 		if (strcmp(command, "page-up") == 0) {
 			for (; np != 0; np--)
-				window_copy_pageup(wp, 0);
+				window_copy_pageup1(wme, 0);
 		}
 		if (strcmp(command, "previous-paragraph") == 0) {
 			for (; np != 0; np--)
-				window_copy_previous_paragraph(wp);
+				window_copy_previous_paragraph(wme);
 		}
 		if (strcmp(command, "previous-space") == 0) {
 			for (; np != 0; np--)
-				window_copy_cursor_previous_word(wp, " ");
+				window_copy_cursor_previous_word(wme, " ");
 		}
 		if (strcmp(command, "previous-word") == 0) {
 			ws = options_get_string(s->options, "word-separators");
 			for (; np != 0; np--)
-				window_copy_cursor_previous_word(wp, ws);
+				window_copy_cursor_previous_word(wme, ws);
 		}
 		if (strcmp(command, "rectangle-toggle") == 0) {
 			data->lineflag = LINE_SEL_NONE;
-			window_copy_rectangle_toggle(wp);
+			window_copy_rectangle_toggle(wme);
 		}
 		if (strcmp(command, "scroll-down") == 0 ||
 		    strcmp(command, "scroll-down-and-cancel") == 0) {
@@ -788,110 +816,110 @@ window_copy_command(struct window_pane *wp, struct client *c, struct session *s,
 			else
 				scroll_exit = data->scroll_exit;
 			for (; np != 0; np--)
-				window_copy_cursor_down(wp, 1);
+				window_copy_cursor_down(wme, 1);
 			if (scroll_exit && data->oy == 0)
 				cancel = 1;
 		}
 		if (strcmp(command, "scroll-up") == 0) {
 			for (; np != 0; np--)
-				window_copy_cursor_up(wp, 1);
+				window_copy_cursor_up(wme, 1);
 		}
 		if (strcmp(command, "search-again") == 0) {
 			if (data->searchtype == WINDOW_COPY_SEARCHUP) {
 				for (; np != 0; np--)
-					window_copy_search_up(wp);
+					window_copy_search_up(wme);
 			} else if (data->searchtype == WINDOW_COPY_SEARCHDOWN) {
 				for (; np != 0; np--)
-					window_copy_search_down(wp);
+					window_copy_search_down(wme);
 			}
 		}
 		if (strcmp(command, "search-reverse") == 0) {
 			if (data->searchtype == WINDOW_COPY_SEARCHUP) {
 				for (; np != 0; np--)
-					window_copy_search_down(wp);
+					window_copy_search_down(wme);
 			} else if (data->searchtype == WINDOW_COPY_SEARCHDOWN) {
 				for (; np != 0; np--)
-					window_copy_search_up(wp);
+					window_copy_search_up(wme);
 			}
 		}
 		if (strcmp(command, "select-line") == 0) {
 			data->lineflag = LINE_SEL_LEFT_RIGHT;
 			data->rectflag = 0;
-			window_copy_cursor_start_of_line(wp);
-			window_copy_start_selection(wp);
+			window_copy_cursor_start_of_line(wme);
+			window_copy_start_selection(wme);
 			for (; np > 1; np--)
-				window_copy_cursor_down(wp, 0);
-			window_copy_cursor_end_of_line(wp);
+				window_copy_cursor_down(wme, 0);
+			window_copy_cursor_end_of_line(wme);
 			redraw = 1;
 		}
 		if (strcmp(command, "select-word") == 0) {
 			data->lineflag = LINE_SEL_LEFT_RIGHT;
 			data->rectflag = 0;
 			ws = options_get_string(s->options, "word-separators");
-			window_copy_cursor_previous_word(wp, ws);
-			window_copy_start_selection(wp);
-			window_copy_cursor_next_word_end(wp, ws);
+			window_copy_cursor_previous_word(wme, ws);
+			window_copy_start_selection(wme);
+			window_copy_cursor_next_word_end(wme, ws);
 			redraw = 1;
 		}
 		if (strcmp(command, "start-of-line") == 0)
-			window_copy_cursor_start_of_line(wp);
+			window_copy_cursor_start_of_line(wme);
 		if (strcmp(command, "top-line") == 0) {
 			data->cx = 0;
 			data->cy = 0;
-			window_copy_update_selection(wp, 1);
+			window_copy_update_selection(wme, 1);
 			redraw = 1;
 		}
 	} else if (args->argc == 2 && *args->argv[1] != '\0') {
 		argument = args->argv[1];
 		if (strcmp(command, "copy-pipe") == 0) {
 			if (s != NULL)
-				window_copy_copy_pipe(wp, s, argument);
+				window_copy_copy_pipe(wme, s, argument);
 		}
 		if (strcmp(command, "copy-pipe-and-cancel") == 0) {
 			if (s != NULL) {
-				window_copy_copy_pipe(wp, s, argument);
+				window_copy_copy_pipe(wme, s, argument);
 				cancel = 1;
 			}
 		}
 		if (strcmp(command, "goto-line") == 0)
-			window_copy_goto_line(wp, argument);
+			window_copy_goto_line(wme, argument);
 		if (strcmp(command, "jump-backward") == 0) {
 			data->jumptype = WINDOW_COPY_JUMPBACKWARD;
 			data->jumpchar = *argument;
 			for (; np != 0; np--)
-				window_copy_cursor_jump_back(wp);
+				window_copy_cursor_jump_back(wme);
 		}
 		if (strcmp(command, "jump-forward") == 0) {
 			data->jumptype = WINDOW_COPY_JUMPFORWARD;
 			data->jumpchar = *argument;
 			for (; np != 0; np--)
-				window_copy_cursor_jump(wp);
+				window_copy_cursor_jump(wme);
 		}
 		if (strcmp(command, "jump-to-backward") == 0) {
 			data->jumptype = WINDOW_COPY_JUMPTOBACKWARD;
 			data->jumpchar = *argument;
 			for (; np != 0; np--)
-				window_copy_cursor_jump_to_back(wp);
+				window_copy_cursor_jump_to_back(wme);
 		}
 		if (strcmp(command, "jump-to-forward") == 0) {
 			data->jumptype = WINDOW_COPY_JUMPTOFORWARD;
 			data->jumpchar = *argument;
 			for (; np != 0; np--)
-				window_copy_cursor_jump_to(wp);
+				window_copy_cursor_jump_to(wme);
 		}
 		if (strcmp(command, "search-backward") == 0) {
 			data->searchtype = WINDOW_COPY_SEARCHUP;
 			free(data->searchstr);
 			data->searchstr = xstrdup(argument);
 			for (; np != 0; np--)
-				window_copy_search_up(wp);
+				window_copy_search_up(wme);
 		}
 		if (strcmp(command, "search-forward") == 0) {
 			data->searchtype = WINDOW_COPY_SEARCHDOWN;
 			free(data->searchstr);
 			data->searchstr = xstrdup(argument);
 			for (; np != 0; np--)
-				window_copy_search_down(wp);
+				window_copy_search_down(wme);
 		}
 		if (strcmp(command, "search-backward-incremental") == 0) {
 			prefix = *argument++;
@@ -907,22 +935,22 @@ window_copy_command(struct window_pane *wp, struct client *c, struct session *s,
 				redraw = 1;
 			}
 			if (*argument == '\0') {
-				window_copy_clear_marks(wp);
+				window_copy_clear_marks(wme);
 				redraw = 1;
 			} else if (prefix == '=' || prefix == '-') {
 				data->searchtype = WINDOW_COPY_SEARCHUP;
 				free(data->searchstr);
 				data->searchstr = xstrdup(argument);
-				if (!window_copy_search_up(wp)) {
-					window_copy_clear_marks(wp);
+				if (!window_copy_search_up(wme)) {
+					window_copy_clear_marks(wme);
 					redraw = 1;
 				}
 			} else if (prefix == '+') {
 				data->searchtype = WINDOW_COPY_SEARCHDOWN;
 				free(data->searchstr);
 				data->searchstr = xstrdup(argument);
-				if (!window_copy_search_down(wp)) {
-					window_copy_clear_marks(wp);
+				if (!window_copy_search_down(wme)) {
+					window_copy_clear_marks(wme);
 					redraw = 1;
 				}
 			}
@@ -941,22 +969,22 @@ window_copy_command(struct window_pane *wp, struct client *c, struct session *s,
 				redraw = 1;
 			}
 			if (*argument == '\0') {
-				window_copy_clear_marks(wp);
+				window_copy_clear_marks(wme);
 				redraw = 1;
 			} else if (prefix == '=' || prefix == '+') {
 				data->searchtype = WINDOW_COPY_SEARCHDOWN;
 				free(data->searchstr);
 				data->searchstr = xstrdup(argument);
-				if (!window_copy_search_down(wp)) {
-					window_copy_clear_marks(wp);
+				if (!window_copy_search_down(wme)) {
+					window_copy_clear_marks(wme);
 					redraw = 1;
 				}
 			} else if (prefix == '-') {
 				data->searchtype = WINDOW_COPY_SEARCHUP;
 				free(data->searchstr);
 				data->searchstr = xstrdup(argument);
-				if (!window_copy_search_up(wp)) {
-					window_copy_clear_marks(wp);
+				if (!window_copy_search_up(wme)) {
+					window_copy_clear_marks(wme);
 					redraw = 1;
 				}
 			}
@@ -964,7 +992,7 @@ window_copy_command(struct window_pane *wp, struct client *c, struct session *s,
 	}
 
 	if (strncmp(command, "search-", 7) != 0 && data->searchmark != NULL) {
-		window_copy_clear_marks(wp);
+		window_copy_clear_marks(wme);
 		redraw = 1;
 		data->searchx = data->searchy = -1;
 	}
@@ -972,14 +1000,14 @@ window_copy_command(struct window_pane *wp, struct client *c, struct session *s,
 	if (cancel)
 		window_pane_reset_mode(wp);
 	else if (redraw)
-		window_copy_redraw_screen(wp);
-	wp->modeprefix = 1;
+		window_copy_redraw_screen(wme);
+	wme->prefix = 1;
 }
 
 static void
-window_copy_scroll_to(struct window_pane *wp, u_int px, u_int py)
+window_copy_scroll_to(struct window_mode_entry *wme, u_int px, u_int py)
 {
-	struct window_copy_mode_data	*data = wp->modedata;
+	struct window_copy_mode_data	*data = wme->data;
 	struct grid			*gd = data->backing->grid;
 	u_int				 offset, gap;
 
@@ -1002,8 +1030,8 @@ window_copy_scroll_to(struct window_pane *wp, u_int px, u_int py)
 		data->oy = gd->hsize - offset;
 	}
 
-	window_copy_update_selection(wp, 1);
-	window_copy_redraw_screen(wp);
+	window_copy_update_selection(wme, 1);
+	window_copy_redraw_screen(wme);
 }
 
 static int
@@ -1119,7 +1147,7 @@ window_copy_is_lowercase(const char *ptr)
  * not found.
  */
 static int
-window_copy_search_jump(struct window_pane *wp, struct grid *gd,
+window_copy_search_jump(struct window_mode_entry *wme, struct grid *gd,
     struct grid *sgd, u_int fx, u_int fy, u_int endline, int cis, int wrap,
     int direction)
 {
@@ -1148,11 +1176,11 @@ window_copy_search_jump(struct window_pane *wp, struct grid *gd,
 	}
 
 	if (found) {
-		window_copy_scroll_to(wp, px, i);
+		window_copy_scroll_to(wme, px, i);
 		return (1);
 	}
 	if (wrap) {
-		return (window_copy_search_jump(wp, gd, sgd,
+		return (window_copy_search_jump(wme, gd, sgd,
 		    direction ? 0 : gd->sx - 1,
 		    direction ? 0 : gd->hsize + gd->sy - 1, fy, cis, 0,
 		    direction));
@@ -1165,9 +1193,10 @@ window_copy_search_jump(struct window_pane *wp, struct grid *gd,
  * down.
  */
 static int
-window_copy_search(struct window_pane *wp, int direction)
+window_copy_search(struct window_mode_entry *wme, int direction)
 {
-	struct window_copy_mode_data	*data = wp->modedata;
+	struct window_pane		*wp = wme->wp;
+	struct window_copy_mode_data	*data = wme->data;
 	struct screen			*s = data->backing, ss;
 	struct screen_write_ctx		 ctx;
 	struct grid			*gd = s->grid;
@@ -1197,20 +1226,20 @@ window_copy_search(struct window_pane *wp, int direction)
 		endline = gd->hsize + gd->sy - 1;
 	else
 		endline = 0;
-	found = window_copy_search_jump(wp, gd, ss.grid, fx, fy, endline, cis,
+	found = window_copy_search_jump(wme, gd, ss.grid, fx, fy, endline, cis,
 	    wrapflag, direction);
 
-	if (window_copy_search_marks(wp, &ss))
-		window_copy_redraw_screen(wp);
+	if (window_copy_search_marks(wme, &ss))
+		window_copy_redraw_screen(wme);
 
 	screen_free(&ss);
 	return (found);
 }
 
 static int
-window_copy_search_marks(struct window_pane *wp, struct screen *ssp)
+window_copy_search_marks(struct window_mode_entry *wme, struct screen *ssp)
 {
-	struct window_copy_mode_data	*data = wp->modedata;
+	struct window_copy_mode_data	*data = wme->data;
 	struct screen			*s = data->backing, ss;
 	struct screen_write_ctx		 ctx;
 	struct grid			*gd = s->grid;
@@ -1264,30 +1293,30 @@ window_copy_search_marks(struct window_pane *wp, struct screen *ssp)
 }
 
 static void
-window_copy_clear_marks(struct window_pane *wp)
+window_copy_clear_marks(struct window_mode_entry *wme)
 {
-	struct window_copy_mode_data	*data = wp->modedata;
+	struct window_copy_mode_data	*data = wme->data;
 
 	free(data->searchmark);
 	data->searchmark = NULL;
 }
 
 static int
-window_copy_search_up(struct window_pane *wp)
+window_copy_search_up(struct window_mode_entry *wme)
 {
-	return (window_copy_search(wp, 0));
+	return (window_copy_search(wme, 0));
 }
 
 static int
-window_copy_search_down(struct window_pane *wp)
+window_copy_search_down(struct window_mode_entry *wme)
 {
-	return (window_copy_search(wp, 1));
+	return (window_copy_search(wme, 1));
 }
 
 static void
-window_copy_goto_line(struct window_pane *wp, const char *linestr)
+window_copy_goto_line(struct window_mode_entry *wme, const char *linestr)
 {
-	struct window_copy_mode_data	*data = wp->modedata;
+	struct window_copy_mode_data	*data = wme->data;
 	const char			*errstr;
 	int				 lineno;
 
@@ -1298,15 +1327,16 @@ window_copy_goto_line(struct window_pane *wp, const char *linestr)
 		lineno = screen_hsize(data->backing);
 
 	data->oy = lineno;
-	window_copy_update_selection(wp, 1);
-	window_copy_redraw_screen(wp);
+	window_copy_update_selection(wme, 1);
+	window_copy_redraw_screen(wme);
 }
 
 static void
-window_copy_write_line(struct window_pane *wp, struct screen_write_ctx *ctx,
-    u_int py)
+window_copy_write_line(struct window_mode_entry *wme,
+    struct screen_write_ctx *ctx, u_int py)
 {
-	struct window_copy_mode_data	*data = wp->modedata;
+	struct window_pane		*wp = wme->wp;
+	struct window_copy_mode_data	*data = wme->data;
 	struct screen			*s = &data->screen;
 	struct options			*oo = wp->window->options;
 	struct grid_cell		 gc;
@@ -1354,19 +1384,19 @@ window_copy_write_line(struct window_pane *wp, struct screen_write_ctx *ctx,
 }
 
 static void
-window_copy_write_lines(struct window_pane *wp, struct screen_write_ctx *ctx,
-    u_int py, u_int ny)
+window_copy_write_lines(struct window_mode_entry *wme,
+    struct screen_write_ctx *ctx, u_int py, u_int ny)
 {
 	u_int	yy;
 
 	for (yy = py; yy < py + ny; yy++)
-		window_copy_write_line(wp, ctx, py);
+		window_copy_write_line(wme, ctx, py);
 }
 
 static void
-window_copy_redraw_selection(struct window_pane *wp, u_int old_y)
+window_copy_redraw_selection(struct window_mode_entry *wme, u_int old_y)
 {
-	struct window_copy_mode_data	*data = wp->modedata;
+	struct window_copy_mode_data	*data = wme->data;
 	u_int				 new_y, start, end;
 
 	new_y = data->cy;
@@ -1377,35 +1407,36 @@ window_copy_redraw_selection(struct window_pane *wp, u_int old_y)
 		start = new_y;
 		end = old_y;
 	}
-	window_copy_redraw_lines(wp, start, end - start + 1);
+	window_copy_redraw_lines(wme, start, end - start + 1);
 }
 
 static void
-window_copy_redraw_lines(struct window_pane *wp, u_int py, u_int ny)
+window_copy_redraw_lines(struct window_mode_entry *wme, u_int py, u_int ny)
 {
-	struct window_copy_mode_data	*data = wp->modedata;
+	struct window_pane		*wp = wme->wp;
+	struct window_copy_mode_data	*data = wme->data;
 	struct screen_write_ctx	 	 ctx;
 	u_int				 i;
 
 	screen_write_start(&ctx, wp, NULL);
 	for (i = py; i < py + ny; i++)
-		window_copy_write_line(wp, &ctx, i);
+		window_copy_write_line(wme, &ctx, i);
 	screen_write_cursormove(&ctx, data->cx, data->cy);
 	screen_write_stop(&ctx);
 }
 
 static void
-window_copy_redraw_screen(struct window_pane *wp)
+window_copy_redraw_screen(struct window_mode_entry *wme)
 {
-	struct window_copy_mode_data	*data = wp->modedata;
+	struct window_copy_mode_data	*data = wme->data;
 
-	window_copy_redraw_lines(wp, 0, screen_size_y(&data->screen));
+	window_copy_redraw_lines(wme, 0, screen_size_y(&data->screen));
 }
 
 static void
-window_copy_synchronize_cursor(struct window_pane *wp)
+window_copy_synchronize_cursor(struct window_mode_entry *wme)
 {
-	struct window_copy_mode_data	*data = wp->modedata;
+	struct window_copy_mode_data	*data = wme->data;
 	u_int				 xx, yy;
 
 	xx = data->cx;
@@ -1426,9 +1457,10 @@ window_copy_synchronize_cursor(struct window_pane *wp)
 }
 
 static void
-window_copy_update_cursor(struct window_pane *wp, u_int cx, u_int cy)
+window_copy_update_cursor(struct window_mode_entry *wme, u_int cx, u_int cy)
 {
-	struct window_copy_mode_data	*data = wp->modedata;
+	struct window_pane		*wp = wme->wp;
+	struct window_copy_mode_data	*data = wme->data;
 	struct screen			*s = &data->screen;
 	struct screen_write_ctx		 ctx;
 	u_int				 old_cx, old_cy;
@@ -1436,9 +1468,9 @@ window_copy_update_cursor(struct window_pane *wp, u_int cx, u_int cy)
 	old_cx = data->cx; old_cy = data->cy;
 	data->cx = cx; data->cy = cy;
 	if (old_cx == screen_size_x(s))
-		window_copy_redraw_lines(wp, old_cy, 1);
+		window_copy_redraw_lines(wme, old_cy, 1);
 	if (data->cx == screen_size_x(s))
-		window_copy_redraw_lines(wp, data->cy, 1);
+		window_copy_redraw_lines(wme, data->cy, 1);
 	else {
 		screen_write_start(&ctx, wp, NULL);
 		screen_write_cursormove(&ctx, data->cx, data->cy);
@@ -1447,9 +1479,9 @@ window_copy_update_cursor(struct window_pane *wp, u_int cx, u_int cy)
 }
 
 static void
-window_copy_start_selection(struct window_pane *wp)
+window_copy_start_selection(struct window_mode_entry *wme)
 {
-	struct window_copy_mode_data	*data = wp->modedata;
+	struct window_copy_mode_data	*data = wme->data;
 
 	data->selx = data->cx;
 	data->sely = screen_hsize(data->backing) + data->cy - data->oy;
@@ -1459,13 +1491,14 @@ window_copy_start_selection(struct window_pane *wp)
 
 	data->cursordrag = CURSORDRAG_ENDSEL;
 
-	window_copy_set_selection(wp, 1);
+	window_copy_set_selection(wme, 1);
 }
 
 static int
-window_copy_adjust_selection(struct window_pane *wp, u_int *selx, u_int *sely)
+window_copy_adjust_selection(struct window_mode_entry *wme, u_int *selx,
+    u_int *sely)
 {
-	struct window_copy_mode_data	*data = wp->modedata;
+	struct window_copy_mode_data	*data = wme->data;
 	struct screen			*s = &data->screen;
 	u_int 				 sx, sy, ty;
 	int				 relpos;
@@ -1495,37 +1528,38 @@ window_copy_adjust_selection(struct window_pane *wp, u_int *selx, u_int *sely)
 }
 
 static int
-window_copy_update_selection(struct window_pane *wp, int may_redraw)
+window_copy_update_selection(struct window_mode_entry *wme, int may_redraw)
 {
-	struct window_copy_mode_data	*data = wp->modedata;
+	struct window_copy_mode_data	*data = wme->data;
 	struct screen			*s = &data->screen;
 
 	if (s->sel == NULL && data->lineflag == LINE_SEL_NONE)
 		return (0);
-	return (window_copy_set_selection(wp, may_redraw));
+	return (window_copy_set_selection(wme, may_redraw));
 }
 
 static int
-window_copy_set_selection(struct window_pane *wp, int may_redraw)
+window_copy_set_selection(struct window_mode_entry *wme, int may_redraw)
 {
-	struct window_copy_mode_data	*data = wp->modedata;
+	struct window_pane		*wp = wme->wp;
+	struct window_copy_mode_data	*data = wme->data;
 	struct screen			*s = &data->screen;
 	struct options			*oo = wp->window->options;
 	struct grid_cell		 gc;
 	u_int				 sx, sy, cy, endsx, endsy;
 	int				 startrelpos, endrelpos;
 
-	window_copy_synchronize_cursor(wp);
+	window_copy_synchronize_cursor(wme);
 
 	/* Adjust the selection. */
 	sx = data->selx;
 	sy = data->sely;
-	startrelpos = window_copy_adjust_selection(wp, &sx, &sy);
+	startrelpos = window_copy_adjust_selection(wme, &sx, &sy);
 
 	/* Adjust the end of selection. */
 	endsx = data->endselx;
 	endsy = data->endsely;
-	endrelpos = window_copy_adjust_selection(wp, &endsx, &endsy);
+	endrelpos = window_copy_adjust_selection(wme, &endsx, &endsy);
 
 	/* Selection is outside of the current screen */
 	if (startrelpos == endrelpos &&
@@ -1549,14 +1583,17 @@ window_copy_set_selection(struct window_pane *wp, int may_redraw)
 		cy = data->cy;
 		if (data->cursordrag == CURSORDRAG_ENDSEL) {
 			if (sy < cy)
-				window_copy_redraw_lines(wp, sy, cy - sy + 1);
+				window_copy_redraw_lines(wme, sy, cy - sy + 1);
 			else
-				window_copy_redraw_lines(wp, cy, sy - cy + 1);
+				window_copy_redraw_lines(wme, cy, sy - cy + 1);
 		} else {
-			if (endsy < cy)
-				window_copy_redraw_lines(wp, endsy, cy - endsy + 1);
-			else
-				window_copy_redraw_lines(wp, cy, endsy - cy + 1);
+			if (endsy < cy) {
+				window_copy_redraw_lines(wme, endsy,
+				    cy - endsy + 1);
+			} else {
+				window_copy_redraw_lines(wme, cy,
+				    endsy - cy + 1);
+			}
 		}
 	}
 
@@ -1564,9 +1601,10 @@ window_copy_set_selection(struct window_pane *wp, int may_redraw)
 }
 
 static void *
-window_copy_get_selection(struct window_pane *wp, size_t *len)
+window_copy_get_selection(struct window_mode_entry *wme, size_t *len)
 {
-	struct window_copy_mode_data	*data = wp->modedata;
+	struct window_pane		*wp = wme->wp;
+	struct window_copy_mode_data	*data = wme->data;
 	struct screen			*s = &data->screen;
 	char				*buf;
 	size_t				 off;
@@ -1599,7 +1637,7 @@ window_copy_get_selection(struct window_pane *wp, size_t *len)
 	}
 
 	/* Trim ex to end of line. */
-	ey_last = window_copy_find_length(wp, ey);
+	ey_last = window_copy_find_length(wme, ey);
 	if (ex > ey_last)
 		ex = ey_last;
 
@@ -1657,7 +1695,7 @@ window_copy_get_selection(struct window_pane *wp, size_t *len)
 
 	/* Copy the lines. */
 	for (i = sy; i <= ey; i++) {
-		window_copy_copy_line(wp, &buf, &off, i,
+		window_copy_copy_line(wme, &buf, &off, i,
 		    (i == sy ? firstsx : restsx),
 		    (i == ey ? lastex : restex));
 	}
@@ -1674,9 +1712,10 @@ window_copy_get_selection(struct window_pane *wp, size_t *len)
 }
 
 static void
-window_copy_copy_buffer(struct window_pane *wp, void *buf, size_t len)
+window_copy_copy_buffer(struct window_mode_entry *wme, void *buf, size_t len)
 {
-	struct screen_write_ctx	ctx;
+	struct window_pane	*wp = wme->wp;
+	struct screen_write_ctx	 ctx;
 
 	if (options_get_number(global_options, "set-clipboard") != 0) {
 		screen_write_start(&ctx, wp, NULL);
@@ -1690,15 +1729,16 @@ window_copy_copy_buffer(struct window_pane *wp, void *buf, size_t len)
 }
 
 static void
-window_copy_copy_pipe(struct window_pane *wp, struct session *s,
+window_copy_copy_pipe(struct window_mode_entry *wme, struct session *s,
     const char *fmt)
 {
-	void		*buf;
-	size_t		 len;
-	struct job	*job;
-	char		*expanded;
+	struct window_pane	*wp = wme->wp;
+	void			*buf;
+	size_t			 len;
+	struct job		*job;
+	char			*expanded;
 
-	buf = window_copy_get_selection(wp, &len);
+	buf = window_copy_get_selection(wme, &len);
 	if (buf == NULL)
 		return;
 	expanded = format_single(NULL, fmt, NULL, s, NULL, wp);
@@ -1707,30 +1747,31 @@ window_copy_copy_pipe(struct window_pane *wp, struct session *s,
 	bufferevent_write(job_get_event(job), buf, len);
 
 	free(expanded);
-	window_copy_copy_buffer(wp, buf, len);
+	window_copy_copy_buffer(wme, buf, len);
 }
 
 static void
-window_copy_copy_selection(struct window_pane *wp)
+window_copy_copy_selection(struct window_mode_entry *wme)
 {
 	char	*buf;
 	size_t	 len;
 
-	buf = window_copy_get_selection(wp, &len);
+	buf = window_copy_get_selection(wme, &len);
 	if (buf != NULL)
-		window_copy_copy_buffer(wp, buf, len);
+		window_copy_copy_buffer(wme, buf, len);
 }
 
 static void
-window_copy_append_selection(struct window_pane *wp)
+window_copy_append_selection(struct window_mode_entry *wme)
 {
+	struct window_pane		*wp = wme->wp;
 	char				*buf;
 	struct paste_buffer		*pb;
 	const char			*bufdata, *bufname;
 	size_t				 len, bufsize;
 	struct screen_write_ctx		 ctx;
 
-	buf = window_copy_get_selection(wp, &len);
+	buf = window_copy_get_selection(wme, &len);
 	if (buf == NULL)
 		return;
 
@@ -1754,10 +1795,10 @@ window_copy_append_selection(struct window_pane *wp)
 }
 
 static void
-window_copy_copy_line(struct window_pane *wp, char **buf, size_t *off, u_int sy,
-    u_int sx, u_int ex)
+window_copy_copy_line(struct window_mode_entry *wme, char **buf, size_t *off,
+    u_int sy, u_int sx, u_int ex)
 {
-	struct window_copy_mode_data	*data = wp->modedata;
+	struct window_copy_mode_data	*data = wme->data;
 	struct grid			*gd = data->backing->grid;
 	struct grid_cell		 gc;
 	struct grid_line		*gl;
@@ -1780,7 +1821,7 @@ window_copy_copy_line(struct window_pane *wp, char **buf, size_t *off, u_int sy,
 	if (wrapped)
 		xx = gl->cellsize;
 	else
-		xx = window_copy_find_length(wp, sy);
+		xx = window_copy_find_length(wme, sy);
 	if (ex > xx)
 		ex = xx;
 	if (sx > xx)
@@ -1814,9 +1855,9 @@ window_copy_copy_line(struct window_pane *wp, char **buf, size_t *off, u_int sy,
 }
 
 static void
-window_copy_clear_selection(struct window_pane *wp)
+window_copy_clear_selection(struct window_mode_entry *wme)
 {
-	struct window_copy_mode_data   *data = wp->modedata;
+	struct window_copy_mode_data   *data = wme->data;
 	u_int				px, py;
 
 	screen_clear_selection(&data->screen);
@@ -1825,15 +1866,16 @@ window_copy_clear_selection(struct window_pane *wp)
 	data->lineflag = LINE_SEL_NONE;
 
 	py = screen_hsize(data->backing) + data->cy - data->oy;
-	px = window_copy_find_length(wp, py);
+	px = window_copy_find_length(wme, py);
 	if (data->cx > px)
-		window_copy_update_cursor(wp, px, data->cy);
+		window_copy_update_cursor(wme, px, data->cy);
 }
 
 static int
-window_copy_in_set(struct window_pane *wp, u_int px, u_int py, const char *set)
+window_copy_in_set(struct window_mode_entry *wme, u_int px, u_int py,
+    const char *set)
 {
-	struct window_copy_mode_data	*data = wp->modedata;
+	struct window_copy_mode_data	*data = wme->data;
 	struct grid_cell		 gc;
 	const struct utf8_data		*ud;
 	struct utf8_data		*copy;
@@ -1860,9 +1902,9 @@ window_copy_in_set(struct window_pane *wp, u_int px, u_int py, const char *set)
 }
 
 static u_int
-window_copy_find_length(struct window_pane *wp, u_int py)
+window_copy_find_length(struct window_mode_entry *wme, u_int py)
 {
-	struct window_copy_mode_data	*data = wp->modedata;
+	struct window_copy_mode_data	*data = wme->data;
 	struct screen			*s = data->backing;
 	struct grid_cell		 gc;
 	u_int				 px;
@@ -1886,9 +1928,9 @@ window_copy_find_length(struct window_pane *wp, u_int py)
 }
 
 static void
-window_copy_cursor_start_of_line(struct window_pane *wp)
+window_copy_cursor_start_of_line(struct window_mode_entry *wme)
 {
-	struct window_copy_mode_data	*data = wp->modedata;
+	struct window_copy_mode_data	*data = wme->data;
 	struct screen			*back_s = data->backing;
 	struct grid			*gd = back_s->grid;
 	u_int				 py;
@@ -1897,25 +1939,25 @@ window_copy_cursor_start_of_line(struct window_pane *wp)
 		py = screen_hsize(back_s) + data->cy - data->oy;
 		while (py > 0 &&
 		    grid_get_line(gd, py - 1)->flags & GRID_LINE_WRAPPED) {
-			window_copy_cursor_up(wp, 0);
+			window_copy_cursor_up(wme, 0);
 			py = screen_hsize(back_s) + data->cy - data->oy;
 		}
 	}
-	window_copy_update_cursor(wp, 0, data->cy);
-	if (window_copy_update_selection(wp, 1))
-		window_copy_redraw_lines(wp, data->cy, 1);
+	window_copy_update_cursor(wme, 0, data->cy);
+	if (window_copy_update_selection(wme, 1))
+		window_copy_redraw_lines(wme, data->cy, 1);
 }
 
 static void
-window_copy_cursor_back_to_indentation(struct window_pane *wp)
+window_copy_cursor_back_to_indentation(struct window_mode_entry *wme)
 {
-	struct window_copy_mode_data	*data = wp->modedata;
+	struct window_copy_mode_data	*data = wme->data;
 	u_int				 px, py, xx;
 	struct grid_cell		 gc;
 
 	px = 0;
 	py = screen_hsize(data->backing) + data->cy - data->oy;
-	xx = window_copy_find_length(wp, py);
+	xx = window_copy_find_length(wme, py);
 
 	while (px < xx) {
 		grid_get_cell(data->backing->grid, px, py, &gc);
@@ -1924,22 +1966,22 @@ window_copy_cursor_back_to_indentation(struct window_pane *wp)
 		px++;
 	}
 
-	window_copy_update_cursor(wp, px, data->cy);
-	if (window_copy_update_selection(wp, 1))
-		window_copy_redraw_lines(wp, data->cy, 1);
+	window_copy_update_cursor(wme, px, data->cy);
+	if (window_copy_update_selection(wme, 1))
+		window_copy_redraw_lines(wme, data->cy, 1);
 }
 
 static void
-window_copy_cursor_end_of_line(struct window_pane *wp)
+window_copy_cursor_end_of_line(struct window_mode_entry *wme)
 {
-	struct window_copy_mode_data	*data = wp->modedata;
+	struct window_copy_mode_data	*data = wme->data;
 	struct screen			*back_s = data->backing;
 	struct grid			*gd = back_s->grid;
 	struct grid_line		*gl;
 	u_int				 px, py;
 
 	py = screen_hsize(back_s) + data->cy - data->oy;
-	px = window_copy_find_length(wp, py);
+	px = window_copy_find_length(wme, py);
 
 	if (data->cx == px && data->lineflag == LINE_SEL_NONE) {
 		if (data->screen.sel != NULL && data->rectflag)
@@ -1950,22 +1992,22 @@ window_copy_cursor_end_of_line(struct window_pane *wp)
 				gl = grid_get_line(gd, py);
 				if (~gl->flags & GRID_LINE_WRAPPED)
 					break;
-				window_copy_cursor_down(wp, 0);
+				window_copy_cursor_down(wme, 0);
 				py = screen_hsize(back_s) + data->cy - data->oy;
 			}
-			px = window_copy_find_length(wp, py);
+			px = window_copy_find_length(wme, py);
 		}
 	}
-	window_copy_update_cursor(wp, px, data->cy);
+	window_copy_update_cursor(wme, px, data->cy);
 
-	if (window_copy_update_selection(wp, 1))
-		window_copy_redraw_lines(wp, data->cy, 1);
+	if (window_copy_update_selection(wme, 1))
+		window_copy_redraw_lines(wme, data->cy, 1);
 }
 
 static void
-window_copy_other_end(struct window_pane *wp)
+window_copy_other_end(struct window_mode_entry *wme)
 {
-	struct window_copy_mode_data	*data = wp->modedata;
+	struct window_copy_mode_data	*data = wme->data;
 	struct screen			*s = &data->screen;
 	u_int				 selx, sely, cy, yy, hsize;
 
@@ -2009,14 +2051,14 @@ window_copy_other_end(struct window_pane *wp)
 	} else
 		data->cy = cy + sely - yy;
 
-	window_copy_update_selection(wp, 1);
-	window_copy_redraw_screen(wp);
+	window_copy_update_selection(wme, 1);
+	window_copy_redraw_screen(wme);
 }
 
 static void
-window_copy_cursor_left(struct window_pane *wp)
+window_copy_cursor_left(struct window_mode_entry *wme)
 {
-	struct window_copy_mode_data	*data = wp->modedata;
+	struct window_copy_mode_data	*data = wme->data;
 	u_int				 py, cx;
 	struct grid_cell		 gc;
 
@@ -2029,19 +2071,19 @@ window_copy_cursor_left(struct window_pane *wp)
 		cx--;
 	}
 	if (cx == 0 && py > 0) {
-		window_copy_cursor_up(wp, 0);
-		window_copy_cursor_end_of_line(wp);
+		window_copy_cursor_up(wme, 0);
+		window_copy_cursor_end_of_line(wme);
 	} else if (cx > 0) {
-		window_copy_update_cursor(wp, cx - 1, data->cy);
-		if (window_copy_update_selection(wp, 1))
-			window_copy_redraw_lines(wp, data->cy, 1);
+		window_copy_update_cursor(wme, cx - 1, data->cy);
+		if (window_copy_update_selection(wme, 1))
+			window_copy_redraw_lines(wme, data->cy, 1);
 	}
 }
 
 static void
-window_copy_cursor_right(struct window_pane *wp)
+window_copy_cursor_right(struct window_mode_entry *wme)
 {
-	struct window_copy_mode_data	*data = wp->modedata;
+	struct window_copy_mode_data	*data = wme->data;
 	u_int				 px, py, yy, cx, cy;
 	struct grid_cell		 gc;
 
@@ -2050,11 +2092,11 @@ window_copy_cursor_right(struct window_pane *wp)
 	if (data->screen.sel != NULL && data->rectflag)
 		px = screen_size_x(&data->screen);
 	else
-		px = window_copy_find_length(wp, py);
+		px = window_copy_find_length(wme, py);
 
 	if (data->cx >= px && py < yy) {
-		window_copy_cursor_start_of_line(wp);
-		window_copy_cursor_down(wp, 0);
+		window_copy_cursor_start_of_line(wme);
+		window_copy_cursor_down(wme, 0);
 	} else if (data->cx < px) {
 		cx = data->cx + 1;
 		cy = screen_hsize(data->backing) + data->cy - data->oy;
@@ -2064,123 +2106,123 @@ window_copy_cursor_right(struct window_pane *wp)
 				break;
 			cx++;
 		}
-		window_copy_update_cursor(wp, cx, data->cy);
-		if (window_copy_update_selection(wp, 1))
-			window_copy_redraw_lines(wp, data->cy, 1);
+		window_copy_update_cursor(wme, cx, data->cy);
+		if (window_copy_update_selection(wme, 1))
+			window_copy_redraw_lines(wme, data->cy, 1);
 	}
 }
 
 static void
-window_copy_cursor_up(struct window_pane *wp, int scroll_only)
+window_copy_cursor_up(struct window_mode_entry *wme, int scroll_only)
 {
-	struct window_copy_mode_data	*data = wp->modedata;
+	struct window_copy_mode_data	*data = wme->data;
 	struct screen			*s = &data->screen;
 	u_int				 ox, oy, px, py;
 
 	oy = screen_hsize(data->backing) + data->cy - data->oy;
-	ox = window_copy_find_length(wp, oy);
+	ox = window_copy_find_length(wme, oy);
 	if (data->cx != ox) {
 		data->lastcx = data->cx;
 		data->lastsx = ox;
 	}
 
 	if (data->lineflag == LINE_SEL_LEFT_RIGHT && oy == data->sely)
-		window_copy_other_end(wp);
+		window_copy_other_end(wme);
 
 	data->cx = data->lastcx;
 	if (scroll_only || data->cy == 0) {
-		window_copy_scroll_down(wp, 1);
+		window_copy_scroll_down(wme, 1);
 		if (scroll_only) {
 			if (data->cy == screen_size_y(s) - 1)
-				window_copy_redraw_lines(wp, data->cy, 1);
+				window_copy_redraw_lines(wme, data->cy, 1);
 			else
-				window_copy_redraw_lines(wp, data->cy, 2);
+				window_copy_redraw_lines(wme, data->cy, 2);
 		}
 	} else {
-		window_copy_update_cursor(wp, data->cx, data->cy - 1);
-		if (window_copy_update_selection(wp, 1)) {
+		window_copy_update_cursor(wme, data->cx, data->cy - 1);
+		if (window_copy_update_selection(wme, 1)) {
 			if (data->cy == screen_size_y(s) - 1)
-				window_copy_redraw_lines(wp, data->cy, 1);
+				window_copy_redraw_lines(wme, data->cy, 1);
 			else
-				window_copy_redraw_lines(wp, data->cy, 2);
+				window_copy_redraw_lines(wme, data->cy, 2);
 		}
 	}
 
 	if (data->screen.sel == NULL || !data->rectflag) {
 		py = screen_hsize(data->backing) + data->cy - data->oy;
-		px = window_copy_find_length(wp, py);
+		px = window_copy_find_length(wme, py);
 		if ((data->cx >= data->lastsx && data->cx != px) ||
 		    data->cx > px)
-			window_copy_cursor_end_of_line(wp);
+			window_copy_cursor_end_of_line(wme);
 	}
 
 	if (data->lineflag == LINE_SEL_LEFT_RIGHT)
-		window_copy_cursor_end_of_line(wp);
+		window_copy_cursor_end_of_line(wme);
 	else if (data->lineflag == LINE_SEL_RIGHT_LEFT)
-		window_copy_cursor_start_of_line(wp);
+		window_copy_cursor_start_of_line(wme);
 }
 
 static void
-window_copy_cursor_down(struct window_pane *wp, int scroll_only)
+window_copy_cursor_down(struct window_mode_entry *wme, int scroll_only)
 {
-	struct window_copy_mode_data	*data = wp->modedata;
+	struct window_copy_mode_data	*data = wme->data;
 	struct screen			*s = &data->screen;
 	u_int				 ox, oy, px, py;
 
 	oy = screen_hsize(data->backing) + data->cy - data->oy;
-	ox = window_copy_find_length(wp, oy);
+	ox = window_copy_find_length(wme, oy);
 	if (data->cx != ox) {
 		data->lastcx = data->cx;
 		data->lastsx = ox;
 	}
 
 	if (data->lineflag == LINE_SEL_RIGHT_LEFT && oy == data->endsely)
-		window_copy_other_end(wp);
+		window_copy_other_end(wme);
 
 	data->cx = data->lastcx;
 	if (scroll_only || data->cy == screen_size_y(s) - 1) {
-		window_copy_scroll_up(wp, 1);
+		window_copy_scroll_up(wme, 1);
 		if (scroll_only && data->cy > 0)
-			window_copy_redraw_lines(wp, data->cy - 1, 2);
+			window_copy_redraw_lines(wme, data->cy - 1, 2);
 	} else {
-		window_copy_update_cursor(wp, data->cx, data->cy + 1);
-		if (window_copy_update_selection(wp, 1))
-			window_copy_redraw_lines(wp, data->cy - 1, 2);
+		window_copy_update_cursor(wme, data->cx, data->cy + 1);
+		if (window_copy_update_selection(wme, 1))
+			window_copy_redraw_lines(wme, data->cy - 1, 2);
 	}
 
 	if (data->screen.sel == NULL || !data->rectflag) {
 		py = screen_hsize(data->backing) + data->cy - data->oy;
-		px = window_copy_find_length(wp, py);
+		px = window_copy_find_length(wme, py);
 		if ((data->cx >= data->lastsx && data->cx != px) ||
 		    data->cx > px)
-			window_copy_cursor_end_of_line(wp);
+			window_copy_cursor_end_of_line(wme);
 	}
 
 	if (data->lineflag == LINE_SEL_LEFT_RIGHT)
-		window_copy_cursor_end_of_line(wp);
+		window_copy_cursor_end_of_line(wme);
 	else if (data->lineflag == LINE_SEL_RIGHT_LEFT)
-		window_copy_cursor_start_of_line(wp);
+		window_copy_cursor_start_of_line(wme);
 }
 
 static void
-window_copy_cursor_jump(struct window_pane *wp)
+window_copy_cursor_jump(struct window_mode_entry *wme)
 {
-	struct window_copy_mode_data	*data = wp->modedata;
+	struct window_copy_mode_data	*data = wme->data;
 	struct screen			*back_s = data->backing;
 	struct grid_cell		 gc;
 	u_int				 px, py, xx;
 
 	px = data->cx + 1;
 	py = screen_hsize(back_s) + data->cy - data->oy;
-	xx = window_copy_find_length(wp, py);
+	xx = window_copy_find_length(wme, py);
 
 	while (px < xx) {
 		grid_get_cell(back_s->grid, px, py, &gc);
 		if (!(gc.flags & GRID_FLAG_PADDING) &&
 		    gc.data.size == 1 && *gc.data.data == data->jumpchar) {
-			window_copy_update_cursor(wp, px, data->cy);
-			if (window_copy_update_selection(wp, 1))
-				window_copy_redraw_lines(wp, data->cy, 1);
+			window_copy_update_cursor(wme, px, data->cy);
+			if (window_copy_update_selection(wme, 1))
+				window_copy_redraw_lines(wme, data->cy, 1);
 			return;
 		}
 		px++;
@@ -2188,9 +2230,9 @@ window_copy_cursor_jump(struct window_pane *wp)
 }
 
 static void
-window_copy_cursor_jump_back(struct window_pane *wp)
+window_copy_cursor_jump_back(struct window_mode_entry *wme)
 {
-	struct window_copy_mode_data	*data = wp->modedata;
+	struct window_copy_mode_data	*data = wme->data;
 	struct screen			*back_s = data->backing;
 	struct grid_cell		 gc;
 	u_int				 px, py;
@@ -2205,9 +2247,9 @@ window_copy_cursor_jump_back(struct window_pane *wp)
 		grid_get_cell(back_s->grid, px, py, &gc);
 		if (!(gc.flags & GRID_FLAG_PADDING) &&
 		    gc.data.size == 1 && *gc.data.data == data->jumpchar) {
-			window_copy_update_cursor(wp, px, data->cy);
-			if (window_copy_update_selection(wp, 1))
-				window_copy_redraw_lines(wp, data->cy, 1);
+			window_copy_update_cursor(wme, px, data->cy);
+			if (window_copy_update_selection(wme, 1))
+				window_copy_redraw_lines(wme, data->cy, 1);
 			return;
 		}
 		if (px == 0)
@@ -2217,24 +2259,24 @@ window_copy_cursor_jump_back(struct window_pane *wp)
 }
 
 static void
-window_copy_cursor_jump_to(struct window_pane *wp)
+window_copy_cursor_jump_to(struct window_mode_entry *wme)
 {
-	struct window_copy_mode_data	*data = wp->modedata;
+	struct window_copy_mode_data	*data = wme->data;
 	struct screen			*back_s = data->backing;
 	struct grid_cell		 gc;
 	u_int				 px, py, xx;
 
 	px = data->cx + 2;
 	py = screen_hsize(back_s) + data->cy - data->oy;
-	xx = window_copy_find_length(wp, py);
+	xx = window_copy_find_length(wme, py);
 
 	while (px < xx) {
 		grid_get_cell(back_s->grid, px, py, &gc);
 		if (!(gc.flags & GRID_FLAG_PADDING) &&
 		    gc.data.size == 1 && *gc.data.data == data->jumpchar) {
-			window_copy_update_cursor(wp, px - 1, data->cy);
-			if (window_copy_update_selection(wp, 1))
-				window_copy_redraw_lines(wp, data->cy, 1);
+			window_copy_update_cursor(wme, px - 1, data->cy);
+			if (window_copy_update_selection(wme, 1))
+				window_copy_redraw_lines(wme, data->cy, 1);
 			return;
 		}
 		px++;
@@ -2242,9 +2284,9 @@ window_copy_cursor_jump_to(struct window_pane *wp)
 }
 
 static void
-window_copy_cursor_jump_to_back(struct window_pane *wp)
+window_copy_cursor_jump_to_back(struct window_mode_entry *wme)
 {
-	struct window_copy_mode_data	*data = wp->modedata;
+	struct window_copy_mode_data	*data = wme->data;
 	struct screen			*back_s = data->backing;
 	struct grid_cell		 gc;
 	u_int				 px, py;
@@ -2262,9 +2304,9 @@ window_copy_cursor_jump_to_back(struct window_pane *wp)
 		grid_get_cell(back_s->grid, px, py, &gc);
 		if (!(gc.flags & GRID_FLAG_PADDING) &&
 		    gc.data.size == 1 && *gc.data.data == data->jumpchar) {
-			window_copy_update_cursor(wp, px + 1, data->cy);
-			if (window_copy_update_selection(wp, 1))
-				window_copy_redraw_lines(wp, data->cy, 1);
+			window_copy_update_cursor(wme, px + 1, data->cy);
+			if (window_copy_update_selection(wme, 1))
+				window_copy_redraw_lines(wme, data->cy, 1);
 			return;
 		}
 		if (px == 0)
@@ -2274,16 +2316,17 @@ window_copy_cursor_jump_to_back(struct window_pane *wp)
 }
 
 static void
-window_copy_cursor_next_word(struct window_pane *wp, const char *separators)
+window_copy_cursor_next_word(struct window_mode_entry *wme,
+    const char *separators)
 {
-	struct window_copy_mode_data	*data = wp->modedata;
+	struct window_copy_mode_data	*data = wme->data;
 	struct screen			*back_s = data->backing;
 	u_int				 px, py, xx, yy;
 	int				 expected = 0;
 
 	px = data->cx;
 	py = screen_hsize(back_s) + data->cy - data->oy;
-	xx = window_copy_find_length(wp, py);
+	xx = window_copy_find_length(wme, py);
 	yy = screen_hsize(back_s) + screen_size_y(back_s) - 1;
 
 	/*
@@ -2294,32 +2337,33 @@ window_copy_cursor_next_word(struct window_pane *wp, const char *separators)
 	 */
 	do {
 		while (px > xx ||
-		    window_copy_in_set(wp, px, py, separators) == expected) {
+		    window_copy_in_set(wme, px, py, separators) == expected) {
 			/* Move down if we're past the end of the line. */
 			if (px > xx) {
 				if (py == yy)
 					return;
-				window_copy_cursor_down(wp, 0);
+				window_copy_cursor_down(wme, 0);
 				px = 0;
 
 				py = screen_hsize(back_s) + data->cy - data->oy;
-				xx = window_copy_find_length(wp, py);
+				xx = window_copy_find_length(wme, py);
 			} else
 				px++;
 		}
 		expected = !expected;
 	} while (expected == 1);
 
-	window_copy_update_cursor(wp, px, data->cy);
-	if (window_copy_update_selection(wp, 1))
-		window_copy_redraw_lines(wp, data->cy, 1);
+	window_copy_update_cursor(wme, px, data->cy);
+	if (window_copy_update_selection(wme, 1))
+		window_copy_redraw_lines(wme, data->cy, 1);
 }
 
 static void
-window_copy_cursor_next_word_end(struct window_pane *wp,
+window_copy_cursor_next_word_end(struct window_mode_entry *wme,
     const char *separators)
 {
-	struct window_copy_mode_data	*data = wp->modedata;
+	struct window_pane		*wp = wme->wp;
+	struct window_copy_mode_data	*data = wme->data;
 	struct options			*oo = wp->window->options;
 	struct screen			*back_s = data->backing;
 	u_int				 px, py, xx, yy;
@@ -2327,11 +2371,11 @@ window_copy_cursor_next_word_end(struct window_pane *wp,
 
 	px = data->cx;
 	py = screen_hsize(back_s) + data->cy - data->oy;
-	xx = window_copy_find_length(wp, py);
+	xx = window_copy_find_length(wme, py);
 	yy = screen_hsize(back_s) + screen_size_y(back_s) - 1;
 
 	keys = options_get_number(oo, "mode-keys");
-	if (keys == MODEKEY_VI && !window_copy_in_set(wp, px, py, separators))
+	if (keys == MODEKEY_VI && !window_copy_in_set(wme, px, py, separators))
 		px++;
 
 	/*
@@ -2342,16 +2386,16 @@ window_copy_cursor_next_word_end(struct window_pane *wp,
 	 */
 	do {
 		while (px > xx ||
-		    window_copy_in_set(wp, px, py, separators) == expected) {
+		    window_copy_in_set(wme, px, py, separators) == expected) {
 			/* Move down if we're past the end of the line. */
 			if (px > xx) {
 				if (py == yy)
 					return;
-				window_copy_cursor_down(wp, 0);
+				window_copy_cursor_down(wme, 0);
 				px = 0;
 
 				py = screen_hsize(back_s) + data->cy - data->oy;
-				xx = window_copy_find_length(wp, py);
+				xx = window_copy_find_length(wme, py);
 			} else
 				px++;
 		}
@@ -2361,17 +2405,17 @@ window_copy_cursor_next_word_end(struct window_pane *wp,
 	if (keys == MODEKEY_VI && px != 0)
 		px--;
 
-	window_copy_update_cursor(wp, px, data->cy);
-	if (window_copy_update_selection(wp, 1))
-		window_copy_redraw_lines(wp, data->cy, 1);
+	window_copy_update_cursor(wme, px, data->cy);
+	if (window_copy_update_selection(wme, 1))
+		window_copy_redraw_lines(wme, data->cy, 1);
 }
 
 /* Move to the previous place where a word begins. */
 static void
-window_copy_cursor_previous_word(struct window_pane *wp,
+window_copy_cursor_previous_word(struct window_mode_entry *wme,
     const char *separators)
 {
-	struct window_copy_mode_data	*data = wp->modedata;
+	struct window_copy_mode_data	*data = wme->data;
 	u_int				 px, py;
 
 	px = data->cx;
@@ -2381,34 +2425,35 @@ window_copy_cursor_previous_word(struct window_pane *wp,
 	for (;;) {
 		if (px > 0) {
 			px--;
-			if (!window_copy_in_set(wp, px, py, separators))
+			if (!window_copy_in_set(wme, px, py, separators))
 				break;
 		} else {
 			if (data->cy == 0 &&
 			    (screen_hsize(data->backing) == 0 ||
 			    data->oy >= screen_hsize(data->backing) - 1))
 				goto out;
-			window_copy_cursor_up(wp, 0);
+			window_copy_cursor_up(wme, 0);
 
 			py = screen_hsize(data->backing) + data->cy - data->oy;
-			px = window_copy_find_length(wp, py);
+			px = window_copy_find_length(wme, py);
 		}
 	}
 
 	/* Move back to the beginning of this word. */
-	while (px > 0 && !window_copy_in_set(wp, px - 1, py, separators))
+	while (px > 0 && !window_copy_in_set(wme, px - 1, py, separators))
 		px--;
 
 out:
-	window_copy_update_cursor(wp, px, data->cy);
-	if (window_copy_update_selection(wp, 1))
-		window_copy_redraw_lines(wp, data->cy, 1);
+	window_copy_update_cursor(wme, px, data->cy);
+	if (window_copy_update_selection(wme, 1))
+		window_copy_redraw_lines(wme, data->cy, 1);
 }
 
 static void
-window_copy_scroll_up(struct window_pane *wp, u_int ny)
+window_copy_scroll_up(struct window_mode_entry *wme, u_int ny)
 {
-	struct window_copy_mode_data	*data = wp->modedata;
+	struct window_pane		*wp = wme->wp;
+	struct window_copy_mode_data	*data = wme->data;
 	struct screen			*s = &data->screen;
 	struct screen_write_ctx		 ctx;
 
@@ -2418,27 +2463,28 @@ window_copy_scroll_up(struct window_pane *wp, u_int ny)
 		return;
 	data->oy -= ny;
 
-	window_copy_update_selection(wp, 0);
+	window_copy_update_selection(wme, 0);
 
 	screen_write_start(&ctx, wp, NULL);
 	screen_write_cursormove(&ctx, 0, 0);
 	screen_write_deleteline(&ctx, ny, 8);
-	window_copy_write_lines(wp, &ctx, screen_size_y(s) - ny, ny);
-	window_copy_write_line(wp, &ctx, 0);
+	window_copy_write_lines(wme, &ctx, screen_size_y(s) - ny, ny);
+	window_copy_write_line(wme, &ctx, 0);
 	if (screen_size_y(s) > 1)
-		window_copy_write_line(wp, &ctx, 1);
+		window_copy_write_line(wme, &ctx, 1);
 	if (screen_size_y(s) > 3)
-		window_copy_write_line(wp, &ctx, screen_size_y(s) - 2);
+		window_copy_write_line(wme, &ctx, screen_size_y(s) - 2);
 	if (s->sel != NULL && screen_size_y(s) > ny)
-		window_copy_write_line(wp, &ctx, screen_size_y(s) - ny - 1);
+		window_copy_write_line(wme, &ctx, screen_size_y(s) - ny - 1);
 	screen_write_cursormove(&ctx, data->cx, data->cy);
 	screen_write_stop(&ctx);
 }
 
 static void
-window_copy_scroll_down(struct window_pane *wp, u_int ny)
+window_copy_scroll_down(struct window_mode_entry *wme, u_int ny)
 {
-	struct window_copy_mode_data	*data = wp->modedata;
+	struct window_pane		*wp = wme->wp;
+	struct window_copy_mode_data	*data = wme->data;
 	struct screen			*s = &data->screen;
 	struct screen_write_ctx		 ctx;
 
@@ -2451,35 +2497,35 @@ window_copy_scroll_down(struct window_pane *wp, u_int ny)
 		return;
 	data->oy += ny;
 
-	window_copy_update_selection(wp, 0);
+	window_copy_update_selection(wme, 0);
 
 	screen_write_start(&ctx, wp, NULL);
 	screen_write_cursormove(&ctx, 0, 0);
 	screen_write_insertline(&ctx, ny, 8);
-	window_copy_write_lines(wp, &ctx, 0, ny);
+	window_copy_write_lines(wme, &ctx, 0, ny);
 	if (s->sel != NULL && screen_size_y(s) > ny)
-		window_copy_write_line(wp, &ctx, ny);
+		window_copy_write_line(wme, &ctx, ny);
 	else if (ny == 1) /* nuke position */
-		window_copy_write_line(wp, &ctx, 1);
+		window_copy_write_line(wme, &ctx, 1);
 	screen_write_cursormove(&ctx, data->cx, data->cy);
 	screen_write_stop(&ctx);
 }
 
 static void
-window_copy_rectangle_toggle(struct window_pane *wp)
+window_copy_rectangle_toggle(struct window_mode_entry *wme)
 {
-	struct window_copy_mode_data	*data = wp->modedata;
+	struct window_copy_mode_data	*data = wme->data;
 	u_int				 px, py;
 
 	data->rectflag = !data->rectflag;
 
 	py = screen_hsize(data->backing) + data->cy - data->oy;
-	px = window_copy_find_length(wp, py);
+	px = window_copy_find_length(wme, py);
 	if (data->cx > px)
-		window_copy_update_cursor(wp, px, data->cy);
+		window_copy_update_cursor(wme, px, data->cy);
 
-	window_copy_update_selection(wp, 1);
-	window_copy_redraw_screen(wp);
+	window_copy_update_selection(wme, 1);
+	window_copy_redraw_screen(wme);
 }
 
 static void
@@ -2489,13 +2535,15 @@ window_copy_move_mouse(struct mouse_event *m)
 	u_int			 x, y;
 
 	wp = cmd_mouse_pane(m, NULL, NULL);
-	if (wp == NULL || wp->mode != &window_copy_mode)
+	if (wp == NULL ||
+	    wp->mode == NULL ||
+	    wp->mode->mode != &window_copy_mode)
 		return;
 
 	if (cmd_mouse_at(wp, m, &x, &y, 0) != 0)
 		return;
 
-	window_copy_update_cursor(wp, x, y);
+	window_copy_update_cursor(wp->mode, x, y);
 }
 
 void
@@ -2508,7 +2556,9 @@ window_copy_start_drag(struct client *c, struct mouse_event *m)
 		return;
 
 	wp = cmd_mouse_pane(m, NULL, NULL);
-	if (wp == NULL || wp->mode != &window_copy_mode)
+	if (wp == NULL ||
+	    wp->mode == NULL ||
+	    wp->mode->mode != &window_copy_mode)
 		return;
 
 	if (cmd_mouse_at(wp, m, &x, &y, 1) != 0)
@@ -2517,9 +2567,9 @@ window_copy_start_drag(struct client *c, struct mouse_event *m)
 	c->tty.mouse_drag_update = window_copy_drag_update;
 	c->tty.mouse_drag_release = NULL; /* will fire MouseDragEnd key */
 
-	window_copy_update_cursor(wp, x, y);
-	window_copy_start_selection(wp);
-	window_copy_redraw_screen(wp);
+	window_copy_update_cursor(wp->mode, x, y);
+	window_copy_start_selection(wp->mode);
+	window_copy_redraw_screen(wp->mode);
 }
 
 static void
@@ -2530,15 +2580,17 @@ window_copy_drag_update(__unused struct client *c, struct mouse_event *m)
 	u_int				 x, y, old_cy;
 
 	wp = cmd_mouse_pane(m, NULL, NULL);
-	if (wp == NULL || wp->mode != &window_copy_mode)
+	if (wp == NULL ||
+	    wp->mode == NULL ||
+	    wp->mode->mode != &window_copy_mode)
 		return;
-	data = wp->modedata;
+	data = wp->mode->data;
 
 	if (cmd_mouse_at(wp, m, &x, &y, 0) != 0)
 		return;
 	old_cy = data->cy;
 
-	window_copy_update_cursor(wp, x, y);
-	if (window_copy_update_selection(wp, 1))
-		window_copy_redraw_selection(wp, old_cy);
+	window_copy_update_cursor(wp->mode, x, y);
+	if (window_copy_update_selection(wp->mode, 1))
+		window_copy_redraw_selection(wp->mode, old_cy);
 }
diff --git a/window-tree.c b/window-tree.c
index 5b7f1994..56bd2b07 100644
--- a/window-tree.c
+++ b/window-tree.c
@@ -24,11 +24,12 @@
 
 #include "tmux.h"
 
-static struct screen	*window_tree_init(struct window_pane *,
+static struct screen	*window_tree_init(struct window_mode_entry *,
 			     struct cmd_find_state *, struct args *);
-static void		 window_tree_free(struct window_pane *);
-static void		 window_tree_resize(struct window_pane *, u_int, u_int);
-static void		 window_tree_key(struct window_pane *,
+static void		 window_tree_free(struct window_mode_entry *);
+static void		 window_tree_resize(struct window_mode_entry *, u_int,
+			     u_int);
+static void		 window_tree_key(struct window_mode_entry *,
 			     struct client *, struct session *,
 			     struct winlink *, key_code, struct mouse_event *);
 
@@ -810,13 +811,14 @@ window_tree_search(__unused void *modedata, void *itemdata, const char *ss)
 }
 
 static struct screen *
-window_tree_init(struct window_pane *wp, struct cmd_find_state *fs,
+window_tree_init(struct window_mode_entry *wme, struct cmd_find_state *fs,
     struct args *args)
 {
+	struct window_pane		*wp = wme->wp;
 	struct window_tree_modedata	*data;
 	struct screen			*s;
 
-	wp->modedata = data = xcalloc(1, sizeof *data);
+	wme->data = data = xcalloc(1, sizeof *data);
 
 	if (args_has(args, 's'))
 		data->type = WINDOW_TREE_SESSION;
@@ -871,9 +873,9 @@ window_tree_destroy(struct window_tree_modedata *data)
 }
 
 static void
-window_tree_free(struct window_pane *wp)
+window_tree_free(struct window_mode_entry *wme)
 {
-	struct window_tree_modedata *data = wp->modedata;
+	struct window_tree_modedata *data = wme->data;
 
 	if (data == NULL)
 		return;
@@ -884,9 +886,9 @@ window_tree_free(struct window_pane *wp)
 }
 
 static void
-window_tree_resize(struct window_pane *wp, u_int sx, u_int sy)
+window_tree_resize(struct window_mode_entry *wme, u_int sx, u_int sy)
 {
-	struct window_tree_modedata	*data = wp->modedata;
+	struct window_tree_modedata	*data = wme->data;
 
 	mode_tree_resize(data->data, sx, sy);
 }
@@ -1119,11 +1121,12 @@ window_tree_mouse(struct window_tree_modedata *data, key_code key, u_int x,
 }
 
 static void
-window_tree_key(struct window_pane *wp, struct client *c,
+window_tree_key(struct window_mode_entry *wme, struct client *c,
     __unused struct session *s, __unused struct winlink *wl, key_code key,
     struct mouse_event *m)
 {
-	struct window_tree_modedata	*data = wp->modedata;
+	struct window_pane		*wp = wme->wp;
+	struct window_tree_modedata	*data = wme->data;
 	struct window_tree_itemdata	*item, *new_item;
 	char				*name, *prompt = NULL;
 	struct cmd_find_state		 fs;
diff --git a/window.c b/window.c
index 673cd84f..366df797 100644
--- a/window.c
+++ b/window.c
@@ -811,7 +811,6 @@ window_pane_create(struct window *w, u_int sx, u_int sy, u_int hlimit)
 	wp->event = NULL;
 
 	wp->mode = NULL;
-	wp->modeprefix = 1;
 
 	wp->layout_cell = NULL;
 
@@ -1054,8 +1053,8 @@ window_pane_resize(struct window_pane *wp, u_int sx, u_int sy)
 	wp->sy = sy;
 
 	screen_resize(&wp->base, sx, sy, wp->saved_grid == NULL);
-	if (wp->mode != NULL)
-		wp->mode->resize(wp, sx, sy);
+	if (wp->mode != NULL && wp->mode->mode->resize != NULL)
+		wp->mode->mode->resize(wp->mode, sx, sy);
 
 	wp->flags |= PANE_RESIZE;
 }
@@ -1222,13 +1221,17 @@ window_pane_set_mode(struct window_pane *wp, const struct window_mode *mode,
 
 	if (wp->mode != NULL)
 		return (1);
-	wp->mode = mode;
+
+	wp->mode = xcalloc(1, sizeof *wp->mode);
+	wp->mode->wp = wp;
+	wp->mode->mode = mode;
+	wp->mode->prefix = 1;
 
 	wp->modelast = time(NULL);
 	evtimer_set(&wp->modetimer, window_pane_mode_timer, wp);
 	evtimer_add(&wp->modetimer, &tv);
 
-	if ((s = wp->mode->init(wp, fs, args)) != NULL)
+	if ((s = wp->mode->mode->init(wp->mode, fs, args)) != NULL)
 		wp->screen = s;
 	wp->flags |= (PANE_REDRAW|PANE_CHANGED);
 
@@ -1245,9 +1248,9 @@ window_pane_reset_mode(struct window_pane *wp)
 
 	evtimer_del(&wp->modetimer);
 
-	wp->mode->free(wp);
+	wp->mode->mode->free(wp->mode);
+	free(wp->mode);
 	wp->mode = NULL;
-	wp->modeprefix = 1;
 
 	wp->screen = &wp->base;
 	wp->flags |= (PANE_REDRAW|PANE_CHANGED);
@@ -1260,15 +1263,16 @@ void
 window_pane_key(struct window_pane *wp, struct client *c, struct session *s,
     struct winlink *wl, key_code key, struct mouse_event *m)
 {
-	struct window_pane	*wp2;
+	struct window_mode_entry	*wme = wp->mode;
+	struct window_pane		*wp2;
 
 	if (KEYC_IS_MOUSE(key) && m == NULL)
 		return;
 
-	if (wp->mode != NULL) {
+	if (wme != NULL) {
 		wp->modelast = time(NULL);
-		if (wp->mode->key != NULL)
-			wp->mode->key(wp, c, s, wl, (key & ~KEYC_XTERM), m);
+		if (wme->mode->key != NULL)
+			wme->mode->key(wme, c, s, wl, (key & ~KEYC_XTERM), m);
 		return;
 	}