Merge remote-tracking branch 'refs/remotes/tmux-openbsd/master'

* refs/remotes/tmux-openbsd/master:
  Switch menus also to be attached to the window instead of an overlay and drawn as part of the client's scene. This means removing the popup menu.
This commit is contained in:
tmux update bot
2026-07-14 22:13:52 +00:00
9 changed files with 567 additions and 381 deletions

View File

@@ -1,4 +1,4 @@
/* $OpenBSD: cmd-display-menu.c,v 1.51 2026/07/07 09:45:09 nicm Exp $ */
/* $OpenBSD: cmd-display-menu.c,v 1.52 2026/07/14 19:07:03 nicm Exp $ */
/*
* Copyright (c) 2019 Nicholas Marriott <nicholas.marriott@gmail.com>
@@ -92,7 +92,7 @@ cmd_display_menu_args_parse(struct args *args, u_int idx, __unused char **cause)
}
static int
cmd_display_menu_get_pos(struct client *tc, struct cmdq_item *item,
cmd_display_menu_get_popup_pos(struct client *tc, struct cmdq_item *item,
struct args *args, u_int *px, u_int *py, u_int w, u_int h)
{
struct tty *tty = &tc->tty;
@@ -127,8 +127,8 @@ cmd_display_menu_get_pos(struct client *tc, struct cmdq_item *item,
}
/* Position of the previous menu, for -x/-y L. */
format_add(ft, "popup_last_x", "%u", tc->menu_last_px);
format_add(ft, "popup_last_y", "%u", tc->menu_last_py + h);
format_add(ft, "popup_last_x", "%u", target->w->menu_last_px);
format_add(ft, "popup_last_y", "%u", target->w->menu_last_py + h);
/*
* If there are any status lines, add this window position and the
@@ -288,6 +288,184 @@ cmd_display_menu_get_pos(struct client *tc, struct cmdq_item *item,
return (1);
}
static int
cmd_display_menu_get_menu_pos(struct client *tc, struct cmdq_item *item,
struct args *args, u_int *px, u_int *py, u_int w, u_int h)
{
struct cmd_find_state *target = cmdq_get_target(item);
struct key_event *event = cmdq_get_event(item);
struct session *s = tc->session;
struct winlink *wl = target->wl;
struct window *window = target->w;
struct window_pane *wp = target->wp;
struct style_ranges *ranges = NULL;
struct style_range *sr = NULL;
const char *xp, *yp;
char *p;
u_int line, ox, oy, sx, sy, lines, position;
long n, max_x, max_y, mouse_x = 0, mouse_y = 0;
struct format_tree *ft;
max_x = window->sx > w ? window->sx - w : 0;
max_y = window->sy > h ? window->sy - h : 0;
tty_window_offset(&tc->tty, &ox, &oy, &sx, &sy);
ft = format_create_from_target(item);
if (event->m.valid) {
mouse_x = event->m.x + ox;
if (event->m.statusat == 0) {
if (event->m.y >= event->m.statuslines)
mouse_y = event->m.y - event->m.statuslines + oy;
else
mouse_y = oy;
} else if (event->m.statusat > 0 &&
event->m.y >= (u_int)event->m.statusat) {
mouse_y = oy + sy - 1;
} else
mouse_y = event->m.y + oy;
format_add(ft, "popup_mouse_x", "%ld", mouse_x);
format_add(ft, "popup_mouse_y", "%ld", mouse_y);
}
format_add(ft, "popup_last_x", "%u", window->menu_last_px);
format_add(ft, "popup_last_y", "%u", window->menu_last_py + h);
lines = status_line_size(tc);
position = options_get_number(s->options, "status-position");
if (status_at_line(tc) != -1 && lines != 0) {
for (line = 0; line < lines; line++) {
ranges = &tc->status.entries[line].ranges;
TAILQ_FOREACH(sr, ranges, entry) {
if (sr->type != STYLE_RANGE_WINDOW)
continue;
if (sr->argument == (u_int)wl->idx)
break;
}
if (sr != NULL)
break;
}
if (sr != NULL) {
format_add(ft, "popup_window_status_line_x", "%u",
sr->start + ox);
if (position == 0) {
format_add(ft, "popup_window_status_line_y",
"%u", h);
} else {
format_add(ft, "popup_window_status_line_y",
"%u", window->sy);
}
}
if (position == 0)
format_add(ft, "popup_status_line_y", "%u", h);
else
format_add(ft, "popup_status_line_y", "%u", window->sy);
}
format_add(ft, "popup_width", "%u", w);
format_add(ft, "popup_height", "%u", h);
n = ((long)window->sx - 1) / 2 - w / 2;
if (n < 0)
format_add(ft, "popup_centre_x", "%u", 0);
else
format_add(ft, "popup_centre_x", "%ld", n);
n = ((long)window->sy - 1) / 2 + h / 2;
if (n >= window->sy)
format_add(ft, "popup_centre_y", "%ld", max_y);
else
format_add(ft, "popup_centre_y", "%ld", n);
if (event->m.valid) {
n = mouse_x - w / 2;
if (n < 0)
format_add(ft, "popup_mouse_centre_x", "%u", 0);
else
format_add(ft, "popup_mouse_centre_x", "%ld", n);
n = mouse_y - h / 2;
if (n + h >= window->sy)
format_add(ft, "popup_mouse_centre_y", "%ld", max_y);
else
format_add(ft, "popup_mouse_centre_y", "%ld", n);
n = mouse_y + h;
if (n >= window->sy)
format_add(ft, "popup_mouse_top", "%u", window->sy - 1);
else
format_add(ft, "popup_mouse_top", "%ld", n);
n = mouse_y - h;
if (n < 0)
format_add(ft, "popup_mouse_bottom", "%u", 0);
else
format_add(ft, "popup_mouse_bottom", "%ld", n);
}
n = wp->yoff + h;
if (n >= window->sy)
format_add(ft, "popup_pane_top", "%ld", max_y);
else
format_add(ft, "popup_pane_top", "%ld", n);
format_add(ft, "popup_pane_bottom", "%u", wp->yoff + wp->sy);
format_add(ft, "popup_pane_left", "%u", wp->xoff);
n = (long)wp->xoff + wp->sx - w;
if (n < 0)
format_add(ft, "popup_pane_right", "%u", 0);
else
format_add(ft, "popup_pane_right", "%ld", n);
xp = args_get(args, 'x');
if (xp == NULL || strcmp(xp, "C") == 0)
xp = "#{popup_centre_x}";
else if (strcmp(xp, "R") == 0)
xp = "#{popup_pane_right}";
else if (strcmp(xp, "P") == 0)
xp = "#{popup_pane_left}";
else if (strcmp(xp, "M") == 0)
xp = "#{popup_mouse_centre_x}";
else if (strcmp(xp, "L") == 0)
xp = "#{popup_last_x}";
else if (strcmp(xp, "W") == 0)
xp = "#{popup_window_status_line_x}";
p = format_expand(ft, xp);
n = strtol(p, NULL, 10);
if (n < 0)
n = 0;
if (n > max_x)
n = max_x;
*px = n;
log_debug("%s: -x: %s = %s = %u (-w %u)", __func__, xp, p, *px, w);
free(p);
yp = args_get(args, 'y');
if (yp == NULL || strcmp(yp, "C") == 0)
yp = "#{popup_centre_y}";
else if (strcmp(yp, "P") == 0)
yp = "#{popup_pane_bottom}";
else if (strcmp(yp, "M") == 0)
yp = "#{popup_mouse_top}";
else if (strcmp(yp, "L") == 0)
yp = "#{popup_last_y}";
else if (strcmp(yp, "S") == 0)
yp = "#{popup_status_line_y}";
else if (strcmp(yp, "W") == 0)
yp = "#{popup_window_status_line_y}";
p = format_expand(ft, yp);
n = strtol(p, NULL, 10);
if (n < h)
n = 0;
else
n -= h;
if (n < 0)
n = 0;
if (n > max_y)
n = max_y;
*py = n;
log_debug("%s: -y: %s = %s = %u (-h %u)", __func__, yp, p, *py, h);
free(p);
format_free(ft);
return (1);
}
static enum cmd_retval
cmd_display_menu_exec(struct cmd *self, struct cmdq_item *item)
{
@@ -308,10 +486,6 @@ cmd_display_menu_exec(struct cmd *self, struct cmdq_item *item)
struct options *o = target->s->curw->window->options;
struct options_entry *oe;
if (tc->overlay_draw != NULL)
return (CMD_RETURN_NORMAL);
if (args_has(args, 'C')) {
if (strcmp(args_get(args, 'C'), "-") == 0)
starting_choice = -1;
@@ -357,8 +531,8 @@ cmd_display_menu_exec(struct cmd *self, struct cmdq_item *item)
}
if (menu->count == 0)
goto out;
if (!cmd_display_menu_get_pos(tc, item, args, &px, &py, menu->width + 4,
menu->count + 2))
if (!cmd_display_menu_get_menu_pos(tc, item, args, &px, &py,
menu->width + 4, menu->count + 2))
goto out;
value = args_get(args, 'b');
@@ -379,7 +553,7 @@ cmd_display_menu_exec(struct cmd *self, struct cmdq_item *item)
if (menu_display(menu, flags, starting_choice, item, px, py, tc, lines,
style, selected_style, border_style, target, NULL, NULL) != 0)
goto out;
return (CMD_RETURN_WAIT);
return (CMD_RETURN_NORMAL);
out:
menu_free(menu);
@@ -445,7 +619,8 @@ cmd_display_popup_exec(struct cmd *self, struct cmdq_item *item)
w = tty->sx;
if (h > tty->sy)
h = tty->sy;
if (!cmd_display_menu_get_pos(tc, item, args, &px, &py, w, h))
if (!cmd_display_menu_get_popup_pos(tc, item, args, &px, &py,
w, h))
goto out;
value = args_get(args, 'd');

291
menu.c
View File

@@ -1,4 +1,4 @@
/* $OpenBSD: menu.c,v 1.68 2026/07/07 09:45:09 nicm Exp $ */
/* $OpenBSD: menu.c,v 1.69 2026/07/14 19:07:03 nicm Exp $ */
/*
* Copyright (c) 2019 Nicholas Marriott <nicholas.marriott@gmail.com>
@@ -24,7 +24,7 @@
#include "tmux.h"
struct menu_data {
struct cmdq_item *item;
struct window *w;
int flags;
char *style;
@@ -37,8 +37,9 @@ struct menu_data {
enum box_lines border_lines;
struct cmd_find_state fs;
key_code key;
struct mouse_event m;
struct screen s;
struct visible_ranges r;
u_int px;
u_int py;
@@ -175,46 +176,14 @@ menu_free(struct menu *menu)
free(menu);
}
struct screen *
menu_mode_cb(__unused struct client *c, void *data, u_int *cx, u_int *cy)
{
struct menu_data *md = data;
*cx = md->px + 2;
if (md->choice == -1)
*cy = md->py;
else
*cy = md->py + 1 + md->choice;
return (&md->s);
}
/* Return parts of the input range which are not obstructed by the menu. */
struct visible_ranges *
menu_check_cb(__unused struct client *c, void *data, u_int px, u_int py,
u_int nx)
{
struct menu_data *md = data;
struct menu *menu = md->menu;
server_client_overlay_range(md->px, md->py, menu->width + 4,
menu->count + 2, px, py, nx, &md->r);
return (&md->r);
}
static void
menu_reapply_styles(struct menu_data *md, struct client *c)
menu_reapply_styles(struct menu_data *md)
{
struct session *s = c->session;
struct options *o;
struct options *o = md->w->options;
struct format_tree *ft;
struct style sytmp;
if (s == NULL)
return;
o = s->curw->window->options;
ft = format_create_defaults(NULL, c, s, s->curw, NULL);
ft = format_create_defaults(NULL, NULL, md->fs.s, md->fs.wl, md->fs.wp);
/* Reapply menu style from options. */
memcpy(&md->style_gc, &grid_default_cell, sizeof md->style_gc);
@@ -257,16 +226,13 @@ menu_reapply_styles(struct menu_data *md, struct client *c)
}
void
menu_draw_cb(struct client *c, void *data)
menu_update(struct menu_data *md)
{
struct menu_data *md = data;
struct tty *tty = &c->tty;
struct screen *s = &md->s;
struct menu *menu = md->menu;
struct screen_write_ctx ctx;
u_int i, px = md->px, py = md->py;
menu_reapply_styles(md, c);
menu_reapply_styles(md);
screen_write_start(&ctx, s);
screen_write_clearscreen(&ctx, 8);
@@ -279,43 +245,95 @@ menu_draw_cb(struct client *c, void *data)
screen_write_menu(&ctx, menu, md->choice, md->border_lines,
&md->style_gc, &md->border_style_gc, &md->selected_style_gc);
screen_write_stop(&ctx);
}
for (i = 0; i < screen_size_y(&md->s); i++)
tty_draw_line(tty, s, 0, i, menu->width + 4, px, py + i, NULL);
static void
menu_free_data(struct menu_data *md)
{
if (md != NULL) {
if (md->cb != NULL)
md->cb(md->menu, UINT_MAX, KEYC_NONE, md->data);
screen_free(&md->s);
menu_free(md->menu);
free(md->style);
free(md->selected_style);
free(md->border_style);
free(md);
}
}
void
menu_free_cb(__unused struct client *c, void *data)
menu_close(struct window *w)
{
struct menu_data *md = data;
if (w->menu != NULL) {
menu_free_data(w->menu);
w->menu = NULL;
if (md->item != NULL)
cmdq_continue(md->item);
redraw_invalidate_scene(w);
window_update_focus(w);
server_redraw_window(w);
}
}
if (md->cb != NULL)
md->cb(md->menu, UINT_MAX, KEYC_NONE, md->data);
void
menu_destroy(struct window *w)
{
menu_free_data(w->menu);
w->menu = NULL;
}
free(md->r.ranges);
screen_free(&md->s);
void
menu_get_cursor(struct menu_data *md, u_int *cx, u_int *cy)
{
*cx = md->px + 2;
if (md->choice == -1)
*cy = md->py;
else
*cy = md->py + 1 + md->choice;
}
menu_free(md->menu);
free(md->style);
free(md->selected_style);
free(md->border_style);
free(md);
struct screen *
menu_screen(struct menu_data *md)
{
return (&md->s);
}
u_int
menu_width(struct menu_data *md)
{
return (md->menu->width + 4);
}
u_int
menu_height(struct menu_data *md)
{
return (md->menu->count + 2);
}
u_int
menu_x(struct menu_data *md)
{
return (md->px);
}
u_int
menu_y(struct menu_data *md)
{
return (md->py);
}
int
menu_key_cb(struct client *c, void *data, struct key_event *event)
menu_key(struct client *c, struct menu_data *md, struct key_event *event)
{
struct menu_data *md = data;
struct menu *menu = md->menu;
struct mouse_event *m = &event->m;
u_int i;
int count = menu->count, old = md->choice;
int n = menu->count, old = md->choice;
int move;
const char *name = NULL;
const struct menu_item *item;
struct key_event saved_event;
struct cmdq_state *state;
enum cmd_parse_status status;
char *error;
@@ -337,7 +355,7 @@ menu_key_cb(struct client *c, void *data, struct key_event *event)
if (m->x < md->px ||
m->x > md->px + 4 + menu->width ||
m->y < md->py + 1 ||
m->y > md->py + 1 + count - 1) {
m->y > md->py + 1 + n - 1) {
if (~md->flags & MENU_STAYOPEN) {
if (!move && MOUSE_RELEASE(m->b))
return (1);
@@ -349,7 +367,7 @@ menu_key_cb(struct client *c, void *data, struct key_event *event)
}
if (md->choice != -1) {
md->choice = -1;
c->flags |= CLIENT_REDRAWOVERLAY;
server_redraw_window_menu(md->w);
}
return (0);
}
@@ -362,10 +380,10 @@ menu_key_cb(struct client *c, void *data, struct key_event *event)
}
md->choice = m->y - (md->py + 1);
if (md->choice != old)
c->flags |= CLIENT_REDRAWOVERLAY;
server_redraw_window_menu(md->w);
return (0);
}
for (i = 0; i < (u_int)count; i++) {
for (i = 0; i < (u_int)n; i++) {
name = menu->items[i].name;
if (name == NULL || *name == '-')
continue;
@@ -382,12 +400,12 @@ menu_key_cb(struct client *c, void *data, struct key_event *event)
old = 0;
do {
if (md->choice == -1 || md->choice == 0)
md->choice = count - 1;
md->choice = n - 1;
else
md->choice--;
name = menu->items[md->choice].name;
} while ((name == NULL || *name == '-') && md->choice != old);
c->flags |= CLIENT_REDRAWOVERLAY;
server_redraw_window_menu(md->w);
return (0);
case KEYC_BSPACE:
if (~md->flags & MENU_TAB)
@@ -396,7 +414,7 @@ menu_key_cb(struct client *c, void *data, struct key_event *event)
case '\011': /* Tab */
if (~md->flags & MENU_TAB)
break;
if (md->choice == count - 1)
if (md->choice == n - 1)
return (1);
/* FALLTHROUGH */
case KEYC_DOWN:
@@ -404,13 +422,13 @@ menu_key_cb(struct client *c, void *data, struct key_event *event)
if (old == -1)
old = 0;
do {
if (md->choice == -1 || md->choice == count - 1)
if (md->choice == -1 || md->choice == n - 1)
md->choice = 0;
else
md->choice++;
name = menu->items[md->choice].name;
} while ((name == NULL || *name == '-') && md->choice != old);
c->flags |= CLIENT_REDRAWOVERLAY;
server_redraw_window_menu(md->w);
return (0);
case KEYC_PPAGE:
case 'b'|KEYC_CTRL:
@@ -428,21 +446,21 @@ menu_key_cb(struct client *c, void *data, struct key_event *event)
break;
}
}
c->flags |= CLIENT_REDRAWOVERLAY;
server_redraw_window_menu(md->w);
break;
case KEYC_NPAGE:
if (md->choice > count - 6) {
md->choice = count - 1;
if (md->choice > n - 6) {
md->choice = n - 1;
name = menu->items[md->choice].name;
} else {
i = 5;
while (i > 0) {
md->choice++;
name = menu->items[md->choice].name;
if (md->choice != count - 1 &&
if (md->choice != n - 1 &&
(name != NULL && *name != '-'))
i--;
else if (md->choice == count - 1)
else if (md->choice == n - 1)
break;
}
}
@@ -450,27 +468,27 @@ menu_key_cb(struct client *c, void *data, struct key_event *event)
md->choice--;
name = menu->items[md->choice].name;
}
c->flags |= CLIENT_REDRAWOVERLAY;
server_redraw_window_menu(md->w);
break;
case 'g':
case KEYC_HOME:
md->choice = 0;
name = menu->items[md->choice].name;
while ((name == NULL || *name == '-') && md->choice != count - 1) {
while ((name == NULL || *name == '-') && md->choice != n - 1) {
md->choice++;
name = menu->items[md->choice].name;
}
c->flags |= CLIENT_REDRAWOVERLAY;
server_redraw_window_menu(md->w);
break;
case 'G':
case KEYC_END:
md->choice = count - 1;
md->choice = n - 1;
name = menu->items[md->choice].name;
while ((name == NULL || *name == '-') && md->choice != 0) {
md->choice--;
name = menu->items[md->choice].name;
}
c->flags |= CLIENT_REDRAWOVERLAY;
server_redraw_window_menu(md->w);
break;
case 'f'|KEYC_CTRL:
break;
@@ -495,14 +513,17 @@ chosen:
return (1);
}
if (md->cb != NULL) {
md->cb(md->menu, md->choice, item->key, md->data);
md->cb = NULL;
return (1);
md->cb(md->menu, md->choice, item->key, md->data);
md->cb = NULL;
return (1);
}
if (md->item != NULL)
event = cmdq_get_event(md->item);
else
if (md->key != KEYC_NONE) {
memset(&saved_event, 0, sizeof saved_event);
saved_event.key = md->key;
memcpy(&saved_event.m, &md->m, sizeof saved_event.m);
event = &saved_event;
} else
event = NULL;
state = cmdq_new_state(&md->fs, event, 0);
@@ -516,11 +537,10 @@ chosen:
return (1);
}
static void
menu_resize_cb(struct client *c, void *data)
void
menu_resize(struct menu_data *md, struct window *w)
{
struct menu_data *md = data;
u_int nx, ny, w, h;
u_int nx, ny, sx, sy;
if (md == NULL)
return;
@@ -528,56 +548,74 @@ menu_resize_cb(struct client *c, void *data)
nx = md->px;
ny = md->py;
w = md->menu->width + 4;
h = md->menu->count + 2;
sx = md->menu->width + 4;
sy = md->menu->count + 2;
if (nx + w > c->tty.sx) {
if (c->tty.sx <= w)
if (nx + sx > w->sx) {
if (w->sx <= sx)
nx = 0;
else
nx = c->tty.sx - w;
nx = w->sx - sx;
}
if (ny + h > c->tty.sy) {
if (c->tty.sy <= h)
if (ny + sy > w->sy) {
if (w->sy <= sy)
ny = 0;
else
ny = c->tty.sy - h;
ny = w->sy - sy;
}
md->px = nx;
md->py = ny;
}
struct menu_data *
menu_prepare(struct menu *menu, int flags, int starting_choice,
int
menu_display(struct menu *menu, int flags, int starting_choice,
struct cmdq_item *item, u_int px, u_int py, struct client *c,
enum box_lines lines, const char *style, const char *selected_style,
const char *border_style, struct cmd_find_state *fs, menu_choice_cb cb,
void *data)
{
struct menu_data *md;
struct key_event *event;
int choice;
const char *name;
struct options *o = c->session->curw->window->options;
u_int sx, sy;
struct window *w;
struct options *o;
if (c->tty.sx < menu->width + 4 || c->tty.sy < menu->count + 2)
return (NULL);
if (px + menu->width + 4 > c->tty.sx)
px = c->tty.sx - menu->width - 4;
if (py + menu->count + 2 > c->tty.sy)
py = c->tty.sy - menu->count - 2;
if (fs == NULL)
w = c->session->curw->window;
else
w = fs->w;
o = w->options;
/* Remember where this menu is so -x/-y L can reuse the position. */
c->menu_last_px = px;
c->menu_last_py = py;
sx = menu->width + 4;
sy = menu->count + 2;
if (sx >= w->sx)
px = 0;
else if (px + sx > w->sx)
px = w->sx - sx;
if (sy >= w->sy)
py = 0;
else if (py + sy > w->sy)
py = w->sy - sy;
w->menu_last_px = px;
w->menu_last_py = py;
if (lines == BOX_LINES_DEFAULT)
lines = options_get_number(o, "menu-border-lines");
md = xcalloc(1, sizeof *md);
md->item = item;
md->w = w;
md->flags = flags;
md->border_lines = lines;
md->key = KEYC_NONE;
if (item != NULL) {
event = cmdq_get_event(item);
md->key = event->key;
memcpy(&md->m, &event->m, sizeof md->m);
}
if (style != NULL)
md->style = xstrdup(style);
@@ -588,7 +626,9 @@ menu_prepare(struct menu *menu, int flags, int starting_choice,
if (fs != NULL)
cmd_find_copy_state(&md->fs, fs);
screen_init(&md->s, menu->width + 4, menu->count + 2, 0);
else if (cmd_find_from_window(&md->fs, w, 0) != 0)
cmd_find_clear_state(&md->fs, 0);
screen_init(&md->s, sx, sy, 0);
if (~md->flags & MENU_NOMOUSE)
md->s.mode |= (MODE_MOUSE_ALL|MODE_MOUSE_BUTTON);
md->s.mode &= ~MODE_CURSOR;
@@ -599,6 +639,9 @@ menu_prepare(struct menu *menu, int flags, int starting_choice,
md->menu = menu;
md->choice = -1;
md->cb = cb;
md->data = data;
if (md->flags & MENU_NOMOUSE) {
if (starting_choice >= (int)menu->count) {
starting_choice = menu->count - 1;
@@ -630,25 +673,11 @@ menu_prepare(struct menu *menu, int flags, int starting_choice,
}
}
md->cb = cb;
md->data = data;
return (md);
}
menu_close(md->w);
md->w->menu = md;
int
menu_display(struct menu *menu, int flags, int starting_choice,
struct cmdq_item *item, u_int px, u_int py, struct client *c,
enum box_lines lines, const char *style, const char *selected_style,
const char *border_style, struct cmd_find_state *fs, menu_choice_cb cb,
void *data)
{
struct menu_data *md;
md = menu_prepare(menu, flags, starting_choice, item, px, py, c, lines,
style, selected_style, border_style, fs, cb, data);
if (md == NULL)
return (-1);
server_client_set_overlay(c, 0, menu_check_cb, menu_mode_cb,
menu_draw_cb, menu_key_cb, menu_free_cb, menu_resize_cb, md);
redraw_invalidate_scene(md->w);
window_update_focus(md->w);
server_redraw_window(md->w);
return (0);
}

View File

@@ -1,4 +1,4 @@
/* $OpenBSD: mode-tree.c,v 1.99 2026/07/06 12:08:52 nicm Exp $ */
/* $OpenBSD: mode-tree.c,v 1.100 2026/07/14 19:07:03 nicm Exp $ */
/*
* Copyright (c) 2017 Nicholas Marriott <nicholas.marriott@gmail.com>
@@ -1409,6 +1409,8 @@ mode_tree_display_menu(struct mode_tree_data *mtd, struct client *c, u_int x,
x -= (menu->width + 4) / 2;
else
x = 0;
x += mtd->wp->xoff;
y += mtd->wp->yoff;
if (menu_display(menu, 0, 0, NULL, x, y, c, BOX_LINES_DEFAULT, NULL,
NULL, NULL, NULL, mode_tree_menu_callback, mtm) != 0) {
mode_tree_remove_ref(mtd);

217
popup.c
View File

@@ -1,4 +1,4 @@
/* $OpenBSD: popup.c,v 1.75 2026/07/09 07:32:58 nicm Exp $ */
/* $OpenBSD: popup.c,v 1.76 2026/07/14 19:07:03 nicm Exp $ */
/*
* Copyright (c) 2020 Nicholas Marriott <nicholas.marriott@gmail.com>
@@ -42,7 +42,6 @@ struct popup_data {
struct colour_palette palette;
struct visible_ranges r;
struct visible_ranges or[2];
struct job *job;
struct input_ctx *ictx;
@@ -50,8 +49,6 @@ struct popup_data {
popup_close_cb cb;
void *arg;
struct menu *menu;
struct menu_data *md;
int close;
/* Current position and size. */
@@ -75,19 +72,6 @@ struct popup_data {
u_int lb;
};
static const struct menu_item popup_menu_items[] = {
{ "Close", 'q', NULL },
{ "#{?buffer_name,Paste #[underscore]#{buffer_name},}", 'p', NULL },
{ "", KEYC_NONE, NULL },
{ "Fill Space", 'F', NULL },
{ "Centre", 'C', NULL },
{ "", KEYC_NONE, NULL },
{ "To Horizontal Pane", 'h', NULL },
{ "To Vertical Pane", 'v', NULL },
{ NULL, KEYC_NONE, NULL }
};
static void
popup_free(struct popup_data *pd)
{
@@ -97,8 +81,6 @@ popup_free(struct popup_data *pd)
job_free(pd->job);
input_free(pd->ictx);
free(pd->or[0].ranges);
free(pd->or[1].ranges);
free(pd->r.ranges);
screen_free(&pd->s);
colour_palette_free(&pd->palette);
@@ -205,9 +187,6 @@ popup_mode_cb(__unused struct client *c, void *data, u_int *cx, u_int *cy)
{
struct popup_data *pd = data;
if (pd->md != NULL)
return (menu_mode_cb(c, pd->md, cx, cy));
if (pd->border_lines == BOX_LINES_NONE) {
*cx = pd->px + pd->s.cx;
*cy = pd->py + pd->s.cy;
@@ -220,52 +199,11 @@ popup_mode_cb(__unused struct client *c, void *data, u_int *cx, u_int *cy)
/* Return parts of the input range which are not obstructed by the popup. */
static struct visible_ranges *
popup_check_cb(struct client* c, void *data, u_int px, u_int py, u_int nx)
popup_check_cb(__unused struct client* c, void *data, u_int px, u_int py,
u_int nx)
{
struct popup_data *pd = data;
struct visible_ranges *r = &pd->r;
struct visible_ranges *mr;
u_int i, j, k = 0;
if (pd->md != NULL) {
/*
* Work out the visible ranges for the menu (that is, the
* ranges not covered by the menu). A menu should have at most
* two ranges and we rely on this being the case.
*/
mr = menu_check_cb(c, pd->md, px, py, nx);
if (mr->used > 2)
fatalx("too many menu ranges");
/*
* Walk the ranges still visible under the menu and check if
* each is visible under the popup as well. At most there can be
* three total ranges if popup and menu do not intersect.
*/
for (i = 0; i < mr->used; i++) {
server_client_overlay_range(pd->px, pd->py, pd->sx,
pd->sy, mr->ranges[i].px, py, mr->ranges[i].nx,
&pd->or[i]);
}
/*
* We now have nonoverlapping ranges from left to right.
* Combine them together into the output.
*/
server_client_ensure_ranges(r, 3);
for (i = 0; i < mr->used; i++) {
for (j = 0; j < pd->or[i].used; j++) {
if (pd->or[i].ranges[j].nx == 0)
continue;
if (k >= 3)
fatalx("too many popup & menu ranges");
r->ranges[k].px = pd->or[i].ranges[j].px;
r->ranges[k].nx = pd->or[i].ranges[j].nx;
k++;
}
}
return (r);
}
server_client_overlay_range(pd->px, pd->py, pd->sx, pd->sy, px, py, nx,
r);
@@ -315,34 +253,21 @@ popup_draw_cb(struct client *c, void *data)
style_ctx.dim = 0;
style_ctx.hyperlinks = s.hyperlinks;
if (pd->md != NULL) {
c->overlay_check = menu_check_cb;
c->overlay_data = pd->md;
} else {
c->overlay_check = NULL;
c->overlay_data = NULL;
}
c->overlay_check = NULL;
c->overlay_data = NULL;
for (i = 0; i < pd->sy; i++)
tty_draw_line(tty, &s, 0, i, pd->sx, px, py + i, &style_ctx);
screen_free(&s);
if (pd->md != NULL) {
c->overlay_check = NULL;
c->overlay_data = NULL;
menu_draw_cb(c, pd->md);
}
c->overlay_check = popup_check_cb;
c->overlay_data = pd;
}
static void
popup_free_cb(struct client *c, void *data)
popup_free_cb(__unused struct client *c, void *data)
{
struct popup_data *pd = data;
struct cmdq_item *item = pd->item;
if (pd->md != NULL)
menu_free_cb(c, pd->md);
if (pd->cb != NULL)
pd->cb(pd->status, pd->arg);
@@ -364,8 +289,6 @@ popup_resize_cb(__unused struct client *c, void *data)
if (pd == NULL)
return;
if (pd->md != NULL)
menu_free_cb(c, pd->md);
/* Adjust position and size. */
if (pd->psy > tty->sy)
@@ -397,96 +320,6 @@ popup_resize_cb(__unused struct client *c, void *data)
}
}
static void
popup_make_pane(struct popup_data *pd, enum layout_type type)
{
struct client *c = pd->c;
struct session *s = c->session;
struct window *w = s->curw->window;
struct layout_cell *lc;
struct window_pane *wp = w->active, *new_wp;
u_int hlimit;
const char *shell;
window_unzoom(w, 1);
lc = layout_split_pane(wp, type, -1, 0);
if (lc == NULL)
return;
hlimit = options_get_number(s->options, "history-limit");
new_wp = window_add_pane(wp->window, NULL, hlimit, 0);
layout_assign_pane(lc, new_wp, 0);
if (pd->job != NULL) {
new_wp->fd = job_transfer(pd->job, &new_wp->pid, new_wp->tty,
sizeof new_wp->tty);
pd->job = NULL;
}
screen_set_title(&pd->s, new_wp->base.title, 0);
screen_free(&new_wp->base);
memcpy(&new_wp->base, &pd->s, sizeof wp->base);
screen_resize(&new_wp->base, new_wp->sx, new_wp->sy, 1);
screen_init(&pd->s, 1, 1, 0);
shell = options_get_string(s->options, "default-shell");
if (!checkshell(shell))
shell = _PATH_BSHELL;
new_wp->shell = xstrdup(shell);
window_pane_set_event(new_wp);
window_set_active_pane(w, new_wp, 1);
new_wp->flags |= PANE_CHANGED;
pd->close = 1;
}
static void
popup_menu_done(__unused struct menu *menu, __unused u_int choice,
key_code key, void *data)
{
struct popup_data *pd = data;
struct client *c = pd->c;
struct paste_buffer *pb;
const char *buf;
size_t len;
pd->md = NULL;
pd->menu = NULL;
server_redraw_client(pd->c);
switch (key) {
case 'p':
pb = paste_get_top(NULL);
if (pb != NULL) {
buf = paste_buffer_data(pb, &len);
bufferevent_write(job_get_event(pd->job), buf, len);
}
break;
case 'F':
pd->sx = c->tty.sx;
pd->sy = c->tty.sy;
pd->px = 0;
pd->py = 0;
server_redraw_client(c);
break;
case 'C':
pd->px = c->tty.sx / 2 - pd->sx / 2;
pd->py = c->tty.sy / 2 - pd->sy / 2;
server_redraw_client(c);
break;
case 'h':
popup_make_pane(pd, LAYOUT_LEFTRIGHT);
break;
case 'v':
popup_make_pane(pd, LAYOUT_TOPBOTTOM);
break;
case 'q':
pd->close = 1;
break;
}
}
static void
popup_handle_drag(struct client *c, struct popup_data *pd,
struct mouse_event *m)
@@ -552,21 +385,9 @@ popup_key_cb(struct client *c, void *data, struct key_event *event)
struct mouse_event *m = &event->m;
const char *buf;
size_t len;
u_int px, py, x;
u_int px, py;
enum { NONE, LEFT, RIGHT, TOP, BOTTOM } border = NONE;
if (pd->md != NULL) {
if (menu_key_cb(c, pd->md, event) == 1) {
pd->md = NULL;
pd->menu = NULL;
if (pd->close)
server_client_clear_overlay(c);
else
server_redraw_client(c);
}
return (0);
}
if (KEYC_IS_MOUSE(event->key)) {
if (pd->dragging != OFF) {
popup_handle_drag(c, pd, m);
@@ -576,8 +397,6 @@ popup_key_cb(struct client *c, void *data, struct key_event *event)
m->x > pd->px + pd->sx - 1 ||
m->y < pd->py ||
m->y > pd->py + pd->sy - 1) {
if (MOUSE_BUTTONS(m->b) == MOUSE_BUTTON_3)
goto menu;
return (0);
}
if (pd->border_lines != BOX_LINES_NONE) {
@@ -593,7 +412,7 @@ popup_key_cb(struct client *c, void *data, struct key_event *event)
if ((m->b & MOUSE_MASK_MODIFIERS) == 0 &&
MOUSE_BUTTONS(m->b) == MOUSE_BUTTON_3 &&
(border == LEFT || border == TOP))
goto menu;
goto out;
if (((m->b & MOUSE_MASK_MODIFIERS) == MOUSE_MASK_META) ||
(border != NONE && !MOUSE_DRAG(m->lb))) {
if (!MOUSE_DRAG(m->b))
@@ -633,17 +452,6 @@ popup_key_cb(struct client *c, void *data, struct key_event *event)
}
return (0);
menu:
pd->menu = menu_create("");
menu_add_items(pd->menu, popup_menu_items, NULL, c, NULL);
if (m->x >= (pd->menu->width + 4) / 2)
x = m->x - (pd->menu->width + 4) / 2;
else
x = 0;
pd->md = menu_prepare(pd->menu, 0, 0, NULL, x, m->y, c,
BOX_LINES_DEFAULT, NULL, NULL, NULL, NULL, popup_menu_done, pd);
c->flags |= CLIENT_REDRAWOVERLAY;
out:
pd->lx = m->x;
pd->ly = m->y;
@@ -664,13 +472,8 @@ popup_job_update_cb(struct job *job)
if (size == 0)
return;
if (pd->md != NULL) {
c->overlay_check = menu_check_cb;
c->overlay_data = pd->md;
} else {
c->overlay_check = NULL;
c->overlay_data = NULL;
}
c->overlay_check = NULL;
c->overlay_data = NULL;
input_parse_screen(pd->ictx, s, popup_init_ctx_cb, pd, data, size);
c->overlay_check = popup_check_cb;
c->overlay_data = pd;

View File

@@ -1,4 +1,4 @@
/* $OpenBSD: screen-redraw.c,v 1.150 2026/07/09 07:32:58 nicm Exp $ */
/* $OpenBSD: screen-redraw.c,v 1.151 2026/07/14 19:07:03 nicm Exp $ */
/*
* Copyright (c) 2026 Nicholas Marriott <nicholas.marriott@gmail.com>
@@ -68,8 +68,9 @@ enum redraw_span_type {
REDRAW_SPAN_STATUS, /* pane status line */
REDRAW_SPAN_BORDER, /* pane border */
REDRAW_SPAN_SCROLLBAR, /* pane scrollbar */
REDRAW_SPAN_MENU, /* window menu */
};
#define REDRAW_SPAN_TYPES 6
#define REDRAW_SPAN_TYPES 7
/* Border connections to adjacent cells. */
#define REDRAW_BORDER_L 0x1
@@ -91,7 +92,8 @@ enum redraw_span_type {
#define REDRAW_PANE_STATUS 0x10
#define REDRAW_PANE_SCROLLBAR 0x20
#define REDRAW_STATUS 0x40
#define REDRAW_OVERLAY 0x80
#define REDRAW_MENU 0x80
#define REDRAW_OVERLAY 0x100
/* Draw everything. */
#define REDRAW_ALL 0x7fffffff
@@ -163,6 +165,15 @@ struct redraw_span_data {
/* Flags for this span. */
int flags;
} sb; /* pane scrollbar */
struct {
/* Menu this span belongs to. */
struct menu_data *md;
/* Position of span inside the menu. */
u_int px;
u_int py;
} m; /* menu */
};
};
@@ -251,6 +262,8 @@ redraw_flags_to_string(int flags)
strlcat(s, "pane-status ", sizeof s);
if (flags & REDRAW_PANE_SCROLLBAR)
strlcat(s, "scrollbar ", sizeof s);
if (flags & REDRAW_MENU)
strlcat(s, "menu ", sizeof s);
if (flags & REDRAW_OVERLAY)
strlcat(s, "overlay ", sizeof s);
if (REDRAW_IS_ALL(flags))
@@ -828,6 +841,34 @@ redraw_mark_two_pane_colours(struct redraw_build_ctx *bctx)
}
}
/* Mark the window menu above all panes. */
static void
redraw_mark_menu(struct redraw_build_ctx *bctx)
{
struct menu_data *md = bctx->w->menu;
struct redraw_build_cell *bc;
u_int px, py, x, y, sx, sy;
if (md == NULL)
return;
sx = menu_width(md);
sy = menu_height(md);
for (py = 0; py < sy; py++) {
for (px = 0; px < sx; px++) {
if (!redraw_window_to_scene(bctx, menu_x(md) + px,
menu_y(md) + py, &x, &y))
continue;
bc = redraw_get_build_cell(bctx, x, y);
memset(bc, 0, sizeof *bc);
bc->data.type = REDRAW_SPAN_MENU;
bc->data.m.md = md;
bc->data.m.px = px;
bc->data.m.py = py;
}
}
}
/* Return true if two adjacent build cells can be joined into one span. */
static int
redraw_compare_data(struct redraw_build_cell *a, struct redraw_build_cell *b)
@@ -874,6 +915,12 @@ redraw_compare_data(struct redraw_build_cell *a, struct redraw_build_cell *b)
ad->sb.flags != bd->sb.flags)
return (0);
return (1);
case REDRAW_SPAN_MENU:
if (ad->m.md != bd->m.md ||
ad->m.py != bd->m.py ||
ad->m.px + 1 != bd->m.px)
return (0);
return (1);
case REDRAW_SPAN_OUTSIDE:
case REDRAW_SPAN_EMPTY:
return (1);
@@ -908,6 +955,7 @@ redraw_build_cells(struct redraw_build_ctx *bctx)
TAILQ_FOREACH_REVERSE(wp, &w->z_index, window_panes_zindex, zentry)
redraw_mark_pane(bctx, wp);
redraw_mark_two_pane_colours(bctx);
redraw_mark_menu(bctx);
}
/*
@@ -1303,6 +1351,20 @@ redraw_draw_scrollbar_span(struct redraw_draw_ctx *dctx,
}
}
/* Draw a menu span. */
static void
redraw_draw_menu_span(struct redraw_draw_ctx *dctx,
struct redraw_span *span, u_int x, u_int y, u_int n)
{
struct redraw_scene *scene = dctx->scene;
struct tty *tty = &scene->c->tty;
struct screen *s = menu_screen(span->data.m.md);
u_int px;
px = span->data.m.px + (x - span->x);
tty_draw_line(tty, s, px, span->data.m.py, n, x, y, NULL);
}
/* Draw a span. */
static void
redraw_draw_span(struct redraw_draw_ctx *dctx, struct redraw_span *span,
@@ -1343,6 +1405,9 @@ redraw_draw_span(struct redraw_draw_ctx *dctx, struct redraw_span *span,
case REDRAW_SPAN_SCROLLBAR:
redraw_draw_scrollbar_span(dctx, span, x, y, n);
break;
case REDRAW_SPAN_MENU:
redraw_draw_menu_span(dctx, span, x, y, n);
break;
}
}
}
@@ -1434,6 +1499,10 @@ redraw_draw_lines(struct redraw_draw_ctx *dctx, int flags)
if (~flags & REDRAW_PANE_SCROLLBAR)
continue;
break;
case REDRAW_SPAN_MENU:
if (~flags & REDRAW_MENU)
continue;
break;
default:
continue;
}
@@ -1445,6 +1514,26 @@ redraw_draw_lines(struct redraw_draw_ctx *dctx, int flags)
}
}
/* Draw menu spans. */
static void
redraw_draw_menu_lines(struct redraw_draw_ctx *dctx)
{
struct redraw_scene *scene = dctx->scene;
struct redraw_line *line;
struct redraw_span *span;
u_int y, cy;
for (y = 0; y < scene->sy; y++) {
line = &scene->lines[y];
if (dctx->flags & REDRAW_STATUS_TOP)
cy = dctx->status_lines + y;
else
cy = y;
TAILQ_FOREACH(span, &line->spans[REDRAW_SPAN_MENU], entry)
redraw_draw_span(dctx, span, cy);
}
}
/* Get line for pane status line. */
static int
redraw_pane_status_line(struct redraw_draw_ctx *dctx,
@@ -1620,6 +1709,8 @@ redraw_draw(struct client *c, struct window_pane *wp, int flags)
if (scene == NULL)
return;
redraw_set_draw_context(&dctx, scene);
if (w->menu != NULL)
menu_update(w->menu);
if (flags & (REDRAW_PANE_BORDER|REDRAW_PANE_STATUS)) {
TAILQ_FOREACH(loop, &scene->w->panes, entry) {
@@ -1686,6 +1777,8 @@ redraw_draw(struct client *c, struct window_pane *wp, int flags)
}
}
}
if (w->menu != NULL && (flags & REDRAW_MENU))
redraw_draw_menu_lines(&dctx);
if (flags & REDRAW_STATUS) {
lines = dctx.status_lines;
@@ -1774,6 +1867,10 @@ redraw_screen(struct client *c)
flags |= (REDRAW_STATUS|REDRAW_PANE_STATUS);
if (c->flags & CLIENT_REDRAWOVERLAY)
flags |= REDRAW_OVERLAY;
if (c->flags & CLIENT_REDRAWMENU)
flags |= REDRAW_MENU;
if (c->session->curw->window->menu != NULL)
flags |= REDRAW_MENU;
if (flags != 0)
redraw_draw(c, NULL, flags);
}
@@ -1784,6 +1881,8 @@ void
redraw_pane(struct client *c, struct window_pane *wp)
{
redraw_draw(c, wp, REDRAW_PANE|REDRAW_PANE_SCROLLBAR);
if (c->session->curw->window->menu != NULL)
redraw_draw(c, NULL, REDRAW_MENU);
}
/* Draw a pane's scrollbar. */

View File

@@ -1,4 +1,4 @@
/* $OpenBSD: server-client.c,v 1.490 2026/07/14 17:17:17 nicm Exp $ */
/* $OpenBSD: server-client.c,v 1.491 2026/07/14 19:07:03 nicm Exp $ */
/*
* Copyright (c) 2009 Nicholas Marriott <nicholas.marriott@gmail.com>
@@ -48,8 +48,6 @@ 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. */
@@ -1651,6 +1649,40 @@ out:
return (CMD_RETURN_NORMAL);
}
/* Handle a key event for the active window menu, if any. */
static int
server_client_handle_menu_key(struct client *c, struct key_event *event)
{
struct window *w = c->session->curw->window;
struct key_event new_event;
struct mouse_event *m;
u_int ox, oy, sx, sy;
if (w->menu == NULL)
return (0);
memcpy(&new_event, event, sizeof new_event);
if (KEYC_IS_MOUSE(event->key)) {
m = &new_event.m;
tty_window_offset(&c->tty, &ox, &oy, &sx, &sy);
m->x += ox;
if (m->statusat == 0) {
if (m->y < m->statuslines)
m->x = m->y = UINT_MAX;
else
m->y = m->y - m->statuslines + oy;
} else if (m->statusat > 0 &&
m->y >= (u_int)m->statusat)
m->x = m->y = UINT_MAX;
else
m->y += oy;
}
if (menu_key(c, w->menu, &new_event) == 1)
menu_close(w);
return (1);
}
/* Handle a key event. */
static int
server_client_handle_key0(struct client *c, struct key_event *event,
@@ -1700,6 +1732,8 @@ server_client_handle_key0(struct client *c, struct key_event *event,
}
server_client_clear_overlay(c);
if (server_client_handle_menu_key(c, event))
return (0);
if (c->prompt != NULL) {
switch (status_prompt_key(c, event->key, &event->m)) {
case PROMPT_KEY_HANDLED:
@@ -1722,7 +1756,8 @@ server_client_handle_key0(struct client *c, struct key_event *event,
if (wp != NULL &&
window_pane_has_prompt(wp) &&
window_pane_is_visible(wp)) {
switch (window_pane_prompt_key(wp, c, event->key, &event->m)) {
switch (window_pane_prompt_key(wp, c, event->key,
&event->m)) {
case PROMPT_KEY_HANDLED:
case PROMPT_KEY_CLOSE:
case PROMPT_KEY_MOVE:
@@ -2074,6 +2109,9 @@ server_client_reset_state(struct client *c)
if (c->overlay_draw != NULL) {
if (c->overlay_mode != NULL)
s = c->overlay_mode(c, c->overlay_data, &cx, &cy);
} else if (w->menu != NULL) {
menu_get_cursor(w->menu, &cx, &cy);
s = menu_screen(w->menu);
} else if (wp != NULL && c->prompt == NULL)
s = wp->screen;
else
@@ -2094,7 +2132,22 @@ server_client_reset_state(struct client *c)
prompt = 1;
status_prompt_cursor(c, &cx, &cy);
} else if (wp != NULL && c->overlay_draw == NULL) {
prompt = server_client_prompt_cursor(c, wp, &mode, &cx, &cy);
if (w->menu != NULL) {
tty_window_offset(tty, &ox, &oy, &sx, &sy);
if (cx < ox || cx >= ox + sx ||
cy < oy || cy >= oy + sy)
mode &= ~MODE_CURSOR;
else {
cx -= ox;
cy -= oy;
if (status_at_line(c) == 0)
cy += status_line_size(c);
}
prompt = 1;
} else {
prompt = server_client_prompt_cursor(c, wp, &mode, &cx,
&cy);
}
if (!prompt) {
cursor = 0;
pane_mode = wp->base.mode;
@@ -2146,7 +2199,7 @@ server_client_reset_state(struct client *c)
* movement events.
*/
if (options_get_number(oo, "mouse")) {
if (c->overlay_draw == NULL) {
if (c->overlay_draw == NULL && w->menu == NULL) {
mode &= ~ALL_MOUSE_MODES;
TAILQ_FOREACH(loop, &w->panes, entry) {
if (loop->screen->mode & MODE_MOUSE_ALL)
@@ -2325,11 +2378,12 @@ server_client_check_redraw(struct client *c)
if (c->flags & (CLIENT_CONTROL|CLIENT_SUSPENDED))
return;
if (c->flags & CLIENT_ALLREDRAWFLAGS) {
log_debug("%s: redraw%s%s%s%s", c->name,
log_debug("%s: redraw%s%s%s%s%s", c->name,
(c->flags & CLIENT_REDRAWWINDOW) ? " window" : "",
(c->flags & CLIENT_REDRAWSTATUS) ? " status" : "",
(c->flags & CLIENT_REDRAWBORDERS) ? " borders" : "",
(c->flags & CLIENT_REDRAWOVERLAY) ? " overlay" : "");
(c->flags & CLIENT_REDRAWOVERLAY) ? " overlay" : "",
(c->flags & CLIENT_REDRAWMENU) ? " menu" : "");
}
/* Work out if a redraw is actually needed. */

View File

@@ -1,4 +1,4 @@
/* $OpenBSD: server-fn.c,v 1.147 2026/07/10 13:38:45 nicm Exp $ */
/* $OpenBSD: server-fn.c,v 1.148 2026/07/14 19:07:03 nicm Exp $ */
/*
* Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>
@@ -128,6 +128,19 @@ server_redraw_window(struct window *w)
}
}
void
server_redraw_window_menu(struct window *w)
{
struct client *c;
TAILQ_FOREACH(c, &clients, entry) {
if (c->session != NULL &&
c->session->curw != NULL &&
c->session->curw->window == w)
c->flags |= CLIENT_REDRAWMENU;
}
}
void
server_redraw_window_borders(struct window *w)
{

36
tmux.h
View File

@@ -1,4 +1,4 @@
/* $OpenBSD: tmux.h,v 1.1403 2026/07/14 17:17:18 nicm Exp $ */
/* $OpenBSD: tmux.h,v 1.1404 2026/07/14 19:07:03 nicm Exp $ */
/*
* Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>
@@ -1435,6 +1435,10 @@ struct window {
uint64_t redraw_scene_generation;
struct menu_data *menu;
u_int menu_last_px;
u_int menu_last_py;
u_int last_new_pane_x;
u_int last_new_pane_y;
@@ -2259,7 +2263,7 @@ struct client {
#define CLIENT_CONTROL_NOOUTPUT 0x4000000
#define CLIENT_DEFAULTSOCKET 0x8000000
#define CLIENT_STARTSERVER 0x10000000
/* 0x20000000 unused */
#define CLIENT_REDRAWMENU 0x20000000
#define CLIENT_NOFORK 0x40000000
#define CLIENT_ACTIVEPANE 0x80000000ULL
#define CLIENT_CONTROL_PAUSEAFTER 0x100000000ULL
@@ -2275,7 +2279,8 @@ struct client {
CLIENT_REDRAWSTATUS| \
CLIENT_REDRAWSTATUSALWAYS| \
CLIENT_REDRAWBORDERS| \
CLIENT_REDRAWOVERLAY)
CLIENT_REDRAWOVERLAY| \
CLIENT_REDRAWMENU)
#define CLIENT_UNATTACHEDFLAGS \
(CLIENT_DEAD| \
CLIENT_SUSPENDED| \
@@ -2329,9 +2334,6 @@ struct client {
void *overlay_data;
struct event overlay_timer;
u_int menu_last_px;
u_int menu_last_py;
struct client_files files;
u_int source_file_depth;
@@ -3317,6 +3319,7 @@ void server_redraw_session_group(struct session *);
void server_status_session(struct session *);
void server_status_session_group(struct session *);
void server_redraw_window(struct window *);
void server_redraw_window_menu(struct window *);
void server_redraw_window_borders(struct window *);
void server_status_window(struct window *);
void server_lock(void);
@@ -4114,20 +4117,21 @@ void menu_add_item(struct menu *, const struct menu_item *,
struct cmdq_item *, struct client *,
struct cmd_find_state *);
void menu_free(struct menu *);
struct menu_data *menu_prepare(struct menu *, int, int, struct cmdq_item *,
u_int, u_int, struct client *, enum box_lines, const char *,
const char *, const char *, struct cmd_find_state *,
menu_choice_cb, void *);
int menu_display(struct menu *, int, int, struct cmdq_item *,
u_int, u_int, struct client *, enum box_lines, const char *,
const char *, const char *, struct cmd_find_state *,
menu_choice_cb, void *);
struct screen *menu_mode_cb(struct client *, void *, u_int *, u_int *);
struct visible_ranges *menu_check_cb(struct client *, void *, u_int, u_int,
u_int);
void menu_draw_cb(struct client *, void *);
void menu_free_cb(struct client *, void *);
int menu_key_cb(struct client *, void *, struct key_event *);
void menu_close(struct window *);
void menu_destroy(struct window *);
void menu_update(struct menu_data *);
struct screen *menu_screen(struct menu_data *);
u_int menu_width(struct menu_data *);
u_int menu_height(struct menu_data *);
u_int menu_x(struct menu_data *);
u_int menu_y(struct menu_data *);
void menu_get_cursor(struct menu_data *, u_int *, u_int *);
void menu_resize(struct menu_data *, struct window *);
int menu_key(struct client *, struct menu_data *, struct key_event *);
/* popup.c */
#define POPUP_CLOSEEXIT 0x1

View File

@@ -1,4 +1,4 @@
/* $OpenBSD: window.c,v 1.362 2026/07/14 17:17:18 nicm Exp $ */
/* $OpenBSD: window.c,v 1.363 2026/07/14 19:07:03 nicm Exp $ */
/*
* Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>
@@ -460,6 +460,7 @@ window_destroy(struct window *w)
layout_free_cell(w->saved_layout_root, 0);
free(w->old_layout);
menu_destroy(w);
window_destroy_panes(w);
if (event_initialized(&w->name_event))
@@ -568,6 +569,11 @@ window_resize(struct window *w, u_int sx, u_int sy, int xpixel, int ypixel)
ypixel == -1 ? w->ypixel : (u_int)ypixel);
w->sx = sx;
w->sy = sy;
if (w->menu != NULL) {
menu_resize(w->menu, w);
redraw_invalidate_scene(w);
server_redraw_window(w);
}
if (xpixel != -1)
w->xpixel = xpixel;
if (ypixel != -1)
@@ -651,7 +657,8 @@ window_pane_update_focus(struct window_pane *wp)
c->session->attached != 0 &&
(c->flags & CLIENT_FOCUSED) &&
c->session->curw->window == wp->window &&
c->overlay_draw == NULL) {
c->overlay_draw == NULL &&
wp->window->menu == NULL) {
focused = 1;
break;
}