diff options
Diffstat (limited to 'usr.bin/tmux')
127 files changed, 28294 insertions, 0 deletions
diff --git a/usr.bin/tmux/Makefile b/usr.bin/tmux/Makefile new file mode 100644 index 00000000000..9200a8cbfaa --- /dev/null +++ b/usr.bin/tmux/Makefile @@ -0,0 +1,48 @@ +# $OpenBSD: Makefile,v 1.1 2009/06/01 22:58:49 nicm Exp $ + +.PATH: ${.CURDIR}/.. + +PROG= tmux +SRCS= arg.c attributes.c buffer-poll.c buffer.c cfg.c client-fn.c \ + client-msg.c client.c clock.c cmd-attach-session.c cmd-bind-key.c \ + cmd-break-pane.c cmd-choose-session.c cmd-choose-window.c \ + cmd-clear-history.c cmd-clock-mode.c cmd-command-prompt.c \ + cmd-confirm-before.c cmd-copy-buffer.c cmd-copy-mode.c \ + cmd-delete-buffer.c cmd-detach-client.c cmd-down-pane.c \ + cmd-find-window.c cmd-generic.c cmd-has-session.c cmd-kill-pane.c \ + cmd-kill-server.c cmd-kill-session.c cmd-kill-window.c \ + cmd-last-window.c cmd-link-window.c cmd-list-buffers.c \ + cmd-list-clients.c cmd-list-commands.c cmd-list-keys.c \ + cmd-list-sessions.c cmd-list-windows.c cmd-list.c cmd-load-buffer.c \ + cmd-lock-server.c cmd-move-window.c cmd-new-session.c cmd-new-window.c \ + cmd-next-layout.c cmd-next-window.c cmd-paste-buffer.c \ + cmd-previous-layout.c cmd-previous-window.c cmd-refresh-client.c \ + cmd-rename-session.c cmd-rename-window.c cmd-resize-pane.c \ + cmd-respawn-window.c cmd-rotate-window.c cmd-save-buffer.c \ + cmd-scroll-mode.c cmd-select-layout.c cmd-select-pane.c \ + cmd-select-prompt.c cmd-select-window.c cmd-send-keys.c \ + cmd-send-prefix.c cmd-server-info.c cmd-set-buffer.c cmd-set-option.c \ + cmd-set-password.c cmd-set-window-option.c cmd-show-buffer.c \ + cmd-show-options.c cmd-show-window-options.c cmd-source-file.c \ + cmd-split-window.c cmd-start-server.c cmd-string.c \ + cmd-suspend-client.c cmd-swap-pane.c cmd-swap-window.c \ + cmd-switch-client.c cmd-unbind-key.c cmd-unlink-window.c \ + cmd-up-pane.c cmd.c colour.c grid-view.c grid.c input-keys.c \ + input.c key-bindings.c key-string.c layout-manual.c layout.c log.c \ + mode-key.c names.c options-cmd.c options.c paste.c procname.c \ + resize.c screen-redraw.c screen-write.c screen.c server-fn.c \ + server-msg.c server.c session.c status.c tmux.c tty-keys.c tty-term.c \ + tty-write.c tty.c utf8.c util.c window-choose.c window-clock.c \ + window-copy.c window-more.c window-scroll.c window.c xmalloc.c + +CFLAGS+= -Wno-long-long -Wall -W -Wnested-externs -Wformat=2 +CFLAGS+= -Wmissing-prototypes -Wstrict-prototypes -Wmissing-declarations +CFLAGS+= -Wwrite-strings -Wshadow -Wpointer-arith -Wsign-compare +CFLAGS+= -Wundef -Wbad-function-cast -Winline -Wcast-align + +LDADD= -lutil -lncurses +DPADD= ${LIBUTIL} + +MAN= tmux.1 + +.include <bsd.prog.mk> diff --git a/usr.bin/tmux/arg.c b/usr.bin/tmux/arg.c new file mode 100644 index 00000000000..47603b3a405 --- /dev/null +++ b/usr.bin/tmux/arg.c @@ -0,0 +1,194 @@ +/* $OpenBSD: arg.c,v 1.1 2009/06/01 22:58:49 nicm Exp $ */ + +/* + * Copyright (c) 2008 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 <fnmatch.h> +#include <stdlib.h> +#include <string.h> + +#include "tmux.h" + +struct client *arg_lookup_client(const char *); +struct session *arg_lookup_session(const char *); + +struct client * +arg_lookup_client(const char *name) +{ + struct client *c; + u_int i; + + for (i = 0; i < ARRAY_LENGTH(&clients); i++) { + c = ARRAY_ITEM(&clients, i); + if (c != NULL && strcmp(name, c->tty.path) == 0) + return (c); + } + + return (NULL); +} + +struct session * +arg_lookup_session(const char *name) +{ + struct session *s, *newest = NULL; + struct timeval *tv; + u_int i; + + tv = NULL; + for (i = 0; i < ARRAY_LENGTH(&sessions); i++) { + s = ARRAY_ITEM(&sessions, i); + if (s == NULL || fnmatch(name, s->name, 0) != 0) + continue; + + if (tv == NULL || timercmp(&s->tv, tv, >)) { + newest = s; + tv = &s->tv; + } + } + + return (newest); +} + +struct client * +arg_parse_client(const char *arg) +{ + struct client *c; + char *arg2; + size_t n; + + if (arg != NULL && (arg[0] != ':' || arg[1] != '\0')) { + arg2 = xstrdup(arg); + + /* Trim a trailing : if any from the argument. */ + n = strlen(arg2); + if (arg2[n - 1] == ':') + arg2[n - 1] = '\0'; + + /* Try and look up the client name. */ + c = arg_lookup_client(arg2); + xfree(arg2); + return (c); + } + + return (NULL); +} + +struct session * +arg_parse_session(const char *arg) +{ + struct session *s; + struct client *c; + char *arg2; + size_t n; + + if (arg != NULL && (arg[0] != ':' || arg[1] != '\0')) { + arg2 = xstrdup(arg); + + /* Trim a trailing : if any from the argument. */ + n = strlen(arg2); + if (arg2[n - 1] == ':') + arg2[n - 1] = '\0'; + + /* See if the argument matches a session. */ + if ((s = arg_lookup_session(arg2)) != NULL) { + xfree(arg2); + return (s); + } + + /* If not try a client. */ + if ((c = arg_lookup_client(arg2)) != NULL) { + xfree(arg2); + return (c->session); + } + + xfree(arg2); + } + + return (NULL); +} + +int +arg_parse_window(const char *arg, struct session **s, int *idx) +{ + char *arg2, *ptr; + const char *errstr; + + *idx = -1; + + /* Handle no argument or a single :. */ + if (arg == NULL || (arg[0] == ':' && arg[1] == '\0')) { + *s = arg_parse_session(NULL); + return (0); + } + + /* Find the separator if any. */ + arg2 = xstrdup(arg); + ptr = strrchr(arg2, ':'); + + /* + * If it is first, this means no session name, so use current session + * and try to convert the rest as index. + */ + if (ptr == arg2) { + *idx = strtonum(ptr + 1, 0, INT_MAX, &errstr); + if (errstr != NULL) { + xfree(arg2); + return (1); + } + + xfree(arg2); + *s = arg_parse_session(NULL); + return (0); + } + + /* If missing, try as an index, else look up immediately. */ + if (ptr == NULL) { + *idx = strtonum(arg2, 0, INT_MAX, &errstr); + if (errstr == NULL) { + /* This is good as an index; use current session. */ + xfree(arg2); + *s = arg_parse_session(NULL); + return (0); + } + + *idx = -1; + goto lookup; + } + + /* If last, strip it and look up as a session. */ + if (ptr[1] == '\0') { + *ptr = '\0'; + goto lookup; + } + + /* Present but not first and not last. Break and convert both. */ + *ptr = '\0'; + *idx = strtonum(ptr + 1, 0, INT_MAX, &errstr); + if (errstr != NULL) { + xfree(arg2); + return (1); + } + +lookup: + /* Look up as session. */ + *s = arg_parse_session(arg2); + xfree(arg2); + if (*s == NULL) + return (1); + return (0); +} diff --git a/usr.bin/tmux/array.h b/usr.bin/tmux/array.h new file mode 100644 index 00000000000..c3603c69894 --- /dev/null +++ b/usr.bin/tmux/array.h @@ -0,0 +1,119 @@ +/* $OpenBSD: array.h,v 1.1 2009/06/01 22:58:49 nicm Exp $ */ + +/* + * Copyright (c) 2006 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. + */ + +#ifndef ARRAY_H +#define ARRAY_H + +#define ARRAY_DECL(n, c) \ + struct n { \ + c *list; \ + u_int num; \ + size_t space; \ + } + +#define ARRAY_ITEM(a, i) ((a)->list[i]) +#define ARRAY_ITEMSIZE(a) (sizeof *(a)->list) +#define ARRAY_INITIALSPACE(a) (10 * ARRAY_ITEMSIZE(a)) + +#define ARRAY_ENSURE(a, n) do { \ + if (UINT_MAX - (n) < (a)->num) \ + fatalx("number too big"); \ + if (SIZE_MAX / ((a)->num + (n)) < ARRAY_ITEMSIZE(a)) \ + fatalx("size too big"); \ + if ((a)->space == 0) { \ + (a)->space = ARRAY_INITIALSPACE(a); \ + (a)->list = xrealloc((a)->list, 1, (a)->space); \ + } \ + while ((a)->space <= ((a)->num + (n)) * ARRAY_ITEMSIZE(a)) { \ + (a)->list = xrealloc((a)->list, 2, (a)->space); \ + (a)->space *= 2; \ + } \ +} while (0) + +#define ARRAY_EMPTY(a) ((a) == NULL || (a)->num == 0) +#define ARRAY_LENGTH(a) ((a)->num) +#define ARRAY_DATA(a) ((a)->list) + +#define ARRAY_FIRST(a) ARRAY_ITEM(a, 0) +#define ARRAY_LAST(a) ARRAY_ITEM(a, (a)->num - 1) + +#define ARRAY_INIT(a) do { \ + (a)->num = 0; \ + (a)->list = NULL; \ + (a)->space = 0; \ +} while (0) +#define ARRAY_CLEAR(a) do { \ + (a)->num = 0; \ +} while (0) + +#define ARRAY_SET(a, i, s) do { \ + (a)->list[i] = s; \ +} while (0) + +#define ARRAY_ADD(a, s) do { \ + ARRAY_ENSURE(a, 1); \ + (a)->list[(a)->num] = s; \ + (a)->num++; \ +} while (0) +#define ARRAY_INSERT(a, i, s) do { \ + ARRAY_ENSURE(a, 1); \ + if ((i) < (a)->num) { \ + memmove((a)->list + (i) + 1, (a)->list + (i), \ + ARRAY_ITEMSIZE(a) * ((a)->num - (i))); \ + } \ + (a)->list[i] = s; \ + (a)->num++; \ +} while (0) +#define ARRAY_REMOVE(a, i) do { \ + if ((i) < (a)->num - 1) { \ + memmove((a)->list + (i), (a)->list + (i) + 1, \ + ARRAY_ITEMSIZE(a) * ((a)->num - (i) - 1)); \ + } \ + (a)->num--; \ + if ((a)->num == 0) \ + ARRAY_FREE(a); \ +} while (0) + +#define ARRAY_EXPAND(a, n) do { \ + ARRAY_ENSURE(a, n); \ + (a)->num += n; \ +} while (0) +#define ARRAY_TRUNC(a, n) do { \ + if ((a)->num > n) \ + (a)->num -= n; \ + else \ + ARRAY_FREE(a); \ +} while (0) + +#define ARRAY_CONCAT(a, b) do { \ + ARRAY_ENSURE(a, (b)->num); \ + memcpy((a)->list + (a)->num, (b)->list, (b)->num * ARRAY_ITEMSIZE(a)) \ + (a)->num += (b)->num; \ +} while (0) + +#define ARRAY_FREE(a) do { \ + if ((a)->list != NULL) \ + xfree((a)->list); \ + ARRAY_INIT(a); \ +} while (0) +#define ARRAY_FREEALL(a) do { \ + ARRAY_FREE(a); \ + xfree(a); \ +} while (0) + +#endif diff --git a/usr.bin/tmux/attributes.c b/usr.bin/tmux/attributes.c new file mode 100644 index 00000000000..7248b99f34a --- /dev/null +++ b/usr.bin/tmux/attributes.c @@ -0,0 +1,92 @@ +/* $OpenBSD: attributes.c,v 1.1 2009/06/01 22:58:49 nicm Exp $ */ + +/* + * Copyright (c) 2009 Joshua Elsasser <josh@elsasser.org> + * + * 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 <string.h> + +#include "tmux.h" + +const char * +attributes_tostring(u_char ch) +{ + static char buf[128]; + + if (ch == 0) + return ("default"); + + buf[0] = '\0'; + if (ch & GRID_ATTR_BRIGHT) + strlcat(buf, "bright,", sizeof (buf)); + if (ch & GRID_ATTR_DIM) + strlcat(buf, "dim,", sizeof (buf)); + if (ch & GRID_ATTR_UNDERSCORE) + strlcat(buf, "underscore,", sizeof (buf)); + if (ch & GRID_ATTR_BLINK) + strlcat(buf, "blink,", sizeof (buf)); + if (ch & GRID_ATTR_REVERSE) + strlcat(buf, "reverse,", sizeof (buf)); + if (ch & GRID_ATTR_HIDDEN) + strlcat(buf, "hidden,", sizeof (buf)); + if (ch & GRID_ATTR_ITALICS) + strlcat(buf, "italics,", sizeof (buf)); + *(strrchr(buf, ',')) = '\0'; + + return (buf); +} + +int +attributes_fromstring(const char *str) +{ + const char delimiters[] = " ,|"; + u_char ch; + size_t end; + + if (*str == '\0' || strcspn(str, delimiters) == 0) + return (-1); + if (strchr(delimiters, str[strlen(str) - 1]) != NULL) + return (-1); + + if (strcasecmp(str, "default") == 0) + return (0); + + ch = 0; + do { + end = strcspn(str, delimiters); + if ((end == 6 && strncasecmp(str, "bright", end) == 0) || + (end == 4 && strncasecmp(str, "bold", end) == 0)) + ch |= GRID_ATTR_BRIGHT; + else if (end == 3 && strncasecmp(str, "dim", end) == 0) + ch |= GRID_ATTR_DIM; + else if (end == 10 && strncasecmp(str, "underscore", end) == 0) + ch |= GRID_ATTR_UNDERSCORE; + else if (end == 5 && strncasecmp(str, "blink", end) == 0) + ch |= GRID_ATTR_BLINK; + else if (end == 7 && strncasecmp(str, "reverse", end) == 0) + ch |= GRID_ATTR_REVERSE; + else if (end == 6 && strncasecmp(str, "hidden", end) == 0) + ch |= GRID_ATTR_HIDDEN; + else if (end == 7 && strncasecmp(str, "italics", end) == 0) + ch |= GRID_ATTR_ITALICS; + else + return (-1); + str += end + strspn(str + end, delimiters); + } while (*str != '\0'); + + return (ch); +} diff --git a/usr.bin/tmux/buffer-poll.c b/usr.bin/tmux/buffer-poll.c new file mode 100644 index 00000000000..dc6507a5e38 --- /dev/null +++ b/usr.bin/tmux/buffer-poll.c @@ -0,0 +1,97 @@ +/* $OpenBSD: buffer-poll.c,v 1.1 2009/06/01 22:58:49 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 <errno.h> +#include <unistd.h> + +#include "tmux.h" + +/* Set up pollfd for buffers. */ +void +buffer_set( + struct pollfd *pfd, int fd, unused struct buffer *in, struct buffer *out) +{ + pfd->fd = fd; + pfd->events = POLLIN; + if (BUFFER_USED(out) > 0) + pfd->events |= POLLOUT; +} + +/* Fill buffers from socket based on poll results. */ +int +buffer_poll(struct pollfd *pfd, struct buffer *in, struct buffer *out) +{ + ssize_t n; + +#if 0 + log_debug("buffer_poll (%ld): fd=%d, revents=%d; out=%zu in=%zu", + (long) getpid(), + pfd->fd, pfd->revents, BUFFER_USED(out), BUFFER_USED(in)); +#endif + + if (pfd->revents & (POLLERR|POLLNVAL|POLLHUP)) + return (-1); + if (pfd->revents & POLLIN) { + buffer_ensure(in, BUFSIZ); + n = read(pfd->fd, BUFFER_IN(in), BUFFER_FREE(in)); +#if 0 + log_debug("buffer_poll: fd=%d, read=%zd", pfd->fd, n); +#endif + if (n == 0) + return (-1); + if (n == -1) { + if (errno != EINTR && errno != EAGAIN) + return (-1); + } else + buffer_add(in, n); + } + if (BUFFER_USED(out) > 0 && pfd->revents & POLLOUT) { + n = write(pfd->fd, BUFFER_OUT(out), BUFFER_USED(out)); +#if 0 + log_debug("buffer_poll: fd=%d, write=%zd", pfd->fd, n); +#endif + if (n == -1) { + if (errno != EINTR && errno != EAGAIN) + return (-1); + } else + buffer_remove(out, n); + } + return (0); +} + +/* Flush buffer output to socket. */ +void +buffer_flush(int fd, struct buffer *in, struct buffer *out) +{ + struct pollfd pfd; + + while (BUFFER_USED(out) > 0) { + buffer_set(&pfd, fd, in, out); + + if (poll(&pfd, 1, INFTIM) == -1) { + if (errno == EAGAIN || errno == EINTR) + continue; + fatal("poll failed"); + } + + if (buffer_poll(&pfd, in, out) != 0) + break; + } +} diff --git a/usr.bin/tmux/buffer.c b/usr.bin/tmux/buffer.c new file mode 100644 index 00000000000..7ff1b10efc0 --- /dev/null +++ b/usr.bin/tmux/buffer.c @@ -0,0 +1,227 @@ +/* $OpenBSD: buffer.c,v 1.1 2009/06/01 22:58:49 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 <string.h> + +#include "tmux.h" + +/* Create a buffer. */ +struct buffer * +buffer_create(size_t size) +{ + struct buffer *b; + + if (size == 0) + fatalx("zero size"); + + b = xcalloc(1, sizeof *b); + + b->base = xmalloc(size); + b->space = size; + + return (b); +} + +/* Destroy a buffer. */ +void +buffer_destroy(struct buffer *b) +{ + xfree(b->base); + xfree(b); +} + +/* Empty a buffer. */ +void +buffer_clear(struct buffer *b) +{ + b->size = 0; + b->off = 0; +} + +/* Ensure free space for size in buffer. */ +void +buffer_ensure(struct buffer *b, size_t size) +{ + if (size == 0) + fatalx("zero size"); + + if (BUFFER_FREE(b) >= size) + return; + + if (b->off > 0) { + if (b->size > 0) + memmove(b->base, b->base + b->off, b->size); + b->off = 0; + } + + if (SIZE_MAX - b->size < size) + fatalx("size too big"); + while (b->space < b->size + size) { + b->base = xrealloc(b->base, 2, b->space); + b->space *= 2; + } +} + +/* Adjust buffer after data appended. */ +void +buffer_add(struct buffer *b, size_t size) +{ + if (size == 0) + fatalx("zero size"); + if (size > b->space - b->size) + fatalx("overflow"); + + b->size += size; +} + +/* Reverse buffer add. */ +void +buffer_reverse_add(struct buffer *b, size_t size) +{ + if (size == 0) + fatalx("zero size"); + if (size > b->size) + fatalx("underflow"); + + b->size -= size; +} + +/* Adjust buffer after data removed. */ +void +buffer_remove(struct buffer *b, size_t size) +{ + if (size == 0) + fatalx("zero size"); + if (size > b->size) + fatalx("underflow"); + + b->size -= size; + b->off += size; +} + +/* Reverse buffer remove. */ +void +buffer_reverse_remove(struct buffer *b, size_t size) +{ + if (size == 0) + fatalx("zero size"); + if (size > b->off) + fatalx("overflow"); + + b->size += size; + b->off -= size; +} + +/* Insert a section into the buffer. */ +void +buffer_insert_range(struct buffer *b, size_t base, size_t size) +{ + if (size == 0) + fatalx("zero size"); + if (base > b->size) + fatalx("range outside buffer"); + + buffer_ensure(b, size); + memmove(b->base + b->off + base + size, + b->base + b->off + base, b->size - base); + b->size += size; +} + +/* Delete a section from the buffer. */ +void +buffer_delete_range(struct buffer *b, size_t base, size_t size) +{ + if (size == 0) + fatalx("zero size"); + if (size > b->size) + fatalx("size too big"); + if (base + size > b->size) + fatalx("range outside buffer"); + + memmove(b->base + b->off + base, + b->base + b->off + base + size, b->size - base - size); + b->size -= size; +} + +/* Copy data into a buffer. */ +void +buffer_write(struct buffer *b, const void *data, size_t size) +{ + if (size == 0) + fatalx("zero size"); + + buffer_ensure(b, size); + memcpy(BUFFER_IN(b), data, size); + buffer_add(b, size); +} + +/* Copy data out of a buffer. */ +void +buffer_read(struct buffer *b, void *data, size_t size) +{ + if (size == 0) + fatalx("zero size"); + if (size > b->size) + fatalx("underflow"); + + memcpy(data, BUFFER_OUT(b), size); + buffer_remove(b, size); +} + +/* Store an 8-bit value. */ +void +buffer_write8(struct buffer *b, uint8_t n) +{ + buffer_ensure(b, 1); + BUFFER_IN(b)[0] = n; + buffer_add(b, 1); +} + +/* Store a 16-bit value. */ +void +buffer_write16(struct buffer *b, uint16_t n) +{ + buffer_ensure(b, 2); + BUFFER_IN(b)[0] = n & 0xff; + BUFFER_IN(b)[1] = n >> 8; + buffer_add(b, 2); +} + +/* Extract an 8-bit value. */ +uint8_t +buffer_read8(struct buffer *b) +{ + uint8_t n; + + n = BUFFER_OUT(b)[0]; + buffer_remove(b, 1); + return (n); +} + +/* Extract a 16-bit value. */ +uint16_t +buffer_read16(struct buffer *b) +{ + uint16_t n; + + n = BUFFER_OUT(b)[0] | (BUFFER_OUT(b)[1] << 8); + buffer_remove(b, 2); + return (n); +} diff --git a/usr.bin/tmux/cfg.c b/usr.bin/tmux/cfg.c new file mode 100644 index 00000000000..2eb035c679b --- /dev/null +++ b/usr.bin/tmux/cfg.c @@ -0,0 +1,132 @@ +/* $OpenBSD: cfg.c,v 1.1 2009/06/01 22:58:49 nicm Exp $ */ + +/* + * Copyright (c) 2008 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/stat.h> + +#include <errno.h> +#include <stdio.h> +#include <string.h> + +#include "tmux.h" + +/* + * Config file parser. Pretty quick and simple, each line is parsed into a + * argv array and executed as a command. + */ + +char *cfg_string(FILE *, char, int); +void printflike2 cfg_print(struct cmd_ctx *, const char *, ...); +void printflike2 cfg_error(struct cmd_ctx *, const char *, ...); + +char *cfg_cause; + +void printflike2 +cfg_print(unused struct cmd_ctx *ctx, unused const char *fmt, ...) +{ +} + +void printflike2 +cfg_error(unused struct cmd_ctx *ctx, const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + xvasprintf(&cfg_cause, fmt, ap); + va_end(ap); +} + +int +load_cfg(const char *path, char **cause) +{ + FILE *f; + u_int n; + struct stat sb; + char *buf, *line, *ptr; + size_t len; + struct cmd_list *cmdlist; + struct cmd_ctx ctx; + + if (stat(path, &sb) != 0) { + xasprintf(cause, "%s: %s", path, strerror(errno)); + return (-1); + } + if (!S_ISREG(sb.st_mode)) { + xasprintf(cause, "%s: not a regular file", path); + return (-1); + } + + if ((f = fopen(path, "rb")) == NULL) { + xasprintf(cause, "%s: %s", path, strerror(errno)); + return (1); + } + n = 0; + + line = NULL; + while ((buf = fgetln(f, &len))) { + if (buf[len - 1] == '\n') + buf[len - 1] = '\0'; + else { + line = xrealloc(line, 1, len + 1); + memcpy(line, buf, len); + line[len] = '\0'; + buf = line; + } + n++; + + if (cmd_string_parse(buf, &cmdlist, cause) != 0) { + if (*cause == NULL) + continue; + goto error; + } + if (cmdlist == NULL) + continue; + cfg_cause = NULL; + + ctx.msgdata = NULL; + ctx.cursession = NULL; + ctx.curclient = NULL; + + ctx.error = cfg_error; + ctx.print = cfg_print; + ctx.info = cfg_print; + + ctx.cmdclient = NULL; + + cfg_cause = NULL; + cmd_list_exec(cmdlist, &ctx); + cmd_list_free(cmdlist); + if (cfg_cause != NULL) { + *cause = cfg_cause; + goto error; + } + } + if (line != NULL) + xfree(line); + fclose(f); + + return (0); + +error: + fclose(f); + + xasprintf(&ptr, "%s: %s at line %u", path, *cause, n); + xfree(*cause); + *cause = ptr; + return (1); +} diff --git a/usr.bin/tmux/client-fn.c b/usr.bin/tmux/client-fn.c new file mode 100644 index 00000000000..8a0e9c8627f --- /dev/null +++ b/usr.bin/tmux/client-fn.c @@ -0,0 +1,92 @@ +/* $OpenBSD: client-fn.c,v 1.1 2009/06/01 22:58:49 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 <stdlib.h> +#include <string.h> + +#include "tmux.h" + +void +client_fill_session(struct msg_command_data *data) +{ + char *env, *ptr1, *ptr2, buf[256]; + size_t len; + const char *errstr; + long long ll; + + data->pid = -1; + if ((env = getenv("TMUX")) == NULL) + return; + + if ((ptr2 = strrchr(env, ',')) == NULL || ptr2 == env) + return; + for (ptr1 = ptr2 - 1; ptr1 > env && *ptr1 != ','; ptr1--) + ; + if (*ptr1 != ',') + return; + ptr1++; + ptr2++; + + len = ptr2 - ptr1 - 1; + if (len > (sizeof buf) - 1) + return; + memcpy(buf, ptr1, len); + buf[len] = '\0'; + + ll = strtonum(buf, 0, LONG_MAX, &errstr); + if (errstr != NULL) + return; + data->pid = ll; + + ll = strtonum(ptr2, 0, UINT_MAX, &errstr); + if (errstr != NULL) + return; + data->idx = ll; +} + +void +client_write_server( + struct client_ctx *cctx, enum hdrtype type, void *buf, size_t len) +{ + struct hdr hdr; + + hdr.type = type; + hdr.size = len; + buffer_write(cctx->srv_out, &hdr, sizeof hdr); + + if (buf != NULL && len > 0) + buffer_write(cctx->srv_out, buf, len); +} + +void +client_write_server2(struct client_ctx *cctx, + enum hdrtype type, void *buf1, size_t len1, void *buf2, size_t len2) +{ + struct hdr hdr; + + hdr.type = type; + hdr.size = len1 + len2; + buffer_write(cctx->srv_out, &hdr, sizeof hdr); + + if (buf1 != NULL && len1 > 0) + buffer_write(cctx->srv_out, buf1, len1); + if (buf2 != NULL && len2 > 0) + buffer_write(cctx->srv_out, buf2, len2); +} diff --git a/usr.bin/tmux/client-msg.c b/usr.bin/tmux/client-msg.c new file mode 100644 index 00000000000..bafbb1d9a45 --- /dev/null +++ b/usr.bin/tmux/client-msg.c @@ -0,0 +1,159 @@ +/* $OpenBSD: client-msg.c,v 1.1 2009/06/01 22:58:49 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 <stdint.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include "tmux.h" + +int client_msg_fn_detach(struct hdr *, struct client_ctx *, char **); +int client_msg_fn_error(struct hdr *, struct client_ctx *, char **); +int client_msg_fn_shutdown(struct hdr *, struct client_ctx *, char **); +int client_msg_fn_exit(struct hdr *, struct client_ctx *, char **); +int client_msg_fn_exited(struct hdr *, struct client_ctx *, char **); +int client_msg_fn_suspend(struct hdr *, struct client_ctx *, char **); + +struct client_msg { + enum hdrtype type; + int (*fn)(struct hdr *, struct client_ctx *, char **); +}; +struct client_msg client_msg_table[] = { + { MSG_DETACH, client_msg_fn_detach }, + { MSG_ERROR, client_msg_fn_error }, + { MSG_EXIT, client_msg_fn_exit }, + { MSG_EXITED, client_msg_fn_exited }, + { MSG_SHUTDOWN, client_msg_fn_shutdown }, + { MSG_SUSPEND, client_msg_fn_suspend }, +}; + +int +client_msg_dispatch(struct client_ctx *cctx, char **error) +{ + struct hdr hdr; + struct client_msg *msg; + u_int i; + + if (BUFFER_USED(cctx->srv_in) < sizeof hdr) + return (1); + memcpy(&hdr, BUFFER_OUT(cctx->srv_in), sizeof hdr); + if (BUFFER_USED(cctx->srv_in) < (sizeof hdr) + hdr.size) + return (1); + buffer_remove(cctx->srv_in, sizeof hdr); + + for (i = 0; i < nitems(client_msg_table); i++) { + msg = client_msg_table + i; + if (msg->type == hdr.type) { + if (msg->fn(&hdr, cctx, error) != 0) + return (-1); + return (0); + } + } + fatalx("unexpected message"); +} + +int +client_msg_fn_error(struct hdr *hdr, struct client_ctx *cctx, char **error) +{ + if (hdr->size > SIZE_MAX - 1) + fatalx("bad MSG_ERROR size"); + + *error = xmalloc(hdr->size + 1); + buffer_read(cctx->srv_in, *error, hdr->size); + (*error)[hdr->size] = '\0'; + + return (-1); +} + +int +client_msg_fn_detach( + struct hdr *hdr, unused struct client_ctx *cctx, unused char **error) +{ + if (hdr->size != 0) + fatalx("bad MSG_DETACH size"); + + client_write_server(cctx, MSG_EXITING, NULL, 0); + cctx->flags |= CCTX_DETACH; + + return (0); +} + +int +client_msg_fn_shutdown( + struct hdr *hdr, unused struct client_ctx *cctx, unused char **error) +{ + if (hdr->size != 0) + fatalx("bad MSG_SHUTDOWN size"); + + client_write_server(cctx, MSG_EXITING, NULL, 0); + cctx->flags |= CCTX_SHUTDOWN; + + return (0); +} + +int +client_msg_fn_exit( + struct hdr *hdr, unused struct client_ctx *cctx, unused char **error) +{ + if (hdr->size != 0) + fatalx("bad MSG_EXIT size"); + + client_write_server(cctx, MSG_EXITING, NULL, 0); + cctx->flags |= CCTX_EXIT; + + return (0); +} + +int +client_msg_fn_exited( + struct hdr *hdr, unused struct client_ctx *cctx, unused char **error) +{ + if (hdr->size != 0) + fatalx("bad MSG_EXITED size"); + + return (-1); +} + +int +client_msg_fn_suspend( + struct hdr *hdr, unused struct client_ctx *cctx, unused char **error) +{ + struct sigaction act; + + if (hdr->size != 0) + fatalx("bad MSG_SUSPEND size"); + + memset(&act, 0, sizeof act); + sigemptyset(&act.sa_mask); + act.sa_flags = SA_RESTART; + + act.sa_handler = SIG_DFL; + if (sigaction(SIGTSTP, &act, NULL) != 0) + fatal("sigaction failed"); + + act.sa_handler = sighandler; + if (sigaction(SIGCONT, &act, NULL) != 0) + fatal("sigaction failed"); + + kill(getpid(), SIGTSTP); + + return (0); +} diff --git a/usr.bin/tmux/client.c b/usr.bin/tmux/client.c new file mode 100644 index 00000000000..2068484ce66 --- /dev/null +++ b/usr.bin/tmux/client.c @@ -0,0 +1,226 @@ +/* $OpenBSD: client.c,v 1.1 2009/06/01 22:58:49 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 <sys/socket.h> +#include <sys/stat.h> +#include <sys/un.h> +#include <sys/wait.h> + +#include <errno.h> +#include <fcntl.h> +#include <pwd.h> +#include <stdlib.h> +#include <string.h> +#include <syslog.h> +#include <unistd.h> + +#include "tmux.h" + +void client_handle_winch(struct client_ctx *); + +int +client_init(char *path, struct client_ctx *cctx, int start_server, int flags) +{ + struct sockaddr_un sa; + struct stat sb; + struct msg_identify_data data; + struct winsize ws; + size_t size; + int mode; + struct buffer *b; + char *name; + + if (lstat(path, &sb) != 0) { + if (start_server && errno == ENOENT) { + if ((cctx->srv_fd = server_start(path)) == -1) + goto start_failed; + goto server_started; + } + goto not_found; + } + if (!S_ISSOCK(sb.st_mode)) { + errno = ENOTSOCK; + goto not_found; + } + + memset(&sa, 0, sizeof sa); + sa.sun_family = AF_UNIX; + size = strlcpy(sa.sun_path, path, sizeof sa.sun_path); + if (size >= sizeof sa.sun_path) { + errno = ENAMETOOLONG; + goto not_found; + } + + if ((cctx->srv_fd = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) + fatal("socket"); + + if (connect( + cctx->srv_fd, (struct sockaddr *) &sa, SUN_LEN(&sa)) == -1) { + if (errno == ECONNREFUSED) { + if (unlink(path) != 0 || !start_server) + goto not_found; + if ((cctx->srv_fd = server_start(path)) == -1) + goto start_failed; + goto server_started; + } + goto not_found; + } + +server_started: + if ((mode = fcntl(cctx->srv_fd, F_GETFL)) == -1) + fatal("fcntl failed"); + if (fcntl(cctx->srv_fd, F_SETFL, mode|O_NONBLOCK) == -1) + fatal("fcntl failed"); + cctx->srv_in = buffer_create(BUFSIZ); + cctx->srv_out = buffer_create(BUFSIZ); + + if (isatty(STDIN_FILENO)) { + if (ioctl(STDIN_FILENO, TIOCGWINSZ, &ws) == -1) + fatal("ioctl(TIOCGWINSZ)"); + data.version = PROTOCOL_VERSION; + data.flags = flags; + data.sx = ws.ws_col; + data.sy = ws.ws_row; + *data.tty = '\0'; + if (getcwd(data.cwd, sizeof data.cwd) == NULL) + *data.cwd = '\0'; + + if ((name = ttyname(STDIN_FILENO)) == NULL) + fatal("ttyname failed"); + if (strlcpy(data.tty, name, sizeof data.tty) >= sizeof data.tty) + fatalx("ttyname failed"); + + b = buffer_create(BUFSIZ); + cmd_send_string(b, getenv("TERM")); + client_write_server2(cctx, MSG_IDENTIFY, + &data, sizeof data, BUFFER_OUT(b), BUFFER_USED(b)); + buffer_destroy(b); + } + + return (0); + +start_failed: + log_warnx("server failed to start"); + return (1); + +not_found: + log_warn("server not found"); + return (1); +} + +int +client_main(struct client_ctx *cctx) +{ + struct pollfd pfd; + char *error; + int xtimeout; /* Yay for ncurses namespace! */ + + siginit(); + + logfile("client"); + setproctitle("client"); + + error = NULL; + xtimeout = INFTIM; + while (!sigterm) { + if (sigchld) { + waitpid(WAIT_ANY, NULL, WNOHANG); + sigchld = 0; + } + if (sigwinch) + client_handle_winch(cctx); + if (sigcont) { + siginit(); + client_write_server(cctx, MSG_WAKEUP, NULL, 0); + sigcont = 0; + } + + switch (client_msg_dispatch(cctx, &error)) { + case -1: + goto out; + case 0: + /* May be more in buffer, don't let poll block. */ + xtimeout = 0; + break; + default: + /* Out of data, poll may block. */ + xtimeout = INFTIM; + break; + } + + pfd.fd = cctx->srv_fd; + pfd.events = POLLIN; + if (BUFFER_USED(cctx->srv_out) > 0) + pfd.events |= POLLOUT; + + if (poll(&pfd, 1, xtimeout) == -1) { + if (errno == EAGAIN || errno == EINTR) + continue; + fatal("poll failed"); + } + + if (buffer_poll(&pfd, cctx->srv_in, cctx->srv_out) != 0) + goto server_dead; + } + +out: + if (sigterm) { + printf("[terminated]\n"); + return (1); + } + + if (cctx->flags & CCTX_SHUTDOWN) { + printf("[server exited]\n"); + return (0); + } + + if (cctx->flags & CCTX_EXIT) { + printf("[exited]\n"); + return (0); + } + + if (cctx->flags & CCTX_DETACH) { + printf("[detached]\n"); + return (0); + } + + printf("[error: %s]\n", error); + return (1); + +server_dead: + printf("[lost server]\n"); + return (0); +} + +void +client_handle_winch(struct client_ctx *cctx) +{ + struct msg_resize_data data; + struct winsize ws; + + if (ioctl(STDIN_FILENO, TIOCGWINSZ, &ws) == -1) + fatal("ioctl failed"); + + data.sx = ws.ws_col; + data.sy = ws.ws_row; + client_write_server(cctx, MSG_RESIZE, &data, sizeof data); + + sigwinch = 0; +} diff --git a/usr.bin/tmux/clock.c b/usr.bin/tmux/clock.c new file mode 100644 index 00000000000..9f13d42bb19 --- /dev/null +++ b/usr.bin/tmux/clock.c @@ -0,0 +1,161 @@ +/* $OpenBSD: clock.c,v 1.1 2009/06/01 22:58:49 nicm 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 <string.h> +#include <time.h> + +#include "tmux.h" + +const char clock_table[14][5][5] = { + { { 1,1,1,1,1 }, /* 0 */ + { 1,0,0,0,1 }, + { 1,0,0,0,1 }, + { 1,0,0,0,1 }, + { 1,1,1,1,1 } }, + { { 0,0,0,0,1 }, /* 1 */ + { 0,0,0,0,1 }, + { 0,0,0,0,1 }, + { 0,0,0,0,1 }, + { 0,0,0,0,1 } }, + { { 1,1,1,1,1 }, /* 2 */ + { 0,0,0,0,1 }, + { 1,1,1,1,1 }, + { 1,0,0,0,0 }, + { 1,1,1,1,1 } }, + { { 1,1,1,1,1 }, /* 3 */ + { 0,0,0,0,1 }, + { 1,1,1,1,1 }, + { 0,0,0,0,1 }, + { 1,1,1,1,1 } }, + { { 1,0,0,0,1 }, /* 4 */ + { 1,0,0,0,1 }, + { 1,1,1,1,1 }, + { 0,0,0,0,1 }, + { 0,0,0,0,1 } }, + { { 1,1,1,1,1 }, /* 5 */ + { 1,0,0,0,0 }, + { 1,1,1,1,1 }, + { 0,0,0,0,1 }, + { 1,1,1,1,1 } }, + { { 1,1,1,1,1 }, /* 6 */ + { 1,0,0,0,0 }, + { 1,1,1,1,1 }, + { 1,0,0,0,1 }, + { 1,1,1,1,1 } }, + { { 1,1,1,1,1 }, /* 7 */ + { 0,0,0,0,1 }, + { 0,0,0,0,1 }, + { 0,0,0,0,1 }, + { 0,0,0,0,1 } }, + { { 1,1,1,1,1 }, /* 8 */ + { 1,0,0,0,1 }, + { 1,1,1,1,1 }, + { 1,0,0,0,1 }, + { 1,1,1,1,1 } }, + { { 1,1,1,1,1 }, /* 9 */ + { 1,0,0,0,1 }, + { 1,1,1,1,1 }, + { 0,0,0,0,1 }, + { 1,1,1,1,1 } }, + { { 0,0,0,0,0 }, /* : */ + { 0,0,1,0,0 }, + { 0,0,0,0,0 }, + { 0,0,1,0,0 }, + { 0,0,0,0,0 } }, + { { 1,1,1,1,1 }, /* A */ + { 1,0,0,0,1 }, + { 1,1,1,1,1 }, + { 1,0,0,0,1 }, + { 1,0,0,0,1 } }, + { { 1,1,1,1,1 }, /* P */ + { 1,0,0,0,1 }, + { 1,1,1,1,1 }, + { 1,0,0,0,0 }, + { 1,0,0,0,0 } }, + { { 1,0,0,0,1 }, /* M */ + { 1,1,0,1,1 }, + { 1,0,1,0,1 }, + { 1,0,0,0,1 }, + { 1,0,0,0,1 } }, +}; + +void +clock_draw(struct screen_write_ctx *ctx, u_int colour, int style) +{ + struct screen *s = ctx->s; + struct grid_cell gc; + char tim[64], *ptr; + time_t t; + u_int i, j, x, y, idx; + + t = time(NULL); + if (style == 0) + strftime(tim, sizeof tim, "%l:%M %p", localtime(&t)); + else + strftime(tim, sizeof tim, "%H:%M", localtime(&t)); + + screen_write_clearscreen(ctx); + memcpy(&gc, &grid_default_cell, sizeof gc); + gc.fg = colour; + + if (screen_size_x(s) < 6 * strlen(tim) || screen_size_y(s) < 6) { + if (screen_size_x(s) >= strlen(tim) && screen_size_y(s) != 0) { + x = (screen_size_x(s) / 2) - (strlen(tim) / 2); + y = screen_size_y(s) / 2; + screen_write_cursormove(ctx, x, y); + + gc.fg = colour; + screen_write_puts(ctx, &gc, "%s", tim); + } + return; + } + + x = (screen_size_x(s) / 2) - 3 * strlen(tim); + y = (screen_size_y(s) / 2) - 3; + + for (ptr = tim; *ptr != '\0'; ptr++) { + if (*ptr >= '0' && *ptr <= '9') + idx = *ptr - '0'; + else if (*ptr == ':') + idx = 10; + else if (*ptr == 'A') + idx = 11; + else if (*ptr == 'P') + idx = 12; + else if (*ptr == 'M') + idx = 13; + else { + x += 6; + continue; + } + + for (j = 0; j < 5; j++) { + screen_write_cursormove(ctx, x, y + j); + for (i = 0; i < 5; i++) { + if (clock_table[idx][j][i]) + gc.attr |= GRID_ATTR_REVERSE; + else + gc.attr &= ~GRID_ATTR_REVERSE; + screen_write_putc(ctx, &gc, ' '); + } + } + x += 6; + } +} diff --git a/usr.bin/tmux/cmd-attach-session.c b/usr.bin/tmux/cmd-attach-session.c new file mode 100644 index 00000000000..4fdaae04cfb --- /dev/null +++ b/usr.bin/tmux/cmd-attach-session.c @@ -0,0 +1,80 @@ +/* $OpenBSD: cmd-attach-session.c,v 1.1 2009/06/01 22:58:49 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 "tmux.h" + +/* + * Attach existing session to the current terminal. + */ + +int cmd_attach_session_exec(struct cmd *, struct cmd_ctx *); + +const struct cmd_entry cmd_attach_session_entry = { + "attach-session", "attach", + "[-d] " CMD_TARGET_SESSION_USAGE, + CMD_DFLAG|CMD_CANTNEST|CMD_STARTSERVER, + cmd_target_init, + cmd_target_parse, + cmd_attach_session_exec, + cmd_target_send, + cmd_target_recv, + cmd_target_free, + cmd_target_print +}; + +int +cmd_attach_session_exec(struct cmd *self, struct cmd_ctx *ctx) +{ + struct cmd_target_data *data = self->data; + struct session *s; + char *cause; + + if (ctx->curclient != NULL) + return (0); + + if (ARRAY_LENGTH(&sessions) == 0) { + ctx->error(ctx, "no sessions"); + return (-1); + } + if ((s = cmd_find_session(ctx, data->target)) == NULL) + return (-1); + + if (!(ctx->cmdclient->flags & CLIENT_TERMINAL)) { + ctx->error(ctx, "not a terminal"); + return (-1); + } + + if (tty_open(&ctx->cmdclient->tty, &cause) != 0) { + ctx->error(ctx, "terminal open failed: %s", cause); + xfree(cause); + return (-1); + } + + if (data->flags & CMD_DFLAG) + server_write_session(s, MSG_DETACH, NULL, 0); + ctx->cmdclient->session = s; + + server_write_client(ctx->cmdclient, MSG_READY, NULL, 0); + recalculate_sizes(); + server_redraw_client(ctx->cmdclient); + + return (1); +} + diff --git a/usr.bin/tmux/cmd-bind-key.c b/usr.bin/tmux/cmd-bind-key.c new file mode 100644 index 00000000000..7db271c5bfc --- /dev/null +++ b/usr.bin/tmux/cmd-bind-key.c @@ -0,0 +1,157 @@ +/* $OpenBSD: cmd-bind-key.c,v 1.1 2009/06/01 22:58:49 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 "tmux.h" + +/* + * Bind a key to a command, this recurses through cmd_*. + */ + +int cmd_bind_key_parse(struct cmd *, int, char **, char **); +int cmd_bind_key_exec(struct cmd *, struct cmd_ctx *); +void cmd_bind_key_send(struct cmd *, struct buffer *); +void cmd_bind_key_recv(struct cmd *, struct buffer *); +void cmd_bind_key_free(struct cmd *); +size_t cmd_bind_key_print(struct cmd *, char *, size_t); + +struct cmd_bind_key_data { + int key; + int can_repeat; + struct cmd_list *cmdlist; +}; + +const struct cmd_entry cmd_bind_key_entry = { + "bind-key", "bind", + "[-r] key command [arguments]", + 0, + NULL, + cmd_bind_key_parse, + cmd_bind_key_exec, + cmd_bind_key_send, + cmd_bind_key_recv, + cmd_bind_key_free, + cmd_bind_key_print +}; + +int +cmd_bind_key_parse(struct cmd *self, int argc, char **argv, char **cause) +{ + struct cmd_bind_key_data *data; + int opt; + + self->data = data = xmalloc(sizeof *data); + data->can_repeat = 0; + data->cmdlist = NULL; + + while ((opt = getopt(argc, argv, "r")) != -1) { + switch (opt) { + case 'r': + data->can_repeat = 1; + break; + default: + goto usage; + } + } + argc -= optind; + argv += optind; + if (argc < 1) + goto usage; + + if ((data->key = key_string_lookup_string(argv[0])) == KEYC_NONE) { + xasprintf(cause, "unknown key: %s", argv[0]); + goto error; + } + + argc--; + argv++; + if ((data->cmdlist = cmd_list_parse(argc, argv, cause)) == NULL) + goto error; + + return (0); + +usage: + xasprintf(cause, "usage: %s %s", self->entry->name, self->entry->usage); + +error: + self->entry->free(self); + return (-1); +} + +int +cmd_bind_key_exec(struct cmd *self, unused struct cmd_ctx *ctx) +{ + struct cmd_bind_key_data *data = self->data; + + if (data == NULL) + return (0); + + key_bindings_add(data->key, data->can_repeat, data->cmdlist); + data->cmdlist = NULL; /* avoid free */ + + return (0); +} + +void +cmd_bind_key_send(struct cmd *self, struct buffer *b) +{ + struct cmd_bind_key_data *data = self->data; + + buffer_write(b, data, sizeof *data); + cmd_list_send(data->cmdlist, b); +} + +void +cmd_bind_key_recv(struct cmd *self, struct buffer *b) +{ + struct cmd_bind_key_data *data; + + self->data = data = xmalloc(sizeof *data); + buffer_read(b, data, sizeof *data); + data->cmdlist = cmd_list_recv(b); +} + +void +cmd_bind_key_free(struct cmd *self) +{ + struct cmd_bind_key_data *data = self->data; + + if (data->cmdlist != NULL) + cmd_list_free(data->cmdlist); + xfree(data); +} + +size_t +cmd_bind_key_print(struct cmd *self, char *buf, size_t len) +{ + struct cmd_bind_key_data *data = self->data; + size_t off = 0; + const char *skey; + + off += xsnprintf(buf, len, "%s", self->entry->name); + if (data == NULL) + return (off); + if (off < len) { + skey = key_string_lookup_key(data->key); + off += xsnprintf(buf + off, len - off, " %s ", skey); + } + if (off < len) + off += cmd_list_print(data->cmdlist, buf + off, len - off); + return (off); +} diff --git a/usr.bin/tmux/cmd-break-pane.c b/usr.bin/tmux/cmd-break-pane.c new file mode 100644 index 00000000000..d66a8ce372f --- /dev/null +++ b/usr.bin/tmux/cmd-break-pane.c @@ -0,0 +1,92 @@ +/* $OpenBSD: cmd-break-pane.c,v 1.1 2009/06/01 22:58:49 nicm 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 <stdlib.h> + +#include "tmux.h" + +/* + * Break pane off into a window. + */ + +int cmd_break_pane_exec(struct cmd *, struct cmd_ctx *); + +const struct cmd_entry cmd_break_pane_entry = { + "break-pane", "breakp", + CMD_PANE_WINDOW_USAGE " [-d]", + CMD_DFLAG, + cmd_pane_init, + cmd_pane_parse, + cmd_break_pane_exec, + cmd_pane_send, + cmd_pane_recv, + cmd_pane_free, + cmd_pane_print +}; + +int +cmd_break_pane_exec(struct cmd *self, struct cmd_ctx *ctx) +{ + struct cmd_pane_data *data = self->data; + struct winlink *wl; + struct session *s; + struct window_pane *wp; + struct window *w; + char *cause; + + if ((wl = cmd_find_window(ctx, data->target, &s)) == NULL) + return (-1); + if (data->pane == -1) + wp = wl->window->active; + else { + wp = window_pane_at_index(wl->window, data->pane); + if (wp == NULL) { + ctx->error(ctx, "no pane: %d", data->pane); + return (-1); + } + } + + if (window_count_panes(wl->window) == 1) { + ctx->error(ctx, "can't break pane: %d", data->pane); + return (-1); + } + + TAILQ_REMOVE(&wl->window->panes, wp, entry); + if (wl->window->active == wp) { + wl->window->active = TAILQ_PREV(wp, window_panes, entry); + if (wl->window->active == NULL) + wl->window->active = TAILQ_NEXT(wp, entry); + } + layout_refresh(wl->window, 0); + + w = wp->window = window_create1(s->sx, s->sy); + TAILQ_INSERT_HEAD(&w->panes, wp, entry); + w->active = wp; + w->name = default_window_name(w); + + wl = session_attach(s, w, -1, &cause); /* can't fail */ + if (!(data->flags & CMD_DFLAG)) + session_select(s, wl->idx); + layout_refresh(w, 0); + + server_redraw_session(s); + + return (0); +} diff --git a/usr.bin/tmux/cmd-choose-session.c b/usr.bin/tmux/cmd-choose-session.c new file mode 100644 index 00000000000..c827ddb207e --- /dev/null +++ b/usr.bin/tmux/cmd-choose-session.c @@ -0,0 +1,107 @@ +/* $OpenBSD: cmd-choose-session.c,v 1.1 2009/06/01 22:58:49 nicm 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 "tmux.h" + +/* + * Enter choice mode to choose a session. + */ + +int cmd_choose_session_exec(struct cmd *, struct cmd_ctx *); + +void cmd_choose_session_callback(void *, int); + +const struct cmd_entry cmd_choose_session_entry = { + "choose-session", NULL, + CMD_TARGET_WINDOW_USAGE, + 0, + cmd_target_init, + cmd_target_parse, + cmd_choose_session_exec, + cmd_target_send, + cmd_target_recv, + cmd_target_free, + cmd_target_print +}; + +struct cmd_choose_session_data { + u_int client; +}; + +int +cmd_choose_session_exec(struct cmd *self, struct cmd_ctx *ctx) +{ + struct cmd_target_data *data = self->data; + struct cmd_choose_session_data *cdata; + struct winlink *wl; + struct session *s; + u_int i, idx, cur; + + if (ctx->curclient == NULL) { + ctx->error(ctx, "must be run interactively"); + return (-1); + } + + if ((wl = cmd_find_window(ctx, data->target, NULL)) == NULL) + return (-1); + + if (window_pane_set_mode(wl->window->active, &window_choose_mode) != 0) + return (0); + + cur = idx = 0; + for (i = 0; i < ARRAY_LENGTH(&sessions); i++) { + s = ARRAY_ITEM(&sessions, i); + if (s == NULL) + continue; + if (s == ctx->curclient->session) + cur = idx; + idx++; + + window_choose_add(wl->window->active, i, + "%s: %u windows [%ux%u]%s", s->name, + winlink_count(&s->windows), s->sx, s->sy, + s->flags & SESSION_UNATTACHED ? "" : " (attached)"); + } + + cdata = xmalloc(sizeof *cdata); + cdata->client = server_client_index(ctx->curclient); + + window_choose_ready( + wl->window->active, cur, cmd_choose_session_callback, cdata); + + return (0); +} + +void +cmd_choose_session_callback(void *data, int idx) +{ + struct cmd_choose_session_data *cdata = data; + struct client *c; + + if (idx != -1 && cdata->client <= ARRAY_LENGTH(&clients) - 1) { + c = ARRAY_ITEM(&clients, cdata->client); + if (c != NULL && (u_int) idx <= ARRAY_LENGTH(&sessions) - 1) { + c->session = ARRAY_ITEM(&sessions, idx); + recalculate_sizes(); + server_redraw_client(c); + } + } + xfree(cdata); +} diff --git a/usr.bin/tmux/cmd-choose-window.c b/usr.bin/tmux/cmd-choose-window.c new file mode 100644 index 00000000000..100d0b57253 --- /dev/null +++ b/usr.bin/tmux/cmd-choose-window.c @@ -0,0 +1,106 @@ +/* $OpenBSD: cmd-choose-window.c,v 1.1 2009/06/01 22:58:49 nicm 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 "tmux.h" + +/* + * Enter choice mode to choose a window. + */ + +int cmd_choose_window_exec(struct cmd *, struct cmd_ctx *); + +void cmd_choose_window_callback(void *, int); + +const struct cmd_entry cmd_choose_window_entry = { + "choose-window", NULL, + CMD_TARGET_WINDOW_USAGE, + 0, + cmd_target_init, + cmd_target_parse, + cmd_choose_window_exec, + cmd_target_send, + cmd_target_recv, + cmd_target_free, + cmd_target_print +}; + +struct cmd_choose_window_data { + u_int session; +}; + +int +cmd_choose_window_exec(struct cmd *self, struct cmd_ctx *ctx) +{ + struct cmd_target_data *data = self->data; + struct cmd_choose_window_data *cdata; + struct session *s; + struct winlink *wl, *wm; + struct window *w; + u_int idx, cur; + + if (ctx->curclient == NULL) { + ctx->error(ctx, "must be run interactively"); + return (-1); + } + s = ctx->curclient->session; + + if ((wl = cmd_find_window(ctx, data->target, NULL)) == NULL) + return (-1); + + if (window_pane_set_mode(wl->window->active, &window_choose_mode) != 0) + return (0); + + cur = idx = 0; + RB_FOREACH(wm, winlinks, &s->windows) { + w = wm->window; + + if (wm == s->curw) + cur = idx; + idx++; + + window_choose_add(wl->window->active, + wm->idx, "%3d: %s [%ux%u %s] (%u panes)", wm->idx, w->name, + w->sx, w->sy, layout_name(w), window_count_panes(w)); + } + + cdata = xmalloc(sizeof *cdata); + if (session_index(s, &cdata->session) != 0) + fatalx("session not found"); + + window_choose_ready( + wl->window->active, cur, cmd_choose_window_callback, cdata); + + return (0); +} + +void +cmd_choose_window_callback(void *data, int idx) +{ + struct cmd_choose_window_data *cdata = data; + struct session *s; + + if (idx != -1 && cdata->session <= ARRAY_LENGTH(&sessions) - 1) { + s = ARRAY_ITEM(&sessions, cdata->session); + if (s != NULL && session_select(s, idx) == 0) + server_redraw_session(s); + recalculate_sizes(); + } + xfree(cdata); +} diff --git a/usr.bin/tmux/cmd-clear-history.c b/usr.bin/tmux/cmd-clear-history.c new file mode 100644 index 00000000000..a15afa8388d --- /dev/null +++ b/usr.bin/tmux/cmd-clear-history.c @@ -0,0 +1,68 @@ +/* $OpenBSD: cmd-clear-history.c,v 1.1 2009/06/01 22:58:49 nicm 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 "tmux.h" + +/* + * Clear pane history. + */ + +void cmd_clear_history_init(struct cmd *, int); +int cmd_clear_history_exec(struct cmd *, struct cmd_ctx *); + +const struct cmd_entry cmd_clear_history_entry = { + "clear-history", "clearhist", + CMD_PANE_WINDOW_USAGE, + 0, + cmd_pane_init, + cmd_pane_parse, + cmd_clear_history_exec, + cmd_pane_send, + cmd_pane_recv, + cmd_pane_free, + cmd_pane_print +}; + +int +cmd_clear_history_exec(struct cmd *self, struct cmd_ctx *ctx) +{ + struct cmd_pane_data *data = self->data; + struct winlink *wl; + struct window_pane *wp; + struct grid *gd; + + if ((wl = cmd_find_window(ctx, data->target, NULL)) == NULL) + return (-1); + if (data->pane == -1) + wp = wl->window->active; + else { + wp = window_pane_at_index(wl->window, data->pane); + if (wp == NULL) { + ctx->error(ctx, "no pane: %d", data->pane); + return (-1); + } + } + gd = wp->base.grid; + + grid_move_lines(gd, 0, gd->hsize, gd->sy); + gd->hsize = 0; + + return (0); +} diff --git a/usr.bin/tmux/cmd-clock-mode.c b/usr.bin/tmux/cmd-clock-mode.c new file mode 100644 index 00000000000..331ec464d82 --- /dev/null +++ b/usr.bin/tmux/cmd-clock-mode.c @@ -0,0 +1,54 @@ +/* $OpenBSD: cmd-clock-mode.c,v 1.1 2009/06/01 22:58:49 nicm 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 "tmux.h" + +/* + * Enter clock mode. + */ + +int cmd_clock_mode_exec(struct cmd *, struct cmd_ctx *); + +const struct cmd_entry cmd_clock_mode_entry = { + "clock-mode", NULL, + CMD_TARGET_WINDOW_USAGE, + 0, + cmd_target_init, + cmd_target_parse, + cmd_clock_mode_exec, + cmd_target_send, + cmd_target_recv, + cmd_target_free, + cmd_target_print +}; + +int +cmd_clock_mode_exec(struct cmd *self, struct cmd_ctx *ctx) +{ + struct cmd_target_data *data = self->data; + struct winlink *wl; + + if ((wl = cmd_find_window(ctx, data->target, NULL)) == NULL) + return (-1); + + window_pane_set_mode(wl->window->active, &window_clock_mode); + + return (0); +} diff --git a/usr.bin/tmux/cmd-command-prompt.c b/usr.bin/tmux/cmd-command-prompt.c new file mode 100644 index 00000000000..82354ef463a --- /dev/null +++ b/usr.bin/tmux/cmd-command-prompt.c @@ -0,0 +1,178 @@ +/* $OpenBSD: cmd-command-prompt.c,v 1.1 2009/06/01 22:58:49 nicm Exp $ */ + +/* + * Copyright (c) 2008 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 <ctype.h> +#include <string.h> + +#include "tmux.h" + +/* + * Prompt for command in client. + */ + +void cmd_command_prompt_init(struct cmd *, int); +int cmd_command_prompt_exec(struct cmd *, struct cmd_ctx *); + +int cmd_command_prompt_callback(void *, const char *); + +const struct cmd_entry cmd_command_prompt_entry = { + "command-prompt", NULL, + CMD_TARGET_CLIENT_USAGE " [template]", + CMD_ARG01, + cmd_command_prompt_init, + cmd_target_parse, + cmd_command_prompt_exec, + cmd_target_send, + cmd_target_recv, + cmd_target_free, + cmd_target_print +}; + +struct cmd_command_prompt_data { + struct client *c; + char *template; +}; + +void +cmd_command_prompt_init(struct cmd *self, int key) +{ + struct cmd_target_data *data; + + cmd_target_init(self, key); + data = self->data; + + switch (key) { + case ',': + data->arg = xstrdup("rename-window '%%'"); + break; + case '.': + data->arg = xstrdup("move-window -t '%%'"); + break; + case 'f': + data->arg = xstrdup("find-window '%%'"); + break; + } +} + +int +cmd_command_prompt_exec(struct cmd *self, struct cmd_ctx *ctx) +{ + struct cmd_target_data *data = self->data; + struct cmd_command_prompt_data *cdata; + struct client *c; + char *hdr, *ptr; + + if ((c = cmd_find_client(ctx, data->target)) == NULL) + return (-1); + + if (c->prompt_string != NULL) + return (0); + + cdata = xmalloc(sizeof *cdata); + cdata->c = c; + if (data->arg != NULL) { + cdata->template = xstrdup(data->arg); + if ((ptr = strchr(data->arg, ' ')) == NULL) + ptr = strchr(data->arg, '\0'); + xasprintf(&hdr, "(%.*s) ", (int) (ptr - data->arg), data->arg); + } else { + cdata->template = NULL; + hdr = xstrdup(":"); + } + status_prompt_set(c, hdr, cmd_command_prompt_callback, cdata, 0); + xfree(hdr); + + return (0); +} + +int +cmd_command_prompt_callback(void *data, const char *s) +{ + struct cmd_command_prompt_data *cdata = data; + struct client *c = cdata->c; + struct cmd_list *cmdlist; + struct cmd_ctx ctx; + char *cause, *ptr, *buf, ch; + size_t len, slen; + + if (s == NULL) { + xfree(cdata); + return (0); + } + slen = strlen(s); + + len = 0; + buf = NULL; + if (cdata->template != NULL) { + ptr = cdata->template; + while (*ptr != '\0') { + switch (ch = *ptr++) { + case '%': + if (*ptr != '%') + break; + ptr++; + + buf = xrealloc(buf, 1, len + slen + 1); + memcpy(buf + len, s, slen); + len += slen; + break; + default: + buf = xrealloc(buf, 1, len + 2); + buf[len++] = ch; + break; + } + } + xfree(cdata->template); + + buf[len] = '\0'; + s = buf; + } + xfree(cdata); + + if (cmd_string_parse(s, &cmdlist, &cause) != 0) { + if (cause == NULL) + return (0); + *cause = toupper((u_char) *cause); + status_message_set(c, cause); + xfree(cause); + cmdlist = NULL; + } + if (buf != NULL) + xfree(buf); + if (cmdlist == NULL) + return (0); + + ctx.msgdata = NULL; + ctx.cursession = c->session; + ctx.curclient = c; + + ctx.error = key_bindings_error; + ctx.print = key_bindings_print; + ctx.info = key_bindings_info; + + ctx.cmdclient = NULL; + + cmd_list_exec(cmdlist, &ctx); + cmd_list_free(cmdlist); + + if (c->prompt_callback != (void *) &cmd_command_prompt_callback) + return (1); + return (0); +} diff --git a/usr.bin/tmux/cmd-confirm-before.c b/usr.bin/tmux/cmd-confirm-before.c new file mode 100644 index 00000000000..4b3b8cab3a2 --- /dev/null +++ b/usr.bin/tmux/cmd-confirm-before.c @@ -0,0 +1,141 @@ +/* $OpenBSD: cmd-confirm-before.c,v 1.1 2009/06/01 22:58:49 nicm Exp $ */ + +/* + * Copyright (c) 2009 Tiago Cunha <me@tiagocunha.org> + * + * 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 <ctype.h> +#include <string.h> + +#include "tmux.h" + +/* + * Asks for confirmation before executing a command. + */ + +int cmd_confirm_before_exec(struct cmd *, struct cmd_ctx *); +void cmd_confirm_before_init(struct cmd *, int); + +int cmd_confirm_before_callback(void *, const char *); + +struct cmd_confirm_before_data { + struct client *c; + char *cmd; +}; + +const struct cmd_entry cmd_confirm_before_entry = { + "confirm-before", "confirm", + CMD_TARGET_CLIENT_USAGE " command", + CMD_ARG1, + cmd_confirm_before_init, + cmd_target_parse, + cmd_confirm_before_exec, + cmd_target_send, + cmd_target_recv, + cmd_target_free, + cmd_target_print +}; + +void +cmd_confirm_before_init(struct cmd *self, int key) +{ + struct cmd_target_data *data; + + cmd_target_init(self, key); + data = self->data; + + switch (key) { + case '&': + data->arg = xstrdup("kill-window"); + break; + case 'x': + data->arg = xstrdup("kill-pane"); + break; + } +} + +int +cmd_confirm_before_exec(unused struct cmd *self, struct cmd_ctx *ctx) +{ + struct cmd_target_data *data = self->data; + struct cmd_confirm_before_data *cdata; + struct client *c; + char *buf, *cmd, *ptr; + + if (ctx->curclient == NULL) { + ctx->error(ctx, "must be run interactively"); + return (-1); + } + + if ((c = cmd_find_client(ctx, data->target)) == NULL) + return (-1); + + ptr = xstrdup(data->arg); + if ((cmd = strtok(ptr, " \t")) == NULL) + cmd = ptr; + xasprintf(&buf, "Confirm '%s'? (y/n) ", cmd); + xfree(ptr); + + cdata = xmalloc(sizeof *cdata); + cdata->cmd = xstrdup(data->arg); + cdata->c = c; + status_prompt_set( + cdata->c, buf, cmd_confirm_before_callback, cdata, PROMPT_SINGLE); + + xfree(buf); + return (1); +} + +int +cmd_confirm_before_callback(void *data, const char *s) +{ + struct cmd_confirm_before_data *cdata = data; + struct client *c = cdata->c; + struct cmd_list *cmdlist; + struct cmd_ctx ctx; + char *cause; + + if (s == NULL || tolower((u_char) s[0]) != 'y' || s[1] != '\0') + goto out; + + if (cmd_string_parse(cdata->cmd, &cmdlist, &cause) != 0) { + if (cause != NULL) { + *cause = toupper((u_char) *cause); + status_message_set(c, cause); + xfree(cause); + } + goto out; + } + + ctx.msgdata = NULL; + ctx.cursession = c->session; + ctx.curclient = c; + + ctx.error = key_bindings_error; + ctx.print = key_bindings_print; + ctx.info = key_bindings_info; + + ctx.cmdclient = NULL; + + cmd_list_exec(cmdlist, &ctx); + cmd_list_free(cmdlist); + +out: + if (cdata->cmd != NULL) + xfree(cdata->cmd); + xfree(cdata); + + return (0); +} diff --git a/usr.bin/tmux/cmd-copy-buffer.c b/usr.bin/tmux/cmd-copy-buffer.c new file mode 100644 index 00000000000..f8381fc9396 --- /dev/null +++ b/usr.bin/tmux/cmd-copy-buffer.c @@ -0,0 +1,222 @@ +/* $OpenBSD: cmd-copy-buffer.c,v 1.1 2009/06/01 22:58:49 nicm Exp $ */ + +/* + * Copyright (c) 2009 Tiago Cunha <me@tiagocunha.org> + * + * 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 <stdlib.h> + +#include "tmux.h" + +/* + * Copies a session paste buffer to another session. + */ + +int cmd_copy_buffer_parse(struct cmd *, int, char **, char **); +int cmd_copy_buffer_exec(struct cmd *, struct cmd_ctx *); +void cmd_copy_buffer_send(struct cmd *, struct buffer *); +void cmd_copy_buffer_recv(struct cmd *, struct buffer *); +void cmd_copy_buffer_free(struct cmd *); +void cmd_copy_buffer_init(struct cmd *, int); +size_t cmd_copy_buffer_print(struct cmd *, char *, size_t); + +struct cmd_copy_buffer_data { + char *dst_session; + char *src_session; + int dst_idx; + int src_idx; +}; + +const struct cmd_entry cmd_copy_buffer_entry = { + "copy-buffer", "copyb", + "[-a src-index] [-b dst-index] [-s src-session] [-t dst-session]", + 0, + cmd_copy_buffer_init, + cmd_copy_buffer_parse, + cmd_copy_buffer_exec, + cmd_copy_buffer_send, + cmd_copy_buffer_recv, + cmd_copy_buffer_free, + cmd_copy_buffer_print +}; + +void +cmd_copy_buffer_init(struct cmd *self, unused int arg) +{ + struct cmd_copy_buffer_data *data; + + self->data = data = xmalloc(sizeof *data); + data->dst_session = NULL; + data->src_session = NULL; + data->dst_idx = -1; + data->src_idx = -1; +} + +int +cmd_copy_buffer_parse(struct cmd *self, int argc, char **argv, char **cause) +{ + struct cmd_copy_buffer_data *data; + const char *errstr; + int n, opt; + + self->entry->init(self, 0); + data = self->data; + + while ((opt = getopt(argc, argv, "a:b:s:t:")) != -1) { + switch (opt) { + case 'a': + if (data->src_idx == -1) { + n = strtonum(optarg, 0, INT_MAX, &errstr); + if (errstr != NULL) { + xasprintf(cause, "buffer %s", errstr); + goto error; + } + data->src_idx = n; + } + break; + case 'b': + if (data->dst_idx == -1) { + n = strtonum(optarg, 0, INT_MAX, &errstr); + if (errstr != NULL) { + xasprintf(cause, "buffer %s", errstr); + goto error; + } + data->dst_idx = n; + } + break; + case 's': + if (data->src_session == NULL) + data->src_session = xstrdup(optarg); + break; + case 't': + if (data->dst_session == NULL) + data->dst_session = xstrdup(optarg); + break; + default: + goto usage; + } + } + argc -= optind; + argv += optind; + + return (0); + +usage: + xasprintf(cause, "usage: %s %s", self->entry->name, self->entry->usage); + +error: + self->entry->free(self); + return (-1); +} + +int +cmd_copy_buffer_exec(struct cmd *self, struct cmd_ctx *ctx) +{ + struct cmd_copy_buffer_data *data = self->data; + struct paste_buffer *pb; + struct session *dst_session, *src_session; + u_int limit; + + if ((dst_session = cmd_find_session(ctx, data->dst_session)) == NULL || + (src_session = cmd_find_session(ctx, data->src_session)) == NULL) + return (-1); + + if (data->src_idx == -1) { + if ((pb = paste_get_top(&src_session->buffers)) == NULL) { + ctx->error(ctx, "no buffers"); + return (-1); + } + } else { + if ((pb = paste_get_index(&src_session->buffers, + data->src_idx)) == NULL) { + ctx->error(ctx, "no buffer %d", data->src_idx); + return (-1); + } + } + + limit = options_get_number(&dst_session->options, "buffer-limit"); + if (data->dst_idx == -1) { + paste_add(&dst_session->buffers, xstrdup(pb->data), limit); + return (0); + } + if (paste_replace(&dst_session->buffers, data->dst_idx, + xstrdup(pb->data)) != 0) { + ctx->error(ctx, "no buffer %d", data->dst_idx); + return (-1); + } + + return (0); +} + +void +cmd_copy_buffer_send(struct cmd *self, struct buffer *b) +{ + struct cmd_copy_buffer_data *data = self->data; + + buffer_write(b, data, sizeof *data); + cmd_send_string(b, data->dst_session); + cmd_send_string(b, data->src_session); +} + +void +cmd_copy_buffer_recv(struct cmd *self, struct buffer *b) +{ + struct cmd_copy_buffer_data *data; + + self->data = data = xmalloc(sizeof *data); + buffer_read(b, data, sizeof *data); + data->dst_session = cmd_recv_string(b); + data->src_session = cmd_recv_string(b); +} + +void +cmd_copy_buffer_free(struct cmd *self) +{ + struct cmd_copy_buffer_data *data = self->data; + + if (data->dst_session != NULL) + xfree(data->dst_session); + if (data->src_session != NULL) + xfree(data->src_session); + xfree(data); +} + +size_t +cmd_copy_buffer_print(struct cmd *self, char *buf, size_t len) +{ + struct cmd_copy_buffer_data *data = self->data; + size_t off = 0; + + off += xsnprintf(buf, len, "%s", self->entry->name); + if (data == NULL) + return (off); + if (off < len && data->src_idx != -1) { + off += xsnprintf(buf + off, len - off, " -a %d", + data->src_idx); + } + if (off < len && data->dst_idx != -1) { + off += xsnprintf(buf + off, len - off, " -b %d", + data->dst_idx); + } + if (off < len && data->src_session != NULL) { + off += cmd_prarg(buf + off, len - off, " -s ", + data->src_session); + } + if (off < len && data->dst_session != NULL) { + off += cmd_prarg(buf + off, len - off, " -t ", + data->dst_session); + } + return (off); +} diff --git a/usr.bin/tmux/cmd-copy-mode.c b/usr.bin/tmux/cmd-copy-mode.c new file mode 100644 index 00000000000..f1121d5ac86 --- /dev/null +++ b/usr.bin/tmux/cmd-copy-mode.c @@ -0,0 +1,56 @@ +/* $OpenBSD: cmd-copy-mode.c,v 1.1 2009/06/01 22:58:49 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 "tmux.h" + +/* + * Enter copy mode. + */ + +int cmd_copy_mode_exec(struct cmd *, struct cmd_ctx *); + +const struct cmd_entry cmd_copy_mode_entry = { + "copy-mode", NULL, + CMD_TARGET_WINDOW_USAGE, + CMD_UFLAG, + cmd_target_init, + cmd_target_parse, + cmd_copy_mode_exec, + cmd_target_send, + cmd_target_recv, + cmd_target_free, + NULL +}; + +int +cmd_copy_mode_exec(struct cmd *self, struct cmd_ctx *ctx) +{ + struct cmd_target_data *data = self->data; + struct winlink *wl; + + if ((wl = cmd_find_window(ctx, data->target, NULL)) == NULL) + return (-1); + + window_pane_set_mode(wl->window->active, &window_copy_mode); + if (data->flags & CMD_UFLAG) + window_copy_pageup(wl->window->active); + + return (0); +} diff --git a/usr.bin/tmux/cmd-delete-buffer.c b/usr.bin/tmux/cmd-delete-buffer.c new file mode 100644 index 00000000000..223c5e42d8b --- /dev/null +++ b/usr.bin/tmux/cmd-delete-buffer.c @@ -0,0 +1,61 @@ +/* $OpenBSD: cmd-delete-buffer.c,v 1.1 2009/06/01 22:58:49 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 <stdlib.h> + +#include "tmux.h" + +/* + * Delete a paste buffer. + */ + +int cmd_delete_buffer_exec(struct cmd *, struct cmd_ctx *); + +const struct cmd_entry cmd_delete_buffer_entry = { + "delete-buffer", "deleteb", + CMD_BUFFER_SESSION_USAGE, + 0, + cmd_buffer_init, + cmd_buffer_parse, + cmd_delete_buffer_exec, + cmd_buffer_send, + cmd_buffer_recv, + cmd_buffer_free, + cmd_buffer_print +}; + +int +cmd_delete_buffer_exec(struct cmd *self, struct cmd_ctx *ctx) +{ + struct cmd_buffer_data *data = self->data; + struct session *s; + + if ((s = cmd_find_session(ctx, data->target)) == NULL) + return (-1); + + if (data->buffer == -1) + paste_free_top(&s->buffers); + else if (paste_free_index(&s->buffers, data->buffer) != 0) { + ctx->error(ctx, "no buffer %d", data->buffer); + return (-1); + } + + return (0); +} diff --git a/usr.bin/tmux/cmd-detach-client.c b/usr.bin/tmux/cmd-detach-client.c new file mode 100644 index 00000000000..6344671f863 --- /dev/null +++ b/usr.bin/tmux/cmd-detach-client.c @@ -0,0 +1,54 @@ +/* $OpenBSD: cmd-detach-client.c,v 1.1 2009/06/01 22:58:49 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 "tmux.h" + +/* + * Detach a client. + */ + +int cmd_detach_client_exec(struct cmd *, struct cmd_ctx *); + +const struct cmd_entry cmd_detach_client_entry = { + "detach-client", "detach", + CMD_TARGET_CLIENT_USAGE, + 0, + cmd_target_init, + cmd_target_parse, + cmd_detach_client_exec, + cmd_target_send, + cmd_target_recv, + cmd_target_free, + cmd_target_print +}; + +int +cmd_detach_client_exec(struct cmd *self, struct cmd_ctx *ctx) +{ + struct cmd_target_data *data = self->data; + struct client *c; + + if ((c = cmd_find_client(ctx, data->target)) == NULL) + return (-1); + + server_write_client(c, MSG_DETACH, NULL, 0); + + return (0); +} diff --git a/usr.bin/tmux/cmd-down-pane.c b/usr.bin/tmux/cmd-down-pane.c new file mode 100644 index 00000000000..86bd41eb949 --- /dev/null +++ b/usr.bin/tmux/cmd-down-pane.c @@ -0,0 +1,61 @@ +/* $OpenBSD: cmd-down-pane.c,v 1.1 2009/06/01 22:58:49 nicm 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 "tmux.h" + +/* + * Move down a pane. + */ + +int cmd_down_pane_exec(struct cmd *, struct cmd_ctx *); + +const struct cmd_entry cmd_down_pane_entry = { + "down-pane", "downp", + CMD_TARGET_WINDOW_USAGE, + 0, + cmd_target_init, + cmd_target_parse, + cmd_down_pane_exec, + cmd_target_send, + cmd_target_recv, + cmd_target_free, + cmd_target_print +}; + +int +cmd_down_pane_exec(struct cmd *self, struct cmd_ctx *ctx) +{ + struct cmd_target_data *data = self->data; + struct winlink *wl; + struct window *w; + + if ((wl = cmd_find_window(ctx, data->target, NULL)) == NULL) + return (-1); + w = wl->window; + + do { + w->active = TAILQ_NEXT(w->active, entry); + if (w->active == NULL) + w->active = TAILQ_FIRST(&w->panes); + layout_refresh(w, 1); + } while (w->active->flags & PANE_HIDDEN); + + return (0); +} diff --git a/usr.bin/tmux/cmd-find-window.c b/usr.bin/tmux/cmd-find-window.c new file mode 100644 index 00000000000..772892c114d --- /dev/null +++ b/usr.bin/tmux/cmd-find-window.c @@ -0,0 +1,160 @@ +/* $OpenBSD: cmd-find-window.c,v 1.1 2009/06/01 22:58:49 nicm 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 <string.h> + +#include "tmux.h" + +/* + * Find window containing text. + */ + +int cmd_find_window_exec(struct cmd *, struct cmd_ctx *); + +void cmd_find_window_callback(void *, int); + +const struct cmd_entry cmd_find_window_entry = { + "find-window", "findw", + CMD_TARGET_WINDOW_USAGE " match-string", + CMD_ARG1, + cmd_target_init, + cmd_target_parse, + cmd_find_window_exec, + cmd_target_send, + cmd_target_recv, + cmd_target_free, + cmd_target_print +}; + +struct cmd_find_window_data { + u_int session; +}; + +int +cmd_find_window_exec(struct cmd *self, struct cmd_ctx *ctx) +{ + struct cmd_target_data *data = self->data; + struct cmd_find_window_data *cdata; + struct session *s; + struct winlink *wl, *wm; + struct window *w; + struct window_pane *wp; + ARRAY_DECL(, u_int) list_idx; + ARRAY_DECL(, char *) list_ctx; + char *sres, *sctx; + u_int i; + + if (ctx->curclient == NULL) { + ctx->error(ctx, "must be run interactively"); + return (-1); + } + s = ctx->curclient->session; + + if ((wl = cmd_find_window(ctx, data->target, NULL)) == NULL) + return (-1); + + ARRAY_INIT(&list_idx); + ARRAY_INIT(&list_ctx); + + RB_FOREACH(wm, winlinks, &s->windows) { + i = 0; + TAILQ_FOREACH(wp, &wm->window->panes, entry) { + i++; + + if (strstr(wm->window->name, data->arg) != NULL) + sctx = xstrdup(""); + else { + sres = window_pane_search(wp, data->arg); + if (sres == NULL && + strstr(wp->base.title, data->arg) == NULL) + continue; + + if (sres == NULL) { + xasprintf(&sctx, + "pane %u title: \"%s\"", i - 1, + wp->base.title); + } else { + xasprintf(&sctx, "\"%s\"", sres); + xfree(sres); + } + } + + ARRAY_ADD(&list_idx, wm->idx); + ARRAY_ADD(&list_ctx, sctx); + } + } + + if (ARRAY_LENGTH(&list_idx) == 0) { + ctx->error(ctx, "no windows matching: %s", data->arg); + ARRAY_FREE(&list_idx); + ARRAY_FREE(&list_ctx); + return (-1); + } + + if (ARRAY_LENGTH(&list_idx) == 1) { + if (session_select(s, ARRAY_FIRST(&list_idx)) == 0) + server_redraw_session(s); + recalculate_sizes(); + goto out; + } + + if (window_pane_set_mode(wl->window->active, &window_choose_mode) != 0) + goto out; + + for (i = 0; i < ARRAY_LENGTH(&list_idx); i++) { + wm = winlink_find_by_index( + &s->windows, ARRAY_ITEM(&list_idx, i)); + w = wm->window; + + sctx = ARRAY_ITEM(&list_ctx, i); + window_choose_add(wl->window->active, + wm->idx, "%3d: %s [%ux%u] (%u panes) %s", wm->idx, w->name, + w->sx, w->sy, window_count_panes(w), sctx); + xfree(sctx); + } + + cdata = xmalloc(sizeof *cdata); + if (session_index(s, &cdata->session) != 0) + fatalx("session not found"); + + window_choose_ready( + wl->window->active, 0, cmd_find_window_callback, cdata); + +out: + ARRAY_FREE(&list_idx); + ARRAY_FREE(&list_ctx); + + return (0); +} + +void +cmd_find_window_callback(void *data, int idx) +{ + struct cmd_find_window_data *cdata = data; + struct session *s; + + if (idx != -1 && cdata->session <= ARRAY_LENGTH(&sessions) - 1) { + s = ARRAY_ITEM(&sessions, cdata->session); + if (s != NULL && session_select(s, idx) == 0) + server_redraw_session(s); + recalculate_sizes(); + } + xfree(cdata); +} diff --git a/usr.bin/tmux/cmd-generic.c b/usr.bin/tmux/cmd-generic.c new file mode 100644 index 00000000000..5518588b0e4 --- /dev/null +++ b/usr.bin/tmux/cmd-generic.c @@ -0,0 +1,694 @@ +/* $OpenBSD: cmd-generic.c,v 1.1 2009/06/01 22:58:49 nicm Exp $ */ + +/* + * Copyright (c) 2008 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 <stdlib.h> +#include <string.h> + +#include "tmux.h" + +#define CMD_FLAGS "adDgkuU" +#define CMD_FLAGMASK (CMD_AFLAG|CMD_DFLAG|CMD_BIGDFLAG|CMD_GFLAG|CMD_KFLAG| \ + CMD_UFLAG|CMD_BIGUFLAG) + +int cmd_do_flags(int, int, int *); +size_t cmd_print_flags(char *, size_t, size_t, int); +int cmd_fill_argument(int, char **, int, char **); + +size_t +cmd_prarg(char *buf, size_t len, const char *prefix, char *arg) +{ + if (strchr(arg, ' ') != NULL) + return (xsnprintf(buf, len, "%s\"%s\"", prefix, arg)); + return (xsnprintf(buf, len, "%s%s", prefix, arg)); +} + +int +cmd_do_flags(int opt, int iflags, int *oflags) +{ + switch (opt) { + case 'a': + if (iflags & CMD_AFLAG) { + (*oflags) |= CMD_AFLAG; + return (0); + } + return (-1); + case 'd': + if (iflags & CMD_DFLAG) { + (*oflags) |= CMD_DFLAG; + return (0); + } + return (-1); + case 'D': + if (iflags & CMD_BIGDFLAG) { + (*oflags) |= CMD_BIGDFLAG; + return (0); + } + return (-1); + case 'g': + if (iflags & CMD_GFLAG) { + (*oflags) |= CMD_GFLAG; + return (0); + } + return (-1); + case 'k': + if (iflags & CMD_KFLAG) { + (*oflags) |= CMD_KFLAG; + return (0); + } + return (-1); + case 'u': + if (iflags & CMD_UFLAG) { + (*oflags) |= CMD_UFLAG; + return (0); + } + return (-1); + case 'U': + if (iflags & CMD_BIGUFLAG) { + (*oflags) |= CMD_BIGUFLAG; + return (0); + } + return (-1); + } + return (1); +} + +size_t +cmd_print_flags(char *buf, size_t len, size_t off, int flags) +{ + size_t boff = off; + + if ((flags & CMD_FLAGMASK) == 0) + return (0); + off += xsnprintf(buf + off, len - off, " -"); + if (off < len && flags & CMD_AFLAG) + off += xsnprintf(buf + off, len - off, "a"); + if (off < len && flags & CMD_BIGDFLAG) + off += xsnprintf(buf + off, len - off, "D"); + if (off < len && flags & CMD_DFLAG) + off += xsnprintf(buf + off, len - off, "d"); + if (off < len && flags & CMD_GFLAG) + off += xsnprintf(buf + off, len - off, "g"); + if (off < len && flags & CMD_KFLAG) + off += xsnprintf(buf + off, len - off, "k"); + if (off < len && flags & CMD_UFLAG) + off += xsnprintf(buf + off, len - off, "u"); + if (off < len && flags & CMD_BIGUFLAG) + off += xsnprintf(buf + off, len - off, "U"); + return (off - boff); +} + +int +cmd_fill_argument(int flags, char **arg, int argc, char **argv) +{ + *arg = NULL; + + if (flags & CMD_ARG1) { + if (argc != 1) + return (-1); + *arg = xstrdup(argv[0]); + return (0); + } + + if (flags & CMD_ARG01) { + if (argc != 0 && argc != 1) + return (-1); + if (argc == 1) + *arg = xstrdup(argv[0]); + return (0); + } + + if (argc != 0) + return (-1); + return (0); +} + +void +cmd_target_init(struct cmd *self, unused int key) +{ + struct cmd_target_data *data; + + self->data = data = xmalloc(sizeof *data); + data->flags = 0; + data->target = NULL; + data->arg = NULL; +} + +int +cmd_target_parse(struct cmd *self, int argc, char **argv, char **cause) +{ + struct cmd_target_data *data; + int opt; + + /* Don't use the entry version since it may be dependent on key. */ + cmd_target_init(self, 0); + data = self->data; + + while ((opt = getopt(argc, argv, CMD_FLAGS "t:")) != -1) { + switch (cmd_do_flags(opt, self->entry->flags, &data->flags)) { + case -1: + goto usage; + case 0: + continue; + } + switch (opt) { + case 't': + if (data->target == NULL) + data->target = xstrdup(optarg); + break; + default: + goto usage; + } + } + argc -= optind; + argv += optind; + + if (cmd_fill_argument(self->entry->flags, &data->arg, argc, argv) != 0) + goto usage; + return (0); + +usage: + xasprintf(cause, "usage: %s %s", self->entry->name, self->entry->usage); + + self->entry->free(self); + return (-1); +} + +void +cmd_target_send(struct cmd *self, struct buffer *b) +{ + struct cmd_target_data *data = self->data; + + buffer_write(b, data, sizeof *data); + cmd_send_string(b, data->target); + cmd_send_string(b, data->arg); +} + +void +cmd_target_recv(struct cmd *self, struct buffer *b) +{ + struct cmd_target_data *data; + + self->data = data = xmalloc(sizeof *data); + buffer_read(b, data, sizeof *data); + data->target = cmd_recv_string(b); + data->arg = cmd_recv_string(b); +} + +void +cmd_target_free(struct cmd *self) +{ + struct cmd_target_data *data = self->data; + + if (data->target != NULL) + xfree(data->target); + if (data->arg != NULL) + xfree(data->arg); + xfree(data); +} + +size_t +cmd_target_print(struct cmd *self, char *buf, size_t len) +{ + struct cmd_target_data *data = self->data; + size_t off = 0; + + off += xsnprintf(buf, len, "%s", self->entry->name); + if (data == NULL) + return (off); + off += cmd_print_flags(buf, len, off, data->flags); + if (off < len && data->target != NULL) + off += cmd_prarg(buf + off, len - off, " -t ", data->target); + if (off < len && data->arg != NULL) + off += cmd_prarg(buf + off, len - off, " ", data->arg); + return (off); +} + +void +cmd_srcdst_init(struct cmd *self, unused int key) +{ + struct cmd_srcdst_data *data; + + self->data = data = xmalloc(sizeof *data); + data->flags = 0; + data->src = NULL; + data->dst = NULL; + data->arg = NULL; +} + +int +cmd_srcdst_parse(struct cmd *self, int argc, char **argv, char **cause) +{ + struct cmd_srcdst_data *data; + int opt; + + cmd_srcdst_init(self, 0); + data = self->data; + + while ((opt = getopt(argc, argv, CMD_FLAGS "s:t:")) != -1) { + switch (cmd_do_flags(opt, self->entry->flags, &data->flags)) { + case -1: + goto usage; + case 0: + continue; + } + switch (opt) { + case 's': + if (data->src == NULL) + data->src = xstrdup(optarg); + break; + case 't': + if (data->dst == NULL) + data->dst = xstrdup(optarg); + break; + default: + goto usage; + } + } + argc -= optind; + argv += optind; + + if (cmd_fill_argument(self->entry->flags, &data->arg, argc, argv) != 0) + goto usage; + return (0); + +usage: + xasprintf(cause, "usage: %s %s", self->entry->name, self->entry->usage); + + self->entry->free(self); + return (-1); +} + +void +cmd_srcdst_send(struct cmd *self, struct buffer *b) +{ + struct cmd_srcdst_data *data = self->data; + + buffer_write(b, data, sizeof *data); + cmd_send_string(b, data->src); + cmd_send_string(b, data->dst); + cmd_send_string(b, data->arg); +} + +void +cmd_srcdst_recv(struct cmd *self, struct buffer *b) +{ + struct cmd_srcdst_data *data; + + self->data = data = xmalloc(sizeof *data); + buffer_read(b, data, sizeof *data); + data->src = cmd_recv_string(b); + data->dst = cmd_recv_string(b); + data->arg = cmd_recv_string(b); +} + +void +cmd_srcdst_free(struct cmd *self) +{ + struct cmd_srcdst_data *data = self->data; + + if (data->src != NULL) + xfree(data->src); + if (data->dst != NULL) + xfree(data->dst); + if (data->arg != NULL) + xfree(data->arg); + xfree(data); +} + +size_t +cmd_srcdst_print(struct cmd *self, char *buf, size_t len) +{ + struct cmd_srcdst_data *data = self->data; + size_t off = 0; + + off += xsnprintf(buf, len, "%s", self->entry->name); + if (data == NULL) + return (off); + off += cmd_print_flags(buf, len, off, data->flags); + if (off < len && data->src != NULL) + off += xsnprintf(buf + off, len - off, " -s %s", data->src); + if (off < len && data->dst != NULL) + off += xsnprintf(buf + off, len - off, " -t %s", data->dst); + if (off < len && data->arg != NULL) + off += cmd_prarg(buf + off, len - off, " ", data->arg); + return (off); +} + +void +cmd_buffer_init(struct cmd *self, unused int key) +{ + struct cmd_buffer_data *data; + + self->data = data = xmalloc(sizeof *data); + data->flags = 0; + data->target = NULL; + data->buffer = -1; + data->arg = NULL; +} + +int +cmd_buffer_parse(struct cmd *self, int argc, char **argv, char **cause) +{ + struct cmd_buffer_data *data; + int opt, n; + const char *errstr; + + cmd_buffer_init(self, 0); + data = self->data; + + while ((opt = getopt(argc, argv, CMD_FLAGS "b:t:")) != -1) { + switch (cmd_do_flags(opt, self->entry->flags, &data->flags)) { + case -1: + goto usage; + case 0: + continue; + } + switch (opt) { + case 'b': + if (data->buffer == -1) { + n = strtonum(optarg, 0, INT_MAX, &errstr); + if (errstr != NULL) { + xasprintf(cause, "buffer %s", errstr); + goto error; + } + data->buffer = n; + } + break; + case 't': + if (data->target == NULL) + data->target = xstrdup(optarg); + break; + default: + goto usage; + } + } + argc -= optind; + argv += optind; + + if (cmd_fill_argument(self->entry->flags, &data->arg, argc, argv) != 0) + goto usage; + return (0); + +usage: + xasprintf(cause, "usage: %s %s", self->entry->name, self->entry->usage); + +error: + self->entry->free(self); + return (-1); +} + +void +cmd_buffer_send(struct cmd *self, struct buffer *b) +{ + struct cmd_buffer_data *data = self->data; + + buffer_write(b, data, sizeof *data); + cmd_send_string(b, data->target); + cmd_send_string(b, data->arg); +} + +void +cmd_buffer_recv(struct cmd *self, struct buffer *b) +{ + struct cmd_buffer_data *data; + + self->data = data = xmalloc(sizeof *data); + buffer_read(b, data, sizeof *data); + data->target = cmd_recv_string(b); + data->arg = cmd_recv_string(b); +} + +void +cmd_buffer_free(struct cmd *self) +{ + struct cmd_buffer_data *data = self->data; + + if (data->target != NULL) + xfree(data->target); + if (data->arg != NULL) + xfree(data->arg); + xfree(data); +} + +size_t +cmd_buffer_print(struct cmd *self, char *buf, size_t len) +{ + struct cmd_buffer_data *data = self->data; + size_t off = 0; + + off += xsnprintf(buf, len, "%s", self->entry->name); + if (data == NULL) + return (off); + off += cmd_print_flags(buf, len, off, data->flags); + if (off < len && data->buffer != -1) + off += xsnprintf(buf + off, len - off, " -b %d", data->buffer); + if (off < len && data->target != NULL) + off += cmd_prarg(buf + off, len - off, " -t ", data->target); + if (off < len && data->arg != NULL) + off += cmd_prarg(buf + off, len - off, " ", data->arg); + return (off); +} + +void +cmd_option_init(struct cmd *self, unused int key) +{ + struct cmd_option_data *data; + + self->data = data = xmalloc(sizeof *data); + data->flags = 0; + data->target = NULL; + data->option = NULL; + data->value = NULL; +} + +int +cmd_option_parse(struct cmd *self, int argc, char **argv, char **cause) +{ + struct cmd_option_data *data; + int opt; + + /* Don't use the entry version since it may be dependent on key. */ + cmd_option_init(self, 0); + data = self->data; + + while ((opt = getopt(argc, argv, CMD_FLAGS "t:")) != -1) { + switch (cmd_do_flags(opt, self->entry->flags, &data->flags)) { + case -1: + goto usage; + case 0: + continue; + } + switch (opt) { + case 't': + if (data->target == NULL) + data->target = xstrdup(optarg); + break; + default: + goto usage; + } + } + argc -= optind; + argv += optind; + + if (argc == 2) { + data->option = xstrdup(argv[0]); + data->value = xstrdup(argv[1]); + } else if (argc == 1) + data->option = xstrdup(argv[0]); + else + goto usage; + return (0); + +usage: + xasprintf(cause, "usage: %s %s", self->entry->name, self->entry->usage); + + self->entry->free(self); + return (-1); +} + +void +cmd_option_send(struct cmd *self, struct buffer *b) +{ + struct cmd_option_data *data = self->data; + + buffer_write(b, data, sizeof *data); + cmd_send_string(b, data->target); + cmd_send_string(b, data->option); + cmd_send_string(b, data->value); +} + +void +cmd_option_recv(struct cmd *self, struct buffer *b) +{ + struct cmd_option_data *data; + + self->data = data = xmalloc(sizeof *data); + buffer_read(b, data, sizeof *data); + data->target = cmd_recv_string(b); + data->option = cmd_recv_string(b); + data->value = cmd_recv_string(b); +} + +void +cmd_option_free(struct cmd *self) +{ + struct cmd_option_data *data = self->data; + + if (data->target != NULL) + xfree(data->target); + if (data->option != NULL) + xfree(data->option); + if (data->value != NULL) + xfree(data->value); + xfree(data); +} + +size_t +cmd_option_print(struct cmd *self, char *buf, size_t len) +{ + struct cmd_option_data *data = self->data; + size_t off = 0; + + off += xsnprintf(buf, len, "%s", self->entry->name); + if (data == NULL) + return (off); + off += cmd_print_flags(buf, len, off, data->flags); + if (off < len && data->target != NULL) + off += cmd_prarg(buf + off, len - off, " -t ", data->target); + if (off < len && data->option != NULL) + off += xsnprintf(buf + off, len - off, " %s", data->option); + if (off < len && data->value != NULL) + off += xsnprintf(buf + off, len - off, " %s", data->value); + return (off); +} + +void +cmd_pane_init(struct cmd *self, unused int key) +{ + struct cmd_pane_data *data; + + self->data = data = xmalloc(sizeof *data); + data->flags = 0; + data->target = NULL; + data->arg = NULL; + data->pane = -1; +} + +int +cmd_pane_parse(struct cmd *self, int argc, char **argv, char **cause) +{ + struct cmd_pane_data *data; + int opt, n; + const char *errstr; + + /* Don't use the entry version since it may be dependent on key. */ + cmd_pane_init(self, 0); + data = self->data; + + while ((opt = getopt(argc, argv, CMD_FLAGS "p:t:")) != -1) { + switch (cmd_do_flags(opt, self->entry->flags, &data->flags)) { + case -1: + goto usage; + case 0: + continue; + } + switch (opt) { + case 'p': + if (data->pane == -1) { + n = strtonum(optarg, 0, INT_MAX, &errstr); + if (errstr != NULL) { + xasprintf(cause, "pane %s", errstr); + goto error; + } + data->pane = n; + } + break; + case 't': + if (data->target == NULL) + data->target = xstrdup(optarg); + break; + default: + goto usage; + } + } + argc -= optind; + argv += optind; + + if (cmd_fill_argument(self->entry->flags, &data->arg, argc, argv) != 0) + goto usage; + return (0); + +usage: + xasprintf(cause, "usage: %s %s", self->entry->name, self->entry->usage); + +error: + self->entry->free(self); + return (-1); +} + +void +cmd_pane_send(struct cmd *self, struct buffer *b) +{ + struct cmd_pane_data *data = self->data; + + buffer_write(b, data, sizeof *data); + cmd_send_string(b, data->target); + cmd_send_string(b, data->arg); +} + +void +cmd_pane_recv(struct cmd *self, struct buffer *b) +{ + struct cmd_pane_data *data; + + self->data = data = xmalloc(sizeof *data); + buffer_read(b, data, sizeof *data); + data->target = cmd_recv_string(b); + data->arg = cmd_recv_string(b); +} + +void +cmd_pane_free(struct cmd *self) +{ + struct cmd_pane_data *data = self->data; + + if (data->target != NULL) + xfree(data->target); + if (data->arg != NULL) + xfree(data->arg); + xfree(data); +} + +size_t +cmd_pane_print(struct cmd *self, char *buf, size_t len) +{ + struct cmd_pane_data *data = self->data; + size_t off = 0; + + off += xsnprintf(buf, len, "%s", self->entry->name); + if (data == NULL) + return (off); + off += cmd_print_flags(buf, len, off, data->flags); + if (off < len && data->target != NULL) + off += cmd_prarg(buf + off, len - off, " -t ", data->target); + if (off < len && data->arg != NULL) + off += cmd_prarg(buf + off, len - off, " ", data->arg); + return (off); +} diff --git a/usr.bin/tmux/cmd-has-session.c b/usr.bin/tmux/cmd-has-session.c new file mode 100644 index 00000000000..ba46d768ebe --- /dev/null +++ b/usr.bin/tmux/cmd-has-session.c @@ -0,0 +1,51 @@ +/* $OpenBSD: cmd-has-session.c,v 1.1 2009/06/01 22:58:49 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 "tmux.h" + +/* + * Cause client to report an error and exit with 1 if session doesn't exist. + */ + +int cmd_has_session_exec(struct cmd *, struct cmd_ctx *); + +const struct cmd_entry cmd_has_session_entry = { + "has-session", "has", + CMD_TARGET_SESSION_USAGE, + 0, + cmd_target_init, + cmd_target_parse, + cmd_has_session_exec, + cmd_target_send, + cmd_target_recv, + cmd_target_free, + cmd_target_print +}; + +int +cmd_has_session_exec(struct cmd *self, struct cmd_ctx *ctx) +{ + struct cmd_target_data *data = self->data; + + if (cmd_find_session(ctx, data->target) == NULL) + return (-1); + + return (0); +} diff --git a/usr.bin/tmux/cmd-kill-pane.c b/usr.bin/tmux/cmd-kill-pane.c new file mode 100644 index 00000000000..c6ecea5aa7f --- /dev/null +++ b/usr.bin/tmux/cmd-kill-pane.c @@ -0,0 +1,72 @@ +/* $OpenBSD: cmd-kill-pane.c,v 1.1 2009/06/01 22:58:49 nicm 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 <stdlib.h> + +#include "tmux.h" + +/* + * Kill pane. + */ + +int cmd_kill_pane_exec(struct cmd *, struct cmd_ctx *); + +const struct cmd_entry cmd_kill_pane_entry = { + "kill-pane", "killp", + CMD_PANE_WINDOW_USAGE, + 0, + cmd_pane_init, + cmd_pane_parse, + cmd_kill_pane_exec, + cmd_pane_send, + cmd_pane_recv, + cmd_pane_free, + cmd_pane_print +}; + +int +cmd_kill_pane_exec(struct cmd *self, struct cmd_ctx *ctx) +{ + struct cmd_pane_data *data = self->data; + struct winlink *wl; + struct window_pane *wp; + + if ((wl = cmd_find_window(ctx, data->target, NULL)) == NULL) + return (-1); + if (data->pane == -1) + wp = wl->window->active; + else { + wp = window_pane_at_index(wl->window, data->pane); + if (wp == NULL) { + ctx->error(ctx, "no pane: %d", data->pane); + return (-1); + } + } + + if (window_count_panes(wl->window) == 1) { + ctx->error(ctx, "can't kill pane: %d", data->pane); + return (-1); + } + + window_remove_pane(wl->window, wp); + server_redraw_window(wl->window); + layout_refresh(wl->window, 0); + return (0); +} diff --git a/usr.bin/tmux/cmd-kill-server.c b/usr.bin/tmux/cmd-kill-server.c new file mode 100644 index 00000000000..e3d2e1ce82e --- /dev/null +++ b/usr.bin/tmux/cmd-kill-server.c @@ -0,0 +1,51 @@ +/* $OpenBSD: cmd-kill-server.c,v 1.1 2009/06/01 22:58:49 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 <signal.h> +#include <unistd.h> + +#include "tmux.h" + +/* + * Kill the server and do nothing else. + */ + +int cmd_kill_server_exec(struct cmd *, struct cmd_ctx *); + +const struct cmd_entry cmd_kill_server_entry = { + "kill-server", NULL, + "", + 0, + NULL, + NULL, + cmd_kill_server_exec, + NULL, + NULL, + NULL, + NULL +}; + +int +cmd_kill_server_exec(unused struct cmd *self, unused struct cmd_ctx *ctx) +{ + sigterm = 1; + + return (0); +} diff --git a/usr.bin/tmux/cmd-kill-session.c b/usr.bin/tmux/cmd-kill-session.c new file mode 100644 index 00000000000..7d63daea66e --- /dev/null +++ b/usr.bin/tmux/cmd-kill-session.c @@ -0,0 +1,68 @@ +/* $OpenBSD: cmd-kill-session.c,v 1.1 2009/06/01 22:58:49 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 "tmux.h" + +/* + * Destroy session, detaching all clients attached to it and destroying any + * windows linked only to this session. + * + * Note this deliberately has no alias to make it hard to hit by accident. + */ + +int cmd_kill_session_exec(struct cmd *, struct cmd_ctx *); + +const struct cmd_entry cmd_kill_session_entry = { + "kill-session", NULL, + CMD_TARGET_SESSION_USAGE, + 0, + cmd_target_init, + cmd_target_parse, + cmd_kill_session_exec, + cmd_target_send, + cmd_target_recv, + cmd_target_free, + cmd_target_print +}; + +int +cmd_kill_session_exec(struct cmd *self, struct cmd_ctx *ctx) +{ + struct cmd_target_data *data = self->data; + struct session *s; + struct client *c; + u_int i; + + if ((s = cmd_find_session(ctx, data->target)) == NULL) + return (-1); + + for (i = 0; i < ARRAY_LENGTH(&clients); i++) { + c = ARRAY_ITEM(&clients, i); + if (c->session == s) { + c->session = NULL; + server_write_client(c, MSG_EXIT, NULL, 0); + } + } + recalculate_sizes(); + + session_destroy(s); + + return (0); +} diff --git a/usr.bin/tmux/cmd-kill-window.c b/usr.bin/tmux/cmd-kill-window.c new file mode 100644 index 00000000000..d74d8eebbf6 --- /dev/null +++ b/usr.bin/tmux/cmd-kill-window.c @@ -0,0 +1,69 @@ +/* $OpenBSD: cmd-kill-window.c,v 1.1 2009/06/01 22:58:49 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 "tmux.h" + +/* + * Destroy window. + */ + +int cmd_kill_window_exec(struct cmd *, struct cmd_ctx *); + +const struct cmd_entry cmd_kill_window_entry = { + "kill-window", "killw", + CMD_TARGET_WINDOW_USAGE, + 0, + cmd_target_init, + cmd_target_parse, + cmd_kill_window_exec, + cmd_target_send, + cmd_target_recv, + cmd_target_free, + cmd_target_print +}; + +int +cmd_kill_window_exec(struct cmd *self, struct cmd_ctx *ctx) +{ + struct cmd_target_data *data = self->data; + struct winlink *wl; + struct session *s; + struct client *c; + u_int i; + int destroyed; + + if ((wl = cmd_find_window(ctx, data->target, &s)) == NULL) + return (-1); + + destroyed = session_detach(s, wl); + for (i = 0; i < ARRAY_LENGTH(&clients); i++) { + c = ARRAY_ITEM(&clients, i); + if (c == NULL || c->session != s) + continue; + if (destroyed) { + c->session = NULL; + server_write_client(c, MSG_EXIT, NULL, 0); + } else + server_redraw_client(c); + } + recalculate_sizes(); + + return (0); +} diff --git a/usr.bin/tmux/cmd-last-window.c b/usr.bin/tmux/cmd-last-window.c new file mode 100644 index 00000000000..6c77b4d5a92 --- /dev/null +++ b/usr.bin/tmux/cmd-last-window.c @@ -0,0 +1,60 @@ +/* $OpenBSD: cmd-last-window.c,v 1.1 2009/06/01 22:58:49 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 "tmux.h" + +/* + * Move to last window. + */ + +int cmd_last_window_exec(struct cmd *, struct cmd_ctx *); + +const struct cmd_entry cmd_last_window_entry = { + "last-window", "last", + CMD_TARGET_SESSION_USAGE, + 0, + cmd_target_init, + cmd_target_parse, + cmd_last_window_exec, + cmd_target_send, + cmd_target_recv, + cmd_target_free, + cmd_target_print +}; + +int +cmd_last_window_exec(struct cmd *self, struct cmd_ctx *ctx) +{ + struct cmd_target_data *data = self->data; + struct session *s; + + if ((s = cmd_find_session(ctx, data->target)) == NULL) + return (-1); + + if (session_last(s) == 0) + server_redraw_session(s); + else { + ctx->error(ctx, "no last window"); + return (-1); + } + recalculate_sizes(); + + return (0); +} diff --git a/usr.bin/tmux/cmd-link-window.c b/usr.bin/tmux/cmd-link-window.c new file mode 100644 index 00000000000..32144859fcf --- /dev/null +++ b/usr.bin/tmux/cmd-link-window.c @@ -0,0 +1,109 @@ +/* $OpenBSD: cmd-link-window.c,v 1.1 2009/06/01 22:58:49 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 <stdlib.h> + +#include "tmux.h" + +/* + * Link a window into another session. + */ + +int cmd_link_window_exec(struct cmd *, struct cmd_ctx *); + +const struct cmd_entry cmd_link_window_entry = { + "link-window", "linkw", + "[-dk] " CMD_SRCDST_WINDOW_USAGE, + CMD_DFLAG|CMD_KFLAG, + cmd_srcdst_init, + cmd_srcdst_parse, + cmd_link_window_exec, + cmd_srcdst_send, + cmd_srcdst_recv, + cmd_srcdst_free, + cmd_srcdst_print +}; + +int +cmd_link_window_exec(struct cmd *self, struct cmd_ctx *ctx) +{ + struct cmd_srcdst_data *data = self->data; + struct session *dst; + struct winlink *wl_src, *wl_dst; + char *cause; + int idx; + + if ((wl_src = cmd_find_window(ctx, data->src, NULL)) == NULL) + return (-1); + + if (arg_parse_window(data->dst, &dst, &idx) != 0) { + ctx->error(ctx, "bad window: %s", data->dst); + return (-1); + } + if (dst == NULL) + dst = ctx->cursession; + if (dst == NULL) + dst = cmd_current_session(ctx); + if (dst == NULL) { + ctx->error(ctx, "session not found: %s", data->dst); + return (-1); + } + + wl_dst = NULL; + if (idx != -1) + wl_dst = winlink_find_by_index(&dst->windows, idx); + if (wl_dst != NULL) { + if (wl_dst->window == wl_src->window) + return (0); + + if (data->flags & CMD_KFLAG) { + /* + * Can't use session_detach as it will destroy session + * if this makes it empty. + */ + session_alert_cancel(dst, wl_dst); + winlink_stack_remove(&dst->lastw, wl_dst); + winlink_remove(&dst->windows, wl_dst); + + /* Force select/redraw if current. */ + if (wl_dst == dst->curw) { + data->flags &= ~CMD_DFLAG; + dst->curw = NULL; + } + } + } + + wl_dst = session_attach(dst, wl_src->window, idx, &cause); + if (wl_dst == NULL) { + ctx->error(ctx, "create session failed: %s", cause); + xfree(cause); + return (-1); + } + + if (data->flags & CMD_DFLAG) + server_status_session(dst); + else { + session_select(dst, wl_dst->idx); + server_redraw_session(dst); + } + recalculate_sizes(); + + return (0); +} diff --git a/usr.bin/tmux/cmd-list-buffers.c b/usr.bin/tmux/cmd-list-buffers.c new file mode 100644 index 00000000000..50a0fa858c5 --- /dev/null +++ b/usr.bin/tmux/cmd-list-buffers.c @@ -0,0 +1,91 @@ +/* $OpenBSD: cmd-list-buffers.c,v 1.1 2009/06/01 22:58:49 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 <string.h> + +#include "tmux.h" + +/* + * List paste buffers. + */ + +int cmd_list_buffers_exec(struct cmd *, struct cmd_ctx *); + +const struct cmd_entry cmd_list_buffers_entry = { + "list-buffers", "lsb", + CMD_TARGET_SESSION_USAGE, + 0, + cmd_target_init, + cmd_target_parse, + cmd_list_buffers_exec, + cmd_target_send, + cmd_target_recv, + cmd_target_free, + cmd_target_print +}; + +int +cmd_list_buffers_exec(struct cmd *self, struct cmd_ctx *ctx) +{ + struct cmd_target_data *data = self->data; + struct session *s; + struct paste_buffer *pb; + u_int idx; + char *tmp; + size_t size, in, out; + + if ((s = cmd_find_session(ctx, data->target)) == NULL) + return (-1); + + if (s->sx > 35) { /* leave three for ... */ + size = s->sx - 32; + tmp = xmalloc(size + 1); + } else { + size = 0; + tmp = NULL; + } + + idx = 0; + while ((pb = paste_walk_stack(&s->buffers, &idx)) != NULL) { + if (tmp != NULL) { + in = out = 0; + while (out < size && pb->data[in] != '\0') { + if (pb->data[in] > 31 && pb->data[in] != 127) + tmp[out++] = pb->data[in]; + in++; + } + tmp[out] = '\0'; + if (out == size) { + tmp[out - 1] = '.'; + tmp[out - 2] = '.'; + tmp[out - 3] = '.'; + } + + ctx->print(ctx, "%d: %zu bytes: \"%s\"", + idx - 1, strlen(pb->data), tmp); + } else + ctx->print(ctx, "%d: %zu bytes", idx, strlen(pb->data)); + } + + if (tmp != NULL) + xfree(tmp); + + return (0); +} diff --git a/usr.bin/tmux/cmd-list-clients.c b/usr.bin/tmux/cmd-list-clients.c new file mode 100644 index 00000000000..e16bf6d007a --- /dev/null +++ b/usr.bin/tmux/cmd-list-clients.c @@ -0,0 +1,67 @@ +/* $OpenBSD: cmd-list-clients.c,v 1.1 2009/06/01 22:58:49 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 <string.h> +#include <time.h> + +#include "tmux.h" + +/* + * List all clients. + */ + +int cmd_list_clients_exec(struct cmd *, struct cmd_ctx *); + +const struct cmd_entry cmd_list_clients_entry = { + "list-clients", "lsc", + "", + 0, + NULL, + NULL, + cmd_list_clients_exec, + NULL, + NULL, + NULL, + NULL +}; + +int +cmd_list_clients_exec(unused struct cmd *self, struct cmd_ctx *ctx) +{ + struct client *c; + u_int i; + const char *s_utf8; + + for (i = 0; i < ARRAY_LENGTH(&clients); i++) { + c = ARRAY_ITEM(&clients, i); + if (c == NULL || c->session == NULL) + continue; + + if (c->tty.flags & TTY_UTF8) + s_utf8 = " (utf8)"; + else + s_utf8 = ""; + ctx->print(ctx, "%s: %s [%ux%u %s]%s", c->tty.path, + c->session->name, c->tty.sx, c->tty.sy, + c->tty.termname, s_utf8); + } + + return (0); +} diff --git a/usr.bin/tmux/cmd-list-commands.c b/usr.bin/tmux/cmd-list-commands.c new file mode 100644 index 00000000000..300df4d174a --- /dev/null +++ b/usr.bin/tmux/cmd-list-commands.c @@ -0,0 +1,51 @@ +/* $OpenBSD: cmd-list-commands.c,v 1.1 2009/06/01 22:58:49 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 "tmux.h" + +/* + * List all commands with usages. + */ + +int cmd_list_commands_exec(struct cmd *, struct cmd_ctx *); + +const struct cmd_entry cmd_list_commands_entry = { + "list-commands", "lscm", + "", + 0, + NULL, + NULL, + cmd_list_commands_exec, + NULL, + NULL, + NULL, + NULL +}; + +int +cmd_list_commands_exec(unused struct cmd *self, struct cmd_ctx *ctx) +{ + const struct cmd_entry **entryp; + + for (entryp = cmd_table; *entryp != NULL; entryp++) + ctx->print(ctx, "%s %s", (*entryp)->name, (*entryp)->usage); + + return (0); +} diff --git a/usr.bin/tmux/cmd-list-keys.c b/usr.bin/tmux/cmd-list-keys.c new file mode 100644 index 00000000000..fa756582319 --- /dev/null +++ b/usr.bin/tmux/cmd-list-keys.c @@ -0,0 +1,59 @@ +/* $OpenBSD: cmd-list-keys.c,v 1.1 2009/06/01 22:58:49 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 "tmux.h" + +/* + * List key bindings. + */ + +int cmd_list_keys_exec(struct cmd *, struct cmd_ctx *); + +const struct cmd_entry cmd_list_keys_entry = { + "list-keys", "lsk", + "", + 0, + NULL, + NULL, + cmd_list_keys_exec, + NULL, + NULL, + NULL, + NULL +}; + +int +cmd_list_keys_exec(unused struct cmd *self, struct cmd_ctx *ctx) +{ + struct key_binding *bd; + const char *key; + char tmp[BUFSIZ]; + + SPLAY_FOREACH(bd, key_bindings, &key_bindings) { + if ((key = key_string_lookup_key(bd->key)) == NULL) + continue; + + *tmp = '\0'; + cmd_list_print(bd->cmdlist, tmp, sizeof tmp); + ctx->print(ctx, "%11s: %s", key, tmp); + } + + return (0); +} diff --git a/usr.bin/tmux/cmd-list-sessions.c b/usr.bin/tmux/cmd-list-sessions.c new file mode 100644 index 00000000000..4cd4f2f7a35 --- /dev/null +++ b/usr.bin/tmux/cmd-list-sessions.c @@ -0,0 +1,67 @@ +/* $OpenBSD: cmd-list-sessions.c,v 1.1 2009/06/01 22:58:49 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 <string.h> +#include <time.h> + +#include "tmux.h" + +/* + * List all sessions. + */ + +int cmd_list_sessions_exec(struct cmd *, struct cmd_ctx *); + +const struct cmd_entry cmd_list_sessions_entry = { + "list-sessions", "ls", "", + 0, + NULL, + NULL, + cmd_list_sessions_exec, + NULL, + NULL, + NULL, + NULL +}; + +int +cmd_list_sessions_exec(unused struct cmd *self, struct cmd_ctx *ctx) +{ + struct session *s; + char *tim; + u_int i; + time_t t; + + for (i = 0; i < ARRAY_LENGTH(&sessions); i++) { + s = ARRAY_ITEM(&sessions, i); + if (s == NULL) + continue; + + t = s->tv.tv_sec; + tim = ctime(&t); + *strchr(tim, '\n') = '\0'; + + ctx->print(ctx, "%s: %u windows (created %s) [%ux%u]%s", + s->name, winlink_count(&s->windows), tim, s->sx, s->sy, + s->flags & SESSION_UNATTACHED ? "" : " (attached)"); + } + + return (0); +} diff --git a/usr.bin/tmux/cmd-list-windows.c b/usr.bin/tmux/cmd-list-windows.c new file mode 100644 index 00000000000..b5686635241 --- /dev/null +++ b/usr.bin/tmux/cmd-list-windows.c @@ -0,0 +1,88 @@ +/* $OpenBSD: cmd-list-windows.c,v 1.1 2009/06/01 22:58:49 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 <unistd.h> + +#include "tmux.h" + +/* + * List windows on given session. + */ + +int cmd_list_windows_exec(struct cmd *, struct cmd_ctx *); + +const struct cmd_entry cmd_list_windows_entry = { + "list-windows", "lsw", + CMD_TARGET_SESSION_USAGE, + 0, + cmd_target_init, + cmd_target_parse, + cmd_list_windows_exec, + cmd_target_send, + cmd_target_recv, + cmd_target_free, + cmd_target_print +}; + +int +cmd_list_windows_exec(struct cmd *self, struct cmd_ctx *ctx) +{ + struct cmd_target_data *data = self->data; + struct session *s; + struct winlink *wl; + struct window *w; + struct window_pane *wp; + struct grid *gd; + u_int i; + unsigned long long size; + const char *name; + + if ((s = cmd_find_session(ctx, data->target)) == NULL) + return (-1); + + RB_FOREACH(wl, winlinks, &s->windows) { + w = wl->window; + ctx->print(ctx, + "%3d: %s [%ux%u]", wl->idx, w->name, w->sx, w->sy); + + TAILQ_FOREACH(wp, &w->panes, entry) { + gd = wp->base.grid; + + size = 0; + for (i = 0; i < gd->hsize; i++) { + size += gd->size[i] * sizeof **gd->data; + size += gd->usize[i] * sizeof **gd->udata; + } + size += gd->hsize * (sizeof *gd->data); + size += gd->hsize * (sizeof *gd->size); + + if (wp->fd != -1) + name = ttyname(wp->fd); + else + name = "unknown"; + ctx->print(ctx, + " %s [%ux%u %s] [history %u/%u, %llu bytes]", + name, wp->sx, wp->sy, layout_name(w), gd->hsize, + gd->hlimit, size); + } + } + + return (0); +} diff --git a/usr.bin/tmux/cmd-list.c b/usr.bin/tmux/cmd-list.c new file mode 100644 index 00000000000..2d778e25e31 --- /dev/null +++ b/usr.bin/tmux/cmd-list.c @@ -0,0 +1,154 @@ +/* $OpenBSD: cmd-list.c,v 1.1 2009/06/01 22:58:49 nicm 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 <string.h> + +#include "tmux.h" + +struct cmd_list * +cmd_list_parse(int argc, char **argv, char **cause) +{ + struct cmd_list *cmdlist; + struct cmd *cmd; + int i, lastsplit; + size_t arglen, new_argc; + char **new_argv; + + cmdlist = xmalloc(sizeof *cmdlist); + TAILQ_INIT(cmdlist); + + lastsplit = 0; + for (i = 0; i < argc; i++) { + arglen = strlen(argv[i]); + if (arglen == 0 || argv[i][arglen - 1] != ';') + continue; + argv[i][arglen - 1] = '\0'; + + if (arglen > 1 && argv[i][arglen - 2] == '\\') { + argv[i][arglen - 2] = ';'; + continue; + } + + new_argc = i - lastsplit; + new_argv = argv + lastsplit; + if (arglen != 1) + new_argc++; + + cmd = cmd_parse(new_argc, new_argv, cause); + if (cmd == NULL) + goto bad; + TAILQ_INSERT_TAIL(cmdlist, cmd, qentry); + + lastsplit = i + 1; + } + + if (lastsplit != argc) { + cmd = cmd_parse(argc - lastsplit, argv + lastsplit, cause); + if (cmd == NULL) + goto bad; + TAILQ_INSERT_TAIL(cmdlist, cmd, qentry); + } + + return (cmdlist); + +bad: + cmd_list_free(cmdlist); + return (NULL); +} + +int +cmd_list_exec(struct cmd_list *cmdlist, struct cmd_ctx *ctx) +{ + struct cmd *cmd; + int n; + + TAILQ_FOREACH(cmd, cmdlist, qentry) { + if ((n = cmd_exec(cmd, ctx)) != 0) + return (n); + } + return (0); +} + +void +cmd_list_send(struct cmd_list *cmdlist, struct buffer *b) +{ + struct cmd *cmd; + u_int n; + + n = 0; + TAILQ_FOREACH(cmd, cmdlist, qentry) + n++; + + buffer_write(b, &n, sizeof n); + TAILQ_FOREACH(cmd, cmdlist, qentry) + cmd_send(cmd, b); +} + +struct cmd_list * +cmd_list_recv(struct buffer *b) +{ + struct cmd_list *cmdlist; + struct cmd *cmd; + u_int n; + + buffer_read(b, &n, sizeof n); + + cmdlist = xmalloc(sizeof *cmdlist); + TAILQ_INIT(cmdlist); + + while (n-- > 0) { + cmd = cmd_recv(b); + TAILQ_INSERT_TAIL(cmdlist, cmd, qentry); + } + + return (cmdlist); +} + +void +cmd_list_free(struct cmd_list *cmdlist) +{ + struct cmd *cmd; + + while (!TAILQ_EMPTY(cmdlist)) { + cmd = TAILQ_FIRST(cmdlist); + TAILQ_REMOVE(cmdlist, cmd, qentry); + cmd_free(cmd); + } + xfree(cmdlist); +} + +size_t +cmd_list_print(struct cmd_list *cmdlist, char *buf, size_t len) +{ + struct cmd *cmd; + size_t off; + + off = 0; + TAILQ_FOREACH(cmd, cmdlist, qentry) { + if (off >= len) + break; + off += cmd_print(cmd, buf + off, len - off); + if (off >= len) + break; + if (TAILQ_NEXT(cmd, qentry) != NULL) + off += xsnprintf(buf + off, len - off, " ; "); + } + return (off); +} diff --git a/usr.bin/tmux/cmd-load-buffer.c b/usr.bin/tmux/cmd-load-buffer.c new file mode 100644 index 00000000000..5ac0246cd13 --- /dev/null +++ b/usr.bin/tmux/cmd-load-buffer.c @@ -0,0 +1,106 @@ +/* $OpenBSD: cmd-load-buffer.c,v 1.1 2009/06/01 22:58:49 nicm Exp $ */ + +/* + * Copyright (c) 2009 Tiago Cunha <me@tiagocunha.org> + * + * 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/stat.h> + +#include <errno.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include "tmux.h" + +/* + * Loads a session paste buffer from a file. + */ + +int cmd_load_buffer_exec(struct cmd *, struct cmd_ctx *); + +const struct cmd_entry cmd_load_buffer_entry = { + "load-buffer", "loadb", + CMD_BUFFER_SESSION_USAGE " path", + CMD_ARG1, + cmd_buffer_init, + cmd_buffer_parse, + cmd_load_buffer_exec, + cmd_buffer_send, + cmd_buffer_recv, + cmd_buffer_free, + cmd_buffer_print +}; + +int +cmd_load_buffer_exec(struct cmd *self, struct cmd_ctx *ctx) +{ + struct cmd_buffer_data *data = self->data; + struct session *s; + struct stat statbuf; + FILE *f; + char *buf; + u_int limit; + + if ((s = cmd_find_session(ctx, data->target)) == NULL) + return (-1); + + if (stat(data->arg, &statbuf) < 0) { + ctx->error(ctx, "%s: %s", data->arg, strerror(errno)); + return (-1); + } + if (!S_ISREG(statbuf.st_mode)) { + ctx->error(ctx, "%s: not a regular file", data->arg); + return (-1); + } + + if ((f = fopen(data->arg, "rb")) == NULL) { + ctx->error(ctx, "%s: %s", data->arg, strerror(errno)); + return (-1); + } + + /* + * We don't want to die due to memory exhaustion, hence xmalloc can't + * be used here. + */ + if ((buf = malloc(statbuf.st_size + 1)) == NULL) { + ctx->error(ctx, "malloc error: %s", strerror(errno)); + return (-1); + } + + if (fread(buf, 1, statbuf.st_size, f) != (size_t) statbuf.st_size) { + ctx->error(ctx, "%s: fread error", data->arg); + xfree(buf); + fclose(f); + return (-1); + } + + buf[statbuf.st_size] = '\0'; + fclose(f); + + limit = options_get_number(&s->options, "buffer-limit"); + if (data->buffer == -1) { + paste_add(&s->buffers, buf, limit); + return (0); + } + if (paste_replace(&s->buffers, data->buffer, buf) != 0) { + ctx->error(ctx, "no buffer %d", data->buffer); + xfree(buf); + return (-1); + } + + return (0); +} diff --git a/usr.bin/tmux/cmd-lock-server.c b/usr.bin/tmux/cmd-lock-server.c new file mode 100644 index 00000000000..e0c3ab60655 --- /dev/null +++ b/usr.bin/tmux/cmd-lock-server.c @@ -0,0 +1,54 @@ +/* $OpenBSD: cmd-lock-server.c,v 1.1 2009/06/01 22:58:49 nicm Exp $ */ + +/* + * Copyright (c) 2008 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 <pwd.h> +#include <string.h> +#include <unistd.h> + +#include "tmux.h" + +/* + * Lock server. + */ + +int cmd_lock_server_exec(struct cmd *, struct cmd_ctx *); + +int cmd_lock_server_callback(void *, const char *); + +const struct cmd_entry cmd_lock_server_entry = { + "lock-server", "lock", + "", + 0, + NULL, + NULL, + cmd_lock_server_exec, + NULL, + NULL, + NULL, + NULL, +}; + +int +cmd_lock_server_exec(unused struct cmd *self, unused struct cmd_ctx *ctx) +{ + server_lock(); + + return (0); +} diff --git a/usr.bin/tmux/cmd-move-window.c b/usr.bin/tmux/cmd-move-window.c new file mode 100644 index 00000000000..bde3ed61380 --- /dev/null +++ b/usr.bin/tmux/cmd-move-window.c @@ -0,0 +1,123 @@ +/* $OpenBSD: cmd-move-window.c,v 1.1 2009/06/01 22:58:49 nicm Exp $ */ + +/* + * Copyright (c) 2008 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 <stdlib.h> + +#include "tmux.h" + +/* + * Move a window. + */ + +int cmd_move_window_exec(struct cmd *, struct cmd_ctx *); + +const struct cmd_entry cmd_move_window_entry = { + "move-window", "movew", + "[-dk] " CMD_SRCDST_WINDOW_USAGE, + CMD_DFLAG|CMD_KFLAG, + cmd_srcdst_init, + cmd_srcdst_parse, + cmd_move_window_exec, + cmd_srcdst_send, + cmd_srcdst_recv, + cmd_srcdst_free, + cmd_srcdst_print +}; + +int +cmd_move_window_exec(struct cmd *self, struct cmd_ctx *ctx) +{ + struct cmd_srcdst_data *data = self->data; + struct session *src, *dst; + struct winlink *wl_src, *wl_dst; + struct client *c; + u_int i; + int destroyed, idx; + char *cause; + + if ((wl_src = cmd_find_window(ctx, data->src, &src)) == NULL) + return (-1); + + if (arg_parse_window(data->dst, &dst, &idx) != 0) { + ctx->error(ctx, "bad window: %s", data->dst); + return (-1); + } + if (dst == NULL) + dst = ctx->cursession; + if (dst == NULL) + dst = cmd_current_session(ctx); + if (dst == NULL) { + ctx->error(ctx, "session not found: %s", data->dst); + return (-1); + } + + wl_dst = NULL; + if (idx != -1) + wl_dst = winlink_find_by_index(&dst->windows, idx); + if (wl_dst != NULL) { + if (wl_dst->window == wl_src->window) + return (0); + + if (data->flags & CMD_KFLAG) { + /* + * Can't use session_detach as it will destroy session + * if this makes it empty. + */ + session_alert_cancel(dst, wl_dst); + winlink_stack_remove(&dst->lastw, wl_dst); + winlink_remove(&dst->windows, wl_dst); + + /* Force select/redraw if current. */ + if (wl_dst == dst->curw) { + data->flags &= ~CMD_DFLAG; + dst->curw = NULL; + } + } + } + + wl_dst = session_attach(dst, wl_src->window, idx, &cause); + if (wl_dst == NULL) { + ctx->error(ctx, "attach window failed: %s", cause); + xfree(cause); + return (-1); + } + + destroyed = session_detach(src, wl_src); + for (i = 0; i < ARRAY_LENGTH(&clients); i++) { + c = ARRAY_ITEM(&clients, i); + if (c == NULL || c->session != src) + continue; + if (destroyed) { + c->session = NULL; + server_write_client(c, MSG_EXIT, NULL, 0); + } else + server_redraw_client(c); + } + + if (data->flags & CMD_DFLAG) + server_status_session(dst); + else { + session_select(dst, wl_dst->idx); + server_redraw_session(dst); + } + recalculate_sizes(); + + return (0); +} diff --git a/usr.bin/tmux/cmd-new-session.c b/usr.bin/tmux/cmd-new-session.c new file mode 100644 index 00000000000..fbf8c286074 --- /dev/null +++ b/usr.bin/tmux/cmd-new-session.c @@ -0,0 +1,248 @@ +/* $OpenBSD: cmd-new-session.c,v 1.1 2009/06/01 22:58:49 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 "tmux.h" + +/* + * Create a new session and attach to the current terminal unless -d is given. + */ + +int cmd_new_session_parse(struct cmd *, int, char **, char **); +int cmd_new_session_exec(struct cmd *, struct cmd_ctx *); +void cmd_new_session_send(struct cmd *, struct buffer *); +void cmd_new_session_recv(struct cmd *, struct buffer *); +void cmd_new_session_free(struct cmd *); +void cmd_new_session_init(struct cmd *, int); +size_t cmd_new_session_print(struct cmd *, char *, size_t); + +struct cmd_new_session_data { + char *newname; + char *winname; + char *cmd; + int flag_detached; +}; + +const struct cmd_entry cmd_new_session_entry = { + "new-session", "new", + "[-d] [-n window-name] [-s session-name] [command]", + CMD_STARTSERVER|CMD_CANTNEST, + cmd_new_session_init, + cmd_new_session_parse, + cmd_new_session_exec, + cmd_new_session_send, + cmd_new_session_recv, + cmd_new_session_free, + cmd_new_session_print +}; + +void +cmd_new_session_init(struct cmd *self, unused int arg) +{ + struct cmd_new_session_data *data; + + self->data = data = xmalloc(sizeof *data); + data->flag_detached = 0; + data->newname = NULL; + data->winname = NULL; + data->cmd = NULL; +} + +int +cmd_new_session_parse(struct cmd *self, int argc, char **argv, char **cause) +{ + struct cmd_new_session_data *data; + int opt; + + self->entry->init(self, 0); + data = self->data; + + while ((opt = getopt(argc, argv, "ds:n:")) != -1) { + switch (opt) { + case 'd': + data->flag_detached = 1; + break; + case 's': + if (data->newname == NULL) + data->newname = xstrdup(optarg); + break; + case 'n': + if (data->winname == NULL) + data->winname = xstrdup(optarg); + break; + default: + goto usage; + } + } + argc -= optind; + argv += optind; + if (argc != 0 && argc != 1) + goto usage; + + if (argc == 1) + data->cmd = xstrdup(argv[0]); + + return (0); + +usage: + xasprintf(cause, "usage: %s %s", self->entry->name, self->entry->usage); + + self->entry->free(self); + return (-1); +} + +int +cmd_new_session_exec(struct cmd *self, struct cmd_ctx *ctx) +{ + struct cmd_new_session_data *data = self->data; + struct client *c = ctx->cmdclient; + struct session *s; + char *cmd, *cwd, *cause; + u_int sx, sy; + + if (ctx->curclient != NULL) + return (0); + + if (!data->flag_detached) { + if (c == NULL) { + ctx->error(ctx, "no client to attach to"); + return (-1); + } + if (!(c->flags & CLIENT_TERMINAL)) { + ctx->error(ctx, "not a terminal"); + return (-1); + } + } + + if (data->newname != NULL && session_find(data->newname) != NULL) { + ctx->error(ctx, "duplicate session: %s", data->newname); + return (-1); + } + + cmd = data->cmd; + if (cmd == NULL) + cmd = options_get_string(&global_options, "default-command"); + if (c == NULL || c->cwd == NULL) + cwd = options_get_string(&global_options, "default-path"); + else + cwd = c->cwd; + + sx = 80; + sy = 25; + if (!data->flag_detached) { + sx = c->tty.sx; + sy = c->tty.sy; + } + + if (options_get_number(&global_options, "status")) { + if (sy == 0) + sy = 1; + else + sy--; + } + + if (!data->flag_detached && tty_open(&c->tty, &cause) != 0) { + ctx->error(ctx, "open terminal failed: %s", cause); + xfree(cause); + return (-1); + } + + + s = session_create(data->newname, cmd, cwd, sx, sy, &cause); + if (s == NULL) { + ctx->error(ctx, "create session failed: %s", cause); + xfree(cause); + return (-1); + } + if (data->winname != NULL) { + xfree(s->curw->window->name); + s->curw->window->name = xstrdup(data->winname); + options_set_number( + &s->curw->window->options, "automatic-rename", 0); + } + + if (data->flag_detached) { + if (c != NULL) + server_write_client(c, MSG_EXIT, NULL, 0); + } else { + c->session = s; + server_write_client(c, MSG_READY, NULL, 0); + server_redraw_client(c); + } + recalculate_sizes(); + + return (1); +} + +void +cmd_new_session_send(struct cmd *self, struct buffer *b) +{ + struct cmd_new_session_data *data = self->data; + + buffer_write(b, data, sizeof *data); + cmd_send_string(b, data->newname); + cmd_send_string(b, data->winname); + cmd_send_string(b, data->cmd); +} + +void +cmd_new_session_recv(struct cmd *self, struct buffer *b) +{ + struct cmd_new_session_data *data; + + self->data = data = xmalloc(sizeof *data); + buffer_read(b, data, sizeof *data); + data->newname = cmd_recv_string(b); + data->winname = cmd_recv_string(b); + data->cmd = cmd_recv_string(b); +} + +void +cmd_new_session_free(struct cmd *self) +{ + struct cmd_new_session_data *data = self->data; + + if (data->newname != NULL) + xfree(data->newname); + if (data->winname != NULL) + xfree(data->winname); + if (data->cmd != NULL) + xfree(data->cmd); + xfree(data); +} + +size_t +cmd_new_session_print(struct cmd *self, char *buf, size_t len) +{ + struct cmd_new_session_data *data = self->data; + size_t off = 0; + + off += xsnprintf(buf, len, "%s", self->entry->name); + if (data == NULL) + return (off); + if (off < len && data->flag_detached) + off += xsnprintf(buf + off, len - off, " -d"); + if (off < len && data->newname != NULL) + off += cmd_prarg(buf + off, len - off, " -s ", data->newname); + if (off < len && data->winname != NULL) + off += cmd_prarg(buf + off, len - off, " -n ", data->winname); + if (off < len && data->cmd != NULL) + off += cmd_prarg(buf + off, len - off, " ", data->cmd); + return (off); +} diff --git a/usr.bin/tmux/cmd-new-window.c b/usr.bin/tmux/cmd-new-window.c new file mode 100644 index 00000000000..3711ff65b46 --- /dev/null +++ b/usr.bin/tmux/cmd-new-window.c @@ -0,0 +1,241 @@ +/* $OpenBSD: cmd-new-window.c,v 1.1 2009/06/01 22:58:49 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 <stdlib.h> + +#include "tmux.h" + +/* + * Create a new window. + */ + +int cmd_new_window_parse(struct cmd *, int, char **, char **); +int cmd_new_window_exec(struct cmd *, struct cmd_ctx *); +void cmd_new_window_send(struct cmd *, struct buffer *); +void cmd_new_window_recv(struct cmd *, struct buffer *); +void cmd_new_window_free(struct cmd *); +void cmd_new_window_init(struct cmd *, int); +size_t cmd_new_window_print(struct cmd *, char *, size_t); + +struct cmd_new_window_data { + char *target; + char *name; + char *cmd; + int flag_detached; + int flag_kill; +}; + +const struct cmd_entry cmd_new_window_entry = { + "new-window", "neww", + "[-dk] [-n window-name] [-t target-window] [command]", + 0, + cmd_new_window_init, + cmd_new_window_parse, + cmd_new_window_exec, + cmd_new_window_send, + cmd_new_window_recv, + cmd_new_window_free, + cmd_new_window_print +}; + +void +cmd_new_window_init(struct cmd *self, unused int arg) +{ + struct cmd_new_window_data *data; + + self->data = data = xmalloc(sizeof *data); + data->target = NULL; + data->name = NULL; + data->cmd = NULL; + data->flag_detached = 0; + data->flag_kill = 0; +} + +int +cmd_new_window_parse(struct cmd *self, int argc, char **argv, char **cause) +{ + struct cmd_new_window_data *data; + int opt; + + self->entry->init(self, 0); + data = self->data; + + while ((opt = getopt(argc, argv, "dkt:n:")) != -1) { + switch (opt) { + case 'd': + data->flag_detached = 1; + break; + case 'k': + data->flag_kill = 1; + break; + case 't': + if (data->target == NULL) + data->target = xstrdup(optarg); + break; + case 'n': + if (data->name == NULL) + data->name = xstrdup(optarg); + break; + default: + goto usage; + } + } + argc -= optind; + argv += optind; + if (argc != 0 && argc != 1) + goto usage; + + if (argc == 1) + data->cmd = xstrdup(argv[0]); + + return (0); + +usage: + xasprintf(cause, "usage: %s %s", self->entry->name, self->entry->usage); + + self->entry->free(self); + return (-1); +} + +int +cmd_new_window_exec(struct cmd *self, struct cmd_ctx *ctx) +{ + struct cmd_new_window_data *data = self->data; + struct session *s; + struct winlink *wl; + char *cmd, *cwd, *cause; + int idx; + + if (data == NULL) + return (0); + + if (arg_parse_window(data->target, &s, &idx) != 0) { + ctx->error(ctx, "bad window: %s", data->target); + return (-1); + } + if (s == NULL) + s = ctx->cursession; + if (s == NULL) + s = cmd_current_session(ctx); + if (s == NULL) { + ctx->error(ctx, "session not found: %s", data->target); + return (-1); + } + + wl = NULL; + if (idx != -1) + wl = winlink_find_by_index(&s->windows, idx); + if (wl != NULL) { + if (data->flag_kill) { + /* + * Can't use session_detach as it will destroy session + * if this makes it empty. + */ + session_alert_cancel(s, wl); + winlink_stack_remove(&s->lastw, wl); + winlink_remove(&s->windows, wl); + + /* Force select/redraw if current. */ + if (wl == s->curw) { + data->flag_detached = 0; + s->curw = NULL; + } + } + } + + cmd = data->cmd; + if (cmd == NULL) + cmd = options_get_string(&s->options, "default-command"); + if (ctx->cmdclient == NULL || ctx->cmdclient->cwd == NULL) + cwd = options_get_string(&global_options, "default-path"); + else + cwd = ctx->cmdclient->cwd; + + wl = session_new(s, data->name, cmd, cwd, idx, &cause); + if (wl == NULL) { + ctx->error(ctx, "create window failed: %s", cause); + xfree(cause); + return (-1); + } + if (!data->flag_detached) { + session_select(s, wl->idx); + server_redraw_session(s); + } else + server_status_session(s); + + return (0); +} + +void +cmd_new_window_send(struct cmd *self, struct buffer *b) +{ + struct cmd_new_window_data *data = self->data; + + buffer_write(b, data, sizeof *data); + cmd_send_string(b, data->target); + cmd_send_string(b, data->name); + cmd_send_string(b, data->cmd); +} + +void +cmd_new_window_recv(struct cmd *self, struct buffer *b) +{ + struct cmd_new_window_data *data; + + self->data = data = xmalloc(sizeof *data); + buffer_read(b, data, sizeof *data); + data->target = cmd_recv_string(b); + data->name = cmd_recv_string(b); + data->cmd = cmd_recv_string(b); +} + +void +cmd_new_window_free(struct cmd *self) +{ + struct cmd_new_window_data *data = self->data; + + if (data->target != NULL) + xfree(data->target); + if (data->name != NULL) + xfree(data->name); + if (data->cmd != NULL) + xfree(data->cmd); + xfree(data); +} + +size_t +cmd_new_window_print(struct cmd *self, char *buf, size_t len) +{ + struct cmd_new_window_data *data = self->data; + size_t off = 0; + + off += xsnprintf(buf, len, "%s", self->entry->name); + if (data == NULL) + return (off); + if (off < len && data->flag_detached) + off += xsnprintf(buf + off, len - off, " -d"); + if (off < len && data->target != NULL) + off += cmd_prarg(buf + off, len - off, " -t ", data->target); + if (off < len && data->name != NULL) + off += cmd_prarg(buf + off, len - off, " -n ", data->name); + if (off < len && data->cmd != NULL) + off += cmd_prarg(buf + off, len - off, " ", data->cmd); + return (off); +} diff --git a/usr.bin/tmux/cmd-next-layout.c b/usr.bin/tmux/cmd-next-layout.c new file mode 100644 index 00000000000..c4bbfcc0f25 --- /dev/null +++ b/usr.bin/tmux/cmd-next-layout.c @@ -0,0 +1,55 @@ +/* $OpenBSD: cmd-next-layout.c,v 1.1 2009/06/01 22:58:49 nicm 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 "tmux.h" + +/* + * Switch window to next layout. + */ + +int cmd_next_layout_exec(struct cmd *, struct cmd_ctx *); + +const struct cmd_entry cmd_next_layout_entry = { + "next-layout", "nextl", + CMD_TARGET_WINDOW_USAGE, + 0, + cmd_target_init, + cmd_target_parse, + cmd_next_layout_exec, + cmd_target_send, + cmd_target_recv, + cmd_target_free, + cmd_target_print +}; + +int +cmd_next_layout_exec(struct cmd *self, struct cmd_ctx *ctx) +{ + struct cmd_target_data *data = self->data; + struct winlink *wl; + + if ((wl = cmd_find_window(ctx, data->target, NULL)) == NULL) + return (-1); + + layout_next(wl->window); + ctx->info(ctx, "layout now: %s", layout_name(wl->window)); + + return (0); +} diff --git a/usr.bin/tmux/cmd-next-window.c b/usr.bin/tmux/cmd-next-window.c new file mode 100644 index 00000000000..c938a8f7cff --- /dev/null +++ b/usr.bin/tmux/cmd-next-window.c @@ -0,0 +1,78 @@ +/* $OpenBSD: cmd-next-window.c,v 1.1 2009/06/01 22:58:49 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 "tmux.h" + +/* + * Move to next window. + */ + +void cmd_next_window_init(struct cmd *, int); +int cmd_next_window_exec(struct cmd *, struct cmd_ctx *); + +const struct cmd_entry cmd_next_window_entry = { + "next-window", "next", + CMD_TARGET_SESSION_USAGE, + CMD_AFLAG, + cmd_next_window_init, + cmd_target_parse, + cmd_next_window_exec, + cmd_target_send, + cmd_target_recv, + cmd_target_free, + cmd_target_print +}; + +void +cmd_next_window_init(struct cmd *self, int key) +{ + struct cmd_target_data *data; + + cmd_target_init(self, key); + data = self->data; + + if (key == KEYC_ADDESC('n')) + data->flags |= CMD_AFLAG; +} + +int +cmd_next_window_exec(struct cmd *self, struct cmd_ctx *ctx) +{ + struct cmd_target_data *data = self->data; + struct session *s; + int activity; + + if ((s = cmd_find_session(ctx, data->target)) == NULL) + return (-1); + + activity = 0; + if (data->flags & CMD_AFLAG) + activity = 1; + + if (session_next(s, activity) == 0) + server_redraw_session(s); + else { + ctx->error(ctx, "no next window"); + return (-1); + } + recalculate_sizes(); + + return (0); +} diff --git a/usr.bin/tmux/cmd-paste-buffer.c b/usr.bin/tmux/cmd-paste-buffer.c new file mode 100644 index 00000000000..fc01461bc7b --- /dev/null +++ b/usr.bin/tmux/cmd-paste-buffer.c @@ -0,0 +1,78 @@ +/* $OpenBSD: cmd-paste-buffer.c,v 1.1 2009/06/01 22:58:49 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 <string.h> + +#include "tmux.h" + +/* + * Paste paste buffer if present. + */ + +int cmd_paste_buffer_exec(struct cmd *, struct cmd_ctx *); + +const struct cmd_entry cmd_paste_buffer_entry = { + "paste-buffer", "pasteb", + "[-d] " CMD_BUFFER_WINDOW_USAGE, + CMD_DFLAG, + cmd_buffer_init, + cmd_buffer_parse, + cmd_paste_buffer_exec, + cmd_buffer_send, + cmd_buffer_recv, + cmd_buffer_free, + cmd_buffer_print +}; + +int +cmd_paste_buffer_exec(struct cmd *self, struct cmd_ctx *ctx) +{ + struct cmd_buffer_data *data = self->data; + struct winlink *wl; + struct window *w; + struct session *s; + struct paste_buffer *pb; + + if ((wl = cmd_find_window(ctx, data->target, &s)) == NULL) + return (-1); + w = wl->window; + + if (data->buffer == -1) + pb = paste_get_top(&s->buffers); + else { + if ((pb = paste_get_index(&s->buffers, data->buffer)) == NULL) { + ctx->error(ctx, "no buffer %d", data->buffer); + return (-1); + } + } + + if (pb != NULL) + buffer_write(w->active->out, pb->data, strlen(pb->data)); + + /* Delete the buffer if -d. */ + if (data->flags & CMD_DFLAG) { + if (data->buffer == -1) + paste_free_top(&s->buffers); + else + paste_free_index(&s->buffers, data->buffer); + } + + return (0); +} diff --git a/usr.bin/tmux/cmd-previous-layout.c b/usr.bin/tmux/cmd-previous-layout.c new file mode 100644 index 00000000000..6a2730432a2 --- /dev/null +++ b/usr.bin/tmux/cmd-previous-layout.c @@ -0,0 +1,55 @@ +/* $OpenBSD: cmd-previous-layout.c,v 1.1 2009/06/01 22:58:49 nicm 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 "tmux.h" + +/* + * Switch window to previous layout. + */ + +int cmd_previous_layout_exec(struct cmd *, struct cmd_ctx *); + +const struct cmd_entry cmd_previous_layout_entry = { + "previous-layout", "prevl", + CMD_TARGET_WINDOW_USAGE, + 0, + cmd_target_init, + cmd_target_parse, + cmd_previous_layout_exec, + cmd_target_send, + cmd_target_recv, + cmd_target_free, + cmd_target_print +}; + +int +cmd_previous_layout_exec(struct cmd *self, struct cmd_ctx *ctx) +{ + struct cmd_target_data *data = self->data; + struct winlink *wl; + + if ((wl = cmd_find_window(ctx, data->target, NULL)) == NULL) + return (-1); + + layout_previous(wl->window); + ctx->info(ctx, "layout now: %s", layout_name(wl->window)); + + return (0); +} diff --git a/usr.bin/tmux/cmd-previous-window.c b/usr.bin/tmux/cmd-previous-window.c new file mode 100644 index 00000000000..b477b140deb --- /dev/null +++ b/usr.bin/tmux/cmd-previous-window.c @@ -0,0 +1,78 @@ +/* $OpenBSD: cmd-previous-window.c,v 1.1 2009/06/01 22:58:49 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 "tmux.h" + +/* + * Move to previous window. + */ + +void cmd_previous_window_init(struct cmd *, int); +int cmd_previous_window_exec(struct cmd *, struct cmd_ctx *); + +const struct cmd_entry cmd_previous_window_entry = { + "previous-window", "prev", + CMD_TARGET_SESSION_USAGE, + CMD_AFLAG, + cmd_previous_window_init, + cmd_target_parse, + cmd_previous_window_exec, + cmd_target_send, + cmd_target_recv, + cmd_target_free, + cmd_target_print +}; + +void +cmd_previous_window_init(struct cmd *self, int key) +{ + struct cmd_target_data *data; + + cmd_target_init(self, key); + data = self->data; + + if (key == KEYC_ADDESC('p')) + data->flags |= CMD_AFLAG; +} + +int +cmd_previous_window_exec(struct cmd *self, struct cmd_ctx *ctx) +{ + struct cmd_target_data *data = self->data; + struct session *s; + int activity; + + if ((s = cmd_find_session(ctx, data->target)) == NULL) + return (-1); + + activity = 0; + if (data->flags & CMD_AFLAG) + activity = 1; + + if (session_previous(s, activity) == 0) + server_redraw_session(s); + else { + ctx->error(ctx, "no previous window"); + return (-1); + } + recalculate_sizes(); + + return (0); +} diff --git a/usr.bin/tmux/cmd-refresh-client.c b/usr.bin/tmux/cmd-refresh-client.c new file mode 100644 index 00000000000..75e2fa74d38 --- /dev/null +++ b/usr.bin/tmux/cmd-refresh-client.c @@ -0,0 +1,54 @@ +/* $OpenBSD: cmd-refresh-client.c,v 1.1 2009/06/01 22:58:49 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 "tmux.h" + +/* + * Refresh client. + */ + +int cmd_refresh_client_exec(struct cmd *, struct cmd_ctx *); + +const struct cmd_entry cmd_refresh_client_entry = { + "refresh-client", "refresh", + CMD_TARGET_CLIENT_USAGE, + 0, + cmd_target_init, + cmd_target_parse, + cmd_refresh_client_exec, + cmd_target_send, + cmd_target_recv, + cmd_target_free, + cmd_target_print +}; + +int +cmd_refresh_client_exec(struct cmd *self, struct cmd_ctx *ctx) +{ + struct cmd_target_data *data = self->data; + struct client *c; + + if ((c = cmd_find_client(ctx, data->target)) == NULL) + return (-1); + + server_redraw_client(c); + + return (0); +} diff --git a/usr.bin/tmux/cmd-rename-session.c b/usr.bin/tmux/cmd-rename-session.c new file mode 100644 index 00000000000..dfeba64d8ff --- /dev/null +++ b/usr.bin/tmux/cmd-rename-session.c @@ -0,0 +1,57 @@ +/* $OpenBSD: cmd-rename-session.c,v 1.1 2009/06/01 22:58:49 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 <stdlib.h> + +#include "tmux.h" + +/* + * Change session name. + */ + +int cmd_rename_session_exec(struct cmd *, struct cmd_ctx *); + +const struct cmd_entry cmd_rename_session_entry = { + "rename-session", "rename", + CMD_TARGET_SESSION_USAGE " new-name", + CMD_ARG1, + cmd_target_init, + cmd_target_parse, + cmd_rename_session_exec, + cmd_target_send, + cmd_target_recv, + cmd_target_free, + cmd_target_print +}; + +int +cmd_rename_session_exec(struct cmd *self, struct cmd_ctx *ctx) +{ + struct cmd_target_data *data = self->data; + struct session *s; + + if ((s = cmd_find_session(ctx, data->target)) == NULL) + return (-1); + + xfree(s->name); + s->name = xstrdup(data->arg); + + return (0); +} diff --git a/usr.bin/tmux/cmd-rename-window.c b/usr.bin/tmux/cmd-rename-window.c new file mode 100644 index 00000000000..d702cb3b847 --- /dev/null +++ b/usr.bin/tmux/cmd-rename-window.c @@ -0,0 +1,61 @@ +/* $OpenBSD: cmd-rename-window.c,v 1.1 2009/06/01 22:58:49 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 <stdlib.h> + +#include "tmux.h" + +/* + * Rename a window. + */ + +int cmd_rename_window_exec(struct cmd *, struct cmd_ctx *); + +const struct cmd_entry cmd_rename_window_entry = { + "rename-window", "renamew", + CMD_TARGET_WINDOW_USAGE " new-name", + CMD_ARG1, + cmd_target_init, + cmd_target_parse, + cmd_rename_window_exec, + cmd_target_send, + cmd_target_recv, + cmd_target_free, + cmd_target_print +}; + +int +cmd_rename_window_exec(struct cmd *self, struct cmd_ctx *ctx) +{ + struct cmd_target_data *data = self->data; + struct session *s; + struct winlink *wl; + + if ((wl = cmd_find_window(ctx, data->target, &s)) == NULL) + return (-1); + + xfree(wl->window->name); + wl->window->name = xstrdup(data->arg); + options_set_number(&wl->window->options, "automatic-rename", 0); + + server_status_session(s); + + return (0); +} diff --git a/usr.bin/tmux/cmd-resize-pane.c b/usr.bin/tmux/cmd-resize-pane.c new file mode 100644 index 00000000000..c9c77e800c7 --- /dev/null +++ b/usr.bin/tmux/cmd-resize-pane.c @@ -0,0 +1,105 @@ +/* $OpenBSD: cmd-resize-pane.c,v 1.1 2009/06/01 22:58:49 nicm 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 <stdlib.h> + +#include "tmux.h" + +/* + * Increase or decrease pane size. + */ + +void cmd_resize_pane_init(struct cmd *, int); +int cmd_resize_pane_exec(struct cmd *, struct cmd_ctx *); + +const struct cmd_entry cmd_resize_pane_entry = { + "resize-pane", "resizep", + CMD_PANE_WINDOW_USAGE "[-DU] [adjustment]", + CMD_ARG01|CMD_BIGUFLAG|CMD_BIGDFLAG, + cmd_resize_pane_init, + cmd_pane_parse, + cmd_resize_pane_exec, + cmd_pane_send, + cmd_pane_recv, + cmd_pane_free, + cmd_pane_print +}; + +void +cmd_resize_pane_init(struct cmd *self, int key) +{ + struct cmd_pane_data *data; + + cmd_pane_init(self, key); + data = self->data; + + if (key == KEYC_ADDCTL(KEYC_DOWN)) + data->flags |= CMD_BIGDFLAG; + + if (key == KEYC_ADDESC(KEYC_UP)) + data->arg = xstrdup("5"); + if (key == KEYC_ADDESC(KEYC_DOWN)) { + data->flags |= CMD_BIGDFLAG; + data->arg = xstrdup("5"); + } +} + +int +cmd_resize_pane_exec(struct cmd *self, struct cmd_ctx *ctx) +{ + struct cmd_pane_data *data = self->data; + struct winlink *wl; + const char *errstr; + struct window_pane *wp; + u_int adjust; + + if ((wl = cmd_find_window(ctx, data->target, NULL)) == NULL) + return (-1); + if (data->pane == -1) + wp = wl->window->active; + else { + wp = window_pane_at_index(wl->window, data->pane); + if (wp == NULL) { + ctx->error(ctx, "no pane: %d", data->pane); + return (-1); + } + } + + if (data->arg == NULL) + adjust = 1; + else { + adjust = strtonum(data->arg, 1, INT_MAX, &errstr); + if (errstr != NULL) { + ctx->error(ctx, "adjustment %s: %s", errstr, data->arg); + return (-1); + } + } + + if (!(data->flags & CMD_BIGDFLAG)) + adjust = -adjust; + if (layout_resize(wp, adjust) != 0) { + ctx->error(ctx, "layout %s " + "does not support resizing", layout_name(wp->window)); + return (-1); + } + server_redraw_window(wl->window); + + return (0); +} diff --git a/usr.bin/tmux/cmd-respawn-window.c b/usr.bin/tmux/cmd-respawn-window.c new file mode 100644 index 00000000000..1693f868860 --- /dev/null +++ b/usr.bin/tmux/cmd-respawn-window.c @@ -0,0 +1,87 @@ +/* $OpenBSD: cmd-respawn-window.c,v 1.1 2009/06/01 22:58:49 nicm Exp $ */ + +/* + * Copyright (c) 2008 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" + +/* + * Respawn a window (restart the command). Kill existing if -k given. + */ + +int cmd_respawn_window_exec(struct cmd *, struct cmd_ctx *); + +const struct cmd_entry cmd_respawn_window_entry = { + "respawn-window", "respawnw", + "[-k] " CMD_TARGET_WINDOW_USAGE " [command]", + CMD_ARG01|CMD_KFLAG, + cmd_target_init, + cmd_target_parse, + cmd_respawn_window_exec, + cmd_target_send, + cmd_target_recv, + cmd_target_free, + cmd_target_print +}; + +int +cmd_respawn_window_exec(struct cmd *self, struct cmd_ctx *ctx) +{ + struct cmd_target_data *data = self->data; + struct winlink *wl; + struct window *w; + struct window_pane *wp; + struct session *s; + const char **env; + char *cause; + + if ((wl = cmd_find_window(ctx, data->target, &s)) == NULL) + return (-1); + w = wl->window; + + if (!(data->flags & CMD_KFLAG)) { + TAILQ_FOREACH(wp, &w->panes, entry) { + if (wp->fd == -1) + continue; + ctx->error(ctx, + "window still active: %s:%d", s->name, wl->idx); + return (-1); + } + } + + env = server_fill_environ(s); + + wp = TAILQ_FIRST(&w->panes); + TAILQ_REMOVE(&w->panes, wp, entry); + window_destroy_panes(w); + TAILQ_INSERT_HEAD(&w->panes, wp, entry); + window_pane_resize(wp, w->sx, w->sy); + if (window_pane_spawn(wp, data->arg, NULL, env, &cause) != 0) { + ctx->error(ctx, "respawn window failed: %s", cause); + xfree(cause); + return (-1); + } + screen_reinit(&wp->base); + + recalculate_sizes(); + server_redraw_window(w); + + return (0); +} diff --git a/usr.bin/tmux/cmd-rotate-window.c b/usr.bin/tmux/cmd-rotate-window.c new file mode 100644 index 00000000000..abe78fdd62b --- /dev/null +++ b/usr.bin/tmux/cmd-rotate-window.c @@ -0,0 +1,111 @@ +/* $OpenBSD: cmd-rotate-window.c,v 1.1 2009/06/01 22:58:49 nicm 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 "tmux.h" + +/* + * Rotate the panes in a window. + */ + +void cmd_rotate_window_init(struct cmd *, int); +int cmd_rotate_window_exec(struct cmd *, struct cmd_ctx *); + +const struct cmd_entry cmd_rotate_window_entry = { + "rotate-window", "rotatew", + "[-DU] " CMD_TARGET_WINDOW_USAGE, + CMD_BIGUFLAG|CMD_BIGDFLAG, + cmd_rotate_window_init, + cmd_target_parse, + cmd_rotate_window_exec, + cmd_target_send, + cmd_target_recv, + cmd_target_free, + cmd_target_print +}; + +void +cmd_rotate_window_init(struct cmd *self, int key) +{ + struct cmd_target_data *data; + + cmd_target_init(self, key); + data = self->data; + + if (key == KEYC_ADDESC('o')) + data->flags |= CMD_BIGDFLAG; +} + +int +cmd_rotate_window_exec(struct cmd *self, struct cmd_ctx *ctx) +{ + struct cmd_target_data *data = self->data; + struct winlink *wl; + struct window *w; + struct window_pane *wp, *wp2; + u_int sx, sy, xoff, yoff; + + if ((wl = cmd_find_window(ctx, data->target, NULL)) == NULL) + return (-1); + w = wl->window; + + if (data->flags & CMD_BIGDFLAG) { + wp = TAILQ_LAST(&w->panes, window_panes); + TAILQ_REMOVE(&w->panes, wp, entry); + TAILQ_INSERT_HEAD(&w->panes, wp, entry); + + xoff = wp->xoff; yoff = wp->yoff; + sx = wp->sx; sy = wp->sy; + TAILQ_FOREACH(wp, &w->panes, entry) { + if ((wp2 = TAILQ_NEXT(wp, entry)) == NULL) + break; + wp->xoff = wp2->xoff; wp->yoff = wp2->yoff; + window_pane_resize(wp, wp2->sx, wp2->sy); + } + wp->xoff = xoff; wp->yoff = yoff; + window_pane_resize(wp, sx, sy); + + if ((wp = TAILQ_PREV(w->active, window_panes, entry)) == NULL) + wp = TAILQ_LAST(&w->panes, window_panes); + window_set_active_pane(w, wp); + } else { + wp = TAILQ_FIRST(&w->panes); + TAILQ_REMOVE(&w->panes, wp, entry); + TAILQ_INSERT_TAIL(&w->panes, wp, entry); + + xoff = wp->xoff; yoff = wp->yoff; + sx = wp->sx; sy = wp->sy; + TAILQ_FOREACH_REVERSE(wp, &w->panes, window_panes, entry) { + if ((wp2 = TAILQ_PREV(wp, window_panes, entry)) == NULL) + break; + wp->xoff = wp2->xoff; wp->yoff = wp2->yoff; + window_pane_resize(wp, wp2->sx, wp2->sy); + } + wp->xoff = xoff; wp->yoff = yoff; + window_pane_resize(wp, sx, sy); + + if ((wp = TAILQ_NEXT(w->active, entry)) == NULL) + wp = TAILQ_FIRST(&w->panes); + window_set_active_pane(w, wp); + } + + layout_refresh(w, 0); + + return (0); +} diff --git a/usr.bin/tmux/cmd-save-buffer.c b/usr.bin/tmux/cmd-save-buffer.c new file mode 100644 index 00000000000..012e4031b37 --- /dev/null +++ b/usr.bin/tmux/cmd-save-buffer.c @@ -0,0 +1,90 @@ +/* $OpenBSD: cmd-save-buffer.c,v 1.1 2009/06/01 22:58:49 nicm Exp $ */ + +/* + * Copyright (c) 2009 Tiago Cunha <me@tiagocunha.org> + * + * 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/stat.h> + +#include <errno.h> +#include <string.h> + +#include "tmux.h" + +/* + * Saves a session paste buffer to a file. + */ + +int cmd_save_buffer_exec(struct cmd *, struct cmd_ctx *); + +const struct cmd_entry cmd_save_buffer_entry = { + "save-buffer", "saveb", + "[-a] " CMD_BUFFER_SESSION_USAGE " path", + CMD_AFLAG|CMD_ARG1, + cmd_buffer_init, + cmd_buffer_parse, + cmd_save_buffer_exec, + cmd_buffer_send, + cmd_buffer_recv, + cmd_buffer_free, + cmd_buffer_print +}; + +int +cmd_save_buffer_exec(struct cmd *self, struct cmd_ctx *ctx) +{ + struct cmd_buffer_data *data = self->data; + struct session *s; + struct paste_buffer *pb; + mode_t mask; + FILE *f; + + if ((s = cmd_find_session(ctx, data->target)) == NULL) + return (-1); + + if (data->buffer == -1) { + if ((pb = paste_get_top(&s->buffers)) == NULL) { + ctx->error(ctx, "no buffers"); + return (-1); + } + } else { + if ((pb = paste_get_index(&s->buffers, data->buffer)) == NULL) { + ctx->error(ctx, "no buffer %d", data->buffer); + return (-1); + } + } + + mask = umask(S_IRWXG | S_IRWXO); + if (data->flags & CMD_AFLAG) + f = fopen(data->arg, "ab"); + else + f = fopen(data->arg, "wb"); + if (f == NULL) { + ctx->error(ctx, "%s: %s", data->arg, strerror(errno)); + return (-1); + } + + if (fwrite(pb->data, 1, strlen(pb->data), f) != strlen(pb->data)) { + ctx->error(ctx, "%s: fwrite error", data->arg); + fclose(f); + return (-1); + } + + fclose(f); + umask(mask); + + return (0); +} diff --git a/usr.bin/tmux/cmd-scroll-mode.c b/usr.bin/tmux/cmd-scroll-mode.c new file mode 100644 index 00000000000..2bbf6e864d0 --- /dev/null +++ b/usr.bin/tmux/cmd-scroll-mode.c @@ -0,0 +1,72 @@ +/* $OpenBSD: cmd-scroll-mode.c,v 1.1 2009/06/01 22:58:49 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 "tmux.h" + +/* + * Enter scroll mode. + */ + +void cmd_scroll_mode_init(struct cmd *, int); +int cmd_scroll_mode_exec(struct cmd *, struct cmd_ctx *); + +const struct cmd_entry cmd_scroll_mode_entry = { + "scroll-mode", NULL, + CMD_TARGET_WINDOW_USAGE, + CMD_UFLAG, + cmd_scroll_mode_init, + cmd_target_parse, + cmd_scroll_mode_exec, + cmd_target_send, + cmd_target_recv, + cmd_target_free, + cmd_target_print +}; + +void +cmd_scroll_mode_init(struct cmd *self, int key) +{ + struct cmd_target_data *data; + + cmd_target_init(self, key); + data = self->data; + + switch (key) { + case KEYC_PPAGE: + data->flags |= CMD_UFLAG; + break; + } +} + +int +cmd_scroll_mode_exec(struct cmd *self, struct cmd_ctx *ctx) +{ + struct cmd_target_data *data = self->data; + struct winlink *wl; + + if ((wl = cmd_find_window(ctx, data->target, NULL)) == NULL) + return (-1); + + window_pane_set_mode(wl->window->active, &window_scroll_mode); + if (data->flags & CMD_UFLAG) + window_scroll_pageup(wl->window->active); + + return (0); +} diff --git a/usr.bin/tmux/cmd-select-layout.c b/usr.bin/tmux/cmd-select-layout.c new file mode 100644 index 00000000000..213e7fd4cb4 --- /dev/null +++ b/usr.bin/tmux/cmd-select-layout.c @@ -0,0 +1,86 @@ +/* $OpenBSD: cmd-select-layout.c,v 1.1 2009/06/01 22:58:49 nicm 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 "tmux.h" + +/* + * Switch window to selected layout. + */ + +void cmd_select_layout_init(struct cmd *, int); +int cmd_select_layout_exec(struct cmd *, struct cmd_ctx *); + +const struct cmd_entry cmd_select_layout_entry = { + "select-layout", "selectl", + CMD_TARGET_WINDOW_USAGE " layout-name", + CMD_ARG1, + cmd_select_layout_init, + cmd_target_parse, + cmd_select_layout_exec, + cmd_target_send, + cmd_target_recv, + cmd_target_free, + cmd_target_print +}; + +void +cmd_select_layout_init(struct cmd *self, int key) +{ + struct cmd_target_data *data; + + cmd_target_init(self, key); + data = self->data; + + switch (key) { + case KEYC_ADDESC('0'): + data->arg = xstrdup("manual-vertical"); + break; + case KEYC_ADDESC('1'): + data->arg = xstrdup("even-horizontal"); + break; + case KEYC_ADDESC('2'): + data->arg = xstrdup("even-vertical"); + break; + case KEYC_ADDESC('9'): + data->arg = xstrdup("active-only"); + break; + } +} + +int +cmd_select_layout_exec(struct cmd *self, struct cmd_ctx *ctx) +{ + struct cmd_target_data *data = self->data; + struct winlink *wl; + int layout; + + if ((wl = cmd_find_window(ctx, data->target, NULL)) == NULL) + return (-1); + + if ((layout = layout_lookup(data->arg)) == -1) { + ctx->error(ctx, "unknown or ambiguous layout: %s", data->arg); + return (-1); + } + + if (layout_select(wl->window, layout) == 0) + ctx->info(ctx, "layout now: %s", layout_name(wl->window)); + + return (0); +} diff --git a/usr.bin/tmux/cmd-select-pane.c b/usr.bin/tmux/cmd-select-pane.c new file mode 100644 index 00000000000..a153ad3b134 --- /dev/null +++ b/usr.bin/tmux/cmd-select-pane.c @@ -0,0 +1,69 @@ +/* $OpenBSD: cmd-select-pane.c,v 1.1 2009/06/01 22:58:49 nicm 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 "tmux.h" + +/* + * Select pane. + */ + +int cmd_select_pane_exec(struct cmd *, struct cmd_ctx *); + +const struct cmd_entry cmd_select_pane_entry = { + "select-pane", "selectp", + CMD_PANE_WINDOW_USAGE, + 0, + cmd_pane_init, + cmd_pane_parse, + cmd_select_pane_exec, + cmd_pane_send, + cmd_pane_recv, + cmd_pane_free, + cmd_pane_print +}; + +int +cmd_select_pane_exec(struct cmd *self, struct cmd_ctx *ctx) +{ + struct cmd_pane_data *data = self->data; + struct winlink *wl; + struct window_pane *wp; + + if ((wl = cmd_find_window(ctx, data->target, NULL)) == NULL) + return (-1); + if (data->pane == -1) + wp = wl->window->active; + else { + wp = window_pane_at_index(wl->window, data->pane); + if (wp == NULL) { + ctx->error(ctx, "no pane: %d", data->pane); + return (-1); + } + } + + if (wp->flags & PANE_HIDDEN) { + ctx->error(ctx, "pane %d is hidden", data->pane); + return (-1); + } + window_set_active_pane(wl->window, wp); + layout_refresh(wl->window, 1); + + return (0); +} diff --git a/usr.bin/tmux/cmd-select-prompt.c b/usr.bin/tmux/cmd-select-prompt.c new file mode 100644 index 00000000000..ff28c5f36b2 --- /dev/null +++ b/usr.bin/tmux/cmd-select-prompt.c @@ -0,0 +1,93 @@ +/* $OpenBSD: cmd-select-prompt.c,v 1.1 2009/06/01 22:58:49 nicm Exp $ */ + +/* + * Copyright (c) 2008 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 <stdlib.h> + +#include "tmux.h" + +/* + * Prompt for window index and select it. + */ + +int cmd_select_prompt_exec(struct cmd *, struct cmd_ctx *); + +int cmd_select_prompt_callback(void *, const char *); + +const struct cmd_entry cmd_select_prompt_entry = { + "select-prompt", NULL, + CMD_TARGET_CLIENT_USAGE, + 0, + cmd_target_init, + cmd_target_parse, + cmd_select_prompt_exec, + cmd_target_send, + cmd_target_recv, + cmd_target_free, + cmd_target_print +}; + +int +cmd_select_prompt_exec(struct cmd *self, struct cmd_ctx *ctx) +{ + struct cmd_target_data *data = self->data; + struct client *c; + + if ((c = cmd_find_client(ctx, data->target)) == NULL) + return (-1); + + if (c->prompt_string != NULL) + return (0); + + status_prompt_set(c, "index ", cmd_select_prompt_callback, c, 0); + + return (0); +} + +int +cmd_select_prompt_callback(void *data, const char *s) +{ + struct client *c = data; + const char *errstr; + char msg[128]; + u_int idx; + + if (s == NULL) + return (0); + + idx = strtonum(s, 0, UINT_MAX, &errstr); + if (errstr != NULL) { + xsnprintf(msg, sizeof msg, "Index %s: %s", errstr, s); + status_message_set(c, msg); + return (0); + } + + if (winlink_find_by_index(&c->session->windows, idx) == NULL) { + xsnprintf(msg, sizeof msg, + "Window not found: %s:%d", c->session->name, idx); + status_message_set(c, msg); + return (0); + } + + if (session_select(c->session, idx) == 0) + server_redraw_session(c->session); + recalculate_sizes(); + + return (0); +} diff --git a/usr.bin/tmux/cmd-select-window.c b/usr.bin/tmux/cmd-select-window.c new file mode 100644 index 00000000000..8b7da8fe535 --- /dev/null +++ b/usr.bin/tmux/cmd-select-window.c @@ -0,0 +1,71 @@ +/* $OpenBSD: cmd-select-window.c,v 1.1 2009/06/01 22:58:49 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 <stdlib.h> + +#include "tmux.h" + +/* + * Select window by index. + */ + +void cmd_select_window_init(struct cmd *, int); +int cmd_select_window_exec(struct cmd *, struct cmd_ctx *); + +const struct cmd_entry cmd_select_window_entry = { + "select-window", "selectw", + CMD_TARGET_WINDOW_USAGE, + 0, + cmd_select_window_init, + cmd_target_parse, + cmd_select_window_exec, + cmd_target_send, + cmd_target_recv, + cmd_target_free, + cmd_target_print +}; + +void +cmd_select_window_init(struct cmd *self, int key) +{ + struct cmd_target_data *data; + + cmd_target_init(self, key); + data = self->data; + + xasprintf(&data->target, ":%d", key - '0'); +} + +int +cmd_select_window_exec(struct cmd *self, struct cmd_ctx *ctx) +{ + struct cmd_target_data *data = self->data; + struct winlink *wl; + struct session *s; + + if ((wl = cmd_find_window(ctx, data->target, &s)) == NULL) + return (-1); + + if (session_select(s, wl->idx) == 0) + server_redraw_session(s); + recalculate_sizes(); + + return (0); +} diff --git a/usr.bin/tmux/cmd-send-keys.c b/usr.bin/tmux/cmd-send-keys.c new file mode 100644 index 00000000000..44a51b2839f --- /dev/null +++ b/usr.bin/tmux/cmd-send-keys.c @@ -0,0 +1,184 @@ +/* $OpenBSD: cmd-send-keys.c,v 1.1 2009/06/01 22:58:49 nicm Exp $ */ + +/* + * Copyright (c) 2008 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 <stdlib.h> + +#include "tmux.h" + +/* + * Send keys to client. + */ + +int cmd_send_keys_parse(struct cmd *, int, char **, char **); +int cmd_send_keys_exec(struct cmd *, struct cmd_ctx *); +void cmd_send_keys_send(struct cmd *, struct buffer *); +void cmd_send_keys_recv(struct cmd *, struct buffer *); +void cmd_send_keys_free(struct cmd *); +size_t cmd_send_keys_print(struct cmd *, char *, size_t); + +struct cmd_send_keys_data { + char *target; + int idx; + u_int nkeys; + int *keys; +}; + +const struct cmd_entry cmd_send_keys_entry = { + "send-keys", "send", + "[-t target-window] key ...", + 0, + NULL, + cmd_send_keys_parse, + cmd_send_keys_exec, + cmd_send_keys_send, + cmd_send_keys_recv, + cmd_send_keys_free, + cmd_send_keys_print +}; + +int +cmd_send_keys_parse(struct cmd *self, int argc, char **argv, char **cause) +{ + struct cmd_send_keys_data *data; + int opt, key; + char *s; + + self->data = data = xmalloc(sizeof *data); + data->target = NULL; + data->idx = -1; + data->nkeys = 0; + data->keys = NULL; + + while ((opt = getopt(argc, argv, "t:")) != -1) { + switch (opt) { + case 't': + if (data->target == NULL) + data->target = xstrdup(optarg); + break; + default: + goto usage; + } + } + argc -= optind; + argv += optind; + if (argc == 0) + goto usage; + + while (argc-- != 0) { + if ((key = key_string_lookup_string(*argv)) != KEYC_NONE) { + data->keys = xrealloc( + data->keys, data->nkeys + 1, sizeof *data->keys); + data->keys[data->nkeys++] = key; + } else { + for (s = *argv; *s != '\0'; s++) { + data->keys = xrealloc(data->keys, + data->nkeys + 1, sizeof *data->keys); + data->keys[data->nkeys++] = *s; + } + } + + argv++; + } + + return (0); + +usage: + xasprintf(cause, "usage: %s %s", self->entry->name, self->entry->usage); + + self->entry->free(self); + return (-1); +} + +int +cmd_send_keys_exec(struct cmd *self, struct cmd_ctx *ctx) +{ + struct cmd_send_keys_data *data = self->data; + struct winlink *wl; + u_int i; + + if (data == NULL) + return (-1); + + if ((wl = cmd_find_window(ctx, data->target, NULL)) == NULL) + return (-1); + + for (i = 0; i < data->nkeys; i++) { + window_pane_key( + wl->window->active, ctx->curclient, data->keys[i]); + } + + return (0); +} + +void +cmd_send_keys_send(struct cmd *self, struct buffer *b) +{ + struct cmd_send_keys_data *data = self->data; + + buffer_write(b, data, sizeof *data); + cmd_send_string(b, data->target); + buffer_write(b, data->keys, data->nkeys * sizeof *data->keys); +} + +void +cmd_send_keys_recv(struct cmd *self, struct buffer *b) +{ + struct cmd_send_keys_data *data; + + self->data = data = xmalloc(sizeof *data); + buffer_read(b, data, sizeof *data); + data->target = cmd_recv_string(b); + data->keys = xcalloc(data->nkeys, sizeof *data->keys); + buffer_read(b, data->keys, data->nkeys * sizeof *data->keys); +} + +void +cmd_send_keys_free(struct cmd *self) +{ + struct cmd_send_keys_data *data = self->data; + + if (data->target != NULL) + xfree(data->target); + xfree(data); +} + +size_t +cmd_send_keys_print(struct cmd *self, char *buf, size_t len) +{ + struct cmd_send_keys_data *data = self->data; + size_t off = 0; + u_int i; + + off += xsnprintf(buf, len, "%s", self->entry->name); + if (data == NULL) + return (off); + if (off < len && data->target != NULL) + off += cmd_prarg(buf + off, len - off, " -t ", data->target); + if (off < len && data->idx != -1) + off += xsnprintf(buf + off, len - off, " -i %d", data->idx); + + for (i = 0; i < data->nkeys; i++) { + if (off >= len) + break; + off += xsnprintf(buf + off, + len - off, " %s", key_string_lookup_key(data->keys[i])); + } + return (off); +} diff --git a/usr.bin/tmux/cmd-send-prefix.c b/usr.bin/tmux/cmd-send-prefix.c new file mode 100644 index 00000000000..dcb61247338 --- /dev/null +++ b/usr.bin/tmux/cmd-send-prefix.c @@ -0,0 +1,57 @@ +/* $OpenBSD: cmd-send-prefix.c,v 1.1 2009/06/01 22:58:49 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 "tmux.h" + +/* + * Send prefix key as a key. + */ + +int cmd_send_prefix_exec(struct cmd *, struct cmd_ctx *); + +const struct cmd_entry cmd_send_prefix_entry = { + "send-prefix", NULL, + CMD_TARGET_WINDOW_USAGE, + 0, + cmd_target_init, + cmd_target_parse, + cmd_send_prefix_exec, + cmd_target_send, + cmd_target_recv, + cmd_target_free, + cmd_target_print +}; + +int +cmd_send_prefix_exec(struct cmd *self, struct cmd_ctx *ctx) +{ + struct cmd_target_data *data = self->data; + struct session *s; + struct winlink *wl; + int key; + + if ((wl = cmd_find_window(ctx, data->target, &s)) == NULL) + return (-1); + + key = options_get_number(&s->options, "prefix"); + window_pane_key(wl->window->active, ctx->curclient, key); + + return (0); +} diff --git a/usr.bin/tmux/cmd-server-info.c b/usr.bin/tmux/cmd-server-info.c new file mode 100644 index 00000000000..653ff3533f3 --- /dev/null +++ b/usr.bin/tmux/cmd-server-info.c @@ -0,0 +1,179 @@ +/* $OpenBSD: cmd-server-info.c,v 1.1 2009/06/01 22:58:49 nicm Exp $ */ + +/* + * Copyright (c) 2008 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/utsname.h> + +#include <stdlib.h> +#include <string.h> +#include <time.h> +#include <unistd.h> + +#include "tmux.h" + +/* + * Show various information about server. + */ + +int cmd_server_info_exec(struct cmd *, struct cmd_ctx *); + +const struct cmd_entry cmd_server_info_entry = { + "server-info", "info", + "", + 0, + NULL, + NULL, + cmd_server_info_exec, + NULL, + NULL, + NULL, + NULL +}; + +int +cmd_server_info_exec(unused struct cmd *self, struct cmd_ctx *ctx) +{ + struct tty_term *term; + struct client *c; + struct session *s; + struct winlink *wl; + struct window *w; + struct window_pane *wp; + struct tty_code *code; + struct tty_term_code_entry *ent; + struct utsname un; + struct grid *gd; + u_int i, j, k; + char out[80]; + char *tim; + time_t t; + u_int lines, ulines; + size_t size, usize; + + tim = ctime(&start_time); + *strchr(tim, '\n') = '\0'; + ctx->print(ctx, "pid %ld, started %s", (long) getpid(), tim); + ctx->print(ctx, "socket path %s, debug level %d%s", + socket_path, debug_level, be_quiet ? ", quiet" : ""); + if (uname(&un) == 0) { + ctx->print(ctx, "system is %s %s %s %s", + un.sysname, un.release, un.version, un.machine); + } + if (cfg_file != NULL) + ctx->print(ctx, "configuration file is %s", cfg_file); + else + ctx->print(ctx, "configuration file not specified"); + ctx->print(ctx, "protocol version is %d", PROTOCOL_VERSION); + ctx->print(ctx, "%u clients, %u sessions", + ARRAY_LENGTH(&clients), ARRAY_LENGTH(&sessions)); + ctx->print(ctx, ""); + + ctx->print(ctx, "Clients:"); + for (i = 0; i < ARRAY_LENGTH(&clients); i++) { + c = ARRAY_ITEM(&clients, i); + if (c == NULL || c->session == NULL) + continue; + + ctx->print(ctx, "%2d: %s (%d, %d): %s [%ux%u %s] " + "[flags=0x%x/0x%x]", i, c->tty.path, c->fd, c->tty.fd, + c->session->name, c->tty.sx, c->tty.sy, c->tty.termname, + c->flags, c->tty.flags); + } + ctx->print(ctx, ""); + + ctx->print(ctx, "Sessions: [%zu/%zu]", + sizeof (struct grid_cell), sizeof (struct grid_utf8)); + for (i = 0; i < ARRAY_LENGTH(&sessions); i++) { + s = ARRAY_ITEM(&sessions, i); + if (s == NULL) + continue; + + t = s->tv.tv_sec; + tim = ctime(&t); + *strchr(tim, '\n') = '\0'; + + ctx->print(ctx, "%2u: %s: %u windows (created %s) [%ux%u] " + "[flags=0x%x]", i, s->name, winlink_count(&s->windows), + tim, s->sx, s->sy, s->flags); + RB_FOREACH(wl, winlinks, &s->windows) { + w = wl->window; + ctx->print(ctx, "%4u: %s [%ux%u] [flags=0x%x, " + "references=%u, layout=%u]", wl->idx, w->name, + w->sx, w->sy, w->flags, w->references, + w->layout); + j = 0; + TAILQ_FOREACH(wp, &w->panes, entry) { + lines = ulines = size = usize = 0; + gd = wp->base.grid; + for (k = 0; k < gd->hsize + gd->sy; k++) { + if (gd->data[k] != NULL) { + lines++; + size += gd->size[k] * + sizeof (**gd->data); + } + if (gd->udata[k] != NULL) { + ulines++; + usize += gd->usize[k] * + sizeof (**gd->udata); + } + } + ctx->print(ctx, "%6u: %s %lu %d %u/%u, %zu " + "bytes; UTF-8 %u/%u, %zu bytes", j, + wp->tty, (u_long) wp->pid, wp->fd, lines, + gd->hsize + gd->sy, size, ulines, + gd->hsize + gd->sy, usize); + j++; + } + } + } + ctx->print(ctx, ""); + + ctx->print(ctx, "Terminals:"); + SLIST_FOREACH(term, &tty_terms, entry) { + ctx->print(ctx, "%s [references=%u, flags=0x%x]:", + term->name, term->references, term->flags); + for (i = 0; i < NTTYCODE; i++) { + ent = &tty_term_codes[i]; + code = &term->codes[ent->code]; + switch (code->type) { + case TTYCODE_NONE: + ctx->print(ctx, "%2u: %s: [missing]", + ent->code, ent->name); + break; + case TTYCODE_STRING: + clean_string( + code->value.string, out, sizeof out); + ctx->print(ctx, "%2u: %s: (string) %s", + ent->code, ent->name, out); + break; + case TTYCODE_NUMBER: + ctx->print(ctx, "%2u: %s: (number) %d", + ent->code, ent->name, code->value.number); + break; + case TTYCODE_FLAG: + ctx->print(ctx, "%2u: %s: (flag) %s", + ent->code, ent->name, + code->value.flag ? "true" : "false"); + break; + } + } + } + ctx->print(ctx, ""); + + return (0); +} diff --git a/usr.bin/tmux/cmd-set-buffer.c b/usr.bin/tmux/cmd-set-buffer.c new file mode 100644 index 00000000000..bd9e1124c27 --- /dev/null +++ b/usr.bin/tmux/cmd-set-buffer.c @@ -0,0 +1,64 @@ +/* $OpenBSD: cmd-set-buffer.c,v 1.1 2009/06/01 22:58:49 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 <stdlib.h> + +#include "tmux.h" + +/* + * Add or set a session paste buffer. + */ + +int cmd_set_buffer_exec(struct cmd *, struct cmd_ctx *); + +const struct cmd_entry cmd_set_buffer_entry = { + "set-buffer", "setb", + CMD_BUFFER_SESSION_USAGE " data", + CMD_ARG1, + cmd_buffer_init, + cmd_buffer_parse, + cmd_set_buffer_exec, + cmd_buffer_send, + cmd_buffer_recv, + cmd_buffer_free, + cmd_buffer_print +}; + +int +cmd_set_buffer_exec(struct cmd *self, struct cmd_ctx *ctx) +{ + struct cmd_buffer_data *data = self->data; + struct session *s; + u_int limit; + + if ((s = cmd_find_session(ctx, data->target)) == NULL) + return (-1); + + limit = options_get_number(&s->options, "buffer-limit"); + if (data->buffer == -1) { + paste_add(&s->buffers, xstrdup(data->arg), limit); + return (0); + } + if (paste_replace(&s->buffers, data->buffer, xstrdup(data->arg)) != 0) { + ctx->error(ctx, "no buffer %d", data->buffer); + return (-1); + } + return (0); +} diff --git a/usr.bin/tmux/cmd-set-option.c b/usr.bin/tmux/cmd-set-option.c new file mode 100644 index 00000000000..da5cd4ef09c --- /dev/null +++ b/usr.bin/tmux/cmd-set-option.c @@ -0,0 +1,169 @@ +/* $OpenBSD: cmd-set-option.c,v 1.1 2009/06/01 22:58:49 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 <stdlib.h> +#include <string.h> + +#include "tmux.h" + +/* + * Set an option. + */ + +int cmd_set_option_exec(struct cmd *, struct cmd_ctx *); + +const struct cmd_entry cmd_set_option_entry = { + "set-option", "set", + CMD_OPTION_SESSION_USAGE, + CMD_GFLAG|CMD_UFLAG, + NULL, + cmd_option_parse, + cmd_set_option_exec, + cmd_option_send, + cmd_option_recv, + cmd_option_free, + cmd_option_print +}; + +const char *set_option_status_keys_list[] = { + "emacs", "vi", NULL +}; +const char *set_option_bell_action_list[] = { + "none", "any", "current", NULL +}; +const struct set_option_entry set_option_table[NSETOPTION] = { + { "bell-action", SET_OPTION_CHOICE, 0, 0, set_option_bell_action_list }, + { "buffer-limit", SET_OPTION_NUMBER, 1, INT_MAX, NULL }, + { "default-command", SET_OPTION_STRING, 0, 0, NULL }, + { "default-path", SET_OPTION_STRING, 0, 0, NULL }, + { "display-time", SET_OPTION_NUMBER, 1, INT_MAX, NULL }, + { "history-limit", SET_OPTION_NUMBER, 0, INT_MAX, NULL }, + { "lock-after-time", SET_OPTION_NUMBER, 0, INT_MAX, NULL }, + { "message-attr", SET_OPTION_ATTRIBUTES, 0, 0, NULL }, + { "message-bg", SET_OPTION_COLOUR, 0, 0, NULL }, + { "message-fg", SET_OPTION_COLOUR, 0, 0, NULL }, + { "prefix", SET_OPTION_KEY, 0, 0, NULL }, + { "repeat-time", SET_OPTION_NUMBER, 0, SHRT_MAX, NULL }, + { "set-remain-on-exit", SET_OPTION_FLAG, 0, 0, NULL }, + { "set-titles", SET_OPTION_FLAG, 0, 0, NULL }, + { "status", SET_OPTION_FLAG, 0, 0, NULL }, + { "status-attr", SET_OPTION_ATTRIBUTES, 0, 0, NULL }, + { "status-bg", SET_OPTION_COLOUR, 0, 0, NULL }, + { "status-fg", SET_OPTION_COLOUR, 0, 0, NULL }, + { "status-interval", SET_OPTION_NUMBER, 0, INT_MAX, NULL }, + { "status-keys", SET_OPTION_CHOICE, 0, 0, set_option_status_keys_list }, + { "status-left", SET_OPTION_STRING, 0, 0, NULL }, + { "status-left-length", SET_OPTION_NUMBER, 0, SHRT_MAX, NULL }, + { "status-right", SET_OPTION_STRING, 0, 0, NULL }, + { "status-right-length", SET_OPTION_NUMBER, 0, SHRT_MAX, NULL }, +}; + +int +cmd_set_option_exec(struct cmd *self, struct cmd_ctx *ctx) +{ + struct cmd_option_data *data = self->data; + struct session *s; + struct client *c; + struct options *oo; + const struct set_option_entry *entry; + u_int i; + + if (data->flags & CMD_GFLAG) + oo = &global_options; + else { + if ((s = cmd_find_session(ctx, data->target)) == NULL) + return (-1); + oo = &s->options; + } + + if (*data->option == '\0') { + ctx->error(ctx, "invalid option"); + return (-1); + } + + entry = NULL; + for (i = 0; i < NSETOPTION; i++) { + if (strncmp(set_option_table[i].name, + data->option, strlen(data->option)) != 0) + continue; + if (entry != NULL) { + ctx->error(ctx, "ambiguous option: %s", data->option); + return (-1); + } + entry = &set_option_table[i]; + + /* Bail now if an exact match. */ + if (strcmp(entry->name, data->option) == 0) + break; + } + if (entry == NULL) { + ctx->error(ctx, "unknown option: %s", data->option); + return (-1); + } + + if (data->flags & CMD_UFLAG) { + if (data->flags & CMD_GFLAG) { + ctx->error(ctx, + "can't unset global option: %s", entry->name); + return (-1); + } + if (data->value != NULL) { + ctx->error(ctx, + "value passed to unset option: %s", entry->name); + return (-1); + } + + options_remove(oo, entry->name); + ctx->info(ctx, "unset option: %s", entry->name); + } else { + switch (entry->type) { + case SET_OPTION_STRING: + set_option_string(ctx, oo, entry, data->value); + break; + case SET_OPTION_NUMBER: + set_option_number(ctx, oo, entry, data->value); + break; + case SET_OPTION_KEY: + set_option_key(ctx, oo, entry, data->value); + break; + case SET_OPTION_COLOUR: + set_option_colour(ctx, oo, entry, data->value); + break; + case SET_OPTION_ATTRIBUTES: + set_option_attributes(ctx, oo, entry, data->value); + break; + case SET_OPTION_FLAG: + set_option_flag(ctx, oo, entry, data->value); + break; + case SET_OPTION_CHOICE: + set_option_choice(ctx, oo, entry, data->value); + break; + } + } + + recalculate_sizes(); + for (i = 0; i < ARRAY_LENGTH(&clients); i++) { + c = ARRAY_ITEM(&clients, i); + if (c != NULL && c->session != NULL) + server_redraw_client(c); + } + + return (0); +} diff --git a/usr.bin/tmux/cmd-set-password.c b/usr.bin/tmux/cmd-set-password.c new file mode 100644 index 00000000000..262ff7be794 --- /dev/null +++ b/usr.bin/tmux/cmd-set-password.c @@ -0,0 +1,169 @@ +/* $OpenBSD: cmd-set-password.c,v 1.1 2009/06/01 22:58:49 nicm 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 <pwd.h> +#include <unistd.h> + +#include "tmux.h" + +/* + * Set server password. + */ + +int cmd_set_password_parse(struct cmd *, int, char **, char **); +int cmd_set_password_exec(struct cmd *, struct cmd_ctx *); +void cmd_set_password_send(struct cmd *, struct buffer *); +void cmd_set_password_recv(struct cmd *, struct buffer *); +void cmd_set_password_free(struct cmd *); +void cmd_set_password_init(struct cmd *, int); +size_t cmd_set_password_print(struct cmd *, char *, size_t); + +struct cmd_set_password_data { + char *password; + int flag_encrypted; +}; + +const struct cmd_entry cmd_set_password_entry = { + "set-password", "pass", + "[-c] password", + 0, + cmd_set_password_init, + cmd_set_password_parse, + cmd_set_password_exec, + cmd_set_password_send, + cmd_set_password_recv, + cmd_set_password_free, + cmd_set_password_print +}; + +void +cmd_set_password_init(struct cmd *self, unused int arg) +{ + struct cmd_set_password_data *data; + + self->data = data = xmalloc(sizeof *data); + data->password = NULL; + data->flag_encrypted = 0; +} + +int +cmd_set_password_parse(struct cmd *self, int argc, char **argv, char **cause) +{ + struct cmd_set_password_data *data; + int opt; + char *out; + + self->entry->init(self, 0); + data = self->data; + + while ((opt = getopt(argc, argv, "c")) != -1) { + switch (opt) { + case 'c': + data->flag_encrypted = 1; + break; + default: + goto usage; + } + } + argc -= optind; + argv += optind; + if (argc != 1) + goto usage; + + if (!data->flag_encrypted) { + if ((out = crypt(argv[0], "$1")) != NULL) + data->password = xstrdup(out); + } else + data->password = xstrdup(argv[0]); + + return (0); + +usage: + xasprintf(cause, "usage: %s %s", self->entry->name, self->entry->usage); + + self->entry->free(self); + return (-1); +} + +int +cmd_set_password_exec(struct cmd *self, struct cmd_ctx *ctx) +{ + struct cmd_set_password_data *data = self->data; + + if (data->password == NULL) { + ctx->error(ctx, "failed to encrypt password"); + return (-1); + } + + if (server_password != NULL) + xfree(server_password); + if (*data->password == '\0') + server_password = NULL; + else + server_password = xstrdup(data->password); + log_debug("pw now %s", server_password); + + return (0); +} + +void +cmd_set_password_send(struct cmd *self, struct buffer *b) +{ + struct cmd_set_password_data *data = self->data; + + buffer_write(b, data, sizeof *data); + cmd_send_string(b, data->password); +} + +void +cmd_set_password_recv(struct cmd *self, struct buffer *b) +{ + struct cmd_set_password_data *data; + + self->data = data = xmalloc(sizeof *data); + buffer_read(b, data, sizeof *data); + data->password = cmd_recv_string(b); +} + +void +cmd_set_password_free(struct cmd *self) +{ + struct cmd_set_password_data *data = self->data; + + if (data->password != NULL) + xfree(data->password); + xfree(data); +} + +size_t +cmd_set_password_print(struct cmd *self, char *buf, size_t len) +{ + struct cmd_set_password_data *data = self->data; + size_t off = 0; + + off += xsnprintf(buf, len, "%s", self->entry->name); + if (data == NULL) + return (off); + if (off < len && data->flag_encrypted) + off += xsnprintf(buf + off, len - off, " -c"); + if (off < len && data->password != NULL) + off += xsnprintf(buf + off, len - off, " password"); + return (off); +} diff --git a/usr.bin/tmux/cmd-set-window-option.c b/usr.bin/tmux/cmd-set-window-option.c new file mode 100644 index 00000000000..9995fd3c2cf --- /dev/null +++ b/usr.bin/tmux/cmd-set-window-option.c @@ -0,0 +1,170 @@ +/* $OpenBSD: cmd-set-window-option.c,v 1.1 2009/06/01 22:58:49 nicm Exp $ */ + +/* + * Copyright (c) 2008 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 <stdlib.h> +#include <string.h> + +#include "tmux.h" + +/* + * Set a window option. + */ + +int cmd_set_window_option_parse(struct cmd *, int, char **, char **); +int cmd_set_window_option_exec(struct cmd *, struct cmd_ctx *); +void cmd_set_window_option_send(struct cmd *, struct buffer *); +void cmd_set_window_option_recv(struct cmd *, struct buffer *); +void cmd_set_window_option_free(struct cmd *); +size_t cmd_set_window_option_print(struct cmd *, char *, size_t); + +const struct cmd_entry cmd_set_window_option_entry = { + "set-window-option", "setw", + CMD_OPTION_WINDOW_USAGE, + CMD_GFLAG|CMD_UFLAG, + NULL, + cmd_option_parse, + cmd_set_window_option_exec, + cmd_option_send, + cmd_option_recv, + cmd_option_free, + cmd_option_print +}; + +const char *set_option_mode_keys_list[] = { + "emacs", "vi", NULL +}; +const char *set_option_clock_mode_style_list[] = { + "12", "24", NULL +}; +const struct set_option_entry set_window_option_table[NSETWINDOWOPTION] = { + { "aggressive-resize", SET_OPTION_FLAG, 0, 0, NULL }, + { "automatic-rename", SET_OPTION_FLAG, 0, 0, NULL }, + { "clock-mode-colour", SET_OPTION_COLOUR, 0, 0, NULL }, + { "clock-mode-style", + SET_OPTION_CHOICE, 0, 0, set_option_clock_mode_style_list }, + { "force-height", SET_OPTION_NUMBER, 0, INT_MAX, NULL }, + { "force-width", SET_OPTION_NUMBER, 0, INT_MAX, NULL }, + { "main-pane-width", SET_OPTION_NUMBER, 1, INT_MAX, NULL }, + { "mode-attr", SET_OPTION_ATTRIBUTES, 0, 0, NULL }, + { "mode-bg", SET_OPTION_COLOUR, 0, 0, NULL }, + { "mode-fg", SET_OPTION_COLOUR, 0, 0, NULL }, + { "mode-keys", SET_OPTION_CHOICE, 0, 0, set_option_mode_keys_list }, + { "monitor-activity", SET_OPTION_FLAG, 0, 0, NULL }, + { "monitor-content", SET_OPTION_STRING, 0, 0, NULL }, + { "remain-on-exit", SET_OPTION_FLAG, 0, 0, NULL }, + { "utf8", SET_OPTION_FLAG, 0, 0, NULL }, + { "window-status-attr", SET_OPTION_ATTRIBUTES, 0, 0, NULL }, + { "window-status-bg", SET_OPTION_COLOUR, 0, 0, NULL }, + { "window-status-fg", SET_OPTION_COLOUR, 0, 0, NULL }, + { "xterm-keys", SET_OPTION_FLAG, 0, 0, NULL }, +}; + +int +cmd_set_window_option_exec(struct cmd *self, struct cmd_ctx *ctx) +{ + struct cmd_option_data *data = self->data; + struct winlink *wl; + struct client *c; + struct options *oo; + const struct set_option_entry *entry; + u_int i; + + if (data->flags & CMD_GFLAG) + oo = &global_window_options; + else { + if ((wl = cmd_find_window(ctx, data->target, NULL)) == NULL) + return (-1); + oo = &wl->window->options; + } + + if (*data->option == '\0') { + ctx->error(ctx, "invalid option"); + return (-1); + } + + entry = NULL; + for (i = 0; i < NSETWINDOWOPTION; i++) { + if (strncmp(set_window_option_table[i].name, + data->option, strlen(data->option)) != 0) + continue; + if (entry != NULL) { + ctx->error(ctx, "ambiguous option: %s", data->option); + return (-1); + } + entry = &set_window_option_table[i]; + + /* Bail now if an exact match. */ + if (strcmp(entry->name, data->option) == 0) + break; + } + if (entry == NULL) { + ctx->error(ctx, "unknown option: %s", data->option); + return (-1); + } + + if (data->flags & CMD_UFLAG) { + if (data->flags & CMD_GFLAG) { + ctx->error(ctx, + "can't unset global option: %s", entry->name); + return (-1); + } + if (data->value != NULL) { + ctx->error(ctx, + "value passed to unset option: %s", entry->name); + return (-1); + } + + options_remove(oo, entry->name); + ctx->info(ctx, "unset option: %s", entry->name); + } else { + switch (entry->type) { + case SET_OPTION_STRING: + set_option_string(ctx, oo, entry, data->value); + break; + case SET_OPTION_NUMBER: + set_option_number(ctx, oo, entry, data->value); + break; + case SET_OPTION_KEY: + set_option_key(ctx, oo, entry, data->value); + break; + case SET_OPTION_COLOUR: + set_option_colour(ctx, oo, entry, data->value); + break; + case SET_OPTION_ATTRIBUTES: + set_option_attributes(ctx, oo, entry, data->value); + break; + case SET_OPTION_FLAG: + set_option_flag(ctx, oo, entry, data->value); + break; + case SET_OPTION_CHOICE: + set_option_choice(ctx, oo, entry, data->value); + break; + } + } + + recalculate_sizes(); + for (i = 0; i < ARRAY_LENGTH(&clients); i++) { + c = ARRAY_ITEM(&clients, i); + if (c != NULL && c->session != NULL) + server_redraw_client(c); + } + + return (0); +} diff --git a/usr.bin/tmux/cmd-show-buffer.c b/usr.bin/tmux/cmd-show-buffer.c new file mode 100644 index 00000000000..30c28eafd86 --- /dev/null +++ b/usr.bin/tmux/cmd-show-buffer.c @@ -0,0 +1,89 @@ +/* $OpenBSD: cmd-show-buffer.c,v 1.1 2009/06/01 22:58:49 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 <stdlib.h> + +#include "tmux.h" + +/* + * Show a session paste buffer. + */ + +int cmd_show_buffer_exec(struct cmd *, struct cmd_ctx *); + +const struct cmd_entry cmd_show_buffer_entry = { + "show-buffer", "showb", + CMD_BUFFER_SESSION_USAGE, + 0, + cmd_buffer_init, + cmd_buffer_parse, + cmd_show_buffer_exec, + cmd_buffer_send, + cmd_buffer_recv, + cmd_buffer_free, + cmd_buffer_print +}; + +int +cmd_show_buffer_exec(struct cmd *self, struct cmd_ctx *ctx) +{ + struct cmd_buffer_data *data = self->data; + struct session *s; + struct paste_buffer *pb; + u_int size; + char *buf, *ptr; + size_t len; + + if ((s = cmd_find_session(ctx, data->target)) == NULL) + return (-1); + + if (data->buffer == -1) { + if ((pb = paste_get_top(&s->buffers)) == NULL) { + ctx->error(ctx, "no buffers"); + return (-1); + } + } else if ((pb = paste_get_index(&s->buffers, data->buffer)) == NULL) { + ctx->error(ctx, "no buffer %d", data->buffer); + return (-1); + } + + if (pb != NULL) { + size = s->sx; + + buf = xmalloc(size + 1); + len = 0; + + ptr = pb->data; + do { + buf[len++] = *ptr++; + + if (len == size) { + buf[len] = '\0'; + ctx->print(ctx, buf); + + len = 0; + } + } while (*ptr != '\0'); + buf[len] = '\0'; + ctx->print(ctx, buf); + } + + return (0); +} diff --git a/usr.bin/tmux/cmd-show-options.c b/usr.bin/tmux/cmd-show-options.c new file mode 100644 index 00000000000..862e666bbfd --- /dev/null +++ b/usr.bin/tmux/cmd-show-options.c @@ -0,0 +1,110 @@ +/* $OpenBSD: cmd-show-options.c,v 1.1 2009/06/01 22:58:49 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 <stdlib.h> +#include <string.h> + +#include "tmux.h" + +/* + * Show options. + */ + +int cmd_show_options_exec(struct cmd *, struct cmd_ctx *); + +const struct cmd_entry cmd_show_options_entry = { + "show-options", "show", + "[-g] " CMD_TARGET_SESSION_USAGE, + CMD_GFLAG, + cmd_target_init, + cmd_target_parse, + cmd_show_options_exec, + cmd_target_send, + cmd_target_recv, + cmd_target_free, + cmd_target_print +}; + +int +cmd_show_options_exec(struct cmd *self, struct cmd_ctx *ctx) +{ + struct cmd_target_data *data = self->data; + struct session *s; + struct options *oo; + const struct set_option_entry *entry; + u_int i; + char *vs; + long long vn; + + if (data->flags & CMD_GFLAG) + oo = &global_options; + else { + if ((s = cmd_find_session(ctx, data->target)) == NULL) + return (-1); + oo = &s->options; + } + + for (i = 0; i < NSETOPTION; i++) { + entry = &set_option_table[i]; + + if (options_find1(oo, entry->name) == NULL) + continue; + + switch (entry->type) { + case SET_OPTION_STRING: + vs = options_get_string(oo, entry->name); + ctx->print(ctx, "%s \"%s\"", entry->name, vs); + break; + case SET_OPTION_NUMBER: + vn = options_get_number(oo, entry->name); + ctx->print(ctx, "%s %lld", entry->name, vn); + break; + case SET_OPTION_KEY: + vn = options_get_number(oo, entry->name); + ctx->print(ctx, "%s %s", + entry->name, key_string_lookup_key(vn)); + break; + case SET_OPTION_COLOUR: + vn = options_get_number(oo, entry->name); + ctx->print(ctx, "%s %s", + entry->name, colour_tostring(vn)); + break; + case SET_OPTION_ATTRIBUTES: + vn = options_get_number(oo, entry->name); + ctx->print(ctx, "%s %s", + entry->name, attributes_tostring(vn)); + break; + case SET_OPTION_FLAG: + vn = options_get_number(oo, entry->name); + if (vn) + ctx->print(ctx, "%s on", entry->name); + else + ctx->print(ctx, "%s off", entry->name); + break; + case SET_OPTION_CHOICE: + vn = options_get_number(oo, entry->name); + ctx->print(ctx, "%s %s", + entry->name, entry->choices[vn]); + break; + } + } + + return (0); +} diff --git a/usr.bin/tmux/cmd-show-window-options.c b/usr.bin/tmux/cmd-show-window-options.c new file mode 100644 index 00000000000..02aa4472343 --- /dev/null +++ b/usr.bin/tmux/cmd-show-window-options.c @@ -0,0 +1,110 @@ +/* $OpenBSD: cmd-show-window-options.c,v 1.1 2009/06/01 22:58:49 nicm Exp $ */ + +/* + * Copyright (c) 2008 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 <stdlib.h> +#include <string.h> + +#include "tmux.h" + +/* + * Show window options. + */ + +int cmd_show_window_options_exec(struct cmd *, struct cmd_ctx *); + +const struct cmd_entry cmd_show_window_options_entry = { + "show-window-options", "showw", + "[-g] " CMD_TARGET_WINDOW_USAGE, + CMD_GFLAG, + cmd_target_init, + cmd_target_parse, + cmd_show_window_options_exec, + cmd_target_send, + cmd_target_recv, + cmd_target_free, + cmd_target_print +}; + +int +cmd_show_window_options_exec(struct cmd *self, struct cmd_ctx *ctx) +{ + struct cmd_target_data *data = self->data; + struct winlink *wl; + struct options *oo; + const struct set_option_entry *entry; + u_int i; + char *vs; + long long vn; + + if (data->flags & CMD_GFLAG) + oo = &global_window_options; + else { + if ((wl = cmd_find_window(ctx, data->target, NULL)) == NULL) + return (-1); + oo = &wl->window->options; + } + + for (i = 0; i < NSETWINDOWOPTION; i++) { + entry = &set_window_option_table[i]; + + if (options_find1(oo, entry->name) == NULL) + continue; + + switch (entry->type) { + case SET_OPTION_STRING: + vs = options_get_string(oo, entry->name); + ctx->print(ctx, "%s \"%s\"", entry->name, vs); + break; + case SET_OPTION_NUMBER: + vn = options_get_number(oo, entry->name); + ctx->print(ctx, "%s %lld", entry->name, vn); + break; + case SET_OPTION_KEY: + vn = options_get_number(oo, entry->name); + ctx->print(ctx, "%s %s", + entry->name, key_string_lookup_key(vn)); + break; + case SET_OPTION_COLOUR: + vn = options_get_number(oo, entry->name); + ctx->print(ctx, "%s %s", + entry->name, colour_tostring(vn)); + break; + case SET_OPTION_ATTRIBUTES: + vn = options_get_number(oo, entry->name); + ctx->print(ctx, "%s %s", + entry->name, attributes_tostring(vn)); + break; + case SET_OPTION_FLAG: + vn = options_get_number(oo, entry->name); + if (vn) + ctx->print(ctx, "%s on", entry->name); + else + ctx->print(ctx, "%s off", entry->name); + break; + case SET_OPTION_CHOICE: + vn = options_get_number(oo, entry->name); + ctx->print(ctx, "%s %s", + entry->name, entry->choices[vn]); + break; + } + } + + return (0); +} diff --git a/usr.bin/tmux/cmd-source-file.c b/usr.bin/tmux/cmd-source-file.c new file mode 100644 index 00000000000..d26d5213418 --- /dev/null +++ b/usr.bin/tmux/cmd-source-file.c @@ -0,0 +1,147 @@ +/* $OpenBSD: cmd-source-file.c,v 1.1 2009/06/01 22:58:49 nicm Exp $ */ + +/* + * Copyright (c) 2008 Tiago Cunha <me@tiagocunha.org> + * + * 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 "tmux.h" + +/* + * Sources a configuration file. + */ + +int cmd_source_file_parse(struct cmd *, int, char **, char **); +int cmd_source_file_exec(struct cmd *, struct cmd_ctx *); +void cmd_source_file_send(struct cmd *, struct buffer *); +void cmd_source_file_recv(struct cmd *, struct buffer *); +void cmd_source_file_free(struct cmd *); +void cmd_source_file_init(struct cmd *, int); +size_t cmd_source_file_print(struct cmd *, char *, size_t); + +struct cmd_source_file_data { + char *path; +}; + +const struct cmd_entry cmd_source_file_entry = { + "source-file", "source", + "path", + 0, + cmd_source_file_init, + cmd_source_file_parse, + cmd_source_file_exec, + cmd_source_file_send, + cmd_source_file_recv, + cmd_source_file_free, + cmd_source_file_print +}; + +void +cmd_source_file_init(struct cmd *self, unused int arg) +{ + struct cmd_source_file_data *data; + + self->data = data = xmalloc(sizeof *data); + data->path = NULL; +} + +int +cmd_source_file_parse(struct cmd *self, int argc, char **argv, char **cause) +{ + struct cmd_source_file_data *data; + int opt; + + self->entry->init(self, 0); + data = self->data; + + while ((opt = getopt(argc, argv, "")) != -1) { + switch (opt) { + default: + goto usage; + } + } + argc -= optind; + argv += optind; + if (argc != 1) + goto usage; + + data->path = xstrdup(argv[0]); + return (0); + +usage: + xasprintf(cause, "usage: %s %s", self->entry->name, self->entry->usage); + + self->entry->free(self); + return (-1); +} + +int +cmd_source_file_exec(struct cmd *self, struct cmd_ctx *ctx) +{ + struct cmd_source_file_data *data = self->data; + char *cause; + + if (load_cfg(data->path, &cause) != 0) { + ctx->error(ctx, "%s", cause); + xfree(cause); + return (-1); + } + + return (0); +} + +void +cmd_source_file_send(struct cmd *self, struct buffer *b) +{ + struct cmd_source_file_data *data = self->data; + + buffer_write(b, data, sizeof *data); + cmd_send_string(b, data->path); +} + +void +cmd_source_file_recv(struct cmd *self, struct buffer *b) +{ + struct cmd_source_file_data *data; + + self->data = data = xmalloc(sizeof *data); + buffer_read(b, data, sizeof *data); + data->path = cmd_recv_string(b); +} + +void +cmd_source_file_free(struct cmd *self) +{ + struct cmd_source_file_data *data = self->data; + + if (data->path != NULL) + xfree(data->path); + xfree(data); +} + +size_t +cmd_source_file_print(struct cmd *self, char *buf, size_t len) +{ + struct cmd_source_file_data *data = self->data; + size_t off = 0; + + off += xsnprintf(buf, len, "%s", self->entry->name); + if (data == NULL) + return (off); + if (off < len && data->path != NULL) + off += cmd_prarg(buf + off, len - off, " ", data->path); + return (off); +} diff --git a/usr.bin/tmux/cmd-split-window.c b/usr.bin/tmux/cmd-split-window.c new file mode 100644 index 00000000000..fd41034f279 --- /dev/null +++ b/usr.bin/tmux/cmd-split-window.c @@ -0,0 +1,235 @@ +/* $OpenBSD: cmd-split-window.c,v 1.1 2009/06/01 22:58:49 nicm 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 <stdlib.h> +#include <unistd.h> + +#include "tmux.h" + +/* + * Split a window (add a new pane). + */ + +int cmd_split_window_parse(struct cmd *, int, char **, char **); +int cmd_split_window_exec(struct cmd *, struct cmd_ctx *); +void cmd_split_window_send(struct cmd *, struct buffer *); +void cmd_split_window_recv(struct cmd *, struct buffer *); +void cmd_split_window_free(struct cmd *); +void cmd_split_window_init(struct cmd *, int); +size_t cmd_split_window_print(struct cmd *, char *, size_t); + +struct cmd_split_window_data { + char *target; + char *cmd; + int flag_detached; + int percentage; + int lines; +}; + +const struct cmd_entry cmd_split_window_entry = { + "split-window", "splitw", + "[-d] [-p percentage|-l lines] [-t target-window] [command]", + 0, + cmd_split_window_init, + cmd_split_window_parse, + cmd_split_window_exec, + cmd_split_window_send, + cmd_split_window_recv, + cmd_split_window_free, + cmd_split_window_print +}; + +void +cmd_split_window_init(struct cmd *self, unused int arg) +{ + struct cmd_split_window_data *data; + + self->data = data = xmalloc(sizeof *data); + data->target = NULL; + data->cmd = NULL; + data->flag_detached = 0; + data->percentage = -1; + data->lines = -1; +} + +int +cmd_split_window_parse(struct cmd *self, int argc, char **argv, char **cause) +{ + struct cmd_split_window_data *data; + int opt, n; + const char *errstr; + + self->entry->init(self, 0); + data = self->data; + + while ((opt = getopt(argc, argv, "dl:p:t:")) != -1) { + switch (opt) { + case 'd': + data->flag_detached = 1; + break; + case 't': + if (data->target == NULL) + data->target = xstrdup(optarg); + break; + case 'l': + if (data->percentage == -1 && data->lines == -1) { + n = strtonum(optarg, 1, INT_MAX, &errstr); + if (errstr != NULL) { + xasprintf(cause, "lines %s", errstr); + goto error; + } + data->lines = n; + } + break; + case 'p': + if (data->lines == -1 && data->percentage == -1) { + n = strtonum(optarg, 1, 100, &errstr); + if (errstr != NULL) { + xasprintf( + cause, "percentage %s", errstr); + goto error; + } + data->percentage = n; + } + break; + default: + goto usage; + } + } + argc -= optind; + argv += optind; + if (argc != 0 && argc != 1) + goto usage; + + if (argc == 1) + data->cmd = xstrdup(argv[0]); + + return (0); + +usage: + xasprintf(cause, "usage: %s %s", self->entry->name, self->entry->usage); + +error: + self->entry->free(self); + return (-1); +} + +int +cmd_split_window_exec(struct cmd *self, struct cmd_ctx *ctx) +{ + struct cmd_split_window_data *data = self->data; + struct session *s; + struct winlink *wl; + struct window *w; + struct window_pane *wp; + const char **env; + char *cmd, *cwd, *cause; + u_int hlimit, lines; + + if ((wl = cmd_find_window(ctx, data->target, &s)) == NULL) + return (-1); + w = wl->window; + + env = server_fill_environ(s); + + cmd = data->cmd; + if (cmd == NULL) + cmd = options_get_string(&s->options, "default-command"); + if (ctx->cmdclient == NULL || ctx->cmdclient->cwd == NULL) + cwd = options_get_string(&global_options, "default-path"); + else + cwd = ctx->cmdclient->cwd; + + lines = -1; + if (data->lines != -1) + lines = data->lines; + else if (data->percentage != -1) + lines = (w->active->sy * data->percentage) / 100; + + hlimit = options_get_number(&s->options, "history-limit"); + wp = window_add_pane(w, lines, cmd, cwd, env, hlimit, &cause); + if (wp == NULL) { + ctx->error(ctx, "create pane failed: %s", cause); + xfree(cause); + return (-1); + } + server_redraw_window(w); + + if (!data->flag_detached) { + window_set_active_pane(w, wp); + session_select(s, wl->idx); + server_redraw_session(s); + } else + server_status_session(s); + layout_refresh(w, 0); + + return (0); +} + +void +cmd_split_window_send(struct cmd *self, struct buffer *b) +{ + struct cmd_split_window_data *data = self->data; + + buffer_write(b, data, sizeof *data); + cmd_send_string(b, data->target); + cmd_send_string(b, data->cmd); +} + +void +cmd_split_window_recv(struct cmd *self, struct buffer *b) +{ + struct cmd_split_window_data *data; + + self->data = data = xmalloc(sizeof *data); + buffer_read(b, data, sizeof *data); + data->target = cmd_recv_string(b); + data->cmd = cmd_recv_string(b); +} + +void +cmd_split_window_free(struct cmd *self) +{ + struct cmd_split_window_data *data = self->data; + + if (data->target != NULL) + xfree(data->target); + if (data->cmd != NULL) + xfree(data->cmd); + xfree(data); +} + +size_t +cmd_split_window_print(struct cmd *self, char *buf, size_t len) +{ + struct cmd_split_window_data *data = self->data; + size_t off = 0; + + off += xsnprintf(buf, len, "%s", self->entry->name); + if (data == NULL) + return (off); + if (off < len && data->flag_detached) + off += xsnprintf(buf + off, len - off, " -d"); + if (off < len && data->target != NULL) + off += cmd_prarg(buf + off, len - off, " -t ", data->target); + if (off < len && data->cmd != NULL) + off += cmd_prarg(buf + off, len - off, " ", data->cmd); + return (off); +} diff --git a/usr.bin/tmux/cmd-start-server.c b/usr.bin/tmux/cmd-start-server.c new file mode 100644 index 00000000000..57dbd8cd376 --- /dev/null +++ b/usr.bin/tmux/cmd-start-server.c @@ -0,0 +1,46 @@ +/* $OpenBSD: cmd-start-server.c,v 1.1 2009/06/01 22:58:49 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 "tmux.h" + +/* + * Start the server and do nothing else. + */ + +int cmd_start_server_exec(struct cmd *, struct cmd_ctx *); + +const struct cmd_entry cmd_start_server_entry = { + "start-server", "start", + "", + CMD_STARTSERVER, + NULL, + NULL, + cmd_start_server_exec, + NULL, + NULL, + NULL, + NULL +}; + +int +cmd_start_server_exec(unused struct cmd *self, unused struct cmd_ctx *ctx) +{ + return (0); +} diff --git a/usr.bin/tmux/cmd-string.c b/usr.bin/tmux/cmd-string.c new file mode 100644 index 00000000000..d151a397bd8 --- /dev/null +++ b/usr.bin/tmux/cmd-string.c @@ -0,0 +1,309 @@ +/* $OpenBSD: cmd-string.c,v 1.1 2009/06/01 22:58:49 nicm Exp $ */ + +/* + * Copyright (c) 2008 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 <errno.h> +#include <stdio.h> +#include <string.h> +#include <stdlib.h> + +#include "tmux.h" + +/* + * Parse a command from a string. + */ + +int cmd_string_getc(const char *, size_t *); +void cmd_string_ungetc(const char *, size_t *); +char *cmd_string_string(const char *, size_t *, char, int); +char *cmd_string_variable(const char *, size_t *); + +int +cmd_string_getc(const char *s, size_t *p) +{ + if (s[*p] == '\0') + return (EOF); + return (s[(*p)++]); +} + +void +cmd_string_ungetc(unused const char *s, size_t *p) +{ + (*p)--; +} + +/* + * Parse command string. Returns -1 on error. If returning -1, cause is error + * string, or NULL for empty command. + */ +int +cmd_string_parse(const char *s, struct cmd_list **cmdlist, char **cause) +{ + size_t p; + int ch, argc, rval, have_arg; + char **argv, *buf, *t, *u; + size_t len; + + if ((t = strchr(s, ' ')) == NULL && (t = strchr(s, '\t')) == NULL) + t = strchr(s, '\0'); + if ((u = strchr(s, '=')) != NULL && u < t) { + if (putenv((char *) s) != 0) { + xasprintf(cause, "assignment failed: %s", s); + return (-1); + } + *cmdlist = NULL; + return (0); + } + + argv = NULL; + argc = 0; + + buf = NULL; + len = 0; + + have_arg = 0; + + *cause = NULL; + + *cmdlist = NULL; + rval = -1; + + p = 0; + for (;;) { + ch = cmd_string_getc(s, &p); + switch (ch) { + case '\'': + if ((t = cmd_string_string(s, &p, '\'', 0)) == NULL) + goto error; + buf = xrealloc(buf, 1, len + strlen(t) + 1); + strlcpy(buf + len, t, strlen(t) + 1); + len += strlen(t); + xfree(t); + + have_arg = 1; + break; + case '"': + if ((t = cmd_string_string(s, &p, '"', 1)) == NULL) + goto error; + buf = xrealloc(buf, 1, len + strlen(t) + 1); + strlcpy(buf + len, t, strlen(t) + 1); + len += strlen(t); + xfree(t); + + have_arg = 1; + break; + case '$': + if ((t = cmd_string_variable(s, &p)) == NULL) + goto error; + buf = xrealloc(buf, 1, len + strlen(t) + 1); + strlcpy(buf + len, t, strlen(t) + 1); + len += strlen(t); + + have_arg = 1; + break; + case '#': + /* Comment: discard rest of line. */ + while ((ch = cmd_string_getc(s, &p)) != EOF) + ; + /* FALLTHROUGH */ + case EOF: + case ' ': + case '\t': + if (have_arg) { + buf = xrealloc(buf, 1, len + 1); + buf[len] = '\0'; + + argv = xrealloc(argv, argc + 1, sizeof *argv); + argv[argc++] = buf; + + buf = NULL; + len = 0; + + have_arg = 0; + } + + if (ch != EOF) + break; + if (argc == 0) + goto out; + + *cmdlist = cmd_list_parse(argc, argv, cause); + if (*cmdlist == NULL) + goto out; + + do + xfree(argv[argc - 1]); + while (--argc > 0); + + rval = 0; + goto out; + default: + if (len >= SIZE_MAX - 2) + goto error; + + buf = xrealloc(buf, 1, len + 1); + buf[len++] = ch; + + have_arg = 1; + break; + } + } + +error: + xasprintf(cause, "invalid or unknown command: %s", s); + +out: + if (buf != NULL) + xfree(buf); + + while (--argc >= 0) + xfree(argv[argc]); + if (argv != NULL) + xfree(argv); + + return (rval); +} + +char * +cmd_string_string(const char *s, size_t *p, char endch, int esc) +{ + int ch; + char *buf, *t; + size_t len; + + buf = NULL; + len = 0; + + while ((ch = cmd_string_getc(s, p)) != endch) { + switch (ch) { + case EOF: + goto error; + case '\\': + if (!esc) + break; + switch (ch = cmd_string_getc(s, p)) { + case EOF: + goto error; + case 'r': + ch = '\r'; + break; + case 'n': + ch = '\n'; + break; + case 't': + ch = '\t'; + break; + } + break; + case '$': + if (!esc) + break; + if ((t = cmd_string_variable(s, p)) == NULL) + goto error; + buf = xrealloc(buf, 1, len + strlen(t) + 1); + strlcpy(buf + len, t, strlen(t) + 1); + len += strlen(t); + continue; + } + + if (len >= SIZE_MAX - 2) + goto error; + buf = xrealloc(buf, 1, len + 1); + buf[len++] = ch; + } + + buf = xrealloc(buf, 1, len + 1); + buf[len] = '\0'; + return (buf); + +error: + if (buf != NULL) + xfree(buf); + return (NULL); +} + +char * +cmd_string_variable(const char *s, size_t *p) +{ + int ch, fch; + char *buf, *t; + size_t len; + +#define cmd_string_first(ch) ((ch) == '_' || \ + ((ch) >= 'a' && (ch) <= 'z') || ((ch) >= 'A' && (ch) <= 'Z')) +#define cmd_string_other(ch) ((ch) == '_' || \ + ((ch) >= 'a' && (ch) <= 'z') || ((ch) >= 'A' && (ch) <= 'Z') || \ + ((ch) >= '0' && (ch) <= '9')) + + buf = NULL; + len = 0; + + fch = EOF; + switch (ch = cmd_string_getc(s, p)) { + case EOF: + goto error; + case '{': + fch = '{'; + + ch = cmd_string_getc(s, p); + if (!cmd_string_first(ch)) + goto error; + /* FALLTHROUGH */ + default: + if (!cmd_string_first(ch)) { + xasprintf(&t, "$%c", ch); + return (t); + } + + buf = xrealloc(buf, 1, len + 1); + buf[len++] = ch; + + for (;;) { + ch = cmd_string_getc(s, p); + if (ch == EOF || !cmd_string_other(ch)) + break; + else { + if (len >= SIZE_MAX - 3) + goto error; + buf = xrealloc(buf, 1, len + 1); + buf[len++] = ch; + } + } + } + + if (fch == '{' && ch != '}') + goto error; + if (ch != EOF && fch != '{') + cmd_string_ungetc(s, p); /* ch */ + + buf = xrealloc(buf, 1, len + 1); + buf[len] = '\0'; + + if ((t = getenv(buf)) == NULL) { + xfree(buf); + return (xstrdup("")); + } + xfree(buf); + return (xstrdup(t)); + +error: + if (buf != NULL) + xfree(buf); + return (NULL); +} diff --git a/usr.bin/tmux/cmd-suspend-client.c b/usr.bin/tmux/cmd-suspend-client.c new file mode 100644 index 00000000000..4b4926f0f25 --- /dev/null +++ b/usr.bin/tmux/cmd-suspend-client.c @@ -0,0 +1,64 @@ +/* $OpenBSD: cmd-suspend-client.c,v 1.1 2009/06/01 22:58:49 nicm 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 <stdlib.h> +#include <string.h> + +#include "tmux.h" + +/* + * Suspend client with SIGTSTP. + */ + +int cmd_suspend_client_exec(struct cmd *, struct cmd_ctx *); + +struct cmd_suspend_client_data { + char *name; + char *target; +}; + +const struct cmd_entry cmd_suspend_client_entry = { + "suspend-client", "suspendc", + "[-c target-client]", + 0, + cmd_target_init, + cmd_target_parse, + cmd_suspend_client_exec, + cmd_target_send, + cmd_target_recv, + cmd_target_free, + cmd_target_print +}; + +int +cmd_suspend_client_exec(struct cmd *self, struct cmd_ctx *ctx) +{ + struct cmd_target_data *data = self->data; + struct client *c; + + if ((c = cmd_find_client(ctx, data->target)) == NULL) + return (-1); + + tty_stop_tty(&c->tty); + c->flags |= CLIENT_SUSPENDED; + server_write_client(c, MSG_SUSPEND, NULL, 0); + + return (0); +} diff --git a/usr.bin/tmux/cmd-swap-pane.c b/usr.bin/tmux/cmd-swap-pane.c new file mode 100644 index 00000000000..62852a198ed --- /dev/null +++ b/usr.bin/tmux/cmd-swap-pane.c @@ -0,0 +1,285 @@ +/* $OpenBSD: cmd-swap-pane.c,v 1.1 2009/06/01 22:58:49 nicm 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 <stdlib.h> + +#include "tmux.h" + +/* + * Swap two panes. + */ + +int cmd_swap_pane_parse(struct cmd *, int, char **, char **); +int cmd_swap_pane_exec(struct cmd *, struct cmd_ctx *); +void cmd_swap_pane_send(struct cmd *, struct buffer *); +void cmd_swap_pane_recv(struct cmd *, struct buffer *); +void cmd_swap_pane_free(struct cmd *); +void cmd_swap_pane_init(struct cmd *, int); +size_t cmd_swap_pane_print(struct cmd *, char *, size_t); + +struct cmd_swap_pane_data { + char *target; + int src; + int dst; + int flag_detached; + int flag_up; + int flag_down; +}; + +const struct cmd_entry cmd_swap_pane_entry = { + "swap-pane", "swapp", + "[-dDU] [-t target-window] [-p src-index] [-q dst-index]", + 0, + cmd_swap_pane_init, + cmd_swap_pane_parse, + cmd_swap_pane_exec, + cmd_swap_pane_send, + cmd_swap_pane_recv, + cmd_swap_pane_free, + cmd_swap_pane_print +}; + +void +cmd_swap_pane_init(struct cmd *self, int key) +{ + struct cmd_swap_pane_data *data; + + self->data = data = xmalloc(sizeof *data); + data->target = NULL; + data->src = -1; + data->dst = -1; + data->flag_detached = 0; + data->flag_up = 0; + data->flag_down = 0; + + switch (key) { + case '{': + data->flag_up = 1; + break; + case '}': + data->flag_down = 1; + break; + } +} + +int +cmd_swap_pane_parse(struct cmd *self, int argc, char **argv, char **cause) +{ + struct cmd_swap_pane_data *data; + int opt, n; + const char *errstr; + + self->entry->init(self, 0); + data = self->data; + + while ((opt = getopt(argc, argv, "dDt:p:q:U")) != -1) { + switch (opt) { + case 'd': + data->flag_detached = 1; + break; + case 'D': + data->flag_up = 0; + data->flag_down = 1; + data->dst = -1; + break; + case 't': + if (data->target == NULL) + data->target = xstrdup(optarg); + break; + case 'p': + if (data->src == -1) { + n = strtonum(optarg, 0, INT_MAX, &errstr); + if (errstr != NULL) { + xasprintf(cause, "src %s", errstr); + goto error; + } + data->src = n; + } + break; + case 'q': + if (data->dst == -1) { + n = strtonum(optarg, 0, INT_MAX, &errstr); + if (errstr != NULL) { + xasprintf(cause, "dst %s", errstr); + goto error; + } + data->dst = n; + } + data->flag_up = 0; + data->flag_down = 0; + break; + case 'U': + data->flag_up = 1; + data->flag_down = 0; + data->dst = -1; + break; + + default: + goto usage; + } + } + argc -= optind; + argv += optind; + if (argc != 0) + goto usage; + + return (0); + +usage: + xasprintf(cause, "usage: %s %s", self->entry->name, self->entry->usage); + +error: + self->entry->free(self); + return (-1); +} + +int +cmd_swap_pane_exec(struct cmd *self, struct cmd_ctx *ctx) +{ + struct cmd_swap_pane_data *data = self->data; + struct winlink *wl; + struct window *w; + struct window_pane *tmp_wp, *src_wp, *dst_wp; + u_int xx, yy; + + if (data == NULL) + return (0); + + if ((wl = cmd_find_window(ctx, data->target, NULL)) == NULL) + return (-1); + w = wl->window; + + if (data->src == -1) + src_wp = w->active; + else { + src_wp = window_pane_at_index(w, data->src); + if (src_wp == NULL) { + ctx->error(ctx, "no pane: %d", data->src); + return (-1); + } + } + if (data->dst == -1) + dst_wp = w->active; + else { + dst_wp = window_pane_at_index(w, data->dst); + if (dst_wp == NULL) { + ctx->error(ctx, "no pane: %d", data->dst); + return (-1); + } + } + + if (data->dst == -1 && data->flag_up) { + if ((dst_wp = TAILQ_PREV(src_wp, window_panes, entry)) == NULL) + dst_wp = TAILQ_LAST(&w->panes, window_panes); + } + if (data->dst == -1 && data->flag_down) { + if ((dst_wp = TAILQ_NEXT(src_wp, entry)) == NULL) + dst_wp = TAILQ_FIRST(&w->panes); + } + + if (src_wp == dst_wp) + return (0); + + tmp_wp = TAILQ_PREV(dst_wp, window_panes, entry); + TAILQ_REMOVE(&w->panes, dst_wp, entry); + TAILQ_REPLACE(&w->panes, src_wp, dst_wp, entry); + if (tmp_wp == src_wp) + tmp_wp = dst_wp; + if (tmp_wp == NULL) + TAILQ_INSERT_HEAD(&w->panes, src_wp, entry); + else + TAILQ_INSERT_AFTER(&w->panes, tmp_wp, src_wp, entry); + + xx = src_wp->xoff; + yy = src_wp->yoff; + src_wp->xoff = dst_wp->xoff; + src_wp->yoff = dst_wp->yoff; + dst_wp->xoff = xx; + dst_wp->yoff = yy; + + xx = src_wp->sx; + yy = src_wp->sy; + window_pane_resize(src_wp, dst_wp->sx, dst_wp->sy); + window_pane_resize(dst_wp, xx, yy); + + if (!data->flag_detached) { + window_set_active_pane(w, dst_wp); + layout_refresh(w, 0); + } + + return (0); +} + +void +cmd_swap_pane_send(struct cmd *self, struct buffer *b) +{ + struct cmd_swap_pane_data *data = self->data; + + buffer_write(b, data, sizeof *data); + cmd_send_string(b, data->target); +} + +void +cmd_swap_pane_recv(struct cmd *self, struct buffer *b) +{ + struct cmd_swap_pane_data *data; + + self->data = data = xmalloc(sizeof *data); + buffer_read(b, data, sizeof *data); + data->target = cmd_recv_string(b); +} + +void +cmd_swap_pane_free(struct cmd *self) +{ + struct cmd_swap_pane_data *data = self->data; + + if (data->target != NULL) + xfree(data->target); + xfree(data); +} + +size_t +cmd_swap_pane_print(struct cmd *self, char *buf, size_t len) +{ + struct cmd_swap_pane_data *data = self->data; + size_t off = 0; + + off += xsnprintf(buf, len, "%s", self->entry->name); + if (data == NULL) + return (off); + if (off < len && + (data->flag_down || data->flag_up || data->flag_detached)) { + off += xsnprintf(buf + off, len - off, " -"); + if (off < len && data->flag_detached) + off += xsnprintf(buf + off, len - off, "d"); + if (off < len && data->flag_up) + off += xsnprintf(buf + off, len - off, "D"); + if (off < len && data->flag_down) + off += xsnprintf(buf + off, len - off, "U"); + } + if (off < len && data->target != NULL) + off += cmd_prarg(buf + off, len - off, " -t ", data->target); + if (off < len && data->src != -1) + off += xsnprintf(buf + off, len - off, " -p %d", data->src); + if (off < len && data->dst != -1) + off += xsnprintf(buf + off, len - off, " -q %d", data->dst); + return (off); +} diff --git a/usr.bin/tmux/cmd-swap-window.c b/usr.bin/tmux/cmd-swap-window.c new file mode 100644 index 00000000000..64c0238fc9a --- /dev/null +++ b/usr.bin/tmux/cmd-swap-window.c @@ -0,0 +1,75 @@ +/* $OpenBSD: cmd-swap-window.c,v 1.1 2009/06/01 22:58:49 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 <stdlib.h> + +#include "tmux.h" + +/* + * Swap one window with another. + */ + +int cmd_swap_window_exec(struct cmd *, struct cmd_ctx *); + +const struct cmd_entry cmd_swap_window_entry = { + "swap-window", "swapw", + "[-d] " CMD_SRCDST_WINDOW_USAGE, + CMD_DFLAG, + cmd_srcdst_init, + cmd_srcdst_parse, + cmd_swap_window_exec, + cmd_srcdst_send, + cmd_srcdst_recv, + cmd_srcdst_free, + cmd_srcdst_print +}; + +int +cmd_swap_window_exec(struct cmd *self, struct cmd_ctx *ctx) +{ + struct cmd_srcdst_data *data = self->data; + struct session *src, *dst; + struct winlink *wl_src, *wl_dst; + struct window *w; + + if ((wl_src = cmd_find_window(ctx, data->src, &src)) == NULL) + return (-1); + if ((wl_dst = cmd_find_window(ctx, data->dst, &dst)) == NULL) + return (-1); + + if (wl_dst->window == wl_src->window) + return (0); + + w = wl_dst->window; + wl_dst->window = wl_src->window; + wl_src->window = w; + + if (!(data->flags & CMD_DFLAG)) { + session_select(dst, wl_dst->idx); + if (src != dst) + session_select(src, wl_src->idx); + } + server_redraw_session(src); + if (src != dst) + server_redraw_session(dst); + recalculate_sizes(); + + return (0); +} diff --git a/usr.bin/tmux/cmd-switch-client.c b/usr.bin/tmux/cmd-switch-client.c new file mode 100644 index 00000000000..627e5b8b873 --- /dev/null +++ b/usr.bin/tmux/cmd-switch-client.c @@ -0,0 +1,161 @@ +/* $OpenBSD: cmd-switch-client.c,v 1.1 2009/06/01 22:58:49 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 <stdlib.h> +#include <string.h> + +#include "tmux.h" + +/* + * Switch client to a different session. + */ + +int cmd_switch_client_parse(struct cmd *, int, char **, char **); +int cmd_switch_client_exec(struct cmd *, struct cmd_ctx *); +void cmd_switch_client_send(struct cmd *, struct buffer *); +void cmd_switch_client_recv(struct cmd *, struct buffer *); +void cmd_switch_client_free(struct cmd *); +size_t cmd_switch_client_print(struct cmd *, char *, size_t); + +struct cmd_switch_client_data { + char *name; + char *target; +}; + +const struct cmd_entry cmd_switch_client_entry = { + "switch-client", "switchc", + "[-c target-client] [-t target-session]", + 0, + NULL, + cmd_switch_client_parse, + cmd_switch_client_exec, + cmd_switch_client_send, + cmd_switch_client_recv, + cmd_switch_client_free, + cmd_switch_client_print +}; + +int +cmd_switch_client_parse(struct cmd *self, int argc, char **argv, char **cause) +{ + struct cmd_switch_client_data *data; + int opt; + + self->data = data = xmalloc(sizeof *data); + data->name = NULL; + data->target = NULL; + + while ((opt = getopt(argc, argv, "c:t:")) != -1) { + switch (opt) { + case 'c': + data->name = xstrdup(optarg); + break; + case 't': + data->target = xstrdup(optarg); + break; + default: + goto usage; + } + } + argc -= optind; + argv += optind; + if (argc != 0) + goto usage; + + return (0); + +usage: + xasprintf(cause, "usage: %s %s", self->entry->name, self->entry->usage); + + self->entry->free(self); + return (-1); +} + +int +cmd_switch_client_exec(struct cmd *self, struct cmd_ctx *ctx) +{ + struct cmd_switch_client_data *data = self->data; + struct client *c; + struct session *s; + + if (data == NULL) + return (0); + + if ((c = cmd_find_client(ctx, data->name)) == NULL) + return (-1); + if ((s = cmd_find_session(ctx, data->target)) == NULL) + return (-1); + + c->session = s; + + recalculate_sizes(); + server_redraw_client(c); + + return (0); +} + +void +cmd_switch_client_send(struct cmd *self, struct buffer *b) +{ + struct cmd_switch_client_data *data = self->data; + + buffer_write(b, data, sizeof *data); + cmd_send_string(b, data->name); + cmd_send_string(b, data->target); +} + +void +cmd_switch_client_recv(struct cmd *self, struct buffer *b) +{ + struct cmd_switch_client_data *data; + + self->data = data = xmalloc(sizeof *data); + buffer_read(b, data, sizeof *data); + data->name = cmd_recv_string(b); + data->target = cmd_recv_string(b); +} + +void +cmd_switch_client_free(struct cmd *self) +{ + struct cmd_switch_client_data *data = self->data; + + if (data->name != NULL) + xfree(data->name); + if (data->target != NULL) + xfree(data->target); + xfree(data); +} + +size_t +cmd_switch_client_print(struct cmd *self, char *buf, size_t len) +{ + struct cmd_switch_client_data *data = self->data; + size_t off = 0; + + off += xsnprintf(buf, len, "%s", self->entry->name); + if (data == NULL) + return (off); + if (off < len && data->name != NULL) + off += cmd_prarg(buf + off, len - off, " -c ", data->name); + if (off < len && data->target != NULL) + off += cmd_prarg(buf + off, len - off, " -t ", data->target); + return (off); +} diff --git a/usr.bin/tmux/cmd-unbind-key.c b/usr.bin/tmux/cmd-unbind-key.c new file mode 100644 index 00000000000..d69a6f59bab --- /dev/null +++ b/usr.bin/tmux/cmd-unbind-key.c @@ -0,0 +1,120 @@ +/* $OpenBSD: cmd-unbind-key.c,v 1.1 2009/06/01 22:58:49 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 "tmux.h" + +/* + * Unbind key from command. + */ + +int cmd_unbind_key_parse(struct cmd *, int, char **, char **); +int cmd_unbind_key_exec(struct cmd *, struct cmd_ctx *); +void cmd_unbind_key_send(struct cmd *, struct buffer *); +void cmd_unbind_key_recv(struct cmd *, struct buffer *); +void cmd_unbind_key_free(struct cmd *); + +struct cmd_unbind_key_data { + int key; +}; + +const struct cmd_entry cmd_unbind_key_entry = { + "unbind-key", "unbind", + "key", + 0, + NULL, + cmd_unbind_key_parse, + cmd_unbind_key_exec, + cmd_unbind_key_send, + cmd_unbind_key_recv, + cmd_unbind_key_free, + NULL +}; + +int +cmd_unbind_key_parse(struct cmd *self, int argc, char **argv, char **cause) +{ + struct cmd_unbind_key_data *data; + int opt; + + self->data = data = xmalloc(sizeof *data); + + while ((opt = getopt(argc, argv, "")) != -1) { + switch (opt) { + default: + goto usage; + } + } + argc -= optind; + argv += optind; + if (argc != 1) + goto usage; + + if ((data->key = key_string_lookup_string(argv[0])) == KEYC_NONE) { + xasprintf(cause, "unknown key: %s", argv[0]); + goto error; + } + + return (0); + +usage: + xasprintf(cause, "usage: %s %s", self->entry->name, self->entry->usage); + +error: + xfree(data); + return (-1); +} + +int +cmd_unbind_key_exec(struct cmd *self, unused struct cmd_ctx *ctx) +{ + struct cmd_unbind_key_data *data = self->data; + + if (data == NULL) + return (0); + + key_bindings_remove(data->key); + + return (0); +} + +void +cmd_unbind_key_send(struct cmd *self, struct buffer *b) +{ + struct cmd_unbind_key_data *data = self->data; + + buffer_write(b, data, sizeof *data); +} + +void +cmd_unbind_key_recv(struct cmd *self, struct buffer *b) +{ + struct cmd_unbind_key_data *data; + + self->data = data = xmalloc(sizeof *data); + buffer_read(b, data, sizeof *data); +} + +void +cmd_unbind_key_free(struct cmd *self) +{ + struct cmd_unbind_key_data *data = self->data; + + xfree(data); +} diff --git a/usr.bin/tmux/cmd-unlink-window.c b/usr.bin/tmux/cmd-unlink-window.c new file mode 100644 index 00000000000..5caebfa02e1 --- /dev/null +++ b/usr.bin/tmux/cmd-unlink-window.c @@ -0,0 +1,74 @@ +/* $OpenBSD: cmd-unlink-window.c,v 1.1 2009/06/01 22:58:49 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 "tmux.h" + +/* + * Unlink a window, unless it would be destroyed by doing so (only one link). + */ + +int cmd_unlink_window_exec(struct cmd *, struct cmd_ctx *); + +const struct cmd_entry cmd_unlink_window_entry = { + "unlink-window", "unlinkw", + CMD_TARGET_WINDOW_USAGE, + 0, + cmd_target_init, + cmd_target_parse, + cmd_unlink_window_exec, + cmd_target_send, + cmd_target_recv, + cmd_target_free, + cmd_target_print +}; + +int +cmd_unlink_window_exec(struct cmd *self, struct cmd_ctx *ctx) +{ + struct cmd_target_data *data = self->data; + struct winlink *wl; + struct session *s; + struct client *c; + u_int i; + int destroyed; + + if ((wl = cmd_find_window(ctx, data->target, &s)) == NULL) + return (-1); + + if (wl->window->references == 1) { + ctx->error(ctx, "window is only linked to one session"); + return (-1); + } + + destroyed = session_detach(s, wl); + for (i = 0; i < ARRAY_LENGTH(&clients); i++) { + c = ARRAY_ITEM(&clients, i); + if (c == NULL || c->session != s) + continue; + if (destroyed) { + c->session = NULL; + server_write_client(c, MSG_EXIT, NULL, 0); + } else + server_redraw_client(c); + } + recalculate_sizes(); + + return (0); +} diff --git a/usr.bin/tmux/cmd-up-pane.c b/usr.bin/tmux/cmd-up-pane.c new file mode 100644 index 00000000000..5d5dad5b978 --- /dev/null +++ b/usr.bin/tmux/cmd-up-pane.c @@ -0,0 +1,61 @@ +/* $OpenBSD: cmd-up-pane.c,v 1.1 2009/06/01 22:58:49 nicm 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 "tmux.h" + +/* + * Move up a pane. + */ + +int cmd_up_pane_exec(struct cmd *, struct cmd_ctx *); + +const struct cmd_entry cmd_up_pane_entry = { + "up-pane", "upp", + CMD_TARGET_WINDOW_USAGE, + 0, + cmd_target_init, + cmd_target_parse, + cmd_up_pane_exec, + cmd_target_send, + cmd_target_recv, + cmd_target_free, + cmd_target_print +}; + +int +cmd_up_pane_exec(struct cmd *self, struct cmd_ctx *ctx) +{ + struct cmd_target_data *data = self->data; + struct winlink *wl; + struct window *w; + + if ((wl = cmd_find_window(ctx, data->target, NULL)) == NULL) + return (-1); + w = wl->window; + + do { + w->active = TAILQ_PREV(w->active, window_panes, entry); + if (w->active == NULL) + w->active = TAILQ_LAST(&w->panes, window_panes); + layout_refresh(w, 1); + } while (w->active->flags & PANE_HIDDEN); + + return (0); +} diff --git a/usr.bin/tmux/cmd.c b/usr.bin/tmux/cmd.c new file mode 100644 index 00000000000..ebceb5486d5 --- /dev/null +++ b/usr.bin/tmux/cmd.c @@ -0,0 +1,393 @@ +/* $OpenBSD: cmd.c,v 1.1 2009/06/01 22:58:49 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/time.h> + +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include "tmux.h" + +const struct cmd_entry *cmd_table[] = { + &cmd_attach_session_entry, + &cmd_bind_key_entry, + &cmd_break_pane_entry, + &cmd_choose_session_entry, + &cmd_choose_window_entry, + &cmd_clear_history_entry, + &cmd_clock_mode_entry, + &cmd_command_prompt_entry, + &cmd_confirm_before_entry, + &cmd_copy_buffer_entry, + &cmd_copy_mode_entry, + &cmd_delete_buffer_entry, + &cmd_detach_client_entry, + &cmd_down_pane_entry, + &cmd_find_window_entry, + &cmd_has_session_entry, + &cmd_kill_pane_entry, + &cmd_kill_server_entry, + &cmd_kill_session_entry, + &cmd_kill_window_entry, + &cmd_last_window_entry, + &cmd_link_window_entry, + &cmd_list_buffers_entry, + &cmd_list_clients_entry, + &cmd_list_commands_entry, + &cmd_list_keys_entry, + &cmd_list_sessions_entry, + &cmd_list_windows_entry, + &cmd_load_buffer_entry, + &cmd_lock_server_entry, + &cmd_move_window_entry, + &cmd_new_session_entry, + &cmd_new_window_entry, + &cmd_next_layout_entry, + &cmd_next_window_entry, + &cmd_paste_buffer_entry, + &cmd_previous_layout_entry, + &cmd_previous_window_entry, + &cmd_refresh_client_entry, + &cmd_rename_session_entry, + &cmd_rename_window_entry, + &cmd_resize_pane_entry, + &cmd_respawn_window_entry, + &cmd_rotate_window_entry, + &cmd_save_buffer_entry, + &cmd_scroll_mode_entry, + &cmd_select_layout_entry, + &cmd_select_pane_entry, + &cmd_select_prompt_entry, + &cmd_select_window_entry, + &cmd_send_keys_entry, + &cmd_send_prefix_entry, + &cmd_server_info_entry, + &cmd_set_buffer_entry, + &cmd_set_option_entry, + &cmd_set_password_entry, + &cmd_set_window_option_entry, + &cmd_show_buffer_entry, + &cmd_show_options_entry, + &cmd_show_window_options_entry, + &cmd_source_file_entry, + &cmd_split_window_entry, + &cmd_start_server_entry, + &cmd_suspend_client_entry, + &cmd_swap_pane_entry, + &cmd_swap_window_entry, + &cmd_switch_client_entry, + &cmd_unbind_key_entry, + &cmd_unlink_window_entry, + &cmd_up_pane_entry, + NULL +}; + +struct cmd * +cmd_parse(int argc, char **argv, char **cause) +{ + const struct cmd_entry **entryp, *entry; + struct cmd *cmd; + char s[BUFSIZ]; + int opt; + + *cause = NULL; + if (argc == 0) + return (NULL); + + entry = NULL; + for (entryp = cmd_table; *entryp != NULL; entryp++) { + if ((*entryp)->alias != NULL && + strcmp((*entryp)->alias, argv[0]) == 0) { + entry = *entryp; + break; + } + + if (strncmp((*entryp)->name, argv[0], strlen(argv[0])) != 0) + continue; + if (entry != NULL) + goto ambiguous; + entry = *entryp; + + /* Bail now if an exact match. */ + if (strcmp(entry->name, argv[0]) == 0) + break; + } + if (entry == NULL) { + xasprintf(cause, "unknown command: %s", argv[0]); + return (NULL); + } + + optreset = 1; + optind = 1; + if (entry->parse == NULL) { + while ((opt = getopt(argc, argv, "")) != -1) { + switch (opt) { + default: + goto usage; + } + } + argc -= optind; + argv += optind; + if (argc != 0) + goto usage; + } + + cmd = xmalloc(sizeof *cmd); + cmd->entry = entry; + cmd->data = NULL; + if (entry->parse != NULL) { + if (entry->parse(cmd, argc, argv, cause) != 0) { + xfree(cmd); + return (NULL); + } + } + return (cmd); + +ambiguous: + *s = '\0'; + for (entryp = cmd_table; *entryp != NULL; entryp++) { + if (strncmp((*entryp)->name, argv[0], strlen(argv[0])) != 0) + continue; + if (strlcat(s, (*entryp)->name, sizeof s) >= sizeof s) + break; + if (strlcat(s, ", ", sizeof s) >= sizeof s) + break; + } + s[strlen(s) - 2] = '\0'; + xasprintf(cause, "ambiguous command: %s, could be: %s", argv[0], s); + return (NULL); + +usage: + xasprintf(cause, "usage: %s %s", entry->name, entry->usage); + return (NULL); +} + +int +cmd_exec(struct cmd *cmd, struct cmd_ctx *ctx) +{ + if (server_locked) { + ctx->error(ctx, "server is locked"); + return (-1); + } + return (cmd->entry->exec(cmd, ctx)); +} + +void +cmd_send(struct cmd *cmd, struct buffer *b) +{ + const struct cmd_entry **entryp; + u_int n; + + n = 0; + for (entryp = cmd_table; *entryp != NULL; entryp++) { + if (*entryp == cmd->entry) + break; + n++; + } + if (*entryp == NULL) + fatalx("command not found"); + + buffer_write(b, &n, sizeof n); + + if (cmd->entry->send != NULL) + cmd->entry->send(cmd, b); +} + +struct cmd * +cmd_recv(struct buffer *b) +{ + const struct cmd_entry **entryp; + struct cmd *cmd; + u_int m, n; + + buffer_read(b, &m, sizeof m); + + n = 0; + for (entryp = cmd_table; *entryp != NULL; entryp++) { + if (n == m) + break; + n++; + } + if (*entryp == NULL) + fatalx("command not found"); + + cmd = xmalloc(sizeof *cmd); + cmd->entry = *entryp; + + if (cmd->entry->recv != NULL) + cmd->entry->recv(cmd, b); + return (cmd); +} + +void +cmd_free(struct cmd *cmd) +{ + if (cmd->data != NULL && cmd->entry->free != NULL) + cmd->entry->free(cmd); + xfree(cmd); +} + +size_t +cmd_print(struct cmd *cmd, char *buf, size_t len) +{ + if (cmd->entry->print == NULL) { + return (xsnprintf(buf, len, "%s", cmd->entry->name)); + } + return (cmd->entry->print(cmd, buf, len)); +} + +void +cmd_send_string(struct buffer *b, const char *s) +{ + size_t n; + + if (s == NULL) { + n = 0; + buffer_write(b, &n, sizeof n); + return; + } + + n = strlen(s) + 1; + buffer_write(b, &n, sizeof n); + + buffer_write(b, s, n); +} + +char * +cmd_recv_string(struct buffer *b) +{ + char *s; + size_t n; + + buffer_read(b, &n, sizeof n); + + if (n == 0) + return (NULL); + + s = xmalloc(n); + buffer_read(b, s, n); + s[n - 1] = '\0'; + + return (s); +} + +struct session * +cmd_current_session(struct cmd_ctx *ctx) +{ + struct msg_command_data *data = ctx->msgdata; + struct timeval *tv; + struct session *s, *newest = NULL; + u_int i; + + if (ctx->cursession != NULL) + return (ctx->cursession); + + if (data != NULL && data->pid != -1) { + if (data->pid != getpid()) { + ctx->error(ctx, "wrong server: %ld", (long) data->pid); + return (NULL); + } + if (data->idx > ARRAY_LENGTH(&sessions)) { + ctx->error(ctx, "index out of range: %d", data->idx); + return (NULL); + } + if ((s = ARRAY_ITEM(&sessions, data->idx)) == NULL) { + ctx->error(ctx, "session doesn't exist: %u", data->idx); + return (NULL); + } + return (s); + } + + tv = NULL; + for (i = 0; i < ARRAY_LENGTH(&sessions); i++) { + s = ARRAY_ITEM(&sessions, i); + if (s != NULL && (tv == NULL || timercmp(&s->tv, tv, >))) { + newest = ARRAY_ITEM(&sessions, i); + tv = &s->tv; + } + } + return (newest); +} + +struct client * +cmd_find_client(struct cmd_ctx *ctx, const char *arg) +{ + struct client *c; + + if (arg == NULL) + c = ctx->curclient; + else { + if ((c = arg_parse_client(arg)) == NULL) { + if (arg != NULL) + ctx->error(ctx, "client not found: %s", arg); + else + ctx->error(ctx, "no client found"); + } + } + return (c); +} + +struct session * +cmd_find_session(struct cmd_ctx *ctx, const char *arg) +{ + struct session *s; + + if (arg == NULL) + s = cmd_current_session(ctx); + else { + if ((s = arg_parse_session(arg)) == NULL) { + if (arg != NULL) + ctx->error(ctx, "session not found: %s", arg); + else + ctx->error(ctx, "no session found"); + } + } + return (s); +} + +struct winlink * +cmd_find_window(struct cmd_ctx *ctx, const char *arg, struct session **sp) +{ + struct session *s; + struct winlink *wl; + int idx; + + wl = NULL; + if (arg_parse_window(arg, &s, &idx) != 0) { + ctx->error(ctx, "bad window: %s", arg); + return (NULL); + } + if (s == NULL) + s = ctx->cursession; + if (s == NULL) + s = cmd_current_session(ctx); + if (s == NULL) + return (NULL); + if (sp != NULL) + *sp = s; + + if (idx == -1) + wl = s->curw; + else + wl = winlink_find_by_index(&s->windows, idx); + if (wl == NULL) + ctx->error(ctx, "window not found: %s:%d", s->name, idx); + return (wl); +} diff --git a/usr.bin/tmux/colour.c b/usr.bin/tmux/colour.c new file mode 100644 index 00000000000..5b13db98760 --- /dev/null +++ b/usr.bin/tmux/colour.c @@ -0,0 +1,123 @@ +/* $OpenBSD: colour.c,v 1.1 2009/06/01 22:58:49 nicm Exp $ */ + +/* + * Copyright (c) 2008 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 <string.h> + +#include "tmux.h" + +const char * +colour_tostring(u_char c) +{ + switch (c) { + case 0: + return ("black"); + case 1: + return ("red"); + case 2: + return ("green"); + case 3: + return ("yellow"); + case 4: + return ("blue"); + case 5: + return ("magenta"); + case 6: + return ("cyan"); + case 7: + return ("white"); + case 8: + return ("default"); + } + return (NULL); +} + +int +colour_fromstring(const char *s) +{ + if (strcasecmp(s, "black") == 0 || (s[0] == '0' && s[1] == '\0')) + return (0); + if (strcasecmp(s, "red") == 0 || (s[0] == '1' && s[1] == '\0')) + return (1); + if (strcasecmp(s, "green") == 0 || (s[0] == '2' && s[1] == '\0')) + return (2); + if (strcasecmp(s, "yellow") == 0 || (s[0] == '3' && s[1] == '\0')) + return (3); + if (strcasecmp(s, "blue") == 0 || (s[0] == '4' && s[1] == '\0')) + return (4); + if (strcasecmp(s, "magenta") == 0 || (s[0] == '5' && s[1] == '\0')) + return (5); + if (strcasecmp(s, "cyan") == 0 || (s[0] == '6' && s[1] == '\0')) + return (6); + if (strcasecmp(s, "white") == 0 || (s[0] == '7' && s[1] == '\0')) + return (7); + if (strcasecmp(s, "default") == 0 || (s[0] == '8' && s[1] == '\0')) + return (8); + return (-1); +} + +u_char +colour_256to16(u_char c) +{ + static const u_char table[256] = { + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, + 0, 4, 4, 4, 12, 12, 2, 6, 4, 4, 12, 12, 2, 2, 6, 4, + 12, 12, 2, 2, 2, 6, 12, 12, 10, 10, 10, 10, 14, 12, 10, 10, + 10, 10, 10, 14, 1, 5, 4, 4, 12, 12, 3, 8, 4, 4, 12, 12, + 2, 2, 6, 4, 12, 12, 2, 2, 2, 6, 12, 12, 10, 10, 10, 10, + 14, 12, 10, 10, 10, 10, 10, 14, 1, 1, 5, 4, 12, 12, 1, 1, + 5, 4, 12, 12, 3, 3, 8, 4, 12, 12, 2, 2, 2, 6, 12, 12, + 10, 10, 10, 10, 14, 12, 10, 10, 10, 10, 10, 14, 1, 1, 1, 5, + 12, 12, 1, 1, 1, 5, 12, 12, 1, 1, 1, 5, 12, 12, 3, 3, + 3, 7, 12, 12, 10, 10, 10, 10, 14, 12, 10, 10, 10, 10, 10, 14, + 9, 9, 9, 9, 13, 12, 9, 9, 9, 9, 13, 12, 9, 9, 9, 9, + 13, 12, 9, 9, 9, 9, 13, 12, 11, 11, 11, 11, 7, 12, 10, 10, + 10, 10, 10, 14, 9, 9, 9, 9, 9, 13, 9, 9, 9, 9, 9, 13, + 9, 9, 9, 9, 9, 13, 9, 9, 9, 9, 9, 13, 9, 9, 9, 9, + 9, 13, 11, 11, 11, 11, 11, 15, 0, 0, 0, 0, 0, 0, 8, 8, + 8, 8, 8, 8, 7, 7, 7, 7, 7, 7, 15, 15, 15, 15, 15, 15 + }; + + return (table[c]); +} + +u_char +colour_256to88(u_char c) +{ + static const u_char table[256] = { + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, + 16, 17, 17, 18, 18, 19, 20, 21, 21, 22, 22, 23, 20, 21, 21, 22, + 22, 23, 24, 25, 25, 26, 26, 27, 24, 25, 25, 26, 26, 27, 28, 29, + 29, 30, 30, 31, 32, 33, 33, 34, 34, 35, 36, 37, 37, 38, 38, 39, + 36, 37, 37, 38, 38, 39, 40, 41, 41, 42, 42, 43, 40, 41, 41, 42, + 42, 43, 44, 45, 45, 46, 46, 47, 32, 33, 33, 34, 34, 35, 36, 37, + 37, 38, 38, 39, 36, 37, 37, 38, 38, 39, 40, 41, 41, 42, 42, 43, + 40, 41, 41, 42, 42, 43, 44, 45, 45, 46, 46, 47, 48, 49, 49, 50, + 50, 51, 52, 53, 53, 54, 54, 55, 52, 53, 53, 54, 54, 55, 56, 57, + 57, 58, 58, 59, 56, 57, 57, 58, 58, 59, 60, 61, 61, 62, 62, 63, + 48, 49, 49, 50, 50, 51, 52, 53, 53, 54, 54, 55, 52, 53, 53, 54, + 54, 55, 56, 57, 57, 58, 58, 59, 56, 57, 57, 58, 58, 59, 60, 61, + 61, 62, 62, 63, 64, 65, 65, 66, 66, 67, 68, 69, 69, 70, 70, 71, + 68, 69, 69, 70, 70, 71, 72, 73, 73, 74, 74, 75, 72, 73, 73, 74, + 74, 75, 76, 77, 77, 78, 78, 79, 0, 0, 80, 80, 80, 81, 81, 81, + 82, 82, 82, 83, 83, 83, 84, 84, 84, 85, 85, 85, 86, 86, 86, 87 + }; + + return (table[c]); +} diff --git a/usr.bin/tmux/grid-view.c b/usr.bin/tmux/grid-view.c new file mode 100644 index 00000000000..7e4920ac7f8 --- /dev/null +++ b/usr.bin/tmux/grid-view.c @@ -0,0 +1,211 @@ +/* $OpenBSD: grid-view.c,v 1.1 2009/06/01 22:58:49 nicm Exp $ */ + +/* + * Copyright (c) 2008 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 <string.h> + +#include "tmux.h" + +/* + * Grid view functions. These work using coordinates relative to the visible + * screen area. + */ + +#define grid_view_x(gd, x) (x) +#define grid_view_y(gd, y) ((gd)->hsize + (y)) + +/* Get cell for reading. */ +const struct grid_cell * +grid_view_peek_cell(struct grid *gd, u_int px, u_int py) +{ + return (grid_peek_cell(gd, grid_view_x(gd, px), grid_view_y(gd, py))); +} + +/* Get cell for writing. */ +struct grid_cell * +grid_view_get_cell(struct grid *gd, u_int px, u_int py) +{ + return (grid_get_cell(gd, grid_view_x(gd, px), grid_view_y(gd, py))); +} + +/* Set cell. */ +void +grid_view_set_cell( + struct grid *gd, u_int px, u_int py, const struct grid_cell *gc) +{ + grid_set_cell(gd, grid_view_x(gd, px), grid_view_y(gd, py), gc); +} + +/* Get UTF-8 for reading. */ +const struct grid_utf8 * +grid_view_peek_utf8(struct grid *gd, u_int px, u_int py) +{ + return (grid_peek_utf8(gd, grid_view_x(gd, px), grid_view_y(gd, py))); +} + +/* Get UTF-8 for writing. */ +struct grid_utf8 * +grid_view_get_utf8(struct grid *gd, u_int px, u_int py) +{ + return (grid_get_utf8(gd, grid_view_x(gd, px), grid_view_y(gd, py))); +} + +/* Set UTF-8. */ +void +grid_view_set_utf8( + struct grid *gd, u_int px, u_int py, const struct grid_utf8 *gu) +{ + grid_set_utf8(gd, grid_view_x(gd, px), grid_view_y(gd, py), gu); +} + +/* Clear area. */ +void +grid_view_clear(struct grid *gd, u_int px, u_int py, u_int nx, u_int ny) +{ + GRID_DEBUG(gd, "px=%u, py=%u, nx=%u, ny=%u", px, py, nx, ny); + + px = grid_view_x(gd, px); + py = grid_view_y(gd, py); + + grid_clear(gd, px, py, nx, ny); +} + +/* Scroll region up. */ +void +grid_view_scroll_region_up(struct grid *gd, u_int rupper, u_int rlower) +{ + GRID_DEBUG(gd, "rupper=%u, rlower=%u", rupper, rlower); + + if (rupper == 0 && rlower == gd->sy - 1) { + grid_scroll_line(gd); + return; + } + + rupper = grid_view_y(gd, rupper); + rlower = grid_view_y(gd, rlower); + + grid_move_lines(gd, rupper, rupper + 1, rlower - rupper); +} + +/* Scroll region down. */ +void +grid_view_scroll_region_down(struct grid *gd, u_int rupper, u_int rlower) +{ + GRID_DEBUG(gd, "rupper=%u, rlower=%u", rupper, rlower); + + rupper = grid_view_y(gd, rupper); + rlower = grid_view_y(gd, rlower); + + grid_move_lines(gd, rupper + 1, rupper, rlower - rupper); +} + +/* Insert lines. */ +void +grid_view_insert_lines(struct grid *gd, u_int py, u_int ny) +{ + u_int sy; + + GRID_DEBUG(gd, "py=%u, ny=%u", py, ny); + + py = grid_view_y(gd, py); + + sy = grid_view_y(gd, gd->sy); + + grid_move_lines(gd, py + ny, py, sy - py - ny); +} + +/* Insert lines in region. */ +void +grid_view_insert_lines_region( + struct grid *gd, unused u_int rupper, u_int rlower, u_int py, u_int ny) +{ + GRID_DEBUG( + gd, "rupper=%u, rlower=%u, py=%u, ny=%u", rupper, rlower, py, ny); + + rlower = grid_view_y(gd, rlower); + + py = grid_view_y(gd, py); + + grid_move_lines(gd, py + ny, py, (rlower + 1) - py - ny); +} + +/* Delete lines. */ +void +grid_view_delete_lines(struct grid *gd, u_int py, u_int ny) +{ + u_int sy; + + GRID_DEBUG(gd, "py=%u, ny=%u", py, ny); + + py = grid_view_y(gd, py); + + sy = grid_view_y(gd, gd->sy); + + grid_move_lines(gd, py, py + ny, sy - py - ny); +} + +/* Delete lines inside scroll region. */ +void +grid_view_delete_lines_region( + struct grid *gd, unused u_int rupper, u_int rlower, u_int py, u_int ny) +{ + GRID_DEBUG( + gd, "rupper=%u, rlower=%u, py=%u, ny=%u", rupper, rlower, py, ny); + + rlower = grid_view_y(gd, rlower); + + py = grid_view_y(gd, py); + + grid_move_lines(gd, py, py + ny, (rlower + 1) - py - ny); +} + +/* Insert characters. */ +void +grid_view_insert_cells(struct grid *gd, u_int px, u_int py, u_int nx) +{ + u_int sx; + + GRID_DEBUG(gd, "px=%u, py=%u, nx=%u", px, py, nx); + + px = grid_view_x(gd, px); + py = grid_view_y(gd, py); + + sx = grid_view_x(gd, gd->sx); + + if (px == sx - 1) + grid_clear(gd, px, py, 1, 1); + else + grid_move_cells(gd, px + nx, px, py, (sx - 1) - (px + nx)); +} + +/* Delete characters. */ +void +grid_view_delete_cells(struct grid *gd, u_int px, u_int py, u_int nx) +{ + u_int sx; + + GRID_DEBUG(gd, "px=%u, py=%u, nx=%u", px, py, nx); + + px = grid_view_x(gd, px); + py = grid_view_y(gd, py); + + sx = grid_view_x(gd, gd->sx); + + grid_move_cells(gd, px, px + nx, py, (sx - 1) - (px + nx)); +} diff --git a/usr.bin/tmux/grid.c b/usr.bin/tmux/grid.c new file mode 100644 index 00000000000..0e90df3441f --- /dev/null +++ b/usr.bin/tmux/grid.c @@ -0,0 +1,497 @@ +/* $OpenBSD: grid.c,v 1.1 2009/06/01 22:58:49 nicm Exp $ */ + +/* + * Copyright (c) 2008 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 <string.h> + +#include "tmux.h" + +/* + * Grid data. This is the basic data structure that represents what is shown on + * screen. + * + * A grid is a grid of cells (struct grid_cell). Lines are not allocated until + * cells in that line are written to. The grid is split into history and + * viewable data with the history starting at row (line) 0 and extending to + * (hsize - 1); from hsize to hsize + (sy - 1) is the viewable data. All + * functions in this file work on absolute coordinates, grid-view.c has + * functions which work on the screen data. + */ + +/* Default grid cell data. */ +const struct grid_cell grid_default_cell = { 0, 0, 8, 8, ' ' }; + +#define grid_put_cell(gd, px, py, gc) do { \ + memcpy(&gd->data[py][px], gc, sizeof gd->data[py][px]); \ +} while (0) +#define grid_put_utf8(gd, px, py, gc) do { \ + memcpy(&gd->udata[py][px], gc, sizeof gd->udata[py][px]); \ +} while (0) + +int grid_check_x(struct grid *, u_int); +int grid_check_y(struct grid *, u_int); + +#ifdef DEBUG +int +grid_check_x(struct grid *gd, u_int px) +{ + if ((px) >= (gd)->sx) + log_fatalx("x out of range: %u", px); + return (0); +} + +int +grid_check_y(struct grid *gd, u_int py) +{ + if ((py) >= (gd)->hsize + (gd)->sy) + log_fatalx("y out of range: %u", py); + return (0); +} +#else +int +grid_check_x(struct grid *gd, u_int px) +{ + if ((px) >= (gd)->sx) { + log_debug("x out of range: %u", px); + return (-1); + } + return (0); +} + +int +grid_check_y(struct grid *gd, u_int py) +{ + if ((py) >= (gd)->hsize + (gd)->sy) { + log_debug("y out of range: %u", py); + return (-1); + } + return (0); +} +#endif + +/* Create a new grid. */ +struct grid * +grid_create(u_int sx, u_int sy, u_int hlimit) +{ + struct grid *gd; + + gd = xmalloc(sizeof *gd); + gd->sx = sx; + gd->sy = sy; + + gd->hsize = 0; + gd->hlimit = hlimit; + + gd->size = xcalloc(gd->sy, sizeof *gd->size); + gd->data = xcalloc(gd->sy, sizeof *gd->data); + + gd->usize = xcalloc(gd->sy, sizeof *gd->usize); + gd->udata = xcalloc(gd->sy, sizeof *gd->udata); + + return (gd); +} + +/* Destroy grid. */ +void +grid_destroy(struct grid *gd) +{ + u_int yy; + + for (yy = 0; yy < gd->hsize + gd->sy; yy++) { + if (gd->udata[yy] != NULL) + xfree(gd->udata[yy]); + if (gd->data[yy] != NULL) + xfree(gd->data[yy]); + } + + if (gd->udata != NULL) + xfree(gd->udata); + if (gd->usize != NULL) + xfree(gd->usize); + + if (gd->data != NULL) + xfree(gd->data); + if (gd->size != NULL) + xfree(gd->size); + + xfree(gd); +} + +/* Compare grids. */ +int +grid_compare(struct grid *ga, struct grid *gb) +{ + struct grid_cell *gca, *gcb; + struct grid_utf8 *gua, *gub; + u_int xx, yy; + + if (ga->sx != gb->sx || ga->sy != ga->sy) + return (1); + + for (yy = 0; yy < ga->sy; yy++) { + if (ga->size[yy] != gb->size[yy]) + return (1); + for (xx = 0; xx < ga->sx; xx++) { + gca = &ga->data[yy][xx]; + gcb = &gb->data[yy][xx]; + if (memcmp(gca, gcb, sizeof (struct grid_cell)) != 0) + return (1); + if (!(gca->flags & GRID_FLAG_UTF8)) + continue; + gua = &ga->udata[yy][xx]; + gub = &gb->udata[yy][xx]; + if (memcmp(gua, gub, sizeof (struct grid_utf8)) != 0) + return (1); + } + } + + return (0); +} + +/* Scroll a line into the history. */ +void +grid_scroll_line(struct grid *gd) +{ + u_int yy; + + GRID_DEBUG(gd, ""); + + if (gd->hsize >= gd->hlimit - 1) { + /* If the limit is hit, free the bottom 10% and shift up. */ + yy = gd->hlimit / 10; + if (yy < 1) + yy = 1; + + grid_move_lines(gd, 0, yy, gd->hsize + gd->sy - yy); + gd->hsize -= yy; + } + + yy = gd->hsize + gd->sy; + + gd->size = xrealloc(gd->size, yy + 1, sizeof *gd->size); + gd->size[yy] = 0; + gd->data = xrealloc(gd->data, yy + 1, sizeof *gd->data); + gd->data[yy] = NULL; + + gd->usize = xrealloc(gd->usize, yy + 1, sizeof *gd->usize); + gd->usize[yy] = 0; + gd->udata = xrealloc(gd->udata, yy + 1, sizeof *gd->udata); + gd->udata[yy] = NULL; + + gd->hsize++; +} + +/* Reduce line to fit to cell. */ +void +grid_reduce_line(struct grid *gd, u_int py, u_int sx) +{ + if (sx < gd->size[py]) { + gd->data[py] = xrealloc(gd->data[py], sx, sizeof **gd->data); + gd->size[py] = sx; + } + if (sx < gd->usize[py]) { + gd->udata[py] = xrealloc(gd->udata[py], sx, sizeof **gd->udata); + gd->usize[py] = sx; + } +} + +/* Expand line to fit to cell. */ +void +grid_expand_line(struct grid *gd, u_int py, u_int sx) +{ + u_int xx; + + if (sx <= gd->size[py]) + return; + + gd->data[py] = xrealloc(gd->data[py], sx, sizeof **gd->data); + for (xx = gd->size[py]; xx < sx; xx++) + grid_put_cell(gd, xx, py, &grid_default_cell); + gd->size[py] = sx; +} + +/* Expand line to fit to cell for UTF-8. */ +void +grid_expand_line_utf8(struct grid *gd, u_int py, u_int sx) +{ + if (sx <= gd->usize[py]) + return; + + gd->udata[py] = xrealloc(gd->udata[py], sx, sizeof **gd->udata); + gd->usize[py] = sx; +} + +/* Get cell for reading. */ +const struct grid_cell * +grid_peek_cell(struct grid *gd, u_int px, u_int py) +{ + if (grid_check_x(gd, px) != 0) + return (&grid_default_cell); + if (grid_check_y(gd, py) != 0) + return (&grid_default_cell); + + if (px >= gd->size[py]) + return (&grid_default_cell); + return (&gd->data[py][px]); +} + +/* Get cell at relative position (for writing). */ +struct grid_cell * +grid_get_cell(struct grid *gd, u_int px, u_int py) +{ + if (grid_check_x(gd, px) != 0) + return (NULL); + if (grid_check_y(gd, py) != 0) + return (NULL); + + grid_expand_line(gd, py, px + 1); + return (&gd->data[py][px]); +} + +/* Set cell at relative position. */ +void +grid_set_cell( + struct grid *gd, u_int px, u_int py, const struct grid_cell *gc) +{ + if (grid_check_x(gd, px) != 0) + return; + if (grid_check_y(gd, py) != 0) + return; + + grid_expand_line(gd, py, px + 1); + grid_put_cell(gd, px, py, gc); +} + +/* Get UTF-8 for reading. */ +const struct grid_utf8 * +grid_peek_utf8(struct grid *gd, u_int px, u_int py) +{ + if (grid_check_x(gd, px) != 0) + return (NULL); + if (grid_check_y(gd, py) != 0) + return (NULL); + + if (px >= gd->usize[py]) + return (NULL); + return (&gd->udata[py][px]); +} + +/* Get utf8 at relative position (for writing). */ +struct grid_utf8 * +grid_get_utf8(struct grid *gd, u_int px, u_int py) +{ + if (grid_check_x(gd, px) != 0) + return (NULL); + if (grid_check_y(gd, py) != 0) + return (NULL); + + grid_expand_line_utf8(gd, py, px + 1); + return (&gd->udata[py][px]); +} + +/* Set utf8 at relative position. */ +void +grid_set_utf8( + struct grid *gd, u_int px, u_int py, const struct grid_utf8 *gc) +{ + if (grid_check_x(gd, px) != 0) + return; + if (grid_check_y(gd, py) != 0) + return; + + grid_expand_line_utf8(gd, py, px + 1); + grid_put_utf8(gd, px, py, gc); +} + +/* + * Clear area. Note this is different from a fill as it just omits unallocated + * cells. + */ +void +grid_clear(struct grid *gd, u_int px, u_int py, u_int nx, u_int ny) +{ + u_int xx, yy; + + GRID_DEBUG(gd, "px=%u, py=%u, nx=%u, ny=%u", px, py, nx, ny); + + if (nx == 0 || ny == 0) + return; + + if (px == 0 && nx == gd->sx) { + grid_clear_lines(gd, py, ny); + return; + } + + if (grid_check_x(gd, px) != 0) + return; + if (grid_check_x(gd, px + nx - 1) != 0) + return; + if (grid_check_y(gd, py) != 0) + return; + if (grid_check_y(gd, py + ny - 1) != 0) + return; + + for (yy = py; yy < py + ny; yy++) { + for (xx = px; xx < px + nx; xx++) { + if (xx >= gd->size[yy]) + break; + grid_put_cell(gd, xx, yy, &grid_default_cell); + } + } +} + +/* Clear lines. This just frees and truncates the lines. */ +void +grid_clear_lines(struct grid *gd, u_int py, u_int ny) +{ + u_int yy; + + GRID_DEBUG(gd, "py=%u, ny=%u", py, ny); + + if (ny == 0) + return; + + if (grid_check_y(gd, py) != 0) + return; + if (grid_check_y(gd, py + ny - 1) != 0) + return; + + for (yy = py; yy < py + ny; yy++) { + if (gd->data[yy] != NULL) { + xfree(gd->data[yy]); + gd->data[yy] = NULL; + gd->size[yy] = 0; + } + if (gd->udata[yy] != NULL) { + xfree(gd->udata[yy]); + gd->udata[yy] = NULL; + gd->usize[yy] = 0; + } + } +} + +/* Move a group of lines. */ +void +grid_move_lines(struct grid *gd, u_int dy, u_int py, u_int ny) +{ + u_int yy; + + GRID_DEBUG(gd, "dy=%u, py=%u, ny=%u", dy, py, ny); + + if (ny == 0 || py == dy) + return; + + if (grid_check_y(gd, py) != 0) + return; + if (grid_check_y(gd, py + ny - 1) != 0) + return; + if (grid_check_y(gd, dy) != 0) + return; + if (grid_check_y(gd, dy + ny - 1) != 0) + return; + + /* Free any lines which are being replaced. */ + for (yy = dy; yy < dy + ny; yy++) { + if (yy >= py && yy < py + ny) + continue; + grid_clear_lines(gd, yy, 1); + } + + memmove(&gd->data[dy], &gd->data[py], ny * (sizeof *gd->data)); + memmove(&gd->size[dy], &gd->size[py], ny * (sizeof *gd->size)); + + memmove(&gd->udata[dy], &gd->udata[py], ny * (sizeof *gd->udata)); + memmove(&gd->usize[dy], &gd->usize[py], ny * (sizeof *gd->usize)); + + /* Wipe any lines that have been moved (without freeing them). */ + for (yy = py; yy < py + ny; yy++) { + if (yy >= dy && yy < dy + ny) + continue; + gd->data[yy] = NULL; + gd->size[yy] = 0; + gd->udata[yy] = NULL; + gd->usize[yy] = 0; + } +} + +/* Clear a group of cells. */ +void +grid_clear_cells(struct grid *gd, u_int px, u_int py, u_int nx) +{ + u_int xx; + + GRID_DEBUG(gd, "px=%u, py=%u, nx=%u", px, py, nx); + + if (nx == 0) + return; + + if (grid_check_x(gd, px) != 0) + return; + if (grid_check_x(gd, px + nx - 1) != 0) + return; + if (grid_check_y(gd, py) != 0) + return; + + for (xx = px; xx < px + nx; xx++) { + if (xx >= gd->size[py]) + break; + grid_put_cell(gd, xx, py, &grid_default_cell); + } +} + +/* Move a group of cells. */ +void +grid_move_cells(struct grid *gd, u_int dx, u_int px, u_int py, u_int nx) +{ + u_int xx; + + GRID_DEBUG(gd, "dx=%u, px=%u, py=%u, nx=%u", dx, px, py, nx); + + if (nx == 0 || px == dx) + return; + + if (grid_check_x(gd, px) != 0) + return; + if (grid_check_x(gd, px + nx - 1) != 0) + return; + if (grid_check_x(gd, dx + nx - 1) != 0) + return; + if (grid_check_y(gd, py) != 0) + return; + + grid_expand_line(gd, py, px + nx); + grid_expand_line(gd, py, dx + nx); + memmove(&gd->data[py][dx], &gd->data[py][px], nx * (sizeof **gd->data)); + + if (gd->udata[py] != NULL) { + grid_expand_line_utf8(gd, py, px + nx); + grid_expand_line_utf8(gd, py, dx + nx); + memmove(&gd->udata[py][dx], + &gd->udata[py][px], nx * (sizeof **gd->udata)); + } + + /* Wipe any cells that have been moved. */ + for (xx = px; xx < px + nx; xx++) { + if (xx >= dx && xx < dx + nx) + continue; + grid_put_cell(gd, xx, py, &grid_default_cell); + } +} + + diff --git a/usr.bin/tmux/input-keys.c b/usr.bin/tmux/input-keys.c new file mode 100644 index 00000000000..dc49f709d73 --- /dev/null +++ b/usr.bin/tmux/input-keys.c @@ -0,0 +1,227 @@ +/* $OpenBSD: input-keys.c,v 1.1 2009/06/01 22:58:49 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 <stdint.h> +#include <stdlib.h> +#include <string.h> + +#include "tmux.h" + +struct input_key_ent { + int key; + const char *data; + + int flags; +#define INPUTKEY_KEYPAD 0x1 /* keypad key */ +#define INPUTKEY_CURSOR 0x2 /* cursor key */ +#define INPUTKEY_CTRL 0x4 /* may be modified with ctrl */ +#define INPUTKEY_XTERM 0x4 /* may have xterm argument appended */ +}; + +struct input_key_ent input_keys[] = { + /* Function keys. */ + { KEYC_F1, "\033OP", INPUTKEY_CTRL|INPUTKEY_XTERM }, + { KEYC_F2, "\033OQ", INPUTKEY_CTRL|INPUTKEY_XTERM }, + { KEYC_F3, "\033OR", INPUTKEY_CTRL|INPUTKEY_XTERM }, + { KEYC_F4, "\033OS", INPUTKEY_CTRL|INPUTKEY_XTERM }, + { KEYC_F5, "\033[15~", INPUTKEY_CTRL|INPUTKEY_XTERM }, + { KEYC_F6, "\033[17~", INPUTKEY_CTRL|INPUTKEY_XTERM }, + { KEYC_F7, "\033[18~", INPUTKEY_CTRL|INPUTKEY_XTERM }, + { KEYC_F8, "\033[19~", INPUTKEY_CTRL|INPUTKEY_XTERM }, + { KEYC_F9, "\033[20~", INPUTKEY_CTRL|INPUTKEY_XTERM }, + { KEYC_F10, "\033[21~", INPUTKEY_CTRL|INPUTKEY_XTERM }, + { KEYC_F11, "\033[23~", INPUTKEY_CTRL|INPUTKEY_XTERM }, + { KEYC_F12, "\033[24~", INPUTKEY_CTRL|INPUTKEY_XTERM }, + { KEYC_F13, "\033[25~", INPUTKEY_CTRL|INPUTKEY_XTERM }, + { KEYC_F14, "\033[26~", INPUTKEY_CTRL|INPUTKEY_XTERM }, + { KEYC_F15, "\033[28~", INPUTKEY_CTRL|INPUTKEY_XTERM }, + { KEYC_F16, "\033[29~", INPUTKEY_CTRL|INPUTKEY_XTERM }, + { KEYC_F17, "\033[31~", INPUTKEY_CTRL|INPUTKEY_XTERM }, + { KEYC_F18, "\033[32~", INPUTKEY_CTRL|INPUTKEY_XTERM }, + { KEYC_F19, "\033[33~", INPUTKEY_CTRL|INPUTKEY_XTERM }, + { KEYC_F20, "\033[34~", INPUTKEY_CTRL|INPUTKEY_XTERM }, + { KEYC_IC, "\033[2~", INPUTKEY_CTRL|INPUTKEY_XTERM }, + { KEYC_DC, "\033[3~", INPUTKEY_CTRL|INPUTKEY_XTERM }, + { KEYC_HOME, "\033[1~", INPUTKEY_CTRL|INPUTKEY_XTERM }, + { KEYC_END, "\033[4~", INPUTKEY_CTRL|INPUTKEY_XTERM }, + { KEYC_NPAGE, "\033[6~", INPUTKEY_CTRL|INPUTKEY_XTERM }, + { KEYC_PPAGE, "\033[5~", INPUTKEY_CTRL|INPUTKEY_XTERM }, + { KEYC_BTAB, "\033[Z", INPUTKEY_CTRL }, + + /* Arrow keys. Cursor versions must come first. */ + { KEYC_ADDCTL(KEYC_UP), "\033Oa", 0 }, + { KEYC_ADDCTL(KEYC_DOWN), "\033Ob", 0 }, + { KEYC_ADDCTL(KEYC_RIGHT), "\033Oc", 0 }, + { KEYC_ADDCTL(KEYC_LEFT), "\033Od", 0 }, + + { KEYC_ADDSFT(KEYC_UP), "\033[a", 0 }, + { KEYC_ADDSFT(KEYC_DOWN), "\033[b", 0 }, + { KEYC_ADDSFT(KEYC_RIGHT), "\033[c", 0 }, + { KEYC_ADDSFT(KEYC_LEFT), "\033[d", 0 }, + + { KEYC_UP, "\033OA", INPUTKEY_CURSOR }, + { KEYC_DOWN, "\033OB", INPUTKEY_CURSOR }, + { KEYC_RIGHT, "\033OC", INPUTKEY_CURSOR }, + { KEYC_LEFT, "\033OD", INPUTKEY_CURSOR }, + + { KEYC_UP, "\033[A", 0 }, + { KEYC_DOWN, "\033[B", 0 }, + { KEYC_RIGHT, "\033[C", 0 }, + { KEYC_LEFT, "\033[D", 0 }, + + /* Keypad keys. Keypad versions must come first. */ + { KEYC_KP0_1, "/", INPUTKEY_KEYPAD }, + { KEYC_KP0_2, "*", INPUTKEY_KEYPAD }, + { KEYC_KP0_3, "-", INPUTKEY_KEYPAD }, + { KEYC_KP1_0, "7", INPUTKEY_KEYPAD }, + { KEYC_KP1_1, "8", INPUTKEY_KEYPAD }, + { KEYC_KP1_2, "9", INPUTKEY_KEYPAD }, + { KEYC_KP1_3, "+", INPUTKEY_KEYPAD }, + { KEYC_KP2_0, "4", INPUTKEY_KEYPAD }, + { KEYC_KP2_1, "5", INPUTKEY_KEYPAD }, + { KEYC_KP2_2, "6", INPUTKEY_KEYPAD }, + { KEYC_KP3_0, "1", INPUTKEY_KEYPAD }, + { KEYC_KP3_1, "2", INPUTKEY_KEYPAD }, + { KEYC_KP3_2, "3", INPUTKEY_KEYPAD }, + { KEYC_KP3_3, "\n", INPUTKEY_KEYPAD }, /* this can be CRLF too? */ + { KEYC_KP4_0, "0", INPUTKEY_KEYPAD }, + { KEYC_KP4_2, ".", INPUTKEY_KEYPAD }, + { KEYC_KP0_1, "\033Oo", 0 }, + { KEYC_KP0_2, "\033Oj", 0 }, + { KEYC_KP0_3, "\033Om", 0 }, + { KEYC_KP1_0, "\033Ow", 0 }, + { KEYC_KP1_1, "\033Ox", 0 }, + { KEYC_KP1_2, "\033Oy", 0 }, + { KEYC_KP1_3, "\033Ok", 0 }, + { KEYC_KP2_0, "\033Ot", 0 }, + { KEYC_KP2_1, "\033Ou", 0 }, + { KEYC_KP2_2, "\033Ov", 0 }, + { KEYC_KP3_0, "\033Oq", 0 }, + { KEYC_KP3_1, "\033Or", 0 }, + { KEYC_KP3_2, "\033Os", 0 }, + { KEYC_KP3_3, "\033OM", 0 }, + { KEYC_KP4_0, "\033Op", 0 }, + { KEYC_KP4_2, "\033On", 0 }, +}; + +/* Translate a key code from client into an output key sequence. */ +void +input_key(struct window_pane *wp, int key) +{ + struct input_key_ent *ike; + u_int i; + char ch; + size_t dlen; + int xterm_keys; + + log_debug2("writing key 0x%x", key); + + if (key != KEYC_NONE && KEYC_REMOVEESC(key) < KEYC_OFFSET) { + if (KEYC_ISESC(key)) + buffer_write8(wp->out, '\033'); + buffer_write8(wp->out, (uint8_t) KEYC_REMOVEESC(key)); + return; + } + + for (i = 0; i < nitems(input_keys); i++) { + ike = &input_keys[i]; + + if ((ike->flags & INPUTKEY_KEYPAD) && + !(wp->screen->mode & MODE_KKEYPAD)) + continue; + if ((ike->flags & INPUTKEY_CURSOR) && + !(wp->screen->mode & MODE_KCURSOR)) + continue; + + if (KEYC_ISESC(key) && KEYC_ADDESC(ike->key) == key) + break; + if (KEYC_ISSFT(key) && KEYC_ADDSFT(ike->key) == key) + break; + if (KEYC_ISCTL(key) && KEYC_ADDCTL(ike->key) == key) { + if (ike->flags & INPUTKEY_CTRL) + break; + } + if (ike->key == key) + break; + } + if (i == nitems(input_keys)) { + log_debug2("key 0x%x missing", key); + return; + } + dlen = strlen(ike->data); + + log_debug2("found key 0x%x: \"%s\"", key, ike->data); + + /* + * If in xterm keys mode, work out and append the modifier as an + * argument. + */ + xterm_keys = options_get_number(&wp->window->options, "xterm-keys"); + if (xterm_keys && ike->flags & INPUTKEY_XTERM) { + ch = '\0'; + if (KEYC_ISSFT(key) && KEYC_ISESC(key) && KEYC_ISCTL(key)) + ch = '8'; + else if (KEYC_ISESC(key) && KEYC_ISCTL(key)) + ch = '7'; + else if (KEYC_ISSFT(key) && KEYC_ISCTL(key)) + ch = '6'; + else if (KEYC_ISCTL(key)) + ch = '5'; + else if (KEYC_ISSFT(key) && KEYC_ISESC(key)) + ch = '4'; + else if (KEYC_ISESC(key)) + ch = '3'; + else if (KEYC_ISSFT(key)) + ch = '2'; + if (ch != '\0') { + buffer_write(wp->out, ike->data, dlen - 1); + buffer_write8(wp->out, ';'); + buffer_write8(wp->out, ch); + buffer_write8(wp->out, ike->data[dlen - 1]); + } else + buffer_write(wp->out, ike->data, dlen); + return; + } + + /* + * Not in xterm mode. Prefix a \033 for escape, and set bit 5 of the + * last byte for ctrl. + */ + if (KEYC_ISESC(key)) + buffer_write8(wp->out, '\033'); + if (KEYC_ISCTL(key) && ike->flags & INPUTKEY_CTRL) { + buffer_write(wp->out, ike->data, dlen - 1); + buffer_write8(wp->out, ike->data[dlen - 1] ^ 0x20); + return; + } + buffer_write(wp->out, ike->data, dlen); +} + +/* Handle input mouse. */ +void +input_mouse(struct window_pane *wp, u_char b, u_char x, u_char y) +{ + if (wp->screen->mode & MODE_MOUSE) { + buffer_write(wp->out, "\033[M", 3); + buffer_write8(wp->out, b + 32); + buffer_write8(wp->out, x + 33); + buffer_write8(wp->out, y + 33); + } +} diff --git a/usr.bin/tmux/input.c b/usr.bin/tmux/input.c new file mode 100644 index 00000000000..2f7105fb7af --- /dev/null +++ b/usr.bin/tmux/input.c @@ -0,0 +1,1276 @@ +/* $OpenBSD: input.c,v 1.1 2009/06/01 22:58:49 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 <stdint.h> +#include <stdlib.h> +#include <string.h> + +#include "tmux.h" + +#define INPUT_C0CONTROL(ch) (ch <= 0x1f) +#define INPUT_INTERMEDIATE(ch) (ch == 0xa0 || (ch >= 0x20 && ch <= 0x2f)) +#define INPUT_PARAMETER(ch) (ch >= 0x30 && ch <= 0x3f) +#define INPUT_UPPERCASE(ch) (ch >= 0x40 && ch <= 0x5f) +#define INPUT_LOWERCASE(ch) (ch >= 0x60 && ch <= 0x7e) +#define INPUT_DELETE(ch) (ch == 0x7f) +#define INPUT_C1CONTROL(ch) (ch >= 0x80 && ch <= 0x9f) +#define INPUT_G1DISPLAYABLE(ch) (ch >= 0xa1 && ch <= 0xfe) +#define INPUT_SPECIAL(ch) (ch == 0xff) + +int input_get_argument(struct input_ctx *, u_int, uint16_t *, uint16_t); +int input_new_argument(struct input_ctx *); +int input_add_argument(struct input_ctx *, u_char); + +void input_start_string(struct input_ctx *, int); +void input_abort_string(struct input_ctx *); +int input_add_string(struct input_ctx *, u_char); +char *input_get_string(struct input_ctx *); + +void input_state(struct input_ctx *, void *); + +void input_state_first(u_char, struct input_ctx *); +void input_state_escape(u_char, struct input_ctx *); +void input_state_intermediate(u_char, struct input_ctx *); +void input_state_sequence_first(u_char, struct input_ctx *); +void input_state_sequence_next(u_char, struct input_ctx *); +void input_state_sequence_intermediate(u_char, struct input_ctx *); +void input_state_string_next(u_char, struct input_ctx *); +void input_state_string_escape(u_char, struct input_ctx *); +void input_state_utf8(u_char, struct input_ctx *); + +void input_handle_character(u_char, struct input_ctx *); +void input_handle_c0_control(u_char, struct input_ctx *); +void input_handle_c1_control(u_char, struct input_ctx *); +void input_handle_private_two(u_char, struct input_ctx *); +void input_handle_standard_two(u_char, struct input_ctx *); +void input_handle_sequence(u_char, struct input_ctx *); + +void input_handle_sequence_cuu(struct input_ctx *); +void input_handle_sequence_cud(struct input_ctx *); +void input_handle_sequence_cuf(struct input_ctx *); +void input_handle_sequence_cub(struct input_ctx *); +void input_handle_sequence_dch(struct input_ctx *); +void input_handle_sequence_dl(struct input_ctx *); +void input_handle_sequence_ich(struct input_ctx *); +void input_handle_sequence_il(struct input_ctx *); +void input_handle_sequence_vpa(struct input_ctx *); +void input_handle_sequence_hpa(struct input_ctx *); +void input_handle_sequence_cup(struct input_ctx *); +void input_handle_sequence_cup(struct input_ctx *); +void input_handle_sequence_ed(struct input_ctx *); +void input_handle_sequence_el(struct input_ctx *); +void input_handle_sequence_sm(struct input_ctx *); +void input_handle_sequence_rm(struct input_ctx *); +void input_handle_sequence_decstbm(struct input_ctx *); +void input_handle_sequence_sgr(struct input_ctx *); +void input_handle_sequence_dsr(struct input_ctx *); + +int input_sequence_cmp(const void *, const void *); + +struct input_sequence_entry { + u_char ch; + void (*fn)(struct input_ctx *); +}; +const struct input_sequence_entry input_sequence_table[] = { + { '@', input_handle_sequence_ich }, + { 'A', input_handle_sequence_cuu }, + { 'B', input_handle_sequence_cud }, + { 'C', input_handle_sequence_cuf }, + { 'D', input_handle_sequence_cub }, + { 'G', input_handle_sequence_hpa }, + { 'H', input_handle_sequence_cup }, + { 'J', input_handle_sequence_ed }, + { 'K', input_handle_sequence_el }, + { 'L', input_handle_sequence_il }, + { 'M', input_handle_sequence_dl }, + { 'P', input_handle_sequence_dch }, + { 'd', input_handle_sequence_vpa }, + { 'f', input_handle_sequence_cup }, + { 'h', input_handle_sequence_sm }, + { 'l', input_handle_sequence_rm }, + { 'm', input_handle_sequence_sgr }, + { 'n', input_handle_sequence_dsr }, + { 'r', input_handle_sequence_decstbm }, +}; + +int +input_sequence_cmp(const void *a, const void *b) +{ + int ai = ((const struct input_sequence_entry *) a)->ch; + int bi = ((const struct input_sequence_entry *) b)->ch; + + return (ai - bi); +} + +int +input_new_argument(struct input_ctx *ictx) +{ + struct input_arg *arg; + + ARRAY_EXPAND(&ictx->args, 1); + + arg = &ARRAY_LAST(&ictx->args); + arg->used = 0; + + return (0); +} + +int +input_add_argument(struct input_ctx *ictx, u_char ch) +{ + struct input_arg *arg; + + if (ARRAY_LENGTH(&ictx->args) == 0) + return (0); + + arg = &ARRAY_LAST(&ictx->args); + if (arg->used > (sizeof arg->data) - 1) + return (-1); + arg->data[arg->used++] = ch; + + return (0); +} + +int +input_get_argument(struct input_ctx *ictx, u_int i, uint16_t *n, uint16_t d) +{ + struct input_arg *arg; + const char *errstr; + + *n = d; + if (i >= ARRAY_LENGTH(&ictx->args)) + return (0); + + arg = &ARRAY_ITEM(&ictx->args, i); + if (*arg->data == '\0') + return (0); + + *n = strtonum(arg->data, 0, UINT16_MAX, &errstr); + if (errstr != NULL) + return (-1); + return (0); +} + +void +input_start_string(struct input_ctx *ictx, int type) +{ + ictx->string_type = type; + ictx->string_len = 0; +} + +void +input_abort_string(struct input_ctx *ictx) +{ + if (ictx->string_buf != NULL) + xfree(ictx->string_buf); + ictx->string_buf = NULL; +} + +int +input_add_string(struct input_ctx *ictx, u_char ch) +{ + ictx->string_buf = xrealloc(ictx->string_buf, 1, ictx->string_len + 1); + ictx->string_buf[ictx->string_len++] = ch; + + if (ictx->string_len >= MAXSTRINGLEN) { + input_abort_string(ictx); + return (1); + } + + return (0); +} + +char * +input_get_string(struct input_ctx *ictx) +{ + char *s; + + if (ictx->string_buf == NULL || input_add_string(ictx, '\0') != 0) + return (xstrdup("")); + + s = ictx->string_buf; + ictx->string_buf = NULL; + return (s); +} + +void +input_state(struct input_ctx *ictx, void *state) +{ + ictx->state = state; +} + +void +input_init(struct window_pane *wp) +{ + struct input_ctx *ictx = &wp->ictx; + + ARRAY_INIT(&ictx->args); + + ictx->string_len = 0; + ictx->string_buf = NULL; + + memcpy(&ictx->cell, &grid_default_cell, sizeof ictx->cell); + + memcpy(&ictx->saved_cell, &grid_default_cell, sizeof ictx->saved_cell); + ictx->saved_cx = 0; + ictx->saved_cy = 0; + + input_state(ictx, input_state_first); +} + +void +input_free(struct window_pane *wp) +{ + if (wp->ictx.string_buf != NULL) + xfree(wp->ictx.string_buf); + + ARRAY_FREE(&wp->ictx.args); +} + +void +input_parse(struct window_pane *wp) +{ + struct input_ctx *ictx = &wp->ictx; + u_char ch; + + if (BUFFER_USED(wp->in) == 0) + return; + + ictx->buf = BUFFER_OUT(wp->in); + ictx->len = BUFFER_USED(wp->in); + ictx->off = 0; + + ictx->wp = wp; + + log_debug2("entry; buffer=%zu", ictx->len); + + if (wp->mode == NULL) + screen_write_start(&ictx->ctx, wp, &wp->base); + else + screen_write_start(&ictx->ctx, NULL, &wp->base); + + if (ictx->off != ictx->len) + wp->window->flags |= WINDOW_ACTIVITY; + while (ictx->off < ictx->len) { + ch = ictx->buf[ictx->off++]; + ictx->state(ch, ictx); + } + + screen_write_stop(&ictx->ctx); + + buffer_remove(wp->in, ictx->len); +} + +void +input_state_first(u_char ch, struct input_ctx *ictx) +{ + ictx->intermediate = '\0'; + + if (INPUT_C0CONTROL(ch)) { + if (ch == 0x1b) + input_state(ictx, input_state_escape); + else + input_handle_c0_control(ch, ictx); + return; + } + +#if 0 + if (INPUT_C1CONTROL(ch)) { + ch -= 0x40; + if (ch == '[') + input_state(ictx, input_state_sequence_first); + else if (ch == ']') { + input_start_string(ictx, STRING_SYSTEM); + input_state(ictx, input_state_string_next); + } else if (ch == '_') { + input_start_string(ictx, STRING_APPLICATION); + input_state(ictx, input_state_string_next); + } else + input_handle_c1_control(ch, ictx); + return; + } +#endif + + if (INPUT_DELETE(ch)) + return; + + input_handle_character(ch, ictx); +} + +void +input_state_escape(u_char ch, struct input_ctx *ictx) +{ + /* Treat C1 control and G1 displayable as 7-bit equivalent. */ + if (INPUT_C1CONTROL(ch) || INPUT_G1DISPLAYABLE(ch)) + ch &= 0x7f; + + if (INPUT_C0CONTROL(ch)) { + input_handle_c0_control(ch, ictx); + return; + } + + if (INPUT_INTERMEDIATE(ch)) { + log_debug2(":: in1 %zu: %hhu (%c)", ictx->off, ch, ch); + ictx->intermediate = ch; + input_state(ictx, input_state_intermediate); + return; + } + + if (INPUT_PARAMETER(ch)) { + input_state(ictx, input_state_first); + input_handle_private_two(ch, ictx); + return; + } + + if (INPUT_UPPERCASE(ch)) { + if (ch == '[') + input_state(ictx, input_state_sequence_first); + else if (ch == ']') { + input_start_string(ictx, STRING_SYSTEM); + input_state(ictx, input_state_string_next); + } else if (ch == '_') { + input_start_string(ictx, STRING_APPLICATION); + input_state(ictx, input_state_string_next); + } else { + input_state(ictx, input_state_first); + input_handle_c1_control(ch, ictx); + } + return; + } + + if (INPUT_LOWERCASE(ch)) { + input_state(ictx, input_state_first); + input_handle_standard_two(ch, ictx); + return; + } + + input_state(ictx, input_state_first); +} + +void +input_state_intermediate(u_char ch, struct input_ctx *ictx) +{ + if (INPUT_INTERMEDIATE(ch)) { + /* Multiple intermediates currently ignored. */ + log_debug2(":: in2 %zu: %hhu (%c)", ictx->off, ch, ch); + return; + } + + if (INPUT_PARAMETER(ch)) { + input_state(ictx, input_state_first); + input_handle_private_two(ch, ictx); + return; + } + + if (INPUT_UPPERCASE(ch) || INPUT_LOWERCASE(ch)) { + input_state(ictx, input_state_first); + input_handle_standard_two(ch, ictx); + return; + } + + input_state(ictx, input_state_first); +} + +void +input_state_sequence_first(u_char ch, struct input_ctx *ictx) +{ + ictx->private = '\0'; + ARRAY_CLEAR(&ictx->args); + + input_state(ictx, input_state_sequence_next); + + if (INPUT_PARAMETER(ch)) { + input_new_argument(ictx); + if (ch >= 0x3c && ch <= 0x3f) { + /* Private control sequence. */ + ictx->private = ch; + return; + } + } + + /* Pass character on directly. */ + input_state_sequence_next(ch, ictx); +} + +void +input_state_sequence_next(u_char ch, struct input_ctx *ictx) +{ + if (INPUT_INTERMEDIATE(ch)) { + if (input_add_argument(ictx, '\0') != 0) + input_state(ictx, input_state_first); + else { + log_debug2(":: si1 %zu: %hhu (%c)", ictx->off, ch, ch); + input_state(ictx, input_state_sequence_intermediate); + } + return; + } + + if (INPUT_PARAMETER(ch)) { + if (ch == ';') { + if (input_add_argument(ictx, '\0') != 0) + input_state(ictx, input_state_first); + else + input_new_argument(ictx); + } else if (input_add_argument(ictx, ch) != 0) + input_state(ictx, input_state_first); + return; + } + + if (INPUT_UPPERCASE(ch) || INPUT_LOWERCASE(ch)) { + if (input_add_argument(ictx, '\0') != 0) + input_state(ictx, input_state_first); + else { + input_state(ictx, input_state_first); + input_handle_sequence(ch, ictx); + } + return; + } + + input_state(ictx, input_state_first); +} + +void +input_state_sequence_intermediate(u_char ch, struct input_ctx *ictx) +{ + if (INPUT_INTERMEDIATE(ch)) { + log_debug2(":: si2 %zu: %hhu (%c)", ictx->off, ch, ch); + return; + } + + if (INPUT_UPPERCASE(ch) || INPUT_LOWERCASE(ch)) { + input_state(ictx, input_state_first); + input_handle_sequence(ch, ictx); + return; + } + + input_state(ictx, input_state_first); +} + +void +input_state_string_next(u_char ch, struct input_ctx *ictx) +{ + if (ch == 0x1b) { + input_state(ictx, input_state_string_escape); + return; + } + if (ch == 0x07) { + input_state_string_escape(ch, ictx); + return; + } + + if (ch >= 0x20 && ch != 0x7f) { + if (input_add_string(ictx, ch) != 0) + input_state(ictx, input_state_first); + return; + } +} + +void +input_state_string_escape(u_char ch, struct input_ctx *ictx) +{ + char *s; + + if (ch == '\007' || ch == '\\') { + input_state(ictx, input_state_first); + switch (ictx->string_type) { + case STRING_SYSTEM: + if (ch != '\007') + return; + s = input_get_string(ictx); + if ((s[0] != '0' && s[0] != '2') || s[1] != ';') { + xfree(s); + return; + } + screen_set_title(ictx->ctx.s, s + 2); + server_status_window(ictx->wp->window); + xfree(s); + break; + case STRING_APPLICATION: + if (ch != '\\') + return; + s = input_get_string(ictx); + screen_set_title(ictx->ctx.s, s); + server_status_window(ictx->wp->window); + xfree(s); + break; + case STRING_NAME: + if (ch != '\\') + return; + xfree(ictx->wp->window->name); + ictx->wp->window->name = input_get_string(ictx); + server_status_window(ictx->wp->window); + break; + } + return; + } + + input_state(ictx, input_state_string_next); + input_state_string_next(ch, ictx); +} + +void +input_state_utf8(u_char ch, struct input_ctx *ictx) +{ + log_debug2("-- un %zu: %hhu (%c)", ictx->off, ch, ch); + + ictx->utf8_buf[ictx->utf8_off++] = ch; + if (--ictx->utf8_len != 0) + return; + input_state(ictx, input_state_first); + + ictx->cell.flags |= GRID_FLAG_UTF8; + screen_write_cell(&ictx->ctx, &ictx->cell, ictx->utf8_buf); + ictx->cell.flags &= ~GRID_FLAG_UTF8; +} + +void +input_handle_character(u_char ch, struct input_ctx *ictx) +{ + struct window_pane *wp = ictx->wp; + + if (ch > 0x7f && options_get_number(&wp->window->options, "utf8")) { + /* + * UTF-8 sequence. + * + * 11000010-11011111 C2-DF start of 2-byte sequence + * 11100000-11101111 E0-EF start of 3-byte sequence + * 11110000-11110100 F0-F4 start of 4-byte sequence + */ + memset(ictx->utf8_buf, 0xff, sizeof ictx->utf8_buf); + ictx->utf8_buf[0] = ch; + ictx->utf8_off = 1; + + if (ch >= 0xc2 && ch <= 0xdf) { + log_debug2("-- u2 %zu: %hhu (%c)", ictx->off, ch, ch); + input_state(ictx, input_state_utf8); + ictx->utf8_len = 1; + return; + } + if (ch >= 0xe0 && ch <= 0xef) { + log_debug2("-- u3 %zu: %hhu (%c)", ictx->off, ch, ch); + input_state(ictx, input_state_utf8); + ictx->utf8_len = 2; + return; + } + if (ch >= 0xf0 && ch <= 0xf4) { + log_debug2("-- u4 %zu: %hhu (%c)", ictx->off, ch, ch); + input_state(ictx, input_state_utf8); + ictx->utf8_len = 3; + return; + } + } + log_debug2("-- ch %zu: %hhu (%c)", ictx->off, ch, ch); + + ictx->cell.data = ch; + screen_write_cell(&ictx->ctx, &ictx->cell, ictx->utf8_buf); +} + +void +input_handle_c0_control(u_char ch, struct input_ctx *ictx) +{ + struct screen *s = ictx->ctx.s; + + log_debug2("-- c0 %zu: %hhu", ictx->off, ch); + + switch (ch) { + case '\0': /* NUL */ + break; + case '\n': /* LF */ + screen_write_linefeed(&ictx->ctx); + break; + case '\r': /* CR */ + screen_write_carriagereturn(&ictx->ctx); + break; + case '\007': /* BELL */ + ictx->wp->window->flags |= WINDOW_BELL; + break; + case '\010': /* BS */ + screen_write_cursorleft(&ictx->ctx, 1); + break; + case '\011': /* TAB */ + s->cx = ((s->cx / 8) * 8) + 8; + if (s->cx > screen_size_x(s) - 1) { + s->cx = 0; + screen_write_cursordown(&ictx->ctx, 1); + } + screen_write_cursormove(&ictx->ctx, s->cx, s->cy); + break; + case '\016': /* SO */ + ictx->cell.attr |= GRID_ATTR_CHARSET; + break; + case '\017': /* SI */ + ictx->cell.attr &= ~GRID_ATTR_CHARSET; + break; + default: + log_debug("unknown c0: %hhu", ch); + break; + } +} + +void +input_handle_c1_control(u_char ch, struct input_ctx *ictx) +{ + log_debug2("-- c1 %zu: %hhu (%c)", ictx->off, ch, ch); + + switch (ch) { + case 'E': /* NEL */ + screen_write_carriagereturn(&ictx->ctx); + screen_write_linefeed(&ictx->ctx); + break; + case 'M': /* RI */ + screen_write_reverseindex(&ictx->ctx); + break; + default: + log_debug("unknown c1: %hhu", ch); + break; + } +} + +void +input_handle_private_two(u_char ch, struct input_ctx *ictx) +{ + struct screen *s = ictx->ctx.s; + + log_debug2( + "-- p2 %zu: %hhu (%c) %hhu", ictx->off, ch, ch, ictx->intermediate); + + switch (ch) { + case '0': /* Dscs (graphics) */ + /* + * Not really supported, but fake it up enough for those that + * use it to switch character sets (by redefining G0 to + * graphics set, rather than switching to G1). + */ + switch (ictx->intermediate) { + case '(': /* G0 */ + ictx->cell.attr |= GRID_ATTR_CHARSET; + break; + } + break; + case '=': /* DECKPAM */ + screen_write_kkeypadmode(&ictx->ctx, 1); + log_debug("kkeypad on (application mode)"); + break; + case '>': /* DECKPNM */ + screen_write_kkeypadmode(&ictx->ctx, 0); + log_debug("kkeypad off (number mode)"); + break; + case '7': /* DECSC */ + memcpy(&ictx->saved_cell, &ictx->cell, sizeof ictx->saved_cell); + ictx->saved_cx = s->cx; + ictx->saved_cy = s->cy; + break; + case '8': /* DECRC */ + memcpy(&ictx->cell, &ictx->saved_cell, sizeof ictx->cell); + screen_write_cursormove( + &ictx->ctx, ictx->saved_cx, ictx->saved_cy); + break; + default: + log_debug("unknown p2: %hhu", ch); + break; + } +} + +void +input_handle_standard_two(u_char ch, struct input_ctx *ictx) +{ + log_debug2( + "-- s2 %zu: %hhu (%c) %hhu", ictx->off, ch, ch, ictx->intermediate); + + switch (ch) { + case 'B': /* Dscs (ASCII) */ + /* + * Not really supported, but fake it up enough for those that + * use it to switch character sets (by redefining G0 to + * graphics set, rather than switching to G1). + */ + switch (ictx->intermediate) { + case '(': /* G0 */ + ictx->cell.attr &= ~GRID_ATTR_CHARSET; + break; + } + break; + case 'c': /* RIS */ + memcpy(&ictx->cell, &grid_default_cell, sizeof ictx->cell); + + memcpy(&ictx->saved_cell, &ictx->cell, sizeof ictx->saved_cell); + ictx->saved_cx = 0; + ictx->saved_cy = 0; + + screen_write_scrollregion( + &ictx->ctx, 0, screen_size_y(ictx->ctx.s) - 1); + + screen_write_insertmode(&ictx->ctx, 0); + screen_write_kcursormode(&ictx->ctx, 0); + screen_write_kkeypadmode(&ictx->ctx, 0); + screen_write_mousemode(&ictx->ctx, 0); + + screen_write_clearscreen(&ictx->ctx); + screen_write_cursormove(&ictx->ctx, 0, 0); + break; + case 'k': + input_start_string(ictx, STRING_NAME); + input_state(ictx, input_state_string_next); + break; + default: + log_debug("unknown s2: %hhu", ch); + break; + } +} + +void +input_handle_sequence(u_char ch, struct input_ctx *ictx) +{ + struct input_sequence_entry *entry, find; + struct screen *s = ictx->ctx.s; + u_int i; + struct input_arg *iarg; + + log_debug2("-- sq %zu: %hhu (%c): %u [sx=%u, sy=%u, cx=%u, cy=%u, " + "ru=%u, rl=%u]", ictx->off, ch, ch, ARRAY_LENGTH(&ictx->args), + screen_size_x(s), screen_size_y(s), s->cx, s->cy, s->rupper, + s->rlower); + for (i = 0; i < ARRAY_LENGTH(&ictx->args); i++) { + iarg = &ARRAY_ITEM(&ictx->args, i); + if (*iarg->data != '\0') + log_debug2(" ++ %u: %s", i, iarg->data); + } + + find.ch = ch; + entry = bsearch(&find, + input_sequence_table, nitems(input_sequence_table), + sizeof input_sequence_table[0], input_sequence_cmp); + if (entry != NULL) + entry->fn(ictx); + else + log_debug("unknown sq: %c (%hhu %hhu)", ch, ch, ictx->private); +} + +void +input_handle_sequence_cuu(struct input_ctx *ictx) +{ + uint16_t n; + + if (ictx->private != '\0') + return; + + if (ARRAY_LENGTH(&ictx->args) > 1) + return; + if (input_get_argument(ictx, 0, &n, 1) != 0) + return; + if (n == 0) + n = 1; + + screen_write_cursorup(&ictx->ctx, n); +} + +void +input_handle_sequence_cud(struct input_ctx *ictx) +{ + uint16_t n; + + if (ictx->private != '\0') + return; + + if (ARRAY_LENGTH(&ictx->args) > 1) + return; + if (input_get_argument(ictx, 0, &n, 1) != 0) + return; + if (n == 0) + n = 1; + + screen_write_cursordown(&ictx->ctx, n); +} + +void +input_handle_sequence_cuf(struct input_ctx *ictx) +{ + uint16_t n; + + if (ictx->private != '\0') + return; + + if (ARRAY_LENGTH(&ictx->args) > 1) + return; + if (input_get_argument(ictx, 0, &n, 1) != 0) + return; + if (n == 0) + n = 1; + + screen_write_cursorright(&ictx->ctx, n); +} + +void +input_handle_sequence_cub(struct input_ctx *ictx) +{ + uint16_t n; + + if (ictx->private != '\0') + return; + + if (ARRAY_LENGTH(&ictx->args) > 1) + return; + if (input_get_argument(ictx, 0, &n, 1) != 0) + return; + if (n == 0) + n = 1; + + screen_write_cursorleft(&ictx->ctx, n); +} + +void +input_handle_sequence_dch(struct input_ctx *ictx) +{ + uint16_t n; + + if (ictx->private != '\0') + return; + + if (ARRAY_LENGTH(&ictx->args) > 1) + return; + if (input_get_argument(ictx, 0, &n, 1) != 0) + return; + if (n == 0) + n = 1; + + screen_write_deletecharacter(&ictx->ctx, n); +} + +void +input_handle_sequence_dl(struct input_ctx *ictx) +{ + uint16_t n; + + if (ictx->private != '\0') + return; + + if (ARRAY_LENGTH(&ictx->args) > 1) + return; + if (input_get_argument(ictx, 0, &n, 1) != 0) + return; + if (n == 0) + n = 1; + + screen_write_deleteline(&ictx->ctx, n); +} + +void +input_handle_sequence_ich(struct input_ctx *ictx) +{ + uint16_t n; + + if (ictx->private != '\0') + return; + + if (ARRAY_LENGTH(&ictx->args) > 1) + return; + if (input_get_argument(ictx, 0, &n, 1) != 0) + return; + if (n == 0) + n = 1; + + screen_write_insertcharacter(&ictx->ctx, n); +} + +void +input_handle_sequence_il(struct input_ctx *ictx) +{ + uint16_t n; + + if (ictx->private != '\0') + return; + + if (ARRAY_LENGTH(&ictx->args) > 1) + return; + if (input_get_argument(ictx, 0, &n, 1) != 0) + return; + if (n == 0) + n = 1; + + screen_write_insertline(&ictx->ctx, n); +} + +void +input_handle_sequence_vpa(struct input_ctx *ictx) +{ + struct screen *s = ictx->ctx.s; + uint16_t n; + + if (ictx->private != '\0') + return; + + if (ARRAY_LENGTH(&ictx->args) > 1) + return; + if (input_get_argument(ictx, 0, &n, 1) != 0) + return; + if (n == 0) + n = 1; + + screen_write_cursormove(&ictx->ctx, s->cx, n - 1); +} + +void +input_handle_sequence_hpa(struct input_ctx *ictx) +{ + struct screen *s = ictx->ctx.s; + uint16_t n; + + if (ictx->private != '\0') + return; + + if (ARRAY_LENGTH(&ictx->args) > 1) + return; + if (input_get_argument(ictx, 0, &n, 1) != 0) + return; + if (n == 0) + n = 1; + + screen_write_cursormove(&ictx->ctx, n - 1, s->cy); +} + +void +input_handle_sequence_cup(struct input_ctx *ictx) +{ + uint16_t n, m; + + if (ictx->private != '\0') + return; + + if (ARRAY_LENGTH(&ictx->args) > 2) + return; + if (input_get_argument(ictx, 0, &n, 1) != 0) + return; + if (input_get_argument(ictx, 1, &m, 1) != 0) + return; + if (n == 0) + n = 1; + if (m == 0) + m = 1; + + screen_write_cursormove(&ictx->ctx, m - 1, n - 1); +} + +void +input_handle_sequence_ed(struct input_ctx *ictx) +{ + uint16_t n; + + if (ictx->private != '\0') + return; + + if (ARRAY_LENGTH(&ictx->args) > 1) + return; + if (input_get_argument(ictx, 0, &n, 0) != 0) + return; + if (n > 2) + return; + + switch (n) { + case 0: + screen_write_clearendofscreen(&ictx->ctx); + break; + case 1: + screen_write_clearstartofscreen(&ictx->ctx); + break; + case 2: + screen_write_clearscreen(&ictx->ctx); + break; + } +} + +void +input_handle_sequence_el(struct input_ctx *ictx) +{ + uint16_t n; + + if (ictx->private != '\0') + return; + + if (ARRAY_LENGTH(&ictx->args) > 1) + return; + if (input_get_argument(ictx, 0, &n, 0) != 0) + return; + if (n > 2) + return; + + switch (n) { + case 0: + screen_write_clearendofline(&ictx->ctx); + break; + case 1: + screen_write_clearstartofline(&ictx->ctx); + break; + case 2: + screen_write_clearline(&ictx->ctx); + break; + } +} + +void +input_handle_sequence_sm(struct input_ctx *ictx) +{ + uint16_t n; + + if (ARRAY_LENGTH(&ictx->args) > 1) + return; + if (input_get_argument(ictx, 0, &n, 0) != 0) + return; + + if (ictx->private == '?') { + switch (n) { + case 1: /* GATM */ + screen_write_kcursormode(&ictx->ctx, 1); + log_debug("kcursor on"); + break; + case 25: /* TCEM */ + screen_write_cursormode(&ictx->ctx, 1); + log_debug("cursor on"); + break; + case 1000: + screen_write_mousemode(&ictx->ctx, 1); + log_debug("mouse on"); + break; + default: + log_debug("unknown SM [%hhu]: %u", ictx->private, n); + break; + } + } else { + switch (n) { + case 4: /* IRM */ + screen_write_insertmode(&ictx->ctx, 1); + log_debug("insert on"); + break; + case 34: + /* Cursor high visibility not supported. */ + break; + default: + log_debug("unknown SM [%hhu]: %u", ictx->private, n); + break; + } + } +} + +void +input_handle_sequence_rm(struct input_ctx *ictx) +{ + uint16_t n; + + if (ARRAY_LENGTH(&ictx->args) > 1) + return; + if (input_get_argument(ictx, 0, &n, 0) != 0) + return; + + if (ictx->private == '?') { + switch (n) { + case 1: /* GATM */ + screen_write_kcursormode(&ictx->ctx, 0); + log_debug("kcursor off"); + break; + case 25: /* TCEM */ + screen_write_cursormode(&ictx->ctx, 0); + log_debug("cursor off"); + break; + case 1000: + screen_write_mousemode(&ictx->ctx, 0); + log_debug("mouse off"); + break; + default: + log_debug("unknown RM [%hhu]: %u", ictx->private, n); + break; + } + } else if (ictx->private == '\0') { + switch (n) { + case 4: /* IRM */ + screen_write_insertmode(&ictx->ctx, 0); + log_debug("insert off"); + break; + case 34: + /* Cursor high visibility not supported. */ + break; + default: + log_debug("unknown RM [%hhu]: %u", ictx->private, n); + break; + } + } +} + +void +input_handle_sequence_dsr(struct input_ctx *ictx) +{ + struct screen *s = ictx->ctx.s; + uint16_t n; + char reply[32]; + + if (ARRAY_LENGTH(&ictx->args) > 1) + return; + if (input_get_argument(ictx, 0, &n, 0) != 0) + return; + + if (ictx->private == '\0') { + switch (n) { + case 6: /* cursor position */ + xsnprintf(reply, sizeof reply, + "\033[%u;%uR", s->cy + 1, s->cx + 1); + log_debug("cursor request, reply: %s", reply); + buffer_write(ictx->wp->out, reply, strlen(reply)); + break; + } + } + +} + +void +input_handle_sequence_decstbm(struct input_ctx *ictx) +{ + struct screen *s = ictx->ctx.s; + uint16_t n, m; + + if (ictx->private != '\0') + return; + + if (ARRAY_LENGTH(&ictx->args) > 2) + return; + if (input_get_argument(ictx, 0, &n, 0) != 0) + return; + if (input_get_argument(ictx, 1, &m, 0) != 0) + return; + if (n == 0) + n = 1; + if (m == 0) + m = screen_size_y(s); + + screen_write_scrollregion(&ictx->ctx, n - 1, m - 1); +} + +void +input_handle_sequence_sgr(struct input_ctx *ictx) +{ + struct grid_cell *gc = &ictx->cell; + u_int i; + uint16_t m, o; + u_char attr; + + if (ARRAY_LENGTH(&ictx->args) == 0) { + attr = gc->attr; + memcpy(gc, &grid_default_cell, sizeof *gc); + gc->attr |= (attr & GRID_ATTR_CHARSET); + return; + } + + for (i = 0; i < ARRAY_LENGTH(&ictx->args); i++) { + if (input_get_argument(ictx, i, &m, 0) != 0) + return; + + if (m == 38 || m == 48) { + i++; + if (input_get_argument(ictx, i, &o, 0) != 0) + return; + if (o != 5) + continue; + + i++; + if (input_get_argument(ictx, i, &o, 0) != 0) + return; + if (m == 38) { + gc->flags |= GRID_FLAG_FG256; + gc->fg = o; + } else if (m == 48) { + gc->flags |= GRID_FLAG_BG256; + gc->bg = o; + } + continue; + } + + switch (m) { + case 0: + case 10: + attr = gc->attr; + memcpy(gc, &grid_default_cell, sizeof *gc); + gc->attr |= (attr & GRID_ATTR_CHARSET); + break; + case 1: + gc->attr |= GRID_ATTR_BRIGHT; + break; + case 2: + gc->attr |= GRID_ATTR_DIM; + break; + case 3: + gc->attr |= GRID_ATTR_ITALICS; + break; + case 4: + gc->attr |= GRID_ATTR_UNDERSCORE; + break; + case 5: + gc->attr |= GRID_ATTR_BLINK; + break; + case 7: + gc->attr |= GRID_ATTR_REVERSE; + break; + case 8: + gc->attr |= GRID_ATTR_HIDDEN; + break; + case 22: + gc->attr &= ~(GRID_ATTR_BRIGHT|GRID_ATTR_DIM); + break; + case 23: + gc->attr &= ~GRID_ATTR_ITALICS; + break; + case 24: + gc->attr &= ~GRID_ATTR_UNDERSCORE; + break; + case 25: + gc->attr &= ~GRID_ATTR_BLINK; + break; + case 27: + gc->attr &= ~GRID_ATTR_REVERSE; + break; + case 30: + case 31: + case 32: + case 33: + case 34: + case 35: + case 36: + case 37: + gc->flags &= ~GRID_FLAG_FG256; + gc->fg = m - 30; + break; + case 39: + gc->flags &= ~GRID_FLAG_FG256; + gc->fg = 8; + break; + case 40: + case 41: + case 42: + case 43: + case 44: + case 45: + case 46: + case 47: + gc->flags &= ~GRID_FLAG_BG256; + gc->bg = m - 40; + break; + case 49: + gc->flags &= ~GRID_FLAG_BG256; + gc->bg = 8; + break; + } + } +} diff --git a/usr.bin/tmux/key-bindings.c b/usr.bin/tmux/key-bindings.c new file mode 100644 index 00000000000..743be1ee736 --- /dev/null +++ b/usr.bin/tmux/key-bindings.c @@ -0,0 +1,237 @@ +/* $OpenBSD: key-bindings.c,v 1.1 2009/06/01 22:58:49 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 <ctype.h> +#include <stdlib.h> +#include <string.h> + +#include "tmux.h" + +SPLAY_GENERATE(key_bindings, key_binding, entry, key_bindings_cmp); + +struct key_bindings key_bindings; + +int +key_bindings_cmp(struct key_binding *bd1, struct key_binding *bd2) +{ + return (bd1->key - bd2->key); +} + +struct key_binding * +key_bindings_lookup(int key) +{ + struct key_binding bd; + + bd.key = key; + return (SPLAY_FIND(key_bindings, &key_bindings, &bd)); +} + +void +key_bindings_add(int key, int can_repeat, struct cmd_list *cmdlist) +{ + struct key_binding *bd; + + if ((bd = key_bindings_lookup(key)) == NULL) { + bd = xmalloc(sizeof *bd); + bd->key = key; + SPLAY_INSERT(key_bindings, &key_bindings, bd); + } else + cmd_list_free(bd->cmdlist); + bd->can_repeat = can_repeat; + bd->cmdlist = cmdlist; +} + +void +key_bindings_remove(int key) +{ + struct key_binding *bd; + + if ((bd = key_bindings_lookup(key)) == NULL) + return; + SPLAY_REMOVE(key_bindings, &key_bindings, bd); + + cmd_list_free(bd->cmdlist); + xfree(bd); +} + +void +key_bindings_init(void) +{ + static const struct { + int key; + int can_repeat; + const struct cmd_entry *entry; + } table[] = { + { ' ', 0, &cmd_next_layout_entry }, + { '!', 0, &cmd_break_pane_entry }, + { '"', 0, &cmd_split_window_entry }, + { '#', 0, &cmd_list_buffers_entry }, + { '&', 0, &cmd_confirm_before_entry }, + { ',', 0, &cmd_command_prompt_entry }, + { '-', 0, &cmd_delete_buffer_entry }, + { '.', 0, &cmd_command_prompt_entry }, + { '0', 0, &cmd_select_window_entry }, + { '1', 0, &cmd_select_window_entry }, + { '2', 0, &cmd_select_window_entry }, + { '3', 0, &cmd_select_window_entry }, + { '4', 0, &cmd_select_window_entry }, + { '5', 0, &cmd_select_window_entry }, + { '6', 0, &cmd_select_window_entry }, + { '7', 0, &cmd_select_window_entry }, + { '8', 0, &cmd_select_window_entry }, + { '9', 0, &cmd_select_window_entry }, + { ':', 0, &cmd_command_prompt_entry }, + { '=', 0, &cmd_scroll_mode_entry }, + { '?', 0, &cmd_list_keys_entry }, + { '[', 0, &cmd_copy_mode_entry }, + { '\'', 0, &cmd_select_prompt_entry }, + { '\032', /* C-z */ 0, &cmd_suspend_client_entry }, + { ']', 0, &cmd_paste_buffer_entry }, + { 'c', 0, &cmd_new_window_entry }, + { 'd', 0, &cmd_detach_client_entry }, + { 'f', 0, &cmd_command_prompt_entry }, + { 'l', 0, &cmd_last_window_entry }, + { 'n', 0, &cmd_next_window_entry }, + { 'o', 0, &cmd_down_pane_entry }, + { 'p', 0, &cmd_previous_window_entry }, + { 'r', 0, &cmd_refresh_client_entry }, + { 's', 0, &cmd_choose_session_entry }, + { 't', 0, &cmd_clock_mode_entry }, + { 'w', 0, &cmd_choose_window_entry }, + { 'x', 0, &cmd_confirm_before_entry }, + { '{', 0, &cmd_swap_pane_entry }, + { '}', 0, &cmd_swap_pane_entry }, + { '\002', 0, &cmd_send_prefix_entry }, + { KEYC_ADDESC('0'), 0, &cmd_select_layout_entry }, + { KEYC_ADDESC('1'), 0, &cmd_select_layout_entry }, + { KEYC_ADDESC('2'), 0, &cmd_select_layout_entry }, + { KEYC_ADDESC('9'), 0, &cmd_select_layout_entry }, + { KEYC_ADDCTL(KEYC_DOWN), 1, &cmd_resize_pane_entry }, + { KEYC_PPAGE, 0, &cmd_scroll_mode_entry }, + { KEYC_ADDESC('n'), 0, &cmd_next_window_entry }, + { KEYC_ADDESC('p'), 0, &cmd_previous_window_entry }, + { KEYC_UP, 1, &cmd_up_pane_entry }, + { KEYC_DOWN, 1, &cmd_down_pane_entry }, + { KEYC_ADDESC(KEYC_UP), 1, &cmd_resize_pane_entry }, + { KEYC_ADDESC(KEYC_DOWN), 1, &cmd_resize_pane_entry }, + { KEYC_ADDCTL(KEYC_UP), 1, &cmd_resize_pane_entry }, + { KEYC_ADDCTL(KEYC_DOWN), 1, &cmd_resize_pane_entry }, + { KEYC_ADDESC('o'), 0, &cmd_rotate_window_entry }, + { '\017', 0, &cmd_rotate_window_entry }, + }; + u_int i; + struct cmd *cmd; + struct cmd_list *cmdlist; + + SPLAY_INIT(&key_bindings); + + for (i = 0; i < nitems(table); i++) { + cmdlist = xmalloc(sizeof *cmdlist); + TAILQ_INIT(cmdlist); + + cmd = xmalloc(sizeof *cmd); + cmd->entry = table[i].entry; + cmd->data = NULL; + if (cmd->entry->init != NULL) + cmd->entry->init(cmd, table[i].key); + TAILQ_INSERT_HEAD(cmdlist, cmd, qentry); + + key_bindings_add(table[i].key, table[i].can_repeat, cmdlist); + } +} + +void +key_bindings_free(void) +{ + struct key_binding *bd; + + while (!SPLAY_EMPTY(&key_bindings)) { + bd = SPLAY_ROOT(&key_bindings); + SPLAY_REMOVE(key_bindings, &key_bindings, bd); + cmd_list_free(bd->cmdlist); + xfree(bd); + } +} + +void printflike2 +key_bindings_error(struct cmd_ctx *ctx, const char *fmt, ...) +{ + va_list ap; + char *msg; + + va_start(ap, fmt); + xvasprintf(&msg, fmt, ap); + va_end(ap); + + *msg = toupper((u_char) *msg); + status_message_set(ctx->curclient, msg); + xfree(msg); +} + +void printflike2 +key_bindings_print(struct cmd_ctx *ctx, const char *fmt, ...) +{ + struct winlink *wl = ctx->cursession->curw; + va_list ap; + + if (wl->window->active->mode != &window_more_mode) + window_pane_reset_mode(wl->window->active); + window_pane_set_mode(wl->window->active, &window_more_mode); + + va_start(ap, fmt); + window_more_vadd(wl->window->active, fmt, ap); + va_end(ap); +} + +void printflike2 +key_bindings_info(struct cmd_ctx *ctx, const char *fmt, ...) +{ + va_list ap; + char *msg; + + if (be_quiet) + return; + + va_start(ap, fmt); + xvasprintf(&msg, fmt, ap); + va_end(ap); + + *msg = toupper((u_char) *msg); + status_message_set(ctx->curclient, msg); + xfree(msg); +} + +void +key_bindings_dispatch(struct key_binding *bd, struct client *c) +{ + struct cmd_ctx ctx; + + ctx.msgdata = NULL; + ctx.cursession = c->session; + ctx.curclient = c; + + ctx.error = key_bindings_error; + ctx.print = key_bindings_print; + ctx.info = key_bindings_info; + + ctx.cmdclient = NULL; + + cmd_list_exec(bd->cmdlist, &ctx); +} diff --git a/usr.bin/tmux/key-string.c b/usr.bin/tmux/key-string.c new file mode 100644 index 00000000000..6280a42fe49 --- /dev/null +++ b/usr.bin/tmux/key-string.c @@ -0,0 +1,198 @@ +/* $OpenBSD: key-string.c,v 1.1 2009/06/01 22:58:49 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 <string.h> + +#include "tmux.h" + +int key_string_search_table(const char *); + +struct { + const char *string; + int key; +} key_string_table[] = { + /* Function keys. */ + { "F1", KEYC_F1 }, + { "F2", KEYC_F2 }, + { "F3", KEYC_F3 }, + { "F4", KEYC_F4 }, + { "F5", KEYC_F5 }, + { "F6", KEYC_F6 }, + { "F7", KEYC_F7 }, + { "F8", KEYC_F8 }, + { "F9", KEYC_F9 }, + { "F10", KEYC_F10 }, + { "F11", KEYC_F11 }, + { "F12", KEYC_F12 }, + { "F13", KEYC_F13 }, + { "F14", KEYC_F14 }, + { "F15", KEYC_F15 }, + { "F16", KEYC_F16 }, + { "F17", KEYC_F17 }, + { "F18", KEYC_F18 }, + { "F19", KEYC_F19 }, + { "F20", KEYC_F20 }, + { "IC", KEYC_IC }, + { "DC", KEYC_DC }, + { "Home", KEYC_HOME }, + { "End", KEYC_END }, + { "NPage", KEYC_NPAGE }, + { "PPage", KEYC_PPAGE }, + { "Tab", '\011' }, + { "BTab", KEYC_BTAB }, + + /* Arrow keys. */ + { "Up", KEYC_UP }, + { "Down", KEYC_DOWN }, + { "Left", KEYC_LEFT }, + { "Right", KEYC_RIGHT }, + + /* Numeric keypad. */ + { "KP/", KEYC_KP0_1 }, + { "KP*", KEYC_KP0_2 }, + { "KP-", KEYC_KP0_3 }, + { "KP7", KEYC_KP1_0 }, + { "KP8", KEYC_KP1_1 }, + { "KP9", KEYC_KP1_2 }, + { "KP+", KEYC_KP1_3 }, + { "KP4", KEYC_KP2_0 }, + { "KP5", KEYC_KP2_1 }, + { "KP6", KEYC_KP2_2 }, + { "KP1", KEYC_KP3_0 }, + { "KP2", KEYC_KP3_1 }, + { "KP3", KEYC_KP3_2 }, + { "KPEnter", KEYC_KP3_3 }, + { "KP0", KEYC_KP4_0 }, + { "KP.", KEYC_KP4_2 }, +}; + +int +key_string_search_table(const char *string) +{ + u_int i; + + for (i = 0; i < nitems(key_string_table); i++) { + if (strcasecmp(string, key_string_table[i].string) == 0) + return (key_string_table[i].key); + } + return (KEYC_NONE); +} + +int +key_string_lookup_string(const char *string) +{ + int key; + const u_char *ptr; + + if (string[0] == '\0') + return (KEYC_NONE); + if (string[1] == '\0') + return (string[0]); + + ptr = NULL; + if (string[0] == 'C' && string[1] == '-') + ptr = string + 2; + else if (string[0] == '^') + ptr = string + 1; + if (ptr != NULL) { + if (ptr[0] == '\0') + return (KEYC_NONE); + if (ptr[1] == '\0') { + if (ptr[0] == 32) + return (0); + if (ptr[0] >= 64 && ptr[0] <= 95) + return (ptr[0] - 64); + if (ptr[0] >= 97 && ptr[0] <= 122) + return (ptr[0] - 96); + return (KEYC_NONE); + } + key = key_string_search_table(ptr); + if (key != KEYC_NONE) + return (KEYC_ADDCTL(key)); + return (KEYC_NONE); + } + + if (string[0] == 'M' && string[1] == '-') { + ptr = string + 2; + if (ptr[0] == '\0') + return (KEYC_NONE); + if (ptr[1] == '\0') { + if (ptr[0] < 32 || ptr[0] > 127) + return (KEYC_NONE); + return (KEYC_ADDESC(ptr[0])); + } + key = key_string_lookup_string(ptr); + if (key != KEYC_NONE) + return (KEYC_ADDESC(key)); + return (KEYC_NONE); + } + + return (key_string_search_table(string)); +} + +const char * +key_string_lookup_key(int key) +{ + static char tmp[24], tmp2[24]; + const char *s; + u_int i; + + if (key == 127) + return (NULL); + + if (KEYC_ISESC(key)) { + if ((s = key_string_lookup_key(KEYC_REMOVEESC(key))) == NULL) + return (NULL); + xsnprintf(tmp2, sizeof tmp2, "M-%s", s); + return (tmp2); + } + if (KEYC_ISCTL(key)) { + if ((s = key_string_lookup_key(KEYC_REMOVECTL(key))) == NULL) + return (NULL); + xsnprintf(tmp2, sizeof tmp2, "C-%s", s); + return (tmp2); + } + if (KEYC_ISSFT(key)) { + if ((s = key_string_lookup_key(KEYC_REMOVESFT(key))) == NULL) + return (NULL); + xsnprintf(tmp2, sizeof tmp2, "S-%s", s); + return (tmp2); + } + + if (key >= 32 && key <= 255) { + tmp[0] = key; + tmp[1] = '\0'; + return (tmp); + } + + if (key >= 0 && key <= 32) { + if (key == 0 || key > 26) + xsnprintf(tmp, sizeof tmp, "C-%c", 64 + key); + else + xsnprintf(tmp, sizeof tmp, "C-%c", 96 + key); + return (tmp); + } + + for (i = 0; i < nitems(key_string_table); i++) { + if (key == key_string_table[i].key) + return (key_string_table[i].string); + } + return (NULL); +} diff --git a/usr.bin/tmux/layout-manual.c b/usr.bin/tmux/layout-manual.c new file mode 100644 index 00000000000..66d472d620b --- /dev/null +++ b/usr.bin/tmux/layout-manual.c @@ -0,0 +1,183 @@ +/* $OpenBSD: layout-manual.c,v 1.1 2009/06/01 22:58:49 nicm 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 "tmux.h" + +void layout_manual_v_update_offsets(struct window *); + +void +layout_manual_v_refresh(struct window *w, unused int active_only) +{ + struct window_pane *wp; + u_int npanes, canfit, total; + int left; + + if (active_only) + return; + + if (TAILQ_EMPTY(&w->panes)) + return; + + /* Clear hidden flags. */ + TAILQ_FOREACH(wp, &w->panes, entry) + wp->flags &= ~PANE_HIDDEN; + + /* Check the new size. */ + npanes = window_count_panes(w); + if (w->sy <= PANE_MINIMUM * npanes) { + /* How many can we fit? */ + canfit = w->sy / PANE_MINIMUM; + if (canfit == 0) { + /* None. Just use this size for the first. */ + TAILQ_FOREACH(wp, &w->panes, entry) { + if (wp == TAILQ_FIRST(&w->panes)) + wp->sy = w->sy; + else + wp->flags |= PANE_HIDDEN; + } + } else { + /* >=1, set minimum for them all. */ + TAILQ_FOREACH(wp, &w->panes, entry) { + if (canfit-- > 0) + wp->sy = PANE_MINIMUM - 1; + else + wp->flags |= PANE_HIDDEN; + } + /* And increase the first by the rest. */ + TAILQ_FIRST(&w->panes)->sy += 1 + w->sy % PANE_MINIMUM; + } + } else { + /* In theory they will all fit. Find the current total. */ + total = 0; + TAILQ_FOREACH(wp, &w->panes, entry) + total += wp->sy; + total += npanes - 1; + + /* Growing or shrinking? */ + left = w->sy - total; + if (left > 0) { + /* Growing. Expand evenly. */ + while (left > 0) { + TAILQ_FOREACH(wp, &w->panes, entry) { + wp->sy++; + if (--left == 0) + break; + } + } + } else { + /* Shrinking. Reduce evenly down to minimum. */ + while (left < 0) { + TAILQ_FOREACH(wp, &w->panes, entry) { + if (wp->sy <= PANE_MINIMUM - 1) + continue; + wp->sy--; + if (++left == 0) + break; + } + } + } + } + + /* Now do the resize. */ + TAILQ_FOREACH(wp, &w->panes, entry) { + wp->sy--; + window_pane_resize(wp, w->sx, wp->sy + 1); + } + + /* Fill in the offsets. */ + layout_manual_v_update_offsets(w); + + /* Switch the active window if necessary. */ + window_set_active_pane(w, w->active); +} + +void +layout_manual_v_resize(struct window_pane *wp, int adjust) +{ + struct window *w = wp->window; + struct window_pane *wq; + + if (adjust > 0) { + /* + * If this is not the last pane, keep trying to increase size + * and remove it from the next panes. If it is the last, do + * so on the previous pane. + */ + if (TAILQ_NEXT(wp, entry) == NULL) { + if (wp == TAILQ_FIRST(&w->panes)) { + /* Only one pane. */ + return; + } + wp = TAILQ_PREV(wp, window_panes, entry); + } + while (adjust-- > 0) { + wq = wp; + while ((wq = TAILQ_NEXT(wq, entry)) != NULL) { + if (wq->sy <= PANE_MINIMUM) + continue; + window_pane_resize(wq, wq->sx, wq->sy - 1); + break; + } + if (wq == NULL) + break; + window_pane_resize(wp, wp->sx, wp->sy + 1); + } + } else { + adjust = -adjust; + /* + * If this is not the last pane, keep trying to reduce size + * and add to the following pane. If it is the last, do so on + * the previous pane. + */ + wq = TAILQ_NEXT(wp, entry); + if (wq == NULL) { + if (wp == TAILQ_FIRST(&w->panes)) { + /* Only one pane. */ + return; + } + wq = wp; + wp = TAILQ_PREV(wq, window_panes, entry); + } + while (adjust-- > 0) { + if (wp->sy <= PANE_MINIMUM) + break; + window_pane_resize(wq, wq->sx, wq->sy + 1); + window_pane_resize(wp, wp->sx, wp->sy - 1); + } + } + + layout_manual_v_update_offsets(w); +} + +void +layout_manual_v_update_offsets(struct window *w) +{ + struct window_pane *wp; + u_int yoff; + + yoff = 0; + TAILQ_FOREACH(wp, &w->panes, entry) { + if (wp->flags & PANE_HIDDEN) + continue; + wp->xoff = 0; + wp->yoff = yoff; + yoff += wp->sy + 1; + } +} diff --git a/usr.bin/tmux/layout.c b/usr.bin/tmux/layout.c new file mode 100644 index 00000000000..62657a4bab7 --- /dev/null +++ b/usr.bin/tmux/layout.c @@ -0,0 +1,373 @@ +/* $OpenBSD: layout.c,v 1.1 2009/06/01 22:58:49 nicm 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 <string.h> + +#include "tmux.h" + +/* + * Each layout has two functions, _refresh to relayout the panes and _resize to + * resize a single pane. + * + * Second argument (int) to _refresh is 1 if the only change has been that the + * active pane has changed. If 0 then panes, active pane or both may have + * changed. + */ + +void layout_active_only_refresh(struct window *, int); +void layout_even_h_refresh(struct window *, int); +void layout_even_v_refresh(struct window *, int); +void layout_main_h_refresh(struct window *, int); +void layout_main_v_refresh(struct window *, int); + +const struct { + const char *name; + void (*refresh)(struct window *, int); + void (*resize)(struct window_pane *, int); +} layouts[] = { + { "manual-vertical", layout_manual_v_refresh, layout_manual_v_resize }, + { "active-only", layout_active_only_refresh, NULL }, + { "even-horizontal", layout_even_h_refresh, NULL }, + { "even-vertical", layout_even_v_refresh, NULL }, + { "main-horizontal", layout_main_h_refresh, NULL }, + { "main-vertical", layout_main_v_refresh, NULL }, +}; + +const char * +layout_name(struct window *w) +{ + return (layouts[w->layout].name); +} + +int +layout_lookup(const char *name) +{ + u_int i; + int matched = -1; + + for (i = 0; i < nitems(layouts); i++) { + if (strncmp(layouts[i].name, name, strlen(name)) == 0) { + if (matched != -1) /* ambiguous */ + return (-1); + matched = i; + } + } + + return (matched); +} + +int +layout_select(struct window *w, u_int layout) +{ + if (layout > nitems(layouts) - 1 || layout == w->layout) + return (-1); + w->layout = layout; + + layout_refresh(w, 0); + return (0); +} + +void +layout_next(struct window *w) +{ + w->layout++; + if (w->layout > nitems(layouts) - 1) + w->layout = 0; + layout_refresh(w, 0); +} + +void +layout_previous(struct window *w) +{ + if (w->layout == 0) + w->layout = nitems(layouts) - 1; + else + w->layout--; + layout_refresh(w, 0); +} + +void +layout_refresh(struct window *w, int active_only) +{ + layouts[w->layout].refresh(w, active_only); + server_redraw_window(w); +} + +int +layout_resize(struct window_pane *wp, int adjust) +{ + struct window *w = wp->window; + + if (layouts[w->layout].resize == NULL) + return (-1); + layouts[w->layout].resize(wp, adjust); + return (0); +} + +void +layout_active_only_refresh(struct window *w, unused int active_only) +{ + struct window_pane *wp; + + TAILQ_FOREACH(wp, &w->panes, entry) { + if (wp == w->active) { + wp->flags &= ~PANE_HIDDEN; + wp->xoff = wp->yoff = 0; + window_pane_resize(wp, w->sx, w->sy); + } else + wp->flags |= PANE_HIDDEN; + } +} + +void +layout_even_h_refresh(struct window *w, int active_only) +{ + struct window_pane *wp; + u_int i, n, width, xoff; + + if (active_only) + return; + + /* Get number of panes. */ + n = window_count_panes(w); + if (n == 0) + return; + + /* How many can we fit? */ + if (w->sx / n < PANE_MINIMUM) { + width = PANE_MINIMUM; + n = w->sx / PANE_MINIMUM; + } else + width = w->sx / n; + + /* Fit the panes. */ + i = xoff = 0; + TAILQ_FOREACH(wp, &w->panes, entry) { + if (i > n) { + wp->flags |= PANE_HIDDEN; + continue; + } + wp->flags &= ~PANE_HIDDEN; + + wp->xoff = xoff; + wp->yoff = 0; + if (i != n - 1) + window_pane_resize(wp, width - 1, w->sy); + else + window_pane_resize(wp, width, w->sy); + + i++; + xoff += width; + } + + /* Any space left? */ + while (xoff++ < w->sx) { + wp = TAILQ_LAST(&w->panes, window_panes); + window_pane_resize(wp, wp->sx + 1, wp->sy); + } +} + +void +layout_even_v_refresh(struct window *w, int active_only) +{ + struct window_pane *wp; + u_int i, n, height, yoff; + + if (active_only) + return; + + /* Get number of panes. */ + n = window_count_panes(w); + if (n == 0) + return; + + /* How many can we fit? */ + if (w->sy / n < PANE_MINIMUM) { + height = PANE_MINIMUM; + n = w->sy / PANE_MINIMUM; + } else + height = w->sy / n; + + /* Fit the panes. */ + i = yoff = 0; + TAILQ_FOREACH(wp, &w->panes, entry) { + if (i > n) { + wp->flags |= PANE_HIDDEN; + continue; + } + wp->flags &= ~PANE_HIDDEN; + + wp->xoff = 0; + wp->yoff = yoff; + if (i != n - 1) + window_pane_resize(wp, w->sx, height - 1); + else + window_pane_resize(wp, w->sx, height); + + i++; + yoff += height; + } + + /* Any space left? */ + while (yoff++ < w->sy) { + wp = TAILQ_LAST(&w->panes, window_panes); + window_pane_resize(wp, wp->sx, wp->sy + 1); + } +} + +void +layout_main_v_refresh(struct window *w, int active_only) +{ + struct window_pane *wp; + u_int i, n, mainwidth, height, yoff; + + if (active_only) + return; + + /* Get number of panes. */ + n = window_count_panes(w); + if (n == 0) + return; + + /* Get the main pane width and add one for separator line. */ + mainwidth = options_get_number(&w->options, "main-pane-width") + 1; + + /* Need >1 pane and minimum columns; if fewer, display active only. */ + if (n == 1 || w->sx < mainwidth + PANE_MINIMUM) { + layout_active_only_refresh(w, active_only); + return; + } + n--; + + /* How many can we fit, not including first? */ + if (w->sy / n < PANE_MINIMUM) { + height = PANE_MINIMUM; + n = w->sy / PANE_MINIMUM; + } else + height = w->sy / n; + + /* Fit the panes. */ + i = yoff = 0; + TAILQ_FOREACH(wp, &w->panes, entry) { + if (wp == TAILQ_FIRST(&w->panes)) { + wp->xoff = 0; + wp->yoff = 0; + window_pane_resize(wp, mainwidth - 1, w->sy); + wp->flags &= ~PANE_HIDDEN; + continue; + } + + if (i > n) { + wp->flags |= PANE_HIDDEN; + continue; + } + wp->flags &= ~PANE_HIDDEN; + + wp->xoff = mainwidth; + wp->yoff = yoff; + if (i != n - 1) + window_pane_resize(wp, w->sx - mainwidth, height - 1); + else + window_pane_resize(wp, w->sx - mainwidth, height); + + i++; + yoff += height; + } + + /* Any space left? */ + while (yoff++ < w->sy) { + wp = TAILQ_LAST(&w->panes, window_panes); + while (wp != NULL && wp == TAILQ_FIRST(&w->panes)) + wp = TAILQ_PREV(wp, window_panes, entry); + if (wp == NULL) + break; + window_pane_resize(wp, wp->sx, wp->sy + 1); + } +} + +void +layout_main_h_refresh(struct window *w, int active_only) +{ + struct window_pane *wp; + u_int i, n, mainheight, width, xoff; + + if (active_only) + return; + + /* Get number of panes. */ + n = window_count_panes(w); + if (n == 0) + return; + + /* Get the main pane height and add one for separator line. */ + mainheight = options_get_number(&w->options, "main-pane-height") + 1; + + /* Need >1 pane and minimum rows; if fewer, display active only. */ + if (n == 1 || w->sy < mainheight + PANE_MINIMUM) { + layout_active_only_refresh(w, active_only); + return; + } + n--; + + /* How many can we fit, not including first? */ + if (w->sx / n < PANE_MINIMUM) { + width = PANE_MINIMUM; + n = w->sx / PANE_MINIMUM; + } else + width = w->sx / n; + + /* Fit the panes. */ + i = xoff = 0; + TAILQ_FOREACH(wp, &w->panes, entry) { + if (wp == TAILQ_FIRST(&w->panes)) { + wp->xoff = 0; + wp->yoff = 0; + window_pane_resize(wp, w->sx, mainheight - 1); + wp->flags &= ~PANE_HIDDEN; + continue; + } + + if (i > n) { + wp->flags |= PANE_HIDDEN; + continue; + } + wp->flags &= ~PANE_HIDDEN; + + wp->xoff = xoff; + wp->yoff = mainheight; + if (i != n - 1) + window_pane_resize(wp, width - 1, w->sy - mainheight); + else + window_pane_resize(wp, width - 1, w->sy - mainheight); + + i++; + xoff += width; + } + + /* Any space left? */ + while (xoff++ < w->sx + 1) { + wp = TAILQ_LAST(&w->panes, window_panes); + while (wp != NULL && wp == TAILQ_FIRST(&w->panes)) + wp = TAILQ_PREV(wp, window_panes, entry); + if (wp == NULL) + break; + window_pane_resize(wp, wp->sx + 1, wp->sy); + } +} diff --git a/usr.bin/tmux/log.c b/usr.bin/tmux/log.c new file mode 100644 index 00000000000..c0105e4d53c --- /dev/null +++ b/usr.bin/tmux/log.c @@ -0,0 +1,250 @@ +/* $OpenBSD: log.c,v 1.1 2009/06/01 22:58:49 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 <errno.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <syslog.h> +#include <time.h> + +#include "tmux.h" + +/* Logging type. */ +#define LOG_TYPE_OFF 0 +#define LOG_TYPE_SYSLOG 1 +#define LOG_TYPE_TTY 2 +#define LOG_TYPE_FILE 3 +int log_type = LOG_TYPE_OFF; + +/* Log file, if needed. */ +FILE *log_file; + +/* Debug level. */ +int log_level; + +/* Open logging to syslog. */ +void +log_open_syslog(int level) +{ + log_type = LOG_TYPE_SYSLOG; + log_level = level; + + openlog(__progname, LOG_PID|LOG_NDELAY, LOG_FACILITY); + + tzset(); +} + +/* Open logging to tty. */ +void +log_open_tty(int level) +{ + log_type = LOG_TYPE_TTY; + log_level = level; + + setlinebuf(stderr); + setlinebuf(stdout); + + tzset(); +} + +/* Open logging to file. */ +void +log_open_file(int level, const char *path) +{ + log_file = fopen(path, "w"); + if (log_file == NULL) + return; + + log_type = LOG_TYPE_FILE; + log_level = level; + + setlinebuf(log_file); + + tzset(); +} + +/* Close logging. */ +void +log_close(void) +{ + if (log_type == LOG_TYPE_FILE) + fclose(log_file); + + log_type = LOG_TYPE_OFF; +} + +/* Write a log message. */ +void +log_write(int pri, const char *msg, ...) +{ + va_list ap; + + va_start(ap, msg); + log_vwrite(pri, msg, ap); + va_end(ap); +} + +/* Write a log message. */ +void +log_vwrite(int pri, const char *msg, va_list ap) +{ + char *fmt; + FILE *f = log_file; + + switch (log_type) { + case LOG_TYPE_SYSLOG: + vsyslog(pri, msg, ap); + break; + case LOG_TYPE_TTY: + if (pri == LOG_INFO) + f = stdout; + else + f = stderr; + /* FALLTHROUGH */ + case LOG_TYPE_FILE: + if (asprintf(&fmt, "%s\n", msg) == -1) + exit(1); + if (vfprintf(f, fmt, ap) == -1) + exit(1); + fflush(f); + free(fmt); + break; + } +} + +/* Log a warning with error string. */ +void printflike1 +log_warn(const char *msg, ...) +{ + va_list ap; + char *fmt; + + va_start(ap, msg); + if (asprintf(&fmt, "%s: %s", msg, strerror(errno)) == -1) + exit(1); + log_vwrite(LOG_CRIT, fmt, ap); + free(fmt); + va_end(ap); +} + +/* Log a warning. */ +void printflike1 +log_warnx(const char *msg, ...) +{ + va_list ap; + + va_start(ap, msg); + log_vwrite(LOG_CRIT, msg, ap); + va_end(ap); +} + +/* Log an informational message. */ +void printflike1 +log_info(const char *msg, ...) +{ + va_list ap; + + if (log_level > -1) { + va_start(ap, msg); + log_vwrite(LOG_INFO, msg, ap); + va_end(ap); + } +} + +/* Log a debug message. */ +void printflike1 +log_debug(const char *msg, ...) +{ + va_list ap; + + if (log_level > 0) { + va_start(ap, msg); + log_vwrite(LOG_DEBUG, msg, ap); + va_end(ap); + } +} + +/* Log a debug message at level 2. */ +void printflike1 +log_debug2(const char *msg, ...) +{ + va_list ap; + + if (log_level > 1) { + va_start(ap, msg); + log_vwrite(LOG_DEBUG, msg, ap); + va_end(ap); + } +} + +/* Log a debug message at level 3. */ +void printflike1 +log_debug3(const char *msg, ...) +{ + va_list ap; + + if (log_level > 2) { + va_start(ap, msg); + log_vwrite(LOG_DEBUG, msg, ap); + va_end(ap); + } +} + +/* Log a critical error, with error string if necessary, and die. */ +__dead void +log_vfatal(const char *msg, va_list ap) +{ + char *fmt; + + if (errno != 0) { + if (asprintf(&fmt, "fatal: %s: %s", msg, strerror(errno)) == -1) + exit(1); + log_vwrite(LOG_CRIT, fmt, ap); + } else { + if (asprintf(&fmt, "fatal: %s", msg) == -1) + exit(1); + log_vwrite(LOG_CRIT, fmt, ap); + } + free(fmt); + + exit(1); +} + +/* Log a critical error, with error string, and die. */ +__dead void printflike1 +log_fatal(const char *msg, ...) +{ + va_list ap; + + va_start(ap, msg); + log_vfatal(msg, ap); +} + +/* Log a critical error and die. */ +__dead void printflike1 +log_fatalx(const char *msg, ...) +{ + va_list ap; + + errno = 0; + va_start(ap, msg); + log_vfatal(msg, ap); +} diff --git a/usr.bin/tmux/mode-key.c b/usr.bin/tmux/mode-key.c new file mode 100644 index 00000000000..e89e52d5572 --- /dev/null +++ b/usr.bin/tmux/mode-key.c @@ -0,0 +1,210 @@ +/* $OpenBSD: mode-key.c,v 1.1 2009/06/01 22:58:49 nicm Exp $ */ + +/* + * Copyright (c) 2008 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 "tmux.h" + +enum mode_key_cmd mode_key_lookup_vi(struct mode_key_data *, int); +enum mode_key_cmd mode_key_lookup_emacs(struct mode_key_data *, int); + +void +mode_key_init(struct mode_key_data *mdata, int type, int flags) +{ + mdata->type = type; + + if (flags & MODEKEY_CANEDIT) + flags |= MODEKEY_EDITMODE; + mdata->flags = flags; +} + +void +mode_key_free(unused struct mode_key_data *mdata) +{ +} + +enum mode_key_cmd +mode_key_lookup(struct mode_key_data *mdata, int key) +{ + switch (mdata->type) { + case MODEKEY_VI: + return (mode_key_lookup_vi(mdata, key)); + case MODEKEY_EMACS: + return (mode_key_lookup_emacs(mdata, key)); + default: + fatalx("unknown mode key type"); + } +} + +enum mode_key_cmd +mode_key_lookup_vi(struct mode_key_data *mdata, int key) +{ + if (KEYC_ISESC(key)) { + key = KEYC_REMOVEESC(key); + if (mdata->flags & MODEKEY_CANEDIT) + mdata->flags ^= MODEKEY_EDITMODE; + } + + + if (mdata->flags & MODEKEY_EDITMODE) { + switch (key) { + case '\003': + return (MODEKEYCMD_QUIT); + case '\033': + if (mdata->flags & MODEKEY_CANEDIT) + mdata->flags &= ~MODEKEY_EDITMODE; + return (MODEKEYCMD_NONE); + case '\010': + case '\177': + return (MODEKEYCMD_BACKSPACE); + case '\011': + return (MODEKEYCMD_COMPLETE); + case KEYC_DC: + return (MODEKEYCMD_DELETE); + case '\r': + return (MODEKEYCMD_CHOOSE); + } + return (MODEKEYCMD_OTHERKEY); + } + + switch (key) { + case '\010': + case '\177': + return (MODEKEYCMD_LEFT); + case KEYC_DC: + return (MODEKEYCMD_DELETE); + case '\011': + return (MODEKEYCMD_COMPLETE); + case 'i': + if (mdata->flags & MODEKEY_CANEDIT) + mdata->flags |= MODEKEY_EDITMODE; + break; + case 'a': + if (mdata->flags & MODEKEY_CANEDIT) { + mdata->flags |= MODEKEY_EDITMODE; + return (MODEKEYCMD_RIGHT); + } + break; + case '\r': + if (mdata->flags & (MODEKEY_CANEDIT|MODEKEY_CHOOSEMODE)) + return (MODEKEYCMD_CHOOSE); + return (MODEKEYCMD_COPYSELECTION); + case '0': + case '^': + return (MODEKEYCMD_STARTOFLINE); + case '\033': + return (MODEKEYCMD_CLEARSELECTION); + case 'j': + case KEYC_DOWN: + return (MODEKEYCMD_DOWN); + case '$': + return (MODEKEYCMD_ENDOFLINE); + case 'h': + case KEYC_LEFT: + return (MODEKEYCMD_LEFT); + case '\006': + case KEYC_NPAGE: + return (MODEKEYCMD_NEXTPAGE); + case 'w': + return (MODEKEYCMD_NEXTWORD); + case '\025': + case KEYC_PPAGE: + return (MODEKEYCMD_PREVIOUSPAGE); + case 'b': + return (MODEKEYCMD_PREVIOUSWORD); + case 'q': + case '\003': + return (MODEKEYCMD_QUIT); + case 'l': + case KEYC_RIGHT: + return (MODEKEYCMD_RIGHT); + case ' ': + return (MODEKEYCMD_STARTSELECTION); + case 'k': + case KEYC_UP: + return (MODEKEYCMD_UP); + case 'p': + return (MODEKEYCMD_PASTE); + } + + return (MODEKEYCMD_NONE); +} + +enum mode_key_cmd +mode_key_lookup_emacs(struct mode_key_data *mdata, int key) +{ + switch (key) { + case '\010': + case '\177': + return (MODEKEYCMD_BACKSPACE); + case KEYC_DC: + return (MODEKEYCMD_DELETE); + case '\011': + return (MODEKEYCMD_COMPLETE); + case '\r': + return (MODEKEYCMD_CHOOSE); + case '\001': + return (MODEKEYCMD_STARTOFLINE); + case '\007': + return (MODEKEYCMD_CLEARSELECTION); + case '\027': + case KEYC_ADDESC('w'): + return (MODEKEYCMD_COPYSELECTION); + case '\016': + case KEYC_DOWN: + return (MODEKEYCMD_DOWN); + case '\005': + return (MODEKEYCMD_ENDOFLINE); + case '\002': + case KEYC_LEFT: + return (MODEKEYCMD_LEFT); + case ' ': + if (mdata->flags & MODEKEY_CANEDIT) + break; + /* FALLTHROUGH */ + case '\026': + case KEYC_NPAGE: + return (MODEKEYCMD_NEXTPAGE); + case KEYC_ADDESC('f'): + return (MODEKEYCMD_NEXTWORD); + case '\031': + return (MODEKEYCMD_PASTE); + case KEYC_ADDESC('v'): + case KEYC_PPAGE: + return (MODEKEYCMD_PREVIOUSPAGE); + case KEYC_ADDESC('b'): + return (MODEKEYCMD_PREVIOUSWORD); + case '\006': + case KEYC_RIGHT: + return (MODEKEYCMD_RIGHT); + case '\000': + return (MODEKEYCMD_STARTSELECTION); + case '\020': + case KEYC_UP: + return (MODEKEYCMD_UP); + case 'q': + if (mdata->flags & MODEKEY_CANEDIT) + break; + /* FALLTHROUGH */ + case '\003': + case '\033': + return (MODEKEYCMD_QUIT); + } + + return (MODEKEYCMD_OTHERKEY); +} diff --git a/usr.bin/tmux/names.c b/usr.bin/tmux/names.c new file mode 100644 index 00000000000..0e181629647 --- /dev/null +++ b/usr.bin/tmux/names.c @@ -0,0 +1,110 @@ +/* $OpenBSD: names.c,v 1.1 2009/06/01 22:58:49 nicm 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 <ctype.h> +#include <libgen.h> +#include <string.h> +#include <unistd.h> + +#include "tmux.h" + +char *parse_window_name(const char *); + +void +set_window_names(void) +{ + struct window *w; + u_int i; + char *name, *wname; + struct timeval tv, tv2; + + if (gettimeofday(&tv, NULL) != 0) + fatal("gettimeofday"); + + for (i = 0; i < ARRAY_LENGTH(&windows); i++) { + w = ARRAY_ITEM(&windows, i); + if (w == NULL || w->active == NULL) + continue; + if (!options_get_number(&w->options, "automatic-rename")) + continue; + + if (timercmp(&tv, &w->name_timer, <)) + continue; + memcpy(&w->name_timer, &tv, sizeof w->name_timer); + tv2.tv_sec = 0; + tv2.tv_usec = NAME_INTERVAL * 1000L; + timeradd(&w->name_timer, &tv2, &w->name_timer); + + if (w->active->screen != &w->active->base) + name = NULL; + else + name = get_proc_name(w->active->fd, w->active->tty); + if (name == NULL) + wname = default_window_name(w); + else { + wname = parse_window_name(name); + xfree(name); + } + + if (strcmp(wname, w->name) == 0) + xfree(wname); + else { + xfree(w->name); + w->name = wname; + server_status_window(w); + } + } +} + +char * +default_window_name(struct window *w) +{ + if (w->active->screen != &w->active->base) + return (xstrdup("[tmux]")); + return (parse_window_name(w->active->cmd)); +} + +char * +parse_window_name(const char *in) +{ + char *copy, *name, *ptr; + + name = copy = xstrdup(in); + if (strncmp(name, "exec ", (sizeof "exec ") - 1) == 0) + name = name + (sizeof "exec ") - 1; + + while (*name == ' ') + name++; + if ((ptr = strchr(name, ' ')) != NULL) + *ptr = '\0'; + + if (*name != '\0') { + ptr = name + strlen(name) - 1; + while (ptr > name && !isalnum(*ptr)) + *ptr-- = '\0'; + } + + if (*name == '/') + name = basename(name); + name = xstrdup(name); + xfree(copy); + return (name); +} + diff --git a/usr.bin/tmux/options-cmd.c b/usr.bin/tmux/options-cmd.c new file mode 100644 index 00000000000..4bc727316ab --- /dev/null +++ b/usr.bin/tmux/options-cmd.c @@ -0,0 +1,182 @@ +/* $OpenBSD: options-cmd.c,v 1.1 2009/06/01 22:58:49 nicm Exp $ */ + +/* + * Copyright (c) 2008 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 <stdlib.h> +#include <string.h> + +#include "tmux.h" + +void +set_option_string(struct cmd_ctx *ctx, struct options *oo, + const struct set_option_entry *entry, char *value) +{ + if (value == NULL) { + ctx->error(ctx, "empty value"); + return; + } + + options_set_string(oo, entry->name, "%s", value); + ctx->info(ctx, "set option: %s -> %s", entry->name, value); +} + +void +set_option_number(struct cmd_ctx *ctx, struct options *oo, + const struct set_option_entry *entry, char *value) +{ + long long number; + const char *errstr; + + if (value == NULL) { + ctx->error(ctx, "empty value"); + return; + } + + number = strtonum(value, entry->minimum, entry->maximum, &errstr); + if (errstr != NULL) { + ctx->error(ctx, "value is %s: %s", errstr, value); + return; + } + options_set_number(oo, entry->name, number); + ctx->info(ctx, "set option: %s -> %lld", entry->name, number); +} + +void +set_option_key(struct cmd_ctx *ctx, struct options *oo, + const struct set_option_entry *entry, char *value) +{ + int key; + + if (value == NULL) { + ctx->error(ctx, "empty value"); + return; + } + + if ((key = key_string_lookup_string(value)) == KEYC_NONE) { + ctx->error(ctx, "unknown key: %s", value); + return; + } + options_set_number(oo, entry->name, key); + ctx->info(ctx, + "set option: %s -> %s", entry->name, key_string_lookup_key(key)); +} + +void +set_option_colour(struct cmd_ctx *ctx, struct options *oo, + const struct set_option_entry *entry, char *value) +{ + int colour; + + if (value == NULL) { + ctx->error(ctx, "empty value"); + return; + } + + if ((colour = colour_fromstring(value)) == -1) { + ctx->error(ctx, "bad colour: %s", value); + return; + } + + options_set_number(oo, entry->name, colour); + ctx->info(ctx, + "set option: %s -> %s", entry->name, colour_tostring(colour)); +} + +void +set_option_attributes(struct cmd_ctx *ctx, struct options *oo, + const struct set_option_entry *entry, char *value) +{ + int attr; + + if (value == NULL) { + ctx->error(ctx, "empty value"); + return; + } + + if ((attr = attributes_fromstring(value)) == -1) { + ctx->error(ctx, "bad attributes: %s", value); + return; + } + + options_set_number(oo, entry->name, attr); + ctx->info(ctx, + "set option: %s -> %s", entry->name, attributes_tostring(attr)); +} + +void +set_option_flag(struct cmd_ctx *ctx, struct options *oo, + const struct set_option_entry *entry, char *value) +{ + int flag; + + if (value == NULL || *value == '\0') + flag = !options_get_number(oo, entry->name); + else { + if ((value[0] == '1' && value[1] == '\0') || + strcasecmp(value, "on") == 0 || + strcasecmp(value, "yes") == 0) + flag = 1; + else if ((value[0] == '0' && value[1] == '\0') || + strcasecmp(value, "off") == 0 || + strcasecmp(value, "no") == 0) + flag = 0; + else { + ctx->error(ctx, "bad value: %s", value); + return; + } + } + + options_set_number(oo, entry->name, flag); + ctx->info(ctx, + "set option: %s -> %s", entry->name, flag ? "on" : "off"); +} + +void +set_option_choice(struct cmd_ctx *ctx, struct options *oo, + const struct set_option_entry *entry, char *value) +{ + const char **choicep; + int n, choice = -1; + + if (value == NULL) { + ctx->error(ctx, "empty value"); + return; + } + + n = 0; + for (choicep = entry->choices; *choicep != NULL; choicep++) { + n++; + if (strncmp(*choicep, value, strlen(value)) != 0) + continue; + + if (choice != -1) { + ctx->error(ctx, "ambiguous option: %s", value); + return; + } + choice = n - 1; + } + if (choice == -1) { + ctx->error(ctx, "unknown option: %s", value); + return; + } + + options_set_number(oo, entry->name, choice); + ctx->info(ctx, + "set option: %s -> %s", entry->name, entry->choices[choice]); +} diff --git a/usr.bin/tmux/options.c b/usr.bin/tmux/options.c new file mode 100644 index 00000000000..11f653481aa --- /dev/null +++ b/usr.bin/tmux/options.c @@ -0,0 +1,160 @@ +/* $OpenBSD: options.c,v 1.1 2009/06/01 22:58:49 nicm Exp $ */ + +/* + * Copyright (c) 2008 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 <stdarg.h> +#include <string.h> + +#include "tmux.h" + +/* + * Option handling; each option has a name, type and value and is stored in + * a splay tree. + */ + +SPLAY_GENERATE(options_tree, options_entry, entry, options_cmp); + +int +options_cmp(struct options_entry *o1, struct options_entry *o2) +{ + return (strcmp(o1->name, o2->name)); +} + +void +options_init(struct options *oo, struct options *parent) +{ + SPLAY_INIT(&oo->tree); + oo->parent = parent; +} + +void +options_free(struct options *oo) +{ + struct options_entry *o; + + while (!SPLAY_EMPTY(&oo->tree)) { + o = SPLAY_ROOT(&oo->tree); + SPLAY_REMOVE(options_tree, &oo->tree, o); + xfree(o->name); + if (o->type == OPTIONS_STRING) + xfree(o->value.string); + xfree(o); + } +} + +struct options_entry * +options_find1(struct options *oo, const char *name) +{ + struct options_entry p; + + p.name = (char *) name; + return (SPLAY_FIND(options_tree, &oo->tree, &p)); +} + +struct options_entry * +options_find(struct options *oo, const char *name) +{ + struct options_entry *o, p; + + p.name = (char *) name; + o = SPLAY_FIND(options_tree, &oo->tree, &p); + while (o == NULL) { + oo = oo->parent; + if (oo == NULL) + break; + o = SPLAY_FIND(options_tree, &oo->tree, &p); + } + return (o); +} + +int +options_remove(struct options *oo, const char *name) +{ + struct options_entry *o; + + if ((o = options_find1(oo, name)) == NULL) + return (-1); + + SPLAY_REMOVE(options_tree, &oo->tree, o); + xfree(o->name); + if (o->type == OPTIONS_STRING) + xfree(o->value.string); + xfree(o); + return (0); +} + +void printflike3 +options_set_string(struct options *oo, const char *name, const char *fmt, ...) +{ + struct options_entry *o; + va_list ap; + + if ((o = options_find1(oo, name)) == NULL) { + o = xmalloc(sizeof *o); + o->name = xstrdup(name); + SPLAY_INSERT(options_tree, &oo->tree, o); + } else if (o->type == OPTIONS_STRING) + xfree(o->value.string); + + va_start(ap, fmt); + o->type = OPTIONS_STRING; + xvasprintf(&o->value.string, fmt, ap); + va_end(ap); +} + +char * +options_get_string(struct options *oo, const char *name) +{ + struct options_entry *o; + + if ((o = options_find(oo, name)) == NULL) + fatalx("missing option"); + if (o->type != OPTIONS_STRING) + fatalx("option not a string"); + return (o->value.string); +} + +void +options_set_number(struct options *oo, const char *name, long long value) +{ + struct options_entry *o; + + if ((o = options_find1(oo, name)) == NULL) { + o = xmalloc(sizeof *o); + o->name = xstrdup(name); + SPLAY_INSERT(options_tree, &oo->tree, o); + } else if (o->type == OPTIONS_STRING) + xfree(o->value.string); + + o->type = OPTIONS_NUMBER; + o->value.number = value; + +} + +long long +options_get_number(struct options *oo, const char *name) +{ + struct options_entry *o; + + if ((o = options_find(oo, name)) == NULL) + fatalx("missing option"); + if (o->type != OPTIONS_NUMBER) + fatalx("option not a number"); + return (o->value.number); +} diff --git a/usr.bin/tmux/paste.c b/usr.bin/tmux/paste.c new file mode 100644 index 00000000000..2f091406e4c --- /dev/null +++ b/usr.bin/tmux/paste.c @@ -0,0 +1,131 @@ +/* $OpenBSD: paste.c,v 1.1 2009/06/01 22:58:49 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/time.h> + +#include <string.h> + +#include "tmux.h" + +void +paste_init_stack(struct paste_stack *ps) +{ + ARRAY_INIT(ps); +} + +void +paste_free_stack(struct paste_stack *ps) +{ + while (paste_free_top(ps) == 0) + ; +} + +struct paste_buffer * +paste_walk_stack(struct paste_stack *ps, uint *idx) +{ + struct paste_buffer *pb; + + pb = paste_get_index(ps, *idx); + (*idx)++; + return (pb); +} + +struct paste_buffer * +paste_get_top(struct paste_stack *ps) +{ + if (ARRAY_LENGTH(ps) == 0) + return (NULL); + return (ARRAY_FIRST(ps)); +} + +struct paste_buffer * +paste_get_index(struct paste_stack *ps, u_int idx) +{ + if (idx >= ARRAY_LENGTH(ps)) + return (NULL); + return (ARRAY_ITEM(ps, idx)); +} + +int +paste_free_top(struct paste_stack *ps) +{ + struct paste_buffer *pb; + + if (ARRAY_LENGTH(ps) == 0) + return (-1); + + pb = ARRAY_FIRST(ps); + ARRAY_REMOVE(ps, 0); + + xfree(pb->data); + xfree(pb); + + return (0); +} + +int +paste_free_index(struct paste_stack *ps, u_int idx) +{ + struct paste_buffer *pb; + + if (idx >= ARRAY_LENGTH(ps)) + return (-1); + + pb = ARRAY_ITEM(ps, idx); + ARRAY_REMOVE(ps, idx); + + xfree(pb->data); + xfree(pb); + + return (0); +} + +void +paste_add(struct paste_stack *ps, char *data, u_int limit) +{ + struct paste_buffer *pb; + + while (ARRAY_LENGTH(ps) >= limit) + ARRAY_TRUNC(ps, 1); + + pb = xmalloc(sizeof *pb); + ARRAY_INSERT(ps, 0, pb); + + pb->data = data; + if (gettimeofday(&pb->tv, NULL) != 0) + fatal("gettimeofday"); +} + +int +paste_replace(struct paste_stack *ps, u_int idx, char *data) +{ + struct paste_buffer *pb; + + if (idx >= ARRAY_LENGTH(ps)) + return (-1); + + pb = ARRAY_ITEM(ps, idx); + xfree(pb->data); + + pb->data = data; + if (gettimeofday(&pb->tv, NULL) != 0) + fatal("gettimeofday"); + + return (0); +} diff --git a/usr.bin/tmux/procname.c b/usr.bin/tmux/procname.c new file mode 100644 index 00000000000..0eb398ee75d --- /dev/null +++ b/usr.bin/tmux/procname.c @@ -0,0 +1,130 @@ +/* $OpenBSD: procname.c,v 1.1 2009/06/01 22:58:49 nicm 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/param.h> +#include <sys/sysctl.h> +#include <sys/stat.h> + +#include <errno.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#define nitems(_a) (sizeof((_a)) / sizeof((_a)[0])) + +#define is_runnable(p) \ + ((p)->p_stat == SRUN || (p)->p_stat == SIDL || (p)->p_stat == SONPROC) +#define is_stopped(p) \ + ((p)->p_stat == SSTOP || (p)->p_stat == SZOMB || (p)->p_stat == SDEAD) + +char *get_proc_name(int, char *); + +char * +get_proc_name(int fd, char *tty) +{ + int mib[4] = { CTL_KERN, KERN_PROC, KERN_PROC_PGRP, 0 }; + struct stat sb; + size_t len; + struct kinfo_proc *buf, *newbuf; + struct proc *p, *bestp; + u_int i; + char *name; + + buf = NULL; + + if (stat(tty, &sb) == -1) + return (NULL); + if ((mib[3] = tcgetpgrp(fd)) == -1) + return (NULL); + +retry: + if (sysctl(mib, nitems(mib), NULL, &len, NULL, 0) == -1) + return (NULL); + len = (len * 5) / 4; + + if ((newbuf = realloc(buf, len)) == NULL) { + free(buf); + return (NULL); + } + buf = newbuf; + + if (sysctl(mib, nitems(mib), buf, &len, NULL, 0) == -1) { + if (errno == ENOMEM) + goto retry; + free(buf); + return (NULL); + } + + bestp = NULL; + for (i = 0; i < len / sizeof (struct kinfo_proc); i++) { + if (buf[i].kp_eproc.e_tdev != sb.st_rdev) + continue; + p = &buf[i].kp_proc; + if (bestp == NULL) { + bestp = p; + continue; + } + + if (is_runnable(p) && !is_runnable(bestp)) + bestp = p; + else if (!is_runnable(p) && is_runnable(bestp)) + continue; + + if (!is_stopped(p) && is_stopped(bestp)) + bestp = p; + else if (is_stopped(p) && !is_stopped(bestp)) + continue; + + if (p->p_estcpu > bestp->p_estcpu) + bestp = p; + else if (p->p_estcpu < bestp->p_estcpu) + continue; + + if (p->p_slptime < bestp->p_slptime) + bestp = p; + else if (p->p_slptime > bestp->p_slptime) + continue; + + if (p->p_flag & P_SINTR && !(bestp->p_flag & P_SINTR)) + bestp = p; + else if (!(p->p_flag & P_SINTR) && bestp->p_flag & P_SINTR) + continue; + + if (LIST_FIRST(&p->p_children) == NULL && + LIST_FIRST(&bestp->p_children) != NULL) /* XXX ugh */ + bestp = p; + else if (LIST_FIRST(&p->p_children) != NULL && + LIST_FIRST(&bestp->p_children) == NULL) + continue; + + if (strcmp(p->p_comm, p->p_comm) < 0) + bestp = p; + else if (strcmp(p->p_comm, p->p_comm) > 0) + continue; + + if (p->p_pid > bestp->p_pid) + bestp = p; + } + + name = NULL; + if (bestp != NULL) + name = strdup(bestp->p_comm); + + free(buf); + return (name); +} diff --git a/usr.bin/tmux/resize.c b/usr.bin/tmux/resize.c new file mode 100644 index 00000000000..e272f0fab28 --- /dev/null +++ b/usr.bin/tmux/resize.c @@ -0,0 +1,138 @@ +/* $OpenBSD: resize.c,v 1.1 2009/06/01 22:58:49 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 <string.h> + +#include "tmux.h" + +/* + * Recalculate window and session sizes. + * + * Every session has the size of the smallest client it is attached to and + * every window the size of the smallest session it is attached to. + * + * So, when a client is resized or a session attached to or detached from a + * client, the window sizes must be recalculated. For each session, find the + * smallest client it is attached to, and resize it to that size. Then for + * every window, find the smallest session it is attached to, resize it to that + * size and clear and redraw every client with it as the current window. + * + * This is quite inefficient - better/additional data structures are needed + * to make it better. + * + * As a side effect, this function updates the SESSION_UNATTACHED flag. This + * flag is necessary to make sure unattached sessions do not limit the size of + * windows that are attached both to them and to other (attached) sessions. + */ + +void +recalculate_sizes(void) +{ + struct session *s; + struct client *c; + struct window *w; + u_int i, j, ssx, ssy, has, limit; + int flag; + + for (i = 0; i < ARRAY_LENGTH(&sessions); i++) { + s = ARRAY_ITEM(&sessions, i); + if (s == NULL) + continue; + + ssx = ssy = UINT_MAX; + for (j = 0; j < ARRAY_LENGTH(&clients); j++) { + c = ARRAY_ITEM(&clients, j); + if (c == NULL) + continue; + if (c->session == s) { + if (c->tty.sx < ssx) + ssx = c->tty.sx; + if (c->tty.sy < ssy) + ssy = c->tty.sy; + } + } + if (ssx == UINT_MAX || ssy == UINT_MAX) { + s->flags |= SESSION_UNATTACHED; + continue; + } + s->flags &= ~SESSION_UNATTACHED; + + if (options_get_number(&s->options, "status")) { + if (ssy == 0) + ssy = 1; + else + ssy--; + } + if (s->sx == ssx && s->sy == ssy) + continue; + + log_debug( + "session size %u,%u (was %u,%u)", ssx, ssy, s->sx, s->sy); + + s->sx = ssx; + s->sy = ssy; + } + + for (i = 0; i < ARRAY_LENGTH(&windows); i++) { + w = ARRAY_ITEM(&windows, i); + if (w == NULL) + continue; + flag = options_get_number(&w->options, "aggressive-resize"); + + ssx = ssy = UINT_MAX; + for (j = 0; j < ARRAY_LENGTH(&sessions); j++) { + s = ARRAY_ITEM(&sessions, j); + if (s == NULL || s->flags & SESSION_UNATTACHED) + continue; + if (flag) + has = s->curw->window == w; + else + has = session_has(s, w); + if (has) { + if (s->sx < ssx) + ssx = s->sx; + if (s->sy < ssy) + ssy = s->sy; + } + } + if (ssx == UINT_MAX || ssy == UINT_MAX) { + w->flags |= WINDOW_HIDDEN; + continue; + } + w->flags &= ~WINDOW_HIDDEN; + + limit = options_get_number(&w->options, "force-width"); + if (limit != 0 && ssx > limit) + ssx = limit; + limit = options_get_number(&w->options, "force-height"); + if (limit != 0 && ssy > limit) + ssy = limit; + + if (w->sx == ssx && w->sy == ssy) + continue; + + log_debug( + "window size %u,%u (was %u,%u)", ssx, ssy, w->sx, w->sy); + + window_resize(w, ssx, ssy); + server_redraw_window(w); + layout_refresh(w, 0); + } +} diff --git a/usr.bin/tmux/screen-redraw.c b/usr.bin/tmux/screen-redraw.c new file mode 100644 index 00000000000..4f6ee46a3d5 --- /dev/null +++ b/usr.bin/tmux/screen-redraw.c @@ -0,0 +1,171 @@ +/* $OpenBSD: screen-redraw.c,v 1.1 2009/06/01 22:58:49 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 <string.h> + +#include "tmux.h" + +int screen_redraw_check_cell(struct client *, u_int, u_int); + +/* Check if cell inside a pane. */ +int +screen_redraw_check_cell(struct client *c, u_int px, u_int py) +{ + struct window *w = c->session->curw->window; + struct window_pane *wp; + + if (px > w->sx || py > w->sy) + return (0); + + TAILQ_FOREACH(wp, &w->panes, entry) { + /* Inside pane. */ + if (px >= wp->xoff && px < wp->xoff + wp->sx && + py >= wp->yoff && py < wp->yoff + wp->sy) + return (1); + + /* Left/right borders. */ + if (py >= wp->yoff && py < wp->yoff + wp->sy) { + if (wp->xoff != 0 && px == wp->xoff - 1) + return (1); + if (px == wp->xoff + wp->sx) + return (1); + } + + /* Top/bottom borders. */ + if (px >= wp->xoff && px < wp->xoff + wp->sx) { + if (wp->yoff != 0 && py == wp->yoff - 1) + return (1); + if (py == wp->yoff + wp->sy) + return (1); + } + } + + return (0); +} + +/* Redraw entire screen.. */ +void +screen_redraw_screen(struct client *c) +{ + struct window *w = c->session->curw->window; + struct tty *tty = &c->tty; + struct window_pane *wp; + struct screen *s; + u_int i, j, sx, sy; + int status, has_acs; + u_char choriz, cvert, cbackg; + + /* Get status line, er, status. */ + status = options_get_number(&c->session->options, "status"); + + /* Work out ACS characters. */ + if (tty_term_has(tty->term, TTYC_ACSC)) { + has_acs = 1; + choriz = tty_get_acs(tty, 'q'); + cvert = tty_get_acs(tty, 'x'); + cbackg = tty_get_acs(tty, '~'); + } else { + has_acs = 0; + choriz = '-'; + cvert = '|'; + cbackg = '.'; + } + + /* Clear the screen. */ + tty_reset(tty); + if (has_acs) + tty_putcode(tty, TTYC_SMACS); + for (j = 0; j < tty->sy - status; j++) { + for (i = 0; i < tty->sx; i++) { + if (!screen_redraw_check_cell(c, i, j)) { + tty_cursor(tty, i, j, 0, 0); + tty_putc(tty, cbackg); + } + } + } + if (has_acs) + tty_putcode(tty, TTYC_RMACS); + + /* Draw the panes. */ + TAILQ_FOREACH(wp, &w->panes, entry) { + if (wp->flags & PANE_HIDDEN) + continue; + + tty_reset(tty); + + s = wp->screen; + sx = wp->sx; + sy = wp->sy; + + /* Draw left and right borders. */ + if (has_acs) + tty_putcode(tty, TTYC_SMACS); + if (wp->xoff > 0) { + for (i = wp->yoff; i < wp->yoff + sy; i++) { + tty_cursor(tty, wp->xoff - 1, i, 0, 0); + tty_putc(tty, cvert); + } + } + if (wp->xoff + sx < tty->sx) { + for (i = wp->yoff; i < wp->yoff + sy; i++) { + tty_cursor(tty, wp->xoff + sx, i, 0, 0); + tty_putc(&c->tty, cvert); + } + } + + /* Draw top and bottom borders. */ + if (wp->yoff > 0) { + tty_cursor(tty, wp->xoff, wp->yoff - 1, 0, 0); + for (i = 0; i < sx; i++) + tty_putc(tty, choriz); + } + if (wp->yoff + sy < tty->sy - status) { + tty_cursor(tty, wp->xoff, wp->yoff + sy, 0, 0); + for (i = 0; i < sx; i++) + tty_putc(tty, choriz); + } + if (has_acs) + tty_putcode(tty, TTYC_RMACS); + + /* Draw the pane. */ + screen_redraw_pane(c, wp); + } + + /* Draw the status line. */ + screen_redraw_status(c); +} + +/* Draw a single pane. */ +void +screen_redraw_pane(struct client *c, struct window_pane *wp) +{ + u_int i; + + for (i = 0; i < wp->sy; i++) + tty_draw_line(&c->tty, wp->screen, i, wp->xoff, wp->yoff); +} + + +/* Draw the status line. */ +void +screen_redraw_status(struct client *c) +{ + tty_draw_line(&c->tty, &c->status, 0, 0, c->tty.sy - 1); +} diff --git a/usr.bin/tmux/screen-write.c b/usr.bin/tmux/screen-write.c new file mode 100644 index 00000000000..30a024272b6 --- /dev/null +++ b/usr.bin/tmux/screen-write.c @@ -0,0 +1,684 @@ +/* $OpenBSD: screen-write.c,v 1.1 2009/06/01 22:58:49 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 <string.h> + +#include "tmux.h" + +void screen_write_save(struct screen_write_ctx *); +void screen_write_overwrite(struct screen_write_ctx *); + +/* Initialise writing with a window. */ +void +screen_write_start( + struct screen_write_ctx *ctx, struct window_pane *wp, struct screen *s) +{ + ctx->wp = wp; + if (wp != NULL && s == NULL) + ctx->s = wp->screen; + else + ctx->s = s; +} + +/* Finish writing. */ +void +screen_write_stop(unused struct screen_write_ctx *ctx) +{ +} + +/* Write character. */ +void +screen_write_putc( + struct screen_write_ctx *ctx, struct grid_cell *gc, u_char ch) +{ + gc->data = ch; + screen_write_cell(ctx, gc, NULL); +} + +/* Write string. */ +void printflike3 +screen_write_puts( + struct screen_write_ctx *ctx, struct grid_cell *gc, const char *fmt, ...) +{ + va_list ap; + char *msg, *ptr; + + va_start(ap, fmt); + xvasprintf(&msg, fmt, ap); + va_end(ap); + + for (ptr = msg; *ptr != '\0'; ptr++) + screen_write_putc(ctx, gc, (u_char) *ptr); + + xfree(msg); +} + +/* Copy from another screen. */ +void +screen_write_copy(struct screen_write_ctx *ctx, + struct screen *src, u_int px, u_int py, u_int nx, u_int ny) +{ + struct screen *s = ctx->s; + struct grid *gd = src->grid; + const struct grid_cell *gc; + struct grid_utf8 *gu; + u_char *udata; + u_int xx, yy, cx, cy; + + cx = s->cx; + cy = s->cy; + for (yy = py; yy < py + ny; yy++) { + for (xx = px; xx < px + nx; xx++) { + if (xx >= gd->sx || yy >= gd->hsize + gd->sy) + gc = &grid_default_cell; + else + gc = grid_peek_cell(gd, xx, yy); + + udata = NULL; + if (gc->flags & GRID_FLAG_UTF8) { + gu = grid_get_utf8(gd, xx, yy); + udata = gu->data; + } + + screen_write_cell(ctx, gc, udata); + } + cy++; + screen_write_cursormove(ctx, cx, cy); + } +} + +/* Save cursor and region positions. */ +void +screen_write_save(struct screen_write_ctx *ctx) +{ + struct screen *s = ctx->s; + + s->old_cx = s->cx; + s->old_cy = s->cy; + + s->old_rlower = s->rlower; + s->old_rupper = s->rupper; +} + +/* Cursor up by ny. */ +void +screen_write_cursorup(struct screen_write_ctx *ctx, u_int ny) +{ + struct screen *s = ctx->s; + + if (ny == 0) + ny = 1; + + if (ny > s->cy) + ny = s->cy; + if (ny == 0) + return; + + s->cy -= ny; +} + +/* Cursor down by ny. */ +void +screen_write_cursordown(struct screen_write_ctx *ctx, u_int ny) +{ + struct screen *s = ctx->s; + + if (ny == 0) + ny = 1; + + if (ny > screen_size_y(s) - 1 - s->cy) + ny = screen_size_y(s) - 1 - s->cy; + if (ny == 0) + return; + + s->cy += ny; +} + +/* Cursor right by nx. */ +void +screen_write_cursorright(struct screen_write_ctx *ctx, u_int nx) +{ + struct screen *s = ctx->s; + + if (nx == 0) + nx = 1; + + if (nx > screen_size_x(s) - 1 - s->cx) + nx = screen_size_x(s) - 1 - s->cx; + if (nx == 0) + return; + + s->cx += nx; +} + +/* Cursor left by nx. */ +void +screen_write_cursorleft(struct screen_write_ctx *ctx, u_int nx) +{ + struct screen *s = ctx->s; + + if (nx == 0) + nx = 1; + + if (nx > s->cx) + nx = s->cx; + if (nx == 0) + return; + + s->cx -= nx; +} + +/* Insert nx characters. */ +void +screen_write_insertcharacter(struct screen_write_ctx *ctx, u_int nx) +{ + struct screen *s = ctx->s; + + if (nx == 0) + nx = 1; + + if (nx > screen_size_x(s) - 1 - s->cx) + nx = screen_size_x(s) - 1 - s->cx; + if (nx == 0) + return; + + screen_write_save(ctx); + + if (s->cx <= screen_size_x(s) - 1) + grid_view_insert_cells(s->grid, s->cx, s->cy, nx); + + tty_write_cmd(ctx->wp, TTY_INSERTCHARACTER, nx); +} + +/* Delete nx characters. */ +void +screen_write_deletecharacter(struct screen_write_ctx *ctx, u_int nx) +{ + struct screen *s = ctx->s; + + if (nx == 0) + nx = 1; + + if (nx > screen_size_x(s) - 1 - s->cx) + nx = screen_size_x(s) - 1 - s->cx; + if (nx == 0) + return; + + screen_write_save(ctx); + + if (s->cx <= screen_size_x(s) - 1) + grid_view_delete_cells(s->grid, s->cx, s->cy, nx); + + tty_write_cmd(ctx->wp, TTY_DELETECHARACTER, nx); +} + +/* Insert ny lines. */ +void +screen_write_insertline(struct screen_write_ctx *ctx, u_int ny) +{ + struct screen *s = ctx->s; + + if (ny == 0) + ny = 1; + + if (ny > screen_size_y(s) - 1 - s->cy) + ny = screen_size_y(s) - 1 - s->cy; + if (ny == 0) + return; + + screen_write_save(ctx); + + if (s->cy < s->rupper || s->cy > s->rlower) + grid_view_insert_lines(s->grid, s->cy, ny); + else { + grid_view_insert_lines_region( + s->grid, s->rupper, s->rlower, s->cy, ny); + } + + tty_write_cmd(ctx->wp, TTY_INSERTLINE, ny); +} + +/* Delete ny lines. */ +void +screen_write_deleteline(struct screen_write_ctx *ctx, u_int ny) +{ + struct screen *s = ctx->s; + + if (ny == 0) + ny = 1; + + if (ny > screen_size_y(s) - 1 - s->cy) + ny = screen_size_y(s) - 1 - s->cy; + if (ny == 0) + return; + + screen_write_save(ctx); + + if (s->cy < s->rupper || s->cy > s->rlower) + grid_view_delete_lines(s->grid, s->cy, ny); + else { + grid_view_delete_lines_region( + s->grid, s->rupper, s->rlower, s->cy, ny); + } + + tty_write_cmd(ctx->wp, TTY_DELETELINE, ny); +} + +/* Clear line at cursor. */ +void +screen_write_clearline(struct screen_write_ctx *ctx) +{ + struct screen *s = ctx->s; + + screen_write_save(ctx); + + grid_view_clear(s->grid, 0, s->cy, screen_size_x(s), 1); + + tty_write_cmd(ctx->wp, TTY_CLEARLINE); +} + +/* Clear to end of line from cursor. */ +void +screen_write_clearendofline(struct screen_write_ctx *ctx) +{ + struct screen *s = ctx->s; + u_int sx; + + screen_write_save(ctx); + + sx = screen_size_x(s); + + if (s->cx <= sx - 1) + grid_view_clear(s->grid, s->cx, s->cy, sx - s->cx, 1); + + tty_write_cmd(ctx->wp, TTY_CLEARENDOFLINE); +} + +/* Clear to start of line from cursor. */ +void +screen_write_clearstartofline(struct screen_write_ctx *ctx) +{ + struct screen *s = ctx->s; + u_int sx; + + screen_write_save(ctx); + + sx = screen_size_x(s); + + if (s->cx > sx - 1) + grid_view_clear(s->grid, 0, s->cy, sx, 1); + else + grid_view_clear(s->grid, 0, s->cy, s->cx + 1, 1); + + tty_write_cmd(ctx->wp, TTY_CLEARSTARTOFLINE); +} + +/* Move cursor to px,py. */ +void +screen_write_cursormove(struct screen_write_ctx *ctx, u_int px, u_int py) +{ + struct screen *s = ctx->s; + + if (px > screen_size_x(s) - 1) + px = screen_size_x(s) - 1; + if (py > screen_size_y(s) - 1) + py = screen_size_y(s) - 1; + + s->cx = px; + s->cy = py; +} + +/* Set cursor mode. */ +void +screen_write_cursormode(struct screen_write_ctx *ctx, int state) +{ + struct screen *s = ctx->s; + + if (state) + s->mode |= MODE_CURSOR; + else + s->mode &= ~MODE_CURSOR; +} + +/* Reverse index (up with scroll). */ +void +screen_write_reverseindex(struct screen_write_ctx *ctx) +{ + struct screen *s = ctx->s; + + screen_write_save(ctx); + + if (s->cy == s->rupper) + grid_view_scroll_region_down(s->grid, s->rupper, s->rlower); + else if (s->cy > 0) + s->cy--; + + tty_write_cmd(ctx->wp, TTY_REVERSEINDEX); +} + +/* Set scroll region. */ +void +screen_write_scrollregion( + struct screen_write_ctx *ctx, u_int rupper, u_int rlower) +{ + struct screen *s = ctx->s; + + if (rupper > screen_size_y(s) - 1) + rupper = screen_size_y(s) - 1; + if (rlower > screen_size_y(s) - 1) + rlower = screen_size_y(s) - 1; + if (rupper > rlower) + return; + + /* Cursor moves to top-left. */ + s->cx = 0; + s->cy = 0; + + s->rupper = rupper; + s->rlower = rlower; +} + +/* Set insert mode. */ +void +screen_write_insertmode(struct screen_write_ctx *ctx, int state) +{ + struct screen *s = ctx->s; + + if (state) + s->mode |= MODE_INSERT; + else + s->mode &= ~MODE_INSERT; +} + +/* Set mouse mode. */ +void +screen_write_mousemode(struct screen_write_ctx *ctx, int state) +{ + struct screen *s = ctx->s; + + if (state) + s->mode |= MODE_MOUSE; + else + s->mode &= ~MODE_MOUSE; +} + +/* Line feed (down with scroll). */ +void +screen_write_linefeed(struct screen_write_ctx *ctx) +{ + struct screen *s = ctx->s; + + screen_write_save(ctx); + + if (s->cy == s->rlower) + grid_view_scroll_region_up(s->grid, s->rupper, s->rlower); + else if (s->cy < screen_size_y(s) - 1) + s->cy++; + + tty_write_cmd(ctx->wp, TTY_LINEFEED); +} + +/* Carriage return (cursor to start of line). */ +void +screen_write_carriagereturn(struct screen_write_ctx *ctx) +{ + struct screen *s = ctx->s; + + s->cx = 0; +} + +/* Set keypad cursor keys mode. */ +void +screen_write_kcursormode(struct screen_write_ctx *ctx, int state) +{ + struct screen *s = ctx->s; + + if (state) + s->mode |= MODE_KCURSOR; + else + s->mode &= ~MODE_KCURSOR; +} + +/* Set keypad number keys mode. */ +void +screen_write_kkeypadmode(struct screen_write_ctx *ctx, int state) +{ + struct screen *s = ctx->s; + + if (state) + s->mode |= MODE_KKEYPAD; + else + s->mode &= ~MODE_KKEYPAD; +} + +/* Clear to end of screen from cursor. */ +void +screen_write_clearendofscreen(struct screen_write_ctx *ctx) +{ + struct screen *s = ctx->s; + u_int sx, sy; + + screen_write_save(ctx); + + sx = screen_size_x(s); + sy = screen_size_y(s); + + if (s->cx <= sx - 1) + grid_view_clear(s->grid, s->cx, s->cy, sx - s->cx, 1); + grid_view_clear(s->grid, 0, s->cy + 1, sx, sy - (s->cy + 1)); + + tty_write_cmd(ctx->wp, TTY_CLEARENDOFSCREEN); +} + +/* Clear to start of screen. */ +void +screen_write_clearstartofscreen(struct screen_write_ctx *ctx) +{ + struct screen *s = ctx->s; + u_int sx; + + screen_write_save(ctx); + + sx = screen_size_x(s); + + if (s->cy > 0) + grid_view_clear(s->grid, 0, 0, sx, s->cy - 1); + if (s->cx > sx - 1) + grid_view_clear(s->grid, 0, s->cy, sx, 1); + else + grid_view_clear(s->grid, 0, s->cy, s->cx, 1); + + tty_write_cmd(ctx->wp, TTY_CLEARSTARTOFSCREEN); +} + +/* Clear entire screen. */ +void +screen_write_clearscreen(struct screen_write_ctx *ctx) +{ + struct screen *s = ctx->s; + + screen_write_save(ctx); + + grid_view_clear(s->grid, 0, 0, screen_size_x(s), screen_size_y(s)); + + tty_write_cmd(ctx->wp, TTY_CLEARSCREEN); +} + +/* Write cell data. */ +void +screen_write_cell( + struct screen_write_ctx *ctx, const struct grid_cell *gc, u_char *udata) +{ + struct screen *s = ctx->s; + struct grid *gd = s->grid; + struct grid_utf8 gu, *tmp_gu; + u_int width, xx, i; + struct grid_cell tmp_gc, *tmp_gc2; + size_t size; + + /* Ignore padding. */ + if (gc->flags & GRID_FLAG_PADDING) + return; + + /* Find character width. */ + if (gc->flags & GRID_FLAG_UTF8) { + width = utf8_width(udata); + + gu.width = width; + memcpy(&gu.data, udata, sizeof gu.data); + } else + width = 1; + + /* If the width is zero, combine onto the previous character. */ + if (width == 0) { + if (s->cx == 0) + return; + tmp_gc2 = grid_view_get_cell(gd, s->cx - 1, s->cy); + if (!(tmp_gc2->flags & GRID_FLAG_UTF8)) { + tmp_gc2->flags |= GRID_FLAG_UTF8; + memset(&gu.data, 0xff, sizeof gu.data); + *gu.data = tmp_gc2->data; + gu.width = 1; + grid_view_set_utf8(gd, s->cx - 1, s->cy, &gu); + } + tmp_gu = grid_view_get_utf8(gd, s->cx - 1, s->cy); + + for (i = 0; i < UTF8_SIZE; i++) { + if (tmp_gu->data[i] == 0xff) + break; + } + memcpy(tmp_gu->data + i, udata, UTF8_SIZE - i); + + /* Assume the previous character has just been input. */ + for (size = 0; size < UTF8_SIZE; size++) { + if (udata[size] == 0xff) + break; + } + tty_write_cmd(ctx->wp, TTY_RAW, udata, size); + return; + } + + /* If the character is wider than the screen, don't print it. */ + if (width > screen_size_x(s)) { + memcpy(&tmp_gc, gc, sizeof tmp_gc); + tmp_gc.data = '_'; + width = 1; + gc = &tmp_gc; + } + + /* Check this will fit on the current line; scroll if not. */ + if (s->cx > screen_size_x(s) - width) { + screen_write_carriagereturn(ctx); + screen_write_linefeed(ctx); + } + + /* Sanity checks. */ + if (s->cx > screen_size_x(s) - 1 || s->cy > screen_size_y(s) - 1) + return; + + /* Handle overwriting of UTF-8 characters. */ + screen_write_overwrite(ctx); + + /* + * If the new character is UTF-8 wide, fill in padding cells. Have + * already ensured there is enough room. + */ + for (xx = s->cx + 1; xx < s->cx + width; xx++) { + tmp_gc2 = grid_view_get_cell(gd, xx, s->cy); + if (tmp_gc2 != NULL) + tmp_gc2->flags |= GRID_FLAG_PADDING; + } + + /* Set the cell. */ + grid_view_set_cell(gd, s->cx, s->cy, gc); + if (gc->flags & GRID_FLAG_UTF8) + grid_view_set_utf8(gd, s->cx, s->cy, &gu); + + /* Move the cursor. */ + screen_write_save(ctx); + s->cx += width; + + /* Draw to the screen if necessary. */ + if (screen_check_selection(s, s->cx - width, s->cy)) { + s->sel.cell.data = gc->data; + tty_write_cmd(ctx->wp, TTY_CELL, &s->sel.cell, &gu); + } else + tty_write_cmd(ctx->wp, TTY_CELL, gc, &gu); +} + +/* + * UTF-8 wide characters are a bit of an annoyance. They take up more than one + * cell on the screen, so following cells must not be drawn by marking them as + * padding. + * + * So far, so good. The problem is, when overwriting a padding cell, or a UTF-8 + * character, it is necessary to also overwrite any other cells which covered + * by the same character. + */ +void +screen_write_overwrite(struct screen_write_ctx *ctx) +{ + struct screen *s = ctx->s; + struct grid *gd = s->grid; + const struct grid_cell *gc; + const struct grid_utf8 *gu; + u_int xx; + + gc = grid_view_peek_cell(gd, s->cx, s->cy); + gu = grid_view_peek_utf8(gd, s->cx, s->cy); + + if (gc->flags & GRID_FLAG_PADDING) { + /* + * A padding cell, so clear any following and leading padding + * cells back to the character. Don't overwrite the current + * cell as that happens later anyway. + */ + xx = s->cx + 1; + while (--xx > 0) { + gc = grid_view_peek_cell(gd, xx, s->cy); + if (!(gc->flags & GRID_FLAG_PADDING)) + break; + grid_view_set_cell(gd, xx, s->cy, &grid_default_cell); + } + + /* Overwrite the character at the start of this padding. */ + grid_view_set_cell(gd, xx, s->cy, &grid_default_cell); + + /* Overwrite following padding cells. */ + xx = s->cx; + while (++xx < screen_size_x(s)) { + gc = grid_view_peek_cell(gd, xx, s->cy); + if (!(gc->flags & GRID_FLAG_PADDING)) + break; + grid_view_set_cell(gd, xx, s->cy, &grid_default_cell); + } + } else if (gc->flags & GRID_FLAG_UTF8 && gu->width > 1) { + /* + * An UTF-8 wide cell; overwrite following padding cells only. + */ + xx = s->cx; + while (++xx < screen_size_x(s)) { + gc = grid_view_peek_cell(gd, xx, s->cy); + if (!(gc->flags & GRID_FLAG_PADDING)) + break; + grid_view_set_cell(gd, xx, s->cy, &grid_default_cell); + } + } +} diff --git a/usr.bin/tmux/screen.c b/usr.bin/tmux/screen.c new file mode 100644 index 00000000000..028653b56c4 --- /dev/null +++ b/usr.bin/tmux/screen.c @@ -0,0 +1,240 @@ +/* $OpenBSD: screen.c,v 1.1 2009/06/01 22:58:49 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 <string.h> + +#include "tmux.h" + +void screen_resize_x(struct screen *, u_int); +void screen_resize_y(struct screen *, u_int); + +/* Create a new screen. */ +void +screen_init(struct screen *s, u_int sx, u_int sy, u_int hlimit) +{ + s->grid = grid_create(sx, sy, hlimit); + + s->title = xstrdup(""); + + screen_reinit(s); +} + +/* Reinitialise screen. */ +void +screen_reinit(struct screen *s) +{ + s->cx = 0; + s->cy = 0; + + s->rupper = 0; + s->rlower = screen_size_y(s) - 1; + + s->mode = MODE_CURSOR; + + grid_clear_lines(s->grid, s->grid->hsize, s->grid->sy - 1); + + screen_clear_selection(s); +} + +/* Destroy a screen. */ +void +screen_free(struct screen *s) +{ + xfree(s->title); + grid_destroy(s->grid); +} + +/* Set screen title. */ +void +screen_set_title(struct screen *s, const char *title) +{ + xfree(s->title); + s->title = xstrdup(title); +} + +/* Resize screen. */ +void +screen_resize(struct screen *s, u_int sx, u_int sy) +{ + if (sx < 1) + sx = 1; + if (sy < 1) + sy = 1; + + if (sx != screen_size_x(s)) + screen_resize_x(s, sx); + if (sy != screen_size_y(s)) + screen_resize_y(s, sy); +} + +void +screen_resize_x(struct screen *s, u_int sx) +{ + struct grid *gd = s->grid; + const struct grid_cell *gc; + const struct grid_utf8 *gu; + u_int xx, yy; + + if (sx == 0) + fatalx("zero size"); + + /* If getting larger, not much to do. */ + if (sx > screen_size_x(s)) { + gd->sx = sx; + return; + } + + /* If getting smaller, nuke any data in lines over the new size. */ + for (yy = gd->hsize; yy < gd->hsize + screen_size_y(s); yy++) { + /* + * If the character after the last is wide or padding, remove + * it and any leading padding. + */ + gc = &grid_default_cell; + for (xx = sx; xx > 0; xx--) { + gc = grid_peek_cell(gd, xx - 1, yy); + if (!(gc->flags & GRID_FLAG_PADDING)) + break; + grid_set_cell(gd, xx - 1, yy, &grid_default_cell); + } + if (xx > 0 && xx != sx && gc->flags & GRID_FLAG_UTF8) { + gu = grid_peek_utf8(gd, xx - 1, yy); + if (gu->width > 1) { + grid_set_cell( + gd, xx - 1, yy, &grid_default_cell); + } + } + + /* Reduce the line size. */ + grid_reduce_line(gd, yy, sx); + } + + if (s->cx >= sx) + s->cx = sx - 1; + gd->sx = sx; +} + +void +screen_resize_y(struct screen *s, u_int sy) +{ + struct grid *gd = s->grid; + u_int oy, yy, ny; + + if (sy == 0) + fatalx("zero size"); + + /* Size decreasing. */ + if (sy < screen_size_y(s)) { + oy = screen_size_y(s); + + if (s->cy != 0) { + /* + * The cursor is not at the start. Try to remove as + * many lines as possible from the top. (Up to the + * cursor line.) + */ + ny = s->cy; + if (ny > oy - sy) + ny = oy - sy; + + grid_view_delete_lines(gd, 0, ny); + + s->cy -= ny; + oy -= ny; + } + + if (sy < oy) { + /* Remove any remaining lines from the bottom. */ + grid_view_delete_lines(gd, sy, oy - sy); + if (s->cy >= sy) + s->cy = sy - 1; + } + } + + /* Resize line arrays. */ + gd->size = xrealloc(gd->size, gd->hsize + sy, sizeof *gd->size); + gd->data = xrealloc(gd->data, gd->hsize + sy, sizeof *gd->data); + gd->usize = xrealloc(gd->usize, gd->hsize + sy, sizeof *gd->usize); + gd->udata = xrealloc(gd->udata, gd->hsize + sy, sizeof *gd->udata); + + /* Size increasing. */ + if (sy > screen_size_y(s)) { + oy = screen_size_y(s); + for (yy = gd->hsize + oy; yy < gd->hsize + sy; yy++) { + gd->size[yy] = 0; + gd->data[yy] = NULL; + gd->usize[yy] = 0; + gd->udata[yy] = NULL; + } + } + + gd->sy = sy; + + s->rupper = 0; + s->rlower = screen_size_y(s) - 1; +} + +/* Set selection. */ +void +screen_set_selection(struct screen *s, + u_int sx, u_int sy, u_int ex, u_int ey, struct grid_cell *gc) +{ + struct screen_sel *sel = &s->sel; + + memcpy(&sel->cell, gc, sizeof sel->cell); + + sel->flag = 1; + if (ey < sy || (sy == ey && ex < sx)) { + sel->sx = ex; sel->sy = ey; + sel->ex = sx; sel->ey = sy; + } else { + sel->sx = sx; sel->sy = sy; + sel->ex = ex; sel->ey = ey; + } +} + +/* Clear selection. */ +void +screen_clear_selection(struct screen *s) +{ + struct screen_sel *sel = &s->sel; + + sel->flag = 0; +} + +/* Check if cell in selection. */ +int +screen_check_selection(struct screen *s, u_int px, u_int py) +{ + struct screen_sel *sel = &s->sel; + + if (!sel->flag || py < sel->sy || py > sel->ey) + return (0); + + if (py == sel->sy && py == sel->ey) { + if (px < sel->sx || px > sel->ex) + return (0); + return (1); + } + + if ((py == sel->sy && px < sel->sx) || (py == sel->ey && px > sel->ex)) + return (0); + return (1); +} diff --git a/usr.bin/tmux/server-fn.c b/usr.bin/tmux/server-fn.c new file mode 100644 index 00000000000..2841930bf13 --- /dev/null +++ b/usr.bin/tmux/server-fn.c @@ -0,0 +1,242 @@ +/* $OpenBSD: server-fn.c,v 1.1 2009/06/01 22:58:49 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/time.h> + +#include <string.h> +#include <unistd.h> + +#include "tmux.h" + +int server_lock_callback(void *, const char *); + +const char ** +server_fill_environ(struct session *s) +{ + static const char *env[] = { NULL /* TMUX= */, "TERM=screen", NULL }; + static char tmuxvar[MAXPATHLEN + 256]; + u_int idx; + + if (session_index(s, &idx) != 0) + fatalx("session not found"); + + xsnprintf(tmuxvar, sizeof tmuxvar, + "TMUX=%s,%ld,%u", socket_path, (long) getpid(), idx); + env[0] = tmuxvar; + + return (env); +} + +void +server_write_client( + struct client *c, enum hdrtype type, const void *buf, size_t len) +{ + struct hdr hdr; + + log_debug("writing %d to client %d", type, c->fd); + + hdr.type = type; + hdr.size = len; + + buffer_write(c->out, &hdr, sizeof hdr); + if (buf != NULL && len > 0) + buffer_write(c->out, buf, len); +} + +void +server_write_session( + struct session *s, enum hdrtype type, const void *buf, size_t len) +{ + struct client *c; + u_int i; + + for (i = 0; i < ARRAY_LENGTH(&clients); i++) { + c = ARRAY_ITEM(&clients, i); + if (c == NULL || c->session == NULL) + continue; + if (c->session == s) + server_write_client(c, type, buf, len); + } +} + +void +server_write_window( + struct window *w, enum hdrtype type, const void *buf, size_t len) +{ + struct client *c; + u_int i; + + for (i = 0; i < ARRAY_LENGTH(&clients); i++) { + c = ARRAY_ITEM(&clients, i); + if (c == NULL || c->session == NULL) + continue; + if (c->session->curw->window == w) + server_write_client(c, type, buf, len); + } +} + +void +server_redraw_client(struct client *c) +{ + c->flags |= CLIENT_REDRAW; +} + +void +server_status_client(struct client *c) +{ + c->flags |= CLIENT_STATUS; +} + +void +server_redraw_session(struct session *s) +{ + struct client *c; + u_int i; + + for (i = 0; i < ARRAY_LENGTH(&clients); i++) { + c = ARRAY_ITEM(&clients, i); + if (c == NULL || c->session == NULL) + continue; + if (c->session == s) + server_redraw_client(c); + } +} + +void +server_status_session(struct session *s) +{ + struct client *c; + u_int i; + + for (i = 0; i < ARRAY_LENGTH(&clients); i++) { + c = ARRAY_ITEM(&clients, i); + if (c == NULL || c->session == NULL) + continue; + if (c->session == s) + server_status_client(c); + } +} + +void +server_redraw_window(struct window *w) +{ + struct client *c; + u_int i; + + for (i = 0; i < ARRAY_LENGTH(&clients); i++) { + c = ARRAY_ITEM(&clients, i); + if (c == NULL || c->session == NULL) + continue; + if (c->session->curw->window == w) + server_redraw_client(c); + } + w->flags |= WINDOW_REDRAW; +} + +void +server_status_window(struct window *w) +{ + struct session *s; + u_int i; + + /* + * This is slightly different. We want to redraw the status line of any + * clients containing this window rather than any where it is the + * current window. + */ + + for (i = 0; i < ARRAY_LENGTH(&sessions); i++) { + s = ARRAY_ITEM(&sessions, i); + if (s != NULL && session_has(s, w)) + server_status_session(s); + } +} + +void +server_lock(void) +{ + struct client *c; + u_int i; + + if (server_locked) + return; + + for (i = 0; i < ARRAY_LENGTH(&clients); i++) { + c = ARRAY_ITEM(&clients, i); + if (c == NULL || c->session == NULL) + continue; + + status_prompt_clear(c); + status_prompt_set( + c, "Password: ", server_lock_callback, c, PROMPT_HIDDEN); + server_redraw_client(c); + } + server_locked = 1; +} + +int +server_lock_callback(unused void *data, const char *s) +{ + return (server_unlock(s)); +} + +int +server_unlock(const char *s) +{ + struct client *c; + u_int i; + char *out; + + if (!server_locked) + return (0); + server_activity = time(NULL); + + if (server_password != NULL) { + if (s == NULL) + return (-1); + out = crypt(s, server_password); + if (strcmp(out, server_password) != 0) + goto wrong; + } + + for (i = 0; i < ARRAY_LENGTH(&clients); i++) { + c = ARRAY_ITEM(&clients, i); + if (c == NULL) + continue; + + status_prompt_clear(c); + server_redraw_client(c); + } + + server_locked = 0; + return (0); + +wrong: + for (i = 0; i < ARRAY_LENGTH(&clients); i++) { + c = ARRAY_ITEM(&clients, i); + if (c == NULL) + continue; + + *c->prompt_buffer = '\0'; + c->prompt_index = 0; + server_status_client(c); + } + + return (-1); +} diff --git a/usr.bin/tmux/server-msg.c b/usr.bin/tmux/server-msg.c new file mode 100644 index 00000000000..05f7e8567d9 --- /dev/null +++ b/usr.bin/tmux/server-msg.c @@ -0,0 +1,304 @@ +/* $OpenBSD: server-msg.c,v 1.1 2009/06/01 22:58:49 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 <errno.h> +#include <stdlib.h> +#include <string.h> +#include <time.h> +#include <unistd.h> + +#include "tmux.h" + +int server_msg_fn_command(struct hdr *, struct client *); +int server_msg_fn_identify(struct hdr *, struct client *); +int server_msg_fn_resize(struct hdr *, struct client *); +int server_msg_fn_exiting(struct hdr *, struct client *); +int server_msg_fn_unlock(struct hdr *, struct client *); +int server_msg_fn_wakeup(struct hdr *, struct client *); + +void printflike2 server_msg_fn_command_error( + struct cmd_ctx *, const char *, ...); +void printflike2 server_msg_fn_command_print( + struct cmd_ctx *, const char *, ...); +void printflike2 server_msg_fn_command_info( + struct cmd_ctx *, const char *, ...); + +struct server_msg { + enum hdrtype type; + int (*fn)(struct hdr *, struct client *); +}; +const struct server_msg server_msg_table[] = { + { MSG_IDENTIFY, server_msg_fn_identify }, + { MSG_COMMAND, server_msg_fn_command }, + { MSG_RESIZE, server_msg_fn_resize }, + { MSG_EXITING, server_msg_fn_exiting }, + { MSG_UNLOCK, server_msg_fn_unlock }, + { MSG_WAKEUP, server_msg_fn_wakeup }, +}; + +int +server_msg_dispatch(struct client *c) +{ + struct hdr hdr; + const struct server_msg *msg; + u_int i; + int n; + + for (;;) { + if (BUFFER_USED(c->in) < sizeof hdr) + return (0); + memcpy(&hdr, BUFFER_OUT(c->in), sizeof hdr); + if (BUFFER_USED(c->in) < (sizeof hdr) + hdr.size) + return (0); + buffer_remove(c->in, sizeof hdr); + + for (i = 0; i < nitems(server_msg_table); i++) { + msg = server_msg_table + i; + if (msg->type == hdr.type) { + if ((n = msg->fn(&hdr, c)) != 0) + return (n); + break; + } + } + if (i == nitems(server_msg_table)) + fatalx("unexpected message"); + } +} + +void printflike2 +server_msg_fn_command_error(struct cmd_ctx *ctx, const char *fmt, ...) +{ + va_list ap; + char *msg; + + va_start(ap, fmt); + xvasprintf(&msg, fmt, ap); + va_end(ap); + + server_write_client(ctx->cmdclient, MSG_ERROR, msg, strlen(msg)); + xfree(msg); +} + +void printflike2 +server_msg_fn_command_print(struct cmd_ctx *ctx, const char *fmt, ...) +{ + va_list ap; + char *msg; + + va_start(ap, fmt); + xvasprintf(&msg, fmt, ap); + va_end(ap); + + server_write_client(ctx->cmdclient, MSG_PRINT, msg, strlen(msg)); + xfree(msg); +} + +void printflike2 +server_msg_fn_command_info(struct cmd_ctx *ctx, const char *fmt, ...) +{ + va_list ap; + char *msg; + + if (be_quiet) + return; + + va_start(ap, fmt); + xvasprintf(&msg, fmt, ap); + va_end(ap); + + server_write_client(ctx->cmdclient, MSG_PRINT, msg, strlen(msg)); + xfree(msg); +} + +int +server_msg_fn_command(struct hdr *hdr, struct client *c) +{ + struct msg_command_data data; + struct cmd_ctx ctx; + struct cmd_list *cmdlist; + struct cmd *cmd; + + if (hdr->size < sizeof data) + fatalx("bad MSG_COMMAND size"); + buffer_read(c->in, &data, sizeof data); + + cmdlist = cmd_list_recv(c->in); + server_activity = time(NULL); + + ctx.error = server_msg_fn_command_error; + ctx.print = server_msg_fn_command_print; + ctx.info = server_msg_fn_command_info; + + ctx.msgdata = &data; + ctx.curclient = NULL; + ctx.cursession = NULL; + + ctx.cmdclient = c; + + if (data.pid != -1) { + TAILQ_FOREACH(cmd, cmdlist, qentry) { + if (cmd->entry->flags & CMD_CANTNEST) { + server_msg_fn_command_error(&ctx, + "sessions should be nested with care. " + "unset $TMUX to force"); + cmd_list_free(cmdlist); + server_write_client(c, MSG_EXIT, NULL, 0); + return (0); + } + } + } + + if (cmd_list_exec(cmdlist, &ctx) != 1) + server_write_client(c, MSG_EXIT, NULL, 0); + cmd_list_free(cmdlist); + return (0); +} + +int +server_msg_fn_identify(struct hdr *hdr, struct client *c) +{ + struct msg_identify_data data; + char *term; + + if (hdr->size < sizeof data) + fatalx("bad MSG_IDENTIFY size"); + buffer_read(c->in, &data, sizeof data); + term = cmd_recv_string(c->in); + + log_debug("identify msg from client: %u,%u (%d)", + data.sx, data.sy, data.version); + + if (data.version != PROTOCOL_VERSION) { +#define MSG "protocol version mismatch" + server_write_client(c, MSG_ERROR, MSG, (sizeof MSG) - 1); +#undef MSG + return (0); + } + + c->tty.sx = data.sx; + c->tty.sy = data.sy; + + c->cwd = NULL; + if (*data.cwd != '\0') + c->cwd = xstrdup(data.cwd); + + data.tty[(sizeof data.tty) - 1] = '\0'; + tty_init(&c->tty, data.tty, 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; + xfree(term); + + c->flags |= CLIENT_TERMINAL; + + return (0); +} + +int +server_msg_fn_resize(struct hdr *hdr, struct client *c) +{ + struct msg_resize_data data; + + if (hdr->size != sizeof data) + fatalx("bad MSG_RESIZE size"); + buffer_read(c->in, &data, sizeof data); + + log_debug("resize msg from client: %u,%u", data.sx, data.sy); + + c->tty.sx = data.sx; + if (c->tty.sx == 0) + c->tty.sx = 80; + c->tty.sy = data.sy; + if (c->tty.sy == 0) + c->tty.sy = 25; + + c->tty.cx = UINT_MAX; + c->tty.cy = UINT_MAX; + c->tty.rupper = UINT_MAX; + c->tty.rlower = UINT_MAX; + + recalculate_sizes(); + + /* Always redraw this client. */ + server_redraw_client(c); + + return (0); +} + +int +server_msg_fn_exiting(struct hdr *hdr, struct client *c) +{ + if (hdr->size != 0) + fatalx("bad MSG_EXITING size"); + + log_debug("exiting msg from client"); + + c->session = NULL; + + tty_close(&c->tty, c->flags & CLIENT_SUSPENDED); + + server_write_client(c, MSG_EXITED, NULL, 0); + + return (0); +} + +int +server_msg_fn_unlock(struct hdr *hdr, struct client *c) +{ + char *pass; + + if (hdr->size == 0) + fatalx("bad MSG_UNLOCK size"); + pass = cmd_recv_string(c->in); + + log_debug("unlock msg from client"); + + if (server_unlock(pass) != 0) { +#define MSG "bad password" + server_write_client(c, MSG_ERROR, MSG, (sizeof MSG) - 1); + server_write_client(c, MSG_EXIT, NULL, 0); + return (0); +#undef MSG + } + + server_write_client(c, MSG_EXIT, NULL, 0); + + return (0); +} + +int +server_msg_fn_wakeup(struct hdr *hdr, struct client *c) +{ + if (hdr->size != 0) + fatalx("bad MSG_WAKEUP size"); + + log_debug("wakeup msg from client"); + + c->flags &= ~CLIENT_SUSPENDED; + tty_start_tty(&c->tty); + server_redraw_client(c); + + return (0); +} diff --git a/usr.bin/tmux/server.c b/usr.bin/tmux/server.c new file mode 100644 index 00000000000..642f38c842c --- /dev/null +++ b/usr.bin/tmux/server.c @@ -0,0 +1,1105 @@ +/* $OpenBSD: server.c,v 1.1 2009/06/01 22:58:49 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 <sys/socket.h> +#include <sys/stat.h> +#include <sys/un.h> +#include <sys/wait.h> + +#include <errno.h> +#include <fcntl.h> +#include <signal.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <syslog.h> +#include <termios.h> +#include <time.h> +#include <unistd.h> + +#include "tmux.h" + +/* + * Main server functions. + */ + +/* Client list. */ +struct clients clients; + +int server_create_socket(void); +int server_main(int); +void server_shutdown(void); +void server_child_signal(void); +void server_fill_windows(struct pollfd **); +void server_handle_windows(struct pollfd **); +void server_fill_clients(struct pollfd **); +void server_handle_clients(struct pollfd **); +struct client *server_accept_client(int); +void server_handle_client(struct client *); +void server_handle_window(struct window *, struct window_pane *); +int server_check_window_bell(struct session *, struct window *, + struct window_pane *); +int server_check_window_activity(struct session *, + struct window *); +int server_check_window_content(struct session *, struct window *, + struct window_pane *); +void server_lost_client(struct client *); +void server_check_window(struct window *); +void server_check_redraw(struct client *); +void server_redraw_locked(struct client *); +void server_check_timers(struct client *); +void server_second_timers(void); +int server_update_socket(void); + +/* Create a new client. */ +struct client * +server_create_client(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->fd = fd; + c->in = buffer_create(BUFSIZ); + c->out = buffer_create(BUFSIZ); + + ARRAY_INIT(&c->prompt_hdata); + + c->tty.fd = -1; + c->title = NULL; + + c->session = NULL; + c->tty.sx = 80; + c->tty.sy = 25; + screen_init(&c->status, c->tty.sx, 1, 0); + + 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 (c); + } + } + ARRAY_ADD(&clients, c); + return (c); +} + +/* Find client index. */ +int +server_client_index(struct client *c) +{ + u_int i; + + for (i = 0; i < ARRAY_LENGTH(&clients); i++) { + if (c == ARRAY_ITEM(&clients, i)) + return (i); + } + return (-1); +} + +/* Fork new server. */ +int +server_start(char *path) +{ + int pair[2], srv_fd; + char *cause; + char rpathbuf[MAXPATHLEN]; + + /* The first client is special and gets a socketpair; create it. */ + if (socketpair(AF_UNIX, SOCK_STREAM, PF_UNSPEC, pair) != 0) + fatal("socketpair failed"); + + switch (fork()) { + case -1: + fatal("fork failed"); + case 0: + break; + default: + close(pair[1]); + return (pair[0]); + } + close(pair[0]); + + /* + * Must daemonise before loading configuration as the PID changes so + * $TMUX would be wrong for sessions created in the config file. + */ + if (daemon(1, 1) != 0) + fatal("daemon failed"); + + ARRAY_INIT(&windows); + ARRAY_INIT(&clients); + ARRAY_INIT(&sessions); + key_bindings_init(); + utf8_build(); + + server_locked = 0; + server_password = NULL; + server_activity = time(NULL); + + start_time = time(NULL); + socket_path = path; + + if (cfg_file != NULL && load_cfg(cfg_file, &cause) != 0) { + log_warnx("%s", cause); + exit(1); + } + logfile("server"); + + log_debug("server started, pid %ld", (long) getpid()); + log_debug("socket path %s", socket_path); + + if (realpath(socket_path, rpathbuf) == NULL) + strlcpy(rpathbuf, socket_path, sizeof rpathbuf); + setproctitle("server (%s)", rpathbuf); + + srv_fd = server_create_socket(); + server_create_client(pair[1]); + + 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 window *w; + struct pollfd *pfds, *pfd; + int nfds, xtimeout; + u_int i, n; + time_t now, last; + + siginit(); + + last = time(NULL); + + pfds = NULL; + for (;;) { + /* If sigterm, kill all windows and clients. */ + if (sigterm) + server_shutdown(); + + /* Handle child exit. */ + if (sigchld) { + server_child_signal(); + sigchld = 0; + } + + /* Recreate socket on SIGUSR1. */ + if (sigusr1) { + close(srv_fd); + srv_fd = server_create_socket(); + sigusr1 = 0; + } + + /* Initialise pollfd array. */ + nfds = 1; + for (i = 0; i < ARRAY_LENGTH(&windows); i++) { + w = ARRAY_ITEM(&windows, i); + if (w != NULL) + nfds += window_count_panes(w); + } + nfds += ARRAY_LENGTH(&clients) * 2; + pfds = xrealloc(pfds, nfds, sizeof *pfds); + memset(pfds, 0, nfds * sizeof *pfds); + pfd = pfds; + + /* Fill server socket. */ + pfd->fd = srv_fd; + pfd->events = POLLIN; + pfd++; + + /* Fill window and client sockets. */ + server_fill_windows(&pfd); + server_fill_clients(&pfd); + + /* Update socket permissions. */ + xtimeout = INFTIM; + if (sigterm || server_update_socket() != 0) + xtimeout = POLL_TIMEOUT; + + /* Do the poll. */ + if ((nfds = poll(pfds, nfds, xtimeout)) == -1) { + if (errno == EAGAIN || errno == EINTR) + continue; + fatal("poll failed"); + } + pfd = pfds; + + /* Handle server socket. */ + if (pfd->revents & (POLLERR|POLLNVAL|POLLHUP)) + fatalx("lost server socket"); + if (pfd->revents & POLLIN) { + server_accept_client(srv_fd); + continue; + } + pfd++; + + /* Call second-based timers. */ + now = time(NULL); + if (now != last) { + last = now; + server_second_timers(); + } + + /* Set window names. */ + set_window_names(); + + /* + * Handle window and client sockets. Clients can create + * windows, so windows must come first to avoid messing up by + * increasing the array size. + */ + server_handle_windows(&pfd); + server_handle_clients(&pfd); + + /* + * If we have no sessions and clients left, let's get out + * of here... + */ + n = 0; + for (i = 0; i < ARRAY_LENGTH(&sessions); i++) { + if (ARRAY_ITEM(&sessions, i) != NULL) + n++; + } + for (i = 0; i < ARRAY_LENGTH(&clients); i++) { + if (ARRAY_ITEM(&clients, i) != NULL) + n++; + } + if (n == 0) + break; + } + if (pfds != NULL) + xfree(pfds); + + for (i = 0; i < ARRAY_LENGTH(&sessions); i++) { + if (ARRAY_ITEM(&sessions, i) != NULL) + session_destroy(ARRAY_ITEM(&sessions, i)); + } + ARRAY_FREE(&sessions); + + for (i = 0; i < ARRAY_LENGTH(&clients); i++) { + if (ARRAY_ITEM(&clients, i) != NULL) + server_lost_client(ARRAY_ITEM(&clients, i)); + } + ARRAY_FREE(&clients); + + key_bindings_free(); + + close(srv_fd); + + unlink(socket_path); + xfree(socket_path); + + options_free(&global_options); + options_free(&global_window_options); + if (server_password != NULL) + xfree(server_password); + + return (0); +} + +/* Kill all clients. */ +void +server_shutdown(void) +{ + struct session *s; + struct client *c; + u_int i, j; + + for (i = 0; i < ARRAY_LENGTH(&sessions); i++) { + s = ARRAY_ITEM(&sessions, i); + for (j = 0; j < ARRAY_LENGTH(&clients); j++) { + c = ARRAY_ITEM(&clients, j); + if (c != NULL && c->session == s) { + s = NULL; + break; + } + } + if (s != NULL) + session_destroy(s); + } + + for (i = 0; i < ARRAY_LENGTH(&clients); i++) { + c = ARRAY_ITEM(&clients, i); + if (c != NULL) + server_write_client(c, MSG_SHUTDOWN, NULL, 0); + } +} + +/* Handle SIGCHLD. */ +void +server_child_signal(void) +{ + struct window *w; + struct window_pane *wp; + int status; + pid_t pid; + u_int i; + + for (;;) { + switch (pid = waitpid(WAIT_ANY, &status, WNOHANG|WUNTRACED)) { + case -1: + if (errno == ECHILD) + return; + fatal("waitpid"); + case 0: + return; + } + if (!WIFSTOPPED(status)) + continue; + if (WSTOPSIG(status) == SIGTTIN || WSTOPSIG(status) == SIGTTOU) + continue; + + 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->pid == pid) { + if (killpg(pid, SIGCONT) != 0) + kill(pid, SIGCONT); + } + } + } + } +} + +/* Fill window pollfds. */ +void +server_fill_windows(struct pollfd **pfd) +{ + struct window *w; + struct window_pane *wp; + 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) { + (*pfd)->fd = wp->fd; + if (wp->fd != -1) { + (*pfd)->events = POLLIN; + if (BUFFER_USED(wp->out) > 0) + (*pfd)->events |= POLLOUT; + } + (*pfd)++; + } + } +} + +/* Handle window pollfds. */ +void +server_handle_windows(struct pollfd **pfd) +{ + struct window *w; + struct window_pane *wp; + 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) { + if (buffer_poll(*pfd, wp->in, wp->out) != 0) { + close(wp->fd); + wp->fd = -1; + } else + server_handle_window(w, wp); + } + (*pfd)++; + } + + server_check_window(w); + } +} + +/* Check for general redraw on client. */ +void +server_check_redraw(struct client *c) +{ + struct session *s; + struct window_pane *wp; + char title[512]; + int flags, redraw; + + if (c == NULL || c->session == NULL) + return; + s = c->session; + + flags = c->tty.flags & TTY_FREEZE; + c->tty.flags &= ~TTY_FREEZE; + + if (options_get_number(&s->options, "set-titles")) { + xsnprintf(title, sizeof title, "%s:%u:%s - \"%s\"", + s->name, s->curw->idx, s->curw->window->name, + s->curw->window->active->screen->title); + 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); + } + } + + if (c->flags & (CLIENT_REDRAW|CLIENT_STATUS)) { + 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) { + if (server_locked) + server_redraw_locked(c); + else + screen_redraw_screen(c); + 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_status(c); + + c->tty.flags |= flags; + + c->flags &= ~(CLIENT_REDRAW|CLIENT_STATUS); +} + +/* Redraw client when locked. */ +void +server_redraw_locked(struct client *c) +{ + struct screen_write_ctx ctx; + struct screen screen; + u_int colour, xx, yy, i; + int style; + + xx = c->tty.sx; + yy = c->tty.sy - 1; + if (xx == 0 || yy == 0) + return; + colour = options_get_number( + &global_window_options, "clock-mode-colour"); + style = options_get_number( + &global_window_options, "clock-mode-style"); + + screen_init(&screen, xx, yy, 0); + + screen_write_start(&ctx, NULL, &screen); + clock_draw(&ctx, colour, style); + screen_write_stop(&ctx); + + for (i = 0; i < screen_size_y(&screen); i++) + tty_draw_line(&c->tty, &screen, i, 0, 0); + screen_redraw_status(c); + + screen_free(&screen); +} + +/* Check for timers on client. */ +void +server_check_timers(struct client *c) +{ + struct session *s; + struct timeval tv; + u_int interval; + + if (c == NULL || c->session == NULL) + return; + s = c->session; + + if (gettimeofday(&tv, NULL) != 0) + fatal("gettimeofday"); + + 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) + c->flags |= CLIENT_STATUS; +} + +/* Fill client pollfds. */ +void +server_fill_clients(struct pollfd **pfd) +{ + 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); + + server_check_timers(c); + server_check_redraw(c); + + if (c == NULL) + (*pfd)->fd = -1; + else { + (*pfd)->fd = c->fd; + (*pfd)->events = POLLIN; + if (BUFFER_USED(c->out) > 0) + (*pfd)->events |= POLLOUT; + } + (*pfd)++; + + if (c == NULL || c->flags & CLIENT_SUSPENDED || + c->tty.fd == -1 || c->session == NULL) + (*pfd)->fd = -1; + else { + (*pfd)->fd = c->tty.fd; + (*pfd)->events = POLLIN; + if (BUFFER_USED(c->tty.out) > 0) + (*pfd)->events |= POLLOUT; + } + (*pfd)++; + } + + /* + * 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; + } +} + +/* Handle client pollfds. */ +void +server_handle_clients(struct pollfd **pfd) +{ + struct client *c; + u_int i; + + for (i = 0; i < ARRAY_LENGTH(&clients); i++) { + c = ARRAY_ITEM(&clients, i); + + if (c != NULL) { + if (buffer_poll(*pfd, c->in, c->out) != 0) { + server_lost_client(c); + (*pfd) += 2; + continue; + } else + server_msg_dispatch(c); + } + (*pfd)++; + + if (c != NULL && !(c->flags & CLIENT_SUSPENDED) && + c->tty.fd != -1 && c->session != NULL) { + if (buffer_poll(*pfd, c->tty.in, c->tty.out) != 0) + server_lost_client(c); + else + server_handle_client(c); + } + (*pfd)++; + } +} + +/* accept(2) and create new client. */ +struct client * +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 (NULL); + fatal("accept failed"); + } + if (sigterm) { + close(fd); + return (NULL); + } + return (server_create_client(fd)); +} + +/* Input data from client. */ +void +server_handle_client(struct client *c) +{ + struct window_pane *wp; + struct screen *s; + struct timeval tv; + struct key_binding *bd; + int key, prefix, status, xtimeout; + int mode; + u_char mouse[3]; + + xtimeout = options_get_number(&c->session->options, "repeat-time"); + if (xtimeout != 0 && c->flags & CLIENT_REPEAT) { + if (gettimeofday(&tv, NULL) != 0) + fatal("gettimeofday"); + if (timercmp(&tv, &c->repeat_timer, >)) + c->flags &= ~(CLIENT_PREFIX|CLIENT_REPEAT); + } + + /* Process keys. */ + prefix = options_get_number(&c->session->options, "prefix"); + while (tty_keys_next(&c->tty, &key, mouse) == 0) { + server_activity = time(NULL); + + if (c->session == NULL) + return; + wp = c->session->curw->window->active; /* could die */ + + status_message_clear(c); + if (c->prompt_string != NULL) { + status_prompt_key(c, key); + continue; + } + if (server_locked) + continue; + + /* Check for mouse keys. */ + if (key == KEYC_MOUSE) { + window_pane_mouse(wp, c, mouse[0], mouse[1], mouse[2]); + continue; + } + + /* No previous prefix key. */ + if (!(c->flags & CLIENT_PREFIX)) { + if (key == prefix) + c->flags |= CLIENT_PREFIX; + else + window_pane_key(wp, c, key); + continue; + } + + /* Prefix key already pressed. Reset prefix and lookup key. */ + c->flags &= ~CLIENT_PREFIX; + if ((bd = key_bindings_lookup(key)) == NULL) { + /* If repeating, treat this as a key, else ignore. */ + if (c->flags & CLIENT_REPEAT) { + c->flags &= ~CLIENT_REPEAT; + if (key == prefix) + 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 (key == prefix) + 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"); + timeradd(&c->repeat_timer, &tv, &c->repeat_timer); + } + + /* Dispatch the command. */ + key_bindings_dispatch(bd, c); + } + if (c->session == NULL) + return; + wp = c->session->curw->window->active; /* could die - do each loop */ + s = wp->screen; + + /* Ensure cursor position and mode settings. */ + status = options_get_number(&c->session->options, "status"); + if (wp->yoff + s->cy < c->tty.sy - status) + tty_cursor(&c->tty, s->cx, s->cy, wp->xoff, wp->yoff); + + mode = s->mode; + if (server_locked) + mode &= ~TTY_NOCURSOR; + tty_update_mode(&c->tty, mode); +} + +/* 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); + } + + tty_free(&c->tty, c->flags & CLIENT_SUSPENDED); + + screen_free(&c->status); + + 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->fd); + buffer_destroy(c->in); + buffer_destroy(c->out); + xfree(c); + + recalculate_sizes(); +} + +/* 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, wp); + 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 window_pane *wp) +{ + struct client *c; + u_int i; + int action; + + 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; + for (i = 0; i < ARRAY_LENGTH(&clients); i++) { + c = ARRAY_ITEM(&clients, i); + if (c != NULL && c->session == s) + tty_putcode(&c->tty, TTYC_BEL); + } + break; + case BELL_CURRENT: + if (w->active != wp) + break; + for (i = 0; i < ARRAY_LENGTH(&clients); i++) { + c = ARRAY_ITEM(&clients, i); + if (c != NULL && c->session == s) + tty_putcode(&c->tty, TTYC_BEL); + } + break; + } + return (1); +} + +int +server_check_window_activity(struct session *s, struct window *w) +{ + 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); + session_alert_add(s, w, WINDOW_ACTIVITY); + return (1); +} + +int +server_check_window_content( + struct session *s, struct window *w, struct window_pane *wp) +{ + char *found, *ptr; + + if (!(w->flags & WINDOW_CONTENT)) + return (0); + if ((ptr = options_get_string(&w->options, "monitor-content")) == NULL) + return (0); + if (*ptr == '\0') + return (0); + if (session_alert_has_window(s, w, WINDOW_CONTENT)) + return (0); + if ((found = window_pane_search(wp, ptr)) == NULL) + return (0); + session_alert_add(s, w, WINDOW_CONTENT); + xfree(found); + return (1); +} + +/* Check if window still exists.. */ +void +server_check_window(struct window *w) +{ + struct window_pane *wp, *wq; + struct client *c; + struct session *s; + struct winlink *wl; + u_int i, j; + int destroyed, flag; + + flag = options_get_number(&w->options, "remain-on-exit"); + + destroyed = 1; + + wp = TAILQ_FIRST(&w->panes); + while (wp != NULL) { + wq = TAILQ_NEXT(wp, entry); + if (wp->fd != -1) + destroyed = 0; + else if (!flag) { + window_remove_pane(w, wp); + server_redraw_window(w); + layout_refresh(w, 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; + 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); + } + /* If the session was destroyed, bail now. */ + if (destroyed) + break; + goto restart; + } + } + + recalculate_sizes(); +} + +/* Call any once-per-second timers. */ +void +server_second_timers(void) +{ + struct window *w; + struct window_pane *wp; + u_int i; + int xtimeout; + struct tm now, then; + static time_t last_t = 0; + time_t t; + + t = time(NULL); + xtimeout = options_get_number(&global_options, "lock-after-time"); + if (xtimeout > 0 && t > server_activity + xtimeout) + server_lock(); + + 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); + } + } + + /* Check for a minute having passed. */ + gmtime_r(&t, &now); + gmtime_r(&last_t, &then); + if (now.tm_min == then.tm_min) + return; + last_t = t; + + /* If locked, redraw all clients. */ + if (server_locked) { + for (i = 0; i < ARRAY_LENGTH(&clients); i++) { + if (ARRAY_ITEM(&clients, i) != NULL) + server_redraw_client(ARRAY_ITEM(&clients, i)); + } + } +} + +/* Update socket execute permissions based on whether sessions are attached. */ +int +server_update_socket(void) +{ + struct session *s; + u_int i; + static int last = -1; + int n; + + n = 0; + for (i = 0; i < ARRAY_LENGTH(&sessions); i++) { + s = ARRAY_ITEM(&sessions, i); + if (s != NULL && !(s->flags & SESSION_UNATTACHED)) { + n++; + break; + } + } + + if (n != last) { + last = n; + if (n != 0) + chmod(socket_path, S_IRWXU); + else + chmod(socket_path, S_IRUSR|S_IWUSR); + } + + return (n); +} diff --git a/usr.bin/tmux/session.c b/usr.bin/tmux/session.c new file mode 100644 index 00000000000..8ae08a772ab --- /dev/null +++ b/usr.bin/tmux/session.c @@ -0,0 +1,378 @@ +/* $OpenBSD: session.c,v 1.1 2009/06/01 22:58:49 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/time.h> + +#include <string.h> +#include <stdlib.h> +#include <unistd.h> + +#include "tmux.h" + +/* Global session list. */ +struct sessions sessions; + +struct winlink *session_next_activity(struct session *, struct winlink *); +struct winlink *session_previous_activity(struct session *, struct winlink *); + +void +session_alert_cancel(struct session *s, struct winlink *wl) +{ + struct session_alert *sa, *sb; + + sa = SLIST_FIRST(&s->alerts); + while (sa != NULL) { + sb = sa; + sa = SLIST_NEXT(sa, entry); + + if (wl == NULL || sb->wl == wl) { + SLIST_REMOVE(&s->alerts, sb, session_alert, entry); + xfree(sb); + } + } +} + +void +session_alert_add(struct session *s, struct window *w, int type) +{ + struct session_alert *sa; + struct winlink *wl; + + RB_FOREACH(wl, winlinks, &s->windows) { + if (wl == s->curw) + continue; + + if (wl->window == w && + !session_alert_has(s, wl, type)) { + sa = xmalloc(sizeof *sa); + sa->wl = wl; + sa->type = type; + SLIST_INSERT_HEAD(&s->alerts, sa, entry); + } + } +} + +int +session_alert_has(struct session *s, struct winlink *wl, int type) +{ + struct session_alert *sa; + + SLIST_FOREACH(sa, &s->alerts, entry) { + if (sa->wl == wl && sa->type == type) + return (1); + } + + return (0); +} + +int +session_alert_has_window(struct session *s, struct window *w, int type) +{ + struct session_alert *sa; + + SLIST_FOREACH(sa, &s->alerts, entry) { + if (sa->wl->window == w && sa->type == type) + return (1); + } + + return (0); +} + +/* Find session by name. */ +struct session * +session_find(const char *name) +{ + struct session *s; + u_int i; + + for (i = 0; i < ARRAY_LENGTH(&sessions); i++) { + s = ARRAY_ITEM(&sessions, i); + if (s != NULL && strcmp(s->name, name) == 0) + return (s); + } + + return (NULL); +} + +/* Create a new session. */ +struct session * +session_create(const char *name, + const char *cmd, const char *cwd, u_int sx, u_int sy, char **cause) +{ + struct session *s; + u_int i; + + s = xmalloc(sizeof *s); + s->flags = 0; + if (gettimeofday(&s->tv, NULL) != 0) + fatal("gettimeofday"); + s->curw = NULL; + SLIST_INIT(&s->lastw); + RB_INIT(&s->windows); + SLIST_INIT(&s->alerts); + paste_init_stack(&s->buffers); + options_init(&s->options, &global_options); + + s->sx = sx; + s->sy = sy; + + for (i = 0; i < ARRAY_LENGTH(&sessions); i++) { + if (ARRAY_ITEM(&sessions, i) == NULL) { + ARRAY_SET(&sessions, i, s); + break; + } + } + if (i == ARRAY_LENGTH(&sessions)) + ARRAY_ADD(&sessions, s); + + if (name != NULL) + s->name = xstrdup(name); + else + xasprintf(&s->name, "%u", i); + if (session_new(s, NULL, cmd, cwd, -1, cause) == NULL) { + session_destroy(s); + return (NULL); + } + session_select(s, 0); + + log_debug("session %s created", s->name); + + return (s); +} + +/* Destroy a session. */ +void +session_destroy(struct session *s) +{ + u_int i; + + log_debug("session %s destroyed", s->name); + + if (session_index(s, &i) != 0) + fatalx("session not found"); + ARRAY_SET(&sessions, i, NULL); + while (!ARRAY_EMPTY(&sessions) && ARRAY_LAST(&sessions) == NULL) + ARRAY_TRUNC(&sessions, 1); + + session_alert_cancel(s, NULL); + options_free(&s->options); + paste_free_stack(&s->buffers); + + while (!SLIST_EMPTY(&s->lastw)) + winlink_stack_remove(&s->lastw, SLIST_FIRST(&s->lastw)); + while (!RB_EMPTY(&s->windows)) + winlink_remove(&s->windows, RB_ROOT(&s->windows)); + + xfree(s->name); + xfree(s); +} + +/* Find session index. */ +int +session_index(struct session *s, u_int *i) +{ + for (*i = 0; *i < ARRAY_LENGTH(&sessions); (*i)++) { + if (s == ARRAY_ITEM(&sessions, *i)) + return (0); + } + return (-1); +} + +/* Create a new window on a session. */ +struct winlink * +session_new(struct session *s, + const char *name, const char *cmd, const char *cwd, int idx, char **cause) +{ + struct window *w; + const char **env; + u_int hlimit; + + env = server_fill_environ(s); + + hlimit = options_get_number(&s->options, "history-limit"); + w = window_create(name, cmd, cwd, env, s->sx, s->sy, hlimit, cause); + if (w == NULL) + return (NULL); + + if (options_get_number(&s->options, "set-remain-on-exit")) + options_set_number(&w->options, "remain-on-exit", 1); + + return (session_attach(s, w, idx, cause)); +} + +/* Attach a window to a session. */ +struct winlink * +session_attach(struct session *s, struct window *w, int idx, char **cause) +{ + struct winlink *wl; + + if ((wl = winlink_add(&s->windows, w, idx)) == NULL) + xasprintf(cause, "index in use: %d", idx); + return (wl); +} + +/* Detach a window from a session. */ +int +session_detach(struct session *s, struct winlink *wl) +{ + if (s->curw == wl && + session_last(s) != 0 && session_previous(s, 0) != 0) + session_next(s, 0); + + session_alert_cancel(s, wl); + winlink_stack_remove(&s->lastw, wl); + winlink_remove(&s->windows, wl); + if (RB_EMPTY(&s->windows)) { + session_destroy(s); + return (1); + } + return (0); +} + +/* Return if session has window. */ +int +session_has(struct session *s, struct window *w) +{ + struct winlink *wl; + + RB_FOREACH(wl, winlinks, &s->windows) { + if (wl->window == w) + return (1); + } + return (0); +} + +struct winlink * +session_next_activity(struct session *s, struct winlink *wl) +{ + while (wl != NULL) { + if (session_alert_has(s, wl, WINDOW_BELL)) + break; + if (session_alert_has(s, wl, WINDOW_ACTIVITY)) + break; + if (session_alert_has(s, wl, WINDOW_CONTENT)) + break; + wl = winlink_next(&s->windows, wl); + } + return (wl); +} + +/* Move session to next window. */ +int +session_next(struct session *s, int activity) +{ + struct winlink *wl; + + if (s->curw == NULL) + return (-1); + + wl = winlink_next(&s->windows, s->curw); + if (activity) + wl = session_next_activity(s, wl); + if (wl == NULL) { + wl = RB_MIN(winlinks, &s->windows); + if (activity && ((wl = session_next_activity(s, wl)) == NULL)) + return (-1); + } + if (wl == s->curw) + return (1); + winlink_stack_remove(&s->lastw, wl); + winlink_stack_push(&s->lastw, s->curw); + s->curw = wl; + session_alert_cancel(s, wl); + return (0); +} + +struct winlink * +session_previous_activity(struct session *s, struct winlink *wl) +{ + while (wl != NULL) { + if (session_alert_has(s, wl, WINDOW_BELL)) + break; + if (session_alert_has(s, wl, WINDOW_ACTIVITY)) + break; + if (session_alert_has(s, wl, WINDOW_CONTENT)) + break; + wl = winlink_previous(&s->windows, wl); + } + return (wl); +} + +/* Move session to previous window. */ +int +session_previous(struct session *s, int activity) +{ + struct winlink *wl; + + if (s->curw == NULL) + return (-1); + + wl = winlink_previous(&s->windows, s->curw); + if (activity) + wl = session_previous_activity(s, wl); + if (wl == NULL) { + wl = RB_MAX(winlinks, &s->windows); + if (activity && (wl = session_previous_activity(s, wl)) == NULL) + return (-1); + } + if (wl == s->curw) + return (1); + winlink_stack_remove(&s->lastw, wl); + winlink_stack_push(&s->lastw, s->curw); + s->curw = wl; + session_alert_cancel(s, wl); + return (0); +} + +/* Move session to specific window. */ +int +session_select(struct session *s, int idx) +{ + struct winlink *wl; + + wl = winlink_find_by_index(&s->windows, idx); + if (wl == NULL) + return (-1); + if (wl == s->curw) + return (1); + winlink_stack_remove(&s->lastw, wl); + winlink_stack_push(&s->lastw, s->curw); + s->curw = wl; + session_alert_cancel(s, wl); + return (0); +} + +/* Move session to last used window. */ +int +session_last(struct session *s) +{ + struct winlink *wl; + + wl = SLIST_FIRST(&s->lastw); + if (wl == NULL) + return (-1); + if (wl == s->curw) + return (1); + + winlink_stack_remove(&s->lastw, wl); + winlink_stack_push(&s->lastw, s->curw); + s->curw = wl; + session_alert_cancel(s, wl); + return (0); +} diff --git a/usr.bin/tmux/status.c b/usr.bin/tmux/status.c new file mode 100644 index 00000000000..d7f09acdb28 --- /dev/null +++ b/usr.bin/tmux/status.c @@ -0,0 +1,960 @@ +/* $OpenBSD: status.c,v 1.1 2009/06/01 22:58:49 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/time.h> + +#include <errno.h> +#include <limits.h> +#include <stdarg.h> +#include <stdlib.h> +#include <string.h> +#include <time.h> +#include <unistd.h> + +#include "tmux.h" + +char *status_replace(struct session *, char *, time_t); +char *status_replace_popen(char **); +size_t status_width(struct winlink *); +char *status_print(struct session *, struct winlink *, struct grid_cell *); + +void status_prompt_add_history(struct client *); +char *status_prompt_complete(const char *); + +/* Draw status for client on the last lines of given context. */ +int +status_redraw(struct client *c) +{ + struct screen_write_ctx ctx; + struct session *s = c->session; + struct winlink *wl; + struct window_pane *wp; + struct screen *sc = NULL, old_status; + char *left, *right, *text, *ptr; + size_t llen, rlen, offset, xx, yy, sy; + size_t size, start, width; + struct grid_cell stdgc, gc; + int larrow, rarrow; + + left = right = NULL; + + /* Create the target screen. */ + memcpy(&old_status, &c->status, sizeof old_status); + screen_init(&c->status, c->tty.sx, 1, 0); + + /* No status line? */ + if (c->tty.sy == 0 || !options_get_number(&s->options, "status")) + goto off; + larrow = rarrow = 0; + + if (gettimeofday(&c->status_timer, NULL) != 0) + fatal("gettimeofday"); + memcpy(&stdgc, &grid_default_cell, sizeof gc); + stdgc.bg = options_get_number(&s->options, "status-fg"); + stdgc.fg = options_get_number(&s->options, "status-bg"); + stdgc.attr |= options_get_number(&s->options, "status-attr"); + + yy = c->tty.sy - 1; + if (yy == 0) + goto blank; + + /* Work out the left and right strings. */ + left = status_replace(s, options_get_string( + &s->options, "status-left"), c->status_timer.tv_sec); + llen = options_get_number(&s->options, "status-left-length"); + if (strlen(left) < llen) + llen = strlen(left); + left[llen] = '\0'; + + right = status_replace(s, options_get_string( + &s->options, "status-right"), c->status_timer.tv_sec); + rlen = options_get_number(&s->options, "status-right-length"); + if (strlen(right) < rlen) + rlen = strlen(right); + right[rlen] = '\0'; + + /* + * Figure out how much space we have for the window list. If there isn't + * enough space, just wimp out. + */ + xx = 0; + if (llen != 0) + xx += llen + 1; + if (rlen != 0) + xx += rlen + 1; + if (c->tty.sx == 0 || c->tty.sx <= xx) + goto blank; + xx = c->tty.sx - xx; + + /* + * Right. We have xx characters to fill. Find out how much is to go in + * them and the offset of the current window (it must be on screen). + */ + width = offset = 0; + RB_FOREACH(wl, winlinks, &s->windows) { + size = status_width(wl) + 1; + if (wl == s->curw) + offset = width; + width += size; + } + start = 0; + + /* If there is enough space for the total width, all is gravy. */ + if (width <= xx) + goto draw; + + /* Find size of current window text. */ + size = status_width(s->curw); + + /* + * If the offset is already on screen, we're good to draw from the + * start and just leave off the end. + */ + if (offset + size < xx) { + if (xx > 0) { + rarrow = 1; + xx--; + } + + width = xx; + goto draw; + } + + /* + * Work out how many characters we need to omit from the start. There + * are xx characters to fill, and offset + size must be the last. So, + * the start character is offset + size - xx. + */ + if (xx > 0) { + larrow = 1; + xx--; + } + + start = offset + size - xx; + if (xx > 0 && width > start + xx + 1) { /* + 1, eh? */ + rarrow = 1; + start++; + xx--; + } + width = xx; + +draw: + /* Bail here if anything is too small too. XXX. */ + if (width == 0 || xx == 0) + goto blank; + + /* Begin drawing and move to the starting position. */ + screen_write_start(&ctx, NULL, &c->status); + if (llen != 0) { + screen_write_cursormove(&ctx, 0, yy); + screen_write_puts(&ctx, &stdgc, "%s ", left); + if (larrow) + screen_write_putc(&ctx, &stdgc, ' '); + } else { + if (larrow) + screen_write_cursormove(&ctx, 1, yy); + else + screen_write_cursormove(&ctx, 0, yy); + } + + /* Draw each character in succession. */ + offset = 0; + RB_FOREACH(wl, winlinks, &s->windows) { + memcpy(&gc, &stdgc, sizeof gc); + text = status_print(s, wl, &gc); + + if (larrow == 1 && offset < start) { + if (session_alert_has(s, wl, WINDOW_ACTIVITY)) + larrow = -1; + else if (session_alert_has(s, wl, WINDOW_BELL)) + larrow = -1; + else if (session_alert_has(s, wl, WINDOW_CONTENT)) + larrow = -1; + } + + for (ptr = text; *ptr != '\0'; ptr++) { + if (offset >= start && offset < start + width) + screen_write_putc(&ctx, &gc, *ptr); + offset++; + } + + if (rarrow == 1 && offset > start + width) { + if (session_alert_has(s, wl, WINDOW_ACTIVITY)) + rarrow = -1; + else if (session_alert_has(s, wl, WINDOW_BELL)) + rarrow = -1; + else if (session_alert_has(s, wl, WINDOW_CONTENT)) + rarrow = -1; + } + + if (offset < start + width) { + if (offset >= start) { + screen_write_putc(&ctx, &stdgc, ' '); + } + offset++; + } + + xfree(text); + } + + /* Fill the remaining space if any. */ + while (offset++ < xx) + screen_write_putc(&ctx, &stdgc, ' '); + + /* Draw the last item. */ + if (rlen != 0) { + screen_write_cursormove(&ctx, c->tty.sx - rlen - 1, yy); + screen_write_puts(&ctx, &stdgc, " %s", right); + } + + /* Draw the arrows. */ + if (larrow != 0) { + memcpy(&gc, &stdgc, sizeof gc); + if (larrow == -1) + gc.attr ^= GRID_ATTR_REVERSE; + if (llen != 0) + screen_write_cursormove(&ctx, llen + 1, yy); + else + screen_write_cursormove(&ctx, 0, yy); + screen_write_putc(&ctx, &gc, '<'); + } + if (rarrow != 0) { + memcpy(&gc, &stdgc, sizeof gc); + if (rarrow == -1) + gc.attr ^= GRID_ATTR_REVERSE; + if (rlen != 0) + screen_write_cursormove(&ctx, c->tty.sx - rlen - 2, yy); + else + screen_write_cursormove(&ctx, c->tty.sx - 1, yy); + screen_write_putc(&ctx, &gc, '>'); + } + + goto out; + +blank: + /* Just draw the whole line as blank. */ + screen_write_start(&ctx, NULL, &c->status); + screen_write_cursormove(&ctx, 0, yy); + for (offset = 0; offset < c->tty.sx; offset++) + screen_write_putc(&ctx, &stdgc, ' '); + + goto out; + +off: + /* + * Draw the real window last line. Necessary to wipe over message if + * status is off. Not sure this is the right place for this. + */ + memcpy(&stdgc, &grid_default_cell, sizeof stdgc); + screen_write_start(&ctx, NULL, &c->status); + + sy = 0; + TAILQ_FOREACH(wp, &s->curw->window->panes, entry) { + sy += wp->sy + 1; + sc = wp->screen; + } + + screen_write_cursormove(&ctx, 0, 0); + if (sy < c->tty.sy) { + /* If the screen is too small, use blank. */ + for (offset = 0; offset < c->tty.sx; offset++) + screen_write_putc(&ctx, &stdgc, ' '); + } else { + screen_write_copy(&ctx, + sc, 0, sc->grid->hsize + screen_size_y(sc) - 1, c->tty.sx, 1); + } + +out: + screen_write_stop(&ctx); + + if (left != NULL) + xfree(left); + if (right != NULL) + xfree(right); + + if (grid_compare(c->status.grid, old_status.grid) == 0) { + screen_free(&old_status); + return (0); + } + screen_free(&old_status); + return (1); +} + +char * +status_replace(struct session *s, char *fmt, time_t t) +{ + struct winlink *wl = s->curw; + static char out[BUFSIZ]; + char in[BUFSIZ], tmp[256], ch, *iptr, *optr, *ptr, *endptr; + char *savedptr; + size_t len; + long n; + + strftime(in, sizeof in, fmt, localtime(&t)); + in[(sizeof in) - 1] = '\0'; + + iptr = in; + optr = out; + savedptr = NULL; + + while (*iptr != '\0') { + if (optr >= out + (sizeof out) - 1) + break; + switch (ch = *iptr++) { + case '#': + errno = 0; + n = strtol(iptr, &endptr, 10); + if ((n == 0 && errno != EINVAL) || + (n == LONG_MIN && errno != ERANGE) || + (n == LONG_MAX && errno != ERANGE) || + n != 0) + iptr = endptr; + if (n <= 0) + n = LONG_MAX; + + ptr = NULL; + switch (*iptr++) { + case '(': + if (ptr == NULL) { + ptr = status_replace_popen(&iptr); + if (ptr == NULL) + break; + savedptr = ptr; + } + /* FALLTHROUGH */ + case 'H': + if (ptr == NULL) { + if (gethostname(tmp, sizeof tmp) != 0) + fatal("gethostname"); + ptr = tmp; + } + /* FALLTHROUGH */ + case 'S': + if (ptr == NULL) + ptr = s->name; + /* FALLTHROUGH */ + case 'T': + if (ptr == NULL) + ptr = wl->window->active->base.title; + len = strlen(ptr); + if ((size_t) n < len) + len = n; + if (optr + len >= out + (sizeof out) - 1) + break; + while (len > 0 && *ptr != '\0') { + *optr++ = *ptr++; + len--; + } + break; + case '#': + *optr++ = '#'; + break; + } + if (savedptr != NULL) { + xfree(savedptr); + savedptr = NULL; + } + break; + default: + *optr++ = ch; + break; + } + } + *optr = '\0'; + + return (xstrdup(out)); +} + +char * +status_replace_popen(char **iptr) +{ + FILE *f; + char *buf, *cmd, *ptr; + int lastesc; + size_t len; + + if (**iptr == '\0') + return (NULL); + if (**iptr == ')') { /* no command given */ + (*iptr)++; + return (NULL); + } + + buf = NULL; + + cmd = xmalloc(strlen(*iptr) + 1); + len = 0; + + lastesc = 0; + for (; **iptr != '\0'; (*iptr)++) { + if (!lastesc && **iptr == ')') + break; /* unescaped ) is the end */ + if (!lastesc && **iptr == '\\') { + lastesc = 1; + continue; /* skip \ if not escaped */ + } + lastesc = 0; + cmd[len++] = **iptr; + } + if (**iptr == '\0') /* no terminating ) */ + goto out; + (*iptr)++; /* skip final ) */ + cmd[len] = '\0'; + + if ((f = popen(cmd, "r")) == NULL) + goto out; + + if ((buf = fgetln(f, &len)) == NULL) { + pclose(f); + goto out; + } + if (buf[len - 1] == '\n') { + buf[len - 1] = '\0'; + buf = xstrdup(buf); + } else { + ptr = xmalloc(len + 1); + memcpy(ptr, buf, len); + ptr[len] = '\0'; + buf = ptr; + } + pclose(f); + +out: + xfree(cmd); + return (buf); +} + +size_t +status_width(struct winlink *wl) +{ + return (xsnprintf(NULL, 0, "%d:%s ", wl->idx, wl->window->name)); +} + +char * +status_print(struct session *s, struct winlink *wl, struct grid_cell *gc) +{ + char *text, flag; + u_char fg, bg, attr; + + fg = options_get_number(&wl->window->options, "window-status-fg"); + if (fg != 8) + gc->fg = fg; + bg = options_get_number(&wl->window->options, "window-status-bg"); + if (bg != 8) + gc->bg = bg; + attr = options_get_number(&wl->window->options, "window-status-attr"); + if (attr != 0) + gc->attr = attr; + + flag = ' '; + if (wl == SLIST_FIRST(&s->lastw)) + flag = '-'; + if (wl == s->curw) + flag = '*'; + + if (session_alert_has(s, wl, WINDOW_ACTIVITY)) { + flag = '#'; + gc->attr ^= GRID_ATTR_REVERSE; + } else if (session_alert_has(s, wl, WINDOW_BELL)) { + flag = '!'; + gc->attr ^= GRID_ATTR_REVERSE; + } else if (session_alert_has(s, wl, WINDOW_CONTENT)) { + flag = '+'; + gc->attr ^= GRID_ATTR_REVERSE; + } + + xasprintf(&text, "%d:%s%c", wl->idx, wl->window->name, flag); + return (text); +} + +void +status_message_set(struct client *c, const char *msg) +{ + struct timeval tv; + int delay; + + delay = options_get_number(&c->session->options, "display-time"); + tv.tv_sec = delay / 1000; + tv.tv_usec = (delay % 1000) * 1000L; + + c->message_string = xstrdup(msg); + if (gettimeofday(&c->message_timer, NULL) != 0) + fatal("gettimeofday"); + timeradd(&c->message_timer, &tv, &c->message_timer); + + c->tty.flags |= (TTY_NOCURSOR|TTY_FREEZE); + c->flags |= CLIENT_STATUS; +} + +void +status_message_clear(struct client *c) +{ + if (c->message_string == NULL) + return; + + xfree(c->message_string); + c->message_string = NULL; + + c->tty.flags &= ~(TTY_NOCURSOR|TTY_FREEZE); + c->flags |= CLIENT_REDRAW; +} + +/* Draw client message on status line of present else on last line. */ +int +status_message_redraw(struct client *c) +{ + struct screen_write_ctx ctx; + struct session *s = c->session; + struct screen old_status; + size_t len; + struct grid_cell gc; + + if (c->tty.sx == 0 || c->tty.sy == 0) + return (0); + memcpy(&old_status, &c->status, sizeof old_status); + screen_init(&c->status, c->tty.sx, 1, 0); + + len = strlen(c->message_string); + if (len > c->tty.sx) + len = c->tty.sx; + + memcpy(&gc, &grid_default_cell, sizeof gc); + gc.bg = options_get_number(&s->options, "message-fg"); + gc.fg = options_get_number(&s->options, "message-bg"); + gc.attr |= options_get_number(&s->options, "message-attr"); + + screen_write_start(&ctx, NULL, &c->status); + + screen_write_cursormove(&ctx, 0, 0); + screen_write_puts(&ctx, &gc, "%.*s", (int) len, c->message_string); + for (; len < c->tty.sx; len++) + screen_write_putc(&ctx, &gc, ' '); + + screen_write_stop(&ctx); + + if (grid_compare(c->status.grid, old_status.grid) == 0) { + screen_free(&old_status); + return (0); + } + screen_free(&old_status); + return (1); +} + +void +status_prompt_set(struct client *c, + const char *msg, int (*fn)(void *, const char *), void *data, int flags) +{ + c->prompt_string = xstrdup(msg); + + c->prompt_buffer = xstrdup(""); + c->prompt_index = 0; + + c->prompt_callback = fn; + c->prompt_data = data; + + c->prompt_hindex = 0; + + c->prompt_flags = flags; + + mode_key_init(&c->prompt_mdata, + options_get_number(&c->session->options, "status-keys"), + MODEKEY_CANEDIT); + + c->tty.flags |= (TTY_NOCURSOR|TTY_FREEZE); + c->flags |= CLIENT_STATUS; +} + +void +status_prompt_clear(struct client *c) +{ + if (c->prompt_string == NULL) + return; + + mode_key_free(&c->prompt_mdata); + + xfree(c->prompt_string); + c->prompt_string = NULL; + + xfree(c->prompt_buffer); + c->prompt_buffer = NULL; + + c->tty.flags &= ~(TTY_NOCURSOR|TTY_FREEZE); + c->flags |= CLIENT_REDRAW; +} + +/* Draw client prompt on status line of present else on last line. */ +int +status_prompt_redraw(struct client *c) +{ + struct screen_write_ctx ctx; + struct session *s = c->session; + struct screen old_status; + size_t i, size, left, len, offset, n; + char ch; + struct grid_cell gc; + + if (c->tty.sx == 0 || c->tty.sy == 0) + return (0); + memcpy(&old_status, &c->status, sizeof old_status); + screen_init(&c->status, c->tty.sx, 1, 0); + offset = 0; + + len = strlen(c->prompt_string); + if (len > c->tty.sx) + len = c->tty.sx; + + memcpy(&gc, &grid_default_cell, sizeof gc); + gc.bg = options_get_number(&s->options, "message-fg"); + gc.fg = options_get_number(&s->options, "message-bg"); + gc.attr |= options_get_number(&s->options, "message-attr"); + + screen_write_start(&ctx, NULL, &c->status); + + screen_write_cursormove(&ctx, 0, 0); + screen_write_puts(&ctx, &gc, "%.*s", (int) len, c->prompt_string); + + left = c->tty.sx - len; + if (left != 0) { + if (c->prompt_index < left) + size = strlen(c->prompt_buffer); + else { + offset = c->prompt_index - left - 1; + if (c->prompt_index == strlen(c->prompt_buffer)) + left--; + size = left; + } + if (c->prompt_flags & PROMPT_HIDDEN) { + n = strlen(c->prompt_buffer); + if (n > left) + n = left; + for (i = 0; i < n; i++) + screen_write_putc(&ctx, &gc, '*'); + } else { + screen_write_puts(&ctx, &gc, + "%.*s", (int) left, c->prompt_buffer + offset); + } + + for (i = len + size; i < c->tty.sx; i++) + screen_write_putc(&ctx, &gc, ' '); + } + + /* Draw a fake cursor. */ + screen_write_cursormove(&ctx, len + c->prompt_index - offset, 0); + if (c->prompt_index == strlen(c->prompt_buffer)) + ch = ' '; + else { + if (c->prompt_flags & PROMPT_HIDDEN) + ch = '*'; + else + ch = c->prompt_buffer[c->prompt_index]; + } + if (ch == '\0') + ch = ' '; + gc.attr ^= GRID_ATTR_REVERSE; + screen_write_putc(&ctx, &gc, ch); + + screen_write_stop(&ctx); + + if (grid_compare(c->status.grid, old_status.grid) == 0) { + screen_free(&old_status); + return (0); + } + screen_free(&old_status); + return (1); +} + +/* Handle keys in prompt. */ +void +status_prompt_key(struct client *c, int key) +{ + struct paste_buffer *pb; + char *s, *first, *last, word[64]; + size_t size, n, off, idx; + + size = strlen(c->prompt_buffer); + switch (mode_key_lookup(&c->prompt_mdata, key)) { + case MODEKEYCMD_LEFT: + if (c->prompt_index > 0) { + c->prompt_index--; + c->flags |= CLIENT_STATUS; + } + break; + case MODEKEYCMD_RIGHT: + if (c->prompt_index < size) { + c->prompt_index++; + c->flags |= CLIENT_STATUS; + } + break; + case MODEKEYCMD_STARTOFLINE: + if (c->prompt_index != 0) { + c->prompt_index = 0; + c->flags |= CLIENT_STATUS; + } + break; + case MODEKEYCMD_ENDOFLINE: + if (c->prompt_index != size) { + c->prompt_index = size; + c->flags |= CLIENT_STATUS; + } + break; + case MODEKEYCMD_COMPLETE: + if (*c->prompt_buffer == '\0') + break; + + idx = c->prompt_index; + if (idx != 0) + idx--; + + /* Find the word we are in. */ + first = c->prompt_buffer + idx; + while (first > c->prompt_buffer && *first != ' ') + first--; + while (*first == ' ') + first++; + last = c->prompt_buffer + idx; + while (*last != '\0' && *last != ' ') + last++; + while (*last == ' ') + last--; + if (*last != '\0') + last++; + if (last <= first || + ((size_t) (last - first)) > (sizeof word) - 1) + break; + memcpy(word, first, last - first); + word[last - first] = '\0'; + + /* And try to complete it. */ + if ((s = status_prompt_complete(word)) == NULL) + break; + + /* Trim out word. */ + n = size - (last - c->prompt_buffer) + 1; /* with \0 */ + memmove(first, last, n); + size -= last - first; + + /* Insert the new word. */ + size += strlen(s); + off = first - c->prompt_buffer; + c->prompt_buffer = xrealloc(c->prompt_buffer, 1, size + 1); + first = c->prompt_buffer + off; + memmove(first + strlen(s), first, n); + memcpy(first, s, strlen(s)); + + c->prompt_index = (first - c->prompt_buffer) + strlen(s); + + c->flags |= CLIENT_STATUS; + break; + case MODEKEYCMD_BACKSPACE: + if (c->prompt_index != 0) { + if (c->prompt_index == size) + c->prompt_buffer[--c->prompt_index] = '\0'; + else { + memmove(c->prompt_buffer + c->prompt_index - 1, + c->prompt_buffer + c->prompt_index, + size + 1 - c->prompt_index); + c->prompt_index--; + } + c->flags |= CLIENT_STATUS; + } + break; + case MODEKEYCMD_DELETE: + if (c->prompt_index != size) { + memmove(c->prompt_buffer + c->prompt_index, + c->prompt_buffer + c->prompt_index + 1, + size + 1 - c->prompt_index); + c->flags |= CLIENT_STATUS; + } + break; + case MODEKEYCMD_UP: + if (server_locked) + break; + + if (ARRAY_LENGTH(&c->prompt_hdata) == 0) + break; + xfree(c->prompt_buffer); + + c->prompt_buffer = xstrdup(ARRAY_ITEM(&c->prompt_hdata, + ARRAY_LENGTH(&c->prompt_hdata) - 1 - c->prompt_hindex)); + if (c->prompt_hindex != ARRAY_LENGTH(&c->prompt_hdata) - 1) + c->prompt_hindex++; + + c->prompt_index = strlen(c->prompt_buffer); + c->flags |= CLIENT_STATUS; + break; + case MODEKEYCMD_DOWN: + if (server_locked) + break; + + xfree(c->prompt_buffer); + + if (c->prompt_hindex != 0) { + c->prompt_hindex--; + c->prompt_buffer = xstrdup(ARRAY_ITEM( + &c->prompt_hdata, ARRAY_LENGTH( + &c->prompt_hdata) - 1 - c->prompt_hindex)); + } else + c->prompt_buffer = xstrdup(""); + + c->prompt_index = strlen(c->prompt_buffer); + c->flags |= CLIENT_STATUS; + break; + case MODEKEYCMD_PASTE: + if ((pb = paste_get_top(&c->session->buffers)) == NULL) + break; + if ((last = strchr(pb->data, '\n')) == NULL) + last = strchr(pb->data, '\0'); + n = last - pb->data; + + c->prompt_buffer = xrealloc(c->prompt_buffer, 1, size + n + 1); + if (c->prompt_index == size) { + memcpy(c->prompt_buffer + c->prompt_index, pb->data, n); + c->prompt_index += n; + c->prompt_buffer[c->prompt_index] = '\0'; + } else { + memmove(c->prompt_buffer + c->prompt_index + n, + c->prompt_buffer + c->prompt_index, + size + 1 - c->prompt_index); + memcpy(c->prompt_buffer + c->prompt_index, pb->data, n); + c->prompt_index += n; + } + + c->flags |= CLIENT_STATUS; + break; + case MODEKEYCMD_CHOOSE: + if (*c->prompt_buffer != '\0') { + status_prompt_add_history(c); + if (c->prompt_callback( + c->prompt_data, c->prompt_buffer) == 0) + status_prompt_clear(c); + break; + } + /* FALLTHROUGH */ + case MODEKEYCMD_QUIT: + if (c->prompt_callback(c->prompt_data, NULL) == 0) + status_prompt_clear(c); + break; + case MODEKEYCMD_OTHERKEY: + if (key < 32 || key > 126) + break; + c->prompt_buffer = xrealloc(c->prompt_buffer, 1, size + 2); + + if (c->prompt_index == size) { + c->prompt_buffer[c->prompt_index++] = key; + c->prompt_buffer[c->prompt_index] = '\0'; + } else { + memmove(c->prompt_buffer + c->prompt_index + 1, + c->prompt_buffer + c->prompt_index, + size + 1 - c->prompt_index); + c->prompt_buffer[c->prompt_index++] = key; + } + + if (c->prompt_flags & PROMPT_SINGLE) { + if (c->prompt_callback( + c->prompt_data, c->prompt_buffer) == 0) + status_prompt_clear(c); + } + + c->flags |= CLIENT_STATUS; + break; + default: + break; + } +} + +/* Add line to the history. */ +void +status_prompt_add_history(struct client *c) +{ + if (server_locked) + return; + + if (ARRAY_LENGTH(&c->prompt_hdata) > 0 && + strcmp(ARRAY_LAST(&c->prompt_hdata), c->prompt_buffer) == 0) + return; + + if (ARRAY_LENGTH(&c->prompt_hdata) == PROMPT_HISTORY) { + xfree(ARRAY_FIRST(&c->prompt_hdata)); + ARRAY_REMOVE(&c->prompt_hdata, 0); + } + + ARRAY_ADD(&c->prompt_hdata, xstrdup(c->prompt_buffer)); +} + +/* Complete word. */ +char * +status_prompt_complete(const char *s) +{ + const struct cmd_entry **cmdent; + const struct set_option_entry *optent; + ARRAY_DECL(, const char *) list; + char *prefix, *s2; + u_int i; + size_t j; + + if (*s == '\0') + return (NULL); + + /* First, build a list of all the possible matches. */ + ARRAY_INIT(&list); + for (cmdent = cmd_table; *cmdent != NULL; cmdent++) { + if (strncmp((*cmdent)->name, s, strlen(s)) == 0) + ARRAY_ADD(&list, (*cmdent)->name); + } + for (i = 0; i < NSETOPTION; i++) { + optent = &set_option_table[i]; + if (strncmp(optent->name, s, strlen(s)) == 0) + ARRAY_ADD(&list, optent->name); + } + for (i = 0; i < NSETWINDOWOPTION; i++) { + optent = &set_window_option_table[i]; + if (strncmp(optent->name, s, strlen(s)) == 0) + ARRAY_ADD(&list, optent->name); + } + + /* If none, bail now. */ + if (ARRAY_LENGTH(&list) == 0) { + ARRAY_FREE(&list); + return (NULL); + } + + /* If an exact match, return it, with a trailing space. */ + if (ARRAY_LENGTH(&list) == 1) { + xasprintf(&s2, "%s ", ARRAY_FIRST(&list)); + ARRAY_FREE(&list); + return (s2); + } + + /* Now loop through the list and find the longest common prefix. */ + prefix = xstrdup(ARRAY_FIRST(&list)); + for (i = 1; i < ARRAY_LENGTH(&list); i++) { + s = ARRAY_ITEM(&list, i); + + j = strlen(s); + if (j > strlen(prefix)) + j = strlen(prefix); + for (; j > 0; j--) { + if (prefix[j - 1] != s[j - 1]) + prefix[j - 1] = '\0'; + } + } + + ARRAY_FREE(&list); + return (prefix); +} diff --git a/usr.bin/tmux/tmux.1 b/usr.bin/tmux/tmux.1 new file mode 100644 index 00000000000..5fd91427a07 --- /dev/null +++ b/usr.bin/tmux/tmux.1 @@ -0,0 +1,1329 @@ +.\" $OpenBSD: tmux.1,v 1.1 2009/06/01 22:58:49 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. +.\" +.Dd April 20, 2009 +.Dt TMUX 1 +.Os +.Sh NAME +.Nm tmux +.Nd "terminal multiplexer" +.Sh SYNOPSIS +.Nm tmux +.Bk -words +.Op Fl 28dqUuv +.Op Fl f Ar file +.Op Fl L Ar socket-name +.Op Fl S Ar socket-path +.Op Ar command Op Ar flags +.Ek +.Sh DESCRIPTION +.Nm +is a terminal multiplexer; it enables a number of terminals to be accessed and +controlled from a single terminal. +.Pp +.Nm +runs as a server-client system. +A server is created automatically when necessary and holds a number of +.Em sessions , +each of which may have a number of +.Em windows +linked to it. +A window may be split on screen into one or more +.Em panes , +each of which is a separate terminal. +Any number of +.Em clients +may connect to a session, or the server +may be controlled by issuing commands with +.Nm . +Communication takes place through a socket, by default placed in +.Pa /tmp . +.Pp +The options are as follows: +.Bl -tag -width "XXXXXXXXXXXX" +.It Fl 2 +Force +.Nm +to assume the terminal supports 256 colours. +.It Fl 8 +Like +.Fl 2 , +indicates the terminal supports 88 colours. +.It Fl d +Force +.Nm +to assume the terminal supports default colours. +.It Fl f Ar file +Specify an alternative configuration file. +By default, +.Nm +will look for a config file at +.Pa ~/.tmux.conf . +The configuration file is a set of +.Nm +commands which are executed in sequence when the server is first started. +.It Fl q +Prevent the server sending various information messages, for example when +window flags are altered. +.It Fl L Ar socket-name +.Nm +stores the server socket in a directory under +.Pa /tmp ; +the default socket is named +.Em default . +This option allows a different socket name to be specified, allowing several +independent +.Nm +servers to be run. +Unlike +.Fl S +a full path is not necessary: the sockets are all created in the same +directory. +.It Fl S Ar socket-path +Specify a full alternative path to the server socket. +If +.Fl S +is specified, the default socket directory is not used and any +.Fl L +flag is ignored. +.It Fl U +Unlock the server. +.It Fl u +Instruct +.Nm +that the terminal support UTF-8. +.It Fl v +Request verbose logging. +This option may be specified multiple times for increasing verbosity. +Log messages will be saved into +.Pa tmux-client-PID.log +and +.Pa tmux-server-PID.log +files in the current directory, where +.Em PID +is the pid of the server or client process. +.It Ar command Op Ar flags +This specifies one of a set of commands used to control +.Nm , +and described in the following sections. +If no command and flags is specified, the +.Ic new-session +command is assumed. +.Pp +.El +.Sh QUICK START +To create a new tmux session running +.Xr vi 1 : +.Pp +.Dl $ tmux new-session vi +.Pp +Most commands have a shorter form, known as an alias. +For new-session, this is +.Ic new : +.Pp +.Dl $ tmux new vi +.Pp +Alternatively, the shortest unambiguous form of a command is accepted. +If there are several options, they are listed: +.Bd -literal -offset indent +$ tmux n +ambiguous command: n, could be: new-session, new-window, next-window +$ +.Ed +.Pp +Within an active session, a new window may be created by typing +.Ql C-b +(ctrl-b, known as the prefix key) +followed by the +.Ql c +key. +.Pp +Windows may be navigated with: +.Ql C-b 0 +(to select window 0), +.Ql C-b 1 +(to select window 1), and so on; +.Ql C-b n +to select the next window; and +.Ql C-b p +to select the previous window. +.Pp +A session may be detached using +.Ql C-b d +and reattached with: +.Pp +.Dl $ tmux attach-session +.Pp +Typing +.Ql C-b \&? +lists the current key bindings in the current window; up and down may be used +to navigate the list or +.Ql Q +to exit from it. +.Sh KEY BINDINGS +.Nm +may be controlled from an attached client by using a key combination of a +prefix key, +.Ql C-b +(ctrl-b) by default, followed by a command key. +.Pp +Some of the default key bindings include: +.Pp +.Bl -tag -width Ds -compact +.It Ql d +Detach current client. +.It Ql c +Create new window. +.It Ql n +Change to next window in the current session. +.It Ql p +Change to previous window in the current session. +.It Ql l +Move to last (previously selected) window in the current session. +.It Ql t +Display a large clock. +.It Ql \&? +List current key bindings. +.El +.Pp +A complete list may be obtained with the +.Ic list-keys +command (bound to +.Ql \&? +by default). +Key bindings may be changed with the +.Ic bind-key +and +.Ic unbind-key +commands. +.Sh HISTORY +.Nm +maintains a configurable history buffer for each window. +By default, up to 2000 lines are kept, this can be altered with the +.Ic history-limit +option (see the +.Ic set-option +command below). +.Sh MODES +A +.Nm +window may be in one of several modes. +The default permits direct access to the terminal attached to the window. +The others are: +.Bl -tag -width Ds +.It Em output mode +This is entered when a command which produces output, such as +.Ic list-keys , +is executed from a key binding. +.It Em scroll mode +This is entered with the +.Ic scroll-mode +command (bound to +.Ql = +by default) and permits the window history buffer to be inspected. +.It Em copy mode +This permits a section of a window or its history to be copied to a +.Em paste buffer +for later insertion into another window. +This mode is entered with the +.Ic copy-mode +command, bound to +.Ql [ +by default. +.El +.Pp +The keys available depend on whether +.Xr emacs 1 +or +.Xr vi 1 +mode is selected (see the +.Ic mode-keys +option). +The following keys are supported as appropriate for the mode: +.Bl -column "FunctionXXXXXXXXXXXX" "viXXXXXX" "emacs" -offset indent +.It Sy "Function" Ta Sy "vi" Ta Sy "emacs" +.It Li "Start of line" Ta "0 or ^" Ta "C-a" +.It Li "Clear selection" Ta "Escape" Ta "C-g" +.It Li "Copy selection" Ta "Enter" Ta "M-w" +.It Li "Cursor down" Ta "j" Ta "Down" +.It Li "End of line" Ta "$" Ta "C-e" +.It Li "Cursor left" Ta "h" Ta "Left" +.It Li "Next page" Ta "C-f" Ta "Page down" +.It Li "Next word" Ta "w" Ta "M-f" +.It Li "Previous page" Ta "C-u" Ta "Page up" +.It Li "Previous word" Ta "b" Ta "M-b" +.It Li "Quit mode" Ta "q" Ta "Escape" +.It Li "Cursor right" Ta "l" Ta "Right" +.It Li "Start selection" Ta "Space" Ta "C-Space" +.It Li "Cursor up" Ta "k" Ta "Up" +.El +.Pp +.Sh BUFFERS +.Nm +maintains a stack of +.Em paste buffers +for each session. +Up to the value of the +.Ic buffer-limit +option are kept; when a new buffer is added, the buffer at the bottom of the +stack is removed. +Buffers may be added using +.Ic copy-mode +or the +.Ic set-buffer +command, and pasted into a window using the +.Ic paste-buffer +command. +.Sh PANES AND LAYOUTS +Each window displayed by +.Nm +may be split into one or more +.Em panes ; +each pane takes up a certain area of the display and is a separate terminal. +A window may be split into panes using the +.Ic split-window +command. +.Pp +Panes are numbered beginning from zero; in horizontal layouts zero is the +leftmost pane and in vertical the topmost. +.Pp +Panes may be arranged using several layouts. +The layout may be cycled with the +.Ic next-layout +command (bound to +.Ql C-space +by default), the current pane may be changed with the +.Ic up-pane +and +.Ic down-pane +commands and the +.Ic rotate-window +and +.Ic swap-pane +commands may be used to swap panes without changing the window layout. +.Pp +The following layouts are supported: +.Bl -tag -width Ds +.It Ic manual +Manual layout splits windows vertically (running across); only with this layout +may panes be resized using the +.Ic resize-pane +command. +.It Ic active-only +Only the active pane is shown - all other panes are hidden. +.It Ic even-horizontal +Panes are spread out evenly from left to right across the window. +.It Ic even-vertical +Panes are spread evenly from top to bottom. +.It Ic main-vertical +A large (81 column) pane is shown on the left of the window and the remaining +panes are spread from top to bottom in the leftover space to the right. +.El +.Sh COMMANDS +This section contains a list of the commands supported by +.Nm . +Most commands accept the optional +.Fl t +argument with one of +.Ar target-client , +.Ar target-session +or +.Ar target-window . +These specify the client, session or window which a command should affect. +.Ar target-client +is the name of the +.Xr pty 4 +file to which the client is connected, for example +.Pa /dev/ttyp1 . +Clients may be listed with the +.Ic list-clients +command. +.Pp +.Ar target-session +is either the name of a session (as listed by the +.Ic list-sessions +command); or the name of a client as for +.Ar target-client , +in this case, the session attached to the client is used. +An +.Xr fnmatch 3 +pattern may be used to match the session name. +If a session is omitted when required, +.Nm tmux +attempts to use the current session; if no current session is available, the +most recently created is chosen. +If no client is specified, the current client is chosen, if possible, or an +error is reported. +.Pp +.Ar target-window +specifies a window in the form +.Em session Ns \&: Ns Em index , +for example mysession:1. +The session is in the same form as for +.Ar target-session . +.Em session , +.Em index +or both may be omitted. +If +.Em session +is omitted, the same rules as for +.Ar target-session +are followed; if +.Em index +is not present, the current window for the given session is used. +When the argument does not contain a colon (:), +.Nm +first attempts to parse it as window index; if that fails, an attempt is made +to match a session or client name. +.Pp +Multiple commands may be specified together as part of a +.Em command sequence . +Each command should be separated by spaces and a semicolon +.Eo ( Ql \& \&; \& Ec ) ; +commands are executed sequentially from left to right. +A literal semicolon may be included by escaping it with a backslash (for +example, when specifying a command sequence to +.Ic bind-key ) . +.Pp +Examples include: +.Pp +.Bd -literal -offset indent +refresh-client -t/dev/ttyp2 + +rename-session -tfirst newname + +set-window-option -t:0 monitor-activity on + +new-window ; split-window -d + +bind-key D detach-client \e\; lock-server +.Ed +.Pp +The following commands are available: +.Bl -tag -width Ds +.It Xo Ic attach-session +.Op Fl d +.Op Fl t Ar target-session +.Xc +.D1 (alias: Ic attach ) +Create a new client in the current terminal and attach it to a session. +If +.Fl d +is specified, any other clients attached to the session are detached. +.Pp +If no server is started, +.Ic attach-session +will attempt to start it; this will fail unless sessions are created in the +configuration file. +.It Xo Ic bind-key +.Op Fl r +.Ar key Ar command Op Ar arguments +.Xc +.D1 (alias: Ic bind ) +Bind key +.Ar key +to +.Ar command . +Keys may be specified prefixed with +.Ql C- +or +.Ql ^ +for ctrl keys, or +.Ql M- +for alt (meta) keys. +The +.Fl r +flag indicates this key may repeat, see the +.Ic repeat-time +option. +.It Xo Ic break-pane +.Op Fl d +.Op Fl p Ar pane-index +.Op Fl t Ar target-window +.Xc +.D1 (alias: Ic breakp) +Break the current pane off from its containing window to make it the only pane +in a new window. +If +.Fl d +is given, the new window does not become the current window. +.It Xo Ic choose-session +.Op Fl t Ar target-window +.Xc +Put a window into session choice mode, where the session for the current +client may be selected interactively from a list. +This command works only from inside +.Nm . +.It Xo Ic choose-window +.Op Fl t Ar target-window +.Xc +Put a window into window choice mode, where the window for the session +attached to the current client may be selected interactively from a list. +This command works only from inside +.Nm . +.It Xo Ic clock-mode +.Op Fl t Ar target-window +.Xc +Display a large clock. +.It Xo Ic command-prompt +.Op Fl t Ar target-client +.Op Ar template +.Xc +Open the command prompt in a client. +This may be used from inside +.Nm +to execute commands interactively. +If +.Ar template +is specified, it is used as the command; any %% in the template will be +replaced by what is entered at the prompt. +.It Xo Ic confirm-before +.Op Fl t Ar target-client +.Ar command +.Xc +.D1 (alias: Ic confirm) +Ask for confirmation before executing +.Ar command . +This command works only from inside +.Nm . +.It Xo Ic copy-buffer +.Op Fl a Ar src-index +.Op Fl b Ar dst-index +.Op Fl s Ar src-session +.Op Fl t Ar dst-session +.Xc +.D1 (alias: Ic copyb) +Copy a session paste buffer to another session. +If no sessions are specified, the current one is used instead. +.It Xo Ic copy-mode +.Op Fl u +.Op Fl t Ar target-window +.Xc +Enter copy mode. +The +.Fl u +option scrolls one page up. +.It Xo Ic delete-buffer +.Op Fl b Ar buffer-index +.Op Fl t Ar target-session +.Xc +.D1 (alias: Ic deleteb ) +Delete the buffer at +.Ar buffer-index , +or the top buffer if not specified. +.It Xo Ic detach-client +.Op Fl t Ar target-client +.Xc +.D1 (alias: Ic detach ) +Detach the current client if bound to a key, or the specified client with +.Fl t . +.It Xo Ic down-pane +.Op Fl p Ar pane-index +.Op Fl t Ar target-window +.Xc +.D1 (alias: Ic downp ) +Move down a pane. +.It Xo Ic find-window +.Op Fl t Ar target-window +.Ar match-string +.Xc +.D1 (alias: Ic findw ) +Search for +.Ar match-string +in window names, titles, and visible content (but not history). +If only one window is matched, it'll be automatically selected, otherwise a +choice list is shown. +This command only works from inside +.Nm . +.It Xo Ic has-session +.Op Fl t Ar target-session +.Xc +.D1 (alias: Ic has ) +Report an error and exit with 1 if the specified session does not exist. +If it does exist, exit with 0. +.It Xo Ic kill-pane +.Op Fl p Ar pane-index +.Op Fl t Ar target-window +.Xc +.D1 (alias: Ic killp ) +Destroy the given pane. +.It Xo Ic kill-server +.Xc +Kill the +.Nm +server and clients and destroy all sessions. +.It Xo Ic kill-session +.Op Fl t Ar target-session +.Xc +Destroy the given session, closing any windows linked to it and no other +sessions, and detaching all clients attached to it. +.It Xo Ic kill-window +.Op Fl t Ar target-window +.Xc +.D1 (alias: Ic killw ) +Kill the current window or the window at +.Ar target-window , +removing it from any sessions to which it is linked. +.It Xo Ic last-window +.Op Fl t Ar target-session +.Xc +.D1 (alias: Ic last ) +Select the last (previously selected) window. +If no +.Ar target-session +is specified, select the last window of the current session. +.It Xo Ic link-window +.Op Fl dk +.Op Fl s Ar src-window +.Op Fl t Ar dst-window +.Xc +.D1 (alias: Ic linkw ) +Link the window at +.Ar src-window +to the specified +.Ar dst-window . +If +.Ar dst-window +is specified and no such window exists, the +.Ar src-window +is linked there. +If +.Fl k +is given and +.Ar dst-window +exists, it is killed, otherwise an error is generated. +If +.Fl d +is given, the newly linked window is not selected. +.It Xo Ic list-buffers +.Op Fl t Ar target-session +.Xc +.D1 (alias: Ic lsb ) +List the buffers in the given session. +.It Xo Ic list-clients +.Xc +.D1 (alias: Ic lsc ) +List all clients attached to the server. +.It Xo Ic list-commands +.Xc +.D1 (alias: Ic lscm ) +List the syntax of all commands supported by +.Nm . +.It Xo Ic list-keys +.Xc +.D1 (alias: Ic lsk ) +List all key bindings. +.It Xo Ic list-sessions +.Xc +.D1 (alias: Ic ls ) +List all sessions managed by the server. +.It Xo Ic list-windows +.Op Fl t Ar target-session +.Xc +.D1 (alias: Ic lsw ) +List windows in the current session or in +.Ar target-session . +.It Xo Ic load-buffer +.Op Fl b Ar buffer-index +.Op Fl t Ar target-session +.Ar path +.Xc +.D1 (alias: Ic loadb ) +Load the contents of the specified paste buffer from +.Ar path . +.It Xo Ic lock-server +.Xc +.D1 (alias: Ic lock ) +Lock the server until a password is entered. +.It Xo Ic move-window +.Op Fl d +.Op Fl s Ar src-window +.Op Fl t Ar dst-window +.Xc +.D1 (alias: Ic movew ) +This is similar to +.Ic link-window , +except the window at +.Ar src-window +is moved to +.Ar dst-window . +.It Xo Ic new-session +.Op Fl d +.Op Fl n Ar window-name +.Op Fl s Ar session-name +.Op Ar command +.Xc +.D1 (alias: Ic new ) +Create a new session with name +.Ar session-name . +The new session is attached to the current terminal unless +.Fl d +is given. +.Ar window-name +and +.Ar command +are the name of and command to execute in the initial window. +.It Xo Ic new-window +.Op Fl d +.Op Fl n Ar window-name +.Op Fl t Ar target-window +.Op Ar command +.Xc +.D1 (alias: Ic neww ) +Create a new window. +If +.Fl d +is given, the session does not make the new window the current window. +.Ar target-window +represents the window to be created. +.Ar command +is the command to execute. +If +.Ar command +is not specified, the default command is used. +.Pp +The +.Ev TERM +environment variable must be set to +.Dq screen +for all programs running +.Em inside +.Nm . +New windows will automatically have +.Dq TERM=screen +added to their environment, but care must be taken not to reset this in shell +start-up files. +.It Xo Ic next-layout +.Op Fl t Ar target-window +.Xc +.D1 (alias: Ic nextl ) +Move a window to the next layout and rearrange the panes to fit. +.It Xo Ic next-window +.Op Fl t Ar target-session +.Xc +.D1 (alias: Ic next ) +Move to the next window in the session. +.It Xo Ic paste-buffer +.Op Fl d +.Op Fl b Ar buffer-index +.Op Fl t Ar target-window +.Xc +.D1 (alias: Ic pasteb ) +Insert the contents of a paste buffer into the current window. +.It Xo Ic previous-window +.Op Fl t Ar target-session +.Xc +.D1 (alias: Ic prev ) +Move to the previous window in the session. +.It Xo Ic refresh-client +.Op Fl t Ar target-client +.Xc +.D1 (alias: Ic refresh ) +Refresh the current client if bound to a key, or a single client if one is given +with +.Fl t . +.It Xo Ic rename-session +.Op Fl t Ar target-session +.Ar new-name +.Xc +.D1 (alias: Ic rename ) +Rename the session to +.Ar new-name . +.It Xo Ic rename-window +.Op Fl t Ar target-window +.Ar new-name +.Xc +.D1 (alias: Ic renamew ) +Rename the current window, or the window at +.Ar target-window +if specified, to +.Ar new-name . +.It Xo Ic resize-pane +.Op Fl DU +.Op Fl p Ar pane-index +.Op Fl t Ar target-window +.Op Ar adjustment +.Xc +.D1 (alias: Ic resizep ) +Resize a pane, upward with +.Fl U +(the default) or downward with +.Fl D . +The +.Ar adjustment +is given in lines (the default is 1). +.It Xo Ic respawn-window +.Op Fl k +.Op Fl t Ar target-window +.Op Ar command +.Xc +.D1 (alias: Ic respawnw ) +Reactive a window in which the command has exited (see the +.Ic remain-on-exit +window option). +If +.Ar command +is not given, the command used when the window was created is executed. +The window must be already inactive, unless +.Fl k +is given, in which case any existing command is killed. +.It Xo Ic rotate-window +.Op Fl DU +.Op Fl t Ar target-window +.Xc +.D1 (alias: Ic rotatew ) +Rotate the positions of the panes within a window, either upward (numerically +lower) with +.Fl U +or downward (numerically higher). +.It Xo Ic save-buffer +.Op Fl a +.Op Fl b Ar buffer-index +.Op Fl t Ar target-session +.Ar path +.Xc +.D1 (alias: Ic saveb ) +Save the contents of the specified paste buffer to +.Ar path . +The +.Fl a +option appends to rather than overwriting the file. +.It Xo Ic scroll-mode +.Op Fl u +.Op Fl t Ar target-window +.Xc +Enter scroll mode. +The +.Fl u +has the same meaning as in the +.Ic copy-mode +command. +.It Xo Ic select-pane +.Op Fl p Ar pane-index +.Op Fl t Ar target-window +.Xc +.D1 (alias: Ic selectp ) +Make pane +.Ar pane-index +the active pane in window +.Ar target-window . +.It Xo Ic select-prompt +.Op Fl t Ar target-client +.Xc +Open a prompt inside +.Ar target-client +allowing a window index to be entered interactively. +.It Xo Ic select-window +.Op Fl t Ar target-window +.Xc +.D1 (alias: Ic selectw ) +Select the window at +.Ar target-window . +.It Xo Ic send-keys +.Op Fl t Ar target-window +.Ar key Ar ... +.Xc +.D1 (alias: Ic send ) +Send a key or keys to a window. +Each argument +.Ar key +is the name of the key (such as +.Ql C-a +or +.Ql npage +) to send; if the string is not recognised as a key, it is sent as a series of +characters. +All arguments are sent sequentially from first to last. +.It Xo Ic send-prefix +.Op Fl t Ar target-window +.Xc +Send the prefix key to a window as if it was pressed. +.It Xo Ic server-info +.Xc +.D1 (alias: Ic info ) +Show server information and terminal details. +.It Xo Ic set-buffer +.Op Fl b Ar buffer-index +.Op Fl t Ar target-session +.Ar data +.Xc +.D1 (alias: Ic setb ) +Set the contents of the specified buffer to +.Ar data . +.It Xo Ic set-option +.Op Fl gu +.Op Fl t Ar target-session +.Ar option Ar value +.Xc +.D1 (alias: Ic set ) +Set an option. +If +.Fl g +is specified, the option is set as a global option. +Global options apply to all sessions which don't have the option explicitly +set. +If +.Fl g +is not used, the option applies only to +.Ar target-session . +The +.Fl u +flag unsets an option, so a session inherits the option from the global +options - it is not possible to unset a global option. +.Pp +Possible options are: +.Bl -tag -width Ds +.It Xo Ic bell-action +.Op Ic any | Ic none | Ic current +.Xc +Set action on window bell. +.Ic any +means a bell in any window linked to a session causes a bell in the current +window of that session, +.Ic none +means all bells are ignored and +.Ic current +means only bell in windows other than the current window are ignored. +.It Ic buffer-limit Ar number +Set the number of buffers kept for each session; as new buffers are added to +the top of the stack, old ones are removed from the bottom if necessary to +maintain this maximum length. +.It Ic default-command Ar command +Set the command used for new windows (if not specified when the window is +created) to +.Ar command . +The default is +.Dq exec $SHELL . +.It Ic default-path Ar path +Set the default working directory for processes created from keys, or +interactively from the prompt. +The default is the current working directory when the server is started. +.It Ic history-limit Ar lines +Set the maximum number of lines held in window history. +This setting applies only to new windows - existing window histories are not +resized and retain the limit at the point they were created. +.It Ic lock-after-time Ar number +Lock the server after +.Ar number +seconds of inactivity. +The default is off (set to 0). +This has no effect as a session option; it must be set as a global option using +.Fl g . +.It Ic message-attr Ar attributes +Set status line message attributes, where +.Ar attributes +is either +.Ic default +or a comma-delimited list of one or more of: +.Ic bright +(or +.Ic bold ) , +.Ic dim , +.Ic underscore , +.Ic blink , +.Ic reverse , +.Ic hidden , +or +.Ic italics . +.It Ic message-bg Ar colour +Set status line message background colour, where +.Ar colour +is one of: +.Ic black , +.Ic red , +.Ic green , +.Ic yellow , +.Ic blue , +.Ic magenta , +.Ic cyan , +.Ic white +or +.Ic default . +.It Ic message-fg Ar colour +Set status line message foreground colour. +.It Ic prefix Ar key +Set the current prefix key. +.It Ic repeat-time Ar number +Allow multiple commands to be entered without pressing the prefix-key again +in the specified +.Ar number +milliseconds (the default is 500). +Whether a key repeats may be set when it is bound using the +.Fl r +flag to +.Ic bind-key . +Repeat is enabled for the default keys of the +.Ic up-pane , +.Ic down-pane , +.Ic resize-pane-up , +and +.Ic resize-pane-down +commands. +.It Xo Ic set-remain-on-exit +.Op Ic on | Ic off +.Xc +Set the +.Ic remain-on-exit +window option for any windows first created in this session. +.It Xo Ic set-titles +.Op Ic on | Ic off +.Xc +Attempt to set the window title using the \ee]2;...\e007 xterm code and +the terminal appears to be an xterm. +This option is enabled by default. +Note that +.Xr elinks 1 +will only attempt to set the window title if the STY environment +variable is set. +.It Xo Ic status +.Op Ic on | Ic off +.Xc +Show or hide the status line. +.It Ic status-attr Ar attributes +Set status line attributes. +.It Ic status-bg Ar colour +Set status line background colour. +.It Ic status-fg Ar colour +Set status line foreground colour. +.It Ic status-interval Ar interval +Update the status bar every +.Ar interval +seconds. +By default, updates will occur every 15 seconds. +A setting of zero disables redrawing at interval. +.It Xo Ic status-keys +.Op Ic vi | Ic emacs +.Xc +Use +.Xr vi 1 - +or +.Xr emacs 1 -style +key bindings in the status line, for example at the command prompt. +Defaults to emacs. +.It Ic status-left Ar string +Display +.Ar string +to the left of the status bar. +.Ar string +will be passed through +.Xr strftime 3 +before being used. +By default, the session name is shown. +.Ar string +may contain any of the following special character pairs: +.Bl -column "Character pair" "Replaced with" -offset indent +.It Sy "Character pair" Ta Sy "Replaced with" +.It Li "#(command)" Ta "First line of command's output" +.It Li "#H" Ta "Hostname of local host" +.It Li "#S" Ta "Session name" +.It Li "#T" Ta "Current window title" +.It Li "##" Ta "A literal" Ql # +.El +.Pp +Where appropriate, these may be prefixed with a number to specify the maximum +length, for example +.Ql #24T . +.It Ic status-left-length Ar length +Set the maximum +.Ar length +of the left component of the status bar. +The default is 10. +.It Ic status-right Ar string +Display +.Ar string +to the right of the status bar. +By default, the date and time will be shown. +As with +.Ic status-left , +.Ar string +will be passed to +.Xr strftime 3 +and character pairs are replaced. +.It Ic status-right-length Ar length +Set the maximum +.Ar length +of the right component of the status bar. +The default is 40. +.El +.It Xo Ic set-password +.Op Fl c +.Ar password +.Xc +.D1 (alias: Ic pass ) +Set the server password. +If the +.Fl c +option is given, a pre-encrypted password may be specified. +By default, the password is blank, thus any entered password will be accepted +when unlocking the server (see the +.Ic lock-server +command). +To prevent variable expansion when an encrypted password is read from a +configuration file, enclose it in single quotes ('). +.It Xo Ic set-window-option +.Op Fl gu +.Op Fl t Ar target-window +.Ar option Ar value +.Xc +.D1 (alias: Ic setw ) +Set a window-specific option. +The +.Fl g +and +.Fl u +flags work similarly to the +.Ic set-option +command. +.Pp +Supported options are: +.Bl -tag -width Ds +.It Xo Ic aggressive-resize +.Op Ic on | Ic off +.Xc +Aggressively resize the chosen window. +This means that +.Nm +will resize the window to the size of the smallest session for which it is the +current window, rather than the smallest session to which it is attached. +The window may resize when the current window is changed on another sessions; +this option is good for full-screen programs which support SIGWINCH and poor for +interactive programs such as shells. +.It Xo Ic automatic-rename +.Op Ic on | Ic off +.Xc +Control automatic window renaming. +When this setting is enabled, +.Nm +will attempt - on supported platforms - to rename the window to reflect the +command currently running in it. +This flag is automatically disabled for an individual window when a name +is specified at creation with +.Ic new-window or +.Ic new-session , +or later with +.Ic rename-window . +It may be switched off globally with: +.Bd -literal -offset indent +set-window-option -g automatic-rename off +.Ed +.It Ic clock-mode-colour Ar colour +Set clock colour. +.It Xo Ic clock-mode-style +.Op Ic 12 | Ic 24 +.Xc +Set clock hour format. +.It Ic force-height Ar height +.It Ic force-width Ar width +Prevent +.Nm +from resizing a window to greater than +.Ar width +or +.Ar height . +A value of zero restores the default unlimited setting. +.It Ic mode-attr Ar attributes +Set window modes attributes. +.It Ic mode-bg Ar colour +Set window modes background colour. +.It Ic mode-fg Ar colour +Set window modes foreground colour. +.It Xo Ic mode-keys +.Op Ic vi | Ic emacs +.Xc +Use +.Xr vi 1 - +or +.Xr emacs 1 -style +key bindings in scroll and copy modes. +Key bindings default to emacs. +.It Xo Ic monitor-activity +.Op Ic on | Ic off +.Xc +Monitor for activity in the window. +Windows with activity are highlighted in the status line. +.It Xo Ic monitor-content Ar match-string +.Xc +Monitor content in the window. When +.Ar match-string +appears in the window, it is highlighted in the status line. +.It Xo Ic remain-on-exit +.Op Ic on | Ic off +.Xc +A window with this flag set is not destroyed when the program running in it +exits. +The window may be reactivated with the +.Ic respawn-window +command. +.It Xo Ic utf8 +.Op Ic on | Ic off +.Xc +Instructs +.Nm +to expect UTF-8 sequences to appear in this window. +.It Ic window-status-attr Ar attributes +Set status line attributes for a single window. +.It Ic window-status-bg Ar colour +Set status line background colour for a single window. +.It Ic window-status-fg Ar colour +Set status line foreground colour for a single window. +.It Xo Ic xterm-keys +.Op Ic on | Ic off +.Xc +If this option is set, +.Nm +will generate +.Xr xterm 1 -style +function key sequences; these have a number included to indicate modifiers such +as shift, meta or ctrl. +.El +.It Xo Ic show-buffer +.Op Fl b Ar buffer-index +.Op Fl t Ar target-session +.Xc +.D1 (alias: Ic showb ) +Display the contents of the specified buffer. +.It Xo Ic show-options +.Op Fl t Ar target-session +.Ar option Ar value +.Xc +.D1 (alias: Ic show ) +Show the currently set options. +If a +.Ar target-session +is specified, the options for that session are shown; otherwise, the global +options are listed. +.It Xo Ic show-window-options +.Op Fl t Ar target-window +.Ar option Ar value +.Xc +.D1 (alias: Ic showw ) +List the current options for the given window. +.It Xo Ic source-file +.Ar path +.Xc +.D1 (alias: Ic source ) +Execute commands from +.Ar path . +.It Xo Ic split-window +.Op Fl d +.Oo Fl l +.Ar lines | +.Fl p Ar percentage Oc +.Op Fl t Ar target-window +.Op Ar command +.Xc +.D1 (alias: splitw ) +Creates a new window by splitting it vertically. +The +.Fl l +and +.Fl p +options specify the size of the new window in lines, or as a percentage, +respectively. +All other options have the same meaning as in the +.Ic new-window +command. +.Pp +A few notes with regard to panes: +.Bl -enum -compact +.It +If attempting to split a window with less than eight lines, an error will be +shown. +.It +If the window is resized, as many panes are shown as can fit without reducing +them below four lines. +.It +The minimum pane size is four lines (including the separator line). +.It +The panes are indexed from top (0) to bottom, with no numbers skipped. +.El +.It Xo Ic start-server +.Xc +.D1 (alias: Ic start ) +Start the +.Nm +server, if not already running, without creating any sessions. +.It Xo Ic suspend-client +.Op Fl c target-client +.Xc +.D1 (alias: Ic suspendc ) +Suspend a client by sending SIGTSTP (tty stop). +.It Xo Ic swap-pane +.Op Fl dDU +.Op Fl p Ar src-index +.Op Fl t Ar target-window +.Op Fl q Ar dst-index +.Xc +.D1 (alias: Ic swapp ) +Swap two panes within a window. +If +.Fl U +is used, the pane is swapped with the pane above (before it numerically); +.Fl D +swaps with the pane below (the next numerically); or +.Ar dst-index +may be give to swap with a specific pane. +.It Xo Ic swap-window +.Op Fl d +.Op Fl s Ar src-window +.Op Fl t Ar dst-window +.Xc +.D1 (alias: Ic swapw ) +This is similar to +.Ic link-window , +except the source and destination windows are swapped. +It is an error if no window exists at +.Ar src-window . +.It Xo Ic switch-client +.Op Fl c Ar target-client Fl t Ar target-session +.Xc +.D1 (alias: Ic switchc ) +Switch the current session for client +.Ar target-client +to +.Ar target-session . +.It Xo Ic unbind-key +.Ar key +.Xc +.D1 (alias: Ic unbind ) +Unbind the key bound to +.Ar key . +.It Xo Ic unlink-window +.Op Fl t Ar target-window +.Xc +.D1 (alias: Ic unlinkw ) +Unlink +.Ar target-window . +A window may be unlinked only if it is linked to multiple sessions - windows may +not be linked to no sessions. +.It Xo Ic up-pane +.Op Fl p Ar pane-index +.Op Fl t Ar target-window +.Xc +.D1 (alias: Ic upp ) +Move up a pane. +.El +.Sh FILES +.Bl -tag -width Ds -compact +.It Pa ~/.tmux.conf +default +.Nm +configuration file +.El +.Sh SEE ALSO +.Xr pty 4 +.Sh AUTHORS +.An Nicholas Marriott Aq nicm@users.sourceforge.net diff --git a/usr.bin/tmux/tmux.c b/usr.bin/tmux/tmux.c new file mode 100644 index 00000000000..aa470d955da --- /dev/null +++ b/usr.bin/tmux/tmux.c @@ -0,0 +1,487 @@ +/* $OpenBSD: tmux.c,v 1.1 2009/06/01 22:58:49 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/stat.h> + +#include <errno.h> +#include <paths.h> +#include <pwd.h> +#include <signal.h> +#include <stdlib.h> +#include <string.h> +#include <syslog.h> +#include <unistd.h> + +#include "tmux.h" + +#ifdef DEBUG +const char *malloc_options = "AFGJPX"; +#endif + +volatile sig_atomic_t sigwinch; +volatile sig_atomic_t sigterm; +volatile sig_atomic_t sigcont; +volatile sig_atomic_t sigchld; +volatile sig_atomic_t sigusr1; +volatile sig_atomic_t sigusr2; + +char *cfg_file; +struct options global_options; +struct options global_window_options; + +int server_locked; +char *server_password; +time_t server_activity; + +int debug_level; +int be_quiet; +time_t start_time; +char *socket_path; + +__dead void usage(void); +char *makesockpath(const char *); + +__dead void +usage(void) +{ + fprintf(stderr, "usage: %s [-28dqUuVv] [-f file] " + "[-L socket-name] [-S socket-path] [command [flags]]\n", + __progname); + exit(1); +} + +void +logfile(const char *name) +{ + char *path; + + log_close(); + if (debug_level > 0) { + xasprintf( + &path, "%s-%s-%ld.log", __progname, name, (long) getpid()); + log_open_file(debug_level, path); + xfree(path); + } +} + +void +sighandler(int sig) +{ + int saved_errno; + + saved_errno = errno; + switch (sig) { + case SIGWINCH: + sigwinch = 1; + break; + case SIGTERM: + sigterm = 1; + break; + case SIGCHLD: + sigchld = 1; + break; + case SIGCONT: + sigcont = 1; + break; + case SIGUSR1: + sigusr1 = 1; + break; + case SIGUSR2: + sigusr2 = 1; + break; + } + errno = saved_errno; +} + +void +siginit(void) +{ + struct sigaction act; + + memset(&act, 0, sizeof act); + sigemptyset(&act.sa_mask); + act.sa_flags = SA_RESTART; + + act.sa_handler = SIG_IGN; + if (sigaction(SIGPIPE, &act, NULL) != 0) + fatal("sigaction failed"); + if (sigaction(SIGINT, &act, NULL) != 0) + fatal("sigaction failed"); + if (sigaction(SIGTSTP, &act, NULL) != 0) + fatal("sigaction failed"); + if (sigaction(SIGQUIT, &act, NULL) != 0) + fatal("sigaction failed"); + + act.sa_handler = sighandler; + if (sigaction(SIGWINCH, &act, NULL) != 0) + fatal("sigaction failed"); + if (sigaction(SIGTERM, &act, NULL) != 0) + fatal("sigaction failed"); + if (sigaction(SIGCHLD, &act, NULL) != 0) + fatal("sigaction failed"); + if (sigaction(SIGUSR1, &act, NULL) != 0) + fatal("sigaction failed"); + if (sigaction(SIGUSR2, &act, NULL) != 0) + fatal("sigaction failed"); +} + +void +sigreset(void) +{ + struct sigaction act; + + memset(&act, 0, sizeof act); + sigemptyset(&act.sa_mask); + + act.sa_handler = SIG_DFL; + if (sigaction(SIGPIPE, &act, NULL) != 0) + fatal("sigaction failed"); + if (sigaction(SIGUSR1, &act, NULL) != 0) + fatal("sigaction failed"); + if (sigaction(SIGUSR2, &act, NULL) != 0) + fatal("sigaction failed"); + if (sigaction(SIGINT, &act, NULL) != 0) + fatal("sigaction failed"); + if (sigaction(SIGTSTP, &act, NULL) != 0) + fatal("sigaction failed"); + if (sigaction(SIGQUIT, &act, NULL) != 0) + fatal("sigaction failed"); + if (sigaction(SIGWINCH, &act, NULL) != 0) + fatal("sigaction failed"); + if (sigaction(SIGTERM, &act, NULL) != 0) + fatal("sigaction failed"); + if (sigaction(SIGCHLD, &act, NULL) != 0) + fatal("sigaction failed"); +} + +char * +makesockpath(const char *label) +{ + char base[MAXPATHLEN], *path; + struct stat sb; + u_int uid; + + uid = getuid(); + xsnprintf(base, MAXPATHLEN, "%s/%s-%d", _PATH_TMP, __progname, uid); + + if (mkdir(base, S_IRWXU) != 0 && errno != EEXIST) + return (NULL); + + if (lstat(base, &sb) != 0) + return (NULL); + if (!S_ISDIR(sb.st_mode)) { + errno = ENOTDIR; + return (NULL); + } + if (sb.st_uid != uid || (sb.st_mode & (S_IRWXG|S_IRWXO)) != 0) { + errno = EACCES; + return (NULL); + } + + xasprintf(&path, "%s/%s", base, label); + return (path); +} + +int +main(int argc, char **argv) +{ + struct client_ctx cctx; + struct msg_command_data cmddata; + struct buffer *b; + struct cmd_list *cmdlist; + struct cmd *cmd; + struct pollfd pfd; + struct hdr hdr; + const char *shell; + struct passwd *pw; + char *s, *path, *label, *cause, *home, *pass = NULL; + char cwd[MAXPATHLEN]; + int retcode, opt, flags, unlock, start_server; + + unlock = flags = 0; + label = path = NULL; + while ((opt = getopt(argc, argv, "28df:L:qS:uUv")) != -1) { + switch (opt) { + case '2': + flags |= IDENTIFY_256COLOURS; + flags &= ~IDENTIFY_88COLOURS; + break; + case '8': + flags |= IDENTIFY_88COLOURS; + flags &= ~IDENTIFY_256COLOURS; + break; + case 'f': + cfg_file = xstrdup(optarg); + break; + case 'L': + if (path != NULL) { + log_warnx("-L and -S cannot be used together"); + exit(1); + } + if (label != NULL) + xfree(label); + label = xstrdup(optarg); + break; + case 'S': + if (label != NULL) { + log_warnx("-L and -S cannot be used together"); + exit(1); + } + if (path != NULL) + xfree(path); + path = xstrdup(optarg); + break; + case 'q': + be_quiet = 1; + break; + case 'u': + flags |= IDENTIFY_UTF8; + break; + case 'U': + unlock = 1; + break; + case 'd': + flags |= IDENTIFY_HASDEFAULTS; + break; + case 'v': + debug_level++; + break; + default: + usage(); + } + } + argc -= optind; + argv += optind; + + log_open_tty(debug_level); + siginit(); + + options_init(&global_options, NULL); + options_set_number(&global_options, "bell-action", BELL_ANY); + options_set_number(&global_options, "buffer-limit", 9); + options_set_number(&global_options, "display-time", 750); + options_set_number(&global_options, "history-limit", 2000); + options_set_number(&global_options, "lock-after-time", 0); + options_set_number(&global_options, "message-attr", GRID_ATTR_REVERSE); + options_set_number(&global_options, "message-bg", 3); + options_set_number(&global_options, "message-fg", 0); + options_set_number(&global_options, "prefix", '\002'); + options_set_number(&global_options, "repeat-time", 500); + options_set_number(&global_options, "set-remain-on-exit", 0); + options_set_number(&global_options, "set-titles", 1); + options_set_number(&global_options, "status", 1); + options_set_number(&global_options, "status-attr", GRID_ATTR_REVERSE); + options_set_number(&global_options, "status-bg", 2); + options_set_number(&global_options, "status-fg", 0); + options_set_number(&global_options, "status-interval", 15); + options_set_number(&global_options, "status-keys", MODEKEY_EMACS); + options_set_number(&global_options, "status-left-length", 10); + options_set_number(&global_options, "status-right-length", 40); + options_set_string(&global_options, "status-left", "[#S]"); + options_set_string( + &global_options, "status-right", "\"#24T\" %%H:%%M %%d-%%b-%%y"); + + options_init(&global_window_options, NULL); + options_set_number(&global_window_options, "aggressive-resize", 0); + options_set_number(&global_window_options, "automatic-rename", 1); + options_set_number(&global_window_options, "clock-mode-colour", 4); + options_set_number(&global_window_options, "clock-mode-style", 1); + options_set_number(&global_window_options, "force-height", 0); + options_set_number(&global_window_options, "force-width", 0); + options_set_number( + &global_window_options, "mode-attr", GRID_ATTR_REVERSE); + options_set_number(&global_window_options, "main-pane-width", 81); + options_set_number(&global_window_options, "main-pane-height", 24); + options_set_number(&global_window_options, "mode-bg", 3); + options_set_number(&global_window_options, "mode-fg", 0); + options_set_number(&global_window_options, "mode-keys", MODEKEY_EMACS); + options_set_number(&global_window_options, "monitor-activity", 0); + options_set_string(&global_window_options, "monitor-content", "%s", ""); + options_set_number(&global_window_options, "utf8", 0); + options_set_number(&global_window_options, "window-status-attr", 0); + options_set_number(&global_window_options, "window-status-bg", 8); + options_set_number(&global_window_options, "window-status-fg", 8); + options_set_number(&global_window_options, "xterm-keys", 0); + options_set_number(&global_window_options, "remain-on-exit", 0); + + if (!(flags & IDENTIFY_UTF8)) { + /* + * If the user has set LANG to contain UTF-8, it is a safe + * assumption that either they are using a UTF-8 terminal, or + * if not they know that output from UTF-8-capable programs may + * be wrong. + */ + if ((s = getenv("LANG")) != NULL && strstr(s, "UTF-8") != NULL) + flags |= IDENTIFY_UTF8; + } + + if (cfg_file == NULL) { + home = getenv("HOME"); + if (home == NULL || *home == '\0') { + pw = getpwuid(getuid()); + if (pw != NULL) + home = pw->pw_dir; + } + xasprintf(&cfg_file, "%s/%s", home, DEFAULT_CFG); + if (access(cfg_file, R_OK) != 0) { + xfree(cfg_file); + cfg_file = NULL; + } + } else { + if (access(cfg_file, R_OK) != 0) { + log_warn("%s", cfg_file); + exit(1); + } + } + + if (label == NULL) + label = xstrdup("default"); + if (path == NULL && (path = makesockpath(label)) == NULL) { + log_warn("can't create socket"); + exit(1); + } + xfree(label); + + shell = getenv("SHELL"); + if (shell == NULL || *shell == '\0') { + pw = getpwuid(getuid()); + if (pw != NULL) + shell = pw->pw_shell; + if (shell == NULL || *shell == '\0') + shell = _PATH_BSHELL; + } + options_set_string( + &global_options, "default-command", "exec %s", shell); + + if (getcwd(cwd, sizeof cwd) == NULL) { + log_warn("getcwd"); + exit(1); + } + options_set_string(&global_options, "default-path", "%s", cwd); + + if (unlock) { + if (argc != 0) { + log_warnx("can't specify a command when unlocking"); + exit(1); + } + cmdlist = NULL; + if ((pass = getpass("Password: ")) == NULL) + exit(1); + start_server = 0; + } else { + if (argc == 0) { + cmd = xmalloc(sizeof *cmd); + cmd->entry = &cmd_new_session_entry; + cmd->entry->init(cmd, 0); + + cmdlist = xmalloc(sizeof *cmdlist); + TAILQ_INIT(cmdlist); + TAILQ_INSERT_HEAD(cmdlist, cmd, qentry); + } else { + cmdlist = cmd_list_parse(argc, argv, &cause); + if (cmdlist == NULL) { + log_warnx("%s", cause); + exit(1); + } + } + start_server = 0; + TAILQ_FOREACH(cmd, cmdlist, qentry) { + if (cmd->entry->flags & CMD_STARTSERVER) { + start_server = 1; + break; + } + } + } + + memset(&cctx, 0, sizeof cctx); + if (client_init(path, &cctx, start_server, flags) != 0) + exit(1); + xfree(path); + + b = buffer_create(BUFSIZ); + if (unlock) { + cmd_send_string(b, pass); + client_write_server( + &cctx, MSG_UNLOCK, BUFFER_OUT(b), BUFFER_USED(b)); + } else { + cmd_list_send(cmdlist, b); + cmd_list_free(cmdlist); + client_fill_session(&cmddata); + client_write_server2(&cctx, MSG_COMMAND, + &cmddata, sizeof cmddata, BUFFER_OUT(b), BUFFER_USED(b)); + } + buffer_destroy(b); + + retcode = 0; + for (;;) { + pfd.fd = cctx.srv_fd; + pfd.events = POLLIN; + if (BUFFER_USED(cctx.srv_out) > 0) + pfd.events |= POLLOUT; + + if (poll(&pfd, 1, INFTIM) == -1) { + if (errno == EAGAIN || errno == EINTR) + continue; + fatal("poll failed"); + } + + if (buffer_poll(&pfd, cctx.srv_in, cctx.srv_out) != 0) + goto out; + + restart: + if (BUFFER_USED(cctx.srv_in) < sizeof hdr) + continue; + memcpy(&hdr, BUFFER_OUT(cctx.srv_in), sizeof hdr); + if (BUFFER_USED(cctx.srv_in) < (sizeof hdr) + hdr.size) + continue; + buffer_remove(cctx.srv_in, sizeof hdr); + + switch (hdr.type) { + case MSG_EXIT: + case MSG_SHUTDOWN: + goto out; + case MSG_ERROR: + retcode = 1; + /* FALLTHROUGH */ + case MSG_PRINT: + if (hdr.size > INT_MAX - 1) + fatalx("bad MSG_PRINT size"); + log_info("%.*s", + (int) hdr.size, BUFFER_OUT(cctx.srv_in)); + if (hdr.size != 0) + buffer_remove(cctx.srv_in, hdr.size); + goto restart; + case MSG_READY: + retcode = client_main(&cctx); + goto out; + default: + fatalx("unexpected command"); + } + } + +out: + options_free(&global_options); + options_free(&global_window_options); + + close(cctx.srv_fd); + buffer_destroy(cctx.srv_in); + buffer_destroy(cctx.srv_out); + + return (retcode); +} diff --git a/usr.bin/tmux/tmux.cat1 b/usr.bin/tmux/tmux.cat1 new file mode 100644 index 00000000000..fe815603ab1 --- /dev/null +++ b/usr.bin/tmux/tmux.cat1 @@ -0,0 +1,813 @@ +TMUX(1) OpenBSD Reference Manual TMUX(1) + +NNAAMMEE + ttmmuuxx - terminal multiplexer + +SSYYNNOOPPSSIISS + ttmmuuxx [--2288ddqqUUuuvv] [--ff _f_i_l_e] [--LL _s_o_c_k_e_t_-_n_a_m_e] [--SS _s_o_c_k_e_t_-_p_a_t_h] + [_c_o_m_m_a_n_d [_f_l_a_g_s]] + +DDEESSCCRRIIPPTTIIOONN + ttmmuuxx is a terminal multiplexer; it enables a number of terminals to be + accessed and controlled from a single terminal. + + ttmmuuxx runs as a server-client system. A server is created automatically + when necessary and holds a number of _s_e_s_s_i_o_n_s, each of which may have a + number of _w_i_n_d_o_w_s linked to it. A window may be split on screen into one + or more _p_a_n_e_s, each of which is a separate terminal. Any number of + _c_l_i_e_n_t_s may connect to a session, or the server may be controlled by is- + suing commands with ttmmuuxx. Communication takes place through a socket, by + default placed in _/_t_m_p. + + The options are as follows: + + --22 Force ttmmuuxx to assume the terminal supports 256 colours. + + --88 Like --22, indicates the terminal supports 88 colours. + + --dd Force ttmmuuxx to assume the terminal supports default colours. + + --ff _f_i_l_e Specify an alternative configuration file. By default, + ttmmuuxx will look for a config file at _~_/_._t_m_u_x_._c_o_n_f. The con- + figuration file is a set of ttmmuuxx commands which are execut- + ed in sequence when the server is first started. + + --qq Prevent the server sending various information messages, + for example when window flags are altered. + + --LL _s_o_c_k_e_t_-_n_a_m_e + ttmmuuxx stores the server socket in a directory under _/_t_m_p; + the default socket is named _d_e_f_a_u_l_t. This option allows a + different socket name to be specified, allowing several in- + dependent ttmmuuxx servers to be run. Unlike --SS a full path is + not necessary: the sockets are all created in the same di- + rectory. + + --SS _s_o_c_k_e_t_-_p_a_t_h + Specify a full alternative path to the server socket. If + --SS is specified, the default socket directory is not used + and any --LL flag is ignored. + + --UU Unlock the server. + + --uu Instruct ttmmuuxx that the terminal support UTF-8. + + --vv Request verbose logging. This option may be specified mul- + tiple times for increasing verbosity. Log messages will be + saved into _t_m_u_x_-_c_l_i_e_n_t_-_P_I_D_._l_o_g and _t_m_u_x_-_s_e_r_v_e_r_-_P_I_D_._l_o_g + files in the current directory, where _P_I_D is the pid of the + server or client process. + + _c_o_m_m_a_n_d [_f_l_a_g_s] + This specifies one of a set of commands used to control + ttmmuuxx, and described in the following sections. If no com- + mand and flags is specified, the nneeww--sseessssiioonn command is as- + sumed. + +QQUUIICCKK SSTTAARRTT + To create a new tmux session running vi(1): + + $ tmux new-session vi + + Most commands have a shorter form, known as an alias. For new-session, + this is nneeww: + + $ tmux new vi + + Alternatively, the shortest unambiguous form of a command is accepted. + If there are several options, they are listed: + + $ tmux n + ambiguous command: n, could be: new-session, new-window, next-window + $ + + Within an active session, a new window may be created by typing `C-b' + (ctrl-b, known as the prefix key) followed by the `c' key. + + Windows may be navigated with: `C-b 0' (to select window 0), `C-b 1' (to + select window 1), and so on; `C-b n' to select the next window; and `C-b + p' to select the previous window. + + A session may be detached using `C-b d' and reattached with: + + $ tmux attach-session + + Typing `C-b ?' lists the current key bindings in the current window; up + and down may be used to navigate the list or `Q' to exit from it. + +KKEEYY BBIINNDDIINNGGSS + ttmmuuxx may be controlled from an attached client by using a key combination + of a prefix key, `C-b' (ctrl-b) by default, followed by a command key. + + Some of the default key bindings include: + + `d' Detach current client. + `c' Create new window. + `n' Change to next window in the current session. + `p' Change to previous window in the current session. + `l' Move to last (previously selected) window in the current session. + `t' Display a large clock. + `?' List current key bindings. + + A complete list may be obtained with the lliisstt--kkeeyyss command (bound to `?' + by default). Key bindings may be changed with the bbiinndd--kkeeyy and uunnbbiinndd-- + kkeeyy commands. + +HHIISSTTOORRYY + ttmmuuxx maintains a configurable history buffer for each window. By de- + fault, up to 2000 lines are kept, this can be altered with the hhiissttoorryy-- + lliimmiitt option (see the sseett--ooppttiioonn command below). + +MMOODDEESS + A ttmmuuxx window may be in one of several modes. The default permits direct + access to the terminal attached to the window. The others are: + + _o_u_t_p_u_t _m_o_d_e + This is entered when a command which produces output, such as + lliisstt--kkeeyyss, is executed from a key binding. + + _s_c_r_o_l_l _m_o_d_e + This is entered with the ssccrroollll--mmooddee command (bound to `=' by de- + fault) and permits the window history buffer to be inspected. + + _c_o_p_y _m_o_d_e + This permits a section of a window or its history to be copied to + a _p_a_s_t_e _b_u_f_f_e_r for later insertion into another window. This + mode is entered with the ccooppyy--mmooddee command, bound to `[' by de- + fault. + + The keys available depend on whether emacs(1) or vi(1) mode is selected + (see the mmooddee--kkeeyyss option). The following keys are supported as appro- + priate for the mode: + + FFuunnccttiioonn vvii eemmaaccss + Start of line 0 or ^ C-a + Clear selection Escape C-g + Copy selection Enter M-w + Cursor down j Down + End of line $ C-e + Cursor left h Left + Next page C-f Page down + Next word w M-f + Previous page C-u Page up + Previous word b M-b + Quit mode q Escape + Cursor right l Right + Start selection Space C-Space + Cursor up k Up + +BBUUFFFFEERRSS + ttmmuuxx maintains a stack of _p_a_s_t_e _b_u_f_f_e_r_s for each session. Up to the val- + ue of the bbuuffffeerr--lliimmiitt option are kept; when a new buffer is added, the + buffer at the bottom of the stack is removed. Buffers may be added using + ccooppyy--mmooddee or the sseett--bbuuffffeerr command, and pasted into a window using the + ppaassttee--bbuuffffeerr command. + +PPAANNEESS AANNDD LLAAYYOOUUTTSS + Each window displayed by ttmmuuxx may be split into one or more _p_a_n_e_s; each + pane takes up a certain area of the display and is a separate terminal. + A window may be split into panes using the sspplliitt--wwiinnddooww command. + + Panes are numbered beginning from zero; in horizontal layouts zero is the + leftmost pane and in vertical the topmost. + + Panes may be arranged using several layouts. The layout may be cycled + with the nneexxtt--llaayyoouutt command (bound to `C-space' by default), the current + pane may be changed with the uupp--ppaannee and ddoowwnn--ppaannee commands and the + rroottaattee--wwiinnddooww and sswwaapp--ppaannee commands may be used to swap panes without + changing the window layout. + + The following layouts are supported: + + mmaannuuaall Manual layout splits windows vertically (running across); only + with this layout may panes be resized using the rreessiizzee--ppaannee com- + mand. + + aaccttiivvee--oonnllyy + Only the active pane is shown - all other panes are hidden. + + eevveenn--hhoorriizzoonnttaall + Panes are spread out evenly from left to right across the window. + + eevveenn--vveerrttiiccaall + Panes are spread evenly from top to bottom. + + mmaaiinn--vveerrttiiccaall + A large (81 column) pane is shown on the left of the window and + the remaining panes are spread from top to bottom in the leftover + space to the right. + +CCOOMMMMAANNDDSS + This section contains a list of the commands supported by ttmmuuxx. Most + commands accept the optional --tt argument with one of _t_a_r_g_e_t_-_c_l_i_e_n_t, + _t_a_r_g_e_t_-_s_e_s_s_i_o_n or _t_a_r_g_e_t_-_w_i_n_d_o_w. These specify the client, session or + window which a command should affect. _t_a_r_g_e_t_-_c_l_i_e_n_t is the name of the + pty(4) file to which the client is connected, for example _/_d_e_v_/_t_t_y_p_1. + Clients may be listed with the lliisstt--cclliieennttss command. + + _t_a_r_g_e_t_-_s_e_s_s_i_o_n is either the name of a session (as listed by the lliisstt-- + sseessssiioonnss command); or the name of a client as for _t_a_r_g_e_t_-_c_l_i_e_n_t, in this + case, the session attached to the client is used. An fnmatch(3) pattern + may be used to match the session name. If a session is omitted when re- + quired, ttmmuuxx attempts to use the current session; if no current session + is available, the most recently created is chosen. If no client is spec- + ified, the current client is chosen, if possible, or an error is report- + ed. + + _t_a_r_g_e_t_-_w_i_n_d_o_w specifies a window in the form _s_e_s_s_i_o_n:_i_n_d_e_x, for example + mysession:1. The session is in the same form as for _t_a_r_g_e_t_-_s_e_s_s_i_o_n. + _s_e_s_s_i_o_n, _i_n_d_e_x or both may be omitted. If _s_e_s_s_i_o_n is omitted, the same + rules as for _t_a_r_g_e_t_-_s_e_s_s_i_o_n are followed; if _i_n_d_e_x is not present, the + current window for the given session is used. When the argument does not + contain a colon (:), ttmmuuxx first attempts to parse it as window index; if + that fails, an attempt is made to match a session or client name. + + Multiple commands may be specified together as part of a _c_o_m_m_a_n_d + _s_e_q_u_e_n_c_e. Each command should be separated by spaces and a semicolon (` + ; '); commands are executed sequentially from left to right. A literal + semicolon may be included by escaping it with a backslash (for example, + when specifying a command sequence to bbiinndd--kkeeyy). + + Examples include: + + refresh-client -t/dev/ttyp2 + + rename-session -tfirst newname + + set-window-option -t:0 monitor-activity on + + new-window ; split-window -d + + bind-key D detach-client \; lock-server + + The following commands are available: + + aattttaacchh--sseessssiioonn [--dd] [--tt _t_a_r_g_e_t_-_s_e_s_s_i_o_n] + (alias: aattttaacchh) + Create a new client in the current terminal and attach it to a + session. If --dd is specified, any other clients attached to the + session are detached. + + If no server is started, aattttaacchh--sseessssiioonn will attempt to start it; + this will fail unless sessions are created in the configuration + file. + + bbiinndd--kkeeyy [--rr] _k_e_y _c_o_m_m_a_n_d [_a_r_g_u_m_e_n_t_s] + (alias: bbiinndd) + Bind key _k_e_y to _c_o_m_m_a_n_d. Keys may be specified prefixed with + `C-' or `^' for ctrl keys, or `M-' for alt (meta) keys. The --rr + flag indicates this key may repeat, see the rreeppeeaatt--ttiimmee option. + + bbrreeaakk--ppaannee [--dd] [--pp _p_a_n_e_-_i_n_d_e_x] [--tt _t_a_r_g_e_t_-_w_i_n_d_o_w] + (alias: bbrreeaakkpp)) + Break the current pane off from its containing window to make it + the only pane in a new window. If --dd is given, the new window + does not become the current window. + + cchhoooossee--sseessssiioonn [--tt _t_a_r_g_e_t_-_w_i_n_d_o_w] + Put a window into session choice mode, where the session for the + current client may be selected interactively from a list. This + command works only from inside ttmmuuxx. + + cchhoooossee--wwiinnddooww [--tt _t_a_r_g_e_t_-_w_i_n_d_o_w] + Put a window into window choice mode, where the window for the + session attached to the current client may be selected interac- + tively from a list. This command works only from inside ttmmuuxx. + + cclloocckk--mmooddee [--tt _t_a_r_g_e_t_-_w_i_n_d_o_w] + Display a large clock. + + ccoommmmaanndd--pprroommpptt [--tt _t_a_r_g_e_t_-_c_l_i_e_n_t] [_t_e_m_p_l_a_t_e] + Open the command prompt in a client. This may be used from in- + side ttmmuuxx to execute commands interactively. If _t_e_m_p_l_a_t_e is + specified, it is used as the command; any %% in the template will + be replaced by what is entered at the prompt. + + ccoonnffiirrmm--bbeeffoorree [--tt _t_a_r_g_e_t_-_c_l_i_e_n_t] _c_o_m_m_a_n_d + (alias: ccoonnffiirrmm)) + Ask for confirmation before executing _c_o_m_m_a_n_d. This command + works only from inside ttmmuuxx. + + ccooppyy--bbuuffffeerr [--aa _s_r_c_-_i_n_d_e_x] [--bb _d_s_t_-_i_n_d_e_x] [--ss _s_r_c_-_s_e_s_s_i_o_n] [--tt + _d_s_t_-_s_e_s_s_i_o_n] + (alias: ccooppyybb)) + Copy a session paste buffer to another session. If no sessions + are specified, the current one is used instead. + + ccooppyy--mmooddee [--uu] [--tt _t_a_r_g_e_t_-_w_i_n_d_o_w] + Enter copy mode. The --uu option scrolls one page up. + + ddeelleettee--bbuuffffeerr [--bb _b_u_f_f_e_r_-_i_n_d_e_x] [--tt _t_a_r_g_e_t_-_s_e_s_s_i_o_n] + (alias: ddeelleetteebb) + Delete the buffer at _b_u_f_f_e_r_-_i_n_d_e_x, or the top buffer if not spec- + ified. + + ddeettaacchh--cclliieenntt [--tt _t_a_r_g_e_t_-_c_l_i_e_n_t] + (alias: ddeettaacchh) + Detach the current client if bound to a key, or the specified + client with --tt. + + ddoowwnn--ppaannee [--pp _p_a_n_e_-_i_n_d_e_x] [--tt _t_a_r_g_e_t_-_w_i_n_d_o_w] + (alias: ddoowwnnpp) + Move down a pane. + + ffiinndd--wwiinnddooww [--tt _t_a_r_g_e_t_-_w_i_n_d_o_w] _m_a_t_c_h_-_s_t_r_i_n_g + (alias: ffiinnddww) + Search for _m_a_t_c_h_-_s_t_r_i_n_g in window names, titles, and visible con- + tent (but not history). If only one window is matched, it'll be + automatically selected, otherwise a choice list is shown. This + command only works from inside ttmmuuxx. + + hhaass--sseessssiioonn [--tt _t_a_r_g_e_t_-_s_e_s_s_i_o_n] + (alias: hhaass) + Report an error and exit with 1 if the specified session does not + exist. If it does exist, exit with 0. + + kkiillll--ppaannee [--pp _p_a_n_e_-_i_n_d_e_x] [--tt _t_a_r_g_e_t_-_w_i_n_d_o_w] + (alias: kkiillllpp) + Destroy the given pane. + + kkiillll--sseerrvveerr + Kill the ttmmuuxx server and clients and destroy all sessions. + + kkiillll--sseessssiioonn [--tt _t_a_r_g_e_t_-_s_e_s_s_i_o_n] + Destroy the given session, closing any windows linked to it and + no other sessions, and detaching all clients attached to it. + + kkiillll--wwiinnddooww [--tt _t_a_r_g_e_t_-_w_i_n_d_o_w] + (alias: kkiillllww) + Kill the current window or the window at _t_a_r_g_e_t_-_w_i_n_d_o_w, removing + it from any sessions to which it is linked. + + llaasstt--wwiinnddooww [--tt _t_a_r_g_e_t_-_s_e_s_s_i_o_n] + (alias: llaasstt) + Select the last (previously selected) window. If no _t_a_r_g_e_t_- + _s_e_s_s_i_o_n is specified, select the last window of the current ses- + sion. + + lliinnkk--wwiinnddooww [--ddkk] [--ss _s_r_c_-_w_i_n_d_o_w] [--tt _d_s_t_-_w_i_n_d_o_w] + (alias: lliinnkkww) + Link the window at _s_r_c_-_w_i_n_d_o_w to the specified _d_s_t_-_w_i_n_d_o_w. If + _d_s_t_-_w_i_n_d_o_w is specified and no such window exists, the _s_r_c_-_w_i_n_d_o_w + is linked there. If --kk is given and _d_s_t_-_w_i_n_d_o_w exists, it is + killed, otherwise an error is generated. If --dd is given, the + newly linked window is not selected. + + lliisstt--bbuuffffeerrss [--tt _t_a_r_g_e_t_-_s_e_s_s_i_o_n] + (alias: llssbb) + List the buffers in the given session. + + lliisstt--cclliieennttss + (alias: llsscc) + List all clients attached to the server. + + lliisstt--ccoommmmaannddss + (alias: llssccmm) + List the syntax of all commands supported by ttmmuuxx. + + lliisstt--kkeeyyss + (alias: llsskk) + List all key bindings. + + lliisstt--sseessssiioonnss + (alias: llss) + List all sessions managed by the server. + + lliisstt--wwiinnddoowwss [--tt _t_a_r_g_e_t_-_s_e_s_s_i_o_n] + (alias: llssww) + List windows in the current session or in _t_a_r_g_e_t_-_s_e_s_s_i_o_n. + + llooaadd--bbuuffffeerr [--bb _b_u_f_f_e_r_-_i_n_d_e_x] [--tt _t_a_r_g_e_t_-_s_e_s_s_i_o_n] _p_a_t_h + (alias: llooaaddbb) + Load the contents of the specified paste buffer from _p_a_t_h. + + lloocckk--sseerrvveerr + (alias: lloocckk) + Lock the server until a password is entered. + + mmoovvee--wwiinnddooww [--dd] [--ss _s_r_c_-_w_i_n_d_o_w] [--tt _d_s_t_-_w_i_n_d_o_w] + (alias: mmoovveeww) + This is similar to lliinnkk--wwiinnddooww, except the window at _s_r_c_-_w_i_n_d_o_w + is moved to _d_s_t_-_w_i_n_d_o_w. + + nneeww--sseessssiioonn [--dd] [--nn _w_i_n_d_o_w_-_n_a_m_e] [--ss _s_e_s_s_i_o_n_-_n_a_m_e] [_c_o_m_m_a_n_d] + (alias: nneeww) + Create a new session with name _s_e_s_s_i_o_n_-_n_a_m_e. The new session is + attached to the current terminal unless --dd is given. _w_i_n_d_o_w_-_n_a_m_e + and _c_o_m_m_a_n_d are the name of and command to execute in the initial + window. + + nneeww--wwiinnddooww [--dd] [--nn _w_i_n_d_o_w_-_n_a_m_e] [--tt _t_a_r_g_e_t_-_w_i_n_d_o_w] [_c_o_m_m_a_n_d] + (alias: nneewwww) + Create a new window. If --dd is given, the session does not make + the new window the current window. _t_a_r_g_e_t_-_w_i_n_d_o_w represents the + window to be created. _c_o_m_m_a_n_d is the command to execute. If + _c_o_m_m_a_n_d is not specified, the default command is used. + + The TERM environment variable must be set to ``screen'' for all + programs running _i_n_s_i_d_e ttmmuuxx. New windows will automatically + have ``TERM=screen'' added to their environment, but care must be + taken not to reset this in shell start-up files. + + nneexxtt--llaayyoouutt [--tt _t_a_r_g_e_t_-_w_i_n_d_o_w] + (alias: nneexxttll) + Move a window to the next layout and rearrange the panes to fit. + + nneexxtt--wwiinnddooww [--tt _t_a_r_g_e_t_-_s_e_s_s_i_o_n] + (alias: nneexxtt) + Move to the next window in the session. + + ppaassttee--bbuuffffeerr [--dd] [--bb _b_u_f_f_e_r_-_i_n_d_e_x] [--tt _t_a_r_g_e_t_-_w_i_n_d_o_w] + (alias: ppaasstteebb) + Insert the contents of a paste buffer into the current window. + + pprreevviioouuss--wwiinnddooww [--tt _t_a_r_g_e_t_-_s_e_s_s_i_o_n] + (alias: pprreevv) + Move to the previous window in the session. + + rreeffrreesshh--cclliieenntt [--tt _t_a_r_g_e_t_-_c_l_i_e_n_t] + (alias: rreeffrreesshh) + Refresh the current client if bound to a key, or a single client + if one is given with --tt. + + rreennaammee--sseessssiioonn [--tt _t_a_r_g_e_t_-_s_e_s_s_i_o_n] _n_e_w_-_n_a_m_e + (alias: rreennaammee) + Rename the session to _n_e_w_-_n_a_m_e. + + rreennaammee--wwiinnddooww [--tt _t_a_r_g_e_t_-_w_i_n_d_o_w] _n_e_w_-_n_a_m_e + (alias: rreennaammeeww) + Rename the current window, or the window at _t_a_r_g_e_t_-_w_i_n_d_o_w if + specified, to _n_e_w_-_n_a_m_e. + + rreessiizzee--ppaannee [--DDUU] [--pp _p_a_n_e_-_i_n_d_e_x] [--tt _t_a_r_g_e_t_-_w_i_n_d_o_w] [_a_d_j_u_s_t_m_e_n_t] + (alias: rreessiizzeepp) + Resize a pane, upward with --UU (the default) or downward with --DD. + The _a_d_j_u_s_t_m_e_n_t is given in lines (the default is 1). + + rreessppaawwnn--wwiinnddooww [--kk] [--tt _t_a_r_g_e_t_-_w_i_n_d_o_w] [_c_o_m_m_a_n_d] + (alias: rreessppaawwnnww) + Reactive a window in which the command has exited (see the + rreemmaaiinn--oonn--eexxiitt window option). If _c_o_m_m_a_n_d is not given, the com- + mand used when the window was created is executed. The window + must be already inactive, unless --kk is given, in which case any + existing command is killed. + + rroottaattee--wwiinnddooww [--DDUU] [--tt _t_a_r_g_e_t_-_w_i_n_d_o_w] + (alias: rroottaatteeww) + Rotate the positions of the panes within a window, either upward + (numerically lower) with --UU or downward (numerically higher). + + ssaavvee--bbuuffffeerr [--aa] [--bb _b_u_f_f_e_r_-_i_n_d_e_x] [--tt _t_a_r_g_e_t_-_s_e_s_s_i_o_n] _p_a_t_h + (alias: ssaavveebb) + Save the contents of the specified paste buffer to _p_a_t_h. The --aa + option appends to rather than overwriting the file. + + ssccrroollll--mmooddee [--uu] [--tt _t_a_r_g_e_t_-_w_i_n_d_o_w] + Enter scroll mode. The --uu has the same meaning as in the ccooppyy-- + mmooddee command. + + sseelleecctt--ppaannee [--pp _p_a_n_e_-_i_n_d_e_x] [--tt _t_a_r_g_e_t_-_w_i_n_d_o_w] + (alias: sseelleeccttpp) + Make pane _p_a_n_e_-_i_n_d_e_x the active pane in window _t_a_r_g_e_t_-_w_i_n_d_o_w. + + sseelleecctt--pprroommpptt [--tt _t_a_r_g_e_t_-_c_l_i_e_n_t] + Open a prompt inside _t_a_r_g_e_t_-_c_l_i_e_n_t allowing a window index to be + entered interactively. + + sseelleecctt--wwiinnddooww [--tt _t_a_r_g_e_t_-_w_i_n_d_o_w] + (alias: sseelleeccttww) + Select the window at _t_a_r_g_e_t_-_w_i_n_d_o_w. + + sseenndd--kkeeyyss [--tt _t_a_r_g_e_t_-_w_i_n_d_o_w] _k_e_y _._._. + (alias: sseenndd) + Send a key or keys to a window. Each argument _k_e_y is the name of + the key (such as `C-a' or `npage' ) to send; if the string is not + recognised as a key, it is sent as a series of characters. All + arguments are sent sequentially from first to last. + + sseenndd--pprreeffiixx [--tt _t_a_r_g_e_t_-_w_i_n_d_o_w] + Send the prefix key to a window as if it was pressed. + + sseerrvveerr--iinnffoo + (alias: iinnffoo) + Show server information and terminal details. + + sseett--bbuuffffeerr [--bb _b_u_f_f_e_r_-_i_n_d_e_x] [--tt _t_a_r_g_e_t_-_s_e_s_s_i_o_n] _d_a_t_a + (alias: sseettbb) + Set the contents of the specified buffer to _d_a_t_a. + + sseett--ooppttiioonn [--gguu] [--tt _t_a_r_g_e_t_-_s_e_s_s_i_o_n] _o_p_t_i_o_n _v_a_l_u_e + (alias: sseett) + Set an option. If --gg is specified, the option is set as a global + option. Global options apply to all sessions which don't have + the option explicitly set. If --gg is not used, the option applies + only to _t_a_r_g_e_t_-_s_e_s_s_i_o_n. The --uu flag unsets an option, so a ses- + sion inherits the option from the global options - it is not pos- + sible to unset a global option. + + Possible options are: + + bbeellll--aaccttiioonn [aannyy | nnoonnee | ccuurrrreenntt] + Set action on window bell. aannyy means a bell in any win- + dow linked to a session causes a bell in the current win- + dow of that session, nnoonnee means all bells are ignored and + ccuurrrreenntt means only bell in windows other than the current + window are ignored. + + bbuuffffeerr--lliimmiitt _n_u_m_b_e_r + Set the number of buffers kept for each session; as new + buffers are added to the top of the stack, old ones are + removed from the bottom if necessary to maintain this + maximum length. + + ddeeffaauulltt--ccoommmmaanndd _c_o_m_m_a_n_d + Set the command used for new windows (if not specified + when the window is created) to _c_o_m_m_a_n_d. The default is + ``exec $SHELL''. + + ddeeffaauulltt--ppaatthh _p_a_t_h + Set the default working directory for processes created + from keys, or interactively from the prompt. The default + is the current working directory when the server is + started. + + hhiissttoorryy--lliimmiitt _l_i_n_e_s + Set the maximum number of lines held in window history. + This setting applies only to new windows - existing win- + dow histories are not resized and retain the limit at the + point they were created. + + lloocckk--aafftteerr--ttiimmee _n_u_m_b_e_r + Lock the server after _n_u_m_b_e_r seconds of inactivity. The + default is off (set to 0). This has no effect as a ses- + sion option; it must be set as a global option using --gg. + + mmeessssaaggee--aattttrr _a_t_t_r_i_b_u_t_e_s + Set status line message attributes, where _a_t_t_r_i_b_u_t_e_s is + either ddeeffaauulltt or a comma-delimited list of one or more + of: bbrriigghhtt (or bboolldd), ddiimm, uunnddeerrssccoorree, bblliinnkk, rreevveerrssee, + hhiiddddeenn, or iittaalliiccss. + + mmeessssaaggee--bbgg _c_o_l_o_u_r + Set status line message background colour, where _c_o_l_o_u_r + is one of: bbllaacckk, rreedd, ggrreeeenn, yyeellllooww, bblluuee, mmaaggeennttaa, + ccyyaann, wwhhiittee or ddeeffaauulltt. + + mmeessssaaggee--ffgg _c_o_l_o_u_r + Set status line message foreground colour. + + pprreeffiixx _k_e_y + Set the current prefix key. + + rreeppeeaatt--ttiimmee _n_u_m_b_e_r + Allow multiple commands to be entered without pressing + the prefix-key again in the specified _n_u_m_b_e_r milliseconds + (the default is 500). Whether a key repeats may be set + when it is bound using the --rr flag to bbiinndd--kkeeyy. Repeat + is enabled for the default keys of the uupp--ppaannee, ddoowwnn-- + ppaannee, rreessiizzee--ppaannee--uupp, and rreessiizzee--ppaannee--ddoowwnn commands. + + sseett--rreemmaaiinn--oonn--eexxiitt [oonn | ooffff] + Set the rreemmaaiinn--oonn--eexxiitt window option for any windows + first created in this session. + + sseett--ttiittlleess [oonn | ooffff] + Attempt to set the window title using the \e]2;...\007 + xterm code and the terminal appears to be an xterm. This + option is enabled by default. Note that elinks(1) will + only attempt to set the window title if the STY environ- + ment variable is set. + + ssttaattuuss [oonn | ooffff] + Show or hide the status line. + + ssttaattuuss--aattttrr _a_t_t_r_i_b_u_t_e_s + Set status line attributes. + + ssttaattuuss--bbgg _c_o_l_o_u_r + Set status line background colour. + + ssttaattuuss--ffgg _c_o_l_o_u_r + Set status line foreground colour. + + ssttaattuuss--iinntteerrvvaall _i_n_t_e_r_v_a_l + Update the status bar every _i_n_t_e_r_v_a_l seconds. By de- + fault, updates will occur every 15 seconds. A setting of + zero disables redrawing at interval. + + ssttaattuuss--kkeeyyss [vvii | eemmaaccss] + Use vi(1)- or emacs(1)-style key bindings in the status + line, for example at the command prompt. Defaults to + emacs. + + ssttaattuuss--lleefftt _s_t_r_i_n_g + Display _s_t_r_i_n_g to the left of the status bar. _s_t_r_i_n_g + will be passed through strftime(3) before being used. By + default, the session name is shown. _s_t_r_i_n_g may contain + any of the following special character pairs: + + CChhaarraacctteerr ppaaiirr RReeppllaacceedd wwiitthh + #(command) First line of command's output + #H Hostname of local host + #S Session name + #T Current window title + ## A literal `#' + + Where appropriate, these may be prefixed with a number to + specify the maximum length, for example `#24T'. + + ssttaattuuss--lleefftt--lleennggtthh _l_e_n_g_t_h + Set the maximum _l_e_n_g_t_h of the left component of the sta- + tus bar. The default is 10. + + ssttaattuuss--rriigghhtt _s_t_r_i_n_g + Display _s_t_r_i_n_g to the right of the status bar. By de- + fault, the date and time will be shown. As with ssttaattuuss-- + lleefftt, _s_t_r_i_n_g will be passed to strftime(3) and character + pairs are replaced. + + ssttaattuuss--rriigghhtt--lleennggtthh _l_e_n_g_t_h + Set the maximum _l_e_n_g_t_h of the right component of the sta- + tus bar. The default is 40. + + sseett--ppaasssswwoorrdd [--cc] _p_a_s_s_w_o_r_d + (alias: ppaassss) + Set the server password. If the --cc option is given, a pre-en- + crypted password may be specified. By default, the password is + blank, thus any entered password will be accepted when unlocking + the server (see the lloocckk--sseerrvveerr command). To prevent variable + expansion when an encrypted password is read from a configuration + file, enclose it in single quotes ('). + + sseett--wwiinnddooww--ooppttiioonn [--gguu] [--tt _t_a_r_g_e_t_-_w_i_n_d_o_w] _o_p_t_i_o_n _v_a_l_u_e + (alias: sseettww) + Set a window-specific option. The --gg and --uu flags work similarly + to the sseett--ooppttiioonn command. + + Supported options are: + + aaggggrreessssiivvee--rreessiizzee [oonn | ooffff] + Aggressively resize the chosen window. This means that + ttmmuuxx will resize the window to the size of the smallest + session for which it is the current window, rather than + the smallest session to which it is attached. The window + may resize when the current window is changed on another + sessions; this option is good for full-screen programs + which support SIGWINCH and poor for interactive programs + such as shells. + + aauuttoommaattiicc--rreennaammee [oonn | ooffff] + Control automatic window renaming. When this setting is + enabled, ttmmuuxx will attempt - on supported platforms - to + rename the window to reflect the command currently run- + ning in it. This flag is automatically disabled for an + individual window when a name is specified at creation + with nneeww--wwiinnddooww oorr nneeww--sseessssiioonn, or later with rreennaammee-- + wwiinnddooww. It may be switched off globally with: + + set-window-option -g automatic-rename off + + cclloocckk--mmooddee--ccoolloouurr _c_o_l_o_u_r + Set clock colour. + + cclloocckk--mmooddee--ssttyyllee [1122 | 2244] + Set clock hour format. + + ffoorrccee--hheeiigghhtt _h_e_i_g_h_t + + ffoorrccee--wwiiddtthh _w_i_d_t_h + Prevent ttmmuuxx from resizing a window to greater than _w_i_d_t_h + or _h_e_i_g_h_t. A value of zero restores the default unlimit- + ed setting. + + mmooddee--aattttrr _a_t_t_r_i_b_u_t_e_s + Set window modes attributes. + + mmooddee--bbgg _c_o_l_o_u_r + Set window modes background colour. + + mmooddee--ffgg _c_o_l_o_u_r + Set window modes foreground colour. + + mmooddee--kkeeyyss [vvii | eemmaaccss] + Use vi(1)- or emacs(1)-style key bindings in scroll and + copy modes. Key bindings default to emacs. + + mmoonniittoorr--aaccttiivviittyy [oonn | ooffff] + Monitor for activity in the window. Windows with activi- + ty are highlighted in the status line. + + mmoonniittoorr--ccoonntteenntt _m_a_t_c_h_-_s_t_r_i_n_g + Monitor content in the window. When _m_a_t_c_h_-_s_t_r_i_n_g appears + in the window, it is highlighted in the status line. + + rreemmaaiinn--oonn--eexxiitt [oonn | ooffff] + A window with this flag set is not destroyed when the + program running in it exits. The window may be reacti- + vated with the rreessppaawwnn--wwiinnddooww command. + + uuttff88 [oonn | ooffff] + Instructs ttmmuuxx to expect UTF-8 sequences to appear in + this window. + + wwiinnddooww--ssttaattuuss--aattttrr _a_t_t_r_i_b_u_t_e_s + Set status line attributes for a single window. + + wwiinnddooww--ssttaattuuss--bbgg _c_o_l_o_u_r + Set status line background colour for a single window. + + wwiinnddooww--ssttaattuuss--ffgg _c_o_l_o_u_r + Set status line foreground colour for a single window. + + xxtteerrmm--kkeeyyss [oonn | ooffff] + If this option is set, ttmmuuxx will generate xterm(1)-style + function key sequences; these have a number included to + indicate modifiers such as shift, meta or ctrl. + + sshhooww--bbuuffffeerr [--bb _b_u_f_f_e_r_-_i_n_d_e_x] [--tt _t_a_r_g_e_t_-_s_e_s_s_i_o_n] + (alias: sshhoowwbb) + Display the contents of the specified buffer. + + sshhooww--ooppttiioonnss [--tt _t_a_r_g_e_t_-_s_e_s_s_i_o_n] _o_p_t_i_o_n _v_a_l_u_e + (alias: sshhooww) + Show the currently set options. If a _t_a_r_g_e_t_-_s_e_s_s_i_o_n is speci- + fied, the options for that session are shown; otherwise, the + global options are listed. + + sshhooww--wwiinnddooww--ooppttiioonnss [--tt _t_a_r_g_e_t_-_w_i_n_d_o_w] _o_p_t_i_o_n _v_a_l_u_e + (alias: sshhoowwww) + List the current options for the given window. + + ssoouurrccee--ffiillee _p_a_t_h + (alias: ssoouurrccee) + Execute commands from _p_a_t_h. + + sspplliitt--wwiinnddooww [--dd] [--ll _l_i_n_e_s | --pp _p_e_r_c_e_n_t_a_g_e] [--tt _t_a_r_g_e_t_-_w_i_n_d_o_w] [_c_o_m_m_a_n_d] + (alias: splitw) + Creates a new window by splitting it vertically. The --ll and --pp + options specify the size of the new window in lines, or as a per- + centage, respectively. All other options have the same meaning + as in the nneeww--wwiinnddooww command. + + A few notes with regard to panes: + 1. If attempting to split a window with less than eight lines, + an error will be shown. + 2. If the window is resized, as many panes are shown as can fit + without reducing them below four lines. + 3. The minimum pane size is four lines (including the separator + line). + 4. The panes are indexed from top (0) to bottom, with no num- + bers skipped. + + ssttaarrtt--sseerrvveerr + (alias: ssttaarrtt) + Start the ttmmuuxx server, if not already running, without creating + any sessions. + + ssuussppeenndd--cclliieenntt [--cc --ttaarrggeett--cclliieenntt] + (alias: ssuussppeennddcc) + Suspend a client by sending SIGTSTP (tty stop). + + sswwaapp--ppaannee [--ddDDUU] [--pp _s_r_c_-_i_n_d_e_x] [--tt _t_a_r_g_e_t_-_w_i_n_d_o_w] [--qq _d_s_t_-_i_n_d_e_x] + (alias: sswwaapppp) + Swap two panes within a window. If --UU is used, the pane is + swapped with the pane above (before it numerically); --DD swaps + with the pane below (the next numerically); or _d_s_t_-_i_n_d_e_x may be + give to swap with a specific pane. + + sswwaapp--wwiinnddooww [--dd] [--ss _s_r_c_-_w_i_n_d_o_w] [--tt _d_s_t_-_w_i_n_d_o_w] + (alias: sswwaappww) + This is similar to lliinnkk--wwiinnddooww, except the source and destination + windows are swapped. It is an error if no window exists at _s_r_c_- + _w_i_n_d_o_w. + + sswwiittcchh--cclliieenntt [--cc _t_a_r_g_e_t_-_c_l_i_e_n_t --tt _t_a_r_g_e_t_-_s_e_s_s_i_o_n] + (alias: sswwiittcchhcc) + Switch the current session for client _t_a_r_g_e_t_-_c_l_i_e_n_t to _t_a_r_g_e_t_- + _s_e_s_s_i_o_n. + + uunnbbiinndd--kkeeyy _k_e_y + (alias: uunnbbiinndd) + Unbind the key bound to _k_e_y. + + uunnlliinnkk--wwiinnddooww [--tt _t_a_r_g_e_t_-_w_i_n_d_o_w] + (alias: uunnlliinnkkww) + Unlink _t_a_r_g_e_t_-_w_i_n_d_o_w. A window may be unlinked only if it is + linked to multiple sessions - windows may not be linked to no + sessions. + + uupp--ppaannee [--pp _p_a_n_e_-_i_n_d_e_x] [--tt _t_a_r_g_e_t_-_w_i_n_d_o_w] + (alias: uupppp) + Move up a pane. + +FFIILLEESS + ~/.tmux.conf + default ttmmuuxx configuration file + +SSEEEE AALLSSOO + pty(4) + +AAUUTTHHOORRSS + Nicholas Marriott <nicm@users.sourceforge.net> + +OpenBSD 4.5 April 20, 2009 13 diff --git a/usr.bin/tmux/tmux.h b/usr.bin/tmux/tmux.h new file mode 100644 index 00000000000..f39f70f601c --- /dev/null +++ b/usr.bin/tmux/tmux.h @@ -0,0 +1,1576 @@ +/* $OpenBSD: tmux.h,v 1.1 2009/06/01 22:58:49 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. + */ + +#ifndef TMUX_H +#define TMUX_H + +#define PROTOCOL_VERSION -13 + +#include <sys/param.h> +#include <sys/time.h> +#include <sys/queue.h> +#include <sys/tree.h> + +#include <getopt.h> +#include <limits.h> +#include <poll.h> +#include <signal.h> +#include <stdarg.h> +#include <stdint.h> +#include <stdio.h> +#include <termios.h> + +#include "array.h" + +extern const char *__progname; + +/* Default configuration file. */ +#define DEFAULT_CFG ".tmux.conf" + +/* Default prompt history length. */ +#define PROMPT_HISTORY 100 + +/* Minimum pane size. */ +#define PANE_MINIMUM 4 /* includes separator line */ + +/* Automatic name refresh interval, in milliseconds. */ +#define NAME_INTERVAL 500 + +/* Escape timer period, in milliseconds. */ +#define ESCAPE_PERIOD 250 + +/* Maximum poll timeout (when attached). */ +#define POLL_TIMEOUT 50 + +/* Fatal errors. */ +#define fatal(msg) log_fatal("%s: %s", __func__, msg); +#define fatalx(msg) log_fatalx("%s: %s", __func__, msg); + +/* Definition to shut gcc up about unused arguments. */ +#define unused __attribute__ ((unused)) + +/* Attribute to make gcc check printf-like arguments. */ +#define printflike1 __attribute__ ((format (printf, 1, 2))) +#define printflike2 __attribute__ ((format (printf, 2, 3))) +#define printflike3 __attribute__ ((format (printf, 3, 4))) +#define printflike4 __attribute__ ((format (printf, 4, 5))) + +/* Number of items in array. */ +#define nitems(_a) (sizeof((_a)) / sizeof((_a)[0])) + +/* Buffer macros. */ +#define BUFFER_USED(b) ((b)->size) +#define BUFFER_FREE(b) ((b)->space - (b)->off - (b)->size) +#define BUFFER_IN(b) ((b)->base + (b)->off + (b)->size) +#define BUFFER_OUT(b) ((b)->base + (b)->off) + +/* Buffer structure. */ +struct buffer { + u_char *base; /* buffer start */ + size_t space; /* total size of buffer */ + + size_t size; /* size of data in buffer */ + size_t off; /* offset of data in buffer */ +}; + +/* Bell option values. */ +#define BELL_NONE 0 +#define BELL_ANY 1 +#define BELL_CURRENT 2 + +/* Key codes. ncurses defines KEY_*. Grrr. */ +#define KEYC_NONE 0x00ffff +#define KEYC_OFFSET 0x010000 +#define KEYC_ESCAPE 0x020000 +#define KEYC_CONTROL 0x080000 +#define KEYC_SHIFT 0x100000 + +#define KEYC_ADDESC(k) ((k) | KEYC_ESCAPE) +#define KEYC_REMOVEESC(k) ((k) & ~KEYC_ESCAPE) +#define KEYC_ISESC(k) ((k) != KEYC_NONE && ((k) & KEYC_ESCAPE)) + +#define KEYC_ADDCTL(k) ((k) | KEYC_CONTROL) +#define KEYC_REMOVECTL(k) ((k) & ~KEYC_CONTROL) +#define KEYC_ISCTL(k) ((k) != KEYC_NONE && ((k) & KEYC_CONTROL)) + +#define KEYC_ADDSFT(k) ((k) | KEYC_SHIFT) +#define KEYC_REMOVESFT(k) ((k) & ~KEYC_SHIFT) +#define KEYC_ISSFT(k) ((k) != KEYC_NONE && ((k) & KEYC_SHIFT)) + +/* Mouse key. */ +#define KEYC_MOUSE (KEYC_OFFSET + 0x00) + +/* Function keys. */ +#define KEYC_F1 (KEYC_OFFSET + 0x01) +#define KEYC_F2 (KEYC_OFFSET + 0x02) +#define KEYC_F3 (KEYC_OFFSET + 0x03) +#define KEYC_F4 (KEYC_OFFSET + 0x04) +#define KEYC_F5 (KEYC_OFFSET + 0x05) +#define KEYC_F6 (KEYC_OFFSET + 0x06) +#define KEYC_F7 (KEYC_OFFSET + 0x07) +#define KEYC_F8 (KEYC_OFFSET + 0x08) +#define KEYC_F9 (KEYC_OFFSET + 0x09) +#define KEYC_F10 (KEYC_OFFSET + 0x10) +#define KEYC_F11 (KEYC_OFFSET + 0x11) +#define KEYC_F12 (KEYC_OFFSET + 0x12) +#define KEYC_F13 (KEYC_OFFSET + 0x13) +#define KEYC_F14 (KEYC_OFFSET + 0x14) +#define KEYC_F15 (KEYC_OFFSET + 0x15) +#define KEYC_F16 (KEYC_OFFSET + 0x16) +#define KEYC_F17 (KEYC_OFFSET + 0x17) +#define KEYC_F18 (KEYC_OFFSET + 0x18) +#define KEYC_F19 (KEYC_OFFSET + 0x19) +#define KEYC_F20 (KEYC_OFFSET + 0x1a) +#define KEYC_IC (KEYC_OFFSET + 0x1b) +#define KEYC_DC (KEYC_OFFSET + 0x1c) +#define KEYC_HOME (KEYC_OFFSET + 0x1d) +#define KEYC_END (KEYC_OFFSET + 0x1e) +#define KEYC_NPAGE (KEYC_OFFSET + 0x1f) +#define KEYC_PPAGE (KEYC_OFFSET + 0x20) +#define KEYC_BTAB (KEYC_OFFSET + 0x21) + +/* Arrow keys. */ +#define KEYC_UP (KEYC_OFFSET + 0x50) +#define KEYC_DOWN (KEYC_OFFSET + 0x51) +#define KEYC_LEFT (KEYC_OFFSET + 0x52) +#define KEYC_RIGHT (KEYC_OFFSET + 0x53) + +/* Numeric keypad. Numbered from top-left, KPY_X. */ +#define KEYC_KP0_1 (KEYC_OFFSET + 0x100) +#define KEYC_KP0_2 (KEYC_OFFSET + 0x101) +#define KEYC_KP0_3 (KEYC_OFFSET + 0x102) +#define KEYC_KP1_0 (KEYC_OFFSET + 0x103) +#define KEYC_KP1_1 (KEYC_OFFSET + 0x104) +#define KEYC_KP1_2 (KEYC_OFFSET + 0x105) +#define KEYC_KP1_3 (KEYC_OFFSET + 0x106) +#define KEYC_KP2_0 (KEYC_OFFSET + 0x107) +#define KEYC_KP2_1 (KEYC_OFFSET + 0x108) +#define KEYC_KP2_2 (KEYC_OFFSET + 0x109) +#define KEYC_KP3_0 (KEYC_OFFSET + 0x10a) +#define KEYC_KP3_1 (KEYC_OFFSET + 0x10b) +#define KEYC_KP3_2 (KEYC_OFFSET + 0x10c) +#define KEYC_KP3_3 (KEYC_OFFSET + 0x10d) +#define KEYC_KP4_0 (KEYC_OFFSET + 0x10e) +#define KEYC_KP4_2 (KEYC_OFFSET + 0x10f) + +/* Termcap codes. */ +enum tty_code_code { + TTYC_AX = 0, + TTYC_ACSC, /* acs_chars, ac */ + TTYC_BEL, /* bell, bl */ + TTYC_BLINK, /* enter_blink_mode, mb */ + TTYC_BOLD, /* enter_bold_mode, md */ + TTYC_CIVIS, /* cursor_invisible, vi */ + TTYC_CLEAR, /* clear_screen, cl */ + TTYC_CNORM, /* cursor_normal, ve */ + TTYC_COLORS, /* max_colors, Co */ + TTYC_CSR, /* change_scroll_region, cs */ + TTYC_CUD, /* parm_down_cursor, DO */ + TTYC_CUD1, /* cursor_down, do */ + TTYC_CUP, /* cursor_address, cm */ + TTYC_DCH, /* parm_dch, DC */ + TTYC_DCH1, /* delete_character, dc */ + TTYC_DIM, /* enter_dim_mode, mh */ + TTYC_DL, /* parm_delete_line, DL */ + TTYC_DL1, /* delete_line, dl */ + TTYC_EL, /* clr_eol, ce */ + TTYC_EL1, /* clr_bol, cb */ + TTYC_ENACS, /* ena_acs, eA */ + TTYC_ICH, /* parm_ich, IC */ + TTYC_ICH1, /* insert_character, ic */ + TTYC_IL, /* parm_insert_line, IL */ + TTYC_IL1, /* insert_line, il */ + TTYC_INVIS, /* enter_secure_mode, mk */ + TTYC_IS1, /* init_1string, i1 */ + TTYC_IS2, /* init_2string, i2 */ + TTYC_IS3, /* init_3string, i3 */ + TTYC_KCBT, /* key_btab, kB */ + TTYC_KCUB1, /* key_left, kl */ + TTYC_KCUD1, /* key_down, kd */ + TTYC_KCUF1, /* key_right, kr */ + TTYC_KCUU1, /* key_up, ku */ + TTYC_KDCH1, /* key_dc, kD */ + TTYC_KEND, /* key_end, ke */ + TTYC_KF1, /* key_f1, k1 */ + TTYC_KF10, /* key_f10, k; */ + TTYC_KF11, /* key_f11, F1 */ + TTYC_KF12, /* key_f12, F2 */ + TTYC_KF13, /* key_f13, F3 */ + TTYC_KF14, /* key_f14, F4 */ + TTYC_KF15, /* key_f15, F5 */ + TTYC_KF16, /* key_f16, F6 */ + TTYC_KF17, /* key_f17, F7 */ + TTYC_KF18, /* key_f18, F8 */ + TTYC_KF19, /* key_f19, F9 */ + TTYC_KF20, /* key_f20, F10 */ + TTYC_KF2, /* key_f2, k2 */ + TTYC_KF3, /* key_f3, k3 */ + TTYC_KF4, /* key_f4, k4 */ + TTYC_KF5, /* key_f5, k5 */ + TTYC_KF6, /* key_f6, k6 */ + TTYC_KF7, /* key_f7, k7 */ + TTYC_KF8, /* key_f8, k8 */ + TTYC_KF9, /* key_f9, k9 */ + TTYC_KHOME, /* key_home, kh */ + TTYC_KICH1, /* key_ic, kI */ + TTYC_KMOUS, /* key_mouse, Km */ + TTYC_KNP, /* key_npage, kN */ + TTYC_KPP, /* key_ppage, kP */ + TTYC_OP, /* orig_pair, op */ + TTYC_REV, /* enter_reverse_mode, mr */ + TTYC_RI, /* scroll_reverse, sr */ + TTYC_RMACS, /* exit_alt_charset_mode */ + TTYC_RMCUP, /* exit_ca_mode, te */ + TTYC_RMIR, /* exit_insert_mode, ei */ + TTYC_RMKX, /* keypad_local, ke */ + TTYC_SETAB, /* set_a_background, AB */ + TTYC_SETAF, /* set_a_foreground, AF */ + TTYC_SGR0, /* exit_attribute_mode, me */ + TTYC_SMACS, /* enter_alt_charset_mode, as */ + TTYC_SMCUP, /* enter_ca_mode, ti */ + TTYC_SMIR, /* enter_insert_mode, im */ + TTYC_SMKX, /* keypad_xmit, ks */ + TTYC_SMSO, /* enter_standout_mode, so */ + TTYC_SMUL, /* enter_underline_mode, us */ + TTYC_XENL, /* eat_newline_glitch, xn */ +}; +#define NTTYCODE (TTYC_XENL + 1) + +/* Termcap types. */ +enum tty_code_type { + TTYCODE_NONE = 0, + TTYCODE_STRING, + TTYCODE_NUMBER, + TTYCODE_FLAG, +}; + +/* Termcap code. */ +struct tty_code { + enum tty_code_type type; + union { + char *string; + int number; + int flag; + } value; +}; + +/* Entry in terminal code table. */ +struct tty_term_code_entry { + enum tty_code_code code; + enum tty_code_type type; + const char *name; +}; + +/* Output commands. */ +enum tty_cmd { + TTY_CELL, + TTY_CLEARENDOFLINE, + TTY_CLEARENDOFSCREEN, + TTY_CLEARLINE, + TTY_CLEARSCREEN, + TTY_CLEARSTARTOFLINE, + TTY_CLEARSTARTOFSCREEN, + TTY_DELETECHARACTER, + TTY_DELETELINE, + TTY_INSERTCHARACTER, + TTY_INSERTLINE, + TTY_LINEFEED, + TTY_RAW, + TTY_REVERSEINDEX, +}; + +/* Message codes. */ +enum hdrtype { + MSG_COMMAND, + MSG_DETACH, + MSG_ERROR, + MSG_EXIT, + MSG_EXITED, + MSG_EXITING, + MSG_IDENTIFY, + MSG_PRINT, + MSG_READY, + MSG_RESIZE, + MSG_SHUTDOWN, + MSG_SUSPEND, + MSG_UNLOCK, + MSG_WAKEUP, +}; + +/* Message header structure. */ +struct hdr { + enum hdrtype type; + size_t size; +}; + +struct msg_command_data { + pid_t pid; /* pid from $TMUX or -1 */ + u_int idx; /* index from $TMUX */ + + size_t namelen; +}; + +struct msg_identify_data { + char tty[TTY_NAME_MAX]; + int version; + + char cwd[MAXPATHLEN]; + +#define IDENTIFY_UTF8 0x1 +#define IDENTIFY_256COLOURS 0x2 +#define IDENTIFY_88COLOURS 0x4 +#define IDENTIFY_HASDEFAULTS 0x8 + int flags; + + u_int sx; + u_int sy; + + size_t termlen; +}; + +struct msg_resize_data { + u_int sx; + u_int sy; +}; + +/* Editing keys. */ +enum mode_key_cmd { + MODEKEYCMD_BACKSPACE = 0x1000, + MODEKEYCMD_CHOOSE, + MODEKEYCMD_CLEARSELECTION, + MODEKEYCMD_COMPLETE, + MODEKEYCMD_COPYSELECTION, + MODEKEYCMD_DELETE, + MODEKEYCMD_DOWN, + MODEKEYCMD_ENDOFLINE, + MODEKEYCMD_LEFT, + MODEKEYCMD_NEXTPAGE, + MODEKEYCMD_NEXTWORD, + MODEKEYCMD_NONE, + MODEKEYCMD_OTHERKEY, + MODEKEYCMD_PASTE, + MODEKEYCMD_PREVIOUSPAGE, + MODEKEYCMD_PREVIOUSWORD, + MODEKEYCMD_QUIT, + MODEKEYCMD_RIGHT, + MODEKEYCMD_STARTOFLINE, + MODEKEYCMD_STARTSELECTION, + MODEKEYCMD_UP, +}; + +struct mode_key_data { + int type; + + int flags; +#define MODEKEY_EDITMODE 0x1 +#define MODEKEY_CANEDIT 0x2 +#define MODEKEY_CHOOSEMODE 0x4 +}; + +#define MODEKEY_EMACS 0 +#define MODEKEY_VI 1 + +/* Modes. */ +#define MODE_CURSOR 0x1 +#define MODE_INSERT 0x2 +#define MODE_KCURSOR 0x4 +#define MODE_KKEYPAD 0x8 +#define MODE_MOUSE 0x10 + +/* Grid output. */ +#if defined(DEBUG) && \ + ((defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L) || \ + (defined(__GNUC__) && __GNUC__ >= 3)) +#define GRID_DEBUG(gd, fmt, ...) log_debug3("%s: (sx=%u, sy=%u, hsize=%u) " \ + fmt, __func__, (gd)->sx, (gd)->sy, (gd)->hsize, ## __VA_ARGS__) +#else +#define GRID_DEBUG(...) +#endif + +/* Grid attributes. */ +#define GRID_ATTR_BRIGHT 0x1 +#define GRID_ATTR_DIM 0x2 +#define GRID_ATTR_UNDERSCORE 0x4 +#define GRID_ATTR_BLINK 0x8 +#define GRID_ATTR_REVERSE 0x10 +#define GRID_ATTR_HIDDEN 0x20 +#define GRID_ATTR_ITALICS 0x40 +#define GRID_ATTR_CHARSET 0x80 /* alternative character set */ + +/* Grid flags. */ +#define GRID_FLAG_FG256 0x1 +#define GRID_FLAG_BG256 0x2 +#define GRID_FLAG_PADDING 0x4 +#define GRID_FLAG_UTF8 0x8 + +/* Grid cell data. */ +struct grid_cell { + u_char attr; + u_char flags; + u_char fg; + u_char bg; + u_char data; +} __packed; + +/* Grid cell UTF-8 data. Used instead of data in grid_cell for UTF-8 cells. */ +#define UTF8_SIZE 8 +struct grid_utf8 { + u_char width; + u_char data[UTF8_SIZE]; +} __packed; + +/* Entire grid of cells. */ +struct grid { + u_int sx; + u_int sy; + + u_int hsize; + u_int hlimit; + + u_int *size; + struct grid_cell **data; + + u_int *usize; + struct grid_utf8 **udata; +}; + +/* Option data structures. */ +struct options_entry { + char *name; + + enum { + OPTIONS_STRING, + OPTIONS_NUMBER, + OPTIONS_KEY, + } type; + union { + char *string; + long long number; + int key; + } value; + + SPLAY_ENTRY(options_entry) entry; +}; + +struct options { + SPLAY_HEAD(options_tree, options_entry) tree; + struct options *parent; +}; + +/* Screen selection. */ +struct screen_sel { + int flag; + + u_int sx; + u_int sy; + + u_int ex; + u_int ey; + + struct grid_cell cell; +}; + +/* Virtual screen. */ +struct screen { + char *title; + + struct grid *grid; /* grid data */ + + u_int cx; /* cursor x */ + u_int cy; /* cursor y */ + + u_int old_cx; + u_int old_cy; + + u_int rupper; /* scroll region top */ + u_int rlower; /* scroll region bottom */ + + u_int old_rupper; + u_int old_rlower; + + int mode; + + struct screen_sel sel; +}; + +/* Screen write context. */ +struct screen_write_ctx { + struct window_pane *wp; + struct screen *s; +}; + +/* Screen size. */ +#define screen_size_x(s) ((s)->grid->sx) +#define screen_size_y(s) ((s)->grid->sy) +#define screen_hsize(s) ((s)->grid->hsize) +#define screen_hlimit(s) ((s)->grid->hlimit) + +/* Input parser sequence argument. */ +struct input_arg { + u_char data[64]; + size_t used; +}; + +/* Input parser context. */ +struct input_ctx { + struct window_pane *wp; + struct screen_write_ctx ctx; + + u_char *buf; + size_t len; + size_t off; + + struct grid_cell cell; + + + struct grid_cell saved_cell; + u_int saved_cx; + u_int saved_cy; + +#define MAXSTRINGLEN 1024 + u_char *string_buf; + size_t string_len; + int string_type; +#define STRING_SYSTEM 0 +#define STRING_APPLICATION 1 +#define STRING_NAME 2 + + u_char utf8_buf[4]; + u_int utf8_len; + u_int utf8_off; + + u_char intermediate; + void *(*state)(u_char, struct input_ctx *); + + u_char private; + ARRAY_DECL(, struct input_arg) args; +}; + +/* + * Window mode. Windows can be in several modes and this is used to call the + * right function to handle input and output. + */ +struct client; +struct window; +struct window_mode { + struct screen *(*init)(struct window_pane *); + void (*free)(struct window_pane *); + void (*resize)(struct window_pane *, u_int, u_int); + void (*key)(struct window_pane *, struct client *, int); + void (*mouse)(struct window_pane *, + struct client *, u_char, u_char, u_char); + void (*timer)(struct window_pane *); +}; + +/* Child window structure. */ +struct window_pane { + struct window *window; + + u_int sx; + u_int sy; + + u_int xoff; + u_int yoff; + + int flags; +#define PANE_HIDDEN 0x1 +#define PANE_RESTART 0x2 +#define PANE_REDRAW 0x4 + + char *cmd; + char *cwd; + + pid_t pid; + int fd; + char tty[TTY_NAME_MAX]; + struct buffer *in; + struct buffer *out; + + struct input_ctx ictx; + + struct screen *screen; + struct screen base; + + const struct window_mode *mode; + void *modedata; + + TAILQ_ENTRY(window_pane) entry; +}; +TAILQ_HEAD(window_panes, window_pane); + +/* Window structure. */ +struct window { + char *name; + struct timeval name_timer; + + struct window_pane *active; + struct window_panes panes; + u_int layout; + + u_int sx; + u_int sy; + + int flags; +#define WINDOW_BELL 0x1 +#define WINDOW_HIDDEN 0x2 +#define WINDOW_ACTIVITY 0x4 +#define WINDOW_CONTENT 0x6 +#define WINDOW_REDRAW 0x8 + + struct options options; + + u_int references; +}; +ARRAY_DECL(windows, struct window *); + +/* Entry on local window list. */ +struct winlink { + int idx; + struct window *window; + + RB_ENTRY(winlink) entry; + SLIST_ENTRY(winlink) sentry; +}; +RB_HEAD(winlinks, winlink); +SLIST_HEAD(winlink_stack, winlink); + +/* Paste buffer. */ +struct paste_buffer { + char *data; + struct timeval tv; +}; +ARRAY_DECL(paste_stack, struct paste_buffer *); + +/* Client session. */ +struct session_alert { + struct winlink *wl; + int type; + + SLIST_ENTRY(session_alert) entry; +}; + +struct session { + char *name; + struct timeval tv; + + u_int sx; + u_int sy; + + struct winlink *curw; + struct winlink_stack lastw; + struct winlinks windows; + + struct options options; + + struct paste_stack buffers; + + SLIST_HEAD(, session_alert) alerts; + +#define SESSION_UNATTACHED 0x1 /* not attached to any clients */ + int flags; +}; +ARRAY_DECL(sessions, struct session *); + +/* TTY information. */ +struct tty_key { + int key; + char *string; + + int flags; +#define TTYKEY_CTRL 0x1 +#define TTYKEY_RAW 0x2 + + RB_ENTRY(tty_key) entry; +}; + +struct tty_term { + char *name; + u_int references; + + struct tty_code codes[NTTYCODE]; + +#define TERM_HASDEFAULTS 0x1 +#define TERM_256COLOURS 0x2 +#define TERM_88COLOURS 0x4 +#define TERM_EARLYWRAP 0x8 + int flags; + + SLIST_ENTRY(tty_term) entry; +}; +SLIST_HEAD(tty_terms, tty_term); + +struct tty { + char *path; + + u_int sx; + u_int sy; + + u_int cx; + u_int cy; + + int mode; + + u_int rlower; + u_int rupper; + + char *termname; + struct tty_term *term; + + int fd; + struct buffer *in; + struct buffer *out; + + int log_fd; + + struct termios tio; + + struct grid_cell cell; + + u_char acs[UCHAR_MAX + 1]; + +#define TTY_NOCURSOR 0x1 +#define TTY_FREEZE 0x2 +#define TTY_ESCAPE 0x4 +#define TTY_UTF8 0x8 + int flags; + + int term_flags; + + struct timeval key_timer; + + size_t ksize; /* maximum key size */ + RB_HEAD(tty_keys, tty_key) ktree; +}; + +/* Client connection. */ +struct client { + int fd; + struct buffer *in; + struct buffer *out; + + char *title; + char *cwd; + + struct tty tty; + struct timeval status_timer; + struct timeval repeat_timer; + + struct screen status; + +#define CLIENT_TERMINAL 0x1 +#define CLIENT_PREFIX 0x2 +#define CLIENT_MOUSE 0x4 +#define CLIENT_REDRAW 0x8 +#define CLIENT_STATUS 0x10 +#define CLIENT_REPEAT 0x20 /* allow command to repeat within repeat time */ +#define CLIENT_SUSPENDED 0x40 + int flags; + + char *message_string; + struct timeval message_timer; + + char *prompt_string; + char *prompt_buffer; + size_t prompt_index; + int (*prompt_callback)(void *, const char *); + void *prompt_data; + +#define PROMPT_HIDDEN 0x1 +#define PROMPT_SINGLE 0x2 + int prompt_flags; + + u_int prompt_hindex; + ARRAY_DECL(, char *) prompt_hdata; + + struct mode_key_data prompt_mdata; + + struct session *session; +}; +ARRAY_DECL(clients, struct client *); + +/* Client context. */ +struct client_ctx { + int srv_fd; + struct buffer *srv_in; + struct buffer *srv_out; + +#define CCTX_DETACH 0x1 +#define CCTX_EXIT 0x2 +#define CCTX_SHUTDOWN 0x4 + int flags; +}; + +/* Key/command line command. */ +struct cmd_ctx { + struct client *cmdclient; + + struct client *curclient; + struct session *cursession; + struct msg_command_data *msgdata; + + void (*print)(struct cmd_ctx *, const char *, ...); + void (*info)(struct cmd_ctx *, const char *, ...); + void (*error)(struct cmd_ctx *, const char *, ...); +}; + +struct cmd { + const struct cmd_entry *entry; + void *data; + + TAILQ_ENTRY(cmd) qentry; +}; +TAILQ_HEAD(cmd_list, cmd); + +struct cmd_entry { + const char *name; + const char *alias; + const char *usage; + +#define CMD_STARTSERVER 0x1 +#define CMD_CANTNEST 0x2 +#define CMD_ARG1 0x4 +#define CMD_ARG01 0x8 +#define CMD_AFLAG 0x10 +#define CMD_DFLAG 0x20 +#define CMD_GFLAG 0x40 +#define CMD_KFLAG 0x80 +#define CMD_UFLAG 0x100 +#define CMD_BIGDFLAG 0x200 +#define CMD_BIGUFLAG 0x400 + + int flags; + + void (*init)(struct cmd *, int); + int (*parse)(struct cmd *, int, char **, char **); + int (*exec)(struct cmd *, struct cmd_ctx *); + void (*send)(struct cmd *, struct buffer *); + void (*recv)(struct cmd *, struct buffer *); + void (*free)(struct cmd *); + size_t (*print)(struct cmd *, char *, size_t); +}; + +/* Generic command data. */ +struct cmd_target_data { + int flags; + char *target; + char *arg; +}; + +struct cmd_srcdst_data { + int flags; + char *src; + char *dst; + char *arg; +}; + +struct cmd_buffer_data { + int flags; + char *target; + int buffer; + char *arg; +}; + +struct cmd_option_data { + int flags; + char *target; + char *option; + char *value; +}; + +struct cmd_pane_data { + int flags; + char *target; + char *arg; + int pane; +}; + +/* Key binding. */ +struct key_binding { + int key; + struct cmd_list *cmdlist; + int can_repeat; + + SPLAY_ENTRY(key_binding) entry; +}; +SPLAY_HEAD(key_bindings, key_binding); + +/* Set/display option data. */ +struct set_option_entry { + const char *name; + enum { + SET_OPTION_STRING, + SET_OPTION_NUMBER, + SET_OPTION_KEY, + SET_OPTION_COLOUR, + SET_OPTION_ATTRIBUTES, + SET_OPTION_FLAG, + SET_OPTION_CHOICE + } type; + + u_int minimum; + u_int maximum; + + const char **choices; +}; +extern const struct set_option_entry set_option_table[]; +extern const struct set_option_entry set_window_option_table[]; +#define NSETOPTION 24 +#define NSETWINDOWOPTION 19 + +/* tmux.c */ +extern volatile sig_atomic_t sigwinch; +extern volatile sig_atomic_t sigterm; +extern volatile sig_atomic_t sigcont; +extern volatile sig_atomic_t sigchld; +extern volatile sig_atomic_t sigusr1; +extern volatile sig_atomic_t sigusr2; +extern struct options global_options; +extern struct options global_window_options; +extern char *cfg_file; +extern int server_locked; +extern char *server_password; +extern time_t server_activity; +extern int debug_level; +extern int be_quiet; +extern time_t start_time; +extern char *socket_path; +void logfile(const char *); +void siginit(void); +void sigreset(void); +void sighandler(int); + +/* cfg.c */ +int load_cfg(const char *, char **x); + +/* mode-key.c */ +void mode_key_init(struct mode_key_data *, int, int); +void mode_key_free(struct mode_key_data *); +enum mode_key_cmd mode_key_lookup(struct mode_key_data *, int); + +/* options.c */ +int options_cmp(struct options_entry *, struct options_entry *); +SPLAY_PROTOTYPE(options_tree, options_entry, entry, options_cmp); +void options_init(struct options *, struct options *); +void options_free(struct options *); +struct options_entry *options_find1(struct options *, const char *); +struct options_entry *options_find(struct options *, const char *); +int options_remove(struct options *, const char *); +void printflike3 options_set_string( + struct options *, const char *, const char *, ...); +char *options_get_string(struct options *, const char *); +void options_set_number(struct options *, const char *, long long); +long long options_get_number(struct options *, const char *); + +/* tty.c */ +u_char tty_get_acs(struct tty *, u_char); +void tty_emulate_repeat(struct tty *, + enum tty_code_code, enum tty_code_code, u_int); +void tty_reset(struct tty *); +void tty_region(struct tty *, u_int, u_int, u_int); +void tty_cursor(struct tty *, u_int, u_int, u_int, u_int); +void tty_cell(struct tty *, + const struct grid_cell *, const struct grid_utf8 *); +void tty_putcode(struct tty *, enum tty_code_code); +void tty_putcode1(struct tty *, enum tty_code_code, int); +void tty_putcode2(struct tty *, enum tty_code_code, int, int); +void tty_puts(struct tty *, const char *); +void tty_putc(struct tty *, u_char); +void tty_init(struct tty *, char *, char *); +void tty_start_tty(struct tty *); +void tty_stop_tty(struct tty *); +void tty_detect_utf8(struct tty *); +void tty_set_title(struct tty *, const char *); +void tty_update_mode(struct tty *, int); +void tty_draw_line( + struct tty *, struct screen *, u_int, u_int, u_int); +void tty_redraw_region(struct tty *, struct window_pane *); +int tty_open(struct tty *, char **); +void tty_close(struct tty *, int); +void tty_free(struct tty *, int); +void tty_write( + struct tty *, struct window_pane *, enum tty_cmd, ...); +void tty_vwrite( + struct tty *, struct window_pane *, enum tty_cmd, va_list); + +/* tty-term.c */ +extern struct tty_terms tty_terms; +extern struct tty_term_code_entry tty_term_codes[NTTYCODE]; +struct tty_term *tty_term_find(char *, int, char **); +void tty_term_free(struct tty_term *); +int tty_term_has(struct tty_term *, enum tty_code_code); +const char *tty_term_string(struct tty_term *, enum tty_code_code); +const char *tty_term_string1(struct tty_term *, enum tty_code_code, int); +const char *tty_term_string2( + struct tty_term *, enum tty_code_code, int, int); +int tty_term_number(struct tty_term *, enum tty_code_code); +int tty_term_flag(struct tty_term *, enum tty_code_code); + +/* tty-keys.c */ +int tty_keys_cmp(struct tty_key *, struct tty_key *); +RB_PROTOTYPE(tty_keys, tty_key, entry, tty_keys_cmp); +void tty_keys_init(struct tty *); +void tty_keys_free(struct tty *); +int tty_keys_next(struct tty *, int *, u_char *); + +/* tty-write.c */ +void tty_write_cmd(struct window_pane *, enum tty_cmd, ...); +void tty_write_mode(struct window_pane *, int); + +/* options-cmd.c */ +void set_option_string(struct cmd_ctx *, + struct options *, const struct set_option_entry *, char *); +void set_option_number(struct cmd_ctx *, + struct options *, const struct set_option_entry *, char *); +void set_option_key(struct cmd_ctx *, + struct options *, const struct set_option_entry *, char *); +void set_option_colour(struct cmd_ctx *, + struct options *, const struct set_option_entry *, char *); +void set_option_attributes(struct cmd_ctx *, + struct options *, const struct set_option_entry *, char *); +void set_option_flag(struct cmd_ctx *, + struct options *, const struct set_option_entry *, char *); +void set_option_choice(struct cmd_ctx *, + struct options *, const struct set_option_entry *, char *); + +/* paste.c */ +void paste_init_stack(struct paste_stack *); +void paste_free_stack(struct paste_stack *); +struct paste_buffer *paste_walk_stack(struct paste_stack *, uint *); +struct paste_buffer *paste_get_top(struct paste_stack *); +struct paste_buffer *paste_get_index(struct paste_stack *, u_int); +int paste_free_top(struct paste_stack *); +int paste_free_index(struct paste_stack *, u_int); +void paste_add(struct paste_stack *, char *, u_int); +int paste_replace(struct paste_stack *, u_int, char *); + +/* clock.c */ +void clock_draw(struct screen_write_ctx *, u_int, int); + +/* arg.c */ +struct client *arg_parse_client(const char *); +struct session *arg_parse_session(const char *); +int arg_parse_window(const char *, struct session **, int *); + +/* cmd.c */ +struct cmd *cmd_parse(int, char **, char **); +int cmd_exec(struct cmd *, struct cmd_ctx *); +void cmd_send(struct cmd *, struct buffer *); +struct cmd *cmd_recv(struct buffer *); +void cmd_free(struct cmd *); +size_t cmd_print(struct cmd *, char *, size_t); +void cmd_send_string(struct buffer *, const char *); +char *cmd_recv_string(struct buffer *); +struct session *cmd_current_session(struct cmd_ctx *); +struct client *cmd_find_client(struct cmd_ctx *, const char *); +struct session *cmd_find_session(struct cmd_ctx *, const char *); +struct winlink *cmd_find_window( + struct cmd_ctx *, const char *, struct session **); +extern const struct cmd_entry *cmd_table[]; +extern const struct cmd_entry cmd_attach_session_entry; +extern const struct cmd_entry cmd_bind_key_entry; +extern const struct cmd_entry cmd_break_pane_entry; +extern const struct cmd_entry cmd_choose_session_entry; +extern const struct cmd_entry cmd_choose_window_entry; +extern const struct cmd_entry cmd_clear_history_entry; +extern const struct cmd_entry cmd_clock_mode_entry; +extern const struct cmd_entry cmd_command_prompt_entry; +extern const struct cmd_entry cmd_confirm_before_entry; +extern const struct cmd_entry cmd_copy_buffer_entry; +extern const struct cmd_entry cmd_copy_mode_entry; +extern const struct cmd_entry cmd_delete_buffer_entry; +extern const struct cmd_entry cmd_detach_client_entry; +extern const struct cmd_entry cmd_down_pane_entry; +extern const struct cmd_entry cmd_find_window_entry; +extern const struct cmd_entry cmd_has_session_entry; +extern const struct cmd_entry cmd_kill_pane_entry; +extern const struct cmd_entry cmd_kill_server_entry; +extern const struct cmd_entry cmd_kill_session_entry; +extern const struct cmd_entry cmd_kill_window_entry; +extern const struct cmd_entry cmd_last_window_entry; +extern const struct cmd_entry cmd_link_window_entry; +extern const struct cmd_entry cmd_list_buffers_entry; +extern const struct cmd_entry cmd_list_clients_entry; +extern const struct cmd_entry cmd_list_commands_entry; +extern const struct cmd_entry cmd_list_keys_entry; +extern const struct cmd_entry cmd_list_sessions_entry; +extern const struct cmd_entry cmd_list_windows_entry; +extern const struct cmd_entry cmd_load_buffer_entry; +extern const struct cmd_entry cmd_lock_server_entry; +extern const struct cmd_entry cmd_move_window_entry; +extern const struct cmd_entry cmd_new_session_entry; +extern const struct cmd_entry cmd_new_window_entry; +extern const struct cmd_entry cmd_next_layout_entry; +extern const struct cmd_entry cmd_next_window_entry; +extern const struct cmd_entry cmd_paste_buffer_entry; +extern const struct cmd_entry cmd_previous_layout_entry; +extern const struct cmd_entry cmd_previous_window_entry; +extern const struct cmd_entry cmd_refresh_client_entry; +extern const struct cmd_entry cmd_rename_session_entry; +extern const struct cmd_entry cmd_rename_window_entry; +extern const struct cmd_entry cmd_resize_pane_entry; +extern const struct cmd_entry cmd_respawn_window_entry; +extern const struct cmd_entry cmd_rotate_window_entry; +extern const struct cmd_entry cmd_save_buffer_entry; +extern const struct cmd_entry cmd_scroll_mode_entry; +extern const struct cmd_entry cmd_select_layout_entry; +extern const struct cmd_entry cmd_select_pane_entry; +extern const struct cmd_entry cmd_select_prompt_entry; +extern const struct cmd_entry cmd_select_window_entry; +extern const struct cmd_entry cmd_send_keys_entry; +extern const struct cmd_entry cmd_send_prefix_entry; +extern const struct cmd_entry cmd_server_info_entry; +extern const struct cmd_entry cmd_set_buffer_entry; +extern const struct cmd_entry cmd_set_option_entry; +extern const struct cmd_entry cmd_set_password_entry; +extern const struct cmd_entry cmd_set_window_option_entry; +extern const struct cmd_entry cmd_show_buffer_entry; +extern const struct cmd_entry cmd_show_options_entry; +extern const struct cmd_entry cmd_show_window_options_entry; +extern const struct cmd_entry cmd_source_file_entry; +extern const struct cmd_entry cmd_split_window_entry; +extern const struct cmd_entry cmd_start_server_entry; +extern const struct cmd_entry cmd_suspend_client_entry; +extern const struct cmd_entry cmd_swap_pane_entry; +extern const struct cmd_entry cmd_swap_window_entry; +extern const struct cmd_entry cmd_switch_client_entry; +extern const struct cmd_entry cmd_unbind_key_entry; +extern const struct cmd_entry cmd_unlink_window_entry; +extern const struct cmd_entry cmd_up_pane_entry; + +/* cmd-list.c */ +struct cmd_list *cmd_list_parse(int, char **, char **); +int cmd_list_exec(struct cmd_list *, struct cmd_ctx *); +void cmd_list_send(struct cmd_list *, struct buffer *); +struct cmd_list *cmd_list_recv(struct buffer *); +void cmd_list_free(struct cmd_list *); +size_t cmd_list_print(struct cmd_list *, char *, size_t); + +/* cmd-string.c */ +int cmd_string_parse(const char *, struct cmd_list **, char **); + +/* cmd-generic.c */ +size_t cmd_prarg(char *, size_t, const char *, char *); +#define CMD_TARGET_WINDOW_USAGE "[-t target-window]" +#define CMD_TARGET_SESSION_USAGE "[-t target-session]" +#define CMD_TARGET_CLIENT_USAGE "[-t target-client]" +void cmd_target_init(struct cmd *, int); +int cmd_target_parse(struct cmd *, int, char **, char **); +void cmd_target_send(struct cmd *, struct buffer *); +void cmd_target_recv(struct cmd *, struct buffer *); +void cmd_target_free(struct cmd *); +size_t cmd_target_print(struct cmd *, char *, size_t); +#define CMD_SRCDST_WINDOW_USAGE "[-s src-window] [-t dst-window]" +#define CMD_SRCDST_SESSION_USAGE "[-s src-session] [-t dst-session]" +#define CMD_SRCDST_CLIENT_USAGE "[-s src-client] [-t dst-client]" +void cmd_srcdst_init(struct cmd *, int); +int cmd_srcdst_parse(struct cmd *, int, char **, char **); +void cmd_srcdst_send(struct cmd *, struct buffer *); +void cmd_srcdst_recv(struct cmd *, struct buffer *); +void cmd_srcdst_free(struct cmd *); +size_t cmd_srcdst_print(struct cmd *, char *, size_t); +#define CMD_BUFFER_WINDOW_USAGE "[-b buffer-index] [-t target-window]" +#define CMD_BUFFER_SESSION_USAGE "[-b buffer-index] [-t target-session]" +#define CMD_BUFFER_CLIENT_USAGE "[-b buffer-index] [-t target-client]" +void cmd_buffer_init(struct cmd *, int); +int cmd_buffer_parse(struct cmd *, int, char **, char **); +void cmd_buffer_send(struct cmd *, struct buffer *); +void cmd_buffer_recv(struct cmd *, struct buffer *); +void cmd_buffer_free(struct cmd *); +size_t cmd_buffer_print(struct cmd *, char *, size_t); +#define CMD_OPTION_WINDOW_USAGE "[-gu] [-t target-window] option [value]" +#define CMD_OPTION_SESSION_USAGE "[-gu] [-t target-session] option [value]" +#define CMD_OPTION_CLIENT_USAGE "[-gu] [-t target-client] option [value]" +void cmd_option_init(struct cmd *, int); +int cmd_option_parse(struct cmd *, int, char **, char **); +void cmd_option_send(struct cmd *, struct buffer *); +void cmd_option_recv(struct cmd *, struct buffer *); +void cmd_option_free(struct cmd *); +size_t cmd_option_print(struct cmd *, char *, size_t); +#define CMD_PANE_WINDOW_USAGE "[-t target-window] [-p pane-index]" +#define CMD_PANE_SESSION_USAGE "[-t target-session] [-p pane-index]" +#define CMD_PANE_CLIENT_USAGE "[-t target-client] [-p pane-index]" +void cmd_pane_init(struct cmd *, int); +int cmd_pane_parse(struct cmd *, int, char **, char **); +void cmd_pane_send(struct cmd *, struct buffer *); +void cmd_pane_recv(struct cmd *, struct buffer *); +void cmd_pane_free(struct cmd *); +size_t cmd_pane_print(struct cmd *, char *, size_t); + +/* client.c */ +int client_init(char *, struct client_ctx *, int, int); +int client_flush(struct client_ctx *); +int client_main(struct client_ctx *); + +/* client-msg.c */ +int client_msg_dispatch(struct client_ctx *, char **); + +/* client-fn.c */ +void client_write_server(struct client_ctx *, enum hdrtype, void *, size_t); +void client_write_server2( + struct client_ctx *, enum hdrtype, void *, size_t, void *, size_t); +void client_fill_session(struct msg_command_data *); + +/* key-bindings.c */ +extern struct key_bindings key_bindings; +int key_bindings_cmp(struct key_binding *, struct key_binding *); +SPLAY_PROTOTYPE(key_bindings, key_binding, entry, key_bindings_cmp); +struct key_binding *key_bindings_lookup(int); +void key_bindings_add(int, int, struct cmd_list *); +void key_bindings_remove(int); +void key_bindings_init(void); +void key_bindings_free(void); +void key_bindings_dispatch(struct key_binding *, struct client *); +void printflike2 key_bindings_error(struct cmd_ctx *, const char *, ...); +void printflike2 key_bindings_print(struct cmd_ctx *, const char *, ...); +void printflike2 key_bindings_info(struct cmd_ctx *, const char *, ...); + +/* key-string.c */ +int key_string_lookup_string(const char *); +const char *key_string_lookup_key(int); + +/* server.c */ +extern struct clients clients; +struct client *server_create_client(int); +int server_client_index(struct client *); +int server_start(char *); + +/* server-msg.c */ +int server_msg_dispatch(struct client *); + +/* server-fn.c */ +const char **server_fill_environ(struct session *); +void server_write_client( + struct client *, enum hdrtype, const void *, size_t); +void server_write_session( + struct session *, enum hdrtype, const void *, size_t); +void server_write_window( + struct window *, enum hdrtype, const void *, size_t); +void server_redraw_client(struct client *); +void server_status_client(struct client *); +void server_redraw_session(struct session *); +void server_status_session(struct session *); +void server_redraw_window(struct window *); +void server_status_window(struct window *); +void server_lock(void); +int server_unlock(const char *); + +/* status.c */ +int status_redraw(struct client *); +void status_message_set(struct client *, const char *); +void status_message_clear(struct client *); +int status_message_redraw(struct client *); +void status_prompt_set(struct client *, + const char *, int (*)(void *, const char *), void *, int); +void status_prompt_clear(struct client *); +int status_prompt_redraw(struct client *); +void status_prompt_key(struct client *, int); + +/* resize.c */ +void recalculate_sizes(void); + +/* input.c */ +void input_init(struct window_pane *); +void input_free(struct window_pane *); +void input_parse(struct window_pane *); + +/* input-key.c */ +void input_key(struct window_pane *, int); +void input_mouse(struct window_pane *, u_char, u_char, u_char); + +/* colour.c */ +const char *colour_tostring(u_char); +int colour_fromstring(const char *); +u_char colour_256to16(u_char); +u_char colour_256to88(u_char); + +/* attributes.c */ +const char *attributes_tostring(u_char); +int attributes_fromstring(const char *); + +/* grid.c */ +extern const struct grid_cell grid_default_cell; +struct grid *grid_create(u_int, u_int, u_int); +void grid_destroy(struct grid *); +int grid_compare(struct grid *, struct grid *); +void grid_reduce_line(struct grid *, u_int, u_int); +void grid_expand_line(struct grid *, u_int, u_int); +void grid_expand_line_utf8(struct grid *, u_int, u_int); +void grid_scroll_line(struct grid *); +const struct grid_cell *grid_peek_cell(struct grid *, u_int, u_int); +struct grid_cell *grid_get_cell(struct grid *, u_int, u_int); +void grid_set_cell(struct grid *, u_int, u_int, const struct grid_cell *); +const struct grid_utf8 *grid_peek_utf8(struct grid *, u_int, u_int); +struct grid_utf8 *grid_get_utf8(struct grid *, u_int, u_int); +void grid_set_utf8(struct grid *, u_int, u_int, const struct grid_utf8 *); +void grid_clear(struct grid *, u_int, u_int, u_int, u_int); +void grid_clear_lines(struct grid *, u_int, u_int); +void grid_move_lines(struct grid *, u_int, u_int, u_int); +void grid_clear_cells(struct grid *, u_int, u_int, u_int); +void grid_move_cells(struct grid *, u_int, u_int, u_int, u_int); + +/* grid-view.c */ +const struct grid_cell *grid_view_peek_cell(struct grid *, u_int, u_int); +struct grid_cell *grid_view_get_cell(struct grid *, u_int, u_int); +void grid_view_set_cell( + struct grid *, u_int, u_int, const struct grid_cell *); +const struct grid_utf8 *grid_view_peek_utf8(struct grid *, u_int, u_int); +struct grid_utf8 *grid_view_get_utf8(struct grid *, u_int, u_int); +void grid_view_set_utf8( + struct grid *, u_int, u_int, const struct grid_utf8 *); +void grid_view_clear(struct grid *, u_int, u_int, u_int, u_int); +void grid_view_scroll_region_up(struct grid *, u_int, u_int); +void grid_view_scroll_region_down(struct grid *, u_int, u_int); +void grid_view_insert_lines(struct grid *, u_int, u_int); +void grid_view_insert_lines_region( + struct grid *, u_int, u_int, u_int, u_int); +void grid_view_delete_lines(struct grid *, u_int, u_int); +void grid_view_delete_lines_region( + struct grid *, u_int, u_int, u_int, u_int); +void grid_view_insert_cells(struct grid *, u_int, u_int, u_int); +void grid_view_delete_cells(struct grid *, u_int, u_int, u_int); + +/* screen-write.c */ +void screen_write_start( + struct screen_write_ctx *, struct window_pane *, struct screen *); +void screen_write_stop(struct screen_write_ctx *); +void printflike3 screen_write_puts( + struct screen_write_ctx *, struct grid_cell *, const char *, ...); +void screen_write_putc( + struct screen_write_ctx *, struct grid_cell *, u_char); +void screen_write_copy(struct screen_write_ctx *, + struct screen *, u_int, u_int, u_int, u_int); +void screen_write_cursorup(struct screen_write_ctx *, u_int); +void screen_write_cursordown(struct screen_write_ctx *, u_int); +void screen_write_cursorright(struct screen_write_ctx *, u_int); +void screen_write_cursorleft(struct screen_write_ctx *, u_int); +void screen_write_insertcharacter(struct screen_write_ctx *, u_int); +void screen_write_deletecharacter(struct screen_write_ctx *, u_int); +void screen_write_insertline(struct screen_write_ctx *, u_int); +void screen_write_deleteline(struct screen_write_ctx *, u_int); +void screen_write_clearline(struct screen_write_ctx *); +void screen_write_clearendofline(struct screen_write_ctx *); +void screen_write_clearstartofline(struct screen_write_ctx *); +void screen_write_cursormove(struct screen_write_ctx *, u_int, u_int); +void screen_write_cursormode(struct screen_write_ctx *, int); +void screen_write_reverseindex(struct screen_write_ctx *); +void screen_write_scrollregion(struct screen_write_ctx *, u_int, u_int); +void screen_write_insertmode(struct screen_write_ctx *, int); +void screen_write_mousemode(struct screen_write_ctx *, int); +void screen_write_linefeed(struct screen_write_ctx *); +void screen_write_carriagereturn(struct screen_write_ctx *); +void screen_write_kcursormode(struct screen_write_ctx *, int); +void screen_write_kkeypadmode(struct screen_write_ctx *, int); +void screen_write_clearendofscreen(struct screen_write_ctx *); +void screen_write_clearstartofscreen(struct screen_write_ctx *); +void screen_write_clearscreen(struct screen_write_ctx *); +void screen_write_cell( + struct screen_write_ctx *, const struct grid_cell *, u_char *); + +/* screen-redraw.c */ +void screen_redraw_screen(struct client *); +void screen_redraw_pane(struct client *, struct window_pane *); +void screen_redraw_status(struct client *); + +/* screen.c */ +void screen_init(struct screen *, u_int, u_int, u_int); +void screen_reinit(struct screen *); +void screen_free(struct screen *); +void screen_set_title(struct screen *, const char *); +void screen_resize(struct screen *, u_int, u_int); +void screen_set_selection( + struct screen *, u_int, u_int, u_int, u_int, struct grid_cell *); +void screen_clear_selection(struct screen *); +int screen_check_selection(struct screen *, u_int, u_int); +void screen_display_copy_area(struct screen *, struct screen *, + u_int, u_int, u_int, u_int, u_int, u_int); + +/* 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 *); +u_int winlink_count(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 *); +void winlink_stack_push(struct winlink_stack *, struct winlink *); +void winlink_stack_remove(struct winlink_stack *, struct winlink *); +int window_index(struct window *, u_int *); +struct window *window_create1(u_int, u_int); +struct window *window_create(const char *, const char *, + const char *, const char **, u_int, u_int, u_int, char **); +void window_destroy(struct window *); +int window_resize(struct window *, u_int, u_int); +void window_set_active_pane(struct window *, struct window_pane *); +struct window_pane *window_add_pane(struct window *, int, + const char *, const char *, const char **, u_int, char **); +void window_remove_pane(struct window *, struct window_pane *); +u_int window_index_of_pane(struct window *, struct window_pane *); +struct window_pane *window_pane_at_index(struct window *, u_int); +u_int window_count_panes(struct window *); +void window_destroy_panes(struct window *); +struct window_pane *window_pane_create(struct window *, u_int, u_int, u_int); +void window_pane_destroy(struct window_pane *); +int window_pane_spawn(struct window_pane *, + const char *, const char *, const char **, char **); +int window_pane_resize(struct window_pane *, u_int, u_int); +void window_calculate_sizes(struct window *); +int window_pane_set_mode( + struct window_pane *, const struct window_mode *); +void window_pane_reset_mode(struct window_pane *); +void window_pane_parse(struct window_pane *); +void window_pane_key(struct window_pane *, struct client *, int); +void window_pane_mouse(struct window_pane *, + struct client *, u_char, u_char, u_char); +char *window_pane_search(struct window_pane *, const char *); + +/* layout.c */ +const char * layout_name(struct window *); +int layout_lookup(const char *); +void layout_refresh(struct window *, int); +int layout_resize(struct window_pane *, int); +int layout_select(struct window *, u_int); +void layout_next(struct window *); +void layout_previous(struct window *); + +/* layout-manual.c */ +void layout_manual_v_refresh(struct window *, int); +void layout_manual_v_resize(struct window_pane *, int); + +/* window-clock.c */ +extern const struct window_mode window_clock_mode; + +/* window-copy.c */ +extern const struct window_mode window_copy_mode; +void window_copy_pageup(struct window_pane *); + +/* window-scroll.c */ +extern const struct window_mode window_scroll_mode; +void window_scroll_pageup(struct window_pane *); + +/* window-more.c */ +extern const struct window_mode window_more_mode; +void window_more_vadd(struct window_pane *, const char *, va_list); +void printflike2 window_more_add(struct window_pane *, const char *, ...); + +/* window-choose.c */ +extern const struct window_mode window_choose_mode; +void window_choose_vadd( + struct window_pane *, int, const char *, va_list); +void printflike3 window_choose_add( + struct window_pane *, int, const char *, ...); +void window_choose_ready(struct window_pane *, + u_int, void (*)(void *, int), void *); + +/* names.c */ +void set_window_names(void); +char *default_window_name(struct window *); + +/* session.c */ +extern struct sessions sessions; +void session_alert_add(struct session *, struct window *, int); +void session_alert_cancel(struct session *, struct winlink *); +int session_alert_has(struct session *, struct winlink *, int); +int session_alert_has_window(struct session *, struct window *, int); +struct session *session_find(const char *); +struct session *session_create(const char *, const char *, + const char *, u_int, u_int, char **); +void session_destroy(struct session *); +int session_index(struct session *, u_int *); +struct winlink *session_new(struct session *, + const char *, const char *, const char *, int, char **); +struct winlink *session_attach( + struct session *, struct window *, int, char **); +int session_detach(struct session *, struct winlink *); +int session_has(struct session *, struct window *); +int session_next(struct session *, int); +int session_previous(struct session *, int); +int session_select(struct session *, int); +int session_last(struct session *); + +/* utf8.c */ +void utf8_build(void); +int utf8_width(u_char *); + +/* util.c */ +char *section_string(char *, size_t, size_t, size_t); +void clean_string(const char *, char *, size_t); + +/* procname.c */ +char *get_proc_name(int, char *); + +/* buffer.c */ +struct buffer *buffer_create(size_t); +void buffer_destroy(struct buffer *); +void buffer_clear(struct buffer *); +void buffer_ensure(struct buffer *, size_t); +void buffer_add(struct buffer *, size_t); +void buffer_reverse_add(struct buffer *, size_t); +void buffer_remove(struct buffer *, size_t); +void buffer_reverse_remove(struct buffer *, size_t); +void buffer_insert_range(struct buffer *, size_t, size_t); +void buffer_delete_range(struct buffer *, size_t, size_t); +void buffer_write(struct buffer *, const void *, size_t); +void buffer_read(struct buffer *, void *, size_t); +void buffer_write8(struct buffer *, uint8_t); +void buffer_write16(struct buffer *, uint16_t); +uint8_t buffer_read8(struct buffer *); +uint16_t buffer_read16(struct buffer *); + +/* buffer-poll.c */ +void buffer_set( + struct pollfd *, int, struct buffer *, struct buffer *); +int buffer_poll(struct pollfd *, struct buffer *, struct buffer *); +void buffer_flush(int, struct buffer *n, struct buffer *); + +/* log.c */ +#define LOG_FACILITY LOG_DAEMON +void log_open_syslog(int); +void log_open_tty(int); +void log_open_file(int, const char *); +void log_close(void); +void log_vwrite(int, const char *, va_list); +void log_write(int, const char *, ...); +void printflike1 log_warn(const char *, ...); +void printflike1 log_warnx(const char *, ...); +void printflike1 log_info(const char *, ...); +void printflike1 log_debug(const char *, ...); +void printflike1 log_debug2(const char *, ...); +void printflike1 log_debug3(const char *, ...); +__dead void log_vfatal(const char *, va_list); +__dead void log_fatal(const char *, ...); +__dead void log_fatalx(const char *, ...); + +/* xmalloc.c */ +char *xstrdup(const char *); +void *xcalloc(size_t, size_t); +void *xmalloc(size_t); +void *xrealloc(void *, size_t, size_t); +void xfree(void *); +int printflike2 xasprintf(char **, const char *, ...); +int xvasprintf(char **, const char *, va_list); +int printflike3 xsnprintf(char *, size_t, const char *, ...); +int xvsnprintf(char *, size_t, const char *, va_list); +int printflike3 printpath(char *, size_t, const char *, ...); + +#endif /* TMUX_H */ diff --git a/usr.bin/tmux/tty-keys.c b/usr.bin/tmux/tty-keys.c new file mode 100644 index 00000000000..524da45bcf7 --- /dev/null +++ b/usr.bin/tmux/tty-keys.c @@ -0,0 +1,412 @@ +/* $OpenBSD: tty-keys.c,v 1.1 2009/06/01 22:58:49 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/time.h> + +#include <string.h> + +#include "tmux.h" + +void tty_keys_add(struct tty *, const char *, int, int); +int tty_keys_parse_xterm(struct tty *, char *, size_t, size_t *); +int tty_keys_parse_mouse(struct tty *, char *, size_t, size_t *, u_char *); + +struct tty_key_ent { + enum tty_code_code code; + const char *string; + + int key; + int flags; +}; + +struct tty_key_ent tty_keys[] = { + /* Function keys. */ + { TTYC_KF1, NULL, KEYC_F1, TTYKEY_CTRL }, + { TTYC_KF2, NULL, KEYC_F2, TTYKEY_CTRL }, + { TTYC_KF3, NULL, KEYC_F3, TTYKEY_CTRL }, + { TTYC_KF4, NULL, KEYC_F4, TTYKEY_CTRL }, + { TTYC_KF5, NULL, KEYC_F5, TTYKEY_CTRL }, + { TTYC_KF6, NULL, KEYC_F6, TTYKEY_CTRL }, + { TTYC_KF7, NULL, KEYC_F7, TTYKEY_CTRL }, + { TTYC_KF8, NULL, KEYC_F8, TTYKEY_CTRL }, + { TTYC_KF9, NULL, KEYC_F9, TTYKEY_CTRL }, + { TTYC_KF10, NULL, KEYC_F10, TTYKEY_CTRL }, + { TTYC_KF11, NULL, KEYC_F11, TTYKEY_CTRL }, + { TTYC_KF12, NULL, KEYC_F12, TTYKEY_CTRL }, + { TTYC_KF13, NULL, KEYC_F13, TTYKEY_CTRL }, + { TTYC_KF14, NULL, KEYC_F14, TTYKEY_CTRL }, + { TTYC_KF15, NULL, KEYC_F15, TTYKEY_CTRL }, + { TTYC_KF16, NULL, KEYC_F16, TTYKEY_CTRL }, + { TTYC_KF17, NULL, KEYC_F17, TTYKEY_CTRL }, + { TTYC_KF18, NULL, KEYC_F18, TTYKEY_CTRL }, + { TTYC_KF19, NULL, KEYC_F19, TTYKEY_CTRL }, + { TTYC_KF20, NULL, KEYC_F20, TTYKEY_CTRL }, + { TTYC_KICH1, NULL, KEYC_IC, TTYKEY_CTRL }, + { TTYC_KDCH1, NULL, KEYC_DC, TTYKEY_CTRL }, + { TTYC_KHOME, NULL, KEYC_HOME, TTYKEY_CTRL }, + { TTYC_KEND, NULL, KEYC_END, TTYKEY_CTRL }, + { TTYC_KNP, NULL, KEYC_NPAGE, TTYKEY_CTRL }, + { TTYC_KPP, NULL, KEYC_PPAGE, TTYKEY_CTRL }, + { TTYC_KCBT, NULL, KEYC_BTAB, TTYKEY_CTRL }, + + /* Arrow keys. */ + { 0, "\033OA", KEYC_UP, TTYKEY_RAW }, + { 0, "\033OB", KEYC_DOWN, TTYKEY_RAW }, + { 0, "\033OC", KEYC_RIGHT, TTYKEY_RAW }, + { 0, "\033OD", KEYC_LEFT, TTYKEY_RAW }, + + { 0, "\033[A", KEYC_UP, TTYKEY_RAW }, + { 0, "\033[B", KEYC_DOWN, TTYKEY_RAW }, + { 0, "\033[C", KEYC_RIGHT, TTYKEY_RAW }, + { 0, "\033[D", KEYC_LEFT, TTYKEY_RAW }, + + { 0, "\033Oa", KEYC_ADDCTL(KEYC_UP), TTYKEY_RAW }, + { 0, "\033Ob", KEYC_ADDCTL(KEYC_DOWN), TTYKEY_RAW }, + { 0, "\033Oc", KEYC_ADDCTL(KEYC_RIGHT), TTYKEY_RAW }, + { 0, "\033Od", KEYC_ADDCTL(KEYC_LEFT), TTYKEY_RAW }, + { 0, "\033[a", KEYC_ADDSFT(KEYC_UP), TTYKEY_RAW }, + { 0, "\033[b", KEYC_ADDSFT(KEYC_DOWN), TTYKEY_RAW }, + { 0, "\033[c", KEYC_ADDSFT(KEYC_RIGHT), TTYKEY_RAW }, + { 0, "\033[d", KEYC_ADDSFT(KEYC_LEFT), TTYKEY_RAW }, + + { TTYC_KCUU1, NULL, KEYC_UP, TTYKEY_CTRL }, + { TTYC_KCUD1, NULL, KEYC_DOWN, TTYKEY_CTRL }, + { TTYC_KCUB1, NULL, KEYC_LEFT, TTYKEY_CTRL }, + { TTYC_KCUF1, NULL, KEYC_RIGHT, TTYKEY_CTRL }, + + /* + * Numeric keypad. termcap and terminfo are totally confusing for this. + * There are definitions for some keypad keys and for function keys, + * but these seem to now be used for the real function keys rather than + * for the keypad keys in application mode (which is different from + * what it says in the termcap file). So, we just hardcode the vt100 + * escape sequences here and always put the terminal into keypad_xmit + * mode. Translation of numbers mode/applications mode is done in + * input-keys.c. + */ + { 0, "\033Oo", KEYC_KP0_1, TTYKEY_RAW }, + { 0, "\033Oj", KEYC_KP0_2, TTYKEY_RAW }, + { 0, "\033Om", KEYC_KP0_3, TTYKEY_RAW }, + { 0, "\033Ow", KEYC_KP1_0, TTYKEY_RAW }, + { 0, "\033Ox", KEYC_KP1_1, TTYKEY_RAW }, + { 0, "\033Oy", KEYC_KP1_2, TTYKEY_RAW }, + { 0, "\033Ok", KEYC_KP1_3, TTYKEY_RAW }, + { 0, "\033Ot", KEYC_KP2_0, TTYKEY_RAW }, + { 0, "\033Ou", KEYC_KP2_1, TTYKEY_RAW }, + { 0, "\033Ov", KEYC_KP2_2, TTYKEY_RAW }, + { 0, "\033Oq", KEYC_KP3_0, TTYKEY_RAW }, + { 0, "\033Or", KEYC_KP3_1, TTYKEY_RAW }, + { 0, "\033Os", KEYC_KP3_2, TTYKEY_RAW }, + { 0, "\033OM", KEYC_KP3_3, TTYKEY_RAW }, + { 0, "\033Op", KEYC_KP4_0, TTYKEY_RAW }, + { 0, "\033On", KEYC_KP4_2, TTYKEY_RAW }, +}; + +RB_GENERATE(tty_keys, tty_key, entry, tty_keys_cmp); + +struct tty_key *tty_keys_find(struct tty *, char *, size_t, size_t *); + +int +tty_keys_cmp(struct tty_key *k1, struct tty_key *k2) +{ + return (strcmp(k1->string, k2->string)); +} + +void +tty_keys_add(struct tty *tty, const char *s, int key, int flags) +{ + struct tty_key *tk, *tl; + + tk = xmalloc(sizeof *tk); + tk->string = xstrdup(s); + tk->key = key; + tk->flags = flags; + + if ((tl = RB_INSERT(tty_keys, &tty->ktree, tk)) != NULL) { + xfree(tk->string); + xfree(tk); + log_debug("key exists: %s (old %x, new %x)", s, tl->key, key); + return; + } + + if (strlen(tk->string) > tty->ksize) + tty->ksize = strlen(tk->string); + log_debug("new key %x: size now %zu (%s)", key, tty->ksize, tk->string); +} + +void +tty_keys_init(struct tty *tty) +{ + struct tty_key_ent *tke; + u_int i; + const char *s; + char tmp[64]; + + RB_INIT(&tty->ktree); + + tty->ksize = 0; + for (i = 0; i < nitems(tty_keys); i++) { + tke = &tty_keys[i]; + + if (tke->flags & TTYKEY_RAW) + s = tke->string; + else { + if (!tty_term_has(tty->term, tke->code)) + continue; + s = tty_term_string(tty->term, tke->code); + if (s[0] != '\033' || s[1] == '\0') + continue; + } + + tty_keys_add(tty, s + 1, tke->key, tke->flags); + if (tke->flags & TTYKEY_CTRL) { + if (strlcpy(tmp, s, sizeof tmp) >= sizeof tmp) + continue; + tmp[strlen(tmp) - 1] ^= 0x20; + tty_keys_add(tty, tmp + 1, KEYC_ADDCTL(tke->key), 0); + } + } +} + +void +tty_keys_free(struct tty *tty) +{ + struct tty_key *tk; + + while (!RB_EMPTY(&tty->ktree)) { + tk = RB_ROOT(&tty->ktree); + RB_REMOVE(tty_keys, &tty->ktree, tk); + xfree(tk->string); + xfree(tk); + } +} + +struct tty_key * +tty_keys_find(struct tty *tty, char *buf, size_t len, size_t *size) +{ + struct tty_key *tk, tl; + char *s; + + if (len == 0) + return (NULL); + + s = xmalloc(tty->ksize + 1); + for (*size = tty->ksize; (*size) > 0; (*size)--) { + if ((*size) > len) + continue; + memcpy(s, buf, *size); + s[*size] = '\0'; + + log_debug2("looking for key: %s", s); + + tl.string = s; + tk = RB_FIND(tty_keys, &tty->ktree, &tl); + if (tk != NULL) { + log_debug2("got key: 0x%x", tk->key); + xfree(s); + return (tk); + } + } + xfree(s); + + return (NULL); +} + +int +tty_keys_next(struct tty *tty, int *key, u_char *mouse) +{ + struct tty_key *tk; + struct timeval tv; + char *buf; + size_t len, size; + + buf = BUFFER_OUT(tty->in); + len = BUFFER_USED(tty->in); + if (len == 0) + return (1); + log_debug("keys are %zu (%.*s)", len, (int) len, buf); + + /* If a normal key, return it. */ + if (*buf != '\033') { + *key = buffer_read8(tty->in); + goto found; + } + + /* Look for matching key string and return if found. */ + tk = tty_keys_find(tty, buf + 1, len - 1, &size); + if (tk != NULL) { + buffer_remove(tty->in, size + 1); + *key = tk->key; + goto found; + } + + /* Not found. Is this a mouse key press? */ + *key = tty_keys_parse_mouse(tty, buf, len, &size, mouse); + if (*key != KEYC_NONE) { + buffer_remove(tty->in, size); + goto found; + } + + /* Not found. Try to parse xterm-type arguments. */ + *key = tty_keys_parse_xterm(tty, buf, len, &size); + if (*key != KEYC_NONE) { + buffer_remove(tty->in, size); + goto found; + } + + /* Escape but no key string. If the timer isn't started, start it. */ + if (!(tty->flags & TTY_ESCAPE)) { + tv.tv_sec = 0; + tv.tv_usec = ESCAPE_PERIOD * 1000L; + if (gettimeofday(&tty->key_timer, NULL) != 0) + fatal("gettimeofday"); + timeradd(&tty->key_timer, &tv, &tty->key_timer); + + tty->flags |= TTY_ESCAPE; + return (1); + } + + /* Skip the escape. */ + buf++; + len--; + + /* Is there a normal key following? */ + if (len != 0 && *buf != '\033') { + buffer_remove(tty->in, 1); + *key = KEYC_ADDESC(buffer_read8(tty->in)); + goto found; + } + + /* Or a key string? */ + if (len > 1) { + tk = tty_keys_find(tty, buf + 1, len - 1, &size); + if (tk != NULL) { + buffer_remove(tty->in, size + 2); + *key = KEYC_ADDESC(tk->key); + goto found; + } + } + + /* If the timer hasn't expired, keep waiting. */ + if (gettimeofday(&tv, NULL) != 0) + fatal("gettimeofday"); + if (timercmp(&tty->key_timer, &tv, >)) + return (1); + + /* Give up and return the escape. */ + buffer_remove(tty->in, 1); + *key = '\033'; + +found: + tty->flags &= ~TTY_ESCAPE; + return (0); +} + +int +tty_keys_parse_mouse( + unused struct tty *tty, char *buf, size_t len, size_t *size, u_char *mouse) +{ + /* + * Mouse sequences are \033[M followed by three characters indicating + * buttons, X and Y, all based at 32 with 1,1 top-left. + */ + + log_debug("mouse input is: %.*s", (int) len, buf); + if (len != 6 || memcmp(buf, "\033[M", 3) != 0) + return (KEYC_NONE); + *size = 6; + + if (buf[3] < 32 || buf[4] < 33 || buf[5] < 33) + return (KEYC_NONE); + + mouse[0] = buf[3] - 32; + mouse[1] = buf[4] - 33; + mouse[2] = buf[5] - 33; + return (KEYC_MOUSE); +} + +int +tty_keys_parse_xterm(struct tty *tty, char *buf, size_t len, size_t *size) +{ + struct tty_key *tk; + char tmp[5]; + size_t tmplen; + int key; + + /* + * xterm sequences with modifier keys are of the form: + * + * ^[[1;xD becomes ^[[D + * ^[[5;x~ becomes ^[[5~ + * + * This function is a bit of a hack. Need to figure out what exact + * format and meaning xterm outputs and fix it. XXX + */ + + log_debug("xterm input is: %.*s", (int) len, buf); + if (len != 6 || memcmp(buf, "\033[1;", 4) != 0) + return (KEYC_NONE); + *size = 6; + + tmplen = 0; + tmp[tmplen++] = '['; + if (buf[5] == '~') { + tmp[tmplen++] = buf[2]; + tmp[tmplen++] = '~'; + } else + tmp[tmplen++] = buf[5]; + log_debug("xterm output is: %.*s", (int) tmplen, tmp); + + tk = tty_keys_find(tty, tmp, tmplen, size); + if (tk == NULL) + return (KEYC_NONE); + key = tk->key; + + switch (buf[4]) { + case '8': + key = KEYC_ADDSFT(key); + key = KEYC_ADDESC(key); + key = KEYC_ADDCTL(key); + break; + case '7': + key = KEYC_ADDESC(key); + key = KEYC_ADDCTL(key); + break; + case '6': + key = KEYC_ADDSFT(key); + key = KEYC_ADDCTL(key); + break; + case '5': + key = KEYC_ADDCTL(key); + break; + case '4': + key = KEYC_ADDSFT(key); + key = KEYC_ADDESC(key); + break; + case '3': + key = KEYC_ADDESC(key); + break; + case '2': + key = KEYC_ADDSFT(key); + break; + } + + *size = 6; + return (key); +} diff --git a/usr.bin/tmux/tty-term.c b/usr.bin/tmux/tty-term.c new file mode 100644 index 00000000000..990fbc552ae --- /dev/null +++ b/usr.bin/tmux/tty-term.c @@ -0,0 +1,393 @@ +/* $OpenBSD: tty-term.c,v 1.1 2009/06/01 22:58:49 nicm Exp $ */ + +/* + * Copyright (c) 2008 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 <ncurses.h> +#include <string.h> +#include <term.h> + +#include "tmux.h" + +void tty_term_quirks(struct tty_term *); +char *tty_term_strip(const char *); + +struct tty_terms tty_terms = SLIST_HEAD_INITIALIZER(tty_terms); + +struct tty_term_code_entry tty_term_codes[NTTYCODE] = { + { TTYC_AX, TTYCODE_FLAG, "AX" }, + { TTYC_ACSC, TTYCODE_STRING, "acsc" }, + { TTYC_BEL, TTYCODE_STRING, "bel" }, + { TTYC_BLINK, TTYCODE_STRING, "blink" }, + { TTYC_BOLD, TTYCODE_STRING, "bold" }, + { TTYC_CIVIS, TTYCODE_STRING, "civis" }, + { TTYC_CLEAR, TTYCODE_STRING, "clear" }, + { TTYC_CNORM, TTYCODE_STRING, "cnorm" }, + { TTYC_COLORS, TTYCODE_NUMBER, "colors" }, + { TTYC_CSR, TTYCODE_STRING, "csr" }, + { TTYC_CUD, TTYCODE_STRING, "cud" }, + { TTYC_CUD1, TTYCODE_STRING, "cud1" }, + { TTYC_CUP, TTYCODE_STRING, "cup" }, + { TTYC_DCH, TTYCODE_STRING, "dch" }, + { TTYC_DCH1, TTYCODE_STRING, "dch1" }, + { TTYC_DIM, TTYCODE_STRING, "dim" }, + { TTYC_DL, TTYCODE_STRING, "dl" }, + { TTYC_DL1, TTYCODE_STRING, "dl1" }, + { TTYC_EL, TTYCODE_STRING, "el" }, + { TTYC_EL1, TTYCODE_STRING, "el1" }, + { TTYC_ENACS, TTYCODE_STRING, "enacs" }, + { TTYC_ICH, TTYCODE_STRING, "ich" }, + { TTYC_ICH1, TTYCODE_STRING, "ich1" }, + { TTYC_IL, TTYCODE_STRING, "il" }, + { TTYC_IL1, TTYCODE_STRING, "il1" }, + { TTYC_INVIS, TTYCODE_STRING, "invis" }, + { TTYC_IS1, TTYCODE_STRING, "is1" }, + { TTYC_IS2, TTYCODE_STRING, "is2" }, + { TTYC_IS3, TTYCODE_STRING, "is3" }, + { TTYC_KCBT, TTYCODE_STRING, "kcbt" }, + { TTYC_KCUB1, TTYCODE_STRING, "kcub1" }, + { TTYC_KCUD1, TTYCODE_STRING, "kcud1" }, + { TTYC_KCUF1, TTYCODE_STRING, "kcuf1" }, + { TTYC_KCUU1, TTYCODE_STRING, "kcuu1" }, + { TTYC_KDCH1, TTYCODE_STRING, "kdch1" }, + { TTYC_KEND, TTYCODE_STRING, "kend" }, + { TTYC_KF1, TTYCODE_STRING, "kf1" }, + { TTYC_KF10, TTYCODE_STRING, "kf10" }, + { TTYC_KF11, TTYCODE_STRING, "kf11" }, + { TTYC_KF12, TTYCODE_STRING, "kf12" }, + { TTYC_KF13, TTYCODE_STRING, "kf13" }, + { TTYC_KF14, TTYCODE_STRING, "kf14" }, + { TTYC_KF15, TTYCODE_STRING, "kf15" }, + { TTYC_KF16, TTYCODE_STRING, "kf16" }, + { TTYC_KF17, TTYCODE_STRING, "kf17" }, + { TTYC_KF18, TTYCODE_STRING, "kf18" }, + { TTYC_KF19, TTYCODE_STRING, "kf19" }, + { TTYC_KF20, TTYCODE_STRING, "kf20" }, + { TTYC_KF2, TTYCODE_STRING, "kf2" }, + { TTYC_KF3, TTYCODE_STRING, "kf3" }, + { TTYC_KF4, TTYCODE_STRING, "kf4" }, + { TTYC_KF5, TTYCODE_STRING, "kf5" }, + { TTYC_KF6, TTYCODE_STRING, "kf6" }, + { TTYC_KF7, TTYCODE_STRING, "kf7" }, + { TTYC_KF8, TTYCODE_STRING, "kf8" }, + { TTYC_KF9, TTYCODE_STRING, "kf9" }, + { TTYC_KHOME, TTYCODE_STRING, "khome" }, + { TTYC_KICH1, TTYCODE_STRING, "kich1" }, + { TTYC_KMOUS, TTYCODE_STRING, "kmous" }, + { TTYC_KNP, TTYCODE_STRING, "knp" }, + { TTYC_KPP, TTYCODE_STRING, "kpp" }, + { TTYC_OP, TTYCODE_STRING, "op" }, + { TTYC_REV, TTYCODE_STRING, "rev" }, + { TTYC_RI, TTYCODE_STRING, "ri" }, + { TTYC_RMACS, TTYCODE_STRING, "rmacs" }, + { TTYC_RMCUP, TTYCODE_STRING, "rmcup" }, + { TTYC_RMIR, TTYCODE_STRING, "rmir" }, + { TTYC_RMKX, TTYCODE_STRING, "rmkx" }, + { TTYC_SETAB, TTYCODE_STRING, "setab" }, + { TTYC_SETAF, TTYCODE_STRING, "setaf" }, + { TTYC_SGR0, TTYCODE_STRING, "sgr0" }, + { TTYC_SMACS, TTYCODE_STRING, "smacs" }, + { TTYC_SMCUP, TTYCODE_STRING, "smcup" }, + { TTYC_SMIR, TTYCODE_STRING, "smir" }, + { TTYC_SMKX, TTYCODE_STRING, "smkx" }, + { TTYC_SMSO, TTYCODE_STRING, "smso" }, + { TTYC_SMUL, TTYCODE_STRING, "smul" }, + { TTYC_XENL, TTYCODE_FLAG, "xenl" }, +}; + +char * +tty_term_strip(const char *s) +{ + const char *ptr; + static char buf[BUFSIZ]; + size_t len; + + /* Ignore strings with no padding. */ + if (strchr(s, '$') == NULL) + return (xstrdup(s)); + + len = 0; + for (ptr = s; *ptr != '\0'; ptr++) { + if (*ptr == '$' && *(ptr + 1) == '<') { + while (*ptr != '\0' && *ptr != '>') + ptr++; + if (*ptr == '>') + ptr++; + } + + buf[len++] = *ptr; + if (len == (sizeof buf) - 1) + break; + } + buf[len] = '\0'; + + return (xstrdup(buf)); +} + +void +tty_term_quirks(struct tty_term *term) +{ + if (strncmp(term->name, "rxvt", 4) == 0) { + /* rxvt supports dch1 but some termcap files do not have it. */ + if (!tty_term_has(term, TTYC_DCH1)) { + term->codes[TTYC_DCH1].type = TTYCODE_STRING; + term->codes[TTYC_DCH1].value.string = xstrdup("\033[P"); + } + } + + if (strncmp(term->name, "xterm", 5) == 0) { + /* xterm supports ich1 but some termcaps omit it. */ + if (!tty_term_has(term, TTYC_ICH1)) { + term->codes[TTYC_ICH1].type = TTYCODE_STRING; + term->codes[TTYC_ICH1].value.string = xstrdup("\033[@"); + } + } +} + +struct tty_term * +tty_term_find(char *name, int fd, char **cause) +{ + struct tty_term *term; + struct tty_term_code_entry *ent; + struct tty_code *code; + u_int i; + int n, error; + char *s; + + SLIST_FOREACH(term, &tty_terms, entry) { + if (strcmp(term->name, name) == 0) { + term->references++; + return (term); + } + } + + log_debug("new term: %s", name); + term = xmalloc(sizeof *term); + term->name = xstrdup(name); + term->references = 1; + term->flags = 0; + SLIST_INSERT_HEAD(&tty_terms, term, entry); + + /* Set up ncurses terminal. */ + if (setupterm(name, fd, &error) != OK) { + switch (error) { + case 0: + xasprintf(cause, "can't use hardcopy terminal"); + break; + case 1: + xasprintf(cause, "missing or unsuitable terminal"); + break; + case 2: + xasprintf(cause, "can't find terminfo database"); + break; + default: + xasprintf(cause, "unknown error"); + break; + } + goto error; + } + + /* Fill in codes. */ + memset(&term->codes, 0, sizeof term->codes); + for (i = 0; i < NTTYCODE; i++) { + ent = &tty_term_codes[i]; + + code = &term->codes[ent->code]; + code->type = TTYCODE_NONE; + switch (ent->type) { + case TTYCODE_NONE: + break; + case TTYCODE_STRING: + s = tigetstr((char *) ent->name); + if (s == NULL || s == (char *) -1) + break; + code->type = TTYCODE_STRING; + code->value.string = tty_term_strip(s); + break; + case TTYCODE_NUMBER: + n = tigetnum((char *) ent->name); + if (n == -1 || n == -2) + break; + code->type = TTYCODE_NUMBER; + code->value.number = n; + break; + case TTYCODE_FLAG: + n = tigetflag((char *) ent->name); + if (n == -1) + break; + code->type = TTYCODE_FLAG; + code->value.number = n; + break; + } + } + tty_term_quirks(term); + + /* Delete ncurses data. */ + del_curterm(cur_term); + + /* These are always required. */ + if (!tty_term_has(term, TTYC_CLEAR)) { + xasprintf(cause, "terminal does not support clear"); + goto error; + } + if (!tty_term_has(term, TTYC_RI)) { + xasprintf(cause, "terminal does not support ri"); + goto error; + } + if (!tty_term_has(term, TTYC_CUP)) { + xasprintf(cause, "terminal does not support cup"); + goto error; + } + + /* These can be emulated so one of the two is required. */ + if (!tty_term_has(term, TTYC_CUD1) && !tty_term_has(term, TTYC_CUD)) { + xasprintf(cause, "terminal does not support cud1 or cud"); + goto error; + } + if (!tty_term_has(term, TTYC_IL1) && !tty_term_has(term, TTYC_IL)) { + xasprintf(cause, "terminal does not support il1 or il"); + goto error; + } + if (!tty_term_has(term, TTYC_DL1) && !tty_term_has(term, TTYC_DL)) { + xasprintf(cause, "terminal does not support dl1 or dl"); + goto error; + } + if (!tty_term_has(term, TTYC_ICH1) && + !tty_term_has(term, TTYC_ICH) && (!tty_term_has(term, TTYC_SMIR) || + !tty_term_has(term, TTYC_RMIR))) { + xasprintf(cause, + "terminal does not support ich1 or ich or smir and rmir"); + goto error; + } + if (!tty_term_has(term, TTYC_DCH1) && !tty_term_has(term, TTYC_DCH)) { + xasprintf(cause, "terminal does not support dch1 or dch"); + goto error; + } + + /* + * Figure out if terminal support default colours. AX is a screen + * extension which indicates this. Also check if op (orig_pair) uses + * the default colours - if it does, this is a good indication the + * terminal supports them. + */ + if (tty_term_flag(term, TTYC_AX)) + term->flags |= TERM_HASDEFAULTS; + if (strcmp(tty_term_string(term, TTYC_OP), "\033[39;49m") == 0) + term->flags |= TERM_HASDEFAULTS; + + /* + * Try to figure out if we have 256 or 88 colours. The standard xterm + * definitions are broken (well, or the way they are parsed is: in any + * case they end up returning 8). So also do a hack. + */ + if (tty_term_number(term, TTYC_COLORS) == 256) + term->flags |= TERM_256COLOURS; + if (strstr(name, "256col") != NULL) /* XXX HACK */ + term->flags |= TERM_256COLOURS; + if (tty_term_number(term, TTYC_COLORS) == 88) + term->flags |= TERM_88COLOURS; + if (strstr(name, "88col") != NULL) /* XXX HACK */ + term->flags |= TERM_88COLOURS; + + /* + * Terminals without xenl (eat newline glitch) wrap at at $COLUMNS - 1 + * rather than $COLUMNS (the cursor can never be beyond $COLUMNS - 1). + * + * This is irritating, most notably because it is impossible to write + * to the very bottom-right of the screen without scrolling. + * + * Flag the terminal here and apply some workarounds in other places to + * do the best possible. + */ + if (!tty_term_flag(term, TTYC_XENL)) + term->flags |= TERM_EARLYWRAP; + + return (term); + +error: + tty_term_free(term); + return (NULL); +} + +void +tty_term_free(struct tty_term *term) +{ + u_int i; + + if (--term->references != 0) + return; + + SLIST_REMOVE(&tty_terms, term, tty_term, entry); + + for (i = 0; i < NTTYCODE; i++) { + if (term->codes[i].type == TTYCODE_STRING) + xfree(term->codes[i].value.string); + } + xfree(term->name); + xfree(term); +} + +int +tty_term_has(struct tty_term *term, enum tty_code_code code) +{ + return (term->codes[code].type != TTYCODE_NONE); +} + +const char * +tty_term_string(struct tty_term *term, enum tty_code_code code) +{ + if (!tty_term_has(term, code)) + return (""); + if (term->codes[code].type != TTYCODE_STRING) + log_fatalx("not a string: %d", code); + return (term->codes[code].value.string); +} + +/* No vtparm. Fucking ncurses. */ +const char * +tty_term_string1(struct tty_term *term, enum tty_code_code code, int a) +{ + return (tparm((char *) tty_term_string(term, code), a)); +} + +const char * +tty_term_string2(struct tty_term *term, enum tty_code_code code, int a, int b) +{ + return (tparm((char *) tty_term_string(term, code), a, b)); +} + +int +tty_term_number(struct tty_term *term, enum tty_code_code code) +{ + if (!tty_term_has(term, code)) + return (0); + if (term->codes[code].type != TTYCODE_NUMBER) + log_fatalx("not a number: %d", code); + return (term->codes[code].value.number); +} + +int +tty_term_flag(struct tty_term *term, enum tty_code_code code) +{ + if (!tty_term_has(term, code)) + return (0); + if (term->codes[code].type != TTYCODE_FLAG) + log_fatalx("not a flag: %d", code); + return (term->codes[code].value.flag); +} + diff --git a/usr.bin/tmux/tty-write.c b/usr.bin/tmux/tty-write.c new file mode 100644 index 00000000000..7ab56a86bea --- /dev/null +++ b/usr.bin/tmux/tty-write.c @@ -0,0 +1,91 @@ +/* $OpenBSD: tty-write.c,v 1.1 2009/06/01 22:58:49 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 "tmux.h" + +void tty_vwrite_cmd(struct window_pane *, enum tty_cmd, va_list); + +void +tty_write_cmd(struct window_pane *wp, enum tty_cmd cmd, ...) +{ + va_list ap; + + va_start(ap, cmd); + tty_vwrite_cmd(wp, cmd, ap); + va_end(ap); +} + +void +tty_vwrite_cmd(struct window_pane *wp, enum tty_cmd cmd, va_list ap) +{ + struct client *c; + va_list aq; + u_int i; + + if (wp == NULL) + return; + + if (wp->window->flags & WINDOW_REDRAW || wp->flags & PANE_REDRAW) + return; + if (wp->window->flags & WINDOW_HIDDEN || wp->flags & PANE_HIDDEN) + return; + + for (i = 0; i < ARRAY_LENGTH(&clients); i++) { + c = ARRAY_ITEM(&clients, i); + if (c == NULL || c->session == NULL) + continue; + if (c->flags & CLIENT_SUSPENDED) + continue; + + if (c->session->curw->window == wp->window) { + tty_update_mode(&c->tty, c->tty.mode & ~MODE_CURSOR); + + va_copy(aq, ap); + tty_vwrite(&c->tty, wp, cmd, aq); + va_end(aq); + } + } +} + +void +tty_write_mode(struct window_pane *wp, int mode) +{ + struct client *c; + u_int i; + + if (wp == NULL) + return; + + if (wp->window->flags & WINDOW_REDRAW || wp->flags & PANE_REDRAW) + return; + if (wp->window->flags & WINDOW_HIDDEN || wp->flags & PANE_HIDDEN) + return; + + for (i = 0; i < ARRAY_LENGTH(&clients); i++) { + c = ARRAY_ITEM(&clients, i); + if (c == NULL || c->session == NULL) + continue; + if (c->flags & CLIENT_SUSPENDED) + continue; + + tty_update_mode(&c->tty, mode); + } +} + diff --git a/usr.bin/tmux/tty.c b/usr.bin/tmux/tty.c new file mode 100644 index 00000000000..c8a61f96d7b --- /dev/null +++ b/usr.bin/tmux/tty.c @@ -0,0 +1,1076 @@ +/* $OpenBSD: tty.c,v 1.1 2009/06/01 22:58:49 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 <errno.h> +#include <fcntl.h> +#include <string.h> +#include <termios.h> +#include <unistd.h> + +#include "tmux.h" + +void tty_fill_acs(struct tty *); + +void tty_raw(struct tty *, const char *); + +int tty_try_256(struct tty *, u_char, const char *); +int tty_try_88(struct tty *, u_char, const char *); + +void tty_attributes(struct tty *, const struct grid_cell *); +void tty_attributes_fg(struct tty *, const struct grid_cell *); +void tty_attributes_bg(struct tty *, const struct grid_cell *); + +void tty_cmd_cell(struct tty *, struct window_pane *, va_list); +void tty_cmd_clearendofline(struct tty *, struct window_pane *, va_list); +void tty_cmd_clearendofscreen(struct tty *, struct window_pane *, va_list); +void tty_cmd_clearline(struct tty *, struct window_pane *, va_list); +void tty_cmd_clearscreen(struct tty *, struct window_pane *, va_list); +void tty_cmd_clearstartofline(struct tty *, struct window_pane *, va_list); +void tty_cmd_clearstartofscreen(struct tty *, struct window_pane *, va_list); +void tty_cmd_deletecharacter(struct tty *, struct window_pane *, va_list); +void tty_cmd_deleteline(struct tty *, struct window_pane *, va_list); +void tty_cmd_insertcharacter(struct tty *, struct window_pane *, va_list); +void tty_cmd_insertline(struct tty *, struct window_pane *, va_list); +void tty_cmd_linefeed(struct tty *, struct window_pane *, va_list); +void tty_cmd_raw(struct tty *, struct window_pane *, va_list); +void tty_cmd_reverseindex(struct tty *, struct window_pane *, va_list); + +void (*tty_cmds[])(struct tty *, struct window_pane *, va_list) = { + tty_cmd_cell, + tty_cmd_clearendofline, + tty_cmd_clearendofscreen, + tty_cmd_clearline, + tty_cmd_clearscreen, + tty_cmd_clearstartofline, + tty_cmd_clearstartofscreen, + tty_cmd_deletecharacter, + tty_cmd_deleteline, + tty_cmd_insertcharacter, + tty_cmd_insertline, + tty_cmd_linefeed, + tty_cmd_raw, + tty_cmd_reverseindex, +}; + +void +tty_init(struct tty *tty, char *path, char *term) +{ + tty->path = xstrdup(path); + if (term == NULL) + tty->termname = xstrdup("unknown"); + else + tty->termname = xstrdup(term); + tty->flags = 0; + tty->term_flags = 0; +} + +int +tty_open(struct tty *tty, char **cause) +{ + int mode; + + tty->fd = open(tty->path, O_RDWR|O_NONBLOCK); + if (tty->fd == -1) { + xasprintf(cause, "%s: %s", tty->path, strerror(errno)); + return (-1); + } + + if ((mode = fcntl(tty->fd, F_GETFL)) == -1) + fatal("fcntl failed"); + if (fcntl(tty->fd, F_SETFL, mode|O_NONBLOCK) == -1) + fatal("fcntl failedo"); + if (fcntl(tty->fd, F_SETFD, FD_CLOEXEC) == -1) + fatal("fcntl failed"); + + if (debug_level > 3) + tty->log_fd = open("tmux.out", O_WRONLY|O_CREAT|O_TRUNC, 0644); + else + tty->log_fd = -1; + + if ((tty->term = tty_term_find(tty->termname, tty->fd, cause)) == NULL) + goto error; + + tty->in = buffer_create(BUFSIZ); + tty->out = buffer_create(BUFSIZ); + + tty->flags &= TTY_UTF8; + + tty_start_tty(tty); + + tty_keys_init(tty); + + tty_fill_acs(tty); + + return (0); + +error: + close(tty->fd); + tty->fd = -1; + + return (-1); +} + +void +tty_start_tty(struct tty *tty) +{ + struct termios tio; + int what; + + tty_detect_utf8(tty); + + if (tcgetattr(tty->fd, &tty->tio) != 0) + fatal("tcgetattr failed"); + memcpy(&tio, &tty->tio, sizeof tio); + tio.c_iflag &= ~(IXON|IXOFF|ICRNL|INLCR|IGNCR|IMAXBEL|ISTRIP); + tio.c_iflag |= IGNBRK; + tio.c_oflag &= ~(OPOST|ONLCR|OCRNL|ONLRET); + tio.c_lflag &= ~(IEXTEN|ICANON|ECHO|ECHOE|ECHONL|ECHOCTL| + ECHOPRT|ECHOKE|ECHOCTL|ISIG); + tio.c_cc[VMIN] = 1; + tio.c_cc[VTIME] = 0; + if (tcsetattr(tty->fd, TCSANOW, &tio) != 0) + fatal("tcsetattr failed"); + + what = 0; + if (ioctl(tty->fd, TIOCFLUSH, &what) != 0) + fatal("ioctl(TIOCFLUSH)"); + + tty_putcode(tty, TTYC_IS1); + tty_putcode(tty, TTYC_IS2); + tty_putcode(tty, TTYC_IS3); + + tty_putcode(tty, TTYC_SMCUP); + tty_putcode(tty, TTYC_SMKX); + tty_putcode(tty, TTYC_ENACS); + tty_putcode(tty, TTYC_CLEAR); + + tty_putcode(tty, TTYC_CNORM); + if (tty_term_has(tty->term, TTYC_KMOUS)) + tty_puts(tty, "\033[?1000l"); + + memcpy(&tty->cell, &grid_default_cell, sizeof tty->cell); + + tty->cx = UINT_MAX; + tty->cy = UINT_MAX; + + tty->rlower = UINT_MAX; + tty->rupper = UINT_MAX; + + tty->mode = MODE_CURSOR; +} + +void +tty_stop_tty(struct tty *tty) +{ + struct winsize ws; + + /* + * Be flexible about error handling and try not kill the server just + * because the fd is invalid. Things like ssh -t can easily leave us + * with a dead tty. + */ + if (ioctl(tty->fd, TIOCGWINSZ, &ws) == -1) + return; + if (tcsetattr(tty->fd, TCSANOW, &tty->tio) == -1) + return; + + tty_raw(tty, tty_term_string2(tty->term, TTYC_CSR, 0, ws.ws_row - 1)); + tty_raw(tty, tty_term_string(tty->term, TTYC_RMACS)); + tty_raw(tty, tty_term_string(tty->term, TTYC_SGR0)); + tty_raw(tty, tty_term_string(tty->term, TTYC_RMKX)); + tty_raw(tty, tty_term_string(tty->term, TTYC_RMCUP)); + tty_raw(tty, tty_term_string(tty->term, TTYC_CLEAR)); + + tty_raw(tty, tty_term_string(tty->term, TTYC_CNORM)); + if (tty_term_has(tty->term, TTYC_KMOUS)) + tty_raw(tty, "\033[?1000l"); +} + +void +tty_detect_utf8(struct tty *tty) +{ + struct pollfd pfd; + char buf[7]; + size_t len; + ssize_t n; + int nfds; + struct termios tio, old_tio; + int what; + + if (tty->flags & TTY_UTF8) + return; + + /* + * If the terminal looks reasonably likely to support this, try to + * write a three-byte UTF-8 wide character to the terminal, then read + * the cursor position. + * + * XXX This entire function is a hack. + */ + + /* Check if the terminal looks sort of vt100. */ + if (strstr(tty_term_string(tty->term, TTYC_CLEAR), "[2J") == NULL || + strstr(tty_term_string(tty->term, TTYC_CUP), "H") == NULL) + return; + + if (tcgetattr(tty->fd, &old_tio) != 0) + fatal("tcgetattr failed"); + cfmakeraw(&tio); + if (tcsetattr(tty->fd, TCSANOW, &tio) != 0) + fatal("tcsetattr failed"); + + what = 0; + if (ioctl(tty->fd, TIOCFLUSH, &what) != 0) + fatal("ioctl(TIOCFLUSH)"); + +#define UTF8_TEST_DATA "\033[H\357\277\246\033[6n" + if (write(tty->fd, UTF8_TEST_DATA, (sizeof UTF8_TEST_DATA) - 1) == -1) + fatal("write failed"); +#undef UTF8_TEST_DATA + + len = 0; + for (;;) { + pfd.fd = tty->fd; + pfd.events = POLLIN; + + nfds = poll(&pfd, 1, 500); + if (nfds == -1) { + if (errno == EAGAIN || errno == EINTR) + continue; + fatal("poll failed"); + } + if (nfds == 0) + break; + if (pfd.revents & (POLLERR|POLLNVAL|POLLHUP)) + break; + if (!(pfd.revents & POLLIN)) + continue; + + if ((n = read(tty->fd, buf + len, 1)) != 1) + break; + buf[++len] = '\0'; + + if (len == (sizeof buf) - 1) { + if (strcmp(buf, "\033[1;3R") == 0) + tty->flags |= TTY_UTF8; + break; + } + } + + if (tcsetattr(tty->fd, TCSANOW, &old_tio) != 0) + fatal("tcsetattr failed"); +} + +void +tty_fill_acs(struct tty *tty) +{ + const char *ptr; + + memset(tty->acs, 0, sizeof tty->acs); + if (!tty_term_has(tty->term, TTYC_ACSC)) + return; + + ptr = tty_term_string(tty->term, TTYC_ACSC); + if (strlen(ptr) % 2 != 0) + return; + for (; *ptr != '\0'; ptr += 2) + tty->acs[(u_char) ptr[0]] = ptr[1]; +} + +u_char +tty_get_acs(struct tty *tty, u_char ch) +{ + if (tty->acs[ch] != '\0') + return (tty->acs[ch]); + return (ch); +} + +void +tty_close(struct tty *tty, int no_stop) +{ + if (tty->fd == -1) + return; + + if (tty->log_fd != -1) { + close(tty->log_fd); + tty->log_fd = -1; + } + + if (!no_stop) + tty_stop_tty(tty); + + tty_term_free(tty->term); + tty_keys_free(tty); + + close(tty->fd); + tty->fd = -1; + + buffer_destroy(tty->in); + buffer_destroy(tty->out); +} + +void +tty_free(struct tty *tty, int no_stop) +{ + tty_close(tty, no_stop); + + if (tty->path != NULL) + xfree(tty->path); + if (tty->termname != NULL) + xfree(tty->termname); +} + +void +tty_raw(struct tty *tty, const char *s) +{ + write(tty->fd, s, strlen(s)); +} + +void +tty_putcode(struct tty *tty, enum tty_code_code code) +{ + tty_puts(tty, tty_term_string(tty->term, code)); +} + +void +tty_putcode1(struct tty *tty, enum tty_code_code code, int a) +{ + if (a < 0) + return; + tty_puts(tty, tty_term_string1(tty->term, code, a)); +} + +void +tty_putcode2(struct tty *tty, enum tty_code_code code, int a, int b) +{ + if (a < 0 || b < 0) + return; + tty_puts(tty, tty_term_string2(tty->term, code, a, b)); +} + +void +tty_puts(struct tty *tty, const char *s) +{ + if (*s == '\0') + return; + buffer_write(tty->out, s, strlen(s)); + + if (tty->log_fd != -1) + write(tty->log_fd, s, strlen(s)); +} + +void +tty_putc(struct tty *tty, u_char ch) +{ + u_int sx; + + if (tty->cell.attr & GRID_ATTR_CHARSET) + ch = tty_get_acs(tty, ch); + buffer_write8(tty->out, ch); + + if (ch >= 0x20 && ch != 0x7f) { + sx = tty->sx; + if (tty->term->flags & TERM_EARLYWRAP) + sx--; + + if (tty->cx == sx) { + tty->cx = 0; + tty->cy++; + } else + tty->cx++; + } + + if (tty->log_fd != -1) + write(tty->log_fd, &ch, 1); +} + +void +tty_set_title(struct tty *tty, const char *title) +{ + if (strstr(tty->termname, "xterm") == NULL && + strstr(tty->termname, "rxvt") == NULL && + strcmp(tty->termname, "screen") != 0) + return; + + tty_puts(tty, "\033]0;"); + tty_puts(tty, title); + tty_putc(tty, '\007'); +} + +void +tty_update_mode(struct tty *tty, int mode) +{ + int changed; + + if (tty->flags & TTY_NOCURSOR) + mode &= ~MODE_CURSOR; + + changed = mode ^ tty->mode; + if (changed & MODE_CURSOR) { + if (mode & MODE_CURSOR) + tty_putcode(tty, TTYC_CNORM); + else + tty_putcode(tty, TTYC_CIVIS); + } + if (changed & MODE_MOUSE) { + if (mode & MODE_MOUSE) + tty_puts(tty, "\033[?1000h"); + else + tty_puts(tty, "\033[?1000l"); + } + tty->mode = mode; +} + +void +tty_emulate_repeat( + struct tty *tty, enum tty_code_code code, enum tty_code_code code1, u_int n) +{ + if (tty_term_has(tty->term, code)) + tty_putcode1(tty, code, n); + else { + while (n-- > 0) + tty_putcode(tty, code1); + } +} + +/* + * Redraw scroll region using data from screen (already updated). Used when + * CSR not supported, or window is a pane that doesn't take up the full + * width of the terminal. + */ +void +tty_redraw_region(struct tty *tty, struct window_pane *wp) +{ + struct screen *s = wp->screen; + u_int i; + + /* + * If region is >= 50% of the screen, just schedule a window redraw. In + * most cases, this is likely to be followed by some more scrolling - + * without this, the entire pane ends up being redrawn many times which + * can be much more data. + */ + if (s->old_rupper - s->old_rlower >= screen_size_y(s) / 2) { + wp->flags |= PANE_REDRAW; + return; + } + + if (s->old_cy < s->old_rupper || s->old_cy > s->old_rlower) { + for (i = s->old_cy; i < screen_size_y(s); i++) + tty_draw_line(tty, s, i, wp->xoff, wp->yoff); + } else { + for (i = s->old_rupper; i <= s->old_rlower; i++) + tty_draw_line(tty, s, i, wp->xoff, wp->yoff); + } +} + +void +tty_draw_line(struct tty *tty, struct screen *s, u_int py, u_int ox, u_int oy) +{ + const struct grid_cell *gc; + const struct grid_utf8 *gu; + u_int i, sx; + + sx = screen_size_x(s); + if (sx > s->grid->size[s->grid->hsize + py]) + sx = s->grid->size[s->grid->hsize + py]; + if (sx > tty->sx) + sx = tty->sx; + + tty_cursor(tty, 0, py, ox, oy); + for (i = 0; i < sx; i++) { + gc = grid_view_peek_cell(s->grid, i, py); + + gu = NULL; + if (gc->flags & GRID_FLAG_UTF8) + gu = grid_view_peek_utf8(s->grid, i, py); + + if (screen_check_selection(s, i, py)) { + s->sel.cell.data = gc->data; + tty_cell(tty, &s->sel.cell, gu); + } else + tty_cell(tty, gc, gu); + } + + if (sx >= tty->sx) + return; + tty_reset(tty); + + tty_cursor(tty, sx, py, ox, oy); + if (screen_size_x(s) >= tty->sx && tty_term_has(tty->term, TTYC_EL)) + tty_putcode(tty, TTYC_EL); + else { + for (i = sx; i < screen_size_x(s); i++) + tty_putc(tty, ' '); + } +} + +void +tty_write(struct tty *tty, struct window_pane *wp, enum tty_cmd cmd, ...) +{ + va_list ap; + + va_start(ap, cmd); + tty_vwrite(tty, wp, cmd, ap); + va_end(ap); +} + +void +tty_vwrite( + struct tty *tty, struct window_pane *wp, enum tty_cmd cmd, va_list ap) +{ + if (tty->flags & TTY_FREEZE || tty->term == NULL) + return; + if (tty_cmds[cmd] != NULL) + tty_cmds[cmd](tty, wp, ap); +} + +void +tty_cmd_insertcharacter(struct tty *tty, struct window_pane *wp, va_list ap) +{ + struct screen *s = wp->screen; + u_int ua; + + if (wp->xoff != 0 || screen_size_x(s) < tty->sx) { + tty_draw_line(tty, wp->screen, s->old_cy, wp->xoff, wp->yoff); + return; + } + + ua = va_arg(ap, u_int); + + tty_reset(tty); + + tty_cursor(tty, s->old_cx, s->old_cy, wp->xoff, wp->yoff); + if (tty_term_has(tty->term, TTYC_ICH) || + tty_term_has(tty->term, TTYC_ICH1)) + tty_emulate_repeat(tty, TTYC_ICH, TTYC_ICH1, ua); + else { + tty_putcode(tty, TTYC_SMIR); + while (ua-- > 0) + tty_putc(tty, ' '); + tty_putcode(tty, TTYC_RMIR); + } +} + +void +tty_cmd_deletecharacter(struct tty *tty, struct window_pane *wp, va_list ap) +{ + struct screen *s = wp->screen; + u_int ua; + + if (wp->xoff != 0 || screen_size_x(s) < tty->sx) { + tty_draw_line(tty, wp->screen, s->old_cy, wp->xoff, wp->yoff); + return; + } + + ua = va_arg(ap, u_int); + + tty_reset(tty); + + tty_cursor(tty, s->old_cx, s->old_cy, wp->xoff, wp->yoff); + tty_emulate_repeat(tty, TTYC_DCH, TTYC_DCH1, ua); +} + +void +tty_cmd_insertline(struct tty *tty, struct window_pane *wp, va_list ap) +{ + struct screen *s = wp->screen; + u_int ua; + + if (wp->xoff != 0 || screen_size_x(s) < tty->sx || + !tty_term_has(tty->term, TTYC_CSR)) { + tty_redraw_region(tty, wp); + return; + } + + ua = va_arg(ap, u_int); + + tty_reset(tty); + + tty_region(tty, s->old_rupper, s->old_rlower, wp->yoff); + + tty_cursor(tty, s->old_cx, s->old_cy, wp->xoff, wp->yoff); + tty_emulate_repeat(tty, TTYC_IL, TTYC_IL1, ua); +} + +void +tty_cmd_deleteline(struct tty *tty, struct window_pane *wp, va_list ap) +{ + struct screen *s = wp->screen; + u_int ua; + + if (wp->xoff != 0 || screen_size_x(s) < tty->sx || + !tty_term_has(tty->term, TTYC_CSR)) { + tty_redraw_region(tty, wp); + return; + } + + ua = va_arg(ap, u_int); + + tty_reset(tty); + + tty_region(tty, s->old_rupper, s->old_rlower, wp->yoff); + + tty_cursor(tty, s->old_cx, s->old_cy, wp->xoff, wp->yoff); + tty_emulate_repeat(tty, TTYC_DL, TTYC_DL1, ua); +} + +void +tty_cmd_clearline(struct tty *tty, struct window_pane *wp, unused va_list ap) +{ + struct screen *s = wp->screen; + u_int i; + + tty_reset(tty); + + tty_cursor(tty, 0, s->old_cy, wp->xoff, wp->yoff); + if (wp->xoff == 0 && screen_size_x(s) >= tty->sx && + tty_term_has(tty->term, TTYC_EL)) { + tty_putcode(tty, TTYC_EL); + } else { + for (i = 0; i < screen_size_x(s); i++) + tty_putc(tty, ' '); + } +} + +void +tty_cmd_clearendofline( + struct tty *tty, struct window_pane *wp, unused va_list ap) +{ + struct screen *s = wp->screen; + u_int i; + + tty_reset(tty); + + tty_cursor(tty, s->old_cx, s->old_cy, wp->xoff, wp->yoff); + if (wp->xoff == 0 && screen_size_x(s) >= tty->sx && + tty_term_has(tty->term, TTYC_EL)) + tty_putcode(tty, TTYC_EL); + else { + for (i = s->old_cx; i < screen_size_x(s); i++) + tty_putc(tty, ' '); + } +} + +void +tty_cmd_clearstartofline( + struct tty *tty, struct window_pane *wp, unused va_list ap) +{ + struct screen *s = wp->screen; + u_int i; + + tty_reset(tty); + + if (wp->xoff == 0 && tty_term_has(tty->term, TTYC_EL1)) { + tty_cursor(tty, s->old_cx, s->old_cy, wp->xoff, wp->yoff); + tty_putcode(tty, TTYC_EL1); + } else { + tty_cursor(tty, 0, s->old_cy, wp->xoff, wp->yoff); + for (i = 0; i < s->old_cx + 1; i++) + tty_putc(tty, ' '); + } +} + +void +tty_cmd_reverseindex(struct tty *tty, struct window_pane *wp, unused va_list ap) +{ + struct screen *s = wp->screen; + + if (wp->xoff != 0 || screen_size_x(s) < tty->sx || + !tty_term_has(tty->term, TTYC_CSR)) { + tty_redraw_region(tty, wp); + return; + } + + tty_reset(tty); + + tty_region(tty, s->old_rupper, s->old_rlower, wp->yoff); + + if (s->old_cy == s->old_rupper) { + tty_cursor(tty, s->old_cx, s->old_rupper, wp->xoff, wp->yoff); + tty_putcode(tty, TTYC_RI); + } +} + +void +tty_cmd_linefeed(struct tty *tty, struct window_pane *wp, unused va_list ap) +{ + struct screen *s = wp->screen; + + if (wp->xoff != 0 || screen_size_x(s) < tty->sx || + !tty_term_has(tty->term, TTYC_CSR)) { + tty_redraw_region(tty, wp); + return; + } + + tty_reset(tty); + + tty_region(tty, s->old_rupper, s->old_rlower, wp->yoff); + + if (s->old_cy == s->old_rlower) { + tty_cursor(tty, s->old_cx, s->old_cy, wp->xoff, wp->yoff); + tty_putc(tty, '\n'); + } +} + +void +tty_cmd_clearendofscreen( + struct tty *tty, struct window_pane *wp, unused va_list ap) +{ + struct screen *s = wp->screen; + u_int i, j, oy; + + oy = wp->yoff; + + tty_reset(tty); + + tty_region(tty, 0, screen_size_y(s) - 1, wp->yoff); + tty_cursor(tty, s->old_cx, s->old_cy, wp->xoff, wp->yoff); + if (wp->xoff == 0 && screen_size_x(s) >= tty->sx && + tty_term_has(tty->term, TTYC_EL)) { + tty_putcode(tty, TTYC_EL); + if (s->old_cy != screen_size_y(s) - 1) { + tty_cursor(tty, 0, s->old_cy + 1, wp->xoff, wp->yoff); + for (i = s->old_cy + 1; i < screen_size_y(s); i++) { + tty_putcode(tty, TTYC_EL); + if (i == screen_size_y(s) - 1) + continue; + tty_emulate_repeat(tty, TTYC_CUD, TTYC_CUD1, 1); + tty->cy++; + } + } + } else { + for (i = s->old_cx; i < screen_size_x(s); i++) + tty_putc(tty, ' '); + for (j = s->old_cy; j < screen_size_y(s); j++) { + tty_cursor(tty, 0, j, wp->xoff, wp->yoff); + for (i = 0; i < screen_size_x(s); i++) + tty_putc(tty, ' '); + } + } +} + +void +tty_cmd_clearstartofscreen( + struct tty *tty, struct window_pane *wp, unused va_list ap) +{ + struct screen *s = wp->screen; + u_int i, j; + + tty_reset(tty); + + tty_region(tty, 0, screen_size_y(s) - 1, wp->yoff); + tty_cursor(tty, 0, 0, wp->xoff, wp->yoff); + if (wp->xoff == 0 && screen_size_x(s) >= tty->sx && + tty_term_has(tty->term, TTYC_EL)) { + for (i = 0; i < s->old_cy; i++) { + tty_putcode(tty, TTYC_EL); + tty_emulate_repeat(tty, TTYC_CUD, TTYC_CUD1, 1); + tty->cy++; + } + } else { + for (j = 0; j < s->old_cy; j++) { + tty_cursor(tty, 0, j, wp->xoff, wp->yoff); + for (i = 0; i < screen_size_x(s); i++) + tty_putc(tty, ' '); + } + } + for (i = 0; i < s->old_cx; i++) + tty_putc(tty, ' '); +} + +void +tty_cmd_clearscreen( + struct tty *tty, struct window_pane *wp, unused va_list ap) +{ + struct screen *s = wp->screen; + u_int i, j; + + tty_reset(tty); + + tty_region(tty, 0, screen_size_y(s) - 1, wp->yoff); + tty_cursor(tty, 0, 0, wp->xoff, wp->yoff); + if (wp->xoff == 0 && screen_size_x(s) >= tty->sx && + tty_term_has(tty->term, TTYC_EL)) { + for (i = 0; i < screen_size_y(s); i++) { + tty_putcode(tty, TTYC_EL); + if (i != screen_size_y(s) - 1) { + tty_emulate_repeat(tty, TTYC_CUD, TTYC_CUD1, 1); + tty->cy++; + } + } + } else { + for (j = 0; j < screen_size_y(s); j++) { + tty_cursor(tty, 0, j, wp->xoff, wp->yoff); + for (i = 0; i < screen_size_x(s); i++) + tty_putc(tty, ' '); + } + } +} + +void +tty_cmd_cell(struct tty *tty, struct window_pane *wp, va_list ap) +{ + struct screen *s = wp->screen; + struct grid_cell *gc; + struct grid_utf8 *gu; + + gc = va_arg(ap, struct grid_cell *); + gu = va_arg(ap, struct grid_utf8 *); + + tty_cursor(tty, s->old_cx, s->old_cy, wp->xoff, wp->yoff); + + tty_cell(tty, gc, gu); +} + +void +tty_cmd_raw(struct tty *tty, unused struct window_pane *wp, va_list ap) +{ + u_char *buf; + size_t i, len; + + buf = va_arg(ap, u_char *); + len = va_arg(ap, size_t); + + for (i = 0; i < len; i++) + tty_putc(tty, buf[i]); +} + +void +tty_cell( + struct tty *tty, const struct grid_cell *gc, const struct grid_utf8 *gu) +{ + u_int i; + + /* Skip last character if terminal is stupid. */ + if (tty->term->flags & TERM_EARLYWRAP && + tty->cy == tty->sy - 1 && tty->cx == tty->sx - 1) + return; + + /* If this is a padding character, do nothing. */ + if (gc->flags & GRID_FLAG_PADDING) + return; + + /* Set the attributes. */ + tty_attributes(tty, gc); + + /* If not UTF-8, write directly. */ + if (!(gc->flags & GRID_FLAG_UTF8)) { + if (gc->data < 0x20 || gc->data == 0x7f) + return; + tty_putc(tty, gc->data); + return; + } + + /* If the terminal doesn't support UTF-8, write underscores. */ + if (!(tty->flags & TTY_UTF8)) { + for (i = 0; i < gu->width; i++) + tty_putc(tty, '_'); + return; + } + + /* Otherwise, write UTF-8. */ + for (i = 0; i < UTF8_SIZE; i++) { + if (gu->data[i] == 0xff) + break; + tty_putc(tty, gu->data[i]); + } +} + +void +tty_reset(struct tty *tty) +{ + struct grid_cell *gc = &tty->cell; + + if (memcmp(gc, &grid_default_cell, sizeof *gc) == 0) + return; + + if (tty_term_has(tty->term, TTYC_RMACS) && gc->attr & GRID_ATTR_CHARSET) + tty_putcode(tty, TTYC_RMACS); + tty_putcode(tty, TTYC_SGR0); + memcpy(gc, &grid_default_cell, sizeof *gc); +} + +void +tty_region(struct tty *tty, u_int rupper, u_int rlower, u_int oy) +{ + if (!tty_term_has(tty->term, TTYC_CSR)) + return; + if (tty->rlower != oy + rlower || tty->rupper != oy + rupper) { + tty->rlower = oy + rlower; + tty->rupper = oy + rupper; + tty->cx = 0; + tty->cy = 0; + tty_putcode2(tty, TTYC_CSR, tty->rupper, tty->rlower); + } +} + +void +tty_cursor(struct tty *tty, u_int cx, u_int cy, u_int ox, u_int oy) +{ + if (ox + cx == 0 && tty->cx != 0 && tty->cy == oy + cy) { + tty->cx = 0; + tty_putc(tty, '\r'); + } else if (tty->cx != ox + cx || tty->cy != oy + cy) { + tty->cx = ox + cx; + tty->cy = oy + cy; + tty_putcode2(tty, TTYC_CUP, tty->cy, tty->cx); + } +} + +void +tty_attributes(struct tty *tty, const struct grid_cell *gc) +{ + struct grid_cell *tc = &tty->cell; + u_char changed; + u_int fg, bg; + + /* If any bits are being cleared, reset everything. */ + if (tc->attr & ~gc->attr) + tty_reset(tty); + + /* Filter out attribute bits already set. */ + changed = gc->attr & ~tc->attr; + tc->attr = gc->attr; + + /* Set the attributes. */ + fg = gc->fg; + bg = gc->bg; + if (changed & GRID_ATTR_BRIGHT) + tty_putcode(tty, TTYC_BOLD); + if (changed & GRID_ATTR_DIM) + tty_putcode(tty, TTYC_DIM); + if (changed & GRID_ATTR_ITALICS) + tty_putcode(tty, TTYC_SMSO); + if (changed & GRID_ATTR_UNDERSCORE) + tty_putcode(tty, TTYC_SMUL); + if (changed & GRID_ATTR_BLINK) + tty_putcode(tty, TTYC_BLINK); + if (changed & GRID_ATTR_REVERSE) { + if (tty_term_has(tty->term, TTYC_REV)) + tty_putcode(tty, TTYC_REV); + else if (tty_term_has(tty->term, TTYC_SMSO)) + tty_putcode(tty, TTYC_SMSO); + } + if (changed & GRID_ATTR_HIDDEN) + tty_putcode(tty, TTYC_INVIS); + if (changed & GRID_ATTR_CHARSET) + tty_putcode(tty, TTYC_SMACS); + + /* Set foreground colour. */ + if (fg != tc->fg || + (gc->flags & GRID_FLAG_FG256) != (tc->flags & GRID_FLAG_FG256)) { + tty_attributes_fg(tty, gc); + tc->fg = fg; + } + + /* Set background colour. */ + if (bg != tc->bg || + (gc->flags & GRID_FLAG_BG256) != (tc->flags & GRID_FLAG_BG256)) { + tty_attributes_bg(tty, gc); + tc->bg = bg; + } +} + +int +tty_try_256(struct tty *tty, u_char colour, const char *type) +{ + char s[32]; + + if (!(tty->term->flags & TERM_256COLOURS) && + !(tty->term_flags & TERM_256COLOURS)) + return (-1); + + xsnprintf(s, sizeof s, "\033[%s;5;%hhum", type, colour); + tty_puts(tty, s); + return (0); +} + +int +tty_try_88(struct tty *tty, u_char colour, const char *type) +{ + char s[32]; + + if (!(tty->term->flags & TERM_88COLOURS) && + !(tty->term_flags & TERM_88COLOURS)) + return (-1); + colour = colour_256to88(colour); + + xsnprintf(s, sizeof s, "\033[%s;5;%hhum", type, colour); + tty_puts(tty, s); + return (0); +} + +void +tty_attributes_fg(struct tty *tty, const struct grid_cell *gc) +{ + u_char fg; + + fg = gc->fg; + if (gc->flags & GRID_FLAG_FG256) { + if (tty_try_256(tty, fg, "38") == 0) + return; + if (tty_try_88(tty, fg, "38") == 0) + return; + fg = colour_256to16(fg); + if (fg & 8) { + fg &= 7; + tty_putcode(tty, TTYC_BOLD); + tty->cell.attr |= GRID_ATTR_BRIGHT; + } else if (tty->cell.attr & GRID_ATTR_BRIGHT) + tty_reset(tty); + } + + if (fg == 8 && + !(tty->term->flags & TERM_HASDEFAULTS) && + !(tty->term_flags & TERM_HASDEFAULTS)) + fg = 7; + if (fg == 8) + tty_puts(tty, "\033[39m"); + else + tty_putcode1(tty, TTYC_SETAF, fg); +} + +void +tty_attributes_bg(struct tty *tty, const struct grid_cell *gc) +{ + u_char bg; + + bg = gc->bg; + if (gc->flags & GRID_FLAG_BG256) { + if (tty_try_256(tty, bg, "48") == 0) + return; + if (tty_try_88(tty, bg, "48") == 0) + return; + bg = colour_256to16(bg); + if (bg & 8) + bg &= 7; + } + + if (bg == 8 && + !(tty->term->flags & TERM_HASDEFAULTS) && + !(tty->term_flags & TERM_HASDEFAULTS)) + bg = 0; + if (bg == 8) + tty_puts(tty, "\033[49m"); + else + tty_putcode1(tty, TTYC_SETAB, bg); +} diff --git a/usr.bin/tmux/utf8.c b/usr.bin/tmux/utf8.c new file mode 100644 index 00000000000..63660e8c135 --- /dev/null +++ b/usr.bin/tmux/utf8.c @@ -0,0 +1,317 @@ +/* $OpenBSD: utf8.c,v 1.1 2009/06/01 22:58:49 nicm Exp $ */ + +/* + * Copyright (c) 2008 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 <string.h> + +#include "tmux.h" + +struct utf8_width_entry { + u_int first; + u_int last; + + int width; + + struct utf8_width_entry *left; + struct utf8_width_entry *right; +}; + +/* Random order. Not optimal but it'll do for now... */ +struct utf8_width_entry utf8_width_table[] = { + { 0x00951, 0x00954, 0, NULL, NULL }, + { 0x00ccc, 0x00ccd, 0, NULL, NULL }, + { 0x0fff9, 0x0fffb, 0, NULL, NULL }, + { 0x20000, 0x2fffd, 2, NULL, NULL }, + { 0x00ebb, 0x00ebc, 0, NULL, NULL }, + { 0x01932, 0x01932, 0, NULL, NULL }, + { 0x0070f, 0x0070f, 0, NULL, NULL }, + { 0x00a70, 0x00a71, 0, NULL, NULL }, + { 0x02329, 0x02329, 2, NULL, NULL }, + { 0x00acd, 0x00acd, 0, NULL, NULL }, + { 0x00ac7, 0x00ac8, 0, NULL, NULL }, + { 0x00a3c, 0x00a3c, 0, NULL, NULL }, + { 0x009cd, 0x009cd, 0, NULL, NULL }, + { 0x00591, 0x005bd, 0, NULL, NULL }, + { 0x01058, 0x01059, 0, NULL, NULL }, + { 0x0ffe0, 0x0ffe6, 2, NULL, NULL }, + { 0x01100, 0x0115f, 2, NULL, NULL }, + { 0x0fe20, 0x0fe23, 0, NULL, NULL }, + { 0x0302a, 0x0302f, 0, NULL, NULL }, + { 0x01772, 0x01773, 0, NULL, NULL }, + { 0x005bf, 0x005bf, 0, NULL, NULL }, + { 0x006ea, 0x006ed, 0, NULL, NULL }, + { 0x00bc0, 0x00bc0, 0, NULL, NULL }, + { 0x00962, 0x00963, 0, NULL, NULL }, + { 0x01732, 0x01734, 0, NULL, NULL }, + { 0x00d41, 0x00d43, 0, NULL, NULL }, + { 0x01b42, 0x01b42, 0, NULL, NULL }, + { 0x00a41, 0x00a42, 0, NULL, NULL }, + { 0x00eb4, 0x00eb9, 0, NULL, NULL }, + { 0x00b01, 0x00b01, 0, NULL, NULL }, + { 0x00e34, 0x00e3a, 0, NULL, NULL }, + { 0x03040, 0x03098, 2, NULL, NULL }, + { 0x0093c, 0x0093c, 0, NULL, NULL }, + { 0x00c4a, 0x00c4d, 0, NULL, NULL }, + { 0x01032, 0x01032, 0, NULL, NULL }, + { 0x00f37, 0x00f37, 0, NULL, NULL }, + { 0x00901, 0x00902, 0, NULL, NULL }, + { 0x00cbf, 0x00cbf, 0, NULL, NULL }, + { 0x0a806, 0x0a806, 0, NULL, NULL }, + { 0x00dd2, 0x00dd4, 0, NULL, NULL }, + { 0x00f71, 0x00f7e, 0, NULL, NULL }, + { 0x01752, 0x01753, 0, NULL, NULL }, + { 0x1d242, 0x1d244, 0, NULL, NULL }, + { 0x005c1, 0x005c2, 0, NULL, NULL }, + { 0x0309b, 0x0a4cf, 2, NULL, NULL }, + { 0xe0100, 0xe01ef, 0, NULL, NULL }, + { 0x017dd, 0x017dd, 0, NULL, NULL }, + { 0x00600, 0x00603, 0, NULL, NULL }, + { 0x009e2, 0x009e3, 0, NULL, NULL }, + { 0x00cc6, 0x00cc6, 0, NULL, NULL }, + { 0x0a80b, 0x0a80b, 0, NULL, NULL }, + { 0x01712, 0x01714, 0, NULL, NULL }, + { 0x00b3c, 0x00b3c, 0, NULL, NULL }, + { 0x01b00, 0x01b03, 0, NULL, NULL }, + { 0x007eb, 0x007f3, 0, NULL, NULL }, + { 0xe0001, 0xe0001, 0, NULL, NULL }, + { 0x1d185, 0x1d18b, 0, NULL, NULL }, + { 0x0feff, 0x0feff, 0, NULL, NULL }, + { 0x01b36, 0x01b3a, 0, NULL, NULL }, + { 0x01920, 0x01922, 0, NULL, NULL }, + { 0x00670, 0x00670, 0, NULL, NULL }, + { 0x00f90, 0x00f97, 0, NULL, NULL }, + { 0x01927, 0x01928, 0, NULL, NULL }, + { 0x0200b, 0x0200f, 0, NULL, NULL }, + { 0x0ff00, 0x0ff60, 2, NULL, NULL }, + { 0x0f900, 0x0faff, 2, NULL, NULL }, + { 0x0fb1e, 0x0fb1e, 0, NULL, NULL }, + { 0x00cbc, 0x00cbc, 0, NULL, NULL }, + { 0x00eb1, 0x00eb1, 0, NULL, NULL }, + { 0x10a38, 0x10a3a, 0, NULL, NULL }, + { 0x007a6, 0x007b0, 0, NULL, NULL }, + { 0x00f80, 0x00f84, 0, NULL, NULL }, + { 0x005c4, 0x005c5, 0, NULL, NULL }, + { 0x0ac00, 0x0d7a3, 2, NULL, NULL }, + { 0x017c9, 0x017d3, 0, NULL, NULL }, + { 0x00d4d, 0x00d4d, 0, NULL, NULL }, + { 0x1d167, 0x1d169, 0, NULL, NULL }, + { 0x01036, 0x01037, 0, NULL, NULL }, + { 0xe0020, 0xe007f, 0, NULL, NULL }, + { 0x00f35, 0x00f35, 0, NULL, NULL }, + { 0x017b4, 0x017b5, 0, NULL, NULL }, + { 0x0206a, 0x0206f, 0, NULL, NULL }, + { 0x00c46, 0x00c48, 0, NULL, NULL }, + { 0x01939, 0x0193b, 0, NULL, NULL }, + { 0x01dc0, 0x01dca, 0, NULL, NULL }, + { 0x10a0c, 0x10a0f, 0, NULL, NULL }, + { 0x0102d, 0x01030, 0, NULL, NULL }, + { 0x017c6, 0x017c6, 0, NULL, NULL }, + { 0x00ec8, 0x00ecd, 0, NULL, NULL }, + { 0x00b41, 0x00b43, 0, NULL, NULL }, + { 0x017b7, 0x017bd, 0, NULL, NULL }, + { 0x1d173, 0x1d182, 0, NULL, NULL }, + { 0x00a47, 0x00a48, 0, NULL, NULL }, + { 0x0232a, 0x0232a, 2, NULL, NULL }, + { 0x01b3c, 0x01b3c, 0, NULL, NULL }, + { 0x10a01, 0x10a03, 0, NULL, NULL }, + { 0x00ae2, 0x00ae3, 0, NULL, NULL }, + { 0x00483, 0x00486, 0, NULL, NULL }, + { 0x0135f, 0x0135f, 0, NULL, NULL }, + { 0x01a17, 0x01a18, 0, NULL, NULL }, + { 0x006e7, 0x006e8, 0, NULL, NULL }, + { 0x03099, 0x0309a, 0, NULL, NULL }, + { 0x00b4d, 0x00b4d, 0, NULL, NULL }, + { 0x00ce2, 0x00ce3, 0, NULL, NULL }, + { 0x00bcd, 0x00bcd, 0, NULL, NULL }, + { 0x00610, 0x00615, 0, NULL, NULL }, + { 0x00f99, 0x00fbc, 0, NULL, NULL }, + { 0x009c1, 0x009c4, 0, NULL, NULL }, + { 0x00730, 0x0074a, 0, NULL, NULL }, + { 0x00300, 0x0036f, 0, NULL, NULL }, + { 0x03030, 0x0303e, 2, NULL, NULL }, + { 0x01b34, 0x01b34, 0, NULL, NULL }, + { 0x1d1aa, 0x1d1ad, 0, NULL, NULL }, + { 0x00dca, 0x00dca, 0, NULL, NULL }, + { 0x006d6, 0x006e4, 0, NULL, NULL }, + { 0x00f86, 0x00f87, 0, NULL, NULL }, + { 0x00b3f, 0x00b3f, 0, NULL, NULL }, + { 0x0fe30, 0x0fe6f, 2, NULL, NULL }, + { 0x01039, 0x01039, 0, NULL, NULL }, + { 0x0094d, 0x0094d, 0, NULL, NULL }, + { 0x00c55, 0x00c56, 0, NULL, NULL }, + { 0x00488, 0x00489, 0, NULL, NULL }, + { 0x00e47, 0x00e4e, 0, NULL, NULL }, + { 0x00a81, 0x00a82, 0, NULL, NULL }, + { 0x00ac1, 0x00ac5, 0, NULL, NULL }, + { 0x0202a, 0x0202e, 0, NULL, NULL }, + { 0x00dd6, 0x00dd6, 0, NULL, NULL }, + { 0x018a9, 0x018a9, 0, NULL, NULL }, + { 0x0064b, 0x0065e, 0, NULL, NULL }, + { 0x00abc, 0x00abc, 0, NULL, NULL }, + { 0x00b82, 0x00b82, 0, NULL, NULL }, + { 0x00f39, 0x00f39, 0, NULL, NULL }, + { 0x020d0, 0x020ef, 0, NULL, NULL }, + { 0x01dfe, 0x01dff, 0, NULL, NULL }, + { 0x30000, 0x3fffd, 2, NULL, NULL }, + { 0x00711, 0x00711, 0, NULL, NULL }, + { 0x0fe00, 0x0fe0f, 0, NULL, NULL }, + { 0x01160, 0x011ff, 0, NULL, NULL }, + { 0x0180b, 0x0180d, 0, NULL, NULL }, + { 0x10a3f, 0x10a3f, 0, NULL, NULL }, + { 0x00981, 0x00981, 0, NULL, NULL }, + { 0x0a825, 0x0a826, 0, NULL, NULL }, + { 0x00941, 0x00948, 0, NULL, NULL }, + { 0x01b6b, 0x01b73, 0, NULL, NULL }, + { 0x00e31, 0x00e31, 0, NULL, NULL }, + { 0x0fe10, 0x0fe19, 2, NULL, NULL }, + { 0x00a01, 0x00a02, 0, NULL, NULL }, + { 0x00a4b, 0x00a4d, 0, NULL, NULL }, + { 0x00f18, 0x00f19, 0, NULL, NULL }, + { 0x00fc6, 0x00fc6, 0, NULL, NULL }, + { 0x02e80, 0x03029, 2, NULL, NULL }, + { 0x00b56, 0x00b56, 0, NULL, NULL }, + { 0x009bc, 0x009bc, 0, NULL, NULL }, + { 0x005c7, 0x005c7, 0, NULL, NULL }, + { 0x02060, 0x02063, 0, NULL, NULL }, + { 0x00c3e, 0x00c40, 0, NULL, NULL }, + { 0x10a05, 0x10a06, 0, NULL, NULL }, +}; + +struct utf8_width_entry *utf8_width_root = NULL; + +int utf8_overlap(struct utf8_width_entry *, struct utf8_width_entry *); +void utf8_print(struct utf8_width_entry *, int); +u_int utf8_combine(const u_char *); +void utf8_split(u_int, u_char *); + +int +utf8_overlap( + struct utf8_width_entry *item1, struct utf8_width_entry *item2) +{ + if (item1->first >= item2->first && item1->first <= item2->last) + return (1); + if (item1->last >= item2->first && item1->last <= item2->last) + return (1); + if (item2->first >= item1->first && item2->first <= item1->last) + return (1); + if (item2->last >= item1->first && item2->last <= item1->last) + return (1); + return (0); +} + +void +utf8_build(void) +{ + struct utf8_width_entry **ptr, *item, *node; + u_int i, j; + + for (i = 0; i < nitems(utf8_width_table); i++) { + item = &utf8_width_table[i]; + + for (j = 0; j < nitems(utf8_width_table); j++) { + if (i != j && utf8_overlap(item, &utf8_width_table[j])) + log_fatalx("utf8 overlap: %u %u", i, j); + } + + ptr = &utf8_width_root; + while (*ptr != NULL) { + node = *ptr; + if (item->last < node->first) + ptr = &(node->left); + else if (item->first > node->last) + ptr = &(node->right); + } + *ptr = item; + } +} + +void +utf8_print(struct utf8_width_entry *node, int n) +{ + log_debug("%*s%04x -> %04x", n, " ", node->first, node->last); + if (node->left != NULL) + utf8_print(node->left, n + 1); + if (node->right != NULL) + utf8_print(node->right, n + 1); +} + +u_int +utf8_combine(const u_char *data) +{ + u_int uvalue; + + if (data[1] == 0xff) + uvalue = data[0]; + else if (data[2] == 0xff) { + uvalue = data[1] & 0x3f; + uvalue |= (data[0] & 0x1f) << 6; + } else if (data[3] == 0xff) { + uvalue = data[2] & 0x3f; + uvalue |= (data[1] & 0x3f) << 6; + uvalue |= (data[0] & 0x0f) << 12; + } else { + uvalue = data[3] & 0x3f; + uvalue |= (data[2] & 0x3f) << 6; + uvalue |= (data[1] & 0x3f) << 12; + uvalue |= (data[0] & 0x3f) << 18; + } + return (uvalue); +} + +void +utf8_split(u_int uvalue, u_char *data) +{ + memset(data, 0xff, 4); + + if (uvalue <= 0x7f) + data[0] = uvalue; + else if (uvalue > 0x7f && uvalue <= 0x7ff) { + data[0] = (uvalue >> 6) | 0xc0; + data[1] = (uvalue & 0x3f) | 0x80; + } else if (uvalue > 0x7ff && uvalue <= 0xffff) { + data[0] = (uvalue >> 12) | 0xe0; + data[1] = ((uvalue >> 6) & 0x3f) | 0x80; + data[2] = (uvalue & 0x3f) | 0x80; + } else if (uvalue > 0xffff && uvalue <= 0x10ffff) { + data[0] = (uvalue >> 18) | 0xf0; + data[1] = ((uvalue >> 12) & 0x3f) | 0x80; + data[2] = ((uvalue >> 6) & 0x3f) | 0x80; + data[3] = (uvalue & 0x3f) | 0x80; + } +} + +int +utf8_width(u_char *udata) +{ + struct utf8_width_entry *item; + u_int uvalue; + + uvalue = utf8_combine(udata); + + item = utf8_width_root; + while (item != NULL) { + if (uvalue < item->first) + item = item->left; + else if (uvalue > item->last) + item = item->right; + else + return (item->width); + } + return (1); +} diff --git a/usr.bin/tmux/util.c b/usr.bin/tmux/util.c new file mode 100644 index 00000000000..da0a50cfd06 --- /dev/null +++ b/usr.bin/tmux/util.c @@ -0,0 +1,72 @@ +/* $OpenBSD: util.c,v 1.1 2009/06/01 22:58:49 nicm 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 "tmux.h" + +/* Return a section of a string around a point. */ +char * +section_string(char *buf, size_t len, size_t sectoff, size_t sectlen) +{ + char *s; + size_t first, last; + + if (len <= sectlen) { + first = 0; + last = len; + } else if (sectoff < sectlen / 2) { + first = 0; + last = sectlen; + } else if (sectoff + sectlen / 2 > len) { + last = len; + first = last - sectlen; + } else { + first = sectoff - sectlen / 2; + last = first + sectlen; + } + + if (last - first > 3 && first != 0) + first += 3; + if (last - first > 3 && last != len) + last -= 3; + + xasprintf(&s, "%s%.*s%s", first == 0 ? "" : "...", + (int) (last - first), buf + first, last == len ? "" : "..."); + return (s); +} + +/* Clean string of invisible characters. */ +void +clean_string(const char *in, char *buf, size_t len) +{ + const u_char *cp; + size_t off; + + off = 0; + for (cp = in; *cp != '\0'; cp++) { + if (off >= len) + break; + if (*cp >= 0x20 && *cp <= 0x7f) + buf[off++] = *cp; + else + off += xsnprintf(buf + off, len - off, "\\%03hho", *cp); + } + if (off < len) + buf[off] = '\0'; +} diff --git a/usr.bin/tmux/window-choose.c b/usr.bin/tmux/window-choose.c new file mode 100644 index 00000000000..21c43b27160 --- /dev/null +++ b/usr.bin/tmux/window-choose.c @@ -0,0 +1,361 @@ +/* $OpenBSD: window-choose.c,v 1.1 2009/06/01 22:58:49 nicm 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 <string.h> + +#include "tmux.h" + +struct screen *window_choose_init(struct window_pane *); +void window_choose_free(struct window_pane *); +void window_choose_resize(struct window_pane *, u_int, u_int); +void window_choose_key(struct window_pane *, struct client *, int); +void window_choose_mouse( + struct window_pane *, struct client *, u_char, u_char, u_char); + +void window_choose_redraw_screen(struct window_pane *); +void window_choose_write_line( + struct window_pane *, struct screen_write_ctx *, u_int); + +void window_choose_scroll_up(struct window_pane *); +void window_choose_scroll_down(struct window_pane *); + +const struct window_mode window_choose_mode = { + window_choose_init, + window_choose_free, + window_choose_resize, + window_choose_key, + window_choose_mouse, + NULL, +}; + +struct window_choose_mode_item { + char *name; + int idx; +}; + +struct window_choose_mode_data { + struct screen screen; + + struct mode_key_data mdata; + + ARRAY_DECL(, struct window_choose_mode_item) list; + u_int top; + u_int selected; + + void (*callback)(void *, int); + void *data; +}; + +void +window_choose_vadd(struct window_pane *wp, int idx, const char *fmt, va_list ap) +{ + struct window_choose_mode_data *data = wp->modedata; + struct window_choose_mode_item *item; + + ARRAY_EXPAND(&data->list, 1); + item = &ARRAY_LAST(&data->list); + xvasprintf(&item->name, fmt, ap); + item->idx = idx; +} + +void printflike3 +window_choose_add(struct window_pane *wp, int idx, const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + window_choose_vadd(wp, idx, fmt, ap); + va_end(ap); +} + +void +window_choose_ready(struct window_pane *wp, + u_int cur, void (*callback)(void *, int), void *cdata) +{ + struct window_choose_mode_data *data = wp->modedata; + struct screen *s = &data->screen; + + data->selected = cur; + if (data->selected > screen_size_y(s) - 1) + data->top = ARRAY_LENGTH(&data->list) - screen_size_y(s); + + data->callback = callback; + data->data = cdata; + + window_choose_redraw_screen(wp); +} + +struct screen * +window_choose_init(struct window_pane *wp) +{ + struct window_choose_mode_data *data; + struct screen *s; + + wp->modedata = data = xmalloc(sizeof *data); + data->callback = NULL; + ARRAY_INIT(&data->list); + data->top = 0; + + s = &data->screen; + screen_init(s, screen_size_x(&wp->base), screen_size_y(&wp->base), 0); + s->mode &= ~MODE_CURSOR; + s->mode |= MODE_MOUSE; + + mode_key_init(&data->mdata, + options_get_number(&wp->window->options, "mode-keys"), + MODEKEY_CHOOSEMODE); + + return (s); +} + +void +window_choose_free(struct window_pane *wp) +{ + struct window_choose_mode_data *data = wp->modedata; + u_int i; + + mode_key_free(&data->mdata); + + for (i = 0; i < ARRAY_LENGTH(&data->list); i++) + xfree(ARRAY_ITEM(&data->list, i).name); + ARRAY_FREE(&data->list); + + screen_free(&data->screen); + xfree(data); +} + +void +window_choose_resize(struct window_pane *wp, u_int sx, u_int sy) +{ + struct window_choose_mode_data *data = wp->modedata; + struct screen *s = &data->screen; + + data->top = 0; + if (data->selected > sy - 1) + data->top = data->selected - (sy - 1); + + screen_resize(s, sx, sy); + window_choose_redraw_screen(wp); +} + +void +window_choose_key(struct window_pane *wp, unused struct client *c, int key) +{ + struct window_choose_mode_data *data = wp->modedata; + struct screen *s = &data->screen; + struct screen_write_ctx ctx; + struct window_choose_mode_item *item; + u_int items; + + items = ARRAY_LENGTH(&data->list); + + switch (mode_key_lookup(&data->mdata, key)) { + case MODEKEYCMD_QUIT: + data->callback(data->data, -1); + window_pane_reset_mode(wp); + break; + case MODEKEYCMD_CHOOSE: + item = &ARRAY_ITEM(&data->list, data->selected); + data->callback(data->data, item->idx); + window_pane_reset_mode(wp); + break; + case MODEKEYCMD_UP: + if (items == 0) + break; + if (data->selected == 0) { + data->selected = items - 1; + if (data->selected > screen_size_y(s) - 1) + data->top = items - screen_size_y(s); + window_choose_redraw_screen(wp); + break; + } + data->selected--; + if (data->selected < data->top) + window_choose_scroll_up(wp); + else { + screen_write_start(&ctx, wp, NULL); + window_choose_write_line( + wp, &ctx, data->selected - data->top); + window_choose_write_line( + wp, &ctx, data->selected + 1 - data->top); + screen_write_stop(&ctx); + } + break; + case MODEKEYCMD_DOWN: + if (items == 0) + break; + if (data->selected == items - 1) { + data->selected = 0; + data->top = 0; + window_choose_redraw_screen(wp); + break; + } + data->selected++; + if (data->selected >= data->top + screen_size_y(&data->screen)) + window_choose_scroll_down(wp); + else { + screen_write_start(&ctx, wp, NULL); + window_choose_write_line( + wp, &ctx, data->selected - data->top); + window_choose_write_line( + wp, &ctx, data->selected - 1 - data->top); + screen_write_stop(&ctx); + } + break; + case MODEKEYCMD_PREVIOUSPAGE: + if (data->selected < screen_size_y(s)) { + data->selected = 0; + data->top = 0; + } else { + data->selected -= screen_size_y(s); + if (data->top < screen_size_y(s)) + data->top = 0; + else + data->top -= screen_size_y(s); + } + window_choose_redraw_screen(wp); + break; + case MODEKEYCMD_NEXTPAGE: + data->selected += screen_size_y(s); + if (data->selected > items - 1) + data->selected = items - 1; + data->top += screen_size_y(s); + if (screen_size_y(s) < items) { + if (data->top + screen_size_y(s) > items) + data->top = items - screen_size_y(s); + } else + data->top = 0; + if (data->selected < data->top) + data->top = data->selected; + window_choose_redraw_screen(wp); + break; + default: + break; + } +} + +void +window_choose_mouse(struct window_pane *wp, + unused struct client *c, u_char b, u_char x, u_char y) +{ + struct window_choose_mode_data *data = wp->modedata; + struct screen *s = &data->screen; + struct window_choose_mode_item *item; + u_int idx; + + if ((b & 3) == 3) + return; + if (x >= screen_size_x(s)) + return; + if (y >= screen_size_y(s)) + return; + + idx = data->top + y; + if (idx >= ARRAY_LENGTH(&data->list)) + return; + data->selected = idx; + + item = &ARRAY_ITEM(&data->list, data->selected); + data->callback(data->data, item->idx); + window_pane_reset_mode(wp); +} + +void +window_choose_write_line( + struct window_pane *wp, struct screen_write_ctx *ctx, u_int py) +{ + struct window_choose_mode_data *data = wp->modedata; + struct window_choose_mode_item *item; + struct screen *s = &data->screen; + struct grid_cell gc; + + if (data->callback == NULL) + fatalx("called before callback assigned"); + + memcpy(&gc, &grid_default_cell, sizeof gc); + if (data->selected == data->top + py) { + gc.fg = options_get_number(&wp->window->options, "mode-bg"); + gc.bg = options_get_number(&wp->window->options, "mode-fg"); + gc.attr |= options_get_number(&wp->window->options, "mode-attr"); + } + + screen_write_cursormove(ctx, 0, py); + if (data->top + py < ARRAY_LENGTH(&data->list)) { + item = &ARRAY_ITEM(&data->list, data->top + py); + screen_write_puts( + ctx, &gc, "%.*s", (int) screen_size_x(s), item->name); + } + while (s->cx < screen_size_x(s)) + screen_write_putc(ctx, &gc, ' '); + +} + +void +window_choose_redraw_screen(struct window_pane *wp) +{ + struct window_choose_mode_data *data = wp->modedata; + struct screen *s = &data->screen; + struct screen_write_ctx ctx; + u_int i; + + screen_write_start(&ctx, wp, NULL); + for (i = 0; i < screen_size_y(s); i++) + window_choose_write_line(wp, &ctx, i); + screen_write_stop(&ctx); +} + +void +window_choose_scroll_up(struct window_pane *wp) +{ + struct window_choose_mode_data *data = wp->modedata; + struct screen_write_ctx ctx; + + if (data->top == 0) + return; + data->top--; + + screen_write_start(&ctx, wp, NULL); + screen_write_cursormove(&ctx, 0, 0); + screen_write_insertline(&ctx, 1); + window_choose_write_line(wp, &ctx, 0); + if (screen_size_y(&data->screen) > 1) + window_choose_write_line(wp, &ctx, 1); + screen_write_stop(&ctx); +} + +void +window_choose_scroll_down(struct window_pane *wp) +{ + struct window_choose_mode_data *data = wp->modedata; + struct screen *s = &data->screen; + struct screen_write_ctx ctx; + + if (data->top >= ARRAY_LENGTH(&data->list)) + return; + data->top++; + + screen_write_start(&ctx, wp, NULL); + screen_write_cursormove(&ctx, 0, 0); + screen_write_deleteline(&ctx, 1); + window_choose_write_line(wp, &ctx, screen_size_y(s) - 1); + if (screen_size_y(&data->screen) > 1) + window_choose_write_line(wp, &ctx, screen_size_y(s) - 2); + screen_write_stop(&ctx); +} diff --git a/usr.bin/tmux/window-clock.c b/usr.bin/tmux/window-clock.c new file mode 100644 index 00000000000..1cc748e44d4 --- /dev/null +++ b/usr.bin/tmux/window-clock.c @@ -0,0 +1,124 @@ +/* $OpenBSD: window-clock.c,v 1.1 2009/06/01 22:58:49 nicm 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 <string.h> +#include <time.h> + +#include "tmux.h" + +struct screen *window_clock_init(struct window_pane *); +void window_clock_free(struct window_pane *); +void window_clock_resize(struct window_pane *, u_int, u_int); +void window_clock_key(struct window_pane *, struct client *, int); +void window_clock_timer(struct window_pane *); + +void window_clock_draw_screen(struct window_pane *); + +const struct window_mode window_clock_mode = { + window_clock_init, + window_clock_free, + window_clock_resize, + window_clock_key, + NULL, + window_clock_timer, +}; + +struct window_clock_mode_data { + struct screen screen; + time_t tim; +}; + +struct screen * +window_clock_init(struct window_pane *wp) +{ + struct window_clock_mode_data *data; + struct screen *s; + + wp->modedata = data = xmalloc(sizeof *data); + data->tim = time(NULL); + + s = &data->screen; + screen_init(s, screen_size_x(&wp->base), screen_size_y(&wp->base), 0); + s->mode &= ~MODE_CURSOR; + + window_clock_draw_screen(wp); + + return (s); +} + +void +window_clock_free(struct window_pane *wp) +{ + struct window_clock_mode_data *data = wp->modedata; + + screen_free(&data->screen); + xfree(data); +} + +void +window_clock_resize(struct window_pane *wp, u_int sx, u_int sy) +{ + struct window_clock_mode_data *data = wp->modedata; + struct screen *s = &data->screen; + + screen_resize(s, sx, sy); + window_clock_draw_screen(wp); +} + +void +window_clock_key( + struct window_pane *wp, unused struct client *c, unused int key) +{ + window_pane_reset_mode(wp); +} + +void +window_clock_timer(struct window_pane *wp) +{ + struct window_clock_mode_data *data = wp->modedata; + struct tm *now, *then; + time_t t; + + t = time(NULL); + now = gmtime(&t); + then = gmtime(&data->tim); + if (now->tm_min == then->tm_min) + return; + data->tim = t; + + window_clock_draw_screen(wp); + server_redraw_window(wp->window); +} + +void +window_clock_draw_screen(struct window_pane *wp) +{ + struct window_clock_mode_data *data = wp->modedata; + struct screen_write_ctx ctx; + u_int colour; + int style; + + colour = options_get_number(&wp->window->options, "clock-mode-colour"); + style = options_get_number(&wp->window->options, "clock-mode-style"); + + screen_write_start(&ctx, NULL, &data->screen); + clock_draw(&ctx, colour, style); + screen_write_stop(&ctx); +} diff --git a/usr.bin/tmux/window-copy.c b/usr.bin/tmux/window-copy.c new file mode 100644 index 00000000000..8a291bfc11c --- /dev/null +++ b/usr.bin/tmux/window-copy.c @@ -0,0 +1,968 @@ +/* $OpenBSD: window-copy.c,v 1.1 2009/06/01 22:58:49 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 <string.h> + +#include "tmux.h" + +struct screen *window_copy_init(struct window_pane *); +void window_copy_free(struct window_pane *); +void window_copy_resize(struct window_pane *, u_int, u_int); +void window_copy_key(struct window_pane *, struct client *, int); +void window_copy_mouse( + struct window_pane *, struct client *, u_char, u_char, u_char); + +void window_copy_redraw_lines(struct window_pane *, u_int, u_int); +void window_copy_redraw_screen(struct window_pane *); +void window_copy_write_line( + struct window_pane *, struct screen_write_ctx *, u_int); +void window_copy_write_lines( + struct window_pane *, struct screen_write_ctx *, u_int, u_int); +void window_copy_write_column( + struct window_pane *, struct screen_write_ctx *, u_int); +void window_copy_write_columns( + struct window_pane *, struct screen_write_ctx *, u_int, u_int); + +void window_copy_update_cursor(struct window_pane *); +void window_copy_start_selection(struct window_pane *); +int window_copy_update_selection(struct window_pane *); +void window_copy_copy_selection(struct window_pane *, struct client *); +void window_copy_copy_line( + struct window_pane *, char **, size_t *, u_int, u_int, u_int); +int window_copy_is_space(struct window_pane *, u_int, u_int); +u_int window_copy_find_length(struct window_pane *, u_int); +void window_copy_cursor_start_of_line(struct window_pane *); +void window_copy_cursor_end_of_line(struct window_pane *); +void window_copy_cursor_left(struct window_pane *); +void window_copy_cursor_right(struct window_pane *); +void window_copy_cursor_up(struct window_pane *); +void window_copy_cursor_down(struct window_pane *); +void window_copy_cursor_next_word(struct window_pane *); +void window_copy_cursor_previous_word(struct window_pane *); +void window_copy_scroll_left(struct window_pane *, u_int); +void window_copy_scroll_right(struct window_pane *, u_int); +void window_copy_scroll_up(struct window_pane *, u_int); +void window_copy_scroll_down(struct window_pane *, u_int); + +const struct window_mode window_copy_mode = { + window_copy_init, + window_copy_free, + window_copy_resize, + window_copy_key, + window_copy_mouse, + NULL, +}; + +struct window_copy_mode_data { + struct screen screen; + + struct mode_key_data mdata; + + u_int ox; + u_int oy; + + u_int selx; + u_int sely; + + u_int cx; + u_int cy; +}; + +struct screen * +window_copy_init(struct window_pane *wp) +{ + struct window_copy_mode_data *data; + struct screen *s; + struct screen_write_ctx ctx; + u_int i; + + wp->modedata = data = xmalloc(sizeof *data); + data->ox = 0; + data->oy = 0; + data->cx = wp->base.cx; + data->cy = wp->base.cy; + + s = &data->screen; + screen_init(s, screen_size_x(&wp->base), screen_size_y(&wp->base), 0); + s->mode |= MODE_MOUSE; + + mode_key_init(&data->mdata, + options_get_number(&wp->window->options, "mode-keys"), 0); + + s->cx = data->cx; + s->cy = data->cy; + + screen_write_start(&ctx, NULL, s); + for (i = 0; i < screen_size_y(s); i++) + window_copy_write_line(wp, &ctx, i); + screen_write_cursormove(&ctx, data->cx, data->cy); + screen_write_stop(&ctx); + + return (s); +} + +void +window_copy_free(struct window_pane *wp) +{ + struct window_copy_mode_data *data = wp->modedata; + + mode_key_free(&data->mdata); + + screen_free(&data->screen); + xfree(data); +} + +void +window_copy_pageup(struct window_pane *wp) +{ + struct window_copy_mode_data *data = wp->modedata; + struct screen *s = &data->screen; + + if (data->oy + screen_size_y(s) > screen_hsize(&wp->base)) + data->oy = screen_hsize(&wp->base); + else + data->oy += screen_size_y(s); + window_copy_update_selection(wp); + window_copy_redraw_screen(wp); +} + +void +window_copy_resize(struct window_pane *wp, u_int sx, u_int sy) +{ + struct window_copy_mode_data *data = wp->modedata; + struct screen *s = &data->screen; + struct screen_write_ctx ctx; + + screen_resize(s, sx, sy); + screen_write_start(&ctx, NULL, s); + window_copy_write_lines(wp, &ctx, 0, screen_size_y(s) - 1); + screen_write_stop(&ctx); + window_copy_update_selection(wp); +} + +void +window_copy_key(struct window_pane *wp, struct client *c, int key) +{ + struct window_copy_mode_data *data = wp->modedata; + struct screen *s = &data->screen; + + switch (mode_key_lookup(&data->mdata, key)) { + case MODEKEYCMD_QUIT: + window_pane_reset_mode(wp); + break; + case MODEKEYCMD_LEFT: + window_copy_cursor_left(wp); + return; + case MODEKEYCMD_RIGHT: + window_copy_cursor_right(wp); + return; + case MODEKEYCMD_UP: + window_copy_cursor_up(wp); + return; + case MODEKEYCMD_DOWN: + window_copy_cursor_down(wp); + return; + case MODEKEYCMD_PREVIOUSPAGE: + window_copy_pageup(wp); + break; + case MODEKEYCMD_NEXTPAGE: + if (data->oy < screen_size_y(s)) + data->oy = 0; + else + data->oy -= screen_size_y(s); + window_copy_update_selection(wp); + window_copy_redraw_screen(wp); + break; + case MODEKEYCMD_STARTSELECTION: + window_copy_start_selection(wp); + break; + case MODEKEYCMD_CLEARSELECTION: + screen_clear_selection(&data->screen); + window_copy_redraw_screen(wp); + break; + case MODEKEYCMD_COPYSELECTION: + if (c != NULL && c->session != NULL) { + window_copy_copy_selection(wp, c); + window_pane_reset_mode(wp); + } + break; + case MODEKEYCMD_STARTOFLINE: + window_copy_cursor_start_of_line(wp); + break; + case MODEKEYCMD_ENDOFLINE: + window_copy_cursor_end_of_line(wp); + break; + case MODEKEYCMD_NEXTWORD: + window_copy_cursor_next_word(wp); + break; + case MODEKEYCMD_PREVIOUSWORD: + window_copy_cursor_previous_word(wp); + break; + default: + break; + } +} + +void +window_copy_mouse(struct window_pane *wp, + unused struct client *c, u_char b, u_char x, u_char y) +{ + struct window_copy_mode_data *data = wp->modedata; + struct screen *s = &data->screen; + + if ((b & 3) == 3) + return; + if (x >= screen_size_x(s)) + return; + if (y >= screen_size_y(s)) + return; + + data->cx = x; + data->cy = y; + + if (window_copy_update_selection(wp)) + window_copy_redraw_screen(wp); + window_copy_update_cursor(wp); +} + +void +window_copy_write_line(struct window_pane *wp, struct screen_write_ctx *ctx, u_int py) +{ + struct window_copy_mode_data *data = wp->modedata; + struct screen *s = &data->screen; + struct grid_cell gc; + char hdr[32]; + size_t size; + + if (py == 0) { + memcpy(&gc, &grid_default_cell, sizeof gc); + size = xsnprintf(hdr, sizeof hdr, + "[%u,%u/%u]", data->ox, data->oy, screen_hsize(&wp->base)); + gc.bg = options_get_number(&wp->window->options, "mode-fg"); + gc.fg = options_get_number(&wp->window->options, "mode-bg"); + gc.attr |= options_get_number(&wp->window->options, "mode-attr"); + screen_write_cursormove(ctx, screen_size_x(s) - size, 0); + screen_write_puts(ctx, &gc, "%s", hdr); + } else + size = 0; + + screen_write_cursormove(ctx, 0, py); + screen_write_copy(ctx, &wp->base, data->ox, (screen_hsize(&wp->base) - + data->oy) + py, screen_size_x(s) - size, 1); +} + +void +window_copy_write_lines( + struct window_pane *wp, struct screen_write_ctx *ctx, u_int py, u_int ny) +{ + u_int yy; + + for (yy = py; yy < py + ny; yy++) + window_copy_write_line(wp, ctx, py); +} + +void +window_copy_write_column( + struct window_pane *wp, struct screen_write_ctx *ctx, u_int px) +{ + struct window_copy_mode_data *data = wp->modedata; + struct screen *s = &data->screen; + + screen_write_cursormove(ctx, px, 0); + screen_write_copy(ctx, &wp->base, + data->ox + px, screen_hsize(&wp->base) - data->oy, 1, screen_size_y(s)); +} + +void +window_copy_write_columns( + struct window_pane *wp, struct screen_write_ctx *ctx, u_int px, u_int nx) +{ + u_int xx; + + for (xx = px; xx < px + nx; xx++) + window_copy_write_column(wp, ctx, xx); +} + +void +window_copy_redraw_lines(struct window_pane *wp, u_int py, u_int ny) +{ + struct window_copy_mode_data *data = wp->modedata; + struct screen_write_ctx ctx; + u_int i; + + screen_write_start(&ctx, wp, NULL); + for (i = py; i < py + ny; i++) + window_copy_write_line(wp, &ctx, i); + screen_write_cursormove(&ctx, data->cx, data->cy); + screen_write_stop(&ctx); +} + +void +window_copy_redraw_screen(struct window_pane *wp) +{ + struct window_copy_mode_data *data = wp->modedata; + + window_copy_redraw_lines(wp, 0, screen_size_y(&data->screen)); +} + +void +window_copy_update_cursor(struct window_pane *wp) +{ + struct window_copy_mode_data *data = wp->modedata; + struct screen_write_ctx ctx; + + screen_write_start(&ctx, wp, NULL); + screen_write_cursormove(&ctx, data->cx, data->cy); + screen_write_stop(&ctx); +} + +void +window_copy_start_selection(struct window_pane *wp) +{ + struct window_copy_mode_data *data = wp->modedata; + struct screen *s = &data->screen; + + data->selx = data->cx + data->ox; + data->sely = screen_hsize(&wp->base) + data->cy - data->oy; + + s->sel.flag = 1; + window_copy_update_selection(wp); +} + +int +window_copy_update_selection(struct window_pane *wp) +{ + struct window_copy_mode_data *data = wp->modedata; + struct screen *s = &data->screen; + struct grid_cell gc; + u_int sx, sy, tx, ty; + + if (!s->sel.flag) + return (0); + + /* Set colours. */ + memcpy(&gc, &grid_default_cell, sizeof gc); + gc.bg = options_get_number(&wp->window->options, "mode-fg"); + gc.fg = options_get_number(&wp->window->options, "mode-bg"); + gc.attr |= options_get_number(&wp->window->options, "mode-attr"); + + /* Find top-left of screen. */ + tx = data->ox; + ty = screen_hsize(&wp->base) - data->oy; + + /* Adjust the selection. */ + sx = data->selx; + sy = data->sely; + if (sy < ty) { + /* Above it. */ + sx = 0; + sy = 0; + } else if (sy > ty + screen_size_y(s) - 1) { + /* Below it. */ + sx = screen_size_x(s) - 1; + sy = screen_size_y(s) - 1; + } else if (sx < tx) { + /* To the left. */ + sx = 0; + } else if (sx > tx + screen_size_x(s) - 1) { + /* To the right. */ + sx = 0; + sy++; + if (sy > screen_size_y(s) - 1) + sy = screen_size_y(s) - 1; + } else { + sx -= tx; + sy -= ty; + } + sy = screen_hsize(s) + sy; + + screen_set_selection( + s, sx, sy, data->cx, screen_hsize(s) + data->cy, &gc); + return (1); +} + +void +window_copy_copy_selection(struct window_pane *wp, struct client *c) +{ + struct window_copy_mode_data *data = wp->modedata; + struct screen *s = &data->screen; + char *buf; + size_t off; + u_int i, xx, yy, sx, sy, ex, ey, limit; + + if (!s->sel.flag) + return; + + buf = xmalloc(1); + off = 0; + + *buf = '\0'; + + /* + * The selection extends from selx,sely to (adjusted) cx,cy on + * the base screen. + */ + + /* Find start and end. */ + xx = data->cx + data->ox; + yy = screen_hsize(&wp->base) + data->cy - data->oy; + if (xx < data->selx || (yy == data->sely && xx < data->selx)) { + sx = xx; sy = yy; + ex = data->selx; ey = data->sely; + } else { + sx = data->selx; sy = data->sely; + ex = xx; ey = yy; + } + + /* Trim ex to end of line. */ + xx = window_copy_find_length(wp, ey); + if (ex > xx) + ex = xx; + + /* Copy the lines. */ + if (sy == ey) + window_copy_copy_line(wp, &buf, &off, sy, sx, ex); + else { + xx = window_copy_find_length(wp, sy); + window_copy_copy_line(wp, &buf, &off, sy, sx, xx); + if (ey - sy > 1) { + for (i = sy + 1; i < ey - 1; i++) { + xx = window_copy_find_length(wp, i); + window_copy_copy_line(wp, &buf, &off, i, 0, xx); + } + } + window_copy_copy_line(wp, &buf, &off, ey, 0, ex); + } + + /* Terminate buffer, overwriting final \n. */ + if (off != 0) + buf[off - 1] = '\0'; + + /* Add the buffer to the stack. */ + limit = options_get_number(&c->session->options, "buffer-limit"); + paste_add(&c->session->buffers, buf, limit); +} + +void +window_copy_copy_line(struct window_pane *wp, + char **buf, size_t *off, u_int sy, u_int sx, u_int ex) +{ + const struct grid_cell *gc; + const struct grid_utf8 *gu; + u_int i, j, xx; + + if (sx > ex) + return; + + xx = window_copy_find_length(wp, sy); + if (ex > xx) + ex = xx; + if (sx > xx) + sx = xx; + + if (sx < ex) { + for (i = sx; i < ex; i++) { + gc = grid_peek_cell(wp->base.grid, i, sy); + if (gc->flags & GRID_FLAG_PADDING) + continue; + if (!(gc->flags & GRID_FLAG_UTF8)) { + *buf = xrealloc(*buf, 1, (*off) + 1); + (*buf)[(*off)++] = gc->data; + } else { + gu = grid_peek_utf8(wp->base.grid, i, sy); + *buf = xrealloc(*buf, 1, (*off) + UTF8_SIZE); + for (j = 0; j < UTF8_SIZE; j++) { + if (gu->data[j] == 0xff) + break; + (*buf)[(*off)++] = gu->data[j]; + } + } + } + } + + *buf = xrealloc(*buf, 1, (*off) + 1); + (*buf)[*off] = '\n'; + (*off)++; +} + +int +window_copy_is_space(struct window_pane *wp, u_int px, u_int py) +{ + const struct grid_cell *gc; + const char *spaces = " -_@"; + + gc = grid_peek_cell(wp->base.grid, px, py); + if (gc->flags & (GRID_FLAG_PADDING|GRID_FLAG_UTF8)) + return (0); + if (gc->data == 0x00 || gc->data == 0x7f) + return (0); + return (strchr(spaces, gc->data) != NULL); +} + +u_int +window_copy_find_length(struct window_pane *wp, u_int py) +{ + const struct grid_cell *gc; + u_int px; + + px = wp->base.grid->size[py]; + while (px > 0) { + gc = grid_peek_cell(wp->base.grid, px - 1, py); + if (gc->flags & GRID_FLAG_UTF8) + break; + if (gc->data != ' ') + break; + px--; + } + return (px); +} + +void +window_copy_cursor_start_of_line(struct window_pane *wp) +{ + struct window_copy_mode_data *data = wp->modedata; + + if (data->ox != 0) + window_copy_scroll_right(wp, data->ox); + data->cx = 0; + + if (window_copy_update_selection(wp)) + window_copy_redraw_lines(wp, data->cy, 1); + else + window_copy_update_cursor(wp); +} + +void +window_copy_cursor_end_of_line(struct window_pane *wp) +{ + struct window_copy_mode_data *data = wp->modedata; + struct screen *s = &data->screen; + u_int px, py; + + py = screen_hsize(&wp->base) + data->cy - data->oy; + px = window_copy_find_length(wp, py); + + /* On screen. */ + if (px > data->ox && px <= data->ox + screen_size_x(s) - 1) + data->cx = px - data->ox; + + /* Off right of screen. */ + if (px > data->ox + screen_size_x(s) - 1) { + /* Move cursor to last and scroll screen. */ + window_copy_scroll_left( + wp, px - data->ox - (screen_size_x(s) - 1)); + data->cx = screen_size_x(s) - 1; + } + + /* Off left of screen. */ + if (px <= data->ox) { + if (px < screen_size_x(s) - 1) { + /* Short enough to fit on screen. */ + window_copy_scroll_right(wp, data->ox); + data->cx = px; + } else { + /* Too long to fit on screen. */ + window_copy_scroll_right( + wp, data->ox - (px - (screen_size_x(s) - 1))); + data->cx = screen_size_x(s) - 1; + } + } + + if (window_copy_update_selection(wp)) + window_copy_redraw_lines(wp, data->cy, 1); + else + window_copy_update_cursor(wp); +} + +void +window_copy_cursor_left(struct window_pane *wp) +{ + struct window_copy_mode_data *data = wp->modedata; + + if (data->cx == 0) { + if (data->ox > 0) + window_copy_scroll_right(wp, 1); + else { + window_copy_cursor_up(wp); + window_copy_cursor_end_of_line(wp); + } + } else { + data->cx--; + if (window_copy_update_selection(wp)) + window_copy_redraw_lines(wp, data->cy, 1); + else + window_copy_update_cursor(wp); + } +} + +void +window_copy_cursor_right(struct window_pane *wp) +{ + struct window_copy_mode_data *data = wp->modedata; + u_int px, py; + + py = screen_hsize(&wp->base) + data->cy - data->oy; + px = window_copy_find_length(wp, py); + + if (data->cx >= px) { + window_copy_cursor_start_of_line(wp); + window_copy_cursor_down(wp); + } else { + data->cx++; + if (window_copy_update_selection(wp)) + window_copy_redraw_lines(wp, data->cy, 1); + else + window_copy_update_cursor(wp); + } +} + +void +window_copy_cursor_up(struct window_pane *wp) +{ + struct window_copy_mode_data *data = wp->modedata; + u_int ox, oy, px, py; + + oy = screen_hsize(&wp->base) + data->cy - data->oy; + ox = window_copy_find_length(wp, oy); + + if (data->cy == 0) + window_copy_scroll_down(wp, 1); + else { + data->cy--; + if (window_copy_update_selection(wp)) + window_copy_redraw_lines(wp, data->cy, 2); + else + window_copy_update_cursor(wp); + } + + py = screen_hsize(&wp->base) + data->cy - data->oy; + px = window_copy_find_length(wp, py); + + if (data->cx + data->ox >= px || data->cx + data->ox >= ox) + window_copy_cursor_end_of_line(wp); +} + +void +window_copy_cursor_down(struct window_pane *wp) +{ + struct window_copy_mode_data *data = wp->modedata; + struct screen *s = &data->screen; + u_int ox, oy, px, py; + + oy = screen_hsize(&wp->base) + data->cy - data->oy; + ox = window_copy_find_length(wp, oy); + + if (data->cy == screen_size_y(s) - 1) + window_copy_scroll_up(wp, 1); + else { + data->cy++; + if (window_copy_update_selection(wp)) + window_copy_redraw_lines(wp, data->cy - 1, 2); + else + window_copy_update_cursor(wp); + } + + py = screen_hsize(&wp->base) + data->cy - data->oy; + px = window_copy_find_length(wp, py); + + if (data->cx + data->ox >= px || data->cx + data->ox >= ox) + window_copy_cursor_end_of_line(wp); +} + +void +window_copy_cursor_next_word(struct window_pane *wp) +{ + struct window_copy_mode_data *data = wp->modedata; + struct screen *s = &data->screen; + u_int px, py, xx, skip; + + px = data->ox + data->cx; + py = screen_hsize(&wp->base) + data->cy - data->oy; + xx = window_copy_find_length(wp, py); + + skip = 1; + if (px < xx) { + /* If currently on a space, skip space. */ + if (window_copy_is_space(wp, px, py)) + skip = 0; + } else + skip = 0; + for (;;) { + if (px >= xx) { + if (skip) { + px = xx; + break; + } + + while (px >= xx) { + if (data->cy == screen_size_y(s) - 1) { + if (data->oy == 0) + goto out; + } + + px = 0; + window_copy_cursor_down(wp); + + py =screen_hsize( + &wp->base) + data->cy - data->oy; + xx = window_copy_find_length(wp, py); + } + } + + if (skip) { + /* Currently skipping non-space (until space). */ + if (window_copy_is_space(wp, px, py)) + break; + } else { + /* Currently skipping space (until non-space). */ + if (!window_copy_is_space(wp, px, py)) + skip = 1; + } + + px++; + } +out: + + /* On screen. */ + if (px > data->ox && px <= data->ox + screen_size_x(s) - 1) + data->cx = px - data->ox; + + /* Off right of screen. */ + if (px > data->ox + screen_size_x(s) - 1) { + /* Move cursor to last and scroll screen. */ + window_copy_scroll_left( + wp, px - data->ox - (screen_size_x(s) - 1)); + data->cx = screen_size_x(s) - 1; + } + + /* Off left of screen. */ + if (px <= data->ox) { + if (px < screen_size_x(s) - 1) { + /* Short enough to fit on screen. */ + window_copy_scroll_right(wp, data->ox); + data->cx = px; + } else { + /* Too long to fit on screen. */ + window_copy_scroll_right( + wp, data->ox - (px - (screen_size_x(s) - 1))); + data->cx = screen_size_x(s) - 1; + } + } + + if (window_copy_update_selection(wp)) + window_copy_redraw_lines(wp, data->cy, 1); + else + window_copy_update_cursor(wp); +} + +void +window_copy_cursor_previous_word(struct window_pane *wp) +{ + struct window_copy_mode_data *data = wp->modedata; + struct screen *s = &data->screen; + u_int ox, px, py, skip; + + ox = px = data->ox + data->cx; + py = screen_hsize(&wp->base) + data->cy - data->oy; + + skip = 1; + if (px != 0) { + /* If currently on a space, skip space. */ + if (window_copy_is_space(wp, px - 1, py)) + skip = 0; + } + for (;;) { + if (px == 0) { + if (ox != 0) + break; + + while (px == 0) { + if (data->cy == 0 && + (screen_hsize(&wp->base) == 0 || + data->oy >= screen_hsize(&wp->base) - 1)) + goto out; + + window_copy_cursor_up(wp); + + py = screen_hsize( + &wp->base) + data->cy - data->oy; + px = window_copy_find_length(wp, py); + } + goto out; + } + + if (skip) { + /* Currently skipping non-space (until space). */ + if (window_copy_is_space(wp, px - 1, py)) + skip = 0; + } else { + /* Currently skipping space (until non-space). */ + if (!window_copy_is_space(wp, px - 1, py)) + break; + } + + px--; + } +out: + + /* On screen. */ + if (px > data->ox && px <= data->ox + screen_size_x(s) - 1) + data->cx = px - data->ox; + + /* Off right of screen. */ + if (px > data->ox + screen_size_x(s) - 1) { + /* Move cursor to last and scroll screen. */ + window_copy_scroll_left( + wp, px - data->ox - (screen_size_x(s) - 1)); + data->cx = screen_size_x(s) - 1; + } + + /* Off left of screen. */ + if (px <= data->ox) { + if (px < screen_size_x(s) - 1) { + /* Short enough to fit on screen. */ + window_copy_scroll_right(wp, data->ox); + data->cx = px; + } else { + /* Too long to fit on screen. */ + window_copy_scroll_right( + wp, data->ox - (px - (screen_size_x(s) - 1))); + data->cx = screen_size_x(s) - 1; + } + } + + if (window_copy_update_selection(wp)) + window_copy_redraw_lines(wp, data->cy, 1); + else + window_copy_update_cursor(wp); +} + +void +window_copy_scroll_left(struct window_pane *wp, u_int nx) +{ + struct window_copy_mode_data *data = wp->modedata; + struct screen *s = &data->screen; + struct screen_write_ctx ctx; + u_int i; + + if (data->ox > SHRT_MAX - nx) + nx = SHRT_MAX - data->ox; + if (nx == 0) + return; + data->ox += nx; + window_copy_update_selection(wp); + + screen_write_start(&ctx, wp, NULL); + for (i = 1; i < screen_size_y(s); i++) { + screen_write_cursormove(&ctx, 0, i); + screen_write_deletecharacter(&ctx, nx); + } + window_copy_write_columns(wp, &ctx, screen_size_x(s) - nx, nx); + window_copy_write_line(wp, &ctx, 0); + if (s->sel.flag) { + window_copy_update_selection(wp); + window_copy_write_lines(wp, &ctx, data->cy, 1); + } + screen_write_cursormove(&ctx, data->cx, data->cy); + screen_write_stop(&ctx); +} + +void +window_copy_scroll_right(struct window_pane *wp, u_int nx) +{ + struct window_copy_mode_data *data = wp->modedata; + struct screen *s = &data->screen; + struct screen_write_ctx ctx; + u_int i; + + if (data->ox < nx) + nx = data->ox; + if (nx == 0) + return; + data->ox -= nx; + window_copy_update_selection(wp); + + screen_write_start(&ctx, wp, NULL); + for (i = 1; i < screen_size_y(s); i++) { + screen_write_cursormove(&ctx, 0, i); + screen_write_insertcharacter(&ctx, nx); + } + window_copy_write_columns(wp, &ctx, 0, nx); + window_copy_write_line(wp, &ctx, 0); + if (s->sel.flag) + window_copy_write_line(wp, &ctx, data->cy); + screen_write_cursormove(&ctx, data->cx, data->cy); + screen_write_stop(&ctx); +} + +void +window_copy_scroll_up(struct window_pane *wp, u_int ny) +{ + struct window_copy_mode_data *data = wp->modedata; + struct screen *s = &data->screen; + struct screen_write_ctx ctx; + + if (data->oy < ny) + ny = data->oy; + if (ny == 0) + return; + data->oy -= ny; + window_copy_update_selection(wp); + + screen_write_start(&ctx, wp, NULL); + screen_write_cursormove(&ctx, 0, 0); + screen_write_deleteline(&ctx, ny); + window_copy_write_lines(wp, &ctx, screen_size_y(s) - ny, ny); + window_copy_write_line(wp, &ctx, 0); + window_copy_write_line(wp, &ctx, 1); + if (s->sel.flag && screen_size_y(s) > ny) + window_copy_write_line(wp, &ctx, screen_size_y(s) - ny - 1); + screen_write_cursormove(&ctx, data->cx, data->cy); + screen_write_stop(&ctx); +} + +void +window_copy_scroll_down(struct window_pane *wp, u_int ny) +{ + struct window_copy_mode_data *data = wp->modedata; + struct screen *s = &data->screen; + struct screen_write_ctx ctx; + + if (ny > screen_hsize(&wp->base)) + return; + + if (data->oy > screen_hsize(&wp->base) - ny) + ny = screen_hsize(&wp->base) - data->oy; + if (ny == 0) + return; + data->oy += ny; + window_copy_update_selection(wp); + + screen_write_start(&ctx, wp, NULL); + screen_write_cursormove(&ctx, 0, 0); + screen_write_insertline(&ctx, ny); + window_copy_write_lines(wp, &ctx, 0, ny); + if (s->sel.flag && screen_size_y(s) > ny) + window_copy_write_line(wp, &ctx, ny); + else if (ny == 1) /* nuke position */ + window_copy_write_line(wp, &ctx, 1); + screen_write_cursormove(&ctx, data->cx, data->cy); + screen_write_stop(&ctx); +} diff --git a/usr.bin/tmux/window-more.c b/usr.bin/tmux/window-more.c new file mode 100644 index 00000000000..3f75f278252 --- /dev/null +++ b/usr.bin/tmux/window-more.c @@ -0,0 +1,252 @@ +/* $OpenBSD: window-more.c,v 1.1 2009/06/01 22:58:49 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 <string.h> + +#include "tmux.h" + +struct screen *window_more_init(struct window_pane *); +void window_more_free(struct window_pane *); +void window_more_resize(struct window_pane *, u_int, u_int); +void window_more_key(struct window_pane *, struct client *, int); + +void window_more_redraw_screen(struct window_pane *); +void window_more_write_line( + struct window_pane *, struct screen_write_ctx *, u_int); + +void window_more_scroll_up(struct window_pane *); +void window_more_scroll_down(struct window_pane *); + +const struct window_mode window_more_mode = { + window_more_init, + window_more_free, + window_more_resize, + window_more_key, + NULL, + NULL, +}; + +struct window_more_mode_data { + struct screen screen; + + struct mode_key_data mdata; + + ARRAY_DECL(, char *) list; + u_int top; +}; + +void +window_more_vadd(struct window_pane *wp, const char *fmt, va_list ap) +{ + struct window_more_mode_data *data = wp->modedata; + struct screen *s = &data->screen; + struct screen_write_ctx ctx; + char *msg; + u_int size; + + xvasprintf(&msg, fmt, ap); + ARRAY_ADD(&data->list, msg); + + screen_write_start(&ctx, wp, NULL); + size = ARRAY_LENGTH(&data->list) - 1; + if (size >= data->top && size <= data->top + screen_size_y(s) - 1) { + window_more_write_line(wp, &ctx, size - data->top); + if (size != data->top) + window_more_write_line(wp, &ctx, 0); + } else + window_more_write_line(wp, &ctx, 0); + screen_write_stop(&ctx); +} + +void printflike2 +window_more_add(struct window_pane *wp, const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + window_more_vadd(wp, fmt, ap); + va_end(ap); +} + +struct screen * +window_more_init(struct window_pane *wp) +{ + struct window_more_mode_data *data; + struct screen *s; + + wp->modedata = data = xmalloc(sizeof *data); + ARRAY_INIT(&data->list); + data->top = 0; + + s = &data->screen; + screen_init(s, screen_size_x(&wp->base), screen_size_y(&wp->base), 0); + s->mode &= ~MODE_CURSOR; + + mode_key_init(&data->mdata, + options_get_number(&wp->window->options, "mode-keys"), 0); + + return (s); +} + +void +window_more_free(struct window_pane *wp) +{ + struct window_more_mode_data *data = wp->modedata; + u_int i; + + mode_key_free(&data->mdata); + + for (i = 0; i < ARRAY_LENGTH(&data->list); i++) + xfree(ARRAY_ITEM(&data->list, i)); + ARRAY_FREE(&data->list); + + screen_free(&data->screen); + xfree(data); +} + +void +window_more_resize(struct window_pane *wp, u_int sx, u_int sy) +{ + struct window_more_mode_data *data = wp->modedata; + struct screen *s = &data->screen; + + screen_resize(s, sx, sy); + window_more_redraw_screen(wp); +} + +void +window_more_key(struct window_pane *wp, unused struct client *c, int key) +{ + struct window_more_mode_data *data = wp->modedata; + struct screen *s = &data->screen; + + switch (mode_key_lookup(&data->mdata, key)) { + case MODEKEYCMD_QUIT: + window_pane_reset_mode(wp); + break; + case MODEKEYCMD_UP: + window_more_scroll_up(wp); + break; + case MODEKEYCMD_DOWN: + window_more_scroll_down(wp); + break; + case MODEKEYCMD_PREVIOUSPAGE: + if (data->top < screen_size_y(s)) + data->top = 0; + else + data->top -= screen_size_y(s); + window_more_redraw_screen(wp); + break; + case MODEKEYCMD_NEXTPAGE: + if (data->top + screen_size_y(s) > ARRAY_LENGTH(&data->list)) + data->top = ARRAY_LENGTH(&data->list); + else + data->top += screen_size_y(s); + window_more_redraw_screen(wp); + break; + default: + break; + } +} + +void +window_more_write_line( + struct window_pane *wp, struct screen_write_ctx *ctx, u_int py) +{ + struct window_more_mode_data *data = wp->modedata; + struct screen *s = &data->screen; + struct grid_cell gc; + char *msg, hdr[32]; + size_t size; + + memcpy(&gc, &grid_default_cell, sizeof gc); + + if (py == 0) { + size = xsnprintf(hdr, sizeof hdr, + "[%u/%u]", data->top, ARRAY_LENGTH(&data->list)); + screen_write_cursormove(ctx, screen_size_x(s) - size, 0); + gc.bg = options_get_number(&wp->window->options, "mode-fg"); + gc.fg = options_get_number(&wp->window->options, "mode-bg"); + gc.attr |= options_get_number(&wp->window->options, "mode-attr"); + screen_write_puts(ctx, &gc, "%s", hdr); + memcpy(&gc, &grid_default_cell, sizeof gc); + } else + size = 0; + + screen_write_cursormove(ctx, 0, py); + if (data->top + py < ARRAY_LENGTH(&data->list)) { + msg = ARRAY_ITEM(&data->list, data->top + py); + screen_write_puts( + ctx, &gc, "%.*s", (int) (screen_size_x(s) - size), msg); + } + while (s->cx < screen_size_x(s) - size) + screen_write_putc(ctx, &gc, ' '); +} + +void +window_more_redraw_screen(struct window_pane *wp) +{ + struct window_more_mode_data *data = wp->modedata; + struct screen *s = &data->screen; + struct screen_write_ctx ctx; + u_int i; + + screen_write_start(&ctx, wp, NULL); + for (i = 0; i < screen_size_y(s); i++) + window_more_write_line(wp, &ctx, i); + screen_write_stop(&ctx); +} + +void +window_more_scroll_up(struct window_pane *wp) +{ + struct window_more_mode_data *data = wp->modedata; + struct screen_write_ctx ctx; + + if (data->top == 0) + return; + data->top--; + + screen_write_start(&ctx, wp, NULL); + screen_write_cursormove(&ctx, 0, 0); + screen_write_insertline(&ctx, 1); + window_more_write_line(wp, &ctx, 0); + window_more_write_line(wp, &ctx, 1); + screen_write_stop(&ctx); +} + +void +window_more_scroll_down(struct window_pane *wp) +{ + struct window_more_mode_data *data = wp->modedata; + struct screen *s = &data->screen; + struct screen_write_ctx ctx; + + if (data->top >= ARRAY_LENGTH(&data->list)) + return; + data->top++; + + screen_write_start(&ctx, wp, NULL); + screen_write_cursormove(&ctx, 0, 0); + screen_write_deleteline(&ctx, 1); + window_more_write_line(wp, &ctx, screen_size_y(s) - 1); + window_more_write_line(wp, &ctx, 0); + screen_write_stop(&ctx); +} diff --git a/usr.bin/tmux/window-scroll.c b/usr.bin/tmux/window-scroll.c new file mode 100644 index 00000000000..14d01e3637b --- /dev/null +++ b/usr.bin/tmux/window-scroll.c @@ -0,0 +1,296 @@ +/* $OpenBSD: window-scroll.c,v 1.1 2009/06/01 22:58:49 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 <string.h> + +#include "tmux.h" + +struct screen *window_scroll_init(struct window_pane *); +void window_scroll_free(struct window_pane *); +void window_scroll_resize(struct window_pane *, u_int, u_int); +void window_scroll_key(struct window_pane *, struct client *, int); + +void window_scroll_redraw_screen(struct window_pane *); +void window_scroll_write_line( + struct window_pane *, struct screen_write_ctx *, u_int); +void window_scroll_write_column( + struct window_pane *, struct screen_write_ctx *, u_int); + +void window_scroll_scroll_up(struct window_pane *); +void window_scroll_scroll_down(struct window_pane *); +void window_scroll_scroll_left(struct window_pane *); +void window_scroll_scroll_right(struct window_pane *); + +const struct window_mode window_scroll_mode = { + window_scroll_init, + window_scroll_free, + window_scroll_resize, + window_scroll_key, + NULL, + NULL, +}; + +struct window_scroll_mode_data { + struct screen screen; + + struct mode_key_data mdata; + + u_int ox; + u_int oy; +}; + +struct screen * +window_scroll_init(struct window_pane *wp) +{ + struct window_scroll_mode_data *data; + struct screen *s; + struct screen_write_ctx ctx; + u_int i; + + wp->modedata = data = xmalloc(sizeof *data); + data->ox = 0; + data->oy = 0; + + s = &data->screen; + screen_init(s, screen_size_x(&wp->base), screen_size_y(&wp->base), 0); + s->mode &= ~MODE_CURSOR; + + mode_key_init(&data->mdata, + options_get_number(&wp->window->options, "mode-keys"), 0); + + screen_write_start(&ctx, NULL, s); + for (i = 0; i < screen_size_y(s); i++) + window_scroll_write_line(wp, &ctx, i); + screen_write_stop(&ctx); + + return (s); +} + +void +window_scroll_free(struct window_pane *wp) +{ + struct window_scroll_mode_data *data = wp->modedata; + + mode_key_free(&data->mdata); + + screen_free(&data->screen); + xfree(data); +} + +void +window_scroll_pageup(struct window_pane *wp) +{ + struct window_scroll_mode_data *data = wp->modedata; + struct screen *s = &data->screen; + + if (data->oy + screen_size_y(s) > screen_hsize(&wp->base)) + data->oy = screen_hsize(&wp->base); + else + data->oy += screen_size_y(s); + + window_scroll_redraw_screen(wp); +} + +void +window_scroll_resize(struct window_pane *wp, u_int sx, u_int sy) +{ + struct window_scroll_mode_data *data = wp->modedata; + struct screen *s = &data->screen; + struct screen_write_ctx ctx; + u_int i; + + screen_resize(s, sx, sy); + screen_write_start(&ctx, NULL, s); + for (i = 0; i < screen_size_y(s); i++) + window_scroll_write_line(wp, &ctx, i); + screen_write_stop(&ctx); +} + +void +window_scroll_key(struct window_pane *wp, unused struct client *c, int key) +{ + struct window_scroll_mode_data *data = wp->modedata; + struct screen *s = &data->screen; + + switch (mode_key_lookup(&data->mdata, key)) { + case MODEKEYCMD_QUIT: + window_pane_reset_mode(wp); + break; + case MODEKEYCMD_LEFT: + window_scroll_scroll_left(wp); + break; + case MODEKEYCMD_RIGHT: + window_scroll_scroll_right(wp); + break; + case MODEKEYCMD_UP: + window_scroll_scroll_up(wp); + break; + case MODEKEYCMD_DOWN: + window_scroll_scroll_down(wp); + break; + case MODEKEYCMD_PREVIOUSPAGE: + window_scroll_pageup(wp); + break; + case MODEKEYCMD_NEXTPAGE: + if (data->oy < screen_size_y(s)) + data->oy = 0; + else + data->oy -= screen_size_y(s); + window_scroll_redraw_screen(wp); + break; + default: + break; + } +} + +void +window_scroll_write_line( + struct window_pane *wp, struct screen_write_ctx *ctx, u_int py) +{ + struct window_scroll_mode_data *data = wp->modedata; + struct screen *s = &data->screen; + struct grid_cell gc; + char hdr[32]; + size_t size; + + if (py == 0) { + memcpy(&gc, &grid_default_cell, sizeof gc); + size = xsnprintf(hdr, sizeof hdr, + "[%u,%u/%u]", data->ox, data->oy, screen_hsize(&wp->base)); + gc.bg = options_get_number(&wp->window->options, "mode-fg"); + gc.fg = options_get_number(&wp->window->options, "mode-bg"); + gc.attr |= options_get_number(&wp->window->options, "mode-attr"); + screen_write_cursormove(ctx, screen_size_x(s) - size, 0); + screen_write_puts(ctx, &gc, "%s", hdr); + memcpy(&gc, &grid_default_cell, sizeof gc); + } else + size = 0; + + screen_write_cursormove(ctx, 0, py); + screen_write_copy(ctx, &wp->base, data->ox, (screen_hsize(&wp->base) - + data->oy) + py, screen_size_x(s) - size, 1); +} + +void +window_scroll_write_column( + struct window_pane *wp, struct screen_write_ctx *ctx, u_int px) +{ + struct window_scroll_mode_data *data = wp->modedata; + struct screen *s = &data->screen; + + screen_write_cursormove(ctx, px, 0); + screen_write_copy(ctx, &wp->base, data->ox + px, + screen_hsize(&wp->base) - data->oy, 1, screen_size_y(s)); +} + +void +window_scroll_redraw_screen(struct window_pane *wp) +{ + struct window_scroll_mode_data *data = wp->modedata; + struct screen *s = &data->screen; + struct screen_write_ctx ctx; + u_int i; + + screen_write_start(&ctx, wp, NULL); + for (i = 0; i < screen_size_y(s); i++) + window_scroll_write_line(wp, &ctx, i); + screen_write_stop(&ctx); +} + +void +window_scroll_scroll_up(struct window_pane *wp) +{ + struct window_scroll_mode_data *data = wp->modedata; + struct screen_write_ctx ctx; + + if (data->oy >= screen_hsize(&wp->base)) + return; + data->oy++; + + screen_write_start(&ctx, wp, NULL); + screen_write_cursormove(&ctx, 0, 0); + screen_write_insertline(&ctx, 1); + window_scroll_write_line(wp, &ctx, 0); + window_scroll_write_line(wp, &ctx, 1); + screen_write_stop(&ctx); +} + +void +window_scroll_scroll_down(struct window_pane *wp) +{ + struct window_scroll_mode_data *data = wp->modedata; + struct screen *s = &data->screen; + struct screen_write_ctx ctx; + + if (data->oy == 0) + return; + data->oy--; + + screen_write_start(&ctx, wp, NULL); + screen_write_cursormove(&ctx, 0, 0); + screen_write_deleteline(&ctx, 1); + window_scroll_write_line(wp, &ctx, screen_size_y(s) - 1); + window_scroll_write_line(wp, &ctx, 0); + screen_write_stop(&ctx); +} + +void +window_scroll_scroll_right(struct window_pane *wp) +{ + struct window_scroll_mode_data *data = wp->modedata; + struct screen *s = &data->screen; + struct screen_write_ctx ctx; + u_int i; + + if (data->ox >= SHRT_MAX) + return; + data->ox++; + + screen_write_start(&ctx, wp, NULL); + for (i = 1; i < screen_size_y(s); i++) { + screen_write_cursormove(&ctx, 0, i); + screen_write_deletecharacter(&ctx, 1); + } + window_scroll_write_column(wp, &ctx, screen_size_x(s) - 1); + window_scroll_write_line(wp, &ctx, 0); + screen_write_stop(&ctx); +} + +void +window_scroll_scroll_left(struct window_pane *wp) +{ + struct window_scroll_mode_data *data = wp->modedata; + struct screen *s = &data->screen; + struct screen_write_ctx ctx; + u_int i; + + if (data->ox == 0) + return; + data->ox--; + + screen_write_start(&ctx, wp, NULL); + for (i = 1; i < screen_size_y(s); i++) { + screen_write_cursormove(&ctx, 0, i); + screen_write_insertcharacter(&ctx, 1); + } + window_scroll_write_column(wp, &ctx, 0); + window_scroll_write_line(wp, &ctx, 0); + screen_write_stop(&ctx); +} diff --git a/usr.bin/tmux/window.c b/usr.bin/tmux/window.c new file mode 100644 index 00000000000..0f328c62b1e --- /dev/null +++ b/usr.bin/tmux/window.c @@ -0,0 +1,625 @@ +/* $OpenBSD: window.c,v 1.1 2009/06/01 22:58:49 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 <errno.h> +#include <fcntl.h> +#include <paths.h> +#include <signal.h> +#include <stdint.h> +#include <stdlib.h> +#include <string.h> +#include <termios.h> +#include <unistd.h> +#include <util.h> + +#include "tmux.h" + +/* + * Each window is attached to one or two panes, each of which is a pty. This + * file contains code to handle them. + * + * A pane has two buffers attached, these are filled and emptied by the main + * server poll loop. Output data is received from pty's in screen format, + * translated and returned as a series of escape sequences and strings via + * input_parse (in input.c). Input data is received as key codes and written + * directly via input_key. + * + * Each pane also has a "virtual" screen (screen.c) which contains the current + * state and is redisplayed when the window is reattached to a client. + * + * 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; + +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"); +} + +u_int +winlink_count(struct winlinks *wwl) +{ + struct winlink *wl; + u_int n; + + n = 0; + RB_FOREACH(wl, winlinks, wwl) + n++; + + return (n); +} + +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(unused struct winlinks *wwl, struct winlink *wl) +{ + return (RB_PREV(winlinks, wwl, wl)); +} + +void +winlink_stack_push(struct winlink_stack *stack, struct winlink *wl) +{ + if (wl == NULL) + return; + + winlink_stack_remove(stack, wl); + SLIST_INSERT_HEAD(stack, wl, sentry); +} + +void +winlink_stack_remove(struct winlink_stack *stack, struct winlink *wl) +{ + struct winlink *wl2; + + if (wl == NULL) + return; + + SLIST_FOREACH(wl2, stack, sentry) { + if (wl2 == wl) { + SLIST_REMOVE(stack, wl, winlink, sentry); + return; + } + } +} + +int +window_index(struct window *s, u_int *i) +{ + for (*i = 0; *i < ARRAY_LENGTH(&windows); (*i)++) { + if (s == ARRAY_ITEM(&windows, *i)) + return (0); + } + return (-1); +} + +struct window * +window_create1(u_int sx, u_int sy) +{ + struct window *w; + u_int i; + + w = xmalloc(sizeof *w); + w->name = NULL; + w->flags = 0; + + TAILQ_INIT(&w->panes); + w->active = NULL; + w->layout = 0; + + w->sx = sx; + w->sy = sy; + + options_init(&w->options, &global_window_options); + + for (i = 0; i < ARRAY_LENGTH(&windows); i++) { + if (ARRAY_ITEM(&windows, i) == NULL) { + ARRAY_SET(&windows, i, w); + break; + } + } + if (i == ARRAY_LENGTH(&windows)) + ARRAY_ADD(&windows, w); + w->references = 0; + + return (w); +} + +struct window * +window_create(const char *name, const char *cmd, const char *cwd, + const char **envp, u_int sx, u_int sy, u_int hlimit, char **cause) +{ + struct window *w; + + w = window_create1(sx, sy); + if (window_add_pane(w, -1, cmd, cwd, envp, hlimit, cause) == NULL) { + window_destroy(w); + return (NULL); + } + w->active = TAILQ_FIRST(&w->panes); + + if (name != NULL) { + w->name = xstrdup(name); + options_set_number(&w->options, "automatic-rename", 0); + } else + w->name = default_window_name(w); + return (w); +} + +void +window_destroy(struct window *w) +{ + u_int i; + + if (window_index(w, &i) != 0) + fatalx("index not found"); + ARRAY_SET(&windows, i, NULL); + while (!ARRAY_EMPTY(&windows) && ARRAY_LAST(&windows) == NULL) + ARRAY_TRUNC(&windows, 1); + + options_free(&w->options); + + window_destroy_panes(w); + + if (w->name != NULL) + xfree(w->name); + xfree(w); +} + +int +window_resize(struct window *w, u_int sx, u_int sy) +{ + w->sx = sx; + w->sy = sy; + + return (0); +} + +void +window_set_active_pane(struct window *w, struct window_pane *wp) +{ + w->active = wp; + while (w->active->flags & PANE_HIDDEN) + w->active = TAILQ_PREV(w->active, window_panes, entry); +} + +struct window_pane * +window_add_pane(struct window *w, int wanty, const char *cmd, + const char *cwd, const char **envp, u_int hlimit, char **cause) +{ + struct window_pane *wp; + u_int sizey; + + if (TAILQ_EMPTY(&w->panes)) + wanty = w->sy; + else { + sizey = w->active->sy - 1; /* for separator */ + if (sizey < PANE_MINIMUM * 2) { + *cause = xstrdup("pane too small"); + return (NULL); + } + + if (wanty == -1) + wanty = sizey / 2; + + if (wanty < PANE_MINIMUM) + wanty = PANE_MINIMUM; + if ((u_int) wanty > sizey - PANE_MINIMUM) + wanty = sizey - PANE_MINIMUM; + + window_pane_resize(w->active, w->sx, sizey - wanty); + } + + wp = window_pane_create(w, w->sx, wanty, hlimit); + if (TAILQ_EMPTY(&w->panes)) + TAILQ_INSERT_HEAD(&w->panes, wp, entry); + else + TAILQ_INSERT_AFTER(&w->panes, w->active, wp, entry); + if (window_pane_spawn(wp, cmd, cwd, envp, cause) != 0) { + window_remove_pane(w, wp); + return (NULL); + } + return (wp); +} + +void +window_remove_pane(struct window *w, struct window_pane *wp) +{ + w->active = TAILQ_PREV(wp, window_panes, entry); + if (w->active == NULL) + w->active = TAILQ_NEXT(wp, entry); + + TAILQ_REMOVE(&w->panes, wp, entry); + window_pane_destroy(wp); +} + +u_int +window_index_of_pane(struct window *w, struct window_pane *find) +{ + struct window_pane *wp; + u_int n; + + n = 0; + TAILQ_FOREACH(wp, &w->panes, entry) { + if (wp == find) + return (n); + n++; + } + fatalx("unknown pane"); +} + +struct window_pane * +window_pane_at_index(struct window *w, u_int idx) +{ + struct window_pane *wp; + u_int n; + + n = 0; + TAILQ_FOREACH(wp, &w->panes, entry) { + if (n == idx) + return (wp); + n++; + } + return (NULL); +} + +u_int +window_count_panes(struct window *w) +{ + struct window_pane *wp; + u_int n; + + n = 0; + TAILQ_FOREACH(wp, &w->panes, entry) + n++; + return (n); +} + +void +window_destroy_panes(struct window *w) +{ + struct window_pane *wp; + + while (!TAILQ_EMPTY(&w->panes)) { + wp = TAILQ_FIRST(&w->panes); + TAILQ_REMOVE(&w->panes, wp, entry); + window_pane_destroy(wp); + } +} + +struct window_pane * +window_pane_create(struct window *w, u_int sx, u_int sy, u_int hlimit) +{ + struct window_pane *wp; + + wp = xcalloc(1, sizeof *wp); + wp->window = w; + + wp->cmd = NULL; + wp->cwd = NULL; + + wp->fd = -1; + wp->in = buffer_create(BUFSIZ); + wp->out = buffer_create(BUFSIZ); + + wp->mode = NULL; + + wp->xoff = 0; + wp->yoff = 0; + + wp->sx = sx; + wp->sy = sy; + + screen_init(&wp->base, sx, sy, hlimit); + wp->screen = &wp->base; + + input_init(wp); + + return (wp); +} + +void +window_pane_destroy(struct window_pane *wp) +{ + if (wp->fd != -1) + close(wp->fd); + + input_free(wp); + + window_pane_reset_mode(wp); + screen_free(&wp->base); + + buffer_destroy(wp->in); + buffer_destroy(wp->out); + + if (wp->cwd != NULL) + xfree(wp->cwd); + if (wp->cmd != NULL) + xfree(wp->cmd); + xfree(wp); +} + +int +window_pane_spawn(struct window_pane *wp, + const char *cmd, const char *cwd, const char **envp, char **cause) +{ + struct winsize ws; + int mode; + const char **envq; + struct timeval tv; + + if (wp->fd != -1) + close(wp->fd); + if (cmd != NULL) { + if (wp->cmd != NULL) + xfree(wp->cmd); + wp->cmd = xstrdup(cmd); + } + if (cwd != NULL) { + if (wp->cwd != NULL) + xfree(wp->cwd); + wp->cwd = xstrdup(cwd); + } + + memset(&ws, 0, sizeof ws); + ws.ws_col = screen_size_x(&wp->base); + ws.ws_row = screen_size_y(&wp->base); + + if (gettimeofday(&wp->window->name_timer, NULL) != 0) + fatal("gettimeofday"); + tv.tv_sec = 0; + tv.tv_usec = NAME_INTERVAL * 1000L; + timeradd(&wp->window->name_timer, &tv, &wp->window->name_timer); + + switch (wp->pid = forkpty(&wp->fd, wp->tty, NULL, &ws)) { + case -1: + wp->fd = -1; + xasprintf(cause, "%s: %s", cmd, strerror(errno)); + return (-1); + case 0: + if (chdir(wp->cwd) != 0) + chdir("/"); + for (envq = envp; *envq != NULL; envq++) { + if (putenv((char *) *envq) != 0) + fatal("putenv failed"); + } + sigreset(); + log_close(); + + execl(_PATH_BSHELL, "sh", "-c", wp->cmd, (char *) NULL); + fatal("execl failed"); + } + + if ((mode = fcntl(wp->fd, F_GETFL)) == -1) + fatal("fcntl failed"); + if (fcntl(wp->fd, F_SETFL, mode|O_NONBLOCK) == -1) + fatal("fcntl failed"); + if (fcntl(wp->fd, F_SETFD, FD_CLOEXEC) == -1) + fatal("fcntl failed"); + + return (0); +} + +int +window_pane_resize(struct window_pane *wp, u_int sx, u_int sy) +{ + struct winsize ws; + + if (sx == wp->sx && sy == wp->sy) + return (-1); + wp->sx = sx; + wp->sy = sy; + + memset(&ws, 0, sizeof ws); + ws.ws_col = sx; + ws.ws_row = sy; + + screen_resize(&wp->base, sx, sy); + if (wp->mode != NULL) + wp->mode->resize(wp, sx, sy); + + if (wp->fd != -1 && ioctl(wp->fd, TIOCSWINSZ, &ws) == -1) + fatal("ioctl failed"); + return (0); +} + +int +window_pane_set_mode(struct window_pane *wp, const struct window_mode *mode) +{ + struct screen *s; + + if (wp->mode != NULL || wp->mode == mode) + return (1); + + wp->mode = mode; + + if ((s = wp->mode->init(wp)) != NULL) + wp->screen = s; + server_redraw_window(wp->window); + return (0); +} + +void +window_pane_reset_mode(struct window_pane *wp) +{ + if (wp->mode == NULL) + return; + + wp->mode->free(wp); + wp->mode = NULL; + + wp->screen = &wp->base; + server_redraw_window(wp->window); +} + +void +window_pane_parse(struct window_pane *wp) +{ + input_parse(wp); +} + +void +window_pane_key(struct window_pane *wp, struct client *c, int key) +{ + if (wp->mode != NULL) { + if (wp->mode->key != NULL) + wp->mode->key(wp, c, key); + } else + input_key(wp, key); +} + +void +window_pane_mouse( + struct window_pane *wp, struct client *c, u_char b, u_char x, u_char y) +{ + /* XXX convert from 1-based? */ + + if (x < wp->xoff || x >= wp->xoff + wp->sx) + return; + if (y < wp->yoff || y >= wp->yoff + wp->sy) + return; + x -= wp->xoff; + y -= wp->yoff; + + if (wp->mode != NULL) { + if (wp->mode->mouse != NULL) + wp->mode->mouse(wp, c, b, x, y); + } else + input_mouse(wp, b, x, y); +} + +char * +window_pane_search(struct window_pane *wp, const char *searchstr) +{ + const struct grid_cell *gc; + const struct grid_utf8 *gu; + char *buf, *s; + size_t off; + u_int i, j, k; + + buf = xmalloc(1); + + for (j = 0; j < screen_size_y(&wp->base); j++) { + off = 0; + for (i = 0; i < screen_size_x(&wp->base); i++) { + gc = grid_view_peek_cell(wp->base.grid, i, j); + if (gc->flags & GRID_FLAG_UTF8) { + gu = grid_view_peek_utf8(wp->base.grid, i, j); + buf = xrealloc(buf, 1, off + 8); + for (k = 0; k < UTF8_SIZE; k++) { + if (gu->data[k] == 0xff) + break; + buf[off++] = gu->data[k]; + } + } else { + buf = xrealloc(buf, 1, off + 1); + buf[off++] = gc->data; + } + } + while (off > 0 && buf[off - 1] == ' ') + off--; + buf[off] = '\0'; + + if ((s = strstr(buf, searchstr)) != NULL) { + s = section_string(buf, off, s - buf, 40); + xfree(buf); + return (s); + } + } + + xfree(buf); + return (NULL); +} diff --git a/usr.bin/tmux/xmalloc.c b/usr.bin/tmux/xmalloc.c new file mode 100644 index 00000000000..6756a14d435 --- /dev/null +++ b/usr.bin/tmux/xmalloc.c @@ -0,0 +1,144 @@ +/* $OpenBSD: xmalloc.c,v 1.1 2009/06/01 22:58:49 nicm Exp $ */ + +/* + * Copyright (c) 2004 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/param.h> + +#include <errno.h> +#include <libgen.h> +#include <stdint.h> +#include <stdlib.h> +#include <string.h> + +#include "tmux.h" + +char * +xstrdup(const char *s) +{ + void *ptr; + size_t len; + + len = strlen(s) + 1; + ptr = xmalloc(len); + + return (strncpy(ptr, s, len)); +} + +void * +xcalloc(size_t nmemb, size_t size) +{ + void *ptr; + + if (size == 0 || nmemb == 0) + fatalx("zero size"); + if (SIZE_MAX / nmemb < size) + fatalx("nmemb * size > SIZE_MAX"); + if ((ptr = calloc(nmemb, size)) == NULL) + fatal("xcalloc failed"); + + return (ptr); +} + +void * +xmalloc(size_t size) +{ + void *ptr; + + if (size == 0) + fatalx("zero size"); + if ((ptr = malloc(size)) == NULL) + fatal("xmalloc failed"); + + return (ptr); +} + +void * +xrealloc(void *oldptr, size_t nmemb, size_t size) +{ + size_t newsize = nmemb * size; + void *newptr; + + if (newsize == 0) + fatalx("zero size"); + if (SIZE_MAX / nmemb < size) + fatalx("nmemb * size > SIZE_MAX"); + if ((newptr = realloc(oldptr, newsize)) == NULL) + fatal("xrealloc failed"); + + return (newptr); +} + +void +xfree(void *ptr) +{ + if (ptr == NULL) + fatalx("null pointer"); + free(ptr); +} + +int printflike2 +xasprintf(char **ret, const char *fmt, ...) +{ + va_list ap; + int i; + + va_start(ap, fmt); + i = xvasprintf(ret, fmt, ap); + va_end(ap); + + return (i); +} + +int +xvasprintf(char **ret, const char *fmt, va_list ap) +{ + int i; + + i = vasprintf(ret, fmt, ap); + if (i < 0 || *ret == NULL) + fatal("xvasprintf failed"); + + return (i); +} + +int printflike3 +xsnprintf(char *buf, size_t len, const char *fmt, ...) +{ + va_list ap; + int i; + + va_start(ap, fmt); + i = xvsnprintf(buf, len, fmt, ap); + va_end(ap); + + return (i); +} + +int +xvsnprintf(char *buf, size_t len, const char *fmt, va_list ap) +{ + int i; + + if (len > INT_MAX) + fatalx("len > INT_MAX"); + + i = vsnprintf(buf, len, fmt, ap); + if (i < 0) + fatal("vsnprintf failed"); + + return (i); +} |