xref: /openbsd-src/usr.bin/tmux/window.c (revision 4c1e55dc91edd6e69ccc60ce855900fbc12cf34f)
1 /* $OpenBSD: window.c,v 1.82 2012/07/10 11:53:01 nicm Exp $ */
2 
3 /*
4  * Copyright (c) 2007 Nicholas Marriott <nicm@users.sourceforge.net>
5  *
6  * Permission to use, copy, modify, and distribute this software for any
7  * purpose with or without fee is hereby granted, provided that the above
8  * copyright notice and this permission notice appear in all copies.
9  *
10  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14  * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER
15  * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
16  * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17  */
18 
19 #include <sys/types.h>
20 #include <sys/ioctl.h>
21 
22 #include <errno.h>
23 #include <fcntl.h>
24 #include <fnmatch.h>
25 #include <paths.h>
26 #include <pwd.h>
27 #include <signal.h>
28 #include <stdint.h>
29 #include <stdlib.h>
30 #include <string.h>
31 #include <termios.h>
32 #include <unistd.h>
33 #include <util.h>
34 
35 #include "tmux.h"
36 
37 /*
38  * Each window is attached to a number of panes, each of which is a pty. This
39  * file contains code to handle them.
40  *
41  * A pane has two buffers attached, these are filled and emptied by the main
42  * server poll loop. Output data is received from pty's in screen format,
43  * translated and returned as a series of escape sequences and strings via
44  * input_parse (in input.c). Input data is received as key codes and written
45  * directly via input_key.
46  *
47  * Each pane also has a "virtual" screen (screen.c) which contains the current
48  * state and is redisplayed when the window is reattached to a client.
49  *
50  * Windows are stored directly on a global array and wrapped in any number of
51  * winlink structs to be linked onto local session RB trees. A reference count
52  * is maintained and a window removed from the global list and destroyed when
53  * it reaches zero.
54  */
55 
56 /* Global window list. */
57 struct windows windows;
58 
59 /* Global panes tree. */
60 struct window_pane_tree all_window_panes;
61 u_int	next_window_pane_id;
62 u_int	next_window_id;
63 
64 void	window_pane_timer_callback(int, short, void *);
65 void	window_pane_read_callback(struct bufferevent *, void *);
66 void	window_pane_error_callback(struct bufferevent *, short, void *);
67 
68 RB_GENERATE(winlinks, winlink, entry, winlink_cmp);
69 
70 int
71 winlink_cmp(struct winlink *wl1, struct winlink *wl2)
72 {
73 	return (wl1->idx - wl2->idx);
74 }
75 
76 RB_GENERATE(window_pane_tree, window_pane, tree_entry, window_pane_cmp);
77 
78 int
79 window_pane_cmp(struct window_pane *wp1, struct window_pane *wp2)
80 {
81 	return (wp1->id - wp2->id);
82 }
83 
84 struct winlink *
85 winlink_find_by_window(struct winlinks *wwl, struct window *w)
86 {
87 	struct winlink	*wl;
88 
89 	RB_FOREACH(wl, winlinks, wwl) {
90 		if (wl->window == w)
91 			return (wl);
92 	}
93 
94 	return (NULL);
95 }
96 
97 struct winlink *
98 winlink_find_by_index(struct winlinks *wwl, int idx)
99 {
100 	struct winlink	wl;
101 
102 	if (idx < 0)
103 		fatalx("bad index");
104 
105 	wl.idx = idx;
106 	return (RB_FIND(winlinks, wwl, &wl));
107 }
108 
109 struct winlink *
110 winlink_find_by_window_id(struct winlinks *wwl, u_int id)
111 {
112 	struct winlink *wl;
113 
114 	RB_FOREACH(wl, winlinks, wwl) {
115 		if (wl->window->id == id)
116 			return (wl);
117 	}
118 	return (NULL);
119 }
120 
121 int
122 winlink_next_index(struct winlinks *wwl, int idx)
123 {
124 	int	i;
125 
126 	i = idx;
127 	do {
128 		if (winlink_find_by_index(wwl, i) == NULL)
129 			return (i);
130 		if (i == INT_MAX)
131 			i = 0;
132 		else
133 			i++;
134 	} while (i != idx);
135 	return (-1);
136 }
137 
138 u_int
139 winlink_count(struct winlinks *wwl)
140 {
141 	struct winlink	*wl;
142 	u_int		 n;
143 
144 	n = 0;
145 	RB_FOREACH(wl, winlinks, wwl)
146 		n++;
147 
148 	return (n);
149 }
150 
151 struct winlink *
152 winlink_add(struct winlinks *wwl, int idx)
153 {
154 	struct winlink	*wl;
155 
156 	if (idx < 0) {
157 		if ((idx = winlink_next_index(wwl, -idx - 1)) == -1)
158 			return (NULL);
159 	} else if (winlink_find_by_index(wwl, idx) != NULL)
160 		return (NULL);
161 
162 	wl = xcalloc(1, sizeof *wl);
163 	wl->idx = idx;
164 	RB_INSERT(winlinks, wwl, wl);
165 
166 	return (wl);
167 }
168 
169 void
170 winlink_set_window(struct winlink *wl, struct window *w)
171 {
172 	wl->window = w;
173 	w->references++;
174 }
175 
176 void
177 winlink_remove(struct winlinks *wwl, struct winlink *wl)
178 {
179 	struct window	*w = wl->window;
180 
181 	RB_REMOVE(winlinks, wwl, wl);
182 	free(wl->status_text);
183 	free(wl);
184 
185 	if (w != NULL) {
186 		if (w->references == 0)
187 			fatal("bad reference count");
188 		w->references--;
189 		if (w->references == 0)
190 			window_destroy(w);
191 	}
192 }
193 
194 struct winlink *
195 winlink_next(struct winlink *wl)
196 {
197 	return (RB_NEXT(winlinks, wwl, wl));
198 }
199 
200 struct winlink *
201 winlink_previous(struct winlink *wl)
202 {
203 	return (RB_PREV(winlinks, wwl, wl));
204 }
205 
206 struct winlink *
207 winlink_next_by_number(struct winlink *wl, struct session *s, int n)
208 {
209 	for (; n > 0; n--) {
210 		if ((wl = RB_NEXT(winlinks, wwl, wl)) == NULL)
211 			wl = RB_MIN(winlinks, &s->windows);
212 	}
213 
214 	return (wl);
215 }
216 
217 struct winlink *
218 winlink_previous_by_number(struct winlink *wl, struct session *s, int n)
219 {
220 	for (; n > 0; n--) {
221 		if ((wl = RB_PREV(winlinks, wwl, wl)) == NULL)
222 			wl = RB_MAX(winlinks, &s->windows);
223 	}
224 
225 	return (wl);
226 }
227 
228 void
229 winlink_stack_push(struct winlink_stack *stack, struct winlink *wl)
230 {
231 	if (wl == NULL)
232 		return;
233 
234 	winlink_stack_remove(stack, wl);
235 	TAILQ_INSERT_HEAD(stack, wl, sentry);
236 }
237 
238 void
239 winlink_stack_remove(struct winlink_stack *stack, struct winlink *wl)
240 {
241 	struct winlink	*wl2;
242 
243 	if (wl == NULL)
244 		return;
245 
246 	TAILQ_FOREACH(wl2, stack, sentry) {
247 		if (wl2 == wl) {
248 			TAILQ_REMOVE(stack, wl, sentry);
249 			return;
250 		}
251 	}
252 }
253 
254 int
255 window_index(struct window *s, u_int *i)
256 {
257 	for (*i = 0; *i < ARRAY_LENGTH(&windows); (*i)++) {
258 		if (s == ARRAY_ITEM(&windows, *i))
259 			return (0);
260 	}
261 	return (-1);
262 }
263 
264 struct window *
265 window_find_by_id(u_int id)
266 {
267 	struct window	*w;
268 	u_int		 i;
269 
270 	for (i = 0; i < ARRAY_LENGTH(&windows); i++) {
271 		w = ARRAY_ITEM(&windows, i);
272 		if (w->id == id)
273 			return (w);
274 	}
275 	return (NULL);
276 }
277 
278 struct window *
279 window_create1(u_int sx, u_int sy)
280 {
281 	struct window	*w;
282 	u_int		 i;
283 
284 	w = xcalloc(1, sizeof *w);
285 	w->id = next_window_id++;
286 	w->name = NULL;
287 	w->flags = 0;
288 
289 	TAILQ_INIT(&w->panes);
290 	w->active = NULL;
291 
292 	w->lastlayout = -1;
293 	w->layout_root = NULL;
294 	TAILQ_INIT(&w->layout_list);
295 
296 	w->sx = sx;
297 	w->sy = sy;
298 
299 	options_init(&w->options, &global_w_options);
300 	if (options_get_number(&w->options, "automatic-rename"))
301 		queue_window_name(w);
302 
303 	for (i = 0; i < ARRAY_LENGTH(&windows); i++) {
304 		if (ARRAY_ITEM(&windows, i) == NULL) {
305 			ARRAY_SET(&windows, i, w);
306 			break;
307 		}
308 	}
309 	if (i == ARRAY_LENGTH(&windows))
310 		ARRAY_ADD(&windows, w);
311 	w->references = 0;
312 
313 	return (w);
314 }
315 
316 struct window *
317 window_create(const char *name, const char *cmd, const char *shell,
318     const char *cwd, struct environ *env, struct termios *tio,
319     u_int sx, u_int sy, u_int hlimit,char **cause)
320 {
321 	struct window		*w;
322 	struct window_pane	*wp;
323 
324 	w = window_create1(sx, sy);
325 	wp = window_add_pane(w, hlimit);
326 	layout_init(w);
327 	if (window_pane_spawn(wp, cmd, shell, cwd, env, tio, cause) != 0) {
328 		window_destroy(w);
329 		return (NULL);
330 	}
331 	w->active = TAILQ_FIRST(&w->panes);
332 	if (name != NULL) {
333 		w->name = xstrdup(name);
334 		options_set_number(&w->options, "automatic-rename", 0);
335 	} else
336 		w->name = default_window_name(w);
337 	return (w);
338 }
339 
340 void
341 window_destroy(struct window *w)
342 {
343 	u_int	i;
344 
345 	if (window_index(w, &i) != 0)
346 		fatalx("index not found");
347 	ARRAY_SET(&windows, i, NULL);
348 	while (!ARRAY_EMPTY(&windows) && ARRAY_LAST(&windows) == NULL)
349 		ARRAY_TRUNC(&windows, 1);
350 
351 	if (w->layout_root != NULL)
352 		layout_free(w);
353 
354 	if (event_initialized(&w->name_timer))
355 		evtimer_del(&w->name_timer);
356 
357 	options_free(&w->options);
358 
359 	window_destroy_panes(w);
360 
361 	free(w->name);
362 	free(w);
363 }
364 
365 void
366 window_set_name(struct window *w, const char *new_name)
367 {
368 	free(w->name);
369 	w->name = xstrdup(new_name);
370 	notify_window_renamed(w);
371 }
372 
373 void
374 window_resize(struct window *w, u_int sx, u_int sy)
375 {
376 	w->sx = sx;
377 	w->sy = sy;
378 }
379 
380 void
381 window_set_active_pane(struct window *w, struct window_pane *wp)
382 {
383 	if (wp == w->active)
384 		return;
385 	w->last = w->active;
386 	w->active = wp;
387 	while (!window_pane_visible(w->active)) {
388 		w->active = TAILQ_PREV(w->active, window_panes, entry);
389 		if (w->active == NULL)
390 			w->active = TAILQ_LAST(&w->panes, window_panes);
391 		if (w->active == wp)
392 			return;
393 	}
394 }
395 
396 struct window_pane *
397 window_get_active_at(struct window *w, u_int x, u_int y)
398 {
399 	struct window_pane	*wp;
400 
401 	TAILQ_FOREACH(wp, &w->panes, entry) {
402 		if (!window_pane_visible(wp))
403 			continue;
404 		if (x < wp->xoff || x > wp->xoff + wp->sx)
405 			continue;
406 		if (y < wp->yoff || y > wp->yoff + wp->sy)
407 			continue;
408 		return (wp);
409 	}
410 	return (NULL);
411 }
412 
413 void
414 window_set_active_at(struct window *w, u_int x, u_int y)
415 {
416 	struct window_pane	*wp;
417 
418 	wp = window_get_active_at(w, x, y);
419 	if (wp != NULL && wp != w->active)
420 		window_set_active_pane(w, wp);
421 }
422 
423 struct window_pane *
424 window_find_string(struct window *w, const char *s)
425 {
426 	u_int	x, y;
427 
428 	x = w->sx / 2;
429 	y = w->sy / 2;
430 
431 	if (strcasecmp(s, "top") == 0)
432 		y = 0;
433 	else if (strcasecmp(s, "bottom") == 0)
434 		y = w->sy - 1;
435 	else if (strcasecmp(s, "left") == 0)
436 		x = 0;
437 	else if (strcasecmp(s, "right") == 0)
438 		x = w->sx - 1;
439 	else if (strcasecmp(s, "top-left") == 0) {
440 		x = 0;
441 		y = 0;
442 	} else if (strcasecmp(s, "top-right") == 0) {
443 		x = w->sx - 1;
444 		y = 0;
445 	} else if (strcasecmp(s, "bottom-left") == 0) {
446 		x = 0;
447 		y = w->sy - 1;
448 	} else if (strcasecmp(s, "bottom-right") == 0) {
449 		x = w->sx - 1;
450 		y = w->sy - 1;
451 	} else
452 		return (NULL);
453 
454 	return (window_get_active_at(w, x, y));
455 }
456 
457 struct window_pane *
458 window_add_pane(struct window *w, u_int hlimit)
459 {
460 	struct window_pane	*wp;
461 
462 	wp = window_pane_create(w, w->sx, w->sy, hlimit);
463 	if (TAILQ_EMPTY(&w->panes))
464 		TAILQ_INSERT_HEAD(&w->panes, wp, entry);
465 	else
466 		TAILQ_INSERT_AFTER(&w->panes, w->active, wp, entry);
467 	return (wp);
468 }
469 
470 void
471 window_remove_pane(struct window *w, struct window_pane *wp)
472 {
473 	if (wp == w->active) {
474 		w->active = w->last;
475 		w->last = NULL;
476 		if (w->active == NULL) {
477 			w->active = TAILQ_PREV(wp, window_panes, entry);
478 			if (w->active == NULL)
479 				w->active = TAILQ_NEXT(wp, entry);
480 		}
481 	} else if (wp == w->last)
482 		w->last = NULL;
483 
484 	TAILQ_REMOVE(&w->panes, wp, entry);
485 	window_pane_destroy(wp);
486 }
487 
488 struct window_pane *
489 window_pane_at_index(struct window *w, u_int idx)
490 {
491 	struct window_pane	*wp;
492 	u_int			 n;
493 
494 	n = options_get_number(&w->options, "pane-base-index");
495 	TAILQ_FOREACH(wp, &w->panes, entry) {
496 		if (n == idx)
497 			return (wp);
498 		n++;
499 	}
500 	return (NULL);
501 }
502 
503 struct window_pane *
504 window_pane_next_by_number(struct window *w, struct window_pane *wp, u_int n)
505 {
506 	for (; n > 0; n--) {
507 		if ((wp = TAILQ_NEXT(wp, entry)) == NULL)
508 			wp = TAILQ_FIRST(&w->panes);
509 	}
510 
511 	return (wp);
512 }
513 
514 struct window_pane *
515 window_pane_previous_by_number(struct window *w, struct window_pane *wp,
516     u_int n)
517 {
518 	for (; n > 0; n--) {
519 		if ((wp = TAILQ_PREV(wp, window_panes, entry)) == NULL)
520 			wp = TAILQ_LAST(&w->panes, window_panes);
521 	}
522 
523 	return (wp);
524 }
525 
526 int
527 window_pane_index(struct window_pane *wp, u_int *i)
528 {
529 	struct window_pane	*wq;
530 	struct window		*w = wp->window;
531 
532 	*i = options_get_number(&w->options, "pane-base-index");
533 	TAILQ_FOREACH(wq, &w->panes, entry) {
534 		if (wp == wq) {
535 			return (0);
536 		}
537 		(*i)++;
538 	}
539 
540 	return (-1);
541 }
542 
543 u_int
544 window_count_panes(struct window *w)
545 {
546 	struct window_pane	*wp;
547 	u_int			 n;
548 
549 	n = 0;
550 	TAILQ_FOREACH(wp, &w->panes, entry)
551 		n++;
552 	return (n);
553 }
554 
555 void
556 window_destroy_panes(struct window *w)
557 {
558 	struct window_pane	*wp;
559 
560 	while (!TAILQ_EMPTY(&w->panes)) {
561 		wp = TAILQ_FIRST(&w->panes);
562 		TAILQ_REMOVE(&w->panes, wp, entry);
563 		window_pane_destroy(wp);
564 	}
565 }
566 
567 /* Return list of printable window flag symbols. No flags is just a space. */
568 char *
569 window_printable_flags(struct session *s, struct winlink *wl)
570 {
571 	char	flags[BUFSIZ];
572 	int	pos;
573 
574 	pos = 0;
575 	if (wl->flags & WINLINK_ACTIVITY)
576 		flags[pos++] = '#';
577 	if (wl->flags & WINLINK_BELL)
578 		flags[pos++] = '!';
579 	if (wl->flags & WINLINK_CONTENT)
580 		flags[pos++] = '+';
581 	if (wl->flags & WINLINK_SILENCE)
582 		flags[pos++] = '~';
583 	if (wl == s->curw)
584 		flags[pos++] = '*';
585 	if (wl == TAILQ_FIRST(&s->lastw))
586 		flags[pos++] = '-';
587 	if (pos == 0)
588 		flags[pos++] = ' ';
589 	flags[pos] = '\0';
590 	return (xstrdup(flags));
591 }
592 
593 /* Find pane in global tree by id. */
594 struct window_pane *
595 window_pane_find_by_id(u_int id)
596 {
597 	struct window_pane	wp;
598 
599 	wp.id = id;
600 	return (RB_FIND(window_pane_tree, &all_window_panes, &wp));
601 }
602 
603 struct window_pane *
604 window_pane_create(struct window *w, u_int sx, u_int sy, u_int hlimit)
605 {
606 	struct window_pane	*wp;
607 
608 	wp = xcalloc(1, sizeof *wp);
609 	wp->window = w;
610 
611 	wp->id = next_window_pane_id++;
612 	RB_INSERT(window_pane_tree, &all_window_panes, wp);
613 
614 	wp->cmd = NULL;
615 	wp->shell = NULL;
616 	wp->cwd = NULL;
617 
618 	wp->fd = -1;
619 	wp->event = NULL;
620 
621 	wp->mode = NULL;
622 
623 	wp->layout_cell = NULL;
624 
625 	wp->xoff = 0;
626 	wp->yoff = 0;
627 
628 	wp->sx = sx;
629 	wp->sy = sy;
630 
631 	wp->pipe_fd = -1;
632 	wp->pipe_off = 0;
633 	wp->pipe_event = NULL;
634 
635 	wp->saved_grid = NULL;
636 
637 	screen_init(&wp->base, sx, sy, hlimit);
638 	wp->screen = &wp->base;
639 
640 	input_init(wp);
641 
642 	return (wp);
643 }
644 
645 void
646 window_pane_destroy(struct window_pane *wp)
647 {
648 	window_pane_reset_mode(wp);
649 
650 	if (event_initialized(&wp->changes_timer))
651 		evtimer_del(&wp->changes_timer);
652 
653 	if (wp->fd != -1) {
654 		bufferevent_free(wp->event);
655 		close(wp->fd);
656 	}
657 
658 	input_free(wp);
659 
660 	screen_free(&wp->base);
661 	if (wp->saved_grid != NULL)
662 		grid_destroy(wp->saved_grid);
663 
664 	if (wp->pipe_fd != -1) {
665 		bufferevent_free(wp->pipe_event);
666 		close(wp->pipe_fd);
667 	}
668 
669 	RB_REMOVE(window_pane_tree, &all_window_panes, wp);
670 
671 	free(wp->cwd);
672 	free(wp->shell);
673 	free(wp->cmd);
674 	free(wp);
675 }
676 
677 int
678 window_pane_spawn(struct window_pane *wp, const char *cmd, const char *shell,
679     const char *cwd, struct environ *env, struct termios *tio, char **cause)
680 {
681 	struct winsize	 ws;
682 	char		*argv0, paneid[16];
683 	const char	*ptr;
684 	struct termios	 tio2;
685 
686 	if (wp->fd != -1) {
687 		bufferevent_free(wp->event);
688 		close(wp->fd);
689 	}
690 	if (cmd != NULL) {
691 		free(wp->cmd);
692 		wp->cmd = xstrdup(cmd);
693 	}
694 	if (shell != NULL) {
695 		free(wp->shell);
696 		wp->shell = xstrdup(shell);
697 	}
698 	if (cwd != NULL) {
699 		free(wp->cwd);
700 		wp->cwd = xstrdup(cwd);
701 	}
702 
703 	memset(&ws, 0, sizeof ws);
704 	ws.ws_col = screen_size_x(&wp->base);
705 	ws.ws_row = screen_size_y(&wp->base);
706 
707 	switch (wp->pid = forkpty(&wp->fd, wp->tty, NULL, &ws)) {
708 	case -1:
709 		wp->fd = -1;
710 		xasprintf(cause, "%s: %s", cmd, strerror(errno));
711 		return (-1);
712 	case 0:
713 		if (chdir(wp->cwd) != 0)
714 			chdir("/");
715 
716 		if (tcgetattr(STDIN_FILENO, &tio2) != 0)
717 			fatal("tcgetattr failed");
718 		if (tio != NULL)
719 			memcpy(tio2.c_cc, tio->c_cc, sizeof tio2.c_cc);
720 		tio2.c_cc[VERASE] = '\177';
721 		if (tcsetattr(STDIN_FILENO, TCSANOW, &tio2) != 0)
722 			fatal("tcgetattr failed");
723 
724 		closefrom(STDERR_FILENO + 1);
725 
726 		xsnprintf(paneid, sizeof paneid, "%%%u", wp->id);
727 		environ_set(env, "TMUX_PANE", paneid);
728 		environ_push(env);
729 
730 		clear_signals(1);
731 		log_close();
732 
733 		setenv("SHELL", wp->shell, 1);
734 		ptr = strrchr(wp->shell, '/');
735 
736 		if (*wp->cmd != '\0') {
737 			/* Use the command. */
738 			if (ptr != NULL && *(ptr + 1) != '\0')
739 				xasprintf(&argv0, "%s", ptr + 1);
740 			else
741 				xasprintf(&argv0, "%s", wp->shell);
742 			execl(wp->shell, argv0, "-c", wp->cmd, (char *) NULL);
743 			fatal("execl failed");
744 		}
745 
746 		/* No command; fork a login shell. */
747 		if (ptr != NULL && *(ptr + 1) != '\0')
748 			xasprintf(&argv0, "-%s", ptr + 1);
749 		else
750 			xasprintf(&argv0, "-%s", wp->shell);
751 		execl(wp->shell, argv0, (char *) NULL);
752 		fatal("execl failed");
753 	}
754 
755 	setblocking(wp->fd, 0);
756 
757 	wp->event = bufferevent_new(wp->fd,
758 	    window_pane_read_callback, NULL, window_pane_error_callback, wp);
759 	bufferevent_enable(wp->event, EV_READ|EV_WRITE);
760 
761 	return (0);
762 }
763 
764 void
765 window_pane_timer_start(struct window_pane *wp)
766 {
767 	struct timeval	tv;
768 
769 	tv.tv_sec = 0;
770 	tv.tv_usec = 1000;
771 
772 	evtimer_del(&wp->changes_timer);
773 	evtimer_set(&wp->changes_timer, window_pane_timer_callback, wp);
774 	evtimer_add(&wp->changes_timer, &tv);
775 }
776 
777 void
778 window_pane_timer_callback(unused int fd, unused short events, void *data)
779 {
780 	struct window_pane	*wp = data;
781 	struct window		*w = wp->window;
782 	u_int			 interval, trigger;
783 
784 	interval = options_get_number(&w->options, "c0-change-interval");
785 	trigger = options_get_number(&w->options, "c0-change-trigger");
786 
787 	if (wp->changes_redraw++ == interval) {
788 		wp->flags |= PANE_REDRAW;
789 		wp->changes_redraw = 0;
790 
791 	}
792 
793 	if (trigger == 0 || wp->changes < trigger) {
794 		wp->flags |= PANE_REDRAW;
795 		wp->flags &= ~PANE_DROP;
796 	} else
797 		window_pane_timer_start(wp);
798 	wp->changes = 0;
799 }
800 
801 /* ARGSUSED */
802 void
803 window_pane_read_callback(unused struct bufferevent *bufev, void *data)
804 {
805 	struct window_pane     *wp = data;
806 	char   		       *new_data;
807 	size_t			new_size;
808 
809 	new_size = EVBUFFER_LENGTH(wp->event->input) - wp->pipe_off;
810 	if (wp->pipe_fd != -1 && new_size > 0) {
811 		new_data = EVBUFFER_DATA(wp->event->input);
812 		bufferevent_write(wp->pipe_event, new_data, new_size);
813 	}
814 
815 	input_parse(wp);
816 
817 	wp->pipe_off = EVBUFFER_LENGTH(wp->event->input);
818 
819 	/*
820 	 * If we get here, we're not outputting anymore, so set the silence
821 	 * flag on the window.
822 	 */
823 	wp->window->flags |= WINDOW_SILENCE;
824 	if (gettimeofday(&wp->window->silence_timer, NULL) != 0)
825 		fatal("gettimeofday failed.");
826 }
827 
828 /* ARGSUSED */
829 void
830 window_pane_error_callback(
831     unused struct bufferevent *bufev, unused short what, void *data)
832 {
833 	struct window_pane *wp = data;
834 
835 	server_destroy_pane(wp);
836 }
837 
838 void
839 window_pane_resize(struct window_pane *wp, u_int sx, u_int sy)
840 {
841 	struct winsize	ws;
842 
843 	if (sx == wp->sx && sy == wp->sy)
844 		return;
845 	wp->sx = sx;
846 	wp->sy = sy;
847 
848 	memset(&ws, 0, sizeof ws);
849 	ws.ws_col = sx;
850 	ws.ws_row = sy;
851 
852 	screen_resize(&wp->base, sx, sy);
853 	if (wp->mode != NULL)
854 		wp->mode->resize(wp, sx, sy);
855 
856 	if (wp->fd != -1 && ioctl(wp->fd, TIOCSWINSZ, &ws) == -1)
857 		fatal("ioctl failed");
858 }
859 
860 /*
861  * Enter alternative screen mode. A copy of the visible screen is saved and the
862  * history is not updated
863  */
864 void
865 window_pane_alternate_on(struct window_pane *wp, struct grid_cell *gc)
866 {
867 	struct screen	*s = &wp->base;
868 	u_int		 sx, sy;
869 
870 	if (wp->saved_grid != NULL)
871 		return;
872 	if (!options_get_number(&wp->window->options, "alternate-screen"))
873 		return;
874 	sx = screen_size_x(s);
875 	sy = screen_size_y(s);
876 
877 	wp->saved_grid = grid_create(sx, sy, 0);
878 	grid_duplicate_lines(wp->saved_grid, 0, s->grid, screen_hsize(s), sy);
879 	wp->saved_cx = s->cx;
880 	wp->saved_cy = s->cy;
881 	memcpy(&wp->saved_cell, gc, sizeof wp->saved_cell);
882 
883 	grid_view_clear(s->grid, 0, 0, sx, sy);
884 
885 	wp->base.grid->flags &= ~GRID_HISTORY;
886 
887 	wp->flags |= PANE_REDRAW;
888 }
889 
890 /* Exit alternate screen mode and restore the copied grid. */
891 void
892 window_pane_alternate_off(struct window_pane *wp, struct grid_cell *gc)
893 {
894 	struct screen	*s = &wp->base;
895 	u_int		 sx, sy;
896 
897 	if (wp->saved_grid == NULL)
898 		return;
899 	if (!options_get_number(&wp->window->options, "alternate-screen"))
900 		return;
901 	sx = screen_size_x(s);
902 	sy = screen_size_y(s);
903 
904 	/*
905 	 * If the current size is bigger, temporarily resize to the old size
906 	 * before copying back.
907 	 */
908 	if (sy > wp->saved_grid->sy)
909 		screen_resize(s, sx, wp->saved_grid->sy);
910 
911 	/* Restore the grid, cursor position and cell. */
912 	grid_duplicate_lines(s->grid, screen_hsize(s), wp->saved_grid, 0, sy);
913 	s->cx = wp->saved_cx;
914 	if (s->cx > screen_size_x(s) - 1)
915 		s->cx = screen_size_x(s) - 1;
916 	s->cy = wp->saved_cy;
917 	if (s->cy > screen_size_y(s) - 1)
918 		s->cy = screen_size_y(s) - 1;
919 	memcpy(gc, &wp->saved_cell, sizeof *gc);
920 
921 	/*
922 	 * Turn history back on (so resize can use it) and then resize back to
923 	 * the current size.
924 	 */
925 	wp->base.grid->flags |= GRID_HISTORY;
926 	if (sy > wp->saved_grid->sy)
927 		screen_resize(s, sx, sy);
928 
929 	grid_destroy(wp->saved_grid);
930 	wp->saved_grid = NULL;
931 
932 	wp->flags |= PANE_REDRAW;
933 }
934 
935 int
936 window_pane_set_mode(struct window_pane *wp, const struct window_mode *mode)
937 {
938 	struct screen	*s;
939 
940 	if (wp->mode != NULL)
941 		return (1);
942 	wp->mode = mode;
943 
944 	if ((s = wp->mode->init(wp)) != NULL)
945 		wp->screen = s;
946 	wp->flags |= PANE_REDRAW;
947 	return (0);
948 }
949 
950 void
951 window_pane_reset_mode(struct window_pane *wp)
952 {
953 	if (wp->mode == NULL)
954 		return;
955 
956 	wp->mode->free(wp);
957 	wp->mode = NULL;
958 
959 	wp->screen = &wp->base;
960 	wp->flags |= PANE_REDRAW;
961 }
962 
963 void
964 window_pane_key(struct window_pane *wp, struct session *sess, int key)
965 {
966 	struct window_pane	*wp2;
967 
968 	if (!window_pane_visible(wp))
969 		return;
970 
971 	if (wp->mode != NULL) {
972 		if (wp->mode->key != NULL)
973 			wp->mode->key(wp, sess, key);
974 		return;
975 	}
976 
977 	if (wp->fd == -1)
978 		return;
979 	input_key(wp, key);
980 	if (options_get_number(&wp->window->options, "synchronize-panes")) {
981 		TAILQ_FOREACH(wp2, &wp->window->panes, entry) {
982 			if (wp2 == wp || wp2->mode != NULL)
983 				continue;
984 			if (wp2->fd != -1 && window_pane_visible(wp2))
985 				input_key(wp2, key);
986 		}
987 	}
988 }
989 
990 void
991 window_pane_mouse(
992     struct window_pane *wp, struct session *sess, struct mouse_event *m)
993 {
994 	if (!window_pane_visible(wp))
995 		return;
996 
997 	if (m->x < wp->xoff || m->x >= wp->xoff + wp->sx)
998 		return;
999 	if (m->y < wp->yoff || m->y >= wp->yoff + wp->sy)
1000 		return;
1001 	m->x -= wp->xoff;
1002 	m->y -= wp->yoff;
1003 
1004 	if (wp->mode != NULL) {
1005 		if (wp->mode->mouse != NULL &&
1006 		    options_get_number(&wp->window->options, "mode-mouse"))
1007 			wp->mode->mouse(wp, sess, m);
1008 	} else if (wp->fd != -1)
1009 		input_mouse(wp, m);
1010 }
1011 
1012 int
1013 window_pane_visible(struct window_pane *wp)
1014 {
1015 	struct window	*w = wp->window;
1016 
1017 	if (wp->xoff >= w->sx || wp->yoff >= w->sy)
1018 		return (0);
1019 	if (wp->xoff + wp->sx > w->sx || wp->yoff + wp->sy > w->sy)
1020 		return (0);
1021 	return (1);
1022 }
1023 
1024 char *
1025 window_pane_search(struct window_pane *wp, const char *searchstr, u_int *lineno)
1026 {
1027 	struct screen	*s = &wp->base;
1028 	char		*newsearchstr, *line, *msg;
1029 	u_int	 	 i;
1030 
1031 	msg = NULL;
1032 	xasprintf(&newsearchstr, "*%s*", searchstr);
1033 
1034 	for (i = 0; i < screen_size_y(s); i++) {
1035 		line = grid_view_string_cells(s->grid, 0, i, screen_size_x(s));
1036 		if (fnmatch(newsearchstr, line, 0) == 0) {
1037 			msg = line;
1038 			if (lineno != NULL)
1039 				*lineno = i;
1040 			break;
1041 		}
1042 		free(line);
1043 	}
1044 
1045 	free(newsearchstr);
1046 	return (msg);
1047 }
1048 
1049 /* Find the pane directly above another. */
1050 struct window_pane *
1051 window_pane_find_up(struct window_pane *wp)
1052 {
1053 	struct window_pane     *wp2;
1054 	u_int			left, top;
1055 
1056 	if (wp == NULL || !window_pane_visible(wp))
1057 		return (NULL);
1058 
1059 	top = wp->yoff;
1060 	if (top == 0)
1061 		top = wp->window->sy + 1;
1062 	left = wp->xoff;
1063 
1064 	TAILQ_FOREACH(wp2, &wp->window->panes, entry) {
1065 		if (!window_pane_visible(wp2))
1066 			continue;
1067 		if (wp2->yoff + wp2->sy + 1 != top)
1068 			continue;
1069 		if (left >= wp2->xoff && left <= wp2->xoff + wp2->sx)
1070 			return (wp2);
1071 	}
1072 	return (NULL);
1073 }
1074 
1075 /* Find the pane directly below another. */
1076 struct window_pane *
1077 window_pane_find_down(struct window_pane *wp)
1078 {
1079 	struct window_pane     *wp2;
1080 	u_int			left, bottom;
1081 
1082 	if (wp == NULL || !window_pane_visible(wp))
1083 		return (NULL);
1084 
1085 	bottom = wp->yoff + wp->sy + 1;
1086 	if (bottom >= wp->window->sy)
1087 		bottom = 0;
1088 	left = wp->xoff;
1089 
1090 	TAILQ_FOREACH(wp2, &wp->window->panes, entry) {
1091 		if (!window_pane_visible(wp2))
1092 			continue;
1093 		if (wp2->yoff != bottom)
1094 			continue;
1095 		if (left >= wp2->xoff && left <= wp2->xoff + wp2->sx)
1096 			return (wp2);
1097 	}
1098 	return (NULL);
1099 }
1100 
1101 /*
1102  * Find the pane directly to the left of another, adjacent to the left side and
1103  * containing the top edge.
1104  */
1105 struct window_pane *
1106 window_pane_find_left(struct window_pane *wp)
1107 {
1108 	struct window_pane     *wp2;
1109 	u_int			left, top;
1110 
1111 	if (wp == NULL || !window_pane_visible(wp))
1112 		return (NULL);
1113 
1114 	left = wp->xoff;
1115 	if (left == 0)
1116 		left = wp->window->sx + 1;
1117 	top = wp->yoff;
1118 
1119 	TAILQ_FOREACH(wp2, &wp->window->panes, entry) {
1120 		if (!window_pane_visible(wp2))
1121 			continue;
1122 		if (wp2->xoff + wp2->sx + 1 != left)
1123 			continue;
1124 		if (top >= wp2->yoff && top <= wp2->yoff + wp2->sy)
1125 			return (wp2);
1126 	}
1127 	return (NULL);
1128 }
1129 
1130 /*
1131  * Find the pane directly to the right of another, that is adjacent to the
1132  * right edge and including the top edge.
1133  */
1134 struct window_pane *
1135 window_pane_find_right(struct window_pane *wp)
1136 {
1137 	struct window_pane     *wp2;
1138 	u_int			right, top;
1139 
1140 	if (wp == NULL || !window_pane_visible(wp))
1141 		return (NULL);
1142 
1143 	right = wp->xoff + wp->sx + 1;
1144 	if (right >= wp->window->sx)
1145 		right = 0;
1146 	top = wp->yoff;
1147 
1148 	TAILQ_FOREACH(wp2, &wp->window->panes, entry) {
1149 		if (!window_pane_visible(wp2))
1150 			continue;
1151 		if (wp2->xoff != right)
1152 			continue;
1153 		if (top >= wp2->yoff && top <= wp2->yoff + wp2->sy)
1154 			return (wp2);
1155 	}
1156 	return (NULL);
1157 }
1158 
1159 /* Clear alert flags for a winlink */
1160 void
1161 winlink_clear_flags(struct winlink *wl)
1162 {
1163 	struct winlink	*wm;
1164 	struct session	*s;
1165 	struct window	*w;
1166 	u_int		 i;
1167 
1168 	for (i = 0; i < ARRAY_LENGTH(&windows); i++) {
1169 		if ((w = ARRAY_ITEM(&windows, i)) == NULL)
1170 			continue;
1171 
1172 		RB_FOREACH(s, sessions, &sessions) {
1173 			if ((wm = session_has(s, w)) == NULL)
1174 				continue;
1175 
1176 			if (wm->window != wl->window)
1177 				continue;
1178 			if ((wm->flags & WINLINK_ALERTFLAGS) == 0)
1179 				continue;
1180 
1181 			wm->flags &= ~WINLINK_ALERTFLAGS;
1182 			server_status_session(s);
1183 		}
1184 	}
1185 }
1186