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