xref: /netbsd-src/external/bsd/tmux/dist/window.c (revision 1b9578b8c2c1f848eeb16dabbfd7d1f0d9fdefbd)
1 /* $Id: window.c,v 1.2 2011/03/12 03:02:59 christos 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 <pwd.h>
26 #include <signal.h>
27 #include <stdlib.h>
28 #include <string.h>
29 #include <termios.h>
30 #include <unistd.h>
31 
32 #include "tmux.h"
33 
34 /*
35  * Each window is attached to a number of panes, each of which is a pty. This
36  * file contains code to handle them.
37  *
38  * A pane has two buffers attached, these are filled and emptied by the main
39  * server poll loop. Output data is received from pty's in screen format,
40  * translated and returned as a series of escape sequences and strings via
41  * input_parse (in input.c). Input data is received as key codes and written
42  * directly via input_key.
43  *
44  * Each pane also has a "virtual" screen (screen.c) which contains the current
45  * state and is redisplayed when the window is reattached to a client.
46  *
47  * Windows are stored directly on a global array and wrapped in any number of
48  * winlink structs to be linked onto local session RB trees. A reference count
49  * is maintained and a window removed from the global list and destroyed when
50  * it reaches zero.
51  */
52 
53 /* Global window list. */
54 struct windows windows;
55 
56 void	window_pane_read_callback(struct bufferevent *, void *);
57 void	window_pane_error_callback(struct bufferevent *, short, void *);
58 
59 RB_GENERATE(winlinks, winlink, entry, winlink_cmp);
60 
61 int
62 winlink_cmp(struct winlink *wl1, struct winlink *wl2)
63 {
64 	return (wl1->idx - wl2->idx);
65 }
66 
67 struct winlink *
68 winlink_find_by_window(struct winlinks *wwl, struct window *w)
69 {
70 	struct winlink	*wl;
71 
72 	RB_FOREACH(wl, winlinks, wwl) {
73 		if (wl->window == w)
74 			return (wl);
75 	}
76 
77 	return (NULL);
78 }
79 
80 struct winlink *
81 winlink_find_by_index(struct winlinks *wwl, int idx)
82 {
83 	struct winlink	wl;
84 
85 	if (idx < 0)
86 		fatalx("bad index");
87 
88 	wl.idx = idx;
89 	return (RB_FIND(winlinks, wwl, &wl));
90 }
91 
92 int
93 winlink_next_index(struct winlinks *wwl, int idx)
94 {
95 	int	i;
96 
97 	i = idx;
98 	do {
99 		if (winlink_find_by_index(wwl, i) == NULL)
100 			return (i);
101 		if (i == INT_MAX)
102 			i = 0;
103 		else
104 			i++;
105 	} while (i != idx);
106 	return (-1);
107 }
108 
109 u_int
110 winlink_count(struct winlinks *wwl)
111 {
112 	struct winlink	*wl;
113 	u_int		 n;
114 
115 	n = 0;
116 	RB_FOREACH(wl, winlinks, wwl)
117 		n++;
118 
119 	return (n);
120 }
121 
122 struct winlink *
123 winlink_add(struct winlinks *wwl, struct window *w, int idx)
124 {
125 	struct winlink	*wl;
126 
127 	if (idx < 0) {
128 		if ((idx = winlink_next_index(wwl, -idx - 1)) == -1)
129 			return (NULL);
130 	} else if (winlink_find_by_index(wwl, idx) != NULL)
131 		return (NULL);
132 
133 	wl = xcalloc(1, sizeof *wl);
134 	wl->idx = idx;
135 	wl->window = w;
136 	RB_INSERT(winlinks, wwl, wl);
137 
138 	w->references++;
139 
140 	return (wl);
141 }
142 
143 void
144 winlink_remove(struct winlinks *wwl, struct winlink *wl)
145 {
146 	struct window	*w = wl->window;
147 
148 	RB_REMOVE(winlinks, wwl, wl);
149 	if (wl->status_text != NULL)
150 		xfree(wl->status_text);
151 	xfree(wl);
152 
153 	if (w->references == 0)
154 		fatal("bad reference count");
155 	w->references--;
156 	if (w->references == 0)
157 		window_destroy(w);
158 }
159 
160 struct winlink *
161 winlink_next(struct winlink *wl)
162 {
163 	return (RB_NEXT(winlinks, wwl, wl));
164 }
165 
166 struct winlink *
167 winlink_previous(struct winlink *wl)
168 {
169 	return (RB_PREV(winlinks, wwl, wl));
170 }
171 
172 struct winlink *
173 winlink_next_by_number(struct winlink *wl, struct session *s, int n)
174 {
175 	for (; n > 0; n--) {
176 		if ((wl = RB_NEXT(winlinks, wwl, wl)) == NULL)
177 			wl = RB_MIN(winlinks, &s->windows);
178 	}
179 
180 	return (wl);
181 }
182 
183 struct winlink *
184 winlink_previous_by_number(struct winlink *wl, struct session *s, int n)
185 {
186 	for (; n > 0; n--) {
187 		if ((wl = RB_PREV(winlinks, wwl, wl)) == NULL)
188 			wl = RB_MAX(winlinks, &s->windows);
189 	}
190 
191 	return (wl);
192 }
193 
194 void
195 winlink_stack_push(struct winlink_stack *stack, struct winlink *wl)
196 {
197 	if (wl == NULL)
198 		return;
199 
200 	winlink_stack_remove(stack, wl);
201 	TAILQ_INSERT_HEAD(stack, wl, sentry);
202 }
203 
204 void
205 winlink_stack_remove(struct winlink_stack *stack, struct winlink *wl)
206 {
207 	struct winlink	*wl2;
208 
209 	if (wl == NULL)
210 		return;
211 
212 	TAILQ_FOREACH(wl2, stack, sentry) {
213 		if (wl2 == wl) {
214 			TAILQ_REMOVE(stack, wl, sentry);
215 			return;
216 		}
217 	}
218 }
219 
220 int
221 window_index(struct window *s, u_int *i)
222 {
223 	for (*i = 0; *i < ARRAY_LENGTH(&windows); (*i)++) {
224 		if (s == ARRAY_ITEM(&windows, *i))
225 			return (0);
226 	}
227 	return (-1);
228 }
229 
230 struct window *
231 window_create1(u_int sx, u_int sy)
232 {
233 	struct window	*w;
234 	u_int		 i;
235 
236 	w = xcalloc(1, sizeof *w);
237 	w->name = NULL;
238 	w->flags = 0;
239 
240 	TAILQ_INIT(&w->panes);
241 	w->active = NULL;
242 
243 	w->lastlayout = -1;
244 	w->layout_root = NULL;
245 
246 	w->sx = sx;
247 	w->sy = sy;
248 
249 	queue_window_name(w);
250 
251 	options_init(&w->options, &global_w_options);
252 
253 	for (i = 0; i < ARRAY_LENGTH(&windows); i++) {
254 		if (ARRAY_ITEM(&windows, i) == NULL) {
255 			ARRAY_SET(&windows, i, w);
256 			break;
257 		}
258 	}
259 	if (i == ARRAY_LENGTH(&windows))
260 		ARRAY_ADD(&windows, w);
261 	w->references = 0;
262 
263 	return (w);
264 }
265 
266 struct window *
267 window_create(const char *name, const char *cmd, const char *shell,
268     const char *cwd, struct environ *env, struct termios *tio,
269     u_int sx, u_int sy, u_int hlimit,char **cause)
270 {
271 	struct window		*w;
272 	struct window_pane	*wp;
273 
274 	w = window_create1(sx, sy);
275 	wp = window_add_pane(w, hlimit);
276 	layout_init(w);
277 	if (window_pane_spawn(wp, cmd, shell, cwd, env, tio, cause) != 0) {
278 		window_destroy(w);
279 		return (NULL);
280 	}
281 	w->active = TAILQ_FIRST(&w->panes);
282 	if (name != NULL) {
283 		w->name = xstrdup(name);
284 		options_set_number(&w->options, "automatic-rename", 0);
285 	} else
286 		w->name = default_window_name(w);
287 	return (w);
288 }
289 
290 void
291 window_destroy(struct window *w)
292 {
293 	u_int	i;
294 
295 	if (window_index(w, &i) != 0)
296 		fatalx("index not found");
297 	ARRAY_SET(&windows, i, NULL);
298 	while (!ARRAY_EMPTY(&windows) && ARRAY_LAST(&windows) == NULL)
299 		ARRAY_TRUNC(&windows, 1);
300 
301 	if (w->layout_root != NULL)
302 		layout_free(w);
303 
304 	evtimer_del(&w->name_timer);
305 
306 	options_free(&w->options);
307 
308 	window_destroy_panes(w);
309 
310 	if (w->name != NULL)
311 		xfree(w->name);
312 	xfree(w);
313 }
314 
315 void
316 window_resize(struct window *w, u_int sx, u_int sy)
317 {
318 	w->sx = sx;
319 	w->sy = sy;
320 }
321 
322 void
323 window_set_active_pane(struct window *w, struct window_pane *wp)
324 {
325 	if (wp == w->active)
326 		return;
327 	w->last = w->active;
328 	w->active = wp;
329 	while (!window_pane_visible(w->active)) {
330 		w->active = TAILQ_PREV(w->active, window_panes, entry);
331 		if (w->active == NULL)
332 			w->active = TAILQ_LAST(&w->panes, window_panes);
333 		if (w->active == wp)
334 			return;
335 	}
336 }
337 
338 void
339 window_set_active_at(struct window *w, u_int x, u_int y)
340 {
341 	struct window_pane	*wp;
342 
343 	TAILQ_FOREACH(wp, &w->panes, entry) {
344 		if (wp == w->active || !window_pane_visible(wp))
345 			continue;
346 		if (x < wp->xoff || x >= wp->xoff + wp->sx)
347 			continue;
348 		if (y < wp->yoff || y >= wp->yoff + wp->sy)
349 			continue;
350 		window_set_active_pane(w, wp);
351 		break;
352 	}
353 }
354 
355 struct window_pane *
356 window_add_pane(struct window *w, u_int hlimit)
357 {
358 	struct window_pane	*wp;
359 
360 	wp = window_pane_create(w, w->sx, w->sy, hlimit);
361 	if (TAILQ_EMPTY(&w->panes))
362 		TAILQ_INSERT_HEAD(&w->panes, wp, entry);
363 	else
364 		TAILQ_INSERT_AFTER(&w->panes, w->active, wp, entry);
365 	return (wp);
366 }
367 
368 void
369 window_remove_pane(struct window *w, struct window_pane *wp)
370 {
371 	if (wp == w->active) {
372 		w->active = w->last;
373 		w->last = NULL;
374 		if (w->active == NULL) {
375 			w->active = TAILQ_PREV(wp, window_panes, entry);
376 			if (w->active == NULL)
377 				w->active = TAILQ_NEXT(wp, entry);
378 		}
379 	} else if (wp == w->last)
380 		w->last = NULL;
381 
382 	TAILQ_REMOVE(&w->panes, wp, entry);
383 	window_pane_destroy(wp);
384 }
385 
386 struct window_pane *
387 window_pane_at_index(struct window *w, u_int idx)
388 {
389 	struct window_pane	*wp;
390 	u_int			 n;
391 
392 	n = 0;
393 	TAILQ_FOREACH(wp, &w->panes, entry) {
394 		if (n == idx)
395 			return (wp);
396 		n++;
397 	}
398 	return (NULL);
399 }
400 
401 struct window_pane *
402 window_pane_next_by_number(struct window *w, struct window_pane *wp, u_int n)
403 {
404 	for (; n > 0; n--) {
405 		if ((wp = TAILQ_NEXT(wp, entry)) == NULL)
406 			wp = TAILQ_FIRST(&w->panes);
407 	}
408 
409 	return (wp);
410 }
411 
412 struct window_pane *
413 window_pane_previous_by_number(struct window *w, struct window_pane *wp,
414     u_int n)
415 {
416 	for (; n > 0; n--) {
417 		if ((wp = TAILQ_PREV(wp, window_panes, entry)) == NULL)
418 			wp = TAILQ_LAST(&w->panes, window_panes);
419 	}
420 
421 	return (wp);
422 }
423 
424 u_int
425 window_pane_index(struct window *w, struct window_pane *wp)
426 {
427 	struct window_pane	*wq;
428 	u_int			 n;
429 
430 	n = 0;
431 	TAILQ_FOREACH(wq, &w->panes, entry) {
432 		if (wp == wq)
433 			break;
434 		n++;
435 	}
436 	return (n);
437 }
438 
439 u_int
440 window_count_panes(struct window *w)
441 {
442 	struct window_pane	*wp;
443 	u_int			 n;
444 
445 	n = 0;
446 	TAILQ_FOREACH(wp, &w->panes, entry)
447 		n++;
448 	return (n);
449 }
450 
451 void
452 window_destroy_panes(struct window *w)
453 {
454 	struct window_pane	*wp;
455 
456 	while (!TAILQ_EMPTY(&w->panes)) {
457 		wp = TAILQ_FIRST(&w->panes);
458 		TAILQ_REMOVE(&w->panes, wp, entry);
459 		window_pane_destroy(wp);
460 	}
461 }
462 
463 struct window_pane *
464 window_pane_create(struct window *w, u_int sx, u_int sy, u_int hlimit)
465 {
466 	struct window_pane	*wp;
467 
468 	wp = xcalloc(1, sizeof *wp);
469 	wp->window = w;
470 
471 	wp->cmd = NULL;
472 	wp->shell = NULL;
473 	wp->cwd = NULL;
474 
475 	wp->fd = -1;
476 	wp->event = NULL;
477 
478 	wp->mode = NULL;
479 
480 	wp->layout_cell = NULL;
481 
482 	wp->xoff = 0;
483 	wp->yoff = 0;
484 
485 	wp->sx = sx;
486 	wp->sy = sy;
487 
488 	wp->pipe_fd = -1;
489 	wp->pipe_off = 0;
490 	wp->pipe_event = NULL;
491 
492 	wp->saved_grid = NULL;
493 
494 	screen_init(&wp->base, sx, sy, hlimit);
495 	wp->screen = &wp->base;
496 
497 	input_init(wp);
498 
499 	return (wp);
500 }
501 
502 void
503 window_pane_destroy(struct window_pane *wp)
504 {
505 	window_pane_reset_mode(wp);
506 
507 	if (wp->fd != -1) {
508 		close(wp->fd);
509 		bufferevent_free(wp->event);
510 	}
511 
512 	input_free(wp);
513 
514 	screen_free(&wp->base);
515 	if (wp->saved_grid != NULL)
516 		grid_destroy(wp->saved_grid);
517 
518 	if (wp->pipe_fd != -1) {
519 		close(wp->pipe_fd);
520 		bufferevent_free(wp->pipe_event);
521 	}
522 
523 	if (wp->cwd != NULL)
524 		xfree(wp->cwd);
525 	if (wp->shell != NULL)
526 		xfree(wp->shell);
527 	if (wp->cmd != NULL)
528 		xfree(wp->cmd);
529 	xfree(wp);
530 }
531 
532 int
533 window_pane_spawn(struct window_pane *wp, const char *cmd, const char *shell,
534     const char *cwd, struct environ *env, struct termios *tio, char **cause)
535 {
536 	struct winsize	 ws;
537 	int		 mode;
538 	char		*argv0;
539 	const char	*ptr;
540 	struct termios	 tio2;
541 
542 	if (wp->fd != -1) {
543 		close(wp->fd);
544 		bufferevent_free(wp->event);
545 	}
546 	if (cmd != NULL) {
547 		if (wp->cmd != NULL)
548 			xfree(wp->cmd);
549 		wp->cmd = xstrdup(cmd);
550 	}
551 	if (shell != NULL) {
552 		if (wp->shell != NULL)
553 			xfree(wp->shell);
554 		wp->shell = xstrdup(shell);
555 	}
556 	if (cwd != NULL) {
557 		if (wp->cwd != NULL)
558 			xfree(wp->cwd);
559 		wp->cwd = xstrdup(cwd);
560 	}
561 
562 	memset(&ws, 0, sizeof ws);
563 	ws.ws_col = screen_size_x(&wp->base);
564 	ws.ws_row = screen_size_y(&wp->base);
565 
566 	switch (wp->pid = forkpty(&wp->fd, wp->tty, NULL, &ws)) {
567 	case -1:
568 		wp->fd = -1;
569 		xasprintf(cause, "%s: %s", cmd, strerror(errno));
570 		return (-1);
571 	case 0:
572 		if (chdir(wp->cwd) != 0)
573 			chdir("/");
574 
575 		if (tcgetattr(STDIN_FILENO, &tio2) != 0)
576 			fatal("tcgetattr failed");
577 		if (tio != NULL)
578 			memcpy(tio2.c_cc, tio->c_cc, sizeof tio2.c_cc);
579 		tio2.c_cc[VERASE] = '\177';
580 		if (tcsetattr(STDIN_FILENO, TCSANOW, &tio2) != 0)
581 			fatal("tcgetattr failed");
582 
583 		closefrom(STDERR_FILENO + 1);
584 
585 		environ_push(env);
586 
587 		clear_signals(1);
588 		log_close();
589 
590 		if (*wp->cmd != '\0') {
591 			/* Set SHELL but only if it is currently not useful. */
592 			shell = getenv("SHELL");
593 			if (shell == NULL || *shell == '\0' || areshell(shell))
594 				setenv("SHELL", wp->shell, 1);
595 
596 			execl(_PATH_BSHELL, "sh", "-c", wp->cmd, (char *) NULL);
597 			fatal("execl failed");
598 		}
599 
600 		/* No command; fork a login shell. */
601 		ptr = strrchr(wp->shell, '/');
602 		if (ptr != NULL && *(ptr + 1) != '\0')
603 			xasprintf(&argv0, "-%s", ptr + 1);
604 		else
605 			xasprintf(&argv0, "-%s", wp->shell);
606 		setenv("SHELL", wp->shell, 1);
607 		execl(wp->shell, argv0, (char *) NULL);
608 		fatal("execl failed");
609 	}
610 
611 	if ((mode = fcntl(wp->fd, F_GETFL)) == -1)
612 		fatal("fcntl failed");
613 	if (fcntl(wp->fd, F_SETFL, mode|O_NONBLOCK) == -1)
614 		fatal("fcntl failed");
615 	wp->event = bufferevent_new(wp->fd,
616 	    window_pane_read_callback, NULL, window_pane_error_callback, wp);
617 	bufferevent_enable(wp->event, EV_READ|EV_WRITE);
618 
619 	return (0);
620 }
621 
622 /* ARGSUSED */
623 void
624 window_pane_read_callback(unused struct bufferevent *bufev, void *data)
625 {
626 	struct window_pane     *wp = data;
627 	char   		       *new_data;
628 	size_t			new_size;
629 
630 	new_size = EVBUFFER_LENGTH(wp->event->input) - wp->pipe_off;
631 	if (wp->pipe_fd != -1 && new_size > 0) {
632 		new_data = (char *)EVBUFFER_DATA(wp->event->input);
633 		bufferevent_write(wp->pipe_event, new_data, new_size);
634 	}
635 
636 	input_parse(wp);
637 
638 	wp->pipe_off = EVBUFFER_LENGTH(wp->event->input);
639 
640 	/*
641 	 * If we get here, we're not outputting anymore, so set the silence
642 	 * flag on the window.
643 	 */
644 	wp->window->flags |= WINDOW_SILENCE;
645 	if (gettimeofday(&wp->window->silence_timer, NULL) != 0)
646 		fatal("gettimeofday failed.");
647 }
648 
649 /* ARGSUSED */
650 void
651 window_pane_error_callback(
652     unused struct bufferevent *bufev, unused short what, void *data)
653 {
654 	struct window_pane *wp = data;
655 
656 	server_destroy_pane(wp);
657 }
658 
659 void
660 window_pane_resize(struct window_pane *wp, u_int sx, u_int sy)
661 {
662 	struct winsize	ws;
663 
664 	if (sx == wp->sx && sy == wp->sy)
665 		return;
666 	wp->sx = sx;
667 	wp->sy = sy;
668 
669 	memset(&ws, 0, sizeof ws);
670 	ws.ws_col = sx;
671 	ws.ws_row = sy;
672 
673 	screen_resize(&wp->base, sx, sy);
674 	if (wp->mode != NULL)
675 		wp->mode->resize(wp, sx, sy);
676 
677 	if (wp->fd != -1 && ioctl(wp->fd, TIOCSWINSZ, &ws) == -1)
678 #ifdef __sun
679 		/*
680 		 * Some versions of Solaris apparently can return an error when
681 		 * resizing; don't know why this happens, can't reproduce on
682 		 * other platforms and ignoring it doesn't seem to cause any
683 		 * issues.
684 		 */
685 		if (errno != EINVAL)
686 #endif
687 		fatal("ioctl failed");
688 }
689 
690 /*
691  * Enter alternative screen mode. A copy of the visible screen is saved and the
692  * history is not updated
693  */
694 void
695 window_pane_alternate_on(struct window_pane *wp, struct grid_cell *gc)
696 {
697 	struct screen	*s = &wp->base;
698 	u_int		 sx, sy;
699 
700 	if (wp->saved_grid != NULL)
701 		return;
702 	if (!options_get_number(&wp->window->options, "alternate-screen"))
703 		return;
704 	sx = screen_size_x(s);
705 	sy = screen_size_y(s);
706 
707 	wp->saved_grid = grid_create(sx, sy, 0);
708 	grid_duplicate_lines(wp->saved_grid, 0, s->grid, screen_hsize(s), sy);
709 	wp->saved_cx = s->cx;
710 	wp->saved_cy = s->cy;
711 	memcpy(&wp->saved_cell, gc, sizeof wp->saved_cell);
712 
713 	grid_view_clear(s->grid, 0, 0, sx, sy);
714 
715 	wp->base.grid->flags &= ~GRID_HISTORY;
716 
717 	wp->flags |= PANE_REDRAW;
718 }
719 
720 /* Exit alternate screen mode and restore the copied grid. */
721 void
722 window_pane_alternate_off(struct window_pane *wp, struct grid_cell *gc)
723 {
724 	struct screen	*s = &wp->base;
725 	u_int		 sx, sy;
726 
727 	if (wp->saved_grid == NULL)
728 		return;
729 	if (!options_get_number(&wp->window->options, "alternate-screen"))
730 		return;
731 	sx = screen_size_x(s);
732 	sy = screen_size_y(s);
733 
734 	/*
735 	 * If the current size is bigger, temporarily resize to the old size
736 	 * before copying back.
737 	 */
738 	if (sy > wp->saved_grid->sy)
739 		screen_resize(s, sx, wp->saved_grid->sy);
740 
741 	/* Restore the grid, cursor position and cell. */
742 	grid_duplicate_lines(s->grid, screen_hsize(s), wp->saved_grid, 0, sy);
743 	s->cx = wp->saved_cx;
744 	if (s->cx > screen_size_x(s) - 1)
745 		s->cx = screen_size_x(s) - 1;
746 	s->cy = wp->saved_cy;
747 	if (s->cy > screen_size_y(s) - 1)
748 		s->cy = screen_size_y(s) - 1;
749 	memcpy(gc, &wp->saved_cell, sizeof *gc);
750 
751 	/*
752 	 * Turn history back on (so resize can use it) and then resize back to
753 	 * the current size.
754 	 */
755 	wp->base.grid->flags |= GRID_HISTORY;
756 	if (sy > wp->saved_grid->sy)
757 		screen_resize(s, sx, sy);
758 
759 	grid_destroy(wp->saved_grid);
760 	wp->saved_grid = NULL;
761 
762 	wp->flags |= PANE_REDRAW;
763 }
764 
765 int
766 window_pane_set_mode(struct window_pane *wp, const struct window_mode *mode)
767 {
768 	struct screen	*s;
769 
770 	if (wp->mode != NULL)
771 		return (1);
772 	wp->mode = mode;
773 
774 	if ((s = wp->mode->init(wp)) != NULL)
775 		wp->screen = s;
776 	wp->flags |= PANE_REDRAW;
777 	return (0);
778 }
779 
780 void
781 window_pane_reset_mode(struct window_pane *wp)
782 {
783 	if (wp->mode == NULL)
784 		return;
785 
786 	wp->mode->free(wp);
787 	wp->mode = NULL;
788 
789 	wp->screen = &wp->base;
790 	wp->flags |= PANE_REDRAW;
791 }
792 
793 void
794 window_pane_key(struct window_pane *wp, struct session *sess, int key)
795 {
796 	struct window_pane	*wp2;
797 
798 	if (!window_pane_visible(wp))
799 		return;
800 
801 	if (wp->mode != NULL) {
802 		if (wp->mode->key != NULL)
803 			wp->mode->key(wp, sess, key);
804 		return;
805 	}
806 
807 	if (wp->fd == -1)
808 		return;
809 	input_key(wp, key);
810 	if (options_get_number(&wp->window->options, "synchronize-panes")) {
811 		TAILQ_FOREACH(wp2, &wp->window->panes, entry) {
812 			if (wp2 == wp || wp2->mode != NULL)
813 				continue;
814 			if (wp2->fd != -1 && window_pane_visible(wp2))
815 				input_key(wp2, key);
816 		}
817 	}
818 }
819 
820 void
821 window_pane_mouse(
822     struct window_pane *wp, struct session *sess, struct mouse_event *m)
823 {
824 	if (!window_pane_visible(wp))
825 		return;
826 
827 	if (m->x < wp->xoff || m->x >= wp->xoff + wp->sx)
828 		return;
829 	if (m->y < wp->yoff || m->y >= wp->yoff + wp->sy)
830 		return;
831 	m->x -= wp->xoff;
832 	m->y -= wp->yoff;
833 
834 	if (wp->mode != NULL) {
835 		if (wp->mode->mouse != NULL)
836 			wp->mode->mouse(wp, sess, m);
837 	} else if (wp->fd != -1)
838 		input_mouse(wp, m);
839 }
840 
841 int
842 window_pane_visible(struct window_pane *wp)
843 {
844 	struct window	*w = wp->window;
845 
846 	if (wp->xoff >= w->sx || wp->yoff >= w->sy)
847 		return (0);
848 	if (wp->xoff + wp->sx > w->sx || wp->yoff + wp->sy > w->sy)
849 		return (0);
850 	return (1);
851 }
852 
853 char *
854 window_pane_search(struct window_pane *wp, const char *searchstr, u_int *lineno)
855 {
856 	struct screen	*s = &wp->base;
857 	char		*newsearchstr, *line, *msg;
858 	u_int	 	 i;
859 
860 	msg = NULL;
861 	xasprintf(&newsearchstr, "*%s*", searchstr);
862 
863 	for (i = 0; i < screen_size_y(s); i++) {
864 		line = grid_view_string_cells(s->grid, 0, i, screen_size_x(s));
865 		if (fnmatch(newsearchstr, line, 0) == 0) {
866 			msg = line;
867 			if (lineno != NULL)
868 				*lineno = i;
869 			break;
870 		}
871 		xfree(line);
872 	}
873 
874 	xfree(newsearchstr);
875 	return (msg);
876 }
877 
878 /* Find the pane directly above another. */
879 struct window_pane *
880 window_pane_find_up(struct window_pane *wp)
881 {
882 	struct window_pane     *wp2;
883 	u_int			left, top;
884 
885 	if (wp == NULL || !window_pane_visible(wp))
886 		return (NULL);
887 
888 	top = wp->yoff;
889 	if (top == 0)
890 		top = wp->window->sy + 1;
891 	left = wp->xoff;
892 
893 	TAILQ_FOREACH(wp2, &wp->window->panes, entry) {
894 		if (!window_pane_visible(wp2))
895 			continue;
896 		if (wp2->yoff + wp2->sy + 1 != top)
897 			continue;
898 		if (left >= wp2->xoff && left <= wp2->xoff + wp2->sx)
899 			return (wp2);
900 	}
901 	return (NULL);
902 }
903 
904 /* Find the pane directly below another. */
905 struct window_pane *
906 window_pane_find_down(struct window_pane *wp)
907 {
908 	struct window_pane     *wp2;
909 	u_int			left, bottom;
910 
911 	if (wp == NULL || !window_pane_visible(wp))
912 		return (NULL);
913 
914 	bottom = wp->yoff + wp->sy + 1;
915 	if (bottom >= wp->window->sy)
916 		bottom = 0;
917 	left = wp->xoff;
918 
919 	TAILQ_FOREACH(wp2, &wp->window->panes, entry) {
920 		if (!window_pane_visible(wp2))
921 			continue;
922 		if (wp2->yoff != bottom)
923 			continue;
924 		if (left >= wp2->xoff && left <= wp2->xoff + wp2->sx)
925 			return (wp2);
926 	}
927 	return (NULL);
928 }
929 
930 /*
931  * Find the pane directly to the left of another, adjacent to the left side and
932  * containing the top edge.
933  */
934 struct window_pane *
935 window_pane_find_left(struct window_pane *wp)
936 {
937 	struct window_pane     *wp2;
938 	u_int			left, top;
939 
940 	if (wp == NULL || !window_pane_visible(wp))
941 		return (NULL);
942 
943 	left = wp->xoff;
944 	if (left == 0)
945 		left = wp->window->sx + 1;
946 	top = wp->yoff;
947 
948 	TAILQ_FOREACH(wp2, &wp->window->panes, entry) {
949 		if (!window_pane_visible(wp2))
950 			continue;
951 		if (wp2->xoff + wp2->sx + 1 != left)
952 			continue;
953 		if (top >= wp2->yoff && top <= wp2->yoff + wp2->sy)
954 			return (wp2);
955 	}
956 	return (NULL);
957 }
958 
959 /*
960  * Find the pane directly to the right of another, that is adjacent to the
961  * right edge and including the top edge.
962  */
963 struct window_pane *
964 window_pane_find_right(struct window_pane *wp)
965 {
966 	struct window_pane     *wp2;
967 	u_int			right, top;
968 
969 	if (wp == NULL || !window_pane_visible(wp))
970 		return (NULL);
971 
972 	right = wp->xoff + wp->sx + 1;
973 	if (right >= wp->window->sx)
974 		right = 0;
975 	top = wp->yoff;
976 
977 	TAILQ_FOREACH(wp2, &wp->window->panes, entry) {
978 		if (!window_pane_visible(wp2))
979 			continue;
980 		if (wp2->xoff != right)
981 			continue;
982 		if (top >= wp2->yoff && top <= wp2->yoff + wp2->sy)
983 			return (wp2);
984 	}
985 	return (NULL);
986 }
987