Compare commits

...

27 Commits

Author SHA1 Message Date
Michael Grant
52c238cdaf
Merge 76b8ee08ef244537c4050bb99346a550c7d5222d into 30e4f119d3952ea124b62a779132b6914baba842 2025-07-01 08:55:46 -04:00
Michael Grant
76b8ee08ef
Merge branch 'tmux:master' into non-blocking-popup-windows 2025-06-16 16:56:26 -04:00
Michael Grant
b9d7771f2a
Merge branch 'tmux:master' into non-blocking-popup-windows 2025-05-15 01:57:02 -04:00
Michael Grant
c22a60e412 Only redraw the whole window if the menu was closed, otherwise the user will see flashing when the mouse moves. 2025-05-06 10:52:48 +02:00
Michael Grant
b9222a6613 A better fix. 2025-05-06 06:56:26 +02:00
Michael Grant
71a7206e8b Merge branch 'non-blocking-popup-windows' of github.com:mgrant0/tmux into non-blocking-popup-windows 2025-05-05 22:19:36 +02:00
Michael Grant
eb3867d02e Fix bug if pane is focused and its menu is open that it does not change focus to the overlay popup if the mouse moves over that rectangle area. 2025-05-05 22:18:27 +02:00
Michael Grant
4ac0c79d8e
Merge branch 'tmux:master' into non-blocking-popup-windows 2025-05-04 07:42:10 -04:00
Michael Grant
2bae40ec7e Move window menu out of overlay into window struct. 2025-04-17 15:10:33 +01:00
Michael Grant
fe3ed1d7a8
Merge branch 'tmux:master' into non-blocking-popup-windows 2025-04-17 08:50:54 -04:00
Michael Grant
f11fa9d7f0 Check for right mouse button if pane is focused, let pane handle it. 2025-04-05 15:48:27 +01:00
Michael Grant
e7bc34df3d fix segv when overlay_check is null 2025-04-05 15:44:22 +01:00
Michael Grant
9327991d8b
Merge branch 'tmux:master' into non-blocking-popup-windows 2025-04-04 05:01:51 -04:00
Michael Grant
5001c8148a To fix a race condition by inhibitting it from calling popup_free_cb twice 2025-04-03 14:12:45 +01:00
Michael Grant
f755a007fa update man page 2025-04-01 17:41:26 +01:00
Michael Grant
4b545ac620
Merge branch 'tmux:master' into non-blocking-popup-windows 2025-03-31 10:52:31 -04:00
Michael Grant
5091236d4d skip foreach loop if done 2025-03-29 23:04:14 +00:00
Michael Grant
7fa11dcd24 -D arg to enable do not block 2025-03-29 22:47:49 +00:00
Michael Grant
6ab507c9ce rename CLIENT_OVERLAYPOPUP_FOCUSED to CLIENT_OVERLAYFOCUSED 2025-03-27 14:34:35 +00:00
Michael Grant
f6818a097f
Merge branch 'tmux:master' into non-blocking-popup-windows 2025-03-26 12:53:30 -04:00
Michael Grant
0a0e9852a2 Add overlay check to scrollbar code to prevent scrollbars obscured by an overlay from flashing while being dragged around. 2025-03-12 15:44:04 -04:00
Michael Grant
c0ff4831fc Add check to check if cursor is behind the overlay popup. 2025-03-12 15:34:48 -04:00
Michael Grant
5327f39134 Try 2. Move popup focus flag into client struct. 2025-03-12 13:52:17 -04:00
Michael Grant
66bf623259
Merge branch 'tmux:master' into non-blocking-popup-windows 2025-03-11 14:29:42 -04:00
Michael Grant
616143ea31
Merge branch 'tmux:master' into non-blocking-popup-windows 2025-03-04 15:20:12 -05:00
Michael Grant
43c947c348
Merge branch 'tmux:master' into non-blocking-popup-windows 2025-02-22 13:47:03 -05:00
Michael Grant
41ef18debb initial commit 2025-01-19 16:20:14 -04:00
8 changed files with 123 additions and 25 deletions

View File

@ -54,7 +54,7 @@ const struct cmd_entry cmd_display_popup_entry = {
.name = "display-popup", .name = "display-popup",
.alias = "popup", .alias = "popup",
.args = { "Bb:Cc:d:e:Eh:s:S:t:T:w:x:y:", 0, -1, NULL }, .args = { "Bb:Cc:Dd:e:Eh:s:S:t:T:w:x:y:", 0, -1, NULL },
.usage = "[-BCE] [-b border-lines] [-c target-client] " .usage = "[-BCE] [-b border-lines] [-c target-client] "
"[-d start-directory] [-e environment] [-h height] " "[-d start-directory] [-e environment] [-h height] "
"[-s style] [-S border-style] " CMD_TARGET_PANE_USAGE "[-s style] [-S border-style] " CMD_TARGET_PANE_USAGE
@ -301,7 +301,7 @@ cmd_display_menu_exec(struct cmd *self, struct cmdq_item *item)
struct options_entry *oe; struct options_entry *oe;
if (tc->overlay_draw != NULL) if ((tc->overlay_draw != NULL) && (tc->flags & CLIENT_OVERLAYFOCUSED))
return (CMD_RETURN_NORMAL); return (CMD_RETURN_NORMAL);
if (args_has(args, 'C')) { if (args_has(args, 'C')) {
@ -484,6 +484,8 @@ cmd_display_popup_exec(struct cmd *self, struct cmdq_item *item)
flags |= POPUP_CLOSEEXITZERO; flags |= POPUP_CLOSEEXITZERO;
else if (args_has(args, 'E')) else if (args_has(args, 'E'))
flags |= POPUP_CLOSEEXIT; flags |= POPUP_CLOSEEXIT;
if (args_has(args, 'D'))
item = NULL;
if (popup_display(flags, lines, item, px, py, w, h, env, shellcmd, argc, if (popup_display(flags, lines, item, px, py, w, h, env, shellcmd, argc,
argv, cwd, title, tc, s, style, border_style, NULL, NULL) != 0) { argv, cwd, title, tc, s, style, border_style, NULL, NULL) != 0) {
cmd_free_argv(argc, argv); cmd_free_argv(argc, argv);
@ -498,5 +500,8 @@ cmd_display_popup_exec(struct cmd *self, struct cmdq_item *item)
free(cwd); free(cwd);
free(title); free(title);
cmd_free_argv(argc, argv); cmd_free_argv(argc, argv);
if (args_has(args, 'D'))
return (CMD_RETURN_NORMAL);
else
return (CMD_RETURN_WAIT); return (CMD_RETURN_WAIT);
} }

11
menu.c
View File

@ -421,10 +421,6 @@ chosen:
return (1); return (1);
} }
if (md->item != NULL)
event = cmdq_get_event(md->item);
else
event = NULL;
state = cmdq_new_state(&md->fs, event, 0); state = cmdq_new_state(&md->fs, event, 0);
status = cmd_parse_and_append(item->command, NULL, c, state, &error); status = cmd_parse_and_append(item->command, NULL, c, state, &error);
@ -544,12 +540,17 @@ menu_display(struct menu *menu, int flags, int starting_choice,
void *data) void *data)
{ {
struct menu_data *md; struct menu_data *md;
struct window *w = c->session->curw->window;
md = menu_prepare(menu, flags, starting_choice, item, px, py, c, lines, md = menu_prepare(menu, flags, starting_choice, item, px, py, c, lines,
style, selected_style, border_style, fs, cb, data); style, selected_style, border_style, fs, cb, data);
if (md == NULL) if (md == NULL)
return (-1); return (-1);
w->md = md;
c->flags |= CLIENT_REDRAWWINDOW;
/*
server_client_set_overlay(c, 0, NULL, menu_mode_cb, menu_draw_cb, server_client_set_overlay(c, 0, NULL, menu_mode_cb, menu_draw_cb,
menu_key_cb, menu_free_cb, NULL, md); menu_key_cb, menu_free_cb, NULL, md);
return (0); */
return (1);
} }

15
popup.c
View File

@ -218,6 +218,7 @@ popup_draw_cb(struct client *c, void *data, struct screen_redraw_ctx *rctx)
u_int i, px = pd->px, py = pd->py; u_int i, px = pd->px, py = pd->py;
struct colour_palette *palette = &pd->palette; struct colour_palette *palette = &pd->palette;
struct grid_cell defaults; struct grid_cell defaults;
struct window *w = c->session->curw->window;
screen_init(&s, pd->sx, pd->sy, 0); screen_init(&s, pd->sx, pd->sy, 0);
screen_write_start(&ctx, &s); screen_write_start(&ctx, &s);
@ -244,6 +245,9 @@ popup_draw_cb(struct client *c, void *data, struct screen_redraw_ctx *rctx)
if (pd->md != NULL) { if (pd->md != NULL) {
c->overlay_check = menu_check_cb; c->overlay_check = menu_check_cb;
c->overlay_data = pd->md; c->overlay_data = pd->md;
} else if (w->md != NULL) {
c->overlay_check = menu_check_cb;
c->overlay_data = w->md;
} else { } else {
c->overlay_check = NULL; c->overlay_check = NULL;
c->overlay_data = NULL; c->overlay_data = NULL;
@ -257,6 +261,10 @@ popup_draw_cb(struct client *c, void *data, struct screen_redraw_ctx *rctx)
c->overlay_check = NULL; c->overlay_check = NULL;
c->overlay_data = NULL; c->overlay_data = NULL;
menu_draw_cb(c, pd->md, rctx); menu_draw_cb(c, pd->md, rctx);
} else if (w->md != NULL) {
c->overlay_check = NULL;
c->overlay_data = NULL;
menu_draw_cb(c, w->md, rctx);
} }
c->overlay_check = popup_check_cb; c->overlay_check = popup_check_cb;
c->overlay_data = pd; c->overlay_data = pd;
@ -511,10 +519,13 @@ popup_key_cb(struct client *c, void *data, struct key_event *event)
m->x > pd->px + pd->sx - 1 || m->x > pd->px + pd->sx - 1 ||
m->y < pd->py || m->y < pd->py ||
m->y > pd->py + pd->sy - 1) { m->y > pd->py + pd->sy - 1) {
if (MOUSE_BUTTONS(m->b) == MOUSE_BUTTON_3) if ((c->flags & CLIENT_OVERLAYFOCUSED) &&
(MOUSE_BUTTONS(m->b) & MOUSE_BUTTON_3))
goto menu; goto menu;
c->flags &= ~CLIENT_OVERLAYFOCUSED;
return (0); return (0);
} }
c->flags |= CLIENT_OVERLAYFOCUSED;
if (pd->border_lines != BOX_LINES_NONE) { if (pd->border_lines != BOX_LINES_NONE) {
if (m->x == pd->px) if (m->x == pd->px)
border = LEFT; border = LEFT;
@ -561,6 +572,7 @@ popup_key_cb(struct client *c, void *data, struct key_event *event)
bufferevent_write(job_get_event(pd->job), buf, len); bufferevent_write(job_get_event(pd->job), buf, len);
return (0); return (0);
} }
if (c->flags & CLIENT_OVERLAYFOCUSED)
input_key(&pd->s, job_get_event(pd->job), event->key); input_key(&pd->s, job_get_event(pd->job), event->key);
} }
return (0); return (0);
@ -725,6 +737,7 @@ popup_display(int flags, enum box_lines lines, struct cmdq_item *item, u_int px,
server_client_set_overlay(c, 0, popup_check_cb, popup_mode_cb, server_client_set_overlay(c, 0, popup_check_cb, popup_mode_cb,
popup_draw_cb, popup_key_cb, popup_free_cb, popup_resize_cb, pd); popup_draw_cb, popup_key_cb, popup_free_cb, popup_resize_cb, pd);
c->flags |= CLIENT_OVERLAYFOCUSED;
return (0); return (0);
} }

View File

@ -857,6 +857,9 @@ screen_redraw_draw_panes(struct screen_redraw_ctx *ctx)
if (window_pane_visible(wp)) if (window_pane_visible(wp))
screen_redraw_draw_pane(ctx, wp); screen_redraw_draw_pane(ctx, wp);
} }
if (w->md != NULL)
w->menu_draw_cb(c, w->md, ctx);
} }
/* Draw the status line. */ /* Draw the status line. */
@ -1018,6 +1021,7 @@ screen_redraw_draw_scrollbar(struct screen_redraw_ctx *ctx,
struct tty *tty = &c->tty; struct tty *tty = &c->tty;
struct grid_cell gc, slgc, *gcp; struct grid_cell gc, slgc, *gcp;
struct style *sb_style = &wp->scrollbar_style; struct style *sb_style = &wp->scrollbar_style;
struct overlay_ranges r;
u_int i, j, imax, jmax; u_int i, j, imax, jmax;
u_int sb_w = sb_style->width, sb_pad = sb_style->pad; u_int sb_w = sb_style->width, sb_pad = sb_style->pad;
int px, py, ox = ctx->ox, oy = ctx->oy; int px, py, ox = ctx->ox, oy = ctx->oy;
@ -1046,6 +1050,16 @@ screen_redraw_draw_scrollbar(struct screen_redraw_ctx *ctx,
py < yoff - oy - 1 || py < yoff - oy - 1 ||
py >= sy || py < 0) py >= sy || py < 0)
continue; continue;
if (c->overlay_check != NULL) {
c->overlay_check(c, c->overlay_data, px, py, 1, &r);
if (r.nx[0] + r.nx[1] == 0)
continue;
}
if (wp->window->md != NULL) {
menu_check_cb(c, wp->window->md, px, py, 1, &r);
if (r.nx[0] + r.nx[1] == 0)
continue;
}
tty_cursor(tty, px, py); tty_cursor(tty, px, py);
if ((sb_pos == PANE_SCROLLBARS_LEFT && if ((sb_pos == PANE_SCROLLBARS_LEFT &&
i >= sb_w && i < sb_w + sb_pad) || i >= sb_w && i < sb_w + sb_pad) ||

View File

@ -2598,6 +2598,7 @@ paste_key:
out: out:
if (s != NULL && key != KEYC_FOCUS_OUT) if (s != NULL && key != KEYC_FOCUS_OUT)
server_client_update_latest(c); server_client_update_latest(c);
free(event->buf); free(event->buf);
free(event); free(event);
return (CMD_RETURN_NORMAL); return (CMD_RETURN_NORMAL);
@ -2608,7 +2609,10 @@ int
server_client_handle_key(struct client *c, struct key_event *event) server_client_handle_key(struct client *c, struct key_event *event)
{ {
struct session *s = c->session; struct session *s = c->session;
struct window *w = s->curw->window;
struct cmdq_item *item; struct cmdq_item *item;
struct window_pane *wp;
int done;
/* Check the client is good to accept input. */ /* Check the client is good to accept input. */
if (s == NULL || (c->flags & CLIENT_UNATTACHEDFLAGS)) if (s == NULL || (c->flags & CLIENT_UNATTACHEDFLAGS))
@ -2625,17 +2629,27 @@ server_client_handle_key(struct client *c, struct key_event *event)
return (0); return (0);
status_message_clear(c); status_message_clear(c);
} }
if (c->overlay_key != NULL) { if (w->md != NULL) {
switch (c->overlay_key(c, c->overlay_data, event)) { done = w->menu_key_cb(c, w->md, event);
case 0: if (done) {
return (0); w->menu_free_cb(c, w->md);
case 1: w->md = NULL;
server_client_clear_overlay(c); c->flags |= CLIENT_REDRAWWINDOW;
return (0);
} }
} }
else if (c->overlay_key != NULL) {
done = c->overlay_key(c, c->overlay_data, event);
if (done)
server_client_clear_overlay(c); server_client_clear_overlay(c);
if (c->prompt_string != NULL) { else {
TAILQ_FOREACH(wp, &w->panes, entry) {
if (~c->flags & CLIENT_OVERLAYFOCUSED)
goto focused;
}
}
return (0);
}
focused: if (c->prompt_string != NULL) {
if (status_prompt_key(c, event->key) == 0) if (status_prompt_key(c, event->key) == 0)
return (0); return (0);
} }
@ -2885,6 +2899,25 @@ out:
bufferevent_enable(wp->event, EV_READ); bufferevent_enable(wp->event, EV_READ);
} }
static int
server_client_check_overlay(struct client *c, u_int px, u_int py)
{
struct overlay_ranges r;
/*
* A unit width range will always return nx[2] == 0 from a check, even
* with multiple overlays, so it's sufficient to check just the first
* two entries.
*/
if (c->overlay_check == NULL)
return (0);
c->overlay_check(c, c->overlay_data, px, py, 1, &r);
if (r.nx[0] + r.nx[1] == 0)
return (0);
return (1);
}
/* /*
* Update cursor position and mode settings. The scroll region and attributes * Update cursor position and mode settings. The scroll region and attributes
* are cleared when idle (waiting for an event) as this is the most likely time * are cleared when idle (waiting for an event) as this is the most likely time
@ -2894,6 +2927,7 @@ out:
* tty_region/tty_reset/tty_update_mode already take care of not resetting * tty_region/tty_reset/tty_update_mode already take care of not resetting
* things that are already in their default state. * things that are already in their default state.
*/ */
static void static void
server_client_reset_state(struct client *c) server_client_reset_state(struct client *c)
{ {
@ -2913,9 +2947,11 @@ server_client_reset_state(struct client *c)
tty->flags &= ~TTY_BLOCK; tty->flags &= ~TTY_BLOCK;
/* Get mode from overlay if any, else from screen. */ /* Get mode from overlay if any, else from screen. */
if (c->overlay_draw != NULL) { if (c->overlay_draw != NULL && c->flags & CLIENT_OVERLAYFOCUSED) {
if (c->overlay_mode != NULL) if (c->overlay_mode != NULL)
s = c->overlay_mode(c, c->overlay_data, &cx, &cy); s = c->overlay_mode(c, c->overlay_data, &cx, &cy);
} else if (w->md != NULL) {
s = w->menu_mode_cb(c, w->md, &cx, &cy);
} else if (c->prompt_string == NULL) } else if (c->prompt_string == NULL)
s = wp->screen; s = wp->screen;
else else
@ -2944,18 +2980,22 @@ server_client_reset_state(struct client *c)
cy = tty->sy - n; cy = tty->sy - n;
} }
cx = c->prompt_cursor; cx = c->prompt_cursor;
} else if (c->overlay_draw == NULL) { } else if (c->overlay_draw == NULL || ~c->flags & CLIENT_OVERLAYFOCUSED) {
cursor = 0; cursor = 0;
tty_window_offset(tty, &ox, &oy, &sx, &sy); tty_window_offset(tty, &ox, &oy, &sx, &sy);
if (wp->xoff + s->cx >= ox && wp->xoff + s->cx <= ox + sx && if (wp->xoff + s->cx >= ox && wp->xoff + s->cx <= ox + sx &&
wp->yoff + s->cy >= oy && wp->yoff + s->cy <= oy + sy) { wp->yoff + s->cy >= oy && wp->yoff + s->cy <= oy + sy) {
cursor = 1;
cx = wp->xoff + s->cx - ox; cx = wp->xoff + s->cx - ox;
cy = wp->yoff + s->cy - oy; cy = wp->yoff + s->cy - oy;
if (status_at_line(c) == 0) if (status_at_line(c) == 0)
cy += status_line_size(c); cy += status_line_size(c);
if (c->overlay_draw != NULL &&
!server_client_check_overlay(c, cx, cy))
cursor = 0;
else
cursor = 1;
} }
if (!cursor) if (!cursor)
mode &= ~MODE_CURSOR; mode &= ~MODE_CURSOR;

9
tmux.1
View File

@ -6921,7 +6921,7 @@ forwards any input read from stdin to the empty pane given by
.Ar target-pane . .Ar target-pane .
.Tg popup .Tg popup
.It Xo Ic display-popup .It Xo Ic display-popup
.Op Fl BCE .Op Fl BCDE
.Op Fl b Ar border-lines .Op Fl b Ar border-lines
.Op Fl c Ar target-client .Op Fl c Ar target-client
.Op Fl d Ar start-directory .Op Fl d Ar start-directory
@ -6944,7 +6944,6 @@ Display a popup running
when omitted) on when omitted) on
.Ar target-client . .Ar target-client .
A popup is a rectangular box drawn over the top of any panes. A popup is a rectangular box drawn over the top of any panes.
Panes are not updated while a popup is present.
.Pp .Pp
.Fl E .Fl E
closes the popup automatically when closes the popup automatically when
@ -7003,6 +7002,12 @@ is a format for the popup title (see
The The
.Fl C .Fl C
flag closes any popup on the client. flag closes any popup on the client.
.Pp
The
.Fl D
flag causes the command not to block and wait for the
.Ar shell-command
to exit.
.Tg showphist .Tg showphist
.It Xo Ic show-prompt-history .It Xo Ic show-prompt-history
.Op Fl T Ar prompt-type .Op Fl T Ar prompt-type

14
tmux.h
View File

@ -1225,6 +1225,13 @@ TAILQ_HEAD(window_panes, window_pane);
RB_HEAD(window_pane_tree, window_pane); RB_HEAD(window_pane_tree, window_pane);
/* Window structure. */ /* Window structure. */
struct key_event;
typedef struct screen *(*window_menu_mode_cb)(struct client *, void *, u_int *,
u_int *);
typedef void (*window_menu_draw_cb)(struct client *, void *,
struct screen_redraw_ctx *);
typedef int (*window_menu_key_cb)(struct client *, void *, struct key_event *);
typedef void (*window_menu_free_cb)(struct client *, void *);
struct window { struct window {
u_int id; u_int id;
void *latest; void *latest;
@ -1247,6 +1254,12 @@ struct window {
struct layout_cell *saved_layout_root; struct layout_cell *saved_layout_root;
char *old_layout; char *old_layout;
struct menu_data *md;
window_menu_mode_cb menu_mode_cb;
window_menu_draw_cb menu_draw_cb;
window_menu_key_cb menu_key_cb;
window_menu_free_cb menu_free_cb;
u_int sx; u_int sx;
u_int sy; u_int sy;
u_int manual_sx; u_int manual_sx;
@ -1983,6 +1996,7 @@ struct client {
#define CLIENT_ASSUMEPASTING 0x2000000000ULL #define CLIENT_ASSUMEPASTING 0x2000000000ULL
#define CLIENT_REDRAWSCROLLBARS 0x4000000000ULL #define CLIENT_REDRAWSCROLLBARS 0x4000000000ULL
#define CLIENT_NO_DETACH_ON_DESTROY 0x8000000000ULL #define CLIENT_NO_DETACH_ON_DESTROY 0x8000000000ULL
#define CLIENT_OVERLAYFOCUSED 0x10000000000ULL
#define CLIENT_ALLREDRAWFLAGS \ #define CLIENT_ALLREDRAWFLAGS \
(CLIENT_REDRAWWINDOW| \ (CLIENT_REDRAWWINDOW| \
CLIENT_REDRAWSTATUS| \ CLIENT_REDRAWSTATUS| \

View File

@ -319,6 +319,12 @@ window_create(u_int sx, u_int sy, u_int xpixel, u_int ypixel)
w->xpixel = xpixel; w->xpixel = xpixel;
w->ypixel = ypixel; w->ypixel = ypixel;
w->md = NULL;
w->menu_mode_cb = menu_mode_cb;
w->menu_draw_cb = menu_draw_cb;
w->menu_key_cb = menu_key_cb;
w->menu_free_cb = menu_free_cb;
w->options = options_create(global_w_options); w->options = options_create(global_w_options);
w->references = 0; w->references = 0;