mirror of
https://github.com/tmux/tmux.git
synced 2026-09-25 06:06:29 +00:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
17d32ae40d | ||
|
|
e4129d92c2 | ||
|
|
6a5de0d8e3 | ||
|
|
ff7efbe37c | ||
|
|
ff7d45ad05 | ||
|
|
15a7944576 | ||
|
|
de69122034 | ||
|
|
f38558ee12 | ||
|
|
38c7eb7413 | ||
|
|
ad785736e9 | ||
|
|
61687f2a5b | ||
|
|
ed2b349c3e | ||
|
|
92d29e54f4 | ||
|
|
a34d2b6b21 | ||
|
|
550231e83a | ||
|
|
102d5185c1 | ||
|
|
1d9029c8d7 | ||
|
|
a23ff880a2 | ||
|
|
32dc7d5095 | ||
|
|
06ef15955c | ||
|
|
ce9bdf6734 | ||
|
|
757b15f2ca | ||
|
|
fcb4e9760a | ||
|
|
03436d273a | ||
|
|
7fdb1c90b7 | ||
|
|
55012ded8d | ||
|
|
de04ae2c65 | ||
|
|
3b931d9c20 | ||
|
|
6f387eec2b | ||
|
|
80f522f577 |
@@ -27,10 +27,10 @@ jobs:
|
|||||||
runner: ubuntu-24.04
|
runner: ubuntu-24.04
|
||||||
make: make
|
make: make
|
||||||
configure: --enable-utf8proc --enable-asan
|
configure: --enable-utf8proc --enable-asan
|
||||||
- name: ubuntu-24.04-arm64
|
#- name: ubuntu-24.04-arm64
|
||||||
runner: ubuntu-24.04-arm
|
# runner: ubuntu-24.04-arm
|
||||||
make: make
|
# make: make
|
||||||
configure: --enable-utf8proc --enable-asan
|
# configure: --enable-utf8proc --enable-asan
|
||||||
- name: macos-26-arm64
|
- name: macos-26-arm64
|
||||||
runner: macos-26
|
runner: macos-26
|
||||||
make: gmake
|
make: gmake
|
||||||
|
|||||||
+2
-4
@@ -188,6 +188,7 @@ dist_tmux_SOURCES = \
|
|||||||
options-table.c \
|
options-table.c \
|
||||||
options.c \
|
options.c \
|
||||||
paste.c \
|
paste.c \
|
||||||
|
popup.c \
|
||||||
proc.c \
|
proc.c \
|
||||||
prompt-history.c \
|
prompt-history.c \
|
||||||
prompt.c \
|
prompt.c \
|
||||||
@@ -257,8 +258,7 @@ check_PROGRAMS = \
|
|||||||
fuzz/input-fuzzer \
|
fuzz/input-fuzzer \
|
||||||
fuzz/cmd-parse-fuzzer \
|
fuzz/cmd-parse-fuzzer \
|
||||||
fuzz/format-fuzzer \
|
fuzz/format-fuzzer \
|
||||||
fuzz/style-fuzzer \
|
fuzz/style-fuzzer
|
||||||
fuzz/layout-fuzzer
|
|
||||||
fuzz_input_fuzzer_LDFLAGS = $(FUZZING_LIBS)
|
fuzz_input_fuzzer_LDFLAGS = $(FUZZING_LIBS)
|
||||||
fuzz_input_fuzzer_LDADD = $(LDADD) $(tmux_OBJECTS)
|
fuzz_input_fuzzer_LDADD = $(LDADD) $(tmux_OBJECTS)
|
||||||
fuzz_cmd_parse_fuzzer_LDFLAGS = $(FUZZING_LIBS)
|
fuzz_cmd_parse_fuzzer_LDFLAGS = $(FUZZING_LIBS)
|
||||||
@@ -267,8 +267,6 @@ fuzz_format_fuzzer_LDFLAGS = $(FUZZING_LIBS)
|
|||||||
fuzz_format_fuzzer_LDADD = $(LDADD) $(tmux_OBJECTS)
|
fuzz_format_fuzzer_LDADD = $(LDADD) $(tmux_OBJECTS)
|
||||||
fuzz_style_fuzzer_LDFLAGS = $(FUZZING_LIBS)
|
fuzz_style_fuzzer_LDFLAGS = $(FUZZING_LIBS)
|
||||||
fuzz_style_fuzzer_LDADD = $(LDADD) $(tmux_OBJECTS)
|
fuzz_style_fuzzer_LDADD = $(LDADD) $(tmux_OBJECTS)
|
||||||
fuzz_layout_fuzzer_LDFLAGS = $(FUZZING_LIBS)
|
|
||||||
fuzz_layout_fuzzer_LDADD = $(LDADD) $(tmux_OBJECTS)
|
|
||||||
endif
|
endif
|
||||||
|
|
||||||
# Install tmux.1 in the right format.
|
# Install tmux.1 in the right format.
|
||||||
|
|||||||
+8
-71
@@ -35,7 +35,7 @@ The usual local layout is:
|
|||||||
```sh
|
```sh
|
||||||
cd /some/where/useful
|
cd /some/where/useful
|
||||||
git clone https://github.com/tmux/tmux.git tmux-portable
|
git clone https://github.com/tmux/tmux.git tmux-portable
|
||||||
git clone https://github.com/tmux/tmux-openbsd-cutover.git tmux-openbsd-cutover
|
git clone https://github.com/ThomasAdam/tmux-obsd.git tmux-openbsd-cutover
|
||||||
```
|
```
|
||||||
|
|
||||||
The exact directory names do not matter, but the examples below use:
|
The exact directory names do not matter, but the examples below use:
|
||||||
@@ -56,19 +56,18 @@ The cutover repository has three important branches:
|
|||||||
|
|
||||||
# Adding the OpenBSD remote to portable
|
# Adding the OpenBSD remote to portable
|
||||||
|
|
||||||
In the portable repository, add the published cutover repository as a remote.
|
In the portable repository, add the cutover repository as a remote:
|
||||||
This works regardless of which branch is checked out in a local cutover clone:
|
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
cd /path/to/tmux-portable
|
cd /path/to/tmux-portable
|
||||||
git remote add tmux-openbsd https://github.com/tmux/tmux-openbsd-cutover.git
|
git remote add tmux-openbsd /path/to/tmux-openbsd-cutover
|
||||||
git config remote.tmux-openbsd.tagOpt --no-tags
|
git config remote.tmux-openbsd.tagOpt --no-tags
|
||||||
```
|
```
|
||||||
|
|
||||||
If the remote already exists, update it instead:
|
If the remote already exists, update it instead:
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
git remote set-url tmux-openbsd https://github.com/tmux/tmux-openbsd-cutover.git
|
git remote set-url tmux-openbsd /path/to/tmux-openbsd-cutover
|
||||||
git config remote.tmux-openbsd.tagOpt --no-tags
|
git config remote.tmux-openbsd.tagOpt --no-tags
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -78,32 +77,6 @@ Fetch the cutover master branch explicitly:
|
|||||||
git fetch --no-tags tmux-openbsd master:refs/remotes/tmux-openbsd/master
|
git fetch --no-tags tmux-openbsd master:refs/remotes/tmux-openbsd/master
|
||||||
```
|
```
|
||||||
|
|
||||||
To merge unpublished changes from a local cutover clone instead, first ensure
|
|
||||||
it has an up-to-date local `master` branch. A normal clone may check out
|
|
||||||
`automation` and have only `origin/master`; fetching `master` from that clone
|
|
||||||
will then fail with `couldn't find remote ref master`.
|
|
||||||
|
|
||||||
With a clean cutover working tree:
|
|
||||||
|
|
||||||
```sh
|
|
||||||
cd /path/to/tmux-openbsd-cutover
|
|
||||||
git fetch --no-tags origin
|
|
||||||
git switch master
|
|
||||||
git merge --ff-only origin/master
|
|
||||||
```
|
|
||||||
|
|
||||||
`git switch master` creates a tracking branch from `origin/master` if there is
|
|
||||||
no local `master` yet. If the fast-forward fails, reconcile the local cutover
|
|
||||||
changes before continuing; do not reset them away.
|
|
||||||
|
|
||||||
Then, in portable, point the remote at that clone and fetch its local `master`:
|
|
||||||
|
|
||||||
```sh
|
|
||||||
cd /path/to/tmux-portable
|
|
||||||
git remote set-url tmux-openbsd /path/to/tmux-openbsd-cutover
|
|
||||||
git fetch --no-tags tmux-openbsd master:refs/remotes/tmux-openbsd/master
|
|
||||||
```
|
|
||||||
|
|
||||||
# Automated syncing
|
# Automated syncing
|
||||||
|
|
||||||
The normal sync is performed by the GitHub Actions workflow in the
|
The normal sync is performed by the GitHub Actions workflow in the
|
||||||
@@ -126,9 +99,7 @@ OpenBSD changes.
|
|||||||
If the workflow fails while merging into portable, do the merge locally and
|
If the workflow fails while merging into portable, do the merge locally and
|
||||||
push the result.
|
push the result.
|
||||||
|
|
||||||
Start with a clean working tree and an up-to-date portable master. If a merge
|
Start from an up-to-date portable master:
|
||||||
is already in progress, skip to resolving conflicts, or abort it before
|
|
||||||
starting again:
|
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
cd /path/to/tmux-portable
|
cd /path/to/tmux-portable
|
||||||
@@ -137,15 +108,10 @@ git checkout master
|
|||||||
git pull --ff-only origin master
|
git pull --ff-only origin master
|
||||||
```
|
```
|
||||||
|
|
||||||
Fetch the published cutover branch. Update an existing remote as well, since
|
Fetch the cutover branch:
|
||||||
it may point at a local clone without a `master` branch or with a stale one:
|
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
if git remote get-url tmux-openbsd >/dev/null 2>&1; then
|
git remote add tmux-openbsd /path/to/tmux-openbsd-cutover 2>/dev/null || true
|
||||||
git remote set-url tmux-openbsd https://github.com/tmux/tmux-openbsd-cutover.git
|
|
||||||
else
|
|
||||||
git remote add tmux-openbsd https://github.com/tmux/tmux-openbsd-cutover.git
|
|
||||||
fi
|
|
||||||
git config remote.tmux-openbsd.tagOpt --no-tags
|
git config remote.tmux-openbsd.tagOpt --no-tags
|
||||||
git fetch --no-tags tmux-openbsd master:refs/remotes/tmux-openbsd/master
|
git fetch --no-tags tmux-openbsd master:refs/remotes/tmux-openbsd/master
|
||||||
```
|
```
|
||||||
@@ -156,22 +122,7 @@ Merge it:
|
|||||||
git merge --no-ff --log refs/remotes/tmux-openbsd/master
|
git merge --no-ff --log refs/remotes/tmux-openbsd/master
|
||||||
```
|
```
|
||||||
|
|
||||||
If merging a local cutover branch instead, use the local-clone preparation
|
Resolve conflicts by deciding whether portable or OpenBSD owns the file.
|
||||||
and fetch commands above in place of this fetch block.
|
|
||||||
|
|
||||||
When the merge reports conflicts, it leaves the merge in progress. List the
|
|
||||||
unresolved files, edit the conflict markers to combine the required portable
|
|
||||||
and OpenBSD changes, then stage each resolved file:
|
|
||||||
|
|
||||||
```sh
|
|
||||||
git diff --name-only --diff-filter=U
|
|
||||||
git diff -- path/to/file
|
|
||||||
git add path/to/file
|
|
||||||
```
|
|
||||||
|
|
||||||
For files that should come entirely from one side, decide whether portable or
|
|
||||||
OpenBSD owns the file before using the commands below. They replace the whole
|
|
||||||
file, including changes outside the conflicting hunks.
|
|
||||||
|
|
||||||
Useful commands:
|
Useful commands:
|
||||||
|
|
||||||
@@ -189,25 +140,11 @@ git add path/to/file
|
|||||||
|
|
||||||
This takes the OpenBSD/cutover version of a conflicted file.
|
This takes the OpenBSD/cutover version of a conflicted file.
|
||||||
|
|
||||||
For a modify/delete conflict, the side that deleted the file has no version
|
|
||||||
to check out. For example, portable generates `Makefile` using autotools and
|
|
||||||
does not track OpenBSD's `Makefile`. If Git reports that `Makefile` was deleted
|
|
||||||
in HEAD and modified in cutover, keep the portable deletion with:
|
|
||||||
|
|
||||||
```sh
|
|
||||||
git rm -- Makefile
|
|
||||||
```
|
|
||||||
|
|
||||||
This removes the OpenBSD file left by the merge; regenerate the portable
|
|
||||||
`Makefile` with your usual configure command before building. Use this only
|
|
||||||
for the unmerged OpenBSD file, not an existing generated build file.
|
|
||||||
|
|
||||||
Before committing, inspect the result:
|
Before committing, inspect the result:
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
git status
|
git status
|
||||||
git diff --check
|
git diff --check
|
||||||
git diff --cached --check
|
|
||||||
git diff --cached --stat
|
git diff --cached --stat
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|||||||
+317
-188
@@ -1,4 +1,4 @@
|
|||||||
/* $OpenBSD: cmd-display-menu.c,v 1.55 2026/09/21 12:14:32 nicm Exp $ */
|
/* $OpenBSD: cmd-display-menu.c,v 1.53 2026/08/31 07:46:55 nicm Exp $ */
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2019 Nicholas Marriott <nicholas.marriott@gmail.com>
|
* Copyright (c) 2019 Nicholas Marriott <nicholas.marriott@gmail.com>
|
||||||
@@ -54,8 +54,8 @@ const struct cmd_entry cmd_display_popup_entry = {
|
|||||||
.name = "display-popup",
|
.name = "display-popup",
|
||||||
.alias = "popup",
|
.alias = "popup",
|
||||||
|
|
||||||
.args = { "Bb:Cc:d:e:Eh:ks:S:t:T:w:x:y:", 0, -1, NULL },
|
.args = { "Bb:Cc:d:e:Eh:kNs:S:t:T:w:x:y:", 0, -1, NULL },
|
||||||
.usage = "[-BCEk] [-b border-lines] [-c target-client] "
|
.usage = "[-BCEkN] [-b border-lines] [-c target-client] "
|
||||||
"[-d start-directory] [-e environment] [-h height] "
|
"[-d start-directory] [-e environment] [-h height] "
|
||||||
"[-s style] [-S border-style] " CMD_TARGET_PANE_USAGE
|
"[-s style] [-S border-style] " CMD_TARGET_PANE_USAGE
|
||||||
" [-T title] [-w width] [-x position] [-y position] "
|
" [-T title] [-w width] [-x position] [-y position] "
|
||||||
@@ -91,6 +91,203 @@ cmd_display_menu_args_parse(struct args *args, u_int idx, __unused char **cause)
|
|||||||
return (type);
|
return (type);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
cmd_display_menu_get_popup_pos(struct client *tc, struct cmdq_item *item,
|
||||||
|
struct args *args, u_int *px, u_int *py, u_int w, u_int h)
|
||||||
|
{
|
||||||
|
struct tty *tty = &tc->tty;
|
||||||
|
struct cmd_find_state *target = cmdq_get_target(item);
|
||||||
|
struct key_event *event = cmdq_get_event(item);
|
||||||
|
struct session *s = tc->session;
|
||||||
|
struct winlink *wl = target->wl;
|
||||||
|
struct window_pane *wp = target->wp;
|
||||||
|
struct style_ranges *ranges = NULL;
|
||||||
|
struct style_range *sr = NULL;
|
||||||
|
const char *xp, *yp;
|
||||||
|
char *p;
|
||||||
|
int top;
|
||||||
|
u_int line, ox, oy, sx, sy, lines, position;
|
||||||
|
long n;
|
||||||
|
struct format_tree *ft;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Work out the position from the -x and -y arguments. This is the
|
||||||
|
* bottom-left position.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* If the popup is too big, stop now. */
|
||||||
|
if (w > tty->sx || h > tty->sy)
|
||||||
|
return (0);
|
||||||
|
|
||||||
|
/* Create format with mouse position if any. */
|
||||||
|
ft = format_create_from_target(item);
|
||||||
|
if (event->m.valid) {
|
||||||
|
format_add(ft, "popup_mouse_x", "%u", event->m.x);
|
||||||
|
format_add(ft, "popup_mouse_y", "%u", event->m.y);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Position of the previous menu, for -x/-y L. */
|
||||||
|
format_add(ft, "popup_last_x", "%u", target->w->menu_last_px);
|
||||||
|
format_add(ft, "popup_last_y", "%u", target->w->menu_last_py + h);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* If there are any status lines, add this window position and the
|
||||||
|
* status line position.
|
||||||
|
*/
|
||||||
|
top = status_at_line(tc);
|
||||||
|
if (top != -1) {
|
||||||
|
lines = status_line_size(tc);
|
||||||
|
if (top == 0)
|
||||||
|
top = lines;
|
||||||
|
else
|
||||||
|
top = 0;
|
||||||
|
position = options_get_number(s->options, "status-position");
|
||||||
|
|
||||||
|
for (line = 0; line < lines; line++) {
|
||||||
|
ranges = &tc->status.entries[line].ranges;
|
||||||
|
TAILQ_FOREACH(sr, ranges, entry) {
|
||||||
|
if (sr->type != STYLE_RANGE_WINDOW)
|
||||||
|
continue;
|
||||||
|
if (sr->argument == (u_int)wl->idx)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (sr != NULL)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (sr != NULL) {
|
||||||
|
format_add(ft, "popup_window_status_line_x", "%u",
|
||||||
|
sr->start);
|
||||||
|
if (position == 0) {
|
||||||
|
format_add(ft, "popup_window_status_line_y",
|
||||||
|
"%u", line + 1 + h);
|
||||||
|
} else {
|
||||||
|
format_add(ft, "popup_window_status_line_y",
|
||||||
|
"%u", tty->sy - lines + line);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (position == 0)
|
||||||
|
format_add(ft, "popup_status_line_y", "%u", lines + h);
|
||||||
|
else {
|
||||||
|
format_add(ft, "popup_status_line_y", "%u",
|
||||||
|
tty->sy - lines);
|
||||||
|
}
|
||||||
|
} else
|
||||||
|
top = 0;
|
||||||
|
|
||||||
|
/* Popup width and height. */
|
||||||
|
format_add(ft, "popup_width", "%u", w);
|
||||||
|
format_add(ft, "popup_height", "%u", h);
|
||||||
|
|
||||||
|
/* Position so popup is in the centre. */
|
||||||
|
n = (long)(tty->sx - 1) / 2 - w / 2;
|
||||||
|
if (n < 0)
|
||||||
|
format_add(ft, "popup_centre_x", "%u", 0);
|
||||||
|
else
|
||||||
|
format_add(ft, "popup_centre_x", "%ld", n);
|
||||||
|
n = (tty->sy - 1) / 2 + h / 2;
|
||||||
|
if (n >= tty->sy)
|
||||||
|
format_add(ft, "popup_centre_y", "%u", tty->sy - h);
|
||||||
|
else
|
||||||
|
format_add(ft, "popup_centre_y", "%ld", n);
|
||||||
|
|
||||||
|
/* Position of popup relative to mouse. */
|
||||||
|
if (event->m.valid) {
|
||||||
|
n = (long)event->m.x - w / 2;
|
||||||
|
if (n < 0)
|
||||||
|
format_add(ft, "popup_mouse_centre_x", "%u", 0);
|
||||||
|
else
|
||||||
|
format_add(ft, "popup_mouse_centre_x", "%ld", n);
|
||||||
|
n = event->m.y - h / 2;
|
||||||
|
if (n + h >= tty->sy) {
|
||||||
|
format_add(ft, "popup_mouse_centre_y", "%u",
|
||||||
|
tty->sy - h);
|
||||||
|
} else
|
||||||
|
format_add(ft, "popup_mouse_centre_y", "%ld", n);
|
||||||
|
n = (long)event->m.y + h;
|
||||||
|
if (n >= tty->sy)
|
||||||
|
format_add(ft, "popup_mouse_top", "%u", tty->sy - 1);
|
||||||
|
else
|
||||||
|
format_add(ft, "popup_mouse_top", "%ld", n);
|
||||||
|
n = event->m.y - h;
|
||||||
|
if (n < 0)
|
||||||
|
format_add(ft, "popup_mouse_bottom", "%u", 0);
|
||||||
|
else
|
||||||
|
format_add(ft, "popup_mouse_bottom", "%ld", n);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Position in pane. */
|
||||||
|
tty_window_offset(&tc->tty, &ox, &oy, &sx, &sy);
|
||||||
|
n = top + wp->yoff - oy + h;
|
||||||
|
if (n >= tty->sy)
|
||||||
|
format_add(ft, "popup_pane_top", "%u", tty->sy - h);
|
||||||
|
else
|
||||||
|
format_add(ft, "popup_pane_top", "%ld", n);
|
||||||
|
format_add(ft, "popup_pane_bottom", "%u", top + wp->yoff + wp->sy - oy);
|
||||||
|
format_add(ft, "popup_pane_left", "%u", wp->xoff - ox);
|
||||||
|
n = (long)wp->xoff + wp->sx - ox - w;
|
||||||
|
if (n < 0)
|
||||||
|
format_add(ft, "popup_pane_right", "%u", 0);
|
||||||
|
else
|
||||||
|
format_add(ft, "popup_pane_right", "%ld", n);
|
||||||
|
|
||||||
|
/* Expand horizontal position. */
|
||||||
|
xp = args_get(args, 'x');
|
||||||
|
if (xp == NULL || strcmp(xp, "C") == 0)
|
||||||
|
xp = "#{popup_centre_x}";
|
||||||
|
else if (strcmp(xp, "R") == 0)
|
||||||
|
xp = "#{popup_pane_right}";
|
||||||
|
else if (strcmp(xp, "P") == 0)
|
||||||
|
xp = "#{popup_pane_left}";
|
||||||
|
else if (strcmp(xp, "M") == 0)
|
||||||
|
xp = "#{popup_mouse_centre_x}";
|
||||||
|
else if (strcmp(xp, "L") == 0)
|
||||||
|
xp = "#{popup_last_x}";
|
||||||
|
else if (strcmp(xp, "W") == 0)
|
||||||
|
xp = "#{popup_window_status_line_x}";
|
||||||
|
p = format_expand(ft, xp);
|
||||||
|
n = strtol(p, NULL, 10);
|
||||||
|
if (n + w >= tty->sx)
|
||||||
|
n = tty->sx - w;
|
||||||
|
else if (n < 0)
|
||||||
|
n = 0;
|
||||||
|
*px = n;
|
||||||
|
log_debug("%s: -x: %s = %s = %u (-w %u)", __func__, xp, p, *px, w);
|
||||||
|
free(p);
|
||||||
|
|
||||||
|
/* Expand vertical position */
|
||||||
|
yp = args_get(args, 'y');
|
||||||
|
if (yp == NULL || strcmp(yp, "C") == 0)
|
||||||
|
yp = "#{popup_centre_y}";
|
||||||
|
else if (strcmp(yp, "P") == 0)
|
||||||
|
yp = "#{popup_pane_bottom}";
|
||||||
|
else if (strcmp(yp, "M") == 0)
|
||||||
|
yp = "#{popup_mouse_top}";
|
||||||
|
else if (strcmp(yp, "L") == 0)
|
||||||
|
yp = "#{popup_last_y}";
|
||||||
|
else if (strcmp(yp, "S") == 0)
|
||||||
|
yp = "#{popup_status_line_y}";
|
||||||
|
else if (strcmp(yp, "W") == 0)
|
||||||
|
yp = "#{popup_window_status_line_y}";
|
||||||
|
p = format_expand(ft, yp);
|
||||||
|
n = strtol(p, NULL, 10);
|
||||||
|
if (n < h)
|
||||||
|
n = 0;
|
||||||
|
else
|
||||||
|
n -= h;
|
||||||
|
if (n + h >= tty->sy)
|
||||||
|
n = tty->sy - h;
|
||||||
|
else if (n < 0)
|
||||||
|
n = 0;
|
||||||
|
*py = n;
|
||||||
|
log_debug("%s: -y: %s = %s = %u (-h %u)", __func__, yp, p, *py, h);
|
||||||
|
free(p);
|
||||||
|
|
||||||
|
format_free(ft);
|
||||||
|
return (1);
|
||||||
|
}
|
||||||
|
|
||||||
static int
|
static int
|
||||||
cmd_display_menu_get_menu_pos(struct client *tc, struct cmdq_item *item,
|
cmd_display_menu_get_menu_pos(struct client *tc, struct cmdq_item *item,
|
||||||
struct args *args, u_int *px, u_int *py, u_int w, u_int h)
|
struct args *args, u_int *px, u_int *py, u_int w, u_int h)
|
||||||
@@ -285,8 +482,8 @@ cmd_display_menu_exec(struct cmd *self, struct cmdq_item *item)
|
|||||||
enum box_lines lines = BOX_LINES_DEFAULT;
|
enum box_lines lines = BOX_LINES_DEFAULT;
|
||||||
char *title, *cause = NULL;
|
char *title, *cause = NULL;
|
||||||
int flags = 0, starting_choice = 0;
|
int flags = 0, starting_choice = 0;
|
||||||
u_int px, py, sx, sy, i, count = args_count(args);
|
u_int px, py, i, count = args_count(args);
|
||||||
struct options *o = target->w->options;
|
struct options *o = target->s->curw->window->options;
|
||||||
struct options_entry *oe;
|
struct options_entry *oe;
|
||||||
|
|
||||||
if (args_has(args, 'C')) {
|
if (args_has(args, 'C')) {
|
||||||
@@ -334,6 +531,9 @@ cmd_display_menu_exec(struct cmd *self, struct cmdq_item *item)
|
|||||||
}
|
}
|
||||||
if (menu->count == 0)
|
if (menu->count == 0)
|
||||||
goto out;
|
goto out;
|
||||||
|
if (!cmd_display_menu_get_menu_pos(tc, item, args, &px, &py,
|
||||||
|
menu->width + 4, menu->count + 2))
|
||||||
|
goto out;
|
||||||
|
|
||||||
value = args_get(args, 'b');
|
value = args_get(args, 'b');
|
||||||
if (value != NULL) {
|
if (value != NULL) {
|
||||||
@@ -344,11 +544,7 @@ cmd_display_menu_exec(struct cmd *self, struct cmdq_item *item)
|
|||||||
cmdq_error(item, "menu-border-lines %s", cause);
|
cmdq_error(item, "menu-border-lines %s", cause);
|
||||||
goto fail;
|
goto fail;
|
||||||
}
|
}
|
||||||
} else
|
}
|
||||||
lines = options_get_number(o, "menu-border-lines");
|
|
||||||
menu_get_size(menu, lines, &sx, &sy);
|
|
||||||
if (!cmd_display_menu_get_menu_pos(tc, item, args, &px, &py, sx, sy))
|
|
||||||
goto out;
|
|
||||||
|
|
||||||
if (args_has(args, 'O'))
|
if (args_has(args, 'O'))
|
||||||
flags |= MENU_STAYOPEN;
|
flags |= MENU_STAYOPEN;
|
||||||
@@ -369,22 +565,6 @@ fail:
|
|||||||
return (CMD_RETURN_ERROR);
|
return (CMD_RETURN_ERROR);
|
||||||
}
|
}
|
||||||
|
|
||||||
static enum pane_lines
|
|
||||||
cmd_display_popup_get_lines(const char *value, char **cause)
|
|
||||||
{
|
|
||||||
const struct options_table_entry *oe;
|
|
||||||
|
|
||||||
if (value == NULL)
|
|
||||||
value = "single";
|
|
||||||
else if (strcmp(value, "rounded") == 0)
|
|
||||||
value = "single";
|
|
||||||
else if (strcmp(value, "padded") == 0)
|
|
||||||
value = "spaces";
|
|
||||||
|
|
||||||
oe = options_search("pane-border-lines");
|
|
||||||
return (options_find_choice(oe, value, cause));
|
|
||||||
}
|
|
||||||
|
|
||||||
static enum cmd_retval
|
static enum cmd_retval
|
||||||
cmd_display_popup_exec(struct cmd *self, struct cmdq_item *item)
|
cmd_display_popup_exec(struct cmd *self, struct cmdq_item *item)
|
||||||
{
|
{
|
||||||
@@ -392,199 +572,148 @@ cmd_display_popup_exec(struct cmd *self, struct cmdq_item *item)
|
|||||||
struct cmd_find_state *target = cmdq_get_target(item);
|
struct cmd_find_state *target = cmdq_get_target(item);
|
||||||
struct session *s = target->s;
|
struct session *s = target->s;
|
||||||
struct client *tc = cmdq_get_target_client(item);
|
struct client *tc = cmdq_get_target_client(item);
|
||||||
struct winlink *wl = target->wl;
|
struct tty *tty = &tc->tty;
|
||||||
struct window *w = wl->window;
|
const char *value, *shell, *shellcmd = NULL;
|
||||||
struct window_pane *wp = target->wp, *new_wp = NULL;
|
const char *style = args_get(args, 's');
|
||||||
struct spawn_context sc = { 0 };
|
|
||||||
struct layout_cell *lc = NULL;
|
|
||||||
struct layout_geometry lg;
|
|
||||||
struct event_payload *ep;
|
|
||||||
struct cmd_find_state fs;
|
|
||||||
const char *value, *style = args_get(args, 's');
|
|
||||||
const char *border_style = args_get(args, 'S');
|
const char *border_style = args_get(args, 'S');
|
||||||
char *cause = NULL, *title = NULL;
|
char *cwd = NULL, *cause = NULL, **argv = NULL;
|
||||||
enum pane_lines lines = PANE_LINES_SINGLE;
|
char *title = NULL;
|
||||||
u_int px, py, sx, sy, count = args_count(args);
|
int modify = popup_present(tc);
|
||||||
|
int flags = -1, argc = 0;
|
||||||
|
enum box_lines lines = BOX_LINES_DEFAULT;
|
||||||
|
u_int px, py, w, h, count = args_count(args);
|
||||||
struct args_value *av;
|
struct args_value *av;
|
||||||
long long ll;
|
struct environ *env = NULL;
|
||||||
|
struct options *o = s->curw->window->options;
|
||||||
|
struct options_entry *oe;
|
||||||
|
|
||||||
if (args_has(args, 'C')) {
|
if (args_has(args, 'C')) {
|
||||||
if (w->modal != NULL)
|
server_client_clear_overlay(tc);
|
||||||
server_kill_pane(w->modal);
|
|
||||||
return (CMD_RETURN_NORMAL);
|
return (CMD_RETURN_NORMAL);
|
||||||
}
|
}
|
||||||
if (tc->flags & CLIENT_CONTROL)
|
if (tc->flags & CLIENT_CONTROL)
|
||||||
return (CMD_RETURN_NORMAL);
|
return (CMD_RETURN_NORMAL);
|
||||||
if (w->modal != NULL)
|
if (!modify && tc->overlay_draw != NULL)
|
||||||
return (CMD_RETURN_NORMAL);
|
return (CMD_RETURN_NORMAL);
|
||||||
|
|
||||||
|
if (!modify) {
|
||||||
|
h = tty->sy / 2;
|
||||||
|
if (args_has(args, 'h')) {
|
||||||
|
h = args_percentage(args, 'h', 1, tty->sy, tty->sy,
|
||||||
|
&cause);
|
||||||
|
if (cause != NULL) {
|
||||||
|
cmdq_error(item, "height %s", cause);
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
w = tty->sx / 2;
|
||||||
|
if (args_has(args, 'w')) {
|
||||||
|
w = args_percentage(args, 'w', 1, tty->sx, tty->sx,
|
||||||
|
&cause);
|
||||||
|
if (cause != NULL) {
|
||||||
|
cmdq_error(item, "width %s", cause);
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (w > tty->sx)
|
||||||
|
w = tty->sx;
|
||||||
|
if (h > tty->sy)
|
||||||
|
h = tty->sy;
|
||||||
|
if (!cmd_display_menu_get_popup_pos(tc, item, args, &px, &py,
|
||||||
|
w, h))
|
||||||
|
goto out;
|
||||||
|
|
||||||
|
value = args_get(args, 'd');
|
||||||
|
if (value != NULL)
|
||||||
|
cwd = format_single_from_target(item, value);
|
||||||
|
else
|
||||||
|
cwd = xstrdup(server_client_get_cwd(tc, s));
|
||||||
|
if (count == 0) {
|
||||||
|
shellcmd = options_get_string(s->options,
|
||||||
|
"default-command");
|
||||||
|
} else if (count == 1)
|
||||||
|
shellcmd = args_string(args, 0);
|
||||||
|
if (count <= 1 && (shellcmd == NULL || *shellcmd == '\0')) {
|
||||||
|
shellcmd = NULL;
|
||||||
|
shell = options_get_string(s->options, "default-shell");
|
||||||
|
if (!checkshell(shell))
|
||||||
|
shell = _PATH_BSHELL;
|
||||||
|
cmd_append_argv(&argc, &argv, shell);
|
||||||
|
} else
|
||||||
|
args_to_vector(args, &argc, &argv);
|
||||||
|
|
||||||
|
if (args_has(args, 'e') >= 1) {
|
||||||
|
env = environ_create();
|
||||||
|
av = args_first_value(args, 'e');
|
||||||
|
while (av != NULL) {
|
||||||
|
environ_put(env, av->string, 0);
|
||||||
|
av = args_next_value(av);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
value = args_get(args, 'b');
|
value = args_get(args, 'b');
|
||||||
if (args_has(args, 'B'))
|
if (args_has(args, 'B'))
|
||||||
lines = PANE_LINES_NONE;
|
lines = BOX_LINES_NONE;
|
||||||
else if (value != NULL) {
|
else if (value != NULL) {
|
||||||
lines = cmd_display_popup_get_lines(value, &cause);
|
oe = options_get(o, "popup-border-lines");
|
||||||
|
lines = options_find_choice(options_table_entry(oe), value,
|
||||||
|
&cause);
|
||||||
if (cause != NULL) {
|
if (cause != NULL) {
|
||||||
cmdq_error(item, "pane-border-lines %s", cause);
|
cmdq_error(item, "popup-border-lines %s", cause);
|
||||||
goto fail;
|
goto fail;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
sy = w->sy / 2;
|
|
||||||
if (args_has(args, 'h')) {
|
|
||||||
ll = args_percentage(args, 'h', 1, w->sy, w->sy, &cause);
|
|
||||||
if (cause != NULL) {
|
|
||||||
cmdq_error(item, "height %s", cause);
|
|
||||||
goto fail;
|
|
||||||
}
|
|
||||||
sy = ll;
|
|
||||||
}
|
|
||||||
|
|
||||||
sx = w->sx / 2;
|
|
||||||
if (args_has(args, 'w')) {
|
|
||||||
ll = args_percentage(args, 'w', 1, w->sx, w->sx, &cause);
|
|
||||||
if (cause != NULL) {
|
|
||||||
cmdq_error(item, "width %s", cause);
|
|
||||||
goto fail;
|
|
||||||
}
|
|
||||||
sx = ll;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (sx > w->sx)
|
|
||||||
sx = w->sx;
|
|
||||||
if (sy > w->sy)
|
|
||||||
sy = w->sy;
|
|
||||||
if ((lines == PANE_LINES_NONE && (sx < 1 || sy < 1)) ||
|
|
||||||
(lines != PANE_LINES_NONE && (sx < 3 || sy < 3)))
|
|
||||||
goto out;
|
|
||||||
if (!cmd_display_menu_get_menu_pos(tc, item, args, &px, &py, sx, sy))
|
|
||||||
goto out;
|
|
||||||
|
|
||||||
lg.sx = sx;
|
|
||||||
lg.sy = sy;
|
|
||||||
lg.xoff = px;
|
|
||||||
lg.yoff = py;
|
|
||||||
if (lines != PANE_LINES_NONE) {
|
|
||||||
lg.sx -= 2;
|
|
||||||
lg.sy -= 2;
|
|
||||||
lg.xoff++;
|
|
||||||
lg.yoff++;
|
|
||||||
}
|
|
||||||
|
|
||||||
window_push_zoom(w, 0, 1);
|
|
||||||
lc = layout_floating_pane(w, wp, &lg);
|
|
||||||
if (lc == NULL) {
|
|
||||||
window_pop_zoom(w);
|
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
|
|
||||||
sc.item = item;
|
|
||||||
sc.s = s;
|
|
||||||
sc.wl = wl;
|
|
||||||
sc.tc = tc;
|
|
||||||
sc.wp0 = wp;
|
|
||||||
sc.lc = lc;
|
|
||||||
sc.idx = -1;
|
|
||||||
sc.cwd = args_get(args, 'd');
|
|
||||||
sc.flags = (SPAWN_FLOATING|SPAWN_MODAL|SPAWN_FLOATOVERZOOM);
|
|
||||||
|
|
||||||
if (count != 1 || *args_string(args, 0) != '\0')
|
|
||||||
args_to_vector(args, &sc.argc, &sc.argv);
|
|
||||||
sc.environ = environ_create();
|
|
||||||
av = args_first_value(args, 'e');
|
|
||||||
while (av != NULL) {
|
|
||||||
environ_put(sc.environ, av->string, 0);
|
|
||||||
av = args_next_value(av);
|
|
||||||
}
|
|
||||||
|
|
||||||
new_wp = spawn_pane(&sc, &cause);
|
|
||||||
if (new_wp == NULL) {
|
|
||||||
cmdq_error(item, "create pane failed: %s", cause);
|
|
||||||
free(cause);
|
|
||||||
cause = NULL;
|
|
||||||
window_pop_zoom(w);
|
|
||||||
goto fail;
|
|
||||||
}
|
|
||||||
window_pop_zoom(w);
|
|
||||||
new_wp->flags |= PANE_CAPTUREALLKEYS;
|
|
||||||
if (!args_has(args, 'E'))
|
|
||||||
new_wp->flags |= PANE_CLOSEONCANCEL;
|
|
||||||
|
|
||||||
options_set_number(new_wp->options, "pane-border-lines", lines);
|
|
||||||
if (args_has(args, 'E') > 1) {
|
|
||||||
if (args_has(args, 'k'))
|
|
||||||
options_set_number(new_wp->options, "remain-on-exit", 4);
|
|
||||||
else
|
|
||||||
options_set_number(new_wp->options, "remain-on-exit", 2);
|
|
||||||
} else if (args_has(args, 'E'))
|
|
||||||
options_set_number(new_wp->options, "remain-on-exit", 0);
|
|
||||||
else if (args_has(args, 'k'))
|
|
||||||
options_set_number(new_wp->options, "remain-on-exit", 3);
|
|
||||||
else
|
|
||||||
options_set_number(new_wp->options, "remain-on-exit", 1);
|
|
||||||
options_set_string(new_wp->options, "remain-on-exit-format", 0, "%s", "");
|
|
||||||
|
|
||||||
if (style != NULL) {
|
|
||||||
if (options_set_string(new_wp->options, "window-style", 0,
|
|
||||||
"%s", style) == NULL) {
|
|
||||||
cmdq_error(item, "bad style: %s", style);
|
|
||||||
goto fail;
|
|
||||||
}
|
|
||||||
options_set_string(new_wp->options, "window-active-style", 0,
|
|
||||||
"%s", style);
|
|
||||||
new_wp->flags |= (PANE_REDRAW|PANE_STYLECHANGED|
|
|
||||||
PANE_THEMECHANGED);
|
|
||||||
}
|
|
||||||
if (border_style != NULL) {
|
|
||||||
if (options_set_string(new_wp->options, "pane-border-style", 0,
|
|
||||||
"%s", border_style) == NULL) {
|
|
||||||
cmdq_error(item, "bad border style: %s", border_style);
|
|
||||||
goto fail;
|
|
||||||
}
|
|
||||||
options_set_string(new_wp->options, "pane-active-border-style",
|
|
||||||
0, "%s", border_style);
|
|
||||||
}
|
|
||||||
if (args_has(args, 'T'))
|
if (args_has(args, 'T'))
|
||||||
title = format_single_from_target(item, args_get(args, 'T'));
|
title = format_single_from_target(item, args_get(args, 'T'));
|
||||||
if (title != NULL) {
|
else
|
||||||
options_set_number(new_wp->options, "pane-border-status",
|
title = xstrdup("");
|
||||||
PANE_STATUS_TOP);
|
|
||||||
options_set_string(new_wp->options, "pane-border-format", 0,
|
if (args_has(args, 'N') || !modify)
|
||||||
"%s", "#{pane_title}");
|
flags = 0;
|
||||||
screen_set_title(&new_wp->base, title, 0);
|
if (args_has(args, 'E') > 1) {
|
||||||
ep = event_payload_create();
|
if (flags == -1)
|
||||||
cmd_find_from_pane(&fs, new_wp, 0);
|
flags = 0;
|
||||||
event_payload_set_target(ep, &fs);
|
flags |= POPUP_CLOSEEXITZERO;
|
||||||
event_payload_set_pane(ep, "pane", new_wp);
|
} else if (args_has(args, 'E')) {
|
||||||
event_payload_set_window(ep, "window", new_wp->window);
|
if (flags == -1)
|
||||||
event_payload_set_string(ep, "new_title", "%s", title);
|
flags = 0;
|
||||||
events_fire("pane-title-changed", ep);
|
flags |= POPUP_CLOSEEXIT;
|
||||||
|
}
|
||||||
|
if (args_has(args, 'k')) {
|
||||||
|
if (flags == -1)
|
||||||
|
flags = 0;
|
||||||
|
flags |= POPUP_CLOSEANYKEY;
|
||||||
}
|
}
|
||||||
|
|
||||||
new_wp->wait_item = item;
|
if (modify) {
|
||||||
server_redraw_session(s);
|
popup_modify(tc, title, style, border_style, lines, flags);
|
||||||
if (sc.argv != NULL)
|
goto out;
|
||||||
cmd_free_argv(sc.argc, sc.argv);
|
}
|
||||||
environ_free(sc.environ);
|
if (popup_display(flags, lines, item, px, py, w, h, env, shellcmd, argc,
|
||||||
|
argv, cwd, title, tc, s, style, border_style, NULL, NULL) != 0)
|
||||||
|
goto out;
|
||||||
|
environ_free(env);
|
||||||
|
free(cwd);
|
||||||
free(title);
|
free(title);
|
||||||
|
cmd_free_argv(argc, argv);
|
||||||
return (CMD_RETURN_WAIT);
|
return (CMD_RETURN_WAIT);
|
||||||
|
|
||||||
out:
|
out:
|
||||||
if (sc.argv != NULL)
|
cmd_free_argv(argc, argv);
|
||||||
cmd_free_argv(sc.argc, sc.argv);
|
environ_free(env);
|
||||||
environ_free(sc.environ);
|
free(cwd);
|
||||||
free(title);
|
free(title);
|
||||||
return (CMD_RETURN_NORMAL);
|
return (CMD_RETURN_NORMAL);
|
||||||
|
|
||||||
fail:
|
fail:
|
||||||
free(cause);
|
free(cause);
|
||||||
if (new_wp != NULL) {
|
cmd_free_argv(argc, argv);
|
||||||
server_client_remove_pane(new_wp);
|
environ_free(env);
|
||||||
layout_close_pane(new_wp);
|
free(cwd);
|
||||||
window_remove_pane(new_wp->window, new_wp);
|
|
||||||
}
|
|
||||||
if (sc.argv != NULL)
|
|
||||||
cmd_free_argv(sc.argc, sc.argv);
|
|
||||||
environ_free(sc.environ);
|
|
||||||
free(title);
|
free(title);
|
||||||
return (CMD_RETURN_ERROR);
|
return (CMD_RETURN_ERROR);
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
# configure.ac
|
# configure.ac
|
||||||
|
|
||||||
AC_INIT([tmux], next-3.9)
|
AC_INIT([tmux], 3.8-rc2)
|
||||||
AC_PREREQ([2.60])
|
AC_PREREQ([2.60])
|
||||||
|
|
||||||
AC_CONFIG_AUX_DIR(etc)
|
AC_CONFIG_AUX_DIR(etc)
|
||||||
|
|||||||
+1
-1
@@ -44,7 +44,7 @@ LLVMFuzzerTestOneInput(const u_char *data, size_t size)
|
|||||||
w = window_create(PANE_WIDTH, PANE_HEIGHT, 0, 0);
|
w = window_create(PANE_WIDTH, PANE_HEIGHT, 0, 0);
|
||||||
wp = window_add_pane(w, NULL, 0, 0);
|
wp = window_add_pane(w, NULL, 0, 0);
|
||||||
bufferevent_pair_new(libevent, BEV_OPT_CLOSE_ON_FREE, vpty);
|
bufferevent_pair_new(libevent, BEV_OPT_CLOSE_ON_FREE, vpty);
|
||||||
wp->ictx = input_init(wp, vpty[0], NULL);
|
wp->ictx = input_init(wp, vpty[0], NULL, NULL);
|
||||||
window_add_ref(w, __func__);
|
window_add_ref(w, __func__);
|
||||||
|
|
||||||
wp->fd = open("/dev/null", O_WRONLY);
|
wp->fd = open("/dev/null", O_WRONLY);
|
||||||
|
|||||||
@@ -1,111 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (c) 2026 Arthur Chan <arthur.chan@adalogics.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.
|
|
||||||
*/
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Fuzz the custom layout parser.
|
|
||||||
*
|
|
||||||
* A layout string such as "bb62,80x24,0,0{40x24,0,0,1,39x24,41,0,2}" is
|
|
||||||
* accepted by select-layout and is what tmux stores and restores for a
|
|
||||||
* window, so it is parsed from configuration and from commands. It drives
|
|
||||||
* layout-custom.c (the string parser and the checksum), then layout.c, which
|
|
||||||
* resizes and assigns the cells.
|
|
||||||
*
|
|
||||||
* layout_parse() refuses a window with no panes, and refuses a layout whose
|
|
||||||
* cell count is smaller than the pane count, so the window is given a fixed
|
|
||||||
* number of panes. The count is fixed rather than derived from the input, so
|
|
||||||
* a mutation always means a different layout string.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <sys/types.h>
|
|
||||||
|
|
||||||
#include <stddef.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <string.h>
|
|
||||||
|
|
||||||
#include "tmux.h"
|
|
||||||
|
|
||||||
#define FUZZER_MAXLEN 1024
|
|
||||||
#define FUZZER_PANES 4
|
|
||||||
|
|
||||||
struct event_base *libevent;
|
|
||||||
|
|
||||||
int
|
|
||||||
LLVMFuzzerTestOneInput(const u_char *data, size_t size)
|
|
||||||
{
|
|
||||||
struct window *w;
|
|
||||||
struct window_pane *wp;
|
|
||||||
char *buf, *cause = NULL, *dump;
|
|
||||||
u_int i;
|
|
||||||
|
|
||||||
if (size == 0 || size > FUZZER_MAXLEN)
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
/* layout_parse() takes a C string. */
|
|
||||||
buf = malloc(size + 1);
|
|
||||||
if (buf == NULL)
|
|
||||||
return 0;
|
|
||||||
memcpy(buf, data, size);
|
|
||||||
buf[size] = '\0';
|
|
||||||
|
|
||||||
w = window_create(80, 24, 0, 0);
|
|
||||||
if (w == NULL) {
|
|
||||||
free(buf);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
window_add_ref(w, __func__);
|
|
||||||
|
|
||||||
for (i = 0; i < FUZZER_PANES; i++) {
|
|
||||||
wp = window_add_pane(w, NULL, 0, 0);
|
|
||||||
if (w->active == NULL)
|
|
||||||
w->active = wp;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (layout_parse(w, buf, &cause) == 0) {
|
|
||||||
dump = layout_dump(w, w->layout_root, 1);
|
|
||||||
free(dump);
|
|
||||||
}
|
|
||||||
free(cause);
|
|
||||||
|
|
||||||
window_remove_ref(w, __func__);
|
|
||||||
|
|
||||||
free(buf);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
int
|
|
||||||
LLVMFuzzerInitialize(__unused int *argc, __unused char ***argv)
|
|
||||||
{
|
|
||||||
const struct options_table_entry *oe;
|
|
||||||
|
|
||||||
global_environ = environ_create();
|
|
||||||
global_options = options_create(NULL);
|
|
||||||
global_s_options = options_create(NULL);
|
|
||||||
global_w_options = options_create(NULL);
|
|
||||||
for (oe = options_table; oe->name != NULL; oe++) {
|
|
||||||
if (oe->scope & OPTIONS_TABLE_SERVER)
|
|
||||||
options_default(global_options, oe);
|
|
||||||
if (oe->scope & OPTIONS_TABLE_SESSION)
|
|
||||||
options_default(global_s_options, oe);
|
|
||||||
if (oe->scope & OPTIONS_TABLE_WINDOW)
|
|
||||||
options_default(global_w_options, oe);
|
|
||||||
}
|
|
||||||
|
|
||||||
libevent = osdep_event_init();
|
|
||||||
|
|
||||||
socket_path = xstrdup("dummy");
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
@@ -1,17 +0,0 @@
|
|||||||
# tmux custom layout strings: "<checksum>,<sx>x<sy>,<x>,<y>{...}" / "[...]"
|
|
||||||
","
|
|
||||||
"x"
|
|
||||||
"{"
|
|
||||||
"}"
|
|
||||||
"["
|
|
||||||
"]"
|
|
||||||
"0"
|
|
||||||
"1"
|
|
||||||
"80x24,0,0"
|
|
||||||
"40x24,0,0"
|
|
||||||
"bb62,"
|
|
||||||
"cafe,"
|
|
||||||
",0,0,0"
|
|
||||||
",0,0{"
|
|
||||||
",0,0["
|
|
||||||
"@"
|
|
||||||
@@ -1,2 +0,0 @@
|
|||||||
[libfuzzer]
|
|
||||||
max_len = 1024
|
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
/* $OpenBSD: input.c,v 1.272 2026/09/21 10:22:31 nicm Exp $ */
|
/* $OpenBSD: input.c,v 1.271 2026/08/31 19:34:09 nicm Exp $ */
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>
|
* Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>
|
||||||
@@ -101,6 +101,7 @@ struct input_ctx {
|
|||||||
struct bufferevent *event;
|
struct bufferevent *event;
|
||||||
struct screen_write_ctx ctx;
|
struct screen_write_ctx ctx;
|
||||||
struct colour_palette *palette;
|
struct colour_palette *palette;
|
||||||
|
struct client *c;
|
||||||
|
|
||||||
struct input_cell cell;
|
struct input_cell cell;
|
||||||
struct input_cell old_cell;
|
struct input_cell old_cell;
|
||||||
@@ -873,7 +874,7 @@ input_restore_state(struct input_ctx *ictx)
|
|||||||
/* Initialise input parser. */
|
/* Initialise input parser. */
|
||||||
struct input_ctx *
|
struct input_ctx *
|
||||||
input_init(struct window_pane *wp, struct bufferevent *bev,
|
input_init(struct window_pane *wp, struct bufferevent *bev,
|
||||||
struct colour_palette *palette)
|
struct colour_palette *palette, struct client *c)
|
||||||
{
|
{
|
||||||
struct input_ctx *ictx;
|
struct input_ctx *ictx;
|
||||||
|
|
||||||
@@ -881,6 +882,7 @@ input_init(struct window_pane *wp, struct bufferevent *bev,
|
|||||||
ictx->wp = wp;
|
ictx->wp = wp;
|
||||||
ictx->event = bev;
|
ictx->event = bev;
|
||||||
ictx->palette = palette;
|
ictx->palette = palette;
|
||||||
|
ictx->c = c;
|
||||||
|
|
||||||
ictx->input_space = INPUT_BUF_START;
|
ictx->input_space = INPUT_BUF_START;
|
||||||
ictx->input_buf = xmalloc(INPUT_BUF_START);
|
ictx->input_buf = xmalloc(INPUT_BUF_START);
|
||||||
@@ -3423,9 +3425,15 @@ input_osc_52(struct input_ctx *ictx, const char *p)
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
if (wp == NULL) {
|
if (wp == NULL) {
|
||||||
free(out);
|
/* Popup window. */
|
||||||
return;
|
if (ictx->c == NULL) {
|
||||||
|
free(out);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
tty_set_selection(&ictx->c->tty, clip, out, outlen);
|
||||||
|
paste_add(NULL, out, outlen);
|
||||||
} else {
|
} else {
|
||||||
|
/* Normal window. */
|
||||||
screen_write_start_pane(&ctx, wp, NULL);
|
screen_write_start_pane(&ctx, wp, NULL);
|
||||||
screen_write_setselection(&ctx, clip, out, outlen);
|
screen_write_setselection(&ctx, clip, out, outlen);
|
||||||
screen_write_stop(&ctx);
|
screen_write_stop(&ctx);
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
/* $OpenBSD: menu.c,v 1.71 2026/09/21 12:14:32 nicm Exp $ */
|
/* $OpenBSD: menu.c,v 1.70 2026/08/17 07:33:55 nicm Exp $ */
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2019 Nicholas Marriott <nicholas.marriott@gmail.com>
|
* Copyright (c) 2019 Nicholas Marriott <nicholas.marriott@gmail.com>
|
||||||
@@ -51,18 +51,6 @@ struct menu_data {
|
|||||||
void *data;
|
void *data;
|
||||||
};
|
};
|
||||||
|
|
||||||
void
|
|
||||||
menu_get_size(struct menu *menu, enum box_lines lines, u_int *sx, u_int *sy)
|
|
||||||
{
|
|
||||||
if (lines == BOX_LINES_NONE) {
|
|
||||||
*sx = menu->item_width + 2;
|
|
||||||
*sy = menu->count;
|
|
||||||
} else {
|
|
||||||
*sx = menu->width + 4;
|
|
||||||
*sy = menu->count + 2;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
void
|
||||||
menu_add_items(struct menu *menu, const struct menu_item *items,
|
menu_add_items(struct menu *menu, const struct menu_item *items,
|
||||||
struct cmdq_item *qitem, struct client *c, struct cmd_find_state *fs)
|
struct cmdq_item *qitem, struct client *c, struct cmd_find_state *fs)
|
||||||
@@ -154,8 +142,6 @@ menu_add_item(struct menu *menu, const struct menu_item *item,
|
|||||||
width = format_width(new_item->name);
|
width = format_width(new_item->name);
|
||||||
if (*new_item->name == '-')
|
if (*new_item->name == '-')
|
||||||
width--;
|
width--;
|
||||||
if (width > menu->item_width)
|
|
||||||
menu->item_width = width;
|
|
||||||
if (width > menu->width)
|
if (width > menu->width)
|
||||||
menu->width = width;
|
menu->width = width;
|
||||||
}
|
}
|
||||||
@@ -252,7 +238,7 @@ menu_update(struct menu_data *md)
|
|||||||
screen_write_clearscreen(&ctx, 8);
|
screen_write_clearscreen(&ctx, 8);
|
||||||
|
|
||||||
if (md->border_lines != BOX_LINES_NONE) {
|
if (md->border_lines != BOX_LINES_NONE) {
|
||||||
screen_write_box(&ctx, menu_width(md), menu_height(md),
|
screen_write_box(&ctx, menu->width + 4, menu->count + 2,
|
||||||
md->border_lines, &md->border_style_gc, menu->title);
|
md->border_lines, &md->border_style_gc, menu->title);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -300,13 +286,11 @@ menu_destroy(struct window *w)
|
|||||||
void
|
void
|
||||||
menu_get_cursor(struct menu_data *md, u_int *cx, u_int *cy)
|
menu_get_cursor(struct menu_data *md, u_int *cx, u_int *cy)
|
||||||
{
|
{
|
||||||
u_int border = (md->border_lines != BOX_LINES_NONE);
|
*cx = md->px + 2;
|
||||||
|
|
||||||
*cx = md->px + 1 + border;
|
|
||||||
if (md->choice == -1)
|
if (md->choice == -1)
|
||||||
*cy = md->py;
|
*cy = md->py;
|
||||||
else
|
else
|
||||||
*cy = md->py + border + md->choice;
|
*cy = md->py + 1 + md->choice;
|
||||||
}
|
}
|
||||||
|
|
||||||
struct screen *
|
struct screen *
|
||||||
@@ -318,19 +302,13 @@ menu_screen(struct menu_data *md)
|
|||||||
u_int
|
u_int
|
||||||
menu_width(struct menu_data *md)
|
menu_width(struct menu_data *md)
|
||||||
{
|
{
|
||||||
u_int sx, sy;
|
return (md->menu->width + 4);
|
||||||
|
|
||||||
menu_get_size(md->menu, md->border_lines, &sx, &sy);
|
|
||||||
return (sx);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
u_int
|
u_int
|
||||||
menu_height(struct menu_data *md)
|
menu_height(struct menu_data *md)
|
||||||
{
|
{
|
||||||
u_int sx, sy;
|
return (md->menu->count + 2);
|
||||||
|
|
||||||
menu_get_size(md->menu, md->border_lines, &sx, &sy);
|
|
||||||
return (sy);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
u_int
|
u_int
|
||||||
@@ -360,7 +338,6 @@ menu_key(struct client *c, struct menu_data *md, struct key_event *event)
|
|||||||
enum cmd_parse_status status;
|
enum cmd_parse_status status;
|
||||||
char *error;
|
char *error;
|
||||||
key_code key;
|
key_code key;
|
||||||
u_int border;
|
|
||||||
|
|
||||||
if (KEYC_IS_MOUSE(event->key)) {
|
if (KEYC_IS_MOUSE(event->key)) {
|
||||||
/*
|
/*
|
||||||
@@ -376,10 +353,10 @@ menu_key(struct client *c, struct menu_data *md, struct key_event *event)
|
|||||||
return (1);
|
return (1);
|
||||||
return (0);
|
return (0);
|
||||||
}
|
}
|
||||||
border = (md->border_lines != BOX_LINES_NONE);
|
if (m->x < md->px ||
|
||||||
if (m->x < md->px || m->x >= md->px + menu_width(md) ||
|
m->x > md->px + 4 + menu->width ||
|
||||||
m->y < md->py + border ||
|
m->y < md->py + 1 ||
|
||||||
m->y >= md->py + border + n) {
|
m->y > md->py + 1 + n - 1) {
|
||||||
if (~md->flags & MENU_STAYOPEN) {
|
if (~md->flags & MENU_STAYOPEN) {
|
||||||
if (!move && MOUSE_RELEASE(m->b))
|
if (!move && MOUSE_RELEASE(m->b))
|
||||||
return (1);
|
return (1);
|
||||||
@@ -402,7 +379,7 @@ menu_key(struct client *c, struct menu_data *md, struct key_event *event)
|
|||||||
if (!MOUSE_WHEEL(m->b) && !MOUSE_DRAG(m->b))
|
if (!MOUSE_WHEEL(m->b) && !MOUSE_DRAG(m->b))
|
||||||
goto chosen;
|
goto chosen;
|
||||||
}
|
}
|
||||||
md->choice = m->y - (md->py + border);
|
md->choice = m->y - (md->py + 1);
|
||||||
if (md->choice != old)
|
if (md->choice != old)
|
||||||
server_redraw_window_menu(md->w);
|
server_redraw_window_menu(md->w);
|
||||||
return (0);
|
return (0);
|
||||||
@@ -569,16 +546,20 @@ menu_resize(struct menu_data *md, struct window *w)
|
|||||||
|
|
||||||
if (md == NULL)
|
if (md == NULL)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
nx = md->px;
|
nx = md->px;
|
||||||
ny = md->py;
|
ny = md->py;
|
||||||
|
|
||||||
menu_get_size(md->menu, md->border_lines, &sx, &sy);
|
sx = md->menu->width + 4;
|
||||||
|
sy = md->menu->count + 2;
|
||||||
|
|
||||||
if (nx + sx > w->sx) {
|
if (nx + sx > w->sx) {
|
||||||
if (w->sx <= sx)
|
if (w->sx <= sx)
|
||||||
nx = 0;
|
nx = 0;
|
||||||
else
|
else
|
||||||
nx = w->sx - sx;
|
nx = w->sx - sx;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ny + sy > w->sy) {
|
if (ny + sy > w->sy) {
|
||||||
if (w->sy <= sy)
|
if (w->sy <= sy)
|
||||||
ny = 0;
|
ny = 0;
|
||||||
@@ -610,9 +591,8 @@ menu_display(struct menu *menu, int flags, int starting_choice,
|
|||||||
w = fs->w;
|
w = fs->w;
|
||||||
o = w->options;
|
o = w->options;
|
||||||
|
|
||||||
if (lines == BOX_LINES_DEFAULT)
|
sx = menu->width + 4;
|
||||||
lines = options_get_number(o, "menu-border-lines");
|
sy = menu->count + 2;
|
||||||
menu_get_size(menu, lines, &sx, &sy);
|
|
||||||
if (sx >= w->sx)
|
if (sx >= w->sx)
|
||||||
px = 0;
|
px = 0;
|
||||||
else if (px + sx > w->sx)
|
else if (px + sx > w->sx)
|
||||||
@@ -624,6 +604,9 @@ menu_display(struct menu *menu, int flags, int starting_choice,
|
|||||||
w->menu_last_px = px;
|
w->menu_last_px = px;
|
||||||
w->menu_last_py = py;
|
w->menu_last_py = py;
|
||||||
|
|
||||||
|
if (lines == BOX_LINES_DEFAULT)
|
||||||
|
lines = options_get_number(o, "menu-border-lines");
|
||||||
|
|
||||||
md = xcalloc(1, sizeof *md);
|
md = xcalloc(1, sizeof *md);
|
||||||
md->w = w;
|
md->w = w;
|
||||||
md->flags = flags;
|
md->flags = flags;
|
||||||
|
|||||||
+6
-11
@@ -1,4 +1,4 @@
|
|||||||
/* $OpenBSD: mode-tree.c,v 1.102 2026/09/21 12:14:32 nicm Exp $ */
|
/* $OpenBSD: mode-tree.c,v 1.101 2026/08/05 07:50:21 nicm Exp $ */
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2017 Nicholas Marriott <nicholas.marriott@gmail.com>
|
* Copyright (c) 2017 Nicholas Marriott <nicholas.marriott@gmail.com>
|
||||||
@@ -1375,14 +1375,12 @@ static void
|
|||||||
mode_tree_display_menu(struct mode_tree_data *mtd, struct client *c, u_int x,
|
mode_tree_display_menu(struct mode_tree_data *mtd, struct client *c, u_int x,
|
||||||
u_int y, int outside)
|
u_int y, int outside)
|
||||||
{
|
{
|
||||||
struct window *w = mtd->wp->window;
|
|
||||||
struct mode_tree_item *mti;
|
struct mode_tree_item *mti;
|
||||||
struct menu *menu;
|
struct menu *menu;
|
||||||
const struct menu_item *items;
|
const struct menu_item *items;
|
||||||
struct mode_tree_menu *mtm;
|
struct mode_tree_menu *mtm;
|
||||||
char *title;
|
char *title;
|
||||||
enum box_lines lines;
|
u_int line;
|
||||||
u_int line, sx, sy;
|
|
||||||
|
|
||||||
if (mtd->offset + y > mtd->line_size - 1)
|
if (mtd->offset + y > mtd->line_size - 1)
|
||||||
line = mtd->current;
|
line = mtd->current;
|
||||||
@@ -1407,17 +1405,14 @@ mode_tree_display_menu(struct mode_tree_data *mtd, struct client *c, u_int x,
|
|||||||
mtm->line = line;
|
mtm->line = line;
|
||||||
mtd->references++;
|
mtd->references++;
|
||||||
|
|
||||||
lines = options_get_number(w->options, "menu-border-lines");
|
if (x >= (menu->width + 4) / 2)
|
||||||
menu_get_size(menu, lines, &sx, &sy);
|
x -= (menu->width + 4) / 2;
|
||||||
if (x >= sx / 2)
|
|
||||||
x -= sx / 2;
|
|
||||||
else
|
else
|
||||||
x = 0;
|
x = 0;
|
||||||
x += mtd->wp->xoff;
|
x += mtd->wp->xoff;
|
||||||
y += mtd->wp->yoff;
|
y += mtd->wp->yoff;
|
||||||
|
if (menu_display(menu, 0, 0, NULL, x, y, c, BOX_LINES_DEFAULT, NULL,
|
||||||
if (menu_display(menu, 0, 0, NULL, x, y, c, lines, NULL, NULL, NULL,
|
NULL, NULL, NULL, mode_tree_menu_callback, mtm) != 0) {
|
||||||
NULL, mode_tree_menu_callback, mtm) != 0) {
|
|
||||||
mode_tree_remove_ref(mtd);
|
mode_tree_remove_ref(mtd);
|
||||||
free(mtm);
|
free(mtm);
|
||||||
menu_free(menu);
|
menu_free(menu);
|
||||||
|
|||||||
+31
-5
@@ -1,4 +1,4 @@
|
|||||||
/* $OpenBSD: options-table.c,v 1.247 2026/09/21 10:22:31 nicm Exp $ */
|
/* $OpenBSD: options-table.c,v 1.245 2026/09/10 11:02:18 nicm Exp $ */
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2011 Nicholas Marriott <nicholas.marriott@gmail.com>
|
* Copyright (c) 2011 Nicholas Marriott <nicholas.marriott@gmail.com>
|
||||||
@@ -76,10 +76,9 @@ static const char *options_table_pane_border_indicators_list[] = {
|
|||||||
"off", "colour", "arrows", "both", NULL
|
"off", "colour", "arrows", "both", NULL
|
||||||
};
|
};
|
||||||
static const char *options_table_pane_border_lines_list[] = {
|
static const char *options_table_pane_border_lines_list[] = {
|
||||||
"single", "double", "heavy", "simple", "number", "spaces", "none",
|
"single", "double", "heavy", "simple", "number", "spaces", "none", NULL
|
||||||
"rounded", NULL
|
|
||||||
};
|
};
|
||||||
static const char *options_table_menu_border_lines_list[] = {
|
static const char *options_table_popup_border_lines_list[] = {
|
||||||
"single", "double", "heavy", "simple", "rounded", "padded", "none", NULL
|
"single", "double", "heavy", "simple", "rounded", "padded", "none", NULL
|
||||||
};
|
};
|
||||||
static const char *options_table_set_clipboard_list[] = {
|
static const char *options_table_set_clipboard_list[] = {
|
||||||
@@ -493,7 +492,7 @@ const struct options_table_entry options_table[] = {
|
|||||||
{ .name = "menu-border-lines",
|
{ .name = "menu-border-lines",
|
||||||
.type = OPTIONS_TABLE_CHOICE,
|
.type = OPTIONS_TABLE_CHOICE,
|
||||||
.scope = OPTIONS_TABLE_WINDOW,
|
.scope = OPTIONS_TABLE_WINDOW,
|
||||||
.choices = options_table_menu_border_lines_list,
|
.choices = options_table_popup_border_lines_list,
|
||||||
.default_num = BOX_LINES_SINGLE,
|
.default_num = BOX_LINES_SINGLE,
|
||||||
.text = "Type of characters used to draw menu border lines. Some of "
|
.text = "Type of characters used to draw menu border lines. Some of "
|
||||||
"these are only supported on terminals with UTF-8 support."
|
"these are only supported on terminals with UTF-8 support."
|
||||||
@@ -1666,6 +1665,33 @@ const struct options_table_entry options_table[] = {
|
|||||||
.text = "Pane scrollbar position."
|
.text = "Pane scrollbar position."
|
||||||
},
|
},
|
||||||
|
|
||||||
|
{ .name = "popup-style",
|
||||||
|
.type = OPTIONS_TABLE_STRING,
|
||||||
|
.scope = OPTIONS_TABLE_WINDOW,
|
||||||
|
.default_str = "bg=themedarkgrey,fg=themewhite",
|
||||||
|
.flags = OPTIONS_TABLE_IS_STYLE,
|
||||||
|
.separator = ",",
|
||||||
|
.text = "Default style of popups."
|
||||||
|
},
|
||||||
|
|
||||||
|
{ .name = "popup-border-style",
|
||||||
|
.type = OPTIONS_TABLE_STRING,
|
||||||
|
.scope = OPTIONS_TABLE_WINDOW,
|
||||||
|
.default_str = "bg=themedarkgrey,fg=themelightgrey",
|
||||||
|
.flags = OPTIONS_TABLE_IS_STYLE,
|
||||||
|
.separator = ",",
|
||||||
|
.text = "Default style of popup borders."
|
||||||
|
},
|
||||||
|
|
||||||
|
{ .name = "popup-border-lines",
|
||||||
|
.type = OPTIONS_TABLE_CHOICE,
|
||||||
|
.scope = OPTIONS_TABLE_WINDOW,
|
||||||
|
.choices = options_table_popup_border_lines_list,
|
||||||
|
.default_num = BOX_LINES_SINGLE,
|
||||||
|
.text = "Type of characters used to draw popup border lines. Some of "
|
||||||
|
"these are only supported on terminals with UTF-8 support."
|
||||||
|
},
|
||||||
|
|
||||||
{ .name = "remain-on-exit",
|
{ .name = "remain-on-exit",
|
||||||
.type = OPTIONS_TABLE_CHOICE,
|
.type = OPTIONS_TABLE_CHOICE,
|
||||||
.scope = OPTIONS_TABLE_WINDOW|OPTIONS_TABLE_PANE,
|
.scope = OPTIONS_TABLE_WINDOW|OPTIONS_TABLE_PANE,
|
||||||
|
|||||||
@@ -0,0 +1,661 @@
|
|||||||
|
/* $OpenBSD: popup.c,v 1.77 2026/08/28 08:02:16 nicm Exp $ */
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2020 Nicholas Marriott <nicholas.marriott@gmail.com>
|
||||||
|
*
|
||||||
|
* Permission to use, copy, modify, and distribute this software for any
|
||||||
|
* purpose with or without fee is hereby granted, provided that the above
|
||||||
|
* copyright notice and this permission notice appear in all copies.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||||
|
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||||
|
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||||
|
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||||
|
* WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER
|
||||||
|
* IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <sys/wait.h>
|
||||||
|
|
||||||
|
#include <signal.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
#include "tmux.h"
|
||||||
|
|
||||||
|
struct popup_data {
|
||||||
|
struct client *c;
|
||||||
|
struct cmdq_item *item;
|
||||||
|
int flags;
|
||||||
|
char *title;
|
||||||
|
|
||||||
|
char *style;
|
||||||
|
char *border_style;
|
||||||
|
struct grid_cell border_cell;
|
||||||
|
enum box_lines border_lines;
|
||||||
|
|
||||||
|
struct screen s;
|
||||||
|
struct grid_cell defaults;
|
||||||
|
struct colour_palette palette;
|
||||||
|
|
||||||
|
struct visible_ranges r;
|
||||||
|
|
||||||
|
struct job *job;
|
||||||
|
struct input_ctx *ictx;
|
||||||
|
int status;
|
||||||
|
popup_close_cb cb;
|
||||||
|
void *arg;
|
||||||
|
|
||||||
|
int close;
|
||||||
|
|
||||||
|
/* Current position and size. */
|
||||||
|
u_int px;
|
||||||
|
u_int py;
|
||||||
|
u_int sx;
|
||||||
|
u_int sy;
|
||||||
|
|
||||||
|
/* Preferred position and size. */
|
||||||
|
u_int ppx;
|
||||||
|
u_int ppy;
|
||||||
|
u_int psx;
|
||||||
|
u_int psy;
|
||||||
|
|
||||||
|
enum { OFF, MOVE, SIZE } dragging;
|
||||||
|
u_int dx;
|
||||||
|
u_int dy;
|
||||||
|
|
||||||
|
u_int lx;
|
||||||
|
u_int ly;
|
||||||
|
u_int lb;
|
||||||
|
};
|
||||||
|
|
||||||
|
static void
|
||||||
|
popup_free(struct popup_data *pd)
|
||||||
|
{
|
||||||
|
server_client_unref(pd->c);
|
||||||
|
|
||||||
|
if (pd->job != NULL)
|
||||||
|
job_free(pd->job);
|
||||||
|
if (pd->ictx != NULL)
|
||||||
|
input_free(pd->ictx);
|
||||||
|
|
||||||
|
free(pd->r.ranges);
|
||||||
|
screen_free(&pd->s);
|
||||||
|
colour_palette_free(&pd->palette);
|
||||||
|
|
||||||
|
free(pd->title);
|
||||||
|
free(pd->style);
|
||||||
|
free(pd->border_style);
|
||||||
|
free(pd);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
popup_reapply_styles(struct popup_data *pd)
|
||||||
|
{
|
||||||
|
struct client *c = pd->c;
|
||||||
|
struct session *s = c->session;
|
||||||
|
struct options *o;
|
||||||
|
struct format_tree *ft;
|
||||||
|
struct style sytmp;
|
||||||
|
|
||||||
|
if (s == NULL)
|
||||||
|
return;
|
||||||
|
o = s->curw->window->options;
|
||||||
|
|
||||||
|
ft = format_create_defaults(NULL, c, s, s->curw, NULL);
|
||||||
|
|
||||||
|
/* Reapply popup style from options. */
|
||||||
|
memcpy(&pd->defaults, &grid_default_cell, sizeof pd->defaults);
|
||||||
|
style_apply(&pd->defaults, o, "popup-style", ft);
|
||||||
|
if (pd->style != NULL) {
|
||||||
|
style_set(&sytmp, &grid_default_cell);
|
||||||
|
if (style_parse(&sytmp, &pd->defaults, pd->style) == 0) {
|
||||||
|
pd->defaults.fg = sytmp.gc.fg;
|
||||||
|
pd->defaults.bg = sytmp.gc.bg;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pd->defaults.attr = 0;
|
||||||
|
|
||||||
|
/* Reapply border style from options. */
|
||||||
|
memcpy(&pd->border_cell, &grid_default_cell, sizeof pd->border_cell);
|
||||||
|
style_apply(&pd->border_cell, o, "popup-border-style", ft);
|
||||||
|
if (pd->border_style != NULL) {
|
||||||
|
style_set(&sytmp, &grid_default_cell);
|
||||||
|
if (style_parse(&sytmp, &pd->border_cell,
|
||||||
|
pd->border_style) == 0) {
|
||||||
|
pd->border_cell.fg = sytmp.gc.fg;
|
||||||
|
pd->border_cell.bg = sytmp.gc.bg;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pd->border_cell.attr = 0;
|
||||||
|
|
||||||
|
format_free(ft);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
popup_redraw_cb(const struct tty_ctx *ttyctx)
|
||||||
|
{
|
||||||
|
struct popup_data *pd = ttyctx->arg;
|
||||||
|
|
||||||
|
pd->c->flags |= CLIENT_REDRAWOVERLAY;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
popup_set_client_cb(struct tty_ctx *ttyctx, struct client *c)
|
||||||
|
{
|
||||||
|
struct popup_data *pd = ttyctx->arg;
|
||||||
|
|
||||||
|
if (c != pd->c)
|
||||||
|
return (0);
|
||||||
|
if (pd->c->flags & CLIENT_REDRAWOVERLAY)
|
||||||
|
return (0);
|
||||||
|
|
||||||
|
ttyctx->wox = 0;
|
||||||
|
ttyctx->woy = 0;
|
||||||
|
ttyctx->wsx = c->tty.sx;
|
||||||
|
ttyctx->wsy = c->tty.sy;
|
||||||
|
|
||||||
|
if (pd->border_lines == BOX_LINES_NONE) {
|
||||||
|
ttyctx->xoff = ttyctx->rxoff = pd->px;
|
||||||
|
ttyctx->yoff = ttyctx->ryoff = pd->py;
|
||||||
|
} else {
|
||||||
|
ttyctx->xoff = ttyctx->rxoff = pd->px + 1;
|
||||||
|
ttyctx->yoff = ttyctx->ryoff = pd->py + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (1);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
popup_init_ctx_cb(struct screen_write_ctx *ctx, struct tty_ctx *ttyctx)
|
||||||
|
{
|
||||||
|
struct popup_data *pd = ctx->arg;
|
||||||
|
|
||||||
|
memcpy(&ttyctx->defaults, &pd->defaults, sizeof ttyctx->defaults);
|
||||||
|
ttyctx->flags &= ~TTY_CTX_WINDOW_BIGGER;
|
||||||
|
ttyctx->style_ctx.defaults = &ttyctx->defaults;
|
||||||
|
ttyctx->style_ctx.palette = &pd->palette;
|
||||||
|
ttyctx->redraw_cb = popup_redraw_cb;
|
||||||
|
ttyctx->set_client_cb = popup_set_client_cb;
|
||||||
|
ttyctx->arg = pd;
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct screen *
|
||||||
|
popup_mode_cb(__unused struct client *c, void *data, u_int *cx, u_int *cy)
|
||||||
|
{
|
||||||
|
struct popup_data *pd = data;
|
||||||
|
|
||||||
|
if (pd->border_lines == BOX_LINES_NONE) {
|
||||||
|
*cx = pd->px + pd->s.cx;
|
||||||
|
*cy = pd->py + pd->s.cy;
|
||||||
|
} else {
|
||||||
|
*cx = pd->px + 1 + pd->s.cx;
|
||||||
|
*cy = pd->py + 1 + pd->s.cy;
|
||||||
|
}
|
||||||
|
return (&pd->s);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Return parts of the input range which are not obstructed by the popup. */
|
||||||
|
static struct visible_ranges *
|
||||||
|
popup_check_cb(__unused struct client* c, void *data, u_int px, u_int py,
|
||||||
|
u_int nx)
|
||||||
|
{
|
||||||
|
struct popup_data *pd = data;
|
||||||
|
struct visible_ranges *r = &pd->r;
|
||||||
|
|
||||||
|
server_client_overlay_range(pd->px, pd->py, pd->sx, pd->sy, px, py, nx,
|
||||||
|
r);
|
||||||
|
return (r);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
popup_draw_cb(struct client *c, void *data)
|
||||||
|
{
|
||||||
|
struct popup_data *pd = data;
|
||||||
|
struct tty *tty = &c->tty;
|
||||||
|
struct screen s;
|
||||||
|
struct screen_write_ctx ctx;
|
||||||
|
u_int i, px = pd->px, py = pd->py;
|
||||||
|
struct grid_cell defaults;
|
||||||
|
struct tty_style_ctx style_ctx;
|
||||||
|
|
||||||
|
popup_reapply_styles(pd);
|
||||||
|
|
||||||
|
screen_init(&s, pd->sx, pd->sy, 0);
|
||||||
|
if (pd->s.hyperlinks != NULL) {
|
||||||
|
hyperlinks_free(s.hyperlinks);
|
||||||
|
s.hyperlinks = hyperlinks_copy(pd->s.hyperlinks);
|
||||||
|
}
|
||||||
|
screen_write_start(&ctx, &s);
|
||||||
|
screen_write_clearscreen(&ctx, 8);
|
||||||
|
|
||||||
|
if (pd->border_lines == BOX_LINES_NONE) {
|
||||||
|
screen_write_cursormove(&ctx, 0, 0, 0);
|
||||||
|
screen_write_fast_copy(&ctx, &pd->s, 0, 0, pd->sx, pd->sy);
|
||||||
|
} else if (pd->sx > 2 && pd->sy > 2) {
|
||||||
|
screen_write_box(&ctx, pd->sx, pd->sy, pd->border_lines,
|
||||||
|
&pd->border_cell, pd->title);
|
||||||
|
screen_write_cursormove(&ctx, 1, 1, 0);
|
||||||
|
screen_write_fast_copy(&ctx, &pd->s, 0, 0, pd->sx - 2,
|
||||||
|
pd->sy - 2);
|
||||||
|
}
|
||||||
|
screen_write_stop(&ctx);
|
||||||
|
|
||||||
|
memcpy(&defaults, &pd->defaults, sizeof defaults);
|
||||||
|
if (defaults.fg == 8)
|
||||||
|
defaults.fg = pd->palette.fg;
|
||||||
|
if (defaults.bg == 8)
|
||||||
|
defaults.bg = pd->palette.bg;
|
||||||
|
style_ctx.defaults = &defaults;
|
||||||
|
style_ctx.palette = &pd->palette;
|
||||||
|
style_ctx.dim = 0;
|
||||||
|
style_ctx.hyperlinks = s.hyperlinks;
|
||||||
|
|
||||||
|
c->overlay_check = NULL;
|
||||||
|
c->overlay_data = NULL;
|
||||||
|
for (i = 0; i < pd->sy; i++)
|
||||||
|
tty_draw_line(tty, &s, 0, i, pd->sx, px, py + i, &style_ctx);
|
||||||
|
screen_free(&s);
|
||||||
|
c->overlay_check = popup_check_cb;
|
||||||
|
c->overlay_data = pd;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
popup_free_cb(__unused struct client *c, void *data)
|
||||||
|
{
|
||||||
|
struct popup_data *pd = data;
|
||||||
|
struct cmdq_item *item = pd->item;
|
||||||
|
|
||||||
|
if (pd->cb != NULL)
|
||||||
|
pd->cb(pd->status, pd->arg);
|
||||||
|
|
||||||
|
if (item != NULL) {
|
||||||
|
if (cmdq_get_client(item) != NULL &&
|
||||||
|
cmdq_get_client(item)->session == NULL)
|
||||||
|
cmdq_get_client(item)->retval = pd->status;
|
||||||
|
cmdq_continue(item);
|
||||||
|
}
|
||||||
|
|
||||||
|
popup_free(pd);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
popup_resize_cb(__unused struct client *c, void *data)
|
||||||
|
{
|
||||||
|
struct popup_data *pd = data;
|
||||||
|
struct tty *tty = &c->tty;
|
||||||
|
|
||||||
|
if (pd == NULL)
|
||||||
|
return;
|
||||||
|
|
||||||
|
/* Adjust position and size. */
|
||||||
|
if (pd->psy > tty->sy)
|
||||||
|
pd->sy = tty->sy;
|
||||||
|
else
|
||||||
|
pd->sy = pd->psy;
|
||||||
|
if (pd->psx > tty->sx)
|
||||||
|
pd->sx = tty->sx;
|
||||||
|
else
|
||||||
|
pd->sx = pd->psx;
|
||||||
|
if (pd->ppy + pd->sy > tty->sy)
|
||||||
|
pd->py = tty->sy - pd->sy;
|
||||||
|
else
|
||||||
|
pd->py = pd->ppy;
|
||||||
|
if (pd->ppx + pd->sx > tty->sx)
|
||||||
|
pd->px = tty->sx - pd->sx;
|
||||||
|
else
|
||||||
|
pd->px = pd->ppx;
|
||||||
|
|
||||||
|
/* Avoid zero size screens. */
|
||||||
|
if (pd->border_lines == BOX_LINES_NONE) {
|
||||||
|
screen_resize(&pd->s, pd->sx, pd->sy, 0);
|
||||||
|
if (pd->job != NULL)
|
||||||
|
job_resize(pd->job, pd->sx, pd->sy );
|
||||||
|
} else if (pd->sx > 2 && pd->sy > 2) {
|
||||||
|
screen_resize(&pd->s, pd->sx - 2, pd->sy - 2, 0);
|
||||||
|
if (pd->job != NULL)
|
||||||
|
job_resize(pd->job, pd->sx - 2, pd->sy - 2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
popup_handle_drag(struct client *c, struct popup_data *pd,
|
||||||
|
struct mouse_event *m)
|
||||||
|
{
|
||||||
|
u_int px, py;
|
||||||
|
|
||||||
|
if (!MOUSE_DRAG(m->b))
|
||||||
|
pd->dragging = OFF;
|
||||||
|
else if (pd->dragging == MOVE) {
|
||||||
|
if (m->x < pd->dx)
|
||||||
|
px = 0;
|
||||||
|
else if (m->x - pd->dx + pd->sx > c->tty.sx)
|
||||||
|
px = c->tty.sx - pd->sx;
|
||||||
|
else
|
||||||
|
px = m->x - pd->dx;
|
||||||
|
if (m->y < pd->dy)
|
||||||
|
py = 0;
|
||||||
|
else if (m->y - pd->dy + pd->sy > c->tty.sy)
|
||||||
|
py = c->tty.sy - pd->sy;
|
||||||
|
else
|
||||||
|
py = m->y - pd->dy;
|
||||||
|
pd->px = px;
|
||||||
|
pd->py = py;
|
||||||
|
pd->dx = m->x - pd->px;
|
||||||
|
pd->dy = m->y - pd->py;
|
||||||
|
pd->ppx = px;
|
||||||
|
pd->ppy = py;
|
||||||
|
server_redraw_client(c);
|
||||||
|
} else if (pd->dragging == SIZE) {
|
||||||
|
if (pd->border_lines == BOX_LINES_NONE) {
|
||||||
|
if (m->x < pd->px + 1)
|
||||||
|
return;
|
||||||
|
if (m->y < pd->py + 1)
|
||||||
|
return;
|
||||||
|
} else {
|
||||||
|
if (m->x < pd->px + 3)
|
||||||
|
return;
|
||||||
|
if (m->y < pd->py + 3)
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
pd->sx = m->x - pd->px;
|
||||||
|
pd->sy = m->y - pd->py;
|
||||||
|
pd->psx = pd->sx;
|
||||||
|
pd->psy = pd->sy;
|
||||||
|
|
||||||
|
if (pd->border_lines == BOX_LINES_NONE) {
|
||||||
|
screen_resize(&pd->s, pd->sx, pd->sy, 0);
|
||||||
|
if (pd->job != NULL)
|
||||||
|
job_resize(pd->job, pd->sx, pd->sy);
|
||||||
|
} else {
|
||||||
|
screen_resize(&pd->s, pd->sx - 2, pd->sy - 2, 0);
|
||||||
|
if (pd->job != NULL)
|
||||||
|
job_resize(pd->job, pd->sx - 2, pd->sy - 2);
|
||||||
|
}
|
||||||
|
server_redraw_client(c);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
popup_key_cb(struct client *c, void *data, struct key_event *event)
|
||||||
|
{
|
||||||
|
struct popup_data *pd = data;
|
||||||
|
struct mouse_event *m = &event->m;
|
||||||
|
const char *buf;
|
||||||
|
size_t len;
|
||||||
|
u_int px, py;
|
||||||
|
enum { NONE, LEFT, RIGHT, TOP, BOTTOM } border = NONE;
|
||||||
|
|
||||||
|
if (KEYC_IS_MOUSE(event->key)) {
|
||||||
|
if (pd->dragging != OFF) {
|
||||||
|
popup_handle_drag(c, pd, m);
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
if (m->x < pd->px ||
|
||||||
|
m->x > pd->px + pd->sx - 1 ||
|
||||||
|
m->y < pd->py ||
|
||||||
|
m->y > pd->py + pd->sy - 1) {
|
||||||
|
return (0);
|
||||||
|
}
|
||||||
|
if (pd->border_lines != BOX_LINES_NONE) {
|
||||||
|
if (m->x == pd->px)
|
||||||
|
border = LEFT;
|
||||||
|
else if (m->x == pd->px + pd->sx - 1)
|
||||||
|
border = RIGHT;
|
||||||
|
else if (m->y == pd->py)
|
||||||
|
border = TOP;
|
||||||
|
else if (m->y == pd->py + pd->sy - 1)
|
||||||
|
border = BOTTOM;
|
||||||
|
}
|
||||||
|
if ((m->b & MOUSE_MASK_MODIFIERS) == 0 &&
|
||||||
|
MOUSE_BUTTONS(m->b) == MOUSE_BUTTON_3 &&
|
||||||
|
(border == LEFT || border == TOP))
|
||||||
|
goto out;
|
||||||
|
if (((m->b & MOUSE_MASK_MODIFIERS) == MOUSE_MASK_META) ||
|
||||||
|
(border != NONE && !MOUSE_DRAG(m->lb))) {
|
||||||
|
if (!MOUSE_DRAG(m->b))
|
||||||
|
goto out;
|
||||||
|
if (MOUSE_BUTTONS(m->lb) == MOUSE_BUTTON_1)
|
||||||
|
pd->dragging = MOVE;
|
||||||
|
else if (MOUSE_BUTTONS(m->lb) == MOUSE_BUTTON_3)
|
||||||
|
pd->dragging = SIZE;
|
||||||
|
pd->dx = m->lx - pd->px;
|
||||||
|
pd->dy = m->ly - pd->py;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ((((pd->flags & (POPUP_CLOSEEXIT|POPUP_CLOSEEXITZERO)) == 0) ||
|
||||||
|
pd->job == NULL) &&
|
||||||
|
(event->key == '\033' || event->key == ('c'|KEYC_CTRL)))
|
||||||
|
return (1);
|
||||||
|
if (pd->job == NULL && (pd->flags & POPUP_CLOSEANYKEY) &&
|
||||||
|
!KEYC_IS_MOUSE(event->key) && !KEYC_IS_PASTE(event->key))
|
||||||
|
return (1);
|
||||||
|
if (pd->job != NULL) {
|
||||||
|
if (KEYC_IS_MOUSE(event->key)) {
|
||||||
|
/* Must be inside, checked already. */
|
||||||
|
if (pd->border_lines == BOX_LINES_NONE) {
|
||||||
|
px = m->x - pd->px;
|
||||||
|
py = m->y - pd->py;
|
||||||
|
} else {
|
||||||
|
px = m->x - pd->px - 1;
|
||||||
|
py = m->y - pd->py - 1;
|
||||||
|
}
|
||||||
|
if (!input_key_get_mouse(&pd->s, m, px, py, &buf, &len))
|
||||||
|
return (0);
|
||||||
|
bufferevent_write(job_get_event(pd->job), buf, len);
|
||||||
|
return (0);
|
||||||
|
}
|
||||||
|
input_key(&pd->s, job_get_event(pd->job), event->key);
|
||||||
|
}
|
||||||
|
return (0);
|
||||||
|
|
||||||
|
out:
|
||||||
|
pd->lx = m->x;
|
||||||
|
pd->ly = m->y;
|
||||||
|
pd->lb = m->b;
|
||||||
|
return (0);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
popup_job_update_cb(struct job *job)
|
||||||
|
{
|
||||||
|
struct popup_data *pd = job_get_data(job);
|
||||||
|
struct evbuffer *evb = job_get_event(job)->input;
|
||||||
|
struct client *c = pd->c;
|
||||||
|
struct screen *s = &pd->s;
|
||||||
|
void *data = EVBUFFER_DATA(evb);
|
||||||
|
size_t size = EVBUFFER_LENGTH(evb);
|
||||||
|
|
||||||
|
if (size == 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
c->overlay_check = NULL;
|
||||||
|
c->overlay_data = NULL;
|
||||||
|
input_parse_screen(pd->ictx, s, popup_init_ctx_cb, pd, data, size);
|
||||||
|
c->overlay_check = popup_check_cb;
|
||||||
|
c->overlay_data = pd;
|
||||||
|
|
||||||
|
evbuffer_drain(evb, size);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
popup_job_complete_cb(struct job *job)
|
||||||
|
{
|
||||||
|
struct popup_data *pd = job_get_data(job);
|
||||||
|
int status;
|
||||||
|
|
||||||
|
status = job_get_status(pd->job);
|
||||||
|
if (WIFEXITED(status))
|
||||||
|
pd->status = WEXITSTATUS(status);
|
||||||
|
else if (WIFSIGNALED(status))
|
||||||
|
pd->status = WTERMSIG(status);
|
||||||
|
else
|
||||||
|
pd->status = 0;
|
||||||
|
pd->job = NULL;
|
||||||
|
|
||||||
|
if ((pd->flags & POPUP_CLOSEEXIT) ||
|
||||||
|
((pd->flags & POPUP_CLOSEEXITZERO) && pd->status == 0))
|
||||||
|
server_client_clear_overlay(pd->c);
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
popup_present(struct client *c)
|
||||||
|
{
|
||||||
|
return (c->overlay_draw == popup_draw_cb);
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
popup_modify(struct client *c, const char *title, const char *style,
|
||||||
|
const char *border_style, enum box_lines lines, int flags)
|
||||||
|
{
|
||||||
|
struct popup_data *pd = c->overlay_data;
|
||||||
|
struct style sytmp;
|
||||||
|
|
||||||
|
if (title != NULL) {
|
||||||
|
if (pd->title != NULL)
|
||||||
|
free(pd->title);
|
||||||
|
pd->title = xstrdup(title);
|
||||||
|
}
|
||||||
|
if (border_style != NULL) {
|
||||||
|
free(pd->border_style);
|
||||||
|
pd->border_style = xstrdup(border_style);
|
||||||
|
style_set(&sytmp, &pd->border_cell);
|
||||||
|
if (style_parse(&sytmp, &pd->border_cell, border_style) == 0) {
|
||||||
|
pd->border_cell.fg = sytmp.gc.fg;
|
||||||
|
pd->border_cell.bg = sytmp.gc.bg;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (style != NULL) {
|
||||||
|
free(pd->style);
|
||||||
|
pd->style = xstrdup(style);
|
||||||
|
style_set(&sytmp, &pd->defaults);
|
||||||
|
if (style_parse(&sytmp, &pd->defaults, style) == 0) {
|
||||||
|
pd->defaults.fg = sytmp.gc.fg;
|
||||||
|
pd->defaults.bg = sytmp.gc.bg;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (lines != BOX_LINES_DEFAULT) {
|
||||||
|
if (lines == BOX_LINES_NONE && pd->border_lines != lines) {
|
||||||
|
screen_resize(&pd->s, pd->sx, pd->sy, 1);
|
||||||
|
job_resize(pd->job, pd->sx, pd->sy);
|
||||||
|
} else if (pd->border_lines == BOX_LINES_NONE &&
|
||||||
|
pd->border_lines != lines) {
|
||||||
|
screen_resize(&pd->s, pd->sx - 2, pd->sy - 2, 1);
|
||||||
|
job_resize(pd->job, pd->sx - 2, pd->sy - 2);
|
||||||
|
}
|
||||||
|
pd->border_lines = lines;
|
||||||
|
tty_resize(&c->tty);
|
||||||
|
}
|
||||||
|
if (flags != -1)
|
||||||
|
pd->flags = flags;
|
||||||
|
|
||||||
|
server_redraw_client(c);
|
||||||
|
return (0);
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
popup_display(int flags, enum box_lines lines, struct cmdq_item *item, u_int px,
|
||||||
|
u_int py, u_int sx, u_int sy, struct environ *env, const char *shellcmd,
|
||||||
|
int argc, char **argv, const char *cwd, const char *title, struct client *c,
|
||||||
|
struct session *s, const char *style, const char *border_style,
|
||||||
|
popup_close_cb cb, void *arg)
|
||||||
|
{
|
||||||
|
struct popup_data *pd;
|
||||||
|
u_int jx, jy;
|
||||||
|
struct options *o;
|
||||||
|
struct style sytmp;
|
||||||
|
|
||||||
|
if (s != NULL)
|
||||||
|
o = s->curw->window->options;
|
||||||
|
else
|
||||||
|
o = c->session->curw->window->options;
|
||||||
|
|
||||||
|
if (lines == BOX_LINES_DEFAULT)
|
||||||
|
lines = options_get_number(o, "popup-border-lines");
|
||||||
|
if (lines == BOX_LINES_NONE) {
|
||||||
|
if (sx < 1 || sy < 1)
|
||||||
|
return (-1);
|
||||||
|
jx = sx;
|
||||||
|
jy = sy;
|
||||||
|
} else {
|
||||||
|
if (sx < 3 || sy < 3)
|
||||||
|
return (-1);
|
||||||
|
jx = sx - 2;
|
||||||
|
jy = sy - 2;
|
||||||
|
}
|
||||||
|
if (c->tty.sx < sx || c->tty.sy < sy)
|
||||||
|
return (-1);
|
||||||
|
|
||||||
|
pd = xcalloc(1, sizeof *pd);
|
||||||
|
pd->item = item;
|
||||||
|
pd->flags = flags;
|
||||||
|
|
||||||
|
if (title != NULL)
|
||||||
|
pd->title = xstrdup(title);
|
||||||
|
if (style != NULL)
|
||||||
|
pd->style = xstrdup(style);
|
||||||
|
if (border_style != NULL)
|
||||||
|
pd->border_style = xstrdup(border_style);
|
||||||
|
|
||||||
|
pd->c = c;
|
||||||
|
pd->c->references++;
|
||||||
|
|
||||||
|
pd->cb = cb;
|
||||||
|
pd->arg = arg;
|
||||||
|
pd->status = 128 + SIGHUP;
|
||||||
|
|
||||||
|
pd->border_lines = lines;
|
||||||
|
memcpy(&pd->border_cell, &grid_default_cell, sizeof pd->border_cell);
|
||||||
|
style_apply(&pd->border_cell, o, "popup-border-style", NULL);
|
||||||
|
if (border_style != NULL) {
|
||||||
|
style_set(&sytmp, &grid_default_cell);
|
||||||
|
if (style_parse(&sytmp, &pd->border_cell, border_style) == 0) {
|
||||||
|
pd->border_cell.fg = sytmp.gc.fg;
|
||||||
|
pd->border_cell.bg = sytmp.gc.bg;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pd->border_cell.attr = 0;
|
||||||
|
|
||||||
|
screen_init(&pd->s, jx, jy, 0);
|
||||||
|
screen_set_default_cursor(&pd->s, global_w_options);
|
||||||
|
colour_palette_init(&pd->palette);
|
||||||
|
colour_palette_from_option(&pd->palette, global_w_options);
|
||||||
|
|
||||||
|
memcpy(&pd->defaults, &grid_default_cell, sizeof pd->defaults);
|
||||||
|
style_apply(&pd->defaults, o, "popup-style", NULL);
|
||||||
|
if (style != NULL) {
|
||||||
|
style_set(&sytmp, &grid_default_cell);
|
||||||
|
if (style_parse(&sytmp, &pd->defaults, style) == 0) {
|
||||||
|
pd->defaults.fg = sytmp.gc.fg;
|
||||||
|
pd->defaults.bg = sytmp.gc.bg;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pd->defaults.attr = 0;
|
||||||
|
|
||||||
|
pd->px = px;
|
||||||
|
pd->py = py;
|
||||||
|
pd->sx = sx;
|
||||||
|
pd->sy = sy;
|
||||||
|
|
||||||
|
pd->ppx = px;
|
||||||
|
pd->ppy = py;
|
||||||
|
pd->psx = sx;
|
||||||
|
pd->psy = sy;
|
||||||
|
|
||||||
|
pd->job = job_run(shellcmd, argc, argv, env, s, cwd,
|
||||||
|
popup_job_update_cb, popup_job_complete_cb, NULL, pd,
|
||||||
|
JOB_NOWAIT|JOB_PTY|JOB_KEEPWRITE|JOB_DEFAULTSHELL, jx, jy);
|
||||||
|
if (pd->job == NULL) {
|
||||||
|
popup_free(pd);
|
||||||
|
return (-1);
|
||||||
|
}
|
||||||
|
pd->ictx = input_init(NULL, job_get_event(pd->job), &pd->palette, c);
|
||||||
|
|
||||||
|
server_client_set_overlay(c, 0, popup_check_cb, popup_mode_cb,
|
||||||
|
popup_draw_cb, popup_key_cb, popup_free_cb, popup_resize_cb, pd);
|
||||||
|
return (0);
|
||||||
|
}
|
||||||
@@ -1,7 +1,6 @@
|
|||||||
#!/bin/sh
|
#!/bin/sh
|
||||||
|
|
||||||
# Popups require an attached terminal and cannot be displayed by a control
|
# Popups require a tty overlay and cannot be displayed by a control client.
|
||||||
# client.
|
|
||||||
# A popup command from control mode must be ignored cleanly, leaving the
|
# A popup command from control mode must be ignored cleanly, leaving the
|
||||||
# client command queue and server usable.
|
# client command queue and server usable.
|
||||||
|
|
||||||
|
|||||||
@@ -1,63 +0,0 @@
|
|||||||
#!/bin/sh
|
|
||||||
|
|
||||||
# Trimming a blank source must leave a visible line for cursor reflow.
|
|
||||||
|
|
||||||
PATH=/bin:/usr/bin
|
|
||||||
TERM=screen
|
|
||||||
export PATH TERM
|
|
||||||
|
|
||||||
[ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux)
|
|
||||||
TMUX="$TEST_TMUX -LtestA$$ -f/dev/null"
|
|
||||||
|
|
||||||
fail()
|
|
||||||
{
|
|
||||||
echo "$*" >&2
|
|
||||||
exit 1
|
|
||||||
}
|
|
||||||
|
|
||||||
cleanup()
|
|
||||||
{
|
|
||||||
$TMUX kill-server 2>/dev/null
|
|
||||||
}
|
|
||||||
trap cleanup 0
|
|
||||||
trap 'exit 1' 1 2 3 15
|
|
||||||
|
|
||||||
$TMUX new-session -d -x80 -y24 -s test 'sleep 100' || exit 1
|
|
||||||
$TMUX set-option -g window-size manual || exit 1
|
|
||||||
$TMUX new-window -t test:1 'sleep 100' || exit 1
|
|
||||||
$TMUX resize-window -t test:0 -x20 -y24 || exit 1
|
|
||||||
|
|
||||||
# Exercise widening, narrowing, and equal widths with no source history.
|
|
||||||
for pair in '0 1' '1 0' '1 2'; do
|
|
||||||
set -- $pair
|
|
||||||
if [ "$2" = 2 ]; then
|
|
||||||
$TMUX new-window -t test:2 'sleep 100' || exit 1
|
|
||||||
$TMUX resize-window -t test:2 -x80 -y24 || exit 1
|
|
||||||
fi
|
|
||||||
source=test:$1.0
|
|
||||||
target=test:$2.0
|
|
||||||
[ "$($TMUX display-message -p -t "$source" '#{history_size}')" = 0 ] ||
|
|
||||||
fail "source has unexpected history"
|
|
||||||
$TMUX copy-mode -s "$source" -t "$target" || exit 1
|
|
||||||
$TMUX has-session -t test || fail "server died copying blank source"
|
|
||||||
[ "$($TMUX display-message -p -t "$target" \
|
|
||||||
'#{pane_in_mode} #{copy_cursor_x} #{copy_cursor_y}')" = '1 0 0' ] ||
|
|
||||||
fail "blank source did not enter copy mode at the first cell"
|
|
||||||
$TMUX send-keys -t "$target" -X cancel || exit 1
|
|
||||||
done
|
|
||||||
|
|
||||||
# Real content still trims trailing empty lines, clamping the cursor to beta.
|
|
||||||
$TMUX respawn-pane -k -t test:0.0 \
|
|
||||||
"printf 'alpha\r\nbeta\r\n\r\n\r\n'; exec sleep 100" || exit 1
|
|
||||||
i=0
|
|
||||||
while [ "$($TMUX display-message -p -t test:0.0 '#{cursor_y}')" != 4 ]; do
|
|
||||||
i=$((i + 1))
|
|
||||||
[ "$i" -lt 10 ] || fail "source output did not arrive"
|
|
||||||
sleep 1
|
|
||||||
done
|
|
||||||
$TMUX copy-mode -s test:0.0 -t test:1.0 || exit 1
|
|
||||||
[ "$($TMUX display-message -p -t test:1.0 \
|
|
||||||
'#{pane_in_mode} #{copy_cursor_y} #{copy_cursor_line}')" = '1 1 beta' ] ||
|
|
||||||
fail "nonblank source content or trailing-line trimming changed"
|
|
||||||
|
|
||||||
exit 0
|
|
||||||
@@ -400,107 +400,6 @@ $TMUX2 send-keys -t "$OUTER" a
|
|||||||
sleep 1
|
sleep 1
|
||||||
must_equal "$(fmt modal:0 '#{window_modal_pane}')" ''
|
must_equal "$(fmt modal:0 '#{window_modal_pane}')" ''
|
||||||
|
|
||||||
$TMUX bind P display-popup -E -t "$p0" -w 20 -h 5 -T popup-title 'cat'
|
|
||||||
$TMUX2 send-keys -t "$OUTER" C-b P
|
|
||||||
sleep 1
|
|
||||||
modal=$(fmt modal:0 '#{window_modal_pane}')
|
|
||||||
[ -n "$modal" ] || fail "display-popup did not create a modal pane"
|
|
||||||
must_equal "$(fmt "$modal" '#{pane_title}')" popup-title
|
|
||||||
must_equal "$($TMUX show-options -pv -t "$modal" pane-border-status)" top
|
|
||||||
must_equal "$($TMUX show-options -pv -t "$modal" pane-border-format)" \
|
|
||||||
'#{pane_title}'
|
|
||||||
$TMUX2 send-keys -t "$OUTER" C-b x z Enter
|
|
||||||
sleep 1
|
|
||||||
must_equal "$($TMUX show -gv @modal-prefix)" no
|
|
||||||
must_equal "$($TMUX show -gv @modal-root)" no
|
|
||||||
case "$($TMUX capture-pane -pt "$modal")" in
|
|
||||||
*xz*) ;;
|
|
||||||
*) fail "keys did not reach display-popup pane" ;;
|
|
||||||
esac
|
|
||||||
$TMUX2 send-keys -t "$OUTER" Escape
|
|
||||||
sleep 1
|
|
||||||
must_equal "$(fmt modal:0 '#{window_modal_pane}')" "$modal"
|
|
||||||
$TMUX2 send-keys -t "$OUTER" C-c
|
|
||||||
sleep 1
|
|
||||||
must_equal "$(fmt modal:0 '#{window_modal_pane}')" ''
|
|
||||||
|
|
||||||
# Creating a popup pane must not fire the split-window hook.
|
|
||||||
check_ok set-hook -t modal after-split-window \
|
|
||||||
"set-option -g @popup-after-split yes"
|
|
||||||
check_ok set-option -g @popup-after-split no
|
|
||||||
check_ok display-popup -E -t "$p0" true
|
|
||||||
must_equal "$($TMUX show-option -gv @popup-after-split)" no
|
|
||||||
check_ok set-hook -u -t modal after-split-window
|
|
||||||
|
|
||||||
# A borderless popup must not create a zero-sized pane in a tiny window.
|
|
||||||
check_ok new-window -d -t modal: -n popup-small 'cat'
|
|
||||||
check_ok set-option -w -t modal:popup-small window-size manual
|
|
||||||
check_ok resize-window -t modal:popup-small -x 1 -y 1
|
|
||||||
small=$(fmt modal:popup-small '#{pane_id}')
|
|
||||||
check_ok bind Z display-popup -B -t "$small" 'cat'
|
|
||||||
$TMUX2 send-keys -t "$OUTER" C-b Z
|
|
||||||
sleep 1
|
|
||||||
must_equal "$(fmt modal:popup-small '#{window_panes}')" 1
|
|
||||||
must_equal "$(fmt modal:popup-small '#{window_modal_pane}')" ''
|
|
||||||
|
|
||||||
$TMUX bind D display-popup -t "$p0" -w 20 -h 5 'printf done'
|
|
||||||
$TMUX2 send-keys -t "$OUTER" C-b D
|
|
||||||
sleep 2
|
|
||||||
modal=$(fmt modal:0 '#{window_modal_pane}')
|
|
||||||
[ -n "$modal" ] || fail "retained display-popup was not created"
|
|
||||||
must_equal "$(fmt "$modal" '#{pane_dead}')" 1
|
|
||||||
case "$($TMUX capture-pane -pt "$modal")" in
|
|
||||||
*'Pane is dead'*) fail "display-popup showed remain-on-exit message" ;;
|
|
||||||
esac
|
|
||||||
$TMUX2 send-keys -t "$OUTER" a
|
|
||||||
sleep 1
|
|
||||||
must_equal "$(fmt modal:0 '#{window_modal_pane}')" "$modal"
|
|
||||||
$TMUX2 send-keys -t "$OUTER" Escape
|
|
||||||
sleep 1
|
|
||||||
must_equal "$(fmt modal:0 '#{window_modal_pane}')" ''
|
|
||||||
|
|
||||||
$TMUX bind K display-popup -k -t "$p0" -w 20 -h 5 'printf done'
|
|
||||||
$TMUX2 send-keys -t "$OUTER" C-b K
|
|
||||||
sleep 2
|
|
||||||
modal=$(fmt modal:0 '#{window_modal_pane}')
|
|
||||||
[ -n "$modal" ] || fail "display-popup -k was not created"
|
|
||||||
must_equal "$(fmt "$modal" '#{pane_dead}')" 1
|
|
||||||
$TMUX2 send-keys -t "$OUTER" a
|
|
||||||
sleep 1
|
|
||||||
must_equal "$(fmt modal:0 '#{window_modal_pane}')" ''
|
|
||||||
|
|
||||||
$TMUX bind F display-popup -EE -t "$p0" -w 20 -h 5 'exit 1'
|
|
||||||
$TMUX2 send-keys -t "$OUTER" C-b F
|
|
||||||
sleep 2
|
|
||||||
modal=$(fmt modal:0 '#{window_modal_pane}')
|
|
||||||
[ -n "$modal" ] || fail "failed display-popup -EE did not remain"
|
|
||||||
must_equal "$(fmt "$modal" '#{pane_dead}')" 1
|
|
||||||
$TMUX2 send-keys -t "$OUTER" a
|
|
||||||
sleep 1
|
|
||||||
must_equal "$(fmt modal:0 '#{window_modal_pane}')" "$modal"
|
|
||||||
$TMUX2 send-keys -t "$OUTER" Escape
|
|
||||||
sleep 1
|
|
||||||
must_equal "$(fmt modal:0 '#{window_modal_pane}')" "$modal"
|
|
||||||
check_ok kill-pane -t "$modal"
|
|
||||||
sleep 1
|
|
||||||
|
|
||||||
check_ok display-popup -EE -t "$p0" true
|
|
||||||
must_equal "$(fmt modal:0 '#{window_modal_pane}')" ''
|
|
||||||
|
|
||||||
$TMUX bind G display-popup -EE -k -t "$p0" -w 20 -h 5 'exit 1'
|
|
||||||
$TMUX2 send-keys -t "$OUTER" C-b G
|
|
||||||
sleep 2
|
|
||||||
modal=$(fmt modal:0 '#{window_modal_pane}')
|
|
||||||
[ -n "$modal" ] || fail "failed display-popup -EE -k did not remain"
|
|
||||||
must_equal "$(fmt "$modal" '#{pane_dead}')" 1
|
|
||||||
must_equal "$($TMUX show-options -pv -t "$modal" remain-on-exit)" failed-key
|
|
||||||
$TMUX2 send-keys -t "$OUTER" a
|
|
||||||
sleep 1
|
|
||||||
must_equal "$(fmt modal:0 '#{window_modal_pane}')" ''
|
|
||||||
|
|
||||||
check_ok display-popup -EE -k -t "$p0" true
|
|
||||||
must_equal "$(fmt modal:0 '#{window_modal_pane}')" ''
|
|
||||||
|
|
||||||
# A nonmodal floating pane may remain above zoom, and switching between it and
|
# A nonmodal floating pane may remain above zoom, and switching between it and
|
||||||
# the zoomed tiled pane must not unzoom the window.
|
# the zoomed tiled pane must not unzoom the window.
|
||||||
check_ok new-window -d -t modal: -n float-over-zoom 'cat'
|
check_ok new-window -d -t modal: -n float-over-zoom 'cat'
|
||||||
|
|||||||
@@ -1,71 +0,0 @@
|
|||||||
#!/bin/sh
|
|
||||||
|
|
||||||
# Chooser menus are centred on the mouse using their displayed width. A
|
|
||||||
# hidden title must not move a borderless menu away from the mouse.
|
|
||||||
|
|
||||||
PATH=/bin:/usr/bin
|
|
||||||
TERM=screen
|
|
||||||
LC_ALL=C.UTF-8
|
|
||||||
export TERM LC_ALL
|
|
||||||
|
|
||||||
[ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux)
|
|
||||||
TMUX="$TEST_TMUX -LtestA$$ -f/dev/null"
|
|
||||||
TMUX2="$TEST_TMUX -LtestB$$ -f/dev/null"
|
|
||||||
|
|
||||||
cleanup()
|
|
||||||
{
|
|
||||||
$TMUX kill-server >/dev/null 2>&1
|
|
||||||
$TMUX2 kill-server >/dev/null 2>&1
|
|
||||||
}
|
|
||||||
trap cleanup 0 1 15
|
|
||||||
|
|
||||||
fail()
|
|
||||||
{
|
|
||||||
echo "$*" >&2
|
|
||||||
exit 1
|
|
||||||
}
|
|
||||||
|
|
||||||
$TMUX new-session -d -s short -x 100 -y 30 'sleep 100' || exit 1
|
|
||||||
$TMUX set -g mouse on || exit 1
|
|
||||||
$TMUX set -g status off || exit 1
|
|
||||||
$TMUX2 new-session -d -x 100 -y 30 "$TMUX attach" || exit 1
|
|
||||||
sleep 1
|
|
||||||
|
|
||||||
for name in short aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa; do
|
|
||||||
$TMUX rename-session "$name" || exit 1
|
|
||||||
for lines in none single padded; do
|
|
||||||
$TMUX set -g menu-border-lines "$lines" || exit 1
|
|
||||||
$TMUX choose-tree -s || exit 1
|
|
||||||
sleep 1
|
|
||||||
|
|
||||||
# Right-button press at window (60, 0), away from either edge.
|
|
||||||
seq=$(printf '\033[<2;61;1M')
|
|
||||||
$TMUX2 send-keys -l "$seq" || exit 1
|
|
||||||
sleep 1
|
|
||||||
|
|
||||||
row=2
|
|
||||||
column=54
|
|
||||||
if [ "$lines" = none ]; then
|
|
||||||
row=1
|
|
||||||
elif [ "$name" != short ]; then
|
|
||||||
column=31
|
|
||||||
fi
|
|
||||||
text=$($TMUX2 capture-pane -p | awk -v row="$row" \
|
|
||||||
-v column="$column" 'NR == row {
|
|
||||||
# Keep byte offsets equal to columns with non-Unicode awk.
|
|
||||||
gsub(/│/, "|")
|
|
||||||
print substr($0, column, 7)
|
|
||||||
}')
|
|
||||||
[ "$text" = 'Select ' ] || \
|
|
||||||
fail "$lines menu for $name: expected Select at ($column, $row), got '$text'"
|
|
||||||
|
|
||||||
# Close the menu, release the button, then leave the chooser.
|
|
||||||
$TMUX2 send-keys Escape || exit 1
|
|
||||||
seq=$(printf '\033[<2;61;1m')
|
|
||||||
$TMUX2 send-keys -l "$seq" || exit 1
|
|
||||||
$TMUX2 send-keys q || exit 1
|
|
||||||
sleep 1
|
|
||||||
done
|
|
||||||
done
|
|
||||||
|
|
||||||
exit 0
|
|
||||||
@@ -1,168 +0,0 @@
|
|||||||
#!/bin/sh
|
|
||||||
|
|
||||||
# Tests that the scroll position of a mode tree list (mode-tree.c) is kept
|
|
||||||
# when the list is rebuilt, using choose-buffer. Buffer mode rebuilds its list
|
|
||||||
# every time the status line is redrawn, which used to move the view so the
|
|
||||||
# selected line was on the bottom row (or the list jumped back to the top)
|
|
||||||
# even if the selected line was already visible. Also checks that when the
|
|
||||||
# list shrinks the view does not leave empty lines at the bottom.
|
|
||||||
#
|
|
||||||
# The list is drawn on a mode screen which capture-pane does not show, so - as
|
|
||||||
# in choose-buffer.sh - a second server provides a client: an inner "tmux
|
|
||||||
# attach" runs inside a pane of the second server, and that pane is captured
|
|
||||||
# to read what the inner client rendered.
|
|
||||||
|
|
||||||
PATH=/bin:/usr/bin
|
|
||||||
TERM=screen
|
|
||||||
|
|
||||||
[ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux)
|
|
||||||
TMP=$(mktemp -d) || exit 1
|
|
||||||
TMUX_TMPDIR="$TMP"
|
|
||||||
export TMUX_TMPDIR
|
|
||||||
TMUX="$TEST_TMUX -LtestA$$ -f/dev/null"
|
|
||||||
TMUX2="$TEST_TMUX -LtestB$$ -f/dev/null"
|
|
||||||
|
|
||||||
cleanup()
|
|
||||||
{
|
|
||||||
$TMUX kill-server 2>/dev/null
|
|
||||||
$TMUX2 kill-server 2>/dev/null
|
|
||||||
rm -rf "$TMP"
|
|
||||||
}
|
|
||||||
trap cleanup EXIT
|
|
||||||
|
|
||||||
fail()
|
|
||||||
{
|
|
||||||
echo "$1" >&2
|
|
||||||
exit 1
|
|
||||||
}
|
|
||||||
|
|
||||||
# capture the screen rendered by the inner client
|
|
||||||
capture()
|
|
||||||
{
|
|
||||||
$TMUX2 capture-pane -p -t out:0 2>/dev/null
|
|
||||||
}
|
|
||||||
|
|
||||||
# wait_for $marker
|
|
||||||
#
|
|
||||||
# Wait (up to ~10s) until the rendered screen contains $marker. The matching
|
|
||||||
# capture is left in CAPTURED.
|
|
||||||
wait_for()
|
|
||||||
{
|
|
||||||
i=0
|
|
||||||
while [ "$i" -lt 50 ]; do
|
|
||||||
CAPTURED=$(capture)
|
|
||||||
if printf '%s\n' "$CAPTURED" | grep -F -q "$1"; then
|
|
||||||
return 0
|
|
||||||
fi
|
|
||||||
sleep 0.2
|
|
||||||
i=$((i + 1))
|
|
||||||
done
|
|
||||||
fail "timed out waiting for '$1'"
|
|
||||||
}
|
|
||||||
|
|
||||||
# wait_gone $marker
|
|
||||||
#
|
|
||||||
# Wait (up to ~10s) until the rendered screen no longer contains $marker. The
|
|
||||||
# matching capture is left in CAPTURED.
|
|
||||||
wait_gone()
|
|
||||||
{
|
|
||||||
i=0
|
|
||||||
while [ "$i" -lt 50 ]; do
|
|
||||||
CAPTURED=$(capture)
|
|
||||||
if ! printf '%s\n' "$CAPTURED" | grep -F -q "$1"; then
|
|
||||||
return 0
|
|
||||||
fi
|
|
||||||
sleep 0.2
|
|
||||||
i=$((i + 1))
|
|
||||||
done
|
|
||||||
fail "timed out waiting for '$1' to disappear"
|
|
||||||
}
|
|
||||||
|
|
||||||
# wait_clients $n
|
|
||||||
#
|
|
||||||
# Wait (up to ~10s) until the test server has exactly $n clients attached.
|
|
||||||
wait_clients()
|
|
||||||
{
|
|
||||||
i=0
|
|
||||||
while [ "$i" -lt 10 ]; do
|
|
||||||
c=$($TMUX list-clients -F x 2>/dev/null | grep -c x)
|
|
||||||
[ "$c" -eq "$1" ] && return 0
|
|
||||||
sleep 1
|
|
||||||
i=$((i + 1))
|
|
||||||
done
|
|
||||||
return 1
|
|
||||||
}
|
|
||||||
|
|
||||||
# first_line
|
|
||||||
#
|
|
||||||
# Print the name of the first buffer shown in CAPTURED.
|
|
||||||
first_line()
|
|
||||||
{
|
|
||||||
printf '%s\n' "$CAPTURED" | grep -F ': S1' | head -1 | \
|
|
||||||
sed 's/.*\(buf[0-9a-z]*\): S1.*/\1/'
|
|
||||||
}
|
|
||||||
|
|
||||||
$TMUX new-session -d -s aaa -x 80 -y 24 'cat' || exit 1
|
|
||||||
|
|
||||||
$TMUX2 new-session -d -s out -x 80 -y 24 "$TMUX attach -t aaa" || exit 1
|
|
||||||
wait_clients 1 || fail "no client attached to test server"
|
|
||||||
|
|
||||||
# Forty buffers, more than fit on the screen.
|
|
||||||
n=0
|
|
||||||
while [ "$n" -lt 40 ]; do
|
|
||||||
$TMUX set-buffer -b "$(printf 'buf%02d' "$n")" "buffer $n" || exit 1
|
|
||||||
n=$((n + 1))
|
|
||||||
done
|
|
||||||
|
|
||||||
# Without a preview the list has the whole pane (23 lines with the status
|
|
||||||
# line), so buf00 to buf22 are visible.
|
|
||||||
$TMUX choose-buffer -t aaa:0 -N -O name -F 'S1' || exit 1
|
|
||||||
wait_for 'buf00: S1'
|
|
||||||
|
|
||||||
# --- scrolling down moves the view -------------------------------------------
|
|
||||||
#
|
|
||||||
# Selecting buf30 scrolls so it is on the bottom row: buf08 is at the top.
|
|
||||||
$TMUX send-keys -t aaa:0 -N 30 Down || exit 1
|
|
||||||
wait_for 'buf30: S1'
|
|
||||||
top=$(first_line)
|
|
||||||
[ "$top" = "buf08" ] || fail "top line after scrolling is $top, not buf08"
|
|
||||||
|
|
||||||
# Moving up to buf20 stays inside the visible lines so the view does not move.
|
|
||||||
$TMUX send-keys -t aaa:0 -N 10 Up || exit 1
|
|
||||||
|
|
||||||
# --- a rebuild keeps the view where it is ------------------------------------
|
|
||||||
#
|
|
||||||
# Add a buffer that sorts into the visible part of the list and redraw the
|
|
||||||
# status line so the list is rebuilt. Once buf15a appears the rebuild has
|
|
||||||
# happened; buf08 must still be the top line. It used to jump back to buf00.
|
|
||||||
$TMUX set-buffer -b buf15a "new buffer" || exit 1
|
|
||||||
$TMUX refresh-client -S || exit 1
|
|
||||||
wait_for 'buf15a: S1'
|
|
||||||
top=$(first_line)
|
|
||||||
[ "$top" = "buf08" ] || fail "top line after rebuild is $top, not buf08"
|
|
||||||
|
|
||||||
# --- a shrinking list does not leave empty lines -----------------------------
|
|
||||||
#
|
|
||||||
# Go to the end of the list (buf39 is on the bottom row) and delete all but
|
|
||||||
# the last ten buffers. They now all fit, so the view goes to the top.
|
|
||||||
$TMUX send-keys -t aaa:0 End || exit 1
|
|
||||||
wait_for 'buf39: S1'
|
|
||||||
for b in $($TMUX list-buffers -F '#{buffer_name}'); do
|
|
||||||
case "$b" in
|
|
||||||
buf3?)
|
|
||||||
;;
|
|
||||||
*)
|
|
||||||
$TMUX delete-buffer -b "$b" || exit 1
|
|
||||||
;;
|
|
||||||
esac
|
|
||||||
done
|
|
||||||
$TMUX refresh-client -S || exit 1
|
|
||||||
wait_gone 'buf29: S1'
|
|
||||||
top=$(first_line)
|
|
||||||
[ "$top" = "buf30" ] || fail "top line after shrinking is $top, not buf30"
|
|
||||||
[ "$(printf '%s\n' "$CAPTURED" | grep -F -c ': S1')" -eq 10 ] || \
|
|
||||||
fail "expected 10 buffers after shrinking"
|
|
||||||
|
|
||||||
$TMUX send-keys -t aaa:0 q
|
|
||||||
|
|
||||||
exit 0
|
|
||||||
@@ -86,11 +86,6 @@ check_value "-gv status-keys" "vi"
|
|||||||
check_fail "unknown value: bogus" set -g status-keys bogus
|
check_fail "unknown value: bogus" set -g status-keys bogus
|
||||||
check_value "-gv status-keys" "vi"
|
check_value "-gv status-keys" "vi"
|
||||||
|
|
||||||
# pane-border-lines accepts rounded as a pane border style.
|
|
||||||
check_ok set -gw pane-border-lines rounded
|
|
||||||
check_value "-gwv pane-border-lines" "rounded"
|
|
||||||
check_ok set -gw pane-border-lines single
|
|
||||||
|
|
||||||
# --- flag options ---------------------------------------------------------
|
# --- flag options ---------------------------------------------------------
|
||||||
#
|
#
|
||||||
# focus-events is an on/off flag. Setting with no value toggles it; explicit
|
# focus-events is an on/off flag. Setting with no value toggles it; explicit
|
||||||
|
|||||||
@@ -107,12 +107,6 @@ $TMUX2 new-pane -x28 -y8 -X4 -Y1 -B double \
|
|||||||
"sh -c 'printf FLOAT; exec sleep 100'" || exit 1
|
"sh -c 'printf FLOAT; exec sleep 100'" || exit 1
|
||||||
compare floating-border-double
|
compare floating-border-double
|
||||||
|
|
||||||
# Larger floating pane with rounded border lines.
|
|
||||||
new_scene 40 12
|
|
||||||
$TMUX2 new-pane -x28 -y8 -X4 -Y1 -B rounded \
|
|
||||||
"sh -c 'printf FLOAT; exec sleep 100'" || exit 1
|
|
||||||
compare floating-border-rounded
|
|
||||||
|
|
||||||
# Floating pane with no border lines: redraw_mark_pane_borders returns early so
|
# Floating pane with no border lines: redraw_mark_pane_borders returns early so
|
||||||
# the float has no border at all, only its (clipped) content over the base pane.
|
# the float has no border at all, only its (clipped) content over the base pane.
|
||||||
new_scene 40 12
|
new_scene 40 12
|
||||||
|
|||||||
@@ -106,28 +106,9 @@ compare menu-over-split
|
|||||||
|
|
||||||
# Menu with no border lines.
|
# Menu with no border lines.
|
||||||
setup 40 14
|
setup 40 14
|
||||||
$TMUX2 display-menu -T "This title must not reserve width" -C 1 \
|
menu -b none -x6 -y8
|
||||||
-b none -x6 -y8 \
|
|
||||||
"Alpha item" a "" \
|
|
||||||
"Beta item" b "" \
|
|
||||||
"" "" "" \
|
|
||||||
"Gamma item" g "" || exit 1
|
|
||||||
sleep 1
|
|
||||||
compare menu-noborder
|
compare menu-noborder
|
||||||
|
|
||||||
# Menu border style follows the explicitly targeted window, not the current
|
|
||||||
# window when a different pane is selected.
|
|
||||||
setup 40 14
|
|
||||||
$TMUX2 set menu-border-lines none || exit 1
|
|
||||||
$TMUX2 neww || exit 1
|
|
||||||
$TMUX2 display-menu -t%0 -T "Menu" -C 1 \
|
|
||||||
"Alpha item" a "" \
|
|
||||||
"Beta item" b "" \
|
|
||||||
"" "" "" \
|
|
||||||
"Gamma item" g "" || exit 1
|
|
||||||
$TMUX2 selectw -t%0 || exit 1
|
|
||||||
compare menu-target-window-noborder
|
|
||||||
|
|
||||||
# Menu with double border lines.
|
# Menu with double border lines.
|
||||||
setup 40 14
|
setup 40 14
|
||||||
menu -b double -x6 -y8
|
menu -b double -x6 -y8
|
||||||
|
|||||||
@@ -0,0 +1,101 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
# Exercise drawing of popups (display-popup) over the window scene. A popup is an
|
||||||
|
# overlay drawn on top of the redraw scene (the overlay_draw path in
|
||||||
|
# screen-redraw.c), so this guards against regressions in how popups appear.
|
||||||
|
#
|
||||||
|
# A popup is modal and stays open until its command exits, so each scene fully
|
||||||
|
# re-creates the servers and re-attaches; the popup is opened in the background
|
||||||
|
# (display-popup blocks the client that runs it) and the outer pane is captured
|
||||||
|
# while it is open.
|
||||||
|
#
|
||||||
|
# Run with GENERATE=1 to (re)create the golden files.
|
||||||
|
|
||||||
|
PATH=/bin:/usr/bin
|
||||||
|
TERM=screen
|
||||||
|
LC_ALL=C.UTF-8
|
||||||
|
export TERM LC_ALL
|
||||||
|
|
||||||
|
[ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux)
|
||||||
|
TMUX=
|
||||||
|
TMUX2=
|
||||||
|
SETUP=0
|
||||||
|
RESULTS=screen-redraw-results
|
||||||
|
|
||||||
|
TMP=$(mktemp)
|
||||||
|
|
||||||
|
cleanup() {
|
||||||
|
rm -f "$TMP"
|
||||||
|
[ -n "$TMUX" ] && $TMUX kill-server 2>/dev/null
|
||||||
|
[ -n "$TMUX2" ] && $TMUX2 kill-server 2>/dev/null
|
||||||
|
}
|
||||||
|
trap cleanup 0 1 15
|
||||||
|
|
||||||
|
fail() {
|
||||||
|
echo "$*" >&2
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
compare() {
|
||||||
|
sleep 1
|
||||||
|
$TMUX capturep -p >$TMP || exit 1
|
||||||
|
if [ -n "$GENERATE" ]; then
|
||||||
|
cp $TMP "$RESULTS/$1.result" || exit 1
|
||||||
|
echo "generated $1"
|
||||||
|
else
|
||||||
|
cmp -s $TMP "$RESULTS/$1.result" || \
|
||||||
|
fail "scene $1 differs from $RESULTS/$1.result"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
C="sh -c 'i=0; while [ \$i -lt 13 ]; do printf \"POP%02d abcdefghij\n\" \$i; i=\$((i + 1)); done; exec sleep 100'"
|
||||||
|
|
||||||
|
# setup: fresh inner window attached inside a fresh outer pane, 40x14.
|
||||||
|
setup() {
|
||||||
|
[ -n "$TMUX" ] && $TMUX kill-server 2>/dev/null
|
||||||
|
[ -n "$TMUX2" ] && $TMUX2 kill-server 2>/dev/null
|
||||||
|
SETUP=$((SETUP + 1))
|
||||||
|
TMUX="$TEST_TMUX -LtestA$$-$SETUP -f/dev/null"
|
||||||
|
TMUX2="$TEST_TMUX -LtestB$$-$SETUP -f/dev/null"
|
||||||
|
$TMUX2 new -d -x40 -y14 "$C" || exit 1
|
||||||
|
$TMUX2 set -g status off || exit 1
|
||||||
|
$TMUX2 set -g window-size manual || exit 1
|
||||||
|
$TMUX2 resizew -x40 -y14 || exit 1
|
||||||
|
$TMUX new -d -x40 -y14 || exit 1
|
||||||
|
$TMUX set -g status off || exit 1
|
||||||
|
$TMUX set -g window-size manual || exit 1
|
||||||
|
$TMUX set -g default-terminal "tmux-256color" || exit 1
|
||||||
|
$TMUX send -l "$TMUX2 attach" || exit 1
|
||||||
|
$TMUX send Enter || exit 1
|
||||||
|
sleep 1
|
||||||
|
}
|
||||||
|
|
||||||
|
# popup <args>: open a popup running a fixed command, in the background (it stays
|
||||||
|
# open because the command sleeps; the servers are killed at the next setup).
|
||||||
|
popup() {
|
||||||
|
$TMUX2 display-popup "$@" -E "sh -c 'printf POPUP; exec sleep 100'" &
|
||||||
|
sleep 1
|
||||||
|
}
|
||||||
|
|
||||||
|
# Basic popup over a single pane.
|
||||||
|
setup
|
||||||
|
popup -w20 -h6 -x6 -y3
|
||||||
|
compare popup-basic
|
||||||
|
|
||||||
|
# Popup over a split: drawn on top of the pane border.
|
||||||
|
setup
|
||||||
|
$TMUX2 splitw -h "$C" || exit 1
|
||||||
|
popup -w24 -h8 -x8 -y3
|
||||||
|
compare popup-over-split
|
||||||
|
|
||||||
|
# Popup with no border lines (-B).
|
||||||
|
setup
|
||||||
|
popup -B -w20 -h6 -x6 -y3
|
||||||
|
compare popup-noborder
|
||||||
|
|
||||||
|
# Popup with double border lines.
|
||||||
|
setup
|
||||||
|
popup -b double -w20 -h6 -x6 -y3
|
||||||
|
compare popup-double
|
||||||
|
|
||||||
|
exit 0
|
||||||
@@ -1,12 +0,0 @@
|
|||||||
base
|
|
||||||
╭──────────────────────────╮
|
|
||||||
│FLOAT │
|
|
||||||
│ │
|
|
||||||
│ │
|
|
||||||
│ │
|
|
||||||
│ │
|
|
||||||
│ │
|
|
||||||
╰──────────────────────────╯
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -1,11 +1,11 @@
|
|||||||
MENU00 abcdefghij
|
MENU00 abcdefghij
|
||||||
MENU01 abcdefghij
|
MENU01 abcdefghij
|
||||||
MENU02 abcdefghij
|
MENU02[38;5;45m[48;5;235m Menu [39m[49m
|
||||||
MENU03 abcdefghij
|
MENU03[38;5;45m[48;5;235m [38;5;250m Alpha item (a) [38;5;45m [39m[49m
|
||||||
MENU04[38;5;250m[48;5;235m Alpha item (a) [39m[49m
|
MENU04[38;5;45m[48;5;235m [38;5;16m[48;5;220m Beta item (b) [38;5;45m[48;5;235m [39m[49m
|
||||||
MENU05[38;5;16m[48;5;220m Beta item (b) [39m[49m
|
MENU05[38;5;45m[48;5;235m [39m[49m
|
||||||
MENU06[38;5;45m[48;5;235m [39m[49m
|
MENU06[38;5;45m[48;5;235m [38;5;250m Gamma item (g) [38;5;45m [39m[49m
|
||||||
MENU07[38;5;250m[48;5;235m Gamma item (g) [39m[49m
|
MENU07[38;5;45m[48;5;235m [39m[49m
|
||||||
MENU08 abcdefghij
|
MENU08 abcdefghij
|
||||||
MENU09 abcdefghij
|
MENU09 abcdefghij
|
||||||
MENU10 abcdefghij
|
MENU10 abcdefghij
|
||||||
|
|||||||
@@ -1,13 +0,0 @@
|
|||||||
MENU00 abcdefghij
|
|
||||||
MENU01 abcdefghij
|
|
||||||
MENU02 abcdefghij
|
|
||||||
MENU03 abcdefghij
|
|
||||||
MENU04 abcd[38;5;250m[48;5;235m Alpha item (a) [39m[49m
|
|
||||||
MENU05 abcd[38;5;16m[48;5;220m Beta item (b) [39m[49m
|
|
||||||
MENU06 abcd[38;5;45m[48;5;235m [39m[49m
|
|
||||||
MENU07 abcd[38;5;250m[48;5;235m Gamma item (g) [39m[49m
|
|
||||||
MENU08 abcdefghij
|
|
||||||
MENU09 abcdefghij
|
|
||||||
MENU10 abcdefghij
|
|
||||||
MENU11 abcdefghij
|
|
||||||
MENU12 abcdefghij
|
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
POP00 ┌──────────────────┐
|
||||||
|
POP01 │POPUP │
|
||||||
|
POP02 │ │
|
||||||
|
POP03 │ │
|
||||||
|
POP04 │ │
|
||||||
|
POP05 └──────────────────┘
|
||||||
|
POP06 abcdefghij
|
||||||
|
POP07 abcdefghij
|
||||||
|
POP08 abcdefghij
|
||||||
|
POP09 abcdefghij
|
||||||
|
POP10 abcdefghij
|
||||||
|
POP11 abcdefghij
|
||||||
|
POP12 abcdefghij
|
||||||
|
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
POP00 ╔══════════════════╗
|
||||||
|
POP01 ║POPUP ║
|
||||||
|
POP02 ║ ║
|
||||||
|
POP03 ║ ║
|
||||||
|
POP04 ║ ║
|
||||||
|
POP05 ╚══════════════════╝
|
||||||
|
POP06 abcdefghij
|
||||||
|
POP07 abcdefghij
|
||||||
|
POP08 abcdefghij
|
||||||
|
POP09 abcdefghij
|
||||||
|
POP10 abcdefghij
|
||||||
|
POP11 abcdefghij
|
||||||
|
POP12 abcdefghij
|
||||||
|
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
POP00 POPUP
|
||||||
|
POP01
|
||||||
|
POP02
|
||||||
|
POP03
|
||||||
|
POP04
|
||||||
|
POP05
|
||||||
|
POP06 abcdefghij
|
||||||
|
POP07 abcdefghij
|
||||||
|
POP08 abcdefghij
|
||||||
|
POP09 abcdefghij
|
||||||
|
POP10 abcdefghij
|
||||||
|
POP11 abcdefghij
|
||||||
|
POP12 abcdefghij
|
||||||
|
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
POP00 ab┌──────────────────────┐fghij
|
||||||
|
POP01 ab│POPUP │fghij
|
||||||
|
POP02 ab│ │fghij
|
||||||
|
POP03 ab│ │fghij
|
||||||
|
POP04 ab│ │fghij
|
||||||
|
POP05 ab│ │fghij
|
||||||
|
POP06 ab│ │fghij
|
||||||
|
POP07 ab└──────────────────────┘fghij
|
||||||
|
POP08 abcdefghij │POP08 abcdefghij
|
||||||
|
POP09 abcdefghij │POP09 abcdefghij
|
||||||
|
POP10 abcdefghij │POP10 abcdefghij
|
||||||
|
POP11 abcdefghij │POP11 abcdefghij
|
||||||
|
POP12 abcdefghij │POP12 abcdefghij
|
||||||
|
│
|
||||||
@@ -1,123 +0,0 @@
|
|||||||
#!/bin/sh
|
|
||||||
|
|
||||||
# A scroll held back by a synchronized update may be sent to all clients while
|
|
||||||
# a later client is being redrawn. An earlier client which cannot scroll the
|
|
||||||
# pane itself must still end up with the right contents.
|
|
||||||
|
|
||||||
PATH=/bin:/usr/bin
|
|
||||||
TERM=screen
|
|
||||||
LC_ALL=C.UTF-8
|
|
||||||
export PATH TERM LC_ALL
|
|
||||||
|
|
||||||
[ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux)
|
|
||||||
|
|
||||||
DIR=$(mktemp -d) || exit 1
|
|
||||||
TMUX_TMPDIR=$DIR
|
|
||||||
export TMUX_TMPDIR
|
|
||||||
|
|
||||||
INNER="$TEST_TMUX -Li$$ -f/dev/null"
|
|
||||||
OUTER="$TEST_TMUX -Lo$$ -f/dev/null"
|
|
||||||
CONTROL=$DIR/control
|
|
||||||
EMITTER=$DIR/emitter.pl
|
|
||||||
|
|
||||||
fail()
|
|
||||||
{
|
|
||||||
echo "$*" >&2
|
|
||||||
exit 1
|
|
||||||
}
|
|
||||||
|
|
||||||
cleanup()
|
|
||||||
{
|
|
||||||
$OUTER kill-server 2>/dev/null
|
|
||||||
$INNER kill-server 2>/dev/null
|
|
||||||
rm -rf "$DIR"
|
|
||||||
}
|
|
||||||
trap cleanup 0 1 15
|
|
||||||
|
|
||||||
wait_for_file()
|
|
||||||
{
|
|
||||||
i=0
|
|
||||||
while [ "$i" -lt 50 ] && [ ! -e "$1" ]; do
|
|
||||||
sleep 0.1
|
|
||||||
i=$((i + 1))
|
|
||||||
done
|
|
||||||
[ -e "$1" ] || fail "$2"
|
|
||||||
}
|
|
||||||
|
|
||||||
wait_for_clients()
|
|
||||||
{
|
|
||||||
i=0
|
|
||||||
while [ "$i" -lt 50 ]; do
|
|
||||||
n=$($INNER list-clients 2>/dev/null | wc -l)
|
|
||||||
[ "$n" -eq "$1" ] && return 0
|
|
||||||
sleep 0.1
|
|
||||||
i=$((i + 1))
|
|
||||||
done
|
|
||||||
fail "client $1 did not attach"
|
|
||||||
}
|
|
||||||
|
|
||||||
cat >"$EMITTER" <<'PERL'
|
|
||||||
use strict;
|
|
||||||
use warnings;
|
|
||||||
|
|
||||||
my $control = $ENV{CONTROL};
|
|
||||||
(my $dir = $control) =~ s{/[^/]+$}{};
|
|
||||||
|
|
||||||
my @rows;
|
|
||||||
for my $row (1 .. 24) {
|
|
||||||
push @rows, sprintf("INIT_ROW_%02d_", $row) . ('X' x 16);
|
|
||||||
}
|
|
||||||
syswrite STDOUT, "\e[H" . join("\r\n", @rows);
|
|
||||||
|
|
||||||
open my $painted, '>', "$dir/painted" or die "$dir/painted: $!\n";
|
|
||||||
close $painted;
|
|
||||||
while (!-e $control) {
|
|
||||||
select undef, undef, undef, 0.01;
|
|
||||||
}
|
|
||||||
|
|
||||||
# Start a synchronized update and scroll, but do not end it.
|
|
||||||
syswrite STDOUT, "\e[?2026h\e[24;1H\r\nSCROLLED_LINE_";
|
|
||||||
open my $scrolled, '>', "$dir/scrolled" or die "$dir/scrolled: $!\n";
|
|
||||||
close $scrolled;
|
|
||||||
select undef, undef, undef, 3;
|
|
||||||
PERL
|
|
||||||
|
|
||||||
$INNER new-session -d -s inner -x 80 -y 24 \
|
|
||||||
"CONTROL='$CONTROL' perl '$EMITTER'" || exit 1
|
|
||||||
$INNER set-option -g status off || exit 1
|
|
||||||
$INNER set-option -g window-size manual || exit 1
|
|
||||||
$INNER set-option -as terminal-features '*:sync' || exit 1
|
|
||||||
|
|
||||||
# The pane is not full width, so clients cannot scroll it.
|
|
||||||
$INNER split-window -h -d -t inner:0.0 'sleep 30' || exit 1
|
|
||||||
|
|
||||||
for name in a b; do
|
|
||||||
$OUTER new-session -d -s $name -x 80 -y 24 \
|
|
||||||
"$TEST_TMUX -Li$$ -f/dev/null attach-session -t inner" || exit 1
|
|
||||||
$OUTER set-option -t $name status off || exit 1
|
|
||||||
if [ "$name" = a ]; then
|
|
||||||
wait_for_clients 1
|
|
||||||
fi
|
|
||||||
done
|
|
||||||
$OUTER set-option -g window-size manual || exit 1
|
|
||||||
wait_for_clients 2
|
|
||||||
B=$($OUTER display-message -p -t b: '#{pane_tty}') || exit 1
|
|
||||||
|
|
||||||
wait_for_file "$DIR/painted" "application did not paint"
|
|
||||||
sleep 1
|
|
||||||
: >"$CONTROL"
|
|
||||||
wait_for_file "$DIR/scrolled" "application did not scroll"
|
|
||||||
sleep 0.2
|
|
||||||
|
|
||||||
# Redraw only the second client while the update is still in progress.
|
|
||||||
$INNER refresh-client -t "$B" || exit 1
|
|
||||||
sleep 1
|
|
||||||
|
|
||||||
$OUTER capture-pane -p -t a: >"$DIR/a" || exit 1
|
|
||||||
$OUTER capture-pane -p -t b: >"$DIR/b" || exit 1
|
|
||||||
sed -n 1p "$DIR/b" | grep -q '^INIT_ROW_02_' ||
|
|
||||||
fail "second client not scrolled"
|
|
||||||
sed -n 24p "$DIR/b" | grep -q '^SCROLLED_LINE_' ||
|
|
||||||
fail "second client missing scrolled line"
|
|
||||||
cmp -s "$DIR/a" "$DIR/b" || fail "first client differs from second client"
|
|
||||||
exit 0
|
|
||||||
@@ -1,150 +0,0 @@
|
|||||||
#!/bin/sh
|
|
||||||
|
|
||||||
# Check that synchronized scrolling does not redraw unchanged lines.
|
|
||||||
|
|
||||||
PATH=/bin:/usr/bin
|
|
||||||
TERM=screen
|
|
||||||
LC_ALL=C.UTF-8
|
|
||||||
export PATH TERM LC_ALL
|
|
||||||
|
|
||||||
[ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux)
|
|
||||||
|
|
||||||
DIR=$(mktemp -d) || exit 1
|
|
||||||
TMUX_TMPDIR=$DIR
|
|
||||||
export TMUX_TMPDIR
|
|
||||||
|
|
||||||
INNER="$TEST_TMUX -Li$$ -f/dev/null"
|
|
||||||
OUTER="$TEST_TMUX -Lo$$ -f/dev/null"
|
|
||||||
CLIENT_BYTES=$DIR/client-bytes
|
|
||||||
CONTROL=$DIR/control
|
|
||||||
EMITTER=$DIR/emitter.pl
|
|
||||||
|
|
||||||
fail()
|
|
||||||
{
|
|
||||||
echo "$*" >&2
|
|
||||||
exit 1
|
|
||||||
}
|
|
||||||
|
|
||||||
cleanup()
|
|
||||||
{
|
|
||||||
$OUTER kill-server 2>/dev/null
|
|
||||||
$INNER kill-server 2>/dev/null
|
|
||||||
rm -rf "$DIR"
|
|
||||||
}
|
|
||||||
trap cleanup 0 1 15
|
|
||||||
|
|
||||||
wait_for_file()
|
|
||||||
{
|
|
||||||
i=0
|
|
||||||
while [ "$i" -lt 50 ] && [ ! -e "$1" ]; do
|
|
||||||
sleep 0.1
|
|
||||||
i=$((i + 1))
|
|
||||||
done
|
|
||||||
[ -e "$1" ] || fail "$2"
|
|
||||||
}
|
|
||||||
|
|
||||||
wait_for_client()
|
|
||||||
{
|
|
||||||
i=0
|
|
||||||
while [ "$i" -lt 50 ]; do
|
|
||||||
$INNER list-clients -F '#{client_termfeatures}' 2>/dev/null |
|
|
||||||
grep -q 'sync' && return 0
|
|
||||||
sleep 0.1
|
|
||||||
i=$((i + 1))
|
|
||||||
done
|
|
||||||
fail "sync-capable client did not attach"
|
|
||||||
}
|
|
||||||
|
|
||||||
wait_for_stable_bytes()
|
|
||||||
{
|
|
||||||
previous=-1
|
|
||||||
stable=0
|
|
||||||
i=0
|
|
||||||
while [ "$i" -lt 50 ]; do
|
|
||||||
current=$(wc -c <"$CLIENT_BYTES" 2>/dev/null) || current=0
|
|
||||||
if [ "$current" -gt 0 ] && [ "$current" -eq "$previous" ]; then
|
|
||||||
stable=$((stable + 1))
|
|
||||||
[ "$stable" -eq 5 ] && return 0
|
|
||||||
else
|
|
||||||
stable=0
|
|
||||||
fi
|
|
||||||
previous=$current
|
|
||||||
sleep 0.1
|
|
||||||
i=$((i + 1))
|
|
||||||
done
|
|
||||||
fail "client byte stream did not become stable"
|
|
||||||
}
|
|
||||||
|
|
||||||
cat >"$EMITTER" <<'PERL'
|
|
||||||
use strict;
|
|
||||||
use warnings;
|
|
||||||
|
|
||||||
my $control = $ENV{CONTROL};
|
|
||||||
(my $dir = $control) =~ s{/[^/]+$}{};
|
|
||||||
|
|
||||||
# Fill the screen outside a synchronized update.
|
|
||||||
my @rows;
|
|
||||||
for my $row (1 .. 24) {
|
|
||||||
push @rows, substr(sprintf("INIT_ROW_%02d_", $row) . ('X' x 79), 0, 79);
|
|
||||||
}
|
|
||||||
syswrite STDOUT, "\e[H" . join("\r\n", @rows);
|
|
||||||
|
|
||||||
open my $painted, '>', "$dir/painted" or die "$dir/painted: $!\n";
|
|
||||||
close $painted;
|
|
||||||
while (!-e $control) {
|
|
||||||
select undef, undef, undef, 0.01;
|
|
||||||
}
|
|
||||||
|
|
||||||
# Scroll one line and overwrite text in a synchronized update.
|
|
||||||
my $frame = "\e[?2026h" .
|
|
||||||
"\e[24;1H\r\nSCROLLED_LINE_" .
|
|
||||||
"\e[23;60HCHANGED_" .
|
|
||||||
"\e[?2026l";
|
|
||||||
my $written = syswrite STDOUT, $frame;
|
|
||||||
die "short synchronized frame write\n"
|
|
||||||
unless defined $written && $written == length $frame;
|
|
||||||
select undef, undef, undef, 3;
|
|
||||||
PERL
|
|
||||||
|
|
||||||
$INNER new-session -d -s inner -x 80 -y 24 \
|
|
||||||
"CONTROL='$CONTROL' perl '$EMITTER'" || exit 1
|
|
||||||
$INNER set-option -g status off || exit 1
|
|
||||||
$INNER set-option -g window-size manual || exit 1
|
|
||||||
$INNER set-option -as terminal-features '*:sync' || exit 1
|
|
||||||
|
|
||||||
$OUTER new-session -d -s outer -x 80 -y 24 \
|
|
||||||
"$TEST_TMUX -Li$$ -f/dev/null attach-session -t inner" ||
|
|
||||||
exit 1
|
|
||||||
$OUTER set-option -g status off || exit 1
|
|
||||||
$OUTER set-option -g window-size manual || exit 1
|
|
||||||
wait_for_client
|
|
||||||
|
|
||||||
wait_for_file "$DIR/painted" "application did not paint"
|
|
||||||
sleep 1
|
|
||||||
$OUTER pipe-pane -O -t outer:0.0 "cat >'$CLIENT_BYTES'" || exit 1
|
|
||||||
: >"$CONTROL"
|
|
||||||
i=0
|
|
||||||
while [ "$i" -lt 50 ]; do
|
|
||||||
grep -q 'SCROLLED_LINE_' "$CLIENT_BYTES" 2>/dev/null && break
|
|
||||||
sleep 0.1
|
|
||||||
i=$((i + 1))
|
|
||||||
done
|
|
||||||
grep -q 'SCROLLED_LINE_' "$CLIENT_BYTES" || fail "scrolled line not sent"
|
|
||||||
wait_for_stable_bytes
|
|
||||||
$OUTER pipe-pane -t outer:0.0 || exit 1
|
|
||||||
|
|
||||||
# Check that only changed lines are redrawn.
|
|
||||||
grep -q 'CHANGED_' "$CLIENT_BYTES" || fail "changed cell not sent"
|
|
||||||
if grep -q 'INIT_ROW_05_' "$CLIENT_BYTES"; then
|
|
||||||
fail "unchanged line redrawn after synchronized scroll"
|
|
||||||
fi
|
|
||||||
|
|
||||||
# The client must show the scrolled screen.
|
|
||||||
$OUTER capture-pane -p -t outer:0.0 >"$DIR/screen" || exit 1
|
|
||||||
sed -n 1p "$DIR/screen" | grep -q '^INIT_ROW_02_' ||
|
|
||||||
fail "first line not scrolled"
|
|
||||||
sed -n 23p "$DIR/screen" | grep -q 'INIT_ROW_24_.*CHANGED_' ||
|
|
||||||
fail "changed cell not on screen"
|
|
||||||
sed -n 24p "$DIR/screen" | grep -q '^SCROLLED_LINE_' ||
|
|
||||||
fail "scrolled line not on screen"
|
|
||||||
exit 0
|
|
||||||
+63
-28
@@ -1,4 +1,4 @@
|
|||||||
/* $OpenBSD: screen-redraw.c,v 1.162 2026/09/24 11:19:39 nicm Exp $ */
|
/* $OpenBSD: screen-redraw.c,v 1.161 2026/09/22 16:56:07 nicm Exp $ */
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2026 Nicholas Marriott <nicholas.marriott@gmail.com>
|
* Copyright (c) 2026 Nicholas Marriott <nicholas.marriott@gmail.com>
|
||||||
@@ -33,7 +33,7 @@
|
|||||||
* this is done at various points, such as when a pane is moved or resized. The
|
* this is done at various points, such as when a pane is moved or resized. The
|
||||||
* scene only includes the part of the client used for the window: panes, pane
|
* scene only includes the part of the client used for the window: panes, pane
|
||||||
* status lines, borders, scrollbars, and any area outside the window. The
|
* status lines, borders, scrollbars, and any area outside the window. The
|
||||||
* client status line is not included.
|
* client status line and overlay are not included.
|
||||||
*
|
*
|
||||||
* A scene is made from spans. A span is a horizontal run of cells on one
|
* A scene is made from spans. A span is a horizontal run of cells on one
|
||||||
* visible line that can be drawn in the same way. Each span has a type, for
|
* visible line that can be drawn in the same way. Each span has a type, for
|
||||||
@@ -93,6 +93,7 @@ enum redraw_span_type {
|
|||||||
#define REDRAW_PANE_SCROLLBAR 0x20
|
#define REDRAW_PANE_SCROLLBAR 0x20
|
||||||
#define REDRAW_STATUS 0x40
|
#define REDRAW_STATUS 0x40
|
||||||
#define REDRAW_MENU 0x80
|
#define REDRAW_MENU 0x80
|
||||||
|
#define REDRAW_OVERLAY 0x100
|
||||||
|
|
||||||
/* Draw everything. */
|
/* Draw everything. */
|
||||||
#define REDRAW_ALL 0x7fffffff
|
#define REDRAW_ALL 0x7fffffff
|
||||||
@@ -263,6 +264,8 @@ redraw_flags_to_string(int flags)
|
|||||||
strlcat(s, "scrollbar ", sizeof s);
|
strlcat(s, "scrollbar ", sizeof s);
|
||||||
if (flags & REDRAW_MENU)
|
if (flags & REDRAW_MENU)
|
||||||
strlcat(s, "menu ", sizeof s);
|
strlcat(s, "menu ", sizeof s);
|
||||||
|
if (flags & REDRAW_OVERLAY)
|
||||||
|
strlcat(s, "overlay ", sizeof s);
|
||||||
if (REDRAW_IS_ALL(flags))
|
if (REDRAW_IS_ALL(flags))
|
||||||
strlcat(s, "all ", sizeof s);
|
strlcat(s, "all ", sizeof s);
|
||||||
if (*s != '\0')
|
if (*s != '\0')
|
||||||
@@ -1378,30 +1381,45 @@ static void
|
|||||||
redraw_draw_span(struct redraw_draw_ctx *dctx, struct redraw_span *span,
|
redraw_draw_span(struct redraw_draw_ctx *dctx, struct redraw_span *span,
|
||||||
u_int y)
|
u_int y)
|
||||||
{
|
{
|
||||||
|
struct redraw_scene *scene = dctx->scene;
|
||||||
struct redraw_span_data *data = &span->data;
|
struct redraw_span_data *data = &span->data;
|
||||||
enum redraw_span_type type = data->type;
|
enum redraw_span_type type = data->type;
|
||||||
|
struct client *c = scene->c;
|
||||||
|
struct tty *tty = &c->tty;
|
||||||
|
struct visible_ranges *r;
|
||||||
|
struct visible_range *rr;
|
||||||
|
u_int i, x, n;
|
||||||
|
|
||||||
if (type == REDRAW_SPAN_STATUS && ~data->st.wp->flags & PANE_NEWSTATUS)
|
if (type == REDRAW_SPAN_STATUS && ~data->st.wp->flags & PANE_NEWSTATUS)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
switch (span->data.type) {
|
r = tty_check_overlay_range(tty, span->x, y, span->width);
|
||||||
case REDRAW_SPAN_PANE:
|
for (i = 0; i < r->used; i++) {
|
||||||
redraw_draw_pane_span(dctx, span, span->x, y, span->width);
|
rr = &r->ranges[i];
|
||||||
break;
|
if (rr->nx == 0)
|
||||||
case REDRAW_SPAN_BORDER:
|
continue;
|
||||||
case REDRAW_SPAN_EMPTY:
|
x = rr->px;
|
||||||
case REDRAW_SPAN_OUTSIDE:
|
n = rr->nx;
|
||||||
redraw_draw_border_span(dctx, span, span->x, y, span->width);
|
|
||||||
break;
|
switch (span->data.type) {
|
||||||
case REDRAW_SPAN_STATUS:
|
case REDRAW_SPAN_PANE:
|
||||||
redraw_draw_status_span(dctx, span, span->x, y, span->width);
|
redraw_draw_pane_span(dctx, span, x, y, n);
|
||||||
break;
|
break;
|
||||||
case REDRAW_SPAN_SCROLLBAR:
|
case REDRAW_SPAN_BORDER:
|
||||||
redraw_draw_scrollbar_span(dctx, span, span->x, y, span->width);
|
case REDRAW_SPAN_EMPTY:
|
||||||
break;
|
case REDRAW_SPAN_OUTSIDE:
|
||||||
case REDRAW_SPAN_MENU:
|
redraw_draw_border_span(dctx, span, x, y, n);
|
||||||
redraw_draw_menu_span(dctx, span, span->x, y, span->width);
|
break;
|
||||||
break;
|
case REDRAW_SPAN_STATUS:
|
||||||
|
redraw_draw_status_span(dctx, span, x, y, n);
|
||||||
|
break;
|
||||||
|
case REDRAW_SPAN_SCROLLBAR:
|
||||||
|
redraw_draw_scrollbar_span(dctx, span, x, y, n);
|
||||||
|
break;
|
||||||
|
case REDRAW_SPAN_MENU:
|
||||||
|
redraw_draw_menu_span(dctx, span, x, y, n);
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1670,8 +1688,10 @@ redraw_draw(struct client *c, struct window_pane *wp, int flags)
|
|||||||
struct screen *sl;
|
struct screen *sl;
|
||||||
struct redraw_scene *scene;
|
struct redraw_scene *scene;
|
||||||
struct window_pane *loop;
|
struct window_pane *loop;
|
||||||
u_int width, i, y, lines;
|
u_int width, i, y, lines, j;
|
||||||
struct redraw_span *first;
|
struct redraw_span *first;
|
||||||
|
struct visible_ranges *r;
|
||||||
|
struct visible_range *rr;
|
||||||
int redraw;
|
int redraw;
|
||||||
|
|
||||||
if (c->flags & CLIENT_SUSPENDED)
|
if (c->flags & CLIENT_SUSPENDED)
|
||||||
@@ -1738,14 +1758,14 @@ redraw_draw(struct client *c, struct window_pane *wp, int flags)
|
|||||||
if (wp != NULL) {
|
if (wp != NULL) {
|
||||||
if (wp->base.mode & MODE_SYNC)
|
if (wp->base.mode & MODE_SYNC)
|
||||||
screen_write_stop_sync(wp);
|
screen_write_stop_sync(wp);
|
||||||
screen_write_sync_clear_dirty(wp);
|
screen_write_clear_dirty(wp);
|
||||||
} else {
|
} else {
|
||||||
TAILQ_FOREACH(loop, &scene->w->panes, entry) {
|
TAILQ_FOREACH(loop, &scene->w->panes, entry) {
|
||||||
if (!window_pane_is_visible(loop))
|
if (!window_pane_is_visible(loop))
|
||||||
continue;
|
continue;
|
||||||
if (loop->base.mode & MODE_SYNC)
|
if (loop->base.mode & MODE_SYNC)
|
||||||
screen_write_stop_sync(loop);
|
screen_write_stop_sync(loop);
|
||||||
screen_write_sync_clear_dirty(loop);
|
screen_write_clear_dirty(loop);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1779,9 +1799,19 @@ redraw_draw(struct client *c, struct window_pane *wp, int flags)
|
|||||||
else
|
else
|
||||||
y = c->tty.sy - lines;
|
y = c->tty.sy - lines;
|
||||||
sl = c->status.active;
|
sl = c->status.active;
|
||||||
for (i = 0; i < lines; i++)
|
for (i = 0; i < lines; i++) {
|
||||||
tty_draw_line(tty, sl, 0, i, tty->sx, 0, y + i, NULL);
|
r = tty_check_overlay_range(tty, 0, y + i, tty->sx);
|
||||||
|
for (j = 0; j < r->used; j++) {
|
||||||
|
rr = &r->ranges[j];
|
||||||
|
if (rr->nx == 0)
|
||||||
|
continue;
|
||||||
|
tty_draw_line(tty, sl, rr->px, i, rr->nx,
|
||||||
|
rr->px, y + i, NULL);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
if (c->overlay_draw != NULL && (flags & REDRAW_OVERLAY))
|
||||||
|
c->overlay_draw(c, c->overlay_data);
|
||||||
|
|
||||||
tty_reset(tty);
|
tty_reset(tty);
|
||||||
|
|
||||||
@@ -1837,13 +1867,18 @@ redraw_screen(struct client *c)
|
|||||||
{
|
{
|
||||||
int flags = 0;
|
int flags = 0;
|
||||||
|
|
||||||
if (c->flags & CLIENT_REDRAWWINDOW)
|
if (c->flags & CLIENT_REDRAWWINDOW) {
|
||||||
redraw_draw(c, NULL, REDRAW_ALL);
|
if (c->flags & CLIENT_REDRAWOVERLAY)
|
||||||
else {
|
redraw_draw(c, NULL, REDRAW_ALL);
|
||||||
|
else
|
||||||
|
redraw_draw(c, NULL, REDRAW_ALL & ~REDRAW_OVERLAY);
|
||||||
|
} else {
|
||||||
if (c->flags & CLIENT_REDRAWBORDERS)
|
if (c->flags & CLIENT_REDRAWBORDERS)
|
||||||
flags |= (REDRAW_PANE_BORDER|REDRAW_PANE_STATUS);
|
flags |= (REDRAW_PANE_BORDER|REDRAW_PANE_STATUS);
|
||||||
if (c->flags & CLIENT_REDRAWSTATUS)
|
if (c->flags & CLIENT_REDRAWSTATUS)
|
||||||
flags |= (REDRAW_STATUS|REDRAW_PANE_STATUS);
|
flags |= (REDRAW_STATUS|REDRAW_PANE_STATUS);
|
||||||
|
if (c->flags & CLIENT_REDRAWOVERLAY)
|
||||||
|
flags |= REDRAW_OVERLAY;
|
||||||
if (c->flags & CLIENT_REDRAWMENU)
|
if (c->flags & CLIENT_REDRAWMENU)
|
||||||
flags |= REDRAW_MENU;
|
flags |= REDRAW_MENU;
|
||||||
if (c->session->curw->window->menu != NULL)
|
if (c->session->curw->window->menu != NULL)
|
||||||
|
|||||||
+53
-176
@@ -1,4 +1,4 @@
|
|||||||
/* $OpenBSD: screen-write.c,v 1.293 2026/09/24 11:19:39 nicm Exp $ */
|
/* $OpenBSD: screen-write.c,v 1.290 2026/08/24 15:05:26 nicm Exp $ */
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>
|
* Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>
|
||||||
@@ -38,9 +38,7 @@ static int screen_write_overwrite(struct screen_write_ctx *,
|
|||||||
struct grid_cell *, u_int);
|
struct grid_cell *, u_int);
|
||||||
static int screen_write_combine(struct screen_write_ctx *,
|
static int screen_write_combine(struct screen_write_ctx *,
|
||||||
const struct grid_cell *);
|
const struct grid_cell *);
|
||||||
static void screen_write_sync_flush_dirty(struct window_pane *);
|
static void screen_write_flush_dirty(struct window_pane *);
|
||||||
static void screen_write_sync_apply_scroll(struct screen_write_ctx *,
|
|
||||||
struct tty_ctx *);
|
|
||||||
|
|
||||||
struct screen_write_citem {
|
struct screen_write_citem {
|
||||||
u_int x;
|
u_int x;
|
||||||
@@ -217,27 +215,6 @@ screen_write_pane_is_obscured(struct screen_write_ctx *ctx)
|
|||||||
return (0);
|
return (0);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Allocate dirty lines for this screen size. */
|
|
||||||
static bitstr_t *
|
|
||||||
screen_write_sync_allocate_dirty(struct window_pane *wp, u_int sy)
|
|
||||||
{
|
|
||||||
bitstr_t *bs = wp->sync_dirty;
|
|
||||||
|
|
||||||
if (bs != NULL && wp->sync_dirty_size == sy)
|
|
||||||
return (bs);
|
|
||||||
|
|
||||||
free(bs);
|
|
||||||
bs = wp->sync_dirty = bit_alloc(sy);
|
|
||||||
if (bs == NULL)
|
|
||||||
fatal("bit_alloc failed");
|
|
||||||
if (wp->sync_dirty_size != 0) {
|
|
||||||
bit_nset(bs, 0, sy - 1);
|
|
||||||
wp->sync_scrolled = 0;
|
|
||||||
}
|
|
||||||
wp->sync_dirty_size = sy;
|
|
||||||
return (bs);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Should we draw to the TTY? */
|
/* Should we draw to the TTY? */
|
||||||
static int
|
static int
|
||||||
screen_write_should_draw_lines(struct screen_write_ctx *ctx, u_int y, u_int ny)
|
screen_write_should_draw_lines(struct screen_write_ctx *ctx, u_int y, u_int ny)
|
||||||
@@ -251,9 +228,21 @@ screen_write_should_draw_lines(struct screen_write_ctx *ctx, u_int y, u_int ny)
|
|||||||
return (0);
|
return (0);
|
||||||
if (s->mode & MODE_SYNC) {
|
if (s->mode & MODE_SYNC) {
|
||||||
if (wp != NULL && y < sy && ny != 0) {
|
if (wp != NULL && y < sy && ny != 0) {
|
||||||
|
bs = wp->sync_dirty;
|
||||||
if (ny > sy - y)
|
if (ny > sy - y)
|
||||||
ny = sy - y;
|
ny = sy - y;
|
||||||
bs = screen_write_sync_allocate_dirty(wp, sy);
|
if (bs == NULL || wp->sync_dirty_size != sy) {
|
||||||
|
if (bs != NULL && wp->sync_dirty_size != sy) {
|
||||||
|
y = 0;
|
||||||
|
ny = sy;
|
||||||
|
}
|
||||||
|
free(bs);
|
||||||
|
|
||||||
|
bs = wp->sync_dirty = bit_alloc(sy);
|
||||||
|
if (bs == NULL)
|
||||||
|
fatal("bit_alloc failed");
|
||||||
|
wp->sync_dirty_size = sy;
|
||||||
|
}
|
||||||
bit_nset(bs, y, y + ny - 1);
|
bit_nset(bs, y, y + ny - 1);
|
||||||
}
|
}
|
||||||
return (0);
|
return (0);
|
||||||
@@ -316,16 +305,21 @@ screen_write_initctx(struct screen_write_ctx *ctx, struct tty_ctx *ttyctx,
|
|||||||
|
|
||||||
if (~ctx->flags & SCREEN_WRITE_SYNC) {
|
if (~ctx->flags & SCREEN_WRITE_SYNC) {
|
||||||
/*
|
/*
|
||||||
* For the active pane showing its base screen, only use
|
* For the active pane showing its base screen or for an
|
||||||
* synchronized updates if requested (commands that move the
|
* overlay (no pane), only use synchronized updates if
|
||||||
* cursor); for other panes or a pane in a mode, always use it,
|
* requested (commands that move the cursor); for other panes
|
||||||
* since the cursor will have to move.
|
* or a pane in a mode, always use it, since the cursor will
|
||||||
|
* have to move.
|
||||||
*/
|
*/
|
||||||
if (ctx->wp != NULL && (ctx->wp != ctx->wp->window->active ||
|
if (ctx->wp != NULL && (ctx->wp != ctx->wp->window->active ||
|
||||||
ctx->wp->screen != &ctx->wp->base))
|
ctx->wp->screen != &ctx->wp->base))
|
||||||
ttyctx->flags |= TTY_CTX_SYNC;
|
ttyctx->flags |= TTY_CTX_SYNC;
|
||||||
else if (is_sync)
|
else {
|
||||||
ttyctx->flags |= TTY_CTX_SYNC;
|
if (ctx->wp == NULL)
|
||||||
|
ttyctx->flags |= TTY_CTX_OVERLAY_SYNC;
|
||||||
|
if (is_sync)
|
||||||
|
ttyctx->flags |= TTY_CTX_SYNC;
|
||||||
|
}
|
||||||
tty_write(tty_cmd_syncstart, ttyctx);
|
tty_write(tty_cmd_syncstart, ttyctx);
|
||||||
ctx->flags |= SCREEN_WRITE_SYNC;
|
ctx->flags |= SCREEN_WRITE_SYNC;
|
||||||
}
|
}
|
||||||
@@ -720,7 +714,7 @@ screen_write_fast_copy(struct screen_write_ctx *ctx, struct screen *src,
|
|||||||
if (!window_position_is_visible(r, xoff + s->cx))
|
if (!window_position_is_visible(r, xoff + s->cx))
|
||||||
break;
|
break;
|
||||||
ttyctx.cell = &gc;
|
ttyctx.cell = &gc;
|
||||||
ttyctx.flags &= TTY_CTX_SYNC;
|
ttyctx.flags &= (TTY_CTX_OVERLAY_SYNC|TTY_CTX_SYNC);
|
||||||
tty_write(tty_cmd_cell, &ttyctx);
|
tty_write(tty_cmd_cell, &ttyctx);
|
||||||
ttyctx.ocx++;
|
ttyctx.ocx++;
|
||||||
|
|
||||||
@@ -844,39 +838,34 @@ screen_write_menu(struct screen_write_ctx *ctx, struct menu *menu, int choice,
|
|||||||
struct screen *s = ctx->s;
|
struct screen *s = ctx->s;
|
||||||
struct grid_cell default_gc;
|
struct grid_cell default_gc;
|
||||||
const struct grid_cell *gc = &default_gc;
|
const struct grid_cell *gc = &default_gc;
|
||||||
u_int border, cx = s->cx, cy = s->cy, i, j, width;
|
u_int cx, cy, i, j, width = menu->width;
|
||||||
const char *name;
|
const char *name;
|
||||||
|
|
||||||
|
cx = s->cx;
|
||||||
|
cy = s->cy;
|
||||||
|
|
||||||
memcpy(&default_gc, menu_gc, sizeof default_gc);
|
memcpy(&default_gc, menu_gc, sizeof default_gc);
|
||||||
|
|
||||||
if (lines == BOX_LINES_NONE) {
|
screen_write_box(ctx, menu->width + 4, menu->count + 2, lines,
|
||||||
border = 0;
|
border_gc, menu->title);
|
||||||
width = menu->item_width;
|
|
||||||
} else {
|
|
||||||
border = 1;
|
|
||||||
width = menu->width;
|
|
||||||
screen_write_box(ctx, width + 4, menu->count + 2, lines,
|
|
||||||
border_gc, menu->title);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (i = 0; i < menu->count; i++) {
|
for (i = 0; i < menu->count; i++) {
|
||||||
name = menu->items[i].name;
|
name = menu->items[i].name;
|
||||||
if (name == NULL) {
|
if (name == NULL) {
|
||||||
screen_write_cursormove(ctx, cx, cy + border + i, 0);
|
screen_write_cursormove(ctx, cx, cy + 1 + i, 0);
|
||||||
screen_write_hline(ctx, width + 2 + (2 * border), 1, 1,
|
screen_write_hline(ctx, width + 4, 1, 1, lines,
|
||||||
lines, border_gc);
|
border_gc);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (choice >= 0 && i == (u_int)choice && *name != '-')
|
if (choice >= 0 && i == (u_int)choice && *name != '-')
|
||||||
gc = choice_gc;
|
gc = choice_gc;
|
||||||
|
|
||||||
screen_write_cursormove(ctx, cx + border, cy + border + i, 0);
|
screen_write_cursormove(ctx, cx + 1, cy + 1 + i, 0);
|
||||||
for (j = 0; j < width + 2; j++)
|
for (j = 0; j < width + 2; j++)
|
||||||
screen_write_putc(ctx, gc, ' ');
|
screen_write_putc(ctx, gc, ' ');
|
||||||
|
|
||||||
screen_write_cursormove(ctx, cx + border + 1, cy + border + i,
|
screen_write_cursormove(ctx, cx + 2, cy + 1 + i, 0);
|
||||||
0);
|
|
||||||
if (*name == '-') {
|
if (*name == '-') {
|
||||||
default_gc.attr |= GRID_ATTR_DIM;
|
default_gc.attr |= GRID_ATTR_DIM;
|
||||||
format_draw(ctx, gc, width, name + 1, NULL, 0);
|
format_draw(ctx, gc, width, name + 1, NULL, 0);
|
||||||
@@ -1043,7 +1032,7 @@ screen_write_sync_callback(__unused int fd, __unused short events, void *arg)
|
|||||||
|
|
||||||
if (wp->base.mode & MODE_SYNC) {
|
if (wp->base.mode & MODE_SYNC) {
|
||||||
wp->base.mode &= ~MODE_SYNC;
|
wp->base.mode &= ~MODE_SYNC;
|
||||||
screen_write_sync_flush_dirty(wp);
|
screen_write_flush_dirty(wp);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1075,7 +1064,7 @@ screen_write_stop_sync(struct window_pane *wp)
|
|||||||
evtimer_del(&wp->sync_timer);
|
evtimer_del(&wp->sync_timer);
|
||||||
wp->base.mode &= ~MODE_SYNC;
|
wp->base.mode &= ~MODE_SYNC;
|
||||||
|
|
||||||
screen_write_sync_flush_dirty(wp);
|
screen_write_flush_dirty(wp);
|
||||||
|
|
||||||
log_debug("%s: %%%u stopped sync mode", __func__, wp->id);
|
log_debug("%s: %%%u stopped sync mode", __func__, wp->id);
|
||||||
}
|
}
|
||||||
@@ -1283,7 +1272,7 @@ screen_write_redraw_line(struct screen_write_ctx *ctx, struct tty_ctx *ttyctx,
|
|||||||
|
|
||||||
/* Redraw dirty lines. */
|
/* Redraw dirty lines. */
|
||||||
static void
|
static void
|
||||||
screen_write_sync_flush_dirty(struct window_pane *wp)
|
screen_write_flush_dirty(struct window_pane *wp)
|
||||||
{
|
{
|
||||||
struct screen_write_ctx ctx;
|
struct screen_write_ctx ctx;
|
||||||
struct tty_ctx ttyctx;
|
struct tty_ctx ttyctx;
|
||||||
@@ -1296,143 +1285,29 @@ screen_write_sync_flush_dirty(struct window_pane *wp)
|
|||||||
screen_write_start_pane(&ctx, wp, s);
|
screen_write_start_pane(&ctx, wp, s);
|
||||||
screen_write_initctx(&ctx, &ttyctx, 1, 1);
|
screen_write_initctx(&ctx, &ttyctx, 1, 1);
|
||||||
|
|
||||||
if (wp->sync_scrolled != 0)
|
for (y = 0; y < sy; y++) {
|
||||||
screen_write_sync_apply_scroll(&ctx, &ttyctx);
|
if (bit_test(wp->sync_dirty, y)) {
|
||||||
|
screen_write_redraw_line(&ctx, &ttyctx, y);
|
||||||
if (~wp->flags & PANE_REDRAW) {
|
lines++;
|
||||||
for (y = 0; y < sy; y++) {
|
|
||||||
if (bit_test(wp->sync_dirty, y)) {
|
|
||||||
screen_write_redraw_line(&ctx, &ttyctx, y);
|
|
||||||
lines++;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
log_debug("%s: %%%u had %u dirty lines", __func__, wp->id, lines);
|
log_debug("%s: %%%u had %u dirty lines", __func__, wp->id, lines);
|
||||||
|
|
||||||
screen_write_stop(&ctx);
|
screen_write_stop(&ctx);
|
||||||
screen_write_sync_clear_dirty(wp);
|
screen_write_clear_dirty(wp);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Clear pending synchronized output. */
|
/* Clear any dirty lines. */
|
||||||
void
|
void
|
||||||
screen_write_sync_clear_dirty(struct window_pane *wp)
|
screen_write_clear_dirty(struct window_pane *wp)
|
||||||
{
|
{
|
||||||
if (wp != NULL) {
|
if (wp != NULL && wp->sync_dirty != NULL) {
|
||||||
free(wp->sync_dirty);
|
free(wp->sync_dirty);
|
||||||
wp->sync_dirty = NULL;
|
wp->sync_dirty = NULL;
|
||||||
wp->sync_dirty_size = 0;
|
wp->sync_dirty_size = 0;
|
||||||
wp->sync_scrolled = 0;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Defer scrolling until the synchronized update ends. */
|
|
||||||
static void
|
|
||||||
screen_write_sync_scroll_dirty(struct screen_write_ctx *ctx)
|
|
||||||
{
|
|
||||||
struct window_pane *wp = ctx->wp;
|
|
||||||
struct screen *s = ctx->s;
|
|
||||||
u_int n = ctx->scrolled, y, sy = screen_size_y(s);
|
|
||||||
u_int ry = s->rlower + 1 - s->rupper;
|
|
||||||
bitstr_t *bs;
|
|
||||||
|
|
||||||
if (n > ry)
|
|
||||||
n = ry;
|
|
||||||
if (wp == NULL ||
|
|
||||||
n == ry ||
|
|
||||||
s->rlower >= sy ||
|
|
||||||
window_pane_scrollbar_overlay_visible(wp)) {
|
|
||||||
screen_write_should_draw_lines(ctx, s->rupper, ry);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (wp->flags & (PANE_REDRAW|PANE_DROP))
|
|
||||||
return;
|
|
||||||
bs = screen_write_sync_allocate_dirty(wp, sy);
|
|
||||||
|
|
||||||
if (wp->sync_scrolled != 0 &&
|
|
||||||
(wp->sync_rupper != s->rupper ||
|
|
||||||
wp->sync_rlower != s->rlower ||
|
|
||||||
wp->sync_bg != ctx->bg)) {
|
|
||||||
log_debug("%s: %%%u region changed, redrawing all", __func__,
|
|
||||||
wp->id);
|
|
||||||
bit_nset(bs, 0, sy - 1);
|
|
||||||
wp->sync_scrolled = 0;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Keep dirty lines aligned with the scrolled grid. */
|
|
||||||
for (y = s->rupper; y + n <= s->rlower; y++) {
|
|
||||||
if (bit_test(bs, y + n))
|
|
||||||
bit_set(bs, y);
|
|
||||||
else
|
|
||||||
bit_clear(bs, y);
|
|
||||||
}
|
|
||||||
bit_nset(bs, s->rlower + 1 - n, s->rlower);
|
|
||||||
|
|
||||||
wp->sync_rupper = s->rupper;
|
|
||||||
wp->sync_rlower = s->rlower;
|
|
||||||
wp->sync_bg = ctx->bg;
|
|
||||||
wp->sync_scrolled += n;
|
|
||||||
if (wp->sync_scrolled >= ry) {
|
|
||||||
bit_nset(bs, s->rupper, s->rlower);
|
|
||||||
wp->sync_scrolled = 0;
|
|
||||||
}
|
|
||||||
window_pane_scrollbar_redraw(wp);
|
|
||||||
|
|
||||||
log_debug("%s: %%%u deferred scroll of %u (region %u-%u)", __func__,
|
|
||||||
wp->id, wp->sync_scrolled, s->rupper, s->rlower);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Redraw the scrolled lines for a client which cannot scroll them. */
|
|
||||||
static void
|
|
||||||
screen_write_sync_redraw_cb(const struct tty_ctx *ttyctx)
|
|
||||||
{
|
|
||||||
struct window_pane *wp = ttyctx->arg;
|
|
||||||
|
|
||||||
bit_nset(wp->sync_dirty, wp->sync_rupper, wp->sync_rlower);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Send the deferred scroll to the client. */
|
|
||||||
static void
|
|
||||||
screen_write_sync_apply_scroll(struct screen_write_ctx *ctx,
|
|
||||||
struct tty_ctx *ttyctx)
|
|
||||||
{
|
|
||||||
struct window_pane *wp = ctx->wp;
|
|
||||||
struct screen *s = ctx->s;
|
|
||||||
u_int sy = screen_size_y(s), n = wp->sync_scrolled;
|
|
||||||
tty_ctx_redraw_cb redraw_cb;
|
|
||||||
|
|
||||||
wp->sync_scrolled = 0;
|
|
||||||
if (wp->flags & PANE_REDRAW)
|
|
||||||
return;
|
|
||||||
if (wp->sync_rlower >= sy || wp->sync_rupper > wp->sync_rlower) {
|
|
||||||
bit_nset(wp->sync_dirty, 0, sy - 1);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if ((ttyctx->flags & TTY_CTX_PANE_OBSCURED) ||
|
|
||||||
window_pane_scrollbar_overlay_visible(wp)) {
|
|
||||||
bit_nset(wp->sync_dirty, wp->sync_rupper, wp->sync_rlower);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
log_debug("%s: %%%u scroll of %u (region %u-%u)", __func__, wp->id, n,
|
|
||||||
wp->sync_rupper, wp->sync_rlower);
|
|
||||||
|
|
||||||
ttyctx->orupper = wp->sync_rupper;
|
|
||||||
ttyctx->orlower = wp->sync_rlower;
|
|
||||||
if (wp->yoff + wp->sy > wp->window->sy)
|
|
||||||
ttyctx->orlower -= (wp->yoff + wp->sy - wp->window->sy);
|
|
||||||
ttyctx->n = n;
|
|
||||||
ttyctx->bg = wp->sync_bg;
|
|
||||||
redraw_cb = ttyctx->redraw_cb;
|
|
||||||
ttyctx->redraw_cb = screen_write_sync_redraw_cb;
|
|
||||||
tty_write(tty_cmd_scrollup, ttyctx);
|
|
||||||
|
|
||||||
ttyctx->redraw_cb = redraw_cb;
|
|
||||||
ttyctx->orupper = s->rupper;
|
|
||||||
ttyctx->orlower = s->rlower;
|
|
||||||
ttyctx->n = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Redraw all visible cells in a pane. */
|
/* Redraw all visible cells in a pane. */
|
||||||
static void
|
static void
|
||||||
screen_write_redraw_pane(struct screen_write_ctx *ctx, struct tty_ctx *ttyctx)
|
screen_write_redraw_pane(struct screen_write_ctx *ctx, struct tty_ctx *ttyctx)
|
||||||
@@ -2552,8 +2427,10 @@ screen_write_collect_flush(struct screen_write_ctx *ctx, int scroll_only,
|
|||||||
if (wp != NULL && (wp->flags & (PANE_REDRAW|PANE_DROP)))
|
if (wp != NULL && (wp->flags & (PANE_REDRAW|PANE_DROP)))
|
||||||
goto discard;
|
goto discard;
|
||||||
if (s->mode & MODE_SYNC) {
|
if (s->mode & MODE_SYNC) {
|
||||||
if (ctx->scrolled != 0)
|
if (ctx->scrolled != 0) {
|
||||||
screen_write_sync_scroll_dirty(ctx);
|
screen_write_should_draw_lines(ctx, s->rupper,
|
||||||
|
s->rlower + 1 - s->rupper);
|
||||||
|
}
|
||||||
for (y = 0; y < screen_size_y(s); y++) {
|
for (y = 0; y < screen_size_y(s); y++) {
|
||||||
cl = &s->write_list[y];
|
cl = &s->write_list[y];
|
||||||
if (!TAILQ_EMPTY(&cl->items))
|
if (!TAILQ_EMPTY(&cl->items))
|
||||||
|
|||||||
+154
-12
@@ -67,6 +67,77 @@ server_client_how_many(void)
|
|||||||
return (n);
|
return (n);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Overlay timer callback. */
|
||||||
|
static void
|
||||||
|
server_client_overlay_timer(__unused int fd, __unused short events, void *data)
|
||||||
|
{
|
||||||
|
server_client_clear_overlay(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Set an overlay on client. */
|
||||||
|
void
|
||||||
|
server_client_set_overlay(struct client *c, u_int delay,
|
||||||
|
overlay_check_cb checkcb, overlay_mode_cb modecb,
|
||||||
|
overlay_draw_cb drawcb, overlay_key_cb keycb, overlay_free_cb freecb,
|
||||||
|
overlay_resize_cb resizecb, void *data)
|
||||||
|
{
|
||||||
|
struct timeval tv;
|
||||||
|
|
||||||
|
if (c->overlay_draw != NULL)
|
||||||
|
server_client_clear_overlay(c);
|
||||||
|
|
||||||
|
tv.tv_sec = delay / 1000;
|
||||||
|
tv.tv_usec = (delay % 1000) * 1000L;
|
||||||
|
|
||||||
|
if (event_initialized(&c->overlay_timer))
|
||||||
|
evtimer_del(&c->overlay_timer);
|
||||||
|
evtimer_set(&c->overlay_timer, server_client_overlay_timer, c);
|
||||||
|
if (delay != 0)
|
||||||
|
evtimer_add(&c->overlay_timer, &tv);
|
||||||
|
|
||||||
|
c->overlay_check = checkcb;
|
||||||
|
c->overlay_mode = modecb;
|
||||||
|
c->overlay_draw = drawcb;
|
||||||
|
c->overlay_key = keycb;
|
||||||
|
c->overlay_free = freecb;
|
||||||
|
c->overlay_resize = resizecb;
|
||||||
|
c->overlay_data = data;
|
||||||
|
|
||||||
|
if (c->overlay_check == NULL)
|
||||||
|
c->tty.flags |= TTY_FREEZE;
|
||||||
|
if (c->overlay_mode == NULL)
|
||||||
|
c->tty.flags |= TTY_NOCURSOR;
|
||||||
|
window_update_focus(c->session->curw->window);
|
||||||
|
server_redraw_client(c);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Clear overlay mode on client. */
|
||||||
|
void
|
||||||
|
server_client_clear_overlay(struct client *c)
|
||||||
|
{
|
||||||
|
if (c->overlay_draw == NULL)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (event_initialized(&c->overlay_timer))
|
||||||
|
evtimer_del(&c->overlay_timer);
|
||||||
|
|
||||||
|
if (c->overlay_free != NULL)
|
||||||
|
c->overlay_free(c, c->overlay_data);
|
||||||
|
|
||||||
|
c->overlay_check = NULL;
|
||||||
|
c->overlay_mode = NULL;
|
||||||
|
c->overlay_draw = NULL;
|
||||||
|
c->overlay_key = NULL;
|
||||||
|
c->overlay_free = NULL;
|
||||||
|
c->overlay_resize = NULL;
|
||||||
|
c->overlay_data = NULL;
|
||||||
|
|
||||||
|
c->tty.flags &= ~(TTY_FREEZE|TTY_NOCURSOR);
|
||||||
|
if (c->session != NULL)
|
||||||
|
window_update_focus(c->session->curw->window);
|
||||||
|
server_redraw_client(c);
|
||||||
|
}
|
||||||
|
|
||||||
/* Are these ranges empty? That is, nothing is visible. */
|
/* Are these ranges empty? That is, nothing is visible. */
|
||||||
int
|
int
|
||||||
server_client_ranges_is_empty(struct visible_ranges *r)
|
server_client_ranges_is_empty(struct visible_ranges *r)
|
||||||
@@ -90,6 +161,52 @@ server_client_ensure_ranges(struct visible_ranges *r, u_int n)
|
|||||||
r->size = n;
|
r->size = n;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Given overlay position and dimensions, return parts of the input range which
|
||||||
|
* are visible.
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
server_client_overlay_range(u_int x, u_int y, u_int sx, u_int sy, u_int px,
|
||||||
|
u_int py, u_int nx, struct visible_ranges *r)
|
||||||
|
{
|
||||||
|
u_int ox, onx;
|
||||||
|
|
||||||
|
/* Trivial case of no overlap in the y direction. */
|
||||||
|
if (py < y || py > y + sy - 1) {
|
||||||
|
server_client_ensure_ranges(r, 1);
|
||||||
|
r->ranges[0].px = px;
|
||||||
|
r->ranges[0].nx = nx;
|
||||||
|
r->used = 1;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
server_client_ensure_ranges(r, 2);
|
||||||
|
|
||||||
|
/* Visible bit to the left of the popup. */
|
||||||
|
if (px < x) {
|
||||||
|
r->ranges[0].px = px;
|
||||||
|
r->ranges[0].nx = x - px;
|
||||||
|
if (r->ranges[0].nx > nx)
|
||||||
|
r->ranges[0].nx = nx;
|
||||||
|
} else {
|
||||||
|
r->ranges[0].px = 0;
|
||||||
|
r->ranges[0].nx = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Visible bit to the right of the popup. */
|
||||||
|
ox = x + sx;
|
||||||
|
if (px > ox)
|
||||||
|
ox = px;
|
||||||
|
onx = px + nx;
|
||||||
|
if (onx > ox) {
|
||||||
|
r->ranges[1].px = ox;
|
||||||
|
r->ranges[1].nx = onx - ox;
|
||||||
|
} else {
|
||||||
|
r->ranges[1].px = 0;
|
||||||
|
r->ranges[1].nx = 0;
|
||||||
|
}
|
||||||
|
r->used = 2;
|
||||||
|
}
|
||||||
|
|
||||||
/* Check if this client is inside this server. */
|
/* Check if this client is inside this server. */
|
||||||
int
|
int
|
||||||
server_client_check_nested(struct client *c)
|
server_client_check_nested(struct client *c)
|
||||||
@@ -373,6 +490,7 @@ server_client_lost(struct client *c)
|
|||||||
cfg_client = NULL;
|
cfg_client = NULL;
|
||||||
c->flags |= CLIENT_DEAD;
|
c->flags |= CLIENT_DEAD;
|
||||||
|
|
||||||
|
server_client_clear_overlay(c);
|
||||||
status_prompt_clear(c);
|
status_prompt_clear(c);
|
||||||
status_message_clear(c);
|
status_message_clear(c);
|
||||||
|
|
||||||
@@ -1631,6 +1749,10 @@ server_client_handle_key0(struct client *c, struct key_event *event,
|
|||||||
if (s == NULL || (c->flags & CLIENT_UNATTACHEDFLAGS))
|
if (s == NULL || (c->flags & CLIENT_UNATTACHEDFLAGS))
|
||||||
return (0);
|
return (0);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Handle theme reporting keys before overlays so they work even when a
|
||||||
|
* popup is open.
|
||||||
|
*/
|
||||||
if (event->key == KEYC_REPORT_LIGHT_THEME) {
|
if (event->key == KEYC_REPORT_LIGHT_THEME) {
|
||||||
server_client_report_theme(c, THEME_LIGHT);
|
server_client_report_theme(c, THEME_LIGHT);
|
||||||
return (0);
|
return (0);
|
||||||
@@ -1641,9 +1763,10 @@ server_client_handle_key0(struct client *c, struct key_event *event,
|
|||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Dead panes waiting for a key, modal cancel keys, panes capturing all keys
|
* Key presses in overlay mode, dead panes waiting for a key, modal cancel
|
||||||
* and the command prompt are special cases. The queue might be blocked so
|
* keys, panes capturing all keys and the command prompt are special cases.
|
||||||
* they need to be processed immediately rather than queued.
|
* The queue might be blocked so they need to be processed immediately
|
||||||
|
* rather than queued.
|
||||||
*/
|
*/
|
||||||
if (~c->flags & CLIENT_READONLY) {
|
if (~c->flags & CLIENT_READONLY) {
|
||||||
if (c->message_string != NULL) {
|
if (c->message_string != NULL) {
|
||||||
@@ -1652,11 +1775,22 @@ server_client_handle_key0(struct client *c, struct key_event *event,
|
|||||||
status_message_clear(c);
|
status_message_clear(c);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (c->overlay_key != NULL) {
|
||||||
|
switch (c->overlay_key(c, c->overlay_data, event)) {
|
||||||
|
case 0:
|
||||||
|
return (0);
|
||||||
|
case 1:
|
||||||
|
server_client_clear_overlay(c);
|
||||||
|
return (0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
server_client_clear_overlay(c);
|
||||||
|
|
||||||
wp = s->curw->window->active;
|
wp = s->curw->window->active;
|
||||||
if (server_client_handle_dead_key(wp, event->key))
|
if (server_client_handle_dead_key(wp, event->key))
|
||||||
return (0);
|
return (0);
|
||||||
if (wp != NULL &&
|
if (wp != NULL && wp == wp->window->modal &&
|
||||||
wp == wp->window->modal &&
|
|
||||||
(wp->flags & PANE_CLOSEONCANCEL) &&
|
(wp->flags & PANE_CLOSEONCANCEL) &&
|
||||||
(event->key == '\033' || event->key == ('c'|KEYC_CTRL))) {
|
(event->key == '\033' || event->key == ('c'|KEYC_CTRL))) {
|
||||||
server_kill_pane(wp);
|
server_kill_pane(wp);
|
||||||
@@ -2046,8 +2180,11 @@ server_client_reset_state(struct client *c)
|
|||||||
flags = (tty->flags & TTY_BLOCK);
|
flags = (tty->flags & TTY_BLOCK);
|
||||||
tty->flags &= ~TTY_BLOCK;
|
tty->flags &= ~TTY_BLOCK;
|
||||||
|
|
||||||
/* Get mode from the menu if any, else from the screen. */
|
/* Get mode from overlay if any, else from screen. */
|
||||||
if (w->menu != NULL) {
|
if (c->overlay_draw != NULL) {
|
||||||
|
if (c->overlay_mode != NULL)
|
||||||
|
s = c->overlay_mode(c, c->overlay_data, &cx, &cy);
|
||||||
|
} else if (w->menu != NULL) {
|
||||||
menu_get_cursor(w->menu, &cx, &cy);
|
menu_get_cursor(w->menu, &cx, &cy);
|
||||||
s = menu_screen(w->menu);
|
s = menu_screen(w->menu);
|
||||||
} else if (wp != NULL && c->prompt == NULL)
|
} else if (wp != NULL && c->prompt == NULL)
|
||||||
@@ -2069,7 +2206,7 @@ server_client_reset_state(struct client *c)
|
|||||||
if (c->prompt != NULL) {
|
if (c->prompt != NULL) {
|
||||||
prompt = 1;
|
prompt = 1;
|
||||||
status_prompt_cursor(c, &cx, &cy);
|
status_prompt_cursor(c, &cx, &cy);
|
||||||
} else if (wp != NULL) {
|
} else if (wp != NULL && c->overlay_draw == NULL) {
|
||||||
if (w->menu != NULL) {
|
if (w->menu != NULL) {
|
||||||
tty_window_offset(tty, &ox, &oy, &sx, &sy);
|
tty_window_offset(tty, &ox, &oy, &sx, &sy);
|
||||||
if (cx < ox || cx >= ox + sx ||
|
if (cx < ox || cx >= ox + sx ||
|
||||||
@@ -2124,7 +2261,7 @@ server_client_reset_state(struct client *c)
|
|||||||
if (!cursor)
|
if (!cursor)
|
||||||
mode &= ~MODE_CURSOR;
|
mode &= ~MODE_CURSOR;
|
||||||
}
|
}
|
||||||
} else if (s == NULL)
|
} else if (c->overlay_mode == NULL || s == NULL)
|
||||||
mode &= ~MODE_CURSOR;
|
mode &= ~MODE_CURSOR;
|
||||||
if (~pane_mode & MODE_SYNC) {
|
if (~pane_mode & MODE_SYNC) {
|
||||||
log_debug("%s: cursor to %u,%u", __func__, cx, cy);
|
log_debug("%s: cursor to %u,%u", __func__, cx, cy);
|
||||||
@@ -2141,7 +2278,7 @@ server_client_reset_state(struct client *c)
|
|||||||
* movement events.
|
* movement events.
|
||||||
*/
|
*/
|
||||||
if (options_get_number(oo, "mouse")) {
|
if (options_get_number(oo, "mouse")) {
|
||||||
if (w->menu == NULL) {
|
if (c->overlay_draw == NULL && w->menu == NULL) {
|
||||||
mode &= ~ALL_MOUSE_MODES;
|
mode &= ~ALL_MOUSE_MODES;
|
||||||
TAILQ_FOREACH(loop, &w->panes, entry) {
|
TAILQ_FOREACH(loop, &w->panes, entry) {
|
||||||
if (loop->screen->mode & MODE_MOUSE_ALL)
|
if (loop->screen->mode & MODE_MOUSE_ALL)
|
||||||
@@ -2157,7 +2294,7 @@ server_client_reset_state(struct client *c)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Clear bracketed paste mode if at the prompt. */
|
/* Clear bracketed paste mode if at the prompt. */
|
||||||
if (prompt)
|
if (c->overlay_draw == NULL && prompt)
|
||||||
mode &= ~MODE_BRACKETPASTE;
|
mode &= ~MODE_BRACKETPASTE;
|
||||||
|
|
||||||
/* Set the terminal mode and reset attributes. */
|
/* Set the terminal mode and reset attributes. */
|
||||||
@@ -2359,10 +2496,11 @@ server_client_check_redraw(struct client *c)
|
|||||||
if (c->flags & (CLIENT_CONTROL|CLIENT_SUSPENDED))
|
if (c->flags & (CLIENT_CONTROL|CLIENT_SUSPENDED))
|
||||||
return;
|
return;
|
||||||
if (c->flags & CLIENT_ALLREDRAWFLAGS) {
|
if (c->flags & CLIENT_ALLREDRAWFLAGS) {
|
||||||
log_debug("%s: redraw%s%s%s%s", c->name,
|
log_debug("%s: redraw%s%s%s%s%s", c->name,
|
||||||
(c->flags & CLIENT_REDRAWWINDOW) ? " window" : "",
|
(c->flags & CLIENT_REDRAWWINDOW) ? " window" : "",
|
||||||
(c->flags & CLIENT_REDRAWSTATUS) ? " status" : "",
|
(c->flags & CLIENT_REDRAWSTATUS) ? " status" : "",
|
||||||
(c->flags & CLIENT_REDRAWBORDERS) ? " borders" : "",
|
(c->flags & CLIENT_REDRAWBORDERS) ? " borders" : "",
|
||||||
|
(c->flags & CLIENT_REDRAWOVERLAY) ? " overlay" : "",
|
||||||
(c->flags & CLIENT_REDRAWMENU) ? " menu" : "");
|
(c->flags & CLIENT_REDRAWMENU) ? " menu" : "");
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2570,6 +2708,10 @@ server_client_dispatch(struct imsg *imsg, void *arg)
|
|||||||
tty_resize(&c->tty);
|
tty_resize(&c->tty);
|
||||||
tty_repeat_requests(&c->tty, 0);
|
tty_repeat_requests(&c->tty, 0);
|
||||||
recalculate_sizes();
|
recalculate_sizes();
|
||||||
|
if (c->overlay_resize == NULL)
|
||||||
|
server_client_clear_overlay(c);
|
||||||
|
else
|
||||||
|
c->overlay_resize(c, c->overlay_data);
|
||||||
server_redraw_client(c);
|
server_redraw_client(c);
|
||||||
if (c->session != NULL)
|
if (c->session != NULL)
|
||||||
server_client_fire_resized(c, old_sx, old_sy);
|
server_client_fire_resized(c, old_sx, old_sy);
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
/* $OpenBSD: status.c,v 1.274 2026/09/21 10:22:31 nicm Exp $ */
|
/* $OpenBSD: status.c,v 1.273 2026/07/06 14:29:10 nicm Exp $ */
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>
|
* Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>
|
||||||
@@ -577,6 +577,8 @@ status_prompt_set(struct client *c, struct cmd_find_state *fs,
|
|||||||
struct prompt_create_data pd;
|
struct prompt_create_data pd;
|
||||||
struct status_prompt_data *spd;
|
struct status_prompt_data *spd;
|
||||||
|
|
||||||
|
server_client_clear_overlay(c);
|
||||||
|
|
||||||
status_message_clear(c);
|
status_message_clear(c);
|
||||||
status_prompt_clear(c);
|
status_prompt_clear(c);
|
||||||
status_push_screen(c);
|
status_push_screen(c);
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
.\" $OpenBSD: tmux.1,v 1.1173 2026/09/23 12:37:50 nicm Exp $
|
.\" $OpenBSD: tmux.1,v 1.1172 2026/09/22 06:58:05 nicm Exp $
|
||||||
.\"
|
.\"
|
||||||
.\" Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>
|
.\" Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>
|
||||||
.\"
|
.\"
|
||||||
@@ -14,7 +14,7 @@
|
|||||||
.\" IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
|
.\" IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
|
||||||
.\" OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
.\" OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||||
.\"
|
.\"
|
||||||
.Dd $Mdocdate: September 23 2026 $
|
.Dd $Mdocdate: September 22 2026 $
|
||||||
.Dt TMUX 1
|
.Dt TMUX 1
|
||||||
.Os
|
.Os
|
||||||
.Sh NAME
|
.Sh NAME
|
||||||
@@ -994,19 +994,13 @@ Will run:
|
|||||||
/bin/sh \-c \[aq]vi \[ti]/.tmux.conf\[aq]
|
/bin/sh \-c \[aq]vi \[ti]/.tmux.conf\[aq]
|
||||||
.Ed
|
.Ed
|
||||||
.Pp
|
.Pp
|
||||||
Unless specified, tmux uses the value of
|
|
||||||
.Ic default\-shell
|
|
||||||
in place of
|
|
||||||
.Pa /bin/sh .
|
|
||||||
.Pp
|
|
||||||
Additionally, the
|
Additionally, the
|
||||||
.Ic new\-window ,
|
.Ic new\-window ,
|
||||||
.Ic new\-session ,
|
.Ic new\-session ,
|
||||||
.Ic split\-window ,
|
.Ic split\-window ,
|
||||||
.Ic respawn\-window ,
|
.Ic respawn\-window
|
||||||
.Ic respawn\-pane
|
|
||||||
and
|
and
|
||||||
.Ic display\-popup
|
.Ic respawn\-pane
|
||||||
commands allow
|
commands allow
|
||||||
.Ar shell\-command
|
.Ar shell\-command
|
||||||
to be given as multiple arguments and executed directly (without
|
to be given as multiple arguments and executed directly (without
|
||||||
@@ -5362,16 +5356,10 @@ section on how to specify
|
|||||||
.Ar style .
|
.Ar style .
|
||||||
.It Ic menu\-border\-lines Ar type
|
.It Ic menu\-border\-lines Ar type
|
||||||
Set the type of characters used for drawing menu borders.
|
Set the type of characters used for drawing menu borders.
|
||||||
.Ar type
|
See
|
||||||
may be one of
|
.Ic popup\-border\-lines
|
||||||
.Ic single ,
|
for possible values for
|
||||||
.Ic rounded ,
|
.Ar border\-lines .
|
||||||
.Ic double ,
|
|
||||||
.Ic heavy ,
|
|
||||||
.Ic simple ,
|
|
||||||
.Ic padded ,
|
|
||||||
or
|
|
||||||
.Ic none .
|
|
||||||
.It Ic message\-command\-style Ar style
|
.It Ic message\-command\-style Ar style
|
||||||
Set status line message command style.
|
Set status line message command style.
|
||||||
This is used for the command prompt with
|
This is used for the command prompt with
|
||||||
@@ -5995,8 +5983,6 @@ single lines using ACS or UTF\-8 characters
|
|||||||
double lines using UTF\-8 characters
|
double lines using UTF\-8 characters
|
||||||
.It heavy
|
.It heavy
|
||||||
heavy lines using UTF\-8 characters
|
heavy lines using UTF\-8 characters
|
||||||
.It rounded
|
|
||||||
single lines with rounded corners using UTF\-8 characters
|
|
||||||
.It simple
|
.It simple
|
||||||
simple ASCII characters
|
simple ASCII characters
|
||||||
.It number
|
.It number
|
||||||
@@ -6031,6 +6017,48 @@ see the
|
|||||||
section.
|
section.
|
||||||
Attributes are ignored.
|
Attributes are ignored.
|
||||||
.Pp
|
.Pp
|
||||||
|
.It Ic popup\-style Ar style
|
||||||
|
Set the popup style.
|
||||||
|
See the
|
||||||
|
.Sx STYLES
|
||||||
|
section on how to specify
|
||||||
|
.Ar style .
|
||||||
|
Attributes are ignored.
|
||||||
|
.Pp
|
||||||
|
.It Ic popup\-border\-style Ar style
|
||||||
|
Set the popup border style.
|
||||||
|
See the
|
||||||
|
.Sx STYLES
|
||||||
|
section on how to specify
|
||||||
|
.Ar style .
|
||||||
|
Attributes are ignored.
|
||||||
|
.Pp
|
||||||
|
.It Ic popup\-border\-lines Ar type
|
||||||
|
Set the type of characters used for drawing popup borders.
|
||||||
|
.Ar type
|
||||||
|
may be one of:
|
||||||
|
.Bl -tag -width Ds
|
||||||
|
.It single
|
||||||
|
single lines using ACS or UTF\-8 characters (default)
|
||||||
|
.It rounded
|
||||||
|
variation of single with rounded corners using UTF\-8 characters
|
||||||
|
.It double
|
||||||
|
double lines using UTF\-8 characters
|
||||||
|
.It heavy
|
||||||
|
heavy lines using UTF\-8 characters
|
||||||
|
.It simple
|
||||||
|
simple ASCII characters
|
||||||
|
.It padded
|
||||||
|
simple ASCII space character
|
||||||
|
.It none
|
||||||
|
no border
|
||||||
|
.El
|
||||||
|
.Pp
|
||||||
|
.Ql double
|
||||||
|
and
|
||||||
|
.Ql heavy
|
||||||
|
will fall back to standard ACS line drawing when UTF\-8 is not supported.
|
||||||
|
.Pp
|
||||||
.It Xo Ic pane\-scrollbars
|
.It Xo Ic pane\-scrollbars
|
||||||
.Op Ic off | modal | on | auto\-hide
|
.Op Ic off | modal | on | auto\-hide
|
||||||
.Xc
|
.Xc
|
||||||
@@ -8177,16 +8205,10 @@ command should be omitted.
|
|||||||
.Pp
|
.Pp
|
||||||
.Fl b
|
.Fl b
|
||||||
sets the type of characters used for drawing menu borders.
|
sets the type of characters used for drawing menu borders.
|
||||||
.Ar border\-lines
|
See
|
||||||
may be one of
|
.Ic popup\-border\-lines
|
||||||
.Ic single ,
|
for possible values for
|
||||||
.Ic rounded ,
|
.Ar border\-lines .
|
||||||
.Ic double ,
|
|
||||||
.Ic heavy ,
|
|
||||||
.Ic simple ,
|
|
||||||
.Ic padded ,
|
|
||||||
or
|
|
||||||
.Ic none .
|
|
||||||
.Pp
|
.Pp
|
||||||
.Fl H
|
.Fl H
|
||||||
sets the style for the selected menu item (see
|
sets the style for the selected menu item (see
|
||||||
@@ -8222,18 +8244,15 @@ Both may be a row or column number, or one of the following special values:
|
|||||||
.It Li "S" Ta Fl y Ta "The line above or below the status line"
|
.It Li "S" Ta Fl y Ta "The line above or below the status line"
|
||||||
.El
|
.El
|
||||||
.Pp
|
.Pp
|
||||||
Or a format, which is expanded with the following additional variables.
|
Or a format, which is expanded including the following additional variables:
|
||||||
The
|
|
||||||
.Ql popup_
|
|
||||||
prefix is retained for compatibility:
|
|
||||||
.Bl -column "XXXXXXXXXXXXXXXXXXXXXXXXXX" -offset indent
|
.Bl -column "XXXXXXXXXXXXXXXXXXXXXXXXXX" -offset indent
|
||||||
.It Sy "Variable name" Ta Sy "Replaced with"
|
.It Sy "Variable name" Ta Sy "Replaced with"
|
||||||
.It Li "popup_centre_x" Ta "Centered in the window"
|
.It Li "popup_centre_x" Ta "Centered in the client"
|
||||||
.It Li "popup_centre_y" Ta "Centered in the window"
|
.It Li "popup_centre_y" Ta "Centered in the client"
|
||||||
.It Li "popup_height" Ta "Height of the menu"
|
.It Li "popup_height" Ta "Height of menu or popup"
|
||||||
.It Li "popup_last_x" Ta "Left of the last menu"
|
.It Li "popup_last_x" Ta "Left of the last menu"
|
||||||
.It Li "popup_last_y" Ta "Bottom of the last menu"
|
.It Li "popup_last_y" Ta "Bottom of the last menu"
|
||||||
.It Li "popup_mouse_bottom" Ta "Bottom at the mouse"
|
.It Li "popup_mouse_bottom" Ta "Bottom of at the mouse"
|
||||||
.It Li "popup_mouse_centre_x" Ta "Horizontal centre at the mouse"
|
.It Li "popup_mouse_centre_x" Ta "Horizontal centre at the mouse"
|
||||||
.It Li "popup_mouse_centre_y" Ta "Vertical centre at the mouse"
|
.It Li "popup_mouse_centre_y" Ta "Vertical centre at the mouse"
|
||||||
.It Li "popup_mouse_top" Ta "Top at the mouse"
|
.It Li "popup_mouse_top" Ta "Top at the mouse"
|
||||||
@@ -8244,7 +8263,7 @@ prefix is retained for compatibility:
|
|||||||
.It Li "popup_pane_right" Ta "Right of the pane"
|
.It Li "popup_pane_right" Ta "Right of the pane"
|
||||||
.It Li "popup_pane_top" Ta "Top of the pane"
|
.It Li "popup_pane_top" Ta "Top of the pane"
|
||||||
.It Li "popup_status_line_y" Ta "Above or below the status line"
|
.It Li "popup_status_line_y" Ta "Above or below the status line"
|
||||||
.It Li "popup_width" Ta "Width of the menu"
|
.It Li "popup_width" Ta "Width of menu or popup"
|
||||||
.It Li "popup_window_status_line_x" Ta "At the window position in status line"
|
.It Li "popup_window_status_line_x" Ta "At the window position in status line"
|
||||||
.It Li "popup_window_status_line_y" Ta "At the status line showing the window"
|
.It Li "popup_window_status_line_y" Ta "At the status line showing the window"
|
||||||
.El
|
.El
|
||||||
@@ -8330,6 +8349,116 @@ lists the format variables and their values.
|
|||||||
.Fl I
|
.Fl I
|
||||||
forwards any input read from stdin to the empty pane given by
|
forwards any input read from stdin to the empty pane given by
|
||||||
.Ar target\-pane .
|
.Ar target\-pane .
|
||||||
|
.Tg popup
|
||||||
|
.It Xo Ic display\-popup
|
||||||
|
.Op Fl BCEkN
|
||||||
|
.Op Fl b Ar border\-lines
|
||||||
|
.Op Fl c Ar target\-client
|
||||||
|
.Op Fl d Ar start\-directory
|
||||||
|
.Op Fl e Ar environment
|
||||||
|
.Op Fl h Ar height
|
||||||
|
.Op Fl s Ar style
|
||||||
|
.Op Fl S Ar border\-style
|
||||||
|
.Op Fl t Ar target\-pane
|
||||||
|
.Op Fl T Ar title
|
||||||
|
.Op Fl w Ar width
|
||||||
|
.Op Fl x Ar position
|
||||||
|
.Op Fl y Ar position
|
||||||
|
.Op Ar shell\-command Op Ar argument ...
|
||||||
|
.Xc
|
||||||
|
.D1 Pq alias: Ic popup
|
||||||
|
Display a popup running
|
||||||
|
.Ar shell\-command
|
||||||
|
(or
|
||||||
|
.Ar default\-command
|
||||||
|
when omitted) on
|
||||||
|
.Ar target\-client .
|
||||||
|
A popup is a rectangular box drawn over the top of any panes.
|
||||||
|
If the command is run inside an existing popup, that popup is modified.
|
||||||
|
Only the
|
||||||
|
.Fl b ,
|
||||||
|
.Fl B ,
|
||||||
|
.Fl C ,
|
||||||
|
.Fl E ,
|
||||||
|
.Fl EE ,
|
||||||
|
.Fl K ,
|
||||||
|
.Fl N ,
|
||||||
|
.Fl s ,
|
||||||
|
and
|
||||||
|
.Fl S
|
||||||
|
options are accepted in this case;
|
||||||
|
all others are ignored.
|
||||||
|
.Pp
|
||||||
|
.Fl E
|
||||||
|
closes the popup automatically when
|
||||||
|
.Ar shell\-command
|
||||||
|
exits.
|
||||||
|
Two
|
||||||
|
.Fl E
|
||||||
|
closes the popup only if
|
||||||
|
.Ar shell\-command
|
||||||
|
exited with success.
|
||||||
|
.Fl k
|
||||||
|
allows any key to dismiss the popup instead of only
|
||||||
|
.Ql Escape
|
||||||
|
or
|
||||||
|
.Ql C\-c .
|
||||||
|
.Pp
|
||||||
|
.Fl x
|
||||||
|
and
|
||||||
|
.Fl y
|
||||||
|
give the position of the popup, they have the same meaning as for the
|
||||||
|
.Ic display\-menu
|
||||||
|
command.
|
||||||
|
.Fl w
|
||||||
|
and
|
||||||
|
.Fl h
|
||||||
|
give the width and height - both may be a percentage (followed by
|
||||||
|
.Ql % ) .
|
||||||
|
If omitted, half of the terminal size is used.
|
||||||
|
.Pp
|
||||||
|
.Fl B
|
||||||
|
does not surround the popup by a border.
|
||||||
|
.Pp
|
||||||
|
.Fl b
|
||||||
|
sets the type of characters used for drawing popup borders.
|
||||||
|
When
|
||||||
|
.Fl B
|
||||||
|
is specified, the
|
||||||
|
.Fl b
|
||||||
|
option is ignored.
|
||||||
|
See
|
||||||
|
.Ic popup\-border\-lines
|
||||||
|
for possible values for
|
||||||
|
.Ar border\-lines .
|
||||||
|
.Pp
|
||||||
|
.Fl s
|
||||||
|
sets the style for the popup and
|
||||||
|
.Fl S
|
||||||
|
sets the style for the popup border (see
|
||||||
|
.Sx STYLES ) .
|
||||||
|
.Pp
|
||||||
|
.Fl e
|
||||||
|
takes the form
|
||||||
|
.Ql VARIABLE=value
|
||||||
|
and sets an environment variable for the popup; it may be specified multiple
|
||||||
|
times.
|
||||||
|
.Pp
|
||||||
|
.Fl T
|
||||||
|
is a format for the popup title (see
|
||||||
|
.Sx FORMATS ) .
|
||||||
|
.Pp
|
||||||
|
The
|
||||||
|
.Fl C
|
||||||
|
flag closes any popup on the client.
|
||||||
|
.Pp
|
||||||
|
.Fl N
|
||||||
|
disables any previously specified
|
||||||
|
.Fl E ,
|
||||||
|
.Fl EE ,
|
||||||
|
or
|
||||||
|
.Fl k
|
||||||
|
option.
|
||||||
.Tg showphist
|
.Tg showphist
|
||||||
.It Xo Ic show\-prompt\-history
|
.It Xo Ic show\-prompt\-history
|
||||||
.Op Fl T Ar prompt\-type
|
.Op Fl T Ar prompt\-type
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
/* $OpenBSD: tmux.h,v 1.1448 2026/09/24 11:19:39 nicm Exp $ */
|
/* $OpenBSD: tmux.h,v 1.1447 2026/09/22 14:10:26 nicm Exp $ */
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>
|
* Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>
|
||||||
@@ -1152,8 +1152,7 @@ enum pane_lines {
|
|||||||
PANE_LINES_SIMPLE,
|
PANE_LINES_SIMPLE,
|
||||||
PANE_LINES_NUMBER,
|
PANE_LINES_NUMBER,
|
||||||
PANE_LINES_SPACES,
|
PANE_LINES_SPACES,
|
||||||
PANE_LINES_NONE,
|
PANE_LINES_NONE
|
||||||
PANE_LINES_ROUNDED
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/* Pane border indicator option. */
|
/* Pane border indicator option. */
|
||||||
@@ -1184,7 +1183,6 @@ struct menu {
|
|||||||
struct menu_item *items;
|
struct menu_item *items;
|
||||||
u_int count;
|
u_int count;
|
||||||
u_int width;
|
u_int width;
|
||||||
u_int item_width;
|
|
||||||
};
|
};
|
||||||
typedef void (*menu_choice_cb)(struct menu *, u_int, key_code, void *);
|
typedef void (*menu_choice_cb)(struct menu *, u_int, key_code, void *);
|
||||||
|
|
||||||
@@ -1347,10 +1345,6 @@ struct window_pane {
|
|||||||
|
|
||||||
bitstr_t *sync_dirty;
|
bitstr_t *sync_dirty;
|
||||||
u_int sync_dirty_size;
|
u_int sync_dirty_size;
|
||||||
u_int sync_scrolled;
|
|
||||||
u_int sync_rupper;
|
|
||||||
u_int sync_rlower;
|
|
||||||
u_int sync_bg;
|
|
||||||
|
|
||||||
u_int sb_slider_y;
|
u_int sb_slider_y;
|
||||||
u_int sb_slider_h;
|
u_int sb_slider_h;
|
||||||
@@ -1805,6 +1799,7 @@ struct tty {
|
|||||||
size_t discarded;
|
size_t discarded;
|
||||||
|
|
||||||
struct termios tio;
|
struct termios tio;
|
||||||
|
struct visible_ranges r;
|
||||||
|
|
||||||
struct grid_cell cell;
|
struct grid_cell cell;
|
||||||
struct grid_cell last_cell;
|
struct grid_cell last_cell;
|
||||||
@@ -1866,6 +1861,7 @@ struct tty_ctx {
|
|||||||
#define TTY_CTX_INVISIBLE_PANES 0x2
|
#define TTY_CTX_INVISIBLE_PANES 0x2
|
||||||
#define TTY_CTX_WINDOW_BIGGER 0x4
|
#define TTY_CTX_WINDOW_BIGGER 0x4
|
||||||
#define TTY_CTX_SYNC 0x8
|
#define TTY_CTX_SYNC 0x8
|
||||||
|
#define TTY_CTX_OVERLAY_SYNC 0x10
|
||||||
#define TTY_CTX_CELL_INVALIDATE 0x20
|
#define TTY_CTX_CELL_INVALIDATE 0x20
|
||||||
#define TTY_CTX_PANE_OBSCURED 0x40
|
#define TTY_CTX_PANE_OBSCURED 0x40
|
||||||
|
|
||||||
@@ -2200,6 +2196,16 @@ struct prompt_draw_data {
|
|||||||
u_int prompt_line;
|
u_int prompt_line;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/* Overlay callbacks */
|
||||||
|
typedef struct visible_ranges *(*overlay_check_cb)(struct client *, void *,
|
||||||
|
u_int, u_int, u_int);
|
||||||
|
typedef struct screen *(*overlay_mode_cb)(struct client *, void *, u_int *,
|
||||||
|
u_int *);
|
||||||
|
typedef void (*overlay_draw_cb)(struct client *, void *);
|
||||||
|
typedef int (*overlay_key_cb)(struct client *, void *, struct key_event *);
|
||||||
|
typedef void (*overlay_free_cb)(struct client *, void *);
|
||||||
|
typedef void (*overlay_resize_cb)(struct client *, void *);
|
||||||
|
|
||||||
/* Client connection. */
|
/* Client connection. */
|
||||||
struct client {
|
struct client {
|
||||||
const char *name;
|
const char *name;
|
||||||
@@ -2285,7 +2291,7 @@ struct client {
|
|||||||
#define CLIENT_SIZECHANGED 0x400000
|
#define CLIENT_SIZECHANGED 0x400000
|
||||||
#define CLIENT_STATUSOFF 0x800000
|
#define CLIENT_STATUSOFF 0x800000
|
||||||
#define CLIENT_REDRAWSTATUSALWAYS 0x1000000
|
#define CLIENT_REDRAWSTATUSALWAYS 0x1000000
|
||||||
/* 0x2000000 unused */
|
#define CLIENT_REDRAWOVERLAY 0x2000000
|
||||||
#define CLIENT_CONTROL_NOOUTPUT 0x4000000
|
#define CLIENT_CONTROL_NOOUTPUT 0x4000000
|
||||||
#define CLIENT_DEFAULTSOCKET 0x8000000
|
#define CLIENT_DEFAULTSOCKET 0x8000000
|
||||||
#define CLIENT_STARTSERVER 0x10000000
|
#define CLIENT_STARTSERVER 0x10000000
|
||||||
@@ -2306,6 +2312,7 @@ struct client {
|
|||||||
CLIENT_REDRAWSTATUS| \
|
CLIENT_REDRAWSTATUS| \
|
||||||
CLIENT_REDRAWSTATUSALWAYS| \
|
CLIENT_REDRAWSTATUSALWAYS| \
|
||||||
CLIENT_REDRAWBORDERS| \
|
CLIENT_REDRAWBORDERS| \
|
||||||
|
CLIENT_REDRAWOVERLAY| \
|
||||||
CLIENT_REDRAWMENU)
|
CLIENT_REDRAWMENU)
|
||||||
#define CLIENT_UNATTACHEDFLAGS \
|
#define CLIENT_UNATTACHEDFLAGS \
|
||||||
(CLIENT_DEAD| \
|
(CLIENT_DEAD| \
|
||||||
@@ -2351,6 +2358,15 @@ struct client {
|
|||||||
u_int pan_ox;
|
u_int pan_ox;
|
||||||
u_int pan_oy;
|
u_int pan_oy;
|
||||||
|
|
||||||
|
overlay_check_cb overlay_check;
|
||||||
|
overlay_mode_cb overlay_mode;
|
||||||
|
overlay_draw_cb overlay_draw;
|
||||||
|
overlay_key_cb overlay_key;
|
||||||
|
overlay_free_cb overlay_free;
|
||||||
|
overlay_resize_cb overlay_resize;
|
||||||
|
void *overlay_data;
|
||||||
|
struct event overlay_timer;
|
||||||
|
|
||||||
struct client_files files;
|
struct client_files files;
|
||||||
u_int source_file_depth;
|
u_int source_file_depth;
|
||||||
|
|
||||||
@@ -2952,6 +2968,8 @@ void tty_default_attributes(struct tty *, u_int,
|
|||||||
void tty_update_mode(struct tty *, int, struct screen *);
|
void tty_update_mode(struct tty *, int, struct screen *);
|
||||||
const struct grid_cell *tty_check_codeset(struct tty *,
|
const struct grid_cell *tty_check_codeset(struct tty *,
|
||||||
const struct grid_cell *);
|
const struct grid_cell *);
|
||||||
|
struct visible_ranges *tty_check_overlay_range(struct tty *, u_int, u_int,
|
||||||
|
u_int);
|
||||||
void tty_sync_start(struct tty *);
|
void tty_sync_start(struct tty *);
|
||||||
void tty_sync_end(struct tty *);
|
void tty_sync_end(struct tty *);
|
||||||
int tty_open(struct tty *, char **);
|
int tty_open(struct tty *, char **);
|
||||||
@@ -3303,8 +3321,14 @@ int server_create_socket(uint64_t, char **);
|
|||||||
|
|
||||||
/* server-client.c */
|
/* server-client.c */
|
||||||
u_int server_client_how_many(void);
|
u_int server_client_how_many(void);
|
||||||
|
void server_client_set_overlay(struct client *, u_int, overlay_check_cb,
|
||||||
|
overlay_mode_cb, overlay_draw_cb, overlay_key_cb,
|
||||||
|
overlay_free_cb, overlay_resize_cb, void *);
|
||||||
|
void server_client_clear_overlay(struct client *);
|
||||||
void server_client_ensure_ranges(struct visible_ranges *, u_int);
|
void server_client_ensure_ranges(struct visible_ranges *, u_int);
|
||||||
int server_client_ranges_is_empty(struct visible_ranges *);
|
int server_client_ranges_is_empty(struct visible_ranges *);
|
||||||
|
void server_client_overlay_range(u_int, u_int, u_int, u_int, u_int, u_int,
|
||||||
|
u_int, struct visible_ranges *);
|
||||||
void server_client_set_key_table(struct client *, const char *);
|
void server_client_set_key_table(struct client *, const char *);
|
||||||
const char *server_client_get_key_table(struct client *);
|
const char *server_client_get_key_table(struct client *);
|
||||||
int server_client_check_nested(struct client *);
|
int server_client_check_nested(struct client *);
|
||||||
@@ -3413,7 +3437,7 @@ void recalculate_sizes_now(int);
|
|||||||
/* input.c */
|
/* input.c */
|
||||||
#define INPUT_BUF_DEFAULT_SIZE 1048576
|
#define INPUT_BUF_DEFAULT_SIZE 1048576
|
||||||
struct input_ctx *input_init(struct window_pane *, struct bufferevent *,
|
struct input_ctx *input_init(struct window_pane *, struct bufferevent *,
|
||||||
struct colour_palette *);
|
struct colour_palette *, struct client *);
|
||||||
void input_free(struct input_ctx *);
|
void input_free(struct input_ctx *);
|
||||||
void input_reset(struct input_ctx *, int);
|
void input_reset(struct input_ctx *, int);
|
||||||
struct evbuffer *input_pending(struct input_ctx *);
|
struct evbuffer *input_pending(struct input_ctx *);
|
||||||
@@ -3590,7 +3614,7 @@ void screen_write_mode_clear(struct screen_write_ctx *, int);
|
|||||||
void screen_write_start_sync(struct window_pane *);
|
void screen_write_start_sync(struct window_pane *);
|
||||||
void screen_write_stop_sync(struct window_pane *);
|
void screen_write_stop_sync(struct window_pane *);
|
||||||
void screen_write_end_sync(struct screen_write_ctx *);
|
void screen_write_end_sync(struct screen_write_ctx *);
|
||||||
void screen_write_sync_clear_dirty(struct window_pane *);
|
void screen_write_clear_dirty(struct window_pane *);
|
||||||
void screen_write_cursorup(struct screen_write_ctx *, u_int);
|
void screen_write_cursorup(struct screen_write_ctx *, u_int);
|
||||||
void screen_write_cursordown(struct screen_write_ctx *, u_int);
|
void screen_write_cursordown(struct screen_write_ctx *, u_int);
|
||||||
void screen_write_cursorright(struct screen_write_ctx *, u_int);
|
void screen_write_cursorright(struct screen_write_ctx *, u_int);
|
||||||
@@ -4153,7 +4177,6 @@ void menu_add_item(struct menu *, const struct menu_item *,
|
|||||||
struct cmdq_item *, struct client *,
|
struct cmdq_item *, struct client *,
|
||||||
struct cmd_find_state *);
|
struct cmd_find_state *);
|
||||||
void menu_free(struct menu *);
|
void menu_free(struct menu *);
|
||||||
void menu_get_size(struct menu *, enum box_lines, u_int *, u_int *);
|
|
||||||
int menu_display(struct menu *, int, int, struct cmdq_item *,
|
int menu_display(struct menu *, int, int, struct cmdq_item *,
|
||||||
u_int, u_int, struct client *, enum box_lines, const char *,
|
u_int, u_int, struct client *, enum box_lines, const char *,
|
||||||
const char *, const char *, struct cmd_find_state *,
|
const char *, const char *, struct cmd_find_state *,
|
||||||
@@ -4170,6 +4193,20 @@ void menu_get_cursor(struct menu_data *, u_int *, u_int *);
|
|||||||
void menu_resize(struct menu_data *, struct window *);
|
void menu_resize(struct menu_data *, struct window *);
|
||||||
int menu_key(struct client *, struct menu_data *, struct key_event *);
|
int menu_key(struct client *, struct menu_data *, struct key_event *);
|
||||||
|
|
||||||
|
/* popup.c */
|
||||||
|
#define POPUP_CLOSEEXIT 0x1
|
||||||
|
#define POPUP_CLOSEEXITZERO 0x2
|
||||||
|
#define POPUP_CLOSEANYKEY 0x4
|
||||||
|
typedef void (*popup_close_cb)(int, void *);
|
||||||
|
int popup_display(int, enum box_lines, struct cmdq_item *, u_int,
|
||||||
|
u_int, u_int, u_int, struct environ *, const char *, int,
|
||||||
|
char **, const char *, const char *, struct client *,
|
||||||
|
struct session *, const char *, const char *,
|
||||||
|
popup_close_cb, void *);
|
||||||
|
int popup_present(struct client *);
|
||||||
|
int popup_modify(struct client *, const char *, const char *,
|
||||||
|
const char *, enum box_lines, int);
|
||||||
|
|
||||||
/* style.c */
|
/* style.c */
|
||||||
int style_parse(struct style *,const struct grid_cell *,
|
int style_parse(struct style *,const struct grid_cell *,
|
||||||
const char *);
|
const char *);
|
||||||
|
|||||||
+2
-1
@@ -52,7 +52,8 @@ tty_draw_line_clear(struct tty *tty, u_int px, u_int py, u_int nx,
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
/* If genuine BCE is available, can try escape sequences. */
|
/* If genuine BCE is available, can try escape sequences. */
|
||||||
if (!wrapped &&
|
if (tty->client->overlay_check == NULL &&
|
||||||
|
!wrapped &&
|
||||||
nx >= 10 &&
|
nx >= 10 &&
|
||||||
!tty_fake_bce(tty, defaults, bg)) {
|
!tty_fake_bce(tty, defaults, bg)) {
|
||||||
/* Off the end of the line, use EL if available. */
|
/* Off the end of the line, use EL if available. */
|
||||||
|
|||||||
+2
-4
@@ -1,4 +1,4 @@
|
|||||||
/* $OpenBSD: tty-features.c,v 1.46 2026/09/24 08:16:13 nicm Exp $ */
|
/* $OpenBSD: tty-features.c,v 1.45 2026/09/22 14:10:26 nicm Exp $ */
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2020 Nicholas Marriott <nicholas.marriott@gmail.com>
|
* Copyright (c) 2020 Nicholas Marriott <nicholas.marriott@gmail.com>
|
||||||
@@ -637,7 +637,6 @@ tty_default_features(struct client *c, const char *name, u_int version)
|
|||||||
"extkeys,"
|
"extkeys,"
|
||||||
"focus,"
|
"focus,"
|
||||||
"hyperlinks,"
|
"hyperlinks,"
|
||||||
"margins,"
|
|
||||||
"usstyle"
|
"usstyle"
|
||||||
},
|
},
|
||||||
{ .name = "ghostty",
|
{ .name = "ghostty",
|
||||||
@@ -646,7 +645,6 @@ tty_default_features(struct client *c, const char *name, u_int version)
|
|||||||
"cstyle,"
|
"cstyle,"
|
||||||
"extkeys,"
|
"extkeys,"
|
||||||
"focus,"
|
"focus,"
|
||||||
"margins,"
|
|
||||||
"overline,"
|
"overline,"
|
||||||
"hyperlinks,"
|
"hyperlinks,"
|
||||||
"osc7,"
|
"osc7,"
|
||||||
@@ -677,7 +675,7 @@ tty_default_features(struct client *c, const char *name, u_int version)
|
|||||||
"cstyle,"
|
"cstyle,"
|
||||||
"extkeys,"
|
"extkeys,"
|
||||||
"focus"
|
"focus"
|
||||||
},
|
}
|
||||||
};
|
};
|
||||||
u_int i;
|
u_int i;
|
||||||
|
|
||||||
|
|||||||
@@ -66,6 +66,7 @@ static void tty_redraw_region(struct tty *, const struct tty_ctx *);
|
|||||||
static void tty_emulate_repeat(struct tty *, enum tty_code_code,
|
static void tty_emulate_repeat(struct tty *, enum tty_code_code,
|
||||||
enum tty_code_code, u_int);
|
enum tty_code_code, u_int);
|
||||||
static void tty_draw_pane(struct tty *, const struct tty_ctx *, u_int);
|
static void tty_draw_pane(struct tty *, const struct tty_ctx *, u_int);
|
||||||
|
static int tty_check_overlay(struct tty *, u_int, u_int);
|
||||||
|
|
||||||
#ifdef ENABLE_SIXEL
|
#ifdef ENABLE_SIXEL
|
||||||
static void tty_write_one(void (*)(struct tty *, const struct tty_ctx *),
|
static void tty_write_one(void (*)(struct tty *, const struct tty_ctx *),
|
||||||
@@ -543,6 +544,8 @@ void
|
|||||||
tty_free(struct tty *tty)
|
tty_free(struct tty *tty)
|
||||||
{
|
{
|
||||||
tty_close(tty);
|
tty_close(tty);
|
||||||
|
|
||||||
|
free(tty->r.ranges);
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
@@ -1196,6 +1199,9 @@ tty_clear_line(struct tty *tty, const struct grid_cell *defaults, u_int py,
|
|||||||
u_int px, u_int nx, u_int bg)
|
u_int px, u_int nx, u_int bg)
|
||||||
{
|
{
|
||||||
struct client *c = tty->client;
|
struct client *c = tty->client;
|
||||||
|
struct visible_ranges *r;
|
||||||
|
struct visible_range *rr;
|
||||||
|
u_int i;
|
||||||
|
|
||||||
log_debug("%s: %s, %u at %u,%u", __func__, c->name, nx, px, py);
|
log_debug("%s: %s, %u at %u,%u", __func__, c->name, nx, px, py);
|
||||||
|
|
||||||
@@ -1204,7 +1210,7 @@ tty_clear_line(struct tty *tty, const struct grid_cell *defaults, u_int py,
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
/* If genuine BCE is available, can try escape sequences. */
|
/* If genuine BCE is available, can try escape sequences. */
|
||||||
if (!tty_fake_bce(tty, defaults, bg)) {
|
if (c->overlay_check == NULL && !tty_fake_bce(tty, defaults, bg)) {
|
||||||
/* Off the end of the line, use EL if available. */
|
/* Off the end of the line, use EL if available. */
|
||||||
if (px + nx >= tty->sx && tty_term_has(tty->term, TTYC_EL)) {
|
if (px + nx >= tty->sx && tty_term_has(tty->term, TTYC_EL)) {
|
||||||
tty_cursor(tty, px, py);
|
tty_cursor(tty, px, py);
|
||||||
@@ -1227,9 +1233,18 @@ tty_clear_line(struct tty *tty, const struct grid_cell *defaults, u_int py,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Couldn't use an escape sequence, use spaces. */
|
/*
|
||||||
tty_cursor(tty, px, py);
|
* Couldn't use an escape sequence, use spaces. Clear only the visible
|
||||||
tty_repeat_space(tty, nx);
|
* bit if there is an overlay.
|
||||||
|
*/
|
||||||
|
r = tty_check_overlay_range(tty, px, py, nx);
|
||||||
|
for (i = 0; i < r->used; i++) {
|
||||||
|
rr = &r->ranges[i];
|
||||||
|
if (rr->nx != 0) {
|
||||||
|
tty_cursor(tty, rr->px, py);
|
||||||
|
tty_repeat_space(tty, rr->nx);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Clear a line, adjusting to visible part of pane. */
|
/* Clear a line, adjusting to visible part of pane. */
|
||||||
@@ -1238,12 +1253,22 @@ tty_clear_pane_line(struct tty *tty, const struct tty_ctx *ctx, u_int py,
|
|||||||
u_int px, u_int nx, u_int bg)
|
u_int px, u_int nx, u_int bg)
|
||||||
{
|
{
|
||||||
struct client *c = tty->client;
|
struct client *c = tty->client;
|
||||||
u_int l, x, rx, ry;
|
struct visible_ranges *r;
|
||||||
|
struct visible_range *ri;
|
||||||
|
u_int i, l, x, rx, ry;
|
||||||
|
|
||||||
log_debug("%s: %s, %u at %u,%u", __func__, c->name, nx, px, py);
|
log_debug("%s: %s, %u at %u,%u", __func__, c->name, nx, px, py);
|
||||||
|
|
||||||
if (tty_clamp_line(tty, ctx, px, py, nx, &l, &x, &rx, &ry))
|
if (tty_clamp_line(tty, ctx, px, py, nx, &l, &x, &rx, &ry)) {
|
||||||
tty_clear_line(tty, &ctx->defaults, ry, x, rx, bg);
|
r = tty_check_overlay_range(tty, x, ry, rx);
|
||||||
|
for (i = 0; i < r->used; i++) {
|
||||||
|
ri = &r->ranges[i];
|
||||||
|
if (ri->nx == 0)
|
||||||
|
continue;
|
||||||
|
tty_clear_line(tty, &ctx->defaults, ry, ri->px, ri->nx,
|
||||||
|
bg);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Clamp area position to visible part of pane. */
|
/* Clamp area position to visible part of pane. */
|
||||||
@@ -1324,8 +1349,11 @@ tty_clear_area(struct tty *tty, const struct tty_ctx *ctx, u_int py,
|
|||||||
if (nx == 0 || ny == 0)
|
if (nx == 0 || ny == 0)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
/* If BCE is available, can try to clear as a region. */
|
/*
|
||||||
if (!tty_fake_bce(tty, defaults, bg)) {
|
* If there is an overlay or BCE is not available, cannot clear as a
|
||||||
|
* region.
|
||||||
|
*/
|
||||||
|
if (c->overlay_check == NULL && !tty_fake_bce(tty, defaults, bg)) {
|
||||||
/* Use ED if clearing off the bottom of the terminal. */
|
/* Use ED if clearing off the bottom of the terminal. */
|
||||||
if (px == 0 &&
|
if (px == 0 &&
|
||||||
px + nx >= tty->sx &&
|
px + nx >= tty->sx &&
|
||||||
@@ -1397,28 +1425,53 @@ static void
|
|||||||
tty_draw_pane(struct tty *tty, const struct tty_ctx *ctx, u_int py)
|
tty_draw_pane(struct tty *tty, const struct tty_ctx *ctx, u_int py)
|
||||||
{
|
{
|
||||||
struct screen *s = ctx->s;
|
struct screen *s = ctx->s;
|
||||||
u_int nx = ctx->sx, i, x, rx, ry;
|
u_int nx = ctx->sx, i, x, rx, ry, j;
|
||||||
|
struct visible_ranges *r;
|
||||||
|
struct visible_range *rr;
|
||||||
|
|
||||||
log_debug("%s: %s %u", __func__, tty->client->name, py);
|
log_debug("%s: %s %u", __func__, tty->client->name, py);
|
||||||
|
|
||||||
if (~ctx->flags & TTY_CTX_WINDOW_BIGGER) {
|
if (~ctx->flags & TTY_CTX_WINDOW_BIGGER) {
|
||||||
tty_draw_line(tty, s, 0, py, nx, ctx->xoff, ctx->yoff + py,
|
r = tty_check_overlay_range(tty, ctx->xoff, ctx->yoff + py, nx);
|
||||||
&ctx->style_ctx);
|
for (j = 0; j < r->used; j++) {
|
||||||
|
rr = &r->ranges[j];
|
||||||
|
if (rr->nx == 0)
|
||||||
|
continue;
|
||||||
|
tty_draw_line(tty, s, rr->px - ctx->xoff, py, rr->nx,
|
||||||
|
rr->px, ctx->yoff + py, &ctx->style_ctx);
|
||||||
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (tty_clamp_line(tty, ctx, 0, py, nx, &i, &x, &rx, &ry))
|
if (tty_clamp_line(tty, ctx, 0, py, nx, &i, &x, &rx, &ry)) {
|
||||||
tty_draw_line(tty, s, i, py, rx, x, ry, &ctx->style_ctx);
|
r = tty_check_overlay_range(tty, x, ry, rx);
|
||||||
|
for (j = 0; j < r->used; j++) {
|
||||||
|
rr = &r->ranges[j];
|
||||||
|
if (rr->nx == 0)
|
||||||
|
continue;
|
||||||
|
tty_draw_line(tty, s, i + rr->px - x, py, rr->nx,
|
||||||
|
rr->px, ry, &ctx->style_ctx);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
tty_cmd_redrawline(struct tty *tty, const struct tty_ctx *ctx)
|
tty_cmd_redrawline(struct tty *tty, const struct tty_ctx *ctx)
|
||||||
{
|
{
|
||||||
u_int i, x, rx, ry;
|
u_int i, x, rx, ry, j;
|
||||||
|
struct visible_ranges *r;
|
||||||
|
struct visible_range *rr;
|
||||||
|
|
||||||
if (tty_clamp_line(tty, ctx, ctx->ocx, ctx->ocy, ctx->n,
|
if (tty_clamp_line(tty, ctx, ctx->ocx, ctx->ocy, ctx->n,
|
||||||
&i, &x, &rx, &ry))
|
&i, &x, &rx, &ry)) {
|
||||||
tty_draw_line(tty, ctx->s, ctx->ocx + i, ctx->ocy, rx, x, ry,
|
r = tty_check_overlay_range(tty, x, ry, rx);
|
||||||
&ctx->style_ctx);
|
for (j = 0; j < r->used; j++) {
|
||||||
|
rr = &r->ranges[j];
|
||||||
|
if (rr->nx == 0)
|
||||||
|
continue;
|
||||||
|
tty_draw_line(tty, ctx->s, ctx->ocx + i + rr->px - x,
|
||||||
|
ctx->ocy, rr->nx, rr->px, ry, &ctx->style_ctx);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Check if character needs to be mapped for codeset. */
|
/* Check if character needs to be mapped for codeset. */
|
||||||
@@ -1455,6 +1508,36 @@ tty_check_codeset(struct tty *tty, const struct grid_cell *gc)
|
|||||||
return (&new);
|
return (&new);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Check if a single character is covered by the overlay. */
|
||||||
|
static int
|
||||||
|
tty_check_overlay(struct tty *tty, u_int px, u_int py)
|
||||||
|
{
|
||||||
|
struct visible_ranges *r;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* With a single character, if there is anything visible (that is, the
|
||||||
|
* range is not empty), it must be that character.
|
||||||
|
*/
|
||||||
|
r = tty_check_overlay_range(tty, px, py, 1);
|
||||||
|
return (!server_client_ranges_is_empty(r));
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Return parts of the input range which are visible. */
|
||||||
|
struct visible_ranges *
|
||||||
|
tty_check_overlay_range(struct tty *tty, u_int px, u_int py, u_int nx)
|
||||||
|
{
|
||||||
|
struct client *c = tty->client;
|
||||||
|
|
||||||
|
if (c->overlay_check == NULL) {
|
||||||
|
server_client_ensure_ranges(&tty->r, 1);
|
||||||
|
tty->r.ranges[0].px = px;
|
||||||
|
tty->r.ranges[0].nx = nx;
|
||||||
|
tty->r.used = 1;
|
||||||
|
return (&tty->r);
|
||||||
|
}
|
||||||
|
return (c->overlay_check(c, c->overlay_data, px, py, nx));
|
||||||
|
}
|
||||||
|
|
||||||
#ifdef ENABLE_SIXEL
|
#ifdef ENABLE_SIXEL
|
||||||
/* Update context for client. */
|
/* Update context for client. */
|
||||||
static int
|
static int
|
||||||
@@ -1598,11 +1681,14 @@ tty_write_one(void (*cmdfn)(struct tty *, const struct tty_ctx *),
|
|||||||
void
|
void
|
||||||
tty_cmd_insertcharacter(struct tty *tty, const struct tty_ctx *ctx)
|
tty_cmd_insertcharacter(struct tty *tty, const struct tty_ctx *ctx)
|
||||||
{
|
{
|
||||||
|
struct client *c = tty->client;
|
||||||
|
|
||||||
if ((ctx->flags & TTY_CTX_WINDOW_BIGGER) ||
|
if ((ctx->flags & TTY_CTX_WINDOW_BIGGER) ||
|
||||||
!tty_full_width(tty, ctx) ||
|
!tty_full_width(tty, ctx) ||
|
||||||
tty_fake_bce(tty, &ctx->defaults, ctx->bg) ||
|
tty_fake_bce(tty, &ctx->defaults, ctx->bg) ||
|
||||||
(!tty_term_has(tty->term, TTYC_ICH) &&
|
(!tty_term_has(tty->term, TTYC_ICH) &&
|
||||||
!tty_term_has(tty->term, TTYC_ICH1))) {
|
!tty_term_has(tty->term, TTYC_ICH1)) ||
|
||||||
|
c->overlay_check != NULL) {
|
||||||
tty_draw_pane(tty, ctx, ctx->ocy);
|
tty_draw_pane(tty, ctx, ctx->ocy);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -1617,11 +1703,14 @@ tty_cmd_insertcharacter(struct tty *tty, const struct tty_ctx *ctx)
|
|||||||
void
|
void
|
||||||
tty_cmd_deletecharacter(struct tty *tty, const struct tty_ctx *ctx)
|
tty_cmd_deletecharacter(struct tty *tty, const struct tty_ctx *ctx)
|
||||||
{
|
{
|
||||||
|
struct client *c = tty->client;
|
||||||
|
|
||||||
if ((ctx->flags & TTY_CTX_WINDOW_BIGGER) ||
|
if ((ctx->flags & TTY_CTX_WINDOW_BIGGER) ||
|
||||||
!tty_full_width(tty, ctx) ||
|
!tty_full_width(tty, ctx) ||
|
||||||
tty_fake_bce(tty, &ctx->defaults, ctx->bg) ||
|
tty_fake_bce(tty, &ctx->defaults, ctx->bg) ||
|
||||||
(!tty_term_has(tty->term, TTYC_DCH) &&
|
(!tty_term_has(tty->term, TTYC_DCH) &&
|
||||||
!tty_term_has(tty->term, TTYC_DCH1))) {
|
!tty_term_has(tty->term, TTYC_DCH1)) ||
|
||||||
|
c->overlay_check != NULL) {
|
||||||
tty_draw_pane(tty, ctx, ctx->ocy);
|
tty_draw_pane(tty, ctx, ctx->ocy);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -1644,13 +1733,16 @@ tty_cmd_clearcharacter(struct tty *tty, const struct tty_ctx *ctx)
|
|||||||
void
|
void
|
||||||
tty_cmd_insertline(struct tty *tty, const struct tty_ctx *ctx)
|
tty_cmd_insertline(struct tty *tty, const struct tty_ctx *ctx)
|
||||||
{
|
{
|
||||||
|
struct client *c = tty->client;
|
||||||
|
|
||||||
if ((ctx->flags & TTY_CTX_WINDOW_BIGGER) ||
|
if ((ctx->flags & TTY_CTX_WINDOW_BIGGER) ||
|
||||||
!tty_full_width(tty, ctx) ||
|
!tty_full_width(tty, ctx) ||
|
||||||
tty_fake_bce(tty, &ctx->defaults, ctx->bg) ||
|
tty_fake_bce(tty, &ctx->defaults, ctx->bg) ||
|
||||||
!tty_term_has(tty->term, TTYC_CSR) ||
|
!tty_term_has(tty->term, TTYC_CSR) ||
|
||||||
!tty_term_has(tty->term, TTYC_IL1) ||
|
!tty_term_has(tty->term, TTYC_IL1) ||
|
||||||
ctx->sx == 1 ||
|
ctx->sx == 1 ||
|
||||||
ctx->sy == 1) {
|
ctx->sy == 1 ||
|
||||||
|
c->overlay_check != NULL) {
|
||||||
tty_redraw_region(tty, ctx);
|
tty_redraw_region(tty, ctx);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -1668,13 +1760,16 @@ tty_cmd_insertline(struct tty *tty, const struct tty_ctx *ctx)
|
|||||||
void
|
void
|
||||||
tty_cmd_deleteline(struct tty *tty, const struct tty_ctx *ctx)
|
tty_cmd_deleteline(struct tty *tty, const struct tty_ctx *ctx)
|
||||||
{
|
{
|
||||||
|
struct client *c = tty->client;
|
||||||
|
|
||||||
if ((ctx->flags & TTY_CTX_WINDOW_BIGGER) ||
|
if ((ctx->flags & TTY_CTX_WINDOW_BIGGER) ||
|
||||||
!tty_full_width(tty, ctx) ||
|
!tty_full_width(tty, ctx) ||
|
||||||
tty_fake_bce(tty, &ctx->defaults, ctx->bg) ||
|
tty_fake_bce(tty, &ctx->defaults, ctx->bg) ||
|
||||||
!tty_term_has(tty->term, TTYC_CSR) ||
|
!tty_term_has(tty->term, TTYC_CSR) ||
|
||||||
!tty_term_has(tty->term, TTYC_DL1) ||
|
!tty_term_has(tty->term, TTYC_DL1) ||
|
||||||
ctx->sx == 1 ||
|
ctx->sx == 1 ||
|
||||||
ctx->sy == 1) {
|
ctx->sy == 1 ||
|
||||||
|
c->overlay_check != NULL) {
|
||||||
tty_redraw_region(tty, ctx);
|
tty_redraw_region(tty, ctx);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -1718,6 +1813,8 @@ tty_cmd_clearstartofline(struct tty *tty, const struct tty_ctx *ctx)
|
|||||||
void
|
void
|
||||||
tty_cmd_reverseindex(struct tty *tty, const struct tty_ctx *ctx)
|
tty_cmd_reverseindex(struct tty *tty, const struct tty_ctx *ctx)
|
||||||
{
|
{
|
||||||
|
struct client *c = tty->client;
|
||||||
|
|
||||||
if (ctx->ocy != ctx->orupper)
|
if (ctx->ocy != ctx->orupper)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
@@ -1728,7 +1825,8 @@ tty_cmd_reverseindex(struct tty *tty, const struct tty_ctx *ctx)
|
|||||||
(!tty_term_has(tty->term, TTYC_RI) &&
|
(!tty_term_has(tty->term, TTYC_RI) &&
|
||||||
!tty_term_has(tty->term, TTYC_RIN)) ||
|
!tty_term_has(tty->term, TTYC_RIN)) ||
|
||||||
ctx->sx == 1 ||
|
ctx->sx == 1 ||
|
||||||
ctx->sy == 1) {
|
ctx->sy == 1 ||
|
||||||
|
c->overlay_check != NULL) {
|
||||||
tty_redraw_region(tty, ctx);
|
tty_redraw_region(tty, ctx);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -1748,6 +1846,8 @@ tty_cmd_reverseindex(struct tty *tty, const struct tty_ctx *ctx)
|
|||||||
void
|
void
|
||||||
tty_cmd_linefeed(struct tty *tty, const struct tty_ctx *ctx)
|
tty_cmd_linefeed(struct tty *tty, const struct tty_ctx *ctx)
|
||||||
{
|
{
|
||||||
|
struct client *c = tty->client;
|
||||||
|
|
||||||
if (ctx->ocy != ctx->orlower)
|
if (ctx->ocy != ctx->orlower)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
@@ -1756,7 +1856,8 @@ tty_cmd_linefeed(struct tty *tty, const struct tty_ctx *ctx)
|
|||||||
tty_fake_bce(tty, &ctx->defaults, 8) ||
|
tty_fake_bce(tty, &ctx->defaults, 8) ||
|
||||||
!tty_term_has(tty->term, TTYC_CSR) ||
|
!tty_term_has(tty->term, TTYC_CSR) ||
|
||||||
ctx->sx == 1 ||
|
ctx->sx == 1 ||
|
||||||
ctx->sy == 1) {
|
ctx->sy == 1 ||
|
||||||
|
c->overlay_check != NULL) {
|
||||||
tty_redraw_region(tty, ctx);
|
tty_redraw_region(tty, ctx);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -1787,6 +1888,7 @@ tty_cmd_linefeed(struct tty *tty, const struct tty_ctx *ctx)
|
|||||||
void
|
void
|
||||||
tty_cmd_scrollup(struct tty *tty, const struct tty_ctx *ctx)
|
tty_cmd_scrollup(struct tty *tty, const struct tty_ctx *ctx)
|
||||||
{
|
{
|
||||||
|
struct client *c = tty->client;
|
||||||
u_int i;
|
u_int i;
|
||||||
|
|
||||||
if ((ctx->flags & TTY_CTX_WINDOW_BIGGER) ||
|
if ((ctx->flags & TTY_CTX_WINDOW_BIGGER) ||
|
||||||
@@ -1794,7 +1896,8 @@ tty_cmd_scrollup(struct tty *tty, const struct tty_ctx *ctx)
|
|||||||
tty_fake_bce(tty, &ctx->defaults, 8) ||
|
tty_fake_bce(tty, &ctx->defaults, 8) ||
|
||||||
!tty_term_has(tty->term, TTYC_CSR) ||
|
!tty_term_has(tty->term, TTYC_CSR) ||
|
||||||
ctx->sx == 1 ||
|
ctx->sx == 1 ||
|
||||||
ctx->sy == 1) {
|
ctx->sy == 1 ||
|
||||||
|
c->overlay_check != NULL) {
|
||||||
tty_redraw_region(tty, ctx);
|
tty_redraw_region(tty, ctx);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -1824,6 +1927,7 @@ void
|
|||||||
tty_cmd_scrolldown(struct tty *tty, const struct tty_ctx *ctx)
|
tty_cmd_scrolldown(struct tty *tty, const struct tty_ctx *ctx)
|
||||||
{
|
{
|
||||||
u_int i;
|
u_int i;
|
||||||
|
struct client *c = tty->client;
|
||||||
|
|
||||||
if ((ctx->flags & TTY_CTX_WINDOW_BIGGER) ||
|
if ((ctx->flags & TTY_CTX_WINDOW_BIGGER) ||
|
||||||
(!tty_full_width(tty, ctx) && !tty_use_margin(tty)) ||
|
(!tty_full_width(tty, ctx) && !tty_use_margin(tty)) ||
|
||||||
@@ -1832,7 +1936,8 @@ tty_cmd_scrolldown(struct tty *tty, const struct tty_ctx *ctx)
|
|||||||
(!tty_term_has(tty->term, TTYC_RI) &&
|
(!tty_term_has(tty->term, TTYC_RI) &&
|
||||||
!tty_term_has(tty->term, TTYC_RIN)) ||
|
!tty_term_has(tty->term, TTYC_RIN)) ||
|
||||||
ctx->sx == 1 ||
|
ctx->sx == 1 ||
|
||||||
ctx->sy == 1) {
|
ctx->sy == 1 ||
|
||||||
|
c->overlay_check != NULL) {
|
||||||
tty_redraw_region(tty, ctx);
|
tty_redraw_region(tty, ctx);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -1920,9 +2025,11 @@ tty_cmd_clearscreen(struct tty *tty, const struct tty_ctx *ctx)
|
|||||||
void
|
void
|
||||||
tty_cmd_alignmenttest(struct tty *tty, const struct tty_ctx *ctx)
|
tty_cmd_alignmenttest(struct tty *tty, const struct tty_ctx *ctx)
|
||||||
{
|
{
|
||||||
|
struct client *c = tty->client;
|
||||||
u_int i, j;
|
u_int i, j;
|
||||||
|
|
||||||
if (ctx->flags & TTY_CTX_WINDOW_BIGGER) {
|
if ((ctx->flags & TTY_CTX_WINDOW_BIGGER) ||
|
||||||
|
c->overlay_check != NULL) {
|
||||||
ctx->redraw_cb(ctx);
|
ctx->redraw_cb(ctx);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -1942,9 +2049,29 @@ tty_cmd_alignmenttest(struct tty *tty, const struct tty_ctx *ctx)
|
|||||||
void
|
void
|
||||||
tty_cmd_cell(struct tty *tty, const struct tty_ctx *ctx)
|
tty_cmd_cell(struct tty *tty, const struct tty_ctx *ctx)
|
||||||
{
|
{
|
||||||
|
const struct grid_cell *gcp = ctx->cell;
|
||||||
|
struct screen *s = ctx->s;
|
||||||
|
struct visible_ranges *r;
|
||||||
|
u_int px, py, i, vis = 0;
|
||||||
|
|
||||||
|
px = ctx->xoff + ctx->ocx - ctx->wox;
|
||||||
|
py = ctx->yoff + ctx->ocy - ctx->woy;
|
||||||
if (!tty_is_visible(tty, ctx, ctx->ocx, ctx->ocy, 1, 1))
|
if (!tty_is_visible(tty, ctx, ctx->ocx, ctx->ocy, 1, 1))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
if (gcp->data.width == 1 && !tty_check_overlay(tty, px, py))
|
||||||
|
return;
|
||||||
|
if (gcp->data.width > 1) { /* could be partially obscured */
|
||||||
|
r = tty_check_overlay_range(tty, px, py, gcp->data.width);
|
||||||
|
for (i = 0; i < r->used; i++)
|
||||||
|
vis += r->ranges[i].nx;
|
||||||
|
if (vis < gcp->data.width) {
|
||||||
|
tty_draw_line(tty, s, s->cx, s->cy, gcp->data.width,
|
||||||
|
px, py, &ctx->style_ctx);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (ctx->xoff + ctx->ocx - ctx->wox > tty->sx - 1 &&
|
if (ctx->xoff + ctx->ocx - ctx->wox > tty->sx - 1 &&
|
||||||
ctx->ocy == ctx->orlower &&
|
ctx->ocy == ctx->orlower &&
|
||||||
tty_full_width(tty, ctx))
|
tty_full_width(tty, ctx))
|
||||||
@@ -1964,6 +2091,9 @@ tty_cmd_cell(struct tty *tty, const struct tty_ctx *ctx)
|
|||||||
void
|
void
|
||||||
tty_cmd_cells(struct tty *tty, const struct tty_ctx *ctx)
|
tty_cmd_cells(struct tty *tty, const struct tty_ctx *ctx)
|
||||||
{
|
{
|
||||||
|
struct visible_ranges *r;
|
||||||
|
struct visible_range *ri;
|
||||||
|
u_int i, px, py, cx;
|
||||||
const char *cp = ctx->data.data;
|
const char *cp = ctx->data.data;
|
||||||
size_t n = ctx->data.size;
|
size_t n = ctx->data.size;
|
||||||
|
|
||||||
@@ -1989,7 +2119,20 @@ tty_cmd_cells(struct tty *tty, const struct tty_ctx *ctx)
|
|||||||
tty_margin_off(tty);
|
tty_margin_off(tty);
|
||||||
tty_cursor_pane_unless_wrap(tty, ctx, ctx->ocx, ctx->ocy);
|
tty_cursor_pane_unless_wrap(tty, ctx, ctx->ocx, ctx->ocy);
|
||||||
tty_attributes(tty, ctx->cell, &ctx->style_ctx);
|
tty_attributes(tty, ctx->cell, &ctx->style_ctx);
|
||||||
tty_putn(tty, cp, n, n);
|
|
||||||
|
/* Get tty position from pane position for overlay check. */
|
||||||
|
px = ctx->xoff + ctx->ocx - ctx->wox;
|
||||||
|
py = ctx->yoff + ctx->ocy - ctx->woy;
|
||||||
|
|
||||||
|
r = tty_check_overlay_range(tty, px, py, n);
|
||||||
|
for (i = 0; i < r->used; i++) {
|
||||||
|
ri = &r->ranges[i];
|
||||||
|
if (ri->nx != 0) {
|
||||||
|
cx = ri->px - ctx->xoff + ctx->wox;
|
||||||
|
tty_cursor_pane_unless_wrap(tty, ctx, cx, ctx->ocy);
|
||||||
|
tty_putn(tty, cp + ri->px - px, ri->nx, ri->nx);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
@@ -2083,8 +2226,23 @@ tty_cmd_sixelimage(struct tty *tty, const struct tty_ctx *ctx)
|
|||||||
void
|
void
|
||||||
tty_cmd_syncstart(struct tty *tty, const struct tty_ctx *ctx)
|
tty_cmd_syncstart(struct tty *tty, const struct tty_ctx *ctx)
|
||||||
{
|
{
|
||||||
if (ctx->flags & TTY_CTX_SYNC)
|
struct client *c = tty->client;
|
||||||
|
|
||||||
|
if ((ctx->flags & TTY_CTX_OVERLAY_SYNC) &&
|
||||||
|
(ctx->flags & TTY_CTX_SYNC)) {
|
||||||
|
/*
|
||||||
|
* This is an overlay and a command that moves the cursor so
|
||||||
|
* start synchronized updates.
|
||||||
|
*/
|
||||||
tty_sync_start(tty);
|
tty_sync_start(tty);
|
||||||
|
} else if (~ctx->flags & TTY_CTX_OVERLAY_SYNC) {
|
||||||
|
/*
|
||||||
|
* This is a pane. If there is an overlay, always start;
|
||||||
|
* otherwise, only if requested.
|
||||||
|
*/
|
||||||
|
if ((ctx->flags & TTY_CTX_SYNC) || c->overlay_draw != NULL)
|
||||||
|
tty_sync_start(tty);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
@@ -2103,6 +2261,10 @@ tty_cell(struct tty *tty, const struct grid_cell *gc,
|
|||||||
if (gc->flags & GRID_FLAG_PADDING)
|
if (gc->flags & GRID_FLAG_PADDING)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
/* Check if character is covered by overlay or floating pane. */
|
||||||
|
if (!tty_check_overlay(tty, tty->cx, tty->cy))
|
||||||
|
return;
|
||||||
|
|
||||||
/* Check the output codeset and apply attributes. */
|
/* Check the output codeset and apply attributes. */
|
||||||
gcp = tty_check_codeset(tty, gc);
|
gcp = tty_check_codeset(tty, gc);
|
||||||
tty_attributes(tty, gcp, style_ctx);
|
tty_attributes(tty, gcp, style_ctx);
|
||||||
|
|||||||
+1
-5
@@ -1,4 +1,4 @@
|
|||||||
/* $OpenBSD: window-border.c,v 1.4 2026/09/11 10:17:16 nicm Exp $ */
|
/* $OpenBSD: window-border.c,v 1.3 2026/07/23 09:38:27 nicm Exp $ */
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2026 Nicholas Marriott <nicholas.marriott@gmail.com>
|
* Copyright (c) 2026 Nicholas Marriott <nicholas.marriott@gmail.com>
|
||||||
@@ -120,10 +120,6 @@ window_get_border_cell(struct window_pane *wp, enum pane_lines pane_lines,
|
|||||||
gc->attr &= ~GRID_ATTR_CHARSET;
|
gc->attr &= ~GRID_ATTR_CHARSET;
|
||||||
utf8_copy(&gc->data, tty_acs_heavy_borders(cell_type));
|
utf8_copy(&gc->data, tty_acs_heavy_borders(cell_type));
|
||||||
break;
|
break;
|
||||||
case PANE_LINES_ROUNDED:
|
|
||||||
gc->attr &= ~GRID_ATTR_CHARSET;
|
|
||||||
utf8_copy(&gc->data, tty_acs_rounded_borders(cell_type));
|
|
||||||
break;
|
|
||||||
case PANE_LINES_SIMPLE:
|
case PANE_LINES_SIMPLE:
|
||||||
gc->attr &= ~GRID_ATTR_CHARSET;
|
gc->attr &= ~GRID_ATTR_CHARSET;
|
||||||
utf8_set(&gc->data, SIMPLE_BORDERS[cell_type]);
|
utf8_set(&gc->data, SIMPLE_BORDERS[cell_type]);
|
||||||
|
|||||||
+1
-1
@@ -665,7 +665,7 @@ window_copy_view_init(struct window_mode_entry *wme,
|
|||||||
|
|
||||||
data->backing = xmalloc(sizeof *data->backing);
|
data->backing = xmalloc(sizeof *data->backing);
|
||||||
screen_init(data->backing, sx, screen_size_y(base), UINT_MAX);
|
screen_init(data->backing, sx, screen_size_y(base), UINT_MAX);
|
||||||
data->ictx = input_init(NULL, NULL, NULL);
|
data->ictx = input_init(NULL, NULL, NULL, NULL);
|
||||||
data->mx = data->cx;
|
data->mx = data->cx;
|
||||||
data->my = screen_hsize(data->backing) + data->cy - data->oy;
|
data->my = screen_hsize(data->backing) + data->cy - data->oy;
|
||||||
data->showmark = 0;
|
data->showmark = 0;
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
/* $OpenBSD: window.c,v 1.378 2026/09/24 11:19:39 nicm Exp $ */
|
/* $OpenBSD: window.c,v 1.377 2026/09/21 10:33:16 nicm Exp $ */
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>
|
* Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>
|
||||||
@@ -691,6 +691,7 @@ window_pane_update_focus(struct window_pane *wp)
|
|||||||
c->session->attached != 0 &&
|
c->session->attached != 0 &&
|
||||||
(c->flags & CLIENT_FOCUSED) &&
|
(c->flags & CLIENT_FOCUSED) &&
|
||||||
c->session->curw->window == wp->window &&
|
c->session->curw->window == wp->window &&
|
||||||
|
c->overlay_draw == NULL &&
|
||||||
wp->window->menu == NULL) {
|
wp->window->menu == NULL) {
|
||||||
focused = 1;
|
focused = 1;
|
||||||
break;
|
break;
|
||||||
@@ -1528,7 +1529,7 @@ window_pane_destroy(struct window_pane *wp)
|
|||||||
window_pane_clear_prompt(wp);
|
window_pane_clear_prompt(wp);
|
||||||
|
|
||||||
window_pane_free_modes(wp);
|
window_pane_free_modes(wp);
|
||||||
screen_write_sync_clear_dirty(wp);
|
screen_write_clear_dirty(wp);
|
||||||
|
|
||||||
if (wp->fd != -1) {
|
if (wp->fd != -1) {
|
||||||
#ifdef HAVE_UTEMPTER
|
#ifdef HAVE_UTEMPTER
|
||||||
@@ -1632,7 +1633,7 @@ window_pane_set_event(struct window_pane *wp)
|
|||||||
NULL, window_pane_error_callback, wp);
|
NULL, window_pane_error_callback, wp);
|
||||||
if (wp->event == NULL)
|
if (wp->event == NULL)
|
||||||
fatalx("out of memory");
|
fatalx("out of memory");
|
||||||
wp->ictx = input_init(wp, wp->event, &wp->palette);
|
wp->ictx = input_init(wp, wp->event, &wp->palette, NULL);
|
||||||
|
|
||||||
bufferevent_enable(wp->event, EV_READ|EV_WRITE);
|
bufferevent_enable(wp->event, EV_READ|EV_WRITE);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user