1/* $OpenBSD$ */
2
3/*
4 * Copyright (c) 2007 Nicholas Marriott <[email protected]>
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 <ctype.h>
23#include <errno.h>
24#include <fcntl.h>
25#include <fnmatch.h>
26#include <regex.h>
27#include <signal.h>
28#include <stdint.h>
29#include <stdlib.h>
30#include <string.h>
31#include <time.h>
32#include <unistd.h>
33
34#include "tmux.h"
35
36/*
37 * Each window is attached to a number of panes, each of which is a pty. This
38 * file contains code to handle them.
39 *
40 * A pane has two buffers attached, these are filled and emptied by the main
41 * server poll loop. Output data is received from pty's in screen format,
42 * translated and returned as a series of escape sequences and strings via
43 * input_parse (in input.c). Input data is received as key codes and written
44 * directly via input_key.
45 *
46 * Each pane also has a "virtual" screen (screen.c) which contains the current
47 * state and is redisplayed when the window is reattached to a client.
48 *
49 * Windows are stored directly on a global array and wrapped in any number of
50 * winlink structs to be linked onto local session RB trees. A reference count
51 * is maintained and a window removed from the global list and destroyed when
52 * it reaches zero.
53 */
54
55/* Global window list. */
56struct windows windows;
57
58/* Global panes tree. */
59struct window_pane_tree all_window_panes;
60static u_int next_window_pane_id;
61static u_int next_window_id;
62static u_int next_active_point;
63
64/* List of window modes. */
65const struct window_mode *all_window_modes[] = {
66 &window_buffer_mode,
67 &window_client_mode,
68 &window_clock_mode,
69 &window_copy_mode,
70 &window_tree_mode,
71 &window_view_mode,
72 NULL
73};
74
75struct window_pane_input_data {
76 struct cmdq_item *item;
77 u_int wp;
78};
79
80static struct window_pane *window_pane_create(struct window *, u_int, u_int,
81 u_int);
82static void window_pane_destroy(struct window_pane *);
83
84RB_GENERATE(windows, window, entry, window_cmp);
85RB_GENERATE(winlinks, winlink, entry, winlink_cmp);
86RB_GENERATE(window_pane_tree, window_pane, tree_entry, window_pane_cmp);
87
88int
89window_cmp(struct window *w1, struct window *w2)
90{
91 return (w1->id - w2->id);
92}
93
94int
95winlink_cmp(struct winlink *wl1, struct winlink *wl2)
96{
97 return (wl1->idx - wl2->idx);
98}
99
100int
101window_pane_cmp(struct window_pane *wp1, struct window_pane *wp2)
102{
103 return (wp1->id - wp2->id);
104}
105
106struct winlink *
107winlink_find_by_window(struct winlinks *wwl, struct window *w)
108{
109 struct winlink *wl;
110
111 RB_FOREACH(wl, winlinks, wwl) {
112 if (wl->window == w)
113 return (wl);
114 }
115
116 return (NULL);
117}
118
119struct winlink *
120winlink_find_by_index(struct winlinks *wwl, int idx)
121{
122 struct winlink wl;
123
124 if (idx < 0)
125 fatalx("bad index");
126
127 wl.idx = idx;
128 return (RB_FIND(winlinks, wwl, &wl));
129}
130
131struct winlink *
132winlink_find_by_window_id(struct winlinks *wwl, u_int id)
133{
134 struct winlink *wl;
135
136 RB_FOREACH(wl, winlinks, wwl) {
137 if (wl->window->id == id)
138 return (wl);
139 }
140 return (NULL);
141}
142
143static int
144winlink_next_index(struct winlinks *wwl, int idx)
145{
146 int i;
147
148 i = idx;
149 do {
150 if (winlink_find_by_index(wwl, i) == NULL)
151 return (i);
152 if (i == INT_MAX)
153 i = 0;
154 else
155 i++;
156 } while (i != idx);
157 return (-1);
158}
159
160u_int
161winlink_count(struct winlinks *wwl)
162{
163 struct winlink *wl;
164 u_int n;
165
166 n = 0;
167 RB_FOREACH(wl, winlinks, wwl)
168 n++;
169
170 return (n);
171}
172
173struct winlink *
174winlink_add(struct winlinks *wwl, int idx)
175{
176 struct winlink *wl;
177
178 if (idx < 0) {
179 if ((idx = winlink_next_index(wwl, -idx - 1)) == -1)
180 return (NULL);
181 } else if (winlink_find_by_index(wwl, idx) != NULL)
182 return (NULL);
183
184 wl = xcalloc(1, sizeof *wl);
185 wl->idx = idx;
186 RB_INSERT(winlinks, wwl, wl);
187
188 return (wl);
189}
190
191void
192winlink_set_window(struct winlink *wl, struct window *w)
193{
194 if (wl->window != NULL) {
195 TAILQ_REMOVE(&wl->window->winlinks, wl, wentry);
196 window_remove_ref(wl->window, __func__);
197 }
198 TAILQ_INSERT_TAIL(&w->winlinks, wl, wentry);
199 wl->window = w;
200 window_add_ref(w, __func__);
201}
202
203void
204winlink_remove(struct winlinks *wwl, struct winlink *wl)
205{
206 struct window *w = wl->window;
207
208 if (w != NULL) {
209 TAILQ_REMOVE(&w->winlinks, wl, wentry);
210 window_remove_ref(w, __func__);
211 }
212
213 RB_REMOVE(winlinks, wwl, wl);
214 free(wl);
215}
216
217struct winlink *
218winlink_next(struct winlink *wl)
219{
220 return (RB_NEXT(winlinks, wwl, wl));
221}
222
223struct winlink *
224winlink_previous(struct winlink *wl)
225{
226 return (RB_PREV(winlinks, wwl, wl));
227}
228
229struct winlink *
230winlink_next_by_number(struct winlink *wl, struct session *s, int n)
231{
232 for (; n > 0; n--) {
233 if ((wl = RB_NEXT(winlinks, wwl, wl)) == NULL)
234 wl = RB_MIN(winlinks, &s->windows);
235 }
236
237 return (wl);
238}
239
240struct winlink *
241winlink_previous_by_number(struct winlink *wl, struct session *s, int n)
242{
243 for (; n > 0; n--) {
244 if ((wl = RB_PREV(winlinks, wwl, wl)) == NULL)
245 wl = RB_MAX(winlinks, &s->windows);
246 }
247
248 return (wl);
249}
250
251void
252winlink_stack_push(struct winlink_stack *stack, struct winlink *wl)
253{
254 if (wl == NULL)
255 return;
256
257 winlink_stack_remove(stack, wl);
258 TAILQ_INSERT_HEAD(stack, wl, sentry);
259}
260
261void
262winlink_stack_remove(struct winlink_stack *stack, struct winlink *wl)
263{
264 struct winlink *wl2;
265
266 if (wl == NULL)
267 return;
268
269 TAILQ_FOREACH(wl2, stack, sentry) {
270 if (wl2 == wl) {
271 TAILQ_REMOVE(stack, wl, sentry);
272 return;
273 }
274 }
275}
276
277struct window *
278window_find_by_id_str(const char *s)
279{
280 const char *errstr;
281 u_int id;
282
283 if (*s != '@')
284 return (NULL);
285
286 id = strtonum(s + 1, 0, UINT_MAX, &errstr);
287 if (errstr != NULL)
288 return (NULL);
289 return (window_find_by_id(id));
290}
291
292struct window *
293window_find_by_id(u_int id)
294{
295 struct window w;
296
297 w.id = id;
298 return (RB_FIND(windows, &windows, &w));
299}
300
301void
302window_update_activity(struct window *w)
303{
304 gettimeofday(&w->activity_time, NULL);
305 alerts_queue(w, WINDOW_ACTIVITY);
306}
307
308struct window *
309window_create(u_int sx, u_int sy, u_int xpixel, u_int ypixel)
310{
311 struct window *w;
312
313 if (xpixel == 0)
314 xpixel = DEFAULT_XPIXEL;
315 if (ypixel == 0)
316 ypixel = DEFAULT_YPIXEL;
317
318 w = xcalloc(1, sizeof *w);
319 w->name = xstrdup("");
320 w->flags = 0;
321
322 TAILQ_INIT(&w->panes);
323 w->active = NULL;
324
325 w->lastlayout = -1;
326 w->layout_root = NULL;
327
328 w->sx = sx;
329 w->sy = sy;
330 w->xpixel = xpixel;
331 w->ypixel = ypixel;
332
333 w->options = options_create(global_w_options);
334
335 w->references = 0;
336 TAILQ_INIT(&w->winlinks);
337
338 w->id = next_window_id++;
339 RB_INSERT(windows, &windows, w);
340
341 window_update_activity(w);
342
343 return (w);
344}
345
346static void
347window_destroy(struct window *w)
348{
349 log_debug("window @%u destroyed (%d references)", w->id, w->references);
350
351 RB_REMOVE(windows, &windows, w);
352
353 if (w->layout_root != NULL)
354 layout_free_cell(w->layout_root);
355 if (w->saved_layout_root != NULL)
356 layout_free_cell(w->saved_layout_root);
357 free(w->old_layout);
358
359 window_destroy_panes(w);
360
361 if (event_initialized(&w->name_event))
362 evtimer_del(&w->name_event);
363
364 if (event_initialized(&w->alerts_timer))
365 evtimer_del(&w->alerts_timer);
366 if (event_initialized(&w->offset_timer))
367 event_del(&w->offset_timer);
368
369 options_free(w->options);
370
371 free(w->name);
372 free(w);
373}
374
375int
376window_pane_destroy_ready(struct window_pane *wp)
377{
378 int n;
379
380 if (wp->pipe_fd != -1) {
381 if (EVBUFFER_LENGTH(wp->pipe_event->output) != 0)
382 return (0);
383 if (ioctl(wp->fd, FIONREAD, &n) != -1 && n > 0)
384 return (0);
385 }
386
387 if (~wp->flags & PANE_EXITED)
388 return (0);
389 return (1);
390}
391
392void
393window_add_ref(struct window *w, const char *from)
394{
395 w->references++;
396 log_debug("%s: @%u %s, now %d", __func__, w->id, from, w->references);
397}
398
399void
400window_remove_ref(struct window *w, const char *from)
401{
402 w->references--;
403 log_debug("%s: @%u %s, now %d", __func__, w->id, from, w->references);
404
405 if (w->references == 0)
406 window_destroy(w);
407}
408
409void
410window_set_name(struct window *w, const char *new_name)
411{
412 free(w->name);
413 utf8_stravis(&w->name, new_name, VIS_OCTAL|VIS_CSTYLE|VIS_TAB|VIS_NL);
414 notify_window("window-renamed", w);
415}
416
417void
418window_resize(struct window *w, u_int sx, u_int sy, int xpixel, int ypixel)
419{
420 if (xpixel == 0)
421 xpixel = DEFAULT_XPIXEL;
422 if (ypixel == 0)
423 ypixel = DEFAULT_YPIXEL;
424
425 log_debug("%s: @%u resize %ux%u (%ux%u)", __func__, w->id, sx, sy,
426 xpixel == -1 ? w->xpixel : xpixel,
427 ypixel == -1 ? w->ypixel : ypixel);
428 w->sx = sx;
429 w->sy = sy;
430 if (xpixel != -1)
431 w->xpixel = xpixel;
432 if (ypixel != -1)
433 w->ypixel = ypixel;
434}
435
436void
437window_pane_send_resize(struct window_pane *wp, int yadjust)
438{
439 struct window *w = wp->window;
440 struct winsize ws;
441
442 if (wp->fd == -1)
443 return;
444
445 memset(&ws, 0, sizeof ws);
446 ws.ws_col = wp->sx;
447 ws.ws_row = wp->sy + yadjust;
448 ws.ws_xpixel = w->xpixel * ws.ws_col;
449 ws.ws_ypixel = w->ypixel * ws.ws_row;
450 if (ioctl(wp->fd, TIOCSWINSZ, &ws) == -1)
451#ifdef __sun
452 /*
453 * Some versions of Solaris apparently can return an error when
454 * resizing; don't know why this happens, can't reproduce on
455 * other platforms and ignoring it doesn't seem to cause any
456 * issues.
457 */
458 if (errno != EINVAL && errno != ENXIO)
459#endif
460 fatal("ioctl failed");
461}
462
463int
464window_has_pane(struct window *w, struct window_pane *wp)
465{
466 struct window_pane *wp1;
467
468 TAILQ_FOREACH(wp1, &w->panes, entry) {
469 if (wp1 == wp)
470 return (1);
471 }
472 return (0);
473}
474
475int
476window_set_active_pane(struct window *w, struct window_pane *wp, int notify)
477{
478 log_debug("%s: pane %%%u", __func__, wp->id);
479
480 if (wp == w->active)
481 return (0);
482 w->last = w->active;
483
484 w->active = wp;
485 w->active->active_point = next_active_point++;
486 w->active->flags |= PANE_CHANGED;
487
488 tty_update_window_offset(w);
489
490 if (notify)
491 notify_window("window-pane-changed", w);
492 return (1);
493}
494
495void
496window_redraw_active_switch(struct window *w, struct window_pane *wp)
497{
498 struct style *sy1, *sy2;
499 int c1, c2;
500
501 if (wp == w->active)
502 return;
503
504 for (;;) {
505 /*
506 * If the active and inactive styles or palettes are different,
507 * need to redraw the panes.
508 */
509 sy1 = &wp->cached_style;
510 sy2 = &wp->cached_active_style;
511 if (!style_equal(sy1, sy2))
512 wp->flags |= PANE_REDRAW;
513 else {
514 c1 = window_pane_get_palette(wp, sy1->gc.fg);
515 c2 = window_pane_get_palette(wp, sy2->gc.fg);
516 if (c1 != c2)
517 wp->flags |= PANE_REDRAW;
518 else {
519 c1 = window_pane_get_palette(wp, sy1->gc.bg);
520 c2 = window_pane_get_palette(wp, sy2->gc.bg);
521 if (c1 != c2)
522 wp->flags |= PANE_REDRAW;
523 }
524 }
525 if (wp == w->active)
526 break;
527 wp = w->active;
528 }
529}
530
531struct window_pane *
532window_get_active_at(struct window *w, u_int x, u_int y)
533{
534 struct window_pane *wp;
535
536 TAILQ_FOREACH(wp, &w->panes, entry) {
537 if (!window_pane_visible(wp))
538 continue;
539 if (x < wp->xoff || x > wp->xoff + wp->sx)
540 continue;
541 if (y < wp->yoff || y > wp->yoff + wp->sy)
542 continue;
543 return (wp);
544 }
545 return (NULL);
546}
547
548struct window_pane *
549window_find_string(struct window *w, const char *s)
550{
551 u_int x, y, top = 0, bottom = w->sy - 1;
552 int status;
553
554 x = w->sx / 2;
555 y = w->sy / 2;
556
557 status = options_get_number(w->options, "pane-border-status");
558 if (status == PANE_STATUS_TOP)
559 top++;
560 else if (status == PANE_STATUS_BOTTOM)
561 bottom--;
562
563 if (strcasecmp(s, "top") == 0)
564 y = top;
565 else if (strcasecmp(s, "bottom") == 0)
566 y = bottom;
567 else if (strcasecmp(s, "left") == 0)
568 x = 0;
569 else if (strcasecmp(s, "right") == 0)
570 x = w->sx - 1;
571 else if (strcasecmp(s, "top-left") == 0) {
572 x = 0;
573 y = top;
574 } else if (strcasecmp(s, "top-right") == 0) {
575 x = w->sx - 1;
576 y = top;
577 } else if (strcasecmp(s, "bottom-left") == 0) {
578 x = 0;
579 y = bottom;
580 } else if (strcasecmp(s, "bottom-right") == 0) {
581 x = w->sx - 1;
582 y = bottom;
583 } else
584 return (NULL);
585
586 return (window_get_active_at(w, x, y));
587}
588
589int
590window_zoom(struct window_pane *wp)
591{
592 struct window *w = wp->window;
593 struct window_pane *wp1;
594
595 if (w->flags & WINDOW_ZOOMED)
596 return (-1);
597
598 if (window_count_panes(w) == 1)
599 return (-1);
600
601 if (w->active != wp)
602 window_set_active_pane(w, wp, 1);
603
604 TAILQ_FOREACH(wp1, &w->panes, entry) {
605 wp1->saved_layout_cell = wp1->layout_cell;
606 wp1->layout_cell = NULL;
607 }
608
609 w->saved_layout_root = w->layout_root;
610 layout_init(w, wp);
611 w->flags |= WINDOW_ZOOMED;
612 notify_window("window-layout-changed", w);
613
614 return (0);
615}
616
617int
618window_unzoom(struct window *w)
619{
620 struct window_pane *wp;
621
622 if (!(w->flags & WINDOW_ZOOMED))
623 return (-1);
624
625 w->flags &= ~WINDOW_ZOOMED;
626 layout_free(w);
627 w->layout_root = w->saved_layout_root;
628 w->saved_layout_root = NULL;
629
630 TAILQ_FOREACH(wp, &w->panes, entry) {
631 wp->layout_cell = wp->saved_layout_cell;
632 wp->saved_layout_cell = NULL;
633 }
634 layout_fix_panes(w);
635 notify_window("window-layout-changed", w);
636
637 return (0);
638}
639
640int
641window_push_zoom(struct window *w, int flag)
642{
643 log_debug("%s: @%u %d", __func__, w->id,
644 flag && (w->flags & WINDOW_ZOOMED));
645 if (flag && (w->flags & WINDOW_ZOOMED))
646 w->flags |= WINDOW_WASZOOMED;
647 else
648 w->flags &= ~WINDOW_WASZOOMED;
649 return (window_unzoom(w) == 0);
650}
651
652int
653window_pop_zoom(struct window *w)
654{
655 log_debug("%s: @%u %d", __func__, w->id,
656 !!(w->flags & WINDOW_WASZOOMED));
657 if (w->flags & WINDOW_WASZOOMED)
658 return (window_zoom(w->active) == 0);
659 return (0);
660}
661
662struct window_pane *
663window_add_pane(struct window *w, struct window_pane *other, u_int hlimit,
664 int flags)
665{
666 struct window_pane *wp;
667
668 if (other == NULL)
669 other = w->active;
670
671 wp = window_pane_create(w, w->sx, w->sy, hlimit);
672 if (TAILQ_EMPTY(&w->panes)) {
673 log_debug("%s: @%u at start", __func__, w->id);
674 TAILQ_INSERT_HEAD(&w->panes, wp, entry);
675 } else if (flags & SPAWN_BEFORE) {
676 log_debug("%s: @%u before %%%u", __func__, w->id, wp->id);
677 if (flags & SPAWN_FULLSIZE)
678 TAILQ_INSERT_HEAD(&w->panes, wp, entry);
679 else
680 TAILQ_INSERT_BEFORE(other, wp, entry);
681 } else {
682 log_debug("%s: @%u after %%%u", __func__, w->id, wp->id);
683 if (flags & SPAWN_FULLSIZE)
684 TAILQ_INSERT_TAIL(&w->panes, wp, entry);
685 else
686 TAILQ_INSERT_AFTER(&w->panes, other, wp, entry);
687 }
688 return (wp);
689}
690
691void
692window_lost_pane(struct window *w, struct window_pane *wp)
693{
694 log_debug("%s: @%u pane %%%u", __func__, w->id, wp->id);
695
696 if (wp == marked_pane.wp)
697 server_clear_marked();
698
699 if (wp == w->active) {
700 w->active = w->last;
701 w->last = NULL;
702 if (w->active == NULL) {
703 w->active = TAILQ_PREV(wp, window_panes, entry);
704 if (w->active == NULL)
705 w->active = TAILQ_NEXT(wp, entry);
706 }
707 if (w->active != NULL) {
708 w->active->flags |= PANE_CHANGED;
709 notify_window("window-pane-changed", w);
710 }
711 } else if (wp == w->last)
712 w->last = NULL;
713}
714
715void
716window_remove_pane(struct window *w, struct window_pane *wp)
717{
718 window_lost_pane(w, wp);
719
720 TAILQ_REMOVE(&w->panes, wp, entry);
721 window_pane_destroy(wp);
722}
723
724struct window_pane *
725window_pane_at_index(struct window *w, u_int idx)
726{
727 struct window_pane *wp;
728 u_int n;
729
730 n = options_get_number(w->options, "pane-base-index");
731 TAILQ_FOREACH(wp, &w->panes, entry) {
732 if (n == idx)
733 return (wp);
734 n++;
735 }
736 return (NULL);
737}
738
739struct window_pane *
740window_pane_next_by_number(struct window *w, struct window_pane *wp, u_int n)
741{
742 for (; n > 0; n--) {
743 if ((wp = TAILQ_NEXT(wp, entry)) == NULL)
744 wp = TAILQ_FIRST(&w->panes);
745 }
746
747 return (wp);
748}
749
750struct window_pane *
751window_pane_previous_by_number(struct window *w, struct window_pane *wp,
752 u_int n)
753{
754 for (; n > 0; n--) {
755 if ((wp = TAILQ_PREV(wp, window_panes, entry)) == NULL)
756 wp = TAILQ_LAST(&w->panes, window_panes);
757 }
758
759 return (wp);
760}
761
762int
763window_pane_index(struct window_pane *wp, u_int *i)
764{
765 struct window_pane *wq;
766 struct window *w = wp->window;
767
768 *i = options_get_number(w->options, "pane-base-index");
769 TAILQ_FOREACH(wq, &w->panes, entry) {
770 if (wp == wq) {
771 return (0);
772 }
773 (*i)++;
774 }
775
776 return (-1);
777}
778
779u_int
780window_count_panes(struct window *w)
781{
782 struct window_pane *wp;
783 u_int n;
784
785 n = 0;
786 TAILQ_FOREACH(wp, &w->panes, entry)
787 n++;
788 return (n);
789}
790
791void
792window_destroy_panes(struct window *w)
793{
794 struct window_pane *wp;
795
796 while (!TAILQ_EMPTY(&w->panes)) {
797 wp = TAILQ_FIRST(&w->panes);
798 TAILQ_REMOVE(&w->panes, wp, entry);
799 window_pane_destroy(wp);
800 }
801}
802
803const char *
804window_printable_flags(struct winlink *wl)
805{
806 struct session *s = wl->session;
807 static char flags[32];
808 int pos;
809
810 pos = 0;
811 if (wl->flags & WINLINK_ACTIVITY)
812 flags[pos++] = '#';
813 if (wl->flags & WINLINK_BELL)
814 flags[pos++] = '!';
815 if (wl->flags & WINLINK_SILENCE)
816 flags[pos++] = '~';
817 if (wl == s->curw)
818 flags[pos++] = '*';
819 if (wl == TAILQ_FIRST(&s->lastw))
820 flags[pos++] = '-';
821 if (server_check_marked() && wl == marked_pane.wl)
822 flags[pos++] = 'M';
823 if (wl->window->flags & WINDOW_ZOOMED)
824 flags[pos++] = 'Z';
825 flags[pos] = '\0';
826 return (flags);
827}
828
829struct window_pane *
830window_pane_find_by_id_str(const char *s)
831{
832 const char *errstr;
833 u_int id;
834
835 if (*s != '%')
836 return (NULL);
837
838 id = strtonum(s + 1, 0, UINT_MAX, &errstr);
839 if (errstr != NULL)
840 return (NULL);
841 return (window_pane_find_by_id(id));
842}
843
844struct window_pane *
845window_pane_find_by_id(u_int id)
846{
847 struct window_pane wp;
848
849 wp.id = id;
850 return (RB_FIND(window_pane_tree, &all_window_panes, &wp));
851}
852
853static struct window_pane *
854window_pane_create(struct window *w, u_int sx, u_int sy, u_int hlimit)
855{
856 struct window_pane *wp;
857 char host[HOST_NAME_MAX + 1];
858
859 wp = xcalloc(1, sizeof *wp);
860 wp->window = w;
861 wp->options = options_create(w->options);
862 wp->flags = PANE_STYLECHANGED;
863
864 wp->id = next_window_pane_id++;
865 RB_INSERT(window_pane_tree, &all_window_panes, wp);
866
867 wp->argc = 0;
868 wp->argv = NULL;
869 wp->shell = NULL;
870 wp->cwd = NULL;
871
872 wp->fd = -1;
873 wp->event = NULL;
874
875 TAILQ_INIT(&wp->modes);
876
877 wp->layout_cell = NULL;
878
879 wp->xoff = 0;
880 wp->yoff = 0;
881
882 wp->sx = wp->osx = sx;
883 wp->sy = wp->osx = sy;
884
885 wp->pipe_fd = -1;
886 wp->pipe_off = 0;
887 wp->pipe_event = NULL;
888
889 wp->saved_grid = NULL;
890 wp->saved_cx = UINT_MAX;
891 wp->saved_cy = UINT_MAX;
892
893 screen_init(&wp->base, sx, sy, hlimit);
894 wp->screen = &wp->base;
895
896 screen_init(&wp->status_screen, 1, 1, 0);
897
898 if (gethostname(host, sizeof host) == 0)
899 screen_set_title(&wp->base, host);
900
901 input_init(wp);
902
903 return (wp);
904}
905
906static void
907window_pane_destroy(struct window_pane *wp)
908{
909 window_pane_reset_mode_all(wp);
910 free(wp->searchstr);
911
912 if (wp->fd != -1) {
913#ifdef HAVE_UTEMPTER
914 utempter_remove_record(wp->fd);
915#endif
916 bufferevent_free(wp->event);
917 close(wp->fd);
918 }
919
920 input_free(wp);
921
922 screen_free(&wp->status_screen);
923
924 screen_free(&wp->base);
925 if (wp->saved_grid != NULL)
926 grid_destroy(wp->saved_grid);
927
928 if (wp->pipe_fd != -1) {
929 bufferevent_free(wp->pipe_event);
930 close(wp->pipe_fd);
931 }
932
933 if (event_initialized(&wp->resize_timer))
934 event_del(&wp->resize_timer);
935
936 RB_REMOVE(window_pane_tree, &all_window_panes, wp);
937
938 options_free(wp->options);
939 free((void *)wp->cwd);
940 free(wp->shell);
941 cmd_free_argv(wp->argc, wp->argv);
942 free(wp->palette);
943 free(wp);
944}
945
946static void
947window_pane_read_callback(__unused struct bufferevent *bufev, void *data)
948{
949 struct window_pane *wp = data;
950 struct evbuffer *evb = wp->event->input;
951 size_t size = EVBUFFER_LENGTH(evb);
952 char *new_data;
953 size_t new_size;
954
955 new_size = size - wp->pipe_off;
956 if (wp->pipe_fd != -1 && new_size > 0) {
957 new_data = EVBUFFER_DATA(evb) + wp->pipe_off;
958 bufferevent_write(wp->pipe_event, new_data, new_size);
959 }
960
961 log_debug("%%%u has %zu bytes", wp->id, size);
962 input_parse(wp);
963
964 wp->pipe_off = EVBUFFER_LENGTH(evb);
965}
966
967static void
968window_pane_error_callback(__unused struct bufferevent *bufev,
969 __unused short what, void *data)
970{
971 struct window_pane *wp = data;
972
973 log_debug("%%%u error", wp->id);
974 wp->flags |= PANE_EXITED;
975
976 if (window_pane_destroy_ready(wp))
977 server_destroy_pane(wp, 1);
978}
979
980void
981window_pane_set_event(struct window_pane *wp)
982{
983 setblocking(wp->fd, 0);
984
985 wp->event = bufferevent_new(wp->fd, window_pane_read_callback,
986 NULL, window_pane_error_callback, wp);
987
988 bufferevent_setwatermark(wp->event, EV_READ, 0, READ_SIZE);
989 bufferevent_enable(wp->event, EV_READ|EV_WRITE);
990}
991
992void
993window_pane_resize(struct window_pane *wp, u_int sx, u_int sy)
994{
995 struct window_mode_entry *wme;
996
997 if (sx == wp->sx && sy == wp->sy)
998 return;
999 wp->sx = sx;
1000 wp->sy = sy;
1001
1002 log_debug("%s: %%%u resize %ux%u", __func__, wp->id, sx, sy);
1003 screen_resize(&wp->base, sx, sy, wp->saved_grid == NULL);
1004
1005 wme = TAILQ_FIRST(&wp->modes);
1006 if (wme != NULL && wme->mode->resize != NULL)
1007 wme->mode->resize(wme, sx, sy);
1008
1009 wp->flags |= (PANE_RESIZE|PANE_RESIZED);
1010}
1011
1012/*
1013 * Enter alternative screen mode. A copy of the visible screen is saved and the
1014 * history is not updated
1015 */
1016void
1017window_pane_alternate_on(struct window_pane *wp, struct grid_cell *gc,
1018 int cursor)
1019{
1020 struct screen *s = &wp->base;
1021 u_int sx, sy;
1022
1023 if (wp->saved_grid != NULL)
1024 return;
1025 if (!options_get_number(wp->options, "alternate-screen"))
1026 return;
1027 sx = screen_size_x(s);
1028 sy = screen_size_y(s);
1029
1030 wp->saved_grid = grid_create(sx, sy, 0);
1031 grid_duplicate_lines(wp->saved_grid, 0, s->grid, screen_hsize(s), sy);
1032 if (cursor) {
1033 wp->saved_cx = s->cx;
1034 wp->saved_cy = s->cy;
1035 }
1036 memcpy(&wp->saved_cell, gc, sizeof wp->saved_cell);
1037
1038 grid_view_clear(s->grid, 0, 0, sx, sy, 8);
1039
1040 wp->base.grid->flags &= ~GRID_HISTORY;
1041
1042 wp->flags |= PANE_REDRAW;
1043}
1044
1045/* Exit alternate screen mode and restore the copied grid. */
1046void
1047window_pane_alternate_off(struct window_pane *wp, struct grid_cell *gc,
1048 int cursor)
1049{
1050 struct screen *s = &wp->base;
1051 u_int sx, sy;
1052
1053 if (!options_get_number(wp->options, "alternate-screen"))
1054 return;
1055
1056 /*
1057 * Restore the cursor position and cell. This happens even if not
1058 * currently in the alternate screen.
1059 */
1060 if (cursor && wp->saved_cx != UINT_MAX && wp->saved_cy != UINT_MAX) {
1061 s->cx = wp->saved_cx;
1062 if (s->cx > screen_size_x(s) - 1)
1063 s->cx = screen_size_x(s) - 1;
1064 s->cy = wp->saved_cy;
1065 if (s->cy > screen_size_y(s) - 1)
1066 s->cy = screen_size_y(s) - 1;
1067 memcpy(gc, &wp->saved_cell, sizeof *gc);
1068 }
1069
1070 if (wp->saved_grid == NULL)
1071 return;
1072 sx = screen_size_x(s);
1073 sy = screen_size_y(s);
1074
1075 /*
1076 * If the current size is bigger, temporarily resize to the old size
1077 * before copying back.
1078 */
1079 if (sy > wp->saved_grid->sy)
1080 screen_resize(s, sx, wp->saved_grid->sy, 1);
1081
1082 /* Restore the saved grid. */
1083 grid_duplicate_lines(s->grid, screen_hsize(s), wp->saved_grid, 0, sy);
1084
1085 /*
1086 * Turn history back on (so resize can use it) and then resize back to
1087 * the current size.
1088 */
1089 wp->base.grid->flags |= GRID_HISTORY;
1090 if (sy > wp->saved_grid->sy || sx != wp->saved_grid->sx)
1091 screen_resize(s, sx, sy, 1);
1092
1093 grid_destroy(wp->saved_grid);
1094 wp->saved_grid = NULL;
1095
1096 wp->flags |= PANE_REDRAW;
1097}
1098
1099void
1100window_pane_set_palette(struct window_pane *wp, u_int n, int colour)
1101{
1102 if (n > 0xff)
1103 return;
1104
1105 if (wp->palette == NULL)
1106 wp->palette = xcalloc(0x100, sizeof *wp->palette);
1107
1108 wp->palette[n] = colour;
1109 wp->flags |= PANE_REDRAW;
1110}
1111
1112void
1113window_pane_unset_palette(struct window_pane *wp, u_int n)
1114{
1115 if (n > 0xff || wp->palette == NULL)
1116 return;
1117
1118 wp->palette[n] = 0;
1119 wp->flags |= PANE_REDRAW;
1120}
1121
1122void
1123window_pane_reset_palette(struct window_pane *wp)
1124{
1125 if (wp->palette == NULL)
1126 return;
1127
1128 free(wp->palette);
1129 wp->palette = NULL;
1130 wp->flags |= PANE_REDRAW;
1131}
1132
1133int
1134window_pane_get_palette(struct window_pane *wp, int c)
1135{
1136 int new;
1137
1138 if (wp == NULL || wp->palette == NULL)
1139 return (-1);
1140
1141 new = -1;
1142 if (c < 8)
1143 new = wp->palette[c];
1144 else if (c >= 90 && c <= 97)
1145 new = wp->palette[8 + c - 90];
1146 else if (c & COLOUR_FLAG_256)
1147 new = wp->palette[c & ~COLOUR_FLAG_256];
1148 if (new == 0)
1149 return (-1);
1150 return (new);
1151}
1152
1153static void
1154window_pane_mode_timer(__unused int fd, __unused short events, void *arg)
1155{
1156 struct window_pane *wp = arg;
1157 struct timeval tv = { .tv_sec = 10 };
1158 int n = 0;
1159
1160 evtimer_del(&wp->modetimer);
1161 evtimer_add(&wp->modetimer, &tv);
1162
1163 log_debug("%%%u in mode: last=%ld", wp->id, (long)wp->modelast);
1164
1165 if (wp->modelast < time(NULL) - WINDOW_MODE_TIMEOUT) {
1166 if (ioctl(wp->fd, FIONREAD, &n) == -1 || n > 0)
1167 window_pane_reset_mode_all(wp);
1168 }
1169}
1170
1171int
1172window_pane_set_mode(struct window_pane *wp, const struct window_mode *mode,
1173 struct cmd_find_state *fs, struct args *args)
1174{
1175 struct timeval tv = { .tv_sec = 10 };
1176 struct window_mode_entry *wme;
1177
1178 if (!TAILQ_EMPTY(&wp->modes) && TAILQ_FIRST(&wp->modes)->mode == mode)
1179 return (1);
1180
1181 wp->modelast = time(NULL);
1182 if (TAILQ_EMPTY(&wp->modes)) {
1183 evtimer_set(&wp->modetimer, window_pane_mode_timer, wp);
1184 evtimer_add(&wp->modetimer, &tv);
1185 }
1186
1187 TAILQ_FOREACH(wme, &wp->modes, entry) {
1188 if (wme->mode == mode)
1189 break;
1190 }
1191 if (wme != NULL) {
1192 TAILQ_REMOVE(&wp->modes, wme, entry);
1193 TAILQ_INSERT_HEAD(&wp->modes, wme, entry);
1194 } else {
1195 wme = xcalloc(1, sizeof *wme);
1196 wme->wp = wp;
1197 wme->mode = mode;
1198 wme->prefix = 1;
1199 TAILQ_INSERT_HEAD(&wp->modes, wme, entry);
1200 wme->screen = wme->mode->init(wme, fs, args);
1201 }
1202
1203 wp->screen = wme->screen;
1204 wp->flags |= (PANE_REDRAW|PANE_CHANGED);
1205
1206 server_status_window(wp->window);
1207 notify_pane("pane-mode-changed", wp);
1208
1209 return (0);
1210}
1211
1212void
1213window_pane_reset_mode(struct window_pane *wp)
1214{
1215 struct window_mode_entry *wme, *next;
1216
1217 if (TAILQ_EMPTY(&wp->modes))
1218 return;
1219
1220 wme = TAILQ_FIRST(&wp->modes);
1221 TAILQ_REMOVE(&wp->modes, wme, entry);
1222 wme->mode->free(wme);
1223 free(wme);
1224
1225 next = TAILQ_FIRST(&wp->modes);
1226 if (next == NULL) {
1227 log_debug("%s: no next mode", __func__);
1228 evtimer_del(&wp->modetimer);
1229 wp->screen = &wp->base;
1230 } else {
1231 log_debug("%s: next mode is %s", __func__, next->mode->name);
1232 wp->screen = next->screen;
1233 if (next->mode->resize != NULL)
1234 next->mode->resize(next, wp->sx, wp->sy);
1235 }
1236 wp->flags |= (PANE_REDRAW|PANE_CHANGED);
1237
1238 server_status_window(wp->window);
1239 notify_pane("pane-mode-changed", wp);
1240}
1241
1242void
1243window_pane_reset_mode_all(struct window_pane *wp)
1244{
1245 while (!TAILQ_EMPTY(&wp->modes))
1246 window_pane_reset_mode(wp);
1247}
1248
1249int
1250window_pane_key(struct window_pane *wp, struct client *c, struct session *s,
1251 struct winlink *wl, key_code key, struct mouse_event *m)
1252{
1253 struct window_mode_entry *wme;
1254 struct window_pane *wp2;
1255
1256 if (KEYC_IS_MOUSE(key) && m == NULL)
1257 return (-1);
1258
1259 wme = TAILQ_FIRST(&wp->modes);
1260 if (wme != NULL) {
1261 wp->modelast = time(NULL);
1262 if (wme->mode->key != NULL)
1263 wme->mode->key(wme, c, s, wl, (key & ~KEYC_XTERM), m);
1264 return (0);
1265 }
1266
1267 if (wp->fd == -1 || wp->flags & PANE_INPUTOFF)
1268 return (0);
1269
1270 if (input_key(wp, key, m) != 0)
1271 return (-1);
1272
1273 if (KEYC_IS_MOUSE(key))
1274 return (0);
1275 if (options_get_number(wp->window->options, "synchronize-panes")) {
1276 TAILQ_FOREACH(wp2, &wp->window->panes, entry) {
1277 if (wp2 != wp &&
1278 TAILQ_EMPTY(&wp2->modes) &&
1279 wp2->fd != -1 &&
1280 (~wp2->flags & PANE_INPUTOFF) &&
1281 window_pane_visible(wp2))
1282 input_key(wp2, key, NULL);
1283 }
1284 }
1285 return (0);
1286}
1287
1288int
1289window_pane_visible(struct window_pane *wp)
1290{
1291 if (~wp->window->flags & WINDOW_ZOOMED)
1292 return (1);
1293 return (wp == wp->window->active);
1294}
1295
1296u_int
1297window_pane_search(struct window_pane *wp, const char *term, int regex,
1298 int ignore)
1299{
1300 struct screen *s = &wp->base;
1301 regex_t r;
1302 char *new = NULL, *line;
1303 u_int i;
1304 int flags = 0, found;
1305 size_t n;
1306
1307 if (!regex) {
1308 if (ignore)
1309 flags |= FNM_CASEFOLD;
1310 xasprintf(&new, "*%s*", term);
1311 } else {
1312 if (ignore)
1313 flags |= REG_ICASE;
1314 if (regcomp(&r, term, flags|REG_EXTENDED) != 0)
1315 return (0);
1316 }
1317
1318 for (i = 0; i < screen_size_y(s); i++) {
1319 line = grid_view_string_cells(s->grid, 0, i, screen_size_x(s));
1320 for (n = strlen(line); n > 0; n--) {
1321 if (!isspace((u_char)line[n - 1]))
1322 break;
1323 line[n - 1] = '\0';
1324 }
1325 log_debug("%s: %s", __func__, line);
1326 if (!regex)
1327 found = (fnmatch(new, line, 0) == 0);
1328 else
1329 found = (regexec(&r, line, 0, NULL, 0) == 0);
1330 free(line);
1331 if (found)
1332 break;
1333 }
1334 if (!regex)
1335 free(new);
1336 else
1337 regfree(&r);
1338
1339 if (i == screen_size_y(s))
1340 return (0);
1341 return (i + 1);
1342}
1343
1344/* Get MRU pane from a list. */
1345static struct window_pane *
1346window_pane_choose_best(struct window_pane **list, u_int size)
1347{
1348 struct window_pane *next, *best;
1349 u_int i;
1350
1351 if (size == 0)
1352 return (NULL);
1353
1354 best = list[0];
1355 for (i = 1; i < size; i++) {
1356 next = list[i];
1357 if (next->active_point > best->active_point)
1358 best = next;
1359 }
1360 return (best);
1361}
1362
1363/*
1364 * Find the pane directly above another. We build a list of those adjacent to
1365 * top edge and then choose the best.
1366 */
1367struct window_pane *
1368window_pane_find_up(struct window_pane *wp)
1369{
1370 struct window *w;
1371 struct window_pane *next, *best, **list;
1372 u_int edge, left, right, end, size;
1373 int status, found;
1374
1375 if (wp == NULL)
1376 return (NULL);
1377 w = wp->window;
1378 status = options_get_number(w->options, "pane-border-status");
1379
1380 list = NULL;
1381 size = 0;
1382
1383 edge = wp->yoff;
1384 if (status == PANE_STATUS_TOP) {
1385 if (edge == 1)
1386 edge = w->sy + 1;
1387 } else if (status == PANE_STATUS_BOTTOM) {
1388 if (edge == 0)
1389 edge = w->sy;
1390 } else {
1391 if (edge == 0)
1392 edge = w->sy + 1;
1393 }
1394
1395 left = wp->xoff;
1396 right = wp->xoff + wp->sx;
1397
1398 TAILQ_FOREACH(next, &w->panes, entry) {
1399 if (next == wp)
1400 continue;
1401 if (next->yoff + next->sy + 1 != edge)
1402 continue;
1403 end = next->xoff + next->sx - 1;
1404
1405 found = 0;
1406 if (next->xoff < left && end > right)
1407 found = 1;
1408 else if (next->xoff >= left && next->xoff <= right)
1409 found = 1;
1410 else if (end >= left && end <= right)
1411 found = 1;
1412 if (!found)
1413 continue;
1414 list = xreallocarray(list, size + 1, sizeof *list);
1415 list[size++] = next;
1416 }
1417
1418 best = window_pane_choose_best(list, size);
1419 free(list);
1420 return (best);
1421}
1422
1423/* Find the pane directly below another. */
1424struct window_pane *
1425window_pane_find_down(struct window_pane *wp)
1426{
1427 struct window *w;
1428 struct window_pane *next, *best, **list;
1429 u_int edge, left, right, end, size;
1430 int status, found;
1431
1432 if (wp == NULL)
1433 return (NULL);
1434 w = wp->window;
1435 status = options_get_number(w->options, "pane-border-status");
1436
1437 list = NULL;
1438 size = 0;
1439
1440 edge = wp->yoff + wp->sy + 1;
1441 if (status == PANE_STATUS_TOP) {
1442 if (edge >= w->sy)
1443 edge = 1;
1444 } else if (status == PANE_STATUS_BOTTOM) {
1445 if (edge >= w->sy - 1)
1446 edge = 0;
1447 } else {
1448 if (edge >= w->sy)
1449 edge = 0;
1450 }
1451
1452 left = wp->xoff;
1453 right = wp->xoff + wp->sx;
1454
1455 TAILQ_FOREACH(next, &w->panes, entry) {
1456 if (next == wp)
1457 continue;
1458 if (next->yoff != edge)
1459 continue;
1460 end = next->xoff + next->sx - 1;
1461
1462 found = 0;
1463 if (next->xoff < left && end > right)
1464 found = 1;
1465 else if (next->xoff >= left && next->xoff <= right)
1466 found = 1;
1467 else if (end >= left && end <= right)
1468 found = 1;
1469 if (!found)
1470 continue;
1471 list = xreallocarray(list, size + 1, sizeof *list);
1472 list[size++] = next;
1473 }
1474
1475 best = window_pane_choose_best(list, size);
1476 free(list);
1477 return (best);
1478}
1479
1480/* Find the pane directly to the left of another. */
1481struct window_pane *
1482window_pane_find_left(struct window_pane *wp)
1483{
1484 struct window *w;
1485 struct window_pane *next, *best, **list;
1486 u_int edge, top, bottom, end, size;
1487 int found;
1488
1489 if (wp == NULL)
1490 return (NULL);
1491 w = wp->window;
1492
1493 list = NULL;
1494 size = 0;
1495
1496 edge = wp->xoff;
1497 if (edge == 0)
1498 edge = w->sx + 1;
1499
1500 top = wp->yoff;
1501 bottom = wp->yoff + wp->sy;
1502
1503 TAILQ_FOREACH(next, &w->panes, entry) {
1504 if (next == wp)
1505 continue;
1506 if (next->xoff + next->sx + 1 != edge)
1507 continue;
1508 end = next->yoff + next->sy - 1;
1509
1510 found = 0;
1511 if (next->yoff < top && end > bottom)
1512 found = 1;
1513 else if (next->yoff >= top && next->yoff <= bottom)
1514 found = 1;
1515 else if (end >= top && end <= bottom)
1516 found = 1;
1517 if (!found)
1518 continue;
1519 list = xreallocarray(list, size + 1, sizeof *list);
1520 list[size++] = next;
1521 }
1522
1523 best = window_pane_choose_best(list, size);
1524 free(list);
1525 return (best);
1526}
1527
1528/* Find the pane directly to the right of another. */
1529struct window_pane *
1530window_pane_find_right(struct window_pane *wp)
1531{
1532 struct window *w;
1533 struct window_pane *next, *best, **list;
1534 u_int edge, top, bottom, end, size;
1535 int found;
1536
1537 if (wp == NULL)
1538 return (NULL);
1539 w = wp->window;
1540
1541 list = NULL;
1542 size = 0;
1543
1544 edge = wp->xoff + wp->sx + 1;
1545 if (edge >= w->sx)
1546 edge = 0;
1547
1548 top = wp->yoff;
1549 bottom = wp->yoff + wp->sy;
1550
1551 TAILQ_FOREACH(next, &w->panes, entry) {
1552 if (next == wp)
1553 continue;
1554 if (next->xoff != edge)
1555 continue;
1556 end = next->yoff + next->sy - 1;
1557
1558 found = 0;
1559 if (next->yoff < top && end > bottom)
1560 found = 1;
1561 else if (next->yoff >= top && next->yoff <= bottom)
1562 found = 1;
1563 else if (end >= top && end <= bottom)
1564 found = 1;
1565 if (!found)
1566 continue;
1567 list = xreallocarray(list, size + 1, sizeof *list);
1568 list[size++] = next;
1569 }
1570
1571 best = window_pane_choose_best(list, size);
1572 free(list);
1573 return (best);
1574}
1575
1576/* Clear alert flags for a winlink */
1577void
1578winlink_clear_flags(struct winlink *wl)
1579{
1580 struct winlink *loop;
1581
1582 wl->window->flags &= ~WINDOW_ALERTFLAGS;
1583 TAILQ_FOREACH(loop, &wl->window->winlinks, wentry) {
1584 if ((loop->flags & WINLINK_ALERTFLAGS) != 0) {
1585 loop->flags &= ~WINLINK_ALERTFLAGS;
1586 server_status_session(loop->session);
1587 }
1588 }
1589}
1590
1591/* Shuffle window indexes up. */
1592int
1593winlink_shuffle_up(struct session *s, struct winlink *wl)
1594{
1595 int idx, last;
1596
1597 if (wl == NULL)
1598 return (-1);
1599 idx = wl->idx + 1;
1600
1601 /* Find the next free index. */
1602 for (last = idx; last < INT_MAX; last++) {
1603 if (winlink_find_by_index(&s->windows, last) == NULL)
1604 break;
1605 }
1606 if (last == INT_MAX)
1607 return (-1);
1608
1609 /* Move everything from last - 1 to idx up a bit. */
1610 for (; last > idx; last--) {
1611 wl = winlink_find_by_index(&s->windows, last - 1);
1612 server_link_window(s, wl, s, last, 0, 0, NULL);
1613 server_unlink_window(s, wl);
1614 }
1615
1616 return (idx);
1617}
1618
1619static void
1620window_pane_input_callback(struct client *c, __unused const char *path,
1621 int error, int closed, struct evbuffer *buffer, void *data)
1622{
1623 struct window_pane_input_data *cdata = data;
1624 struct window_pane *wp;
1625 u_char *buf = EVBUFFER_DATA(buffer);
1626 size_t len = EVBUFFER_LENGTH(buffer);
1627
1628 wp = window_pane_find_by_id(cdata->wp);
1629 if (wp == NULL || closed || error != 0 || c->flags & CLIENT_DEAD) {
1630 if (wp == NULL)
1631 c->flags |= CLIENT_EXIT;
1632
1633 evbuffer_drain(buffer, len);
1634 cmdq_continue(cdata->item);
1635
1636 server_client_unref(c);
1637 free(cdata);
1638 return;
1639 }
1640 input_parse_buffer(wp, buf, len);
1641 evbuffer_drain(buffer, len);
1642}
1643
1644int
1645window_pane_start_input(struct window_pane *wp, struct cmdq_item *item,
1646 char **cause)
1647{
1648 struct client *c = item->client;
1649 struct window_pane_input_data *cdata;
1650
1651 if (~wp->flags & PANE_EMPTY) {
1652 *cause = xstrdup("pane is not empty");
1653 return (-1);
1654 }
1655
1656 cdata = xmalloc(sizeof *cdata);
1657 cdata->item = item;
1658 cdata->wp = wp->id;
1659
1660 c->references++;
1661 file_read(c, "-", window_pane_input_callback, cdata);
1662
1663 return (0);
1664}
1665