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