diff --git a/CHANGES b/CHANGES index a7e64ead..a7b1470d 100644 --- a/CHANGES +++ b/CHANGES @@ -1,3 +1,10 @@ +26 October 2007 + +* (nicm) Redo window data structures. The global array remains, but each per- + session list is now a RB tree of winlink structures. This disassociates the + window index from the array size (allowing arbitrary indexes) which still + allowing windows to have multiple indexes. + 25 October 2007 * (nicm) has-session command: checks if session exists. @@ -163,5 +170,5 @@ (including mutt, emacs). No status bar yet and no key remapping or other customisation. -$Id: CHANGES,v 1.51 2007-10-25 17:44:24 nicm Exp $ +$Id: CHANGES,v 1.52 2007-10-26 12:29:07 nicm Exp $ diff --git a/TODO b/TODO index d58f333e..f40aa2a2 100644 --- a/TODO +++ b/TODO @@ -1,4 +1,3 @@ -- decide if TIOCPKT is necessary and either handle it or remove the code - it would be nice if there wasn't so much copying buffers about, audit uses - useful env vars like WINDOW - sort out who controls the buffers in local.c a bit @@ -20,8 +19,6 @@ - profile/optimise, particularly (i suspect) input.c - tidy up input.c a bit - decide about customised status line -- rethink data structures. window->index is O(n), could have a w->idx member - or use queues/trees and avoid NULLs? - client could pass term/tty fd up to server and then do nothing. what problems would this cause? -- need access to all terminfo data at once... signals? - cleanup/redesign IPC diff --git a/cmd-kill-window.c b/cmd-kill-window.c index 07d16a97..dce7f768 100644 --- a/cmd-kill-window.c +++ b/cmd-kill-window.c @@ -1,4 +1,4 @@ -/* $Id: cmd-kill-window.c,v 1.1 2007-10-19 11:10:35 nicm Exp $ */ +/* $Id: cmd-kill-window.c,v 1.2 2007-10-26 12:29:07 nicm Exp $ */ /* * Copyright (c) 2007 Nicholas Marriott @@ -92,7 +92,7 @@ cmd_kill_window_exec(void *ptr, struct cmd_ctx *ctx) struct cmd_kill_window_data *data = ptr, std = { -1 }; struct client *c = ctx->client; struct session *s = ctx->session; - struct window *w; + struct winlink *wl; u_int i; int destroyed; @@ -100,13 +100,13 @@ cmd_kill_window_exec(void *ptr, struct cmd_ctx *ctx) data = &std; if (data->idx == -1) - w = s->window; - else if ((w = window_at(&s->windows, data->idx)) == NULL) { + wl = s->curw; + else if ((wl = winlink_find_by_index(&s->windows, data->idx)) == NULL) { ctx->error(ctx, "no window %u", data->idx); return; } - destroyed = session_detach(s, w); + destroyed = session_detach(s, wl); for (i = 0; i < ARRAY_LENGTH(&clients); i++) { c = ARRAY_ITEM(&clients, i); if (c == NULL || c->session != s) diff --git a/cmd-list-sessions.c b/cmd-list-sessions.c index 81aa1bb3..6bacdde2 100644 --- a/cmd-list-sessions.c +++ b/cmd-list-sessions.c @@ -1,4 +1,4 @@ -/* $Id: cmd-list-sessions.c,v 1.6 2007-10-23 09:36:07 nicm Exp $ */ +/* $Id: cmd-list-sessions.c,v 1.7 2007-10-26 12:29:07 nicm Exp $ */ /* * Copyright (c) 2007 Nicholas Marriott @@ -45,8 +45,9 @@ cmd_list_sessions_exec(unused void *ptr, struct cmd_ctx *ctx) { struct client *c = ctx->client; struct session *s = ctx->session; + struct winlink *wl; char *tim; - u_int i, j, n; + u_int i, n; for (i = 0; i < ARRAY_LENGTH(&sessions); i++) { s = ARRAY_ITEM(&sessions, i); @@ -54,10 +55,8 @@ cmd_list_sessions_exec(unused void *ptr, struct cmd_ctx *ctx) continue; n = 0; - for (j = 0; j < ARRAY_LENGTH(&s->windows); j++) { - if (ARRAY_ITEM(&s->windows, j) != NULL) - n++; - } + RB_FOREACH(wl, winlinks, &s->windows) + n++; tim = ctime(&s->tim); *strchr(tim, '\n') = '\0'; diff --git a/cmd-list-windows.c b/cmd-list-windows.c index 6807845f..a2bce816 100644 --- a/cmd-list-windows.c +++ b/cmd-list-windows.c @@ -1,4 +1,4 @@ -/* $Id: cmd-list-windows.c,v 1.3 2007-10-23 09:36:07 nicm Exp $ */ +/* $Id: cmd-list-windows.c,v 1.4 2007-10-26 12:29:07 nicm Exp $ */ /* * Copyright (c) 2007 Nicholas Marriott @@ -44,17 +44,14 @@ cmd_list_windows_exec(unused void *ptr, struct cmd_ctx *ctx) { struct client *c = ctx->client; struct session *s = ctx->session; + struct winlink *wl; struct window *w; - u_int i; - for (i = 0; i < ARRAY_LENGTH(&s->windows); i++) { - w = ARRAY_ITEM(&s->windows, i); - if (w == NULL) - continue; - - ctx->print(ctx, - "%u: %s \"%s\" (%s) [%ux%u]", i, w->name, w->screen.title, - ttyname(w->fd), w->screen.sx, w->screen.sy); + RB_FOREACH(wl, winlinks, &s->windows) { + w = wl->window; + ctx->print(ctx, "%u: %s \"%s\" (%s) [%ux%u]", wl->idx, + w->name, w->screen.title, ttyname(w->fd), + w->screen.sx, w->screen.sy); } if (!(ctx->flags & CMD_KEY)) diff --git a/cmd-new-session.c b/cmd-new-session.c index 80f77490..aeff70bf 100644 --- a/cmd-new-session.c +++ b/cmd-new-session.c @@ -1,4 +1,4 @@ -/* $Id: cmd-new-session.c,v 1.13 2007-10-19 17:15:29 nicm Exp $ */ +/* $Id: cmd-new-session.c,v 1.14 2007-10-26 12:29:07 nicm Exp $ */ /* * Copyright (c) 2007 Nicholas Marriott @@ -133,8 +133,8 @@ cmd_new_session_exec(void *ptr, struct cmd_ctx *ctx) if (c->session == NULL) fatalx("session_create failed"); if (data->winname != NULL) { - xfree(c->session->window->name); - c->session->window->name = xstrdup(data->winname); + xfree(c->session->curw->window->name); + c->session->curw->window->name = xstrdup(data->winname); } if (data->flag_detached) diff --git a/cmd-new-window.c b/cmd-new-window.c index ca87cb53..ea81734d 100644 --- a/cmd-new-window.c +++ b/cmd-new-window.c @@ -1,4 +1,4 @@ -/* $Id: cmd-new-window.c,v 1.9 2007-10-19 09:21:25 nicm Exp $ */ +/* $Id: cmd-new-window.c,v 1.10 2007-10-26 12:29:07 nicm Exp $ */ /* * Copyright (c) 2007 Nicholas Marriott @@ -19,6 +19,7 @@ #include #include +#include #include "tmux.h" @@ -35,11 +36,12 @@ void cmd_new_window_free(void *); struct cmd_new_window_data { char *name; char *cmd; + int idx; int flag_detached; }; const struct cmd_entry cmd_new_window_entry = { - "new-window", "neww", "[-d] [-n name] [command]", + "new-window", "neww", "[-d] [-i index] [-n name] [command]", 0, cmd_new_window_parse, cmd_new_window_exec, @@ -52,15 +54,24 @@ int cmd_new_window_parse(void **ptr, int argc, char **argv, char **cause) { struct cmd_new_window_data *data; + const char *errstr; int opt; *ptr = data = xmalloc(sizeof *data); + data->idx = -1; data->flag_detached = 0; data->name = NULL; data->cmd = NULL; - while ((opt = getopt(argc, argv, "dn:")) != EOF) { + while ((opt = getopt(argc, argv, "di:n:")) != EOF) { switch (opt) { + case 'i': + data->idx = strtonum(optarg, 0, UINT_MAX, &errstr); + if (errstr != NULL) { + xasprintf(cause, "index %s", errstr); + goto error; + } + break; case 'n': data->name = xstrdup(optarg); break; @@ -85,6 +96,7 @@ usage: usage(cause, "%s %s", cmd_new_window_entry.name, cmd_new_window_entry.usage); +error: cmd_new_window_free(data); return (-1); } @@ -92,11 +104,12 @@ usage: void cmd_new_window_exec(void *ptr, struct cmd_ctx *ctx) { - struct cmd_new_window_data *data = ptr, std = { NULL, NULL, 0 }; + struct cmd_new_window_data *data = ptr; + struct cmd_new_window_data std = { NULL, NULL, -1, 0 }; struct client *c = ctx->client; struct session *s = ctx->session; + struct winlink *wl; char *cmd; - u_int i; if (data == NULL) data = &std; @@ -105,12 +118,14 @@ cmd_new_window_exec(void *ptr, struct cmd_ctx *ctx) if (cmd == NULL) cmd = default_command; - if (session_new(s, data->name, cmd, &i) != 0) { + if (data->idx < 0) + data->idx = -1; + if ((wl = session_new(s, data->name, cmd, data->idx)) == NULL) { ctx->error(ctx, "command failed: %s", cmd); return; } if (!data->flag_detached) { - session_select(s, i); + session_select(s, wl->idx); server_redraw_session(s); } else server_status_session(s); diff --git a/cmd-rename-window.c b/cmd-rename-window.c index fab15f32..1816d082 100644 --- a/cmd-rename-window.c +++ b/cmd-rename-window.c @@ -1,4 +1,4 @@ -/* $Id: cmd-rename-window.c,v 1.7 2007-10-19 17:15:29 nicm Exp $ */ +/* $Id: cmd-rename-window.c,v 1.8 2007-10-26 12:29:07 nicm Exp $ */ /* * Copyright (c) 2007 Nicholas Marriott @@ -96,19 +96,19 @@ cmd_rename_window_exec(void *ptr, struct cmd_ctx *ctx) struct cmd_rename_window_data *data = ptr; struct client *c = ctx->client; struct session *s = ctx->session; - struct window *w; + struct winlink *wl; if (data == NULL) return; if (data->idx == -1) - w = s->window; - else if ((w = window_at(&s->windows, data->idx)) == NULL) { + wl = s->curw; + else if ((wl = winlink_find_by_index(&s->windows, data->idx)) == NULL) { ctx->error(ctx, "no window %u", data->idx); return; } - xfree(w->name); - w->name = xstrdup(data->newname); + xfree(wl->window->name); + wl->window->name = xstrdup(data->newname); server_status_session(s); diff --git a/cmd-select-window.c b/cmd-select-window.c index c0633567..9b906ea4 100644 --- a/cmd-select-window.c +++ b/cmd-select-window.c @@ -1,4 +1,4 @@ -/* $Id: cmd-select-window.c,v 1.4 2007-10-19 09:21:26 nicm Exp $ */ +/* $Id: cmd-select-window.c,v 1.5 2007-10-26 12:29:07 nicm Exp $ */ /* * Copyright (c) 2007 Nicholas Marriott @@ -108,10 +108,16 @@ cmd_select_window_exec(void *ptr, struct cmd_ctx *ctx) if (data == NULL) return; - if (session_select(s, data->idx) == 0) + switch (session_select(s, data->idx)) { + case 0: server_redraw_session(s); - else + break; + case 1: + break; + default: ctx->error(ctx, "no window %u", data->idx); + break; + } if (!(ctx->flags & CMD_KEY)) server_write_client(c, MSG_EXIT, NULL, 0); diff --git a/cmd-send-prefix.c b/cmd-send-prefix.c index 304afbcc..62e3b4bc 100644 --- a/cmd-send-prefix.c +++ b/cmd-send-prefix.c @@ -1,4 +1,4 @@ -/* $Id: cmd-send-prefix.c,v 1.2 2007-10-19 23:33:20 nicm Exp $ */ +/* $Id: cmd-send-prefix.c,v 1.3 2007-10-26 12:29:07 nicm Exp $ */ /* * Copyright (c) 2007 Nicholas Marriott @@ -49,5 +49,5 @@ cmd_send_prefix_exec(unused void *ptr, struct cmd_ctx *ctx) return; } - window_key(c->session->window, prefix_key); + input_translate_key(c->session->curw->window->out, prefix_key); } diff --git a/key-bindings.c b/key-bindings.c index ec1014d4..72b89c9a 100644 --- a/key-bindings.c +++ b/key-bindings.c @@ -1,4 +1,4 @@ -/* $Id: key-bindings.c,v 1.11 2007-10-23 10:25:57 nicm Exp $ */ +/* $Id: key-bindings.c,v 1.12 2007-10-26 12:29:07 nicm Exp $ */ /* * Copyright (c) 2007 Nicholas Marriott @@ -177,7 +177,7 @@ key_bindings_print(struct cmd_ctx *ctx, const char *fmt, ...) if (!(c->flags & CLIENT_HOLD)) { input_store_zero(c->out, CODE_CURSOROFF); - for (i = 0; i < c->session->window->screen.sy; i++) { + for (i = 0; i < c->session->curw->window->screen.sy; i++) { input_store_two(c->out, CODE_CURSORMOVE, i + 1, 1); input_store_zero(c->out, CODE_CLEARLINE); } diff --git a/server-fn.c b/server-fn.c index 74f07a0e..536ad781 100644 --- a/server-fn.c +++ b/server-fn.c @@ -1,4 +1,4 @@ -/* $Id: server-fn.c,v 1.22 2007-10-23 10:48:23 nicm Exp $ */ +/* $Id: server-fn.c,v 1.23 2007-10-26 12:29:07 nicm Exp $ */ /* * Copyright (c) 2007 Nicholas Marriott @@ -114,7 +114,7 @@ server_write_window_cur( for (i = 0; i < ARRAY_LENGTH(&clients); i++) { c = ARRAY_ITEM(&clients, i); if (c != NULL && - c->session != NULL && c->session->window == w) { + c->session != NULL && c->session->curw->window == w) { if (c->flags & CLIENT_HOLD) /* XXX OUTPUT only */ continue; server_write_client(c, type, buf, len); @@ -165,7 +165,7 @@ server_status_client(struct client *c) void server_clear_client(struct client *c) { - struct screen *s = &c->session->window->screen; + struct screen *s = &c->session->curw->window->screen; struct hdr hdr; size_t size; u_int i; @@ -192,7 +192,7 @@ server_clear_client(struct client *c) void server_redraw_client(struct client *c) { - struct screen *s = &c->session->window->screen; + struct screen *s = &c->session->curw->window->screen; struct hdr hdr; size_t size; @@ -248,7 +248,8 @@ server_clear_window_cur(struct window *w) for (i = 0; i < ARRAY_LENGTH(&clients); i++) { c = ARRAY_ITEM(&clients, i); - if (c != NULL && c->session != NULL && c->session->window == w) + if (c != NULL && + c->session != NULL && c->session->curw->window == w) server_clear_client(c); } } @@ -276,7 +277,8 @@ server_redraw_window_cur(struct window *w) for (i = 0; i < ARRAY_LENGTH(&clients); i++) { c = ARRAY_ITEM(&clients, i); - if (c != NULL && c->session != NULL && c->session->window == w) + if (c != NULL && + c->session != NULL && c->session->curw->window == w) server_redraw_client(c); } } @@ -304,7 +306,8 @@ server_status_window_cur(struct window *w) for (i = 0; i < ARRAY_LENGTH(&clients); i++) { c = ARRAY_ITEM(&clients, i); - if (c != NULL && c->session != NULL && c->session->window == w) + if (c != NULL && + c->session != NULL && c->session->curw->window == w) server_status_client(c); } } @@ -327,6 +330,7 @@ server_status_window_all(struct window *w) void server_write_message(struct client *c, const char *fmt, ...) { + struct screen *s = &c->session->curw->window->screen; struct hdr hdr; va_list ap; char *msg; @@ -364,8 +368,7 @@ server_write_message(struct client *c, const char *fmt, ...) size = BUFFER_USED(c->out); if (status_lines == 0) { - screen_draw( - &c->session->window->screen, c->out, c->sy - 1, c->sy - 1); + screen_draw(s, c->out, c->sy - 1, c->sy - 1); } else status_write(c); diff --git a/server-msg.c b/server-msg.c index 15be6f88..ae25a84c 100644 --- a/server-msg.c +++ b/server-msg.c @@ -1,4 +1,4 @@ -/* $Id: server-msg.c,v 1.29 2007-10-23 10:48:23 nicm Exp $ */ +/* $Id: server-msg.c,v 1.30 2007-10-26 12:29:07 nicm Exp $ */ /* * Copyright (c) 2007 Nicholas Marriott @@ -223,7 +223,7 @@ server_msg_fn_keys(struct hdr *hdr, struct client *c) if (key == prefix_key) c->flags |= CLIENT_PREFIX; else - window_key(c->session->window, key); + input_translate_key(c->session->curw->window->out, key); } return (0); diff --git a/server.c b/server.c index 877724fd..81c0cddd 100644 --- a/server.c +++ b/server.c @@ -1,4 +1,4 @@ -/* $Id: server.c,v 1.34 2007-10-24 11:30:02 nicm Exp $ */ +/* $Id: server.c,v 1.35 2007-10-26 12:29:07 nicm Exp $ */ /* * Copyright (c) 2007 Nicholas Marriott @@ -228,7 +228,7 @@ server_handle_windows(struct pollfd **pfd) for (i = 0; i < ARRAY_LENGTH(&windows); i++) { if ((w = ARRAY_ITEM(&windows, i)) != NULL) { - if (window_poll(w, *pfd) != 0) + if (buffer_poll(*pfd, w->in, w->out) != 0) server_lost_window(w); else server_handle_window(w); @@ -259,14 +259,14 @@ server_fill_clients(struct pollfd **pfd) /* Handle client pollfds. */ void -server_handle_clients(struct pollfd *(*pfd)) +server_handle_clients(struct pollfd **pfd) { struct client *c; u_int i; for (i = 0; i < ARRAY_LENGTH(&clients); i++) { if ((c = ARRAY_ITEM(&clients, i)) != NULL) { - if (buffer_poll((*pfd), c->in, c->out) != 0) + if (buffer_poll(*pfd, c->in, c->out) != 0) server_lost_client(c); else server_msg_dispatch(c); @@ -345,7 +345,7 @@ server_handle_window(struct window *w) u_int i; b = buffer_create(BUFSIZ); - window_data(w, b); + input_parse(w, b); if (BUFFER_USED(b) != 0) { server_write_window_cur( w, MSG_DATA, BUFFER_OUT(b), BUFFER_USED(b)); @@ -368,7 +368,7 @@ server_handle_window(struct window *w) case BELL_CURRENT: for (i = 0; i < ARRAY_LENGTH(&sessions); i++) { s = ARRAY_ITEM(&sessions, i); - if (s != NULL && s->window == w) + if (s != NULL && s->curw->window == w) server_write_session(s, MSG_DATA, "\007", 1); } break; @@ -384,6 +384,7 @@ server_lost_window(struct window *w) { struct client *c; struct session *s; + struct winlink *wl; u_int i, j; int destroyed; @@ -397,16 +398,23 @@ server_lost_window(struct window *w) continue; /* Detach window and either redraw or kill clients. */ - destroyed = session_detach(s, w); - for (j = 0; j < ARRAY_LENGTH(&clients); j++) { - c = ARRAY_ITEM(&clients, j); - if (c == NULL || c->session != s) + restart: + RB_FOREACH(wl, winlinks, &s->windows) { + if (wl->window != w) continue; - if (destroyed) { + destroyed = session_detach(s, wl); + for (j = 0; j < ARRAY_LENGTH(&clients); j++) { + c = ARRAY_ITEM(&clients, j); + if (c == NULL || c->session != s) + continue; + if (!destroyed) { + server_redraw_client(c); + continue; + } c->session = NULL; server_write_client(c, MSG_EXIT, NULL, 0); - } else - server_redraw_client(c); + } + goto restart; } } diff --git a/session.c b/session.c index c55542e4..54067a5b 100644 --- a/session.c +++ b/session.c @@ -1,4 +1,4 @@ -/* $Id: session.c,v 1.24 2007-10-24 11:05:59 nicm Exp $ */ +/* $Id: session.c,v 1.25 2007-10-26 12:29:07 nicm Exp $ */ /* * Copyright (c) 2007 Nicholas Marriott @@ -29,33 +29,41 @@ struct sessions sessions; void -session_cancelbell(struct session *s, struct window *w) +session_cancelbell(struct session *s, struct winlink *wl) { u_int i; - if (window_index(&s->bells, w, &i) == 0) - window_remove(&s->bells, w); + for (i = 0; i < ARRAY_LENGTH(&s->bells); i++) { + if (ARRAY_ITEM(&s->bells, i) == wl) { + ARRAY_REMOVE(&s->bells, i); + break; + } + } } void session_addbell(struct session *s, struct window *w) { - u_int i; + struct winlink *wl; - /* Never bell in the current window. */ - if (w == s->window || !session_has(s, w)) - return; - - if (window_index(&s->bells, w, &i) != 0) - window_add(&s->bells, w); + RB_FOREACH(wl, winlinks, &s->windows) { + if (wl == s->curw) + continue; + if (wl->window == w && !session_hasbell(s, wl)) + ARRAY_ADD(&s->bells, wl); + } } int -session_hasbell(struct session *s, struct window *w) +session_hasbell(struct session *s, struct winlink *wl) { u_int i; - return (window_index(&s->bells, w, &i) == 0); + for (i = 0; i < ARRAY_LENGTH(&s->bells); i++) { + if (ARRAY_ITEM(&s->bells, i) == wl) + return (1); + } + return (0); } /* Find session by name. */ @@ -83,8 +91,8 @@ session_create(const char *name, const char *cmd, u_int sx, u_int sy) s = xmalloc(sizeof *s); s->tim = time(NULL); - s->window = s->last = NULL; - ARRAY_INIT(&s->windows); + s->curw = s->lastw = NULL; + RB_INIT(&s->windows); ARRAY_INIT(&s->bells); s->sx = sx; @@ -103,7 +111,7 @@ session_create(const char *name, const char *cmd, u_int sx, u_int sy) s->name = xstrdup(name); else xasprintf(&s->name, "%u", i); - if (session_new(s, NULL, cmd, &i) != 0) { + if (session_new(s, NULL, cmd, -1) == NULL) { session_destroy(s); return (NULL); } @@ -116,7 +124,8 @@ session_create(const char *name, const char *cmd, u_int sx, u_int sy) void session_destroy(struct session *s) { - u_int i; + struct winlink *wl; + u_int i; if (session_index(s, &i) != 0) fatalx("session not found"); @@ -124,11 +133,11 @@ session_destroy(struct session *s) while (!ARRAY_EMPTY(&sessions) && ARRAY_LAST(&sessions) == NULL) ARRAY_TRUNC(&sessions, 1); - for (i = 0; i < ARRAY_LENGTH(&s->windows); i++) { - if (ARRAY_ITEM(&s->windows, i) != NULL) - window_remove(&s->windows, ARRAY_ITEM(&s->windows, i)); + while (!RB_EMPTY(&s->windows)) { + wl = RB_ROOT(&s->windows); + RB_REMOVE(winlinks, &s->windows, wl); + winlink_remove(&s->windows, wl); } - ARRAY_FREE(&s->windows); xfree(s->name); xfree(s); @@ -146,44 +155,43 @@ session_index(struct session *s, u_int *i) } /* Create a new window on a session. */ -int -session_new(struct session *s, const char *name, const char *cmd, u_int *i) +struct winlink * +session_new(struct session *s, const char *name, const char *cmd, int idx) { struct window *w; const char *environ[] = { NULL, "TERM=screen", NULL }; char buf[256]; + u_int i; - if (session_index(s, i) != 0) + if (session_index(s, &i) != 0) fatalx("session not found"); - xsnprintf(buf, sizeof buf, "TMUX=%ld,%u", (long) getpid(), *i); + xsnprintf(buf, sizeof buf, "TMUX=%ld,%u", (long) getpid(), i); environ[0] = buf; if ((w = window_create(name, cmd, environ, s->sx, s->sy)) == NULL) - return (-1); - session_attach(s, w); - - window_index(&s->windows, w, i); - return (0); + return (NULL); + return (session_attach(s, w, idx)); } /* Attach a window to a session. */ -void -session_attach(struct session *s, struct window *w) +struct winlink * +session_attach(struct session *s, struct window *w, int idx) { - window_add(&s->windows, w); + return (winlink_add(&s->windows, w, idx)); } /* Detach a window from a session. */ int -session_detach(struct session *s, struct window *w) +session_detach(struct session *s, struct winlink *wl) { - if (s->window == w && session_last(s) != 0 && session_previous(s) != 0) + if (s->curw == wl && session_last(s) != 0 && session_previous(s) != 0) session_next(s); - if (s->last == w) - s->last = NULL; + if (s->lastw == wl) + s->lastw = NULL; - window_remove(&s->windows, w); - if (ARRAY_EMPTY(&s->windows)) { + session_cancelbell(s, wl); + winlink_remove(&s->windows, wl); + if (RB_EMPTY(&s->windows)) { session_destroy(s); return (1); } @@ -194,34 +202,32 @@ session_detach(struct session *s, struct window *w) int session_has(struct session *s, struct window *w) { - u_int i; + struct winlink *wl; - return (window_index(&s->windows, w, &i) == 0); + RB_FOREACH(wl, winlinks, &s->windows) { + if (wl->window == w) + return (1); + } + return (0); } /* Move session to next window. */ int session_next(struct session *s) { - struct window *w; - u_int n; + struct winlink *wl; - if (s->window == NULL) + if (s->curw == NULL) return (-1); - w = window_next(&s->windows, s->window); - if (w == NULL) { - n = 0; - while ((w = ARRAY_ITEM(&s->windows, n)) == NULL) - n++; - if (w == s->window) - return (1); - } - if (w == s->window) - return (0); - s->last = s->window; - s->window = w; - session_cancelbell(s, w); + wl = winlink_next(&s->windows, s->curw); + if (wl == NULL) + wl = RB_MIN(winlinks, &s->windows); + if (wl == s->curw) + return (1); + s->lastw = s->curw; + s->curw = wl; + session_cancelbell(s, wl); return (0); } @@ -229,22 +235,19 @@ session_next(struct session *s) int session_previous(struct session *s) { - struct window *w; + struct winlink *wl; - if (s->window == NULL) + if (s->curw == NULL) return (-1); - w = window_previous(&s->windows, s->window); - if (w == NULL) { - w = ARRAY_LAST(&s->windows); - if (w == s->window) - return (1); - } - if (w == s->window) - return (0); - s->last = s->window; - s->window = w; - session_cancelbell(s, w); + wl = winlink_previous(&s->windows, s->curw); + if (wl == NULL) + wl = RB_MAX(winlinks, &s->windows); + if (wl == s->curw) + return (1); + s->lastw = s->curw; + s->curw = wl; + session_cancelbell(s, wl); return (0); } @@ -252,16 +255,16 @@ session_previous(struct session *s) int session_select(struct session *s, u_int i) { - struct window *w; + struct winlink *wl; - w = window_at(&s->windows, i); - if (w == NULL) + wl = winlink_find_by_index(&s->windows, i); + if (wl == NULL) return (-1); - if (w == s->window) - return (0); - s->last = s->window; - s->window = w; - session_cancelbell(s, w); + if (wl == s->curw) + return (1); + s->lastw = s->curw; + s->curw = wl; + session_cancelbell(s, wl); return (0); } @@ -269,16 +272,16 @@ session_select(struct session *s, u_int i) int session_last(struct session *s) { - struct window *w; + struct winlink *wl; - w = s->last; - if (w == NULL) + wl = s->lastw; + if (wl == NULL) return (-1); - if (w == s->window) + if (wl == s->curw) return (1); - s->last = s->window; - s->window = w; - session_cancelbell(s, w); + s->lastw = s->curw; + s->curw = wl; + session_cancelbell(s, wl); return (0); } diff --git a/status.c b/status.c index 3d1758b9..0f1b5680 100644 --- a/status.c +++ b/status.c @@ -1,4 +1,4 @@ -/* $Id: status.c,v 1.6 2007-10-12 12:37:48 nicm Exp $ */ +/* $Id: status.c,v 1.7 2007-10-26 12:29:07 nicm Exp $ */ /* * Copyright (c) 2007 Nicholas Marriott @@ -27,11 +27,10 @@ void status_print(struct buffer *, size_t *, const char *, ...); void status_write(struct client *c) { - struct screen *s = &c->session->window->screen; + struct screen *s = &c->session->curw->window->screen; struct buffer *b = c->out; - struct window *w; + struct winlink *wl; size_t size; - u_int i; char flag; input_store_zero(b, CODE_CURSOROFF); @@ -39,19 +38,16 @@ status_write(struct client *c) input_store_two(b, CODE_ATTRIBUTES, 0, status_colour); size = c->sx; - for (i = 0; i < ARRAY_LENGTH(&c->session->windows); i++) { - w = ARRAY_ITEM(&c->session->windows, i); - if (w == NULL) - continue; - + RB_FOREACH(wl, winlinks, &c->session->windows) { flag = ' '; - if (w == c->session->last) + if (wl == c->session->lastw) flag = '-'; - if (w == c->session->window) + if (wl == c->session->curw) flag = '*'; - if (session_hasbell(c->session, w)) + if (session_hasbell(c->session, wl)) flag = '!'; - status_print(b, &size, "%u:%s%c ", i, w->name, flag); + status_print( + b, &size, "%u:%s%c ", wl->idx, wl->window->name, flag); if (size == 0) break; diff --git a/tmux.h b/tmux.h index fc02963c..be4ed5e1 100644 --- a/tmux.h +++ b/tmux.h @@ -1,4 +1,4 @@ -/* $Id: tmux.h,v 1.70 2007-10-25 17:44:24 nicm Exp $ */ +/* $Id: tmux.h,v 1.71 2007-10-26 12:29:07 nicm Exp $ */ /* * Copyright (c) 2007 Nicholas Marriott @@ -19,6 +19,9 @@ #ifndef TMUX_H #define TMUX_H +/* Shut up gcc warnings about empty if bodies. */ +#define RB_AUGMENT(x) do {} while (0) + #include #include #include @@ -385,9 +388,6 @@ struct input_ctx { ARRAY_DECL(, struct input_arg) args; }; -/* Input context macros. */ -#define INPUT_FLAGS(ictx) ((ictx)->flags) - /* Window structure. */ struct window { char *name; @@ -396,17 +396,26 @@ struct window { struct buffer *in; struct buffer *out; - u_int references; - struct input_ctx ictx; int flags; #define WINDOW_BELL 0x1 struct screen screen; + + u_int references; }; ARRAY_DECL(windows, struct window *); +/* Entry on local window list. */ +struct winlink { + int idx; + struct window *window; + + RB_ENTRY(winlink) entry; +}; +RB_HEAD(winlinks, winlink); + /* Client session. */ struct session { char *name; @@ -415,11 +424,11 @@ struct session { u_int sx; u_int sy; - struct window *window; - struct window *last; - struct windows windows; + struct winlink *curw; + struct winlink *lastw; + struct winlinks windows; - struct windows bells; /* windows with bells */ + ARRAY_DECL(, struct winlink *) bells; /* windows with bells */ #define SESSION_UNATTACHED 0x1 /* not attached to any clients */ int flags; @@ -665,33 +674,34 @@ void local_output(struct buffer *, size_t); /* window.c */ extern struct windows windows; +int window_cmp(struct window *, struct window *); +int winlink_cmp(struct winlink *, struct winlink *); +RB_PROTOTYPE(windows, window, entry, window_cmp); +RB_PROTOTYPE(winlinks, winlink, entry, winlink_cmp); +struct winlink *winlink_find_by_index(struct winlinks *, int); +struct winlink *winlink_find_by_window(struct winlinks *, struct window *); +int winlink_next_index(struct winlinks *); +struct winlink *winlink_add(struct winlinks *, struct window *, int); +void winlink_remove(struct winlinks *, struct winlink *); +struct winlink *winlink_next(struct winlinks *, struct winlink *); +struct winlink *winlink_previous(struct winlinks *, struct winlink *); struct window *window_create( const char *, const char *, const char **, u_int, u_int); -int window_index(struct windows *, struct window *, u_int *); -void window_add(struct windows *, struct window *); -void window_remove(struct windows *, struct window *); void window_destroy(struct window *); -struct window *window_next(struct windows *, struct window *); -struct window *window_previous(struct windows *, struct window *); -struct window *window_at(struct windows *, u_int); int window_resize(struct window *, u_int, u_int); -int window_poll(struct window *, struct pollfd *); -void window_key(struct window *, int); -void window_data(struct window *, struct buffer *); /* session.c */ extern struct sessions sessions; -void session_cancelbell(struct session *, struct window *); +void session_cancelbell(struct session *, struct winlink *); void session_addbell(struct session *, struct window *); -int session_hasbell(struct session *, struct window *); +int session_hasbell(struct session *, struct winlink *); struct session *session_find(const char *); struct session *session_create(const char *, const char *, u_int, u_int); void session_destroy(struct session *); int session_index(struct session *, u_int *); -int session_new( - struct session *, const char *, const char *, u_int *); -void session_attach(struct session *, struct window *); -int session_detach(struct session *, struct window *); +struct winlink *session_new(struct session *, const char *, const char *, int); +struct winlink *session_attach(struct session *, struct window *, int); +int session_detach(struct session *, struct winlink *); int session_has(struct session *, struct window *); int session_next(struct session *); int session_previous(struct session *); diff --git a/window.c b/window.c index 9420dab1..5965b5ed 100644 --- a/window.c +++ b/window.c @@ -1,4 +1,4 @@ -/* $Id: window.c,v 1.23 2007-10-24 15:29:29 nicm Exp $ */ +/* $Id: window.c,v 1.24 2007-10-26 12:29:07 nicm Exp $ */ /* * Copyright (c) 2007 Nicholas Marriott @@ -22,6 +22,7 @@ #include #include #include +#include #include #include #include @@ -47,15 +48,109 @@ * Each window also has a "virtual" screen (screen.c) which contains the * current state and is redisplayed when the window is reattached to a client. * - * A global list of windows is maintained, and a window may also be a member - * of any number of sessions. A reference count is maintained and a window - * removed from the global list and destroyed when it reaches zero. + * Windows are stored directly on a global array and wrapped in any number of + * winlink structs to be linked onto local session RB trees A reference count + * is maintained and a window removed from the global list and destroyed when + * it reaches zero */ /* Global window list. */ -struct windows windows; +struct windows windows; + +RB_GENERATE(winlinks, winlink, entry, winlink_cmp); + +int +winlink_cmp(struct winlink *wl1, struct winlink *wl2) +{ + return (wl1->idx - wl2->idx); +} + +struct winlink * +winlink_find_by_index(struct winlinks *wwl, int idx) +{ + struct winlink wl; + + if (idx < 0) + fatalx("bad index"); + + wl.idx = idx; + return (RB_FIND(winlinks, wwl, &wl)); +} + +int +winlink_next_index(struct winlinks *wwl) +{ + u_int i; + + for (i = 0; i < INT_MAX; i++) { + if (winlink_find_by_index(wwl, i) == NULL) + return (i); + } + + fatalx("no free indexes"); +} + +struct winlink * +winlink_add(struct winlinks *wwl, struct window *w, int idx) +{ + struct winlink *wl; + + if (idx == -1) + idx = winlink_next_index(wwl); + else if (winlink_find_by_index(wwl, idx) != NULL) + return (NULL); + + if (idx < 0) + fatalx("bad index"); + + wl = xcalloc(1, sizeof *wl); + wl->idx = idx; + wl->window = w; + RB_INSERT(winlinks, wwl, wl); + + w->references++; + + return (wl); +} + +void +winlink_remove(struct winlinks *wwl, struct winlink *wl) +{ + struct window *w = wl->window; + + RB_REMOVE(winlinks, wwl, wl); + xfree(wl); + + if (w->references == 0) + fatal("bad reference count"); + w->references--; + if (w->references == 0) + window_destroy(w); +} + +struct winlink * +winlink_next(unused struct winlinks *wwl, struct winlink *wl) +{ + return (RB_NEXT(winlinks, wwl, wl)); +} + +struct winlink * +winlink_previous(struct winlinks *wwl, struct winlink *wl) +{ + struct winlink *wk; + int idx = wl->idx; + + wk = NULL; + wl = RB_MIN(winlinks, wwl); + while (wl != NULL && wl->idx < idx) { + wk = wl; + wl = RB_NEXT(winlinks, wwl, wl); + } + if (wl == NULL) + return (NULL); + return (wk); +} -/* Create a new window. */ struct window * window_create( const char *name, const char *cmd, const char **environ, u_int sx, u_int sy) @@ -92,10 +187,6 @@ window_create( if (fcntl(fd, F_SETFD, FD_CLOEXEC) == -1) fatal("fcntl failed"); - mode = 1; - if (ioctl(fd, TIOCPKT, &mode) == -1) - fatal("ioctl failed"); - w = xmalloc(sizeof *w); w->fd = fd; w->in = buffer_create(BUFSIZ); @@ -126,60 +217,23 @@ window_create( } else w->name = xstrdup(name); - window_add(&windows, w); - w->references = 1; + ARRAY_ADD(&windows, w); + w->references = 0; return (w); } -/* Find window index in list. */ -int -window_index(struct windows *ww, struct window *w, u_int *i) -{ - for (*i = 0; *i < ARRAY_LENGTH(ww); (*i)++) { - if (w == ARRAY_ITEM(ww, *i)) - return (0); - } - return (-1); -} - -/* Add a window to a list. */ -void -window_add(struct windows *ww, struct window *w) -{ - u_int i; - - if (window_index(ww, NULL, &i) != 0) - ARRAY_ADD(ww, w); - else - ARRAY_SET(ww, i, w); - - w->references++; -} - -/* Remove a window from a list. */ -void -window_remove(struct windows *ww, struct window *w) -{ - u_int i; - - if (window_index(ww, w, &i) != 0) - fatalx("window not found"); - ARRAY_SET(ww, i, NULL); - while (!ARRAY_EMPTY(ww) && ARRAY_LAST(ww) == NULL) - ARRAY_TRUNC(ww, 1); - - w->references--; - if (w->references == 0) - window_destroy(w); - if (w->references == 1) - window_remove(&windows, w); -} - -/* Destroy a window. */ void 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); + close(w->fd); input_free(w); @@ -193,55 +247,6 @@ window_destroy(struct window *w) xfree(w); } -/* Locate next window in list. */ -struct window * -window_next(struct windows *ww, struct window *w) -{ - u_int i; - - if (window_index(ww, w, &i) != 0) - fatalx("window not found"); - - if (i == ARRAY_LENGTH(ww) - 1) - return (NULL); - do { - i++; - w = window_at(ww, i); - if (w != NULL) - return (w); - } while (i != ARRAY_LENGTH(ww) - 1); - return (NULL); -} - -/* Locate previous window in list. */ -struct window * -window_previous(struct windows *ww, struct window *w) -{ - u_int i; - - if (window_index(ww, w, &i) != 0) - fatalx("window not found"); - if (i == 0) - return (NULL); - do { - i--; - w = window_at(ww, i); - if (w != NULL) - return (w); - } while (i != 0); - return (NULL); -} - -/* Locate window at specific position in list. */ -struct window * -window_at(struct windows *ww, u_int i) -{ - if (i >= ARRAY_LENGTH(ww)) - return (NULL); - return (ARRAY_ITEM(ww, i)); -} - -/* Resize a window. */ int window_resize(struct window *w, u_int sx, u_int sy) { @@ -261,51 +266,3 @@ window_resize(struct window *w, u_int sx, u_int sy) return (0); } -/* Handle window poll results. This is special because of TIOCPKT. */ -int -window_poll(struct window *w, struct pollfd *pfd) -{ - struct termios tio; - size_t size; - u_char *ptr; - - size = BUFFER_USED(w->in); - if (buffer_poll(pfd, w->in, w->out) != 0) - return (-1); - - if (BUFFER_USED(w->in) == size) - return (0); - ptr = BUFFER_IN(w->in) - (BUFFER_USED(w->in) - size); - - log_debug("window packet: %hhu", *ptr); - switch (*ptr) { - case TIOCPKT_DATA: - case TIOCPKT_FLUSHREAD: - case TIOCPKT_FLUSHWRITE: - case TIOCPKT_STOP: - case TIOCPKT_START: - case TIOCPKT_DOSTOP: - case TIOCPKT_NOSTOP: - buffer_delete_range(w->in, size, 1); - break; - case TIOCPKT_IOCTL: - buffer_delete_range(w->in, size, 1 + sizeof tio); - break; - } - - return (0); -} - -/* Process window key. */ -void -window_key(struct window *w, int key) -{ - input_translate_key(w->out, key); -} - -/* Process output data from child process. */ -void -window_data(struct window *w, struct buffer *b) -{ - input_parse(w, b); -}