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. */ |
56 | struct windows windows; |
57 | |
58 | /* Global panes tree. */ |
59 | struct window_pane_tree all_window_panes; |
60 | static u_int next_window_pane_id; |
61 | static u_int next_window_id; |
62 | static u_int next_active_point; |
63 | |
64 | /* List of window modes. */ |
65 | const 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 | |
75 | struct window_pane_input_data { |
76 | struct cmdq_item *item; |
77 | u_int wp; |
78 | }; |
79 | |
80 | static struct window_pane *window_pane_create(struct window *, u_int, u_int, |
81 | u_int); |
82 | static void window_pane_destroy(struct window_pane *); |
83 | |
84 | RB_GENERATE(windows, window, entry, window_cmp); |
85 | RB_GENERATE(winlinks, winlink, entry, winlink_cmp); |
86 | RB_GENERATE(window_pane_tree, window_pane, tree_entry, window_pane_cmp); |
87 | |
88 | int |
89 | window_cmp(struct window *w1, struct window *w2) |
90 | { |
91 | return (w1->id - w2->id); |
92 | } |
93 | |
94 | int |
95 | winlink_cmp(struct winlink *wl1, struct winlink *wl2) |
96 | { |
97 | return (wl1->idx - wl2->idx); |
98 | } |
99 | |
100 | int |
101 | window_pane_cmp(struct window_pane *wp1, struct window_pane *wp2) |
102 | { |
103 | return (wp1->id - wp2->id); |
104 | } |
105 | |
106 | struct winlink * |
107 | winlink_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 | |
119 | struct winlink * |
120 | winlink_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 | |
131 | struct winlink * |
132 | winlink_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 | |
143 | static int |
144 | winlink_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 | |
160 | u_int |
161 | winlink_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 | |
173 | struct winlink * |
174 | winlink_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 | |
191 | void |
192 | winlink_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 | |
203 | void |
204 | winlink_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 | |
217 | struct winlink * |
218 | winlink_next(struct winlink *wl) |
219 | { |
220 | return (RB_NEXT(winlinks, wwl, wl)); |
221 | } |
222 | |
223 | struct winlink * |
224 | winlink_previous(struct winlink *wl) |
225 | { |
226 | return (RB_PREV(winlinks, wwl, wl)); |
227 | } |
228 | |
229 | struct winlink * |
230 | winlink_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 | |
240 | struct winlink * |
241 | winlink_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 | |
251 | void |
252 | winlink_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 | |
261 | void |
262 | winlink_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 | |
277 | struct window * |
278 | window_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 | |
292 | struct window * |
293 | window_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 | |
301 | void |
302 | window_update_activity(struct window *w) |
303 | { |
304 | gettimeofday(&w->activity_time, NULL); |
305 | alerts_queue(w, WINDOW_ACTIVITY); |
306 | } |
307 | |
308 | struct window * |
309 | window_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 | |
346 | static void |
347 | window_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 | |
375 | int |
376 | window_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 | |
392 | void |
393 | window_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 | |
399 | void |
400 | window_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 | |
409 | void |
410 | window_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 | |
417 | void |
418 | window_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 | |
436 | void |
437 | window_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 | |
463 | int |
464 | window_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 | |
475 | int |
476 | window_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 | |
495 | void |
496 | window_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 | |
531 | struct window_pane * |
532 | window_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 | |
548 | struct window_pane * |
549 | window_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 | |
589 | int |
590 | window_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 | |
617 | int |
618 | window_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 | |
640 | int |
641 | window_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 | |
652 | int |
653 | window_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 | |
662 | struct window_pane * |
663 | window_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 | |
691 | void |
692 | window_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 | |
715 | void |
716 | window_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 | |
724 | struct window_pane * |
725 | window_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 | |
739 | struct window_pane * |
740 | window_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 | |
750 | struct window_pane * |
751 | window_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 | |
762 | int |
763 | window_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 | |
779 | u_int |
780 | window_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 | |
791 | void |
792 | window_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 | |
803 | const char * |
804 | window_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 | |
829 | struct window_pane * |
830 | window_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 | |
844 | struct window_pane * |
845 | window_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 | |
853 | static struct window_pane * |
854 | window_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 | |
906 | static void |
907 | window_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 | |
946 | static void |
947 | window_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 | |
967 | static void |
968 | window_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 | |
980 | void |
981 | window_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 | |
992 | void |
993 | window_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 | */ |
1016 | void |
1017 | window_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. */ |
1046 | void |
1047 | window_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 | |
1099 | void |
1100 | window_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 | |
1112 | void |
1113 | window_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 | |
1122 | void |
1123 | window_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 | |
1133 | int |
1134 | window_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 | |
1153 | static void |
1154 | window_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 | |
1171 | int |
1172 | window_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 | |
1212 | void |
1213 | window_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 | |
1242 | void |
1243 | window_pane_reset_mode_all(struct window_pane *wp) |
1244 | { |
1245 | while (!TAILQ_EMPTY(&wp->modes)) |
1246 | window_pane_reset_mode(wp); |
1247 | } |
1248 | |
1249 | int |
1250 | window_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 | |
1288 | int |
1289 | window_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 | |
1296 | u_int |
1297 | window_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. */ |
1345 | static struct window_pane * |
1346 | window_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 | */ |
1367 | struct window_pane * |
1368 | window_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. */ |
1424 | struct window_pane * |
1425 | window_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. */ |
1481 | struct window_pane * |
1482 | window_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. */ |
1529 | struct window_pane * |
1530 | window_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 */ |
1577 | void |
1578 | winlink_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. */ |
1592 | int |
1593 | winlink_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 | |
1619 | static void |
1620 | window_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 | |
1644 | int |
1645 | window_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 | |