mirror of
https://github.com/tmux/tmux.git
synced 2026-09-17 18:44:45 +00:00
Compare commits
136 Commits
image-fall
...
local_wind
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b3f8598903 | ||
|
|
9b0ad73a4b | ||
|
|
bd8218e85d | ||
|
|
8f3101d820 | ||
|
|
77309c07b0 | ||
|
|
c8e889694d | ||
|
|
b402687fcd | ||
|
|
0530a7d89a | ||
|
|
6bdb9ba54c | ||
|
|
f2bfd611e2 | ||
|
|
9c1d5a080b | ||
|
|
b28244e2fe | ||
|
|
8b3834c34f | ||
|
|
af3e4d2e5b | ||
|
|
c552d1bf16 | ||
|
|
6a1ecdcce3 | ||
|
|
39357c829c | ||
|
|
40381bdcd9 | ||
|
|
7bad4f7722 | ||
|
|
65e811570b | ||
|
|
dd31900f80 | ||
|
|
013ada58c8 | ||
|
|
78dad45f3b | ||
|
|
52b452948f | ||
|
|
267746603a | ||
|
|
b26b240613 | ||
|
|
8e48c889dd | ||
|
|
f4445a310a | ||
|
|
4157b64754 | ||
|
|
0d2aafe7b7 | ||
|
|
6f4c9b32af | ||
|
|
eb77a75ab7 | ||
|
|
0851650c6b | ||
|
|
1459c90a7f | ||
|
|
af29c080ba | ||
|
|
50ea32162f | ||
|
|
2d5328a860 | ||
|
|
e5db7598b2 | ||
|
|
dc78d8f9ea | ||
|
|
022b8451e3 | ||
|
|
5b54c7d41a | ||
|
|
f94036b2b0 | ||
|
|
7d358208b9 | ||
|
|
c8ff8ea96f | ||
|
|
402d366bba | ||
|
|
41e701ad84 | ||
|
|
f763f94556 | ||
|
|
c9f5ecb4b5 | ||
|
|
7792a74c76 | ||
|
|
4cabc2ae85 | ||
|
|
995b395795 | ||
|
|
3d44f75d09 | ||
|
|
c1f947a3c5 | ||
|
|
696a16cca2 | ||
|
|
936bb6e7cd | ||
|
|
8dfa903346 | ||
|
|
95a7170709 | ||
|
|
554c4bbde4 | ||
|
|
ac23f21a2c | ||
|
|
6696151008 | ||
|
|
50d0348b64 | ||
|
|
bf7bbba7ea | ||
|
|
b28b1b7b9a | ||
|
|
8499d8007e | ||
|
|
e5a2058c7c | ||
|
|
e94fcd96f9 | ||
|
|
c5017e6da8 | ||
|
|
0169f9e18c | ||
|
|
b170c19274 | ||
|
|
22b3679752 | ||
|
|
4714f478fe | ||
|
|
72c5037f06 | ||
|
|
6238793ad7 | ||
|
|
da641e4e8d | ||
|
|
19a085a672 | ||
|
|
f672a0f677 | ||
|
|
db194ced5b | ||
|
|
11ed661e2c | ||
|
|
b2de803871 | ||
|
|
30abbe36a8 | ||
|
|
62044f02df | ||
|
|
a18d4e0067 | ||
|
|
0f241b7447 | ||
|
|
1b86039df3 | ||
|
|
efaa1fe31c | ||
|
|
1729bb8e8a | ||
|
|
9fdd04ae61 | ||
|
|
87a8bda023 | ||
|
|
d384557b63 | ||
|
|
534a4f81fc | ||
|
|
a0d3585c6c | ||
|
|
ec34ff52cc | ||
|
|
0ac8138ce3 | ||
|
|
c41cad4c5f | ||
|
|
f466d6289e | ||
|
|
851c5a933d | ||
|
|
10048f6579 | ||
|
|
a598b2a910 | ||
|
|
2ec1038ffd | ||
|
|
cbb1da66c3 | ||
|
|
35ab660042 | ||
|
|
40a485db9c | ||
|
|
641e75e6e2 | ||
|
|
2e394bef11 | ||
|
|
4a9dbac435 | ||
|
|
d60dbcf91c | ||
|
|
bd483a63cf | ||
|
|
d5dc0ba855 | ||
|
|
e56a042153 | ||
|
|
1995b5559d | ||
|
|
fe384e06bc | ||
|
|
0198ee9e51 | ||
|
|
72e705cbd6 | ||
|
|
d5afb67a81 | ||
|
|
3894397b07 | ||
|
|
36381f0884 | ||
|
|
5cd1976a4a | ||
|
|
c9ae3f6a02 | ||
|
|
d2322d0114 | ||
|
|
7a431ec7b9 | ||
|
|
114aa68082 | ||
|
|
f07a76d959 | ||
|
|
ca0d77855f | ||
|
|
cd3f577c85 | ||
|
|
9228f97d7d | ||
|
|
6db5175e4e | ||
|
|
fba5ece05c | ||
|
|
01a775a46c | ||
|
|
4918360ea1 | ||
|
|
433a5cf580 | ||
|
|
fc9270c943 | ||
|
|
fa8c5789a6 | ||
|
|
cce56fa19b | ||
|
|
ff9c89da0c | ||
|
|
fde879c347 | ||
|
|
38808509d5 |
4
.github/workflows/lock.yml
vendored
4
.github/workflows/lock.yml
vendored
@@ -16,8 +16,10 @@ concurrency:
|
|||||||
jobs:
|
jobs:
|
||||||
action:
|
action:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
if: github.repository == 'tmux/tmux'
|
||||||
|
timeout-minutes: 10
|
||||||
steps:
|
steps:
|
||||||
- uses: dessant/lock-threads@v6
|
- uses: dessant/lock-threads@89ae32b08ed1a541efecbab17912962a5e38981c # v6
|
||||||
with:
|
with:
|
||||||
github-token: ${{ github.token }}
|
github-token: ${{ github.token }}
|
||||||
issue-inactive-days: '30'
|
issue-inactive-days: '30'
|
||||||
|
|||||||
9
.github/workflows/regress.yml
vendored
9
.github/workflows/regress.yml
vendored
@@ -16,7 +16,8 @@ jobs:
|
|||||||
regress:
|
regress:
|
||||||
name: ${{ matrix.name }}
|
name: ${{ matrix.name }}
|
||||||
runs-on: ${{ matrix.runner }}
|
runs-on: ${{ matrix.runner }}
|
||||||
timeout-minutes: 45
|
if: github.repository == 'tmux/tmux'
|
||||||
|
timeout-minutes: 300
|
||||||
|
|
||||||
strategy:
|
strategy:
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
@@ -37,7 +38,9 @@ jobs:
|
|||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: checkout
|
- name: checkout
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4
|
||||||
|
with:
|
||||||
|
persist-credentials: false
|
||||||
|
|
||||||
- name: dependencies
|
- name: dependencies
|
||||||
if: runner.os == 'Linux'
|
if: runner.os == 'Linux'
|
||||||
@@ -80,7 +83,7 @@ jobs:
|
|||||||
|
|
||||||
- name: logs
|
- name: logs
|
||||||
if: failure()
|
if: failure()
|
||||||
uses: actions/upload-artifact@v4
|
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
|
||||||
with:
|
with:
|
||||||
name: regress-logs-${{ matrix.name }}
|
name: regress-logs-${{ matrix.name }}
|
||||||
path: regress/logs/*.log
|
path: regress/logs/*.log
|
||||||
|
|||||||
20
CHANGES
20
CHANGES
@@ -1,7 +1,4 @@
|
|||||||
CHANGES FROM 3.7b TO 3.8
|
CHANGES FROM 3.7c TO 3.8
|
||||||
|
|
||||||
* Build with jemalloc on macOS to avoid what appears to be a bug in the system
|
|
||||||
calloc(3) (issue 5385).
|
|
||||||
|
|
||||||
* Many improvements to floating panes:
|
* Many improvements to floating panes:
|
||||||
|
|
||||||
@@ -121,8 +118,6 @@ CHANGES FROM 3.7b TO 3.8
|
|||||||
* Copy mode no longer exits at the bottom while a selection is in progress
|
* Copy mode no longer exits at the bottom while a selection is in progress
|
||||||
(issue 5349).
|
(issue 5349).
|
||||||
|
|
||||||
* Fix scrollbar initial state so scrollbars appear on new windows (issue 5339).
|
|
||||||
|
|
||||||
* Add style attributes for dimming colours (dim=) and for hyperlinks (link= and
|
* Add style attributes for dimming colours (dim=) and for hyperlinks (link= and
|
||||||
nolink) (issues 4842 and 4280 from Moritz Angermann).
|
nolink) (issues 4842 and 4280 from Moritz Angermann).
|
||||||
|
|
||||||
@@ -169,7 +164,18 @@ CHANGES FROM 3.7b TO 3.8
|
|||||||
* Do not crash looking for the next or previous session (issue 5344), or when
|
* Do not crash looking for the next or previous session (issue 5344), or when
|
||||||
no client is available.
|
no client is available.
|
||||||
|
|
||||||
* Check time periodically in loops rather than for every item (issue 5367).
|
CHANGES FROM 3.7b TO 3.7c
|
||||||
|
|
||||||
|
* Build with jemalloc on macOS to avoid what appears to be a bug in calloc
|
||||||
|
(issue 5385).
|
||||||
|
|
||||||
|
* Fix scrollbar initial state so they appear on new windows (issue 5339).
|
||||||
|
|
||||||
|
* Check time periodically in loops rather than every one (issue 5367).
|
||||||
|
|
||||||
|
* Use message-style again as default for message-format.
|
||||||
|
|
||||||
|
* Unzoom before creating floating panes to avoid a crash.
|
||||||
|
|
||||||
CHANGES FROM 3.7a TO 3.7b
|
CHANGES FROM 3.7a TO 3.7b
|
||||||
|
|
||||||
|
|||||||
@@ -85,6 +85,7 @@ endif
|
|||||||
|
|
||||||
# List of sources.
|
# List of sources.
|
||||||
dist_tmux_SOURCES = \
|
dist_tmux_SOURCES = \
|
||||||
|
active.c \
|
||||||
alerts.c \
|
alerts.c \
|
||||||
arguments.c \
|
arguments.c \
|
||||||
attributes.c \
|
attributes.c \
|
||||||
@@ -246,9 +247,9 @@ if HAVE_UTF8PROC
|
|||||||
nodist_tmux_SOURCES += compat/utf8proc.c
|
nodist_tmux_SOURCES += compat/utf8proc.c
|
||||||
endif
|
endif
|
||||||
|
|
||||||
# Enable image support.
|
# Enable sixel support.
|
||||||
if ENABLE_IMAGES
|
if ENABLE_SIXEL
|
||||||
dist_tmux_SOURCES += image.c image-sixel.c image-fallback.c
|
dist_tmux_SOURCES += image.c image-sixel.c
|
||||||
endif
|
endif
|
||||||
|
|
||||||
# Add bits for fuzzing if enabled.
|
# Add bits for fuzzing if enabled.
|
||||||
|
|||||||
737
active.c
Normal file
737
active.c
Normal file
@@ -0,0 +1,737 @@
|
|||||||
|
/* $OpenBSD$ */
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2026 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 USE, DATA OR PROFITS, WHETHER IN AN
|
||||||
|
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||||
|
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <sys/types.h>
|
||||||
|
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
#include "tmux.h"
|
||||||
|
|
||||||
|
/* Local last-window stack entry. */
|
||||||
|
struct active_window_entry {
|
||||||
|
u_int window;
|
||||||
|
TAILQ_ENTRY(active_window_entry) entry;
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Local last-window stack. */
|
||||||
|
TAILQ_HEAD(active_window_stack, active_window_entry);
|
||||||
|
|
||||||
|
/* Per-client and per-session local active window state. */
|
||||||
|
struct active_window {
|
||||||
|
struct client *client;
|
||||||
|
struct session *session;
|
||||||
|
|
||||||
|
u_int window;
|
||||||
|
struct active_window_stack lastw;
|
||||||
|
|
||||||
|
RB_ENTRY(active_window) entry;
|
||||||
|
};
|
||||||
|
RB_HEAD(active_windows, active_window);
|
||||||
|
|
||||||
|
/* Pane entry in a client-local last-pane stack. */
|
||||||
|
struct active_pane_entry {
|
||||||
|
struct window_pane *wp;
|
||||||
|
TAILQ_ENTRY(active_pane_entry) entry;
|
||||||
|
};
|
||||||
|
TAILQ_HEAD(active_pane_stack, active_pane_entry);
|
||||||
|
|
||||||
|
/* Per-client and per-window local active pane state. */
|
||||||
|
struct active_pane {
|
||||||
|
struct client *client;
|
||||||
|
struct window *window;
|
||||||
|
enum active_mode mode;
|
||||||
|
struct window_pane *pane;
|
||||||
|
struct active_pane_stack last_panes;
|
||||||
|
|
||||||
|
RB_ENTRY(active_pane) entry;
|
||||||
|
};
|
||||||
|
RB_HEAD(active_panes, active_pane);
|
||||||
|
|
||||||
|
/* Compare local active window states by client and session. */
|
||||||
|
static int
|
||||||
|
active_window_cmp(struct active_window *aw1, struct active_window *aw2)
|
||||||
|
{
|
||||||
|
uintptr_t c1 = (uintptr_t)aw1->client;
|
||||||
|
uintptr_t c2 = (uintptr_t)aw2->client;
|
||||||
|
uintptr_t s1 = (uintptr_t)aw1->session;
|
||||||
|
uintptr_t s2 = (uintptr_t)aw2->session;
|
||||||
|
|
||||||
|
if (c1 < c2)
|
||||||
|
return (-1);
|
||||||
|
if (c1 > c2)
|
||||||
|
return (1);
|
||||||
|
if (s1 < s2)
|
||||||
|
return (-1);
|
||||||
|
if (s1 > s2)
|
||||||
|
return (1);
|
||||||
|
return (0);
|
||||||
|
}
|
||||||
|
RB_GENERATE_STATIC(active_windows, active_window, entry, active_window_cmp);
|
||||||
|
|
||||||
|
/* Compare local active pane states by client and window. */
|
||||||
|
static int
|
||||||
|
active_pane_cmp(struct active_pane *ap1, struct active_pane *ap2)
|
||||||
|
{
|
||||||
|
uintptr_t c1 = (uintptr_t)ap1->client;
|
||||||
|
uintptr_t c2 = (uintptr_t)ap2->client;
|
||||||
|
uintptr_t w1 = (uintptr_t)ap1->window;
|
||||||
|
uintptr_t w2 = (uintptr_t)ap2->window;
|
||||||
|
|
||||||
|
if (c1 < c2)
|
||||||
|
return (-1);
|
||||||
|
if (c1 > c2)
|
||||||
|
return (1);
|
||||||
|
if (w1 < w2)
|
||||||
|
return (-1);
|
||||||
|
if (w1 > w2)
|
||||||
|
return (1);
|
||||||
|
return (0);
|
||||||
|
}
|
||||||
|
RB_GENERATE_STATIC(active_panes, active_pane, entry, active_pane_cmp);
|
||||||
|
|
||||||
|
/* All local active window states. */
|
||||||
|
static struct active_windows active_windows = RB_INITIALIZER(&active_windows);
|
||||||
|
|
||||||
|
/* All local active pane states. */
|
||||||
|
static struct active_panes active_panes = RB_INITIALIZER(&active_panes);
|
||||||
|
|
||||||
|
/* Find local active window state for a client and session. */
|
||||||
|
static struct active_window *
|
||||||
|
active_window_get(struct client *c, struct session *s)
|
||||||
|
{
|
||||||
|
struct active_window aw;
|
||||||
|
|
||||||
|
if (c == NULL || s == NULL)
|
||||||
|
return (NULL);
|
||||||
|
aw.client = c;
|
||||||
|
aw.session = s;
|
||||||
|
return (RB_FIND(active_windows, &active_windows, &aw));
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Add local active window state for a client and session. */
|
||||||
|
static struct active_window *
|
||||||
|
active_window_add(struct client *c, struct session *s, struct winlink *wl)
|
||||||
|
{
|
||||||
|
struct active_window *aw;
|
||||||
|
|
||||||
|
aw = active_window_get(c, s);
|
||||||
|
if (aw == NULL) {
|
||||||
|
aw = xcalloc(1, sizeof *aw);
|
||||||
|
aw->client = c;
|
||||||
|
aw->session = s;
|
||||||
|
TAILQ_INIT(&aw->lastw);
|
||||||
|
RB_INSERT(active_windows, &active_windows, aw);
|
||||||
|
}
|
||||||
|
if (wl != NULL)
|
||||||
|
aw->window = wl->window->id;
|
||||||
|
return (aw);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Remove a window from a local last-window stack. */
|
||||||
|
static void
|
||||||
|
active_window_stack_remove(struct active_window_stack *stack, u_int window)
|
||||||
|
{
|
||||||
|
struct active_window_entry *awe, *awe1;
|
||||||
|
|
||||||
|
TAILQ_FOREACH_SAFE(awe, stack, entry, awe1) {
|
||||||
|
if (awe->window == window) {
|
||||||
|
TAILQ_REMOVE(stack, awe, entry);
|
||||||
|
free(awe);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Push a window onto a local last-window stack. */
|
||||||
|
static void
|
||||||
|
active_window_stack_push(struct active_window_stack *stack, u_int window)
|
||||||
|
{
|
||||||
|
struct active_window_entry *awe;
|
||||||
|
|
||||||
|
active_window_stack_remove(stack, window);
|
||||||
|
awe = xcalloc(1, sizeof *awe);
|
||||||
|
awe->window = window;
|
||||||
|
TAILQ_INSERT_HEAD(stack, awe, entry);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Free local active window state. */
|
||||||
|
static void
|
||||||
|
active_window_free(struct active_window *aw)
|
||||||
|
{
|
||||||
|
struct active_window_entry *awe;
|
||||||
|
|
||||||
|
while (!TAILQ_EMPTY(&aw->lastw)) {
|
||||||
|
awe = TAILQ_FIRST(&aw->lastw);
|
||||||
|
TAILQ_REMOVE(&aw->lastw, awe, entry);
|
||||||
|
free(awe);
|
||||||
|
}
|
||||||
|
RB_REMOVE(active_windows, &active_windows, aw);
|
||||||
|
free(aw);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Resolve local active window state to a valid winlink. */
|
||||||
|
static struct winlink *
|
||||||
|
active_window_resolve(struct active_window *aw)
|
||||||
|
{
|
||||||
|
struct winlink *wl, *next;
|
||||||
|
u_int window;
|
||||||
|
|
||||||
|
if (aw == NULL || aw->session->curw == NULL)
|
||||||
|
return (NULL);
|
||||||
|
|
||||||
|
wl = winlink_find_by_window_id(&aw->session->windows, aw->window);
|
||||||
|
if (wl != NULL)
|
||||||
|
return (wl);
|
||||||
|
|
||||||
|
while (!TAILQ_EMPTY(&aw->lastw)) {
|
||||||
|
window = TAILQ_FIRST(&aw->lastw)->window;
|
||||||
|
|
||||||
|
next = winlink_find_by_window_id(&aw->session->windows, window);
|
||||||
|
active_window_stack_remove(&aw->lastw, window);
|
||||||
|
if (next != NULL) {
|
||||||
|
aw->window = next->window->id;
|
||||||
|
return (next);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
aw->window = aw->session->curw->window->id;
|
||||||
|
return (aw->session->curw);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Notify the server that a client's effective window changed. */
|
||||||
|
static void
|
||||||
|
active_window_changed(struct client *c, struct session *s, struct winlink *wl,
|
||||||
|
struct winlink *old)
|
||||||
|
{
|
||||||
|
struct event_payload *ep;
|
||||||
|
struct cmd_find_state fs;
|
||||||
|
|
||||||
|
if (wl == NULL || wl == old)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (options_get_number(global_options, "focus-events")) {
|
||||||
|
if (old != NULL)
|
||||||
|
window_update_focus(old->window);
|
||||||
|
window_update_focus(wl->window);
|
||||||
|
}
|
||||||
|
winlink_clear_flags(wl);
|
||||||
|
window_update_activity(wl->window);
|
||||||
|
tty_update_window_offset(wl->window);
|
||||||
|
|
||||||
|
if (c != NULL && c->session == s)
|
||||||
|
wl->window->latest = c;
|
||||||
|
|
||||||
|
ep = event_payload_create();
|
||||||
|
cmd_find_from_winlink(&fs, wl, 0);
|
||||||
|
event_payload_set_target(ep, &fs);
|
||||||
|
event_payload_set_client(ep, "client", c);
|
||||||
|
event_payload_set_session(ep, "session", s);
|
||||||
|
event_payload_set_window(ep, "window", wl->window);
|
||||||
|
event_payload_set_window(ep, "new_window", wl->window);
|
||||||
|
event_payload_set_int(ep, "window_index", wl->idx);
|
||||||
|
event_payload_set_int(ep, "new_window_index", wl->idx);
|
||||||
|
if (old != NULL) {
|
||||||
|
event_payload_set_window(ep, "old_window", old->window);
|
||||||
|
event_payload_set_int(ep, "old_window_index", old->idx);
|
||||||
|
}
|
||||||
|
events_fire("client-window-changed", ep);
|
||||||
|
|
||||||
|
if (c != NULL) {
|
||||||
|
c->flags |= CLIENT_REDRAWWINDOW|CLIENT_REDRAWSTATUS|
|
||||||
|
CLIENT_REDRAWBORDERS;
|
||||||
|
tty_update_client_offset(c);
|
||||||
|
status_update_cache(s);
|
||||||
|
}
|
||||||
|
recalculate_sizes();
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Find local active pane state for a client and window. */
|
||||||
|
static struct active_pane *
|
||||||
|
active_pane_get(struct client *c, struct window *w)
|
||||||
|
{
|
||||||
|
struct active_pane ap;
|
||||||
|
|
||||||
|
if (c == NULL || w == NULL)
|
||||||
|
return (NULL);
|
||||||
|
ap.client = c;
|
||||||
|
ap.window = w;
|
||||||
|
return (RB_FIND(active_panes, &active_panes, &ap));
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Add local active pane state for a client and window. */
|
||||||
|
static struct active_pane *
|
||||||
|
active_pane_add(struct client *c, struct window *w)
|
||||||
|
{
|
||||||
|
struct active_pane *ap;
|
||||||
|
|
||||||
|
ap = active_pane_get(c, w);
|
||||||
|
if (ap == NULL) {
|
||||||
|
ap = xcalloc(1, sizeof *ap);
|
||||||
|
ap->client = c;
|
||||||
|
ap->window = w;
|
||||||
|
TAILQ_INIT(&ap->last_panes);
|
||||||
|
RB_INSERT(active_panes, &active_panes, ap);
|
||||||
|
}
|
||||||
|
return (ap);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Remove a pane from a client-local last-pane stack. */
|
||||||
|
static void
|
||||||
|
active_pane_stack_remove(struct active_pane *ap,
|
||||||
|
struct window_pane *wp)
|
||||||
|
{
|
||||||
|
struct active_pane_entry *ape, *ape1;
|
||||||
|
|
||||||
|
TAILQ_FOREACH_SAFE(ape, &ap->last_panes, entry, ape1) {
|
||||||
|
if (ape->wp == wp) {
|
||||||
|
TAILQ_REMOVE(&ap->last_panes, ape, entry);
|
||||||
|
free(ape);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Push a pane onto a client-local last-pane stack. */
|
||||||
|
static void
|
||||||
|
active_pane_stack_push(struct active_pane *ap, struct window_pane *wp)
|
||||||
|
{
|
||||||
|
struct active_pane_entry *ape;
|
||||||
|
|
||||||
|
if (wp == NULL)
|
||||||
|
return;
|
||||||
|
active_pane_stack_remove(ap, wp);
|
||||||
|
ape = xcalloc(1, sizeof *ape);
|
||||||
|
ape->wp = wp;
|
||||||
|
TAILQ_INSERT_HEAD(&ap->last_panes, ape, entry);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Free local active pane state. */
|
||||||
|
static void
|
||||||
|
active_pane_free(struct active_pane *ap)
|
||||||
|
{
|
||||||
|
struct active_pane_entry *ape, *ape1;
|
||||||
|
|
||||||
|
RB_REMOVE(active_panes, &active_panes, ap);
|
||||||
|
TAILQ_FOREACH_SAFE(ape, &ap->last_panes, entry, ape1) {
|
||||||
|
TAILQ_REMOVE(&ap->last_panes, ape, entry);
|
||||||
|
free(ape);
|
||||||
|
}
|
||||||
|
free(ap);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Check whether a pane can be the effective pane for a window. */
|
||||||
|
static int
|
||||||
|
active_pane_valid(struct window *w, struct window_pane *wp)
|
||||||
|
{
|
||||||
|
if (w == NULL || wp == NULL || wp->window != w)
|
||||||
|
return (0);
|
||||||
|
if (!window_has_pane(w, wp))
|
||||||
|
return (0);
|
||||||
|
if (w->modal != NULL && wp != w->modal)
|
||||||
|
return (0);
|
||||||
|
return (1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Get the shared pane for a window, accounting for modal panes. */
|
||||||
|
static struct window_pane *
|
||||||
|
active_pane_default(struct window *w)
|
||||||
|
{
|
||||||
|
if (w == NULL)
|
||||||
|
return (NULL);
|
||||||
|
if (w->modal != NULL)
|
||||||
|
return (w->modal);
|
||||||
|
return (w->active);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Return if a client has local window selection for a session. */
|
||||||
|
int
|
||||||
|
active_is_local_window(struct client *c, struct session *s)
|
||||||
|
{
|
||||||
|
return (active_window_get(c, s) != NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Make a client use local or shared window selection for a session. */
|
||||||
|
void
|
||||||
|
active_set_local_window(struct client *c, struct session *s,
|
||||||
|
enum active_mode mode)
|
||||||
|
{
|
||||||
|
struct active_window *aw;
|
||||||
|
struct winlink *wl, *old;
|
||||||
|
|
||||||
|
if (c == NULL || s == NULL || s->curw == NULL)
|
||||||
|
return;
|
||||||
|
|
||||||
|
old = active_get_effective_winlink(c, s);
|
||||||
|
if (mode == ACTIVE_LOCAL) {
|
||||||
|
active_window_add(c, s, old);
|
||||||
|
if (c != NULL && c->session == s)
|
||||||
|
c->flags |= CLIENT_REDRAWSTATUS;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
aw = active_window_get(c, s);
|
||||||
|
if (aw != NULL) {
|
||||||
|
active_window_free(aw);
|
||||||
|
wl = active_get_effective_winlink(c, s);
|
||||||
|
active_window_changed(c, s, wl, old);
|
||||||
|
if (c != NULL && c->session == s)
|
||||||
|
c->flags |= CLIENT_REDRAWSTATUS;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Return the effective winlink for a client and session. */
|
||||||
|
struct winlink *
|
||||||
|
active_get_effective_winlink(struct client *c, struct session *s)
|
||||||
|
{
|
||||||
|
struct active_window *aw;
|
||||||
|
|
||||||
|
if (s == NULL)
|
||||||
|
return (NULL);
|
||||||
|
aw = active_window_get(c, s);
|
||||||
|
if (aw != NULL)
|
||||||
|
return (active_window_resolve(aw));
|
||||||
|
return (s->curw);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Return the effective window for a client and session. */
|
||||||
|
struct window *
|
||||||
|
active_get_effective_window(struct client *c, struct session *s)
|
||||||
|
{
|
||||||
|
struct winlink *wl;
|
||||||
|
|
||||||
|
wl = active_get_effective_winlink(c, s);
|
||||||
|
if (wl == NULL)
|
||||||
|
return (NULL);
|
||||||
|
return (wl->window);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Select a winlink for a client and session. */
|
||||||
|
int
|
||||||
|
active_select_window(struct client *c, struct session *s, struct winlink *wl)
|
||||||
|
{
|
||||||
|
struct active_window *aw;
|
||||||
|
struct winlink *old;
|
||||||
|
int changed;
|
||||||
|
|
||||||
|
if (wl == NULL)
|
||||||
|
return (-1);
|
||||||
|
aw = active_window_get(c, s);
|
||||||
|
if (aw == NULL)
|
||||||
|
return (session_set_current(s, wl));
|
||||||
|
|
||||||
|
old = active_window_resolve(aw);
|
||||||
|
if (old == wl)
|
||||||
|
return (1);
|
||||||
|
if (old != NULL)
|
||||||
|
active_window_stack_push(&aw->lastw, old->window->id);
|
||||||
|
active_window_stack_remove(&aw->lastw, wl->window->id);
|
||||||
|
aw->window = wl->window->id;
|
||||||
|
changed = (old != wl);
|
||||||
|
active_window_changed(c, s, wl, old);
|
||||||
|
return (changed ? 0 : 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Select a window by index. */
|
||||||
|
int
|
||||||
|
active_select_window_index(struct client *c, struct session *s, int idx)
|
||||||
|
{
|
||||||
|
struct winlink *wl;
|
||||||
|
|
||||||
|
wl = winlink_find_by_index(&s->windows, idx);
|
||||||
|
return (active_select_window(c, s, wl));
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Select the next window. */
|
||||||
|
int
|
||||||
|
active_next_window(struct client *c, struct session *s, int alert)
|
||||||
|
{
|
||||||
|
struct winlink *wl;
|
||||||
|
|
||||||
|
wl = active_get_effective_winlink(c, s);
|
||||||
|
if (wl == NULL)
|
||||||
|
return (-1);
|
||||||
|
do {
|
||||||
|
wl = winlink_next(wl);
|
||||||
|
if (wl == NULL)
|
||||||
|
wl = RB_MIN(winlinks, &s->windows);
|
||||||
|
if (!alert || (wl->flags & WINLINK_ALERTFLAGS))
|
||||||
|
return (active_select_window(c, s, wl));
|
||||||
|
} while (wl != active_get_effective_winlink(c, s));
|
||||||
|
return (-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Select the previous window. */
|
||||||
|
int
|
||||||
|
active_previous_window(struct client *c, struct session *s, int alert)
|
||||||
|
{
|
||||||
|
struct winlink *wl;
|
||||||
|
|
||||||
|
wl = active_get_effective_winlink(c, s);
|
||||||
|
if (wl == NULL)
|
||||||
|
return (-1);
|
||||||
|
do {
|
||||||
|
wl = winlink_previous(wl);
|
||||||
|
if (wl == NULL)
|
||||||
|
wl = RB_MAX(winlinks, &s->windows);
|
||||||
|
if (!alert || (wl->flags & WINLINK_ALERTFLAGS))
|
||||||
|
return (active_select_window(c, s, wl));
|
||||||
|
} while (wl != active_get_effective_winlink(c, s));
|
||||||
|
return (-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Select the last window. */
|
||||||
|
int
|
||||||
|
active_last_window(struct client *c, struct session *s)
|
||||||
|
{
|
||||||
|
struct active_window *aw;
|
||||||
|
struct winlink *wl;
|
||||||
|
u_int window;
|
||||||
|
|
||||||
|
aw = active_window_get(c, s);
|
||||||
|
if (aw == NULL)
|
||||||
|
return (session_last(s));
|
||||||
|
while (!TAILQ_EMPTY(&aw->lastw)) {
|
||||||
|
window = TAILQ_FIRST(&aw->lastw)->window;
|
||||||
|
|
||||||
|
wl = winlink_find_by_window_id(&s->windows, window);
|
||||||
|
if (wl != NULL)
|
||||||
|
return (active_select_window(c, s, wl));
|
||||||
|
active_window_stack_remove(&aw->lastw, window);
|
||||||
|
}
|
||||||
|
return (-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Return if this winlink is the effective winlink. */
|
||||||
|
int
|
||||||
|
active_is_effective_window(struct client *c, struct session *s,
|
||||||
|
struct winlink *wl)
|
||||||
|
{
|
||||||
|
return (wl != NULL && wl == active_get_effective_winlink(c, s));
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Return if this winlink is the last effective winlink. */
|
||||||
|
int
|
||||||
|
active_is_last_window(struct client *c, struct session *s, struct winlink *wl)
|
||||||
|
{
|
||||||
|
struct active_window *aw;
|
||||||
|
struct active_window_entry *awe;
|
||||||
|
|
||||||
|
if (wl == NULL)
|
||||||
|
return (0);
|
||||||
|
aw = active_window_get(c, s);
|
||||||
|
if (aw == NULL)
|
||||||
|
return (wl == TAILQ_FIRST(&s->lastw));
|
||||||
|
TAILQ_FOREACH(awe, &aw->lastw, entry) {
|
||||||
|
if (winlink_find_by_window_id(&s->windows, awe->window) == NULL)
|
||||||
|
continue;
|
||||||
|
return (wl->window->id == awe->window);
|
||||||
|
}
|
||||||
|
return (0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Return if a client has local pane selection for a window. */
|
||||||
|
int
|
||||||
|
active_has_local_pane(struct client *c, struct window *w)
|
||||||
|
{
|
||||||
|
struct active_pane *ap;
|
||||||
|
|
||||||
|
ap = active_pane_get(c, w);
|
||||||
|
return (ap != NULL && ap->mode == ACTIVE_LOCAL);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Make a client use local or shared pane selection for a window. */
|
||||||
|
void
|
||||||
|
active_set_pane_mode(struct client *c, struct window *w, enum active_mode mode)
|
||||||
|
{
|
||||||
|
struct active_pane *ap;
|
||||||
|
struct window_pane *wp;
|
||||||
|
|
||||||
|
if (c == NULL || w == NULL)
|
||||||
|
return;
|
||||||
|
if (mode == ACTIVE_SHARED) {
|
||||||
|
ap = active_pane_get(c, w);
|
||||||
|
if (ap != NULL)
|
||||||
|
ap->mode = ACTIVE_SHARED;
|
||||||
|
server_redraw_client(c);
|
||||||
|
server_status_client(c);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (server_client_how_many() < 2) {
|
||||||
|
ap = active_pane_get(c, w);
|
||||||
|
if (ap != NULL)
|
||||||
|
ap->mode = ACTIVE_SHARED;
|
||||||
|
server_redraw_client(c);
|
||||||
|
server_status_client(c);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
wp = active_get_effective_pane(c, w);
|
||||||
|
if (wp == NULL)
|
||||||
|
wp = active_pane_default(w);
|
||||||
|
ap = active_pane_add(c, w);
|
||||||
|
ap->mode = ACTIVE_LOCAL;
|
||||||
|
ap->pane = wp;
|
||||||
|
server_redraw_client(c);
|
||||||
|
server_status_client(c);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Return the effective active pane for a client and window. */
|
||||||
|
struct window_pane *
|
||||||
|
active_get_effective_pane(struct client *c, struct window *w)
|
||||||
|
{
|
||||||
|
struct active_pane *ap;
|
||||||
|
struct window_pane *wp;
|
||||||
|
|
||||||
|
wp = active_pane_default(w);
|
||||||
|
ap = active_pane_get(c, w);
|
||||||
|
if (ap == NULL || ap->mode != ACTIVE_LOCAL)
|
||||||
|
return (wp);
|
||||||
|
if (!active_pane_valid(w, ap->pane)) {
|
||||||
|
ap->pane = wp;
|
||||||
|
if (wp != NULL)
|
||||||
|
active_pane_stack_remove(ap, wp);
|
||||||
|
return (wp);
|
||||||
|
}
|
||||||
|
return (ap->pane);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Return the effective last pane for a client and window. */
|
||||||
|
struct window_pane *
|
||||||
|
active_get_last_pane(struct client *c, struct window *w)
|
||||||
|
{
|
||||||
|
struct active_pane *ap;
|
||||||
|
struct active_pane_entry *ape, *ape1;
|
||||||
|
|
||||||
|
if (!active_has_local_pane(c, w))
|
||||||
|
return (w == NULL ? NULL : TAILQ_FIRST(&w->last_panes));
|
||||||
|
|
||||||
|
ap = active_pane_get(c, w);
|
||||||
|
TAILQ_FOREACH_SAFE(ape, &ap->last_panes, entry, ape1) {
|
||||||
|
if (active_pane_valid(w, ape->wp))
|
||||||
|
return (ape->wp);
|
||||||
|
TAILQ_REMOVE(&ap->last_panes, ape, entry);
|
||||||
|
free(ape);
|
||||||
|
}
|
||||||
|
return (NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Select a pane using either shared or local state. */
|
||||||
|
int
|
||||||
|
active_set_pane(struct client *c, struct window *w, struct window_pane *wp,
|
||||||
|
int notify)
|
||||||
|
{
|
||||||
|
struct active_pane *ap;
|
||||||
|
struct window_pane *lastwp;
|
||||||
|
|
||||||
|
if (!active_pane_valid(w, wp))
|
||||||
|
return (0);
|
||||||
|
if (!active_has_local_pane(c, w))
|
||||||
|
return (window_set_active_pane(w, wp, notify));
|
||||||
|
|
||||||
|
lastwp = active_get_effective_pane(c, w);
|
||||||
|
if (wp == lastwp)
|
||||||
|
return (0);
|
||||||
|
|
||||||
|
ap = active_pane_add(c, w);
|
||||||
|
active_pane_stack_remove(ap, wp);
|
||||||
|
active_pane_stack_push(ap, lastwp);
|
||||||
|
ap->pane = wp;
|
||||||
|
|
||||||
|
if (c != NULL) {
|
||||||
|
server_redraw_client(c);
|
||||||
|
server_status_client(c);
|
||||||
|
}
|
||||||
|
return (1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Remove all local pane state if fewer than two clients remain. */
|
||||||
|
void
|
||||||
|
active_check_clients(void)
|
||||||
|
{
|
||||||
|
struct active_pane *ap, *ap1;
|
||||||
|
|
||||||
|
if (server_client_how_many() >= 2)
|
||||||
|
return;
|
||||||
|
|
||||||
|
RB_FOREACH_SAFE(ap, active_panes, &active_panes, ap1) {
|
||||||
|
if (ap->mode == ACTIVE_LOCAL) {
|
||||||
|
server_redraw_client(ap->client);
|
||||||
|
server_status_client(ap->client);
|
||||||
|
}
|
||||||
|
active_pane_free(ap);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Remove all state for a client. */
|
||||||
|
void
|
||||||
|
active_remove_client(struct client *c)
|
||||||
|
{
|
||||||
|
struct active_window *aw, *aw1;
|
||||||
|
struct active_pane *ap, *ap1;
|
||||||
|
|
||||||
|
RB_FOREACH_SAFE(aw, active_windows, &active_windows, aw1) {
|
||||||
|
if (aw->client == c)
|
||||||
|
active_window_free(aw);
|
||||||
|
}
|
||||||
|
RB_FOREACH_SAFE(ap, active_panes, &active_panes, ap1) {
|
||||||
|
if (ap->client == c)
|
||||||
|
active_pane_free(ap);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Remove all window state for a session. */
|
||||||
|
void
|
||||||
|
active_remove_session(struct session *s)
|
||||||
|
{
|
||||||
|
struct active_window *aw, *aw1;
|
||||||
|
|
||||||
|
RB_FOREACH_SAFE(aw, active_windows, &active_windows, aw1) {
|
||||||
|
if (aw->session == s)
|
||||||
|
active_window_free(aw);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Remove a window from all local state. */
|
||||||
|
void
|
||||||
|
active_remove_window(struct window *w)
|
||||||
|
{
|
||||||
|
struct active_window *aw;
|
||||||
|
struct active_pane *ap, *ap1;
|
||||||
|
|
||||||
|
RB_FOREACH(aw, active_windows, &active_windows) {
|
||||||
|
active_window_stack_remove(&aw->lastw, w->id);
|
||||||
|
if (aw->window == w->id && aw->session->curw != NULL)
|
||||||
|
aw->window = aw->session->curw->window->id;
|
||||||
|
}
|
||||||
|
RB_FOREACH_SAFE(ap, active_panes, &active_panes, ap1) {
|
||||||
|
if (ap->window == w)
|
||||||
|
active_pane_free(ap);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Remove a pane from all local pane state. */
|
||||||
|
void
|
||||||
|
active_remove_pane(struct window_pane *wp)
|
||||||
|
{
|
||||||
|
struct active_pane *ap, *ap1;
|
||||||
|
|
||||||
|
RB_FOREACH_SAFE(ap, active_panes, &active_panes, ap1) {
|
||||||
|
active_pane_stack_remove(ap, wp);
|
||||||
|
if (ap->pane == wp)
|
||||||
|
ap->pane = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
2
alerts.c
2
alerts.c
@@ -314,7 +314,7 @@ alerts_set_message(struct winlink *wl, const char *type, const char *option)
|
|||||||
tty_putcode(&c->tty, TTYC_BEL);
|
tty_putcode(&c->tty, TTYC_BEL);
|
||||||
if (visual == VISUAL_OFF)
|
if (visual == VISUAL_OFF)
|
||||||
continue;
|
continue;
|
||||||
if (c->session->curw == wl) {
|
if (active_get_effective_winlink(c, c->session) == wl) {
|
||||||
status_message_set(c, -1, 1, 0, 0,
|
status_message_set(c, -1, 1, 0, 0,
|
||||||
"%s in current window", type);
|
"%s in current window", type);
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
/* $OpenBSD: arguments.c,v 1.66 2026/06/26 09:54:56 nicm Exp $ */
|
/* $OpenBSD: arguments.c,v 1.67 2026/08/25 06:04:33 nicm Exp $ */
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2010 Nicholas Marriott <nicholas.marriott@gmail.com>
|
* Copyright (c) 2010 Nicholas Marriott <nicholas.marriott@gmail.com>
|
||||||
@@ -762,8 +762,6 @@ args_make_commands_now(struct cmd *self, struct cmdq_item *item, u_int idx,
|
|||||||
cmdq_error(item, "%s", error);
|
cmdq_error(item, "%s", error);
|
||||||
free(error);
|
free(error);
|
||||||
}
|
}
|
||||||
else
|
|
||||||
cmdlist->references++;
|
|
||||||
args_make_commands_free(state);
|
args_make_commands_free(state);
|
||||||
return (cmdlist);
|
return (cmdlist);
|
||||||
}
|
}
|
||||||
@@ -827,8 +825,10 @@ args_make_commands(struct args_command_state *state, int argc, char **argv,
|
|||||||
int i;
|
int i;
|
||||||
|
|
||||||
if (state->cmdlist != NULL) {
|
if (state->cmdlist != NULL) {
|
||||||
if (argc == 0)
|
if (argc == 0) {
|
||||||
|
state->cmdlist->references++;
|
||||||
return (state->cmdlist);
|
return (state->cmdlist);
|
||||||
|
}
|
||||||
return (cmd_list_copy(state->cmdlist, argc, argv));
|
return (cmd_list_copy(state->cmdlist, argc, argv));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
16
cfg.c
16
cfg.c
@@ -1,4 +1,4 @@
|
|||||||
/* $OpenBSD: cfg.c,v 1.90 2026/07/14 17:17:17 nicm Exp $ */
|
/* $OpenBSD: cfg.c,v 1.91 2026/08/03 13:38:42 nicm Exp $ */
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2008 Nicholas Marriott <nicholas.marriott@gmail.com>
|
* Copyright (c) 2008 Nicholas Marriott <nicholas.marriott@gmail.com>
|
||||||
@@ -223,13 +223,15 @@ cfg_print_causes(struct cmdq_item *item)
|
|||||||
{
|
{
|
||||||
struct client *c = cmdq_get_client(item);
|
struct client *c = cmdq_get_client(item);
|
||||||
u_int i;
|
u_int i;
|
||||||
|
char *cause;
|
||||||
|
|
||||||
for (i = 0; i < cfg_ncauses; i++) {
|
for (i = 0; i < cfg_ncauses; i++) {
|
||||||
|
cause = cfg_causes[i];
|
||||||
if (c != NULL && (c->flags & CLIENT_CONTROL))
|
if (c != NULL && (c->flags & CLIENT_CONTROL))
|
||||||
control_write(c, "%%config-error %s", cfg_causes[i]);
|
control_notify_write(c, "%%config-error %s", cause);
|
||||||
else
|
else
|
||||||
cmdq_print(item, "%s", cfg_causes[i]);
|
cmdq_print(item, "%s", cause);
|
||||||
free(cfg_causes[i]);
|
free(cause);
|
||||||
}
|
}
|
||||||
|
|
||||||
free(cfg_causes);
|
free(cfg_causes);
|
||||||
@@ -244,14 +246,16 @@ cfg_show_causes(struct session *s)
|
|||||||
struct window_pane *wp;
|
struct window_pane *wp;
|
||||||
struct window_mode_entry *wme;
|
struct window_mode_entry *wme;
|
||||||
u_int i;
|
u_int i;
|
||||||
|
char *cause;
|
||||||
|
|
||||||
if (cfg_ncauses == 0)
|
if (cfg_ncauses == 0)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (c != NULL && (c->flags & CLIENT_CONTROL)) {
|
if (c != NULL && (c->flags & CLIENT_CONTROL)) {
|
||||||
for (i = 0; i < cfg_ncauses; i++) {
|
for (i = 0; i < cfg_ncauses; i++) {
|
||||||
control_write(c, "%%config-error %s", cfg_causes[i]);
|
cause = cfg_causes[i];
|
||||||
free(cfg_causes[i]);
|
control_notify_write(c, "%%config-error %s", cause);
|
||||||
|
free(cause);
|
||||||
}
|
}
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|||||||
10
client.c
10
client.c
@@ -1,4 +1,4 @@
|
|||||||
/* $OpenBSD: client.c,v 1.166 2026/07/10 15:45:11 nicm Exp $ */
|
/* $OpenBSD: client.c,v 1.168 2026/08/17 20:14:31 nicm Exp $ */
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>
|
* Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>
|
||||||
@@ -276,7 +276,7 @@ client_main(struct event_base *base, int argc, char **argv, uint64_t flags,
|
|||||||
proc_set_signals(client_proc, client_signal);
|
proc_set_signals(client_proc, client_signal);
|
||||||
|
|
||||||
/* Save the flags. */
|
/* Save the flags. */
|
||||||
client_flags = flags;
|
client_flags = flags|CLIENT_WRITE_ACK;
|
||||||
log_debug("flags are %#llx", (unsigned long long)client_flags);
|
log_debug("flags are %#llx", (unsigned long long)client_flags);
|
||||||
|
|
||||||
/* Initialize the client socket and start the server. */
|
/* Initialize the client socket and start the server. */
|
||||||
@@ -712,6 +712,9 @@ client_dispatch_wait(struct imsg *imsg)
|
|||||||
fprintf(stderr, "server version is too old for client\n");
|
fprintf(stderr, "server version is too old for client\n");
|
||||||
proc_exit(client_proc);
|
proc_exit(client_proc);
|
||||||
break;
|
break;
|
||||||
|
default:
|
||||||
|
log_debug("unknown message type %u", imsg->hdr.type);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -798,5 +801,8 @@ client_dispatch_attached(struct imsg *imsg)
|
|||||||
system(data);
|
system(data);
|
||||||
proc_send(client_peer, MSG_UNLOCK, -1, NULL, 0);
|
proc_send(client_peer, MSG_UNLOCK, -1, NULL, 0);
|
||||||
break;
|
break;
|
||||||
|
default:
|
||||||
|
log_debug("unknown message type %u", imsg->hdr.type);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -93,7 +93,7 @@ cmd_attach_session(struct cmdq_item *item, const char *tflag, int dflag,
|
|||||||
if (wl != NULL) {
|
if (wl != NULL) {
|
||||||
if (wp != NULL)
|
if (wp != NULL)
|
||||||
window_set_active_pane(wp->window, wp, 1);
|
window_set_active_pane(wp->window, wp, 1);
|
||||||
session_set_current(s, wl);
|
active_select_window(c, s, wl);
|
||||||
if (wp != NULL)
|
if (wp != NULL)
|
||||||
cmd_find_from_winlink_pane(current, wl, wp, 0);
|
cmd_find_from_winlink_pane(current, wl, wp, 0);
|
||||||
else
|
else
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
/* $OpenBSD: cmd-break-pane.c,v 1.75 2026/07/23 09:38:27 nicm Exp $ */
|
/* $OpenBSD: cmd-break-pane.c,v 1.76 2026/08/17 07:04:45 nicm Exp $ */
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2009 Nicholas Marriott <nicholas.marriott@gmail.com>
|
* Copyright (c) 2009 Nicholas Marriott <nicholas.marriott@gmail.com>
|
||||||
@@ -93,14 +93,18 @@ cmd_break_pane_exec(struct cmd *self, struct cmdq_item *item)
|
|||||||
struct cmd_find_state *current = cmdq_get_current(item);
|
struct cmd_find_state *current = cmdq_get_current(item);
|
||||||
struct cmd_find_state *target = cmdq_get_target(item);
|
struct cmd_find_state *target = cmdq_get_target(item);
|
||||||
struct cmd_find_state *source = cmdq_get_source(item);
|
struct cmd_find_state *source = cmdq_get_source(item);
|
||||||
|
struct client *c = cmdq_get_client(item);
|
||||||
struct client *tc = cmdq_get_target_client(item);
|
struct client *tc = cmdq_get_target_client(item);
|
||||||
|
struct client *ac;
|
||||||
struct winlink *wl = source->wl;
|
struct winlink *wl = source->wl;
|
||||||
|
struct winlink *current_wl;
|
||||||
struct session *src_s = source->s;
|
struct session *src_s = source->s;
|
||||||
struct session *dst_s = target->s;
|
struct session *dst_s = target->s;
|
||||||
struct window_pane *wp = source->wp;
|
struct window_pane *wp = source->wp;
|
||||||
struct window *w = wl->window, *old_w = w;
|
struct window *w = wl->window, *old_w = w;
|
||||||
char *cause, *cp;
|
char *cause, *cp;
|
||||||
int idx = target->idx, before, old_idx = wl->idx;
|
int idx = target->idx, before, old_idx = wl->idx;
|
||||||
|
int detached, local;
|
||||||
const char *template, *name = args_get(args, 'n');
|
const char *template, *name = args_get(args, 'n');
|
||||||
|
|
||||||
if (wp == w->modal) {
|
if (wp == w->modal) {
|
||||||
@@ -117,11 +121,18 @@ cmd_break_pane_exec(struct cmd *self, struct cmdq_item *item)
|
|||||||
}
|
}
|
||||||
|
|
||||||
before = args_has(args, 'b');
|
before = args_has(args, 'b');
|
||||||
|
ac = tc;
|
||||||
|
if (ac == NULL || ac->session != dst_s)
|
||||||
|
ac = c;
|
||||||
|
detached = args_has(args, 'd');
|
||||||
|
local = (!detached && active_is_local_window(ac, dst_s));
|
||||||
if (args_has(args, 'a') || before) {
|
if (args_has(args, 'a') || before) {
|
||||||
if (target->wl != NULL)
|
if (target->wl != NULL)
|
||||||
idx = winlink_shuffle_up(dst_s, target->wl, before);
|
idx = winlink_shuffle_up(dst_s, target->wl, before);
|
||||||
else
|
else {
|
||||||
idx = winlink_shuffle_up(dst_s, dst_s->curw, before);
|
current_wl = active_get_effective_winlink(ac, dst_s);
|
||||||
|
idx = winlink_shuffle_up(dst_s, current_wl, before);
|
||||||
|
}
|
||||||
if (idx == -1)
|
if (idx == -1)
|
||||||
return (CMD_RETURN_ERROR);
|
return (CMD_RETURN_ERROR);
|
||||||
}
|
}
|
||||||
@@ -129,7 +140,7 @@ cmd_break_pane_exec(struct cmd *self, struct cmdq_item *item)
|
|||||||
|
|
||||||
if (window_count_panes(w, 1) == 1) {
|
if (window_count_panes(w, 1) == 1) {
|
||||||
if (server_link_window(src_s, wl, dst_s, idx, 0,
|
if (server_link_window(src_s, wl, dst_s, idx, 0,
|
||||||
!args_has(args, 'd'), &cause) != 0) {
|
!detached && !local, &cause) != 0) {
|
||||||
cmdq_error(item, "%s", cause);
|
cmdq_error(item, "%s", cause);
|
||||||
free(cause);
|
free(cause);
|
||||||
return (CMD_RETURN_ERROR);
|
return (CMD_RETURN_ERROR);
|
||||||
@@ -142,6 +153,13 @@ cmd_break_pane_exec(struct cmd *self, struct cmdq_item *item)
|
|||||||
wl = winlink_find_by_window(&dst_s->windows, w);
|
wl = winlink_find_by_window(&dst_s->windows, w);
|
||||||
if (wl == NULL)
|
if (wl == NULL)
|
||||||
return (CMD_RETURN_ERROR);
|
return (CMD_RETURN_ERROR);
|
||||||
|
if (local) {
|
||||||
|
active_select_window(ac, dst_s, wl);
|
||||||
|
if (ac != NULL && ac->session == dst_s)
|
||||||
|
cmd_find_from_client(current, ac, 0);
|
||||||
|
if (ac != NULL && ac->session == dst_s)
|
||||||
|
server_redraw_client(ac);
|
||||||
|
}
|
||||||
window_fire_pane_moved(wp, old_w, old_idx, w, wl->idx);
|
window_fire_pane_moved(wp, old_w, old_idx, w, wl->idx);
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
@@ -174,19 +192,28 @@ cmd_break_pane_exec(struct cmd *self, struct cmdq_item *item)
|
|||||||
}
|
}
|
||||||
window_set_fill_cells(w);
|
window_set_fill_cells(w);
|
||||||
|
|
||||||
|
if (idx == -1)
|
||||||
|
idx = -1 - options_get_number(dst_s->options, "base-index");
|
||||||
|
wl = session_attach(dst_s, w, idx, &cause); /* can't fail */
|
||||||
|
|
||||||
layout_init(w, wp);
|
layout_init(w, wp);
|
||||||
wp->flags |= PANE_CHANGED;
|
wp->flags |= PANE_CHANGED;
|
||||||
colour_palette_from_option(&wp->palette, wp->options);
|
colour_palette_from_option(&wp->palette, wp->options);
|
||||||
|
|
||||||
if (idx == -1)
|
|
||||||
idx = -1 - options_get_number(dst_s->options, "base-index");
|
|
||||||
wl = session_attach(dst_s, w, idx, &cause); /* can't fail */
|
|
||||||
window_remove_ref(w, __func__);
|
window_remove_ref(w, __func__);
|
||||||
events_fire_window("window-created", w);
|
events_fire_window("window-created", w);
|
||||||
window_fire_pane_moved(wp, old_w, old_idx, w, wl->idx);
|
window_fire_pane_moved(wp, old_w, old_idx, w, wl->idx);
|
||||||
if (!args_has(args, 'd')) {
|
if (!detached) {
|
||||||
|
if (local)
|
||||||
|
active_select_window(ac, dst_s, wl);
|
||||||
|
else
|
||||||
session_select(dst_s, wl->idx);
|
session_select(dst_s, wl->idx);
|
||||||
|
if (local && ac != NULL && ac->session == dst_s)
|
||||||
|
cmd_find_from_client(current, ac, 0);
|
||||||
|
else
|
||||||
cmd_find_from_session(current, dst_s, 0);
|
cmd_find_from_session(current, dst_s, 0);
|
||||||
|
if (local && ac != NULL && ac->session == dst_s)
|
||||||
|
server_redraw_client(ac);
|
||||||
}
|
}
|
||||||
|
|
||||||
server_redraw_session(src_s);
|
server_redraw_session(src_s);
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
/* $OpenBSD: cmd-command-prompt.c,v 1.75 2026/06/25 11:39:11 nicm Exp $ */
|
/* $OpenBSD: cmd-command-prompt.c,v 1.76 2026/08/25 06:04:33 nicm Exp $ */
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2008 Nicholas Marriott <nicholas.marriott@gmail.com>
|
* Copyright (c) 2008 Nicholas Marriott <nicholas.marriott@gmail.com>
|
||||||
@@ -242,9 +242,11 @@ cmd_command_prompt_callback(struct client *c, void *data, const char *s,
|
|||||||
} else if (item == NULL) {
|
} else if (item == NULL) {
|
||||||
new_item = cmdq_get_command(cmdlist, NULL);
|
new_item = cmdq_get_command(cmdlist, NULL);
|
||||||
cmdq_append(c, new_item);
|
cmdq_append(c, new_item);
|
||||||
|
cmd_list_free(cmdlist);
|
||||||
} else {
|
} else {
|
||||||
new_item = cmdq_get_command(cmdlist, cmdq_get_state(item));
|
new_item = cmdq_get_command(cmdlist, cmdq_get_state(item));
|
||||||
cmdq_insert_after(item, new_item);
|
cmdq_insert_after(item, new_item);
|
||||||
|
cmd_list_free(cmdlist);
|
||||||
}
|
}
|
||||||
cmd_free_argv(argc, argv);
|
cmd_free_argv(argc, argv);
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
/* $OpenBSD: cmd-display-menu.c,v 1.52 2026/07/14 19:07:03 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>
|
||||||
@@ -483,7 +483,7 @@ cmd_display_menu_exec(struct cmd *self, struct cmdq_item *item)
|
|||||||
char *title, *cause = NULL;
|
char *title, *cause = NULL;
|
||||||
int flags = 0, starting_choice = 0;
|
int flags = 0, starting_choice = 0;
|
||||||
u_int px, py, i, count = args_count(args);
|
u_int px, py, i, count = args_count(args);
|
||||||
struct options *o = target->s->curw->window->options;
|
struct options *o = target->w->options;
|
||||||
struct options_entry *oe;
|
struct options_entry *oe;
|
||||||
|
|
||||||
if (args_has(args, 'C')) {
|
if (args_has(args, 'C')) {
|
||||||
@@ -584,13 +584,15 @@ cmd_display_popup_exec(struct cmd *self, struct cmdq_item *item)
|
|||||||
u_int px, py, w, h, count = args_count(args);
|
u_int px, py, w, h, count = args_count(args);
|
||||||
struct args_value *av;
|
struct args_value *av;
|
||||||
struct environ *env = NULL;
|
struct environ *env = NULL;
|
||||||
struct options *o = s->curw->window->options;
|
struct options *o = target->w->options;
|
||||||
struct options_entry *oe;
|
struct options_entry *oe;
|
||||||
|
|
||||||
if (args_has(args, 'C')) {
|
if (args_has(args, 'C')) {
|
||||||
server_client_clear_overlay(tc);
|
server_client_clear_overlay(tc);
|
||||||
return (CMD_RETURN_NORMAL);
|
return (CMD_RETURN_NORMAL);
|
||||||
}
|
}
|
||||||
|
if (tc->flags & CLIENT_CONTROL)
|
||||||
|
return (CMD_RETURN_NORMAL);
|
||||||
if (!modify && tc->overlay_draw != NULL)
|
if (!modify && tc->overlay_draw != NULL)
|
||||||
return (CMD_RETURN_NORMAL);
|
return (CMD_RETURN_NORMAL);
|
||||||
|
|
||||||
|
|||||||
84
cmd-find.c
84
cmd-find.c
@@ -884,13 +884,13 @@ cmd_find_from_client(struct cmd_find_state *fs, struct client *c, int flags)
|
|||||||
if (c->session != NULL) {
|
if (c->session != NULL) {
|
||||||
cmd_find_clear_state(fs, flags);
|
cmd_find_clear_state(fs, flags);
|
||||||
|
|
||||||
fs->wp = c->session->curw->window->active;
|
fs->wl = active_get_effective_winlink(c, c->session);
|
||||||
|
fs->wp = active_get_effective_pane(c, fs->wl->window);
|
||||||
if (fs->wp == NULL) {
|
if (fs->wp == NULL) {
|
||||||
cmd_find_from_session(fs, c->session, flags);
|
cmd_find_from_session(fs, c->session, flags);
|
||||||
return (0);
|
return (0);
|
||||||
}
|
}
|
||||||
fs->s = c->session;
|
fs->s = c->session;
|
||||||
fs->wl = fs->s->curw;
|
|
||||||
fs->w = fs->wl->window;
|
fs->w = fs->wl->window;
|
||||||
|
|
||||||
cmd_find_log_state(__func__, fs);
|
cmd_find_log_state(__func__, fs);
|
||||||
@@ -931,6 +931,57 @@ unknown_pane:
|
|||||||
return (cmd_find_from_nothing(fs, flags));
|
return (cmd_find_from_nothing(fs, flags));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Find state from mouse target. */
|
||||||
|
static int
|
||||||
|
cmd_find_target_from_mouse(struct cmd_find_state *fs, struct cmdq_item *item,
|
||||||
|
enum cmd_find_type type)
|
||||||
|
{
|
||||||
|
struct mouse_event *m = &cmdq_get_event(item)->m;
|
||||||
|
struct client *c = cmdq_get_client(item);
|
||||||
|
struct client *tc = cmdq_get_target_client(item);
|
||||||
|
|
||||||
|
switch (type) {
|
||||||
|
case CMD_FIND_PANE:
|
||||||
|
if (m->s != -1 && m->w == -1 && m->wp == -1 &&
|
||||||
|
(fs->s = session_find_by_id(m->s)) != NULL) {
|
||||||
|
if (tc != NULL && tc->session == fs->s)
|
||||||
|
fs->wl = active_get_effective_winlink(tc, fs->s);
|
||||||
|
else if (c != NULL && c->session == fs->s)
|
||||||
|
fs->wl = active_get_effective_winlink(c, fs->s);
|
||||||
|
else
|
||||||
|
fs->wl = fs->s->curw;
|
||||||
|
if (fs->wl != NULL) {
|
||||||
|
fs->w = fs->wl->window;
|
||||||
|
fs->wp = fs->w->active;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fs->wp = cmd_mouse_pane(m, &fs->s, &fs->wl);
|
||||||
|
if (fs->wp != NULL) {
|
||||||
|
fs->w = fs->wl->window;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
/* FALLTHROUGH */
|
||||||
|
case CMD_FIND_WINDOW:
|
||||||
|
case CMD_FIND_SESSION:
|
||||||
|
fs->wl = cmd_mouse_window(m, &fs->s);
|
||||||
|
if (fs->s != NULL && m->w == -1 &&
|
||||||
|
tc != NULL && tc->session == fs->s)
|
||||||
|
fs->wl = active_get_effective_winlink(tc, fs->s);
|
||||||
|
else if (fs->s != NULL && m->w == -1 &&
|
||||||
|
c != NULL && c->session == fs->s)
|
||||||
|
fs->wl = active_get_effective_winlink(c, fs->s);
|
||||||
|
if (fs->wl == NULL && fs->s != NULL)
|
||||||
|
fs->wl = fs->s->curw;
|
||||||
|
if (fs->wl != NULL) {
|
||||||
|
fs->w = fs->wl->window;
|
||||||
|
fs->wp = fs->w->active;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return (fs->wp == NULL ? -1 : 0);
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Split target into pieces and resolve for the given type. Fills in the given
|
* Split target into pieces and resolve for the given type. Fills in the given
|
||||||
* state. Returns 0 on success or -1 on error.
|
* state. Returns 0 on success or -1 on error.
|
||||||
@@ -939,7 +990,6 @@ int
|
|||||||
cmd_find_target(struct cmd_find_state *fs, struct cmdq_item *item,
|
cmd_find_target(struct cmd_find_state *fs, struct cmdq_item *item,
|
||||||
const char *target, enum cmd_find_type type, int flags)
|
const char *target, enum cmd_find_type type, int flags)
|
||||||
{
|
{
|
||||||
struct mouse_event *m;
|
|
||||||
struct client *c;
|
struct client *c;
|
||||||
struct cmd_find_state current;
|
struct cmd_find_state current;
|
||||||
char *colon, *period, *copy = NULL, tmp[256];
|
char *colon, *period, *copy = NULL, tmp[256];
|
||||||
@@ -1015,35 +1065,15 @@ cmd_find_target(struct cmd_find_state *fs, struct cmdq_item *item,
|
|||||||
cmdq_error(item, "no current client");
|
cmdq_error(item, "no current client");
|
||||||
goto error;
|
goto error;
|
||||||
}
|
}
|
||||||
fs->wl = c->session->curw;
|
fs->wl = active_get_effective_winlink(c, c->session);
|
||||||
fs->wp = c->session->curw->window->active;
|
fs->wp = fs->wl->window->active;
|
||||||
fs->w = c->session->curw->window;
|
fs->w = fs->wl->window;
|
||||||
goto found;
|
goto found;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Mouse target is a plain = or {mouse}. */
|
/* Mouse target is a plain = or {mouse}. */
|
||||||
if (strcmp(target, "=") == 0 || strcmp(target, "{mouse}") == 0) {
|
if (strcmp(target, "=") == 0 || strcmp(target, "{mouse}") == 0) {
|
||||||
m = &cmdq_get_event(item)->m;
|
if (cmd_find_target_from_mouse(fs, item, type) != 0) {
|
||||||
switch (type) {
|
|
||||||
case CMD_FIND_PANE:
|
|
||||||
fs->wp = cmd_mouse_pane(m, &fs->s, &fs->wl);
|
|
||||||
if (fs->wp != NULL) {
|
|
||||||
fs->w = fs->wl->window;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
/* FALLTHROUGH */
|
|
||||||
case CMD_FIND_WINDOW:
|
|
||||||
case CMD_FIND_SESSION:
|
|
||||||
fs->wl = cmd_mouse_window(m, &fs->s);
|
|
||||||
if (fs->wl == NULL && fs->s != NULL)
|
|
||||||
fs->wl = fs->s->curw;
|
|
||||||
if (fs->wl != NULL) {
|
|
||||||
fs->w = fs->wl->window;
|
|
||||||
fs->wp = fs->w->active;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if (fs->wp == NULL) {
|
|
||||||
if (~flags & CMD_FIND_QUIET)
|
if (~flags & CMD_FIND_QUIET)
|
||||||
cmdq_error(item, "no mouse target");
|
cmdq_error(item, "no mouse target");
|
||||||
goto error;
|
goto error;
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
/* $OpenBSD: cmd-if-shell.c,v 1.86 2025/08/01 09:05:51 nicm Exp $ */
|
/* $OpenBSD: cmd-if-shell.c,v 1.87 2026/08/25 06:04:33 nicm Exp $ */
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2009 Tiago Cunha <me@tiagocunha.org>
|
* Copyright (c) 2009 Tiago Cunha <me@tiagocunha.org>
|
||||||
@@ -98,6 +98,7 @@ cmd_if_shell_exec(struct cmd *self, struct cmdq_item *item)
|
|||||||
return (CMD_RETURN_ERROR);
|
return (CMD_RETURN_ERROR);
|
||||||
new_item = cmdq_get_command(cmdlist, cmdq_get_state(item));
|
new_item = cmdq_get_command(cmdlist, cmdq_get_state(item));
|
||||||
cmdq_insert_after(item, new_item);
|
cmdq_insert_after(item, new_item);
|
||||||
|
cmd_list_free(cmdlist);
|
||||||
return (CMD_RETURN_NORMAL);
|
return (CMD_RETURN_NORMAL);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -164,9 +165,11 @@ cmd_if_shell_callback(struct job *job)
|
|||||||
} else if (item == NULL) {
|
} else if (item == NULL) {
|
||||||
new_item = cmdq_get_command(cmdlist, NULL);
|
new_item = cmdq_get_command(cmdlist, NULL);
|
||||||
cmdq_append(c, new_item);
|
cmdq_append(c, new_item);
|
||||||
|
cmd_list_free(cmdlist);
|
||||||
} else {
|
} else {
|
||||||
new_item = cmdq_get_command(cmdlist, cmdq_get_state(item));
|
new_item = cmdq_get_command(cmdlist, cmdq_get_state(item));
|
||||||
cmdq_insert_after(item, new_item);
|
cmdq_insert_after(item, new_item);
|
||||||
|
cmd_list_free(cmdlist);
|
||||||
}
|
}
|
||||||
|
|
||||||
out:
|
out:
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
/* $OpenBSD: cmd-join-pane.c,v 1.73 2026/07/17 15:24:30 nicm Exp $ */
|
/* $OpenBSD: cmd-join-pane.c,v 1.74 2026/08/03 20:29:52 nicm Exp $ */
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2011 George Nachman <tmux@georgester.com>
|
* Copyright (c) 2011 George Nachman <tmux@georgester.com>
|
||||||
@@ -413,13 +413,16 @@ cmd_join_pane_exec(struct cmd *self, struct cmdq_item *item)
|
|||||||
struct cmd_find_state *current = cmdq_get_current(item);
|
struct cmd_find_state *current = cmdq_get_current(item);
|
||||||
struct cmd_find_state *target = cmdq_get_target(item);
|
struct cmd_find_state *target = cmdq_get_target(item);
|
||||||
struct cmd_find_state *source = cmdq_get_source(item);
|
struct cmd_find_state *source = cmdq_get_source(item);
|
||||||
|
struct client *c = cmdq_get_client(item);
|
||||||
|
struct client *tc = cmdq_get_target_client(item);
|
||||||
|
struct client *ac;
|
||||||
struct session *dst_s;
|
struct session *dst_s;
|
||||||
struct winlink *src_wl, *dst_wl;
|
struct winlink *src_wl, *dst_wl;
|
||||||
struct window *src_w, *dst_w;
|
struct window *src_w, *dst_w;
|
||||||
struct window_pane *src_wp, *dst_wp;
|
struct window_pane *src_wp, *dst_wp;
|
||||||
const char *s;
|
const char *s;
|
||||||
char *cause = NULL;
|
char *cause = NULL;
|
||||||
int flags = 0, dst_idx;
|
int flags = 0, dst_idx, local;
|
||||||
struct layout_cell *lc;
|
struct layout_cell *lc;
|
||||||
|
|
||||||
dst_s = target->s;
|
dst_s = target->s;
|
||||||
@@ -427,29 +430,31 @@ cmd_join_pane_exec(struct cmd *self, struct cmdq_item *item)
|
|||||||
dst_wp = target->wp;
|
dst_wp = target->wp;
|
||||||
dst_w = dst_wl->window;
|
dst_w = dst_wl->window;
|
||||||
dst_idx = dst_wl->idx;
|
dst_idx = dst_wl->idx;
|
||||||
|
ac = tc;
|
||||||
|
if (ac == NULL || ac->session != dst_s)
|
||||||
|
ac = c;
|
||||||
|
local = active_is_local_window(ac, dst_s);
|
||||||
|
|
||||||
if (cmd_get_entry(self) == &cmd_move_pane_entry) {
|
if (cmd_get_entry(self) == &cmd_move_pane_entry) {
|
||||||
if (args_has(args, 'M'))
|
if (args_has(args, 'M'))
|
||||||
return (cmd_join_pane_mouse_update(item));
|
return (cmd_join_pane_mouse_update(item));
|
||||||
if (!window_pane_is_floating(dst_wp)) {
|
if (args_has(args, 'P') ||
|
||||||
cmdq_error(item, "pane is not floating");
|
args_has(args, 'z') ||
|
||||||
return (CMD_RETURN_ERROR);
|
args_has(args, 'X') ||
|
||||||
}
|
|
||||||
if ((s = args_get(args, 'P')) != NULL) {
|
|
||||||
server_unzoom_window(dst_w);
|
|
||||||
return (cmd_join_pane_place(item, dst_wl, dst_wp, s));
|
|
||||||
}
|
|
||||||
if ((s = args_get(args, 'z')) != NULL) {
|
|
||||||
server_unzoom_window(dst_w);
|
|
||||||
return (cmd_join_pane_zindex(item, dst_wl, dst_wp, s));
|
|
||||||
}
|
|
||||||
if (args_has(args, 'X') ||
|
|
||||||
args_has(args, 'Y') ||
|
args_has(args, 'Y') ||
|
||||||
args_has(args, 'U') ||
|
args_has(args, 'U') ||
|
||||||
args_has(args, 'D') ||
|
args_has(args, 'D') ||
|
||||||
args_has(args, 'L') ||
|
args_has(args, 'L') ||
|
||||||
args_has(args, 'R')) {
|
args_has(args, 'R')) {
|
||||||
|
if (!window_pane_is_floating(dst_wp)) {
|
||||||
|
cmdq_error(item, "pane is not floating");
|
||||||
|
return (CMD_RETURN_ERROR);
|
||||||
|
}
|
||||||
server_unzoom_window(dst_w);
|
server_unzoom_window(dst_w);
|
||||||
|
if ((s = args_get(args, 'P')) != NULL)
|
||||||
|
return (cmd_join_pane_place(item, dst_wl, dst_wp, s));
|
||||||
|
if ((s = args_get(args, 'z')) != NULL)
|
||||||
|
return (cmd_join_pane_zindex(item, dst_wl, dst_wp, s));
|
||||||
return (cmd_join_pane_move(item, args, dst_wl, dst_wp));
|
return (cmd_join_pane_move(item, args, dst_wl, dst_wp));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -514,8 +519,17 @@ cmd_join_pane_exec(struct cmd *self, struct cmdq_item *item)
|
|||||||
|
|
||||||
if (!args_has(args, 'd')) {
|
if (!args_has(args, 'd')) {
|
||||||
window_set_active_pane(dst_w, src_wp, 1);
|
window_set_active_pane(dst_w, src_wp, 1);
|
||||||
|
if (local)
|
||||||
|
active_select_window(ac, dst_s, dst_wl);
|
||||||
|
else
|
||||||
session_select(dst_s, dst_idx);
|
session_select(dst_s, dst_idx);
|
||||||
|
if (local && ac != NULL && ac->session == dst_s)
|
||||||
|
cmd_find_from_client(current, ac, 0);
|
||||||
|
else
|
||||||
cmd_find_from_session(current, dst_s, 0);
|
cmd_find_from_session(current, dst_s, 0);
|
||||||
|
if (local && ac != NULL && ac->session == dst_s)
|
||||||
|
server_redraw_client(ac);
|
||||||
|
else
|
||||||
server_redraw_session(dst_s);
|
server_redraw_session(dst_s);
|
||||||
} else
|
} else
|
||||||
server_status_session(dst_s);
|
server_status_session(dst_s);
|
||||||
|
|||||||
@@ -62,12 +62,15 @@ cmd_move_window_exec(struct cmd *self, struct cmdq_item *item)
|
|||||||
struct args *args = cmd_get_args(self);
|
struct args *args = cmd_get_args(self);
|
||||||
struct cmd_find_state *source = cmdq_get_source(item);
|
struct cmd_find_state *source = cmdq_get_source(item);
|
||||||
struct cmd_find_state target;
|
struct cmd_find_state target;
|
||||||
|
struct client *c = cmdq_get_client(item);
|
||||||
|
struct client *tc = cmdq_get_target_client(item);
|
||||||
|
struct client *ac;
|
||||||
const char *tflag = args_get(args, 't');
|
const char *tflag = args_get(args, 't');
|
||||||
struct session *src = source->s;
|
struct session *src = source->s;
|
||||||
struct session *dst;
|
struct session *dst;
|
||||||
struct winlink *wl = source->wl;
|
struct winlink *wl = source->wl, *dstwl, *current_wl;
|
||||||
char *cause;
|
char *cause;
|
||||||
int idx, kflag, dflag, sflag, before;
|
int idx, kflag, dflag, sflag, before, local;
|
||||||
|
|
||||||
if (args_has(args, 'r')) {
|
if (args_has(args, 'r')) {
|
||||||
if (cmd_find_target(&target, item, tflag, CMD_FIND_SESSION,
|
if (cmd_find_target(&target, item, tflag, CMD_FIND_SESSION,
|
||||||
@@ -90,23 +93,37 @@ cmd_move_window_exec(struct cmd *self, struct cmdq_item *item)
|
|||||||
dflag = args_has(args, 'd');
|
dflag = args_has(args, 'd');
|
||||||
sflag = args_has(args, 's');
|
sflag = args_has(args, 's');
|
||||||
|
|
||||||
|
ac = tc;
|
||||||
|
if (ac == NULL || ac->session != dst)
|
||||||
|
ac = c;
|
||||||
|
local = (!dflag && active_is_local_window(ac, dst));
|
||||||
|
|
||||||
before = args_has(args, 'b');
|
before = args_has(args, 'b');
|
||||||
if (args_has(args, 'a') || before) {
|
if (args_has(args, 'a') || before) {
|
||||||
if (target.wl != NULL)
|
if (target.wl != NULL)
|
||||||
idx = winlink_shuffle_up(dst, target.wl, before);
|
idx = winlink_shuffle_up(dst, target.wl, before);
|
||||||
else
|
else {
|
||||||
idx = winlink_shuffle_up(dst, dst->curw, before);
|
current_wl = active_get_effective_winlink(ac, dst);
|
||||||
|
idx = winlink_shuffle_up(dst, current_wl, before);
|
||||||
|
}
|
||||||
if (idx == -1)
|
if (idx == -1)
|
||||||
return (CMD_RETURN_ERROR);
|
return (CMD_RETURN_ERROR);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (server_link_window(src, wl, dst, idx, kflag, !dflag, &cause) != 0) {
|
if (server_link_window(src, wl, dst, idx, kflag, !dflag && !local,
|
||||||
|
&cause) != 0) {
|
||||||
cmdq_error(item, "%s", cause);
|
cmdq_error(item, "%s", cause);
|
||||||
free(cause);
|
free(cause);
|
||||||
return (CMD_RETURN_ERROR);
|
return (CMD_RETURN_ERROR);
|
||||||
}
|
}
|
||||||
|
dstwl = winlink_find_by_window(&dst->windows, wl->window);
|
||||||
if (cmd_get_entry(self) == &cmd_move_window_entry)
|
if (cmd_get_entry(self) == &cmd_move_window_entry)
|
||||||
server_unlink_window(src, wl);
|
server_unlink_window(src, wl);
|
||||||
|
if (local && dstwl != NULL) {
|
||||||
|
active_select_window(ac, dst, dstwl);
|
||||||
|
if (ac != NULL && ac->session == dst)
|
||||||
|
server_redraw_client(ac);
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Renumber the winlinks in the src session only, the destination
|
* Renumber the winlinks in the src session only, the destination
|
||||||
|
|||||||
@@ -58,9 +58,11 @@ cmd_new_window_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 spawn_context sc = { 0 };
|
struct spawn_context sc = { 0 };
|
||||||
struct client *tc = cmdq_get_target_client(item);
|
struct client *tc = cmdq_get_target_client(item);
|
||||||
|
struct client *ac;
|
||||||
struct session *s = target->s;
|
struct session *s = target->s;
|
||||||
struct winlink *wl = target->wl, *new_wl = NULL;
|
struct winlink *wl = target->wl, *new_wl = NULL;
|
||||||
int idx = target->idx, before, count = args_count(args);
|
int idx = target->idx, before, count = args_count(args);
|
||||||
|
int detached, local;
|
||||||
char *cause = NULL, *cp, *expanded, *wname = NULL;
|
char *cause = NULL, *cp, *expanded, *wname = NULL;
|
||||||
const char *template, *name;
|
const char *template, *name;
|
||||||
struct cmd_find_state fs;
|
struct cmd_find_state fs;
|
||||||
@@ -107,10 +109,15 @@ cmd_new_window_exec(struct cmd *self, struct cmdq_item *item)
|
|||||||
free(wname);
|
free(wname);
|
||||||
if (args_has(args, 'd'))
|
if (args_has(args, 'd'))
|
||||||
return (CMD_RETURN_NORMAL);
|
return (CMD_RETURN_NORMAL);
|
||||||
if (session_set_current(s, new_wl) == 0)
|
if (active_select_window(c, s, new_wl) == 0) {
|
||||||
|
if (active_is_local_window(c, s) && c != NULL &&
|
||||||
|
c->session == s)
|
||||||
|
server_redraw_client(c);
|
||||||
|
else
|
||||||
server_redraw_session(s);
|
server_redraw_session(s);
|
||||||
|
}
|
||||||
if (c != NULL && c->session != NULL)
|
if (c != NULL && c->session != NULL)
|
||||||
s->curw->window->latest = c;
|
active_get_effective_window(c, s)->latest = c;
|
||||||
recalculate_sizes();
|
recalculate_sizes();
|
||||||
return (CMD_RETURN_NORMAL);
|
return (CMD_RETURN_NORMAL);
|
||||||
}
|
}
|
||||||
@@ -140,10 +147,16 @@ cmd_new_window_exec(struct cmd *self, struct cmdq_item *item)
|
|||||||
sc.idx = idx;
|
sc.idx = idx;
|
||||||
sc.cwd = args_get(args, 'c');
|
sc.cwd = args_get(args, 'c');
|
||||||
|
|
||||||
|
ac = tc;
|
||||||
|
if (ac == NULL || ac->session != s)
|
||||||
|
ac = c;
|
||||||
|
detached = args_has(args, 'd');
|
||||||
|
local = (!detached && active_is_local_window(ac, s));
|
||||||
|
|
||||||
sc.flags = 0;
|
sc.flags = 0;
|
||||||
if (args_has(args, 'E') || (count == 1 && *args_string(args, 0) == '\0'))
|
if (args_has(args, 'E') || (count == 1 && *args_string(args, 0) == '\0'))
|
||||||
sc.flags |= SPAWN_EMPTY;
|
sc.flags |= SPAWN_EMPTY;
|
||||||
if (args_has(args, 'd'))
|
if (detached || local)
|
||||||
sc.flags |= SPAWN_DETACHED;
|
sc.flags |= SPAWN_DETACHED;
|
||||||
if (args_has(args, 'k'))
|
if (args_has(args, 'k'))
|
||||||
sc.flags |= SPAWN_KILL;
|
sc.flags |= SPAWN_KILL;
|
||||||
@@ -153,7 +166,13 @@ cmd_new_window_exec(struct cmd *self, struct cmdq_item *item)
|
|||||||
free(cause);
|
free(cause);
|
||||||
goto fail;
|
goto fail;
|
||||||
}
|
}
|
||||||
if (!args_has(args, 'd') || new_wl == s->curw) {
|
if (local) {
|
||||||
|
if (active_select_window(ac, s, new_wl) == 0)
|
||||||
|
cmd_find_from_winlink(current, new_wl, 0);
|
||||||
|
if (ac != NULL && ac->session == s)
|
||||||
|
server_redraw_client(ac);
|
||||||
|
server_status_session_group(s);
|
||||||
|
} else if (!detached || new_wl == s->curw) {
|
||||||
cmd_find_from_winlink(current, new_wl, 0);
|
cmd_find_from_winlink(current, new_wl, 0);
|
||||||
server_redraw_session_group(s);
|
server_redraw_session_group(s);
|
||||||
} else
|
} else
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
/* $OpenBSD: cmd-parse.y,v 1.58 2026/04/27 12:31:11 nicm Exp $ */
|
/* $OpenBSD: cmd-parse.y,v 1.59 2026/08/31 07:51:56 nicm Exp $ */
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2019 Nicholas Marriott <nicholas.marriott@gmail.com>
|
* Copyright (c) 2019 Nicholas Marriott <nicholas.marriott@gmail.com>
|
||||||
@@ -802,6 +802,7 @@ cmd_parse_expand_alias(struct cmd_parse_command *cmd,
|
|||||||
if (last == NULL) {
|
if (last == NULL) {
|
||||||
pr->status = CMD_PARSE_SUCCESS;
|
pr->status = CMD_PARSE_SUCCESS;
|
||||||
pr->cmdlist = cmd_list_new();
|
pr->cmdlist = cmd_list_new();
|
||||||
|
cmd_parse_free_commands(cmds);
|
||||||
return (1);
|
return (1);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -814,6 +815,7 @@ cmd_parse_expand_alias(struct cmd_parse_command *cmd,
|
|||||||
pi->flags |= CMD_PARSE_NOALIAS;
|
pi->flags |= CMD_PARSE_NOALIAS;
|
||||||
cmd_parse_build_commands(cmds, pi, pr);
|
cmd_parse_build_commands(cmds, pi, pr);
|
||||||
pi->flags &= ~CMD_PARSE_NOALIAS;
|
pi->flags &= ~CMD_PARSE_NOALIAS;
|
||||||
|
cmd_parse_free_commands(cmds);
|
||||||
return (1);
|
return (1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
15
cmd-queue.c
15
cmd-queue.c
@@ -1,4 +1,4 @@
|
|||||||
/* $OpenBSD: cmd-queue.c,v 1.121 2026/07/14 17:17:17 nicm Exp $ */
|
/* $OpenBSD: cmd-queue.c,v 1.123 2026/08/24 20:34:26 nicm Exp $ */
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2013 Nicholas Marriott <nicholas.marriott@gmail.com>
|
* Copyright (c) 2013 Nicholas Marriott <nicholas.marriott@gmail.com>
|
||||||
@@ -802,7 +802,7 @@ cmdq_guard(struct cmdq_item *item, const char *guard, int flags)
|
|||||||
u_int number = item->number;
|
u_int number = item->number;
|
||||||
|
|
||||||
if (c != NULL && (c->flags & CLIENT_CONTROL))
|
if (c != NULL && (c->flags & CLIENT_CONTROL))
|
||||||
control_write(c, "%%%s %ld %u %d", guard, t, number, flags);
|
control_write_guard(c, guard, t, number, flags);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Show message from command. */
|
/* Show message from command. */
|
||||||
@@ -850,7 +850,18 @@ cmdq_error(struct cmdq_item *item, const char *fmt, ...)
|
|||||||
|
|
||||||
if (c == NULL) {
|
if (c == NULL) {
|
||||||
cmd_get_source(cmd, &file, &line);
|
cmd_get_source(cmd, &file, &line);
|
||||||
|
if (!cfg_finished) {
|
||||||
|
if (file != NULL)
|
||||||
cfg_add_cause("%s:%u: %s", file, line, msg);
|
cfg_add_cause("%s:%u: %s", file, line, msg);
|
||||||
|
else
|
||||||
|
cfg_add_cause("%s", msg);
|
||||||
|
} else {
|
||||||
|
if (file != NULL) {
|
||||||
|
server_add_message("message: %s:%u: %s", file,
|
||||||
|
line, msg);
|
||||||
|
} else
|
||||||
|
server_add_message("message: %s", msg);
|
||||||
|
}
|
||||||
} else if (c->session == NULL || (c->flags & CLIENT_CONTROL)) {
|
} else if (c->session == NULL || (c->flags & CLIENT_CONTROL)) {
|
||||||
server_add_message("%s message: %s", c->name, msg);
|
server_add_message("%s message: %s", c->name, msg);
|
||||||
if (~c->flags & CLIENT_UTF8) {
|
if (~c->flags & CLIENT_UTF8) {
|
||||||
|
|||||||
@@ -203,7 +203,7 @@ cmd_refresh_client_exec(struct cmd *self, struct cmdq_item *item)
|
|||||||
if (args_has(args, 'c'))
|
if (args_has(args, 'c'))
|
||||||
tc->pan_window = NULL;
|
tc->pan_window = NULL;
|
||||||
else {
|
else {
|
||||||
w = tc->session->curw->window;
|
w = active_get_effective_window(tc, tc->session);
|
||||||
if (tc->pan_window != w) {
|
if (tc->pan_window != w) {
|
||||||
tc->pan_window = w;
|
tc->pan_window = w;
|
||||||
tc->pan_ox = tty->oox;
|
tc->pan_ox = tty->oox;
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
/* $OpenBSD: cmd-resize-pane.c,v 1.67 2026/07/10 13:38:45 nicm Exp $ */
|
/* $OpenBSD: cmd-resize-pane.c,v 1.68 2026/08/31 07:44:39 nicm Exp $ */
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2009 Nicholas Marriott <nicholas.marriott@gmail.com>
|
* Copyright (c) 2009 Nicholas Marriott <nicholas.marriott@gmail.com>
|
||||||
@@ -92,6 +92,7 @@ cmd_resize_pane_exec(struct cmd *self, struct cmdq_item *item)
|
|||||||
return (CMD_RETURN_NORMAL);
|
return (CMD_RETURN_NORMAL);
|
||||||
}
|
}
|
||||||
server_unzoom_window(w);
|
server_unzoom_window(w);
|
||||||
|
lc = wp->layout_cell; /* may have been replaced by unzoom */
|
||||||
|
|
||||||
if (args_has(args, 'x')) {
|
if (args_has(args, 'x')) {
|
||||||
x = args_percentage(args, 'x', 0, PANE_MAXIMUM, w->sx, &cause);
|
x = args_percentage(args, 'x', 0, PANE_MAXIMUM, w->sx, &cause);
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
/* $OpenBSD: cmd-run-shell.c,v 1.93 2026/07/17 12:42:51 nicm Exp $ */
|
/* $OpenBSD: cmd-run-shell.c,v 1.94 2026/08/25 06:04:33 nicm Exp $ */
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2009 Tiago Cunha <me@tiagocunha.org>
|
* Copyright (c) 2009 Tiago Cunha <me@tiagocunha.org>
|
||||||
@@ -79,7 +79,10 @@ static void
|
|||||||
cmd_run_shell_print(struct job *job, const char *msg)
|
cmd_run_shell_print(struct job *job, const char *msg)
|
||||||
{
|
{
|
||||||
struct cmd_run_shell_data *cdata = job_get_data(job);
|
struct cmd_run_shell_data *cdata = job_get_data(job);
|
||||||
|
struct client *c = cdata->client;
|
||||||
|
struct session *s;
|
||||||
struct window_pane *wp = NULL;
|
struct window_pane *wp = NULL;
|
||||||
|
struct window *w;
|
||||||
struct cmd_find_state fs;
|
struct cmd_find_state fs;
|
||||||
struct window_mode_entry *wme;
|
struct window_mode_entry *wme;
|
||||||
|
|
||||||
@@ -90,8 +93,11 @@ cmd_run_shell_print(struct job *job, const char *msg)
|
|||||||
cmdq_print(cdata->item, "%s", msg);
|
cmdq_print(cdata->item, "%s", msg);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (cdata->client != NULL && cdata->client->session != NULL)
|
if (c != NULL && c->session != NULL) {
|
||||||
wp = cdata->client->session->curw->window->active;
|
s = c->session;
|
||||||
|
w = active_get_effective_window(c, s);
|
||||||
|
wp = active_get_effective_pane(c, w);
|
||||||
|
}
|
||||||
if (wp == NULL && cmd_find_from_nothing(&fs, 0) == 0)
|
if (wp == NULL && cmd_find_from_nothing(&fs, 0) == 0)
|
||||||
wp = fs.wp;
|
wp = fs.wp;
|
||||||
if (wp == NULL)
|
if (wp == NULL)
|
||||||
@@ -233,9 +239,11 @@ cmd_run_shell_timer(__unused int fd, __unused short events, void* arg)
|
|||||||
} else if (item == NULL) {
|
} else if (item == NULL) {
|
||||||
new_item = cmdq_get_command(cmdlist, NULL);
|
new_item = cmdq_get_command(cmdlist, NULL);
|
||||||
cmdq_append(c, new_item);
|
cmdq_append(c, new_item);
|
||||||
|
cmd_list_free(cmdlist);
|
||||||
} else {
|
} else {
|
||||||
new_item = cmdq_get_command(cmdlist, cmdq_get_state(item));
|
new_item = cmdq_get_command(cmdlist, cmdq_get_state(item));
|
||||||
cmdq_insert_after(item, new_item);
|
cmdq_insert_after(item, new_item);
|
||||||
|
cmd_list_free(cmdlist);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (cdata->item != NULL)
|
if (cdata->item != NULL)
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
/* $OpenBSD: cmd-select-pane.c,v 1.77 2026/07/17 12:42:51 nicm Exp $ */
|
/* $OpenBSD: cmd-select-pane.c,v 1.78 2026/08/20 09:19:24 nicm Exp $ */
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2009 Nicholas Marriott <nicholas.marriott@gmail.com>
|
* Copyright (c) 2009 Nicholas Marriott <nicholas.marriott@gmail.com>
|
||||||
@@ -33,8 +33,8 @@ const struct cmd_entry cmd_select_pane_entry = {
|
|||||||
.name = "select-pane",
|
.name = "select-pane",
|
||||||
.alias = "selectp",
|
.alias = "selectp",
|
||||||
|
|
||||||
.args = { "DdegLlMmP:RT:t:UZ", 0, 0, NULL }, /* -P and -g deprecated */
|
.args = { "DdegLlMmP:RsST:t:UZ", 0, 0, NULL }, /* -P and -g deprecated */
|
||||||
.usage = "[-DdeLlMmRUZ] [-T title] " CMD_TARGET_PANE_USAGE,
|
.usage = "[-DdeLlMmRsSUZ] [-T title] " CMD_TARGET_PANE_USAGE,
|
||||||
|
|
||||||
.target = { 't', CMD_FIND_PANE, 0 },
|
.target = { 't', CMD_FIND_PANE, 0 },
|
||||||
|
|
||||||
@@ -68,10 +68,11 @@ cmd_select_pane_redraw(struct window *w)
|
|||||||
TAILQ_FOREACH(c, &clients, entry) {
|
TAILQ_FOREACH(c, &clients, entry) {
|
||||||
if (c->session == NULL || (c->flags & CLIENT_CONTROL))
|
if (c->session == NULL || (c->flags & CLIENT_CONTROL))
|
||||||
continue;
|
continue;
|
||||||
if (c->session->curw->window == w && tty_window_bigger(&c->tty))
|
if (active_get_effective_window(c, c->session) == w &&
|
||||||
|
tty_window_bigger(&c->tty))
|
||||||
server_redraw_client(c);
|
server_redraw_client(c);
|
||||||
else {
|
else {
|
||||||
if (c->session->curw->window == w)
|
if (active_get_effective_window(c, c->session) == w)
|
||||||
c->flags |= CLIENT_REDRAWBORDERS;
|
c->flags |= CLIENT_REDRAWBORDERS;
|
||||||
if (session_has(c->session, w))
|
if (session_has(c->session, w))
|
||||||
c->flags |= CLIENT_REDRAWSTATUS;
|
c->flags |= CLIENT_REDRAWSTATUS;
|
||||||
@@ -84,6 +85,7 @@ static enum cmd_retval
|
|||||||
cmd_select_pane_marked_pane(struct cmd *self, struct cmdq_item *item)
|
cmd_select_pane_marked_pane(struct cmd *self, struct cmdq_item *item)
|
||||||
{
|
{
|
||||||
struct args *args = cmd_get_args(self);
|
struct args *args = cmd_get_args(self);
|
||||||
|
struct client *c = cmdq_get_client(item);
|
||||||
struct cmd_find_state *target = cmdq_get_target(item);
|
struct cmd_find_state *target = cmdq_get_target(item);
|
||||||
struct event_payload *ep;
|
struct event_payload *ep;
|
||||||
struct cmd_find_state fs;
|
struct cmd_find_state fs;
|
||||||
@@ -137,8 +139,9 @@ cmd_select_pane_marked_pane(struct cmd *self, struct cmdq_item *item)
|
|||||||
server_status_window(mwp->window);
|
server_status_window(mwp->window);
|
||||||
}
|
}
|
||||||
if (window_pane_is_floating(wp)) {
|
if (window_pane_is_floating(wp)) {
|
||||||
|
if (!active_has_local_pane(c, wp->window))
|
||||||
window_redraw_active_switch(wp->window, wp);
|
window_redraw_active_switch(wp->window, wp);
|
||||||
window_set_active_pane(wp->window, wp, 1);
|
active_set_pane(c, wp->window, wp, 1);
|
||||||
}
|
}
|
||||||
return (CMD_RETURN_NORMAL);
|
return (CMD_RETURN_NORMAL);
|
||||||
}
|
}
|
||||||
@@ -152,25 +155,40 @@ cmd_select_pane_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 event_payload *ep;
|
struct event_payload *ep;
|
||||||
struct cmd_find_state fs;
|
struct cmd_find_state fs;
|
||||||
|
struct client *c = cmdq_get_client(item);
|
||||||
struct winlink *wl = target->wl;
|
struct winlink *wl = target->wl;
|
||||||
struct window *w = wl->window;
|
struct window *w = wl->window;
|
||||||
struct session *s = target->s;
|
struct session *s = target->s;
|
||||||
struct window_pane *wp = target->wp, *lastwp;
|
struct window_pane *wp = target->wp, *lastwp, *activewp;
|
||||||
struct options *oo = wp->options;
|
struct options *oo = wp->options;
|
||||||
char *title;
|
char *title;
|
||||||
const char *style;
|
const char *style;
|
||||||
struct options_entry *o;
|
struct options_entry *o;
|
||||||
|
int visible, Zflag = args_has(args, 'Z');
|
||||||
|
|
||||||
|
if (args_has(args, 's') || args_has(args, 'S')) {
|
||||||
|
if (c == NULL || c->session == NULL) {
|
||||||
|
cmdq_error(item, "no current client");
|
||||||
|
return (CMD_RETURN_ERROR);
|
||||||
|
}
|
||||||
|
if (args_has(args, 's'))
|
||||||
|
active_set_pane_mode(c, w, ACTIVE_LOCAL);
|
||||||
|
else
|
||||||
|
active_set_pane_mode(c, w, ACTIVE_SHARED);
|
||||||
|
return (CMD_RETURN_NORMAL);
|
||||||
|
}
|
||||||
|
|
||||||
if (entry == &cmd_last_pane_entry || args_has(args, 'l')) {
|
if (entry == &cmd_last_pane_entry || args_has(args, 'l')) {
|
||||||
/*
|
/*
|
||||||
* Check for no last pane found in case the other pane was
|
* Check for no last pane found in case the other pane was
|
||||||
* spawned without being visited (for example split-window -d).
|
* spawned without being visited (for example split-window -d).
|
||||||
*/
|
*/
|
||||||
lastwp = TAILQ_FIRST(&w->last_panes);
|
lastwp = active_get_last_pane(c, w);
|
||||||
if (lastwp == NULL && window_count_panes(w, 1) == 2) {
|
if (lastwp == NULL && window_count_panes(w, 1) == 2) {
|
||||||
lastwp = TAILQ_PREV(w->active, window_panes, entry);
|
activewp = active_get_effective_pane(c, w);
|
||||||
|
lastwp = TAILQ_PREV(activewp, window_panes, entry);
|
||||||
if (lastwp == NULL)
|
if (lastwp == NULL)
|
||||||
lastwp = TAILQ_NEXT(w->active, entry);
|
lastwp = TAILQ_NEXT(activewp, entry);
|
||||||
}
|
}
|
||||||
if (lastwp == NULL) {
|
if (lastwp == NULL) {
|
||||||
cmdq_error(item, "no last pane");
|
cmdq_error(item, "no last pane");
|
||||||
@@ -185,14 +203,19 @@ cmd_select_pane_exec(struct cmd *self, struct cmdq_item *item)
|
|||||||
server_redraw_window_borders(lastwp->window);
|
server_redraw_window_borders(lastwp->window);
|
||||||
server_status_window(lastwp->window);
|
server_status_window(lastwp->window);
|
||||||
} else {
|
} else {
|
||||||
if (window_push_zoom(w, 0, args_has(args, 'Z')))
|
if (w->modal != NULL && lastwp != w->modal)
|
||||||
|
visible = 1;
|
||||||
|
else
|
||||||
|
visible = window_pane_is_visible(lastwp);
|
||||||
|
if (!visible && window_push_zoom(w, 0, Zflag))
|
||||||
server_redraw_window(w);
|
server_redraw_window(w);
|
||||||
|
if (!active_has_local_pane(c, w))
|
||||||
window_redraw_active_switch(w, lastwp);
|
window_redraw_active_switch(w, lastwp);
|
||||||
if (window_set_active_pane(w, lastwp, 1)) {
|
if (active_set_pane(c, w, lastwp, 1)) {
|
||||||
cmd_find_from_winlink(current, wl, 0);
|
cmd_find_from_winlink(current, wl, 0);
|
||||||
cmd_select_pane_redraw(w);
|
cmd_select_pane_redraw(w);
|
||||||
}
|
}
|
||||||
if (window_pop_zoom(w))
|
if (!visible && window_pop_zoom(w))
|
||||||
server_redraw_window(w);
|
server_redraw_window(w);
|
||||||
}
|
}
|
||||||
return (CMD_RETURN_NORMAL);
|
return (CMD_RETURN_NORMAL);
|
||||||
@@ -266,16 +289,22 @@ cmd_select_pane_exec(struct cmd *self, struct cmdq_item *item)
|
|||||||
return (CMD_RETURN_NORMAL);
|
return (CMD_RETURN_NORMAL);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (wp == w->active)
|
activewp = active_get_effective_pane(c, w);
|
||||||
|
if (wp == activewp)
|
||||||
return (CMD_RETURN_NORMAL);
|
return (CMD_RETURN_NORMAL);
|
||||||
if (window_push_zoom(w, 0, args_has(args, 'Z')))
|
if (w->modal != NULL && wp != w->modal)
|
||||||
|
visible = 1;
|
||||||
|
else
|
||||||
|
visible = window_pane_is_visible(wp);
|
||||||
|
if (!visible && window_push_zoom(w, 0, Zflag))
|
||||||
server_redraw_window(w);
|
server_redraw_window(w);
|
||||||
|
if (!active_has_local_pane(c, w))
|
||||||
window_redraw_active_switch(w, wp);
|
window_redraw_active_switch(w, wp);
|
||||||
if (window_set_active_pane(w, wp, 1))
|
if (active_set_pane(c, w, wp, 1))
|
||||||
cmd_find_from_winlink_pane(current, wl, wp, 0);
|
cmd_find_from_winlink_pane(current, wl, wp, 0);
|
||||||
cmdq_insert_hook(s, item, current, "after-select-pane");
|
cmdq_insert_hook(s, item, current, "after-select-pane");
|
||||||
cmd_select_pane_redraw(w);
|
cmd_select_pane_redraw(w);
|
||||||
if (window_pop_zoom(w))
|
if (!visible && window_pop_zoom(w))
|
||||||
server_redraw_window(w);
|
server_redraw_window(w);
|
||||||
|
|
||||||
return (CMD_RETURN_NORMAL);
|
return (CMD_RETURN_NORMAL);
|
||||||
|
|||||||
@@ -33,8 +33,8 @@ const struct cmd_entry cmd_select_window_entry = {
|
|||||||
.name = "select-window",
|
.name = "select-window",
|
||||||
.alias = "selectw",
|
.alias = "selectw",
|
||||||
|
|
||||||
.args = { "lnpTt:", 0, 0, NULL },
|
.args = { "LlSnpTt:", 0, 0, NULL },
|
||||||
.usage = "[-lnpT] " CMD_TARGET_WINDOW_USAGE,
|
.usage = "[-LlnpST] " CMD_TARGET_WINDOW_USAGE,
|
||||||
|
|
||||||
.target = { 't', CMD_FIND_WINDOW, 0 },
|
.target = { 't', CMD_FIND_WINDOW, 0 },
|
||||||
|
|
||||||
@@ -88,8 +88,9 @@ cmd_select_window_exec(struct cmd *self, struct cmdq_item *item)
|
|||||||
struct client *c = cmdq_get_client(item);
|
struct client *c = cmdq_get_client(item);
|
||||||
struct cmd_find_state *current = cmdq_get_current(item);
|
struct cmd_find_state *current = cmdq_get_current(item);
|
||||||
struct cmd_find_state *target = cmdq_get_target(item);
|
struct cmd_find_state *target = cmdq_get_target(item);
|
||||||
struct winlink *wl = target->wl;
|
struct winlink *wl = target->wl, *current_wl;
|
||||||
struct session *s = target->s;
|
struct session *s = target->s;
|
||||||
|
enum active_mode mode;
|
||||||
int next, previous, last, activity;
|
int next, previous, last, activity;
|
||||||
|
|
||||||
next = (cmd_get_entry(self) == &cmd_next_window_entry);
|
next = (cmd_get_entry(self) == &cmd_next_window_entry);
|
||||||
@@ -102,25 +103,66 @@ cmd_select_window_exec(struct cmd *self, struct cmdq_item *item)
|
|||||||
if (args_has(args, 'l'))
|
if (args_has(args, 'l'))
|
||||||
last = 1;
|
last = 1;
|
||||||
|
|
||||||
|
if (args_has(args, 'L') || args_has(args, 'S')) {
|
||||||
|
if (c == NULL || c->session == NULL) {
|
||||||
|
cmdq_error(item, "no current client");
|
||||||
|
return (CMD_RETURN_ERROR);
|
||||||
|
}
|
||||||
|
if (args_has(args, 'S'))
|
||||||
|
mode = ACTIVE_SHARED;
|
||||||
|
else
|
||||||
|
mode = ACTIVE_LOCAL;
|
||||||
|
active_set_local_window(c, s, mode);
|
||||||
|
if (!next &&
|
||||||
|
!previous &&
|
||||||
|
!last &&
|
||||||
|
!args_has(args, 't') &&
|
||||||
|
!args_has(args, 'T')) {
|
||||||
|
if (current->s == s) {
|
||||||
|
if (c->session == s)
|
||||||
|
cmd_find_from_client(current, c, 0);
|
||||||
|
else
|
||||||
|
cmd_find_from_session(current, s, 0);
|
||||||
|
}
|
||||||
|
if (c->session == s)
|
||||||
|
server_redraw_client(c);
|
||||||
|
else
|
||||||
|
server_redraw_session(s);
|
||||||
|
cmdq_insert_hook(s, item, current, "after-select-window");
|
||||||
|
recalculate_sizes();
|
||||||
|
return (CMD_RETURN_NORMAL);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (active_is_local_window(c, s))
|
||||||
|
mode = ACTIVE_LOCAL;
|
||||||
|
else
|
||||||
|
mode = ACTIVE_SHARED;
|
||||||
if (next || previous || last) {
|
if (next || previous || last) {
|
||||||
activity = args_has(args, 'a');
|
activity = args_has(args, 'a');
|
||||||
if (next) {
|
if (next) {
|
||||||
if (session_next(s, activity) != 0) {
|
if (active_next_window(c, s, activity) != 0) {
|
||||||
cmdq_error(item, "no next window");
|
cmdq_error(item, "no next window");
|
||||||
return (CMD_RETURN_ERROR);
|
return (CMD_RETURN_ERROR);
|
||||||
}
|
}
|
||||||
} else if (previous) {
|
} else if (previous) {
|
||||||
if (session_previous(s, activity) != 0) {
|
if (active_previous_window(c, s, activity) != 0) {
|
||||||
cmdq_error(item, "no previous window");
|
cmdq_error(item, "no previous window");
|
||||||
return (CMD_RETURN_ERROR);
|
return (CMD_RETURN_ERROR);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (session_last(s) != 0) {
|
if (active_last_window(c, s) != 0) {
|
||||||
cmdq_error(item, "no last window");
|
cmdq_error(item, "no last window");
|
||||||
return (CMD_RETURN_ERROR);
|
return (CMD_RETURN_ERROR);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (mode == ACTIVE_LOCAL && c != NULL && c->session == s)
|
||||||
|
cmd_find_from_client(current, c, 0);
|
||||||
|
else
|
||||||
cmd_find_from_session(current, s, 0);
|
cmd_find_from_session(current, s, 0);
|
||||||
|
if (mode == ACTIVE_LOCAL && c != NULL && c->session == s)
|
||||||
|
server_redraw_client(c);
|
||||||
|
else
|
||||||
server_redraw_session(s);
|
server_redraw_session(s);
|
||||||
cmdq_insert_hook(s, item, current, "after-select-window");
|
cmdq_insert_hook(s, item, current, "after-select-window");
|
||||||
} else {
|
} else {
|
||||||
@@ -128,22 +170,37 @@ cmd_select_window_exec(struct cmd *self, struct cmdq_item *item)
|
|||||||
* If -T and select-window is invoked on same window as
|
* If -T and select-window is invoked on same window as
|
||||||
* current, switch to previous window.
|
* current, switch to previous window.
|
||||||
*/
|
*/
|
||||||
if (args_has(args, 'T') && wl == s->curw) {
|
current_wl = active_get_effective_winlink(c, s);
|
||||||
if (session_last(s) != 0) {
|
if (args_has(args, 'T') && wl == current_wl) {
|
||||||
|
if (active_last_window(c, s) != 0) {
|
||||||
cmdq_error(item, "no last window");
|
cmdq_error(item, "no last window");
|
||||||
return (-1);
|
return (-1);
|
||||||
}
|
}
|
||||||
if (current->s == s)
|
if (current->s == s) {
|
||||||
|
if (mode == ACTIVE_LOCAL && c != NULL &&
|
||||||
|
c->session == s)
|
||||||
|
cmd_find_from_client(current, c, 0);
|
||||||
|
else
|
||||||
cmd_find_from_session(current, s, 0);
|
cmd_find_from_session(current, s, 0);
|
||||||
|
}
|
||||||
|
if (mode == ACTIVE_LOCAL && c != NULL && c->session == s)
|
||||||
|
server_redraw_client(c);
|
||||||
|
else
|
||||||
server_redraw_session(s);
|
server_redraw_session(s);
|
||||||
} else if (session_select(s, wl->idx) == 0) {
|
} else if (active_select_window(c, s, wl) == 0) {
|
||||||
|
if (mode == ACTIVE_LOCAL && c != NULL && c->session == s)
|
||||||
|
cmd_find_from_client(current, c, 0);
|
||||||
|
else
|
||||||
cmd_find_from_session(current, s, 0);
|
cmd_find_from_session(current, s, 0);
|
||||||
|
if (mode == ACTIVE_LOCAL && c != NULL && c->session == s)
|
||||||
|
server_redraw_client(c);
|
||||||
|
else
|
||||||
server_redraw_session(s);
|
server_redraw_session(s);
|
||||||
}
|
}
|
||||||
cmdq_insert_hook(s, item, current, "after-select-window");
|
cmdq_insert_hook(s, item, current, "after-select-window");
|
||||||
}
|
}
|
||||||
if (c != NULL && c->session != NULL)
|
if (c != NULL && c->session != NULL)
|
||||||
s->curw->window->latest = c;
|
active_get_effective_window(c, s)->latest = c;
|
||||||
recalculate_sizes();
|
recalculate_sizes();
|
||||||
|
|
||||||
return (CMD_RETURN_NORMAL);
|
return (CMD_RETURN_NORMAL);
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
/* $OpenBSD: cmd-set-buffer.c,v 1.37 2026/02/15 17:43:26 nicm Exp $ */
|
/* $OpenBSD: cmd-set-buffer.c,v 1.38 2026/08/24 07:05:23 nicm Exp $ */
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>
|
* Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>
|
||||||
@@ -100,6 +100,7 @@ cmd_set_buffer_exec(struct cmd *self, struct cmdq_item *item)
|
|||||||
cmdq_error(item, "%s", cause);
|
cmdq_error(item, "%s", cause);
|
||||||
goto fail;
|
goto fail;
|
||||||
}
|
}
|
||||||
|
free(bufname);
|
||||||
return (CMD_RETURN_NORMAL);
|
return (CMD_RETURN_NORMAL);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -107,8 +108,10 @@ cmd_set_buffer_exec(struct cmd *self, struct cmdq_item *item)
|
|||||||
cmdq_error(item, "no data specified");
|
cmdq_error(item, "no data specified");
|
||||||
goto fail;
|
goto fail;
|
||||||
}
|
}
|
||||||
if ((newsize = strlen(args_string(args, 0))) == 0)
|
if ((newsize = strlen(args_string(args, 0))) == 0) {
|
||||||
|
free(bufname);
|
||||||
return (CMD_RETURN_NORMAL);
|
return (CMD_RETURN_NORMAL);
|
||||||
|
}
|
||||||
|
|
||||||
if (args_has(args, 'a') && pb != NULL) {
|
if (args_has(args, 'a') && pb != NULL) {
|
||||||
olddata = paste_buffer_data(pb, &bufsize);
|
olddata = paste_buffer_data(pb, &bufsize);
|
||||||
@@ -127,6 +130,7 @@ cmd_set_buffer_exec(struct cmd *self, struct cmdq_item *item)
|
|||||||
if (args_has(args, 'w') && tc != NULL)
|
if (args_has(args, 'w') && tc != NULL)
|
||||||
tty_set_selection(&tc->tty, "", bufdata, bufsize);
|
tty_set_selection(&tc->tty, "", bufdata, bufsize);
|
||||||
|
|
||||||
|
free(bufname);
|
||||||
return (CMD_RETURN_NORMAL);
|
return (CMD_RETURN_NORMAL);
|
||||||
|
|
||||||
fail:
|
fail:
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
/* $OpenBSD: cmd-source-file.c,v 1.62 2025/11/18 08:42:09 nicm Exp $ */
|
/* $OpenBSD: cmd-source-file.c,v 1.63 2026/08/18 08:05:05 nicm Exp $ */
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2008 Tiago Cunha <me@tiagocunha.org>
|
* Copyright (c) 2008 Tiago Cunha <me@tiagocunha.org>
|
||||||
@@ -50,6 +50,7 @@ const struct cmd_entry cmd_source_file_entry = {
|
|||||||
|
|
||||||
struct cmd_source_file_data {
|
struct cmd_source_file_data {
|
||||||
struct cmdq_item *item;
|
struct cmdq_item *item;
|
||||||
|
struct client *client;
|
||||||
int flags;
|
int flags;
|
||||||
|
|
||||||
struct cmdq_item *after;
|
struct cmdq_item *after;
|
||||||
@@ -60,10 +61,24 @@ struct cmd_source_file_data {
|
|||||||
u_int nfiles;
|
u_int nfiles;
|
||||||
};
|
};
|
||||||
|
|
||||||
static enum cmd_retval
|
static void
|
||||||
cmd_source_file_complete_cb(struct cmdq_item *item, __unused void *data)
|
cmd_source_file_free_data(struct cmd_source_file_data *cdata)
|
||||||
{
|
{
|
||||||
struct client *c = cmdq_get_client(item);
|
u_int i;
|
||||||
|
|
||||||
|
for (i = 0; i < cdata->nfiles; i++)
|
||||||
|
free(cdata->files[i]);
|
||||||
|
free(cdata->files);
|
||||||
|
if (cdata->client != NULL)
|
||||||
|
server_client_unref(cdata->client);
|
||||||
|
free(cdata);
|
||||||
|
}
|
||||||
|
|
||||||
|
static enum cmd_retval
|
||||||
|
cmd_source_file_complete_cb(struct cmdq_item *item, void *data)
|
||||||
|
{
|
||||||
|
struct cmd_source_file_data *cdata = data;
|
||||||
|
struct client *c = cdata->client;
|
||||||
|
|
||||||
if (c == NULL) {
|
if (c == NULL) {
|
||||||
cmd_source_file_depth--;
|
cmd_source_file_depth--;
|
||||||
@@ -74,36 +89,36 @@ cmd_source_file_complete_cb(struct cmdq_item *item, __unused void *data)
|
|||||||
}
|
}
|
||||||
|
|
||||||
cfg_print_causes(item);
|
cfg_print_causes(item);
|
||||||
|
cmd_source_file_free_data(cdata);
|
||||||
return (CMD_RETURN_NORMAL);
|
return (CMD_RETURN_NORMAL);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
cmd_source_file_complete(struct client *c, struct cmd_source_file_data *cdata)
|
cmd_source_file_complete(struct cmd_source_file_data *cdata)
|
||||||
{
|
{
|
||||||
|
struct client *c = cdata->client;
|
||||||
struct cmdq_item *new_item;
|
struct cmdq_item *new_item;
|
||||||
u_int i;
|
|
||||||
|
|
||||||
if (cfg_finished) {
|
if (!cfg_finished) {
|
||||||
|
cmd_source_file_free_data(cdata);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (cdata->retval == CMD_RETURN_ERROR &&
|
if (cdata->retval == CMD_RETURN_ERROR &&
|
||||||
c != NULL &&
|
c != NULL &&
|
||||||
c->session == NULL)
|
c->session == NULL)
|
||||||
c->retval = 1;
|
c->retval = 1;
|
||||||
new_item = cmdq_get_callback(cmd_source_file_complete_cb, NULL);
|
new_item = cmdq_get_callback(cmd_source_file_complete_cb, cdata);
|
||||||
cmdq_insert_after(cdata->after, new_item);
|
cmdq_insert_after(cdata->after, new_item);
|
||||||
}
|
|
||||||
|
|
||||||
for (i = 0; i < cdata->nfiles; i++)
|
|
||||||
free(cdata->files[i]);
|
|
||||||
free(cdata->files);
|
|
||||||
free(cdata);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
cmd_source_file_done(struct client *c, const char *path, int error,
|
cmd_source_file_done(__unused struct client *oc, const char *path,
|
||||||
int closed, struct evbuffer *buffer, void *data)
|
int error, int closed, struct evbuffer *buffer, void *data)
|
||||||
{
|
{
|
||||||
struct cmd_source_file_data *cdata = data;
|
struct cmd_source_file_data *cdata = data;
|
||||||
struct cmdq_item *item = cdata->item;
|
struct cmdq_item *item = cdata->item;
|
||||||
|
struct client *c = cdata->client;
|
||||||
void *bdata = EVBUFFER_DATA(buffer);
|
void *bdata = EVBUFFER_DATA(buffer);
|
||||||
size_t bsize = EVBUFFER_LENGTH(buffer);
|
size_t bsize = EVBUFFER_LENGTH(buffer);
|
||||||
u_int n;
|
u_int n;
|
||||||
@@ -127,7 +142,7 @@ cmd_source_file_done(struct client *c, const char *path, int error,
|
|||||||
if (n < cdata->nfiles)
|
if (n < cdata->nfiles)
|
||||||
file_read(c, cdata->files[n], cmd_source_file_done, cdata);
|
file_read(c, cdata->files[n], cmd_source_file_done, cdata);
|
||||||
else {
|
else {
|
||||||
cmd_source_file_complete(c, cdata);
|
cmd_source_file_complete(cdata);
|
||||||
cmdq_continue(item);
|
cmdq_continue(item);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -187,6 +202,9 @@ cmd_source_file_exec(struct cmd *self, struct cmdq_item *item)
|
|||||||
|
|
||||||
cdata = xcalloc(1, sizeof *cdata);
|
cdata = xcalloc(1, sizeof *cdata);
|
||||||
cdata->item = item;
|
cdata->item = item;
|
||||||
|
cdata->client = c;
|
||||||
|
if (c != NULL)
|
||||||
|
c->references++;
|
||||||
|
|
||||||
if (args_has(args, 'q'))
|
if (args_has(args, 'q'))
|
||||||
cdata->flags |= CMD_PARSE_QUIET;
|
cdata->flags |= CMD_PARSE_QUIET;
|
||||||
@@ -249,7 +267,7 @@ cmd_source_file_exec(struct cmd *self, struct cmdq_item *item)
|
|||||||
file_read(c, cdata->files[0], cmd_source_file_done, cdata);
|
file_read(c, cdata->files[0], cmd_source_file_done, cdata);
|
||||||
retval = CMD_RETURN_WAIT;
|
retval = CMD_RETURN_WAIT;
|
||||||
} else
|
} else
|
||||||
cmd_source_file_complete(c, cdata);
|
cmd_source_file_complete(cdata);
|
||||||
|
|
||||||
free(cwd);
|
free(cwd);
|
||||||
return (retval);
|
return (retval);
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
/* $OpenBSD: cmd-split-window.c,v 1.146 2026/07/21 12:28:43 nicm Exp $ */
|
/* $OpenBSD: cmd-split-window.c,v 1.150 2026/08/20 09:19:24 nicm Exp $ */
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2009 Nicholas Marriott <nicholas.marriott@gmail.com>
|
* Copyright (c) 2009 Nicholas Marriott <nicholas.marriott@gmail.com>
|
||||||
@@ -40,8 +40,8 @@ const struct cmd_entry cmd_new_pane_entry = {
|
|||||||
.name = "new-pane",
|
.name = "new-pane",
|
||||||
.alias = "newp",
|
.alias = "newp",
|
||||||
|
|
||||||
.args = { "bB:Cc:de:EfF:hIkl:LMm:Op:PR:s:S:t:T:vWx:X:y:Y:Z", 0, -1, NULL },
|
.args = { "AbB:Cc:de:EfF:hIkl:KLMm:Op:PR:s:S:t:T:vWx:X:y:Y:Z", 0, -1, NULL },
|
||||||
.usage = "[-bCdefhIklMOPvWZ] [-B border-lines] "
|
.usage = "[-AbCdefhIkKLMOPvWZ] [-B border-lines] "
|
||||||
"[-c start-directory] [-e environment] "
|
"[-c start-directory] [-e environment] "
|
||||||
"[-F format] [-l size] [-m message] [-p percentage] "
|
"[-F format] [-l size] [-m message] [-p percentage] "
|
||||||
"[-s style] [-S active-border-style] "
|
"[-s style] [-S active-border-style] "
|
||||||
@@ -89,6 +89,7 @@ cmd_split_window_exec(struct cmd *self, struct cmdq_item *item)
|
|||||||
struct cmd_find_state fs;
|
struct cmd_find_state fs;
|
||||||
struct key_event *event = cmdq_get_event(item);
|
struct key_event *event = cmdq_get_event(item);
|
||||||
int input, empty, is_floating, flags = 0;
|
int input, empty, is_floating, flags = 0;
|
||||||
|
int restore_zoom = 0;
|
||||||
const char *template, *style, *value;
|
const char *template, *style, *value;
|
||||||
char *cause = NULL, *cp, *title;
|
char *cause = NULL, *cp, *title;
|
||||||
const struct options_table_entry *oe;
|
const struct options_table_entry *oe;
|
||||||
@@ -96,9 +97,16 @@ cmd_split_window_exec(struct cmd *self, struct cmdq_item *item)
|
|||||||
enum pane_lines lines;
|
enum pane_lines lines;
|
||||||
u_int count = args_count(args);
|
u_int count = args_count(args);
|
||||||
|
|
||||||
|
if (window_active_pane_is_over_zoom(w))
|
||||||
|
restore_zoom = 1;
|
||||||
|
|
||||||
if (cmd_get_entry(self) == &cmd_new_pane_entry)
|
if (cmd_get_entry(self) == &cmd_new_pane_entry)
|
||||||
is_floating = !args_has(args, 'L');
|
is_floating = !args_has(args, 'L');
|
||||||
else {
|
else {
|
||||||
|
if (!window_pane_is_visible(wp))
|
||||||
|
restore_zoom = 0;
|
||||||
|
if (!restore_zoom)
|
||||||
|
window_unzoom(w, 1);
|
||||||
is_floating = window_pane_is_floating(wp);
|
is_floating = window_pane_is_floating(wp);
|
||||||
flags |= SPAWN_SPLIT;
|
flags |= SPAWN_SPLIT;
|
||||||
}
|
}
|
||||||
@@ -132,7 +140,11 @@ cmd_split_window_exec(struct cmd *self, struct cmdq_item *item)
|
|||||||
if (args_has(args, 'Z'))
|
if (args_has(args, 'Z'))
|
||||||
flags |= SPAWN_ZOOM;
|
flags |= SPAWN_ZOOM;
|
||||||
if (args_has(args, 'O'))
|
if (args_has(args, 'O'))
|
||||||
flags |= SPAWN_MODAL;
|
flags |= SPAWN_MODAL|SPAWN_FLOATOVERZOOM;
|
||||||
|
if (is_floating && args_has(args, 'A'))
|
||||||
|
flags |= SPAWN_FLOATOVERZOOM;
|
||||||
|
if ((w->flags & WINDOW_ZOOMED) && (flags & SPAWN_FLOATOVERZOOM))
|
||||||
|
restore_zoom = 1;
|
||||||
|
|
||||||
input = args_has(args, 'I');
|
input = args_has(args, 'I');
|
||||||
if (input || (count == 1 && *args_string(args, 0) == '\0'))
|
if (input || (count == 1 && *args_string(args, 0) == '\0'))
|
||||||
@@ -168,6 +180,8 @@ cmd_split_window_exec(struct cmd *self, struct cmdq_item *item)
|
|||||||
if (cause != NULL) {
|
if (cause != NULL) {
|
||||||
cmdq_error(item, "%s", cause);
|
cmdq_error(item, "%s", cause);
|
||||||
free(cause);
|
free(cause);
|
||||||
|
if (restore_zoom)
|
||||||
|
window_pop_zoom(w);
|
||||||
return (CMD_RETURN_ERROR);
|
return (CMD_RETURN_ERROR);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -201,6 +215,8 @@ cmd_split_window_exec(struct cmd *self, struct cmdq_item *item)
|
|||||||
*/
|
*/
|
||||||
goto fail;
|
goto fail;
|
||||||
}
|
}
|
||||||
|
if (args_has(args, 'K') && args_has(args, 'O'))
|
||||||
|
new_wp->flags |= PANE_CAPTUREALLKEYS;
|
||||||
if (args_has(args, 'C') && args_has(args, 'O'))
|
if (args_has(args, 'C') && args_has(args, 'O'))
|
||||||
new_wp->flags |= PANE_CLOSEONCLICK;
|
new_wp->flags |= PANE_CLOSEONCLICK;
|
||||||
|
|
||||||
@@ -270,7 +286,10 @@ cmd_split_window_exec(struct cmd *self, struct cmdq_item *item)
|
|||||||
if (~flags & SPAWN_DETACHED)
|
if (~flags & SPAWN_DETACHED)
|
||||||
cmd_find_from_winlink_pane(current, wl, new_wp, 0);
|
cmd_find_from_winlink_pane(current, wl, new_wp, 0);
|
||||||
|
|
||||||
if ((~flags & SPAWN_FLOATING) && !args_has(args, 'O')) {
|
if (restore_zoom) {
|
||||||
|
window_pop_zoom(wp->window);
|
||||||
|
server_redraw_window(wp->window);
|
||||||
|
} else if ((~flags & SPAWN_FLOATING) && !args_has(args, 'O')) {
|
||||||
window_pop_zoom(wp->window);
|
window_pop_zoom(wp->window);
|
||||||
server_redraw_window(wp->window);
|
server_redraw_window(wp->window);
|
||||||
}
|
}
|
||||||
@@ -321,8 +340,9 @@ fail:
|
|||||||
if (!is_floating)
|
if (!is_floating)
|
||||||
layout_close_pane(new_wp);
|
layout_close_pane(new_wp);
|
||||||
window_remove_pane(wp->window, new_wp);
|
window_remove_pane(wp->window, new_wp);
|
||||||
} else if (args_has(args, 'O'))
|
}
|
||||||
window_pop_modal_zoom(wp->window);
|
if (restore_zoom || (~flags & SPAWN_FLOATING))
|
||||||
|
window_pop_zoom(wp->window);
|
||||||
if (sc.argv != NULL)
|
if (sc.argv != NULL)
|
||||||
cmd_free_argv(sc.argc, sc.argv);
|
cmd_free_argv(sc.argc, sc.argv);
|
||||||
environ_free(sc.environ);
|
environ_free(sc.environ);
|
||||||
|
|||||||
@@ -48,10 +48,14 @@ cmd_swap_window_exec(struct cmd *self, struct cmdq_item *item)
|
|||||||
struct args *args = cmd_get_args(self);
|
struct args *args = cmd_get_args(self);
|
||||||
struct cmd_find_state *source = cmdq_get_source(item);
|
struct cmd_find_state *source = cmdq_get_source(item);
|
||||||
struct cmd_find_state *target = cmdq_get_target(item);
|
struct cmd_find_state *target = cmdq_get_target(item);
|
||||||
|
struct client *c = cmdq_get_client(item);
|
||||||
|
struct client *tc = cmdq_get_target_client(item);
|
||||||
|
struct client *ac;
|
||||||
struct session *src = source->s, *dst = target->s;
|
struct session *src = source->s, *dst = target->s;
|
||||||
struct session_group *sg_src, *sg_dst;
|
struct session_group *sg_src, *sg_dst;
|
||||||
struct winlink *wl_src = source->wl, *wl_dst = target->wl;
|
struct winlink *wl_src = source->wl, *wl_dst = target->wl;
|
||||||
struct window *w_src, *w_dst;
|
struct window *w_src, *w_dst;
|
||||||
|
int local_src, local_dst;
|
||||||
|
|
||||||
sg_src = session_group_contains(src);
|
sg_src = session_group_contains(src);
|
||||||
sg_dst = session_group_contains(dst);
|
sg_dst = session_group_contains(dst);
|
||||||
@@ -79,11 +83,23 @@ cmd_swap_window_exec(struct cmd *self, struct cmdq_item *item)
|
|||||||
|
|
||||||
if (marked_pane.wl == wl_src)
|
if (marked_pane.wl == wl_src)
|
||||||
marked_pane.wl = wl_dst;
|
marked_pane.wl = wl_dst;
|
||||||
|
ac = tc;
|
||||||
|
if (ac == NULL)
|
||||||
|
ac = c;
|
||||||
|
local_dst = active_is_local_window(ac, dst);
|
||||||
|
local_src = active_is_local_window(ac, src);
|
||||||
if (args_has(args, 'd')) {
|
if (args_has(args, 'd')) {
|
||||||
|
if (local_dst)
|
||||||
|
active_select_window(ac, dst, wl_dst);
|
||||||
|
else
|
||||||
session_select(dst, wl_dst->idx);
|
session_select(dst, wl_dst->idx);
|
||||||
if (src != dst)
|
if (src != dst) {
|
||||||
|
if (local_src)
|
||||||
|
active_select_window(ac, src, wl_src);
|
||||||
|
else
|
||||||
session_select(src, wl_src->idx);
|
session_select(src, wl_src->idx);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
session_group_synchronize_from(src);
|
session_group_synchronize_from(src);
|
||||||
server_redraw_session_group(src);
|
server_redraw_session_group(src);
|
||||||
if (src != dst) {
|
if (src != dst) {
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
/* $OpenBSD: cmd-switch-client.c,v 1.74 2026/05/22 15:22:43 nicm Exp $ */
|
/* $OpenBSD: cmd-switch-client.c,v 1.75 2026/08/20 09:19:24 nicm Exp $ */
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>
|
* Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>
|
||||||
@@ -53,11 +53,12 @@ cmd_switch_client_exec(struct cmd *self, struct cmdq_item *item)
|
|||||||
struct cmd_find_state target;
|
struct cmd_find_state target;
|
||||||
const char *tflag = args_get(args, 't');
|
const char *tflag = args_get(args, 't');
|
||||||
enum cmd_find_type type;
|
enum cmd_find_type type;
|
||||||
int flags;
|
int flags, visible, window_target;
|
||||||
|
int Zflag = args_has(args, 'Z');
|
||||||
struct client *c = cmdq_get_client(item);
|
struct client *c = cmdq_get_client(item);
|
||||||
struct client *tc = cmdq_get_target_client(item);
|
struct client *tc = cmdq_get_target_client(item);
|
||||||
struct session *s;
|
struct session *s;
|
||||||
struct winlink *wl;
|
struct winlink *wl, *current_wl;
|
||||||
struct window *w;
|
struct window *w;
|
||||||
struct window_pane *wp;
|
struct window_pane *wp;
|
||||||
const char *tablename;
|
const char *tablename;
|
||||||
@@ -65,8 +66,13 @@ cmd_switch_client_exec(struct cmd *self, struct cmdq_item *item)
|
|||||||
struct sort_criteria sort_crit;
|
struct sort_criteria sort_crit;
|
||||||
uid_t uid;
|
uid_t uid;
|
||||||
|
|
||||||
if (tflag != NULL &&
|
window_target = 0;
|
||||||
(tflag[strcspn(tflag, ":.%")] != '\0' || strcmp(tflag, "=") == 0)) {
|
if (tflag != NULL) {
|
||||||
|
if (tflag[strcspn(tflag, ":.%")] != '\0' ||
|
||||||
|
strcmp(tflag, "=") == 0)
|
||||||
|
window_target = 1;
|
||||||
|
}
|
||||||
|
if (window_target) {
|
||||||
type = CMD_FIND_PANE;
|
type = CMD_FIND_PANE;
|
||||||
flags = 0;
|
flags = 0;
|
||||||
} else {
|
} else {
|
||||||
@@ -137,17 +143,26 @@ cmd_switch_client_exec(struct cmd *self, struct cmdq_item *item)
|
|||||||
} else {
|
} else {
|
||||||
if (cmdq_get_client(item) == NULL)
|
if (cmdq_get_client(item) == NULL)
|
||||||
return (CMD_RETURN_NORMAL);
|
return (CMD_RETURN_NORMAL);
|
||||||
if (wl != NULL && wp != NULL && wp != wl->window->active) {
|
if (window_target && wl != NULL && wp != NULL &&
|
||||||
|
wp != wl->window->active) {
|
||||||
w = wl->window;
|
w = wl->window;
|
||||||
if (window_push_zoom(w, 0, args_has(args, 'Z')))
|
if (w->modal != NULL && wp != w->modal)
|
||||||
|
visible = 1;
|
||||||
|
else
|
||||||
|
visible = window_pane_is_visible(wp);
|
||||||
|
if (!visible && window_push_zoom(w, 0, Zflag))
|
||||||
server_redraw_window(w);
|
server_redraw_window(w);
|
||||||
window_redraw_active_switch(w, wp);
|
window_redraw_active_switch(w, wp);
|
||||||
window_set_active_pane(w, wp, 1);
|
window_set_active_pane(w, wp, 1);
|
||||||
if (window_pop_zoom(w))
|
if (!visible && window_pop_zoom(w))
|
||||||
server_redraw_window(w);
|
server_redraw_window(w);
|
||||||
}
|
}
|
||||||
if (wl != NULL) {
|
if (window_target && wl != NULL) {
|
||||||
session_set_current(s, wl);
|
active_select_window(tc, s, wl);
|
||||||
|
if (active_is_local_window(tc, s)) {
|
||||||
|
current_wl = active_get_effective_winlink(tc, s);
|
||||||
|
cmd_find_from_winlink(current, current_wl, 0);
|
||||||
|
} else
|
||||||
cmd_find_from_session(current, s, 0);
|
cmd_find_from_session(current, s, 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -31,7 +31,7 @@ getpeereid(int s, uid_t *uid, gid_t *gid)
|
|||||||
{
|
{
|
||||||
#ifdef HAVE_SO_PEERCRED
|
#ifdef HAVE_SO_PEERCRED
|
||||||
struct ucred uc;
|
struct ucred uc;
|
||||||
int len = sizeof uc;
|
socklen_t len = sizeof uc;
|
||||||
|
|
||||||
if (getsockopt(s, SOL_SOCKET, SO_PEERCRED, &uc, &len) == -1)
|
if (getsockopt(s, SOL_SOCKET, SO_PEERCRED, &uc, &len) == -1)
|
||||||
return (-1);
|
return (-1);
|
||||||
|
|||||||
23
configure.ac
23
configure.ac
@@ -530,24 +530,15 @@ if test "x$enable_cgroups" = xyes; then
|
|||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Enable image support.
|
# Enable sixel support.
|
||||||
AC_ARG_ENABLE(
|
|
||||||
images,
|
|
||||||
AS_HELP_STRING(--enable-images, enable SIXEL and ASCII images)
|
|
||||||
)
|
|
||||||
|
|
||||||
# Keep the old option as an alias for one compatibility cycle.
|
|
||||||
AC_ARG_ENABLE(
|
AC_ARG_ENABLE(
|
||||||
sixel,
|
sixel,
|
||||||
AS_HELP_STRING(--enable-sixel, deprecated alias for --enable-images)
|
AS_HELP_STRING(--enable-sixel, enable sixel images)
|
||||||
)
|
)
|
||||||
if test "x$enable_sixel" = xyes; then
|
if test "x$enable_sixel" = xyes; then
|
||||||
enable_images=yes
|
AC_DEFINE(ENABLE_SIXEL)
|
||||||
fi
|
fi
|
||||||
if test "x$enable_images" = xyes; then
|
AM_CONDITIONAL(ENABLE_SIXEL, [test "x$enable_sixel" = xyes])
|
||||||
AC_DEFINE(ENABLE_IMAGES)
|
|
||||||
fi
|
|
||||||
AM_CONDITIONAL(ENABLE_IMAGES, [test "x$enable_images" = xyes])
|
|
||||||
|
|
||||||
# Check for b64_ntop. If we have b64_ntop, we assume b64_pton as well.
|
# Check for b64_ntop. If we have b64_ntop, we assume b64_pton as well.
|
||||||
AC_MSG_CHECKING(for b64_ntop)
|
AC_MSG_CHECKING(for b64_ntop)
|
||||||
@@ -1146,10 +1137,10 @@ if test "x$enable_asan" = xyes; then
|
|||||||
else
|
else
|
||||||
AC_MSG_NOTICE([ASAN: off])
|
AC_MSG_NOTICE([ASAN: off])
|
||||||
fi
|
fi
|
||||||
if test "x$enable_images" = xyes; then
|
if test "x$enable_sixel" = xyes; then
|
||||||
AC_MSG_NOTICE([images: on])
|
AC_MSG_NOTICE([SIXEL: on])
|
||||||
else
|
else
|
||||||
AC_MSG_NOTICE([images: off])
|
AC_MSG_NOTICE([SIXEL: off])
|
||||||
fi
|
fi
|
||||||
if test "x$enable_debug" = xyes; then
|
if test "x$enable_debug" = xyes; then
|
||||||
AC_MSG_NOTICE([debug: on])
|
AC_MSG_NOTICE([debug: on])
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
/* $OpenBSD: control-notify.c,v 1.37 2026/07/10 13:38:45 nicm Exp $ */
|
/* $OpenBSD: control-notify.c,v 1.38 2026/08/03 13:38:42 nicm Exp $ */
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2012 Nicholas Marriott <nicholas.marriott@gmail.com>
|
* Copyright (c) 2012 Nicholas Marriott <nicholas.marriott@gmail.com>
|
||||||
@@ -44,7 +44,8 @@ control_pane_mode_changed_cb(__unused const char *name,
|
|||||||
TAILQ_FOREACH(c, &clients, entry) {
|
TAILQ_FOREACH(c, &clients, entry) {
|
||||||
if (!CONTROL_SHOULD_NOTIFY_CLIENT(c))
|
if (!CONTROL_SHOULD_NOTIFY_CLIENT(c))
|
||||||
continue;
|
continue;
|
||||||
control_write(c, "%%pane-mode-changed %%%u", wp->id);
|
control_notify_write(c, "%%pane-mode-changed %%%u",
|
||||||
|
wp->id);
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -53,8 +54,9 @@ control_pane_mode_changed_cb(__unused const char *name,
|
|||||||
if (value == NULL)
|
if (value == NULL)
|
||||||
return;
|
return;
|
||||||
TAILQ_FOREACH(c, &clients, entry) {
|
TAILQ_FOREACH(c, &clients, entry) {
|
||||||
if (CONTROL_SHOULD_NOTIFY_CLIENT(c))
|
if (!CONTROL_SHOULD_NOTIFY_CLIENT(c))
|
||||||
control_write(c, "%%pane-mode-changed %s", value);
|
continue;
|
||||||
|
control_notify_write(c, "%%pane-mode-changed %s", value);
|
||||||
}
|
}
|
||||||
free(value);
|
free(value);
|
||||||
}
|
}
|
||||||
@@ -92,7 +94,7 @@ control_window_layout_changed_cb(__unused const char *name,
|
|||||||
continue;
|
continue;
|
||||||
s = c->session;
|
s = c->session;
|
||||||
if (winlink_find_by_window_id(&s->windows, w->id) != NULL)
|
if (winlink_find_by_window_id(&s->windows, w->id) != NULL)
|
||||||
control_write(c, "%s", cp);
|
control_notify_write(c, "%s", cp);
|
||||||
}
|
}
|
||||||
free(cp);
|
free(cp);
|
||||||
}
|
}
|
||||||
@@ -111,7 +113,7 @@ control_window_pane_changed_cb(__unused const char *name,
|
|||||||
if (!CONTROL_SHOULD_NOTIFY_CLIENT(c))
|
if (!CONTROL_SHOULD_NOTIFY_CLIENT(c))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
control_write(c, "%%window-pane-changed @%u %%%u", w->id,
|
control_notify_write(c, "%%window-pane-changed @%u %%%u", w->id,
|
||||||
w->active->id);
|
w->active->id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -133,9 +135,11 @@ control_window_unlinked_cb(__unused const char *name, struct event_payload *ep,
|
|||||||
cs = c->session;
|
cs = c->session;
|
||||||
|
|
||||||
if (winlink_find_by_window_id(&cs->windows, w->id) != NULL)
|
if (winlink_find_by_window_id(&cs->windows, w->id) != NULL)
|
||||||
control_write(c, "%%window-close @%u", w->id);
|
control_notify_write(c, "%%window-close @%u", w->id);
|
||||||
else
|
else {
|
||||||
control_write(c, "%%unlinked-window-close @%u", w->id);
|
control_notify_write(c, "%%unlinked-window-close @%u",
|
||||||
|
w->id);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -156,9 +160,11 @@ control_window_linked_cb(__unused const char *name, struct event_payload *ep,
|
|||||||
cs = c->session;
|
cs = c->session;
|
||||||
|
|
||||||
if (winlink_find_by_window_id(&cs->windows, w->id) != NULL)
|
if (winlink_find_by_window_id(&cs->windows, w->id) != NULL)
|
||||||
control_write(c, "%%window-add @%u", w->id);
|
control_notify_write(c, "%%window-add @%u", w->id);
|
||||||
else
|
else {
|
||||||
control_write(c, "%%unlinked-window-add @%u", w->id);
|
control_notify_write(c, "%%unlinked-window-add @%u",
|
||||||
|
w->id);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -179,11 +185,11 @@ control_window_renamed_cb(__unused const char *name, struct event_payload *ep,
|
|||||||
cs = c->session;
|
cs = c->session;
|
||||||
|
|
||||||
if (winlink_find_by_window_id(&cs->windows, w->id) != NULL) {
|
if (winlink_find_by_window_id(&cs->windows, w->id) != NULL) {
|
||||||
control_write(c, "%%window-renamed @%u %s", w->id,
|
control_notify_write(c, "%%window-renamed @%u %s",
|
||||||
w->name);
|
|
||||||
} else {
|
|
||||||
control_write(c, "%%unlinked-window-renamed @%u %s",
|
|
||||||
w->id, w->name);
|
w->id, w->name);
|
||||||
|
} else {
|
||||||
|
control_notify_write(c,
|
||||||
|
"%%unlinked-window-renamed @%u %s", w->id, w->name);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -206,15 +212,38 @@ control_client_session_changed_cb(__unused const char *name,
|
|||||||
continue;
|
continue;
|
||||||
|
|
||||||
if (cc == c) {
|
if (cc == c) {
|
||||||
control_write(c, "%%session-changed $%u %s", s->id,
|
control_notify_write(c, "%%session-changed $%u %s",
|
||||||
s->name);
|
s->id, s->name);
|
||||||
} else {
|
} else {
|
||||||
control_write(c, "%%client-session-changed %s $%u %s",
|
control_notify_write(c,
|
||||||
cc->name, s->id, s->name);
|
"%%client-session-changed %s $%u %s", cc->name,
|
||||||
|
s->id, s->name);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Notify control clients that a client changed its effective window. */
|
||||||
|
static void
|
||||||
|
control_client_window_changed_cb(__unused const char *name,
|
||||||
|
struct event_payload *ep, __unused void *sink_data)
|
||||||
|
{
|
||||||
|
struct client *cc = event_payload_get_client(ep, "client");
|
||||||
|
struct session *s = event_payload_get_session(ep, "session");
|
||||||
|
struct window *w = event_payload_get_window(ep, "window");
|
||||||
|
struct client *c;
|
||||||
|
|
||||||
|
if (cc == NULL || s == NULL || w == NULL)
|
||||||
|
return;
|
||||||
|
|
||||||
|
TAILQ_FOREACH(c, &clients, entry) {
|
||||||
|
if (!CONTROL_SHOULD_NOTIFY_CLIENT(c))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
control_write(c, "%%client-window-changed %s $%u @%u",
|
||||||
|
cc->name, s->id, w->id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* Notify control clients that a client detached. */
|
/* Notify control clients that a client detached. */
|
||||||
static void
|
static void
|
||||||
control_client_detached_cb(__unused const char *name, struct event_payload *ep,
|
control_client_detached_cb(__unused const char *name, struct event_payload *ep,
|
||||||
@@ -226,8 +255,9 @@ control_client_detached_cb(__unused const char *name, struct event_payload *ep,
|
|||||||
if (cc == NULL)
|
if (cc == NULL)
|
||||||
return;
|
return;
|
||||||
TAILQ_FOREACH(c, &clients, entry) {
|
TAILQ_FOREACH(c, &clients, entry) {
|
||||||
if (CONTROL_SHOULD_NOTIFY_CLIENT(c))
|
if (!CONTROL_SHOULD_NOTIFY_CLIENT(c))
|
||||||
control_write(c, "%%client-detached %s", cc->name);
|
continue;
|
||||||
|
control_notify_write(c, "%%client-detached %s", cc->name);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -244,8 +274,8 @@ control_session_renamed_cb(__unused const char *name, struct event_payload *ep,
|
|||||||
TAILQ_FOREACH(c, &clients, entry) {
|
TAILQ_FOREACH(c, &clients, entry) {
|
||||||
if (!CONTROL_SHOULD_NOTIFY_CLIENT(c))
|
if (!CONTROL_SHOULD_NOTIFY_CLIENT(c))
|
||||||
continue;
|
continue;
|
||||||
|
control_notify_write(c, "%%session-renamed $%u %s", s->id,
|
||||||
control_write(c, "%%session-renamed $%u %s", s->id, s->name);
|
s->name);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -259,8 +289,7 @@ control_session_created_cb(__unused const char *name,
|
|||||||
TAILQ_FOREACH(c, &clients, entry) {
|
TAILQ_FOREACH(c, &clients, entry) {
|
||||||
if (!CONTROL_SHOULD_NOTIFY_CLIENT(c))
|
if (!CONTROL_SHOULD_NOTIFY_CLIENT(c))
|
||||||
continue;
|
continue;
|
||||||
|
control_notify_write(c, "%%sessions-changed");
|
||||||
control_write(c, "%%sessions-changed");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -274,8 +303,7 @@ control_session_closed_cb(__unused const char *name,
|
|||||||
TAILQ_FOREACH(c, &clients, entry) {
|
TAILQ_FOREACH(c, &clients, entry) {
|
||||||
if (!CONTROL_SHOULD_NOTIFY_CLIENT(c))
|
if (!CONTROL_SHOULD_NOTIFY_CLIENT(c))
|
||||||
continue;
|
continue;
|
||||||
|
control_notify_write(c, "%%sessions-changed");
|
||||||
control_write(c, "%%sessions-changed");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -298,9 +326,8 @@ control_session_window_changed_cb(__unused const char *name,
|
|||||||
TAILQ_FOREACH(c, &clients, entry) {
|
TAILQ_FOREACH(c, &clients, entry) {
|
||||||
if (!CONTROL_SHOULD_NOTIFY_CLIENT(c))
|
if (!CONTROL_SHOULD_NOTIFY_CLIENT(c))
|
||||||
continue;
|
continue;
|
||||||
|
control_notify_write(c, "%%session-window-changed $%u @%u",
|
||||||
control_write(c, "%%session-window-changed $%u @%u", s->id,
|
s->id, s->curw->window->id);
|
||||||
s->curw->window->id);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -317,8 +344,7 @@ control_paste_buffer_changed_cb(__unused const char *name,
|
|||||||
TAILQ_FOREACH(c, &clients, entry) {
|
TAILQ_FOREACH(c, &clients, entry) {
|
||||||
if (!CONTROL_SHOULD_NOTIFY_CLIENT(c))
|
if (!CONTROL_SHOULD_NOTIFY_CLIENT(c))
|
||||||
continue;
|
continue;
|
||||||
|
control_notify_write(c, "%%paste-buffer-changed %s", pbname);
|
||||||
control_write(c, "%%paste-buffer-changed %s", pbname);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -335,8 +361,7 @@ control_paste_buffer_deleted_cb(__unused const char *name,
|
|||||||
TAILQ_FOREACH(c, &clients, entry) {
|
TAILQ_FOREACH(c, &clients, entry) {
|
||||||
if (!CONTROL_SHOULD_NOTIFY_CLIENT(c))
|
if (!CONTROL_SHOULD_NOTIFY_CLIENT(c))
|
||||||
continue;
|
continue;
|
||||||
|
control_notify_write(c, "%%paste-buffer-deleted %s", pbname);
|
||||||
control_write(c, "%%paste-buffer-deleted %s", pbname);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -356,6 +381,7 @@ control_build_events(void)
|
|||||||
{ "window-linked", control_window_linked_cb },
|
{ "window-linked", control_window_linked_cb },
|
||||||
{ "window-renamed", control_window_renamed_cb },
|
{ "window-renamed", control_window_renamed_cb },
|
||||||
{ "client-session-changed", control_client_session_changed_cb },
|
{ "client-session-changed", control_client_session_changed_cb },
|
||||||
|
{ "client-window-changed", control_client_window_changed_cb },
|
||||||
{ "client-detached", control_client_detached_cb },
|
{ "client-detached", control_client_detached_cb },
|
||||||
{ "session-renamed", control_session_renamed_cb },
|
{ "session-renamed", control_session_renamed_cb },
|
||||||
{ "session-created", control_session_created_cb },
|
{ "session-created", control_session_created_cb },
|
||||||
|
|||||||
187
control.c
187
control.c
@@ -1,4 +1,4 @@
|
|||||||
/* $OpenBSD: control.c,v 1.62 2026/07/17 08:37:29 nicm Exp $ */
|
/* $OpenBSD: control.c,v 1.66 2026/08/18 07:43:44 nicm Exp $ */
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2012 Nicholas Marriott <nicholas.marriott@gmail.com>
|
* Copyright (c) 2012 Nicholas Marriott <nicholas.marriott@gmail.com>
|
||||||
@@ -53,6 +53,17 @@ struct control_block {
|
|||||||
TAILQ_ENTRY(control_block) all_entry;
|
TAILQ_ENTRY(control_block) all_entry;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
* A notification line deferred because it was generated while a command's
|
||||||
|
* %begin/%end guard block was open. Notifications must never appear inside a
|
||||||
|
* guard block, so they are held here and flushed once the block closes.
|
||||||
|
*/
|
||||||
|
struct control_line {
|
||||||
|
char *line;
|
||||||
|
|
||||||
|
TAILQ_ENTRY(control_line) entry;
|
||||||
|
};
|
||||||
|
|
||||||
/* Control client pane. */
|
/* Control client pane. */
|
||||||
struct control_pane {
|
struct control_pane {
|
||||||
u_int pane;
|
u_int pane;
|
||||||
@@ -102,6 +113,13 @@ struct control_state {
|
|||||||
struct bufferevent *write_event;
|
struct bufferevent *write_event;
|
||||||
|
|
||||||
struct monitor_set *subs;
|
struct monitor_set *subs;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Depth of open %begin/%end guard blocks and notifications deferred
|
||||||
|
* until the outermost block closes.
|
||||||
|
*/
|
||||||
|
int guard_depth;
|
||||||
|
TAILQ_HEAD(, control_line) deferred;
|
||||||
};
|
};
|
||||||
|
|
||||||
/* Low and high watermarks. */
|
/* Low and high watermarks. */
|
||||||
@@ -352,7 +370,7 @@ control_continue_pane(struct client *c, struct window_pane *wp)
|
|||||||
cp->flags &= ~CONTROL_PANE_PAUSED;
|
cp->flags &= ~CONTROL_PANE_PAUSED;
|
||||||
memcpy(&cp->offset, &wp->offset, sizeof cp->offset);
|
memcpy(&cp->offset, &wp->offset, sizeof cp->offset);
|
||||||
memcpy(&cp->queued, &wp->offset, sizeof cp->queued);
|
memcpy(&cp->queued, &wp->offset, sizeof cp->queued);
|
||||||
control_write(c, "%%continue %%%u", wp->id);
|
control_notify_write(c, "%%continue %%%u", wp->id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -366,52 +384,139 @@ control_pause_pane(struct client *c, struct window_pane *wp)
|
|||||||
if (~cp->flags & CONTROL_PANE_PAUSED) {
|
if (~cp->flags & CONTROL_PANE_PAUSED) {
|
||||||
cp->flags |= CONTROL_PANE_PAUSED;
|
cp->flags |= CONTROL_PANE_PAUSED;
|
||||||
control_discard_pane(c, cp);
|
control_discard_pane(c, cp);
|
||||||
control_write(c, "%%pause %%%u", wp->id);
|
control_notify_write(c, "%%pause %%%u", wp->id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Write a line. */
|
/*
|
||||||
static void printflike(2, 0)
|
* Reset a pane after its buffer has been replaced: drop any output still
|
||||||
control_vwrite(struct client *c, const char *fmt, va_list ap)
|
* queued from the old buffer and start again from the pane's own offset.
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
control_reset_pane(struct client *c, struct window_pane *wp)
|
||||||
{
|
{
|
||||||
struct control_state *cs = c->control_state;
|
struct control_pane *cp;
|
||||||
char *s;
|
|
||||||
|
|
||||||
xvasprintf(&s, fmt, ap);
|
if (c->control_state == NULL)
|
||||||
log_debug("%s: %s: writing line: %s", __func__, c->name, s);
|
return;
|
||||||
|
cp = control_get_pane(c, wp);
|
||||||
bufferevent_write(cs->write_event, s, strlen(s));
|
if (cp == NULL)
|
||||||
bufferevent_write(cs->write_event, "\n", 1);
|
return;
|
||||||
|
control_discard_pane(c, cp);
|
||||||
bufferevent_enable(cs->write_event, EV_WRITE);
|
memcpy(&cp->offset, &wp->offset, sizeof cp->offset);
|
||||||
free(s);
|
memcpy(&cp->queued, &wp->offset, sizeof cp->queued);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Write a line. */
|
/* Write an already-formatted line, queueing it behind %output if needed. */
|
||||||
void
|
static void
|
||||||
control_write(struct client *c, const char *fmt, ...)
|
control_write_line(struct client *c, char *line)
|
||||||
{
|
{
|
||||||
struct control_state *cs = c->control_state;
|
struct control_state *cs = c->control_state;
|
||||||
struct control_block *cb;
|
struct control_block *cb;
|
||||||
va_list ap;
|
|
||||||
|
|
||||||
va_start(ap, fmt);
|
|
||||||
|
|
||||||
if (TAILQ_EMPTY(&cs->all_blocks)) {
|
if (TAILQ_EMPTY(&cs->all_blocks)) {
|
||||||
control_vwrite(c, fmt, ap);
|
log_debug("%s: %s: writing line: %s", __func__, c->name, line);
|
||||||
va_end(ap);
|
bufferevent_write(cs->write_event, line, strlen(line));
|
||||||
|
bufferevent_write(cs->write_event, "\n", 1);
|
||||||
|
bufferevent_enable(cs->write_event, EV_WRITE);
|
||||||
|
free(line);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
cb = xcalloc(1, sizeof *cb);
|
cb = xcalloc(1, sizeof *cb);
|
||||||
xvasprintf(&cb->line, fmt, ap);
|
cb->line = line;
|
||||||
TAILQ_INSERT_TAIL(&cs->all_blocks, cb, all_entry);
|
TAILQ_INSERT_TAIL(&cs->all_blocks, cb, all_entry);
|
||||||
cb->t = get_timer();
|
cb->t = get_timer();
|
||||||
|
|
||||||
log_debug("%s: %s: storing line: %s", __func__, c->name, cb->line);
|
log_debug("%s: %s: storing line: %s", __func__, c->name, cb->line);
|
||||||
bufferevent_enable(cs->write_event, EV_WRITE);
|
bufferevent_enable(cs->write_event, EV_WRITE);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Flush notifications that were deferred while a guard block was open. */
|
||||||
|
static void
|
||||||
|
control_flush_deferred(struct client *c)
|
||||||
|
{
|
||||||
|
struct control_state *cs = c->control_state;
|
||||||
|
struct control_line *cl, *cl1;
|
||||||
|
|
||||||
|
TAILQ_FOREACH_SAFE(cl, &cs->deferred, entry, cl1) {
|
||||||
|
TAILQ_REMOVE(&cs->deferred, cl, entry);
|
||||||
|
control_write_line(c, cl->line);
|
||||||
|
free(cl);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Write a line of command output or error text. This is a sink for arbitrary
|
||||||
|
* user-controlled text (command output, capture-pane, error messages), so it
|
||||||
|
* must never try to interpret the content: guard tracking is done only in
|
||||||
|
* control_write_guard.
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
control_write(struct client *c, const char *fmt, ...)
|
||||||
|
{
|
||||||
|
va_list ap;
|
||||||
|
char *line;
|
||||||
|
|
||||||
|
va_start(ap, fmt);
|
||||||
|
xvasprintf(&line, fmt, ap);
|
||||||
va_end(ap);
|
va_end(ap);
|
||||||
|
|
||||||
|
control_write_line(c, line);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Write a %begin, %end or %error guard around a command's output. This is the
|
||||||
|
* only place guard lines are produced, so the block depth is maintained here;
|
||||||
|
* when the outermost block closes any deferred notifications are flushed after
|
||||||
|
* it. "guard" is always one of the fixed strings from cmdq_guard, never user
|
||||||
|
* text.
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
control_write_guard(struct client *c, const char *guard, long t, u_int number,
|
||||||
|
int flags)
|
||||||
|
{
|
||||||
|
struct control_state *cs = c->control_state;
|
||||||
|
char *line;
|
||||||
|
|
||||||
|
if (strcmp(guard, "begin") == 0)
|
||||||
|
cs->guard_depth++;
|
||||||
|
|
||||||
|
xasprintf(&line, "%%%s %ld %u %d", guard, t, number, flags);
|
||||||
|
control_write_line(c, line);
|
||||||
|
|
||||||
|
if (strcmp(guard, "begin") != 0 && cs->guard_depth > 0 &&
|
||||||
|
--cs->guard_depth == 0)
|
||||||
|
control_flush_deferred(c);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Write a notification line. Notifications must never appear inside a command's
|
||||||
|
* %begin/%end guard block, so if one is open the line is deferred until it
|
||||||
|
* closes.
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
control_notify_write(struct client *c, const char *fmt, ...)
|
||||||
|
{
|
||||||
|
struct control_state *cs = c->control_state;
|
||||||
|
struct control_line *cl;
|
||||||
|
va_list ap;
|
||||||
|
char *line;
|
||||||
|
|
||||||
|
va_start(ap, fmt);
|
||||||
|
xvasprintf(&line, fmt, ap);
|
||||||
|
va_end(ap);
|
||||||
|
|
||||||
|
if (cs->guard_depth == 0) {
|
||||||
|
control_write_line(c, line);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
log_debug("%s: %s: deferring notification: %s", __func__, c->name,
|
||||||
|
line);
|
||||||
|
cl = xcalloc(1, sizeof *cl);
|
||||||
|
cl->line = line;
|
||||||
|
TAILQ_INSERT_TAIL(&cs->deferred, cl, entry);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Check age for this pane. */
|
/* Check age for this pane. */
|
||||||
@@ -438,7 +543,7 @@ control_check_age(struct client *c, struct window_pane *wp,
|
|||||||
return (0);
|
return (0);
|
||||||
cp->flags |= CONTROL_PANE_PAUSED;
|
cp->flags |= CONTROL_PANE_PAUSED;
|
||||||
control_discard_pane(c, cp);
|
control_discard_pane(c, cp);
|
||||||
control_write(c, "%%pause %%%u", wp->id);
|
control_notify_write(c, "%%pause %%%u", wp->id);
|
||||||
} else {
|
} else {
|
||||||
if (age < CONTROL_MAXIMUM_AGE)
|
if (age < CONTROL_MAXIMUM_AGE)
|
||||||
return (0);
|
return (0);
|
||||||
@@ -805,14 +910,17 @@ control_sub_change(struct monitor_change *change, __unused void *data)
|
|||||||
|
|
||||||
if (wp != NULL) {
|
if (wp != NULL) {
|
||||||
w = wp->window;
|
w = wp->window;
|
||||||
control_write(c, "%%subscription-changed %s $%u @%u %u %%%u : %s",
|
control_notify_write(c,
|
||||||
|
"%%subscription-changed %s $%u @%u %u %%%u : %s",
|
||||||
change->name, s->id, w->id, wl->idx, wp->id, change->value);
|
change->name, s->id, w->id, wl->idx, wp->id, change->value);
|
||||||
} else if (wl != NULL) {
|
} else if (wl != NULL) {
|
||||||
w = wl->window;
|
w = wl->window;
|
||||||
control_write(c, "%%subscription-changed %s $%u @%u %u - : %s",
|
control_notify_write(c,
|
||||||
|
"%%subscription-changed %s $%u @%u %u - : %s",
|
||||||
change->name, s->id, w->id, wl->idx, change->value);
|
change->name, s->id, w->id, wl->idx, change->value);
|
||||||
} else {
|
} else {
|
||||||
control_write(c, "%%subscription-changed %s $%u - - - : %s",
|
control_notify_write(c,
|
||||||
|
"%%subscription-changed %s $%u - - - : %s",
|
||||||
change->name, s->id, change->value);
|
change->name, s->id, change->value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -835,6 +943,7 @@ control_start(struct client *c)
|
|||||||
RB_INIT(&cs->windows);
|
RB_INIT(&cs->windows);
|
||||||
TAILQ_INIT(&cs->pending_list);
|
TAILQ_INIT(&cs->pending_list);
|
||||||
TAILQ_INIT(&cs->all_blocks);
|
TAILQ_INIT(&cs->all_blocks);
|
||||||
|
TAILQ_INIT(&cs->deferred);
|
||||||
cs->subs = monitor_create_client(c, control_sub_change, NULL);
|
cs->subs = monitor_create_client(c, control_sub_change, NULL);
|
||||||
|
|
||||||
cs->read_event = bufferevent_new(c->fd, control_read_callback,
|
cs->read_event = bufferevent_new(c->fd, control_read_callback,
|
||||||
@@ -878,6 +987,19 @@ control_discard(struct client *c)
|
|||||||
bufferevent_disable(cs->read_event, EV_READ);
|
bufferevent_disable(cs->read_event, EV_READ);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Discard all tmux-owned queued control blocks and stop writing. */
|
||||||
|
void
|
||||||
|
control_discard_all(struct client *c)
|
||||||
|
{
|
||||||
|
struct control_state *cs = c->control_state;
|
||||||
|
struct control_block *cb, *cb1;
|
||||||
|
|
||||||
|
control_discard(c);
|
||||||
|
TAILQ_FOREACH_SAFE(cb, &cs->all_blocks, all_entry, cb1)
|
||||||
|
control_free_block(cs, cb);
|
||||||
|
bufferevent_disable(cs->write_event, EV_WRITE);
|
||||||
|
}
|
||||||
|
|
||||||
/* Stop control mode. */
|
/* Stop control mode. */
|
||||||
void
|
void
|
||||||
control_stop(struct client *c)
|
control_stop(struct client *c)
|
||||||
@@ -885,12 +1007,19 @@ control_stop(struct client *c)
|
|||||||
struct control_state *cs = c->control_state;
|
struct control_state *cs = c->control_state;
|
||||||
struct control_block *cb, *cb1;
|
struct control_block *cb, *cb1;
|
||||||
struct control_window *cw, *cw1;
|
struct control_window *cw, *cw1;
|
||||||
|
struct control_line *cl, *cl1;
|
||||||
|
|
||||||
if (cs == NULL)
|
if (cs == NULL)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
monitor_destroy(cs->subs);
|
monitor_destroy(cs->subs);
|
||||||
|
|
||||||
|
TAILQ_FOREACH_SAFE(cl, &cs->deferred, entry, cl1) {
|
||||||
|
TAILQ_REMOVE(&cs->deferred, cl, entry);
|
||||||
|
free(cl->line);
|
||||||
|
free(cl);
|
||||||
|
}
|
||||||
|
|
||||||
if (~c->flags & CLIENT_CONTROLCONTROL)
|
if (~c->flags & CLIENT_CONTROLCONTROL)
|
||||||
bufferevent_free(cs->write_event);
|
bufferevent_free(cs->write_event);
|
||||||
bufferevent_free(cs->read_event);
|
bufferevent_free(cs->read_event);
|
||||||
|
|||||||
84
file.c
84
file.c
@@ -1,4 +1,4 @@
|
|||||||
/* $OpenBSD: file.c,v 1.21 2026/07/26 15:08:15 nicm Exp $ */
|
/* $OpenBSD: file.c,v 1.22 2026/08/17 07:56:56 nicm Exp $ */
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2019 Nicholas Marriott <nicholas.marriott@gmail.com>
|
* Copyright (c) 2019 Nicholas Marriott <nicholas.marriott@gmail.com>
|
||||||
@@ -502,6 +502,7 @@ file_push(struct client_file *cf)
|
|||||||
} else if (cf->stream > 2) {
|
} else if (cf->stream > 2) {
|
||||||
close.stream = cf->stream;
|
close.stream = cf->stream;
|
||||||
proc_send(cf->peer, MSG_WRITE_CLOSE, -1, &close, sizeof close);
|
proc_send(cf->peer, MSG_WRITE_CLOSE, -1, &close, sizeof close);
|
||||||
|
if (cf->c == NULL || (~cf->c->flags & CLIENT_WRITE_ACK))
|
||||||
file_fire_done(cf);
|
file_fire_done(cf);
|
||||||
}
|
}
|
||||||
free(msg);
|
free(msg);
|
||||||
@@ -527,14 +528,48 @@ file_write_left(struct client_files *files)
|
|||||||
return (waiting != 0);
|
return (waiting != 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Finish writing a client file. */
|
||||||
|
static void
|
||||||
|
file_write_finished(struct client_file *cf)
|
||||||
|
{
|
||||||
|
struct msg_write_done msg;
|
||||||
|
|
||||||
|
if (cf->event != NULL) {
|
||||||
|
bufferevent_free(cf->event);
|
||||||
|
cf->event = NULL;
|
||||||
|
}
|
||||||
|
if (cf->fd != -1) {
|
||||||
|
if (close(cf->fd) != 0 && cf->error == 0)
|
||||||
|
cf->error = errno;
|
||||||
|
cf->fd = -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
msg.stream = cf->stream;
|
||||||
|
msg.error = cf->error;
|
||||||
|
proc_send(cf->peer, MSG_WRITE_DONE, -1, &msg, sizeof msg);
|
||||||
|
|
||||||
|
if (cf->cb != NULL)
|
||||||
|
cf->cb(NULL, NULL, 0, -1, NULL, cf->data);
|
||||||
|
file_free(cf);
|
||||||
|
}
|
||||||
|
|
||||||
/* Client file write error callback. */
|
/* Client file write error callback. */
|
||||||
static void
|
static void
|
||||||
file_write_error_callback(__unused struct bufferevent *bev, __unused short what,
|
file_write_error_callback(__unused struct bufferevent *bev, short what,
|
||||||
void *arg)
|
void *arg)
|
||||||
{
|
{
|
||||||
struct client_file *cf = arg;
|
struct client_file *cf = arg;
|
||||||
|
int error;
|
||||||
|
|
||||||
|
if (what & EVBUFFER_ERROR)
|
||||||
|
error = errno;
|
||||||
|
else
|
||||||
|
error = EIO;
|
||||||
|
if (error == 0)
|
||||||
|
error = EIO;
|
||||||
|
|
||||||
log_debug("write error file %d", cf->stream);
|
log_debug("write error file %d", cf->stream);
|
||||||
|
cf->error = error;
|
||||||
|
|
||||||
bufferevent_free(cf->event);
|
bufferevent_free(cf->event);
|
||||||
cf->event = NULL;
|
cf->event = NULL;
|
||||||
@@ -542,7 +577,9 @@ file_write_error_callback(__unused struct bufferevent *bev, __unused short what,
|
|||||||
close(cf->fd);
|
close(cf->fd);
|
||||||
cf->fd = -1;
|
cf->fd = -1;
|
||||||
|
|
||||||
if (cf->cb != NULL)
|
if (cf->closed)
|
||||||
|
file_write_finished(cf);
|
||||||
|
else if (cf->cb != NULL)
|
||||||
cf->cb(NULL, NULL, 0, -1, NULL, cf->data);
|
cf->cb(NULL, NULL, 0, -1, NULL, cf->data);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -554,15 +591,10 @@ file_write_callback(__unused struct bufferevent *bev, void *arg)
|
|||||||
|
|
||||||
log_debug("write check file %d", cf->stream);
|
log_debug("write check file %d", cf->stream);
|
||||||
|
|
||||||
if (cf->cb != NULL)
|
if (cf->closed && EVBUFFER_LENGTH(cf->event->output) == 0)
|
||||||
|
file_write_finished(cf);
|
||||||
|
else if (cf->cb != NULL)
|
||||||
cf->cb(NULL, NULL, 0, -1, NULL, cf->data);
|
cf->cb(NULL, NULL, 0, -1, NULL, cf->data);
|
||||||
|
|
||||||
if (cf->closed && EVBUFFER_LENGTH(cf->event->output) == 0) {
|
|
||||||
bufferevent_free(cf->event);
|
|
||||||
close(cf->fd);
|
|
||||||
RB_REMOVE(client_files, cf->tree, cf);
|
|
||||||
file_free(cf);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Handle a file write open message (client). */
|
/* Handle a file write open message (client). */
|
||||||
@@ -663,14 +695,10 @@ file_write_close(struct client_files *files, struct imsg *imsg)
|
|||||||
if ((cf = RB_FIND(client_files, files, &find)) == NULL)
|
if ((cf = RB_FIND(client_files, files, &find)) == NULL)
|
||||||
fatalx("unknown stream number");
|
fatalx("unknown stream number");
|
||||||
log_debug("close file %d", cf->stream);
|
log_debug("close file %d", cf->stream);
|
||||||
|
cf->closed = 1;
|
||||||
|
|
||||||
if (cf->event == NULL || EVBUFFER_LENGTH(cf->event->output) == 0) {
|
if (cf->event == NULL || EVBUFFER_LENGTH(cf->event->output) == 0) {
|
||||||
if (cf->event != NULL)
|
file_write_finished(cf);
|
||||||
bufferevent_free(cf->event);
|
|
||||||
if (cf->fd != -1)
|
|
||||||
close(cf->fd);
|
|
||||||
RB_REMOVE(client_files, files, cf);
|
|
||||||
file_free(cf);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -829,6 +857,28 @@ file_write_ready(struct client_files *files, struct imsg *imsg)
|
|||||||
return (0);
|
return (0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Handle a write done message (server). */
|
||||||
|
int
|
||||||
|
file_write_done(struct client_files *files, struct imsg *imsg)
|
||||||
|
{
|
||||||
|
struct msg_write_done *msg = imsg->data;
|
||||||
|
size_t msglen = imsg->hdr.len - IMSG_HEADER_SIZE;
|
||||||
|
struct client_file find, *cf;
|
||||||
|
|
||||||
|
if (msglen != sizeof *msg)
|
||||||
|
return (-1);
|
||||||
|
find.stream = msg->stream;
|
||||||
|
if ((cf = RB_FIND(client_files, files, &find)) == NULL)
|
||||||
|
return (0);
|
||||||
|
if (cf->c == NULL || (~cf->c->flags & CLIENT_WRITE_ACK))
|
||||||
|
return (0);
|
||||||
|
|
||||||
|
log_debug("file %d write done", cf->stream);
|
||||||
|
cf->error = msg->error;
|
||||||
|
file_fire_done(cf);
|
||||||
|
return (0);
|
||||||
|
}
|
||||||
|
|
||||||
/* Handle read data message (server). */
|
/* Handle read data message (server). */
|
||||||
int
|
int
|
||||||
file_read_data(struct client_files *files, struct imsg *imsg)
|
file_read_data(struct client_files *files, struct imsg *imsg)
|
||||||
|
|||||||
271
format.c
271
format.c
@@ -1,4 +1,4 @@
|
|||||||
/* $OpenBSD: format.c,v 1.410 2026/07/29 20:43:20 nicm Exp $ */
|
/* $OpenBSD: format.c,v 1.415 2026/08/31 19:34:09 nicm Exp $ */
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2011 Nicholas Marriott <nicholas.marriott@gmail.com>
|
* Copyright (c) 2011 Nicholas Marriott <nicholas.marriott@gmail.com>
|
||||||
@@ -806,7 +806,7 @@ format_cb_window_active_clients(struct format_tree *ft)
|
|||||||
if (client_session == NULL)
|
if (client_session == NULL)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
if (w == client_session->curw->window)
|
if (w == active_get_effective_window(loop, client_session))
|
||||||
n++;
|
n++;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -838,7 +838,7 @@ format_cb_window_active_clients_list(struct format_tree *ft)
|
|||||||
if (client_session == NULL)
|
if (client_session == NULL)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
if (w == client_session->curw->window) {
|
if (w == active_get_effective_window(loop, client_session)) {
|
||||||
if (EVBUFFER_LENGTH(buffer) > 0)
|
if (EVBUFFER_LENGTH(buffer) > 0)
|
||||||
evbuffer_add(buffer, ",", 1);
|
evbuffer_add(buffer, ",", 1);
|
||||||
evbuffer_add_printf(buffer, "%s", loop->name);
|
evbuffer_add_printf(buffer, "%s", loop->name);
|
||||||
@@ -1067,7 +1067,8 @@ format_cb_pane_fg(struct format_tree *ft)
|
|||||||
if (wp == NULL)
|
if (wp == NULL)
|
||||||
return (NULL);
|
return (NULL);
|
||||||
|
|
||||||
tty_default_colours(&gc, wp, NULL);
|
tty_default_colours(&gc, wp,
|
||||||
|
active_get_effective_pane(ft->c, wp->window), NULL);
|
||||||
return (xstrdup(colour_tostring(gc.fg)));
|
return (xstrdup(colour_tostring(gc.fg)));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1118,7 +1119,8 @@ format_cb_pane_bg(struct format_tree *ft)
|
|||||||
if (wp == NULL)
|
if (wp == NULL)
|
||||||
return (NULL);
|
return (NULL);
|
||||||
|
|
||||||
tty_default_colours(&gc, wp, NULL);
|
tty_default_colours(&gc, wp,
|
||||||
|
active_get_effective_pane(ft->c, wp->window), NULL);
|
||||||
return (xstrdup(colour_tostring(gc.bg)));
|
return (xstrdup(colour_tostring(gc.bg)));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1887,6 +1889,37 @@ format_cb_cursor_blinking(struct format_tree *ft)
|
|||||||
return (NULL);
|
return (NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Callback for history_added. */
|
||||||
|
static void *
|
||||||
|
format_cb_history_added(struct format_tree *ft)
|
||||||
|
{
|
||||||
|
if (ft->wp != NULL)
|
||||||
|
return (format_printf("%u", ft->wp->base.grid->scroll_added));
|
||||||
|
return (NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Callback for history_collected. */
|
||||||
|
static void *
|
||||||
|
format_cb_history_collected(struct format_tree *ft)
|
||||||
|
{
|
||||||
|
struct window_pane *wp = ft->wp;
|
||||||
|
|
||||||
|
if (wp != NULL)
|
||||||
|
return (format_printf("%u", wp->base.grid->scroll_collected));
|
||||||
|
return (NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Callback for history_generation. */
|
||||||
|
static void *
|
||||||
|
format_cb_history_generation(struct format_tree *ft)
|
||||||
|
{
|
||||||
|
struct window_pane *wp = ft->wp;
|
||||||
|
|
||||||
|
if (wp != NULL)
|
||||||
|
return (format_printf("%u", wp->base.grid->scroll_generation));
|
||||||
|
return (NULL);
|
||||||
|
}
|
||||||
|
|
||||||
/* Callback for history_limit. */
|
/* Callback for history_limit. */
|
||||||
static void *
|
static void *
|
||||||
format_cb_history_limit(struct format_tree *ft)
|
format_cb_history_limit(struct format_tree *ft)
|
||||||
@@ -2101,18 +2134,103 @@ format_cb_synchronized_output_flag(struct format_tree *ft)
|
|||||||
return (NULL);
|
return (NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Callback for pane_private_modes. */
|
||||||
|
static void *
|
||||||
|
format_cb_pane_private_modes(struct format_tree *ft)
|
||||||
|
{
|
||||||
|
static const struct {
|
||||||
|
int mode;
|
||||||
|
int number;
|
||||||
|
} table[] = {
|
||||||
|
{ MODE_KCURSOR, 1 }, /* DECCKM */
|
||||||
|
{ MODE_ORIGIN, 6 }, /* DECOM */
|
||||||
|
{ MODE_WRAP, 7 }, /* DECAWM */
|
||||||
|
{ MODE_CURSOR_BLINKING, 12 }, /* cursor blinking */
|
||||||
|
{ MODE_CURSOR, 25 }, /* DECTCEM */
|
||||||
|
{ MODE_MOUSE_STANDARD, 1000 }, /* mouse normal tracking */
|
||||||
|
{ MODE_MOUSE_BUTTON, 1002 }, /* mouse button tracking */
|
||||||
|
{ MODE_MOUSE_ALL, 1003 }, /* mouse any tracking */
|
||||||
|
{ MODE_FOCUSON, 1004 }, /* focus reporting */
|
||||||
|
{ MODE_MOUSE_UTF8, 1005 }, /* mouse: UTF-8 */
|
||||||
|
{ MODE_MOUSE_SGR, 1006 }, /* mouse: SGR */
|
||||||
|
{ MODE_BRACKETPASTE, 2004 }, /* bracketed paste */
|
||||||
|
{ MODE_SYNC, 2026 }, /* synchronized output */
|
||||||
|
{ MODE_THEME_UPDATES, 2031 }, /* theme update notifications */
|
||||||
|
};
|
||||||
|
int mode;
|
||||||
|
char *value = NULL, *tmp;
|
||||||
|
u_int i;
|
||||||
|
|
||||||
|
if (ft->wp == NULL)
|
||||||
|
return (NULL);
|
||||||
|
mode = ft->wp->base.mode;
|
||||||
|
|
||||||
|
for (i = 0; i < nitems(table); i++) {
|
||||||
|
if (~mode & table[i].mode)
|
||||||
|
continue;
|
||||||
|
/*
|
||||||
|
* Only report cursor blinking when set by the application, not
|
||||||
|
* when it comes from the cursor-style option.
|
||||||
|
*/
|
||||||
|
if (table[i].mode == MODE_CURSOR_BLINKING &&
|
||||||
|
(~mode & MODE_CURSOR_BLINKING_SET))
|
||||||
|
continue;
|
||||||
|
if (value == NULL)
|
||||||
|
xasprintf(&value, "%d", table[i].number);
|
||||||
|
else {
|
||||||
|
xasprintf(&tmp, "%s,%d", value, table[i].number);
|
||||||
|
free(value);
|
||||||
|
value = tmp;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (value == NULL)
|
||||||
|
return (xstrdup(""));
|
||||||
|
return (value);
|
||||||
|
}
|
||||||
|
|
||||||
/* Callback for pane_active. */
|
/* Callback for pane_active. */
|
||||||
static void *
|
static void *
|
||||||
format_cb_pane_active(struct format_tree *ft)
|
format_cb_pane_active(struct format_tree *ft)
|
||||||
{
|
{
|
||||||
|
struct window_pane *active;
|
||||||
|
|
||||||
if (ft->wp != NULL) {
|
if (ft->wp != NULL) {
|
||||||
if (ft->wp == ft->wp->window->active)
|
active = active_get_effective_pane(ft->c, ft->wp->window);
|
||||||
|
if (ft->wp == active)
|
||||||
return (xstrdup("1"));
|
return (xstrdup("1"));
|
||||||
return (xstrdup("0"));
|
return (xstrdup("0"));
|
||||||
}
|
}
|
||||||
return (NULL);
|
return (NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Callback for pane_local_active. */
|
||||||
|
static void *
|
||||||
|
format_cb_pane_local_active(struct format_tree *ft)
|
||||||
|
{
|
||||||
|
struct window_pane *active;
|
||||||
|
|
||||||
|
if (ft->wp != NULL && active_has_local_pane(ft->c, ft->wp->window)) {
|
||||||
|
active = active_get_effective_pane(ft->c, ft->wp->window);
|
||||||
|
if (ft->wp == active)
|
||||||
|
return (xstrdup("1"));
|
||||||
|
return (xstrdup("0"));
|
||||||
|
}
|
||||||
|
return (NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Callback for pane_local_mode. */
|
||||||
|
static void *
|
||||||
|
format_cb_pane_local_mode(struct format_tree *ft)
|
||||||
|
{
|
||||||
|
struct window *w = ft->w;
|
||||||
|
|
||||||
|
if (ft->wp != NULL)
|
||||||
|
w = ft->wp->window;
|
||||||
|
if (active_has_local_pane(ft->c, w))
|
||||||
|
return (xstrdup("1"));
|
||||||
|
return (xstrdup("0"));
|
||||||
|
}
|
||||||
|
|
||||||
/* Callback for pane_at_left. */
|
/* Callback for pane_at_left. */
|
||||||
static void *
|
static void *
|
||||||
format_cb_pane_at_left(struct format_tree *ft)
|
format_cb_pane_at_left(struct format_tree *ft)
|
||||||
@@ -2222,6 +2340,19 @@ format_cb_pane_last_output_time(struct format_tree *ft)
|
|||||||
return (NULL);
|
return (NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Callback for pane_output_generation. */
|
||||||
|
static void *
|
||||||
|
format_cb_pane_output_generation(struct format_tree *ft)
|
||||||
|
{
|
||||||
|
unsigned long long value;
|
||||||
|
|
||||||
|
if (ft->wp != NULL) {
|
||||||
|
value = ft->wp->output_generation;
|
||||||
|
return (format_printf("%llu", value));
|
||||||
|
}
|
||||||
|
return (NULL);
|
||||||
|
}
|
||||||
|
|
||||||
/* Callback for pane_last_prompt_time. */
|
/* Callback for pane_last_prompt_time. */
|
||||||
static void *
|
static void *
|
||||||
format_cb_pane_last_prompt_time(struct format_tree *ft)
|
format_cb_pane_last_prompt_time(struct format_tree *ft)
|
||||||
@@ -2391,7 +2522,7 @@ static void *
|
|||||||
format_cb_pane_last(struct format_tree *ft)
|
format_cb_pane_last(struct format_tree *ft)
|
||||||
{
|
{
|
||||||
if (ft->wp != NULL) {
|
if (ft->wp != NULL) {
|
||||||
if (ft->wp == TAILQ_FIRST(&ft->wp->window->last_panes))
|
if (ft->wp == active_get_last_pane(ft->c, ft->wp->window))
|
||||||
return (xstrdup("1"));
|
return (xstrdup("1"));
|
||||||
return (xstrdup("0"));
|
return (xstrdup("0"));
|
||||||
}
|
}
|
||||||
@@ -2964,26 +3095,52 @@ format_cb_sixel_support(__unused struct format_tree *ft)
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Callback for image_support. */
|
|
||||||
static void *
|
|
||||||
format_cb_image_support(__unused struct format_tree *ft)
|
|
||||||
{
|
|
||||||
#ifdef ENABLE_IMAGES
|
|
||||||
return (xstrdup("1"));
|
|
||||||
#else
|
|
||||||
return (xstrdup("0"));
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Callback for active_window_index. */
|
/* Callback for active_window_index. */
|
||||||
static void *
|
static void *
|
||||||
format_cb_active_window_index(struct format_tree *ft)
|
format_cb_active_window_index(struct format_tree *ft)
|
||||||
{
|
{
|
||||||
if (ft->s != NULL)
|
struct winlink *wl;
|
||||||
return (format_printf("%u", ft->s->curw->idx));
|
|
||||||
|
if (ft->s != NULL) {
|
||||||
|
wl = active_get_effective_winlink(ft->c, ft->s);
|
||||||
|
if (wl != NULL)
|
||||||
|
return (format_printf("%u", wl->idx));
|
||||||
|
}
|
||||||
return (NULL);
|
return (NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Callback for active_window_id. */
|
||||||
|
static void *
|
||||||
|
format_cb_active_window_id(struct format_tree *ft)
|
||||||
|
{
|
||||||
|
struct winlink *wl;
|
||||||
|
|
||||||
|
if (ft->s != NULL) {
|
||||||
|
wl = active_get_effective_winlink(ft->c, ft->s);
|
||||||
|
if (wl != NULL)
|
||||||
|
return (format_printf("@%u", wl->window->id));
|
||||||
|
}
|
||||||
|
return (NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Callback for local_active_window. */
|
||||||
|
static void *
|
||||||
|
format_cb_local_active_window(struct format_tree *ft)
|
||||||
|
{
|
||||||
|
if (ft->c != NULL && ft->s != NULL)
|
||||||
|
return (format_printf("%d", active_is_local_window(ft->c, ft->s)));
|
||||||
|
return (xstrdup("0"));
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Callback for active_window_mode. */
|
||||||
|
static void *
|
||||||
|
format_cb_active_window_mode(struct format_tree *ft)
|
||||||
|
{
|
||||||
|
if (ft->c != NULL && ft->s != NULL && active_is_local_window(ft->c, ft->s))
|
||||||
|
return (xstrdup("local"));
|
||||||
|
return (xstrdup("shared"));
|
||||||
|
}
|
||||||
|
|
||||||
/* Callback for last_window_index. */
|
/* Callback for last_window_index. */
|
||||||
static void *
|
static void *
|
||||||
format_cb_last_window_index(struct format_tree *ft)
|
format_cb_last_window_index(struct format_tree *ft)
|
||||||
@@ -3001,8 +3158,11 @@ format_cb_last_window_index(struct format_tree *ft)
|
|||||||
static void *
|
static void *
|
||||||
format_cb_window_active(struct format_tree *ft)
|
format_cb_window_active(struct format_tree *ft)
|
||||||
{
|
{
|
||||||
|
struct winlink *wl;
|
||||||
|
|
||||||
if (ft->wl != NULL) {
|
if (ft->wl != NULL) {
|
||||||
if (ft->wl == ft->wl->session->curw)
|
wl = active_get_effective_winlink(ft->c, ft->wl->session);
|
||||||
|
if (ft->wl == wl)
|
||||||
return (xstrdup("1"));
|
return (xstrdup("1"));
|
||||||
return (xstrdup("0"));
|
return (xstrdup("0"));
|
||||||
}
|
}
|
||||||
@@ -3082,7 +3242,7 @@ static void *
|
|||||||
format_cb_window_flags(struct format_tree *ft)
|
format_cb_window_flags(struct format_tree *ft)
|
||||||
{
|
{
|
||||||
if (ft->wl != NULL)
|
if (ft->wl != NULL)
|
||||||
return (xstrdup(window_printable_flags(ft->wl, 1)));
|
return (xstrdup(window_printable_flags(ft->c, ft->wl, 1)));
|
||||||
return (NULL);
|
return (NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -3269,7 +3429,7 @@ static void *
|
|||||||
format_cb_window_raw_flags(struct format_tree *ft)
|
format_cb_window_raw_flags(struct format_tree *ft)
|
||||||
{
|
{
|
||||||
if (ft->wl != NULL)
|
if (ft->wl != NULL)
|
||||||
return (xstrdup(window_printable_flags(ft->wl, 0)));
|
return (xstrdup(window_printable_flags(ft->c, ft->wl, 0)));
|
||||||
return (NULL);
|
return (NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -3479,9 +3639,15 @@ struct format_table_entry {
|
|||||||
* Only variables which are added by the caller go into the tree.
|
* Only variables which are added by the caller go into the tree.
|
||||||
*/
|
*/
|
||||||
static const struct format_table_entry format_table[] = {
|
static const struct format_table_entry format_table[] = {
|
||||||
|
{ "active_window_id", FORMAT_TABLE_STRING,
|
||||||
|
format_cb_active_window_id
|
||||||
|
},
|
||||||
{ "active_window_index", FORMAT_TABLE_STRING,
|
{ "active_window_index", FORMAT_TABLE_STRING,
|
||||||
format_cb_active_window_index
|
format_cb_active_window_index
|
||||||
},
|
},
|
||||||
|
{ "active_window_mode", FORMAT_TABLE_STRING,
|
||||||
|
format_cb_active_window_mode
|
||||||
|
},
|
||||||
{ "alternate_on", FORMAT_TABLE_STRING,
|
{ "alternate_on", FORMAT_TABLE_STRING,
|
||||||
format_cb_alternate_on
|
format_cb_alternate_on
|
||||||
},
|
},
|
||||||
@@ -3620,12 +3786,21 @@ static const struct format_table_entry format_table[] = {
|
|||||||
{ "cursor_y", FORMAT_TABLE_STRING,
|
{ "cursor_y", FORMAT_TABLE_STRING,
|
||||||
format_cb_cursor_y
|
format_cb_cursor_y
|
||||||
},
|
},
|
||||||
|
{ "history_added", FORMAT_TABLE_STRING,
|
||||||
|
format_cb_history_added
|
||||||
|
},
|
||||||
{ "history_all_bytes", FORMAT_TABLE_STRING,
|
{ "history_all_bytes", FORMAT_TABLE_STRING,
|
||||||
format_cb_history_all_bytes
|
format_cb_history_all_bytes
|
||||||
},
|
},
|
||||||
{ "history_bytes", FORMAT_TABLE_STRING,
|
{ "history_bytes", FORMAT_TABLE_STRING,
|
||||||
format_cb_history_bytes
|
format_cb_history_bytes
|
||||||
},
|
},
|
||||||
|
{ "history_collected", FORMAT_TABLE_STRING,
|
||||||
|
format_cb_history_collected
|
||||||
|
},
|
||||||
|
{ "history_generation", FORMAT_TABLE_STRING,
|
||||||
|
format_cb_history_generation
|
||||||
|
},
|
||||||
{ "history_limit", FORMAT_TABLE_STRING,
|
{ "history_limit", FORMAT_TABLE_STRING,
|
||||||
format_cb_history_limit
|
format_cb_history_limit
|
||||||
},
|
},
|
||||||
@@ -3638,9 +3813,6 @@ static const struct format_table_entry format_table[] = {
|
|||||||
{ "host_short", FORMAT_TABLE_STRING,
|
{ "host_short", FORMAT_TABLE_STRING,
|
||||||
format_cb_host_short
|
format_cb_host_short
|
||||||
},
|
},
|
||||||
{ "image_support", FORMAT_TABLE_STRING,
|
|
||||||
format_cb_image_support
|
|
||||||
},
|
|
||||||
{ "insert_flag", FORMAT_TABLE_STRING,
|
{ "insert_flag", FORMAT_TABLE_STRING,
|
||||||
format_cb_insert_flag
|
format_cb_insert_flag
|
||||||
},
|
},
|
||||||
@@ -3653,6 +3825,9 @@ static const struct format_table_entry format_table[] = {
|
|||||||
{ "last_window_index", FORMAT_TABLE_STRING,
|
{ "last_window_index", FORMAT_TABLE_STRING,
|
||||||
format_cb_last_window_index
|
format_cb_last_window_index
|
||||||
},
|
},
|
||||||
|
{ "local_active_window", FORMAT_TABLE_STRING,
|
||||||
|
format_cb_local_active_window
|
||||||
|
},
|
||||||
{ "mouse_all_flag", FORMAT_TABLE_STRING,
|
{ "mouse_all_flag", FORMAT_TABLE_STRING,
|
||||||
format_cb_mouse_all_flag
|
format_cb_mouse_all_flag
|
||||||
},
|
},
|
||||||
@@ -3797,6 +3972,12 @@ static const struct format_table_entry format_table[] = {
|
|||||||
{ "pane_left", FORMAT_TABLE_STRING,
|
{ "pane_left", FORMAT_TABLE_STRING,
|
||||||
format_cb_pane_left
|
format_cb_pane_left
|
||||||
},
|
},
|
||||||
|
{ "pane_local_active", FORMAT_TABLE_STRING,
|
||||||
|
format_cb_pane_local_active
|
||||||
|
},
|
||||||
|
{ "pane_local_mode", FORMAT_TABLE_STRING,
|
||||||
|
format_cb_pane_local_mode
|
||||||
|
},
|
||||||
{ "pane_marked", FORMAT_TABLE_STRING,
|
{ "pane_marked", FORMAT_TABLE_STRING,
|
||||||
format_cb_pane_marked
|
format_cb_pane_marked
|
||||||
},
|
},
|
||||||
@@ -3809,6 +3990,9 @@ static const struct format_table_entry format_table[] = {
|
|||||||
{ "pane_mode", FORMAT_TABLE_STRING,
|
{ "pane_mode", FORMAT_TABLE_STRING,
|
||||||
format_cb_pane_mode
|
format_cb_pane_mode
|
||||||
},
|
},
|
||||||
|
{ "pane_output_generation", FORMAT_TABLE_STRING,
|
||||||
|
format_cb_pane_output_generation
|
||||||
|
},
|
||||||
{ "pane_path", FORMAT_TABLE_STRING,
|
{ "pane_path", FORMAT_TABLE_STRING,
|
||||||
format_cb_pane_path
|
format_cb_pane_path
|
||||||
},
|
},
|
||||||
@@ -3827,6 +4011,9 @@ static const struct format_table_entry format_table[] = {
|
|||||||
{ "pane_pipe_pid", FORMAT_TABLE_STRING,
|
{ "pane_pipe_pid", FORMAT_TABLE_STRING,
|
||||||
format_cb_pane_pipe_pid
|
format_cb_pane_pipe_pid
|
||||||
},
|
},
|
||||||
|
{ "pane_private_modes", FORMAT_TABLE_STRING,
|
||||||
|
format_cb_pane_private_modes
|
||||||
|
},
|
||||||
{ "pane_right", FORMAT_TABLE_STRING,
|
{ "pane_right", FORMAT_TABLE_STRING,
|
||||||
format_cb_pane_right
|
format_cb_pane_right
|
||||||
},
|
},
|
||||||
@@ -4214,6 +4401,8 @@ format_log_debug_cb(const char *key, const char *value, void *arg)
|
|||||||
void
|
void
|
||||||
format_log_debug(struct format_tree *ft, const char *prefix)
|
format_log_debug(struct format_tree *ft, const char *prefix)
|
||||||
{
|
{
|
||||||
|
if (log_get_level() == 0)
|
||||||
|
return;
|
||||||
format_each(ft, format_log_debug_cb, (void *)prefix);
|
format_each(ft, format_log_debug_cb, (void *)prefix);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -4342,7 +4531,7 @@ format_quote_shell(const char *s)
|
|||||||
|
|
||||||
at = out = xmalloc(strlen(s) * 2 + 1);
|
at = out = xmalloc(strlen(s) * 2 + 1);
|
||||||
for (cp = s; *cp != '\0'; cp++) {
|
for (cp = s; *cp != '\0'; cp++) {
|
||||||
if (strchr("|&;<>()$`\\\"'*?[# =%\n\t", *cp) != NULL)
|
if (strchr("|&;<>(){}$`\\\"'*?[# =%\n\t", *cp) != NULL)
|
||||||
*at++ = '\\';
|
*at++ = '\\';
|
||||||
*at++ = *cp;
|
*at++ = *cp;
|
||||||
}
|
}
|
||||||
@@ -5194,15 +5383,18 @@ format_add_window_neighbour(struct format_tree *nft, struct winlink *wl,
|
|||||||
struct session *s, const char *prefix)
|
struct session *s, const char *prefix)
|
||||||
{
|
{
|
||||||
struct options_entry *o;
|
struct options_entry *o;
|
||||||
|
struct winlink *current;
|
||||||
const char *oname;
|
const char *oname;
|
||||||
char *key, *prefixed, *oval;
|
char *key, *prefixed, *oval;
|
||||||
|
|
||||||
|
current = active_get_effective_winlink(nft->c, s);
|
||||||
|
|
||||||
xasprintf(&key, "%s_window_index", prefix);
|
xasprintf(&key, "%s_window_index", prefix);
|
||||||
format_add(nft, key, "%u", wl->idx);
|
format_add(nft, key, "%u", wl->idx);
|
||||||
free(key);
|
free(key);
|
||||||
|
|
||||||
xasprintf(&key, "%s_window_active", prefix);
|
xasprintf(&key, "%s_window_active", prefix);
|
||||||
format_add(nft, key, "%d", wl == s->curw);
|
format_add(nft, key, "%d", wl == current);
|
||||||
free(key);
|
free(key);
|
||||||
|
|
||||||
o = options_first(wl->window->options);
|
o = options_first(wl->window->options);
|
||||||
@@ -5230,10 +5422,10 @@ format_loop_windows(struct format_expand_state *es, const char *fmt)
|
|||||||
struct cmdq_item *item = ft->item;
|
struct cmdq_item *item = ft->item;
|
||||||
struct format_tree *nft;
|
struct format_tree *nft;
|
||||||
struct format_expand_state next;
|
struct format_expand_state next;
|
||||||
char *all, *active, *use, *expanded, *value;
|
char *all, *active_fmt, *use, *expanded, *value;
|
||||||
struct evbuffer *buffer;
|
struct evbuffer *buffer;
|
||||||
size_t size;
|
size_t size;
|
||||||
struct winlink *wl, **l;
|
struct winlink *wl, **l, *active_wl;
|
||||||
struct window *w;
|
struct window *w;
|
||||||
int i, n;
|
int i, n;
|
||||||
|
|
||||||
@@ -5242,9 +5434,9 @@ format_loop_windows(struct format_expand_state *es, const char *fmt)
|
|||||||
return (NULL);
|
return (NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (format_choose(es, fmt, &all, &active, 0) != 0) {
|
if (format_choose(es, fmt, &all, &active_fmt, 0) != 0) {
|
||||||
all = xstrdup(fmt);
|
all = xstrdup(fmt);
|
||||||
active = NULL;
|
active_fmt = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
buffer = evbuffer_new();
|
buffer = evbuffer_new();
|
||||||
@@ -5252,12 +5444,13 @@ format_loop_windows(struct format_expand_state *es, const char *fmt)
|
|||||||
fatalx("out of memory");
|
fatalx("out of memory");
|
||||||
|
|
||||||
l = sort_get_winlinks_session(s, &n, sc);
|
l = sort_get_winlinks_session(s, &n, sc);
|
||||||
|
active_wl = active_get_effective_winlink(ft->c, s);
|
||||||
for (i = 0; i < n; i++) {
|
for (i = 0; i < n; i++) {
|
||||||
wl = l[i];
|
wl = l[i];
|
||||||
w = wl->window;
|
w = wl->window;
|
||||||
format_log(es, "window loop: %u @%u", wl->idx, w->id);
|
format_log(es, "window loop: %u @%u", wl->idx, w->id);
|
||||||
if (active != NULL && wl == s->curw)
|
if (active_fmt != NULL && wl == active_wl)
|
||||||
use = active;
|
use = active_fmt;
|
||||||
else
|
else
|
||||||
use = all;
|
use = all;
|
||||||
nft = format_create(c, item, FORMAT_WINDOW|w->id, ft->flags);
|
nft = format_create(c, item, FORMAT_WINDOW|w->id, ft->flags);
|
||||||
@@ -5266,11 +5459,11 @@ format_loop_windows(struct format_expand_state *es, const char *fmt)
|
|||||||
format_add(nft, "loop_last_flag", "%d", i == n - 1);
|
format_add(nft, "loop_last_flag", "%d", i == n - 1);
|
||||||
|
|
||||||
/* Add neighbour window data to the format tree. */
|
/* Add neighbour window data to the format tree. */
|
||||||
if (i > 0 && l[i - 1] == s->curw)
|
if (i > 0 && l[i - 1] == active_wl)
|
||||||
format_add(nft, "window_after_active", "1");
|
format_add(nft, "window_after_active", "1");
|
||||||
else
|
else
|
||||||
format_add(nft, "window_after_active", "0");
|
format_add(nft, "window_after_active", "0");
|
||||||
if (i + 1 < n && l[i + 1] == s->curw)
|
if (i + 1 < n && l[i + 1] == active_wl)
|
||||||
format_add(nft, "window_before_active", "1");
|
format_add(nft, "window_before_active", "1");
|
||||||
else
|
else
|
||||||
format_add(nft, "window_before_active", "0");
|
format_add(nft, "window_before_active", "0");
|
||||||
@@ -5290,7 +5483,7 @@ format_loop_windows(struct format_expand_state *es, const char *fmt)
|
|||||||
free(expanded);
|
free(expanded);
|
||||||
}
|
}
|
||||||
|
|
||||||
free(active);
|
free(active_fmt);
|
||||||
free(all);
|
free(all);
|
||||||
|
|
||||||
if ((size = EVBUFFER_LENGTH(buffer)) != 0)
|
if ((size = EVBUFFER_LENGTH(buffer)) != 0)
|
||||||
@@ -6878,9 +7071,9 @@ format_defaults(struct format_tree *ft, struct client *c, struct session *s,
|
|||||||
if (s == NULL && c != NULL)
|
if (s == NULL && c != NULL)
|
||||||
s = c->session;
|
s = c->session;
|
||||||
if (wl == NULL && s != NULL)
|
if (wl == NULL && s != NULL)
|
||||||
wl = s->curw;
|
wl = active_get_effective_winlink(c, s);
|
||||||
if (wp == NULL && wl != NULL)
|
if (wp == NULL && wl != NULL)
|
||||||
wp = wl->window->active;
|
wp = active_get_effective_pane(c, wl->window);
|
||||||
|
|
||||||
if (c != NULL)
|
if (c != NULL)
|
||||||
format_defaults_client(ft, c);
|
format_defaults_client(ft, c);
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
/* $OpenBSD: grid-view.c,v 1.38 2026/01/22 08:55:01 nicm Exp $ */
|
/* $OpenBSD: grid-view.c,v 1.39 2026/08/03 12:58:53 nicm Exp $ */
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2008 Nicholas Marriott <nicholas.marriott@gmail.com>
|
* Copyright (c) 2008 Nicholas Marriott <nicholas.marriott@gmail.com>
|
||||||
@@ -47,9 +47,9 @@ grid_view_set_cell(struct grid *gd, u_int px, u_int py,
|
|||||||
|
|
||||||
/* Set padding. */
|
/* Set padding. */
|
||||||
void
|
void
|
||||||
grid_view_set_padding(struct grid *gd, u_int px, u_int py)
|
grid_view_set_padding(struct grid *gd, u_int px, u_int py, int bg)
|
||||||
{
|
{
|
||||||
grid_set_padding(gd, grid_view_x(gd, px), grid_view_y(gd, py));
|
grid_set_padding(gd, grid_view_x(gd, px), grid_view_y(gd, py), bg);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Set cells. */
|
/* Set cells. */
|
||||||
|
|||||||
231
grid.c
231
grid.c
@@ -1,4 +1,4 @@
|
|||||||
/* $OpenBSD: grid.c,v 1.155 2026/07/29 17:42:56 nicm Exp $ */
|
/* $OpenBSD: grid.c,v 1.158 2026/09/01 12:49:49 nicm Exp $ */
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2008 Nicholas Marriott <nicholas.marriott@gmail.com>
|
* Copyright (c) 2008 Nicholas Marriott <nicholas.marriott@gmail.com>
|
||||||
@@ -41,9 +41,6 @@
|
|||||||
/* Default grid cell data. */
|
/* Default grid cell data. */
|
||||||
const struct grid_cell grid_default_cell = {
|
const struct grid_cell grid_default_cell = {
|
||||||
{ { ' ' }, 0, 1, 1 }, 0, 0, 8, 8, 8, 0
|
{ { ' ' }, 0, 1, 1 }, 0, 0, 8, 8, 8, 0
|
||||||
#ifdef ENABLE_IMAGES
|
|
||||||
, 0, 0, 0
|
|
||||||
#endif
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -52,38 +49,16 @@ const struct grid_cell grid_default_cell = {
|
|||||||
*/
|
*/
|
||||||
static const struct grid_cell grid_padding_cell = {
|
static const struct grid_cell grid_padding_cell = {
|
||||||
{ { '!' }, 0, 0, 0 }, 0, GRID_FLAG_PADDING, 8, 8, 8, 0
|
{ { '!' }, 0, 0, 0 }, 0, GRID_FLAG_PADDING, 8, 8, 8, 0
|
||||||
#ifdef ENABLE_IMAGES
|
|
||||||
, 0, 0, 0
|
|
||||||
#endif
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/* Cleared grid cell data. */
|
/* Cleared grid cell data. */
|
||||||
static const struct grid_cell grid_cleared_cell = {
|
static const struct grid_cell grid_cleared_cell = {
|
||||||
{ { ' ' }, 0, 1, 1 }, 0, GRID_FLAG_CLEARED, 8, 8, 8, 0
|
{ { ' ' }, 0, 1, 1 }, 0, GRID_FLAG_CLEARED, 8, 8, 8, 0
|
||||||
#ifdef ENABLE_IMAGES
|
|
||||||
, 0, 0, 0
|
|
||||||
#endif
|
|
||||||
};
|
};
|
||||||
static const struct grid_cell_entry grid_cleared_entry = {
|
static const struct grid_cell_entry grid_cleared_entry = {
|
||||||
{ .data = { 0, 8, 8, ' ' } }, GRID_FLAG_CLEARED
|
{ .data = { 0, 8, 8, ' ' } }, GRID_FLAG_CLEARED
|
||||||
};
|
};
|
||||||
|
|
||||||
#ifdef ENABLE_IMAGES
|
|
||||||
/* Return the image referenced by a stored cell, or zero. */
|
|
||||||
static u_int
|
|
||||||
grid_entry_image(struct grid_line *gl, struct grid_cell_entry *gce)
|
|
||||||
{
|
|
||||||
struct grid_extd_entry *gee;
|
|
||||||
|
|
||||||
if (~gce->flags & GRID_FLAG_EXTENDED || gce->offset >= gl->extdsize)
|
|
||||||
return (0);
|
|
||||||
gee = &gl->extddata[gce->offset];
|
|
||||||
if (~gee->flags & GRID_FLAG_IMAGE)
|
|
||||||
return (0);
|
|
||||||
return (image_get_id_by_grid_id(gee->image_id));
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifdef __APPLE__
|
#ifdef __APPLE__
|
||||||
void
|
void
|
||||||
grid_check_is_clear(struct grid *gd)
|
grid_check_is_clear(struct grid *gd)
|
||||||
@@ -159,10 +134,6 @@ grid_need_extended_cell(const struct grid_cell_entry *gce,
|
|||||||
return (1);
|
return (1);
|
||||||
if (gc->flags & GRID_FLAG_TAB)
|
if (gc->flags & GRID_FLAG_TAB)
|
||||||
return (1);
|
return (1);
|
||||||
#ifdef ENABLE_IMAGES
|
|
||||||
if (gc->flags & GRID_FLAG_IMAGE)
|
|
||||||
return (1);
|
|
||||||
#endif
|
|
||||||
return (0);
|
return (0);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -210,11 +181,6 @@ grid_extended_cell(struct grid_line *gl, struct grid_cell_entry *gce,
|
|||||||
gee->bg = gc->bg;
|
gee->bg = gc->bg;
|
||||||
gee->us = gc->us;
|
gee->us = gc->us;
|
||||||
gee->link = gc->link;
|
gee->link = gc->link;
|
||||||
#ifdef ENABLE_IMAGES
|
|
||||||
gee->image_id = image_get_grid_id(gc->image_id);
|
|
||||||
gee->image_x = gc->image_x;
|
|
||||||
gee->image_y = gc->image_y;
|
|
||||||
#endif
|
|
||||||
return (gee);
|
return (gee);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -302,12 +268,6 @@ grid_clear_cell(struct grid *gd, u_int px, u_int py, u_int bg, int moved)
|
|||||||
struct grid_extd_entry *gee;
|
struct grid_extd_entry *gee;
|
||||||
u_int old_offset = gce->offset;
|
u_int old_offset = gce->offset;
|
||||||
int had_extd = (gce->flags & GRID_FLAG_EXTENDED);
|
int had_extd = (gce->flags & GRID_FLAG_EXTENDED);
|
||||||
#ifdef ENABLE_IMAGES
|
|
||||||
u_int image_id = grid_entry_image(gl, gce);
|
|
||||||
|
|
||||||
if (!moved && image_id != 0)
|
|
||||||
image_free(image_id);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
memcpy(gce, &grid_cleared_entry, sizeof *gce);
|
memcpy(gce, &grid_cleared_entry, sizeof *gce);
|
||||||
if (!moved && had_extd && old_offset < gl->extdsize) {
|
if (!moved && had_extd && old_offset < gl->extdsize) {
|
||||||
@@ -354,13 +314,6 @@ grid_cells_look_equal(const struct grid_cell *gc1, const struct grid_cell *gc2)
|
|||||||
return (0);
|
return (0);
|
||||||
if (gc1->link != gc2->link)
|
if (gc1->link != gc2->link)
|
||||||
return (0);
|
return (0);
|
||||||
#ifdef ENABLE_IMAGES
|
|
||||||
if ((gc1->flags & GRID_FLAG_IMAGE) &&
|
|
||||||
(gc1->image_id != gc2->image_id ||
|
|
||||||
gc1->image_x != gc2->image_x ||
|
|
||||||
gc1->image_y != gc2->image_y))
|
|
||||||
return (0);
|
|
||||||
#endif
|
|
||||||
return (1);
|
return (1);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -393,10 +346,6 @@ static void
|
|||||||
grid_free_line(struct grid *gd, u_int py)
|
grid_free_line(struct grid *gd, u_int py)
|
||||||
{
|
{
|
||||||
struct grid_line *gl = &gd->linedata[py];
|
struct grid_line *gl = &gd->linedata[py];
|
||||||
#ifdef ENABLE_IMAGES
|
|
||||||
struct grid_cell_entry *gce;
|
|
||||||
u_int image_id, px;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifdef __APPLE__
|
#ifdef __APPLE__
|
||||||
assert(gl->cellused <= gl->cellsize);
|
assert(gl->cellused <= gl->cellsize);
|
||||||
@@ -404,14 +353,6 @@ grid_free_line(struct grid *gd, u_int py)
|
|||||||
assert(gl->cellsize == 0 || gl->celldata != NULL);
|
assert(gl->cellsize == 0 || gl->celldata != NULL);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef ENABLE_IMAGES
|
|
||||||
for (px = 0; px < gl->cellsize; px++) {
|
|
||||||
gce = &gl->celldata[px];
|
|
||||||
image_id = grid_entry_image(gl, gce);
|
|
||||||
if (image_id != 0)
|
|
||||||
image_free(image_id);
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
free(gl->celldata);
|
free(gl->celldata);
|
||||||
free(gl->extddata);
|
free(gl->extddata);
|
||||||
memset(gl, 0, sizeof *gl);
|
memset(gl, 0, sizeof *gl);
|
||||||
@@ -683,11 +624,6 @@ grid_get_cell1(struct grid_line *gl, u_int px, struct grid_cell *gc)
|
|||||||
gc->bg = gee->bg;
|
gc->bg = gee->bg;
|
||||||
gc->us = gee->us;
|
gc->us = gee->us;
|
||||||
gc->link = gee->link;
|
gc->link = gee->link;
|
||||||
#ifdef ENABLE_IMAGES
|
|
||||||
gc->image_id = image_get_id_by_grid_id(gee->image_id);
|
|
||||||
gc->image_x = gee->image_x;
|
|
||||||
gc->image_y = gee->image_y;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
if (gc->flags & GRID_FLAG_TAB)
|
if (gc->flags & GRID_FLAG_TAB)
|
||||||
grid_set_tab(gc, gee->data);
|
grid_set_tab(gc, gee->data);
|
||||||
@@ -708,11 +644,6 @@ grid_get_cell1(struct grid_line *gl, u_int px, struct grid_cell *gc)
|
|||||||
gc->us = 8;
|
gc->us = 8;
|
||||||
utf8_set(&gc->data, gce->data.data);
|
utf8_set(&gc->data, gce->data.data);
|
||||||
gc->link = 0;
|
gc->link = 0;
|
||||||
#ifdef ENABLE_IMAGES
|
|
||||||
gc->image_id = 0;
|
|
||||||
gc->image_x = 0;
|
|
||||||
gc->image_y = 0;
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Get cell for reading. */
|
/* Get cell for reading. */
|
||||||
@@ -732,9 +663,6 @@ grid_set_cell(struct grid *gd, u_int px, u_int py, const struct grid_cell *gc)
|
|||||||
{
|
{
|
||||||
struct grid_line *gl;
|
struct grid_line *gl;
|
||||||
struct grid_cell_entry *gce;
|
struct grid_cell_entry *gce;
|
||||||
#ifdef ENABLE_IMAGES
|
|
||||||
u_int old_id, new_id = 0;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
if (grid_check_y(gd, __func__, py) != 0)
|
if (grid_check_y(gd, __func__, py) != 0)
|
||||||
return;
|
return;
|
||||||
@@ -746,17 +674,6 @@ grid_set_cell(struct grid *gd, u_int px, u_int py, const struct grid_cell *gc)
|
|||||||
gl->cellused = px + 1;
|
gl->cellused = px + 1;
|
||||||
|
|
||||||
gce = &gl->celldata[px];
|
gce = &gl->celldata[px];
|
||||||
#ifdef ENABLE_IMAGES
|
|
||||||
old_id = grid_entry_image(gl, gce);
|
|
||||||
if (gc->flags & GRID_FLAG_IMAGE)
|
|
||||||
new_id = gc->image_id;
|
|
||||||
if (old_id != new_id) {
|
|
||||||
if (old_id != 0)
|
|
||||||
image_free(old_id);
|
|
||||||
if (new_id != 0)
|
|
||||||
image_ref(new_id);
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
if (grid_need_extended_cell(gce, gc))
|
if (grid_need_extended_cell(gce, gc))
|
||||||
grid_extended_cell(gl, gce, gc);
|
grid_extended_cell(gl, gce, gc);
|
||||||
else
|
else
|
||||||
@@ -765,9 +682,13 @@ grid_set_cell(struct grid *gd, u_int px, u_int py, const struct grid_cell *gc)
|
|||||||
|
|
||||||
/* Set padding at position. */
|
/* Set padding at position. */
|
||||||
void
|
void
|
||||||
grid_set_padding(struct grid *gd, u_int px, u_int py)
|
grid_set_padding(struct grid *gd, u_int px, u_int py, int bg)
|
||||||
{
|
{
|
||||||
grid_set_cell(gd, px, py, &grid_padding_cell);
|
struct grid_cell gc;
|
||||||
|
|
||||||
|
memcpy(&gc, &grid_padding_cell, sizeof gc);
|
||||||
|
gc.bg = bg;
|
||||||
|
grid_set_cell(gd, px, py, &gc);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Set cells at position. */
|
/* Set cells at position. */
|
||||||
@@ -778,10 +699,6 @@ grid_set_cells(struct grid *gd, u_int px, u_int py, const struct grid_cell *gc,
|
|||||||
struct grid_line *gl;
|
struct grid_line *gl;
|
||||||
struct grid_cell_entry *gce;
|
struct grid_cell_entry *gce;
|
||||||
struct grid_extd_entry *gee;
|
struct grid_extd_entry *gee;
|
||||||
const struct grid_cell *new_gc;
|
|
||||||
#ifdef ENABLE_IMAGES
|
|
||||||
struct grid_cell old_gc, image_gc;
|
|
||||||
#endif
|
|
||||||
u_int i;
|
u_int i;
|
||||||
|
|
||||||
if (grid_check_y(gd, __func__, py) != 0)
|
if (grid_check_y(gd, __func__, py) != 0)
|
||||||
@@ -795,39 +712,11 @@ grid_set_cells(struct grid *gd, u_int px, u_int py, const struct grid_cell *gc,
|
|||||||
|
|
||||||
for (i = 0; i < slen; i++) {
|
for (i = 0; i < slen; i++) {
|
||||||
gce = &gl->celldata[px + i];
|
gce = &gl->celldata[px + i];
|
||||||
new_gc = gc;
|
if (grid_need_extended_cell(gce, gc)) {
|
||||||
#ifdef ENABLE_IMAGES
|
gee = grid_extended_cell(gl, gce, gc);
|
||||||
{
|
|
||||||
u_int old_id = grid_entry_image(gl, gce);
|
|
||||||
u_int new_id;
|
|
||||||
|
|
||||||
if (old_id != 0 && (~gc->flags & GRID_FLAG_IMAGE)) {
|
|
||||||
grid_get_cell1(gl, px + i, &old_gc);
|
|
||||||
memcpy(&image_gc, gc, sizeof image_gc);
|
|
||||||
image_gc.flags |= GRID_FLAG_IMAGE|GRID_FLAG_IMAGE_DAMAGED;
|
|
||||||
image_gc.image_id = old_gc.image_id;
|
|
||||||
image_gc.image_x = old_gc.image_x;
|
|
||||||
image_gc.image_y = old_gc.image_y;
|
|
||||||
new_gc = &image_gc;
|
|
||||||
}
|
|
||||||
if (new_gc->flags & GRID_FLAG_IMAGE)
|
|
||||||
new_id = new_gc->image_id;
|
|
||||||
else
|
|
||||||
new_id = 0;
|
|
||||||
|
|
||||||
if (old_id != new_id) {
|
|
||||||
if (old_id != 0)
|
|
||||||
image_free(old_id);
|
|
||||||
if (new_id != 0)
|
|
||||||
image_ref(new_id);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
if (grid_need_extended_cell(gce, new_gc)) {
|
|
||||||
gee = grid_extended_cell(gl, gce, new_gc);
|
|
||||||
gee->data = utf8_build_one(s[i]);
|
gee->data = utf8_build_one(s[i]);
|
||||||
} else
|
} else
|
||||||
grid_store_cell(gce, new_gc, s[i]);
|
grid_store_cell(gce, gc, s[i]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -942,9 +831,6 @@ grid_move_cells(struct grid *gd, u_int dx, u_int px, u_int py, u_int nx,
|
|||||||
{
|
{
|
||||||
struct grid_line *gl;
|
struct grid_line *gl;
|
||||||
u_int xx;
|
u_int xx;
|
||||||
#ifdef ENABLE_IMAGES
|
|
||||||
u_int image_id;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
if (nx == 0 || px == dx)
|
if (nx == 0 || px == dx)
|
||||||
return;
|
return;
|
||||||
@@ -955,16 +841,6 @@ grid_move_cells(struct grid *gd, u_int dx, u_int px, u_int py, u_int nx,
|
|||||||
|
|
||||||
grid_expand_line(gd, py, px + nx, 8);
|
grid_expand_line(gd, py, px + nx, 8);
|
||||||
grid_expand_line(gd, py, dx + nx, 8);
|
grid_expand_line(gd, py, dx + nx, 8);
|
||||||
#ifdef ENABLE_IMAGES
|
|
||||||
/* References in the source range are transferred, not duplicated. */
|
|
||||||
for (xx = dx; xx < dx + nx; xx++) {
|
|
||||||
if (xx >= px && xx < px + nx)
|
|
||||||
continue;
|
|
||||||
image_id = grid_entry_image(gl, &gl->celldata[xx]);
|
|
||||||
if (image_id != 0)
|
|
||||||
image_free(image_id);
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
memmove(&gl->celldata[dx], &gl->celldata[px],
|
memmove(&gl->celldata[dx], &gl->celldata[px],
|
||||||
nx * sizeof *gl->celldata);
|
nx * sizeof *gl->celldata);
|
||||||
if (dx + nx > gl->cellused)
|
if (dx + nx > gl->cellused)
|
||||||
@@ -1337,13 +1213,6 @@ grid_string_cells(struct grid *gd, u_int px, u_int py, u_int nx,
|
|||||||
grid_get_cell(gd, xx, py, &gc);
|
grid_get_cell(gd, xx, py, &gc);
|
||||||
if (gc.flags & GRID_FLAG_PADDING)
|
if (gc.flags & GRID_FLAG_PADDING)
|
||||||
continue;
|
continue;
|
||||||
#ifdef ENABLE_IMAGES
|
|
||||||
if (gc.flags & GRID_FLAG_IMAGE) {
|
|
||||||
utf8_set(&gc.data, ' ');
|
|
||||||
gc.flags &= ~(GRID_FLAG_IMAGE|GRID_FLAG_IMAGE_DAMAGED);
|
|
||||||
gc.image_id = gc.image_x = gc.image_y = 0;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
if (lastgc != NULL && (flags & GRID_STRING_WITH_SEQUENCES)) {
|
if (lastgc != NULL && (flags & GRID_STRING_WITH_SEQUENCES)) {
|
||||||
grid_string_cells_code(*lastgc, &gc, code, sizeof code,
|
grid_string_cells_code(*lastgc, &gc, code, sizeof code,
|
||||||
@@ -1411,9 +1280,6 @@ grid_duplicate_lines(struct grid *dst, u_int dy, struct grid *src, u_int sy,
|
|||||||
{
|
{
|
||||||
struct grid_line *dstl, *srcl;
|
struct grid_line *dstl, *srcl;
|
||||||
u_int yy;
|
u_int yy;
|
||||||
#ifdef ENABLE_IMAGES
|
|
||||||
u_int px, image_id;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
if (dy + ny > dst->hsize + dst->sy)
|
if (dy + ny > dst->hsize + dst->sy)
|
||||||
ny = dst->hsize + dst->sy - dy;
|
ny = dst->hsize + dst->sy - dy;
|
||||||
@@ -1442,13 +1308,6 @@ grid_duplicate_lines(struct grid *dst, u_int dy, struct grid *src, u_int sy,
|
|||||||
} else
|
} else
|
||||||
dstl->extddata = NULL;
|
dstl->extddata = NULL;
|
||||||
|
|
||||||
#ifdef ENABLE_IMAGES
|
|
||||||
for (px = 0; px < dstl->cellsize; px++) {
|
|
||||||
image_id = grid_entry_image(dstl, &dstl->celldata[px]);
|
|
||||||
if (image_id != 0)
|
|
||||||
image_ref(image_id);
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
sy++;
|
sy++;
|
||||||
dy++;
|
dy++;
|
||||||
}
|
}
|
||||||
@@ -1462,30 +1321,6 @@ grid_reflow_dead(struct grid_line *gl)
|
|||||||
gl->flags = GRID_LINE_DEAD;
|
gl->flags = GRID_LINE_DEAD;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Image rows are cell-aligned and must not be reflowed like text. */
|
|
||||||
static int
|
|
||||||
grid_reflow_has_image(struct grid_line *gl)
|
|
||||||
{
|
|
||||||
struct grid_cell gc;
|
|
||||||
u_int i;
|
|
||||||
|
|
||||||
if (~gl->flags & GRID_LINE_EXTENDED)
|
|
||||||
return (0);
|
|
||||||
for (i = 0; i < gl->cellused; i++) {
|
|
||||||
grid_get_cell1(gl, i, &gc);
|
|
||||||
#ifdef ENABLE_IMAGES
|
|
||||||
if (gc.flags & GRID_FLAG_IMAGE)
|
|
||||||
return (1);
|
|
||||||
#endif
|
|
||||||
/* Kitty Unicode placeholder base character (U+10EEEE). */
|
|
||||||
if (gc.data.size >= 4 && gc.data.data[0] == 0xf4 &&
|
|
||||||
gc.data.data[1] == 0x8e && gc.data.data[2] == 0xbb &&
|
|
||||||
gc.data.data[3] == 0xae)
|
|
||||||
return (1);
|
|
||||||
}
|
|
||||||
return (0);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Add lines, return the first new one. */
|
/* Add lines, return the first new one. */
|
||||||
static struct grid_line *
|
static struct grid_line *
|
||||||
grid_reflow_add(struct grid *gd, u_int n)
|
grid_reflow_add(struct grid *gd, u_int n)
|
||||||
@@ -1548,10 +1383,6 @@ grid_reflow_join(struct grid *target, struct grid *gd, u_int sx, u_int yy,
|
|||||||
break;
|
break;
|
||||||
line = yy + 1 + lines;
|
line = yy + 1 + lines;
|
||||||
|
|
||||||
/* Do not join wrapped text to a cell-aligned image row. */
|
|
||||||
if (grid_reflow_has_image(&gd->linedata[line]))
|
|
||||||
break;
|
|
||||||
|
|
||||||
/* If the next line is empty, skip it. */
|
/* If the next line is empty, skip it. */
|
||||||
if (~gd->linedata[line].flags & GRID_LINE_WRAPPED)
|
if (~gd->linedata[line].flags & GRID_LINE_WRAPPED)
|
||||||
wrapped = 0;
|
wrapped = 0;
|
||||||
@@ -1612,7 +1443,8 @@ grid_reflow_join(struct grid *target, struct grid *gd, u_int sx, u_int yy,
|
|||||||
|
|
||||||
/* Remove the lines that were completely consumed. */
|
/* Remove the lines that were completely consumed. */
|
||||||
for (i = yy + 1; i < yy + 1 + lines; i++) {
|
for (i = yy + 1; i < yy + 1 + lines; i++) {
|
||||||
grid_free_line(gd, i);
|
free(gd->linedata[i].celldata);
|
||||||
|
free(gd->linedata[i].extddata);
|
||||||
grid_reflow_dead(&gd->linedata[i]);
|
grid_reflow_dead(&gd->linedata[i]);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1668,15 +1500,6 @@ grid_reflow_split(struct grid *target, struct grid *gd, u_int sx, u_int yy,
|
|||||||
}
|
}
|
||||||
width += gc.data.width;
|
width += gc.data.width;
|
||||||
grid_set_cell(target, xx, line, &gc);
|
grid_set_cell(target, xx, line, &gc);
|
||||||
#ifdef ENABLE_IMAGES
|
|
||||||
/*
|
|
||||||
* The tail of the original line is discarded below. Transfer
|
|
||||||
* its image reference to the new cell rather than duplicating
|
|
||||||
* it.
|
|
||||||
*/
|
|
||||||
if (gc.flags & GRID_FLAG_IMAGE)
|
|
||||||
image_free(gc.image_id);
|
|
||||||
#endif
|
|
||||||
xx++;
|
xx++;
|
||||||
}
|
}
|
||||||
if (flags & GRID_LINE_WRAPPED)
|
if (flags & GRID_LINE_WRAPPED)
|
||||||
@@ -1723,12 +1546,6 @@ grid_reflow(struct grid *gd, u_int sx)
|
|||||||
if (gl->flags & GRID_LINE_DEAD)
|
if (gl->flags & GRID_LINE_DEAD)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
/* Keep image markers at their original cell coordinates. */
|
|
||||||
if (grid_reflow_has_image(gl)) {
|
|
||||||
grid_reflow_move(target, gl);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Work out the width of this line. at is the point at which
|
* Work out the width of this line. at is the point at which
|
||||||
* the available width is hit, and width is the full line
|
* the available width is hit, and width is the full line
|
||||||
@@ -1818,7 +1635,7 @@ grid_wrap_position(struct grid *gd, u_int px, u_int py, u_int *wx, u_int *wy)
|
|||||||
void
|
void
|
||||||
grid_unwrap_position(struct grid *gd, u_int *px, u_int *py, u_int wx, u_int wy)
|
grid_unwrap_position(struct grid *gd, u_int *px, u_int *py, u_int wx, u_int wy)
|
||||||
{
|
{
|
||||||
u_int yy, ay = 0;
|
u_int yy, ay = 0, ey = gd->hsize + gd->sy - 1;
|
||||||
|
|
||||||
for (yy = 0; yy < gd->hsize + gd->sy - 1; yy++) {
|
for (yy = 0; yy < gd->hsize + gd->sy - 1; yy++) {
|
||||||
if (ay == wy)
|
if (ay == wy)
|
||||||
@@ -1832,7 +1649,7 @@ grid_unwrap_position(struct grid *gd, u_int *px, u_int *py, u_int wx, u_int wy)
|
|||||||
* until we find the end or the line now containing wx.
|
* until we find the end or the line now containing wx.
|
||||||
*/
|
*/
|
||||||
if (wx == UINT_MAX) {
|
if (wx == UINT_MAX) {
|
||||||
while (gd->linedata[yy].flags & GRID_LINE_WRAPPED)
|
while (yy < ey && gd->linedata[yy].flags & GRID_LINE_WRAPPED)
|
||||||
yy++;
|
yy++;
|
||||||
wx = gd->linedata[yy].cellused;
|
wx = gd->linedata[yy].cellused;
|
||||||
} else {
|
} else {
|
||||||
@@ -1894,21 +1711,29 @@ grid_in_set(struct grid *gd, u_int px, u_int py, const char *set)
|
|||||||
{
|
{
|
||||||
struct grid_cell gc, tmp_gc;
|
struct grid_cell gc, tmp_gc;
|
||||||
u_int pxx;
|
u_int pxx;
|
||||||
|
int has_tab, has_space;
|
||||||
|
|
||||||
|
has_tab = (strchr(set, '\t') != NULL);
|
||||||
|
has_space = (strchr(set, ' ') != NULL);
|
||||||
|
|
||||||
grid_get_cell(gd, px, py, &gc);
|
grid_get_cell(gd, px, py, &gc);
|
||||||
if (strchr(set, '\t')) {
|
|
||||||
if (gc.flags & GRID_FLAG_PADDING) {
|
if (gc.flags & GRID_FLAG_PADDING) {
|
||||||
|
if (!has_tab && !has_space)
|
||||||
|
return (0);
|
||||||
pxx = px;
|
pxx = px;
|
||||||
do
|
do
|
||||||
grid_get_cell(gd, --pxx, py, &tmp_gc);
|
grid_get_cell(gd, --pxx, py, &tmp_gc);
|
||||||
while (pxx > 0 && tmp_gc.flags & GRID_FLAG_PADDING);
|
while (pxx > 0 && tmp_gc.flags & GRID_FLAG_PADDING);
|
||||||
if (tmp_gc.flags & GRID_FLAG_TAB)
|
if (((has_tab || has_space) &&
|
||||||
|
(tmp_gc.flags & GRID_FLAG_TAB)) ||
|
||||||
|
(has_space && utf8_has_whitespace(&tmp_gc.data)))
|
||||||
return (tmp_gc.data.width - (px - pxx));
|
return (tmp_gc.data.width - (px - pxx));
|
||||||
} else if (gc.flags & GRID_FLAG_TAB)
|
|
||||||
return (gc.data.width);
|
|
||||||
}
|
|
||||||
if (gc.flags & GRID_FLAG_PADDING)
|
|
||||||
return (0);
|
return (0);
|
||||||
|
}
|
||||||
|
if ((has_tab || has_space) && (gc.flags & GRID_FLAG_TAB))
|
||||||
|
return (gc.data.width);
|
||||||
|
if (has_space && utf8_has_whitespace(&gc.data))
|
||||||
|
return (gc.data.width == 0 ? 1 : gc.data.width);
|
||||||
return (utf8_cstrhas(set, &gc.data));
|
return (utf8_cstrhas(set, &gc.data));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
460
image-fallback.c
460
image-fallback.c
@@ -1,460 +0,0 @@
|
|||||||
/* $OpenBSD$ */
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Copyright (c) 2026 Michael Grant <mgrant@grant.org>
|
|
||||||
*
|
|
||||||
* 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 <limits.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <string.h>
|
|
||||||
|
|
||||||
#include "tmux.h"
|
|
||||||
|
|
||||||
/* Character-cell fallback for clients without a graphical image protocol. */
|
|
||||||
|
|
||||||
enum image_glyph_detail {
|
|
||||||
IMAGE_GLYPH_ASCII,
|
|
||||||
IMAGE_GLYPH_SHADE5,
|
|
||||||
IMAGE_GLYPH_SHADE8,
|
|
||||||
IMAGE_GLYPH_HALF,
|
|
||||||
IMAGE_GLYPH_QUADRANT,
|
|
||||||
IMAGE_GLYPH_SEXTANT
|
|
||||||
};
|
|
||||||
|
|
||||||
enum image_glyph_palette {
|
|
||||||
IMAGE_GLYPH_PALETTE_8,
|
|
||||||
IMAGE_GLYPH_PALETTE_16,
|
|
||||||
IMAGE_GLYPH_PALETTE_256,
|
|
||||||
IMAGE_GLYPH_PALETTE_RGB
|
|
||||||
};
|
|
||||||
|
|
||||||
struct image_rgb {
|
|
||||||
u_char r;
|
|
||||||
u_char g;
|
|
||||||
u_char b;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct image_fallback_data {
|
|
||||||
u_char *shade5;
|
|
||||||
u_char *shade8;
|
|
||||||
};
|
|
||||||
|
|
||||||
static const struct image_rgb image_ansi_colours[16] = {
|
|
||||||
{ 0x00, 0x00, 0x00 }, { 0x80, 0x00, 0x00 },
|
|
||||||
{ 0x00, 0x80, 0x00 }, { 0x80, 0x80, 0x00 },
|
|
||||||
{ 0x00, 0x00, 0x80 }, { 0x80, 0x00, 0x80 },
|
|
||||||
{ 0x00, 0x80, 0x80 }, { 0xc0, 0xc0, 0xc0 },
|
|
||||||
{ 0x80, 0x80, 0x80 }, { 0xff, 0x00, 0x00 },
|
|
||||||
{ 0x00, 0xff, 0x00 }, { 0xff, 0xff, 0x00 },
|
|
||||||
{ 0x00, 0x00, 0xff }, { 0xff, 0x00, 0xff },
|
|
||||||
{ 0x00, 0xff, 0xff }, { 0xff, 0xff, 0xff }
|
|
||||||
};
|
|
||||||
|
|
||||||
/* Return squared distance between two RGB colours. */
|
|
||||||
static u_int
|
|
||||||
image_glyph_distance(struct image_rgb a, struct image_rgb b)
|
|
||||||
{
|
|
||||||
int r = a.r - b.r, g = a.g - b.g, bl = a.b - b.b;
|
|
||||||
|
|
||||||
return (r * r + g * g + bl * bl);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Return if an RGB colour is close to grey. */
|
|
||||||
static int
|
|
||||||
image_glyph_low_saturation(struct image_rgb colour)
|
|
||||||
{
|
|
||||||
u_int minimum, maximum;
|
|
||||||
|
|
||||||
minimum = maximum = colour.r;
|
|
||||||
if (colour.g < minimum)
|
|
||||||
minimum = colour.g;
|
|
||||||
if (colour.b < minimum)
|
|
||||||
minimum = colour.b;
|
|
||||||
if (colour.g > maximum)
|
|
||||||
maximum = colour.g;
|
|
||||||
if (colour.b > maximum)
|
|
||||||
maximum = colour.b;
|
|
||||||
return (maximum == 0 || (maximum - minimum) * 4 <= maximum);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Find the closest ANSI colour. */
|
|
||||||
static u_int
|
|
||||||
image_glyph_nearest_ansi(struct image_rgb colour, u_int colours)
|
|
||||||
{
|
|
||||||
u_int i, distance, best_distance = UINT_MAX, best = 0;
|
|
||||||
int neutral = image_glyph_low_saturation(colour);
|
|
||||||
|
|
||||||
for (i = 0; i < colours; i++) {
|
|
||||||
if (neutral && i != 0 && i != 7 &&
|
|
||||||
(colours != 16 || (i != 8 && i != 15)))
|
|
||||||
continue;
|
|
||||||
distance = image_glyph_distance(colour, image_ansi_colours[i]);
|
|
||||||
if (distance < best_distance) {
|
|
||||||
best_distance = distance;
|
|
||||||
best = i;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return (best);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Quantize an RGB colour for a fallback palette. */
|
|
||||||
static struct image_rgb
|
|
||||||
image_glyph_quantize(struct image_rgb colour, enum image_glyph_palette palette,
|
|
||||||
int *output)
|
|
||||||
{
|
|
||||||
struct image_rgb result;
|
|
||||||
int value;
|
|
||||||
u_int index;
|
|
||||||
|
|
||||||
if (palette == IMAGE_GLYPH_PALETTE_RGB) {
|
|
||||||
*output = colour_join_rgb(colour.r, colour.g, colour.b);
|
|
||||||
return (colour);
|
|
||||||
}
|
|
||||||
if (palette == IMAGE_GLYPH_PALETTE_256) {
|
|
||||||
*output = colour_find_rgb(colour.r, colour.g, colour.b);
|
|
||||||
value = colour_256toRGB(*output);
|
|
||||||
colour_split_rgb(value, &result.r, &result.g, &result.b);
|
|
||||||
return (result);
|
|
||||||
}
|
|
||||||
index = image_glyph_nearest_ansi(colour,
|
|
||||||
palette == IMAGE_GLYPH_PALETTE_8 ? 8 : 16);
|
|
||||||
result = image_ansi_colours[index];
|
|
||||||
if (index < 8)
|
|
||||||
*output = index;
|
|
||||||
else
|
|
||||||
*output = 90 + index - 8;
|
|
||||||
return (result);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Fit two representative colours to image samples. */
|
|
||||||
static void
|
|
||||||
image_glyph_fit_colours(const struct image_rgb *samples, u_int count,
|
|
||||||
struct image_rgb centres[2])
|
|
||||||
{
|
|
||||||
u_int sum[2][3], counts[2], group, i, j, iteration, distance;
|
|
||||||
u_int maximum = 0, first = 0, second = 0;
|
|
||||||
|
|
||||||
for (i = 0; i < count; i++) {
|
|
||||||
for (j = i + 1; j < count; j++) {
|
|
||||||
distance = image_glyph_distance(samples[i], samples[j]);
|
|
||||||
if (distance > maximum) {
|
|
||||||
maximum = distance;
|
|
||||||
first = i;
|
|
||||||
second = j;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
centres[0] = samples[first];
|
|
||||||
centres[1] = samples[second];
|
|
||||||
for (iteration = 0; iteration < 4; iteration++) {
|
|
||||||
memset(sum, 0, sizeof sum);
|
|
||||||
memset(counts, 0, sizeof counts);
|
|
||||||
for (i = 0; i < count; i++) {
|
|
||||||
group = image_glyph_distance(samples[i], centres[1]) <
|
|
||||||
image_glyph_distance(samples[i], centres[0]);
|
|
||||||
sum[group][0] += samples[i].r;
|
|
||||||
sum[group][1] += samples[i].g;
|
|
||||||
sum[group][2] += samples[i].b;
|
|
||||||
counts[group]++;
|
|
||||||
}
|
|
||||||
for (i = 0; i < 2; i++) {
|
|
||||||
if (counts[i] == 0)
|
|
||||||
continue;
|
|
||||||
centres[i].r = (sum[i][0] + counts[i] / 2) / counts[i];
|
|
||||||
centres[i].g = (sum[i][1] + counts[i] / 2) / counts[i];
|
|
||||||
centres[i].b = (sum[i][2] + counts[i] / 2) / counts[i];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Set UTF-8 data from an ACS key. */
|
|
||||||
static int
|
|
||||||
image_glyph_set_acs(struct tty *tty, struct utf8_data *data, u_char key)
|
|
||||||
{
|
|
||||||
struct utf8_data *ud;
|
|
||||||
const char *s = tty_acs_get(tty, key);
|
|
||||||
|
|
||||||
if (s == NULL)
|
|
||||||
return (0);
|
|
||||||
ud = utf8_fromcstr(s);
|
|
||||||
if (ud[0].size == 0) {
|
|
||||||
free(ud);
|
|
||||||
return (0);
|
|
||||||
}
|
|
||||||
memcpy(data, &ud[0], sizeof *data);
|
|
||||||
free(ud);
|
|
||||||
return (1);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Return the ACS key for a fallback block mask. */
|
|
||||||
static u_char
|
|
||||||
image_glyph_block_key(enum image_glyph_detail detail, u_int mask)
|
|
||||||
{
|
|
||||||
static const u_char half[4] = {
|
|
||||||
0, TTY_ACS_IMAGE_HALF_UPPER, TTY_ACS_IMAGE_HALF_LOWER,
|
|
||||||
TTY_ACS_IMAGE_BLOCK
|
|
||||||
};
|
|
||||||
static const u_char quadrant[16] = {
|
|
||||||
0, TTY_ACS_IMAGE_QUADRANT_UPPER_LEFT,
|
|
||||||
TTY_ACS_IMAGE_QUADRANT_UPPER_RIGHT, TTY_ACS_IMAGE_HALF_UPPER,
|
|
||||||
TTY_ACS_IMAGE_QUADRANT_LOWER_LEFT, TTY_ACS_IMAGE_HALF_LEFT,
|
|
||||||
TTY_ACS_IMAGE_QUADRANT_UPPER_RIGHT_LOWER_LEFT,
|
|
||||||
TTY_ACS_IMAGE_QUADRANT_UPPER_LEFT_UPPER_RIGHT_LOWER_LEFT,
|
|
||||||
TTY_ACS_IMAGE_QUADRANT_LOWER_RIGHT,
|
|
||||||
TTY_ACS_IMAGE_QUADRANT_UPPER_LEFT_LOWER_RIGHT,
|
|
||||||
TTY_ACS_IMAGE_HALF_RIGHT,
|
|
||||||
TTY_ACS_IMAGE_QUADRANT_UPPER_LEFT_UPPER_RIGHT_LOWER_RIGHT,
|
|
||||||
TTY_ACS_IMAGE_HALF_LOWER,
|
|
||||||
TTY_ACS_IMAGE_QUADRANT_UPPER_LEFT_LOWER_LEFT_LOWER_RIGHT,
|
|
||||||
TTY_ACS_IMAGE_QUADRANT_UPPER_RIGHT_LOWER_LEFT_LOWER_RIGHT,
|
|
||||||
TTY_ACS_IMAGE_BLOCK
|
|
||||||
};
|
|
||||||
|
|
||||||
if (detail == IMAGE_GLYPH_HALF)
|
|
||||||
return (half[mask]);
|
|
||||||
if (detail == IMAGE_GLYPH_QUADRANT)
|
|
||||||
return (quadrant[mask]);
|
|
||||||
if (mask == 0)
|
|
||||||
return (0);
|
|
||||||
if (mask == 21)
|
|
||||||
return (TTY_ACS_IMAGE_HALF_LEFT);
|
|
||||||
if (mask == 42)
|
|
||||||
return (TTY_ACS_IMAGE_HALF_RIGHT);
|
|
||||||
if (mask == 63)
|
|
||||||
return (TTY_ACS_IMAGE_BLOCK);
|
|
||||||
return (tty_acs_image_sextant(mask));
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Dither image brightness into cached shade levels. */
|
|
||||||
static u_char *
|
|
||||||
image_glyph_make_shades(struct image *im, u_int levels)
|
|
||||||
{
|
|
||||||
struct image_fallback_data *data;
|
|
||||||
int *values, value, represented, error;
|
|
||||||
size_t cells, index;
|
|
||||||
u_int x, y, scan, level, minimum = 255, maximum = 0;
|
|
||||||
u_int sx, sy;
|
|
||||||
int reverse;
|
|
||||||
u_char *result;
|
|
||||||
|
|
||||||
data = image_get_fallback_data(im);
|
|
||||||
if (data == NULL) {
|
|
||||||
data = xcalloc(1, sizeof *data);
|
|
||||||
image_set_fallback_data(im, data);
|
|
||||||
}
|
|
||||||
result = (levels == 5 ? data->shade5 : data->shade8);
|
|
||||||
if (result != NULL)
|
|
||||||
return (result);
|
|
||||||
image_get_size_in_cells(im, &sx, &sy);
|
|
||||||
cells = (size_t)sx * sy;
|
|
||||||
values = xcalloc(cells, sizeof *values);
|
|
||||||
result = xcalloc(cells, 1);
|
|
||||||
for (y = 0; y < sy; y++) {
|
|
||||||
for (x = 0; x < sx; x++) {
|
|
||||||
index = (size_t)y * sx + x;
|
|
||||||
value = image_get_brightness(im, x, y);
|
|
||||||
if ((u_int)value < minimum)
|
|
||||||
minimum = value;
|
|
||||||
if ((u_int)value > maximum)
|
|
||||||
maximum = value;
|
|
||||||
values[index] = value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (maximum > minimum) {
|
|
||||||
for (index = 0; index < cells; index++)
|
|
||||||
values[index] = (values[index] - minimum) * 255 /
|
|
||||||
(maximum - minimum);
|
|
||||||
}
|
|
||||||
for (y = 0; y < sy; y++) {
|
|
||||||
reverse = (y & 1);
|
|
||||||
for (scan = 0; scan < sx; scan++) {
|
|
||||||
x = (reverse ? sx - scan - 1 : scan);
|
|
||||||
index = (size_t)y * sx + x;
|
|
||||||
value = values[index];
|
|
||||||
if (value < 0)
|
|
||||||
value = 0;
|
|
||||||
if (value > 255)
|
|
||||||
value = 255;
|
|
||||||
level = (value * (levels - 1) + 127) / 255;
|
|
||||||
result[index] = level;
|
|
||||||
represented = level * 255 / (levels - 1);
|
|
||||||
error = value - represented;
|
|
||||||
if (!reverse && x + 1 < sx)
|
|
||||||
values[index + 1] += error * 7 / 16;
|
|
||||||
if (reverse && x != 0)
|
|
||||||
values[index - 1] += error * 7 / 16;
|
|
||||||
if (y + 1 == sy)
|
|
||||||
continue;
|
|
||||||
if (!reverse && x != 0)
|
|
||||||
values[index + sx - 1] += error * 3 / 16;
|
|
||||||
if (reverse && x + 1 < sx)
|
|
||||||
values[index + sx + 1] += error * 3 / 16;
|
|
||||||
values[index + sx] += error * 5 / 16;
|
|
||||||
if (!reverse && x + 1 < sx)
|
|
||||||
values[index + sx + 1] += error / 16;
|
|
||||||
if (reverse && x != 0)
|
|
||||||
values[index + sx - 1] += error / 16;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
free(values);
|
|
||||||
if (levels == 5)
|
|
||||||
data->shade5 = result;
|
|
||||||
else
|
|
||||||
data->shade8 = result;
|
|
||||||
return (result);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Select the best fallback colour palette for a terminal. */
|
|
||||||
static enum image_glyph_palette
|
|
||||||
image_glyph_get_palette(struct tty *tty)
|
|
||||||
{
|
|
||||||
int colours;
|
|
||||||
|
|
||||||
if (tty->term->flags & TERM_RGBCOLOURS)
|
|
||||||
return (IMAGE_GLYPH_PALETTE_RGB);
|
|
||||||
if (tty->term->flags & TERM_256COLOURS)
|
|
||||||
return (IMAGE_GLYPH_PALETTE_256);
|
|
||||||
colours = tty_term_number(tty->term, TTYC_COLORS);
|
|
||||||
if (colours >= 256)
|
|
||||||
return (IMAGE_GLYPH_PALETTE_256);
|
|
||||||
if (colours >= 16)
|
|
||||||
return (IMAGE_GLYPH_PALETTE_16);
|
|
||||||
return (IMAGE_GLYPH_PALETTE_8);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Select the best fallback glyph detail for a terminal. */
|
|
||||||
static enum image_glyph_detail
|
|
||||||
image_glyph_get_detail(struct tty *tty, enum image_glyph_palette palette)
|
|
||||||
{
|
|
||||||
if (tty_acs_needed(tty))
|
|
||||||
return (IMAGE_GLYPH_ASCII);
|
|
||||||
if (tty->term->flags & TERM_IMAGE_SEXTANTS)
|
|
||||||
return (IMAGE_GLYPH_SEXTANT);
|
|
||||||
if (tty->term->flags & TERM_IMAGE_QUADRANTS)
|
|
||||||
return (IMAGE_GLYPH_QUADRANT);
|
|
||||||
if (palette != IMAGE_GLYPH_PALETTE_8)
|
|
||||||
return (IMAGE_GLYPH_HALF);
|
|
||||||
if (tty_term_has(tty->term, TTYC_NOBR))
|
|
||||||
return (IMAGE_GLYPH_SHADE5);
|
|
||||||
return (IMAGE_GLYPH_SHADE8);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Draw one image cell with a coloured block glyph. */
|
|
||||||
static void
|
|
||||||
image_glyph_block(struct tty *tty, struct image *im, u_int x, u_int y,
|
|
||||||
enum image_glyph_detail detail, enum image_glyph_palette palette,
|
|
||||||
struct grid_cell *out)
|
|
||||||
{
|
|
||||||
struct image_rgb samples[6], centres[2], quantized[2];
|
|
||||||
u_int columns, rows, sx, sy, i, n, mask;
|
|
||||||
int colours[2];
|
|
||||||
u_char key;
|
|
||||||
|
|
||||||
columns = (detail == IMAGE_GLYPH_HALF ? 1 : 2);
|
|
||||||
rows = (detail == IMAGE_GLYPH_HALF ? 2 :
|
|
||||||
detail == IMAGE_GLYPH_QUADRANT ? 2 : 3);
|
|
||||||
i = 0;
|
|
||||||
for (sy = 0; sy < rows; sy++) {
|
|
||||||
for (sx = 0; sx < columns; sx++) {
|
|
||||||
image_get_cell_average(im, x, y, sx, sy, columns, rows,
|
|
||||||
&samples[i].r, &samples[i].g, &samples[i].b);
|
|
||||||
i++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
n = columns * rows;
|
|
||||||
image_glyph_fit_colours(samples, n, centres);
|
|
||||||
quantized[0] = image_glyph_quantize(centres[0], palette, &colours[0]);
|
|
||||||
quantized[1] = image_glyph_quantize(centres[1], palette, &colours[1]);
|
|
||||||
mask = 0;
|
|
||||||
for (i = 0; i < n; i++) {
|
|
||||||
if (image_glyph_distance(samples[i], quantized[1]) <
|
|
||||||
image_glyph_distance(samples[i], quantized[0]))
|
|
||||||
mask |= (1U << i);
|
|
||||||
}
|
|
||||||
key = image_glyph_block_key(detail, mask);
|
|
||||||
if (key == 0)
|
|
||||||
utf8_set(&out->data, ' ');
|
|
||||||
else if (!image_glyph_set_acs(tty, &out->data, key))
|
|
||||||
utf8_set(&out->data, ' ');
|
|
||||||
out->fg = colours[1];
|
|
||||||
out->bg = colours[0];
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Fill a grid cell with an image fallback glyph. */
|
|
||||||
void
|
|
||||||
image_get_fallback_cell(struct tty *tty, struct image *im, u_int x, u_int y,
|
|
||||||
const struct grid_cell *gc, struct grid_cell *out,
|
|
||||||
__unused const struct tty_style_ctx *style_ctx)
|
|
||||||
{
|
|
||||||
static const char ascii[] = " .:-=+*#%@";
|
|
||||||
static const u_char shades[5] = { 0, TTY_ACS_IMAGE_SHADE_LIGHT,
|
|
||||||
TTY_ACS_IMAGE_SHADE_MEDIUM, TTY_ACS_IMAGE_SHADE_DARK,
|
|
||||||
TTY_ACS_IMAGE_BLOCK };
|
|
||||||
static const u_char bold_shades[8] = { 0,
|
|
||||||
TTY_ACS_IMAGE_SHADE_LIGHT, TTY_ACS_IMAGE_SHADE_LIGHT,
|
|
||||||
TTY_ACS_IMAGE_SHADE_MEDIUM, TTY_ACS_IMAGE_SHADE_MEDIUM,
|
|
||||||
TTY_ACS_IMAGE_SHADE_DARK, TTY_ACS_IMAGE_BLOCK,
|
|
||||||
TTY_ACS_IMAGE_BLOCK };
|
|
||||||
enum image_glyph_palette palette;
|
|
||||||
enum image_glyph_detail detail;
|
|
||||||
u_char *levels, key;
|
|
||||||
u_int level = 0, sx;
|
|
||||||
|
|
||||||
memcpy(out, gc, sizeof *out);
|
|
||||||
out->flags &= ~GRID_FLAG_IMAGE;
|
|
||||||
palette = image_glyph_get_palette(tty);
|
|
||||||
detail = image_glyph_get_detail(tty, palette);
|
|
||||||
if (detail == IMAGE_GLYPH_ASCII) {
|
|
||||||
level = image_get_brightness(im, x, y) * (sizeof ascii - 2) / 255;
|
|
||||||
utf8_set(&out->data, ascii[level]);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (detail == IMAGE_GLYPH_SHADE5 || detail == IMAGE_GLYPH_SHADE8) {
|
|
||||||
levels = image_glyph_make_shades(im,
|
|
||||||
detail == IMAGE_GLYPH_SHADE5 ? 5 : 8);
|
|
||||||
image_get_size_in_cells(im, &sx, NULL);
|
|
||||||
level = levels[(size_t)y * sx + x];
|
|
||||||
key = (detail == IMAGE_GLYPH_SHADE5 ? shades[level] :
|
|
||||||
bold_shades[level]);
|
|
||||||
if (key == 0)
|
|
||||||
utf8_set(&out->data, ' ');
|
|
||||||
else if (!image_glyph_set_acs(tty, &out->data, key))
|
|
||||||
utf8_set(&out->data, ' ');
|
|
||||||
out->fg = 7;
|
|
||||||
out->bg = 0;
|
|
||||||
out->attr &= ~GRID_ATTR_BRIGHT;
|
|
||||||
if (detail == IMAGE_GLYPH_SHADE8 &&
|
|
||||||
(level == 2 || level == 4 || level == 7))
|
|
||||||
out->attr |= GRID_ATTR_BRIGHT;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
image_glyph_block(tty, im, x, y, detail, palette, out);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Free cached fallback rendering data for an image. */
|
|
||||||
void
|
|
||||||
image_free_fallback(struct image *im)
|
|
||||||
{
|
|
||||||
struct image_fallback_data *data;
|
|
||||||
|
|
||||||
data = image_get_fallback_data(im);
|
|
||||||
if (data == NULL)
|
|
||||||
return;
|
|
||||||
free(data->shade5);
|
|
||||||
free(data->shade8);
|
|
||||||
free(data);
|
|
||||||
}
|
|
||||||
796
image-sixel.c
796
image-sixel.c
@@ -2,7 +2,6 @@
|
|||||||
|
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2019 Nicholas Marriott <nicholas.marriott@gmail.com>
|
* Copyright (c) 2019 Nicholas Marriott <nicholas.marriott@gmail.com>
|
||||||
* Copyright (c) 2026 Michael Grant <mgrant@grant.org>
|
|
||||||
*
|
*
|
||||||
* Permission to use, copy, modify, and distribute this software for any
|
* Permission to use, copy, modify, and distribute this software for any
|
||||||
* purpose with or without fee is hereby granted, provided that the above
|
* purpose with or without fee is hereby granted, provided that the above
|
||||||
@@ -19,7 +18,6 @@
|
|||||||
|
|
||||||
#include <sys/types.h>
|
#include <sys/types.h>
|
||||||
|
|
||||||
#include <limits.h>
|
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
@@ -27,10 +25,6 @@
|
|||||||
|
|
||||||
#define SIXEL_WIDTH_LIMIT 10000
|
#define SIXEL_WIDTH_LIMIT 10000
|
||||||
#define SIXEL_HEIGHT_LIMIT 10000
|
#define SIXEL_HEIGHT_LIMIT 10000
|
||||||
#define SIXEL_PALETTE_SIZE 256
|
|
||||||
#define SIXEL_HISTOGRAM_LEVELS 32
|
|
||||||
#define SIXEL_HISTOGRAM_SIZE (SIXEL_HISTOGRAM_LEVELS * \
|
|
||||||
SIXEL_HISTOGRAM_LEVELS * SIXEL_HISTOGRAM_LEVELS)
|
|
||||||
|
|
||||||
struct sixel_line {
|
struct sixel_line {
|
||||||
u_int x;
|
u_int x;
|
||||||
@@ -72,57 +66,6 @@ struct sixel_chunk {
|
|||||||
char *data;
|
char *data;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct sixel_image_cache {
|
|
||||||
u_int server_id;
|
|
||||||
u_int xpixel;
|
|
||||||
u_int ypixel;
|
|
||||||
size_t size;
|
|
||||||
uint64_t age;
|
|
||||||
struct sixel_image *si;
|
|
||||||
struct sixel_image_cache *next;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct sixel_output {
|
|
||||||
struct sixel_image_cache *images;
|
|
||||||
size_t size;
|
|
||||||
uint64_t age;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct sixel_hgram {
|
|
||||||
u_int count;
|
|
||||||
uint64_t red;
|
|
||||||
uint64_t green;
|
|
||||||
uint64_t blue;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct sixel_box {
|
|
||||||
u_int red_min;
|
|
||||||
u_int red_max;
|
|
||||||
u_int green_min;
|
|
||||||
u_int green_max;
|
|
||||||
u_int blue_min;
|
|
||||||
u_int blue_max;
|
|
||||||
u_int count;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct sixel_rgb {
|
|
||||||
u_char red;
|
|
||||||
u_char green;
|
|
||||||
u_char blue;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct sixel_source {
|
|
||||||
const u_char *pixels;
|
|
||||||
size_t stride;
|
|
||||||
u_int width;
|
|
||||||
u_int height;
|
|
||||||
u_int canvas_width;
|
|
||||||
u_int canvas_height;
|
|
||||||
u_int sx;
|
|
||||||
u_int sy;
|
|
||||||
};
|
|
||||||
|
|
||||||
/* Grow a SIXEL image to contain a line. */
|
|
||||||
static int
|
static int
|
||||||
sixel_parse_expand_lines(struct sixel_image *si, u_int y)
|
sixel_parse_expand_lines(struct sixel_image *si, u_int y)
|
||||||
{
|
{
|
||||||
@@ -135,7 +78,6 @@ sixel_parse_expand_lines(struct sixel_image *si, u_int y)
|
|||||||
return (0);
|
return (0);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Grow a SIXEL line to contain a pixel. */
|
|
||||||
static int
|
static int
|
||||||
sixel_parse_expand_line(struct sixel_image *si, struct sixel_line *sl, u_int x)
|
sixel_parse_expand_line(struct sixel_image *si, struct sixel_line *sl, u_int x)
|
||||||
{
|
{
|
||||||
@@ -150,7 +92,6 @@ sixel_parse_expand_line(struct sixel_image *si, struct sixel_line *sl, u_int x)
|
|||||||
return (0);
|
return (0);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Return a SIXEL palette index at a pixel. */
|
|
||||||
static u_int
|
static u_int
|
||||||
sixel_get_pixel(struct sixel_image *si, u_int x, u_int y)
|
sixel_get_pixel(struct sixel_image *si, u_int x, u_int y)
|
||||||
{
|
{
|
||||||
@@ -164,7 +105,6 @@ sixel_get_pixel(struct sixel_image *si, u_int x, u_int y)
|
|||||||
return (sl->data[x]);
|
return (sl->data[x]);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Set a SIXEL palette index at a pixel. */
|
|
||||||
static int
|
static int
|
||||||
sixel_set_pixel(struct sixel_image *si, u_int x, u_int y, u_int c)
|
sixel_set_pixel(struct sixel_image *si, u_int x, u_int y, u_int c)
|
||||||
{
|
{
|
||||||
@@ -179,7 +119,6 @@ sixel_set_pixel(struct sixel_image *si, u_int x, u_int y, u_int c)
|
|||||||
return (0);
|
return (0);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Write a SIXEL six-pixel column. */
|
|
||||||
static int
|
static int
|
||||||
sixel_parse_write(struct sixel_image *si, u_int ch)
|
sixel_parse_write(struct sixel_image *si, u_int ch)
|
||||||
{
|
{
|
||||||
@@ -194,7 +133,6 @@ sixel_parse_write(struct sixel_image *si, u_int ch)
|
|||||||
return (0);
|
return (0);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Parse a SIXEL raster attribute sequence. */
|
|
||||||
static const char *
|
static const char *
|
||||||
sixel_parse_attributes(struct sixel_image *si, const char *cp, const char *end)
|
sixel_parse_attributes(struct sixel_image *si, const char *cp, const char *end)
|
||||||
{
|
{
|
||||||
@@ -248,7 +186,6 @@ sixel_parse_attributes(struct sixel_image *si, const char *cp, const char *end)
|
|||||||
return (last);
|
return (last);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Parse a SIXEL colour register sequence. */
|
|
||||||
static const char *
|
static const char *
|
||||||
sixel_parse_colour(struct sixel_image *si, const char *cp, const char *end)
|
sixel_parse_colour(struct sixel_image *si, const char *cp, const char *end)
|
||||||
{
|
{
|
||||||
@@ -312,7 +249,6 @@ sixel_parse_colour(struct sixel_image *si, const char *cp, const char *end)
|
|||||||
return (last);
|
return (last);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Parse a SIXEL repeat sequence. */
|
|
||||||
static const char *
|
static const char *
|
||||||
sixel_parse_repeat(struct sixel_image *si, const char *cp, const char *end)
|
sixel_parse_repeat(struct sixel_image *si, const char *cp, const char *end)
|
||||||
{
|
{
|
||||||
@@ -354,7 +290,6 @@ sixel_parse_repeat(struct sixel_image *si, const char *cp, const char *end)
|
|||||||
return (last);
|
return (last);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Parse SIXEL data into an indexed image. */
|
|
||||||
struct sixel_image *
|
struct sixel_image *
|
||||||
sixel_parse(const char *buf, size_t len, u_int p2, u_int xpixel, u_int ypixel)
|
sixel_parse(const char *buf, size_t len, u_int p2, u_int xpixel, u_int ypixel)
|
||||||
{
|
{
|
||||||
@@ -420,7 +355,6 @@ bad:
|
|||||||
return (NULL);
|
return (NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Free an indexed SIXEL image. */
|
|
||||||
void
|
void
|
||||||
sixel_free(struct sixel_image *si)
|
sixel_free(struct sixel_image *si)
|
||||||
{
|
{
|
||||||
@@ -434,7 +368,6 @@ sixel_free(struct sixel_image *si)
|
|||||||
free(si);
|
free(si);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Write a SIXEL image to the debug log. */
|
|
||||||
void
|
void
|
||||||
sixel_log(struct sixel_image *si)
|
sixel_log(struct sixel_image *si)
|
||||||
{
|
{
|
||||||
@@ -461,121 +394,24 @@ sixel_log(struct sixel_image *si)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Return the cell dimensions occupied by a SIXEL image. */
|
|
||||||
void
|
void
|
||||||
sixel_size_in_cells(struct sixel_image *si, u_int *x, u_int *y)
|
sixel_size_in_cells(struct sixel_image *si, u_int *x, u_int *y)
|
||||||
{
|
{
|
||||||
if (si->xpixel == 0)
|
if ((si->x % si->xpixel) == 0)
|
||||||
si->xpixel = 8;
|
*x = (si->x / si->xpixel);
|
||||||
if (si->ypixel == 0)
|
|
||||||
si->ypixel = 16;
|
|
||||||
image_size_in_cells(si->x, si->y, si->xpixel, si->ypixel, x, y);
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifdef ENABLE_IMAGES
|
|
||||||
/* Convert one HLS component to RGB. */
|
|
||||||
static double
|
|
||||||
sixel_hue(double p, double q, double t)
|
|
||||||
{
|
|
||||||
if (t < 0)
|
|
||||||
t += 1;
|
|
||||||
if (t > 1)
|
|
||||||
t -= 1;
|
|
||||||
if (t < 1.0 / 6)
|
|
||||||
return (p + (q - p) * 6 * t);
|
|
||||||
if (t < 1.0 / 2)
|
|
||||||
return (q);
|
|
||||||
if (t < 2.0 / 3)
|
|
||||||
return (p + (q - p) * (2.0 / 3 - t) * 6);
|
|
||||||
return (p);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Convert a SIXEL colour register to RGB. */
|
|
||||||
static void
|
|
||||||
sixel_colour_to_rgb(u_int colour, u_char *r, u_char *g, u_char *b)
|
|
||||||
{
|
|
||||||
u_int type = colour >> 25;
|
|
||||||
double h, l, s, p, q;
|
|
||||||
|
|
||||||
if (type == 2) {
|
|
||||||
*r = (((colour >> 16) & 0xff) * 255 + 50) / 100;
|
|
||||||
*g = (((colour >> 8) & 0xff) * 255 + 50) / 100;
|
|
||||||
*b = ((colour & 0xff) * 255 + 50) / 100;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (type != 1) {
|
|
||||||
*r = *g = *b = 0;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
h = ((colour >> 16) & 0x1ff) / 360.0;
|
|
||||||
l = ((colour >> 8) & 0xff) / 100.0;
|
|
||||||
s = (colour & 0xff) / 100.0;
|
|
||||||
if (s == 0) {
|
|
||||||
*r = *g = *b = l * 255 + 0.5;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
q = l < 0.5 ? l * (1 + s) : l + s - l * s;
|
|
||||||
p = 2 * l - q;
|
|
||||||
/* SIXEL HLS has blue at 0, red at 120 and green at 240 degrees. */
|
|
||||||
*r = sixel_hue(p, q, h) * 255 + 0.5;
|
|
||||||
*g = sixel_hue(p, q, h - 1.0 / 3) * 255 + 0.5;
|
|
||||||
*b = sixel_hue(p, q, h + 1.0 / 3) * 255 + 0.5;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Convert decoded SIXEL data into the immutable image. */
|
|
||||||
struct image *
|
|
||||||
sixel_to_image(struct sixel_image *si)
|
|
||||||
{
|
|
||||||
u_char *pixels, *pixel, r, g, b;
|
|
||||||
u_int x, y, c, sx, sy;
|
|
||||||
struct image *im;
|
|
||||||
|
|
||||||
if ((uint64_t)si->x * si->y * 4 > SIZE_MAX)
|
|
||||||
return (NULL);
|
|
||||||
pixels = xcalloc(si->x * si->y, 4);
|
|
||||||
for (y = 0; y < si->y; y++) {
|
|
||||||
for (x = 0; x < si->x; x++) {
|
|
||||||
c = sixel_get_pixel(si, x, y);
|
|
||||||
pixel = pixels + ((size_t)y * si->x + x) * 4;
|
|
||||||
if (c == 0) {
|
|
||||||
pixel[3] = si->p2 == 1 ? 0 : 255;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
c--;
|
|
||||||
if (c < si->ncolours)
|
|
||||||
sixel_colour_to_rgb(si->colours[c], &r, &g, &b);
|
|
||||||
else
|
else
|
||||||
r = g = b = 0;
|
*x = 1 + (si->x / si->xpixel);
|
||||||
pixel[0] = r;
|
if ((si->y % si->ypixel) == 0)
|
||||||
pixel[1] = g;
|
*y = (si->y / si->ypixel);
|
||||||
pixel[2] = b;
|
|
||||||
pixel[3] = 255;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
sixel_size_in_cells(si, &sx, &sy);
|
|
||||||
if ((uint64_t)sx * si->xpixel > UINT_MAX ||
|
|
||||||
(uint64_t)sy * si->ypixel > UINT_MAX) {
|
|
||||||
free(pixels);
|
|
||||||
return (NULL);
|
|
||||||
}
|
|
||||||
im = image_create(si->x, si->y, sx * si->xpixel, sy * si->ypixel,
|
|
||||||
sx, sy, pixels);
|
|
||||||
if (im == NULL)
|
|
||||||
free(pixels);
|
|
||||||
else
|
else
|
||||||
image_set_sixel(im, si);
|
*y = 1 + (si->y / si->ypixel);
|
||||||
return (im);
|
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
|
|
||||||
/* Scale or crop an indexed SIXEL image. */
|
|
||||||
struct sixel_image *
|
struct sixel_image *
|
||||||
sixel_scale(struct sixel_image *si, u_int xpixel, u_int ypixel, u_int ox,
|
sixel_scale(struct sixel_image *si, u_int xpixel, u_int ypixel, u_int ox,
|
||||||
u_int oy, u_int sx, u_int sy, int colours)
|
u_int oy, u_int sx, u_int sy, int colours)
|
||||||
{
|
{
|
||||||
struct sixel_image *new;
|
struct sixel_image *new;
|
||||||
uint64_t x0, x1, y0, y1;
|
|
||||||
u_int cx, cy, pox, poy, psx, psy, tsx, tsy, px, py;
|
u_int cx, cy, pox, poy, psx, psy, tsx, tsy, px, py;
|
||||||
u_int x, y, i;
|
u_int x, y, i;
|
||||||
|
|
||||||
@@ -599,19 +435,10 @@ sixel_scale(struct sixel_image *si, u_int xpixel, u_int ypixel, u_int ox,
|
|||||||
if (ypixel == 0)
|
if (ypixel == 0)
|
||||||
ypixel = si->ypixel;
|
ypixel = si->ypixel;
|
||||||
|
|
||||||
/*
|
pox = ox * si->xpixel;
|
||||||
* Map cell boundaries over the actual raster, not the rounded-up cell
|
poy = oy * si->ypixel;
|
||||||
* canvas. Otherwise a raster shorter than its last cell row produces an
|
psx = sx * si->xpixel;
|
||||||
* empty strip when it is scaled for output.
|
psy = sy * si->ypixel;
|
||||||
*/
|
|
||||||
x0 = (uint64_t)ox * si->x / cx;
|
|
||||||
x1 = (uint64_t)(ox + sx) * si->x / cx;
|
|
||||||
y0 = (uint64_t)oy * si->y / cy;
|
|
||||||
y1 = (uint64_t)(oy + sy) * si->y / cy;
|
|
||||||
pox = x0;
|
|
||||||
poy = y0;
|
|
||||||
psx = x1 - x0;
|
|
||||||
psy = y1 - y0;
|
|
||||||
|
|
||||||
tsx = sx * xpixel;
|
tsx = sx * xpixel;
|
||||||
tsy = sy * ypixel;
|
tsy = sy * ypixel;
|
||||||
@@ -650,7 +477,6 @@ sixel_scale(struct sixel_image *si, u_int xpixel, u_int ypixel, u_int ox,
|
|||||||
return (new);
|
return (new);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Append data to a growing SIXEL output buffer. */
|
|
||||||
static void
|
static void
|
||||||
sixel_print_add(char **buf, size_t *len, size_t *used, const char *s,
|
sixel_print_add(char **buf, size_t *len, size_t *used, const char *s,
|
||||||
size_t slen)
|
size_t slen)
|
||||||
@@ -663,7 +489,6 @@ sixel_print_add(char **buf, size_t *len, size_t *used, const char *s,
|
|||||||
(*used) += slen;
|
(*used) += slen;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Append a SIXEL character repetition to an output buffer. */
|
|
||||||
static void
|
static void
|
||||||
sixel_print_repeat(char **buf, size_t *len, size_t *used, u_int count, char ch)
|
sixel_print_repeat(char **buf, size_t *len, size_t *used, u_int count, char ch)
|
||||||
{
|
{
|
||||||
@@ -685,7 +510,6 @@ sixel_print_repeat(char **buf, size_t *len, size_t *used, u_int count, char ch)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Build compressed SIXEL output chunks for a sixel row. */
|
|
||||||
static void
|
static void
|
||||||
sixel_print_compress_colors(struct sixel_image *si, struct sixel_chunk *chunks,
|
sixel_print_compress_colors(struct sixel_image *si, struct sixel_chunk *chunks,
|
||||||
u_int y, u_int *active, u_int *nactive)
|
u_int y, u_int *active, u_int *nactive)
|
||||||
@@ -738,7 +562,6 @@ sixel_print_compress_colors(struct sixel_image *si, struct sixel_chunk *chunks,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Encode an indexed SIXEL image for terminal output. */
|
|
||||||
char *
|
char *
|
||||||
sixel_print(struct sixel_image *si, struct sixel_image *map, size_t *size)
|
sixel_print(struct sixel_image *si, struct sixel_image *map, size_t *size)
|
||||||
{
|
{
|
||||||
@@ -826,605 +649,6 @@ sixel_print(struct sixel_image *si, struct sixel_image *map, size_t *size)
|
|||||||
return (buf);
|
return (buf);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Split a 5-bit RGB histogram into an adaptive palette using median cut. */
|
|
||||||
static void
|
|
||||||
sixel_box_update(struct sixel_box *box, struct sixel_hgram *hg)
|
|
||||||
{
|
|
||||||
struct sixel_hgram *entry;
|
|
||||||
u_int red, green, blue, index;
|
|
||||||
u_int red_min = SIXEL_HISTOGRAM_LEVELS;
|
|
||||||
u_int green_min = SIXEL_HISTOGRAM_LEVELS;
|
|
||||||
u_int blue_min = SIXEL_HISTOGRAM_LEVELS;
|
|
||||||
u_int red_max = 0, green_max = 0, blue_max = 0;
|
|
||||||
u_int count = 0;
|
|
||||||
|
|
||||||
for (red = box->red_min; red <= box->red_max; red++) {
|
|
||||||
for (green = box->green_min; green <= box->green_max; green++) {
|
|
||||||
for (blue = box->blue_min; blue <= box->blue_max; blue++) {
|
|
||||||
index = (red << 10)|(green << 5)|blue;
|
|
||||||
entry = &hg[index];
|
|
||||||
if (entry->count == 0)
|
|
||||||
continue;
|
|
||||||
if (red < red_min)
|
|
||||||
red_min = red;
|
|
||||||
if (red > red_max)
|
|
||||||
red_max = red;
|
|
||||||
if (green < green_min)
|
|
||||||
green_min = green;
|
|
||||||
if (green > green_max)
|
|
||||||
green_max = green;
|
|
||||||
if (blue < blue_min)
|
|
||||||
blue_min = blue;
|
|
||||||
if (blue > blue_max)
|
|
||||||
blue_max = blue;
|
|
||||||
count += entry->count;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
box->count = count;
|
|
||||||
if (count == 0)
|
|
||||||
return;
|
|
||||||
box->red_min = red_min;
|
|
||||||
box->red_max = red_max;
|
|
||||||
box->green_min = green_min;
|
|
||||||
box->green_max = green_max;
|
|
||||||
box->blue_min = blue_min;
|
|
||||||
box->blue_max = blue_max;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Split a histogram box at its weighted median. */
|
|
||||||
static int
|
|
||||||
sixel_box_split(struct sixel_box *box, struct sixel_box *new,
|
|
||||||
struct sixel_hgram *hg)
|
|
||||||
{
|
|
||||||
u_int levels[SIXEL_HISTOGRAM_LEVELS] = { 0 };
|
|
||||||
u_int red, green, blue, index, channel, first, last, level;
|
|
||||||
u_int red_range, green_range, blue_range, count = 0;
|
|
||||||
|
|
||||||
red_range = box->red_max - box->red_min;
|
|
||||||
green_range = box->green_max - box->green_min;
|
|
||||||
blue_range = box->blue_max - box->blue_min;
|
|
||||||
if (red_range == 0 && green_range == 0 && blue_range == 0)
|
|
||||||
return (0);
|
|
||||||
if (green_range >= red_range && green_range >= blue_range)
|
|
||||||
channel = 1;
|
|
||||||
else if (red_range >= blue_range)
|
|
||||||
channel = 0;
|
|
||||||
else
|
|
||||||
channel = 2;
|
|
||||||
|
|
||||||
for (red = box->red_min; red <= box->red_max; red++) {
|
|
||||||
for (green = box->green_min; green <= box->green_max; green++) {
|
|
||||||
for (blue = box->blue_min; blue <= box->blue_max; blue++) {
|
|
||||||
index = (red << 10)|(green << 5)|blue;
|
|
||||||
if (channel == 0)
|
|
||||||
levels[red] += hg[index].count;
|
|
||||||
else if (channel == 1)
|
|
||||||
levels[green] += hg[index].count;
|
|
||||||
else
|
|
||||||
levels[blue] += hg[index].count;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (channel == 0) {
|
|
||||||
first = box->red_min;
|
|
||||||
last = box->red_max;
|
|
||||||
} else if (channel == 1) {
|
|
||||||
first = box->green_min;
|
|
||||||
last = box->green_max;
|
|
||||||
} else {
|
|
||||||
first = box->blue_min;
|
|
||||||
last = box->blue_max;
|
|
||||||
}
|
|
||||||
for (level = first; level < last; level++) {
|
|
||||||
count += levels[level];
|
|
||||||
if (count >= box->count / 2)
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
/* Keep the maximum occupied level in the new box. */
|
|
||||||
if (level == last)
|
|
||||||
level--;
|
|
||||||
memcpy(new, box, sizeof *new);
|
|
||||||
if (channel == 0) {
|
|
||||||
box->red_max = level;
|
|
||||||
new->red_min = level + 1;
|
|
||||||
} else if (channel == 1) {
|
|
||||||
box->green_max = level;
|
|
||||||
new->green_min = level + 1;
|
|
||||||
} else {
|
|
||||||
box->blue_max = level;
|
|
||||||
new->blue_min = level + 1;
|
|
||||||
}
|
|
||||||
sixel_box_update(box, hg);
|
|
||||||
sixel_box_update(new, hg);
|
|
||||||
return (box->count != 0 && new->count != 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Build an adaptive palette from an RGB histogram. */
|
|
||||||
static u_int
|
|
||||||
sixel_make_palette(struct sixel_hgram *hg,
|
|
||||||
struct sixel_rgb *palette)
|
|
||||||
{
|
|
||||||
struct sixel_box boxes[SIXEL_PALETTE_SIZE], new;
|
|
||||||
struct sixel_box *box;
|
|
||||||
uint64_t best_score, score, red, green, blue, count;
|
|
||||||
u_int i, nboxes = 1, best, r, g, b, index;
|
|
||||||
u_int red_range, green_range, blue_range;
|
|
||||||
|
|
||||||
memset(&boxes[0], 0, sizeof boxes[0]);
|
|
||||||
boxes[0].red_max = boxes[0].green_max = boxes[0].blue_max =
|
|
||||||
SIXEL_HISTOGRAM_LEVELS - 1;
|
|
||||||
sixel_box_update(&boxes[0], hg);
|
|
||||||
if (boxes[0].count == 0)
|
|
||||||
return (0);
|
|
||||||
|
|
||||||
while (nboxes < SIXEL_PALETTE_SIZE) {
|
|
||||||
best = nboxes;
|
|
||||||
best_score = 0;
|
|
||||||
for (i = 0; i < nboxes; i++) {
|
|
||||||
box = &boxes[i];
|
|
||||||
red_range = box->red_max - box->red_min;
|
|
||||||
green_range = box->green_max - box->green_min;
|
|
||||||
blue_range = box->blue_max - box->blue_min;
|
|
||||||
score = (uint64_t)box->count *
|
|
||||||
(red_range * red_range + green_range * green_range +
|
|
||||||
blue_range * blue_range);
|
|
||||||
if (score > best_score) {
|
|
||||||
best = i;
|
|
||||||
best_score = score;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (best == nboxes ||
|
|
||||||
!sixel_box_split(&boxes[best], &new, hg))
|
|
||||||
break;
|
|
||||||
memcpy(&boxes[nboxes++], &new, sizeof new);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (i = 0; i < nboxes; i++) {
|
|
||||||
box = &boxes[i];
|
|
||||||
red = green = blue = count = 0;
|
|
||||||
for (r = box->red_min; r <= box->red_max; r++) {
|
|
||||||
for (g = box->green_min; g <= box->green_max; g++) {
|
|
||||||
for (b = box->blue_min; b <= box->blue_max; b++) {
|
|
||||||
index = (r << 10)|(g << 5)|b;
|
|
||||||
red += hg[index].red;
|
|
||||||
green += hg[index].green;
|
|
||||||
blue += hg[index].blue;
|
|
||||||
count += hg[index].count;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
palette[i].red = (red + count / 2) / count;
|
|
||||||
palette[i].green = (green + count / 2) / count;
|
|
||||||
palette[i].blue = (blue + count / 2) / count;
|
|
||||||
}
|
|
||||||
return (nboxes);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Find the closest adaptive palette entry for an RGB colour. */
|
|
||||||
static u_int
|
|
||||||
sixel_nearest_colour(struct sixel_rgb *palette, u_int ncolours,
|
|
||||||
uint16_t *cache, u_int red, u_int green, u_int blue)
|
|
||||||
{
|
|
||||||
uint64_t distance, best_distance = UINT64_MAX;
|
|
||||||
int dr, dg, db;
|
|
||||||
u_int i, best = 0, index;
|
|
||||||
|
|
||||||
index = ((red >> 3) << 10)|((green >> 3) << 5)|(blue >> 3);
|
|
||||||
if (cache[index] != UINT16_MAX)
|
|
||||||
return (cache[index]);
|
|
||||||
for (i = 0; i < ncolours; i++) {
|
|
||||||
dr = (int)red - palette[i].red;
|
|
||||||
dg = (int)green - palette[i].green;
|
|
||||||
db = (int)blue - palette[i].blue;
|
|
||||||
distance = 3ULL * dr * dr + 6ULL * dg * dg + db * db;
|
|
||||||
if (distance < best_distance) {
|
|
||||||
best = i;
|
|
||||||
best_distance = distance;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
cache[index] = best;
|
|
||||||
return (best);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Clamp an RGB component to the valid range. */
|
|
||||||
static u_int
|
|
||||||
sixel_clamp_colour(int colour)
|
|
||||||
{
|
|
||||||
if (colour < 0)
|
|
||||||
return (0);
|
|
||||||
if (colour > 255)
|
|
||||||
return (255);
|
|
||||||
return (colour);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Return a source pixel mapped to an output SIXEL pixel. */
|
|
||||||
static const u_char *
|
|
||||||
sixel_from_image_pixel(const struct sixel_source *source, u_int sourcex0,
|
|
||||||
u_int sourcey0,
|
|
||||||
u_int sourcewidth, u_int sourceheight, u_int sx, u_int sy, u_int x,
|
|
||||||
u_int y)
|
|
||||||
{
|
|
||||||
u_int sourcex, sourcey;
|
|
||||||
|
|
||||||
sourcex = sourcex0 + (uint64_t)x * sourcewidth / sx;
|
|
||||||
sourcey = sourcey0 + (uint64_t)y * sourceheight / sy;
|
|
||||||
if (sourcex >= source->width)
|
|
||||||
sourcex = source->width - 1;
|
|
||||||
if (sourcey >= source->height)
|
|
||||||
sourcey = source->height - 1;
|
|
||||||
return (source->pixels + sourcey * source->stride + sourcex * 4);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Render an image rectangle as an indexed SIXEL image. */
|
|
||||||
static struct sixel_image *
|
|
||||||
sixel_from_image(struct image *im, u_int ox, u_int oy, u_int cells_x,
|
|
||||||
u_int cells_y, u_int xpixel, u_int ypixel)
|
|
||||||
{
|
|
||||||
struct sixel_image *si;
|
|
||||||
struct sixel_source source;
|
|
||||||
struct sixel_hgram *hg, *entry;
|
|
||||||
struct sixel_rgb palette[SIXEL_PALETTE_SIZE];
|
|
||||||
const u_char *pixel;
|
|
||||||
uint16_t *cache;
|
|
||||||
int *current, *next, *tmp;
|
|
||||||
int red_error, green_error, blue_error, alpha_error;
|
|
||||||
u_int x, y, sx, sy, index, error_index;
|
|
||||||
u_int sourcex0, sourcey0, sourcewidth, sourceheight;
|
|
||||||
u_int red, green, blue, alpha, colour, i, ncolours;
|
|
||||||
uint64_t destination_width, destination_height;
|
|
||||||
uint64_t content_width, content_height, x0, x1, y0, y1;
|
|
||||||
|
|
||||||
/* Work out the requested cell crop in destination pixel coordinates. */
|
|
||||||
source.pixels = image_get_pixels(im, &source.stride, NULL);
|
|
||||||
image_get_size(im, &source.width, &source.height);
|
|
||||||
image_get_canvas_size(im, &source.canvas_width,
|
|
||||||
&source.canvas_height);
|
|
||||||
image_get_size_in_cells(im, &source.sx, &source.sy);
|
|
||||||
destination_width = (uint64_t)source.sx * xpixel;
|
|
||||||
destination_height = (uint64_t)source.sy * ypixel;
|
|
||||||
if (destination_width > UINT_MAX || destination_height > UINT_MAX)
|
|
||||||
return (NULL);
|
|
||||||
content_width = ((uint64_t)source.width * destination_width +
|
|
||||||
source.canvas_width - 1) / source.canvas_width;
|
|
||||||
content_height = ((uint64_t)source.height * destination_height +
|
|
||||||
source.canvas_height - 1) / source.canvas_height;
|
|
||||||
|
|
||||||
/* Convert the requested cell rectangle to clipped output pixel bounds. */
|
|
||||||
x0 = (uint64_t)ox * xpixel;
|
|
||||||
y0 = (uint64_t)oy * ypixel;
|
|
||||||
x1 = ((uint64_t)ox + cells_x) * xpixel;
|
|
||||||
y1 = ((uint64_t)oy + cells_y) * ypixel;
|
|
||||||
if (x1 > content_width)
|
|
||||||
x1 = content_width;
|
|
||||||
if (y1 > content_height)
|
|
||||||
y1 = content_height;
|
|
||||||
if (x1 <= x0 || y1 <= y0)
|
|
||||||
return (NULL);
|
|
||||||
|
|
||||||
/* The clipped output bounds determine the SIXEL image dimensions. */
|
|
||||||
sx = x1 - x0;
|
|
||||||
sy = y1 - y0;
|
|
||||||
if (sx == 0 || sy == 0 || sx > SIXEL_WIDTH_LIMIT ||
|
|
||||||
sy > SIXEL_HEIGHT_LIMIT)
|
|
||||||
return (NULL);
|
|
||||||
|
|
||||||
/* Map the requested cell crop to the source image's pixel rectangle. */
|
|
||||||
image_get_pixel_rect(im, ox, oy, cells_x, cells_y, &sourcex0,
|
|
||||||
&sourcey0, &sourcewidth, &sourceheight);
|
|
||||||
if (sourcewidth == 0 || sourceheight == 0)
|
|
||||||
return (NULL);
|
|
||||||
|
|
||||||
/* Build an adaptive palette from the visible nontransparent pixels. */
|
|
||||||
hg = xcalloc(SIXEL_HISTOGRAM_SIZE, sizeof *hg);
|
|
||||||
for (y = 0; y < sy; y++) {
|
|
||||||
for (x = 0; x < sx; x++) {
|
|
||||||
pixel = sixel_from_image_pixel(&source, sourcex0, sourcey0,
|
|
||||||
sourcewidth, sourceheight, sx, sy, x, y);
|
|
||||||
if (pixel[3] == 0)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
/* Add this opaque pixel to its 5-bit RGB histogram bucket. */
|
|
||||||
index = ((pixel[0] >> 3) << 10)|
|
|
||||||
((pixel[1] >> 3) << 5)|(pixel[2] >> 3);
|
|
||||||
entry = &hg[index];
|
|
||||||
entry->count++;
|
|
||||||
entry->red += pixel[0];
|
|
||||||
entry->green += pixel[1];
|
|
||||||
entry->blue += pixel[2];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ncolours = sixel_make_palette(hg, palette);
|
|
||||||
free(hg);
|
|
||||||
if (ncolours == 0)
|
|
||||||
return (NULL);
|
|
||||||
|
|
||||||
/* Create the indexed SIXEL image and convert its palette to SIXEL RGB. */
|
|
||||||
si = xcalloc(1, sizeof *si);
|
|
||||||
si->xpixel = xpixel;
|
|
||||||
si->ypixel = ypixel;
|
|
||||||
si->p2 = 1;
|
|
||||||
si->set_ra = 1;
|
|
||||||
si->ra_x = sx;
|
|
||||||
si->ra_y = sy;
|
|
||||||
si->ncolours = si->used_colours = ncolours;
|
|
||||||
si->colours = xcalloc(si->ncolours, sizeof *si->colours);
|
|
||||||
for (i = 0; i < si->ncolours; i++) {
|
|
||||||
red = (palette[i].red * 100 + 127) / 255;
|
|
||||||
green = (palette[i].green * 100 + 127) / 255;
|
|
||||||
blue = (palette[i].blue * 100 + 127) / 255;
|
|
||||||
si->colours[i] = (2U << 25)|(red << 16)|(green << 8)|blue;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Floyd-Steinberg dither colour and alpha into the indexed image. */
|
|
||||||
cache = xmalloc(SIXEL_HISTOGRAM_SIZE * sizeof *cache);
|
|
||||||
memset(cache, 0xff, SIXEL_HISTOGRAM_SIZE * sizeof *cache);
|
|
||||||
current = xcalloc(((size_t)sx + 2) * 4, sizeof *current);
|
|
||||||
next = xcalloc(((size_t)sx + 2) * 4, sizeof *next);
|
|
||||||
for (y = 0; y < sy; y++) {
|
|
||||||
for (x = 0; x < sx; x++) {
|
|
||||||
pixel = sixel_from_image_pixel(&source, sourcex0, sourcey0,
|
|
||||||
sourcewidth, sourceheight, sx, sy, x, y);
|
|
||||||
error_index = (x + 1) * 4;
|
|
||||||
/* SIXEL pixels are binary, so dither alpha separately. */
|
|
||||||
alpha = sixel_clamp_colour((int)pixel[3] +
|
|
||||||
current[error_index + 3] / 16);
|
|
||||||
alpha_error = (int)alpha;
|
|
||||||
if (alpha >= 128) {
|
|
||||||
alpha_error -= 255;
|
|
||||||
red = sixel_clamp_colour((int)pixel[0] +
|
|
||||||
current[error_index] / 16);
|
|
||||||
green = sixel_clamp_colour((int)pixel[1] +
|
|
||||||
current[error_index + 1] / 16);
|
|
||||||
blue = sixel_clamp_colour((int)pixel[2] +
|
|
||||||
current[error_index + 2] / 16);
|
|
||||||
colour = sixel_nearest_colour(palette, ncolours, cache,
|
|
||||||
red, green, blue);
|
|
||||||
if (sixel_set_pixel(si, x, y, colour + 1) != 0)
|
|
||||||
goto fail;
|
|
||||||
|
|
||||||
/* Calculate the RGB error introduced by palette quantization. */
|
|
||||||
red_error = (int)red - palette[colour].red;
|
|
||||||
green_error = (int)green - palette[colour].green;
|
|
||||||
blue_error = (int)blue - palette[colour].blue;
|
|
||||||
/*
|
|
||||||
* Diffuse the error with the Floyd-Steinberg 7/16, 3/16,
|
|
||||||
* 5/16, 1/16 kernel; the accumulated error is divided by 16.
|
|
||||||
*/
|
|
||||||
current[error_index + 4] += red_error * 7;
|
|
||||||
current[error_index + 5] += green_error * 7;
|
|
||||||
current[error_index + 6] += blue_error * 7;
|
|
||||||
next[error_index - 4] += red_error * 3;
|
|
||||||
next[error_index - 3] += green_error * 3;
|
|
||||||
next[error_index - 2] += blue_error * 3;
|
|
||||||
next[error_index] += red_error * 5;
|
|
||||||
next[error_index + 1] += green_error * 5;
|
|
||||||
next[error_index + 2] += blue_error * 5;
|
|
||||||
next[error_index + 4] += red_error;
|
|
||||||
next[error_index + 5] += green_error;
|
|
||||||
next[error_index + 6] += blue_error;
|
|
||||||
}
|
|
||||||
/* Diffuse alpha independently using the same kernel. */
|
|
||||||
current[error_index + 7] += alpha_error * 7;
|
|
||||||
next[error_index - 1] += alpha_error * 3;
|
|
||||||
next[error_index + 3] += alpha_error * 5;
|
|
||||||
next[error_index + 7] += alpha_error;
|
|
||||||
}
|
|
||||||
/* Advance to the next output row's accumulated error. */
|
|
||||||
tmp = current;
|
|
||||||
current = next;
|
|
||||||
next = tmp;
|
|
||||||
|
|
||||||
/* Reuse the old row buffer to accumulate the row after that. */
|
|
||||||
memset(next, 0, ((size_t)sx + 2) * 4 * sizeof *next);
|
|
||||||
}
|
|
||||||
|
|
||||||
free(current);
|
|
||||||
free(next);
|
|
||||||
free(cache);
|
|
||||||
return (si);
|
|
||||||
|
|
||||||
fail:
|
|
||||||
/* Discard a partially built image after an allocation or size failure. */
|
|
||||||
free(current);
|
|
||||||
free(next);
|
|
||||||
free(cache);
|
|
||||||
sixel_free(si);
|
|
||||||
return (NULL);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Return the SIXEL output cache for a terminal. */
|
|
||||||
static struct sixel_output *
|
|
||||||
sixel_get_output(struct tty *tty)
|
|
||||||
{
|
|
||||||
struct sixel_output *so = tty->image_data;
|
|
||||||
|
|
||||||
if (so == NULL) {
|
|
||||||
so = xcalloc(1, sizeof *so);
|
|
||||||
tty->image_data = so;
|
|
||||||
}
|
|
||||||
return (so);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Return the memory used by an indexed SIXEL image. */
|
|
||||||
static size_t
|
|
||||||
sixel_image_size(struct sixel_image *si)
|
|
||||||
{
|
|
||||||
uint64_t size;
|
|
||||||
|
|
||||||
if ((uint64_t)si->x * si->y > SIZE_MAX / sizeof(uint16_t))
|
|
||||||
return (0);
|
|
||||||
size = (uint64_t)si->x * si->y * sizeof(uint16_t);
|
|
||||||
if ((uint64_t)si->ncolours * sizeof *si->colours > SIZE_MAX - size)
|
|
||||||
return (0);
|
|
||||||
size += (uint64_t)si->ncolours * sizeof *si->colours;
|
|
||||||
if (size > SIZE_MAX)
|
|
||||||
return (0);
|
|
||||||
return (size);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Remove an image from the SIXEL output cache. */
|
|
||||||
static void
|
|
||||||
sixel_remove_cache(struct sixel_output *so, struct sixel_image_cache **pp)
|
|
||||||
{
|
|
||||||
struct sixel_image_cache *cache = *pp;
|
|
||||||
|
|
||||||
*pp = cache->next;
|
|
||||||
so->size -= cache->size;
|
|
||||||
sixel_free(cache->si);
|
|
||||||
free(cache);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Drop SIXEL cache entries whose source images have gone away. */
|
|
||||||
static void
|
|
||||||
sixel_collect_images(struct sixel_output *so)
|
|
||||||
{
|
|
||||||
struct sixel_image_cache **pp, *cache;
|
|
||||||
|
|
||||||
for (pp = &so->images; (cache = *pp) != NULL; ) {
|
|
||||||
if (image_find(cache->server_id) == NULL)
|
|
||||||
sixel_remove_cache(so, pp);
|
|
||||||
else
|
|
||||||
pp = &cache->next;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Free SIXEL output state for a terminal. */
|
|
||||||
void
|
|
||||||
sixel_free_output(struct tty *tty, __unused int send)
|
|
||||||
{
|
|
||||||
struct sixel_output *so = tty->image_data;
|
|
||||||
struct sixel_image_cache *cache, *next;
|
|
||||||
|
|
||||||
if (so == NULL)
|
|
||||||
return;
|
|
||||||
for (cache = so->images; cache != NULL; cache = next) {
|
|
||||||
next = cache->next;
|
|
||||||
sixel_free(cache->si);
|
|
||||||
free(cache);
|
|
||||||
}
|
|
||||||
free(so);
|
|
||||||
tty->image_data = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Render an image at a terminal's current pixel geometry. */
|
|
||||||
static struct sixel_image *
|
|
||||||
sixel_render_image(struct image *im, u_int xpixel, u_int ypixel)
|
|
||||||
{
|
|
||||||
struct sixel_image *original;
|
|
||||||
u_int sx, sy;
|
|
||||||
|
|
||||||
image_get_size_in_cells(im, &sx, &sy);
|
|
||||||
/* Preserve SIXEL's original palette and indexed pixels when possible. */
|
|
||||||
original = image_get_sixel(im);
|
|
||||||
if (original != NULL)
|
|
||||||
return (sixel_scale(original, xpixel, ypixel, 0, 0, sx, sy, 1));
|
|
||||||
return (sixel_from_image(im, 0, 0, sx, sy, xpixel, ypixel));
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Return a rendered image from the SIXEL output cache. */
|
|
||||||
static struct sixel_image *
|
|
||||||
sixel_get_image(struct tty *tty, struct image *im)
|
|
||||||
{
|
|
||||||
struct sixel_output *so = sixel_get_output(tty);
|
|
||||||
struct sixel_image_cache **pp, *cache, **oldest;
|
|
||||||
struct sixel_image *si;
|
|
||||||
size_t size;
|
|
||||||
|
|
||||||
sixel_collect_images(so);
|
|
||||||
for (cache = so->images; cache != NULL; cache = cache->next) {
|
|
||||||
if (cache->server_id != image_get_id(im) ||
|
|
||||||
cache->xpixel != tty->xpixel ||
|
|
||||||
cache->ypixel != tty->ypixel)
|
|
||||||
continue;
|
|
||||||
cache->age = ++so->age;
|
|
||||||
return (cache->si);
|
|
||||||
}
|
|
||||||
|
|
||||||
si = sixel_render_image(im, tty->xpixel, tty->ypixel);
|
|
||||||
if (si == NULL)
|
|
||||||
return (NULL);
|
|
||||||
size = sixel_image_size(si);
|
|
||||||
if (size == 0 || size > IMAGE_SIZE_LIMIT) {
|
|
||||||
/* The renderer still has a usable image, but it is not cacheable. */
|
|
||||||
return (si);
|
|
||||||
}
|
|
||||||
while (so->size > IMAGE_SIZE_LIMIT - size) {
|
|
||||||
oldest = NULL;
|
|
||||||
for (pp = &so->images; (cache = *pp) != NULL;
|
|
||||||
pp = &cache->next) {
|
|
||||||
if (oldest == NULL || cache->age < (*oldest)->age)
|
|
||||||
oldest = pp;
|
|
||||||
}
|
|
||||||
if (oldest == NULL)
|
|
||||||
break;
|
|
||||||
sixel_remove_cache(so, oldest);
|
|
||||||
}
|
|
||||||
cache = xcalloc(1, sizeof *cache);
|
|
||||||
cache->server_id = image_get_id(im);
|
|
||||||
cache->xpixel = tty->xpixel;
|
|
||||||
cache->ypixel = tty->ypixel;
|
|
||||||
cache->size = size;
|
|
||||||
cache->age = ++so->age;
|
|
||||||
cache->si = si;
|
|
||||||
cache->next = so->images;
|
|
||||||
so->images = cache;
|
|
||||||
so->size += size;
|
|
||||||
return (si);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Return if a SIXEL image is held by the output cache. */
|
|
||||||
static int
|
|
||||||
sixel_image_is_cached(struct tty *tty, struct sixel_image *si)
|
|
||||||
{
|
|
||||||
struct sixel_output *so = tty->image_data;
|
|
||||||
struct sixel_image_cache *cache;
|
|
||||||
|
|
||||||
if (so == NULL)
|
|
||||||
return (0);
|
|
||||||
for (cache = so->images; cache != NULL; cache = cache->next) {
|
|
||||||
if (cache->si == si)
|
|
||||||
return (1);
|
|
||||||
}
|
|
||||||
return (0);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Draw an image rectangle with SIXEL output. */
|
|
||||||
void
|
|
||||||
sixel_draw_rect(struct tty *tty, const struct image_rect *rectangle,
|
|
||||||
__unused const struct tty_style_ctx *style_ctx)
|
|
||||||
{
|
|
||||||
struct sixel_image *si, *crop;
|
|
||||||
char *data;
|
|
||||||
size_t size;
|
|
||||||
u_int source_x, source_y, width, height;
|
|
||||||
u_int destination_x, destination_y;
|
|
||||||
|
|
||||||
si = sixel_get_image(tty, image_rect_get_image(rectangle));
|
|
||||||
if (si == NULL)
|
|
||||||
return;
|
|
||||||
image_rect_get_coords(rectangle, &source_x, &source_y, &width,
|
|
||||||
&height, &destination_x, &destination_y);
|
|
||||||
crop = sixel_scale(si, tty->xpixel, tty->ypixel,
|
|
||||||
source_x, source_y, width, height, 1);
|
|
||||||
if (!sixel_image_is_cached(tty, si))
|
|
||||||
sixel_free(si);
|
|
||||||
if (crop == NULL)
|
|
||||||
return;
|
|
||||||
data = sixel_print(crop, NULL, &size);
|
|
||||||
sixel_free(crop);
|
|
||||||
if (data == NULL)
|
|
||||||
return;
|
|
||||||
tty_region_off(tty);
|
|
||||||
tty_margin_off(tty);
|
|
||||||
tty_cursor(tty, destination_x, destination_y);
|
|
||||||
tty->flags |= TTY_NOBLOCK;
|
|
||||||
tty_putn(tty, data, size, 0);
|
|
||||||
tty_invalidate(tty);
|
|
||||||
free(data);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Convert a SIXEL image to a fallback screen. */
|
|
||||||
struct screen *
|
struct screen *
|
||||||
sixel_to_screen(struct sixel_image *si)
|
sixel_to_screen(struct sixel_image *si)
|
||||||
{
|
{
|
||||||
|
|||||||
849
image.c
849
image.c
@@ -2,7 +2,6 @@
|
|||||||
|
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>
|
* Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>
|
||||||
* Copyright (c) 2026 Michael Grant <mgrant@grant.org>
|
|
||||||
*
|
*
|
||||||
* Permission to use, copy, modify, and distribute this software for any
|
* Permission to use, copy, modify, and distribute this software for any
|
||||||
* purpose with or without fee is hereby granted, provided that the above
|
* purpose with or without fee is hereby granted, provided that the above
|
||||||
@@ -19,740 +18,210 @@
|
|||||||
|
|
||||||
#include <sys/types.h>
|
#include <sys/types.h>
|
||||||
|
|
||||||
#include <limits.h>
|
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
#include "tmux.h"
|
#include "tmux.h"
|
||||||
|
|
||||||
/* An average of part of an image cell. RGB is premultiplied. */
|
static struct images all_images = TAILQ_HEAD_INITIALIZER(all_images);
|
||||||
struct image_sample {
|
static u_int all_images_count;
|
||||||
u_char red;
|
#define MAX_IMAGE_COUNT 20
|
||||||
u_char green;
|
|
||||||
u_char blue;
|
|
||||||
u_char alpha;
|
|
||||||
u_char brightness;
|
|
||||||
};
|
|
||||||
|
|
||||||
/* Half blocks, quadrants and sextants all divide evenly into a 2 by 6 grid. */
|
static void printflike(3, 4)
|
||||||
#define IMAGE_SAMPLE_COLUMNS 2
|
image_log(struct image *im, const char* from, const char* fmt, ...)
|
||||||
#define IMAGE_SAMPLE_ROWS 6
|
|
||||||
struct image_cell {
|
|
||||||
struct image_sample whole;
|
|
||||||
struct image_sample samples[IMAGE_SAMPLE_ROWS][IMAGE_SAMPLE_COLUMNS];
|
|
||||||
};
|
|
||||||
|
|
||||||
/* Immutable image data and cell geometry. */
|
|
||||||
struct image {
|
|
||||||
u_int id;
|
|
||||||
u_short grid_id;
|
|
||||||
u_int references;
|
|
||||||
u_int width;
|
|
||||||
u_int height;
|
|
||||||
u_int canvas_width;
|
|
||||||
u_int canvas_height;
|
|
||||||
u_int sx;
|
|
||||||
u_int sy;
|
|
||||||
size_t stride;
|
|
||||||
size_t size;
|
|
||||||
u_char *pixels;
|
|
||||||
/* Original indexed SIXEL data, if this image arrived as SIXEL. */
|
|
||||||
struct sixel_image *sixel;
|
|
||||||
struct image_cell *cells; /* lazily generated text samples */
|
|
||||||
struct image_fallback_data *fallback_data;
|
|
||||||
|
|
||||||
RB_ENTRY(image) entry;
|
|
||||||
};
|
|
||||||
RB_HEAD(images, image);
|
|
||||||
|
|
||||||
/* A cell-aligned part of an image to draw at a terminal position. */
|
|
||||||
struct image_rect {
|
|
||||||
struct image *image;
|
|
||||||
struct grid_cell cell;
|
|
||||||
u_int source_x;
|
|
||||||
u_int source_y;
|
|
||||||
u_int width;
|
|
||||||
u_int height;
|
|
||||||
u_int destination_x;
|
|
||||||
u_int destination_y;
|
|
||||||
};
|
|
||||||
|
|
||||||
static struct images images = RB_INITIALIZER(&images);
|
|
||||||
static u_int image_next_id;
|
|
||||||
static u_short image_next_grid_id;
|
|
||||||
static struct image *image_grid_ids[USHRT_MAX + 1];
|
|
||||||
|
|
||||||
struct image_backend {
|
|
||||||
const char *name;
|
|
||||||
int flags;
|
|
||||||
void (*draw_rect)(struct tty *,
|
|
||||||
const struct image_rect *, const struct tty_style_ctx *);
|
|
||||||
void (*free)(struct tty *, int);
|
|
||||||
};
|
|
||||||
|
|
||||||
static const struct image_backend image_backend_fallback = {
|
|
||||||
"fallback", IMAGE_BACKEND_SCROLLS, NULL, NULL
|
|
||||||
};
|
|
||||||
static const struct image_backend image_backend_sixel = {
|
|
||||||
"sixel", IMAGE_BACKEND_GRAPHICAL|IMAGE_BACKEND_TEMPORAL,
|
|
||||||
sixel_draw_rect, sixel_free_output
|
|
||||||
};
|
|
||||||
|
|
||||||
/* Find the image backend supported by a terminal. */
|
|
||||||
static const struct image_backend *
|
|
||||||
image_tty_find_backend(struct tty *tty)
|
|
||||||
{
|
{
|
||||||
if (tty->term != NULL && tty->term->flags & TERM_SIXEL &&
|
va_list ap;
|
||||||
tty->xpixel != 0 && tty->ypixel != 0)
|
char s[128];
|
||||||
return (&image_backend_sixel);
|
|
||||||
return (&image_backend_fallback);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Update a terminal's image backend after its capabilities change. */
|
if (log_get_level() == 0)
|
||||||
void
|
|
||||||
image_tty_update(struct tty *tty)
|
|
||||||
{
|
|
||||||
const struct image_backend *backend;
|
|
||||||
|
|
||||||
backend = image_tty_find_backend(tty);
|
|
||||||
if (tty->image_backend == backend)
|
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (tty->image_backend != NULL && tty->image_backend->free != NULL)
|
if (fmt == NULL) {
|
||||||
tty->image_backend->free(tty, !!(tty->flags & TTY_OPENED));
|
log_debug("%s: %p (%ux%u %u,%u)", from, im, im->sx, im->sy,
|
||||||
tty->image_data = NULL;
|
im->px, im->py);
|
||||||
tty->image_backend = backend;
|
return;
|
||||||
log_debug("%s: %s image backend is %s", __func__,
|
}
|
||||||
tty->client->name, backend->name);
|
|
||||||
|
va_start(ap, fmt);
|
||||||
|
vsnprintf(s, sizeof s, fmt, ap);
|
||||||
|
va_end(ap);
|
||||||
|
|
||||||
|
log_debug("%s: %p (%ux%u %u,%u): %s", from, im, im->sx, im->sy,
|
||||||
|
im->px, im->py, s);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Return the flags for a terminal's image backend. */
|
|
||||||
int
|
|
||||||
image_backend_flags(struct tty *tty)
|
|
||||||
{
|
|
||||||
image_tty_update(tty);
|
|
||||||
return (tty->image_backend->flags);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Discard image backend state after a terminal geometry change. */
|
|
||||||
void
|
|
||||||
image_tty_geometry_changed(struct tty *tty)
|
|
||||||
{
|
|
||||||
image_tty_update(tty);
|
|
||||||
if (tty->image_backend->free != NULL)
|
|
||||||
tty->image_backend->free(tty, !!(tty->flags & TTY_OPENED));
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Free image backend state for a terminal. */
|
|
||||||
void
|
|
||||||
image_tty_free(struct tty *tty, int send)
|
|
||||||
{
|
|
||||||
if (tty->image_backend != NULL && tty->image_backend->free != NULL)
|
|
||||||
tty->image_backend->free(tty, send);
|
|
||||||
tty->image_backend = NULL;
|
|
||||||
tty->image_data = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Compare images by server ID. */
|
|
||||||
static int
|
|
||||||
image_cmp(struct image *a, struct image *b)
|
|
||||||
{
|
|
||||||
if (a->id < b->id)
|
|
||||||
return (-1);
|
|
||||||
if (a->id > b->id)
|
|
||||||
return (1);
|
|
||||||
return (0);
|
|
||||||
}
|
|
||||||
RB_GENERATE_STATIC(images, image, entry, image_cmp);
|
|
||||||
|
|
||||||
/* Average a rectangle of image pixels into one fallback sample. */
|
|
||||||
static void
|
static void
|
||||||
image_sample(struct image *im, uint64_t sample_x, uint64_t sample_y,
|
image_free(struct image *im)
|
||||||
uint64_t sample_columns, uint64_t sample_rows, struct image_sample *sample)
|
|
||||||
{
|
{
|
||||||
const u_char *pixel;
|
image_log(im, __func__, NULL);
|
||||||
uint64_t red = 0, green = 0, blue = 0, alpha = 0;
|
|
||||||
uint64_t brightness = 0, count = 0;
|
|
||||||
u_int x, y, x0, x1, y0, y1;
|
|
||||||
|
|
||||||
x0 = sample_x * im->canvas_width / sample_columns;
|
TAILQ_REMOVE(&all_images, im, all_entry);
|
||||||
x1 = ((sample_x + 1) * im->canvas_width + sample_columns - 1) /
|
all_images_count--;
|
||||||
sample_columns;
|
|
||||||
y0 = sample_y * im->canvas_height / sample_rows;
|
|
||||||
y1 = ((sample_y + 1) * im->canvas_height + sample_rows - 1) /
|
|
||||||
sample_rows;
|
|
||||||
if (x1 <= x0)
|
|
||||||
x1 = x0 + 1;
|
|
||||||
if (y1 <= y0)
|
|
||||||
y1 = y0 + 1;
|
|
||||||
count = (uint64_t)(x1 - x0) * (y1 - y0);
|
|
||||||
if (x0 >= im->width || y0 >= im->height)
|
|
||||||
return;
|
|
||||||
if (x1 > im->width)
|
|
||||||
x1 = im->width;
|
|
||||||
if (y1 > im->height)
|
|
||||||
y1 = im->height;
|
|
||||||
|
|
||||||
for (y = y0; y < y1; y++) {
|
TAILQ_REMOVE(im->list, im, entry);
|
||||||
for (x = x0; x < x1; x++) {
|
sixel_free(im->data);
|
||||||
pixel = im->pixels + y * im->stride + x * 4;
|
free(im->fallback);
|
||||||
red += pixel[0] * pixel[3] / 255;
|
|
||||||
green += pixel[1] * pixel[3] / 255;
|
|
||||||
blue += pixel[2] * pixel[3] / 255;
|
|
||||||
alpha += pixel[3];
|
|
||||||
brightness += ((2126ULL * pixel[0] +
|
|
||||||
7152ULL * pixel[1] + 722ULL * pixel[2]) / 10000) *
|
|
||||||
pixel[3] / 255;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (count == 0)
|
|
||||||
return;
|
|
||||||
sample->red = red / count;
|
|
||||||
sample->green = green / count;
|
|
||||||
sample->blue = blue / count;
|
|
||||||
sample->alpha = alpha / count;
|
|
||||||
sample->brightness = brightness / count;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Build fallback samples for every image cell. */
|
|
||||||
static void
|
|
||||||
image_make_cells(struct image *im)
|
|
||||||
{
|
|
||||||
struct image_cell *cell;
|
|
||||||
uint64_t columns, rows;
|
|
||||||
u_int x, y, sample_x, sample_y;
|
|
||||||
|
|
||||||
columns = (uint64_t)im->sx * IMAGE_SAMPLE_COLUMNS;
|
|
||||||
rows = (uint64_t)im->sy * IMAGE_SAMPLE_ROWS;
|
|
||||||
im->cells = xcalloc((size_t)im->sx * im->sy, sizeof *im->cells);
|
|
||||||
for (y = 0; y < im->sy; y++) {
|
|
||||||
for (x = 0; x < im->sx; x++) {
|
|
||||||
cell = &im->cells[(size_t)y * im->sx + x];
|
|
||||||
image_sample(im, x, y, im->sx, im->sy, &cell->whole);
|
|
||||||
for (sample_y = 0; sample_y < IMAGE_SAMPLE_ROWS;
|
|
||||||
sample_y++) {
|
|
||||||
for (sample_x = 0;
|
|
||||||
sample_x < IMAGE_SAMPLE_COLUMNS; sample_x++) {
|
|
||||||
image_sample(im,
|
|
||||||
(uint64_t)x * IMAGE_SAMPLE_COLUMNS + sample_x,
|
|
||||||
(uint64_t)y * IMAGE_SAMPLE_ROWS + sample_y,
|
|
||||||
columns, rows,
|
|
||||||
&cell->samples[sample_y][sample_x]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Find an image by server ID. */
|
|
||||||
struct image *
|
|
||||||
image_find(u_int id)
|
|
||||||
{
|
|
||||||
struct image find;
|
|
||||||
|
|
||||||
find.id = id;
|
|
||||||
return (RB_FIND(images, &images, &find));
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Return an image's server ID. */
|
|
||||||
u_int
|
|
||||||
image_get_id(const struct image *im)
|
|
||||||
{
|
|
||||||
return (im->id);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Return an image's pixel dimensions. */
|
|
||||||
void
|
|
||||||
image_get_size(const struct image *im, u_int *width, u_int *height)
|
|
||||||
{
|
|
||||||
if (width != NULL)
|
|
||||||
*width = im->width;
|
|
||||||
if (height != NULL)
|
|
||||||
*height = im->height;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Return an image canvas's pixel dimensions. */
|
|
||||||
void
|
|
||||||
image_get_canvas_size(const struct image *im, u_int *width,
|
|
||||||
u_int *height)
|
|
||||||
{
|
|
||||||
if (width != NULL)
|
|
||||||
*width = im->canvas_width;
|
|
||||||
if (height != NULL)
|
|
||||||
*height = im->canvas_height;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Return an image's cell dimensions. */
|
|
||||||
void
|
|
||||||
image_get_size_in_cells(const struct image *im, u_int *sx, u_int *sy)
|
|
||||||
{
|
|
||||||
if (sx != NULL)
|
|
||||||
*sx = im->sx;
|
|
||||||
if (sy != NULL)
|
|
||||||
*sy = im->sy;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Return an image's RGBA pixels and layout. */
|
|
||||||
const u_char *
|
|
||||||
image_get_pixels(const struct image *im, size_t *stride, size_t *size)
|
|
||||||
{
|
|
||||||
if (stride != NULL)
|
|
||||||
*stride = im->stride;
|
|
||||||
if (size != NULL)
|
|
||||||
*size = im->size;
|
|
||||||
return (im->pixels);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Return an image's original SIXEL data. */
|
|
||||||
struct sixel_image *
|
|
||||||
image_get_sixel(const struct image *im)
|
|
||||||
{
|
|
||||||
return (im->sixel);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Associate original SIXEL data with an image. */
|
|
||||||
void
|
|
||||||
image_set_sixel(struct image *im, struct sixel_image *si)
|
|
||||||
{
|
|
||||||
im->sixel = si;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Return an image's cached fallback rendering data. */
|
|
||||||
struct image_fallback_data *
|
|
||||||
image_get_fallback_data(const struct image *im)
|
|
||||||
{
|
|
||||||
return (im->fallback_data);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Associate cached fallback rendering data with an image. */
|
|
||||||
void
|
|
||||||
image_set_fallback_data(struct image *im, struct image_fallback_data *data)
|
|
||||||
{
|
|
||||||
im->fallback_data = data;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Return the image for a drawing rectangle. */
|
|
||||||
struct image *
|
|
||||||
image_rect_get_image(const struct image_rect *rectangle)
|
|
||||||
{
|
|
||||||
return (rectangle->image);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Return the source and destination coordinates of a drawing rectangle. */
|
|
||||||
void
|
|
||||||
image_rect_get_coords(const struct image_rect *rectangle,
|
|
||||||
u_int *source_x, u_int *source_y, u_int *width, u_int *height,
|
|
||||||
u_int *destination_x, u_int *destination_y)
|
|
||||||
{
|
|
||||||
*source_x = rectangle->source_x;
|
|
||||||
*source_y = rectangle->source_y;
|
|
||||||
*width = rectangle->width;
|
|
||||||
*height = rectangle->height;
|
|
||||||
*destination_x = rectangle->destination_x;
|
|
||||||
*destination_y = rectangle->destination_y;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Create and register an immutable image. */
|
|
||||||
struct image *
|
|
||||||
image_create(u_int width, u_int height, u_int canvas_width,
|
|
||||||
u_int canvas_height, u_int sx, u_int sy, u_char *pixels)
|
|
||||||
{
|
|
||||||
struct image *im;
|
|
||||||
u_int i;
|
|
||||||
|
|
||||||
if (width == 0 || height == 0 || canvas_width < width ||
|
|
||||||
canvas_height < height || sx == 0 || sy == 0 || pixels == NULL)
|
|
||||||
return (NULL);
|
|
||||||
if ((uint64_t)width * height * 4 > SIZE_MAX)
|
|
||||||
return (NULL);
|
|
||||||
if ((uint64_t)sx * sy > SIZE_MAX / sizeof *im->cells)
|
|
||||||
return (NULL);
|
|
||||||
if (sx > USHRT_MAX || sy > USHRT_MAX)
|
|
||||||
return (NULL);
|
|
||||||
for (i = 0; i < USHRT_MAX; i++) {
|
|
||||||
if (++image_next_grid_id == 0)
|
|
||||||
image_next_grid_id++;
|
|
||||||
if (image_grid_ids[image_next_grid_id] == NULL)
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if (i == USHRT_MAX)
|
|
||||||
return (NULL);
|
|
||||||
|
|
||||||
im = xcalloc(1, sizeof *im);
|
|
||||||
do {
|
|
||||||
if (++image_next_id == 0)
|
|
||||||
image_next_id++;
|
|
||||||
im->id = image_next_id;
|
|
||||||
} while (image_find(im->id) != NULL);
|
|
||||||
|
|
||||||
im->references = 1;
|
|
||||||
im->grid_id = image_next_grid_id;
|
|
||||||
im->width = width;
|
|
||||||
im->height = height;
|
|
||||||
im->canvas_width = canvas_width;
|
|
||||||
im->canvas_height = canvas_height;
|
|
||||||
im->sx = sx;
|
|
||||||
im->sy = sy;
|
|
||||||
im->stride = (size_t)width * 4;
|
|
||||||
im->size = im->stride * height;
|
|
||||||
im->pixels = pixels;
|
|
||||||
|
|
||||||
RB_INSERT(images, &images, im);
|
|
||||||
image_grid_ids[im->grid_id] = im;
|
|
||||||
log_debug("%s: image %u is %ux%u pixels on %ux%u canvas, "
|
|
||||||
"%ux%u cells", __func__, im->id, width, height, canvas_width,
|
|
||||||
canvas_height, sx, sy);
|
|
||||||
return (im);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Return the compact grid ID for an image. */
|
|
||||||
u_short
|
|
||||||
image_get_grid_id(u_int id)
|
|
||||||
{
|
|
||||||
struct image *im = image_find(id);
|
|
||||||
|
|
||||||
if (im == NULL)
|
|
||||||
return (0);
|
|
||||||
return (im->grid_id);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Return the server image ID for a compact grid ID. */
|
|
||||||
u_int
|
|
||||||
image_get_id_by_grid_id(u_short grid_id)
|
|
||||||
{
|
|
||||||
struct image *im;
|
|
||||||
|
|
||||||
if (grid_id == 0 || (im = image_grid_ids[grid_id]) == NULL)
|
|
||||||
return (0);
|
|
||||||
return (im->id);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Add a reference to an image. */
|
|
||||||
void
|
|
||||||
image_ref(u_int id)
|
|
||||||
{
|
|
||||||
struct image *im = image_find(id);
|
|
||||||
|
|
||||||
if (im == NULL)
|
|
||||||
fatalx("reference to missing image %u", id);
|
|
||||||
if (im->references == UINT_MAX)
|
|
||||||
fatalx("too many references to image %u", id);
|
|
||||||
im->references++;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Drop a reference to an image. */
|
|
||||||
void
|
|
||||||
image_free(u_int id)
|
|
||||||
{
|
|
||||||
struct image *im = image_find(id);
|
|
||||||
|
|
||||||
if (im == NULL)
|
|
||||||
fatalx("free of missing image %u", id);
|
|
||||||
if (--im->references != 0)
|
|
||||||
return;
|
|
||||||
|
|
||||||
log_debug("%s: freeing image %u", __func__, id);
|
|
||||||
RB_REMOVE(images, &images, im);
|
|
||||||
image_grid_ids[im->grid_id] = NULL;
|
|
||||||
free(im->pixels);
|
|
||||||
if (im->sixel != NULL)
|
|
||||||
sixel_free(im->sixel);
|
|
||||||
free(im->cells);
|
|
||||||
image_free_fallback(im);
|
|
||||||
free(im);
|
free(im);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Return a precomputed fallback cell sample. */
|
|
||||||
static const struct image_cell *
|
|
||||||
image_get_cell(struct image *im, u_int x, u_int y)
|
|
||||||
{
|
|
||||||
if (im == NULL || x >= im->sx || y >= im->sy)
|
|
||||||
return (NULL);
|
|
||||||
if (im->cells == NULL)
|
|
||||||
image_make_cells(im);
|
|
||||||
return (&im->cells[(size_t)y * im->sx + x]);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Return the brightness of an image cell. */
|
|
||||||
u_char
|
|
||||||
image_get_brightness(struct image *im, u_int x, u_int y)
|
|
||||||
{
|
|
||||||
const struct image_cell *cell;
|
|
||||||
|
|
||||||
cell = image_get_cell(im, x, y);
|
|
||||||
if (cell == NULL)
|
|
||||||
return (0);
|
|
||||||
return (cell->whole.brightness);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Average part of an image cell for fallback rendering. */
|
|
||||||
void
|
|
||||||
image_get_cell_average(struct image *im, u_int x, u_int y, u_int part_x,
|
|
||||||
u_int part_y, u_int parts_x, u_int parts_y, u_char *red, u_char *green,
|
|
||||||
u_char *blue)
|
|
||||||
{
|
|
||||||
const struct image_cell *cell;
|
|
||||||
u_int ix, iy, x0, x1, y0, y1, count;
|
|
||||||
u_int value_red, value_green, value_blue;
|
|
||||||
|
|
||||||
*red = *green = *blue = 0;
|
|
||||||
if (parts_x == 0 || parts_y == 0 || part_x >= parts_x ||
|
|
||||||
part_y >= parts_y)
|
|
||||||
return;
|
|
||||||
cell = image_get_cell(im, x, y);
|
|
||||||
if (cell == NULL)
|
|
||||||
return;
|
|
||||||
x0 = part_x * IMAGE_SAMPLE_COLUMNS / parts_x;
|
|
||||||
x1 = (part_x + 1) * IMAGE_SAMPLE_COLUMNS / parts_x;
|
|
||||||
y0 = part_y * IMAGE_SAMPLE_ROWS / parts_y;
|
|
||||||
y1 = (part_y + 1) * IMAGE_SAMPLE_ROWS / parts_y;
|
|
||||||
value_red = value_green = value_blue = count = 0;
|
|
||||||
for (iy = y0; iy < y1; iy++) {
|
|
||||||
for (ix = x0; ix < x1; ix++) {
|
|
||||||
value_red += cell->samples[iy][ix].red;
|
|
||||||
value_green += cell->samples[iy][ix].green;
|
|
||||||
value_blue += cell->samples[iy][ix].blue;
|
|
||||||
count++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (count == 0)
|
|
||||||
return;
|
|
||||||
*red = value_red / count;
|
|
||||||
*green = value_green / count;
|
|
||||||
*blue = value_blue / count;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Get the terminal cell used to draw an image marker. */
|
|
||||||
int
|
int
|
||||||
image_get_draw_cell(struct tty *tty, const struct grid_cell *gc,
|
image_free_all(struct screen *s)
|
||||||
struct grid_cell *out, const struct tty_style_ctx *style_ctx)
|
|
||||||
{
|
{
|
||||||
struct image *im = image_find(gc->image_id);
|
struct image *im, *im1;
|
||||||
|
int redraw = !TAILQ_EMPTY(&s->images);
|
||||||
|
|
||||||
if (image_backend_flags(tty) & IMAGE_BACKEND_GRAPHICAL) {
|
if (redraw)
|
||||||
memcpy(out, gc, sizeof *out);
|
log_debug ("%s", __func__);
|
||||||
out->flags &= ~(GRID_FLAG_IMAGE|GRID_FLAG_IMAGE_DAMAGED|
|
TAILQ_FOREACH_SAFE(im, &s->images, entry, im1)
|
||||||
GRID_FLAG_SELECTED);
|
image_free(im);
|
||||||
return (1);
|
return (redraw);
|
||||||
}
|
|
||||||
if (gc->flags & GRID_FLAG_IMAGE_DAMAGED) {
|
|
||||||
memcpy(out, gc, sizeof *out);
|
|
||||||
out->flags &= ~(GRID_FLAG_IMAGE|GRID_FLAG_IMAGE_DAMAGED);
|
|
||||||
return (0);
|
|
||||||
}
|
|
||||||
image_get_fallback_cell(tty, im, gc->image_x, gc->image_y, gc, out,
|
|
||||||
style_ctx);
|
|
||||||
return (0);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Store an image marker in a grid cell. */
|
/* Create text placeholder for an image. */
|
||||||
void
|
static void
|
||||||
image_set_cell(struct grid_cell *gc, struct image *im, u_int x, u_int y)
|
image_fallback(char **ret, u_int sx, u_int sy)
|
||||||
{
|
{
|
||||||
/* Keep the cell contents as the underlay for transparent pixels. */
|
char *buf, *label;
|
||||||
gc->flags &= ~GRID_FLAG_IMAGE_DAMAGED;
|
u_int py, size, lsize;
|
||||||
gc->flags |= GRID_FLAG_IMAGE;
|
|
||||||
gc->image_id = im->id;
|
|
||||||
gc->image_x = x;
|
|
||||||
gc->image_y = y;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Convert a cell-aligned image rectangle into source pixel coordinates. */
|
if (sy == 0) {
|
||||||
void
|
*ret = xstrdup("");
|
||||||
image_get_pixel_rect(const struct image *im, u_int x, u_int y,
|
|
||||||
u_int width, u_int height, u_int *px, u_int *py, u_int *pwidth,
|
|
||||||
u_int *pheight)
|
|
||||||
{
|
|
||||||
u_int x1, y1;
|
|
||||||
|
|
||||||
*px = *py = *pwidth = *pheight = 0;
|
|
||||||
if (im == NULL || x >= im->sx || y >= im->sy || width == 0 ||
|
|
||||||
height == 0)
|
|
||||||
return;
|
|
||||||
if (width > im->sx - x)
|
|
||||||
width = im->sx - x;
|
|
||||||
if (height > im->sy - y)
|
|
||||||
height = im->sy - y;
|
|
||||||
|
|
||||||
*px = (uint64_t)x * im->canvas_width / im->sx;
|
|
||||||
*py = (uint64_t)y * im->canvas_height / im->sy;
|
|
||||||
x1 = ((uint64_t)(x + width) * im->canvas_width + im->sx - 1) /
|
|
||||||
im->sx;
|
|
||||||
y1 = ((uint64_t)(y + height) * im->canvas_height + im->sy - 1) /
|
|
||||||
im->sy;
|
|
||||||
if (*px >= im->width || *py >= im->height) {
|
|
||||||
*px = *py = 0;
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (x1 <= *px)
|
|
||||||
x1 = *px + 1;
|
/* Allocate first line. */
|
||||||
if (y1 <= *py)
|
lsize = xasprintf(&label, "SIXEL IMAGE (%ux%u)\r\n", sx, sy) + 1;
|
||||||
y1 = *py + 1;
|
if (sx < lsize - 3)
|
||||||
if (x1 > im->width)
|
size = lsize - 1;
|
||||||
x1 = im->width;
|
else
|
||||||
if (y1 > im->height)
|
size = sx + 2;
|
||||||
y1 = im->height;
|
|
||||||
*pwidth = x1 - *px;
|
/* Remaining lines. Every placeholder line has \r\n at the end. */
|
||||||
*pheight = y1 - *py;
|
size += (sx + 2) * (sy - 1) + 1;
|
||||||
|
*ret = buf = xmalloc(size);
|
||||||
|
|
||||||
|
/* Render first line. */
|
||||||
|
if (sx < lsize - 3) {
|
||||||
|
memcpy(buf, label, lsize);
|
||||||
|
buf += lsize - 1;
|
||||||
|
} else {
|
||||||
|
memcpy(buf, label, lsize - 3);
|
||||||
|
buf += lsize - 3;
|
||||||
|
memset(buf, '+', sx - lsize + 3);
|
||||||
|
buf += sx - lsize + 3;
|
||||||
|
snprintf(buf, 3, "\r\n");
|
||||||
|
buf += 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Remaining lines. */
|
||||||
|
for (py = 1; py < sy; py++) {
|
||||||
|
memset(buf, '+', sx);
|
||||||
|
buf += sx;
|
||||||
|
snprintf(buf, 3, "\r\n");
|
||||||
|
buf += 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
free(label);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Calculate the cell dimensions required for pixel dimensions. */
|
struct image*
|
||||||
void
|
image_store(struct screen *s, struct sixel_image *si)
|
||||||
image_size_in_cells(u_int width, u_int height, u_int xpixel, u_int ypixel,
|
|
||||||
u_int *sx, u_int *sy)
|
|
||||||
{
|
{
|
||||||
if (xpixel == 0)
|
struct image *im;
|
||||||
xpixel = 8;
|
|
||||||
if (ypixel == 0)
|
im = xcalloc(1, sizeof *im);
|
||||||
ypixel = 16;
|
im->s = s;
|
||||||
*sx = width / xpixel + (width % xpixel != 0);
|
im->data = si;
|
||||||
*sy = height / ypixel + (height % ypixel != 0);
|
|
||||||
|
im->px = s->cx;
|
||||||
|
im->py = s->cy;
|
||||||
|
sixel_size_in_cells(si, &im->sx, &im->sy);
|
||||||
|
|
||||||
|
image_fallback(&im->fallback, im->sx, im->sy);
|
||||||
|
|
||||||
|
image_log(im, __func__, NULL);
|
||||||
|
im->list = &s->images;
|
||||||
|
TAILQ_INSERT_TAIL(im->list, im, entry);
|
||||||
|
|
||||||
|
TAILQ_INSERT_TAIL(&all_images, im, all_entry);
|
||||||
|
if (++all_images_count == MAX_IMAGE_COUNT)
|
||||||
|
image_free(TAILQ_FIRST(&all_images));
|
||||||
|
|
||||||
|
return (im);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Return if a screen area contains image markers. */
|
int
|
||||||
static int
|
image_check_line(struct screen *s, u_int py, u_int ny)
|
||||||
|
{
|
||||||
|
struct image *im, *im1;
|
||||||
|
int redraw = 0, in;
|
||||||
|
|
||||||
|
TAILQ_FOREACH_SAFE(im, &s->images, entry, im1) {
|
||||||
|
in = (py + ny > im->py && py < im->py + im->sy);
|
||||||
|
image_log(im, __func__, "py=%u, ny=%u, in=%d", py, ny, in);
|
||||||
|
if (in) {
|
||||||
|
image_free(im);
|
||||||
|
redraw = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return (redraw);
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
image_check_area(struct screen *s, u_int px, u_int py, u_int nx, u_int ny)
|
image_check_area(struct screen *s, u_int px, u_int py, u_int nx, u_int ny)
|
||||||
{
|
{
|
||||||
struct grid_cell gc;
|
struct image *im, *im1;
|
||||||
u_int x, y, ex, ey;
|
int redraw = 0, in;
|
||||||
u_int sx = screen_size_x(s), sy = screen_size_y(s);
|
|
||||||
|
|
||||||
if (px >= sx || py >= sy || nx == 0 || ny == 0)
|
TAILQ_FOREACH_SAFE(im, &s->images, entry, im1) {
|
||||||
return (0);
|
in = (py < im->py + im->sy &&
|
||||||
if (nx > sx - px)
|
py + ny > im->py &&
|
||||||
ex = sx;
|
px < im->px + im->sx &&
|
||||||
else
|
px + nx > im->px);
|
||||||
ex = px + nx;
|
image_log(im, __func__, "py=%u, ny=%u, in=%d", py, ny, in);
|
||||||
if (ny > sy - py)
|
if (in) {
|
||||||
ey = sy;
|
image_free(im);
|
||||||
else
|
redraw = 1;
|
||||||
ey = py + ny;
|
|
||||||
for (y = py; y < ey; y++) {
|
|
||||||
for (x = px; x < ex; x++) {
|
|
||||||
grid_view_get_cell(s->grid, x, y, &gc);
|
|
||||||
if (gc.flags & GRID_FLAG_IMAGE)
|
|
||||||
return (1);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return (0);
|
return (redraw);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Redraw image markers in a screen area. */
|
int
|
||||||
void
|
image_scroll_up(struct screen *s, u_int lines)
|
||||||
image_redraw_area(struct screen_write_ctx *ctx, u_int px, u_int py, u_int nx,
|
|
||||||
u_int ny)
|
|
||||||
{
|
{
|
||||||
if (ctx->wp != NULL && image_check_area(ctx->s, px, py, nx, ny))
|
struct image *im, *im1;
|
||||||
ctx->wp->flags |= PANE_REDRAW;
|
int redraw = 0;
|
||||||
}
|
u_int sx, sy;
|
||||||
|
struct sixel_image *new;
|
||||||
|
|
||||||
/* Redraw all image markers on a screen. */
|
TAILQ_FOREACH_SAFE(im, &s->images, entry, im1) {
|
||||||
void
|
if (im->py >= lines) {
|
||||||
image_redraw_all(struct screen_write_ctx *ctx)
|
image_log(im, __func__, "1, lines=%u", lines);
|
||||||
{
|
im->py -= lines;
|
||||||
image_redraw_area(ctx, 0, 0, screen_size_x(ctx->s),
|
redraw = 1;
|
||||||
screen_size_y(ctx->s));
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Redraw images after a scrolling operation. */
|
|
||||||
void
|
|
||||||
image_redraw_scroll(struct screen_write_ctx *ctx, __unused u_int lines)
|
|
||||||
{
|
|
||||||
image_redraw_all(ctx);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Draw the graphical image marker runs in one visible scene span. */
|
|
||||||
void
|
|
||||||
image_draw_line(struct tty *tty, struct screen *s, u_int px, u_int py,
|
|
||||||
u_int nx, u_int atx, u_int aty, const struct tty_style_ctx *style_ctx)
|
|
||||||
{
|
|
||||||
const struct image_backend *backend;
|
|
||||||
struct image_rect rectangle;
|
|
||||||
struct grid_cell gc, next;
|
|
||||||
struct image *im;
|
|
||||||
u_int i, run;
|
|
||||||
|
|
||||||
image_tty_update(tty);
|
|
||||||
backend = tty->image_backend;
|
|
||||||
if (~backend->flags & IMAGE_BACKEND_GRAPHICAL)
|
|
||||||
return;
|
|
||||||
|
|
||||||
for (i = 0; i < nx; i += run) {
|
|
||||||
grid_view_get_cell(s->grid, px + i, py, &gc);
|
|
||||||
if (~gc.flags & GRID_FLAG_IMAGE) {
|
|
||||||
run = 1;
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if ((backend->flags & IMAGE_BACKEND_TEMPORAL) &&
|
if (im->py + im->sy <= lines) {
|
||||||
(gc.flags & GRID_FLAG_IMAGE_DAMAGED)) {
|
image_log(im, __func__, "2, lines=%u", lines);
|
||||||
run = 1;
|
image_free(im);
|
||||||
|
redraw = 1;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
im = image_find(gc.image_id);
|
sx = im->sx;
|
||||||
if (im == NULL) {
|
sy = (im->py + im->sy) - lines;
|
||||||
run = 1;
|
image_log(im, __func__, "3, lines=%u, sy=%u", lines, sy);
|
||||||
continue;
|
|
||||||
}
|
new = sixel_scale(im->data, 0, 0, 0, im->sy - sy, sx, sy, 1);
|
||||||
for (run = 1; i + run < nx; run++) {
|
sixel_free(im->data);
|
||||||
grid_view_get_cell(s->grid, px + i + run, py, &next);
|
im->data = new;
|
||||||
if (~next.flags & GRID_FLAG_IMAGE ||
|
|
||||||
((backend->flags & IMAGE_BACKEND_TEMPORAL) &&
|
im->py = 0;
|
||||||
(next.flags & GRID_FLAG_IMAGE_DAMAGED)) ||
|
sixel_size_in_cells(im->data, &im->sx, &im->sy);
|
||||||
next.image_id != gc.image_id ||
|
|
||||||
next.image_y != gc.image_y ||
|
free(im->fallback);
|
||||||
next.image_x != gc.image_x + run)
|
image_fallback(&im->fallback, im->sx, im->sy);
|
||||||
break;
|
redraw = 1;
|
||||||
}
|
|
||||||
rectangle.image = im;
|
|
||||||
memcpy(&rectangle.cell, &gc, sizeof rectangle.cell);
|
|
||||||
rectangle.source_x = gc.image_x;
|
|
||||||
rectangle.source_y = gc.image_y;
|
|
||||||
rectangle.width = run;
|
|
||||||
rectangle.height = 1;
|
|
||||||
rectangle.destination_x = atx + i;
|
|
||||||
rectangle.destination_y = aty;
|
|
||||||
backend->draw_rect(tty, &rectangle, style_ctx);
|
|
||||||
}
|
}
|
||||||
}
|
return (redraw);
|
||||||
|
|
||||||
/*
|
|
||||||
* Put image marker cells at the cursor. The pixel object is immutable; only
|
|
||||||
* the marker rectangle is clipped to the available pane cells.
|
|
||||||
*/
|
|
||||||
void
|
|
||||||
image_write(struct screen_write_ctx *ctx, struct image *im, u_int bg)
|
|
||||||
{
|
|
||||||
struct screen *s = ctx->s;
|
|
||||||
struct grid *gd = s->grid;
|
|
||||||
struct grid_cell gc;
|
|
||||||
u_int cx = s->cx, cy = s->cy;
|
|
||||||
u_int x, y, sx, sy, lines;
|
|
||||||
|
|
||||||
image_get_size_in_cells(im, &sx, &sy);
|
|
||||||
if (sx > screen_size_x(s) - cx)
|
|
||||||
sx = screen_size_x(s) - cx;
|
|
||||||
if (sy > screen_size_y(s) - 1)
|
|
||||||
sy = screen_size_y(s) - 1;
|
|
||||||
if (sx == 0 || sy == 0)
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (screen_size_y(s) - cy <= sy) {
|
|
||||||
lines = sy - (screen_size_y(s) - cy) + 1;
|
|
||||||
screen_write_scrollup(ctx, lines, bg);
|
|
||||||
if (lines > cy)
|
|
||||||
screen_write_cursormove(ctx, -1, 0, 0);
|
|
||||||
else
|
|
||||||
screen_write_cursormove(ctx, -1, cy - lines, 0);
|
|
||||||
cy = s->cy;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (y = 0; y < sy; y++) {
|
|
||||||
for (x = 0; x < sx; x++) {
|
|
||||||
grid_view_get_cell(gd, cx + x, cy + y, &gc);
|
|
||||||
image_set_cell(&gc, im, x, y);
|
|
||||||
grid_view_set_cell(gd, cx + x, cy + y, &gc);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
image_redraw_area(ctx, cx, cy, sx, sy);
|
|
||||||
screen_write_cursormove(ctx, 0, cy + sy, 0);
|
|
||||||
}
|
}
|
||||||
|
|||||||
16
input.c
16
input.c
@@ -1,4 +1,4 @@
|
|||||||
/* $OpenBSD: input.c,v 1.269 2026/07/20 11:16:33 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>
|
||||||
@@ -1049,6 +1049,7 @@ input_parse_buffer(struct window_pane *wp, const u_char *buf, size_t len)
|
|||||||
if (len == 0)
|
if (len == 0)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
wp->output_generation++;
|
||||||
window_update_activity(wp->window);
|
window_update_activity(wp->window);
|
||||||
if (~wp->flags & PANE_ACTIVITY) {
|
if (~wp->flags & PANE_ACTIVITY) {
|
||||||
wp->flags |= PANE_ACTIVITY;
|
wp->flags |= PANE_ACTIVITY;
|
||||||
@@ -1583,7 +1584,7 @@ input_csi_dispatch(struct input_ctx *ictx)
|
|||||||
case -1:
|
case -1:
|
||||||
break;
|
break;
|
||||||
case 0:
|
case 0:
|
||||||
#ifdef ENABLE_IMAGES
|
#ifdef ENABLE_SIXEL
|
||||||
input_reply(ictx, 1, "\033[?1;2;4c");
|
input_reply(ictx, 1, "\033[?1;2;4c");
|
||||||
#else
|
#else
|
||||||
input_reply(ictx, 1, "\033[?1;2c");
|
input_reply(ictx, 1, "\033[?1;2c");
|
||||||
@@ -1978,7 +1979,7 @@ input_csi_dispatch_rm_private(struct input_ctx *ictx)
|
|||||||
screen_write_mode_clear(sctx, MODE_BRACKETPASTE);
|
screen_write_mode_clear(sctx, MODE_BRACKETPASTE);
|
||||||
break;
|
break;
|
||||||
case 2026:
|
case 2026:
|
||||||
screen_write_stop_sync(ictx->wp);
|
screen_write_end_sync(sctx);
|
||||||
break;
|
break;
|
||||||
case 2031:
|
case 2031:
|
||||||
screen_write_mode_clear(sctx, MODE_THEME_UPDATES);
|
screen_write_mode_clear(sctx, MODE_THEME_UPDATES);
|
||||||
@@ -2101,7 +2102,7 @@ input_csi_dispatch_sm_private(struct input_ctx *ictx)
|
|||||||
static void
|
static void
|
||||||
input_csi_dispatch_sm_graphics(__unused struct input_ctx *ictx)
|
input_csi_dispatch_sm_graphics(__unused struct input_ctx *ictx)
|
||||||
{
|
{
|
||||||
#ifdef ENABLE_IMAGES
|
#ifdef ENABLE_SIXEL
|
||||||
int n, m, o;
|
int n, m, o;
|
||||||
|
|
||||||
if (ictx->param_list_len > 3)
|
if (ictx->param_list_len > 3)
|
||||||
@@ -2626,7 +2627,7 @@ input_dcs_dispatch(struct input_ctx *ictx)
|
|||||||
const char prefix[] = "tmux;";
|
const char prefix[] = "tmux;";
|
||||||
const u_int prefixlen = (sizeof prefix) - 1;
|
const u_int prefixlen = (sizeof prefix) - 1;
|
||||||
long long allow_passthrough = 0;
|
long long allow_passthrough = 0;
|
||||||
#ifdef ENABLE_IMAGES
|
#ifdef ENABLE_SIXEL
|
||||||
struct window *w;
|
struct window *w;
|
||||||
struct sixel_image *si;
|
struct sixel_image *si;
|
||||||
int p2;
|
int p2;
|
||||||
@@ -2642,7 +2643,7 @@ input_dcs_dispatch(struct input_ctx *ictx)
|
|||||||
return (0);
|
return (0);
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef ENABLE_IMAGES
|
#ifdef ENABLE_SIXEL
|
||||||
if (wp != NULL && buf[0] == 'q' && ictx->interm_len == 0) {
|
if (wp != NULL && buf[0] == 'q' && ictx->interm_len == 0) {
|
||||||
w = wp->window;
|
w = wp->window;
|
||||||
if (input_split(ictx) != 0)
|
if (input_split(ictx) != 0)
|
||||||
@@ -3077,7 +3078,8 @@ input_osc_10(struct input_ctx *ictx, const char *p)
|
|||||||
return;
|
return;
|
||||||
c = window_pane_get_fg_control_client(wp);
|
c = window_pane_get_fg_control_client(wp);
|
||||||
if (c == -1) {
|
if (c == -1) {
|
||||||
tty_default_colours(&defaults, wp, NULL);
|
tty_default_colours(&defaults, wp, wp->window->active,
|
||||||
|
NULL);
|
||||||
if (COLOUR_DEFAULT(defaults.fg))
|
if (COLOUR_DEFAULT(defaults.fg))
|
||||||
c = window_pane_get_fg(wp);
|
c = window_pane_get_fg(wp);
|
||||||
else
|
else
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
/* $OpenBSD: key-bindings.c,v 1.190 2026/07/22 19:23:59 nicm Exp $ */
|
/* $OpenBSD: key-bindings.c,v 1.192 2026/08/06 09:05:04 nicm Exp $ */
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>
|
* Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>
|
||||||
@@ -31,6 +31,8 @@
|
|||||||
" 'Rename' 'r' {command-prompt -I '#S' {rename-session -- '%%'}}" \
|
" 'Rename' 'r' {command-prompt -I '#S' {rename-session -- '%%'}}" \
|
||||||
" 'Detach' 'd' {detach-client}" \
|
" 'Detach' 'd' {detach-client}" \
|
||||||
" ''" \
|
" ''" \
|
||||||
|
" '#{?local_active_window,Shared Window,Local Window}' 'L' {select-window #{?local_active_window,-S,-L}}" \
|
||||||
|
" ''" \
|
||||||
" 'New Session' 's' {new-session}" \
|
" 'New Session' 's' {new-session}" \
|
||||||
" 'New Window' 'w' {new-window}"
|
" 'New Window' 'w' {new-window}"
|
||||||
#define DEFAULT_WINDOW_MENU \
|
#define DEFAULT_WINDOW_MENU \
|
||||||
@@ -72,6 +74,8 @@
|
|||||||
" '#{?#{!:#{pane_floating_flag}},Horizontal Split,}' 'h' {split-window -h}" \
|
" '#{?#{!:#{pane_floating_flag}},Horizontal Split,}' 'h' {split-window -h}" \
|
||||||
" '#{?#{!:#{pane_floating_flag}},Vertical Split,}' 'v' {split-window -v}" \
|
" '#{?#{!:#{pane_floating_flag}},Vertical Split,}' 'v' {split-window -v}" \
|
||||||
" ''" \
|
" ''" \
|
||||||
|
" '#{?#{>=:#{window_active_clients},2},#{?pane_local_mode,Shared Active Pane,Local Active Pane},}' 'a' {if -F '#{pane_local_mode}' {select-pane -S} {select-pane -s}}" \
|
||||||
|
" ''" \
|
||||||
" '#{?#{&&:#{!:#{pane_floating_flag}},#{>:#{window_panes},1}},Swap Up,}' 'u' {swap-pane -U}" \
|
" '#{?#{&&:#{!:#{pane_floating_flag}},#{>:#{window_panes},1}},Swap Up,}' 'u' {swap-pane -U}" \
|
||||||
" '#{?#{&&:#{!:#{pane_floating_flag}},#{>:#{window_panes},1}},Swap Down,}' 'd' {swap-pane -D}" \
|
" '#{?#{&&:#{!:#{pane_floating_flag}},#{>:#{window_panes},1}},Swap Down,}' 'd' {swap-pane -D}" \
|
||||||
" '#{?pane_marked_set,,-}Swap Marked' 's' {swap-pane}" \
|
" '#{?pane_marked_set,,-}Swap Marked' 's' {swap-pane}" \
|
||||||
@@ -421,6 +425,7 @@ key_bindings_init(void)
|
|||||||
"bind -N 'Spread panes out evenly' E { select-layout -E }",
|
"bind -N 'Spread panes out evenly' E { select-layout -E }",
|
||||||
"bind -N 'Switch to the last client' L { switch-client -l }",
|
"bind -N 'Switch to the last client' L { switch-client -l }",
|
||||||
"bind -N 'Clear the marked pane' M { select-pane -M }",
|
"bind -N 'Clear the marked pane' M { select-pane -M }",
|
||||||
|
"bind -N 'Change the pane title' T { command-prompt -I'#T' { select-pane -T '%%' } }",
|
||||||
"bind -N 'Enter copy mode' [ { copy-mode }",
|
"bind -N 'Enter copy mode' [ { copy-mode }",
|
||||||
"bind -N 'Paste the most recent paste buffer' ] { paste-buffer -p }",
|
"bind -N 'Paste the most recent paste buffer' ] { paste-buffer -p }",
|
||||||
"bind -N 'Create a new window' c { new-window }",
|
"bind -N 'Create a new window' c { new-window }",
|
||||||
@@ -502,6 +507,7 @@ key_bindings_init(void)
|
|||||||
|
|
||||||
/* Mouse button 1 down on pane. */
|
/* Mouse button 1 down on pane. */
|
||||||
"bind -n MouseDown1Pane { select-pane -t=; send -M }",
|
"bind -n MouseDown1Pane { select-pane -t=; send -M }",
|
||||||
|
"bind -n M-MouseDown1Pane { move-pane -P front -t= }",
|
||||||
|
|
||||||
/* Mouse button 1 drag on pane. */
|
/* Mouse button 1 drag on pane. */
|
||||||
"bind -n MouseDrag1Pane { if -F '#{||:#{pane_in_mode},#{mouse_any_flag}}' { send -M } { copy-mode -M } }",
|
"bind -n MouseDrag1Pane { if -F '#{||:#{pane_in_mode},#{mouse_any_flag}}' { send -M } { copy-mode -M } }",
|
||||||
@@ -523,6 +529,7 @@ key_bindings_init(void)
|
|||||||
|
|
||||||
/* Mouse button 1 on border. */
|
/* Mouse button 1 on border. */
|
||||||
"bind -n MouseDown1Border { select-pane -M }",
|
"bind -n MouseDown1Border { select-pane -M }",
|
||||||
|
"bind -n M-MouseDown1Border { move-pane -P front -t= }",
|
||||||
|
|
||||||
/* Mouse button 1 drag on border. */
|
/* Mouse button 1 drag on border. */
|
||||||
"bind -n MouseDrag1Border { resize-pane -M }",
|
"bind -n MouseDrag1Border { resize-pane -M }",
|
||||||
@@ -595,7 +602,7 @@ key_bindings_init(void)
|
|||||||
"bind -Tcopy-mode g { command-prompt -P -p'(goto line)' { send -X goto-line -- '%%' } }",
|
"bind -Tcopy-mode g { command-prompt -P -p'(goto line)' { send -X goto-line -- '%%' } }",
|
||||||
"bind -Tcopy-mode n { send -X search-again }",
|
"bind -Tcopy-mode n { send -X search-again }",
|
||||||
"bind -Tcopy-mode q { send -X cancel }",
|
"bind -Tcopy-mode q { send -X cancel }",
|
||||||
"bind -Tcopy-mode r { send -X refresh-toggle }",
|
"bind -Tcopy-mode r { send -X refresh-now }",
|
||||||
"bind -Tcopy-mode t { command-prompt -P -1p'(jump to forward)' { send -X jump-to-forward -- '%%' } }",
|
"bind -Tcopy-mode t { command-prompt -P -1p'(jump to forward)' { send -X jump-to-forward -- '%%' } }",
|
||||||
"bind -Tcopy-mode Home { send -X start-of-line }",
|
"bind -Tcopy-mode Home { send -X start-of-line }",
|
||||||
"bind -Tcopy-mode End { send -X end-of-line }",
|
"bind -Tcopy-mode End { send -X end-of-line }",
|
||||||
@@ -705,7 +712,7 @@ key_bindings_init(void)
|
|||||||
"bind -Tcopy-mode-vi n { send -X search-again }",
|
"bind -Tcopy-mode-vi n { send -X search-again }",
|
||||||
"bind -Tcopy-mode-vi o { send -X other-end }",
|
"bind -Tcopy-mode-vi o { send -X other-end }",
|
||||||
"bind -Tcopy-mode-vi q { send -X cancel }",
|
"bind -Tcopy-mode-vi q { send -X cancel }",
|
||||||
"bind -Tcopy-mode-vi r { send -X refresh-toggle }",
|
"bind -Tcopy-mode-vi r { send -X refresh-now }",
|
||||||
"bind -Tcopy-mode-vi t { command-prompt -P -1p'(jump to forward)' { send -X jump-to-forward -- '%%' } }",
|
"bind -Tcopy-mode-vi t { command-prompt -P -1p'(jump to forward)' { send -X jump-to-forward -- '%%' } }",
|
||||||
"bind -Tcopy-mode-vi v { send -X rectangle-toggle }",
|
"bind -Tcopy-mode-vi v { send -X rectangle-toggle }",
|
||||||
"bind -Tcopy-mode-vi w { send -X next-word }",
|
"bind -Tcopy-mode-vi w { send -X next-word }",
|
||||||
|
|||||||
50
layout.c
50
layout.c
@@ -1,4 +1,4 @@
|
|||||||
/* $OpenBSD: layout.c,v 1.96 2026/07/15 13:02:33 nicm Exp $ */
|
/* $OpenBSD: layout.c,v 1.98 2026/08/25 18:38:05 nicm Exp $ */
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2009 Nicholas Marriott <nicholas.marriott@gmail.com>
|
* Copyright (c) 2009 Nicholas Marriott <nicholas.marriott@gmail.com>
|
||||||
@@ -539,6 +539,10 @@ layout_resize_check(struct window *w, struct layout_cell *lc,
|
|||||||
|
|
||||||
status = window_get_pane_status(w);
|
status = window_get_pane_status(w);
|
||||||
|
|
||||||
|
/* Floating cells do not take space from the tiled layout. */
|
||||||
|
if (!layout_cell_is_tiled(lc) && !layout_cell_has_tiled_child(lc))
|
||||||
|
return (0);
|
||||||
|
|
||||||
if (lc->type == LAYOUT_WINDOWPANE) {
|
if (lc->type == LAYOUT_WINDOWPANE) {
|
||||||
/* Space available in this cell only. */
|
/* Space available in this cell only. */
|
||||||
if (type == LAYOUT_LEFTRIGHT) {
|
if (type == LAYOUT_LEFTRIGHT) {
|
||||||
@@ -568,6 +572,9 @@ layout_resize_check(struct window *w, struct layout_cell *lc,
|
|||||||
/* Different type: minimum of available space in child cells. */
|
/* Different type: minimum of available space in child cells. */
|
||||||
minimum = UINT_MAX;
|
minimum = UINT_MAX;
|
||||||
TAILQ_FOREACH(lcchild, &lc->cells, entry) {
|
TAILQ_FOREACH(lcchild, &lc->cells, entry) {
|
||||||
|
if (!layout_cell_is_tiled(lcchild) &&
|
||||||
|
!layout_cell_has_tiled_child(lcchild))
|
||||||
|
continue;
|
||||||
available = layout_resize_check(w, lcchild, type);
|
available = layout_resize_check(w, lcchild, type);
|
||||||
if (available < minimum)
|
if (available < minimum)
|
||||||
minimum = available;
|
minimum = available;
|
||||||
@@ -661,7 +668,7 @@ layout_resize_set_size(struct window *w, struct layout_cell *lc,
|
|||||||
|
|
||||||
/* Find and return the nearest neighbour to a cell in a specific direction. */
|
/* Find and return the nearest neighbour to a cell in a specific direction. */
|
||||||
static struct layout_cell *
|
static struct layout_cell *
|
||||||
layout_cell_get_neighbour_direction(struct layout_cell *lc, int direction)
|
layout_cell_get_neighbour_dir(struct layout_cell *lc, int direction)
|
||||||
{
|
{
|
||||||
struct layout_cell *lcn = lc;
|
struct layout_cell *lcn = lc;
|
||||||
|
|
||||||
@@ -695,9 +702,9 @@ layout_cell_get_neighbour(struct layout_cell *lc)
|
|||||||
if (lc == TAILQ_LAST(&lcparent->cells, layout_cells))
|
if (lc == TAILQ_LAST(&lcparent->cells, layout_cells))
|
||||||
direction = !direction;
|
direction = !direction;
|
||||||
|
|
||||||
lcother = layout_cell_get_neighbour_direction(lc, direction);
|
lcother = layout_cell_get_neighbour_dir(lc, direction);
|
||||||
if (lcother == NULL)
|
if (lcother == NULL)
|
||||||
lcother = layout_cell_get_neighbour_direction(lc, !direction);
|
lcother = layout_cell_get_neighbour_dir(lc, !direction);
|
||||||
|
|
||||||
return (lcother);
|
return (lcother);
|
||||||
}
|
}
|
||||||
@@ -860,7 +867,7 @@ layout_resize_pane_to(struct window_pane *wp, enum layout_type type,
|
|||||||
size = lc->g.sx;
|
size = lc->g.sx;
|
||||||
else
|
else
|
||||||
size = lc->g.sy;
|
size = lc->g.sy;
|
||||||
if (lc == TAILQ_LAST(&lcparent->cells, layout_cells))
|
if (layout_cell_is_last_tiled(lc))
|
||||||
change = size - new_size;
|
change = size - new_size;
|
||||||
else
|
else
|
||||||
change = new_size - size;
|
change = new_size - size;
|
||||||
@@ -985,11 +992,11 @@ layout_resize_pane(struct window_pane *wp, enum layout_type type, int change,
|
|||||||
if (lcparent == NULL)
|
if (lcparent == NULL)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
/* If this is the last cell, move back one. */
|
/* If this is the last tiled cell, move back one. */
|
||||||
if (lc == TAILQ_LAST(&lcparent->cells, layout_cells)) {
|
if (layout_cell_is_last_tiled(lc)) {
|
||||||
do
|
lc = layout_cell_get_neighbour_dir(lc, 0);
|
||||||
lc = TAILQ_PREV(lc, layout_cells, entry);
|
if (lc == NULL)
|
||||||
while (lc->flags & LAYOUT_CELL_FLOATING);
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
layout_resize_layout(wp->window, lc, type, change, opposite);
|
layout_resize_layout(wp->window, lc, type, change, opposite);
|
||||||
@@ -1007,22 +1014,22 @@ layout_resize_pane_grow(struct window *w, struct layout_cell *lc,
|
|||||||
lcadd = lc;
|
lcadd = lc;
|
||||||
|
|
||||||
/* Look towards the tail for a suitable cell for reduction. */
|
/* Look towards the tail for a suitable cell for reduction. */
|
||||||
lcremove = TAILQ_NEXT(lc, entry);
|
lcremove = layout_cell_get_neighbour_dir(lc, 1);
|
||||||
while (lcremove != NULL) {
|
while (lcremove != NULL) {
|
||||||
size = layout_resize_check(w, lcremove, type);
|
size = layout_resize_check(w, lcremove, type);
|
||||||
if (size > 0)
|
if (size > 0)
|
||||||
break;
|
break;
|
||||||
lcremove = TAILQ_NEXT(lcremove, entry);
|
lcremove = layout_cell_get_neighbour_dir(lcremove, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* If none found, look towards the head. */
|
/* If none found, look towards the head. */
|
||||||
if (opposite && lcremove == NULL) {
|
if (opposite && lcremove == NULL) {
|
||||||
lcremove = TAILQ_PREV(lc, layout_cells, entry);
|
lcremove = layout_cell_get_neighbour_dir(lc, 0);
|
||||||
while (lcremove != NULL) {
|
while (lcremove != NULL) {
|
||||||
size = layout_resize_check(w, lcremove, type);
|
size = layout_resize_check(w, lcremove, type);
|
||||||
if (size > 0)
|
if (size > 0)
|
||||||
break;
|
break;
|
||||||
lcremove = TAILQ_PREV(lcremove, layout_cells, entry);
|
lcremove = layout_cell_get_neighbour_dir(lcremove, 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (lcremove == NULL)
|
if (lcremove == NULL)
|
||||||
@@ -1050,13 +1057,13 @@ layout_resize_pane_shrink(struct window *w, struct layout_cell *lc,
|
|||||||
size = layout_resize_check(w, lcremove, type);
|
size = layout_resize_check(w, lcremove, type);
|
||||||
if (size != 0)
|
if (size != 0)
|
||||||
break;
|
break;
|
||||||
lcremove = TAILQ_PREV(lcremove, layout_cells, entry);
|
lcremove = layout_cell_get_neighbour_dir(lcremove, 0);
|
||||||
} while (lcremove != NULL);
|
} while (lcremove != NULL);
|
||||||
if (lcremove == NULL)
|
if (lcremove == NULL)
|
||||||
return (0);
|
return (0);
|
||||||
|
|
||||||
/* And add onto the next cell (from the original cell). */
|
/* And add onto the next cell (from the original cell). */
|
||||||
lcadd = TAILQ_NEXT(lc, entry);
|
lcadd = layout_cell_get_neighbour_dir(lc, 1);
|
||||||
if (lcadd == NULL)
|
if (lcadd == NULL)
|
||||||
return (0);
|
return (0);
|
||||||
|
|
||||||
@@ -1650,7 +1657,10 @@ layout_get_tiled_cell(struct cmdq_item *item, struct args *args,
|
|||||||
return (NULL);
|
return (NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
window_push_zoom(wp->window, 1, (flags & SPAWN_ZOOM));
|
if (window_active_pane_is_over_zoom(w))
|
||||||
|
window_push_zoom(w, 0, 1);
|
||||||
|
else
|
||||||
|
window_push_zoom(w, 1, (flags & SPAWN_ZOOM));
|
||||||
lc = layout_split_pane(wp, type, size, flags);
|
lc = layout_split_pane(wp, type, size, flags);
|
||||||
if (lc == NULL)
|
if (lc == NULL)
|
||||||
*cause = xstrdup("no space for a new pane");
|
*cause = xstrdup("no space for a new pane");
|
||||||
@@ -1677,8 +1687,10 @@ layout_get_floating_cell(struct cmdq_item *item, struct args *args,
|
|||||||
return (NULL);
|
return (NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (flags & SPAWN_MODAL)
|
if (flags & SPAWN_FLOATOVERZOOM)
|
||||||
window_push_modal_zoom(w);
|
window_push_zoom(wp->window, 0, 1);
|
||||||
|
else if (window_active_pane_is_over_zoom(w))
|
||||||
|
window_push_zoom(wp->window, 0, 1);
|
||||||
else
|
else
|
||||||
window_push_zoom(wp->window, 1, (flags & SPAWN_ZOOM));
|
window_push_zoom(wp->window, 1, (flags & SPAWN_ZOOM));
|
||||||
lcnew = layout_floating_pane(w, wp, &fg);
|
lcnew = layout_floating_pane(w, wp, &fg);
|
||||||
|
|||||||
8
menu.c
8
menu.c
@@ -1,4 +1,4 @@
|
|||||||
/* $OpenBSD: menu.c,v 1.69 2026/07/14 19:07:03 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>
|
||||||
@@ -337,6 +337,7 @@ menu_key(struct client *c, struct menu_data *md, struct key_event *event)
|
|||||||
struct cmdq_state *state;
|
struct cmdq_state *state;
|
||||||
enum cmd_parse_status status;
|
enum cmd_parse_status status;
|
||||||
char *error;
|
char *error;
|
||||||
|
key_code key;
|
||||||
|
|
||||||
if (KEYC_IS_MOUSE(event->key)) {
|
if (KEYC_IS_MOUSE(event->key)) {
|
||||||
/*
|
/*
|
||||||
@@ -387,7 +388,8 @@ menu_key(struct client *c, struct menu_data *md, struct key_event *event)
|
|||||||
name = menu->items[i].name;
|
name = menu->items[i].name;
|
||||||
if (name == NULL || *name == '-')
|
if (name == NULL || *name == '-')
|
||||||
continue;
|
continue;
|
||||||
if ((event->key & ~KEYC_MASK_FLAGS) == menu->items[i].key) {
|
key = (event->key & ~KEYC_MASK_FLAGS);
|
||||||
|
if (key == (menu->items[i].key & ~KEYC_MASK_FLAGS)) {
|
||||||
md->choice = i;
|
md->choice = i;
|
||||||
goto chosen;
|
goto chosen;
|
||||||
}
|
}
|
||||||
@@ -584,7 +586,7 @@ menu_display(struct menu *menu, int flags, int starting_choice,
|
|||||||
struct options *o;
|
struct options *o;
|
||||||
|
|
||||||
if (fs == NULL)
|
if (fs == NULL)
|
||||||
w = c->session->curw->window;
|
w = active_get_effective_window(c, c->session);
|
||||||
else
|
else
|
||||||
w = fs->w;
|
w = fs->w;
|
||||||
o = w->options;
|
o = w->options;
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
/* $OpenBSD: mode-tree.c,v 1.100 2026/07/14 19:07:03 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>
|
||||||
@@ -47,7 +47,7 @@ enum mode_tree_preview {
|
|||||||
"#[acs]x" MODE_TREE_PREFIX_STYLE " }," \
|
"#[acs]x" MODE_TREE_PREFIX_STYLE " }," \
|
||||||
"#{mode_tree_repeat}}" \
|
"#{mode_tree_repeat}}" \
|
||||||
"#{?mode_tree_branch," \
|
"#{?mode_tree_branch," \
|
||||||
"#[acs]#{?mode_tree_last,mq,tq}" MODE_TREE_PREFIX_STYLE "> ,}" \
|
"#[acs]#{?mode_tree_last,mq,tq}+" MODE_TREE_PREFIX_STYLE " ,}" \
|
||||||
"#{?mode_tree_has_children," \
|
"#{?mode_tree_has_children," \
|
||||||
"#{?mode_tree_expanded,#[fg=themered]-" MODE_TREE_PREFIX_STYLE " ," \
|
"#{?mode_tree_expanded,#[fg=themered]-" MODE_TREE_PREFIX_STYLE " ," \
|
||||||
"#[fg=themegreen]+" MODE_TREE_PREFIX_STYLE " }," \
|
"#[fg=themegreen]+" MODE_TREE_PREFIX_STYLE " }," \
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
/* $OpenBSD: options-table.c,v 1.242 2026/07/27 08:03:01 nicm Exp $ */
|
/* $OpenBSD: options-table.c,v 1.244 2026/09/01 12:49:49 nicm Exp $ */
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2011 Nicholas Marriott <nicholas.marriott@gmail.com>
|
* Copyright (c) 2011 Nicholas Marriott <nicholas.marriott@gmail.com>
|
||||||
@@ -305,6 +305,16 @@ const struct options_table_entry options_table[] = {
|
|||||||
"When this is reached, the oldest buffer is deleted."
|
"When this is reached, the oldest buffer is deleted."
|
||||||
},
|
},
|
||||||
|
|
||||||
|
{ .name = "clear-on-attach",
|
||||||
|
.type = OPTIONS_TABLE_FLAG,
|
||||||
|
.scope = OPTIONS_TABLE_SERVER,
|
||||||
|
.default_num = 1,
|
||||||
|
.text = "Whether to use the alternate screen and clear it when "
|
||||||
|
"a client is attached. When disabled, tmux does not "
|
||||||
|
"enter the alternate screen on attach so terminal "
|
||||||
|
"content before tmux remains in scrollback."
|
||||||
|
},
|
||||||
|
|
||||||
{ .name = "command-alias",
|
{ .name = "command-alias",
|
||||||
.type = OPTIONS_TABLE_STRING,
|
.type = OPTIONS_TABLE_STRING,
|
||||||
.scope = OPTIONS_TABLE_SERVER,
|
.scope = OPTIONS_TABLE_SERVER,
|
||||||
@@ -1115,7 +1125,8 @@ const struct options_table_entry options_table[] = {
|
|||||||
{ .name = "status-style",
|
{ .name = "status-style",
|
||||||
.type = OPTIONS_TABLE_STRING,
|
.type = OPTIONS_TABLE_STRING,
|
||||||
.scope = OPTIONS_TABLE_SESSION,
|
.scope = OPTIONS_TABLE_SESSION,
|
||||||
.default_str = "bg=themegreen,fg=themeblack",
|
.default_str = "bg=#{?local_active_window,themeblue,themegreen},"
|
||||||
|
"fg=themeblack",
|
||||||
.flags = OPTIONS_TABLE_IS_STYLE,
|
.flags = OPTIONS_TABLE_IS_STYLE,
|
||||||
.separator = ",",
|
.separator = ",",
|
||||||
.text = "Style of the status line."
|
.text = "Style of the status line."
|
||||||
@@ -1240,7 +1251,8 @@ const struct options_table_entry options_table[] = {
|
|||||||
* underscore.
|
* underscore.
|
||||||
*/
|
*/
|
||||||
.default_str = "!\"#$%&'()*+,-./:;<=>?@[\\]^`{|}~",
|
.default_str = "!\"#$%&'()*+,-./:;<=>?@[\\]^`{|}~",
|
||||||
.text = "Characters considered to separate words."
|
.text = "Characters considered to separate words; a space matches "
|
||||||
|
"any character with the Unicode White_Space property."
|
||||||
},
|
},
|
||||||
|
|
||||||
/* Window options. */
|
/* Window options. */
|
||||||
@@ -1542,8 +1554,9 @@ const struct options_table_entry options_table[] = {
|
|||||||
.scope = OPTIONS_TABLE_WINDOW|OPTIONS_TABLE_PANE,
|
.scope = OPTIONS_TABLE_WINDOW|OPTIONS_TABLE_PANE,
|
||||||
.default_str = "fg=#{?pane_modal_flag,themeblue,"
|
.default_str = "fg=#{?pane_modal_flag,themeblue,"
|
||||||
"#{?pane_marked,thememagenta,"
|
"#{?pane_marked,thememagenta,"
|
||||||
|
"#{?pane_local_active,themecyan,"
|
||||||
"#{?synchronize-panes,themered,"
|
"#{?synchronize-panes,themered,"
|
||||||
"#{?pane_in_mode,themeyellow,themegreen}}}}",
|
"#{?pane_in_mode,themeyellow,themegreen}}}}}",
|
||||||
.flags = OPTIONS_TABLE_IS_STYLE,
|
.flags = OPTIONS_TABLE_IS_STYLE,
|
||||||
.separator = ",",
|
.separator = ",",
|
||||||
.text = "Style of the active pane border."
|
.text = "Style of the active pane border."
|
||||||
@@ -1971,6 +1984,8 @@ const struct options_table_entry options_table[] = {
|
|||||||
"Run when a client is resized."),
|
"Run when a client is resized."),
|
||||||
OPTIONS_TABLE_HOOK("client-session-changed", "",
|
OPTIONS_TABLE_HOOK("client-session-changed", "",
|
||||||
"Run when a client's attached session is changed."),
|
"Run when a client's attached session is changed."),
|
||||||
|
OPTIONS_TABLE_HOOK("client-window-changed", "",
|
||||||
|
"Run when a client's effective window is changed."),
|
||||||
OPTIONS_TABLE_HOOK("client-light-theme", "",
|
OPTIONS_TABLE_HOOK("client-light-theme", "",
|
||||||
"Run when a client switches to a light theme."),
|
"Run when a client switches to a light theme."),
|
||||||
OPTIONS_TABLE_HOOK("client-dark-theme", "",
|
OPTIONS_TABLE_HOOK("client-dark-theme", "",
|
||||||
|
|||||||
11
popup.c
11
popup.c
@@ -1,4 +1,4 @@
|
|||||||
/* $OpenBSD: popup.c,v 1.76 2026/07/14 19:07:03 nicm Exp $ */
|
/* $OpenBSD: popup.c,v 1.77 2026/08/28 08:02:16 nicm Exp $ */
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2020 Nicholas Marriott <nicholas.marriott@gmail.com>
|
* Copyright (c) 2020 Nicholas Marriott <nicholas.marriott@gmail.com>
|
||||||
@@ -79,6 +79,7 @@ popup_free(struct popup_data *pd)
|
|||||||
|
|
||||||
if (pd->job != NULL)
|
if (pd->job != NULL)
|
||||||
job_free(pd->job);
|
job_free(pd->job);
|
||||||
|
if (pd->ictx != NULL)
|
||||||
input_free(pd->ictx);
|
input_free(pd->ictx);
|
||||||
|
|
||||||
free(pd->r.ranges);
|
free(pd->r.ranges);
|
||||||
@@ -102,9 +103,9 @@ popup_reapply_styles(struct popup_data *pd)
|
|||||||
|
|
||||||
if (s == NULL)
|
if (s == NULL)
|
||||||
return;
|
return;
|
||||||
o = s->curw->window->options;
|
o = active_get_effective_window(c, s)->options;
|
||||||
|
|
||||||
ft = format_create_defaults(NULL, c, s, s->curw, NULL);
|
ft = format_create_defaults(NULL, c, s, NULL, NULL);
|
||||||
|
|
||||||
/* Reapply popup style from options. */
|
/* Reapply popup style from options. */
|
||||||
memcpy(&pd->defaults, &grid_default_cell, sizeof pd->defaults);
|
memcpy(&pd->defaults, &grid_default_cell, sizeof pd->defaults);
|
||||||
@@ -569,9 +570,9 @@ popup_display(int flags, enum box_lines lines, struct cmdq_item *item, u_int px,
|
|||||||
struct style sytmp;
|
struct style sytmp;
|
||||||
|
|
||||||
if (s != NULL)
|
if (s != NULL)
|
||||||
o = s->curw->window->options;
|
o = active_get_effective_window(c, s)->options;
|
||||||
else
|
else
|
||||||
o = c->session->curw->window->options;
|
o = active_get_effective_window(c, c->session)->options;
|
||||||
|
|
||||||
if (lines == BOX_LINES_DEFAULT)
|
if (lines == BOX_LINES_DEFAULT)
|
||||||
lines = options_get_number(o, "popup-border-lines");
|
lines = options_get_number(o, "popup-border-lines");
|
||||||
|
|||||||
6
proc.c
6
proc.c
@@ -1,4 +1,4 @@
|
|||||||
/* $OpenBSD: proc.c,v 1.31 2026/06/08 21:38:19 nicm Exp $ */
|
/* $OpenBSD: proc.c,v 1.32 2026/08/04 13:16:03 claudio Exp $ */
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2015 Nicholas Marriott <nicholas.marriott@gmail.com>
|
* Copyright (c) 2015 Nicholas Marriott <nicholas.marriott@gmail.com>
|
||||||
@@ -79,7 +79,7 @@ static void
|
|||||||
proc_event_cb(__unused int fd, short events, void *arg)
|
proc_event_cb(__unused int fd, short events, void *arg)
|
||||||
{
|
{
|
||||||
struct tmuxpeer *peer = arg;
|
struct tmuxpeer *peer = arg;
|
||||||
ssize_t n;
|
int n;
|
||||||
struct imsg imsg;
|
struct imsg imsg;
|
||||||
|
|
||||||
if (!(peer->flags & PEER_BAD) && (events & EV_READ)) {
|
if (!(peer->flags & PEER_BAD) && (events & EV_READ)) {
|
||||||
@@ -88,7 +88,7 @@ proc_event_cb(__unused int fd, short events, void *arg)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
for (;;) {
|
for (;;) {
|
||||||
if ((n = imsg_get(&peer->ibuf, &imsg)) == -1) {
|
if ((n = imsgbuf_get(&peer->ibuf, &imsg)) == -1) {
|
||||||
peer->dispatchcb(NULL, peer->arg);
|
peer->dispatchcb(NULL, peer->arg);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|||||||
4
prompt.c
4
prompt.c
@@ -1,4 +1,4 @@
|
|||||||
/* $OpenBSD: prompt.c,v 1.5 2026/07/13 10:29:17 nicm Exp $ */
|
/* $OpenBSD: prompt.c,v 1.6 2026/08/17 06:45:16 nicm Exp $ */
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2026 Nicholas Marriott <nicholas.marriott@gmail.com>
|
* Copyright (c) 2026 Nicholas Marriott <nicholas.marriott@gmail.com>
|
||||||
@@ -1334,7 +1334,7 @@ process_key:
|
|||||||
if (pr->index != size) {
|
if (pr->index != size) {
|
||||||
memmove(pr->buffer + pr->index,
|
memmove(pr->buffer + pr->index,
|
||||||
pr->buffer + pr->index + 1,
|
pr->buffer + pr->index + 1,
|
||||||
(size + 1 - pr->index) *
|
(size - pr->index) *
|
||||||
sizeof *pr->buffer);
|
sizeof *pr->buffer);
|
||||||
goto changed;
|
goto changed;
|
||||||
}
|
}
|
||||||
|
|||||||
151
regress/active-window.sh
Normal file
151
regress/active-window.sh
Normal file
@@ -0,0 +1,151 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
PATH=/bin:/usr/bin
|
||||||
|
TERM=screen
|
||||||
|
LC_ALL=C.UTF-8
|
||||||
|
LANG=C.UTF-8
|
||||||
|
export TERM LC_ALL LANG
|
||||||
|
|
||||||
|
[ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux)
|
||||||
|
TMUX="$TEST_TMUX -LtestA$$ -f/dev/null"
|
||||||
|
|
||||||
|
TMPDIR=$(mktemp -d)
|
||||||
|
IN1="$TMPDIR/in1"
|
||||||
|
IN2="$TMPDIR/in2"
|
||||||
|
OUT1="$TMPDIR/out1"
|
||||||
|
OUT2="$TMPDIR/out2"
|
||||||
|
PID1=
|
||||||
|
PID2=
|
||||||
|
|
||||||
|
cleanup()
|
||||||
|
{
|
||||||
|
[ -n "$PID1" ] && kill "$PID1" 2>/dev/null
|
||||||
|
[ -n "$PID2" ] && kill "$PID2" 2>/dev/null
|
||||||
|
$TMUX kill-server 2>/dev/null
|
||||||
|
rm -rf "$TMPDIR"
|
||||||
|
}
|
||||||
|
trap cleanup EXIT
|
||||||
|
|
||||||
|
fail()
|
||||||
|
{
|
||||||
|
echo "$1"
|
||||||
|
echo "client1:"
|
||||||
|
cat "$OUT1" 2>/dev/null
|
||||||
|
echo "client2:"
|
||||||
|
cat "$OUT2" 2>/dev/null
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
wait_for()
|
||||||
|
{
|
||||||
|
file=$1
|
||||||
|
pattern=$2
|
||||||
|
i=0
|
||||||
|
|
||||||
|
while [ "$i" -lt 60 ]; do
|
||||||
|
if grep -F -- "$pattern" "$file" >/dev/null 2>&1; then
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
sleep 0.1
|
||||||
|
i=$((i + 1))
|
||||||
|
done
|
||||||
|
fail "missing '$pattern' in $file"
|
||||||
|
}
|
||||||
|
|
||||||
|
send1()
|
||||||
|
{
|
||||||
|
printf '%s\n' "$*" >&3
|
||||||
|
}
|
||||||
|
|
||||||
|
send2()
|
||||||
|
{
|
||||||
|
printf '%s\n' "$*" >&4
|
||||||
|
}
|
||||||
|
|
||||||
|
check_server()
|
||||||
|
{
|
||||||
|
out=$($TMUX display-message -p -t "$1" "$2")
|
||||||
|
[ "$out" = "$3" ] || fail "expected '$3' for $1 $2, got '$out'"
|
||||||
|
}
|
||||||
|
|
||||||
|
$TMUX kill-server 2>/dev/null
|
||||||
|
$TMUX new-session -d -s aw -x 80 -y 24 -n w0 || exit 1
|
||||||
|
$TMUX new-window -d -t aw: -n w1 || exit 1
|
||||||
|
$TMUX new-window -d -t aw: -n w2 || exit 1
|
||||||
|
$TMUX new-session -d -s aw2 -x 80 -y 24 -n x0 || exit 1
|
||||||
|
$TMUX new-window -d -t aw2: -n x1 || exit 1
|
||||||
|
|
||||||
|
mkfifo "$IN1" "$IN2" || exit 1
|
||||||
|
: >"$OUT1"
|
||||||
|
: >"$OUT2"
|
||||||
|
$TMUX -C attach-session -t aw <"$IN1" >"$OUT1" 2>&1 &
|
||||||
|
PID1=$!
|
||||||
|
$TMUX -C attach-session -t aw <"$IN2" >"$OUT2" 2>&1 &
|
||||||
|
PID2=$!
|
||||||
|
exec 3>"$IN1"
|
||||||
|
exec 4>"$IN2"
|
||||||
|
|
||||||
|
send1 'display-message -p C1READY'
|
||||||
|
send2 'display-message -p C2READY'
|
||||||
|
wait_for "$OUT1" C1READY
|
||||||
|
wait_for "$OUT2" C2READY
|
||||||
|
|
||||||
|
# Default shared selection: client 1 changes session current, client 2 follows.
|
||||||
|
send1 'display-message -p "C1A #{active_window_index} #{local_active_window} #{window_index}"'
|
||||||
|
wait_for "$OUT1" 'C1A 0 0 0'
|
||||||
|
send1 'select-window -t :1'
|
||||||
|
wait_for "$OUT2" '%session-window-changed $0 @1'
|
||||||
|
send2 'display-message -p "C2A #{active_window_index} #{local_active_window} #{window_index}"'
|
||||||
|
wait_for "$OUT2" 'C2A 1 0 1'
|
||||||
|
check_server 'aw:' '#{window_index}' '1'
|
||||||
|
|
||||||
|
# Local selection changes only client 1 and becomes the default command target.
|
||||||
|
send1 'select-window -L'
|
||||||
|
send1 'select-window -t :2'
|
||||||
|
send1 'display-message -p "C1B #{active_window_index} #{local_active_window} #{window_index}"'
|
||||||
|
wait_for "$OUT1" 'C1B 2 1 2'
|
||||||
|
send2 'display-message -p "C2B #{active_window_index} #{local_active_window} #{window_index}"'
|
||||||
|
wait_for "$OUT2" 'C2B 1 0 1'
|
||||||
|
check_server 'aw:' '#{window_index}' '1'
|
||||||
|
|
||||||
|
# Local last-window history is independent of the session shared stack.
|
||||||
|
send1 'last-window'
|
||||||
|
send1 'display-message -p "C1C #{active_window_index} #{window_index}"'
|
||||||
|
wait_for "$OUT1" 'C1C 1 1'
|
||||||
|
send1 'next-window'
|
||||||
|
send1 'display-message -p "C1D #{active_window_index} #{window_index}"'
|
||||||
|
wait_for "$OUT1" 'C1D 2 2'
|
||||||
|
check_server 'aw:' '#{window_index}' '1'
|
||||||
|
|
||||||
|
# Session switches preserve per-session local selections.
|
||||||
|
send1 'switch-client -t aw2'
|
||||||
|
send1 'select-window -L'
|
||||||
|
send1 'select-window -t :1'
|
||||||
|
send1 'display-message -p "C1E #{session_name} #{active_window_index} #{local_active_window}"'
|
||||||
|
wait_for "$OUT1" 'C1E aw2 1 1'
|
||||||
|
send1 'switch-client -t aw'
|
||||||
|
send1 'display-message -p "C1F #{session_name} #{active_window_index} #{local_active_window}"'
|
||||||
|
wait_for "$OUT1" 'C1F aw 2 1'
|
||||||
|
|
||||||
|
# Returning to shared mode immediately follows the session current window.
|
||||||
|
send1 'select-window -S'
|
||||||
|
send1 'display-message -p "C1G #{active_window_index} #{local_active_window} #{window_index}"'
|
||||||
|
wait_for "$OUT1" 'C1G 1 0 1'
|
||||||
|
|
||||||
|
# Invalid local windows recover to a valid session window without changing curw.
|
||||||
|
send1 'select-window -L'
|
||||||
|
send1 'select-window -t :2'
|
||||||
|
$TMUX kill-window -t aw:2 || fail 'kill-window failed'
|
||||||
|
send1 'display-message -p "C1H #{active_window_index} #{local_active_window} #{window_index}"'
|
||||||
|
wait_for "$OUT1" 'C1H 1 1 1'
|
||||||
|
check_server 'aw:' '#{window_index}' '1'
|
||||||
|
|
||||||
|
# Creating a window without -d in local mode selects it locally only.
|
||||||
|
send1 'new-window -n w3'
|
||||||
|
send1 'display-message -p "C1I #{local_active_window} #{window_name}"'
|
||||||
|
wait_for "$OUT1" 'C1I 1 w3'
|
||||||
|
send2 'display-message -p "C2C #{local_active_window} #{window_name}"'
|
||||||
|
wait_for "$OUT2" 'C2C 0 w1'
|
||||||
|
check_server 'aw:' '#{window_name}' 'w1'
|
||||||
|
|
||||||
|
exit 0
|
||||||
@@ -28,7 +28,7 @@ $TMUX kill-server 2>/dev/null
|
|||||||
|
|
||||||
TMP=$(mktemp)
|
TMP=$(mktemp)
|
||||||
TMP2=$(mktemp)
|
TMP2=$(mktemp)
|
||||||
trap 'rm -f "$TMP" "$TMP2"; $TMUX kill-server 2>/dev/null' 0 1 15
|
trap 'rm -f "$TMP" "$TMP2" "$TMP".*; $TMUX kill-server 2>/dev/null' 0 1 15
|
||||||
|
|
||||||
# check_ok $cmd...
|
# check_ok $cmd...
|
||||||
#
|
#
|
||||||
@@ -270,6 +270,27 @@ if [ $? -eq 0 ]; then
|
|||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
# A write error after a successful open must be reported to the server.
|
||||||
|
dd if=/dev/zero of="$TMP.big" bs=100000 count=1 2>/dev/null
|
||||||
|
check_ok load-buffer -b write-error "$TMP.big"
|
||||||
|
mkfifo "$TMP.fifo" || exit 1
|
||||||
|
dd if="$TMP.fifo" of="$TMP.got" bs=1 count=8 2>/dev/null &
|
||||||
|
reader=$!
|
||||||
|
exec 3>"$TMP.fifo"
|
||||||
|
out=$($TMUX save-buffer -b write-error "$TMP.fifo" 3>&- 2>&1)
|
||||||
|
status=$?
|
||||||
|
exec 3>&-
|
||||||
|
wait "$reader"
|
||||||
|
if [ $status -eq 0 ]; then
|
||||||
|
echo "save-buffer after write error succeeded."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
if [ "$(wc -c <"$TMP.got")" -ne 8 ]; then
|
||||||
|
echo "save-buffer write error test wrote wrong amount."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
check_ok delete-buffer -b write-error
|
||||||
|
|
||||||
# save-buffer - writes to stdout and load-buffer - reads from stdin.
|
# save-buffer - writes to stdout and load-buffer - reads from stdin.
|
||||||
out=$($TMUX save-buffer -b sb -)
|
out=$($TMUX save-buffer -b sb -)
|
||||||
if [ "$out" != "data" ]; then
|
if [ "$out" != "data" ]; then
|
||||||
|
|||||||
77
regress/control-client-exit-stalled.sh
Executable file
77
regress/control-client-exit-stalled.sh
Executable file
@@ -0,0 +1,77 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
# A control client whose terminal has stopped accepting output (for example
|
||||||
|
# its ssh connection or terminal emulator died) never drains the output that
|
||||||
|
# server_client_check_exit waits on before sending MSG_EXIT, and may then
|
||||||
|
# never close its socket either. This must not prevent the server from
|
||||||
|
# exiting: after kill-server the server previously stayed alive forever with
|
||||||
|
# server_exit set, closing every new connection immediately, so every new
|
||||||
|
# client failed with "server exited unexpectedly" until the server was killed
|
||||||
|
# by hand. The server now bounds the exit handshake: it discards output it
|
||||||
|
# can never deliver and drops the client if it still does not close.
|
||||||
|
#
|
||||||
|
# The stalled terminal is simulated by a fifo whose read end is held open but
|
||||||
|
# never read.
|
||||||
|
|
||||||
|
PATH=/bin:/usr/bin
|
||||||
|
TERM=screen
|
||||||
|
|
||||||
|
[ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux)
|
||||||
|
TMUX="$TEST_TMUX -Ltest"
|
||||||
|
$TMUX kill-server 2>/dev/null
|
||||||
|
|
||||||
|
DIR=$(mktemp -d)
|
||||||
|
FIFO=$DIR/fifo
|
||||||
|
SERVER=
|
||||||
|
|
||||||
|
mkfifo "$FIFO" || exit 1
|
||||||
|
|
||||||
|
cleanup() {
|
||||||
|
[ -n "$SERVER" ] && kill -9 "$SERVER" 2>/dev/null
|
||||||
|
$TMUX kill-server 2>/dev/null
|
||||||
|
exec 8<&- 2>/dev/null
|
||||||
|
rm -rf "$DIR"
|
||||||
|
}
|
||||||
|
trap cleanup 0 1 15
|
||||||
|
|
||||||
|
# A detached session whose pane floods printable output forever.
|
||||||
|
$TMUX -f/dev/null new -d -x 80 -y 24 -s rt 'cat /dev/zero | tr "\000" x' || exit 1
|
||||||
|
SERVER=$($TMUX display -pt rt '#{pid}')
|
||||||
|
|
||||||
|
# Attach a control client with output down the fifo; stdin held open by sleep
|
||||||
|
# so it stays attached. The fifo fills and is never read, so a backlog forms
|
||||||
|
# in the server that can never be delivered.
|
||||||
|
( sleep 60 ) | $TMUX -f/dev/null -C attach -t rt >"$FIFO" 2>&1 &
|
||||||
|
exec 8<"$FIFO"
|
||||||
|
|
||||||
|
n=0
|
||||||
|
while [ $n -lt 50 ]; do
|
||||||
|
$TMUX lsc -F '#{client_name}' 2>/dev/null | grep -q . && break
|
||||||
|
sleep 0.1
|
||||||
|
n=$((n + 1))
|
||||||
|
done
|
||||||
|
$TMUX lsc -F '#{client_name}' 2>/dev/null | grep -q . ||
|
||||||
|
{ echo "control client did not attach"; exit 1; }
|
||||||
|
|
||||||
|
# Let the pane flood fill the fifo so the client's output is stuck.
|
||||||
|
sleep 2
|
||||||
|
|
||||||
|
# Ask the server to exit. This sets CLIENT_EXIT on the stalled client, whose
|
||||||
|
# output can never drain.
|
||||||
|
$TMUX kill-server 2>/dev/null
|
||||||
|
|
||||||
|
# The server must exit within the exit timeout (10 seconds) plus slack,
|
||||||
|
# rather than staying in limbo forever rejecting new clients.
|
||||||
|
n=0
|
||||||
|
while [ $n -lt 100 ]; do
|
||||||
|
if ! kill -0 "$SERVER" 2>/dev/null; then
|
||||||
|
SERVER=
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
sleep 0.2
|
||||||
|
n=$((n + 1))
|
||||||
|
done
|
||||||
|
|
||||||
|
echo "server did not exit after kill-server; new clients see:"
|
||||||
|
$TMUX ls 2>&1
|
||||||
|
exit 1
|
||||||
158
regress/control-notify-guard.sh
Executable file
158
regress/control-notify-guard.sh
Executable file
@@ -0,0 +1,158 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
PATH=/bin:/usr/bin
|
||||||
|
TERM=screen
|
||||||
|
LC_ALL=C.UTF-8
|
||||||
|
LANG=C.UTF-8
|
||||||
|
export TERM LC_ALL LANG
|
||||||
|
|
||||||
|
[ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux)
|
||||||
|
TMUX="$TEST_TMUX -LtestA$$ -f/dev/null"
|
||||||
|
|
||||||
|
TMPDIR=$(mktemp -d)
|
||||||
|
IN="$TMPDIR/in"
|
||||||
|
OUT="$TMPDIR/out"
|
||||||
|
BADCFG="$TMPDIR/bad.conf"
|
||||||
|
PID=
|
||||||
|
|
||||||
|
cleanup()
|
||||||
|
{
|
||||||
|
exec 3>&- 2>/dev/null
|
||||||
|
[ -n "$PID" ] && kill "$PID" 2>/dev/null
|
||||||
|
$TMUX kill-server 2>/dev/null
|
||||||
|
rm -rf "$TMPDIR"
|
||||||
|
}
|
||||||
|
trap cleanup EXIT
|
||||||
|
|
||||||
|
fail()
|
||||||
|
{
|
||||||
|
echo "$1" >&2
|
||||||
|
[ -s "$OUT" ] && cat "$OUT" >&2
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
wait_for()
|
||||||
|
{
|
||||||
|
pattern=$1
|
||||||
|
timeout=${2:-60}
|
||||||
|
i=0
|
||||||
|
|
||||||
|
while [ "$i" -lt "$timeout" ]; do
|
||||||
|
grep -F -- "$pattern" "$OUT" >/dev/null 2>&1 && return 0
|
||||||
|
sleep 0.1
|
||||||
|
i=$((i + 1))
|
||||||
|
done
|
||||||
|
fail "missing: $pattern"
|
||||||
|
}
|
||||||
|
|
||||||
|
wait_for_count()
|
||||||
|
{
|
||||||
|
pattern=$1
|
||||||
|
expected=$2
|
||||||
|
timeout=${3:-60}
|
||||||
|
i=0
|
||||||
|
|
||||||
|
while [ "$i" -lt "$timeout" ]; do
|
||||||
|
count=$(grep -F -c -- "$pattern" "$OUT" 2>/dev/null)
|
||||||
|
[ "$count" -ge "$expected" ] && return 0
|
||||||
|
sleep 0.1
|
||||||
|
i=$((i + 1))
|
||||||
|
done
|
||||||
|
fail "missing count $expected for: $pattern"
|
||||||
|
}
|
||||||
|
|
||||||
|
send()
|
||||||
|
{
|
||||||
|
printf '%s\n' "$1" >&3 || fail "failed to send: $1"
|
||||||
|
}
|
||||||
|
|
||||||
|
# A notification is a standalone protocol message. It must never be part of
|
||||||
|
# the response between the real %begin and matching %end or %error for a
|
||||||
|
# command. Ignore guard-looking command output while a real guard is open.
|
||||||
|
check_guards()
|
||||||
|
{
|
||||||
|
awk '
|
||||||
|
function notification(line) {
|
||||||
|
return line ~ /^%(sessions-changed|session-changed |unlinked-window-add )/ ||
|
||||||
|
line ~ /^%(pause |continue |config-error )/
|
||||||
|
}
|
||||||
|
!open && $0 ~ /^%begin [0-9]+ [0-9]+ [0-9]+$/ {
|
||||||
|
open = 1
|
||||||
|
time = $2
|
||||||
|
number = $3
|
||||||
|
flags = $4
|
||||||
|
next
|
||||||
|
}
|
||||||
|
open && ($0 == "%end " time " " number " " flags ||
|
||||||
|
$0 == "%error " time " " number " " flags) {
|
||||||
|
open = 0
|
||||||
|
next
|
||||||
|
}
|
||||||
|
notification($0) {
|
||||||
|
seen[$1]++
|
||||||
|
if (open) {
|
||||||
|
print "notification inside guard: " $0 > "/dev/stderr"
|
||||||
|
bad = 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
END {
|
||||||
|
if (open) {
|
||||||
|
print "unterminated command guard" > "/dev/stderr"
|
||||||
|
bad = 1
|
||||||
|
}
|
||||||
|
if (seen["%sessions-changed"] < 2 ||
|
||||||
|
seen["%session-changed"] < 1 ||
|
||||||
|
seen["%unlinked-window-add"] < 2 ||
|
||||||
|
seen["%pause"] < 1 || seen["%continue"] < 1 ||
|
||||||
|
seen["%config-error"] < 1) {
|
||||||
|
print "missing expected notification" > "/dev/stderr"
|
||||||
|
bad = 1
|
||||||
|
}
|
||||||
|
exit bad
|
||||||
|
}' "$OUT" || fail "bad control protocol notification ordering"
|
||||||
|
}
|
||||||
|
|
||||||
|
$TMUX kill-server 2>/dev/null
|
||||||
|
$TMUX new-session -d -s guard -x 80 -y 24 || exit 1
|
||||||
|
pane=$($TMUX display-message -p -t guard:0.0 '#{pane_id}') || exit 1
|
||||||
|
|
||||||
|
mkfifo "$IN" || exit 1
|
||||||
|
: >"$OUT"
|
||||||
|
$TMUX -C attach-session -t guard <"$IN" >"$OUT" 2>&1 &
|
||||||
|
PID=$!
|
||||||
|
exec 3>"$IN"
|
||||||
|
|
||||||
|
# Attaching generates a session notification synchronously from inside the
|
||||||
|
# attach command. It must follow the closing guard.
|
||||||
|
wait_for '%session-changed '
|
||||||
|
|
||||||
|
# Guard-looking output is arbitrary command output, not protocol state. Make
|
||||||
|
# new-session both generate notifications and print an unmatched fake %begin;
|
||||||
|
# it must not leave those notifications permanently deferred.
|
||||||
|
send "set-option -g @fake-begin '%begin 1 2 3'"
|
||||||
|
send "new-session -d -P -F '#{@fake-begin}' -s fake-begin"
|
||||||
|
wait_for_count '%sessions-changed' 1
|
||||||
|
|
||||||
|
# Likewise, fake %end output from a command that generates multiple
|
||||||
|
# notifications must not flush them before the command actually ends.
|
||||||
|
send "set-option -g @fake-end '%end 1 2 3'"
|
||||||
|
send "new-session -d -P -F '#{@fake-end}' -s fake-end"
|
||||||
|
wait_for_count '%sessions-changed' 2
|
||||||
|
|
||||||
|
# Exercise notification paths outside control-notify.c.
|
||||||
|
send "refresh-client -A '$pane:pause'"
|
||||||
|
wait_for "%pause $pane"
|
||||||
|
send "refresh-client -A '$pane:continue'"
|
||||||
|
wait_for "%continue $pane"
|
||||||
|
|
||||||
|
printf '%s\n' 'not-a-command' >"$BADCFG"
|
||||||
|
send "source-file '$BADCFG'"
|
||||||
|
wait_for '%config-error '
|
||||||
|
|
||||||
|
# A final command guarantees all earlier command guards have completed before
|
||||||
|
# the transcript is checked.
|
||||||
|
send "display-message -p 'guard-check-done'"
|
||||||
|
wait_for 'guard-check-done'
|
||||||
|
check_guards
|
||||||
|
|
||||||
|
exit 0
|
||||||
@@ -117,6 +117,7 @@ $TMUX send-keys -X copy-selection
|
|||||||
# Test that vi cursor movement does not stop on the padding cell of a wide
|
# Test that vi cursor movement does not stop on the padding cell of a wide
|
||||||
# character at the end of a line.
|
# character at the end of a line.
|
||||||
$TMUX kill-server 2>/dev/null
|
$TMUX kill-server 2>/dev/null
|
||||||
|
sleep 1
|
||||||
$TMUX new -d -x20 -y5 \
|
$TMUX new -d -x20 -y5 \
|
||||||
"printf 'abc中\nxyz\n'; exec cat" || exit 1
|
"printf 'abc中\nxyz\n'; exec cat" || exit 1
|
||||||
$TMUX set-window-option -g mode-keys vi
|
$TMUX set-window-option -g mode-keys vi
|
||||||
|
|||||||
163
regress/copy-mode-unicode-whitespace.sh
Executable file
163
regress/copy-mode-unicode-whitespace.sh
Executable file
@@ -0,0 +1,163 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
PATH=/bin:/usr/bin
|
||||||
|
TERM=screen
|
||||||
|
|
||||||
|
[ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux)
|
||||||
|
|
||||||
|
fail()
|
||||||
|
{
|
||||||
|
echo "$1"
|
||||||
|
[ -z "$TMUX" ] || $TMUX kill-server 2>/dev/null
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
goto_cell()
|
||||||
|
{
|
||||||
|
row=$1
|
||||||
|
column=$2
|
||||||
|
|
||||||
|
$TMUX send-keys -X history-top || fail "$mode: history-top failed"
|
||||||
|
$TMUX send-keys -X start-of-line || fail "$mode: start-of-line failed"
|
||||||
|
[ "$row" -eq 0 ] ||
|
||||||
|
$TMUX send-keys -X -N "$row" cursor-down ||
|
||||||
|
fail "$mode: cursor-down failed"
|
||||||
|
[ "$column" -eq 0 ] ||
|
||||||
|
$TMUX send-keys -X -N "$column" cursor-right ||
|
||||||
|
fail "$mode: cursor-right failed"
|
||||||
|
}
|
||||||
|
|
||||||
|
check_cursor()
|
||||||
|
{
|
||||||
|
expected=$1
|
||||||
|
actual=$($TMUX display-message -p '#{copy_cursor_x},#{copy_cursor_y}')
|
||||||
|
[ "$actual" = "$expected" ] ||
|
||||||
|
fail "$mode: expected cursor $expected, got $actual"
|
||||||
|
}
|
||||||
|
|
||||||
|
check_word()
|
||||||
|
{
|
||||||
|
row=$1
|
||||||
|
column=$2
|
||||||
|
expected=$3
|
||||||
|
|
||||||
|
goto_cell "$row" "$column"
|
||||||
|
$TMUX set-buffer sentinel || fail "$mode: set-buffer failed"
|
||||||
|
$TMUX send-keys -X select-word || fail "$mode: select-word failed"
|
||||||
|
$TMUX send-keys -X copy-selection || fail "$mode: copy-selection failed"
|
||||||
|
actual=$($TMUX show-buffer 2>/dev/null)
|
||||||
|
[ "$actual" = "$expected" ] ||
|
||||||
|
fail "$mode: expected word '$expected', got '$actual'"
|
||||||
|
}
|
||||||
|
|
||||||
|
check_next_word()
|
||||||
|
{
|
||||||
|
row=$1
|
||||||
|
expected=$2
|
||||||
|
|
||||||
|
goto_cell "$row" 0
|
||||||
|
$TMUX send-keys -X next-word || fail "$mode: next-word failed"
|
||||||
|
$TMUX set-buffer sentinel || fail "$mode: set-buffer failed"
|
||||||
|
$TMUX send-keys -X select-word || fail "$mode: select-word failed"
|
||||||
|
$TMUX send-keys -X copy-selection || fail "$mode: copy-selection failed"
|
||||||
|
actual=$($TMUX show-buffer 2>/dev/null)
|
||||||
|
[ "$actual" = "$expected" ] ||
|
||||||
|
fail "$mode: expected next word '$expected', got '$actual'"
|
||||||
|
}
|
||||||
|
|
||||||
|
for mode in emacs vi; do
|
||||||
|
TMUX="$TEST_TMUX -Lunicode-whitespace-$mode-$$ -f/dev/null"
|
||||||
|
$TMUX kill-server 2>/dev/null
|
||||||
|
$TMUX new-session -d -x16 -y20 \
|
||||||
|
"printf 'aa bb\naa\tbb\naa\302\240bb\naa\343\200\200bb\naa:bb\naaaaaaaaaaaaaaa\302\240b\nabcdefghijklmnopq\nhardone\nhardtwo\naa\341\232\200bb\naa\342\200\200bb\naa\342\200\250bb\naa\342\200\251bb\naa\342\200\257bb\naa\342\201\237bb\nxa\302\205b c\n'; exec cat" ||
|
||||||
|
fail "$mode: new-session failed"
|
||||||
|
$TMUX set-option -g window-size manual || fail "$mode: set size failed"
|
||||||
|
$TMUX set-window-option -g mode-keys "$mode" ||
|
||||||
|
fail "$mode: set mode-keys failed"
|
||||||
|
$TMUX set-window-option -g word-separators "" ||
|
||||||
|
fail "$mode: clear word-separators failed"
|
||||||
|
$TMUX copy-mode || fail "$mode: copy-mode failed"
|
||||||
|
|
||||||
|
# ASCII space and TAB retain their existing behaviour.
|
||||||
|
goto_cell 0 0
|
||||||
|
$TMUX send-keys -X next-space || fail "$mode: ASCII next-space failed"
|
||||||
|
check_cursor 3,0
|
||||||
|
goto_cell 1 0
|
||||||
|
$TMUX send-keys -X next-space || fail "$mode: TAB next-space failed"
|
||||||
|
check_cursor 8,1
|
||||||
|
$TMUX send-keys -X previous-space ||
|
||||||
|
fail "$mode: TAB previous-space failed"
|
||||||
|
check_cursor 0,1
|
||||||
|
|
||||||
|
# NBSP is whitespace for selection and word movement.
|
||||||
|
goto_cell 2 0
|
||||||
|
$TMUX send-keys -X next-word || fail "$mode: NBSP next-word failed"
|
||||||
|
check_cursor 3,2
|
||||||
|
$TMUX send-keys -X previous-word ||
|
||||||
|
fail "$mode: NBSP previous-word failed"
|
||||||
|
check_cursor 0,2
|
||||||
|
check_word 2 0 aa
|
||||||
|
goto_cell 2 0
|
||||||
|
$TMUX send-keys -X next-word-end ||
|
||||||
|
fail "$mode: NBSP next-word-end failed"
|
||||||
|
if [ "$mode" = emacs ]; then
|
||||||
|
check_cursor 2,2
|
||||||
|
else
|
||||||
|
check_cursor 1,2
|
||||||
|
fi
|
||||||
|
|
||||||
|
# U+3000 is two columns wide; both its leading and padding cells must be
|
||||||
|
# skipped as one whitespace character in either direction.
|
||||||
|
goto_cell 3 0
|
||||||
|
$TMUX send-keys -X next-word || fail "$mode: U+3000 next-word failed"
|
||||||
|
check_cursor 4,3
|
||||||
|
$TMUX send-keys -X previous-word ||
|
||||||
|
fail "$mode: U+3000 previous-word failed"
|
||||||
|
check_cursor 0,3
|
||||||
|
goto_cell 3 0
|
||||||
|
$TMUX send-keys -X next-space-end ||
|
||||||
|
fail "$mode: U+3000 next-space-end failed"
|
||||||
|
if [ "$mode" = emacs ]; then
|
||||||
|
check_cursor 2,3
|
||||||
|
else
|
||||||
|
check_cursor 1,3
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Other characters in word-separators remain literal when space enables
|
||||||
|
# Unicode whitespace matching.
|
||||||
|
$TMUX set-window-option -g word-separators ': ' ||
|
||||||
|
fail "$mode: set custom word-separators failed"
|
||||||
|
goto_cell 4 0
|
||||||
|
$TMUX send-keys -X next-word || fail "$mode: literal next-word failed"
|
||||||
|
check_cursor 2,4
|
||||||
|
$TMUX set-window-option -g word-separators "" ||
|
||||||
|
fail "$mode: restore word-separators failed"
|
||||||
|
|
||||||
|
# Whitespace in the last column remains a boundary across a soft wrap.
|
||||||
|
goto_cell 5 0
|
||||||
|
$TMUX send-keys -X next-space ||
|
||||||
|
fail "$mode: wrapped NBSP next-space failed"
|
||||||
|
check_cursor 0,6
|
||||||
|
|
||||||
|
# A word split only by a soft wrap remains one word, while a hard line
|
||||||
|
# boundary still terminates selection.
|
||||||
|
check_word 7 0 abcdefghijklmnopq
|
||||||
|
check_word 9 0 hardone
|
||||||
|
|
||||||
|
# Check the remaining representative White_Space ranges and U+0085
|
||||||
|
# combined into a cell with another character.
|
||||||
|
check_next_word 11 bb
|
||||||
|
check_next_word 12 bb
|
||||||
|
check_next_word 13 bb
|
||||||
|
check_next_word 14 bb
|
||||||
|
check_next_word 15 bb
|
||||||
|
check_next_word 16 bb
|
||||||
|
goto_cell 17 0
|
||||||
|
$TMUX send-keys -X next-word ||
|
||||||
|
fail "$mode: combined U+0085 next-word failed"
|
||||||
|
check_cursor 2,17
|
||||||
|
|
||||||
|
$TMUX kill-server 2>/dev/null
|
||||||
|
done
|
||||||
|
|
||||||
|
exit 0
|
||||||
@@ -199,5 +199,74 @@ must_fail $TMUX resize-pane -t "$id" -x 0
|
|||||||
must_fail $TMUX resize-pane -t "$id" -y 0
|
must_fail $TMUX resize-pane -t "$id" -y 0
|
||||||
$TMUX kill-pane -t "$id" || exit 1
|
$TMUX kill-pane -t "$id" || exit 1
|
||||||
|
|
||||||
|
# --- Tiled pane resize with floating cells in the layout ---
|
||||||
|
|
||||||
|
$TMUX set-option -w -u pane-border-lines || exit 1
|
||||||
|
base=$($TMUX display-message -p '#{pane_id}')
|
||||||
|
|
||||||
|
# A floating cell after the last tiled cell must not become the recipient of a
|
||||||
|
# relative or absolute resize. This is the sequence from GitHub issue 5135.
|
||||||
|
floating=$($TMUX new-pane -dPF '#{pane_id}' -t "$base" \
|
||||||
|
-x 50% -y 50% -X 50% -Y 50% 'sleep 100') ||
|
||||||
|
fail "new-pane for tiled resize test failed"
|
||||||
|
lower=$($TMUX split-window -dPF '#{pane_id}' -t "$base" 'sleep 100') ||
|
||||||
|
fail "split-window for tiled resize test failed"
|
||||||
|
|
||||||
|
$TMUX resize-pane -t "$lower" -U 5 || fail "relative tiled resize failed"
|
||||||
|
must_equal "$($TMUX display-message -p -t "$base" '#{pane_height}')" 7
|
||||||
|
must_equal "$($TMUX display-message -p -t "$lower" '#{pane_top}')" 8
|
||||||
|
must_equal "$($TMUX display-message -p -t "$lower" '#{pane_height}')" 16
|
||||||
|
must_equal "$($TMUX display-message -p -t "$floating" '#{pane_top}')" 13
|
||||||
|
must_equal "$($TMUX display-message -p -t "$floating" '#{pane_height}')" 10
|
||||||
|
|
||||||
|
$TMUX resize-pane -t "$lower" -y 11 || fail "absolute tiled reset failed"
|
||||||
|
$TMUX resize-pane -t "$lower" -y 16 || fail "absolute tiled resize failed"
|
||||||
|
must_equal "$($TMUX display-message -p -t "$base" '#{pane_height}')" 7
|
||||||
|
must_equal "$($TMUX display-message -p -t "$lower" '#{pane_top}')" 8
|
||||||
|
must_equal "$($TMUX display-message -p -t "$lower" '#{pane_height}')" 16
|
||||||
|
must_equal "$($TMUX display-message -p -t "$floating" '#{pane_height}')" 10
|
||||||
|
|
||||||
|
$TMUX kill-pane -t "$floating" || exit 1
|
||||||
|
$TMUX kill-pane -t "$lower" || exit 1
|
||||||
|
|
||||||
|
# A floating cell between tiled siblings must be skipped when finding the cell
|
||||||
|
# which donates space to a resize.
|
||||||
|
lower=$($TMUX split-window -dPF '#{pane_id}' -t "$base" 'sleep 100') ||
|
||||||
|
fail "split-window for middle floating cell test failed"
|
||||||
|
floating=$($TMUX new-pane -dPF '#{pane_id}' -t "$base" \
|
||||||
|
-x 20 -y 8 -X 30 -Y 8 'sleep 100') ||
|
||||||
|
fail "new-pane for middle floating cell test failed"
|
||||||
|
|
||||||
|
$TMUX resize-pane -t "$base" -D 3 || fail "resize past floating cell failed"
|
||||||
|
must_equal "$($TMUX display-message -p -t "$base" '#{pane_height}')" 15
|
||||||
|
must_equal "$($TMUX display-message -p -t "$lower" '#{pane_top}')" 16
|
||||||
|
must_equal "$($TMUX display-message -p -t "$lower" '#{pane_height}')" 8
|
||||||
|
must_equal "$($TMUX display-message -p -t "$floating" '#{pane_height}')" 6
|
||||||
|
|
||||||
|
$TMUX kill-pane -t "$floating" || exit 1
|
||||||
|
$TMUX kill-pane -t "$lower" || exit 1
|
||||||
|
|
||||||
|
# A small floating child must not limit the available tiled space in a nested
|
||||||
|
# layout with a different split direction.
|
||||||
|
lower=$($TMUX split-window -dPF '#{pane_id}' -t "$base" 'sleep 100') ||
|
||||||
|
fail "split-window for nested floating cell test failed"
|
||||||
|
right=$($TMUX split-window -dhPF '#{pane_id}' -t "$lower" 'sleep 100') ||
|
||||||
|
fail "horizontal split for nested floating cell test failed"
|
||||||
|
floating=$($TMUX new-pane -dPF '#{pane_id}' -t "$lower" \
|
||||||
|
-x 20 -y 3 -X 30 -Y 10 'sleep 100') ||
|
||||||
|
fail "new-pane for nested floating cell test failed"
|
||||||
|
|
||||||
|
$TMUX resize-pane -t "$base" -D 3 || fail "nested tiled resize failed"
|
||||||
|
must_equal "$($TMUX display-message -p -t "$base" '#{pane_height}')" 15
|
||||||
|
must_equal "$($TMUX display-message -p -t "$lower" '#{pane_top}')" 16
|
||||||
|
must_equal "$($TMUX display-message -p -t "$lower" '#{pane_height}')" 8
|
||||||
|
must_equal "$($TMUX display-message -p -t "$right" '#{pane_top}')" 16
|
||||||
|
must_equal "$($TMUX display-message -p -t "$right" '#{pane_height}')" 8
|
||||||
|
must_equal "$($TMUX display-message -p -t "$floating" '#{pane_height}')" 1
|
||||||
|
|
||||||
|
$TMUX kill-pane -t "$floating" || exit 1
|
||||||
|
$TMUX kill-pane -t "$right" || exit 1
|
||||||
|
$TMUX kill-pane -t "$lower" || exit 1
|
||||||
|
|
||||||
$TMUX kill-server 2>/dev/null
|
$TMUX kill-server 2>/dev/null
|
||||||
exit 0
|
exit 0
|
||||||
|
|||||||
@@ -1,93 +0,0 @@
|
|||||||
#!/bin/sh
|
|
||||||
|
|
||||||
# Grid-resident SIXEL image markers: history, copy, overwrite and fallback.
|
|
||||||
|
|
||||||
PATH=/bin:/usr/bin
|
|
||||||
TERM=screen
|
|
||||||
|
|
||||||
[ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux)
|
|
||||||
TMUX="$TEST_TMUX -Limage$$ -f/dev/null"
|
|
||||||
TMUX2="$TEST_TMUX -Limage-client$$ -f/dev/null"
|
|
||||||
$TMUX kill-server 2>/dev/null
|
|
||||||
$TMUX2 kill-server 2>/dev/null
|
|
||||||
|
|
||||||
TMP=$(mktemp)
|
|
||||||
trap "$TMUX kill-server 2>/dev/null; $TMUX2 kill-server 2>/dev/null; rm -f $TMP" 0 1 15
|
|
||||||
|
|
||||||
$TMUX new-session -d -x 20 -y 8 "
|
|
||||||
printf '\033Pq\"1;1;8;16#0;2;100;100;100#0~~~~~~~~\044-~~~~~~~~\033\\'
|
|
||||||
i=0
|
|
||||||
while [ \$i -lt 10 ]; do printf '\nline-%d' \$i; i=\$((i + 1)); done
|
|
||||||
sleep 10"
|
|
||||||
|
|
||||||
sleep 1
|
|
||||||
[ "$($TMUX display-message -p '#{image_support}')" = 0 ] && exit 0
|
|
||||||
|
|
||||||
# Images scroll as grid cells, while capture output contains ordinary spaces.
|
|
||||||
[ "$($TMUX display-message -p '#{history_size}')" -gt 0 ] || exit 1
|
|
||||||
$TMUX capture-pane -pS- >$TMP || exit 1
|
|
||||||
grep 'Pq' $TMP >/dev/null && exit 1
|
|
||||||
|
|
||||||
# Copy mode duplicates the backing grid, including image marker references.
|
|
||||||
$TMUX copy-mode || exit 1
|
|
||||||
$TMUX send-keys -X history-top || exit 1
|
|
||||||
$TMUX capture-pane -p >$TMP || exit 1
|
|
||||||
$TMUX send-keys -X cancel || exit 1
|
|
||||||
|
|
||||||
# Ordinary text overwrites marker cells through the normal grid write path.
|
|
||||||
$TMUX new-window -d "
|
|
||||||
printf '\033Pq\"1;1;8;16#0;2;100;100;100#0~~~~~~~~\044-~~~~~~~~\033\\'
|
|
||||||
printf '\033[HXY'
|
|
||||||
sleep 10"
|
|
||||||
sleep 1
|
|
||||||
[ "$($TMUX capture-pane -pt:1 -S0 -E0)" = "XY" ] || exit 1
|
|
||||||
|
|
||||||
# A 26-pixel raster occupies two default 16-pixel cells, but the final cell
|
|
||||||
# remains only partially filled instead of stretching the raster to 32 pixels.
|
|
||||||
# A client without an image feature exposes this in the sampled text backend.
|
|
||||||
$TMUX kill-server 2>/dev/null
|
|
||||||
$TMUX2 new-session -d -x 10 -y 4 "
|
|
||||||
printf '\033Pq\"1;1;26;26#0;2;100;100;100#0!26~-!26~-!26~-!26~-!26B\033\\'
|
|
||||||
sleep 10" || exit 1
|
|
||||||
$TMUX2 set -g status off || exit 1
|
|
||||||
$TMUX2 set -as terminal-features \
|
|
||||||
',*:image-quadrants:image-sextants' || exit 1
|
|
||||||
$TMUX new-session -d -x 10 -y 4 || exit 1
|
|
||||||
$TMUX set -g status off || exit 1
|
|
||||||
$TMUX send-keys -l "$TMUX2 attach-session" || exit 1
|
|
||||||
$TMUX send-keys Enter || exit 1
|
|
||||||
sleep 1
|
|
||||||
$TMUX2 list-clients -F '#{client_termfeatures}' | \
|
|
||||||
grep -q 'image-quadrants.*image-sextants' || exit 1
|
|
||||||
$TMUX capture-pane -pS0 -E0 >$TMP || exit 1
|
|
||||||
[ -n "$(sed -n 1p $TMP)" ] || exit 1
|
|
||||||
|
|
||||||
# Selection redraws must leave fallback image cells visible.
|
|
||||||
$TMUX2 copy-mode || exit 1
|
|
||||||
$TMUX2 send-keys -X history-top || exit 1
|
|
||||||
$TMUX2 send-keys -X start-of-line || exit 1
|
|
||||||
$TMUX2 send-keys -X begin-selection || exit 1
|
|
||||||
sleep 1
|
|
||||||
$TMUX capture-pane -pS0 -E0 >$TMP || exit 1
|
|
||||||
[ -n "$(sed -n 1p $TMP)" ] || exit 1
|
|
||||||
|
|
||||||
# Image marker rows remain cell-aligned when a narrower terminal causes text
|
|
||||||
# reflow. The second image cell is clipped, not moved to the following row.
|
|
||||||
$TMUX2 send-keys -X cancel || exit 1
|
|
||||||
$TMUX2 new-window -d "
|
|
||||||
printf '\033Pq\"1;1;26;26#0;2;100;100;100#0!26~-!26~-!26~-!26~-!26B\033\\'
|
|
||||||
sleep 10" || exit 1
|
|
||||||
$TMUX2 select-window -t:1 || exit 1
|
|
||||||
sleep 1
|
|
||||||
$TMUX resize-window -x 1 -y 4 || exit 1
|
|
||||||
sleep 1
|
|
||||||
$TMUX capture-pane -pS0 -E3 >$TMP || exit 1
|
|
||||||
[ -n "$(sed -n 1p $TMP)" ] || exit 1
|
|
||||||
[ -z "$(sed -n 2p $TMP)" ] || exit 1
|
|
||||||
$TMUX resize-window -x 10 -y 4 || exit 1
|
|
||||||
sleep 1
|
|
||||||
$TMUX capture-pane -pS0 -E3 >$TMP || exit 1
|
|
||||||
[ "$(sed -n 1p $TMP | wc -c)" = 61 ] || exit 1
|
|
||||||
[ -z "$(sed -n 2p $TMP)" ] || exit 1
|
|
||||||
|
|
||||||
exit 0
|
|
||||||
201
regress/local-pane-selection.sh
Normal file
201
regress/local-pane-selection.sh
Normal file
@@ -0,0 +1,201 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
PATH=/bin:/usr/bin
|
||||||
|
TERM=screen
|
||||||
|
LANG=C.UTF-8
|
||||||
|
LC_ALL=C.UTF-8
|
||||||
|
export TERM LANG LC_ALL
|
||||||
|
|
||||||
|
[ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux)
|
||||||
|
TMUX="$TEST_TMUX -LtestA$$ -f/dev/null"
|
||||||
|
|
||||||
|
TMPDIR=${TMPDIR:-/tmp}/tmux-local-pane.$$
|
||||||
|
mkdir -p "$TMPDIR" || exit 1
|
||||||
|
IN1="$TMPDIR/in1"
|
||||||
|
IN2="$TMPDIR/in2"
|
||||||
|
OUT1="$TMPDIR/out1"
|
||||||
|
OUT2="$TMPDIR/out2"
|
||||||
|
|
||||||
|
cleanup()
|
||||||
|
{
|
||||||
|
$TMUX kill-server 2>/dev/null
|
||||||
|
rm -rf "$TMPDIR"
|
||||||
|
}
|
||||||
|
trap cleanup 0 1 15
|
||||||
|
|
||||||
|
fail()
|
||||||
|
{
|
||||||
|
echo "$1"
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
wait_for()
|
||||||
|
{
|
||||||
|
file=$1
|
||||||
|
pattern=$2
|
||||||
|
i=0
|
||||||
|
while [ "$i" -lt 20 ]; do
|
||||||
|
if grep -q "$pattern" "$file"; then
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
sleep 1
|
||||||
|
i=$((i + 1))
|
||||||
|
done
|
||||||
|
echo "missing '$pattern' in $file"
|
||||||
|
cat "$file"
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
send1()
|
||||||
|
{
|
||||||
|
printf '%s\n' "$*" >&3
|
||||||
|
}
|
||||||
|
|
||||||
|
send2()
|
||||||
|
{
|
||||||
|
printf '%s\n' "$*" >&4
|
||||||
|
}
|
||||||
|
|
||||||
|
check_fmt()
|
||||||
|
{
|
||||||
|
out=$($TMUX display-message -p -t "$1" "$2" 2>&1)
|
||||||
|
if [ "$out" != "$3" ]; then
|
||||||
|
echo "Format '$2' for '$1' wrong."
|
||||||
|
echo "Expected: '$3'"
|
||||||
|
echo "But got: '$out'"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
$TMUX kill-server 2>/dev/null
|
||||||
|
$TMUX new-session -d -s active -x 80 -y 24 || exit 1
|
||||||
|
$TMUX split-window -d -h -t active:0 || exit 1
|
||||||
|
$TMUX split-window -d -v -t active:0.0 || exit 1
|
||||||
|
style=$($TMUX show-options -wgv pane-active-border-style) || exit 1
|
||||||
|
case "$style" in
|
||||||
|
*pane_local_active*themecyan*) ;;
|
||||||
|
*) fail "pane-active-border-style does not include local active colour" ;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
p0=$($TMUX display-message -p -t active:0.0 '#{pane_id}') || exit 1
|
||||||
|
p1=$($TMUX display-message -p -t active:0.1 '#{pane_id}') || exit 1
|
||||||
|
p2=$($TMUX display-message -p -t active:0.2 '#{pane_id}') || exit 1
|
||||||
|
$TMUX select-pane -t "$p0" || exit 1
|
||||||
|
check_fmt active:0 '#{pane_id}' "$p0"
|
||||||
|
|
||||||
|
mkfifo "$IN1" "$IN2" || exit 1
|
||||||
|
: >"$OUT1"
|
||||||
|
: >"$OUT2"
|
||||||
|
$TMUX -C attach-session -t active <"$IN1" >"$OUT1" 2>&1 &
|
||||||
|
PID1=$!
|
||||||
|
exec 3>"$IN1"
|
||||||
|
|
||||||
|
send1 'display-message -p C1_READY'
|
||||||
|
wait_for "$OUT1" 'C1_READY' || exit 1
|
||||||
|
|
||||||
|
# One attached client cannot enter local mode.
|
||||||
|
send1 'select-pane -s'
|
||||||
|
send1 "select-pane -t $p1"
|
||||||
|
send1 'display-message -p "C1_SINGLE:#{pane_id}:#{pane_active}:#{pane_local_active}"'
|
||||||
|
wait_for "$OUT1" "C1_SINGLE:$p1:1:" || exit 1
|
||||||
|
check_fmt active:0 '#{pane_id}' "$p1"
|
||||||
|
send1 "select-pane -t $p0"
|
||||||
|
check_fmt active:0 '#{pane_id}' "$p0"
|
||||||
|
|
||||||
|
$TMUX -C attach-session -t active <"$IN2" >"$OUT2" 2>&1 &
|
||||||
|
PID2=$!
|
||||||
|
exec 4>"$IN2"
|
||||||
|
|
||||||
|
send2 'display-message -p C2_READY'
|
||||||
|
wait_for "$OUT2" 'C2_READY' || exit 1
|
||||||
|
|
||||||
|
# Both clients start in shared mode and see the window active pane.
|
||||||
|
send1 'display-message -p "C1_START:#{pane_id}:#{pane_active}:#{pane_local_active}"'
|
||||||
|
send2 'display-message -p "C2_START:#{pane_id}:#{pane_active}:#{pane_local_active}"'
|
||||||
|
wait_for "$OUT1" "C1_START:$p0:1:" || exit 1
|
||||||
|
wait_for "$OUT2" "C2_START:$p0:1:" || exit 1
|
||||||
|
|
||||||
|
# Client 1 enters local mode and selects a pane without changing shared state.
|
||||||
|
send1 'select-pane -s'
|
||||||
|
send1 "select-pane -t $p1"
|
||||||
|
send1 'display-message -p "C1_LOCAL:#{pane_id}:#{pane_active}:#{pane_local_active}:#{pane_local_mode}"'
|
||||||
|
send1 "display-message -p -t $p0 \"C1_LOCAL_INACTIVE:#{pane_id}:#{pane_active}:#{pane_local_active}\""
|
||||||
|
wait_for "$OUT1" "C1_LOCAL:$p1:1:1:1" || exit 1
|
||||||
|
wait_for "$OUT1" "C1_LOCAL_INACTIVE:$p0:0:0" || exit 1
|
||||||
|
check_fmt active:0 '#{pane_id}' "$p0"
|
||||||
|
send2 'display-message -p "C2_SHARED:#{pane_id}:#{pane_active}:#{pane_local_active}"'
|
||||||
|
wait_for "$OUT2" "C2_SHARED:$p0:1:" || exit 1
|
||||||
|
|
||||||
|
# Client 2 can keep an independent local pane in the same window.
|
||||||
|
send2 'select-pane -s'
|
||||||
|
send2 "select-pane -t $p2"
|
||||||
|
send2 'display-message -p "C2_LOCAL:#{pane_id}:#{pane_active}:#{pane_local_active}"'
|
||||||
|
wait_for "$OUT2" "C2_LOCAL:$p2:1:1" || exit 1
|
||||||
|
send1 'display-message -p "C1_STILL:#{pane_id}:#{pane_active}:#{pane_local_active}"'
|
||||||
|
wait_for "$OUT1" "C1_STILL:$p1:1:1" || exit 1
|
||||||
|
|
||||||
|
# Local last-pane history is independent of the shared window history.
|
||||||
|
send1 "select-pane -t $p2"
|
||||||
|
send1 'last-pane'
|
||||||
|
send1 'display-message -p "C1_LAST:#{pane_id}:#{pane_last}"'
|
||||||
|
wait_for "$OUT1" "C1_LAST:$p1:0" || exit 1
|
||||||
|
check_fmt active:0 '#{pane_id}' "$p0"
|
||||||
|
|
||||||
|
# Switching back to shared mode immediately follows window->active.
|
||||||
|
send1 'select-pane -S'
|
||||||
|
send1 'display-message -p "C1_SHARED_AGAIN:#{pane_id}:#{pane_active}"'
|
||||||
|
wait_for "$OUT1" "C1_SHARED_AGAIN:$p0:1" || exit 1
|
||||||
|
send1 "select-pane -t $p1"
|
||||||
|
check_fmt active:0 '#{pane_id}' "$p1"
|
||||||
|
send2 'display-message -p "C2_STILL_LOCAL:#{pane_id}:#{pane_active}"'
|
||||||
|
wait_for "$OUT2" "C2_STILL_LOCAL:$p2:1" || exit 1
|
||||||
|
|
||||||
|
# Local selection survives switching away and back to the window.
|
||||||
|
send2 'new-window -n other'
|
||||||
|
send2 'previous-window'
|
||||||
|
send2 'display-message -p "C2_AFTER_WINDOW_SWITCH:#{pane_id}:#{pane_active}"'
|
||||||
|
wait_for "$OUT2" "C2_AFTER_WINDOW_SWITCH:$p2:1" || exit 1
|
||||||
|
|
||||||
|
# Removing a locally active pane recovers to the shared active pane.
|
||||||
|
$TMUX kill-pane -t "$p2" || exit 1
|
||||||
|
send2 'display-message -p "C2_AFTER_KILL:#{pane_id}:#{pane_active}"'
|
||||||
|
wait_for "$OUT2" "C2_AFTER_KILL:$p1:1" || exit 1
|
||||||
|
|
||||||
|
# Local selection of a floating pane does not raise it unless requested.
|
||||||
|
f1=$($TMUX new-pane -dPF '#{pane_id}' -x 20 -y 6 -X 8 -Y 3 \
|
||||||
|
-t active:0 'sleep 100') || exit 1
|
||||||
|
f2=$($TMUX new-pane -dPF '#{pane_id}' -x 20 -y 6 -X 12 -Y 5 \
|
||||||
|
-t active:0 'sleep 100') || exit 1
|
||||||
|
check_fmt "$f1" '#{pane_z}' 1
|
||||||
|
check_fmt "$f2" '#{pane_z}' 0
|
||||||
|
send1 'select-pane -s'
|
||||||
|
send1 "select-pane -t $f1"
|
||||||
|
send1 "display-message -p -t $f1 \"C1_FLOAT_LOCAL:#{pane_id}:#{pane_active}:#{pane_z}\""
|
||||||
|
wait_for "$OUT1" "C1_FLOAT_LOCAL:$f1:1:1" || exit 1
|
||||||
|
check_fmt "$f1" '#{pane_z}' 1
|
||||||
|
send1 "select-pane -M -t $f1"
|
||||||
|
send1 "display-message -p -t $f1 \"C1_FLOAT_BORDER:#{pane_id}:#{pane_active}:#{pane_z}\""
|
||||||
|
wait_for "$OUT1" "C1_FLOAT_BORDER:$f1:1:1" || exit 1
|
||||||
|
check_fmt "$f1" '#{pane_z}' 1
|
||||||
|
send1 "move-pane -P front -t $f1"
|
||||||
|
send1 "display-message -p -t $f1 \"C1_FLOAT_RAISE:#{pane_z}\""
|
||||||
|
wait_for "$OUT1" "C1_FLOAT_RAISE:0" || exit 1
|
||||||
|
check_fmt "$f1" '#{pane_z}' 0
|
||||||
|
$TMUX kill-pane -t "$f1" || exit 1
|
||||||
|
$TMUX kill-pane -t "$f2" || exit 1
|
||||||
|
|
||||||
|
# When only one client remains, local mode is cleared.
|
||||||
|
send1 'select-pane -s'
|
||||||
|
send1 "select-pane -t $p0"
|
||||||
|
send1 'display-message -p "C1_LOCAL_BEFORE_DETACH:#{pane_id}:#{pane_local_active}"'
|
||||||
|
wait_for "$OUT1" "C1_LOCAL_BEFORE_DETACH:$p0:1" || exit 1
|
||||||
|
kill "$PID2" 2>/dev/null
|
||||||
|
wait "$PID2" 2>/dev/null
|
||||||
|
send1 'display-message -p "C1_AFTER_DETACH:#{pane_id}:#{pane_local_active}"'
|
||||||
|
wait_for "$OUT1" "C1_AFTER_DETACH:$p1:" || exit 1
|
||||||
|
|
||||||
|
kill "$PID1" 2>/dev/null
|
||||||
|
wait "$PID1" 2>/dev/null
|
||||||
|
$TMUX display-message -p alive >/dev/null 2>&1 || fail "server died"
|
||||||
|
|
||||||
|
exit 0
|
||||||
@@ -105,6 +105,24 @@ drag()
|
|||||||
sleep 1
|
sleep 1
|
||||||
}
|
}
|
||||||
|
|
||||||
|
meta_drag()
|
||||||
|
{
|
||||||
|
scol="$1"
|
||||||
|
srow="$2"
|
||||||
|
ecol="$3"
|
||||||
|
erow="$4"
|
||||||
|
|
||||||
|
seq=$(printf '\033[<8;%s;%sM' "$scol" "$srow")
|
||||||
|
$TMUX2 send-keys -t "$OUTER" -l "$seq" 2>/dev/null
|
||||||
|
sleep 0.2
|
||||||
|
seq=$(printf '\033[<40;%s;%sM' "$ecol" "$erow")
|
||||||
|
$TMUX2 send-keys -t "$OUTER" -l "$seq" 2>/dev/null
|
||||||
|
sleep 0.2
|
||||||
|
seq=$(printf '\033[<8;%s;%sm' "$ecol" "$erow")
|
||||||
|
$TMUX2 send-keys -t "$OUTER" -l "$seq" 2>/dev/null
|
||||||
|
sleep 1
|
||||||
|
}
|
||||||
|
|
||||||
cleanup
|
cleanup
|
||||||
|
|
||||||
check_ok new-session -d -s modal -x 80 -y 24 'cat'
|
check_ok new-session -d -s modal -x 80 -y 24 'cat'
|
||||||
@@ -127,12 +145,16 @@ case "$(fmt "$modal" '#{pane_flags}')" in
|
|||||||
*O*) ;;
|
*O*) ;;
|
||||||
*) fail "modal pane flags do not include O" ;;
|
*) fail "modal pane flags do not include O" ;;
|
||||||
esac
|
esac
|
||||||
|
case "$(fmt "$modal" '#{pane_flags}')" in
|
||||||
|
*A*) ;;
|
||||||
|
*) fail "modal pane flags do not include A" ;;
|
||||||
|
esac
|
||||||
case "$(fmt modal:0 '#{window_flags}')" in
|
case "$(fmt modal:0 '#{window_flags}')" in
|
||||||
*O*) ;;
|
*O*) ;;
|
||||||
*) fail "modal window flags do not include O" ;;
|
*) fail "modal window flags do not include O" ;;
|
||||||
esac
|
esac
|
||||||
must_equal "$(fmt modal:0 '#{window_modal_pane}')" "$modal"
|
must_equal "$(fmt modal:0 '#{window_modal_pane}')" "$modal"
|
||||||
must_equal "$(fmt modal:0 '#{window_zoomed_flag}')" 0
|
must_equal "$(fmt modal:0 '#{window_zoomed_flag}')" 1
|
||||||
|
|
||||||
check_fail "window already has a modal pane" \
|
check_fail "window already has a modal pane" \
|
||||||
new-pane -O -x 10 -y 4 'cat'
|
new-pane -O -x 10 -y 4 'cat'
|
||||||
@@ -159,12 +181,14 @@ under=$($TMUX split-window -PF '#{pane_id}' -t "$p0" 'cat') ||
|
|||||||
sleep 1
|
sleep 1
|
||||||
must_equal "$(fmt modal:0 '#{pane_id}')" "$modal"
|
must_equal "$(fmt modal:0 '#{pane_id}')" "$modal"
|
||||||
must_equal "$(fmt "$under" '#{pane_active}')" 0
|
must_equal "$(fmt "$under" '#{pane_active}')" 0
|
||||||
|
must_equal "$(fmt modal:0 '#{window_zoomed_flag}')" 1
|
||||||
|
|
||||||
float=$($TMUX new-pane -PF '#{pane_id}' -x 10 -y 4 -X 5 -Y 3 'cat') ||
|
float=$($TMUX new-pane -PF '#{pane_id}' -x 10 -y 4 -X 5 -Y 3 'cat') ||
|
||||||
fail "new floating pane under modal failed"
|
fail "new floating pane under modal failed"
|
||||||
sleep 1
|
sleep 1
|
||||||
must_equal "$(fmt modal:0 '#{pane_id}')" "$modal"
|
must_equal "$(fmt modal:0 '#{pane_id}')" "$modal"
|
||||||
must_equal "$(fmt "$float" '#{pane_active}')" 0
|
must_equal "$(fmt "$float" '#{pane_active}')" 0
|
||||||
|
must_equal "$(fmt modal:0 '#{window_zoomed_flag}')" 1
|
||||||
|
|
||||||
check_ok new-window -d -t modal: -n other 'cat'
|
check_ok new-window -d -t modal: -n other 'cat'
|
||||||
check_ok select-window -t modal:other
|
check_ok select-window -t modal:other
|
||||||
@@ -303,6 +327,164 @@ check_ok kill-pane -t "$modal"
|
|||||||
sleep 1
|
sleep 1
|
||||||
must_equal "$(fmt modal:0 '#{window_modal_pane}')" ''
|
must_equal "$(fmt modal:0 '#{window_modal_pane}')" ''
|
||||||
must_equal "$(fmt modal:0 '#{pane_id}')" "$p0"
|
must_equal "$(fmt modal:0 '#{pane_id}')" "$p0"
|
||||||
|
must_equal "$(fmt "$p0" '#{window_zoomed_flag}:#{pane_zoomed_flag}')" 0:0
|
||||||
|
|
||||||
|
ignored=$($TMUX new-pane -KdPF '#{pane_id}' -t "$p0" 'cat') ||
|
||||||
|
fail "new-pane -K without -O failed"
|
||||||
|
check_ok kill-pane -t "$ignored"
|
||||||
|
|
||||||
|
$TMUX set -g @modal-prefix no
|
||||||
|
$TMUX set -g @modal-root no
|
||||||
|
$TMUX bind -n z set -g @modal-root yes
|
||||||
|
|
||||||
|
modal=$($TMUX new-pane -OKPF '#{pane_id}' -t "$p0" \
|
||||||
|
-x 20 -y 5 -X 20 -Y 10 'cat') ||
|
||||||
|
fail "new-pane -OK failed"
|
||||||
|
sleep 1
|
||||||
|
$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 key-capturing modal pane" ;;
|
||||||
|
esac
|
||||||
|
left=$(fmt "$modal" '#{pane_left}')
|
||||||
|
top=$(fmt "$modal" '#{pane_top}')
|
||||||
|
meta_drag $((left + 2)) $((top + 2)) $((left + 7)) $((top + 4))
|
||||||
|
new_left=$(fmt "$modal" '#{pane_left}')
|
||||||
|
new_top=$(fmt "$modal" '#{pane_top}')
|
||||||
|
[ "$new_left" -gt "$left" ] || [ "$new_top" -gt "$top" ] ||
|
||||||
|
fail "key-capturing modal pane did not move"
|
||||||
|
check_ok kill-pane -t "$modal"
|
||||||
|
sleep 1
|
||||||
|
|
||||||
|
# A nonmodal floating pane may remain above zoom, and switching between it and
|
||||||
|
# the zoomed tiled pane must not unzoom the window.
|
||||||
|
check_ok new-window -d -t modal: -n float-over-zoom 'cat'
|
||||||
|
base=$(fmt modal:float-over-zoom '#{pane_id}')
|
||||||
|
check_ok split-window -dh -t "$base" 'cat'
|
||||||
|
check_ok resize-pane -Z -t "$base"
|
||||||
|
over=$($TMUX new-pane -APF '#{pane_id}' -t "$base" \
|
||||||
|
-x 20 -y 5 -X 20 -Y 10 'cat') ||
|
||||||
|
fail "new-pane -A failed"
|
||||||
|
must_equal "$(fmt "$over" '#{pane_floating_flag}:#{pane_active}')" 1:1
|
||||||
|
case "$(fmt "$over" '#{pane_flags}')" in
|
||||||
|
*A*) ;;
|
||||||
|
*) fail "float-over-zoom pane flags do not include A" ;;
|
||||||
|
esac
|
||||||
|
must_equal "$(fmt "$base" '#{window_zoomed_flag}:#{pane_zoomed_flag}')" 1:1
|
||||||
|
check_ok select-pane -t "$base"
|
||||||
|
must_equal "$(fmt "$base" '#{window_zoomed_flag}:#{pane_active}')" 1:1
|
||||||
|
check_ok select-pane -t "$over"
|
||||||
|
must_equal "$(fmt "$over" '#{window_zoomed_flag}:#{pane_active}')" 1:1
|
||||||
|
client=$($TMUX list-clients -F '#{client_name}' | head -1)
|
||||||
|
[ -n "$client" ] || fail "no client for switch-client test"
|
||||||
|
check_ok switch-client -c "$client" -t "$base"
|
||||||
|
must_equal "$(fmt "$base" '#{window_zoomed_flag}:#{pane_active}')" 1:1
|
||||||
|
check_ok switch-client -c "$client" -t "$over"
|
||||||
|
must_equal "$(fmt "$over" '#{window_zoomed_flag}:#{pane_active}')" 1:1
|
||||||
|
check_ok kill-pane -t "$over"
|
||||||
|
must_equal "$(fmt "$base" '#{window_zoomed_flag}:#{pane_zoomed_flag}')" 1:1
|
||||||
|
check_ok resize-pane -Z -t "$base"
|
||||||
|
|
||||||
|
ignored=$($TMUX new-pane -ALdPF '#{pane_id}' -t "$base" 'cat') ||
|
||||||
|
fail "new-pane -A -L failed"
|
||||||
|
case "$(fmt "$ignored" '#{pane_flags}')" in
|
||||||
|
*A*) fail "tiled pane flags include A" ;;
|
||||||
|
*) ;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
# Existing floating panes are filtered when zoom begins: -A panes remain in the
|
||||||
|
# visible layout and ordinary floating panes do not.
|
||||||
|
check_ok new-window -d -t modal: -n existing-over-zoom 'cat'
|
||||||
|
base=$(fmt modal:existing-over-zoom '#{pane_id}')
|
||||||
|
check_ok split-window -dh -t "$base" 'cat'
|
||||||
|
over=$($TMUX new-pane -AdPF '#{pane_id}' -t "$base" \
|
||||||
|
-x 20 -y 5 -X 20 -Y 10 'cat') ||
|
||||||
|
fail "pre-existing new-pane -A failed"
|
||||||
|
under=$($TMUX new-pane -dPF '#{pane_id}' -t "$base" \
|
||||||
|
-x 15 -y 4 -X 2 -Y 2 'cat') ||
|
||||||
|
fail "pre-existing ordinary new-pane failed"
|
||||||
|
check_ok select-window -t modal:existing-over-zoom
|
||||||
|
check_ok select-pane -t "$base"
|
||||||
|
check_ok resize-pane -Z -t "$base"
|
||||||
|
must_equal "$(fmt "$over" '#{pane_floating_flag}')" 1
|
||||||
|
must_equal "$(fmt "$under" '#{pane_floating_flag}')" 0
|
||||||
|
must_equal "$(fmt "$base" '#{window_zoomed_flag}:#{pane_zoomed_flag}')" 1:1
|
||||||
|
|
||||||
|
# Geometry changed in the visible zoom layout is copied back when unzooming.
|
||||||
|
check_ok select-pane -t "$over"
|
||||||
|
left=$(fmt "$over" '#{pane_left}')
|
||||||
|
top=$(fmt "$over" '#{pane_top}')
|
||||||
|
meta_drag $((left + 2)) $((top + 2)) $((left + 7)) $((top + 4))
|
||||||
|
new_left=$(fmt "$over" '#{pane_left}')
|
||||||
|
new_top=$(fmt "$over" '#{pane_top}')
|
||||||
|
[ "$new_left" -gt "$left" ] || [ "$new_top" -gt "$top" ] ||
|
||||||
|
fail "float-over-zoom pane did not move"
|
||||||
|
must_equal "$(fmt "$base" '#{window_zoomed_flag}:#{pane_zoomed_flag}')" 1:1
|
||||||
|
check_ok resize-pane -Z -t "$base"
|
||||||
|
must_equal "$(fmt "$over" '#{pane_left}:#{pane_top}')" \
|
||||||
|
"$new_left:$new_top"
|
||||||
|
must_equal "$(fmt "$under" '#{pane_floating_flag}')" 1
|
||||||
|
|
||||||
|
# Resizing with the over-zoom pane active restores the original zoom target.
|
||||||
|
check_ok resize-pane -Z -t "$base"
|
||||||
|
check_ok select-pane -t "$over"
|
||||||
|
check_ok resize-window -t modal:existing-over-zoom -x 90 -y 30
|
||||||
|
must_equal "$(fmt "$base" \
|
||||||
|
'#{window_width}x#{window_height}:#{window_zoomed_flag}:#{pane_zoomed_flag}')" \
|
||||||
|
90x30:1:1
|
||||||
|
must_equal "$(fmt "$over" '#{pane_floating_flag}:#{pane_active}')" 1:1
|
||||||
|
|
||||||
|
# Natural pane exit uses a different removal path from kill-pane and must also
|
||||||
|
# preserve zoom.
|
||||||
|
dying=$($TMUX new-pane -AdPF '#{pane_id}' -t "$base" \
|
||||||
|
-x 12 -y 4 -X 4 -Y 3 'true') ||
|
||||||
|
fail "short-lived new-pane -A failed"
|
||||||
|
i=0
|
||||||
|
while $TMUX list-panes -a -F '#{pane_id}' | grep -qx "$dying"; do
|
||||||
|
i=$((i + 1))
|
||||||
|
[ $i -gt 50 ] && fail "short-lived float-over-zoom pane did not exit"
|
||||||
|
sleep 0.1
|
||||||
|
done
|
||||||
|
must_equal "$(fmt "$base" '#{window_zoomed_flag}:#{pane_zoomed_flag}')" 1:1
|
||||||
|
|
||||||
|
# A pane with -A is also above a zoom target which was itself floating. The
|
||||||
|
# temporary tiled target must sit behind retained floating panes, then return
|
||||||
|
# to the normal floating z order when unzoomed.
|
||||||
|
check_ok new-window -d -t modal: -n floating-zoom-target 'cat'
|
||||||
|
base=$(fmt modal:floating-zoom-target '#{pane_id}')
|
||||||
|
check_ok split-window -dh -t "$base" 'cat'
|
||||||
|
target=$($TMUX new-pane -dPF '#{pane_id}' -t "$base" \
|
||||||
|
-x 30 -y 10 -X 10 -Y 5 'cat') ||
|
||||||
|
fail "floating zoom target creation failed"
|
||||||
|
over=$($TMUX new-pane -AdPF '#{pane_id}' -t "$base" \
|
||||||
|
-x 15 -y 5 -X 15 -Y 8 'cat') ||
|
||||||
|
fail "float-over-zoom pane creation failed"
|
||||||
|
check_ok select-window -t modal:floating-zoom-target
|
||||||
|
check_ok select-pane -t "$target"
|
||||||
|
must_equal "$(fmt "$target" '#{pane_z}')" 0
|
||||||
|
must_equal "$(fmt "$over" '#{pane_z}')" 1
|
||||||
|
|
||||||
|
check_ok resize-pane -Z -t "$target"
|
||||||
|
must_equal "$(fmt "$target" \
|
||||||
|
'#{window_zoomed_flag}:#{pane_zoomed_flag}:#{pane_floating_flag}:#{pane_z}')" \
|
||||||
|
1:1:0:2
|
||||||
|
must_equal "$(fmt "$over" '#{pane_floating_flag}:#{pane_z}')" 1:0
|
||||||
|
check_ok select-pane -t "$over"
|
||||||
|
must_equal "$(fmt "$over" '#{window_zoomed_flag}:#{pane_active}')" 1:1
|
||||||
|
check_ok resize-pane -Z -t "$target"
|
||||||
|
must_equal "$(fmt "$over" '#{pane_active}:#{pane_z}')" 1:0
|
||||||
|
must_equal "$(fmt "$target" '#{pane_floating_flag}:#{pane_z}')" 1:1
|
||||||
|
|
||||||
|
# If the target remains active, it returns to the front on unzoom.
|
||||||
|
check_ok select-pane -t "$target"
|
||||||
|
check_ok resize-pane -Z -t "$target"
|
||||||
|
must_equal "$(fmt "$over" '#{pane_floating_flag}:#{pane_z}')" 1:0
|
||||||
|
check_ok resize-pane -Z -t "$target"
|
||||||
|
must_equal "$(fmt "$target" '#{pane_active}:#{pane_z}')" 1:0
|
||||||
|
must_equal "$(fmt "$over" '#{pane_z}')" 1
|
||||||
|
|
||||||
cleanup
|
cleanup
|
||||||
exit 0
|
exit 0
|
||||||
|
|||||||
@@ -269,6 +269,22 @@ test_customize_mode()
|
|||||||
assert_alive "customize-mode exit"
|
assert_alive "customize-mode exit"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
test_customize_break_pane()
|
||||||
|
{
|
||||||
|
start_client option-break
|
||||||
|
side=$($TMUX split-window -d -P -F '#{pane_id}' \
|
||||||
|
-t option-break:0 'cat') || fail "customize split failed"
|
||||||
|
|
||||||
|
$TMUX customize-mode -t "$side" || fail "customize-mode failed"
|
||||||
|
wait_mode "$side" 1
|
||||||
|
$TMUX break-pane -d -s "$side" || fail "customize break-pane failed"
|
||||||
|
|
||||||
|
assert_alive "customize-mode break-pane"
|
||||||
|
wait_mode "$side" 1
|
||||||
|
$TMUX send-keys -t "$side" q || fail "customize-mode quit failed"
|
||||||
|
wait_mode "$side" 0
|
||||||
|
}
|
||||||
|
|
||||||
test_copy_mode()
|
test_copy_mode()
|
||||||
{
|
{
|
||||||
start_client copy-a 'i=0; while [ $i -lt 200 ]; do echo "copy mutation line $i"; i=$((i + 1)); done; cat'
|
start_client copy-a 'i=0; while [ $i -lt 200 ]; do echo "copy mutation line $i"; i=$((i + 1)); done; cat'
|
||||||
@@ -302,6 +318,7 @@ test_choose_tree
|
|||||||
test_choose_buffer
|
test_choose_buffer
|
||||||
test_choose_client
|
test_choose_client
|
||||||
test_customize_mode
|
test_customize_mode
|
||||||
|
test_customize_break_pane
|
||||||
test_copy_mode
|
test_copy_mode
|
||||||
cleanup
|
cleanup
|
||||||
exit 0
|
exit 0
|
||||||
|
|||||||
@@ -19,7 +19,10 @@
|
|||||||
# - respawn-pane/respawn-window refusing a live pane without -k, working on a
|
# - respawn-pane/respawn-window refusing a live pane without -k, working on a
|
||||||
# dead pane (remain-on-exit) and killing with -k;
|
# dead pane (remain-on-exit) and killing with -k;
|
||||||
# - resize-pane -x/-y in cells and percent, -L/-R/-U/-D adjustments and -Z
|
# - resize-pane -x/-y in cells and percent, -L/-R/-U/-D adjustments and -Z
|
||||||
# zoom/unzoom (including implicit unzoom on split).
|
# zoom/unzoom, layout round trips, zoom target selection, pane removal,
|
||||||
|
# window resizing and implicit unzoom on layout changes;
|
||||||
|
# - swap-pane preserving one or two zoomed windows, including cross-window
|
||||||
|
# swaps of hidden panes and zoom targets.
|
||||||
#
|
#
|
||||||
# window-ops.sh covers window-level commands and buffers.sh paste buffers.
|
# window-ops.sh covers window-level commands and buffers.sh paste buffers.
|
||||||
|
|
||||||
@@ -144,7 +147,8 @@ check_panes P:0 "0:$p3 1:$p0 2:$p2 3:$p1"
|
|||||||
# break-pane and join-pane.
|
# break-pane and join-pane.
|
||||||
|
|
||||||
# break-pane moves a pane to a new window; -P -F prints where it went and -n
|
# break-pane moves a pane to a new window; -P -F prints where it went and -n
|
||||||
# names the new window.
|
# names the new window. Moving a pane out of a zoomed window unzooms it first.
|
||||||
|
check_ok resize-pane -Z -t "$p0"
|
||||||
out=$($TMUX break-pane -d -P -F '#{window_index}:#{pane_id}' -n broken \
|
out=$($TMUX break-pane -d -P -F '#{window_index}:#{pane_id}' -n broken \
|
||||||
-s "$p1" -t P:)
|
-s "$p1" -t P:)
|
||||||
if [ "$out" != "1:$p1" ]; then
|
if [ "$out" != "1:$p1" ]; then
|
||||||
@@ -153,10 +157,14 @@ if [ "$out" != "1:$p1" ]; then
|
|||||||
fi
|
fi
|
||||||
check_fmt 'P:1' '#{window_name}:#{window_panes}' 'broken:1'
|
check_fmt 'P:1' '#{window_name}:#{window_panes}' 'broken:1'
|
||||||
check_fmt 'P:0' '#{window_panes}' '3'
|
check_fmt 'P:0' '#{window_panes}' '3'
|
||||||
|
check_fmt 'P:0' '#{window_zoomed_flag}' '0'
|
||||||
|
|
||||||
# join-pane -v moves it back (the source window, left empty, is destroyed).
|
# join-pane -v moves it back (the source window, left empty, is destroyed) and
|
||||||
|
# also unzooms the destination before changing its layout.
|
||||||
|
check_ok resize-pane -Z -t "$p0"
|
||||||
check_ok join-pane -d -v -s P:broken.0 -t "$p2"
|
check_ok join-pane -d -v -s P:broken.0 -t "$p2"
|
||||||
check_fmt 'P:0' '#{window_panes}' '4'
|
check_fmt 'P:0' '#{window_panes}' '4'
|
||||||
|
check_fmt 'P:0' '#{window_zoomed_flag}' '0'
|
||||||
if $TMUX has-session -t P:broken 2>/dev/null; then
|
if $TMUX has-session -t P:broken 2>/dev/null; then
|
||||||
echo "Window 'broken' still exists after join-pane."
|
echo "Window 'broken' still exists after join-pane."
|
||||||
exit 1
|
exit 1
|
||||||
@@ -192,13 +200,13 @@ check_fail "invalid window name: $(printf 'a\377b')" \
|
|||||||
break-pane -d -n "$(printf 'a\377b')" -s "$p1" -t P:
|
break-pane -d -n "$(printf 'a\377b')" -s "$p1" -t P:
|
||||||
|
|
||||||
# join-pane can move a pane from one window to another without destroying
|
# join-pane can move a pane from one window to another without destroying
|
||||||
# the source window if other panes remain. (On this branch move-pane is
|
# the source window if other panes remain. move-pane does the same when no
|
||||||
# reserved for floating panes, covered by floating-pane-geometry.sh.)
|
# floating-pane movement flags are given.
|
||||||
check_ok new-window -d -t P:5 -n other
|
check_ok new-window -d -t P:5 -n other
|
||||||
check_ok join-pane -d -s "$p1" -t P:5.0
|
check_ok join-pane -d -s "$p1" -t P:5.0
|
||||||
check_fmt 'P:5' '#{window_panes}' '2'
|
check_fmt 'P:5' '#{window_panes}' '2'
|
||||||
check_fmt 'P:0' '#{window_panes}' '3'
|
check_fmt 'P:0' '#{window_panes}' '3'
|
||||||
check_ok join-pane -d -v -s "$p1" -t "$p2"
|
check_ok move-pane -d -v -s "$p1" -t "$p2"
|
||||||
check_fmt 'P:0' '#{window_panes}' '4'
|
check_fmt 'P:0' '#{window_panes}' '4'
|
||||||
check_fmt 'P:5' '#{window_panes}' '1'
|
check_fmt 'P:5' '#{window_panes}' '1'
|
||||||
|
|
||||||
@@ -299,6 +307,122 @@ check_fmt 'P:0' '#{window_panes}' '5'
|
|||||||
p6=$($TMUX display-message -p -t P:0.2 '#{pane_id}')
|
p6=$($TMUX display-message -p -t P:0.2 '#{pane_id}')
|
||||||
check_ok kill-pane -t "$p6"
|
check_ok kill-pane -t "$p6"
|
||||||
|
|
||||||
|
# Zoom and unzoom preserve the exact tiled layout. Selecting another pane
|
||||||
|
# without -Z unzooms, while -Z transfers zoom to the selected pane.
|
||||||
|
layout=$($TMUX display-message -p -t P:0 '#{window_layout}')
|
||||||
|
check_ok select-pane -t "$p0"
|
||||||
|
check_ok resize-pane -Z -t "$p0"
|
||||||
|
check_ok select-pane -t "$p2"
|
||||||
|
check_fmt "$p2" '#{window_zoomed_flag}:#{pane_active}' '0:1'
|
||||||
|
check_fmt P:0 '#{window_layout}' "$layout"
|
||||||
|
|
||||||
|
check_ok select-pane -t "$p0"
|
||||||
|
check_ok resize-pane -Z -t "$p0"
|
||||||
|
check_ok select-pane -Z -t "$p2"
|
||||||
|
check_fmt "$p2" '#{window_zoomed_flag}:#{pane_zoomed_flag}:#{pane_active}' \
|
||||||
|
'1:1:1'
|
||||||
|
check_ok resize-pane -Z -t "$p2"
|
||||||
|
check_fmt P:0 '#{window_layout}' "$layout"
|
||||||
|
|
||||||
|
# Directional selection temporarily restores the full layout to find its
|
||||||
|
# neighbour, then follows the same unzoom or -Z transfer rules.
|
||||||
|
check_ok select-pane -t "$p0"
|
||||||
|
check_ok resize-pane -Z -t "$p0"
|
||||||
|
check_ok select-pane -t "$p0"
|
||||||
|
check_fmt "$p0" '#{window_zoomed_flag}:#{pane_zoomed_flag}:#{pane_active}' \
|
||||||
|
'1:1:1'
|
||||||
|
check_ok select-pane -D -t "$p0"
|
||||||
|
check_fmt "$p2" '#{window_zoomed_flag}:#{pane_active}' '0:1'
|
||||||
|
|
||||||
|
check_ok select-pane -t "$p0"
|
||||||
|
check_ok resize-pane -Z -t "$p0"
|
||||||
|
check_ok select-pane -D -Z -t "$p0"
|
||||||
|
check_fmt "$p2" '#{window_zoomed_flag}:#{pane_zoomed_flag}:#{pane_active}' \
|
||||||
|
'1:1:1'
|
||||||
|
check_ok resize-pane -Z -t "$p2"
|
||||||
|
check_fmt P:0 '#{window_layout}' "$layout"
|
||||||
|
|
||||||
|
# The last-pane path has separate zoom handling, both with and without -Z.
|
||||||
|
check_ok select-pane -t "$p0"
|
||||||
|
check_ok select-pane -t "$p2"
|
||||||
|
check_ok resize-pane -Z -t "$p2"
|
||||||
|
check_ok select-pane -l -t P:0
|
||||||
|
check_fmt "$p0" '#{window_zoomed_flag}:#{pane_zoomed_flag}:#{pane_active}' \
|
||||||
|
'0:0:1'
|
||||||
|
|
||||||
|
check_ok select-pane -t "$p0"
|
||||||
|
check_ok select-pane -t "$p2"
|
||||||
|
check_ok resize-pane -Z -t "$p2"
|
||||||
|
check_ok select-pane -l -Z -t P:0
|
||||||
|
check_fmt "$p0" '#{window_zoomed_flag}:#{pane_zoomed_flag}:#{pane_active}' \
|
||||||
|
'1:1:1'
|
||||||
|
check_ok resize-pane -Z -t "$p0"
|
||||||
|
check_fmt P:0 '#{window_layout}' "$layout"
|
||||||
|
|
||||||
|
# Killing either a hidden ordinary pane or the zoom target unzooms.
|
||||||
|
check_ok new-window -d -t P:12 -n zoom-kill 'cat'
|
||||||
|
zk0=$($TMUX display-message -p -t P:12.0 '#{pane_id}')
|
||||||
|
zk1=$($TMUX split-window -d -P -F '#{pane_id}' -t P:12.0 'cat')
|
||||||
|
zk2=$($TMUX split-window -d -P -F '#{pane_id}' -t P:12.0 'cat')
|
||||||
|
check_ok resize-pane -Z -t "$zk0"
|
||||||
|
check_ok kill-pane -t "$zk1"
|
||||||
|
check_fmt "$zk0" '#{window_panes}:#{window_zoomed_flag}:#{pane_zoomed_flag}' \
|
||||||
|
'2:0:0'
|
||||||
|
check_ok resize-pane -Z -t "$zk0"
|
||||||
|
check_ok kill-pane -t "$zk0"
|
||||||
|
check_fmt "$zk2" '#{window_panes}:#{window_zoomed_flag}:#{pane_zoomed_flag}' \
|
||||||
|
'1:0:0'
|
||||||
|
check_ok kill-window -t P:12
|
||||||
|
|
||||||
|
# The same cases through natural process exit exercise server_destroy_pane.
|
||||||
|
check_ok new-window -d -t P:13 -n zoom-exit 'cat'
|
||||||
|
ze0=$($TMUX display-message -p -t P:13.0 '#{pane_id}')
|
||||||
|
ze1=$($TMUX split-window -d -P -F '#{pane_id}' -t P:13.0 'cat')
|
||||||
|
ze2=$($TMUX split-window -d -P -F '#{pane_id}' -t P:13.0 'cat')
|
||||||
|
check_ok resize-pane -Z -t "$ze0"
|
||||||
|
check_ok send-keys -t "$ze1" C-d
|
||||||
|
i=0
|
||||||
|
while [ "$($TMUX display-message -p -t P:13 '#{window_panes}')" != 2 ]; do
|
||||||
|
i=$((i + 1))
|
||||||
|
[ $i -gt 50 ] && { echo "Hidden pane did not exit."; exit 1; }
|
||||||
|
sleep 0.1
|
||||||
|
done
|
||||||
|
check_fmt "$ze0" '#{window_zoomed_flag}:#{pane_zoomed_flag}' '0:0'
|
||||||
|
check_ok resize-pane -Z -t "$ze2"
|
||||||
|
check_ok send-keys -t "$ze2" C-d
|
||||||
|
i=0
|
||||||
|
while [ "$($TMUX display-message -p -t P:13 '#{window_panes}')" != 1 ]; do
|
||||||
|
i=$((i + 1))
|
||||||
|
[ $i -gt 50 ] && { echo "Zoomed pane did not exit."; exit 1; }
|
||||||
|
sleep 0.1
|
||||||
|
done
|
||||||
|
check_fmt "$ze0" '#{window_zoomed_flag}:#{pane_zoomed_flag}' '0:0'
|
||||||
|
check_ok kill-window -t P:13
|
||||||
|
|
||||||
|
# resize-window restores both tiled and floating zoom targets after rebuilding
|
||||||
|
# the layout at the new size.
|
||||||
|
check_ok new-window -d -t P:14 -n zoom-resize 'cat'
|
||||||
|
zr0=$($TMUX display-message -p -t P:14.0 '#{pane_id}')
|
||||||
|
check_ok split-window -d -t P:14.0 'cat'
|
||||||
|
check_ok resize-pane -Z -t "$zr0"
|
||||||
|
check_ok resize-window -t P:14 -x 90 -y 30
|
||||||
|
check_fmt "$zr0" \
|
||||||
|
'#{window_width}x#{window_height}:#{window_zoomed_flag}:#{pane_zoomed_flag}' \
|
||||||
|
'90x30:1:1'
|
||||||
|
check_ok resize-pane -Z -t "$zr0"
|
||||||
|
zrf=$($TMUX new-pane -dP -F '#{pane_id}' -t P:14 -x 20 -y 6 'cat')
|
||||||
|
zrf_size=$($TMUX display-message -p -t "$zrf" \
|
||||||
|
'#{pane_width}x#{pane_height}')
|
||||||
|
check_ok resize-pane -Z -t "$zrf"
|
||||||
|
check_ok resize-window -t P:14 -x 100 -y 32
|
||||||
|
check_fmt "$zrf" \
|
||||||
|
'#{window_width}x#{window_height}:#{window_zoomed_flag}:#{pane_zoomed_flag}' \
|
||||||
|
'100x32:1:1'
|
||||||
|
check_ok resize-pane -Z -t "$zrf"
|
||||||
|
check_fmt "$zrf" '#{pane_floating_flag}:#{pane_width}x#{pane_height}' \
|
||||||
|
"1:$zrf_size"
|
||||||
|
check_ok kill-window -t P:14
|
||||||
|
|
||||||
# ---------------------------------------------------------------------------
|
# ---------------------------------------------------------------------------
|
||||||
# kill-pane.
|
# kill-pane.
|
||||||
|
|
||||||
@@ -307,8 +431,10 @@ check_ok kill-pane -t "$p3"
|
|||||||
check_panes P:0 "0:$p0 1:$p2 2:$p1"
|
check_panes P:0 "0:$p0 1:$p2 2:$p1"
|
||||||
|
|
||||||
# -a kills every pane except the target.
|
# -a kills every pane except the target.
|
||||||
|
check_ok resize-pane -Z -t "$p0"
|
||||||
check_ok kill-pane -a -t "$p0"
|
check_ok kill-pane -a -t "$p0"
|
||||||
check_panes P:0 "0:$p0"
|
check_panes P:0 "0:$p0"
|
||||||
|
check_fmt "$p0" '#{window_zoomed_flag}:#{pane_zoomed_flag}' '0:0'
|
||||||
|
|
||||||
# Killing the last pane in a window kills the window.
|
# Killing the last pane in a window kills the window.
|
||||||
check_ok new-window -d -t P:7 -n goner
|
check_ok new-window -d -t P:7 -n goner
|
||||||
@@ -516,6 +642,10 @@ check_panes P:3 "0:$r0 1:$r1 2:$r2"
|
|||||||
# Swapping a pane with itself quietly does nothing.
|
# Swapping a pane with itself quietly does nothing.
|
||||||
check_ok swap-pane -d -s "$r1" -t "$r1"
|
check_ok swap-pane -d -s "$r1" -t "$r1"
|
||||||
check_panes P:3 "0:$r0 1:$r1 2:$r2"
|
check_panes P:3 "0:$r0 1:$r1 2:$r2"
|
||||||
|
check_ok resize-pane -Z -t "$r1"
|
||||||
|
check_ok swap-pane -d -Z -s "$r1" -t "$r1"
|
||||||
|
check_fmt "$r1" '#{window_zoomed_flag}:#{pane_zoomed_flag}' '1:1'
|
||||||
|
check_ok resize-pane -Z -t "$r1"
|
||||||
|
|
||||||
# Panes can be swapped between different windows.
|
# Panes can be swapped between different windows.
|
||||||
check_ok swap-pane -d -s "$o0" -t "$r1"
|
check_ok swap-pane -d -s "$o0" -t "$r1"
|
||||||
@@ -525,10 +655,38 @@ check_ok swap-pane -d -s "$r1" -t "$o0"
|
|||||||
check_panes P:3 "0:$r0 1:$r1 2:$r2"
|
check_panes P:3 "0:$r0 1:$r1 2:$r2"
|
||||||
check_panes P:5 "0:$o0"
|
check_panes P:5 "0:$o0"
|
||||||
|
|
||||||
|
# With both windows zoomed, -Z preserves each window's zoom when hidden panes
|
||||||
|
# are exchanged and when the zoom targets themselves are exchanged.
|
||||||
|
o1=$($TMUX split-window -dP -F '#{pane_id}' -t P:5.0)
|
||||||
|
check_ok resize-pane -Z -t "$r0"
|
||||||
|
check_ok resize-pane -Z -t "$o0"
|
||||||
|
check_ok swap-pane -d -Z -s "$r1" -t "$o1"
|
||||||
|
check_fmt "$r0" \
|
||||||
|
'#{window_name}:#{window_zoomed_flag}:#{pane_zoomed_flag}' 'swaps:1:1'
|
||||||
|
check_fmt "$o0" \
|
||||||
|
'#{window_name}:#{window_zoomed_flag}:#{pane_zoomed_flag}' 'other:1:1'
|
||||||
|
check_fmt "$r1" '#{window_name}' 'other'
|
||||||
|
check_fmt "$o1" '#{window_name}' 'swaps'
|
||||||
|
|
||||||
|
check_ok swap-pane -d -Z -s "$r0" -t "$o0"
|
||||||
|
check_fmt "$o0" \
|
||||||
|
'#{window_name}:#{window_zoomed_flag}:#{pane_zoomed_flag}' 'swaps:1:1'
|
||||||
|
check_fmt "$r0" \
|
||||||
|
'#{window_name}:#{window_zoomed_flag}:#{pane_zoomed_flag}' 'other:1:1'
|
||||||
|
check_ok resize-pane -Z -t "$o0"
|
||||||
|
check_ok resize-pane -Z -t "$r0"
|
||||||
|
|
||||||
|
# Restore the original panes before the remaining swap tests.
|
||||||
|
check_ok swap-pane -d -s "$r0" -t "$o0"
|
||||||
|
check_ok swap-pane -d -s "$r1" -t "$o1"
|
||||||
|
check_ok kill-pane -t "$o1"
|
||||||
|
check_panes P:3 "0:$r0 1:$r1 2:$r2"
|
||||||
|
check_panes P:5 "0:$o0"
|
||||||
|
|
||||||
# -Z keeps the window zoomed across the swap.
|
# -Z keeps the window zoomed across the swap.
|
||||||
check_ok resize-pane -Z -t "$r0"
|
check_ok resize-pane -Z -t "$r0"
|
||||||
check_ok swap-pane -d -Z -s "$r0" -t "$r1"
|
check_ok swap-pane -d -Z -s "$r0" -t "$r1"
|
||||||
check_fmt 'P:3' '#{window_zoomed_flag}' '1'
|
check_fmt "$r0" '#{window_zoomed_flag}:#{pane_zoomed_flag}' '1:1'
|
||||||
check_ok resize-pane -Z -t P:3
|
check_ok resize-pane -Z -t P:3
|
||||||
check_panes P:3 "0:$r1 1:$r0 2:$r2"
|
check_panes P:3 "0:$r1 1:$r0 2:$r2"
|
||||||
|
|
||||||
|
|||||||
@@ -35,9 +35,9 @@ check_alive()
|
|||||||
|
|
||||||
# A pipe-pane -I child may write after the pane process has exited. With
|
# A pipe-pane -I child may write after the pane process has exited. With
|
||||||
# remain-on-exit, the pane stays around but its bufferevent has been freed.
|
# remain-on-exit, the pane stays around but its bufferevent has been freed.
|
||||||
check_ok new-session -d -s pipe -x 80 -y 24 'sleep 0.2'
|
check_ok new-session -d -s pipe -x 80 -y 24 'sleep 1'
|
||||||
check_ok set-option -t pipe:0 remain-on-exit on
|
check_ok set-option -t pipe:0 remain-on-exit on
|
||||||
check_ok pipe-pane -t pipe:0.0 -I 'sleep 0.6; printf x'
|
check_ok pipe-pane -t pipe:0.0 -I 'sleep 2; printf x'
|
||||||
|
|
||||||
i=0
|
i=0
|
||||||
while [ "$($TMUX display-message -p -t pipe:0.0 '#{pane_dead}')" != "1" ]; do
|
while [ "$($TMUX display-message -p -t pipe:0.0 '#{pane_dead}')" != "1" ]; do
|
||||||
@@ -46,7 +46,7 @@ while [ "$($TMUX display-message -p -t pipe:0.0 '#{pane_dead}')" != "1" ]; do
|
|||||||
sleep 0.1
|
sleep 0.1
|
||||||
done
|
done
|
||||||
|
|
||||||
sleep 0.7
|
sleep 2
|
||||||
check_alive
|
check_alive
|
||||||
|
|
||||||
$TMUX kill-server 2>/dev/null
|
$TMUX kill-server 2>/dev/null
|
||||||
|
|||||||
87
regress/respawn-pane-control-lag.sh
Normal file
87
regress/respawn-pane-control-lag.sh
Normal file
@@ -0,0 +1,87 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
# respawn-pane frees the pane's buffer and creates an empty one. The pane
|
||||||
|
# offsets and each control client's offsets used to keep pointing into the old
|
||||||
|
# buffer, so if an attached control client had not caught up on the pane's
|
||||||
|
# output, the next read from the new process ran off the end of the new buffer
|
||||||
|
# and the server crashed in input_parse.
|
||||||
|
#
|
||||||
|
# Two ways for a client to be behind are tested: a control client whose output
|
||||||
|
# goes down a fifo that is never read (a second, healthy control client keeps
|
||||||
|
# the pane being read), and a healthy control client viewing a session that
|
||||||
|
# the pane's window has been moved out of.
|
||||||
|
|
||||||
|
PATH=/bin:/usr/bin
|
||||||
|
TERM=screen
|
||||||
|
|
||||||
|
[ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux)
|
||||||
|
TMUX="$TEST_TMUX -Ltest"
|
||||||
|
$TMUX kill-server 2>/dev/null
|
||||||
|
|
||||||
|
DIR=$(mktemp -d)
|
||||||
|
FIFO=$DIR/fifo
|
||||||
|
SERVER=
|
||||||
|
|
||||||
|
mkfifo "$FIFO" || exit 1
|
||||||
|
|
||||||
|
cleanup() {
|
||||||
|
[ -n "$SERVER" ] && kill -9 "$SERVER" 2>/dev/null
|
||||||
|
$TMUX kill-server 2>/dev/null
|
||||||
|
exec 8<&- 2>/dev/null
|
||||||
|
rm -rf "$DIR"
|
||||||
|
}
|
||||||
|
trap cleanup 0 1 15
|
||||||
|
|
||||||
|
alive() {
|
||||||
|
if ! kill -0 "$SERVER" 2>/dev/null; then
|
||||||
|
SERVER=
|
||||||
|
echo "server died after $1"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
$TMUX has -t rt || exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
wait_clients() {
|
||||||
|
n=0
|
||||||
|
while [ $n -lt 50 ]; do
|
||||||
|
[ "$($TMUX lsc 2>/dev/null | wc -l)" -ge $1 ] && return
|
||||||
|
sleep 0.1
|
||||||
|
n=$((n + 1))
|
||||||
|
done
|
||||||
|
echo "control clients did not attach"; exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
# A detached session with a pane that writes a line every 10 milliseconds.
|
||||||
|
$TMUX -f/dev/null new -d -x 80 -y 24 -s rt \
|
||||||
|
'while :; do echo xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx; sleep 0.01; done' ||
|
||||||
|
exit 1
|
||||||
|
SERVER=$($TMUX display -pt rt '#{pid}')
|
||||||
|
|
||||||
|
# One control client with output down the unread fifo and one reading
|
||||||
|
# normally; stdin of both held open by sleep so they stay attached.
|
||||||
|
( sleep 60 ) | $TMUX -f/dev/null -C attach -t rt >"$FIFO" 2>&1 &
|
||||||
|
exec 8<"$FIFO"
|
||||||
|
( sleep 60 ) | $TMUX -f/dev/null -C attach -t rt >/dev/null 2>&1 &
|
||||||
|
wait_clients 2
|
||||||
|
|
||||||
|
# Let the first client fall behind, then respawn the pane. The new process
|
||||||
|
# writes at once and the server must survive reading it.
|
||||||
|
sleep 3
|
||||||
|
$TMUX respawn-pane -k -t rt:0 'echo respawned; sleep 60' || exit 1
|
||||||
|
sleep 1
|
||||||
|
alive "respawn-pane with a lagging control client"
|
||||||
|
|
||||||
|
# Now a pane whose window is moved out of the session both clients view: their
|
||||||
|
# offsets for it stop advancing, so they fall behind until it is respawned.
|
||||||
|
$TMUX neww -d -t rt \
|
||||||
|
'while :; do echo yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy; sleep 0.01; done' ||
|
||||||
|
exit 1
|
||||||
|
sleep 1
|
||||||
|
$TMUX new -d -s other || exit 1
|
||||||
|
$TMUX movew -d -s rt:1 -t other: || exit 1
|
||||||
|
sleep 2
|
||||||
|
$TMUX respawn-pane -k -t other:1 'echo respawned; sleep 60' || exit 1
|
||||||
|
sleep 1
|
||||||
|
alive "respawn-pane on a window moved out of the clients' session"
|
||||||
|
|
||||||
|
exit 0
|
||||||
49
regress/server-socket-error.sh
Executable file
49
regress/server-socket-error.sh
Executable file
@@ -0,0 +1,49 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
# Server socket creation failures must produce a nonzero client exit status.
|
||||||
|
|
||||||
|
PATH=/bin:/usr/bin
|
||||||
|
TERM=screen
|
||||||
|
export TERM
|
||||||
|
|
||||||
|
[ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux)
|
||||||
|
|
||||||
|
# Root bypasses directory mode bits, so it cannot trigger this failure.
|
||||||
|
[ "$(id -u)" -eq 0 ] && exit 0
|
||||||
|
|
||||||
|
TMP=$(mktemp -d) || exit 1
|
||||||
|
SOCKET="$TMP/ro/socket"
|
||||||
|
|
||||||
|
cleanup()
|
||||||
|
{
|
||||||
|
chmod 700 "$TMP/ro" 2>/dev/null
|
||||||
|
rm -rf "$TMP"
|
||||||
|
}
|
||||||
|
|
||||||
|
fail()
|
||||||
|
{
|
||||||
|
echo "$1" >&2
|
||||||
|
cleanup
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
mkdir "$TMP/ro" || exit 1
|
||||||
|
chmod 500 "$TMP/ro" || exit 1
|
||||||
|
trap cleanup 0 1 15
|
||||||
|
|
||||||
|
check_failure()
|
||||||
|
{
|
||||||
|
output=$($TEST_TMUX -S "$SOCKET" -f/dev/null "$@" 2>&1)
|
||||||
|
retval=$?
|
||||||
|
|
||||||
|
[ "$retval" -ne 0 ] || fail "tmux $* exited zero: $output"
|
||||||
|
case "$output" in
|
||||||
|
"error creating $SOCKET ("*) ;;
|
||||||
|
*) fail "tmux $* produced unexpected error: $output" ;;
|
||||||
|
esac
|
||||||
|
}
|
||||||
|
|
||||||
|
check_failure start-server
|
||||||
|
check_failure new-session -d
|
||||||
|
|
||||||
|
exit 0
|
||||||
173
regress/sync-output-atomic.sh
Normal file
173
regress/sync-output-atomic.sh
Normal file
@@ -0,0 +1,173 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
# Application synchronized updates must remain one physical client transaction,
|
||||||
|
# including when BSU, printable cells and ESU arrive in one pane read.
|
||||||
|
|
||||||
|
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
|
||||||
|
ASSERT=$DIR/assert-sync-output.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_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_marker()
|
||||||
|
{
|
||||||
|
i=0
|
||||||
|
while [ "$i" -lt 50 ]; do
|
||||||
|
grep -q 'FRAME_000001_ROW_23' "$CLIENT_BYTES" 2>/dev/null && return 0
|
||||||
|
sleep 0.1
|
||||||
|
i=$((i + 1))
|
||||||
|
done
|
||||||
|
fail "client did not receive both synchronized frames"
|
||||||
|
}
|
||||||
|
|
||||||
|
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{/[^/]+$}{};
|
||||||
|
open my $ready, '>', "$dir/ready" or die "$dir/ready: $!\n";
|
||||||
|
close $ready;
|
||||||
|
while (!-e $control) {
|
||||||
|
select undef, undef, undef, 0.01;
|
||||||
|
}
|
||||||
|
for my $frame (0 .. 1) {
|
||||||
|
my @rows;
|
||||||
|
for my $row (0 .. 23) {
|
||||||
|
my $marker = sprintf "FRAME_%06d_ROW_%02d_", $frame, $row;
|
||||||
|
push @rows, substr($marker . ('X' x 79), 0, 79);
|
||||||
|
}
|
||||||
|
my $frame = "\e[?2026h\e[H" . join("\r\n", @rows) .
|
||||||
|
"\e[?2026l";
|
||||||
|
my $written = syswrite STDOUT, $frame;
|
||||||
|
die "short synchronized frame write\n"
|
||||||
|
unless defined $written && $written == length $frame;
|
||||||
|
select undef, undef, undef, 0.2;
|
||||||
|
}
|
||||||
|
select undef, undef, undef, 2;
|
||||||
|
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
|
||||||
|
|
||||||
|
i=0
|
||||||
|
while [ "$i" -lt 50 ] && [ ! -e "$DIR/ready" ]; do
|
||||||
|
sleep 0.1
|
||||||
|
i=$((i + 1))
|
||||||
|
done
|
||||||
|
[ -e "$DIR/ready" ] || fail "application emitter did not become ready"
|
||||||
|
$OUTER pipe-pane -O -t outer:0.0 "cat >'$CLIENT_BYTES'" || exit 1
|
||||||
|
: >"$CONTROL"
|
||||||
|
wait_for_marker
|
||||||
|
wait_for_stable_bytes
|
||||||
|
$OUTER pipe-pane -t outer:0.0 || exit 1
|
||||||
|
|
||||||
|
cat >"$ASSERT" <<'PERL'
|
||||||
|
use strict;
|
||||||
|
use warnings;
|
||||||
|
|
||||||
|
my $path = shift;
|
||||||
|
open my $fh, '<:raw', $path or die "$path: $!\n";
|
||||||
|
local $/;
|
||||||
|
my $bytes = <$fh>;
|
||||||
|
my ($on, $off) = ("\e[?2026h", "\e[?2026l");
|
||||||
|
my ($active, %seen);
|
||||||
|
|
||||||
|
for (my $i = 0; $i < length($bytes);) {
|
||||||
|
if (substr($bytes, $i, length($on)) eq $on) {
|
||||||
|
die "duplicate synchronized-output start at byte $i\n" if $active;
|
||||||
|
$active = 1;
|
||||||
|
$i += length($on);
|
||||||
|
next;
|
||||||
|
}
|
||||||
|
if (substr($bytes, $i, length($off)) eq $off) {
|
||||||
|
die "unmatched synchronized-output end at byte $i\n" unless $active;
|
||||||
|
$active = 0;
|
||||||
|
$i += length($off);
|
||||||
|
next;
|
||||||
|
}
|
||||||
|
if (substr($bytes, $i) =~ /\AFRAME_(\d{6})_ROW_(\d{2})_/) {
|
||||||
|
my $marker = "$1:$2";
|
||||||
|
die "marker $marker outside synchronized output\n" unless $active;
|
||||||
|
$seen{$marker}++;
|
||||||
|
}
|
||||||
|
$i++;
|
||||||
|
}
|
||||||
|
die "unterminated synchronized-output transaction\n" if $active;
|
||||||
|
for my $frame (0 .. 1) {
|
||||||
|
for my $row (0 .. 23) {
|
||||||
|
my $marker = sprintf "%06d:%02d", $frame, $row;
|
||||||
|
die "missing marker $marker\n" unless $seen{$marker};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
PERL
|
||||||
|
|
||||||
|
perl "$ASSERT" "$CLIENT_BYTES" || exit 1
|
||||||
43
regress/tab-cell-background.sh
Executable file
43
regress/tab-cell-background.sh
Executable file
@@ -0,0 +1,43 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
# When a write breaks up a wide character or a tab, the columns it covered must
|
||||||
|
# keep the background colour, both in the grid and on the terminal. The second
|
||||||
|
# server is attached to the first so that what is actually sent is checked, not
|
||||||
|
# just what tmux has stored.
|
||||||
|
|
||||||
|
PATH=/bin:/usr/bin
|
||||||
|
TERM=screen
|
||||||
|
|
||||||
|
[ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux)
|
||||||
|
TMUX="$TEST_TMUX -LtestA$$ -f/dev/null"
|
||||||
|
$TMUX kill-server 2>/dev/null
|
||||||
|
TMUX2="$TEST_TMUX -LtestB$$ -f/dev/null"
|
||||||
|
$TMUX2 kill-server 2>/dev/null
|
||||||
|
|
||||||
|
TMP=$(mktemp)
|
||||||
|
trap "rm -f $TMP" 0 1 15
|
||||||
|
|
||||||
|
# 1. Write inside a tab. 2. Overwrite the right half of a wide character.
|
||||||
|
# 3. Two tabs of different colours, the first partly deleted so that padding
|
||||||
|
# from both ends up next to each other, then written over.
|
||||||
|
# The sleep is so that the client is attached before anything is written and
|
||||||
|
# the changes are sent as they happen rather than as one redraw.
|
||||||
|
$TMUX2 -f/dev/null new -d -x24 -y3 "
|
||||||
|
sleep 2
|
||||||
|
printf '\033[44m\033[K\t\033[4GX'
|
||||||
|
printf '\033[2;1H\033[44m\033[K\344\270\226\033[2GX'
|
||||||
|
printf '\033[3;1H\033[42m\033[K\t\033[43m\033[K\t\033[0m\033[4G\033[8P\rx'
|
||||||
|
cat" || exit 1
|
||||||
|
$TMUX2 set -g status off || exit 1
|
||||||
|
|
||||||
|
$TMUX -f/dev/null new -x24 -y4 -d "$TMUX2 attach" || exit 1
|
||||||
|
sleep 4
|
||||||
|
|
||||||
|
$TMUX capturep -pe -S0 -E2 >$TMP || exit 1
|
||||||
|
$TMUX kill-server 2>/dev/null
|
||||||
|
$TMUX2 kill-server 2>/dev/null
|
||||||
|
|
||||||
|
printf '\033[44m X\n X\n\033[49mx\033[42m \033[43m \033[49m\n' |
|
||||||
|
cmp - $TMP || exit 1
|
||||||
|
|
||||||
|
exit 0
|
||||||
@@ -105,6 +105,21 @@ sleep 1
|
|||||||
CLIENT=$($TMUX2 list-clients -F '#{client_name}' | head -1)
|
CLIENT=$($TMUX2 list-clients -F '#{client_name}' | head -1)
|
||||||
[ -n "$CLIENT" ] || fail "no inner client"
|
[ -n "$CLIENT" ] || fail "no inner client"
|
||||||
|
|
||||||
|
# A variation selector which widens the previous character must use window
|
||||||
|
# coordinates when checking visibility in a pane with a nonzero x offset.
|
||||||
|
$TMUX2 set -s variation-selector-always-wide on || exit 1
|
||||||
|
WINDOW=$($TMUX2 neww -dPF '#{window_id}' "exec sleep 100") || exit 1
|
||||||
|
PANE=$($TMUX2 splitw -dhPF '#{pane_id}' -t "$WINDOW" \
|
||||||
|
"exec sleep 100") || exit 1
|
||||||
|
$TMUX2 selectw -t "$WINDOW" || exit 1
|
||||||
|
$TMUX2 respawnp -k -t "$PANE" \
|
||||||
|
"printf '\nA\342\234\217\357\270\217B'; exec sleep 100" || exit 1
|
||||||
|
sleep 1
|
||||||
|
$TMUX2 capturep -p -t "$PANE" >$TMP || exit 1
|
||||||
|
EXPECTED=$(printf 'A\342\234\217\357\270\217B')
|
||||||
|
check_line 2 "$EXPECTED"
|
||||||
|
$TMUX2 killw -t "$WINDOW" || exit 1
|
||||||
|
|
||||||
# Long line, then short line: default cells after cellsize must clear stale text.
|
# Long line, then short line: default cells after cellsize must clear stale text.
|
||||||
capture
|
capture
|
||||||
check_line 1 "abcdefghijklmnopqrst"
|
check_line 1 "abcdefghijklmnopqrst"
|
||||||
|
|||||||
@@ -17,7 +17,8 @@
|
|||||||
# unlink the last link without -k;
|
# unlink the last link without -k;
|
||||||
# - swap-window within and between sessions, -d keeping the active window,
|
# - swap-window within and between sessions, -d keeping the active window,
|
||||||
# and the grouped-sessions error;
|
# and the grouped-sessions error;
|
||||||
# - rotate-window -U/-D rotating pane positions;
|
# - rotate-window -U/-D rotating pane positions, with and without preserving
|
||||||
|
# zoom using -Z;
|
||||||
# - kill-window switching to the last (previously current) window, kill-window
|
# - kill-window switching to the last (previously current) window, kill-window
|
||||||
# -a killing all other windows and the "-f only valid with -a" guard.
|
# -a killing all other windows and the "-f only valid with -a" guard.
|
||||||
#
|
#
|
||||||
@@ -306,6 +307,24 @@ check_fmt 'R:0' '#{pane_index}:#{pane_id}' "0:$p1"
|
|||||||
check_ok rotate-window -D -t R:0
|
check_ok rotate-window -D -t R:0
|
||||||
check_fmt 'R:0' '#{pane_index}:#{pane_id}' "0:$p0"
|
check_fmt 'R:0' '#{pane_index}:#{pane_id}' "0:$p0"
|
||||||
|
|
||||||
|
# Rotation without -Z unzooms. With -Z it preserves zoom and transfers it to
|
||||||
|
# the pane which arrives at the active position.
|
||||||
|
layout=$($TMUX display-message -p -t R:0 '#{window_layout}')
|
||||||
|
check_ok resize-pane -Z -t "$p0"
|
||||||
|
check_ok rotate-window -U -t R:0
|
||||||
|
check_fmt "$p1" '#{window_zoomed_flag}:#{pane_zoomed_flag}:#{pane_active}' \
|
||||||
|
'0:0:1'
|
||||||
|
check_ok rotate-window -D -t R:0
|
||||||
|
check_fmt 'R:0' '#{window_layout}' "$layout"
|
||||||
|
|
||||||
|
check_ok resize-pane -Z -t "$p0"
|
||||||
|
check_ok rotate-window -U -Z -t R:0
|
||||||
|
check_fmt "$p1" '#{window_zoomed_flag}:#{pane_zoomed_flag}:#{pane_active}' \
|
||||||
|
'1:1:1'
|
||||||
|
check_ok resize-pane -Z -t "$p1"
|
||||||
|
check_ok rotate-window -D -t R:0
|
||||||
|
check_fmt 'R:0' '#{window_layout}' "$layout"
|
||||||
|
|
||||||
# ---------------------------------------------------------------------------
|
# ---------------------------------------------------------------------------
|
||||||
# kill-window.
|
# kill-window.
|
||||||
|
|
||||||
|
|||||||
16
resize.c
16
resize.c
@@ -1,4 +1,4 @@
|
|||||||
/* $OpenBSD: resize.c,v 1.58 2026/07/17 08:37:29 nicm Exp $ */
|
/* $OpenBSD: resize.c,v 1.59 2026/08/20 09:19:24 nicm Exp $ */
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>
|
* Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>
|
||||||
@@ -42,8 +42,8 @@ resize_fire_window_resized(struct window *w, u_int old_sx, u_int old_sy)
|
|||||||
void
|
void
|
||||||
resize_window(struct window *w, u_int sx, u_int sy, int xpixel, int ypixel)
|
resize_window(struct window *w, u_int sx, u_int sy, int xpixel, int ypixel)
|
||||||
{
|
{
|
||||||
|
struct window_pane *zwp;
|
||||||
u_int old_sx = w->sx, old_sy = w->sy;
|
u_int old_sx = w->sx, old_sy = w->sy;
|
||||||
int zoomed;
|
|
||||||
|
|
||||||
/* Check size limits. */
|
/* Check size limits. */
|
||||||
if (sx < WINDOW_MINIMUM)
|
if (sx < WINDOW_MINIMUM)
|
||||||
@@ -56,8 +56,8 @@ resize_window(struct window *w, u_int sx, u_int sy, int xpixel, int ypixel)
|
|||||||
sy = WINDOW_MAXIMUM;
|
sy = WINDOW_MAXIMUM;
|
||||||
|
|
||||||
/* If the window is zoomed, unzoom. */
|
/* If the window is zoomed, unzoom. */
|
||||||
zoomed = w->flags & WINDOW_ZOOMED;
|
zwp = window_zoomed_pane(w);
|
||||||
if (zoomed)
|
if (zwp != NULL)
|
||||||
window_unzoom(w, 1);
|
window_unzoom(w, 1);
|
||||||
|
|
||||||
/* Resize the layout first. */
|
/* Resize the layout first. */
|
||||||
@@ -73,8 +73,8 @@ resize_window(struct window *w, u_int sx, u_int sy, int xpixel, int ypixel)
|
|||||||
sx, sy, w->layout_root->g.sx, w->layout_root->g.sy);
|
sx, sy, w->layout_root->g.sx, w->layout_root->g.sy);
|
||||||
|
|
||||||
/* Restore the window zoom state. */
|
/* Restore the window zoom state. */
|
||||||
if (zoomed)
|
if (zwp != NULL && window_has_pane(w, zwp))
|
||||||
window_zoom(w->active);
|
window_zoom(zwp);
|
||||||
|
|
||||||
tty_update_window_offset(w);
|
tty_update_window_offset(w);
|
||||||
server_redraw_window(w);
|
server_redraw_window(w);
|
||||||
@@ -348,10 +348,10 @@ recalculate_size_skip_client(struct client *loop, __unused int type,
|
|||||||
* is not the current window - this is used for aggressive-resize.
|
* is not the current window - this is used for aggressive-resize.
|
||||||
* Otherwise skip any session that doesn't contain the window.
|
* Otherwise skip any session that doesn't contain the window.
|
||||||
*/
|
*/
|
||||||
if (loop->session->curw == NULL)
|
if (active_get_effective_window(loop, loop->session) == NULL)
|
||||||
return (1);
|
return (1);
|
||||||
if (current)
|
if (current)
|
||||||
return (loop->session->curw->window != w);
|
return (active_get_effective_window(loop, loop->session) != w);
|
||||||
return (session_has(loop->session, w) == 0);
|
return (session_has(loop->session, w) == 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -295,7 +295,7 @@ static void
|
|||||||
redraw_set_context(struct client *c, struct redraw_build_ctx *bctx)
|
redraw_set_context(struct client *c, struct redraw_build_ctx *bctx)
|
||||||
{
|
{
|
||||||
struct session *s = c->session;
|
struct session *s = c->session;
|
||||||
struct window *w = s->curw->window;
|
struct window *w = active_get_effective_window(c, s);
|
||||||
|
|
||||||
memset(bctx, 0, sizeof *bctx);
|
memset(bctx, 0, sizeof *bctx);
|
||||||
bctx->c = c;
|
bctx->c = c;
|
||||||
@@ -969,7 +969,7 @@ static struct redraw_scene *
|
|||||||
redraw_make_scene(struct client *c)
|
redraw_make_scene(struct client *c)
|
||||||
{
|
{
|
||||||
struct session *s = c->session;
|
struct session *s = c->session;
|
||||||
struct window *w = s->curw->window;
|
struct window *w = active_get_effective_window(c, s);
|
||||||
struct redraw_build_ctx bctx;
|
struct redraw_build_ctx bctx;
|
||||||
struct redraw_scene *scene;
|
struct redraw_scene *scene;
|
||||||
struct redraw_build_cell *bc, *last;
|
struct redraw_build_cell *bc, *last;
|
||||||
@@ -1079,7 +1079,7 @@ static struct redraw_scene *
|
|||||||
redraw_get_scene(struct client *c)
|
redraw_get_scene(struct client *c)
|
||||||
{
|
{
|
||||||
struct redraw_scene *scene = c->redraw_scene;
|
struct redraw_scene *scene = c->redraw_scene;
|
||||||
struct window *w = c->session->curw->window;
|
struct window *w = active_get_effective_window(c, c->session);
|
||||||
const char *reason = NULL;
|
const char *reason = NULL;
|
||||||
u_int ox, oy, sx, sy;
|
u_int ox, oy, sx, sy;
|
||||||
|
|
||||||
@@ -1117,7 +1117,7 @@ redraw_draw_pane_span(struct redraw_draw_ctx *dctx,
|
|||||||
struct tty_style_ctx style_ctx;
|
struct tty_style_ctx style_ctx;
|
||||||
u_int px, py;
|
u_int px, py;
|
||||||
|
|
||||||
tty_default_colours(&defaults, wp, &style_ctx.dim);
|
tty_default_colours(&defaults, wp, dctx->active, &style_ctx.dim);
|
||||||
style_ctx.defaults = &defaults;
|
style_ctx.defaults = &defaults;
|
||||||
style_ctx.palette = &wp->palette;
|
style_ctx.palette = &wp->palette;
|
||||||
style_ctx.hyperlinks = s->hyperlinks;
|
style_ctx.hyperlinks = s->hyperlinks;
|
||||||
@@ -1125,9 +1125,6 @@ redraw_draw_pane_span(struct redraw_draw_ctx *dctx,
|
|||||||
px = span->data.p.px + (x - span->x);
|
px = span->data.p.px + (x - span->x);
|
||||||
py = span->data.p.py;
|
py = span->data.p.py;
|
||||||
tty_draw_line(tty, s, px, py, n, x, y, &style_ctx);
|
tty_draw_line(tty, s, px, py, n, x, y, &style_ctx);
|
||||||
#ifdef ENABLE_IMAGES
|
|
||||||
image_draw_line(tty, s, px, py, n, x, y, &style_ctx);
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Get default border style for spans without a pane. */
|
/* Get default border style for spans without a pane. */
|
||||||
@@ -1143,7 +1140,7 @@ redraw_get_default_border_style(struct redraw_draw_ctx *dctx,
|
|||||||
struct grid_cell *dgc = &dctx->default_gc;
|
struct grid_cell *dgc = &dctx->default_gc;
|
||||||
|
|
||||||
if (~dctx->flags & REDRAW_DEFAULT_SET) {
|
if (~dctx->flags & REDRAW_DEFAULT_SET) {
|
||||||
ft = format_create_defaults(NULL, c, s, s->curw, NULL);
|
ft = format_create_defaults(NULL, c, s, NULL, NULL);
|
||||||
memcpy(dgc, &grid_default_cell, sizeof *dgc);
|
memcpy(dgc, &grid_default_cell, sizeof *dgc);
|
||||||
style_add(dgc, oo, "pane-border-style", ft);
|
style_add(dgc, oo, "pane-border-style", ft);
|
||||||
format_free(ft);
|
format_free(ft);
|
||||||
@@ -1337,7 +1334,7 @@ redraw_draw_scrollbar_span(struct redraw_draw_ctx *dctx,
|
|||||||
memcpy(&slgc, &gc, sizeof slgc);
|
memcpy(&slgc, &gc, sizeof slgc);
|
||||||
slgc.fg = gc.bg;
|
slgc.fg = gc.bg;
|
||||||
slgc.bg = gc.fg;
|
slgc.bg = gc.fg;
|
||||||
tty_default_colours(&pad_gc, wp, NULL);
|
tty_default_colours(&pad_gc, wp, dctx->active, NULL);
|
||||||
|
|
||||||
sb_w = sb_style->width;
|
sb_w = sb_style->width;
|
||||||
sb_pad = sb_style->pad;
|
sb_pad = sb_style->pad;
|
||||||
@@ -1606,14 +1603,16 @@ redraw_set_draw_context(struct redraw_draw_ctx *dctx,
|
|||||||
struct session *s = c->session;
|
struct session *s = c->session;
|
||||||
struct options *oo = s->options;
|
struct options *oo = s->options;
|
||||||
struct tty *tty = &c->tty;
|
struct tty *tty = &c->tty;
|
||||||
|
struct winlink *wl;
|
||||||
u_int lines;
|
u_int lines;
|
||||||
|
|
||||||
memset(dctx, 0, sizeof *dctx);
|
memset(dctx, 0, sizeof *dctx);
|
||||||
dctx->scene = scene;
|
dctx->scene = scene;
|
||||||
|
|
||||||
if (server_is_marked(s, s->curw, marked_pane.wp))
|
wl = active_get_effective_winlink(c, s);
|
||||||
|
if (server_is_marked(s, wl, marked_pane.wp))
|
||||||
dctx->marked = marked_pane.wp;
|
dctx->marked = marked_pane.wp;
|
||||||
dctx->active = s->curw->window->active;
|
dctx->active = active_get_effective_pane(c, wl->window);
|
||||||
|
|
||||||
lines = status_line_size(c);
|
lines = status_line_size(c);
|
||||||
if (options_get_number(oo, "status-position") == 0)
|
if (options_get_number(oo, "status-position") == 0)
|
||||||
@@ -1686,7 +1685,7 @@ redraw_draw(struct client *c, struct window_pane *wp, int flags)
|
|||||||
{
|
{
|
||||||
struct redraw_draw_ctx dctx;
|
struct redraw_draw_ctx dctx;
|
||||||
struct session *s = c->session;
|
struct session *s = c->session;
|
||||||
struct window *w = s->curw->window;
|
struct window *w = active_get_effective_window(c, s);
|
||||||
struct tty *tty = &c->tty;
|
struct tty *tty = &c->tty;
|
||||||
struct screen *sl;
|
struct screen *sl;
|
||||||
struct redraw_scene *scene;
|
struct redraw_scene *scene;
|
||||||
@@ -1820,6 +1819,15 @@ redraw_draw(struct client *c, struct window_pane *wp, int flags)
|
|||||||
tty_reset(tty);
|
tty_reset(tty);
|
||||||
tty_sync_end(tty);
|
tty_sync_end(tty);
|
||||||
|
|
||||||
|
#ifdef ENABLE_SIXEL
|
||||||
|
if (wp != NULL)
|
||||||
|
tty_draw_images(c, wp);
|
||||||
|
else {
|
||||||
|
TAILQ_FOREACH(loop, &scene->w->panes, entry)
|
||||||
|
tty_draw_images(c, loop);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
log_debug("%s: finished @%u redraw", c->name, scene->w->id);
|
log_debug("%s: finished @%u redraw", c->name, scene->w->id);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1877,7 +1885,7 @@ redraw_screen(struct client *c)
|
|||||||
flags |= REDRAW_OVERLAY;
|
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 (active_get_effective_window(c, c->session)->menu != NULL)
|
||||||
flags |= REDRAW_MENU;
|
flags |= REDRAW_MENU;
|
||||||
if (flags != 0)
|
if (flags != 0)
|
||||||
redraw_draw(c, NULL, flags);
|
redraw_draw(c, NULL, flags);
|
||||||
@@ -1889,7 +1897,7 @@ void
|
|||||||
redraw_pane(struct client *c, struct window_pane *wp)
|
redraw_pane(struct client *c, struct window_pane *wp)
|
||||||
{
|
{
|
||||||
redraw_draw(c, wp, REDRAW_PANE|REDRAW_PANE_SCROLLBAR);
|
redraw_draw(c, wp, REDRAW_PANE|REDRAW_PANE_SCROLLBAR);
|
||||||
if (c->session->curw->window->menu != NULL)
|
if (active_get_effective_window(c, c->session)->menu != NULL)
|
||||||
redraw_draw(c, NULL, REDRAW_MENU);
|
redraw_draw(c, NULL, REDRAW_MENU);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
332
screen-write.c
332
screen-write.c
@@ -1,4 +1,4 @@
|
|||||||
/* $OpenBSD: screen-write.c,v 1.285 2026/07/26 09:02:08 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>
|
||||||
@@ -134,7 +134,7 @@ screen_write_redraw_cb(const struct tty_ctx *ttyctx)
|
|||||||
static int
|
static int
|
||||||
screen_write_set_client_cb(struct tty_ctx *ttyctx, struct client *c)
|
screen_write_set_client_cb(struct tty_ctx *ttyctx, struct client *c)
|
||||||
{
|
{
|
||||||
struct window_pane *wp = ttyctx->arg;
|
struct window_pane *wp = ttyctx->arg, *active;
|
||||||
|
|
||||||
if (ttyctx->flags & TTY_CTX_INVISIBLE_PANES) {
|
if (ttyctx->flags & TTY_CTX_INVISIBLE_PANES) {
|
||||||
if (session_has(c->session, wp->window))
|
if (session_has(c->session, wp->window))
|
||||||
@@ -142,7 +142,7 @@ screen_write_set_client_cb(struct tty_ctx *ttyctx, struct client *c)
|
|||||||
return (0);
|
return (0);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (c->session->curw->window != wp->window)
|
if (active_get_effective_window(c, c->session) != wp->window)
|
||||||
return (0);
|
return (0);
|
||||||
if (wp->layout_cell == NULL)
|
if (wp->layout_cell == NULL)
|
||||||
return (0);
|
return (0);
|
||||||
@@ -160,11 +160,14 @@ screen_write_set_client_cb(struct tty_ctx *ttyctx, struct client *c)
|
|||||||
return (-1);
|
return (-1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
active = active_get_effective_pane(c, wp->window);
|
||||||
if (tty_window_offset(&c->tty, &ttyctx->wox, &ttyctx->woy, &ttyctx->wsx,
|
if (tty_window_offset(&c->tty, &ttyctx->wox, &ttyctx->woy, &ttyctx->wsx,
|
||||||
&ttyctx->wsy))
|
&ttyctx->wsy))
|
||||||
ttyctx->flags |= TTY_CTX_WINDOW_BIGGER;
|
ttyctx->flags |= TTY_CTX_WINDOW_BIGGER;
|
||||||
else
|
else
|
||||||
ttyctx->flags &= ~TTY_CTX_WINDOW_BIGGER;
|
ttyctx->flags &= ~TTY_CTX_WINDOW_BIGGER;
|
||||||
|
tty_default_colours(&ttyctx->defaults, wp, active,
|
||||||
|
&ttyctx->style_ctx.dim);
|
||||||
|
|
||||||
ttyctx->xoff = ttyctx->rxoff = wp->xoff;
|
ttyctx->xoff = ttyctx->rxoff = wp->xoff;
|
||||||
ttyctx->yoff = ttyctx->ryoff = wp->yoff;
|
ttyctx->yoff = ttyctx->ryoff = wp->yoff;
|
||||||
@@ -296,7 +299,7 @@ screen_write_initctx(struct screen_write_ctx *ctx, struct tty_ctx *ttyctx,
|
|||||||
ttyctx->redraw_cb = screen_write_redraw_cb;
|
ttyctx->redraw_cb = screen_write_redraw_cb;
|
||||||
if (ctx->wp != NULL) {
|
if (ctx->wp != NULL) {
|
||||||
tty_default_colours(&ttyctx->defaults, ctx->wp,
|
tty_default_colours(&ttyctx->defaults, ctx->wp,
|
||||||
&ttyctx->style_ctx.dim);
|
ctx->wp->window->active, &ttyctx->style_ctx.dim);
|
||||||
ttyctx->style_ctx.palette = &ctx->wp->palette;
|
ttyctx->style_ctx.palette = &ctx->wp->palette;
|
||||||
ttyctx->set_client_cb = screen_write_set_client_cb;
|
ttyctx->set_client_cb = screen_write_set_client_cb;
|
||||||
ttyctx->arg = ctx->wp;
|
ttyctx->arg = ctx->wp;
|
||||||
@@ -305,12 +308,14 @@ 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 or for an overlay (no pane), we want to
|
* For the active pane showing its base screen or for an
|
||||||
* only use synchronized updates if requested (commands that
|
* overlay (no pane), only use synchronized updates if
|
||||||
* move the cursor); for other panes, always use it, since the
|
* requested (commands that move the cursor); for other panes
|
||||||
* 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))
|
||||||
ttyctx->flags |= TTY_CTX_SYNC;
|
ttyctx->flags |= TTY_CTX_SYNC;
|
||||||
else {
|
else {
|
||||||
if (ctx->wp == NULL)
|
if (ctx->wp == NULL)
|
||||||
@@ -338,10 +343,18 @@ screen_write_make_list(struct screen *s)
|
|||||||
void
|
void
|
||||||
screen_write_free_list(struct screen *s)
|
screen_write_free_list(struct screen *s)
|
||||||
{
|
{
|
||||||
|
struct screen_write_cline *cl;
|
||||||
|
struct screen_write_citem *ci, *ci1;
|
||||||
u_int y;
|
u_int y;
|
||||||
|
|
||||||
for (y = 0; y < screen_size_y(s); y++)
|
for (y = 0; y < screen_size_y(s); y++) {
|
||||||
free(s->write_list[y].data);
|
cl = &s->write_list[y];
|
||||||
|
TAILQ_FOREACH_SAFE(ci, &cl->items, entry, ci1) {
|
||||||
|
TAILQ_REMOVE(&cl->items, ci, entry);
|
||||||
|
screen_write_free_citem(ci);
|
||||||
|
}
|
||||||
|
free(cl->data);
|
||||||
|
}
|
||||||
free(s->write_list);
|
free(s->write_list);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1059,6 +1072,19 @@ screen_write_stop_sync(struct window_pane *wp)
|
|||||||
log_debug("%s: %%%u stopped sync mode", __func__, wp->id);
|
log_debug("%s: %%%u stopped sync mode", __func__, wp->id);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Flush pending output before clearing sync mode. */
|
||||||
|
void
|
||||||
|
screen_write_end_sync(struct screen_write_ctx *ctx)
|
||||||
|
{
|
||||||
|
struct window_pane *wp = ctx->wp;
|
||||||
|
|
||||||
|
if (wp == NULL)
|
||||||
|
return;
|
||||||
|
if (wp->base.mode & MODE_SYNC)
|
||||||
|
screen_write_collect_flush(ctx, 0, __func__);
|
||||||
|
screen_write_stop_sync(wp);
|
||||||
|
}
|
||||||
|
|
||||||
/* Cursor up by ny. */
|
/* Cursor up by ny. */
|
||||||
void
|
void
|
||||||
screen_write_cursorup(struct screen_write_ctx *ctx, u_int ny)
|
screen_write_cursorup(struct screen_write_ctx *ctx, u_int ny)
|
||||||
@@ -1308,8 +1334,9 @@ screen_write_alignmenttest(struct screen_write_ctx *ctx)
|
|||||||
memcpy(&gc, &grid_default_cell, sizeof gc);
|
memcpy(&gc, &grid_default_cell, sizeof gc);
|
||||||
utf8_set(&gc.data, 'E');
|
utf8_set(&gc.data, 'E');
|
||||||
|
|
||||||
#ifdef ENABLE_IMAGES
|
#ifdef ENABLE_SIXEL
|
||||||
image_redraw_all(ctx);
|
if (image_free_all(s) && ctx->wp != NULL)
|
||||||
|
ctx->wp->flags |= PANE_REDRAW;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
for (yy = 0; yy < screen_size_y(s); yy++) {
|
for (yy = 0; yy < screen_size_y(s); yy++) {
|
||||||
@@ -1354,8 +1381,9 @@ screen_write_insertcharacter(struct screen_write_ctx *ctx, u_int nx, u_int bg)
|
|||||||
if (s->cx > screen_size_x(s) - 1)
|
if (s->cx > screen_size_x(s) - 1)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
#ifdef ENABLE_IMAGES
|
#ifdef ENABLE_SIXEL
|
||||||
image_redraw_area(ctx, 0, s->cy, screen_size_x(s), 1);
|
if (image_check_line(s, s->cy, 1) && ctx->wp != NULL)
|
||||||
|
ctx->wp->flags |= PANE_REDRAW;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
screen_write_initctx(ctx, &ttyctx, 0, 1);
|
screen_write_initctx(ctx, &ttyctx, 0, 1);
|
||||||
@@ -1394,8 +1422,9 @@ screen_write_deletecharacter(struct screen_write_ctx *ctx, u_int nx, u_int bg)
|
|||||||
if (s->cx > screen_size_x(s) - 1)
|
if (s->cx > screen_size_x(s) - 1)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
#ifdef ENABLE_IMAGES
|
#ifdef ENABLE_SIXEL
|
||||||
image_redraw_area(ctx, 0, s->cy, screen_size_x(s), 1);
|
if (image_check_line(s, s->cy, 1) && ctx->wp != NULL)
|
||||||
|
ctx->wp->flags |= PANE_REDRAW;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
screen_write_initctx(ctx, &ttyctx, 0, 1);
|
screen_write_initctx(ctx, &ttyctx, 0, 1);
|
||||||
@@ -1434,8 +1463,9 @@ screen_write_clearcharacter(struct screen_write_ctx *ctx, u_int nx, u_int bg)
|
|||||||
if (s->cx > screen_size_x(s) - 1)
|
if (s->cx > screen_size_x(s) - 1)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
#ifdef ENABLE_IMAGES
|
#ifdef ENABLE_SIXEL
|
||||||
image_redraw_area(ctx, 0, s->cy, screen_size_x(s), 1);
|
if (image_check_line(s, s->cy, 1) && ctx->wp != NULL)
|
||||||
|
ctx->wp->flags |= PANE_REDRAW;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
screen_write_initctx(ctx, &ttyctx, 0, 1);
|
screen_write_initctx(ctx, &ttyctx, 0, 1);
|
||||||
@@ -1468,8 +1498,9 @@ screen_write_insertline(struct screen_write_ctx *ctx, u_int ny, u_int bg)
|
|||||||
if (ny == 0)
|
if (ny == 0)
|
||||||
ny = 1;
|
ny = 1;
|
||||||
|
|
||||||
#ifdef ENABLE_IMAGES
|
#ifdef ENABLE_SIXEL
|
||||||
image_redraw_area(ctx, 0, s->cy, screen_size_x(s), sy - s->cy);
|
if (image_check_line(s, s->cy, sy - s->cy) && ctx->wp != NULL)
|
||||||
|
ctx->wp->flags |= PANE_REDRAW;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
if (s->cy < s->rupper || s->cy > s->rlower) {
|
if (s->cy < s->rupper || s->cy > s->rlower) {
|
||||||
@@ -1535,8 +1566,9 @@ screen_write_deleteline(struct screen_write_ctx *ctx, u_int ny, u_int bg)
|
|||||||
if (ny == 0)
|
if (ny == 0)
|
||||||
ny = 1;
|
ny = 1;
|
||||||
|
|
||||||
#ifdef ENABLE_IMAGES
|
#ifdef ENABLE_SIXEL
|
||||||
image_redraw_area(ctx, 0, s->cy, screen_size_x(s), sy - s->cy);
|
if (image_check_line(s, s->cy, sy - s->cy) && ctx->wp != NULL)
|
||||||
|
ctx->wp->flags |= PANE_REDRAW;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
if (s->cy < s->rupper || s->cy > s->rlower) {
|
if (s->cy < s->rupper || s->cy > s->rlower) {
|
||||||
@@ -1607,8 +1639,9 @@ screen_write_clearline(struct screen_write_ctx *ctx, u_int bg)
|
|||||||
if (gl->cellsize == 0 && COLOUR_DEFAULT(bg))
|
if (gl->cellsize == 0 && COLOUR_DEFAULT(bg))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
#ifdef ENABLE_IMAGES
|
#ifdef ENABLE_SIXEL
|
||||||
image_redraw_area(ctx, 0, s->cy, screen_size_x(s), 1);
|
if (image_check_line(s, s->cy, 1) && ctx->wp != NULL)
|
||||||
|
ctx->wp->flags |= PANE_REDRAW;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
flags = gl->flags & GRID_LINE_OSC133_FLAGS;
|
flags = gl->flags & GRID_LINE_OSC133_FLAGS;
|
||||||
@@ -1645,8 +1678,9 @@ screen_write_clearendofline(struct screen_write_ctx *ctx, u_int bg)
|
|||||||
if (s->cx > sx - 1 || (s->cx >= gl->cellsize && COLOUR_DEFAULT(bg)))
|
if (s->cx > sx - 1 || (s->cx >= gl->cellsize && COLOUR_DEFAULT(bg)))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
#ifdef ENABLE_IMAGES
|
#ifdef ENABLE_SIXEL
|
||||||
image_redraw_area(ctx, 0, s->cy, screen_size_x(s), 1);
|
if (image_check_line(s, s->cy, 1) && ctx->wp != NULL)
|
||||||
|
ctx->wp->flags |= PANE_REDRAW;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
grid_view_clear(s->grid, s->cx, s->cy, sx - s->cx, 1, bg);
|
grid_view_clear(s->grid, s->cx, s->cy, sx - s->cx, 1, bg);
|
||||||
@@ -1671,8 +1705,9 @@ screen_write_clearstartofline(struct screen_write_ctx *ctx, u_int bg)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef ENABLE_IMAGES
|
#ifdef ENABLE_SIXEL
|
||||||
image_redraw_area(ctx, 0, s->cy, screen_size_x(s), 1);
|
if (image_check_line(s, s->cy, 1) && ctx->wp != NULL)
|
||||||
|
ctx->wp->flags |= PANE_REDRAW;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
if (s->cx > sx - 1)
|
if (s->cx > sx - 1)
|
||||||
@@ -1724,8 +1759,9 @@ screen_write_reverseindex(struct screen_write_ctx *ctx, u_int bg)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef ENABLE_IMAGES
|
#ifdef ENABLE_SIXEL
|
||||||
image_redraw_all(ctx);
|
if (image_free_all(s) && ctx->wp != NULL)
|
||||||
|
ctx->wp->flags |= PANE_REDRAW;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
grid_view_scroll_region_down(s->grid, s->rupper, s->rlower, bg);
|
grid_view_scroll_region_down(s->grid, s->rupper, s->rlower, bg);
|
||||||
@@ -1775,6 +1811,9 @@ screen_write_linefeed(struct screen_write_ctx *ctx, int wrapped, u_int bg)
|
|||||||
struct screen *s = ctx->s;
|
struct screen *s = ctx->s;
|
||||||
struct grid *gd = s->grid;
|
struct grid *gd = s->grid;
|
||||||
struct grid_line *gl;
|
struct grid_line *gl;
|
||||||
|
#ifdef ENABLE_SIXEL
|
||||||
|
int redraw = 0;
|
||||||
|
#endif
|
||||||
u_int rupper = s->rupper, rlower = s->rlower;
|
u_int rupper = s->rupper, rlower = s->rlower;
|
||||||
|
|
||||||
gl = grid_get_line(gd, gd->hsize + s->cy);
|
gl = grid_get_line(gd, gd->hsize + s->cy);
|
||||||
@@ -1795,12 +1834,13 @@ screen_write_linefeed(struct screen_write_ctx *ctx, int wrapped, u_int bg)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef ENABLE_IMAGES
|
#ifdef ENABLE_SIXEL
|
||||||
if (rlower == screen_size_y(s) - 1)
|
if (rlower == screen_size_y(s) - 1)
|
||||||
image_redraw_scroll(ctx, 1);
|
redraw = image_scroll_up(s, 1);
|
||||||
else
|
else
|
||||||
image_redraw_area(ctx, 0, rupper, screen_size_x(s),
|
redraw = image_check_line(s, rupper, rlower - rupper);
|
||||||
rlower - rupper);
|
if (redraw && ctx->wp != NULL)
|
||||||
|
ctx->wp->flags |= PANE_REDRAW;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
grid_view_scroll_region_up(gd, s->rupper, s->rlower, bg);
|
grid_view_scroll_region_up(gd, s->rupper, s->rlower, bg);
|
||||||
@@ -1826,8 +1866,9 @@ screen_write_scrollup(struct screen_write_ctx *ctx, u_int lines, u_int bg)
|
|||||||
ctx->bg = bg;
|
ctx->bg = bg;
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef ENABLE_IMAGES
|
#ifdef ENABLE_SIXEL
|
||||||
image_redraw_scroll(ctx, lines);
|
if (image_scroll_up(s, lines) && ctx->wp != NULL)
|
||||||
|
ctx->wp->flags |= PANE_REDRAW;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
for (i = 0; i < lines; i++) {
|
for (i = 0; i < lines; i++) {
|
||||||
@@ -1854,8 +1895,9 @@ screen_write_scrolldown(struct screen_write_ctx *ctx, u_int lines, u_int bg)
|
|||||||
else if (lines > s->rlower - s->rupper + 1)
|
else if (lines > s->rlower - s->rupper + 1)
|
||||||
lines = s->rlower - s->rupper + 1;
|
lines = s->rlower - s->rupper + 1;
|
||||||
|
|
||||||
#ifdef ENABLE_IMAGES
|
#ifdef ENABLE_SIXEL
|
||||||
image_redraw_all(ctx);
|
if (image_free_all(s) && ctx->wp != NULL)
|
||||||
|
ctx->wp->flags |= PANE_REDRAW;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
for (i = 0; i < lines; i++)
|
for (i = 0; i < lines; i++)
|
||||||
@@ -1894,8 +1936,9 @@ screen_write_clearendofscreen(struct screen_write_ctx *ctx, u_int bg)
|
|||||||
struct visible_ranges *r;
|
struct visible_ranges *r;
|
||||||
struct visible_range *ri;
|
struct visible_range *ri;
|
||||||
|
|
||||||
#ifdef ENABLE_IMAGES
|
#ifdef ENABLE_SIXEL
|
||||||
image_redraw_area(ctx, 0, s->cy, screen_size_x(s), sy - s->cy);
|
if (image_check_line(s, s->cy, sy - s->cy) && ctx->wp != NULL)
|
||||||
|
ctx->wp->flags |= PANE_REDRAW;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
screen_write_initctx(ctx, &ttyctx, 1, 1);
|
screen_write_initctx(ctx, &ttyctx, 1, 1);
|
||||||
@@ -1974,8 +2017,9 @@ screen_write_clearstartofscreen(struct screen_write_ctx *ctx, u_int bg)
|
|||||||
struct visible_ranges *r;
|
struct visible_ranges *r;
|
||||||
struct visible_range *ri;
|
struct visible_range *ri;
|
||||||
|
|
||||||
#ifdef ENABLE_IMAGES
|
#ifdef ENABLE_SIXEL
|
||||||
image_redraw_area(ctx, 0, 0, screen_size_x(s), s->cy + 1);
|
if (image_check_line(s, 0, s->cy - 1) && ctx->wp != NULL)
|
||||||
|
ctx->wp->flags |= PANE_REDRAW;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
screen_write_initctx(ctx, &ttyctx, 1, 1);
|
screen_write_initctx(ctx, &ttyctx, 1, 1);
|
||||||
@@ -2046,8 +2090,9 @@ screen_write_clearscreen(struct screen_write_ctx *ctx, u_int bg)
|
|||||||
struct visible_ranges *r;
|
struct visible_ranges *r;
|
||||||
struct visible_range *ri;
|
struct visible_range *ri;
|
||||||
|
|
||||||
#ifdef ENABLE_IMAGES
|
#ifdef ENABLE_SIXEL
|
||||||
image_redraw_all(ctx);
|
if (image_free_all(s) && ctx->wp != NULL)
|
||||||
|
ctx->wp->flags |= PANE_REDRAW;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
screen_write_initctx(ctx, &ttyctx, 1, 1);
|
screen_write_initctx(ctx, &ttyctx, 1, 1);
|
||||||
@@ -2461,15 +2506,62 @@ screen_write_collect_insert_clear(struct screen_write_ctx *ctx, u_int px,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Clear a cell that is being broken up by a write over part of it, keeping the
|
||||||
|
* background so the columns it covered do not lose their colour.
|
||||||
|
*/
|
||||||
|
static void
|
||||||
|
screen_write_clear_cell(struct grid *gd, u_int px, u_int py)
|
||||||
|
{
|
||||||
|
struct grid_cell gc;
|
||||||
|
int bg;
|
||||||
|
|
||||||
|
grid_view_get_cell(gd, px, py, &gc);
|
||||||
|
bg = gc.bg;
|
||||||
|
|
||||||
|
memcpy(&gc, &grid_default_cell, sizeof gc);
|
||||||
|
gc.bg = bg;
|
||||||
|
grid_view_set_cell(gd, px, py, &gc);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Insert clears for a range of cells already cleared in the grid. Adjacent
|
||||||
|
* cells may have come from different characters and so have different
|
||||||
|
* backgrounds, so this is done in runs of the same colour.
|
||||||
|
*/
|
||||||
|
static void
|
||||||
|
screen_write_insert_clears(struct screen_write_ctx *ctx, u_int px, u_int nx)
|
||||||
|
{
|
||||||
|
struct screen *s = ctx->s;
|
||||||
|
struct grid_cell gc;
|
||||||
|
u_int xx, start = px, n;
|
||||||
|
int bg = 8;
|
||||||
|
|
||||||
|
for (xx = px; xx < px + nx; xx++) {
|
||||||
|
grid_view_get_cell(s->grid, xx, s->cy, &gc);
|
||||||
|
if (xx == start)
|
||||||
|
bg = gc.bg;
|
||||||
|
else if (gc.bg != bg) {
|
||||||
|
n = xx - start;
|
||||||
|
log_debug("%s: from %u, size %u", __func__, start, n);
|
||||||
|
screen_write_collect_insert_clear(ctx, start, n, bg);
|
||||||
|
start = xx;
|
||||||
|
bg = gc.bg;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
log_debug("%s: from %u, size %u", __func__, start, xx - start);
|
||||||
|
screen_write_collect_insert_clear(ctx, start, xx - start, bg);
|
||||||
|
}
|
||||||
|
|
||||||
/* Finish and store collected cells. */
|
/* Finish and store collected cells. */
|
||||||
void
|
void
|
||||||
screen_write_collect_end(struct screen_write_ctx *ctx)
|
screen_write_collect_end(struct screen_write_ctx *ctx)
|
||||||
{
|
{
|
||||||
struct screen *s = ctx->s;
|
struct screen *s = ctx->s;
|
||||||
struct screen_write_citem *ci = ctx->item, *bci = NULL, *aci;
|
struct screen_write_citem *ci = ctx->item;
|
||||||
struct screen_write_cline *cl = &s->write_list[s->cy];
|
struct screen_write_cline *cl = &s->write_list[s->cy];
|
||||||
struct grid_cell gc;
|
struct grid_cell gc;
|
||||||
u_int xx;
|
u_int xx, bx = 0, bnx = 0;
|
||||||
|
|
||||||
if (ci->used == 0)
|
if (ci->used == 0)
|
||||||
return;
|
return;
|
||||||
@@ -2485,8 +2577,7 @@ screen_write_collect_end(struct screen_write_ctx *ctx)
|
|||||||
grid_view_get_cell(s->grid, xx, s->cy, &gc);
|
grid_view_get_cell(s->grid, xx, s->cy, &gc);
|
||||||
if (~gc.flags & GRID_FLAG_PADDING)
|
if (~gc.flags & GRID_FLAG_PADDING)
|
||||||
break;
|
break;
|
||||||
grid_view_set_cell(s->grid, xx, s->cy,
|
screen_write_clear_cell(s->grid, xx, s->cy);
|
||||||
&grid_default_cell);
|
|
||||||
log_debug("%s: padding erased (before) at %u (cx %u)",
|
log_debug("%s: padding erased (before) at %u (cx %u)",
|
||||||
__func__, xx, s->cx);
|
__func__, xx, s->cx);
|
||||||
}
|
}
|
||||||
@@ -2495,51 +2586,36 @@ screen_write_collect_end(struct screen_write_ctx *ctx)
|
|||||||
grid_view_get_cell(s->grid, 0, s->cy, &gc);
|
grid_view_get_cell(s->grid, 0, s->cy, &gc);
|
||||||
if (gc.data.width > 1 ||
|
if (gc.data.width > 1 ||
|
||||||
(gc.flags & GRID_FLAG_PADDING)) {
|
(gc.flags & GRID_FLAG_PADDING)) {
|
||||||
grid_view_set_cell(s->grid, xx, s->cy,
|
screen_write_clear_cell(s->grid, xx, s->cy);
|
||||||
&grid_default_cell);
|
|
||||||
log_debug("%s: padding erased (before) at %u "
|
log_debug("%s: padding erased (before) at %u "
|
||||||
"(cx %u)", __func__, xx, s->cx);
|
"(cx %u)", __func__, xx, s->cx);
|
||||||
}
|
}
|
||||||
}
|
bx = xx;
|
||||||
if (xx != s->cx) {
|
bnx = s->cx - xx;
|
||||||
bci = ctx->item;
|
|
||||||
bci->type = CLEAR;
|
|
||||||
bci->x = xx;
|
|
||||||
bci->bg = 8;
|
|
||||||
bci->used = s->cx - xx;
|
|
||||||
log_debug("%s: padding erased (before): from %u, "
|
|
||||||
"size %u", __func__, bci->x, bci->used);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef ENABLE_IMAGES
|
#ifdef ENABLE_SIXEL
|
||||||
image_redraw_area(ctx, s->cx, s->cy, ci->used, 1);
|
if (image_check_area(s, s->cx, s->cy, ci->used, 1) && ctx->wp != NULL)
|
||||||
|
ctx->wp->flags |= PANE_REDRAW;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
grid_view_set_cells(s->grid, s->cx, s->cy, &ci->gc, cl->data + ci->x,
|
grid_view_set_cells(s->grid, s->cx, s->cy, &ci->gc, cl->data + ci->x,
|
||||||
ci->used);
|
ci->used);
|
||||||
if (bci != NULL)
|
if (bnx != 0)
|
||||||
screen_write_collect_insert(ctx, bci);
|
screen_write_insert_clears(ctx, bx, bnx);
|
||||||
screen_write_set_cursor(ctx, s->cx + ci->used, -1);
|
screen_write_set_cursor(ctx, s->cx + ci->used, -1);
|
||||||
|
|
||||||
for (xx = s->cx; xx < screen_size_x(s); xx++) {
|
for (xx = s->cx; xx < screen_size_x(s); xx++) {
|
||||||
grid_view_get_cell(s->grid, xx, s->cy, &gc);
|
grid_view_get_cell(s->grid, xx, s->cy, &gc);
|
||||||
if (~gc.flags & GRID_FLAG_PADDING)
|
if (~gc.flags & GRID_FLAG_PADDING)
|
||||||
break;
|
break;
|
||||||
grid_view_set_cell(s->grid, xx, s->cy, &grid_default_cell);
|
screen_write_clear_cell(s->grid, xx, s->cy);
|
||||||
log_debug("%s: padding erased (after) at %u (cx %u)",
|
log_debug("%s: padding erased (after) at %u (cx %u)", __func__,
|
||||||
__func__, xx, s->cx);
|
xx, s->cx);
|
||||||
}
|
|
||||||
if (xx != s->cx) {
|
|
||||||
aci = ctx->item;
|
|
||||||
aci->type = CLEAR;
|
|
||||||
aci->x = s->cx;
|
|
||||||
aci->bg = 8;
|
|
||||||
aci->used = xx - s->cx;
|
|
||||||
log_debug("%s: padding erased (after): from %u, size %u",
|
|
||||||
__func__, aci->x, aci->used);
|
|
||||||
screen_write_collect_insert(ctx, aci);
|
|
||||||
}
|
}
|
||||||
|
if (xx != s->cx)
|
||||||
|
screen_write_insert_clears(ctx, s->cx, xx - s->cx);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Write cell data, collecting if necessary. */
|
/* Write cell data, collecting if necessary. */
|
||||||
@@ -2607,9 +2683,6 @@ screen_write_cell(struct screen_write_ctx *ctx, const struct grid_cell *gc)
|
|||||||
struct grid_line *gl;
|
struct grid_line *gl;
|
||||||
struct grid_cell_entry *gce;
|
struct grid_cell_entry *gce;
|
||||||
struct grid_cell tmp_gc, now_gc;
|
struct grid_cell tmp_gc, now_gc;
|
||||||
#ifdef ENABLE_IMAGES
|
|
||||||
struct grid_cell image_gc;
|
|
||||||
#endif
|
|
||||||
struct tty_ctx ttyctx;
|
struct tty_ctx ttyctx;
|
||||||
u_int sx = screen_size_x(s), sy = screen_size_y(s);
|
u_int sx = screen_size_x(s), sy = screen_size_y(s);
|
||||||
u_int width = ud->width, xx, not_wrap, i, n, vis;
|
u_int width = ud->width, xx, not_wrap, i, n, vis;
|
||||||
@@ -2654,20 +2727,6 @@ screen_write_cell(struct screen_write_ctx *ctx, const struct grid_cell *gc)
|
|||||||
return;
|
return;
|
||||||
screen_write_initctx(ctx, &ttyctx, 0, 0);
|
screen_write_initctx(ctx, &ttyctx, 0, 0);
|
||||||
|
|
||||||
#ifdef ENABLE_IMAGES
|
|
||||||
/* Update the text underlay without removing an image placement. */
|
|
||||||
grid_view_get_cell(gd, s->cx, s->cy, &now_gc);
|
|
||||||
if ((now_gc.flags & GRID_FLAG_IMAGE) &&
|
|
||||||
(~gc->flags & GRID_FLAG_IMAGE)) {
|
|
||||||
image_redraw_area(ctx, s->cx, s->cy, width, 1);
|
|
||||||
memcpy(&image_gc, gc, sizeof image_gc);
|
|
||||||
image_gc.flags |= GRID_FLAG_IMAGE|GRID_FLAG_IMAGE_DAMAGED;
|
|
||||||
image_gc.image_id = now_gc.image_id;
|
|
||||||
image_gc.image_x = now_gc.image_x;
|
|
||||||
image_gc.image_y = now_gc.image_y;
|
|
||||||
gc = &image_gc;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
/* Handle overwriting of UTF-8 characters. */
|
/* Handle overwriting of UTF-8 characters. */
|
||||||
gl = grid_get_line(s->grid, s->grid->hsize + s->cy);
|
gl = grid_get_line(s->grid, s->grid->hsize + s->cy);
|
||||||
if (gl->flags & GRID_LINE_EXTENDED) {
|
if (gl->flags & GRID_LINE_EXTENDED) {
|
||||||
@@ -2684,7 +2743,7 @@ screen_write_cell(struct screen_write_ctx *ctx, const struct grid_cell *gc)
|
|||||||
*/
|
*/
|
||||||
for (xx = s->cx + 1; xx < s->cx + width; xx++) {
|
for (xx = s->cx + 1; xx < s->cx + width; xx++) {
|
||||||
log_debug("%s: new padding at %u,%u", __func__, xx, s->cy);
|
log_debug("%s: new padding at %u,%u", __func__, xx, s->cy);
|
||||||
grid_view_set_padding(gd, xx, s->cy);
|
grid_view_set_padding(gd, xx, s->cy, gc->bg);
|
||||||
skip = 0;
|
skip = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2806,10 +2865,11 @@ screen_write_combine(struct screen_write_ctx *ctx, const struct grid_cell *gc)
|
|||||||
struct grid *gd = s->grid;
|
struct grid *gd = s->grid;
|
||||||
const struct utf8_data *ud = &gc->data;
|
const struct utf8_data *ud = &gc->data;
|
||||||
struct options *oo = global_options;
|
struct options *oo = global_options;
|
||||||
u_int i, n, cx = s->cx, cy = s->cy, vis, yoff = 0;
|
u_int i, n, cx = s->cx, cy = s->cy, vis;
|
||||||
struct grid_cell last;
|
struct grid_cell last;
|
||||||
struct tty_ctx ttyctx;
|
struct tty_ctx ttyctx;
|
||||||
int force_wide = 0, zero_width = 0;
|
int force_wide = 0, zero_width = 0;
|
||||||
|
int xoff = 0, yoff = 0;
|
||||||
struct visible_ranges *r;
|
struct visible_ranges *r;
|
||||||
|
|
||||||
/* Ignore U+3164 HANGUL_FILLER entirely. */
|
/* Ignore U+3164 HANGUL_FILLER entirely. */
|
||||||
@@ -2896,16 +2956,18 @@ screen_write_combine(struct screen_write_ctx *ctx, const struct grid_cell *gc)
|
|||||||
/* Set the new cell. */
|
/* Set the new cell. */
|
||||||
grid_view_set_cell(gd, cx - n, cy, &last);
|
grid_view_set_cell(gd, cx - n, cy, &last);
|
||||||
if (force_wide)
|
if (force_wide)
|
||||||
grid_view_set_padding(gd, cx - 1, cy);
|
grid_view_set_padding(gd, cx - 1, cy, last.bg);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Check if all of this character is visible. No character will be
|
* Check if all of this character is visible. No character will be
|
||||||
* obscured in the middle, only on left or right, but there could be an
|
* obscured in the middle, only on left or right, but there could be an
|
||||||
* empty range in the visible ranges so we add them all up.
|
* empty range in the visible ranges so we add them all up.
|
||||||
*/
|
*/
|
||||||
if (wp != NULL)
|
if (wp != NULL) {
|
||||||
|
xoff = wp->xoff;
|
||||||
yoff = wp->yoff;
|
yoff = wp->yoff;
|
||||||
r = window_visible_ranges(wp, cx - n, cy + yoff, n, NULL);
|
}
|
||||||
|
r = window_visible_ranges(wp, xoff + cx - n, cy + yoff, n, NULL);
|
||||||
for (i = 0, vis = 0; i < r->used; i++)
|
for (i = 0, vis = 0; i < r->used; i++)
|
||||||
vis += r->ranges[i].nx;
|
vis += r->ranges[i].nx;
|
||||||
if (vis < n) {
|
if (vis < n) {
|
||||||
@@ -2965,12 +3027,12 @@ screen_write_overwrite(struct screen_write_ctx *ctx, struct grid_cell *gc,
|
|||||||
if (~tmp_gc.flags & GRID_FLAG_PADDING)
|
if (~tmp_gc.flags & GRID_FLAG_PADDING)
|
||||||
break;
|
break;
|
||||||
log_debug("%s: padding at %u,%u", __func__, xx, s->cy);
|
log_debug("%s: padding at %u,%u", __func__, xx, s->cy);
|
||||||
grid_view_set_cell(gd, xx, s->cy, &grid_default_cell);
|
screen_write_clear_cell(gd, xx, s->cy);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Overwrite the character at the start of this padding. */
|
/* Overwrite the character at the start of this padding. */
|
||||||
log_debug("%s: character at %u,%u", __func__, xx, s->cy);
|
log_debug("%s: character at %u,%u", __func__, xx, s->cy);
|
||||||
grid_view_set_cell(gd, xx, s->cy, &grid_default_cell);
|
screen_write_clear_cell(gd, xx, s->cy);
|
||||||
done = 1;
|
done = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2988,17 +3050,7 @@ screen_write_overwrite(struct screen_write_ctx *ctx, struct grid_cell *gc,
|
|||||||
break;
|
break;
|
||||||
log_debug("%s: overwrite at %u,%u", __func__, xx,
|
log_debug("%s: overwrite at %u,%u", __func__, xx,
|
||||||
s->cy);
|
s->cy);
|
||||||
if (gc->flags & GRID_FLAG_TAB) {
|
screen_write_clear_cell(gd, xx, s->cy);
|
||||||
memcpy(&tmp_gc, gc, sizeof tmp_gc);
|
|
||||||
memset(tmp_gc.data.data, 0,
|
|
||||||
sizeof tmp_gc.data.data);
|
|
||||||
*tmp_gc.data.data = ' ';
|
|
||||||
tmp_gc.data.width = tmp_gc.data.size =
|
|
||||||
tmp_gc.data.have = 1;
|
|
||||||
grid_view_set_cell(gd, xx, s->cy, &tmp_gc);
|
|
||||||
} else
|
|
||||||
grid_view_set_cell(gd, xx, s->cy,
|
|
||||||
&grid_default_cell);
|
|
||||||
done = 1;
|
done = 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -3037,21 +3089,63 @@ screen_write_rawstring(struct screen_write_ctx *ctx, u_char *str, u_int len,
|
|||||||
tty_write(tty_cmd_rawstring, &ttyctx);
|
tty_write(tty_cmd_rawstring, &ttyctx);
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef ENABLE_IMAGES
|
#ifdef ENABLE_SIXEL
|
||||||
/* Write a SIXEL image. */
|
/* Write a SIXEL image. */
|
||||||
void
|
void
|
||||||
screen_write_sixelimage(struct screen_write_ctx *ctx, struct sixel_image *si,
|
screen_write_sixelimage(struct screen_write_ctx *ctx, struct sixel_image *si,
|
||||||
u_int bg)
|
u_int bg)
|
||||||
{
|
{
|
||||||
struct image *im;
|
struct screen *s = ctx->s;
|
||||||
|
struct grid *gd = s->grid;
|
||||||
|
struct tty_ctx ttyctx;
|
||||||
|
u_int x, y, sx, sy, cx = s->cx, cy = s->cy, i, lines;
|
||||||
|
struct sixel_image *new;
|
||||||
|
|
||||||
im = sixel_to_image(si);
|
if (screen_size_y(s) == 1)
|
||||||
if (im == NULL) {
|
|
||||||
sixel_free(si);
|
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
sixel_size_in_cells(si, &x, &y);
|
||||||
|
if (x > screen_size_x(s) || y > screen_size_y(s) - 1) {
|
||||||
|
if (x > screen_size_x(s) - cx)
|
||||||
|
sx = screen_size_x(s) - cx;
|
||||||
|
else
|
||||||
|
sx = x;
|
||||||
|
if (y > screen_size_y(s) - 1)
|
||||||
|
sy = screen_size_y(s) - 1;
|
||||||
|
else
|
||||||
|
sy = y;
|
||||||
|
new = sixel_scale(si, 0, 0, 0, y - sy, sx, sy, 1);
|
||||||
|
sixel_free(si);
|
||||||
|
if (new == NULL)
|
||||||
|
return;
|
||||||
|
sixel_size_in_cells(new, &x, &y);
|
||||||
|
si = new;
|
||||||
}
|
}
|
||||||
image_write(ctx, im, bg);
|
|
||||||
image_free(image_get_id(im));
|
sy = screen_size_y(s) - cy;
|
||||||
|
if (sy <= y) {
|
||||||
|
lines = y - sy + 1;
|
||||||
|
if (image_scroll_up(s, lines) && ctx->wp != NULL)
|
||||||
|
ctx->wp->flags |= PANE_REDRAW;
|
||||||
|
for (i = 0; i < lines; i++) {
|
||||||
|
grid_view_scroll_region_up(gd, 0, screen_size_y(s) - 1,
|
||||||
|
bg);
|
||||||
|
screen_write_collect_scroll(ctx, bg);
|
||||||
|
}
|
||||||
|
ctx->scrolled += lines;
|
||||||
|
if (lines > cy)
|
||||||
|
screen_write_cursormove(ctx, -1, 0, 0);
|
||||||
|
else
|
||||||
|
screen_write_cursormove(ctx, -1, cy - lines, 0);
|
||||||
|
}
|
||||||
|
screen_write_collect_flush(ctx, 0, __func__);
|
||||||
|
|
||||||
|
screen_write_initctx(ctx, &ttyctx, 0, 0);
|
||||||
|
ttyctx.image = image_store(s, si);
|
||||||
|
|
||||||
|
tty_write(tty_cmd_sixelimage, &ttyctx);
|
||||||
|
|
||||||
|
screen_write_cursormove(ctx, 0, cy + y, 0);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|||||||
50
screen.c
50
screen.c
@@ -93,6 +93,11 @@ screen_init(struct screen *s, u_int sx, u_int sy, u_int hlimit)
|
|||||||
s->tabs = NULL;
|
s->tabs = NULL;
|
||||||
s->sel = NULL;
|
s->sel = NULL;
|
||||||
|
|
||||||
|
#ifdef ENABLE_SIXEL
|
||||||
|
TAILQ_INIT(&s->images);
|
||||||
|
TAILQ_INIT(&s->saved_images);
|
||||||
|
#endif
|
||||||
|
|
||||||
s->write_list = NULL;
|
s->write_list = NULL;
|
||||||
s->hyperlinks = NULL;
|
s->hyperlinks = NULL;
|
||||||
|
|
||||||
@@ -126,6 +131,11 @@ screen_reinit(struct screen *s, int check)
|
|||||||
|
|
||||||
screen_clear_selection(s);
|
screen_clear_selection(s);
|
||||||
screen_free_titles(s);
|
screen_free_titles(s);
|
||||||
|
|
||||||
|
#ifdef ENABLE_SIXEL
|
||||||
|
image_free_all(s);
|
||||||
|
#endif
|
||||||
|
|
||||||
screen_set_progress_bar(s, PROGRESS_BAR_HIDDEN, 0);
|
screen_set_progress_bar(s, PROGRESS_BAR_HIDDEN, 0);
|
||||||
screen_reset_hyperlinks(s);
|
screen_reset_hyperlinks(s);
|
||||||
}
|
}
|
||||||
@@ -144,6 +154,10 @@ screen_reset_hyperlinks(struct screen *s)
|
|||||||
void
|
void
|
||||||
screen_free(struct screen *s)
|
screen_free(struct screen *s)
|
||||||
{
|
{
|
||||||
|
#ifdef ENABLE_SIXEL
|
||||||
|
struct image *im;
|
||||||
|
#endif
|
||||||
|
|
||||||
free(s->sel);
|
free(s->sel);
|
||||||
free(s->tabs);
|
free(s->tabs);
|
||||||
free(s->path);
|
free(s->path);
|
||||||
@@ -160,6 +174,17 @@ screen_free(struct screen *s)
|
|||||||
hyperlinks_free(s->hyperlinks);
|
hyperlinks_free(s->hyperlinks);
|
||||||
screen_free_titles(s);
|
screen_free_titles(s);
|
||||||
|
|
||||||
|
#ifdef ENABLE_SIXEL
|
||||||
|
/*
|
||||||
|
* Images saved when entering the alternate screen stay linked in the
|
||||||
|
* global list; move them back so they are freed and unlinked here, or
|
||||||
|
* a later eviction would write through this freed screen.
|
||||||
|
*/
|
||||||
|
TAILQ_CONCAT(&s->images, &s->saved_images, entry);
|
||||||
|
TAILQ_FOREACH(im, &s->images, entry)
|
||||||
|
im->list = &s->images;
|
||||||
|
image_free_all(s);
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Reset tabs to default, eight spaces apart. */
|
/* Reset tabs to default, eight spaces apart. */
|
||||||
@@ -352,6 +377,10 @@ screen_resize_cursor(struct screen *s, u_int sx, u_int sy, int reflow,
|
|||||||
if (sy != screen_size_y(s))
|
if (sy != screen_size_y(s))
|
||||||
screen_resize_y(s, sy, eat_empty, &cy);
|
screen_resize_y(s, sy, eat_empty, &cy);
|
||||||
|
|
||||||
|
#ifdef ENABLE_SIXEL
|
||||||
|
image_free_all(s);
|
||||||
|
#endif
|
||||||
|
|
||||||
if (reflow)
|
if (reflow)
|
||||||
screen_reflow(s, sx, &cx, &cy, cursor);
|
screen_reflow(s, sx, &cx, &cy, cursor);
|
||||||
|
|
||||||
@@ -663,6 +692,10 @@ int
|
|||||||
screen_alternate_on(struct screen *s, struct grid_cell *gc, int cursor)
|
screen_alternate_on(struct screen *s, struct grid_cell *gc, int cursor)
|
||||||
{
|
{
|
||||||
u_int sx, sy;
|
u_int sx, sy;
|
||||||
|
#ifdef ENABLE_SIXEL
|
||||||
|
struct image *im;
|
||||||
|
#endif
|
||||||
|
|
||||||
if (SCREEN_IS_ALTERNATE(s))
|
if (SCREEN_IS_ALTERNATE(s))
|
||||||
return 0;
|
return 0;
|
||||||
sx = screen_size_x(s);
|
sx = screen_size_x(s);
|
||||||
@@ -676,6 +709,12 @@ screen_alternate_on(struct screen *s, struct grid_cell *gc, int cursor)
|
|||||||
}
|
}
|
||||||
memcpy(&s->saved_cell, gc, sizeof s->saved_cell);
|
memcpy(&s->saved_cell, gc, sizeof s->saved_cell);
|
||||||
|
|
||||||
|
#ifdef ENABLE_SIXEL
|
||||||
|
TAILQ_CONCAT(&s->saved_images, &s->images, entry);
|
||||||
|
TAILQ_FOREACH(im, &s->saved_images, entry)
|
||||||
|
im->list = &s->saved_images;
|
||||||
|
#endif
|
||||||
|
|
||||||
grid_view_clear(s->grid, 0, 0, sx, sy, 8);
|
grid_view_clear(s->grid, 0, 0, sx, sy, 8);
|
||||||
|
|
||||||
s->saved_flags = s->grid->flags;
|
s->saved_flags = s->grid->flags;
|
||||||
@@ -689,6 +728,10 @@ int
|
|||||||
screen_alternate_off(struct screen *s, struct grid_cell *gc, int cursor)
|
screen_alternate_off(struct screen *s, struct grid_cell *gc, int cursor)
|
||||||
{
|
{
|
||||||
u_int sx = screen_size_x(s), sy = screen_size_y(s);
|
u_int sx = screen_size_x(s), sy = screen_size_y(s);
|
||||||
|
#ifdef ENABLE_SIXEL
|
||||||
|
struct image *im;
|
||||||
|
#endif
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* If the current size is different, temporarily resize to the old size
|
* If the current size is different, temporarily resize to the old size
|
||||||
* before copying back.
|
* before copying back.
|
||||||
@@ -731,6 +774,13 @@ screen_alternate_off(struct screen *s, struct grid_cell *gc, int cursor)
|
|||||||
grid_destroy(s->saved_grid);
|
grid_destroy(s->saved_grid);
|
||||||
s->saved_grid = NULL;
|
s->saved_grid = NULL;
|
||||||
|
|
||||||
|
#ifdef ENABLE_SIXEL
|
||||||
|
image_free_all(s);
|
||||||
|
TAILQ_CONCAT(&s->images, &s->saved_images, entry);
|
||||||
|
TAILQ_FOREACH(im, &s->images, entry)
|
||||||
|
im->list = &s->images;
|
||||||
|
#endif
|
||||||
|
|
||||||
if (s->cx > screen_size_x(s) - 1)
|
if (s->cx > screen_size_x(s) - 1)
|
||||||
s->cx = screen_size_x(s) - 1;
|
s->cx = screen_size_x(s) - 1;
|
||||||
if (s->cy > screen_size_y(s) - 1)
|
if (s->cy > screen_size_y(s) - 1)
|
||||||
|
|||||||
206
server-client.c
206
server-client.c
@@ -1,4 +1,4 @@
|
|||||||
/* $OpenBSD: server-client.c,v 1.500 2026/07/28 13:17:45 nicm Exp $ */
|
/* $OpenBSD: server-client.c,v 1.509 2026/08/28 07:36:01 nicm Exp $ */
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2009 Nicholas Marriott <nicholas.marriott@gmail.com>
|
* Copyright (c) 2009 Nicholas Marriott <nicholas.marriott@gmail.com>
|
||||||
@@ -36,7 +36,8 @@ static void server_client_check_window_resize(struct window *);
|
|||||||
static key_code server_client_check_mouse(struct client *, struct key_event *);
|
static key_code server_client_check_mouse(struct client *, struct key_event *);
|
||||||
static void server_client_repeat_timer(int, short, void *);
|
static void server_client_repeat_timer(int, short, void *);
|
||||||
static void server_client_click_timer(int, short, void *);
|
static void server_client_click_timer(int, short, void *);
|
||||||
static void server_client_check_exit(struct client *);
|
static void server_client_check_exit(struct client *, int);
|
||||||
|
static void server_client_exit_timer(int, short, void *);
|
||||||
static void server_client_check_redraw(struct client *);
|
static void server_client_check_redraw(struct client *);
|
||||||
static void server_client_check_modes(struct client *);
|
static void server_client_check_modes(struct client *);
|
||||||
static void server_client_set_title(struct client *);
|
static void server_client_set_title(struct client *);
|
||||||
@@ -44,6 +45,7 @@ static void server_client_set_path(struct client *);
|
|||||||
static void server_client_set_progress_bar(struct client *);
|
static void server_client_set_progress_bar(struct client *);
|
||||||
static void server_client_reset_state(struct client *);
|
static void server_client_reset_state(struct client *);
|
||||||
static void server_client_update_latest(struct client *);
|
static void server_client_update_latest(struct client *);
|
||||||
|
static int server_client_handle_dead_key(struct window_pane *, key_code);
|
||||||
static void server_client_dispatch(struct imsg *, void *);
|
static void server_client_dispatch(struct imsg *, void *);
|
||||||
static int server_client_dispatch_command(struct client *, struct imsg *);
|
static int server_client_dispatch_command(struct client *, struct imsg *);
|
||||||
static int server_client_dispatch_identify(struct client *, struct imsg *);
|
static int server_client_dispatch_identify(struct client *, struct imsg *);
|
||||||
@@ -105,7 +107,7 @@ server_client_set_overlay(struct client *c, u_int delay,
|
|||||||
c->tty.flags |= TTY_FREEZE;
|
c->tty.flags |= TTY_FREEZE;
|
||||||
if (c->overlay_mode == NULL)
|
if (c->overlay_mode == NULL)
|
||||||
c->tty.flags |= TTY_NOCURSOR;
|
c->tty.flags |= TTY_NOCURSOR;
|
||||||
window_update_focus(c->session->curw->window);
|
window_update_focus(active_get_effective_window(c, c->session));
|
||||||
server_redraw_client(c);
|
server_redraw_client(c);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -132,7 +134,7 @@ server_client_clear_overlay(struct client *c)
|
|||||||
|
|
||||||
c->tty.flags &= ~(TTY_FREEZE|TTY_NOCURSOR);
|
c->tty.flags &= ~(TTY_FREEZE|TTY_NOCURSOR);
|
||||||
if (c->session != NULL)
|
if (c->session != NULL)
|
||||||
window_update_focus(c->session->curw->window);
|
window_update_focus(active_get_effective_window(c, c->session));
|
||||||
server_redraw_client(c);
|
server_redraw_client(c);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -309,6 +311,7 @@ server_client_create(int fd)
|
|||||||
|
|
||||||
evtimer_set(&c->repeat_timer, server_client_repeat_timer, c);
|
evtimer_set(&c->repeat_timer, server_client_repeat_timer, c);
|
||||||
evtimer_set(&c->click_timer, server_client_click_timer, c);
|
evtimer_set(&c->click_timer, server_client_click_timer, c);
|
||||||
|
evtimer_set(&c->exit_timer, server_client_exit_timer, c);
|
||||||
|
|
||||||
c->click_wp = -1;
|
c->click_wp = -1;
|
||||||
|
|
||||||
@@ -376,7 +379,8 @@ server_client_attached_lost(struct client *c)
|
|||||||
found = NULL;
|
found = NULL;
|
||||||
TAILQ_FOREACH(loop, &clients, entry) {
|
TAILQ_FOREACH(loop, &clients, entry) {
|
||||||
s = loop->session;
|
s = loop->session;
|
||||||
if (loop == c || s == NULL || s->curw->window != w)
|
if (loop == c || s == NULL ||
|
||||||
|
active_get_effective_window(loop, s) != w)
|
||||||
continue;
|
continue;
|
||||||
if (found == NULL || timercmp(&loop->activity_time,
|
if (found == NULL || timercmp(&loop->activity_time,
|
||||||
&found->activity_time, >))
|
&found->activity_time, >))
|
||||||
@@ -457,15 +461,15 @@ server_client_set_session(struct client *c, struct session *s)
|
|||||||
c->flags |= CLIENT_FOCUSED;
|
c->flags |= CLIENT_FOCUSED;
|
||||||
|
|
||||||
if (old != NULL && old->curw != NULL)
|
if (old != NULL && old->curw != NULL)
|
||||||
window_update_focus(old->curw->window);
|
window_update_focus(active_get_effective_window(c, old));
|
||||||
if (s != NULL) {
|
if (s != NULL) {
|
||||||
s->curw->window->latest = c;
|
active_get_effective_window(c, s)->latest = c;
|
||||||
recalculate_sizes();
|
recalculate_sizes();
|
||||||
window_update_focus(s->curw->window);
|
window_update_focus(active_get_effective_window(c, s));
|
||||||
session_update_activity(s, NULL);
|
session_update_activity(s, NULL);
|
||||||
session_theme_changed(s);
|
session_theme_changed(s);
|
||||||
gettimeofday(&s->last_attached_time, NULL);
|
gettimeofday(&s->last_attached_time, NULL);
|
||||||
s->curw->flags &= ~WINLINK_ALERTFLAGS;
|
active_get_effective_winlink(c, s)->flags &= ~WINLINK_ALERTFLAGS;
|
||||||
alerts_check_session(s);
|
alerts_check_session(s);
|
||||||
tty_update_client_offset(c);
|
tty_update_client_offset(c);
|
||||||
status_timer_start(c);
|
status_timer_start(c);
|
||||||
@@ -490,6 +494,7 @@ server_client_lost(struct client *c)
|
|||||||
server_client_clear_overlay(c);
|
server_client_clear_overlay(c);
|
||||||
status_prompt_clear(c);
|
status_prompt_clear(c);
|
||||||
status_message_clear(c);
|
status_message_clear(c);
|
||||||
|
active_remove_client(c);
|
||||||
|
|
||||||
RB_FOREACH_SAFE(cf, client_files, &c->files, cf1) {
|
RB_FOREACH_SAFE(cf, client_files, &c->files, cf1) {
|
||||||
cf->error = EINTR;
|
cf->error = EINTR;
|
||||||
@@ -503,6 +508,7 @@ server_client_lost(struct client *c)
|
|||||||
server_client_attached_lost(c);
|
server_client_attached_lost(c);
|
||||||
events_fire_client("client-detached", c);
|
events_fire_client("client-detached", c);
|
||||||
}
|
}
|
||||||
|
active_check_clients();
|
||||||
if (c->name != NULL && (c->flags & (CLIENT_CONTROL|CLIENT_TERMINAL)))
|
if (c->name != NULL && (c->flags & (CLIENT_CONTROL|CLIENT_TERMINAL)))
|
||||||
events_fire_client("client-closed", c);
|
events_fire_client("client-closed", c);
|
||||||
|
|
||||||
@@ -521,10 +527,14 @@ server_client_lost(struct client *c)
|
|||||||
input_cancel_requests(c);
|
input_cancel_requests(c);
|
||||||
|
|
||||||
free(c->title);
|
free(c->title);
|
||||||
|
free(c->path);
|
||||||
free((void *)c->cwd);
|
free((void *)c->cwd);
|
||||||
|
free(c->exit_session);
|
||||||
|
free(c->exit_message);
|
||||||
|
|
||||||
evtimer_del(&c->repeat_timer);
|
evtimer_del(&c->repeat_timer);
|
||||||
evtimer_del(&c->click_timer);
|
evtimer_del(&c->click_timer);
|
||||||
|
evtimer_del(&c->exit_timer);
|
||||||
if (event_initialized(&c->cycle_timer))
|
if (event_initialized(&c->cycle_timer))
|
||||||
evtimer_del(&c->cycle_timer);
|
evtimer_del(&c->cycle_timer);
|
||||||
|
|
||||||
@@ -677,7 +687,7 @@ server_client_in_scrollbar_area(struct window_pane *wp, int px, int py)
|
|||||||
static void
|
static void
|
||||||
server_client_update_scrollbar_hover(struct client *c, int type, int px, int py)
|
server_client_update_scrollbar_hover(struct client *c, int type, int px, int py)
|
||||||
{
|
{
|
||||||
struct window *w = c->session->curw->window;
|
struct window *w = active_get_effective_window(c, c->session);
|
||||||
struct window_pane *wp;
|
struct window_pane *wp;
|
||||||
|
|
||||||
if (type != KEYC_TYPE_MOUSEMOVE)
|
if (type != KEYC_TYPE_MOUSEMOVE)
|
||||||
@@ -797,8 +807,7 @@ server_client_check_mouse_in_pane(struct window_pane *wp, int px, int py,
|
|||||||
} else {
|
} else {
|
||||||
/* Try the pane borders. */
|
/* Try the pane borders. */
|
||||||
TAILQ_FOREACH(fwp, &w->panes, entry) {
|
TAILQ_FOREACH(fwp, &w->panes, entry) {
|
||||||
if ((w->flags & WINDOW_ZOOMED) &&
|
if (!window_pane_is_visible(fwp))
|
||||||
(~fwp->flags & PANE_ZOOMED))
|
|
||||||
continue;
|
continue;
|
||||||
if (window_pane_is_floating(fwp) &&
|
if (window_pane_is_floating(fwp) &&
|
||||||
window_pane_get_pane_lines(fwp) == PANE_LINES_NONE)
|
window_pane_get_pane_lines(fwp) == PANE_LINES_NONE)
|
||||||
@@ -850,7 +859,7 @@ server_client_check_mouse(struct client *c, struct key_event *event)
|
|||||||
{
|
{
|
||||||
struct mouse_event *m = &event->m;
|
struct mouse_event *m = &event->m;
|
||||||
struct session *s = c->session, *fs;
|
struct session *s = c->session, *fs;
|
||||||
struct window *w = s->curw->window;
|
struct window *w = active_get_effective_window(c, s);
|
||||||
struct winlink *fwl;
|
struct winlink *fwl;
|
||||||
struct window_pane *wp, *fwp, *lwp = NULL;
|
struct window_pane *wp, *fwp, *lwp = NULL;
|
||||||
u_int x, y, sx, sy, px, py, n, sl_mpos = 0;
|
u_int x, y, sx, sy, px, py, n, sl_mpos = 0;
|
||||||
@@ -1353,7 +1362,7 @@ server_client_update_latest(struct client *c)
|
|||||||
|
|
||||||
if (c->session == NULL)
|
if (c->session == NULL)
|
||||||
return;
|
return;
|
||||||
w = c->session->curw->window;
|
w = active_get_effective_window(c, c->session);
|
||||||
|
|
||||||
if (w->latest == c)
|
if (w->latest == c)
|
||||||
return;
|
return;
|
||||||
@@ -1385,6 +1394,21 @@ server_client_repeat_time(struct client *c, struct key_binding *bd)
|
|||||||
return (repeat);
|
return (repeat);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Handle a key press on a dead pane waiting for a key. */
|
||||||
|
static int
|
||||||
|
server_client_handle_dead_key(struct window_pane *wp, key_code key)
|
||||||
|
{
|
||||||
|
if (wp == NULL ||
|
||||||
|
(~wp->flags & PANE_EXITED) ||
|
||||||
|
KEYC_IS_MOUSE(key) ||
|
||||||
|
KEYC_IS_PASTE(key) ||
|
||||||
|
options_get_number(wp->options, "remain-on-exit") != 3)
|
||||||
|
return (0);
|
||||||
|
options_set_number(wp->options, "remain-on-exit", 0);
|
||||||
|
server_destroy_pane(wp, 0);
|
||||||
|
return (1);
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Handle data key input from client. This owns and can modify the key event it
|
* Handle data key input from client. This owns and can modify the key event it
|
||||||
* is given and is responsible for freeing it.
|
* is given and is responsible for freeing it.
|
||||||
@@ -1417,7 +1441,7 @@ server_client_key_callback(struct cmdq_item *item, void *data)
|
|||||||
/* Check the client is good to accept input. */
|
/* Check the client is good to accept input. */
|
||||||
if (s == NULL || (c->flags & CLIENT_UNATTACHEDFLAGS))
|
if (s == NULL || (c->flags & CLIENT_UNATTACHEDFLAGS))
|
||||||
goto out;
|
goto out;
|
||||||
wl = s->curw;
|
wl = active_get_effective_winlink(c, s);
|
||||||
|
|
||||||
/* Update the activity timer. */
|
/* Update the activity timer. */
|
||||||
memcpy(&c->last_activity_time, &c->activity_time,
|
memcpy(&c->last_activity_time, &c->activity_time,
|
||||||
@@ -1470,6 +1494,18 @@ server_client_key_callback(struct cmdq_item *item, void *data)
|
|||||||
server_client_is_assume_paste(c))
|
server_client_is_assume_paste(c))
|
||||||
goto paste_key;
|
goto paste_key;
|
||||||
|
|
||||||
|
/* Forward keys directly if this pane is capturing all keys. */
|
||||||
|
if (wp != NULL &&
|
||||||
|
(wp->flags & PANE_CAPTUREALLKEYS) &&
|
||||||
|
(~wp->flags & PANE_EXITED) &&
|
||||||
|
!KEYC_IS_MOUSE(key) &&
|
||||||
|
TAILQ_EMPTY(&wp->modes))
|
||||||
|
goto forward_key;
|
||||||
|
|
||||||
|
/* Focus events are not keys and cannot be bound. */
|
||||||
|
if (key == KEYC_FOCUS_IN || key == KEYC_FOCUS_OUT)
|
||||||
|
goto forward_key;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Work out the current key table. If the pane is in a mode, use
|
* Work out the current key table. If the pane is in a mode, use
|
||||||
* the mode table instead of the default key table.
|
* the mode table instead of the default key table.
|
||||||
@@ -1634,15 +1670,8 @@ try_again:
|
|||||||
}
|
}
|
||||||
|
|
||||||
forward_key:
|
forward_key:
|
||||||
if (wp != NULL &&
|
if (server_client_handle_dead_key(wp, key))
|
||||||
(wp->flags & PANE_EXITED) &&
|
|
||||||
!KEYC_IS_MOUSE(key) &&
|
|
||||||
!KEYC_IS_PASTE(key) &&
|
|
||||||
options_get_number(wp->options, "remain-on-exit") == 3) {
|
|
||||||
options_set_number(wp->options, "remain-on-exit", 0);
|
|
||||||
server_destroy_pane(wp, 0);
|
|
||||||
goto out;
|
goto out;
|
||||||
}
|
|
||||||
if (c->flags & CLIENT_READONLY)
|
if (c->flags & CLIENT_READONLY)
|
||||||
goto out;
|
goto out;
|
||||||
if (wp != NULL)
|
if (wp != NULL)
|
||||||
@@ -1671,7 +1700,7 @@ out:
|
|||||||
static int
|
static int
|
||||||
server_client_handle_menu_key(struct client *c, struct key_event *event)
|
server_client_handle_menu_key(struct client *c, struct key_event *event)
|
||||||
{
|
{
|
||||||
struct window *w = c->session->curw->window;
|
struct window *w = active_get_effective_window(c, c->session);
|
||||||
struct key_event new_event;
|
struct key_event new_event;
|
||||||
struct mouse_event *m;
|
struct mouse_event *m;
|
||||||
u_int ox, oy, sx, sy;
|
u_int ox, oy, sx, sy;
|
||||||
@@ -1710,6 +1739,7 @@ server_client_handle_key0(struct client *c, struct key_event *event,
|
|||||||
{
|
{
|
||||||
struct session *s = c->session;
|
struct session *s = c->session;
|
||||||
struct cmdq_item *item;
|
struct cmdq_item *item;
|
||||||
|
struct window *w;
|
||||||
struct window_pane *wp;
|
struct window_pane *wp;
|
||||||
|
|
||||||
/* Check the client is good to accept input. */
|
/* Check the client is good to accept input. */
|
||||||
@@ -1730,9 +1760,9 @@ server_client_handle_key0(struct client *c, struct key_event *event,
|
|||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Key presses in overlay mode and the command prompt are a special
|
* Key presses in overlay mode, for panes capturing all keys and in the
|
||||||
* case. The queue might be blocked so they need to be processed
|
* command prompt are a special case. The queue might be blocked so they
|
||||||
* immediately rather than queued.
|
* 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) {
|
||||||
@@ -1752,6 +1782,21 @@ server_client_handle_key0(struct client *c, struct key_event *event,
|
|||||||
}
|
}
|
||||||
|
|
||||||
server_client_clear_overlay(c);
|
server_client_clear_overlay(c);
|
||||||
|
|
||||||
|
wp = s->curw->window->active;
|
||||||
|
if (wp != NULL &&
|
||||||
|
(wp->flags & PANE_CAPTUREALLKEYS) &&
|
||||||
|
TAILQ_EMPTY(&wp->modes) &&
|
||||||
|
!KEYC_IS_MOUSE(event->key)) {
|
||||||
|
if (server_client_handle_dead_key(wp, event->key))
|
||||||
|
return (0);
|
||||||
|
if (~wp->flags & PANE_EXITED) {
|
||||||
|
window_pane_key(wp, c, s, s->curw, event->key,
|
||||||
|
&event->m);
|
||||||
|
return (0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (server_client_handle_menu_key(c, event))
|
if (server_client_handle_menu_key(c, event))
|
||||||
return (0);
|
return (0);
|
||||||
if (c->prompt != NULL) {
|
if (c->prompt != NULL) {
|
||||||
@@ -1765,9 +1810,10 @@ server_client_handle_key0(struct client *c, struct key_event *event,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
wp = s->curw->window->active;
|
w = active_get_effective_window(c, s);
|
||||||
|
wp = w->active;
|
||||||
if (wp == NULL || !window_pane_has_prompt(wp)) {
|
if (wp == NULL || !window_pane_has_prompt(wp)) {
|
||||||
TAILQ_FOREACH(wp, &s->curw->window->panes, entry) {
|
TAILQ_FOREACH(wp, &w->panes, entry) {
|
||||||
if (window_pane_has_prompt(wp) &&
|
if (window_pane_has_prompt(wp) &&
|
||||||
window_pane_is_visible(wp))
|
window_pane_is_visible(wp))
|
||||||
break;
|
break;
|
||||||
@@ -1849,7 +1895,7 @@ server_client_loop(void)
|
|||||||
|
|
||||||
/* Check clients. */
|
/* Check clients. */
|
||||||
TAILQ_FOREACH(c, &clients, entry) {
|
TAILQ_FOREACH(c, &clients, entry) {
|
||||||
server_client_check_exit(c);
|
server_client_check_exit(c, 0);
|
||||||
if (c->session != NULL && c->session->curw != NULL) {
|
if (c->session != NULL && c->session->curw != NULL) {
|
||||||
server_client_check_modes(c);
|
server_client_check_modes(c);
|
||||||
server_client_check_redraw(c);
|
server_client_check_redraw(c);
|
||||||
@@ -2109,8 +2155,8 @@ static void
|
|||||||
server_client_reset_state(struct client *c)
|
server_client_reset_state(struct client *c)
|
||||||
{
|
{
|
||||||
struct tty *tty = &c->tty;
|
struct tty *tty = &c->tty;
|
||||||
struct window *w = c->session->curw->window;
|
struct window *w = active_get_effective_window(c, c->session);
|
||||||
struct window_pane *wp = w->active, *loop;
|
struct window_pane *wp, *loop;
|
||||||
struct screen *s = NULL;
|
struct screen *s = NULL;
|
||||||
struct options *oo = c->session->options;
|
struct options *oo = c->session->options;
|
||||||
int mode = 0, cursor, flags, pane_mode = 0;
|
int mode = 0, cursor, flags, pane_mode = 0;
|
||||||
@@ -2118,6 +2164,7 @@ server_client_reset_state(struct client *c)
|
|||||||
u_int sb_w;
|
u_int sb_w;
|
||||||
struct visible_ranges *r;
|
struct visible_ranges *r;
|
||||||
|
|
||||||
|
wp = active_get_effective_pane(c, w);
|
||||||
if (c->flags & (CLIENT_CONTROL|CLIENT_SUSPENDED))
|
if (c->flags & (CLIENT_CONTROL|CLIENT_SUSPENDED))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
@@ -2285,9 +2332,37 @@ server_client_click_timer(__unused int fd, __unused short events, void *data)
|
|||||||
c->flags &= ~(CLIENT_DOUBLECLICK|CLIENT_TRIPLECLICK);
|
c->flags &= ~(CLIENT_DOUBLECLICK|CLIENT_TRIPLECLICK);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Check if client should be exited. */
|
/* Start client exit timer. */
|
||||||
static void
|
static void
|
||||||
server_client_check_exit(struct client *c)
|
server_client_start_exit_timer(struct client *c)
|
||||||
|
{
|
||||||
|
struct timeval tv = { .tv_sec = 10 };
|
||||||
|
|
||||||
|
if (!evtimer_pending(&c->exit_timer, NULL))
|
||||||
|
evtimer_add(&c->exit_timer, &tv);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Exit timer has expired: stop waiting for the client. */
|
||||||
|
static void
|
||||||
|
server_client_exit_timer(__unused int fd, __unused short events, void *data)
|
||||||
|
{
|
||||||
|
struct client *c = data;
|
||||||
|
|
||||||
|
if (c->flags & (CLIENT_DEAD|CLIENT_SUSPENDED))
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (c->flags & CLIENT_EXITED) {
|
||||||
|
log_debug("%s: %s took too long to exit", __func__, c->name);
|
||||||
|
server_client_lost(c);
|
||||||
|
} else if (c->flags & CLIENT_EXIT) {
|
||||||
|
log_debug("%s: %s took too long to flush", __func__, c->name);
|
||||||
|
server_client_check_exit(c, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Check if client should be exited, abandoning buffered output if forced. */
|
||||||
|
static void
|
||||||
|
server_client_check_exit(struct client *c, int force)
|
||||||
{
|
{
|
||||||
struct client_file *cf;
|
struct client_file *cf;
|
||||||
const char *name = c->exit_session;
|
const char *name = c->exit_session;
|
||||||
@@ -2300,16 +2375,29 @@ server_client_check_exit(struct client *c)
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
if (c->flags & CLIENT_CONTROL) {
|
if (c->flags & CLIENT_CONTROL) {
|
||||||
|
if (force)
|
||||||
|
control_discard_all(c);
|
||||||
|
else {
|
||||||
control_discard(c);
|
control_discard(c);
|
||||||
if (!control_all_done(c))
|
if (!control_all_done(c)) {
|
||||||
|
server_client_start_exit_timer(c);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!force) {
|
||||||
RB_FOREACH(cf, client_files, &c->files) {
|
RB_FOREACH(cf, client_files, &c->files) {
|
||||||
if (EVBUFFER_LENGTH(cf->buffer) != 0)
|
if (EVBUFFER_LENGTH(cf->buffer) != 0) {
|
||||||
|
server_client_start_exit_timer(c);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
c->flags |= CLIENT_EXITED;
|
c->flags |= CLIENT_EXITED;
|
||||||
|
|
||||||
|
evtimer_del(&c->exit_timer);
|
||||||
|
server_client_start_exit_timer(c);
|
||||||
|
|
||||||
switch (c->exit_type) {
|
switch (c->exit_type) {
|
||||||
case CLIENT_EXIT_RETURN:
|
case CLIENT_EXIT_RETURN:
|
||||||
if (c->exit_message != NULL)
|
if (c->exit_message != NULL)
|
||||||
@@ -2331,8 +2419,6 @@ server_client_check_exit(struct client *c)
|
|||||||
proc_send(c->peer, c->exit_msgtype, -1, name, strlen(name) + 1);
|
proc_send(c->peer, c->exit_msgtype, -1, name, strlen(name) + 1);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
free(c->exit_session);
|
|
||||||
free(c->exit_message);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Redraw timer callback. */
|
/* Redraw timer callback. */
|
||||||
@@ -2350,7 +2436,7 @@ server_client_redraw_timer(__unused int fd, __unused short events,
|
|||||||
static void
|
static void
|
||||||
server_client_check_modes(struct client *c)
|
server_client_check_modes(struct client *c)
|
||||||
{
|
{
|
||||||
struct window *w = c->session->curw->window;
|
struct window *w;
|
||||||
struct window_pane *wp;
|
struct window_pane *wp;
|
||||||
struct window_mode_entry *wme;
|
struct window_mode_entry *wme;
|
||||||
|
|
||||||
@@ -2358,6 +2444,7 @@ server_client_check_modes(struct client *c)
|
|||||||
return;
|
return;
|
||||||
if (~c->flags & CLIENT_REDRAWSTATUS)
|
if (~c->flags & CLIENT_REDRAWSTATUS)
|
||||||
return;
|
return;
|
||||||
|
w = active_get_effective_window(c, c->session);
|
||||||
TAILQ_FOREACH(wp, &w->panes, entry) {
|
TAILQ_FOREACH(wp, &w->panes, entry) {
|
||||||
wme = TAILQ_FIRST(&wp->modes);
|
wme = TAILQ_FIRST(&wp->modes);
|
||||||
if (wme != NULL && wme->mode->update != NULL)
|
if (wme != NULL && wme->mode->update != NULL)
|
||||||
@@ -2370,7 +2457,7 @@ static int
|
|||||||
server_client_any_pane_redraw(struct client *c)
|
server_client_any_pane_redraw(struct client *c)
|
||||||
{
|
{
|
||||||
struct session *s = c->session;
|
struct session *s = c->session;
|
||||||
struct window *w = s->curw->window;
|
struct window *w = active_get_effective_window(c, s);
|
||||||
struct window_pane *wp;
|
struct window_pane *wp;
|
||||||
|
|
||||||
if (c->flags & CLIENT_REDRAWWINDOW)
|
if (c->flags & CLIENT_REDRAWWINDOW)
|
||||||
@@ -2388,7 +2475,7 @@ server_client_check_redraw(struct client *c)
|
|||||||
{
|
{
|
||||||
struct session *s = c->session;
|
struct session *s = c->session;
|
||||||
struct tty *tty = &c->tty;
|
struct tty *tty = &c->tty;
|
||||||
struct window *w = s->curw->window;
|
struct window *w = active_get_effective_window(c, s);
|
||||||
struct window_pane *wp;
|
struct window_pane *wp;
|
||||||
int needed, tflags, mode = tty->mode;
|
int needed, tflags, mode = tty->mode;
|
||||||
struct timeval tv = { .tv_usec = 1000 };
|
struct timeval tv = { .tv_usec = 1000 };
|
||||||
@@ -2408,7 +2495,7 @@ server_client_check_redraw(struct client *c)
|
|||||||
|
|
||||||
/* Work out if a redraw is actually needed. */
|
/* Work out if a redraw is actually needed. */
|
||||||
needed = 0;
|
needed = 0;
|
||||||
if (c->flags & CLIENT_ALLREDRAWFLAGS)
|
if (c->flags & (CLIENT_ALLREDRAWFLAGS|CLIENT_REDRAWSCROLLBARS))
|
||||||
needed = 1;
|
needed = 1;
|
||||||
else if (server_client_any_pane_redraw(c))
|
else if (server_client_any_pane_redraw(c))
|
||||||
needed = 1;
|
needed = 1;
|
||||||
@@ -2434,8 +2521,14 @@ server_client_check_redraw(struct client *c)
|
|||||||
log_debug("redraw timer started");
|
log_debug("redraw timer started");
|
||||||
evtimer_add(&ev, &tv);
|
evtimer_add(&ev, &tv);
|
||||||
}
|
}
|
||||||
if (server_client_any_pane_redraw(c))
|
TAILQ_FOREACH(wp, &w->panes, entry) {
|
||||||
|
if (wp->flags & PANE_REDRAW) {
|
||||||
c->flags |= CLIENT_REDRAWWINDOW;
|
c->flags |= CLIENT_REDRAWWINDOW;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (wp->flags & PANE_REDRAWSCROLLBAR)
|
||||||
|
c->flags |= CLIENT_REDRAWSCROLLBARS;
|
||||||
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2454,7 +2547,8 @@ server_client_check_redraw(struct client *c)
|
|||||||
log_debug("%s: redraw pane %%%u", __func__,
|
log_debug("%s: redraw pane %%%u", __func__,
|
||||||
wp->id);
|
wp->id);
|
||||||
redraw_pane(c, wp);
|
redraw_pane(c, wp);
|
||||||
} else if (wp->flags & PANE_REDRAWSCROLLBAR) {
|
} else if ((wp->flags & PANE_REDRAWSCROLLBAR) ||
|
||||||
|
(c->flags & CLIENT_REDRAWSCROLLBARS)) {
|
||||||
log_debug("%s: redraw scrollbar %%%u", __func__,
|
log_debug("%s: redraw scrollbar %%%u", __func__,
|
||||||
wp->id);
|
wp->id);
|
||||||
redraw_pane_scrollbar(c, wp);
|
redraw_pane_scrollbar(c, wp);
|
||||||
@@ -2484,7 +2578,8 @@ server_client_check_redraw(struct client *c)
|
|||||||
* All the redraw flags can now be cleared. Also record how many bytes
|
* All the redraw flags can now be cleared. Also record how many bytes
|
||||||
* were written.
|
* were written.
|
||||||
*/
|
*/
|
||||||
c->flags &= ~(CLIENT_ALLREDRAWFLAGS|CLIENT_STATUSFORCE);
|
c->flags &= ~(CLIENT_ALLREDRAWFLAGS|CLIENT_REDRAWSCROLLBARS|
|
||||||
|
CLIENT_STATUSFORCE);
|
||||||
c->redraw = EVBUFFER_LENGTH(tty->out);
|
c->redraw = EVBUFFER_LENGTH(tty->out);
|
||||||
log_debug("%s: redraw added %zu bytes", c->name, c->redraw);
|
log_debug("%s: redraw added %zu bytes", c->name, c->redraw);
|
||||||
}
|
}
|
||||||
@@ -2519,14 +2614,15 @@ static void
|
|||||||
server_client_set_path(struct client *c)
|
server_client_set_path(struct client *c)
|
||||||
{
|
{
|
||||||
struct session *s = c->session;
|
struct session *s = c->session;
|
||||||
|
struct window *w = active_get_effective_window(c, s);
|
||||||
const char *path;
|
const char *path;
|
||||||
|
|
||||||
if (s->curw == NULL || s->curw->window->active == NULL)
|
if (w == NULL || w->active == NULL)
|
||||||
return;
|
return;
|
||||||
if (s->curw->window->active->base.path == NULL)
|
if (w->active->base.path == NULL)
|
||||||
path = "";
|
path = "";
|
||||||
else
|
else
|
||||||
path = s->curw->window->active->base.path;
|
path = w->active->base.path;
|
||||||
if (c->path == NULL || strcmp(path, c->path) != 0) {
|
if (c->path == NULL || strcmp(path, c->path) != 0) {
|
||||||
free(c->path);
|
free(c->path);
|
||||||
c->path = xstrdup(path);
|
c->path = xstrdup(path);
|
||||||
@@ -2539,11 +2635,12 @@ static void
|
|||||||
server_client_set_progress_bar(struct client *c)
|
server_client_set_progress_bar(struct client *c)
|
||||||
{
|
{
|
||||||
struct session *s = c->session;
|
struct session *s = c->session;
|
||||||
|
struct window *w = active_get_effective_window(c, s);
|
||||||
struct progress_bar *pane_pb;
|
struct progress_bar *pane_pb;
|
||||||
|
|
||||||
if (s->curw == NULL || s->curw->window->active == NULL)
|
if (w == NULL || w->active == NULL)
|
||||||
return;
|
return;
|
||||||
pane_pb = &s->curw->window->active->base.progress_bar;
|
pane_pb = &w->active->base.progress_bar;
|
||||||
if (pane_pb->state == c->progress_bar.state &&
|
if (pane_pb->state == c->progress_bar.state &&
|
||||||
pane_pb->progress == c->progress_bar.progress)
|
pane_pb->progress == c->progress_bar.progress)
|
||||||
return;
|
return;
|
||||||
@@ -2651,6 +2748,10 @@ server_client_dispatch(struct imsg *imsg, void *arg)
|
|||||||
if (file_write_ready(&c->files, imsg) != 0)
|
if (file_write_ready(&c->files, imsg) != 0)
|
||||||
goto bad;
|
goto bad;
|
||||||
break;
|
break;
|
||||||
|
case MSG_WRITE_DONE:
|
||||||
|
if (file_write_done(&c->files, imsg) != 0)
|
||||||
|
goto bad;
|
||||||
|
break;
|
||||||
case MSG_READ:
|
case MSG_READ:
|
||||||
if (file_read_data(&c->files, imsg) != 0)
|
if (file_read_data(&c->files, imsg) != 0)
|
||||||
goto bad;
|
goto bad;
|
||||||
@@ -3076,6 +3177,7 @@ server_client_remove_pane(struct window_pane *wp)
|
|||||||
{
|
{
|
||||||
struct client *c;
|
struct client *c;
|
||||||
|
|
||||||
|
active_remove_pane(wp);
|
||||||
TAILQ_FOREACH(c, &clients, entry) {
|
TAILQ_FOREACH(c, &clients, entry) {
|
||||||
if (c->tty.mouse_last_pane == (int)wp->id) {
|
if (c->tty.mouse_last_pane == (int)wp->id) {
|
||||||
c->tty.mouse_last_pane = -1;
|
c->tty.mouse_last_pane = -1;
|
||||||
@@ -3091,6 +3193,7 @@ server_client_print(struct client *c, int parse, struct evbuffer *evb)
|
|||||||
{
|
{
|
||||||
void *data = EVBUFFER_DATA(evb);
|
void *data = EVBUFFER_DATA(evb);
|
||||||
size_t size = EVBUFFER_LENGTH(evb);
|
size_t size = EVBUFFER_LENGTH(evb);
|
||||||
|
struct window *w;
|
||||||
struct window_pane *wp;
|
struct window_pane *wp;
|
||||||
struct window_mode_entry *wme;
|
struct window_mode_entry *wme;
|
||||||
char *sanitized, *msg, *line, empty = '\0';
|
char *sanitized, *msg, *line, empty = '\0';
|
||||||
@@ -3129,7 +3232,8 @@ server_client_print(struct client *c, int parse, struct evbuffer *evb)
|
|||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
wp = c->session->curw->window->active;
|
w = active_get_effective_window(c, c->session);
|
||||||
|
wp = active_get_effective_pane(c, w);
|
||||||
wme = TAILQ_FIRST(&wp->modes);
|
wme = TAILQ_FIRST(&wp->modes);
|
||||||
if (wme == NULL || wme->mode != &window_view_mode)
|
if (wme == NULL || wme->mode != &window_view_mode)
|
||||||
window_pane_set_mode(wp, NULL, &window_view_mode, NULL, NULL,
|
window_pane_set_mode(wp, NULL, &window_view_mode, NULL, NULL,
|
||||||
|
|||||||
21
server-fn.c
21
server-fn.c
@@ -1,4 +1,4 @@
|
|||||||
/* $OpenBSD: server-fn.c,v 1.149 2026/07/27 14:25:46 nicm Exp $ */
|
/* $OpenBSD: server-fn.c,v 1.151 2026/08/20 09:19:24 nicm Exp $ */
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>
|
* Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>
|
||||||
@@ -122,8 +122,7 @@ server_redraw_window(struct window *w)
|
|||||||
|
|
||||||
TAILQ_FOREACH(c, &clients, entry) {
|
TAILQ_FOREACH(c, &clients, entry) {
|
||||||
if (c->session != NULL &&
|
if (c->session != NULL &&
|
||||||
c->session->curw != NULL &&
|
active_get_effective_window(c, c->session) == w)
|
||||||
c->session->curw->window == w)
|
|
||||||
server_redraw_client(c);
|
server_redraw_client(c);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -135,8 +134,7 @@ server_redraw_window_menu(struct window *w)
|
|||||||
|
|
||||||
TAILQ_FOREACH(c, &clients, entry) {
|
TAILQ_FOREACH(c, &clients, entry) {
|
||||||
if (c->session != NULL &&
|
if (c->session != NULL &&
|
||||||
c->session->curw != NULL &&
|
active_get_effective_window(c, c->session) == w)
|
||||||
c->session->curw->window == w)
|
|
||||||
c->flags |= CLIENT_REDRAWMENU;
|
c->flags |= CLIENT_REDRAWMENU;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -148,8 +146,7 @@ server_redraw_window_borders(struct window *w)
|
|||||||
|
|
||||||
TAILQ_FOREACH(c, &clients, entry) {
|
TAILQ_FOREACH(c, &clients, entry) {
|
||||||
if (c->session != NULL &&
|
if (c->session != NULL &&
|
||||||
c->session->curw != NULL &&
|
active_get_effective_window(c, c->session) == w)
|
||||||
c->session->curw->window == w)
|
|
||||||
c->flags |= CLIENT_REDRAWBORDERS;
|
c->flags |= CLIENT_REDRAWBORDERS;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -226,10 +223,11 @@ server_kill_pane(struct window_pane *wp)
|
|||||||
server_kill_window(w, 1);
|
server_kill_window(w, 1);
|
||||||
recalculate_sizes();
|
recalculate_sizes();
|
||||||
} else {
|
} else {
|
||||||
server_unzoom_window(w);
|
window_push_zoom(w, 0, wp->flags & PANE_FLOATOVERZOOM);
|
||||||
server_client_remove_pane(wp);
|
server_client_remove_pane(wp);
|
||||||
layout_close_pane(wp);
|
layout_close_pane(wp);
|
||||||
window_remove_pane(w, wp);
|
window_remove_pane(w, wp);
|
||||||
|
window_pop_zoom(w);
|
||||||
server_redraw_window(w);
|
server_redraw_window(w);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -421,15 +419,17 @@ server_destroy_pane(struct window_pane *wp, int notify)
|
|||||||
if (notify)
|
if (notify)
|
||||||
server_fire_pane_exit("pane-exited", wp);
|
server_fire_pane_exit("pane-exited", wp);
|
||||||
|
|
||||||
server_unzoom_window(w);
|
window_push_zoom(w, 0, wp->flags & PANE_FLOATOVERZOOM);
|
||||||
server_client_remove_pane(wp);
|
server_client_remove_pane(wp);
|
||||||
layout_close_pane(wp);
|
layout_close_pane(wp);
|
||||||
window_remove_pane(w, wp);
|
window_remove_pane(w, wp);
|
||||||
|
|
||||||
if (TAILQ_EMPTY(&w->panes))
|
if (TAILQ_EMPTY(&w->panes))
|
||||||
server_kill_window(w, 1);
|
server_kill_window(w, 1);
|
||||||
else
|
else {
|
||||||
|
window_pop_zoom(w);
|
||||||
server_redraw_window(w);
|
server_redraw_window(w);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
@@ -551,6 +551,7 @@ server_check_unattached(void)
|
|||||||
continue;
|
continue;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
server_destroy_session(s);
|
||||||
session_destroy(s, 1, __func__);
|
session_destroy(s, 1, __func__);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
5
server.c
5
server.c
@@ -1,4 +1,4 @@
|
|||||||
/* $OpenBSD: server.c,v 1.214 2026/07/10 13:38:45 nicm Exp $ */
|
/* $OpenBSD: server.c,v 1.216 2026/08/18 07:24:49 nicm Exp $ */
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>
|
* Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>
|
||||||
@@ -129,6 +129,7 @@ server_create_socket(uint64_t flags, char **cause)
|
|||||||
mask = umask(S_IXUSR|S_IRWXG|S_IRWXO);
|
mask = umask(S_IXUSR|S_IRWXG|S_IRWXO);
|
||||||
if (bind(fd, (struct sockaddr *)&sa, sizeof sa) == -1) {
|
if (bind(fd, (struct sockaddr *)&sa, sizeof sa) == -1) {
|
||||||
saved_errno = errno;
|
saved_errno = errno;
|
||||||
|
umask(mask);
|
||||||
close(fd);
|
close(fd);
|
||||||
errno = saved_errno;
|
errno = saved_errno;
|
||||||
goto fail;
|
goto fail;
|
||||||
@@ -240,6 +241,7 @@ server_start(struct tmuxproc *client, uint64_t flags, struct event_base *base,
|
|||||||
if (cause != NULL) {
|
if (cause != NULL) {
|
||||||
if (c != NULL) {
|
if (c != NULL) {
|
||||||
c->exit_message = cause;
|
c->exit_message = cause;
|
||||||
|
c->retval = 1;
|
||||||
c->flags |= CLIENT_EXIT;
|
c->flags |= CLIENT_EXIT;
|
||||||
} else {
|
} else {
|
||||||
fprintf(stderr, "%s\n", cause);
|
fprintf(stderr, "%s\n", cause);
|
||||||
@@ -398,6 +400,7 @@ server_accept(int fd, short events, __unused void *data)
|
|||||||
c = server_client_create(newfd);
|
c = server_client_create(newfd);
|
||||||
if (!server_acl_join(c)) {
|
if (!server_acl_join(c)) {
|
||||||
c->exit_message = xstrdup("access not allowed");
|
c->exit_message = xstrdup("access not allowed");
|
||||||
|
c->retval = 1;
|
||||||
c->flags |= CLIENT_EXIT;
|
c->flags |= CLIENT_EXIT;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
/* $OpenBSD: session.c,v 1.106 2026/07/13 13:01:14 nicm Exp $ */
|
/* $OpenBSD: session.c,v 1.107 2026/08/05 07:35:35 nicm Exp $ */
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>
|
* Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>
|
||||||
@@ -203,6 +203,7 @@ session_destroy(struct session *s, int notify, const char *from)
|
|||||||
if (s->curw == NULL)
|
if (s->curw == NULL)
|
||||||
return;
|
return;
|
||||||
s->curw = NULL;
|
s->curw = NULL;
|
||||||
|
active_remove_session(s);
|
||||||
|
|
||||||
RB_REMOVE(sessions, &sessions, s);
|
RB_REMOVE(sessions, &sessions, s);
|
||||||
if (notify)
|
if (notify)
|
||||||
@@ -709,8 +710,10 @@ session_group_synchronize1(struct session *target, struct session *s)
|
|||||||
/* Fix up the current window. */
|
/* Fix up the current window. */
|
||||||
if (s->curw != NULL)
|
if (s->curw != NULL)
|
||||||
s->curw = winlink_find_by_index(&s->windows, s->curw->idx);
|
s->curw = winlink_find_by_index(&s->windows, s->curw->idx);
|
||||||
else
|
else if (target->curw != NULL)
|
||||||
s->curw = winlink_find_by_index(&s->windows, target->curw->idx);
|
s->curw = winlink_find_by_index(&s->windows, target->curw->idx);
|
||||||
|
if (s->curw == NULL)
|
||||||
|
s->curw = RB_MIN(winlinks, &s->windows);
|
||||||
|
|
||||||
/* Fix up the last window stack. */
|
/* Fix up the last window stack. */
|
||||||
memcpy(&old_lastw, &s->lastw, sizeof old_lastw);
|
memcpy(&old_lastw, &s->lastw, sizeof old_lastw);
|
||||||
|
|||||||
4
sort.c
4
sort.c
@@ -1,4 +1,4 @@
|
|||||||
/* $OpenBSD: sort.c,v 1.9 2026/06/29 07:45:09 nicm Exp $ */
|
/* $OpenBSD: sort.c,v 1.10 2026/08/25 07:23:30 nicm Exp $ */
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2026 Dane Jensen <dhcjensen@gmail.com>
|
* Copyright (c) 2026 Dane Jensen <dhcjensen@gmail.com>
|
||||||
@@ -32,7 +32,7 @@ sort_qsort(void *l, u_int len, u_int size, int (*cmp)(const void *,
|
|||||||
u_int i;
|
u_int i;
|
||||||
void *tmp, **ll;
|
void *tmp, **ll;
|
||||||
|
|
||||||
if (sort_crit->order == SORT_END)
|
if (len < 2 || sort_crit->order == SORT_END)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (sort_crit->order == SORT_ORDER) {
|
if (sort_crit->order == SORT_ORDER) {
|
||||||
|
|||||||
29
spawn.c
29
spawn.c
@@ -1,4 +1,4 @@
|
|||||||
/* $OpenBSD: spawn.c,v 1.50 2026/07/23 09:38:27 nicm Exp $ */
|
/* $OpenBSD: spawn.c,v 1.52 2026/08/20 09:19:24 nicm Exp $ */
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2019 Nicholas Marriott <nicholas.marriott@gmail.com>
|
* Copyright (c) 2019 Nicholas Marriott <nicholas.marriott@gmail.com>
|
||||||
@@ -243,7 +243,7 @@ struct window_pane *
|
|||||||
spawn_pane(struct spawn_context *sc, char **cause)
|
spawn_pane(struct spawn_context *sc, char **cause)
|
||||||
{
|
{
|
||||||
struct cmdq_item *item = sc->item;
|
struct cmdq_item *item = sc->item;
|
||||||
struct client *c;
|
struct client *c, *loop;
|
||||||
struct session *s = sc->s;
|
struct session *s = sc->s;
|
||||||
struct session *ts;
|
struct session *ts;
|
||||||
struct window *w = sc->wl->window;
|
struct window *w = sc->wl->window;
|
||||||
@@ -331,6 +331,20 @@ spawn_pane(struct spawn_context *sc, char **cause)
|
|||||||
input_free(sc->wp0->ictx);
|
input_free(sc->wp0->ictx);
|
||||||
sc->wp0->ictx = NULL;
|
sc->wp0->ictx = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The old buffer is gone and the new one starts empty, so
|
||||||
|
* offsets into the old buffer no longer mean anything. Reset
|
||||||
|
* them, and drop output control clients still had queued.
|
||||||
|
*/
|
||||||
|
sc->wp0->offset.used = 0;
|
||||||
|
sc->wp0->base_offset = 0;
|
||||||
|
sc->wp0->pipe_offset.used = 0;
|
||||||
|
TAILQ_FOREACH(loop, &clients, entry) {
|
||||||
|
if (loop->flags & CLIENT_CONTROL)
|
||||||
|
control_reset_pane(loop, sc->wp0);
|
||||||
|
}
|
||||||
|
|
||||||
new_wp = sc->wp0;
|
new_wp = sc->wp0;
|
||||||
new_wp->flags &= ~(PANE_STATUSREADY|PANE_STATUSDRAWN);
|
new_wp->flags &= ~(PANE_STATUSREADY|PANE_STATUSDRAWN);
|
||||||
} else {
|
} else {
|
||||||
@@ -346,6 +360,8 @@ spawn_pane(struct spawn_context *sc, char **cause)
|
|||||||
}
|
}
|
||||||
if (sc->flags & SPAWN_FLOATING)
|
if (sc->flags & SPAWN_FLOATING)
|
||||||
new_wp->layout_cell->flags |= LAYOUT_CELL_FLOATING;
|
new_wp->layout_cell->flags |= LAYOUT_CELL_FLOATING;
|
||||||
|
if (sc->flags & SPAWN_FLOATOVERZOOM)
|
||||||
|
new_wp->flags |= PANE_FLOATOVERZOOM;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* If window currently zoomed, window_set_active_pane calls
|
* If window currently zoomed, window_set_active_pane calls
|
||||||
@@ -731,10 +747,10 @@ spawn_editor(struct client *c, const char *buf, size_t len,
|
|||||||
lg.sy = w->sy * 9 / 10;
|
lg.sy = w->sy * 9 / 10;
|
||||||
lg.xoff = w->sx / 2 - lg.sx / 2;
|
lg.xoff = w->sx / 2 - lg.sx / 2;
|
||||||
lg.yoff = w->sy / 2 - lg.sy / 2;
|
lg.yoff = w->sy / 2 - lg.sy / 2;
|
||||||
window_push_modal_zoom(w);
|
window_push_zoom(w, 0, 1);
|
||||||
lc = layout_floating_pane(w, NULL, &lg);
|
lc = layout_floating_pane(w, NULL, &lg);
|
||||||
if (lc == NULL) {
|
if (lc == NULL) {
|
||||||
window_pop_modal_zoom(w);
|
window_pop_zoom(w);
|
||||||
spawn_editor_free(es);
|
spawn_editor_free(es);
|
||||||
return (NULL);
|
return (NULL);
|
||||||
}
|
}
|
||||||
@@ -751,17 +767,18 @@ spawn_editor(struct client *c, const char *buf, size_t len,
|
|||||||
sc.environ = env;
|
sc.environ = env;
|
||||||
sc.idx = -1;
|
sc.idx = -1;
|
||||||
sc.cwd = _PATH_TMP;
|
sc.cwd = _PATH_TMP;
|
||||||
sc.flags = SPAWN_FLOATING|SPAWN_MODAL;
|
sc.flags = SPAWN_FLOATING|SPAWN_MODAL|SPAWN_FLOATOVERZOOM;
|
||||||
|
|
||||||
wp = spawn_pane(&sc, &cause);
|
wp = spawn_pane(&sc, &cause);
|
||||||
free(cmd);
|
free(cmd);
|
||||||
environ_free(env);
|
environ_free(env);
|
||||||
if (wp == NULL) {
|
if (wp == NULL) {
|
||||||
free(cause);
|
free(cause);
|
||||||
window_pop_modal_zoom(w);
|
window_pop_zoom(w);
|
||||||
spawn_editor_free(es);
|
spawn_editor_free(es);
|
||||||
return (NULL);
|
return (NULL);
|
||||||
}
|
}
|
||||||
|
window_pop_zoom(w);
|
||||||
options_set_number(wp->options, "remain-on-exit", 0);
|
options_set_number(wp->options, "remain-on-exit", 0);
|
||||||
es->pid = wp->pid;
|
es->pid = wp->pid;
|
||||||
wp->editor = es;
|
wp->editor = es;
|
||||||
|
|||||||
6
style.c
6
style.c
@@ -30,11 +30,7 @@
|
|||||||
|
|
||||||
/* Default style. */
|
/* Default style. */
|
||||||
static struct style style_default = {
|
static struct style style_default = {
|
||||||
{ { { ' ' }, 0, 1, 1 }, 0, 0, 8, 8, 0, 0
|
{ { { ' ' }, 0, 1, 1 }, 0, 0, 8, 8, 0, 0 },
|
||||||
#ifdef ENABLE_IMAGES
|
|
||||||
, 0, 0, 0
|
|
||||||
#endif
|
|
||||||
},
|
|
||||||
0,
|
0,
|
||||||
0,
|
0,
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
/* $OpenBSD: tmux-protocol.h,v 1.2 2023/01/06 07:09:27 nicm Exp $ */
|
/* $OpenBSD: tmux-protocol.h,v 1.3 2026/08/17 07:56:56 nicm Exp $ */
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2021 Nicholas Marriott <nicholas.marriott@gmail.com>
|
* Copyright (c) 2021 Nicholas Marriott <nicholas.marriott@gmail.com>
|
||||||
@@ -67,7 +67,8 @@ enum msgtype {
|
|||||||
MSG_WRITE,
|
MSG_WRITE,
|
||||||
MSG_WRITE_READY,
|
MSG_WRITE_READY,
|
||||||
MSG_WRITE_CLOSE,
|
MSG_WRITE_CLOSE,
|
||||||
MSG_READ_CANCEL
|
MSG_READ_CANCEL,
|
||||||
|
MSG_WRITE_DONE
|
||||||
};
|
};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -116,4 +117,9 @@ struct msg_write_close {
|
|||||||
int stream;
|
int stream;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct msg_write_done {
|
||||||
|
int stream;
|
||||||
|
int error;
|
||||||
|
};
|
||||||
|
|
||||||
#endif /* TMUX_PROTOCOL_H */
|
#endif /* TMUX_PROTOCOL_H */
|
||||||
|
|||||||
94
tmux.1
94
tmux.1
@@ -1,4 +1,4 @@
|
|||||||
.\" $OpenBSD: tmux.1,v 1.1152 2026/07/29 08:58:33 nicm Exp $
|
.\" $OpenBSD: tmux.1,v 1.1163 2026/09/01 12:49:49 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: July 29 2026 $
|
.Dd $Mdocdate: September 1 2026 $
|
||||||
.Dt TMUX 1
|
.Dt TMUX 1
|
||||||
.Os
|
.Os
|
||||||
.Sh NAME
|
.Sh NAME
|
||||||
@@ -356,6 +356,8 @@ Force redraw of the attached client.
|
|||||||
Select a new session for the attached client interactively.
|
Select a new session for the attached client interactively.
|
||||||
.It t
|
.It t
|
||||||
Show the time.
|
Show the time.
|
||||||
|
.It T
|
||||||
|
Change the current pane title.
|
||||||
.It w
|
.It w
|
||||||
Choose the current window interactively.
|
Choose the current window interactively.
|
||||||
.It x
|
.It x
|
||||||
@@ -2206,14 +2208,14 @@ Move to the end of the next word.
|
|||||||
.Xc
|
.Xc
|
||||||
Same as
|
Same as
|
||||||
.Ic next\-word
|
.Ic next\-word
|
||||||
but use a space alone as the word separator.
|
but treat all non-whitespace characters as part of a word.
|
||||||
.It Xo
|
.It Xo
|
||||||
.Ic next\-space\-end
|
.Ic next\-space\-end
|
||||||
(vi: E)
|
(vi: E)
|
||||||
.Xc
|
.Xc
|
||||||
Same as
|
Same as
|
||||||
.Ic next\-word\-end
|
.Ic next\-word\-end
|
||||||
but use a space alone as the word separator.
|
but treat all non-whitespace characters as part of a word.
|
||||||
.It Xo
|
.It Xo
|
||||||
.Ic other\-end
|
.Ic other\-end
|
||||||
(vi: o)
|
(vi: o)
|
||||||
@@ -2287,7 +2289,7 @@ Move to the previous word.
|
|||||||
.Xc
|
.Xc
|
||||||
Same as
|
Same as
|
||||||
.Ic previous\-word
|
.Ic previous\-word
|
||||||
but use a space alone as the word separator.
|
but treat all non-whitespace characters as part of a word.
|
||||||
.It Xo
|
.It Xo
|
||||||
.Ic rectangle\-on
|
.Ic rectangle\-on
|
||||||
.Xc
|
.Xc
|
||||||
@@ -2319,10 +2321,14 @@ the bottom and is paused while a selection is in progress.
|
|||||||
.Xc
|
.Xc
|
||||||
Turn off automatic refresh of the content from the pane.
|
Turn off automatic refresh of the content from the pane.
|
||||||
.It Xo
|
.It Xo
|
||||||
.Ic refresh\-toggle
|
.Ic refresh\-now
|
||||||
(vi: r)
|
(vi: r)
|
||||||
(emacs: r)
|
(emacs: r)
|
||||||
.Xc
|
.Xc
|
||||||
|
Refresh the content from the pane once.
|
||||||
|
.It Xo
|
||||||
|
.Ic refresh\-toggle
|
||||||
|
.Xc
|
||||||
Toggle automatic refresh of the content from the pane.
|
Toggle automatic refresh of the content from the pane.
|
||||||
.It Xo
|
.It Xo
|
||||||
.Ic scroll\-bottom
|
.Ic scroll\-bottom
|
||||||
@@ -2568,8 +2574,8 @@ Word separators can be customized with the
|
|||||||
session option.
|
session option.
|
||||||
Next word moves to the start of the next word, next word end to the end of the
|
Next word moves to the start of the next word, next word end to the end of the
|
||||||
next word and previous word to the start of the previous word.
|
next word and previous word to the start of the previous word.
|
||||||
The three next and previous space keys work similarly but use a space alone as
|
The three next and previous space keys work similarly but treat all
|
||||||
the word separator.
|
non-whitespace characters as part of a word.
|
||||||
Setting
|
Setting
|
||||||
.Em word\-separators
|
.Em word\-separators
|
||||||
to the empty string makes next/previous word equivalent to next/previous space.
|
to the empty string makes next/previous word equivalent to next/previous space.
|
||||||
@@ -3638,7 +3644,7 @@ but a different format may be specified with
|
|||||||
.Fl F .
|
.Fl F .
|
||||||
.Tg newp
|
.Tg newp
|
||||||
.It Xo Ic new\-pane
|
.It Xo Ic new\-pane
|
||||||
.Op Fl bCdefhIkLMOPvWZ
|
.Op Fl AbCdefhIkKLMOPvWZ
|
||||||
.Op Fl B Ar border\-lines
|
.Op Fl B Ar border\-lines
|
||||||
.Op Fl c Ar start\-directory
|
.Op Fl c Ar start\-directory
|
||||||
.Op Fl e Ar environment
|
.Op Fl e Ar environment
|
||||||
@@ -3687,14 +3693,17 @@ sets the pane border lines for floating panes; see
|
|||||||
.Fl M
|
.Fl M
|
||||||
may be used when bound to a mouse drag key; the new floating pane is resized to
|
may be used when bound to a mouse drag key; the new floating pane is resized to
|
||||||
follow the mouse drag.
|
follow the mouse drag.
|
||||||
It has no effect with
|
.Fl A
|
||||||
.Fl L .
|
creates a floating pane which remains visible above a zoomed pane.
|
||||||
.Fl O
|
.Fl O
|
||||||
creates a modal pane.
|
creates a modal pane.
|
||||||
A modal pane is always the active pane and prevents interaction with any other
|
A modal pane is always the active pane and prevents interaction with any other
|
||||||
panes while it is active.
|
panes while it is active.
|
||||||
A window can only have one modal pane and it must be a floating pane.
|
A window can only have one modal pane and it must be a floating pane.
|
||||||
With
|
With
|
||||||
|
.Fl K ,
|
||||||
|
all keys, including the prefix key, are passed directly to the modal pane.
|
||||||
|
With
|
||||||
.Fl C ,
|
.Fl C ,
|
||||||
the modal pane is closed when the mouse is clicked outside it.
|
the modal pane is closed when the mouse is clicked outside it.
|
||||||
.Pp
|
.Pp
|
||||||
@@ -3984,7 +3993,7 @@ applies the last set layout if possible (undoes the most recent layout change).
|
|||||||
spreads the current pane and any panes next to it out evenly.
|
spreads the current pane and any panes next to it out evenly.
|
||||||
.Tg selectp
|
.Tg selectp
|
||||||
.It Xo Ic select\-pane
|
.It Xo Ic select\-pane
|
||||||
.Op Fl DdeLlMmRUZ
|
.Op Fl ADdeLlMmRsSUZ
|
||||||
.Op Fl T Ar title
|
.Op Fl T Ar title
|
||||||
.Op Fl t Ar target\-pane
|
.Op Fl t Ar target\-pane
|
||||||
.Xc
|
.Xc
|
||||||
@@ -3992,6 +4001,8 @@ spreads the current pane and any panes next to it out evenly.
|
|||||||
Make pane
|
Make pane
|
||||||
.Ar target\-pane
|
.Ar target\-pane
|
||||||
the active pane in its window.
|
the active pane in its window.
|
||||||
|
.Fl A
|
||||||
|
raises the pane if it is floating.
|
||||||
If one of
|
If one of
|
||||||
.Fl D ,
|
.Fl D ,
|
||||||
.Fl L ,
|
.Fl L ,
|
||||||
@@ -4006,6 +4017,15 @@ keeps the window zoomed if it was zoomed.
|
|||||||
is the same as using the
|
is the same as using the
|
||||||
.Ic last\-pane
|
.Ic last\-pane
|
||||||
command.
|
command.
|
||||||
|
.Fl s
|
||||||
|
switches the client to local pane selection for the target window when more
|
||||||
|
than one client is attached; the pane currently active for the client is
|
||||||
|
retained.
|
||||||
|
Local pane selection is disabled automatically when fewer than two clients
|
||||||
|
remain attached.
|
||||||
|
.Fl S
|
||||||
|
switches the client back to shared pane selection for the target window, so the
|
||||||
|
window's active pane is used.
|
||||||
.Fl e
|
.Fl e
|
||||||
enables or
|
enables or
|
||||||
.Fl d
|
.Fl d
|
||||||
@@ -4029,12 +4049,22 @@ and
|
|||||||
.Ic swap\-window .
|
.Ic swap\-window .
|
||||||
.Tg selectw
|
.Tg selectw
|
||||||
.It Xo Ic select\-window
|
.It Xo Ic select\-window
|
||||||
.Op Fl lnpT
|
.Op Fl LlnpST
|
||||||
.Op Fl t Ar target\-window
|
.Op Fl t Ar target\-window
|
||||||
.Xc
|
.Xc
|
||||||
.D1 Pq alias: Ic selectw
|
.D1 Pq alias: Ic selectw
|
||||||
Select the window at
|
Select the window at
|
||||||
.Ar target\-window .
|
.Ar target\-window .
|
||||||
|
.Fl L
|
||||||
|
makes the attached client use a local current window for the target session.
|
||||||
|
Window navigation from that client then changes only that client's effective
|
||||||
|
window and leaves the session current window unchanged.
|
||||||
|
.Fl S
|
||||||
|
returns the client to the shared session current window.
|
||||||
|
Without
|
||||||
|
.Fl L ,
|
||||||
|
the current window is shared by the session and stored in the session.
|
||||||
|
.Pp
|
||||||
.Fl l ,
|
.Fl l ,
|
||||||
.Fl n
|
.Fl n
|
||||||
and
|
and
|
||||||
@@ -4745,6 +4775,15 @@ for backspace.
|
|||||||
Set the number of buffers; as new buffers are added to the top of the stack,
|
Set the number of buffers; as new buffers are added to the top of the stack,
|
||||||
old ones are removed from the bottom if necessary to maintain this maximum
|
old ones are removed from the bottom if necessary to maintain this maximum
|
||||||
length.
|
length.
|
||||||
|
.It Xo Ic clear\-on\-attach
|
||||||
|
.Op Ic on | off
|
||||||
|
.Xc
|
||||||
|
When enabled (the default), the client terminal switches to the alternate
|
||||||
|
screen and clears it when a client is attached.
|
||||||
|
When disabled,
|
||||||
|
.Nm
|
||||||
|
does not enter the alternate screen; instead the existing terminal content is
|
||||||
|
scrolled into the scrollback buffer so it remains accessible.
|
||||||
.It Xo Ic command\-alias[]
|
.It Xo Ic command\-alias[]
|
||||||
.Ar name=value
|
.Ar name=value
|
||||||
.Xc
|
.Xc
|
||||||
@@ -5041,6 +5080,10 @@ This is an array option where each entry is a colon-separated string made up
|
|||||||
of a terminal type pattern (matched using
|
of a terminal type pattern (matched using
|
||||||
.Xr glob 7
|
.Xr glob 7
|
||||||
patterns) followed by a list of terminal features.
|
patterns) followed by a list of terminal features.
|
||||||
|
A feature may be suffixed with
|
||||||
|
.Ql @
|
||||||
|
to disable it; for example,
|
||||||
|
.Ql xterm*:sync@ .
|
||||||
The available features are:
|
The available features are:
|
||||||
.Bl -tag -width Ds
|
.Bl -tag -width Ds
|
||||||
.It 256
|
.It 256
|
||||||
@@ -5057,10 +5100,6 @@ Supports extended keys.
|
|||||||
Supports focus reporting.
|
Supports focus reporting.
|
||||||
.It hyperlinks
|
.It hyperlinks
|
||||||
Supports OSC 8 hyperlinks.
|
Supports OSC 8 hyperlinks.
|
||||||
.It image-quadrants
|
|
||||||
Allows image fallbacks to use Unicode quadrant block characters.
|
|
||||||
.It image-sextants
|
|
||||||
Allows image fallbacks to use Unicode sextant block characters.
|
|
||||||
.It ignorefkeys
|
.It ignorefkeys
|
||||||
Ignore function keys from
|
Ignore function keys from
|
||||||
.Xr terminfo 5
|
.Xr terminfo 5
|
||||||
@@ -5095,6 +5134,8 @@ Supports
|
|||||||
title setting.
|
title setting.
|
||||||
.It usstyle
|
.It usstyle
|
||||||
Allows underscore style and colour to be set.
|
Allows underscore style and colour to be set.
|
||||||
|
.It utf8
|
||||||
|
Supports UTF\-8 output.
|
||||||
.El
|
.El
|
||||||
.It Ic terminal\-overrides[] Ar string
|
.It Ic terminal\-overrides[] Ar string
|
||||||
Allow terminal descriptions read using
|
Allow terminal descriptions read using
|
||||||
@@ -5629,6 +5670,11 @@ If set to both, a bell and a message are produced.
|
|||||||
Sets the session's conception of what characters are considered word
|
Sets the session's conception of what characters are considered word
|
||||||
separators, for the purposes of the next and previous word commands in
|
separators, for the purposes of the next and previous word commands in
|
||||||
copy mode.
|
copy mode.
|
||||||
|
A space in
|
||||||
|
.Ar string
|
||||||
|
matches any character with the Unicode
|
||||||
|
.Em White_Space
|
||||||
|
property.
|
||||||
.El
|
.El
|
||||||
.Pp
|
.Pp
|
||||||
Available window options are:
|
Available window options are:
|
||||||
@@ -7241,7 +7287,10 @@ shows the four frames in turn, changing every 200 milliseconds.
|
|||||||
The following variables are available, where appropriate:
|
The following variables are available, where appropriate:
|
||||||
.Bl -column "XXXXXXXXXXXXXXXXXXX" "XXXXX"
|
.Bl -column "XXXXXXXXXXXXXXXXXXX" "XXXXX"
|
||||||
.It Sy "Variable name" Ta Sy "Alias" Ta Sy "Replaced with"
|
.It Sy "Variable name" Ta Sy "Alias" Ta Sy "Replaced with"
|
||||||
.It Li "active_window_index" Ta "" Ta "Index of active window in session"
|
.It Li "active_window_id" Ta "" Ta "ID of effective active window"
|
||||||
|
.It Li "active_window_index" Ta "" Ta "Index of effective active window"
|
||||||
|
.It Li "local_active_window" Ta "" Ta "1 if client has local window selection"
|
||||||
|
.It Li "active_window_mode" Ta "" Ta "Window selection mode, shared or local"
|
||||||
.It Li "alternate_on" Ta "" Ta "1 if pane is in alternate screen"
|
.It Li "alternate_on" Ta "" Ta "1 if pane is in alternate screen"
|
||||||
.It Li "alternate_saved_x" Ta "" Ta "Saved cursor X in alternate screen"
|
.It Li "alternate_saved_x" Ta "" Ta "Saved cursor X in alternate screen"
|
||||||
.It Li "alternate_saved_y" Ta "" Ta "Saved cursor Y in alternate screen"
|
.It Li "alternate_saved_y" Ta "" Ta "Saved cursor Y in alternate screen"
|
||||||
@@ -7298,7 +7347,10 @@ The following variables are available, where appropriate:
|
|||||||
.It Li "cursor_very_visible" Ta "" Ta "1 if the cursor is in very visible mode"
|
.It Li "cursor_very_visible" Ta "" Ta "1 if the cursor is in very visible mode"
|
||||||
.It Li "cursor_x" Ta "" Ta "Cursor X position in pane"
|
.It Li "cursor_x" Ta "" Ta "Cursor X position in pane"
|
||||||
.It Li "cursor_y" Ta "" Ta "Cursor Y position in pane"
|
.It Li "cursor_y" Ta "" Ta "Cursor Y position in pane"
|
||||||
|
.It Li "history_added" Ta "" Ta "Number of lines scrolled into pane history"
|
||||||
.It Li "history_bytes" Ta "" Ta "Number of bytes in window history"
|
.It Li "history_bytes" Ta "" Ta "Number of bytes in window history"
|
||||||
|
.It Li "history_collected" Ta "" Ta "Number of oldest pane history lines discarded at the history limit"
|
||||||
|
.It Li "history_generation" Ta "" Ta "Generation incremented when pane history is cleared or reflowed"
|
||||||
.It Li "history_limit" Ta "" Ta "Maximum window history lines"
|
.It Li "history_limit" Ta "" Ta "Maximum window history lines"
|
||||||
.It Li "history_size" Ta "" Ta "Size of history in lines"
|
.It Li "history_size" Ta "" Ta "Size of history in lines"
|
||||||
.It Li "hook" Ta "" Ta "Name of running hook, if any"
|
.It Li "hook" Ta "" Ta "Name of running hook, if any"
|
||||||
@@ -7392,15 +7444,19 @@ The following variables are available, where appropriate:
|
|||||||
.It Li "pane_last" Ta "" Ta "1 if last pane"
|
.It Li "pane_last" Ta "" Ta "1 if last pane"
|
||||||
.It Li "pane_last_output_time" Ta "" Ta "Time pane last produced output"
|
.It Li "pane_last_output_time" Ta "" Ta "Time pane last produced output"
|
||||||
.It Li "pane_last_prompt_time" Ta "" Ta "Time most recent OSC 133 prompt began"
|
.It Li "pane_last_prompt_time" Ta "" Ta "Time most recent OSC 133 prompt began"
|
||||||
|
.It Li "pane_local_active" Ta "" Ta "1 if active pane is client-local"
|
||||||
|
.It Li "pane_local_mode" Ta "" Ta "1 if client has local pane selection"
|
||||||
.It Li "pane_left" Ta "" Ta "Left of pane"
|
.It Li "pane_left" Ta "" Ta "Left of pane"
|
||||||
.It Li "pane_marked" Ta "" Ta "1 if this is the marked pane"
|
.It Li "pane_marked" Ta "" Ta "1 if this is the marked pane"
|
||||||
.It Li "pane_marked_set" Ta "" Ta "1 if a marked pane is set"
|
.It Li "pane_marked_set" Ta "" Ta "1 if a marked pane is set"
|
||||||
.It Li "pane_modal_flag" Ta "" Ta "1 if pane is modal"
|
.It Li "pane_modal_flag" Ta "" Ta "1 if pane is modal"
|
||||||
.It Li "pane_mode" Ta "" Ta "Name of pane mode, if any"
|
.It Li "pane_mode" Ta "" Ta "Name of pane mode, if any"
|
||||||
|
.It Li "pane_output_generation" Ta "" Ta "Generation incremented when pane output is processed"
|
||||||
.It Li "pane_path" Ta "" Ta "Path of pane (can be set by application)"
|
.It Li "pane_path" Ta "" Ta "Path of pane (can be set by application)"
|
||||||
.It Li "pane_pid" Ta "" Ta "PID of first process in pane"
|
.It Li "pane_pid" Ta "" Ta "PID of first process in pane"
|
||||||
.It Li "pane_pipe" Ta "" Ta "1 if pane is being piped"
|
.It Li "pane_pipe" Ta "" Ta "1 if pane is being piped"
|
||||||
.It Li "pane_pipe_pid" Ta "" Ta "PID of pipe process, if any"
|
.It Li "pane_pipe_pid" Ta "" Ta "PID of pipe process, if any"
|
||||||
|
.It Li "pane_private_modes" Ta "" Ta "List of enabled DECSET private modes"
|
||||||
.It Li "pane_pb_state" Ta "" Ta "Pane progress bar state, one of hidden, normal, error, indeterminate, paused (can be set by application)"
|
.It Li "pane_pb_state" Ta "" Ta "Pane progress bar state, one of hidden, normal, error, indeterminate, paused (can be set by application)"
|
||||||
.It Li "pane_pb_progress" Ta "" Ta "Pane progress bar progress percentage (can be set by application)"
|
.It Li "pane_pb_progress" Ta "" Ta "Pane progress bar progress percentage (can be set by application)"
|
||||||
.It Li "pane_right" Ta "" Ta "Right of pane"
|
.It Li "pane_right" Ta "" Ta "Right of pane"
|
||||||
@@ -7473,7 +7529,7 @@ The following variables are available, where appropriate:
|
|||||||
.It Li "uid" Ta "" Ta "Server UID"
|
.It Li "uid" Ta "" Ta "Server UID"
|
||||||
.It Li "user" Ta "" Ta "Server user"
|
.It Li "user" Ta "" Ta "Server user"
|
||||||
.It Li "version" Ta "" Ta "Server version"
|
.It Li "version" Ta "" Ta "Server version"
|
||||||
.It Li "window_active" Ta "" Ta "1 if window active"
|
.It Li "window_active" Ta "" Ta "1 if window is the effective active window"
|
||||||
.It Li "window_active_clients" Ta "" Ta "Number of clients viewing this window"
|
.It Li "window_active_clients" Ta "" Ta "Number of clients viewing this window"
|
||||||
.It Li "window_active_clients_list" Ta "" Ta "List of clients viewing this window"
|
.It Li "window_active_clients_list" Ta "" Ta "List of clients viewing this window"
|
||||||
.It Li "window_active_sessions" Ta "" Ta "Number of sessions on which this window is active"
|
.It Li "window_active_sessions" Ta "" Ta "Number of sessions on which this window is active"
|
||||||
|
|||||||
6
tmux.c
6
tmux.c
@@ -1,4 +1,4 @@
|
|||||||
/* $OpenBSD: tmux.c,v 1.222 2026/07/19 19:09:30 nicm Exp $ */
|
/* $OpenBSD: tmux.c,v 1.223 2026/08/17 14:47:41 nicm Exp $ */
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>
|
* Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>
|
||||||
@@ -425,7 +425,7 @@ main(int argc, char **argv)
|
|||||||
while ((opt = getopt(argc, argv, "2c:CDdf:hlL:NqS:T:uUvV")) != -1) {
|
while ((opt = getopt(argc, argv, "2c:CDdf:hlL:NqS:T:uUvV")) != -1) {
|
||||||
switch (opt) {
|
switch (opt) {
|
||||||
case '2':
|
case '2':
|
||||||
tty_add_features(&feat, "256", ":,");
|
tty_parse_features("256", ":,", &feat, NULL);
|
||||||
break;
|
break;
|
||||||
case 'c':
|
case 'c':
|
||||||
shell_command = optarg;
|
shell_command = optarg;
|
||||||
@@ -473,7 +473,7 @@ main(int argc, char **argv)
|
|||||||
path = xstrdup(optarg);
|
path = xstrdup(optarg);
|
||||||
break;
|
break;
|
||||||
case 'T':
|
case 'T':
|
||||||
tty_add_features(&feat, optarg, ":,");
|
tty_parse_features(optarg, ":,", &feat, NULL);
|
||||||
break;
|
break;
|
||||||
case 'u':
|
case 'u':
|
||||||
flags |= CLIENT_UTF8;
|
flags |= CLIENT_UTF8;
|
||||||
|
|||||||
244
tmux.h
244
tmux.h
@@ -1,4 +1,4 @@
|
|||||||
/* $OpenBSD: tmux.h,v 1.1418 2026/07/29 17:42:56 nicm Exp $ */
|
/* $OpenBSD: tmux.h,v 1.1433 2026/09/01 12:49:49 nicm Exp $ */
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>
|
* Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>
|
||||||
@@ -75,13 +75,7 @@ struct screen_write_cline;
|
|||||||
struct screen_write_ctx;
|
struct screen_write_ctx;
|
||||||
struct session;
|
struct session;
|
||||||
|
|
||||||
#ifdef ENABLE_IMAGES
|
#ifdef ENABLE_SIXEL
|
||||||
struct image;
|
|
||||||
struct image_backend;
|
|
||||||
struct image_fallback_data;
|
|
||||||
struct image_rect;
|
|
||||||
#endif
|
|
||||||
#ifdef ENABLE_IMAGES
|
|
||||||
struct sixel_image;
|
struct sixel_image;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@@ -489,6 +483,7 @@ enum tty_code_code {
|
|||||||
TTYC_ICH1,
|
TTYC_ICH1,
|
||||||
TTYC_IL,
|
TTYC_IL,
|
||||||
TTYC_IL1,
|
TTYC_IL1,
|
||||||
|
TTYC_IND,
|
||||||
TTYC_INDN,
|
TTYC_INDN,
|
||||||
TTYC_INVIS,
|
TTYC_INVIS,
|
||||||
TTYC_KCBT,
|
TTYC_KCBT,
|
||||||
@@ -809,8 +804,6 @@ struct colour_palette {
|
|||||||
#define GRID_FLAG_NOPALETTE 0x20
|
#define GRID_FLAG_NOPALETTE 0x20
|
||||||
#define GRID_FLAG_CLEARED 0x40
|
#define GRID_FLAG_CLEARED 0x40
|
||||||
#define GRID_FLAG_TAB 0x80
|
#define GRID_FLAG_TAB 0x80
|
||||||
#define GRID_FLAG_IMAGE 0x100
|
|
||||||
#define GRID_FLAG_IMAGE_DAMAGED 0x200
|
|
||||||
|
|
||||||
/* Grid line flags. */
|
/* Grid line flags. */
|
||||||
#define GRID_LINE_WRAPPED 0x1
|
#define GRID_LINE_WRAPPED 0x1
|
||||||
@@ -866,32 +859,22 @@ struct colour_palette {
|
|||||||
struct grid_cell {
|
struct grid_cell {
|
||||||
struct utf8_data data;
|
struct utf8_data data;
|
||||||
u_short attr;
|
u_short attr;
|
||||||
u_short flags;
|
u_char flags;
|
||||||
int fg;
|
int fg;
|
||||||
int bg;
|
int bg;
|
||||||
int us;
|
int us;
|
||||||
u_int link;
|
u_int link;
|
||||||
#ifdef ENABLE_IMAGES
|
|
||||||
u_int image_id;
|
|
||||||
u_int image_x;
|
|
||||||
u_int image_y;
|
|
||||||
#endif
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/* Grid extended cell entry. */
|
/* Grid extended cell entry. */
|
||||||
struct grid_extd_entry {
|
struct grid_extd_entry {
|
||||||
utf8_char data;
|
utf8_char data;
|
||||||
u_short attr;
|
u_short attr;
|
||||||
u_short flags;
|
u_char flags;
|
||||||
int fg;
|
int fg;
|
||||||
int bg;
|
int bg;
|
||||||
int us;
|
int us;
|
||||||
u_int link;
|
u_int link;
|
||||||
#ifdef ENABLE_IMAGES
|
|
||||||
u_short image_id;
|
|
||||||
u_short image_x;
|
|
||||||
u_short image_y;
|
|
||||||
#endif
|
|
||||||
} __packed;
|
} __packed;
|
||||||
|
|
||||||
/* Grid cell entry. */
|
/* Grid cell entry. */
|
||||||
@@ -905,7 +888,7 @@ struct grid_cell_entry {
|
|||||||
u_char data;
|
u_char data;
|
||||||
} data;
|
} data;
|
||||||
};
|
};
|
||||||
u_short flags;
|
u_char flags;
|
||||||
} __packed;
|
} __packed;
|
||||||
|
|
||||||
/* OSC 133 data for a grid line. */
|
/* OSC 133 data for a grid line. */
|
||||||
@@ -1039,30 +1022,24 @@ struct style {
|
|||||||
u_int link;
|
u_int link;
|
||||||
};
|
};
|
||||||
|
|
||||||
#ifdef ENABLE_IMAGES
|
#ifdef ENABLE_SIXEL
|
||||||
/* Private ACS keys used by the text image backend. */
|
/* Image. */
|
||||||
#define TTY_ACS_IMAGE_SHADE_LIGHT 0x80
|
struct image {
|
||||||
#define TTY_ACS_IMAGE_SHADE_MEDIUM 0x81
|
struct screen *s;
|
||||||
#define TTY_ACS_IMAGE_SHADE_DARK 0x82
|
struct sixel_image *data;
|
||||||
#define TTY_ACS_IMAGE_BLOCK 0x83
|
char *fallback;
|
||||||
#define TTY_ACS_IMAGE_HALF_UPPER 0x84
|
|
||||||
#define TTY_ACS_IMAGE_HALF_LOWER 0x85
|
|
||||||
#define TTY_ACS_IMAGE_HALF_LEFT 0x86
|
|
||||||
#define TTY_ACS_IMAGE_HALF_RIGHT 0x87
|
|
||||||
#define TTY_ACS_IMAGE_QUADRANT_LOWER_LEFT 0x88
|
|
||||||
#define TTY_ACS_IMAGE_QUADRANT_LOWER_RIGHT 0x89
|
|
||||||
#define TTY_ACS_IMAGE_QUADRANT_UPPER_LEFT 0x8a
|
|
||||||
#define TTY_ACS_IMAGE_QUADRANT_UPPER_LEFT_LOWER_LEFT_LOWER_RIGHT 0x8b
|
|
||||||
#define TTY_ACS_IMAGE_QUADRANT_UPPER_LEFT_LOWER_RIGHT 0x8c
|
|
||||||
#define TTY_ACS_IMAGE_QUADRANT_UPPER_LEFT_UPPER_RIGHT_LOWER_LEFT 0x8d
|
|
||||||
#define TTY_ACS_IMAGE_QUADRANT_UPPER_LEFT_UPPER_RIGHT_LOWER_RIGHT 0x8e
|
|
||||||
#define TTY_ACS_IMAGE_QUADRANT_UPPER_RIGHT 0x8f
|
|
||||||
#define TTY_ACS_IMAGE_QUADRANT_UPPER_RIGHT_LOWER_LEFT 0x90
|
|
||||||
#define TTY_ACS_IMAGE_QUADRANT_UPPER_RIGHT_LOWER_LEFT_LOWER_RIGHT 0x91
|
|
||||||
#define TTY_ACS_IMAGE_SEXTANT_FIRST 0x92
|
|
||||||
#define TTY_ACS_IMAGE_SEXTANT_LAST 0xcd
|
|
||||||
|
|
||||||
#define IMAGE_SIZE_LIMIT (64 * 1024 * 1024)
|
u_int px;
|
||||||
|
u_int py;
|
||||||
|
u_int sx;
|
||||||
|
u_int sy;
|
||||||
|
|
||||||
|
struct images *list;
|
||||||
|
TAILQ_ENTRY (image) entry;
|
||||||
|
|
||||||
|
TAILQ_ENTRY (image) all_entry;
|
||||||
|
};
|
||||||
|
TAILQ_HEAD(images, image);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/* Cursor style. */
|
/* Cursor style. */
|
||||||
@@ -1121,6 +1098,11 @@ struct screen {
|
|||||||
bitstr_t *tabs;
|
bitstr_t *tabs;
|
||||||
struct screen_sel *sel;
|
struct screen_sel *sel;
|
||||||
|
|
||||||
|
#ifdef ENABLE_SIXEL
|
||||||
|
struct images images;
|
||||||
|
struct images saved_images;
|
||||||
|
#endif
|
||||||
|
|
||||||
struct screen_write_cline *write_list;
|
struct screen_write_cline *write_list;
|
||||||
|
|
||||||
struct hyperlinks *hyperlinks;
|
struct hyperlinks *hyperlinks;
|
||||||
@@ -1354,6 +1336,8 @@ struct window_pane {
|
|||||||
#define PANE_CMDRUNNING 0x20000
|
#define PANE_CMDRUNNING 0x20000
|
||||||
#define PANE_ACTIVITY 0x40000
|
#define PANE_ACTIVITY 0x40000
|
||||||
#define PANE_CLOSEONCLICK 0x80000
|
#define PANE_CLOSEONCLICK 0x80000
|
||||||
|
#define PANE_CAPTUREALLKEYS 0x100000
|
||||||
|
#define PANE_FLOATOVERZOOM 0x200000
|
||||||
|
|
||||||
bitstr_t *sync_dirty;
|
bitstr_t *sync_dirty;
|
||||||
u_int sync_dirty_size;
|
u_int sync_dirty_size;
|
||||||
@@ -1373,9 +1357,10 @@ struct window_pane {
|
|||||||
char tty[TTY_NAME_MAX];
|
char tty[TTY_NAME_MAX];
|
||||||
int status;
|
int status;
|
||||||
struct timeval dead_time;
|
struct timeval dead_time;
|
||||||
struct cmdq_item *wait_item; /* new-pane -W: waiting for pane exit */
|
struct cmdq_item *wait_item;
|
||||||
struct spawn_editor_state *editor;
|
struct spawn_editor_state *editor;
|
||||||
|
|
||||||
|
uint64_t output_generation;
|
||||||
time_t last_output_time;
|
time_t last_output_time;
|
||||||
time_t last_prompt_time;
|
time_t last_prompt_time;
|
||||||
time_t cmd_start_time;
|
time_t cmd_start_time;
|
||||||
@@ -1460,6 +1445,7 @@ struct window {
|
|||||||
struct window_pane *active;
|
struct window_pane *active;
|
||||||
struct window_pane *modal;
|
struct window_pane *modal;
|
||||||
struct window_pane *modal_last;
|
struct window_pane *modal_last;
|
||||||
|
struct window_pane *was_zoomed;
|
||||||
struct window_panes last_panes;
|
struct window_panes last_panes;
|
||||||
struct window_panes z_index;
|
struct window_panes z_index;
|
||||||
struct window_panes panes;
|
struct window_panes panes;
|
||||||
@@ -1502,7 +1488,6 @@ struct window {
|
|||||||
#define WINDOW_ZOOMED 0x8
|
#define WINDOW_ZOOMED 0x8
|
||||||
#define WINDOW_WASZOOMED 0x10
|
#define WINDOW_WASZOOMED 0x10
|
||||||
#define WINDOW_RESIZE 0x20
|
#define WINDOW_RESIZE 0x20
|
||||||
#define WINDOW_WASMODALZOOMED 0x40
|
|
||||||
#define WINDOW_ALERTFLAGS (WINDOW_BELL|WINDOW_ACTIVITY|WINDOW_SILENCE)
|
#define WINDOW_ALERTFLAGS (WINDOW_BELL|WINDOW_ACTIVITY|WINDOW_SILENCE)
|
||||||
|
|
||||||
int alerts_queued;
|
int alerts_queued;
|
||||||
@@ -1737,7 +1722,7 @@ struct key_event {
|
|||||||
struct tty_term {
|
struct tty_term {
|
||||||
char *name;
|
char *name;
|
||||||
struct tty *tty;
|
struct tty *tty;
|
||||||
int features;
|
int applied_features;
|
||||||
|
|
||||||
char acs[UCHAR_MAX + 1][2];
|
char acs[UCHAR_MAX + 1][2];
|
||||||
|
|
||||||
@@ -1750,10 +1735,7 @@ struct tty_term {
|
|||||||
#define TERM_RGBCOLOURS 0x10
|
#define TERM_RGBCOLOURS 0x10
|
||||||
#define TERM_VT100LIKE 0x20
|
#define TERM_VT100LIKE 0x20
|
||||||
#define TERM_SIXEL 0x40
|
#define TERM_SIXEL 0x40
|
||||||
#ifdef ENABLE_IMAGES
|
#define TERM_INVALIDMS 0x80
|
||||||
#define TERM_IMAGE_QUADRANTS 0x80
|
|
||||||
#define TERM_IMAGE_SEXTANTS 0x100
|
|
||||||
#endif
|
|
||||||
int flags;
|
int flags;
|
||||||
|
|
||||||
LIST_ENTRY(tty_term) entry;
|
LIST_ENTRY(tty_term) entry;
|
||||||
@@ -1833,8 +1815,9 @@ struct tty {
|
|||||||
#define TTY_WAITFG 0x2000
|
#define TTY_WAITFG 0x2000
|
||||||
#define TTY_WAITBG 0x4000
|
#define TTY_WAITBG 0x4000
|
||||||
#define TTY_BRACKETPASTE 0x8000
|
#define TTY_BRACKETPASTE 0x8000
|
||||||
|
#define TTY_HAVESYNC 0x10000
|
||||||
#define TTY_ALL_REQUEST_FLAGS \
|
#define TTY_ALL_REQUEST_FLAGS \
|
||||||
(TTY_HAVEDA|TTY_HAVEDA2|TTY_HAVEXDA)
|
(TTY_HAVEDA|TTY_HAVEDA2|TTY_HAVEXDA|TTY_HAVESYNC)
|
||||||
int flags;
|
int flags;
|
||||||
|
|
||||||
struct tty_term *term;
|
struct tty_term *term;
|
||||||
@@ -1855,10 +1838,6 @@ struct tty {
|
|||||||
|
|
||||||
struct event key_timer;
|
struct event key_timer;
|
||||||
struct tty_key *key_tree;
|
struct tty_key *key_tree;
|
||||||
#ifdef ENABLE_IMAGES
|
|
||||||
const struct image_backend *image_backend;
|
|
||||||
void *image_data;
|
|
||||||
#endif
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/* Terminal command context. */
|
/* Terminal command context. */
|
||||||
@@ -1895,7 +1874,7 @@ struct tty_ctx {
|
|||||||
size_t size;
|
size_t size;
|
||||||
} sel;
|
} sel;
|
||||||
|
|
||||||
#ifdef ENABLE_IMAGES
|
#ifdef ENABLE_SIXEL
|
||||||
struct image *image;
|
struct image *image;
|
||||||
#endif
|
#endif
|
||||||
};
|
};
|
||||||
@@ -2252,6 +2231,7 @@ struct client {
|
|||||||
|
|
||||||
char *term_name;
|
char *term_name;
|
||||||
int term_features;
|
int term_features;
|
||||||
|
int term_nofeatures;
|
||||||
char *term_type;
|
char *term_type;
|
||||||
char **term_caps;
|
char **term_caps;
|
||||||
u_int term_ncaps;
|
u_int term_ncaps;
|
||||||
@@ -2270,6 +2250,8 @@ struct client {
|
|||||||
struct event click_timer;
|
struct event click_timer;
|
||||||
int click_loc;
|
int click_loc;
|
||||||
int click_wp;
|
int click_wp;
|
||||||
|
|
||||||
|
struct event exit_timer;
|
||||||
u_int click_button;
|
u_int click_button;
|
||||||
struct mouse_event click_event;
|
struct mouse_event click_event;
|
||||||
|
|
||||||
@@ -2310,14 +2292,14 @@ struct client {
|
|||||||
#define CLIENT_STARTSERVER 0x10000000
|
#define CLIENT_STARTSERVER 0x10000000
|
||||||
#define CLIENT_REDRAWMENU 0x20000000
|
#define CLIENT_REDRAWMENU 0x20000000
|
||||||
#define CLIENT_NOFORK 0x40000000
|
#define CLIENT_NOFORK 0x40000000
|
||||||
/* 0x80000000ULL unused */
|
#define CLIENT_REDRAWSCROLLBARS 0x80000000ULL
|
||||||
#define CLIENT_CONTROL_PAUSEAFTER 0x100000000ULL
|
#define CLIENT_CONTROL_PAUSEAFTER 0x100000000ULL
|
||||||
#define CLIENT_CONTROL_WAITEXIT 0x200000000ULL
|
#define CLIENT_CONTROL_WAITEXIT 0x200000000ULL
|
||||||
#define CLIENT_WINDOWSIZECHANGED 0x400000000ULL
|
#define CLIENT_WINDOWSIZECHANGED 0x400000000ULL
|
||||||
/* 0x800000000ULL unused */
|
/* 0x800000000ULL unused */
|
||||||
#define CLIENT_BRACKETPASTING 0x1000000000ULL
|
#define CLIENT_BRACKETPASTING 0x1000000000ULL
|
||||||
#define CLIENT_ASSUMEPASTING 0x2000000000ULL
|
#define CLIENT_ASSUMEPASTING 0x2000000000ULL
|
||||||
/* 0x4000000000ULL unused */
|
#define CLIENT_WRITE_ACK 0x4000000000ULL
|
||||||
#define CLIENT_NO_DETACH_ON_DESTROY 0x8000000000ULL
|
#define CLIENT_NO_DETACH_ON_DESTROY 0x8000000000ULL
|
||||||
#define CLIENT_ALLREDRAWFLAGS \
|
#define CLIENT_ALLREDRAWFLAGS \
|
||||||
(CLIENT_REDRAWWINDOW| \
|
(CLIENT_REDRAWWINDOW| \
|
||||||
@@ -2556,6 +2538,7 @@ struct spawn_context {
|
|||||||
#define SPAWN_HORIZONTAL 0x200
|
#define SPAWN_HORIZONTAL 0x200
|
||||||
#define SPAWN_SPLIT 0x400
|
#define SPAWN_SPLIT 0x400
|
||||||
#define SPAWN_MODAL 0x800
|
#define SPAWN_MODAL 0x800
|
||||||
|
#define SPAWN_FLOATOVERZOOM 0x1000
|
||||||
};
|
};
|
||||||
|
|
||||||
/* Paste buffer. */
|
/* Paste buffer. */
|
||||||
@@ -2794,6 +2777,42 @@ void events_fire_window(const char *, struct window *);
|
|||||||
void events_fire_pane(const char *, struct window_pane *);
|
void events_fire_pane(const char *, struct window_pane *);
|
||||||
void events_fire_winlink(const char *, struct winlink *);
|
void events_fire_winlink(const char *, struct winlink *);
|
||||||
|
|
||||||
|
/* active.c */
|
||||||
|
enum active_mode {
|
||||||
|
ACTIVE_SHARED,
|
||||||
|
ACTIVE_LOCAL
|
||||||
|
};
|
||||||
|
int active_is_local_window(struct client *, struct session *);
|
||||||
|
void active_set_local_window(struct client *, struct session *,
|
||||||
|
enum active_mode);
|
||||||
|
struct winlink *active_get_effective_winlink(struct client *,
|
||||||
|
struct session *);
|
||||||
|
struct window *active_get_effective_window(struct client *,
|
||||||
|
struct session *);
|
||||||
|
int active_select_window(struct client *, struct session *,
|
||||||
|
struct winlink *);
|
||||||
|
int active_select_window_index(struct client *, struct session *, int);
|
||||||
|
int active_next_window(struct client *, struct session *, int);
|
||||||
|
int active_previous_window(struct client *, struct session *, int);
|
||||||
|
int active_last_window(struct client *, struct session *);
|
||||||
|
void active_remove_client(struct client *);
|
||||||
|
void active_remove_session(struct session *);
|
||||||
|
void active_remove_window(struct window *);
|
||||||
|
int active_is_effective_window(struct client *, struct session *,
|
||||||
|
struct winlink *);
|
||||||
|
int active_is_last_window(struct client *, struct session *,
|
||||||
|
struct winlink *);
|
||||||
|
int active_has_local_pane(struct client *, struct window *);
|
||||||
|
void active_set_pane_mode(struct client *, struct window *,
|
||||||
|
enum active_mode);
|
||||||
|
struct window_pane *active_get_effective_pane(struct client *,
|
||||||
|
struct window *);
|
||||||
|
struct window_pane *active_get_last_pane(struct client *, struct window *);
|
||||||
|
int active_set_pane(struct client *, struct window *,
|
||||||
|
struct window_pane *, int);
|
||||||
|
void active_check_clients(void);
|
||||||
|
void active_remove_pane(struct window_pane *);
|
||||||
|
|
||||||
/* format-draw.c */
|
/* format-draw.c */
|
||||||
void format_draw(struct screen_write_ctx *, const struct grid_cell *,
|
void format_draw(struct screen_write_ctx *, const struct grid_cell *,
|
||||||
u_int, const char *, struct style_ranges *, int);
|
u_int, const char *, struct style_ranges *, int);
|
||||||
@@ -3011,16 +3030,20 @@ void tty_cmd_scrolldown(struct tty *, const struct tty_ctx *);
|
|||||||
void tty_cmd_reverseindex(struct tty *, const struct tty_ctx *);
|
void tty_cmd_reverseindex(struct tty *, const struct tty_ctx *);
|
||||||
void tty_cmd_setselection(struct tty *, const struct tty_ctx *);
|
void tty_cmd_setselection(struct tty *, const struct tty_ctx *);
|
||||||
void tty_cmd_rawstring(struct tty *, const struct tty_ctx *);
|
void tty_cmd_rawstring(struct tty *, const struct tty_ctx *);
|
||||||
|
#ifdef ENABLE_SIXEL
|
||||||
|
void tty_cmd_sixelimage(struct tty *, const struct tty_ctx *);
|
||||||
|
void tty_draw_images(struct client *, struct window_pane *);
|
||||||
|
#endif
|
||||||
void tty_cmd_syncstart(struct tty *, const struct tty_ctx *);
|
void tty_cmd_syncstart(struct tty *, const struct tty_ctx *);
|
||||||
void tty_default_colours(struct grid_cell *, struct window_pane *, u_int *);
|
void tty_default_colours(struct grid_cell *, struct window_pane *,
|
||||||
|
struct window_pane *, u_int *);
|
||||||
|
|
||||||
/* tty-term.c */
|
/* tty-term.c */
|
||||||
extern struct tty_terms tty_terms;
|
extern struct tty_terms tty_terms;
|
||||||
u_int tty_term_ncodes(void);
|
u_int tty_term_ncodes(void);
|
||||||
void tty_term_apply(struct tty_term *, const char *, int);
|
void tty_term_apply(struct tty_term *, const char *, int);
|
||||||
void tty_term_apply_overrides(struct tty_term *);
|
void tty_term_apply_overrides(struct tty_term *);
|
||||||
struct tty_term *tty_term_create(struct tty *, char *, char **, u_int, int *,
|
struct tty_term *tty_term_create(struct tty *, char *, char **, u_int, char **);
|
||||||
char **);
|
|
||||||
void tty_term_free(struct tty_term *);
|
void tty_term_free(struct tty_term *);
|
||||||
int tty_term_read_list(const char *, int, char ***, u_int *,
|
int tty_term_read_list(const char *, int, char ***, u_int *,
|
||||||
char **);
|
char **);
|
||||||
@@ -3042,19 +3065,18 @@ int tty_term_flag(struct tty_term *, enum tty_code_code);
|
|||||||
const char *tty_term_describe(struct tty_term *, enum tty_code_code);
|
const char *tty_term_describe(struct tty_term *, enum tty_code_code);
|
||||||
|
|
||||||
/* tty-features.c */
|
/* tty-features.c */
|
||||||
void tty_add_features(int *, const char *, const char *);
|
void tty_parse_client_features(struct client *, const char *,
|
||||||
|
const char *);
|
||||||
|
void tty_parse_features(const char *, const char *, int *, int *);
|
||||||
const char *tty_get_features(int);
|
const char *tty_get_features(int);
|
||||||
int tty_feature_present(struct tty_term *, const char *);
|
int tty_feature_present(struct tty_term *, const char *);
|
||||||
int tty_apply_features(struct tty_term *, int);
|
int tty_apply_features(struct tty_term *);
|
||||||
void tty_default_features(int *, const char *, u_int);
|
void tty_default_features(struct client *, const char *, u_int);
|
||||||
|
|
||||||
/* tty-acs.c */
|
/* tty-acs.c */
|
||||||
int tty_acs_needed(struct tty *);
|
int tty_acs_needed(struct tty *);
|
||||||
const char *tty_acs_get(struct tty *, u_char);
|
const char *tty_acs_get(struct tty *, u_char);
|
||||||
int tty_acs_reverse_get(struct tty *, const char *, size_t);
|
int tty_acs_reverse_get(struct tty *, const char *, size_t);
|
||||||
#ifdef ENABLE_IMAGES
|
|
||||||
u_char tty_acs_image_sextant(u_int);
|
|
||||||
#endif
|
|
||||||
const struct utf8_data *tty_acs_double_borders(int);
|
const struct utf8_data *tty_acs_double_borders(int);
|
||||||
const struct utf8_data *tty_acs_heavy_borders(int);
|
const struct utf8_data *tty_acs_heavy_borders(int);
|
||||||
const struct utf8_data *tty_acs_rounded_borders(int);
|
const struct utf8_data *tty_acs_rounded_borders(int);
|
||||||
@@ -3303,6 +3325,7 @@ void file_write_close(struct client_files *, struct imsg *);
|
|||||||
void file_read_open(struct client_files *, struct tmuxpeer *, struct imsg *,
|
void file_read_open(struct client_files *, struct tmuxpeer *, struct imsg *,
|
||||||
int, int, client_file_cb, void *);
|
int, int, client_file_cb, void *);
|
||||||
int file_write_ready(struct client_files *, struct imsg *);
|
int file_write_ready(struct client_files *, struct imsg *);
|
||||||
|
int file_write_done(struct client_files *, struct imsg *);
|
||||||
int file_read_data(struct client_files *, struct imsg *);
|
int file_read_data(struct client_files *, struct imsg *);
|
||||||
int file_read_done(struct client_files *, struct imsg *);
|
int file_read_done(struct client_files *, struct imsg *);
|
||||||
void file_read_cancel(struct client_files *, struct imsg *);
|
void file_read_cancel(struct client_files *, struct imsg *);
|
||||||
@@ -3519,7 +3542,7 @@ void grid_clear_history(struct grid *);
|
|||||||
const struct grid_line *grid_peek_line(struct grid *, u_int);
|
const struct grid_line *grid_peek_line(struct grid *, u_int);
|
||||||
void grid_get_cell(struct grid *, u_int, u_int, struct grid_cell *);
|
void grid_get_cell(struct grid *, u_int, u_int, struct grid_cell *);
|
||||||
void grid_set_cell(struct grid *, u_int, u_int, const struct grid_cell *);
|
void grid_set_cell(struct grid *, u_int, u_int, const struct grid_cell *);
|
||||||
void grid_set_padding(struct grid *, u_int, u_int);
|
void grid_set_padding(struct grid *, u_int, u_int, int);
|
||||||
void grid_set_cells(struct grid *, u_int, u_int, const struct grid_cell *,
|
void grid_set_cells(struct grid *, u_int, u_int, const struct grid_cell *,
|
||||||
const char *, size_t);
|
const char *, size_t);
|
||||||
struct grid_line *grid_get_line(struct grid *, u_int);
|
struct grid_line *grid_get_line(struct grid *, u_int);
|
||||||
@@ -3564,7 +3587,7 @@ void grid_reader_cursor_back_to_indentation(struct grid_reader *);
|
|||||||
void grid_view_get_cell(struct grid *, u_int, u_int, struct grid_cell *);
|
void grid_view_get_cell(struct grid *, u_int, u_int, struct grid_cell *);
|
||||||
void grid_view_set_cell(struct grid *, u_int, u_int,
|
void grid_view_set_cell(struct grid *, u_int, u_int,
|
||||||
const struct grid_cell *);
|
const struct grid_cell *);
|
||||||
void grid_view_set_padding(struct grid *, u_int, u_int);
|
void grid_view_set_padding(struct grid *, u_int, u_int, int);
|
||||||
void grid_view_set_cells(struct grid *, u_int, u_int,
|
void grid_view_set_cells(struct grid *, u_int, u_int,
|
||||||
const struct grid_cell *, const char *, size_t);
|
const struct grid_cell *, const char *, size_t);
|
||||||
void grid_view_clear_history(struct grid *, u_int);
|
void grid_view_clear_history(struct grid *, u_int);
|
||||||
@@ -3620,6 +3643,7 @@ void screen_write_mode_set(struct screen_write_ctx *, int);
|
|||||||
void screen_write_mode_clear(struct screen_write_ctx *, int);
|
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_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);
|
||||||
@@ -3654,7 +3678,7 @@ void screen_write_setselection(struct screen_write_ctx *, const char *,
|
|||||||
u_char *, u_int);
|
u_char *, u_int);
|
||||||
void screen_write_rawstring(struct screen_write_ctx *, u_char *, u_int,
|
void screen_write_rawstring(struct screen_write_ctx *, u_char *, u_int,
|
||||||
int);
|
int);
|
||||||
#ifdef ENABLE_IMAGES
|
#ifdef ENABLE_SIXEL
|
||||||
void screen_write_sixelimage(struct screen_write_ctx *,
|
void screen_write_sixelimage(struct screen_write_ctx *,
|
||||||
struct sixel_image *, u_int);
|
struct sixel_image *, u_int);
|
||||||
#endif
|
#endif
|
||||||
@@ -3749,8 +3773,8 @@ void window_resize(struct window *, u_int, u_int, int, int);
|
|||||||
void window_pane_send_resize(struct window_pane *, u_int, u_int);
|
void window_pane_send_resize(struct window_pane *, u_int, u_int);
|
||||||
int window_zoom(struct window_pane *);
|
int window_zoom(struct window_pane *);
|
||||||
int window_unzoom(struct window *, int);
|
int window_unzoom(struct window *, int);
|
||||||
void window_push_modal_zoom(struct window *);
|
int window_active_pane_is_over_zoom(struct window *);
|
||||||
int window_pop_modal_zoom(struct window *);
|
struct window_pane *window_zoomed_pane(struct window *);
|
||||||
int window_push_zoom(struct window *, int, int);
|
int window_push_zoom(struct window *, int, int);
|
||||||
int window_pop_zoom(struct window *);
|
int window_pop_zoom(struct window *);
|
||||||
void window_lost_pane(struct window *, struct window_pane *);
|
void window_lost_pane(struct window *, struct window_pane *);
|
||||||
@@ -3794,7 +3818,7 @@ int window_pane_is_visible(struct window_pane *);
|
|||||||
int window_pane_exited(struct window_pane *);
|
int window_pane_exited(struct window_pane *);
|
||||||
u_int window_pane_search(struct window_pane *, const char *, int,
|
u_int window_pane_search(struct window_pane *, const char *, int,
|
||||||
int);
|
int);
|
||||||
const char *window_printable_flags(struct winlink *, int);
|
const char *window_printable_flags(struct client *, struct winlink *, int);
|
||||||
const char *window_pane_printable_flags(struct window_pane *);
|
const char *window_pane_printable_flags(struct window_pane *);
|
||||||
struct window_pane *window_pane_find_up(struct window_pane *);
|
struct window_pane *window_pane_find_up(struct window_pane *);
|
||||||
struct window_pane *window_pane_find_down(struct window_pane *);
|
struct window_pane *window_pane_find_down(struct window_pane *);
|
||||||
@@ -4046,6 +4070,7 @@ time_t monitor_get_fire_time(struct monitor_set *, const char *);
|
|||||||
|
|
||||||
/* control.c */
|
/* control.c */
|
||||||
void control_discard(struct client *);
|
void control_discard(struct client *);
|
||||||
|
void control_discard_all(struct client *);
|
||||||
void control_start(struct client *);
|
void control_start(struct client *);
|
||||||
void control_ready(struct client *);
|
void control_ready(struct client *);
|
||||||
void control_stop(struct client *);
|
void control_stop(struct client *);
|
||||||
@@ -4054,6 +4079,7 @@ void control_set_pane_on(struct client *, struct window_pane *);
|
|||||||
void control_set_pane_off(struct client *, struct window_pane *);
|
void control_set_pane_off(struct client *, struct window_pane *);
|
||||||
void control_continue_pane(struct client *, struct window_pane *);
|
void control_continue_pane(struct client *, struct window_pane *);
|
||||||
void control_pause_pane(struct client *, struct window_pane *);
|
void control_pause_pane(struct client *, struct window_pane *);
|
||||||
|
void control_reset_pane(struct client *, struct window_pane *);
|
||||||
void control_set_window_size(struct client *, u_int, u_int, u_int);
|
void control_set_window_size(struct client *, u_int, u_int, u_int);
|
||||||
int control_get_window_size(struct client *, u_int, u_int *, u_int *);
|
int control_get_window_size(struct client *, u_int, u_int *, u_int *);
|
||||||
void control_clear_window_size(struct client *, u_int);
|
void control_clear_window_size(struct client *, u_int);
|
||||||
@@ -4061,6 +4087,8 @@ struct window_pane_offset *control_pane_offset(struct client *,
|
|||||||
struct window_pane *, int *);
|
struct window_pane *, int *);
|
||||||
void control_reset_offsets(struct client *);
|
void control_reset_offsets(struct client *);
|
||||||
void printflike(2, 3) control_write(struct client *, const char *, ...);
|
void printflike(2, 3) control_write(struct client *, const char *, ...);
|
||||||
|
void printflike(2, 3) control_notify_write(struct client *, const char *, ...);
|
||||||
|
void control_write_guard(struct client *, const char *, long, u_int, int);
|
||||||
void control_write_output(struct client *, struct window_pane *);
|
void control_write_output(struct client *, struct window_pane *);
|
||||||
int control_all_done(struct client *);
|
int control_all_done(struct client *);
|
||||||
void control_add_sub(struct client *, const char *, enum monitor_type, int,
|
void control_add_sub(struct client *, const char *, enum monitor_type, int,
|
||||||
@@ -4116,6 +4144,7 @@ void session_update_history(struct session *);
|
|||||||
/* utf8.c */
|
/* utf8.c */
|
||||||
enum utf8_state utf8_towc (const struct utf8_data *, wchar_t *);
|
enum utf8_state utf8_towc (const struct utf8_data *, wchar_t *);
|
||||||
enum utf8_state utf8_fromwc(wchar_t wc, struct utf8_data *);
|
enum utf8_state utf8_fromwc(wchar_t wc, struct utf8_data *);
|
||||||
|
int utf8_has_whitespace(const struct utf8_data *);
|
||||||
void utf8_update_width_cache(void);
|
void utf8_update_width_cache(void);
|
||||||
utf8_char utf8_build_one(u_char);
|
utf8_char utf8_build_one(u_char);
|
||||||
enum utf8_state utf8_from_data(const struct utf8_data *, utf8_char *);
|
enum utf8_state utf8_from_data(const struct utf8_data *, utf8_char *);
|
||||||
@@ -4237,66 +4266,16 @@ void spawn_editor_finish(struct window_pane *);
|
|||||||
/* regsub.c */
|
/* regsub.c */
|
||||||
char *regsub(const char *, const char *, const char *, int);
|
char *regsub(const char *, const char *, const char *, int);
|
||||||
|
|
||||||
#ifdef ENABLE_IMAGES
|
#ifdef ENABLE_SIXEL
|
||||||
/* image.c */
|
/* image.c */
|
||||||
#define IMAGE_BACKEND_GRAPHICAL 0x1
|
int image_free_all(struct screen *);
|
||||||
#define IMAGE_BACKEND_SCROLLS 0x2
|
struct image *image_store(struct screen *, struct sixel_image *);
|
||||||
#define IMAGE_BACKEND_TEMPORAL 0x4
|
int image_check_line(struct screen *, u_int, u_int);
|
||||||
|
int image_check_area(struct screen *, u_int, u_int, u_int, u_int);
|
||||||
|
int image_scroll_up(struct screen *, u_int);
|
||||||
|
|
||||||
struct image *image_create(u_int, u_int, u_int, u_int, u_int, u_int,
|
|
||||||
u_char *);
|
|
||||||
struct image *image_find(u_int);
|
|
||||||
u_int image_get_id(const struct image *);
|
|
||||||
u_short image_get_grid_id(u_int);
|
|
||||||
u_int image_get_id_by_grid_id(u_short);
|
|
||||||
void image_get_size(const struct image *, u_int *, u_int *);
|
|
||||||
void image_get_canvas_size(const struct image *, u_int *, u_int *);
|
|
||||||
void image_get_size_in_cells(const struct image *, u_int *, u_int *);
|
|
||||||
const u_char *image_get_pixels(const struct image *, size_t *, size_t *);
|
|
||||||
struct sixel_image *image_get_sixel(const struct image *);
|
|
||||||
void image_set_sixel(struct image *, struct sixel_image *);
|
|
||||||
u_char image_get_brightness(struct image *, u_int, u_int);
|
|
||||||
void image_get_cell_average(struct image *, u_int, u_int, u_int, u_int,
|
|
||||||
u_int, u_int, u_char *, u_char *, u_char *);
|
|
||||||
struct image_fallback_data *image_get_fallback_data(const struct image *);
|
|
||||||
void image_set_fallback_data(struct image *,
|
|
||||||
struct image_fallback_data *);
|
|
||||||
void image_ref(u_int);
|
|
||||||
void image_free(u_int);
|
|
||||||
void image_set_cell(struct grid_cell *, struct image *, u_int,
|
|
||||||
u_int);
|
|
||||||
void image_write(struct screen_write_ctx *, struct image *, u_int);
|
|
||||||
void image_get_pixel_rect(const struct image *, u_int, u_int,
|
|
||||||
u_int, u_int, u_int *, u_int *, u_int *, u_int *);
|
|
||||||
void image_size_in_cells(u_int, u_int, u_int, u_int, u_int *,
|
|
||||||
u_int *);
|
|
||||||
void image_redraw_area(struct screen_write_ctx *, u_int, u_int,
|
|
||||||
u_int, u_int);
|
|
||||||
void image_redraw_all(struct screen_write_ctx *);
|
|
||||||
void image_redraw_scroll(struct screen_write_ctx *, u_int);
|
|
||||||
int image_backend_flags(struct tty *);
|
|
||||||
int image_get_draw_cell(struct tty *, const struct grid_cell *,
|
|
||||||
struct grid_cell *, const struct tty_style_ctx *);
|
|
||||||
void image_tty_update(struct tty *);
|
|
||||||
void image_tty_geometry_changed(struct tty *);
|
|
||||||
void image_tty_free(struct tty *, int);
|
|
||||||
void image_draw_line(struct tty *, struct screen *, u_int, u_int,
|
|
||||||
u_int, u_int, u_int, const struct tty_style_ctx *);
|
|
||||||
void image_get_fallback_cell(struct tty *, struct image *, u_int,
|
|
||||||
u_int, const struct grid_cell *, struct grid_cell *,
|
|
||||||
const struct tty_style_ctx *);
|
|
||||||
void image_free_fallback(struct image *);
|
|
||||||
struct image *image_rect_get_image(const struct image_rect *);
|
|
||||||
void image_rect_get_coords(const struct image_rect *,
|
|
||||||
u_int *, u_int *, u_int *, u_int *, u_int *, u_int *);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifdef ENABLE_IMAGES
|
|
||||||
/* image-sixel.c */
|
/* image-sixel.c */
|
||||||
#define SIXEL_COLOUR_REGISTERS 1024
|
#define SIXEL_COLOUR_REGISTERS 1024
|
||||||
void sixel_draw_rect(struct tty *,
|
|
||||||
const struct image_rect *, const struct tty_style_ctx *);
|
|
||||||
void sixel_free_output(struct tty *, int);
|
|
||||||
struct sixel_image *sixel_parse(const char *, size_t, u_int, u_int, u_int);
|
struct sixel_image *sixel_parse(const char *, size_t, u_int, u_int, u_int);
|
||||||
void sixel_free(struct sixel_image *);
|
void sixel_free(struct sixel_image *);
|
||||||
void sixel_log(struct sixel_image *);
|
void sixel_log(struct sixel_image *);
|
||||||
@@ -4306,7 +4285,6 @@ struct sixel_image *sixel_scale(struct sixel_image *, u_int, u_int, u_int,
|
|||||||
char *sixel_print(struct sixel_image *, struct sixel_image *,
|
char *sixel_print(struct sixel_image *, struct sixel_image *,
|
||||||
size_t *);
|
size_t *);
|
||||||
struct screen *sixel_to_screen(struct sixel_image *);
|
struct screen *sixel_to_screen(struct sixel_image *);
|
||||||
struct image *sixel_to_image(struct sixel_image *);
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/* server-acl.c */
|
/* server-acl.c */
|
||||||
|
|||||||
67
tty-acs.c
67
tty-acs.c
@@ -64,65 +64,9 @@ static const struct tty_acs_entry tty_acs_table[] = {
|
|||||||
{ '{', "\317\200" }, /* greek pi */
|
{ '{', "\317\200" }, /* greek pi */
|
||||||
{ '|', "\342\211\240" }, /* not-equal */
|
{ '|', "\342\211\240" }, /* not-equal */
|
||||||
{ '}', "\302\243" }, /* UK pound sign */
|
{ '}', "\302\243" }, /* UK pound sign */
|
||||||
{ '~', "\302\267" }, /* bullet */
|
{ '~', "\302\267" } /* bullet */
|
||||||
#ifdef ENABLE_IMAGES
|
|
||||||
{ TTY_ACS_IMAGE_SHADE_LIGHT, "\342\226\221" },
|
|
||||||
{ TTY_ACS_IMAGE_SHADE_MEDIUM, "\342\226\222" },
|
|
||||||
{ TTY_ACS_IMAGE_SHADE_DARK, "\342\226\223" },
|
|
||||||
{ TTY_ACS_IMAGE_BLOCK, "\342\226\210" },
|
|
||||||
{ TTY_ACS_IMAGE_HALF_UPPER, "\342\226\200" },
|
|
||||||
{ TTY_ACS_IMAGE_HALF_LOWER, "\342\226\204" },
|
|
||||||
{ TTY_ACS_IMAGE_HALF_LEFT, "\342\226\214" },
|
|
||||||
{ TTY_ACS_IMAGE_HALF_RIGHT, "\342\226\220" },
|
|
||||||
{ TTY_ACS_IMAGE_QUADRANT_LOWER_LEFT, "\342\226\226" },
|
|
||||||
{ TTY_ACS_IMAGE_QUADRANT_LOWER_RIGHT, "\342\226\227" },
|
|
||||||
{ TTY_ACS_IMAGE_QUADRANT_UPPER_LEFT, "\342\226\230" },
|
|
||||||
{ TTY_ACS_IMAGE_QUADRANT_UPPER_LEFT_LOWER_LEFT_LOWER_RIGHT,
|
|
||||||
"\342\226\231" },
|
|
||||||
{ TTY_ACS_IMAGE_QUADRANT_UPPER_LEFT_LOWER_RIGHT, "\342\226\232" },
|
|
||||||
{ TTY_ACS_IMAGE_QUADRANT_UPPER_LEFT_UPPER_RIGHT_LOWER_LEFT,
|
|
||||||
"\342\226\233" },
|
|
||||||
{ TTY_ACS_IMAGE_QUADRANT_UPPER_LEFT_UPPER_RIGHT_LOWER_RIGHT,
|
|
||||||
"\342\226\234" },
|
|
||||||
{ TTY_ACS_IMAGE_QUADRANT_UPPER_RIGHT, "\342\226\235" },
|
|
||||||
{ TTY_ACS_IMAGE_QUADRANT_UPPER_RIGHT_LOWER_LEFT, "\342\226\236" },
|
|
||||||
{ TTY_ACS_IMAGE_QUADRANT_UPPER_RIGHT_LOWER_LEFT_LOWER_RIGHT,
|
|
||||||
"\342\226\237" }
|
|
||||||
#endif
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#ifdef ENABLE_IMAGES
|
|
||||||
static char tty_acs_image_sextants[60][5];
|
|
||||||
|
|
||||||
static void
|
|
||||||
tty_acs_image_sextants_init(void)
|
|
||||||
{
|
|
||||||
u_int cp, i;
|
|
||||||
|
|
||||||
if (tty_acs_image_sextants[0][0] != '\0')
|
|
||||||
return;
|
|
||||||
for (i = 0; i < nitems(tty_acs_image_sextants); i++) {
|
|
||||||
cp = 0x1fb00 + i;
|
|
||||||
tty_acs_image_sextants[i][0] = 0xf0 | (cp >> 18);
|
|
||||||
tty_acs_image_sextants[i][1] = 0x80 | ((cp >> 12) & 0x3f);
|
|
||||||
tty_acs_image_sextants[i][2] = 0x80 | ((cp >> 6) & 0x3f);
|
|
||||||
tty_acs_image_sextants[i][3] = 0x80 | (cp & 0x3f);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
u_char
|
|
||||||
tty_acs_image_sextant(u_int mask)
|
|
||||||
{
|
|
||||||
if (mask == 0 || mask == 21 || mask == 42 || mask >= 63)
|
|
||||||
return (0);
|
|
||||||
if (mask < 21)
|
|
||||||
return (TTY_ACS_IMAGE_SEXTANT_FIRST + mask - 1);
|
|
||||||
if (mask < 42)
|
|
||||||
return (TTY_ACS_IMAGE_SEXTANT_FIRST + mask - 2);
|
|
||||||
return (TTY_ACS_IMAGE_SEXTANT_FIRST + mask - 3);
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/* Table mapping UTF-8 to ACS entries. */
|
/* Table mapping UTF-8 to ACS entries. */
|
||||||
struct tty_acs_reverse_entry {
|
struct tty_acs_reverse_entry {
|
||||||
const char *string;
|
const char *string;
|
||||||
@@ -295,15 +239,6 @@ tty_acs_get(struct tty *tty, u_char ch)
|
|||||||
return (&tty->term->acs[ch][0]);
|
return (&tty->term->acs[ch][0]);
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef ENABLE_IMAGES
|
|
||||||
if (ch >= TTY_ACS_IMAGE_SEXTANT_FIRST &&
|
|
||||||
ch <= TTY_ACS_IMAGE_SEXTANT_LAST) {
|
|
||||||
tty_acs_image_sextants_init();
|
|
||||||
return (tty_acs_image_sextants[
|
|
||||||
ch - TTY_ACS_IMAGE_SEXTANT_FIRST]);
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/* Otherwise look up the UTF-8 translation. */
|
/* Otherwise look up the UTF-8 translation. */
|
||||||
entry = bsearch(&ch, tty_acs_table, nitems(tty_acs_table),
|
entry = bsearch(&ch, tty_acs_table, nitems(tty_acs_table),
|
||||||
sizeof tty_acs_table[0], tty_acs_cmp);
|
sizeof tty_acs_table[0], tty_acs_cmp);
|
||||||
|
|||||||
21
tty-draw.c
21
tty-draw.c
@@ -123,9 +123,6 @@ tty_draw_line(struct tty *tty, struct screen *s, u_int px, u_int py, u_int nx,
|
|||||||
struct grid *gd = s->grid;
|
struct grid *gd = s->grid;
|
||||||
const struct grid_cell *gcp;
|
const struct grid_cell *gcp;
|
||||||
struct grid_cell gc, ngc, last;
|
struct grid_cell gc, ngc, last;
|
||||||
#ifdef ENABLE_IMAGES
|
|
||||||
struct grid_cell image_gc;
|
|
||||||
#endif
|
|
||||||
struct grid_line *gl;
|
struct grid_line *gl;
|
||||||
u_int i, j, last_i, cx, ex, width;
|
u_int i, j, last_i, cx, ex, width;
|
||||||
u_int cellsize, bg;
|
u_int cellsize, bg;
|
||||||
@@ -256,24 +253,14 @@ tty_draw_line(struct tty *tty, struct screen *s, u_int px, u_int py, u_int nx,
|
|||||||
/* Get the current cell. */
|
/* Get the current cell. */
|
||||||
grid_view_get_cell(gd, px + i, py, &gc);
|
grid_view_get_cell(gd, px + i, py, &gc);
|
||||||
|
|
||||||
#ifdef ENABLE_IMAGES
|
|
||||||
if (gc.flags & GRID_FLAG_IMAGE) {
|
|
||||||
(void)image_get_draw_cell(tty, &gc, &image_gc,
|
|
||||||
style_ctx);
|
|
||||||
gcp = &image_gc;
|
|
||||||
} else
|
|
||||||
gcp = &gc;
|
|
||||||
#else
|
|
||||||
gcp = &gc;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/* Work out empty cells. */
|
/* Work out empty cells. */
|
||||||
empty = tty_draw_line_get_empty(gcp, &last, nx - i);
|
empty = tty_draw_line_get_empty(&gc, &last,
|
||||||
|
nx - i);
|
||||||
if (empty != 0)
|
if (empty != 0)
|
||||||
;
|
gcp = &gc;
|
||||||
else {
|
else {
|
||||||
/* Update for codeset if needed. */
|
/* Update for codeset if needed. */
|
||||||
gcp = tty_check_codeset(tty, gcp);
|
gcp = tty_check_codeset(tty, &gc);
|
||||||
|
|
||||||
/* And for selection. */
|
/* And for selection. */
|
||||||
if (gcp->flags & GRID_FLAG_SELECTED) {
|
if (gcp->flags & GRID_FLAG_SELECTED) {
|
||||||
|
|||||||
107
tty-features.c
107
tty-features.c
@@ -1,4 +1,4 @@
|
|||||||
/* $OpenBSD: tty-features.c,v 1.40 2026/07/01 06:17:58 nicm Exp $ */
|
/* $OpenBSD: tty-features.c,v 1.43 2026/08/31 12:41:03 kirill Exp $ */
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2020 Nicholas Marriott <nicholas.marriott@gmail.com>
|
* Copyright (c) 2020 Nicholas Marriott <nicholas.marriott@gmail.com>
|
||||||
@@ -224,11 +224,9 @@ static const struct tty_feature tty_feature_strikethrough = {
|
|||||||
0
|
0
|
||||||
};
|
};
|
||||||
|
|
||||||
#define TTY_FEATURE_SYNC "Sync=\\E[?2026%?%p1%{1}%-%tl%eh%;"
|
|
||||||
|
|
||||||
/* Terminal supports synchronized updates. */
|
/* Terminal supports synchronized updates. */
|
||||||
static const char *const tty_feature_sync_capabilities[] = {
|
static const char *const tty_feature_sync_capabilities[] = {
|
||||||
TTY_FEATURE_SYNC,
|
"Sync=\\E[?2026%?%p1%{1}%-%tl%eh%;",
|
||||||
NULL
|
NULL
|
||||||
};
|
};
|
||||||
static const struct tty_feature tty_feature_sync = {
|
static const struct tty_feature tty_feature_sync = {
|
||||||
@@ -359,22 +357,6 @@ static const struct tty_feature tty_feature_sixel = {
|
|||||||
TERM_SIXEL
|
TERM_SIXEL
|
||||||
};
|
};
|
||||||
|
|
||||||
#ifdef ENABLE_IMAGES
|
|
||||||
static const char *const tty_feature_image_capabilities[] = {
|
|
||||||
NULL
|
|
||||||
};
|
|
||||||
static const struct tty_feature tty_feature_image_quadrants = {
|
|
||||||
"image-quadrants",
|
|
||||||
tty_feature_image_capabilities,
|
|
||||||
TERM_IMAGE_QUADRANTS
|
|
||||||
};
|
|
||||||
static const struct tty_feature tty_feature_image_sextants = {
|
|
||||||
"image-sextants",
|
|
||||||
tty_feature_image_capabilities,
|
|
||||||
TERM_IMAGE_SEXTANTS
|
|
||||||
};
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/* Terminal supports the OSC 9;4 progress bar. */
|
/* Terminal supports the OSC 9;4 progress bar. */
|
||||||
static const char *const tty_feature_progressbar_capabilities[] = {
|
static const char *const tty_feature_progressbar_capabilities[] = {
|
||||||
"Spb=\\E]9;4;%p1%d;%p2%d\\E\\\\",
|
"Spb=\\E]9;4;%p1%d;%p2%d\\E\\\\",
|
||||||
@@ -386,6 +368,13 @@ static const struct tty_feature tty_feature_progressbar = {
|
|||||||
0
|
0
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/* Terminal supports UTF-8. */
|
||||||
|
static const struct tty_feature tty_feature_utf8 = {
|
||||||
|
"utf8",
|
||||||
|
NULL,
|
||||||
|
0
|
||||||
|
};
|
||||||
|
|
||||||
/* Available terminal features. */
|
/* Available terminal features. */
|
||||||
static const struct tty_feature *const tty_features[] = {
|
static const struct tty_feature *const tty_features[] = {
|
||||||
&tty_feature_256,
|
&tty_feature_256,
|
||||||
@@ -397,10 +386,6 @@ static const struct tty_feature *const tty_features[] = {
|
|||||||
&tty_feature_extkeys,
|
&tty_feature_extkeys,
|
||||||
&tty_feature_focus,
|
&tty_feature_focus,
|
||||||
&tty_feature_ignorefkeys,
|
&tty_feature_ignorefkeys,
|
||||||
#ifdef ENABLE_IMAGES
|
|
||||||
&tty_feature_image_quadrants,
|
|
||||||
&tty_feature_image_sextants,
|
|
||||||
#endif
|
|
||||||
&tty_feature_margins,
|
&tty_feature_margins,
|
||||||
&tty_feature_mouse,
|
&tty_feature_mouse,
|
||||||
&tty_feature_osc7,
|
&tty_feature_osc7,
|
||||||
@@ -412,20 +397,33 @@ static const struct tty_feature *const tty_features[] = {
|
|||||||
&tty_feature_strikethrough,
|
&tty_feature_strikethrough,
|
||||||
&tty_feature_sync,
|
&tty_feature_sync,
|
||||||
&tty_feature_title,
|
&tty_feature_title,
|
||||||
&tty_feature_usstyle
|
&tty_feature_usstyle,
|
||||||
|
&tty_feature_utf8
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/* Parse features for client. */
|
||||||
void
|
void
|
||||||
tty_add_features(int *feat, const char *s, const char *separators)
|
tty_parse_client_features(struct client *c, const char *s, const char *sep)
|
||||||
|
{
|
||||||
|
tty_parse_features(s, sep, &c->term_features, &c->term_nofeatures);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Parse features list. */
|
||||||
|
void
|
||||||
|
tty_parse_features(const char *s, const char *sep, int *enabled, int *disabled)
|
||||||
{
|
{
|
||||||
const struct tty_feature *tf;
|
const struct tty_feature *tf;
|
||||||
char *next, *loop, *copy;
|
char *next, *loop, *copy;
|
||||||
u_int i;
|
u_int i;
|
||||||
|
int remove;
|
||||||
|
|
||||||
log_debug("adding terminal features %s", s);
|
log_debug("adding terminal features %s", s);
|
||||||
|
|
||||||
loop = copy = xstrdup(s);
|
loop = copy = xstrdup(s);
|
||||||
while ((next = strsep(&loop, separators)) != NULL) {
|
while ((next = strsep(&loop, sep)) != NULL) {
|
||||||
|
remove = (*next != '\0' && next[strlen(next) - 1] == '@');
|
||||||
|
if (remove)
|
||||||
|
next[strlen(next) - 1] = '\0';
|
||||||
for (i = 0; i < nitems(tty_features); i++) {
|
for (i = 0; i < nitems(tty_features); i++) {
|
||||||
tf = tty_features[i];
|
tf = tty_features[i];
|
||||||
if (strcasecmp(tf->name, next) == 0)
|
if (strcasecmp(tf->name, next) == 0)
|
||||||
@@ -435,14 +433,24 @@ tty_add_features(int *feat, const char *s, const char *separators)
|
|||||||
log_debug("unknown terminal feature: %s", next);
|
log_debug("unknown terminal feature: %s", next);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (~(*feat) & (1 << i)) {
|
if (remove) {
|
||||||
|
log_debug("removing terminal feature: %s", tf->name);
|
||||||
|
*enabled &= ~(1 << i);
|
||||||
|
if (disabled != NULL)
|
||||||
|
*disabled |= 1 << i;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (disabled != NULL && *disabled & (1 << i))
|
||||||
|
continue;
|
||||||
|
if (~(*enabled) & (1 << i)) {
|
||||||
log_debug("adding terminal feature: %s", tf->name);
|
log_debug("adding terminal feature: %s", tf->name);
|
||||||
(*feat) |= (1 << i);
|
(*enabled) |= (1 << i);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
free(copy);
|
free(copy);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Get features as string. */
|
||||||
const char *
|
const char *
|
||||||
tty_get_features(int feat)
|
tty_get_features(int feat)
|
||||||
{
|
{
|
||||||
@@ -464,6 +472,7 @@ tty_get_features(int feat)
|
|||||||
return (s);
|
return (s);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Check if feature is present. */
|
||||||
int
|
int
|
||||||
tty_feature_present(struct tty_term *term, const char *name)
|
tty_feature_present(struct tty_term *term, const char *name)
|
||||||
{
|
{
|
||||||
@@ -472,10 +481,13 @@ tty_feature_present(struct tty_term *term, const char *name)
|
|||||||
u_int i;
|
u_int i;
|
||||||
char *copy;
|
char *copy;
|
||||||
|
|
||||||
|
if (strcmp(name, "utf8") == 0)
|
||||||
|
return ((term->tty->client->flags & CLIENT_UTF8) != 0);
|
||||||
|
|
||||||
for (i = 0; i < nitems(tty_features); i++) {
|
for (i = 0; i < nitems(tty_features); i++) {
|
||||||
tf = tty_features[i];
|
tf = tty_features[i];
|
||||||
if (strcmp(tf->name, name) == 0) {
|
if (strcmp(tf->name, name) == 0) {
|
||||||
if (term->features & (1 << i))
|
if (term->applied_features & (1 << i))
|
||||||
return (1);
|
return (1);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -485,7 +497,8 @@ tty_feature_present(struct tty_term *term, const char *name)
|
|||||||
* We don't just have the feature flag set. Check if the capabilities
|
* We don't just have the feature flag set. Check if the capabilities
|
||||||
* supported by the client are actual set instead.
|
* supported by the client are actual set instead.
|
||||||
*/
|
*/
|
||||||
if (tf == NULL || strcmp(name, "ignorefkeys") == 0)
|
if (tf == NULL || tf->capabilities == NULL ||
|
||||||
|
strcmp(name, "ignorefkeys") == 0)
|
||||||
return (0);
|
return (0);
|
||||||
if (tf->flags != 0 && (term->flags & tf->flags) != tf->flags)
|
if (tf->flags != 0 && (term->flags & tf->flags) != tf->flags)
|
||||||
return (0);
|
return (0);
|
||||||
@@ -503,19 +516,23 @@ tty_feature_present(struct tty_term *term, const char *name)
|
|||||||
return (1);
|
return (1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Apply featurs to terminal. */
|
||||||
int
|
int
|
||||||
tty_apply_features(struct tty_term *term, int feat)
|
tty_apply_features(struct tty_term *term)
|
||||||
{
|
{
|
||||||
|
struct client *c = term->tty->client;
|
||||||
const struct tty_feature *tf;
|
const struct tty_feature *tf;
|
||||||
const char *const *capability;
|
const char *const *capability;
|
||||||
|
int feat;
|
||||||
u_int i;
|
u_int i;
|
||||||
|
|
||||||
|
feat = (c->term_features & ~c->term_nofeatures);
|
||||||
if (feat == 0)
|
if (feat == 0)
|
||||||
return (0);
|
return (0);
|
||||||
log_debug("applying terminal features: %s", tty_get_features(feat));
|
log_debug("applying terminal features: %s", tty_get_features(feat));
|
||||||
|
|
||||||
for (i = 0; i < nitems(tty_features); i++) {
|
for (i = 0; i < nitems(tty_features); i++) {
|
||||||
if ((term->features & (1 << i)) || (~feat & (1 << i)))
|
if ((term->applied_features & (1 << i)) || (~feat & (1 << i)))
|
||||||
continue;
|
continue;
|
||||||
tf = tty_features[i];
|
tf = tty_features[i];
|
||||||
|
|
||||||
@@ -529,15 +546,18 @@ tty_apply_features(struct tty_term *term, int feat)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
term->flags |= tf->flags;
|
term->flags |= tf->flags;
|
||||||
|
if (tf == &tty_feature_utf8)
|
||||||
|
c->flags |= CLIENT_UTF8;
|
||||||
}
|
}
|
||||||
if ((term->features | feat) == term->features)
|
if ((term->applied_features|feat) == term->applied_features)
|
||||||
return (0);
|
return (0);
|
||||||
term->features |= feat;
|
term->applied_features |= feat;
|
||||||
return (1);
|
return (1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Add default features for a terminal identified by name and version. */
|
||||||
void
|
void
|
||||||
tty_default_features(int *feat, const char *name, u_int version)
|
tty_default_features(struct client *c, const char *name, u_int version)
|
||||||
{
|
{
|
||||||
static const struct {
|
static const struct {
|
||||||
const char *name;
|
const char *name;
|
||||||
@@ -603,7 +623,6 @@ tty_default_features(int *feat, const char *name, u_int version)
|
|||||||
"extkeys,"
|
"extkeys,"
|
||||||
"focus,"
|
"focus,"
|
||||||
"hyperlinks,"
|
"hyperlinks,"
|
||||||
"sixel,"
|
|
||||||
"usstyle"
|
"usstyle"
|
||||||
},
|
},
|
||||||
{ .name = "ghostty",
|
{ .name = "ghostty",
|
||||||
@@ -619,6 +638,18 @@ tty_default_features(int *feat, const char *name, u_int version)
|
|||||||
"usstyle,"
|
"usstyle,"
|
||||||
"progressbar"
|
"progressbar"
|
||||||
},
|
},
|
||||||
|
{ .name = "Rio",
|
||||||
|
.features = TTY_FEATURES_BASE_MODERN_XTERM ","
|
||||||
|
"ccolour,"
|
||||||
|
"cstyle,"
|
||||||
|
"focus,"
|
||||||
|
"overline,"
|
||||||
|
"hyperlinks,"
|
||||||
|
"osc7,"
|
||||||
|
"sync,"
|
||||||
|
"usstyle,"
|
||||||
|
"progressbar"
|
||||||
|
},
|
||||||
{ .name = "XTerm",
|
{ .name = "XTerm",
|
||||||
/*
|
/*
|
||||||
* xterm also supports DECSLRM and DECFRA, but they can be
|
* xterm also supports DECSLRM and DECFRA, but they can be
|
||||||
@@ -639,6 +670,6 @@ tty_default_features(int *feat, const char *name, u_int version)
|
|||||||
continue;
|
continue;
|
||||||
if (version != 0 && version < table[i].version)
|
if (version != 0 && version < table[i].version)
|
||||||
continue;
|
continue;
|
||||||
tty_add_features(feat, table[i].features, ",");
|
tty_parse_client_features(c, table[i].features, ",");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
99
tty-keys.c
99
tty-keys.c
@@ -1,4 +1,4 @@
|
|||||||
/* $OpenBSD: tty-keys.c,v 1.211 2026/07/21 07:12:49 nicm Exp $ */
|
/* $OpenBSD: tty-keys.c,v 1.214 2026/08/18 09:01:20 nicm Exp $ */
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>
|
* Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>
|
||||||
@@ -59,6 +59,7 @@ static int tty_keys_device_attributes2(struct tty *, const char *, size_t,
|
|||||||
size_t *);
|
size_t *);
|
||||||
static int tty_keys_extended_device_attributes(struct tty *, const char *,
|
static int tty_keys_extended_device_attributes(struct tty *, const char *,
|
||||||
size_t, size_t *);
|
size_t, size_t *);
|
||||||
|
static int tty_keys_sync(struct tty *, const char *, size_t, size_t *);
|
||||||
static int tty_keys_palette(struct tty *, const char *, size_t, size_t *);
|
static int tty_keys_palette(struct tty *, const char *, size_t, size_t *);
|
||||||
|
|
||||||
/* A key tree entry. */
|
/* A key tree entry. */
|
||||||
@@ -771,6 +772,17 @@ tty_keys_next(struct tty *tty)
|
|||||||
goto partial_key;
|
goto partial_key;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Is this a synchronized update mode response? */
|
||||||
|
switch (tty_keys_sync(tty, buf, len, &size)) {
|
||||||
|
case 0: /* yes */
|
||||||
|
key = KEYC_UNKNOWN;
|
||||||
|
goto complete_key;
|
||||||
|
case -1: /* no, or not valid */
|
||||||
|
break;
|
||||||
|
case 1: /* partial */
|
||||||
|
goto partial_key;
|
||||||
|
}
|
||||||
|
|
||||||
/* Is this a primary device attributes response? */
|
/* Is this a primary device attributes response? */
|
||||||
switch (tty_keys_device_attributes(tty, buf, len, &size)) {
|
switch (tty_keys_device_attributes(tty, buf, len, &size)) {
|
||||||
case 0: /* yes */
|
case 0: /* yes */
|
||||||
@@ -1008,12 +1020,12 @@ complete_key:
|
|||||||
/* Check for focus events. */
|
/* Check for focus events. */
|
||||||
if (key == KEYC_FOCUS_OUT) {
|
if (key == KEYC_FOCUS_OUT) {
|
||||||
c->flags &= ~CLIENT_FOCUSED;
|
c->flags &= ~CLIENT_FOCUSED;
|
||||||
window_update_focus(c->session->curw->window);
|
window_update_focus(active_get_effective_window(c, c->session));
|
||||||
events_fire_client("client-focus-out", c);
|
events_fire_client("client-focus-out", c);
|
||||||
} else if (key == KEYC_FOCUS_IN) {
|
} else if (key == KEYC_FOCUS_IN) {
|
||||||
c->flags |= CLIENT_FOCUSED;
|
c->flags |= CLIENT_FOCUSED;
|
||||||
events_fire_client("client-focus-in", c);
|
events_fire_client("client-focus-in", c);
|
||||||
window_update_focus(c->session->curw->window);
|
window_update_focus(active_get_effective_window(c, c->session));
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Fire the key. */
|
/* Fire the key. */
|
||||||
@@ -1447,7 +1459,6 @@ tty_keys_device_attributes(struct tty *tty, const char *buf, size_t len,
|
|||||||
size_t *size)
|
size_t *size)
|
||||||
{
|
{
|
||||||
struct client *c = tty->client;
|
struct client *c = tty->client;
|
||||||
int *features = &c->term_features;
|
|
||||||
u_int i, n = 0;
|
u_int i, n = 0;
|
||||||
char tmp[128], *endptr, p[32] = { 0 }, *cp, *next;
|
char tmp[128], *endptr, p[32] = { 0 }, *cp, *next;
|
||||||
|
|
||||||
@@ -1504,13 +1515,13 @@ tty_keys_device_attributes(struct tty *tty, const char *buf, size_t len,
|
|||||||
for (i = 1; i < n; i++) {
|
for (i = 1; i < n; i++) {
|
||||||
log_debug("%s: DA feature: %d", c->name, p[i]);
|
log_debug("%s: DA feature: %d", c->name, p[i]);
|
||||||
if (p[i] == 4)
|
if (p[i] == 4)
|
||||||
tty_add_features(features, "sixel", ",");
|
tty_parse_client_features(c, "sixel", ",");
|
||||||
if (p[i] == 21)
|
if (p[i] == 21)
|
||||||
tty_add_features(features, "margins", ",");
|
tty_parse_client_features(c, "margins", ",");
|
||||||
if (p[i] == 28)
|
if (p[i] == 28)
|
||||||
tty_add_features(features, "rectfill", ",");
|
tty_parse_client_features(c, "rectfill", ",");
|
||||||
if (p[i] == 52)
|
if (p[i] == 52)
|
||||||
tty_add_features(features, "clipboard", ",");
|
tty_parse_client_features(c, "clipboard", ",");
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -1522,6 +1533,54 @@ tty_keys_device_attributes(struct tty *tty, const char *buf, size_t len,
|
|||||||
return (0);
|
return (0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Handle a synchronized update mode response. Returns 0 for success, -1 for
|
||||||
|
* failure, 1 for partial.
|
||||||
|
*/
|
||||||
|
static int
|
||||||
|
tty_keys_sync(struct tty *tty, const char *buf, size_t len, size_t *size)
|
||||||
|
{
|
||||||
|
struct client *c = tty->client;
|
||||||
|
static const char prefix[] = "\033[?2026;";
|
||||||
|
size_t i;
|
||||||
|
int status;
|
||||||
|
|
||||||
|
*size = 0;
|
||||||
|
if (tty->flags & TTY_HAVESYNC)
|
||||||
|
return (-1);
|
||||||
|
|
||||||
|
/* The response is always \033[?2026;Ps$y. */
|
||||||
|
for (i = 0; i < (sizeof prefix) - 1; i++) {
|
||||||
|
if (i == len)
|
||||||
|
return (1);
|
||||||
|
if (buf[i] != prefix[i])
|
||||||
|
return (-1);
|
||||||
|
}
|
||||||
|
if (i == len)
|
||||||
|
return (1);
|
||||||
|
if (buf[i] < '0' || buf[i] > '4')
|
||||||
|
return (-1);
|
||||||
|
status = buf[i++] - '0';
|
||||||
|
if (i == len)
|
||||||
|
return (1);
|
||||||
|
if (buf[i++] != '$')
|
||||||
|
return (-1);
|
||||||
|
if (i == len)
|
||||||
|
return (1);
|
||||||
|
if (buf[i++] != 'y')
|
||||||
|
return (-1);
|
||||||
|
*size = i;
|
||||||
|
|
||||||
|
if (status == 1 || status == 2 || status == 3) {
|
||||||
|
tty_parse_client_features(c, "sync", ",");
|
||||||
|
tty_update_features(tty);
|
||||||
|
}
|
||||||
|
log_debug("%s: received DECRPM %.*s", c->name, (int)*size, buf);
|
||||||
|
tty->flags |= TTY_HAVESYNC;
|
||||||
|
|
||||||
|
return (0);
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Handle secondary device attributes input. Returns 0 for success, -1 for
|
* Handle secondary device attributes input. Returns 0 for success, -1 for
|
||||||
* failure, 1 for partial.
|
* failure, 1 for partial.
|
||||||
@@ -1531,7 +1590,6 @@ tty_keys_device_attributes2(struct tty *tty, const char *buf, size_t len,
|
|||||||
size_t *size)
|
size_t *size)
|
||||||
{
|
{
|
||||||
struct client *c = tty->client;
|
struct client *c = tty->client;
|
||||||
int *features = &c->term_features;
|
|
||||||
u_int i, n = 0;
|
u_int i, n = 0;
|
||||||
char tmp[128], *endptr, p[32] = { 0 }, *cp, *next;
|
char tmp[128], *endptr, p[32] = { 0 }, *cp, *next;
|
||||||
|
|
||||||
@@ -1585,13 +1643,13 @@ tty_keys_device_attributes2(struct tty *tty, const char *buf, size_t len,
|
|||||||
*/
|
*/
|
||||||
switch (p[0]) {
|
switch (p[0]) {
|
||||||
case 'M': /* mintty */
|
case 'M': /* mintty */
|
||||||
tty_default_features(features, "mintty", 0);
|
tty_default_features(c, "mintty", 0);
|
||||||
break;
|
break;
|
||||||
case 'T': /* tmux */
|
case 'T': /* tmux */
|
||||||
tty_default_features(features, "tmux", 0);
|
tty_default_features(c, "tmux", 0);
|
||||||
break;
|
break;
|
||||||
case 'U': /* rxvt-unicode */
|
case 'U': /* rxvt-unicode */
|
||||||
tty_default_features(features, "rxvt-unicode", 0);
|
tty_default_features(c, "rxvt-unicode", 0);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
log_debug("%s: received secondary DA %.*s", c->name, (int)*size, buf);
|
log_debug("%s: received secondary DA %.*s", c->name, (int)*size, buf);
|
||||||
@@ -1611,7 +1669,6 @@ tty_keys_extended_device_attributes(struct tty *tty, const char *buf,
|
|||||||
size_t len, size_t *size)
|
size_t len, size_t *size)
|
||||||
{
|
{
|
||||||
struct client *c = tty->client;
|
struct client *c = tty->client;
|
||||||
int *features = &c->term_features;
|
|
||||||
u_int i;
|
u_int i;
|
||||||
char tmp[128];
|
char tmp[128];
|
||||||
|
|
||||||
@@ -1654,19 +1711,21 @@ tty_keys_extended_device_attributes(struct tty *tty, const char *buf,
|
|||||||
|
|
||||||
/* Add terminal features. */
|
/* Add terminal features. */
|
||||||
if (strncmp(tmp, "iTerm2 ", 7) == 0)
|
if (strncmp(tmp, "iTerm2 ", 7) == 0)
|
||||||
tty_default_features(features, "iTerm2", 0);
|
tty_default_features(c, "iTerm2", 0);
|
||||||
else if (strncmp(tmp, "tmux ", 5) == 0)
|
else if (strncmp(tmp, "tmux ", 5) == 0)
|
||||||
tty_default_features(features, "tmux", 0);
|
tty_default_features(c, "tmux", 0);
|
||||||
else if (strncmp(tmp, "XTerm(", 6) == 0)
|
else if (strncmp(tmp, "XTerm(", 6) == 0)
|
||||||
tty_default_features(features, "XTerm", 0);
|
tty_default_features(c, "XTerm", 0);
|
||||||
else if (strncmp(tmp, "mintty ", 7) == 0)
|
else if (strncmp(tmp, "mintty ", 7) == 0)
|
||||||
tty_default_features(features, "mintty", 0);
|
tty_default_features(c, "mintty", 0);
|
||||||
else if (strncmp(tmp, "foot(", 5) == 0)
|
else if (strncmp(tmp, "foot(", 5) == 0)
|
||||||
tty_default_features(features, "foot", 0);
|
tty_default_features(c, "foot", 0);
|
||||||
else if (strncmp(tmp, "WezTerm ", 7) == 0)
|
else if (strncmp(tmp, "WezTerm ", 7) == 0)
|
||||||
tty_default_features(features, "WezTerm", 0);
|
tty_default_features(c, "WezTerm", 0);
|
||||||
else if (strncmp(tmp, "ghostty ", 8) == 0)
|
else if (strncmp(tmp, "ghostty ", 8) == 0)
|
||||||
tty_default_features(features, "ghostty", 0);
|
tty_default_features(c, "ghostty", 0);
|
||||||
|
else if (strncmp(tmp, "Rio ", 4) == 0)
|
||||||
|
tty_default_features(c, "Rio", 0);
|
||||||
log_debug("%s: received extended DA %.*s", c->name, (int)*size, buf);
|
log_debug("%s: received extended DA %.*s", c->name, (int)*size, buf);
|
||||||
|
|
||||||
free(c->term_type);
|
free(c->term_type);
|
||||||
|
|||||||
43
tty-term.c
43
tty-term.c
@@ -1,4 +1,4 @@
|
|||||||
/* $OpenBSD: tty-term.c,v 1.106 2026/06/13 09:17:29 nicm Exp $ */
|
/* $OpenBSD: tty-term.c,v 1.109 2026/08/25 08:37:08 nicm Exp $ */
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2008 Nicholas Marriott <nicholas.marriott@gmail.com>
|
* Copyright (c) 2008 Nicholas Marriott <nicholas.marriott@gmail.com>
|
||||||
@@ -31,6 +31,7 @@
|
|||||||
#include "tmux.h"
|
#include "tmux.h"
|
||||||
|
|
||||||
static char *tty_term_strip(const char *);
|
static char *tty_term_strip(const char *);
|
||||||
|
static void tty_term_validate(struct tty_term *);
|
||||||
|
|
||||||
struct tty_terms tty_terms = LIST_HEAD_INITIALIZER(tty_terms);
|
struct tty_terms tty_terms = LIST_HEAD_INITIALIZER(tty_terms);
|
||||||
|
|
||||||
@@ -110,6 +111,7 @@ static const struct tty_term_code_entry tty_term_codes[] = {
|
|||||||
[TTYC_ICH] = { TTYCODE_STRING, "ich" },
|
[TTYC_ICH] = { TTYCODE_STRING, "ich" },
|
||||||
[TTYC_IL1] = { TTYCODE_STRING, "il1" },
|
[TTYC_IL1] = { TTYCODE_STRING, "il1" },
|
||||||
[TTYC_IL] = { TTYCODE_STRING, "il" },
|
[TTYC_IL] = { TTYCODE_STRING, "il" },
|
||||||
|
[TTYC_IND] = { TTYCODE_STRING, "ind" },
|
||||||
[TTYC_INDN] = { TTYCODE_STRING, "indn" },
|
[TTYC_INDN] = { TTYCODE_STRING, "indn" },
|
||||||
[TTYC_INVIS] = { TTYCODE_STRING, "invis" },
|
[TTYC_INVIS] = { TTYCODE_STRING, "invis" },
|
||||||
[TTYC_KCBT] = { TTYCODE_STRING, "kcbt" },
|
[TTYC_KCBT] = { TTYCODE_STRING, "kcbt" },
|
||||||
@@ -519,12 +521,33 @@ tty_term_apply_overrides(struct tty_term *term)
|
|||||||
acs = "a#j+k+l+m+n+o-p-q-r-s-t+u+v+w+x|y<z>~.";
|
acs = "a#j+k+l+m+n+o-p-q-r-s-t+u+v+w+x|y<z>~.";
|
||||||
for (; acs[0] != '\0' && acs[1] != '\0'; acs += 2)
|
for (; acs[0] != '\0' && acs[1] != '\0'; acs += 2)
|
||||||
term->acs[(u_char) acs[0]][0] = acs[1];
|
term->acs[(u_char) acs[0]][0] = acs[1];
|
||||||
|
|
||||||
|
tty_term_validate(term);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
tty_term_validate(struct tty_term *term)
|
||||||
|
{
|
||||||
|
struct tty_code *code = &term->codes[TTYC_MS];
|
||||||
|
|
||||||
|
if (code->type != TTYCODE_STRING)
|
||||||
|
return;
|
||||||
|
if (*tty_term_string_ss(term, TTYC_MS, "c", "?") != '\0') {
|
||||||
|
term->flags &= ~TERM_INVALIDMS;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
log_debug("removing invalid Ms capability");
|
||||||
|
term->flags |= TERM_INVALIDMS;
|
||||||
|
free(code->value.string);
|
||||||
|
code->type = TTYCODE_NONE;
|
||||||
}
|
}
|
||||||
|
|
||||||
struct tty_term *
|
struct tty_term *
|
||||||
tty_term_create(struct tty *tty, char *name, char **caps, u_int ncaps,
|
tty_term_create(struct tty *tty, char *name, char **caps, u_int ncaps,
|
||||||
int *feat, char **cause)
|
char **cause)
|
||||||
{
|
{
|
||||||
|
struct client *c = tty->client;
|
||||||
struct tty_term *term;
|
struct tty_term *term;
|
||||||
const struct tty_term_code_entry *ent;
|
const struct tty_term_code_entry *ent;
|
||||||
struct tty_code *code;
|
struct tty_code *code;
|
||||||
@@ -596,7 +619,7 @@ tty_term_create(struct tty *tty, char *name, char **caps, u_int ncaps,
|
|||||||
offset = 0;
|
offset = 0;
|
||||||
first = tty_term_override_next(s, &offset);
|
first = tty_term_override_next(s, &offset);
|
||||||
if (first != NULL && fnmatch(first, term->name, 0) == 0)
|
if (first != NULL && fnmatch(first, term->name, 0) == 0)
|
||||||
tty_add_features(feat, s + offset, ":");
|
tty_parse_client_features(c, s + offset, ":");
|
||||||
a = options_array_next(a);
|
a = options_array_next(a);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -606,14 +629,14 @@ tty_term_create(struct tty *tty, char *name, char **caps, u_int ncaps,
|
|||||||
del_curterm(cur_term);
|
del_curterm(cur_term);
|
||||||
#endif
|
#endif
|
||||||
/* Check for COLORTERM. */
|
/* Check for COLORTERM. */
|
||||||
envent = environ_find(tty->client->environ, "COLORTERM");
|
envent = environ_find(c->environ, "COLORTERM");
|
||||||
if (envent != NULL) {
|
if (envent != NULL) {
|
||||||
log_debug("%s COLORTERM=%s", tty->client->name, envent->value);
|
log_debug("%s COLORTERM=%s", c->name, envent->value);
|
||||||
if (strcasecmp(envent->value, "truecolor") == 0 ||
|
if (strcasecmp(envent->value, "truecolor") == 0 ||
|
||||||
strcasecmp(envent->value, "24bit") == 0)
|
strcasecmp(envent->value, "24bit") == 0)
|
||||||
tty_add_features(feat, "RGB", ",");
|
tty_parse_client_features(c, "RGB", ",");
|
||||||
else if (strstr(envent->value, "256") != NULL)
|
else if (strstr(envent->value, "256") != NULL)
|
||||||
tty_add_features(feat, "256", ",");
|
tty_parse_client_features(c, "256", ",");
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Apply overrides so any capabilities used for features are changed. */
|
/* Apply overrides so any capabilities used for features are changed. */
|
||||||
@@ -644,17 +667,17 @@ tty_term_create(struct tty *tty, char *name, char **caps, u_int ncaps,
|
|||||||
s = tty_term_string(term, TTYC_CLEAR);
|
s = tty_term_string(term, TTYC_CLEAR);
|
||||||
if (tty_term_flag(term, TTYC_XT) || strncmp(s, "\033[", 2) == 0) {
|
if (tty_term_flag(term, TTYC_XT) || strncmp(s, "\033[", 2) == 0) {
|
||||||
term->flags |= TERM_VT100LIKE;
|
term->flags |= TERM_VT100LIKE;
|
||||||
tty_add_features(feat, "bpaste,focus,title", ",");
|
tty_parse_client_features(c, "bpaste,focus,title", ",");
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Add RGB feature if terminal has RGB colours. */
|
/* Add RGB feature if terminal has RGB colours. */
|
||||||
if ((tty_term_flag(term, TTYC_TC) || tty_term_has(term, TTYC_RGB)) &&
|
if ((tty_term_flag(term, TTYC_TC) || tty_term_has(term, TTYC_RGB)) &&
|
||||||
(!tty_term_has(term, TTYC_SETRGBF) ||
|
(!tty_term_has(term, TTYC_SETRGBF) ||
|
||||||
!tty_term_has(term, TTYC_SETRGBB)))
|
!tty_term_has(term, TTYC_SETRGBB)))
|
||||||
tty_add_features(feat, "RGB", ",");
|
tty_parse_client_features(c, "RGB", ",");
|
||||||
|
|
||||||
/* Apply the features and overrides again. */
|
/* Apply the features and overrides again. */
|
||||||
if (tty_apply_features(term, *feat))
|
if (tty_apply_features(term))
|
||||||
tty_term_apply_overrides(term);
|
tty_term_apply_overrides(term);
|
||||||
|
|
||||||
/* Log the capabilities. */
|
/* Log the capabilities. */
|
||||||
|
|||||||
203
tty.c
203
tty.c
@@ -1,4 +1,4 @@
|
|||||||
/* $OpenBSD: tty.c,v 1.477 2026/07/17 12:42:51 nicm Exp $ */
|
/* $OpenBSD: tty.c,v 1.480 2026/08/25 08:37:08 nicm Exp $ */
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>
|
* Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>
|
||||||
@@ -68,6 +68,11 @@ static void tty_emulate_repeat(struct tty *, enum tty_code_code,
|
|||||||
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);
|
static int tty_check_overlay(struct tty *, u_int, u_int);
|
||||||
|
|
||||||
|
#ifdef ENABLE_SIXEL
|
||||||
|
static void tty_write_one(void (*)(struct tty *, const struct tty_ctx *),
|
||||||
|
struct client *, struct tty_ctx *);
|
||||||
|
#endif
|
||||||
|
|
||||||
#define tty_use_margin(tty) \
|
#define tty_use_margin(tty) \
|
||||||
(tty->term->flags & TERM_DECSLRM)
|
(tty->term->flags & TERM_DECSLRM)
|
||||||
#define tty_full_width(tty, ctx) \
|
#define tty_full_width(tty, ctx) \
|
||||||
@@ -158,21 +163,10 @@ tty_resize(struct tty *tty)
|
|||||||
void
|
void
|
||||||
tty_set_size(struct tty *tty, u_int sx, u_int sy, u_int xpixel, u_int ypixel)
|
tty_set_size(struct tty *tty, u_int sx, u_int sy, u_int xpixel, u_int ypixel)
|
||||||
{
|
{
|
||||||
#ifdef ENABLE_IMAGES
|
|
||||||
int geometry_changed;
|
|
||||||
|
|
||||||
geometry_changed = (tty->xpixel != xpixel || tty->ypixel != ypixel);
|
|
||||||
#endif
|
|
||||||
tty->sx = sx;
|
tty->sx = sx;
|
||||||
tty->sy = sy;
|
tty->sy = sy;
|
||||||
tty->xpixel = xpixel;
|
tty->xpixel = xpixel;
|
||||||
tty->ypixel = ypixel;
|
tty->ypixel = ypixel;
|
||||||
#ifdef ENABLE_IMAGES
|
|
||||||
if (geometry_changed) {
|
|
||||||
image_tty_geometry_changed(tty);
|
|
||||||
server_redraw_client(tty->client);
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
@@ -283,7 +277,7 @@ tty_open(struct tty *tty, char **cause)
|
|||||||
struct client *c = tty->client;
|
struct client *c = tty->client;
|
||||||
|
|
||||||
tty->term = tty_term_create(tty, c->term_name, c->term_caps,
|
tty->term = tty_term_create(tty, c->term_name, c->term_caps,
|
||||||
c->term_ncaps, &c->term_features, cause);
|
c->term_ncaps, cause);
|
||||||
if (tty->term == NULL) {
|
if (tty->term == NULL) {
|
||||||
tty_close(tty);
|
tty_close(tty);
|
||||||
return (-1);
|
return (-1);
|
||||||
@@ -344,6 +338,7 @@ tty_start_tty(struct tty *tty)
|
|||||||
{
|
{
|
||||||
struct client *c = tty->client;
|
struct client *c = tty->client;
|
||||||
struct termios tio;
|
struct termios tio;
|
||||||
|
u_int i;
|
||||||
|
|
||||||
setblocking(c->fd, 0);
|
setblocking(c->fd, 0);
|
||||||
event_add(&tty->event_in, NULL);
|
event_add(&tty->event_in, NULL);
|
||||||
@@ -359,10 +354,21 @@ tty_start_tty(struct tty *tty)
|
|||||||
if (tcsetattr(c->fd, TCSANOW, &tio) == 0)
|
if (tcsetattr(c->fd, TCSANOW, &tio) == 0)
|
||||||
tcflush(c->fd, TCOFLUSH);
|
tcflush(c->fd, TCOFLUSH);
|
||||||
|
|
||||||
|
if (options_get_number(global_options, "clear-on-attach")) {
|
||||||
tty_putcode(tty, TTYC_SMCUP);
|
tty_putcode(tty, TTYC_SMCUP);
|
||||||
|
|
||||||
tty_putcode(tty, TTYC_SMKX);
|
|
||||||
tty_putcode(tty, TTYC_CLEAR);
|
tty_putcode(tty, TTYC_CLEAR);
|
||||||
|
} else {
|
||||||
|
tty_putcode_ii(tty, TTYC_CSR, 0, tty->sy - 1);
|
||||||
|
tty_putcode_ii(tty, TTYC_CUP, 0, tty->sy - 1);
|
||||||
|
if (tty_term_has(tty->term, TTYC_INDN))
|
||||||
|
tty_putcode_i(tty, TTYC_INDN, tty->sy + 1);
|
||||||
|
else if (tty_term_has(tty->term, TTYC_IND)) {
|
||||||
|
for (i = 0; i < tty->sy + 1; i++)
|
||||||
|
tty_putcode(tty, TTYC_IND);
|
||||||
|
} else
|
||||||
|
tty_putcode(tty, TTYC_CLEAR);
|
||||||
|
}
|
||||||
|
tty_putcode(tty, TTYC_SMKX);
|
||||||
|
|
||||||
if (tty_acs_needed(tty)) {
|
if (tty_acs_needed(tty)) {
|
||||||
log_debug("%s: using capabilities for ACS", c->name);
|
log_debug("%s: using capabilities for ACS", c->name);
|
||||||
@@ -409,6 +415,8 @@ tty_send_requests(struct tty *tty)
|
|||||||
tty_puts(tty, "\033[>c");
|
tty_puts(tty, "\033[>c");
|
||||||
if (~tty->flags & TTY_HAVEXDA)
|
if (~tty->flags & TTY_HAVEXDA)
|
||||||
tty_puts(tty, "\033[>q");
|
tty_puts(tty, "\033[>q");
|
||||||
|
if (~tty->flags & TTY_HAVESYNC)
|
||||||
|
tty_puts(tty, "\033[?2026$p");
|
||||||
tty_puts(tty, "\033]10;?\033\\\033]11;?\033\\");
|
tty_puts(tty, "\033]10;?\033\\\033]11;?\033\\");
|
||||||
tty->flags |= (TTY_WAITBG|TTY_WAITFG);
|
tty->flags |= (TTY_WAITBG|TTY_WAITFG);
|
||||||
} else
|
} else
|
||||||
@@ -476,6 +484,7 @@ tty_stop_tty(struct tty *tty)
|
|||||||
tty_raw(tty, tty_term_string(tty->term, TTYC_RMACS));
|
tty_raw(tty, tty_term_string(tty->term, TTYC_RMACS));
|
||||||
tty_raw(tty, tty_term_string(tty->term, TTYC_SGR0));
|
tty_raw(tty, tty_term_string(tty->term, TTYC_SGR0));
|
||||||
tty_raw(tty, tty_term_string(tty->term, TTYC_RMKX));
|
tty_raw(tty, tty_term_string(tty->term, TTYC_RMKX));
|
||||||
|
if (options_get_number(global_options, "clear-on-attach"))
|
||||||
tty_raw(tty, tty_term_string(tty->term, TTYC_CLEAR));
|
tty_raw(tty, tty_term_string(tty->term, TTYC_CLEAR));
|
||||||
if (tty->cstyle != SCREEN_CURSOR_DEFAULT) {
|
if (tty->cstyle != SCREEN_CURSOR_DEFAULT) {
|
||||||
if (tty_term_has(tty->term, TTYC_SE))
|
if (tty_term_has(tty->term, TTYC_SE))
|
||||||
@@ -501,7 +510,10 @@ tty_stop_tty(struct tty *tty)
|
|||||||
|
|
||||||
if (tty_use_margin(tty))
|
if (tty_use_margin(tty))
|
||||||
tty_raw(tty, tty_term_string(tty->term, TTYC_DSMG));
|
tty_raw(tty, tty_term_string(tty->term, TTYC_DSMG));
|
||||||
|
if (options_get_number(global_options, "clear-on-attach"))
|
||||||
tty_raw(tty, tty_term_string(tty->term, TTYC_RMCUP));
|
tty_raw(tty, tty_term_string(tty->term, TTYC_RMCUP));
|
||||||
|
else
|
||||||
|
tty_raw(tty, tty_term_string(tty->term, TTYC_CLEAR));
|
||||||
|
|
||||||
if (tty->term->flags & TERM_VT100LIKE)
|
if (tty->term->flags & TERM_VT100LIKE)
|
||||||
tty_raw(tty, "\033[?2031l");
|
tty_raw(tty, "\033[?2031l");
|
||||||
@@ -517,9 +529,6 @@ tty_close(struct tty *tty)
|
|||||||
tty_stop_tty(tty);
|
tty_stop_tty(tty);
|
||||||
|
|
||||||
if (tty->flags & TTY_OPENED) {
|
if (tty->flags & TTY_OPENED) {
|
||||||
#ifdef ENABLE_IMAGES
|
|
||||||
image_tty_free(tty, 1);
|
|
||||||
#endif
|
|
||||||
evbuffer_free(tty->in);
|
evbuffer_free(tty->in);
|
||||||
event_del(&tty->event_in);
|
event_del(&tty->event_in);
|
||||||
evbuffer_free(tty->out);
|
evbuffer_free(tty->out);
|
||||||
@@ -536,9 +545,7 @@ void
|
|||||||
tty_free(struct tty *tty)
|
tty_free(struct tty *tty)
|
||||||
{
|
{
|
||||||
tty_close(tty);
|
tty_close(tty);
|
||||||
#ifdef ENABLE_IMAGES
|
|
||||||
image_tty_free(tty, 0);
|
|
||||||
#endif
|
|
||||||
free(tty->r.ranges);
|
free(tty->r.ranges);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -547,11 +554,8 @@ tty_update_features(struct tty *tty)
|
|||||||
{
|
{
|
||||||
struct client *c = tty->client;
|
struct client *c = tty->client;
|
||||||
|
|
||||||
if (tty_apply_features(tty->term, c->term_features))
|
if (tty_apply_features(tty->term))
|
||||||
tty_term_apply_overrides(tty->term);
|
tty_term_apply_overrides(tty->term);
|
||||||
#ifdef ENABLE_IMAGES
|
|
||||||
image_tty_update(tty);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
if (tty_use_margin(tty))
|
if (tty_use_margin(tty))
|
||||||
tty_putcode(tty, TTYC_ENMG);
|
tty_putcode(tty, TTYC_ENMG);
|
||||||
@@ -951,7 +955,7 @@ int
|
|||||||
tty_window_bigger(struct tty *tty)
|
tty_window_bigger(struct tty *tty)
|
||||||
{
|
{
|
||||||
struct client *c = tty->client;
|
struct client *c = tty->client;
|
||||||
struct window *w = c->session->curw->window;
|
struct window *w = active_get_effective_window(c, c->session);
|
||||||
|
|
||||||
return (tty->sx < w->sx || tty->sy - status_line_size(c) < w->sy);
|
return (tty->sx < w->sx || tty->sy - status_line_size(c) < w->sy);
|
||||||
}
|
}
|
||||||
@@ -973,10 +977,11 @@ static int
|
|||||||
tty_window_offset1(struct tty *tty, u_int *ox, u_int *oy, u_int *sx, u_int *sy)
|
tty_window_offset1(struct tty *tty, u_int *ox, u_int *oy, u_int *sx, u_int *sy)
|
||||||
{
|
{
|
||||||
struct client *c = tty->client;
|
struct client *c = tty->client;
|
||||||
struct window *w = c->session->curw->window;
|
struct window *w = active_get_effective_window(c, c->session);
|
||||||
struct window_pane *wp = w->active;
|
struct window_pane *wp;
|
||||||
u_int cx, cy, lines;
|
u_int cx, cy, lines;
|
||||||
|
|
||||||
|
wp = active_get_effective_pane(c, w);
|
||||||
lines = status_line_size(c);
|
lines = status_line_size(c);
|
||||||
|
|
||||||
if (tty->sx >= w->sx && tty->sy - lines >= w->sy) {
|
if (tty->sx >= w->sx && tty->sy - lines >= w->sy) {
|
||||||
@@ -1040,8 +1045,7 @@ tty_update_window_offset(struct window *w)
|
|||||||
|
|
||||||
TAILQ_FOREACH(c, &clients, entry) {
|
TAILQ_FOREACH(c, &clients, entry) {
|
||||||
if (c->session != NULL &&
|
if (c->session != NULL &&
|
||||||
c->session->curw != NULL &&
|
active_get_effective_window(c, c->session) == w)
|
||||||
c->session->curw->window == w)
|
|
||||||
tty_update_client_offset(c);
|
tty_update_client_offset(c);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1536,6 +1540,60 @@ tty_check_overlay_range(struct tty *tty, u_int px, u_int py, u_int nx)
|
|||||||
return (c->overlay_check(c, c->overlay_data, px, py, nx));
|
return (c->overlay_check(c, c->overlay_data, px, py, nx));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef ENABLE_SIXEL
|
||||||
|
/* Update context for client. */
|
||||||
|
static int
|
||||||
|
tty_set_client_cb(struct tty_ctx *ttyctx, struct client *c)
|
||||||
|
{
|
||||||
|
struct window_pane *wp = ttyctx->arg;
|
||||||
|
|
||||||
|
if (active_get_effective_window(c, c->session) != wp->window)
|
||||||
|
return (0);
|
||||||
|
if (wp->layout_cell == NULL)
|
||||||
|
return (0);
|
||||||
|
|
||||||
|
if (tty_window_offset(&c->tty, &ttyctx->wox, &ttyctx->woy, &ttyctx->wsx,
|
||||||
|
&ttyctx->wsy))
|
||||||
|
ttyctx->flags |= TTY_CTX_WINDOW_BIGGER;
|
||||||
|
else
|
||||||
|
ttyctx->flags &= ~TTY_CTX_WINDOW_BIGGER;
|
||||||
|
|
||||||
|
ttyctx->yoff = ttyctx->ryoff = wp->yoff;
|
||||||
|
if (status_at_line(c) == 0)
|
||||||
|
ttyctx->yoff += status_line_size(c);
|
||||||
|
|
||||||
|
return (1);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
tty_draw_images(struct client *c, struct window_pane *wp)
|
||||||
|
{
|
||||||
|
struct image *im;
|
||||||
|
struct tty_ctx ttyctx;
|
||||||
|
|
||||||
|
TAILQ_FOREACH(im, &wp->screen->images, entry) {
|
||||||
|
memset(&ttyctx, 0, sizeof ttyctx);
|
||||||
|
|
||||||
|
/* Set the client independent properties. */
|
||||||
|
ttyctx.ocx = im->px;
|
||||||
|
ttyctx.ocy = im->py;
|
||||||
|
|
||||||
|
ttyctx.orlower = wp->screen->rlower;
|
||||||
|
ttyctx.orupper = wp->screen->rupper;
|
||||||
|
|
||||||
|
ttyctx.xoff = ttyctx.rxoff = wp->xoff;
|
||||||
|
ttyctx.sx = wp->sx;
|
||||||
|
ttyctx.sy = wp->sy;
|
||||||
|
|
||||||
|
ttyctx.image = im;
|
||||||
|
ttyctx.arg = wp;
|
||||||
|
ttyctx.set_client_cb = tty_set_client_cb;
|
||||||
|
ttyctx.flags |= TTY_CTX_INVISIBLE_PANES;
|
||||||
|
tty_write_one(tty_cmd_sixelimage, c, &ttyctx);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
void
|
void
|
||||||
tty_sync_start(struct tty *tty)
|
tty_sync_start(struct tty *tty)
|
||||||
{
|
{
|
||||||
@@ -1609,6 +1667,19 @@ tty_write(void (*cmdfn)(struct tty *, const struct tty_ctx *),
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef ENABLE_SIXEL
|
||||||
|
/* Only write to the incoming tty instead of every client. */
|
||||||
|
static void
|
||||||
|
tty_write_one(void (*cmdfn)(struct tty *, const struct tty_ctx *),
|
||||||
|
struct client *c, struct tty_ctx *ctx)
|
||||||
|
{
|
||||||
|
if (ctx->set_client_cb == NULL)
|
||||||
|
return;
|
||||||
|
if ((ctx->set_client_cb(ctx, c)) == 1)
|
||||||
|
cmdfn(&c->tty, ctx);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
void
|
void
|
||||||
tty_cmd_insertcharacter(struct tty *tty, const struct tty_ctx *ctx)
|
tty_cmd_insertcharacter(struct tty *tty, const struct tty_ctx *ctx)
|
||||||
{
|
{
|
||||||
@@ -2102,6 +2173,58 @@ tty_cmd_rawstring(struct tty *tty, const struct tty_ctx *ctx)
|
|||||||
tty_invalidate(tty);
|
tty_invalidate(tty);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef ENABLE_SIXEL
|
||||||
|
void
|
||||||
|
tty_cmd_sixelimage(struct tty *tty, const struct tty_ctx *ctx)
|
||||||
|
{
|
||||||
|
struct image *im = ctx->image;
|
||||||
|
struct sixel_image *si = im->data;
|
||||||
|
struct sixel_image *new;
|
||||||
|
char *data;
|
||||||
|
size_t size;
|
||||||
|
u_int cx = ctx->ocx, cy = ctx->ocy, sx, sy;
|
||||||
|
u_int i, j, x, y, rx, ry;
|
||||||
|
int fallback = 0;
|
||||||
|
|
||||||
|
if ((~tty->term->flags & TERM_SIXEL) &&
|
||||||
|
!tty_term_has(tty->term, TTYC_SXL))
|
||||||
|
fallback = 1;
|
||||||
|
if (tty->xpixel == 0 || tty->ypixel == 0)
|
||||||
|
fallback = 1;
|
||||||
|
|
||||||
|
sixel_size_in_cells(si, &sx, &sy);
|
||||||
|
log_debug("%s: image is %ux%u", __func__, sx, sy);
|
||||||
|
if (!tty_clamp_area(tty, ctx, cx, cy, sx, sy, &i, &j, &x, &y, &rx, &ry))
|
||||||
|
return;
|
||||||
|
log_debug("%s: clamping to %u,%u-%u,%u", __func__, i, j, rx, ry);
|
||||||
|
|
||||||
|
if (fallback == 1) {
|
||||||
|
data = xstrdup(im->fallback);
|
||||||
|
size = strlen(data);
|
||||||
|
} else {
|
||||||
|
new = sixel_scale(si, tty->xpixel, tty->ypixel, i, j, rx, ry, 0);
|
||||||
|
if (new == NULL)
|
||||||
|
return;
|
||||||
|
|
||||||
|
data = sixel_print(new, si, &size);
|
||||||
|
}
|
||||||
|
if (data != NULL) {
|
||||||
|
log_debug("%s: %zu bytes: %s", __func__, size, data);
|
||||||
|
tty_region_off(tty);
|
||||||
|
tty_margin_off(tty);
|
||||||
|
tty_cursor(tty, x, y);
|
||||||
|
|
||||||
|
tty->flags |= TTY_NOBLOCK;
|
||||||
|
tty_add(tty, data, size);
|
||||||
|
tty_invalidate(tty);
|
||||||
|
free(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fallback == 0)
|
||||||
|
sixel_free(new);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
void
|
void
|
||||||
tty_cmd_syncstart(struct tty *tty, const struct tty_ctx *ctx)
|
tty_cmd_syncstart(struct tty *tty, const struct tty_ctx *ctx)
|
||||||
{
|
{
|
||||||
@@ -2129,9 +2252,6 @@ tty_cell(struct tty *tty, const struct grid_cell *gc,
|
|||||||
const struct tty_style_ctx *style_ctx)
|
const struct tty_style_ctx *style_ctx)
|
||||||
{
|
{
|
||||||
const struct grid_cell *gcp;
|
const struct grid_cell *gcp;
|
||||||
#ifdef ENABLE_IMAGES
|
|
||||||
struct grid_cell image_gc;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/* Skip last character if terminal is stupid. */
|
/* Skip last character if terminal is stupid. */
|
||||||
if ((tty->term->flags & TERM_NOAM) &&
|
if ((tty->term->flags & TERM_NOAM) &&
|
||||||
@@ -2147,14 +2267,6 @@ tty_cell(struct tty *tty, const struct grid_cell *gc,
|
|||||||
if (!tty_check_overlay(tty, tty->cx, tty->cy))
|
if (!tty_check_overlay(tty, tty->cx, tty->cy))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
#ifdef ENABLE_IMAGES
|
|
||||||
if (gc->flags & GRID_FLAG_IMAGE) {
|
|
||||||
if (image_get_draw_cell(tty, gc, &image_gc, style_ctx))
|
|
||||||
return;
|
|
||||||
gc = &image_gc;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/* 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);
|
||||||
@@ -3047,23 +3159,24 @@ tty_style_changed(struct window_pane *wp)
|
|||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
tty_default_colours(struct grid_cell *gc, struct window_pane *wp, u_int *dim)
|
tty_default_colours(struct grid_cell *gc, struct window_pane *wp,
|
||||||
|
struct window_pane *active, u_int *dim)
|
||||||
{
|
{
|
||||||
if (wp->flags & PANE_STYLECHANGED)
|
if (wp->flags & PANE_STYLECHANGED)
|
||||||
tty_style_changed (wp);
|
tty_style_changed (wp);
|
||||||
|
|
||||||
memcpy(gc, &grid_default_cell, sizeof *gc);
|
memcpy(gc, &grid_default_cell, sizeof *gc);
|
||||||
if (wp == wp->window->active && wp->cached_active_gc.fg != 8)
|
if (wp == active && wp->cached_active_gc.fg != 8)
|
||||||
gc->fg = wp->cached_active_gc.fg;
|
gc->fg = wp->cached_active_gc.fg;
|
||||||
else
|
else
|
||||||
gc->fg = wp->cached_gc.fg;
|
gc->fg = wp->cached_gc.fg;
|
||||||
if (wp == wp->window->active && wp->cached_active_gc.bg != 8)
|
if (wp == active && wp->cached_active_gc.bg != 8)
|
||||||
gc->bg = wp->cached_active_gc.bg;
|
gc->bg = wp->cached_active_gc.bg;
|
||||||
else
|
else
|
||||||
gc->bg = wp->cached_gc.bg;
|
gc->bg = wp->cached_gc.bg;
|
||||||
|
|
||||||
if (dim != NULL) {
|
if (dim != NULL) {
|
||||||
if (wp == wp->window->active)
|
if (wp == active)
|
||||||
*dim = wp->cached_active_dim;
|
*dim = wp->cached_active_dim;
|
||||||
else
|
else
|
||||||
*dim = wp->cached_dim;
|
*dim = wp->cached_dim;
|
||||||
|
|||||||
68
utf8.c
68
utf8.c
@@ -1,4 +1,4 @@
|
|||||||
/* $OpenBSD: utf8.c,v 1.71 2026/05/12 09:37:25 nicm Exp $ */
|
/* $OpenBSD: utf8.c,v 1.72 2026/09/01 12:49:49 nicm Exp $ */
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2008 Nicholas Marriott <nicholas.marriott@gmail.com>
|
* Copyright (c) 2008 Nicholas Marriott <nicholas.marriott@gmail.com>
|
||||||
@@ -603,6 +603,72 @@ utf8_towc(const struct utf8_data *ud, wchar_t *wc)
|
|||||||
return (UTF8_DONE);
|
return (UTF8_DONE);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Check for a Unicode whitespace character. */
|
||||||
|
int
|
||||||
|
utf8_has_whitespace(const struct utf8_data *ud)
|
||||||
|
{
|
||||||
|
struct utf8_data tmp;
|
||||||
|
wchar_t wc;
|
||||||
|
u_int offset = 0, size;
|
||||||
|
u_char ch;
|
||||||
|
|
||||||
|
while (offset < ud->size) {
|
||||||
|
ch = ud->data[offset];
|
||||||
|
if (ch < 0x80) {
|
||||||
|
wc = ch;
|
||||||
|
size = 1;
|
||||||
|
} else {
|
||||||
|
if (ch >= 0xc2 && ch <= 0xdf)
|
||||||
|
size = 2;
|
||||||
|
else if (ch >= 0xe0 && ch <= 0xef)
|
||||||
|
size = 3;
|
||||||
|
else if (ch >= 0xf0 && ch <= 0xf4)
|
||||||
|
size = 4;
|
||||||
|
else
|
||||||
|
return (0);
|
||||||
|
if (size > ud->size - offset)
|
||||||
|
return (0);
|
||||||
|
|
||||||
|
memset(&tmp, 0, sizeof tmp);
|
||||||
|
memcpy(tmp.data, ud->data + offset, size);
|
||||||
|
tmp.size = tmp.have = size;
|
||||||
|
if (utf8_towc(&tmp, &wc) != UTF8_DONE)
|
||||||
|
return (0);
|
||||||
|
}
|
||||||
|
offset += size;
|
||||||
|
|
||||||
|
switch (wc) {
|
||||||
|
case 0x0009:
|
||||||
|
case 0x000A:
|
||||||
|
case 0x000B:
|
||||||
|
case 0x000C:
|
||||||
|
case 0x000D:
|
||||||
|
case 0x0020:
|
||||||
|
case 0x0085:
|
||||||
|
case 0x00A0:
|
||||||
|
case 0x1680:
|
||||||
|
case 0x2000:
|
||||||
|
case 0x2001:
|
||||||
|
case 0x2002:
|
||||||
|
case 0x2003:
|
||||||
|
case 0x2004:
|
||||||
|
case 0x2005:
|
||||||
|
case 0x2006:
|
||||||
|
case 0x2007:
|
||||||
|
case 0x2008:
|
||||||
|
case 0x2009:
|
||||||
|
case 0x200A:
|
||||||
|
case 0x2028:
|
||||||
|
case 0x2029:
|
||||||
|
case 0x202F:
|
||||||
|
case 0x205F:
|
||||||
|
case 0x3000:
|
||||||
|
return (1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return (0);
|
||||||
|
}
|
||||||
|
|
||||||
/* Convert wide character to UTF-8 character. */
|
/* Convert wide character to UTF-8 character. */
|
||||||
enum utf8_state
|
enum utf8_state
|
||||||
utf8_fromwc(wchar_t wc, struct utf8_data *ud)
|
utf8_fromwc(wchar_t wc, struct utf8_data *ud)
|
||||||
|
|||||||
@@ -155,9 +155,14 @@ window_pane_get_border_style(struct window_pane *wp, struct client *c,
|
|||||||
struct format_tree *ft;
|
struct format_tree *ft;
|
||||||
const char *option;
|
const char *option;
|
||||||
struct grid_cell *saved;
|
struct grid_cell *saved;
|
||||||
int *flag;
|
struct window *w;
|
||||||
|
struct window_pane *active;
|
||||||
|
int local, *flag;
|
||||||
|
|
||||||
if (wp == c->session->curw->window->active) {
|
w = active_get_effective_window(c, c->session);
|
||||||
|
active = active_get_effective_pane(c, w);
|
||||||
|
local = (wp == active && active_has_local_pane(c, wp->window));
|
||||||
|
if (wp == active) {
|
||||||
flag = &wp->active_border_gc_set;
|
flag = &wp->active_border_gc_set;
|
||||||
saved = &wp->active_border_gc;
|
saved = &wp->active_border_gc;
|
||||||
option = "pane-active-border-style";
|
option = "pane-active-border-style";
|
||||||
@@ -167,8 +172,15 @@ window_pane_get_border_style(struct window_pane *wp, struct client *c,
|
|||||||
option = "pane-border-style";
|
option = "pane-border-style";
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!*flag) {
|
if (local) {
|
||||||
ft = format_create_defaults(NULL, c, s, s->curw, wp);
|
ft = format_create_defaults(NULL, c, s, s->curw, wp);
|
||||||
|
style_apply(gc, wp->options, option, ft);
|
||||||
|
format_free(ft);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!*flag) {
|
||||||
|
ft = format_create_defaults(NULL, c, s, NULL, wp);
|
||||||
style_apply(saved, wp->options, option, ft);
|
style_apply(saved, wp->options, option, ft);
|
||||||
format_free(ft);
|
format_free(ft);
|
||||||
*flag = 1;
|
*flag = 1;
|
||||||
@@ -197,7 +209,7 @@ window_make_pane_status(struct window_pane *wp, struct client *c, u_int width,
|
|||||||
return (0);
|
return (0);
|
||||||
|
|
||||||
ft = format_create(c, NULL, FORMAT_PANE|wp->id, FORMAT_STATUS);
|
ft = format_create(c, NULL, FORMAT_PANE|wp->id, FORMAT_STATUS);
|
||||||
format_defaults(ft, c, c->session, c->session->curw, wp);
|
format_defaults(ft, c, c->session, NULL, wp);
|
||||||
|
|
||||||
fmt = options_get_string(wp->options, "pane-border-format");
|
fmt = options_get_string(wp->options, "pane-border-format");
|
||||||
expanded = format_expand_time(ft, fmt);
|
expanded = format_expand_time(ft, fmt);
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
/* $OpenBSD: window-buffer.c,v 1.51 2026/07/15 12:45:39 nicm Exp $ */
|
/* $OpenBSD: window-buffer.c,v 1.52 2026/08/24 21:19:40 nicm Exp $ */
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2017 Nicholas Marriott <nicholas.marriott@gmail.com>
|
* Copyright (c) 2017 Nicholas Marriott <nicholas.marriott@gmail.com>
|
||||||
@@ -567,9 +567,7 @@ window_buffer_edit_close_cb(char *buf, size_t len, void *arg)
|
|||||||
}
|
}
|
||||||
|
|
||||||
oldbuf = paste_buffer_data(pb, &oldlen);
|
oldbuf = paste_buffer_data(pb, &oldlen);
|
||||||
if (oldlen != '\0' &&
|
if (oldlen != 0 && oldbuf[oldlen - 1] != '\n' && buf[len - 1] == '\n')
|
||||||
oldbuf[oldlen - 1] != '\n' &&
|
|
||||||
buf[len - 1] == '\n')
|
|
||||||
len--;
|
len--;
|
||||||
if (len != 0)
|
if (len != 0)
|
||||||
paste_replace(pb, buf, len);
|
paste_replace(pb, buf, len);
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
/* $OpenBSD: window-client.c,v 1.47 2026/07/14 17:17:18 nicm Exp $ */
|
/* $OpenBSD: window-client.c,v 1.49 2026/08/31 12:41:03 kirill Exp $ */
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2017 Nicholas Marriott <nicholas.marriott@gmail.com>
|
* Copyright (c) 2017 Nicholas Marriott <nicholas.marriott@gmail.com>
|
||||||
@@ -102,7 +102,8 @@ static const char *window_client_info_lines[] = {
|
|||||||
WINDOW_CLIENT_FEATURE(sync) " "
|
WINDOW_CLIENT_FEATURE(sync) " "
|
||||||
WINDOW_CLIENT_FEATURE(title),
|
WINDOW_CLIENT_FEATURE(title),
|
||||||
" #[#{E:tree-mode-border-style},acs]x#[default] "
|
" #[#{E:tree-mode-border-style},acs]x#[default] "
|
||||||
WINDOW_CLIENT_FEATURE(usstyle),
|
WINDOW_CLIENT_FEATURE(usstyle) " "
|
||||||
|
WINDOW_CLIENT_FEATURE(utf8),
|
||||||
"#[#{E:tree-mode-border-style},acs]qqqqqqqqqqqqqqn#{R:q,#{window_width}}#[default]",
|
"#[#{E:tree-mode-border-style},acs]qqqqqqqqqqqqqqn#{R:q,#{window_width}}#[default]",
|
||||||
|
|
||||||
"#[fg=themelightgrey]prefix #[#{E:tree-mode-border-style},acs]x#[default] "
|
"#[fg=themelightgrey]prefix #[#{E:tree-mode-border-style},acs]x#[default] "
|
||||||
@@ -113,14 +114,16 @@ static const char *window_client_info_lines[] = {
|
|||||||
"#{?#{I/c:kmous},,#[align=right]unavailable: [kmous] missing}",
|
"#{?#{I/c:kmous},,#[align=right]unavailable: [kmous] missing}",
|
||||||
|
|
||||||
"#[fg=themelightgrey]set-clipboard #[#{E:tree-mode-border-style},acs]x#[default] "
|
"#[fg=themelightgrey]set-clipboard #[#{E:tree-mode-border-style},acs]x#[default] "
|
||||||
"#{?#{!=:#{set-clipboard},off},#{?#{I/f:clipboard},,"
|
"#{?#{!=:#{set-clipboard},off},#{?#{I/c:Ms},,"
|
||||||
"#[fg=themered]}#{set-clipboard},#[fg=themelightgrey]off} "
|
"#[fg=themered]}#{set-clipboard},#[fg=themelightgrey]off} "
|
||||||
"#{?#{I/f:clipboard},,#[align=right]unavailable: [Ms] missing}",
|
"#{?#{I/c:Ms},,#[align=right]unavailable: [Ms] "
|
||||||
|
"#{?clipboard_invalid,invalid,missing}}",
|
||||||
|
|
||||||
"#[fg=themelightgrey]get-clipboard #[#{E:tree-mode-border-style},acs]x#[default] "
|
"#[fg=themelightgrey]get-clipboard #[#{E:tree-mode-border-style},acs]x#[default] "
|
||||||
"#{?#{!=:#{get-clipboard},off},#{?#{I/f:clipboard},,"
|
"#{?#{!=:#{get-clipboard},off},#{?#{I/c:Ms},,"
|
||||||
"#[fg=themered]}#{get-clipboard},#[fg=themelightgrey]off} "
|
"#[fg=themered]}#{get-clipboard},#[fg=themelightgrey]off} "
|
||||||
"#{?#{I/f:clipboard},,#[align=right]unavailable: [Ms] missing}",
|
"#{?#{I/c:Ms},,#[align=right]unavailable: [Ms] "
|
||||||
|
"#{?clipboard_invalid,invalid,missing}}",
|
||||||
|
|
||||||
"#[fg=themelightgrey]focus-events #[#{E:tree-mode-border-style},acs]x#[default] "
|
"#[fg=themelightgrey]focus-events #[#{E:tree-mode-border-style},acs]x#[default] "
|
||||||
"#{?focus-events,#{?#{I/f:focus},,#[fg=themered]}on,#[fg=themelightgrey]off} "
|
"#{?focus-events,#{?#{I/f:focus},,#[fg=themered]}on,#[fg=themelightgrey]off} "
|
||||||
@@ -266,13 +269,18 @@ window_client_draw_info(__unused void *modedata, void *itemdata,
|
|||||||
struct window_client_itemdata *item = itemdata;
|
struct window_client_itemdata *item = itemdata;
|
||||||
struct client *c = item->c;
|
struct client *c = item->c;
|
||||||
struct screen *s = ctx->s;
|
struct screen *s = ctx->s;
|
||||||
struct window *w = c->session->curw->window;
|
struct window *w;
|
||||||
struct grid_cell gc;
|
struct grid_cell gc;
|
||||||
u_int cx = s->cx, cy = s->cy, i;
|
u_int cx = s->cx, cy = s->cy, i;
|
||||||
struct format_tree *ft;
|
struct format_tree *ft;
|
||||||
char *expanded;
|
char *expanded;
|
||||||
|
|
||||||
|
w = active_get_effective_window(c, c->session);
|
||||||
ft = format_create_defaults(NULL, c, NULL, NULL, NULL);
|
ft = format_create_defaults(NULL, c, NULL, NULL, NULL);
|
||||||
|
if (c->tty.term->flags & TERM_INVALIDMS)
|
||||||
|
format_add(ft, "clipboard_invalid", "1");
|
||||||
|
else
|
||||||
|
format_add(ft, "clipboard_invalid", "0");
|
||||||
|
|
||||||
screen_write_cursormove(ctx, cx, cy, 0);
|
screen_write_cursormove(ctx, cx, cy, 0);
|
||||||
for (i = 0; i < nitems(window_client_info_lines); i++) {
|
for (i = 0; i < nitems(window_client_info_lines); i++) {
|
||||||
|
|||||||
124
window-copy.c
124
window-copy.c
@@ -1,4 +1,4 @@
|
|||||||
/* $OpenBSD: window-copy.c,v 1.424 2026/07/29 17:42:56 nicm Exp $ */
|
/* $OpenBSD: window-copy.c,v 1.429 2026/09/01 13:04:29 nicm Exp $ */
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>
|
* Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>
|
||||||
@@ -54,6 +54,7 @@ static void window_copy_redraw_screen(struct window_mode_entry *);
|
|||||||
static void window_copy_do_refresh(struct window_mode_entry *, int);
|
static void window_copy_do_refresh(struct window_mode_entry *, int);
|
||||||
static void window_copy_refresh_timer(int, short, void *);
|
static void window_copy_refresh_timer(int, short, void *);
|
||||||
static void window_copy_refresh_arm(struct window_mode_entry *);
|
static void window_copy_refresh_arm(struct window_mode_entry *);
|
||||||
|
static int window_copy_refresh_allowed(struct window_mode_entry *);
|
||||||
static void window_copy_refresh_start(struct window_mode_entry *);
|
static void window_copy_refresh_start(struct window_mode_entry *);
|
||||||
static void window_copy_refresh_stop(struct window_mode_entry *);
|
static void window_copy_refresh_stop(struct window_mode_entry *);
|
||||||
static void window_copy_style_changed(struct window_mode_entry *);
|
static void window_copy_style_changed(struct window_mode_entry *);
|
||||||
@@ -2206,15 +2207,76 @@ window_copy_cmd_selection_mode(struct window_copy_cmd_state *cs)
|
|||||||
struct window_mode_entry *wme = cs->wme;
|
struct window_mode_entry *wme = cs->wme;
|
||||||
struct options *so = cs->s->options;
|
struct options *so = cs->s->options;
|
||||||
struct window_copy_mode_data *data = wme->data;
|
struct window_copy_mode_data *data = wme->data;
|
||||||
|
struct grid_reader gr;
|
||||||
const char *s = args_string(cs->wargs, 0);
|
const char *s = args_string(cs->wargs, 0);
|
||||||
|
u_int sx, sy, ex, ey, fx, fy, x, y;
|
||||||
|
|
||||||
if (s == NULL || strcasecmp(s, "char") == 0 || strcasecmp(s, "c") == 0)
|
if (s == NULL || strcasecmp(s, "char") == 0 || strcasecmp(s, "c") == 0)
|
||||||
data->selflag = SEL_CHAR;
|
data->selflag = SEL_CHAR;
|
||||||
else if (strcasecmp(s, "word") == 0 || strcasecmp(s, "w") == 0) {
|
else if (strcasecmp(s, "word") == 0 || strcasecmp(s, "w") == 0) {
|
||||||
data->separators = options_get_string(so, "word-separators");
|
data->separators = options_get_string(so, "word-separators");
|
||||||
data->selflag = SEL_WORD;
|
data->selflag = SEL_WORD;
|
||||||
} else if (strcasecmp(s, "line") == 0 || strcasecmp(s, "l") == 0)
|
} else if (strcasecmp(s, "line") == 0 || strcasecmp(s, "l") == 0) {
|
||||||
data->selflag = SEL_LINE;
|
data->selflag = SEL_LINE;
|
||||||
|
if (data->screen.sel == NULL)
|
||||||
|
return (WINDOW_COPY_CMD_MOVE);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Line selection normally starts with select-line, which sets
|
||||||
|
* up the reset positions used when the cursor changes
|
||||||
|
* direction. Do the same when changing an existing selection
|
||||||
|
* to line mode.
|
||||||
|
*/
|
||||||
|
if (data->cursordrag == CURSORDRAG_SEL) {
|
||||||
|
fx = data->endselx;
|
||||||
|
fy = data->endsely;
|
||||||
|
} else {
|
||||||
|
fx = data->selx;
|
||||||
|
fy = data->sely;
|
||||||
|
}
|
||||||
|
|
||||||
|
sx = data->selx;
|
||||||
|
sy = data->sely;
|
||||||
|
ex = data->endselx;
|
||||||
|
ey = data->endsely;
|
||||||
|
if (ey < sy || (ey == sy && ex < sx)) {
|
||||||
|
x = sx; sx = ex; ex = x;
|
||||||
|
y = sy; sy = ey; ey = y;
|
||||||
|
}
|
||||||
|
grid_reader_start(&gr, data->backing->grid, sx, sy);
|
||||||
|
grid_reader_cursor_start_of_line(&gr, 1);
|
||||||
|
grid_reader_get_cursor(&gr, &sx, &sy);
|
||||||
|
grid_reader_start(&gr, data->backing->grid, ex, ey);
|
||||||
|
grid_reader_cursor_end_of_line(&gr, 1, 0);
|
||||||
|
grid_reader_get_cursor(&gr, &ex, &ey);
|
||||||
|
|
||||||
|
data->rectflag = 0;
|
||||||
|
data->selrx = data->selx = sx;
|
||||||
|
data->selry = data->sely = sy;
|
||||||
|
data->endselrx = data->endselx = ex;
|
||||||
|
data->endselry = data->endsely = ey;
|
||||||
|
|
||||||
|
x = data->cx;
|
||||||
|
y = screen_hsize(data->backing) + data->cy - data->oy;
|
||||||
|
data->dx = fx;
|
||||||
|
data->dy = fy;
|
||||||
|
if (data->cursordrag != CURSORDRAG_NONE &&
|
||||||
|
(y < fy || (y == fy && x < fx))) {
|
||||||
|
data->lineflag = LINE_SEL_RIGHT_LEFT;
|
||||||
|
data->cursordrag = CURSORDRAG_SEL;
|
||||||
|
window_copy_scroll_to(wme, sx, sy, 1);
|
||||||
|
} else {
|
||||||
|
data->lineflag = LINE_SEL_LEFT_RIGHT;
|
||||||
|
if (data->cursordrag != CURSORDRAG_NONE) {
|
||||||
|
data->cursordrag = CURSORDRAG_ENDSEL;
|
||||||
|
x = window_copy_cursor_limit(wme, ey, 0);
|
||||||
|
window_copy_scroll_to(wme, x, ey, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (data->cursordrag == CURSORDRAG_NONE)
|
||||||
|
window_copy_set_selection(wme, 0, 0);
|
||||||
|
return (WINDOW_COPY_CMD_REDRAW);
|
||||||
|
}
|
||||||
return (WINDOW_COPY_CMD_MOVE);
|
return (WINDOW_COPY_CMD_MOVE);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -3036,6 +3098,20 @@ window_copy_refresh_arm(struct window_mode_entry *wme)
|
|||||||
evtimer_add(&data->refresh_timer, &tv);
|
evtimer_add(&data->refresh_timer, &tv);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
window_copy_refresh_allowed(struct window_mode_entry *wme)
|
||||||
|
{
|
||||||
|
struct window_copy_mode_data *data = wme->data;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Do not refresh a view of another pane (copy-mode -s): the source may
|
||||||
|
* disappear and changes are not tracked on this pane.
|
||||||
|
*/
|
||||||
|
if (data->viewmode || wme->swp != wme->wp)
|
||||||
|
return (0);
|
||||||
|
return (1);
|
||||||
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
window_copy_refresh_timer(__unused int fd, __unused short events, void *arg)
|
window_copy_refresh_timer(__unused int fd, __unused short events, void *arg)
|
||||||
{
|
{
|
||||||
@@ -3070,11 +3146,7 @@ window_copy_refresh_start(struct window_mode_entry *wme)
|
|||||||
{
|
{
|
||||||
struct window_copy_mode_data *data = wme->data;
|
struct window_copy_mode_data *data = wme->data;
|
||||||
|
|
||||||
/*
|
if (!window_copy_refresh_allowed(wme) || data->refresh_active)
|
||||||
* Do not refresh a view of another pane (copy-mode -s): the source may
|
|
||||||
* disappear and changes are not tracked on this pane.
|
|
||||||
*/
|
|
||||||
if (data->viewmode || wme->swp != wme->wp || data->refresh_active)
|
|
||||||
return;
|
return;
|
||||||
data->refresh_active = 1;
|
data->refresh_active = 1;
|
||||||
window_copy_refresh_arm(wme);
|
window_copy_refresh_arm(wme);
|
||||||
@@ -3089,6 +3161,25 @@ window_copy_refresh_stop(struct window_mode_entry *wme)
|
|||||||
evtimer_del(&data->refresh_timer);
|
evtimer_del(&data->refresh_timer);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static enum window_copy_cmd_action
|
||||||
|
window_copy_cmd_refresh_now(struct window_copy_cmd_state *cs)
|
||||||
|
{
|
||||||
|
struct window_mode_entry *wme = cs->wme;
|
||||||
|
struct window_copy_mode_data *data = wme->data;
|
||||||
|
struct window_pane *wp = wme->wp;
|
||||||
|
int follow;
|
||||||
|
|
||||||
|
if (!window_copy_refresh_allowed(wme))
|
||||||
|
return (WINDOW_COPY_CMD_NOTHING);
|
||||||
|
|
||||||
|
follow = (data->oy == 0 &&
|
||||||
|
data->cy == screen_size_y(&data->screen) - 1);
|
||||||
|
window_copy_do_refresh(wme, follow);
|
||||||
|
wp->flags &= ~PANE_UNSEENCHANGES;
|
||||||
|
|
||||||
|
return (WINDOW_COPY_CMD_REDRAW);
|
||||||
|
}
|
||||||
|
|
||||||
static enum window_copy_cmd_action
|
static enum window_copy_cmd_action
|
||||||
window_copy_cmd_refresh_on(struct window_copy_cmd_state *cs)
|
window_copy_cmd_refresh_on(struct window_copy_cmd_state *cs)
|
||||||
{
|
{
|
||||||
@@ -3631,6 +3722,12 @@ static const struct {
|
|||||||
.clear = WINDOW_COPY_CMD_CLEAR_NEVER,
|
.clear = WINDOW_COPY_CMD_CLEAR_NEVER,
|
||||||
.f = window_copy_cmd_refresh_off
|
.f = window_copy_cmd_refresh_off
|
||||||
},
|
},
|
||||||
|
{ .command = "refresh-now",
|
||||||
|
.args = { "", 0, 0, NULL },
|
||||||
|
.flags = WINDOW_COPY_CMD_FLAG_READONLY,
|
||||||
|
.clear = WINDOW_COPY_CMD_CLEAR_NEVER,
|
||||||
|
.f = window_copy_cmd_refresh_now
|
||||||
|
},
|
||||||
{ .command = "refresh-toggle",
|
{ .command = "refresh-toggle",
|
||||||
.args = { "", 0, 0, NULL },
|
.args = { "", 0, 0, NULL },
|
||||||
.flags = WINDOW_COPY_CMD_FLAG_READONLY,
|
.flags = WINDOW_COPY_CMD_FLAG_READONLY,
|
||||||
@@ -4057,7 +4154,7 @@ window_copy_search_lr_regex(struct grid *gd, u_int *ppx, u_int *psx, u_int py,
|
|||||||
endline = gd->hsize + gd->sy - 1;
|
endline = gd->hsize + gd->sy - 1;
|
||||||
pywrap = py;
|
pywrap = py;
|
||||||
while (buf != NULL &&
|
while (buf != NULL &&
|
||||||
pywrap <= endline &&
|
pywrap < endline &&
|
||||||
len < WINDOW_COPY_SEARCH_MAX_LINE) {
|
len < WINDOW_COPY_SEARCH_MAX_LINE) {
|
||||||
gl = grid_get_line(gd, pywrap);
|
gl = grid_get_line(gd, pywrap);
|
||||||
if (~gl->flags & GRID_LINE_WRAPPED)
|
if (~gl->flags & GRID_LINE_WRAPPED)
|
||||||
@@ -4116,7 +4213,7 @@ window_copy_search_rl_regex(struct grid *gd, u_int *ppx, u_int *psx, u_int py,
|
|||||||
endline = gd->hsize + gd->sy - 1;
|
endline = gd->hsize + gd->sy - 1;
|
||||||
pywrap = py;
|
pywrap = py;
|
||||||
while (buf != NULL &&
|
while (buf != NULL &&
|
||||||
pywrap <= endline &&
|
pywrap < endline &&
|
||||||
len < WINDOW_COPY_SEARCH_MAX_LINE) {
|
len < WINDOW_COPY_SEARCH_MAX_LINE) {
|
||||||
gl = grid_get_line(gd, pywrap);
|
gl = grid_get_line(gd, pywrap);
|
||||||
if (~gl->flags & GRID_LINE_WRAPPED)
|
if (~gl->flags & GRID_LINE_WRAPPED)
|
||||||
@@ -4265,7 +4362,7 @@ window_copy_stringify(struct grid *gd, u_int py, u_int first, u_int last,
|
|||||||
}
|
}
|
||||||
if (dlen == 1)
|
if (dlen == 1)
|
||||||
buf[bx++] = *d;
|
buf[bx++] = *d;
|
||||||
else {
|
else if (dlen != 0) {
|
||||||
memcpy(buf + bx, d, dlen);
|
memcpy(buf + bx, d, dlen);
|
||||||
bx += dlen;
|
bx += dlen;
|
||||||
}
|
}
|
||||||
@@ -4317,6 +4414,7 @@ window_copy_cstrtocellpos(struct grid *gd, u_int ncells, u_int *ppx, u_int *ppy,
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
ncells = cell;
|
||||||
|
|
||||||
/* Locate starting cell. */
|
/* Locate starting cell. */
|
||||||
cell = 0;
|
cell = 0;
|
||||||
@@ -4814,6 +4912,8 @@ window_copy_search_marks(struct window_mode_entry *wme, struct screen *ssp,
|
|||||||
cflags |= REG_ICASE;
|
cflags |= REG_ICASE;
|
||||||
if (regcomp(®, sbuf, cflags) != 0) {
|
if (regcomp(®, sbuf, cflags) != 0) {
|
||||||
free(sbuf);
|
free(sbuf);
|
||||||
|
free(data->searchmark);
|
||||||
|
data->searchmark = NULL;
|
||||||
return (0);
|
return (0);
|
||||||
}
|
}
|
||||||
free(sbuf);
|
free(sbuf);
|
||||||
@@ -6151,9 +6251,7 @@ window_copy_copy_line(struct window_mode_entry *wme, char **buf, size_t *off,
|
|||||||
grid_get_cell(gd, i, sy, &gc);
|
grid_get_cell(gd, i, sy, &gc);
|
||||||
if (gc.flags & GRID_FLAG_PADDING)
|
if (gc.flags & GRID_FLAG_PADDING)
|
||||||
continue;
|
continue;
|
||||||
if (gc.flags & GRID_FLAG_IMAGE)
|
if (gc.flags & GRID_FLAG_TAB)
|
||||||
utf8_set(&ud, ' ');
|
|
||||||
else if (gc.flags & GRID_FLAG_TAB)
|
|
||||||
utf8_set(&ud, '\t');
|
utf8_set(&ud, '\t');
|
||||||
else
|
else
|
||||||
utf8_copy(&ud, &gc.data);
|
utf8_copy(&ud, &gc.data);
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
/* $OpenBSD: window-panes.c,v 1.4 2026/07/29 14:06:32 nicm Exp $ */
|
/* $OpenBSD: window-panes.c,v 1.5 2026/08/25 06:04:33 nicm Exp $ */
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2026 Nicholas Marriott <nicholas.marriott@gmail.com>
|
* Copyright (c) 2026 Nicholas Marriott <nicholas.marriott@gmail.com>
|
||||||
@@ -1009,6 +1009,7 @@ window_panes_run_command(struct window_panes_modedata *data, struct client *c,
|
|||||||
} else {
|
} else {
|
||||||
new_item = cmdq_get_command(cmdlist, NULL);
|
new_item = cmdq_get_command(cmdlist, NULL);
|
||||||
cmdq_append(c, new_item);
|
cmdq_append(c, new_item);
|
||||||
|
cmd_list_free(cmdlist);
|
||||||
}
|
}
|
||||||
free(expanded);
|
free(expanded);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
/* $OpenBSD: window-switch.c,v 1.2 2026/07/14 17:17:18 nicm Exp $ */
|
/* $OpenBSD: window-switch.c,v 1.3 2026/08/25 07:23:30 nicm Exp $ */
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2026 Nicholas Marriott <nicholas.marriott@gmail.com>
|
* Copyright (c) 2026 Nicholas Marriott <nicholas.marriott@gmail.com>
|
||||||
@@ -227,6 +227,7 @@ window_switch_build(struct window_switch_modedata *data)
|
|||||||
m = xreallocarray(m, n + 1, sizeof *m);
|
m = xreallocarray(m, n + 1, sizeof *m);
|
||||||
m[n++] = item;
|
m[n++] = item;
|
||||||
}
|
}
|
||||||
|
if (n > 1)
|
||||||
qsort(m, n, sizeof *m, window_switch_compare);
|
qsort(m, n, sizeof *m, window_switch_compare);
|
||||||
|
|
||||||
free(data->matches);
|
free(data->matches);
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
/* $OpenBSD: window-tree.c,v 1.94 2026/07/14 17:17:18 nicm Exp $ */
|
/* $OpenBSD: window-tree.c,v 1.96 2026/08/25 07:23:30 nicm Exp $ */
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2017 Nicholas Marriott <nicholas.marriott@gmail.com>
|
* Copyright (c) 2017 Nicholas Marriott <nicholas.marriott@gmail.com>
|
||||||
@@ -642,6 +642,7 @@ window_tree_draw_session(struct window_tree_modedata *data, struct session *s,
|
|||||||
}
|
}
|
||||||
free(label);
|
free(label);
|
||||||
}
|
}
|
||||||
|
format_free(ft);
|
||||||
|
|
||||||
if (loop != end - 1) {
|
if (loop != end - 1) {
|
||||||
screen_write_cursormove(ctx, cx + offset + width, cy,
|
screen_write_cursormove(ctx, cx + offset + width, cy,
|
||||||
@@ -790,6 +791,7 @@ window_tree_draw_window(struct window_tree_modedata *data, struct session *s,
|
|||||||
}
|
}
|
||||||
free(label);
|
free(label);
|
||||||
}
|
}
|
||||||
|
format_free(ft);
|
||||||
|
|
||||||
if (loop != end - 1) {
|
if (loop != end - 1) {
|
||||||
screen_write_cursormove(ctx, cx + offset + width, cy,
|
screen_write_cursormove(ctx, cx + offset + width, cy,
|
||||||
@@ -1020,6 +1022,10 @@ window_tree_swap(void *cur_itemdata, void *other_itemdata,
|
|||||||
window_tree_pull_item(other, &other_session, &other_winlink,
|
window_tree_pull_item(other, &other_session, &other_winlink,
|
||||||
&other_pane);
|
&other_pane);
|
||||||
|
|
||||||
|
if (cur_session == NULL || cur_winlink == NULL)
|
||||||
|
return (0);
|
||||||
|
if (other_session == NULL || other_winlink == NULL)
|
||||||
|
return (0);
|
||||||
if (cur_session != other_session)
|
if (cur_session != other_session)
|
||||||
return (0);
|
return (0);
|
||||||
|
|
||||||
|
|||||||
155
window.c
155
window.c
@@ -1,4 +1,4 @@
|
|||||||
/* $OpenBSD: window.c,v 1.369 2026/07/29 14:06:32 nicm Exp $ */
|
/* $OpenBSD: window.c,v 1.373 2026/08/24 21:17:19 nicm Exp $ */
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>
|
* Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>
|
||||||
@@ -451,6 +451,7 @@ window_destroy(struct window *w)
|
|||||||
{
|
{
|
||||||
log_debug("window @%u destroyed (%d references)", w->id, w->references);
|
log_debug("window @%u destroyed (%d references)", w->id, w->references);
|
||||||
|
|
||||||
|
active_remove_window(w);
|
||||||
window_unzoom(w, 0);
|
window_unzoom(w, 0);
|
||||||
RB_REMOVE(windows, &windows, w);
|
RB_REMOVE(windows, &windows, w);
|
||||||
|
|
||||||
@@ -672,6 +673,7 @@ void
|
|||||||
window_pane_update_focus(struct window_pane *wp)
|
window_pane_update_focus(struct window_pane *wp)
|
||||||
{
|
{
|
||||||
struct client *c;
|
struct client *c;
|
||||||
|
struct window *w;
|
||||||
int focused = 0;
|
int focused = 0;
|
||||||
|
|
||||||
if (wp != NULL && (~wp->flags & PANE_EXITED)) {
|
if (wp != NULL && (~wp->flags & PANE_EXITED)) {
|
||||||
@@ -679,10 +681,12 @@ window_pane_update_focus(struct window_pane *wp)
|
|||||||
focused = 0;
|
focused = 0;
|
||||||
else {
|
else {
|
||||||
TAILQ_FOREACH(c, &clients, entry) {
|
TAILQ_FOREACH(c, &clients, entry) {
|
||||||
if (c->session != NULL &&
|
if (c->session == NULL)
|
||||||
c->session->attached != 0 &&
|
continue;
|
||||||
|
w = active_get_effective_window(c, c->session);
|
||||||
|
if (c->session->attached != 0 &&
|
||||||
(c->flags & CLIENT_FOCUSED) &&
|
(c->flags & CLIENT_FOCUSED) &&
|
||||||
c->session->curw->window == wp->window &&
|
w == wp->window &&
|
||||||
c->overlay_draw == NULL &&
|
c->overlay_draw == NULL &&
|
||||||
wp->window->menu == NULL) {
|
wp->window->menu == NULL) {
|
||||||
focused = 1;
|
focused = 1;
|
||||||
@@ -718,7 +722,7 @@ window_set_active_pane(struct window *w, struct window_pane *wp, int notify)
|
|||||||
return (0);
|
return (0);
|
||||||
if (w->modal != NULL && wp != w->modal)
|
if (w->modal != NULL && wp != w->modal)
|
||||||
return (0);
|
return (0);
|
||||||
if (w->flags & WINDOW_ZOOMED)
|
if ((w->flags & WINDOW_ZOOMED) && !window_pane_is_visible(wp))
|
||||||
window_unzoom(w, 1);
|
window_unzoom(w, 1);
|
||||||
lastwp = w->active;
|
lastwp = w->active;
|
||||||
|
|
||||||
@@ -788,7 +792,8 @@ window_redraw_active_switch(struct window *w, struct window_pane *wp)
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
/* If the pane is floating, move to the front. */
|
/* If the pane is floating, move to the front. */
|
||||||
if (window_pane_is_floating(wp)) {
|
if (window_pane_is_floating(wp) &&
|
||||||
|
TAILQ_FIRST(&w->z_index) != wp) {
|
||||||
TAILQ_REMOVE(&w->z_index, wp, zentry);
|
TAILQ_REMOVE(&w->z_index, wp, zentry);
|
||||||
TAILQ_INSERT_HEAD(&w->z_index, wp, zentry);
|
TAILQ_INSERT_HEAD(&w->z_index, wp, zentry);
|
||||||
wp->flags |= PANE_REDRAW;
|
wp->flags |= PANE_REDRAW;
|
||||||
@@ -918,13 +923,18 @@ window_zoom(struct window_pane *wp)
|
|||||||
{
|
{
|
||||||
struct window *w = wp->window;
|
struct window *w = wp->window;
|
||||||
struct window_pane *wp1;
|
struct window_pane *wp1;
|
||||||
|
struct layout_cell *lc;
|
||||||
|
struct layout_geometry lg;
|
||||||
|
|
||||||
if (w->flags & WINDOW_ZOOMED)
|
if (w->flags & WINDOW_ZOOMED)
|
||||||
return (-1);
|
return (-1);
|
||||||
if (window_count_panes(w, 1) == 1)
|
if (window_count_panes(w, 1) == 1)
|
||||||
return (-1);
|
return (-1);
|
||||||
|
|
||||||
if (w->active != wp)
|
if (w->active != wp &&
|
||||||
|
(w->active == NULL ||
|
||||||
|
(~w->active->flags & PANE_FLOATOVERZOOM) ||
|
||||||
|
!window_pane_is_floating(w->active)))
|
||||||
window_set_active_pane(w, wp, 1);
|
window_set_active_pane(w, wp, 1);
|
||||||
wp->flags |= PANE_ZOOMED;
|
wp->flags |= PANE_ZOOMED;
|
||||||
|
|
||||||
@@ -935,6 +945,22 @@ window_zoom(struct window_pane *wp)
|
|||||||
|
|
||||||
w->saved_layout_root = w->layout_root;
|
w->saved_layout_root = w->layout_root;
|
||||||
layout_init(w, wp);
|
layout_init(w, wp);
|
||||||
|
TAILQ_FOREACH(wp1, &w->panes, entry) {
|
||||||
|
lc = wp1->saved_layout_cell;
|
||||||
|
if (wp1 == wp ||
|
||||||
|
(~wp1->flags & PANE_FLOATOVERZOOM) ||
|
||||||
|
lc == NULL ||
|
||||||
|
(~lc->flags & LAYOUT_CELL_FLOATING))
|
||||||
|
continue;
|
||||||
|
memcpy(&lg, &lc->g, sizeof lg);
|
||||||
|
lc = layout_floating_pane(w, wp, &lg);
|
||||||
|
layout_assign_pane(lc, wp1, 0);
|
||||||
|
}
|
||||||
|
/* A floating zoom target is now tiled, so put it behind the floats. */
|
||||||
|
if (wp->saved_layout_cell->flags & LAYOUT_CELL_FLOATING) {
|
||||||
|
TAILQ_REMOVE(&w->z_index, wp, zentry);
|
||||||
|
TAILQ_INSERT_TAIL(&w->z_index, wp, zentry);
|
||||||
|
}
|
||||||
w->flags |= WINDOW_ZOOMED;
|
w->flags |= WINDOW_ZOOMED;
|
||||||
events_fire_window("window-zoomed", w);
|
events_fire_window("window-zoomed", w);
|
||||||
events_fire_window("window-layout-changed", w);
|
events_fire_window("window-layout-changed", w);
|
||||||
@@ -946,11 +972,26 @@ window_zoom(struct window_pane *wp)
|
|||||||
int
|
int
|
||||||
window_unzoom(struct window *w, int notify)
|
window_unzoom(struct window *w, int notify)
|
||||||
{
|
{
|
||||||
struct window_pane *wp;
|
struct window_pane *wp, *zoomed = NULL;
|
||||||
|
struct layout_cell *slc;
|
||||||
|
|
||||||
if (!(w->flags & WINDOW_ZOOMED))
|
if (~w->flags & WINDOW_ZOOMED)
|
||||||
return (-1);
|
return (-1);
|
||||||
|
|
||||||
|
TAILQ_FOREACH(wp, &w->panes, entry) {
|
||||||
|
if (wp->flags & PANE_ZOOMED)
|
||||||
|
zoomed = wp;
|
||||||
|
if (~wp->flags & PANE_FLOATOVERZOOM)
|
||||||
|
continue;
|
||||||
|
if (wp->flags & PANE_ZOOMED)
|
||||||
|
continue;
|
||||||
|
slc = wp->saved_layout_cell;
|
||||||
|
if (slc == NULL || wp->layout_cell == NULL)
|
||||||
|
continue;
|
||||||
|
memcpy(&slc->g, &wp->layout_cell->g, sizeof slc->g);
|
||||||
|
memcpy(&slc->fg, &wp->layout_cell->fg, sizeof slc->fg);
|
||||||
|
}
|
||||||
|
|
||||||
w->flags &= ~WINDOW_ZOOMED;
|
w->flags &= ~WINDOW_ZOOMED;
|
||||||
layout_free(w, 0);
|
layout_free(w, 0);
|
||||||
w->layout_root = w->saved_layout_root;
|
w->layout_root = w->saved_layout_root;
|
||||||
@@ -961,6 +1002,22 @@ window_unzoom(struct window *w, int notify)
|
|||||||
wp->saved_layout_cell = NULL;
|
wp->saved_layout_cell = NULL;
|
||||||
wp->flags &= ~PANE_ZOOMED;
|
wp->flags &= ~PANE_ZOOMED;
|
||||||
}
|
}
|
||||||
|
/* Put a floating zoom target back into the floating part of the list. */
|
||||||
|
if (zoomed != NULL && window_pane_is_floating(zoomed)) {
|
||||||
|
TAILQ_REMOVE(&w->z_index, zoomed, zentry);
|
||||||
|
if (zoomed == w->active)
|
||||||
|
TAILQ_INSERT_HEAD(&w->z_index, zoomed, zentry);
|
||||||
|
else {
|
||||||
|
TAILQ_FOREACH(wp, &w->z_index, zentry) {
|
||||||
|
if (!window_pane_is_floating(wp))
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (wp == NULL)
|
||||||
|
TAILQ_INSERT_TAIL(&w->z_index, zoomed, zentry);
|
||||||
|
else
|
||||||
|
TAILQ_INSERT_BEFORE(wp, zoomed, zentry);
|
||||||
|
}
|
||||||
|
}
|
||||||
layout_fix_panes(w, NULL);
|
layout_fix_panes(w, NULL);
|
||||||
|
|
||||||
if (notify) {
|
if (notify) {
|
||||||
@@ -972,48 +1029,69 @@ window_unzoom(struct window *w, int notify)
|
|||||||
return (0);
|
return (0);
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
struct window_pane *
|
||||||
window_push_modal_zoom(struct window *w)
|
window_zoomed_pane(struct window *w)
|
||||||
{
|
{
|
||||||
if (w->flags & WINDOW_ZOOMED)
|
struct window_pane *wp;
|
||||||
w->flags |= WINDOW_WASMODALZOOMED;
|
|
||||||
else
|
if (~w->flags & WINDOW_ZOOMED)
|
||||||
w->flags &= ~WINDOW_WASMODALZOOMED;
|
return (NULL);
|
||||||
window_unzoom(w, 1);
|
TAILQ_FOREACH_REVERSE(wp, &w->z_index, window_panes_zindex, zentry) {
|
||||||
|
if (wp->layout_cell != NULL && !window_pane_is_floating(wp))
|
||||||
|
return (wp);
|
||||||
|
}
|
||||||
|
return (NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
int
|
int
|
||||||
window_pop_modal_zoom(struct window *w)
|
window_active_pane_is_over_zoom(struct window *w)
|
||||||
{
|
{
|
||||||
struct window_pane *wp = w->active;
|
if (~w->flags & WINDOW_ZOOMED)
|
||||||
|
|
||||||
if (~w->flags & WINDOW_WASMODALZOOMED)
|
|
||||||
return (0);
|
return (0);
|
||||||
w->flags &= ~WINDOW_WASMODALZOOMED;
|
if (w->active == NULL)
|
||||||
if (wp != NULL && window_has_pane(w, wp))
|
|
||||||
return (window_zoom(wp) == 0);
|
|
||||||
return (0);
|
return (0);
|
||||||
|
if (~w->active->flags & PANE_FLOATOVERZOOM)
|
||||||
|
return (0);
|
||||||
|
return (window_pane_is_floating(w->active));
|
||||||
}
|
}
|
||||||
|
|
||||||
int
|
int
|
||||||
window_push_zoom(struct window *w, int always, int flag)
|
window_push_zoom(struct window *w, int always, int flag)
|
||||||
{
|
{
|
||||||
|
struct window_pane *wp = window_zoomed_pane(w);
|
||||||
|
|
||||||
log_debug("%s: @%u %d", __func__, w->id,
|
log_debug("%s: @%u %d", __func__, w->id,
|
||||||
flag && (w->flags & WINDOW_ZOOMED));
|
flag && (w->flags & WINDOW_ZOOMED));
|
||||||
if (flag && (always || (w->flags & WINDOW_ZOOMED)))
|
if (flag && (always || (w->flags & WINDOW_ZOOMED)))
|
||||||
w->flags |= WINDOW_WASZOOMED;
|
w->flags |= WINDOW_WASZOOMED;
|
||||||
else
|
else
|
||||||
w->flags &= ~WINDOW_WASZOOMED;
|
w->flags &= ~WINDOW_WASZOOMED;
|
||||||
|
if (w->flags & WINDOW_WASZOOMED)
|
||||||
|
w->was_zoomed = wp;
|
||||||
|
else
|
||||||
|
w->was_zoomed = NULL;
|
||||||
return (window_unzoom(w, 1) == 0);
|
return (window_unzoom(w, 1) == 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
int
|
int
|
||||||
window_pop_zoom(struct window *w)
|
window_pop_zoom(struct window *w)
|
||||||
{
|
{
|
||||||
|
struct window_pane *wp = w->was_zoomed;
|
||||||
|
|
||||||
log_debug("%s: @%u %d", __func__, w->id,
|
log_debug("%s: @%u %d", __func__, w->id,
|
||||||
!!(w->flags & WINDOW_WASZOOMED));
|
!!(w->flags & WINDOW_WASZOOMED));
|
||||||
if (w->flags & WINDOW_WASZOOMED)
|
if (w->flags & WINDOW_WASZOOMED) {
|
||||||
return (window_zoom(w->active) == 0);
|
w->flags &= ~WINDOW_WASZOOMED;
|
||||||
|
w->was_zoomed = NULL;
|
||||||
|
if (w->active != NULL &&
|
||||||
|
((~w->active->flags & PANE_FLOATOVERZOOM) ||
|
||||||
|
!window_pane_is_floating(w->active)))
|
||||||
|
wp = w->active;
|
||||||
|
if (wp == NULL || !window_has_pane(w, wp))
|
||||||
|
wp = w->active;
|
||||||
|
if (wp != NULL)
|
||||||
|
return (window_zoom(wp) == 0);
|
||||||
|
}
|
||||||
return (0);
|
return (0);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1065,8 +1143,8 @@ window_lost_pane(struct window *w, struct window_pane *wp)
|
|||||||
server_clear_marked();
|
server_clear_marked();
|
||||||
if (wp == w->modal_last)
|
if (wp == w->modal_last)
|
||||||
w->modal_last = NULL;
|
w->modal_last = NULL;
|
||||||
if (w->modal_last == NULL)
|
if (wp == w->was_zoomed)
|
||||||
w->flags &= ~WINDOW_WASMODALZOOMED;
|
w->was_zoomed = NULL;
|
||||||
|
|
||||||
window_pane_stack_remove(&w->last_panes, wp);
|
window_pane_stack_remove(&w->last_panes, wp);
|
||||||
if (wp == w->active) {
|
if (wp == w->active) {
|
||||||
@@ -1093,7 +1171,6 @@ window_lost_pane(struct window *w, struct window_pane *wp)
|
|||||||
}
|
}
|
||||||
} else if (wp == w->modal) {
|
} else if (wp == w->modal) {
|
||||||
w->modal = w->modal_last = NULL;
|
w->modal = w->modal_last = NULL;
|
||||||
w->flags &= ~WINDOW_WASMODALZOOMED;
|
|
||||||
}
|
}
|
||||||
redraw_invalidate_scene(w);
|
redraw_invalidate_scene(w);
|
||||||
}
|
}
|
||||||
@@ -1101,13 +1178,9 @@ window_lost_pane(struct window *w, struct window_pane *wp)
|
|||||||
void
|
void
|
||||||
window_remove_pane(struct window *w, struct window_pane *wp)
|
window_remove_pane(struct window *w, struct window_pane *wp)
|
||||||
{
|
{
|
||||||
int pop = (wp == w->modal);
|
|
||||||
|
|
||||||
window_lost_pane(w, wp);
|
window_lost_pane(w, wp);
|
||||||
TAILQ_REMOVE(&w->panes, wp, entry);
|
TAILQ_REMOVE(&w->panes, wp, entry);
|
||||||
TAILQ_REMOVE(&w->z_index, wp, zentry);
|
TAILQ_REMOVE(&w->z_index, wp, zentry);
|
||||||
if (pop && window_pop_modal_zoom(w))
|
|
||||||
server_redraw_window(w);
|
|
||||||
redraw_invalidate_scene(w);
|
redraw_invalidate_scene(w);
|
||||||
window_pane_destroy(wp);
|
window_pane_destroy(wp);
|
||||||
}
|
}
|
||||||
@@ -1219,7 +1292,7 @@ window_destroy_panes(struct window *w)
|
|||||||
}
|
}
|
||||||
|
|
||||||
const char *
|
const char *
|
||||||
window_printable_flags(struct winlink *wl, int escape)
|
window_printable_flags(struct client *c, struct winlink *wl, int escape)
|
||||||
{
|
{
|
||||||
struct session *s = wl->session;
|
struct session *s = wl->session;
|
||||||
static char flags[32];
|
static char flags[32];
|
||||||
@@ -1234,9 +1307,9 @@ window_printable_flags(struct winlink *wl, int escape)
|
|||||||
flags[pos++] = '!';
|
flags[pos++] = '!';
|
||||||
if (wl->flags & WINLINK_SILENCE)
|
if (wl->flags & WINLINK_SILENCE)
|
||||||
flags[pos++] = '~';
|
flags[pos++] = '~';
|
||||||
if (wl == s->curw)
|
if (active_is_effective_window(c, s, wl))
|
||||||
flags[pos++] = '*';
|
flags[pos++] = '*';
|
||||||
if (wl == TAILQ_FIRST(&s->lastw))
|
if (active_is_last_window(c, s, wl))
|
||||||
flags[pos++] = '-';
|
flags[pos++] = '-';
|
||||||
if (server_check_marked() && wl == marked_pane.wl)
|
if (server_check_marked() && wl == marked_pane.wl)
|
||||||
flags[pos++] = 'M';
|
flags[pos++] = 'M';
|
||||||
@@ -1263,6 +1336,8 @@ window_pane_printable_flags(struct window_pane *wp)
|
|||||||
flags[pos++] = 'Z';
|
flags[pos++] = 'Z';
|
||||||
if (window_pane_is_floating(wp))
|
if (window_pane_is_floating(wp))
|
||||||
flags[pos++] = 'F';
|
flags[pos++] = 'F';
|
||||||
|
if (wp->flags & PANE_FLOATOVERZOOM)
|
||||||
|
flags[pos++] = 'A';
|
||||||
if (wp == w->modal)
|
if (wp == w->modal)
|
||||||
flags[pos++] = 'O';
|
flags[pos++] = 'O';
|
||||||
flags[pos] = '\0';
|
flags[pos] = '\0';
|
||||||
@@ -1408,6 +1483,8 @@ window_pane_scrollbar_overlay_visible(struct window_pane *wp)
|
|||||||
void
|
void
|
||||||
window_pane_scrollbar_redraw(struct window_pane *wp)
|
window_pane_scrollbar_redraw(struct window_pane *wp)
|
||||||
{
|
{
|
||||||
|
if (!window_pane_scrollbar_visible(wp))
|
||||||
|
return;
|
||||||
if (window_pane_scrollbar_overlay_visible(wp)) {
|
if (window_pane_scrollbar_overlay_visible(wp)) {
|
||||||
wp->flags |= PANE_REDRAW;
|
wp->flags |= PANE_REDRAW;
|
||||||
return;
|
return;
|
||||||
@@ -1475,16 +1552,16 @@ window_pane_free(struct window_pane *wp)
|
|||||||
log_debug("pane %%%u freed (%d references)", wp->id, wp->references);
|
log_debug("pane %%%u freed (%d references)", wp->id, wp->references);
|
||||||
|
|
||||||
free(wp->searchstr);
|
free(wp->searchstr);
|
||||||
|
|
||||||
screen_free(&wp->status_screen);
|
screen_free(&wp->status_screen);
|
||||||
screen_free(&wp->base);
|
screen_free(&wp->base);
|
||||||
|
free(wp->r.ranges);
|
||||||
options_free(wp->options);
|
options_free(wp->options);
|
||||||
free((void *)wp->cwd);
|
free((void *)wp->cwd);
|
||||||
free(wp->shell);
|
free(wp->shell);
|
||||||
cmd_free_argv(wp->argc, wp->argv);
|
cmd_free_argv(wp->argc, wp->argv);
|
||||||
colour_palette_free(&wp->palette);
|
colour_palette_free(&wp->palette);
|
||||||
style_ranges_free(&wp->border_status_line.ranges);
|
style_ranges_free(&wp->border_status_line.ranges);
|
||||||
|
free(wp->border_status_line.expanded);
|
||||||
free(wp);
|
free(wp);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1969,7 +2046,7 @@ window_pane_is_visible(struct window_pane *wp)
|
|||||||
{
|
{
|
||||||
if (~wp->window->flags & WINDOW_ZOOMED)
|
if (~wp->window->flags & WINDOW_ZOOMED)
|
||||||
return (1);
|
return (1);
|
||||||
return (wp == wp->window->active);
|
return (wp->layout_cell != NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
int
|
int
|
||||||
@@ -2562,7 +2639,7 @@ window_pane_get_bg(struct window_pane *wp)
|
|||||||
|
|
||||||
c = window_pane_get_bg_control_client(wp);
|
c = window_pane_get_bg_control_client(wp);
|
||||||
if (c == -1) {
|
if (c == -1) {
|
||||||
tty_default_colours(&defaults, wp, NULL);
|
tty_default_colours(&defaults, wp, wp->window->active, NULL);
|
||||||
if (COLOUR_DEFAULT(defaults.bg))
|
if (COLOUR_DEFAULT(defaults.bg))
|
||||||
c = window_get_bg_client(wp);
|
c = window_get_bg_client(wp);
|
||||||
else
|
else
|
||||||
|
|||||||
Reference in New Issue
Block a user