"Fossies" - the Fresh Open Source Software Archive

Member "tmux-3.2a/cmd-display-menu.c" (10 Jun 2021, 10798 Bytes) of package /linux/misc/tmux-3.2a.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "cmd-display-menu.c" see the Fossies "Dox" file reference documentation and the latest Fossies "Diffs" side-by-side code changes report: 3.2_vs_3.2a.

    1 /* $OpenBSD$ */
    2 
    3 /*
    4  * Copyright (c) 2019 Nicholas Marriott <nicholas.marriott@gmail.com>
    5  *
    6  * Permission to use, copy, modify, and distribute this software for any
    7  * purpose with or without fee is hereby granted, provided that the above
    8  * copyright notice and this permission notice appear in all copies.
    9  *
   10  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
   11  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
   12  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
   13  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
   14  * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER
   15  * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
   16  * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
   17  */
   18 
   19 #include <sys/types.h>
   20 
   21 #include <stdlib.h>
   22 #include <string.h>
   23 
   24 #include "tmux.h"
   25 
   26 /*
   27  * Display a menu on a client.
   28  */
   29 
   30 static enum cmd_retval  cmd_display_menu_exec(struct cmd *,
   31                 struct cmdq_item *);
   32 static enum cmd_retval  cmd_display_popup_exec(struct cmd *,
   33                 struct cmdq_item *);
   34 
   35 const struct cmd_entry cmd_display_menu_entry = {
   36     .name = "display-menu",
   37     .alias = "menu",
   38 
   39     .args = { "c:t:OT:x:y:", 1, -1 },
   40     .usage = "[-O] [-c target-client] " CMD_TARGET_PANE_USAGE " [-T title] "
   41          "[-x position] [-y position] name key command ...",
   42 
   43     .target = { 't', CMD_FIND_PANE, 0 },
   44 
   45     .flags = CMD_AFTERHOOK|CMD_CLIENT_CFLAG,
   46     .exec = cmd_display_menu_exec
   47 };
   48 
   49 const struct cmd_entry cmd_display_popup_entry = {
   50     .name = "display-popup",
   51     .alias = "popup",
   52 
   53     .args = { "Cc:d:Eh:t:w:x:y:", 0, -1 },
   54     .usage = "[-CE] [-c target-client] [-d start-directory] [-h height] "
   55              CMD_TARGET_PANE_USAGE " [-w width] "
   56              "[-x position] [-y position] [command]",
   57 
   58     .target = { 't', CMD_FIND_PANE, 0 },
   59 
   60     .flags = CMD_AFTERHOOK|CMD_CLIENT_CFLAG,
   61     .exec = cmd_display_popup_exec
   62 };
   63 
   64 static int
   65 cmd_display_menu_get_position(struct client *tc, struct cmdq_item *item,
   66     struct args *args, u_int *px, u_int *py, u_int w, u_int h)
   67 {
   68     struct tty      *tty = &tc->tty;
   69     struct cmd_find_state   *target = cmdq_get_target(item);
   70     struct key_event    *event = cmdq_get_event(item);
   71     struct session      *s = tc->session;
   72     struct winlink      *wl = target->wl;
   73     struct window_pane  *wp = target->wp;
   74     struct style_ranges *ranges = NULL;
   75     struct style_range  *sr = NULL;
   76     const char      *xp, *yp;
   77     char            *p;
   78     int          top;
   79     u_int            line, ox, oy, sx, sy, lines, position;
   80     long             n;
   81     struct format_tree  *ft;
   82 
   83     /*
   84      * Work out the position from the -x and -y arguments. This is the
   85      * bottom-left position.
   86      */
   87 
   88     /* If the popup is too big, stop now. */
   89     if (w > tty->sx || h > tty->sy)
   90         return (0);
   91 
   92     /* Create format with mouse position if any. */
   93     ft = format_create_from_target(item);
   94     if (event->m.valid) {
   95         format_add(ft, "popup_mouse_x", "%u", event->m.x);
   96         format_add(ft, "popup_mouse_y", "%u", event->m.y);
   97     }
   98 
   99     /*
  100      * If there are any status lines, add this window position and the
  101      * status line position.
  102      */
  103     top = status_at_line(tc);
  104     if (top != -1) {
  105         lines = status_line_size(tc);
  106         if (top == 0)
  107             top = lines;
  108         else
  109             top = 0;
  110         position = options_get_number(s->options, "status-position");
  111 
  112         for (line = 0; line < lines; line++) {
  113             ranges = &tc->status.entries[line].ranges;
  114             TAILQ_FOREACH(sr, ranges, entry) {
  115                 if (sr->type != STYLE_RANGE_WINDOW)
  116                     continue;
  117                 if (sr->argument == (u_int)wl->idx)
  118                     break;
  119             }
  120             if (sr != NULL)
  121                 break;
  122         }
  123         if (line == lines)
  124             ranges = &tc->status.entries[0].ranges;
  125 
  126         if (sr != NULL) {
  127             format_add(ft, "popup_window_status_line_x", "%u",
  128                 sr->start);
  129             if (position == 0) {
  130                 format_add(ft, "popup_window_status_line_y",
  131                     "%u", line + 1 + h);
  132             } else {
  133                 format_add(ft, "popup_window_status_line_y",
  134                     "%u", tty->sy - lines + line);
  135             }
  136         }
  137 
  138         if (position == 0)
  139             format_add(ft, "popup_status_line_y", "%u", lines + h);
  140         else {
  141             format_add(ft, "popup_status_line_y", "%u",
  142                 tty->sy - lines);
  143         }
  144     } else
  145         top = 0;
  146 
  147     /* Popup width and height. */
  148     format_add(ft, "popup_width", "%u", w);
  149     format_add(ft, "popup_height", "%u", h);
  150 
  151     /* Position so popup is in the centre. */
  152     n = (long)(tty->sx - 1) / 2 - w / 2;
  153     if (n < 0)
  154         format_add(ft, "popup_centre_x", "%u", 0);
  155     else
  156         format_add(ft, "popup_centre_x", "%ld", n);
  157     n = (tty->sy - 1) / 2 + h / 2;
  158     if (n >= tty->sy)
  159         format_add(ft, "popup_centre_y", "%u", tty->sy - h);
  160     else
  161         format_add(ft, "popup_centre_y", "%ld", n);
  162 
  163     /* Position of popup relative to mouse. */
  164     if (event->m.valid) {
  165         n = (long)event->m.x - w / 2;
  166         if (n < 0)
  167             format_add(ft, "popup_mouse_centre_x", "%u", 0);
  168         else
  169             format_add(ft, "popup_mouse_centre_x", "%ld", n);
  170         n = event->m.y - h / 2;
  171         if (n + h >= tty->sy) {
  172             format_add(ft, "popup_mouse_centre_y", "%u",
  173                 tty->sy - h);
  174         } else
  175             format_add(ft, "popup_mouse_centre_y", "%ld", n);
  176         n = (long)event->m.y + h;
  177         if (n + h >= tty->sy)
  178             format_add(ft, "popup_mouse_top", "%u", tty->sy - h);
  179         else
  180             format_add(ft, "popup_mouse_top", "%ld", n);
  181         n = event->m.y - h;
  182         if (n < 0)
  183             format_add(ft, "popup_mouse_bottom", "%u", 0);
  184         else
  185             format_add(ft, "popup_mouse_bottom", "%ld", n);
  186     }
  187 
  188     /* Position in pane. */
  189     tty_window_offset(&tc->tty, &ox, &oy, &sx, &sy);
  190     n = top + wp->yoff - oy + h;
  191     if (n >= tty->sy)
  192         format_add(ft, "popup_pane_top", "%u", tty->sy - h);
  193     else
  194         format_add(ft, "popup_pane_top", "%ld", n);
  195     format_add(ft, "popup_pane_bottom", "%u", top + wp->yoff + wp->sy - oy);
  196     format_add(ft, "popup_pane_left", "%u", wp->xoff - ox);
  197     n = (long)wp->xoff + wp->sx - ox - w;
  198     if (n < 0)
  199         format_add(ft, "popup_pane_right", "%u", 0);
  200     else
  201         format_add(ft, "popup_pane_right", "%ld", n);
  202 
  203     /* Expand horizontal position. */
  204     xp = args_get(args, 'x');
  205     if (xp == NULL || strcmp(xp, "C") == 0)
  206         xp = "#{popup_centre_x}";
  207     else if (strcmp(xp, "R") == 0)
  208         xp = "#{popup_pane_right}";
  209     else if (strcmp(xp, "P") == 0)
  210         xp = "#{popup_pane_left}";
  211     else if (strcmp(xp, "M") == 0)
  212         xp = "#{popup_mouse_centre_x}";
  213     else if (strcmp(xp, "W") == 0)
  214         xp = "#{popup_window_status_line_x}";
  215     p = format_expand(ft, xp);
  216     n = strtol(p, NULL, 10);
  217     if (n + w >= tty->sx)
  218         n = tty->sx - w;
  219     else if (n < 0)
  220         n = 0;
  221     *px = n;
  222     log_debug("%s: -x: %s = %s = %u", __func__, xp, p, *px);
  223     free(p);
  224 
  225     /* Expand vertical position  */
  226     yp = args_get(args, 'y');
  227     if (yp == NULL || strcmp(yp, "C") == 0)
  228         yp = "#{popup_centre_y}";
  229     else if (strcmp(yp, "P") == 0)
  230         yp = "#{popup_pane_bottom}";
  231     else if (strcmp(yp, "M") == 0)
  232         yp = "#{popup_mouse_top}";
  233     else if (strcmp(yp, "S") == 0)
  234         yp = "#{popup_status_line_y}";
  235     else if (strcmp(yp, "W") == 0)
  236         yp = "#{popup_window_status_line_y}";
  237     p = format_expand(ft, yp);
  238     n = strtol(p, NULL, 10);
  239     if (n < h)
  240         n = 0;
  241     else
  242         n -= h;
  243     if (n + h >= tty->sy)
  244         n = tty->sy - h;
  245     else if (n < 0)
  246         n = 0;
  247     *py = n;
  248     log_debug("%s: -y: %s = %s = %u", __func__, yp, p, *py);
  249     free(p);
  250 
  251     return (1);
  252 }
  253 
  254 static enum cmd_retval
  255 cmd_display_menu_exec(struct cmd *self, struct cmdq_item *item)
  256 {
  257     struct args     *args = cmd_get_args(self);
  258     struct cmd_find_state   *target = cmdq_get_target(item);
  259     struct key_event    *event = cmdq_get_event(item);
  260     struct client       *tc = cmdq_get_target_client(item);
  261     struct menu     *menu = NULL;
  262     struct menu_item     menu_item;
  263     const char      *key;
  264     char            *title, *name;
  265     int          flags = 0, i;
  266     u_int            px, py;
  267 
  268     if (tc->overlay_draw != NULL)
  269         return (CMD_RETURN_NORMAL);
  270 
  271     if (args_has(args, 'T'))
  272         title = format_single_from_target(item, args_get(args, 'T'));
  273     else
  274         title = xstrdup("");
  275     menu = menu_create(title);
  276 
  277     for (i = 0; i != args->argc; /* nothing */) {
  278         name = args->argv[i++];
  279         if (*name == '\0') {
  280             menu_add_item(menu, NULL, item, tc, target);
  281             continue;
  282         }
  283 
  284         if (args->argc - i < 2) {
  285             cmdq_error(item, "not enough arguments");
  286             free(title);
  287             menu_free(menu);
  288             return (CMD_RETURN_ERROR);
  289         }
  290         key = args->argv[i++];
  291 
  292         menu_item.name = name;
  293         menu_item.key = key_string_lookup_string(key);
  294         menu_item.command = args->argv[i++];
  295 
  296         menu_add_item(menu, &menu_item, item, tc, target);
  297     }
  298     free(title);
  299     if (menu == NULL) {
  300         cmdq_error(item, "invalid menu arguments");
  301         return (CMD_RETURN_ERROR);
  302     }
  303     if (menu->count == 0) {
  304         menu_free(menu);
  305         return (CMD_RETURN_NORMAL);
  306     }
  307     if (!cmd_display_menu_get_position(tc, item, args, &px, &py,
  308         menu->width + 4, menu->count + 2)) {
  309         menu_free(menu);
  310         return (CMD_RETURN_NORMAL);
  311     }
  312 
  313     if (args_has(args, 'O'))
  314         flags |= MENU_STAYOPEN;
  315     if (!event->m.valid)
  316         flags |= MENU_NOMOUSE;
  317     if (menu_display(menu, flags, item, px, py, tc, target, NULL,
  318         NULL) != 0)
  319         return (CMD_RETURN_NORMAL);
  320     return (CMD_RETURN_WAIT);
  321 }
  322 
  323 static enum cmd_retval
  324 cmd_display_popup_exec(struct cmd *self, struct cmdq_item *item)
  325 {
  326     struct args     *args = cmd_get_args(self);
  327     struct cmd_find_state   *target = cmdq_get_target(item);
  328     struct session      *s = target->s;
  329     struct client       *tc = cmdq_get_target_client(item);
  330     struct tty      *tty = &tc->tty;
  331     const char      *value, *shell[] = { NULL, NULL };
  332     const char      *shellcmd = NULL;
  333     char            *cwd, *cause, **argv = args->argv;
  334     int          flags = 0, argc = args->argc;
  335     u_int            px, py, w, h;
  336 
  337     if (args_has(args, 'C')) {
  338         server_client_clear_overlay(tc);
  339         return (CMD_RETURN_NORMAL);
  340     }
  341     if (tc->overlay_draw != NULL)
  342         return (CMD_RETURN_NORMAL);
  343 
  344     h = tty->sy / 2;
  345     if (args_has(args, 'h')) {
  346         h = args_percentage(args, 'h', 1, tty->sy, tty->sy, &cause);
  347         if (cause != NULL) {
  348             cmdq_error(item, "height %s", cause);
  349             free(cause);
  350             return (CMD_RETURN_ERROR);
  351         }
  352     }
  353 
  354     w = tty->sx / 2;
  355     if (args_has(args, 'w')) {
  356         w = args_percentage(args, 'w', 1, tty->sx, tty->sx, &cause);
  357         if (cause != NULL) {
  358             cmdq_error(item, "width %s", cause);
  359             free(cause);
  360             return (CMD_RETURN_ERROR);
  361         }
  362     }
  363 
  364     if (w > tty->sx - 1)
  365         w = tty->sx - 1;
  366     if (h > tty->sy - 1)
  367         h = tty->sy - 1;
  368     if (!cmd_display_menu_get_position(tc, item, args, &px, &py, w, h))
  369         return (CMD_RETURN_NORMAL);
  370 
  371     value = args_get(args, 'd');
  372     if (value != NULL)
  373         cwd = format_single_from_target(item, value);
  374     else
  375         cwd = xstrdup(server_client_get_cwd(tc, s));
  376     if (argc == 0)
  377         shellcmd = options_get_string(s->options, "default-command");
  378     else if (argc == 1)
  379         shellcmd = argv[0];
  380     if (argc <= 1 && (shellcmd == NULL || *shellcmd == '\0')) {
  381         shellcmd = NULL;
  382         shell[0] = options_get_string(s->options, "default-shell");
  383         if (!checkshell(shell[0]))
  384             shell[0] = _PATH_BSHELL;
  385         argc = 1;
  386         argv = (char**)shell;
  387     }
  388 
  389     if (args_has(args, 'E') > 1)
  390         flags |= POPUP_CLOSEEXITZERO;
  391     else if (args_has(args, 'E'))
  392         flags |= POPUP_CLOSEEXIT;
  393     if (popup_display(flags, item, px, py, w, h, shellcmd, argc, argv, cwd,
  394         tc, s, NULL, NULL) != 0)
  395         return (CMD_RETURN_NORMAL);
  396     return (CMD_RETURN_WAIT);
  397 }