Merge branch 'master' into 5211-fix-select-layout-list-windows

This commit is contained in:
Michael Grant
2026-06-29 19:26:12 +02:00
21 changed files with 806 additions and 224 deletions

View File

@@ -251,10 +251,10 @@ cmd_resize_pane_mouse_resize_move_floating(struct client *c,
sb_pos = options_get_number(w->options, "pane-scrollbars-position");
left = wp->xoff - 1;
right = wp->xoff + sx;
if (window_pane_show_scrollbar(wp, scrollbars) &&
if (window_pane_scrollbar_reserve(wp, scrollbars) &&
sb_pos == PANE_SCROLLBARS_LEFT) {
left -= wp->scrollbar_style.width + wp->scrollbar_style.pad;
} else if (window_pane_show_scrollbar(wp, scrollbars) &&
} else if (window_pane_scrollbar_reserve(wp, scrollbars) &&
sb_pos == PANE_SCROLLBARS_RIGHT) {
right += wp->scrollbar_style.width + wp->scrollbar_style.pad;
}

View File

@@ -40,8 +40,8 @@ const struct cmd_entry cmd_new_pane_entry = {
.args = { "bB:c:de:EfF:hIkl:Lm:p:PR:s:S:t:T:vWx:X:y:Y:Z", 0, -1, NULL },
.usage = "[-bdefhIklPvWZ] [-B border-lines] "
"[-c start-directory] [-e environment] "
"[-F format] [-l size] [-m message] [-p percentage] "
"[-c start-directory] [-e environment] "
"[-F format] [-l size] [-m message] [-p percentage] "
"[-s style] [-S active-border-style] "
"[-R inactive-border-style] [-T title] [-x width] [-y height] "
"[-X x-position] [-Y y-position] " CMD_TARGET_PANE_USAGE " "
@@ -95,7 +95,7 @@ cmd_split_window_exec(struct cmd *self, struct cmdq_item *item)
if (cmd_get_entry(self) == &cmd_new_pane_entry)
is_floating = !args_has(args, 'L');
else
is_floating = 0;
is_floating = window_pane_is_floating(wp);
flags = is_floating ? SPAWN_FLOATING : 0;
if (args_has(args, 'b'))

49
grid.c
View File

@@ -18,6 +18,9 @@
#include <sys/types.h>
#ifdef __APPLE__
#include <assert.h>
#endif
#include <stdlib.h>
#include <string.h>
@@ -56,6 +59,28 @@ static const struct grid_cell_entry grid_cleared_entry = {
{ .data = { 0, 8, 8, ' ' } }, GRID_FLAG_CLEARED
};
#ifdef __APPLE__
static void
grid_check_lines(struct grid *gd)
{
u_int i, j;
for (i = 0; i < gd->hsize + gd->sy; i++) {
for (j = i + 1; j < gd->hsize + gd->sy; j++) {
if (gd->linedata[i].celldata != NULL)
assert(gd->linedata[i].celldata != gd->linedata[j].celldata);
if (gd->linedata[i].extddata != NULL)
assert(gd->linedata[i].extddata != gd->linedata[j].extddata);
}
}
}
#else
static void
grid_check_lines(__unused struct grid *gd)
{
}
#endif
/* Store cell in entry. */
static void
grid_store_cell(struct grid_cell_entry *gce, const struct grid_cell *gc,
@@ -287,9 +312,17 @@ grid_set_tab(struct grid_cell *gc, u_int width)
static void
grid_free_line(struct grid *gd, u_int py)
{
free(gd->linedata[py].celldata);
free(gd->linedata[py].extddata);
memset(&gd->linedata[py], 0, sizeof gd->linedata[py]);
struct grid_line *gl = &gd->linedata[py];
#ifdef __APPLE__
assert(gl->cellused <= gl->cellsize);
assert(gl->extdsize == 0 || gl->extddata != NULL);
assert(gl->cellsize == 0 || gl->celldata != NULL);
#endif
free(gl->celldata);
free(gl->extddata);
memset(gl, 0, sizeof *gl);
}
/* Free several lines. */
@@ -449,6 +482,8 @@ grid_scroll_history(struct grid *gd, u_int bg)
gd->linedata[gd->hsize].time = current_time;
gd->hsize++;
gd->scroll_added++;
grid_check_lines(gd);
}
/* Clear the history. */
@@ -498,6 +533,8 @@ grid_scroll_history_region(struct grid *gd, u_int upper, u_int lower, u_int bg)
gd->hscrolled++;
gd->hsize++;
gd->scroll_added++;
grid_check_lines(gd);
}
/* Expand line to fit to cell. */
@@ -759,6 +796,8 @@ grid_move_lines(struct grid *gd, u_int dy, u_int py, u_int ny, u_int bg)
}
if (py != 0 && (py < dy || py >= dy + ny))
gd->linedata[py - 1].flags &= ~GRID_LINE_WRAPPED;
grid_check_lines(gd);
}
/* Move a group of cells. */
@@ -1248,6 +1287,8 @@ grid_duplicate_lines(struct grid *dst, u_int dy, struct grid *src, u_int sy,
sy++;
dy++;
}
grid_check_lines(dst);
}
/* Mark line as dead. */
@@ -1544,6 +1585,8 @@ grid_reflow(struct grid *gd, u_int sx)
gd->linedata = target->linedata;
free(target);
gd->scroll_generation++;
grid_check_lines(gd);
}
/* Convert to position based on wrapped lines. */

View File

@@ -41,6 +41,7 @@
*/
#define MAX_HYPERLINKS 5000
#define MAX_HYPERLINK_URI 1024
static long long hyperlinks_next_external_id = 1;
static u_int global_hyperlinks_count;
@@ -145,9 +146,13 @@ hyperlinks_put(struct hyperlinks *hl, const char *uri_in,
internal_id_in = "";
utf8_stravis(&uri, uri_in, VIS_OCTAL|VIS_CSTYLE);
utf8_stravis(&internal_id, internal_id_in, VIS_OCTAL|VIS_CSTYLE);
if (strlen(uri) > MAX_HYPERLINK_URI) {
free(uri);
return (0);
}
if (*internal_id_in != '\0') {
utf8_stravis(&internal_id, internal_id_in, VIS_OCTAL|VIS_CSTYLE);
if (*internal_id != '\0') {
find.uri = uri;
find.internal_id = internal_id;

View File

@@ -586,7 +586,7 @@ sixel_print(struct sixel_image *si, struct sixel_image *map, size_t *size)
len = 8192;
buf = xmalloc(len);
tmplen = xsnprintf(tmp, sizeof tmp, "\033P0;%uq", si->p2);
tmplen = xsnprintf(tmp, sizeof tmp, "\033P9;%uq", si->p2);
sixel_print_add(&buf, &len, &used, tmp, tmplen);
if (si->set_ra) {

280
layout.c
View File

@@ -31,8 +31,9 @@
* a cell which contains a list of cells, and 'leaf' to refer to a cell that
* contains a window pane. A leaf is considered to be 'tiled' if it is to be
* drawn as a part of the tiled layout. A 'neighbour' is a sibling that is also
* tiled. A cell's 'split' size refers to the side that is shortened when
* splitting it, determined by the parent's type.
* tiled or a node that contains a tiled leaf in a subtree. A cell's 'split'
* size refers to the side that is shortened when splitting it, determined by
* the parent's type.
*
* Each window has a pointer to the root of its layout tree (containing its
* panes), every pane has a pointer back to the cell containing it, and each
@@ -298,6 +299,27 @@ layout_cell_is_first_tiled(struct layout_cell *lc)
return (lcchild == lc);
}
static struct layout_cell *
layout_cell_get_first_tiled(struct layout_cell *lc)
{
struct layout_cell *lcchild, *lcchild2;
if (layout_cell_is_tiled(lc))
return (lc);
if (lc->type == LAYOUT_WINDOWPANE)
return (NULL);
TAILQ_FOREACH(lcchild, &lc->cells, entry) {
if (layout_cell_is_tiled(lcchild))
return (lcchild);
if (lcchild->type != LAYOUT_WINDOWPANE) {
lcchild2 = layout_cell_get_first_tiled(lcchild);
if (lcchild2 != NULL)
return (lcchild2);
}
}
return (NULL);
}
/* Fix cell offsets for a child cell. */
static void
@@ -442,7 +464,7 @@ layout_fix_panes(struct window *w, struct window_pane *skip)
sy--;
}
if (window_pane_show_scrollbar(wp, scrollbars)) {
if (window_pane_scrollbar_reserve(wp, scrollbars)) {
sb_w = wp->scrollbar_style.width;
sb_pad = wp->scrollbar_style.pad;
if (sb_w < 1)
@@ -515,7 +537,7 @@ layout_resize_check(struct window *w, struct layout_cell *lc,
/* Space available in this cell only. */
if (type == LAYOUT_LEFTRIGHT) {
available = lc->sx;
if (scrollbars)
if (scrollbars == PANE_SCROLLBARS_ALWAYS)
minimum = PANE_MINIMUM + sb_style->width +
sb_style->pad;
else
@@ -611,6 +633,20 @@ layout_resize_adjust(struct window *w, struct layout_cell *lc,
}
}
/* Resizes a cell to a specified size */
void
layout_resize_set_size(struct window *w, struct layout_cell *lc,
enum layout_type type, u_int size)
{
int change;
if (type == LAYOUT_LEFTRIGHT)
change = size - lc->sx;
else
change = size - lc->sy;
layout_resize_adjust(w, lc, type, change);
}
/* Find and return the nearest neighbour to a cell in a specific direction. */
static struct layout_cell *
layout_cell_get_neighbour_direction(struct layout_cell *lc, int direction)
@@ -1186,6 +1222,101 @@ layout_resize_child_cells(struct window *w, struct layout_cell *lc)
}
}
/*
* Replaces the provided layout cell with a new node of the specified type and
* inserts the cell into it. Used when creating new cells requires a different
* layout type, or when the root layout is a window pane.
*/
struct layout_cell *
layout_replace_with_node(struct window *w, struct layout_cell *lc,
enum layout_type type)
{
struct layout_cell *lcparent;
lcparent = layout_create_cell(lc->parent);
layout_make_node(lcparent, type);
layout_set_size(lcparent, lc->sx, lc->sy, lc->xoff, lc->yoff);
if (lc->parent == NULL)
w->layout_root = lcparent;
else
TAILQ_REPLACE(&lc->parent->cells, lc, lcparent, entry);
/* Insert the old cell. */
lc->parent = lcparent;
TAILQ_INSERT_HEAD(&lcparent->cells, lc, entry);
return (lcparent);
}
/* Checks if there is enough space for two new panes. */
int
layout_split_check_space(struct window_pane *wp, struct layout_cell *lc,
enum layout_type type)
{
struct style *sb_style = &wp->scrollbar_style;
u_int minimum, sx = lc->sx, sy = lc->sy;
int scrollbars, status;
if (lc->flags & LAYOUT_CELL_FLOATING)
fatalx("floating cells cannot be split");
status = window_get_pane_status(wp->window);
scrollbars = options_get_number(wp->window->options, "pane-scrollbars");
switch (type) {
case LAYOUT_LEFTRIGHT:
if (scrollbars == PANE_SCROLLBARS_ALWAYS) {
minimum = PANE_MINIMUM * 2 + sb_style->width +
sb_style->pad;
} else
minimum = PANE_MINIMUM * 2 + 1;
if (sx < minimum)
return (0);
break;
case LAYOUT_TOPBOTTOM:
if (layout_add_horizontal_border(wp->window, lc, status))
minimum = PANE_MINIMUM * 2 + 2;
else
minimum = PANE_MINIMUM * 2 + 1;
if (sy < minimum)
return (0);
break;
default:
fatalx("bad layout type");
}
return (1);
}
/* Calculates the new cell sizes when splitting a pane. */
void
layout_split_sizes(struct layout_cell *lc, int size, int before,
enum layout_type type, u_int *size1, u_int *size2, u_int *saved_size)
{
u_int s1, s2, ss;
u_int sx = lc->sx, sy = lc->sy;
if (type == LAYOUT_LEFTRIGHT)
ss = sx;
else
ss = sy;
if (size < 0)
s2 = ((ss + 1) / 2) - 1;
else if (before)
s2 = ss - size - 1;
else
s2 = size;
if (s2 < PANE_MINIMUM)
s2 = PANE_MINIMUM;
else if (s2 > sx - 2)
s2 = ss - 2;
s1 = ss - 1 - s2;
*size1 = s1;
*size2 = s2;
*saved_size = ss;
}
/*
* Split a pane into two. size is a hint, or -1 for default half/half
* split. This must be followed by layout_assign_pane before much else happens!
@@ -1195,11 +1326,10 @@ layout_split_pane(struct window_pane *wp, enum layout_type type, int size,
int flags)
{
struct layout_cell *lc, *lcparent, *lcnew, *lc1, *lc2;
struct style *sb_style = &wp->scrollbar_style;
u_int sx, sy, xoff, yoff, size1, size2, minimum;
u_int sx, sy, xoff, yoff, size1, size2;
u_int new_size, saved_size, resize_first = 0;
int full_size = (flags & SPAWN_FULLSIZE), status;
int scrollbars;
int full_size = (flags & SPAWN_FULLSIZE);
int before = (flags & SPAWN_BEFORE);
/*
* If full_size is specified, add a new cell at the top of the window
@@ -1209,8 +1339,6 @@ layout_split_pane(struct window_pane *wp, enum layout_type type, int size,
lc = wp->window->layout_root;
else
lc = wp->layout_cell;
status = window_get_pane_status(wp->window);
scrollbars = options_get_number(wp->window->options, "pane-scrollbars");
/* Copy the old cell size. */
sx = lc->sx;
@@ -1219,47 +1347,14 @@ layout_split_pane(struct window_pane *wp, enum layout_type type, int size,
yoff = lc->yoff;
/* Check there is enough space for the two new panes. */
switch (type) {
case LAYOUT_LEFTRIGHT:
if (scrollbars) {
minimum = PANE_MINIMUM * 2 + sb_style->width +
sb_style->pad;
} else
minimum = PANE_MINIMUM * 2 + 1;
if (sx < minimum)
return (NULL);
break;
case LAYOUT_TOPBOTTOM:
if (layout_add_horizontal_border(wp->window, lc, status))
minimum = PANE_MINIMUM * 2 + 2;
else
minimum = PANE_MINIMUM * 2 + 1;
if (sy < minimum)
return (NULL);
break;
default:
fatalx("bad layout type");
}
if (!layout_split_check_space(wp, lc, type))
return (NULL);
/*
* Calculate new cell sizes. size is the target size or -1 for middle
* split, size1 is the size of the top/left and size2 the bottom/right.
*/
if (type == LAYOUT_LEFTRIGHT)
saved_size = sx;
else
saved_size = sy;
if (size < 0)
size2 = ((saved_size + 1) / 2) - 1;
else if (flags & SPAWN_BEFORE)
size2 = saved_size - size - 1;
else
size2 = size;
if (size2 < PANE_MINIMUM)
size2 = PANE_MINIMUM;
else if (size2 > saved_size - 2)
size2 = saved_size - 2;
size1 = saved_size - 1 - size2;
layout_split_sizes(lc, size, before, type, &size1, &size2, &saved_size);
/* Which size are we using? */
if (flags & SPAWN_BEFORE)
@@ -1317,17 +1412,7 @@ layout_split_pane(struct window_pane *wp, enum layout_type type, int size,
*/
/* Create and insert the replacement parent. */
lcparent = layout_create_cell(lc->parent);
layout_make_node(lcparent, type);
layout_set_size(lcparent, sx, sy, xoff, yoff);
if (lc->parent == NULL)
wp->window->layout_root = lcparent;
else
TAILQ_REPLACE(&lc->parent->cells, lc, lcparent, entry);
/* Insert the old cell. */
lc->parent = lcparent;
TAILQ_INSERT_HEAD(&lcparent->cells, lc, entry);
lcparent = layout_replace_with_node(wp->window, lc, type);
/* Create the new child cell. */
lcnew = layout_create_cell(lcparent);
@@ -1386,14 +1471,7 @@ layout_floating_pane(struct window *w, struct window_pane *wp, u_int sx,
* Adding a pane to a root that isn't node. Must create and
* insert a new root.
*/
lcparent = layout_create_cell(NULL);
layout_make_node(lcparent, LAYOUT_TOPBOTTOM);
layout_set_size(lcparent, w->sx, w->sy, 0, 0);
w->layout_root = lcparent;
/* Insert the old cell. */
lc->parent = lcparent;
TAILQ_INSERT_HEAD(&lcparent->cells, lc, entry);
lcparent = layout_replace_with_node(w, lc, LAYOUT_TOPBOTTOM);
}
lcnew = layout_create_cell(lcparent);
@@ -1714,7 +1792,75 @@ layout_remove_tile(struct window *w, struct layout_cell *lc)
layout_resize_adjust(w, lcneighbour, type, change);
}
/* Zeroing out the cell geometry until the cell is retiled. */
layout_set_size(lc, 0, 0, 0, 0);
/*
* Zeroing out the cell geometry until the cell is retiled unless this
* is the top level node.
*/
if (lc->parent != NULL)
layout_set_size(lc, 0, 0, 0, 0);
return (1);
}
/*
* Inserts a cell back into the tiled layout by taking half the space from its
* nearest neighbour.
*/
int
layout_insert_tile(struct window *w, struct layout_cell *lc)
{
struct layout_cell *lcneighbour, *lctiled, *lcparent;
enum layout_type type;
u_int size1, size2, saved_size;
if (lc == NULL)
fatalx("layout cell cannot be null when tiling");
lcparent = lc->parent;
if (lc->flags & LAYOUT_CELL_FLOATING)
return (1);
if (lcparent == NULL) {
/* Only pane in the layout. */
layout_set_size(lc, w->sx, w->sy, 0, 0);
return (1);
}
type = lcparent->type;
lcneighbour = layout_cell_get_neighbour(lc);
if (lcneighbour == NULL) {
/*
* This will become the only visible cell in the parent.
* Tile the parent, then set the child's 'split' size.
*/
layout_insert_tile(w, lcparent);
if (type == LAYOUT_LEFTRIGHT)
size1 = lcparent->sx;
else
size1 = lcparent->sy;
layout_resize_set_size(w, lc, type, size1);
} else {
/*
* If the neighbour is a node, a tiled child in the subtree of
* the neighbour is needed to check for space.
*/
lctiled = layout_cell_get_first_tiled(lcneighbour);
if (!layout_split_check_space(lctiled->wp, lcneighbour, type))
return (0);
layout_split_sizes(lcneighbour, -1, 0, type, &size1, &size2,
&saved_size);
layout_resize_set_size(w, lc, type, size1);
layout_resize_set_size(w, lcneighbour, type, size2);
}
/* Setting opposite of the 'split' size to that of the parent. */
if (lcparent->type == LAYOUT_LEFTRIGHT) {
size1 = lcparent->sy;
type = LAYOUT_TOPBOTTOM;
} else {
size1 = lcparent->sx;
type = LAYOUT_LEFTRIGHT;
}
layout_resize_set_size(w, lc, type, size1);
return (1);
}

View File

@@ -64,7 +64,7 @@ static const char *options_table_cursor_style_list[] = {
"blinking-bar", "bar", NULL
};
static const char *options_table_pane_scrollbars_list[] = {
"off", "modal", "on", NULL
"off", "modal", "on", "auto-hide", NULL
};
static const char *options_table_pane_scrollbars_position_list[] = {
"right", "left", NULL
@@ -1594,7 +1594,17 @@ const struct options_table_entry options_table[] = {
.scope = OPTIONS_TABLE_WINDOW,
.choices = options_table_pane_scrollbars_list,
.default_num = PANE_SCROLLBARS_OFF,
.text = "Pane scrollbar state."
.text = "Pane scrollbar state: off, on, modal, or auto-hide."
},
{ .name = "pane-scrollbars-timeout",
.type = OPTIONS_TABLE_NUMBER,
.scope = OPTIONS_TABLE_WINDOW,
.minimum = 0,
.maximum = INT_MAX,
.default_num = 500,
.unit = "milliseconds",
.text = "Time before modal and auto-hide pane scrollbars disappear."
},
{ .name = "pane-scrollbars-style",

View File

@@ -1264,6 +1264,7 @@ options_push_changes(const char *name)
strcmp(name, "pane-border-lines") == 0 ||
strcmp(name, "pane-border-status") == 0 ||
strcmp(name, "pane-scrollbars") == 0 ||
strcmp(name, "pane-scrollbars-timeout") == 0 ||
strcmp(name, "pane-scrollbars-position") == 0 ||
strcmp(name, "pane-scrollbars-style") == 0)
redraw_invalidate_all_scenes();
@@ -1288,6 +1289,10 @@ options_push_changes(const char *name)
RB_FOREACH(w, windows, &windows)
layout_fix_panes(w, NULL);
}
if (strcmp(name, "pane-scrollbars") == 0) {
RB_FOREACH(wp, window_pane_tree, &all_window_panes)
window_pane_scrollbar_hide(wp);
}
if (strcmp(name, "pane-scrollbars-style") == 0) {
RB_FOREACH(wp, window_pane_tree, &all_window_panes) {
style_set_scrollbar_style_from_option(

View File

@@ -110,20 +110,26 @@ must_fail $TMUX set-buffer -b "bad${invalid}name" data
# Titles set by commands allow '#', ':' and '.'.
$TMUX select-pane -T 'title#:.ok' || fail "command title rejected"
must_equal "$($TMUX display-message -p '#{pane_title}')" 'title#:.ok'
$TMUX send-keys "printf '\\033]2;title#[fg=red]ok\\007'" Enter || exit 1
sleep 1
must_equal "$($TMUX display-message -p '#{pane_title}')" 'title#[fg=red]ok'
$TMUX send-keys "printf '\\033]2;title#(bad)\\007'" Enter || exit 1
sleep 1
must_equal "$($TMUX display-message -p '#{pane_title}')" 'title_(bad)'
# Buffer names allow '#', ':' and '.'.
$TMUX set-buffer -b 'buffer#:.ok' data || fail "buffer name rejected"
must_equal "$($TMUX list-buffers -F '#{buffer_name}')" 'buffer#:.ok'
# Window names from escape sequences reject '#', ':' and '.' by cleaning them.
# Window names from escape sequences allow '#' except in '#('.
$TMUX send-keys "printf '\\033kescape#:.ok\\033\\\\'" Enter || exit 1
sleep 1
must_equal "$($TMUX display-message -p '#{window_name}')" 'escape___ok'
must_equal "$($TMUX display-message -p '#{window_name}')" 'escape#__ok'
# Titles from escape sequences reject only '#'.
$TMUX send-keys "printf '\\033]2;escape#:.ok\\007'" Enter || exit 1
sleep 1
must_equal "$($TMUX display-message -p '#{pane_title}')" 'escape_:.ok'
must_equal "$($TMUX display-message -p '#{pane_title}')" 'escape#:.ok'
# Invalid UTF-8 from escape sequences is ignored.
$TMUX rename-window 'before-invalid' || exit 1

View File

@@ -24,7 +24,8 @@
#include "tmux.h"
static void
regsub_copy(char **buf, ssize_t *len, const char *text, size_t start, size_t end)
regsub_copy(char **buf, ssize_t *len, const char *text, size_t start,
size_t end)
{
size_t add = end - start;

View File

@@ -81,6 +81,7 @@ enum redraw_span_type {
#define REDRAW_BORDER_IS_ARROW 0x1
#define REDRAW_SCROLLBAR_LEFT 0x2
#define REDRAW_SCROLLBAR_RIGHT 0x4
#define REDRAW_SCROLLBAR_OVERLAY 0x8
/* Draw operations. */
#define REDRAW_PANE 0x1
@@ -447,7 +448,7 @@ redraw_mark_pane_inside(struct redraw_build_ctx *bctx, struct window_pane *wp)
/* Mark scrollbar data. */
static void
redraw_mark_pane_scrollbar(struct redraw_build_ctx *bctx,
struct window_pane *wp, int sb_w, int sb_left)
struct window_pane *wp, int sb_w, int sb_left, int overlay)
{
struct redraw_build_cell *bc;
u_int x, y;
@@ -457,7 +458,13 @@ redraw_mark_pane_scrollbar(struct redraw_build_ctx *bctx,
if (sb_w == 0)
return;
if (sb_left) {
if (overlay && sb_left) {
sx = wp->xoff;
ex = sx + sb_w - 1;
} else if (overlay) {
ex = wp->xoff + (int)wp->sx - 1;
sx = ex - sb_w + 1;
} else if (sb_left) {
sx = wp->xoff - sb_w;
ex = wp->xoff - 1;
} else {
@@ -481,6 +488,8 @@ redraw_mark_pane_scrollbar(struct redraw_build_ctx *bctx,
bc->data.sb.flags |= REDRAW_SCROLLBAR_LEFT;
else
bc->data.sb.flags |= REDRAW_SCROLLBAR_RIGHT;
if (overlay)
bc->data.sb.flags |= REDRAW_SCROLLBAR_OVERLAY;
}
}
}
@@ -755,19 +764,30 @@ redraw_mark_pane_borders(struct redraw_build_ctx *bctx, struct window_pane *wp,
static void
redraw_mark_pane(struct redraw_build_ctx *bctx, struct window_pane *wp)
{
int sb_w = 0, sb_left = 0;
int sb_w = 0, sb_left = 0, overlay = 0;
if (!window_pane_is_visible(wp))
return;
if (window_pane_show_scrollbar(wp, bctx->sb))
sb_w = wp->scrollbar_style.width + wp->scrollbar_style.pad;
if (window_pane_scrollbar_visible(wp, bctx->sb)) {
overlay = window_pane_scrollbar_overlay(wp, bctx->sb);
if (overlay) {
sb_w = wp->scrollbar_style.width +
wp->scrollbar_style.pad;
if (sb_w > (int)wp->sx) {
sb_w = wp->scrollbar_style.width;
if (sb_w > (int)wp->sx)
sb_w = wp->sx;
}
} else
sb_w = wp->scrollbar_style.width + wp->scrollbar_style.pad;
}
if (sb_w != 0 && bctx->sbp == PANE_SCROLLBARS_LEFT)
sb_left = 1;
redraw_mark_pane_inside(bctx, wp);
redraw_mark_pane_borders(bctx, wp, sb_w, sb_left);
redraw_mark_pane_scrollbar(bctx, wp, sb_w, sb_left);
redraw_mark_pane_borders(bctx, wp, overlay ? 0 : sb_w, sb_left);
redraw_mark_pane_scrollbar(bctx, wp, sb_w, sb_left, overlay);
}
/* Choose the pane that will provide the border style for two-pane layouts. */
@@ -1220,7 +1240,7 @@ redraw_draw_scrollbar_span(struct redraw_draw_ctx *dctx,
struct screen *s = wp->screen;
struct tty *tty = &scene->c->tty;
struct style *sb_style = &wp->scrollbar_style;
struct grid_cell gc, slgc, *gcp;
struct grid_cell gc, slgc, pad_gc, *gcp;
double pct_view;
u_int total_height, slider_h, slider_y;
u_int sb_h = span->data.sb.height;
@@ -1260,6 +1280,7 @@ redraw_draw_scrollbar_span(struct redraw_draw_ctx *dctx,
memcpy(&slgc, &gc, sizeof slgc);
slgc.fg = gc.bg;
slgc.bg = gc.fg;
tty_default_colours(&pad_gc, wp, NULL);
sb_w = sb_style->width;
sb_pad = sb_style->pad;
@@ -1269,12 +1290,12 @@ redraw_draw_scrollbar_span(struct redraw_draw_ctx *dctx,
for (i = 0; i < n; i++) {
if (span->data.sb.flags & REDRAW_SCROLLBAR_LEFT) {
if (off + i >= sb_w && off + i < sb_w + sb_pad) {
tty_cell(tty, &grid_default_cell, NULL);
tty_cell(tty, &pad_gc, NULL);
continue;
}
} else {
if (off + i < sb_pad) {
tty_cell(tty, &grid_default_cell, NULL);
tty_cell(tty, &pad_gc, NULL);
continue;
}
}

View File

@@ -1524,6 +1524,7 @@ screen_write_clearline(struct screen_write_ctx *ctx, u_int bg)
struct grid_line *gl;
u_int sx = screen_size_x(s);
struct screen_write_citem *ci = ctx->item;
u_int flags;
gl = grid_get_line(s->grid, s->grid->hsize + s->cy);
if (gl->cellsize == 0 && COLOUR_DEFAULT(bg))
@@ -1534,7 +1535,10 @@ screen_write_clearline(struct screen_write_ctx *ctx, u_int bg)
ctx->wp->flags |= PANE_REDRAW;
#endif
flags = gl->flags & (GRID_LINE_START_PROMPT|GRID_LINE_START_OUTPUT);
grid_view_clear(s->grid, 0, s->cy, sx, 1, bg);
gl = grid_get_line(s->grid, s->grid->hsize + s->cy);
gl->flags |= flags;
screen_write_collect_clear(ctx, s->cy, 1);
ci->x = 0;
@@ -2178,6 +2182,10 @@ screen_write_collect_flush_scrolled(struct screen_write_ctx *ctx)
screen_write_redraw_pane(ctx, &ttyctx);
return 0;
}
if (wp != NULL && window_pane_scrollbar_overlay_visible(wp)) {
wp->flags |= PANE_REDRAW;
return 0;
}
log_debug("%s: scrolled %u (region %u-%u)", __func__, ctx->scrolled,
s->rupper, s->rlower);
@@ -2191,7 +2199,7 @@ screen_write_collect_flush_scrolled(struct screen_write_ctx *ctx)
tty_write(tty_cmd_scrollup, &ttyctx);
if (wp != NULL)
wp->flags |= PANE_REDRAWSCROLLBAR;
window_pane_scrollbar_redraw(wp);
return 1;
}

View File

@@ -48,6 +48,8 @@ static void server_client_dispatch(struct imsg *, void *);
static int server_client_dispatch_command(struct client *, struct imsg *);
static int server_client_dispatch_identify(struct client *, struct imsg *);
static int server_client_dispatch_shell(struct client *);
static void server_client_update_scrollbar_hover(struct client *, int, int,
int);
static void server_client_report_theme(struct client *, enum client_theme);
/* Compare client windows. */
@@ -601,6 +603,60 @@ server_client_exec(struct client *c, const char *cmd)
free(msg);
}
/* Is this point inside the auto-hide scrollbar interaction area? */
static int
server_client_in_scrollbar_area(struct window_pane *wp, int px, int py)
{
struct window *w = wp->window;
u_int width, pad, total;
int sb, sb_pos, start, end;
sb = options_get_number(w->options, "pane-scrollbars");
if (!window_pane_scrollbar_overlay(wp, sb))
return (0);
if (py < wp->yoff || py >= wp->yoff + (int)wp->sy)
return (0);
width = wp->scrollbar_style.width;
pad = wp->scrollbar_style.pad;
total = width + pad;
if (total == 0 || total > wp->sx)
total = wp->sx;
sb_pos = options_get_number(w->options, "pane-scrollbars-position");
if (sb_pos == PANE_SCROLLBARS_LEFT) {
start = wp->xoff;
end = wp->xoff + (int)total - 1;
} else {
end = wp->xoff + (int)wp->sx - 1;
start = end - (int)total + 1;
}
return (px >= start && px <= end);
}
/* Update auto-hide scrollbars for a mouse movement. */
static void
server_client_update_scrollbar_hover(struct client *c, int type, int px, int py)
{
struct window *w = c->session->curw->window;
struct window_pane *wp;
if (type != KEYC_TYPE_MOUSEMOVE)
return;
TAILQ_FOREACH(wp, &w->panes, entry) {
if (!window_pane_is_visible(wp))
continue;
if (server_client_in_scrollbar_area(wp, px, py)) {
wp->sb_auto_hover = 1;
window_pane_scrollbar_show(wp, 1);
} else {
wp->sb_auto_hover = 0;
window_pane_scrollbar_start_timer(wp);
}
}
}
/* Is the mouse inside a pane? */
static enum key_code_mouse_location
server_client_check_mouse_in_pane(struct window_pane *wp, int px, int py,
@@ -611,14 +667,18 @@ server_client_check_mouse_in_pane(struct window_pane *wp, int px, int py,
int pane_status, sb, sb_pos, sb_w, sb_pad;
int pane_status_line, sl_top, sl_bottom;
int bdr_bottom, bdr_top, bdr_left, bdr_right;
int sb_start, sb_end, sb_overlay;
sb = options_get_number(w->options, "pane-scrollbars");
sb_pos = options_get_number(w->options, "pane-scrollbars-position");
pane_status = window_pane_get_pane_status(wp);
sb_overlay = window_pane_scrollbar_overlay(wp, sb);
if (window_pane_show_scrollbar(wp, sb)) {
if (window_pane_scrollbar_visible(wp, sb)) {
sb_w = wp->scrollbar_style.width;
sb_pad = wp->scrollbar_style.pad;
if (sb_overlay && sb_w > (int)wp->sx)
sb_w = wp->sx;
} else {
sb_w = 0;
sb_pad = 0;
@@ -631,9 +691,34 @@ server_client_check_mouse_in_pane(struct window_pane *wp, int px, int py,
else
pane_status_line = -1; /* not used */
bdr_left = wp->xoff - 1;
if (sb_pos == PANE_SCROLLBARS_LEFT)
if (!sb_overlay && sb_pos == PANE_SCROLLBARS_LEFT)
bdr_left -= sb_pad + sb_w;
if (sb_overlay && sb_w != 0 &&
py >= wp->yoff && py < wp->yoff + (int)wp->sy &&
px >= wp->xoff && px < wp->xoff + (int)wp->sx) {
if (sb_pos == PANE_SCROLLBARS_LEFT) {
sb_start = wp->xoff;
sb_end = sb_start + sb_w - 1;
} else {
sb_end = wp->xoff + (int)wp->sx - 1;
sb_start = sb_end - sb_w + 1;
}
if (px >= sb_start && px <= sb_end) {
sl_top = wp->yoff + wp->sb_slider_y;
sl_bottom = (wp->yoff + wp->sb_slider_y +
wp->sb_slider_h - 1);
if (py < sl_top)
return (KEYC_MOUSE_LOCATION_SCROLLBAR_UP);
else if (py >= sl_top && py <= sl_bottom) {
*sl_mpos = (py - wp->sb_slider_y - wp->yoff);
return (KEYC_MOUSE_LOCATION_SCROLLBAR_SLIDER);
} else
return (KEYC_MOUSE_LOCATION_SCROLLBAR_DOWN);
}
return (KEYC_MOUSE_LOCATION_PANE);
}
/* Check if point is within the pane or scrollbar. */
if (((pane_status != PANE_STATUS_OFF &&
py != pane_status_line && py != wp->yoff + (int)wp->sy) ||
@@ -681,7 +766,7 @@ server_client_check_mouse_in_pane(struct window_pane *wp, int px, int py,
if (window_pane_is_floating(fwp) &&
window_pane_get_pane_lines(fwp) == PANE_LINES_NONE)
continue;
if (window_pane_show_scrollbar(fwp, sb)) {
if (window_pane_scrollbar_reserve(fwp, sb)) {
sb_w = fwp->scrollbar_style.width;
sb_pad = fwp->scrollbar_style.pad;
} else {
@@ -911,10 +996,14 @@ have_event:
tty_window_offset(&c->tty, &m->ox, &m->oy, &sx, &sy);
log_debug("mouse window @%u at %u,%u (%ux%u)",
w->id, m->ox, m->oy, sx, sy);
if (px > sx || py > sy)
if (px > sx || py > sy) {
server_client_update_scrollbar_hover(c, type,
-1, -1);
return (KEYC_UNKNOWN);
}
px = px + m->ox;
py = py + m->oy;
server_client_update_scrollbar_hover(c, type, px, py);
if (type == KEYC_TYPE_MOUSEDRAG && lwp != NULL) {
/* Use pane from last mouse event. */
@@ -947,7 +1036,8 @@ have_event:
m->wp = wp->id;
m->w = wp->window->id;
}
}
} else
server_client_update_scrollbar_hover(c, type, -1, -1);
/* Reset click type or add a click timer if needed. */
if (type == KEYC_TYPE_MOUSEDOWN ||
@@ -1907,8 +1997,9 @@ server_client_reset_state(struct client *c)
struct window_pane *wp = server_client_get_pane(c), *loop;
struct screen *s = NULL;
struct options *oo = c->session->options;
int mode = 0, cursor, flags, pane_mode = 0;
int mode = 0, cursor, flags, pane_mode = 0, sb;
u_int cx = 0, cy = 0, ox, oy, sx, sy, prompt = 0;
u_int sb_w;
struct visible_ranges *r;
if (c->flags & (CLIENT_CONTROL|CLIENT_SUSPENDED))
@@ -1961,6 +2052,21 @@ server_client_reset_state(struct client *c)
if (!window_position_is_visible(r, cx))
cursor = 0;
if (window_pane_scrollbar_overlay_visible(wp)) {
sb_w = wp->scrollbar_style.width;
if (sb_w > wp->sx)
sb_w = wp->sx;
if (sb_w != 0 &&
options_get_number(w->options,
"pane-scrollbars-position") ==
PANE_SCROLLBARS_LEFT) {
if (s->cx < sb_w)
cursor = 0;
} else if (sb_w != 0 &&
s->cx >= wp->sx - sb_w)
cursor = 0;
}
if (status_at_line(c) == 0)
cy += status_line_size(c);
}
@@ -1988,7 +2094,10 @@ server_client_reset_state(struct client *c)
mode |= MODE_MOUSE_ALL;
}
}
if (options_get_number(oo, "focus-follows-mouse"))
sb = options_get_number(w->options, "pane-scrollbars");
if (options_get_number(oo, "focus-follows-mouse") ||
sb == PANE_SCROLLBARS_MODAL ||
sb == PANE_SCROLLBARS_AUTOHIDE)
mode |= MODE_MOUSE_ALL;
else if (~mode & MODE_MOUSE_ALL)
mode |= MODE_MOUSE_BUTTON;

4
sort.c
View File

@@ -26,8 +26,8 @@
static struct sort_criteria *sort_criteria;
static void
sort_qsort(void *l, u_int len, u_int size, int (*cmp)(const void *, const void *),
struct sort_criteria *sort_crit)
sort_qsort(void *l, u_int len, u_int size, int (*cmp)(const void *,
const void *), struct sort_criteria *sort_crit)
{
u_int i;
void *tmp, **ll;

254
tmux.1
View File

@@ -2744,6 +2744,15 @@ and
.Fl Y
options set the position of the upper-left corner of the pane.
If omitted, new floating panes are cascaded from the top-left of the window.
The
.Fl x ,
.Fl y ,
.Fl X ,
and
.Fl Y
options may be followed by
.Ql %
to specify a percentage of the window size.
.Pp
If the pane had previously been floating, the position and sizes are restored
from the saved values not specified by the
@@ -3611,7 +3620,7 @@ but a different format may be specified with
.Fl F .
.Tg newp
.It Xo Ic new\-pane
.Op Fl bdefhIkPvWZ
.Op Fl bdefhIkLPvWZ
.Op Fl B Ar border\-lines
.Op Fl c Ar start\-directory
.Op Fl e Ar environment
@@ -3624,101 +3633,49 @@ but a different format may be specified with
.Op Fl S Ar active\-border\-style
.Op Fl t Ar target\-pane
.Op Fl T Ar title
.Op Fl x Ar width
.Op Fl y Ar height
.Op Fl X Ar x-position
.Op Fl Y Ar y-position
.Op Ar shell\-command Op Ar argument ...
.Xc
.D1 Pq alias: Ic newp
Create a new pane.
The new pane is created by splitting
.Ar target\-pane .
If
.Fl d
is given, the session does not make the new pane the current pane.
.Fl Z
zooms if the window is not zoomed, or keeps it zoomed if already zoomed.
.Fl s
sets the style for the pane content.
.Fl S
sets the border style when the pane is active and
.Fl R
sets the border style when the pane is inactive (see
.Sx STYLES ) .
.Fl T
sets the pane title.
Creates a new floating pane.
The
.Fl x
and
.Fl y
options set the width and height of the floating pane in columns and lines
respectively.
The default is half the window width and a quarter the window height.
The
.Fl X
and
.Fl Y
options set the position of the upper-left corner of the pane.
If omitted, new floating panes are cascaded from the top-left of the window.
The
.Fl x ,
.Fl y ,
.Fl X ,
and
.Fl Y
options may be followed by
.Ql %
to specify a percentage of the window size.
.Fl B
sets the pane border lines for floating panes; see
.Ic pane\-border\-lines .
.Pp
.Fl h
does a horizontal split and
.Fl v
a vertical split; if neither is specified,
.Fl v
is assumed.
The
.Fl l
option specifies the size of the new pane in lines (for vertical split) or in
columns (for horizontal split);
.Ar size
may be followed by
.Ql %
to specify a percentage of the available space.
.Fl p
is a shorthand option for this.
The
.Fl b
option causes the new pane to be created to the left of or above
.Ar target\-pane .
The
.Fl f
option creates a new pane spanning the full window height (with
.Fl h )
or full window width (with
.Fl v ) ,
instead of splitting the active pane.
.Pp
.Fl k
keeps the pane open after the optional
.Ar shell\-command
exits and waits for a key to be pressed before closing it.
The message shown is controlled by the
.Ic remain\-on\-exit\-format
option.
.Fl m Ar message
is equivalent to
.Fl k
but also sets the
.Ic remain\-on\-exit\-format
option for this pane to
.Ar message .
.Pp
.Fl W
Waits until
.Ar shell\-command
exits, then returns its exit status.
For example:
.Bd -literal -offset indent
$ tmux new-pane -W 'vi afile'
$ echo $?
0
.Ed
.Pp
.Fl E ,
or an empty
.Ar shell\-command ,
(\[aq]\[aq]) will create an empty pane with no command running in it;
.Ic display-message
.Fl I
can write to an empty pane.
The
.Fl I
flag will create an empty pane and forward any output from stdin to it.
For example:
.Bd -literal -offset indent
$ make 2>&1|tmux new\-pane \-dI &
.Ed
.Fl L
option makes
.Ic new\-pane
behave like
.Ic split\-window .
.Pp
All other options have the same meaning as for the
.Ic new\-window
.Ic split\-window
command.
.Tg nextl
.It Ic next\-layout Op Fl t Ar target\-window
@@ -4209,14 +4166,92 @@ the command behaves like
.Op Ar shell\-command Op Ar argument ...
.Xc
.D1 Pq alias: Ic splitw
Creates a new pane by splitting
Create a new pane by splitting
.Ar target\-pane .
Shares behavior with
.Ic new\-pane .
If
.Fl d
is given, the session does not make the new pane the current pane.
.Fl Z
zooms if the window is not zoomed, or keeps it zoomed if already zoomed.
.Fl s
sets the style for the pane content.
.Fl S
sets the border style when the pane is active and
.Fl R
sets the border style when the pane is inactive (see
.Sx STYLES ) .
.Fl T
sets the pane title.
.Pp
.Fl h
does a horizontal split and
.Fl v
a vertical split; if neither is specified,
.Fl v
is assumed.
The
.Fl l
option specifies the size of the new pane in lines (for vertical split) or in
columns (for horizontal split);
.Ar size
may be followed by
.Ql %
to specify a percentage of the available space.
.Fl p
is a shorthand option for this.
The
.Fl b
option causes the new pane to be created to the left of or above
.Ar target\-pane .
The
.Fl f
option creates a new pane spanning the full window height (with
.Fl h )
or full window width (with
.Fl v ) ,
instead of splitting the active pane.
.Pp
.Fl k
keeps the pane open after the optional
.Ar shell\-command
exits and waits for a key to be pressed before closing it.
The message shown is controlled by the
.Ic remain\-on\-exit\-format
option.
.Fl m Ar message
is equivalent to
.Fl k
but also sets the
.Ic remain\-on\-exit\-format
option for this pane to
.Ar message .
.Pp
.Fl W
Waits until
.Ar shell\-command
exits, then returns its exit status.
For example:
.Bd -literal -offset indent
$ tmux new-pane -W 'vi afile'
$ echo $?
0
.Ed
.Pp
.Fl E ,
or an empty
.Ar shell\-command ,
(\[aq]\[aq]) will create an empty pane with no command running in it;
.Ic display-message
.Fl I
can write to an empty pane.
The
.Fl I
flag will create an empty pane and forward any output from stdin to it.
For example:
.Bd -literal -offset indent
$ make 2>&1|tmux new\-pane \-dI &
.Ed
.Pp
See
.Ic new\-pane
for more details.
.Tg swapp
.It Xo Ic swap\-pane
.Op Fl dDUZ
@@ -6019,7 +6054,7 @@ and
will fall back to standard ACS line drawing when UTF\-8 is not supported.
.Pp
.It Xo Ic pane\-scrollbars
.Op Ic off | modal | on
.Op Ic off | modal | on | auto\-hide
.Xc
When enabled, a character based scrollbar appears on the left or right
of each pane.
@@ -6029,19 +6064,29 @@ represents the position and size of the visible part of the pane content.
.Pp
If set to
.Ic on
the scrollbar is visible all the time.
the scrollbar is visible all the time and the pane is narrowed by the width of
the scrollbar.
If set to
.Ic modal
the scrollbar only appears when the pane is in copy mode or view mode.
When the scrollbar is visible, the pane is narrowed by the width of the
scrollbar and the text in the pane is reflowed.
the scrollbar only appears when the pane is in copy mode or view mode and
auto-hides when not in use.
If set to
.Ic modal ,
the pane is narrowed only when the scrollbar is visible.
.Ic auto\-hide
the scrollbar is available all the time but auto-hides when not in use.
With
.Ic modal
and
.Ic auto\-hide ,
the scrollbar overlays the pane and does not narrow or reflow the pane.
.Pp
See also
.Ic pane\-scrollbars\-style .
.Pp
.It Xo Ic pane\-scrollbars\-position
.Op Ic left | right
.Xc
Sets which side of the pane to display pane scrollbars on.
.Pp
.It Ic pane\-scrollbars\-style Ar style
Set the scrollbars style.
For how to specify
@@ -6058,10 +6103,13 @@ attribute sets the width of the scrollbar and the
attribute the padding between the scrollbar and the pane.
Other attributes are ignored.
.Pp
.It Xo Ic pane\-scrollbars\-position
.Op Ic left | right
.Xc
Sets which side of the pane to display pane scrollbars on.
.It Ic pane\-scrollbars\-timeout Ar time
Set the time in milliseconds before scrollbars are hidden when
.Ic pane\-scrollbars
is
.Ic modal
or
.Ic auto\-hide .
.Pp
.It Ic pane\-status\-current\-style Ar style
Set status line style for the currently active pane.

9
tmux.c
View File

@@ -286,11 +286,14 @@ clean_name(const char *name, const char* forbid)
{
char *copy, *cp, *new_name;
if (*name == '\0' || !utf8_isvalid(name))
if (!utf8_isvalid(name))
return (NULL);
copy = xstrdup(name);
for (cp = copy; *cp != '\0'; cp++) {
if (strchr(forbid, *cp) != NULL)
if (*cp == '#' && strchr(forbid, '#') != NULL) {
if (cp[1] == '(')
*cp = '_';
} else if (strchr(forbid, *cp) != NULL)
*cp = '_';
}
utf8_stravis(&new_name, copy, VIS_OCTAL|VIS_CSTYLE|VIS_TAB|VIS_NL);
@@ -308,7 +311,7 @@ check_name(const char *name, const char *forbid)
{
const char *cp;
if (*name == '\0' || !utf8_isvalid(name))
if (!utf8_isvalid(name))
return (0);
for (cp = name; *cp != '\0'; cp++) {
if (strchr(forbid, *cp) != NULL)

21
tmux.h
View File

@@ -1300,6 +1300,9 @@ struct window_pane {
u_int sb_slider_y;
u_int sb_slider_h;
int sb_auto_visible;
int sb_auto_hover;
struct event sb_auto_timer;
int argc;
char **argv;
@@ -1474,6 +1477,7 @@ TAILQ_HEAD(winlink_stack, winlink);
#define PANE_SCROLLBARS_OFF 0
#define PANE_SCROLLBARS_MODAL 1
#define PANE_SCROLLBARS_ALWAYS 2
#define PANE_SCROLLBARS_AUTOHIDE 3
/* Pane scrollbars position option. */
#define PANE_SCROLLBARS_RIGHT 0
@@ -3636,6 +3640,14 @@ void window_set_fill_character(struct window *);
void window_pane_default_cursor(struct window_pane *);
int window_pane_mode(struct window_pane *);
int window_pane_show_scrollbar(struct window_pane *, int);
int window_pane_scrollbar_reserve(struct window_pane *, int);
int window_pane_scrollbar_visible(struct window_pane *, int);
int window_pane_scrollbar_overlay(struct window_pane *, int);
int window_pane_scrollbar_overlay_visible(struct window_pane *);
void window_pane_scrollbar_show(struct window_pane *, int);
void window_pane_scrollbar_hide(struct window_pane *);
void window_pane_scrollbar_start_timer(struct window_pane *);
void window_pane_scrollbar_redraw(struct window_pane *);
int window_pane_get_bg(struct window_pane *);
int window_pane_get_fg(struct window_pane *);
int window_pane_get_fg_control_client(struct window_pane *);
@@ -3684,6 +3696,8 @@ void layout_fix_offsets(struct window *);
void layout_fix_panes(struct window *, struct window_pane *);
void layout_resize_adjust(struct window *, struct layout_cell *,
enum layout_type, int);
void layout_resize_set_size(struct window *, struct layout_cell *,
enum layout_type, u_int);
struct layout_cell *layout_cell_get_neighbour(struct layout_cell *);
void layout_init(struct window *, struct window_pane *);
void layout_free(struct window *);
@@ -3698,6 +3712,12 @@ int layout_resize_floating_pane_to(struct window_pane *,
enum layout_type, u_int, char **);
void layout_assign_pane(struct layout_cell *, struct window_pane *,
int);
int layout_split_check_space(struct window_pane *,
struct layout_cell *, enum layout_type);
void layout_split_sizes(struct layout_cell *, int, int,
enum layout_type, u_int *, u_int *, u_int *);
struct layout_cell *layout_replace_with_node(struct window *,
struct layout_cell *, enum layout_type);
struct layout_cell *layout_split_pane(struct window_pane *, enum layout_type,
int, int);
struct layout_cell *layout_floating_pane(struct window *, struct window_pane *,
@@ -3714,6 +3734,7 @@ int layout_floating_args_parse(struct cmdq_item *, struct args *,
enum pane_lines, struct window *, u_int *, u_int *, int *,
int *, char **);
int layout_remove_tile(struct window *, struct layout_cell *);
int layout_insert_tile(struct window *, struct layout_cell *);
/* layout-custom.c */
char *layout_dump(struct window *, struct layout_cell *);

View File

@@ -61,8 +61,10 @@ static int window_copy_line_number_mode(struct window_mode_entry *);
static int window_copy_line_number_is_absolute(struct window_mode_entry *);
static int window_copy_line_numbers_active(struct window_mode_entry *);
static u_int window_copy_line_number_width(struct window_mode_entry *);
static u_int window_copy_cursor_offset(struct window_mode_entry *, u_int, u_int);
static u_int window_copy_cursor_unoffset(struct window_mode_entry *, u_int, u_int);
static u_int window_copy_cursor_offset(struct window_mode_entry *, u_int,
u_int);
static u_int window_copy_cursor_unoffset(struct window_mode_entry *, u_int,
u_int);
static void window_copy_write_line(struct window_mode_entry *,
struct screen_write_ctx *, u_int);
static void window_copy_write_lines(struct window_mode_entry *,
@@ -860,6 +862,7 @@ window_copy_scroll1(struct window_mode_entry *wme, struct window_pane *wp,
if (data->searchmark != NULL && !data->timeout)
window_copy_search_marks(wme, NULL, data->searchregex, 1);
window_copy_update_selection(wme, 1, 0);
window_pane_scrollbar_show(wp, 1);
window_copy_redraw_screen(wme);
}
@@ -913,6 +916,7 @@ window_copy_pageup1(struct window_mode_entry *wme, int half_page)
if (data->searchmark != NULL && !data->timeout)
window_copy_search_marks(wme, NULL, data->searchregex, 1);
window_copy_update_selection(wme, 1, 0);
window_pane_scrollbar_show(wme->wp, 1);
window_copy_redraw_screen(wme);
}
@@ -973,6 +977,7 @@ window_copy_pagedown1(struct window_mode_entry *wme, int half_page,
if (data->searchmark != NULL && !data->timeout)
window_copy_search_marks(wme, NULL, data->searchregex, 1);
window_copy_update_selection(wme, 1, 0);
window_pane_scrollbar_show(wme->wp, 1);
window_copy_redraw_screen(wme);
return (0);
}
@@ -1789,7 +1794,7 @@ window_copy_cmd_history_bottom(struct window_copy_cmd_state *cs)
struct window_mode_entry *wme = cs->wme;
struct window_copy_mode_data *data = wme->data;
struct screen *s = data->backing;
u_int oy;
u_int oy, old_oy = data->oy;
oy = screen_hsize(s) + data->cy - data->oy;
if (data->lineflag == LINE_SEL_RIGHT_LEFT && oy == data->endsely)
@@ -1802,6 +1807,8 @@ window_copy_cmd_history_bottom(struct window_copy_cmd_state *cs)
if (data->searchmark != NULL && !data->timeout)
window_copy_search_marks(wme, NULL, data->searchregex, 1);
window_copy_update_selection(wme, 1, 0);
if (data->oy != old_oy)
window_pane_scrollbar_show(wme->wp, 1);
return (WINDOW_COPY_CMD_REDRAW);
}
@@ -1810,7 +1817,7 @@ window_copy_cmd_history_top(struct window_copy_cmd_state *cs)
{
struct window_mode_entry *wme = cs->wme;
struct window_copy_mode_data *data = wme->data;
u_int oy;
u_int oy, old_oy = data->oy;
oy = screen_hsize(data->backing) + data->cy - data->oy;
if (data->lineflag == LINE_SEL_LEFT_RIGHT && oy == data->sely)
@@ -1823,6 +1830,8 @@ window_copy_cmd_history_top(struct window_copy_cmd_state *cs)
if (data->searchmark != NULL && !data->timeout)
window_copy_search_marks(wme, NULL, data->searchregex, 1);
window_copy_update_selection(wme, 1, 0);
if (data->oy != old_oy)
window_pane_scrollbar_show(wme->wp, 1);
return (WINDOW_COPY_CMD_REDRAW);
}
@@ -3772,7 +3781,7 @@ window_copy_scroll_to(struct window_mode_entry *wme, u_int px, u_int py,
{
struct window_copy_mode_data *data = wme->data;
struct grid *gd = data->backing->grid;
u_int offset, gap;
u_int offset, gap, old_oy = data->oy;
data->cx = px;
@@ -3796,6 +3805,8 @@ window_copy_scroll_to(struct window_mode_entry *wme, u_int px, u_int py,
if (!no_redraw && data->searchmark != NULL && !data->timeout)
window_copy_search_marks(wme, NULL, data->searchregex, 1);
window_copy_update_selection(wme, 1, 0);
if (data->oy != old_oy)
window_pane_scrollbar_show(wme->wp, 1);
if (!no_redraw)
window_copy_redraw_screen(wme);
}
@@ -5154,6 +5165,9 @@ window_copy_write_line(struct window_mode_entry *wme,
else
content_sx = sx;
screen_write_cursormove(ctx, 0, py, 0);
screen_write_clearline(ctx, 8);
ft = format_create_defaults(NULL, NULL, NULL, NULL, wp);
style_apply(&gc, oo, "copy-mode-position-style", ft);
@@ -5275,7 +5289,10 @@ window_copy_redraw_lines(struct window_mode_entry *wme, u_int py, u_int ny)
return;
}
screen_write_start_pane(&ctx, wp, NULL);
if (window_pane_scrollbar_overlay_visible(wp))
screen_write_start(&ctx, &data->screen);
else
screen_write_start_pane(&ctx, wp, NULL);
for (i = py; i < py + ny; i++)
window_copy_write_line(wme, &ctx, i);
screen_write_cursormove(&ctx,
@@ -5283,7 +5300,7 @@ window_copy_redraw_lines(struct window_mode_entry *wme, u_int py, u_int ny)
0);
screen_write_stop(&ctx);
wp->flags |= PANE_REDRAWSCROLLBAR;
window_pane_scrollbar_redraw(wp);
}
static void
@@ -6539,6 +6556,7 @@ window_copy_scroll_up(struct window_mode_entry *wme, u_int ny)
if (ny == 0)
return;
data->oy -= ny;
window_pane_scrollbar_show(wp, 1);
if (data->searchmark != NULL && !data->timeout)
window_copy_search_marks(wme, NULL, data->searchregex, 1);
@@ -6570,7 +6588,10 @@ window_copy_scroll_up(struct window_mode_entry *wme, u_int ny)
return;
}
screen_write_start_pane(&ctx, wp, NULL);
if (window_pane_scrollbar_overlay_visible(wp))
screen_write_start(&ctx, &data->screen);
else
screen_write_start_pane(&ctx, wp, NULL);
screen_write_cursormove(&ctx, 0, 0, 0);
screen_write_deleteline(&ctx, ny, 8);
window_copy_write_lines(wme, &ctx, screen_size_y(s) - ny, ny);
@@ -6585,7 +6606,7 @@ window_copy_scroll_up(struct window_mode_entry *wme, u_int ny)
window_copy_cursor_offset(wme, data->cx, screen_size_x(s)), data->cy,
0);
screen_write_stop(&ctx);
wp->flags |= PANE_REDRAWSCROLLBAR;
window_pane_scrollbar_redraw(wp);
}
static void
@@ -6604,6 +6625,7 @@ window_copy_scroll_down(struct window_mode_entry *wme, u_int ny)
if (ny == 0)
return;
data->oy += ny;
window_pane_scrollbar_show(wp, 1);
if (data->searchmark != NULL && !data->timeout)
window_copy_search_marks(wme, NULL, data->searchregex, 1);
@@ -6630,7 +6652,10 @@ window_copy_scroll_down(struct window_mode_entry *wme, u_int ny)
return;
}
screen_write_start_pane(&ctx, wp, NULL);
if (window_pane_scrollbar_overlay_visible(wp))
screen_write_start(&ctx, &data->screen);
else
screen_write_start_pane(&ctx, wp, NULL);
screen_write_cursormove(&ctx, 0, 0, 0);
screen_write_insertline(&ctx, ny, 8);
window_copy_write_lines(wme, &ctx, 0, ny);
@@ -6641,7 +6666,7 @@ window_copy_scroll_down(struct window_mode_entry *wme, u_int ny)
screen_write_cursormove(&ctx, window_copy_cursor_offset(wme, data->cx,
screen_size_x(s)), data->cy, 0);
screen_write_stop(&ctx);
wp->flags |= PANE_REDRAWSCROLLBAR;
window_pane_scrollbar_redraw(wp);
}
static void

View File

@@ -1138,6 +1138,7 @@ window_tree_init(struct window_mode_entry *wme, struct cmd_find_state *fs,
window_tree_get_key, window_tree_swap, window_tree_sort,
window_tree_help, data, window_tree_menu_items, &s);
mode_tree_zoom(data->data, args);
mode_tree_view_name(data->data, "preview");
mode_tree_build(data->data);
mode_tree_draw(data->data);

View File

@@ -126,7 +126,7 @@ window_visible_ranges(struct window_pane *base_wp, int px, int py, u_int width,
continue;
sb_w = wp->scrollbar_style.width + wp->scrollbar_style.pad;
if (!window_pane_show_scrollbar(wp, sb))
if (!window_pane_scrollbar_reserve(wp, sb))
sb_w = sb_pos = 0;
for (i = 0; i < r->used; i++) {

134
window.c
View File

@@ -71,6 +71,7 @@ struct window_pane_input_data {
static struct window_pane *window_pane_create(struct window *, u_int, u_int,
u_int);
static void window_pane_destroy(struct window_pane *);
static void window_pane_scrollbar_timer(int, short, void *);
static void window_pane_full_size_offset(struct window_pane *wp,
int *xoff, int *yoff, u_int *sx, u_int *sy);
@@ -631,6 +632,7 @@ window_redraw_active_switch(struct window *w, struct window_pane *wp)
TAILQ_REMOVE(&w->z_index, wp, zentry);
TAILQ_INSERT_HEAD(&w->z_index, wp, zentry);
wp->flags |= PANE_REDRAW;
redraw_invalidate_scene(w);
}
wp = w->active;
@@ -1111,6 +1113,7 @@ window_pane_create(struct window *w, u_int sx, u_int sy, u_int hlimit)
screen_init(&wp->status_screen, 1, 1, 0);
style_ranges_init(&wp->border_status_line.ranges);
evtimer_set(&wp->sb_auto_timer, window_pane_scrollbar_timer, wp);
if (gethostname(host, sizeof host) == 0)
screen_set_title(&wp->base, host, 0);
@@ -1157,6 +1160,52 @@ window_pane_free_modes(struct window_pane *wp)
wp->screen = &wp->base;
}
static void
window_pane_scrollbar_timer(__unused int fd, __unused short events, void *arg)
{
struct window_pane *wp = arg;
wp->sb_auto_hover = 0;
window_pane_scrollbar_hide(wp);
}
static int
window_pane_scrollbar_auto_hide(struct window_pane *wp)
{
int sb;
sb = options_get_number(wp->window->options, "pane-scrollbars");
return (sb == PANE_SCROLLBARS_MODAL || sb == PANE_SCROLLBARS_AUTOHIDE);
}
int
window_pane_scrollbar_overlay_visible(struct window_pane *wp)
{
int sb;
sb = options_get_number(wp->window->options, "pane-scrollbars");
return (window_pane_scrollbar_overlay(wp, sb) &&
window_pane_scrollbar_visible(wp, sb));
}
void
window_pane_scrollbar_redraw(struct window_pane *wp)
{
if (window_pane_scrollbar_overlay_visible(wp)) {
wp->flags |= PANE_REDRAW;
return;
}
wp->flags |= PANE_REDRAWSCROLLBAR;
}
static void
window_pane_scrollbar_redraw_visibility(struct window_pane *wp)
{
redraw_invalidate_scene(wp->window);
wp->flags |= PANE_REDRAW;
server_redraw_window(wp->window);
}
static void
window_pane_destroy(struct window_pane *wp)
{
@@ -1192,6 +1241,8 @@ window_pane_destroy(struct window_pane *wp)
event_del(&wp->resize_timer);
if (event_initialized(&wp->sync_timer))
event_del(&wp->sync_timer);
if (event_initialized(&wp->sb_auto_timer))
event_del(&wp->sb_auto_timer);
window_pane_clear_resizes(wp, NULL);
RB_REMOVE(window_pane_tree, &all_window_panes, wp);
@@ -1261,7 +1312,8 @@ window_pane_set_event(struct window_pane *wp)
}
void
window_pane_clear_resizes(struct window_pane *wp, struct window_pane_resize *except)
window_pane_clear_resizes(struct window_pane *wp,
struct window_pane_resize *except)
{
struct window_pane_resize *r, *r1;
@@ -1740,7 +1792,7 @@ window_pane_full_size_offset(struct window_pane *wp, int *xoff, int *yoff,
pane_scrollbars = options_get_number(w->options, "pane-scrollbars");
sb_pos = options_get_number(w->options, "pane-scrollbars-position");
if (window_pane_show_scrollbar(wp, pane_scrollbars))
if (window_pane_scrollbar_reserve(wp, pane_scrollbars))
sb_w = wp->scrollbar_style.width + wp->scrollbar_style.pad;
else
sb_w = 0;
@@ -2167,12 +2219,90 @@ window_pane_show_scrollbar(struct window_pane *wp, int sb_option)
if (SCREEN_IS_ALTERNATE(&wp->base))
return (0);
if (sb_option == PANE_SCROLLBARS_ALWAYS ||
sb_option == PANE_SCROLLBARS_AUTOHIDE ||
(sb_option == PANE_SCROLLBARS_MODAL &&
window_pane_mode(wp) != WINDOW_PANE_NO_MODE))
return (1);
return (0);
}
int
window_pane_scrollbar_reserve(struct window_pane *wp, int sb_option)
{
if (!window_pane_show_scrollbar(wp, sb_option))
return (0);
return (sb_option == PANE_SCROLLBARS_ALWAYS);
}
int
window_pane_scrollbar_overlay(struct window_pane *wp, int sb_option)
{
if (!window_pane_show_scrollbar(wp, sb_option))
return (0);
return (window_pane_scrollbar_auto_hide(wp));
}
int
window_pane_scrollbar_visible(struct window_pane *wp, int sb_option)
{
if (!window_pane_show_scrollbar(wp, sb_option))
return (0);
if (!window_pane_scrollbar_auto_hide(wp))
return (1);
return (wp->sb_auto_visible);
}
void
window_pane_scrollbar_start_timer(struct window_pane *wp)
{
struct timeval tv;
u_int delay;
if (!window_pane_scrollbar_auto_hide(wp) || !wp->sb_auto_visible)
return;
delay = options_get_number(wp->window->options,
"pane-scrollbars-timeout");
tv.tv_sec = delay / 1000;
tv.tv_usec = (delay % 1000) * 1000L;
evtimer_del(&wp->sb_auto_timer);
evtimer_add(&wp->sb_auto_timer, &tv);
}
void
window_pane_scrollbar_show(struct window_pane *wp, int start_timer)
{
int changed = 0;
int sb;
if (!window_pane_scrollbar_auto_hide(wp))
return;
sb = options_get_number(wp->window->options, "pane-scrollbars");
if (!window_pane_show_scrollbar(wp, sb))
return;
if (!wp->sb_auto_visible) {
wp->sb_auto_visible = 1;
changed = 1;
}
evtimer_del(&wp->sb_auto_timer);
if (start_timer)
window_pane_scrollbar_start_timer(wp);
if (changed)
window_pane_scrollbar_redraw_visibility(wp);
}
void
window_pane_scrollbar_hide(struct window_pane *wp)
{
if (event_initialized(&wp->sb_auto_timer))
evtimer_del(&wp->sb_auto_timer);
wp->sb_auto_hover = 0;
if (!wp->sb_auto_visible)
return;
wp->sb_auto_visible = 0;
window_pane_scrollbar_redraw_visibility(wp);
}
int
window_pane_get_bg(struct window_pane *wp)
{