diff --git a/cmd-display-menu.c b/cmd-display-menu.c index ba1bcf4ae..2aaf3238c 100644 --- a/cmd-display-menu.c +++ b/cmd-display-menu.c @@ -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 @@ -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'); diff --git a/menu.c b/menu.c index ec7b9781b..aab25d7c5 100644 --- a/menu.c +++ b/menu.c @@ -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 @@ -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); } diff --git a/mode-tree.c b/mode-tree.c index ef859b15e..defd587bb 100644 --- a/mode-tree.c +++ b/mode-tree.c @@ -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 @@ -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); diff --git a/popup.c b/popup.c index fad0eb498..3bc7eafa6 100644 --- a/popup.c +++ b/popup.c @@ -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 @@ -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; diff --git a/screen-redraw.c b/screen-redraw.c index fd67041f7..454c5df6c 100644 --- a/screen-redraw.c +++ b/screen-redraw.c @@ -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 @@ -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. */ diff --git a/server-client.c b/server-client.c index 5c5a374fb..6abbd03fd 100644 --- a/server-client.c +++ b/server-client.c @@ -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 @@ -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. */ diff --git a/server-fn.c b/server-fn.c index 9bf48b162..862f4d836 100644 --- a/server-fn.c +++ b/server-fn.c @@ -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 @@ -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) { diff --git a/tmux.h b/tmux.h index 739b49332..877aee7c1 100644 --- a/tmux.h +++ b/tmux.h @@ -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 @@ -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 diff --git a/window.c b/window.c index f882c2426..abeb87a1c 100644 --- a/window.c +++ b/window.c @@ -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 @@ -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; }