1 /* $Id: window.c,v 1.126 2010-02-08 18:10:07 tcunha Exp $ */
4 * Copyright (c) 2007 Nicholas Marriott <nicm@users.sourceforge.net>
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.
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.
19 #include <sys/types.h>
20 #include <sys/ioctl.h>
36 * Each window is attached to a number of panes, each of which is a pty. This
37 * file contains code to handle them.
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.
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.
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
54 /* Global window list. */
55 struct windows windows
;
57 void window_pane_read_callback(struct bufferevent
*, void *);
58 void window_pane_error_callback(struct bufferevent
*, short, void *);
60 RB_GENERATE(winlinks
, winlink
, entry
, winlink_cmp
);
63 winlink_cmp(struct winlink
*wl1
, struct winlink
*wl2
)
65 return (wl1
->idx
- wl2
->idx
);
69 winlink_find_by_window(struct winlinks
*wwl
, struct window
*w
)
73 RB_FOREACH(wl
, winlinks
, wwl
) {
82 winlink_find_by_index(struct winlinks
*wwl
, int idx
)
90 return (RB_FIND(winlinks
, wwl
, &wl
));
94 winlink_next_index(struct winlinks
*wwl
, int idx
)
100 if (winlink_find_by_index(wwl
, i
) == NULL
)
111 winlink_count(struct winlinks
*wwl
)
117 RB_FOREACH(wl
, winlinks
, wwl
)
124 winlink_add(struct winlinks
*wwl
, struct window
*w
, int idx
)
129 if ((idx
= winlink_next_index(wwl
, -idx
- 1)) == -1)
131 } else if (winlink_find_by_index(wwl
, idx
) != NULL
)
134 wl
= xcalloc(1, sizeof *wl
);
137 RB_INSERT(winlinks
, wwl
, wl
);
145 winlink_remove(struct winlinks
*wwl
, struct winlink
*wl
)
147 struct window
*w
= wl
->window
;
149 RB_REMOVE(winlinks
, wwl
, wl
);
150 if (wl
->status_text
!= NULL
)
151 xfree(wl
->status_text
);
154 if (w
->references
== 0)
155 fatal("bad reference count");
157 if (w
->references
== 0)
162 winlink_next(struct winlink
*wl
)
164 return (RB_NEXT(winlinks
, wwl
, wl
));
168 winlink_previous(struct winlink
*wl
)
170 return (RB_PREV(winlinks
, wwl
, wl
));
174 winlink_stack_push(struct winlink_stack
*stack
, struct winlink
*wl
)
179 winlink_stack_remove(stack
, wl
);
180 TAILQ_INSERT_HEAD(stack
, wl
, sentry
);
184 winlink_stack_remove(struct winlink_stack
*stack
, struct winlink
*wl
)
191 TAILQ_FOREACH(wl2
, stack
, sentry
) {
193 TAILQ_REMOVE(stack
, wl
, sentry
);
200 window_index(struct window
*s
, u_int
*i
)
202 for (*i
= 0; *i
< ARRAY_LENGTH(&windows
); (*i
)++) {
203 if (s
== ARRAY_ITEM(&windows
, *i
))
210 window_create1(u_int sx
, u_int sy
)
215 w
= xcalloc(1, sizeof *w
);
219 TAILQ_INIT(&w
->panes
);
223 w
->layout_root
= NULL
;
228 queue_window_name(w
);
230 options_init(&w
->options
, &global_w_options
);
232 for (i
= 0; i
< ARRAY_LENGTH(&windows
); i
++) {
233 if (ARRAY_ITEM(&windows
, i
) == NULL
) {
234 ARRAY_SET(&windows
, i
, w
);
238 if (i
== ARRAY_LENGTH(&windows
))
239 ARRAY_ADD(&windows
, w
);
246 window_create(const char *name
, const char *cmd
, const char *shell
,
247 const char *cwd
, struct environ
*env
, struct termios
*tio
,
248 u_int sx
, u_int sy
, u_int hlimit
,char **cause
)
251 struct window_pane
*wp
;
253 w
= window_create1(sx
, sy
);
254 wp
= window_add_pane(w
, hlimit
);
256 if (window_pane_spawn(wp
, cmd
, shell
, cwd
, env
, tio
, cause
) != 0) {
260 w
->active
= TAILQ_FIRST(&w
->panes
);
262 w
->name
= xstrdup(name
);
263 options_set_number(&w
->options
, "automatic-rename", 0);
265 w
->name
= default_window_name(w
);
270 window_destroy(struct window
*w
)
274 if (window_index(w
, &i
) != 0)
275 fatalx("index not found");
276 ARRAY_SET(&windows
, i
, NULL
);
277 while (!ARRAY_EMPTY(&windows
) && ARRAY_LAST(&windows
) == NULL
)
278 ARRAY_TRUNC(&windows
, 1);
280 if (w
->layout_root
!= NULL
)
283 evtimer_del(&w
->name_timer
);
285 options_free(&w
->options
);
287 window_destroy_panes(w
);
295 window_resize(struct window
*w
, u_int sx
, u_int sy
)
302 window_set_active_pane(struct window
*w
, struct window_pane
*wp
)
305 while (!window_pane_visible(w
->active
)) {
306 w
->active
= TAILQ_PREV(w
->active
, window_panes
, entry
);
307 if (w
->active
== NULL
)
308 w
->active
= TAILQ_LAST(&w
->panes
, window_panes
);
315 window_set_active_at(struct window
*w
, u_int x
, u_int y
)
317 struct window_pane
*wp
;
319 TAILQ_FOREACH(wp
, &w
->panes
, entry
) {
320 if (!window_pane_visible(wp
))
322 if (x
< wp
->xoff
|| x
>= wp
->xoff
+ wp
->sx
)
324 if (y
< wp
->yoff
|| y
>= wp
->yoff
+ wp
->sy
)
326 window_set_active_pane(w
, wp
);
332 window_add_pane(struct window
*w
, u_int hlimit
)
334 struct window_pane
*wp
;
336 wp
= window_pane_create(w
, w
->sx
, w
->sy
, hlimit
);
337 if (TAILQ_EMPTY(&w
->panes
))
338 TAILQ_INSERT_HEAD(&w
->panes
, wp
, entry
);
340 TAILQ_INSERT_AFTER(&w
->panes
, w
->active
, wp
, entry
);
345 window_remove_pane(struct window
*w
, struct window_pane
*wp
)
347 w
->active
= TAILQ_PREV(wp
, window_panes
, entry
);
348 if (w
->active
== NULL
)
349 w
->active
= TAILQ_NEXT(wp
, entry
);
351 TAILQ_REMOVE(&w
->panes
, wp
, entry
);
352 window_pane_destroy(wp
);
356 window_pane_at_index(struct window
*w
, u_int idx
)
358 struct window_pane
*wp
;
362 TAILQ_FOREACH(wp
, &w
->panes
, entry
) {
371 window_pane_index(struct window
*w
, struct window_pane
*wp
)
373 struct window_pane
*wq
;
377 TAILQ_FOREACH(wq
, &w
->panes
, entry
) {
386 window_count_panes(struct window
*w
)
388 struct window_pane
*wp
;
392 TAILQ_FOREACH(wp
, &w
->panes
, entry
)
398 window_destroy_panes(struct window
*w
)
400 struct window_pane
*wp
;
402 while (!TAILQ_EMPTY(&w
->panes
)) {
403 wp
= TAILQ_FIRST(&w
->panes
);
404 TAILQ_REMOVE(&w
->panes
, wp
, entry
);
405 window_pane_destroy(wp
);
410 window_pane_create(struct window
*w
, u_int sx
, u_int sy
, u_int hlimit
)
412 struct window_pane
*wp
;
414 wp
= xcalloc(1, sizeof *wp
);
426 wp
->layout_cell
= NULL
;
436 wp
->pipe_event
= NULL
;
438 wp
->saved_grid
= NULL
;
440 screen_init(&wp
->base
, sx
, sy
, hlimit
);
441 wp
->screen
= &wp
->base
;
449 window_pane_destroy(struct window_pane
*wp
)
453 bufferevent_free(wp
->event
);
458 window_pane_reset_mode(wp
);
459 screen_free(&wp
->base
);
460 if (wp
->saved_grid
!= NULL
)
461 grid_destroy(wp
->saved_grid
);
463 if (wp
->pipe_fd
!= -1) {
465 bufferevent_free(wp
->pipe_event
);
470 if (wp
->shell
!= NULL
)
478 window_pane_spawn(struct window_pane
*wp
, const char *cmd
, const char *shell
,
479 const char *cwd
, struct environ
*env
, struct termios
*tio
, char **cause
)
483 char *argv0
, **varp
, *var
;
484 ARRAY_DECL(, char *) varlist
;
485 struct environ_entry
*envent
;
492 bufferevent_free(wp
->event
);
497 wp
->cmd
= xstrdup(cmd
);
500 if (wp
->shell
!= NULL
)
502 wp
->shell
= xstrdup(shell
);
507 wp
->cwd
= xstrdup(cwd
);
510 memset(&ws
, 0, sizeof ws
);
511 ws
.ws_col
= screen_size_x(&wp
->base
);
512 ws
.ws_row
= screen_size_y(&wp
->base
);
514 switch (wp
->pid
= forkpty(&wp
->fd
, wp
->tty
, NULL
, &ws
)) {
517 xasprintf(cause
, "%s: %s", cmd
, strerror(errno
));
520 if (chdir(wp
->cwd
) != 0)
523 if (tcgetattr(STDIN_FILENO
, &tio2
) != 0)
524 fatal("tcgetattr failed");
526 memcpy(tio2
.c_cc
, tio
->c_cc
, sizeof tio2
.c_cc
);
527 tio2
.c_cc
[VERASE
] = '\177';
528 if (tcsetattr(STDIN_FILENO
, TCSANOW
, &tio2
) != 0)
529 fatal("tcgetattr failed");
531 ARRAY_INIT(&varlist
);
532 for (varp
= environ
; *varp
!= NULL
; varp
++) {
533 var
= xstrdup(*varp
);
534 var
[strcspn(var
, "=")] = '\0';
535 ARRAY_ADD(&varlist
, var
);
537 for (i
= 0; i
< ARRAY_LENGTH(&varlist
); i
++) {
538 var
= ARRAY_ITEM(&varlist
, i
);
541 RB_FOREACH(envent
, environ
, env
) {
542 if (envent
->value
!= NULL
)
543 setenv(envent
->name
, envent
->value
, 1);
546 server_signal_clear();
549 if (*wp
->cmd
!= '\0') {
550 /* Set SHELL but only if it is currently not useful. */
551 shell
= getenv("SHELL");
552 if (shell
== NULL
|| *shell
== '\0' || areshell(shell
))
553 setenv("SHELL", wp
->shell
, 1);
555 execl(_PATH_BSHELL
, "sh", "-c", wp
->cmd
, (char *) NULL
);
556 fatal("execl failed");
559 /* No command; fork a login shell. */
560 ptr
= strrchr(wp
->shell
, '/');
561 if (ptr
!= NULL
&& *(ptr
+ 1) != '\0')
562 xasprintf(&argv0
, "-%s", ptr
+ 1);
564 xasprintf(&argv0
, "-%s", wp
->shell
);
565 setenv("SHELL", wp
->shell
, 1);
566 execl(wp
->shell
, argv0
, (char *) NULL
);
567 fatal("execl failed");
570 if ((mode
= fcntl(wp
->fd
, F_GETFL
)) == -1)
571 fatal("fcntl failed");
572 if (fcntl(wp
->fd
, F_SETFL
, mode
|O_NONBLOCK
) == -1)
573 fatal("fcntl failed");
574 if (fcntl(wp
->fd
, F_SETFD
, FD_CLOEXEC
) == -1)
575 fatal("fcntl failed");
576 wp
->event
= bufferevent_new(wp
->fd
,
577 window_pane_read_callback
, NULL
, window_pane_error_callback
, wp
);
578 bufferevent_enable(wp
->event
, EV_READ
|EV_WRITE
);
585 window_pane_read_callback(unused
struct bufferevent
*bufev
, void *data
)
587 struct window_pane
*wp
= data
;
589 window_pane_parse(wp
);
594 window_pane_error_callback(
595 unused
struct bufferevent
*bufev
, unused
short what
, void *data
)
597 struct window_pane
*wp
= data
;
599 server_destroy_pane(wp
);
603 window_pane_resize(struct window_pane
*wp
, u_int sx
, u_int sy
)
607 if (sx
== wp
->sx
&& sy
== wp
->sy
)
612 memset(&ws
, 0, sizeof ws
);
616 screen_resize(&wp
->base
, sx
, sy
);
617 if (wp
->mode
!= NULL
)
618 wp
->mode
->resize(wp
, sx
, sy
);
620 if (wp
->fd
!= -1 && ioctl(wp
->fd
, TIOCSWINSZ
, &ws
) == -1)
623 * Some versions of Solaris apparently can return an error when
624 * resizing; don't know why this happens, can't reproduce on
625 * other platforms and ignoring it doesn't seem to cause any
630 fatal("ioctl failed");
634 window_pane_set_mode(struct window_pane
*wp
, const struct window_mode
*mode
)
638 if (wp
->mode
!= NULL
)
642 if ((s
= wp
->mode
->init(wp
)) != NULL
)
644 wp
->flags
|= PANE_REDRAW
;
649 window_pane_reset_mode(struct window_pane
*wp
)
651 if (wp
->mode
== NULL
)
657 wp
->screen
= &wp
->base
;
658 wp
->flags
|= PANE_REDRAW
;
662 window_pane_parse(struct window_pane
*wp
)
667 new_size
= EVBUFFER_LENGTH(wp
->event
->input
) - wp
->pipe_off
;
668 if (wp
->pipe_fd
!= -1 && new_size
> 0) {
669 data
= EVBUFFER_DATA(wp
->event
->input
);
670 bufferevent_write(wp
->pipe_event
, data
, new_size
);
675 wp
->pipe_off
= EVBUFFER_LENGTH(wp
->event
->input
);
679 window_pane_key(struct window_pane
*wp
, struct client
*c
, int key
)
681 struct window_pane
*wp2
;
683 if (!window_pane_visible(wp
))
686 if (wp
->mode
!= NULL
) {
687 if (wp
->mode
->key
!= NULL
)
688 wp
->mode
->key(wp
, c
, key
);
695 if (options_get_number(&wp
->window
->options
, "synchronize-panes")) {
696 TAILQ_FOREACH(wp2
, &wp
->window
->panes
, entry
) {
697 if (wp2
== wp
|| wp2
->mode
!= NULL
)
699 if (wp2
->fd
!= -1 && window_pane_visible(wp2
))
707 struct window_pane
*wp
, struct client
*c
, struct mouse_event
*m
)
709 if (!window_pane_visible(wp
))
712 if (m
->x
< wp
->xoff
|| m
->x
>= wp
->xoff
+ wp
->sx
)
714 if (m
->y
< wp
->yoff
|| m
->y
>= wp
->yoff
+ wp
->sy
)
719 if (wp
->mode
!= NULL
) {
720 if (wp
->mode
->mouse
!= NULL
)
721 wp
->mode
->mouse(wp
, c
, m
);
722 } else if (wp
->fd
!= -1)
727 window_pane_visible(struct window_pane
*wp
)
729 struct window
*w
= wp
->window
;
731 if (wp
->xoff
>= w
->sx
|| wp
->yoff
>= w
->sy
)
733 if (wp
->xoff
+ wp
->sx
> w
->sx
|| wp
->yoff
+ wp
->sy
> w
->sy
)
739 window_pane_search(struct window_pane
*wp
, const char *searchstr
, u_int
*lineno
)
741 struct screen
*s
= &wp
->base
;
742 char *newsearchstr
, *line
, *msg
;
746 xasprintf(&newsearchstr
, "*%s*", searchstr
);
748 for (i
= 0; i
< screen_size_y(s
); i
++) {
749 line
= grid_view_string_cells(s
->grid
, 0, i
, screen_size_x(s
));
750 if (fnmatch(newsearchstr
, line
, 0) == 0) {