Merge master.

This commit is contained in:
Michael Grant
2026-01-23 09:29:39 +00:00
14 changed files with 294 additions and 118 deletions

View File

@@ -20,17 +20,23 @@ LDADD = $(LIBOBJS)
# Set flags for gcc. # Set flags for gcc.
if IS_GCC if IS_GCC
AM_CFLAGS += -std=gnu99 -O2 AM_CFLAGS += -std=gnu99
if IS_OPTIMIZED
AM_CFLAGS += -O2
else
AM_CFLAGS += -O0
endif
if IS_DEBUG if IS_DEBUG
AM_CFLAGS += -g AM_CFLAGS += -g
AM_CFLAGS += -Wno-long-long -Wall -W -Wformat=2 AM_CFLAGS += -Wno-long-long -Wall -W -Wformat=2 -Wno-use-after-free
AM_CFLAGS += -Wmissing-prototypes -Wstrict-prototypes -Wmissing-declarations AM_CFLAGS += -Wmissing-prototypes -Wstrict-prototypes -Wmissing-declarations
AM_CFLAGS += -Wwrite-strings -Wshadow -Wpointer-arith -Wsign-compare AM_CFLAGS += -Wwrite-strings -Wshadow -Wpointer-arith -Wsign-compare
AM_CFLAGS += -Wundef -Wbad-function-cast -Winline -Wcast-align AM_CFLAGS += -Wundef -Wbad-function-cast -Winline -Wno-cast-align
AM_CFLAGS += -Wdeclaration-after-statement -Wno-pointer-sign -Wno-attributes AM_CFLAGS += -Wdeclaration-after-statement -Wno-pointer-sign -Wno-attributes
AM_CFLAGS += -Wno-unused-result -Wno-format-y2k AM_CFLAGS += -Wno-unused-result -Wno-format-y2k -Wno-unknown-warning-option
AM_CFLAGS += -Wno-maybe-uninitialized
if IS_DARWIN if IS_DARWIN
AM_CFLAGS += -Wno-deprecated-declarations -Wno-cast-align -Wno-macro-redefined AM_CFLAGS += -Wno-deprecated-declarations -Wno-macro-redefined
endif endif
AM_CPPFLAGS += -DDEBUG AM_CPPFLAGS += -DDEBUG
endif endif

View File

@@ -42,8 +42,8 @@ const struct cmd_entry cmd_command_prompt_entry = {
.name = "command-prompt", .name = "command-prompt",
.alias = NULL, .alias = NULL,
.args = { "1bFkliI:Np:t:T:", 0, 1, cmd_command_prompt_args_parse }, .args = { "1beFiklI:Np:t:T:", 0, 1, cmd_command_prompt_args_parse },
.usage = "[-1bFkliN] [-I inputs] [-p prompts] " CMD_TARGET_CLIENT_USAGE .usage = "[-1beFiklN] [-I inputs] [-p prompts] " CMD_TARGET_CLIENT_USAGE
" [-T prompt-type] [template]", " [-T prompt-type] [template]",
.flags = CMD_CLIENT_TFLAG, .flags = CMD_CLIENT_TFLAG,
@@ -84,7 +84,7 @@ cmd_command_prompt_exec(struct cmd *self, struct cmdq_item *item)
struct client *tc = cmdq_get_target_client(item); struct client *tc = cmdq_get_target_client(item);
struct cmd_find_state *target = cmdq_get_target(item); struct cmd_find_state *target = cmdq_get_target(item);
const char *type, *s, *input; const char *type, *s, *input;
struct cmd_command_prompt_cdata *cdata; struct cmd_command_prompt_cdata *cdata;
char *tmp, *prompts, *prompt, *next_prompt; char *tmp, *prompts, *prompt, *next_prompt;
char *inputs = NULL, *next_input; char *inputs = NULL, *next_input;
u_int count = args_count(args); u_int count = args_count(args);
@@ -163,6 +163,8 @@ cmd_command_prompt_exec(struct cmd *self, struct cmdq_item *item)
cdata->flags |= PROMPT_INCREMENTAL; cdata->flags |= PROMPT_INCREMENTAL;
else if (args_has(args, 'k')) else if (args_has(args, 'k'))
cdata->flags |= PROMPT_KEY; cdata->flags |= PROMPT_KEY;
else if (args_has(args, 'e'))
cdata->flags |= PROMPT_BSPACE_EXIT;
status_prompt_set(tc, target, cdata->prompts[0].prompt, status_prompt_set(tc, target, cdata->prompts[0].prompt,
cdata->prompts[0].input, cmd_command_prompt_callback, cdata->prompts[0].input, cmd_command_prompt_callback,
cmd_command_prompt_free, cdata, cdata->flags, cdata->prompt_type); cmd_command_prompt_free, cdata, cdata->flags, cdata->prompt_type);
@@ -234,7 +236,7 @@ out:
static void static void
cmd_command_prompt_free(void *data) cmd_command_prompt_free(void *data)
{ {
struct cmd_command_prompt_cdata *cdata = data; struct cmd_command_prompt_cdata *cdata = data;
u_int i; u_int i;
for (i = 0; i < cdata->count; i++) { for (i = 0; i < cdata->count; i++) {

View File

@@ -64,6 +64,15 @@ AC_ARG_ENABLE(
) )
AM_CONDITIONAL(IS_DEBUG, test "x$enable_debug" = xyes) AM_CONDITIONAL(IS_DEBUG, test "x$enable_debug" = xyes)
# Is this --enable-optimizations?
AC_ARG_ENABLE(
optimizations,
AS_HELP_STRING(--enable-optimizations, enable optimization build flags),
,
enable_optimizations=yes
)
AM_CONDITIONAL(IS_OPTIMIZED, test "x$enable_optimizations" = xyes)
# Is this a static build? # Is this a static build?
AC_ARG_ENABLE( AC_ARG_ENABLE(
static, static,

View File

@@ -454,6 +454,20 @@ format_job_tidy(struct format_job_tree *jobs, int force)
} }
} }
/* Work around needless -Wformat-nonliteral gcc warning. */
#ifdef __GNUC__
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wformat-nonliteral"
#endif
static size_t
format_strftime(char *s, size_t max, const char *fmt, const struct tm *tm)
{
return (strftime(s, max, fmt, tm));
}
#ifdef __GNUC__
#pragma GCC diagnostic pop
#endif
/* Tidy old jobs for all clients. */ /* Tidy old jobs for all clients. */
void void
format_tidy_jobs(void) format_tidy_jobs(void)
@@ -3963,7 +3977,7 @@ found:
else { else {
if (time_format != NULL) { if (time_format != NULL) {
localtime_r(&t, &tm); localtime_r(&t, &tm);
strftime(s, sizeof s, time_format, &tm); format_strftime(s, sizeof s, time_format, &tm);
} else { } else {
ctime_r(&t, s); ctime_r(&t, s);
s[strcspn(s, "\n")] = '\0'; s[strcspn(s, "\n")] = '\0';
@@ -5540,7 +5554,8 @@ format_expand1(struct format_expand_state *es, const char *fmt)
es->time = time(NULL); es->time = time(NULL);
localtime_r(&es->time, &es->tm); localtime_r(&es->time, &es->tm);
} }
if (strftime(expanded, sizeof expanded, fmt, &es->tm) == 0) { if (format_strftime(expanded, sizeof expanded, fmt,
&es->tm) == 0) {
format_log(es, "format is too long"); format_log(es, "format is too long");
return (xstrdup("")); return (xstrdup(""));
} }

View File

@@ -49,6 +49,8 @@
" '#{?#{m/r:(copy|view)-mode,#{pane_mode}},Go To Top,}' '<' {send -X history-top}" \ " '#{?#{m/r:(copy|view)-mode,#{pane_mode}},Go To Top,}' '<' {send -X history-top}" \
" '#{?#{m/r:(copy|view)-mode,#{pane_mode}},Go To Bottom,}' '>' {send -X history-bottom}" \ " '#{?#{m/r:(copy|view)-mode,#{pane_mode}},Go To Bottom,}' '>' {send -X history-bottom}" \
" ''" \ " ''" \
" '#{?#{&&:#{buffer_size},#{!:#{pane_in_mode}}},Paste #[underscore]#{=/9/...:buffer_sample},}' 'p' {paste-buffer}" \
" ''" \
" '#{?mouse_word,Search For #[underscore]#{=/9/...:mouse_word},}' 'C-r' {if -F '#{?#{m/r:(copy|view)-mode,#{pane_mode}},0,1}' 'copy-mode -t='; send -Xt= search-backward -- \"#{q:mouse_word}\"}" \ " '#{?mouse_word,Search For #[underscore]#{=/9/...:mouse_word},}' 'C-r' {if -F '#{?#{m/r:(copy|view)-mode,#{pane_mode}},0,1}' 'copy-mode -t='; send -Xt= search-backward -- \"#{q:mouse_word}\"}" \
" '#{?mouse_word,Type #[underscore]#{=/9/...:mouse_word},}' 'C-y' {copy-mode -q; send-keys -l -- \"#{q:mouse_word}\"}" \ " '#{?mouse_word,Type #[underscore]#{=/9/...:mouse_word},}' 'C-y' {copy-mode -q; send-keys -l -- \"#{q:mouse_word}\"}" \
" '#{?mouse_word,Copy #[underscore]#{=/9/...:mouse_word},}' 'c' {copy-mode -q; set-buffer -- \"#{q:mouse_word}\"}" \ " '#{?mouse_word,Copy #[underscore]#{=/9/...:mouse_word},}' 'c' {copy-mode -q; set-buffer -- \"#{q:mouse_word}\"}" \

52
regress/border-arrows.sh Normal file
View File

@@ -0,0 +1,52 @@
#!/bin/sh
# Test for GitHub issue #4780 - pane-border-indicators both arrows missing
# on second pane in a two-pane horizontal split.
#
# When pane-border-indicators is set to "both", arrow indicators should
# appear when EITHER pane is selected. Before the fix, arrows only appeared
# when the LEFT pane was selected.
PATH=/bin:/usr/bin
TERM=screen
[ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux)
TMUX="$TEST_TMUX -Ltest"
$TMUX kill-server 2>/dev/null
TMUX_OUTER="$TEST_TMUX -Ltest2"
$TMUX_OUTER kill-server 2>/dev/null
trap "$TMUX kill-server 2>/dev/null; $TMUX_OUTER kill-server 2>/dev/null" 0 1 15
# Start outer tmux that will capture the inner tmux's rendering
$TMUX_OUTER -f/dev/null new -d -x80 -y24 "$TMUX -f/dev/null new -x78 -y22" || exit 1
sleep 1
# Set pane-border-indicators to "both" in inner tmux
$TMUX set -g pane-border-indicators both || exit 1
# Create horizontal split (two panes side by side)
$TMUX splitw -h || exit 1
sleep 1
# Helper to check for arrow characters in captured output
has_arrow() {
echo "$1" | grep -qE '(←|→|↑|↓)'
}
# Test 1: Select left pane (pane 0) and check for arrows
$TMUX selectp -t 0
sleep 1
left_output=$($TMUX_OUTER capturep -Cep 2>/dev/null)
has_arrow "$left_output" || exit 1
# Test 2: Select right pane (pane 1) and check for arrows
# This is the case that failed before the fix
$TMUX selectp -t 1
sleep 1
right_output=$($TMUX_OUTER capturep -Cep 2>/dev/null)
has_arrow "$right_output" || exit 1
$TMUX kill-server 2>/dev/null
$TMUX_OUTER kill-server 2>/dev/null
exit 0

View File

@@ -98,7 +98,7 @@ screen_redraw_border_set(struct window *w, struct window_pane *wp,
} }
} }
/* Return if window has only two panes. */ /* Return 1 if window has only two panes. */
static int static int
screen_redraw_two_panes(struct window *w, enum layout_type *type) screen_redraw_two_panes(struct window *w, enum layout_type *type)
{ {
@@ -196,7 +196,7 @@ screen_redraw_pane_border(struct screen_redraw_ctx *ctx, struct window_pane *wp,
if ((int)px == wp->xoff + (int)wp->sx + sb_w - 1) if ((int)px == wp->xoff + (int)wp->sx + sb_w - 1)
return (SCREEN_REDRAW_BORDER_RIGHT); return (SCREEN_REDRAW_BORDER_RIGHT);
} }
} else { /* sb_pos == PANE_SCROLLBARS_RIGHT or none. */ } else { /* sb_pos == PANE_SCROLLBARS_RIGHT or disabled */
if (wp->xoff == 0 && px == wp->sx + sb_w) if (wp->xoff == 0 && px == wp->sx + sb_w)
if (!hsplit || (hsplit && py <= wp->sy / 2)) if (!hsplit || (hsplit && py <= wp->sy / 2))
return (SCREEN_REDRAW_BORDER_RIGHT); return (SCREEN_REDRAW_BORDER_RIGHT);
@@ -900,6 +900,79 @@ screen_redraw_draw_borders_style(struct screen_redraw_ctx *ctx, u_int x,
return (&wp->border_gc); return (&wp->border_gc);
} }
/* Draw arrow indicator if enabled. */
static void
screen_redraw_draw_border_arrows(struct screen_redraw_ctx *ctx, u_int i,
u_int j, u_int cell_type, struct window_pane *wp,
struct window_pane *active, struct grid_cell *gc)
{
struct client *c = ctx->c;
struct session *s = c->session;
struct window *w = s->curw->window;
struct options *oo = w->options;
u_int x = ctx->ox + i, y = ctx->oy + j;
int value, arrows = 0, border;
enum layout_type type;
if (wp == NULL)
return;
if ((int)i != wp->xoff + 1 && (int)j != wp->yoff + 1)
return;
value = options_get_number(oo, "pane-border-indicators");
if (value != PANE_BORDER_ARROWS && value != PANE_BORDER_BOTH)
return;
border = screen_redraw_pane_border(ctx, active, x, y);
if (border == SCREEN_REDRAW_INSIDE)
return;
if ((int)i == wp->xoff + 1) {
if (border == SCREEN_REDRAW_OUTSIDE) {
if (screen_redraw_two_panes(wp->window, &type)) {
if (active == TAILQ_FIRST(&w->panes))
border = SCREEN_REDRAW_BORDER_BOTTOM;
else
border = SCREEN_REDRAW_BORDER_TOP;
arrows = 1;
}
} else {
if (cell_type == CELL_LEFTRIGHT)
arrows = 1;
else if (cell_type == CELL_TOPJOIN &&
border == SCREEN_REDRAW_BORDER_BOTTOM)
arrows = 1;
else if (cell_type == CELL_BOTTOMJOIN &&
border == SCREEN_REDRAW_BORDER_TOP)
arrows = 1;
}
}
if ((int)j == wp->yoff + 1) {
if (border == SCREEN_REDRAW_OUTSIDE) {
if (screen_redraw_two_panes(wp->window, 0)) {
if (active == TAILQ_FIRST(&w->panes))
border = SCREEN_REDRAW_BORDER_RIGHT;
else
border = SCREEN_REDRAW_BORDER_LEFT;
arrows = 1;
}
} else {
if (cell_type == CELL_TOPBOTTOM)
arrows = 1;
else if (cell_type == CELL_LEFTJOIN &&
border == SCREEN_REDRAW_BORDER_RIGHT)
arrows = 1;
else if (cell_type == CELL_RIGHTJOIN &&
border == SCREEN_REDRAW_BORDER_LEFT)
arrows = 1;
}
}
if (arrows) {
gc->attr |= GRID_ATTR_CHARSET;
utf8_set(&gc->data, BORDER_MARKERS[border]);
}
}
/* Draw a border cell. */ /* Draw a border cell. */
static void static void
screen_redraw_draw_borders_cell(struct screen_redraw_ctx *ctx, u_int i, u_int j) screen_redraw_draw_borders_cell(struct screen_redraw_ctx *ctx, u_int i, u_int j)
@@ -915,7 +988,7 @@ screen_redraw_draw_borders_cell(struct screen_redraw_ctx *ctx, u_int i, u_int j)
const struct grid_cell *tmp; const struct grid_cell *tmp;
struct overlay_ranges r; struct overlay_ranges r;
u_int cell_type, x = ctx->ox + i, y = ctx->oy + j; u_int cell_type, x = ctx->ox + i, y = ctx->oy + j;
int arrows = 0, border, isolates; int isolates;
if (c->overlay_check != NULL) { if (c->overlay_check != NULL) {
c->overlay_check(c, c->overlay_data, x, y, 1, &r); c->overlay_check(c, c->overlay_data, x, y, 1, &r);
@@ -963,32 +1036,7 @@ screen_redraw_draw_borders_cell(struct screen_redraw_ctx *ctx, u_int i, u_int j)
if (isolates) if (isolates)
tty_puts(tty, END_ISOLATE); tty_puts(tty, END_ISOLATE);
switch (options_get_number(oo, "pane-border-indicators")) { screen_redraw_draw_border_arrows(ctx, i, j, cell_type, wp, active, &gc);
case PANE_BORDER_ARROWS:
case PANE_BORDER_BOTH:
arrows = 1;
break;
}
if (wp != NULL && arrows) {
border = screen_redraw_pane_border(ctx, active, x, y);
if ((((int)i == wp->xoff + 1 &&
(cell_type == CELL_LEFTRIGHT ||
(cell_type == CELL_TOPJOIN &&
border == SCREEN_REDRAW_BORDER_BOTTOM) ||
(cell_type == CELL_BOTTOMJOIN &&
border == SCREEN_REDRAW_BORDER_TOP))) ||
((int)j == wp->yoff + 1 &&
(cell_type == CELL_TOPBOTTOM ||
(cell_type == CELL_LEFTJOIN &&
border == SCREEN_REDRAW_BORDER_RIGHT) ||
(cell_type == CELL_RIGHTJOIN &&
border == SCREEN_REDRAW_BORDER_LEFT)))) &&
screen_redraw_check_is(ctx, x, y, active)) {
gc.attr |= GRID_ATTR_CHARSET;
utf8_set(&gc.data, BORDER_MARKERS[border]);
}
}
tty_cell(tty, &gc, &grid_default_cell, NULL, NULL); tty_cell(tty, &gc, &grid_default_cell, NULL, NULL);
if (isolates) if (isolates)

View File

@@ -25,6 +25,8 @@
static struct screen_write_citem *screen_write_collect_trim( static struct screen_write_citem *screen_write_collect_trim(
struct screen_write_ctx *, u_int, u_int, u_int, int *); struct screen_write_ctx *, u_int, u_int, u_int, int *);
static void screen_write_collect_insert(struct screen_write_ctx *,
struct screen_write_citem *);
static void screen_write_collect_clear(struct screen_write_ctx *, u_int, static void screen_write_collect_clear(struct screen_write_ctx *, u_int,
u_int); u_int);
static void screen_write_collect_scroll(struct screen_write_ctx *, u_int); static void screen_write_collect_scroll(struct screen_write_ctx *, u_int);
@@ -1334,7 +1336,7 @@ screen_write_clearendofline(struct screen_write_ctx *ctx, u_int bg)
struct screen *s = ctx->s; struct screen *s = ctx->s;
struct grid_line *gl; struct grid_line *gl;
u_int sx = screen_size_x(s); u_int sx = screen_size_x(s);
struct screen_write_citem *ci = ctx->item, *before; struct screen_write_citem *ci = ctx->item;
if (s->cx == 0) { if (s->cx == 0) {
screen_write_clearline(ctx, bg); screen_write_clearline(ctx, bg);
@@ -1352,16 +1354,11 @@ screen_write_clearendofline(struct screen_write_ctx *ctx, u_int bg)
grid_view_clear(s->grid, s->cx, s->cy, sx - s->cx, 1, bg); grid_view_clear(s->grid, s->cx, s->cy, sx - s->cx, 1, bg);
before = screen_write_collect_trim(ctx, s->cy, s->cx, sx - s->cx, NULL);
ci->x = s->cx; ci->x = s->cx;
ci->used = sx - s->cx; ci->used = sx - s->cx;
ci->type = CLEAR; ci->type = CLEAR;
ci->bg = bg; ci->bg = bg;
if (before == NULL) screen_write_collect_insert(ctx, ci);
TAILQ_INSERT_TAIL(&ctx->s->write_list[s->cy].items, ci, entry);
else
TAILQ_INSERT_BEFORE(before, ci, entry);
ctx->item = screen_write_get_citem();
} }
/* Clear to start of line from cursor. */ /* Clear to start of line from cursor. */
@@ -1370,7 +1367,7 @@ screen_write_clearstartofline(struct screen_write_ctx *ctx, u_int bg)
{ {
struct screen *s = ctx->s; struct screen *s = ctx->s;
u_int sx = screen_size_x(s); u_int sx = screen_size_x(s);
struct screen_write_citem *ci = ctx->item, *before; struct screen_write_citem *ci = ctx->item;
if (s->cx >= sx - 1) { if (s->cx >= sx - 1) {
screen_write_clearline(ctx, bg); screen_write_clearline(ctx, bg);
@@ -1387,16 +1384,11 @@ screen_write_clearstartofline(struct screen_write_ctx *ctx, u_int bg)
else else
grid_view_clear(s->grid, 0, s->cy, s->cx + 1, 1, bg); grid_view_clear(s->grid, 0, s->cy, s->cx + 1, 1, bg);
before = screen_write_collect_trim(ctx, s->cy, 0, s->cx + 1, NULL);
ci->x = 0; ci->x = 0;
ci->used = s->cx + 1; ci->used = s->cx + 1;
ci->type = CLEAR; ci->type = CLEAR;
ci->bg = bg; ci->bg = bg;
if (before == NULL) screen_write_collect_insert(ctx, ci);
TAILQ_INSERT_TAIL(&ctx->s->write_list[s->cy].items, ci, entry);
else
TAILQ_INSERT_BEFORE(before, ci, entry);
ctx->item = screen_write_get_citem();
} }
/* Move cursor to px,py. */ /* Move cursor to px,py. */
@@ -1892,6 +1884,8 @@ screen_write_collect_flush(struct screen_write_ctx *ctx, int scroll_only,
last = UINT_MAX; last = UINT_MAX;
TAILQ_FOREACH_SAFE(ci, &cl->items, entry, tmp) { TAILQ_FOREACH_SAFE(ci, &cl->items, entry, tmp) {
log_debug("collect list: x=%u (last %u), y=%u, used=%u",
ci->x, last, y, ci->used);
if (last != UINT_MAX && ci->x <= last) { if (last != UINT_MAX && ci->x <= last) {
fatalx("collect list not in order: %u <= %u", fatalx("collect list not in order: %u <= %u",
ci->x, last); ci->x, last);
@@ -1948,29 +1942,39 @@ screen_write_collect_flush(struct screen_write_ctx *ctx, int scroll_only,
log_debug("%s: flushed %u items (%s)", __func__, items, from); log_debug("%s: flushed %u items (%s)", __func__, items, from);
} }
/* Finish and store collected cells. */ /* Insert an item on current line. */
void void
screen_write_collect_end(struct screen_write_ctx *ctx) screen_write_collect_insert(struct screen_write_ctx *ctx,
struct screen_write_citem *ci)
{ {
struct screen *s = ctx->s; struct screen *s = ctx->s;
struct screen_write_citem *ci = ctx->item, *nci, *before;
struct screen_write_cline *cl = &s->write_list[s->cy]; struct screen_write_cline *cl = &s->write_list[s->cy];
struct grid_cell gc; struct screen_write_citem *before;
u_int xx;
int wrapped = ci->wrapped;
if (ci->used == 0) before = screen_write_collect_trim(ctx, s->cy, ci->x, ci->used,
return; &ci->wrapped);
before = screen_write_collect_trim(ctx, s->cy, s->cx, ci->used,
&wrapped);
ci->x = s->cx;
ci->wrapped = wrapped;
if (before == NULL) if (before == NULL)
TAILQ_INSERT_TAIL(&cl->items, ci, entry); TAILQ_INSERT_TAIL(&cl->items, ci, entry);
else else
TAILQ_INSERT_BEFORE(before, ci, entry); TAILQ_INSERT_BEFORE(before, ci, entry);
ctx->item = screen_write_get_citem(); ctx->item = screen_write_get_citem();
}
/* Finish and store collected cells. */
void
screen_write_collect_end(struct screen_write_ctx *ctx)
{
struct screen *s = ctx->s;
struct screen_write_citem *ci = ctx->item, *bci = NULL, *aci;
struct screen_write_cline *cl = &s->write_list[s->cy];
struct grid_cell gc;
u_int xx;
if (ci->used == 0)
return;
ci->x = s->cx;
screen_write_collect_insert(ctx, ci);
log_debug("%s: %u %.*s (at %u,%u)", __func__, ci->used, log_debug("%s: %u %.*s (at %u,%u)", __func__, ci->used,
(int)ci->used, cl->data + ci->x, s->cx, s->cy); (int)ci->used, cl->data + ci->x, s->cx, s->cy);
@@ -1982,27 +1986,28 @@ screen_write_collect_end(struct screen_write_ctx *ctx)
break; break;
grid_view_set_cell(s->grid, xx, s->cy, grid_view_set_cell(s->grid, xx, s->cy,
&grid_default_cell); &grid_default_cell);
log_debug("%s: padding erased (before) at %u", log_debug("%s: padding erased (before) at %u (cx %u)",
__func__, xx); __func__, xx, s->cx);
} }
if (xx != s->cx) { if (xx != s->cx) {
if (xx == 0) if (xx == 0)
grid_view_get_cell(s->grid, 0, s->cy, &gc); grid_view_get_cell(s->grid, 0, s->cy, &gc);
if (gc.data.width > 1) { if (gc.data.width > 1 ||
(gc.flags & GRID_FLAG_PADDING)) {
grid_view_set_cell(s->grid, xx, s->cy, grid_view_set_cell(s->grid, xx, s->cy,
&grid_default_cell); &grid_default_cell);
log_debug("%s: padding erased (before) at %u", log_debug("%s: padding erased (before) at %u "
__func__, xx); "(cx %u)", __func__, xx, s->cx);
} }
} }
if (xx != s->cx) { if (xx != s->cx) {
nci = ctx->item; bci = ctx->item;
nci->type = CLEAR; bci->type = CLEAR;
nci->x = xx; bci->x = xx;
nci->bg = 8; bci->bg = 8;
nci->used = s->cx - xx; bci->used = s->cx - xx;
TAILQ_INSERT_BEFORE(ci, nci, entry); log_debug("%s: padding erased (before): from %u, "
ctx->item = screen_write_get_citem(); "size %u", __func__, bci->x, bci->used);
} }
} }
@@ -2013,6 +2018,8 @@ screen_write_collect_end(struct screen_write_ctx *ctx)
grid_view_set_cells(s->grid, s->cx, s->cy, &ci->gc, cl->data + ci->x, grid_view_set_cells(s->grid, s->cx, s->cy, &ci->gc, cl->data + ci->x,
ci->used); ci->used);
if (bci != NULL)
screen_write_collect_insert(ctx, bci);
screen_write_set_cursor(ctx, s->cx + ci->used, -1); screen_write_set_cursor(ctx, s->cx + ci->used, -1);
for (xx = s->cx; xx < screen_size_x(s); xx++) { for (xx = s->cx; xx < screen_size_x(s); xx++) {
@@ -2020,16 +2027,18 @@ screen_write_collect_end(struct screen_write_ctx *ctx)
if (~gc.flags & GRID_FLAG_PADDING) if (~gc.flags & GRID_FLAG_PADDING)
break; break;
grid_view_set_cell(s->grid, xx, s->cy, &grid_default_cell); grid_view_set_cell(s->grid, xx, s->cy, &grid_default_cell);
log_debug("%s: padding erased (after) at %u", __func__, xx); log_debug("%s: padding erased (after) at %u (cx %u)",
__func__, xx, s->cx);
} }
if (xx != s->cx) { if (xx != s->cx) {
nci = ctx->item; aci = ctx->item;
nci->type = CLEAR; aci->type = CLEAR;
nci->x = s->cx; aci->x = s->cx;
nci->bg = 8; aci->bg = 8;
nci->used = xx - s->cx; aci->used = xx - s->cx;
TAILQ_INSERT_AFTER(&cl->items, ci, nci, entry); log_debug("%s: padding erased (after): from %u, size %u",
ctx->item = screen_write_get_citem(); __func__, aci->x, aci->used);
screen_write_collect_insert(ctx, aci);
} }
} }

View File

@@ -311,6 +311,8 @@ server_client_create(int fd)
evtimer_set(&c->repeat_timer, server_client_repeat_timer, c); evtimer_set(&c->repeat_timer, server_client_repeat_timer, c);
evtimer_set(&c->click_timer, server_client_click_timer, c); evtimer_set(&c->click_timer, server_client_click_timer, c);
c->click_wp = -1;
TAILQ_INIT(&c->input_requests); TAILQ_INIT(&c->input_requests);
TAILQ_INSERT_TAIL(&clients, c, entry); TAILQ_INSERT_TAIL(&clients, c, entry);
@@ -759,21 +761,17 @@ server_client_check_mouse(struct client *c, struct key_event *event)
if (c->flags & CLIENT_DOUBLECLICK) { if (c->flags & CLIENT_DOUBLECLICK) {
evtimer_del(&c->click_timer); evtimer_del(&c->click_timer);
c->flags &= ~CLIENT_DOUBLECLICK; c->flags &= ~CLIENT_DOUBLECLICK;
if (m->b == c->click_button) { type = SECOND;
type = SECOND; x = m->x, y = m->y, b = m->b;
x = m->x, y = m->y, b = m->b; log_debug("second-click at %u,%u", x, y);
log_debug("second-click at %u,%u", x, y); c->flags |= CLIENT_TRIPLECLICK;
c->flags |= CLIENT_TRIPLECLICK;
}
} else if (c->flags & CLIENT_TRIPLECLICK) { } else if (c->flags & CLIENT_TRIPLECLICK) {
evtimer_del(&c->click_timer); evtimer_del(&c->click_timer);
c->flags &= ~CLIENT_TRIPLECLICK; c->flags &= ~CLIENT_TRIPLECLICK;
if (m->b == c->click_button) { type = TRIPLE;
type = TRIPLE; x = m->x, y = m->y, b = m->b;
x = m->x, y = m->y, b = m->b; log_debug("triple-click at %u,%u", x, y);
log_debug("triple-click at %u,%u", x, y); goto have_event;
goto have_event;
}
} }
/* DOWN is the only remaining event type. */ /* DOWN is the only remaining event type. */
@@ -783,17 +781,6 @@ server_client_check_mouse(struct client *c, struct key_event *event)
log_debug("down at %u,%u", x, y); log_debug("down at %u,%u", x, y);
c->flags |= CLIENT_DOUBLECLICK; c->flags |= CLIENT_DOUBLECLICK;
} }
if (KEYC_CLICK_TIMEOUT != 0) {
memcpy(&c->click_event, m, sizeof c->click_event);
c->click_button = m->b;
log_debug("click timer started");
tv.tv_sec = KEYC_CLICK_TIMEOUT / 1000;
tv.tv_usec = (KEYC_CLICK_TIMEOUT % 1000) * 1000L;
evtimer_del(&c->click_timer);
evtimer_add(&c->click_timer, &tv);
}
} }
have_event: have_event:
@@ -914,6 +901,34 @@ have_event:
} }
} }
/* Reset click type or add a click timer if needed. */
if (type == DOWN ||
type == SECOND ||
type == TRIPLE) {
if (type != DOWN &&
(m->b != c->click_button ||
where != (enum mouse_where)c->click_where ||
m->wp != c->click_wp)) {
type = DOWN;
log_debug("click sequence reset at %u,%u", x, y);
c->flags &= ~CLIENT_TRIPLECLICK;
c->flags |= CLIENT_DOUBLECLICK;
}
if (type != TRIPLE && KEYC_CLICK_TIMEOUT != 0) {
memcpy(&c->click_event, m, sizeof c->click_event);
c->click_button = m->b;
c->click_where = where;
c->click_wp = m->wp;
log_debug("click timer started");
tv.tv_sec = KEYC_CLICK_TIMEOUT / 1000;
tv.tv_usec = (KEYC_CLICK_TIMEOUT % 1000) * 1000L;
evtimer_del(&c->click_timer);
evtimer_add(&c->click_timer, &tv);
}
}
/* Stop dragging if needed. */ /* Stop dragging if needed. */
if (type != DRAG && if (type != DRAG &&
type != WHEEL && type != WHEEL &&

View File

@@ -1383,6 +1383,11 @@ process_key:
break; break;
case KEYC_BSPACE: case KEYC_BSPACE:
case 'h'|KEYC_CTRL: case 'h'|KEYC_CTRL:
if (c->prompt_flags & PROMPT_BSPACE_EXIT && size == 0) {
if (c->prompt_inputcb(c, c->prompt_data, NULL, 1) == 0)
status_prompt_clear(c);
break;
}
if (c->prompt_index != 0) { if (c->prompt_index != 0) {
if (c->prompt_index == size) if (c->prompt_index == size)
c->prompt_buffer[--c->prompt_index].size = 0; c->prompt_buffer[--c->prompt_index].size = 0;

6
tmux.1
View File

@@ -6718,7 +6718,7 @@ See
for possible values for for possible values for
.Ar prompt-type . .Ar prompt-type .
.It Xo Ic command-prompt .It Xo Ic command-prompt
.Op Fl 1bFiklN .Op Fl 1beFiklN
.Op Fl I Ar inputs .Op Fl I Ar inputs
.Op Fl p Ar prompts .Op Fl p Ar prompts
.Op Fl t Ar target-client .Op Fl t Ar target-client
@@ -6791,6 +6791,10 @@ makes the prompt only accept numeric key presses.
.Fl i .Fl i
executes the command every time the prompt input changes instead of when the executes the command every time the prompt input changes instead of when the
user exits the command prompt. user exits the command prompt.
.Fl e
makes
.Em BSpace
cancel an empty prompt.
.Pp .Pp
.Fl T .Fl T
tells tells

3
tmux.h
View File

@@ -1984,6 +1984,8 @@ struct client {
struct event repeat_timer; struct event repeat_timer;
struct event click_timer; struct event click_timer;
int click_where;
int click_wp;
u_int click_button; u_int click_button;
struct mouse_event click_event; struct mouse_event click_event;
@@ -2094,6 +2096,7 @@ struct client {
#define PROMPT_KEY 0x10 #define PROMPT_KEY 0x10
#define PROMPT_ACCEPT 0x20 #define PROMPT_ACCEPT 0x20
#define PROMPT_QUOTENEXT 0x40 #define PROMPT_QUOTENEXT 0x40
#define PROMPT_BSPACE_EXIT 0x80
int prompt_flags; int prompt_flags;
enum prompt_type prompt_type; enum prompt_type prompt_type;
int prompt_cursor; int prompt_cursor;

View File

@@ -228,7 +228,6 @@ window_clock_draw_screen(struct window_mode_entry *wme)
struct screen *s = &data->screen; struct screen *s = &data->screen;
struct grid_cell gc; struct grid_cell gc;
char tim[64], *ptr; char tim[64], *ptr;
const char *timeformat;
time_t t; time_t t;
struct tm *tm; struct tm *tm;
u_int i, j, x, y, idx; u_int i, j, x, y, idx;
@@ -242,20 +241,18 @@ window_clock_draw_screen(struct window_mode_entry *wme)
tm = localtime(&t); tm = localtime(&t);
if (style == 0 || style == 2) { if (style == 0 || style == 2) {
if (style == 2) if (style == 2)
timeformat = "%l:%M:%S "; strftime(tim, sizeof tim, "%l:%M:%S ", localtime(&t));
else else
timeformat = "%l:%M "; strftime(tim, sizeof tim, "%l:%M ", localtime(&t));
strftime(tim, sizeof tim, timeformat, localtime(&t));
if (tm->tm_hour >= 12) if (tm->tm_hour >= 12)
strlcat(tim, "PM", sizeof tim); strlcat(tim, "PM", sizeof tim);
else else
strlcat(tim, "AM", sizeof tim); strlcat(tim, "AM", sizeof tim);
} else { } else {
if (style == 3) if (style == 3)
timeformat = "%H:%M:%S"; strftime(tim, sizeof tim, "%H:%M:%S", tm);
else else
timeformat = "%H:%M"; strftime(tim, sizeof tim, "%H:%M", tm);
strftime(tim, sizeof tim, timeformat, tm);
} }
screen_write_clearscreen(&ctx, 8); screen_write_clearscreen(&ctx, 8);

View File

@@ -3237,6 +3237,15 @@ window_copy_command(struct window_mode_entry *wme, struct client *c,
window_pane_reset_mode(wp); window_pane_reset_mode(wp);
else if (action == WINDOW_COPY_CMD_REDRAW) else if (action == WINDOW_COPY_CMD_REDRAW)
window_copy_redraw_screen(wme); window_copy_redraw_screen(wme);
else if (action == WINDOW_COPY_CMD_NOTHING) {
/*
* Nothing is not actually nothing - most commands at least
* move the cursor (what would be the point of a command that
* literally does nothing?) and in that case we need to redraw
* the first line to update the indicator.
*/
window_copy_redraw_lines(wme, 0, 1);
}
} }
static void static void