7 Commits

Author SHA1 Message Date
Thomas Adam
1cf7889fd7 Feedback from nicm 2026-03-06 18:59:26 +00:00
Thomas Adam
9c3ec2b8eb Refactor kitty images to use unified image cache API
The original kitty implementation used a passthrough approach where images
were forwarded directly to the outer terminal without being stored in tmux's
image cache.

This refactors kitty images to work like sixel images.
2026-03-05 17:21:40 +00:00
Thomas Adam
aefdb34846 rename: ENABLE_SIXEL to ENABLE_SIXEL_IMAGES 2026-03-05 17:21:18 +00:00
Thomas Adam
5a233e26ec rename ENABLE_KITTY to ENABLE_KITTY_IMAGES 2026-03-05 17:21:18 +00:00
Thomas Adam
1634db8589 configure: deprecate enable-sixel
This moves --enable-sixel to --enable-sixel-images
2026-03-05 17:21:18 +00:00
Thomas Adam
3b7e9fb175 format: add image_support
This adds a new image_support format which returns:

   "sixel" -- if sixel is compiled in
   "kitty" -- if kitty is compiled in
   "kitty,sixel" -- if compiled with both kitty and sixel
2026-03-05 17:21:18 +00:00
Thomas Adam
cf6cbe430c Add support for the kitty graphics protocol
Kitty-capable terminals (kitty, ghostty, and others) can display
inline images via APC escape sequences.

Kitty's image support uses a passthrough model: APC sequences from
programs running inside panes are relayed verbatim to the outer
terminal with cursor positioning adjusted for the pane offset.

The outer terminal handles all image rendering and therefore itself must
be kitty-aware.
2026-03-05 17:21:18 +00:00
37 changed files with 1406 additions and 2326 deletions

View File

@@ -113,9 +113,7 @@ dist_tmux_SOURCES = \
cmd-list-windows.c \
cmd-load-buffer.c \
cmd-lock-server.c \
cmd-minimise-pane.c \
cmd-move-window.c \
cmd-new-pane.c \
cmd-new-session.c \
cmd-new-window.c \
cmd-parse.y \
@@ -234,8 +232,18 @@ nodist_tmux_SOURCES += compat/utf8proc.c
endif
# Enable sixel support.
if ENABLE_SIXEL
if ENABLE_SIXEL_IMAGES
dist_tmux_SOURCES += image.c image-sixel.c
else
# If not sixel, still need image.c for kitty.
if ENABLE_KITTY_IMAGES
dist_tmux_SOURCES += image.c
endif
endif
# Enable kitty graphics protocol support.
if ENABLE_KITTY_IMAGES
dist_tmux_SOURCES += image-kitty.c
endif
if NEED_FUZZING

View File

@@ -63,7 +63,6 @@ cmd_copy_mode_exec(struct cmd *self, struct cmdq_item *item)
struct client *c = cmdq_get_client(item);
struct session *s;
struct window_pane *wp = target->wp, *swp;
u_int tty_ox, tty_oy, tty_sx, tty_sy;
if (args_has(args, 'q')) {
window_pane_reset_mode_all(wp);
@@ -95,9 +94,8 @@ cmd_copy_mode_exec(struct cmd *self, struct cmdq_item *item)
if (args_has(args, 'd'))
window_copy_pagedown(wp, 0, args_has(args, 'e'));
if (args_has(args, 'S')) {
tty_window_offset(&c->tty, &tty_ox, &tty_oy, &tty_sx, &tty_sy);
window_copy_scroll(wp, c->tty.mouse_slider_mpos, event->m.y,
tty_oy, args_has(args, 'e'));
args_has(args, 'e'));
return (CMD_RETURN_NORMAL);
}

View File

@@ -370,7 +370,7 @@ cmd_display_menu_exec(struct cmd *self, struct cmdq_item *item)
flags |= MENU_NOMOUSE;
if (menu_display(menu, flags, starting_choice, item, px, py, tc, lines,
style, selected_style, border_style, target, NULL, NULL) != 0)
goto out;
return (CMD_RETURN_NORMAL);
return (CMD_RETURN_WAIT);
out:

View File

@@ -70,10 +70,10 @@ cmd_display_panes_draw_pane(struct screen_redraw_ctx *ctx,
char buf[16], lbuf[16], rbuf[16], *ptr;
size_t len, llen, rlen;
if (wp->xoff + (int)wp->sx <= ctx->ox ||
wp->xoff >= ctx->ox + (int)ctx->sx ||
wp->yoff + (int)wp->sy <= ctx->oy ||
wp->yoff >= ctx->oy + (int)ctx->sy)
if (wp->xoff + wp->sx <= ctx->ox ||
wp->xoff >= ctx->ox + ctx->sx ||
wp->yoff + wp->sy <= ctx->oy ||
wp->yoff >= ctx->oy + ctx->sy)
return;
if (wp->xoff >= ctx->ox && wp->xoff + wp->sx <= ctx->ox + ctx->sx) {

View File

@@ -978,20 +978,15 @@ cmd_find_target(struct cmd_find_state *fs, struct cmdq_item *item,
} else if (cmd_find_from_client(&current, cmdq_get_client(item),
flags) == 0) {
fs->current = &current;
/* No active pane, window empty, return the window instead. */
if (current.wp == NULL) {
type = CMD_FIND_WINDOW;
}
log_debug("%s: current is from client", __func__);
} else {
if (~flags & CMD_FIND_QUIET)
cmdq_error(item, "no current target");
goto error;
}
/*
if (!cmd_find_valid_state(fs->current))
fatalx("invalid current find state");
*/
/* An empty or NULL target is the current. */
if (target == NULL || *target == '\0')
goto current;
@@ -1030,7 +1025,7 @@ cmd_find_target(struct cmd_find_state *fs, struct cmdq_item *item,
fs->w = fs->wl->window;
fs->wp = fs->w->active;
}
goto found;
break;
}
if (fs->wp == NULL) {
if (~flags & CMD_FIND_QUIET)

View File

@@ -62,11 +62,6 @@ cmd_kill_pane_exec(struct cmd *self, struct cmdq_item *item)
return (CMD_RETURN_NORMAL);
}
if (wp == NULL) {
/* No active window pane. */
cmdq_error(item, "No active pane to kill.");
return (CMD_RETURN_ERROR);
}
server_kill_pane(wp);
return (CMD_RETURN_NORMAL);
}

View File

@@ -1,192 +0,0 @@
/* $OpenBSD$ */
/*
* Copyright (c) 2009 Nicholas Marriott <nicholas.marriott@gmail.com>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER
* IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
* OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include <sys/types.h>
#include <stdlib.h>
#include <string.h>
#include "tmux.h"
/*
* Increase or decrease pane size.
*/
static enum cmd_retval cmd_minimise_pane_minimise_exec(struct cmd *, struct cmdq_item *);
static enum cmd_retval cmd_minimise_pane_unminimise_exec(struct cmd *, struct cmdq_item *);
static enum cmd_retval cmd_minimise_pane_minimise(struct window *, struct window_pane *);
static enum cmd_retval cmd_minimise_pane_unminimise(struct window *, struct window_pane *);
const struct cmd_entry cmd_minimise_pane_entry = {
.name = "minimise-pane",
.alias = "minimize-pane",
.args = { "at:", 0, 1, NULL },
.usage = "[-a] " CMD_TARGET_PANE_USAGE,
.target = { 't', CMD_FIND_PANE, 0 },
.flags = CMD_AFTERHOOK,
.exec = cmd_minimise_pane_minimise_exec
};
const struct cmd_entry cmd_unminimise_pane_entry = {
.name = "unminimise-pane",
.alias = "unminimize-pane",
.args = { "at:", 0, 1, NULL },
.usage = "[-a] " CMD_TARGET_PANE_USAGE,
.target = { 't', CMD_FIND_PANE, 0 },
.flags = CMD_AFTERHOOK,
.exec = cmd_minimise_pane_unminimise_exec
};
static enum cmd_retval
cmd_minimise_pane_minimise_exec(struct cmd *self, struct cmdq_item *item)
{
__attribute((unused)) struct args *args = cmd_get_args(self);
struct cmd_find_state *target = cmdq_get_target(item);
struct winlink *wl = target->wl;
struct window *w = wl->window;
struct window_pane *wp;
u_int id;
char *cause = NULL;
enum cmd_retval rv;
if (args_has(args, 'a')) {
TAILQ_FOREACH(wp, &w->z_index, zentry) {
if (!window_pane_visible(wp))
continue;
rv = cmd_minimise_pane_minimise(w, wp);
if (rv != CMD_RETURN_NORMAL)
return(rv);
}
return (CMD_RETURN_NORMAL);
} else {
wp = target->wp;
if (wp == NULL) {
id = args_strtonum_and_expand(args, 't', 0, INT_MAX, item, &cause);
if (cause != NULL) {
cmdq_error(item, "%s target pane", cause);
return (CMD_RETURN_ERROR);
}
wp = window_pane_find_by_id(id);
}
if (wp == NULL) {
cmdq_error(item, "No target pane to miminise.");
return (CMD_RETURN_ERROR);
}
return(cmd_minimise_pane_minimise(w, wp));
}
}
static enum cmd_retval
cmd_minimise_pane_unminimise_exec(struct cmd *self, struct cmdq_item *item)
{
__attribute((unused)) struct args *args = cmd_get_args(self);
struct cmd_find_state *target = cmdq_get_target(item);
struct winlink *wl = target->wl;
struct window *w = wl->window;
struct window_pane *wp;
u_int id;
char *cause = NULL;
enum cmd_retval rv;
if (args_has(args, 'a')) {
TAILQ_FOREACH(wp, &w->z_index, zentry) {
if (!window_pane_visible(wp))
continue;
rv = cmd_minimise_pane_unminimise(w, wp);
if (rv != CMD_RETURN_NORMAL)
return(rv);
}
return (CMD_RETURN_NORMAL);
} else {
wp = target->wp;
if (wp == NULL) {
id = args_strtonum_and_expand(args, 't', 0, INT_MAX, item, &cause);
if (cause != NULL) {
cmdq_error(item, "%s target pane", cause);
return (CMD_RETURN_ERROR);
}
wp = window_pane_find_by_id(id);
}
if (wp == NULL) {
cmdq_error(item, "No target pane to unmiminise.");
return (CMD_RETURN_ERROR);
}
return(cmd_minimise_pane_unminimise(w, wp));
}
}
static enum cmd_retval
cmd_minimise_pane_minimise(struct window *w, struct window_pane *wp)
{
struct window_pane *wp2;
wp->flags |= PANE_MINIMISED;
window_deactivate_pane(w, wp, 1);
/* Fix pane offsets and sizes. */
if (w->layout_root != NULL) {
wp->saved_layout_cell = wp->layout_cell;
layout_minimise_cell(w, wp->layout_cell);
layout_fix_offsets(w);
layout_fix_panes(w, NULL);
}
/* Find next visible window in z-index. */
TAILQ_FOREACH(wp2, &w->z_index, zentry) {
if (!window_pane_visible(wp2))
continue;
break;
}
if (wp2 != NULL)
window_set_active_pane(w, wp2, 1);
notify_window("window-layout-changed", w);
server_redraw_window(w);
return (CMD_RETURN_NORMAL);
}
static enum cmd_retval
cmd_minimise_pane_unminimise(struct window *w, struct window_pane *wp)
{
wp->flags &= ~PANE_MINIMISED;
/* Fix pane offsets and sizes. */
if (w->layout_root != NULL && wp->saved_layout_cell != NULL) {
wp->layout_cell = wp->saved_layout_cell;
wp->saved_layout_cell = NULL;
layout_unminimise_cell(w, wp->layout_cell);
layout_fix_offsets(w);
layout_fix_panes(w, NULL);
}
window_set_active_pane(w, wp, 1);
notify_window("window-layout-changed", w);
server_redraw_window(w);
return (CMD_RETURN_NORMAL);
}

View File

@@ -1,245 +0,0 @@
/* $OpenBSD$ */
/*
* Copyright (c) 2009 Nicholas Marriott <nicholas.marriott@gmail.com>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER
* IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
* OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include <sys/types.h>
#include <errno.h>
#include <fcntl.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include "tmux.h"
#define NEW_PANE_TEMPLATE "#{session_name}:#{window_index}.#{pane_index}"
static enum cmd_retval cmd_new_pane_exec(struct cmd *,
struct cmdq_item *);
const struct cmd_entry cmd_new_pane_entry = {
.name = "new-pane",
.alias = "newp",
.args = { "bc:de:fF:h:Il:p:Pt:w:x:y:Z", 0, -1, NULL },
.usage = "[-bdefhIPvZ] [-c start-directory] [-e environment] "
"[-F format] [-l size] " CMD_TARGET_PANE_USAGE
" [shell-command [argument ...]]",
.target = { 't', CMD_FIND_PANE, 0 },
.flags = 0,
.exec = cmd_new_pane_exec
};
static enum cmd_retval
cmd_new_pane_exec(struct cmd *self, struct cmdq_item *item)
{
struct args *args = cmd_get_args(self);
struct cmd_find_state *current = cmdq_get_current(item);
struct cmd_find_state *target = cmdq_get_target(item);
struct spawn_context sc = { 0 };
struct client *tc = cmdq_get_target_client(item);
struct session *s = target->s;
struct winlink *wl = target->wl;
struct window *w = wl->window;
struct window_pane *wp = target->wp, *new_wp;
struct layout_cell *lc;
struct cmd_find_state fs;
int flags, input;
const char *template;
char *cause = NULL, *cp;
struct args_value *av;
u_int count = args_count(args);
u_int x, y, sx, sy, pct;
static u_int last_x = 0, last_y = 0;
if (args_has(args, 'f')) {
sx = w->sx;
sy = w->sy;
} else {
if (args_has(args, 'l')) {
sx = args_percentage_and_expand(args, 'l', 0, INT_MAX, w->sx,
item, &cause);
sy = args_percentage_and_expand(args, 'l', 0, INT_MAX, w->sy,
item, &cause);
} else if (args_has(args, 'p')) {
pct = args_strtonum_and_expand(args, 'p', 0, 100, item,
&cause);
if (cause == NULL) {
sx = w->sx * pct / 100;
sy = w->sy * pct / 100;
}
} else if (cause == NULL) {
sx = w->sx / 2;
sy = w->sy / 2;
}
if (cause != NULL) {
cmdq_error(item, "size %s", cause);
free(cause);
return (CMD_RETURN_ERROR);
}
}
if (args_has(args, 'w')) {
sx = args_strtonum_and_expand(args, 'w', 0, w->sx, item,
&cause);
if (cause != NULL) {
cmdq_error(item, "size %s", cause);
free(cause);
return (CMD_RETURN_ERROR);
}
}
if (args_has(args, 'h')) {
sy = args_strtonum_and_expand(args, 'h', 0, w->sy, item,
&cause);
if (cause != NULL) {
cmdq_error(item, "size %s", cause);
free(cause);
return (CMD_RETURN_ERROR);
}
}
if (args_has(args, 'x')) {
x = args_strtonum_and_expand(args, 'x', 0, w->sx, item,
&cause);
if (cause != NULL) {
cmdq_error(item, "size %s", cause);
free(cause);
return (CMD_RETURN_ERROR);
}
} else {
if (last_x == 0) {
x = 5;
} else {
x = (last_x += 5);
if (last_x > w->sx)
x = 5;
}
}
if (args_has(args, 'y')) {
y = args_strtonum_and_expand(args, 'y', 0, w->sx, item,
&cause);
if (cause != NULL) {
cmdq_error(item, "size %s", cause);
free(cause);
return (CMD_RETURN_ERROR);
}
} else {
if (last_y == 0) {
y = 5;
} else {
y = (last_y += 5);
if (last_y > w->sy)
y = 5;
}
}
input = (args_has(args, 'I') && count == 0);
flags = SPAWN_FLOATING;
if (args_has(args, 'b'))
flags |= SPAWN_BEFORE;
if (args_has(args, 'f'))
flags |= SPAWN_FULLSIZE;
if (input || (count == 1 && *args_string(args, 0) == '\0'))
flags |= SPAWN_EMPTY;
sc.item = item;
sc.s = s;
sc.wl = wl;
sc.wp0 = wp;
/* Floating panes sit in layout cells which are not in the layout_root
* tree so we call it with parent == NULL.
*/
lc = layout_create_cell(NULL);
lc->xoff = x;
lc->yoff = y;
lc->sx = sx;
lc->sy = sy;
sc.lc = lc;
last_x = x; /* Statically save last xoff & yoff so that new */
last_y = y; /* floating panes offset so they don't overlap. */
args_to_vector(args, &sc.argc, &sc.argv);
sc.environ = environ_create();
av = args_first_value(args, 'e');
while (av != NULL) {
environ_put(sc.environ, av->string, 0);
av = args_next_value(av);
}
sc.idx = -1;
sc.cwd = args_get(args, 'c');
sc.flags = flags;
if (args_has(args, 'd'))
sc.flags |= SPAWN_DETACHED;
if (args_has(args, 'Z'))
sc.flags |= SPAWN_ZOOM;
if ((new_wp = spawn_pane(&sc, &cause)) == NULL) {
cmdq_error(item, "create pane failed: %s", cause);
free(cause);
if (sc.argv != NULL)
cmd_free_argv(sc.argc, sc.argv);
environ_free(sc.environ);
return (CMD_RETURN_ERROR);
}
if (input) {
switch (window_pane_start_input(new_wp, item, &cause)) {
case -1:
server_client_remove_pane(new_wp);
window_remove_pane(wp->window, new_wp);
cmdq_error(item, "%s", cause);
free(cause);
if (sc.argv != NULL)
cmd_free_argv(sc.argc, sc.argv);
environ_free(sc.environ);
return (CMD_RETURN_ERROR);
case 1:
input = 0;
break;
}
}
if (!args_has(args, 'd'))
cmd_find_from_winlink_pane(current, wl, new_wp, 0);
window_pop_zoom(wp->window);
server_redraw_window(wp->window);
server_status_session(s);
if (args_has(args, 'P')) {
if ((template = args_get(args, 'F')) == NULL)
template = NEW_PANE_TEMPLATE;
cp = format_single(item, template, tc, s, wl, new_wp);
cmdq_print(item, "%s", cp);
free(cp);
}
cmd_find_from_winlink_pane(&fs, wl, new_wp, 0);
cmdq_insert_hook(s, item, &fs, "after-split-window");
if (sc.argv != NULL)
cmd_free_argv(sc.argc, sc.argv);
environ_free(sc.environ);
if (input)
return (CMD_RETURN_WAIT);
return (CMD_RETURN_NORMAL);
}

View File

@@ -29,9 +29,7 @@
static enum cmd_retval cmd_resize_pane_exec(struct cmd *, struct cmdq_item *);
static void cmd_resize_pane_mouse_update_floating(struct client *,
struct mouse_event *);
static void cmd_resize_pane_mouse_update_tiled(struct client *,
static void cmd_resize_pane_mouse_update(struct client *,
struct mouse_event *);
const struct cmd_entry cmd_resize_pane_entry = {
@@ -82,15 +80,8 @@ cmd_resize_pane_exec(struct cmd *self, struct cmdq_item *item)
return (CMD_RETURN_NORMAL);
if (c == NULL || c->session != s)
return (CMD_RETURN_NORMAL);
if (c->tty.mouse_wp->flags & PANE_FLOATING) {
window_redraw_active_switch(w, c->tty.mouse_wp);
window_set_active_pane(w, c->tty.mouse_wp, 1);
c->tty.mouse_drag_update = cmd_resize_pane_mouse_update_floating;
cmd_resize_pane_mouse_update_floating(c, &event->m);
} else {
c->tty.mouse_drag_update = cmd_resize_pane_mouse_update_tiled;
cmd_resize_pane_mouse_update_tiled(c, &event->m);
}
c->tty.mouse_drag_update = cmd_resize_pane_mouse_update;
cmd_resize_pane_mouse_update(c, &event->m);
return (CMD_RETURN_NORMAL);
}
@@ -158,136 +149,7 @@ cmd_resize_pane_exec(struct cmd *self, struct cmdq_item *item)
}
static void
cmd_resize_pane_mouse_update_floating(struct client *c, struct mouse_event *m)
{
struct winlink *wl;
struct window *w;
struct window_pane *wp;
struct layout_cell *lc;
u_int y, ly, x, lx, new_sx, new_sy;
int new_xoff, new_yoff, resizes = 0;
wl = cmd_mouse_window(m, NULL);
if (wl == NULL) {
c->tty.mouse_drag_update = NULL;
return;
}
w = wl->window;
y = m->y + m->oy; x = m->x + m->ox;
if (m->statusat == 0 && y >= m->statuslines)
y -= m->statuslines;
else if (m->statusat > 0 && y >= (u_int)m->statusat)
y = m->statusat - 1;
ly = m->ly + m->oy; lx = m->lx + m->ox;
if (m->statusat == 0 && ly >= (u_int)m->statuslines)
ly -= m->statuslines;
else if (m->statusat > 0 && ly >= (u_int)m->statusat)
ly = m->statusat - 1;
wp = c->tty.mouse_wp;
lc = wp->layout_cell;
log_debug("%s: %%%u resize_pane xoff=%u sx=%u xy=%ux%u lxy=%ux%u",
__func__, wp->id, wp->xoff, wp->sx, x, y, lx, ly);
if ((((int)lx == wp->xoff - 1) || ((int)lx == wp->xoff)) &&
((int)ly == wp->yoff - 1)) {
/* Top left corner */
new_sx = lc->sx + (lx - x);
if (new_sx < PANE_MINIMUM)
new_sx = PANE_MINIMUM;
new_sy = lc->sy + (ly - y);
if (new_sy < PANE_MINIMUM)
new_sy = PANE_MINIMUM;
new_xoff = x + 1; /* Because mouse is on border at xoff - 1 */
new_yoff = y + 1;
layout_set_size(lc, new_sx, new_sy, new_xoff, new_yoff);
resizes++;
} else if ((((int)lx == wp->xoff + (int)wp->sx + 1) ||
((int)lx == wp->xoff + (int)wp->sx)) &&
((int)ly == wp->yoff - 1)) {
/* Top right corner */
new_sx = x - lc->xoff;
if (new_sx < PANE_MINIMUM)
new_sx = PANE_MINIMUM;
new_sy = lc->sy + (ly - y);
if (new_sy < PANE_MINIMUM)
new_sy = PANE_MINIMUM;
new_yoff = y + 1;
layout_set_size(lc, new_sx, new_sy, lc->xoff, new_yoff);
resizes++;
} else if ((((int)lx == wp->xoff - 1) || ((int)lx == wp->xoff)) &&
((int)ly == wp->yoff + (int)wp->sy)) {
/* Bottom left corner */
new_sx = lc->sx + (lx - x);
if (new_sx < PANE_MINIMUM)
new_sx = PANE_MINIMUM;
new_sy = y - lc->yoff;
if (new_sy < PANE_MINIMUM)
return;
new_xoff = x + 1;
layout_set_size(lc, new_sx, new_sy, new_xoff, lc->yoff);
resizes++;
} else if ((((int)lx == wp->xoff + (int)wp->sx + 1) ||
((int)lx == wp->xoff + (int)wp->sx)) &&
((int)ly == wp->yoff + (int)wp->sy)) {
/* Bottom right corner */
new_sx = x - lc->xoff;
if (new_sx < PANE_MINIMUM)
new_sx = PANE_MINIMUM;
new_sy = y - lc->yoff;
if (new_sy < PANE_MINIMUM)
new_sy = PANE_MINIMUM;
layout_set_size(lc, new_sx, new_sy, lc->xoff, lc->yoff);
resizes++;
} else if ((int)lx == wp->xoff + (int)wp->sx + 1) {
/* Right border */
new_sx = x - lc->xoff;
if (new_sx < PANE_MINIMUM)
return;
layout_set_size(lc, new_sx, lc->sy, lc->xoff, lc->yoff);
resizes++;
} else if ((int)lx == wp->xoff - 1) {
/* Left border */
new_sx = lc->sx + (lx - x);
if (new_sx < PANE_MINIMUM)
return;
new_xoff = x + 1;
layout_set_size(lc, new_sx, lc->sy, new_xoff, lc->yoff);
resizes++;
} else if ((int)ly == wp->yoff + (int)wp->sy) {
/* Bottom border */
new_sy = y - lc->yoff;
if (new_sy < PANE_MINIMUM)
return;
layout_set_size(lc, lc->sx, new_sy, lc->xoff, lc->yoff);
resizes++;
} else if ((int)ly == wp->yoff - 1) {
/* Top border (move instead of resize) */
new_xoff = lc->xoff + (x - lx);
new_yoff = y + 1;
layout_set_size(lc, lc->sx, lc->sy, new_xoff, new_yoff);
/* To resize instead of move:
new_sy = wp->sy + (ly - y);
if (new_sy < PANE_MINIMUM)
return;
new_yoff = y + 1;
layout_set_size(lc, lc->sx, new_sy, lc->xoff, new_yoff);
*/
resizes++;
} else {
log_debug("%s: %%%u resize_pane xoff=%u sx=%u xy=%ux%u lxy=%ux%u <else>",
__func__, wp->id, wp->xoff, wp->sx, x, y, lx, ly);
}
if (resizes != 0) {
layout_fix_panes(w, NULL);
server_redraw_window(w);
server_redraw_window_borders(w);
}
}
static void
cmd_resize_pane_mouse_update_tiled(struct client *c, struct mouse_event *m)
cmd_resize_pane_mouse_update(struct client *c, struct mouse_event *m)
{
struct winlink *wl;
struct window *w;

View File

@@ -90,7 +90,7 @@ cmd_select_layout_exec(struct cmd *self, struct cmdq_item *item)
previous = 1;
oldlayout = w->old_layout;
w->old_layout = layout_dump(w, w->layout_root);
w->old_layout = layout_dump(w->layout_root);
if (next || previous) {
if (next)

View File

@@ -160,10 +160,6 @@ cmd_select_pane_exec(struct cmd *self, struct cmdq_item *item)
server_redraw_window_borders(markedwp->window);
server_status_window(markedwp->window);
}
if (wp->flags & PANE_FLOATING) {
window_redraw_active_switch(w, wp);
window_set_active_pane(w, wp, 1);
}
return (CMD_RETURN_NORMAL);
}

View File

@@ -71,11 +71,6 @@ cmd_split_window_exec(struct cmd *self, struct cmdq_item *item)
struct args_value *av;
u_int count = args_count(args), curval = 0;
if (wp->flags & PANE_FLOATING) {
cmdq_error(item, "can't split a floating pane");
return (CMD_RETURN_ERROR);
}
type = LAYOUT_TOPBOTTOM;
if (args_has(args, 'h'))
type = LAYOUT_LEFTRIGHT;

10
cmd.c
View File

@@ -69,10 +69,8 @@ extern const struct cmd_entry cmd_load_buffer_entry;
extern const struct cmd_entry cmd_lock_client_entry;
extern const struct cmd_entry cmd_lock_server_entry;
extern const struct cmd_entry cmd_lock_session_entry;
extern const struct cmd_entry cmd_minimise_pane_entry;
extern const struct cmd_entry cmd_move_pane_entry;
extern const struct cmd_entry cmd_move_window_entry;
extern const struct cmd_entry cmd_new_pane_entry;
extern const struct cmd_entry cmd_new_session_entry;
extern const struct cmd_entry cmd_new_window_entry;
extern const struct cmd_entry cmd_next_layout_entry;
@@ -118,7 +116,6 @@ extern const struct cmd_entry cmd_swap_window_entry;
extern const struct cmd_entry cmd_switch_client_entry;
extern const struct cmd_entry cmd_unbind_key_entry;
extern const struct cmd_entry cmd_unlink_window_entry;
extern const struct cmd_entry cmd_unminimise_pane_entry;
extern const struct cmd_entry cmd_wait_for_entry;
const struct cmd_entry *cmd_table[] = {
@@ -164,10 +161,8 @@ const struct cmd_entry *cmd_table[] = {
&cmd_lock_client_entry,
&cmd_lock_server_entry,
&cmd_lock_session_entry,
&cmd_minimise_pane_entry,
&cmd_move_pane_entry,
&cmd_move_window_entry,
&cmd_new_pane_entry,
&cmd_new_session_entry,
&cmd_new_window_entry,
&cmd_next_layout_entry,
@@ -213,7 +208,6 @@ const struct cmd_entry *cmd_table[] = {
&cmd_switch_client_entry,
&cmd_unbind_key_entry,
&cmd_unlink_window_entry,
&cmd_unminimise_pane_entry,
&cmd_wait_for_entry,
NULL
};
@@ -778,9 +772,9 @@ cmd_mouse_at(struct window_pane *wp, struct mouse_event *m, u_int *xp,
if (m->statusat == 0 && y >= m->statuslines)
y -= m->statuslines;
if ((int)x < wp->xoff || (int)x >= wp->xoff + (int)wp->sx)
if (x < wp->xoff || x >= wp->xoff + wp->sx)
return (-1);
if ((int)y < wp->yoff || (int)y >= wp->yoff + (int)wp->sy)
if (y < wp->yoff || y >= wp->yoff + wp->sy)
return (-1);
if (xp != NULL)

View File

@@ -463,13 +463,32 @@ fi
# Enable sixel support.
AC_ARG_ENABLE(
sixel,
AS_HELP_STRING(--enable-sixel, enable sixel images)
sixel-images,
AS_HELP_STRING(--enable-sixel-images, enable sixel images)
)
if test "x$enable_sixel" = xyes; then
AC_DEFINE(ENABLE_SIXEL)
AC_ARG_ENABLE(
sixel,
AS_HELP_STRING(--enable-sixel support),
[
enable_sixel_images="$enableval"
]
)
if test "x$enable_sixel_images" = xyes; then
AC_DEFINE(ENABLE_SIXEL_IMAGES)
fi
AM_CONDITIONAL(ENABLE_SIXEL, [test "x$enable_sixel" = xyes])
AM_CONDITIONAL(ENABLE_SIXEL_IMAGES, [test "x$enable_sixel_images" = xyes])
# Enable kitty graphics protocol support.
AC_ARG_ENABLE(
kitty-images,
AS_HELP_STRING(--enable-kitty-images, enable kitty terminal graphics)
)
if test "x$enable_kitty_images" = xyes; then
AC_DEFINE(ENABLE_KITTY_IMAGES)
fi
AM_CONDITIONAL(ENABLE_KITTY_IMAGES, [test "x$enable_kitty_images" = xyes])
# Check for b64_ntop. If we have b64_ntop, we assume b64_pton as well.
AC_MSG_CHECKING(for b64_ntop)

View File

@@ -825,8 +825,8 @@ format_cb_window_layout(struct format_tree *ft)
return (NULL);
if (w->saved_layout_root != NULL)
return (layout_dump(w, w->saved_layout_root));
return (layout_dump(w, w->layout_root));
return (layout_dump(w->saved_layout_root));
return (layout_dump(w->layout_root));
}
/* Callback for window_visible_layout. */
@@ -838,7 +838,7 @@ format_cb_window_visible_layout(struct format_tree *ft)
if (w == NULL)
return (NULL);
return (layout_dump(w, w->layout_root));
return (layout_dump(w->layout_root));
}
/* Callback for pane_start_command. */
@@ -1004,20 +1004,6 @@ format_cb_pane_fg(struct format_tree *ft)
return (xstrdup(colour_tostring(gc.fg)));
}
/* Callback for pane_floating_flag. */
static void *
format_cb_pane_floating_flag(struct format_tree *ft)
{
struct window_pane *wp = ft->wp;
if (wp != NULL) {
if (wp->flags & PANE_FLOATING)
return (xstrdup("1"));
return (xstrdup("0"));
}
return (NULL);
}
/* Callback for pane_bg. */
static void *
format_cb_pane_bg(struct format_tree *ft)
@@ -2576,12 +2562,16 @@ format_cb_version(__unused struct format_tree *ft)
/* Callback for sixel_support. */
static void *
format_cb_sixel_support(__unused struct format_tree *ft)
format_cb_image_support(__unused struct format_tree *ft)
{
#ifdef ENABLE_SIXEL
return (xstrdup("1"));
#if defined(ENABLE_SIXEL_IMAGES) && defined(ENABLE_KITTY_IMAGES)
return (xstrdup("kitty,sixel"));
#elif defined(ENABLE_SIXEL_IMAGES)
return (xstrdup("sixel"));
#elif defined(ENABLE_KITTY_IMAGES)
return (xstrdup("kitty"));
#else
return (xstrdup("0"));
return (NULL);
#endif
}
@@ -3204,6 +3194,9 @@ static const struct format_table_entry format_table[] = {
{ "host_short", FORMAT_TABLE_STRING,
format_cb_host_short
},
{ "image_support", FORMAT_TABLE_STRING,
format_cb_image_support
},
{ "insert_flag", FORMAT_TABLE_STRING,
format_cb_insert_flag
},
@@ -3309,9 +3302,6 @@ static const struct format_table_entry format_table[] = {
{ "pane_fg", FORMAT_TABLE_STRING,
format_cb_pane_fg
},
{ "pane_floating_flag", FORMAT_TABLE_STRING,
format_cb_pane_floating_flag
},
{ "pane_format", FORMAT_TABLE_STRING,
format_cb_pane_format
},
@@ -3483,9 +3473,6 @@ static const struct format_table_entry format_table[] = {
{ "session_windows", FORMAT_TABLE_STRING,
format_cb_session_windows
},
{ "sixel_support", FORMAT_TABLE_STRING,
format_cb_sixel_support
},
{ "socket_path", FORMAT_TABLE_STRING,
format_cb_socket_path
},

11
grid.c
View File

@@ -495,7 +495,7 @@ static void
grid_expand_line(struct grid *gd, u_int py, u_int sx, u_int bg)
{
struct grid_line *gl;
u_int xx;
u_int xx, old_cellsize;
gl = &gd->linedata[py];
if (sx <= gl->cellsize)
@@ -508,13 +508,10 @@ grid_expand_line(struct grid *gd, u_int py, u_int sx, u_int bg)
else if (gd->sx > sx)
sx = gd->sx;
gl->celldata = xreallocarray(gl->celldata, sx,
old_cellsize = gl->cellsize;
gl->celldata = xrecallocarray(gl->celldata, old_cellsize, sx,
sizeof *gl->celldata);
if (gl->cellsize < sx) {
memset(gl->celldata + gl->cellsize, 0,
(sx - gl->cellsize) * sizeof *gl->celldata);
}
for (xx = gl->cellsize; xx < sx; xx++)
for (xx = old_cellsize; xx < sx; xx++)
grid_clear_cell(gd, xx, py, bg);
gl->cellsize = sx;
}

325
image-kitty.c Normal file
View File

@@ -0,0 +1,325 @@
/* $OpenBSD$ */
/*
* Copyright (c) 2026 Thomas Adam <thomas@xteddy.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 <stdlib.h>
#include <string.h>
#include "tmux.h"
/*
* kitty_image stores the raw decoded pixel data and metadata from a kitty
* graphics protocol APC sequence. It is used to re-emit the sequence to the
* outer terminal on redraw.
*/
struct kitty_image {
/* Control-data fields parsed from the APC sequence. */
char action; /* a=: 'T'=transmit+display, 't', 'p', 'd' */
u_int format; /* f=: 32=RGBA, 24=RGB, 100=PNG */
char medium; /* t=: 'd'=direct, 'f'=file, 't'=tmp, 's'=shm */
u_int pixel_w; /* s=: source image pixel width */
u_int pixel_h; /* v=: source image pixel height */
u_int cols; /* c=: display columns (0=auto) */
u_int rows; /* r=: display rows (0=auto) */
u_int image_id; /* i=: image id (0=unassigned) */
u_int image_num; /* I=: image number */
u_int placement_id; /* p=: placement id */
u_int more; /* m=: 1=more chunks coming, 0=last */
u_int quiet; /* q=: suppress responses */
int z_index; /* z=: z-index */
char compression; /* o=: 'z'=zlib, 0=none */
char delete_what; /* d=: delete target (used with a=d) */
/* Cell size at the time of parsing (from the owning window). */
u_int xpixel;
u_int ypixel;
/* Original base64-encoded payload (concatenated across all chunks). */
char *encoded;
size_t encodedlen;
char *ctrl;
size_t ctrllen;
};
/*
* Parse control-data key=value pairs from a kitty APC sequence.
* Format: key=value,key=value,...
*/
static int
kitty_parse_control(const char *ctrl, size_t ctrllen, struct kitty_image *ki)
{
const char *p = ctrl, *end = ctrl + ctrllen, *errstr;
char key[4], val[32];
size_t klen, vlen;
while (p < end) {
klen = 0;
while (p < end && *p != '=' && klen < sizeof(key) - 1)
key[klen++] = *p++;
key[klen] = '\0';
if (p >= end || *p != '=')
return (-1);
p++;
vlen = 0;
while (p < end && *p != ',' && vlen < sizeof(val) - 1)
val[vlen++] = *p++;
val[vlen] = '\0';
if (p < end && *p == ',')
p++;
if (klen != 1)
continue;
switch (key[0]) {
case 'a':
ki->action = val[0];
break;
case 'f':
ki->format = strtonum(val, 0, UINT_MAX, &errstr);
if (errstr != NULL)
return (-1);
break;
case 't':
ki->medium = val[0];
break;
case 's':
ki->pixel_w = strtonum(val, 0, UINT_MAX, &errstr);
if (errstr != NULL)
return (-1);
break;
case 'v':
ki->pixel_h = strtonum(val, 0, UINT_MAX, &errstr);
if (errstr != NULL)
return (-1);
break;
case 'c':
ki->cols = strtonum(val, 0, UINT_MAX, &errstr);
if (errstr != NULL)
return (-1);
break;
case 'r':
ki->rows = strtonum(val, 0, UINT_MAX, &errstr);
if (errstr != NULL)
return (-1);
break;
case 'i':
ki->image_id = strtonum(val, 0, UINT_MAX, &errstr);
if (errstr != NULL)
return (-1);
break;
case 'I':
ki->image_num = strtonum(val, 0, UINT_MAX, &errstr);
if (errstr != NULL)
return (-1);
break;
case 'p':
ki->placement_id = strtonum(val, 0, UINT_MAX, &errstr);
if (errstr != NULL)
return (-1);
break;
case 'm':
ki->more = strtonum(val, 0, UINT_MAX, &errstr);
if (errstr != NULL)
return (-1);
break;
case 'q':
ki->quiet = strtonum(val, 0, UINT_MAX, &errstr);
if (errstr != NULL)
return (-1);
break;
case 'z':
ki->z_index = strtonum(val, INT_MIN, INT_MAX, &errstr);
if (errstr != NULL)
return (-1);
break;
case 'o':
ki->compression = val[0];
break;
case 'd':
ki->delete_what = val[0];
break;
}
}
return (0);
}
/*
* Parse a kitty APC body (after the leading 'G').
* Stores the original control string and base64 payload verbatim for
* pass-through re-emission to the outer terminal.
*/
struct kitty_image *
kitty_parse(const u_char *buf, size_t len, u_int xpixel, u_int ypixel)
{
struct kitty_image *ki;
const u_char *semi;
const char *ctrl;
size_t ctrllen, paylen;
if (len == 0)
return (NULL);
semi = memchr(buf, ';', len);
if (semi != NULL) {
ctrl = (const char *)buf;
ctrllen = semi - buf;
paylen = len - ctrllen - 1;
} else {
ctrl = (const char *)buf;
ctrllen = len;
paylen = 0;
}
ki = xcalloc(1, sizeof *ki);
ki->xpixel = xpixel;
ki->ypixel = ypixel;
ki->action = 'T';
ki->format = 32;
ki->medium = 'd';
if (kitty_parse_control(ctrl, ctrllen, ki) != 0) {
free(ki);
return (NULL);
}
if (paylen > 0) {
ki->encoded = xmalloc(paylen + 1);
memcpy(ki->encoded, semi + 1, paylen);
ki->encoded[paylen] = '\0';
ki->encodedlen = paylen;
}
ki->ctrl = xmalloc(ctrllen + 1);
memcpy(ki->ctrl, ctrl, ctrllen);
ki->ctrl[ctrllen] = '\0';
ki->ctrllen = ctrllen;
return (ki);
}
void
kitty_free(struct kitty_image *ki)
{
if (ki == NULL)
return;
free(ki->encoded);
free(ki->ctrl);
free(ki);
}
/*
* Get the size in cells of a kitty image. If cols/rows are 0 (auto),
* calculate from pixel dimensions. Returns size via sx/sy pointers.
*/
void
kitty_size_in_cells(struct kitty_image *ki, u_int *sx, u_int *sy)
{
*sx = ki->cols;
*sy = ki->rows;
/*
* If cols/rows are 0, they mean "auto" - calculate from
* pixel dimensions.
*/
if (*sx == 0 && ki->pixel_w > 0 && ki->xpixel > 0) {
*sx = (ki->pixel_w + ki->xpixel - 1) / ki->xpixel;
}
if (*sy == 0 && ki->pixel_h > 0 && ki->ypixel > 0) {
*sy = (ki->pixel_h + ki->ypixel - 1) / ki->ypixel;
}
/* If still 0, use a reasonable default */
if (*sx == 0)
*sx = 10;
if (*sy == 0)
*sy = 10;
}
char
kitty_get_action(struct kitty_image *ki)
{
return (ki->action);
}
u_int
kitty_get_image_id(struct kitty_image *ki)
{
return (ki->image_id);
}
u_int
kitty_get_rows(struct kitty_image *ki)
{
return (ki->rows);
}
/*
* Serialize a kitty_image back into an APC escape sequence for transmission
* to the terminal. This recreates the original command that was parsed.
*/
char *
kitty_print(struct kitty_image *ki, size_t *outlen)
{
char *out;
size_t total, pos;
if (ki == NULL || ki->ctrl == NULL)
return (NULL);
/* Calculate total length: ESC _ G + ctrl + ; + encoded + ESC \ */
total = 3 + ki->ctrllen; /* \033_G + ctrl */
if (ki->encoded != NULL && ki->encodedlen > 0) {
total += 1 + ki->encodedlen; /* ; + encoded */
}
total += 2; /* \033\\ */
out = xmalloc(total + 1);
*outlen = total;
/* Build the sequence */
pos = 0;
memcpy(out + pos, "\033_G", 3);
pos += 3;
memcpy(out + pos, ki->ctrl, ki->ctrllen);
pos += ki->ctrllen;
if (ki->encoded != NULL && ki->encodedlen > 0) {
out[pos++] = ';';
memcpy(out + pos, ki->encoded, ki->encodedlen);
pos += ki->encodedlen;
}
memcpy(out + pos, "\033\\", 2);
pos += 2;
out[pos] = '\0';
return (out);
}
char *
kitty_delete_all(size_t *outlen)
{
char *out;
out = xstrdup("\033_Ga=d,d=a\033\\");
*outlen = strlen(out);
return (out);
}

112
image.c
View File

@@ -61,7 +61,22 @@ image_free(struct image *im)
all_images_count--;
TAILQ_REMOVE(&s->images, im, entry);
sixel_free(im->data);
switch (im->type) {
#ifdef ENABLE_SIXEL_IMAGES
case IMAGE_SIXEL:
sixel_free(im->data.sixel);
break;
#endif
#ifdef ENABLE_KITTY_IMAGES
case IMAGE_KITTY:
kitty_free(im->data.kitty);
break;
#endif
default:
break;
}
free(im->fallback);
free(im);
}
@@ -81,13 +96,30 @@ image_free_all(struct screen *s)
/* Create text placeholder for an image. */
static void
image_fallback(char **ret, u_int sx, u_int sy)
image_fallback(char **ret, enum image_type type, u_int sx, u_int sy)
{
char *buf, *label;
u_int py, size, lsize;
const char *type_name;
switch (type) {
#ifdef ENABLE_SIXEL_IMAGES
case IMAGE_SIXEL:
type_name = "SIXEL";
break;
#endif
#ifdef ENABLE_KITTY_IMAGES
case IMAGE_KITTY:
type_name = "KITTY";
break;
#endif
default:
type_name = "UNKNOWN";
break;
}
/* Allocate first line. */
lsize = xasprintf(&label, "SIXEL IMAGE (%ux%u)\r\n", sx, sy) + 1;
lsize = xasprintf(&label, "%s IMAGE (%ux%u)\r\n", type_name, sx, sy) + 1;
if (sx < lsize - 3)
size = lsize - 1;
else
@@ -122,19 +154,36 @@ image_fallback(char **ret, u_int sx, u_int sy)
}
struct image*
image_store(struct screen *s, struct sixel_image *si)
image_store(struct screen *s, enum image_type type, void *data)
{
struct image *im;
im = xcalloc(1, sizeof *im);
im->type = type;
im->s = s;
im->data = si;
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);
switch (type) {
#ifdef ENABLE_SIXEL_IMAGES
case IMAGE_SIXEL:
im->data.sixel = data;
sixel_size_in_cells(im->data.sixel, &im->sx, &im->sy);
break;
#endif
#ifdef ENABLE_KITTY_IMAGES
case IMAGE_KITTY:
im->data.kitty = data;
kitty_size_in_cells(im->data.kitty, &im->sx, &im->sy);
break;
#endif
default:
break;
}
image_fallback(&im->fallback, type, im->sx, im->sy);
image_log(im, __func__, NULL);
TAILQ_INSERT_TAIL(&s->images, im, entry);
@@ -188,8 +237,10 @@ image_scroll_up(struct screen *s, u_int lines)
{
struct image *im, *im1;
int redraw = 0;
u_int sx, sy;
#ifdef ENABLE_SIXEL_IMAGES
struct sixel_image *new;
u_int sx, sy;
#endif
TAILQ_FOREACH_SAFE(im, &s->images, entry, im1) {
if (im->py >= lines) {
@@ -204,20 +255,43 @@ image_scroll_up(struct screen *s, u_int lines)
redraw = 1;
continue;
}
sx = im->sx;
sy = (im->py + im->sy) - lines;
image_log(im, __func__, "3, lines=%u, sy=%u", lines, sy);
new = sixel_scale(im->data, 0, 0, 0, im->sy - sy, sx, sy, 1);
sixel_free(im->data);
im->data = new;
/* Image is partially scrolled off - need to crop it */
switch (im->type) {
#ifdef ENABLE_SIXEL_IMAGES
case IMAGE_SIXEL:
sx = im->sx;
sy = (im->py + im->sy) - lines;
image_log(im, __func__, "sixel, lines=%u, sy=%u",
lines, sy);
im->py = 0;
sixel_size_in_cells(im->data, &im->sx, &im->sy);
new = sixel_scale(im->data.sixel, 0, 0, 0, im->sy - sy,
sx, sy, 1);
sixel_free(im->data.sixel);
im->data.sixel = new;
free(im->fallback);
image_fallback(&im->fallback, im->sx, im->sy);
redraw = 1;
im->py = 0;
sixel_size_in_cells(im->data.sixel, &im->sx, &im->sy);
free(im->fallback);
image_fallback(&im->fallback, im->type, im->sx, im->sy);
redraw = 1;
break;
#endif
#ifdef ENABLE_KITTY_IMAGES
case IMAGE_KITTY:
/*
* For kitty images, we can't rescale - the terminal
* owns the placement. Just adjust position and let
* the terminal handle clipping.
*/
im->py = 0;
redraw = 1;
break;
#endif
default:
break;
}
}
return (redraw);
}

101
input.c
View File

@@ -145,6 +145,7 @@ struct input_ctx {
*/
struct evbuffer *since_ground;
struct event ground_timer;
};
/* Helper functions. */
@@ -180,6 +181,7 @@ static void input_ground(struct input_ctx *);
static void input_enter_dcs(struct input_ctx *);
static void input_enter_osc(struct input_ctx *);
static void input_exit_osc(struct input_ctx *);
static int input_da1_has_sixel(struct input_ctx *);
static void input_enter_apc(struct input_ctx *);
static void input_exit_apc(struct input_ctx *);
static void input_enter_rename(struct input_ctx *);
@@ -1381,6 +1383,10 @@ input_esc_dispatch(struct input_ctx *ictx)
input_reset_cell(ictx);
screen_write_reset(sctx);
screen_write_fullredraw(sctx);
#ifdef ENABLE_KITTY_IMAGES
if (ictx->wp != NULL)
tty_kitty_delete_all_pane(ictx->wp);
#endif
break;
case INPUT_ESC_IND:
screen_write_linefeed(sctx, 0, ictx->cell.cell.bg);
@@ -1556,11 +1562,10 @@ input_csi_dispatch(struct input_ctx *ictx)
case -1:
break;
case 0:
#ifdef ENABLE_SIXEL
input_reply(ictx, 1, "\033[?1;2;4c");
#else
input_reply(ictx, 1, "\033[?1;2c");
#endif
if (input_da1_has_sixel(ictx))
input_reply(ictx, 1, "\033[?1;2;4c");
else
input_reply(ictx, 1, "\033[?1;2c");
break;
default:
log_debug("%s: unknown '%c'", __func__, ictx->ch);
@@ -2030,7 +2035,7 @@ input_csi_dispatch_sm_private(struct input_ctx *ictx)
static void
input_csi_dispatch_sm_graphics(__unused struct input_ctx *ictx)
{
#ifdef ENABLE_SIXEL
#ifdef ENABLE_SIXEL_IMAGES
int n, m, o;
if (ictx->param_list_len > 3)
@@ -2555,7 +2560,7 @@ input_dcs_dispatch(struct input_ctx *ictx)
const char prefix[] = "tmux;";
const u_int prefixlen = (sizeof prefix) - 1;
long long allow_passthrough = 0;
#ifdef ENABLE_SIXEL
#ifdef ENABLE_SIXEL_IMAGES
struct window *w;
struct sixel_image *si;
int p2;
@@ -2570,7 +2575,7 @@ input_dcs_dispatch(struct input_ctx *ictx)
return (0);
}
#ifdef ENABLE_SIXEL
#ifdef ENABLE_SIXEL_IMAGES
w = wp->window;
if (buf[0] == 'q' && ictx->interm_len == 0) {
if (input_split(ictx) != 0)
@@ -2716,6 +2721,79 @@ input_enter_apc(struct input_ctx *ictx)
ictx->flags &= ~INPUT_LAST;
}
/*
* Check if any client viewing this pane has an outer terminal that supports
* sixel, so we can report it in the DA1 response.
*/
static int
input_da1_has_sixel(__unused struct input_ctx *ictx)
{
#ifdef ENABLE_SIXEL_IMAGES
struct window_pane *wp = ictx->wp;
struct client *c;
if (wp == NULL)
return (0);
TAILQ_FOREACH(c, &clients, entry) {
if (c->session == NULL)
continue;
if (c->session->curw->window != wp->window)
continue;
if (c->tty.term->flags & TERM_SIXEL)
return (1);
}
#endif
return (0);
}
#ifdef ENABLE_KITTY_IMAGES
/* Handle a kitty graphics APC sequence. */
static void
input_apc_kitty_image(struct input_ctx *ictx)
{
struct screen_write_ctx *sctx = &ictx->ctx;
struct window_pane *wp = ictx->wp;
struct window *w;
struct kitty_image *ki;
if (wp == NULL)
return;
w = wp->window;
ki = kitty_parse(ictx->input_buf + 1, ictx->input_len - 1,
w->xpixel, w->ypixel);
if (ki == NULL)
return;
/* Handle query commands. */
if (kitty_get_action(ki) == 'q') {
if (kitty_get_image_id(ki) != 0)
input_reply(ictx, 0, "\033_Gi=%u;OK\033\\",
kitty_get_image_id(ki));
else
input_reply(ictx, 0, "\033_Ga=q;OK\033\\");
kitty_free(ki);
return;
}
/* Store image placements and trigger a redraw. */
if (kitty_get_action(ki) == 'T' || kitty_get_action(ki) == 't' ||
kitty_get_action(ki) == 'p') {
screen_write_kittyimage(sctx, ki);
} else {
/* For other actions (delete, etc.), pass through. */
char *apc;
size_t apclen;
apclen = xasprintf(&apc, "\033_%s\033\\", ictx->input_buf);
tty_kitty_passthrough(wp, apc, apclen, sctx->s->cx,
sctx->s->cy);
free(apc);
kitty_free(ki);
}
}
#endif
/* APC terminator (ST) received. */
static void
input_exit_apc(struct input_ctx *ictx)
@@ -2727,6 +2805,13 @@ input_exit_apc(struct input_ctx *ictx)
return;
log_debug("%s: \"%s\"", __func__, ictx->input_buf);
#ifdef ENABLE_KITTY_IMAGES
if (ictx->input_len >= 1 && ictx->input_buf[0] == 'G') {
input_apc_kitty_image(ictx);
return;
}
#endif
if (wp != NULL &&
options_get_number(wp->options, "allow-set-title") &&
screen_set_title(sctx->s, ictx->input_buf)) {

View File

@@ -350,10 +350,6 @@ key_bindings_init(void)
{
static const char *const defaults[] = {
/* Prefix keys. */
"bind -N 'Minimise pane' _ { minimise-pane }",
/* Mouse button 1 double click on status line. */
"bind -n DoubleClick1Status { minimise-pane -t= }",
"bind -N 'Send the prefix key' C-b { send-prefix }",
"bind -N 'Rotate through the panes' C-o { rotate-window }",
"bind -N 'Suspend the current client' C-z { suspend-client }",
@@ -365,7 +361,6 @@ key_bindings_init(void)
"bind -N 'Split window horizontally' % { split-window -h }",
"bind -N 'Kill current window' & { confirm-before -p\"kill-window #W? (y/n)\" kill-window }",
"bind -N 'Prompt for window index to select' \"'\" { command-prompt -T window-target -pindex { select-window -t ':%%' } }",
"bind -N 'New floating pane' * { new-pane }",
"bind -N 'Switch to previous client' ( { switch-client -p }",
"bind -N 'Switch to next client' ) { switch-client -n }",
"bind -N 'Rename current window' , { command-prompt -I'#W' { rename-window -- '%%' } }",
@@ -464,9 +459,6 @@ key_bindings_init(void)
/* Mouse button 1 triple click on pane. */
"bind -n TripleClick1Pane { select-pane -t=; if -F '#{||:#{pane_in_mode},#{mouse_any_flag}}' { send -M } { copy-mode -H; send -X select-line; run -d0.3; send -X copy-pipe-and-cancel } }",
/* Mouse button 1 on border. */
"bind -n MouseDown1Border { select-pane -M }",
/* Mouse button 1 drag on border. */
"bind -n MouseDrag1Border { resize-pane -M }",

View File

@@ -27,11 +27,10 @@ static struct layout_cell *layout_find_bottomright(struct layout_cell *);
static u_short layout_checksum(const char *);
static int layout_append(struct layout_cell *, char *,
size_t);
static int layout_construct(struct layout_cell *,
const char **, struct layout_cell **,
struct layout_cell **);
static struct layout_cell *layout_construct(struct layout_cell *,
const char **);
static void layout_assign(struct window_pane **,
struct layout_cell *, int);
struct layout_cell *);
/* Find the bottom-right cell. */
static struct layout_cell *
@@ -59,33 +58,14 @@ layout_checksum(const char *layout)
/* Dump layout as a string. */
char *
layout_dump(struct window *w, struct layout_cell *root)
layout_dump(struct layout_cell *root)
{
char layout[8192], *out;
int braket;
struct window_pane *wp;
char layout[8192], *out;
*layout = '\0';
if (layout_append(root, layout, sizeof layout) != 0)
return (NULL);
braket = 0;
TAILQ_FOREACH(wp, &w->z_index, zentry) {
if (~wp->flags & PANE_FLOATING)
break;
if (!braket) {
strcat(layout, "<");
braket = 1;
}
if (layout_append(wp->layout_cell, layout, sizeof layout) != 0)
return (NULL);
strcat(layout, ",");
}
if (braket) {
/* Overwrite the trailing ','. */
layout[strlen(layout) - 1] = '>';
}
xasprintf(&out, "%04hx,%s", layout_checksum(layout), layout);
return (out);
}
@@ -101,8 +81,7 @@ layout_append(struct layout_cell *lc, char *buf, size_t len)
if (len == 0)
return (-1);
if (lc == NULL)
return (0);
if (lc->wp != NULL) {
tmplen = xsnprintf(tmp, sizeof tmp, "%ux%u,%u,%u,%u",
lc->sx, lc->sy, lc->xoff, lc->yoff, lc->wp->id);
@@ -130,7 +109,6 @@ layout_append(struct layout_cell *lc, char *buf, size_t len)
}
buf[strlen(buf) - 1] = brackets[0];
break;
case LAYOUT_FLOATING:
case LAYOUT_WINDOWPANE:
break;
}
@@ -147,7 +125,6 @@ layout_check(struct layout_cell *lc)
switch (lc->type) {
case LAYOUT_WINDOWPANE:
case LAYOUT_FLOATING:
break;
case LAYOUT_LEFTRIGHT:
TAILQ_FOREACH(lcchild, &lc->cells, entry) {
@@ -179,7 +156,7 @@ layout_check(struct layout_cell *lc)
int
layout_parse(struct window *w, const char *layout, char **cause)
{
struct layout_cell *lcchild, *tiled_lc = NULL, *floating_lc = NULL;
struct layout_cell *lc, *lcchild;
struct window_pane *wp;
u_int npanes, ncells, sx = 0, sy = 0;
u_short csum;
@@ -196,16 +173,11 @@ layout_parse(struct window *w, const char *layout, char **cause)
}
/* Build the layout. */
if (layout_construct(NULL, &layout, &tiled_lc, &floating_lc) != 0) {
lc = layout_construct(NULL, &layout);
if (lc == NULL) {
*cause = xstrdup("invalid layout");
return (-1);
}
if (tiled_lc == NULL) {
/* A stub layout cell for an empty window. */
tiled_lc = layout_create_cell(NULL);
tiled_lc->type = LAYOUT_LEFTRIGHT;
layout_set_size(tiled_lc, w->sx, w->sy, 0, 0);
}
if (*layout != '\0') {
*cause = xstrdup("invalid layout");
goto fail;
@@ -214,10 +186,8 @@ layout_parse(struct window *w, const char *layout, char **cause)
/* Check this window will fit into the layout. */
for (;;) {
npanes = window_count_panes(w);
ncells = layout_count_cells(tiled_lc);
ncells += layout_count_cells(floating_lc);
ncells = layout_count_cells(lc);
if (npanes > ncells) {
/* Modify this to open a new pane */
xasprintf(cause, "have %u panes but need %u", npanes,
ncells);
goto fail;
@@ -225,17 +195,9 @@ layout_parse(struct window *w, const char *layout, char **cause)
if (npanes == ncells)
break;
/*
* Fewer panes than cells - close floating panes first
* then close the bottom right until.
*/
if (floating_lc && ! TAILQ_EMPTY(&floating_lc->cells)) {
lcchild = TAILQ_FIRST(&floating_lc->cells);
layout_destroy_cell(w, lcchild, &floating_lc);
} else {
lcchild = layout_find_bottomright(tiled_lc);
layout_destroy_cell(w, lcchild, &tiled_lc);
}
/* Fewer panes than cells - close the bottom right. */
lcchild = layout_find_bottomright(lc);
layout_destroy_cell(w, lcchild, &lc);
}
/*
@@ -243,112 +205,85 @@ layout_parse(struct window *w, const char *layout, char **cause)
* an incorrect top cell size - if it is larger than the top child then
* correct that (if this is still wrong the check code will catch it).
*/
switch (tiled_lc->type) {
switch (lc->type) {
case LAYOUT_WINDOWPANE:
break;
case LAYOUT_LEFTRIGHT:
TAILQ_FOREACH(lcchild, &tiled_lc->cells, entry) {
TAILQ_FOREACH(lcchild, &lc->cells, entry) {
sy = lcchild->sy + 1;
sx += lcchild->sx + 1;
}
break;
case LAYOUT_TOPBOTTOM:
TAILQ_FOREACH(lcchild, &tiled_lc->cells, entry) {
TAILQ_FOREACH(lcchild, &lc->cells, entry) {
sx = lcchild->sx + 1;
sy += lcchild->sy + 1;
}
break;
case LAYOUT_FLOATING:
*cause = xstrdup("invalid layout");
goto fail;
}
if (tiled_lc->type != LAYOUT_WINDOWPANE &&
(tiled_lc->sx != sx || tiled_lc->sy != sy)) {
log_debug("fix layout %u,%u to %u,%u", tiled_lc->sx,
tiled_lc->sy, sx,sy);
layout_print_cell(tiled_lc, __func__, 0);
tiled_lc->sx = sx - 1; tiled_lc->sy = sy - 1;
if (lc->type != LAYOUT_WINDOWPANE && (lc->sx != sx || lc->sy != sy)) {
log_debug("fix layout %u,%u to %u,%u", lc->sx, lc->sy, sx,sy);
layout_print_cell(lc, __func__, 0);
lc->sx = sx - 1; lc->sy = sy - 1;
}
/* Check the new layout. */
if (!layout_check(tiled_lc)) {
if (!layout_check(lc)) {
*cause = xstrdup("size mismatch after applying layout");
goto fail;
}
/* Resize window to the layout size. */
if (sx != 0 && sy != 0)
window_resize(w, tiled_lc->sx, tiled_lc->sy, -1, -1);
/* Resize to the layout size. */
window_resize(w, lc->sx, lc->sy, -1, -1);
/* Destroy the old layout and swap to the new. */
layout_free_cell(w->layout_root);
w->layout_root = tiled_lc;
w->layout_root = lc;
/* Assign the panes into the cells. */
wp = TAILQ_FIRST(&w->panes);
layout_assign(&wp, tiled_lc, 0);
layout_assign(&wp, floating_lc, 1);
/* Fix z_indexes. */
while (!TAILQ_EMPTY(&w->z_index)) {
wp = TAILQ_FIRST(&w->z_index);
TAILQ_REMOVE(&w->z_index, wp, zentry);
}
layout_fix_zindexes(w, floating_lc);
layout_fix_zindexes(w, tiled_lc);
layout_assign(&wp, lc);
/* Update pane offsets and sizes. */
layout_fix_offsets(w);
layout_fix_panes(w, NULL);
recalculate_sizes();
layout_print_cell(tiled_lc, __func__, 0);
layout_print_cell(floating_lc, __func__, 0);
/* Free the floating layout cell, no longer needed. */
layout_free_cell(floating_lc);
layout_print_cell(lc, __func__, 0);
notify_window("window-layout-changed", w);
return (0);
fail:
layout_free_cell(tiled_lc);
layout_free_cell(floating_lc);
layout_free_cell(lc);
return (-1);
}
/* Assign panes into cells. */
static void
layout_assign(struct window_pane **wp, struct layout_cell *lc, int floating)
layout_assign(struct window_pane **wp, struct layout_cell *lc)
{
struct layout_cell *lcchild;
if (lc == NULL)
return;
switch (lc->type) {
case LAYOUT_WINDOWPANE:
layout_make_leaf(lc, *wp);
if (floating) {
(*wp)->flags |= PANE_FLOATING;
}
*wp = TAILQ_NEXT(*wp, entry);
return;
case LAYOUT_LEFTRIGHT:
case LAYOUT_TOPBOTTOM:
case LAYOUT_FLOATING:
TAILQ_FOREACH(lcchild, &lc->cells, entry)
layout_assign(wp, lcchild, 1);
layout_assign(wp, lcchild);
return;
}
}
/* Construct a cell from all or part of a layout tree. */
static struct layout_cell *
layout_construct_cell(struct layout_cell *lcparent, const char **layout)
layout_construct(struct layout_cell *lcparent, const char **layout)
{
struct layout_cell *lc;
struct layout_cell *lc, *lcchild;
u_int sx, sy, xoff, yoff;
const char *saved;
@@ -389,42 +324,17 @@ layout_construct_cell(struct layout_cell *lcparent, const char **layout)
lc->xoff = xoff;
lc->yoff = yoff;
return (lc);
}
/*
* Given a character string layout, recursively construct cells.
* Possible return values:
* lc LAYOUT_WINDOWPANE, no children
* lc LAYOUT_LEFTRIGHT or LAYOUT_TOPBOTTOM, with children
* floating_lc LAYOUT_FLOATING, with children
*/
static int
layout_construct(struct layout_cell *lcparent, const char **layout,
struct layout_cell **lc, struct layout_cell **floating_lc)
{
struct layout_cell *lcchild, *saved_lc;
*lc = layout_construct_cell(lcparent, layout);
switch (**layout) {
case ',':
case '}':
case ']':
case '>':
case '\0':
return (0);
return (lc);
case '{':
(*lc)->type = LAYOUT_LEFTRIGHT;
lc->type = LAYOUT_LEFTRIGHT;
break;
case '[':
(*lc)->type = LAYOUT_TOPBOTTOM;
break;
case '<':
saved_lc = *lc;
*lc = layout_create_cell(lcparent);
(*lc)->type = LAYOUT_FLOATING;
lc->type = LAYOUT_TOPBOTTOM;
break;
default:
goto fail;
@@ -432,12 +342,13 @@ layout_construct(struct layout_cell *lcparent, const char **layout,
do {
(*layout)++;
if (layout_construct(*lc, layout, &lcchild, floating_lc) != 0)
lcchild = layout_construct(lc, layout);
if (lcchild == NULL)
goto fail;
TAILQ_INSERT_TAIL(&(*lc)->cells, lcchild, entry);
TAILQ_INSERT_TAIL(&lc->cells, lcchild, entry);
} while (**layout == ',');
switch ((*lc)->type) {
switch (lc->type) {
case LAYOUT_LEFTRIGHT:
if (**layout != '}')
goto fail;
@@ -446,21 +357,14 @@ layout_construct(struct layout_cell *lcparent, const char **layout,
if (**layout != ']')
goto fail;
break;
case LAYOUT_FLOATING:
if (**layout != '>')
goto fail;
*floating_lc = *lc;
*lc = saved_lc;
break;
default:
goto fail;
}
(*layout)++;
return (0);
return (lc);
fail:
layout_free_cell(*lc);
layout_free_cell(*floating_lc);
return (-1);
layout_free_cell(lc);
return (NULL);
}

175
layout.c
View File

@@ -83,24 +83,9 @@ layout_free_cell(struct layout_cell *lc)
layout_free_cell(lcchild);
}
break;
case LAYOUT_FLOATING:
/* A Floating layout cell is only used temporarily
* while select-layout constructs a layout.
* Cleave the children from the temp layout, then
* free temp floating layout cell. Each floating
* pane has stub layout.
*/
while (!TAILQ_EMPTY(&lc->cells)) {
lcchild = TAILQ_FIRST(&lc->cells);
TAILQ_REMOVE(&lc->cells, lcchild, entry);
lcchild->parent = NULL;
}
break;
case LAYOUT_WINDOWPANE:
if (lc->wp != NULL) {
lc->wp->layout_cell->parent = NULL;
if (lc->wp != NULL)
lc->wp->layout_cell = NULL;
}
break;
}
@@ -113,9 +98,6 @@ layout_print_cell(struct layout_cell *lc, const char *hdr, u_int n)
struct layout_cell *lcchild;
const char *type;
if (lc == NULL)
return;
switch (lc->type) {
case LAYOUT_LEFTRIGHT:
type = "LEFTRIGHT";
@@ -123,9 +105,6 @@ layout_print_cell(struct layout_cell *lc, const char *hdr, u_int n)
case LAYOUT_TOPBOTTOM:
type = "TOPBOTTOM";
break;
case LAYOUT_FLOATING:
type = "FLOATING";
break;
case LAYOUT_WINDOWPANE:
type = "WINDOWPANE";
break;
@@ -139,7 +118,6 @@ layout_print_cell(struct layout_cell *lc, const char *hdr, u_int n)
switch (lc->type) {
case LAYOUT_LEFTRIGHT:
case LAYOUT_TOPBOTTOM:
case LAYOUT_FLOATING:
TAILQ_FOREACH(lcchild, &lc->cells, entry)
layout_print_cell(lcchild, hdr, n + 1);
break;
@@ -175,7 +153,6 @@ layout_search_by_border(struct layout_cell *lc, u_int x, u_int y)
return (last);
break;
case LAYOUT_WINDOWPANE:
case LAYOUT_FLOATING:
break;
}
@@ -221,29 +198,6 @@ layout_make_node(struct layout_cell *lc, enum layout_type type)
lc->wp = NULL;
}
void
layout_fix_zindexes(struct window *w, struct layout_cell *lc)
{
struct layout_cell *lcchild;
if (lc == NULL)
return;
switch (lc->type) {
case LAYOUT_WINDOWPANE:
TAILQ_INSERT_TAIL(&w->z_index, lc->wp, zentry);
break;
case LAYOUT_LEFTRIGHT:
case LAYOUT_TOPBOTTOM:
case LAYOUT_FLOATING:
TAILQ_FOREACH(lcchild, &lc->cells, entry)
layout_fix_zindexes(w, lcchild);
return;
default:
fatalx("bad layout type");
}
}
/* Fix cell offsets for a child cell. */
static void
layout_fix_offsets1(struct layout_cell *lc)
@@ -254,9 +208,6 @@ layout_fix_offsets1(struct layout_cell *lc)
if (lc->type == LAYOUT_LEFTRIGHT) {
xoff = lc->xoff;
TAILQ_FOREACH(lcchild, &lc->cells, entry) {
if (lcchild->type == LAYOUT_WINDOWPANE &&
lcchild->wp->flags & PANE_MINIMISED)
continue;
lcchild->xoff = xoff;
lcchild->yoff = lc->yoff;
if (lcchild->type != LAYOUT_WINDOWPANE)
@@ -266,8 +217,6 @@ layout_fix_offsets1(struct layout_cell *lc)
} else {
yoff = lc->yoff;
TAILQ_FOREACH(lcchild, &lc->cells, entry) {
if (lcchild->wp->flags & PANE_MINIMISED)
continue;
lcchild->xoff = lc->xoff;
lcchild->yoff = yoff;
if (lcchild->type != LAYOUT_WINDOWPANE)
@@ -358,8 +307,7 @@ layout_fix_panes(struct window *w, struct window_pane *skip)
sx = lc->sx;
sy = lc->sy;
if (~wp->flags & PANE_FLOATING &&
layout_add_horizontal_border(w, lc, status)) {
if (layout_add_horizontal_border(w, lc, status)) {
if (status == PANE_STATUS_TOP)
wp->yoff++;
sy--;
@@ -405,7 +353,6 @@ layout_count_cells(struct layout_cell *lc)
return (1);
case LAYOUT_LEFTRIGHT:
case LAYOUT_TOPBOTTOM:
case LAYOUT_FLOATING:
count = 0;
TAILQ_FOREACH(lcchild, &lc->cells, entry)
count += layout_count_cells(lcchild);
@@ -515,7 +462,7 @@ layout_resize_adjust(struct window *w, struct layout_cell *lc,
}
}
/* Destroy a cell and redistribute the space in tiled cells. */
/* Destroy a cell and redistribute the space. */
void
layout_destroy_cell(struct window *w, struct layout_cell *lc,
struct layout_cell **lcroot)
@@ -523,66 +470,13 @@ layout_destroy_cell(struct window *w, struct layout_cell *lc,
struct layout_cell *lcother, *lcparent;
/*
* If no parent, this is either a floating pane or the last
* pane so window close is imminent and there is no need to
* resize anything.
* If no parent, this is the last pane so window close is imminent and
* there is no need to resize anything.
*/
lcparent = lc->parent;
if (lcparent == NULL) {
if (lc->wp != NULL && ~lc->wp->flags & PANE_FLOATING)
*lcroot = NULL;
layout_free_cell(lc);
return;
}
/* In tiled layouts, merge the space into the previous or next cell. */
if (lcparent->type != LAYOUT_FLOATING) {
if (lc == TAILQ_FIRST(&lcparent->cells))
lcother = TAILQ_NEXT(lc, entry);
else
lcother = TAILQ_PREV(lc, layout_cells, entry);
if (lcother != NULL && lcparent->type == LAYOUT_LEFTRIGHT)
layout_resize_adjust(w, lcother, lcparent->type, lc->sx + 1);
else if (lcother != NULL)
layout_resize_adjust(w, lcother, lcparent->type, lc->sy + 1);
}
/* Remove this from the parent's list. */
TAILQ_REMOVE(&lcparent->cells, lc, entry);
layout_free_cell(lc);
if (lcparent->type == LAYOUT_FLOATING)
return;
/*
* In tiled layouts, if the parent now has one cell, remove
* the parent from the tree and replace it by that cell.
*/
lc = TAILQ_FIRST(&lcparent->cells);
if (lc != NULL && TAILQ_NEXT(lc, entry) == NULL) {
TAILQ_REMOVE(&lcparent->cells, lc, entry);
lc->parent = lcparent->parent;
if (lc->parent == NULL) {
lc->xoff = 0; lc->yoff = 0;
*lcroot = lc;
} else
TAILQ_REPLACE(&lc->parent->cells, lcparent, lc, entry);
layout_free_cell(lcparent);
}
}
/* Minimise a cell and redistribute the space in tiled cells. */
void
layout_minimise_cell(struct window *w, struct layout_cell *lc)
{
struct layout_cell *lcother, *lcparent, *lcchild;
u_int space = 0;
lcparent = lc->parent;
if (lcparent == NULL ||
lcparent->type == LAYOUT_FLOATING) {
*lcroot = NULL;
return;
}
@@ -596,44 +490,26 @@ layout_minimise_cell(struct window *w, struct layout_cell *lc)
else if (lcother != NULL)
layout_resize_adjust(w, lcother, lcparent->type, lc->sy + 1);
/* If the parent cells are all minimised, minimise it too. */
if (lcparent != NULL) {
TAILQ_FOREACH(lcchild, &lcparent->cells, entry) {
if (lcchild->wp == NULL ||
lcchild->wp->flags & PANE_MINIMISED)
continue;
if (lcparent->type == LAYOUT_LEFTRIGHT) {
space += lcchild->sx;
} else if (lcparent->type == LAYOUT_TOPBOTTOM) {
space += lcchild->sy;
}
}
if (space == 0)
layout_minimise_cell(w, lcparent);
}
}
/* Remove this from the parent's list. */
TAILQ_REMOVE(&lcparent->cells, lc, entry);
layout_free_cell(lc);
/* Unminimise a cell and redistribute the space in tiled cells. */
void
layout_unminimise_cell(struct window *w, struct layout_cell *lc)
{
struct layout_cell *lcother, *lcparent;
/*
* If the parent now has one cell, remove the parent from the tree and
* replace it by that cell.
*/
lc = TAILQ_FIRST(&lcparent->cells);
if (TAILQ_NEXT(lc, entry) == NULL) {
TAILQ_REMOVE(&lcparent->cells, lc, entry);
lcparent = lc->parent;
if (lcparent == NULL) {
return;
}
lc->parent = lcparent->parent;
if (lc->parent == NULL) {
lc->xoff = 0; lc->yoff = 0;
*lcroot = lc;
} else
TAILQ_REPLACE(&lc->parent->cells, lcparent, lc, entry);
/* In tiled layouts, merge the space into the previous or next cell. */
if (lcparent->type != LAYOUT_FLOATING) {
if (lc == TAILQ_FIRST(&lcparent->cells))
lcother = TAILQ_NEXT(lc, entry);
else
lcother = TAILQ_PREV(lc, layout_cells, entry);
if (lcother != NULL && lcparent->type == LAYOUT_LEFTRIGHT)
layout_resize_adjust(w, lcother, lcparent->type, -(lc->sx + 1));
else if (lcother != NULL)
layout_resize_adjust(w, lcother, lcparent->type, -(lc->sy + 1));
layout_free_cell(lcparent);
}
}
@@ -865,7 +741,7 @@ layout_resize_pane_shrink(struct window *w, struct layout_cell *lc,
return (size);
}
/* Assign window pane to new cell. */
/* Assign window pane to newly split cell. */
void
layout_assign_pane(struct layout_cell *lc, struct window_pane *wp,
int do_not_resize)
@@ -1209,9 +1085,6 @@ layout_close_pane(struct window_pane *wp)
{
struct window *w = wp->window;
if (wp->layout_cell == NULL)
return;
/* Remove the cell. */
layout_destroy_cell(w, wp->layout_cell, &w->layout_root);

File diff suppressed because it is too large Load Diff

View File

@@ -36,7 +36,6 @@ static int screen_write_overwrite(struct screen_write_ctx *,
struct grid_cell *, u_int);
static int screen_write_combine(struct screen_write_ctx *,
const struct grid_cell *);
static int screen_write_pane_obscured(struct window_pane *);
struct screen_write_citem {
u_int x;
@@ -575,8 +574,6 @@ screen_write_fast_copy(struct screen_write_ctx *ctx, struct screen *src,
struct grid *gd = src->grid;
struct grid_cell gc;
u_int xx, yy, cx = s->cx, cy = s->cy;
int yoff = 0;
struct visible_ranges *r;
if (nx == 0 || ny == 0)
return;
@@ -585,24 +582,17 @@ screen_write_fast_copy(struct screen_write_ctx *ctx, struct screen *src,
if (yy >= gd->hsize + gd->sy)
break;
s->cx = cx;
screen_write_initctx(ctx, &ttyctx, 0);
if (wp != NULL) {
yoff = wp->yoff;
}
r = screen_redraw_get_visible_ranges(wp, px, s->cy + yoff, nx,
NULL);
if (wp != NULL)
screen_write_initctx(ctx, &ttyctx, 0);
for (xx = px; xx < px + nx; xx++) {
if (xx >= grid_get_line(gd, yy)->cellsize &&
s->cx >= grid_get_line(ctx->s->grid,
s->cy)->cellsize)
s->cx >= grid_get_line(ctx->s->grid, s->cy)->cellsize)
break;
grid_get_cell(gd, xx, yy, &gc);
if (xx + gc.data.width > px + nx)
break;
grid_view_set_cell(ctx->s->grid, s->cx, s->cy, &gc);
if (wp != NULL) {
if (! screen_redraw_is_visible(r, px))
break;
ttyctx.cell = &gc;
tty_write(tty_cmd_cell, &ttyctx);
ttyctx.ocx++;
@@ -1082,7 +1072,7 @@ screen_write_alignmenttest(struct screen_write_ctx *ctx)
memcpy(&gc, &grid_default_cell, sizeof gc);
utf8_set(&gc.data, 'E');
#ifdef ENABLE_SIXEL
#ifdef ENABLE_SIXEL_IMAGES
if (image_free_all(s) && ctx->wp != NULL)
ctx->wp->flags |= PANE_REDRAW;
#endif
@@ -1121,7 +1111,7 @@ screen_write_insertcharacter(struct screen_write_ctx *ctx, u_int nx, u_int bg)
if (s->cx > screen_size_x(s) - 1)
return;
#ifdef ENABLE_SIXEL
#ifdef ENABLE_SIXEL_IMAGES
if (image_check_line(s, s->cy, 1) && ctx->wp != NULL)
ctx->wp->flags |= PANE_REDRAW;
#endif
@@ -1154,7 +1144,7 @@ screen_write_deletecharacter(struct screen_write_ctx *ctx, u_int nx, u_int bg)
if (s->cx > screen_size_x(s) - 1)
return;
#ifdef ENABLE_SIXEL
#ifdef ENABLE_SIXEL_IMAGES
if (image_check_line(s, s->cy, 1) && ctx->wp != NULL)
ctx->wp->flags |= PANE_REDRAW;
#endif
@@ -1187,7 +1177,7 @@ screen_write_clearcharacter(struct screen_write_ctx *ctx, u_int nx, u_int bg)
if (s->cx > screen_size_x(s) - 1)
return;
#ifdef ENABLE_SIXEL
#ifdef ENABLE_SIXEL_IMAGES
if (image_check_line(s, s->cy, 1) && ctx->wp != NULL)
ctx->wp->flags |= PANE_REDRAW;
#endif
@@ -1210,14 +1200,14 @@ screen_write_insertline(struct screen_write_ctx *ctx, u_int ny, u_int bg)
struct grid *gd = s->grid;
struct tty_ctx ttyctx;
#ifdef ENABLE_SIXEL
#ifdef ENABLE_SIXEL_IMAGES
u_int sy = screen_size_y(s);
#endif
if (ny == 0)
ny = 1;
#ifdef ENABLE_SIXEL
#ifdef ENABLE_SIXEL_IMAGES
if (image_check_line(s, s->cy, sy - s->cy) && ctx->wp != NULL)
ctx->wp->flags |= PANE_REDRAW;
#endif
@@ -1270,7 +1260,7 @@ screen_write_deleteline(struct screen_write_ctx *ctx, u_int ny, u_int bg)
if (ny == 0)
ny = 1;
#ifdef ENABLE_SIXEL
#ifdef ENABLE_SIXEL_IMAGES
if (image_check_line(s, s->cy, sy - s->cy) && ctx->wp != NULL)
ctx->wp->flags |= PANE_REDRAW;
#endif
@@ -1323,7 +1313,7 @@ screen_write_clearline(struct screen_write_ctx *ctx, u_int bg)
if (gl->cellsize == 0 && COLOUR_DEFAULT(bg))
return;
#ifdef ENABLE_SIXEL
#ifdef ENABLE_SIXEL_IMAGES
if (image_check_line(s, s->cy, 1) && ctx->wp != NULL)
ctx->wp->flags |= PANE_REDRAW;
#endif
@@ -1357,7 +1347,7 @@ screen_write_clearendofline(struct screen_write_ctx *ctx, u_int bg)
if (s->cx > sx - 1 || (s->cx >= gl->cellsize && COLOUR_DEFAULT(bg)))
return;
#ifdef ENABLE_SIXEL
#ifdef ENABLE_SIXEL_IMAGES
if (image_check_line(s, s->cy, 1) && ctx->wp != NULL)
ctx->wp->flags |= PANE_REDRAW;
#endif
@@ -1384,7 +1374,7 @@ screen_write_clearstartofline(struct screen_write_ctx *ctx, u_int bg)
return;
}
#ifdef ENABLE_SIXEL
#ifdef ENABLE_SIXEL_IMAGES
if (image_check_line(s, s->cy, 1) && ctx->wp != NULL)
ctx->wp->flags |= PANE_REDRAW;
#endif
@@ -1401,23 +1391,6 @@ screen_write_clearstartofline(struct screen_write_ctx *ctx, u_int bg)
screen_write_collect_insert(ctx, ci);
}
/* Clear part of a line from px for nx columns. */
static void
screen_write_clearpartofline(struct screen_write_ctx *ctx, u_int px, u_int nx,
u_int bg)
{
struct screen_write_citem *ci = ctx->item;
if (nx == 0)
return;
ci->x = px;
ci->used = nx;
ci->type = CLEAR;
ci->bg = bg;
screen_write_collect_insert(ctx, ci);
}
/* Move cursor to px,py. */
void
screen_write_cursormove(struct screen_write_ctx *ctx, int px, int py,
@@ -1449,7 +1422,7 @@ screen_write_reverseindex(struct screen_write_ctx *ctx, u_int bg)
struct tty_ctx ttyctx;
if (s->cy == s->rupper) {
#ifdef ENABLE_SIXEL
#ifdef ENABLE_SIXEL_IMAGES
if (image_free_all(s) && ctx->wp != NULL)
ctx->wp->flags |= PANE_REDRAW;
#endif
@@ -1496,7 +1469,7 @@ screen_write_linefeed(struct screen_write_ctx *ctx, int wrapped, u_int bg)
struct screen *s = ctx->s;
struct grid *gd = s->grid;
struct grid_line *gl;
#ifdef ENABLE_SIXEL
#ifdef ENABLE_SIXEL_IMAGES
int redraw = 0;
#endif
u_int rupper = s->rupper, rlower = s->rlower;
@@ -1514,7 +1487,7 @@ screen_write_linefeed(struct screen_write_ctx *ctx, int wrapped, u_int bg)
}
if (s->cy == s->rlower) {
#ifdef ENABLE_SIXEL
#ifdef ENABLE_SIXEL_IMAGES
if (rlower == screen_size_y(s) - 1)
redraw = image_scroll_up(s, 1);
else
@@ -1547,7 +1520,7 @@ screen_write_scrollup(struct screen_write_ctx *ctx, u_int lines, u_int bg)
ctx->bg = bg;
}
#ifdef ENABLE_SIXEL
#ifdef ENABLE_SIXEL_IMAGES
if (image_scroll_up(s, lines) && ctx->wp != NULL)
ctx->wp->flags |= PANE_REDRAW;
#endif
@@ -1576,7 +1549,7 @@ screen_write_scrolldown(struct screen_write_ctx *ctx, u_int lines, u_int bg)
else if (lines > s->rlower - s->rupper + 1)
lines = s->rlower - s->rupper + 1;
#ifdef ENABLE_SIXEL
#ifdef ENABLE_SIXEL_IMAGES
if (image_free_all(s) && ctx->wp != NULL)
ctx->wp->flags |= PANE_REDRAW;
#endif
@@ -1600,15 +1573,12 @@ screen_write_carriagereturn(struct screen_write_ctx *ctx)
void
screen_write_clearendofscreen(struct screen_write_ctx *ctx, u_int bg)
{
struct screen *s = ctx->s;
struct grid *gd = s->grid;
struct tty_ctx ttyctx;
u_int sx = screen_size_x(s), sy = screen_size_y(s);
u_int y, i, xoff, yoff, cx_save, cy_save;
struct visible_ranges *r;
struct visible_range *ri;
struct screen *s = ctx->s;
struct grid *gd = s->grid;
struct tty_ctx ttyctx;
u_int sx = screen_size_x(s), sy = screen_size_y(s);
#ifdef ENABLE_SIXEL
#ifdef ENABLE_SIXEL_IMAGES
if (image_check_line(s, s->cy, sy - s->cy) && ctx->wp != NULL)
ctx->wp->flags |= PANE_REDRAW;
#endif
@@ -1631,66 +1601,18 @@ screen_write_clearendofscreen(struct screen_write_ctx *ctx, u_int bg)
screen_write_collect_clear(ctx, s->cy + 1, sy - (s->cy + 1));
screen_write_collect_flush(ctx, 0, __func__);
if (! screen_write_pane_obscured(ctx->wp)) {
tty_write(tty_cmd_clearendofscreen, &ttyctx);
return;
}
/* Can't just clear screen, must avoid floating windows. */
cx_save = s->cx;
cy_save = s->cy;
if (ctx->wp != NULL) {
xoff = ctx->wp->xoff;
yoff = ctx->wp->yoff;
} else {
xoff = 0;
yoff = 0;
}
/* First line: visible ranges from the current cursor to end. */
if (s->cx <= sx - 1) {
r = screen_redraw_get_visible_ranges(ctx->wp,
xoff + s->cx, yoff + s->cy, sx - s->cx, NULL);
for (i = 0; i < r->used; i++) {
ri = &r->ranges[i];
if (ri->nx == 0)
continue;
screen_write_clearpartofline(ctx,
ri->px - xoff, ri->nx, bg);
}
}
/* Remaining lines: visible ranges across the full width. */
for (y = s->cy + 1; y < sy; y++) {
screen_write_set_cursor(ctx, 0, y);
r = screen_redraw_get_visible_ranges(ctx->wp,
xoff, yoff + y, sx, NULL);
for (i = 0; i < r->used; i++) {
ri = &r->ranges[i];
if (ri->nx == 0)
continue;
screen_write_clearpartofline(ctx,
ri->px - xoff, ri->nx, bg);
}
}
screen_write_collect_flush(ctx, 0, __func__);
screen_write_set_cursor(ctx, cx_save, cy_save);
tty_write(tty_cmd_clearendofscreen, &ttyctx);
}
/* Clear to start of screen. */
void
screen_write_clearstartofscreen(struct screen_write_ctx *ctx, u_int bg)
{
struct screen *s = ctx->s;
struct tty_ctx ttyctx;
u_int sx = screen_size_x(s);
u_int y, i, xoff, yoff, cx_save, cy_save;
struct visible_ranges *r;
struct visible_range *ri;
struct screen *s = ctx->s;
struct tty_ctx ttyctx;
u_int sx = screen_size_x(s);
#ifdef ENABLE_SIXEL
#ifdef ENABLE_SIXEL_IMAGES
if (image_check_line(s, 0, s->cy - 1) && ctx->wp != NULL)
ctx->wp->flags |= PANE_REDRAW;
#endif
@@ -1707,64 +1629,18 @@ screen_write_clearstartofscreen(struct screen_write_ctx *ctx, u_int bg)
screen_write_collect_clear(ctx, 0, s->cy);
screen_write_collect_flush(ctx, 0, __func__);
if (! screen_write_pane_obscured(ctx->wp)) {
tty_write(tty_cmd_clearstartofscreen, &ttyctx);
return;
}
/* Can't just clear screen, must avoid floating windows. */
cx_save = s->cx;
cy_save = s->cy;
if (ctx->wp != NULL) {
xoff = ctx->wp->xoff;
yoff = ctx->wp->yoff;
} else {
xoff = 0;
yoff = 0;
}
/* Lines 0 to cy-1: visible ranges across the full width. */
for (y = 0; y < s->cy; y++) {
screen_write_set_cursor(ctx, 0, y);
r = screen_redraw_get_visible_ranges(ctx->wp,
xoff, yoff + y, sx, NULL);
for (i = 0; i < r->used; i++) {
ri = &r->ranges[i];
if (ri->nx == 0)
continue;
screen_write_clearpartofline(ctx,
ri->px - xoff, ri->nx, bg);
}
}
/* Last line: visible ranges from 0 to cursor (inclusive). */
screen_write_set_cursor(ctx, 0, s->cy);
r = screen_redraw_get_visible_ranges(ctx->wp,
xoff, yoff + cy_save, s->cx + 1, NULL);
for (i = 0; i < r->used; i++) {
ri = &r->ranges[i];
if (ri->nx == 0)
continue;
screen_write_clearpartofline(ctx, ri->px - xoff, ri->nx, bg);
}
screen_write_collect_flush(ctx, 0, __func__);
screen_write_set_cursor(ctx, cx_save, cy_save);
tty_write(tty_cmd_clearstartofscreen, &ttyctx);
}
/* Clear entire screen. */
void
screen_write_clearscreen(struct screen_write_ctx *ctx, u_int bg)
{
struct screen *s = ctx->s;
struct tty_ctx ttyctx;
u_int sx = screen_size_x(s), sy = screen_size_y(s);
u_int y, i, xoff, yoff, cx_save, cy_save;
struct visible_ranges *r;
struct visible_range *ri;
struct screen *s = ctx->s;
struct tty_ctx ttyctx;
u_int sx = screen_size_x(s), sy = screen_size_y(s);
#ifdef ENABLE_SIXEL
#ifdef ENABLE_SIXEL_IMAGES
if (image_free_all(s) && ctx->wp != NULL)
ctx->wp->flags |= PANE_REDRAW;
#endif
@@ -1781,38 +1657,7 @@ screen_write_clearscreen(struct screen_write_ctx *ctx, u_int bg)
grid_view_clear(s->grid, 0, 0, sx, sy, bg);
screen_write_collect_clear(ctx, 0, sy);
if (! screen_write_pane_obscured(ctx->wp)) {
tty_write(tty_cmd_clearscreen, &ttyctx);
return;
}
/* Can't just clear screen, must avoid floating windows. */
cx_save = s->cx;
cy_save = s->cy;
if (ctx->wp != NULL) {
xoff = ctx->wp->xoff;
yoff = ctx->wp->yoff;
} else {
xoff = 0;
yoff = 0;
}
for (y = 0; y < sy; y++) {
screen_write_set_cursor(ctx, 0, y);
r = screen_redraw_get_visible_ranges(ctx->wp,
xoff, yoff + y, sx, NULL);
for (i = 0; i < r->used; i++) {
ri = &r->ranges[i];
if (ri->nx == 0)
continue;
screen_write_clearpartofline(ctx,
ri->px - xoff, ri->nx, bg);
}
}
screen_write_collect_flush(ctx, 0, __func__);
screen_write_set_cursor(ctx, cx_save, cy_save);
tty_write(tty_cmd_clearscreen, &ttyctx);
}
/* Clear entire history. */
@@ -1964,39 +1809,6 @@ screen_write_collect_scroll(struct screen_write_ctx *ctx, u_int bg)
TAILQ_INSERT_TAIL(&ctx->s->write_list[s->rlower].items, ci, entry);
}
/* Return 1 if there is a floating window pane overlapping this pane. */
static int
screen_write_pane_obscured(struct window_pane *base_wp)
{
struct window_pane *wp;
struct window *w;
int found_self = 0;
if (base_wp == NULL)
return(0);
w = base_wp->window;
/* Check if there is a floating pane. xxxx borders? scrollbars? */
TAILQ_FOREACH_REVERSE(wp, &w->z_index, window_panes_zindex, zentry) {
if (wp == base_wp) {
found_self = 1;
continue;
}
if (found_self && wp->flags & PANE_FLOATING &&
! (wp->flags & PANE_MINIMISED) &&
((wp->yoff >= base_wp->yoff &&
wp->yoff <= base_wp->yoff + (int)base_wp->sy) ||
(wp->yoff + (int)wp->sy >= base_wp->yoff &&
wp->yoff + wp->sy <= base_wp->yoff + base_wp->sy)) &&
((wp->xoff >= base_wp->xoff &&
wp->xoff <= base_wp->xoff + (int)base_wp->sx) ||
(wp->xoff + (int)wp->sx >= base_wp->xoff &&
wp->xoff + wp->sx <= base_wp->xoff + base_wp->sx)))
return (1);
}
return (0);
}
/* Flush collected lines. */
static void
screen_write_collect_flush(struct screen_write_ctx *ctx, int scroll_only,
@@ -2005,14 +1817,8 @@ screen_write_collect_flush(struct screen_write_ctx *ctx, int scroll_only,
struct screen *s = ctx->s;
struct screen_write_citem *ci, *tmp;
struct screen_write_cline *cl;
u_int y, cx, cy, last, items = 0, i;
u_int wr_start, wr_end, wr_length, wsx, wsy;
int r_start, r_end, ci_start, ci_end;
int xoff, yoff;
u_int y, cx, cy, last, items = 0;
struct tty_ctx ttyctx;
struct visible_ranges *r;
struct visible_range *ri;
struct window_pane *wp = ctx->wp;
if (s->mode & MODE_SYNC) {
for (y = 0; y < screen_size_y(s); y++) {
@@ -2032,14 +1838,11 @@ screen_write_collect_flush(struct screen_write_ctx *ctx, int scroll_only,
ctx->scrolled = s->rlower - s->rupper + 1;
screen_write_initctx(ctx, &ttyctx, 1);
if (wp != NULL && wp->yoff + wp->sy > wp->window->sy)
ttyctx.orlower -= (wp->yoff + wp->sy - wp->window->sy);
ttyctx.num = ctx->scrolled;
ttyctx.bg = ctx->bg;
ttyctx.obscured = screen_write_pane_obscured(wp);
tty_write(tty_cmd_scrollup, &ttyctx);
if (wp != NULL)
if (ctx->wp != NULL)
ctx->wp->flags |= PANE_REDRAWSCROLLBAR;
}
ctx->scrolled = 0;
@@ -2049,29 +1852,8 @@ screen_write_collect_flush(struct screen_write_ctx *ctx, int scroll_only,
return;
cx = s->cx; cy = s->cy;
/* The xoff and width of window pane relative to the window we
* are writing to relative to the visible_ranges array. */
if (wp != NULL) {
wsx = wp->window->sx;
wsy = wp->window->sy;
xoff = wp->xoff;
yoff = wp->yoff;
} else {
wsx = screen_size_x(s);
wsy = screen_size_y(s);
xoff = 0;
yoff = 0;
}
for (y = 0; y < screen_size_y(s); y++) {
if (y + yoff >= wsy)
continue;
cl = &ctx->s->write_list[y];
r = screen_redraw_get_visible_ranges(wp, 0, y + yoff, wsx,
NULL);
last = UINT_MAX;
TAILQ_FOREACH_SAFE(ci, &cl->items, entry, tmp) {
log_debug("collect list: x=%u (last %u), y=%u, used=%u",
@@ -2080,57 +1862,27 @@ screen_write_collect_flush(struct screen_write_ctx *ctx, int scroll_only,
fatalx("collect list not in order: %u <= %u",
ci->x, last);
}
wr_length = 0;
for (i = 0; i < r->used; i++) {
ri = &r->ranges[i];
if (ri->nx == 0) continue;
r_start = ri->px;
r_end = ri->px + ri->nx;
ci_start = ci->x;
ci_end = ci->x + ci->used;
if (ci_start + xoff > r_end ||
ci_end + xoff < r_start)
continue;
if (r_start > ci_start + xoff)
wr_start = ci_start +
(r_start - (ci_start + xoff));
else
wr_start = ci_start;
if (ci_end + xoff > r_end)
wr_end = ci_end -
((ci_end + xoff) - r_end);
else
wr_end = ci_end;
wr_length = wr_end - wr_start;
if (wr_length <= 0)
continue;
screen_write_set_cursor(ctx, wr_start, y);
if (ci->type == CLEAR) {
screen_write_initctx(ctx, &ttyctx, 1);
ttyctx.bg = ci->bg;
ttyctx.num = wr_length;
tty_write(tty_cmd_clearcharacter,
&ttyctx);
} else {
screen_write_initctx(ctx, &ttyctx, 0);
ttyctx.cell = &ci->gc;
ttyctx.wrapped = ci->wrapped;
ttyctx.ptr = cl->data + wr_start;
ttyctx.num = wr_length;
tty_write(tty_cmd_cells, &ttyctx);
}
items++;
TAILQ_REMOVE(&cl->items, ci, entry);
screen_write_free_citem(ci);
last = ci->x;
screen_write_set_cursor(ctx, ci->x, y);
if (ci->type == CLEAR) {
screen_write_initctx(ctx, &ttyctx, 1);
ttyctx.bg = ci->bg;
ttyctx.num = ci->used;
tty_write(tty_cmd_clearcharacter, &ttyctx);
} else {
screen_write_initctx(ctx, &ttyctx, 0);
ttyctx.cell = &ci->gc;
ttyctx.wrapped = ci->wrapped;
ttyctx.ptr = cl->data + ci->x;
ttyctx.num = ci->used;
tty_write(tty_cmd_cells, &ttyctx);
}
items++;
TAILQ_REMOVE(&cl->items, ci, entry);
screen_write_free_citem(ci);
last = ci->x;
}
}
s->cx = cx; s->cy = cy;
log_debug("%s: flushed %u items (%s)", __func__, items, from);
@@ -2205,7 +1957,7 @@ screen_write_collect_end(struct screen_write_ctx *ctx)
}
}
#ifdef ENABLE_SIXEL
#ifdef ENABLE_SIXEL_IMAGES
if (image_check_area(s, s->cx, s->cy, ci->used, 1) && ctx->wp != NULL)
ctx->wp->flags |= PANE_REDRAW;
#endif
@@ -2295,7 +2047,6 @@ void
screen_write_cell(struct screen_write_ctx *ctx, const struct grid_cell *gc)
{
struct screen *s = ctx->s;
struct window_pane *wp = ctx->wp;
struct grid *gd = s->grid;
const struct utf8_data *ud = &gc->data;
struct grid_line *gl;
@@ -2303,10 +2054,8 @@ screen_write_cell(struct screen_write_ctx *ctx, const struct grid_cell *gc)
struct grid_cell tmp_gc, now_gc;
struct tty_ctx ttyctx;
u_int sx = screen_size_x(s), sy = screen_size_y(s);
u_int width = ud->width, xx, not_wrap, i, n, vis;
int selected, skip = 1, redraw = 0, yoff = 0;
struct visible_ranges *r;
struct visible_range *ri;
u_int width = ud->width, xx, not_wrap;
int selected, skip = 1, redraw = 0;
/* Ignore padding cells. */
if (gc->flags & GRID_FLAG_PADDING)
@@ -2404,11 +2153,6 @@ screen_write_cell(struct screen_write_ctx *ctx, const struct grid_cell *gc)
if (selected)
skip = 0;
if (wp != NULL)
yoff = wp->yoff;
r = screen_redraw_get_visible_ranges(wp, s->cx, s->cy + yoff, width,
NULL);
/*
* Move the cursor. If not wrapping, stick at the last character and
* replace it.
@@ -2430,30 +2174,11 @@ screen_write_cell(struct screen_write_ctx *ctx, const struct grid_cell *gc)
if (!skip && !(s->mode & MODE_SYNC)) {
if (selected) {
screen_select_cell(s, &tmp_gc, gc);
ttyctx.cell = &tmp_gc;
} else
memcpy(&tmp_gc, gc, sizeof tmp_gc);
ttyctx.cell = &tmp_gc;
ttyctx.cell = gc;
ttyctx.num = redraw ? 2 : 0;
for (i=0, vis=0; i < r->used; i++) vis += r->ranges[i].nx;
if (vis < width) {
/* Wide character or tab partly obscured. Write
* spaces one by one in unobscured region(s).
*/
*tmp_gc.data.data = ' ';
tmp_gc.data.width = tmp_gc.data.size =
tmp_gc.data.have = 1;
for (i=0; i < r->used; i++) {
ri = &r->ranges[i];
if (ri->nx == 0) continue;
for (n = 0; n < ri->nx; n++) {
screen_write_set_cursor(ctx, ri->px + n,
-1);
tty_write(tty_cmd_cell, &ttyctx);
}
}
} else {
tty_write(tty_cmd_cell, &ttyctx);
}
tty_write(tty_cmd_cell, &ttyctx);
}
}
@@ -2462,14 +2187,12 @@ static int
screen_write_combine(struct screen_write_ctx *ctx, const struct grid_cell *gc)
{
struct screen *s = ctx->s;
struct window_pane *wp = ctx->wp;
struct grid *gd = s->grid;
const struct utf8_data *ud = &gc->data;
u_int i, n, cx = s->cx, cy = s->cy, vis, yoff = 0;
u_int n, cx = s->cx, cy = s->cy;
struct grid_cell last;
struct tty_ctx ttyctx;
int force_wide = 0, zero_width = 0;
struct visible_ranges *r;
/* Ignore U+3164 HANGUL_FILLER entirely. */
if (utf8_is_hangul_filler(ud))
@@ -2557,23 +2280,6 @@ screen_write_combine(struct screen_write_ctx *ctx, const struct grid_cell *gc)
if (force_wide)
grid_view_set_padding(gd, cx - 1, cy);
/*
* 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 empty range in the visible ranges so we add them all up.
*/
if (wp != NULL)
yoff = wp->yoff;
r = screen_redraw_get_visible_ranges(wp, cx - n, cy + yoff, n, NULL);
for (i=0, vis=0; i < r->used; i++) vis += r->ranges[i].nx;
if (vis < n) {
/*
* Part of this character is obscured. Return 1
* and let screen_write_cell write a space.
*/
return (1);
}
/*
* Redraw the combined cell. If forcing the cell to width 2, reset the
* cached cursor position in the tty, since we don't really know
@@ -2692,7 +2398,7 @@ screen_write_rawstring(struct screen_write_ctx *ctx, u_char *str, u_int len,
tty_write(tty_cmd_rawstring, &ttyctx);
}
#ifdef ENABLE_SIXEL
#ifdef ENABLE_SIXEL_IMAGES
/* Write a SIXEL image. */
void
screen_write_sixelimage(struct screen_write_ctx *ctx, struct sixel_image *si,
@@ -2744,7 +2450,7 @@ screen_write_sixelimage(struct screen_write_ctx *ctx, struct sixel_image *si,
screen_write_collect_flush(ctx, 0, __func__);
screen_write_initctx(ctx, &ttyctx, 0);
ttyctx.ptr = image_store(s, si);
ttyctx.ptr = image_store(s, IMAGE_SIXEL, si);
tty_write(tty_cmd_sixelimage, &ttyctx);
@@ -2752,6 +2458,38 @@ screen_write_sixelimage(struct screen_write_ctx *ctx, struct sixel_image *si,
}
#endif
#ifdef ENABLE_KITTY_IMAGES
void
screen_write_kittyimage(struct screen_write_ctx *ctx, struct kitty_image *ki)
{
struct screen *s = ctx->s;
struct tty_ctx ttyctx;
struct image *im;
if (ki == NULL)
return;
/* Store the image in the cache. */
im = image_store(s, IMAGE_KITTY, ki);
/* Trigger a tty write to send to all terminals. */
if (im != NULL && ctx->wp != NULL) {
screen_write_collect_flush(ctx, 0, __func__);
screen_write_initctx(ctx, &ttyctx, 0);
ttyctx.ptr = im;
ttyctx.arg = ctx->wp;
ttyctx.ocx = s->cx;
ttyctx.ocy = s->cy;
ttyctx.set_client_cb = tty_set_client_cb;
tty_write(tty_cmd_kittyimage, &ttyctx);
}
/* Move cursor past the image. */
if (kitty_get_rows(ki) > 0)
screen_write_cursormove(ctx, 0, s->cy + kitty_get_rows(ki), 0);
}
#endif
/* Turn alternate screen on. */
void
screen_write_alternateon(struct screen_write_ctx *ctx, struct grid_cell *gc,

View File

@@ -89,7 +89,7 @@ screen_init(struct screen *s, u_int sx, u_int sy, u_int hlimit)
s->tabs = NULL;
s->sel = NULL;
#ifdef ENABLE_SIXEL
#ifdef ENABLE_IMAGES
TAILQ_INIT(&s->images);
TAILQ_INIT(&s->saved_images);
#endif
@@ -127,7 +127,7 @@ screen_reinit(struct screen *s)
screen_clear_selection(s);
screen_free_titles(s);
#ifdef ENABLE_SIXEL
#ifdef ENABLE_IMAGES
image_free_all(s);
#endif
@@ -164,7 +164,7 @@ screen_free(struct screen *s)
hyperlinks_free(s->hyperlinks);
screen_free_titles(s);
#ifdef ENABLE_SIXEL
#ifdef ENABLE_IMAGES
image_free_all(s);
#endif
}
@@ -324,7 +324,7 @@ screen_resize_cursor(struct screen *s, u_int sx, u_int sy, int reflow,
if (sy != screen_size_y(s))
screen_resize_y(s, sy, eat_empty, &cy);
#ifdef ENABLE_SIXEL
#ifdef ENABLE_IMAGES
image_free_all(s);
#endif
@@ -649,7 +649,7 @@ screen_alternate_on(struct screen *s, struct grid_cell *gc, int cursor)
}
memcpy(&s->saved_cell, gc, sizeof s->saved_cell);
#ifdef ENABLE_SIXEL
#ifdef ENABLE_IMAGES
TAILQ_CONCAT(&s->saved_images, &s->images, entry);
#endif
@@ -707,7 +707,7 @@ screen_alternate_off(struct screen *s, struct grid_cell *gc, int cursor)
grid_destroy(s->saved_grid);
s->saved_grid = NULL;
#ifdef ENABLE_SIXEL
#ifdef ENABLE_IMAGES
image_free_all(s);
TAILQ_CONCAT(&s->images, &s->saved_images, entry);
#endif

View File

@@ -617,8 +617,7 @@ server_client_check_mouse_in_pane(struct window_pane *wp, u_int px, u_int py,
struct options *wo = w->options;
struct window_pane *fwp;
int pane_status, sb, sb_pos, sb_w, sb_pad;
u_int pane_status_line, sl_top, sl_bottom;
u_int bdr_bottom, bdr_top, bdr_left, bdr_right;
u_int line, sl_top, sl_bottom;
sb = options_get_number(wo, "pane-scrollbars");
sb_pos = options_get_number(wo, "pane-scrollbars-position");
@@ -631,19 +630,16 @@ server_client_check_mouse_in_pane(struct window_pane *wp, u_int px, u_int py,
sb_w = 0;
sb_pad = 0;
}
if (pane_status == PANE_STATUS_TOP)
pane_status_line = wp->yoff - 1;
line = wp->yoff - 1;
else if (pane_status == PANE_STATUS_BOTTOM)
pane_status_line = wp->yoff + wp->sy;
else
pane_status_line = -1; /* not used */
line = wp->yoff + wp->sy;
/* Check if point is within the pane or scrollbar. */
if (((pane_status != PANE_STATUS_OFF &&
py != pane_status_line && py != wp->yoff + wp->sy) ||
py != line && py != wp->yoff + wp->sy) ||
(wp->yoff == 0 && py < wp->sy) ||
((int)py >= wp->yoff && py < wp->yoff + wp->sy)) &&
(py >= wp->yoff && py < wp->yoff + wp->sy)) &&
((sb_pos == PANE_SCROLLBARS_RIGHT &&
px < wp->xoff + wp->sx + sb_pad + sb_w) ||
(sb_pos == PANE_SCROLLBARS_LEFT &&
@@ -653,8 +649,8 @@ server_client_check_mouse_in_pane(struct window_pane *wp, u_int px, u_int py,
(px >= wp->xoff + wp->sx + sb_pad &&
px < wp->xoff + wp->sx + sb_pad + sb_w)) ||
(sb_pos == PANE_SCROLLBARS_LEFT &&
((int)px >= wp->xoff - sb_pad - sb_w &&
(int)px < wp->xoff - sb_pad))) {
(px >= wp->xoff - sb_pad - sb_w &&
px < wp->xoff - sb_pad))) {
/* Check where inside the scrollbar. */
sl_top = wp->yoff + wp->sb_slider_y;
sl_bottom = (wp->yoff + wp->sb_slider_y +
@@ -667,12 +663,6 @@ server_client_check_mouse_in_pane(struct window_pane *wp, u_int px, u_int py,
return (SCROLLBAR_SLIDER);
} else /* py > sl_bottom */
return (SCROLLBAR_DOWN);
} else if (wp->flags & PANE_FLOATING &&
((int)px == wp->xoff - 1 ||
(int)py == wp->yoff - 1 ||
(int)py == wp->yoff + (int)wp->sy)) {
/* Floating pane left, bottom or top border. */
return (BORDER);
} else {
/* Must be inside the pane. */
return (PANE);
@@ -680,32 +670,16 @@ server_client_check_mouse_in_pane(struct window_pane *wp, u_int px, u_int py,
} else if (~w->flags & WINDOW_ZOOMED) {
/* Try the pane borders if not zoomed. */
TAILQ_FOREACH(fwp, &w->panes, entry) {
if (sb_pos == PANE_SCROLLBARS_LEFT)
bdr_right = fwp->xoff + fwp->sx;
else
/* PANE_SCROLLBARS_RIGHT or none. */
bdr_right = fwp->xoff + fwp->sx + sb_pad + sb_w;
if ((int)py >= fwp->yoff - 1 && py <= fwp->yoff + fwp->sy) {
if (px == bdr_right)
break;
if (wp->flags & PANE_FLOATING) {
/* Floating pane, check if left border. */
bdr_left = fwp->xoff - 1;
if (px == bdr_left)
break;
}
}
if ((int)px >= fwp->xoff - 1 && px <= fwp->xoff + fwp->sx) {
bdr_bottom = fwp->yoff + fwp->sy;
if (py == bdr_bottom)
break;
if (wp->flags & PANE_FLOATING) {
/* Floating pane, check if top border. */
bdr_top = fwp->yoff - 1;
if (py == bdr_top)
break;
}
}
if ((((sb_pos == PANE_SCROLLBARS_RIGHT &&
fwp->xoff + fwp->sx + sb_pad + sb_w == px) ||
(sb_pos == PANE_SCROLLBARS_LEFT &&
fwp->xoff + fwp->sx == px)) &&
fwp->yoff <= 1 + py &&
fwp->yoff + fwp->sy >= py) ||
(fwp->yoff + fwp->sy == py &&
fwp->xoff <= 1 + px &&
fwp->xoff + fwp->sx >= px))
break;
}
if (fwp != NULL)
return (BORDER);
@@ -875,11 +849,9 @@ have_event:
* scrollbar.
*/
if (where == NOWHERE) {
if (c->tty.mouse_scrolling_flag) {
if (c->tty.mouse_scrolling_flag)
where = SCROLLBAR_SLIDER;
m->wp = c->tty.mouse_wp->id;
m->w = c->tty.mouse_wp->window->id;
} else {
else {
px = x;
if (m->statusat == 0 && y >= m->statuslines)
py = y - m->statuslines;
@@ -896,13 +868,8 @@ have_event:
px = px + m->ox;
py = py + m->oy;
if (type == DRAG &&
c->tty.mouse_wp != NULL)
/* Use pane from last mouse event. */
wp = c->tty.mouse_wp;
else
/* Try inside the pane. */
wp = window_get_active_at(w, px, py);
/* Try inside the pane. */
wp = window_get_active_at(w, px, py);
if (wp == NULL)
return (KEYC_UNKNOWN);
where = server_client_check_mouse_in_pane(wp, px, py,
@@ -1119,7 +1086,6 @@ have_event:
break;
}
c->tty.mouse_drag_flag = 0;
c->tty.mouse_wp = NULL;
c->tty.mouse_slider_mpos = -1;
goto out;
}
@@ -1135,7 +1101,6 @@ have_event:
if (wp != NULL &&
wp != w->active &&
options_get_number(s->options, "focus-follows-mouse")) {
window_redraw_active_switch(w, wp);
window_set_active_pane(w, wp, 1);
server_redraw_window_borders(w);
server_status_window(w);
@@ -1347,11 +1312,6 @@ have_event:
* where the user grabbed.
*/
c->tty.mouse_drag_flag = MOUSE_BUTTONS(b) + 1;
/* Only change pane if not already dragging a pane border. */
if (c->tty.mouse_wp == NULL) {
wp = window_get_active_at(w, px, py);
c->tty.mouse_wp = wp;
}
if (c->tty.mouse_scrolling_flag == 0 &&
where == SCROLLBAR_SLIDER) {
c->tty.mouse_scrolling_flag = 1;
@@ -3020,7 +2980,7 @@ server_client_reset_state(struct client *c)
if (c->overlay_draw != NULL) {
if (c->overlay_mode != NULL)
s = c->overlay_mode(c, c->overlay_data, &cx, &cy);
} else if (wp != NULL && c->prompt_string == NULL)
} else if (c->prompt_string == NULL)
s = wp->screen;
else
s = c->status.active;
@@ -3048,7 +3008,7 @@ server_client_reset_state(struct client *c)
cy = tty->sy - 1;
}
cx = c->prompt_cursor;
} else if (wp != NULL && c->overlay_draw == NULL) {
} else if (c->overlay_draw == NULL) {
cursor = 0;
tty_window_offset(tty, &ox, &oy, &sx, &sy);
if (wp->xoff + s->cx >= ox && wp->xoff + s->cx <= ox + sx &&
@@ -3061,15 +3021,9 @@ server_client_reset_state(struct client *c)
if (status_at_line(c) == 0)
cy += status_line_size(c);
}
if (!screen_redraw_is_visible(
screen_redraw_get_visible_ranges(wp, cx, cy, 1, NULL), cx))
cursor = 0;
if (!cursor)
mode &= ~MODE_CURSOR;
} else
mode &= ~MODE_CURSOR;
}
log_debug("%s: cursor to %u,%u", __func__, cx, cy);
tty_cursor(tty, cx, cy);
@@ -3754,7 +3708,7 @@ server_client_dispatch_identify(struct client *c, struct imsg *imsg)
c->term_name = xstrdup("unknown");
}
if (c->ttyname != NULL && *c->ttyname != '\0')
if (c->ttyname == NULL || *c->ttyname != '\0')
name = xstrdup(c->ttyname);
else
xasprintf(&name, "client-%ld", (long)c->pid);

View File

@@ -276,13 +276,6 @@ spawn_pane(struct spawn_context *sc, char **cause)
layout_assign_pane(sc->lc, new_wp, 0);
}
/*
* If window currently zoomed, window_set_active_pane calls
* window_unzoom which it copies back the saved_layout_cell.
*/
if (w->flags & WINDOW_ZOOMED)
new_wp->saved_layout_cell = new_wp->layout_cell;
/*
* Now we have a pane with nothing running in it ready for the new
* process. Work out the command and arguments and store the working

3
tmux.1
View File

@@ -6266,6 +6266,7 @@ The following variables are available, where appropriate:
.It Li "hook_window_name" Ta "" Ta "Name of window where hook was run, if any"
.It Li "host" Ta "#H" Ta "Hostname of local host"
.It Li "host_short" Ta "#h" Ta "Hostname of local host (no domain name)"
.It Li "image_support" Ta "" Ta "Returns the string of the support image (sixel, kitty)"
.It Li "insert_flag" Ta "" Ta "Pane insert flag"
.It Li "key_string" Ta "" Ta "String representation of the key binding"
.It Li "key_repeat" Ta "" Ta "1 if key binding is repeatable"
@@ -6310,7 +6311,6 @@ The following variables are available, where appropriate:
.It Li "pane_dead_status" Ta "" Ta "Exit status of process in dead pane"
.It Li "pane_dead_time" Ta "" Ta "Exit time of process in dead pane"
.It Li "pane_fg" Ta "" Ta "Pane foreground colour"
.It Li "pane_floating_flag" Ta "" Ta "1 if pane is floating"
.It Li "pane_format" Ta "" Ta "1 if format is for a pane"
.It Li "pane_height" Ta "" Ta "Height of pane"
.It Li "pane_id" Ta "#D" Ta "Unique pane ID"
@@ -6381,7 +6381,6 @@ The following variables are available, where appropriate:
.It Li "session_stack" Ta "" Ta "Window indexes in most recent order"
.It Li "session_windows" Ta "" Ta "Number of windows in session"
.It Li "socket_path" Ta "" Ta "Server socket path"
.It Li "sixel_support" Ta "" Ta "1 if server has support for SIXEL"
.It Li "start_time" Ta "" Ta "Server start time"
.It Li "synchronized_output_flag" Ta "" Ta "1 if pane has synchronized output enabled"
.It Li "uid" Ta "" Ta "Server UID"

144
tmux.h
View File

@@ -68,10 +68,19 @@ struct screen_write_cline;
struct screen_write_ctx;
struct session;
#ifdef ENABLE_SIXEL
/* Convenience macro: defined if any image protocol is compiled in. */
#if defined(ENABLE_SIXEL_IMAGES) || defined(ENABLE_KITTY_IMAGES)
#define ENABLE_IMAGES
#endif
#ifdef ENABLE_SIXEL_IMAGES
struct sixel_image;
#endif
#ifdef ENABLE_KITTY_IMAGES
struct kitty_image;
#endif
struct tty_ctx;
struct tty_code;
struct tty_key;
@@ -925,11 +934,25 @@ struct style {
enum style_default_type default_type;
};
#ifdef ENABLE_SIXEL
#ifdef ENABLE_IMAGES
/* Image types. */
enum image_type {
IMAGE_SIXEL,
IMAGE_KITTY
};
/* Image. */
struct image {
enum image_type type;
struct screen *s;
struct sixel_image *data;
union {
#ifdef ENABLE_SIXEL_IMAGES
struct sixel_image *sixel;
#endif
#ifdef ENABLE_KITTY_IMAGES
struct kitty_image *kitty;
#endif
} data;
char *fallback;
u_int px;
@@ -984,7 +1007,7 @@ struct screen {
bitstr_t *tabs;
struct screen_sel *sel;
#ifdef ENABLE_SIXEL
#ifdef ENABLE_IMAGES
struct images images;
struct images saved_images;
#endif
@@ -1063,8 +1086,8 @@ struct screen_redraw_ctx {
u_int sx;
u_int sy;
int ox;
int oy;
u_int ox;
u_int oy;
};
/* Screen size. */
@@ -1179,19 +1202,6 @@ enum client_theme {
THEME_DARK
};
/* Visible range array element. */
struct visible_range {
u_int px; /* start */
u_int nx; /* length */
};
/* Visible areas not obstructed. */
struct visible_ranges {
struct visible_range *ranges; /* dynamically allocated array */
u_int used; /* number of entries in ranges */
u_int size; /* allocated capacity of ranges */
};
/* Child window structure. */
struct window_pane {
u_int id;
@@ -1206,11 +1216,10 @@ struct window_pane {
u_int sx;
u_int sy;
int xoff;
int yoff;
u_int xoff;
u_int yoff;
int flags;
int saved_flags;
#define PANE_REDRAW 0x1
#define PANE_DROP 0x2
#define PANE_FOCUSED 0x4
@@ -1227,8 +1236,6 @@ struct window_pane {
#define PANE_THEMECHANGED 0x2000
#define PANE_UNSEENCHANGES 0x4000
#define PANE_REDRAWSCROLLBAR 0x8000
#define PANE_FLOATING 0x10000
#define PANE_MINIMISED 0x20000
u_int sb_slider_y;
u_int sb_slider_h;
@@ -1284,15 +1291,11 @@ struct window_pane {
struct style scrollbar_style;
struct visible_ranges r;
TAILQ_ENTRY(window_pane) entry; /* link in list of all panes */
TAILQ_ENTRY(window_pane) sentry; /* link in list of last visited */
TAILQ_ENTRY(window_pane) zentry; /* z-index link in list of all panes */
RB_ENTRY(window_pane) tree_entry;
};
TAILQ_HEAD(window_panes, window_pane);
TAILQ_HEAD(window_panes_zindex, window_pane);
RB_HEAD(window_pane_tree, window_pane);
/* Window structure. */
@@ -1312,7 +1315,6 @@ struct window {
struct window_pane *active;
struct window_panes last_panes;
struct window_panes z_index;
struct window_panes panes;
int lastlayout;
@@ -1406,7 +1408,6 @@ TAILQ_HEAD(winlink_stack, winlink);
enum layout_type {
LAYOUT_LEFTRIGHT,
LAYOUT_TOPBOTTOM,
LAYOUT_FLOATING,
LAYOUT_WINDOWPANE
};
@@ -1558,6 +1559,19 @@ struct key_event {
size_t len;
};
/* Visible range array element. */
struct visible_range {
u_int px; /* start */
u_int nx; /* length */
};
/* Visible areas not obstructed. */
struct visible_ranges {
struct visible_range *ranges; /* dynamically allocated array */
u_int used; /* number of entries in ranges */
u_int size; /* allocated capacity of ranges */
};
/* Terminal definition. */
struct tty_term {
char *name;
@@ -1575,6 +1589,7 @@ struct tty_term {
#define TERM_RGBCOLOURS 0x10
#define TERM_VT100LIKE 0x20
#define TERM_SIXEL 0x40
#define TERM_KITTY 0x80
int flags;
LIST_ENTRY(tty_term) entry;
@@ -1657,7 +1672,7 @@ struct tty {
int mouse_drag_flag;
int mouse_scrolling_flag;
int mouse_slider_mpos;
struct window_pane *mouse_wp;
void (*mouse_drag_update)(struct client *,
struct mouse_event *);
void (*mouse_drag_release)(struct client *,
@@ -1703,10 +1718,10 @@ struct tty_ctx {
u_int orlower;
/* Target region (usually pane) offset and size. */
int xoff;
int yoff;
int rxoff;
int ryoff;
u_int xoff;
u_int yoff;
u_int rxoff;
u_int ryoff;
u_int sx;
u_int sy;
@@ -1723,9 +1738,6 @@ struct tty_ctx {
u_int woy;
u_int wsx;
u_int wsy;
/* tty partly obscured, it will need to be surgically scrolled. */
u_int obscured;
};
/* Saved message entry. */
@@ -2277,7 +2289,6 @@ struct spawn_context {
#define SPAWN_FULLSIZE 0x20
#define SPAWN_EMPTY 0x40
#define SPAWN_ZOOM 0x80
#define SPAWN_FLOATING 0x100
};
/* Paste buffer. */
@@ -2633,7 +2644,7 @@ struct visible_ranges *tty_check_overlay_range(struct tty *, u_int, u_int,
void tty_draw_line(struct tty *, struct screen *, u_int, u_int, u_int,
u_int, u_int, const struct grid_cell *, struct colour_palette *);
#ifdef ENABLE_SIXEL
#ifdef ENABLE_IMAGES
void tty_draw_images(struct client *, struct window_pane *, struct screen *);
#endif
@@ -2667,10 +2678,22 @@ void tty_cmd_reverseindex(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 *);
#ifdef ENABLE_SIXEL
#ifdef ENABLE_IMAGES
int tty_set_client_cb(struct tty_ctx *, struct client *);
#endif
#ifdef ENABLE_SIXEL_IMAGES
void tty_cmd_sixelimage(struct tty *, const struct tty_ctx *);
#endif
#ifdef ENABLE_KITTY_IMAGES
void tty_cmd_kittyimage(struct tty *, const struct tty_ctx *);
void tty_kitty_delete_all(struct tty *);
void tty_kitty_delete_all_pane(struct window_pane *);
void tty_kitty_passthrough(struct window_pane *, const char *, size_t,
u_int, u_int);
#endif
void tty_cmd_syncstart(struct tty *, const struct tty_ctx *);
void tty_default_colours(struct grid_cell *, struct window_pane *);
@@ -3273,10 +3296,15 @@ void screen_write_setselection(struct screen_write_ctx *, const char *,
u_char *, u_int);
void screen_write_rawstring(struct screen_write_ctx *, u_char *, u_int,
int);
#ifdef ENABLE_SIXEL
#ifdef ENABLE_SIXEL_IMAGES
void screen_write_sixelimage(struct screen_write_ctx *,
struct sixel_image *, u_int);
#endif
#ifdef ENABLE_KITTY_IMAGES
void screen_write_kittyimage(struct screen_write_ctx *,
struct kitty_image *);
#endif
void screen_write_alternateon(struct screen_write_ctx *,
struct grid_cell *, int);
void screen_write_alternateoff(struct screen_write_ctx *,
@@ -3285,10 +3313,6 @@ void screen_write_alternateoff(struct screen_write_ctx *,
/* screen-redraw.c */
void screen_redraw_screen(struct client *);
void screen_redraw_pane(struct client *, struct window_pane *, int);
int screen_redraw_is_visible(struct visible_ranges *, u_int px);
struct visible_ranges *screen_redraw_get_visible_ranges(struct window_pane *,
u_int, u_int, u_int, struct visible_ranges *);
/* screen.c */
void screen_init(struct screen *, u_int, u_int, u_int);
@@ -3351,8 +3375,6 @@ struct window_pane *window_find_string(struct window *, const char *);
int window_has_pane(struct window *, struct window_pane *);
int window_set_active_pane(struct window *, struct window_pane *,
int);
int window_deactivate_pane(struct window *, struct window_pane *,
int);
void window_update_focus(struct window *);
void window_pane_update_focus(struct window_pane *);
void window_redraw_active_switch(struct window *,
@@ -3379,7 +3401,6 @@ struct window_pane *window_pane_find_by_id_str(const char *);
struct window_pane *window_pane_find_by_id(u_int);
int window_pane_destroy_ready(struct window_pane *);
void window_pane_resize(struct window_pane *, u_int, u_int);
void window_pane_move(struct window_pane *, int, int);
int window_pane_set_mode(struct window_pane *,
struct window_pane *, const struct window_mode *,
struct cmd_find_state *, struct args *);
@@ -3433,8 +3454,6 @@ void layout_free_cell(struct layout_cell *);
void layout_print_cell(struct layout_cell *, const char *, u_int);
void layout_destroy_cell(struct window *, struct layout_cell *,
struct layout_cell **);
void layout_minimise_cell(struct window *, struct layout_cell *);
void layout_unminimise_cell(struct window *, struct layout_cell *);
void layout_resize_layout(struct window *, struct layout_cell *,
enum layout_type, int, int);
struct layout_cell *layout_search_by_border(struct layout_cell *, u_int, u_int);
@@ -3442,7 +3461,6 @@ void layout_set_size(struct layout_cell *, u_int, u_int, u_int,
u_int);
void layout_make_leaf(struct layout_cell *, struct window_pane *);
void layout_make_node(struct layout_cell *, enum layout_type);
void layout_fix_zindexes(struct window *, struct layout_cell *);
void layout_fix_offsets(struct window *);
void layout_fix_panes(struct window *, struct window_pane *);
void layout_resize_adjust(struct window *, struct layout_cell *,
@@ -3463,7 +3481,7 @@ int layout_spread_cell(struct window *, struct layout_cell *);
void layout_spread_out(struct window_pane *);
/* layout-custom.c */
char *layout_dump(struct window *, struct layout_cell *);
char *layout_dump(struct layout_cell *);
int layout_parse(struct window *, const char *, char **);
/* layout-set.c */
@@ -3537,7 +3555,7 @@ void printflike(3, 4) window_copy_add(struct window_pane *, int, const char *,
...);
void printflike(3, 0) window_copy_vadd(struct window_pane *, int, const char *,
va_list);
void window_copy_scroll(struct window_pane *, int, u_int, u_int, int);
void window_copy_scroll(struct window_pane *, int, u_int, int);
void window_copy_pageup(struct window_pane *, int);
void window_copy_pagedown(struct window_pane *, int, int);
void window_copy_start_drag(struct client *, struct mouse_event *);
@@ -3750,14 +3768,16 @@ struct window_pane *spawn_pane(struct spawn_context *, char **);
/* regsub.c */
char *regsub(const char *, const char *, const char *, int);
#ifdef ENABLE_SIXEL
#ifdef ENABLE_IMAGES
/* image.c */
int image_free_all(struct screen *);
struct image *image_store(struct screen *, struct sixel_image *);
struct image *image_store(struct screen *, enum image_type, void *);
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);
#endif
#ifdef ENABLE_SIXEL_IMAGES
/* image-sixel.c */
#define SIXEL_COLOUR_REGISTERS 1024
struct sixel_image *sixel_parse(const char *, size_t, u_int, u_int, u_int);
@@ -3771,6 +3791,18 @@ char *sixel_print(struct sixel_image *, struct sixel_image *,
struct screen *sixel_to_screen(struct sixel_image *);
#endif
#ifdef ENABLE_KITTY_IMAGES
/* image-kitty.c */
struct kitty_image *kitty_parse(const u_char *, size_t, u_int, u_int);
void kitty_free(struct kitty_image *);
void kitty_size_in_cells(struct kitty_image *, u_int *, u_int *);
char kitty_get_action(struct kitty_image *);
u_int kitty_get_image_id(struct kitty_image *);
u_int kitty_get_rows(struct kitty_image *);
char *kitty_print(struct kitty_image *, size_t *);
char *kitty_delete_all(size_t *);
#endif
/* server-acl.c */
void server_acl_init(void);
struct server_acl_user *server_acl_user_find(uid_t);

BIN
tools/image.kitty Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 172 KiB

View File

@@ -122,7 +122,7 @@ tty_draw_line(struct tty *tty, struct screen *s, u_int px, u_int py, u_int nx,
char buf[1000];
size_t len;
enum tty_draw_line_state current_state, next_state;
/* xxx maybe check overlay here? */
/*
* py is the line in the screen to draw. px is the start x and nx is
* the width to draw. atx,aty is the line on the terminal to draw it.

View File

@@ -357,6 +357,17 @@ static const struct tty_feature tty_feature_sixel = {
TERM_SIXEL
};
/* Terminal has kitty graphics protocol capability. */
static const char *const tty_feature_kitty_capabilities[] = {
"Kty",
NULL
};
static const struct tty_feature tty_feature_kitty = {
"kitty",
tty_feature_kitty_capabilities,
TERM_KITTY
};
/* Available terminal features. */
static const struct tty_feature *const tty_features[] = {
&tty_feature_256,
@@ -368,6 +379,7 @@ static const struct tty_feature *const tty_features[] = {
&tty_feature_extkeys,
&tty_feature_focus,
&tty_feature_ignorefkeys,
&tty_feature_kitty,
&tty_feature_margins,
&tty_feature_mouse,
&tty_feature_osc7,
@@ -500,7 +512,19 @@ tty_default_features(int *feat, const char *name, u_int version)
*/
.features = TTY_FEATURES_BASE_MODERN_XTERM
",ccolour,cstyle,extkeys,focus"
}
},
#ifdef ENABLE_KITTY_IMAGES
{ .name = "kitty",
.features = TTY_FEATURES_BASE_MODERN_XTERM
",ccolour,cstyle,extkeys,focus,sync,hyperlinks"
",kitty"
},
{ .name = "ghostty",
.features = TTY_FEATURES_BASE_MODERN_XTERM
",ccolour,cstyle,extkeys,focus,sync,hyperlinks"
",kitty"
},
#endif
};
u_int i;

View File

@@ -60,6 +60,10 @@ static int tty_keys_device_attributes2(struct tty *, const char *, size_t,
static int tty_keys_extended_device_attributes(struct tty *, const char *,
size_t, size_t *);
static int tty_keys_palette(struct tty *, const char *, size_t, size_t *);
#ifdef ENABLE_KITTY_IMAGES
static int tty_keys_kitty_graphics(struct tty *, const char *, size_t,
size_t *);
#endif
/* A key tree entry. */
struct tty_key {
@@ -759,6 +763,19 @@ tty_keys_next(struct tty *tty)
goto partial_key;
}
#ifdef ENABLE_KITTY_IMAGES
/* Is this a kitty graphics protocol response? ESC _ G ... ESC \ */
switch (tty_keys_kitty_graphics(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;
}
#endif
/* Is this a primary device attributes response? */
switch (tty_keys_device_attributes(tty, buf, len, &size)) {
case 0: /* yes */
@@ -1640,6 +1657,14 @@ tty_keys_extended_device_attributes(struct tty *tty, const char *buf,
tty_default_features(features, "mintty", 0);
else if (strncmp(tmp, "foot(", 5) == 0)
tty_default_features(features, "foot", 0);
#ifdef ENABLE_KITTY_IMAGES
else if (strncmp(tmp, "kitty ", 6) == 0 ||
strcmp(tmp, "kitty") == 0)
tty_default_features(features, "kitty", 0);
else if (strncmp(tmp, "ghostty ", 8) == 0 ||
strcmp(tmp, "ghostty") == 0)
tty_default_features(features, "ghostty", 0);
#endif
log_debug("%s: received extended DA %.*s", c->name, (int)*size, buf);
free(c->term_type);
@@ -1795,3 +1820,74 @@ tty_keys_palette(struct tty *tty, const char *buf, size_t len, size_t *size)
return (0);
}
#ifdef ENABLE_KITTY_IMAGES
/*
* Handle kitty graphics protocol response from outer terminal.
* Format: ESC _ G <key=value,...> ; <message> ESC \
* The response to our capability probe (a=q) is:
* ESC _ G i=31;OK ESC \ (supported)
* ESC _ G i=31;ENOSYS:... (not supported)
* Returns 0 for success, -1 for not a kitty response, 1 for partial.
*/
static int
tty_keys_kitty_graphics(struct tty *tty, const char *buf, size_t len,
size_t *size)
{
struct client *c = tty->client;
int *features = &c->term_features;
const char *semi;
size_t i;
char tmp[256];
*size = 0;
/*
* Kitty APC response starts with ESC _ G (3 bytes).
* The 8-bit C1 equivalent 0x9f could also be used but is rare.
*/
if (buf[0] != '\033')
return (-1);
if (len == 1)
return (1);
if (buf[1] != '_')
return (-1);
if (len == 2)
return (1);
if (buf[2] != 'G')
return (-1);
if (len == 3)
return (1);
/* Collect body up to ESC \ (ST). */
for (i = 0; i < sizeof(tmp) - 1; i++) {
if (3 + i + 1 >= len)
return (1); /* partial */
if (buf[3 + i] == '\033' && buf[3 + i + 1] == '\\') {
tmp[i] = '\0';
*size = 3 + i + 2;
break;
}
tmp[i] = buf[3 + i];
}
if (i == sizeof(tmp) - 1)
return (-1); /* too long, not a valid response */
log_debug("%s: kitty graphics response: %s", c->name, tmp);
/*
* Check if the message (after the semicolon) starts with "OK".
* The format is: i=31;OK or i=31;ENOSYS:...
*/
semi = strchr(tmp, ';');
if (semi != NULL && strncmp(semi + 1, "OK", 2) == 0) {
log_debug("%s: kitty graphics supported", c->name);
tty_add_features(features, "kitty", ",");
tty_update_features(tty);
} else {
log_debug("%s: kitty graphics not supported", c->name);
}
return (0);
}
#endif

View File

@@ -461,6 +461,9 @@ tty_term_apply_overrides(struct tty_term *term)
/* Log the SIXEL flag. */
log_debug("SIXEL flag is %d", !!(term->flags & TERM_SIXEL));
/* Log the KITTY flag. */
log_debug("KITTY flag is %d", !!(term->flags & TERM_KITTY));
/* Update the RGB flag if the terminal has RGB colours. */
if (tty_term_has(term, TTYC_SETRGBF) &&
tty_term_has(term, TTYC_SETRGBB))

343
tty.c
View File

@@ -67,7 +67,7 @@ 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 int tty_check_overlay(struct tty *, u_int, u_int);
#ifdef ENABLE_SIXEL
#ifdef ENABLE_IMAGES
static void tty_write_one(void (*)(struct tty *, const struct tty_ctx *),
struct client *, struct tty_ctx *);
#endif
@@ -391,6 +391,23 @@ tty_send_requests(struct tty *tty)
return;
if (tty->term->flags & TERM_VT100LIKE) {
#ifdef ENABLE_KITTY_IMAGES
/*
* Send the kitty graphics capability probe BEFORE the DA1
* request. Per the kitty spec, a supporting terminal sends
* its APC response before processing any subsequent requests.
* So the reply ordering will be:
* 1. ESC _ G i=31;OK ESC \ (kitty response, if supported)
* 2. ESC [ ? ... c (DA1 response)
* 3. ESC [ > ... c (DA2 response)
* 4. ESC P > | ... ESC \ (XDA response)
* which is exactly what our parsers expect.
* Only probe if the kitty feature isn't already enabled.
*/
if (~tty->term->flags & TERM_KITTY)
tty_puts(tty,
"\033_Gi=31,s=1,v=1,a=q,t=d,f=24;AAAA\033\\");
#endif
if (~tty->flags & TTY_HAVEDA)
tty_puts(tty, "\033[c");
if (~tty->flags & TTY_HAVEDA2)
@@ -1003,10 +1020,7 @@ tty_window_offset1(struct tty *tty, u_int *ox, u_int *oy, u_int *sx, u_int *sy)
else if (cy > w->sy - *sy)
*oy = w->sy - *sy;
else
/* cy-sy/2 was causing panned panes to scroll
* when the cursor was half way down the pane.
*/
*oy = cy - *sy + 1; /* cy - *sy / 2; */
*oy = cy - *sy / 2;
}
c->pan_window = NULL;
@@ -1126,23 +1140,23 @@ static int
tty_clamp_line(struct tty *tty, const struct tty_ctx *ctx, u_int px, u_int py,
u_int nx, u_int *i, u_int *x, u_int *rx, u_int *ry)
{
int xoff = ctx->rxoff + px;
u_int xoff = ctx->rxoff + px;
if (!tty_is_visible(tty, ctx, px, py, nx, 1))
return (0);
*ry = ctx->yoff + py - ctx->woy;
if (xoff >= (int)ctx->wox && xoff + nx <= ctx->wox + ctx->wsx) {
if (xoff >= ctx->wox && xoff + nx <= ctx->wox + ctx->wsx) {
/* All visible. */
*i = 0;
*x = ctx->xoff + px - ctx->wox;
*rx = nx;
} else if (xoff < (int)ctx->wox && xoff + nx > ctx->wox + ctx->wsx) {
} else if (xoff < ctx->wox && xoff + nx > ctx->wox + ctx->wsx) {
/* Both left and right not visible. */
*i = ctx->wox;
*x = 0;
*rx = ctx->wsx;
} else if (xoff < (int)ctx->wox) {
} else if (xoff < ctx->wox) {
/* Left not visible. */
*i = ctx->wox - (ctx->xoff + px);
*x = 0;
@@ -1209,23 +1223,13 @@ static void
tty_clear_pane_line(struct tty *tty, const struct tty_ctx *ctx, u_int py,
u_int px, u_int nx, u_int bg)
{
struct client *c = tty->client;
struct visible_ranges *r;
struct visible_range *ri;
u_int i, l, x, rx, ry;
struct client *c = tty->client;
u_int i, x, rx, ry;
log_debug("%s: %s, %u at %u,%u", __func__, c->name, nx, px, py);
if (tty_clamp_line(tty, ctx, px, py, nx, &l, &x, &rx, &ry)) {
r = tty_check_overlay_range(tty, x, ry, rx);
for (i=0; i < r->used; i++) {
ri = &r->ranges[i];
if (ri->nx == 0)
continue;
tty_clear_line(tty, &ctx->defaults, ry, ri->px, ri->nx,
bg);
}
}
if (tty_clamp_line(tty, ctx, px, py, nx, &i, &x, &rx, &ry))
tty_clear_line(tty, &ctx->defaults, ry, x, rx, bg);
}
/* Clamp area position to visible part of pane. */
@@ -1292,17 +1296,12 @@ tty_clamp_area(struct tty *tty, const struct tty_ctx *ctx, u_int px, u_int py,
/* Clear an area, adjusting to visible part of pane. */
static void
tty_clear_area(struct tty *tty, const struct tty_ctx *ctx, u_int py,
tty_clear_area(struct tty *tty, const struct grid_cell *defaults, u_int py,
u_int ny, u_int px, u_int nx, u_int bg)
{
struct client *c = tty->client;
const struct grid_cell *defaults = &ctx->defaults;
struct window *w = c->session->curw->window;
struct window_pane *wpl, *wp = ctx->arg;
struct visible_ranges *r;
struct visible_range *ri;
u_int i, yy, overlap = 0, oy = 0;
char tmp[64];
struct client *c = tty->client;
u_int yy;
char tmp[64];
log_debug("%s: %s, %u,%u at %u,%u", __func__, c->name, nx, ny, px, py);
@@ -1310,21 +1309,8 @@ tty_clear_area(struct tty *tty, const struct tty_ctx *ctx, u_int py,
if (nx == 0 || ny == 0)
return;
/* Verify there's nothing overlapping in z-index before using BCE. */
TAILQ_FOREACH(wpl, &w->z_index, zentry) {
if (wpl == wp || ~wpl->flags & PANE_FLOATING)
continue;
if (wpl->xoff - 1 > (int)(px + nx) || wpl->xoff + wpl->sx + 1 < px)
continue;
if (wpl->yoff - 1 > (int)(py + ny) || wpl->yoff + wpl->sy + 1 < py)
continue;
overlap++;
if (overlap > 0) break;
}
/* If genuine BCE is available, can try escape sequences. */
if (!overlap && c->overlay_check == NULL &&
!tty_fake_bce(tty, defaults, bg)) {
if (c->overlay_check == NULL && !tty_fake_bce(tty, defaults, bg)) {
/* Use ED if clearing off the bottom of the terminal. */
if (px == 0 &&
px + nx >= tty->sx &&
@@ -1375,18 +1361,9 @@ tty_clear_area(struct tty *tty, const struct tty_ctx *ctx, u_int py,
}
}
if (c->session->statusat == 0)
oy = c->session->statuslines;
/* Couldn't use an escape sequence, loop over the lines. */
for (yy = py; yy < py + ny; yy++) {
r = tty_check_overlay_range(tty, px, yy - oy, nx);
for (i=0; i < r->used; i++) {
ri = &r->ranges[i];
if (ri->nx == 0) continue;
tty_clear_line(tty, defaults, yy, ri->px, ri->nx, bg);
}
}
for (yy = py; yy < py + ny; yy++)
tty_clear_line(tty, defaults, yy, px, nx, bg);
}
/* Clear an area in a pane. */
@@ -1394,57 +1371,28 @@ static void
tty_clear_pane_area(struct tty *tty, const struct tty_ctx *ctx, u_int py,
u_int ny, u_int px, u_int nx, u_int bg)
{
u_int i, j, x, y, rx, ry;
u_int i, j, x, y, rx, ry;
if (tty_clamp_area(tty, ctx, px, py, nx, ny, &i, &j, &x, &y, &rx, &ry))
tty_clear_area(tty, ctx, y, ry, x, rx, bg);
tty_clear_area(tty, &ctx->defaults, y, ry, x, rx, bg);
}
/* Redraw a line at py of a screen taking into account obscured ranges.
* Menus and popups are always on top, ctx->arg == NULL.
*/
static void
tty_draw_pane(struct tty *tty, const struct tty_ctx *ctx, u_int py)
{
struct screen *s = ctx->s;
struct window_pane *wp = ctx->arg;
struct visible_ranges *r = NULL;
struct visible_range *ri;
u_int nx = ctx->sx, i, px, x, rx, ry;
struct screen *s = ctx->s;
u_int nx = ctx->sx, i, x, rx, ry;
log_debug("%s: %s %u %d", __func__, tty->client->name, py, ctx->bigger);
if (!ctx->bigger) {
if (wp) {
r = tty_check_overlay_range(tty, 0, ctx->yoff + py, nx);
for (i=0; i < r->used; i++) {
ri = &r->ranges[i];
if (ri->nx == 0) continue;
tty_draw_line(tty, s, ri->px, py, ri->nx,
ctx->xoff + ri->px, ctx->yoff + py,
&ctx->defaults, ctx->palette);
}
} else {
tty_draw_line(tty, s, 0, py, nx, ctx->xoff,
ctx->yoff + py, &ctx->defaults, ctx->palette);
}
tty_draw_line(tty, s, 0, py, nx, ctx->xoff, ctx->yoff + py,
&ctx->defaults, ctx->palette);
return;
}
if (tty_clamp_line(tty, ctx, 0, py, nx, &px, &x, &rx, &ry)) {
if (wp) {
r = tty_check_overlay_range(tty, i, py, rx);
for (i=0; i < r->used; i++) {
ri = &r->ranges[i];
if (ri->nx == 0)
continue;
tty_draw_line(tty, s, i, py, ri->nx,
x + ri->px, ry, &ctx->defaults,
ctx->palette);
}
} else {
tty_draw_line(tty, s, px, py, rx, x, ry, &ctx->defaults,
ctx->palette);
}
if (tty_clamp_line(tty, ctx, 0, py, nx, &i, &x, &rx, &ry)) {
tty_draw_line(tty, s, i, py, rx, x, ry, &ctx->defaults,
ctx->palette);
}
}
@@ -1511,9 +1459,9 @@ 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));
}
#ifdef ENABLE_SIXEL
#ifdef ENABLE_IMAGES
/* Update context for client. */
static int
int
tty_set_client_cb(struct tty_ctx *ttyctx, struct client *c)
{
struct window_pane *wp = ttyctx->arg;
@@ -1527,7 +1475,7 @@ tty_set_client_cb(struct tty_ctx *ttyctx, struct client *c)
ttyctx->bigger = tty_window_offset(&c->tty, &ttyctx->wox, &ttyctx->woy,
&ttyctx->wsx, &ttyctx->wsy);
ttyctx->yoff = ttyctx->ryoff = wp->yoff; /* xxxx find another way to do this */
ttyctx->yoff = ttyctx->ryoff = wp->yoff;
if (status_at_line(c) == 0)
ttyctx->yoff += status_line_size(c);
@@ -1555,10 +1503,25 @@ tty_draw_images(struct client *c, struct window_pane *wp, struct screen *s)
ttyctx.sy = wp->sy;
ttyctx.ptr = im;
ttyctx.arg = wp; /* xxx remove this */
ttyctx.arg = wp;
ttyctx.set_client_cb = tty_set_client_cb;
ttyctx.allow_invisible_panes = 1;
tty_write_one(tty_cmd_sixelimage, c, &ttyctx);
/* Call the appropriate rendering function based on image type */
switch (im->type) {
#ifdef ENABLE_SIXEL_IMAGES
case IMAGE_SIXEL:
tty_write_one(tty_cmd_sixelimage, c, &ttyctx);
break;
#endif
#ifdef ENABLE_KITTY_IMAGES
case IMAGE_KITTY:
tty_write_one(tty_cmd_kittyimage, c, &ttyctx);
break;
#endif
default:
break;
}
}
}
#endif
@@ -1636,7 +1599,7 @@ tty_write(void (*cmdfn)(struct tty *, const struct tty_ctx *),
}
}
#ifdef ENABLE_SIXEL
#ifdef ENABLE_IMAGES
/* Only write to the incoming tty instead of every client. */
static void
tty_write_one(void (*cmdfn)(struct tty *, const struct tty_ctx *),
@@ -1869,8 +1832,8 @@ tty_cmd_linefeed(struct tty *tty, const struct tty_ctx *ctx)
void
tty_cmd_scrollup(struct tty *tty, const struct tty_ctx *ctx)
{
struct client *c = tty->client;
u_int i;
struct client *c = tty->client;
u_int i;
if (ctx->bigger ||
(!tty_full_width(tty, ctx) && !tty_use_margin(tty)) ||
@@ -1878,8 +1841,7 @@ tty_cmd_scrollup(struct tty *tty, const struct tty_ctx *ctx)
!tty_term_has(tty->term, TTYC_CSR) ||
ctx->sx == 1 ||
ctx->sy == 1 ||
c->overlay_check != NULL ||
ctx->obscured) {
c->overlay_check != NULL) {
tty_redraw_region(tty, ctx);
return;
}
@@ -2037,7 +1999,7 @@ tty_cmd_cell(struct tty *tty, const struct tty_ctx *ctx)
{
const struct grid_cell *gcp = ctx->cell;
struct screen *s = ctx->s;
struct visible_ranges *r = NULL;
struct visible_ranges *r;
u_int px, py, i, vis = 0;
px = ctx->xoff + ctx->ocx - ctx->wox;
@@ -2047,7 +2009,6 @@ tty_cmd_cell(struct tty *tty, const struct tty_ctx *ctx)
return;
if (ctx->num == 2) {
/* xxxx need to check visible range */
tty_draw_line(tty, s, 0, s->cy, screen_size_x(s),
ctx->xoff - ctx->wox, py, &ctx->defaults, ctx->palette);
return;
@@ -2073,9 +2034,8 @@ tty_cmd_cell(struct tty *tty, const struct tty_ctx *ctx)
tty_margin_off(tty);
tty_cursor_pane_unless_wrap(tty, ctx, ctx->ocx, ctx->ocy);
if (screen_redraw_is_visible(r, px))
tty_cell(tty, ctx->cell, &ctx->defaults, ctx->palette,
ctx->s->hyperlinks);
tty_cell(tty, ctx->cell, &ctx->defaults, ctx->palette,
ctx->s->hyperlinks);
if (ctx->num == 1)
tty_invalidate(tty);
@@ -2085,7 +2045,7 @@ void
tty_cmd_cells(struct tty *tty, const struct tty_ctx *ctx)
{
struct visible_ranges *r;
struct visible_range *ri;
struct visible_range *rr;
u_int i, px, py, cx;
char *cp = ctx->ptr;
@@ -2119,11 +2079,11 @@ tty_cmd_cells(struct tty *tty, const struct tty_ctx *ctx)
r = tty_check_overlay_range(tty, px, py, ctx->num);
for (i = 0; i < r->used; i++) {
ri = &r->ranges[i];
if (ri->nx != 0) {
cx = ri->px - ctx->xoff + ctx->wox;
rr = &r->ranges[i];
if (rr->nx != 0) {
cx = rr->px - ctx->xoff + ctx->wox;
tty_cursor_pane_unless_wrap(tty, ctx, cx, ctx->ocy);
tty_putn(tty, cp + ri->px - px, ri->nx, ri->nx);
tty_putn(tty, cp + rr->px - px, rr->nx, rr->nx);
}
}
}
@@ -2164,12 +2124,12 @@ tty_cmd_rawstring(struct tty *tty, const struct tty_ctx *ctx)
tty_invalidate(tty);
}
#ifdef ENABLE_SIXEL
#ifdef ENABLE_SIXEL_IMAGES
void
tty_cmd_sixelimage(struct tty *tty, const struct tty_ctx *ctx)
{
struct image *im = ctx->ptr;
struct sixel_image *si = im->data;
struct sixel_image *si = im->data.sixel;
struct sixel_image *new;
char *data;
size_t size;
@@ -2216,6 +2176,142 @@ tty_cmd_sixelimage(struct tty *tty, const struct tty_ctx *ctx)
}
#endif
#ifdef ENABLE_KITTY_IMAGES
static int
tty_has_kitty(struct tty *tty)
{
return (tty->term->flags & TERM_KITTY);
}
void
tty_cmd_kittyimage(struct tty *tty, const struct tty_ctx *ctx)
{
struct image *im = ctx->ptr;
char *data;
size_t size;
u_int cx = ctx->ocx, cy = ctx->ocy;
int fallback = 0;
if (im == NULL || im->data.kitty == NULL)
return;
/* Check if this terminal supports kitty graphics. */
if (!tty_has_kitty(tty))
fallback = 1;
log_debug("%s: image at %u,%u (fallback=%d)", __func__, cx, cy,
fallback);
if (fallback == 1) {
/* Use text fallback for non-kitty terminals. */
data = xstrdup(im->fallback);
size = strlen(data);
} else {
/* Re-serialize the kitty image command. */
data = kitty_print(im->data.kitty, &size);
}
if (data != NULL) {
log_debug("%s: %zu bytes", __func__, size);
tty_region_off(tty);
tty_margin_off(tty);
tty_cursor(tty, cx + ctx->xoff, cy + ctx->yoff);
tty->flags |= TTY_NOBLOCK;
tty_add(tty, data, size);
tty_invalidate(tty);
free(data);
}
}
/*
* Pass a kitty APC sequence directly to all attached kitty-capable clients
* showing the given pane. The outer terminal's cursor is first moved to
* the pane-relative position (cx, cy) so that images placed at "current
* cursor" land in the right spot. Pass cx=cy=UINT_MAX to skip cursor
* positioning (e.g. for delete commands that don't depend on position).
*/
void
tty_kitty_passthrough(struct window_pane *wp, const char *data, size_t len,
u_int cx, u_int cy)
{
struct client *c;
struct tty *tty;
u_int x, y;
TAILQ_FOREACH(c, &clients, entry) {
if (c->session == NULL || c->tty.term == NULL)
continue;
if (c->flags & CLIENT_SUSPENDED)
continue;
if (c->tty.flags & TTY_FREEZE)
continue;
tty = &c->tty;
if (!tty_has_kitty(tty))
continue;
if (c->session->curw->window != wp->window)
continue;
if (!window_pane_visible(wp))
continue;
/* Position cursor at the correct screen location. */
if (cx != UINT_MAX && cy != UINT_MAX) {
x = wp->xoff + cx;
y = wp->yoff + cy;
if (status_at_line(c) == 0)
y += status_line_size(c);
tty_region_off(tty);
tty_margin_off(tty);
tty_cursor(tty, x, y);
}
tty->flags |= TTY_NOBLOCK;
tty_add(tty, data, len);
tty_invalidate(tty);
}
}
/*
* Delete all kitty image placements from the outer terminal unconditionally.
* Called directly (not via tty_write) so it fires on every full window
* redraw regardless of whether the current window has any stored images.
*/
void
tty_kitty_delete_all(struct tty *tty)
{
char *data;
size_t size;
if (!tty_has_kitty(tty))
return;
if ((data = kitty_delete_all(&size)) == NULL)
return;
tty->flags |= TTY_NOBLOCK;
tty_add(tty, data, size);
tty_invalidate(tty);
free(data);
}
/*
* Delete all kitty image placements via passthrough for a specific pane.
* Used on terminal reset (RIS) so images are cleared from the outer terminal.
*/
void
tty_kitty_delete_all_pane(struct window_pane *wp)
{
char *data;
size_t size;
if ((data = kitty_delete_all(&size)) == NULL)
return;
tty_kitty_passthrough(wp, data, size, UINT_MAX, UINT_MAX);
free(data);
}
#endif
void
tty_cmd_syncstart(struct tty *tty, const struct tty_ctx *ctx)
{
@@ -2365,17 +2461,8 @@ tty_margin_off(struct tty *tty)
static void
tty_margin_pane(struct tty *tty, const struct tty_ctx *ctx)
{
int l, r;
l = ctx->xoff - ctx->wox;
r = ctx->xoff + ctx->sx - 1 - ctx->wox;
if (l < 0) l = 0;
if (l > (int)ctx->wsx) l = ctx->wsx;
if (r < 0) r = 0;
if (r > (int)ctx->wsx) r = ctx->wsx;
tty_margin(tty, l, r);
tty_margin(tty, ctx->xoff - ctx->wox,
ctx->xoff + ctx->sx - 1 - ctx->wox);
}
/* Set margin at absolute position. */

View File

@@ -42,7 +42,7 @@ static void window_copy_formats(struct window_mode_entry *,
struct format_tree *);
static struct screen *window_copy_get_screen(struct window_mode_entry *);
static void window_copy_scroll1(struct window_mode_entry *,
struct window_pane *wp, int, u_int, u_int, int);
struct window_pane *wp, int, u_int, int);
static void window_copy_pageup1(struct window_mode_entry *, int);
static int window_copy_pagedown1(struct window_mode_entry *, int, int);
static void window_copy_next_paragraph(struct window_mode_entry *);
@@ -596,19 +596,19 @@ window_copy_vadd(struct window_pane *wp, int parse, const char *fmt, va_list ap)
void
window_copy_scroll(struct window_pane *wp, int sl_mpos, u_int my,
u_int tty_oy, int scroll_exit)
int scroll_exit)
{
struct window_mode_entry *wme = TAILQ_FIRST(&wp->modes);
if (wme != NULL) {
window_set_active_pane(wp->window, wp, 0);
window_copy_scroll1(wme, wp, sl_mpos, my, tty_oy, scroll_exit);
window_copy_scroll1(wme, wp, sl_mpos, my, scroll_exit);
}
}
static void
window_copy_scroll1(struct window_mode_entry *wme, struct window_pane *wp,
int sl_mpos, u_int my, u_int tty_oy, int scroll_exit)
int sl_mpos, u_int my, int scroll_exit)
{
struct window_copy_mode_data *data = wme->data;
u_int ox, oy, px, py, n, offset, size;
@@ -616,29 +616,21 @@ window_copy_scroll1(struct window_mode_entry *wme, struct window_pane *wp,
u_int slider_height = wp->sb_slider_h;
u_int sb_height = wp->sy, sb_top = wp->yoff;
u_int sy = screen_size_y(data->backing);
u_int my_w;
int new_slider_y, delta;
/*
* sl_mpos is where in the slider the user is dragging, mouse is
* dragging this y point relative to top of slider.
*
* my is a raw tty y coordinate; sb_top (= wp->yoff) is a window
* coordinate. Convert my to window coordinates by adding tty_oy
* (the window pan offset). sl_mpos already has the statuslines
* adjustment baked in (see server_client_check_mouse), so no further
* statuslines correction is needed here.
*/
my_w = my + tty_oy;
if (my_w <= sb_top + (u_int)sl_mpos) {
if (my <= sb_top + sl_mpos) {
/* Slider banged into top. */
new_slider_y = sb_top - wp->yoff;
} else if (my_w - sl_mpos > sb_top + sb_height - slider_height) {
} else if (my - sl_mpos > sb_top + sb_height - slider_height) {
/* Slider banged into bottom. */
new_slider_y = sb_top - wp->yoff + (sb_height - slider_height);
} else {
/* Slider is somewhere in the middle. */
new_slider_y = my_w - wp->yoff - sl_mpos;
new_slider_y = my - wp->yoff - sl_mpos;
}
if (TAILQ_FIRST(&wp->modes) == NULL ||
@@ -1511,11 +1503,8 @@ window_copy_cmd_scroll_to_mouse(struct window_copy_cmd_state *cs)
struct client *c = cs->c;
struct mouse_event *m = cs->m;
int scroll_exit = args_has(cs->wargs, 'e');
u_int tty_ox, tty_oy, tty_sx, tty_sy;
tty_window_offset(&c->tty, &tty_ox, &tty_oy, &tty_sx, &tty_sy);
window_copy_scroll(wp, c->tty.mouse_slider_mpos, m->y, tty_oy,
scroll_exit);
window_copy_scroll(wp, c->tty.mouse_slider_mpos, m->y, scroll_exit);
return (WINDOW_COPY_CMD_NOTHING);
}
@@ -4716,7 +4705,7 @@ window_copy_write_lines(struct window_mode_entry *wme,
u_int yy;
for (yy = py; yy < py + ny; yy++)
window_copy_write_line(wme, ctx, yy);
window_copy_write_line(wme, ctx, py);
}
static void
@@ -6082,10 +6071,7 @@ window_copy_drag_update(struct client *c, struct mouse_event *m)
if (c == NULL)
return;
if (c->tty.mouse_wp != NULL)
wp = c->tty.mouse_wp;
else
wp = cmd_mouse_pane(m, NULL, NULL);
wp = cmd_mouse_pane(m, NULL, NULL);
if (wp == NULL)
return;
wme = TAILQ_FIRST(&wp->modes);

136
window.c
View File

@@ -306,7 +306,6 @@ window_create(u_int sx, u_int sy, u_int xpixel, u_int ypixel)
w->flags = 0;
TAILQ_INIT(&w->panes);
TAILQ_INIT(&w->z_index);
TAILQ_INIT(&w->last_panes);
w->active = NULL;
@@ -528,8 +527,6 @@ window_set_active_pane(struct window *w, struct window_pane *wp, int notify)
if (wp == w->active)
return (0);
if (w->flags & WINDOW_ZOOMED)
window_unzoom(w, 1);
lastwp = w->active;
window_pane_stack_remove(&w->last_panes, wp);
@@ -539,20 +536,6 @@ window_set_active_pane(struct window *w, struct window_pane *wp, int notify)
w->active->active_point = next_active_point++;
w->active->flags |= PANE_CHANGED;
if (wp->flags & PANE_MINIMISED) {
wp->flags &= ~PANE_MINIMISED;
if (w->layout_root != NULL) {
wp->layout_cell = wp->saved_layout_cell;
wp->saved_layout_cell = NULL;
layout_unminimise_cell(w, wp->layout_cell);
layout_fix_offsets(w);
layout_fix_panes(w, NULL);
}
}
notify_window("window-layout-changed", w);
server_redraw_window(w);
if (options_get_number(global_options, "focus-events")) {
window_pane_update_focus(lastwp);
window_pane_update_focus(w->active);
@@ -565,33 +548,6 @@ window_set_active_pane(struct window *w, struct window_pane *wp, int notify)
return (1);
}
int
window_deactivate_pane(struct window *w, struct window_pane *wp, int notify)
{
struct window_pane *lastwp;
log_debug("%s: pane %%%u", __func__, wp->id);
if (w->flags & WINDOW_ZOOMED)
window_unzoom(w, 1);
lastwp = w->active;
window_pane_stack_remove(&w->last_panes, wp);
window_pane_stack_push(&w->last_panes, lastwp);
w->active = NULL;
if (options_get_number(global_options, "focus-events")) {
window_pane_update_focus(lastwp);
}
tty_update_window_offset(w);
if (notify)
notify_window("window-pane-changed", w);
return (1);
}
static int
window_pane_get_palette(struct window_pane *wp, int c)
{
@@ -632,20 +588,7 @@ window_redraw_active_switch(struct window *w, struct window_pane *wp)
}
if (wp == w->active)
break;
/* If you want tiled planes to be able to bury
* floating planes then do this regardless of
* wp->flags & PANE_FLOATING or not. A new option?
*/
if (wp->flags & PANE_FLOATING) {
TAILQ_REMOVE(&w->z_index, wp, zentry);
TAILQ_INSERT_HEAD(&w->z_index, wp, zentry);
wp->flags |= PANE_REDRAW;
}
wp = w->active;
if (wp == NULL)
break;
}
}
@@ -653,34 +596,16 @@ struct window_pane *
window_get_active_at(struct window *w, u_int x, u_int y)
{
struct window_pane *wp;
int status, xoff, yoff;
u_int sx, sy;
u_int xoff, yoff, sx, sy;
status = options_get_number(w->options, "pane-border-status");
TAILQ_FOREACH(wp, &w->z_index, zentry) {
TAILQ_FOREACH(wp, &w->panes, entry) {
if (!window_pane_visible(wp))
continue;
window_pane_full_size_offset(wp, &xoff, &yoff, &sx, &sy);
if (~wp->flags & PANE_FLOATING) {
/* Tiled, select up to including bottom or
right border. */
if ((int)x < xoff || x > xoff + sx)
continue;
if (status == PANE_STATUS_TOP) {
if ((int)y < yoff - 1 || y > yoff + sy)
continue;
} else {
if ((int)y < yoff || y > yoff + sy)
continue;
}
} else {
/* Floating, include top or or left border. */
if ((int)x < xoff - 1 || x > xoff + sx)
continue;
if ((int)y < yoff - 1 || y > yoff + sy)
continue;
}
if (x < xoff || x > xoff + sx)
continue;
if (y < yoff || y > yoff + sy)
continue;
return (wp);
}
return (NULL);
@@ -736,24 +661,12 @@ window_zoom(struct window_pane *wp)
if (w->flags & WINDOW_ZOOMED)
return (-1);
if (window_count_panes(w) == 1)
return (-1);
if (w->active != wp)
window_set_active_pane(w, wp, 1);
/* Bring pane above other tiled panes and minimise floating panes. */
TAILQ_FOREACH(wp1, &w->z_index, zentry) {
if (wp1 == wp) {
wp1->saved_flags |= (wp1->flags & PANE_MINIMISED);
wp1->flags &= ~PANE_MINIMISED;
continue;
}
if (wp1->flags & PANE_FLOATING) {
wp1->saved_flags |= (wp1->flags & PANE_MINIMISED);
wp1->flags |= PANE_MINIMISED;
continue;
}
break;
}
TAILQ_FOREACH(wp1, &w->panes, entry) {
wp1->saved_layout_cell = wp1->layout_cell;
wp1->layout_cell = NULL;
@@ -780,14 +693,6 @@ window_unzoom(struct window *w, int notify)
w->layout_root = w->saved_layout_root;
w->saved_layout_root = NULL;
TAILQ_FOREACH(wp, &w->z_index, zentry) {
if (wp->flags & PANE_FLOATING) {
wp->flags &= ~PANE_MINIMISED | (wp->saved_flags & PANE_MINIMISED);
continue;
}
break;
}
TAILQ_FOREACH(wp, &w->panes, entry) {
wp->layout_cell = wp->saved_layout_cell;
wp->saved_layout_cell = NULL;
@@ -843,17 +748,11 @@ window_add_pane(struct window *w, struct window_pane *other, u_int hlimit,
TAILQ_INSERT_BEFORE(other, wp, entry);
} else {
log_debug("%s: @%u after %%%u", __func__, w->id, wp->id);
if (flags & (SPAWN_FULLSIZE|SPAWN_FLOATING))
if (flags & SPAWN_FULLSIZE)
TAILQ_INSERT_TAIL(&w->panes, wp, entry);
else
TAILQ_INSERT_AFTER(&w->panes, other, wp, entry);
}
/* Floating panes are created above tiled planes. */
if (flags & SPAWN_FLOATING) {
wp->flags |= PANE_FLOATING;
TAILQ_INSERT_HEAD(&w->z_index, wp, zentry);
} else
TAILQ_INSERT_TAIL(&w->z_index, wp, zentry);
return (wp);
}
@@ -888,7 +787,6 @@ window_remove_pane(struct window *w, struct window_pane *wp)
window_lost_pane(w, wp);
TAILQ_REMOVE(&w->panes, wp, entry);
TAILQ_REMOVE(&w->z_index, wp, zentry);
window_pane_destroy(wp);
}
@@ -972,7 +870,6 @@ window_destroy_panes(struct window *w)
while (!TAILQ_EMPTY(&w->panes)) {
wp = TAILQ_FIRST(&w->panes);
TAILQ_REMOVE(&w->panes, wp, entry);
TAILQ_REMOVE(&w->z_index, wp, zentry);
window_pane_destroy(wp);
}
}
@@ -1208,15 +1105,6 @@ window_pane_resize(struct window_pane *wp, u_int sx, u_int sy)
wme->mode->resize(wme, sx, sy);
}
void
window_pane_move(struct window_pane *wp, int xoff, int yoff)
{
wp->xoff = xoff;
wp->yoff = yoff;
log_debug("%s: %%%u resize %ux%u", __func__, wp->id, xoff, yoff);
}
int
window_pane_set_mode(struct window_pane *wp, struct window_pane *swp,
const struct window_mode *mode, struct cmd_find_state *fs,
@@ -1384,10 +1272,8 @@ window_pane_key(struct window_pane *wp, struct client *c, struct session *s,
int
window_pane_visible(struct window_pane *wp)
{
if (~wp->window->flags & WINDOW_ZOOMED &&
~wp->flags & PANE_MINIMISED)
if (~wp->window->flags & WINDOW_ZOOMED)
return (1);
return (wp == wp->window->active);
}