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