1 /* $OpenBSD: cmd-display-menu.c,v 1.19 2020/05/16 15:24:28 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:T:x:y:", 1, -1 }, 40 .usage = "[-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 (!event->m.valid) 233 flags |= MENU_NOMOUSE; 234 if (menu_display(menu, flags, item, px, py, tc, target, NULL, 235 NULL) != 0) 236 return (CMD_RETURN_NORMAL); 237 return (CMD_RETURN_WAIT); 238 } 239 240 static enum cmd_retval 241 cmd_display_popup_exec(struct cmd *self, struct cmdq_item *item) 242 { 243 struct args *args = cmd_get_args(self); 244 struct cmd_find_state *target = cmdq_get_target(item); 245 struct client *tc = cmdq_get_target_client(item); 246 struct tty *tty = &tc->tty; 247 const char *value, *cmd = NULL, **lines = NULL; 248 const char *shellcmd = NULL; 249 char *cwd, *cause; 250 int flags = 0; 251 u_int px, py, w, h, nlines = 0; 252 253 if (args_has(args, 'C')) { 254 server_client_clear_overlay(tc); 255 return (CMD_RETURN_NORMAL); 256 } 257 if (tc->overlay_draw != NULL) 258 return (CMD_RETURN_NORMAL); 259 260 if (args->argc >= 1) 261 cmd = args->argv[0]; 262 if (args->argc >= 2) { 263 lines = (const char **)args->argv + 1; 264 nlines = args->argc - 1; 265 } 266 267 if (nlines != 0) 268 h = popup_height(nlines, lines) + 2; 269 else 270 h = tty->sy / 2; 271 if (args_has(args, 'h')) { 272 h = args_percentage(args, 'h', 1, tty->sy, tty->sy, &cause); 273 if (cause != NULL) { 274 cmdq_error(item, "height %s", cause); 275 free(cause); 276 return (CMD_RETURN_ERROR); 277 } 278 } 279 280 if (nlines != 0) 281 w = popup_width(item, nlines, lines, tc, target) + 2; 282 else 283 w = tty->sx / 2; 284 if (args_has(args, 'w')) { 285 w = args_percentage(args, 'w', 1, tty->sx, tty->sx, &cause); 286 if (cause != NULL) { 287 cmdq_error(item, "width %s", cause); 288 free(cause); 289 return (CMD_RETURN_ERROR); 290 } 291 } 292 293 if (w > tty->sx - 1) 294 w = tty->sx - 1; 295 if (h > tty->sy - 1) 296 h = tty->sy - 1; 297 cmd_display_menu_get_position(tc, item, args, &px, &py, w, h); 298 299 value = args_get(args, 'd'); 300 if (value != NULL) 301 cwd = format_single_from_target(item, value); 302 else 303 cwd = xstrdup(server_client_get_cwd(tc, target->s)); 304 305 value = args_get(args, 'R'); 306 if (value != NULL) 307 shellcmd = format_single_from_target(item, value); 308 309 if (args_has(args, 'K')) 310 flags |= POPUP_WRITEKEYS; 311 if (args_has(args, 'E') > 1) 312 flags |= POPUP_CLOSEEXITZERO; 313 else if (args_has(args, 'E')) 314 flags |= POPUP_CLOSEEXIT; 315 if (popup_display(flags, item, px, py, w, h, nlines, lines, shellcmd, 316 cmd, cwd, tc, target, NULL, NULL) != 0) 317 return (CMD_RETURN_NORMAL); 318 return (CMD_RETURN_WAIT); 319 } 320