1 /* $OpenBSD: cmd-display-menu.c,v 1.20 2020/10/30 08:55:56 nicm Exp $ */ 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 = { "CEKc:d:h:R:t:w:x:y:", 0, -1 }, 54 .usage = "[-CEK] [-c target-client] [-d start-directory] [-h height] " 55 "[-R shell-command] " CMD_TARGET_PANE_USAGE " [-w width] " 56 "[-x position] [-y position] [command line ...]", 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 void 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; 75 struct style_range *sr; 76 const char *xp, *yp; 77 u_int line, ox, oy, sx, sy, lines; 78 79 lines = status_line_size(tc); 80 for (line = 0; line < lines; line++) { 81 ranges = &tc->status.entries[line].ranges; 82 TAILQ_FOREACH(sr, ranges, entry) { 83 if (sr->type == STYLE_RANGE_WINDOW) 84 break; 85 } 86 if (sr != NULL) 87 break; 88 } 89 if (line == lines) 90 ranges = &tc->status.entries[0].ranges; 91 92 xp = args_get(args, 'x'); 93 if (xp == NULL || strcmp(xp, "C") == 0) 94 *px = (tty->sx - 1) / 2 - w / 2; 95 else if (strcmp(xp, "R") == 0) 96 *px = tty->sx - 1; 97 else if (strcmp(xp, "P") == 0) { 98 tty_window_offset(&tc->tty, &ox, &oy, &sx, &sy); 99 if (wp->xoff >= ox) 100 *px = wp->xoff - ox; 101 else 102 *px = 0; 103 } else if (strcmp(xp, "M") == 0) { 104 if (event->m.valid && event->m.x > w / 2) 105 *px = event->m.x - w / 2; 106 else 107 *px = 0; 108 } else if (strcmp(xp, "W") == 0) { 109 if (status_at_line(tc) == -1) 110 *px = 0; 111 else { 112 TAILQ_FOREACH(sr, ranges, entry) { 113 if (sr->type != STYLE_RANGE_WINDOW) 114 continue; 115 if (sr->argument == (u_int)wl->idx) 116 break; 117 } 118 if (sr != NULL) 119 *px = sr->start; 120 else 121 *px = 0; 122 } 123 } else 124 *px = strtoul(xp, NULL, 10); 125 if ((*px) + w >= tty->sx) 126 *px = tty->sx - w; 127 128 yp = args_get(args, 'y'); 129 if (yp == NULL || strcmp(yp, "C") == 0) 130 *py = (tty->sy - 1) / 2 + h / 2; 131 else if (strcmp(yp, "P") == 0) { 132 tty_window_offset(&tc->tty, &ox, &oy, &sx, &sy); 133 if (wp->yoff + wp->sy >= oy) 134 *py = wp->yoff + wp->sy - oy; 135 else 136 *py = 0; 137 } else if (strcmp(yp, "M") == 0) { 138 if (event->m.valid) 139 *py = event->m.y + h; 140 else 141 *py = 0; 142 } else if (strcmp(yp, "S") == 0) { 143 if (options_get_number(s->options, "status-position") == 0) { 144 if (lines != 0) 145 *py = lines + h; 146 else 147 *py = 0; 148 } else { 149 if (lines != 0) 150 *py = tty->sy - lines; 151 else 152 *py = tty->sy; 153 } 154 } else if (strcmp(yp, "W") == 0) { 155 if (options_get_number(s->options, "status-position") == 0) { 156 if (lines != 0) 157 *py = line + 1 + h; 158 else 159 *py = 0; 160 } else { 161 if (lines != 0) 162 *py = tty->sy - lines + line; 163 else 164 *py = tty->sy; 165 } 166 } else 167 *py = strtoul(yp, NULL, 10); 168 if (*py < h) 169 *py = 0; 170 else 171 *py -= h; 172 if ((*py) + h >= tty->sy) 173 *py = tty->sy - h; 174 } 175 176 static enum cmd_retval 177 cmd_display_menu_exec(struct cmd *self, struct cmdq_item *item) 178 { 179 struct args *args = cmd_get_args(self); 180 struct cmd_find_state *target = cmdq_get_target(item); 181 struct key_event *event = cmdq_get_event(item); 182 struct client *tc = cmdq_get_target_client(item); 183 struct menu *menu = NULL; 184 struct menu_item menu_item; 185 const char *key; 186 char *title, *name; 187 int flags = 0, i; 188 u_int px, py; 189 190 if (tc->overlay_draw != NULL) 191 return (CMD_RETURN_NORMAL); 192 193 if (args_has(args, 'T')) 194 title = format_single_from_target(item, args_get(args, 'T')); 195 else 196 title = xstrdup(""); 197 menu = menu_create(title); 198 199 for (i = 0; i != args->argc; /* nothing */) { 200 name = args->argv[i++]; 201 if (*name == '\0') { 202 menu_add_item(menu, NULL, item, tc, target); 203 continue; 204 } 205 206 if (args->argc - i < 2) { 207 cmdq_error(item, "not enough arguments"); 208 free(title); 209 menu_free(menu); 210 return (CMD_RETURN_ERROR); 211 } 212 key = args->argv[i++]; 213 214 menu_item.name = name; 215 menu_item.key = key_string_lookup_string(key); 216 menu_item.command = args->argv[i++]; 217 218 menu_add_item(menu, &menu_item, item, tc, target); 219 } 220 free(title); 221 if (menu == NULL) { 222 cmdq_error(item, "invalid menu arguments"); 223 return (CMD_RETURN_ERROR); 224 } 225 if (menu->count == 0) { 226 menu_free(menu); 227 return (CMD_RETURN_NORMAL); 228 } 229 cmd_display_menu_get_position(tc, item, args, &px, &py, menu->width + 4, 230 menu->count + 2); 231 232 if (args_has(args, 'O')) 233 flags |= MENU_STAYOPEN; 234 if (!event->m.valid) 235 flags |= MENU_NOMOUSE; 236 if (menu_display(menu, flags, item, px, py, tc, target, NULL, 237 NULL) != 0) 238 return (CMD_RETURN_NORMAL); 239 return (CMD_RETURN_WAIT); 240 } 241 242 static enum cmd_retval 243 cmd_display_popup_exec(struct cmd *self, struct cmdq_item *item) 244 { 245 struct args *args = cmd_get_args(self); 246 struct cmd_find_state *target = cmdq_get_target(item); 247 struct client *tc = cmdq_get_target_client(item); 248 struct tty *tty = &tc->tty; 249 const char *value, *cmd = NULL, **lines = NULL; 250 const char *shellcmd = NULL; 251 char *cwd, *cause; 252 int flags = 0; 253 u_int px, py, w, h, nlines = 0; 254 255 if (args_has(args, 'C')) { 256 server_client_clear_overlay(tc); 257 return (CMD_RETURN_NORMAL); 258 } 259 if (tc->overlay_draw != NULL) 260 return (CMD_RETURN_NORMAL); 261 262 if (args->argc >= 1) 263 cmd = args->argv[0]; 264 if (args->argc >= 2) { 265 lines = (const char **)args->argv + 1; 266 nlines = args->argc - 1; 267 } 268 269 if (nlines != 0) 270 h = popup_height(nlines, lines) + 2; 271 else 272 h = tty->sy / 2; 273 if (args_has(args, 'h')) { 274 h = args_percentage(args, 'h', 1, tty->sy, tty->sy, &cause); 275 if (cause != NULL) { 276 cmdq_error(item, "height %s", cause); 277 free(cause); 278 return (CMD_RETURN_ERROR); 279 } 280 } 281 282 if (nlines != 0) 283 w = popup_width(item, nlines, lines, tc, target) + 2; 284 else 285 w = tty->sx / 2; 286 if (args_has(args, 'w')) { 287 w = args_percentage(args, 'w', 1, tty->sx, tty->sx, &cause); 288 if (cause != NULL) { 289 cmdq_error(item, "width %s", cause); 290 free(cause); 291 return (CMD_RETURN_ERROR); 292 } 293 } 294 295 if (w > tty->sx - 1) 296 w = tty->sx - 1; 297 if (h > tty->sy - 1) 298 h = tty->sy - 1; 299 cmd_display_menu_get_position(tc, item, args, &px, &py, w, h); 300 301 value = args_get(args, 'd'); 302 if (value != NULL) 303 cwd = format_single_from_target(item, value); 304 else 305 cwd = xstrdup(server_client_get_cwd(tc, target->s)); 306 307 value = args_get(args, 'R'); 308 if (value != NULL) 309 shellcmd = format_single_from_target(item, value); 310 311 if (args_has(args, 'K')) 312 flags |= POPUP_WRITEKEYS; 313 if (args_has(args, 'E') > 1) 314 flags |= POPUP_CLOSEEXITZERO; 315 else if (args_has(args, 'E')) 316 flags |= POPUP_CLOSEEXIT; 317 if (popup_display(flags, item, px, py, w, h, nlines, lines, shellcmd, 318 cmd, cwd, tc, target, NULL, NULL) != 0) 319 return (CMD_RETURN_NORMAL); 320 return (CMD_RETURN_WAIT); 321 } 322