aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--buffer-poll.c16
-rw-r--r--server-client.c728
-rw-r--r--server-job.c57
-rw-r--r--server-msg.c279
-rw-r--r--server-window.c281
-rw-r--r--server.c996
-rw-r--r--tmux.h19
7 files changed, 1209 insertions, 1167 deletions
diff --git a/buffer-poll.c b/buffer-poll.c
index 87153d16..63b45f47 100644
--- a/buffer-poll.c
+++ b/buffer-poll.c
@@ -1,4 +1,4 @@
-/* $Id: buffer-poll.c,v 1.17 2009-10-11 23:55:26 tcunha Exp $ */
+/* $Id: buffer-poll.c,v 1.18 2009-10-23 17:49:47 tcunha Exp $ */
/*
* Copyright (c) 2007 Nicholas Marriott <nicm@users.sourceforge.net>
@@ -25,15 +25,15 @@
/* Fill buffers from socket based on poll results. */
int
-buffer_poll(struct pollfd *pfd, struct buffer *in, struct buffer *out)
+buffer_poll(int fd, int events, struct buffer *in, struct buffer *out)
{
ssize_t n;
- if (pfd->revents & (POLLERR|POLLNVAL))
+ if (events & (POLLERR|POLLNVAL))
return (-1);
- if (in != NULL && pfd->revents & POLLIN) {
+ if (in != NULL && events & POLLIN) {
buffer_ensure(in, BUFSIZ);
- n = read(pfd->fd, BUFFER_IN(in), BUFFER_FREE(in));
+ n = read(fd, BUFFER_IN(in), BUFFER_FREE(in));
if (n == 0)
return (-1);
if (n == -1) {
@@ -41,10 +41,10 @@ buffer_poll(struct pollfd *pfd, struct buffer *in, struct buffer *out)
return (-1);
} else
buffer_add(in, n);
- } else if (pfd->revents & POLLHUP)
+ } else if (events & POLLHUP)
return (-1);
- if (out != NULL && BUFFER_USED(out) > 0 && pfd->revents & POLLOUT) {
- n = write(pfd->fd, BUFFER_OUT(out), BUFFER_USED(out));
+ if (out != NULL && BUFFER_USED(out) > 0 && events & POLLOUT) {
+ n = write(fd, BUFFER_OUT(out), BUFFER_USED(out));
if (n == -1) {
if (errno != EINTR && errno != EAGAIN)
return (-1);
diff --git a/server-client.c b/server-client.c
new file mode 100644
index 00000000..f031be14
--- /dev/null
+++ b/server-client.c
@@ -0,0 +1,728 @@
+/* $Id: server-client.c,v 1.1 2009-10-23 17:49:47 tcunha Exp $ */
+
+/*
+ * Copyright (c) 2009 Nicholas Marriott <nicm@users.sourceforge.net>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER
+ * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
+ * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <sys/types.h>
+
+#include <fcntl.h>
+#include <string.h>
+#include <paths.h>
+#include <unistd.h>
+
+#include "tmux.h"
+
+void server_client_handle_data(struct client *);
+void server_client_check_redraw(struct client *);
+void server_client_set_title(struct client *);
+void server_client_check_timers(struct client *);
+
+int server_client_msg_dispatch(struct client *);
+void server_client_msg_command(struct client *, struct msg_command_data *);
+void server_client_msg_identify(
+ struct client *, struct msg_identify_data *, int);
+void server_client_msg_shell(struct client *);
+
+void printflike2 server_client_msg_error(struct cmd_ctx *, const char *, ...);
+void printflike2 server_client_msg_print(struct cmd_ctx *, const char *, ...);
+void printflike2 server_client_msg_info(struct cmd_ctx *, const char *, ...);
+
+
+/* Create a new client. */
+void
+server_client_create(int fd)
+{
+ struct client *c;
+ int mode;
+ u_int i;
+
+ if ((mode = fcntl(fd, F_GETFL)) == -1)
+ fatal("fcntl failed");
+ if (fcntl(fd, F_SETFL, mode|O_NONBLOCK) == -1)
+ fatal("fcntl failed");
+ if (fcntl(fd, F_SETFD, FD_CLOEXEC) == -1)
+ fatal("fcntl failed");
+
+ c = xcalloc(1, sizeof *c);
+ c->references = 0;
+ imsg_init(&c->ibuf, fd);
+
+ if (gettimeofday(&c->tv, NULL) != 0)
+ fatal("gettimeofday failed");
+
+ ARRAY_INIT(&c->prompt_hdata);
+
+ c->tty.fd = -1;
+ c->title = NULL;
+
+ c->session = NULL;
+ c->tty.sx = 80;
+ c->tty.sy = 24;
+
+ screen_init(&c->status, c->tty.sx, 1, 0);
+ job_tree_init(&c->status_jobs);
+
+ c->message_string = NULL;
+
+ c->prompt_string = NULL;
+ c->prompt_buffer = NULL;
+ c->prompt_index = 0;
+
+ for (i = 0; i < ARRAY_LENGTH(&clients); i++) {
+ if (ARRAY_ITEM(&clients, i) == NULL) {
+ ARRAY_SET(&clients, i, c);
+ return;
+ }
+ }
+ ARRAY_ADD(&clients, c);
+ log_debug("new client %d", fd);
+}
+
+/* Lost a client. */
+void
+server_client_lost(struct client *c)
+{
+ u_int i;
+
+ for (i = 0; i < ARRAY_LENGTH(&clients); i++) {
+ if (ARRAY_ITEM(&clients, i) == c)
+ ARRAY_SET(&clients, i, NULL);
+ }
+ log_debug("lost client %d", c->ibuf.fd);
+
+ /*
+ * If CLIENT_TERMINAL hasn't been set, then tty_init hasn't been called
+ * and tty_free might close an unrelated fd.
+ */
+ if (c->flags & CLIENT_TERMINAL)
+ tty_free(&c->tty);
+
+ screen_free(&c->status);
+ job_tree_free(&c->status_jobs);
+
+ if (c->title != NULL)
+ xfree(c->title);
+
+ if (c->message_string != NULL)
+ xfree(c->message_string);
+
+ if (c->prompt_string != NULL)
+ xfree(c->prompt_string);
+ if (c->prompt_buffer != NULL)
+ xfree(c->prompt_buffer);
+ for (i = 0; i < ARRAY_LENGTH(&c->prompt_hdata); i++)
+ xfree(ARRAY_ITEM(&c->prompt_hdata, i));
+ ARRAY_FREE(&c->prompt_hdata);
+
+ if (c->cwd != NULL)
+ xfree(c->cwd);
+
+ close(c->ibuf.fd);
+ imsg_clear(&c->ibuf);
+
+ for (i = 0; i < ARRAY_LENGTH(&dead_clients); i++) {
+ if (ARRAY_ITEM(&dead_clients, i) == NULL) {
+ ARRAY_SET(&dead_clients, i, c);
+ break;
+ }
+ }
+ if (i == ARRAY_LENGTH(&dead_clients))
+ ARRAY_ADD(&dead_clients, c);
+ c->flags |= CLIENT_DEAD;
+
+ recalculate_sizes();
+}
+
+/* Process a single client event. */
+void
+server_client_callback(int fd, int events, void *data)
+{
+ struct client *c = data;
+
+ if (fd == c->ibuf.fd) {
+ if (events & (POLLERR|POLLNVAL|POLLHUP))
+ goto client_lost;
+
+ if (events & POLLOUT && msgbuf_write(&c->ibuf.w) < 0)
+ goto client_lost;
+
+ if (c->flags & CLIENT_BAD) {
+ if (c->ibuf.w.queued == 0)
+ goto client_lost;
+ return;
+ }
+
+ if (events & POLLIN && server_client_msg_dispatch(c) != 0)
+ goto client_lost;
+ }
+
+ if (c->tty.fd != -1 && fd == c->tty.fd) {
+ if (c->flags & CLIENT_SUSPENDED || c->session == NULL)
+ return;
+
+ if (buffer_poll(fd, events, c->tty.in, c->tty.out) != 0)
+ goto client_lost;
+ server_client_handle_data(c);
+ }
+
+ return;
+
+client_lost:
+ server_client_lost(c);
+}
+
+/* Input data from client. */
+void
+server_client_handle_data(struct client *c)
+{
+ struct window *w;
+ struct window_pane *wp;
+ struct screen *s;
+ struct options *oo;
+ struct timeval tv;
+ struct key_binding *bd;
+ struct keylist *keylist;
+ struct mouse_event mouse;
+ int key, status, xtimeout, mode, isprefix;
+ u_int i;
+
+ xtimeout = options_get_number(&c->session->options, "repeat-time");
+ if (xtimeout != 0 && c->flags & CLIENT_REPEAT) {
+ if (gettimeofday(&tv, NULL) != 0)
+ fatal("gettimeofday failed");
+ if (timercmp(&tv, &c->repeat_timer, >))
+ c->flags &= ~(CLIENT_PREFIX|CLIENT_REPEAT);
+ }
+
+ /* Process keys. */
+ keylist = options_get_data(&c->session->options, "prefix");
+ while (tty_keys_next(&c->tty, &key, &mouse) == 0) {
+ if (c->session == NULL)
+ return;
+
+ c->session->activity = time(NULL);
+ w = c->session->curw->window;
+ wp = w->active; /* could die */
+ oo = &c->session->options;
+
+ /* Special case: number keys jump to pane in identify mode. */
+ if (c->flags & CLIENT_IDENTIFY && key >= '0' && key <= '9') {
+ wp = window_pane_at_index(w, key - '0');
+ if (wp != NULL && window_pane_visible(wp))
+ window_set_active_pane(w, wp);
+ server_clear_identify(c);
+ continue;
+ }
+
+ status_message_clear(c);
+ server_clear_identify(c);
+ if (c->prompt_string != NULL) {
+ status_prompt_key(c, key);
+ continue;
+ }
+
+ /* Check for mouse keys. */
+ if (key == KEYC_MOUSE) {
+ if (options_get_number(oo, "mouse-select-pane")) {
+ window_set_active_at(w, mouse.x, mouse.y);
+ wp = w->active;
+ }
+ window_pane_mouse(wp, c, &mouse);
+ continue;
+ }
+
+ /* Is this a prefix key? */
+ isprefix = 0;
+ for (i = 0; i < ARRAY_LENGTH(keylist); i++) {
+ if (key == ARRAY_ITEM(keylist, i)) {
+ isprefix = 1;
+ break;
+ }
+ }
+
+ /* No previous prefix key. */
+ if (!(c->flags & CLIENT_PREFIX)) {
+ if (isprefix)
+ c->flags |= CLIENT_PREFIX;
+ else {
+ /* Try as a non-prefix key binding. */
+ if ((bd = key_bindings_lookup(key)) == NULL)
+ window_pane_key(wp, c, key);
+ else
+ key_bindings_dispatch(bd, c);
+ }
+ continue;
+ }
+
+ /* Prefix key already pressed. Reset prefix and lookup key. */
+ c->flags &= ~CLIENT_PREFIX;
+ if ((bd = key_bindings_lookup(key | KEYC_PREFIX)) == NULL) {
+ /* If repeating, treat this as a key, else ignore. */
+ if (c->flags & CLIENT_REPEAT) {
+ c->flags &= ~CLIENT_REPEAT;
+ if (isprefix)
+ c->flags |= CLIENT_PREFIX;
+ else
+ window_pane_key(wp, c, key);
+ }
+ continue;
+ }
+
+ /* If already repeating, but this key can't repeat, skip it. */
+ if (c->flags & CLIENT_REPEAT && !bd->can_repeat) {
+ c->flags &= ~CLIENT_REPEAT;
+ if (isprefix)
+ c->flags |= CLIENT_PREFIX;
+ else
+ window_pane_key(wp, c, key);
+ continue;
+ }
+
+ /* If this key can repeat, reset the repeat flags and timer. */
+ if (xtimeout != 0 && bd->can_repeat) {
+ c->flags |= CLIENT_PREFIX|CLIENT_REPEAT;
+
+ tv.tv_sec = xtimeout / 1000;
+ tv.tv_usec = (xtimeout % 1000) * 1000L;
+ if (gettimeofday(&c->repeat_timer, NULL) != 0)
+ fatal("gettimeofday failed");
+ timeradd(&c->repeat_timer, &tv, &c->repeat_timer);
+ }
+
+ /* Dispatch the command. */
+ key_bindings_dispatch(bd, c);
+ }
+ if (c->session == NULL)
+ return;
+ w = c->session->curw->window;
+ wp = w->active;
+ oo = &c->session->options;
+ s = wp->screen;
+
+ /*
+ * Update cursor position and mode settings. The scroll region and
+ * attributes are cleared across poll(2) as this is the most likely
+ * time a user may interrupt tmux, for example with ~^Z in ssh(1). This
+ * is a compromise between excessive resets and likelihood of an
+ * interrupt.
+ *
+ * tty_region/tty_reset/tty_update_mode already take care of not
+ * resetting things that are already in their default state.
+ */
+ tty_region(&c->tty, 0, c->tty.sy - 1);
+
+ status = options_get_number(oo, "status");
+ if (!window_pane_visible(wp) || wp->yoff + s->cy >= c->tty.sy - status)
+ tty_cursor(&c->tty, 0, 0);
+ else
+ tty_cursor(&c->tty, wp->xoff + s->cx, wp->yoff + s->cy);
+
+ mode = s->mode;
+ if (TAILQ_NEXT(TAILQ_FIRST(&w->panes), entry) != NULL &&
+ options_get_number(oo, "mouse-select-pane"))
+ mode |= MODE_MOUSE;
+ tty_update_mode(&c->tty, mode);
+ tty_reset(&c->tty);
+}
+
+/* Client functions that need to happen every loop. */
+void
+server_client_loop(void)
+{
+ struct client *c;
+ struct window *w;
+ struct window_pane *wp;
+ u_int i;
+
+ for (i = 0; i < ARRAY_LENGTH(&clients); i++) {
+ c = ARRAY_ITEM(&clients, i);
+ if (c == NULL || c->session == NULL)
+ continue;
+
+ server_client_check_timers(c);
+ server_client_check_redraw(c);
+ }
+
+ /*
+ * Any windows will have been redrawn as part of clients, so clear
+ * their flags now.
+ */
+ for (i = 0; i < ARRAY_LENGTH(&windows); i++) {
+ w = ARRAY_ITEM(&windows, i);
+ if (w == NULL)
+ continue;
+
+ w->flags &= ~WINDOW_REDRAW;
+ TAILQ_FOREACH(wp, &w->panes, entry)
+ wp->flags &= ~PANE_REDRAW;
+ }
+}
+
+/* Check for client redraws. */
+void
+server_client_check_redraw(struct client *c)
+{
+ struct session *s = c->session;
+ struct window_pane *wp;
+ int flags, redraw;
+
+ flags = c->tty.flags & TTY_FREEZE;
+ c->tty.flags &= ~TTY_FREEZE;
+
+ if (c->flags & (CLIENT_REDRAW|CLIENT_STATUS)) {
+ if (options_get_number(&s->options, "set-titles"))
+ server_client_set_title(c);
+
+ if (c->message_string != NULL)
+ redraw = status_message_redraw(c);
+ else if (c->prompt_string != NULL)
+ redraw = status_prompt_redraw(c);
+ else
+ redraw = status_redraw(c);
+ if (!redraw)
+ c->flags &= ~CLIENT_STATUS;
+ }
+
+ if (c->flags & CLIENT_REDRAW) {
+ screen_redraw_screen(c, 0);
+ c->flags &= ~CLIENT_STATUS;
+ } else {
+ TAILQ_FOREACH(wp, &c->session->curw->window->panes, entry) {
+ if (wp->flags & PANE_REDRAW)
+ screen_redraw_pane(c, wp);
+ }
+ }
+
+ if (c->flags & CLIENT_STATUS)
+ screen_redraw_screen(c, 1);
+
+ c->tty.flags |= flags;
+
+ c->flags &= ~(CLIENT_REDRAW|CLIENT_STATUS);
+}
+
+/* Set client title. */
+void
+server_client_set_title(struct client *c)
+{
+ struct session *s = c->session;
+ const char *template;
+ char *title;
+
+ template = options_get_string(&s->options, "set-titles-string");
+
+ title = status_replace(c, template, time(NULL));
+ if (c->title == NULL || strcmp(title, c->title) != 0) {
+ if (c->title != NULL)
+ xfree(c->title);
+ c->title = xstrdup(title);
+ tty_set_title(&c->tty, c->title);
+ }
+ xfree(title);
+}
+
+/* Check client timers. */
+void
+server_client_check_timers(struct client *c)
+{
+ struct session *s = c->session;
+ struct job *job;
+ struct timeval tv;
+ u_int interval;
+
+ if (gettimeofday(&tv, NULL) != 0)
+ fatal("gettimeofday failed");
+
+ if (c->flags & CLIENT_IDENTIFY && timercmp(&tv, &c->identify_timer, >))
+ server_clear_identify(c);
+
+ if (c->message_string != NULL && timercmp(&tv, &c->message_timer, >))
+ status_message_clear(c);
+
+ if (c->message_string != NULL || c->prompt_string != NULL) {
+ /*
+ * Don't need timed redraw for messages/prompts so bail now.
+ * The status timer isn't reset when they are redrawn anyway.
+ */
+ return;
+
+ }
+ if (!options_get_number(&s->options, "status"))
+ return;
+
+ /* Check timer; resolution is only a second so don't be too clever. */
+ interval = options_get_number(&s->options, "status-interval");
+ if (interval == 0)
+ return;
+ if (tv.tv_sec < c->status_timer.tv_sec ||
+ ((u_int) tv.tv_sec) - c->status_timer.tv_sec >= interval) {
+ /* Run the jobs for this client and schedule for redraw. */
+ RB_FOREACH(job, jobs, &c->status_jobs)
+ job_run(job);
+ c->flags |= CLIENT_STATUS;
+ }
+}
+
+/* Dispatch message from client. */
+int
+server_client_msg_dispatch(struct client *c)
+{
+ struct imsg imsg;
+ struct msg_command_data commanddata;
+ struct msg_identify_data identifydata;
+ struct msg_environ_data environdata;
+ ssize_t n, datalen;
+
+ if ((n = imsg_read(&c->ibuf)) == -1 || n == 0)
+ return (-1);
+
+ for (;;) {
+ if ((n = imsg_get(&c->ibuf, &imsg)) == -1)
+ return (-1);
+ if (n == 0)
+ return (0);
+ datalen = imsg.hdr.len - IMSG_HEADER_SIZE;
+
+ if (imsg.hdr.peerid != PROTOCOL_VERSION) {
+ server_write_client(c, MSG_VERSION, NULL, 0);
+ c->flags |= CLIENT_BAD;
+ imsg_free(&imsg);
+ continue;
+ }
+
+ log_debug("got %d from client %d", imsg.hdr.type, c->ibuf.fd);
+ switch (imsg.hdr.type) {
+ case MSG_COMMAND:
+ if (datalen != sizeof commanddata)
+ fatalx("bad MSG_COMMAND size");
+ memcpy(&commanddata, imsg.data, sizeof commanddata);
+
+ server_client_msg_command(c, &commanddata);
+ break;
+ case MSG_IDENTIFY:
+ if (datalen != sizeof identifydata)
+ fatalx("bad MSG_IDENTIFY size");
+ if (imsg.fd == -1)
+ fatalx("MSG_IDENTIFY missing fd");
+ memcpy(&identifydata, imsg.data, sizeof identifydata);
+
+ server_client_msg_identify(c, &identifydata, imsg.fd);
+ break;
+ case MSG_RESIZE:
+ if (datalen != 0)
+ fatalx("bad MSG_RESIZE size");
+
+ tty_resize(&c->tty);
+ recalculate_sizes();
+ server_redraw_client(c);
+ break;
+ case MSG_EXITING:
+ if (datalen != 0)
+ fatalx("bad MSG_EXITING size");
+
+ c->session = NULL;
+ tty_close(&c->tty);
+ server_write_client(c, MSG_EXITED, NULL, 0);
+ break;
+ case MSG_WAKEUP:
+ case MSG_UNLOCK:
+ if (datalen != 0)
+ fatalx("bad MSG_WAKEUP size");
+
+ if (!(c->flags & CLIENT_SUSPENDED))
+ break;
+ c->flags &= ~CLIENT_SUSPENDED;
+ tty_start_tty(&c->tty);
+ server_redraw_client(c);
+ recalculate_sizes();
+ if (c->session != NULL)
+ c->session->activity = time(NULL);
+ break;
+ case MSG_ENVIRON:
+ if (datalen != sizeof environdata)
+ fatalx("bad MSG_ENVIRON size");
+ memcpy(&environdata, imsg.data, sizeof environdata);
+
+ environdata.var[(sizeof environdata.var) - 1] = '\0';
+ if (strchr(environdata.var, '=') != NULL)
+ environ_put(&c->environ, environdata.var);
+ break;
+ case MSG_SHELL:
+ if (datalen != 0)
+ fatalx("bad MSG_SHELL size");
+
+ server_client_msg_shell(c);
+ break;
+ default:
+ fatalx("unexpected message");
+ }
+
+ imsg_free(&imsg);
+ }
+}
+
+/* Callback to send error message to client. */
+void printflike2
+server_client_msg_error(struct cmd_ctx *ctx, const char *fmt, ...)
+{
+ struct msg_print_data data;
+ va_list ap;
+
+ va_start(ap, fmt);
+ xvsnprintf(data.msg, sizeof data.msg, fmt, ap);
+ va_end(ap);
+
+ server_write_client(ctx->cmdclient, MSG_ERROR, &data, sizeof data);
+}
+
+/* Callback to send print message to client. */
+void printflike2
+server_client_msg_print(struct cmd_ctx *ctx, const char *fmt, ...)
+{
+ struct msg_print_data data;
+ va_list ap;
+
+ va_start(ap, fmt);
+ xvsnprintf(data.msg, sizeof data.msg, fmt, ap);
+ va_end(ap);
+
+ server_write_client(ctx->cmdclient, MSG_PRINT, &data, sizeof data);
+}
+
+/* Callback to send print message to client, if not quiet. */
+void printflike2
+server_client_msg_info(struct cmd_ctx *ctx, const char *fmt, ...)
+{
+ struct msg_print_data data;
+ va_list ap;
+
+ if (be_quiet)
+ return;
+
+ va_start(ap, fmt);
+ xvsnprintf(data.msg, sizeof data.msg, fmt, ap);
+ va_end(ap);
+
+ server_write_client(ctx->cmdclient, MSG_PRINT, &data, sizeof data);
+}
+
+/* Handle command message. */
+void
+server_client_msg_command(struct client *c, struct msg_command_data *data)
+{
+ struct cmd_ctx ctx;
+ struct cmd_list *cmdlist = NULL;
+ struct cmd *cmd;
+ int argc;
+ char **argv, *cause;
+
+ if (c->session != NULL)
+ c->session->activity = time(NULL);
+
+ ctx.error = server_client_msg_error;
+ ctx.print = server_client_msg_print;
+ ctx.info = server_client_msg_info;
+
+ ctx.msgdata = data;
+ ctx.curclient = NULL;
+
+ ctx.cmdclient = c;
+
+ argc = data->argc;
+ data->argv[(sizeof data->argv) - 1] = '\0';
+ if (cmd_unpack_argv(data->argv, sizeof data->argv, argc, &argv) != 0) {
+ server_client_msg_error(&ctx, "command too long");
+ goto error;
+ }
+
+ if (argc == 0) {
+ argc = 1;
+ argv = xcalloc(1, sizeof *argv);
+ *argv = xstrdup("new-session");
+ }
+
+ if ((cmdlist = cmd_list_parse(argc, argv, &cause)) == NULL) {
+ server_client_msg_error(&ctx, "%s", cause);
+ cmd_free_argv(argc, argv);
+ goto error;
+ }
+ cmd_free_argv(argc, argv);
+
+ if (data->pid != -1) {
+ TAILQ_FOREACH(cmd, cmdlist, qentry) {
+ if (cmd->entry->flags & CMD_CANTNEST) {
+ server_client_msg_error(&ctx,
+ "sessions should be nested with care. "
+ "unset $TMUX to force");
+ goto error;
+ }
+ }
+ }
+
+ if (cmd_list_exec(cmdlist, &ctx) != 1)
+ server_write_client(c, MSG_EXIT, NULL, 0);
+ cmd_list_free(cmdlist);
+ return;
+
+error:
+ if (cmdlist != NULL)
+ cmd_list_free(cmdlist);
+ server_write_client(c, MSG_EXIT, NULL, 0);
+}
+
+/* Handle identify message. */
+void
+server_client_msg_identify(
+ struct client *c, struct msg_identify_data *data, int fd)
+{
+ c->cwd = NULL;
+ data->cwd[(sizeof data->cwd) - 1] = '\0';
+ if (*data->cwd != '\0')
+ c->cwd = xstrdup(data->cwd);
+
+ data->term[(sizeof data->term) - 1] = '\0';
+ tty_init(&c->tty, fd, data->term);
+ if (data->flags & IDENTIFY_UTF8)
+ c->tty.flags |= TTY_UTF8;
+ if (data->flags & IDENTIFY_256COLOURS)
+ c->tty.term_flags |= TERM_256COLOURS;
+ else if (data->flags & IDENTIFY_88COLOURS)
+ c->tty.term_flags |= TERM_88COLOURS;
+ if (data->flags & IDENTIFY_HASDEFAULTS)
+ c->tty.term_flags |= TERM_HASDEFAULTS;
+
+ tty_resize(&c->tty);
+
+ c->flags |= CLIENT_TERMINAL;
+}
+
+/* Handle shell message. */
+void
+server_client_msg_shell(struct client *c)
+{
+ struct msg_shell_data data;
+ const char *shell;
+
+ shell = options_get_string(&global_s_options, "default-shell");
+
+ if (*shell == '\0' || areshell(shell))
+ shell = _PATH_BSHELL;
+ if (strlcpy(data.shell, shell, sizeof data.shell) >= sizeof data.shell)
+ strlcpy(data.shell, _PATH_BSHELL, sizeof data.shell);
+
+ server_write_client(c, MSG_SHELL, &data, sizeof data);
+ c->flags |= CLIENT_BAD; /* it will die after exec */
+}
diff --git a/server-job.c b/server-job.c
new file mode 100644
index 00000000..a3d4d0dd
--- /dev/null
+++ b/server-job.c
@@ -0,0 +1,57 @@
+/* $Id: server-job.c,v 1.1 2009-10-23 17:49:47 tcunha Exp $ */
+
+/*
+ * Copyright (c) 2009 Nicholas Marriott <nicm@users.sourceforge.net>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER
+ * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
+ * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <sys/types.h>
+
+#include <unistd.h>
+
+#include "tmux.h"
+
+/* Process a single job event. */
+void
+server_job_callback(int fd, int events, void *data)
+{
+ struct job *job = data;
+
+ if (job->fd == -1)
+ return;
+
+ if (buffer_poll(fd, events, job->out, NULL) != 0) {
+ close(job->fd);
+ job->fd = -1;
+ }
+}
+
+/* Job functions that happen once a loop. */
+void
+server_job_loop(void)
+{
+ struct job *job;
+
+restart:
+ SLIST_FOREACH(job, &all_jobs, lentry) {
+ if (job->flags & JOB_DONE || job->fd != -1 || job->pid != -1)
+ continue;
+ job->flags |= JOB_DONE;
+
+ if (job->callbackfn != NULL) {
+ job->callbackfn(job);
+ goto restart; /* could be freed by callback */
+ }
+ }
+}
diff --git a/server-msg.c b/server-msg.c
deleted file mode 100644
index 3c01d7cc..00000000
--- a/server-msg.c
+++ /dev/null
@@ -1,279 +0,0 @@
-/* $Id: server-msg.c,v 1.92 2009-10-23 17:33:26 tcunha Exp $ */
-
-/*
- * Copyright (c) 2007 Nicholas Marriott <nicm@users.sourceforge.net>
- *
- * Permission to use, copy, modify, and distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER
- * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
- * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#include <sys/types.h>
-#include <sys/ioctl.h>
-
-#include <errno.h>
-#include <stdlib.h>
-#include <string.h>
-#include <time.h>
-#include <unistd.h>
-
-#include "tmux.h"
-
-void server_msg_command(struct client *, struct msg_command_data *);
-void server_msg_identify(struct client *, struct msg_identify_data *, int);
-void server_msg_shell(struct client *);
-
-void printflike2 server_msg_command_error(struct cmd_ctx *, const char *, ...);
-void printflike2 server_msg_command_print(struct cmd_ctx *, const char *, ...);
-void printflike2 server_msg_command_info(struct cmd_ctx *, const char *, ...);
-
-int
-server_msg_dispatch(struct client *c)
-{
- struct imsg imsg;
- struct msg_command_data commanddata;
- struct msg_identify_data identifydata;
- struct msg_environ_data environdata;
- ssize_t n, datalen;
-
- if ((n = imsg_read(&c->ibuf)) == -1 || n == 0)
- return (-1);
-
- for (;;) {
- if ((n = imsg_get(&c->ibuf, &imsg)) == -1)
- return (-1);
- if (n == 0)
- return (0);
- datalen = imsg.hdr.len - IMSG_HEADER_SIZE;
-
- if (imsg.hdr.peerid != PROTOCOL_VERSION) {
- server_write_client(c, MSG_VERSION, NULL, 0);
- c->flags |= CLIENT_BAD;
- imsg_free(&imsg);
- continue;
- }
-
- log_debug("got %d from client %d", imsg.hdr.type, c->ibuf.fd);
- switch (imsg.hdr.type) {
- case MSG_COMMAND:
- if (datalen != sizeof commanddata)
- fatalx("bad MSG_COMMAND size");
- memcpy(&commanddata, imsg.data, sizeof commanddata);
-
- server_msg_command(c, &commanddata);
- break;
- case MSG_IDENTIFY:
- if (datalen != sizeof identifydata)
- fatalx("bad MSG_IDENTIFY size");
- if (imsg.fd == -1)
- fatalx("MSG_IDENTIFY missing fd");
- memcpy(&identifydata, imsg.data, sizeof identifydata);
-
- server_msg_identify(c, &identifydata, imsg.fd);
- break;
- case MSG_RESIZE:
- if (datalen != 0)
- fatalx("bad MSG_RESIZE size");
-
- tty_resize(&c->tty);
- recalculate_sizes();
- server_redraw_client(c);
- break;
- case MSG_EXITING:
- if (datalen != 0)
- fatalx("bad MSG_EXITING size");
-
- c->session = NULL;
- tty_close(&c->tty);
- server_write_client(c, MSG_EXITED, NULL, 0);
- break;
- case MSG_WAKEUP:
- case MSG_UNLOCK:
- if (datalen != 0)
- fatalx("bad MSG_WAKEUP size");
-
- if (!(c->flags & CLIENT_SUSPENDED))
- break;
- c->flags &= ~CLIENT_SUSPENDED;
- tty_start_tty(&c->tty);
- server_redraw_client(c);
- recalculate_sizes();
- if (c->session != NULL)
- c->session->activity = time(NULL);
- break;
- case MSG_ENVIRON:
- if (datalen != sizeof environdata)
- fatalx("bad MSG_ENVIRON size");
- memcpy(&environdata, imsg.data, sizeof environdata);
-
- environdata.var[(sizeof environdata.var) - 1] = '\0';
- if (strchr(environdata.var, '=') != NULL)
- environ_put(&c->environ, environdata.var);
- break;
- case MSG_SHELL:
- if (datalen != 0)
- fatalx("bad MSG_SHELL size");
-
- server_msg_shell(c);
- break;
- default:
- fatalx("unexpected message");
- }
-
- imsg_free(&imsg);
- }
-}
-
-void printflike2
-server_msg_command_error(struct cmd_ctx *ctx, const char *fmt, ...)
-{
- struct msg_print_data data;
- va_list ap;
-
- va_start(ap, fmt);
- xvsnprintf(data.msg, sizeof data.msg, fmt, ap);
- va_end(ap);
-
- server_write_client(ctx->cmdclient, MSG_ERROR, &data, sizeof data);
-}
-
-void printflike2
-server_msg_command_print(struct cmd_ctx *ctx, const char *fmt, ...)
-{
- struct msg_print_data data;
- va_list ap;
-
- va_start(ap, fmt);
- xvsnprintf(data.msg, sizeof data.msg, fmt, ap);
- va_end(ap);
-
- server_write_client(ctx->cmdclient, MSG_PRINT, &data, sizeof data);
-}
-
-void printflike2
-server_msg_command_info(struct cmd_ctx *ctx, const char *fmt, ...)
-{
- struct msg_print_data data;
- va_list ap;
-
- if (be_quiet)
- return;
-
- va_start(ap, fmt);
- xvsnprintf(data.msg, sizeof data.msg, fmt, ap);
- va_end(ap);
-
- server_write_client(ctx->cmdclient, MSG_PRINT, &data, sizeof data);
-}
-
-void
-server_msg_command(struct client *c, struct msg_command_data *data)
-{
- struct cmd_ctx ctx;
- struct cmd_list *cmdlist = NULL;
- struct cmd *cmd;
- int argc;
- char **argv, *cause;
-
- if (c->session != NULL)
- c->session->activity = time(NULL);
-
- ctx.error = server_msg_command_error;
- ctx.print = server_msg_command_print;
- ctx.info = server_msg_command_info;
-
- ctx.msgdata = data;
- ctx.curclient = NULL;
-
- ctx.cmdclient = c;
-
- argc = data->argc;
- data->argv[(sizeof data->argv) - 1] = '\0';
- if (cmd_unpack_argv(data->argv, sizeof data->argv, argc, &argv) != 0) {
- server_msg_command_error(&ctx, "command too long");
- goto error;
- }
-
- if (argc == 0) {
- argc = 1;
- argv = xcalloc(1, sizeof *argv);
- *argv = xstrdup("new-session");
- }
-
- if ((cmdlist = cmd_list_parse(argc, argv, &cause)) == NULL) {
- server_msg_command_error(&ctx, "%s", cause);
- cmd_free_argv(argc, argv);
- goto error;
- }
- cmd_free_argv(argc, argv);
-
- if (data->pid != -1) {
- TAILQ_FOREACH(cmd, cmdlist, qentry) {
- if (cmd->entry->flags & CMD_CANTNEST) {
- server_msg_command_error(&ctx,
- "sessions should be nested with care. "
- "unset $TMUX to force");
- goto error;
- }
- }
- }
-
- if (cmd_list_exec(cmdlist, &ctx) != 1)
- server_write_client(c, MSG_EXIT, NULL, 0);
- cmd_list_free(cmdlist);
- return;
-
-error:
- if (cmdlist != NULL)
- cmd_list_free(cmdlist);
- server_write_client(c, MSG_EXIT, NULL, 0);
-}
-
-void
-server_msg_identify(struct client *c, struct msg_identify_data *data, int fd)
-{
- c->cwd = NULL;
- data->cwd[(sizeof data->cwd) - 1] = '\0';
- if (*data->cwd != '\0')
- c->cwd = xstrdup(data->cwd);
-
- data->term[(sizeof data->term) - 1] = '\0';
- tty_init(&c->tty, fd, data->term);
- if (data->flags & IDENTIFY_UTF8)
- c->tty.flags |= TTY_UTF8;
- if (data->flags & IDENTIFY_256COLOURS)
- c->tty.term_flags |= TERM_256COLOURS;
- else if (data->flags & IDENTIFY_88COLOURS)
- c->tty.term_flags |= TERM_88COLOURS;
- if (data->flags & IDENTIFY_HASDEFAULTS)
- c->tty.term_flags |= TERM_HASDEFAULTS;
-
- tty_resize(&c->tty);
-
- c->flags |= CLIENT_TERMINAL;
-}
-
-void
-server_msg_shell(struct client *c)
-{
- struct msg_shell_data data;
- const char *shell;
-
- shell = options_get_string(&global_s_options, "default-shell");
-
- if (*shell == '\0' || areshell(shell))
- shell = _PATH_BSHELL;
- if (strlcpy(data.shell, shell, sizeof data.shell) >= sizeof data.shell)
- strlcpy(data.shell, _PATH_BSHELL, sizeof data.shell);
-
- server_write_client(c, MSG_SHELL, &data, sizeof data);
- c->flags |= CLIENT_BAD; /* it will die after exec */
-}
diff --git a/server-window.c b/server-window.c
new file mode 100644
index 00000000..fe554afd
--- /dev/null
+++ b/server-window.c
@@ -0,0 +1,281 @@
+/* $Id: server-window.c,v 1.1 2009-10-23 17:49:47 tcunha Exp $ */
+
+/*
+ * Copyright (c) 2009 Nicholas Marriott <nicm@users.sourceforge.net>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER
+ * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
+ * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <sys/types.h>
+
+#include <unistd.h>
+
+#include "tmux.h"
+
+int server_window_check_bell(struct session *, struct window *);
+int server_window_check_activity(struct session *, struct window *);
+int server_window_check_content(
+ struct session *, struct window *, struct window_pane *);
+void server_window_check_alive(struct window *);
+
+/* Process a single window pane event. */
+void
+server_window_callback(int fd, int events, void *data)
+{
+ struct window_pane *wp = data;
+
+ if (wp->fd == -1)
+ return;
+
+ if (fd == wp->fd) {
+ if (buffer_poll(fd, events, wp->in, wp->out) != 0) {
+ close(wp->fd);
+ wp->fd = -1;
+ } else
+ window_pane_parse(wp);
+ }
+
+ if (fd == wp->pipe_fd) {
+ if (buffer_poll(fd, events, NULL, wp->pipe_buf) != 0) {
+ buffer_destroy(wp->pipe_buf);
+ close(wp->pipe_fd);
+ wp->pipe_fd = -1;
+ }
+ }
+}
+
+/* Window functions that need to happen every loop. */
+void
+server_window_loop(void)
+{
+ struct window *w;
+ struct window_pane *wp;
+ struct session *s;
+ u_int i, j;
+
+ for (i = 0; i < ARRAY_LENGTH(&windows); i++) {
+ w = ARRAY_ITEM(&windows, i);
+ if (w == NULL)
+ continue;
+
+ for (j = 0; j < ARRAY_LENGTH(&sessions); j++) {
+ s = ARRAY_ITEM(&sessions, j);
+ if (s == NULL || !session_has(s, w))
+ continue;
+
+ if (server_window_check_bell(s, w) ||
+ server_window_check_activity(s, w))
+ server_status_session(s);
+ TAILQ_FOREACH(wp, &w->panes, entry)
+ server_window_check_content(s, w, wp);
+ }
+ w->flags &= ~(WINDOW_BELL|WINDOW_ACTIVITY|WINDOW_CONTENT);
+
+ server_window_check_alive(w);
+ }
+
+ set_window_names();
+}
+
+/* Check for bell in window. */
+int
+server_window_check_bell(struct session *s, struct window *w)
+{
+ struct client *c;
+ u_int i;
+ int action, visual;
+
+ if (!(w->flags & WINDOW_BELL))
+ return (0);
+
+ if (session_alert_has_window(s, w, WINDOW_BELL))
+ return (0);
+ session_alert_add(s, w, WINDOW_BELL);
+
+ action = options_get_number(&s->options, "bell-action");
+ switch (action) {
+ case BELL_ANY:
+ if (s->flags & SESSION_UNATTACHED)
+ break;
+ visual = options_get_number(&s->options, "visual-bell");
+ for (i = 0; i < ARRAY_LENGTH(&clients); i++) {
+ c = ARRAY_ITEM(&clients, i);
+ if (c == NULL || c->session != s)
+ continue;
+ if (!visual) {
+ tty_putcode(&c->tty, TTYC_BEL);
+ continue;
+ }
+ if (c->session->curw->window == w) {
+ status_message_set(c, "Bell in current window");
+ continue;
+ }
+ status_message_set(c, "Bell in window %u",
+ winlink_find_by_window(&s->windows, w)->idx);
+ }
+ break;
+ case BELL_CURRENT:
+ if (s->flags & SESSION_UNATTACHED)
+ break;
+ visual = options_get_number(&s->options, "visual-bell");
+ for (i = 0; i < ARRAY_LENGTH(&clients); i++) {
+ c = ARRAY_ITEM(&clients, i);
+ if (c == NULL || c->session != s)
+ continue;
+ if (c->session->curw->window != w)
+ continue;
+ if (!visual) {
+ tty_putcode(&c->tty, TTYC_BEL);
+ continue;
+ }
+ status_message_set(c, "Bell in current window");
+ }
+ break;
+ }
+
+ return (1);
+}
+
+/* Check for activity in window. */
+int
+server_window_check_activity(struct session *s, struct window *w)
+{
+ struct client *c;
+ u_int i;
+
+ if (!(w->flags & WINDOW_ACTIVITY))
+ return (0);
+ if (s->curw->window == w)
+ return (0);
+
+ if (!options_get_number(&w->options, "monitor-activity"))
+ return (0);
+
+ if (session_alert_has_window(s, w, WINDOW_ACTIVITY))
+ return (0);
+ session_alert_add(s, w, WINDOW_ACTIVITY);
+
+ if (s->flags & SESSION_UNATTACHED)
+ return (0);
+ if (options_get_number(&s->options, "visual-activity")) {
+ for (i = 0; i < ARRAY_LENGTH(&clients); i++) {
+ c = ARRAY_ITEM(&clients, i);
+ if (c == NULL || c->session != s)
+ continue;
+ status_message_set(c, "Activity in window %u",
+ winlink_find_by_window(&s->windows, w)->idx);
+ }
+ }
+
+ return (1);
+}
+
+/* Check for content change in window. */
+int
+server_window_check_content(
+ struct session *s, struct window *w, struct window_pane *wp)
+{
+ struct client *c;
+ u_int i;
+ char *found, *ptr;
+
+ if (!(w->flags & WINDOW_ACTIVITY)) /* activity for new content */
+ return (0);
+ if (s->curw->window == w)
+ return (0);
+
+ ptr = options_get_string(&w->options, "monitor-content");
+ if (ptr == NULL || *ptr == '\0')
+ return (0);
+
+ if (session_alert_has_window(s, w, WINDOW_CONTENT))
+ return (0);
+
+ if ((found = window_pane_search(wp, ptr, NULL)) == NULL)
+ return (0);
+ xfree(found);
+
+ session_alert_add(s, w, WINDOW_CONTENT);
+ if (s->flags & SESSION_UNATTACHED)
+ return (0);
+ if (options_get_number(&s->options, "visual-content")) {
+ for (i = 0; i < ARRAY_LENGTH(&clients); i++) {
+ c = ARRAY_ITEM(&clients, i);
+ if (c == NULL || c->session != s)
+ continue;
+ status_message_set(c, "Content in window %u",
+ winlink_find_by_window(&s->windows, w)->idx);
+ }
+ }
+
+ return (1);
+}
+
+/* Check if window still exists. */
+void
+server_window_check_alive(struct window *w)
+{
+ struct window_pane *wp, *wq;
+ struct options *oo = &w->options;
+ struct session *s;
+ struct winlink *wl;
+ u_int i;
+ int destroyed;
+
+ destroyed = 1;
+
+ wp = TAILQ_FIRST(&w->panes);
+ while (wp != NULL) {
+ wq = TAILQ_NEXT(wp, entry);
+ /*
+ * If the pane has died and the remain-on-exit flag is not set,
+ * remove the pane; otherwise, if the flag is set, don't allow
+ * the window to be destroyed (or it'll close when the last
+ * pane dies).
+ */
+ if (wp->fd == -1 && !options_get_number(oo, "remain-on-exit")) {
+ layout_close_pane(wp);
+ window_remove_pane(w, wp);
+ server_redraw_window(w);
+ } else
+ destroyed = 0;
+ wp = wq;
+ }
+
+ if (!destroyed)
+ return;
+
+ for (i = 0; i < ARRAY_LENGTH(&sessions); i++) {
+ s = ARRAY_ITEM(&sessions, i);
+ if (s == NULL)
+ continue;
+ if (!session_has(s, w))
+ continue;
+
+ restart:
+ /* Detach window and either redraw or kill clients. */
+ RB_FOREACH(wl, winlinks, &s->windows) {
+ if (wl->window != w)
+ continue;
+ if (session_detach(s, wl)) {
+ server_destroy_session_group(s);
+ break;
+ }
+ server_redraw_session(s);
+ server_status_session_group(s);
+ goto restart;
+ }
+ }
+
+ recalculate_sizes();
+}
diff --git a/server.c b/server.c
index f59c1664..c2daaf59 100644
--- a/server.c
+++ b/server.c
@@ -1,4 +1,4 @@
-/* $Id: server.c,v 1.210 2009-10-15 01:43:16 tcunha Exp $ */
+/* $Id: server.c,v 1.211 2009-10-23 17:49:47 tcunha Exp $ */
/*
* Copyright (c) 2007 Nicholas Marriott <nicm@users.sourceforge.net>
@@ -46,80 +46,70 @@ struct clients dead_clients;
/* Mapping of a pollfd to an fd independent of its position in the array. */
struct poll_item {
- struct pollfd pfd;
+ int fd;
+ int events;
+
+ void (*fn)(int, int, void *);
+ void *data;
RB_ENTRY(poll_item) entry;
};
RB_HEAD(poll_items, poll_item) poll_items;
int server_poll_cmp(struct poll_item *, struct poll_item *);
-struct pollfd *server_poll_lookup(int);
-void server_poll_add(int, int);
+struct poll_item*server_poll_lookup(int);
+void server_poll_add(int, int, void (*)(int, int, void *), void *);
struct pollfd *server_poll_flatten(int *);
-void server_poll_parse(struct pollfd *);
+void server_poll_dispatch(struct pollfd *, int);
void server_poll_reset(void);
RB_PROTOTYPE(poll_items, poll_item, entry, server_poll_cmp);
RB_GENERATE(poll_items, poll_item, entry, server_poll_cmp);
-void server_create_client(int);
int server_create_socket(void);
+void server_callback(int, int, void *);
int server_main(int);
void server_shutdown(void);
int server_should_shutdown(void);
void server_child_signal(void);
void server_fill_windows(void);
-void server_handle_windows(void);
void server_fill_clients(void);
-void server_handle_clients(void);
void server_fill_jobs(void);
-void server_handle_jobs(void);
-void server_accept_client(int);
-void server_handle_client(struct client *);
-void server_handle_window(struct window *, struct window_pane *);
-int server_check_window_bell(struct session *, struct window *);
-int server_check_window_activity(struct session *,
- struct window *);
-int server_check_window_content(struct session *, struct window *,
- struct window_pane *);
void server_clean_dead(void);
-void server_lost_client(struct client *);
-void server_check_window(struct window *);
-void server_check_redraw(struct client *);
-void server_set_title(struct client *);
-void server_check_timers(struct client *);
-void server_check_jobs(void);
+void server_second_timers(void);
void server_lock_server(void);
void server_lock_sessions(void);
-void server_check_clients(void);
-void server_second_timers(void);
int server_update_socket(void);
int
server_poll_cmp(struct poll_item *pitem1, struct poll_item *pitem2)
{
- return (pitem1->pfd.fd - pitem2->pfd.fd);
-}
-
-struct pollfd *
-server_poll_lookup(int fd)
-{
- struct poll_item pitem;
-
- pitem.pfd.fd = fd;
- return (&RB_FIND(poll_items, &poll_items, &pitem)->pfd);
+ return (pitem1->fd - pitem2->fd);
}
void
-server_poll_add(int fd, int events)
+server_poll_add(int fd, int events, void (*fn)(int, int, void *), void *data)
{
struct poll_item *pitem;
pitem = xmalloc(sizeof *pitem);
- pitem->pfd.fd = fd;
- pitem->pfd.events = events;
+ pitem->fd = fd;
+ pitem->events = events;
+
+ pitem->fn = fn;
+ pitem->data = data;
+
RB_INSERT(poll_items, &poll_items, pitem);
}
+struct poll_item *
+server_poll_lookup(int fd)
+{
+ struct poll_item pitem;
+
+ pitem.fd = fd;
+ return (RB_FIND(poll_items, &poll_items, &pitem));
+}
+
struct pollfd *
server_poll_flatten(int *nfds)
{
@@ -130,23 +120,25 @@ server_poll_flatten(int *nfds)
*nfds = 0;
RB_FOREACH(pitem, poll_items, &poll_items) {
pfds = xrealloc(pfds, (*nfds) + 1, sizeof *pfds);
- pfds[*nfds].fd = pitem->pfd.fd;
- pfds[*nfds].events = pitem->pfd.events;
+ pfds[*nfds].fd = pitem->fd;
+ pfds[*nfds].events = pitem->events;
(*nfds)++;
}
return (pfds);
}
void
-server_poll_parse(struct pollfd *pfds)
+server_poll_dispatch(struct pollfd *pfds, int nfds)
{
struct poll_item *pitem;
- int nfds;
+ struct pollfd *pfd;
- nfds = 0;
- RB_FOREACH(pitem, poll_items, &poll_items) {
- pitem->pfd.revents = pfds[nfds].revents;
- nfds++;
+ while (nfds > 0) {
+ pfd = &pfds[--nfds];
+ if (pfd->revents != 0) {
+ pitem = server_poll_lookup(pfd->fd);
+ pitem->fn(pitem->fd, pfd->revents, pitem->data);
+ }
}
xfree(pfds);
}
@@ -163,13 +155,34 @@ server_poll_reset(void)
}
}
-/* Create a new client. */
-void
-server_create_client(int fd)
+/* Create server socket. */
+int
+server_create_socket(void)
{
- struct client *c;
- int mode;
- u_int i;
+ struct sockaddr_un sa;
+ size_t size;
+ mode_t mask;
+ int fd, mode;
+
+ memset(&sa, 0, sizeof sa);
+ sa.sun_family = AF_UNIX;
+ size = strlcpy(sa.sun_path, socket_path, sizeof sa.sun_path);
+ if (size >= sizeof sa.sun_path) {
+ errno = ENAMETOOLONG;
+ fatal("socket failed");
+ }
+ unlink(sa.sun_path);
+
+ if ((fd = socket(AF_UNIX, SOCK_STREAM, 0)) == -1)
+ fatal("socket failed");
+
+ mask = umask(S_IXUSR|S_IRWXG|S_IRWXO);
+ if (bind(fd, (struct sockaddr *) &sa, SUN_LEN(&sa)) == -1)
+ fatal("bind failed");
+ umask(mask);
+
+ if (listen(fd, 16) == -1)
+ fatal("listen failed");
if ((mode = fcntl(fd, F_GETFL)) == -1)
fatal("fcntl failed");
@@ -178,39 +191,33 @@ server_create_client(int fd)
if (fcntl(fd, F_SETFD, FD_CLOEXEC) == -1)
fatal("fcntl failed");
- c = xcalloc(1, sizeof *c);
- c->references = 0;
- imsg_init(&c->ibuf, fd);
-
- if (gettimeofday(&c->tv, NULL) != 0)
- fatal("gettimeofday failed");
-
- ARRAY_INIT(&c->prompt_hdata);
-
- c->tty.fd = -1;
- c->title = NULL;
-
- c->session = NULL;
- c->tty.sx = 80;
- c->tty.sy = 24;
-
- screen_init(&c->status, c->tty.sx, 1, 0);
- job_tree_init(&c->status_jobs);
+ return (fd);
+}
- c->message_string = NULL;
+/* Callback for server socket. */
+void
+server_callback(int fd, int events, unused void *data)
+{
+ struct sockaddr_storage sa;
+ socklen_t slen = sizeof sa;
+ int newfd;
- c->prompt_string = NULL;
- c->prompt_buffer = NULL;
- c->prompt_index = 0;
+ if (events & (POLLERR|POLLNVAL|POLLHUP))
+ fatalx("lost server socket");
+ if (!(events & POLLIN))
+ return;
- for (i = 0; i < ARRAY_LENGTH(&clients); i++) {
- if (ARRAY_ITEM(&clients, i) == NULL) {
- ARRAY_SET(&clients, i, c);
+ newfd = accept(fd, (struct sockaddr *) &sa, &slen);
+ if (newfd == -1) {
+ if (errno == EAGAIN || errno == EINTR || errno == ECONNABORTED)
return;
- }
+ fatal("accept failed");
}
- ARRAY_ADD(&clients, c);
- log_debug("new client %d", fd);
+ if (sigterm) {
+ close(newfd);
+ return;
+ }
+ server_client_create(newfd);
}
/* Fork new server. */
@@ -270,7 +277,7 @@ server_start(char *path)
#endif
srv_fd = server_create_socket();
- server_create_client(pair[1]);
+ server_client_create(pair[1]);
if (access(SYSTEM_CFG, R_OK) != 0) {
if (errno != ENOENT) {
@@ -298,50 +305,11 @@ error:
exit(server_main(srv_fd));
}
-/* Create server socket. */
-int
-server_create_socket(void)
-{
- struct sockaddr_un sa;
- size_t size;
- mode_t mask;
- int fd, mode;
-
- memset(&sa, 0, sizeof sa);
- sa.sun_family = AF_UNIX;
- size = strlcpy(sa.sun_path, socket_path, sizeof sa.sun_path);
- if (size >= sizeof sa.sun_path) {
- errno = ENAMETOOLONG;
- fatal("socket failed");
- }
- unlink(sa.sun_path);
-
- if ((fd = socket(AF_UNIX, SOCK_STREAM, 0)) == -1)
- fatal("socket failed");
-
- mask = umask(S_IXUSR|S_IRWXG|S_IRWXO);
- if (bind(fd, (struct sockaddr *) &sa, SUN_LEN(&sa)) == -1)
- fatal("bind failed");
- umask(mask);
-
- if (listen(fd, 16) == -1)
- fatal("listen failed");
-
- if ((mode = fcntl(fd, F_GETFL)) == -1)
- fatal("fcntl failed");
- if (fcntl(fd, F_SETFL, mode|O_NONBLOCK) == -1)
- fatal("fcntl failed");
- if (fcntl(fd, F_SETFD, FD_CLOEXEC) == -1)
- fatal("fcntl failed");
-
- return (fd);
-}
-
/* Main server loop. */
int
server_main(int srv_fd)
{
- struct pollfd *pfds, *pfd;
+ struct pollfd *pfds;
int nfds, xtimeout;
u_int i;
time_t now, last;
@@ -374,13 +342,9 @@ server_main(int srv_fd)
sigusr1 = 0;
}
- /* Collect any jobs that have died and process clients. */
- server_check_jobs();
- server_check_clients();
-
/* Initialise pollfd array and add server socket. */
server_poll_reset();
- server_poll_add(srv_fd, POLLIN);
+ server_poll_add(srv_fd, POLLIN, server_callback, NULL);
/* Fill window and client sockets. */
server_fill_jobs();
@@ -399,16 +363,7 @@ server_main(int srv_fd)
continue;
fatal("poll failed");
}
- server_poll_parse(pfds);
-
- /* Handle server socket. */
- pfd = server_poll_lookup(srv_fd);
- if (pfd->revents & (POLLERR|POLLNVAL|POLLHUP))
- fatalx("lost server socket");
- if (pfd->revents & POLLIN) {
- server_accept_client(srv_fd);
- continue;
- }
+ server_poll_dispatch(pfds, nfds);
/* Call second-based timers. */
now = time(NULL);
@@ -417,13 +372,10 @@ server_main(int srv_fd)
server_second_timers();
}
- /* Set window names. */
- set_window_names();
-
- /* Handle window and client sockets. */
- server_handle_jobs();
- server_handle_windows();
- server_handle_clients();
+ /* Run once-per-loop events. */
+ server_job_loop();
+ server_window_loop();
+ server_client_loop();
/* Collect any unset key bindings. */
key_bindings_clean();
@@ -441,7 +393,7 @@ server_main(int srv_fd)
for (i = 0; i < ARRAY_LENGTH(&clients); i++) {
if (ARRAY_ITEM(&clients, i) != NULL)
- server_lost_client(ARRAY_ITEM(&clients, i));
+ server_client_lost(ARRAY_ITEM(&clients, i));
}
ARRAY_FREE(&clients);
@@ -471,7 +423,7 @@ server_shutdown(void)
c = ARRAY_ITEM(&clients, i);
if (c != NULL) {
if (c->flags & (CLIENT_BAD|CLIENT_SUSPENDED))
- server_lost_client(c);
+ server_client_lost(c);
else
server_write_client(c, MSG_SHUTDOWN, NULL, 0);
}
@@ -574,193 +526,18 @@ server_fill_windows(void)
events = POLLIN;
if (BUFFER_USED(wp->out) > 0)
events |= POLLOUT;
- server_poll_add(wp->fd, events);
+ server_poll_add(
+ wp->fd, events, server_window_callback, wp);
if (wp->pipe_fd == -1)
continue;
events = 0;
if (BUFFER_USED(wp->pipe_buf) > 0)
events |= POLLOUT;
- server_poll_add(wp->pipe_fd, events);
- }
- }
-}
-
-/* Handle window pollfds. */
-void
-server_handle_windows(void)
-{
- struct window *w;
- struct window_pane *wp;
- struct pollfd *pfd;
- u_int i;
-
- for (i = 0; i < ARRAY_LENGTH(&windows); i++) {
- w = ARRAY_ITEM(&windows, i);
- if (w == NULL)
- continue;
-
- TAILQ_FOREACH(wp, &w->panes, entry) {
- if (wp->fd == -1)
- continue;
- if ((pfd = server_poll_lookup(wp->fd)) == NULL)
- continue;
- if (buffer_poll(pfd, wp->in, wp->out) != 0) {
- close(wp->fd);
- wp->fd = -1;
- } else
- server_handle_window(w, wp);
-
- if (wp->pipe_fd == -1)
- continue;
- if ((pfd = server_poll_lookup(wp->pipe_fd)) == NULL)
- continue;
- if (buffer_poll(pfd, NULL, wp->pipe_buf) != 0) {
- buffer_destroy(wp->pipe_buf);
- close(wp->pipe_fd);
- wp->pipe_fd = -1;
- }
- }
-
- server_check_window(w);
- }
-}
-
-/* Check clients for redraw and timers. */
-void
-server_check_clients(void)
-{
- struct client *c;
- struct window *w;
- struct window_pane *wp;
- u_int i;
-
- for (i = 0; i < ARRAY_LENGTH(&clients); i++) {
- c = ARRAY_ITEM(&clients, i);
- if (c == NULL || c->session == NULL)
- continue;
-
- server_check_timers(c);
- server_check_redraw(c);
- }
-
- /*
- * Clear any window redraw flags (will have been redrawn as part of
- * client).
- */
- for (i = 0; i < ARRAY_LENGTH(&windows); i++) {
- w = ARRAY_ITEM(&windows, i);
- if (w == NULL)
- continue;
-
- w->flags &= ~WINDOW_REDRAW;
- TAILQ_FOREACH(wp, &w->panes, entry)
- wp->flags &= ~PANE_REDRAW;
- }
-}
-
-/* Check for general redraw on client. */
-void
-server_check_redraw(struct client *c)
-{
- struct session *s = c->session;
- struct window_pane *wp;
- int flags, redraw;
-
- flags = c->tty.flags & TTY_FREEZE;
- c->tty.flags &= ~TTY_FREEZE;
-
- if (c->flags & (CLIENT_REDRAW|CLIENT_STATUS)) {
- if (options_get_number(&s->options, "set-titles"))
- server_set_title(c);
-
- if (c->message_string != NULL)
- redraw = status_message_redraw(c);
- else if (c->prompt_string != NULL)
- redraw = status_prompt_redraw(c);
- else
- redraw = status_redraw(c);
- if (!redraw)
- c->flags &= ~CLIENT_STATUS;
- }
-
- if (c->flags & CLIENT_REDRAW) {
- screen_redraw_screen(c, 0);
- c->flags &= ~CLIENT_STATUS;
- } else {
- TAILQ_FOREACH(wp, &c->session->curw->window->panes, entry) {
- if (wp->flags & PANE_REDRAW)
- screen_redraw_pane(c, wp);
+ server_poll_add(
+ wp->pipe_fd, events, server_window_callback, wp);
}
}
-
- if (c->flags & CLIENT_STATUS)
- screen_redraw_screen(c, 1);
-
- c->tty.flags |= flags;
-
- c->flags &= ~(CLIENT_REDRAW|CLIENT_STATUS);
-}
-
-/* Set client title. */
-void
-server_set_title(struct client *c)
-{
- struct session *s = c->session;
- const char *template;
- char *title;
-
- template = options_get_string(&s->options, "set-titles-string");
-
- title = status_replace(c, template, time(NULL));
- if (c->title == NULL || strcmp(title, c->title) != 0) {
- if (c->title != NULL)
- xfree(c->title);
- c->title = xstrdup(title);
- tty_set_title(&c->tty, c->title);
- }
- xfree(title);
-}
-
-/* Check for timers on client. */
-void
-server_check_timers(struct client *c)
-{
- struct session *s = c->session;
- struct job *job;
- struct timeval tv;
- u_int interval;
-
- if (gettimeofday(&tv, NULL) != 0)
- fatal("gettimeofday failed");
-
- if (c->flags & CLIENT_IDENTIFY && timercmp(&tv, &c->identify_timer, >))
- server_clear_identify(c);
-
- if (c->message_string != NULL && timercmp(&tv, &c->message_timer, >))
- status_message_clear(c);
-
- if (c->message_string != NULL || c->prompt_string != NULL) {
- /*
- * Don't need timed redraw for messages/prompts so bail now.
- * The status timer isn't reset when they are redrawn anyway.
- */
- return;
- }
- if (!options_get_number(&s->options, "status"))
- return;
-
- /* Check timer; resolution is only a second so don't be too clever. */
- interval = options_get_number(&s->options, "status-interval");
- if (interval == 0)
- return;
- if (tv.tv_sec < c->status_timer.tv_sec ||
- ((u_int) tv.tv_sec) - c->status_timer.tv_sec >= interval) {
- /* Run the jobs for this client and schedule for redraw. */
- RB_FOREACH(job, jobs, &c->status_jobs)
- job_run(job);
- c->flags |= CLIENT_STATUS;
- }
}
/* Fill client pollfds. */
@@ -780,7 +557,8 @@ server_fill_clients(void)
events |= POLLIN;
if (c->ibuf.w.queued > 0)
events |= POLLOUT;
- server_poll_add(c->ibuf.fd, events);
+ server_poll_add(
+ c->ibuf.fd, events, server_client_callback, c);
}
if (c != NULL && !(c->flags & CLIENT_SUSPENDED) &&
@@ -788,7 +566,8 @@ server_fill_clients(void)
events = POLLIN;
if (BUFFER_USED(c->tty.out) > 0)
events |= POLLOUT;
- server_poll_add(c->tty.fd, events);
+ server_poll_add(
+ c->tty.fd, events, server_client_callback, c);
}
}
}
@@ -802,328 +581,10 @@ server_fill_jobs(void)
SLIST_FOREACH(job, &all_jobs, lentry) {
if (job->fd == -1)
continue;
- server_poll_add(job->fd, POLLIN);
+ server_poll_add(job->fd, POLLIN, server_job_callback, job);
}
}
-/* Handle job fds. */
-void
-server_handle_jobs(void)
-{
- struct job *job;
- struct pollfd *pfd;
-
- SLIST_FOREACH(job, &all_jobs, lentry) {
- if (job->fd == -1)
- continue;
- if ((pfd = server_poll_lookup(job->fd)) == NULL)
- continue;
- if (buffer_poll(pfd, job->out, NULL) != 0) {
- close(job->fd);
- job->fd = -1;
- }
- }
-}
-
-/* Handle job fds. */
-void
-server_check_jobs(void)
-{
- struct job *job;
-
-restart:
- SLIST_FOREACH(job, &all_jobs, lentry) {
- if (job->flags & JOB_DONE || job->fd != -1 || job->pid != -1)
- continue;
- job->flags |= JOB_DONE;
-
- if (job->callbackfn != NULL) {
- job->callbackfn(job);
- goto restart; /* could be freed by callback */
- }
- }
-}
-
-/* Handle client pollfds. */
-void
-server_handle_clients(void)
-{
- struct client *c;
- struct pollfd *pfd;
- u_int i;
-
- for (i = 0; i < ARRAY_LENGTH(&clients); i++) {
- c = ARRAY_ITEM(&clients, i);
-
- if (c != NULL) {
- if ((pfd = server_poll_lookup(c->ibuf.fd)) == NULL)
- continue;
- if (pfd->revents & (POLLERR|POLLNVAL|POLLHUP)) {
- server_lost_client(c);
- continue;
- }
-
- if (pfd->revents & POLLOUT) {
- if (msgbuf_write(&c->ibuf.w) < 0) {
- server_lost_client(c);
- continue;
- }
- }
-
- if (c->flags & CLIENT_BAD) {
- if (c->ibuf.w.queued == 0)
- server_lost_client(c);
- continue;
- } else if (pfd->revents & POLLIN) {
- if (server_msg_dispatch(c) != 0) {
- server_lost_client(c);
- continue;
- }
- }
- }
-
- if (c != NULL && !(c->flags & CLIENT_SUSPENDED) &&
- c->tty.fd != -1 && c->session != NULL) {
- if ((pfd = server_poll_lookup(c->tty.fd)) == NULL)
- continue;
- if (buffer_poll(pfd, c->tty.in, c->tty.out) != 0)
- server_lost_client(c);
- else
- server_handle_client(c);
- }
- }
-}
-
-/* accept(2) and create new client. */
-void
-server_accept_client(int srv_fd)
-{
- struct sockaddr_storage sa;
- socklen_t slen = sizeof sa;
- int fd;
-
- fd = accept(srv_fd, (struct sockaddr *) &sa, &slen);
- if (fd == -1) {
- if (errno == EAGAIN || errno == EINTR || errno == ECONNABORTED)
- return;
- fatal("accept failed");
- }
- if (sigterm) {
- close(fd);
- return;
- }
- server_create_client(fd);
-}
-
-/* Input data from client. */
-void
-server_handle_client(struct client *c)
-{
- struct window *w;
- struct window_pane *wp;
- struct screen *s;
- struct options *oo;
- struct timeval tv;
- struct key_binding *bd;
- struct keylist *keylist;
- struct mouse_event mouse;
- int key, status, xtimeout, mode, isprefix;
- u_int i;
-
- xtimeout = options_get_number(&c->session->options, "repeat-time");
- if (xtimeout != 0 && c->flags & CLIENT_REPEAT) {
- if (gettimeofday(&tv, NULL) != 0)
- fatal("gettimeofday failed");
- if (timercmp(&tv, &c->repeat_timer, >))
- c->flags &= ~(CLIENT_PREFIX|CLIENT_REPEAT);
- }
-
- /* Process keys. */
- keylist = options_get_data(&c->session->options, "prefix");
- while (tty_keys_next(&c->tty, &key, &mouse) == 0) {
- if (c->session == NULL)
- return;
-
- c->session->activity = time(NULL);
- w = c->session->curw->window;
- wp = w->active; /* could die */
- oo = &c->session->options;
-
- /* Special case: number keys jump to pane in identify mode. */
- if (c->flags & CLIENT_IDENTIFY && key >= '0' && key <= '9') {
- wp = window_pane_at_index(w, key - '0');
- if (wp != NULL && window_pane_visible(wp))
- window_set_active_pane(w, wp);
- server_clear_identify(c);
- continue;
- }
-
- status_message_clear(c);
- server_clear_identify(c);
- if (c->prompt_string != NULL) {
- status_prompt_key(c, key);
- continue;
- }
-
- /* Check for mouse keys. */
- if (key == KEYC_MOUSE) {
- if (options_get_number(oo, "mouse-select-pane")) {
- window_set_active_at(w, mouse.x, mouse.y);
- wp = w->active;
- }
- window_pane_mouse(wp, c, &mouse);
- continue;
- }
-
- /* Is this a prefix key? */
- isprefix = 0;
- for (i = 0; i < ARRAY_LENGTH(keylist); i++) {
- if (key == ARRAY_ITEM(keylist, i)) {
- isprefix = 1;
- break;
- }
- }
-
- /* No previous prefix key. */
- if (!(c->flags & CLIENT_PREFIX)) {
- if (isprefix)
- c->flags |= CLIENT_PREFIX;
- else {
- /* Try as a non-prefix key binding. */
- if ((bd = key_bindings_lookup(key)) == NULL)
- window_pane_key(wp, c, key);
- else
- key_bindings_dispatch(bd, c);
- }
- continue;
- }
-
- /* Prefix key already pressed. Reset prefix and lookup key. */
- c->flags &= ~CLIENT_PREFIX;
- if ((bd = key_bindings_lookup(key | KEYC_PREFIX)) == NULL) {
- /* If repeating, treat this as a key, else ignore. */
- if (c->flags & CLIENT_REPEAT) {
- c->flags &= ~CLIENT_REPEAT;
- if (isprefix)
- c->flags |= CLIENT_PREFIX;
- else
- window_pane_key(wp, c, key);
- }
- continue;
- }
-
- /* If already repeating, but this key can't repeat, skip it. */
- if (c->flags & CLIENT_REPEAT && !bd->can_repeat) {
- c->flags &= ~CLIENT_REPEAT;
- if (isprefix)
- c->flags |= CLIENT_PREFIX;
- else
- window_pane_key(wp, c, key);
- continue;
- }
-
- /* If this key can repeat, reset the repeat flags and timer. */
- if (xtimeout != 0 && bd->can_repeat) {
- c->flags |= CLIENT_PREFIX|CLIENT_REPEAT;
-
- tv.tv_sec = xtimeout / 1000;
- tv.tv_usec = (xtimeout % 1000) * 1000L;
- if (gettimeofday(&c->repeat_timer, NULL) != 0)
- fatal("gettimeofday failed");
- timeradd(&c->repeat_timer, &tv, &c->repeat_timer);
- }
-
- /* Dispatch the command. */
- key_bindings_dispatch(bd, c);
- }
- if (c->session == NULL)
- return;
- w = c->session->curw->window;
- wp = w->active;
- oo = &c->session->options;
- s = wp->screen;
-
- /*
- * Update cursor position and mode settings. The scroll region and
- * attributes are cleared across poll(2) as this is the most likely
- * time a user may interrupt tmux, for example with ~^Z in ssh(1). This
- * is a compromise between excessive resets and likelihood of an
- * interrupt.
- *
- * tty_region/tty_reset/tty_update_mode already take care of not
- * resetting things that are already in their default state.
- */
- tty_region(&c->tty, 0, c->tty.sy - 1);
-
- status = options_get_number(oo, "status");
- if (!window_pane_visible(wp) || wp->yoff + s->cy >= c->tty.sy - status)
- tty_cursor(&c->tty, 0, 0);
- else
- tty_cursor(&c->tty, wp->xoff + s->cx, wp->yoff + s->cy);
-
- mode = s->mode;
- if (TAILQ_NEXT(TAILQ_FIRST(&w->panes), entry) != NULL &&
- options_get_number(oo, "mouse-select-pane"))
- mode |= MODE_MOUSE;
- tty_update_mode(&c->tty, mode);
- tty_reset(&c->tty);
-}
-
-/* Lost a client. */
-void
-server_lost_client(struct client *c)
-{
- u_int i;
-
- for (i = 0; i < ARRAY_LENGTH(&clients); i++) {
- if (ARRAY_ITEM(&clients, i) == c)
- ARRAY_SET(&clients, i, NULL);
- }
- log_debug("lost client %d", c->ibuf.fd);
-
- /*
- * If CLIENT_TERMINAL hasn't been set, then tty_init hasn't been called
- * and tty_free might close an unrelated fd.
- */
- if (c->flags & CLIENT_TERMINAL)
- tty_free(&c->tty);
-
- screen_free(&c->status);
- job_tree_free(&c->status_jobs);
-
- if (c->title != NULL)
- xfree(c->title);
-
- if (c->message_string != NULL)
- xfree(c->message_string);
-
- if (c->prompt_string != NULL)
- xfree(c->prompt_string);
- if (c->prompt_buffer != NULL)
- xfree(c->prompt_buffer);
- for (i = 0; i < ARRAY_LENGTH(&c->prompt_hdata); i++)
- xfree(ARRAY_ITEM(&c->prompt_hdata, i));
- ARRAY_FREE(&c->prompt_hdata);
-
- if (c->cwd != NULL)
- xfree(c->cwd);
-
- close(c->ibuf.fd);
- imsg_clear(&c->ibuf);
-
- for (i = 0; i < ARRAY_LENGTH(&dead_clients); i++) {
- if (ARRAY_ITEM(&dead_clients, i) == NULL) {
- ARRAY_SET(&dead_clients, i, c);
- break;
- }
- }
- if (i == ARRAY_LENGTH(&dead_clients))
- ARRAY_ADD(&dead_clients, c);
- c->flags |= CLIENT_DEAD;
-
- recalculate_sizes();
-}
-
/* Free dead, unreferenced clients and sessions. */
void
server_clean_dead(void)
@@ -1149,221 +610,29 @@ server_clean_dead(void)
}
}
-/* Handle window data. */
-void
-server_handle_window(struct window *w, struct window_pane *wp)
-{
- struct session *s;
- u_int i;
- int update;
-
- window_pane_parse(wp);
-
- if ((w->flags & (WINDOW_BELL|WINDOW_ACTIVITY|WINDOW_CONTENT)) == 0)
- return;
-
- update = 0;
- for (i = 0; i < ARRAY_LENGTH(&sessions); i++) {
- s = ARRAY_ITEM(&sessions, i);
- if (s == NULL || !session_has(s, w))
- continue;
-
- update += server_check_window_bell(s, w);
- update += server_check_window_activity(s, w);
- update += server_check_window_content(s, w, wp);
- }
- if (update)
- server_status_window(w);
-
- w->flags &= ~(WINDOW_BELL|WINDOW_ACTIVITY|WINDOW_CONTENT);
-}
-
-int
-server_check_window_bell(struct session *s, struct window *w)
-{
- struct client *c;
- u_int i;
- int action, visual;
-
- if (!(w->flags & WINDOW_BELL))
- return (0);
- if (session_alert_has_window(s, w, WINDOW_BELL))
- return (0);
- session_alert_add(s, w, WINDOW_BELL);
-
- action = options_get_number(&s->options, "bell-action");
- switch (action) {
- case BELL_ANY:
- if (s->flags & SESSION_UNATTACHED)
- break;
- visual = options_get_number(&s->options, "visual-bell");
- for (i = 0; i < ARRAY_LENGTH(&clients); i++) {
- c = ARRAY_ITEM(&clients, i);
- if (c == NULL || c->session != s)
- continue;
- if (!visual) {
- tty_putcode(&c->tty, TTYC_BEL);
- continue;
- }
- if (c->session->curw->window == w) {
- status_message_set(c, "Bell in current window");
- continue;
- }
- status_message_set(c, "Bell in window %u",
- winlink_find_by_window(&s->windows, w)->idx);
- }
- break;
- case BELL_CURRENT:
- if (s->flags & SESSION_UNATTACHED)
- break;
- visual = options_get_number(&s->options, "visual-bell");
- for (i = 0; i < ARRAY_LENGTH(&clients); i++) {
- c = ARRAY_ITEM(&clients, i);
- if (c == NULL || c->session != s)
- continue;
- if (c->session->curw->window != w)
- continue;
- if (!visual) {
- tty_putcode(&c->tty, TTYC_BEL);
- continue;
- }
- status_message_set(c, "Bell in current window");
- }
- break;
- }
- return (1);
-}
-
-int
-server_check_window_activity(struct session *s, struct window *w)
-{
- struct client *c;
- u_int i;
-
- if (!(w->flags & WINDOW_ACTIVITY))
- return (0);
-
- if (!options_get_number(&w->options, "monitor-activity"))
- return (0);
-
- if (session_alert_has_window(s, w, WINDOW_ACTIVITY))
- return (0);
- if (s->curw->window == w)
- return (0);
-
- session_alert_add(s, w, WINDOW_ACTIVITY);
- if (s->flags & SESSION_UNATTACHED)
- return (0);
- if (options_get_number(&s->options, "visual-activity")) {
- for (i = 0; i < ARRAY_LENGTH(&clients); i++) {
- c = ARRAY_ITEM(&clients, i);
- if (c == NULL || c->session != s)
- continue;
- status_message_set(c, "Activity in window %u",
- winlink_find_by_window(&s->windows, w)->idx);
- }
- }
-
- return (1);
-}
-
-int
-server_check_window_content(
- struct session *s, struct window *w, struct window_pane *wp)
-{
- struct client *c;
- u_int i;
- char *found, *ptr;
-
- if (!(w->flags & WINDOW_ACTIVITY)) /* activity for new content */
- return (0);
-
- ptr = options_get_string(&w->options, "monitor-content");
- if (ptr == NULL || *ptr == '\0')
- return (0);
-
- if (session_alert_has_window(s, w, WINDOW_CONTENT))
- return (0);
- if (s->curw->window == w)
- return (0);
-
- if ((found = window_pane_search(wp, ptr, NULL)) == NULL)
- return (0);
- xfree(found);
-
- session_alert_add(s, w, WINDOW_CONTENT);
- if (s->flags & SESSION_UNATTACHED)
- return (0);
- if (options_get_number(&s->options, "visual-content")) {
- for (i = 0; i < ARRAY_LENGTH(&clients); i++) {
- c = ARRAY_ITEM(&clients, i);
- if (c == NULL || c->session != s)
- continue;
- status_message_set(c, "Content in window %u",
- winlink_find_by_window(&s->windows, w)->idx);
- }
- }
-
- return (1);
-}
-
-/* Check if window still exists. */
+/* Call any once-per-second timers. */
void
-server_check_window(struct window *w)
+server_second_timers(void)
{
- struct window_pane *wp, *wq;
- struct options *oo = &w->options;
- struct session *s;
- struct winlink *wl;
+ struct window *w;
+ struct window_pane *wp;
u_int i;
- int destroyed;
-
- destroyed = 1;
-
- wp = TAILQ_FIRST(&w->panes);
- while (wp != NULL) {
- wq = TAILQ_NEXT(wp, entry);
- /*
- * If the pane has died and the remain-on-exit flag is not set,
- * remove the pane; otherwise, if the flag is set, don't allow
- * the window to be destroyed (or it'll close when the last
- * pane dies).
- */
- if (wp->fd == -1 && !options_get_number(oo, "remain-on-exit")) {
- layout_close_pane(wp);
- window_remove_pane(w, wp);
- server_redraw_window(w);
- } else
- destroyed = 0;
- wp = wq;
- }
- if (!destroyed)
- return;
+ if (options_get_number(&global_s_options, "lock-server"))
+ server_lock_server();
+ else
+ server_lock_sessions();
- for (i = 0; i < ARRAY_LENGTH(&sessions); i++) {
- s = ARRAY_ITEM(&sessions, i);
- if (s == NULL)
- continue;
- if (!session_has(s, w))
+ for (i = 0; i < ARRAY_LENGTH(&windows); i++) {
+ w = ARRAY_ITEM(&windows, i);
+ if (w == NULL)
continue;
- restart:
- /* Detach window and either redraw or kill clients. */
- RB_FOREACH(wl, winlinks, &s->windows) {
- if (wl->window != w)
- continue;
- if (session_detach(s, wl)) {
- server_destroy_session_group(s);
- break;
- }
- server_redraw_session(s);
- server_status_session_group(s);
- goto restart;
+ TAILQ_FOREACH(wp, &w->panes, entry) {
+ if (wp->mode != NULL && wp->mode->timer != NULL)
+ wp->mode->timer(wp);
}
}
-
- recalculate_sizes();
}
/* Lock the server if ALL sessions have hit the time limit. */
@@ -1421,31 +690,6 @@ server_lock_sessions(void)
}
}
-/* Call any once-per-second timers. */
-void
-server_second_timers(void)
-{
- struct window *w;
- struct window_pane *wp;
- u_int i;
-
- if (options_get_number(&global_s_options, "lock-server"))
- server_lock_server();
- else
- server_lock_sessions();
-
- for (i = 0; i < ARRAY_LENGTH(&windows); i++) {
- w = ARRAY_ITEM(&windows, i);
- if (w == NULL)
- continue;
-
- TAILQ_FOREACH(wp, &w->panes, entry) {
- if (wp->mode != NULL && wp->mode->timer != NULL)
- wp->mode->timer(wp);
- }
- }
-}
-
/* Update socket execute permissions based on whether sessions are attached. */
int
server_update_socket(void)
diff --git a/tmux.h b/tmux.h
index 03414ad7..a39001cb 100644
--- a/tmux.h
+++ b/tmux.h
@@ -1,4 +1,4 @@
-/* $Id: tmux.h,v 1.483 2009-10-23 17:32:26 tcunha Exp $ */
+/* $Id: tmux.h,v 1.484 2009-10-23 17:49:47 tcunha Exp $ */
/*
* Copyright (c) 2007 Nicholas Marriott <nicm@users.sourceforge.net>
@@ -1515,8 +1515,19 @@ extern struct clients clients;
extern struct clients dead_clients;
int server_start(char *);
-/* server-msg.c */
-int server_msg_dispatch(struct client *);
+/* server-client.c */
+void server_client_create(int);
+void server_client_lost(struct client *);
+void server_client_callback(int, int, void *);
+void server_client_loop(void);
+
+/* server-job.c */
+void server_job_callback(int, int, void *);
+void server_job_loop(void);
+
+/* server-window.c */
+void server_window_callback(int, int, void *);
+void server_window_loop(void);
/* server-fn.c */
void server_fill_environ(struct session *, struct environ *);
@@ -1848,7 +1859,7 @@ void buffer_write8(struct buffer *, uint8_t);
uint8_t buffer_read8(struct buffer *);
/* buffer-poll.c */
-int buffer_poll(struct pollfd *, struct buffer *, struct buffer *);
+int buffer_poll(int, int, struct buffer *, struct buffer *);
/* log.c */
void log_open_tty(int);