Merge branch 'master' into sixel

This commit is contained in:
topcat001 2022-08-28 13:43:07 -07:00
commit a82f14c7b2
72 changed files with 2110 additions and 509 deletions

View File

@ -4,8 +4,14 @@ on:
schedule:
- cron: '0 0 * * *'
permissions:
contents: read
jobs:
lock:
permissions:
issues: write # for dessant/lock-threads to lock issues
pull-requests: write # for dessant/lock-threads to lock PRs
runs-on: ubuntu-latest
steps:
- uses: dessant/lock-threads@v2

61
CHANGES
View File

@ -1,5 +1,62 @@
CHANGES FROM 3.3 TO 3.3a
* Do not crash when run-shell produces output from a config file.
* Do not unintentionally turn off all mouse mode when button mode is also
present.
CHANGES FROM 3.2a TO 3.3
* Add an ACL list for users connecting to the tmux socket. Users may be
forbidden from attaching, forced to attach read-only, or allowed to attach
read-write. A new command, server-access, configures the list. File system
permissions must still be configured manually.
* Emit window-layout-changed on swap-pane.
* Better error reporting when applying custom layouts.
* Handle ANSI escape sequences in run-shell output.
* Add pane_start_path to match start_command.
* Set PWD so shells have a hint about the real path.
* Do not allow pipe-pane on dead panes.
* Do not report mouse positions (incorrectly) above the maximum of 223 in
normal mouse mode.
* Add an option (default off) to control the passthrough escape sequence.
* Support more mouse buttons when the terminal sends them.
* Add a window-resized hook which is fired when the window is actually resized
which may be later than the client resize.
* Add next_session_id format with the next session ID.
* Add formats for client and server UID and user.
* Add argument to refresh-client -l to forward clipboard to a pane.
* Add remain-on-exit-format to set text shown when pane is dead.
* With split-window -f use percentages of window size not pane size.
* Add an option (fill-character) to set the character used for unused areas of
a client.
* Add an option (scroll-on-clear) to control if tmux scrolls into history on
clear.
* Add a capability for OSC 7 and use it similarly to how the title is set (and
controlled by the same set-titles option).
* Add support for systemd socket activation (where systemd creates the Unix
domain socket for tmux rather than tmux creating it). Build with
--enable-systemd.
* Add an option (pane-border-indicators) to select how the active pane is shown
on the pane border (colour, arrows or both).
@ -1245,7 +1302,7 @@ Incompatible Changes
bind -Tcopy-mode C-r command-prompt -i -p'search up' "send -X search-backward-incremental '%%'"
There are also some new commmands available with send -X, such as
There are also some new commands available with send -X, such as
copy-pipe-and-cancel.
* set-remain-on-exit has gone -- can be achieved with hooks instead.
* Hooks: before hooks have been removed and only a selection of commands now
@ -3221,7 +3278,7 @@ The list of older changes is below.
* (nicm) -n on new-session is now -s, and -n is now the initial window name.
This was documented but not implemented :-/.
* (nicm) kill-window command, bound to & by default (because it should be hard
to hit accidently).
to hit accidentally).
* (nicm) bell-style option with three choices: "none" completely ignore bell;
"any" pass through a bell in any window to current; "current" ignore bells
except in current window. This applies only to the bell terminal signal,

View File

@ -123,6 +123,7 @@ dist_tmux_SOURCES = \
cmd-select-pane.c \
cmd-select-window.c \
cmd-send-keys.c \
cmd-server-access.c \
cmd-set-buffer.c \
cmd-set-environment.c \
cmd-set-option.c \
@ -149,6 +150,7 @@ dist_tmux_SOURCES = \
grid-reader.c \
grid-view.c \
grid.c \
hyperlinks.c \
input-keys.c \
input.c \
image.c \
@ -173,6 +175,7 @@ dist_tmux_SOURCES = \
screen-redraw.c \
screen-write.c \
screen.c \
server-acl.c \
server-client.c \
server-fn.c \
server.c \

View File

@ -831,6 +831,12 @@ args_strtonum(struct args *args, u_char flag, long long minval,
return (0);
}
value = TAILQ_LAST(&entry->values, args_values);
if (value == NULL ||
value->type != ARGS_STRING ||
value->string == NULL) {
*cause = xstrdup("missing");
return (0);
}
ll = strtonum(value->string, minval, maxval, &errstr);
if (errstr != NULL) {
@ -842,6 +848,41 @@ args_strtonum(struct args *args, u_char flag, long long minval,
return (ll);
}
/* Convert an argument value to a number, and expand formats. */
long long
args_strtonum_and_expand(struct args *args, u_char flag, long long minval,
long long maxval, struct cmdq_item *item, char **cause)
{
const char *errstr;
char *formatted;
long long ll;
struct args_entry *entry;
struct args_value *value;
if ((entry = args_find(args, flag)) == NULL) {
*cause = xstrdup("missing");
return (0);
}
value = TAILQ_LAST(&entry->values, args_values);
if (value == NULL ||
value->type != ARGS_STRING ||
value->string == NULL) {
*cause = xstrdup("missing");
return (0);
}
formatted = format_single_from_target(item, value->string);
ll = strtonum(formatted, minval, maxval, &errstr);
free(formatted);
if (errstr != NULL) {
*cause = xstrdup(errstr);
return (0);
}
*cause = NULL;
return (ll);
}
/* Convert an argument to a number which may be a percentage. */
long long
args_percentage(struct args *args, u_char flag, long long minval,
@ -854,6 +895,10 @@ args_percentage(struct args *args, u_char flag, long long minval,
*cause = xstrdup("missing");
return (0);
}
if (TAILQ_EMPTY(&entry->values)) {
*cause = xstrdup("empty");
return (0);
}
value = TAILQ_LAST(&entry->values, args_values)->string;
return (args_string_percentage(value, minval, maxval, curval, cause));
}
@ -868,6 +913,10 @@ args_string_percentage(const char *value, long long minval, long long maxval,
size_t valuelen = strlen(value);
char *copy;
if (valuelen == 0) {
*cause = xstrdup("empty");
return (0);
}
if (value[valuelen - 1] == '%') {
copy = xstrdup(value);
copy[valuelen - 1] = '\0';
@ -898,3 +947,74 @@ args_string_percentage(const char *value, long long minval, long long maxval,
*cause = NULL;
return (ll);
}
/*
* Convert an argument to a number which may be a percentage, and expand
* formats.
*/
long long
args_percentage_and_expand(struct args *args, u_char flag, long long minval,
long long maxval, long long curval, struct cmdq_item *item, char **cause)
{
const char *value;
struct args_entry *entry;
if ((entry = args_find(args, flag)) == NULL) {
*cause = xstrdup("missing");
return (0);
}
if (TAILQ_EMPTY(&entry->values)) {
*cause = xstrdup("empty");
return (0);
}
value = TAILQ_LAST(&entry->values, args_values)->string;
return (args_string_percentage_and_expand(value, minval, maxval, curval,
item, cause));
}
/*
* Convert a string to a number which may be a percentage, and expand formats.
*/
long long
args_string_percentage_and_expand(const char *value, long long minval,
long long maxval, long long curval, struct cmdq_item *item, char **cause)
{
const char *errstr;
long long ll;
size_t valuelen = strlen(value);
char *copy, *f;
if (value[valuelen - 1] == '%') {
copy = xstrdup(value);
copy[valuelen - 1] = '\0';
f = format_single_from_target(item, copy);
ll = strtonum(f, 0, 100, &errstr);
free(f);
free(copy);
if (errstr != NULL) {
*cause = xstrdup(errstr);
return (0);
}
ll = (curval * ll) / 100;
if (ll < minval) {
*cause = xstrdup("too small");
return (0);
}
if (ll > maxval) {
*cause = xstrdup("too large");
return (0);
}
} else {
f = format_single_from_target(item, value);
ll = strtonum(f, minval, maxval, &errstr);
free(f);
if (errstr != NULL) {
*cause = xstrdup(errstr);
return (0);
}
}
*cause = NULL;
return (ll);
}

26
cfg.c
View File

@ -51,8 +51,7 @@ cfg_done(__unused struct cmdq_item *item, __unused void *data)
return (CMD_RETURN_NORMAL);
cfg_finished = 1;
if (!RB_EMPTY(&sessions))
cfg_show_causes(RB_MIN(sessions, &sessions));
cfg_show_causes(NULL);
if (cfg_item != NULL)
cmdq_continue(cfg_item);
@ -238,11 +237,29 @@ cfg_print_causes(struct cmdq_item *item)
void
cfg_show_causes(struct session *s)
{
struct client *c = TAILQ_FIRST(&clients);
struct window_pane *wp;
struct window_mode_entry *wme;
u_int i;
if (s == NULL || cfg_ncauses == 0)
if (cfg_ncauses == 0)
return;
if (c != NULL && (c->flags & CLIENT_CONTROL)) {
for (i = 0; i < cfg_ncauses; i++) {
control_write(c, "%%config-error %s", cfg_causes[i]);
free(cfg_causes[i]);
}
goto out;
}
if (s == NULL) {
if (c != NULL && c->session != NULL)
s = c->session;
else
s = RB_MIN(sessions, &sessions);
}
if (s == NULL || s->attached == 0) /* wait for an attached session */
return;
wp = s->curw->window->active;
@ -250,10 +267,11 @@ cfg_show_causes(struct session *s)
if (wme == NULL || wme->mode != &window_view_mode)
window_pane_set_mode(wp, NULL, &window_view_mode, NULL, NULL);
for (i = 0; i < cfg_ncauses; i++) {
window_copy_add(wp, "%s", cfg_causes[i]);
window_copy_add(wp, 0, "%s", cfg_causes[i]);
free(cfg_causes[i]);
}
out:
free(cfg_causes);
cfg_causes = NULL;
cfg_ncauses = 0;

View File

@ -361,6 +361,7 @@ client_main(struct event_base *base, int argc, char **argv, uint64_t flags,
/* Send identify messages. */
client_send_identify(ttynam, termname, caps, ncaps, cwd, feat);
tty_term_free_list(caps, ncaps);
proc_flush_peer(client_peer);
/* Send first command. */
if (msg == MSG_COMMAND) {

View File

@ -43,7 +43,7 @@ const struct cmd_entry cmd_attach_session_entry = {
/* -t is special */
.flags = CMD_STARTSERVER,
.flags = CMD_STARTSERVER|CMD_READONLY,
.exec = cmd_attach_session_exec
};
@ -69,6 +69,7 @@ cmd_attach_session(struct cmdq_item *item, const char *tflag, int dflag,
if (c == NULL)
return (CMD_RETURN_NORMAL);
if (server_client_check_nested(c)) {
cmdq_error(item, "sessions should be nested with care, "
"unset $TMUX to force");
@ -157,6 +158,9 @@ cmd_attach_session(struct cmdq_item *item, const char *tflag, int dflag,
c->flags |= CLIENT_ATTACHED;
}
if (cfg_finished)
cfg_show_causes(s);
return (CMD_RETURN_NORMAL);
}

View File

@ -53,8 +53,8 @@ const struct cmd_entry cmd_clear_history_entry = {
.name = "clear-history",
.alias = "clearhist",
.args = { "t:", 0, 0, NULL },
.usage = CMD_TARGET_PANE_USAGE,
.args = { "Ht:", 0, 0, NULL },
.usage = "[-H] " CMD_TARGET_PANE_USAGE,
.target = { 't', CMD_FIND_PANE, 0 },
@ -133,7 +133,8 @@ cmd_capture_pane_history(struct args *args, struct cmdq_item *item,
if (Sflag != NULL && strcmp(Sflag, "-") == 0)
top = 0;
else {
n = args_strtonum(args, 'S', INT_MIN, SHRT_MAX, &cause);
n = args_strtonum_and_expand(args, 'S', INT_MIN, SHRT_MAX,
item, &cause);
if (cause != NULL) {
top = gd->hsize;
free(cause);
@ -149,7 +150,8 @@ cmd_capture_pane_history(struct args *args, struct cmdq_item *item,
if (Eflag != NULL && strcmp(Eflag, "-") == 0)
bottom = gd->hsize + gd->sy - 1;
else {
n = args_strtonum(args, 'E', INT_MIN, SHRT_MAX, &cause);
n = args_strtonum_and_expand(args, 'E', INT_MIN, SHRT_MAX,
item, &cause);
if (cause != NULL) {
bottom = gd->hsize + gd->sy - 1;
free(cause);
@ -175,7 +177,7 @@ cmd_capture_pane_history(struct args *args, struct cmdq_item *item,
buf = NULL;
for (i = top; i <= bottom; i++) {
line = grid_string_cells(gd, 0, i, sx, &gc, with_codes,
escape_c0, !join_lines && !no_trim);
escape_c0, !join_lines && !no_trim, wp->screen);
linelen = strlen(line);
buf = cmd_capture_pane_append(buf, len, line, linelen);
@ -202,6 +204,8 @@ cmd_capture_pane_exec(struct cmd *self, struct cmdq_item *item)
if (cmd_get_entry(self) == &cmd_clear_history_entry) {
window_pane_reset_mode_all(wp);
grid_clear_history(wp->base.grid);
if (args_has(args, 'H'))
screen_reset_hyperlinks(wp->screen);
return (CMD_RETURN_NORMAL);
}

View File

@ -100,7 +100,7 @@ cmd_choose_tree_exec(struct cmd *self, struct cmdq_item *item)
const struct window_mode *mode;
if (cmd_get_entry(self) == &cmd_choose_buffer_entry) {
if (paste_get_top(NULL) == NULL)
if (paste_is_empty())
return (CMD_RETURN_NORMAL);
mode = &window_buffer_mode;
} else if (cmd_get_entry(self) == &cmd_choose_client_entry) {

View File

@ -112,7 +112,7 @@ cmd_command_prompt_exec(struct cmd *self, struct cmdq_item *item)
}
next_prompt = prompts;
} else
next_prompt = prompts = xstrdup (s);
next_prompt = prompts = xstrdup(s);
if ((s = args_get(args, 'I')) != NULL)
next_input = inputs = xstrdup(s);
else

View File

@ -144,7 +144,7 @@ cmd_display_panes_draw_pane(struct screen_redraw_ctx *ctx,
llen = 0;
if (sx < len * 6 || sy < 5) {
tty_attributes(tty, &fgc, &grid_default_cell, NULL);
tty_attributes(tty, &fgc, &grid_default_cell, NULL, NULL);
if (sx >= len + llen + 1) {
len += llen + 1;
tty_cursor(tty, xoff + px - len / 2, yoff + py);
@ -161,7 +161,7 @@ cmd_display_panes_draw_pane(struct screen_redraw_ctx *ctx,
px -= len * 3;
py -= 2;
tty_attributes(tty, &bgc, &grid_default_cell, NULL);
tty_attributes(tty, &bgc, &grid_default_cell, NULL, NULL);
for (ptr = buf; *ptr != '\0'; ptr++) {
if (*ptr < '0' || *ptr > '9')
continue;
@ -179,7 +179,7 @@ cmd_display_panes_draw_pane(struct screen_redraw_ctx *ctx,
if (sy <= 6)
goto out;
tty_attributes(tty, &fgc, &grid_default_cell, NULL);
tty_attributes(tty, &fgc, &grid_default_cell, NULL, NULL);
if (rlen != 0 && sx >= rlen) {
tty_cursor(tty, xoff + sx - rlen, yoff);
tty_putn(tty, rbuf, rlen, rlen);

View File

@ -71,10 +71,11 @@ cmd_join_pane_exec(struct cmd *self, struct cmdq_item *item)
struct window *src_w, *dst_w;
struct window_pane *src_wp, *dst_wp;
char *cause = NULL;
int size, percentage, dst_idx;
int size, dst_idx;
int flags;
enum layout_type type;
struct layout_cell *lc;
u_int curval = 0;
dst_s = target->s;
dst_wl = target->wl;
@ -97,23 +98,30 @@ cmd_join_pane_exec(struct cmd *self, struct cmdq_item *item)
if (args_has(args, 'h'))
type = LAYOUT_LEFTRIGHT;
/* If the 'p' flag is dropped then this bit can be moved into 'l'. */
if (args_has(args, 'l') || args_has(args, 'p')) {
if (args_has(args, 'f')) {
if (type == LAYOUT_TOPBOTTOM)
curval = dst_w->sy;
else
curval = dst_w->sx;
} else {
if (type == LAYOUT_TOPBOTTOM)
curval = dst_wp->sy;
else
curval = dst_wp->sx;
}
}
size = -1;
if (args_has(args, 'l')) {
if (type == LAYOUT_TOPBOTTOM) {
size = args_percentage(args, 'l', 0, INT_MAX,
dst_wp->sy, &cause);
} else {
size = args_percentage(args, 'l', 0, INT_MAX,
dst_wp->sx, &cause);
}
size = args_percentage_and_expand(args, 'l', 0, INT_MAX, curval,
item, &cause);
} else if (args_has(args, 'p')) {
percentage = args_strtonum(args, 'p', 0, 100, &cause);
if (cause == NULL) {
if (type == LAYOUT_TOPBOTTOM)
size = (dst_wp->sy * percentage) / 100;
else
size = (dst_wp->sx * percentage) / 100;
}
size = args_strtonum_and_expand(args, 'l', 0, 100, item,
&cause);
if (cause == NULL)
size = curval * size / 100;
}
if (cause != NULL) {
cmdq_error(item, "size %s", cause);

View File

@ -77,7 +77,7 @@ cmd_load_buffer_done(__unused struct client *c, const char *path, int error,
} else if (tc != NULL &&
tc->session != NULL &&
(~tc->flags & CLIENT_DEAD))
tty_set_selection(&tc->tty, copy, bsize);
tty_set_selection(&tc->tty, "", copy, bsize);
if (tc != NULL)
server_client_unref(tc);
}

View File

@ -333,13 +333,6 @@ cmd_new_session_exec(struct cmd *self, struct cmdq_item *item)
server_client_set_key_table(c, NULL);
}
/*
* If there are still configuration file errors to display, put the new
* session's current window into more mode and display them now.
*/
if (cfg_finished)
cfg_show_causes(s);
/* Print if requested. */
if (args_has(args, 'P')) {
if ((template = args_get(args, 'F')) == NULL)
@ -357,6 +350,9 @@ cmd_new_session_exec(struct cmd *self, struct cmdq_item *item)
cmd_find_from_session(&fs, s, 0);
cmdq_insert_hook(s, item, &fs, "after-new-session");
if (cfg_finished)
cfg_show_causes(s);
if (sc.argv != NULL)
cmd_free_argv(sc.argc, sc.argv);
free(cwd);

View File

@ -67,6 +67,12 @@ cmd_pipe_pane_exec(struct cmd *self, struct cmdq_item *item)
struct format_tree *ft;
sigset_t set, oldset;
/* Do nothing if pane is dead. */
if (wp->fd == -1 || (wp->flags & PANE_EXITED)) {
cmdq_error(item, "target pane has exited");
return (CMD_RETURN_ERROR);
}
/* Destroy the old pipe. */
old_fd = wp->pipe_fd;
if (wp->pipe_fd != -1) {

View File

@ -19,9 +19,11 @@
#include <sys/types.h>
#include <ctype.h>
#include <pwd.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <unistd.h>
#include "tmux.h"
@ -124,7 +126,7 @@ cmdq_new(void)
{
struct cmdq_list *queue;
queue = xcalloc (1, sizeof *queue);
queue = xcalloc(1, sizeof *queue);
TAILQ_INIT (&queue->list);
return (queue);
}
@ -558,17 +560,31 @@ cmdq_add_message(struct cmdq_item *item)
{
struct client *c = item->client;
struct cmdq_state *state = item->state;
const char *name, *key;
const char *key;
char *tmp;
uid_t uid;
struct passwd *pw;
char *user = NULL;
tmp = cmd_print(item->cmd);
if (c != NULL) {
name = c->name;
uid = proc_get_peer_uid(c->peer);
if (uid != (uid_t)-1 && uid != getuid()) {
if ((pw = getpwuid(uid)) != NULL)
xasprintf(&user, "[%s]", pw->pw_name);
else
user = xstrdup("[unknown]");
} else
user = xstrdup("");
if (c->session != NULL && state->event.key != KEYC_NONE) {
key = key_string_lookup_key(state->event.key, 0);
server_add_message("%s key %s: %s", name, key, tmp);
} else
server_add_message("%s command: %s", name, tmp);
server_add_message("%s%s key %s: %s", c->name, user,
key, tmp);
} else {
server_add_message("%s%s command: %s", c->name, user,
tmp);
}
free(user);
} else
server_add_message("command: %s", tmp);
free(tmp);
@ -840,7 +856,7 @@ cmdq_print(struct cmdq_item *item, const char *fmt, ...)
window_pane_set_mode(wp, NULL, &window_view_mode, NULL,
NULL);
}
window_copy_add(wp, "%s", msg);
window_copy_add(wp, 0, "%s", msg);
}
free(msg);

View File

@ -185,7 +185,7 @@ cmd_refresh_client_clipboard(struct cmd *self, struct cmdq_item *item)
}
if (i != tc->clipboard_npanes)
return (CMD_RETURN_NORMAL);
tc->clipboard_panes = xreallocarray (tc->clipboard_panes,
tc->clipboard_panes = xreallocarray(tc->clipboard_panes,
tc->clipboard_npanes + 1, sizeof *tc->clipboard_panes);
tc->clipboard_panes[tc->clipboard_npanes++] = fs.wp->id;
}

View File

@ -84,22 +84,17 @@ cmd_run_shell_print(struct job *job, const char *msg)
if (cdata->wp_id != -1)
wp = window_pane_find_by_id(cdata->wp_id);
if (wp == NULL) {
if (cdata->item != NULL) {
cmdq_print(cdata->item, "%s", msg);
return;
}
if (cmd_find_from_nothing(&fs, 0) != 0)
return;
if (wp == NULL && cdata->item != NULL && cdata->client != NULL)
wp = server_client_get_pane(cdata->client);
if (wp == NULL && cmd_find_from_nothing(&fs, 0) == 0)
wp = fs.wp;
if (wp == NULL)
return;
}
if (wp == NULL)
return;
wme = TAILQ_FIRST(&wp->modes);
if (wme == NULL || wme->mode != &window_view_mode)
window_pane_set_mode(wp, NULL, &window_view_mode, NULL, NULL);
window_copy_add(wp, "%s", msg);
window_copy_add(wp, 1, "%s", msg);
}
static enum cmd_retval
@ -227,7 +222,8 @@ cmd_run_shell_callback(struct job *job)
int retcode, status;
do {
if ((line = evbuffer_readline(event->input)) != NULL) {
line = evbuffer_readln(event->input, NULL, EVBUFFER_EOL_LF);
if (line != NULL) {
cmd_run_shell_print(job, line);
free(line);
}

View File

@ -77,7 +77,7 @@ cmd_select_layout_exec(struct cmd *self, struct cmdq_item *item)
struct window *w = wl->window;
struct window_pane *wp = target->wp;
const char *layoutname;
char *oldlayout;
char *oldlayout, *cause;
int next, previous, layout;
server_unzoom_window(w);
@ -124,8 +124,9 @@ cmd_select_layout_exec(struct cmd *self, struct cmdq_item *item)
}
if (layoutname != NULL) {
if (layout_parse(w, layoutname) == -1) {
cmdq_error(item, "can't set layout: %s", layoutname);
if (layout_parse(w, layoutname, &cause) == -1) {
cmdq_error(item, "%s: %s", cause, layoutname);
free(cause);
goto error;
}
goto changed;

View File

@ -151,7 +151,8 @@ cmd_send_keys_exec(struct cmd *self, struct cmdq_item *item)
char *cause = NULL;
if (args_has(args, 'N')) {
np = args_strtonum(args, 'N', 1, UINT_MAX, &cause);
np = args_strtonum_and_expand(args, 'N', 1, UINT_MAX, item,
&cause);
if (cause != NULL) {
cmdq_error(item, "repeat count %s", cause);
free(cause);

147
cmd-server-access.c Normal file
View File

@ -0,0 +1,147 @@
/* $OpenBSD$ */
/*
* Copyright (c) 2021 Dallas Lyons <dallasdlyons@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/stat.h>
#include <sys/types.h>
#include <pwd.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include "tmux.h"
/*
* Controls access to session.
*/
static enum cmd_retval cmd_server_access_exec(struct cmd *, struct cmdq_item *);
const struct cmd_entry cmd_server_access_entry = {
.name = "server-access",
.alias = NULL,
.args = { "adlrw", 0, 1, NULL },
.usage = "[-adlrw] " CMD_TARGET_PANE_USAGE " [user]",
.flags = CMD_CLIENT_CANFAIL,
.exec = cmd_server_access_exec
};
static enum cmd_retval
cmd_server_access_deny(struct cmdq_item *item, struct passwd *pw)
{
struct client *loop;
struct server_acl_user *user;
uid_t uid;
if ((user = server_acl_user_find(pw->pw_uid)) == NULL) {
cmdq_error(item, "user %s not found", pw->pw_name);
return (CMD_RETURN_ERROR);
}
TAILQ_FOREACH(loop, &clients, entry) {
uid = proc_get_peer_uid(loop->peer);
if (uid == server_acl_get_uid(user)) {
loop->exit_message = xstrdup("access not allowed");
loop->flags |= CLIENT_EXIT;
}
}
server_acl_user_deny(pw->pw_uid);
return (CMD_RETURN_NORMAL);
}
static enum cmd_retval
cmd_server_access_exec(struct cmd *self, struct cmdq_item *item)
{
struct args *args = cmd_get_args(self);
struct client *c = cmdq_get_target_client(item);
char *name;
struct passwd *pw = NULL;
if (args_has(args, 'l')) {
server_acl_display(item);
return (CMD_RETURN_NORMAL);
}
if (args_count(args) == 0) {
cmdq_error(item, "missing user argument");
return (CMD_RETURN_ERROR);
}
name = format_single(item, args_string(args, 0), c, NULL, NULL, NULL);
if (*name != '\0')
pw = getpwnam(name);
if (pw == NULL) {
cmdq_error(item, "unknown user: %s", name);
return (CMD_RETURN_ERROR);
}
free(name);
if (pw->pw_uid == 0 || pw->pw_uid == getuid()) {
cmdq_error(item, "%s owns the server, can't change access",
pw->pw_name);
return (CMD_RETURN_ERROR);
}
if (args_has(args, 'a') && args_has(args, 'd')) {
cmdq_error(item, "-a and -d cannot be used together");
return (CMD_RETURN_ERROR);
}
if (args_has(args, 'w') && args_has(args, 'r')) {
cmdq_error(item, "-r and -w cannot be used together");
return (CMD_RETURN_ERROR);
}
if (args_has(args, 'd'))
return (cmd_server_access_deny(item, pw));
if (args_has(args, 'a')) {
if (server_acl_user_find(pw->pw_uid) != NULL) {
cmdq_error(item, "user %s is already added",
pw->pw_name);
return (CMD_RETURN_ERROR);
}
server_acl_user_allow(pw->pw_uid);
/* Do not return - allow -r or -w with -a. */
} else if (args_has(args, 'r') || args_has(args, 'w')) {
/* -r or -w implies -a if user does not exist. */
if (server_acl_user_find(pw->pw_uid) == NULL)
server_acl_user_allow(pw->pw_uid);
}
if (args_has(args, 'w')) {
if (server_acl_user_find(pw->pw_uid) == NULL) {
cmdq_error(item, "user %s not found", pw->pw_name);
return (CMD_RETURN_ERROR);
}
server_acl_user_allow_write(pw->pw_uid);
return (CMD_RETURN_NORMAL);
}
if (args_has(args, 'r')) {
if (server_acl_user_find(pw->pw_uid) == NULL) {
cmdq_error(item, "user %s not found", pw->pw_name);
return (CMD_RETURN_ERROR);
}
server_acl_user_deny_write(pw->pw_uid);
return (CMD_RETURN_NORMAL);
}
return (CMD_RETURN_NORMAL);
}

View File

@ -69,8 +69,13 @@ cmd_set_buffer_exec(struct cmd *self, struct cmdq_item *item)
pb = paste_get_name(bufname);
if (cmd_get_entry(self) == &cmd_delete_buffer_entry) {
if (pb == NULL)
if (pb == NULL) {
if (bufname != NULL) {
cmdq_error(item, "unknown buffer: %s", bufname);
return (CMD_RETURN_ERROR);
}
pb = paste_get_top(&bufname);
}
if (pb == NULL) {
cmdq_error(item, "no buffer");
return (CMD_RETURN_ERROR);
@ -80,8 +85,13 @@ cmd_set_buffer_exec(struct cmd *self, struct cmdq_item *item)
}
if (args_has(args, 'n')) {
if (pb == NULL)
if (pb == NULL) {
if (bufname != NULL) {
cmdq_error(item, "unknown buffer: %s", bufname);
return (CMD_RETURN_ERROR);
}
pb = paste_get_top(&bufname);
}
if (pb == NULL) {
cmdq_error(item, "no buffer");
return (CMD_RETURN_ERROR);
@ -121,7 +131,7 @@ cmd_set_buffer_exec(struct cmd *self, struct cmdq_item *item)
return (CMD_RETURN_ERROR);
}
if (args_has(args, 'w') && tc != NULL)
tty_set_selection(&tc->tty, bufdata, bufsize);
tty_set_selection(&tc->tty, "", bufdata, bufsize);
return (CMD_RETURN_NORMAL);
}

View File

@ -65,67 +65,46 @@ cmd_split_window_exec(struct cmd *self, struct cmdq_item *item)
enum layout_type type;
struct layout_cell *lc;
struct cmd_find_state fs;
int size, percentage, flags, input;
const char *template, *errstr, *p;
char *cause, *cp, *copy;
size_t plen;
int size, flags, input;
const char *template;
char *cause = NULL, *cp;
struct args_value *av;
u_int count = args_count(args);
u_int count = args_count(args), curval = 0;
type = LAYOUT_TOPBOTTOM;
if (args_has(args, 'h'))
type = LAYOUT_LEFTRIGHT;
else
type = LAYOUT_TOPBOTTOM;
if ((p = args_get(args, 'l')) != NULL) {
plen = strlen(p);
if (p[plen - 1] == '%') {
copy = xstrdup(p);
copy[plen - 1] = '\0';
percentage = strtonum(copy, 0, INT_MAX, &errstr);
free(copy);
if (errstr != NULL) {
cmdq_error(item, "percentage %s", errstr);
return (CMD_RETURN_ERROR);
}
if (args_has(args, 'f')) {
if (type == LAYOUT_TOPBOTTOM)
size = (w->sy * percentage) / 100;
else
size = (w->sx * percentage) / 100;
} else {
if (type == LAYOUT_TOPBOTTOM)
size = (wp->sy * percentage) / 100;
else
size = (wp->sx * percentage) / 100;
}
} else {
size = args_strtonum(args, 'l', 0, INT_MAX, &cause);
if (cause != NULL) {
cmdq_error(item, "lines %s", cause);
free(cause);
return (CMD_RETURN_ERROR);
}
}
} else if (args_has(args, 'p')) {
percentage = args_strtonum(args, 'p', 0, INT_MAX, &cause);
if (cause != NULL) {
cmdq_error(item, "create pane failed: -p %s", cause);
free(cause);
return (CMD_RETURN_ERROR);
}
/* If the 'p' flag is dropped then this bit can be moved into 'l'. */
if (args_has(args, 'l') || args_has(args, 'p')) {
if (args_has(args, 'f')) {
if (type == LAYOUT_TOPBOTTOM)
size = (w->sy * percentage) / 100;
curval = w->sy;
else
size = (w->sx * percentage) / 100;
curval = w->sx;
} else {
if (type == LAYOUT_TOPBOTTOM)
size = (wp->sy * percentage) / 100;
curval = wp->sy;
else
size = (wp->sx * percentage) / 100;
curval = wp->sx;
}
} else
size = -1;
}
size = -1;
if (args_has(args, 'l')) {
size = args_percentage_and_expand(args, 'l', 0, INT_MAX, curval,
item, &cause);
} else if (args_has(args, 'p')) {
size = args_strtonum_and_expand(args, 'l', 0, 100, item,
&cause);
if (cause == NULL)
size = curval * size / 100;
}
if (cause != NULL) {
cmdq_error(item, "size %s", cause);
free(cause);
return (CMD_RETURN_ERROR);
}
window_push_zoom(wp->window, 1, args_has(args, 'Z'));
input = (args_has(args, 'I') && count == 0);

View File

@ -135,6 +135,9 @@ cmd_swap_pane_exec(struct cmd *self, struct cmdq_item *item)
}
server_redraw_window(src_w);
server_redraw_window(dst_w);
notify_window("window-layout-changed", src_w);
if (src_w != dst_w)
notify_window("window-layout-changed", dst_w);
out:
if (window_pop_zoom(src_w))

2
cmd.c
View File

@ -95,6 +95,7 @@ extern const struct cmd_entry cmd_select_pane_entry;
extern const struct cmd_entry cmd_select_window_entry;
extern const struct cmd_entry cmd_send_keys_entry;
extern const struct cmd_entry cmd_send_prefix_entry;
extern const struct cmd_entry cmd_server_access_entry;
extern const struct cmd_entry cmd_set_buffer_entry;
extern const struct cmd_entry cmd_set_environment_entry;
extern const struct cmd_entry cmd_set_hook_entry;
@ -187,6 +188,7 @@ const struct cmd_entry *cmd_table[] = {
&cmd_select_window_entry,
&cmd_send_keys_entry,
&cmd_send_prefix_entry,
&cmd_server_access_entry,
&cmd_set_buffer_entry,
&cmd_set_environment_entry,
&cmd_set_hook_entry,

View File

@ -18,6 +18,7 @@
#include <sys/socket.h>
#include <stdio.h>
#include <unistd.h>
#ifdef HAVE_UCRED_H
#include <ucred.h>
@ -38,9 +39,6 @@ getpeereid(int s, uid_t *uid, gid_t *gid)
*gid = uc.gid;
return (0);
#elif defined(HAVE_GETPEERUCRED)
int
getpeereid(int s, uid_t *uid, gid_t *gid)
{
ucred_t *ucred = NULL;
if (getpeerucred(s, &ucred) == -1)
@ -51,9 +49,9 @@ getpeereid(int s, uid_t *uid, gid_t *gid)
return (-1);
ucred_free(ucred);
return (0);
}
#else
errno = EOPNOTSUPP;
return (-1);
*uid = geteuid();
*gid = getegid();
return (0);
#endif
}

View File

@ -267,6 +267,12 @@ if test "x$found_libevent" = xno; then
AC_MSG_ERROR("libevent not found")
fi
# Look for yacc.
AC_CHECK_PROG(found_yacc, $YACC, yes, no)
if test "x$found_yacc" = xno; then
AC_MSG_ERROR("yacc not found")
fi
# Look for ncurses or curses. Try pkg-config first then directly for the
# library.
PKG_CHECK_MODULES(

View File

@ -234,3 +234,16 @@ control_notify_session_window_changed(struct session *s)
s->curw->window->id);
}
}
void
control_notify_paste_buffer_changed(const char *name)
{
struct client *c;
TAILQ_FOREACH(c, &clients, entry) {
if (!CONTROL_SHOULD_NOTIFY_CLIENT(c))
continue;
control_write(c, "%%paste-changed %s", name);
}
}

View File

@ -775,13 +775,16 @@ control_start(struct client *c)
cs->read_event = bufferevent_new(c->fd, control_read_callback,
control_write_callback, control_error_callback, c);
bufferevent_enable(cs->read_event, EV_READ);
if (cs->read_event == NULL)
fatalx("out of memory");
if (c->flags & CLIENT_CONTROLCONTROL)
cs->write_event = cs->read_event;
else {
cs->write_event = bufferevent_new(c->out_fd, NULL,
control_write_callback, control_error_callback, c);
if (cs->write_event == NULL)
fatalx("out of memory");
}
bufferevent_setwatermark(cs->write_event, EV_WRITE, CONTROL_BUFFER_LOW,
0);
@ -792,6 +795,13 @@ control_start(struct client *c)
}
}
/* Control client ready. */
void
control_ready(struct client *c)
{
bufferevent_enable(c->control_state->read_event, EV_READ);
}
/* Discard all output for a client. */
void
control_discard(struct client *c)

View File

@ -182,9 +182,11 @@ void
environ_update(struct options *oo, struct environ *src, struct environ *dst)
{
struct environ_entry *envent;
struct environ_entry *envent1;
struct options_entry *o;
struct options_array_item *a;
union options_value *ov;
int found;
o = options_get(oo, "update-environment");
if (o == NULL)
@ -192,14 +194,15 @@ environ_update(struct options *oo, struct environ *src, struct environ *dst)
a = options_array_first(o);
while (a != NULL) {
ov = options_array_item_value(a);
RB_FOREACH(envent, environ, src) {
if (fnmatch(ov->string, envent->name, 0) == 0)
break;
found = 0;
RB_FOREACH_SAFE(envent, environ, src, envent1) {
if (fnmatch(ov->string, envent->name, 0) == 0) {
environ_set(dst, envent->name, 0, "%s", envent->value);
found = 1;
}
}
if (envent == NULL)
if (!found)
environ_clear(dst, ov->string);
else
environ_set(dst, envent->name, 0, "%s", envent->value);
a = options_array_next(a);
}
}

4
file.c
View File

@ -585,6 +585,8 @@ file_write_open(struct client_files *files, struct tmuxpeer *peer,
cf->event = bufferevent_new(cf->fd, NULL, file_write_callback,
file_write_error_callback, cf);
if (cf->event == NULL)
fatalx("out of memory");
bufferevent_enable(cf->event, EV_WRITE);
goto reply;
@ -744,6 +746,8 @@ file_read_open(struct client_files *files, struct tmuxpeer *peer,
cf->event = bufferevent_new(cf->fd, file_read_callback, NULL,
file_read_error_callback, cf);
if (cf->event == NULL)
fatalx("out of memory");
bufferevent_enable(cf->event, EV_READ);
return;

View File

@ -801,6 +801,20 @@ format_cb_start_command(struct format_tree *ft)
return (cmd_stringify_argv(wp->argc, wp->argv));
}
/* Callback for pane_start_path. */
static void *
format_cb_start_path(struct format_tree *ft)
{
struct window_pane *wp = ft->wp;
if (wp == NULL)
return (NULL);
if (wp->cwd == NULL)
return (xstrdup(""));
return (xstrdup(wp->cwd));
}
/* Callback for pane_current_command. */
static void *
format_cb_current_command(struct format_tree *ft)
@ -1131,6 +1145,25 @@ format_cb_mouse_word(struct format_tree *ft)
return (format_grid_word(gd, x, gd->hsize + y));
}
/* Callback for mouse_hyperlink. */
static void *
format_cb_mouse_hyperlink(struct format_tree *ft)
{
struct window_pane *wp;
struct grid *gd;
u_int x, y;
if (!ft->m.valid)
return (NULL);
wp = cmd_mouse_pane(&ft->m, NULL, NULL);
if (wp == NULL)
return (NULL);
if (cmd_mouse_at(wp, &ft->m, &x, &y, 0) != 0)
return (NULL);
gd = wp->base.grid;
return (format_grid_hyperlink(gd, x, gd->hsize + y, wp->screen));
}
/* Callback for mouse_line. */
static void *
format_cb_mouse_line(struct format_tree *ft)
@ -2597,7 +2630,7 @@ format_cb_user(__unused struct format_tree *ft)
if ((pw = getpwuid(getuid())) != NULL)
return (xstrdup(pw->pw_name));
return NULL;
return (NULL);
}
/* Format table type. */
@ -2775,6 +2808,9 @@ static const struct format_table_entry format_table[] = {
{ "mouse_button_flag", FORMAT_TABLE_STRING,
format_cb_mouse_button_flag
},
{ "mouse_hyperlink", FORMAT_TABLE_STRING,
format_cb_mouse_hyperlink
},
{ "mouse_line", FORMAT_TABLE_STRING,
format_cb_mouse_line
},
@ -2898,6 +2934,9 @@ static const struct format_table_entry format_table[] = {
{ "pane_start_command", FORMAT_TABLE_STRING,
format_cb_start_command
},
{ "pane_start_path", FORMAT_TABLE_STRING,
format_cb_start_path
},
{ "pane_synchronized", FORMAT_TABLE_STRING,
format_cb_pane_synchronized
},
@ -3370,12 +3409,12 @@ format_quote_style(const char *s)
}
/* Make a prettier time. */
static char *
format_pretty_time(time_t t)
char *
format_pretty_time(time_t t, int seconds)
{
struct tm now_tm, tm;
time_t now, age;
char s[6];
char s[9];
time(&now);
if (now < t)
@ -3387,7 +3426,10 @@ format_pretty_time(time_t t)
/* Last 24 hours. */
if (age < 24 * 3600) {
strftime(s, sizeof s, "%H:%M", &tm);
if (seconds)
strftime(s, sizeof s, "%H:%M:%S", &tm);
else
strftime(s, sizeof s, "%H:%M", &tm);
return (xstrdup(s));
}
@ -3492,7 +3534,7 @@ found:
if (t == 0)
return (NULL);
if (modifiers & FORMAT_PRETTY)
found = format_pretty_time(t);
found = format_pretty_time(t, 0);
else {
if (time_format != NULL) {
localtime_r(&t, &tm);
@ -3522,12 +3564,12 @@ found:
}
if (modifiers & FORMAT_QUOTE_SHELL) {
saved = found;
found = xstrdup(format_quote_shell(saved));
found = format_quote_shell(saved);
free(saved);
}
if (modifiers & FORMAT_QUOTE_STYLE) {
saved = found;
found = xstrdup(format_quote_style(saved));
found = format_quote_style(saved);
free(saved);
}
return (found);
@ -4586,7 +4628,7 @@ format_expand1(struct format_expand_state *es, const char *fmt)
{
struct format_tree *ft = es->ft;
char *buf, *out, *name;
const char *ptr, *s;
const char *ptr, *s, *style_end = NULL;
size_t off, len, n, outlen;
int ch, brackets;
char expanded[8192];
@ -4681,18 +4723,20 @@ format_expand1(struct format_expand_state *es, const char *fmt)
break;
fmt += n + 1;
continue;
case '[':
case '#':
/*
* If ##[ (with two or more #s), then it is a style and
* can be left for format_draw to handle.
*/
ptr = fmt;
n = 2;
ptr = fmt - (ch == '[');
n = 2 - (ch == '[');
while (*ptr == '#') {
ptr++;
n++;
}
if (*ptr == '[') {
style_end = format_skip(fmt - 2, "]");
format_log(es, "found #*%zu[", n);
while (len - off < n + 2) {
buf = xreallocarray(buf, 2, len);
@ -4715,10 +4759,12 @@ format_expand1(struct format_expand_state *es, const char *fmt)
continue;
default:
s = NULL;
if (ch >= 'A' && ch <= 'Z')
s = format_upper[ch - 'A'];
else if (ch >= 'a' && ch <= 'z')
s = format_lower[ch - 'a'];
if (fmt > style_end) { /* skip inside #[] */
if (ch >= 'A' && ch <= 'Z')
s = format_upper[ch - 'A'];
else if (ch >= 'a' && ch <= 'z')
s = format_lower[ch - 'a'];
}
if (s == NULL) {
while (len - off < 3) {
buf = xreallocarray(buf, 2, len);
@ -5040,3 +5086,20 @@ format_grid_line(struct grid *gd, u_int y)
}
return (s);
}
/* Return hyperlink at given coordinates. Caller frees. */
char *
format_grid_hyperlink(struct grid *gd, u_int x, u_int y, struct screen* s)
{
const char *uri;
struct grid_cell gc;
grid_get_cell(gd, x, y, &gc);
if (gc.flags & GRID_FLAG_PADDING)
return (NULL);
if (s->hyperlinks == NULL || gc.link == 0)
return (NULL);
if (!hyperlinks_get(s->hyperlinks, gc.link, &uri, NULL, NULL))
return (NULL);
return (xstrdup(uri));
}

View File

@ -231,5 +231,5 @@ grid_view_string_cells(struct grid *gd, u_int px, u_int py, u_int nx)
px = grid_view_x(gd, px);
py = grid_view_y(gd, py);
return (grid_string_cells(gd, px, py, nx, NULL, 0, 0, 0));
return (grid_string_cells(gd, px, py, nx, NULL, 0, 0, 0, NULL));
}

87
grid.c
View File

@ -37,7 +37,7 @@
/* Default grid cell data. */
const struct grid_cell grid_default_cell = {
{ { ' ' }, 0, 1, 1 }, 0, 0, 8, 8, 0
{ { ' ' }, 0, 1, 1 }, 0, 0, 8, 8, 0, 0
};
/*
@ -45,12 +45,12 @@ const struct grid_cell grid_default_cell = {
* appears in the grid - because of this, they are always extended cells.
*/
static const struct grid_cell grid_padding_cell = {
{ { '!' }, 0, 0, 0 }, 0, GRID_FLAG_PADDING, 8, 8, 0
{ { '!' }, 0, 0, 0 }, 0, GRID_FLAG_PADDING, 8, 8, 0, 0
};
/* Cleared grid cell data. */
static const struct grid_cell grid_cleared_cell = {
{ { ' ' }, 0, 1, 1 }, 0, GRID_FLAG_CLEARED, 8, 8, 0
{ { ' ' }, 0, 1, 1 }, 0, GRID_FLAG_CLEARED, 8, 8, 0, 0
};
static const struct grid_cell_entry grid_cleared_entry = {
GRID_FLAG_CLEARED, { .data = { 0, 8, 8, ' ' } }
@ -90,6 +90,8 @@ grid_need_extended_cell(const struct grid_cell_entry *gce,
return (1);
if (gc->us != 0) /* only supports 256 or RGB */
return (1);
if (gc->link != 0)
return (1);
return (0);
}
@ -131,6 +133,7 @@ grid_extended_cell(struct grid_line *gl, struct grid_cell_entry *gce,
gee->fg = gc->fg;
gee->bg = gc->bg;
gee->us = gc->us;
gee->link = gc->link;
return (gee);
}
@ -231,6 +234,8 @@ grid_cells_look_equal(const struct grid_cell *gc1, const struct grid_cell *gc2)
return (0);
if (gc1->attr != gc2->attr || gc1->flags != gc2->flags)
return (0);
if (gc1->link != gc2->link)
return (0);
return (1);
}
@ -399,6 +404,7 @@ grid_scroll_history(struct grid *gd, u_int bg)
gd->hscrolled++;
grid_compact_line(&gd->linedata[gd->hsize]);
gd->linedata[gd->hsize].time = current_time;
gd->hsize++;
}
@ -438,6 +444,7 @@ grid_scroll_history_region(struct grid *gd, u_int upper, u_int lower, u_int bg)
/* Move the line into the history. */
memcpy(gl_history, gl_upper, sizeof *gl_history);
gl_history->time = current_time;
/* Then move the region up and clear the bottom line. */
memmove(gl_upper, gl_upper + 1, (lower - upper) * sizeof *gl_upper);
@ -507,6 +514,7 @@ grid_get_cell1(struct grid_line *gl, u_int px, struct grid_cell *gc)
gc->fg = gee->fg;
gc->bg = gee->bg;
gc->us = gee->us;
gc->link = gee->link;
utf8_to_data(gee->data, &gc->data);
}
return;
@ -522,6 +530,7 @@ grid_get_cell1(struct grid_line *gl, u_int px, struct grid_cell *gc)
gc->bg |= COLOUR_FLAG_256;
gc->us = 0;
utf8_set(&gc->data, gce->data.data);
gc->link = 0;
}
/* Get cell for reading. */
@ -876,18 +885,47 @@ grid_string_cells_add_code(char *buf, size_t len, u_int n, int *s, int *newc,
}
}
static int
grid_string_cells_add_hyperlink(char *buf, size_t len, const char *id,
const char *uri, int escape_c0)
{
char *tmp;
if (strlen(uri) + strlen(id) + 17 >= len)
return (0);
if (escape_c0)
strlcat(buf, "\\033]8;", len);
else
strlcat(buf, "\033]8;", len);
if (*id != '\0') {
xasprintf(&tmp, "id=%s;", id);
strlcat(buf, tmp, len);
free(tmp);
} else
strlcat(buf, ";", len);
strlcat(buf, uri, len);
if (escape_c0)
strlcat(buf, "\\033\\\\", len);
else
strlcat(buf, "\033\\", len);
return (1);
}
/*
* Returns ANSI code to set particular attributes (colour, bold and so on)
* given a current state.
*/
static void
grid_string_cells_code(const struct grid_cell *lastgc,
const struct grid_cell *gc, char *buf, size_t len, int escape_c0)
const struct grid_cell *gc, char *buf, size_t len, int escape_c0,
struct screen *sc, int *has_link)
{
int oldc[64], newc[64], s[128];
size_t noldc, nnewc, n, i;
u_int attr = gc->attr, lastattr = lastgc->attr;
char tmp[64];
int oldc[64], newc[64], s[128];
size_t noldc, nnewc, n, i;
u_int attr = gc->attr, lastattr = lastgc->attr;
char tmp[64];
const char *uri, *id;
struct {
u_int mask;
@ -977,19 +1015,32 @@ grid_string_cells_code(const struct grid_cell *lastgc,
else
strlcat(buf, "\017", len); /* SI */
}
/* Add hyperlink if changed. */
if (sc != NULL && sc->hyperlinks != NULL && lastgc->link != gc->link) {
if (hyperlinks_get(sc->hyperlinks, gc->link, &uri, &id, NULL)) {
*has_link = grid_string_cells_add_hyperlink(buf, len,
id, uri, escape_c0);
} else if (*has_link) {
grid_string_cells_add_hyperlink(buf, len, "", "",
escape_c0);
*has_link = 0;
}
}
}
/* Convert cells into a string. */
char *
grid_string_cells(struct grid *gd, u_int px, u_int py, u_int nx,
struct grid_cell **lastgc, int with_codes, int escape_c0, int trim)
struct grid_cell **lastgc, int with_codes, int escape_c0, int trim,
struct screen *s)
{
struct grid_cell gc;
static struct grid_cell lastgc1;
const char *data;
char *buf, code[128];
char *buf, code[8192];
size_t len, off, size, codelen;
u_int xx;
u_int xx, has_link = 0;
const struct grid_line *gl;
if (lastgc != NULL && *lastgc == NULL) {
@ -1011,7 +1062,7 @@ grid_string_cells(struct grid *gd, u_int px, u_int py, u_int nx,
if (with_codes) {
grid_string_cells_code(*lastgc, &gc, code, sizeof code,
escape_c0);
escape_c0, s, &has_link);
codelen = strlen(code);
memcpy(*lastgc, &gc, sizeof **lastgc);
} else
@ -1037,6 +1088,18 @@ grid_string_cells(struct grid *gd, u_int px, u_int py, u_int nx,
off += size;
}
if (has_link) {
grid_string_cells_add_hyperlink(code, sizeof code, "", "",
escape_c0);
codelen = strlen(code);
while (len < off + size + codelen + 1) {
buf = xreallocarray(buf, 2, len);
len *= 2;
}
memcpy(buf + off, code, codelen);
off += codelen;
}
if (trim) {
while (off > 0 && buf[off - 1] == ' ')
off--;

227
hyperlinks.c Normal file
View File

@ -0,0 +1,227 @@
/* $OpenBSD$ */
/*
* Copyright (c) 2021 Will <author@will.party>
* Copyright (c) 2022 Jeff Chiang <pobomp@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 <stdlib.h>
#include <string.h>
#include "tmux.h"
/*
* OSC 8 hyperlinks, described at:
*
* https://gist.github.com/egmontkob/eb114294efbcd5adb1944c9f3cb5feda
*
* Each hyperlink and ID combination is assigned a number ("inner" in this
* file) which is stored in an extended grid cell and maps into a tree here.
*
* Each URI has one inner number and one external ID (which tmux uses to send
* the hyperlink to the terminal) and one internal ID (which is received from
* the sending application inside tmux).
*
* Anonymous hyperlinks are each unique and are not reused even if they have
* the same URI (terminals will not want to tie them together).
*/
#define MAX_HYPERLINKS 5000
static uint64_t hyperlinks_next_external_id = 1;
static u_int global_hyperlinks_count;
struct hyperlinks_uri {
struct hyperlinks *tree;
u_int inner;
const char *internal_id;
const char *external_id;
const char *uri;
TAILQ_ENTRY(hyperlinks_uri) list_entry;
RB_ENTRY(hyperlinks_uri) by_inner_entry;
RB_ENTRY(hyperlinks_uri) by_uri_entry; /* by internal ID and URI */
};
RB_HEAD(hyperlinks_by_uri_tree, hyperlinks_uri);
RB_HEAD(hyperlinks_by_inner_tree, hyperlinks_uri);
TAILQ_HEAD(hyperlinks_list, hyperlinks_uri);
static struct hyperlinks_list global_hyperlinks =
TAILQ_HEAD_INITIALIZER(global_hyperlinks);
struct hyperlinks {
u_int next_inner;
struct hyperlinks_by_inner_tree by_inner;
struct hyperlinks_by_uri_tree by_uri;
};
static int
hyperlinks_by_uri_cmp(struct hyperlinks_uri *left, struct hyperlinks_uri *right)
{
int r;
if (*left->internal_id == '\0' || *right->internal_id == '\0') {
/*
* If both URIs are anonymous, use the inner for comparison so
* that they do not match even if the URI is the same - each
* anonymous URI should be unique.
*/
if (*left->internal_id != '\0')
return (-1);
if (*right->internal_id != '\0')
return (1);
return (left->inner - right->inner);
}
r = strcmp(left->internal_id, right->internal_id);
if (r != 0)
return (r);
return (strcmp(left->uri, right->uri));
}
RB_PROTOTYPE_STATIC(hyperlinks_by_uri_tree, hyperlinks_uri, by_uri_entry,
hyperlinks_by_uri_cmp);
RB_GENERATE_STATIC(hyperlinks_by_uri_tree, hyperlinks_uri, by_uri_entry,
hyperlinks_by_uri_cmp);
static int
hyperlinks_by_inner_cmp(struct hyperlinks_uri *left,
struct hyperlinks_uri *right)
{
return (left->inner - right->inner);
}
RB_PROTOTYPE_STATIC(hyperlinks_by_inner_tree, hyperlinks_uri, by_inner_entry,
hyperlinks_by_inner_cmp);
RB_GENERATE_STATIC(hyperlinks_by_inner_tree, hyperlinks_uri, by_inner_entry,
hyperlinks_by_inner_cmp);
/* Remove a hyperlink. */
static void
hyperlinks_remove(struct hyperlinks_uri *hlu)
{
struct hyperlinks *hl = hlu->tree;
TAILQ_REMOVE(&global_hyperlinks, hlu, list_entry);
global_hyperlinks_count--;
RB_REMOVE(hyperlinks_by_inner_tree, &hl->by_inner, hlu);
RB_REMOVE(hyperlinks_by_uri_tree, &hl->by_uri, hlu);
free((void *)hlu->internal_id);
free((void *)hlu->external_id);
free((void *)hlu->uri);
free(hlu);
}
/* Store a new hyperlink or return if it already exists. */
u_int
hyperlinks_put(struct hyperlinks *hl, const char *uri_in,
const char *internal_id_in)
{
struct hyperlinks_uri find, *hlu;
char *uri, *internal_id, *external_id;
/*
* Anonymous URI are stored with an empty internal ID and the tree
* comparator will make sure they never match each other (so each
* anonymous URI is unique).
*/
if (internal_id_in == NULL)
internal_id_in = "";
utf8_stravis(&uri, uri_in, VIS_OCTAL|VIS_CSTYLE);
utf8_stravis(&internal_id, internal_id_in, VIS_OCTAL|VIS_CSTYLE);
if (*internal_id_in != '\0') {
find.uri = uri;
find.internal_id = internal_id;
hlu = RB_FIND(hyperlinks_by_uri_tree, &hl->by_uri, &find);
if (hlu != NULL) {
free (uri);
free (internal_id);
return (hlu->inner);
}
}
xasprintf(&external_id, "tmux%llX", hyperlinks_next_external_id++);
hlu = xcalloc(1, sizeof *hlu);
hlu->inner = hl->next_inner++;
hlu->internal_id = internal_id;
hlu->external_id = external_id;
hlu->uri = uri;
hlu->tree = hl;
RB_INSERT(hyperlinks_by_uri_tree, &hl->by_uri, hlu);
RB_INSERT(hyperlinks_by_inner_tree, &hl->by_inner, hlu);
TAILQ_INSERT_TAIL(&global_hyperlinks, hlu, list_entry);
if (++global_hyperlinks_count == MAX_HYPERLINKS)
hyperlinks_remove(TAILQ_FIRST(&global_hyperlinks));
return (hlu->inner);
}
/* Get hyperlink by inner number. */
int
hyperlinks_get(struct hyperlinks *hl, u_int inner, const char **uri_out,
const char **internal_id_out, const char **external_id_out)
{
struct hyperlinks_uri find, *hlu;
find.inner = inner;
hlu = RB_FIND(hyperlinks_by_inner_tree, &hl->by_inner, &find);
if (hlu == NULL)
return (0);
if (internal_id_out != NULL)
*internal_id_out = hlu->internal_id;
if (external_id_out != NULL)
*external_id_out = hlu->external_id;
*uri_out = hlu->uri;
return (1);
}
/* Initialize hyperlink set. */
struct hyperlinks *
hyperlinks_init(void)
{
struct hyperlinks *hl;
hl = xcalloc(1, sizeof *hl);
hl->next_inner = 1;
RB_INIT(&hl->by_uri);
RB_INIT(&hl->by_inner);
return (hl);
}
/* Free all hyperlinks but not the set itself. */
void
hyperlinks_reset(struct hyperlinks *hl)
{
struct hyperlinks_uri *hlu, *hlu1;
RB_FOREACH_SAFE(hlu, hyperlinks_by_inner_tree, &hl->by_inner, hlu1)
hyperlinks_remove(hlu);
}
/* Free hyperlink set. */
void
hyperlinks_free(struct hyperlinks *hl)
{
hyperlinks_reset(hl);
free(hl);
}

View File

@ -606,19 +606,34 @@ input_key_get_mouse(struct screen *s, struct mouse_event *m, u_int x, u_int y,
len = xsnprintf(buf, sizeof buf, "\033[<%u;%u;%u%c",
m->sgr_b, x + 1, y + 1, m->sgr_type);
} else if (s->mode & MODE_MOUSE_UTF8) {
if (m->b > 0x7ff - 32 || x > 0x7ff - 33 || y > 0x7ff - 33)
if (m->b > MOUSE_PARAM_UTF8_MAX - MOUSE_PARAM_BTN_OFF ||
x > MOUSE_PARAM_UTF8_MAX - MOUSE_PARAM_POS_OFF ||
y > MOUSE_PARAM_UTF8_MAX - MOUSE_PARAM_POS_OFF)
return (0);
len = xsnprintf(buf, sizeof buf, "\033[M");
len += input_key_split2(m->b + 32, &buf[len]);
len += input_key_split2(x + 33, &buf[len]);
len += input_key_split2(y + 33, &buf[len]);
len += input_key_split2(m->b + MOUSE_PARAM_BTN_OFF, &buf[len]);
len += input_key_split2(x + MOUSE_PARAM_POS_OFF, &buf[len]);
len += input_key_split2(y + MOUSE_PARAM_POS_OFF, &buf[len]);
} else {
if (m->b > 223)
if (m->b + MOUSE_PARAM_BTN_OFF > MOUSE_PARAM_MAX)
return (0);
len = xsnprintf(buf, sizeof buf, "\033[M");
buf[len++] = m->b + 32;
buf[len++] = x + 33;
buf[len++] = y + 33;
buf[len++] = m->b + MOUSE_PARAM_BTN_OFF;
/*
* The incoming x and y may be out of the range which can be
* supported by the "normal" mouse protocol. Clamp the
* coordinates to the supported range.
*/
if (x + MOUSE_PARAM_POS_OFF > MOUSE_PARAM_MAX)
buf[len++] = MOUSE_PARAM_MAX;
else
buf[len++] = x + MOUSE_PARAM_POS_OFF;
if (y + MOUSE_PARAM_POS_OFF > MOUSE_PARAM_MAX)
buf[len++] = MOUSE_PARAM_MAX;
else
buf[len++] = y + MOUSE_PARAM_POS_OFF;
}
*rbuf = buf;

76
input.c
View File

@ -135,6 +135,7 @@ static void input_set_state(struct input_ctx *,
static void input_reset_cell(struct input_ctx *);
static void input_osc_4(struct input_ctx *, const char *);
static void input_osc_8(struct input_ctx *, const char *);
static void input_osc_10(struct input_ctx *, const char *);
static void input_osc_11(struct input_ctx *, const char *);
static void input_osc_12(struct input_ctx *, const char *);
@ -1078,6 +1079,9 @@ input_reply(struct input_ctx *ictx, const char *fmt, ...)
va_list ap;
char *reply;
if (bev == NULL)
return;
va_start(ap, fmt);
xvasprintf(&reply, fmt, ap);
va_end(ap);
@ -1798,6 +1802,8 @@ input_csi_dispatch_sm_private(struct input_ctx *ictx)
screen_write_mode_set(sctx, MODE_FOCUSON);
if (wp == NULL)
break;
if (!options_get_number(global_options, "focus-events"))
break;
if (wp->flags & PANE_FOCUSED)
bufferevent_write(wp->event, "\033[I", 3);
else
@ -2235,25 +2241,30 @@ input_enter_dcs(struct input_ctx *ictx)
static int
input_dcs_dispatch(struct input_ctx *ictx)
{
struct screen_write_ctx *sctx = &ictx->ctx;
struct window_pane *wp = ictx->wp;
struct options *oo = wp->options;
struct screen_write_ctx *sctx = &ictx->ctx;
struct window *w = wp->window;
u_char *buf = ictx->input_buf;
size_t len = ictx->input_len;
const char prefix[] = "tmux;";
const u_int prefixlen = (sizeof prefix) - 1;
struct sixel_image *si;
long long allow_passthrough = 0;
if (wp == NULL)
return (0);
if (ictx->flags & INPUT_DISCARD)
return (0);
if (!options_get_number(ictx->wp->options, "allow-passthrough"))
allow_passthrough = options_get_number(oo, "allow-passthrough");
if (!allow_passthrough)
return (0);
log_debug("%s: \"%s\"", __func__, buf);
if (len >= prefixlen && strncmp(buf, prefix, prefixlen) == 0)
screen_write_rawstring(sctx, buf + prefixlen, len - prefixlen);
if (len >= prefixlen && strncmp(buf, prefix, prefixlen) == 0) {
screen_write_rawstring(sctx, buf + prefixlen, len - prefixlen,
allow_passthrough == 2);
}
if (buf[0] == 'q') {
si = sixel_parse(buf, len, w->xpixel, w->ypixel);
@ -2297,6 +2308,8 @@ input_exit_osc(struct input_ctx *ictx)
option = 0;
while (*p >= '0' && *p <= '9')
option = option * 10 + *p++ - '0';
if (*p != ';' && *p != '\0')
return;
if (*p == ';')
p++;
@ -2321,6 +2334,9 @@ input_exit_osc(struct input_ctx *ictx)
}
}
break;
case 8:
input_osc_8(ictx, p);
break;
case 10:
input_osc_10(ictx, p);
break;
@ -2565,6 +2581,47 @@ input_osc_4(struct input_ctx *ictx, const char *p)
free(copy);
}
/* Handle the OSC 8 sequence for embedding hyperlinks. */
static void
input_osc_8(struct input_ctx *ictx, const char *p)
{
struct hyperlinks *hl = ictx->ctx.s->hyperlinks;
struct grid_cell *gc = &ictx->cell.cell;
const char *start, *end, *uri;
char *id = NULL;
for (start = p; (end = strpbrk(start, ":;")) != NULL; start = end + 1) {
if (end - start >= 4 && strncmp(start, "id=", 3) == 0) {
if (id != NULL)
goto bad;
id = xstrndup(start + 3, end - start - 3);
}
/* The first ; is the end of parameters and start of the URI. */
if (*end == ';')
break;
}
if (end == NULL || *end != ';')
goto bad;
uri = end + 1;
if (*uri == '\0') {
gc->link = 0;
free(id);
return;
}
gc->link = hyperlinks_put(hl, uri, id);
if (id == NULL)
log_debug("hyperlink (anonymous) %s = %u", uri, gc->link);
else
log_debug("hyperlink (id=%s) %s = %u", id, uri, gc->link);
free(id);
return;
bad:
log_debug("bad OSC 8 %s", p);
free(id);
}
/* Handle the OSC 10 sequence for setting and querying foreground colour. */
static void
input_osc_10(struct input_ctx *ictx, const char *p)
@ -2698,6 +2755,9 @@ input_osc_52(struct input_ctx *ictx, const char *p)
int outlen, state;
struct screen_write_ctx ctx;
struct paste_buffer *pb;
const char* allow = "cpqs01234567";
char flags[sizeof "cpqs01234567"] = "";
u_int i, j = 0;
if (wp == NULL)
return;
@ -2712,6 +2772,12 @@ input_osc_52(struct input_ctx *ictx, const char *p)
return;
log_debug("%s: %s", __func__, end);
for (i = 0; p + i != end; i++) {
if (strchr(allow, p[i]) != NULL && strchr(flags, p[i]) == NULL)
flags[j++] = p[i];
}
log_debug("%s: %.*s %s", __func__, (int)(end - p - 1), p, flags);
if (strcmp(end, "?") == 0) {
if ((pb = paste_get_top(NULL)) != NULL)
buf = paste_buffer_data(pb, &len);
@ -2733,7 +2799,7 @@ input_osc_52(struct input_ctx *ictx, const char *p)
}
screen_write_start_pane(&ctx, wp, NULL);
screen_write_setselection(&ctx, out, outlen);
screen_write_setselection(&ctx, flags, out, outlen);
screen_write_stop(&ctx);
notify_pane("pane-set-clipboard", wp);

View File

@ -54,6 +54,9 @@
" '#{?mouse_word,Copy #[underscore]#{=/9/...:mouse_word},}' 'c' {copy-mode -q; set-buffer -- \"#{q:mouse_word}\"}" \
" '#{?mouse_line,Copy Line,}' 'l' {copy-mode -q; set-buffer -- \"#{q:mouse_line}\"}" \
" ''" \
" '#{?mouse_hyperlink,Type #[underscore]#{=/9/...:mouse_hyperlink},}' 'C-h' {copy-mode -q; send-keys -l -- \"#{q:mouse_hyperlink}\"}" \
" '#{?mouse_hyperlink,Copy #[underscore]#{=/9/...:mouse_hyperlink},}' 'h' {copy-mode -q; set-buffer -- \"#{q:mouse_hyperlink}\"}" \
" ''" \
" 'Horizontal Split' 'h' {split-window -h}" \
" 'Vertical Split' 'v' {split-window -v}" \
" ''" \
@ -341,7 +344,7 @@ key_bindings_init_done(__unused struct cmdq_item *item, __unused void *data)
void
key_bindings_init(void)
{
static const char *defaults[] = {
static const char *const defaults[] = {
/* Prefix keys. */
"bind -N 'Send the prefix key' C-b { send-prefix }",
"bind -N 'Rotate through the panes' C-o { rotate-window }",
@ -602,6 +605,7 @@ key_bindings_init(void)
"bind -Tcopy-mode-vi h { send -X cursor-left }",
"bind -Tcopy-mode-vi j { send -X cursor-down }",
"bind -Tcopy-mode-vi k { send -X cursor-up }",
"bind -Tcopy-mode-vi z { send -X scroll-middle }",
"bind -Tcopy-mode-vi l { send -X cursor-right }",
"bind -Tcopy-mode-vi n { send -X search-again }",
"bind -Tcopy-mode-vi o { send -X other-end }",
@ -613,6 +617,8 @@ key_bindings_init(void)
"bind -Tcopy-mode-vi '{' { send -X previous-paragraph }",
"bind -Tcopy-mode-vi '}' { send -X next-paragraph }",
"bind -Tcopy-mode-vi % { send -X next-matching-bracket }",
"bind -Tcopy-mode-vi Home { send -X start-of-line }",
"bind -Tcopy-mode-vi End { send -X end-of-line }",
"bind -Tcopy-mode-vi MouseDown1Pane { select-pane }",
"bind -Tcopy-mode-vi MouseDrag1Pane { select-pane; send -X begin-selection }",
"bind -Tcopy-mode-vi MouseDragEnd1Pane { send -X copy-pipe-and-cancel }",

View File

@ -154,7 +154,7 @@ layout_check(struct layout_cell *lc)
/* Parse a layout string and arrange window as layout. */
int
layout_parse(struct window *w, const char *layout)
layout_parse(struct window *w, const char *layout, char **cause)
{
struct layout_cell *lc, *lcchild;
struct window_pane *wp;
@ -165,22 +165,31 @@ layout_parse(struct window *w, const char *layout)
if (sscanf(layout, "%hx,", &csum) != 1)
return (-1);
layout += 5;
if (csum != layout_checksum(layout))
if (csum != layout_checksum(layout)) {
*cause = xstrdup("invalid layout");
return (-1);
}
/* Build the layout. */
lc = layout_construct(NULL, &layout);
if (lc == NULL)
if (lc == NULL) {
*cause = xstrdup("invalid layout");
return (-1);
if (*layout != '\0')
}
if (*layout != '\0') {
*cause = xstrdup("invalid layout");
goto fail;
}
/* Check this window will fit into the layout. */
for (;;) {
npanes = window_count_panes(w);
ncells = layout_count_cells(lc);
if (npanes > ncells)
if (npanes > ncells) {
xasprintf(cause, "have %u panes but need %u", npanes,
ncells);
goto fail;
}
if (npanes == ncells)
break;
@ -217,8 +226,10 @@ layout_parse(struct window *w, const char *layout)
}
/* Check the new layout. */
if (!layout_check(lc))
if (!layout_check(lc)) {
*cause = xstrdup("size mismatch after applying layout");
return (-1);
}
/* Resize to the layout size. */
window_resize(w, lc->sx, lc->sy, -1, -1);

2
log.c
View File

@ -143,7 +143,7 @@ fatal(const char *msg, ...)
va_list ap;
if (snprintf(tmp, sizeof tmp, "fatal: %s: ", strerror(errno)) < 0)
exit (1);
exit(1);
va_start(ap, msg);
log_vwrite(msg, ap, tmp);

86
menu.c
View File

@ -56,7 +56,7 @@ menu_add_item(struct menu *menu, const struct menu_item *item,
{
struct menu_item *new_item;
const char *key = NULL, *cmd, *suffix = "";
char *s, *name;
char *s, *trimmed, *name;
u_int width, max_width;
int line;
size_t keylen, slen;
@ -103,11 +103,13 @@ menu_add_item(struct menu *menu, const struct menu_item *item,
max_width--;
suffix = ">";
}
if (key != NULL)
xasprintf(&name, "%.*s%s#[default] #[align=right](%s)",
(int)max_width, s, suffix, key);
else
xasprintf(&name, "%.*s%s", (int)max_width, s, suffix);
trimmed = format_trim_right(s, max_width);
if (key != NULL) {
xasprintf(&name, "%s%s#[default] #[align=right](%s)",
trimmed, suffix, key);
} else
xasprintf(&name, "%s%s", trimmed, suffix);
free(trimmed);
new_item->name = name;
free(s);
@ -158,11 +160,16 @@ menu_free(struct menu *menu)
}
struct screen *
menu_mode_cb(__unused struct client *c, void *data, __unused u_int *cx,
__unused u_int *cy)
menu_mode_cb(__unused struct client *c, void *data, u_int *cx, u_int *cy)
{
struct menu_data *md = data;
*cx = md->px + 2;
if (md->choice == -1)
*cy = md->py;
else
*cy = md->py + 1 + md->choice;
return (&md->s);
}
@ -316,27 +323,64 @@ menu_key_cb(struct client *c, void *data, struct key_event *event)
} while ((name == NULL || *name == '-') && md->choice != old);
c->flags |= CLIENT_REDRAWOVERLAY;
return (0);
case 'g':
case KEYC_PPAGE:
case '\002': /* C-b */
if (md->choice > 5)
md->choice -= 5;
else
if (md->choice < 6)
md->choice = 0;
while (md->choice != count && (name == NULL || *name == '-'))
else {
i = 5;
while (i > 0) {
md->choice--;
name = menu->items[md->choice].name;
if (md->choice != 0 &&
(name != NULL && *name != '-'))
i--;
else if (md->choice == 0)
break;
}
}
c->flags |= CLIENT_REDRAWOVERLAY;
break;
case KEYC_NPAGE:
if (md->choice > count - 6) {
md->choice = count - 1;
name = menu->items[md->choice].name;
} else {
i = 5;
while (i > 0) {
md->choice++;
name = menu->items[md->choice].name;
if (md->choice != count - 1 &&
(name != NULL && *name != '-'))
i++;
else if (md->choice == count - 1)
break;
}
}
while (name == NULL || *name == '-') {
md->choice--;
name = menu->items[md->choice].name;
}
c->flags |= CLIENT_REDRAWOVERLAY;
break;
case 'g':
case KEYC_HOME:
md->choice = 0;
name = menu->items[md->choice].name;
while (name == NULL || *name == '-') {
md->choice++;
if (md->choice == count)
md->choice = -1;
name = menu->items[md->choice].name;
}
c->flags |= CLIENT_REDRAWOVERLAY;
break;
case 'G':
case KEYC_NPAGE:
if (md->choice > count - 6)
md->choice = count - 1;
else
md->choice += 5;
while (md->choice != -1 && (name == NULL || *name == '-'))
case KEYC_END:
md->choice = count - 1;
name = menu->items[md->choice].name;
while (name == NULL || *name == '-') {
md->choice--;
name = menu->items[md->choice].name;
}
c->flags |= CLIENT_REDRAWOVERLAY;
break;
case '\006': /* C-f */

View File

@ -32,6 +32,7 @@ struct notify_entry {
struct session *session;
struct window *window;
int pane;
const char *pbname;
};
static struct cmdq_item *
@ -46,7 +47,7 @@ notify_insert_one_hook(struct cmdq_item *item, struct notify_entry *ne,
if (log_get_level() != 0) {
s = cmd_list_print(cmdlist, 0);
log_debug("%s: hook %s is: %s", __func__, ne->name, s);
free (s);
free(s);
}
new_item = cmdq_get_command(cmdlist, state);
return (cmdq_insert_after(item, new_item));
@ -149,6 +150,8 @@ notify_callback(struct cmdq_item *item, void *data)
control_notify_session_closed(ne->session);
if (strcmp(ne->name, "session-window-changed") == 0)
control_notify_session_window_changed(ne->session);
if (strcmp(ne->name, "paste-buffer-changed") == 0)
control_notify_paste_buffer_changed(ne->pbname);
notify_insert_hook(item, ne);
@ -164,6 +167,7 @@ notify_callback(struct cmdq_item *item, void *data)
format_free(ne->formats);
free((void *)ne->name);
free((void *)ne->pbname);
free(ne);
return (CMD_RETURN_NORMAL);
@ -171,7 +175,8 @@ notify_callback(struct cmdq_item *item, void *data)
static void
notify_add(const char *name, struct cmd_find_state *fs, struct client *c,
struct session *s, struct window *w, struct window_pane *wp)
struct session *s, struct window *w, struct window_pane *wp,
const char *pbname)
{
struct notify_entry *ne;
struct cmdq_item *item;
@ -187,6 +192,7 @@ notify_add(const char *name, struct cmd_find_state *fs, struct client *c,
ne->session = s;
ne->window = w;
ne->pane = (wp != NULL ? wp->id : -1);
ne->pbname = (pbname != NULL ? xstrdup(pbname) : NULL);
ne->formats = format_create(NULL, NULL, 0, FORMAT_NOJOBS);
format_add(ne->formats, "hook", "%s", name);
@ -248,7 +254,7 @@ notify_client(const char *name, struct client *c)
struct cmd_find_state fs;
cmd_find_from_client(&fs, c, 0);
notify_add(name, &fs, c, NULL, NULL, NULL);
notify_add(name, &fs, c, NULL, NULL, NULL, NULL);
}
void
@ -260,7 +266,7 @@ notify_session(const char *name, struct session *s)
cmd_find_from_session(&fs, s, 0);
else
cmd_find_from_nothing(&fs, 0);
notify_add(name, &fs, NULL, s, NULL, NULL);
notify_add(name, &fs, NULL, s, NULL, NULL, NULL);
}
void
@ -269,7 +275,7 @@ notify_winlink(const char *name, struct winlink *wl)
struct cmd_find_state fs;
cmd_find_from_winlink(&fs, wl, 0);
notify_add(name, &fs, NULL, wl->session, wl->window, NULL);
notify_add(name, &fs, NULL, wl->session, wl->window, NULL, NULL);
}
void
@ -278,7 +284,7 @@ notify_session_window(const char *name, struct session *s, struct window *w)
struct cmd_find_state fs;
cmd_find_from_session_window(&fs, s, w, 0);
notify_add(name, &fs, NULL, s, w, NULL);
notify_add(name, &fs, NULL, s, w, NULL, NULL);
}
void
@ -287,7 +293,7 @@ notify_window(const char *name, struct window *w)
struct cmd_find_state fs;
cmd_find_from_window(&fs, w, 0);
notify_add(name, &fs, NULL, NULL, w, NULL);
notify_add(name, &fs, NULL, NULL, w, NULL, NULL);
}
void
@ -296,5 +302,14 @@ notify_pane(const char *name, struct window_pane *wp)
struct cmd_find_state fs;
cmd_find_from_pane(&fs, wp, 0);
notify_add(name, &fs, NULL, NULL, NULL, wp);
notify_add(name, &fs, NULL, NULL, NULL, wp, NULL);
}
void
notify_paste_buffer(const char *pbname)
{
struct cmd_find_state fs;
cmd_find_clear_state(&fs, 0);
notify_add("paste-buffer-changed", &fs, NULL, NULL, NULL, NULL, pbname);
}

View File

@ -87,6 +87,9 @@ static const char *options_table_detach_on_destroy_list[] = {
static const char *options_table_extended_keys_list[] = {
"off", "on", "always", NULL
};
static const char *options_table_allow_passthrough_list[] = {
"off", "on", "all", NULL
};
/* Status line format. */
#define OPTIONS_TABLE_STATUS_FORMAT1 \
@ -362,7 +365,8 @@ const struct options_table_entry options_table[] = {
.scope = OPTIONS_TABLE_SERVER,
.flags = OPTIONS_TABLE_IS_ARRAY,
.default_str = "xterm*:clipboard:ccolour:cstyle:focus:title,"
"screen*:title",
"screen*:title,"
"rxvt*:ignorefkeys",
.separator = ",",
.text = "List of terminal features, used if they cannot be "
"automatically detected."
@ -802,11 +806,14 @@ const struct options_table_entry options_table[] = {
},
{ .name = "allow-passthrough",
.type = OPTIONS_TABLE_FLAG,
.type = OPTIONS_TABLE_CHOICE,
.scope = OPTIONS_TABLE_WINDOW|OPTIONS_TABLE_PANE,
.choices = options_table_allow_passthrough_list,
.default_num = 0,
.text = "Whether applications are allowed to use the escape sequence "
"to bypass tmux."
"to bypass tmux. Can be 'off' (disallowed), 'on' (allowed "
"if the pane is visible), or 'all' (allowed even if the pane "
"is invisible)."
},
{ .name = "allow-rename",

View File

@ -1106,7 +1106,6 @@ options_push_changes(const char *name)
struct session *s;
struct window *w;
struct window_pane *wp;
int c;
log_debug("%s: %s", __func__, name);
@ -1119,18 +1118,12 @@ options_push_changes(const char *name)
}
}
if (strcmp(name, "cursor-colour") == 0) {
RB_FOREACH(wp, window_pane_tree, &all_window_panes) {
c = options_get_number(wp->options, name);
wp->screen->default_ccolour = c;
}
RB_FOREACH(wp, window_pane_tree, &all_window_panes)
window_pane_default_cursor(wp);
}
if (strcmp(name, "cursor-style") == 0) {
RB_FOREACH(wp, window_pane_tree, &all_window_panes) {
wp->screen->default_mode = 0;
screen_set_cursor_style(options_get_number(wp->options,
name), &wp->screen->default_cstyle,
&wp->screen->default_mode);
}
RB_FOREACH(wp, window_pane_tree, &all_window_panes)
window_pane_default_cursor(wp);
}
if (strcmp(name, "fill-character") == 0) {
RB_FOREACH(w, windows, &windows)

19
paste.c
View File

@ -111,6 +111,12 @@ paste_walk(struct paste_buffer *pb)
return (RB_NEXT(paste_time_tree, &paste_by_time, pb));
}
int
paste_is_empty(void)
{
return RB_ROOT(&paste_by_time) == NULL;
}
/* Get the most recent automatic buffer. */
struct paste_buffer *
paste_get_top(const char **name)
@ -118,6 +124,8 @@ paste_get_top(const char **name)
struct paste_buffer *pb;
pb = RB_MIN(paste_time_tree, &paste_by_time);
while (pb != NULL && !pb->automatic)
pb = RB_NEXT(paste_time_tree, &paste_by_time, pb);
if (pb == NULL)
return (NULL);
if (name != NULL)
@ -142,6 +150,8 @@ paste_get_name(const char *name)
void
paste_free(struct paste_buffer *pb)
{
notify_paste_buffer(pb->name);
RB_REMOVE(paste_name_tree, &paste_by_name, pb);
RB_REMOVE(paste_time_tree, &paste_by_time, pb);
if (pb->automatic)
@ -198,6 +208,8 @@ paste_add(const char *prefix, char *data, size_t size)
pb->order = paste_next_order++;
RB_INSERT(paste_name_tree, &paste_by_name, pb);
RB_INSERT(paste_time_tree, &paste_by_time, pb);
notify_paste_buffer(pb->name);
}
/* Rename a paste buffer. */
@ -245,6 +257,9 @@ paste_rename(const char *oldname, const char *newname, char **cause)
RB_INSERT(paste_name_tree, &paste_by_name, pb);
notify_paste_buffer(oldname);
notify_paste_buffer(newname);
return (0);
}
@ -293,6 +308,8 @@ paste_set(char *data, size_t size, const char *name, char **cause)
RB_INSERT(paste_name_tree, &paste_by_name, pb);
RB_INSERT(paste_time_tree, &paste_by_time, pb);
notify_paste_buffer(name);
return (0);
}
@ -303,6 +320,8 @@ paste_replace(struct paste_buffer *pb, char *data, size_t size)
free(pb->data);
pb->data = data;
pb->size = size;
notify_paste_buffer(pb->name);
}
/* Convert start of buffer into a nice string. */

8
proc.c
View File

@ -202,7 +202,7 @@ proc_start(const char *name)
#endif
, event_get_version(), event_get_method()
#ifdef HAVE_UTF8PROC
, utf8proc_version ()
, utf8proc_version()
#endif
);
@ -349,6 +349,12 @@ proc_kill_peer(struct tmuxpeer *peer)
peer->flags |= PEER_BAD;
}
void
proc_flush_peer(struct tmuxpeer *peer)
{
imsg_flush(&peer->ibuf);
}
void
proc_toggle_log(struct tmuxproc *tp)
{

View File

@ -0,0 +1,30 @@
#!/bin/sh
# capture-pane -e for OSC 8 hyperlink
PATH=/bin:/usr/bin
TERM=screen
[ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux)
TMUX="$TEST_TMUX -Ltest"
TMP=$(mktemp)
TMP2=$(mktemp)
trap "rm -f $TMP $TMP2" 0 1 15
$TMUX kill-server 2>/dev/null
do_test() {
$TMUX -f/dev/null new -d "
printf '$1'
$TMUX capturep -peS0 -E1 >$TMP"
echo $2 > $TMP2
sleep 1
cmp $TMP $TMP2 || exit 1
return 0
}
do_test '\033]8;id=1;https://github.com\033\\test1\033]8;;\033\\\n' '\033]8;id=1;https://github.com\033\\test1\033]8;;\033\\\n' || exit 1
do_test '\033]8;;https://github.com/tmux/tmux\033\\test1\033]8;;\033\\\n' '\033]8;;https://github.com/tmux/tmux\033\\test1\033]8;;\033\\\n' || exit 1
$TMUX has 2>/dev/null && exit 1
exit 0

View File

@ -22,8 +22,8 @@ $TMUX -f/dev/null new -d "
sleep 1
(
printf '\033[1m\033[31m\033[42mabc\033[0m\033[31m\033[49mdef\033[39m\n'
printf '\033[100m bright bg \033[49m\n'
printf '\033[1m\033[31m\033[42mabc\033[0m\033[31m\033[49mdef\n'
printf '\033[39m\033[100m bright bg\n'
) | cmp - $TMP || exit 1
$TMUX has 2>/dev/null && exit 1

View File

@ -11,16 +11,13 @@ TMUX="$TEST_TMUX -Ltest"
$TMUX kill-server 2>/dev/null
TMP=$(mktemp)
trap "rm -f $TMP" 0 1 15
OUT=$(mktemp)
trap "rm -f $TMP $OUT" 0 1 15
cat <<EOF >$TMP
if 'true' 'wibble wobble'
EOF
$TMUX -f$TMP new -d || exit 1
sleep 1
E=$($TMUX display -p '#{pane_in_mode}')
$TMUX kill-server 2>/dev/null
[ "$E" = "1" ] || exit 1
exit 0
$TMUX -f$TMP -C new <<EOF >$OUT
EOF
grep -q "^%config-error $TMP:1: $TMP:1: unknown command: wibble$" $OUT

View File

@ -8,13 +8,14 @@ TERM=screen
[ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux)
TMUX="$TEST_TMUX -Ltest"
$TMUX kill-server 2>/dev/null
sleep 1
$TMUX -f/dev/null new -d 'sleep 1000' || exit 1
P=$($TMUX display -pt0:0.0 '#{pane_pid}')
$TMUX -f/dev/null new -d || exit 1
sleep 1
$TMUX kill-session -t0:
sleep 1
sleep 3
kill -0 $P 2>/dev/null && exit 1
$TMUX kill-server 2>/dev/null

View File

@ -738,7 +738,7 @@ screen_redraw_draw_borders_cell(struct screen_redraw_ctx *ctx, u_int i, u_int j)
}
}
tty_cell(tty, &gc, &grid_default_cell, NULL);
tty_cell(tty, &gc, &grid_default_cell, NULL, NULL);
if (isolates)
tty_puts(tty, START_ISOLATE);
}

View File

@ -2143,12 +2143,14 @@ screen_write_overwrite(struct screen_write_ctx *ctx, struct grid_cell *gc,
/* Set external clipboard. */
void
screen_write_setselection(struct screen_write_ctx *ctx, u_char *str, u_int len)
screen_write_setselection(struct screen_write_ctx *ctx, const char *flags,
u_char *str, u_int len)
{
struct tty_ctx ttyctx;
screen_write_initctx(ctx, &ttyctx, 0);
ttyctx.ptr = str;
ttyctx.ptr2 = (void *)flags;
ttyctx.num = len;
tty_write(tty_cmd_setselection, &ttyctx);
@ -2156,13 +2158,15 @@ screen_write_setselection(struct screen_write_ctx *ctx, u_char *str, u_int len)
/* Write unmodified string. */
void
screen_write_rawstring(struct screen_write_ctx *ctx, u_char *str, u_int len)
screen_write_rawstring(struct screen_write_ctx *ctx, u_char *str, u_int len,
int allow_invisible_panes)
{
struct tty_ctx ttyctx;
screen_write_initctx(ctx, &ttyctx, 0);
ttyctx.ptr = str;
ttyctx.num = len;
ttyctx.allow_invisible_panes = allow_invisible_panes;
tty_write(tty_cmd_rawstring, &ttyctx);
}

View File

@ -90,6 +90,7 @@ screen_init(struct screen *s, u_int sx, u_int sy, u_int hlimit)
TAILQ_INIT(&s->images);
s->write_list = NULL;
s->hyperlinks = NULL;
screen_reinit(s);
}
@ -104,7 +105,7 @@ screen_reinit(struct screen *s)
s->rupper = 0;
s->rlower = screen_size_y(s) - 1;
s->mode = MODE_CURSOR|MODE_WRAP;
s->mode = MODE_CURSOR|MODE_WRAP|(s->mode & MODE_CRLF);
if (options_get_number(global_options, "extended-keys") == 2)
s->mode |= MODE_KEXTENDED;
@ -120,6 +121,17 @@ screen_reinit(struct screen *s)
screen_clear_selection(s);
screen_free_titles(s);
image_free_all(s);
screen_reset_hyperlinks(s);
}
/* Reset hyperlinks of a screen. */
void
screen_reset_hyperlinks(struct screen *s)
{
if (s->hyperlinks == NULL)
s->hyperlinks = hyperlinks_init();
else
hyperlinks_reset(s->hyperlinks);
}
/* Destroy a screen. */
@ -138,6 +150,8 @@ screen_free(struct screen *s)
grid_destroy(s->saved_grid);
grid_destroy(s->grid);
if (s->hyperlinks != NULL)
hyperlinks_free(s->hyperlinks);
screen_free_titles(s);
image_free_all(s);
}
@ -666,9 +680,9 @@ screen_mode_to_string(int mode)
static char tmp[1024];
if (mode == 0)
return "NONE";
return ("NONE");
if (mode == ALL_MODES)
return "ALL";
return ("ALL");
*tmp = '\0';
if (mode & MODE_CURSOR)

186
server-acl.c Normal file
View File

@ -0,0 +1,186 @@
/* $OpenBSD$ */
/*
* Copyright (c) 2021 Holland Schutte, Jayson Morberg
* Copyright (c) 2021 Dallas Lyons <dallasdlyons@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/stat.h>
#include <sys/socket.h>
#include <ctype.h>
#include <pwd.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include "tmux.h"
struct server_acl_user {
uid_t uid;
int flags;
#define SERVER_ACL_READONLY 0x1
RB_ENTRY(server_acl_user) entry;
};
static int
server_acl_cmp(struct server_acl_user *user1, struct server_acl_user *user2)
{
if (user1->uid < user2->uid)
return (-1);
return (user1->uid > user2->uid);
}
RB_HEAD(server_acl_entries, server_acl_user) server_acl_entries;
RB_GENERATE_STATIC(server_acl_entries, server_acl_user, entry, server_acl_cmp);
/* Initialize server_acl tree. */
void
server_acl_init(void)
{
RB_INIT(&server_acl_entries);
if (getuid() != 0)
server_acl_user_allow(0);
server_acl_user_allow(getuid());
}
/* Find user entry. */
struct server_acl_user*
server_acl_user_find(uid_t uid)
{
struct server_acl_user find = { .uid = uid };
return (RB_FIND(server_acl_entries, &server_acl_entries, &find));
}
/* Display the tree. */
void
server_acl_display(struct cmdq_item *item)
{
struct server_acl_user *loop;
struct passwd *pw;
const char *name;
RB_FOREACH(loop, server_acl_entries, &server_acl_entries) {
if (loop->uid == 0)
continue;
if ((pw = getpwuid(loop->uid)) != NULL)
name = pw->pw_name;
else
name = "unknown";
if (loop->flags == SERVER_ACL_READONLY)
cmdq_print(item, "%s (R)", name);
else
cmdq_print(item, "%s (W)", name);
}
}
/* Allow a user. */
void
server_acl_user_allow(uid_t uid)
{
struct server_acl_user *user;
user = server_acl_user_find(uid);
if (user == NULL) {
user = xcalloc(1, sizeof *user);
user->uid = uid;
RB_INSERT(server_acl_entries, &server_acl_entries, user);
}
}
/* Deny a user (remove from the tree). */
void
server_acl_user_deny(uid_t uid)
{
struct server_acl_user *user;
user = server_acl_user_find(uid);
if (user != NULL) {
RB_REMOVE(server_acl_entries, &server_acl_entries, user);
free(user);
}
}
/* Allow this user write access. */
void
server_acl_user_allow_write(uid_t uid)
{
struct server_acl_user *user;
struct client *c;
user = server_acl_user_find(uid);
if (user == NULL)
return;
user->flags &= ~SERVER_ACL_READONLY;
TAILQ_FOREACH(c, &clients, entry) {
uid = proc_get_peer_uid(c->peer);
if (uid != (uid_t)-1 && uid == user->uid)
c->flags &= ~CLIENT_READONLY;
}
}
/* Deny this user write access. */
void
server_acl_user_deny_write(uid_t uid)
{
struct server_acl_user *user;
struct client *c;
user = server_acl_user_find(uid);
if (user == NULL)
return;
user->flags |= SERVER_ACL_READONLY;
TAILQ_FOREACH(c, &clients, entry) {
uid = proc_get_peer_uid(c->peer);
if (uid != (uid_t)-1 && uid == user->uid)
c->flags |= CLIENT_READONLY;
}
}
/*
* Check if the client's UID exists in the ACL list and if so, set as read only
* if needed. Return false if the user does not exist.
*/
int
server_acl_join(struct client *c)
{
struct server_acl_user *user;
uid_t uid;
uid = proc_get_peer_uid(c->peer);
if (uid == (uid_t)-1)
return (0);
user = server_acl_user_find(uid);
if (user == NULL)
return (0);
if (user->flags & SERVER_ACL_READONLY)
c->flags |= CLIENT_READONLY;
return (1);
}
/* Get UID for user entry. */
uid_t
server_acl_get_uid(struct server_acl_user *user)
{
return (user->uid);
}

View File

@ -2218,7 +2218,8 @@ server_client_check_pane_buffer(struct window_pane *wp)
}
wpo = control_pane_offset(c, wp, &flag);
if (wpo == NULL) {
off = 0;
if (!flag)
off = 0;
continue;
}
if (!flag)
@ -2772,6 +2773,14 @@ server_client_dispatch(struct imsg *imsg, void *arg)
}
}
/* Callback when command is not allowed. */
static enum cmd_retval
server_client_read_only(struct cmdq_item *item, __unused void *data)
{
cmdq_error(item, "client is read-only");
return (CMD_RETURN_ERROR);
}
/* Callback when command is done. */
static enum cmd_retval
server_client_command_done(struct cmdq_item *item, __unused void *data)
@ -2780,8 +2789,11 @@ server_client_command_done(struct cmdq_item *item, __unused void *data)
if (~c->flags & CLIENT_ATTACHED)
c->flags |= CLIENT_EXIT;
else if (~c->flags & CLIENT_EXIT)
else if (~c->flags & CLIENT_EXIT) {
if (c->flags & CLIENT_CONTROL)
control_ready(c);
tty_send_requests(&c->tty);
}
return (CMD_RETURN_NORMAL);
}
@ -2796,6 +2808,7 @@ server_client_dispatch_command(struct client *c, struct imsg *imsg)
char **argv, *cause;
struct cmd_parse_result *pr;
struct args_value *values;
struct cmdq_item *new_item;
if (c->flags & CLIENT_EXIT)
return;
@ -2834,7 +2847,12 @@ server_client_dispatch_command(struct client *c, struct imsg *imsg)
free(values);
cmd_free_argv(argc, argv);
cmdq_append(c, cmdq_get_command(pr->cmdlist, NULL));
if ((c->flags & CLIENT_READONLY) &&
!cmd_list_all_have(pr->cmdlist, CMD_READONLY))
new_item = cmdq_get_callback(server_client_read_only, NULL);
else
new_item = cmdq_get_command(pr->cmdlist, NULL);
cmdq_append(c, new_item);
cmdq_append(c, cmdq_get_callback(server_client_command_done, NULL));
cmd_list_free(pr->cmdlist);
@ -3072,9 +3090,11 @@ server_client_set_flags(struct client *c, const char *flags)
continue;
log_debug("client %s set flag %s", c->name, next);
if (not)
if (not) {
if (c->flags & CLIENT_READONLY)
flag &= ~CLIENT_READONLY;
c->flags &= ~flag;
else
} else
c->flags |= flag;
if (flag == CLIENT_CONTROL_NOOUTPUT)
control_reset_offsets(c);
@ -3142,7 +3162,7 @@ server_client_add_client_window(struct client *c, u_int id)
cw->window = id;
RB_INSERT(client_windows, &c->windows, cw);
}
return cw;
return (cw);
}
/* Get client active pane. */

View File

@ -53,6 +53,8 @@ struct cmd_find_state marked_pane;
static u_int message_next;
struct message_list message_log;
time_t current_time;
static int server_loop(void);
static void server_send_exit(void);
static void server_accept(int, short, void *);
@ -211,7 +213,6 @@ server_start(struct tmuxproc *client, int flags, struct event_base *base,
RB_INIT(&sessions);
key_bindings_init();
TAILQ_INIT(&message_log);
gettimeofday(&start_time, NULL);
#ifdef HAVE_SYSTEMD
@ -245,6 +246,8 @@ server_start(struct tmuxproc *client, int flags, struct event_base *base,
evtimer_set(&server_ev_tidy, server_tidy_event, NULL);
evtimer_add(&server_ev_tidy, &tv);
server_acl_init();
server_add_accept(0);
proc_loop(server_proc, server_loop);
@ -261,6 +264,8 @@ server_loop(void)
struct client *c;
u_int items;
current_time = time (NULL);
do {
items = cmdq_next(NULL);
TAILQ_FOREACH(c, &clients, entry) {
@ -361,9 +366,10 @@ server_update_socket(void)
static void
server_accept(int fd, short events, __unused void *data)
{
struct sockaddr_storage sa;
socklen_t slen = sizeof sa;
int newfd;
struct sockaddr_storage sa;
socklen_t slen = sizeof sa;
int newfd;
struct client *c;
server_add_accept(0);
if (!(events & EV_READ))
@ -380,11 +386,16 @@ server_accept(int fd, short events, __unused void *data)
}
fatal("accept failed");
}
if (server_exit) {
close(newfd);
return;
}
server_client_create(newfd);
c = server_client_create(newfd);
if (!server_acl_join(c)) {
c->exit_message = xstrdup("access not allowed");
c->flags |= CLIENT_EXIT;
}
}
/*

25
spawn.c
View File

@ -209,7 +209,7 @@ spawn_pane(struct spawn_context *sc, char **cause)
struct window_pane *new_wp;
struct environ *child;
struct environ_entry *ee;
char **argv, *cp, **argvp, *argv0, *cwd;
char **argv, *cp, **argvp, *argv0, *cwd, *new_cwd;
const char *cmd, *tmp;
int argc;
u_int idx;
@ -225,9 +225,15 @@ spawn_pane(struct spawn_context *sc, char **cause)
* Work out the current working directory. If respawning, use
* the pane's stored one unless specified.
*/
if (sc->cwd != NULL)
if (sc->cwd != NULL) {
cwd = format_single(item, sc->cwd, c, target->s, NULL, NULL);
else if (~sc->flags & SPAWN_RESPAWN)
if (*cwd != '/') {
xasprintf(&new_cwd, "%s/%s", server_client_get_cwd(c,
target->s), cwd);
free(cwd);
cwd = new_cwd;
}
} else if (~sc->flags & SPAWN_RESPAWN)
cwd = xstrdup(server_client_get_cwd(c, target->s));
else
cwd = NULL;
@ -335,8 +341,7 @@ spawn_pane(struct spawn_context *sc, char **cause)
log_debug("%s: cmd=%s", __func__, cp);
free(cp);
}
if (cwd != NULL)
log_debug("%s: cwd=%s", __func__, cwd);
log_debug("%s: cwd=%s", __func__, new_wp->cwd);
cmd_log_argv(new_wp->argc, new_wp->argv, "%s", __func__);
environ_log(child, "%s: environment ", __func__);
@ -382,9 +387,13 @@ 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 &&
((tmp = find_home()) == NULL || chdir(tmp) != 0) &&
chdir("/") != 0)
if (chdir(new_wp->cwd) == 0)
environ_set(child, "PWD", 0, "%s", new_wp->cwd);
else if ((tmp = find_home()) != NULL && chdir(tmp) == 0)
environ_set(child, "PWD", 0, "%s", tmp);
else if (chdir("/") == 0)
environ_set(child, "PWD", 0, "/");
else
fatal("chdir failed");
/*

View File

@ -1576,11 +1576,25 @@ status_prompt_add_history(const char *line, u_int type)
status_prompt_hsize[type] = newsize;
}
/* Add to completion list. */
static void
status_prompt_add_list(char ***list, u_int *size, const char *s)
{
u_int i;
for (i = 0; i < *size; i++) {
if (strcmp((*list)[i], s) == 0)
return;
}
*list = xreallocarray(*list, (*size) + 1, sizeof **list);
(*list)[(*size)++] = xstrdup(s);
}
/* Build completion list. */
static char **
status_prompt_complete_list(u_int *size, const char *s, int at_start)
{
char **list = NULL;
char **list = NULL, *tmp;
const char **layout, *value, *cp;
const struct cmd_entry **cmdent;
const struct options_table_entry *oe;
@ -1594,15 +1608,11 @@ status_prompt_complete_list(u_int *size, const char *s, int at_start)
*size = 0;
for (cmdent = cmd_table; *cmdent != NULL; cmdent++) {
if (strncmp((*cmdent)->name, s, slen) == 0) {
list = xreallocarray(list, (*size) + 1, sizeof *list);
list[(*size)++] = xstrdup((*cmdent)->name);
}
if (strncmp((*cmdent)->name, s, slen) == 0)
status_prompt_add_list(&list, size, (*cmdent)->name);
if ((*cmdent)->alias != NULL &&
strncmp((*cmdent)->alias, s, slen) == 0) {
list = xreallocarray(list, (*size) + 1, sizeof *list);
list[(*size)++] = xstrdup((*cmdent)->alias);
}
strncmp((*cmdent)->alias, s, slen) == 0)
status_prompt_add_list(&list, size, (*cmdent)->alias);
}
o = options_get_only(global_options, "command-alias");
if (o != NULL) {
@ -1615,8 +1625,9 @@ status_prompt_complete_list(u_int *size, const char *s, int at_start)
if (slen > valuelen || strncmp(value, s, slen) != 0)
goto next;
list = xreallocarray(list, (*size) + 1, sizeof *list);
list[(*size)++] = xstrndup(value, valuelen);
xasprintf(&tmp, "%.*s", (int)valuelen, value);
status_prompt_add_list(&list, size, tmp);
free(tmp);
next:
a = options_array_next(a);
@ -1624,18 +1635,13 @@ status_prompt_complete_list(u_int *size, const char *s, int at_start)
}
if (at_start)
return (list);
for (oe = options_table; oe->name != NULL; oe++) {
if (strncmp(oe->name, s, slen) == 0) {
list = xreallocarray(list, (*size) + 1, sizeof *list);
list[(*size)++] = xstrdup(oe->name);
}
if (strncmp(oe->name, s, slen) == 0)
status_prompt_add_list(&list, size, oe->name);
}
for (layout = layouts; *layout != NULL; layout++) {
if (strncmp(*layout, s, slen) == 0) {
list = xreallocarray(list, (*size) + 1, sizeof *list);
list[(*size)++] = xstrdup(*layout);
}
if (strncmp(*layout, s, slen) == 0)
status_prompt_add_list(&list, size, *layout);
}
return (list);
}

View File

@ -30,7 +30,7 @@
/* Default style. */
static struct style style_default = {
{ { { ' ' }, 0, 1, 1 }, 0, 0, 8, 8, 0 },
{ { { ' ' }, 0, 1, 1 }, 0, 0, 8, 8, 0, 0 },
0,
8,

98
tmux.1
View File

@ -217,8 +217,6 @@ that is set does not contain
.Qq UTF-8
or
.Qq UTF8 .
This is equivalent to
.Fl T Ar UTF-8 .
.It Fl T Ar features
Set terminal features for the client.
This is a comma-separated list of features.
@ -1488,6 +1486,44 @@ option.
.D1 Pq alias: Ic rename
Rename the session to
.Ar new-name .
.It Xo Ic server-access
.Op Fl adlrw
.Op Ar user
.Xc
Change the access or read/write permission of
.Ar user .
The user running the
.Nm
server (its owner) and the root user cannot be changed and are always
permitted access.
.Pp
.Fl a
and
.Fl d
are used to give or revoke access for the specified user.
If the user is already attached, the
.Fl d
flag causes their clients to be detached.
.Pp
.Fl r
and
.Fl w
change the permissions for
.Ar user :
.Fl r
makes their clients read-only and
.Fl w
writable.
.Fl l
lists current access permissions.
.Pp
By default, the access list is empty and
.Nm
creates sockets with file system permissions preventing access by any user
other than the owner (and root).
These permissions must be changed manually.
Great care should be taken not to allow access to untrusted users even
read-only.
.Tg showmsgs
.It Xo Ic show-messages
.Op Fl JT
@ -1775,6 +1811,7 @@ The following commands are supported in copy mode:
.It Li "search-forward <for>" Ta "/" Ta ""
.It Li "search-forward-incremental <for>" Ta "" Ta "C-s"
.It Li "search-forward-text <for>" Ta "" Ta ""
.It Li "scroll-middle" Ta "z" Ta ""
.It Li "search-reverse" Ta "N" Ta "N"
.It Li "select-line" Ta "V" Ta ""
.It Li "select-word" Ta "" Ta ""
@ -2077,9 +2114,11 @@ is not given, "detach-client -t '%%'" is used.
specifies the initial sort field: one of
.Ql name ,
.Ql size ,
.Ql creation ,
.Ql creation
(time),
or
.Ql activity .
.Ql activity
(time).
.Fl r
reverses the sort order.
.Fl f
@ -2161,7 +2200,8 @@ specifies the initial sort field: one of
.Ql index ,
.Ql name ,
or
.Ql time .
.Ql time
(activity).
.Fl r
reverses the sort order.
.Fl f
@ -3621,6 +3661,14 @@ Allows setting the cursor style.
Supports extended keys.
.It focus
Supports focus reporting.
.It hyperlinks
Supports OSC 8 hyperlinks.
.It ignorefkeys
Ignore function keys from
.Xr terminfo 5
and use the
.Nm
internal set only.
.It margins
Supports DECSLRM margins.
.It mouse
@ -4417,11 +4465,17 @@ Available pane options are:
.Pp
.Bl -tag -width Ds -compact
.It Xo Ic allow-passthrough
.Op Ic on | off
.Op Ic on | off | all
.Xc
Allow programs in the pane to bypass
.Nm
using a terminal escape sequence (\eePtmux;...\ee\e\e).
If set to
.Ic on ,
passthrough sequences will be allowed only if the pane is visible.
If set to
.Ic all ,
they will be allowed even if the pane is invisible.
.Pp
.It Xo Ic allow-rename
.Op Ic on | off
@ -5072,7 +5126,7 @@ The following variables are available, where appropriate:
.It Li "client_name" Ta "" Ta "Name of client"
.It Li "client_pid" Ta "" Ta "PID of client process"
.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_readonly" Ta "" Ta "1 if client is read-only"
.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"
@ -5117,6 +5171,7 @@ The following variables are available, where appropriate:
.It Li "mouse_all_flag" Ta "" Ta "Pane mouse all flag"
.It Li "mouse_any_flag" Ta "" Ta "Pane mouse any flag"
.It Li "mouse_button_flag" Ta "" Ta "Pane mouse button flag"
.It Li "mouse_hyperlink" Ta "" Ta "Hyperlink under mouse, if any"
.It Li "mouse_line" Ta "" Ta "Line under mouse, if any"
.It Li "mouse_sgr_flag" Ta "" Ta "Pane mouse SGR flag"
.It Li "mouse_standard_flag" Ta "" Ta "Pane mouse standard flag"
@ -5157,6 +5212,7 @@ The following variables are available, where appropriate:
.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_start_command" Ta "" Ta "Command pane started with"
.It Li "pane_start_path" Ta "" Ta "Path pane started with"
.It Li "pane_synchronized" Ta "" Ta "1 if pane is synchronized"
.It Li "pane_tabs" Ta "" Ta "Pane tab positions"
.It Li "pane_title" Ta "#T" Ta "Title of pane (can be set by application)"
@ -5583,11 +5639,11 @@ session option.
.Pp
Commands related to the status line are as follows:
.Bl -tag -width Ds
.Tg clrphist
.Tg clearphist
.It Xo Ic clear-prompt-history
.Op Fl T Ar prompt-type
.Xc
.D1 Pq alias: Ic clrphist
.D1 Pq alias: Ic clearphist
Clear status prompt history for prompt type
.Ar prompt-type .
If
@ -5841,7 +5897,7 @@ milliseconds.
If
.Ar delay
is not given, the
.Ic message-time
.Ic display-time
option is used; a delay of zero waits for a key press.
.Ql N
ignores key presses and closes only after the delay expires.
@ -6062,7 +6118,8 @@ is not given, "paste-buffer -b '%%'" is used.
.Pp
.Fl O
specifies the initial sort field: one of
.Ql time ,
.Ql time
(creation),
.Ql name
or
.Ql size .
@ -6080,9 +6137,14 @@ a format for each shortcut key; both are evaluated once for each line.
starts without the preview.
This command works only if at least one client is attached.
.Tg clearhist
.It Ic clear-history Op Fl t Ar target-pane
.It Xo Ic clear-history
.Op Fl H
.Op Fl t Ar target-pane
.Xc
.D1 Pq alias: Ic clearhist
Remove and free the history for the specified pane.
.Fl H
also removes all hyperlinks.
.Tg deleteb
.It Ic delete-buffer Op Fl b Ar buffer-name
.D1 Pq alias: Ic deleteb
@ -6370,6 +6432,12 @@ Disable and enable focus reporting.
These are set automatically if the
.Em XT
capability is present.
.It Em \&Hls
Set or clear a hyperlink annotation.
.It Em \&Nobr
Tell
.Nm
that the terminal does not use bright colors for bold display.
.It Em \&Rect
Tell
.Nm
@ -6489,6 +6557,8 @@ The client is now attached to the session with ID
.Ar session-id ,
which is named
.Ar name .
.It Ic %config-error Ar error
An error has happened in a configuration file.
.It Ic %continue Ar pane-id
The pane has been continued after being paused (if the
.Ar pause-after
@ -6532,6 +6602,10 @@ escapes non-printable characters and backslash as octal \\xxx.
The pane with ID
.Ar pane-id
has changed mode.
.It Ic %paste-buffer-changed Ar name
Paste buffer
.Ar name
has been changed.
.It Ic %pause Ar pane-id
The pane has been paused (if the
.Ar pause-after

2
tmux.c
View File

@ -325,7 +325,7 @@ find_home(void)
const char *
getversion(void)
{
return TMUX_VERSION;
return (TMUX_VERSION);
}
int

119
tmux.h
View File

@ -50,6 +50,8 @@ struct control_state;
struct environ;
struct format_job_tree;
struct format_tree;
struct hyperlinks_uri;
struct hyperlinks;
struct input_ctx;
struct job;
struct menu_data;
@ -367,6 +369,7 @@ enum tty_code_code {
TTYC_ENFCS,
TTYC_ENMG,
TTYC_FSL,
TTYC_HLS,
TTYC_HOME,
TTYC_HPA,
TTYC_ICH,
@ -513,6 +516,7 @@ enum tty_code_code {
TTYC_KUP6,
TTYC_KUP7,
TTYC_MS,
TTYC_NOBR,
TTYC_OL,
TTYC_OP,
TTYC_RECT,
@ -583,6 +587,12 @@ enum tty_code_code {
#define MOTION_MOUSE_MODES (MODE_MOUSE_BUTTON|MODE_MOUSE_ALL)
#define CURSOR_MODES (MODE_CURSOR|MODE_CURSOR_BLINKING|MODE_CURSOR_VERY_VISIBLE)
/* Mouse protocol constants. */
#define MOUSE_PARAM_MAX 0xff
#define MOUSE_PARAM_UTF8_MAX 0x7ff
#define MOUSE_PARAM_BTN_OFF 0x20
#define MOUSE_PARAM_POS_OFF 0x21
/* A single UTF-8 character. */
typedef u_int utf8_char;
@ -686,6 +696,7 @@ struct grid_cell {
int fg;
int bg;
int us;
u_int link;
};
/* Grid extended cell entry. */
@ -696,6 +707,7 @@ struct grid_extd_entry {
int fg;
int bg;
int us;
u_int link;
} __packed;
/* Grid cell entry. */
@ -722,6 +734,7 @@ struct grid_line {
u_int extdsize;
int flags;
time_t time;
};
/* Entire grid of cells. */
@ -862,6 +875,8 @@ struct screen {
struct images images;
struct screen_write_cline *write_list;
struct hyperlinks *hyperlinks;
};
/* Screen write context. */
@ -1436,39 +1451,47 @@ struct tty_ctx {
u_int num;
void *ptr;
int more;
void *ptr2;
/*
* Whether this command should be sent even when the pane is not
* visible (used for a passthrough sequence when allow-passthrough is
* "all").
*/
int allow_invisible_panes;
/*
* Cursor and region position before the screen was updated - this is
* where the command should be applied; the values in the screen have
* already been updated.
*/
u_int ocx;
u_int ocy;
u_int ocx;
u_int ocy;
u_int orupper;
u_int orlower;
u_int orupper;
u_int orlower;
/* Target region (usually pane) offset and size. */
u_int xoff;
u_int yoff;
u_int rxoff;
u_int ryoff;
u_int sx;
u_int sy;
u_int xoff;
u_int yoff;
u_int rxoff;
u_int ryoff;
u_int sx;
u_int sy;
/* The background colour used for clearing (erasing). */
u_int bg;
u_int bg;
/* The default colours and palette. */
struct grid_cell defaults;
struct colour_palette *palette;
struct grid_cell defaults;
struct colour_palette *palette;
/* Containing region (usually window) offset and size. */
int bigger;
u_int wox;
u_int woy;
u_int wsx;
u_int wsy;
int bigger;
u_int wox;
u_int woy;
u_int wsx;
u_int wsy;
};
/* Saved message entry. */
@ -2045,6 +2068,7 @@ struct tmuxpeer *proc_add_peer(struct tmuxproc *, int,
void (*)(struct imsg *, void *), void *);
void proc_remove_peer(struct tmuxpeer *);
void proc_kill_peer(struct tmuxpeer *);
void proc_flush_peer(struct tmuxpeer *);
void proc_toggle_log(struct tmuxproc *);
pid_t proc_fork_and_daemon(int *);
uid_t proc_get_peer_uid(struct tmuxpeer *);
@ -2071,6 +2095,7 @@ u_int paste_buffer_order(struct paste_buffer *);
time_t paste_buffer_created(struct paste_buffer *);
const char *paste_buffer_data(struct paste_buffer *, size_t *);
struct paste_buffer *paste_walk(struct paste_buffer *);
int paste_is_empty(void);
struct paste_buffer *paste_get_top(const char **);
struct paste_buffer *paste_get_name(const char *);
void paste_free(struct paste_buffer *);
@ -2107,6 +2132,7 @@ void format_add_cb(struct format_tree *, const char *, format_cb);
void format_log_debug(struct format_tree *, const char *);
void format_each(struct format_tree *, void (*)(const char *,
const char *, void *), void *);
char *format_pretty_time(time_t, int);
char *format_expand_time(struct format_tree *, const char *);
char *format_expand(struct format_tree *, const char *);
char *format_single(struct cmdq_item *, const char *,
@ -2129,6 +2155,8 @@ void format_defaults_paste_buffer(struct format_tree *,
struct paste_buffer *);
void format_lost_client(struct client *);
char *format_grid_word(struct grid *, u_int, u_int);
char *format_grid_hyperlink(struct grid *, u_int, u_int,
struct screen *);
char *format_grid_line(struct grid *, u_int);
/* format-draw.c */
@ -2147,6 +2175,7 @@ void notify_winlink(const char *, struct winlink *);
void notify_session_window(const char *, struct session *, struct window *);
void notify_window(const char *, struct window *);
void notify_pane(const char *, struct window_pane *);
void notify_paste_buffer(const char *);
/* options.c */
struct options *options_create(struct options *);
@ -2256,7 +2285,8 @@ void tty_update_window_offset(struct window *);
void tty_update_client_offset(struct client *);
void tty_raw(struct tty *, const char *);
void tty_attributes(struct tty *, const struct grid_cell *,
const struct grid_cell *, struct colour_palette *);
const struct grid_cell *, struct colour_palette *,
struct hyperlinks *);
void tty_reset(struct tty *);
void tty_region_off(struct tty *);
void tty_margin_off(struct tty *);
@ -2273,7 +2303,8 @@ void tty_puts(struct tty *, const char *);
void tty_putc(struct tty *, u_char);
void tty_putn(struct tty *, const void *, size_t, u_int);
void tty_cell(struct tty *, const struct grid_cell *,
const struct grid_cell *, struct colour_palette *);
const struct grid_cell *, struct colour_palette *,
struct hyperlinks *);
int tty_init(struct tty *, struct client *);
void tty_resize(struct tty *);
void tty_set_size(struct tty *, u_int, u_int, u_int, u_int);
@ -2292,7 +2323,7 @@ int tty_open(struct tty *, char **);
void tty_close(struct tty *);
void tty_free(struct tty *);
void tty_update_features(struct tty *);
void tty_set_selection(struct tty *, const char *, size_t);
void tty_set_selection(struct tty *, const char *, const char *, size_t);
void tty_write(void (*)(struct tty *, const struct tty_ctx *),
struct tty_ctx *);
void tty_cmd_alignmenttest(struct tty *, const struct tty_ctx *);
@ -2398,10 +2429,16 @@ struct args_value *args_first_value(struct args *, u_char);
struct args_value *args_next_value(struct args_value *);
long long args_strtonum(struct args *, u_char, long long, long long,
char **);
long long args_strtonum_and_expand(struct args *, u_char, long long,
long long, struct cmdq_item *, char **);
long long args_percentage(struct args *, u_char, long long,
long long, long long, char **);
long long args_string_percentage(const char *, long long, long long,
long long, char **);
long long args_percentage_and_expand(struct args *, u_char, long long,
long long, long long, struct cmdq_item *, char **);
long long args_string_percentage_and_expand(const char *, long long,
long long, long long, struct cmdq_item *, char **);
/* cmd-find.c */
int cmd_find_target(struct cmd_find_state *, struct cmdq_item *,
@ -2595,6 +2632,7 @@ extern struct tmuxproc *server_proc;
extern struct clients clients;
extern struct cmd_find_state marked_pane;
extern struct message_list message_log;
extern time_t current_time;
void server_set_marked(struct session *, struct winlink *,
struct window_pane *);
void server_clear_marked(void);
@ -2769,7 +2807,7 @@ void grid_clear_lines(struct grid *, u_int, u_int, u_int);
void grid_move_lines(struct grid *, u_int, u_int, u_int, u_int);
void grid_move_cells(struct grid *, u_int, u_int, u_int, u_int, u_int);
char *grid_string_cells(struct grid *, u_int, u_int, u_int,
struct grid_cell **, int, int, int);
struct grid_cell **, int, int, int, struct screen *);
void grid_duplicate_lines(struct grid *, u_int, struct grid *, u_int,
u_int);
void grid_reflow(struct grid *, u_int);
@ -2882,8 +2920,10 @@ void screen_write_collect_end(struct screen_write_ctx *);
void screen_write_collect_add(struct screen_write_ctx *,
const struct grid_cell *);
void screen_write_cell(struct screen_write_ctx *, const struct grid_cell *);
void screen_write_setselection(struct screen_write_ctx *, u_char *, u_int);
void screen_write_rawstring(struct screen_write_ctx *, u_char *, u_int);
void screen_write_setselection(struct screen_write_ctx *, const char *,
u_char *, u_int);
void screen_write_rawstring(struct screen_write_ctx *, u_char *, u_int,
int);
void screen_write_sixelimage(struct screen_write_ctx *,
struct sixel_image *, u_int);
void screen_write_alternateon(struct screen_write_ctx *,
@ -2900,6 +2940,7 @@ void screen_init(struct screen *, u_int, u_int, u_int);
void screen_reinit(struct screen *);
void screen_free(struct screen *);
void screen_reset_tabs(struct screen *);
void screen_reset_hyperlinks(struct screen *);
void screen_set_cursor_style(u_int, enum screen_cursor_style *, int *);
void screen_set_cursor_colour(struct screen *, int);
int screen_set_title(struct screen *, const char *);
@ -3007,6 +3048,7 @@ void *window_pane_get_new_data(struct window_pane *,
void window_pane_update_used_data(struct window_pane *,
struct window_pane_offset *, size_t);
void window_set_fill_character(struct window *);
void window_pane_default_cursor(struct window_pane *);
/* layout.c */
u_int layout_count_cells(struct layout_cell *);
@ -3043,7 +3085,7 @@ void layout_spread_out(struct window_pane *);
/* layout-custom.c */
char *layout_dump(struct layout_cell *);
int layout_parse(struct window *, const char *);
int layout_parse(struct window *, const char *, char **);
/* layout-set.c */
int layout_set_lookup(const char *);
@ -3108,8 +3150,9 @@ extern const struct window_mode window_client_mode;
/* window-copy.c */
extern const struct window_mode window_copy_mode;
extern const struct window_mode window_view_mode;
void printflike(2, 3) window_copy_add(struct window_pane *, const char *, ...);
void printflike(2, 0) window_copy_vadd(struct window_pane *, const char *,
void printflike(3, 4) window_copy_add(struct window_pane *, int, const char *,
...);
void printflike(3, 0) window_copy_vadd(struct window_pane *, int, const char *,
va_list);
void window_copy_pageup(struct window_pane *, int);
void window_copy_start_drag(struct client *, struct mouse_event *);
@ -3127,6 +3170,7 @@ char *parse_window_name(const char *);
/* control.c */
void control_discard(struct client *);
void control_start(struct client *);
void control_ready(struct client *);
void control_stop(struct client *);
void control_set_pane_on(struct client *, struct window_pane *);
void control_set_pane_off(struct client *, struct window_pane *);
@ -3157,6 +3201,7 @@ void control_notify_session_renamed(struct session *);
void control_notify_session_created(struct session *);
void control_notify_session_closed(struct session *);
void control_notify_session_window_changed(struct session *);
void control_notify_paste_buffer_changed(const char *);
/* session.c */
extern struct sessions sessions;
@ -3311,4 +3356,24 @@ char *sixel_print(struct sixel_image *, struct sixel_image *,
size_t *);
struct screen *sixel_to_screen(struct sixel_image *);
/* server-acl.c */
void server_acl_init(void);
struct server_acl_user *server_acl_user_find(uid_t);
void server_acl_display(struct cmdq_item *);
void server_acl_user_allow(uid_t);
void server_acl_user_deny(uid_t);
void server_acl_user_allow_write(uid_t);
void server_acl_user_deny_write(uid_t);
int server_acl_join(struct client *);
uid_t server_acl_get_uid(struct server_acl_user *);
/* hyperlink.c */
u_int hyperlinks_put(struct hyperlinks *, const char *,
const char *);
int hyperlinks_get(struct hyperlinks *, u_int,
const char **, const char **, const char **);
struct hyperlinks *hyperlinks_init(void);
void hyperlinks_reset(struct hyperlinks *);
void hyperlinks_free(struct hyperlinks *);
#endif /* TMUX_H */

View File

@ -19,7 +19,7 @@ Standardized Video Terminals" in the April-1984 issue of BYTE magazine.
ANSI X3.4-1977 defines the 7-bit ASCII character set (C0 and G0). It was
written in 1968, revised in 1977, and explains the decisions made in laying out
the ASCII code. In particular, it explains why ANSI chose to make ASCII
incompatible with EBCDIC in order to make it self-consistant.
incompatible with EBCDIC in order to make it self-consistent.
ANSI X3.41-1974 introduces the idea of an 8-bit ASCII character set (C1 and G1
in addition to the existing C0 and G0). It describes how to use the 8-bit
@ -268,7 +268,7 @@ Oct Hex * (* marks function used in DEC VT series or LA series terminals)
073 3B ;
074 3C < * DECANSI - Switch from VT52 mode to VT100 mode
075 3D = * DECKPAM - Set keypad to applications mode (ESCape instead of digits)
076 3E > * DECKPNM - Set keypad to numeric mode (digits intead of ESCape seq)
076 3E > * DECKPNM - Set keypad to numeric mode (digits instead of ESCape seq)
077 3F ?
DCS Device Control Strings used by DEC terminals (ends with ST)
@ -294,7 +294,7 @@ Pt = Start VT105 graphics on a VT125
==============================================================================
Indepenent control functions (from Appendix E of X3.64-1977).
Independent control functions (from Appendix E of X3.64-1977).
These four controls have the same meaning regardless of the current
definition of the C0 and C1 control sets. Each control is a two-character
ESCape sequence, the 2nd character is lowercase.
@ -449,7 +449,7 @@ Oct Hex * (* marks function used in DEC VT series or LA series terminals)
* [16h = TTM - Transmit Termination Mode, send scrolling region
[17h = SATM - Send Area Transmit Mode, send entire buffer
[18h = TSM - Tabulation Stop Mode, lines are independent
[19h = EBM - Editing Boundry Mode, all of memory affected
[19h = EBM - Editing Boundary Mode, all of memory affected
* [20h = LNM - Linefeed Newline Mode, LF interpreted as CR LF
* [?1h = DECCKM - Cursor Keys Mode, send ESC O A for cursor up
* [?2h = DECANM - ANSI Mode, use ESC < to switch VT52 to ANSI

View File

@ -21,6 +21,12 @@
#include <stdlib.h>
#include <string.h>
#if defined(HAVE_CURSES_H)
#include <curses.h>
#elif defined(HAVE_NCURSES_H)
#include <ncurses.h>
#endif
#include "tmux.h"
/*
@ -36,13 +42,13 @@
/* A named terminal feature. */
struct tty_feature {
const char *name;
const char **capabilities;
int flags;
const char *name;
const char *const *capabilities;
int flags;
};
/* Terminal has xterm(1) title setting. */
static const char *tty_feature_title_capabilities[] = {
static const char *const tty_feature_title_capabilities[] = {
"tsl=\\E]0;", /* should be using TS really */
"fsl=\\a",
NULL
@ -54,7 +60,7 @@ static const struct tty_feature tty_feature_title = {
};
/* Terminal has OSC 7 working directory. */
static const char *tty_feature_osc7_capabilities[] = {
static const char *const tty_feature_osc7_capabilities[] = {
"Swd=\\E]7;",
"fsl=\\a",
NULL
@ -66,7 +72,7 @@ static const struct tty_feature tty_feature_osc7 = {
};
/* Terminal has mouse support. */
static const char *tty_feature_mouse_capabilities[] = {
static const char *const tty_feature_mouse_capabilities[] = {
"kmous=\\E[M",
NULL
};
@ -77,7 +83,7 @@ static const struct tty_feature tty_feature_mouse = {
};
/* Terminal can set the clipboard with OSC 52. */
static const char *tty_feature_clipboard_capabilities[] = {
static const char *const tty_feature_clipboard_capabilities[] = {
"Ms=\\E]52;%p1%s;%p2%s\\a",
NULL
};
@ -87,12 +93,27 @@ static const struct tty_feature tty_feature_clipboard = {
0
};
/* Terminal supports OSC 8 hyperlinks. */
static const char *tty_feature_hyperlinks_capabilities[] = {
#if defined (__OpenBSD__) || (defined(NCURSES_VERSION_MAJOR) && \
(NCURSES_VERSION_MAJOR > 5 || \
(NCURSES_VERSION_MAJOR == 5 && NCURSES_VERSION_MINOR > 8)))
"*:Hls=\\E]8;%?%p1%l%tid=%p1%s%;;%p2%s\\E\\\\",
#endif
NULL
};
static const struct tty_feature tty_feature_hyperlinks = {
"hyperlinks",
tty_feature_hyperlinks_capabilities,
0
};
/*
* Terminal supports RGB colour. This replaces setab and setaf also since
* terminals with RGB have versions that do not allow setting colours from the
* 256 palette.
*/
static const char *tty_feature_rgb_capabilities[] = {
static const char *const tty_feature_rgb_capabilities[] = {
"AX",
"setrgbf=\\E[38;2;%p1%d;%p2%d;%p3%dm",
"setrgbb=\\E[48;2;%p1%d;%p2%d;%p3%dm",
@ -107,7 +128,7 @@ static const struct tty_feature tty_feature_rgb = {
};
/* Terminal supports 256 colours. */
static const char *tty_feature_256_capabilities[] = {
static const char *const tty_feature_256_capabilities[] = {
"AX",
"setab=\\E[%?%p1%{8}%<%t4%p1%d%e%p1%{16}%<%t10%p1%{8}%-%d%e48;5;%p1%d%;m",
"setaf=\\E[%?%p1%{8}%<%t3%p1%d%e%p1%{16}%<%t9%p1%{8}%-%d%e38;5;%p1%d%;m",
@ -120,7 +141,7 @@ static const struct tty_feature tty_feature_256 = {
};
/* Terminal supports overline. */
static const char *tty_feature_overline_capabilities[] = {
static const char *const tty_feature_overline_capabilities[] = {
"Smol=\\E[53m",
NULL
};
@ -131,7 +152,7 @@ static const struct tty_feature tty_feature_overline = {
};
/* Terminal supports underscore styles. */
static const char *tty_feature_usstyle_capabilities[] = {
static const char *const tty_feature_usstyle_capabilities[] = {
"Smulx=\\E[4::%p1%dm",
"Setulc=\\E[58::2::%p1%{65536}%/%d::%p1%{256}%/%{255}%&%d::%p1%{255}%&%d%;m",
"ol=\\E[59m",
@ -144,7 +165,7 @@ static const struct tty_feature tty_feature_usstyle = {
};
/* Terminal supports bracketed paste. */
static const char *tty_feature_bpaste_capabilities[] = {
static const char *const tty_feature_bpaste_capabilities[] = {
"Enbp=\\E[?2004h",
"Dsbp=\\E[?2004l",
NULL
@ -156,7 +177,7 @@ static const struct tty_feature tty_feature_bpaste = {
};
/* Terminal supports focus reporting. */
static const char *tty_feature_focus_capabilities[] = {
static const char *const tty_feature_focus_capabilities[] = {
"Enfcs=\\E[?1004h",
"Dsfcs=\\E[?1004l",
NULL
@ -168,7 +189,7 @@ static const struct tty_feature tty_feature_focus = {
};
/* Terminal supports cursor styles. */
static const char *tty_feature_cstyle_capabilities[] = {
static const char *const tty_feature_cstyle_capabilities[] = {
"Ss=\\E[%p1%d q",
"Se=\\E[2 q",
NULL
@ -180,7 +201,7 @@ static const struct tty_feature tty_feature_cstyle = {
};
/* Terminal supports cursor colours. */
static const char *tty_feature_ccolour_capabilities[] = {
static const char *const tty_feature_ccolour_capabilities[] = {
"Cs=\\E]12;%p1%s\\a",
"Cr=\\E]112\\a",
NULL
@ -192,7 +213,7 @@ static const struct tty_feature tty_feature_ccolour = {
};
/* Terminal supports strikethrough. */
static const char *tty_feature_strikethrough_capabilities[] = {
static const char *const tty_feature_strikethrough_capabilities[] = {
"smxx=\\E[9m",
NULL
};
@ -203,7 +224,7 @@ static const struct tty_feature tty_feature_strikethrough = {
};
/* Terminal supports synchronized updates. */
static const char *tty_feature_sync_capabilities[] = {
static const char *const tty_feature_sync_capabilities[] = {
"Sync=\\EP=%p1%ds\\E\\\\",
NULL
};
@ -214,7 +235,7 @@ static const struct tty_feature tty_feature_sync = {
};
/* Terminal supports extended keys. */
static const char *tty_feature_extkeys_capabilities[] = {
static const char *const tty_feature_extkeys_capabilities[] = {
"Eneks=\\E[>4;1m",
"Dseks=\\E[>4m",
NULL
@ -226,7 +247,7 @@ static const struct tty_feature tty_feature_extkeys = {
};
/* Terminal supports DECSLRM margins. */
static const char *tty_feature_margins_capabilities[] = {
static const char *const tty_feature_margins_capabilities[] = {
"Enmg=\\E[?69h",
"Dsmg=\\E[?69l",
"Clmg=\\E[s",
@ -240,7 +261,7 @@ static const struct tty_feature tty_feature_margins = {
};
/* Terminal supports DECFRA rectangle fill. */
static const char *tty_feature_rectfill_capabilities[] = {
static const char *const tty_feature_rectfill_capabilities[] = {
"Rect",
NULL
};
@ -250,15 +271,91 @@ static const struct tty_feature tty_feature_rectfill = {
TERM_DECFRA
};
/* Use builtin function keys only. */
static const char *const tty_feature_ignorefkeys_capabilities[] = {
"kf0@",
"kf1@",
"kf2@",
"kf3@",
"kf4@",
"kf5@",
"kf6@",
"kf7@",
"kf8@",
"kf9@",
"kf10@",
"kf11@",
"kf12@",
"kf13@",
"kf14@",
"kf15@",
"kf16@",
"kf17@",
"kf18@",
"kf19@",
"kf20@",
"kf21@",
"kf22@",
"kf23@",
"kf24@",
"kf25@",
"kf26@",
"kf27@",
"kf28@",
"kf29@",
"kf30@",
"kf31@",
"kf32@",
"kf33@",
"kf34@",
"kf35@",
"kf36@",
"kf37@",
"kf38@",
"kf39@",
"kf40@",
"kf41@",
"kf42@",
"kf43@",
"kf44@",
"kf45@",
"kf46@",
"kf47@",
"kf48@",
"kf49@",
"kf50@",
"kf51@",
"kf52@",
"kf53@",
"kf54@",
"kf55@",
"kf56@",
"kf57@",
"kf58@",
"kf59@",
"kf60@",
"kf61@",
"kf62@",
"kf63@",
NULL
};
static const struct tty_feature tty_feature_ignorefkeys = {
"ignorefkeys",
tty_feature_ignorefkeys_capabilities,
0
};
/* Available terminal features. */
static const struct tty_feature *tty_features[] = {
static const struct tty_feature *const tty_features[] = {
&tty_feature_256,
&tty_feature_bpaste,
&tty_feature_ccolour,
&tty_feature_clipboard,
&tty_feature_hyperlinks,
&tty_feature_cstyle,
&tty_feature_extkeys,
&tty_feature_focus,
&tty_feature_ignorefkeys,
&tty_feature_margins,
&tty_feature_mouse,
&tty_feature_osc7,
@ -323,9 +420,9 @@ tty_get_features(int feat)
int
tty_apply_features(struct tty_term *term, int feat)
{
const struct tty_feature *tf;
const char **capability;
u_int i;
const struct tty_feature *tf;
const char *const *capability;
u_int i;
if (feat == 0)
return (0);
@ -356,7 +453,7 @@ tty_apply_features(struct tty_term *term, int feat)
void
tty_default_features(int *feat, const char *name, u_int version)
{
static struct {
static const struct {
const char *name;
u_int version;
const char *features;
@ -369,14 +466,14 @@ tty_default_features(int *feat, const char *name, u_int version)
},
{ .name = "tmux",
.features = TTY_FEATURES_BASE_MODERN_XTERM
",ccolour,cstyle,focus,overline,usstyle"
",ccolour,cstyle,focus,overline,usstyle,hyperlinks"
},
{ .name = "rxvt-unicode",
.features = "256,bpaste,ccolour,cstyle,mouse,title"
.features = "256,bpaste,ccolour,cstyle,mouse,title,ignorefkeys"
},
{ .name = "iTerm2",
.features = TTY_FEATURES_BASE_MODERN_XTERM
",cstyle,extkeys,margins,usstyle,sync"
",cstyle,extkeys,margins,usstyle,sync,osc7,hyperlinks"
},
{ .name = "XTerm",
/*

View File

@ -126,7 +126,7 @@ static const struct tty_default_key_raw tty_default_raw_keys[] = {
{ "\033\033[C", KEYC_RIGHT|KEYC_CURSOR|KEYC_META },
{ "\033\033[D", KEYC_LEFT|KEYC_CURSOR|KEYC_META },
/* Other (xterm) "cursor" keys. */
/* Other xterm keys. */
{ "\033OH", KEYC_HOME },
{ "\033OF", KEYC_END },
@ -139,7 +139,7 @@ static const struct tty_default_key_raw tty_default_raw_keys[] = {
{ "\033\033[H", KEYC_HOME|KEYC_META|KEYC_IMPLIED_META },
{ "\033\033[F", KEYC_END|KEYC_META|KEYC_IMPLIED_META },
/* rxvt-style arrow + modifier keys. */
/* rxvt arrow keys. */
{ "\033Oa", KEYC_UP|KEYC_CTRL },
{ "\033Ob", KEYC_DOWN|KEYC_CTRL },
{ "\033Oc", KEYC_RIGHT|KEYC_CTRL },
@ -150,7 +150,31 @@ static const struct tty_default_key_raw tty_default_raw_keys[] = {
{ "\033[c", KEYC_RIGHT|KEYC_SHIFT },
{ "\033[d", KEYC_LEFT|KEYC_SHIFT },
/* rxvt-style function + modifier keys (C = ^, S = $, C-S = @). */
/* rxvt function keys. */
{ "\033[11~", KEYC_F1 },
{ "\033[12~", KEYC_F2 },
{ "\033[13~", KEYC_F3 },
{ "\033[14~", KEYC_F4 },
{ "\033[15~", KEYC_F5 },
{ "\033[17~", KEYC_F6 },
{ "\033[18~", KEYC_F7 },
{ "\033[19~", KEYC_F8 },
{ "\033[20~", KEYC_F9 },
{ "\033[21~", KEYC_F10 },
{ "\033[23~", KEYC_F1|KEYC_SHIFT },
{ "\033[24~", KEYC_F2|KEYC_SHIFT },
{ "\033[25~", KEYC_F3|KEYC_SHIFT },
{ "\033[26~", KEYC_F4|KEYC_SHIFT },
{ "\033[28~", KEYC_F5|KEYC_SHIFT },
{ "\033[29~", KEYC_F6|KEYC_SHIFT },
{ "\033[31~", KEYC_F7|KEYC_SHIFT },
{ "\033[32~", KEYC_F8|KEYC_SHIFT },
{ "\033[33~", KEYC_F9|KEYC_SHIFT },
{ "\033[34~", KEYC_F10|KEYC_SHIFT },
{ "\033[23$", KEYC_F11|KEYC_SHIFT },
{ "\033[24$", KEYC_F12|KEYC_SHIFT },
{ "\033[11^", KEYC_F1|KEYC_CTRL },
{ "\033[12^", KEYC_F2|KEYC_CTRL },
{ "\033[13^", KEYC_F3|KEYC_CTRL },
@ -163,31 +187,6 @@ static const struct tty_default_key_raw tty_default_raw_keys[] = {
{ "\033[21^", KEYC_F10|KEYC_CTRL },
{ "\033[23^", KEYC_F11|KEYC_CTRL },
{ "\033[24^", KEYC_F12|KEYC_CTRL },
{ "\033[2^", KEYC_IC|KEYC_CTRL },
{ "\033[3^", KEYC_DC|KEYC_CTRL },
{ "\033[7^", KEYC_HOME|KEYC_CTRL },
{ "\033[8^", KEYC_END|KEYC_CTRL },
{ "\033[6^", KEYC_NPAGE|KEYC_CTRL },
{ "\033[5^", KEYC_PPAGE|KEYC_CTRL },
{ "\033[11$", KEYC_F1|KEYC_SHIFT },
{ "\033[12$", KEYC_F2|KEYC_SHIFT },
{ "\033[13$", KEYC_F3|KEYC_SHIFT },
{ "\033[14$", KEYC_F4|KEYC_SHIFT },
{ "\033[15$", KEYC_F5|KEYC_SHIFT },
{ "\033[17$", KEYC_F6|KEYC_SHIFT },
{ "\033[18$", KEYC_F7|KEYC_SHIFT },
{ "\033[19$", KEYC_F8|KEYC_SHIFT },
{ "\033[20$", KEYC_F9|KEYC_SHIFT },
{ "\033[21$", KEYC_F10|KEYC_SHIFT },
{ "\033[23$", KEYC_F11|KEYC_SHIFT },
{ "\033[24$", KEYC_F12|KEYC_SHIFT },
{ "\033[2$", KEYC_IC|KEYC_SHIFT },
{ "\033[3$", KEYC_DC|KEYC_SHIFT },
{ "\033[7$", KEYC_HOME|KEYC_SHIFT },
{ "\033[8$", KEYC_END|KEYC_SHIFT },
{ "\033[6$", KEYC_NPAGE|KEYC_SHIFT },
{ "\033[5$", KEYC_PPAGE|KEYC_SHIFT },
{ "\033[11@", KEYC_F1|KEYC_CTRL|KEYC_SHIFT },
{ "\033[12@", KEYC_F2|KEYC_CTRL|KEYC_SHIFT },
@ -201,12 +200,6 @@ static const struct tty_default_key_raw tty_default_raw_keys[] = {
{ "\033[21@", KEYC_F10|KEYC_CTRL|KEYC_SHIFT },
{ "\033[23@", KEYC_F11|KEYC_CTRL|KEYC_SHIFT },
{ "\033[24@", KEYC_F12|KEYC_CTRL|KEYC_SHIFT },
{ "\033[2@", KEYC_IC|KEYC_CTRL|KEYC_SHIFT },
{ "\033[3@", KEYC_DC|KEYC_CTRL|KEYC_SHIFT },
{ "\033[7@", KEYC_HOME|KEYC_CTRL|KEYC_SHIFT },
{ "\033[8@", KEYC_END|KEYC_CTRL|KEYC_SHIFT },
{ "\033[6@", KEYC_NPAGE|KEYC_CTRL|KEYC_SHIFT },
{ "\033[5@", KEYC_PPAGE|KEYC_CTRL|KEYC_SHIFT },
/* Focus tracking. */
{ "\033[I", KEYC_FOCUS_IN },
@ -798,6 +791,8 @@ partial_key:
/* Get the time period. */
delay = options_get_number(global_options, "escape-time");
if (delay == 0)
delay = 1;
tv.tv_sec = delay / 1000;
tv.tv_usec = (delay % 1000) * 1000L;
@ -939,34 +934,16 @@ tty_keys_extended_key(struct tty *tty, const char *buf, size_t len,
nkey = number;
/* Update the modifiers. */
switch (modifiers) {
case 2:
nkey |= KEYC_SHIFT;
break;
case 3:
nkey |= (KEYC_META|KEYC_IMPLIED_META);
break;
case 4:
nkey |= (KEYC_SHIFT|KEYC_META|KEYC_IMPLIED_META);
break;
case 5:
nkey |= KEYC_CTRL;
break;
case 6:
nkey |= (KEYC_SHIFT|KEYC_CTRL);
break;
case 7:
nkey |= (KEYC_META|KEYC_CTRL);
break;
case 8:
nkey |= (KEYC_SHIFT|KEYC_META|KEYC_IMPLIED_META|KEYC_CTRL);
break;
case 9:
nkey |= (KEYC_META|KEYC_IMPLIED_META);
break;
default:
*key = KEYC_NONE;
break;
if (modifiers > 0) {
modifiers--;
if (modifiers & 1)
nkey |= KEYC_SHIFT;
if (modifiers & 2)
nkey |= (KEYC_META|KEYC_IMPLIED_META); /* Alt */
if (modifiers & 4)
nkey |= KEYC_CTRL;
if (modifiers & 8)
nkey |= (KEYC_META|KEYC_IMPLIED_META); /* Meta */
}
/*
@ -1061,17 +1038,13 @@ tty_keys_mouse(struct tty *tty, const char *buf, size_t len, size_t *size,
log_debug("%s: mouse input: %.*s", c->name, (int)*size, buf);
/* Check and return the mouse input. */
if (b < 32)
if (b < MOUSE_PARAM_BTN_OFF ||
x < MOUSE_PARAM_POS_OFF ||
y < MOUSE_PARAM_POS_OFF)
return (-1);
b -= 32;
if (x >= 33)
x -= 33;
else
x = 256 - x;
if (y >= 33)
y -= 33;
else
y = 256 - y;
b -= MOUSE_PARAM_BTN_OFF;
x -= MOUSE_PARAM_POS_OFF;
y -= MOUSE_PARAM_POS_OFF;
} else if (buf[2] == '<') {
/* Read the three inputs. */
*size = 3;

View File

@ -103,6 +103,7 @@ static const struct tty_term_code_entry tty_term_codes[] = {
[TTYC_ENFCS] = { TTYCODE_STRING, "Enfcs" },
[TTYC_ENMG] = { TTYCODE_STRING, "Enmg" },
[TTYC_FSL] = { TTYCODE_STRING, "fsl" },
[TTYC_HLS] = { TTYCODE_STRING, "Hls" },
[TTYC_HOME] = { TTYCODE_STRING, "home" },
[TTYC_HPA] = { TTYCODE_STRING, "hpa" },
[TTYC_ICH1] = { TTYCODE_STRING, "ich1" },
@ -249,6 +250,7 @@ static const struct tty_term_code_entry tty_term_codes[] = {
[TTYC_KUP6] = { TTYCODE_STRING, "kUP6" },
[TTYC_KUP7] = { TTYCODE_STRING, "kUP7" },
[TTYC_MS] = { TTYCODE_STRING, "Ms" },
[TTYC_NOBR] = { TTYCODE_STRING, "Nobr" },
[TTYC_OL] = { TTYCODE_STRING, "ol" },
[TTYC_OP] = { TTYCODE_STRING, "op" },
[TTYC_RECT] = { TTYCODE_STRING, "Rect" },

154
tty.c
View File

@ -69,7 +69,7 @@ static void tty_emulate_repeat(struct tty *, enum tty_code_code,
static void tty_repeat_space(struct tty *, u_int);
static void tty_draw_pane(struct tty *, const struct tty_ctx *, u_int);
static void tty_default_attributes(struct tty *, const struct grid_cell *,
struct colour_palette *, u_int);
struct colour_palette *, u_int, struct hyperlinks *);
static int tty_check_overlay(struct tty *, u_int, u_int);
static void tty_check_overlay_range(struct tty *, u_int, u_int, u_int,
struct overlay_ranges *);
@ -671,7 +671,7 @@ static void
tty_force_cursor_colour(struct tty *tty, int c)
{
u_char r, g, b;
char s[13] = "";
char s[13];
if (c != -1)
c = colour_force_rgb(c);
@ -814,7 +814,7 @@ tty_update_mode(struct tty *tty, int mode, struct screen *s)
tty_puts(tty, "\033[?1006h");
if (mode & MODE_MOUSE_ALL)
tty_puts(tty, "\033[?1000h\033[?1002h\033[?1003h");
if (mode & MODE_MOUSE_BUTTON)
else if (mode & MODE_MOUSE_BUTTON)
tty_puts(tty, "\033[?1000h\033[?1002h");
else if (mode & MODE_MOUSE_STANDARD)
tty_puts(tty, "\033[?1000h");
@ -1455,7 +1455,8 @@ tty_draw_line(struct tty *tty, struct screen *s, u_int px, u_int py, u_int nx,
tty_term_has(tty->term, TTYC_EL1) &&
!tty_fake_bce(tty, defaults, 8) &&
c->overlay_check == NULL) {
tty_default_attributes(tty, defaults, palette, 8);
tty_default_attributes(tty, defaults, palette, 8,
s->hyperlinks);
tty_cursor(tty, nx - 1, aty);
tty_putcode(tty, TTYC_EL1);
cleared = 1;
@ -1480,9 +1481,11 @@ tty_draw_line(struct tty *tty, struct screen *s, u_int px, u_int py, u_int nx,
gcp->fg != last.fg ||
gcp->bg != last.bg ||
gcp->us != last.us ||
gcp->link != last.link ||
ux + width + gcp->data.width > nx ||
(sizeof buf) - len < gcp->data.size)) {
tty_attributes(tty, &last, defaults, palette);
tty_attributes(tty, &last, defaults, palette,
s->hyperlinks);
if (last.flags & GRID_FLAG_CLEARED) {
log_debug("%s: %zu cleared", __func__, len);
tty_clear_line(tty, defaults, aty, atx + ux,
@ -1515,7 +1518,8 @@ tty_draw_line(struct tty *tty, struct screen *s, u_int px, u_int py, u_int nx,
ux += gcp->data.width;
} else if (hidden != 0 || ux + gcp->data.width > nx) {
if (~gcp->flags & GRID_FLAG_PADDING) {
tty_attributes(tty, &last, defaults, palette);
tty_attributes(tty, &last, defaults, palette,
s->hyperlinks);
for (j = 0; j < OVERLAY_MAX_RANGES; j++) {
if (r.nx[j] == 0)
continue;
@ -1532,7 +1536,8 @@ tty_draw_line(struct tty *tty, struct screen *s, u_int px, u_int py, u_int nx,
}
}
} else if (gcp->attr & GRID_ATTR_CHARSET) {
tty_attributes(tty, &last, defaults, palette);
tty_attributes(tty, &last, defaults, palette,
s->hyperlinks);
tty_cursor(tty, atx + ux, aty);
for (j = 0; j < gcp->data.size; j++)
tty_putc(tty, gcp->data.data[j]);
@ -1544,7 +1549,7 @@ tty_draw_line(struct tty *tty, struct screen *s, u_int px, u_int py, u_int nx,
}
}
if (len != 0 && ((~last.flags & GRID_FLAG_CLEARED) || last.bg != 8)) {
tty_attributes(tty, &last, defaults, palette);
tty_attributes(tty, &last, defaults, palette, s->hyperlinks);
if (last.flags & GRID_FLAG_CLEARED) {
log_debug("%s: %zu cleared (end)", __func__, len);
tty_clear_line(tty, defaults, aty, atx + ux, width,
@ -1560,7 +1565,8 @@ tty_draw_line(struct tty *tty, struct screen *s, u_int px, u_int py, u_int nx,
if (!cleared && ux < nx) {
log_debug("%s: %u to end of line (%zu cleared)", __func__,
nx - ux, len);
tty_default_attributes(tty, defaults, palette, 8);
tty_default_attributes(tty, defaults, palette, 8,
s->hyperlinks);
tty_clear_line(tty, defaults, aty, atx + ux, nx - ux, 8);
}
@ -1652,13 +1658,20 @@ tty_write(void (*cmdfn)(struct tty *, const struct tty_ctx *),
if (ctx->set_client_cb == NULL)
return;
TAILQ_FOREACH(c, &clients, entry) {
if (!tty_client_ready(c))
continue;
state = ctx->set_client_cb(ctx, c);
if (state == -1)
break;
if (state == 0)
continue;
if (ctx->allow_invisible_panes) {
if (c->session == NULL ||
c->tty.term == NULL ||
c->flags & CLIENT_SUSPENDED)
continue;
} else {
if (!tty_client_ready(c))
continue;
state = ctx->set_client_cb(ctx, c);
if (state == -1)
break;
if (state == 0)
continue;
}
cmdfn(&c->tty, ctx);
}
}
@ -1678,7 +1691,8 @@ tty_cmd_insertcharacter(struct tty *tty, const struct tty_ctx *ctx)
return;
}
tty_default_attributes(tty, &ctx->defaults, ctx->palette, ctx->bg);
tty_default_attributes(tty, &ctx->defaults, ctx->palette, ctx->bg,
ctx->s->hyperlinks);
tty_cursor_pane(tty, ctx, ctx->ocx, ctx->ocy);
@ -1700,7 +1714,8 @@ tty_cmd_deletecharacter(struct tty *tty, const struct tty_ctx *ctx)
return;
}
tty_default_attributes(tty, &ctx->defaults, ctx->palette, ctx->bg);
tty_default_attributes(tty, &ctx->defaults, ctx->palette, ctx->bg,
ctx->s->hyperlinks);
tty_cursor_pane(tty, ctx, ctx->ocx, ctx->ocy);
@ -1710,7 +1725,8 @@ tty_cmd_deletecharacter(struct tty *tty, const struct tty_ctx *ctx)
void
tty_cmd_clearcharacter(struct tty *tty, const struct tty_ctx *ctx)
{
tty_default_attributes(tty, &ctx->defaults, ctx->palette, ctx->bg);
tty_default_attributes(tty, &ctx->defaults, ctx->palette, ctx->bg,
ctx->s->hyperlinks);
tty_clear_pane_line(tty, ctx, ctx->ocy, ctx->ocx, ctx->num, ctx->bg);
}
@ -1732,7 +1748,8 @@ tty_cmd_insertline(struct tty *tty, const struct tty_ctx *ctx)
return;
}
tty_default_attributes(tty, &ctx->defaults, ctx->palette, ctx->bg);
tty_default_attributes(tty, &ctx->defaults, ctx->palette, ctx->bg,
ctx->s->hyperlinks);
tty_region_pane(tty, ctx, ctx->orupper, ctx->orlower);
tty_margin_off(tty);
@ -1759,7 +1776,8 @@ tty_cmd_deleteline(struct tty *tty, const struct tty_ctx *ctx)
return;
}
tty_default_attributes(tty, &ctx->defaults, ctx->palette, ctx->bg);
tty_default_attributes(tty, &ctx->defaults, ctx->palette, ctx->bg,
ctx->s->hyperlinks);
tty_region_pane(tty, ctx, ctx->orupper, ctx->orlower);
tty_margin_off(tty);
@ -1772,7 +1790,8 @@ tty_cmd_deleteline(struct tty *tty, const struct tty_ctx *ctx)
void
tty_cmd_clearline(struct tty *tty, const struct tty_ctx *ctx)
{
tty_default_attributes(tty, &ctx->defaults, ctx->palette, ctx->bg);
tty_default_attributes(tty, &ctx->defaults, ctx->palette, ctx->bg,
ctx->s->hyperlinks);
tty_clear_pane_line(tty, ctx, ctx->ocy, 0, ctx->sx, ctx->bg);
}
@ -1782,7 +1801,8 @@ tty_cmd_clearendofline(struct tty *tty, const struct tty_ctx *ctx)
{
u_int nx = ctx->sx - ctx->ocx;
tty_default_attributes(tty, &ctx->defaults, ctx->palette, ctx->bg);
tty_default_attributes(tty, &ctx->defaults, ctx->palette, ctx->bg,
ctx->s->hyperlinks);
tty_clear_pane_line(tty, ctx, ctx->ocy, ctx->ocx, nx, ctx->bg);
}
@ -1790,7 +1810,8 @@ tty_cmd_clearendofline(struct tty *tty, const struct tty_ctx *ctx)
void
tty_cmd_clearstartofline(struct tty *tty, const struct tty_ctx *ctx)
{
tty_default_attributes(tty, &ctx->defaults, ctx->palette, ctx->bg);
tty_default_attributes(tty, &ctx->defaults, ctx->palette, ctx->bg,
ctx->s->hyperlinks);
tty_clear_pane_line(tty, ctx, ctx->ocy, 0, ctx->ocx + 1, ctx->bg);
}
@ -1816,7 +1837,8 @@ tty_cmd_reverseindex(struct tty *tty, const struct tty_ctx *ctx)
return;
}
tty_default_attributes(tty, &ctx->defaults, ctx->palette, ctx->bg);
tty_default_attributes(tty, &ctx->defaults, ctx->palette, ctx->bg,
ctx->s->hyperlinks);
tty_region_pane(tty, ctx, ctx->orupper, ctx->orlower);
tty_margin_pane(tty, ctx);
@ -1847,7 +1869,8 @@ tty_cmd_linefeed(struct tty *tty, const struct tty_ctx *ctx)
return;
}
tty_default_attributes(tty, &ctx->defaults, ctx->palette, ctx->bg);
tty_default_attributes(tty, &ctx->defaults, ctx->palette, ctx->bg,
ctx->s->hyperlinks);
tty_region_pane(tty, ctx, ctx->orupper, ctx->orlower);
tty_margin_pane(tty, ctx);
@ -1887,7 +1910,8 @@ tty_cmd_scrollup(struct tty *tty, const struct tty_ctx *ctx)
return;
}
tty_default_attributes(tty, &ctx->defaults, ctx->palette, ctx->bg);
tty_default_attributes(tty, &ctx->defaults, ctx->palette, ctx->bg,
ctx->s->hyperlinks);
tty_region_pane(tty, ctx, ctx->orupper, ctx->orlower);
tty_margin_pane(tty, ctx);
@ -1927,7 +1951,8 @@ tty_cmd_scrolldown(struct tty *tty, const struct tty_ctx *ctx)
return;
}
tty_default_attributes(tty, &ctx->defaults, ctx->palette, ctx->bg);
tty_default_attributes(tty, &ctx->defaults, ctx->palette, ctx->bg,
ctx->s->hyperlinks);
tty_region_pane(tty, ctx, ctx->orupper, ctx->orlower);
tty_margin_pane(tty, ctx);
@ -1946,7 +1971,8 @@ tty_cmd_clearendofscreen(struct tty *tty, const struct tty_ctx *ctx)
{
u_int px, py, nx, ny;
tty_default_attributes(tty, &ctx->defaults, ctx->palette, ctx->bg);
tty_default_attributes(tty, &ctx->defaults, ctx->palette, ctx->bg,
ctx->s->hyperlinks);
tty_region_pane(tty, ctx, 0, ctx->sy - 1);
tty_margin_off(tty);
@ -1970,7 +1996,8 @@ tty_cmd_clearstartofscreen(struct tty *tty, const struct tty_ctx *ctx)
{
u_int px, py, nx, ny;
tty_default_attributes(tty, &ctx->defaults, ctx->palette, ctx->bg);
tty_default_attributes(tty, &ctx->defaults, ctx->palette, ctx->bg,
ctx->s->hyperlinks);
tty_region_pane(tty, ctx, 0, ctx->sy - 1);
tty_margin_off(tty);
@ -1994,7 +2021,8 @@ tty_cmd_clearscreen(struct tty *tty, const struct tty_ctx *ctx)
{
u_int px, py, nx, ny;
tty_default_attributes(tty, &ctx->defaults, ctx->palette, ctx->bg);
tty_default_attributes(tty, &ctx->defaults, ctx->palette, ctx->bg,
ctx->s->hyperlinks);
tty_region_pane(tty, ctx, 0, ctx->sy - 1);
tty_margin_off(tty);
@ -2017,7 +2045,8 @@ tty_cmd_alignmenttest(struct tty *tty, const struct tty_ctx *ctx)
return;
}
tty_attributes(tty, &grid_default_cell, &ctx->defaults, ctx->palette);
tty_attributes(tty, &grid_default_cell, &ctx->defaults, ctx->palette,
ctx->s->hyperlinks);
tty_region_pane(tty, ctx, 0, ctx->sy - 1);
tty_margin_off(tty);
@ -2063,7 +2092,8 @@ tty_cmd_cell(struct tty *tty, const struct tty_ctx *ctx)
tty_margin_off(tty);
tty_cursor_pane_unless_wrap(tty, ctx, ctx->ocx, ctx->ocy);
tty_cell(tty, ctx->cell, &ctx->defaults, ctx->palette);
tty_cell(tty, ctx->cell, &ctx->defaults, ctx->palette,
ctx->s->hyperlinks);
}
void
@ -2094,7 +2124,7 @@ tty_cmd_cells(struct tty *tty, const struct tty_ctx *ctx)
tty_margin_off(tty);
tty_cursor_pane_unless_wrap(tty, ctx, ctx->ocx, ctx->ocy);
tty_attributes(tty, ctx->cell, &ctx->defaults, ctx->palette);
tty_attributes(tty, ctx->cell, &ctx->defaults, ctx->palette, ctx->s->hyperlinks);
/* Get tty position from pane position for overlay check. */
px = ctx->xoff + ctx->ocx - ctx->wox;
@ -2114,11 +2144,12 @@ tty_cmd_cells(struct tty *tty, const struct tty_ctx *ctx)
void
tty_cmd_setselection(struct tty *tty, const struct tty_ctx *ctx)
{
tty_set_selection(tty, ctx->ptr, ctx->num);
tty_set_selection(tty, ctx->ptr2, ctx->ptr, ctx->num);
}
void
tty_set_selection(struct tty *tty, const char *buf, size_t len)
tty_set_selection(struct tty *tty, const char *flags, const char *buf,
size_t len)
{
char *encoded;
size_t size;
@ -2133,7 +2164,7 @@ tty_set_selection(struct tty *tty, const char *buf, size_t len)
b64_ntop(buf, len, encoded, size);
tty->flags |= TTY_NOBLOCK;
tty_putcode_ptr2(tty, TTYC_MS, "", encoded);
tty_putcode_ptr2(tty, TTYC_MS, flags, encoded);
free(encoded);
}
@ -2208,7 +2239,8 @@ tty_cmd_syncstart(struct tty *tty, const struct tty_ctx *ctx)
void
tty_cell(struct tty *tty, const struct grid_cell *gc,
const struct grid_cell *defaults, struct colour_palette *palette)
const struct grid_cell *defaults, struct colour_palette *palette,
struct hyperlinks *hl)
{
const struct grid_cell *gcp;
@ -2224,11 +2256,11 @@ tty_cell(struct tty *tty, const struct grid_cell *gc,
/* Check the output codeset and apply attributes. */
gcp = tty_check_codeset(tty, gc);
tty_attributes(tty, gcp, defaults, palette);
tty_attributes(tty, gcp, defaults, palette, hl);
/* If it is a single character, write with putc to handle ACS. */
if (gcp->data.size == 1) {
tty_attributes(tty, gcp, defaults, palette);
tty_attributes(tty, gcp, defaults, palette, hl);
if (*gcp->data.data < 0x20 || *gcp->data.data == 0x7f)
return;
tty_putc(tty, *gcp->data.data);
@ -2245,6 +2277,8 @@ tty_reset(struct tty *tty)
struct grid_cell *gc = &tty->cell;
if (!grid_cells_equal(gc, &grid_default_cell)) {
if (gc->link != 0)
tty_putcode_ptr2(tty, TTYC_HLS, "", "");
if ((gc->attr & GRID_ATTR_CHARSET) && tty_acs_needed(tty))
tty_putcode(tty, TTYC_RMACS);
tty_putcode(tty, TTYC_SGR0);
@ -2534,9 +2568,29 @@ out:
tty->cy = cy;
}
static void
tty_hyperlink(struct tty *tty, const struct grid_cell *gc,
struct hyperlinks *hl)
{
const char *uri, *id;
if (gc->link == tty->cell.link)
return;
tty->cell.link = gc->link;
if (hl == NULL)
return;
if (gc->link == 0 || !hyperlinks_get(hl, gc->link, &uri, NULL, &id))
tty_putcode_ptr2(tty, TTYC_HLS, "", "");
else
tty_putcode_ptr2(tty, TTYC_HLS, id, uri);
}
void
tty_attributes(struct tty *tty, const struct grid_cell *gc,
const struct grid_cell *defaults, struct colour_palette *palette)
const struct grid_cell *defaults, struct colour_palette *palette,
struct hyperlinks *hl)
{
struct grid_cell *tc = &tty->cell, gc2;
int changed;
@ -2554,7 +2608,8 @@ tty_attributes(struct tty *tty, const struct grid_cell *gc,
if (gc2.attr == tty->last_cell.attr &&
gc2.fg == tty->last_cell.fg &&
gc2.bg == tty->last_cell.bg &&
gc2.us == tty->last_cell.us)
gc2.us == tty->last_cell.us &&
gc2.link == tty->last_cell.link)
return;
/*
@ -2631,6 +2686,9 @@ tty_attributes(struct tty *tty, const struct grid_cell *gc,
if ((changed & GRID_ATTR_CHARSET) && tty_acs_needed(tty))
tty_putcode(tty, TTYC_SMACS);
/* Set hyperlink if any. */
tty_hyperlink(tty, gc, hl);
memcpy(&tty->last_cell, &gc2, sizeof tty->last_cell);
}
@ -2705,12 +2763,14 @@ tty_check_fg(struct tty *tty, struct colour_palette *palette,
/*
* Perform substitution if this pane has a palette. If the bright
* attribute is set, use the bright entry in the palette by changing to
* the aixterm colour.
* attribute is set and Nobr is not present, use the bright entry in
* the palette by changing to the aixterm colour
*/
if (~gc->flags & GRID_FLAG_NOPALETTE) {
c = gc->fg;
if (c < 8 && gc->attr & GRID_ATTR_BRIGHT)
if (c < 8 &&
gc->attr & GRID_ATTR_BRIGHT &&
!tty_term_has(tty->term, TTYC_NOBR))
c += 90;
if ((c = colour_palette_get(palette, c)) != -1)
gc->fg = c;
@ -2996,13 +3056,13 @@ tty_default_colours(struct grid_cell *gc, struct window_pane *wp)
static void
tty_default_attributes(struct tty *tty, const struct grid_cell *defaults,
struct colour_palette *palette, u_int bg)
struct colour_palette *palette, u_int bg, struct hyperlinks *hl)
{
struct grid_cell gc;
memcpy(&gc, &grid_default_cell, sizeof gc);
gc.bg = bg;
tty_attributes(tty, &gc, defaults, palette);
tty_attributes(tty, &gc, defaults, palette, hl);
}
static void

View File

@ -308,7 +308,7 @@ window_buffer_get_key(void *modedata, void *itemdata, u_int line)
}
pb = paste_get_name(item->name);
if (pb == NULL)
return KEYC_NONE;
return (KEYC_NONE);
ft = format_create(NULL, NULL, FORMAT_NONE, 0);
format_defaults(ft, NULL, NULL, 0, NULL);
@ -320,7 +320,7 @@ window_buffer_get_key(void *modedata, void *itemdata, u_int line)
key = key_string_lookup_string(expanded);
free(expanded);
format_free(ft);
return key;
return (key);
}
static struct screen *

View File

@ -281,7 +281,7 @@ window_client_get_key(void *modedata, void *itemdata, u_int line)
key = key_string_lookup_string(expanded);
free(expanded);
format_free(ft);
return key;
return (key);
}
static struct screen *

View File

@ -222,6 +222,8 @@ struct window_copy_mode_data {
struct screen *backing;
int backing_written; /* backing display started */
struct screen *writing;
struct input_ctx *ictx;
int viewmode; /* view mode entered */
@ -467,13 +469,16 @@ window_copy_view_init(struct window_mode_entry *wme,
struct window_pane *wp = wme->wp;
struct window_copy_mode_data *data;
struct screen *base = &wp->base;
struct screen *s;
u_int sx = screen_size_x(base);
data = window_copy_common_init(wme);
data->viewmode = 1;
data->backing = s = xmalloc(sizeof *data->backing);
screen_init(s, screen_size_x(base), screen_size_y(base), UINT_MAX);
data->backing = xmalloc(sizeof *data->backing);
screen_init(data->backing, sx, screen_size_y(base), UINT_MAX);
data->writing = xmalloc(sizeof *data->writing);
screen_init(data->writing, sx, screen_size_y(base), 0);
data->ictx = input_init(NULL, NULL, NULL);
data->mx = data->cx;
data->my = screen_hsize(data->backing) + data->cy - data->oy;
data->showmark = 0;
@ -492,6 +497,12 @@ window_copy_free(struct window_mode_entry *wme)
free(data->searchstr);
free(data->jumpchar);
if (data->writing != NULL) {
screen_free(data->writing);
free(data->writing);
}
if (data->ictx != NULL)
input_free(data->ictx);
screen_free(data->backing);
free(data->backing);
@ -500,41 +511,67 @@ window_copy_free(struct window_mode_entry *wme)
}
void
window_copy_add(struct window_pane *wp, const char *fmt, ...)
window_copy_add(struct window_pane *wp, int parse, const char *fmt, ...)
{
va_list ap;
va_start(ap, fmt);
window_copy_vadd(wp, fmt, ap);
window_copy_vadd(wp, parse, fmt, ap);
va_end(ap);
}
static void
window_copy_init_ctx_cb(__unused struct screen_write_ctx *ctx,
struct tty_ctx *ttyctx)
{
memcpy(&ttyctx->defaults, &grid_default_cell, sizeof ttyctx->defaults);
ttyctx->palette = NULL;
ttyctx->redraw_cb = NULL;
ttyctx->set_client_cb = NULL;
ttyctx->arg = NULL;
}
void
window_copy_vadd(struct window_pane *wp, const char *fmt, va_list ap)
window_copy_vadd(struct window_pane *wp, int parse, const char *fmt, va_list ap)
{
struct window_mode_entry *wme = TAILQ_FIRST(&wp->modes);
struct window_copy_mode_data *data = wme->data;
struct screen *backing = data->backing;
struct screen_write_ctx back_ctx, ctx;
struct screen *writing = data->writing;
struct screen_write_ctx writing_ctx, backing_ctx, ctx;
struct grid_cell gc;
u_int old_hsize, old_cy;
u_int sx = screen_size_x(backing);
char *text;
memcpy(&gc, &grid_default_cell, sizeof gc);
if (parse) {
vasprintf(&text, fmt, ap);
screen_write_start(&writing_ctx, writing);
screen_write_reset(&writing_ctx);
input_parse_screen(data->ictx, writing, window_copy_init_ctx_cb,
data, text, strlen(text));
free(text);
}
old_hsize = screen_hsize(data->backing);
screen_write_start(&back_ctx, backing);
screen_write_start(&backing_ctx, backing);
if (data->backing_written) {
/*
* On the second or later line, do a CRLF before writing
* (so it's on a new line).
*/
screen_write_carriagereturn(&back_ctx);
screen_write_linefeed(&back_ctx, 0, 8);
screen_write_carriagereturn(&backing_ctx);
screen_write_linefeed(&backing_ctx, 0, 8);
} else
data->backing_written = 1;
old_cy = backing->cy;
screen_write_vnputs(&back_ctx, 0, &gc, fmt, ap);
screen_write_stop(&back_ctx);
if (parse)
screen_write_fast_copy(&backing_ctx, writing, 0, 0, sx, 1);
else {
memcpy(&gc, &grid_default_cell, sizeof gc);
screen_write_vnputs(&backing_ctx, 0, &gc, fmt, ap);
}
screen_write_stop(&backing_ctx);
data->oy += screen_hsize(data->backing) - old_hsize;
@ -1213,6 +1250,32 @@ window_copy_cmd_cursor_right(struct window_copy_cmd_state *cs)
return (WINDOW_COPY_CMD_NOTHING);
}
static enum window_copy_cmd_action
window_copy_cmd_scroll_middle(struct window_copy_cmd_state *cs)
{
struct window_mode_entry *wme = cs->wme;
struct window_copy_mode_data *data = wme->data;
u_int mid_value, oy, delta;
int scroll_up; /* >0 up, <0 down */
mid_value = (screen_size_y(&data->screen) - 1) / 2;
scroll_up = data->cy - mid_value;
delta = abs(scroll_up);
oy = screen_hsize(data->backing) + data->cy - data->oy;
log_debug ("XXX %u %u %u %d %u", mid_value, oy, delta, scroll_up, data->oy);
if (scroll_up > 0 && data->oy >= delta) {
window_copy_scroll_up(wme, delta);
data->cy -= delta;
} else if (scroll_up < 0 && oy >= delta) {
window_copy_scroll_down(wme, delta);
data->cy += delta;
}
window_copy_update_selection(wme, 0, 0);
return (WINDOW_COPY_CMD_REDRAW);
}
static enum window_copy_cmd_action
window_copy_cmd_cursor_up(struct window_copy_cmd_state *cs)
{
@ -2743,6 +2806,12 @@ static const struct {
.clear = WINDOW_COPY_CMD_CLEAR_ALWAYS,
.f = window_copy_cmd_scroll_down_and_cancel
},
{ .command = "scroll-middle",
.minargs = 0,
.maxargs = 0,
.clear = WINDOW_COPY_CMD_CLEAR_ALWAYS,
.f = window_copy_cmd_scroll_middle
},
{ .command = "scroll-up",
.minargs = 0,
.maxargs = 0,
@ -3575,6 +3644,8 @@ window_copy_search(struct window_mode_entry *wme, int direction, int regex)
data->searchall = 0;
} else
visible_only = (strcmp(wp->searchstr, str) == 0);
if (visible_only == 0 && data->searchmark != NULL)
window_copy_clear_marks(wme);
free(wp->searchstr);
wp->searchstr = xstrdup(str);
wp->searchregex = regex;
@ -3634,6 +3705,7 @@ window_copy_search(struct window_mode_entry *wme, int direction, int regex)
if (direction &&
window_copy_search_mark_at(data, fx, fy, &at) == 0 &&
at > 0 &&
data->searchmark != NULL &&
data->searchmark[at] == data->searchmark[at - 1]) {
window_copy_move_after_search_mark(data, &fx, &fy,
wrapflag);
@ -3666,6 +3738,7 @@ window_copy_search(struct window_mode_entry *wme, int direction, int regex)
&start) == 0) {
while (window_copy_search_mark_at(data, fx, fy,
&at) == 0 &&
data->searchmark != NULL &&
data->searchmark[at] ==
data->searchmark[start]) {
data->cx = fx;
@ -4055,8 +4128,9 @@ window_copy_write_line(struct window_mode_entry *wme,
struct window_copy_mode_data *data = wme->data;
struct screen *s = &data->screen;
struct options *oo = wp->window->options;
struct grid_line *gl;
struct grid_cell gc, mgc, cgc, mkgc;
char hdr[512];
char hdr[512], tmp[256], *t;
size_t size = 0;
u_int hsize = screen_hsize(data->backing);
@ -4070,23 +4144,29 @@ window_copy_write_line(struct window_mode_entry *wme,
mkgc.flags |= GRID_FLAG_NOPALETTE;
if (py == 0 && s->rupper < s->rlower && !data->hide_position) {
gl = grid_get_line(data->backing->grid, hsize - data->oy);
if (gl->time == 0)
xsnprintf(tmp, sizeof tmp, "[%u/%u]", data->oy, hsize);
else {
t = format_pretty_time(gl->time, 1);
xsnprintf(tmp, sizeof tmp, "%s [%u/%u]", t, data->oy,
hsize);
free(t);
}
if (data->searchmark == NULL) {
if (data->timeout) {
size = xsnprintf(hdr, sizeof hdr,
"(timed out) [%u/%u]", data->oy, hsize);
} else {
size = xsnprintf(hdr, sizeof hdr,
"[%u/%u]", data->oy, hsize);
}
"(timed out) %s", tmp);
} else
size = xsnprintf(hdr, sizeof hdr, "%s", tmp);
} else {
if (data->searchcount == -1) {
if (data->searchcount == -1)
size = xsnprintf(hdr, sizeof hdr, "%s", tmp);
else {
size = xsnprintf(hdr, sizeof hdr,
"[%u/%u]", data->oy, hsize);
} else {
size = xsnprintf(hdr, sizeof hdr,
"(%d%s results) [%u/%u]", data->searchcount,
data->searchmore ? "+" : "", data->oy,
hsize);
"(%d%s results) %s", data->searchcount,
data->searchmore ? "+" : "", tmp);
}
}
if (size > screen_size_x(s))
@ -4533,7 +4613,7 @@ window_copy_copy_buffer(struct window_mode_entry *wme, const char *prefix,
if (options_get_number(global_options, "set-clipboard") != 0) {
screen_write_start_pane(&ctx, wp, NULL);
screen_write_setselection(&ctx, buf, len);
screen_write_setselection(&ctx, "", buf, len);
screen_write_stop(&ctx);
notify_pane("pane-set-clipboard", wp);
}
@ -4607,7 +4687,7 @@ window_copy_append_selection(struct window_mode_entry *wme)
if (options_get_number(global_options, "set-clipboard") != 0) {
screen_write_start_pane(&ctx, wp, NULL);
screen_write_setselection(&ctx, buf, len);
screen_write_setselection(&ctx, "", buf, len);
screen_write_stop(&ctx);
notify_pane("pane-set-clipboard", wp);
}

View File

@ -272,9 +272,10 @@ window_tree_cmp_window(const void *a0, const void *b0)
static int
window_tree_cmp_pane(const void *a0, const void *b0)
{
const struct window_pane *const *a = a0;
const struct window_pane *const *b = b0;
int result;
struct window_pane **a = (struct window_pane **)a0;
struct window_pane **b = (struct window_pane **)b0;
int result;
u_int ai, bi;
if (window_tree_sort->field == WINDOW_TREE_BY_TIME)
result = (*a)->active_point - (*b)->active_point;
@ -283,7 +284,9 @@ window_tree_cmp_pane(const void *a0, const void *b0)
* Panes don't have names, so use number order for any other
* sort field.
*/
result = (*a)->id - (*b)->id;
window_pane_index(*a, &ai);
window_pane_index(*b, &bi);
result = ai - bi;
}
if (window_tree_sort->reversed)
result = -result;
@ -895,7 +898,7 @@ window_tree_get_key(void *modedata, void *itemdata, u_int line)
key = key_string_lookup_string(expanded);
free(expanded);
format_free(ft);
return key;
return (key);
}
static struct screen *
@ -1243,12 +1246,17 @@ window_tree_key(struct window_mode_entry *wme, struct client *c,
item = mode_tree_get_current(data->data);
finished = mode_tree_key(data->data, c, &key, m, &x, &y);
again:
if (item != (new_item = mode_tree_get_current(data->data))) {
item = new_item;
data->offset = 0;
}
if (KEYC_IS_MOUSE(key) && m != NULL)
if (KEYC_IS_MOUSE(key) && m != NULL) {
key = window_tree_mouse(data, key, x, item);
goto again;
}
switch (key) {
case '<':
data->offset--;

View File

@ -940,6 +940,7 @@ window_pane_create(struct window *w, u_int sx, u_int sy, u_int hlimit)
screen_init(&wp->base, sx, sy, hlimit);
wp->screen = &wp->base;
window_pane_default_cursor(wp);
screen_init(&wp->status_screen, 1, 1, 0);
@ -1042,6 +1043,8 @@ window_pane_set_event(struct window_pane *wp)
wp->event = bufferevent_new(wp->fd, window_pane_read_callback,
NULL, window_pane_error_callback, wp);
if (wp->event == NULL)
fatalx("out of memory");
wp->ictx = input_init(wp, wp->event, &wp->palette);
bufferevent_enable(wp->event, EV_READ|EV_WRITE);
@ -1056,7 +1059,7 @@ window_pane_resize(struct window_pane *wp, u_int sx, u_int sy)
if (sx == wp->sx && sy == wp->sy)
return;
r = xmalloc (sizeof *r);
r = xmalloc(sizeof *r);
r->sx = sx;
r->sy = sy;
r->osx = wp->sx;
@ -1618,3 +1621,17 @@ window_set_fill_character(struct window *w)
w->fill_character = ud;
}
}
void
window_pane_default_cursor(struct window_pane *wp)
{
struct screen *s = wp->screen;
int c;
c = options_get_number(wp->options, "cursor-colour");
s->default_ccolour = c;
c = options_get_number(wp->options, "cursor-style");
s->default_mode = 0;
screen_set_cursor_style(c, &s->default_cstyle, &s->default_mode);
}