mirror of
https://github.com/tmux/tmux.git
synced 2025-01-09 01:28:49 +00:00
Merge branch 'master' into sixel
This commit is contained in:
commit
a82f14c7b2
6
.github/workflows/lock.yml
vendored
6
.github/workflows/lock.yml
vendored
@ -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
61
CHANGES
@ -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,
|
||||
|
@ -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 \
|
||||
|
120
arguments.c
120
arguments.c
@ -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
26
cfg.c
@ -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;
|
||||
|
1
client.c
1
client.c
@ -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) {
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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) {
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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) {
|
||||
|
30
cmd-queue.c
30
cmd-queue.c
@ -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);
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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
147
cmd-server-access.c
Normal 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);
|
||||
}
|
@ -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);
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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
2
cmd.c
@ -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,
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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(
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
12
control.c
12
control.c
@ -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)
|
||||
|
15
environ.c
15
environ.c
@ -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
4
file.c
@ -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;
|
||||
|
||||
|
93
format.c
93
format.c
@ -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));
|
||||
}
|
||||
|
@ -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
87
grid.c
@ -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
227
hyperlinks.c
Normal 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);
|
||||
}
|
31
input-keys.c
31
input-keys.c
@ -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
76
input.c
@ -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);
|
||||
|
||||
|
@ -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 }",
|
||||
|
@ -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
2
log.c
@ -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
86
menu.c
@ -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 */
|
||||
|
31
notify.c
31
notify.c
@ -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);
|
||||
}
|
||||
|
@ -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",
|
||||
|
15
options.c
15
options.c
@ -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
19
paste.c
@ -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
8
proc.c
@ -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)
|
||||
{
|
||||
|
30
regress/capture-pane-hyperlink.sh
Normal file
30
regress/capture-pane-hyperlink.sh
Normal 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
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
|
20
screen.c
20
screen.c
@ -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
186
server-acl.c
Normal 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);
|
||||
}
|
@ -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. */
|
||||
|
21
server.c
21
server.c
@ -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
25
spawn.c
@ -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");
|
||||
|
||||
/*
|
||||
|
46
status.c
46
status.c
@ -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);
|
||||
}
|
||||
|
2
style.c
2
style.c
@ -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
98
tmux.1
@ -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
2
tmux.c
@ -325,7 +325,7 @@ find_home(void)
|
||||
const char *
|
||||
getversion(void)
|
||||
{
|
||||
return TMUX_VERSION;
|
||||
return (TMUX_VERSION);
|
||||
}
|
||||
|
||||
int
|
||||
|
119
tmux.h
119
tmux.h
@ -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 */
|
||||
|
@ -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
|
||||
|
153
tty-features.c
153
tty-features.c
@ -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",
|
||||
/*
|
||||
|
117
tty-keys.c
117
tty-keys.c
@ -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;
|
||||
|
@ -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
154
tty.c
@ -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
|
||||
|
@ -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 *
|
||||
|
@ -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 *
|
||||
|
136
window-copy.c
136
window-copy.c
@ -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);
|
||||
}
|
||||
|
@ -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--;
|
||||
|
19
window.c
19
window.c
@ -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);
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user