aboutsummaryrefslogtreecommitdiff
path: root/window.c
diff options
context:
space:
mode:
Diffstat (limited to 'window.c')
-rw-r--r--window.c317
1 files changed, 317 insertions, 0 deletions
diff --git a/window.c b/window.c
new file mode 100644
index 00000000..47d9e08d
--- /dev/null
+++ b/window.c
@@ -0,0 +1,317 @@
+/* $Id: window.c,v 1.1.1.1 2007-07-09 19:04:12 nicm 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 <fcntl.h>
+#include <paths.h>
+#include <signal.h>
+#include <stdlib.h>
+#include <string.h>
+#define TTYDEFCHARS
+#include <termios.h>
+#include <unistd.h>
+#include <util.h>
+
+#include "tmux.h"
+
+/*
+ * Each window is attached to a pty. This file contains code to handle them.
+ *
+ * A window has two buffers attached, these are filled and emptied by the main
+ * server poll loop. Output data is received from pty's in ANSI format,
+ * translated and returned as a series of ANSI escape sequences and strings.
+ * Input data is received in ANSI format and written directly to the pty.
+ *
+ * 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.
+ */
+
+/* Global window list. */
+struct windows windows;
+
+/* Create a new window. */
+struct window *
+window_create(const char *cmd, u_int sx, u_int sy)
+{
+ struct window *w;
+ struct winsize ws;
+ struct termios tio;
+ int fd, mode;
+ char pid[16], *ptr, *name;
+
+ memset(&ws, 0, sizeof ws);
+ ws.ws_col = sx;
+ ws.ws_row = sy;
+
+ memset(&tio, 0, sizeof tio);
+ tio.c_iflag = TTYDEF_IFLAG;
+ tio.c_oflag = TTYDEF_OFLAG;
+ tio.c_lflag = TTYDEF_LFLAG;
+ tio.c_cflag = TTYDEF_CFLAG;
+ memcpy(&tio.c_cc, ttydefchars, sizeof tio.c_cc);
+ cfsetspeed(&tio, TTYDEF_SPEED);
+
+ xsnprintf(pid, sizeof pid, "%ld", (long) getpid());
+ switch (forkpty(&fd, NULL, &tio, &ws)) {
+ case -1:
+ return (NULL);
+ case 0:
+ if (setenv("TMUX", pid, 1) != 0)
+ log_fatal("setenv");
+ if (setenv("TERM", "screen", 1) != 0)
+ log_fatal("setenv");
+ log_close();
+
+ execl(_PATH_BSHELL, "sh", "-c", cmd, (char *) NULL);
+ log_fatal("execl");
+ }
+
+ if ((mode = fcntl(fd, F_GETFL)) == -1)
+ log_fatal("fcntl");
+ if (fcntl(fd, F_SETFL, mode|O_NONBLOCK) == -1)
+ log_fatal("fcntl");
+
+ mode = 1;
+ if (ioctl(fd, TIOCPKT, &mode) == -1)
+ log_fatal("ioctl(TIOCPKT)");
+
+ w = xmalloc(sizeof *w);
+ w->fd = fd;
+ w->in = buffer_create(BUFSIZ);
+ w->out = buffer_create(BUFSIZ);
+ screen_create(&w->screen, sx, sy);
+
+ name = xstrdup(cmd);
+ if ((ptr = strchr(name, ' ')) != NULL) {
+ if (ptr != name && ptr[-1] != '\\')
+ *ptr = '\0';
+ else {
+ while ((ptr = strchr(ptr + 1, ' ')) != NULL) {
+ if (ptr[-1] != '\\') {
+ *ptr = '\0';
+ break;
+ }
+ }
+ }
+ }
+ strlcpy(w->name, xbasename(name), sizeof w->name);
+ xfree(name);
+
+ window_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)
+ log_fatalx("window_remove: window not found");
+ ARRAY_REMOVE(ww, i);
+
+ w->references--;
+ if (w->references == 0) {
+ window_destroy(w);
+ window_remove(&windows, w);
+ }
+}
+
+/* Destroy a window. */
+void
+window_destroy(struct window *w)
+{
+ close(w->fd);
+
+ buffer_destroy(w->in);
+ buffer_destroy(w->out);
+ 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)
+ log_fatalx("window_next: 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)
+ log_fatalx("window_previous: 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)
+{
+ struct winsize ws;
+
+ if (sx == w->screen.sx && sy == w->screen.sy)
+ return (-1);
+
+ memset(&ws, 0, sizeof ws);
+ ws.ws_col = sx;
+ ws.ws_row = sy;
+
+ screen_resize(&w->screen, sx, sy);
+
+ if (ioctl(w->fd, TIOCSWINSZ, &ws) == -1)
+ log_fatal("ioctl(TIOCSWINSZ)");
+ 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 input. */
+void
+window_input(struct window *w, struct buffer *b, size_t size)
+{
+ int key;
+
+ while (size != 0) {
+ if (size < 1)
+ break;
+ size--;
+ key = input_extract8(b);
+ if (key == '\e') {
+ if (size < 2)
+ log_fatalx("window_input: underflow");
+ size -= 2;
+ key = (int16_t) input_extract16(b);
+ }
+ input_key(w->out, key);
+ }
+}
+
+/*
+ * Process window output. Output is translated into a series of ANSI escape
+ * sequences and strings and returned.
+ */
+void
+window_output(struct window *w, struct buffer *b)
+{
+ size_t used;
+
+ used = input_parse(
+ BUFFER_OUT(w->in), BUFFER_USED(w->in), b, &w->screen);
+ if (used != 0)
+ buffer_remove(w->in, used);
+}