mirror of
https://github.com/tmux/tmux.git
synced 2025-01-06 07:48:48 +00:00
Merge branch 'master' into 3.2-rc
This commit is contained in:
commit
c01251d023
2
.github/travis/build-all.sh
vendored
2
.github/travis/build-all.sh
vendored
@ -35,4 +35,4 @@ tar -zxf ncurses-*.tar.gz || exit 1
|
||||
|
||||
sh autogen.sh || exit 1
|
||||
PKG_CONFIG_PATH=$BUILD/lib/pkgconfig ./configure --prefix=$BUILD "$@"
|
||||
make && make install || exit 1
|
||||
make && make install || (cat config.log; exit 1)
|
||||
|
2
.gitignore
vendored
2
.gitignore
vendored
@ -19,3 +19,5 @@ configure
|
||||
tmux.1.*
|
||||
*.dSYM
|
||||
cmd-parse.c
|
||||
fuzz/*-fuzzer
|
||||
.dirstamp
|
||||
|
70
CHANGES
70
CHANGES
@ -1,5 +1,69 @@
|
||||
CHANGES FROM 3.1c TO 3.2
|
||||
|
||||
* Improve performance of format evaluation.
|
||||
|
||||
* Make jump command support UTF-8 in copy mode.
|
||||
|
||||
* Support X11 colour names and other colour formats for OSC 10 and 11.
|
||||
|
||||
* Add "pipe" variants of "copy-pipe" commands which do not copy.
|
||||
|
||||
* Include "focused" in client flags.
|
||||
|
||||
* Send Unicode directional isolate characters around horizontal pane borders if
|
||||
the terminal supports UTF-8 and an extension terminfo(5) capability "Bidi" is
|
||||
present.
|
||||
|
||||
* Add a -S flag to new-window to make it select the existing window if one
|
||||
with the given name already exists rather than failing with an error.
|
||||
|
||||
* Addd a format modifier to check if a window or session name exists (N/w or
|
||||
N/s).
|
||||
|
||||
* Add compat clock_gettime for older macOS.
|
||||
|
||||
* Add a no-detached choice to detach-on-destroy which detaches only if there
|
||||
are no other detached sessions to switch to.
|
||||
|
||||
* Add rectangle-on and rectangle-off copy mode commands.
|
||||
|
||||
* Change so that window_flags escapes # automatically. A new format
|
||||
window_raw_flags contains the old unescaped version.
|
||||
|
||||
* Add -N flag to never start server even if command would normally do so.
|
||||
|
||||
* With incremental search, start empty and only repeat the previous search if
|
||||
the user tries to search again with an empty prompt.
|
||||
|
||||
* Add a value for remain-on-exit that only keeps the pane if the program
|
||||
failed.
|
||||
|
||||
* Add a -C flag to run-shell to use a tmux command rather than a shell command.
|
||||
|
||||
* Do not list user options with show-hooks.
|
||||
|
||||
* Remove current match indicator in copy mode which can't work anymore since we
|
||||
only search the visible region.
|
||||
|
||||
* Make synchronize-panes a pane option and add -U flag to set-option to unset
|
||||
an option on all panes.
|
||||
|
||||
* Make replacement of ##s consistent when drawing formats, whether followed by
|
||||
[ or not. Add a flag (e) to the q: format modifier to double up #s
|
||||
|
||||
* Add -N flag to display-panes to ignore keys.
|
||||
|
||||
* Change how escaping is processed for formats so that ## and # can be used in
|
||||
styles.
|
||||
|
||||
* Add a 'w' format modifier for string width.
|
||||
|
||||
* Add support for Haiku.
|
||||
|
||||
* Expand menu and popup -x and -y as formats.
|
||||
|
||||
* Add numeric comparisons for formats.
|
||||
|
||||
* Fire focus events even when the pane is in a mode.
|
||||
|
||||
* Add -O flag to display-menu to not automatically close when all mouse buttons
|
||||
@ -267,9 +331,9 @@ CHANGES FROM 3.1c TO 3.2
|
||||
* Change default position for display-menu -x and -y to centre rather than top
|
||||
left.
|
||||
|
||||
* Add support for per-client transient popups, similar to menus. These are
|
||||
created with new command display-popup. Popups may either show fixed text and
|
||||
trigger a tmux command when a key is pressed, or run a program (-R flag).
|
||||
* Add support for per-client transient popups, similar to menus but which are
|
||||
connected to an external command (like a pane). These are created with new
|
||||
command display-popup.
|
||||
|
||||
* Change double and triple click bindings so that only one is fired (previously
|
||||
double click was fired on the way to triple click). Also add default double
|
||||
|
14
Makefile.am
14
Makefile.am
@ -28,7 +28,7 @@ AM_CFLAGS += -Wmissing-prototypes -Wstrict-prototypes -Wmissing-declarations
|
||||
AM_CFLAGS += -Wwrite-strings -Wshadow -Wpointer-arith -Wsign-compare
|
||||
AM_CFLAGS += -Wundef -Wbad-function-cast -Winline -Wcast-align
|
||||
AM_CFLAGS += -Wdeclaration-after-statement -Wno-pointer-sign -Wno-attributes
|
||||
AM_CFLAGS += -Wno-unused-result
|
||||
AM_CFLAGS += -Wno-unused-result -Wno-format-y2k
|
||||
AM_CPPFLAGS += -DDEBUG
|
||||
endif
|
||||
AM_CPPFLAGS += -iquote.
|
||||
@ -58,6 +58,11 @@ if IS_NETBSD
|
||||
AM_CPPFLAGS += -D_OPENBSD_SOURCE
|
||||
endif
|
||||
|
||||
# Set flags for Haiku.
|
||||
if IS_HAIKU
|
||||
AM_CPPFLAGS += -D_BSD_SOURCE
|
||||
endif
|
||||
|
||||
# List of sources.
|
||||
dist_tmux_SOURCES = \
|
||||
alerts.c \
|
||||
@ -136,6 +141,7 @@ dist_tmux_SOURCES = \
|
||||
file.c \
|
||||
format.c \
|
||||
format-draw.c \
|
||||
grid-reader.c \
|
||||
grid-view.c \
|
||||
grid.c \
|
||||
input-keys.c \
|
||||
@ -197,6 +203,12 @@ if HAVE_UTF8PROC
|
||||
nodist_tmux_SOURCES += compat/utf8proc.c
|
||||
endif
|
||||
|
||||
if NEED_FUZZING
|
||||
check_PROGRAMS = fuzz/input-fuzzer
|
||||
fuzz_input_fuzzer_LDFLAGS = $(FUZZING_LIBS)
|
||||
fuzz_input_fuzzer_LDADD = $(LDADD) $(tmux_OBJECTS)
|
||||
endif
|
||||
|
||||
# Install tmux.1 in the right format.
|
||||
install-exec-hook:
|
||||
if test x@MANFORMAT@ = xmdoc; then \
|
||||
|
1
alerts.c
1
alerts.c
@ -18,7 +18,6 @@
|
||||
|
||||
#include <sys/types.h>
|
||||
|
||||
#include <event.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "tmux.h"
|
||||
|
30
cfg.c
30
cfg.c
@ -27,12 +27,15 @@
|
||||
#include "tmux.h"
|
||||
|
||||
struct client *cfg_client;
|
||||
static char *cfg_file;
|
||||
int cfg_finished;
|
||||
static char **cfg_causes;
|
||||
static u_int cfg_ncauses;
|
||||
static struct cmdq_item *cfg_item;
|
||||
|
||||
int cfg_quiet = 1;
|
||||
char **cfg_files;
|
||||
u_int cfg_nfiles;
|
||||
|
||||
static enum cmd_retval
|
||||
cfg_client_done(__unused struct cmdq_item *item, __unused void *data)
|
||||
{
|
||||
@ -59,19 +62,11 @@ cfg_done(__unused struct cmdq_item *item, __unused void *data)
|
||||
return (CMD_RETURN_NORMAL);
|
||||
}
|
||||
|
||||
void
|
||||
set_cfg_file(const char *path)
|
||||
{
|
||||
free(cfg_file);
|
||||
cfg_file = xstrdup(path);
|
||||
}
|
||||
|
||||
void
|
||||
start_cfg(void)
|
||||
{
|
||||
struct client *c;
|
||||
char **paths;
|
||||
u_int i, n;
|
||||
u_int i;
|
||||
|
||||
/*
|
||||
* Configuration files are loaded without a client, so commands are run
|
||||
@ -89,15 +84,12 @@ start_cfg(void)
|
||||
cmdq_append(c, cfg_item);
|
||||
}
|
||||
|
||||
if (cfg_file == NULL) {
|
||||
expand_paths(TMUX_CONF, &paths, &n);
|
||||
for (i = 0; i < n; i++) {
|
||||
load_cfg(paths[i], c, NULL, CMD_PARSE_QUIET, NULL);
|
||||
free(paths[i]);
|
||||
}
|
||||
free(paths);
|
||||
} else
|
||||
load_cfg(cfg_file, c, NULL, 0, NULL);
|
||||
for (i = 0; i < cfg_nfiles; i++) {
|
||||
if (cfg_quiet)
|
||||
load_cfg(cfg_files[i], c, NULL, CMD_PARSE_QUIET, NULL);
|
||||
else
|
||||
load_cfg(cfg_files[i], c, NULL, 0, NULL);
|
||||
}
|
||||
|
||||
cmdq_append(NULL, cmdq_get_callback(cfg_done, NULL));
|
||||
}
|
||||
|
343
client.c
343
client.c
@ -24,7 +24,6 @@
|
||||
#include <sys/file.h>
|
||||
|
||||
#include <errno.h>
|
||||
#include <event.h>
|
||||
#include <fcntl.h>
|
||||
#include <signal.h>
|
||||
#include <stdlib.h>
|
||||
@ -62,7 +61,8 @@ static __dead void client_exec(const char *,const char *);
|
||||
static int client_get_lock(char *);
|
||||
static int client_connect(struct event_base *, const char *,
|
||||
uint64_t);
|
||||
static void client_send_identify(const char *, const char *, int);
|
||||
static void client_send_identify(const char *, const char *,
|
||||
char **, u_int, const char *, int);
|
||||
static void client_signal(int);
|
||||
static void client_dispatch(struct imsg *, void *);
|
||||
static void client_dispatch_attached(struct imsg *);
|
||||
@ -127,6 +127,8 @@ retry:
|
||||
log_debug("connect failed: %s", strerror(errno));
|
||||
if (errno != ECONNREFUSED && errno != ENOENT)
|
||||
goto failed;
|
||||
if (flags & CLIENT_NOSTARTSERVER)
|
||||
goto failed;
|
||||
if (~flags & CLIENT_STARTSERVER)
|
||||
goto failed;
|
||||
close(fd);
|
||||
@ -221,20 +223,7 @@ client_exit_message(void)
|
||||
static void
|
||||
client_exit(void)
|
||||
{
|
||||
struct client_file *cf;
|
||||
size_t left;
|
||||
int waiting = 0;
|
||||
|
||||
RB_FOREACH (cf, client_files, &client_files) {
|
||||
if (cf->event == NULL)
|
||||
continue;
|
||||
left = EVBUFFER_LENGTH(cf->event->output);
|
||||
if (left != 0) {
|
||||
waiting++;
|
||||
log_debug("file %u %zu bytes left", cf->stream, left);
|
||||
}
|
||||
}
|
||||
if (waiting == 0)
|
||||
if (!file_write_left(&client_files))
|
||||
proc_exit(client_proc);
|
||||
}
|
||||
|
||||
@ -246,13 +235,14 @@ client_main(struct event_base *base, int argc, char **argv, uint64_t flags,
|
||||
struct cmd_parse_result *pr;
|
||||
struct msg_command *data;
|
||||
int fd, i;
|
||||
const char *ttynam, *cwd;
|
||||
const char *ttynam, *termname, *cwd;
|
||||
pid_t ppid;
|
||||
enum msgtype msg;
|
||||
struct termios tio, saved_tio;
|
||||
size_t size, linesize = 0;
|
||||
ssize_t linelen;
|
||||
char *line = NULL;
|
||||
char *line = NULL, **caps = NULL, *cause;
|
||||
u_int ncaps = 0;
|
||||
|
||||
/* Ignore SIGCHLD now or daemon() in the server will leave a zombie. */
|
||||
signal(SIGCHLD, SIG_IGN);
|
||||
@ -308,6 +298,8 @@ client_main(struct event_base *base, int argc, char **argv, uint64_t flags,
|
||||
cwd = "/";
|
||||
if ((ttynam = ttyname(STDIN_FILENO)) == NULL)
|
||||
ttynam = "";
|
||||
if ((termname = getenv("TERM")) == NULL)
|
||||
termname = "";
|
||||
|
||||
/*
|
||||
* Drop privileges for client. "proc exec" is needed for -c and for
|
||||
@ -323,6 +315,16 @@ client_main(struct event_base *base, int argc, char **argv, uint64_t flags,
|
||||
NULL) != 0)
|
||||
fatal("pledge failed");
|
||||
|
||||
/* Load terminfo entry if any. */
|
||||
if (isatty(STDIN_FILENO) &&
|
||||
*termname != '\0' &&
|
||||
tty_term_read_list(termname, STDIN_FILENO, &caps, &ncaps,
|
||||
&cause) != 0) {
|
||||
fprintf(stderr, "%s\n", cause);
|
||||
free(cause);
|
||||
return (1);
|
||||
}
|
||||
|
||||
/* Free stuff that is not used in the client. */
|
||||
if (ptm_fd != -1)
|
||||
close(ptm_fd);
|
||||
@ -353,7 +355,8 @@ client_main(struct event_base *base, int argc, char **argv, uint64_t flags,
|
||||
}
|
||||
|
||||
/* Send identify messages. */
|
||||
client_send_identify(ttynam, cwd, feat);
|
||||
client_send_identify(ttynam, termname, caps, ncaps, cwd, feat);
|
||||
tty_term_free_list(caps, ncaps);
|
||||
|
||||
/* Send first command. */
|
||||
if (msg == MSG_COMMAND) {
|
||||
@ -436,27 +439,32 @@ client_main(struct event_base *base, int argc, char **argv, uint64_t flags,
|
||||
|
||||
/* Send identify messages to server. */
|
||||
static void
|
||||
client_send_identify(const char *ttynam, const char *cwd, int feat)
|
||||
client_send_identify(const char *ttynam, const char *termname, char **caps,
|
||||
u_int ncaps, const char *cwd, int feat)
|
||||
{
|
||||
const char *s;
|
||||
char **ss;
|
||||
size_t sslen;
|
||||
int fd, flags = client_flags;
|
||||
pid_t pid;
|
||||
char **ss;
|
||||
size_t sslen;
|
||||
int fd, flags = client_flags;
|
||||
pid_t pid;
|
||||
u_int i;
|
||||
|
||||
proc_send(client_peer, MSG_IDENTIFY_FLAGS, -1, &flags, sizeof flags);
|
||||
proc_send(client_peer, MSG_IDENTIFY_LONGFLAGS, -1, &client_flags,
|
||||
sizeof client_flags);
|
||||
|
||||
if ((s = getenv("TERM")) == NULL)
|
||||
s = "";
|
||||
proc_send(client_peer, MSG_IDENTIFY_TERM, -1, s, strlen(s) + 1);
|
||||
proc_send(client_peer, MSG_IDENTIFY_TERM, -1, termname,
|
||||
strlen(termname) + 1);
|
||||
proc_send(client_peer, MSG_IDENTIFY_FEATURES, -1, &feat, sizeof feat);
|
||||
|
||||
proc_send(client_peer, MSG_IDENTIFY_TTYNAME, -1, ttynam,
|
||||
strlen(ttynam) + 1);
|
||||
proc_send(client_peer, MSG_IDENTIFY_CWD, -1, cwd, strlen(cwd) + 1);
|
||||
|
||||
for (i = 0; i < ncaps; i++) {
|
||||
proc_send(client_peer, MSG_IDENTIFY_TERMINFO, -1,
|
||||
caps[i], strlen(caps[i]) + 1);
|
||||
}
|
||||
|
||||
if ((fd = dup(STDIN_FILENO)) == -1)
|
||||
fatal("dup failed");
|
||||
proc_send(client_peer, MSG_IDENTIFY_STDIN, fd, NULL, 0);
|
||||
@ -477,257 +485,6 @@ client_send_identify(const char *ttynam, const char *cwd, int feat)
|
||||
proc_send(client_peer, MSG_IDENTIFY_DONE, -1, NULL, 0);
|
||||
}
|
||||
|
||||
/* File write error callback. */
|
||||
static void
|
||||
client_write_error_callback(__unused struct bufferevent *bev,
|
||||
__unused short what, void *arg)
|
||||
{
|
||||
struct client_file *cf = arg;
|
||||
|
||||
log_debug("write error file %d", cf->stream);
|
||||
|
||||
bufferevent_free(cf->event);
|
||||
cf->event = NULL;
|
||||
|
||||
close(cf->fd);
|
||||
cf->fd = -1;
|
||||
|
||||
if (client_exitflag)
|
||||
client_exit();
|
||||
}
|
||||
|
||||
/* File write callback. */
|
||||
static void
|
||||
client_write_callback(__unused struct bufferevent *bev, void *arg)
|
||||
{
|
||||
struct client_file *cf = arg;
|
||||
|
||||
if (cf->closed && EVBUFFER_LENGTH(cf->event->output) == 0) {
|
||||
bufferevent_free(cf->event);
|
||||
close(cf->fd);
|
||||
RB_REMOVE(client_files, &client_files, cf);
|
||||
file_free(cf);
|
||||
}
|
||||
|
||||
if (client_exitflag)
|
||||
client_exit();
|
||||
}
|
||||
|
||||
/* Open write file. */
|
||||
static void
|
||||
client_write_open(void *data, size_t datalen)
|
||||
{
|
||||
struct msg_write_open *msg = data;
|
||||
const char *path;
|
||||
struct msg_write_ready reply;
|
||||
struct client_file find, *cf;
|
||||
const int flags = O_NONBLOCK|O_WRONLY|O_CREAT;
|
||||
int error = 0;
|
||||
|
||||
if (datalen < sizeof *msg)
|
||||
fatalx("bad MSG_WRITE_OPEN size");
|
||||
if (datalen == sizeof *msg)
|
||||
path = "-";
|
||||
else
|
||||
path = (const char *)(msg + 1);
|
||||
log_debug("open write file %d %s", msg->stream, path);
|
||||
|
||||
find.stream = msg->stream;
|
||||
if ((cf = RB_FIND(client_files, &client_files, &find)) == NULL) {
|
||||
cf = file_create(NULL, msg->stream, NULL, NULL);
|
||||
RB_INSERT(client_files, &client_files, cf);
|
||||
} else {
|
||||
error = EBADF;
|
||||
goto reply;
|
||||
}
|
||||
if (cf->closed) {
|
||||
error = EBADF;
|
||||
goto reply;
|
||||
}
|
||||
|
||||
cf->fd = -1;
|
||||
if (msg->fd == -1)
|
||||
cf->fd = open(path, msg->flags|flags, 0644);
|
||||
else {
|
||||
if (msg->fd != STDOUT_FILENO && msg->fd != STDERR_FILENO)
|
||||
errno = EBADF;
|
||||
else {
|
||||
cf->fd = dup(msg->fd);
|
||||
if (~client_flags & CLIENT_CONTROL)
|
||||
close(msg->fd); /* can only be used once */
|
||||
}
|
||||
}
|
||||
if (cf->fd == -1) {
|
||||
error = errno;
|
||||
goto reply;
|
||||
}
|
||||
|
||||
cf->event = bufferevent_new(cf->fd, NULL, client_write_callback,
|
||||
client_write_error_callback, cf);
|
||||
bufferevent_enable(cf->event, EV_WRITE);
|
||||
goto reply;
|
||||
|
||||
reply:
|
||||
reply.stream = msg->stream;
|
||||
reply.error = error;
|
||||
proc_send(client_peer, MSG_WRITE_READY, -1, &reply, sizeof reply);
|
||||
}
|
||||
|
||||
/* Write to client file. */
|
||||
static void
|
||||
client_write_data(void *data, size_t datalen)
|
||||
{
|
||||
struct msg_write_data *msg = data;
|
||||
struct client_file find, *cf;
|
||||
size_t size = datalen - sizeof *msg;
|
||||
|
||||
if (datalen < sizeof *msg)
|
||||
fatalx("bad MSG_WRITE size");
|
||||
find.stream = msg->stream;
|
||||
if ((cf = RB_FIND(client_files, &client_files, &find)) == NULL)
|
||||
fatalx("unknown stream number");
|
||||
log_debug("write %zu to file %d", size, cf->stream);
|
||||
|
||||
if (cf->event != NULL)
|
||||
bufferevent_write(cf->event, msg + 1, size);
|
||||
}
|
||||
|
||||
/* Close client file. */
|
||||
static void
|
||||
client_write_close(void *data, size_t datalen)
|
||||
{
|
||||
struct msg_write_close *msg = data;
|
||||
struct client_file find, *cf;
|
||||
|
||||
if (datalen != sizeof *msg)
|
||||
fatalx("bad MSG_WRITE_CLOSE size");
|
||||
find.stream = msg->stream;
|
||||
if ((cf = RB_FIND(client_files, &client_files, &find)) == NULL)
|
||||
fatalx("unknown stream number");
|
||||
log_debug("close file %d", cf->stream);
|
||||
|
||||
if (cf->event == NULL || EVBUFFER_LENGTH(cf->event->output) == 0) {
|
||||
if (cf->event != NULL)
|
||||
bufferevent_free(cf->event);
|
||||
if (cf->fd != -1)
|
||||
close(cf->fd);
|
||||
RB_REMOVE(client_files, &client_files, cf);
|
||||
file_free(cf);
|
||||
}
|
||||
}
|
||||
|
||||
/* File read callback. */
|
||||
static void
|
||||
client_read_callback(__unused struct bufferevent *bev, void *arg)
|
||||
{
|
||||
struct client_file *cf = arg;
|
||||
void *bdata;
|
||||
size_t bsize;
|
||||
struct msg_read_data *msg;
|
||||
size_t msglen;
|
||||
|
||||
msg = xmalloc(sizeof *msg);
|
||||
for (;;) {
|
||||
bdata = EVBUFFER_DATA(cf->event->input);
|
||||
bsize = EVBUFFER_LENGTH(cf->event->input);
|
||||
|
||||
if (bsize == 0)
|
||||
break;
|
||||
if (bsize > MAX_IMSGSIZE - IMSG_HEADER_SIZE - sizeof *msg)
|
||||
bsize = MAX_IMSGSIZE - IMSG_HEADER_SIZE - sizeof *msg;
|
||||
log_debug("read %zu from file %d", bsize, cf->stream);
|
||||
|
||||
msglen = (sizeof *msg) + bsize;
|
||||
msg = xrealloc(msg, msglen);
|
||||
msg->stream = cf->stream;
|
||||
memcpy(msg + 1, bdata, bsize);
|
||||
proc_send(client_peer, MSG_READ, -1, msg, msglen);
|
||||
|
||||
evbuffer_drain(cf->event->input, bsize);
|
||||
}
|
||||
free(msg);
|
||||
}
|
||||
|
||||
/* File read error callback. */
|
||||
static void
|
||||
client_read_error_callback(__unused struct bufferevent *bev,
|
||||
__unused short what, void *arg)
|
||||
{
|
||||
struct client_file *cf = arg;
|
||||
struct msg_read_done msg;
|
||||
|
||||
log_debug("read error file %d", cf->stream);
|
||||
|
||||
msg.stream = cf->stream;
|
||||
msg.error = 0;
|
||||
proc_send(client_peer, MSG_READ_DONE, -1, &msg, sizeof msg);
|
||||
|
||||
bufferevent_free(cf->event);
|
||||
close(cf->fd);
|
||||
RB_REMOVE(client_files, &client_files, cf);
|
||||
file_free(cf);
|
||||
}
|
||||
|
||||
/* Open read file. */
|
||||
static void
|
||||
client_read_open(void *data, size_t datalen)
|
||||
{
|
||||
struct msg_read_open *msg = data;
|
||||
const char *path;
|
||||
struct msg_read_done reply;
|
||||
struct client_file find, *cf;
|
||||
const int flags = O_NONBLOCK|O_RDONLY;
|
||||
int error;
|
||||
|
||||
if (datalen < sizeof *msg)
|
||||
fatalx("bad MSG_READ_OPEN size");
|
||||
if (datalen == sizeof *msg)
|
||||
path = "-";
|
||||
else
|
||||
path = (const char *)(msg + 1);
|
||||
log_debug("open read file %d %s", msg->stream, path);
|
||||
|
||||
find.stream = msg->stream;
|
||||
if ((cf = RB_FIND(client_files, &client_files, &find)) == NULL) {
|
||||
cf = file_create(NULL, msg->stream, NULL, NULL);
|
||||
RB_INSERT(client_files, &client_files, cf);
|
||||
} else {
|
||||
error = EBADF;
|
||||
goto reply;
|
||||
}
|
||||
if (cf->closed) {
|
||||
error = EBADF;
|
||||
goto reply;
|
||||
}
|
||||
|
||||
cf->fd = -1;
|
||||
if (msg->fd == -1)
|
||||
cf->fd = open(path, flags);
|
||||
else {
|
||||
if (msg->fd != STDIN_FILENO)
|
||||
errno = EBADF;
|
||||
else {
|
||||
cf->fd = dup(msg->fd);
|
||||
if (~client_flags & CLIENT_CONTROL)
|
||||
close(msg->fd); /* can only be used once */
|
||||
}
|
||||
}
|
||||
if (cf->fd == -1) {
|
||||
error = errno;
|
||||
goto reply;
|
||||
}
|
||||
|
||||
cf->event = bufferevent_new(cf->fd, client_read_callback, NULL,
|
||||
client_read_error_callback, cf);
|
||||
bufferevent_enable(cf->event, EV_READ);
|
||||
return;
|
||||
|
||||
reply:
|
||||
reply.stream = msg->stream;
|
||||
reply.error = error;
|
||||
proc_send(client_peer, MSG_READ_DONE, -1, &reply, sizeof reply);
|
||||
}
|
||||
|
||||
/* Run command in shell; used for -c. */
|
||||
static __dead void
|
||||
client_exec(const char *shell, const char *shellcmd)
|
||||
@ -802,13 +559,25 @@ client_signal(int sig)
|
||||
}
|
||||
}
|
||||
|
||||
/* Callback for file write error or close. */
|
||||
static void
|
||||
client_file_check_cb(__unused struct client *c, __unused const char *path,
|
||||
__unused int error, __unused int closed, __unused struct evbuffer *buffer,
|
||||
__unused void *data)
|
||||
{
|
||||
if (client_exitflag)
|
||||
client_exit();
|
||||
}
|
||||
|
||||
/* Callback for client read events. */
|
||||
static void
|
||||
client_dispatch(struct imsg *imsg, __unused void *arg)
|
||||
{
|
||||
if (imsg == NULL) {
|
||||
client_exitreason = CLIENT_EXIT_LOST_SERVER;
|
||||
client_exitval = 1;
|
||||
if (!client_exitflag) {
|
||||
client_exitreason = CLIENT_EXIT_LOST_SERVER;
|
||||
client_exitval = 1;
|
||||
}
|
||||
proc_exit(client_proc);
|
||||
return;
|
||||
}
|
||||
@ -916,16 +685,20 @@ client_dispatch_wait(struct imsg *imsg)
|
||||
proc_exit(client_proc);
|
||||
break;
|
||||
case MSG_READ_OPEN:
|
||||
client_read_open(data, datalen);
|
||||
file_read_open(&client_files, client_peer, imsg, 1,
|
||||
!(client_flags & CLIENT_CONTROL), client_file_check_cb,
|
||||
NULL);
|
||||
break;
|
||||
case MSG_WRITE_OPEN:
|
||||
client_write_open(data, datalen);
|
||||
file_write_open(&client_files, client_peer, imsg, 1,
|
||||
!(client_flags & CLIENT_CONTROL), client_file_check_cb,
|
||||
NULL);
|
||||
break;
|
||||
case MSG_WRITE:
|
||||
client_write_data(data, datalen);
|
||||
file_write_data(&client_files, imsg);
|
||||
break;
|
||||
case MSG_WRITE_CLOSE:
|
||||
client_write_close(data, datalen);
|
||||
file_write_close(&client_files, imsg);
|
||||
break;
|
||||
case MSG_OLDSTDERR:
|
||||
case MSG_OLDSTDIN:
|
||||
|
@ -18,6 +18,7 @@
|
||||
|
||||
#include <sys/types.h>
|
||||
|
||||
#include <paths.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
@ -50,10 +51,10 @@ const struct cmd_entry cmd_display_popup_entry = {
|
||||
.name = "display-popup",
|
||||
.alias = "popup",
|
||||
|
||||
.args = { "CEKc:d:h:R:t:w:x:y:", 0, -1 },
|
||||
.usage = "[-CEK] [-c target-client] [-d start-directory] [-h height] "
|
||||
"[-R shell-command] " CMD_TARGET_PANE_USAGE " [-w width] "
|
||||
"[-x position] [-y position] [command line ...]",
|
||||
.args = { "Cc:d:Eh:t:w:x:y:", 0, -1 },
|
||||
.usage = "[-CE] [-c target-client] [-d start-directory] [-h height] "
|
||||
CMD_TARGET_PANE_USAGE " [-w width] "
|
||||
"[-x position] [-y position] [command]",
|
||||
|
||||
.target = { 't', CMD_FIND_PANE, 0 },
|
||||
|
||||
@ -61,7 +62,7 @@ const struct cmd_entry cmd_display_popup_entry = {
|
||||
.exec = cmd_display_popup_exec
|
||||
};
|
||||
|
||||
static void
|
||||
static int
|
||||
cmd_display_menu_get_position(struct client *tc, struct cmdq_item *item,
|
||||
struct args *args, u_int *px, u_int *py, u_int w, u_int h)
|
||||
{
|
||||
@ -71,44 +72,46 @@ cmd_display_menu_get_position(struct client *tc, struct cmdq_item *item,
|
||||
struct session *s = tc->session;
|
||||
struct winlink *wl = target->wl;
|
||||
struct window_pane *wp = target->wp;
|
||||
struct style_ranges *ranges;
|
||||
struct style_range *sr;
|
||||
struct style_ranges *ranges = NULL;
|
||||
struct style_range *sr = NULL;
|
||||
const char *xp, *yp;
|
||||
u_int line, ox, oy, sx, sy, lines;
|
||||
char *p;
|
||||
int top;
|
||||
u_int line, ox, oy, sx, sy, lines, position;
|
||||
long n;
|
||||
struct format_tree *ft;
|
||||
|
||||
lines = status_line_size(tc);
|
||||
for (line = 0; line < lines; line++) {
|
||||
ranges = &tc->status.entries[line].ranges;
|
||||
TAILQ_FOREACH(sr, ranges, entry) {
|
||||
if (sr->type == STYLE_RANGE_WINDOW)
|
||||
break;
|
||||
}
|
||||
if (sr != NULL)
|
||||
break;
|
||||
/*
|
||||
* Work out the position from the -x and -y arguments. This is the
|
||||
* bottom-left position.
|
||||
*/
|
||||
|
||||
/* If the popup is too big, stop now. */
|
||||
if (w > tty->sx || h > tty->sy)
|
||||
return (0);
|
||||
|
||||
/* Create format with mouse position if any. */
|
||||
ft = format_create_from_target(item);
|
||||
if (event->m.valid) {
|
||||
format_add(ft, "popup_mouse_x", "%u", event->m.x);
|
||||
format_add(ft, "popup_mouse_y", "%u", event->m.y);
|
||||
}
|
||||
if (line == lines)
|
||||
ranges = &tc->status.entries[0].ranges;
|
||||
|
||||
xp = args_get(args, 'x');
|
||||
if (xp == NULL || strcmp(xp, "C") == 0)
|
||||
*px = (tty->sx - 1) / 2 - w / 2;
|
||||
else if (strcmp(xp, "R") == 0)
|
||||
*px = tty->sx - 1;
|
||||
else if (strcmp(xp, "P") == 0) {
|
||||
tty_window_offset(&tc->tty, &ox, &oy, &sx, &sy);
|
||||
if (wp->xoff >= ox)
|
||||
*px = wp->xoff - ox;
|
||||
/*
|
||||
* If there are any status lines, add this window position and the
|
||||
* status line position.
|
||||
*/
|
||||
top = status_at_line(tc);
|
||||
if (top != -1) {
|
||||
lines = status_line_size(tc);
|
||||
if (top == 0)
|
||||
top = lines;
|
||||
else
|
||||
*px = 0;
|
||||
} else if (strcmp(xp, "M") == 0) {
|
||||
if (event->m.valid && event->m.x > w / 2)
|
||||
*px = event->m.x - w / 2;
|
||||
else
|
||||
*px = 0;
|
||||
} else if (strcmp(xp, "W") == 0) {
|
||||
if (status_at_line(tc) == -1)
|
||||
*px = 0;
|
||||
else {
|
||||
top = 0;
|
||||
position = options_get_number(s->options, "status-position");
|
||||
|
||||
for (line = 0; line < lines; line++) {
|
||||
ranges = &tc->status.entries[line].ranges;
|
||||
TAILQ_FOREACH(sr, ranges, entry) {
|
||||
if (sr->type != STYLE_RANGE_WINDOW)
|
||||
continue;
|
||||
@ -116,61 +119,137 @@ cmd_display_menu_get_position(struct client *tc, struct cmdq_item *item,
|
||||
break;
|
||||
}
|
||||
if (sr != NULL)
|
||||
*px = sr->start;
|
||||
else
|
||||
*px = 0;
|
||||
break;
|
||||
}
|
||||
if (line == lines)
|
||||
ranges = &tc->status.entries[0].ranges;
|
||||
|
||||
if (sr != NULL) {
|
||||
format_add(ft, "popup_window_status_line_x", "%u",
|
||||
sr->start);
|
||||
if (position == 0) {
|
||||
format_add(ft, "popup_window_status_line_y",
|
||||
"%u", line + 1 + h);
|
||||
} else {
|
||||
format_add(ft, "popup_window_status_line_y",
|
||||
"%u", tty->sy - lines + line);
|
||||
}
|
||||
}
|
||||
|
||||
if (position == 0)
|
||||
format_add(ft, "popup_status_line_y", "%u", lines + h);
|
||||
else {
|
||||
format_add(ft, "popup_status_line_y", "%u",
|
||||
tty->sy - lines);
|
||||
}
|
||||
} else
|
||||
*px = strtoul(xp, NULL, 10);
|
||||
if ((*px) + w >= tty->sx)
|
||||
*px = tty->sx - w;
|
||||
top = 0;
|
||||
|
||||
/* Popup width and height. */
|
||||
format_add(ft, "popup_width", "%u", w);
|
||||
format_add(ft, "popup_height", "%u", h);
|
||||
|
||||
/* Position so popup is in the centre. */
|
||||
n = (long)(tty->sx - 1) / 2 - w / 2;
|
||||
if (n < 0)
|
||||
format_add(ft, "popup_centre_x", "%u", 0);
|
||||
else
|
||||
format_add(ft, "popup_centre_x", "%ld", n);
|
||||
n = (tty->sy - 1) / 2 + h / 2;
|
||||
if (n >= tty->sy)
|
||||
format_add(ft, "popup_centre_y", "%u", tty->sy - h);
|
||||
else
|
||||
format_add(ft, "popup_centre_y", "%ld", n);
|
||||
|
||||
/* Position of popup relative to mouse. */
|
||||
if (event->m.valid) {
|
||||
n = (long)event->m.x - w / 2;
|
||||
if (n < 0)
|
||||
format_add(ft, "popup_mouse_centre_x", "%u", 0);
|
||||
else
|
||||
format_add(ft, "popup_mouse_centre_x", "%ld", n);
|
||||
n = event->m.y - h / 2;
|
||||
if (n + h >= tty->sy) {
|
||||
format_add(ft, "popup_mouse_centre_y", "%u",
|
||||
tty->sy - h);
|
||||
} else
|
||||
format_add(ft, "popup_mouse_centre_y", "%ld", n);
|
||||
n = (long)event->m.y + h;
|
||||
if (n + h >= tty->sy)
|
||||
format_add(ft, "popup_mouse_top", "%u", tty->sy - h);
|
||||
else
|
||||
format_add(ft, "popup_mouse_top", "%ld", n);
|
||||
n = event->m.y - h;
|
||||
if (n < 0)
|
||||
format_add(ft, "popup_mouse_bottom", "%u", 0);
|
||||
else
|
||||
format_add(ft, "popup_mouse_bottom", "%ld", n);
|
||||
}
|
||||
|
||||
/* Position in pane. */
|
||||
tty_window_offset(&tc->tty, &ox, &oy, &sx, &sy);
|
||||
n = top + wp->yoff - oy + h;
|
||||
if (n >= tty->sy)
|
||||
format_add(ft, "popup_pane_top", "%u", tty->sy - h);
|
||||
else
|
||||
format_add(ft, "popup_pane_top", "%ld", n);
|
||||
format_add(ft, "popup_pane_bottom", "%u", top + wp->yoff + wp->sy - oy);
|
||||
format_add(ft, "popup_pane_left", "%u", wp->xoff - ox);
|
||||
n = (long)wp->xoff + wp->sx - ox - w;
|
||||
if (n < 0)
|
||||
format_add(ft, "popup_pane_right", "%u", 0);
|
||||
else
|
||||
format_add(ft, "popup_pane_right", "%ld", n);
|
||||
|
||||
/* Expand horizontal position. */
|
||||
xp = args_get(args, 'x');
|
||||
if (xp == NULL || strcmp(xp, "C") == 0)
|
||||
xp = "#{popup_centre_x}";
|
||||
else if (strcmp(xp, "R") == 0)
|
||||
xp = "#{popup_right}";
|
||||
else if (strcmp(xp, "P") == 0)
|
||||
xp = "#{popup_pane_left}";
|
||||
else if (strcmp(xp, "M") == 0)
|
||||
xp = "#{popup_mouse_centre_x}";
|
||||
else if (strcmp(xp, "W") == 0)
|
||||
xp = "#{popup_window_status_line_x}";
|
||||
p = format_expand(ft, xp);
|
||||
n = strtol(p, NULL, 10);
|
||||
if (n + w >= tty->sx)
|
||||
n = tty->sx - w;
|
||||
else if (n < 0)
|
||||
n = 0;
|
||||
*px = n;
|
||||
log_debug("%s: -x: %s = %s = %u", __func__, xp, p, *px);
|
||||
free(p);
|
||||
|
||||
/* Expand vertical position */
|
||||
yp = args_get(args, 'y');
|
||||
if (yp == NULL || strcmp(yp, "C") == 0)
|
||||
*py = (tty->sy - 1) / 2 + h / 2;
|
||||
else if (strcmp(yp, "P") == 0) {
|
||||
tty_window_offset(&tc->tty, &ox, &oy, &sx, &sy);
|
||||
if (wp->yoff + wp->sy >= oy)
|
||||
*py = wp->yoff + wp->sy - oy;
|
||||
else
|
||||
*py = 0;
|
||||
} else if (strcmp(yp, "M") == 0) {
|
||||
if (event->m.valid)
|
||||
*py = event->m.y + h;
|
||||
else
|
||||
*py = 0;
|
||||
} else if (strcmp(yp, "S") == 0) {
|
||||
if (options_get_number(s->options, "status-position") == 0) {
|
||||
if (lines != 0)
|
||||
*py = lines + h;
|
||||
else
|
||||
*py = 0;
|
||||
} else {
|
||||
if (lines != 0)
|
||||
*py = tty->sy - lines;
|
||||
else
|
||||
*py = tty->sy;
|
||||
}
|
||||
} else if (strcmp(yp, "W") == 0) {
|
||||
if (options_get_number(s->options, "status-position") == 0) {
|
||||
if (lines != 0)
|
||||
*py = line + 1 + h;
|
||||
else
|
||||
*py = 0;
|
||||
} else {
|
||||
if (lines != 0)
|
||||
*py = tty->sy - lines + line;
|
||||
else
|
||||
*py = tty->sy;
|
||||
}
|
||||
} else
|
||||
*py = strtoul(yp, NULL, 10);
|
||||
if (*py < h)
|
||||
*py = 0;
|
||||
yp = "#{popup_centre_y}";
|
||||
else if (strcmp(yp, "P") == 0)
|
||||
yp = "#{popup_pane_bottom}";
|
||||
else if (strcmp(yp, "M") == 0)
|
||||
yp = "#{popup_mouse_top}";
|
||||
else if (strcmp(yp, "S") == 0)
|
||||
yp = "#{popup_status_line_y}";
|
||||
else if (strcmp(yp, "W") == 0)
|
||||
yp = "#{popup_window_status_line_y}";
|
||||
p = format_expand(ft, yp);
|
||||
n = strtol(p, NULL, 10);
|
||||
if (n < h)
|
||||
n = 0;
|
||||
else
|
||||
*py -= h;
|
||||
if ((*py) + h >= tty->sy)
|
||||
*py = tty->sy - h;
|
||||
n -= h;
|
||||
if (n + h >= tty->sy)
|
||||
n = tty->sy - h;
|
||||
else if (n < 0)
|
||||
n = 0;
|
||||
*py = n;
|
||||
log_debug("%s: -y: %s = %s = %u", __func__, yp, p, *py);
|
||||
free(p);
|
||||
|
||||
return (1);
|
||||
}
|
||||
|
||||
static enum cmd_retval
|
||||
@ -226,8 +305,11 @@ cmd_display_menu_exec(struct cmd *self, struct cmdq_item *item)
|
||||
menu_free(menu);
|
||||
return (CMD_RETURN_NORMAL);
|
||||
}
|
||||
cmd_display_menu_get_position(tc, item, args, &px, &py, menu->width + 4,
|
||||
menu->count + 2);
|
||||
if (!cmd_display_menu_get_position(tc, item, args, &px, &py,
|
||||
menu->width + 4, menu->count + 2)) {
|
||||
menu_free(menu);
|
||||
return (CMD_RETURN_NORMAL);
|
||||
}
|
||||
|
||||
if (args_has(args, 'O'))
|
||||
flags |= MENU_STAYOPEN;
|
||||
@ -244,13 +326,14 @@ cmd_display_popup_exec(struct cmd *self, struct cmdq_item *item)
|
||||
{
|
||||
struct args *args = cmd_get_args(self);
|
||||
struct cmd_find_state *target = cmdq_get_target(item);
|
||||
struct session *s = target->s;
|
||||
struct client *tc = cmdq_get_target_client(item);
|
||||
struct tty *tty = &tc->tty;
|
||||
const char *value, *cmd = NULL, **lines = NULL;
|
||||
const char *value, *shell[] = { NULL, NULL };
|
||||
const char *shellcmd = NULL;
|
||||
char *cwd, *cause;
|
||||
int flags = 0;
|
||||
u_int px, py, w, h, nlines = 0;
|
||||
char *cwd, *cause, **argv = args->argv;
|
||||
int flags = 0, argc = args->argc;
|
||||
u_int px, py, w, h;
|
||||
|
||||
if (args_has(args, 'C')) {
|
||||
server_client_clear_overlay(tc);
|
||||
@ -259,17 +342,7 @@ cmd_display_popup_exec(struct cmd *self, struct cmdq_item *item)
|
||||
if (tc->overlay_draw != NULL)
|
||||
return (CMD_RETURN_NORMAL);
|
||||
|
||||
if (args->argc >= 1)
|
||||
cmd = args->argv[0];
|
||||
if (args->argc >= 2) {
|
||||
lines = (const char **)args->argv + 1;
|
||||
nlines = args->argc - 1;
|
||||
}
|
||||
|
||||
if (nlines != 0)
|
||||
h = popup_height(nlines, lines) + 2;
|
||||
else
|
||||
h = tty->sy / 2;
|
||||
h = tty->sy / 2;
|
||||
if (args_has(args, 'h')) {
|
||||
h = args_percentage(args, 'h', 1, tty->sy, tty->sy, &cause);
|
||||
if (cause != NULL) {
|
||||
@ -279,10 +352,7 @@ cmd_display_popup_exec(struct cmd *self, struct cmdq_item *item)
|
||||
}
|
||||
}
|
||||
|
||||
if (nlines != 0)
|
||||
w = popup_width(item, nlines, lines, tc, target) + 2;
|
||||
else
|
||||
w = tty->sx / 2;
|
||||
w = tty->sx / 2;
|
||||
if (args_has(args, 'w')) {
|
||||
w = args_percentage(args, 'w', 1, tty->sx, tty->sx, &cause);
|
||||
if (cause != NULL) {
|
||||
@ -296,26 +366,33 @@ cmd_display_popup_exec(struct cmd *self, struct cmdq_item *item)
|
||||
w = tty->sx - 1;
|
||||
if (h > tty->sy - 1)
|
||||
h = tty->sy - 1;
|
||||
cmd_display_menu_get_position(tc, item, args, &px, &py, w, h);
|
||||
if (!cmd_display_menu_get_position(tc, item, args, &px, &py, w, h))
|
||||
return (CMD_RETURN_NORMAL);
|
||||
|
||||
value = args_get(args, 'd');
|
||||
if (value != NULL)
|
||||
cwd = format_single_from_target(item, value);
|
||||
else
|
||||
cwd = xstrdup(server_client_get_cwd(tc, target->s));
|
||||
cwd = xstrdup(server_client_get_cwd(tc, s));
|
||||
if (argc == 0)
|
||||
shellcmd = options_get_string(s->options, "default-command");
|
||||
else if (argc == 1)
|
||||
shellcmd = argv[0];
|
||||
if (argc <= 1 && (shellcmd == NULL || *shellcmd == '\0')) {
|
||||
shellcmd = NULL;
|
||||
shell[0] = options_get_string(s->options, "default-shell");
|
||||
if (!checkshell(shell[0]))
|
||||
shell[0] = _PATH_BSHELL;
|
||||
argc = 1;
|
||||
argv = (char**)shell;
|
||||
}
|
||||
|
||||
value = args_get(args, 'R');
|
||||
if (value != NULL)
|
||||
shellcmd = format_single_from_target(item, value);
|
||||
|
||||
if (args_has(args, 'K'))
|
||||
flags |= POPUP_WRITEKEYS;
|
||||
if (args_has(args, 'E') > 1)
|
||||
flags |= POPUP_CLOSEEXITZERO;
|
||||
else if (args_has(args, 'E'))
|
||||
flags |= POPUP_CLOSEEXIT;
|
||||
if (popup_display(flags, item, px, py, w, h, nlines, lines, shellcmd,
|
||||
cmd, cwd, tc, target, NULL, NULL) != 0)
|
||||
if (popup_display(flags, item, px, py, w, h, shellcmd, argc, argv, cwd,
|
||||
tc, s, NULL, NULL) != 0)
|
||||
return (CMD_RETURN_NORMAL);
|
||||
return (CMD_RETURN_WAIT);
|
||||
}
|
||||
|
@ -34,8 +34,8 @@ const struct cmd_entry cmd_display_panes_entry = {
|
||||
.name = "display-panes",
|
||||
.alias = "displayp",
|
||||
|
||||
.args = { "bd:t:", 0, 1 },
|
||||
.usage = "[-b] [-d duration] " CMD_TARGET_CLIENT_USAGE " [template]",
|
||||
.args = { "bd:Nt:", 0, 1 },
|
||||
.usage = "[-bN] [-d duration] " CMD_TARGET_CLIENT_USAGE " [template]",
|
||||
|
||||
.flags = CMD_AFTERHOOK|CMD_CLIENT_TFLAG,
|
||||
.exec = cmd_display_panes_exec
|
||||
@ -284,8 +284,15 @@ cmd_display_panes_exec(struct cmd *self, struct cmdq_item *item)
|
||||
else
|
||||
cdata->item = item;
|
||||
|
||||
server_client_set_overlay(tc, delay, NULL, NULL, cmd_display_panes_draw,
|
||||
cmd_display_panes_key, cmd_display_panes_free, cdata);
|
||||
if (args_has(args, 'N')) {
|
||||
server_client_set_overlay(tc, delay, NULL, NULL,
|
||||
cmd_display_panes_draw, NULL, cmd_display_panes_free,
|
||||
cdata);
|
||||
} else {
|
||||
server_client_set_overlay(tc, delay, NULL, NULL,
|
||||
cmd_display_panes_draw, cmd_display_panes_key,
|
||||
cmd_display_panes_free, cdata);
|
||||
}
|
||||
|
||||
if (args_has(args, 'b'))
|
||||
return (CMD_RETURN_NORMAL);
|
||||
|
@ -128,7 +128,7 @@ cmd_if_shell_exec(struct cmd *self, struct cmdq_item *item)
|
||||
cdata->input.c->references++;
|
||||
cmd_find_copy_state(&cdata->input.fs, target);
|
||||
|
||||
if (job_run(shellcmd, s,
|
||||
if (job_run(shellcmd, 0, NULL, s,
|
||||
server_client_get_cwd(cmdq_get_client(item), s), NULL,
|
||||
cmd_if_shell_callback, cmd_if_shell_free, cdata, 0, -1,
|
||||
-1) == NULL) {
|
||||
|
@ -142,7 +142,10 @@ cmd_join_pane_exec(struct cmd *self, struct cmdq_item *item)
|
||||
src_wp->window = dst_w;
|
||||
options_set_parent(src_wp->options, dst_w->options);
|
||||
src_wp->flags |= PANE_STYLECHANGED;
|
||||
TAILQ_INSERT_AFTER(&dst_w->panes, dst_wp, src_wp, entry);
|
||||
if (flags & SPAWN_BEFORE)
|
||||
TAILQ_INSERT_BEFORE(dst_wp, src_wp, entry);
|
||||
else
|
||||
TAILQ_INSERT_AFTER(&dst_w->panes, dst_wp, src_wp, entry);
|
||||
layout_assign_pane(lc, src_wp);
|
||||
|
||||
recalculate_sizes();
|
||||
|
@ -28,14 +28,14 @@
|
||||
*/
|
||||
|
||||
#define LIST_WINDOWS_TEMPLATE \
|
||||
"#{window_index}: #{window_name}#{window_flags} " \
|
||||
"#{window_index}: #{window_name}#{window_raw_flags} " \
|
||||
"(#{window_panes} panes) " \
|
||||
"[#{window_width}x#{window_height}] " \
|
||||
"[layout #{window_layout}] #{window_id}" \
|
||||
"#{?window_active, (active),}";
|
||||
#define LIST_WINDOWS_WITH_SESSION_TEMPLATE \
|
||||
"#{session_name}:" \
|
||||
"#{window_index}: #{window_name}#{window_flags} " \
|
||||
"#{window_index}: #{window_name}#{window_raw_flags} " \
|
||||
"(#{window_panes} panes) " \
|
||||
"[#{window_width}x#{window_height}] "
|
||||
|
||||
|
@ -38,8 +38,8 @@ const struct cmd_entry cmd_new_window_entry = {
|
||||
.name = "new-window",
|
||||
.alias = "neww",
|
||||
|
||||
.args = { "abc:de:F:kn:Pt:", 0, -1 },
|
||||
.usage = "[-abdkP] [-c start-directory] [-e environment] [-F format] "
|
||||
.args = { "abc:de:F:kn:PSt:", 0, -1 },
|
||||
.usage = "[-abdkPS] [-c start-directory] [-e environment] [-F format] "
|
||||
"[-n window-name] " CMD_TARGET_WINDOW_USAGE " [command]",
|
||||
|
||||
.target = { 't', CMD_FIND_WINDOW, CMD_FIND_WINDOW_INDEX },
|
||||
@ -52,6 +52,7 @@ static enum cmd_retval
|
||||
cmd_new_window_exec(struct cmd *self, struct cmdq_item *item)
|
||||
{
|
||||
struct args *args = cmd_get_args(self);
|
||||
struct client *c = cmdq_get_client(item);
|
||||
struct cmd_find_state *current = cmdq_get_current(item);
|
||||
struct cmd_find_state *target = cmdq_get_target(item);
|
||||
struct spawn_context sc;
|
||||
@ -59,12 +60,41 @@ cmd_new_window_exec(struct cmd *self, struct cmdq_item *item)
|
||||
struct session *s = target->s;
|
||||
struct winlink *wl = target->wl;
|
||||
int idx = target->idx, before;
|
||||
struct winlink *new_wl;
|
||||
struct winlink *new_wl = NULL;
|
||||
char *cause = NULL, *cp;
|
||||
const char *template, *add;
|
||||
const char *template, *add, *name;
|
||||
struct cmd_find_state fs;
|
||||
struct args_value *value;
|
||||
|
||||
/*
|
||||
* If -S and -n are given and -t is not and a single window with this
|
||||
* name already exists, select it.
|
||||
*/
|
||||
name = args_get(args, 'n');
|
||||
if (args_has(args, 'S') && name != NULL && target->idx == -1) {
|
||||
RB_FOREACH(wl, winlinks, &s->windows) {
|
||||
if (strcmp(wl->window->name, name) != 0)
|
||||
continue;
|
||||
if (new_wl == NULL) {
|
||||
new_wl = wl;
|
||||
continue;
|
||||
}
|
||||
cmdq_error(item, "multiple windows named %s", name);
|
||||
return (CMD_RETURN_ERROR);
|
||||
}
|
||||
if (new_wl != NULL) {
|
||||
if (args_has(args, 'd'))
|
||||
return (CMD_RETURN_NORMAL);
|
||||
if (session_set_current(s, new_wl) == 0)
|
||||
server_redraw_session(s);
|
||||
if (c != NULL && c->session != NULL)
|
||||
s->curw->window->latest = c;
|
||||
recalculate_sizes();
|
||||
return (CMD_RETURN_NORMAL);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
before = args_has(args, 'b');
|
||||
if (args_has(args, 'a') || before) {
|
||||
idx = winlink_shuffle_up(s, wl, before);
|
||||
|
@ -1505,8 +1505,12 @@ yylex_token(int ch)
|
||||
state == NONE)
|
||||
break;
|
||||
|
||||
/* Spaces and comments inside quotes after \n are removed. */
|
||||
/*
|
||||
* Spaces and comments inside quotes after \n are removed but
|
||||
* the \n is left.
|
||||
*/
|
||||
if (ch == '\n' && state != NONE) {
|
||||
yylex_append1(&buf, &len, '\n');
|
||||
while ((ch = yylex_getc()) == ' ' || ch == '\t')
|
||||
/* nothing */;
|
||||
if (ch != '#')
|
||||
|
@ -768,7 +768,11 @@ cmdq_running(struct client *c)
|
||||
{
|
||||
struct cmdq_list *queue = cmdq_get(c);
|
||||
|
||||
return (queue->item);
|
||||
if (queue->item == NULL)
|
||||
return (NULL);
|
||||
if (queue->item->flags & CMDQ_WAITING)
|
||||
return (NULL);
|
||||
return (queue->item);
|
||||
}
|
||||
|
||||
/* Print a guard line. */
|
||||
|
@ -40,8 +40,8 @@ const struct cmd_entry cmd_run_shell_entry = {
|
||||
.name = "run-shell",
|
||||
.alias = "run",
|
||||
|
||||
.args = { "bd:t:", 0, 1 },
|
||||
.usage = "[-b] [-d delay] " CMD_TARGET_PANE_USAGE " [shell-command]",
|
||||
.args = { "bd:Ct:", 0, 1 },
|
||||
.usage = "[-bC] [-d delay] " CMD_TARGET_PANE_USAGE " [shell-command]",
|
||||
|
||||
.target = { 't', CMD_FIND_PANE, CMD_FIND_CANFAIL },
|
||||
|
||||
@ -50,13 +50,16 @@ const struct cmd_entry cmd_run_shell_entry = {
|
||||
};
|
||||
|
||||
struct cmd_run_shell_data {
|
||||
struct client *client;
|
||||
char *cmd;
|
||||
int shell;
|
||||
char *cwd;
|
||||
struct cmdq_item *item;
|
||||
struct session *s;
|
||||
int wp_id;
|
||||
struct event timer;
|
||||
int flags;
|
||||
struct cmd_parse_input pi;
|
||||
};
|
||||
|
||||
static void
|
||||
@ -93,49 +96,69 @@ cmd_run_shell_exec(struct cmd *self, struct cmdq_item *item)
|
||||
struct args *args = cmd_get_args(self);
|
||||
struct cmd_find_state *target = cmdq_get_target(item);
|
||||
struct cmd_run_shell_data *cdata;
|
||||
struct client *tc = cmdq_get_target_client(item);
|
||||
struct session *s = target->s;
|
||||
struct window_pane *wp = target->wp;
|
||||
const char *delay;
|
||||
double d;
|
||||
struct timeval tv;
|
||||
char *end;
|
||||
int wait = !args_has(args, 'b');
|
||||
|
||||
if ((delay = args_get(args, 'd')) != NULL) {
|
||||
d = strtod(delay, &end);
|
||||
if (*end != '\0') {
|
||||
cmdq_error(item, "invalid delay time: %s", delay);
|
||||
return (CMD_RETURN_ERROR);
|
||||
}
|
||||
} else if (args->argc == 0)
|
||||
return (CMD_RETURN_NORMAL);
|
||||
|
||||
cdata = xcalloc(1, sizeof *cdata);
|
||||
if (args->argc != 0)
|
||||
cdata->cmd = format_single_from_target(item, args->argv[0]);
|
||||
|
||||
cdata->shell = !args_has(args, 'C');
|
||||
if (!cdata->shell) {
|
||||
memset(&cdata->pi, 0, sizeof cdata->pi);
|
||||
cmd_get_source(self, &cdata->pi.file, &cdata->pi.line);
|
||||
if (wait)
|
||||
cdata->pi.item = item;
|
||||
cdata->pi.c = tc;
|
||||
cmd_find_copy_state(&cdata->pi.fs, target);
|
||||
}
|
||||
|
||||
if (args_has(args, 't') && wp != NULL)
|
||||
cdata->wp_id = wp->id;
|
||||
else
|
||||
cdata->wp_id = -1;
|
||||
|
||||
if (!args_has(args, 'b'))
|
||||
if (wait) {
|
||||
cdata->client = cmdq_get_client(item);
|
||||
cdata->item = item;
|
||||
else
|
||||
} else {
|
||||
cdata->client = tc;
|
||||
cdata->flags |= JOB_NOWAIT;
|
||||
}
|
||||
if (cdata->client != NULL)
|
||||
cdata->client->references++;
|
||||
|
||||
cdata->cwd = xstrdup(server_client_get_cwd(cmdq_get_client(item), s));
|
||||
|
||||
cdata->s = s;
|
||||
if (s != NULL)
|
||||
session_add_ref(s, __func__);
|
||||
|
||||
evtimer_set(&cdata->timer, cmd_run_shell_timer, cdata);
|
||||
|
||||
if ((delay = args_get(args, 'd')) != NULL) {
|
||||
d = strtod(delay, &end);
|
||||
if (*end != '\0') {
|
||||
cmdq_error(item, "invalid delay time: %s", delay);
|
||||
cmd_run_shell_free(cdata);
|
||||
return (CMD_RETURN_ERROR);
|
||||
}
|
||||
if (delay != NULL) {
|
||||
timerclear(&tv);
|
||||
tv.tv_sec = (time_t)d;
|
||||
tv.tv_usec = (d - (double)tv.tv_sec) * 1000000U;
|
||||
evtimer_add(&cdata->timer, &tv);
|
||||
} else
|
||||
cmd_run_shell_timer(-1, 0, cdata);
|
||||
event_active(&cdata->timer, EV_TIMEOUT, 1);
|
||||
|
||||
if (args_has(args, 'b'))
|
||||
if (!wait)
|
||||
return (CMD_RETURN_NORMAL);
|
||||
return (CMD_RETURN_WAIT);
|
||||
}
|
||||
@ -144,17 +167,37 @@ static void
|
||||
cmd_run_shell_timer(__unused int fd, __unused short events, void* arg)
|
||||
{
|
||||
struct cmd_run_shell_data *cdata = arg;
|
||||
struct client *c = cdata->client;
|
||||
const char *cmd = cdata->cmd;
|
||||
char *error;
|
||||
struct cmdq_item *item = cdata->item;
|
||||
enum cmd_parse_status status;
|
||||
|
||||
if (cdata->cmd != NULL) {
|
||||
if (job_run(cdata->cmd, cdata->s, cdata->cwd, NULL,
|
||||
if (cmd != NULL && cdata->shell) {
|
||||
if (job_run(cmd, 0, NULL, cdata->s, cdata->cwd, NULL,
|
||||
cmd_run_shell_callback, cmd_run_shell_free, cdata,
|
||||
cdata->flags, -1, -1) == NULL)
|
||||
cmd_run_shell_free(cdata);
|
||||
} else {
|
||||
if (cdata->item != NULL)
|
||||
cmdq_continue(cdata->item);
|
||||
cmd_run_shell_free(cdata);
|
||||
return;
|
||||
}
|
||||
|
||||
if (cmd != NULL) {
|
||||
if (item != NULL) {
|
||||
status = cmd_parse_and_insert(cmd, &cdata->pi, item,
|
||||
cmdq_get_state(item), &error);
|
||||
} else {
|
||||
status = cmd_parse_and_append(cmd, &cdata->pi, c, NULL,
|
||||
&error);
|
||||
}
|
||||
if (status == CMD_PARSE_ERROR) {
|
||||
cmdq_error(cdata->item, "%s", error);
|
||||
free(error);
|
||||
}
|
||||
}
|
||||
|
||||
if (cdata->item != NULL)
|
||||
cmdq_continue(cdata->item);
|
||||
cmd_run_shell_free(cdata);
|
||||
}
|
||||
|
||||
static void
|
||||
@ -215,6 +258,8 @@ cmd_run_shell_free(void *data)
|
||||
evtimer_del(&cdata->timer);
|
||||
if (cdata->s != NULL)
|
||||
session_remove_ref(cdata->s, __func__);
|
||||
if (cdata->client != NULL)
|
||||
server_client_unref(cdata->client);
|
||||
free(cdata->cwd);
|
||||
free(cdata->cmd);
|
||||
free(cdata);
|
||||
|
@ -108,7 +108,7 @@ cmd_save_buffer_exec(struct cmd *self, struct cmdq_item *item)
|
||||
if (args_has(args, 'a'))
|
||||
flags = O_APPEND;
|
||||
else
|
||||
flags = 0;
|
||||
flags = O_TRUNC;
|
||||
file_write(cmdq_get_client(item), path, flags, bufdata, bufsize,
|
||||
cmd_save_buffer_done, item);
|
||||
free(path);
|
||||
|
@ -108,11 +108,15 @@ cmd_select_pane_exec(struct cmd *self, struct cmdq_item *item)
|
||||
cmdq_error(item, "no last pane");
|
||||
return (CMD_RETURN_ERROR);
|
||||
}
|
||||
if (args_has(args, 'e'))
|
||||
if (args_has(args, 'e')) {
|
||||
lastwp->flags &= ~PANE_INPUTOFF;
|
||||
else if (args_has(args, 'd'))
|
||||
server_redraw_window_borders(lastwp->window);
|
||||
server_status_window(lastwp->window);
|
||||
} else if (args_has(args, 'd')) {
|
||||
lastwp->flags |= PANE_INPUTOFF;
|
||||
else {
|
||||
server_redraw_window_borders(lastwp->window);
|
||||
server_status_window(lastwp->window);
|
||||
} else {
|
||||
if (window_push_zoom(w, args_has(args, 'Z')))
|
||||
server_redraw_window(w);
|
||||
window_redraw_active_switch(w, lastwp);
|
||||
@ -188,10 +192,14 @@ cmd_select_pane_exec(struct cmd *self, struct cmdq_item *item)
|
||||
|
||||
if (args_has(args, 'e')) {
|
||||
wp->flags &= ~PANE_INPUTOFF;
|
||||
server_redraw_window_borders(wp->window);
|
||||
server_status_window(wp->window);
|
||||
return (CMD_RETURN_NORMAL);
|
||||
}
|
||||
if (args_has(args, 'd')) {
|
||||
wp->flags |= PANE_INPUTOFF;
|
||||
server_redraw_window_borders(wp->window);
|
||||
server_status_window(wp->window);
|
||||
return (CMD_RETURN_NORMAL);
|
||||
}
|
||||
|
||||
|
@ -33,8 +33,8 @@ const struct cmd_entry cmd_set_option_entry = {
|
||||
.name = "set-option",
|
||||
.alias = "set",
|
||||
|
||||
.args = { "aFgopqst:uw", 1, 2 },
|
||||
.usage = "[-aFgopqsuw] " CMD_TARGET_PANE_USAGE " option [value]",
|
||||
.args = { "aFgopqst:uUw", 1, 2 },
|
||||
.usage = "[-aFgopqsuUw] " CMD_TARGET_PANE_USAGE " option [value]",
|
||||
|
||||
.target = { 't', CMD_FIND_PANE, CMD_FIND_CANFAIL },
|
||||
|
||||
@ -74,8 +74,9 @@ cmd_set_option_exec(struct cmd *self, struct cmdq_item *item)
|
||||
struct args *args = cmd_get_args(self);
|
||||
int append = args_has(args, 'a');
|
||||
struct cmd_find_state *target = cmdq_get_target(item);
|
||||
struct window_pane *loop;
|
||||
struct options *oo;
|
||||
struct options_entry *parent, *o;
|
||||
struct options_entry *parent, *o, *po;
|
||||
char *name, *argument, *value = NULL, *cause;
|
||||
int window, idx, already, error, ambiguous;
|
||||
int scope;
|
||||
@ -148,7 +149,19 @@ cmd_set_option_exec(struct cmd *self, struct cmdq_item *item)
|
||||
}
|
||||
|
||||
/* Change the option. */
|
||||
if (args_has(args, 'u')) {
|
||||
if (args_has(args, 'U') && scope == OPTIONS_TABLE_WINDOW) {
|
||||
TAILQ_FOREACH(loop, &target->w->panes, entry) {
|
||||
po = options_get_only(loop->options, name);
|
||||
if (po == NULL)
|
||||
continue;
|
||||
if (options_remove_or_default(po, idx, &cause) != 0) {
|
||||
cmdq_error(item, "%s", cause);
|
||||
free(cause);
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (args_has(args, 'u') || args_has(args, 'U')) {
|
||||
if (o == NULL)
|
||||
goto out;
|
||||
if (options_remove_or_default(o, idx, &cause) != 0) {
|
||||
|
@ -200,11 +200,13 @@ cmd_show_options_all(struct cmd *self, struct cmdq_item *item, int scope,
|
||||
u_int idx;
|
||||
int parent;
|
||||
|
||||
o = options_first(oo);
|
||||
while (o != NULL) {
|
||||
if (options_table_entry(o) == NULL)
|
||||
cmd_show_options_print(self, item, o, -1, 0);
|
||||
o = options_next(o);
|
||||
if (cmd_get_entry(self) != &cmd_show_hooks_entry) {
|
||||
o = options_first(oo);
|
||||
while (o != NULL) {
|
||||
if (options_table_entry(o) == NULL)
|
||||
cmd_show_options_print(self, item, o, -1, 0);
|
||||
o = options_next(o);
|
||||
}
|
||||
}
|
||||
for (oe = options_table; oe->name != NULL; oe++) {
|
||||
if (~oe->scope & scope)
|
||||
|
611
colour.c
611
colour.c
@ -22,6 +22,7 @@
|
||||
#include <ctype.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <math.h>
|
||||
|
||||
#include "tmux.h"
|
||||
|
||||
@ -111,6 +112,9 @@ colour_tostring(int c)
|
||||
static char s[32];
|
||||
u_char r, g, b;
|
||||
|
||||
if (c == -1)
|
||||
return ("invalid");
|
||||
|
||||
if (c & COLOUR_FLAG_RGB) {
|
||||
colour_split_rgb(c, &r, &g, &b);
|
||||
xsnprintf(s, sizeof s, "#%02x%02x%02x", r, g, b);
|
||||
@ -233,7 +237,7 @@ colour_fromstring(const char *s)
|
||||
return (96);
|
||||
if (strcasecmp(s, "brightwhite") == 0 || strcmp(s, "97") == 0)
|
||||
return (97);
|
||||
return (-1);
|
||||
return (colour_byname(s));
|
||||
}
|
||||
|
||||
/* Convert 256 colour to RGB colour. */
|
||||
@ -335,3 +339,608 @@ colour_256to16(int c)
|
||||
|
||||
return (table[c & 0xff]);
|
||||
}
|
||||
|
||||
/* Get colour by X11 colour name. */
|
||||
int
|
||||
colour_byname(const char *name)
|
||||
{
|
||||
static const struct {
|
||||
const char *name;
|
||||
int c;
|
||||
} colours[] = {
|
||||
{ "AliceBlue", 0xf0f8ff },
|
||||
{ "AntiqueWhite", 0xfaebd7 },
|
||||
{ "AntiqueWhite1", 0xffefdb },
|
||||
{ "AntiqueWhite2", 0xeedfcc },
|
||||
{ "AntiqueWhite3", 0xcdc0b0 },
|
||||
{ "AntiqueWhite4", 0x8b8378 },
|
||||
{ "BlanchedAlmond", 0xffebcd },
|
||||
{ "BlueViolet", 0x8a2be2 },
|
||||
{ "CadetBlue", 0x5f9ea0 },
|
||||
{ "CadetBlue1", 0x98f5ff },
|
||||
{ "CadetBlue2", 0x8ee5ee },
|
||||
{ "CadetBlue3", 0x7ac5cd },
|
||||
{ "CadetBlue4", 0x53868b },
|
||||
{ "CornflowerBlue", 0x6495ed },
|
||||
{ "DarkBlue", 0x00008b },
|
||||
{ "DarkCyan", 0x008b8b },
|
||||
{ "DarkGoldenrod", 0xb8860b },
|
||||
{ "DarkGoldenrod1", 0xffb90f },
|
||||
{ "DarkGoldenrod2", 0xeead0e },
|
||||
{ "DarkGoldenrod3", 0xcd950c },
|
||||
{ "DarkGoldenrod4", 0x8b6508 },
|
||||
{ "DarkGray", 0xa9a9a9 },
|
||||
{ "DarkGreen", 0x006400 },
|
||||
{ "DarkGrey", 0xa9a9a9 },
|
||||
{ "DarkKhaki", 0xbdb76b },
|
||||
{ "DarkMagenta", 0x8b008b },
|
||||
{ "DarkOliveGreen", 0x556b2f },
|
||||
{ "DarkOliveGreen1", 0xcaff70 },
|
||||
{ "DarkOliveGreen2", 0xbcee68 },
|
||||
{ "DarkOliveGreen3", 0xa2cd5a },
|
||||
{ "DarkOliveGreen4", 0x6e8b3d },
|
||||
{ "DarkOrange", 0xff8c00 },
|
||||
{ "DarkOrange1", 0xff7f00 },
|
||||
{ "DarkOrange2", 0xee7600 },
|
||||
{ "DarkOrange3", 0xcd6600 },
|
||||
{ "DarkOrange4", 0x8b4500 },
|
||||
{ "DarkOrchid", 0x9932cc },
|
||||
{ "DarkOrchid1", 0xbf3eff },
|
||||
{ "DarkOrchid2", 0xb23aee },
|
||||
{ "DarkOrchid3", 0x9a32cd },
|
||||
{ "DarkOrchid4", 0x68228b },
|
||||
{ "DarkRed", 0x8b0000 },
|
||||
{ "DarkSalmon", 0xe9967a },
|
||||
{ "DarkSeaGreen", 0x8fbc8f },
|
||||
{ "DarkSeaGreen1", 0xc1ffc1 },
|
||||
{ "DarkSeaGreen2", 0xb4eeb4 },
|
||||
{ "DarkSeaGreen3", 0x9bcd9b },
|
||||
{ "DarkSeaGreen4", 0x698b69 },
|
||||
{ "DarkSlateBlue", 0x483d8b },
|
||||
{ "DarkSlateGray", 0x2f4f4f },
|
||||
{ "DarkSlateGray1", 0x97ffff },
|
||||
{ "DarkSlateGray2", 0x8deeee },
|
||||
{ "DarkSlateGray3", 0x79cdcd },
|
||||
{ "DarkSlateGray4", 0x528b8b },
|
||||
{ "DarkSlateGrey", 0x2f4f4f },
|
||||
{ "DarkTurquoise", 0x00ced1 },
|
||||
{ "DarkViolet", 0x9400d3 },
|
||||
{ "DeepPink", 0xff1493 },
|
||||
{ "DeepPink1", 0xff1493 },
|
||||
{ "DeepPink2", 0xee1289 },
|
||||
{ "DeepPink3", 0xcd1076 },
|
||||
{ "DeepPink4", 0x8b0a50 },
|
||||
{ "DeepSkyBlue", 0x00bfff },
|
||||
{ "DeepSkyBlue1", 0x00bfff },
|
||||
{ "DeepSkyBlue2", 0x00b2ee },
|
||||
{ "DeepSkyBlue3", 0x009acd },
|
||||
{ "DeepSkyBlue4", 0x00688b },
|
||||
{ "DimGray", 0x696969 },
|
||||
{ "DimGrey", 0x696969 },
|
||||
{ "DodgerBlue", 0x1e90ff },
|
||||
{ "DodgerBlue1", 0x1e90ff },
|
||||
{ "DodgerBlue2", 0x1c86ee },
|
||||
{ "DodgerBlue3", 0x1874cd },
|
||||
{ "DodgerBlue4", 0x104e8b },
|
||||
{ "FloralWhite", 0xfffaf0 },
|
||||
{ "ForestGreen", 0x228b22 },
|
||||
{ "GhostWhite", 0xf8f8ff },
|
||||
{ "GreenYellow", 0xadff2f },
|
||||
{ "HotPink", 0xff69b4 },
|
||||
{ "HotPink1", 0xff6eb4 },
|
||||
{ "HotPink2", 0xee6aa7 },
|
||||
{ "HotPink3", 0xcd6090 },
|
||||
{ "HotPink4", 0x8b3a62 },
|
||||
{ "IndianRed", 0xcd5c5c },
|
||||
{ "IndianRed1", 0xff6a6a },
|
||||
{ "IndianRed2", 0xee6363 },
|
||||
{ "IndianRed3", 0xcd5555 },
|
||||
{ "IndianRed4", 0x8b3a3a },
|
||||
{ "LavenderBlush", 0xfff0f5 },
|
||||
{ "LavenderBlush1", 0xfff0f5 },
|
||||
{ "LavenderBlush2", 0xeee0e5 },
|
||||
{ "LavenderBlush3", 0xcdc1c5 },
|
||||
{ "LavenderBlush4", 0x8b8386 },
|
||||
{ "LawnGreen", 0x7cfc00 },
|
||||
{ "LemonChiffon", 0xfffacd },
|
||||
{ "LemonChiffon1", 0xfffacd },
|
||||
{ "LemonChiffon2", 0xeee9bf },
|
||||
{ "LemonChiffon3", 0xcdc9a5 },
|
||||
{ "LemonChiffon4", 0x8b8970 },
|
||||
{ "LightBlue", 0xadd8e6 },
|
||||
{ "LightBlue1", 0xbfefff },
|
||||
{ "LightBlue2", 0xb2dfee },
|
||||
{ "LightBlue3", 0x9ac0cd },
|
||||
{ "LightBlue4", 0x68838b },
|
||||
{ "LightCoral", 0xf08080 },
|
||||
{ "LightCyan", 0xe0ffff },
|
||||
{ "LightCyan1", 0xe0ffff },
|
||||
{ "LightCyan2", 0xd1eeee },
|
||||
{ "LightCyan3", 0xb4cdcd },
|
||||
{ "LightCyan4", 0x7a8b8b },
|
||||
{ "LightGoldenrod", 0xeedd82 },
|
||||
{ "LightGoldenrod1", 0xffec8b },
|
||||
{ "LightGoldenrod2", 0xeedc82 },
|
||||
{ "LightGoldenrod3", 0xcdbe70 },
|
||||
{ "LightGoldenrod4", 0x8b814c },
|
||||
{ "LightGoldenrodYellow", 0xfafad2 },
|
||||
{ "LightGray", 0xd3d3d3 },
|
||||
{ "LightGreen", 0x90ee90 },
|
||||
{ "LightGrey", 0xd3d3d3 },
|
||||
{ "LightPink", 0xffb6c1 },
|
||||
{ "LightPink1", 0xffaeb9 },
|
||||
{ "LightPink2", 0xeea2ad },
|
||||
{ "LightPink3", 0xcd8c95 },
|
||||
{ "LightPink4", 0x8b5f65 },
|
||||
{ "LightSalmon", 0xffa07a },
|
||||
{ "LightSalmon1", 0xffa07a },
|
||||
{ "LightSalmon2", 0xee9572 },
|
||||
{ "LightSalmon3", 0xcd8162 },
|
||||
{ "LightSalmon4", 0x8b5742 },
|
||||
{ "LightSeaGreen", 0x20b2aa },
|
||||
{ "LightSkyBlue", 0x87cefa },
|
||||
{ "LightSkyBlue1", 0xb0e2ff },
|
||||
{ "LightSkyBlue2", 0xa4d3ee },
|
||||
{ "LightSkyBlue3", 0x8db6cd },
|
||||
{ "LightSkyBlue4", 0x607b8b },
|
||||
{ "LightSlateBlue", 0x8470ff },
|
||||
{ "LightSlateGray", 0x778899 },
|
||||
{ "LightSlateGrey", 0x778899 },
|
||||
{ "LightSteelBlue", 0xb0c4de },
|
||||
{ "LightSteelBlue1", 0xcae1ff },
|
||||
{ "LightSteelBlue2", 0xbcd2ee },
|
||||
{ "LightSteelBlue3", 0xa2b5cd },
|
||||
{ "LightSteelBlue4", 0x6e7b8b },
|
||||
{ "LightYellow", 0xffffe0 },
|
||||
{ "LightYellow1", 0xffffe0 },
|
||||
{ "LightYellow2", 0xeeeed1 },
|
||||
{ "LightYellow3", 0xcdcdb4 },
|
||||
{ "LightYellow4", 0x8b8b7a },
|
||||
{ "LimeGreen", 0x32cd32 },
|
||||
{ "MediumAquamarine", 0x66cdaa },
|
||||
{ "MediumBlue", 0x0000cd },
|
||||
{ "MediumOrchid", 0xba55d3 },
|
||||
{ "MediumOrchid1", 0xe066ff },
|
||||
{ "MediumOrchid2", 0xd15fee },
|
||||
{ "MediumOrchid3", 0xb452cd },
|
||||
{ "MediumOrchid4", 0x7a378b },
|
||||
{ "MediumPurple", 0x9370db },
|
||||
{ "MediumPurple1", 0xab82ff },
|
||||
{ "MediumPurple2", 0x9f79ee },
|
||||
{ "MediumPurple3", 0x8968cd },
|
||||
{ "MediumPurple4", 0x5d478b },
|
||||
{ "MediumSeaGreen", 0x3cb371 },
|
||||
{ "MediumSlateBlue", 0x7b68ee },
|
||||
{ "MediumSpringGreen", 0x00fa9a },
|
||||
{ "MediumTurquoise", 0x48d1cc },
|
||||
{ "MediumVioletRed", 0xc71585 },
|
||||
{ "MidnightBlue", 0x191970 },
|
||||
{ "MintCream", 0xf5fffa },
|
||||
{ "MistyRose", 0xffe4e1 },
|
||||
{ "MistyRose1", 0xffe4e1 },
|
||||
{ "MistyRose2", 0xeed5d2 },
|
||||
{ "MistyRose3", 0xcdb7b5 },
|
||||
{ "MistyRose4", 0x8b7d7b },
|
||||
{ "NavajoWhite", 0xffdead },
|
||||
{ "NavajoWhite1", 0xffdead },
|
||||
{ "NavajoWhite2", 0xeecfa1 },
|
||||
{ "NavajoWhite3", 0xcdb38b },
|
||||
{ "NavajoWhite4", 0x8b795e },
|
||||
{ "NavyBlue", 0x000080 },
|
||||
{ "OldLace", 0xfdf5e6 },
|
||||
{ "OliveDrab", 0x6b8e23 },
|
||||
{ "OliveDrab1", 0xc0ff3e },
|
||||
{ "OliveDrab2", 0xb3ee3a },
|
||||
{ "OliveDrab3", 0x9acd32 },
|
||||
{ "OliveDrab4", 0x698b22 },
|
||||
{ "OrangeRed", 0xff4500 },
|
||||
{ "OrangeRed1", 0xff4500 },
|
||||
{ "OrangeRed2", 0xee4000 },
|
||||
{ "OrangeRed3", 0xcd3700 },
|
||||
{ "OrangeRed4", 0x8b2500 },
|
||||
{ "PaleGoldenrod", 0xeee8aa },
|
||||
{ "PaleGreen", 0x98fb98 },
|
||||
{ "PaleGreen1", 0x9aff9a },
|
||||
{ "PaleGreen2", 0x90ee90 },
|
||||
{ "PaleGreen3", 0x7ccd7c },
|
||||
{ "PaleGreen4", 0x548b54 },
|
||||
{ "PaleTurquoise", 0xafeeee },
|
||||
{ "PaleTurquoise1", 0xbbffff },
|
||||
{ "PaleTurquoise2", 0xaeeeee },
|
||||
{ "PaleTurquoise3", 0x96cdcd },
|
||||
{ "PaleTurquoise4", 0x668b8b },
|
||||
{ "PaleVioletRed", 0xdb7093 },
|
||||
{ "PaleVioletRed1", 0xff82ab },
|
||||
{ "PaleVioletRed2", 0xee799f },
|
||||
{ "PaleVioletRed3", 0xcd6889 },
|
||||
{ "PaleVioletRed4", 0x8b475d },
|
||||
{ "PapayaWhip", 0xffefd5 },
|
||||
{ "PeachPuff", 0xffdab9 },
|
||||
{ "PeachPuff1", 0xffdab9 },
|
||||
{ "PeachPuff2", 0xeecbad },
|
||||
{ "PeachPuff3", 0xcdaf95 },
|
||||
{ "PeachPuff4", 0x8b7765 },
|
||||
{ "PowderBlue", 0xb0e0e6 },
|
||||
{ "RebeccaPurple", 0x663399 },
|
||||
{ "RosyBrown", 0xbc8f8f },
|
||||
{ "RosyBrown1", 0xffc1c1 },
|
||||
{ "RosyBrown2", 0xeeb4b4 },
|
||||
{ "RosyBrown3", 0xcd9b9b },
|
||||
{ "RosyBrown4", 0x8b6969 },
|
||||
{ "RoyalBlue", 0x4169e1 },
|
||||
{ "RoyalBlue1", 0x4876ff },
|
||||
{ "RoyalBlue2", 0x436eee },
|
||||
{ "RoyalBlue3", 0x3a5fcd },
|
||||
{ "RoyalBlue4", 0x27408b },
|
||||
{ "SaddleBrown", 0x8b4513 },
|
||||
{ "SandyBrown", 0xf4a460 },
|
||||
{ "SeaGreen", 0x2e8b57 },
|
||||
{ "SeaGreen1", 0x54ff9f },
|
||||
{ "SeaGreen2", 0x4eee94 },
|
||||
{ "SeaGreen3", 0x43cd80 },
|
||||
{ "SeaGreen4", 0x2e8b57 },
|
||||
{ "SkyBlue", 0x87ceeb },
|
||||
{ "SkyBlue1", 0x87ceff },
|
||||
{ "SkyBlue2", 0x7ec0ee },
|
||||
{ "SkyBlue3", 0x6ca6cd },
|
||||
{ "SkyBlue4", 0x4a708b },
|
||||
{ "SlateBlue", 0x6a5acd },
|
||||
{ "SlateBlue1", 0x836fff },
|
||||
{ "SlateBlue2", 0x7a67ee },
|
||||
{ "SlateBlue3", 0x6959cd },
|
||||
{ "SlateBlue4", 0x473c8b },
|
||||
{ "SlateGray", 0x708090 },
|
||||
{ "SlateGray1", 0xc6e2ff },
|
||||
{ "SlateGray2", 0xb9d3ee },
|
||||
{ "SlateGray3", 0x9fb6cd },
|
||||
{ "SlateGray4", 0x6c7b8b },
|
||||
{ "SlateGrey", 0x708090 },
|
||||
{ "SpringGreen", 0x00ff7f },
|
||||
{ "SpringGreen1", 0x00ff7f },
|
||||
{ "SpringGreen2", 0x00ee76 },
|
||||
{ "SpringGreen3", 0x00cd66 },
|
||||
{ "SpringGreen4", 0x008b45 },
|
||||
{ "SteelBlue", 0x4682b4 },
|
||||
{ "SteelBlue1", 0x63b8ff },
|
||||
{ "SteelBlue2", 0x5cacee },
|
||||
{ "SteelBlue3", 0x4f94cd },
|
||||
{ "SteelBlue4", 0x36648b },
|
||||
{ "VioletRed", 0xd02090 },
|
||||
{ "VioletRed1", 0xff3e96 },
|
||||
{ "VioletRed2", 0xee3a8c },
|
||||
{ "VioletRed3", 0xcd3278 },
|
||||
{ "VioletRed4", 0x8b2252 },
|
||||
{ "WebGray", 0x808080 },
|
||||
{ "WebGreen", 0x008000 },
|
||||
{ "WebGrey", 0x808080 },
|
||||
{ "WebMaroon", 0x800000 },
|
||||
{ "WebPurple", 0x800080 },
|
||||
{ "WhiteSmoke", 0xf5f5f5 },
|
||||
{ "X11Gray", 0xbebebe },
|
||||
{ "X11Green", 0x00ff00 },
|
||||
{ "X11Grey", 0xbebebe },
|
||||
{ "X11Maroon", 0xb03060 },
|
||||
{ "X11Purple", 0xa020f0 },
|
||||
{ "YellowGreen", 0x9acd32 },
|
||||
{ "alice blue", 0xf0f8ff },
|
||||
{ "antique white", 0xfaebd7 },
|
||||
{ "aqua", 0x00ffff },
|
||||
{ "aquamarine", 0x7fffd4 },
|
||||
{ "aquamarine1", 0x7fffd4 },
|
||||
{ "aquamarine2", 0x76eec6 },
|
||||
{ "aquamarine3", 0x66cdaa },
|
||||
{ "aquamarine4", 0x458b74 },
|
||||
{ "azure", 0xf0ffff },
|
||||
{ "azure1", 0xf0ffff },
|
||||
{ "azure2", 0xe0eeee },
|
||||
{ "azure3", 0xc1cdcd },
|
||||
{ "azure4", 0x838b8b },
|
||||
{ "beige", 0xf5f5dc },
|
||||
{ "bisque", 0xffe4c4 },
|
||||
{ "bisque1", 0xffe4c4 },
|
||||
{ "bisque2", 0xeed5b7 },
|
||||
{ "bisque3", 0xcdb79e },
|
||||
{ "bisque4", 0x8b7d6b },
|
||||
{ "black", 0x000000 },
|
||||
{ "blanched almond", 0xffebcd },
|
||||
{ "blue violet", 0x8a2be2 },
|
||||
{ "blue", 0x0000ff },
|
||||
{ "blue1", 0x0000ff },
|
||||
{ "blue2", 0x0000ee },
|
||||
{ "blue3", 0x0000cd },
|
||||
{ "blue4", 0x00008b },
|
||||
{ "brown", 0xa52a2a },
|
||||
{ "brown1", 0xff4040 },
|
||||
{ "brown2", 0xee3b3b },
|
||||
{ "brown3", 0xcd3333 },
|
||||
{ "brown4", 0x8b2323 },
|
||||
{ "burlywood", 0xdeb887 },
|
||||
{ "burlywood1", 0xffd39b },
|
||||
{ "burlywood2", 0xeec591 },
|
||||
{ "burlywood3", 0xcdaa7d },
|
||||
{ "burlywood4", 0x8b7355 },
|
||||
{ "cadet blue", 0x5f9ea0 },
|
||||
{ "chartreuse", 0x7fff00 },
|
||||
{ "chartreuse1", 0x7fff00 },
|
||||
{ "chartreuse2", 0x76ee00 },
|
||||
{ "chartreuse3", 0x66cd00 },
|
||||
{ "chartreuse4", 0x458b00 },
|
||||
{ "chocolate", 0xd2691e },
|
||||
{ "chocolate1", 0xff7f24 },
|
||||
{ "chocolate2", 0xee7621 },
|
||||
{ "chocolate3", 0xcd661d },
|
||||
{ "chocolate4", 0x8b4513 },
|
||||
{ "coral", 0xff7f50 },
|
||||
{ "coral1", 0xff7256 },
|
||||
{ "coral2", 0xee6a50 },
|
||||
{ "coral3", 0xcd5b45 },
|
||||
{ "coral4", 0x8b3e2f },
|
||||
{ "cornflower blue", 0x6495ed },
|
||||
{ "cornsilk", 0xfff8dc },
|
||||
{ "cornsilk1", 0xfff8dc },
|
||||
{ "cornsilk2", 0xeee8cd },
|
||||
{ "cornsilk3", 0xcdc8b1 },
|
||||
{ "cornsilk4", 0x8b8878 },
|
||||
{ "crimson", 0xdc143c },
|
||||
{ "cyan", 0x00ffff },
|
||||
{ "cyan1", 0x00ffff },
|
||||
{ "cyan2", 0x00eeee },
|
||||
{ "cyan3", 0x00cdcd },
|
||||
{ "cyan4", 0x008b8b },
|
||||
{ "dark blue", 0x00008b },
|
||||
{ "dark cyan", 0x008b8b },
|
||||
{ "dark goldenrod", 0xb8860b },
|
||||
{ "dark gray", 0xa9a9a9 },
|
||||
{ "dark green", 0x006400 },
|
||||
{ "dark grey", 0xa9a9a9 },
|
||||
{ "dark khaki", 0xbdb76b },
|
||||
{ "dark magenta", 0x8b008b },
|
||||
{ "dark olive green", 0x556b2f },
|
||||
{ "dark orange", 0xff8c00 },
|
||||
{ "dark orchid", 0x9932cc },
|
||||
{ "dark red", 0x8b0000 },
|
||||
{ "dark salmon", 0xe9967a },
|
||||
{ "dark sea green", 0x8fbc8f },
|
||||
{ "dark slate blue", 0x483d8b },
|
||||
{ "dark slate gray", 0x2f4f4f },
|
||||
{ "dark slate grey", 0x2f4f4f },
|
||||
{ "dark turquoise", 0x00ced1 },
|
||||
{ "dark violet", 0x9400d3 },
|
||||
{ "deep pink", 0xff1493 },
|
||||
{ "deep sky blue", 0x00bfff },
|
||||
{ "dim gray", 0x696969 },
|
||||
{ "dim grey", 0x696969 },
|
||||
{ "dodger blue", 0x1e90ff },
|
||||
{ "firebrick", 0xb22222 },
|
||||
{ "firebrick1", 0xff3030 },
|
||||
{ "firebrick2", 0xee2c2c },
|
||||
{ "firebrick3", 0xcd2626 },
|
||||
{ "firebrick4", 0x8b1a1a },
|
||||
{ "floral white", 0xfffaf0 },
|
||||
{ "forest green", 0x228b22 },
|
||||
{ "fuchsia", 0xff00ff },
|
||||
{ "gainsboro", 0xdcdcdc },
|
||||
{ "ghost white", 0xf8f8ff },
|
||||
{ "gold", 0xffd700 },
|
||||
{ "gold1", 0xffd700 },
|
||||
{ "gold2", 0xeec900 },
|
||||
{ "gold3", 0xcdad00 },
|
||||
{ "gold4", 0x8b7500 },
|
||||
{ "goldenrod", 0xdaa520 },
|
||||
{ "goldenrod1", 0xffc125 },
|
||||
{ "goldenrod2", 0xeeb422 },
|
||||
{ "goldenrod3", 0xcd9b1d },
|
||||
{ "goldenrod4", 0x8b6914 },
|
||||
{ "green yellow", 0xadff2f },
|
||||
{ "green", 0x00ff00 },
|
||||
{ "green1", 0x00ff00 },
|
||||
{ "green2", 0x00ee00 },
|
||||
{ "green3", 0x00cd00 },
|
||||
{ "green4", 0x008b00 },
|
||||
{ "honeydew", 0xf0fff0 },
|
||||
{ "honeydew1", 0xf0fff0 },
|
||||
{ "honeydew2", 0xe0eee0 },
|
||||
{ "honeydew3", 0xc1cdc1 },
|
||||
{ "honeydew4", 0x838b83 },
|
||||
{ "hot pink", 0xff69b4 },
|
||||
{ "indian red", 0xcd5c5c },
|
||||
{ "indigo", 0x4b0082 },
|
||||
{ "ivory", 0xfffff0 },
|
||||
{ "ivory1", 0xfffff0 },
|
||||
{ "ivory2", 0xeeeee0 },
|
||||
{ "ivory3", 0xcdcdc1 },
|
||||
{ "ivory4", 0x8b8b83 },
|
||||
{ "khaki", 0xf0e68c },
|
||||
{ "khaki1", 0xfff68f },
|
||||
{ "khaki2", 0xeee685 },
|
||||
{ "khaki3", 0xcdc673 },
|
||||
{ "khaki4", 0x8b864e },
|
||||
{ "lavender blush", 0xfff0f5 },
|
||||
{ "lavender", 0xe6e6fa },
|
||||
{ "lawn green", 0x7cfc00 },
|
||||
{ "lemon chiffon", 0xfffacd },
|
||||
{ "light blue", 0xadd8e6 },
|
||||
{ "light coral", 0xf08080 },
|
||||
{ "light cyan", 0xe0ffff },
|
||||
{ "light goldenrod yellow", 0xfafad2 },
|
||||
{ "light goldenrod", 0xeedd82 },
|
||||
{ "light gray", 0xd3d3d3 },
|
||||
{ "light green", 0x90ee90 },
|
||||
{ "light grey", 0xd3d3d3 },
|
||||
{ "light pink", 0xffb6c1 },
|
||||
{ "light salmon", 0xffa07a },
|
||||
{ "light sea green", 0x20b2aa },
|
||||
{ "light sky blue", 0x87cefa },
|
||||
{ "light slate blue", 0x8470ff },
|
||||
{ "light slate gray", 0x778899 },
|
||||
{ "light slate grey", 0x778899 },
|
||||
{ "light steel blue", 0xb0c4de },
|
||||
{ "light yellow", 0xffffe0 },
|
||||
{ "lime green", 0x32cd32 },
|
||||
{ "lime", 0x00ff00 },
|
||||
{ "linen", 0xfaf0e6 },
|
||||
{ "magenta", 0xff00ff },
|
||||
{ "magenta1", 0xff00ff },
|
||||
{ "magenta2", 0xee00ee },
|
||||
{ "magenta3", 0xcd00cd },
|
||||
{ "magenta4", 0x8b008b },
|
||||
{ "maroon", 0xb03060 },
|
||||
{ "maroon1", 0xff34b3 },
|
||||
{ "maroon2", 0xee30a7 },
|
||||
{ "maroon3", 0xcd2990 },
|
||||
{ "maroon4", 0x8b1c62 },
|
||||
{ "medium aquamarine", 0x66cdaa },
|
||||
{ "medium blue", 0x0000cd },
|
||||
{ "medium orchid", 0xba55d3 },
|
||||
{ "medium purple", 0x9370db },
|
||||
{ "medium sea green", 0x3cb371 },
|
||||
{ "medium slate blue", 0x7b68ee },
|
||||
{ "medium spring green", 0x00fa9a },
|
||||
{ "medium turquoise", 0x48d1cc },
|
||||
{ "medium violet red", 0xc71585 },
|
||||
{ "midnight blue", 0x191970 },
|
||||
{ "mint cream", 0xf5fffa },
|
||||
{ "misty rose", 0xffe4e1 },
|
||||
{ "moccasin", 0xffe4b5 },
|
||||
{ "navajo white", 0xffdead },
|
||||
{ "navy blue", 0x000080 },
|
||||
{ "navy", 0x000080 },
|
||||
{ "old lace", 0xfdf5e6 },
|
||||
{ "olive drab", 0x6b8e23 },
|
||||
{ "olive", 0x808000 },
|
||||
{ "orange red", 0xff4500 },
|
||||
{ "orange", 0xffa500 },
|
||||
{ "orange1", 0xffa500 },
|
||||
{ "orange2", 0xee9a00 },
|
||||
{ "orange3", 0xcd8500 },
|
||||
{ "orange4", 0x8b5a00 },
|
||||
{ "orchid", 0xda70d6 },
|
||||
{ "orchid1", 0xff83fa },
|
||||
{ "orchid2", 0xee7ae9 },
|
||||
{ "orchid3", 0xcd69c9 },
|
||||
{ "orchid4", 0x8b4789 },
|
||||
{ "pale goldenrod", 0xeee8aa },
|
||||
{ "pale green", 0x98fb98 },
|
||||
{ "pale turquoise", 0xafeeee },
|
||||
{ "pale violet red", 0xdb7093 },
|
||||
{ "papaya whip", 0xffefd5 },
|
||||
{ "peach puff", 0xffdab9 },
|
||||
{ "peru", 0xcd853f },
|
||||
{ "pink", 0xffc0cb },
|
||||
{ "pink1", 0xffb5c5 },
|
||||
{ "pink2", 0xeea9b8 },
|
||||
{ "pink3", 0xcd919e },
|
||||
{ "pink4", 0x8b636c },
|
||||
{ "plum", 0xdda0dd },
|
||||
{ "plum1", 0xffbbff },
|
||||
{ "plum2", 0xeeaeee },
|
||||
{ "plum3", 0xcd96cd },
|
||||
{ "plum4", 0x8b668b },
|
||||
{ "powder blue", 0xb0e0e6 },
|
||||
{ "purple", 0xa020f0 },
|
||||
{ "purple1", 0x9b30ff },
|
||||
{ "purple2", 0x912cee },
|
||||
{ "purple3", 0x7d26cd },
|
||||
{ "purple4", 0x551a8b },
|
||||
{ "rebecca purple", 0x663399 },
|
||||
{ "red", 0xff0000 },
|
||||
{ "red1", 0xff0000 },
|
||||
{ "red2", 0xee0000 },
|
||||
{ "red3", 0xcd0000 },
|
||||
{ "red4", 0x8b0000 },
|
||||
{ "rosy brown", 0xbc8f8f },
|
||||
{ "royal blue", 0x4169e1 },
|
||||
{ "saddle brown", 0x8b4513 },
|
||||
{ "salmon", 0xfa8072 },
|
||||
{ "salmon1", 0xff8c69 },
|
||||
{ "salmon2", 0xee8262 },
|
||||
{ "salmon3", 0xcd7054 },
|
||||
{ "salmon4", 0x8b4c39 },
|
||||
{ "sandy brown", 0xf4a460 },
|
||||
{ "sea green", 0x2e8b57 },
|
||||
{ "seashell", 0xfff5ee },
|
||||
{ "seashell1", 0xfff5ee },
|
||||
{ "seashell2", 0xeee5de },
|
||||
{ "seashell3", 0xcdc5bf },
|
||||
{ "seashell4", 0x8b8682 },
|
||||
{ "sienna", 0xa0522d },
|
||||
{ "sienna1", 0xff8247 },
|
||||
{ "sienna2", 0xee7942 },
|
||||
{ "sienna3", 0xcd6839 },
|
||||
{ "sienna4", 0x8b4726 },
|
||||
{ "silver", 0xc0c0c0 },
|
||||
{ "sky blue", 0x87ceeb },
|
||||
{ "slate blue", 0x6a5acd },
|
||||
{ "slate gray", 0x708090 },
|
||||
{ "slate grey", 0x708090 },
|
||||
{ "snow", 0xfffafa },
|
||||
{ "snow1", 0xfffafa },
|
||||
{ "snow2", 0xeee9e9 },
|
||||
{ "snow3", 0xcdc9c9 },
|
||||
{ "snow4", 0x8b8989 },
|
||||
{ "spring green", 0x00ff7f },
|
||||
{ "steel blue", 0x4682b4 },
|
||||
{ "tan", 0xd2b48c },
|
||||
{ "tan1", 0xffa54f },
|
||||
{ "tan2", 0xee9a49 },
|
||||
{ "tan3", 0xcd853f },
|
||||
{ "tan4", 0x8b5a2b },
|
||||
{ "teal", 0x008080 },
|
||||
{ "thistle", 0xd8bfd8 },
|
||||
{ "thistle1", 0xffe1ff },
|
||||
{ "thistle2", 0xeed2ee },
|
||||
{ "thistle3", 0xcdb5cd },
|
||||
{ "thistle4", 0x8b7b8b },
|
||||
{ "tomato", 0xff6347 },
|
||||
{ "tomato1", 0xff6347 },
|
||||
{ "tomato2", 0xee5c42 },
|
||||
{ "tomato3", 0xcd4f39 },
|
||||
{ "tomato4", 0x8b3626 },
|
||||
{ "turquoise", 0x40e0d0 },
|
||||
{ "turquoise1", 0x00f5ff },
|
||||
{ "turquoise2", 0x00e5ee },
|
||||
{ "turquoise3", 0x00c5cd },
|
||||
{ "turquoise4", 0x00868b },
|
||||
{ "violet red", 0xd02090 },
|
||||
{ "violet", 0xee82ee },
|
||||
{ "web gray", 0x808080 },
|
||||
{ "web green", 0x008000 },
|
||||
{ "web grey", 0x808080 },
|
||||
{ "web maroon", 0x800000 },
|
||||
{ "web purple", 0x800080 },
|
||||
{ "wheat", 0xf5deb3 },
|
||||
{ "wheat1", 0xffe7ba },
|
||||
{ "wheat2", 0xeed8ae },
|
||||
{ "wheat3", 0xcdba96 },
|
||||
{ "wheat4", 0x8b7e66 },
|
||||
{ "white smoke", 0xf5f5f5 },
|
||||
{ "white", 0xffffff },
|
||||
{ "x11 gray", 0xbebebe },
|
||||
{ "x11 green", 0x00ff00 },
|
||||
{ "x11 grey", 0xbebebe },
|
||||
{ "x11 maroon", 0xb03060 },
|
||||
{ "x11 purple", 0xa020f0 },
|
||||
{ "yellow green", 0x9acd32 },
|
||||
{ "yellow", 0xffff00 },
|
||||
{ "yellow1", 0xffff00 },
|
||||
{ "yellow2", 0xeeee00 },
|
||||
{ "yellow3", 0xcdcd00 },
|
||||
{ "yellow4", 0x8b8b00 }
|
||||
};
|
||||
u_int i;
|
||||
int c;
|
||||
|
||||
if (strncmp(name, "grey", 4) == 0 || strncmp(name, "gray", 4) == 0) {
|
||||
if (!isdigit((u_char)name[4]))
|
||||
return (0xbebebe|COLOUR_FLAG_RGB);
|
||||
c = round(2.55 * atoi(name + 4));
|
||||
if (c < 0 || c > 255)
|
||||
return (-1);
|
||||
return (colour_join_rgb(c, c, c));
|
||||
}
|
||||
for (i = 0; i < nitems(colours); i++) {
|
||||
if (strcasecmp(colours[i].name, name) == 0)
|
||||
return (colours[i].c|COLOUR_FLAG_RGB);
|
||||
}
|
||||
return (-1);
|
||||
}
|
||||
|
37
compat.h
37
compat.h
@ -27,6 +27,19 @@
|
||||
#include <termios.h>
|
||||
#include <wchar.h>
|
||||
|
||||
#ifdef HAVE_EVENT2_EVENT_H
|
||||
#include <event2/event.h>
|
||||
#include <event2/event_compat.h>
|
||||
#include <event2/event_struct.h>
|
||||
#include <event2/buffer.h>
|
||||
#include <event2/buffer_compat.h>
|
||||
#include <event2/bufferevent.h>
|
||||
#include <event2/bufferevent_struct.h>
|
||||
#include <event2/bufferevent_compat.h>
|
||||
#else
|
||||
#include <event.h>
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_MALLOC_TRIM
|
||||
#include <malloc.h>
|
||||
#endif
|
||||
@ -52,6 +65,9 @@
|
||||
#ifndef __packed
|
||||
#define __packed __attribute__ ((__packed__))
|
||||
#endif
|
||||
#ifndef __weak
|
||||
#define __weak __attribute__ ((__weak__))
|
||||
#endif
|
||||
|
||||
#ifndef ECHOPRT
|
||||
#define ECHOPRT 0
|
||||
@ -110,6 +126,10 @@ void warnx(const char *, ...);
|
||||
#define pledge(s, p) (0)
|
||||
#endif
|
||||
|
||||
#ifndef IMAXBEL
|
||||
#define IMAXBEL 0
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_STDINT_H
|
||||
#include <stdint.h>
|
||||
#else
|
||||
@ -245,6 +265,13 @@ void warnx(const char *, ...);
|
||||
#define HOST_NAME_MAX 255
|
||||
#endif
|
||||
|
||||
#ifndef CLOCK_REALTIME
|
||||
#define CLOCK_REALTIME 0
|
||||
#endif
|
||||
#ifndef CLOCK_MONOTONIC
|
||||
#define CLOCK_MONOTONIC CLOCK_REALTIME
|
||||
#endif
|
||||
|
||||
#ifndef HAVE_FLOCK
|
||||
#define LOCK_SH 0
|
||||
#define LOCK_EX 0
|
||||
@ -322,6 +349,11 @@ const char *getprogname(void);
|
||||
void setproctitle(const char *, ...);
|
||||
#endif
|
||||
|
||||
#ifndef HAVE_CLOCK_GETTIME
|
||||
/* clock_gettime.c */
|
||||
int clock_gettime(int, struct timespec *);
|
||||
#endif
|
||||
|
||||
#ifndef HAVE_B64_NTOP
|
||||
/* base64.c */
|
||||
#undef b64_ntop
|
||||
@ -391,6 +423,11 @@ int utf8proc_mbtowc(wchar_t *, const char *, size_t);
|
||||
int utf8proc_wctomb(char *, wchar_t);
|
||||
#endif
|
||||
|
||||
#ifdef NEED_FUZZING
|
||||
/* tmux.c */
|
||||
#define main __weak main
|
||||
#endif
|
||||
|
||||
/* getopt.c */
|
||||
extern int BSDopterr;
|
||||
extern int BSDoptind;
|
||||
|
37
compat/clock_gettime.c
Normal file
37
compat/clock_gettime.c
Normal file
@ -0,0 +1,37 @@
|
||||
/*
|
||||
* Copyright (c) 2021 Nicholas Marriott <nicholas.marriott@gmail.com>
|
||||
*
|
||||
* 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 "compat.h"
|
||||
|
||||
#ifndef TIMEVAL_TO_TIMESPEC
|
||||
#define TIMEVAL_TO_TIMESPEC(tv, ts) do { \
|
||||
(ts)->tv_sec = (tv)->tv_sec; \
|
||||
(ts)->tv_nsec = (tv)->tv_usec * 1000; \
|
||||
} while (0)
|
||||
#endif
|
||||
|
||||
int
|
||||
clock_gettime(int clock, struct timespec *ts)
|
||||
{
|
||||
struct timeval tv;
|
||||
|
||||
gettimeofday(&tv, NULL);
|
||||
TIMEVAL_TO_TIMESPEC(&tv, ts);
|
||||
return 0;
|
||||
}
|
@ -50,6 +50,8 @@
|
||||
# include <libproc.h>
|
||||
#endif
|
||||
|
||||
#include "compat.h"
|
||||
|
||||
#ifndef OPEN_MAX
|
||||
# define OPEN_MAX 256
|
||||
#endif
|
||||
|
82
compat/forkpty-haiku.c
Normal file
82
compat/forkpty-haiku.c
Normal file
@ -0,0 +1,82 @@
|
||||
/*
|
||||
* Copyright (c) 2008 Nicholas Marriott <nicholas.marriott@gmail.com>
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER
|
||||
* IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
|
||||
* OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/ioctl.h>
|
||||
|
||||
#include <fcntl.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "compat.h"
|
||||
|
||||
void fatal(const char *, ...);
|
||||
void fatalx(const char *, ...);
|
||||
|
||||
pid_t
|
||||
forkpty(int *master, char *name, struct termios *tio, struct winsize *ws)
|
||||
{
|
||||
int slave = -1;
|
||||
char *path;
|
||||
pid_t pid;
|
||||
|
||||
if ((*master = open("/dev/ptmx", O_RDWR|O_NOCTTY)) == -1)
|
||||
return (-1);
|
||||
if (grantpt(*master) != 0)
|
||||
goto out;
|
||||
if (unlockpt(*master) != 0)
|
||||
goto out;
|
||||
|
||||
if ((path = ptsname(*master)) == NULL)
|
||||
goto out;
|
||||
if (name != NULL)
|
||||
strlcpy(name, path, TTY_NAME_MAX);
|
||||
if ((slave = open(path, O_RDWR|O_NOCTTY)) == -1)
|
||||
goto out;
|
||||
|
||||
switch (pid = fork()) {
|
||||
case -1:
|
||||
goto out;
|
||||
case 0:
|
||||
close(*master);
|
||||
|
||||
setsid();
|
||||
if (ioctl(slave, TIOCSCTTY, NULL) == -1)
|
||||
fatal("ioctl failed");
|
||||
|
||||
if (tio != NULL && tcsetattr(slave, TCSAFLUSH, tio) == -1)
|
||||
fatal("tcsetattr failed");
|
||||
if (ioctl(slave, TIOCSWINSZ, ws) == -1)
|
||||
fatal("ioctl failed");
|
||||
|
||||
dup2(slave, 0);
|
||||
dup2(slave, 1);
|
||||
dup2(slave, 2);
|
||||
if (slave > 2)
|
||||
close(slave);
|
||||
return (0);
|
||||
}
|
||||
|
||||
close(slave);
|
||||
return (pid);
|
||||
|
||||
out:
|
||||
if (*master != -1)
|
||||
close(*master);
|
||||
if (slave != -1)
|
||||
close(slave);
|
||||
return (-1);
|
||||
}
|
187
configure.ac
187
configure.ac
@ -1,6 +1,6 @@
|
||||
# configure.ac
|
||||
|
||||
AC_INIT([tmux], 3.2-rc3)
|
||||
AC_INIT([tmux], 3.2-rc4)
|
||||
AC_PREREQ([2.60])
|
||||
|
||||
AC_CONFIG_AUX_DIR(etc)
|
||||
@ -21,6 +21,26 @@ SAVED_CFLAGS="$CFLAGS"
|
||||
SAVED_CPPFLAGS="$CPPFLAGS"
|
||||
SAVED_LDFLAGS="$LDFLAGS"
|
||||
|
||||
# Is this oss-fuzz build?
|
||||
AC_ARG_ENABLE(
|
||||
fuzzing,
|
||||
AC_HELP_STRING(--enable-fuzzing, build fuzzers)
|
||||
)
|
||||
AC_ARG_VAR(
|
||||
FUZZING_LIBS,
|
||||
AC_HELP_STRING(libraries to link fuzzing targets with)
|
||||
)
|
||||
|
||||
# Set up convenient fuzzing defaults before initializing compiler.
|
||||
if test "x$enable_fuzzing" = xyes; then
|
||||
AC_DEFINE(NEED_FUZZING)
|
||||
test "x$CC" == x && CC=clang
|
||||
test "x$FUZZING_LIBS" == x && \
|
||||
FUZZING_LIBS="-fsanitize=fuzzer"
|
||||
test "x$SAVED_CFLAGS" == x && \
|
||||
AM_CFLAGS="-g -fsanitize=fuzzer-no-link,address"
|
||||
fi
|
||||
|
||||
# Set up the compiler in two different ways and say yes we may want to install.
|
||||
AC_PROG_CC
|
||||
AM_PROG_CC_C_O
|
||||
@ -54,8 +74,11 @@ if test "x$enable_static" = xyes; then
|
||||
LDFLAGS="$AM_LDFLAGS $SAVED_LDFLAGS"
|
||||
fi
|
||||
|
||||
# Do we need fuzzers?
|
||||
AM_CONDITIONAL(NEED_FUZZING, test "x$enable_fuzzing" = xyes)
|
||||
|
||||
# Is this gcc?
|
||||
AM_CONDITIONAL(IS_GCC, test "x$GCC" = xyes)
|
||||
AM_CONDITIONAL(IS_GCC, test "x$GCC" = xyes -a "x$enable_fuzzing" != xyes)
|
||||
|
||||
# Is this Sun CC?
|
||||
AC_EGREP_CPP(
|
||||
@ -110,6 +133,7 @@ AC_CHECK_FUNCS([ \
|
||||
AC_REPLACE_FUNCS([ \
|
||||
asprintf \
|
||||
cfmakeraw \
|
||||
clock_gettime \
|
||||
closefrom \
|
||||
explicit_bzero \
|
||||
fgetln \
|
||||
@ -119,8 +143,6 @@ AC_REPLACE_FUNCS([ \
|
||||
getline \
|
||||
getprogname \
|
||||
memmem \
|
||||
recallocarray \
|
||||
reallocarray \
|
||||
setenv \
|
||||
setproctitle \
|
||||
strcasestr \
|
||||
@ -132,6 +154,26 @@ AC_REPLACE_FUNCS([ \
|
||||
])
|
||||
AC_FUNC_STRNLEN
|
||||
|
||||
# Clang sanitizers wrap reallocarray even if it isn't available on the target
|
||||
# system. When compiled it always returns NULL and crashes the program. To
|
||||
# detect this we need a more complicated test.
|
||||
AC_MSG_CHECKING([for working reallocarray])
|
||||
AC_RUN_IFELSE([AC_LANG_PROGRAM(
|
||||
[#include <stdlib.h>],
|
||||
[return (reallocarray(NULL, 1, 1) == NULL);]
|
||||
)],
|
||||
AC_MSG_RESULT(yes),
|
||||
[AC_LIBOBJ(reallocarray) AC_MSG_RESULT([no])]
|
||||
)
|
||||
AC_MSG_CHECKING([for working recallocarray])
|
||||
AC_RUN_IFELSE([AC_LANG_PROGRAM(
|
||||
[#include <stdlib.h>],
|
||||
[return (recallocarray(NULL, 1, 1, 1) == NULL);]
|
||||
)],
|
||||
AC_MSG_RESULT(yes),
|
||||
[AC_LIBOBJ(recallocarray) AC_MSG_RESULT([no])]
|
||||
)
|
||||
|
||||
# Look for clock_gettime. Must come before event_init.
|
||||
AC_SEARCH_LIBS(clock_gettime, rt)
|
||||
|
||||
@ -141,88 +183,112 @@ AC_SEARCH_LIBS(clock_gettime, rt)
|
||||
# implementations.
|
||||
AC_LIBOBJ(getopt)
|
||||
|
||||
# Look for libevent.
|
||||
# Look for libevent. Try libevent_core or libevent with pkg-config first then
|
||||
# look for the library.
|
||||
PKG_CHECK_MODULES(
|
||||
LIBEVENT,
|
||||
libevent,
|
||||
LIBEVENT_CORE,
|
||||
[libevent_core >= 2],
|
||||
[
|
||||
AM_CFLAGS="$LIBEVENT_CFLAGS $AM_CFLAGS"
|
||||
CFLAGS="$AM_CFLAGS $SAVED_CFLAGS"
|
||||
LIBS="$LIBEVENT_LIBS $LIBS"
|
||||
AM_CPPFLAGS="$LIBEVENT_CORE_CFLAGS $AM_CPPFLAGS"
|
||||
CPPFLAGS="$AM_CPPFLAGS $SAVED_CPPFLAGS"
|
||||
LIBS="$LIBEVENT_CORE_LIBS $LIBS"
|
||||
found_libevent=yes
|
||||
],
|
||||
found_libevent=no
|
||||
)
|
||||
if test x$found_libevent = xno; then
|
||||
PKG_CHECK_MODULES(
|
||||
LIBEVENT,
|
||||
[libevent >= 2],
|
||||
[
|
||||
AM_CPPFLAGS="$LIBEVENT_CFLAGS $AM_CPPFLAGS"
|
||||
CPPFLAGS="$AM_CPPFLAGS $SAVED_CPPFLAGS"
|
||||
LIBS="$LIBEVENT_LIBS $LIBS"
|
||||
found_libevent=yes
|
||||
],
|
||||
found_libevent=no
|
||||
)
|
||||
fi
|
||||
if test x$found_libevent = xno; then
|
||||
AC_SEARCH_LIBS(
|
||||
event_init,
|
||||
[event_core event event-1.4],
|
||||
found_libevent=yes,
|
||||
found_libevent=no
|
||||
)
|
||||
fi
|
||||
AC_CHECK_HEADER(
|
||||
event2/event.h,
|
||||
AC_DEFINE(HAVE_EVENT2_EVENT_H),
|
||||
[
|
||||
AC_SEARCH_LIBS(
|
||||
event_init,
|
||||
[event event-1.4 event2],
|
||||
found_libevent=yes,
|
||||
AC_CHECK_HEADER(
|
||||
event.h,
|
||||
AC_DEFINE(HAVE_EVENT_H),
|
||||
found_libevent=no
|
||||
)
|
||||
]
|
||||
)
|
||||
AC_CHECK_HEADER(
|
||||
event.h,
|
||||
,
|
||||
found_libevent=no
|
||||
)
|
||||
if test "x$found_libevent" = xno; then
|
||||
AC_MSG_ERROR("libevent not found")
|
||||
fi
|
||||
|
||||
# Look for ncurses.
|
||||
# Look for ncurses or curses. Try pkg-config first then directly for the
|
||||
# library.
|
||||
PKG_CHECK_MODULES(
|
||||
LIBTINFO,
|
||||
tinfo,
|
||||
found_ncurses=yes,
|
||||
[
|
||||
AM_CPPFLAGS="$LIBTINFO_CFLAGS $AM_CPPFLAGS"
|
||||
CPPFLAGS="$LIBTINFO_CFLAGS $SAVED_CPPFLAGS"
|
||||
LIBS="$LIBTINFO_LIBS $LIBS"
|
||||
found_ncurses=yes
|
||||
],
|
||||
found_ncurses=no
|
||||
)
|
||||
if test "x$found_ncurses" = xno; then
|
||||
PKG_CHECK_MODULES(
|
||||
LIBNCURSES,
|
||||
ncurses,
|
||||
found_ncurses=yes,
|
||||
[
|
||||
AM_CPPFLAGS="$LIBNCURSES_CFLAGS $AM_CPPFLAGS"
|
||||
CPPFLAGS="$LIBNCURSES_CFLAGS $SAVED_CPPFLAGS"
|
||||
LIBS="$LIBNCURSES_LIBS $LIBS"
|
||||
found_ncurses=yes
|
||||
],
|
||||
found_ncurses=no
|
||||
)
|
||||
fi
|
||||
if test "x$found_ncurses" = xno; then
|
||||
PKG_CHECK_MODULES(
|
||||
LIBNCURSES,
|
||||
LIBNCURSESW,
|
||||
ncursesw,
|
||||
found_ncurses=yes,
|
||||
[
|
||||
AM_CPPFLAGS="$LIBNCURSESW_CFLAGS $AM_CPPFLAGS"
|
||||
CPPFLAGS="$LIBNCURSESW_CFLAGS $SAVED_CPPFLAGS"
|
||||
LIBS="$LIBNCURSESW_LIBS $LIBS"
|
||||
found_ncurses=yes
|
||||
],
|
||||
found_ncurses=no
|
||||
)
|
||||
fi
|
||||
if test "x$found_ncurses" = xyes; then
|
||||
AM_CFLAGS="$LIBNCURSES_CFLAGS $LIBTINFO_CFLAGS $AM_CFLAGS"
|
||||
CFLAGS="$LIBNCURSES_CFLAGS $LIBTINFO_CFLAGS $CFLAGS"
|
||||
LIBS="$LIBNCURSES_LIBS $LIBTINFO_LIBS $LIBS"
|
||||
else
|
||||
# pkg-config didn't work, try ncurses.
|
||||
AC_CHECK_LIB(
|
||||
tinfo,
|
||||
if test "x$found_ncurses" = xno; then
|
||||
AC_SEARCH_LIBS(
|
||||
setupterm,
|
||||
[tinfo ncurses ncursesw],
|
||||
found_ncurses=yes,
|
||||
found_ncurses=no
|
||||
)
|
||||
if test "x$found_ncurses" = xno; then
|
||||
AC_CHECK_LIB(
|
||||
ncurses,
|
||||
setupterm,
|
||||
found_ncurses=yes,
|
||||
found_ncurses=no
|
||||
)
|
||||
fi
|
||||
if test "x$found_ncurses" = xyes; then
|
||||
AC_CHECK_HEADER(
|
||||
ncurses.h,
|
||||
LIBS="$LIBS -lncurses",
|
||||
found_ncurses=no)
|
||||
found_ncurses=no
|
||||
)
|
||||
fi
|
||||
fi
|
||||
if test "x$found_ncurses" = xyes; then
|
||||
AC_DEFINE(HAVE_NCURSES_H)
|
||||
else
|
||||
# No ncurses, try curses.
|
||||
AC_CHECK_LIB(
|
||||
curses,
|
||||
setupterm,
|
||||
@ -232,7 +298,8 @@ else
|
||||
AC_CHECK_HEADER(
|
||||
curses.h,
|
||||
,
|
||||
found_curses=no)
|
||||
found_curses=no
|
||||
)
|
||||
if test "x$found_curses" = xyes; then
|
||||
LIBS="$LIBS -lcurses"
|
||||
AC_DEFINE(HAVE_CURSES_H)
|
||||
@ -298,12 +365,11 @@ AC_TRY_LINK(
|
||||
found_b64_ntop=yes,
|
||||
found_b64_ntop=no
|
||||
)
|
||||
AC_MSG_RESULT($found_b64_ntop)
|
||||
OLD_LIBS="$LIBS"
|
||||
if test "x$found_b64_ntop" = xno; then
|
||||
AC_MSG_RESULT(no)
|
||||
|
||||
AC_MSG_CHECKING(for b64_ntop with -lresolv)
|
||||
OLD_LIBS="$LIBS"
|
||||
LIBS="$LIBS -lresolv"
|
||||
LIBS="$OLD_LIBS -lresolv"
|
||||
AC_TRY_LINK(
|
||||
[
|
||||
#include <sys/types.h>
|
||||
@ -314,15 +380,27 @@ if test "x$found_b64_ntop" = xno; then
|
||||
found_b64_ntop=yes,
|
||||
found_b64_ntop=no
|
||||
)
|
||||
if test "x$found_b64_ntop" = xno; then
|
||||
LIBS="$OLD_LIBS"
|
||||
AC_MSG_RESULT(no)
|
||||
fi
|
||||
AC_MSG_RESULT($found_b64_ntop)
|
||||
fi
|
||||
if test "x$found_b64_ntop" = xno; then
|
||||
AC_MSG_CHECKING(for b64_ntop with -lnetwork)
|
||||
LIBS="$OLD_LIBS -lnetwork"
|
||||
AC_TRY_LINK(
|
||||
[
|
||||
#include <sys/types.h>
|
||||
#include <netinet/in.h>
|
||||
#include <resolv.h>
|
||||
],
|
||||
[b64_ntop(NULL, 0, NULL, 0);],
|
||||
found_b64_ntop=yes,
|
||||
found_b64_ntop=no
|
||||
)
|
||||
AC_MSG_RESULT($found_b64_ntop)
|
||||
fi
|
||||
if test "x$found_b64_ntop" = xyes; then
|
||||
AC_DEFINE(HAVE_B64_NTOP)
|
||||
AC_MSG_RESULT(yes)
|
||||
else
|
||||
LIBS="$OLD_LIBS"
|
||||
AC_LIBOBJ(base64)
|
||||
fi
|
||||
|
||||
@ -658,6 +736,10 @@ case "$host_os" in
|
||||
AC_MSG_RESULT(cygwin)
|
||||
PLATFORM=cygwin
|
||||
;;
|
||||
*haiku*)
|
||||
AC_MSG_RESULT(haiku)
|
||||
PLATFORM=haiku
|
||||
;;
|
||||
*)
|
||||
AC_MSG_RESULT(unknown)
|
||||
PLATFORM=unknown
|
||||
@ -673,6 +755,7 @@ AM_CONDITIONAL(IS_NETBSD, test "x$PLATFORM" = xnetbsd)
|
||||
AM_CONDITIONAL(IS_OPENBSD, test "x$PLATFORM" = xopenbsd)
|
||||
AM_CONDITIONAL(IS_SUNOS, test "x$PLATFORM" = xsunos)
|
||||
AM_CONDITIONAL(IS_HPUX, test "x$PLATFORM" = xhpux)
|
||||
AM_CONDITIONAL(IS_HAIKU, test "x$PLATFORM" = xhaiku)
|
||||
AM_CONDITIONAL(IS_UNKNOWN, test "x$PLATFORM" = xunknown)
|
||||
|
||||
# Save our CFLAGS/CPPFLAGS/LDFLAGS for the Makefile and restore the old user
|
||||
|
@ -49,7 +49,7 @@ control_notify_window_layout_changed(struct window *w)
|
||||
char *cp;
|
||||
|
||||
template = "%layout-change #{window_id} #{window_layout} "
|
||||
"#{window_visible_layout} #{window_flags}";
|
||||
"#{window_visible_layout} #{window_raw_flags}";
|
||||
|
||||
TAILQ_FOREACH(c, &clients, entry) {
|
||||
if (!CONTROL_SHOULD_NOTIFY_CLIENT(c) || c->session == NULL)
|
||||
|
@ -19,7 +19,6 @@
|
||||
|
||||
#include <sys/types.h>
|
||||
|
||||
#include <event.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <time.h>
|
||||
|
454
file.c
454
file.c
@ -27,10 +27,17 @@
|
||||
|
||||
#include "tmux.h"
|
||||
|
||||
/*
|
||||
* IPC file handling. Both client and server use the same data structures
|
||||
* (client_file and client_files) to store list of active files. Most functions
|
||||
* are for use either in client or server but not both.
|
||||
*/
|
||||
|
||||
static int file_next_stream = 3;
|
||||
|
||||
RB_GENERATE(client_files, client_file, entry, file_cmp);
|
||||
|
||||
/* Get path for file, either as given or from working directory. */
|
||||
static char *
|
||||
file_get_path(struct client *c, const char *file)
|
||||
{
|
||||
@ -43,6 +50,7 @@ file_get_path(struct client *c, const char *file)
|
||||
return (path);
|
||||
}
|
||||
|
||||
/* Tree comparison function. */
|
||||
int
|
||||
file_cmp(struct client_file *cf1, struct client_file *cf2)
|
||||
{
|
||||
@ -53,11 +61,47 @@ file_cmp(struct client_file *cf1, struct client_file *cf2)
|
||||
return (0);
|
||||
}
|
||||
|
||||
/*
|
||||
* Create a file object in the client process - the peer is the server to send
|
||||
* messages to. Check callback is fired when the file is finished with so the
|
||||
* process can decide if it needs to exit (if it is waiting for files to
|
||||
* flush).
|
||||
*/
|
||||
struct client_file *
|
||||
file_create(struct client *c, int stream, client_file_cb cb, void *cbdata)
|
||||
file_create_with_peer(struct tmuxpeer *peer, struct client_files *files,
|
||||
int stream, client_file_cb cb, void *cbdata)
|
||||
{
|
||||
struct client_file *cf;
|
||||
|
||||
cf = xcalloc(1, sizeof *cf);
|
||||
cf->c = NULL;
|
||||
cf->references = 1;
|
||||
cf->stream = stream;
|
||||
|
||||
cf->buffer = evbuffer_new();
|
||||
if (cf->buffer == NULL)
|
||||
fatalx("out of memory");
|
||||
|
||||
cf->cb = cb;
|
||||
cf->data = cbdata;
|
||||
|
||||
cf->peer = peer;
|
||||
cf->tree = files;
|
||||
RB_INSERT(client_files, files, cf);
|
||||
|
||||
return (cf);
|
||||
}
|
||||
|
||||
/* Create a file object in the server, communicating with the given client. */
|
||||
struct client_file *
|
||||
file_create_with_client(struct client *c, int stream, client_file_cb cb,
|
||||
void *cbdata)
|
||||
{
|
||||
struct client_file *cf;
|
||||
|
||||
if (c != NULL && (c->flags & CLIENT_ATTACHED))
|
||||
c = NULL;
|
||||
|
||||
cf = xcalloc(1, sizeof *cf);
|
||||
cf->c = c;
|
||||
cf->references = 1;
|
||||
@ -71,6 +115,8 @@ file_create(struct client *c, int stream, client_file_cb cb, void *cbdata)
|
||||
cf->data = cbdata;
|
||||
|
||||
if (cf->c != NULL) {
|
||||
cf->peer = cf->c->peer;
|
||||
cf->tree = &cf->c->files;
|
||||
RB_INSERT(client_files, &cf->c->files, cf);
|
||||
cf->c->references++;
|
||||
}
|
||||
@ -78,6 +124,7 @@ file_create(struct client *c, int stream, client_file_cb cb, void *cbdata)
|
||||
return (cf);
|
||||
}
|
||||
|
||||
/* Free a file. */
|
||||
void
|
||||
file_free(struct client_file *cf)
|
||||
{
|
||||
@ -87,13 +134,15 @@ file_free(struct client_file *cf)
|
||||
evbuffer_free(cf->buffer);
|
||||
free(cf->path);
|
||||
|
||||
if (cf->c != NULL) {
|
||||
RB_REMOVE(client_files, &cf->c->files, cf);
|
||||
if (cf->tree != NULL)
|
||||
RB_REMOVE(client_files, cf->tree, cf);
|
||||
if (cf->c != NULL)
|
||||
server_client_unref(cf->c);
|
||||
}
|
||||
|
||||
free(cf);
|
||||
}
|
||||
|
||||
/* Event to fire the done callback. */
|
||||
static void
|
||||
file_fire_done_cb(__unused int fd, __unused short events, void *arg)
|
||||
{
|
||||
@ -105,21 +154,22 @@ file_fire_done_cb(__unused int fd, __unused short events, void *arg)
|
||||
file_free(cf);
|
||||
}
|
||||
|
||||
/* Add an event to fire the done callback (used by the server). */
|
||||
void
|
||||
file_fire_done(struct client_file *cf)
|
||||
{
|
||||
event_once(-1, EV_TIMEOUT, file_fire_done_cb, cf, NULL);
|
||||
}
|
||||
|
||||
/* Fire the read callback. */
|
||||
void
|
||||
file_fire_read(struct client_file *cf)
|
||||
{
|
||||
struct client *c = cf->c;
|
||||
|
||||
if (cf->cb != NULL)
|
||||
cf->cb(c, cf->path, cf->error, 0, cf->buffer, cf->data);
|
||||
cf->cb(cf->c, cf->path, cf->error, 0, cf->buffer, cf->data);
|
||||
}
|
||||
|
||||
/* Can this file be printed to? */
|
||||
int
|
||||
file_can_print(struct client *c)
|
||||
{
|
||||
@ -130,6 +180,7 @@ file_can_print(struct client *c)
|
||||
return (1);
|
||||
}
|
||||
|
||||
/* Print a message to a file. */
|
||||
void
|
||||
file_print(struct client *c, const char *fmt, ...)
|
||||
{
|
||||
@ -140,6 +191,7 @@ file_print(struct client *c, const char *fmt, ...)
|
||||
va_end(ap);
|
||||
}
|
||||
|
||||
/* Print a message to a file. */
|
||||
void
|
||||
file_vprint(struct client *c, const char *fmt, va_list ap)
|
||||
{
|
||||
@ -151,7 +203,7 @@ file_vprint(struct client *c, const char *fmt, va_list ap)
|
||||
|
||||
find.stream = 1;
|
||||
if ((cf = RB_FIND(client_files, &c->files, &find)) == NULL) {
|
||||
cf = file_create(c, 1, NULL, NULL);
|
||||
cf = file_create_with_client(c, 1, NULL, NULL);
|
||||
cf->path = xstrdup("-");
|
||||
|
||||
evbuffer_add_vprintf(cf->buffer, fmt, ap);
|
||||
@ -166,6 +218,7 @@ file_vprint(struct client *c, const char *fmt, va_list ap)
|
||||
}
|
||||
}
|
||||
|
||||
/* Print a buffer to a file. */
|
||||
void
|
||||
file_print_buffer(struct client *c, void *data, size_t size)
|
||||
{
|
||||
@ -177,7 +230,7 @@ file_print_buffer(struct client *c, void *data, size_t size)
|
||||
|
||||
find.stream = 1;
|
||||
if ((cf = RB_FIND(client_files, &c->files, &find)) == NULL) {
|
||||
cf = file_create(c, 1, NULL, NULL);
|
||||
cf = file_create_with_client(c, 1, NULL, NULL);
|
||||
cf->path = xstrdup("-");
|
||||
|
||||
evbuffer_add(cf->buffer, data, size);
|
||||
@ -192,6 +245,7 @@ file_print_buffer(struct client *c, void *data, size_t size)
|
||||
}
|
||||
}
|
||||
|
||||
/* Report an error to a file. */
|
||||
void
|
||||
file_error(struct client *c, const char *fmt, ...)
|
||||
{
|
||||
@ -206,7 +260,7 @@ file_error(struct client *c, const char *fmt, ...)
|
||||
|
||||
find.stream = 2;
|
||||
if ((cf = RB_FIND(client_files, &c->files, &find)) == NULL) {
|
||||
cf = file_create(c, 2, NULL, NULL);
|
||||
cf = file_create_with_client(c, 2, NULL, NULL);
|
||||
cf->path = xstrdup("-");
|
||||
|
||||
evbuffer_add_vprintf(cf->buffer, fmt, ap);
|
||||
@ -223,19 +277,21 @@ file_error(struct client *c, const char *fmt, ...)
|
||||
va_end(ap);
|
||||
}
|
||||
|
||||
/* Write data to a file. */
|
||||
void
|
||||
file_write(struct client *c, const char *path, int flags, const void *bdata,
|
||||
size_t bsize, client_file_cb cb, void *cbdata)
|
||||
{
|
||||
struct client_file *cf;
|
||||
FILE *f;
|
||||
struct msg_write_open *msg;
|
||||
size_t msglen;
|
||||
int fd = -1;
|
||||
u_int stream = file_next_stream++;
|
||||
FILE *f;
|
||||
const char *mode;
|
||||
|
||||
if (strcmp(path, "-") == 0) {
|
||||
cf = file_create(c, file_next_stream++, cb, cbdata);
|
||||
cf = file_create_with_client(c, stream, cb, cbdata);
|
||||
cf->path = xstrdup("-");
|
||||
|
||||
fd = STDOUT_FILENO;
|
||||
@ -248,7 +304,7 @@ file_write(struct client *c, const char *path, int flags, const void *bdata,
|
||||
goto skip;
|
||||
}
|
||||
|
||||
cf = file_create(c, file_next_stream++, cb, cbdata);
|
||||
cf = file_create_with_client(c, stream, cb, cbdata);
|
||||
cf->path = file_get_path(c, path);
|
||||
|
||||
if (c == NULL || c->flags & CLIENT_ATTACHED) {
|
||||
@ -283,7 +339,7 @@ skip:
|
||||
msg->fd = fd;
|
||||
msg->flags = flags;
|
||||
memcpy(msg + 1, cf->path, msglen - sizeof *msg);
|
||||
if (proc_send(c->peer, MSG_WRITE_OPEN, -1, msg, msglen) != 0) {
|
||||
if (proc_send(cf->peer, MSG_WRITE_OPEN, -1, msg, msglen) != 0) {
|
||||
free(msg);
|
||||
cf->error = EINVAL;
|
||||
goto done;
|
||||
@ -295,18 +351,21 @@ done:
|
||||
file_fire_done(cf);
|
||||
}
|
||||
|
||||
/* Read a file. */
|
||||
void
|
||||
file_read(struct client *c, const char *path, client_file_cb cb, void *cbdata)
|
||||
{
|
||||
struct client_file *cf;
|
||||
FILE *f;
|
||||
struct msg_read_open *msg;
|
||||
size_t msglen, size;
|
||||
size_t msglen;
|
||||
int fd = -1;
|
||||
u_int stream = file_next_stream++;
|
||||
FILE *f;
|
||||
size_t size;
|
||||
char buffer[BUFSIZ];
|
||||
|
||||
if (strcmp(path, "-") == 0) {
|
||||
cf = file_create(c, file_next_stream++, cb, cbdata);
|
||||
cf = file_create_with_client(c, stream, cb, cbdata);
|
||||
cf->path = xstrdup("-");
|
||||
|
||||
fd = STDIN_FILENO;
|
||||
@ -319,7 +378,7 @@ file_read(struct client *c, const char *path, client_file_cb cb, void *cbdata)
|
||||
goto skip;
|
||||
}
|
||||
|
||||
cf = file_create(c, file_next_stream++, cb, cbdata);
|
||||
cf = file_create_with_client(c, stream, cb, cbdata);
|
||||
cf->path = file_get_path(c, path);
|
||||
|
||||
if (c == NULL || c->flags & CLIENT_ATTACHED) {
|
||||
@ -355,7 +414,7 @@ skip:
|
||||
msg->stream = cf->stream;
|
||||
msg->fd = fd;
|
||||
memcpy(msg + 1, cf->path, msglen - sizeof *msg);
|
||||
if (proc_send(c->peer, MSG_READ_OPEN, -1, msg, msglen) != 0) {
|
||||
if (proc_send(cf->peer, MSG_READ_OPEN, -1, msg, msglen) != 0) {
|
||||
free(msg);
|
||||
cf->error = EINVAL;
|
||||
goto done;
|
||||
@ -367,21 +426,21 @@ done:
|
||||
file_fire_done(cf);
|
||||
}
|
||||
|
||||
/* Push event, fired if there is more writing to be done. */
|
||||
static void
|
||||
file_push_cb(__unused int fd, __unused short events, void *arg)
|
||||
{
|
||||
struct client_file *cf = arg;
|
||||
struct client *c = cf->c;
|
||||
|
||||
if (~c->flags & CLIENT_DEAD)
|
||||
if (cf->c == NULL || ~cf->c->flags & CLIENT_DEAD)
|
||||
file_push(cf);
|
||||
file_free(cf);
|
||||
}
|
||||
|
||||
/* Push uwritten data to the client for a file, if it will accept it. */
|
||||
void
|
||||
file_push(struct client_file *cf)
|
||||
{
|
||||
struct client *c = cf->c;
|
||||
struct msg_write_data *msg;
|
||||
size_t msglen, sent, left;
|
||||
struct msg_write_close close;
|
||||
@ -397,21 +456,364 @@ file_push(struct client_file *cf)
|
||||
msg = xrealloc(msg, msglen);
|
||||
msg->stream = cf->stream;
|
||||
memcpy(msg + 1, EVBUFFER_DATA(cf->buffer), sent);
|
||||
if (proc_send(c->peer, MSG_WRITE, -1, msg, msglen) != 0)
|
||||
if (proc_send(cf->peer, MSG_WRITE, -1, msg, msglen) != 0)
|
||||
break;
|
||||
evbuffer_drain(cf->buffer, sent);
|
||||
|
||||
left = EVBUFFER_LENGTH(cf->buffer);
|
||||
log_debug("%s: file %d sent %zu, left %zu", c->name, cf->stream,
|
||||
sent, left);
|
||||
log_debug("file %d sent %zu, left %zu", cf->stream, sent, left);
|
||||
}
|
||||
if (left != 0) {
|
||||
cf->references++;
|
||||
event_once(-1, EV_TIMEOUT, file_push_cb, cf, NULL);
|
||||
} else if (cf->stream > 2) {
|
||||
close.stream = cf->stream;
|
||||
proc_send(c->peer, MSG_WRITE_CLOSE, -1, &close, sizeof close);
|
||||
proc_send(cf->peer, MSG_WRITE_CLOSE, -1, &close, sizeof close);
|
||||
file_fire_done(cf);
|
||||
}
|
||||
free(msg);
|
||||
}
|
||||
|
||||
/* Check if any files have data left to write. */
|
||||
int
|
||||
file_write_left(struct client_files *files)
|
||||
{
|
||||
struct client_file *cf;
|
||||
size_t left;
|
||||
int waiting = 0;
|
||||
|
||||
RB_FOREACH(cf, client_files, files) {
|
||||
if (cf->event == NULL)
|
||||
continue;
|
||||
left = EVBUFFER_LENGTH(cf->event->output);
|
||||
if (left != 0) {
|
||||
waiting++;
|
||||
log_debug("file %u %zu bytes left", cf->stream, left);
|
||||
}
|
||||
}
|
||||
return (waiting != 0);
|
||||
}
|
||||
|
||||
/* Client file write error callback. */
|
||||
static void
|
||||
file_write_error_callback(__unused struct bufferevent *bev, __unused short what,
|
||||
void *arg)
|
||||
{
|
||||
struct client_file *cf = arg;
|
||||
|
||||
log_debug("write error file %d", cf->stream);
|
||||
|
||||
if (cf->cb != NULL)
|
||||
cf->cb(NULL, NULL, 0, -1, NULL, cf->data);
|
||||
|
||||
bufferevent_free(cf->event);
|
||||
cf->event = NULL;
|
||||
|
||||
close(cf->fd);
|
||||
cf->fd = -1;
|
||||
}
|
||||
|
||||
/* Client file write callback. */
|
||||
static void
|
||||
file_write_callback(__unused struct bufferevent *bev, void *arg)
|
||||
{
|
||||
struct client_file *cf = arg;
|
||||
|
||||
log_debug("write check file %d", cf->stream);
|
||||
|
||||
if (cf->cb != NULL)
|
||||
cf->cb(NULL, NULL, 0, -1, NULL, cf->data);
|
||||
|
||||
if (cf->closed && EVBUFFER_LENGTH(cf->event->output) == 0) {
|
||||
bufferevent_free(cf->event);
|
||||
close(cf->fd);
|
||||
RB_REMOVE(client_files, cf->tree, cf);
|
||||
file_free(cf);
|
||||
}
|
||||
}
|
||||
|
||||
/* Handle a file write open message (client). */
|
||||
void
|
||||
file_write_open(struct client_files *files, struct tmuxpeer *peer,
|
||||
struct imsg *imsg, int allow_streams, int close_received,
|
||||
client_file_cb cb, void *cbdata)
|
||||
{
|
||||
struct msg_write_open *msg = imsg->data;
|
||||
size_t msglen = imsg->hdr.len - IMSG_HEADER_SIZE;
|
||||
const char *path;
|
||||
struct msg_write_ready reply;
|
||||
struct client_file find, *cf;
|
||||
const int flags = O_NONBLOCK|O_WRONLY|O_CREAT;
|
||||
int error = 0;
|
||||
|
||||
if (msglen < sizeof *msg)
|
||||
fatalx("bad MSG_WRITE_OPEN size");
|
||||
if (msglen == sizeof *msg)
|
||||
path = "-";
|
||||
else
|
||||
path = (const char *)(msg + 1);
|
||||
log_debug("open write file %d %s", msg->stream, path);
|
||||
|
||||
find.stream = msg->stream;
|
||||
if ((cf = RB_FIND(client_files, files, &find)) != NULL) {
|
||||
error = EBADF;
|
||||
goto reply;
|
||||
}
|
||||
cf = file_create_with_peer(peer, files, msg->stream, cb, cbdata);
|
||||
if (cf->closed) {
|
||||
error = EBADF;
|
||||
goto reply;
|
||||
}
|
||||
|
||||
cf->fd = -1;
|
||||
if (msg->fd == -1)
|
||||
cf->fd = open(path, msg->flags|flags, 0644);
|
||||
else if (allow_streams) {
|
||||
if (msg->fd != STDOUT_FILENO && msg->fd != STDERR_FILENO)
|
||||
errno = EBADF;
|
||||
else {
|
||||
cf->fd = dup(msg->fd);
|
||||
if (close_received)
|
||||
close(msg->fd); /* can only be used once */
|
||||
}
|
||||
} else
|
||||
errno = EBADF;
|
||||
if (cf->fd == -1) {
|
||||
error = errno;
|
||||
goto reply;
|
||||
}
|
||||
|
||||
cf->event = bufferevent_new(cf->fd, NULL, file_write_callback,
|
||||
file_write_error_callback, cf);
|
||||
bufferevent_enable(cf->event, EV_WRITE);
|
||||
goto reply;
|
||||
|
||||
reply:
|
||||
reply.stream = msg->stream;
|
||||
reply.error = error;
|
||||
proc_send(peer, MSG_WRITE_READY, -1, &reply, sizeof reply);
|
||||
}
|
||||
|
||||
/* Handle a file write data message (client). */
|
||||
void
|
||||
file_write_data(struct client_files *files, struct imsg *imsg)
|
||||
{
|
||||
struct msg_write_data *msg = imsg->data;
|
||||
size_t msglen = imsg->hdr.len - IMSG_HEADER_SIZE;
|
||||
struct client_file find, *cf;
|
||||
size_t size = msglen - sizeof *msg;
|
||||
|
||||
if (msglen < sizeof *msg)
|
||||
fatalx("bad MSG_WRITE size");
|
||||
find.stream = msg->stream;
|
||||
if ((cf = RB_FIND(client_files, files, &find)) == NULL)
|
||||
fatalx("unknown stream number");
|
||||
log_debug("write %zu to file %d", size, cf->stream);
|
||||
|
||||
if (cf->event != NULL)
|
||||
bufferevent_write(cf->event, msg + 1, size);
|
||||
}
|
||||
|
||||
/* Handle a file write close message (client). */
|
||||
void
|
||||
file_write_close(struct client_files *files, struct imsg *imsg)
|
||||
{
|
||||
struct msg_write_close *msg = imsg->data;
|
||||
size_t msglen = imsg->hdr.len - IMSG_HEADER_SIZE;
|
||||
struct client_file find, *cf;
|
||||
|
||||
if (msglen != sizeof *msg)
|
||||
fatalx("bad MSG_WRITE_CLOSE size");
|
||||
find.stream = msg->stream;
|
||||
if ((cf = RB_FIND(client_files, files, &find)) == NULL)
|
||||
fatalx("unknown stream number");
|
||||
log_debug("close file %d", cf->stream);
|
||||
|
||||
if (cf->event == NULL || EVBUFFER_LENGTH(cf->event->output) == 0) {
|
||||
if (cf->event != NULL)
|
||||
bufferevent_free(cf->event);
|
||||
if (cf->fd != -1)
|
||||
close(cf->fd);
|
||||
RB_REMOVE(client_files, files, cf);
|
||||
file_free(cf);
|
||||
}
|
||||
}
|
||||
|
||||
/* Client file read error callback. */
|
||||
static void
|
||||
file_read_error_callback(__unused struct bufferevent *bev, __unused short what,
|
||||
void *arg)
|
||||
{
|
||||
struct client_file *cf = arg;
|
||||
struct msg_read_done msg;
|
||||
|
||||
log_debug("read error file %d", cf->stream);
|
||||
|
||||
msg.stream = cf->stream;
|
||||
msg.error = 0;
|
||||
proc_send(cf->peer, MSG_READ_DONE, -1, &msg, sizeof msg);
|
||||
|
||||
bufferevent_free(cf->event);
|
||||
close(cf->fd);
|
||||
RB_REMOVE(client_files, cf->tree, cf);
|
||||
file_free(cf);
|
||||
}
|
||||
|
||||
/* Client file read callback. */
|
||||
static void
|
||||
file_read_callback(__unused struct bufferevent *bev, void *arg)
|
||||
{
|
||||
struct client_file *cf = arg;
|
||||
void *bdata;
|
||||
size_t bsize;
|
||||
struct msg_read_data *msg;
|
||||
size_t msglen;
|
||||
|
||||
msg = xmalloc(sizeof *msg);
|
||||
for (;;) {
|
||||
bdata = EVBUFFER_DATA(cf->event->input);
|
||||
bsize = EVBUFFER_LENGTH(cf->event->input);
|
||||
|
||||
if (bsize == 0)
|
||||
break;
|
||||
if (bsize > MAX_IMSGSIZE - IMSG_HEADER_SIZE - sizeof *msg)
|
||||
bsize = MAX_IMSGSIZE - IMSG_HEADER_SIZE - sizeof *msg;
|
||||
log_debug("read %zu from file %d", bsize, cf->stream);
|
||||
|
||||
msglen = (sizeof *msg) + bsize;
|
||||
msg = xrealloc(msg, msglen);
|
||||
msg->stream = cf->stream;
|
||||
memcpy(msg + 1, bdata, bsize);
|
||||
proc_send(cf->peer, MSG_READ, -1, msg, msglen);
|
||||
|
||||
evbuffer_drain(cf->event->input, bsize);
|
||||
}
|
||||
free(msg);
|
||||
}
|
||||
|
||||
/* Handle a file read open message (client). */
|
||||
void
|
||||
file_read_open(struct client_files *files, struct tmuxpeer *peer,
|
||||
struct imsg *imsg, int allow_streams, int close_received, client_file_cb cb,
|
||||
void *cbdata)
|
||||
{
|
||||
struct msg_read_open *msg = imsg->data;
|
||||
size_t msglen = imsg->hdr.len - IMSG_HEADER_SIZE;
|
||||
const char *path;
|
||||
struct msg_read_done reply;
|
||||
struct client_file find, *cf;
|
||||
const int flags = O_NONBLOCK|O_RDONLY;
|
||||
int error;
|
||||
|
||||
if (msglen < sizeof *msg)
|
||||
fatalx("bad MSG_READ_OPEN size");
|
||||
if (msglen == sizeof *msg)
|
||||
path = "-";
|
||||
else
|
||||
path = (const char *)(msg + 1);
|
||||
log_debug("open read file %d %s", msg->stream, path);
|
||||
|
||||
find.stream = msg->stream;
|
||||
if ((cf = RB_FIND(client_files, files, &find)) != NULL) {
|
||||
error = EBADF;
|
||||
goto reply;
|
||||
}
|
||||
cf = file_create_with_peer(peer, files, msg->stream, cb, cbdata);
|
||||
if (cf->closed) {
|
||||
error = EBADF;
|
||||
goto reply;
|
||||
}
|
||||
|
||||
cf->fd = -1;
|
||||
if (msg->fd == -1)
|
||||
cf->fd = open(path, flags);
|
||||
else if (allow_streams) {
|
||||
if (msg->fd != STDIN_FILENO)
|
||||
errno = EBADF;
|
||||
else {
|
||||
cf->fd = dup(msg->fd);
|
||||
if (close_received)
|
||||
close(msg->fd); /* can only be used once */
|
||||
}
|
||||
} else
|
||||
errno = EBADF;
|
||||
if (cf->fd == -1) {
|
||||
error = errno;
|
||||
goto reply;
|
||||
}
|
||||
|
||||
cf->event = bufferevent_new(cf->fd, file_read_callback, NULL,
|
||||
file_read_error_callback, cf);
|
||||
bufferevent_enable(cf->event, EV_READ);
|
||||
return;
|
||||
|
||||
reply:
|
||||
reply.stream = msg->stream;
|
||||
reply.error = error;
|
||||
proc_send(peer, MSG_READ_DONE, -1, &reply, sizeof reply);
|
||||
}
|
||||
|
||||
/* Handle a write ready message (server). */
|
||||
void
|
||||
file_write_ready(struct client_files *files, struct imsg *imsg)
|
||||
{
|
||||
struct msg_write_ready *msg = imsg->data;
|
||||
size_t msglen = imsg->hdr.len - IMSG_HEADER_SIZE;
|
||||
struct client_file find, *cf;
|
||||
|
||||
if (msglen != sizeof *msg)
|
||||
fatalx("bad MSG_WRITE_READY size");
|
||||
find.stream = msg->stream;
|
||||
if ((cf = RB_FIND(client_files, files, &find)) == NULL)
|
||||
return;
|
||||
if (msg->error != 0) {
|
||||
cf->error = msg->error;
|
||||
file_fire_done(cf);
|
||||
} else
|
||||
file_push(cf);
|
||||
}
|
||||
|
||||
/* Handle read data message (server). */
|
||||
void
|
||||
file_read_data(struct client_files *files, struct imsg *imsg)
|
||||
{
|
||||
struct msg_read_data *msg = imsg->data;
|
||||
size_t msglen = imsg->hdr.len - IMSG_HEADER_SIZE;
|
||||
struct client_file find, *cf;
|
||||
void *bdata = msg + 1;
|
||||
size_t bsize = msglen - sizeof *msg;
|
||||
|
||||
if (msglen < sizeof *msg)
|
||||
fatalx("bad MSG_READ_DATA size");
|
||||
find.stream = msg->stream;
|
||||
if ((cf = RB_FIND(client_files, files, &find)) == NULL)
|
||||
return;
|
||||
|
||||
log_debug("file %d read %zu bytes", cf->stream, bsize);
|
||||
if (cf->error == 0) {
|
||||
if (evbuffer_add(cf->buffer, bdata, bsize) != 0) {
|
||||
cf->error = ENOMEM;
|
||||
file_fire_done(cf);
|
||||
} else
|
||||
file_fire_read(cf);
|
||||
}
|
||||
}
|
||||
|
||||
/* Handle a read done message (server). */
|
||||
void
|
||||
file_read_done(struct client_files *files, struct imsg *imsg)
|
||||
{
|
||||
struct msg_read_done *msg = imsg->data;
|
||||
size_t msglen = imsg->hdr.len - IMSG_HEADER_SIZE;
|
||||
struct client_file find, *cf;
|
||||
|
||||
if (msglen != sizeof *msg)
|
||||
fatalx("bad MSG_READ_DONE size");
|
||||
find.stream = msg->stream;
|
||||
if ((cf = RB_FIND(client_files, files, &find)) == NULL)
|
||||
return;
|
||||
|
||||
log_debug("file %d read done", cf->stream);
|
||||
cf->error = msg->error;
|
||||
file_fire_done(cf);
|
||||
}
|
||||
|
178
format-draw.c
178
format-draw.c
@ -486,6 +486,18 @@ format_draw_right(struct screen_write_ctx *octx, u_int available, u_int ocx,
|
||||
focus_end, frs);
|
||||
}
|
||||
|
||||
/* Draw multiple characters. */
|
||||
static void
|
||||
format_draw_many(struct screen_write_ctx *ctx, struct style *sy, char ch,
|
||||
u_int n)
|
||||
{
|
||||
u_int i;
|
||||
|
||||
utf8_set(&sy->gc.data, ch);
|
||||
for (i = 0; i < n; i++)
|
||||
screen_write_cell(ctx, &sy->gc);
|
||||
}
|
||||
|
||||
/* Draw a format to a screen. */
|
||||
void
|
||||
format_draw(struct screen_write_ctx *octx, const struct grid_cell *base,
|
||||
@ -509,10 +521,10 @@ format_draw(struct screen_write_ctx *octx, const struct grid_cell *base,
|
||||
size_t size = strlen(expanded);
|
||||
struct screen *os = octx->s, s[TOTAL];
|
||||
struct screen_write_ctx ctx[TOTAL];
|
||||
u_int ocx = os->cx, ocy = os->cy, i, width[TOTAL];
|
||||
u_int ocx = os->cx, ocy = os->cy, n, i, width[TOTAL];
|
||||
u_int map[] = { LEFT, LEFT, CENTRE, RIGHT };
|
||||
int focus_start = -1, focus_end = -1;
|
||||
int list_state = -1, fill = -1;
|
||||
int list_state = -1, fill = -1, even;
|
||||
enum style_align list_align = STYLE_ALIGN_DEFAULT;
|
||||
struct grid_cell gc, current_default;
|
||||
struct style sy, saved_sy;
|
||||
@ -547,6 +559,38 @@ format_draw(struct screen_write_ctx *octx, const struct grid_cell *base,
|
||||
*/
|
||||
cp = expanded;
|
||||
while (*cp != '\0') {
|
||||
/* Handle sequences of #. */
|
||||
if (cp[0] == '#' && cp[1] != '[' && cp[1] != '\0') {
|
||||
for (n = 1; cp[n] == '#'; n++)
|
||||
/* nothing */;
|
||||
even = ((n % 2) == 0);
|
||||
if (cp[n] != '[') {
|
||||
cp += n;
|
||||
if (even)
|
||||
n = (n / 2);
|
||||
else
|
||||
n = (n / 2) + 1;
|
||||
width[current] += n;
|
||||
format_draw_many(&ctx[current], &sy, '#', n);
|
||||
continue;
|
||||
}
|
||||
if (even)
|
||||
cp += (n + 1);
|
||||
else
|
||||
cp += (n - 1);
|
||||
if (sy.ignore)
|
||||
continue;
|
||||
format_draw_many(&ctx[current], &sy, '#', n / 2);
|
||||
width[current] += (n / 2);
|
||||
if (even) {
|
||||
utf8_set(ud, '[');
|
||||
screen_write_cell(&ctx[current], &sy.gc);
|
||||
width[current]++;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Is this not a style? */
|
||||
if (cp[0] != '#' || cp[1] != '[' || sy.ignore) {
|
||||
/* See if this is a UTF-8 character. */
|
||||
if ((more = utf8_open(ud, *cp)) == UTF8_MORE) {
|
||||
@ -796,13 +840,33 @@ u_int
|
||||
format_width(const char *expanded)
|
||||
{
|
||||
const char *cp, *end;
|
||||
u_int width = 0;
|
||||
u_int n, width = 0;
|
||||
struct utf8_data ud;
|
||||
enum utf8_state more;
|
||||
|
||||
cp = expanded;
|
||||
while (*cp != '\0') {
|
||||
if (cp[0] == '#' && cp[1] == '[') {
|
||||
if (*cp == '#') {
|
||||
for (n = 1; cp[n] == '#'; n++)
|
||||
/* nothing */;
|
||||
if (cp[n] != '[') {
|
||||
width += n;
|
||||
cp += n;
|
||||
continue;
|
||||
}
|
||||
width += (n / 2); /* one for each ## */
|
||||
|
||||
if ((n % 2) == 0) {
|
||||
/*
|
||||
* An even number of #s means that all #s are
|
||||
* escaped, so not a style.
|
||||
*/
|
||||
width++; /* one for the [ */
|
||||
cp += (n + 1);
|
||||
continue;
|
||||
}
|
||||
cp += (n - 1); /* point to the [ */
|
||||
|
||||
end = format_skip(cp + 2, "]");
|
||||
if (end == NULL)
|
||||
return (0);
|
||||
@ -823,19 +887,57 @@ format_width(const char *expanded)
|
||||
return (width);
|
||||
}
|
||||
|
||||
/* Trim on the left, taking #[] into account. */
|
||||
/*
|
||||
* Trim on the left, taking #[] into account. Note, we copy the whole set of
|
||||
* unescaped #s, but only add their escaped size to width. This is because the
|
||||
* format_draw function will actually do the escaping when it runs
|
||||
*/
|
||||
char *
|
||||
format_trim_left(const char *expanded, u_int limit)
|
||||
{
|
||||
char *copy, *out;
|
||||
const char *cp = expanded, *end;
|
||||
u_int width = 0;
|
||||
u_int even, n, width = 0;
|
||||
struct utf8_data ud;
|
||||
enum utf8_state more;
|
||||
|
||||
out = copy = xmalloc(strlen(expanded) + 1);
|
||||
out = copy = xcalloc(1, strlen(expanded) + 1);
|
||||
while (*cp != '\0') {
|
||||
if (cp[0] == '#' && cp[1] == '[') {
|
||||
if (width >= limit)
|
||||
break;
|
||||
if (*cp == '#') {
|
||||
for (end = cp + 1; *end == '#'; end++)
|
||||
/* nothing */;
|
||||
n = end - cp;
|
||||
if (*end != '[') {
|
||||
if (n > limit - width)
|
||||
n = limit - width;
|
||||
memcpy(out, cp, n);
|
||||
out += n;
|
||||
width += n;
|
||||
cp = end;
|
||||
continue;
|
||||
}
|
||||
even = ((n % 2) == 0);
|
||||
|
||||
n /= 2;
|
||||
if (n > limit - width)
|
||||
n = limit - width;
|
||||
width += n;
|
||||
n *= 2;
|
||||
memcpy(out, cp, n);
|
||||
out += n;
|
||||
|
||||
if (even) {
|
||||
if (width + 1 <= limit) {
|
||||
*out++ = '[';
|
||||
width++;
|
||||
}
|
||||
cp = end + 1;
|
||||
continue;
|
||||
}
|
||||
cp = end - 1;
|
||||
|
||||
end = format_skip(cp + 2, "]");
|
||||
if (end == NULL)
|
||||
break;
|
||||
@ -873,7 +975,7 @@ format_trim_right(const char *expanded, u_int limit)
|
||||
{
|
||||
char *copy, *out;
|
||||
const char *cp = expanded, *end;
|
||||
u_int width = 0, total_width, skip;
|
||||
u_int width = 0, total_width, skip, old_n, even, n;
|
||||
struct utf8_data ud;
|
||||
enum utf8_state more;
|
||||
|
||||
@ -882,12 +984,64 @@ format_trim_right(const char *expanded, u_int limit)
|
||||
return (xstrdup(expanded));
|
||||
skip = total_width - limit;
|
||||
|
||||
out = copy = xmalloc(strlen(expanded) + 1);
|
||||
out = copy = xcalloc(1, strlen(expanded) + 1);
|
||||
while (*cp != '\0') {
|
||||
if (cp[0] == '#' && cp[1] == '[') {
|
||||
if (*cp == '#') {
|
||||
for (end = cp + 1; *end == '#'; end++)
|
||||
/* nothing */;
|
||||
old_n = n = end - cp;
|
||||
if (*end != '[') {
|
||||
if (width <= skip) {
|
||||
if (skip - width >= n)
|
||||
n = 0;
|
||||
else
|
||||
n -= (skip - width);
|
||||
}
|
||||
if (n != 0) {
|
||||
memcpy(out, cp, n);
|
||||
out += n;
|
||||
}
|
||||
|
||||
/*
|
||||
* The width always increases by the full
|
||||
* amount even if we can't copy anything yet.
|
||||
*/
|
||||
width += old_n;
|
||||
cp = end;
|
||||
continue;
|
||||
}
|
||||
even = ((n % 2) == 0);
|
||||
|
||||
n /= 2;
|
||||
if (width <= skip) {
|
||||
if (skip - width >= n)
|
||||
n = 0;
|
||||
else
|
||||
n -= (skip - width);
|
||||
}
|
||||
if (n != 0) {
|
||||
/*
|
||||
* Copy the full amount because it hasn't been
|
||||
* escaped yet.
|
||||
*/
|
||||
memcpy(out, cp, old_n);
|
||||
out += old_n;
|
||||
}
|
||||
cp += old_n;
|
||||
width += (old_n / 2) - even;
|
||||
|
||||
if (even) {
|
||||
if (width > skip)
|
||||
*out++ = '[';
|
||||
width++;
|
||||
continue;
|
||||
}
|
||||
cp = end - 1;
|
||||
|
||||
end = format_skip(cp + 2, "]");
|
||||
if (end == NULL)
|
||||
if (end == NULL) {
|
||||
break;
|
||||
}
|
||||
memcpy(out, cp, end + 1 - cp);
|
||||
out += (end + 1 - cp);
|
||||
cp = end + 1;
|
||||
|
89
fuzz/input-fuzzer.c
Normal file
89
fuzz/input-fuzzer.c
Normal file
@ -0,0 +1,89 @@
|
||||
/*
|
||||
* Copyright (c) 2020 Sergey Nizovtsev <snizovtsev@gmail.com>
|
||||
*
|
||||
* 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 <stddef.h>
|
||||
#include <assert.h>
|
||||
|
||||
#include "tmux.h"
|
||||
|
||||
#define FUZZER_MAXLEN 512
|
||||
#define PANE_WIDTH 80
|
||||
#define PANE_HEIGHT 25
|
||||
|
||||
struct event_base *libevent;
|
||||
|
||||
int
|
||||
LLVMFuzzerTestOneInput(const unsigned char *data, size_t size)
|
||||
{
|
||||
struct bufferevent *vpty[2];
|
||||
struct window *w;
|
||||
struct window_pane *wp;
|
||||
int error;
|
||||
|
||||
/*
|
||||
* Since AFL doesn't support -max_len paramenter we have to
|
||||
* discard long inputs manually.
|
||||
*/
|
||||
if (size > FUZZER_MAXLEN)
|
||||
return 0;
|
||||
|
||||
w = window_create(PANE_WIDTH, PANE_HEIGHT, 0, 0);
|
||||
wp = window_add_pane(w, NULL, 0, 0);
|
||||
bufferevent_pair_new(libevent, BEV_OPT_CLOSE_ON_FREE, vpty);
|
||||
wp->ictx = input_init(wp, vpty[0]);
|
||||
window_add_ref(w, __func__);
|
||||
|
||||
input_parse_buffer(wp, (u_char*) data, size);
|
||||
while (cmdq_next(NULL) != 0)
|
||||
;
|
||||
error = event_base_loop(libevent, EVLOOP_NONBLOCK);
|
||||
if (error == -1)
|
||||
errx(1, "event_base_loop failed");
|
||||
|
||||
assert(w->references == 1);
|
||||
window_remove_ref(w, __func__);
|
||||
|
||||
bufferevent_free(vpty[0]);
|
||||
bufferevent_free(vpty[1]);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
LLVMFuzzerInitialize(__unused int *argc, __unused char ***argv)
|
||||
{
|
||||
const struct options_table_entry *oe;
|
||||
|
||||
global_environ = environ_create();
|
||||
global_options = options_create(NULL);
|
||||
global_s_options = options_create(NULL);
|
||||
global_w_options = options_create(NULL);
|
||||
for (oe = options_table; oe->name != NULL; oe++) {
|
||||
if (oe->scope & OPTIONS_TABLE_SERVER)
|
||||
options_default(global_options, oe);
|
||||
if (oe->scope & OPTIONS_TABLE_SESSION)
|
||||
options_default(global_s_options, oe);
|
||||
if (oe->scope & OPTIONS_TABLE_WINDOW)
|
||||
options_default(global_w_options, oe);
|
||||
}
|
||||
libevent = osdep_event_init();
|
||||
|
||||
options_set_number(global_w_options, "monitor-bell", 0);
|
||||
options_set_number(global_w_options, "allow-rename", 1);
|
||||
options_set_number(global_options, "set-clipboard", 2);
|
||||
|
||||
return 0;
|
||||
}
|
8
fuzz/input-fuzzer.dict
Normal file
8
fuzz/input-fuzzer.dict
Normal file
@ -0,0 +1,8 @@
|
||||
"\x1b["
|
||||
"1000"
|
||||
"2004"
|
||||
"1049"
|
||||
"38;2"
|
||||
"100;"
|
||||
"tmux;"
|
||||
"rgb:00/00/00"
|
2
fuzz/input-fuzzer.options
Normal file
2
fuzz/input-fuzzer.options
Normal file
@ -0,0 +1,2 @@
|
||||
[libfuzzer]
|
||||
max_len = 512
|
365
grid-reader.c
Normal file
365
grid-reader.c
Normal file
@ -0,0 +1,365 @@
|
||||
/* $OpenBSD$ */
|
||||
|
||||
/*
|
||||
* Copyright (c) 2020 Anindya Mukherjee <anindya49@hotmail.com>
|
||||
*
|
||||
* 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 "tmux.h"
|
||||
#include <string.h>
|
||||
|
||||
/* Initialise virtual cursor. */
|
||||
void
|
||||
grid_reader_start(struct grid_reader *gr, struct grid *gd, u_int cx, u_int cy)
|
||||
{
|
||||
gr->gd = gd;
|
||||
gr->cx = cx;
|
||||
gr->cy = cy;
|
||||
}
|
||||
|
||||
/* Get cursor position from reader. */
|
||||
void
|
||||
grid_reader_get_cursor(struct grid_reader *gr, u_int *cx, u_int *cy)
|
||||
{
|
||||
*cx = gr->cx;
|
||||
*cy = gr->cy;
|
||||
}
|
||||
|
||||
/* Get length of line containing the cursor. */
|
||||
u_int
|
||||
grid_reader_line_length(struct grid_reader *gr)
|
||||
{
|
||||
return (grid_line_length(gr->gd, gr->cy));
|
||||
}
|
||||
|
||||
/* Move cursor forward one position. */
|
||||
void
|
||||
grid_reader_cursor_right(struct grid_reader *gr, int wrap, int all)
|
||||
{
|
||||
u_int px;
|
||||
struct grid_cell gc;
|
||||
|
||||
if (all)
|
||||
px = gr->gd->sx;
|
||||
else
|
||||
px = grid_reader_line_length(gr);
|
||||
|
||||
if (wrap && gr->cx >= px && gr->cy < gr->gd->hsize + gr->gd->sy - 1) {
|
||||
grid_reader_cursor_start_of_line(gr, 0);
|
||||
grid_reader_cursor_down(gr);
|
||||
} else if (gr->cx < px) {
|
||||
gr->cx++;
|
||||
while (gr->cx < px) {
|
||||
grid_get_cell(gr->gd, gr->cx, gr->cy, &gc);
|
||||
if (~gc.flags & GRID_FLAG_PADDING)
|
||||
break;
|
||||
gr->cx++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Move cursor back one position. */
|
||||
void
|
||||
grid_reader_cursor_left(struct grid_reader *gr)
|
||||
{
|
||||
struct grid_cell gc;
|
||||
|
||||
while (gr->cx > 0) {
|
||||
grid_get_cell(gr->gd, gr->cx, gr->cy, &gc);
|
||||
if (~gc.flags & GRID_FLAG_PADDING)
|
||||
break;
|
||||
gr->cx--;
|
||||
}
|
||||
if (gr->cx == 0 && gr->cy > 0) {
|
||||
grid_reader_cursor_up(gr);
|
||||
grid_reader_cursor_end_of_line(gr, 0, 0);
|
||||
} else if (gr->cx > 0)
|
||||
gr->cx--;
|
||||
}
|
||||
|
||||
/* Move cursor down one line. */
|
||||
void
|
||||
grid_reader_cursor_down(struct grid_reader *gr)
|
||||
{
|
||||
struct grid_cell gc;
|
||||
|
||||
if (gr->cy < gr->gd->hsize + gr->gd->sy - 1)
|
||||
gr->cy++;
|
||||
while (gr->cx > 0) {
|
||||
grid_get_cell(gr->gd, gr->cx, gr->cy, &gc);
|
||||
if (~gc.flags & GRID_FLAG_PADDING)
|
||||
break;
|
||||
gr->cx--;
|
||||
}
|
||||
}
|
||||
|
||||
/* Move cursor up one line. */
|
||||
void
|
||||
grid_reader_cursor_up(struct grid_reader *gr)
|
||||
{
|
||||
struct grid_cell gc;
|
||||
|
||||
if (gr->cy > 0)
|
||||
gr->cy--;
|
||||
while (gr->cx > 0) {
|
||||
grid_get_cell(gr->gd, gr->cx, gr->cy, &gc);
|
||||
if (~gc.flags & GRID_FLAG_PADDING)
|
||||
break;
|
||||
gr->cx--;
|
||||
}
|
||||
}
|
||||
|
||||
/* Move cursor to the start of the line. */
|
||||
void
|
||||
grid_reader_cursor_start_of_line(struct grid_reader *gr, int wrap)
|
||||
{
|
||||
if (wrap) {
|
||||
while (gr->cy > 0 &&
|
||||
grid_get_line(gr->gd, gr->cy - 1)->flags &
|
||||
GRID_LINE_WRAPPED)
|
||||
gr->cy--;
|
||||
}
|
||||
gr->cx = 0;
|
||||
}
|
||||
|
||||
/* Move cursor to the end of the line. */
|
||||
void
|
||||
grid_reader_cursor_end_of_line(struct grid_reader *gr, int wrap, int all)
|
||||
{
|
||||
u_int yy;
|
||||
|
||||
if (wrap) {
|
||||
yy = gr->gd->hsize + gr->gd->sy - 1;
|
||||
while (gr->cy < yy && grid_get_line(gr->gd, gr->cy)->flags &
|
||||
GRID_LINE_WRAPPED)
|
||||
gr->cy++;
|
||||
}
|
||||
if (all)
|
||||
gr->cx = gr->gd->sx;
|
||||
else
|
||||
gr->cx = grid_reader_line_length(gr);
|
||||
}
|
||||
|
||||
/* Check if character under cursor is in set. */
|
||||
int
|
||||
grid_reader_in_set(struct grid_reader *gr, const char *set)
|
||||
{
|
||||
struct grid_cell gc;
|
||||
|
||||
grid_get_cell(gr->gd, gr->cx, gr->cy, &gc);
|
||||
if (gc.flags & GRID_FLAG_PADDING)
|
||||
return (0);
|
||||
return (utf8_cstrhas(set, &gc.data));
|
||||
}
|
||||
|
||||
/* Move cursor to the start of the next word. */
|
||||
void
|
||||
grid_reader_cursor_next_word(struct grid_reader *gr, const char *separators)
|
||||
{
|
||||
u_int xx, yy;
|
||||
int expected = 0;
|
||||
|
||||
/* Do not break up wrapped words. */
|
||||
if (grid_get_line(gr->gd, gr->cy)->flags & GRID_LINE_WRAPPED)
|
||||
xx = grid_reader_line_length(gr) - 1;
|
||||
else
|
||||
xx = grid_reader_line_length(gr);
|
||||
yy = gr->gd->hsize + gr->gd->sy - 1;
|
||||
|
||||
/*
|
||||
* If we started inside a word, skip over word characters. Then skip
|
||||
* over separators till the next word.
|
||||
*
|
||||
* expected is initially set to 0 for the former and then 1 for the
|
||||
* latter. It is finally set to 0 when the beginning of the next word is
|
||||
* found.
|
||||
*/
|
||||
do {
|
||||
while (gr->cx > xx ||
|
||||
grid_reader_in_set(gr, separators) == expected) {
|
||||
/* Move down if we are past the end of the line. */
|
||||
if (gr->cx > xx) {
|
||||
if (gr->cy == yy)
|
||||
return;
|
||||
grid_reader_cursor_start_of_line(gr, 0);
|
||||
grid_reader_cursor_down(gr);
|
||||
|
||||
if (grid_get_line(gr->gd, gr->cy)->flags &
|
||||
GRID_LINE_WRAPPED)
|
||||
xx = grid_reader_line_length(gr) - 1;
|
||||
else
|
||||
xx = grid_reader_line_length(gr);
|
||||
} else
|
||||
gr->cx++;
|
||||
}
|
||||
expected = !expected;
|
||||
} while (expected == 1);
|
||||
}
|
||||
|
||||
/* Move cursor to the end of the next word. */
|
||||
void
|
||||
grid_reader_cursor_next_word_end(struct grid_reader *gr, const char *separators)
|
||||
{
|
||||
u_int xx, yy;
|
||||
int expected = 1;
|
||||
|
||||
/* Do not break up wrapped words. */
|
||||
if (grid_get_line(gr->gd, gr->cy)->flags & GRID_LINE_WRAPPED)
|
||||
xx = grid_reader_line_length(gr) - 1;
|
||||
else
|
||||
xx = grid_reader_line_length(gr);
|
||||
yy = gr->gd->hsize + gr->gd->sy - 1;
|
||||
|
||||
/*
|
||||
* If we started on a separator, skip over separators. Then skip over
|
||||
* word characters till the next separator.
|
||||
*
|
||||
* expected is initially set to 1 for the former and then 1 for the
|
||||
* latter. It is finally set to 1 when the end of the next word is
|
||||
* found.
|
||||
*/
|
||||
do {
|
||||
while (gr->cx > xx ||
|
||||
grid_reader_in_set(gr, separators) == expected) {
|
||||
/* Move down if we are past the end of the line. */
|
||||
if (gr->cx > xx) {
|
||||
if (gr->cy == yy)
|
||||
return;
|
||||
grid_reader_cursor_start_of_line(gr, 0);
|
||||
grid_reader_cursor_down(gr);
|
||||
|
||||
if (grid_get_line(gr->gd, gr->cy)->flags &
|
||||
GRID_LINE_WRAPPED)
|
||||
xx = grid_reader_line_length(gr) - 1;
|
||||
else
|
||||
xx = grid_reader_line_length(gr);
|
||||
} else
|
||||
gr->cx++;
|
||||
}
|
||||
expected = !expected;
|
||||
} while (expected == 0);
|
||||
}
|
||||
|
||||
/* Move to the previous place where a word begins. */
|
||||
void
|
||||
grid_reader_cursor_previous_word(struct grid_reader *gr, const char *separators,
|
||||
int already)
|
||||
{
|
||||
int oldx, oldy, r;
|
||||
|
||||
/* Move back to the previous word character. */
|
||||
if (already || grid_reader_in_set(gr, separators)) {
|
||||
for (;;) {
|
||||
if (gr->cx > 0) {
|
||||
gr->cx--;
|
||||
if (!grid_reader_in_set(gr, separators))
|
||||
break;
|
||||
} else {
|
||||
if (gr->cy == 0)
|
||||
return;
|
||||
grid_reader_cursor_up(gr);
|
||||
grid_reader_cursor_end_of_line(gr, 0, 0);
|
||||
|
||||
/* Stop if separator at EOL. */
|
||||
if (gr->cx > 0) {
|
||||
oldx = gr->cx;
|
||||
gr->cx--;
|
||||
r = grid_reader_in_set(gr, separators);
|
||||
gr->cx = oldx;
|
||||
if (r)
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Move back to the beginning of this word. */
|
||||
do {
|
||||
oldx = gr->cx;
|
||||
oldy = gr->cy;
|
||||
if (gr->cx == 0) {
|
||||
if (gr->cy == 0 ||
|
||||
~grid_get_line(gr->gd, gr->cy - 1)->flags &
|
||||
GRID_LINE_WRAPPED)
|
||||
break;
|
||||
grid_reader_cursor_up(gr);
|
||||
grid_reader_cursor_end_of_line(gr, 0, 0);
|
||||
}
|
||||
if (gr->cx > 0)
|
||||
gr->cx--;
|
||||
} while (!grid_reader_in_set(gr, separators));
|
||||
gr->cx = oldx;
|
||||
gr->cy = oldy;
|
||||
}
|
||||
|
||||
/* Jump forward to character. */
|
||||
int
|
||||
grid_reader_cursor_jump(struct grid_reader *gr, const struct utf8_data *jc)
|
||||
{
|
||||
struct grid_cell gc;
|
||||
u_int px, py, xx, yy;
|
||||
|
||||
px = gr->cx;
|
||||
yy = gr->gd->hsize + gr->gd->sy - 1;
|
||||
|
||||
for (py = gr->cy; py <= yy; py++) {
|
||||
xx = grid_line_length(gr->gd, py);
|
||||
while (px < xx) {
|
||||
grid_get_cell(gr->gd, px, py, &gc);
|
||||
if (!(gc.flags & GRID_FLAG_PADDING) &&
|
||||
gc.data.size == jc->size &&
|
||||
memcmp(gc.data.data, jc->data, gc.data.size) == 0) {
|
||||
gr->cx = px;
|
||||
gr->cy = py;
|
||||
return 1;
|
||||
}
|
||||
px++;
|
||||
}
|
||||
|
||||
if (py == yy ||
|
||||
!(grid_get_line(gr->gd, py)->flags & GRID_LINE_WRAPPED))
|
||||
return 0;
|
||||
px = 0;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Jump back to character. */
|
||||
int
|
||||
grid_reader_cursor_jump_back(struct grid_reader *gr, const struct utf8_data *jc)
|
||||
{
|
||||
struct grid_cell gc;
|
||||
u_int px, py, xx;
|
||||
|
||||
xx = gr->cx + 1;
|
||||
|
||||
for (py = gr->cy + 1; py > 0; py--) {
|
||||
for (px = xx; px > 0; px--) {
|
||||
grid_get_cell(gr->gd, px - 1, py - 1, &gc);
|
||||
if (!(gc.flags & GRID_FLAG_PADDING) &&
|
||||
gc.data.size == jc->size &&
|
||||
memcmp(gc.data.data, jc->data, gc.data.size) == 0) {
|
||||
gr->cx = px - 1;
|
||||
gr->cy = py - 1;
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (py == 1 ||
|
||||
!(grid_get_line(gr->gd, py - 2)->flags & GRID_LINE_WRAPPED))
|
||||
return 0;
|
||||
xx = grid_line_length(gr->gd, py - 2);
|
||||
}
|
||||
return 0;
|
||||
}
|
5
grid.c
5
grid.c
@ -701,7 +701,6 @@ grid_move_lines(struct grid *gd, u_int dy, u_int py, u_int ny, u_int bg)
|
||||
gd->linedata[py - 1].flags &= ~GRID_LINE_WRAPPED;
|
||||
}
|
||||
|
||||
|
||||
/* Move a group of cells. */
|
||||
void
|
||||
grid_move_cells(struct grid *gd, u_int dx, u_int px, u_int py, u_int nx,
|
||||
@ -1049,14 +1048,14 @@ grid_duplicate_lines(struct grid *dst, u_int dy, struct grid *src, u_int sy,
|
||||
srcl->cellsize * sizeof *dstl->celldata);
|
||||
} else
|
||||
dstl->celldata = NULL;
|
||||
|
||||
if (srcl->extdsize != 0) {
|
||||
dstl->extdsize = srcl->extdsize;
|
||||
dstl->extddata = xreallocarray(NULL, dstl->extdsize,
|
||||
sizeof *dstl->extddata);
|
||||
memcpy(dstl->extddata, srcl->extddata, dstl->extdsize *
|
||||
sizeof *dstl->extddata);
|
||||
}
|
||||
} else
|
||||
dstl->extddata = NULL;
|
||||
|
||||
sy++;
|
||||
dy++;
|
||||
|
142
input.c
142
input.c
@ -138,6 +138,8 @@ static void input_osc_10(struct input_ctx *, const char *);
|
||||
static void input_osc_11(struct input_ctx *, const char *);
|
||||
static void input_osc_52(struct input_ctx *, const char *);
|
||||
static void input_osc_104(struct input_ctx *, const char *);
|
||||
static void input_osc_110(struct input_ctx *, const char *);
|
||||
static void input_osc_111(struct input_ctx *, const char *);
|
||||
|
||||
/* Transition entry/exit handlers. */
|
||||
static void input_clear(struct input_ctx *);
|
||||
@ -2099,6 +2101,7 @@ input_csi_dispatch_sgr(struct input_ctx *ictx)
|
||||
gc->attr |= GRID_ATTR_UNDERSCORE;
|
||||
break;
|
||||
case 5:
|
||||
case 6:
|
||||
gc->attr |= GRID_ATTR_BLINK;
|
||||
break;
|
||||
case 7:
|
||||
@ -2110,6 +2113,10 @@ input_csi_dispatch_sgr(struct input_ctx *ictx)
|
||||
case 9:
|
||||
gc->attr |= GRID_ATTR_STRIKETHROUGH;
|
||||
break;
|
||||
case 21:
|
||||
gc->attr &= ~GRID_ATTR_ALL_UNDERSCORE;
|
||||
gc->attr |= GRID_ATTR_UNDERSCORE_2;
|
||||
break;
|
||||
case 22:
|
||||
gc->attr &= ~(GRID_ATTR_BRIGHT|GRID_ATTR_DIM);
|
||||
break;
|
||||
@ -2304,6 +2311,12 @@ input_exit_osc(struct input_ctx *ictx)
|
||||
case 104:
|
||||
input_osc_104(ictx, p);
|
||||
break;
|
||||
case 110:
|
||||
input_osc_110(ictx, p);
|
||||
break;
|
||||
case 111:
|
||||
input_osc_111(ictx, p);
|
||||
break;
|
||||
case 112:
|
||||
if (*p == '\0') /* no arguments allowed */
|
||||
screen_set_cursor_colour(sctx->s, "");
|
||||
@ -2422,50 +2435,43 @@ input_top_bit_set(struct input_ctx *ictx)
|
||||
|
||||
/* Parse colour from OSC. */
|
||||
static int
|
||||
input_osc_parse_colour(const char *p, u_int *r, u_int *g, u_int *b)
|
||||
input_osc_parse_colour(const char *p)
|
||||
{
|
||||
u_int rsize, gsize, bsize;
|
||||
const char *cp, *s = p;
|
||||
double c, m, y, k = 0;
|
||||
u_int r, g, b;
|
||||
size_t len = strlen(p);
|
||||
int colour = -1;
|
||||
char *copy;
|
||||
|
||||
if (sscanf(p, "rgb:%x/%x/%x", r, g, b) != 3)
|
||||
return (0);
|
||||
p += 4;
|
||||
|
||||
cp = strchr(p, '/');
|
||||
rsize = cp - p;
|
||||
if (rsize == 1)
|
||||
(*r) = (*r) | ((*r) << 4);
|
||||
else if (rsize == 3)
|
||||
(*r) >>= 4;
|
||||
else if (rsize == 4)
|
||||
(*r) >>= 8;
|
||||
else if (rsize != 2)
|
||||
return (0);
|
||||
|
||||
p = cp + 1;
|
||||
cp = strchr(p, '/');
|
||||
gsize = cp - p;
|
||||
if (gsize == 1)
|
||||
(*g) = (*g) | ((*g) << 4);
|
||||
else if (gsize == 3)
|
||||
(*g) >>= 4;
|
||||
else if (gsize == 4)
|
||||
(*g) >>= 8;
|
||||
else if (gsize != 2)
|
||||
return (0);
|
||||
|
||||
bsize = strlen(cp + 1);
|
||||
if (bsize == 1)
|
||||
(*b) = (*b) | ((*b) << 4);
|
||||
else if (bsize == 3)
|
||||
(*b) >>= 4;
|
||||
else if (bsize == 4)
|
||||
(*b) >>= 8;
|
||||
else if (bsize != 2)
|
||||
return (0);
|
||||
|
||||
log_debug("%s: %s = %02x%02x%02x", __func__, s, *r, *g, *b);
|
||||
return (1);
|
||||
if ((len == 12 && sscanf(p, "rgb:%02x/%02x/%02x", &r, &g, &b) == 3) ||
|
||||
(len == 7 && sscanf(p, "#%02x%02x%02x", &r, &g, &b) == 3) ||
|
||||
sscanf(p, "%d,%d,%d", &r, &g, &b) == 3)
|
||||
colour = colour_join_rgb(r, g, b);
|
||||
else if ((len == 18 &&
|
||||
sscanf(p, "rgb:%04x/%04x/%04x", &r, &g, &b) == 3) ||
|
||||
(len == 13 && sscanf(p, "#%04x%04x%04x", &r, &g, &b) == 3))
|
||||
colour = colour_join_rgb(r >> 8, g >> 8, b >> 8);
|
||||
else if ((sscanf(p, "cmyk:%lf/%lf/%lf/%lf", &c, &m, &y, &k) == 4 ||
|
||||
sscanf(p, "cmy:%lf/%lf/%lf", &c, &m, &y) == 3) &&
|
||||
c >= 0 && c <= 1 && m >= 0 && m <= 1 &&
|
||||
y >= 0 && y <= 1 && k >= 0 && k <= 1) {
|
||||
colour = colour_join_rgb(
|
||||
(1 - c) * (1 - k) * 255,
|
||||
(1 - m) * (1 - k) * 255,
|
||||
(1 - y) * (1 - k) * 255);
|
||||
} else {
|
||||
while (len != 0 && *p == ' ') {
|
||||
p++;
|
||||
len--;
|
||||
}
|
||||
while (len != 0 && p[len - 1] == ' ')
|
||||
len--;
|
||||
copy = xstrndup(p, len);
|
||||
colour = colour_byname(copy);
|
||||
free(copy);
|
||||
}
|
||||
log_debug("%s: %s = %s", __func__, p, colour_tostring(colour));
|
||||
return (colour);
|
||||
}
|
||||
|
||||
/* Reply to a colour request. */
|
||||
@ -2493,7 +2499,7 @@ input_osc_4(struct input_ctx *ictx, const char *p)
|
||||
struct window_pane *wp = ictx->wp;
|
||||
char *copy, *s, *next = NULL;
|
||||
long idx;
|
||||
u_int r, g, b;
|
||||
int c;
|
||||
|
||||
if (wp == NULL)
|
||||
return;
|
||||
@ -2507,12 +2513,12 @@ input_osc_4(struct input_ctx *ictx, const char *p)
|
||||
goto bad;
|
||||
|
||||
s = strsep(&next, ";");
|
||||
if (!input_osc_parse_colour(s, &r, &g, &b)) {
|
||||
if ((c = input_osc_parse_colour(s)) == -1) {
|
||||
s = next;
|
||||
continue;
|
||||
}
|
||||
|
||||
window_pane_set_palette(wp, idx, colour_join_rgb(r, g, b));
|
||||
window_pane_set_palette(wp, idx, c);
|
||||
s = next;
|
||||
}
|
||||
|
||||
@ -2530,7 +2536,7 @@ input_osc_10(struct input_ctx *ictx, const char *p)
|
||||
{
|
||||
struct window_pane *wp = ictx->wp;
|
||||
struct grid_cell defaults;
|
||||
u_int r, g, b;
|
||||
int c;
|
||||
|
||||
if (wp == NULL)
|
||||
return;
|
||||
@ -2541,9 +2547,9 @@ input_osc_10(struct input_ctx *ictx, const char *p)
|
||||
return;
|
||||
}
|
||||
|
||||
if (!input_osc_parse_colour(p, &r, &g, &b))
|
||||
if ((c = input_osc_parse_colour(p)) == -1)
|
||||
goto bad;
|
||||
wp->fg = colour_join_rgb(r, g, b);
|
||||
wp->fg = c;
|
||||
wp->flags |= (PANE_REDRAW|PANE_STYLECHANGED);
|
||||
|
||||
return;
|
||||
@ -2552,13 +2558,29 @@ bad:
|
||||
log_debug("bad OSC 10: %s", p);
|
||||
}
|
||||
|
||||
/* Handle the OSC 110 sequence for resetting background colour. */
|
||||
static void
|
||||
input_osc_110(struct input_ctx *ictx, const char *p)
|
||||
{
|
||||
struct window_pane *wp = ictx->wp;
|
||||
|
||||
if (wp == NULL)
|
||||
return;
|
||||
|
||||
if (*p != '\0')
|
||||
return;
|
||||
|
||||
wp->fg = 8;
|
||||
wp->flags |= (PANE_REDRAW|PANE_STYLECHANGED);
|
||||
}
|
||||
|
||||
/* Handle the OSC 11 sequence for setting and querying background colour. */
|
||||
static void
|
||||
input_osc_11(struct input_ctx *ictx, const char *p)
|
||||
{
|
||||
struct window_pane *wp = ictx->wp;
|
||||
struct grid_cell defaults;
|
||||
u_int r, g, b;
|
||||
int c;
|
||||
|
||||
if (wp == NULL)
|
||||
return;
|
||||
@ -2569,9 +2591,9 @@ input_osc_11(struct input_ctx *ictx, const char *p)
|
||||
return;
|
||||
}
|
||||
|
||||
if (!input_osc_parse_colour(p, &r, &g, &b))
|
||||
goto bad;
|
||||
wp->bg = colour_join_rgb(r, g, b);
|
||||
if ((c = input_osc_parse_colour(p)) == -1)
|
||||
goto bad;
|
||||
wp->bg = c;
|
||||
wp->flags |= (PANE_REDRAW|PANE_STYLECHANGED);
|
||||
|
||||
return;
|
||||
@ -2580,6 +2602,22 @@ bad:
|
||||
log_debug("bad OSC 11: %s", p);
|
||||
}
|
||||
|
||||
/* Handle the OSC 111 sequence for resetting background colour. */
|
||||
static void
|
||||
input_osc_111(struct input_ctx *ictx, const char *p)
|
||||
{
|
||||
struct window_pane *wp = ictx->wp;
|
||||
|
||||
if (wp == NULL)
|
||||
return;
|
||||
|
||||
if (*p != '\0')
|
||||
return;
|
||||
|
||||
wp->bg = 8;
|
||||
wp->flags |= (PANE_REDRAW|PANE_STYLECHANGED);
|
||||
}
|
||||
|
||||
/* Handle the OSC 52 sequence for setting the clipboard. */
|
||||
static void
|
||||
input_osc_52(struct input_ctx *ictx, const char *p)
|
||||
|
54
job.c
54
job.c
@ -66,19 +66,20 @@ struct job {
|
||||
/* All jobs list. */
|
||||
static LIST_HEAD(joblist, job) all_jobs = LIST_HEAD_INITIALIZER(all_jobs);
|
||||
|
||||
/* Start a job running, if it isn't already. */
|
||||
/* Start a job running. */
|
||||
struct job *
|
||||
job_run(const char *cmd, struct session *s, const char *cwd,
|
||||
job_update_cb updatecb, job_complete_cb completecb, job_free_cb freecb,
|
||||
void *data, int flags, int sx, int sy)
|
||||
job_run(const char *cmd, int argc, char **argv, struct session *s,
|
||||
const char *cwd, job_update_cb updatecb, job_complete_cb completecb,
|
||||
job_free_cb freecb, void *data, int flags, int sx, int sy)
|
||||
{
|
||||
struct job *job;
|
||||
struct environ *env;
|
||||
pid_t pid;
|
||||
int nullfd, out[2], master;
|
||||
const char *home;
|
||||
sigset_t set, oldset;
|
||||
struct winsize ws;
|
||||
struct job *job;
|
||||
struct environ *env;
|
||||
pid_t pid;
|
||||
int nullfd, out[2], master;
|
||||
const char *home;
|
||||
sigset_t set, oldset;
|
||||
struct winsize ws;
|
||||
char **argvp;
|
||||
|
||||
/*
|
||||
* Do not set TERM during .tmux.conf, it is nice to be able to use
|
||||
@ -99,7 +100,13 @@ job_run(const char *cmd, struct session *s, const char *cwd,
|
||||
goto fail;
|
||||
pid = fork();
|
||||
}
|
||||
log_debug("%s: cmd=%s, cwd=%s", __func__, cmd, cwd == NULL ? "" : cwd);
|
||||
if (cmd == NULL) {
|
||||
cmd_log_argv(argc, argv, "%s:", __func__);
|
||||
log_debug("%s: cwd=%s", __func__, cwd == NULL ? "" : cwd);
|
||||
} else {
|
||||
log_debug("%s: cmd=%s, cwd=%s", __func__, cmd,
|
||||
cwd == NULL ? "" : cwd);
|
||||
}
|
||||
|
||||
switch (pid) {
|
||||
case -1:
|
||||
@ -112,10 +119,10 @@ job_run(const char *cmd, struct session *s, const char *cwd,
|
||||
proc_clear_signals(server_proc, 1);
|
||||
sigprocmask(SIG_SETMASK, &oldset, NULL);
|
||||
|
||||
if (cwd == NULL || chdir(cwd) != 0) {
|
||||
if ((home = find_home()) == NULL || chdir(home) != 0)
|
||||
chdir("/");
|
||||
}
|
||||
if ((cwd == NULL || chdir(cwd) != 0) &&
|
||||
((home = find_home()) == NULL || chdir(home) != 0) &&
|
||||
chdir("/") != 0)
|
||||
fatal("chdir failed");
|
||||
|
||||
environ_push(env);
|
||||
environ_free(env);
|
||||
@ -139,8 +146,14 @@ job_run(const char *cmd, struct session *s, const char *cwd,
|
||||
}
|
||||
closefrom(STDERR_FILENO + 1);
|
||||
|
||||
execl(_PATH_BSHELL, "sh", "-c", cmd, (char *) NULL);
|
||||
fatal("execl failed");
|
||||
if (cmd != NULL) {
|
||||
execl(_PATH_BSHELL, "sh", "-c", cmd, (char *) NULL);
|
||||
fatal("execl failed");
|
||||
} else {
|
||||
argvp = cmd_copy_argv(argc, argv);
|
||||
execvp(argvp[0], argvp);
|
||||
fatal("execvp failed");
|
||||
}
|
||||
}
|
||||
|
||||
sigprocmask(SIG_SETMASK, &oldset, NULL);
|
||||
@ -150,7 +163,10 @@ job_run(const char *cmd, struct session *s, const char *cwd,
|
||||
job->state = JOB_RUNNING;
|
||||
job->flags = flags;
|
||||
|
||||
job->cmd = xstrdup(cmd);
|
||||
if (cmd != NULL)
|
||||
job->cmd = xstrdup(cmd);
|
||||
else
|
||||
job->cmd = cmd_stringify_argv(argc, argv);
|
||||
job->pid = pid;
|
||||
job->status = 0;
|
||||
|
||||
|
2
names.c
2
names.c
@ -109,6 +109,8 @@ default_window_name(struct window *w)
|
||||
{
|
||||
char *cmd, *s;
|
||||
|
||||
if (w->active == NULL)
|
||||
return (xstrdup(""));
|
||||
cmd = cmd_stringify_argv(w->active->argc, w->active->argv);
|
||||
if (cmd != NULL && *cmd != '\0')
|
||||
s = parse_window_name(cmd);
|
||||
|
@ -68,6 +68,12 @@ static const char *options_table_set_clipboard_list[] = {
|
||||
static const char *options_table_window_size_list[] = {
|
||||
"largest", "smallest", "manual", "latest", NULL
|
||||
};
|
||||
static const char *options_table_remain_on_exit_list[] = {
|
||||
"off", "on", "failed", NULL
|
||||
};
|
||||
static const char *options_table_detach_on_destroy_list[] = {
|
||||
"off", "on", "no-detached", NULL
|
||||
};
|
||||
|
||||
/* Status line format. */
|
||||
#define OPTIONS_TABLE_STATUS_FORMAT1 \
|
||||
@ -401,8 +407,9 @@ const struct options_table_entry options_table[] = {
|
||||
},
|
||||
|
||||
{ .name = "detach-on-destroy",
|
||||
.type = OPTIONS_TABLE_FLAG,
|
||||
.type = OPTIONS_TABLE_CHOICE,
|
||||
.scope = OPTIONS_TABLE_SESSION,
|
||||
.choices = options_table_detach_on_destroy_list,
|
||||
.default_num = 1,
|
||||
.text = "Whether to detach when a session is destroyed, or switch "
|
||||
"the client to another session if any exist."
|
||||
@ -948,16 +955,17 @@ const struct options_table_entry options_table[] = {
|
||||
},
|
||||
|
||||
{ .name = "remain-on-exit",
|
||||
.type = OPTIONS_TABLE_FLAG,
|
||||
.type = OPTIONS_TABLE_CHOICE,
|
||||
.scope = OPTIONS_TABLE_WINDOW|OPTIONS_TABLE_PANE,
|
||||
.choices = options_table_remain_on_exit_list,
|
||||
.default_num = 0,
|
||||
.text = "Whether panes should remain ('on') or be automatically "
|
||||
"killed ('off') when the program inside exits."
|
||||
"killed ('off' or 'failed') when the program inside exits."
|
||||
},
|
||||
|
||||
{ .name = "synchronize-panes",
|
||||
.type = OPTIONS_TABLE_FLAG,
|
||||
.scope = OPTIONS_TABLE_WINDOW,
|
||||
.scope = OPTIONS_TABLE_WINDOW|OPTIONS_TABLE_PANE,
|
||||
.default_num = 0,
|
||||
.text = "Whether typing should be sent to all panes simultaneously."
|
||||
},
|
||||
|
@ -157,8 +157,7 @@ options_value_to_string(struct options_entry *o, union options_value *ov,
|
||||
case OPTIONS_TABLE_CHOICE:
|
||||
s = xstrdup(o->tableentry->choices[ov->number]);
|
||||
break;
|
||||
case OPTIONS_TABLE_STRING:
|
||||
case OPTIONS_TABLE_COMMAND:
|
||||
default:
|
||||
fatalx("not a number option type");
|
||||
}
|
||||
return (s);
|
||||
@ -311,6 +310,8 @@ options_default_to_string(const struct options_table_entry *oe)
|
||||
case OPTIONS_TABLE_CHOICE:
|
||||
s = xstrdup(oe->choices[oe->default_num]);
|
||||
break;
|
||||
default:
|
||||
fatalx("unknown option type");
|
||||
}
|
||||
return (s);
|
||||
}
|
||||
@ -703,7 +704,7 @@ options_get_number(struct options *oo, const char *name)
|
||||
if (o == NULL)
|
||||
fatalx("missing option %s", name);
|
||||
if (!OPTIONS_IS_NUMBER(o))
|
||||
fatalx("option %s is not a number", name);
|
||||
fatalx("option %s is not a number", name);
|
||||
return (o->value.number);
|
||||
}
|
||||
|
||||
|
@ -19,7 +19,6 @@
|
||||
#include <sys/param.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
#include <event.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
|
@ -20,12 +20,13 @@
|
||||
#include <sys/sysctl.h>
|
||||
|
||||
#include <Availability.h>
|
||||
#include <event.h>
|
||||
#include <libproc.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "compat.h"
|
||||
|
||||
char *osdep_get_name(int, char *);
|
||||
char *osdep_get_cwd(int);
|
||||
struct event_base *osdep_event_init(void);
|
||||
|
@ -23,7 +23,6 @@
|
||||
|
||||
#include <err.h>
|
||||
#include <errno.h>
|
||||
#include <event.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
@ -24,7 +24,6 @@
|
||||
|
||||
#include <err.h>
|
||||
#include <errno.h>
|
||||
#include <event.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
52
osdep-haiku.c
Normal file
52
osdep-haiku.c
Normal file
@ -0,0 +1,52 @@
|
||||
/* $OpenBSD$ */
|
||||
|
||||
/*
|
||||
* Copyright (c) 2009 Nicholas Marriott <nicholas.marriott@gmail.com>
|
||||
*
|
||||
* 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 <kernel/OS.h>
|
||||
|
||||
#include "tmux.h"
|
||||
|
||||
char *
|
||||
osdep_get_name(int fd, __unused char *tty)
|
||||
{
|
||||
pid_t tid;
|
||||
team_info tinfo;
|
||||
|
||||
if ((tid = tcgetpgrp(fd)) == -1)
|
||||
return (NULL);
|
||||
|
||||
if (get_team_info(tid, &tinfo) != B_OK)
|
||||
return (NULL);
|
||||
|
||||
/* Up to the first 64 characters. */
|
||||
return (xstrdup(tinfo.args));
|
||||
}
|
||||
|
||||
char *
|
||||
osdep_get_cwd(int fd)
|
||||
{
|
||||
return (NULL);
|
||||
}
|
||||
|
||||
struct event_base *
|
||||
osdep_event_init(void)
|
||||
{
|
||||
return (event_init());
|
||||
}
|
@ -18,8 +18,6 @@
|
||||
|
||||
#include <sys/types.h>
|
||||
|
||||
#include <event.h>
|
||||
|
||||
#include "tmux.h"
|
||||
|
||||
char *
|
||||
|
@ -20,7 +20,6 @@
|
||||
#include <sys/stat.h>
|
||||
#include <sys/param.h>
|
||||
|
||||
#include <event.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
|
@ -22,7 +22,6 @@
|
||||
#include <sys/sysctl.h>
|
||||
|
||||
#include <errno.h>
|
||||
#include <event.h>
|
||||
#include <limits.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
@ -23,11 +23,12 @@
|
||||
#include <sys/stat.h>
|
||||
|
||||
#include <errno.h>
|
||||
#include <event.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "compat.h"
|
||||
|
||||
#ifndef nitems
|
||||
#define nitems(_a) (sizeof((_a)) / sizeof((_a)[0]))
|
||||
#endif
|
||||
|
@ -19,7 +19,6 @@
|
||||
#include <sys/param.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
#include <event.h>
|
||||
#include <fcntl.h>
|
||||
#include <procfs.h>
|
||||
#include <stdio.h>
|
||||
|
@ -18,8 +18,6 @@
|
||||
|
||||
#include <sys/types.h>
|
||||
|
||||
#include <event.h>
|
||||
|
||||
#include "tmux.h"
|
||||
|
||||
char *
|
||||
|
211
popup.c
211
popup.c
@ -31,13 +31,7 @@ struct popup_data {
|
||||
struct cmdq_item *item;
|
||||
int flags;
|
||||
|
||||
char **lines;
|
||||
u_int nlines;
|
||||
|
||||
char *cmd;
|
||||
struct cmd_find_state fs;
|
||||
struct screen s;
|
||||
|
||||
struct job *job;
|
||||
struct input_ctx *ictx;
|
||||
int status;
|
||||
@ -104,54 +98,11 @@ popup_init_ctx_cb(struct screen_write_ctx *ctx, struct tty_ctx *ttyctx)
|
||||
ttyctx->arg = pd;
|
||||
}
|
||||
|
||||
static void
|
||||
popup_write_screen(struct client *c, struct popup_data *pd)
|
||||
{
|
||||
struct cmdq_item *item = pd->item;
|
||||
struct screen_write_ctx ctx;
|
||||
char *copy, *next, *loop, *tmp;
|
||||
struct format_tree *ft;
|
||||
u_int i, y;
|
||||
|
||||
ft = format_create(c, item, FORMAT_NONE, 0);
|
||||
if (cmd_find_valid_state(&pd->fs))
|
||||
format_defaults(ft, c, pd->fs.s, pd->fs.wl, pd->fs.wp);
|
||||
else
|
||||
format_defaults(ft, c, NULL, NULL, NULL);
|
||||
|
||||
screen_write_start(&ctx, &pd->s);
|
||||
screen_write_clearscreen(&ctx, 8);
|
||||
|
||||
y = 0;
|
||||
for (i = 0; i < pd->nlines; i++) {
|
||||
if (y == pd->sy - 2)
|
||||
break;
|
||||
copy = next = xstrdup(pd->lines[i]);
|
||||
while ((loop = strsep(&next, "\n")) != NULL) {
|
||||
if (y == pd->sy - 2)
|
||||
break;
|
||||
tmp = format_expand(ft, loop);
|
||||
screen_write_cursormove(&ctx, 0, y, 0);
|
||||
format_draw(&ctx, &grid_default_cell, pd->sx - 2, tmp,
|
||||
NULL);
|
||||
free(tmp);
|
||||
y++;
|
||||
}
|
||||
free(copy);
|
||||
}
|
||||
|
||||
format_free(ft);
|
||||
screen_write_cursormove(&ctx, 0, y, 0);
|
||||
screen_write_stop(&ctx);
|
||||
}
|
||||
|
||||
static struct screen *
|
||||
popup_mode_cb(struct client *c, u_int *cx, u_int *cy)
|
||||
{
|
||||
struct popup_data *pd = c->overlay_data;
|
||||
|
||||
if (pd->ictx == NULL)
|
||||
return (0);
|
||||
*cx = pd->px + 1 + pd->s.cx;
|
||||
*cy = pd->py + 1 + pd->s.cy;
|
||||
return (&pd->s);
|
||||
@ -199,14 +150,12 @@ popup_free_cb(struct client *c)
|
||||
{
|
||||
struct popup_data *pd = c->overlay_data;
|
||||
struct cmdq_item *item = pd->item;
|
||||
u_int i;
|
||||
|
||||
if (pd->cb != NULL)
|
||||
pd->cb(pd->status, pd->arg);
|
||||
|
||||
if (item != NULL) {
|
||||
if (pd->ictx != NULL &&
|
||||
cmdq_get_client(item) != NULL &&
|
||||
if (cmdq_get_client(item) != NULL &&
|
||||
cmdq_get_client(item)->session == NULL)
|
||||
cmdq_get_client(item)->retval = pd->status;
|
||||
cmdq_continue(item);
|
||||
@ -215,15 +164,9 @@ popup_free_cb(struct client *c)
|
||||
|
||||
if (pd->job != NULL)
|
||||
job_free(pd->job);
|
||||
if (pd->ictx != NULL)
|
||||
input_free(pd->ictx);
|
||||
|
||||
for (i = 0; i < pd->nlines; i++)
|
||||
free(pd->lines[i]);
|
||||
free(pd->lines);
|
||||
input_free(pd->ictx);
|
||||
|
||||
screen_free(&pd->s);
|
||||
free(pd->cmd);
|
||||
free(pd);
|
||||
}
|
||||
|
||||
@ -262,9 +205,7 @@ popup_handle_drag(struct client *c, struct popup_data *pd,
|
||||
pd->sy = m->y - pd->py;
|
||||
|
||||
screen_resize(&pd->s, pd->sx - 2, pd->sy - 2, 0);
|
||||
if (pd->ictx == NULL)
|
||||
popup_write_screen(c, pd);
|
||||
else if (pd->job != NULL)
|
||||
if (pd->job != NULL)
|
||||
job_resize(pd->job, pd->sx - 2, pd->sy - 2);
|
||||
server_redraw_client(c);
|
||||
}
|
||||
@ -275,13 +216,8 @@ popup_key_cb(struct client *c, struct key_event *event)
|
||||
{
|
||||
struct popup_data *pd = c->overlay_data;
|
||||
struct mouse_event *m = &event->m;
|
||||
struct cmd_find_state *fs = &pd->fs;
|
||||
struct format_tree *ft;
|
||||
const char *cmd, *buf;
|
||||
const char *buf;
|
||||
size_t len;
|
||||
struct cmdq_state *state;
|
||||
enum cmd_parse_status status;
|
||||
char *error;
|
||||
|
||||
if (KEYC_IS_MOUSE(event->key)) {
|
||||
if (pd->dragging != OFF) {
|
||||
@ -313,56 +249,22 @@ popup_key_cb(struct client *c, struct key_event *event)
|
||||
}
|
||||
}
|
||||
|
||||
if (pd->ictx != NULL && (pd->flags & POPUP_WRITEKEYS)) {
|
||||
if (((pd->flags & (POPUP_CLOSEEXIT|POPUP_CLOSEEXITZERO)) == 0 ||
|
||||
pd->job == NULL) &&
|
||||
(event->key == '\033' || event->key == '\003'))
|
||||
return (1);
|
||||
if (pd->job == NULL)
|
||||
return (0);
|
||||
if ((((pd->flags & (POPUP_CLOSEEXIT|POPUP_CLOSEEXITZERO)) == 0) ||
|
||||
pd->job == NULL) &&
|
||||
(event->key == '\033' || event->key == '\003'))
|
||||
return (1);
|
||||
if (pd->job != NULL) {
|
||||
if (KEYC_IS_MOUSE(event->key)) {
|
||||
/* Must be inside, checked already. */
|
||||
if (!input_key_get_mouse(&pd->s, m, m->x - pd->px,
|
||||
m->y - pd->py, &buf, &len))
|
||||
if (!input_key_get_mouse(&pd->s, m, m->x - pd->px - 1,
|
||||
m->y - pd->py - 1, &buf, &len))
|
||||
return (0);
|
||||
bufferevent_write(job_get_event(pd->job), buf, len);
|
||||
return (0);
|
||||
}
|
||||
input_key(&pd->s, job_get_event(pd->job), event->key);
|
||||
return (0);
|
||||
}
|
||||
|
||||
if (pd->cmd == NULL)
|
||||
return (1);
|
||||
|
||||
ft = format_create(NULL, pd->item, FORMAT_NONE, 0);
|
||||
if (cmd_find_valid_state(fs))
|
||||
format_defaults(ft, c, fs->s, fs->wl, fs->wp);
|
||||
else
|
||||
format_defaults(ft, c, NULL, NULL, NULL);
|
||||
format_add(ft, "popup_key", "%s", key_string_lookup_key(event->key, 0));
|
||||
if (KEYC_IS_MOUSE(event->key)) {
|
||||
format_add(ft, "popup_mouse", "1");
|
||||
format_add(ft, "popup_mouse_x", "%u", m->x - pd->px);
|
||||
format_add(ft, "popup_mouse_y", "%u", m->y - pd->py);
|
||||
}
|
||||
cmd = format_expand(ft, pd->cmd);
|
||||
format_free(ft);
|
||||
|
||||
if (pd->item != NULL)
|
||||
event = cmdq_get_event(pd->item);
|
||||
else
|
||||
event = NULL;
|
||||
state = cmdq_new_state(&pd->fs, event, 0);
|
||||
|
||||
status = cmd_parse_and_append(cmd, NULL, c, state, &error);
|
||||
if (status == CMD_PARSE_ERROR) {
|
||||
cmdq_append(c, cmdq_get_error(error));
|
||||
free(error);
|
||||
}
|
||||
cmdq_free_state(state);
|
||||
|
||||
return (1);
|
||||
return (0);
|
||||
|
||||
out:
|
||||
pd->lx = m->x;
|
||||
@ -415,62 +317,12 @@ popup_job_complete_cb(struct job *job)
|
||||
server_client_clear_overlay(pd->c);
|
||||
}
|
||||
|
||||
u_int
|
||||
popup_height(u_int nlines, const char **lines)
|
||||
{
|
||||
char *copy, *next, *loop;
|
||||
u_int i, height = 0;
|
||||
|
||||
for (i = 0; i < nlines; i++) {
|
||||
copy = next = xstrdup(lines[i]);
|
||||
while ((loop = strsep(&next, "\n")) != NULL)
|
||||
height++;
|
||||
free(copy);
|
||||
}
|
||||
|
||||
return (height);
|
||||
}
|
||||
|
||||
u_int
|
||||
popup_width(struct cmdq_item *item, u_int nlines, const char **lines,
|
||||
struct client *c, struct cmd_find_state *fs)
|
||||
{
|
||||
char *copy, *next, *loop, *tmp;
|
||||
struct format_tree *ft;
|
||||
u_int i, width = 0, tmpwidth;
|
||||
|
||||
ft = format_create(cmdq_get_client(item), item, FORMAT_NONE, 0);
|
||||
if (fs != NULL && cmd_find_valid_state(fs))
|
||||
format_defaults(ft, c, fs->s, fs->wl, fs->wp);
|
||||
else
|
||||
format_defaults(ft, c, NULL, NULL, NULL);
|
||||
|
||||
for (i = 0; i < nlines; i++) {
|
||||
copy = next = xstrdup(lines[i]);
|
||||
while ((loop = strsep(&next, "\n")) != NULL) {
|
||||
tmp = format_expand(ft, loop);
|
||||
tmpwidth = format_width(tmp);
|
||||
if (tmpwidth > width)
|
||||
width = tmpwidth;
|
||||
free(tmp);
|
||||
}
|
||||
free(copy);
|
||||
}
|
||||
|
||||
format_free(ft);
|
||||
return (width);
|
||||
}
|
||||
|
||||
int
|
||||
popup_display(int flags, struct cmdq_item *item, u_int px, u_int py, u_int sx,
|
||||
u_int sy, u_int nlines, const char **lines, const char *shellcmd,
|
||||
const char *cmd, const char *cwd, struct client *c,
|
||||
struct cmd_find_state *fs, popup_close_cb cb, void *arg)
|
||||
u_int sy, const char *shellcmd, int argc, char **argv, const char *cwd,
|
||||
struct client *c, struct session *s, popup_close_cb cb, void *arg)
|
||||
{
|
||||
struct popup_data *pd;
|
||||
u_int i;
|
||||
struct session *s;
|
||||
int jobflags;
|
||||
|
||||
if (sx < 3 || sy < 3)
|
||||
return (-1);
|
||||
@ -488,39 +340,17 @@ popup_display(int flags, struct cmdq_item *item, u_int px, u_int py, u_int sx,
|
||||
pd->arg = arg;
|
||||
pd->status = 128 + SIGHUP;
|
||||
|
||||
if (fs != NULL)
|
||||
cmd_find_copy_state(&pd->fs, fs);
|
||||
screen_init(&pd->s, sx - 2, sy - 2, 0);
|
||||
|
||||
if (cmd != NULL)
|
||||
pd->cmd = xstrdup(cmd);
|
||||
|
||||
pd->px = px;
|
||||
pd->py = py;
|
||||
pd->sx = sx;
|
||||
pd->sy = sy;
|
||||
|
||||
pd->nlines = nlines;
|
||||
if (pd->nlines != 0)
|
||||
pd->lines = xreallocarray(NULL, pd->nlines, sizeof *pd->lines);
|
||||
|
||||
for (i = 0; i < pd->nlines; i++)
|
||||
pd->lines[i] = xstrdup(lines[i]);
|
||||
popup_write_screen(c, pd);
|
||||
|
||||
if (shellcmd != NULL) {
|
||||
if (fs != NULL)
|
||||
s = fs->s;
|
||||
else
|
||||
s = NULL;
|
||||
jobflags = JOB_NOWAIT|JOB_PTY;
|
||||
if (flags & POPUP_WRITEKEYS)
|
||||
jobflags |= JOB_KEEPWRITE;
|
||||
pd->job = job_run(shellcmd, s, cwd, popup_job_update_cb,
|
||||
popup_job_complete_cb, NULL, pd, jobflags, pd->sx - 2,
|
||||
pd->sy - 2);
|
||||
pd->ictx = input_init(NULL, job_get_event(pd->job));
|
||||
}
|
||||
pd->job = job_run(shellcmd, argc, argv, s, cwd,
|
||||
popup_job_update_cb, popup_job_complete_cb, NULL, pd,
|
||||
JOB_NOWAIT|JOB_PTY|JOB_KEEPWRITE, pd->sx - 2, pd->sy - 2);
|
||||
pd->ictx = input_init(NULL, job_get_event(pd->job));
|
||||
|
||||
server_client_set_overlay(c, 0, popup_check_cb, popup_mode_cb,
|
||||
popup_draw_cb, popup_key_cb, popup_free_cb, pd);
|
||||
@ -606,9 +436,8 @@ popup_editor(struct client *c, const char *buf, size_t len,
|
||||
py = (c->tty.sy / 2) - (sy / 2);
|
||||
|
||||
xasprintf(&cmd, "%s %s", editor, path);
|
||||
if (popup_display(POPUP_WRITEKEYS|POPUP_CLOSEEXIT, NULL, px, py, sx, sy,
|
||||
0, NULL, cmd, NULL, _PATH_TMP, c, NULL, popup_editor_close_cb,
|
||||
pe) != 0) {
|
||||
if (popup_display(POPUP_CLOSEEXIT, NULL, px, py, sx, sy, cmd, 0, NULL,
|
||||
_PATH_TMP, c, NULL, popup_editor_close_cb, pe) != 0) {
|
||||
popup_editor_free(pe);
|
||||
free(cmd);
|
||||
return (-1);
|
||||
|
38
proc.c
38
proc.c
@ -17,11 +17,12 @@
|
||||
*/
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/queue.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/uio.h>
|
||||
#include <sys/utsname.h>
|
||||
|
||||
#include <errno.h>
|
||||
#include <event.h>
|
||||
#include <signal.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
@ -47,6 +48,8 @@ struct tmuxproc {
|
||||
struct event ev_sigusr1;
|
||||
struct event ev_sigusr2;
|
||||
struct event ev_sigwinch;
|
||||
|
||||
TAILQ_HEAD(, tmuxpeer) peers;
|
||||
};
|
||||
|
||||
struct tmuxpeer {
|
||||
@ -60,6 +63,8 @@ struct tmuxpeer {
|
||||
|
||||
void (*dispatchcb)(struct imsg *, void *);
|
||||
void *arg;
|
||||
|
||||
TAILQ_ENTRY(tmuxpeer) entry;
|
||||
};
|
||||
|
||||
static int peer_check_version(struct tmuxpeer *, struct imsg *);
|
||||
@ -203,6 +208,7 @@ proc_start(const char *name)
|
||||
|
||||
tp = xcalloc(1, sizeof *tp);
|
||||
tp->name = xstrdup(name);
|
||||
TAILQ_INIT(&tp->peers);
|
||||
|
||||
return (tp);
|
||||
}
|
||||
@ -220,6 +226,10 @@ proc_loop(struct tmuxproc *tp, int (*loopcb)(void))
|
||||
void
|
||||
proc_exit(struct tmuxproc *tp)
|
||||
{
|
||||
struct tmuxpeer *peer;
|
||||
|
||||
TAILQ_FOREACH(peer, &tp->peers, entry)
|
||||
imsg_flush(&peer->ibuf);
|
||||
tp->exit = 1;
|
||||
}
|
||||
|
||||
@ -310,6 +320,7 @@ proc_add_peer(struct tmuxproc *tp, int fd,
|
||||
event_set(&peer->event, fd, EV_READ, proc_event_cb, peer);
|
||||
|
||||
log_debug("add peer %p: %d (%p)", peer, fd, arg);
|
||||
TAILQ_INSERT_TAIL(&tp->peers, peer, entry);
|
||||
|
||||
proc_update_event(peer);
|
||||
return (peer);
|
||||
@ -318,6 +329,7 @@ proc_add_peer(struct tmuxproc *tp, int fd,
|
||||
void
|
||||
proc_remove_peer(struct tmuxpeer *peer)
|
||||
{
|
||||
TAILQ_REMOVE(&peer->parent->peers, peer, entry);
|
||||
log_debug("remove peer %p", peer);
|
||||
|
||||
event_del(&peer->event);
|
||||
@ -338,3 +350,27 @@ proc_toggle_log(struct tmuxproc *tp)
|
||||
{
|
||||
log_toggle(tp->name);
|
||||
}
|
||||
|
||||
pid_t
|
||||
proc_fork_and_daemon(int *fd)
|
||||
{
|
||||
pid_t pid;
|
||||
int pair[2];
|
||||
|
||||
if (socketpair(AF_UNIX, SOCK_STREAM, PF_UNSPEC, pair) != 0)
|
||||
fatal("socketpair failed");
|
||||
switch (pid = fork()) {
|
||||
case -1:
|
||||
fatal("fork failed");
|
||||
case 0:
|
||||
close(pair[0]);
|
||||
*fd = pair[1];
|
||||
if (daemon(1, 0) != 0)
|
||||
fatal("daemon failed");
|
||||
return (0);
|
||||
default:
|
||||
close(pair[1]);
|
||||
*fd = pair[0];
|
||||
return (pid);
|
||||
}
|
||||
}
|
||||
|
243
regress/osc-11colours.sh
Normal file
243
regress/osc-11colours.sh
Normal file
@ -0,0 +1,243 @@
|
||||
#!/bin/sh
|
||||
|
||||
PATH=/bin:/usr/bin
|
||||
TERM=screen
|
||||
|
||||
[ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux)
|
||||
TMUX="$TEST_TMUX -Ltest"
|
||||
$TMUX kill-server 2>/dev/null
|
||||
|
||||
$TMUX -vv new -d
|
||||
$TMUX set -g remain-on-exit on
|
||||
|
||||
do_test() {
|
||||
$TMUX splitw "printf '$1'"
|
||||
c="$($TMUX display -p '#{pane_bg}')"
|
||||
$TMUX kill-pane
|
||||
[ "$c" != "$2" ] && return 1
|
||||
return 0
|
||||
}
|
||||
|
||||
do_test '\033]11;rgb:ff/ff/ff\007' '#ffffff' || exit 1
|
||||
do_test '\033]11;rgb:ff/ff/ff\007\033]111\007' 'default' || exit 1
|
||||
|
||||
do_test '\033]11;cmy:0.9373/0.6941/0.4549\007' '#0f4e8b' || exit 1
|
||||
do_test '\033]11;cmyk:0.88/0.44/0.00/0.45\007' '#104e8c' || exit 1
|
||||
|
||||
do_test '\033]11;16,78,139\007' '#104e8b' || exit 1
|
||||
do_test '\033]11;#104E8B\007' '#104e8b' || exit 1
|
||||
do_test '\033]11;#10004E008B00\007' '#104e8b' || exit 1
|
||||
do_test '\033]11;DodgerBlue4\007' '#104e8b' || exit 1
|
||||
do_test '\033]11;DodgerBlue4 \007' '#104e8b' || exit 1
|
||||
do_test '\033]11; DodgerBlue4\007' '#104e8b' || exit 1
|
||||
do_test '\033]11;rgb:10/4E/8B\007' '#104e8b' || exit 1
|
||||
do_test '\033]11;rgb:1000/4E00/8B00\007' '#104e8b' || exit 1
|
||||
|
||||
do_test '\033]11;grey\007' '#bebebe' || exit 1
|
||||
do_test '\033]11;grey0\007' '#000000' || exit 1
|
||||
do_test '\033]11;grey1\007' '#030303' || exit 1
|
||||
do_test '\033]11;grey2\007' '#050505' || exit 1
|
||||
do_test '\033]11;grey3\007' '#080808' || exit 1
|
||||
do_test '\033]11;grey4\007' '#0a0a0a' || exit 1
|
||||
do_test '\033]11;grey5\007' '#0d0d0d' || exit 1
|
||||
do_test '\033]11;grey6\007' '#0f0f0f' || exit 1
|
||||
do_test '\033]11;grey7\007' '#121212' || exit 1
|
||||
do_test '\033]11;grey8\007' '#141414' || exit 1
|
||||
do_test '\033]11;grey9\007' '#171717' || exit 1
|
||||
do_test '\033]11;grey10\007' '#1a1a1a' || exit 1
|
||||
do_test '\033]11;grey11\007' '#1c1c1c' || exit 1
|
||||
do_test '\033]11;grey12\007' '#1f1f1f' || exit 1
|
||||
do_test '\033]11;grey13\007' '#212121' || exit 1
|
||||
do_test '\033]11;grey14\007' '#242424' || exit 1
|
||||
do_test '\033]11;grey15\007' '#262626' || exit 1
|
||||
do_test '\033]11;grey16\007' '#292929' || exit 1
|
||||
do_test '\033]11;grey17\007' '#2b2b2b' || exit 1
|
||||
do_test '\033]11;grey18\007' '#2e2e2e' || exit 1
|
||||
do_test '\033]11;grey19\007' '#303030' || exit 1
|
||||
do_test '\033]11;grey20\007' '#333333' || exit 1
|
||||
do_test '\033]11;grey21\007' '#363636' || exit 1
|
||||
do_test '\033]11;grey22\007' '#383838' || exit 1
|
||||
do_test '\033]11;grey23\007' '#3b3b3b' || exit 1
|
||||
do_test '\033]11;grey24\007' '#3d3d3d' || exit 1
|
||||
do_test '\033]11;grey25\007' '#404040' || exit 1
|
||||
do_test '\033]11;grey26\007' '#424242' || exit 1
|
||||
do_test '\033]11;grey27\007' '#454545' || exit 1
|
||||
do_test '\033]11;grey28\007' '#474747' || exit 1
|
||||
do_test '\033]11;grey29\007' '#4a4a4a' || exit 1
|
||||
do_test '\033]11;grey30\007' '#4d4d4d' || exit 1
|
||||
do_test '\033]11;grey31\007' '#4f4f4f' || exit 1
|
||||
do_test '\033]11;grey32\007' '#525252' || exit 1
|
||||
do_test '\033]11;grey33\007' '#545454' || exit 1
|
||||
do_test '\033]11;grey34\007' '#575757' || exit 1
|
||||
do_test '\033]11;grey35\007' '#595959' || exit 1
|
||||
do_test '\033]11;grey36\007' '#5c5c5c' || exit 1
|
||||
do_test '\033]11;grey37\007' '#5e5e5e' || exit 1
|
||||
do_test '\033]11;grey38\007' '#616161' || exit 1
|
||||
do_test '\033]11;grey39\007' '#636363' || exit 1
|
||||
do_test '\033]11;grey40\007' '#666666' || exit 1
|
||||
do_test '\033]11;grey41\007' '#696969' || exit 1
|
||||
do_test '\033]11;grey42\007' '#6b6b6b' || exit 1
|
||||
do_test '\033]11;grey43\007' '#6e6e6e' || exit 1
|
||||
do_test '\033]11;grey44\007' '#707070' || exit 1
|
||||
do_test '\033]11;grey45\007' '#737373' || exit 1
|
||||
do_test '\033]11;grey46\007' '#757575' || exit 1
|
||||
do_test '\033]11;grey47\007' '#787878' || exit 1
|
||||
do_test '\033]11;grey48\007' '#7a7a7a' || exit 1
|
||||
do_test '\033]11;grey49\007' '#7d7d7d' || exit 1
|
||||
do_test '\033]11;grey50\007' '#7f7f7f' || exit 1
|
||||
do_test '\033]11;grey51\007' '#828282' || exit 1
|
||||
do_test '\033]11;grey52\007' '#858585' || exit 1
|
||||
do_test '\033]11;grey53\007' '#878787' || exit 1
|
||||
do_test '\033]11;grey54\007' '#8a8a8a' || exit 1
|
||||
do_test '\033]11;grey55\007' '#8c8c8c' || exit 1
|
||||
do_test '\033]11;grey56\007' '#8f8f8f' || exit 1
|
||||
do_test '\033]11;grey57\007' '#919191' || exit 1
|
||||
do_test '\033]11;grey58\007' '#949494' || exit 1
|
||||
do_test '\033]11;grey59\007' '#969696' || exit 1
|
||||
do_test '\033]11;grey60\007' '#999999' || exit 1
|
||||
do_test '\033]11;grey61\007' '#9c9c9c' || exit 1
|
||||
do_test '\033]11;grey62\007' '#9e9e9e' || exit 1
|
||||
do_test '\033]11;grey63\007' '#a1a1a1' || exit 1
|
||||
do_test '\033]11;grey64\007' '#a3a3a3' || exit 1
|
||||
do_test '\033]11;grey65\007' '#a6a6a6' || exit 1
|
||||
do_test '\033]11;grey66\007' '#a8a8a8' || exit 1
|
||||
do_test '\033]11;grey67\007' '#ababab' || exit 1
|
||||
do_test '\033]11;grey68\007' '#adadad' || exit 1
|
||||
do_test '\033]11;grey69\007' '#b0b0b0' || exit 1
|
||||
do_test '\033]11;grey70\007' '#b3b3b3' || exit 1
|
||||
do_test '\033]11;grey71\007' '#b5b5b5' || exit 1
|
||||
do_test '\033]11;grey72\007' '#b8b8b8' || exit 1
|
||||
do_test '\033]11;grey73\007' '#bababa' || exit 1
|
||||
do_test '\033]11;grey74\007' '#bdbdbd' || exit 1
|
||||
do_test '\033]11;grey75\007' '#bfbfbf' || exit 1
|
||||
do_test '\033]11;grey76\007' '#c2c2c2' || exit 1
|
||||
do_test '\033]11;grey77\007' '#c4c4c4' || exit 1
|
||||
do_test '\033]11;grey78\007' '#c7c7c7' || exit 1
|
||||
do_test '\033]11;grey79\007' '#c9c9c9' || exit 1
|
||||
do_test '\033]11;grey80\007' '#cccccc' || exit 1
|
||||
do_test '\033]11;grey81\007' '#cfcfcf' || exit 1
|
||||
do_test '\033]11;grey82\007' '#d1d1d1' || exit 1
|
||||
do_test '\033]11;grey83\007' '#d4d4d4' || exit 1
|
||||
do_test '\033]11;grey84\007' '#d6d6d6' || exit 1
|
||||
do_test '\033]11;grey85\007' '#d9d9d9' || exit 1
|
||||
do_test '\033]11;grey86\007' '#dbdbdb' || exit 1
|
||||
do_test '\033]11;grey87\007' '#dedede' || exit 1
|
||||
do_test '\033]11;grey88\007' '#e0e0e0' || exit 1
|
||||
do_test '\033]11;grey89\007' '#e3e3e3' || exit 1
|
||||
do_test '\033]11;grey90\007' '#e5e5e5' || exit 1
|
||||
do_test '\033]11;grey91\007' '#e8e8e8' || exit 1
|
||||
do_test '\033]11;grey92\007' '#ebebeb' || exit 1
|
||||
do_test '\033]11;grey93\007' '#ededed' || exit 1
|
||||
do_test '\033]11;grey94\007' '#f0f0f0' || exit 1
|
||||
do_test '\033]11;grey95\007' '#f2f2f2' || exit 1
|
||||
do_test '\033]11;grey96\007' '#f5f5f5' || exit 1
|
||||
do_test '\033]11;grey97\007' '#f7f7f7' || exit 1
|
||||
do_test '\033]11;grey98\007' '#fafafa' || exit 1
|
||||
do_test '\033]11;grey99\007' '#fcfcfc' || exit 1
|
||||
do_test '\033]11;grey100\007' '#ffffff' || exit 1
|
||||
|
||||
do_test '\033]11;gray\007' '#bebebe' || exit 1
|
||||
do_test '\033]11;gray0\007' '#000000' || exit 1
|
||||
do_test '\033]11;gray1\007' '#030303' || exit 1
|
||||
do_test '\033]11;gray2\007' '#050505' || exit 1
|
||||
do_test '\033]11;gray3\007' '#080808' || exit 1
|
||||
do_test '\033]11;gray4\007' '#0a0a0a' || exit 1
|
||||
do_test '\033]11;gray5\007' '#0d0d0d' || exit 1
|
||||
do_test '\033]11;gray6\007' '#0f0f0f' || exit 1
|
||||
do_test '\033]11;gray7\007' '#121212' || exit 1
|
||||
do_test '\033]11;gray8\007' '#141414' || exit 1
|
||||
do_test '\033]11;gray9\007' '#171717' || exit 1
|
||||
do_test '\033]11;gray10\007' '#1a1a1a' || exit 1
|
||||
do_test '\033]11;gray11\007' '#1c1c1c' || exit 1
|
||||
do_test '\033]11;gray12\007' '#1f1f1f' || exit 1
|
||||
do_test '\033]11;gray13\007' '#212121' || exit 1
|
||||
do_test '\033]11;gray14\007' '#242424' || exit 1
|
||||
do_test '\033]11;gray15\007' '#262626' || exit 1
|
||||
do_test '\033]11;gray16\007' '#292929' || exit 1
|
||||
do_test '\033]11;gray17\007' '#2b2b2b' || exit 1
|
||||
do_test '\033]11;gray18\007' '#2e2e2e' || exit 1
|
||||
do_test '\033]11;gray19\007' '#303030' || exit 1
|
||||
do_test '\033]11;gray20\007' '#333333' || exit 1
|
||||
do_test '\033]11;gray21\007' '#363636' || exit 1
|
||||
do_test '\033]11;gray22\007' '#383838' || exit 1
|
||||
do_test '\033]11;gray23\007' '#3b3b3b' || exit 1
|
||||
do_test '\033]11;gray24\007' '#3d3d3d' || exit 1
|
||||
do_test '\033]11;gray25\007' '#404040' || exit 1
|
||||
do_test '\033]11;gray26\007' '#424242' || exit 1
|
||||
do_test '\033]11;gray27\007' '#454545' || exit 1
|
||||
do_test '\033]11;gray28\007' '#474747' || exit 1
|
||||
do_test '\033]11;gray29\007' '#4a4a4a' || exit 1
|
||||
do_test '\033]11;gray30\007' '#4d4d4d' || exit 1
|
||||
do_test '\033]11;gray31\007' '#4f4f4f' || exit 1
|
||||
do_test '\033]11;gray32\007' '#525252' || exit 1
|
||||
do_test '\033]11;gray33\007' '#545454' || exit 1
|
||||
do_test '\033]11;gray34\007' '#575757' || exit 1
|
||||
do_test '\033]11;gray35\007' '#595959' || exit 1
|
||||
do_test '\033]11;gray36\007' '#5c5c5c' || exit 1
|
||||
do_test '\033]11;gray37\007' '#5e5e5e' || exit 1
|
||||
do_test '\033]11;gray38\007' '#616161' || exit 1
|
||||
do_test '\033]11;gray39\007' '#636363' || exit 1
|
||||
do_test '\033]11;gray40\007' '#666666' || exit 1
|
||||
do_test '\033]11;gray41\007' '#696969' || exit 1
|
||||
do_test '\033]11;gray42\007' '#6b6b6b' || exit 1
|
||||
do_test '\033]11;gray43\007' '#6e6e6e' || exit 1
|
||||
do_test '\033]11;gray44\007' '#707070' || exit 1
|
||||
do_test '\033]11;gray45\007' '#737373' || exit 1
|
||||
do_test '\033]11;gray46\007' '#757575' || exit 1
|
||||
do_test '\033]11;gray47\007' '#787878' || exit 1
|
||||
do_test '\033]11;gray48\007' '#7a7a7a' || exit 1
|
||||
do_test '\033]11;gray49\007' '#7d7d7d' || exit 1
|
||||
do_test '\033]11;gray50\007' '#7f7f7f' || exit 1
|
||||
do_test '\033]11;gray51\007' '#828282' || exit 1
|
||||
do_test '\033]11;gray52\007' '#858585' || exit 1
|
||||
do_test '\033]11;gray53\007' '#878787' || exit 1
|
||||
do_test '\033]11;gray54\007' '#8a8a8a' || exit 1
|
||||
do_test '\033]11;gray55\007' '#8c8c8c' || exit 1
|
||||
do_test '\033]11;gray56\007' '#8f8f8f' || exit 1
|
||||
do_test '\033]11;gray57\007' '#919191' || exit 1
|
||||
do_test '\033]11;gray58\007' '#949494' || exit 1
|
||||
do_test '\033]11;gray59\007' '#969696' || exit 1
|
||||
do_test '\033]11;gray60\007' '#999999' || exit 1
|
||||
do_test '\033]11;gray61\007' '#9c9c9c' || exit 1
|
||||
do_test '\033]11;gray62\007' '#9e9e9e' || exit 1
|
||||
do_test '\033]11;gray63\007' '#a1a1a1' || exit 1
|
||||
do_test '\033]11;gray64\007' '#a3a3a3' || exit 1
|
||||
do_test '\033]11;gray65\007' '#a6a6a6' || exit 1
|
||||
do_test '\033]11;gray66\007' '#a8a8a8' || exit 1
|
||||
do_test '\033]11;gray67\007' '#ababab' || exit 1
|
||||
do_test '\033]11;gray68\007' '#adadad' || exit 1
|
||||
do_test '\033]11;gray69\007' '#b0b0b0' || exit 1
|
||||
do_test '\033]11;gray70\007' '#b3b3b3' || exit 1
|
||||
do_test '\033]11;gray71\007' '#b5b5b5' || exit 1
|
||||
do_test '\033]11;gray72\007' '#b8b8b8' || exit 1
|
||||
do_test '\033]11;gray73\007' '#bababa' || exit 1
|
||||
do_test '\033]11;gray74\007' '#bdbdbd' || exit 1
|
||||
do_test '\033]11;gray75\007' '#bfbfbf' || exit 1
|
||||
do_test '\033]11;gray76\007' '#c2c2c2' || exit 1
|
||||
do_test '\033]11;gray77\007' '#c4c4c4' || exit 1
|
||||
do_test '\033]11;gray78\007' '#c7c7c7' || exit 1
|
||||
do_test '\033]11;gray79\007' '#c9c9c9' || exit 1
|
||||
do_test '\033]11;gray80\007' '#cccccc' || exit 1
|
||||
do_test '\033]11;gray81\007' '#cfcfcf' || exit 1
|
||||
do_test '\033]11;gray82\007' '#d1d1d1' || exit 1
|
||||
do_test '\033]11;gray83\007' '#d4d4d4' || exit 1
|
||||
do_test '\033]11;gray84\007' '#d6d6d6' || exit 1
|
||||
do_test '\033]11;gray85\007' '#d9d9d9' || exit 1
|
||||
do_test '\033]11;gray86\007' '#dbdbdb' || exit 1
|
||||
do_test '\033]11;gray87\007' '#dedede' || exit 1
|
||||
do_test '\033]11;gray88\007' '#e0e0e0' || exit 1
|
||||
do_test '\033]11;gray89\007' '#e3e3e3' || exit 1
|
||||
do_test '\033]11;gray90\007' '#e5e5e5' || exit 1
|
||||
do_test '\033]11;gray91\007' '#e8e8e8' || exit 1
|
||||
do_test '\033]11;gray92\007' '#ebebeb' || exit 1
|
||||
do_test '\033]11;gray93\007' '#ededed' || exit 1
|
||||
do_test '\033]11;gray94\007' '#f0f0f0' || exit 1
|
||||
do_test '\033]11;gray95\007' '#f2f2f2' || exit 1
|
||||
do_test '\033]11;gray96\007' '#f5f5f5' || exit 1
|
||||
do_test '\033]11;gray97\007' '#f7f7f7' || exit 1
|
||||
do_test '\033]11;gray98\007' '#fafafa' || exit 1
|
||||
do_test '\033]11;gray99\007' '#fcfcfc' || exit 1
|
||||
do_test '\033]11;gray100\007' '#ffffff' || exit 1
|
||||
|
||||
$TMUX -f/dev/null kill-server 2>/dev/null
|
||||
exit 0
|
@ -32,8 +32,8 @@ static void screen_redraw_set_context(struct client *,
|
||||
struct screen_redraw_ctx *);
|
||||
|
||||
#define CELL_INSIDE 0
|
||||
#define CELL_LEFTRIGHT 1
|
||||
#define CELL_TOPBOTTOM 2
|
||||
#define CELL_TOPBOTTOM 1
|
||||
#define CELL_LEFTRIGHT 2
|
||||
#define CELL_TOPLEFT 3
|
||||
#define CELL_TOPRIGHT 4
|
||||
#define CELL_BOTTOMLEFT 5
|
||||
@ -47,6 +47,9 @@ static void screen_redraw_set_context(struct client *,
|
||||
|
||||
#define CELL_BORDERS " xqlkmjwvtun~"
|
||||
|
||||
#define START_ISOLATE "\342\201\246"
|
||||
#define END_ISOLATE "\342\201\251"
|
||||
|
||||
static const struct utf8_data screen_redraw_double_borders[] = {
|
||||
{ "", 0, 0, 0 },
|
||||
{ "\342\225\221", 0, 3, 1 }, /* U+2551 */
|
||||
@ -299,7 +302,7 @@ screen_redraw_type_of_cell(struct client *c, u_int px, u_int py,
|
||||
case 13: /* 1101, left right bottom */
|
||||
return (CELL_TOPJOIN);
|
||||
case 12: /* 1100, left right */
|
||||
return (CELL_TOPBOTTOM);
|
||||
return (CELL_LEFTRIGHT);
|
||||
case 11: /* 1011, left top bottom */
|
||||
return (CELL_RIGHTJOIN);
|
||||
case 10: /* 1010, left top */
|
||||
@ -313,7 +316,7 @@ screen_redraw_type_of_cell(struct client *c, u_int px, u_int py,
|
||||
case 5: /* 0101, right bottom */
|
||||
return (CELL_TOPLEFT);
|
||||
case 3: /* 0011, top bottom */
|
||||
return (CELL_LEFTRIGHT);
|
||||
return (CELL_TOPBOTTOM);
|
||||
}
|
||||
return (CELL_OUTSIDE);
|
||||
}
|
||||
@ -407,7 +410,7 @@ screen_redraw_make_pane_status(struct client *c, struct window_pane *wp,
|
||||
struct format_tree *ft;
|
||||
char *expanded;
|
||||
int pane_status = rctx->pane_status;
|
||||
u_int width, i, cell_type, top, px, py;
|
||||
u_int width, i, cell_type, px, py;
|
||||
struct screen_write_ctx ctx;
|
||||
struct screen old;
|
||||
|
||||
@ -432,16 +435,12 @@ screen_redraw_make_pane_status(struct client *c, struct window_pane *wp,
|
||||
|
||||
screen_write_start(&ctx, &wp->status_screen);
|
||||
|
||||
if (rctx->statustop)
|
||||
top = rctx->statuslines;
|
||||
else
|
||||
top = 0;
|
||||
for (i = 0; i < width; i++) {
|
||||
px = wp->xoff + 2 + i;
|
||||
if (rctx->pane_status == PANE_STATUS_TOP)
|
||||
py = top + wp->yoff - 1;
|
||||
py = wp->yoff - 1;
|
||||
else
|
||||
py = top + wp->yoff + wp->sy;
|
||||
py = wp->yoff + wp->sy;
|
||||
cell_type = screen_redraw_type_of_cell(c, px, py, pane_status);
|
||||
screen_redraw_border_set(wp, pane_lines, cell_type, &gc);
|
||||
screen_write_cell(&ctx, &gc);
|
||||
@ -684,7 +683,7 @@ screen_redraw_draw_borders_cell(struct screen_redraw_ctx *ctx, u_int i, u_int j)
|
||||
struct tty *tty = &c->tty;
|
||||
struct window_pane *wp;
|
||||
u_int cell_type, x = ctx->ox + i, y = ctx->oy + j;
|
||||
int pane_status = ctx->pane_status;
|
||||
int pane_status = ctx->pane_status, isolates;
|
||||
struct grid_cell gc;
|
||||
const struct grid_cell *tmp;
|
||||
|
||||
@ -709,11 +708,22 @@ screen_redraw_draw_borders_cell(struct screen_redraw_ctx *ctx, u_int i, u_int j)
|
||||
}
|
||||
screen_redraw_border_set(wp, ctx->pane_lines, cell_type, &gc);
|
||||
|
||||
if (cell_type == CELL_TOPBOTTOM &&
|
||||
(c->flags & CLIENT_UTF8) &&
|
||||
tty_term_has(tty->term, TTYC_BIDI))
|
||||
isolates = 1;
|
||||
else
|
||||
isolates = 0;
|
||||
|
||||
if (ctx->statustop)
|
||||
tty_cursor(tty, i, ctx->statuslines + j);
|
||||
else
|
||||
tty_cursor(tty, i, j);
|
||||
if (isolates)
|
||||
tty_puts(tty, END_ISOLATE);
|
||||
tty_cell(tty, &gc, &grid_default_cell, NULL);
|
||||
if (isolates)
|
||||
tty_puts(tty, START_ISOLATE);
|
||||
}
|
||||
|
||||
/* Draw the borders. */
|
||||
|
377
screen-write.c
377
screen-write.c
@ -23,13 +23,11 @@
|
||||
|
||||
#include "tmux.h"
|
||||
|
||||
static struct screen_write_citem *screen_write_collect_trim(
|
||||
struct screen_write_ctx *, u_int, u_int, u_int, int *);
|
||||
static void screen_write_collect_clear(struct screen_write_ctx *, u_int,
|
||||
u_int);
|
||||
static void screen_write_collect_clear_end(struct screen_write_ctx *, u_int,
|
||||
u_int);
|
||||
static void screen_write_collect_clear_start(struct screen_write_ctx *,
|
||||
u_int, u_int);
|
||||
static void screen_write_collect_scroll(struct screen_write_ctx *);
|
||||
static void screen_write_collect_scroll(struct screen_write_ctx *, u_int);
|
||||
static void screen_write_collect_flush(struct screen_write_ctx *, int,
|
||||
const char *);
|
||||
|
||||
@ -38,23 +36,44 @@ static int screen_write_overwrite(struct screen_write_ctx *,
|
||||
static const struct grid_cell *screen_write_combine(struct screen_write_ctx *,
|
||||
const struct utf8_data *, u_int *);
|
||||
|
||||
struct screen_write_collect_item {
|
||||
u_int x;
|
||||
int wrapped;
|
||||
struct screen_write_citem {
|
||||
u_int x;
|
||||
int wrapped;
|
||||
|
||||
enum { TEXT, CLEAR_END, CLEAR_START } type;
|
||||
u_int used;
|
||||
u_int bg;
|
||||
enum { TEXT, CLEAR } type;
|
||||
u_int used;
|
||||
u_int bg;
|
||||
|
||||
struct grid_cell gc;
|
||||
struct grid_cell gc;
|
||||
|
||||
TAILQ_ENTRY(screen_write_collect_item) entry;
|
||||
TAILQ_ENTRY(screen_write_citem) entry;
|
||||
};
|
||||
struct screen_write_collect_line {
|
||||
u_int bg;
|
||||
char *data;
|
||||
TAILQ_HEAD(, screen_write_collect_item) items;
|
||||
struct screen_write_cline {
|
||||
char *data;
|
||||
TAILQ_HEAD(, screen_write_citem) items;
|
||||
};
|
||||
TAILQ_HEAD(, screen_write_citem) screen_write_citem_freelist =
|
||||
TAILQ_HEAD_INITIALIZER(screen_write_citem_freelist);
|
||||
|
||||
static struct screen_write_citem *
|
||||
screen_write_get_citem(void)
|
||||
{
|
||||
struct screen_write_citem *ci;
|
||||
|
||||
ci = TAILQ_FIRST(&screen_write_citem_freelist);
|
||||
if (ci != NULL) {
|
||||
TAILQ_REMOVE(&screen_write_citem_freelist, ci, entry);
|
||||
memset(ci, 0, sizeof *ci);
|
||||
return (ci);
|
||||
}
|
||||
return (xcalloc(1, sizeof *ci));
|
||||
}
|
||||
|
||||
static void
|
||||
screen_write_free_citem(struct screen_write_citem *ci)
|
||||
{
|
||||
TAILQ_INSERT_TAIL(&screen_write_citem_freelist, ci, entry);
|
||||
}
|
||||
|
||||
static void
|
||||
screen_write_offset_timer(__unused int fd, __unused short events, void *data)
|
||||
@ -125,7 +144,8 @@ screen_write_set_client_cb(struct tty_ctx *ttyctx, struct client *c)
|
||||
* Redraw is already deferred to redraw another pane - redraw
|
||||
* this one also when that happens.
|
||||
*/
|
||||
log_debug("adding %%%u to deferred redraw", wp->id);
|
||||
log_debug("%s: adding %%%u to deferred redraw", __func__,
|
||||
wp->id);
|
||||
wp->flags |= PANE_REDRAW;
|
||||
return (-1);
|
||||
}
|
||||
@ -220,7 +240,7 @@ screen_write_init(struct screen_write_ctx *ctx, struct screen *s)
|
||||
|
||||
if (ctx->s->write_list == NULL)
|
||||
screen_write_make_list(ctx->s);
|
||||
ctx->item = xcalloc(1, sizeof *ctx->item);
|
||||
ctx->item = screen_write_get_citem();
|
||||
|
||||
ctx->scrolled = 0;
|
||||
ctx->bg = 8;
|
||||
@ -259,7 +279,6 @@ screen_write_start_callback(struct screen_write_ctx *ctx, struct screen *s,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* Initialize writing. */
|
||||
void
|
||||
screen_write_start(struct screen_write_ctx *ctx, struct screen *s)
|
||||
@ -279,14 +298,7 @@ screen_write_stop(struct screen_write_ctx *ctx)
|
||||
screen_write_collect_end(ctx);
|
||||
screen_write_collect_flush(ctx, 0, __func__);
|
||||
|
||||
log_debug("%s: %u cells (%u written, %u skipped)", __func__,
|
||||
ctx->cells, ctx->written, ctx->skipped);
|
||||
if (ctx->wp != NULL) {
|
||||
ctx->wp->written += ctx->written;
|
||||
ctx->wp->skipped += ctx->skipped;
|
||||
}
|
||||
|
||||
free(ctx->item);
|
||||
screen_write_free_citem(ctx->item);
|
||||
}
|
||||
|
||||
/* Reset screen state. */
|
||||
@ -1095,9 +1107,10 @@ screen_write_deleteline(struct screen_write_ctx *ctx, u_int ny, u_int bg)
|
||||
void
|
||||
screen_write_clearline(struct screen_write_ctx *ctx, u_int bg)
|
||||
{
|
||||
struct screen *s = ctx->s;
|
||||
struct grid_line *gl;
|
||||
u_int sx = screen_size_x(s);
|
||||
struct screen *s = ctx->s;
|
||||
struct grid_line *gl;
|
||||
u_int sx = screen_size_x(s);
|
||||
struct screen_write_citem *ci = ctx->item;
|
||||
|
||||
gl = grid_get_line(s->grid, s->grid->hsize + s->cy);
|
||||
if (gl->cellsize == 0 && COLOUR_DEFAULT(bg))
|
||||
@ -1106,18 +1119,22 @@ screen_write_clearline(struct screen_write_ctx *ctx, u_int bg)
|
||||
grid_view_clear(s->grid, 0, s->cy, sx, 1, bg);
|
||||
|
||||
screen_write_collect_clear(ctx, s->cy, 1);
|
||||
ctx->s->write_list[s->cy].bg = 1 + bg;
|
||||
ctx->item->used = 0;
|
||||
ci->x = 0;
|
||||
ci->used = sx;
|
||||
ci->type = CLEAR;
|
||||
ci->bg = bg;
|
||||
TAILQ_INSERT_TAIL(&ctx->s->write_list[s->cy].items, ci, entry);
|
||||
ctx->item = screen_write_get_citem();
|
||||
}
|
||||
|
||||
/* Clear to end of line from cursor. */
|
||||
void
|
||||
screen_write_clearendofline(struct screen_write_ctx *ctx, u_int bg)
|
||||
{
|
||||
struct screen *s = ctx->s;
|
||||
struct grid_line *gl;
|
||||
u_int sx = screen_size_x(s);
|
||||
struct screen_write_collect_item *ci = ctx->item;
|
||||
struct screen *s = ctx->s;
|
||||
struct grid_line *gl;
|
||||
u_int sx = screen_size_x(s);
|
||||
struct screen_write_citem *ci = ctx->item, *before;
|
||||
|
||||
if (s->cx == 0) {
|
||||
screen_write_clearline(ctx, bg);
|
||||
@ -1130,12 +1147,16 @@ screen_write_clearendofline(struct screen_write_ctx *ctx, u_int bg)
|
||||
|
||||
grid_view_clear(s->grid, s->cx, s->cy, sx - s->cx, 1, bg);
|
||||
|
||||
screen_write_collect_clear_end(ctx, s->cy, s->cx);
|
||||
before = screen_write_collect_trim(ctx, s->cy, s->cx, sx - s->cx, NULL);
|
||||
ci->x = s->cx;
|
||||
ci->type = CLEAR_END;
|
||||
ci->used = sx - s->cx;
|
||||
ci->type = CLEAR;
|
||||
ci->bg = bg;
|
||||
TAILQ_INSERT_TAIL(&ctx->s->write_list[s->cy].items, ci, entry);
|
||||
ctx->item = xcalloc(1, sizeof *ctx->item);
|
||||
if (before == NULL)
|
||||
TAILQ_INSERT_TAIL(&ctx->s->write_list[s->cy].items, ci, entry);
|
||||
else
|
||||
TAILQ_INSERT_BEFORE(before, ci, entry);
|
||||
ctx->item = screen_write_get_citem();
|
||||
}
|
||||
|
||||
/* Clear to start of line from cursor. */
|
||||
@ -1143,8 +1164,8 @@ void
|
||||
screen_write_clearstartofline(struct screen_write_ctx *ctx, u_int bg)
|
||||
{
|
||||
struct screen *s = ctx->s;
|
||||
u_int sx = screen_size_x(s);
|
||||
struct screen_write_collect_item *ci = ctx->item;
|
||||
u_int sx = screen_size_x(s);
|
||||
struct screen_write_citem *ci = ctx->item, *before;
|
||||
|
||||
if (s->cx >= sx - 1) {
|
||||
screen_write_clearline(ctx, bg);
|
||||
@ -1156,12 +1177,16 @@ screen_write_clearstartofline(struct screen_write_ctx *ctx, u_int bg)
|
||||
else
|
||||
grid_view_clear(s->grid, 0, s->cy, s->cx + 1, 1, bg);
|
||||
|
||||
screen_write_collect_clear_start(ctx, s->cy, s->cx);
|
||||
ci->x = s->cx;
|
||||
ci->type = CLEAR_START;
|
||||
before = screen_write_collect_trim(ctx, s->cy, 0, s->cx + 1, NULL);
|
||||
ci->x = 0;
|
||||
ci->used = s->cx + 1;
|
||||
ci->type = CLEAR;
|
||||
ci->bg = bg;
|
||||
TAILQ_INSERT_TAIL(&ctx->s->write_list[s->cy].items, ci, entry);
|
||||
ctx->item = xcalloc(1, sizeof *ctx->item);
|
||||
if (before == NULL)
|
||||
TAILQ_INSERT_TAIL(&ctx->s->write_list[s->cy].items, ci, entry);
|
||||
else
|
||||
TAILQ_INSERT_BEFORE(before, ci, entry);
|
||||
ctx->item = screen_write_get_citem();
|
||||
}
|
||||
|
||||
/* Move cursor to px,py. */
|
||||
@ -1183,6 +1208,7 @@ screen_write_cursormove(struct screen_write_ctx *ctx, int px, int py,
|
||||
if (py != -1 && (u_int)py > screen_size_y(s) - 1)
|
||||
py = screen_size_y(s) - 1;
|
||||
|
||||
log_debug("%s: from %u,%u to %u,%u", __func__, s->cx, s->cy, px, py);
|
||||
screen_write_set_cursor(ctx, px, py);
|
||||
}
|
||||
|
||||
@ -1240,8 +1266,6 @@ screen_write_linefeed(struct screen_write_ctx *ctx, int wrapped, u_int bg)
|
||||
gl = grid_get_line(gd, gd->hsize + s->cy);
|
||||
if (wrapped)
|
||||
gl->flags |= GRID_LINE_WRAPPED;
|
||||
else
|
||||
gl->flags &= ~GRID_LINE_WRAPPED;
|
||||
|
||||
log_debug("%s: at %u,%u (region %u-%u)", __func__, s->cx, s->cy,
|
||||
s->rupper, s->rlower);
|
||||
@ -1253,7 +1277,7 @@ screen_write_linefeed(struct screen_write_ctx *ctx, int wrapped, u_int bg)
|
||||
|
||||
if (s->cy == s->rlower) {
|
||||
grid_view_scroll_region_up(gd, s->rupper, s->rlower, bg);
|
||||
screen_write_collect_scroll(ctx);
|
||||
screen_write_collect_scroll(ctx, bg);
|
||||
ctx->scrolled++;
|
||||
} else if (s->cy < screen_size_y(s) - 1)
|
||||
screen_write_set_cursor(ctx, -1, s->cy + 1);
|
||||
@ -1279,7 +1303,7 @@ screen_write_scrollup(struct screen_write_ctx *ctx, u_int lines, u_int bg)
|
||||
|
||||
for (i = 0; i < lines; i++) {
|
||||
grid_view_scroll_region_up(gd, s->rupper, s->rlower, bg);
|
||||
screen_write_collect_scroll(ctx);
|
||||
screen_write_collect_scroll(ctx, bg);
|
||||
}
|
||||
ctx->scrolled += lines;
|
||||
}
|
||||
@ -1393,107 +1417,114 @@ screen_write_clearhistory(struct screen_write_ctx *ctx)
|
||||
grid_clear_history(ctx->s->grid);
|
||||
}
|
||||
|
||||
/* Clear to start of a collected line. */
|
||||
static void
|
||||
screen_write_collect_clear_start(struct screen_write_ctx *ctx, u_int y, u_int x)
|
||||
/* Trim collected items. */
|
||||
static struct screen_write_citem *
|
||||
screen_write_collect_trim(struct screen_write_ctx *ctx, u_int y, u_int x,
|
||||
u_int used, int *wrapped)
|
||||
{
|
||||
struct screen_write_collect_item *ci, *tmp;
|
||||
size_t size = 0;
|
||||
u_int items = 0;
|
||||
struct screen_write_cline *cl = &ctx->s->write_list[y];
|
||||
struct screen_write_citem *ci, *ci2, *tmp, *before = NULL;
|
||||
u_int sx = x, ex = x + used - 1;
|
||||
u_int csx, cex;
|
||||
|
||||
if (TAILQ_EMPTY(&ctx->s->write_list[y].items))
|
||||
return;
|
||||
TAILQ_FOREACH_SAFE(ci, &ctx->s->write_list[y].items, entry, tmp) {
|
||||
switch (ci->type) {
|
||||
case CLEAR_START:
|
||||
break;
|
||||
case CLEAR_END:
|
||||
if (ci->x <= x)
|
||||
ci->x = x;
|
||||
if (TAILQ_EMPTY(&cl->items))
|
||||
return (NULL);
|
||||
TAILQ_FOREACH_SAFE(ci, &cl->items, entry, tmp) {
|
||||
csx = ci->x;
|
||||
cex = ci->x + ci->used - 1;
|
||||
|
||||
/* Item is entirely before. */
|
||||
if (cex < sx) {
|
||||
log_debug("%s: %p %u-%u before %u-%u", __func__, ci,
|
||||
csx, cex, sx, ex);
|
||||
continue;
|
||||
case TEXT:
|
||||
if (ci->x > x)
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Item is entirely after. */
|
||||
if (csx > ex) {
|
||||
log_debug("%s: %p %u-%u after %u-%u", __func__, ci,
|
||||
csx, cex, sx, ex);
|
||||
before = ci;
|
||||
break;
|
||||
}
|
||||
items++;
|
||||
size += ci->used;
|
||||
TAILQ_REMOVE(&ctx->s->write_list[y].items, ci, entry);
|
||||
free(ci);
|
||||
}
|
||||
ctx->skipped += size;
|
||||
log_debug("%s: dropped %u items (%zu bytes) (line %u)", __func__, items,
|
||||
size, y);
|
||||
}
|
||||
|
||||
/* Clear to end of a collected line. */
|
||||
static void
|
||||
screen_write_collect_clear_end(struct screen_write_ctx *ctx, u_int y, u_int x)
|
||||
{
|
||||
struct screen_write_collect_item *ci, *tmp;
|
||||
size_t size = 0;
|
||||
u_int items = 0;
|
||||
|
||||
if (TAILQ_EMPTY(&ctx->s->write_list[y].items))
|
||||
return;
|
||||
TAILQ_FOREACH_SAFE(ci, &ctx->s->write_list[y].items, entry, tmp) {
|
||||
switch (ci->type) {
|
||||
case CLEAR_START:
|
||||
if (ci->x >= x)
|
||||
ci->x = x;
|
||||
/* Item is entirely inside. */
|
||||
if (csx >= sx && cex <= ex) {
|
||||
log_debug("%s: %p %u-%u inside %u-%u", __func__, ci,
|
||||
csx, cex, sx, ex);
|
||||
TAILQ_REMOVE(&cl->items, ci, entry);
|
||||
screen_write_free_citem(ci);
|
||||
if (csx == 0 && ci->wrapped && wrapped != NULL)
|
||||
*wrapped = 1;
|
||||
continue;
|
||||
case CLEAR_END:
|
||||
break;
|
||||
case TEXT:
|
||||
if (ci->x < x)
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Item under the start. */
|
||||
if (csx < sx && cex >= sx && cex <= ex) {
|
||||
log_debug("%s: %p %u-%u start %u-%u", __func__, ci,
|
||||
csx, cex, sx, ex);
|
||||
ci->used = sx - csx;
|
||||
log_debug("%s: %p now %u-%u", __func__, ci, ci->x,
|
||||
ci->x + ci->used + 1);
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Item covers the end. */
|
||||
if (cex > ex && csx >= sx && csx <= ex) {
|
||||
log_debug("%s: %p %u-%u end %u-%u", __func__, ci,
|
||||
csx, cex, sx, ex);
|
||||
ci->x = ex + 1;
|
||||
ci->used = cex - ex;
|
||||
log_debug("%s: %p now %u-%u", __func__, ci, ci->x,
|
||||
ci->x + ci->used + 1);
|
||||
before = ci;
|
||||
break;
|
||||
}
|
||||
items++;
|
||||
size += ci->used;
|
||||
TAILQ_REMOVE(&ctx->s->write_list[y].items, ci, entry);
|
||||
free(ci);
|
||||
|
||||
/* Item must cover both sides. */
|
||||
log_debug("%s: %p %u-%u under %u-%u", __func__, ci,
|
||||
csx, cex, sx, ex);
|
||||
ci2 = screen_write_get_citem();
|
||||
ci2->type = ci->type;
|
||||
ci2->bg = ci->bg;
|
||||
memcpy(&ci2->gc, &ci->gc, sizeof ci2->gc);
|
||||
TAILQ_INSERT_AFTER(&cl->items, ci, ci2, entry);
|
||||
|
||||
ci->used = sx - csx;
|
||||
ci2->x = ex + 1;
|
||||
ci2->used = cex - ex;
|
||||
|
||||
log_debug("%s: %p now %u-%u (%p) and %u-%u (%p)", __func__, ci,
|
||||
ci->x, ci->x + ci->used - 1, ci, ci2->x,
|
||||
ci2->x + ci2->used - 1, ci2);
|
||||
before = ci2;
|
||||
break;
|
||||
}
|
||||
ctx->skipped += size;
|
||||
log_debug("%s: dropped %u items (%zu bytes) (line %u)", __func__, items,
|
||||
size, y);
|
||||
return (before);
|
||||
}
|
||||
|
||||
/* Clear collected lines. */
|
||||
static void
|
||||
screen_write_collect_clear(struct screen_write_ctx *ctx, u_int y, u_int n)
|
||||
{
|
||||
struct screen_write_collect_item *ci, *tmp;
|
||||
struct screen_write_collect_line *cl;
|
||||
u_int i, items;
|
||||
size_t size;
|
||||
struct screen_write_cline *cl;
|
||||
u_int i;
|
||||
|
||||
for (i = y; i < y + n; i++) {
|
||||
if (TAILQ_EMPTY(&ctx->s->write_list[i].items))
|
||||
continue;
|
||||
items = 0;
|
||||
size = 0;
|
||||
cl = &ctx->s->write_list[i];
|
||||
TAILQ_FOREACH_SAFE(ci, &cl->items, entry, tmp) {
|
||||
items++;
|
||||
size += ci->used;
|
||||
TAILQ_REMOVE(&cl->items, ci, entry);
|
||||
free(ci);
|
||||
}
|
||||
ctx->skipped += size;
|
||||
log_debug("%s: dropped %u items (%zu bytes) (line %u)",
|
||||
__func__, items, size, y);
|
||||
TAILQ_CONCAT(&screen_write_citem_freelist, &cl->items, entry);
|
||||
}
|
||||
}
|
||||
|
||||
/* Scroll collected lines up. */
|
||||
static void
|
||||
screen_write_collect_scroll(struct screen_write_ctx *ctx)
|
||||
screen_write_collect_scroll(struct screen_write_ctx *ctx, u_int bg)
|
||||
{
|
||||
struct screen *s = ctx->s;
|
||||
struct screen_write_collect_line *cl;
|
||||
u_int y;
|
||||
char *saved;
|
||||
struct screen *s = ctx->s;
|
||||
struct screen_write_cline *cl;
|
||||
u_int y;
|
||||
char *saved;
|
||||
struct screen_write_citem *ci;
|
||||
|
||||
log_debug("%s: at %u,%u (region %u-%u)", __func__, s->cx, s->cy,
|
||||
s->rupper, s->rlower);
|
||||
@ -1503,11 +1534,16 @@ screen_write_collect_scroll(struct screen_write_ctx *ctx)
|
||||
for (y = s->rupper; y < s->rlower; y++) {
|
||||
cl = &ctx->s->write_list[y + 1];
|
||||
TAILQ_CONCAT(&ctx->s->write_list[y].items, &cl->items, entry);
|
||||
ctx->s->write_list[y].bg = cl->bg;
|
||||
ctx->s->write_list[y].data = cl->data;
|
||||
}
|
||||
ctx->s->write_list[s->rlower].bg = 1 + 8;
|
||||
ctx->s->write_list[s->rlower].data = saved;
|
||||
|
||||
ci = screen_write_get_citem();
|
||||
ci->x = 0;
|
||||
ci->used = screen_size_x(s);
|
||||
ci->type = CLEAR;
|
||||
ci->bg = bg;
|
||||
TAILQ_INSERT_TAIL(&ctx->s->write_list[s->rlower].items, ci, entry);
|
||||
}
|
||||
|
||||
/* Flush collected lines. */
|
||||
@ -1515,12 +1551,11 @@ static void
|
||||
screen_write_collect_flush(struct screen_write_ctx *ctx, int scroll_only,
|
||||
const char *from)
|
||||
{
|
||||
struct screen *s = ctx->s;
|
||||
struct screen_write_collect_item *ci, *tmp;
|
||||
struct screen_write_collect_line *cl;
|
||||
u_int y, cx, cy, items = 0;
|
||||
struct tty_ctx ttyctx;
|
||||
size_t written = 0;
|
||||
struct screen *s = ctx->s;
|
||||
struct screen_write_citem *ci, *tmp;
|
||||
struct screen_write_cline *cl;
|
||||
u_int y, cx, cy, last, items = 0;
|
||||
struct tty_ctx ttyctx;
|
||||
|
||||
if (ctx->scrolled != 0) {
|
||||
log_debug("%s: scrolled %u (region %u-%u)", __func__,
|
||||
@ -1542,23 +1577,18 @@ screen_write_collect_flush(struct screen_write_ctx *ctx, int scroll_only,
|
||||
cx = s->cx; cy = s->cy;
|
||||
for (y = 0; y < screen_size_y(s); y++) {
|
||||
cl = &ctx->s->write_list[y];
|
||||
if (cl->bg != 0) {
|
||||
screen_write_set_cursor(ctx, 0, y);
|
||||
screen_write_initctx(ctx, &ttyctx, 1);
|
||||
ttyctx.bg = cl->bg - 1;
|
||||
tty_write(tty_cmd_clearline, &ttyctx);
|
||||
}
|
||||
last = UINT_MAX;
|
||||
TAILQ_FOREACH_SAFE(ci, &cl->items, entry, tmp) {
|
||||
if (last != UINT_MAX && ci->x <= last) {
|
||||
fatalx("collect list not in order: %u <= %u",
|
||||
ci->x, last);
|
||||
}
|
||||
screen_write_set_cursor(ctx, ci->x, y);
|
||||
if (ci->type == CLEAR_END) {
|
||||
log_debug("XXX %u %u", ci->x, ci->bg);
|
||||
if (ci->type == CLEAR) {
|
||||
screen_write_initctx(ctx, &ttyctx, 1);
|
||||
ttyctx.bg = ci->bg;
|
||||
tty_write(tty_cmd_clearendofline, &ttyctx);
|
||||
} else if (ci->type == CLEAR_START) {
|
||||
screen_write_initctx(ctx, &ttyctx, 1);
|
||||
ttyctx.bg = ci->bg;
|
||||
tty_write(tty_cmd_clearstartofline, &ttyctx);
|
||||
ttyctx.num = ci->used;
|
||||
tty_write(tty_cmd_clearcharacter, &ttyctx);
|
||||
} else {
|
||||
screen_write_initctx(ctx, &ttyctx, 0);
|
||||
ttyctx.cell = &ci->gc;
|
||||
@ -1567,38 +1597,41 @@ screen_write_collect_flush(struct screen_write_ctx *ctx, int scroll_only,
|
||||
ttyctx.num = ci->used;
|
||||
tty_write(tty_cmd_cells, &ttyctx);
|
||||
}
|
||||
|
||||
items++;
|
||||
written += ci->used;
|
||||
|
||||
TAILQ_REMOVE(&cl->items, ci, entry);
|
||||
free(ci);
|
||||
screen_write_free_citem(ci);
|
||||
last = ci->x;
|
||||
}
|
||||
cl->bg = 0;
|
||||
}
|
||||
s->cx = cx; s->cy = cy;
|
||||
|
||||
log_debug("%s: flushed %u items (%zu bytes) (%s)", __func__, items,
|
||||
written, from);
|
||||
ctx->written += written;
|
||||
log_debug("%s: flushed %u items (%s)", __func__, items, from);
|
||||
}
|
||||
|
||||
/* Finish and store collected cells. */
|
||||
void
|
||||
screen_write_collect_end(struct screen_write_ctx *ctx)
|
||||
{
|
||||
struct screen *s = ctx->s;
|
||||
struct screen_write_collect_item *ci = ctx->item;
|
||||
struct screen_write_collect_line *cl = &s->write_list[s->cy];
|
||||
struct grid_cell gc;
|
||||
u_int xx;
|
||||
struct screen *s = ctx->s;
|
||||
struct screen_write_citem *ci = ctx->item, *before;
|
||||
struct screen_write_cline *cl = &s->write_list[s->cy];
|
||||
struct grid_cell gc;
|
||||
u_int xx;
|
||||
int wrapped = ci->wrapped;
|
||||
|
||||
if (ci->used == 0)
|
||||
return;
|
||||
|
||||
before = screen_write_collect_trim(ctx, s->cy, s->cx, ci->used,
|
||||
&wrapped);
|
||||
ci->x = s->cx;
|
||||
TAILQ_INSERT_TAIL(&cl->items, ci, entry);
|
||||
ctx->item = xcalloc(1, sizeof *ctx->item);
|
||||
ci->wrapped = wrapped;
|
||||
if (before == NULL)
|
||||
TAILQ_INSERT_TAIL(&cl->items, ci, entry);
|
||||
else
|
||||
TAILQ_INSERT_BEFORE(before, ci, entry);
|
||||
ctx->item = screen_write_get_citem();
|
||||
|
||||
log_debug("%s: %u %.*s (at %u,%u)", __func__, ci->used,
|
||||
(int)ci->used, cl->data + ci->x, s->cx, s->cy);
|
||||
@ -1634,10 +1667,10 @@ void
|
||||
screen_write_collect_add(struct screen_write_ctx *ctx,
|
||||
const struct grid_cell *gc)
|
||||
{
|
||||
struct screen *s = ctx->s;
|
||||
struct screen_write_collect_item *ci;
|
||||
u_int sx = screen_size_x(s);
|
||||
int collect;
|
||||
struct screen *s = ctx->s;
|
||||
struct screen_write_citem *ci;
|
||||
u_int sx = screen_size_x(s);
|
||||
int collect;
|
||||
|
||||
/*
|
||||
* Don't need to check that the attributes and whatnot are still the
|
||||
@ -1662,7 +1695,6 @@ screen_write_collect_add(struct screen_write_ctx *ctx,
|
||||
screen_write_cell(ctx, gc);
|
||||
return;
|
||||
}
|
||||
ctx->cells++;
|
||||
|
||||
if (s->cx > sx - 1 || ctx->item->used > sx - 1 - s->cx)
|
||||
screen_write_collect_end(ctx);
|
||||
@ -1699,7 +1731,6 @@ screen_write_cell(struct screen_write_ctx *ctx, const struct grid_cell *gc)
|
||||
/* Ignore padding cells. */
|
||||
if (gc->flags & GRID_FLAG_PADDING)
|
||||
return;
|
||||
ctx->cells++;
|
||||
|
||||
/* If the width is zero, combine onto the previous character. */
|
||||
if (width == 0) {
|
||||
@ -1826,9 +1857,7 @@ screen_write_cell(struct screen_write_ctx *ctx, const struct grid_cell *gc)
|
||||
} else
|
||||
ttyctx.cell = gc;
|
||||
tty_write(tty_cmd_cell, &ttyctx);
|
||||
ctx->written++;
|
||||
} else
|
||||
ctx->skipped++;
|
||||
}
|
||||
}
|
||||
|
||||
/* Combine a UTF-8 zero-width character onto the previous. */
|
||||
@ -1974,6 +2003,8 @@ screen_write_alternateon(struct screen_write_ctx *ctx, struct grid_cell *gc,
|
||||
|
||||
if (wp != NULL && !options_get_number(wp->options, "alternate-screen"))
|
||||
return;
|
||||
|
||||
screen_write_collect_flush(ctx, 0, __func__);
|
||||
screen_alternate_on(ctx->s, gc, cursor);
|
||||
|
||||
screen_write_initctx(ctx, &ttyctx, 1);
|
||||
@ -1990,6 +2021,8 @@ screen_write_alternateoff(struct screen_write_ctx *ctx, struct grid_cell *gc,
|
||||
|
||||
if (wp != NULL && !options_get_number(wp->options, "alternate-screen"))
|
||||
return;
|
||||
|
||||
screen_write_collect_flush(ctx, 0, __func__);
|
||||
screen_alternate_off(ctx->s, gc, cursor);
|
||||
|
||||
screen_write_initctx(ctx, &ttyctx, 1);
|
||||
|
41
screen.c
41
screen.c
@ -573,7 +573,14 @@ screen_alternate_on(struct screen *s, struct grid_cell *gc, int cursor)
|
||||
void
|
||||
screen_alternate_off(struct screen *s, struct grid_cell *gc, int cursor)
|
||||
{
|
||||
u_int sx, sy;
|
||||
u_int sx = screen_size_x(s), sy = screen_size_y(s);
|
||||
|
||||
/*
|
||||
* If the current size is different, temporarily resize to the old size
|
||||
* before copying back.
|
||||
*/
|
||||
if (s->saved_grid != NULL)
|
||||
screen_resize(s, s->saved_grid->sx, s->saved_grid->sy, 1);
|
||||
|
||||
/*
|
||||
* Restore the cursor position and cell. This happens even if not
|
||||
@ -581,29 +588,23 @@ screen_alternate_off(struct screen *s, struct grid_cell *gc, int cursor)
|
||||
*/
|
||||
if (cursor && s->saved_cx != UINT_MAX && s->saved_cy != UINT_MAX) {
|
||||
s->cx = s->saved_cx;
|
||||
if (s->cx > screen_size_x(s) - 1)
|
||||
s->cx = screen_size_x(s) - 1;
|
||||
s->cy = s->saved_cy;
|
||||
if (s->cy > screen_size_y(s) - 1)
|
||||
s->cy = screen_size_y(s) - 1;
|
||||
if (gc != NULL)
|
||||
memcpy(gc, &s->saved_cell, sizeof *gc);
|
||||
}
|
||||
|
||||
if (s->saved_grid == NULL)
|
||||
/* If not in the alternate screen, do nothing more. */
|
||||
if (s->saved_grid == NULL) {
|
||||
if (s->cx > screen_size_x(s) - 1)
|
||||
s->cx = screen_size_x(s) - 1;
|
||||
if (s->cy > screen_size_y(s) - 1)
|
||||
s->cy = screen_size_y(s) - 1;
|
||||
return;
|
||||
sx = screen_size_x(s);
|
||||
sy = screen_size_y(s);
|
||||
|
||||
/*
|
||||
* If the current size is bigger, temporarily resize to the old size
|
||||
* before copying back.
|
||||
*/
|
||||
if (sy > s->saved_grid->sy)
|
||||
screen_resize(s, sx, s->saved_grid->sy, 1);
|
||||
}
|
||||
|
||||
/* Restore the saved grid. */
|
||||
grid_duplicate_lines(s->grid, screen_hsize(s), s->saved_grid, 0, sy);
|
||||
grid_duplicate_lines(s->grid, screen_hsize(s), s->saved_grid, 0,
|
||||
s->saved_grid->sy);
|
||||
|
||||
/*
|
||||
* Turn history back on (so resize can use it) and then resize back to
|
||||
@ -611,9 +612,13 @@ screen_alternate_off(struct screen *s, struct grid_cell *gc, int cursor)
|
||||
*/
|
||||
if (s->saved_flags & GRID_HISTORY)
|
||||
s->grid->flags |= GRID_HISTORY;
|
||||
if (sy > s->saved_grid->sy || sx != s->saved_grid->sx)
|
||||
screen_resize(s, sx, sy, 1);
|
||||
screen_resize(s, sx, sy, 1);
|
||||
|
||||
grid_destroy(s->saved_grid);
|
||||
s->saved_grid = NULL;
|
||||
|
||||
if (s->cx > screen_size_x(s) - 1)
|
||||
s->cx = screen_size_x(s) - 1;
|
||||
if (s->cy > screen_size_y(s) - 1)
|
||||
s->cy = screen_size_y(s) - 1;
|
||||
}
|
||||
|
132
server-client.c
132
server-client.c
@ -21,7 +21,6 @@
|
||||
#include <sys/uio.h>
|
||||
|
||||
#include <errno.h>
|
||||
#include <event.h>
|
||||
#include <fcntl.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
@ -40,6 +39,7 @@ static void server_client_repeat_timer(int, short, void *);
|
||||
static void server_client_click_timer(int, short, void *);
|
||||
static void server_client_check_exit(struct client *);
|
||||
static void server_client_check_redraw(struct client *);
|
||||
static void server_client_check_modes(struct client *);
|
||||
static void server_client_set_title(struct client *);
|
||||
static void server_client_reset_state(struct client *);
|
||||
static int server_client_assume_paste(struct session *);
|
||||
@ -48,12 +48,6 @@ static void server_client_dispatch(struct imsg *, void *);
|
||||
static void server_client_dispatch_command(struct client *, struct imsg *);
|
||||
static void server_client_dispatch_identify(struct client *, struct imsg *);
|
||||
static void server_client_dispatch_shell(struct client *);
|
||||
static void server_client_dispatch_write_ready(struct client *,
|
||||
struct imsg *);
|
||||
static void server_client_dispatch_read_data(struct client *,
|
||||
struct imsg *);
|
||||
static void server_client_dispatch_read_done(struct client *,
|
||||
struct imsg *);
|
||||
|
||||
/* Compare client windows. */
|
||||
static int
|
||||
@ -310,6 +304,7 @@ server_client_lost(struct client *c)
|
||||
|
||||
free(c->term_name);
|
||||
free(c->term_type);
|
||||
tty_term_free_list(c->term_caps, c->term_ncaps);
|
||||
|
||||
status_free(c);
|
||||
|
||||
@ -1353,6 +1348,7 @@ server_client_loop(void)
|
||||
TAILQ_FOREACH(c, &clients, entry) {
|
||||
server_client_check_exit(c);
|
||||
if (c->session != NULL) {
|
||||
server_client_check_modes(c);
|
||||
server_client_check_redraw(c);
|
||||
server_client_reset_state(c);
|
||||
}
|
||||
@ -1777,11 +1773,11 @@ server_client_check_exit(struct client *c)
|
||||
|
||||
switch (c->exit_type) {
|
||||
case CLIENT_EXIT_RETURN:
|
||||
if (c->exit_message != NULL) {
|
||||
if (c->exit_message != NULL)
|
||||
msize = strlen(c->exit_message) + 1;
|
||||
size = (sizeof c->retval) + msize;
|
||||
} else
|
||||
size = (sizeof c->retval);
|
||||
else
|
||||
msize = 0;
|
||||
size = (sizeof c->retval) + msize;
|
||||
data = xmalloc(size);
|
||||
memcpy(data, &c->retval, sizeof c->retval);
|
||||
if (c->exit_message != NULL)
|
||||
@ -1808,6 +1804,28 @@ server_client_redraw_timer(__unused int fd, __unused short events,
|
||||
log_debug("redraw timer fired");
|
||||
}
|
||||
|
||||
/*
|
||||
* Check if modes need to be updated. Only modes in the current window are
|
||||
* updated and it is done when the status line is redrawn.
|
||||
*/
|
||||
static void
|
||||
server_client_check_modes(struct client *c)
|
||||
{
|
||||
struct window *w = c->session->curw->window;
|
||||
struct window_pane *wp;
|
||||
struct window_mode_entry *wme;
|
||||
|
||||
if (c->flags & (CLIENT_CONTROL|CLIENT_SUSPENDED))
|
||||
return;
|
||||
if (~c->flags & CLIENT_REDRAWSTATUS)
|
||||
return;
|
||||
TAILQ_FOREACH(wp, &w->panes, entry) {
|
||||
wme = TAILQ_FIRST(&wp->modes);
|
||||
if (wme != NULL && wme->mode->update != NULL)
|
||||
wme->mode->update(wme);
|
||||
}
|
||||
}
|
||||
|
||||
/* Check for client redraws. */
|
||||
static void
|
||||
server_client_check_redraw(struct client *c)
|
||||
@ -1977,16 +1995,17 @@ server_client_dispatch(struct imsg *imsg, void *arg)
|
||||
datalen = imsg->hdr.len - IMSG_HEADER_SIZE;
|
||||
|
||||
switch (imsg->hdr.type) {
|
||||
case MSG_IDENTIFY_CLIENTPID:
|
||||
case MSG_IDENTIFY_CWD:
|
||||
case MSG_IDENTIFY_ENVIRON:
|
||||
case MSG_IDENTIFY_FEATURES:
|
||||
case MSG_IDENTIFY_FLAGS:
|
||||
case MSG_IDENTIFY_LONGFLAGS:
|
||||
case MSG_IDENTIFY_TERM:
|
||||
case MSG_IDENTIFY_TTYNAME:
|
||||
case MSG_IDENTIFY_CWD:
|
||||
case MSG_IDENTIFY_STDIN:
|
||||
case MSG_IDENTIFY_STDOUT:
|
||||
case MSG_IDENTIFY_ENVIRON:
|
||||
case MSG_IDENTIFY_CLIENTPID:
|
||||
case MSG_IDENTIFY_TERM:
|
||||
case MSG_IDENTIFY_TERMINFO:
|
||||
case MSG_IDENTIFY_TTYNAME:
|
||||
case MSG_IDENTIFY_DONE:
|
||||
server_client_dispatch_identify(c, imsg);
|
||||
break;
|
||||
@ -2044,13 +2063,13 @@ server_client_dispatch(struct imsg *imsg, void *arg)
|
||||
server_client_dispatch_shell(c);
|
||||
break;
|
||||
case MSG_WRITE_READY:
|
||||
server_client_dispatch_write_ready(c, imsg);
|
||||
file_write_ready(&c->files, imsg);
|
||||
break;
|
||||
case MSG_READ:
|
||||
server_client_dispatch_read_data(c, imsg);
|
||||
file_read_data(&c->files, imsg);
|
||||
break;
|
||||
case MSG_READ_DONE:
|
||||
server_client_dispatch_read_done(c, imsg);
|
||||
file_read_done(&c->files, imsg);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -2180,6 +2199,14 @@ server_client_dispatch_identify(struct client *c, struct imsg *imsg)
|
||||
c->term_name = xstrdup(data);
|
||||
log_debug("client %p IDENTIFY_TERM %s", c, data);
|
||||
break;
|
||||
case MSG_IDENTIFY_TERMINFO:
|
||||
if (datalen == 0 || data[datalen - 1] != '\0')
|
||||
fatalx("bad MSG_IDENTIFY_TERMINFO string");
|
||||
c->term_caps = xreallocarray(c->term_caps, c->term_ncaps + 1,
|
||||
sizeof *c->term_caps);
|
||||
c->term_caps[c->term_ncaps++] = xstrdup(data);
|
||||
log_debug("client %p IDENTIFY_TERMINFO %s", c, data);
|
||||
break;
|
||||
case MSG_IDENTIFY_TTYNAME:
|
||||
if (datalen == 0 || data[datalen - 1] != '\0')
|
||||
fatalx("bad MSG_IDENTIFY_TTYNAME string");
|
||||
@ -2280,71 +2307,6 @@ server_client_dispatch_shell(struct client *c)
|
||||
proc_kill_peer(c->peer);
|
||||
}
|
||||
|
||||
/* Handle write ready message. */
|
||||
static void
|
||||
server_client_dispatch_write_ready(struct client *c, struct imsg *imsg)
|
||||
{
|
||||
struct msg_write_ready *msg = imsg->data;
|
||||
size_t msglen = imsg->hdr.len - IMSG_HEADER_SIZE;
|
||||
struct client_file find, *cf;
|
||||
|
||||
if (msglen != sizeof *msg)
|
||||
fatalx("bad MSG_WRITE_READY size");
|
||||
find.stream = msg->stream;
|
||||
if ((cf = RB_FIND(client_files, &c->files, &find)) == NULL)
|
||||
return;
|
||||
if (msg->error != 0) {
|
||||
cf->error = msg->error;
|
||||
file_fire_done(cf);
|
||||
} else
|
||||
file_push(cf);
|
||||
}
|
||||
|
||||
/* Handle read data message. */
|
||||
static void
|
||||
server_client_dispatch_read_data(struct client *c, struct imsg *imsg)
|
||||
{
|
||||
struct msg_read_data *msg = imsg->data;
|
||||
size_t msglen = imsg->hdr.len - IMSG_HEADER_SIZE;
|
||||
struct client_file find, *cf;
|
||||
void *bdata = msg + 1;
|
||||
size_t bsize = msglen - sizeof *msg;
|
||||
|
||||
if (msglen < sizeof *msg)
|
||||
fatalx("bad MSG_READ_DATA size");
|
||||
find.stream = msg->stream;
|
||||
if ((cf = RB_FIND(client_files, &c->files, &find)) == NULL)
|
||||
return;
|
||||
|
||||
log_debug("%s: file %d read %zu bytes", c->name, cf->stream, bsize);
|
||||
if (cf->error == 0) {
|
||||
if (evbuffer_add(cf->buffer, bdata, bsize) != 0) {
|
||||
cf->error = ENOMEM;
|
||||
file_fire_done(cf);
|
||||
} else
|
||||
file_fire_read(cf);
|
||||
}
|
||||
}
|
||||
|
||||
/* Handle read done message. */
|
||||
static void
|
||||
server_client_dispatch_read_done(struct client *c, struct imsg *imsg)
|
||||
{
|
||||
struct msg_read_done *msg = imsg->data;
|
||||
size_t msglen = imsg->hdr.len - IMSG_HEADER_SIZE;
|
||||
struct client_file find, *cf;
|
||||
|
||||
if (msglen != sizeof *msg)
|
||||
fatalx("bad MSG_READ_DONE size");
|
||||
find.stream = msg->stream;
|
||||
if ((cf = RB_FIND(client_files, &c->files, &find)) == NULL)
|
||||
return;
|
||||
|
||||
log_debug("%s: file %d read done", c->name, cf->stream);
|
||||
cf->error = msg->error;
|
||||
file_fire_done(cf);
|
||||
}
|
||||
|
||||
/* Get client working directory. */
|
||||
const char *
|
||||
server_client_get_cwd(struct client *c, struct session *s)
|
||||
@ -2432,6 +2394,8 @@ server_client_get_flags(struct client *c)
|
||||
*s = '\0';
|
||||
if (c->flags & CLIENT_ATTACHED)
|
||||
strlcat(s, "attached,", sizeof s);
|
||||
if (c->flags & CLIENT_FOCUSED)
|
||||
strlcat(s, "focused,", sizeof s);
|
||||
if (c->flags & CLIENT_CONTROL)
|
||||
strlcat(s, "control-mode,", sizeof s);
|
||||
if (c->flags & CLIENT_IGNORESIZE)
|
||||
|
41
server-fn.c
41
server-fn.c
@ -312,6 +312,7 @@ server_destroy_pane(struct window_pane *wp, int notify)
|
||||
struct grid_cell gc;
|
||||
time_t t;
|
||||
char tim[26];
|
||||
int remain_on_exit;
|
||||
|
||||
if (wp->fd != -1) {
|
||||
#ifdef HAVE_UTEMPTER
|
||||
@ -323,10 +324,17 @@ server_destroy_pane(struct window_pane *wp, int notify)
|
||||
wp->fd = -1;
|
||||
}
|
||||
|
||||
if (options_get_number(wp->options, "remain-on-exit")) {
|
||||
if (~wp->flags & PANE_STATUSREADY)
|
||||
return;
|
||||
|
||||
remain_on_exit = options_get_number(wp->options, "remain-on-exit");
|
||||
if (remain_on_exit != 0 && (~wp->flags & PANE_STATUSREADY))
|
||||
return;
|
||||
switch (remain_on_exit) {
|
||||
case 0:
|
||||
break;
|
||||
case 2:
|
||||
if (WIFEXITED(wp->status) && WEXITSTATUS(wp->status) == 0)
|
||||
break;
|
||||
/* FALLTHROUGH */
|
||||
case 1:
|
||||
if (wp->flags & PANE_STATUSDRAWN)
|
||||
return;
|
||||
wp->flags |= PANE_STATUSDRAWN;
|
||||
@ -394,9 +402,8 @@ server_destroy_session_group(struct session *s)
|
||||
static struct session *
|
||||
server_next_session(struct session *s)
|
||||
{
|
||||
struct session *s_loop, *s_out;
|
||||
struct session *s_loop, *s_out = NULL;
|
||||
|
||||
s_out = NULL;
|
||||
RB_FOREACH(s_loop, sessions, &sessions) {
|
||||
if (s_loop == s)
|
||||
continue;
|
||||
@ -407,17 +414,35 @@ server_next_session(struct session *s)
|
||||
return (s_out);
|
||||
}
|
||||
|
||||
static struct session *
|
||||
server_next_detached_session(struct session *s)
|
||||
{
|
||||
struct session *s_loop, *s_out = NULL;
|
||||
|
||||
RB_FOREACH(s_loop, sessions, &sessions) {
|
||||
if (s_loop == s || s_loop->attached)
|
||||
continue;
|
||||
if (s_out == NULL ||
|
||||
timercmp(&s_loop->activity_time, &s_out->activity_time, <))
|
||||
s_out = s_loop;
|
||||
}
|
||||
return (s_out);
|
||||
}
|
||||
|
||||
void
|
||||
server_destroy_session(struct session *s)
|
||||
{
|
||||
struct client *c;
|
||||
struct session *s_new;
|
||||
int detach_on_destroy;
|
||||
|
||||
if (!options_get_number(s->options, "detach-on-destroy"))
|
||||
detach_on_destroy = options_get_number(s->options, "detach-on-destroy");
|
||||
if (detach_on_destroy == 0)
|
||||
s_new = server_next_session(s);
|
||||
else if (detach_on_destroy == 2)
|
||||
s_new = server_next_detached_session(s);
|
||||
else
|
||||
s_new = NULL;
|
||||
|
||||
TAILQ_FOREACH(c, &clients, entry) {
|
||||
if (c->session != s)
|
||||
continue;
|
||||
|
30
server.c
30
server.c
@ -24,7 +24,6 @@
|
||||
#include <sys/wait.h>
|
||||
|
||||
#include <errno.h>
|
||||
#include <event.h>
|
||||
#include <fcntl.h>
|
||||
#include <signal.h>
|
||||
#include <stdio.h>
|
||||
@ -155,35 +154,22 @@ int
|
||||
server_start(struct tmuxproc *client, int flags, struct event_base *base,
|
||||
int lockfd, char *lockfile)
|
||||
{
|
||||
int pair[2];
|
||||
sigset_t set, oldset;
|
||||
struct client *c = NULL;
|
||||
char *cause = NULL;
|
||||
int fd;
|
||||
sigset_t set, oldset;
|
||||
struct client *c = NULL;
|
||||
char *cause = NULL;
|
||||
|
||||
sigfillset(&set);
|
||||
sigprocmask(SIG_BLOCK, &set, &oldset);
|
||||
|
||||
if (~flags & CLIENT_NOFORK) {
|
||||
if (socketpair(AF_UNIX, SOCK_STREAM, PF_UNSPEC, pair) != 0)
|
||||
fatal("socketpair failed");
|
||||
|
||||
switch (fork()) {
|
||||
case -1:
|
||||
fatal("fork failed");
|
||||
case 0:
|
||||
break;
|
||||
default:
|
||||
if (proc_fork_and_daemon(&fd) != 0) {
|
||||
sigprocmask(SIG_SETMASK, &oldset, NULL);
|
||||
close(pair[1]);
|
||||
return (pair[0]);
|
||||
return (fd);
|
||||
}
|
||||
close(pair[0]);
|
||||
if (daemon(1, 0) != 0)
|
||||
fatal("daemon failed");
|
||||
}
|
||||
|
||||
server_client_flags = flags;
|
||||
proc_clear_signals(client, 0);
|
||||
server_client_flags = flags;
|
||||
|
||||
if (event_reinit(base) != 0)
|
||||
fatalx("event_reinit failed");
|
||||
@ -212,7 +198,7 @@ server_start(struct tmuxproc *client, int flags, struct event_base *base,
|
||||
if (server_fd != -1)
|
||||
server_update_socket();
|
||||
if (~flags & CLIENT_NOFORK)
|
||||
c = server_client_create(pair[1]);
|
||||
c = server_client_create(fd);
|
||||
else
|
||||
options_set_number(global_options, "exit-empty", 0);
|
||||
|
||||
|
15
spawn.c
15
spawn.c
@ -182,7 +182,7 @@ spawn_window(struct spawn_context *sc, char **cause)
|
||||
NULL);
|
||||
options_set_number(w->options, "automatic-rename", 0);
|
||||
} else
|
||||
w->name = xstrdup(default_window_name(w));
|
||||
w->name = default_window_name(w);
|
||||
}
|
||||
|
||||
/* Switch to the new window if required. */
|
||||
@ -263,8 +263,9 @@ spawn_pane(struct spawn_context *sc, char **cause)
|
||||
}
|
||||
|
||||
/*
|
||||
* Now we have a pane with nothing running in it ready for the new process.
|
||||
* Work out the command and arguments and store the working directory.
|
||||
* Now we have a pane with nothing running in it ready for the new
|
||||
* process. Work out the command and arguments and store the working
|
||||
* directory.
|
||||
*/
|
||||
if (sc->argc == 0 && (~sc->flags & SPAWN_RESPAWN)) {
|
||||
cmd = options_get_string(s->options, "default-command");
|
||||
@ -377,10 +378,10 @@ spawn_pane(struct spawn_context *sc, char **cause)
|
||||
* Child process. Change to the working directory or home if that
|
||||
* fails.
|
||||
*/
|
||||
if (chdir(new_wp->cwd) != 0) {
|
||||
if ((tmp = find_home()) == NULL || chdir(tmp) != 0)
|
||||
chdir("/");
|
||||
}
|
||||
if (chdir(new_wp->cwd) != 0 &&
|
||||
((tmp = find_home()) == NULL || chdir(tmp) != 0) &&
|
||||
chdir("/") != 0)
|
||||
fatal("chdir failed");
|
||||
|
||||
/*
|
||||
* Update terminal escape characters from the session if available and
|
||||
|
58
status.c
58
status.c
@ -543,7 +543,7 @@ status_prompt_set(struct client *c, struct cmd_find_state *fs,
|
||||
prompt_free_cb freecb, void *data, int flags)
|
||||
{
|
||||
struct format_tree *ft;
|
||||
char *tmp, *cp;
|
||||
char *tmp;
|
||||
|
||||
if (fs != NULL)
|
||||
ft = format_create_from_state(NULL, c, fs);
|
||||
@ -563,7 +563,13 @@ status_prompt_set(struct client *c, struct cmd_find_state *fs,
|
||||
|
||||
c->prompt_string = format_expand_time(ft, msg);
|
||||
|
||||
c->prompt_buffer = utf8_fromcstr(tmp);
|
||||
if (flags & PROMPT_INCREMENTAL) {
|
||||
c->prompt_last = xstrdup(tmp);
|
||||
c->prompt_buffer = utf8_fromcstr("");
|
||||
} else {
|
||||
c->prompt_last = NULL;
|
||||
c->prompt_buffer = utf8_fromcstr(tmp);
|
||||
}
|
||||
c->prompt_index = utf8_strlen(c->prompt_buffer);
|
||||
|
||||
c->prompt_inputcb = inputcb;
|
||||
@ -579,11 +585,8 @@ status_prompt_set(struct client *c, struct cmd_find_state *fs,
|
||||
c->tty.flags |= (TTY_NOCURSOR|TTY_FREEZE);
|
||||
c->flags |= CLIENT_REDRAWSTATUS;
|
||||
|
||||
if ((flags & PROMPT_INCREMENTAL) && *tmp != '\0') {
|
||||
xasprintf(&cp, "=%s", tmp);
|
||||
c->prompt_inputcb(c, c->prompt_data, cp, 0);
|
||||
free(cp);
|
||||
}
|
||||
if (flags & PROMPT_INCREMENTAL)
|
||||
c->prompt_inputcb(c, c->prompt_data, "=", 0);
|
||||
|
||||
free(tmp);
|
||||
format_free(ft);
|
||||
@ -599,6 +602,9 @@ status_prompt_clear(struct client *c)
|
||||
if (c->prompt_freecb != NULL && c->prompt_data != NULL)
|
||||
c->prompt_freecb(c->prompt_data);
|
||||
|
||||
free(c->prompt_last);
|
||||
c->prompt_last = NULL;
|
||||
|
||||
free(c->prompt_string);
|
||||
c->prompt_string = NULL;
|
||||
|
||||
@ -1260,17 +1266,27 @@ process_key:
|
||||
status_prompt_clear(c);
|
||||
break;
|
||||
case '\022': /* C-r */
|
||||
if (c->prompt_flags & PROMPT_INCREMENTAL) {
|
||||
if (~c->prompt_flags & PROMPT_INCREMENTAL)
|
||||
break;
|
||||
if (c->prompt_buffer[0].size == 0) {
|
||||
prefix = '=';
|
||||
free (c->prompt_buffer);
|
||||
c->prompt_buffer = utf8_fromcstr(c->prompt_last);
|
||||
c->prompt_index = utf8_strlen(c->prompt_buffer);
|
||||
} else
|
||||
prefix = '-';
|
||||
goto changed;
|
||||
}
|
||||
break;
|
||||
goto changed;
|
||||
case '\023': /* C-s */
|
||||
if (c->prompt_flags & PROMPT_INCREMENTAL) {
|
||||
if (~c->prompt_flags & PROMPT_INCREMENTAL)
|
||||
break;
|
||||
if (c->prompt_buffer[0].size == 0) {
|
||||
prefix = '=';
|
||||
free (c->prompt_buffer);
|
||||
c->prompt_buffer = utf8_fromcstr(c->prompt_last);
|
||||
c->prompt_index = utf8_strlen(c->prompt_buffer);
|
||||
} else
|
||||
prefix = '+';
|
||||
goto changed;
|
||||
}
|
||||
break;
|
||||
goto changed;
|
||||
default:
|
||||
goto append_key;
|
||||
}
|
||||
@ -1303,12 +1319,14 @@ append_key:
|
||||
}
|
||||
|
||||
if (c->prompt_flags & PROMPT_SINGLE) {
|
||||
s = utf8_tocstr(c->prompt_buffer);
|
||||
if (strlen(s) != 1)
|
||||
if (utf8_strlen(c->prompt_buffer) != 1)
|
||||
status_prompt_clear(c);
|
||||
else if (c->prompt_inputcb(c, c->prompt_data, s, 1) == 0)
|
||||
status_prompt_clear(c);
|
||||
free(s);
|
||||
else {
|
||||
s = utf8_tocstr(c->prompt_buffer);
|
||||
if (c->prompt_inputcb(c, c->prompt_data, s, 1) == 0)
|
||||
status_prompt_clear(c);
|
||||
free(s);
|
||||
}
|
||||
}
|
||||
|
||||
changed:
|
||||
|
277
tmux.1
277
tmux.1
@ -191,6 +191,11 @@ directories are missing).
|
||||
Behave as a login shell.
|
||||
This flag currently has no effect and is for compatibility with other shells
|
||||
when using tmux as a login shell.
|
||||
.It Fl N
|
||||
Do not start the server even if the command would normally do so (for example
|
||||
.Ic new-session
|
||||
or
|
||||
.Ic start-server ) .
|
||||
.It Fl S Ar socket-path
|
||||
Specify a full alternative path to the server socket.
|
||||
If
|
||||
@ -517,6 +522,67 @@ Commands separated by semicolons together form a
|
||||
- if a command in the sequence encounters an error, no subsequent commands are
|
||||
executed.
|
||||
.Pp
|
||||
It is recommended that a semicolon used as a command separator should be
|
||||
written as an individual token, for example from
|
||||
.Xr sh 1 :
|
||||
.Bd -literal -offset indent
|
||||
$ tmux neww \\; splitw
|
||||
.Ed
|
||||
.Pp
|
||||
Or:
|
||||
.Bd -literal -offset indent
|
||||
$ tmux neww ';' splitw
|
||||
.Ed
|
||||
.Pp
|
||||
Or from the tmux command prompt:
|
||||
.Bd -literal -offset indent
|
||||
neww ; splitw
|
||||
.Ed
|
||||
.Pp
|
||||
However, a trailing semicolon is also interpreted as a command separator,
|
||||
for example in these
|
||||
.Xr sh 1
|
||||
commands:
|
||||
.Bd -literal -offset indent
|
||||
$ tmux neww\e\e; splitw
|
||||
.Ed
|
||||
.Pp
|
||||
Or:
|
||||
.Bd -literal -offset indent
|
||||
$ tmux 'neww;' splitw
|
||||
.Ed
|
||||
.Pp
|
||||
As in these examples, when running tmux from the shell extra care must be taken
|
||||
to properly quote semicolons:
|
||||
.Bl -enum -offset Ds
|
||||
.It
|
||||
Semicolons that should be interpreted as a command separator
|
||||
should be escaped according to the shell conventions.
|
||||
For
|
||||
.Xr sh 1
|
||||
this typically means quoted (such as
|
||||
.Ql neww ';' splitw )
|
||||
or escaped (such as
|
||||
.Ql neww \e\e\e\e; splitw ) .
|
||||
.It
|
||||
Individual semicolons or trailing semicolons that should be interpreted as
|
||||
arguments should be escaped twice: once according to the shell conventions and
|
||||
a second time for
|
||||
.Nm ;
|
||||
for example:
|
||||
.Bd -literal -offset indent
|
||||
$ tmux neww 'foo\e\e;' bar
|
||||
$ tmux neww foo\e\e\e\e; bar
|
||||
.Ed
|
||||
.It
|
||||
Semicolons that are not individual tokens or trailing another token should only
|
||||
be escaped once according to shell conventions; for example:
|
||||
.Bd -literal -offset indent
|
||||
$ tmux neww 'foo-;-bar'
|
||||
$ tmux neww foo-\e\e;-bar
|
||||
.Ed
|
||||
.El
|
||||
.Pp
|
||||
Comments are marked by the unquoted # character - any remaining text after a
|
||||
comment is ignored until the end of the line.
|
||||
.Pp
|
||||
@ -867,12 +933,12 @@ arguments are
|
||||
commands.
|
||||
This may be a single argument passed to the shell, for example:
|
||||
.Bd -literal -offset indent
|
||||
new-window 'vi /etc/passwd'
|
||||
new-window 'vi ~/.tmux.conf'
|
||||
.Ed
|
||||
.Pp
|
||||
Will run:
|
||||
.Bd -literal -offset indent
|
||||
/bin/sh -c 'vi /etc/passwd'
|
||||
/bin/sh -c 'vi ~/.tmux.conf'
|
||||
.Ed
|
||||
.Pp
|
||||
Additionally, the
|
||||
@ -889,7 +955,7 @@ to be given as multiple arguments and executed directly (without
|
||||
This can avoid issues with shell quoting.
|
||||
For example:
|
||||
.Bd -literal -offset indent
|
||||
$ tmux new-window vi /etc/passwd
|
||||
$ tmux new-window vi ~/.tmux.conf
|
||||
.Ed
|
||||
.Pp
|
||||
Will run
|
||||
@ -935,7 +1001,7 @@ $ tmux kill-window -t :1
|
||||
|
||||
$ tmux new-window \e; split-window -d
|
||||
|
||||
$ tmux new-session -d 'vi /etc/passwd' \e; split-window -d \e; attach
|
||||
$ tmux new-session -d 'vi ~/.tmux.conf' \e; split-window -d \e; attach
|
||||
.Ed
|
||||
.Sh CLIENTS AND SESSIONS
|
||||
The
|
||||
@ -1357,7 +1423,7 @@ a pane ID such as
|
||||
.Ql %0 ;
|
||||
.Ql %*
|
||||
for all panes in the attached session;
|
||||
an window ID such as
|
||||
a window ID such as
|
||||
.Ql @0 ;
|
||||
or
|
||||
.Ql @*
|
||||
@ -1648,10 +1714,15 @@ The following commands are supported in copy mode:
|
||||
.It Li "page-down" Ta "C-f" Ta "PageDown"
|
||||
.It Li "page-down-and-cancel" Ta "" Ta ""
|
||||
.It Li "page-up" Ta "C-b" Ta "PageUp"
|
||||
.It Li "pipe [<command>] [<prefix>]" Ta "" Ta ""
|
||||
.It Li "pipe-no-clear [<command>] [<prefix>]" Ta "" Ta ""
|
||||
.It Li "pipe-and-cancel [<command>] [<prefix>]" Ta "" Ta ""
|
||||
.It Li "previous-matching-bracket" Ta "" Ta "M-C-b"
|
||||
.It Li "previous-paragraph" Ta "{" Ta "M-{"
|
||||
.It Li "previous-space" Ta "B" Ta ""
|
||||
.It Li "previous-word" Ta "b" Ta "M-b"
|
||||
.It Li "rectangle-on" Ta "" Ta ""
|
||||
.It Li "rectangle-off" Ta "" Ta ""
|
||||
.It Li "rectangle-toggle" Ta "v" Ta "R"
|
||||
.It Li "refresh-from-pane" Ta "r" Ta "r"
|
||||
.It Li "scroll-down" Ta "C-e" Ta "C-Down"
|
||||
@ -1701,7 +1772,9 @@ so buffers are named
|
||||
.Ql buffer1
|
||||
and so on).
|
||||
Pipe commands take a command argument which is the command to which the
|
||||
copied text is piped.
|
||||
selected text is piped.
|
||||
.Ql copy-pipe
|
||||
variants also copy the selection.
|
||||
The
|
||||
.Ql -and-cancel
|
||||
variants of some commands exit copy mode after they have completed (for copy
|
||||
@ -2098,7 +2171,7 @@ starts without the option information.
|
||||
This command works only if at least one client is attached.
|
||||
.It Xo
|
||||
.Ic display-panes
|
||||
.Op Fl b
|
||||
.Op Fl bN
|
||||
.Op Fl d Ar duration
|
||||
.Op Fl t Ar target-client
|
||||
.Op Ar template
|
||||
@ -2111,7 +2184,9 @@ See the
|
||||
and
|
||||
.Ic display-panes-active-colour
|
||||
session options.
|
||||
The indicator is closed when a key is pressed or
|
||||
The indicator is closed when a key is pressed (unless
|
||||
.Fl N
|
||||
is given) or
|
||||
.Ar duration
|
||||
milliseconds have passed.
|
||||
If
|
||||
@ -2339,7 +2414,7 @@ the
|
||||
.Ic base-index
|
||||
option.
|
||||
.It Xo Ic new-window
|
||||
.Op Fl abdkP
|
||||
.Op Fl abdkPS
|
||||
.Op Fl c Ar start-directory
|
||||
.Op Fl e Ar environment
|
||||
.Op Fl F Ar format
|
||||
@ -2368,6 +2443,14 @@ represents the window to be created; if the target already exists an error is
|
||||
shown, unless the
|
||||
.Fl k
|
||||
flag is used, in which case it is destroyed.
|
||||
If
|
||||
.Fl S
|
||||
is given and a window named
|
||||
.Ar window-name
|
||||
already exists, it is selected (unless
|
||||
.Fl d
|
||||
is also given in which case the command does nothing).
|
||||
.Pp
|
||||
.Ar shell-command
|
||||
is the command to execute.
|
||||
If
|
||||
@ -3134,7 +3217,7 @@ abc123
|
||||
Commands which set options are as follows:
|
||||
.Bl -tag -width Ds
|
||||
.It Xo Ic set-option
|
||||
.Op Fl aFgopqsuw
|
||||
.Op Fl aFgopqsuUw
|
||||
.Op Fl t Ar target-pane
|
||||
.Ar option Ar value
|
||||
.Xc
|
||||
@ -3167,6 +3250,11 @@ flag unsets an option, so a session inherits the option from the global
|
||||
options (or with
|
||||
.Fl g ,
|
||||
restores a global option to the default).
|
||||
.Fl U
|
||||
unsets an option (like
|
||||
.Fl u )
|
||||
but if the option is a pane option also unsets the option on any panes in the
|
||||
window.
|
||||
.Ar value
|
||||
depends on the option and may be a number, a string, or a flag (on, off, or
|
||||
omitted to toggle).
|
||||
@ -3559,12 +3647,16 @@ The default is 80x24.
|
||||
If enabled and the session is no longer attached to any clients, it is
|
||||
destroyed.
|
||||
.It Xo Ic detach-on-destroy
|
||||
.Op Ic on | off
|
||||
.Op Ic off | on | no-detached
|
||||
.Xc
|
||||
If on (the default), the client is detached when the session it is attached to
|
||||
is destroyed.
|
||||
If off, the client is switched to the most recently active of the remaining
|
||||
sessions.
|
||||
If
|
||||
.Ic no-detached ,
|
||||
the client is detached only if there are no detached sessions; if detached
|
||||
sessions exist, the client is switched to the most recently active.
|
||||
.It Ic display-panes-active-colour Ar colour
|
||||
Set the colour used by the
|
||||
.Ic display-panes
|
||||
@ -4058,12 +4150,6 @@ see the
|
||||
section.
|
||||
Attributes are ignored.
|
||||
.Pp
|
||||
.It Xo Ic synchronize-panes
|
||||
.Op Ic on | off
|
||||
.Xc
|
||||
Duplicate input to any pane to all other panes in the same window (only
|
||||
for panes that are not in any special mode).
|
||||
.Pp
|
||||
.It Ic window-status-activity-style Ar style
|
||||
Set status line style for windows with an activity alert.
|
||||
For how to specify
|
||||
@ -4178,14 +4264,23 @@ interactive application starts and restores it on exit, so that any output
|
||||
visible before the application starts reappears unchanged after it exits.
|
||||
.Pp
|
||||
.It Xo Ic remain-on-exit
|
||||
.Op Ic on | off
|
||||
.Op Ic on | off | failed
|
||||
.Xc
|
||||
A pane with this flag set is not destroyed when the program running in it
|
||||
exits.
|
||||
If set to
|
||||
.Ic failed ,
|
||||
then only when the program exit status is not zero.
|
||||
The pane may be reactivated with the
|
||||
.Ic respawn-pane
|
||||
command.
|
||||
.Pp
|
||||
.It Xo Ic synchronize-panes
|
||||
.Op Ic on | off
|
||||
.Xc
|
||||
Duplicate input to all other panes in the same window where this option is also
|
||||
on (only for panes that are not in any mode).
|
||||
.Pp
|
||||
.It Ic window-active-style Ar style
|
||||
Set the pane style when it is the active pane.
|
||||
For how to specify
|
||||
@ -4589,7 +4684,9 @@ pads the string to a given width, for example
|
||||
will result in a width of at least 10 characters.
|
||||
A positive width pads on the left, a negative on the right.
|
||||
.Ql n
|
||||
expands to the length of the variable, for example
|
||||
expands to the length of the variable and
|
||||
.Ql w
|
||||
to its width when displayed, for example
|
||||
.Ql #{n:window_name} .
|
||||
.Pp
|
||||
Prefixing a time variable with
|
||||
@ -4632,7 +4729,12 @@ of the variable respectively.
|
||||
.Ql q:\&
|
||||
will escape
|
||||
.Xr sh 1
|
||||
special characters.
|
||||
special characters or with a
|
||||
.Ql h
|
||||
suffix, escape hash characters (so
|
||||
.Ql #
|
||||
becomes
|
||||
.Ql ## ) .
|
||||
.Ql E:\&
|
||||
will expand the format twice, for example
|
||||
.Ql #{E:status-left}
|
||||
@ -4658,6 +4760,17 @@ For example, to get a list of windows formatted like the status line:
|
||||
#{W:#{E:window-status-format} ,#{E:window-status-current-format} }
|
||||
.Ed
|
||||
.Pp
|
||||
.Ql N:\&
|
||||
checks if a window (without any suffix or with the
|
||||
.Ql w
|
||||
suffix) or a session (with the
|
||||
.Ql s
|
||||
suffix) name exists, for example
|
||||
.Ql `N/w:foo`
|
||||
is replaced with 1 if a window named
|
||||
.Ql foo
|
||||
exists.
|
||||
.Pp
|
||||
A prefix of the form
|
||||
.Ql s/foo/bar/:\&
|
||||
will substitute
|
||||
@ -4704,6 +4817,7 @@ will be replaced by
|
||||
The following variables are available, where appropriate:
|
||||
.Bl -column "XXXXXXXXXXXXXXXXXXX" "XXXXX"
|
||||
.It Sy "Variable name" Ta Sy "Alias" Ta Sy "Replaced with"
|
||||
.It Li "active_window_index" Ta "" Ta "Index of active window in session"
|
||||
.It Li "alternate_on" Ta "" Ta "1 if pane is in alternate screen"
|
||||
.It Li "alternate_saved_x" Ta "" Ta "Saved cursor X in alternate screen"
|
||||
.It Li "alternate_saved_y" Ta "" Ta "Saved cursor Y in alternate screen"
|
||||
@ -4726,9 +4840,9 @@ The following variables are available, where appropriate:
|
||||
.It Li "client_prefix" Ta "" Ta "1 if prefix key has been pressed"
|
||||
.It Li "client_readonly" Ta "" Ta "1 if client is readonly"
|
||||
.It Li "client_session" Ta "" Ta "Name of the client's session"
|
||||
.It Li "client_termfeatures" Ta "" Ta "Terminal features of client, if any"
|
||||
.It Li "client_termname" Ta "" Ta "Terminal name of client"
|
||||
.It Li "client_termtype" Ta "" Ta "Terminal type of client, if available"
|
||||
.It Li "client_termfeatures" Ta "" Ta "Terminal features of client, if any"
|
||||
.It Li "client_tty" Ta "" Ta "Pseudo terminal of client"
|
||||
.It Li "client_utf8" Ta "" Ta "1 if client supports UTF-8"
|
||||
.It Li "client_width" Ta "" Ta "Width of client"
|
||||
@ -4737,6 +4851,7 @@ The following variables are available, where appropriate:
|
||||
.It Li "command_list_alias" Ta "" Ta "Command alias if listing commands"
|
||||
.It Li "command_list_name" Ta "" Ta "Command name if listing commands"
|
||||
.It Li "command_list_usage" Ta "" Ta "Command usage if listing commands"
|
||||
.It Li "config_files" Ta "" Ta "List of configuration files loaded"
|
||||
.It Li "copy_cursor_line" Ta "" Ta "Line the cursor is on in copy mode"
|
||||
.It Li "copy_cursor_word" Ta "" Ta "Word under cursor in copy mode"
|
||||
.It Li "copy_cursor_x" Ta "" Ta "Cursor X position in copy mode"
|
||||
@ -4759,6 +4874,7 @@ The following variables are available, where appropriate:
|
||||
.It Li "insert_flag" Ta "" Ta "Pane insert flag"
|
||||
.It Li "keypad_cursor_flag" Ta "" Ta "Pane keypad cursor flag"
|
||||
.It Li "keypad_flag" Ta "" Ta "Pane keypad flag"
|
||||
.It Li "last_window_index" Ta "" Ta "Index of last window in session"
|
||||
.It Li "line" Ta "" Ta "Line number in the list"
|
||||
.It Li "mouse_all_flag" Ta "" Ta "Pane mouse all flag"
|
||||
.It Li "mouse_any_flag" Ta "" Ta "Pane mouse any flag"
|
||||
@ -4776,11 +4892,13 @@ The following variables are available, where appropriate:
|
||||
.It Li "pane_at_left" Ta "" Ta "1 if pane is at the left of window"
|
||||
.It Li "pane_at_right" Ta "" Ta "1 if pane is at the right of window"
|
||||
.It Li "pane_at_top" Ta "" Ta "1 if pane is at the top of window"
|
||||
.It Li "pane_bg" Ta "" Ta "Pane background colour"
|
||||
.It Li "pane_bottom" Ta "" Ta "Bottom of pane"
|
||||
.It Li "pane_current_command" Ta "" Ta "Current command if available"
|
||||
.It Li "pane_current_path" Ta "" Ta "Current path if available"
|
||||
.It Li "pane_dead" Ta "" Ta "1 if pane is dead"
|
||||
.It Li "pane_dead_status" Ta "" Ta "Exit status of process in dead pane"
|
||||
.It Li "pane_fg" Ta "" Ta "Pane foreground colour"
|
||||
.It Li "pane_format" Ta "" Ta "1 if format is for a pane"
|
||||
.It Li "pane_height" Ta "" Ta "Height of pane"
|
||||
.It Li "pane_id" Ta "#D" Ta "Unique pane ID"
|
||||
@ -4797,7 +4915,6 @@ The following variables are available, where appropriate:
|
||||
.It Li "pane_pipe" Ta "" Ta "1 if pane is being piped"
|
||||
.It Li "pane_right" Ta "" Ta "Right of pane"
|
||||
.It Li "pane_search_string" Ta "" Ta "Last search string in copy mode"
|
||||
.It Li "pane_skipped" Ta "" Ta "Bytes skipped as not visible in pane"
|
||||
.It Li "pane_start_command" Ta "" Ta "Command pane started with"
|
||||
.It Li "pane_synchronized" Ta "" Ta "1 if pane is synchronized"
|
||||
.It Li "pane_tabs" Ta "" Ta "Pane tab positions"
|
||||
@ -4805,17 +4922,13 @@ The following variables are available, where appropriate:
|
||||
.It Li "pane_top" Ta "" Ta "Top of pane"
|
||||
.It Li "pane_tty" Ta "" Ta "Pseudo terminal of pane"
|
||||
.It Li "pane_width" Ta "" Ta "Width of pane"
|
||||
.It Li "pane_written" Ta "" Ta "Bytes written by pane (aside from redrawing)"
|
||||
.It Li "pid" Ta "" Ta "Server PID"
|
||||
.It Li "popup_key" Ta "" Ta "Key pressed in popup"
|
||||
.It Li "popup_mouse_x" Ta "" Ta "Mouse X position in popup"
|
||||
.It Li "popup_mouse_y" Ta "" Ta "Mouse Y position in popup"
|
||||
.It Li "rectangle_toggle" Ta "" Ta "1 if rectangle selection is activated"
|
||||
.It Li "scroll_position" Ta "" Ta "Scroll position in copy mode"
|
||||
.It Li "scroll_region_lower" Ta "" Ta "Bottom of scroll region in pane"
|
||||
.It Li "scroll_region_upper" Ta "" Ta "Top of scroll region in pane"
|
||||
.It Li "search_present" Ta "" Ta "1 if search started in copy mode"
|
||||
.It Li "search_match" Ta "" Ta "Search match if any"
|
||||
.It Li "search_present" Ta "" Ta "1 if search started in copy mode"
|
||||
.It Li "selection_active" Ta "" Ta "1 if selection started and changes with the cursor in copy mode"
|
||||
.It Li "selection_end_x" Ta "" Ta "X position of the end of the selection"
|
||||
.It Li "selection_end_y" Ta "" Ta "Y position of the end of the selection"
|
||||
@ -4858,7 +4971,8 @@ The following variables are available, where appropriate:
|
||||
.It Li "window_cell_height" Ta "" Ta "Height of each cell in pixels"
|
||||
.It Li "window_cell_width" Ta "" Ta "Width of each cell in pixels"
|
||||
.It Li "window_end_flag" Ta "" Ta "1 if window has the highest index"
|
||||
.It Li "window_flags" Ta "#F" Ta "Window flags"
|
||||
.It Li "window_flags" Ta "#F" Ta "Window flags with # escaped as ##"
|
||||
.It Li "window_raw_flags" Ta "" Ta "Window flags with nothing escaped"
|
||||
.It Li "window_format" Ta "" Ta "1 if format is for a window"
|
||||
.It Li "window_height" Ta "" Ta "Height of window"
|
||||
.It Li "window_id" Ta "" Ta "Unique window ID"
|
||||
@ -5386,6 +5500,28 @@ Both may be a row or column number, or one of the following special values:
|
||||
.It Li "S" Ta Fl y Ta "The line above or below the status line"
|
||||
.El
|
||||
.Pp
|
||||
Or a format, which is expanded including the following additional variables:
|
||||
.Bl -column "XXXXXXXXXXXXXXXXXXXXXXXXXX" -offset indent
|
||||
.It Sy "Variable name" Ta Sy "Replaced with"
|
||||
.It Li "popup_centre_x" Ta "Centered in the client"
|
||||
.It Li "popup_centre_y" Ta "Centered in the client"
|
||||
.It Li "popup_height" Ta "Height of menu or popup"
|
||||
.It Li "popup_mouse_bottom" Ta "Bottom of at the mouse"
|
||||
.It Li "popup_mouse_centre_x" Ta "Horizontal centre at the mouse"
|
||||
.It Li "popup_mouse_centre_y" Ta "Vertical centre at the mouse"
|
||||
.It Li "popup_mouse_top" Ta "Top at the mouse"
|
||||
.It Li "popup_mouse_x" Ta "Mouse X position"
|
||||
.It Li "popup_mouse_y" Ta "Mouse Y position"
|
||||
.It Li "popup_pane_bottom" Ta "Bottom of the pane"
|
||||
.It Li "popup_pane_left" Ta "Left of the pane"
|
||||
.It Li "popup_pane_right" Ta "Right of the pane"
|
||||
.It Li "popup_pane_top" Ta "Top of the pane"
|
||||
.It Li "popup_status_line_y" Ta "Above or below the status line"
|
||||
.It Li "popup_width" Ta "Width of menu or popup"
|
||||
.It Li "popup_window_status_line_x" Ta "At the window position in status line"
|
||||
.It Li "popup_window_status_line_y" Ta "At the status line showing the window"
|
||||
.El
|
||||
.Pp
|
||||
Each menu consists of items followed by a key shortcut shown in brackets.
|
||||
If the menu is too large to fit on the terminal, it is not displayed.
|
||||
Pressing the key shortcut chooses the corresponding item.
|
||||
@ -5445,58 +5581,24 @@ lists the format variables and their values.
|
||||
forwards any input read from stdin to the empty pane given by
|
||||
.Ar target-pane .
|
||||
.It Xo Ic display-popup
|
||||
.Op Fl CEK
|
||||
.Op Fl CE
|
||||
.Op Fl c Ar target-client
|
||||
.Op Fl d Ar start-directory
|
||||
.Op Fl h Ar height
|
||||
.Op Fl R Ar shell-command
|
||||
.Op Fl t Ar target-pane
|
||||
.Op Fl w Ar width
|
||||
.Op Fl x Ar position
|
||||
.Op Fl y Ar position
|
||||
.Op Ar command Ar line Ar ...
|
||||
.Op Ar shell-command
|
||||
.Xc
|
||||
.D1 (alias: Ic popup )
|
||||
Display a popup on
|
||||
Display a popup running
|
||||
.Ar shell-command
|
||||
on
|
||||
.Ar target-client .
|
||||
A popup is a rectangular box drawn over the top of any panes.
|
||||
Panes are not updated while a popup is present.
|
||||
The popup content may be given in two ways:
|
||||
.Bl -enum -offset Ds
|
||||
.It
|
||||
A set of lines as arguments.
|
||||
Each line is a format which is expanded using
|
||||
.Ar target-pane
|
||||
as the target.
|
||||
If a line contains newlines it is split into multiple lines.
|
||||
Lines may use styles, see the
|
||||
.Sx STYLES
|
||||
section.
|
||||
.It
|
||||
A shell command given by
|
||||
.Fl R
|
||||
which is run and any output shown in the pane.
|
||||
.El
|
||||
.Pp
|
||||
The first argument,
|
||||
.Ar command ,
|
||||
is a
|
||||
.Nm
|
||||
command which is run when a key is pressed.
|
||||
The key is available in the
|
||||
.Ql popup_key
|
||||
format.
|
||||
After
|
||||
.Ar command
|
||||
is run, the popup is closed.
|
||||
It may be empty to discard any key presses.
|
||||
If
|
||||
.Fl K
|
||||
is given together with
|
||||
.Fl R ,
|
||||
key presses are instead passed to the
|
||||
.Fl R
|
||||
shell command.
|
||||
.Fl E
|
||||
closes the popup automatically when
|
||||
.Ar shell-command
|
||||
@ -5506,14 +5608,6 @@ Two
|
||||
closes the popup only if
|
||||
.Ar shell-command
|
||||
exited with success.
|
||||
With
|
||||
.Fl K ,
|
||||
.Ql Escape
|
||||
and
|
||||
.Ql C-c
|
||||
close the popup unless
|
||||
.Fl E
|
||||
is also given.
|
||||
.Pp
|
||||
.Fl x
|
||||
and
|
||||
@ -5526,11 +5620,7 @@ and
|
||||
.Fl h
|
||||
give the width and height - both may be a percentage (followed by
|
||||
.Ql % ) .
|
||||
If omitted, without
|
||||
.Fl R
|
||||
they are calculated from the given lines and with
|
||||
.Fl R
|
||||
they use half the terminal size.
|
||||
If omitted, half of the terminal size is used.
|
||||
.Pp
|
||||
The
|
||||
.Fl C
|
||||
@ -5789,7 +5879,7 @@ Lock each client individually by running the command specified by the
|
||||
.Ic lock-command
|
||||
option.
|
||||
.It Xo Ic run-shell
|
||||
.Op Fl b
|
||||
.Op Fl bC
|
||||
.Op Fl d Ar delay
|
||||
.Op Fl t Ar target-pane
|
||||
.Op Ar shell-command
|
||||
@ -5797,9 +5887,14 @@ option.
|
||||
.D1 (alias: Ic run )
|
||||
Execute
|
||||
.Ar shell-command
|
||||
in the background without creating a window.
|
||||
Before being executed, shell-command is expanded using the rules specified in
|
||||
the
|
||||
or (with
|
||||
.Fl C )
|
||||
a
|
||||
.Nm
|
||||
command in the background without creating a window.
|
||||
Before being executed,
|
||||
.Ar shell-command
|
||||
is expanded using the rules specified in the
|
||||
.Sx FORMATS
|
||||
section.
|
||||
With
|
||||
@ -5809,11 +5904,13 @@ the command is run in the background.
|
||||
waits for
|
||||
.Ar delay
|
||||
seconds before starting the command.
|
||||
After the command finishes, any output to stdout is displayed in view mode (in
|
||||
the pane specified by
|
||||
If
|
||||
.Fl C
|
||||
is not given, any output to stdout is displayed in view mode (in the pane
|
||||
specified by
|
||||
.Fl t
|
||||
or the current pane if omitted).
|
||||
If the command doesn't return success, the exit status is also displayed.
|
||||
or the current pane if omitted) after the command finishes.
|
||||
If the command fails, the exit status is also displayed.
|
||||
.It Xo Ic wait-for
|
||||
.Op Fl L | S | U
|
||||
.Ar channel
|
||||
@ -5876,6 +5973,10 @@ option should be used.
|
||||
An existing extension that tells
|
||||
.Nm
|
||||
the terminal supports default colours.
|
||||
.It Em \&Bidi
|
||||
Tell
|
||||
.Nm
|
||||
that the terminal supports the VTE bidirectional text extensions.
|
||||
.It Em \&Cs , Cr
|
||||
Set the cursor colour.
|
||||
The first takes a single string argument and is used to set the colour;
|
||||
|
57
tmux.c
57
tmux.c
@ -21,7 +21,6 @@
|
||||
#include <sys/utsname.h>
|
||||
|
||||
#include <errno.h>
|
||||
#include <event.h>
|
||||
#include <fcntl.h>
|
||||
#include <langinfo.h>
|
||||
#include <locale.h>
|
||||
@ -54,7 +53,7 @@ static __dead void
|
||||
usage(void)
|
||||
{
|
||||
fprintf(stderr,
|
||||
"usage: %s [-2CDluvV] [-c shell-command] [-f file] [-L socket-name]\n"
|
||||
"usage: %s [-2CDlNuvV] [-c shell-command] [-f file] [-L socket-name]\n"
|
||||
" [-S socket-path] [-T features] [command [flags]]\n",
|
||||
getprogname());
|
||||
exit(1);
|
||||
@ -139,11 +138,12 @@ expand_path(const char *path, const char *home)
|
||||
return (xstrdup(path));
|
||||
}
|
||||
|
||||
void
|
||||
expand_paths(const char *s, char ***paths, u_int *n)
|
||||
static void
|
||||
expand_paths(const char *s, char ***paths, u_int *n, int ignore_errors)
|
||||
{
|
||||
const char *home = find_home();
|
||||
char *copy, *next, *tmp, resolved[PATH_MAX], *expanded;
|
||||
char *path;
|
||||
u_int i;
|
||||
|
||||
*paths = NULL;
|
||||
@ -159,20 +159,26 @@ expand_paths(const char *s, char ***paths, u_int *n)
|
||||
if (realpath(expanded, resolved) == NULL) {
|
||||
log_debug("%s: realpath(\"%s\") failed: %s", __func__,
|
||||
expanded, strerror(errno));
|
||||
if (ignore_errors) {
|
||||
free(expanded);
|
||||
continue;
|
||||
}
|
||||
path = expanded;
|
||||
} else {
|
||||
path = xstrdup(resolved);
|
||||
free(expanded);
|
||||
continue;
|
||||
}
|
||||
free(expanded);
|
||||
for (i = 0; i < *n; i++) {
|
||||
if (strcmp(resolved, (*paths)[i]) == 0)
|
||||
if (strcmp(path, (*paths)[i]) == 0)
|
||||
break;
|
||||
}
|
||||
if (i != *n) {
|
||||
log_debug("%s: duplicate path: %s", __func__, resolved);
|
||||
log_debug("%s: duplicate path: %s", __func__, path);
|
||||
free(path);
|
||||
continue;
|
||||
}
|
||||
*paths = xreallocarray(*paths, (*n) + 1, sizeof *paths);
|
||||
(*paths)[(*n)++] = xstrdup(resolved);
|
||||
(*paths)[(*n)++] = path;
|
||||
}
|
||||
free(copy);
|
||||
}
|
||||
@ -190,7 +196,7 @@ make_label(const char *label, char **cause)
|
||||
label = "default";
|
||||
uid = getuid();
|
||||
|
||||
expand_paths(TMUX_SOCK, &paths, &n);
|
||||
expand_paths(TMUX_SOCK, &paths, &n, 1);
|
||||
if (n == 0) {
|
||||
xasprintf(cause, "no suitable socket path");
|
||||
return (NULL);
|
||||
@ -321,10 +327,11 @@ main(int argc, char **argv)
|
||||
{
|
||||
char *path = NULL, *label = NULL;
|
||||
char *cause, **var;
|
||||
const char *s, *shell, *cwd;
|
||||
const char *s, *cwd;
|
||||
int opt, keys, feat = 0;
|
||||
uint64_t flags = 0;
|
||||
const struct options_table_entry *oe;
|
||||
u_int i;
|
||||
|
||||
if (setlocale(LC_CTYPE, "en_US.UTF-8") == NULL &&
|
||||
setlocale(LC_CTYPE, "C.UTF-8") == NULL) {
|
||||
@ -341,7 +348,14 @@ main(int argc, char **argv)
|
||||
if (**argv == '-')
|
||||
flags = CLIENT_LOGIN;
|
||||
|
||||
while ((opt = getopt(argc, argv, "2c:CDdf:lL:qS:T:uUvV")) != -1) {
|
||||
global_environ = environ_create();
|
||||
for (var = environ; *var != NULL; var++)
|
||||
environ_put(global_environ, *var, 0);
|
||||
if ((cwd = find_cwd()) != NULL)
|
||||
environ_set(global_environ, "PWD", 0, "%s", cwd);
|
||||
expand_paths(TMUX_CONF, &cfg_files, &cfg_nfiles, 1);
|
||||
|
||||
while ((opt = getopt(argc, argv, "2c:CDdf:lL:NqS:T:uUvV")) != -1) {
|
||||
switch (opt) {
|
||||
case '2':
|
||||
tty_add_features(&feat, "256", ":,");
|
||||
@ -359,7 +373,11 @@ main(int argc, char **argv)
|
||||
flags |= CLIENT_CONTROL;
|
||||
break;
|
||||
case 'f':
|
||||
set_cfg_file(optarg);
|
||||
for (i = 0; i < cfg_nfiles; i++)
|
||||
free(cfg_files[i]);
|
||||
free(cfg_files);
|
||||
expand_paths(optarg, &cfg_files, &cfg_nfiles, 0);
|
||||
cfg_quiet = 0;
|
||||
break;
|
||||
case 'V':
|
||||
printf("%s %s\n", getprogname(), getversion());
|
||||
@ -371,6 +389,9 @@ main(int argc, char **argv)
|
||||
free(label);
|
||||
label = xstrdup(optarg);
|
||||
break;
|
||||
case 'N':
|
||||
flags |= CLIENT_NOSTARTSERVER;
|
||||
break;
|
||||
case 'q':
|
||||
break;
|
||||
case 'S':
|
||||
@ -426,12 +447,6 @@ main(int argc, char **argv)
|
||||
flags |= CLIENT_UTF8;
|
||||
}
|
||||
|
||||
global_environ = environ_create();
|
||||
for (var = environ; *var != NULL; var++)
|
||||
environ_put(global_environ, *var, 0);
|
||||
if ((cwd = find_cwd()) != NULL)
|
||||
environ_set(global_environ, "PWD", 0, "%s", cwd);
|
||||
|
||||
global_options = options_create(NULL);
|
||||
global_s_options = options_create(NULL);
|
||||
global_w_options = options_create(NULL);
|
||||
@ -448,8 +463,8 @@ main(int argc, char **argv)
|
||||
* The default shell comes from SHELL or from the user's passwd entry
|
||||
* if available.
|
||||
*/
|
||||
shell = getshell();
|
||||
options_set_string(global_s_options, "default-shell", 0, "%s", shell);
|
||||
options_set_string(global_s_options, "default-shell", 0, "%s",
|
||||
getshell());
|
||||
|
||||
/* Override keys to vi if VISUAL or EDITOR are set. */
|
||||
if ((s = getenv("VISUAL")) != NULL || (s = getenv("EDITOR")) != NULL) {
|
||||
|
156
tmux.h
156
tmux.h
@ -22,7 +22,6 @@
|
||||
#include <sys/time.h>
|
||||
#include <sys/uio.h>
|
||||
|
||||
#include <event.h>
|
||||
#include <limits.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdio.h>
|
||||
@ -57,8 +56,8 @@ struct mouse_event;
|
||||
struct options;
|
||||
struct options_array_item;
|
||||
struct options_entry;
|
||||
struct screen_write_collect_item;
|
||||
struct screen_write_collect_line;
|
||||
struct screen_write_citem;
|
||||
struct screen_write_cline;
|
||||
struct screen_write_ctx;
|
||||
struct session;
|
||||
struct tty_ctx;
|
||||
@ -263,6 +262,7 @@ enum tty_code_code {
|
||||
TTYC_AX,
|
||||
TTYC_BCE,
|
||||
TTYC_BEL,
|
||||
TTYC_BIDI,
|
||||
TTYC_BLINK,
|
||||
TTYC_BOLD,
|
||||
TTYC_CIVIS,
|
||||
@ -501,6 +501,7 @@ enum msgtype {
|
||||
MSG_IDENTIFY_FEATURES,
|
||||
MSG_IDENTIFY_STDOUT,
|
||||
MSG_IDENTIFY_LONGFLAGS,
|
||||
MSG_IDENTIFY_TERMINFO,
|
||||
|
||||
MSG_COMMAND = 200,
|
||||
MSG_DETACH,
|
||||
@ -728,6 +729,13 @@ struct grid {
|
||||
struct grid_line *linedata;
|
||||
};
|
||||
|
||||
/* Virtual cursor in a grid. */
|
||||
struct grid_reader {
|
||||
struct grid *gd;
|
||||
u_int cx;
|
||||
u_int cy;
|
||||
};
|
||||
|
||||
/* Style alignment. */
|
||||
enum style_align {
|
||||
STYLE_ALIGN_DEFAULT,
|
||||
@ -789,55 +797,51 @@ struct style {
|
||||
struct screen_sel;
|
||||
struct screen_titles;
|
||||
struct screen {
|
||||
char *title;
|
||||
char *path;
|
||||
struct screen_titles *titles;
|
||||
char *title;
|
||||
char *path;
|
||||
struct screen_titles *titles;
|
||||
|
||||
struct grid *grid; /* grid data */
|
||||
struct grid *grid; /* grid data */
|
||||
|
||||
u_int cx; /* cursor x */
|
||||
u_int cy; /* cursor y */
|
||||
u_int cx; /* cursor x */
|
||||
u_int cy; /* cursor y */
|
||||
|
||||
u_int cstyle; /* cursor style */
|
||||
char *ccolour; /* cursor colour string */
|
||||
u_int cstyle; /* cursor style */
|
||||
char *ccolour; /* cursor colour string */
|
||||
|
||||
u_int rupper; /* scroll region top */
|
||||
u_int rlower; /* scroll region bottom */
|
||||
u_int rupper; /* scroll region top */
|
||||
u_int rlower; /* scroll region bottom */
|
||||
|
||||
int mode;
|
||||
int mode;
|
||||
|
||||
u_int saved_cx;
|
||||
u_int saved_cy;
|
||||
struct grid *saved_grid;
|
||||
struct grid_cell saved_cell;
|
||||
int saved_flags;
|
||||
u_int saved_cx;
|
||||
u_int saved_cy;
|
||||
struct grid *saved_grid;
|
||||
struct grid_cell saved_cell;
|
||||
int saved_flags;
|
||||
|
||||
bitstr_t *tabs;
|
||||
struct screen_sel *sel;
|
||||
bitstr_t *tabs;
|
||||
struct screen_sel *sel;
|
||||
|
||||
struct screen_write_collect_line *write_list;
|
||||
struct screen_write_cline *write_list;
|
||||
};
|
||||
|
||||
/* Screen write context. */
|
||||
typedef void (*screen_write_init_ctx_cb)(struct screen_write_ctx *,
|
||||
struct tty_ctx *);
|
||||
struct screen_write_ctx {
|
||||
struct window_pane *wp;
|
||||
struct screen *s;
|
||||
struct window_pane *wp;
|
||||
struct screen *s;
|
||||
|
||||
int flags;
|
||||
int flags;
|
||||
#define SCREEN_WRITE_SYNC 0x1
|
||||
|
||||
screen_write_init_ctx_cb init_ctx_cb;
|
||||
void *arg;
|
||||
screen_write_init_ctx_cb init_ctx_cb;
|
||||
void *arg;
|
||||
|
||||
struct screen_write_collect_item *item;
|
||||
u_int scrolled;
|
||||
u_int bg;
|
||||
|
||||
u_int cells;
|
||||
u_int written;
|
||||
u_int skipped;
|
||||
struct screen_write_citem *item;
|
||||
u_int scrolled;
|
||||
u_int bg;
|
||||
};
|
||||
|
||||
/* Screen redraw context. */
|
||||
@ -889,6 +893,7 @@ struct window_mode {
|
||||
struct cmd_find_state *, struct args *);
|
||||
void (*free)(struct window_mode_entry *);
|
||||
void (*resize)(struct window_mode_entry *, u_int, u_int);
|
||||
void (*update)(struct window_mode_entry *);
|
||||
void (*key)(struct window_mode_entry *, struct client *,
|
||||
struct session *, struct winlink *, key_code,
|
||||
struct mouse_event *);
|
||||
@ -995,9 +1000,6 @@ struct window_pane {
|
||||
char *searchstr;
|
||||
int searchregex;
|
||||
|
||||
size_t written;
|
||||
size_t skipped;
|
||||
|
||||
int border_gc_set;
|
||||
struct grid_cell border_gc;
|
||||
|
||||
@ -1539,6 +1541,8 @@ typedef void (*client_file_cb) (struct client *, const char *, int, int,
|
||||
struct evbuffer *, void *);
|
||||
struct client_file {
|
||||
struct client *c;
|
||||
struct tmuxpeer *peer;
|
||||
struct client_files *tree;
|
||||
int references;
|
||||
int stream;
|
||||
|
||||
@ -1601,6 +1605,8 @@ struct client {
|
||||
char *term_name;
|
||||
int term_features;
|
||||
char *term_type;
|
||||
char **term_caps;
|
||||
u_int term_ncaps;
|
||||
|
||||
char *ttyname;
|
||||
struct tty tty;
|
||||
@ -1629,7 +1635,7 @@ struct client {
|
||||
#define CLIENT_DEAD 0x200
|
||||
#define CLIENT_REDRAWBORDERS 0x400
|
||||
#define CLIENT_READONLY 0x800
|
||||
/* 0x1000 unused */
|
||||
#define CLIENT_NOSTARTSERVER 0x1000
|
||||
#define CLIENT_CONTROL 0x2000
|
||||
#define CLIENT_CONTROLCONTROL 0x4000
|
||||
#define CLIENT_FOCUSED 0x8000
|
||||
@ -1687,6 +1693,7 @@ struct client {
|
||||
|
||||
char *prompt_string;
|
||||
struct utf8_data *prompt_buffer;
|
||||
char *prompt_last;
|
||||
size_t prompt_index;
|
||||
prompt_input_cb prompt_inputcb;
|
||||
prompt_free_cb prompt_freecb;
|
||||
@ -1881,8 +1888,6 @@ const char *sig2name(int);
|
||||
const char *find_cwd(void);
|
||||
const char *find_home(void);
|
||||
const char *getversion(void);
|
||||
void expand_paths(const char *, char ***, u_int *);
|
||||
|
||||
|
||||
/* proc.c */
|
||||
struct imsg;
|
||||
@ -1897,16 +1902,19 @@ struct tmuxpeer *proc_add_peer(struct tmuxproc *, int,
|
||||
void proc_remove_peer(struct tmuxpeer *);
|
||||
void proc_kill_peer(struct tmuxpeer *);
|
||||
void proc_toggle_log(struct tmuxproc *);
|
||||
pid_t proc_fork_and_daemon(int *);
|
||||
|
||||
/* cfg.c */
|
||||
extern int cfg_finished;
|
||||
extern struct client *cfg_client;
|
||||
extern char **cfg_files;
|
||||
extern u_int cfg_nfiles;
|
||||
extern int cfg_quiet;
|
||||
void start_cfg(void);
|
||||
int load_cfg(const char *, struct client *, struct cmdq_item *, int,
|
||||
struct cmdq_item **);
|
||||
int load_cfg_from_buffer(const void *, size_t, const char *,
|
||||
struct client *, struct cmdq_item *, int, struct cmdq_item **);
|
||||
void set_cfg_file(const char *);
|
||||
void printflike(1, 2) cfg_add_cause(const char *, ...);
|
||||
void cfg_print_causes(struct cmdq_item *);
|
||||
void cfg_show_causes(struct session *);
|
||||
@ -1937,7 +1945,7 @@ char *paste_make_sample(struct paste_buffer *);
|
||||
#define FORMAT_WINDOW 0x40000000U
|
||||
struct format_tree;
|
||||
struct format_modifier;
|
||||
typedef char *(*format_cb)(struct format_tree *);
|
||||
typedef void *(*format_cb)(struct format_tree *);
|
||||
const char *format_skip(const char *, const char *);
|
||||
int format_true(const char *);
|
||||
struct format_tree *format_create(struct client *, struct cmdq_item *, int,
|
||||
@ -2060,9 +2068,9 @@ typedef void (*job_free_cb) (void *);
|
||||
#define JOB_NOWAIT 0x1
|
||||
#define JOB_KEEPWRITE 0x2
|
||||
#define JOB_PTY 0x4
|
||||
struct job *job_run(const char *, struct session *, const char *,
|
||||
job_update_cb, job_complete_cb, job_free_cb, void *, int,
|
||||
int, int);
|
||||
struct job *job_run(const char *, int, char **, struct session *,
|
||||
const char *, job_update_cb, job_complete_cb, job_free_cb,
|
||||
void *, int, int, int);
|
||||
void job_free(struct job *);
|
||||
void job_resize(struct job *, u_int, u_int);
|
||||
void job_check_died(pid_t, int);
|
||||
@ -2163,8 +2171,12 @@ extern struct tty_terms tty_terms;
|
||||
u_int tty_term_ncodes(void);
|
||||
void tty_term_apply(struct tty_term *, const char *, int);
|
||||
void tty_term_apply_overrides(struct tty_term *);
|
||||
struct tty_term *tty_term_create(struct tty *, char *, int *, int, char **);
|
||||
struct tty_term *tty_term_create(struct tty *, char *, char **, u_int, int *,
|
||||
char **);
|
||||
void tty_term_free(struct tty_term *);
|
||||
int tty_term_read_list(const char *, int, char ***, u_int *,
|
||||
char **);
|
||||
void tty_term_free_list(char **, u_int);
|
||||
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);
|
||||
@ -2370,7 +2382,10 @@ void alerts_check_session(struct session *);
|
||||
/* file.c */
|
||||
int file_cmp(struct client_file *, struct client_file *);
|
||||
RB_PROTOTYPE(client_files, client_file, entry, file_cmp);
|
||||
struct client_file *file_create(struct client *, int, client_file_cb, void *);
|
||||
struct client_file *file_create_with_peer(struct tmuxpeer *,
|
||||
struct client_files *, int, client_file_cb, void *);
|
||||
struct client_file *file_create_with_client(struct client *, int,
|
||||
client_file_cb, void *);
|
||||
void file_free(struct client_file *);
|
||||
void file_fire_done(struct client_file *);
|
||||
void file_fire_read(struct client_file *);
|
||||
@ -2383,6 +2398,16 @@ void file_write(struct client *, const char *, int, const void *, size_t,
|
||||
client_file_cb, void *);
|
||||
void file_read(struct client *, const char *, client_file_cb, void *);
|
||||
void file_push(struct client_file *);
|
||||
int file_write_left(struct client_files *);
|
||||
void file_write_open(struct client_files *, struct tmuxpeer *,
|
||||
struct imsg *, int, int, client_file_cb, void *);
|
||||
void file_write_data(struct client_files *, struct imsg *);
|
||||
void file_write_close(struct client_files *, struct imsg *);
|
||||
void file_read_open(struct client_files *, struct tmuxpeer *, struct imsg *,
|
||||
int, int, client_file_cb, void *);
|
||||
void file_write_ready(struct client_files *, struct imsg *);
|
||||
void file_read_data(struct client_files *, struct imsg *);
|
||||
void file_read_done(struct client_files *, struct imsg *);
|
||||
|
||||
/* server.c */
|
||||
extern struct tmuxproc *server_proc;
|
||||
@ -2509,6 +2534,7 @@ const char *colour_tostring(int);
|
||||
int colour_fromstring(const char *s);
|
||||
int colour_256toRGB(int);
|
||||
int colour_256to16(int);
|
||||
int colour_byname(const char *);
|
||||
|
||||
/* attributes.c */
|
||||
const char *attributes_tostring(int);
|
||||
@ -2549,6 +2575,26 @@ void grid_wrap_position(struct grid *, u_int, u_int, u_int *, u_int *);
|
||||
void grid_unwrap_position(struct grid *, u_int *, u_int *, u_int, u_int);
|
||||
u_int grid_line_length(struct grid *, u_int);
|
||||
|
||||
/* grid-reader.c */
|
||||
void grid_reader_start(struct grid_reader *, struct grid *, u_int, u_int);
|
||||
void grid_reader_get_cursor(struct grid_reader *, u_int *, u_int *);
|
||||
u_int grid_reader_line_length(struct grid_reader *);
|
||||
int grid_reader_in_set(struct grid_reader *, const char *);
|
||||
void grid_reader_cursor_right(struct grid_reader *, int, int);
|
||||
void grid_reader_cursor_left(struct grid_reader *);
|
||||
void grid_reader_cursor_down(struct grid_reader *);
|
||||
void grid_reader_cursor_up(struct grid_reader *);
|
||||
void grid_reader_cursor_start_of_line(struct grid_reader *, int);
|
||||
void grid_reader_cursor_end_of_line(struct grid_reader *, int, int);
|
||||
void grid_reader_cursor_next_word(struct grid_reader *, const char *);
|
||||
void grid_reader_cursor_next_word_end(struct grid_reader *, const char *);
|
||||
void grid_reader_cursor_previous_word(struct grid_reader *, const char *,
|
||||
int);
|
||||
int grid_reader_cursor_jump(struct grid_reader *,
|
||||
const struct utf8_data *);
|
||||
int grid_reader_cursor_jump_back(struct grid_reader *,
|
||||
const struct utf8_data *);
|
||||
|
||||
/* grid-view.c */
|
||||
void grid_view_get_cell(struct grid *, u_int, u_int, struct grid_cell *);
|
||||
void grid_view_set_cell(struct grid *, u_int, u_int,
|
||||
@ -2668,7 +2714,6 @@ void screen_alternate_off(struct screen *, struct grid_cell *, int);
|
||||
/* window.c */
|
||||
extern struct windows windows;
|
||||
extern struct window_pane_tree all_window_panes;
|
||||
extern const struct window_mode *all_window_modes[];
|
||||
int window_cmp(struct window *, struct window *);
|
||||
RB_PROTOTYPE(windows, window, entry, window_cmp);
|
||||
int winlink_cmp(struct winlink *, struct winlink *);
|
||||
@ -2739,7 +2784,7 @@ int window_pane_key(struct window_pane *, struct client *,
|
||||
int window_pane_visible(struct window_pane *);
|
||||
u_int window_pane_search(struct window_pane *, const char *, int,
|
||||
int);
|
||||
const char *window_printable_flags(struct winlink *);
|
||||
const char *window_printable_flags(struct winlink *, int);
|
||||
struct window_pane *window_pane_find_up(struct window_pane *);
|
||||
struct window_pane *window_pane_find_down(struct window_pane *);
|
||||
struct window_pane *window_pane_find_left(struct window_pane *);
|
||||
@ -2995,18 +3040,13 @@ int menu_display(struct menu *, int, struct cmdq_item *, u_int,
|
||||
menu_choice_cb, void *);
|
||||
|
||||
/* popup.c */
|
||||
#define POPUP_WRITEKEYS 0x1
|
||||
#define POPUP_CLOSEEXIT 0x2
|
||||
#define POPUP_CLOSEEXITZERO 0x4
|
||||
#define POPUP_CLOSEEXIT 0x1
|
||||
#define POPUP_CLOSEEXITZERO 0x2
|
||||
typedef void (*popup_close_cb)(int, void *);
|
||||
typedef void (*popup_finish_edit_cb)(char *, size_t, void *);
|
||||
u_int popup_width(struct cmdq_item *, u_int, const char **,
|
||||
struct client *, struct cmd_find_state *);
|
||||
u_int popup_height(u_int, const char **);
|
||||
int popup_display(int, struct cmdq_item *, u_int, u_int, u_int,
|
||||
u_int, u_int, const char **, const char *, const char *,
|
||||
const char *, struct client *, struct cmd_find_state *,
|
||||
popup_close_cb, void *);
|
||||
u_int, const char *, int, char **, const char *,
|
||||
struct client *, struct session *, popup_close_cb, void *);
|
||||
int popup_editor(struct client *, const char *, size_t,
|
||||
popup_finish_edit_cb, void *);
|
||||
|
||||
|
166
tty-term.c
166
tty-term.c
@ -61,6 +61,7 @@ static const struct tty_term_code_entry tty_term_codes[] = {
|
||||
[TTYC_AX] = { TTYCODE_FLAG, "AX" },
|
||||
[TTYC_BCE] = { TTYCODE_FLAG, "bce" },
|
||||
[TTYC_BEL] = { TTYCODE_STRING, "bel" },
|
||||
[TTYC_BIDI] = { TTYCODE_STRING, "Bidi" },
|
||||
[TTYC_BLINK] = { TTYCODE_STRING, "blink" },
|
||||
[TTYC_BOLD] = { TTYCODE_STRING, "bold" },
|
||||
[TTYC_CIVIS] = { TTYCODE_STRING, "civis" },
|
||||
@ -452,7 +453,8 @@ tty_term_apply_overrides(struct tty_term *term)
|
||||
}
|
||||
|
||||
struct tty_term *
|
||||
tty_term_create(struct tty *tty, char *name, int *feat, int fd, char **cause)
|
||||
tty_term_create(struct tty *tty, char *name, char **caps, u_int ncaps,
|
||||
int *feat, char **cause)
|
||||
{
|
||||
struct tty_term *term;
|
||||
const struct tty_term_code_entry *ent;
|
||||
@ -460,10 +462,9 @@ tty_term_create(struct tty *tty, char *name, int *feat, int fd, char **cause)
|
||||
struct options_entry *o;
|
||||
struct options_array_item *a;
|
||||
union options_value *ov;
|
||||
u_int i;
|
||||
int n, error;
|
||||
const char *s, *acs;
|
||||
size_t offset;
|
||||
u_int i, j;
|
||||
const char *s, *acs, *value;
|
||||
size_t offset, namelen;
|
||||
char *first;
|
||||
|
||||
log_debug("adding term %s", name);
|
||||
@ -474,57 +475,38 @@ tty_term_create(struct tty *tty, char *name, int *feat, int fd, char **cause)
|
||||
term->codes = xcalloc(tty_term_ncodes(), sizeof *term->codes);
|
||||
LIST_INSERT_HEAD(&tty_terms, term, entry);
|
||||
|
||||
/* Set up curses terminal. */
|
||||
if (setupterm(name, fd, &error) != OK) {
|
||||
switch (error) {
|
||||
case 1:
|
||||
xasprintf(cause, "can't use hardcopy terminal: %s",
|
||||
name);
|
||||
break;
|
||||
case 0:
|
||||
xasprintf(cause, "missing or unsuitable terminal: %s",
|
||||
name);
|
||||
break;
|
||||
case -1:
|
||||
xasprintf(cause, "can't find terminfo database");
|
||||
break;
|
||||
default:
|
||||
xasprintf(cause, "unknown error");
|
||||
break;
|
||||
}
|
||||
goto error;
|
||||
}
|
||||
|
||||
/* Fill in codes. */
|
||||
for (i = 0; i < tty_term_ncodes(); i++) {
|
||||
ent = &tty_term_codes[i];
|
||||
for (i = 0; i < ncaps; i++) {
|
||||
namelen = strcspn(caps[i], "=");
|
||||
if (namelen == 0)
|
||||
continue;
|
||||
value = caps[i] + namelen + 1;
|
||||
|
||||
code = &term->codes[i];
|
||||
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)
|
||||
for (j = 0; j < tty_term_ncodes(); j++) {
|
||||
ent = &tty_term_codes[j];
|
||||
if (strncmp(ent->name, caps[i], namelen) != 0)
|
||||
continue;
|
||||
if (ent->name[namelen] != '\0')
|
||||
continue;
|
||||
|
||||
code = &term->codes[j];
|
||||
code->type = TTYCODE_NONE;
|
||||
switch (ent->type) {
|
||||
case TTYCODE_NONE:
|
||||
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)
|
||||
case TTYCODE_STRING:
|
||||
code->type = TTYCODE_STRING;
|
||||
code->value.string = tty_term_strip(value);
|
||||
break;
|
||||
code->type = TTYCODE_NUMBER;
|
||||
code->value.number = n;
|
||||
break;
|
||||
case TTYCODE_FLAG:
|
||||
n = tigetflag((char *) ent->name);
|
||||
if (n == -1)
|
||||
case TTYCODE_NUMBER:
|
||||
code->type = TTYCODE_NUMBER;
|
||||
code->value.number = atoi(value);
|
||||
break;
|
||||
code->type = TTYCODE_FLAG;
|
||||
code->value.flag = n;
|
||||
break;
|
||||
case TTYCODE_FLAG:
|
||||
code->type = TTYCODE_FLAG;
|
||||
code->value.flag = (*value == '1');
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -648,6 +630,88 @@ tty_term_free(struct tty_term *term)
|
||||
free(term);
|
||||
}
|
||||
|
||||
int
|
||||
tty_term_read_list(const char *name, int fd, char ***caps, u_int *ncaps,
|
||||
char **cause)
|
||||
{
|
||||
const struct tty_term_code_entry *ent;
|
||||
int error, n;
|
||||
u_int i;
|
||||
const char *s;
|
||||
char tmp[11];
|
||||
|
||||
if (setupterm(name, fd, &error) != OK) {
|
||||
switch (error) {
|
||||
case 1:
|
||||
xasprintf(cause, "can't use hardcopy terminal: %s",
|
||||
name);
|
||||
break;
|
||||
case 0:
|
||||
xasprintf(cause, "missing or unsuitable terminal: %s",
|
||||
name);
|
||||
break;
|
||||
case -1:
|
||||
xasprintf(cause, "can't find terminfo database");
|
||||
break;
|
||||
default:
|
||||
xasprintf(cause, "unknown error");
|
||||
break;
|
||||
}
|
||||
return (-1);
|
||||
}
|
||||
|
||||
*ncaps = 0;
|
||||
*caps = NULL;
|
||||
|
||||
for (i = 0; i < tty_term_ncodes(); i++) {
|
||||
ent = &tty_term_codes[i];
|
||||
switch (ent->type) {
|
||||
case TTYCODE_NONE:
|
||||
break;
|
||||
case TTYCODE_STRING:
|
||||
s = tigetstr((char *)ent->name);
|
||||
if (s == NULL || s == (char *)-1)
|
||||
continue;
|
||||
break;
|
||||
case TTYCODE_NUMBER:
|
||||
n = tigetnum((char *)ent->name);
|
||||
if (n == -1 || n == -2)
|
||||
continue;
|
||||
xsnprintf(tmp, sizeof tmp, "%d", n);
|
||||
s = tmp;
|
||||
break;
|
||||
case TTYCODE_FLAG:
|
||||
n = tigetflag((char *) ent->name);
|
||||
if (n == -1)
|
||||
continue;
|
||||
if (n)
|
||||
s = "1";
|
||||
else
|
||||
s = "0";
|
||||
break;
|
||||
}
|
||||
*caps = xreallocarray(*caps, (*ncaps) + 1, sizeof **caps);
|
||||
xasprintf(&(*caps)[*ncaps], "%s=%s", ent->name, s);
|
||||
(*ncaps)++;
|
||||
}
|
||||
|
||||
#if !defined(NCURSES_VERSION_MAJOR) || NCURSES_VERSION_MAJOR > 5 || \
|
||||
(NCURSES_VERSION_MAJOR == 5 && NCURSES_VERSION_MINOR > 6)
|
||||
del_curterm(cur_term);
|
||||
#endif
|
||||
return (0);
|
||||
}
|
||||
|
||||
void
|
||||
tty_term_free_list(char **caps, u_int ncaps)
|
||||
{
|
||||
u_int i;
|
||||
|
||||
for (i = 0; i < ncaps; i++)
|
||||
free(caps[i]);
|
||||
free(caps);
|
||||
}
|
||||
|
||||
int
|
||||
tty_term_has(struct tty_term *term, enum tty_code_code code)
|
||||
{
|
||||
|
47
tty.c
47
tty.c
@ -249,8 +249,8 @@ tty_open(struct tty *tty, char **cause)
|
||||
{
|
||||
struct client *c = tty->client;
|
||||
|
||||
tty->term = tty_term_create(tty, c->term_name, &c->term_features,
|
||||
c->fd, cause);
|
||||
tty->term = tty_term_create(tty, c->term_name, c->term_caps,
|
||||
c->term_ncaps, &c->term_features, cause);
|
||||
if (tty->term == NULL) {
|
||||
tty_close(tty);
|
||||
return (-1);
|
||||
@ -694,28 +694,26 @@ tty_update_mode(struct tty *tty, int mode, struct screen *s)
|
||||
}
|
||||
if ((changed & ALL_MOUSE_MODES) &&
|
||||
tty_term_has(tty->term, TTYC_KMOUS)) {
|
||||
if ((mode & ALL_MOUSE_MODES) == 0)
|
||||
/*
|
||||
* If the mouse modes have changed, clear any that are set and
|
||||
* apply again. There are differences in how terminals track
|
||||
* the various bits.
|
||||
*/
|
||||
if (tty->mode & MODE_MOUSE_SGR)
|
||||
tty_puts(tty, "\033[?1006l");
|
||||
if ((changed & MODE_MOUSE_STANDARD) &&
|
||||
(~mode & MODE_MOUSE_STANDARD))
|
||||
if (tty->mode & MODE_MOUSE_STANDARD)
|
||||
tty_puts(tty, "\033[?1000l");
|
||||
if ((changed & MODE_MOUSE_BUTTON) &&
|
||||
(~mode & MODE_MOUSE_BUTTON))
|
||||
if (tty->mode & MODE_MOUSE_BUTTON)
|
||||
tty_puts(tty, "\033[?1002l");
|
||||
if ((changed & MODE_MOUSE_ALL) &&
|
||||
(~mode & MODE_MOUSE_ALL))
|
||||
if (tty->mode & MODE_MOUSE_ALL)
|
||||
tty_puts(tty, "\033[?1003l");
|
||||
|
||||
if (mode & ALL_MOUSE_MODES)
|
||||
tty_puts(tty, "\033[?1006h");
|
||||
if ((changed & MODE_MOUSE_STANDARD) &&
|
||||
(mode & MODE_MOUSE_STANDARD))
|
||||
if (mode & MODE_MOUSE_STANDARD)
|
||||
tty_puts(tty, "\033[?1000h");
|
||||
if ((changed & MODE_MOUSE_BUTTON) &&
|
||||
(mode & MODE_MOUSE_BUTTON))
|
||||
if (mode & MODE_MOUSE_BUTTON)
|
||||
tty_puts(tty, "\033[?1002h");
|
||||
if ((changed & MODE_MOUSE_ALL) &&
|
||||
(mode & MODE_MOUSE_ALL))
|
||||
if (mode & MODE_MOUSE_ALL)
|
||||
tty_puts(tty, "\033[?1003h");
|
||||
}
|
||||
if (changed & MODE_BRACKETPASTE) {
|
||||
@ -1533,20 +1531,9 @@ tty_cmd_deletecharacter(struct tty *tty, const struct tty_ctx *ctx)
|
||||
void
|
||||
tty_cmd_clearcharacter(struct tty *tty, const struct tty_ctx *ctx)
|
||||
{
|
||||
if (ctx->bigger) {
|
||||
tty_draw_pane(tty, ctx, ctx->ocy);
|
||||
return;
|
||||
}
|
||||
|
||||
tty_default_attributes(tty, &ctx->defaults, ctx->palette, ctx->bg);
|
||||
|
||||
tty_cursor_pane(tty, ctx, ctx->ocx, ctx->ocy);
|
||||
|
||||
if (tty_term_has(tty->term, TTYC_ECH) &&
|
||||
!tty_fake_bce(tty, &ctx->defaults, 8))
|
||||
tty_putcode1(tty, TTYC_ECH, ctx->num);
|
||||
else
|
||||
tty_repeat_space(tty, ctx->num);
|
||||
tty_clear_pane_line(tty, ctx, ctx->ocy, ctx->ocx, ctx->num, ctx->bg);
|
||||
}
|
||||
|
||||
void
|
||||
@ -2449,7 +2436,7 @@ tty_check_fg(struct tty *tty, int *palette, struct grid_cell *gc)
|
||||
/* Is this a 256-colour colour? */
|
||||
if (gc->fg & COLOUR_FLAG_256) {
|
||||
/* And not a 256 colour mode? */
|
||||
if (colours != 256) {
|
||||
if (colours < 256) {
|
||||
gc->fg = colour_256to16(gc->fg);
|
||||
if (gc->fg & 8) {
|
||||
gc->fg &= 7;
|
||||
@ -2502,7 +2489,7 @@ tty_check_bg(struct tty *tty, int *palette, struct grid_cell *gc)
|
||||
* palette. Bold background doesn't exist portably, so just
|
||||
* discard the bold bit if set.
|
||||
*/
|
||||
if (colours != 256) {
|
||||
if (colours < 256) {
|
||||
gc->bg = colour_256to16(gc->bg);
|
||||
if (gc->bg & 8) {
|
||||
gc->bg &= 7;
|
||||
|
8
utf8.c
8
utf8.c
@ -216,7 +216,11 @@ utf8_width(struct utf8_data *ud, int *width)
|
||||
{
|
||||
wchar_t wc;
|
||||
|
||||
#ifdef HAVE_UTF8PROC
|
||||
switch (utf8proc_mbtowc(&wc, ud->data, ud->size)) {
|
||||
#else
|
||||
switch (mbtowc(&wc, ud->data, ud->size)) {
|
||||
#endif
|
||||
case -1:
|
||||
log_debug("UTF-8 %.*s, mbtowc() %d", (int)ud->size, ud->data,
|
||||
errno);
|
||||
@ -225,7 +229,11 @@ utf8_width(struct utf8_data *ud, int *width)
|
||||
case 0:
|
||||
return (UTF8_ERROR);
|
||||
}
|
||||
#ifdef HAVE_UTF8PROC
|
||||
*width = utf8proc_wcwidth(wc);
|
||||
#else
|
||||
*width = wcwidth(wc);
|
||||
#endif
|
||||
if (*width >= 0 && *width <= 0xff)
|
||||
return (UTF8_DONE);
|
||||
log_debug("UTF-8 %.*s, wcwidth() %d", (int)ud->size, ud->data, *width);
|
||||
|
@ -31,6 +31,7 @@ static struct screen *window_buffer_init(struct window_mode_entry *,
|
||||
static void window_buffer_free(struct window_mode_entry *);
|
||||
static void window_buffer_resize(struct window_mode_entry *, u_int,
|
||||
u_int);
|
||||
static void window_buffer_update(struct window_mode_entry *);
|
||||
static void window_buffer_key(struct window_mode_entry *,
|
||||
struct client *, struct session *,
|
||||
struct winlink *, key_code, struct mouse_event *);
|
||||
@ -63,6 +64,7 @@ const struct window_mode window_buffer_mode = {
|
||||
.init = window_buffer_init,
|
||||
.free = window_buffer_free,
|
||||
.resize = window_buffer_resize,
|
||||
.update = window_buffer_update,
|
||||
.key = window_buffer_key,
|
||||
};
|
||||
|
||||
@ -335,6 +337,16 @@ window_buffer_resize(struct window_mode_entry *wme, u_int sx, u_int sy)
|
||||
mode_tree_resize(data->data, sx, sy);
|
||||
}
|
||||
|
||||
static void
|
||||
window_buffer_update(struct window_mode_entry *wme)
|
||||
{
|
||||
struct window_buffer_modedata *data = wme->data;
|
||||
|
||||
mode_tree_build(data->data);
|
||||
mode_tree_draw(data->data);
|
||||
data->wp->flags |= PANE_REDRAW;
|
||||
}
|
||||
|
||||
static void
|
||||
window_buffer_do_delete(void *modedata, void *itemdata,
|
||||
__unused struct client *c, __unused key_code key)
|
||||
|
@ -30,6 +30,7 @@ static struct screen *window_client_init(struct window_mode_entry *,
|
||||
static void window_client_free(struct window_mode_entry *);
|
||||
static void window_client_resize(struct window_mode_entry *, u_int,
|
||||
u_int);
|
||||
static void window_client_update(struct window_mode_entry *);
|
||||
static void window_client_key(struct window_mode_entry *,
|
||||
struct client *, struct session *,
|
||||
struct winlink *, key_code, struct mouse_event *);
|
||||
@ -59,6 +60,7 @@ const struct window_mode window_client_mode = {
|
||||
.init = window_client_init,
|
||||
.free = window_client_free,
|
||||
.resize = window_client_resize,
|
||||
.update = window_client_update,
|
||||
.key = window_client_key,
|
||||
};
|
||||
|
||||
@ -311,6 +313,16 @@ window_client_resize(struct window_mode_entry *wme, u_int sx, u_int sy)
|
||||
mode_tree_resize(data->data, sx, sy);
|
||||
}
|
||||
|
||||
static void
|
||||
window_client_update(struct window_mode_entry *wme)
|
||||
{
|
||||
struct window_client_modedata *data = wme->data;
|
||||
|
||||
mode_tree_build(data->data);
|
||||
mode_tree_draw(data->data);
|
||||
data->wp->flags |= PANE_REDRAW;
|
||||
}
|
||||
|
||||
static void
|
||||
window_client_do_detach(void *modedata, void *itemdata,
|
||||
__unused struct client *c, key_code key)
|
||||
|
763
window-copy.c
763
window-copy.c
File diff suppressed because it is too large
Load Diff
@ -190,13 +190,6 @@ window_customize_scope_text(enum window_customize_scope scope,
|
||||
u_int idx;
|
||||
|
||||
switch (scope) {
|
||||
case WINDOW_CUSTOMIZE_NONE:
|
||||
case WINDOW_CUSTOMIZE_KEY:
|
||||
case WINDOW_CUSTOMIZE_SERVER:
|
||||
case WINDOW_CUSTOMIZE_GLOBAL_SESSION:
|
||||
case WINDOW_CUSTOMIZE_GLOBAL_WINDOW:
|
||||
s = xstrdup("");
|
||||
break;
|
||||
case WINDOW_CUSTOMIZE_PANE:
|
||||
window_pane_index(fs->wp, &idx);
|
||||
xasprintf(&s, "pane %u", idx);
|
||||
@ -207,6 +200,9 @@ window_customize_scope_text(enum window_customize_scope scope,
|
||||
case WINDOW_CUSTOMIZE_WINDOW:
|
||||
xasprintf(&s, "window %u", fs->wl->idx);
|
||||
break;
|
||||
default:
|
||||
s = xstrdup("");
|
||||
break;
|
||||
}
|
||||
return (s);
|
||||
}
|
||||
|
@ -29,6 +29,7 @@ static struct screen *window_tree_init(struct window_mode_entry *,
|
||||
static void window_tree_free(struct window_mode_entry *);
|
||||
static void window_tree_resize(struct window_mode_entry *, u_int,
|
||||
u_int);
|
||||
static void window_tree_update(struct window_mode_entry *);
|
||||
static void window_tree_key(struct window_mode_entry *,
|
||||
struct client *, struct session *,
|
||||
struct winlink *, key_code, struct mouse_event *);
|
||||
@ -79,6 +80,7 @@ const struct window_mode window_tree_mode = {
|
||||
.init = window_tree_init,
|
||||
.free = window_tree_free,
|
||||
.resize = window_tree_resize,
|
||||
.update = window_tree_update,
|
||||
.key = window_tree_key,
|
||||
};
|
||||
|
||||
@ -937,6 +939,16 @@ window_tree_resize(struct window_mode_entry *wme, u_int sx, u_int sy)
|
||||
mode_tree_resize(data->data, sx, sy);
|
||||
}
|
||||
|
||||
static void
|
||||
window_tree_update(struct window_mode_entry *wme)
|
||||
{
|
||||
struct window_tree_modedata *data = wme->data;
|
||||
|
||||
mode_tree_build(data->data);
|
||||
mode_tree_draw(data->data);
|
||||
data->wp->flags |= PANE_REDRAW;
|
||||
}
|
||||
|
||||
static char *
|
||||
window_tree_get_target(struct window_tree_itemdata *item,
|
||||
struct cmd_find_state *fs)
|
||||
|
47
window.c
47
window.c
@ -61,17 +61,6 @@ static u_int next_window_pane_id;
|
||||
static u_int next_window_id;
|
||||
static u_int next_active_point;
|
||||
|
||||
/* List of window modes. */
|
||||
const struct window_mode *all_window_modes[] = {
|
||||
&window_buffer_mode,
|
||||
&window_client_mode,
|
||||
&window_clock_mode,
|
||||
&window_copy_mode,
|
||||
&window_tree_mode,
|
||||
&window_view_mode,
|
||||
NULL
|
||||
};
|
||||
|
||||
struct window_pane_input_data {
|
||||
struct cmdq_item *item;
|
||||
u_int wp;
|
||||
@ -810,15 +799,18 @@ window_destroy_panes(struct window *w)
|
||||
}
|
||||
|
||||
const char *
|
||||
window_printable_flags(struct winlink *wl)
|
||||
window_printable_flags(struct winlink *wl, int escape)
|
||||
{
|
||||
struct session *s = wl->session;
|
||||
static char flags[32];
|
||||
int pos;
|
||||
|
||||
pos = 0;
|
||||
if (wl->flags & WINLINK_ACTIVITY)
|
||||
if (wl->flags & WINLINK_ACTIVITY) {
|
||||
flags[pos++] = '#';
|
||||
if (escape)
|
||||
flags[pos++] = '#';
|
||||
}
|
||||
if (wl->flags & WINLINK_BELL)
|
||||
flags[pos++] = '!';
|
||||
if (wl->flags & WINLINK_SILENCE)
|
||||
@ -1155,12 +1147,27 @@ window_pane_reset_mode_all(struct window_pane *wp)
|
||||
window_pane_reset_mode(wp);
|
||||
}
|
||||
|
||||
static void
|
||||
window_pane_copy_key(struct window_pane *wp, key_code key)
|
||||
{
|
||||
struct window_pane *loop;
|
||||
|
||||
TAILQ_FOREACH(loop, &wp->window->panes, entry) {
|
||||
if (loop != wp &&
|
||||
TAILQ_EMPTY(&loop->modes) &&
|
||||
loop->fd != -1 &&
|
||||
(~loop->flags & PANE_INPUTOFF) &&
|
||||
window_pane_visible(loop) &&
|
||||
options_get_number(loop->options, "synchronize-panes"))
|
||||
input_key_pane(loop, key, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
int
|
||||
window_pane_key(struct window_pane *wp, struct client *c, struct session *s,
|
||||
struct winlink *wl, key_code key, struct mouse_event *m)
|
||||
{
|
||||
struct window_mode_entry *wme;
|
||||
struct window_pane *wp2;
|
||||
|
||||
if (KEYC_IS_MOUSE(key) && m == NULL)
|
||||
return (-1);
|
||||
@ -1182,16 +1189,8 @@ window_pane_key(struct window_pane *wp, struct client *c, struct session *s,
|
||||
|
||||
if (KEYC_IS_MOUSE(key))
|
||||
return (0);
|
||||
if (options_get_number(wp->window->options, "synchronize-panes")) {
|
||||
TAILQ_FOREACH(wp2, &wp->window->panes, entry) {
|
||||
if (wp2 != wp &&
|
||||
TAILQ_EMPTY(&wp2->modes) &&
|
||||
wp2->fd != -1 &&
|
||||
(~wp2->flags & PANE_INPUTOFF) &&
|
||||
window_pane_visible(wp2))
|
||||
input_key_pane(wp2, key, NULL);
|
||||
}
|
||||
}
|
||||
if (options_get_number(wp->options, "synchronize-panes"))
|
||||
window_pane_copy_key(wp, key);
|
||||
return (0);
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user