xref: /openbsd-src/usr.bin/tmux/window.c (revision a1f482fe91ac54f15e1363851901f1f78c11d875)
1*a1f482feSnicm /* $OpenBSD: window.c,v 1.299 2024/12/06 09:06:57 nicm Exp $ */
2311827fbSnicm 
3311827fbSnicm /*
498ca8272Snicm  * Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>
5311827fbSnicm  *
6311827fbSnicm  * Permission to use, copy, modify, and distribute this software for any
7311827fbSnicm  * purpose with or without fee is hereby granted, provided that the above
8311827fbSnicm  * copyright notice and this permission notice appear in all copies.
9311827fbSnicm  *
10311827fbSnicm  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11311827fbSnicm  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12311827fbSnicm  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13311827fbSnicm  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14311827fbSnicm  * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER
15311827fbSnicm  * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
16311827fbSnicm  * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17311827fbSnicm  */
18311827fbSnicm 
19311827fbSnicm #include <sys/types.h>
20d0600f38Snicm #include <sys/ioctl.h>
21311827fbSnicm 
227878fe97Snicm #include <ctype.h>
23311827fbSnicm #include <errno.h>
24311827fbSnicm #include <fcntl.h>
25570a3589Snicm #include <fnmatch.h>
26da643456Snicm #include <regex.h>
27189d1393Snicm #include <signal.h>
28311827fbSnicm #include <stdint.h>
29311827fbSnicm #include <stdlib.h>
30311827fbSnicm #include <string.h>
312413453fSnicm #include <time.h>
32311827fbSnicm #include <unistd.h>
33311827fbSnicm #include <util.h>
349d9ffcabSnicm #include <vis.h>
35311827fbSnicm 
36311827fbSnicm #include "tmux.h"
37311827fbSnicm 
38311827fbSnicm /*
39af9e4c5dSnicm  * Each window is attached to a number of panes, each of which is a pty. This
40311827fbSnicm  * file contains code to handle them.
41311827fbSnicm  *
42311827fbSnicm  * A pane has two buffers attached, these are filled and emptied by the main
43311827fbSnicm  * server poll loop. Output data is received from pty's in screen format,
44311827fbSnicm  * translated and returned as a series of escape sequences and strings via
45311827fbSnicm  * input_parse (in input.c). Input data is received as key codes and written
46311827fbSnicm  * directly via input_key.
47311827fbSnicm  *
48311827fbSnicm  * Each pane also has a "virtual" screen (screen.c) which contains the current
49311827fbSnicm  * state and is redisplayed when the window is reattached to a client.
50311827fbSnicm  *
51311827fbSnicm  * Windows are stored directly on a global array and wrapped in any number of
52311827fbSnicm  * winlink structs to be linked onto local session RB trees. A reference count
53311827fbSnicm  * is maintained and a window removed from the global list and destroyed when
54311827fbSnicm  * it reaches zero.
55311827fbSnicm  */
56311827fbSnicm 
57311827fbSnicm /* Global window list. */
58311827fbSnicm struct windows windows;
59311827fbSnicm 
603e0f2533Snicm /* Global panes tree. */
613e0f2533Snicm struct window_pane_tree all_window_panes;
6298864f58Snicm static u_int	next_window_pane_id;
6398864f58Snicm static u_int	next_window_id;
6498864f58Snicm static u_int	next_active_point;
65543206bbSnicm 
66aab3c1a6Snicm struct window_pane_input_data {
67aab3c1a6Snicm 	struct cmdq_item	*item;
68aab3c1a6Snicm 	u_int			 wp;
6983e2ab13Snicm 	struct client_file	*file;
70aab3c1a6Snicm };
71aab3c1a6Snicm 
72ced21769Snicm static struct window_pane *window_pane_create(struct window *, u_int, u_int,
73ced21769Snicm 		    u_int);
74ced21769Snicm static void	window_pane_destroy(struct window_pane *);
75ced21769Snicm 
76ae69181dSnicm RB_GENERATE(windows, window, entry, window_cmp);
77ced21769Snicm RB_GENERATE(winlinks, winlink, entry, winlink_cmp);
78ced21769Snicm RB_GENERATE(window_pane_tree, window_pane, tree_entry, window_pane_cmp);
79ae69181dSnicm 
80ae69181dSnicm int
81ae69181dSnicm window_cmp(struct window *w1, struct window *w2)
82ae69181dSnicm {
83ae69181dSnicm 	return (w1->id - w2->id);
84ae69181dSnicm }
85ae69181dSnicm 
86311827fbSnicm int
87311827fbSnicm winlink_cmp(struct winlink *wl1, struct winlink *wl2)
88311827fbSnicm {
89311827fbSnicm 	return (wl1->idx - wl2->idx);
90311827fbSnicm }
91311827fbSnicm 
923e0f2533Snicm int
933e0f2533Snicm window_pane_cmp(struct window_pane *wp1, struct window_pane *wp2)
943e0f2533Snicm {
953e0f2533Snicm 	return (wp1->id - wp2->id);
963e0f2533Snicm }
973e0f2533Snicm 
98311827fbSnicm struct winlink *
99e63d884aSnicm winlink_find_by_window(struct winlinks *wwl, struct window *w)
100e63d884aSnicm {
101e63d884aSnicm 	struct winlink	*wl;
102e63d884aSnicm 
103e63d884aSnicm 	RB_FOREACH(wl, winlinks, wwl) {
104e63d884aSnicm 		if (wl->window == w)
105e63d884aSnicm 			return (wl);
106e63d884aSnicm 	}
107e63d884aSnicm 
108e63d884aSnicm 	return (NULL);
109e63d884aSnicm }
110e63d884aSnicm 
111e63d884aSnicm struct winlink *
112311827fbSnicm winlink_find_by_index(struct winlinks *wwl, int idx)
113311827fbSnicm {
114311827fbSnicm 	struct winlink	wl;
115311827fbSnicm 
116311827fbSnicm 	if (idx < 0)
117311827fbSnicm 		fatalx("bad index");
118311827fbSnicm 
119311827fbSnicm 	wl.idx = idx;
120311827fbSnicm 	return (RB_FIND(winlinks, wwl, &wl));
121311827fbSnicm }
122311827fbSnicm 
123abb1e3d2Snicm struct winlink *
124abb1e3d2Snicm winlink_find_by_window_id(struct winlinks *wwl, u_int id)
125abb1e3d2Snicm {
126abb1e3d2Snicm 	struct winlink *wl;
127abb1e3d2Snicm 
128abb1e3d2Snicm 	RB_FOREACH(wl, winlinks, wwl) {
129abb1e3d2Snicm 		if (wl->window->id == id)
130abb1e3d2Snicm 			return (wl);
131abb1e3d2Snicm 	}
132d083c389Snicm 	return (NULL);
133abb1e3d2Snicm }
134abb1e3d2Snicm 
135ced21769Snicm static int
136bc7e6300Snicm winlink_next_index(struct winlinks *wwl, int idx)
137311827fbSnicm {
138bc7e6300Snicm 	int	i;
139311827fbSnicm 
140bc7e6300Snicm 	i = idx;
141bc7e6300Snicm 	do {
142311827fbSnicm 		if (winlink_find_by_index(wwl, i) == NULL)
143311827fbSnicm 			return (i);
144bc7e6300Snicm 		if (i == INT_MAX)
145bc7e6300Snicm 			i = 0;
146bc7e6300Snicm 		else
147bc7e6300Snicm 			i++;
148bc7e6300Snicm 	} while (i != idx);
149bc7e6300Snicm 	return (-1);
150311827fbSnicm }
151311827fbSnicm 
152311827fbSnicm u_int
153311827fbSnicm winlink_count(struct winlinks *wwl)
154311827fbSnicm {
155311827fbSnicm 	struct winlink	*wl;
156311827fbSnicm 	u_int		 n;
157311827fbSnicm 
158311827fbSnicm 	n = 0;
159311827fbSnicm 	RB_FOREACH(wl, winlinks, wwl)
160311827fbSnicm 		n++;
161311827fbSnicm 
162311827fbSnicm 	return (n);
163311827fbSnicm }
164311827fbSnicm 
165311827fbSnicm struct winlink *
16613f42f0aSnicm winlink_add(struct winlinks *wwl, int idx)
167311827fbSnicm {
168311827fbSnicm 	struct winlink	*wl;
169311827fbSnicm 
170bc7e6300Snicm 	if (idx < 0) {
171bc7e6300Snicm 		if ((idx = winlink_next_index(wwl, -idx - 1)) == -1)
172311827fbSnicm 			return (NULL);
173bc7e6300Snicm 	} else if (winlink_find_by_index(wwl, idx) != NULL)
174bc7e6300Snicm 		return (NULL);
175311827fbSnicm 
176311827fbSnicm 	wl = xcalloc(1, sizeof *wl);
177311827fbSnicm 	wl->idx = idx;
178311827fbSnicm 	RB_INSERT(winlinks, wwl, wl);
179311827fbSnicm 
180311827fbSnicm 	return (wl);
181311827fbSnicm }
182311827fbSnicm 
183311827fbSnicm void
18413f42f0aSnicm winlink_set_window(struct winlink *wl, struct window *w)
18513f42f0aSnicm {
186de5a0fddSnicm 	if (wl->window != NULL) {
187de5a0fddSnicm 		TAILQ_REMOVE(&wl->window->winlinks, wl, wentry);
18854279ec3Snicm 		window_remove_ref(wl->window, __func__);
189de5a0fddSnicm 	}
190de5a0fddSnicm 	TAILQ_INSERT_TAIL(&w->winlinks, wl, wentry);
19113f42f0aSnicm 	wl->window = w;
19254279ec3Snicm 	window_add_ref(w, __func__);
19313f42f0aSnicm }
19413f42f0aSnicm 
19513f42f0aSnicm void
196311827fbSnicm winlink_remove(struct winlinks *wwl, struct winlink *wl)
197311827fbSnicm {
198311827fbSnicm 	struct window	*w = wl->window;
199311827fbSnicm 
200de5a0fddSnicm 	if (w != NULL) {
201de5a0fddSnicm 		TAILQ_REMOVE(&w->winlinks, wl, wentry);
20254279ec3Snicm 		window_remove_ref(w, __func__);
203de5a0fddSnicm 	}
204de5a0fddSnicm 
205311827fbSnicm 	RB_REMOVE(winlinks, wwl, wl);
2067d053cf9Snicm 	free(wl);
20713f42f0aSnicm }
208311827fbSnicm 
209311827fbSnicm struct winlink *
2105c8958bdSnicm winlink_next(struct winlink *wl)
211311827fbSnicm {
212311827fbSnicm 	return (RB_NEXT(winlinks, wwl, wl));
213311827fbSnicm }
214311827fbSnicm 
215311827fbSnicm struct winlink *
2165c8958bdSnicm winlink_previous(struct winlink *wl)
217311827fbSnicm {
218311827fbSnicm 	return (RB_PREV(winlinks, wwl, wl));
219311827fbSnicm }
220311827fbSnicm 
2211cabf2d5Snicm struct winlink *
222385f8052Snicm winlink_next_by_number(struct winlink *wl, struct session *s, int n)
2231cabf2d5Snicm {
2241cabf2d5Snicm 	for (; n > 0; n--) {
2251cabf2d5Snicm 		if ((wl = RB_NEXT(winlinks, wwl, wl)) == NULL)
226385f8052Snicm 			wl = RB_MIN(winlinks, &s->windows);
2271cabf2d5Snicm 	}
2281cabf2d5Snicm 
2291cabf2d5Snicm 	return (wl);
2301cabf2d5Snicm }
2311cabf2d5Snicm 
2321cabf2d5Snicm struct winlink *
233385f8052Snicm winlink_previous_by_number(struct winlink *wl, struct session *s, int n)
2341cabf2d5Snicm {
2351cabf2d5Snicm 	for (; n > 0; n--) {
2361cabf2d5Snicm 		if ((wl = RB_PREV(winlinks, wwl, wl)) == NULL)
237385f8052Snicm 			wl = RB_MAX(winlinks, &s->windows);
2381cabf2d5Snicm 	}
2391cabf2d5Snicm 
2401cabf2d5Snicm 	return (wl);
2411cabf2d5Snicm }
2421cabf2d5Snicm 
243311827fbSnicm void
244311827fbSnicm winlink_stack_push(struct winlink_stack *stack, struct winlink *wl)
245311827fbSnicm {
246311827fbSnicm 	if (wl == NULL)
247311827fbSnicm 		return;
248311827fbSnicm 
249311827fbSnicm 	winlink_stack_remove(stack, wl);
25001b2421eSnicm 	TAILQ_INSERT_HEAD(stack, wl, sentry);
251a0aa010aSnicm 	wl->flags |= WINLINK_VISITED;
252311827fbSnicm }
253311827fbSnicm 
254311827fbSnicm void
255311827fbSnicm winlink_stack_remove(struct winlink_stack *stack, struct winlink *wl)
256311827fbSnicm {
257a0aa010aSnicm 	if (wl != NULL && (wl->flags & WINLINK_VISITED)) {
25801b2421eSnicm 		TAILQ_REMOVE(stack, wl, sentry);
259a0aa010aSnicm 		wl->flags &= ~WINLINK_VISITED;
260311827fbSnicm 	}
261311827fbSnicm }
262311827fbSnicm 
263311827fbSnicm struct window *
2643b2ac4f9Snicm window_find_by_id_str(const char *s)
2653b2ac4f9Snicm {
2663b2ac4f9Snicm 	const char	*errstr;
2673b2ac4f9Snicm 	u_int		 id;
2683b2ac4f9Snicm 
2693b2ac4f9Snicm 	if (*s != '@')
2703b2ac4f9Snicm 		return (NULL);
2713b2ac4f9Snicm 
2723b2ac4f9Snicm 	id = strtonum(s + 1, 0, UINT_MAX, &errstr);
2733b2ac4f9Snicm 	if (errstr != NULL)
2743b2ac4f9Snicm 		return (NULL);
2753b2ac4f9Snicm 	return (window_find_by_id(id));
2763b2ac4f9Snicm }
2773b2ac4f9Snicm 
2783b2ac4f9Snicm struct window *
279abb1e3d2Snicm window_find_by_id(u_int id)
280abb1e3d2Snicm {
281ae69181dSnicm 	struct window	w;
282abb1e3d2Snicm 
283ae69181dSnicm 	w.id = id;
284ae69181dSnicm 	return (RB_FIND(windows, &windows, &w));
285abb1e3d2Snicm }
286abb1e3d2Snicm 
28781fe4598Snicm void
28881fe4598Snicm window_update_activity(struct window *w)
28981fe4598Snicm {
29081fe4598Snicm 	gettimeofday(&w->activity_time, NULL);
29181fe4598Snicm 	alerts_queue(w, WINDOW_ACTIVITY);
29281fe4598Snicm }
29381fe4598Snicm 
294abb1e3d2Snicm struct window *
2954a8b0ea5Snicm window_create(u_int sx, u_int sy, u_int xpixel, u_int ypixel)
296311827fbSnicm {
297311827fbSnicm 	struct window	*w;
298311827fbSnicm 
2994a8b0ea5Snicm 	if (xpixel == 0)
3004a8b0ea5Snicm 		xpixel = DEFAULT_XPIXEL;
3014a8b0ea5Snicm 	if (ypixel == 0)
3024a8b0ea5Snicm 		ypixel = DEFAULT_YPIXEL;
3034a8b0ea5Snicm 
30430567b26Snicm 	w = xcalloc(1, sizeof *w);
30505266a29Snicm 	w->name = xstrdup("");
3066e0f28f8Snicm 	w->flags = 0;
307311827fbSnicm 
308311827fbSnicm 	TAILQ_INIT(&w->panes);
309a0aa010aSnicm 	TAILQ_INIT(&w->last_panes);
310311827fbSnicm 	w->active = NULL;
311af9e4c5dSnicm 
312ba17146dSnicm 	w->lastlayout = -1;
313af9e4c5dSnicm 	w->layout_root = NULL;
314311827fbSnicm 
315311827fbSnicm 	w->sx = sx;
316311827fbSnicm 	w->sy = sy;
31734ce0afcSnicm 	w->manual_sx = sx;
31834ce0afcSnicm 	w->manual_sy = sy;
3194a8b0ea5Snicm 	w->xpixel = xpixel;
3204a8b0ea5Snicm 	w->ypixel = ypixel;
321311827fbSnicm 
322d89252e5Snicm 	w->options = options_create(global_w_options);
323311827fbSnicm 
324311827fbSnicm 	w->references = 0;
325de5a0fddSnicm 	TAILQ_INIT(&w->winlinks);
326311827fbSnicm 
327ae69181dSnicm 	w->id = next_window_id++;
328ae69181dSnicm 	RB_INSERT(windows, &windows, w);
329ae69181dSnicm 
3302c25e4b4Snicm 	window_set_fill_character(w);
33181fe4598Snicm 	window_update_activity(w);
33281fe4598Snicm 
3332077c06bSnicm 	log_debug("%s: @%u create %ux%u (%ux%u)", __func__, w->id, sx, sy,
3342077c06bSnicm 	    w->xpixel, w->ypixel);
335311827fbSnicm 	return (w);
336311827fbSnicm }
337311827fbSnicm 
3387ecc8255Snicm static void
339311827fbSnicm window_destroy(struct window *w)
340311827fbSnicm {
34154279ec3Snicm 	log_debug("window @%u destroyed (%d references)", w->id, w->references);
342de5a0fddSnicm 
3430ad0daf4Snicm 	window_unzoom(w, 0);
344ae69181dSnicm 	RB_REMOVE(windows, &windows, w);
345311827fbSnicm 
346af9e4c5dSnicm 	if (w->layout_root != NULL)
347fe6d0ff2Snicm 		layout_free_cell(w->layout_root);
348fe6d0ff2Snicm 	if (w->saved_layout_root != NULL)
349fe6d0ff2Snicm 		layout_free_cell(w->saved_layout_root);
350edb21bd1Snicm 	free(w->old_layout);
351af9e4c5dSnicm 
352cdfce789Snicm 	window_destroy_panes(w);
353cdfce789Snicm 
35400b2deccSnicm 	if (event_initialized(&w->name_event))
35500b2deccSnicm 		evtimer_del(&w->name_event);
356f5015e68Snicm 
35781fe4598Snicm 	if (event_initialized(&w->alerts_timer))
35881fe4598Snicm 		evtimer_del(&w->alerts_timer);
3597b470e93Snicm 	if (event_initialized(&w->offset_timer))
3607b470e93Snicm 		event_del(&w->offset_timer);
36181fe4598Snicm 
362d89252e5Snicm 	options_free(w->options);
3632c25e4b4Snicm 	free(w->fill_character);
364311827fbSnicm 
3657d053cf9Snicm 	free(w->name);
3667d053cf9Snicm 	free(w);
367311827fbSnicm }
368311827fbSnicm 
369f35e5f52Snicm int
370f35e5f52Snicm window_pane_destroy_ready(struct window_pane *wp)
371f35e5f52Snicm {
3729017429cSnicm 	int	n;
3739017429cSnicm 
3749017429cSnicm 	if (wp->pipe_fd != -1) {
3759017429cSnicm 		if (EVBUFFER_LENGTH(wp->pipe_event->output) != 0)
376f35e5f52Snicm 			return (0);
3779017429cSnicm 		if (ioctl(wp->fd, FIONREAD, &n) != -1 && n > 0)
3789017429cSnicm 			return (0);
3799017429cSnicm 	}
380f35e5f52Snicm 
381f35e5f52Snicm 	if (~wp->flags & PANE_EXITED)
382f35e5f52Snicm 		return (0);
383f35e5f52Snicm 	return (1);
384f35e5f52Snicm }
385f35e5f52Snicm 
386980b7663Snicm void
38754279ec3Snicm window_add_ref(struct window *w, const char *from)
388a3560846Snicm {
38954279ec3Snicm 	w->references++;
39054279ec3Snicm 	log_debug("%s: @%u %s, now %d", __func__, w->id, from, w->references);
39154279ec3Snicm }
39254279ec3Snicm 
39354279ec3Snicm void
39454279ec3Snicm window_remove_ref(struct window *w, const char *from)
39554279ec3Snicm {
396a3560846Snicm 	w->references--;
39754279ec3Snicm 	log_debug("%s: @%u %s, now %d", __func__, w->id, from, w->references);
39854279ec3Snicm 
399a3560846Snicm 	if (w->references == 0)
400a3560846Snicm 		window_destroy(w);
401a3560846Snicm }
402a3560846Snicm 
403a3560846Snicm void
404fb51d899Snicm window_set_name(struct window *w, const char *new_name)
405fb51d899Snicm {
4067d053cf9Snicm 	free(w->name);
4079d9ffcabSnicm 	utf8_stravis(&w->name, new_name, VIS_OCTAL|VIS_CSTYLE|VIS_TAB|VIS_NL);
4082ae124feSnicm 	notify_window("window-renamed", w);
409fb51d899Snicm }
410fb51d899Snicm 
411fb51d899Snicm void
4124a8b0ea5Snicm window_resize(struct window *w, u_int sx, u_int sy, int xpixel, int ypixel)
413311827fbSnicm {
4144a8b0ea5Snicm 	if (xpixel == 0)
4154a8b0ea5Snicm 		xpixel = DEFAULT_XPIXEL;
4164a8b0ea5Snicm 	if (ypixel == 0)
4174a8b0ea5Snicm 		ypixel = DEFAULT_YPIXEL;
4184a8b0ea5Snicm 
4194a8b0ea5Snicm 	log_debug("%s: @%u resize %ux%u (%ux%u)", __func__, w->id, sx, sy,
4200b4cfe0bSnicm 	    xpixel == -1 ? w->xpixel : (u_int)xpixel,
4210b4cfe0bSnicm 	    ypixel == -1 ? w->ypixel : (u_int)ypixel);
422311827fbSnicm 	w->sx = sx;
423311827fbSnicm 	w->sy = sy;
4244a8b0ea5Snicm 	if (xpixel != -1)
4254a8b0ea5Snicm 		w->xpixel = xpixel;
4264a8b0ea5Snicm 	if (ypixel != -1)
4274a8b0ea5Snicm 		w->ypixel = ypixel;
4284a8b0ea5Snicm }
4294a8b0ea5Snicm 
4304a8b0ea5Snicm void
4317592946fSnicm window_pane_send_resize(struct window_pane *wp, u_int sx, u_int sy)
4324a8b0ea5Snicm {
4334a8b0ea5Snicm 	struct window	*w = wp->window;
4344a8b0ea5Snicm 	struct winsize	 ws;
4354a8b0ea5Snicm 
4364a8b0ea5Snicm 	if (wp->fd == -1)
4374a8b0ea5Snicm 		return;
43849cd5680Snicm 
4397592946fSnicm 	log_debug("%s: %%%u resize to %u,%u", __func__, wp->id, sx, sy);
4404a8b0ea5Snicm 
4414a8b0ea5Snicm 	memset(&ws, 0, sizeof ws);
4427592946fSnicm 	ws.ws_col = sx;
443061703b1Snicm 	ws.ws_row = sy;
4444a8b0ea5Snicm 	ws.ws_xpixel = w->xpixel * ws.ws_col;
4454a8b0ea5Snicm 	ws.ws_ypixel = w->ypixel * ws.ws_row;
4464a8b0ea5Snicm 	if (ioctl(wp->fd, TIOCSWINSZ, &ws) == -1)
4474a8b0ea5Snicm 		fatal("ioctl failed");
448311827fbSnicm }
449311827fbSnicm 
450a00af3f5Snicm int
451e048bb79Snicm window_has_pane(struct window *w, struct window_pane *wp)
452e048bb79Snicm {
453e048bb79Snicm 	struct window_pane	*wp1;
454e048bb79Snicm 
455e048bb79Snicm 	TAILQ_FOREACH(wp1, &w->panes, entry) {
456e048bb79Snicm 		if (wp1 == wp)
457e048bb79Snicm 			return (1);
458e048bb79Snicm 	}
459e048bb79Snicm 	return (0);
460e048bb79Snicm }
461e048bb79Snicm 
4621a773291Snicm void
4631a773291Snicm window_update_focus(struct window *w)
4641a773291Snicm {
4651a773291Snicm 	if (w != NULL) {
4661a773291Snicm 		log_debug("%s: @%u", __func__, w->id);
4671a773291Snicm 		window_pane_update_focus(w->active);
4681a773291Snicm 	}
4691a773291Snicm }
4701a773291Snicm 
4711a773291Snicm void
4721a773291Snicm window_pane_update_focus(struct window_pane *wp)
4731a773291Snicm {
4741a773291Snicm 	struct client	*c;
4751a773291Snicm 	int		 focused = 0;
4761a773291Snicm 
477ffe6655cSnicm 	if (wp != NULL && (~wp->flags & PANE_EXITED)) {
4781a773291Snicm 		if (wp != wp->window->active)
4791a773291Snicm 			focused = 0;
4801a773291Snicm 		else {
4811a773291Snicm 			TAILQ_FOREACH(c, &clients, entry) {
4821a773291Snicm 				if (c->session != NULL &&
4831a773291Snicm 				    c->session->attached != 0 &&
4841a773291Snicm 				    (c->flags & CLIENT_FOCUSED) &&
485502b52cbSnicm 				    c->session->curw->window == wp->window &&
486502b52cbSnicm 				    c->overlay_draw == NULL) {
4871a773291Snicm 					focused = 1;
4881a773291Snicm 					break;
4891a773291Snicm 				}
4901a773291Snicm 			}
4911a773291Snicm 		}
4921a773291Snicm 		if (!focused && (wp->flags & PANE_FOCUSED)) {
4931a773291Snicm 			log_debug("%s: %%%u focus out", __func__, wp->id);
4941a773291Snicm 			if (wp->base.mode & MODE_FOCUSON)
4951a773291Snicm 				bufferevent_write(wp->event, "\033[O", 3);
4961a773291Snicm 			notify_pane("pane-focus-out", wp);
4971a773291Snicm 			wp->flags &= ~PANE_FOCUSED;
4981a773291Snicm 		} else if (focused && (~wp->flags & PANE_FOCUSED)) {
4991a773291Snicm 			log_debug("%s: %%%u focus in", __func__, wp->id);
5001a773291Snicm 			if (wp->base.mode & MODE_FOCUSON)
5011a773291Snicm 				bufferevent_write(wp->event, "\033[I", 3);
5021a773291Snicm 			notify_pane("pane-focus-in", wp);
5031a773291Snicm 			wp->flags |= PANE_FOCUSED;
5041a773291Snicm 		} else
5051a773291Snicm 			log_debug("%s: %%%u focus unchanged", __func__, wp->id);
5061a773291Snicm 	}
5071a773291Snicm }
5081a773291Snicm 
509e048bb79Snicm int
510c26c4f79Snicm window_set_active_pane(struct window *w, struct window_pane *wp, int notify)
511311827fbSnicm {
512a0aa010aSnicm 	struct window_pane *lastwp;
513a0aa010aSnicm 
514c26c4f79Snicm 	log_debug("%s: pane %%%u", __func__, wp->id);
515c26c4f79Snicm 
5167bf60b74Snicm 	if (wp == w->active)
517a00af3f5Snicm 		return (0);
518a0aa010aSnicm 	lastwp = w->active;
519a0aa010aSnicm 
520a0aa010aSnicm 	window_pane_stack_remove(&w->last_panes, wp);
521a0aa010aSnicm 	window_pane_stack_push(&w->last_panes, lastwp);
522c26c4f79Snicm 
523311827fbSnicm 	w->active = wp;
5248615a3a4Snicm 	w->active->active_point = next_active_point++;
525dbd9724dSnicm 	w->active->flags |= PANE_CHANGED;
526c26c4f79Snicm 
5271a773291Snicm 	if (options_get_number(global_options, "focus-events")) {
528a0aa010aSnicm 		window_pane_update_focus(lastwp);
5291a773291Snicm 		window_pane_update_focus(w->active);
5301a773291Snicm 	}
5311a773291Snicm 
5327b470e93Snicm 	tty_update_window_offset(w);
533c26c4f79Snicm 
534c26c4f79Snicm 	if (notify)
5353c14ce20Snicm 		notify_window("window-pane-changed", w);
536a00af3f5Snicm 	return (1);
537311827fbSnicm }
538311827fbSnicm 
53933a1e283Snicm static int
54033a1e283Snicm window_pane_get_palette(struct window_pane *wp, int c)
54133a1e283Snicm {
54233a1e283Snicm 	if (wp == NULL)
54333a1e283Snicm 		return (-1);
54433a1e283Snicm 	return (colour_palette_get(&wp->palette, c));
54533a1e283Snicm }
54633a1e283Snicm 
54718c28068Snicm void
54818c28068Snicm window_redraw_active_switch(struct window *w, struct window_pane *wp)
54918c28068Snicm {
550bf46d0ceSnicm 	struct grid_cell	*gc1, *gc2;
5516e0f28f8Snicm 	int			 c1, c2;
55218c28068Snicm 
55318c28068Snicm 	if (wp == w->active)
55418c28068Snicm 		return;
55518c28068Snicm 
5566e0f28f8Snicm 	for (;;) {
55718c28068Snicm 		/*
5586e0f28f8Snicm 		 * If the active and inactive styles or palettes are different,
5596e0f28f8Snicm 		 * need to redraw the panes.
56018c28068Snicm 		 */
561bf46d0ceSnicm 		gc1 = &wp->cached_gc;
562bf46d0ceSnicm 		gc2 = &wp->cached_active_gc;
563bf46d0ceSnicm 		if (!grid_cells_look_equal(gc1, gc2))
56418c28068Snicm 			wp->flags |= PANE_REDRAW;
5656e0f28f8Snicm 		else {
566bf46d0ceSnicm 			c1 = window_pane_get_palette(wp, gc1->fg);
567bf46d0ceSnicm 			c2 = window_pane_get_palette(wp, gc2->fg);
5686e0f28f8Snicm 			if (c1 != c2)
5696e0f28f8Snicm 				wp->flags |= PANE_REDRAW;
5706e0f28f8Snicm 			else {
571bf46d0ceSnicm 				c1 = window_pane_get_palette(wp, gc1->bg);
572bf46d0ceSnicm 				c2 = window_pane_get_palette(wp, gc2->bg);
5736e0f28f8Snicm 				if (c1 != c2)
5746e0f28f8Snicm 					wp->flags |= PANE_REDRAW;
5756e0f28f8Snicm 			}
5766e0f28f8Snicm 		}
5776e0f28f8Snicm 		if (wp == w->active)
5786e0f28f8Snicm 			break;
5796e0f28f8Snicm 		wp = w->active;
5806e0f28f8Snicm 	}
58118c28068Snicm }
58218c28068Snicm 
5839c4e01faSnicm struct window_pane *
5849c4e01faSnicm window_get_active_at(struct window *w, u_int x, u_int y)
5859c4e01faSnicm {
5869c4e01faSnicm 	struct window_pane	*wp;
587d2117533Snicm 	int			 pane_scrollbars;
588d2117533Snicm 	u_int			 sb_pos, sb_w, xoff, sx;
589d2117533Snicm 
590d2117533Snicm 	pane_scrollbars = options_get_number(w->options, "pane-scrollbars");
591d2117533Snicm 	sb_pos = options_get_number(w->options, "pane-scrollbars-position");
5929c4e01faSnicm 
5939c4e01faSnicm 	TAILQ_FOREACH(wp, &w->panes, entry) {
5949c4e01faSnicm 		if (!window_pane_visible(wp))
5959c4e01faSnicm 			continue;
596d2117533Snicm 
597d2117533Snicm 		if (pane_scrollbars == PANE_SCROLLBARS_ALWAYS ||
598d2117533Snicm 		    (pane_scrollbars == PANE_SCROLLBARS_MODAL &&
599dd0df669Snicm 		     window_pane_mode(wp) != WINDOW_PANE_NO_MODE)) {
600dd0df669Snicm 			sb_w = wp->scrollbar_style.width +
601dd0df669Snicm 			    wp->scrollbar_style.pad;
602dd0df669Snicm 		} else
603d2117533Snicm 			sb_w = 0;
604d2117533Snicm 
605d2117533Snicm 		if (sb_pos == PANE_SCROLLBARS_LEFT) {
606d2117533Snicm 			xoff = wp->xoff - sb_w;
607d2117533Snicm 			sx = wp->sx + sb_w;
608d2117533Snicm 		} else { /* sb_pos == PANE_SCROLLBARS_RIGHT */
609d2117533Snicm 			xoff = wp->xoff;
610d2117533Snicm 			sx = wp->sx + sb_w;
611d2117533Snicm 		}
612d2117533Snicm 		if (x < xoff || x > xoff + sx)
6139c4e01faSnicm 			continue;
6149c4e01faSnicm 		if (y < wp->yoff || y > wp->yoff + wp->sy)
6159c4e01faSnicm 			continue;
6169c4e01faSnicm 		return (wp);
6179c4e01faSnicm 	}
6189c4e01faSnicm 	return (NULL);
6199c4e01faSnicm }
6209c4e01faSnicm 
6219c4e01faSnicm struct window_pane *
6229c4e01faSnicm window_find_string(struct window *w, const char *s)
6239c4e01faSnicm {
6249a3c4d25Snicm 	u_int	x, y, top = 0, bottom = w->sy - 1;
6259a3c4d25Snicm 	int	status;
6269c4e01faSnicm 
6279c4e01faSnicm 	x = w->sx / 2;
6289c4e01faSnicm 	y = w->sy / 2;
6299c4e01faSnicm 
6309a3c4d25Snicm 	status = options_get_number(w->options, "pane-border-status");
6319a3c4d25Snicm 	if (status == PANE_STATUS_TOP)
6329a3c4d25Snicm 		top++;
6339a3c4d25Snicm 	else if (status == PANE_STATUS_BOTTOM)
6349a3c4d25Snicm 		bottom--;
6359a3c4d25Snicm 
6369c4e01faSnicm 	if (strcasecmp(s, "top") == 0)
6379a3c4d25Snicm 		y = top;
6389c4e01faSnicm 	else if (strcasecmp(s, "bottom") == 0)
6399a3c4d25Snicm 		y = bottom;
6409c4e01faSnicm 	else if (strcasecmp(s, "left") == 0)
6419c4e01faSnicm 		x = 0;
6429c4e01faSnicm 	else if (strcasecmp(s, "right") == 0)
6439c4e01faSnicm 		x = w->sx - 1;
6449c4e01faSnicm 	else if (strcasecmp(s, "top-left") == 0) {
6459c4e01faSnicm 		x = 0;
6469a3c4d25Snicm 		y = top;
6479c4e01faSnicm 	} else if (strcasecmp(s, "top-right") == 0) {
6489c4e01faSnicm 		x = w->sx - 1;
6499a3c4d25Snicm 		y = top;
6509c4e01faSnicm 	} else if (strcasecmp(s, "bottom-left") == 0) {
6519c4e01faSnicm 		x = 0;
6529a3c4d25Snicm 		y = bottom;
6539c4e01faSnicm 	} else if (strcasecmp(s, "bottom-right") == 0) {
6549c4e01faSnicm 		x = w->sx - 1;
6559a3c4d25Snicm 		y = bottom;
6569c4e01faSnicm 	} else
6579c4e01faSnicm 		return (NULL);
6589c4e01faSnicm 
6599c4e01faSnicm 	return (window_get_active_at(w, x, y));
6606f0d6aceSnicm }
6616f0d6aceSnicm 
662b4a3311eSnicm int
663b4a3311eSnicm window_zoom(struct window_pane *wp)
664b4a3311eSnicm {
665b4a3311eSnicm 	struct window		*w = wp->window;
666b4a3311eSnicm 	struct window_pane	*wp1;
667b4a3311eSnicm 
668b4a3311eSnicm 	if (w->flags & WINDOW_ZOOMED)
669b4a3311eSnicm 		return (-1);
670b4a3311eSnicm 
6711de5dd9dSnicm 	if (window_count_panes(w) == 1)
6721de5dd9dSnicm 		return (-1);
6731de5dd9dSnicm 
674b4a3311eSnicm 	if (w->active != wp)
675c26c4f79Snicm 		window_set_active_pane(w, wp, 1);
676b4a3311eSnicm 
677b4a3311eSnicm 	TAILQ_FOREACH(wp1, &w->panes, entry) {
678b4a3311eSnicm 		wp1->saved_layout_cell = wp1->layout_cell;
679b4a3311eSnicm 		wp1->layout_cell = NULL;
680b4a3311eSnicm 	}
681b4a3311eSnicm 
682b4a3311eSnicm 	w->saved_layout_root = w->layout_root;
683b4a3311eSnicm 	layout_init(w, wp);
684b4a3311eSnicm 	w->flags |= WINDOW_ZOOMED;
6852ae124feSnicm 	notify_window("window-layout-changed", w);
686b4a3311eSnicm 
687b4a3311eSnicm 	return (0);
688b4a3311eSnicm }
689b4a3311eSnicm 
690b4a3311eSnicm int
6910ad0daf4Snicm window_unzoom(struct window *w, int notify)
692b4a3311eSnicm {
6936ba5a684Snicm 	struct window_pane	*wp;
694b4a3311eSnicm 
695b4a3311eSnicm 	if (!(w->flags & WINDOW_ZOOMED))
696b4a3311eSnicm 		return (-1);
697b4a3311eSnicm 
698b4a3311eSnicm 	w->flags &= ~WINDOW_ZOOMED;
699b4a3311eSnicm 	layout_free(w);
700b4a3311eSnicm 	w->layout_root = w->saved_layout_root;
701b3e07ccdSnicm 	w->saved_layout_root = NULL;
702b4a3311eSnicm 
7036ba5a684Snicm 	TAILQ_FOREACH(wp, &w->panes, entry) {
7046ba5a684Snicm 		wp->layout_cell = wp->saved_layout_cell;
7056ba5a684Snicm 		wp->saved_layout_cell = NULL;
706b4a3311eSnicm 	}
707baddd6b2Snicm 	layout_fix_panes(w, NULL);
7080ad0daf4Snicm 
7090ad0daf4Snicm 	if (notify)
7102ae124feSnicm 		notify_window("window-layout-changed", w);
711b4a3311eSnicm 
712b4a3311eSnicm 	return (0);
713b4a3311eSnicm }
714b4a3311eSnicm 
715721430b8Snicm int
716baddd6b2Snicm window_push_zoom(struct window *w, int always, int flag)
717721430b8Snicm {
718721430b8Snicm 	log_debug("%s: @%u %d", __func__, w->id,
719721430b8Snicm 	    flag && (w->flags & WINDOW_ZOOMED));
720baddd6b2Snicm 	if (flag && (always || (w->flags & WINDOW_ZOOMED)))
721721430b8Snicm 		w->flags |= WINDOW_WASZOOMED;
722721430b8Snicm 	else
723721430b8Snicm 		w->flags &= ~WINDOW_WASZOOMED;
7240ad0daf4Snicm 	return (window_unzoom(w, 1) == 0);
725721430b8Snicm }
726721430b8Snicm 
727721430b8Snicm int
728721430b8Snicm window_pop_zoom(struct window *w)
729721430b8Snicm {
730721430b8Snicm 	log_debug("%s: @%u %d", __func__, w->id,
731721430b8Snicm 	    !!(w->flags & WINDOW_WASZOOMED));
732721430b8Snicm 	if (w->flags & WINDOW_WASZOOMED)
733721430b8Snicm 		return (window_zoom(w->active) == 0);
734721430b8Snicm 	return (0);
735721430b8Snicm }
736721430b8Snicm 
737311827fbSnicm struct window_pane *
738c26c4f79Snicm window_add_pane(struct window *w, struct window_pane *other, u_int hlimit,
739c26c4f79Snicm     int flags)
740311827fbSnicm {
741311827fbSnicm 	struct window_pane	*wp;
742311827fbSnicm 
743f241812aSnicm 	if (other == NULL)
744f241812aSnicm 		other = w->active;
745f241812aSnicm 
746af9e4c5dSnicm 	wp = window_pane_create(w, w->sx, w->sy, hlimit);
747766e1650Snicm 	if (TAILQ_EMPTY(&w->panes)) {
748766e1650Snicm 		log_debug("%s: @%u at start", __func__, w->id);
749311827fbSnicm 		TAILQ_INSERT_HEAD(&w->panes, wp, entry);
750c26c4f79Snicm 	} else if (flags & SPAWN_BEFORE) {
751766e1650Snicm 		log_debug("%s: @%u before %%%u", __func__, w->id, wp->id);
752c26c4f79Snicm 		if (flags & SPAWN_FULLSIZE)
753e7b8083aSnicm 			TAILQ_INSERT_HEAD(&w->panes, wp, entry);
754e7b8083aSnicm 		else
755f241812aSnicm 			TAILQ_INSERT_BEFORE(other, wp, entry);
756766e1650Snicm 	} else {
757766e1650Snicm 		log_debug("%s: @%u after %%%u", __func__, w->id, wp->id);
758c26c4f79Snicm 		if (flags & SPAWN_FULLSIZE)
759e7b8083aSnicm 			TAILQ_INSERT_TAIL(&w->panes, wp, entry);
760e7b8083aSnicm 		else
761f241812aSnicm 			TAILQ_INSERT_AFTER(&w->panes, other, wp, entry);
762766e1650Snicm 	}
763311827fbSnicm 	return (wp);
764311827fbSnicm }
765311827fbSnicm 
766311827fbSnicm void
767e2f09fc8Snicm window_lost_pane(struct window *w, struct window_pane *wp)
768311827fbSnicm {
769b81c3e5bSnicm 	log_debug("%s: @%u pane %%%u", __func__, w->id, wp->id);
770b81c3e5bSnicm 
7717519eda3Snicm 	if (wp == marked_pane.wp)
7722986c025Snicm 		server_clear_marked();
7732986c025Snicm 
774a0aa010aSnicm 	window_pane_stack_remove(&w->last_panes, wp);
7759c5bcde4Snicm 	if (wp == w->active) {
776a0aa010aSnicm 		w->active = TAILQ_FIRST(&w->last_panes);
7772bb1dad1Snicm 		if (w->active == NULL) {
778311827fbSnicm 			w->active = TAILQ_PREV(wp, window_panes, entry);
779311827fbSnicm 			if (w->active == NULL)
780311827fbSnicm 				w->active = TAILQ_NEXT(wp, entry);
7819c5bcde4Snicm 		}
7823c14ce20Snicm 		if (w->active != NULL) {
783a0aa010aSnicm 			window_pane_stack_remove(&w->last_panes, w->active);
78435b300b4Snicm 			w->active->flags |= PANE_CHANGED;
7853c14ce20Snicm 			notify_window("window-pane-changed", w);
7860b716d3aSnicm 			window_update_focus(w);
7873c14ce20Snicm 		}
788a0aa010aSnicm 	}
789e2f09fc8Snicm }
790e2f09fc8Snicm 
791e2f09fc8Snicm void
792e2f09fc8Snicm window_remove_pane(struct window *w, struct window_pane *wp)
793e2f09fc8Snicm {
794e2f09fc8Snicm 	window_lost_pane(w, wp);
795311827fbSnicm 
796311827fbSnicm 	TAILQ_REMOVE(&w->panes, wp, entry);
797311827fbSnicm 	window_pane_destroy(wp);
798311827fbSnicm }
799311827fbSnicm 
800311827fbSnicm struct window_pane *
801311827fbSnicm window_pane_at_index(struct window *w, u_int idx)
802311827fbSnicm {
803311827fbSnicm 	struct window_pane	*wp;
804311827fbSnicm 	u_int			 n;
805311827fbSnicm 
806d89252e5Snicm 	n = options_get_number(w->options, "pane-base-index");
807311827fbSnicm 	TAILQ_FOREACH(wp, &w->panes, entry) {
808311827fbSnicm 		if (n == idx)
809311827fbSnicm 			return (wp);
810311827fbSnicm 		n++;
811311827fbSnicm 	}
812311827fbSnicm 	return (NULL);
813311827fbSnicm }
814311827fbSnicm 
815385f8052Snicm struct window_pane *
816385f8052Snicm window_pane_next_by_number(struct window *w, struct window_pane *wp, u_int n)
817385f8052Snicm {
818385f8052Snicm 	for (; n > 0; n--) {
819385f8052Snicm 		if ((wp = TAILQ_NEXT(wp, entry)) == NULL)
820385f8052Snicm 			wp = TAILQ_FIRST(&w->panes);
821385f8052Snicm 	}
822385f8052Snicm 
823385f8052Snicm 	return (wp);
824385f8052Snicm }
825385f8052Snicm 
826385f8052Snicm struct window_pane *
827385f8052Snicm window_pane_previous_by_number(struct window *w, struct window_pane *wp,
828385f8052Snicm     u_int n)
829385f8052Snicm {
830385f8052Snicm 	for (; n > 0; n--) {
831385f8052Snicm 		if ((wp = TAILQ_PREV(wp, window_panes, entry)) == NULL)
832385f8052Snicm 			wp = TAILQ_LAST(&w->panes, window_panes);
833385f8052Snicm 	}
834385f8052Snicm 
835385f8052Snicm 	return (wp);
836385f8052Snicm }
837385f8052Snicm 
838f176699aSnicm int
839f176699aSnicm window_pane_index(struct window_pane *wp, u_int *i)
8409629a58fSnicm {
8419629a58fSnicm 	struct window_pane	*wq;
842f176699aSnicm 	struct window		*w = wp->window;
8439629a58fSnicm 
844d89252e5Snicm 	*i = options_get_number(w->options, "pane-base-index");
8459629a58fSnicm 	TAILQ_FOREACH(wq, &w->panes, entry) {
846f176699aSnicm 		if (wp == wq) {
847f176699aSnicm 			return (0);
8489629a58fSnicm 		}
849f176699aSnicm 		(*i)++;
850f176699aSnicm 	}
851f176699aSnicm 
852f176699aSnicm 	return (-1);
8539629a58fSnicm }
8549629a58fSnicm 
8559629a58fSnicm u_int
856311827fbSnicm window_count_panes(struct window *w)
857311827fbSnicm {
858311827fbSnicm 	struct window_pane	*wp;
859311827fbSnicm 	u_int			 n;
860311827fbSnicm 
861311827fbSnicm 	n = 0;
862311827fbSnicm 	TAILQ_FOREACH(wp, &w->panes, entry)
863311827fbSnicm 		n++;
864311827fbSnicm 	return (n);
865311827fbSnicm }
866311827fbSnicm 
867311827fbSnicm void
868311827fbSnicm window_destroy_panes(struct window *w)
869311827fbSnicm {
870311827fbSnicm 	struct window_pane	*wp;
871311827fbSnicm 
872a0aa010aSnicm 	while (!TAILQ_EMPTY(&w->last_panes)) {
873a0aa010aSnicm 		wp = TAILQ_FIRST(&w->last_panes);
874a0aa010aSnicm 		window_pane_stack_remove(&w->last_panes, wp);
875a0aa010aSnicm 	}
876a0aa010aSnicm 
877311827fbSnicm 	while (!TAILQ_EMPTY(&w->panes)) {
878311827fbSnicm 		wp = TAILQ_FIRST(&w->panes);
879311827fbSnicm 		TAILQ_REMOVE(&w->panes, wp, entry);
880311827fbSnicm 		window_pane_destroy(wp);
881311827fbSnicm 	}
882311827fbSnicm }
883311827fbSnicm 
88461076a28Snicm const char *
885b1a38154Snicm window_printable_flags(struct winlink *wl, int escape)
8863e60752eSnicm {
8879418efdbSnicm 	struct session	*s = wl->session;
88861076a28Snicm 	static char	 flags[32];
8893e60752eSnicm 	int		 pos;
8903e60752eSnicm 
8913e60752eSnicm 	pos = 0;
892b1a38154Snicm 	if (wl->flags & WINLINK_ACTIVITY) {
8933e60752eSnicm 		flags[pos++] = '#';
894b1a38154Snicm 		if (escape)
895b1a38154Snicm 			flags[pos++] = '#';
896b1a38154Snicm 	}
8973e60752eSnicm 	if (wl->flags & WINLINK_BELL)
8983e60752eSnicm 		flags[pos++] = '!';
8993e60752eSnicm 	if (wl->flags & WINLINK_SILENCE)
9003e60752eSnicm 		flags[pos++] = '~';
9013e60752eSnicm 	if (wl == s->curw)
9023e60752eSnicm 		flags[pos++] = '*';
9033e60752eSnicm 	if (wl == TAILQ_FIRST(&s->lastw))
9043e60752eSnicm 		flags[pos++] = '-';
9057519eda3Snicm 	if (server_check_marked() && wl == marked_pane.wl)
9062986c025Snicm 		flags[pos++] = 'M';
907b4a3311eSnicm 	if (wl->window->flags & WINDOW_ZOOMED)
908b4a3311eSnicm 		flags[pos++] = 'Z';
9093e60752eSnicm 	flags[pos] = '\0';
91061076a28Snicm 	return (flags);
9113e60752eSnicm }
9123e60752eSnicm 
9133b2ac4f9Snicm struct window_pane *
9143b2ac4f9Snicm window_pane_find_by_id_str(const char *s)
9153b2ac4f9Snicm {
9163b2ac4f9Snicm 	const char	*errstr;
9173b2ac4f9Snicm 	u_int		 id;
9183b2ac4f9Snicm 
9193b2ac4f9Snicm 	if (*s != '%')
9203b2ac4f9Snicm 		return (NULL);
9213b2ac4f9Snicm 
9223b2ac4f9Snicm 	id = strtonum(s + 1, 0, UINT_MAX, &errstr);
9233b2ac4f9Snicm 	if (errstr != NULL)
9243b2ac4f9Snicm 		return (NULL);
9253b2ac4f9Snicm 	return (window_pane_find_by_id(id));
9263b2ac4f9Snicm }
9273b2ac4f9Snicm 
9283e0f2533Snicm struct window_pane *
9293e0f2533Snicm window_pane_find_by_id(u_int id)
9303e0f2533Snicm {
9313e0f2533Snicm 	struct window_pane	wp;
9323e0f2533Snicm 
9333e0f2533Snicm 	wp.id = id;
9343e0f2533Snicm 	return (RB_FIND(window_pane_tree, &all_window_panes, &wp));
9353e0f2533Snicm }
9363e0f2533Snicm 
937ced21769Snicm static struct window_pane *
938311827fbSnicm window_pane_create(struct window *w, u_int sx, u_int sy, u_int hlimit)
939311827fbSnicm {
940311827fbSnicm 	struct window_pane	*wp;
9415581cc00Snicm 	char			 host[HOST_NAME_MAX + 1];
942311827fbSnicm 
943311827fbSnicm 	wp = xcalloc(1, sizeof *wp);
944311827fbSnicm 	wp->window = w;
9456e0f28f8Snicm 	wp->options = options_create(w->options);
9466e0f28f8Snicm 	wp->flags = PANE_STYLECHANGED;
947311827fbSnicm 
948abb1e3d2Snicm 	wp->id = next_window_pane_id++;
9493e0f2533Snicm 	RB_INSERT(window_pane_tree, &all_window_panes, wp);
9503e0f2533Snicm 
951311827fbSnicm 	wp->fd = -1;
952311827fbSnicm 
9532c8678f7Snicm 	TAILQ_INIT(&wp->modes);
954311827fbSnicm 
9557592946fSnicm 	TAILQ_INIT (&wp->resize_queue);
956311827fbSnicm 
95749cd5680Snicm 	wp->sx = sx;
95849cd5680Snicm 	wp->sy = sy;
959311827fbSnicm 
960095994acSnicm 	wp->pipe_fd = -1;
961095994acSnicm 
962891e2565Snicm 	wp->control_bg = -1;
963891e2565Snicm 	wp->control_fg = -1;
964891e2565Snicm 
965dd0df669Snicm 	style_set_scrollbar_style_from_option(&wp->scrollbar_style,
966dd0df669Snicm 	    wp->options);
967dd0df669Snicm 
96833a1e283Snicm 	colour_palette_init(&wp->palette);
9690f97d08fSnicm 	colour_palette_from_option(&wp->palette, wp->options);
9700f97d08fSnicm 
971311827fbSnicm 	screen_init(&wp->base, sx, sy, hlimit);
972311827fbSnicm 	wp->screen = &wp->base;
973bd08eb64Snicm 	window_pane_default_cursor(wp);
974311827fbSnicm 
975bc3b19faSnicm 	screen_init(&wp->status_screen, 1, 1, 0);
976bc3b19faSnicm 
9775581cc00Snicm 	if (gethostname(host, sizeof host) == 0)
9785581cc00Snicm 		screen_set_title(&wp->base, host);
9795581cc00Snicm 
980311827fbSnicm 	return (wp);
981311827fbSnicm }
982311827fbSnicm 
983ced21769Snicm static void
984311827fbSnicm window_pane_destroy(struct window_pane *wp)
985311827fbSnicm {
9867592946fSnicm 	struct window_pane_resize	*r;
9877592946fSnicm 	struct window_pane_resize	*r1;
9887592946fSnicm 
9892c8678f7Snicm 	window_pane_reset_mode_all(wp);
9904f67ae45Snicm 	free(wp->searchstr);
991a96d71afSnicm 
992fdcac80eSnicm 	if (wp->fd != -1) {
993fdcac80eSnicm 		bufferevent_free(wp->event);
99484045c9aSnicm 		close(wp->fd);
995fdcac80eSnicm 	}
996cf6b0e8fSnicm 	if (wp->ictx != NULL)
99729ebed37Snicm 		input_free(wp->ictx);
998311827fbSnicm 
999d680d99bSnicm 	screen_free(&wp->status_screen);
1000d680d99bSnicm 
1001311827fbSnicm 	screen_free(&wp->base);
1002311827fbSnicm 
1003095994acSnicm 	if (wp->pipe_fd != -1) {
1004e1e559afSnicm 		bufferevent_free(wp->pipe_event);
100584045c9aSnicm 		close(wp->pipe_fd);
1006095994acSnicm 	}
1007095994acSnicm 
1008243e282fSnicm 	if (event_initialized(&wp->resize_timer))
1009243e282fSnicm 		event_del(&wp->resize_timer);
10107592946fSnicm 	TAILQ_FOREACH_SAFE(r, &wp->resize_queue, entry, r1) {
10117592946fSnicm 		TAILQ_REMOVE(&wp->resize_queue, r, entry);
10127592946fSnicm 		free(r);
10137592946fSnicm 	}
1014243e282fSnicm 
10153e0f2533Snicm 	RB_REMOVE(window_pane_tree, &all_window_panes, wp);
10163e0f2533Snicm 
10176e0f28f8Snicm 	options_free(wp->options);
10183baa4a0cSnicm 	free((void *)wp->cwd);
10197d053cf9Snicm 	free(wp->shell);
102058034113Snicm 	cmd_free_argv(wp->argc, wp->argv);
102133a1e283Snicm 	colour_palette_free(&wp->palette);
10227d053cf9Snicm 	free(wp);
1023311827fbSnicm }
1024311827fbSnicm 
102598864f58Snicm static void
1026d0e2e7f1Snicm window_pane_read_callback(__unused struct bufferevent *bufev, void *data)
1027fdcac80eSnicm {
1028fdcac80eSnicm 	struct window_pane		*wp = data;
10296da7fc1eSnicm 	struct evbuffer			*evb = wp->event->input;
10302920028dSnicm 	struct window_pane_offset	*wpo = &wp->pipe_offset;
103198864f58Snicm 	size_t				 size = EVBUFFER_LENGTH(evb);
1032247bcdbfSnicm 	char				*new_data;
10333f7a83aaSnicm 	size_t				 new_size;
10342920028dSnicm 	struct client			*c;
1035fdcac80eSnicm 
10362920028dSnicm 	if (wp->pipe_fd != -1) {
10372920028dSnicm 		new_data = window_pane_get_new_data(wp, wpo, &new_size);
10382920028dSnicm 		if (new_size > 0) {
1039247bcdbfSnicm 			bufferevent_write(wp->pipe_event, new_data, new_size);
1040a34cf9c8Snicm 			window_pane_update_used_data(wp, wpo, new_size);
10412920028dSnicm 		}
1042247bcdbfSnicm 	}
1043247bcdbfSnicm 
104449157452Snicm 	log_debug("%%%u has %zu bytes", wp->id, size);
10452920028dSnicm 	TAILQ_FOREACH(c, &clients, entry) {
1046a34cf9c8Snicm 		if (c->session != NULL && (c->flags & CLIENT_CONTROL))
10472920028dSnicm 			control_write_output(c, wp);
10482920028dSnicm 	}
104929ebed37Snicm 	input_parse_pane(wp);
1050a34cf9c8Snicm 	bufferevent_disable(wp->event, EV_READ);
1051fdcac80eSnicm }
1052fdcac80eSnicm 
105398864f58Snicm static void
1054d0e2e7f1Snicm window_pane_error_callback(__unused struct bufferevent *bufev,
1055d0e2e7f1Snicm     __unused short what, void *data)
1056fdcac80eSnicm {
1057fdcac80eSnicm 	struct window_pane *wp = data;
1058fdcac80eSnicm 
1059f35e5f52Snicm 	log_debug("%%%u error", wp->id);
10609017429cSnicm 	wp->flags |= PANE_EXITED;
1061f35e5f52Snicm 
1062f35e5f52Snicm 	if (window_pane_destroy_ready(wp))
10634fc586aaSnicm 		server_destroy_pane(wp, 1);
1064fdcac80eSnicm }
1065fdcac80eSnicm 
1066fdcac80eSnicm void
1067c26c4f79Snicm window_pane_set_event(struct window_pane *wp)
1068c26c4f79Snicm {
1069c26c4f79Snicm 	setblocking(wp->fd, 0);
1070c26c4f79Snicm 
1071c26c4f79Snicm 	wp->event = bufferevent_new(wp->fd, window_pane_read_callback,
1072c26c4f79Snicm 	    NULL, window_pane_error_callback, wp);
10737c8808e4Snicm 	if (wp->event == NULL)
10747c8808e4Snicm 		fatalx("out of memory");
107533a1e283Snicm 	wp->ictx = input_init(wp, wp->event, &wp->palette);
1076c26c4f79Snicm 
1077c26c4f79Snicm 	bufferevent_enable(wp->event, EV_READ|EV_WRITE);
1078c26c4f79Snicm }
1079c26c4f79Snicm 
1080c26c4f79Snicm void
1081311827fbSnicm window_pane_resize(struct window_pane *wp, u_int sx, u_int sy)
1082311827fbSnicm {
10832c8678f7Snicm 	struct window_mode_entry	*wme;
10847592946fSnicm 	struct window_pane_resize	*r;
10852c8678f7Snicm 
1086311827fbSnicm 	if (sx == wp->sx && sy == wp->sy)
1087980b7663Snicm 		return;
10887592946fSnicm 
10897592946fSnicm 	r = xmalloc(sizeof *r);
10907592946fSnicm 	r->sx = sx;
10917592946fSnicm 	r->sy = sy;
10927592946fSnicm 	r->osx = wp->sx;
10937592946fSnicm 	r->osy = wp->sy;
10947592946fSnicm 	TAILQ_INSERT_TAIL (&wp->resize_queue, r, entry);
10957592946fSnicm 
1096311827fbSnicm 	wp->sx = sx;
1097311827fbSnicm 	wp->sy = sy;
1098311827fbSnicm 
10998b7c4252Snicm 	log_debug("%s: %%%u resize %ux%u", __func__, wp->id, sx, sy);
1100af550f8bSnicm 	screen_resize(&wp->base, sx, sy, wp->base.saved_grid == NULL);
11012c8678f7Snicm 
11022c8678f7Snicm 	wme = TAILQ_FIRST(&wp->modes);
11032c8678f7Snicm 	if (wme != NULL && wme->mode->resize != NULL)
11042c8678f7Snicm 		wme->mode->resize(wme, sx, sy);
1105311827fbSnicm }
1106311827fbSnicm 
1107311827fbSnicm int
1108cdfe74adSnicm window_pane_set_mode(struct window_pane *wp, struct window_pane *swp,
1109cdfe74adSnicm     const struct window_mode *mode, struct cmd_find_state *fs,
1110cdfe74adSnicm     struct args *args)
1111311827fbSnicm {
11122c8678f7Snicm 	struct window_mode_entry	*wme;
1113d2117533Snicm 	struct window			*w = wp->window;
1114311827fbSnicm 
11152c8678f7Snicm 	if (!TAILQ_EMPTY(&wp->modes) && TAILQ_FIRST(&wp->modes)->mode == mode)
1116311827fbSnicm 		return (1);
111730a94f45Snicm 
11182c8678f7Snicm 	TAILQ_FOREACH(wme, &wp->modes, entry) {
11192c8678f7Snicm 		if (wme->mode == mode)
11202c8678f7Snicm 			break;
11212c8678f7Snicm 	}
1122610f37cfSnicm 	if (wme != NULL) {
11232c8678f7Snicm 		TAILQ_REMOVE(&wp->modes, wme, entry);
1124610f37cfSnicm 		TAILQ_INSERT_HEAD(&wp->modes, wme, entry);
1125610f37cfSnicm 	} else {
11262c8678f7Snicm 		wme = xcalloc(1, sizeof *wme);
11272c8678f7Snicm 		wme->wp = wp;
1128cdfe74adSnicm 		wme->swp = swp;
11292c8678f7Snicm 		wme->mode = mode;
11302c8678f7Snicm 		wme->prefix = 1;
1131610f37cfSnicm 		TAILQ_INSERT_HEAD(&wp->modes, wme, entry);
11322c8678f7Snicm 		wme->screen = wme->mode->init(wme, fs, args);
11332c8678f7Snicm 	}
11342c8678f7Snicm 	wp->screen = wme->screen;
1135d2117533Snicm 
1136d2117533Snicm 	wp->flags |= (PANE_REDRAW|PANE_REDRAWSCROLLBAR|PANE_CHANGED);
1137d2117533Snicm 	layout_fix_panes(w, NULL);
11385dc47092Snicm 
113901c0c428Snicm 	server_redraw_window_borders(wp->window);
11405dc47092Snicm 	server_status_window(wp->window);
11413c14ce20Snicm 	notify_pane("pane-mode-changed", wp);
11422c8678f7Snicm 
1143311827fbSnicm 	return (0);
1144311827fbSnicm }
1145311827fbSnicm 
1146311827fbSnicm void
1147311827fbSnicm window_pane_reset_mode(struct window_pane *wp)
1148311827fbSnicm {
11492c8678f7Snicm 	struct window_mode_entry	*wme, *next;
1150d2117533Snicm 	struct window			*w = wp->window;
11512c8678f7Snicm 
11522c8678f7Snicm 	if (TAILQ_EMPTY(&wp->modes))
1153311827fbSnicm 		return;
1154311827fbSnicm 
11552c8678f7Snicm 	wme = TAILQ_FIRST(&wp->modes);
11562c8678f7Snicm 	TAILQ_REMOVE(&wp->modes, wme, entry);
11572c8678f7Snicm 	wme->mode->free(wme);
11582c8678f7Snicm 	free(wme);
11592c8678f7Snicm 
11602c8678f7Snicm 	next = TAILQ_FIRST(&wp->modes);
11612c8678f7Snicm 	if (next == NULL) {
1162d7514f5bSnicm 		wp->flags &= ~PANE_UNSEENCHANGES;
11632c8678f7Snicm 		log_debug("%s: no next mode", __func__);
1164311827fbSnicm 		wp->screen = &wp->base;
11652c8678f7Snicm 	} else {
11662c8678f7Snicm 		log_debug("%s: next mode is %s", __func__, next->mode->name);
11672c8678f7Snicm 		wp->screen = next->screen;
116810b50ca4Snicm 		if (next->mode->resize != NULL)
11692c8678f7Snicm 			next->mode->resize(next, wp->sx, wp->sy);
11702c8678f7Snicm 	}
1171d2117533Snicm 
1172d2117533Snicm 	wp->flags |= (PANE_REDRAW|PANE_REDRAWSCROLLBAR|PANE_CHANGED);
1173d2117533Snicm 	layout_fix_panes(w, NULL);
11745dc47092Snicm 
117501c0c428Snicm 	server_redraw_window_borders(wp->window);
11765dc47092Snicm 	server_status_window(wp->window);
11773c14ce20Snicm 	notify_pane("pane-mode-changed", wp);
1178311827fbSnicm }
1179311827fbSnicm 
1180311827fbSnicm void
11812c8678f7Snicm window_pane_reset_mode_all(struct window_pane *wp)
11822c8678f7Snicm {
11832c8678f7Snicm 	while (!TAILQ_EMPTY(&wp->modes))
11842c8678f7Snicm 		window_pane_reset_mode(wp);
11852c8678f7Snicm }
11862c8678f7Snicm 
11878367f274Snicm static void
1188482624f4Snicm window_pane_copy_paste(struct window_pane *wp, char *buf, size_t len)
1189482624f4Snicm {
1190482624f4Snicm  	struct window_pane	*loop;
1191482624f4Snicm 
1192482624f4Snicm 	TAILQ_FOREACH(loop, &wp->window->panes, entry) {
1193482624f4Snicm 		if (loop != wp &&
1194482624f4Snicm 		    TAILQ_EMPTY(&loop->modes) &&
1195482624f4Snicm 		    loop->fd != -1 &&
1196482624f4Snicm 		    (~loop->flags & PANE_INPUTOFF) &&
1197482624f4Snicm 		    window_pane_visible(loop) &&
1198482624f4Snicm 		    options_get_number(loop->options, "synchronize-panes")) {
1199482624f4Snicm 			log_debug("%s: %.*s", __func__, (int)len, buf);
1200482624f4Snicm 			bufferevent_write(loop->event, buf, len);
1201482624f4Snicm 		}
1202482624f4Snicm 	}
1203482624f4Snicm }
1204482624f4Snicm 
1205482624f4Snicm static void
12068367f274Snicm window_pane_copy_key(struct window_pane *wp, key_code key)
12078367f274Snicm {
12088367f274Snicm  	struct window_pane	*loop;
12098367f274Snicm 
12108367f274Snicm 	TAILQ_FOREACH(loop, &wp->window->panes, entry) {
12118367f274Snicm 		if (loop != wp &&
12128367f274Snicm 		    TAILQ_EMPTY(&loop->modes) &&
12138367f274Snicm 		    loop->fd != -1 &&
12148367f274Snicm 		    (~loop->flags & PANE_INPUTOFF) &&
12158367f274Snicm 		    window_pane_visible(loop) &&
12168367f274Snicm 		    options_get_number(loop->options, "synchronize-panes"))
12178367f274Snicm 			input_key_pane(loop, key, NULL);
12188367f274Snicm 	}
12198367f274Snicm }
12208367f274Snicm 
1221482624f4Snicm void
1222*a1f482feSnicm window_pane_paste(struct window_pane *wp, key_code key, char *buf, size_t len)
1223482624f4Snicm {
1224482624f4Snicm 	if (!TAILQ_EMPTY(&wp->modes))
1225482624f4Snicm 		return;
1226482624f4Snicm 
1227482624f4Snicm 	if (wp->fd == -1 || wp->flags & PANE_INPUTOFF)
1228482624f4Snicm 		return;
1229482624f4Snicm 
1230*a1f482feSnicm 	if (KEYC_IS_PASTE(key) && (~wp->screen->mode & MODE_BRACKETPASTE))
1231*a1f482feSnicm 		return;
1232*a1f482feSnicm 
1233482624f4Snicm 	log_debug("%s: %.*s", __func__, (int)len, buf);
1234482624f4Snicm 	bufferevent_write(wp->event, buf, len);
1235482624f4Snicm 
1236482624f4Snicm 	if (options_get_number(wp->options, "synchronize-panes"))
1237482624f4Snicm 		window_pane_copy_paste(wp, buf, len);
1238482624f4Snicm }
1239482624f4Snicm 
1240de7a415fSnicm int
1241e048bb79Snicm window_pane_key(struct window_pane *wp, struct client *c, struct session *s,
1242bf52409eSnicm     struct winlink *wl, key_code key, struct mouse_event *m)
1243311827fbSnicm {
12442c8678f7Snicm 	struct window_mode_entry	*wme;
12459274c26aSnicm 
1246e048bb79Snicm 	if (KEYC_IS_MOUSE(key) && m == NULL)
1247de7a415fSnicm 		return (-1);
1248e048bb79Snicm 
12492c8678f7Snicm 	wme = TAILQ_FIRST(&wp->modes);
125030a94f45Snicm 	if (wme != NULL) {
12515416581eSnicm 		if (wme->mode->key != NULL && c != NULL) {
12525416581eSnicm 			key &= ~KEYC_MASK_FLAGS;
12535416581eSnicm 			wme->mode->key(wme, c, s, wl, key, m);
12545416581eSnicm 		}
1255de7a415fSnicm 		return (0);
12569274c26aSnicm 	}
12579274c26aSnicm 
12584dca3db4Snicm 	if (wp->fd == -1 || wp->flags & PANE_INPUTOFF)
1259de7a415fSnicm 		return (0);
12604dca3db4Snicm 
126129ebed37Snicm 	if (input_key_pane(wp, key, m) != 0)
1262de7a415fSnicm 		return (-1);
1263e048bb79Snicm 
1264e048bb79Snicm 	if (KEYC_IS_MOUSE(key))
1265de7a415fSnicm 		return (0);
12668367f274Snicm 	if (options_get_number(wp->options, "synchronize-panes"))
12678367f274Snicm 		window_pane_copy_key(wp, key);
1268de7a415fSnicm 	return (0);
1269311827fbSnicm }
1270311827fbSnicm 
127198c9454eSnicm int
1272b81c3e5bSnicm window_pane_visible(struct window_pane *wp)
127398c9454eSnicm {
12747b470e93Snicm 	if (~wp->window->flags & WINDOW_ZOOMED)
1275b81c3e5bSnicm 		return (1);
12767b470e93Snicm 	return (wp == wp->window->active);
127798c9454eSnicm }
127898c9454eSnicm 
1279d39fdc49Snicm int
1280d39fdc49Snicm window_pane_exited(struct window_pane *wp)
1281d39fdc49Snicm {
1282d39fdc49Snicm 	return (wp->fd == -1 || (wp->flags & PANE_EXITED));
1283d39fdc49Snicm }
1284d39fdc49Snicm 
12855ed50bb3Snicm u_int
1286da643456Snicm window_pane_search(struct window_pane *wp, const char *term, int regex,
1287da643456Snicm     int ignore)
12885ed50bb3Snicm {
12895ed50bb3Snicm 	struct screen	*s = &wp->base;
1290da643456Snicm 	regex_t		 r;
1291da643456Snicm 	char		*new = NULL, *line;
12925ed50bb3Snicm 	u_int		 i;
1293da643456Snicm 	int		 flags = 0, found;
12947878fe97Snicm 	size_t		 n;
12955ed50bb3Snicm 
1296da643456Snicm 	if (!regex) {
1297da643456Snicm 		if (ignore)
1298da643456Snicm 			flags |= FNM_CASEFOLD;
1299da643456Snicm 		xasprintf(&new, "*%s*", term);
1300da643456Snicm 	} else {
1301da643456Snicm 		if (ignore)
1302da643456Snicm 			flags |= REG_ICASE;
1303da643456Snicm 		if (regcomp(&r, term, flags|REG_EXTENDED) != 0)
1304da643456Snicm 			return (0);
1305da643456Snicm 	}
13065ed50bb3Snicm 
13075ed50bb3Snicm 	for (i = 0; i < screen_size_y(s); i++) {
13085ed50bb3Snicm 		line = grid_view_string_cells(s->grid, 0, i, screen_size_x(s));
13097878fe97Snicm 		for (n = strlen(line); n > 0; n--) {
13107878fe97Snicm 			if (!isspace((u_char)line[n - 1]))
13117878fe97Snicm 				break;
13127878fe97Snicm 			line[n - 1] = '\0';
13137878fe97Snicm 		}
13147878fe97Snicm 		log_debug("%s: %s", __func__, line);
1315da643456Snicm 		if (!regex)
1316af7fb06aSnicm 			found = (fnmatch(new, line, flags) == 0);
1317da643456Snicm 		else
1318da643456Snicm 			found = (regexec(&r, line, 0, NULL, 0) == 0);
13195ed50bb3Snicm 		free(line);
1320da643456Snicm 		if (found)
13215ed50bb3Snicm 			break;
13225ed50bb3Snicm 	}
1323da643456Snicm 	if (!regex)
1324da643456Snicm 		free(new);
1325da643456Snicm 	else
1326da643456Snicm 		regfree(&r);
13275ed50bb3Snicm 
13285ed50bb3Snicm 	if (i == screen_size_y(s))
13295ed50bb3Snicm 		return (0);
13305ed50bb3Snicm 	return (i + 1);
13315ed50bb3Snicm }
13325ed50bb3Snicm 
13338615a3a4Snicm /* Get MRU pane from a list. */
133498864f58Snicm static struct window_pane *
133538e446e0Snicm window_pane_choose_best(struct window_pane **list, u_int size)
13368615a3a4Snicm {
13378615a3a4Snicm 	struct window_pane	*next, *best;
13388615a3a4Snicm 	u_int			 i;
13398615a3a4Snicm 
134038e446e0Snicm 	if (size == 0)
13418615a3a4Snicm 		return (NULL);
13428615a3a4Snicm 
134338e446e0Snicm 	best = list[0];
134438e446e0Snicm 	for (i = 1; i < size; i++) {
134538e446e0Snicm 		next = list[i];
13468615a3a4Snicm 		if (next->active_point > best->active_point)
13478615a3a4Snicm 			best = next;
13488615a3a4Snicm 	}
13498615a3a4Snicm 	return (best);
13508615a3a4Snicm }
13518615a3a4Snicm 
13528615a3a4Snicm /*
13538615a3a4Snicm  * Find the pane directly above another. We build a list of those adjacent to
13548615a3a4Snicm  * top edge and then choose the best.
13558615a3a4Snicm  */
1356836a95beSnicm struct window_pane *
1357836a95beSnicm window_pane_find_up(struct window_pane *wp)
1358836a95beSnicm {
1359c1cd4700Snicm 	struct window		*w;
136038e446e0Snicm 	struct window_pane	*next, *best, **list;
136138e446e0Snicm 	u_int			 edge, left, right, end, size;
13629f9d0405Snicm 	int			 status, found;
1363836a95beSnicm 
13647b470e93Snicm 	if (wp == NULL)
1365836a95beSnicm 		return (NULL);
1366c1cd4700Snicm 	w = wp->window;
1367c1cd4700Snicm 	status = options_get_number(w->options, "pane-border-status");
136838e446e0Snicm 
136938e446e0Snicm 	list = NULL;
137038e446e0Snicm 	size = 0;
1371836a95beSnicm 
13728615a3a4Snicm 	edge = wp->yoff;
1373c1cd4700Snicm 	if (status == PANE_STATUS_TOP) {
1374c1cd4700Snicm 		if (edge == 1)
1375c1cd4700Snicm 			edge = w->sy + 1;
1376c1cd4700Snicm 	} else if (status == PANE_STATUS_BOTTOM) {
1377c1cd4700Snicm 		if (edge == 0)
1378c1cd4700Snicm 			edge = w->sy;
1379c1cd4700Snicm 	} else {
1380c1cd4700Snicm 		if (edge == 0)
1381c1cd4700Snicm 			edge = w->sy + 1;
1382c1cd4700Snicm 	}
13838615a3a4Snicm 
1384836a95beSnicm 	left = wp->xoff;
13858615a3a4Snicm 	right = wp->xoff + wp->sx;
1386836a95beSnicm 
1387c1cd4700Snicm 	TAILQ_FOREACH(next, &w->panes, entry) {
13887b470e93Snicm 		if (next == wp)
1389836a95beSnicm 			continue;
13908615a3a4Snicm 		if (next->yoff + next->sy + 1 != edge)
1391836a95beSnicm 			continue;
13928615a3a4Snicm 		end = next->xoff + next->sx - 1;
13938615a3a4Snicm 
13948615a3a4Snicm 		found = 0;
13958615a3a4Snicm 		if (next->xoff < left && end > right)
13968615a3a4Snicm 			found = 1;
13978615a3a4Snicm 		else if (next->xoff >= left && next->xoff <= right)
13988615a3a4Snicm 			found = 1;
13998615a3a4Snicm 		else if (end >= left && end <= right)
14008615a3a4Snicm 			found = 1;
140138e446e0Snicm 		if (!found)
140238e446e0Snicm 			continue;
140338e446e0Snicm 		list = xreallocarray(list, size + 1, sizeof *list);
140438e446e0Snicm 		list[size++] = next;
1405836a95beSnicm 	}
14068615a3a4Snicm 
140738e446e0Snicm 	best = window_pane_choose_best(list, size);
140838e446e0Snicm 	free(list);
14098615a3a4Snicm 	return (best);
1410836a95beSnicm }
1411836a95beSnicm 
1412836a95beSnicm /* Find the pane directly below another. */
1413836a95beSnicm struct window_pane *
1414836a95beSnicm window_pane_find_down(struct window_pane *wp)
1415836a95beSnicm {
1416c1cd4700Snicm 	struct window		*w;
141738e446e0Snicm 	struct window_pane	*next, *best, **list;
141838e446e0Snicm 	u_int			 edge, left, right, end, size;
14199f9d0405Snicm 	int			 status, found;
1420836a95beSnicm 
14217b470e93Snicm 	if (wp == NULL)
1422836a95beSnicm 		return (NULL);
1423c1cd4700Snicm 	w = wp->window;
1424c1cd4700Snicm 	status = options_get_number(w->options, "pane-border-status");
142538e446e0Snicm 
142638e446e0Snicm 	list = NULL;
142738e446e0Snicm 	size = 0;
1428836a95beSnicm 
14298615a3a4Snicm 	edge = wp->yoff + wp->sy + 1;
1430c1cd4700Snicm 	if (status == PANE_STATUS_TOP) {
1431c1cd4700Snicm 		if (edge >= w->sy)
1432c1cd4700Snicm 			edge = 1;
1433c1cd4700Snicm 	} else if (status == PANE_STATUS_BOTTOM) {
1434c1cd4700Snicm 		if (edge >= w->sy - 1)
1435c1cd4700Snicm 			edge = 0;
1436c1cd4700Snicm 	} else {
14375f9583aeSnicm 		if (edge >= w->sy)
1438c1cd4700Snicm 			edge = 0;
1439c1cd4700Snicm 	}
14408615a3a4Snicm 
1441836a95beSnicm 	left = wp->xoff;
14428615a3a4Snicm 	right = wp->xoff + wp->sx;
1443836a95beSnicm 
1444c1cd4700Snicm 	TAILQ_FOREACH(next, &w->panes, entry) {
14457b470e93Snicm 		if (next == wp)
1446836a95beSnicm 			continue;
14478615a3a4Snicm 		if (next->yoff != edge)
1448836a95beSnicm 			continue;
14498615a3a4Snicm 		end = next->xoff + next->sx - 1;
14508615a3a4Snicm 
14518615a3a4Snicm 		found = 0;
14528615a3a4Snicm 		if (next->xoff < left && end > right)
14538615a3a4Snicm 			found = 1;
14548615a3a4Snicm 		else if (next->xoff >= left && next->xoff <= right)
14558615a3a4Snicm 			found = 1;
14568615a3a4Snicm 		else if (end >= left && end <= right)
14578615a3a4Snicm 			found = 1;
145838e446e0Snicm 		if (!found)
145938e446e0Snicm 			continue;
146038e446e0Snicm 		list = xreallocarray(list, size + 1, sizeof *list);
146138e446e0Snicm 		list[size++] = next;
1462836a95beSnicm 	}
1463836a95beSnicm 
146438e446e0Snicm 	best = window_pane_choose_best(list, size);
146538e446e0Snicm 	free(list);
14668615a3a4Snicm 	return (best);
14678615a3a4Snicm }
14688615a3a4Snicm 
14698615a3a4Snicm /* Find the pane directly to the left of another. */
1470836a95beSnicm struct window_pane *
1471836a95beSnicm window_pane_find_left(struct window_pane *wp)
1472836a95beSnicm {
1473c1cd4700Snicm 	struct window		*w;
147438e446e0Snicm 	struct window_pane	*next, *best, **list;
147538e446e0Snicm 	u_int			 edge, top, bottom, end, size;
14768615a3a4Snicm 	int			 found;
1477836a95beSnicm 
14787b470e93Snicm 	if (wp == NULL)
1479836a95beSnicm 		return (NULL);
1480c1cd4700Snicm 	w = wp->window;
148138e446e0Snicm 
148238e446e0Snicm 	list = NULL;
148338e446e0Snicm 	size = 0;
1484836a95beSnicm 
14858615a3a4Snicm 	edge = wp->xoff;
14868615a3a4Snicm 	if (edge == 0)
1487c1cd4700Snicm 		edge = w->sx + 1;
14888615a3a4Snicm 
1489836a95beSnicm 	top = wp->yoff;
14908615a3a4Snicm 	bottom = wp->yoff + wp->sy;
1491836a95beSnicm 
1492c1cd4700Snicm 	TAILQ_FOREACH(next, &w->panes, entry) {
14937b470e93Snicm 		if (next == wp)
1494836a95beSnicm 			continue;
14958615a3a4Snicm 		if (next->xoff + next->sx + 1 != edge)
1496836a95beSnicm 			continue;
14978615a3a4Snicm 		end = next->yoff + next->sy - 1;
14988615a3a4Snicm 
14998615a3a4Snicm 		found = 0;
15008615a3a4Snicm 		if (next->yoff < top && end > bottom)
15018615a3a4Snicm 			found = 1;
15028615a3a4Snicm 		else if (next->yoff >= top && next->yoff <= bottom)
15038615a3a4Snicm 			found = 1;
15048615a3a4Snicm 		else if (end >= top && end <= bottom)
15058615a3a4Snicm 			found = 1;
150638e446e0Snicm 		if (!found)
150738e446e0Snicm 			continue;
150838e446e0Snicm 		list = xreallocarray(list, size + 1, sizeof *list);
150938e446e0Snicm 		list[size++] = next;
1510836a95beSnicm 	}
1511836a95beSnicm 
151238e446e0Snicm 	best = window_pane_choose_best(list, size);
151338e446e0Snicm 	free(list);
15148615a3a4Snicm 	return (best);
15158615a3a4Snicm }
15168615a3a4Snicm 
15178615a3a4Snicm /* Find the pane directly to the right of another. */
1518836a95beSnicm struct window_pane *
1519836a95beSnicm window_pane_find_right(struct window_pane *wp)
1520836a95beSnicm {
1521c1cd4700Snicm 	struct window		*w;
152238e446e0Snicm 	struct window_pane	*next, *best, **list;
152338e446e0Snicm 	u_int			 edge, top, bottom, end, size;
15248615a3a4Snicm 	int			 found;
1525836a95beSnicm 
15267b470e93Snicm 	if (wp == NULL)
1527836a95beSnicm 		return (NULL);
1528c1cd4700Snicm 	w = wp->window;
152938e446e0Snicm 
153038e446e0Snicm 	list = NULL;
153138e446e0Snicm 	size = 0;
1532836a95beSnicm 
15338615a3a4Snicm 	edge = wp->xoff + wp->sx + 1;
1534c1cd4700Snicm 	if (edge >= w->sx)
15358615a3a4Snicm 		edge = 0;
15368615a3a4Snicm 
1537836a95beSnicm 	top = wp->yoff;
15388615a3a4Snicm 	bottom = wp->yoff + wp->sy;
1539836a95beSnicm 
1540c1cd4700Snicm 	TAILQ_FOREACH(next, &w->panes, entry) {
15417b470e93Snicm 		if (next == wp)
1542836a95beSnicm 			continue;
15438615a3a4Snicm 		if (next->xoff != edge)
1544836a95beSnicm 			continue;
15458615a3a4Snicm 		end = next->yoff + next->sy - 1;
15468615a3a4Snicm 
15478615a3a4Snicm 		found = 0;
15488615a3a4Snicm 		if (next->yoff < top && end > bottom)
15498615a3a4Snicm 			found = 1;
15508615a3a4Snicm 		else if (next->yoff >= top && next->yoff <= bottom)
15518615a3a4Snicm 			found = 1;
15528615a3a4Snicm 		else if (end >= top && end <= bottom)
15538615a3a4Snicm 			found = 1;
155438e446e0Snicm 		if (!found)
155538e446e0Snicm 			continue;
155638e446e0Snicm 		list = xreallocarray(list, size + 1, sizeof *list);
155738e446e0Snicm 		list[size++] = next;
1558836a95beSnicm 	}
15598615a3a4Snicm 
156038e446e0Snicm 	best = window_pane_choose_best(list, size);
156138e446e0Snicm 	free(list);
15628615a3a4Snicm 	return (best);
1563836a95beSnicm }
156420ef07adSnicm 
1565a0aa010aSnicm void
1566a0aa010aSnicm window_pane_stack_push(struct window_panes *stack, struct window_pane *wp)
1567a0aa010aSnicm {
1568a0aa010aSnicm 	if (wp != NULL) {
1569a0aa010aSnicm 		window_pane_stack_remove(stack, wp);
1570a0aa010aSnicm 		TAILQ_INSERT_HEAD(stack, wp, sentry);
1571a0aa010aSnicm 		wp->flags |= PANE_VISITED;
1572a0aa010aSnicm 	}
1573a0aa010aSnicm }
1574a0aa010aSnicm 
1575a0aa010aSnicm void
1576a0aa010aSnicm window_pane_stack_remove(struct window_panes *stack, struct window_pane *wp)
1577a0aa010aSnicm {
1578a0aa010aSnicm 	if (wp != NULL && (wp->flags & PANE_VISITED)) {
1579a0aa010aSnicm 		TAILQ_REMOVE(stack, wp, sentry);
1580a0aa010aSnicm 		wp->flags &= ~PANE_VISITED;
1581a0aa010aSnicm 	}
1582a0aa010aSnicm }
1583a0aa010aSnicm 
158420ef07adSnicm /* Clear alert flags for a winlink */
158520ef07adSnicm void
158620ef07adSnicm winlink_clear_flags(struct winlink *wl)
158720ef07adSnicm {
1588de5a0fddSnicm 	struct winlink	*loop;
158920ef07adSnicm 
1590de5a0fddSnicm 	wl->window->flags &= ~WINDOW_ALERTFLAGS;
1591de5a0fddSnicm 	TAILQ_FOREACH(loop, &wl->window->winlinks, wentry) {
1592de5a0fddSnicm 		if ((loop->flags & WINLINK_ALERTFLAGS) != 0) {
1593de5a0fddSnicm 			loop->flags &= ~WINLINK_ALERTFLAGS;
1594de5a0fddSnicm 			server_status_session(loop->session);
159520ef07adSnicm 		}
159620ef07adSnicm 	}
159720ef07adSnicm }
1598fe1fa7a5Snicm 
1599bb3288f8Snicm /* Shuffle window indexes up. */
1600fe1fa7a5Snicm int
16014f91c935Snicm winlink_shuffle_up(struct session *s, struct winlink *wl, int before)
1602fe1fa7a5Snicm {
1603fe1fa7a5Snicm 	int	 idx, last;
1604fe1fa7a5Snicm 
1605c26c4f79Snicm 	if (wl == NULL)
1606c26c4f79Snicm 		return (-1);
16074f91c935Snicm 	if (before)
16084f91c935Snicm 		idx = wl->idx;
16094f91c935Snicm 	else
1610fe1fa7a5Snicm 		idx = wl->idx + 1;
1611fe1fa7a5Snicm 
1612fe1fa7a5Snicm 	/* Find the next free index. */
1613fe1fa7a5Snicm 	for (last = idx; last < INT_MAX; last++) {
1614fe1fa7a5Snicm 		if (winlink_find_by_index(&s->windows, last) == NULL)
1615fe1fa7a5Snicm 			break;
1616fe1fa7a5Snicm 	}
1617fe1fa7a5Snicm 	if (last == INT_MAX)
1618fe1fa7a5Snicm 		return (-1);
1619fe1fa7a5Snicm 
1620fe1fa7a5Snicm 	/* Move everything from last - 1 to idx up a bit. */
1621fe1fa7a5Snicm 	for (; last > idx; last--) {
1622fe1fa7a5Snicm 		wl = winlink_find_by_index(&s->windows, last - 1);
16234f91c935Snicm 		RB_REMOVE(winlinks, &s->windows, wl);
16244f91c935Snicm 		wl->idx++;
16254f91c935Snicm 		RB_INSERT(winlinks, &s->windows, wl);
1626fe1fa7a5Snicm 	}
1627fe1fa7a5Snicm 
1628fe1fa7a5Snicm 	return (idx);
1629fe1fa7a5Snicm }
1630aab3c1a6Snicm 
1631aab3c1a6Snicm static void
1632f4bc7c7aSnicm window_pane_input_callback(struct client *c, __unused const char *path,
1633f4bc7c7aSnicm     int error, int closed, struct evbuffer *buffer, void *data)
1634aab3c1a6Snicm {
1635aab3c1a6Snicm 	struct window_pane_input_data	*cdata = data;
1636aab3c1a6Snicm 	struct window_pane		*wp;
1637f4bc7c7aSnicm 	u_char				*buf = EVBUFFER_DATA(buffer);
1638f4bc7c7aSnicm 	size_t				 len = EVBUFFER_LENGTH(buffer);
1639aab3c1a6Snicm 
1640aab3c1a6Snicm 	wp = window_pane_find_by_id(cdata->wp);
164183e2ab13Snicm 	if (cdata->file != NULL && (wp == NULL || c->flags & CLIENT_DEAD)) {
1642ab9ea5e6Snicm 		if (wp == NULL) {
1643ab9ea5e6Snicm 			c->retval = 1;
1644b51cd9caSnicm 			c->flags |= CLIENT_EXIT;
1645ab9ea5e6Snicm 		}
164683e2ab13Snicm 		file_cancel(cdata->file);
164783e2ab13Snicm 	} else if (cdata->file == NULL || closed || error != 0) {
1648780ca620Snicm 		cmdq_continue(cdata->item);
1649f4bc7c7aSnicm 		server_client_unref(c);
1650f4bc7c7aSnicm 		free(cdata);
165183e2ab13Snicm 	} else
1652fb601f47Snicm 		input_parse_buffer(wp, buf, len);
1653f4bc7c7aSnicm 	evbuffer_drain(buffer, len);
1654aab3c1a6Snicm }
1655aab3c1a6Snicm 
1656aab3c1a6Snicm int
1657aab3c1a6Snicm window_pane_start_input(struct window_pane *wp, struct cmdq_item *item,
1658aab3c1a6Snicm     char **cause)
1659aab3c1a6Snicm {
1660040343aeSnicm 	struct client			*c = cmdq_get_client(item);
1661aab3c1a6Snicm 	struct window_pane_input_data	*cdata;
1662aab3c1a6Snicm 
1663aab3c1a6Snicm 	if (~wp->flags & PANE_EMPTY) {
1664aab3c1a6Snicm 		*cause = xstrdup("pane is not empty");
1665aab3c1a6Snicm 		return (-1);
1666aab3c1a6Snicm 	}
1667d110efb0Snicm 	if (c->flags & (CLIENT_DEAD|CLIENT_EXITED))
1668d110efb0Snicm 		return (1);
1669d110efb0Snicm 	if (c->session != NULL)
1670d110efb0Snicm 		return (1);
1671aab3c1a6Snicm 
1672aab3c1a6Snicm 	cdata = xmalloc(sizeof *cdata);
1673aab3c1a6Snicm 	cdata->item = item;
1674aab3c1a6Snicm 	cdata->wp = wp->id;
167583e2ab13Snicm 	cdata->file = file_read(c, "-", window_pane_input_callback, cdata);
1676f4bc7c7aSnicm 	c->references++;
1677f4bc7c7aSnicm 
1678f4bc7c7aSnicm 	return (0);
1679aab3c1a6Snicm }
16802920028dSnicm 
16812920028dSnicm void *
16822920028dSnicm window_pane_get_new_data(struct window_pane *wp,
16832920028dSnicm     struct window_pane_offset *wpo, size_t *size)
16842920028dSnicm {
16852920028dSnicm 	size_t	used = wpo->used - wp->base_offset;
16862920028dSnicm 
16872920028dSnicm 	*size = EVBUFFER_LENGTH(wp->event->input) - used;
16882920028dSnicm 	return (EVBUFFER_DATA(wp->event->input) + used);
16892920028dSnicm }
16902920028dSnicm 
16912920028dSnicm void
16922920028dSnicm window_pane_update_used_data(struct window_pane *wp,
1693a34cf9c8Snicm     struct window_pane_offset *wpo, size_t size)
16942920028dSnicm {
16952920028dSnicm 	size_t	used = wpo->used - wp->base_offset;
16962920028dSnicm 
16972920028dSnicm 	if (size > EVBUFFER_LENGTH(wp->event->input) - used)
16982920028dSnicm 		size = EVBUFFER_LENGTH(wp->event->input) - used;
16992920028dSnicm 	wpo->used += size;
17002920028dSnicm }
17012c25e4b4Snicm 
17022c25e4b4Snicm void
17032c25e4b4Snicm window_set_fill_character(struct window *w)
17042c25e4b4Snicm {
17052c25e4b4Snicm 	const char		*value;
17062c25e4b4Snicm 	struct utf8_data	*ud;
17072c25e4b4Snicm 
17082c25e4b4Snicm 	free(w->fill_character);
17092c25e4b4Snicm 	w->fill_character = NULL;
17102c25e4b4Snicm 
17112c25e4b4Snicm 	value = options_get_string(w->options, "fill-character");
17122c25e4b4Snicm 	if (*value != '\0' && utf8_isvalid(value)) {
17132c25e4b4Snicm 		ud = utf8_fromcstr(value);
17142c25e4b4Snicm 		if (ud != NULL && ud[0].width == 1)
17152c25e4b4Snicm 			w->fill_character = ud;
17162c25e4b4Snicm 	}
17172c25e4b4Snicm }
1718bd08eb64Snicm 
1719bd08eb64Snicm void
1720bd08eb64Snicm window_pane_default_cursor(struct window_pane *wp)
1721bd08eb64Snicm {
1722d1cbf82eSnicm 	screen_set_default_cursor(wp->screen, wp->options);
1723bd08eb64Snicm }
1724ad46cda3Snicm 
1725ad46cda3Snicm int
1726ad46cda3Snicm window_pane_mode(struct window_pane *wp)
1727ad46cda3Snicm {
1728ad46cda3Snicm 	if (TAILQ_FIRST(&wp->modes) != NULL) {
1729ad46cda3Snicm 		if (TAILQ_FIRST(&wp->modes)->mode == &window_copy_mode)
1730ad46cda3Snicm 			return (WINDOW_PANE_COPY_MODE);
1731ad46cda3Snicm 		if (TAILQ_FIRST(&wp->modes)->mode == &window_view_mode)
1732ad46cda3Snicm 			return (WINDOW_PANE_VIEW_MODE);
1733ad46cda3Snicm 	}
1734ad46cda3Snicm 	return (WINDOW_PANE_NO_MODE);
1735ad46cda3Snicm }
17368b458060Snicm 
17378b458060Snicm /* Return 1 if scrollbar is or should be displayed. */
17388b458060Snicm int
17398b458060Snicm window_pane_show_scrollbar(struct window_pane *wp, int sb_option)
17408b458060Snicm {
17418b458060Snicm 	if (SCREEN_IS_ALTERNATE(wp->screen))
17428b458060Snicm 		return (0);
17438b458060Snicm 	if (sb_option == PANE_SCROLLBARS_ALWAYS ||
17448b458060Snicm 	    (sb_option == PANE_SCROLLBARS_MODAL &&
17458b458060Snicm 	    window_pane_mode(wp) != WINDOW_PANE_NO_MODE))
17468b458060Snicm 		return (1);
17478b458060Snicm 	return (0);
17488b458060Snicm }
1749