Merge branch 'sixel-passthrough' into sixel

pull/3363/head
Nicholas Marriott 2020-01-30 09:11:01 +00:00
commit 3a741aacd1
26 changed files with 621 additions and 280 deletions

27
CHANGES
View File

@ -1,5 +1,32 @@
CHANGES FROM 3.0 to X.X
* Add support for adding a note to a key binding (with bind-key -N) and use
this to add descriptions to the default key bindings. A new -N flag to
list-keys shows key bindings with notes. Change the default ? binding to use
this to show a readable summary of keys. Also extend command-prompt to return
the name of the key pressed and add a default binding (/) to show the note
for the next key pressed.
* Add support for the iTerm2 DSR 1337 sequence to get the terminal version.
* Treat plausible but invalid keys (like C-BSpace) as literal like any other
unrecognised string passed to send-keys
* Detect iTerm2 and enable use of DECSLRM (much faster with horizontally split
windows).
* Add -Z to default switch-client command in tree mode.
* Add ~ to quoted characters for %%%.
* Document client exit messages in the manual page.
* Do not let read-only clients limit the size, unless all clients are
read-only.
* Add a number of new formats to inspect what sessions and clients a window is
present or active in.
* Change file reading and writing to go through the client if necessary. This
fixes commands like "tmux loadb /dev/fd/X". Also modify source-file to
support "-" for standard input, like load-buffer and save-buffer.

View File

@ -11,8 +11,9 @@ EXTRA_DIST = \
dist_EXTRA_tmux_SOURCES = compat/*.[ch]
# Preprocessor flags.
AM_CPPFLAGS += @XOPEN_DEFINES@
AM_CPPFLAGS += -DTMUX_CONF="\"$(sysconfdir)/tmux.conf:~/.tmux.conf:~/.config/tmux/tmux.conf\""
AM_CPPFLAGS += @XOPEN_DEFINES@ \
-DTMUX_VERSION="\"@VERSION@\"" \
-DTMUX_CONF="\"$(sysconfdir)/tmux.conf:~/.tmux.conf:~/.config/tmux/tmux.conf\""
# Additional object files.
LDADD = $(LIBOBJS)

View File

@ -865,6 +865,12 @@ client_dispatch_wait(struct imsg *imsg)
case MSG_WRITE_CLOSE:
client_write_close(data, datalen);
break;
case MSG_OLDSTDERR:
case MSG_OLDSTDIN:
case MSG_OLDSTDOUT:
fprintf(stderr, "server version is too old for client\n");
proc_exit(client_proc);
break;
}
}

View File

@ -33,8 +33,8 @@ const struct cmd_entry cmd_bind_key_entry = {
.name = "bind-key",
.alias = "bind",
.args = { "cnrT:", 2, -1 },
.usage = "[-cnr] [-T key-table] key "
.args = { "cnrN:T:", 2, -1 },
.usage = "[-cnr] [-T key-table] [-N note] key "
"command [arguments]",
.flags = CMD_AFTERHOOK,
@ -46,10 +46,10 @@ cmd_bind_key_exec(struct cmd *self, struct cmdq_item *item)
{
struct args *args = self->args;
key_code key;
const char *tablename;
const char *tablename, *note;
struct cmd_parse_result *pr;
char **argv = args->argv;
int argc = args->argc;
int argc = args->argc, repeat;
key = key_string_lookup_string(argv[0]);
if (key == KEYC_NONE || key == KEYC_UNKNOWN) {
@ -63,6 +63,7 @@ cmd_bind_key_exec(struct cmd *self, struct cmdq_item *item)
tablename = "root";
else
tablename = "prefix";
repeat = args_has(args, 'r');
if (argc == 2)
pr = cmd_parse_from_string(argv[1], NULL);
@ -79,6 +80,7 @@ cmd_bind_key_exec(struct cmd *self, struct cmdq_item *item)
case CMD_PARSE_SUCCESS:
break;
}
key_bindings_add(tablename, key, args_has(args, 'r'), pr->cmdlist);
note = args_get(args, 'N');
key_bindings_add(tablename, key, note, repeat, pr->cmdlist);
return (CMD_RETURN_NORMAL);
}

View File

@ -40,8 +40,8 @@ const struct cmd_entry cmd_command_prompt_entry = {
.name = "command-prompt",
.alias = NULL,
.args = { "1iI:Np:t:", 0, 1 },
.usage = "[-1Ni] [-I inputs] [-p prompts] " CMD_TARGET_CLIENT_USAGE " "
.args = { "1kiI:Np:t:", 0, 1 },
.usage = "[-1kiN] [-I inputs] [-p prompts] " CMD_TARGET_CLIENT_USAGE " "
"[template]",
.flags = 0,
@ -122,6 +122,8 @@ cmd_command_prompt_exec(struct cmd *self, struct cmdq_item *item)
cdata->flags |= PROMPT_NUMERIC;
else if (args_has(args, 'i'))
cdata->flags |= PROMPT_INCREMENTAL;
else if (args_has(args, 'k'))
cdata->flags |= PROMPT_KEY;
status_prompt_set(c, prompt, input, cmd_command_prompt_callback,
cmd_command_prompt_free, cdata, cdata->flags);
free(prompt);

View File

@ -36,8 +36,8 @@ const struct cmd_entry cmd_list_keys_entry = {
.name = "list-keys",
.alias = "lsk",
.args = { "T:", 0, 0 },
.usage = "[-T key-table]",
.args = { "1NP:T:", 0, 1 },
.usage = "[-1N] [-P prefix-string] [-T key-table] [key]",
.flags = CMD_STARTSERVER|CMD_AFTERHOOK,
.exec = cmd_list_keys_exec
@ -54,6 +54,88 @@ const struct cmd_entry cmd_list_commands_entry = {
.exec = cmd_list_keys_exec
};
static u_int
cmd_list_keys_get_width(const char *tablename, key_code only)
{
struct key_table *table;
struct key_binding *bd;
u_int width, keywidth = 0;
table = key_bindings_get_table(tablename, 0);
if (table == NULL)
return (0);
bd = key_bindings_first(table);
while (bd != NULL) {
if ((only != KEYC_UNKNOWN && bd->key != only) ||
KEYC_IS_MOUSE(bd->key) ||
bd->note == NULL) {
bd = key_bindings_next(table, bd);
continue;
}
width = utf8_cstrwidth(key_string_lookup_key(bd->key));
if (width > keywidth)
keywidth = width;
bd = key_bindings_next(table, bd);
}
return (keywidth);
}
static int
cmd_list_keys_print_notes(struct cmdq_item *item, struct args *args,
const char *tablename, u_int keywidth, key_code only, const char *prefix)
{
struct client *c = cmd_find_client(item, NULL, 1);
struct key_table *table;
struct key_binding *bd;
const char *key;
char *tmp;
int found = 0;
table = key_bindings_get_table(tablename, 0);
if (table == NULL)
return (0);
bd = key_bindings_first(table);
while (bd != NULL) {
if ((only != KEYC_UNKNOWN && bd->key != only) ||
KEYC_IS_MOUSE(bd->key) ||
bd->note == NULL) {
bd = key_bindings_next(table, bd);
continue;
}
found = 1;
key = key_string_lookup_key(bd->key);
tmp = utf8_padcstr(key, keywidth + 1);
if (args_has(args, '1') && c != NULL)
status_message_set(c, "%s%s%s", prefix, tmp, bd->note);
else
cmdq_print(item, "%s%s%s", prefix, tmp, bd->note);
free(tmp);
if (args_has(args, '1'))
break;
bd = key_bindings_next(table, bd);
}
return (found);
}
static char *
cmd_list_keys_get_prefix(struct args *args, key_code *prefix)
{
char *s;
*prefix = options_get_number(global_s_options, "prefix");
if (!args_has(args, 'P')) {
if (*prefix != KEYC_NONE)
xasprintf(&s, "%s ", key_string_lookup_key(*prefix));
else
s = xstrdup("");
} else
s = xstrdup(args_get(args, 'P'));
return (s);
}
static enum cmd_retval
cmd_list_keys_exec(struct cmd *self, struct cmdq_item *item)
{
@ -61,19 +143,63 @@ cmd_list_keys_exec(struct cmd *self, struct cmdq_item *item)
struct key_table *table;
struct key_binding *bd;
const char *tablename, *r;
char *key, *cp, *tmp;
int repeat, width, tablewidth, keywidth;
char *key, *cp, *tmp, *start, *empty;
key_code prefix, only = KEYC_UNKNOWN;
int repeat, width, tablewidth, keywidth, found = 0;
size_t tmpsize, tmpused, cplen;
if (self->entry == &cmd_list_commands_entry)
return (cmd_list_keys_commands(self, item));
if (args->argc != 0) {
only = key_string_lookup_string(args->argv[0]);
if (only == KEYC_UNKNOWN) {
cmdq_error(item, "invalid key: %s", args->argv[0]);
return (CMD_RETURN_ERROR);
}
}
tablename = args_get(args, 'T');
if (tablename != NULL && key_bindings_get_table(tablename, 0) == NULL) {
cmdq_error(item, "table %s doesn't exist", tablename);
return (CMD_RETURN_ERROR);
}
if (args_has(args, 'N')) {
if (tablename == NULL) {
start = cmd_list_keys_get_prefix(args, &prefix);
keywidth = cmd_list_keys_get_width("root", only);
if (prefix != KEYC_NONE) {
width = cmd_list_keys_get_width("prefix", only);
if (width == 0)
prefix = KEYC_NONE;
else if (width > keywidth)
keywidth = width;
}
empty = utf8_padcstr("", utf8_cstrwidth(start));
found = cmd_list_keys_print_notes(item, args, "root",
keywidth, only, empty);
if (prefix != KEYC_NONE) {
if (cmd_list_keys_print_notes(item, args,
"prefix", keywidth, only, start))
found = 1;
}
free(empty);
} else {
if (args_has(args, 'P'))
start = xstrdup(args_get(args, 'P'));
else
start = xstrdup("");
keywidth = cmd_list_keys_get_width(tablename, only);
found = cmd_list_keys_print_notes(item, args, tablename,
keywidth, only, start);
}
free(start);
goto out;
}
repeat = 0;
tablewidth = keywidth = 0;
table = key_bindings_first_table ();
@ -84,6 +210,10 @@ cmd_list_keys_exec(struct cmd *self, struct cmdq_item *item)
}
bd = key_bindings_first(table);
while (bd != NULL) {
if (only != KEYC_UNKNOWN && bd->key != only) {
bd = key_bindings_next(table, bd);
continue;
}
key = args_escape(key_string_lookup_key(bd->key));
if (bd->flags & KEY_BINDING_REPEAT)
@ -113,6 +243,11 @@ cmd_list_keys_exec(struct cmd *self, struct cmdq_item *item)
}
bd = key_bindings_first(table);
while (bd != NULL) {
if (only != KEYC_UNKNOWN && bd->key != only) {
bd = key_bindings_next(table, bd);
continue;
}
found = 1;
key = args_escape(key_string_lookup_key(bd->key));
if (!repeat)
@ -162,13 +297,18 @@ cmd_list_keys_exec(struct cmd *self, struct cmdq_item *item)
free(tmp);
out:
if (only != KEYC_UNKNOWN && !found) {
cmdq_error(item, "unknown key: %s", args->argv[0]);
return (CMD_RETURN_ERROR);
}
return (CMD_RETURN_NORMAL);
}
static enum cmd_retval
cmd_list_keys_commands(struct cmd *self, struct cmdq_item *item)
{
struct args *args = self->args;
struct args *args = self->args;
const struct cmd_entry **entryp;
const struct cmd_entry *entry;
struct format_tree *ft;

View File

@ -133,7 +133,12 @@ statements : statement '\n'
free($2);
}
statement : condition
statement : /* empty */
{
$$ = xmalloc (sizeof *$$);
TAILQ_INIT($$);
}
| condition
{
struct cmd_parse_state *ps = &parse_state;
@ -144,11 +149,6 @@ statement : condition
cmd_parse_free_commands($1);
}
}
| assignment
{
$$ = xmalloc (sizeof *$$);
TAILQ_INIT($$);
}
| commands
{
struct cmd_parse_state *ps = &parse_state;
@ -194,8 +194,10 @@ expanded : format
free($1);
}
assignment : /* empty */
| EQUALS
optional_assignment : /* empty */
| assignment
assignment : EQUALS
{
struct cmd_parse_state *ps = &parse_state;
int flags = ps->input->flags;
@ -339,7 +341,8 @@ commands : command
struct cmd_parse_state *ps = &parse_state;
$$ = cmd_parse_new_commands();
if (ps->scope == NULL || ps->scope->flag)
if ($1->name != NULL &&
(ps->scope == NULL || ps->scope->flag))
TAILQ_INSERT_TAIL($$, $1, entry);
else
cmd_parse_free_command($1);
@ -358,7 +361,8 @@ commands : command
{
struct cmd_parse_state *ps = &parse_state;
if (ps->scope == NULL || ps->scope->flag) {
if ($3->name != NULL &&
(ps->scope == NULL || ps->scope->flag)) {
$$ = $1;
TAILQ_INSERT_TAIL($$, $3, entry);
} else {
@ -372,7 +376,15 @@ commands : command
$$ = $1;
}
command : assignment TOKEN
command : assignment
{
struct cmd_parse_state *ps = &parse_state;
$$ = xcalloc(1, sizeof *$$);
$$->name = NULL;
$$->line = ps->input->line;
}
| optional_assignment TOKEN
{
struct cmd_parse_state *ps = &parse_state;
@ -381,7 +393,7 @@ command : assignment TOKEN
$$->line = ps->input->line;
}
| assignment TOKEN arguments
| optional_assignment TOKEN arguments
{
struct cmd_parse_state *ps = &parse_state;

View File

@ -60,6 +60,9 @@ static struct cmdq_item *
cmd_send_keys_inject_key(struct client *c, struct cmd_find_state *fs,
struct cmdq_item *item, key_code key)
{
struct session *s = fs->s;
struct winlink *wl = fs->wl;
struct window_pane *wp = fs->wp;
struct window_mode_entry *wme;
struct key_table *table;
struct key_binding *bd;
@ -68,7 +71,8 @@ cmd_send_keys_inject_key(struct client *c, struct cmd_find_state *fs,
if (wme == NULL || wme->mode->key_table == NULL) {
if (options_get_number(fs->wp->window->options, "xterm-keys"))
key |= KEYC_XTERM;
window_pane_key(fs->wp, item->client, fs->s, fs->wl, key, NULL);
if (window_pane_key(wp, item->client, s, wl, key, NULL) != 0)
return (NULL);
return (item);
}
table = key_bindings_get_table(wme->mode->key_table(wme), 1);
@ -87,6 +91,7 @@ cmd_send_keys_inject_string(struct client *c, struct cmd_find_state *fs,
struct cmdq_item *item, struct args *args, int i)
{
const char *s = args->argv[i];
struct cmdq_item *new_item;
struct utf8_data *ud, *uc;
wchar_t wc;
key_code key;
@ -104,8 +109,11 @@ cmd_send_keys_inject_string(struct client *c, struct cmd_find_state *fs,
literal = args_has(args, 'l');
if (!literal) {
key = key_string_lookup_string(s);
if (key != KEYC_NONE && key != KEYC_UNKNOWN)
return (cmd_send_keys_inject_key(c, fs, item, key));
if (key != KEYC_NONE && key != KEYC_UNKNOWN) {
new_item = cmd_send_keys_inject_key(c, fs, item, key);
if (new_item != NULL)
return (new_item);
}
literal = 1;
}
if (literal) {

View File

@ -171,8 +171,12 @@ void warnx(const char *, ...);
#endif
#ifndef FNM_CASEFOLD
#ifdef FNM_IGNORECASE
#define FNM_CASEFOLD FNM_IGNORECASE
#else
#define FNM_CASEFOLD 0
#endif
#endif
#ifndef INFTIM
#define INFTIM -1

View File

@ -1121,7 +1121,7 @@ format_create(struct client *c, struct cmdq_item *item, int tag, int flags)
ft->flags = flags;
ft->time = time(NULL);
format_add(ft, "version", "%s", VERSION);
format_add(ft, "version", "%s", getversion());
format_add_cb(ft, "host", format_cb_host);
format_add_cb(ft, "host_short", format_cb_host_short);
format_add_cb(ft, "pid", format_cb_pid);
@ -2362,7 +2362,6 @@ format_defaults_client(struct format_tree *ft, struct client *c)
struct session *s;
const char *name;
struct tty *tty = &c->tty;
const char *types[] = TTY_TYPES;
if (ft->s == NULL)
ft->s = c->session;
@ -2380,8 +2379,6 @@ format_defaults_client(struct format_tree *ft, struct client *c)
if (tty->term_name != NULL)
format_add(ft, "client_termname", "%s", tty->term_name);
if (tty->term_name != NULL)
format_add(ft, "client_termtype", "%s", types[tty->term_type]);
format_add_tv(ft, "client_created", &c->creation_time);
format_add_tv(ft, "client_activity", &c->activity_time);

View File

@ -149,7 +149,7 @@ input_split2(u_int c, u_char *dst)
}
/* Translate a key code into an output key sequence. */
void
int
input_key(struct window_pane *wp, key_code key, struct mouse_event *m)
{
const struct input_key_ent *ike;
@ -166,14 +166,14 @@ input_key(struct window_pane *wp, key_code key, struct mouse_event *m)
if (KEYC_IS_MOUSE(key)) {
if (m != NULL && m->wp != -1 && (u_int)m->wp == wp->id)
input_key_mouse(wp, m);
return;
return (0);
}
/* Literal keys go as themselves (can't be more than eight bits). */
if (key & KEYC_LITERAL) {
ud.data[0] = (u_char)key;
bufferevent_write(wp->event, &ud.data[0], 1);
return;
return (0);
}
/* Is this backspace? */
@ -194,15 +194,15 @@ input_key(struct window_pane *wp, key_code key, struct mouse_event *m)
bufferevent_write(wp->event, "\033", 1);
ud.data[0] = justkey;
bufferevent_write(wp->event, &ud.data[0], 1);
return;
return (0);
}
if (justkey > 0x7f && justkey < KEYC_BASE) {
if (utf8_split(justkey, &ud) != UTF8_DONE)
return;
return (-1);
if (key & KEYC_ESCAPE)
bufferevent_write(wp->event, "\033", 1);
bufferevent_write(wp->event, ud.data, ud.size);
return;
return (0);
}
/*
@ -213,7 +213,7 @@ input_key(struct window_pane *wp, key_code key, struct mouse_event *m)
if ((out = xterm_keys_lookup(key)) != NULL) {
bufferevent_write(wp->event, out, strlen(out));
free(out);
return;
return (0);
}
}
key &= ~KEYC_XTERM;
@ -236,7 +236,7 @@ input_key(struct window_pane *wp, key_code key, struct mouse_event *m)
}
if (i == nitems(input_keys)) {
log_debug("key 0x%llx missing", key);
return;
return (-1);
}
dlen = strlen(ike->data);
log_debug("found key 0x%llx: \"%s\"", key, ike->data);
@ -245,6 +245,7 @@ input_key(struct window_pane *wp, key_code key, struct mouse_event *m)
if (key & KEYC_ESCAPE)
bufferevent_write(wp->event, "\033", 1);
bufferevent_write(wp->event, ike->data, dlen);
return (0);
}
/* Translate mouse and output. */

12
input.c
View File

@ -20,6 +20,7 @@
#include <netinet/in.h>
#include <ctype.h>
#include <resolv.h>
#include <stdlib.h>
#include <string.h>
@ -772,6 +773,7 @@ input_save_state(struct input_ctx *ictx)
ictx->old_mode = s->mode;
}
/* Restore screen state. */
static void
input_restore_state(struct input_ctx *ictx)
{
@ -1301,6 +1303,7 @@ input_csi_dispatch(struct input_ctx *ictx)
struct input_table_entry *entry;
int i, n, m;
u_int cx, bg = ictx->cell.cell.bg;
char *copy, *cp;
if (ictx->flags & INPUT_DISCARD)
return (0);
@ -1432,6 +1435,13 @@ input_csi_dispatch(struct input_ctx *ictx)
case 6:
input_reply(ictx, "\033[%u;%uR", s->cy + 1, s->cx + 1);
break;
case 1337: /* Terminal version, from iTerm2. */
copy = xstrdup(getversion());
for (cp = copy; *cp != '\0'; cp++)
*cp = toupper((u_char)*cp);
input_reply(ictx, "\033[TMUX %sn", copy);
free(copy);
break;
default:
log_debug("%s: unknown '%c'", __func__, ictx->ch);
break;
@ -2151,8 +2161,6 @@ static int
input_dcs_dispatch(struct input_ctx *ictx)
{
struct screen_write_ctx *sctx = &ictx->ctx;
struct window_pane *wp = ictx->wp;
struct window *w = wp->window;
u_char *buf = ictx->input_buf;
size_t len = ictx->input_len;
const char prefix[] = "tmux;";

View File

@ -90,6 +90,7 @@ key_bindings_free(struct key_table *table, struct key_binding *bd)
{
RB_REMOVE(key_bindings, &table->key_bindings, bd);
cmd_list_free(bd->cmdlist);
free((void *)bd->note);
free(bd);
}
@ -163,7 +164,7 @@ key_bindings_next(__unused struct key_table *table, struct key_binding *bd)
}
void
key_bindings_add(const char *name, key_code key, int repeat,
key_bindings_add(const char *name, key_code key, const char *note, int repeat,
struct cmd_list *cmdlist)
{
struct key_table *table;
@ -177,6 +178,8 @@ key_bindings_add(const char *name, key_code key, int repeat,
bd = xcalloc(1, sizeof *bd);
bd->key = key;
if (note != NULL)
bd->note = xstrdup(note);
RB_INSERT(key_bindings, &table->key_bindings, bd);
if (repeat)
@ -226,87 +229,88 @@ void
key_bindings_init(void)
{
static const char *defaults[] = {
"bind C-b send-prefix",
"bind C-o rotate-window",
"bind C-z suspend-client",
"bind Space next-layout",
"bind ! break-pane",
"bind '\"' split-window",
"bind '#' list-buffers",
"bind '$' command-prompt -I'#S' \"rename-session -- '%%'\"",
"bind % split-window -h",
"bind & confirm-before -p\"kill-window #W? (y/n)\" kill-window",
"bind \"'\" command-prompt -pindex \"select-window -t ':%%'\"",
"bind ( switch-client -p",
"bind ) switch-client -n",
"bind , command-prompt -I'#W' \"rename-window -- '%%'\"",
"bind - delete-buffer",
"bind . command-prompt \"move-window -t '%%'\"",
"bind 0 select-window -t:=0",
"bind 1 select-window -t:=1",
"bind 2 select-window -t:=2",
"bind 3 select-window -t:=3",
"bind 4 select-window -t:=4",
"bind 5 select-window -t:=5",
"bind 6 select-window -t:=6",
"bind 7 select-window -t:=7",
"bind 8 select-window -t:=8",
"bind 9 select-window -t:=9",
"bind : command-prompt",
"bind \\; last-pane",
"bind = choose-buffer -Z",
"bind ? list-keys",
"bind D choose-client -Z",
"bind E select-layout -E",
"bind L switch-client -l",
"bind M select-pane -M",
"bind [ copy-mode",
"bind ] paste-buffer",
"bind c new-window",
"bind d detach-client",
"bind f command-prompt \"find-window -Z -- '%%'\"",
"bind i display-message",
"bind l last-window",
"bind m select-pane -m",
"bind n next-window",
"bind o select-pane -t:.+",
"bind p previous-window",
"bind q display-panes",
"bind r refresh-client",
"bind s choose-tree -Zs",
"bind t clock-mode",
"bind w choose-tree -Zw",
"bind x confirm-before -p\"kill-pane #P? (y/n)\" kill-pane",
"bind z resize-pane -Z",
"bind '{' swap-pane -U",
"bind '}' swap-pane -D",
"bind '~' show-messages",
"bind PPage copy-mode -u",
"bind -r Up select-pane -U",
"bind -r Down select-pane -D",
"bind -r Left select-pane -L",
"bind -r Right select-pane -R",
"bind M-1 select-layout even-horizontal",
"bind M-2 select-layout even-vertical",
"bind M-3 select-layout main-horizontal",
"bind M-4 select-layout main-vertical",
"bind M-5 select-layout tiled",
"bind M-n next-window -a",
"bind M-o rotate-window -D",
"bind M-p previous-window -a",
"bind -r S-Up refresh-client -U 10",
"bind -r S-Down refresh-client -D 10",
"bind -r S-Left refresh-client -L 10",
"bind -r S-Right refresh-client -R 10",
"bind -r DC refresh-client -c",
"bind -r M-Up resize-pane -U 5",
"bind -r M-Down resize-pane -D 5",
"bind -r M-Left resize-pane -L 5",
"bind -r M-Right resize-pane -R 5",
"bind -r C-Up resize-pane -U",
"bind -r C-Down resize-pane -D",
"bind -r C-Left resize-pane -L",
"bind -r C-Right resize-pane -R",
"bind -N 'Send the prefix key' C-b send-prefix",
"bind -N 'Rotate through the panes' C-o rotate-window",
"bind -N 'Suspend the current client' C-z suspend-client",
"bind -N 'Select next layout' Space next-layout",
"bind -N 'Break pane to a new window' ! break-pane",
"bind -N 'Split window vertically' '\"' split-window",
"bind -N 'List all paste buffers' '#' list-buffers",
"bind -N 'Rename current session' '$' command-prompt -I'#S' \"rename-session -- '%%'\"",
"bind -N 'Split window horizontally' % split-window -h",
"bind -N 'Kill current window' & confirm-before -p\"kill-window #W? (y/n)\" kill-window",
"bind -N 'Prompt for window index to select' \"'\" command-prompt -pindex \"select-window -t ':%%'\"",
"bind -N 'Switch to previous client' ( switch-client -p",
"bind -N 'Switch to next client' ) switch-client -n",
"bind -N 'Rename current window' , command-prompt -I'#W' \"rename-window -- '%%'\"",
"bind -N 'Delete the most recent paste buffer' - delete-buffer",
"bind -N 'Move the current window' . command-prompt \"move-window -t '%%'\"",
"bind -N 'Describe key binding' '/' command-prompt -kpkey 'list-keys -1N \"%%%\"'",
"bind -N 'Select window 0' 0 select-window -t:=0",
"bind -N 'Select window 1' 1 select-window -t:=1",
"bind -N 'Select window 2' 2 select-window -t:=2",
"bind -N 'Select window 3' 3 select-window -t:=3",
"bind -N 'Select window 4' 4 select-window -t:=4",
"bind -N 'Select window 5' 5 select-window -t:=5",
"bind -N 'Select window 6' 6 select-window -t:=6",
"bind -N 'Select window 7' 7 select-window -t:=7",
"bind -N 'Select window 8' 8 select-window -t:=8",
"bind -N 'Select window 9' 9 select-window -t:=9",
"bind -N 'Prompt for a command' : command-prompt",
"bind -N 'Move to the previously active pane' \\; last-pane",
"bind -N 'Choose a paste buffer from a list' = choose-buffer -Z",
"bind -N 'List key bindings' ? list-keys -N",
"bind -N 'Choose a client from a list' D choose-client -Z",
"bind -N 'Spread panes out evenly' E select-layout -E",
"bind -N 'Switch to the last client' L switch-client -l",
"bind -N 'Clear the marked pane' M select-pane -M",
"bind -N 'Enter copy mode' [ copy-mode",
"bind -N 'Paste the most recent paste buffer' ] paste-buffer",
"bind -N 'Create a new window' c new-window",
"bind -N 'Detach the current client' d detach-client",
"bind -N 'Search for a pane' f command-prompt \"find-window -Z -- '%%'\"",
"bind -N 'Display window information' i display-message",
"bind -N 'Select the previously current window' l last-window",
"bind -N 'Toggle the marked pane' m select-pane -m",
"bind -N 'Select the next window' n next-window",
"bind -N 'Select the next pane' o select-pane -t:.+",
"bind -N 'Select the previous pane' p previous-window",
"bind -N 'Display pane numbers' q display-panes",
"bind -N 'Redraw the current client' r refresh-client",
"bind -N 'Choose a session from a list' s choose-tree -Zs",
"bind -N 'Show a clock' t clock-mode",
"bind -N 'Choose a window from a list' w choose-tree -Zw",
"bind -N 'Kill the active pane' x confirm-before -p\"kill-pane #P? (y/n)\" kill-pane",
"bind -N 'Zoom the active pane' z resize-pane -Z",
"bind -N 'Swap the active pane with the pane above' '{' swap-pane -U",
"bind -N 'Swap the active pane with the pane below' '}' swap-pane -D",
"bind -N 'Show messages' '~' show-messages",
"bind -N 'Enter copy mode and scroll up' PPage copy-mode -u",
"bind -N 'Select the pane above the active pane' -r Up select-pane -U",
"bind -N 'Select the pane below the active pane' -r Down select-pane -D",
"bind -N 'Select the pane to the left of the active pane' -r Left select-pane -L",
"bind -N 'Select the pane to the right of the active pane' -r Right select-pane -R",
"bind -N 'Set the even-horizontal layout' M-1 select-layout even-horizontal",
"bind -N 'Set the even-vertical layout' M-2 select-layout even-vertical",
"bind -N 'Set the main-horizontal layout' M-3 select-layout main-horizontal",
"bind -N 'Set the main-vertical layout' M-4 select-layout main-vertical",
"bind -N 'Select the tiled layout' M-5 select-layout tiled",
"bind -N 'Select the next window with an alert' M-n next-window -a",
"bind -N 'Rotate through the panes in reverse' M-o rotate-window -D",
"bind -N 'Select the previous window with an alert' M-p previous-window -a",
"bind -N 'Move the visible part of the window up' -r S-Up refresh-client -U 10",
"bind -N 'Move the visible part of the window down' -r S-Down refresh-client -D 10",
"bind -N 'Move the visible part of the window left' -r S-Left refresh-client -L 10",
"bind -N 'Move the visible part of the window right' -r S-Right refresh-client -R 10",
"bind -N 'Reset so the visible part of the window follows the cursor' -r DC refresh-client -c",
"bind -N 'Resize the pane up by 5' -r M-Up resize-pane -U 5",
"bind -N 'Resize the pane down by 5' -r M-Down resize-pane -D 5",
"bind -N 'Resize the pane left by 5' -r M-Left resize-pane -L 5",
"bind -N 'Resize the pane right by 5' -r M-Right resize-pane -R 5",
"bind -N 'Resize the pane up' -r C-Up resize-pane -U",
"bind -N 'Resize the pane down' -r C-Down resize-pane -D",
"bind -N 'Resize the pane left' -r C-Left resize-pane -L",
"bind -N 'Resize the pane right' -r C-Right resize-pane -R",
"bind -n MouseDown1Pane select-pane -t=\\; send-keys -M",
"bind -n MouseDrag1Border resize-pane -M",

2
proc.c
View File

@ -181,7 +181,7 @@ proc_start(const char *name)
memset(&u, 0, sizeof u);
log_debug("%s started (%ld): version %s, socket %s, protocol %d", name,
(long)getpid(), VERSION, socket_path, PROTOCOL_VERSION);
(long)getpid(), getversion(), socket_path, PROTOCOL_VERSION);
log_debug("on %s %s %s; libevent %s (%s)", u.sysname, u.release,
u.version, event_get_version(), event_get_method());

View File

@ -66,10 +66,26 @@ resize_window(struct window *w, u_int sx, u_int sy, int xpixel, int ypixel)
static int
ignore_client_size(struct client *c)
{
struct client *loop;
if (c->session == NULL)
return (1);
if (c->flags & CLIENT_NOSIZEFLAGS)
return (1);
if (c->flags & CLIENT_READONLY) {
/*
* Ignore readonly clients if there are any attached clients
* that aren't readonly.
*/
TAILQ_FOREACH (loop, &clients, entry) {
if (loop->session == NULL)
continue;
if (loop->flags & CLIENT_NOSIZEFLAGS)
continue;
if (~loop->flags & CLIENT_READONLY)
return (1);
}
}
if ((c->flags & CLIENT_CONTROL) && (~c->flags & CLIENT_SIZECHANGED))
return (1);
return (0);
@ -363,14 +379,15 @@ recalculate_sizes(void)
* client.
*/
TAILQ_FOREACH(c, &clients, entry) {
s = c->session;
if (s != NULL && !(c->flags & CLIENT_UNATTACHEDFLAGS))
s->attached++;
if (ignore_client_size(c))
continue;
s = c->session;
if (c->tty.sy <= s->statuslines || (c->flags & CLIENT_CONTROL))
c->flags |= CLIENT_STATUSOFF;
else
c->flags &= ~CLIENT_STATUSOFF;
s->attached++;
}
/* Walk each window and adjust the size. */

View File

@ -1032,7 +1032,7 @@ server_client_key_callback(struct cmdq_item *item, void *data)
key_code key0;
/* Check the client is good to accept input. */
if (s == NULL || (c->flags & (CLIENT_DEAD|CLIENT_SUSPENDED)) != 0)
if (s == NULL || (c->flags & CLIENT_UNATTACHEDFLAGS))
goto out;
wl = s->curw;
@ -1219,7 +1219,7 @@ server_client_handle_key(struct client *c, struct key_event *event)
struct cmdq_item *item;
/* Check the client is good to accept input. */
if (s == NULL || (c->flags & (CLIENT_DEAD|CLIENT_SUSPENDED)) != 0)
if (s == NULL || (c->flags & CLIENT_UNATTACHEDFLAGS))
return (0);
/*

35
spawn.c
View File

@ -223,6 +223,17 @@ spawn_pane(struct spawn_context *sc, char **cause)
spawn_log(__func__, sc);
/*
* Work out the current working directory. If respawning, use
* the pane's stored one unless specified.
*/
if (sc->cwd != NULL)
cwd = format_single(item, sc->cwd, c, s, NULL, NULL);
else if (~sc->flags & SPAWN_RESPAWN)
cwd = xstrdup(server_client_get_cwd(c, s));
else
cwd = NULL;
/*
* If we are respawning then get rid of the old process. Otherwise
* either create a new cell or assign to the one we are given.
@ -233,6 +244,7 @@ spawn_pane(struct spawn_context *sc, char **cause)
window_pane_index(sc->wp0, &idx);
xasprintf(cause, "pane %s:%d.%u still active",
s->name, sc->wl->idx, idx);
free(cwd);
return (NULL);
}
if (sc->wp0->fd != -1) {
@ -253,8 +265,8 @@ 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.
* 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");
@ -269,6 +281,10 @@ spawn_pane(struct spawn_context *sc, char **cause)
argc = sc->argc;
argv = sc->argv;
}
if (cwd != NULL) {
free(new_wp->cwd);
new_wp->cwd = cwd;
}
/*
* Replace the stored arguments if there are new ones. If not, the
@ -280,21 +296,6 @@ spawn_pane(struct spawn_context *sc, char **cause)
new_wp->argv = cmd_copy_argv(argc, argv);
}
/*
* Work out the current working directory. If respawning, use
* the pane's stored one unless specified.
*/
if (sc->cwd != NULL)
cwd = format_single(item, sc->cwd, c, s, NULL, NULL);
else if (~sc->flags & SPAWN_RESPAWN)
cwd = xstrdup(server_client_get_cwd(c, s));
else
cwd = NULL;
if (cwd != NULL) {
free(new_wp->cwd);
new_wp->cwd = cwd;
}
/* Create an environment for this pane. */
child = environ_for_session(s, 0);
if (sc->environ != NULL)

View File

@ -915,11 +915,17 @@ status_prompt_key(struct client *c, key_code key)
{
struct options *oo = c->session->options;
char *s, *cp, word[64], prefix = '=';
const char *histstr, *ws = NULL;
const char *histstr, *ws = NULL, *keystring;
size_t size, n, off, idx, used;
struct utf8_data tmp, *first, *last, *ud;
int keys;
if (c->prompt_flags & PROMPT_KEY) {
keystring = key_string_lookup_key(key);
c->prompt_inputcb(c, c->prompt_data, keystring, 1);
status_prompt_clear(c);
return (0);
}
size = utf8_strlen(c->prompt_buffer);
if (c->prompt_flags & PROMPT_NUMERIC) {

70
tmux.1
View File

@ -214,7 +214,6 @@ was given) and off.
Report the
.Nm
version.
.Pp
.It Ar command Op Ar flags
This specifies one of a set of commands used to control
.Nm ,
@ -556,7 +555,7 @@ Braces may be enclosed inside braces, for example:
.Bd -literal -offset indent
bind x if-shell "true" {
if-shell "true" {
display "true!"
display "true!"
}
}
.Ed
@ -1292,6 +1291,17 @@ shows the parsed commands and line numbers if possible.
Start the
.Nm
server, if not already running, without creating any sessions.
.Pp
Note that as by default the
.Nm
server will exit with no sessions, this is only useful if a session is created in
.Pa ~/.tmux.conf ,
.Ic exit-empty
is turned off, or another command is run as part of the same command sequence.
For example:
.Bd -literal -offset indent
$ tmux start \\; show -g
.Ed
.It Xo Ic suspend-client
.Op Fl t Ar target-client
.Xc
@ -1340,7 +1350,8 @@ is used,
option will not be applied.
.Pp
.Fl T
sets the client's key table; the next key from the client will be interpreted from
sets the client's key table; the next key from the client will be interpreted
from
.Ar key-table .
This may be used to configure multiple prefix keys, or to bind commands to
sequences of keys.
@ -2538,10 +2549,11 @@ This is similar to
except the source and destination windows are swapped.
It is an error if no window exists at
.Ar src-window .
If
.Fl d
is given, the new window does not become the current window.
.Pp
Like
.Ic swap-pane ,
if
If
.Fl s
is omitted and a marked pane is present (see
.Ic select-pane
@ -2617,6 +2629,7 @@ Commands related to key bindings are as follows:
.Bl -tag -width Ds
.It Xo Ic bind-key
.Op Fl nr
.Op Fl N Ar note
.Op Fl T Ar key-table
.Ar key Ar command Op Ar arguments
.Xc
@ -2664,22 +2677,46 @@ The
flag indicates this key may repeat, see the
.Ic repeat-time
option.
.Fl N
attaches a note to the key (shown with
.Ic list-keys
.Fl N ) .
.Pp
To view the default bindings and possible commands, see the
.Ic list-keys
command.
.It Xo Ic list-keys
.Op Fl T Ar key-table
.Op Fl 1N
.Op Fl P Ar prefix-string Fl T Ar key-table
.Op key
.Xc
.D1 (alias: Ic lsk )
List all key bindings.
Without
.Fl T
all key tables are printed.
By default this shows all keys or any bindings for
.Ar key
in the syntax of the
.Ic bind-key
command.
.Fl N
instead show keys and attached notes, i
.Ar key-table
if given or in the
.Em root
and
.Em prefix
key tables by default.
.Fl P
specifies a prefix to print before each key.
With
.Fl 1
only the first matching key and note is shown.
.Pp
Without
.Fl N ,
.Fl T
only
.Ar key-table .
prints only keys in
.Ar key-table ,
otherwise all key tables are printed.
.It Xo Ic send-keys
.Op Fl FHlMRX
.Op Fl N Ar repeat-count
@ -4232,7 +4269,6 @@ The following variables are available, where appropriate:
.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_termname" Ta "" Ta "Terminal name of client"
.It Li "client_termtype" Ta "" Ta "Terminal type of client"
.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"
@ -4700,7 +4736,7 @@ session option.
Commands related to the status line are as follows:
.Bl -tag -width Ds
.It Xo Ic command-prompt
.Op Fl 1Ni
.Op Fl 1ikN
.Op Fl I Ar inputs
.Op Fl p Ar prompts
.Op Fl t Ar target-client
@ -4750,6 +4786,10 @@ but any quotation marks are escaped.
.Fl 1
makes the prompt only accept one key press, in this case the resulting input
is a single character.
.Fl k
is like
.Fl 1
but the key press is translated to a key name.
.Fl N
makes the prompt only accept numeric key presses.
.Fl i
@ -4913,7 +4953,7 @@ When the
option is reached, the oldest automatically named buffer is deleted.
Explicitly named buffers are not subject to
.Ic buffer-limit
and may be deleted with
and may be deleted with the
.Ic delete-buffer
command.
.Pp

15
tmux.c
View File

@ -18,6 +18,7 @@
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/utsname.h>
#include <errno.h>
#include <event.h>
@ -209,6 +210,12 @@ find_home(void)
return (home);
}
const char *
getversion(void)
{
return TMUX_VERSION;
}
int
main(int argc, char **argv)
{
@ -235,7 +242,7 @@ main(int argc, char **argv)
flags = 0;
label = path = NULL;
while ((opt = getopt(argc, argv, "2c:Cdf:lL:qS:uUVv")) != -1) {
while ((opt = getopt(argc, argv, "2c:Cdf:lL:qS:uUvV")) != -1) {
switch (opt) {
case '2':
flags |= CLIENT_256COLOURS;
@ -249,12 +256,12 @@ main(int argc, char **argv)
else
flags |= CLIENT_CONTROL;
break;
case 'V':
printf("%s %s\n", getprogname(), VERSION);
exit(0);
case 'f':
set_cfg_file(optarg);
break;
case 'V':
printf("%s %s\n", getprogname(), getversion());
exit(0);
case 'l':
flags |= CLIENT_LOGIN;
break;

51
tmux.h
View File

@ -1187,8 +1187,11 @@ struct tty_term {
struct tty_code *codes;
#define TERM_256COLOURS 0x1
#define TERM_EARLYWRAP 0x2
#define TERM_SIXEL 0x4
#define TERM_NOXENL 0x2
#define TERM_DECSLRM 0x4
#define TERM_DECFRA 0x8
#define TERM_RGBCOLOURS 0x10
#define TERM_SIXEL 0x20
int flags;
LIST_ENTRY(tty_term) entry;
@ -1197,6 +1200,7 @@ LIST_HEAD(tty_terms, tty_term);
struct tty {
struct client *client;
struct event start_timer;
u_int sx;
u_int sy;
@ -1245,22 +1249,14 @@ struct tty {
#define TTY_OPENED 0x20
#define TTY_FOCUS 0x40
#define TTY_BLOCK 0x80
#define TTY_NOBLOCK 0x100
#define TTY_HAVEDA 0x100
#define TTY_HAVEDSR 0x200
#define TTY_NOBLOCK 0x400
int flags;
struct tty_term *term;
char *term_name;
int term_flags;
enum {
TTY_VT100,
TTY_VT101,
TTY_VT102,
TTY_VT220,
TTY_VT320,
TTY_VT420,
TTY_VT520,
TTY_UNKNOWN
} term_type;
u_int mouse_last_x;
u_int mouse_last_y;
@ -1274,15 +1270,6 @@ struct tty {
struct event key_timer;
struct tty_key *key_tree;
};
#define TTY_TYPES \
{ "VT100", \
"VT101", \
"VT102", \
"VT220", \
"VT320", \
"VT420", \
"VT520", \
"Unknown" }
/* TTY command context. */
struct tty_ctx {
@ -1293,6 +1280,7 @@ struct tty_ctx {
u_int num;
void *ptr;
int more;
/*
* Cursor and region position before the screen was updated - this is
@ -1617,11 +1605,14 @@ struct client {
CLIENT_REDRAWSTATUSALWAYS| \
CLIENT_REDRAWBORDERS| \
CLIENT_REDRAWOVERLAY)
#define CLIENT_UNATTACHEDFLAGS \
(CLIENT_DEAD| \
CLIENT_SUSPENDED| \
CLIENT_DETACHING)
#define CLIENT_NOSIZEFLAGS \
(CLIENT_DEAD| \
CLIENT_SUSPENDED| \
CLIENT_DETACHING| \
CLIENT_READONLY)
CLIENT_DETACHING)
int flags;
struct key_table *keytable;
@ -1644,6 +1635,7 @@ struct client {
#define PROMPT_NUMERIC 0x2
#define PROMPT_INCREMENTAL 0x4
#define PROMPT_NOFORMAT 0x8
#define PROMPT_KEY 0x10
int prompt_flags;
struct session *session;
@ -1671,6 +1663,7 @@ TAILQ_HEAD(clients, client);
struct key_binding {
key_code key;
struct cmd_list *cmdlist;
const char *note;
int flags;
#define KEY_BINDING_REPEAT 0x1
@ -1798,6 +1791,7 @@ int areshell(const char *);
void setblocking(int, int);
const char *find_cwd(void);
const char *find_home(void);
const char *getversion(void);
/* proc.c */
struct imsg;
@ -2014,7 +2008,7 @@ void tty_draw_images(struct tty *, struct window_pane *, struct screen *);
int tty_open(struct tty *, char **);
void tty_close(struct tty *);
void tty_free(struct tty *);
void tty_set_type(struct tty *, int, int);
void tty_set_flags(struct tty *, int);
void tty_write(void (*)(struct tty *, const struct tty_ctx *),
struct tty_ctx *);
void tty_cmd_alignmenttest(struct tty *, const struct tty_ctx *);
@ -2184,7 +2178,8 @@ void key_bindings_unref_table(struct key_table *);
struct key_binding *key_bindings_get(struct key_table *, key_code);
struct key_binding *key_bindings_first(struct key_table *);
struct key_binding *key_bindings_next(struct key_table *, struct key_binding *);
void key_bindings_add(const char *, key_code, int, struct cmd_list *);
void key_bindings_add(const char *, key_code, const char *, int,
struct cmd_list *);
void key_bindings_remove(const char *, key_code);
void key_bindings_remove_table(const char *);
void key_bindings_init(void);
@ -2315,7 +2310,7 @@ void input_parse(struct window_pane *);
void input_parse_buffer(struct window_pane *, u_char *, size_t);
/* input-key.c */
void input_key(struct window_pane *, key_code, struct mouse_event *);
int input_key(struct window_pane *, key_code, struct mouse_event *);
/* xterm-keys.c */
char *xterm_keys_lookup(key_code);
@ -2540,7 +2535,7 @@ int window_pane_set_mode(struct window_pane *,
struct args *);
void window_pane_reset_mode(struct window_pane *);
void window_pane_reset_mode_all(struct window_pane *);
void window_pane_key(struct window_pane *, struct client *,
int window_pane_key(struct window_pane *, struct client *,
struct session *, struct winlink *, key_code,
struct mouse_event *);
int window_pane_visible(struct window_pane *);

View File

@ -52,6 +52,8 @@ static int tty_keys_clipboard(struct tty *, const char *, size_t,
size_t *);
static int tty_keys_device_attributes(struct tty *, const char *, size_t,
size_t *);
static int tty_keys_device_status_report(struct tty *, const char *,
size_t, size_t *);
/* Default raw keys. */
struct tty_default_key_raw {
@ -607,6 +609,17 @@ tty_keys_next(struct tty *tty)
goto partial_key;
}
/* Is this a device status report response? */
switch (tty_keys_device_status_report(tty, buf, len, &size)) {
case 0: /* yes */
key = KEYC_UNKNOWN;
goto complete_key;
case -1: /* no, or not valid */
break;
case 1: /* partial */
goto partial_key;
}
/* Is this a mouse key press? */
switch (tty_keys_mouse(tty, buf, len, &size, &m)) {
case 0: /* yes */
@ -1001,13 +1014,14 @@ static int
tty_keys_device_attributes(struct tty *tty, const char *buf, size_t len,
size_t *size)
{
struct client *c = tty->client;
u_int i, n = 0;
char tmp[64], *endptr, p[32] = { 0 }, *cp, *next;
static const char *types[] = TTY_TYPES;
int type, flags = 0;
struct client *c = tty->client;
u_int i, n = 0;
char tmp[64], *endptr, p[32] = { 0 }, *cp, *next;
int flags = 0;
*size = 0;
if (tty->flags & TTY_HAVEDA)
return (-1);
/* First three bytes are always \033[?. */
if (buf[0] != '\033')
@ -1038,44 +1052,80 @@ tty_keys_device_attributes(struct tty *tty, const char *buf, size_t len,
cp = tmp;
while ((next = strsep(&cp, ";")) != NULL) {
p[n] = strtoul(next, &endptr, 10);
if (*endptr != '\0' && *endptr != ';')
if (*endptr != '\0')
p[n] = 0;
n++;
}
/* Store terminal type. */
type = TTY_UNKNOWN;
/* Set terminal flags. */
switch (p[0]) {
case 1:
if (p[1] == 2)
type = TTY_VT100;
else if (p[1] == 0)
type = TTY_VT101;
break;
case 6:
type = TTY_VT102;
break;
case 62:
type = TTY_VT220;
break;
case 63:
type = TTY_VT320;
break;
case 64:
type = TTY_VT420;
break;
case 65:
type = TTY_VT520;
case 64: /* VT420 */
flags |= (TERM_DECFRA|TERM_DECSLRM);
break;
}
for (i = 2; i < n; i++) {
for (i = 1; i < n; i++) {
log_debug("%s: DA feature: %d", c->name, p[i]);
if (p[i] == 4)
flags |= TERM_SIXEL;
}
tty_set_type(tty, type, flags);
log_debug("%s: received DA %.*s (%s)", c->name, (int)*size, buf,
types[type]);
tty_set_flags(tty, flags);
tty->flags |= TTY_HAVEDA;
return (0);
}
/*
* Handle device status report input. Returns 0 for success, -1 for failure, 1
* for partial.
*/
static int
tty_keys_device_status_report(struct tty *tty, const char *buf, size_t len,
size_t *size)
{
struct client *c = tty->client;
u_int i;
char tmp[64];
int flags = 0;
*size = 0;
if (tty->flags & TTY_HAVEDSR)
return (-1);
/* First three bytes are always \033[. */
if (buf[0] != '\033')
return (-1);
if (len == 1)
return (1);
if (buf[1] != '[')
return (-1);
if (len == 2)
return (1);
if (buf[2] != 'I' && buf[2] != 'T')
return (-1);
if (len == 3)
return (1);
/* Copy the rest up to a 'n'. */
for (i = 0; i < (sizeof tmp) - 1 && buf[2 + i] != 'n'; i++) {
if (2 + i == len)
return (1);
tmp[i] = buf[2 + i];
}
if (i == (sizeof tmp) - 1)
return (-1);
tmp[i] = '\0';
*size = 3 + i;
/* Set terminal flags. */
if (strncmp(tmp, "ITERM2 ", 7) == 0)
flags |= (TERM_DECSLRM|TERM_256COLOURS|TERM_RGBCOLOURS);
if (strncmp(tmp, "TMUX ", 5) == 0)
flags |= (TERM_256COLOURS|TERM_RGBCOLOURS);
log_debug("%s: received DSR %.*s", c->name, (int)*size, buf);
tty_set_flags(tty, flags);
tty->flags |= TTY_HAVEDSR;
return (0);
}

View File

@ -529,11 +529,16 @@ tty_term_find(char *name, int fd, char **cause)
goto error;
}
/* Figure out if we have 256 colours (or more). */
if (tty_term_number(term, TTYC_COLORS) >= 256 ||
tty_term_has(term, TTYC_RGB))
/* Set flag if terminal has 256 colours. */
if (tty_term_number(term, TTYC_COLORS) >= 256)
term->flags |= TERM_256COLOURS;
/* Set flag if terminal has RGB colours. */
if ((tty_term_flag(term, TTYC_TC) || tty_term_has(term, TTYC_RGB)) ||
(tty_term_has(term, TTYC_SETRGBF) &&
tty_term_has(term, TTYC_SETRGBB)))
term->flags |= TERM_RGBCOLOURS;
/*
* Terminals without xenl (eat newline glitch) wrap at at $COLUMNS - 1
* rather than $COLUMNS (the cursor can never be beyond $COLUMNS - 1).
@ -545,7 +550,7 @@ tty_term_find(char *name, int fd, char **cause)
* do the best possible.
*/
if (!tty_term_flag(term, TTYC_XENL))
term->flags |= TERM_EARLYWRAP;
term->flags |= TERM_NOXENL;
/* Generate ACS table. If none is present, use nearest ASCII. */
memset(term->acs, 0, sizeof term->acs);
@ -568,22 +573,7 @@ tty_term_find(char *name, int fd, char **cause)
code->type = TTYCODE_STRING;
}
/*
* On terminals with RGB colour (Tc or RGB), fill in setrgbf and
* setrgbb if they are missing.
*/
if ((tty_term_flag(term, TTYC_TC) || tty_term_flag(term, TTYC_RGB)) &&
!tty_term_has(term, TTYC_SETRGBF) &&
!tty_term_has(term, TTYC_SETRGBB)) {
code = &term->codes[TTYC_SETRGBF];
code->value.string = xstrdup("\033[38;2;%p1%d;%p2%d;%p3%dm");
code->type = TTYCODE_STRING;
code = &term->codes[TTYC_SETRGBB];
code->value.string = xstrdup("\033[48;2;%p1%d;%p2%d;%p3%dm");
code->type = TTYCODE_STRING;
}
/* Log it. */
/* Log the capabilities. */
for (i = 0; i < tty_term_ncodes(); i++)
log_debug("%s%s", name, tty_term_describe(term, i));

73
tty.c
View File

@ -74,7 +74,7 @@ static void tty_default_attributes(struct tty *, struct window_pane *,
u_int);
#define tty_use_margin(tty) \
((tty)->term_type == TTY_VT420)
((tty->term->flags|tty->term_flags) & TERM_DECSLRM)
#define tty_pane_full_width(tty, ctx) \
((ctx)->xoff == 0 && screen_size_x((ctx)->wp->screen) >= (tty)->sx)
@ -115,9 +115,7 @@ tty_init(struct tty *tty, struct client *c, int fd, char *term)
tty->ccolour = xstrdup("");
tty->flags = 0;
tty->term_flags = 0;
tty->term_type = TTY_UNKNOWN;
return (0);
}
@ -292,11 +290,22 @@ tty_open(struct tty *tty, char **cause)
return (0);
}
static void
tty_start_timer_callback(__unused int fd, __unused short events, void *data)
{
struct tty *tty = data;
struct client *c = tty->client;
log_debug("%s: start timer fired", c->name);
tty->flags |= (TTY_HAVEDA|TTY_HAVEDSR);
}
void
tty_start_tty(struct tty *tty)
{
struct client *c = tty->client;
struct termios tio;
struct timeval tv = { .tv_sec = 1 };
if (tty->fd != -1 && tcgetattr(tty->fd, &tty->tio) == 0) {
setblocking(tty->fd, 0);
@ -334,8 +343,15 @@ tty_start_tty(struct tty *tty)
tty->flags |= TTY_FOCUS;
tty_puts(tty, "\033[?1004h");
}
tty_puts(tty, "\033[c");
}
if (~tty->flags & TTY_HAVEDA)
tty_puts(tty, "\033[c");
if (~tty->flags & TTY_HAVEDSR)
tty_puts(tty, "\033[1337n");
} else
tty->flags |= (TTY_HAVEDA|TTY_HAVEDSR);
evtimer_set(&tty->start_timer, tty_start_timer_callback, tty);
evtimer_add(&tty->start_timer, &tv);
tty->flags |= TTY_STARTED;
tty_invalidate(tty);
@ -357,6 +373,8 @@ tty_stop_tty(struct tty *tty)
return;
tty->flags &= ~TTY_STARTED;
evtimer_del(&tty->start_timer);
event_del(&tty->timer);
tty->flags &= ~TTY_BLOCK;
@ -443,9 +461,8 @@ tty_free(struct tty *tty)
}
void
tty_set_type(struct tty *tty, int type, int flags)
tty_set_flags(struct tty *tty, int flags)
{
tty->term_type = type;
tty->term_flags |= flags;
if (tty_use_margin(tty))
@ -549,7 +566,7 @@ tty_putc(struct tty *tty, u_char ch)
{
const char *acs;
if ((tty->term->flags & TERM_EARLYWRAP) &&
if ((tty->term->flags & TERM_NOXENL) &&
ch >= 0x20 && ch != 0x7f &&
tty->cy == tty->sy - 1 &&
tty->cx + 1 >= tty->sx)
@ -575,7 +592,7 @@ tty_putc(struct tty *tty, u_char ch)
* where we think it should be after a line wrap - this
* means it works on sensible terminals as well.
*/
if (tty->term->flags & TERM_EARLYWRAP)
if (tty->term->flags & TERM_NOXENL)
tty_putcode2(tty, TTYC_CUP, tty->cy, tty->cx);
} else
tty->cx++;
@ -585,7 +602,7 @@ tty_putc(struct tty *tty, u_char ch)
void
tty_putn(struct tty *tty, const void *buf, size_t len, u_int width)
{
if ((tty->term->flags & TERM_EARLYWRAP) &&
if ((tty->term->flags & TERM_NOXENL) &&
tty->cy == tty->sy - 1 &&
tty->cx + len >= tty->sx)
len = tty->sx - tty->cx - 1;
@ -1135,7 +1152,8 @@ tty_clear_area(struct tty *tty, struct window_pane *wp, u_int py, u_int ny,
* background colour isn't default (because it doesn't work
* after SGR 0).
*/
if (tty->term_type == TTY_VT420 && !COLOUR_DEFAULT(bg)) {
if (((tty->term->flags|tty->term_flags) & TERM_DECFRA) &&
!COLOUR_DEFAULT(bg)) {
xsnprintf(tmp, sizeof tmp, "\033[32;%u;%u;%u;%u$x",
py + 1, px + 1, py + ny, px + nx);
tty_puts(tty, tmp);
@ -1862,7 +1880,7 @@ tty_cmd_cells(struct tty *tty, const struct tty_ctx *ctx)
ctx->xoff + ctx->ocx + ctx->num > ctx->ox + ctx->sx)) {
if (!ctx->wrapped ||
!tty_pane_full_width(tty, ctx) ||
(tty->term->flags & TERM_EARLYWRAP) ||
(tty->term->flags & TERM_NOXENL) ||
ctx->xoff + ctx->ocx != 0 ||
ctx->yoff + ctx->ocy != tty->cy + 1 ||
tty->cx < tty->sx ||
@ -1957,7 +1975,7 @@ tty_cell(struct tty *tty, const struct grid_cell *gc, struct window_pane *wp)
const struct grid_cell *gcp;
/* Skip last character if terminal is stupid. */
if ((tty->term->flags & TERM_EARLYWRAP) &&
if ((tty->term->flags & TERM_NOXENL) &&
tty->cy == tty->sy - 1 &&
tty->cx == tty->sx - 1)
return;
@ -2116,7 +2134,7 @@ tty_cursor_pane_unless_wrap(struct tty *tty, const struct tty_ctx *ctx,
{
if (!ctx->wrapped ||
!tty_pane_full_width(tty, ctx) ||
(tty->term->flags & TERM_EARLYWRAP) ||
(tty->term->flags & TERM_NOXENL) ||
ctx->xoff + cx != 0 ||
ctx->yoff + cy != tty->cy + 1 ||
tty->cx < tty->sx ||
@ -2453,11 +2471,10 @@ tty_check_fg(struct tty *tty, struct window_pane *wp, struct grid_cell *gc)
/* Is this a 24-bit colour? */
if (gc->fg & COLOUR_FLAG_RGB) {
/* Not a 24-bit terminal? Translate to 256-colour palette. */
if (!tty_term_has(tty->term, TTYC_SETRGBF)) {
colour_split_rgb(gc->fg, &r, &g, &b);
gc->fg = colour_find_rgb(r, g, b);
} else
if ((tty->term->flags|tty->term_flags) & TERM_RGBCOLOURS)
return;
colour_split_rgb(gc->fg, &r, &g, &b);
gc->fg = colour_find_rgb(r, g, b);
}
/* How many colours does this terminal have? */
@ -2503,11 +2520,10 @@ tty_check_bg(struct tty *tty, struct window_pane *wp, struct grid_cell *gc)
/* Is this a 24-bit colour? */
if (gc->bg & COLOUR_FLAG_RGB) {
/* Not a 24-bit terminal? Translate to 256-colour palette. */
if (!tty_term_has(tty->term, TTYC_SETRGBB)) {
colour_split_rgb(gc->bg, &r, &g, &b);
gc->bg = colour_find_rgb(r, g, b);
} else
if ((tty->term->flags|tty->term_flags) & TERM_RGBCOLOURS)
return;
colour_split_rgb(gc->bg, &r, &g, &b);
gc->bg = colour_find_rgb(r, g, b);
}
/* How many colours does this terminal have? */
@ -2684,15 +2700,14 @@ tty_try_colour(struct tty *tty, int colour, const char *type)
}
if (colour & COLOUR_FLAG_RGB) {
colour_split_rgb(colour & 0xffffff, &r, &g, &b);
if (*type == '3') {
if (!tty_term_has(tty->term, TTYC_SETRGBF))
return (-1);
colour_split_rgb(colour & 0xffffff, &r, &g, &b);
goto fallback_rgb;
tty_putcode3(tty, TTYC_SETRGBF, r, g, b);
} else {
if (!tty_term_has(tty->term, TTYC_SETRGBB))
return (-1);
colour_split_rgb(colour & 0xffffff, &r, &g, &b);
goto fallback_rgb;
tty_putcode3(tty, TTYC_SETRGBB, r, g, b);
}
return (0);
@ -2705,6 +2720,12 @@ fallback_256:
log_debug("%s: 256 colour fallback: %s", tty->client->name, s);
tty_puts(tty, s);
return (0);
fallback_rgb:
xsnprintf(s, sizeof s, "\033[%s;2;%d;%d;%dm", type, r, g, b);
log_debug("%s: RGB colour fallback: %s", tty->client->name, s);
tty_puts(tty, s);
return (0);
}
static void

View File

@ -210,7 +210,7 @@ window_client_draw(__unused void *modedata, void *itemdata,
struct window_pane *wp;
u_int cx = s->cx, cy = s->cy, lines, at;
if (c->session == NULL || (c->flags & (CLIENT_DEAD|CLIENT_DETACHING)))
if (c->session == NULL || (c->flags & CLIENT_UNATTACHEDFLAGS))
return;
wp = c->session->curw->window->active;

View File

@ -1239,7 +1239,7 @@ window_pane_reset_mode_all(struct window_pane *wp)
window_pane_reset_mode(wp);
}
void
int
window_pane_key(struct window_pane *wp, struct client *c, struct session *s,
struct winlink *wl, key_code key, struct mouse_event *m)
{
@ -1247,23 +1247,24 @@ window_pane_key(struct window_pane *wp, struct client *c, struct session *s,
struct window_pane *wp2;
if (KEYC_IS_MOUSE(key) && m == NULL)
return;
return (-1);
wme = TAILQ_FIRST(&wp->modes);
if (wme != NULL) {
wp->modelast = time(NULL);
if (wme->mode->key != NULL)
wme->mode->key(wme, c, s, wl, (key & ~KEYC_XTERM), m);
return;
return (0);
}
if (wp->fd == -1 || wp->flags & PANE_INPUTOFF)
return;
return (0);
input_key(wp, key, m);
if (input_key(wp, key, m) != 0)
return (-1);
if (KEYC_IS_MOUSE(key))
return;
return (0);
if (options_get_number(wp->window->options, "synchronize-panes")) {
TAILQ_FOREACH(wp2, &wp->window->panes, entry) {
if (wp2 != wp &&
@ -1274,6 +1275,7 @@ window_pane_key(struct window_pane *wp, struct client *c, struct session *s,
input_key(wp2, key, NULL);
}
}
return (0);
}
int