diff --git a/CHANGES b/CHANGES index 550e2d6d..e0928bb0 100644 --- a/CHANGES +++ b/CHANGES @@ -1,5 +1,16 @@ 14 January 2009 +* Multiple vertical window splitting. Minimum pane size is four lines, an + (unhelpful) error will be shown if attempting to split a window with less + that eight lines. If the window is resized, as many panes are shown as can + fit without reducing them below four lines. There is (currently!) not a way + to show a hidden a pane without making the window larger. + + Note the -p and -l options to split-window are now gone, these may reappear + once I think them through again. + + To come: up-pane, down-pane; switch-pane will support -p and become + select-pane. * Server locking on inactivity (lock-after-time) is now disabled by default. 13 January 2009 @@ -906,7 +917,7 @@ (including mutt, emacs). No status bar yet and no key remapping or other customisation. -$Id: CHANGES,v 1.202 2009-01-14 18:41:55 nicm Exp $ +$Id: CHANGES,v 1.203 2009-01-14 19:29:32 nicm Exp $ LocalWords: showw utf UTF fulvio ciriaco joshe OSC APC gettime abc DEF OA clr LocalWords: rivo nurges lscm Erdely eol smysession mysession ek dstname RB diff --git a/TODO b/TODO index c8d1f460..30bc96be 100644 --- a/TODO +++ b/TODO @@ -98,3 +98,9 @@ - key handling sucks a bit and needs to be reworked - prefix-time should only apply to a few commands otherwise it is too annoying +----- +SPLITTING +emacs-style: + - when resizing, always expand and reduce from top to bottom + - minimum size 4 + separator + - if not enough room hide windows from top to bottom diff --git a/cmd-kill-pane.c b/cmd-kill-pane.c index ccfcfe39..81040cc1 100644 --- a/cmd-kill-pane.c +++ b/cmd-kill-pane.c @@ -1,4 +1,4 @@ -/* $Id: cmd-kill-pane.c,v 1.1 2009-01-13 06:50:10 nicm Exp $ */ +/* $Id: cmd-kill-pane.c,v 1.2 2009-01-14 19:29:32 nicm Exp $ */ /* * Copyright (c) 2009 Nicholas Marriott @@ -53,17 +53,18 @@ cmd_kill_pane_exec(struct cmd *self, struct cmd_ctx *ctx) if (data->pane == -1) wp = wl->window->active; else { - if (data->pane > 1 || wl->window->panes[data->pane] == NULL) { + wp = window_pane_at_index(wl->window, data->pane); + if (wp == NULL) { ctx->error(ctx, "no pane: %d", data->pane); return; } - wp = wl->window->panes[data->pane]; } - if (window_remove_pane(wl->window, wp) != 0) { + if (window_count_panes(wl->window) == 1) { ctx->error(ctx, "can't kill pane: %d", data->pane); return; } + window_remove_pane(wl->window, wp); server_redraw_window(wl->window); if (ctx->cmdclient != NULL) diff --git a/cmd-list-windows.c b/cmd-list-windows.c index 66360f65..ef740a22 100644 --- a/cmd-list-windows.c +++ b/cmd-list-windows.c @@ -1,4 +1,4 @@ -/* $Id: cmd-list-windows.c,v 1.27 2009-01-12 18:22:47 nicm Exp $ */ +/* $Id: cmd-list-windows.c,v 1.28 2009-01-14 19:29:32 nicm Exp $ */ /* * Copyright (c) 2007 Nicholas Marriott @@ -62,10 +62,7 @@ cmd_list_windows_exec(struct cmd *self, struct cmd_ctx *ctx) ctx->print(ctx, "%d: %s [%ux%u]", wl->idx, w->name, w->sx, w->sy); - for (i = 0; i < 2; i++) { - wp = w->panes[i]; - if (wp == NULL) - continue; + TAILQ_FOREACH(wp, &w->panes, entry) { gd = wp->base.grid; size = 0; @@ -79,9 +76,9 @@ cmd_list_windows_exec(struct cmd *self, struct cmd_ctx *ctx) else name = ""; - ctx->print(ctx, " pane %d:" - " %s [%ux%u] [history %u/%u, %llu bytes]", i, name, - wp->sx, wp->sy, gd->hsize, gd->hlimit, size); + ctx->print(ctx, + " %s [%ux%u] [history %u/%u, %llu bytes]", i, + name, wp->sx, wp->sy, gd->hsize, gd->hlimit, size); } } diff --git a/cmd-resize-pane-down.c b/cmd-resize-pane-down.c index 1737f99e..4f61db45 100644 --- a/cmd-resize-pane-down.c +++ b/cmd-resize-pane-down.c @@ -1,4 +1,4 @@ -/* $Id: cmd-resize-pane-down.c,v 1.1 2009-01-12 19:23:14 nicm Exp $ */ +/* $Id: cmd-resize-pane-down.c,v 1.2 2009-01-14 19:29:32 nicm Exp $ */ /* * Copyright (c) 2009 Nicholas Marriott @@ -23,69 +23,81 @@ #include "tmux.h" /* - * Increase pane size. + * Decrease pane size. */ void cmd_resize_pane_down_exec(struct cmd *, struct cmd_ctx *); const struct cmd_entry cmd_resize_pane_down_entry = { - "resize-pane-down", "resizep-dn", - CMD_TARGET_WINDOW_USAGE " [adjustment]", + "resize-pane-down", "resizep-down", + CMD_PANE_WINDOW_USAGE " [adjustment]", CMD_ZEROONEARG, - cmd_target_init, - cmd_target_parse, + cmd_pane_init, + cmd_pane_parse, cmd_resize_pane_down_exec, - cmd_target_send, - cmd_target_recv, - cmd_target_free, - cmd_target_print + cmd_pane_send, + cmd_pane_recv, + cmd_pane_free, + cmd_pane_print }; void cmd_resize_pane_down_exec(struct cmd *self, struct cmd_ctx *ctx) { - struct cmd_target_data *data = self->data; + struct cmd_pane_data *data = self->data; struct winlink *wl; - int adjust; const char *errstr; - u_int y0, y1; + struct window_pane *wp, *wq; + u_int adjust; if ((wl = cmd_find_window(ctx, data->target, NULL)) == NULL) return; -#ifdef notyet if (data->pane == -1) wp = wl->window->active; else { - if (data->pane > 1 || wl->window->panes[data->pane] == NULL) { + wp = window_pane_at_index(wl->window, data->pane); + if (wp == NULL) { ctx->error(ctx, "no pane: %d", data->pane); return; } - wp = wl->window->panes[data->pane]; } -#endif if (data->arg == NULL) adjust = 1; else { - adjust = strtonum(data->arg, 0, INT_MAX, &errstr); + adjust = strtonum(data->arg, 1, INT_MAX, &errstr); if (errstr != NULL) { ctx->error(ctx, "adjustment %s: %s", errstr, data->arg); return; } } - if (wl->window->panes[1] == NULL) - goto out; + /* + * If this is not the last window, keep trying to increase size and + * remove it from the next windows. If it is the last, do so on the + * previous window. + */ + if (TAILQ_NEXT(wp, entry) == NULL) { + if (wp == TAILQ_FIRST(&wl->window->panes)) { + /* Only one pane. */ + goto out; + } + wp = TAILQ_PREV(wp, window_panes, entry); + } + while (adjust-- > 0) { + wq = wp; + while ((wq = TAILQ_NEXT(wq, entry)) != NULL) { + if (wq->sy > PANE_MINIMUM) { + window_pane_resize(wq, wq->sx, wq->sy - 1); + break; + } + } + if (wq == NULL) + break; + window_pane_resize(wp, wp->sx, wp->sy + 1); - y0 = wl->window->panes[0]->sy; - y1 = wl->window->panes[1]->sy; - if (adjust >= y1) - adjust = y1 - 1; - y0 += adjust; - y1 -= adjust; - window_pane_resize(wl->window->panes[0], wl->window->sx, y0); - window_pane_resize(wl->window->panes[1], wl->window->sx, y1); - wl->window->panes[1]->yoff = y0 + 1; + } + window_update_panes(wl->window); server_redraw_window(wl->window); diff --git a/cmd-resize-pane-up.c b/cmd-resize-pane-up.c index d9e58243..82f8f8ec 100644 --- a/cmd-resize-pane-up.c +++ b/cmd-resize-pane-up.c @@ -1,4 +1,4 @@ -/* $Id: cmd-resize-pane-up.c,v 1.1 2009-01-12 19:23:14 nicm Exp $ */ +/* $Id: cmd-resize-pane-up.c,v 1.2 2009-01-14 19:29:32 nicm Exp $ */ /* * Copyright (c) 2009 Nicholas Marriott @@ -30,62 +30,69 @@ void cmd_resize_pane_up_exec(struct cmd *, struct cmd_ctx *); const struct cmd_entry cmd_resize_pane_up_entry = { "resize-pane-up", "resizep-up", - CMD_TARGET_WINDOW_USAGE " [adjustment]", + CMD_PANE_WINDOW_USAGE " [adjustment]", CMD_ZEROONEARG, - cmd_target_init, - cmd_target_parse, + cmd_pane_init, + cmd_pane_parse, cmd_resize_pane_up_exec, - cmd_target_send, - cmd_target_recv, - cmd_target_free, - cmd_target_print + cmd_pane_send, + cmd_pane_recv, + cmd_pane_free, + cmd_pane_print }; void cmd_resize_pane_up_exec(struct cmd *self, struct cmd_ctx *ctx) { - struct cmd_target_data *data = self->data; + struct cmd_pane_data *data = self->data; struct winlink *wl; - int adjust; const char *errstr; - u_int y0, y1; + struct window_pane *wp, *wq; + u_int adjust; if ((wl = cmd_find_window(ctx, data->target, NULL)) == NULL) return; -#ifdef notyet if (data->pane == -1) wp = wl->window->active; else { - if (data->pane > 1 || wl->window->panes[data->pane] == NULL) { + wp = window_pane_at_index(wl->window, data->pane); + if (wp == NULL) { ctx->error(ctx, "no pane: %d", data->pane); return; } - wp = wl->window->panes[data->pane]; } -#endif if (data->arg == NULL) adjust = 1; else { - adjust = strtonum(data->arg, 0, INT_MAX, &errstr); + adjust = strtonum(data->arg, 1, INT_MAX, &errstr); if (errstr != NULL) { ctx->error(ctx, "adjustment %s: %s", errstr, data->arg); return; } } - if (wl->window->panes[1] == NULL) - goto out; - - y0 = wl->window->panes[0]->sy; - y1 = wl->window->panes[1]->sy; - if (adjust >= y0) - adjust = y0 - 1; - y0 -= adjust; - y1 += adjust; - window_pane_resize(wl->window->panes[0], wl->window->sx, y0); - window_pane_resize(wl->window->panes[1], wl->window->sx, y1); - wl->window->panes[1]->yoff = y0 + 1; + /* + * If this is not the last window, keep trying to reduce size and add + * to the following window. If it is the last, do so on the previous + * window. + */ + wq = TAILQ_NEXT(wp, entry); + if (wq == NULL) { + if (wp == TAILQ_FIRST(&wl->window->panes)) { + /* Only one pane. */ + goto out; + } + wq = wp; + wp = TAILQ_PREV(wq, window_panes, entry); + } + while (adjust-- > 0) { + if (wp->sy <= PANE_MINIMUM) + break; + window_pane_resize(wq, wq->sx, wq->sy + 1); + window_pane_resize(wp, wp->sx, wp->sy - 1); + } + window_update_panes(wl->window); server_redraw_window(wl->window); diff --git a/cmd-respawn-window.c b/cmd-respawn-window.c index a50eadd0..7b52d493 100644 --- a/cmd-respawn-window.c +++ b/cmd-respawn-window.c @@ -1,4 +1,4 @@ -/* $Id: cmd-respawn-window.c,v 1.9 2009-01-13 06:50:10 nicm Exp $ */ +/* $Id: cmd-respawn-window.c,v 1.10 2009-01-14 19:29:32 nicm Exp $ */ /* * Copyright (c) 2008 Nicholas Marriott @@ -47,6 +47,7 @@ cmd_respawn_window_exec(struct cmd *self, struct cmd_ctx *ctx) struct cmd_target_data *data = self->data; struct winlink *wl; struct window *w; + struct window_pane *wp; struct session *s; const char *env[] = CHILD_ENVIRON; char buf[256]; @@ -56,10 +57,14 @@ cmd_respawn_window_exec(struct cmd *self, struct cmd_ctx *ctx) return; w = wl->window; - if ((w->panes[0]->fd != -1 || (w->panes[1] != NULL && - w->panes[1]->fd != -1)) && !(data->flags & CMD_KFLAG)) { - ctx->error(ctx, "window still active: %s:%d", s->name, wl->idx); - return; + if (!(data->flags & CMD_KFLAG)) { + TAILQ_FOREACH(wp, &w->panes, entry) { + if (wp->fd == -1) + continue; + ctx->error(ctx, + "window still active: %s:%d", s->name, wl->idx); + return; + } } if (session_index(s, &i) != 0) @@ -67,14 +72,16 @@ cmd_respawn_window_exec(struct cmd *self, struct cmd_ctx *ctx) xsnprintf(buf, sizeof buf, "TMUX=%ld,%u", (long) getpid(), i); env[0] = buf; - if (w->panes[1] != NULL) - window_remove_pane(w, w->panes[1]); - - if (window_pane_spawn(w->panes[0], data->arg, NULL, env) != 0) { + wp = TAILQ_FIRST(&w->panes); + TAILQ_REMOVE(&w->panes, wp, entry); + window_destroy_panes(w); + TAILQ_INSERT_HEAD(&w->panes, wp, entry); + window_pane_resize(wp, w->sx, w->sy); + if (window_pane_spawn(wp, data->arg, NULL, env) != 0) { ctx->error(ctx, "respawn failed: %s:%d", s->name, wl->idx); return; } - screen_reinit(&w->panes[0]->base); + screen_reinit(&wp->base); recalculate_sizes(); server_redraw_window(w); diff --git a/cmd-split-window.c b/cmd-split-window.c index 37b220e2..9019932e 100644 --- a/cmd-split-window.c +++ b/cmd-split-window.c @@ -1,4 +1,4 @@ -/* $Id: cmd-split-window.c,v 1.3 2009-01-12 18:22:47 nicm Exp $ */ +/* $Id: cmd-split-window.c,v 1.4 2009-01-14 19:29:32 nicm Exp $ */ /* * Copyright (c) 2009 Nicholas Marriott @@ -39,14 +39,11 @@ struct cmd_split_window_data { char *target; char *cmd; int flag_detached; - - int lines; - int percentage; }; const struct cmd_entry cmd_split_window_entry = { "split-window", "splitw", - "[-d] [-t target-window] [-l lines|-p percentage] [command]", + "[-d] [-t target-window] [command]", 0, cmd_split_window_init, cmd_split_window_parse, @@ -66,16 +63,13 @@ cmd_split_window_init(struct cmd *self, unused int arg) data->target = NULL; data->cmd = NULL; data->flag_detached = 0; - data->lines = -1; - data->percentage = -1; } int cmd_split_window_parse(struct cmd *self, int argc, char **argv, char **cause) { struct cmd_split_window_data *data; - int opt, n; - const char *errstr; + int opt; self->entry->init(self, 0); data = self->data; @@ -85,30 +79,6 @@ cmd_split_window_parse(struct cmd *self, int argc, char **argv, char **cause) case 'd': data->flag_detached = 1; break; - case 'l': - if (data->percentage != -1) - goto usage; - if (data->lines == -1) { - n = strtonum(optarg, 0, INT_MAX, &errstr); - if (errstr != NULL) { - xasprintf(cause, "number %s", errstr); - goto error; - } - data->lines = n; - } - break; - case 'p': - if (data->lines != -1) - goto usage; - if (data->percentage == -1) { - n = strtonum(optarg, 0, INT_MAX, &errstr); - if (errstr != NULL) { - xasprintf(cause, "number %s", errstr); - goto error; - } - data->percentage = n; - } - break; case 't': if (data->target == NULL) data->target = xstrdup(optarg); @@ -130,7 +100,6 @@ cmd_split_window_parse(struct cmd *self, int argc, char **argv, char **cause) usage: xasprintf(cause, "usage: %s %s", self->entry->name, self->entry->usage); -error: self->entry->free(self); return (-1); } @@ -142,20 +111,16 @@ cmd_split_window_exec(struct cmd *self, struct cmd_ctx *ctx) struct session *s; struct winlink *wl; struct window *w; + struct window_pane *wp; const char *env[] = CHILD_ENVIRON; char buf[256]; char *cmd, *cwd; - u_int i, hlimit, size; + u_int i, hlimit; if ((wl = cmd_find_window(ctx, data->target, &s)) == NULL) return; w = wl->window; - if (w->panes[1] != NULL) { - ctx->error(ctx, "window is already split"); - return; - } - if (session_index(s, &i) != 0) fatalx("session not found"); xsnprintf(buf, sizeof buf, "TMUX=%ld,%u", (long) getpid(), i); @@ -169,26 +134,15 @@ cmd_split_window_exec(struct cmd *self, struct cmd_ctx *ctx) else cwd = ctx->cmdclient->cwd; - size = w->sy / 2; - if (data->lines != -1) - size = data->lines; - if (data->percentage != -1) { - if (data->percentage > 100) - data->percentage = 100; - size = (w->sy * data->percentage) / 100; - } - if (size > w->sy) - size = w->sy; /* let window_add_pane sort it out */ - hlimit = options_get_number(&s->options, "history-limit"); - if (window_add_pane(w, size, cmd, cwd, env, hlimit) < 0) { + if ((wp = window_add_pane(w, cmd, cwd, env, hlimit)) == NULL) { ctx->error(ctx, "command failed: %s", cmd); return; } server_redraw_window(w); if (!data->flag_detached) { - w->active = w->panes[1]; + window_set_active_pane(w, wp); session_select(s, wl->idx); server_redraw_session(s); } else diff --git a/cmd-switch-pane.c b/cmd-switch-pane.c index a84f01fa..01466fdb 100644 --- a/cmd-switch-pane.c +++ b/cmd-switch-pane.c @@ -1,4 +1,4 @@ -/* $Id: cmd-switch-pane.c,v 1.1 2009-01-11 23:31:46 nicm Exp $ */ +/* $Id: cmd-switch-pane.c,v 1.2 2009-01-14 19:29:32 nicm Exp $ */ /* * Copyright (c) 2009 Nicholas Marriott @@ -44,17 +44,17 @@ cmd_switch_pane_exec(struct cmd *self, struct cmd_ctx *ctx) { struct cmd_target_data *data = self->data; struct winlink *wl; + struct window_pane *wp; if ((wl = cmd_find_window(ctx, data->target, NULL)) == NULL) return; + + wp = TAILQ_NEXT(wl->window->active, entry); + if (wp == NULL) + wp = TAILQ_FIRST(&wl->window->panes); + window_set_active_pane(wl->window, wp); - if (wl->window->panes[1] != NULL) { - if (wl->window->active == wl->window->panes[0]) - wl->window->active = wl->window->panes[1]; - else - wl->window->active = wl->window->panes[0]; - server_redraw_window(wl->window); - } + server_redraw_window(wl->window); if (ctx->cmdclient != NULL) server_write_client(ctx->cmdclient, MSG_EXIT, NULL, 0); diff --git a/key-bindings.c b/key-bindings.c index f3ce1bb8..633b7d53 100644 --- a/key-bindings.c +++ b/key-bindings.c @@ -1,4 +1,4 @@ -/* $Id: key-bindings.c,v 1.45 2009-01-13 01:08:40 nicm Exp $ */ +/* $Id: key-bindings.c,v 1.46 2009-01-14 19:29:32 nicm Exp $ */ /* * Copyright (c) 2007 Nicholas Marriott @@ -108,6 +108,7 @@ key_bindings_init(void) { 's', &cmd_list_sessions_entry }, { 't', &cmd_clock_mode_entry }, { 'w', &cmd_list_windows_entry }, + { 'x', &cmd_kill_pane_entry, }, { KEYC_ADDCTL(KEYC_UP), &cmd_resize_pane_up_entry }, { KEYC_ADDCTL(KEYC_DOWN), &cmd_resize_pane_down_entry }, { META, &cmd_send_prefix_entry }, diff --git a/screen-redraw.c b/screen-redraw.c index f8c8eed5..2094bb6f 100644 --- a/screen-redraw.c +++ b/screen-redraw.c @@ -1,4 +1,4 @@ -/* $Id: screen-redraw.c,v 1.17 2009-01-12 18:22:47 nicm Exp $ */ +/* $Id: screen-redraw.c,v 1.18 2009-01-14 19:29:32 nicm Exp $ */ /* * Copyright (c) 2007 Nicholas Marriott @@ -23,7 +23,7 @@ #include "tmux.h" void screen_redraw_blankx(struct client *, u_int, u_int); -void screen_redraw_blanky(struct client *, u_int, u_int); +void screen_redraw_blanky(struct client *, u_int, u_int, char); void screen_redraw_line(struct client *, struct screen *, u_int, u_int); /* Redraw entire screen.. */ @@ -42,38 +42,24 @@ screen_redraw_screen(struct client *c, struct screen *s) return; } - /* - * A normal client screen is made up of three parts: a top window, a - * bottom window and a status line. The bottom window may be turned - * off; the status line is always drawn. - */ + status = options_get_number(&c->session->options, "status"); - /* Draw the top window. */ - wp = w->panes[0]; - s = wp->screen; - - sy = screen_size_y(s); - if (screen_size_y(s) == c->sy && w->panes[1] == NULL) - sy--; - cx = s->cx; - cy = s->cy; - for (i = 0; i < sy; i++) - screen_redraw_line(c, s, wp->yoff, i); - s->cx = cx; - s->cy = cy; - - /* Draw the bottom window. */ - if (c->sy > 2 && w->panes[1] != NULL) { - wp = w->panes[1]; + /* Draw the panes. */ + TAILQ_FOREACH(wp, &w->panes, entry) { s = wp->screen; sy = screen_size_y(s); - if (wp->yoff + screen_size_y(s) == s->cy) + if (!status && TAILQ_NEXT(wp, entry) == NULL) sy--; + cx = s->cx; cy = s->cy; - for (i = 0; i < sy; i++) - screen_redraw_line(c, s, wp->yoff, i); + if (wp->yoff + sy <= w->sy) { + for (i = 0; i < sy; i++) + screen_redraw_line(c, s, wp->yoff, i); + if (TAILQ_NEXT(wp, entry) != NULL) + screen_redraw_blanky(c, wp->yoff + sy, 1, '-'); + } s->cx = cx; s->cy = cy; } @@ -82,12 +68,7 @@ screen_redraw_screen(struct client *c, struct screen *s) if (w->sx < c->sx) screen_redraw_blankx(c, w->sx, c->sx - w->sx); if (w->sy < c->sy - status) - screen_redraw_blanky(c, w->sy, c->sy - w->sy); - - /* Draw separator line. */ - s = w->panes[0]->screen; - if (c->sy > 1 && screen_size_y(s) != w->sy) - screen_redraw_blanky(c, screen_size_y(s), 1); + screen_redraw_blanky(c, w->sy, c->sy - w->sy, '='); /* Draw the status line. */ screen_redraw_status(c); @@ -120,7 +101,7 @@ screen_redraw_blankx(struct client *c, u_int ox, u_int nx) /* Draw blank lines. */ void -screen_redraw_blanky(struct client *c, u_int oy, u_int ny) +screen_redraw_blanky(struct client *c, u_int oy, u_int ny, char ch) { u_int i, j; @@ -129,7 +110,7 @@ screen_redraw_blanky(struct client *c, u_int oy, u_int ny) tty_putcode2(&c->tty, TTYC_CUP, oy + j, 0); for (i = 0; i < c->sx; i++) { if (j == 0) - tty_putc(&c->tty, '-'); + tty_putc(&c->tty, ch); else tty_putc(&c->tty, ' '); } diff --git a/server.c b/server.c index e3d4919a..13e09b8f 100644 --- a/server.c +++ b/server.c @@ -1,4 +1,4 @@ -/* $Id: server.c,v 1.99 2009-01-13 06:50:10 nicm Exp $ */ +/* $Id: server.c,v 1.100 2009-01-14 19:29:32 nicm Exp $ */ /* * Copyright (c) 2007 Nicholas Marriott @@ -49,9 +49,9 @@ void server_fill_clients(struct pollfd **); void server_handle_clients(struct pollfd **); struct client *server_accept_client(int); void server_handle_client(struct client *); -void server_handle_window(struct window *, int); +void server_handle_window(struct window *, struct window_pane *wp); void server_lost_client(struct client *); -int server_lost_window(struct window *, int); +void server_check_window(struct window *); void server_check_redraw(struct client *); void server_redraw_locked(struct client *); void server_check_timers(struct client *); @@ -171,6 +171,7 @@ server_start(const char *path) int server_main(const char *srv_path, int srv_fd) { + struct window *w; struct pollfd *pfds, *pfd; int nfds, xtimeout; u_int i, n; @@ -184,7 +185,11 @@ server_main(const char *srv_path, int srv_fd) while (!sigterm) { /* Initialise pollfd array. */ nfds = 1; - nfds += ARRAY_LENGTH(&windows) * 2; + for (i = 0; i < ARRAY_LENGTH(&windows); i++) { + w = ARRAY_ITEM(&windows, i); + if (w != NULL) + nfds += window_count_panes(w); + } nfds += ARRAY_LENGTH(&clients) * 2; pfds = xrealloc(pfds, nfds, sizeof *pfds); pfd = pfds; @@ -282,17 +287,16 @@ server_fill_windows(struct pollfd **pfd) { struct window *w; struct window_pane *wp; - u_int i, j; + u_int i; for (i = 0; i < ARRAY_LENGTH(&windows); i++) { w = ARRAY_ITEM(&windows, i); - for (j = 0; j < 2; j++) { - if (w != NULL) - wp = w->panes[j]; - if (w == NULL || wp == NULL || wp->fd == -1) { - (*pfd)->fd = -1; - } else { - (*pfd)->fd = wp->fd; + if (w == NULL) + continue; + + TAILQ_FOREACH(wp, &w->panes, entry) { + (*pfd)->fd = wp->fd; + if (wp->fd != -1) { (*pfd)->events = POLLIN; if (BUFFER_USED(wp->out) > 0) (*pfd)->events |= POLLOUT; @@ -308,24 +312,25 @@ server_handle_windows(struct pollfd **pfd) { struct window *w; struct window_pane *wp; - u_int i, j; + u_int i; for (i = 0; i < ARRAY_LENGTH(&windows); i++) { w = ARRAY_ITEM(&windows, i); - for (j = 0; j < 2; j++) { - if (w != NULL) - wp = w->panes[j]; - if (w != NULL && wp != NULL && wp->fd != -1) { + if (w == NULL) + continue; + + TAILQ_FOREACH(wp, &w->panes, entry) { + if (wp->fd != -1) { if (buffer_poll(*pfd, wp->in, wp->out) != 0) { - if (server_lost_window(w, j) != 0) { - (*pfd) += 1 - j; - break; - } + close(wp->fd); + wp->fd = -1; } else - server_handle_window(w, j); + server_handle_window(w, wp); } (*pfd)++; } + + server_check_window(w); } } @@ -660,14 +665,14 @@ server_lost_client(struct client *c) /* Handle window data. */ void -server_handle_window(struct window *w, int pane) +server_handle_window(struct window *w, struct window_pane *wp) { struct session *s; struct client *c; u_int i; int action, update; - window_pane_parse(w->panes[pane]); + window_pane_parse(wp); if (!(w->flags & WINDOW_BELL) && !(w->flags & WINDOW_ACTIVITY)) return; @@ -694,7 +699,7 @@ server_handle_window(struct window *w, int pane) } break; case BELL_CURRENT: - if (w->active != w->panes[pane]) + if (w->active != wp) break; for (i = 0; i < ARRAY_LENGTH(&clients); i++) { c = ARRAY_ITEM(&clients, i); @@ -719,29 +724,35 @@ server_handle_window(struct window *w, int pane) w->flags &= ~(WINDOW_BELL|WINDOW_ACTIVITY); } -/* Lost window: move clients on to next window. */ -int -server_lost_window(struct window *w, int pane) +/* Check if window still exists.. */ +void +server_check_window(struct window *w) { + struct window_pane *wp, *wq; struct client *c; struct session *s; struct winlink *wl; - struct window_pane *wp; u_int i, j; - int destroyed; + int destroyed, flag; - wp = w->panes[pane]; - log_debug("lost window %d (%s pane %d)", wp->fd, w->name, pane); + flag = options_get_number(&w->options, "remain-on-exit"); - if (window_remove_pane(w, wp) == 0) { - server_redraw_window(w); - return (0); - } + destroyed = 1; - if (options_get_number(&w->options, "remain-on-exit")) { - wp->fd = -1; - return (0); - } + wp = TAILQ_FIRST(&w->panes); + while (wp != NULL) { + wq = TAILQ_NEXT(wp, entry); + if (wp->fd != -1) + destroyed = 0; + else if (!flag) { + window_remove_pane(w, wp); + server_redraw_window(w); + } + wp = wq; + } + + if (!destroyed) + return; for (i = 0; i < ARRAY_LENGTH(&sessions); i++) { s = ARRAY_ITEM(&sessions, i); @@ -775,7 +786,6 @@ server_lost_window(struct window *w, int pane) } recalculate_sizes(); - return (1); } /* Call any once-per-second timers. */ @@ -784,7 +794,7 @@ server_second_timers(void) { struct window *w; struct window_pane *wp; - u_int i, j; + u_int i; int xtimeout; struct tm now, then; static time_t last_t = 0; @@ -799,10 +809,8 @@ server_second_timers(void) w = ARRAY_ITEM(&windows, i); if (w == NULL) continue; - for (j = 0; j < 2; j++) { - wp = w->panes[j]; - if (wp == NULL) - continue; + + TAILQ_FOREACH(wp, &w->panes, entry) { if (wp->mode != NULL && wp->mode->timer != NULL) wp->mode->timer(wp); } diff --git a/status.c b/status.c index 147b9b05..59ccdc62 100644 --- a/status.c +++ b/status.c @@ -1,4 +1,4 @@ -/* $Id: status.c,v 1.61 2009-01-12 18:22:47 nicm Exp $ */ +/* $Id: status.c,v 1.62 2009-01-14 19:29:32 nicm Exp $ */ /* * Copyright (c) 2007 Nicholas Marriott @@ -43,6 +43,7 @@ status_redraw(struct client *c) struct session *s = c->session; struct winlink *wl; struct window_pane *wp; + struct screen *sc; char *left, *right, *text, *ptr; size_t llen, rlen, offset, xx, yy, sy; size_t size, start, width; @@ -258,20 +259,22 @@ off: * status is off. Not sure this is the right place for this. */ screen_write_start(&ctx, NULL, &c->status); - wp = s->curw->window->panes[0]; - sy = wp->sy; - if (s->curw->window->panes[1] != NULL) { - wp = s->curw->window->panes[1]; - sy += wp->sy + 1; + + sy = 0; + TAILQ_FOREACH(wp, &s->curw->window->panes, entry) { + sy += wp->sy; + sc = wp->screen; } + sy++; + screen_write_cursormove(&ctx, 0, 0); if (sy < c->sy - 1) { /* If the screen is too small, use blank. */ for (offset = 0; offset < c->sx; offset++) screen_write_putc(&ctx, &gc, ' '); } else { - screen_write_copy(&ctx, wp->screen, 0, wp->screen->grid->hsize + - screen_size_y(wp->screen) - 1, c->sx, 1); + screen_write_copy(&ctx, + sc, 0, sc->grid->hsize + screen_size_y(sc) - 1, c->sx, 1); } out: diff --git a/tmux.h b/tmux.h index 12f07b96..dd72d55b 100644 --- a/tmux.h +++ b/tmux.h @@ -1,4 +1,4 @@ -/* $Id: tmux.h,v 1.229 2009-01-13 06:50:10 nicm Exp $ */ +/* $Id: tmux.h,v 1.230 2009-01-14 19:29:32 nicm Exp $ */ /* * Copyright (c) 2007 Nicholas Marriott @@ -124,6 +124,9 @@ extern const char *__progname; /* Default environment. */ #define CHILD_ENVIRON { NULL /* TMUX= */, "TERM=screen", NULL } +/* Minimum pane size. */ +#define PANE_MINIMUM 4 /* includes separator line */ + /* Fatal errors. */ #define fatal(msg) log_fatal("%s: %s", __func__, msg); #define fatalx(msg) log_fatalx("%s: %s", __func__, msg); @@ -578,6 +581,9 @@ struct window_pane { u_int sy; u_int yoff; + + int flags; +#define PANE_HIDDEN 0x1 char *cmd; char *cwd; @@ -593,14 +599,17 @@ struct window_pane { const struct window_mode *mode; void *modedata; + + TAILQ_ENTRY(window_pane) entry; }; +TAILQ_HEAD(window_panes, window_pane); /* Window structure. */ struct window { char *name; - struct window_pane *active; - struct window_pane *panes[2]; + struct window_pane *active; + struct window_panes panes; u_int sx; u_int sy; @@ -1378,13 +1387,21 @@ struct winlink *winlink_next(struct winlinks *, struct winlink *); struct winlink *winlink_previous(struct winlinks *, struct winlink *); void winlink_stack_push(struct winlink_stack *, struct winlink *); void winlink_stack_remove(struct winlink_stack *, struct winlink *); +int window_index(struct window *, u_int *); struct window *window_create(const char *, const char *, const char *, const char **, u_int, u_int, u_int); void window_destroy(struct window *); int window_resize(struct window *, u_int, u_int); -int window_add_pane(struct window *, - u_int, const char *, const char *, const char **, u_int); -int window_remove_pane(struct window *, struct window_pane *); +void window_set_active_pane(struct window *, struct window_pane *); +struct window_pane *window_add_pane(struct window *, + const char *, const char *, const char **, u_int); +void window_remove_pane(struct window *, struct window_pane *); +u_int window_index_of_pane(struct window *, struct window_pane *); +struct window_pane *window_pane_at_index(struct window *, u_int); +void window_fit_panes(struct window *); +void window_update_panes(struct window *); +u_int window_count_panes(struct window *); +void window_destroy_panes(struct window *); struct window_pane *window_pane_create(struct window *, u_int, u_int, u_int); void window_pane_destroy(struct window_pane *); int window_pane_spawn(struct window_pane *, diff --git a/tty-write.c b/tty-write.c index 093c5430..a776ca57 100644 --- a/tty-write.c +++ b/tty-write.c @@ -1,4 +1,4 @@ -/* $Id: tty-write.c,v 1.5 2009-01-12 18:22:47 nicm Exp $ */ +/* $Id: tty-write.c,v 1.6 2009-01-14 19:29:32 nicm Exp $ */ /* * Copyright (c) 2007 Nicholas Marriott @@ -38,7 +38,7 @@ tty_vwrite_window(void *ptr, enum tty_cmd cmd, va_list ap) va_list aq; u_int i; - if (wp->window->flags & WINDOW_HIDDEN) + if (wp->window->flags & WINDOW_HIDDEN || wp->flags & PANE_HIDDEN) return; for (i = 0; i < ARRAY_LENGTH(&clients); i++) { diff --git a/window.c b/window.c index 6d8564d4..64082b23 100644 --- a/window.c +++ b/window.c @@ -1,4 +1,4 @@ -/* $Id: window.c,v 1.57 2009-01-13 06:50:10 nicm Exp $ */ +/* $Id: window.c,v 1.58 2009-01-14 19:29:32 nicm Exp $ */ /* * Copyright (c) 2007 Nicholas Marriott @@ -204,17 +204,29 @@ winlink_stack_remove(struct winlink_stack *stack, struct winlink *wl) } } +int +window_index(struct window *s, u_int *i) +{ + for (*i = 0; *i < ARRAY_LENGTH(&windows); (*i)++) { + if (s == ARRAY_ITEM(&windows, *i)) + return (0); + } + return (-1); +} + struct window * window_create(const char *name, const char *cmd, const char *cwd, const char **envp, u_int sx, u_int sy, u_int hlimit) { struct window *w; + u_int i; char *ptr, *copy; w = xmalloc(sizeof *w); w->flags = 0; - w->panes[0] = NULL; - w->panes[1] = NULL; + + TAILQ_INIT(&w->panes); + w->active = NULL; w->sx = sx; w->sy = sy; @@ -244,14 +256,21 @@ window_create(const char *name, const char *cmd, } else w->name = xstrdup(name); - ARRAY_ADD(&windows, w); + for (i = 0; i < ARRAY_LENGTH(&windows); i++) { + if (ARRAY_ITEM(&windows, i) == NULL) { + ARRAY_SET(&windows, i, w); + break; + } + } + if (i == ARRAY_LENGTH(&windows)) + ARRAY_ADD(&windows, w); w->references = 0; - if (window_add_pane(w, w->sy, cmd, cwd, envp, hlimit) < 0) { + if (window_add_pane(w, cmd, cwd, envp, hlimit) == NULL) { window_destroy(w); return (NULL); } - w->active = w->panes[0]; + w->active = TAILQ_FIRST(&w->panes); return (w); } @@ -260,17 +279,15 @@ window_destroy(struct window *w) { u_int i; - for (i = 0; i < ARRAY_LENGTH(&windows); i++) { - if (ARRAY_ITEM(&windows, i) == w) - break; - } - ARRAY_REMOVE(&windows, i); + if (window_index(w, &i) != 0) + fatalx("index not found"); + ARRAY_SET(&windows, i, NULL); + while (!ARRAY_EMPTY(&windows) && ARRAY_LAST(&windows) == NULL) + ARRAY_TRUNC(&windows, 1); options_free(&w->options); - window_pane_destroy(w->panes[0]); - if (w->panes[1] != NULL) - window_pane_destroy(w->panes[1]); + window_destroy_panes(w); xfree(w->name); xfree(w); @@ -279,111 +296,213 @@ window_destroy(struct window *w) int window_resize(struct window *w, u_int sx, u_int sy) { - int change; - u_int y0, y1; + w->sx = sx; + w->sy = sy; - if (w->panes[1] == NULL) - window_pane_resize(w->panes[0], sx, sy); - else { - if (sy <= 3) { - y0 = 1; - y1 = 1; + window_fit_panes(w); + return (0); +} + +void +window_fit_panes(struct window *w) +{ + struct window_pane *wp; + u_int npanes, canfit, total; + int left; + + if (TAILQ_EMPTY(&w->panes)) + return; + + /* Clear hidden flags. */ + TAILQ_FOREACH(wp, &w->panes, entry) + wp->flags &= ~PANE_HIDDEN; + + /* Check the new size. */ + npanes = window_count_panes(w); + if (w->sy <= PANE_MINIMUM * npanes) { + /* How many can we fit? */ + canfit = w->sy / PANE_MINIMUM; + if (canfit == 0) { + /* None. Just use this size for the first. */ + TAILQ_FOREACH(wp, &w->panes, entry) { + if (wp == TAILQ_FIRST(&w->panes)) + wp->sy = w->sy; + else + wp->flags |= PANE_HIDDEN; + } } else { - y0 = w->panes[0]->sy; - y1 = w->panes[1]->sy; - - change = sy - w->sy; - if (change > 0) { - while (change > 0) { - if (y1 < y0) - y1++; - else - y0++; - change--; + /* >=1, set minimum for them all. */ + TAILQ_FOREACH(wp, &w->panes, entry) { + if (canfit-- > 0) + wp->sy = PANE_MINIMUM - 1; + else + wp->flags |= PANE_HIDDEN; + } + /* And increase the first by the rest. */ + TAILQ_FIRST(&w->panes)->sy += 1 + w->sy % PANE_MINIMUM; + } + } else { + /* In theory they will all fit. Find the current total. */ + total = 0; + TAILQ_FOREACH(wp, &w->panes, entry) + total += wp->sy; + total += npanes - 1; + + /* Growing or shrinking? */ + left = w->sy - total; + if (left > 0) { + /* Growing. Expand evenly. */ + while (left > 0) { + TAILQ_FOREACH(wp, &w->panes, entry) { + wp->sy++; + if (--left == 0) + break; } - } else if (change < 0) { - while (change < 0) { - if (y1 > y0) - y1--; - else - y0--; - change++; + } + } else { + /* Shrinking. Reduce evenly down to minimum. */ + while (left < 0) { + TAILQ_FOREACH(wp, &w->panes, entry) { + if (wp->sy <= PANE_MINIMUM - 1) + continue; + wp->sy--; + if (++left == 0) + break; } } } - window_pane_resize(w->panes[0], sx, y0); - window_pane_resize(w->panes[1], sx, y1); - w->panes[1]->yoff = y0 + 1; + } + + /* Now do the resize. */ + TAILQ_FOREACH(wp, &w->panes, entry) { + wp->sy--; + window_pane_resize(wp, w->sx, wp->sy + 1); } - w->sx = sx; - w->sy = sy; - return (0); + /* Fill in the offsets. */ + window_update_panes(w); + + /* Switch the active window if necessary. */ + window_set_active_pane(w, w->active); } -int -window_add_pane(struct window *w, u_int y1, +void +window_update_panes(struct window *w) +{ + struct window_pane *wp; + u_int yoff; + + yoff = 0; + TAILQ_FOREACH(wp, &w->panes, entry) { + if (wp->flags & PANE_HIDDEN) + continue; + wp->yoff = yoff; + yoff += wp->sy + 1; + } +} + +void +window_set_active_pane(struct window *w, struct window_pane *wp) +{ + w->active = wp; + while (w->active->flags & PANE_HIDDEN) + w->active = TAILQ_PREV(w->active, window_panes, entry); +} + +struct window_pane * +window_add_pane(struct window *w, const char *cmd, const char *cwd, const char **envp, u_int hlimit) { struct window_pane *wp; - u_int y0; + u_int wanty; - if (w->panes[1] != NULL) - return (-1); - - if (w->panes[0] == NULL) { - /* No existing panes. */ - wp = w->panes[0] = window_pane_create(w, w->sx, w->sy, hlimit); - wp->yoff = 0; - } else { - /* One existing pane. */ - if (y1 > w->sy) - y1 = w->sy; - y0 = w->sy - y1; - if (y0 == 0) { - y0 = 1; - y1--; - } - if (y0 > 1) - y0--; - else if (y1 > 1) - y1--; - window_pane_resize(w->panes[0], w->sx, y0); - - wp = w->panes[1] = window_pane_create(w, w->sx, y1, hlimit); - wp->yoff = y0 + 1; + if (TAILQ_EMPTY(&w->panes)) + wanty = w->sy; + else { + if (w->active->sy < PANE_MINIMUM * 2) + return (NULL); + wanty = (w->active->sy / 2 + w->active->sy % 2) - 1; + window_pane_resize(w->active, w->sx, w->active->sy / 2); } + wp = window_pane_create(w, w->sx, wanty, hlimit); + if (TAILQ_EMPTY(&w->panes)) + TAILQ_INSERT_HEAD(&w->panes, wp, entry); + else + TAILQ_INSERT_AFTER(&w->panes, w->active, wp, entry); + window_update_panes(w); if (window_pane_spawn(wp, cmd, cwd, envp) != 0) { window_remove_pane(w, wp); - return (-1); + return (NULL); } - return (0); + return (wp); } -int +void window_remove_pane(struct window *w, struct window_pane *wp) { - int pane; + w->active = TAILQ_PREV(wp, window_panes, entry); + if (w->active == NULL) + w->active = TAILQ_NEXT(wp, entry); - pane = 0; - if (wp == w->panes[1]) - pane = 1; + TAILQ_REMOVE(&w->panes, wp, entry); + window_pane_destroy(wp); - if (w->panes[1] != NULL) { - window_pane_destroy(w->panes[pane]); - w->panes[pane] = NULL; - if (pane == 0) { - w->panes[0] = w->panes[1]; - w->panes[1] = NULL; - } - w->active = w->panes[0]; + window_fit_panes(w); +} - window_pane_resize(w->active, w->sx, w->sy); - w->active->yoff = 0; - return (0); +u_int +window_index_of_pane(struct window *w, struct window_pane *find) +{ + struct window_pane *wp; + u_int n; + + n = 0; + TAILQ_FOREACH(wp, &w->panes, entry) { + if (wp == find) + return (n); + n++; + } + fatalx("unknown pane"); +} + +struct window_pane * +window_pane_at_index(struct window *w, u_int idx) +{ + struct window_pane *wp; + u_int n; + + n = 0; + TAILQ_FOREACH(wp, &w->panes, entry) { + if (n == idx) + return (wp); + n++; + } + return (NULL); +} + +u_int +window_count_panes(struct window *w) +{ + struct window_pane *wp; + u_int n; + + n = 0; + TAILQ_FOREACH(wp, &w->panes, entry) + n++; + return (n); +} + +void +window_destroy_panes(struct window *w) +{ + struct window_pane *wp; + + while (!TAILQ_EMPTY(&w->panes)) { + wp = TAILQ_FIRST(&w->panes); + TAILQ_REMOVE(&w->panes, wp, entry); + window_pane_destroy(wp); } - return (1); } struct window_pane * @@ -391,7 +510,7 @@ window_pane_create(struct window *w, u_int sx, u_int sy, u_int hlimit) { struct window_pane *wp; - wp = xmalloc(sizeof *wp); + wp = xcalloc(1, sizeof *wp); wp->window = w; wp->cmd = NULL; diff --git a/xmalloc.c b/xmalloc.c index 50b22fef..68861c2c 100644 --- a/xmalloc.c +++ b/xmalloc.c @@ -1,4 +1,4 @@ -/* $Id: xmalloc.c,v 1.6 2008-08-07 20:20:52 nicm Exp $ */ +/* $Id: xmalloc.c,v 1.7 2009-01-14 19:29:32 nicm Exp $ */ /* * Copyright (c) 2004 Nicholas Marriott @@ -44,7 +44,7 @@ xcalloc(size_t nmemb, size_t size) void *ptr; if (size == 0 || nmemb == 0) - fatalx("zero size"); + { abort();fatalx("zero size");} if (SIZE_MAX / nmemb < size) fatalx("nmemb * size > SIZE_MAX"); if ((ptr = calloc(nmemb, size)) == NULL)