103 Commits

Author SHA1 Message Date
Michael Grant
8a90fce476 Fix for window size taller than tty. 2026-03-10 13:21:01 +00:00
Michael Grant
e928e80a42 Fix scrollbar issue not respecting oy when window size taller than tty. 2026-03-09 16:28:06 +00:00
Michael Grant
28c04b21f6 Minor fixup. 2026-03-09 08:35:24 +00:00
Michael Grant
46427f1dd7 Merge branch 'master' into floating_panes 2026-03-09 08:31:58 +00:00
Michael Grant
528e856566 Merge pull request #4893 from daneofmanythings/floating_panes__float_format
feat(format variable): added format variable for floating panes.
2026-03-08 17:14:16 +00:00
Michael Grant
14a934d412 Fix screen clearing issue to surgically clear around floating panes. 2026-03-08 17:11:55 +00:00
Thomas Adam
d0caf0a322 Merge branch 'obsd-master' 2026-03-06 12:01:08 +00:00
tb
91b5108eae tmux: don't leak menu if the tty is too small to display it
ok nicm
2026-03-06 08:19:22 +00:00
nicm
2c7f73f9c4 Do not use recallocarray because the stored size may have changed during
reflow so may not match what it expects, fixes crash reported by Caspar
Schutijser.
2026-03-06 08:09:58 +00:00
Thomas Adam
5310592967 Merge branch 'obsd-master' 2026-03-06 04:01:09 +00:00
nicm
41bddae907 Fix incorrect condition for creating client name, from Vlad Apostol in
GitHub issue 4911.
2026-03-05 22:50:14 +00:00
Dane Jensen
0faed7aeb5 added format flag for floating panes. 2026-03-04 12:08:43 -08:00
Michael Grant
8e6450c8fc Merge branch 'master' into floating_panes 2026-03-04 13:16:52 +00:00
Michael Grant
6898e26a7a Fix bug with utf-8 extended characters not respecting visible range. 2026-02-24 01:19:24 +00:00
Michael Grant
e2cf40ef99 Fix crash cause by not having a wp pointer in tty.c. 2026-02-23 08:08:31 +00:00
Michael Grant
96515c9f47 Merge branch 'master' into floating_panes 2026-02-18 09:28:38 +00:00
Michael Grant
96e18faa93 Merge branch 'master' into floating_panes 2026-02-11 09:40:21 +00:00
Michael Grant
476c6e89ad Check the overlay visible ranges when we have a tty and check the panes visible ranges when we have a wp. Overlays checked at tty level (for most part) and pane overlays checked at screen level where possible to keep separation of abstraction relatively clean. 2026-02-11 09:30:48 +00:00
Michael Grant
b46a96d454 Second try with static visual_ranges using calloc. 2026-01-29 14:34:18 +00:00
Michael Grant
f67cf7d053 Return static range as last resort. 2026-01-29 11:46:21 +00:00
Nicholas Marriott
2ad0f4a9e7 Merge branch 'master' into floating_panes 2026-01-29 08:43:41 +00:00
Michael Grant
1ec9ca497b Merge with visible_ranges banch. Convert visible_ranges to new style. 2026-01-28 15:26:00 +00:00
Michael Grant
dd5d0f4f0c Merge master. 2026-01-23 09:29:39 +00:00
Nicholas Marriott
d1a6ce8e7f Associate each visible_ranges with some other object (tty, popup_data, etc) so
it is easier to keep track of its lifecycle, but still avoid allocating for
each use.
2026-01-21 21:29:56 +00:00
Michael Grant
b22537e8a4 Conflict resolve. 2026-01-21 19:36:05 +00:00
Michael Grant
a25c14d472 Change overlay_ranges into visible_ranges. 2026-01-21 19:34:19 +00:00
Nicholas Marriott
7730d38339 Skip correct width when moving to next position. 2026-01-21 15:58:11 +00:00
Nicholas Marriott
58e498c9d3 Use right cell for empty check. 2026-01-21 15:35:33 +00:00
Nicholas Marriott
35485f2b5e Fix selection with tty_draw_line. 2026-01-21 12:23:46 +00:00
Nicholas Marriott
b108653f02 Change overlay_ranges to visible_ranges. 2026-01-20 21:18:33 +00:00
Nicholas Marriott
25f72cf240 Rewrite tty_draw_line to be simpler and not to check overlay ranges. 2026-01-20 21:18:20 +00:00
Thomas Adam
d1df8dcccc focus-follows-mouse: honour floating panes
Make sure the z-ordering of floating panes is honoured when
focus-follows-mouse is in use.
2026-01-09 19:00:17 +00:00
Michael Grant
a50d00aa47 Merge pull request #4799 from tmux/master
Sync from master
2026-01-09 11:30:16 +01:00
Michael Grant
34add944f2 Merge branch 'master' into feature-floating-window-panes 2026-01-07 23:41:29 +01:00
Michael Grant
95f85efc49 Fix some of the offset issues when windows size is greater than tty size. 2026-01-07 22:28:23 +00:00
Michael Grant
f2f6a05e2c Add cmd-minimise-pane.c 2026-01-06 16:44:38 +00:00
Michael Grant
bd442a27ee Fix some redraw issues. Fix scrollbar left/right display issues. 2025-12-24 10:45:17 +00:00
Michael Grant
73c0eb96c3 Merge branch 'master' into feature-floating-window-panes 2025-12-18 23:14:59 +01:00
Michael Grant
6a4a4a432b Add support to minimise panes, both tiled and floating. New PREFIX _ key binding to minimise a pane. New functions minimise-pane and unminimise-pane. Add double-click on pane in status to minimise pane. Single click on pane in status unminimises pane. 2025-12-18 18:19:17 +00:00
Michael Grant
263529e886 Cleanup from previous commit. 2025-12-09 20:11:44 +00:00
Michael Grant
818797ff08 Fix scrolling issue that collided with PR #4738 Fix y offset of mouse if status at top. 2025-12-09 17:47:36 +00:00
Michael Grant
d263dbe9dd Merge branch 'tmux:master' into feature-floating-window-panes 2025-12-09 13:35:31 +00:00
Michael Grant
67319ad9d9 Fix tty-clear-area and make aware of floating panes. 2025-12-09 13:34:26 +00:00
Michael Grant
2591df66cc Bugfix status line at top and floating panes. 2025-12-09 00:14:06 +00:00
Michael Grant
2ac78bccb5 Bugfix status line at top. 2025-12-08 15:01:54 +00:00
Michael Grant
47de44001e Merge changes from master. 2025-12-08 14:30:57 +00:00
Michael Grant
eaa467618b 1. Rework floating panes to have a stub layout_cell, 2. Add new <..> format to list-windows & select-layout for floating anes, 3. Fix zooming to work with floating panes, 4. Fix several display issues. 2025-12-08 14:28:17 +00:00
Michael Grant
ac01f15bfe Merge branch 'master' into feature-floating-window-panes 2025-12-06 21:14:38 +00:00
Michael Grant
5df5217e2e Merge branch 'tmux:master' into feature-floating-window-panes 2025-11-28 10:34:17 +00:00
Michael Grant
a6ec2b76c2 Merge branch 'tmux:master' into feature-floating-window-panes 2025-11-19 14:23:05 +00:00
Michael Grant
bc01b300c4 Merge branch 'tmux:master' into feature-floating-window-panes 2025-11-14 21:10:00 +00:00
Michael Grant
c8d4d8cb66 Bugfix fix some uninitialised variable warnings. 2025-11-14 11:02:12 +01:00
Michael Grant
315202b007 Merge branch 'tmux:master' into feature-floating-window-panes 2025-11-14 09:29:06 +00:00
Michael Grant
7194fdca38 Fix many display problems with floating windows up against the window edge and borders of other windows and many off-by-one errors. 2025-11-10 23:07:06 +01:00
Michael Grant
31d0945059 Fix dragging a window to y==0 when pane border status enabled. window_get_active_at() needs to return the pane at the top of the window when called with y==0, otherwise it returns null as if there is no pane at the top line. 2025-11-10 23:04:03 +01:00
Michael Grant
466e79d572 Bugfix for size of pane_border status not properly clipping. 2025-11-07 22:53:42 +01:00
Michael Grant
3bb4f72a4b Add new key binding ctrl-b * to create new floating pane. New panes created at increasing offsets. 2025-11-07 12:25:59 +01:00
Michael Grant
329e9d54ab Add support for clicking on a floating pane border to make it active. Including bugfix to click bottom border of floating panes. 2025-11-07 11:43:41 +01:00
Michael Grant
9c45dd693b Comment cleanup. 2025-11-07 01:24:39 +01:00
Michael Grant
cc671e4495 Fix mouse drag in copy-mode to properly select around floating panes. 2025-11-07 01:24:19 +01:00
Michael Grant
61ca158de1 Fix pane borders, including fixing 2 side-by-side or top-bottom panes with split coulering. 2025-11-06 12:16:45 +01:00
Michael Grant
7e0038c691 Bugfix split pane border colours. 2025-11-04 15:48:23 +01:00
Michael Grant
04792d0656 Bugfix, max margin should be right side of window. 2025-11-03 22:38:35 +01:00
Michael Grant
6c5cc40039 Bugfix, more changes to allow xoff and yoff to be int. 2025-11-03 21:56:15 +01:00
Michael Grant
6b462474f0 Bugfix so floating wp can be moved to top of window. 2025-11-02 11:52:21 +01:00
Michael Grant
9a061a2fee Bugfix floating panes display when off window edge. 2025-11-02 09:56:57 +01:00
Michael Grant
6dd552d689 Fix floating pane redraw bugs. Allow floating panes to be partly out of the window. This required changing xoff and yoff from u_int to int and it required a fair bit of casting for example when xoff is added to sx or comparing px to xoff. It makes sense for px and sx to be u_int since they refers to things which should never be negative. 2025-11-01 21:47:54 +01:00
Michael Grant
34e858ea05 Add support to drag and resize floating window panes. 2025-10-30 21:42:32 +01:00
Michael Grant
39d2839e37 Bugfix visible_ranges calculation, no longer need to inject vr into tty_draw_line. 2025-10-28 12:50:13 +00:00
Michael Grant
7980d00e8c Attempt to take care of case of partially obscured wide characters by floating panes. (Not yet tested!) 2025-10-28 11:01:27 +00:00
Michael Grant
25ce5b4281 Add separate z-index list. Each window has its own z-order list of panes now. 2025-10-28 08:07:32 +00:00
Michael Grant
0cd3ab6299 Fix active border colour if only floating panes in the window. 2025-10-27 23:35:53 +00:00
Michael Grant
28d038fb16 Bugfix calculation error on bottom border when pane border status on and scrollbar enabled. 2025-10-27 22:18:07 +00:00
Michael Grant
10dc308e6b Add checking the redraw of floating panes and the pane border status. 2025-10-27 21:28:31 +00:00
Michael Grant
6344bab6cc Fix typo with visible_ranges struct name. Add support for checking visual ranges to tty_draw_pane. 2025-10-27 19:54:21 +00:00
Michael Grant
984fbacccf Change the visible_ranges array to work more like the overlay_ranges array, except be able to grow. 2025-10-26 14:18:27 +00:00
Michael Grant
cc83ca6628 Add new newp command. 2025-10-26 14:02:57 +00:00
Michael Grant
04fc6ea11b Renamed floating window panes simple new-pane. 2025-10-24 14:24:26 +01:00
Michael Grant
8a9e2fccbd Move floating window stuff to new file: cmd-new-pane.c. 2025-10-24 10:25:18 +01:00
Michael Grant
cc1324e2d9 Cleanup and simplification. Array of visual ranges now just simple array. 2025-10-24 09:53:12 +01:00
Michael Grant
b2226d1608 Add scrollbar width to right border calculation. 2025-10-24 00:06:18 +01:00
Michael Grant
8ce0af3fc9 Fix a fencepost error (not sure why wp->sy+1 isn't the border). 2025-10-23 23:59:49 +01:00
Michael Grant
b315a6c3d1 Add mouse detection on top & left borders of floating panes. 2025-10-23 23:25:48 +01:00
Michael Grant
baf642b7d2 window_redraw_active_switch now brings floating pane to front. Fix compile warning in window_add_pane. 2025-10-23 20:30:32 +01:00
Michael Grant
8db76e9057 Bugfix fix redraw of overlapping floating panes. 2025-10-23 18:05:04 +01:00
Michael Grant
e3b7bf9b31 Add -x, -y, -w, -h args to set the xoff, yoff, sx, and sy of the pane. 2025-10-23 00:12:54 +01:00
Michael Grant
38724f2e86 Return error if you try to split a floating pane. 2025-10-22 23:37:58 +01:00
Michael Grant
65fc09fac9 Hide cursor behind floating panes. 2025-10-22 23:29:21 +01:00
Michael Grant
3197b715b3 Bugfix display of above-below windows with a floating pane. 2025-10-22 22:50:10 +01:00
Michael Grant
26362dfc72 Bugfix display of 2 side-by-side panes with overlapping floating pane. 2025-10-22 22:07:42 +01:00
Michael Grant
275781ce45 Merge branch 'feature-floating-window-panes' of github.com:mgrant0/tmux into feature-floating-window-panes 2025-10-21 09:17:13 +01:00
Michael Grant
72dbbfedce Manage visible ranges when drawing scrollbars. 2025-10-21 09:13:33 +01:00
Michael Grant
a6c6eabb5a Merge branch 'tmux:master' into feature-floating-window-panes 2025-10-21 08:14:38 +01:00
Michael Grant
379e4d976c Fix scrolling, redraw, and borders of floating panes. 2025-10-19 14:11:08 +01:00
Michael Grant
a6514e2656 Merge branch 'feature-floating-window-panes' of github.com:mgrant0/tmux into feature-floating-window-panes 2025-10-16 10:24:40 +01:00
Michael Grant
032af23e18 Merge branch 'tmux:master' into feature-floating-window-panes 2025-10-16 09:35:25 +01:00
Michael Grant
b5f9293014 Possible fixes leak 2025-10-13 08:45:32 +01:00
Michael Grant
a37db55689 Bugfix. 2025-10-12 10:38:47 +01:00
Michael Grant
1b959d0eff Cleanup before reworking collect_scroll. 2025-10-12 05:48:30 +01:00
Michael Grant
ce03f1abea Cleanup - screen_redraw_get_visible_ranges returns a value rather than pass addr of arg. Bugfix to redraw code. 2025-10-11 18:07:22 +01:00
Michael Grant
5faf41b695 Add support in screen-write.c to properly display cmd output when there are floating panes on the screen. 2025-10-10 23:59:47 +01:00
Michael Grant
7634daa834 Add function screen_redraw_get_visual_ranges to figure out what parts of floating panes obscure a target pane being redrawn. 2025-10-06 22:19:25 +01:00
Michael Grant
968f439672 Initial commit. Add new new-floating-window command to create panes without a layout_cell indicating they are floating panes. 2025-10-04 09:15:51 +01:00
37 changed files with 2337 additions and 1417 deletions

View File

@@ -113,7 +113,9 @@ 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 \
@@ -232,18 +234,8 @@ nodist_tmux_SOURCES += compat/utf8proc.c
endif
# Enable sixel support.
if ENABLE_SIXEL_IMAGES
if ENABLE_SIXEL
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,6 +63,7 @@ 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);
@@ -94,8 +95,9 @@ 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,
args_has(args, 'e'));
tty_oy, 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)
return (CMD_RETURN_NORMAL);
goto out;
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 + wp->sx <= ctx->ox ||
wp->xoff >= ctx->ox + ctx->sx ||
wp->yoff + wp->sy <= ctx->oy ||
wp->yoff >= ctx->oy + ctx->sy)
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)
return;
if (wp->xoff >= ctx->ox && wp->xoff + wp->sx <= ctx->ox + ctx->sx) {

View File

@@ -978,15 +978,20 @@ 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;
@@ -1025,7 +1030,7 @@ cmd_find_target(struct cmd_find_state *fs, struct cmdq_item *item,
fs->w = fs->wl->window;
fs->wp = fs->w->active;
}
break;
goto found;
}
if (fs->wp == NULL) {
if (~flags & CMD_FIND_QUIET)

View File

@@ -62,6 +62,11 @@ 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);
}

192
cmd-minimise-pane.c Normal file
View File

@@ -0,0 +1,192 @@
/* $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);
}

245
cmd-new-pane.c Normal file
View File

@@ -0,0 +1,245 @@
/* $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,7 +29,9 @@
static enum cmd_retval cmd_resize_pane_exec(struct cmd *, struct cmdq_item *);
static void cmd_resize_pane_mouse_update(struct client *,
static void cmd_resize_pane_mouse_update_floating(struct client *,
struct mouse_event *);
static void cmd_resize_pane_mouse_update_tiled(struct client *,
struct mouse_event *);
const struct cmd_entry cmd_resize_pane_entry = {
@@ -80,8 +82,15 @@ 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);
c->tty.mouse_drag_update = cmd_resize_pane_mouse_update;
cmd_resize_pane_mouse_update(c, &event->m);
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);
}
return (CMD_RETURN_NORMAL);
}
@@ -149,7 +158,136 @@ cmd_resize_pane_exec(struct cmd *self, struct cmdq_item *item)
}
static void
cmd_resize_pane_mouse_update(struct client *c, struct mouse_event *m)
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)
{
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->layout_root);
w->old_layout = layout_dump(w, w->layout_root);
if (next || previous) {
if (next)

View File

@@ -160,6 +160,10 @@ 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,6 +71,11 @@ 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,8 +69,10 @@ 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;
@@ -116,6 +118,7 @@ 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[] = {
@@ -161,8 +164,10 @@ 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,
@@ -208,6 +213,7 @@ 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
};
@@ -772,9 +778,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 (x < wp->xoff || x >= wp->xoff + wp->sx)
if ((int)x < wp->xoff || (int)x >= wp->xoff + (int)wp->sx)
return (-1);
if (y < wp->yoff || y >= wp->yoff + wp->sy)
if ((int)y < wp->yoff || (int)y >= wp->yoff + (int)wp->sy)
return (-1);
if (xp != NULL)

View File

@@ -462,33 +462,14 @@ if test "x$enable_cgroups" = xyes; then
fi
# Enable sixel support.
AC_ARG_ENABLE(
sixel-images,
AS_HELP_STRING(--enable-sixel-images, enable sixel images)
)
AC_ARG_ENABLE(
sixel,
AS_HELP_STRING(--enable-sixel support),
[
enable_sixel_images="$enableval"
]
AS_HELP_STRING(--enable-sixel, enable sixel images)
)
if test "x$enable_sixel_images" = xyes; then
AC_DEFINE(ENABLE_SIXEL_IMAGES)
if test "x$enable_sixel" = xyes; then
AC_DEFINE(ENABLE_SIXEL)
fi
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])
AM_CONDITIONAL(ENABLE_SIXEL, [test "x$enable_sixel" = 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->saved_layout_root));
return (layout_dump(w->layout_root));
return (layout_dump(w, w->saved_layout_root));
return (layout_dump(w, 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->layout_root));
return (layout_dump(w, w->layout_root));
}
/* Callback for pane_start_command. */
@@ -1004,6 +1004,20 @@ 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)
@@ -2562,16 +2576,12 @@ format_cb_version(__unused struct format_tree *ft)
/* Callback for sixel_support. */
static void *
format_cb_image_support(__unused struct format_tree *ft)
format_cb_sixel_support(__unused struct format_tree *ft)
{
#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"));
#ifdef ENABLE_SIXEL
return (xstrdup("1"));
#else
return (NULL);
return (xstrdup("0"));
#endif
}
@@ -3194,9 +3204,6 @@ 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
},
@@ -3302,6 +3309,9 @@ 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
},
@@ -3473,6 +3483,9 @@ 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, old_cellsize;
u_int xx;
gl = &gd->linedata[py];
if (sx <= gl->cellsize)
@@ -508,10 +508,13 @@ grid_expand_line(struct grid *gd, u_int py, u_int sx, u_int bg)
else if (gd->sx > sx)
sx = gd->sx;
old_cellsize = gl->cellsize;
gl->celldata = xrecallocarray(gl->celldata, old_cellsize, sx,
gl->celldata = xreallocarray(gl->celldata, sx,
sizeof *gl->celldata);
for (xx = old_cellsize; xx < sx; xx++)
if (gl->cellsize < sx) {
memset(gl->celldata + gl->cellsize, 0,
(sx - gl->cellsize) * sizeof *gl->celldata);
}
for (xx = gl->cellsize; xx < sx; xx++)
grid_clear_cell(gd, xx, py, bg);
gl->cellsize = sx;
}

View File

@@ -1,325 +0,0 @@
/* $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,22 +61,7 @@ image_free(struct image *im)
all_images_count--;
TAILQ_REMOVE(&s->images, im, entry);
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;
}
sixel_free(im->data);
free(im->fallback);
free(im);
}
@@ -96,30 +81,13 @@ image_free_all(struct screen *s)
/* Create text placeholder for an image. */
static void
image_fallback(char **ret, enum image_type type, u_int sx, u_int sy)
image_fallback(char **ret, 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, "%s IMAGE (%ux%u)\r\n", type_name, sx, sy) + 1;
lsize = xasprintf(&label, "SIXEL IMAGE (%ux%u)\r\n", sx, sy) + 1;
if (sx < lsize - 3)
size = lsize - 1;
else
@@ -154,36 +122,19 @@ image_fallback(char **ret, enum image_type type, u_int sx, u_int sy)
}
struct image*
image_store(struct screen *s, enum image_type type, void *data)
image_store(struct screen *s, struct sixel_image *si)
{
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);
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_fallback(&im->fallback, im->sx, im->sy);
image_log(im, __func__, NULL);
TAILQ_INSERT_TAIL(&s->images, im, entry);
@@ -237,10 +188,8 @@ image_scroll_up(struct screen *s, u_int lines)
{
struct image *im, *im1;
int redraw = 0;
#ifdef ENABLE_SIXEL_IMAGES
struct sixel_image *new;
u_int sx, sy;
#endif
struct sixel_image *new;
TAILQ_FOREACH_SAFE(im, &s->images, entry, im1) {
if (im->py >= lines) {
@@ -255,43 +204,20 @@ 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);
/* 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);
new = sixel_scale(im->data, 0, 0, 0, im->sy - sy, sx, sy, 1);
sixel_free(im->data);
im->data = new;
new = sixel_scale(im->data.sixel, 0, 0, 0, im->sy - sy,
sx, sy, 1);
sixel_free(im->data.sixel);
im->data.sixel = new;
im->py = 0;
sixel_size_in_cells(im->data, &im->sx, &im->sy);
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;
}
free(im->fallback);
image_fallback(&im->fallback, im->sx, im->sy);
redraw = 1;
}
return (redraw);
}

101
input.c
View File

@@ -145,7 +145,6 @@ struct input_ctx {
*/
struct evbuffer *since_ground;
struct event ground_timer;
};
/* Helper functions. */
@@ -181,7 +180,6 @@ 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 *);
@@ -1383,10 +1381,6 @@ 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);
@@ -1562,10 +1556,11 @@ input_csi_dispatch(struct input_ctx *ictx)
case -1:
break;
case 0:
if (input_da1_has_sixel(ictx))
input_reply(ictx, 1, "\033[?1;2;4c");
else
input_reply(ictx, 1, "\033[?1;2c");
#ifdef ENABLE_SIXEL
input_reply(ictx, 1, "\033[?1;2;4c");
#else
input_reply(ictx, 1, "\033[?1;2c");
#endif
break;
default:
log_debug("%s: unknown '%c'", __func__, ictx->ch);
@@ -2035,7 +2030,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_IMAGES
#ifdef ENABLE_SIXEL
int n, m, o;
if (ictx->param_list_len > 3)
@@ -2560,7 +2555,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_IMAGES
#ifdef ENABLE_SIXEL
struct window *w;
struct sixel_image *si;
int p2;
@@ -2575,7 +2570,7 @@ input_dcs_dispatch(struct input_ctx *ictx)
return (0);
}
#ifdef ENABLE_SIXEL_IMAGES
#ifdef ENABLE_SIXEL
w = wp->window;
if (buf[0] == 'q' && ictx->interm_len == 0) {
if (input_split(ictx) != 0)
@@ -2721,79 +2716,6 @@ 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)
@@ -2805,13 +2727,6 @@ 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,6 +350,10 @@ 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 }",
@@ -361,6 +365,7 @@ 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 -- '%%' } }",
@@ -459,6 +464,9 @@ 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,10 +27,11 @@ 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 struct layout_cell *layout_construct(struct layout_cell *,
const char **);
static int layout_construct(struct layout_cell *,
const char **, struct layout_cell **,
struct layout_cell **);
static void layout_assign(struct window_pane **,
struct layout_cell *);
struct layout_cell *, int);
/* Find the bottom-right cell. */
static struct layout_cell *
@@ -58,14 +59,33 @@ layout_checksum(const char *layout)
/* Dump layout as a string. */
char *
layout_dump(struct layout_cell *root)
layout_dump(struct window *w, struct layout_cell *root)
{
char layout[8192], *out;
char layout[8192], *out;
int braket;
struct window_pane *wp;
*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);
}
@@ -81,7 +101,8 @@ 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);
@@ -109,6 +130,7 @@ 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;
}
@@ -125,6 +147,7 @@ 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) {
@@ -156,7 +179,7 @@ layout_check(struct layout_cell *lc)
int
layout_parse(struct window *w, const char *layout, char **cause)
{
struct layout_cell *lc, *lcchild;
struct layout_cell *lcchild, *tiled_lc = NULL, *floating_lc = NULL;
struct window_pane *wp;
u_int npanes, ncells, sx = 0, sy = 0;
u_short csum;
@@ -173,11 +196,16 @@ layout_parse(struct window *w, const char *layout, char **cause)
}
/* Build the layout. */
lc = layout_construct(NULL, &layout);
if (lc == NULL) {
if (layout_construct(NULL, &layout, &tiled_lc, &floating_lc) != 0) {
*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;
@@ -186,8 +214,10 @@ 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(lc);
ncells = layout_count_cells(tiled_lc);
ncells += layout_count_cells(floating_lc);
if (npanes > ncells) {
/* Modify this to open a new pane */
xasprintf(cause, "have %u panes but need %u", npanes,
ncells);
goto fail;
@@ -195,9 +225,17 @@ layout_parse(struct window *w, const char *layout, char **cause)
if (npanes == ncells)
break;
/* Fewer panes than cells - close the bottom right. */
lcchild = layout_find_bottomright(lc);
layout_destroy_cell(w, lcchild, &lc);
/*
* 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);
}
}
/*
@@ -205,85 +243,112 @@ 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 (lc->type) {
switch (tiled_lc->type) {
case LAYOUT_WINDOWPANE:
break;
case LAYOUT_LEFTRIGHT:
TAILQ_FOREACH(lcchild, &lc->cells, entry) {
TAILQ_FOREACH(lcchild, &tiled_lc->cells, entry) {
sy = lcchild->sy + 1;
sx += lcchild->sx + 1;
}
break;
case LAYOUT_TOPBOTTOM:
TAILQ_FOREACH(lcchild, &lc->cells, entry) {
TAILQ_FOREACH(lcchild, &tiled_lc->cells, entry) {
sx = lcchild->sx + 1;
sy += lcchild->sy + 1;
}
break;
case LAYOUT_FLOATING:
*cause = xstrdup("invalid layout");
goto fail;
}
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;
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;
}
/* Check the new layout. */
if (!layout_check(lc)) {
if (!layout_check(tiled_lc)) {
*cause = xstrdup("size mismatch after applying layout");
goto fail;
}
/* Resize to the layout size. */
window_resize(w, lc->sx, lc->sy, -1, -1);
/* Resize window to the layout size. */
if (sx != 0 && sy != 0)
window_resize(w, tiled_lc->sx, tiled_lc->sy, -1, -1);
/* Destroy the old layout and swap to the new. */
layout_free_cell(w->layout_root);
w->layout_root = lc;
w->layout_root = tiled_lc;
/* Assign the panes into the cells. */
wp = TAILQ_FIRST(&w->panes);
layout_assign(&wp, lc);
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);
/* Update pane offsets and sizes. */
layout_fix_offsets(w);
layout_fix_panes(w, NULL);
recalculate_sizes();
layout_print_cell(lc, __func__, 0);
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);
notify_window("window-layout-changed", w);
return (0);
fail:
layout_free_cell(lc);
layout_free_cell(tiled_lc);
layout_free_cell(floating_lc);
return (-1);
}
/* Assign panes into cells. */
static void
layout_assign(struct window_pane **wp, struct layout_cell *lc)
layout_assign(struct window_pane **wp, struct layout_cell *lc, int floating)
{
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);
layout_assign(wp, lcchild, 1);
return;
}
}
/* Construct a cell from all or part of a layout tree. */
static struct layout_cell *
layout_construct(struct layout_cell *lcparent, const char **layout)
layout_construct_cell(struct layout_cell *lcparent, const char **layout)
{
struct layout_cell *lc, *lcchild;
struct layout_cell *lc;
u_int sx, sy, xoff, yoff;
const char *saved;
@@ -324,17 +389,42 @@ layout_construct(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 (lc);
return (0);
case '{':
lc->type = LAYOUT_LEFTRIGHT;
(*lc)->type = LAYOUT_LEFTRIGHT;
break;
case '[':
lc->type = LAYOUT_TOPBOTTOM;
(*lc)->type = LAYOUT_TOPBOTTOM;
break;
case '<':
saved_lc = *lc;
*lc = layout_create_cell(lcparent);
(*lc)->type = LAYOUT_FLOATING;
break;
default:
goto fail;
@@ -342,13 +432,12 @@ layout_construct(struct layout_cell *lcparent, const char **layout)
do {
(*layout)++;
lcchild = layout_construct(lc, layout);
if (lcchild == NULL)
if (layout_construct(*lc, layout, &lcchild, floating_lc) != 0)
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;
@@ -357,14 +446,21 @@ 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 (lc);
return (0);
fail:
layout_free_cell(lc);
return (NULL);
layout_free_cell(*lc);
layout_free_cell(*floating_lc);
return (-1);
}

175
layout.c
View File

@@ -83,9 +83,24 @@ 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)
if (lc->wp != NULL) {
lc->wp->layout_cell->parent = NULL;
lc->wp->layout_cell = NULL;
}
break;
}
@@ -98,6 +113,9 @@ 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";
@@ -105,6 +123,9 @@ 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;
@@ -118,6 +139,7 @@ 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;
@@ -153,6 +175,7 @@ layout_search_by_border(struct layout_cell *lc, u_int x, u_int y)
return (last);
break;
case LAYOUT_WINDOWPANE:
case LAYOUT_FLOATING:
break;
}
@@ -198,6 +221,29 @@ 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)
@@ -208,6 +254,9 @@ 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)
@@ -217,6 +266,8 @@ 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)
@@ -307,7 +358,8 @@ layout_fix_panes(struct window *w, struct window_pane *skip)
sx = lc->sx;
sy = lc->sy;
if (layout_add_horizontal_border(w, lc, status)) {
if (~wp->flags & PANE_FLOATING &&
layout_add_horizontal_border(w, lc, status)) {
if (status == PANE_STATUS_TOP)
wp->yoff++;
sy--;
@@ -353,6 +405,7 @@ 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);
@@ -462,7 +515,7 @@ layout_resize_adjust(struct window *w, struct layout_cell *lc,
}
}
/* Destroy a cell and redistribute the space. */
/* Destroy a cell and redistribute the space in tiled cells. */
void
layout_destroy_cell(struct window *w, struct layout_cell *lc,
struct layout_cell **lcroot)
@@ -470,13 +523,66 @@ layout_destroy_cell(struct window *w, struct layout_cell *lc,
struct layout_cell *lcother, *lcparent;
/*
* If no parent, this is the last pane so window close is imminent and
* there is no need to resize anything.
* 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.
*/
lcparent = lc->parent;
if (lcparent == NULL) {
if (lc->wp != NULL && ~lc->wp->flags & PANE_FLOATING)
*lcroot = NULL;
layout_free_cell(lc);
*lcroot = NULL;
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) {
return;
}
@@ -490,26 +596,44 @@ layout_destroy_cell(struct window *w, struct layout_cell *lc,
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 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);
}
}
/*
* 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);
/* 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;
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);
lcparent = lc->parent;
if (lcparent == NULL) {
return;
}
layout_free_cell(lcparent);
/* 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));
}
}
@@ -741,7 +865,7 @@ layout_resize_pane_shrink(struct window *w, struct layout_cell *lc,
return (size);
}
/* Assign window pane to newly split cell. */
/* Assign window pane to new cell. */
void
layout_assign_pane(struct layout_cell *lc, struct window_pane *wp,
int do_not_resize)
@@ -1085,6 +1209,9 @@ 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,6 +36,7 @@ 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;
@@ -574,6 +575,8 @@ 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;
@@ -582,17 +585,24 @@ screen_write_fast_copy(struct screen_write_ctx *ctx, struct screen *src,
if (yy >= gd->hsize + gd->sy)
break;
s->cx = cx;
if (wp != NULL)
screen_write_initctx(ctx, &ttyctx, 0);
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);
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++;
@@ -1072,7 +1082,7 @@ screen_write_alignmenttest(struct screen_write_ctx *ctx)
memcpy(&gc, &grid_default_cell, sizeof gc);
utf8_set(&gc.data, 'E');
#ifdef ENABLE_SIXEL_IMAGES
#ifdef ENABLE_SIXEL
if (image_free_all(s) && ctx->wp != NULL)
ctx->wp->flags |= PANE_REDRAW;
#endif
@@ -1111,7 +1121,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_IMAGES
#ifdef ENABLE_SIXEL
if (image_check_line(s, s->cy, 1) && ctx->wp != NULL)
ctx->wp->flags |= PANE_REDRAW;
#endif
@@ -1144,7 +1154,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_IMAGES
#ifdef ENABLE_SIXEL
if (image_check_line(s, s->cy, 1) && ctx->wp != NULL)
ctx->wp->flags |= PANE_REDRAW;
#endif
@@ -1177,7 +1187,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_IMAGES
#ifdef ENABLE_SIXEL
if (image_check_line(s, s->cy, 1) && ctx->wp != NULL)
ctx->wp->flags |= PANE_REDRAW;
#endif
@@ -1200,14 +1210,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_IMAGES
#ifdef ENABLE_SIXEL
u_int sy = screen_size_y(s);
#endif
if (ny == 0)
ny = 1;
#ifdef ENABLE_SIXEL_IMAGES
#ifdef ENABLE_SIXEL
if (image_check_line(s, s->cy, sy - s->cy) && ctx->wp != NULL)
ctx->wp->flags |= PANE_REDRAW;
#endif
@@ -1260,7 +1270,7 @@ screen_write_deleteline(struct screen_write_ctx *ctx, u_int ny, u_int bg)
if (ny == 0)
ny = 1;
#ifdef ENABLE_SIXEL_IMAGES
#ifdef ENABLE_SIXEL
if (image_check_line(s, s->cy, sy - s->cy) && ctx->wp != NULL)
ctx->wp->flags |= PANE_REDRAW;
#endif
@@ -1313,7 +1323,7 @@ screen_write_clearline(struct screen_write_ctx *ctx, u_int bg)
if (gl->cellsize == 0 && COLOUR_DEFAULT(bg))
return;
#ifdef ENABLE_SIXEL_IMAGES
#ifdef ENABLE_SIXEL
if (image_check_line(s, s->cy, 1) && ctx->wp != NULL)
ctx->wp->flags |= PANE_REDRAW;
#endif
@@ -1347,7 +1357,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_IMAGES
#ifdef ENABLE_SIXEL
if (image_check_line(s, s->cy, 1) && ctx->wp != NULL)
ctx->wp->flags |= PANE_REDRAW;
#endif
@@ -1374,7 +1384,7 @@ screen_write_clearstartofline(struct screen_write_ctx *ctx, u_int bg)
return;
}
#ifdef ENABLE_SIXEL_IMAGES
#ifdef ENABLE_SIXEL
if (image_check_line(s, s->cy, 1) && ctx->wp != NULL)
ctx->wp->flags |= PANE_REDRAW;
#endif
@@ -1391,6 +1401,23 @@ 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,
@@ -1422,7 +1449,7 @@ screen_write_reverseindex(struct screen_write_ctx *ctx, u_int bg)
struct tty_ctx ttyctx;
if (s->cy == s->rupper) {
#ifdef ENABLE_SIXEL_IMAGES
#ifdef ENABLE_SIXEL
if (image_free_all(s) && ctx->wp != NULL)
ctx->wp->flags |= PANE_REDRAW;
#endif
@@ -1469,7 +1496,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_IMAGES
#ifdef ENABLE_SIXEL
int redraw = 0;
#endif
u_int rupper = s->rupper, rlower = s->rlower;
@@ -1487,7 +1514,7 @@ screen_write_linefeed(struct screen_write_ctx *ctx, int wrapped, u_int bg)
}
if (s->cy == s->rlower) {
#ifdef ENABLE_SIXEL_IMAGES
#ifdef ENABLE_SIXEL
if (rlower == screen_size_y(s) - 1)
redraw = image_scroll_up(s, 1);
else
@@ -1520,7 +1547,7 @@ screen_write_scrollup(struct screen_write_ctx *ctx, u_int lines, u_int bg)
ctx->bg = bg;
}
#ifdef ENABLE_SIXEL_IMAGES
#ifdef ENABLE_SIXEL
if (image_scroll_up(s, lines) && ctx->wp != NULL)
ctx->wp->flags |= PANE_REDRAW;
#endif
@@ -1549,7 +1576,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_IMAGES
#ifdef ENABLE_SIXEL
if (image_free_all(s) && ctx->wp != NULL)
ctx->wp->flags |= PANE_REDRAW;
#endif
@@ -1573,12 +1600,15 @@ 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);
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;
#ifdef ENABLE_SIXEL_IMAGES
#ifdef ENABLE_SIXEL
if (image_check_line(s, s->cy, sy - s->cy) && ctx->wp != NULL)
ctx->wp->flags |= PANE_REDRAW;
#endif
@@ -1601,18 +1631,66 @@ 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__);
tty_write(tty_cmd_clearendofscreen, &ttyctx);
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);
}
/* 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);
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;
#ifdef ENABLE_SIXEL_IMAGES
#ifdef ENABLE_SIXEL
if (image_check_line(s, 0, s->cy - 1) && ctx->wp != NULL)
ctx->wp->flags |= PANE_REDRAW;
#endif
@@ -1629,18 +1707,64 @@ 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__);
tty_write(tty_cmd_clearstartofscreen, &ttyctx);
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);
}
/* 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);
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;
#ifdef ENABLE_SIXEL_IMAGES
#ifdef ENABLE_SIXEL
if (image_free_all(s) && ctx->wp != NULL)
ctx->wp->flags |= PANE_REDRAW;
#endif
@@ -1657,7 +1781,38 @@ 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);
tty_write(tty_cmd_clearscreen, &ttyctx);
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);
}
/* Clear entire history. */
@@ -1809,6 +1964,39 @@ 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,
@@ -1817,8 +2005,14 @@ 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;
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;
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++) {
@@ -1838,11 +2032,14 @@ 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 (ctx->wp != NULL)
if (wp != NULL)
ctx->wp->flags |= PANE_REDRAWSCROLLBAR;
}
ctx->scrolled = 0;
@@ -1852,8 +2049,29 @@ 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",
@@ -1862,27 +2080,57 @@ screen_write_collect_flush(struct screen_write_ctx *ctx, int scroll_only,
fatalx("collect list not in order: %u <= %u",
ci->x, last);
}
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++;
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;
TAILQ_REMOVE(&cl->items, ci, entry);
screen_write_free_citem(ci);
last = ci->x;
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;
}
}
}
s->cx = cx; s->cy = cy;
log_debug("%s: flushed %u items (%s)", __func__, items, from);
@@ -1957,7 +2205,7 @@ screen_write_collect_end(struct screen_write_ctx *ctx)
}
}
#ifdef ENABLE_SIXEL_IMAGES
#ifdef ENABLE_SIXEL
if (image_check_area(s, s->cx, s->cy, ci->used, 1) && ctx->wp != NULL)
ctx->wp->flags |= PANE_REDRAW;
#endif
@@ -2047,6 +2295,7 @@ 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;
@@ -2054,8 +2303,10 @@ 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;
int selected, skip = 1, redraw = 0;
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;
/* Ignore padding cells. */
if (gc->flags & GRID_FLAG_PADDING)
@@ -2153,6 +2404,11 @@ 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.
@@ -2174,11 +2430,30 @@ 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
ttyctx.cell = gc;
memcpy(&tmp_gc, gc, sizeof tmp_gc);
ttyctx.cell = &tmp_gc;
ttyctx.num = redraw ? 2 : 0;
tty_write(tty_cmd_cell, &ttyctx);
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);
}
}
}
@@ -2187,12 +2462,14 @@ 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 n, cx = s->cx, cy = s->cy;
u_int i, n, cx = s->cx, cy = s->cy, vis, yoff = 0;
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))
@@ -2280,6 +2557,23 @@ 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
@@ -2398,7 +2692,7 @@ screen_write_rawstring(struct screen_write_ctx *ctx, u_char *str, u_int len,
tty_write(tty_cmd_rawstring, &ttyctx);
}
#ifdef ENABLE_SIXEL_IMAGES
#ifdef ENABLE_SIXEL
/* Write a SIXEL image. */
void
screen_write_sixelimage(struct screen_write_ctx *ctx, struct sixel_image *si,
@@ -2450,7 +2744,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, IMAGE_SIXEL, si);
ttyctx.ptr = image_store(s, si);
tty_write(tty_cmd_sixelimage, &ttyctx);
@@ -2458,38 +2752,6 @@ 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_IMAGES
#ifdef ENABLE_SIXEL
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_IMAGES
#ifdef ENABLE_SIXEL
image_free_all(s);
#endif
@@ -164,7 +164,7 @@ screen_free(struct screen *s)
hyperlinks_free(s->hyperlinks);
screen_free_titles(s);
#ifdef ENABLE_IMAGES
#ifdef ENABLE_SIXEL
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_IMAGES
#ifdef ENABLE_SIXEL
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_IMAGES
#ifdef ENABLE_SIXEL
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_IMAGES
#ifdef ENABLE_SIXEL
image_free_all(s);
TAILQ_CONCAT(&s->images, &s->saved_images, entry);
#endif

View File

@@ -617,7 +617,8 @@ 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 line, sl_top, sl_bottom;
u_int pane_status_line, sl_top, sl_bottom;
u_int bdr_bottom, bdr_top, bdr_left, bdr_right;
sb = options_get_number(wo, "pane-scrollbars");
sb_pos = options_get_number(wo, "pane-scrollbars-position");
@@ -630,16 +631,19 @@ 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)
line = wp->yoff - 1;
pane_status_line = wp->yoff - 1;
else if (pane_status == PANE_STATUS_BOTTOM)
line = wp->yoff + wp->sy;
pane_status_line = wp->yoff + wp->sy;
else
pane_status_line = -1; /* not used */
/* Check if point is within the pane or scrollbar. */
if (((pane_status != PANE_STATUS_OFF &&
py != line && py != wp->yoff + wp->sy) ||
py != pane_status_line && py != wp->yoff + wp->sy) ||
(wp->yoff == 0 && py < wp->sy) ||
(py >= wp->yoff && py < wp->yoff + wp->sy)) &&
((int)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 &&
@@ -649,8 +653,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 &&
(px >= wp->xoff - sb_pad - sb_w &&
px < wp->xoff - sb_pad))) {
((int)px >= wp->xoff - sb_pad - sb_w &&
(int)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 +
@@ -663,6 +667,12 @@ 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);
@@ -670,16 +680,32 @@ 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_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 (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 (fwp != NULL)
return (BORDER);
@@ -849,9 +875,11 @@ have_event:
* scrollbar.
*/
if (where == NOWHERE) {
if (c->tty.mouse_scrolling_flag)
if (c->tty.mouse_scrolling_flag) {
where = SCROLLBAR_SLIDER;
else {
m->wp = c->tty.mouse_wp->id;
m->w = c->tty.mouse_wp->window->id;
} else {
px = x;
if (m->statusat == 0 && y >= m->statuslines)
py = y - m->statuslines;
@@ -868,8 +896,13 @@ have_event:
px = px + m->ox;
py = py + m->oy;
/* Try inside the pane. */
wp = window_get_active_at(w, px, py);
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);
if (wp == NULL)
return (KEYC_UNKNOWN);
where = server_client_check_mouse_in_pane(wp, px, py,
@@ -1086,6 +1119,7 @@ have_event:
break;
}
c->tty.mouse_drag_flag = 0;
c->tty.mouse_wp = NULL;
c->tty.mouse_slider_mpos = -1;
goto out;
}
@@ -1101,6 +1135,7 @@ 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);
@@ -1312,6 +1347,11 @@ 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;
@@ -2980,7 +3020,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 (c->prompt_string == NULL)
} else if (wp != NULL && c->prompt_string == NULL)
s = wp->screen;
else
s = c->status.active;
@@ -3008,7 +3048,7 @@ server_client_reset_state(struct client *c)
cy = tty->sy - 1;
}
cx = c->prompt_cursor;
} else if (c->overlay_draw == NULL) {
} else if (wp != NULL && 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 &&
@@ -3021,9 +3061,15 @@ 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);
@@ -3708,7 +3754,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,6 +276,13 @@ 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,7 +6266,6 @@ 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"
@@ -6311,6 +6310,7 @@ 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,6 +6381,7 @@ 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,19 +68,10 @@ struct screen_write_cline;
struct screen_write_ctx;
struct session;
/* 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
#ifdef ENABLE_SIXEL
struct sixel_image;
#endif
#ifdef ENABLE_KITTY_IMAGES
struct kitty_image;
#endif
struct tty_ctx;
struct tty_code;
struct tty_key;
@@ -934,25 +925,11 @@ struct style {
enum style_default_type default_type;
};
#ifdef ENABLE_IMAGES
/* Image types. */
enum image_type {
IMAGE_SIXEL,
IMAGE_KITTY
};
#ifdef ENABLE_SIXEL
/* Image. */
struct image {
enum image_type type;
struct screen *s;
union {
#ifdef ENABLE_SIXEL_IMAGES
struct sixel_image *sixel;
#endif
#ifdef ENABLE_KITTY_IMAGES
struct kitty_image *kitty;
#endif
} data;
struct sixel_image *data;
char *fallback;
u_int px;
@@ -1007,7 +984,7 @@ struct screen {
bitstr_t *tabs;
struct screen_sel *sel;
#ifdef ENABLE_IMAGES
#ifdef ENABLE_SIXEL
struct images images;
struct images saved_images;
#endif
@@ -1086,8 +1063,8 @@ struct screen_redraw_ctx {
u_int sx;
u_int sy;
u_int ox;
u_int oy;
int ox;
int oy;
};
/* Screen size. */
@@ -1202,6 +1179,19 @@ 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;
@@ -1216,10 +1206,11 @@ struct window_pane {
u_int sx;
u_int sy;
u_int xoff;
u_int yoff;
int xoff;
int yoff;
int flags;
int saved_flags;
#define PANE_REDRAW 0x1
#define PANE_DROP 0x2
#define PANE_FOCUSED 0x4
@@ -1236,6 +1227,8 @@ 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;
@@ -1291,11 +1284,15 @@ 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. */
@@ -1315,6 +1312,7 @@ struct window {
struct window_pane *active;
struct window_panes last_panes;
struct window_panes z_index;
struct window_panes panes;
int lastlayout;
@@ -1408,6 +1406,7 @@ TAILQ_HEAD(winlink_stack, winlink);
enum layout_type {
LAYOUT_LEFTRIGHT,
LAYOUT_TOPBOTTOM,
LAYOUT_FLOATING,
LAYOUT_WINDOWPANE
};
@@ -1559,19 +1558,6 @@ 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;
@@ -1589,7 +1575,6 @@ 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;
@@ -1672,7 +1657,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 *,
@@ -1718,10 +1703,10 @@ struct tty_ctx {
u_int orlower;
/* Target region (usually pane) offset and size. */
u_int xoff;
u_int yoff;
u_int rxoff;
u_int ryoff;
int xoff;
int yoff;
int rxoff;
int ryoff;
u_int sx;
u_int sy;
@@ -1738,6 +1723,9 @@ 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. */
@@ -2289,6 +2277,7 @@ struct spawn_context {
#define SPAWN_FULLSIZE 0x20
#define SPAWN_EMPTY 0x40
#define SPAWN_ZOOM 0x80
#define SPAWN_FLOATING 0x100
};
/* Paste buffer. */
@@ -2644,7 +2633,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_IMAGES
#ifdef ENABLE_SIXEL
void tty_draw_images(struct client *, struct window_pane *, struct screen *);
#endif
@@ -2678,22 +2667,10 @@ 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_IMAGES
int tty_set_client_cb(struct tty_ctx *, struct client *);
#endif
#ifdef ENABLE_SIXEL_IMAGES
#ifdef ENABLE_SIXEL
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 *);
@@ -3296,15 +3273,10 @@ 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_IMAGES
#ifdef ENABLE_SIXEL
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 *,
@@ -3313,6 +3285,10 @@ 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);
@@ -3375,6 +3351,8 @@ 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 *,
@@ -3401,6 +3379,7 @@ 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 *);
@@ -3454,6 +3433,8 @@ 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);
@@ -3461,6 +3442,7 @@ 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 *,
@@ -3481,7 +3463,7 @@ int layout_spread_cell(struct window *, struct layout_cell *);
void layout_spread_out(struct window_pane *);
/* layout-custom.c */
char *layout_dump(struct layout_cell *);
char *layout_dump(struct window *, struct layout_cell *);
int layout_parse(struct window *, const char *, char **);
/* layout-set.c */
@@ -3555,7 +3537,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, int);
void window_copy_scroll(struct window_pane *, int, u_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 *);
@@ -3768,16 +3750,14 @@ struct window_pane *spawn_pane(struct spawn_context *, char **);
/* regsub.c */
char *regsub(const char *, const char *, const char *, int);
#ifdef ENABLE_IMAGES
#ifdef ENABLE_SIXEL
/* image.c */
int image_free_all(struct screen *);
struct image *image_store(struct screen *, enum image_type, void *);
struct image *image_store(struct screen *, struct sixel_image *);
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);
@@ -3791,18 +3771,6 @@ 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);

Binary file not shown.

Before

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,17 +357,6 @@ 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,
@@ -379,7 +368,6 @@ 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,
@@ -512,19 +500,7 @@ 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,10 +60,6 @@ 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 {
@@ -763,19 +759,6 @@ 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 */
@@ -1657,14 +1640,6 @@ 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);
@@ -1820,74 +1795,3 @@ 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,9 +461,6 @@ 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_IMAGES
#ifdef ENABLE_SIXEL
static void tty_write_one(void (*)(struct tty *, const struct tty_ctx *),
struct client *, struct tty_ctx *);
#endif
@@ -391,23 +391,6 @@ 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)
@@ -1020,7 +1003,10 @@ 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
*oy = cy - *sy / 2;
/* 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; */
}
c->pan_window = NULL;
@@ -1140,23 +1126,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)
{
u_int xoff = ctx->rxoff + px;
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 >= ctx->wox && xoff + nx <= ctx->wox + ctx->wsx) {
if (xoff >= (int)ctx->wox && xoff + nx <= ctx->wox + ctx->wsx) {
/* All visible. */
*i = 0;
*x = ctx->xoff + px - ctx->wox;
*rx = nx;
} else if (xoff < ctx->wox && xoff + nx > ctx->wox + ctx->wsx) {
} else if (xoff < (int)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 < ctx->wox) {
} else if (xoff < (int)ctx->wox) {
/* Left not visible. */
*i = ctx->wox - (ctx->xoff + px);
*x = 0;
@@ -1223,13 +1209,23 @@ 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;
u_int i, x, rx, ry;
struct client *c = tty->client;
struct visible_ranges *r;
struct visible_range *ri;
u_int i, l, x, rx, ry;
log_debug("%s: %s, %u at %u,%u", __func__, c->name, nx, px, py);
if (tty_clamp_line(tty, ctx, px, py, nx, &i, &x, &rx, &ry))
tty_clear_line(tty, &ctx->defaults, ry, x, rx, bg);
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);
}
}
}
/* Clamp area position to visible part of pane. */
@@ -1296,12 +1292,17 @@ 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 grid_cell *defaults, u_int py,
tty_clear_area(struct tty *tty, const struct tty_ctx *ctx, u_int py,
u_int ny, u_int px, u_int nx, u_int bg)
{
struct client *c = tty->client;
u_int yy;
char tmp[64];
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];
log_debug("%s: %s, %u,%u at %u,%u", __func__, c->name, nx, ny, px, py);
@@ -1309,8 +1310,21 @@ tty_clear_area(struct tty *tty, const struct grid_cell *defaults, 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 (c->overlay_check == NULL && !tty_fake_bce(tty, defaults, bg)) {
if (!overlap && 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 &&
@@ -1361,9 +1375,18 @@ tty_clear_area(struct tty *tty, const struct grid_cell *defaults, 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++)
tty_clear_line(tty, defaults, yy, px, nx, bg);
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);
}
}
}
/* Clear an area in a pane. */
@@ -1371,28 +1394,57 @@ 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->defaults, y, ry, x, rx, bg);
tty_clear_area(tty, ctx, 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;
u_int nx = ctx->sx, i, x, rx, ry;
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;
log_debug("%s: %s %u %d", __func__, tty->client->name, py, ctx->bigger);
if (!ctx->bigger) {
tty_draw_line(tty, s, 0, py, nx, ctx->xoff, ctx->yoff + py,
&ctx->defaults, ctx->palette);
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);
}
return;
}
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);
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);
}
}
}
@@ -1459,9 +1511,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_IMAGES
#ifdef ENABLE_SIXEL
/* Update context for client. */
int
static int
tty_set_client_cb(struct tty_ctx *ttyctx, struct client *c)
{
struct window_pane *wp = ttyctx->arg;
@@ -1475,7 +1527,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;
ttyctx->yoff = ttyctx->ryoff = wp->yoff; /* xxxx find another way to do this */
if (status_at_line(c) == 0)
ttyctx->yoff += status_line_size(c);
@@ -1503,25 +1555,10 @@ tty_draw_images(struct client *c, struct window_pane *wp, struct screen *s)
ttyctx.sy = wp->sy;
ttyctx.ptr = im;
ttyctx.arg = wp;
ttyctx.arg = wp; /* xxx remove this */
ttyctx.set_client_cb = tty_set_client_cb;
ttyctx.allow_invisible_panes = 1;
/* 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;
}
tty_write_one(tty_cmd_sixelimage, c, &ttyctx);
}
}
#endif
@@ -1599,7 +1636,7 @@ tty_write(void (*cmdfn)(struct tty *, const struct tty_ctx *),
}
}
#ifdef ENABLE_IMAGES
#ifdef ENABLE_SIXEL
/* Only write to the incoming tty instead of every client. */
static void
tty_write_one(void (*cmdfn)(struct tty *, const struct tty_ctx *),
@@ -1832,8 +1869,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)) ||
@@ -1841,7 +1878,8 @@ 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) {
c->overlay_check != NULL ||
ctx->obscured) {
tty_redraw_region(tty, ctx);
return;
}
@@ -1999,7 +2037,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;
struct visible_ranges *r = NULL;
u_int px, py, i, vis = 0;
px = ctx->xoff + ctx->ocx - ctx->wox;
@@ -2009,6 +2047,7 @@ 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;
@@ -2034,8 +2073,9 @@ tty_cmd_cell(struct tty *tty, const struct tty_ctx *ctx)
tty_margin_off(tty);
tty_cursor_pane_unless_wrap(tty, ctx, ctx->ocx, ctx->ocy);
tty_cell(tty, ctx->cell, &ctx->defaults, ctx->palette,
ctx->s->hyperlinks);
if (screen_redraw_is_visible(r, px))
tty_cell(tty, ctx->cell, &ctx->defaults, ctx->palette,
ctx->s->hyperlinks);
if (ctx->num == 1)
tty_invalidate(tty);
@@ -2045,7 +2085,7 @@ void
tty_cmd_cells(struct tty *tty, const struct tty_ctx *ctx)
{
struct visible_ranges *r;
struct visible_range *rr;
struct visible_range *ri;
u_int i, px, py, cx;
char *cp = ctx->ptr;
@@ -2079,11 +2119,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++) {
rr = &r->ranges[i];
if (rr->nx != 0) {
cx = rr->px - ctx->xoff + ctx->wox;
ri = &r->ranges[i];
if (ri->nx != 0) {
cx = ri->px - ctx->xoff + ctx->wox;
tty_cursor_pane_unless_wrap(tty, ctx, cx, ctx->ocy);
tty_putn(tty, cp + rr->px - px, rr->nx, rr->nx);
tty_putn(tty, cp + ri->px - px, ri->nx, ri->nx);
}
}
}
@@ -2124,12 +2164,12 @@ tty_cmd_rawstring(struct tty *tty, const struct tty_ctx *ctx)
tty_invalidate(tty);
}
#ifdef ENABLE_SIXEL_IMAGES
#ifdef ENABLE_SIXEL
void
tty_cmd_sixelimage(struct tty *tty, const struct tty_ctx *ctx)
{
struct image *im = ctx->ptr;
struct sixel_image *si = im->data.sixel;
struct sixel_image *si = im->data;
struct sixel_image *new;
char *data;
size_t size;
@@ -2176,142 +2216,6 @@ 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)
{
@@ -2461,8 +2365,17 @@ tty_margin_off(struct tty *tty)
static void
tty_margin_pane(struct tty *tty, const struct tty_ctx *ctx)
{
tty_margin(tty, ctx->xoff - ctx->wox,
ctx->xoff + ctx->sx - 1 - ctx->wox);
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);
}
/* 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, int);
struct window_pane *wp, int, u_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,
int scroll_exit)
u_int tty_oy, 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, scroll_exit);
window_copy_scroll1(wme, wp, sl_mpos, my, tty_oy, scroll_exit);
}
}
static void
window_copy_scroll1(struct window_mode_entry *wme, struct window_pane *wp,
int sl_mpos, u_int my, int scroll_exit)
int sl_mpos, u_int my, u_int tty_oy, int scroll_exit)
{
struct window_copy_mode_data *data = wme->data;
u_int ox, oy, px, py, n, offset, size;
@@ -616,21 +616,29 @@ 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.
*/
if (my <= sb_top + sl_mpos) {
my_w = my + tty_oy;
if (my_w <= sb_top + (u_int)sl_mpos) {
/* Slider banged into top. */
new_slider_y = sb_top - wp->yoff;
} else if (my - sl_mpos > sb_top + sb_height - slider_height) {
} else if (my_w - 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 - wp->yoff - sl_mpos;
new_slider_y = my_w - wp->yoff - sl_mpos;
}
if (TAILQ_FIRST(&wp->modes) == NULL ||
@@ -1503,8 +1511,11 @@ 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;
window_copy_scroll(wp, c->tty.mouse_slider_mpos, m->y, scroll_exit);
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);
return (WINDOW_COPY_CMD_NOTHING);
}
@@ -4705,7 +4716,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, py);
window_copy_write_line(wme, ctx, yy);
}
static void
@@ -6071,7 +6082,10 @@ window_copy_drag_update(struct client *c, struct mouse_event *m)
if (c == NULL)
return;
wp = cmd_mouse_pane(m, NULL, NULL);
if (c->tty.mouse_wp != NULL)
wp = c->tty.mouse_wp;
else
wp = cmd_mouse_pane(m, NULL, NULL);
if (wp == NULL)
return;
wme = TAILQ_FIRST(&wp->modes);

136
window.c
View File

@@ -306,6 +306,7 @@ 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;
@@ -527,6 +528,8 @@ 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);
@@ -536,6 +539,20 @@ 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);
@@ -548,6 +565,33 @@ 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)
{
@@ -588,7 +632,20 @@ 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;
}
}
@@ -596,16 +653,34 @@ struct window_pane *
window_get_active_at(struct window *w, u_int x, u_int y)
{
struct window_pane *wp;
u_int xoff, yoff, sx, sy;
int status, xoff, yoff;
u_int sx, sy;
TAILQ_FOREACH(wp, &w->panes, entry) {
status = options_get_number(w->options, "pane-border-status");
TAILQ_FOREACH(wp, &w->z_index, zentry) {
if (!window_pane_visible(wp))
continue;
window_pane_full_size_offset(wp, &xoff, &yoff, &sx, &sy);
if (x < xoff || x > xoff + sx)
continue;
if (y < yoff || y > yoff + sy)
continue;
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;
}
return (wp);
}
return (NULL);
@@ -661,12 +736,24 @@ 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;
@@ -693,6 +780,14 @@ 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;
@@ -748,11 +843,17 @@ 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)
if (flags & (SPAWN_FULLSIZE|SPAWN_FLOATING))
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);
}
@@ -787,6 +888,7 @@ 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);
}
@@ -870,6 +972,7 @@ 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);
}
}
@@ -1105,6 +1208,15 @@ 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,
@@ -1272,8 +1384,10 @@ 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)
if (~wp->window->flags & WINDOW_ZOOMED &&
~wp->flags & PANE_MINIMISED)
return (1);
return (wp == wp->window->active);
}